我如何设置一个NSDate对象到午夜?

我有一个NSDate对象,我想将它设置为任意时间(比如午夜),这样我就可以使用timeIntervalSince1970函数来一致地检索数据,而不用担心创build对象的时间。

我已经尝试使用NSCalendar并通过使用一些Objective-C方法修改其组件,如下所示:

 let date: NSDate = NSDate() let cal: NSCalendar = NSCalendar(calendarIdentifier: NSGregorianCalendar)! let components: NSDateComponents = cal.components(NSCalendarUnit./* a unit of time */CalendarUnit, fromDate: date) let newDate: NSDate = cal.dateFromComponents(components) 

上述方法的问题是你只能设置一个单位时间( /* a unit of time */ ),所以你只能有以下一个准确的:

  1. 小时
  2. 分钟

有没有办法同时设置小时,分钟和秒钟,并保留date(日/月/年)?

你的陈述

上述方法的问题是,你只能设置一个单位的时间…

是不正确的。 NSCalendarUnit符合从BitwiseOperationsTypeinheritance的RawOptionSetType协议。 这意味着这些选项可以与&|按位组合 。

Swift 2(Xcode 7)中,这又变成了一个OptionSetType ,它提供了一组类似的接口,例如在Swift 2.0中将NSCalendarUnit与OR(pipe道)组合起来的错误 。

因此,以下编译和工作在iOS 7和iOS 8中:

 let date = NSDate() let cal = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)! // Swift 1.2: let components = cal.components(.CalendarUnitDay | .CalendarUnitMonth | .CalendarUnitYear, fromDate: date) // Swift 2: let components = cal.components([.Day , .Month, .Year ], fromDate: date) let newDate = cal.dateFromComponents(components) 

(请注意,我省略了variables的types注释,Swift编译器会从作业右侧的expression式中自动推断出types。)

使用rangeOfUnit()方法(iOS 7和iOS 8)也rangeOfUnit()给定date(午夜)的开始时间:

 let date = NSDate() let cal = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)! var newDate : NSDate? // Swift 1.2: cal.rangeOfUnit(.CalendarUnitDay, startDate: &newDate, interval: nil, forDate: date) // Swift 2: cal.rangeOfUnit(.Day, startDate: &newDate, interval: nil, forDate: date) 

如果您的部署目标是iOS 8,那么它更简单:

 let date = NSDate() let cal = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)! let newDate = cal.startOfDayForDate(date) 

Swift 3(Xcode 8)的更新

 let date = Date() let cal = Calendar(identifier: .gregorian) let newDate = cal.startOfDay(for: date) 

是。

你根本不需要摆弄NSCalendar的组件; 您可以简单地调用dateBySettingHour方法,并使用ofDate参数与您的现有date。

 let date: NSDate = NSDate() let cal: NSCalendar = NSCalendar(calendarIdentifier: NSGregorianCalendar)! let newDate: NSDate = cal.dateBySettingHour(0, minute: 0, second: 0, ofDate: date, options: NSCalendarOptions())! 

对于Swift 3:

 let date: Date = Date() let cal: Calendar = Calendar(identifier: .gregorian) let newDate: Date = cal.date(bySettingHour: 0, minute: 0, second: 0, of: date)! 

那么,从1970年开始,你就可以做到

 let time: NSTimeInterval = newDate.timeIntervalSince1970 

dateBySettingHour在OS X Mavericks(10.9)中引入,并获得了iOS 8的iOS支持。

NSCalendar.h声明:

 /* This API returns a new NSDate object representing the date calculated by setting hour, minute, and second to a given time. If no such time exists, the next available time is returned (which could, for example, be in a different day than the nominal target date). The intent is to return a date on the same day as the original date argument. This may result in a date which is earlier than the given date, of course. */ - (NSDate *)dateBySettingHour:(NSInteger)h minute:(NSInteger)m second:(NSInteger)s ofDate:(NSDate *)date options:(NSCalendarOptions)opts NS_AVAILABLE(10_9, 8_0); 

下面是一个如何做,而不使用dateBySettingHour函数(以确保您的代码仍然与iOS 7设备兼容)的示例:

 NSDate* now = [NSDate date]; NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar]; NSDateComponents *dateComponents = [gregorian components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit) fromDate:now]; NSDate* midnightLastNight = [gregorian dateFromComponents:dateComponents]; 

呸。

有一个原因,我更喜欢在C#中编码…

任何人都喜欢一些可读的代码..?

 DateTime midnightLastNight = DateTime.Today; 

😉

在我看来,最容易validation的解决scheme,也许不是最快捷的,就是使用string。

 func set( hours: Int, minutes: Int, seconds: Int, ofDate date: Date ) -> Date { let dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyyy-MM-dd" let newDateString = "\(dateFormatter.string(from: date)) \(hours):\(minutes):\(seconds)" dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss" return dateFormatter.date(from: newDateString) } 

Swift iOS 8及以上 :人们往往忘记日历和DateFormatter对象有一个时区。 如果您没有设置所需的timzone,并且默认的时区值对您不合适,则可能会导致产生的小时数和分钟数。

注意:在一个真正的应用程序,你可以优化这个代码更多。

注意:您甚至可以花时间在一台设备上search错误,而另一台设备则由于时区设置不同而无法使用相同的代码。

注意:一定要添加一个现有的时区标识符! 此代码不处理该错误。

 func dateTodayZeroHour() -> Date { var cal = Calendar.current cal.timeZone = TimeZone(identifier: "Europe/Paris")! return cal.startOfDay(for: Date()) } 

你甚至可以扩展语言。 如果默认时区适合您,请勿设置。

 extension Date { var midnight: Date { var cal = Calendar.current cal.timeZone = TimeZone(identifier: "Europe/Paris")! return cal.startOfDay(for: self) } var midday: Date { var cal = Calendar.current cal.timeZone = TimeZone(identifier: "Europe/Paris")! return cal.date(byAdding: .hour, value: 12, to: self.midnight)! } } 

像这样使用它:

 let formatter = DateFormatter() formatter.timeZone = TimeZone(identifier: "Europe/Paris") formatter.dateFormat = "yyyy/MM/dd HH:mm:ss" let midnight = Date().midnight let midnightString = formatter.string(from: midnight) let midday = Date().midday let middayString = formatter.string(from: midday) let wheneverMidnight = formatter.date(from: "2018/12/05 08:08:08")!.midnight let wheneverMidnightString = formatter.string(from: wheneverMidnight) print("dates: \(midnightString) \(middayString) \(wheneverMidnightString)") 

string转换和DateFormatter在我们的情况下需要格式化和移动时区,因为date对象本身并不保留时区值。

注意:但是由于计算链中某处的时区偏移,值可能会有所不同!

 func resetHourMinuteSecond(date: NSDate, hour: Int, minute: Int, second: Int) -> NSDate{ let nsdate = NSCalendar.currentCalendar().dateBySettingHour(hour, minute: minute, second: second, ofDate: date, options: NSCalendarOptions(rawValue: 0)) return nsdate! }