NSDate一天的开始和结束


104
    -(NSDate *)beginningOfDay:(NSDate *)date
{
    NSCalendar *cal = [NSCalendar currentCalendar];
    NSDateComponents *components = [cal components:( NSMonthCalendarUnit | NSYearCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit ) fromDate:date];

    [components setHour:0];
    [components setMinute:0];
    [components setSecond:0];

    return [cal dateFromComponents:components];

}

-(NSDate *)endOfDay:(NSDate *)date
{
    NSCalendar *cal = [NSCalendar currentCalendar];
    NSDateComponents *components = [cal components:(  NSMonthCalendarUnit | NSYearCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit ) fromDate:date];

    [components setHour:23];
    [components setMinute:59];
    [components setSecond:59];

    return [cal dateFromComponents:components];

}

当我打电话时:[self endOfDay:[NSDate date]]; 我得到本月的第一天……为什么?我使用这两种方法是因为我需要一个从第一个日期的第一秒(beginningOfDay:date1)到第二个日期的最后一秒(endOfDay:Date2)的间隔...


2
如果要将小时设置为UTC时间为零,则它是获取timeIntervalSince ...的更简单方法,截断小时,然后转换回NSDate。如果您首先通过时区的secondsFromGMT调整时间间隔,然后在截断后以相反的方式进行调整,则这将适用于另一个时区。(一天的结束时间显然是23:59:59.999,这可以在您有时间间隔的情况下通过简单的加法获得。)
Hot Licks 2012年

将“一天结束”指定为实际结束之前的任意时间(在这种情况下为一秒)似乎是故障软件的诀窍。如果您使用1毫秒,则毛刺将不那么频繁。或者您可以使用23个小时,以便更可能发现任何错误。:-)
爱德华·布雷

Answers:


33

你失踪NSDayCalendarUnit

NSDateComponents *components = [cal components:( NSMonthCalendarUnit | NSYearCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit ) fromDate:date];

1
我有一个奇怪的行为,如果我添加日部分,我会得到前一天的23:00。
Mike M

3
@Mike使用时区:components.timeZone = [NSTimeZone timeZoneWithName:@“ GMT”];
dimaxyu

@dimaxyu是正确的。迅速:components.timeZone = NSTimeZone(名称:“ GMT”)
Tom Bevelander

220

一天的开始/一天的结束-Swift 4

  // Extension

extension Date {
    var startOfDay: Date {
        return Calendar.current.startOfDay(for: self)
    }

    var endOfDay: Date {
        var components = DateComponents()
        components.day = 1
        components.second = -1
        return Calendar.current.date(byAdding: components, to: startOfDay)!
    }

    var startOfMonth: Date {
        let components = Calendar.current.dateComponents([.year, .month], from: startOfDay)
        return Calendar.current.date(from: components)!
    }

    var endOfMonth: Date {
        var components = DateComponents()
        components.month = 1
        components.second = -1
        return Calendar.current.date(byAdding: components, to: startOfMonth)!
    }
}

// End of day = Start of tomorrow minus 1 second
// End of month = Start of next month minus 1 second

1
NSYearCalendarUnitNSMonthCalendarUnitNSDayCalendarUnit已被弃用的iOS 8.使用的NSCalendarUnitYearNSCalendarUnitMonthNSCalendarUnitDay而不是!
T Blank

6
对于一天的开始,请为iOS8 +使用[[NSCalendar currentCalendar] startOfDayForDate:...]
GingerBreadMane

5
这将返回当前时区的startOfDay!NSDate预期采用UTC,但是此转换器返回的是针对默认时区的校正时间。
Tom Bevelander

3
为什么endOfDay是可选的?是否存在startOfDay不为零而endOfDay为零的情况?
Mansurov Ruslan

1
所以,这取决于你如何使用你介意结束,天开始之间的一个第二间隙developer.apple.com/documentation/foundation/nsdate/...
atlex2

29

Swift 5 简单而精确的答案。

