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 */),因此您只能使用下列其中一个准确度:
有没有办法同时设置小时,分钟和秒并保留日期(日/月/年)?
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)
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)
以下是如何在不使用该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)
;-)
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对象本身并不保留或关心时区值。
小心!由于您的计算链中某个地方的时区偏移,结果值可能会有所不同!
斯威夫特 5+
let date = Calendar.current.date(bySettingHour: 0, minute: 0, second: 0, of: Date())
Run Code Online (Sandbox Code Playgroud)