NSDate
オブジェクトがあり、timeIntervalSince1970
関数を使用して時間を気にせずにデータを一貫して取得できるように、任意の時間(たとえば、真夜中)に設定したいwhenオブジェクトが作成されます。
私は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つしか設定できないことです。そのため、次のいずれか1つだけを正確に設定できます。
時間、分、秒を同時に設定し、日付(日/月/年)を保持する方法はありますか?
あなたの声明
上記の方法の問題は、時間単位を1つしか設定できないことです...
正しくありません。 NSCalendarUnit
は、RawOptionSetType
を継承するBitwiseOperationsType
プロトコルに準拠しています。これは、オプションを_&
_および_|
_とビット単位で組み合わせることができることを意味します。
Swift 2(Xcode 7)で、これは再びセットに似たインターフェースを提供するOptionSetType
に変更されました。たとえば- NSCalendarUnitとOR(パイプ)in Swift 2. との組み合わせエラー。
したがって、以下は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)
_
Ifデプロイメントターゲットが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
パラメーターを使用できます。
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;
;-)
SwiftiOS 8以降:人々はCalendarおよびDateFormatterオブジェクトにTimeZoneがあることを忘れがちです。目的のティムゾーンを設定せず、デフォルトのタイムゾーン値が適切でない場合、結果の時間と分がオフになる可能性があります。
注:実際のアプリでは、このコードをさらに最適化できます。
注:タイムゾーンを気にしない場合、結果は1つのデバイスでは問題なく、別のデバイスではタイムゾーン設定が異なるだけで悪い結果になる可能性があります。
注:必ず既存のタイムゾーン識別子を追加してください!このコードは、タイムゾーン名の欠落またはスペルミスを処理しません。
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は、日付オブジェクト自体がタイムゾーン値を保持または気にしないため、一部の書式設定とタイムゾーンの移動に必要です。
気をつけろ!結果の値は、計算チェーンのどこかでタイムゾーンがずれているために異なる可能性があります!
私の意見では、検証は最も簡単ですが、おそらく最も迅速ではない解決策は、文字列を使用することです。
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)
}
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!
}