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

时间:2014-10-04 04:32:37

标签: ios datetime swift nsdate nscalendar

我有一个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)

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

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

9 个答案:

答案 0 :(得分:81)

您的陈述

  

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

不正确。 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)

答案 1 :(得分: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())!

对于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
在OS X Mavericks(10.9)中引入了

dateBySettingHour,并在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);

答案 2 :(得分: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];

呸。

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

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

DateTime midnightLastNight = DateTime.Today;

- )

答案 3 :(得分: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())   
}

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

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,因为日期对象本身不保留或关心时区值。

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

答案 4 :(得分:2)

以防万一有人在寻找:

使用SwiftDate你可以这样做:

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

答案 5 :(得分:1)

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

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 :(得分:0)

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

答案 7 :(得分:0)

迅速5 +

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

答案 8 :(得分:0)

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

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