开始时间:00:00:00

结束时间:23:59:59.5

let date = Date() // current date or replace with a specific date
let calendar = Calendar.current
let startTime = calendar.startOfDay(for: date)
let endTime = calendar.date(bySettingHour: 23, minute: 59, second: 59, of: date)

额外

let yesterday = Calendar.current.date(byAdding: .day, value: -1, to: noon)!
let tomorrow = Calendar.current.date(byAdding: .day, value: 1, to: noon)!
let specificDate = Date("2020-01-01")

extension Date {
    init(_ dateString:String) {
        let dateStringFormatter = DateFormatter()
        dateStringFormatter.dateFormat = "yyyy-MM-dd"
        dateStringFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX") as Locale
        let date = dateStringFormatter.date(from: dateString)!
        self.init(timeInterval:0, since:date)
    }
}

2
这有何不同let dateAtEnd = Calendar.current.date(bySettingHour: 23, minute: 59, second: 59, of: Date())
安德烈斯·卡内拉

1
没有不同 。
日,八月Lin

21

在iOS 8+中,这确实很方便。你可以做:

let startOfDay = NSCalendar.currentCalendar().startOfDayForDate(date)

要获得一天的结束时间,只需使用NSCalendar方法23小时59分59秒,具体取决于您如何定义一天的结束时间。

// Swift 2.0
let components = NSDateComponents()
components.hour = 23
components.minute = 59
components.second = 59
let endOfDay = NSCalendar.currentCalendar().dateByAddingComponents(components, toDate: startOfDay, options: NSCalendarOptions(rawValue: 0))

日期数学

Apple iOS NSCalendar文档。(请参阅部分:日历计算

NSHipster讨论了NSCalendar方法


17

我对NSDate的Swift扩展:

斯威夫特1.2

extension NSDate {

    func beginningOfDay() -> NSDate {
        var calendar = NSCalendar.currentCalendar()
        var components = calendar.components(.CalendarUnitYear | .CalendarUnitMonth | .CalendarUnitDay, fromDate: self)
        return calendar.dateFromComponents(components)!
    }

    func endOfDay() -> NSDate {
        var components = NSDateComponents()
        components.day = 1
        var date = NSCalendar.currentCalendar().dateByAddingComponents(components, toDate: self.beginningOfDay(), options: .allZeros)!
        date = date.dateByAddingTimeInterval(-1)!
        return date
    }
}

雨燕2.0

extension NSDate {

    func beginningOfDay() -> NSDate {
        let calendar = NSCalendar.currentCalendar()
        let components = calendar.components([.Year, .Month, .Day], fromDate: self)
        return calendar.dateFromComponents(components)!
    }

    func endOfDay() -> NSDate {
        let components = NSDateComponents()
        components.day = 1
        var date = NSCalendar.currentCalendar().dateByAddingComponents(components, toDate: self.beginningOfDay(), options: [])!
        date = date.dateByAddingTimeInterval(-1)
        return date
    }
}

您可以将秒设置为-1,而无需使用dateByAddingTimeInterval即可获得相同的值
Ben Sinclair

为了避免时区问题,请添加:components.timeZone = NSTimeZone(name:“ GMT”)
Tom Bevelander

12

Swift 5.1-XCode 11使用Date类代替NSDateCalender代替NSCalender

extension Date {

    var startOfDay : Date {
        let calendar = Calendar.current
        let unitFlags = Set<Calendar.Component>([.year, .month, .day])
        let components = calendar.dateComponents(unitFlags, from: self)
        return calendar.date(from: components)!
   }

    var endOfDay : Date {
        var components = DateComponents()
        components.day = 1
        let date = Calendar.current.date(byAdding: components, to: self.startOfDay)
        return (date?.addingTimeInterval(-1))!
    }
}

用法:

    let myDate = Date()
    let startOfDate = myDate.startOfDay
    let endOfDate = myDate.endOfDay

Date在操场上尝试时遇到错误未声明的类型
John Doe

1
在这里为我工作。您是否导入了UIKit import UIKit
2016年

2
@Ben Date and Calendar不在UIKit中,而是在Foundation中,但是UIKit导入Foundation
Arbitur

4

您不必将组件设置为零,只需忽略它们即可:

-(NSDate *)beginningOfDay:(NSDate *)date
{
    NSCalendar *calendar = [NSCalendar currentCalendar];
    NSDateComponents *components = [calendar components:NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit fromDate:date];
    return [calendar dateFromComponents:components];
}

2
“最新”可用:[calendar startOfDayForDate:date],但-endOfDayForDate:不幸的是,没有...
朱利安·F·韦纳特

4

对我来说,这里以及在stackoverflow上其他地方都没有答案。从今天开始,我做到了。

NSCalendar * gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian]; 
[gregorian setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];    
NSDateComponents *components = [gregorian components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay fromDate:[NSDate date]]; 
[components setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]]; 
NSDate *beginningOfToday = [gregorian dateFromComponents:components];

