web-dev-qa-db-ja.com

NSDateオブジェクトを真夜中に設定するにはどうすればよいですか?

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. 時間

時間、分、秒を同時に設定し、日付(日/月/年)を保持する方法はありますか?

50
AstroCB

あなたの声明

上記の方法の問題は、時間単位を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)
_
84
Martin R

はい。

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

以下は、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;

;-)

8
Mike Gledhill

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は、日付オブジェクト自体がタイムゾーン値を保持または気にしないため、一部の書式設定とタイムゾーンの移動に必要です。

気をつけろ!結果の値は、計算チェーンのどこかでタイムゾーンがずれているために異なる可能性があります!

5
t1ser

誰かがこれを探している場合に備えて:

SwiftDate を使用すると、これを行うことができます:

Date().atTime(hour: 0, minute: 0, second: 0)
2
Francois Nadeau

私の意見では、検証は最も簡単ですが、おそらく最も迅速ではない解決策は、文字列を使用することです。

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)
}
1
Jason
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!
}
0
Frank Hou