2018年11月7日水曜日

サマータイム関連のバグ ふたたび

サマータイムに伴うバグを作っていたのを発見!
うう、何回目だろう。気にしているつもりなんだけどなぁー。
参考になる人がいるかもなので、共有しますっ

1. ○分後 を取得する


たとえば、30分後を取得したいとき。

        let date = dateGMT(year: 2018, month: 11, day: 4, hour: 8, minute: 30)
        print("日付:", date)
        let cal = Calendar(identifier: .gregorian)
        var comps = cal.dateComponents([.year, .month, .day, .hour, .minute], from: date)
        comps.minute! += 30
        print("30分後", cal.date(from: comps)!)

とかくと、

        日付: 2018-11-04 08:30:00 +0000
        30分後 2018-11-04 09:00:00 +0000

...となりますが、iPhoneのTime Zoneをサンフランシスコにしていると、

        日付: 2018-11-04 08:30:00 +0000
        30分後 2018-11-04 10:00:00 +0000

になります。
2018-11-04 2:00:00 PDT = 2018-11-04 09:00:00 GMT がサマータイム終了タイミングで、1時間進むためです。
○分後 という絶対秒数を追加するには、素直にdate.addingTimeInterval(1800) と書けっつーことですな。

※ PDT = Pacific Daylight Time = 太平洋 サマータイム時間 UTC-7
    PST = Pacific Standard Time = 太平洋 標準時間 UTC-8


2. カレンダーイベントの登録

カレンダー(EventKit)で、イベント(EKEvent)を登録する際、開始日時と終了日時でサマータイムの有無が変わる場合、注意が必要です。

たとえば、以下の日付のイベントを登録した場合。

開始日時: 2018-11-04 1:30:00 PDT = 2018-11-04 08:30:00 +0000
終了日時: 2018-11-04 1:00:00 PST = 2018-11-04 09:00:00 +0000

        let event = EKEvent(eventStore: eventStore)
        event.calendar = eventCalendar
        event.title = "Test"
        event.startDate = dateGMT(year: 2018, month: 11, day: 4, hour: 8, minute: 30)
        event.endDate = dateGMT(year: 2018, month: 11, day: 4, hour: 9, minute: 00)

        do {
            try eventStore.save(event, span: .thisEvent)
        } catch {
            print(error)
        }

iPhone端末のTime Zoneがサンフランシスコなど(US/Pacific)に設定されていると、以下の日付になってしまいます。

開始日時: 2018-11-04 1:30:00 PDT =  2018-11-04 08:30:00 +0000
終了日時: 2018-11-04 1:00:00 PDT = 2018-11-04 08:00:00 +0000

つまり開始が終了よりも遅い時間に。
ちゃんとDate型で指定しているのだから反応してよーーとは思うんですけどねぇw

ちなみに、iCloudのカレンダーで上記の状態になります。
同期しているGoogleカレンダーに登録すると、終了時刻が勝手に2時間後に置き換わるようです。
開始 > 終了 というのが不正な状態なので、その場合は置き換わるようになっていそう。

開始と終了日付のTime Zoneが別々に指定できたらよいのですが、iPhoneカレンダーは1つのTime Zone (サマータイム有無は現在の日時のものが有効?)の設定のみ。
Googleカレンダーは別々にTime Zoneが設定できるものの、サマータイム有無について設定ができるわけではない。

EKEventのtimeZoneプロパティに TimeZone(secondsFromGMT: 0) を設定すると正しい日時が登録ができます。
が、Googleカレンダーの場合、詳細を見るとGMTでの登録なのがわかる。

どうするのが一番良い方法なのかは分からず。
iPhoneの設定 > カレンダーにある、「Time Zone Override」の設定によっても変わる予感も。
ふむー。
• • •