请注意此[gregorian setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];[components setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];

创建日历后,它将使用当前时区进行初始化,并且从其组件中提取日期后,由于NSDate没有时区,因此将当前时区中的日期视为UTC时区。因此,我们需要在提取组件之前设置时区,然后在从这些组件提取日期时设置时区。


4

迅捷3

  class func today() -> NSDate {
        return NSDate()
    }

    class func dayStart() -> NSDate {
          return NSCalendar.current.startOfDay(for: NSDate() as Date) as NSDate
    }

    class func dayEnd() -> NSDate {
        let components = NSDateComponents()
        components.day = 1
        components.second = -1
        return NSCalendar.current.date(byAdding: components as DateComponents, to: self.dayStart() as Date)
    }

4

Swift3使用* XCode8
苹果NS从类名中删除了,因此NSDate可以换成Date。如果尝试将它们强制转换为它们将始终失败,则可能会收到编译器警告,但是当您在操场上运行它们时它们会正常工作。

我将NSDate核心数据模型中生成的替换为Date,它们仍然可以工作。

extension Date {
  func startTime() -> Date {
    return Calendar.current.startOfDay(for: self)
  }

  func endTime() -> Date {
    var components = DateComponents()
    components.day = 1
    components.second = -1
    return Calendar.current.date(byAdding: components, to: startTime())!
  }
}

4

在Swift 3及更高版本中

extension Date {
    var startOfDayDate: Date {
        return Calendar.current.startOfDay(for: self)
    }

    var endOfDayDate: Date {
        let nextDayDate = Calendar.current.date(byAdding: .day, value: 1, to: self.startOfDayDate)!
        return nextDayDate.addingTimeInterval(-1)
    }
}

用法:

var currentDayStart = Date().startOfDayDate
var currentDayEnd = Date().endOfDayDate

2

获得结果的另一种方法:

NSDate *date = [NSDate date];

NSDateComponents *components = [[NSDateComponents alloc] init];
components.day = [[NSCalendar currentCalendar] ordinalityOfUnit:(NSCalendarUnitDay) inUnit:(NSCalendarUnitEra) forDate:date];
NSDate *dayBegin = [[NSCalendar currentCalendar] dateFromComponents:components];

components.day += 1;
NSDate *dayEnd = [[NSCalendar currentCalendar] dateFromComponents:components];



2

迅捷4

    var calendar = Calendar.current
    calendar.timeZone = NSTimeZone(abbreviation: "UTC")! as TimeZone
    let dateAtMidnight = calendar.startOfDay(for: Date())

    //For End Date
    var components = DateComponents()
    components.day = 1
    components.second = -1

    let dateAtEnd = calendar.date(byAdding: components, to: dateAtMidnight)

    print("dateAtMidnight :: \(dateAtMidnight)")
    print("dateAtEnd :: \(dateAtEnd!)")

2

我认为在Swift中最简洁的方法如下:

