【问题标题】:How can I set an NSDate object to midnight?如何将 NSDate 对象设置为午夜?
【发布时间】:2014-11-29 03:27:33
【问题描述】:

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

我尝试使用 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. 小时
  3. 分钟

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

【问题讨论】:

    标签: ios datetime swift nsdate nscalendar


    【解决方案1】:

    使用当前日历获取当前时间的开始日期。

    let today = Calendar.current.startOfDay(for: Date())
    

    【讨论】:

      【解决方案2】:

      Swift 5+

      let date = Calendar.current.date(bySettingHour: 0, minute: 0, second: 0, of: Date())
      

      【讨论】:

      • 这将返回相对于时区的零小时。
      【解决方案3】:

      Swift iOS 8 及更高版本:人们往往会忘记 Calendar 和 DateFormatter 对象具有 TimeZone。如果您没有设置所需的时区并且默认时区值不适合您,则生成的小时和分钟可能会关闭。

      注意:在实际应用中,您可以进一步优化此代码。

      注意:如果不关心时区,结果可能在一台设备上还可以,而在另一台设备上的结果可能会因为时区设置不同而变差。

      注意:请务必添加现有的时区标识符!此代码不处理丢失或拼写错误的时区名称。

      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)")
      

      在我们的例子中需要字符串转换和 DateFormatter 来进行一些格式化和移动时区,因为日期对象本身并不保留或关心时区值。

      小心!结果值可能会因计算链中某处的时区偏移而有所不同!

      【讨论】:

      • value= 12 不正确。使用:符号,就像在任何其他方法参数中一样
      【解决方案4】:

      以防万一有人在寻找这个:

      使用SwiftDate 你可以这样做:

      Date().atTime(hour: 0, minute: 0, second: 0)
      

      【讨论】:

        【解决方案5】:

        在我看来,最容易验证但可能不是最快的解决方案是使用字符串。

        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)
        }
        

        【讨论】:

          【解决方案6】:

          你的陈述

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

          不正确。 NSCalendarUnit 符合 RawOptionSetType 协议,该协议 继承自BitwiseOperationsType。这意味着选项可以按位 结合&|

          Swift 2 (Xcode 7) 中再次更改为 一个OptionSetType,它提供了一个类似集合的界面,见 例如Error combining NSCalendarUnit with OR (pipe) in Swift 2.0

          因此,以下在 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)
          

          (请注意,我省略了变量的类型注释,Swift 编译器 从右边的表达式自动推断类型 任务。)

          也可以确定给定一天(午夜)的开始时间 使用rangeOfUnit() 方法(iOS 7 和 iOS 8):

          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)
          

          【讨论】:

          • 奇怪的是startOfDayForDate:方法虽然暴露在头文件中,但没有记录在案,还有很多新方法也没有记录。谢谢你。
          • @Desdenova:iOS 8.0 API Diffs 中也列出了它,但您是对的,NSCalendar 文档确实不是最新的。
          • @Desdenova dateBySettingHour 方法也是如此。
          • @adnako:我很确定 startOfDayForDate 在所有时区都能正常工作。你有具体的例子吗?
          • @MartinR 我的时区是 UTC+3,值是 '2016-06-28 15:35:44 UTC',如果我在这个日期调用 startOfDayForDate 结果是 '2016-06-27世界标准时间 21:00:00”。这不是我所需要的。
          【解决方案7】:

          是的。

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

          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);
          

          【讨论】:

          • @SantaClaus 它似乎没有出现在正式文档中,但它出现在 WWDC 2013 的 this sessionNSCalendar.h 文件中。它在 OS X 10.9+ 和 iOS 8 中受支持。
          • 非常重要的是要注意它需要 ios 8 然后
          • 在iOS7中如何实现?
          • @jose920405 那是因为它是 NSCalendar 类的一个方法。
          【解决方案8】:
          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!
          }
          

          【讨论】:

            【解决方案9】:

            下面是一个示例,说明如何在不使用 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;
            

            ;-)

            【讨论】:

            • 我希望日期在 Swift 中像 C# 一样易于使用 :(.
            猜你喜欢
            • 1970-01-01
            • 2012-02-20
            • 1970-01-01
            • 2015-10-05
            • 1970-01-01
            • 2011-12-26
            • 2014-08-26
            • 2011-10-14
            相关资源
            最近更新 更多