如何将NSDate对象设置为午夜?

Ast*_*oCB 50 datetime nsdate nscalendar ios swift

我有一个NSDate对象,我想将它设置为任意时间(比如半夜),这样我可以使用timeIntervalSince1970的功能一致,而不用担心时间来检索数据时创建的对象.

我尝试使用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)
Run Code Online (Sandbox Code Playgroud)

上述方法的问题在于您只能设置一个时间单位(/* a unit of time */),因此您只能使用下列其中一个准确度:

  1. 天
  2. 月
  3. 年
  4. 小时
  5. 分钟
  6. 秒

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

Mar*_*n R 86

你的陈述

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

是不正确的.NSCalendarUnit符合RawOptionSetType继承自的协议BitwiseOperationsType.这意味着选项可以与&和按位组合|.

在Swift 2(Xcode 7)中,它再次被改为OptionSetType提供类似集合的接口,例如参见Swift 2.0中将NSCalendarUnit与OR(管道)组合的错误.

因此,以下编译适用于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)
Run Code Online (Sandbox Code Playgroud)

(请注意,我省略了变量的类型注释,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)
Run Code Online (Sandbox Code Playgroud)

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

let date = NSDate()
let cal = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)!
let newDate = cal.startOfDayForDate(date)
Run Code Online (Sandbox Code Playgroud)

更新Swift 3(Xcode 8):

let date = Date()
let cal = Calendar(identifier: .gregorian)
let newDate = cal.startOfDay(for: date)
Run Code Online (Sandbox Code Playgroud)

  • @Desdenova:它也列在[iOS 8.0 API Diffs](https://developer.apple.com/library/ios/releasenotes/General/iOS80APIDiffs/frameworks/Foundation.html)中,但你是对的,` NSCalendar`文档确实不是最新的. (3认同)

Ast*_*oCB 33

是.

你根本不需要摆弄这些组件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())!
Run Code Online (Sandbox Code Playgroud)

对于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)!
Run Code Online (Sandbox Code Playgroud)

然后,为了获得自1970年以来的时间,你可以做到

let time: NSTimeInterval = newDate.timeIntervalSince1970
Run Code Online (Sandbox Code Playgroud)

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);
Run Code Online (Sandbox Code Playgroud)

  • 非常重要的是要注意它需要ios 8然后 (2认同)

Mik*_*ill 8

以下是如何在不使用该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];
Run Code Online (Sandbox Code Playgroud)

呸.

我更喜欢用C#编码...

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

DateTime midnightLastNight = DateTime.Today;
Run Code Online (Sandbox Code Playgroud)

;-)


t1s*_*ser 5

Swift iOS 8及更高版本:人们倾向于忘记Calendar和DateFormatter对象具有TimeZone。如果您未设置所需的时区并且默认时区值不适合您,则可能会导致小时数和分钟数的变化。

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

注意:当不关心时区时,由于时区设置不同,结果可能在一台设备上是好的,而在另一台设备上的效果不好。

注意:请确保添加现有的时区标识符!此代码无法处理时区名称丢失或拼写错误的情况。

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

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

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)!
    }
}
Run Code Online (Sandbox Code Playgroud)

并像这样使用它:

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)")
Run Code Online (Sandbox Code Playgroud)

在本例中,为了进行某种格式设置和移动时区,需要使用字符串转换和DateFormatter,因为date对象本身并不保留或关心时区值。

小心!由于您的计算链中某个地方的时区偏移,结果值可能会有所不同!


Wil*_* Hu 5

斯威夫特 5+

let date = Calendar.current.date(bySettingHour: 0, minute: 0, second: 0, of: Date())
Run Code Online (Sandbox Code Playgroud)