extension Date {
    func startOfDay() -> Date {
        return Calendar.current.startOfDay(for: self)
    }
    func endOfDay() -> Date {
        return Calendar.current.date(bySettingHour: 23, minute: 59, second 59, of: self)
    }
}

那很完美!谢谢!
Baran Emre

1

由于iOS 8.0+ / macOS 10.12+ / tvOS 10.0+ / watchOS 3.0+Foundation中有一个内置功能,您可以直接使用它。无需实现自己的功能。

public func startOfDay(for date: Date) -> Date

因此,您可以通过以下方式使用它:

let midnightDate = Calendar(identifier: .gregorian).startOfDay(for: Date())

值得记住的是,这要考虑设备时区。您可以设置.timeZonecalendar,如果你想拥有如UTC区。

链接到Apple参考页面:https : //developer.apple.com/reference/foundation/nscalendar/1417161-startofday


1

只是使用dateInterval(of:start:interval:for:)的另一种方式Calendar

返回时startDate包含一天的开始时间和一天中interval的秒数。

func startAndEnd(of date : Date) -> (start : Date, end : Date) {
    var startDate = Date()
    var interval : TimeInterval = 0.0
    Calendar.current.dateInterval(of: .day, start: &startDate, interval: &interval, for: date)
    var endDate = startDate.addingTimeInterval(interval-1)
    return (start : startDate, end : endDate)
}

let (start, end) = startAndEnd(of: Date())
print(start, end)

1

这是我在Swift 4.2中使用的:

    let calendar = Calendar.current
    let fromDate = calendar.startOfDay(for: Date())
    let endDate = calendar.date(bySettingHour: 23, minute: 59, second: 59, of: Date())

对我来说就像是一种魅力。您可以将其添加到起始日期和结束日期的扩展名中Date,但是请记住,添加扩展名会增加编译时间(除非与该类位于同一文件中),因此,如果仅在一个地方或一个类中需要它。 ..不要使用扩展名。


0
extension Date {
    func stringFrom(dateFormat: String) -> String {
        let formatter = DateFormatter()
        formatter.dateFormat = dateFormat
        return formatter.string(from: self)
    }

    func firstSecondInDay() -> Date {
        let dayStr = self.stringFrom(dateFormat: "yyyy-MM-dd")
        let firstSecondStr = "\(dayStr) 00:00:00"
        let format = DateFormatter()
        format.dateFormat = "yyyy-MM-dd HH:mm:ss"
        return format.date(from: firstSecondStr)!
    }

    func lastSecondInDay() -> Date {
        let dayStr = self.stringFrom(dateFormat: "yyyy-MM-dd")
        let laseSecondStr = "\(dayStr) 23:59:59"
        let format = DateFormatter()
        format.dateFormat = "yyyy-MM-dd HH:mm:ss"
        return format.date(from: laseSecondStr)!
    }
}

0

仅供参考,在Swift 4中设置一天的开始和结束的简单方法

var comp: DateComponents = Calendar.current.dateComponents([.year, .month, .day, .hour, .minute, .second], from: Date())
comp.hour = 0
comp.minute = 0
comp.second = 0
comp.timeZone = TimeZone(abbreviation: "UTC")!


//Set Start of Day
let startDate : Date = Calendar.current.date(from: comp)!
print(“Day of Start : \(startDate)")


//Set End of Day
comp.hour = 23
comp.minute = 59
comp.second = 59

let endDate : Date = Calendar.current.date(from:comp)!
print("Day of End : \(endDate)")

-1

日历单位应视为间隔。从iOS 10开始,Calendar有一些不错的方法

let day = Calendar.autoupdatingCurrent.dateInterval(of: .day, for: Date())
day?.end
day?.start

您可以使用相同的方法来获取任何日历组件的开始/结束(周/月/年等)


这是不正确的。day?.end估计为明天12:00 AM,而不是今晚11:59 PM。
Casey Wagner
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.