【Swift】TimeZone初期化時の注意点 【サマータイム】


TimeZoneの初期化

日本時間はグリニッジ標準時となるGMTより +9時間 した時間となります。
swiftのDate型はGMT基準となるため、DateFormatter等で文字列に変換する場合
TimeZoneを日本時間に指定する必要があります。

TimeZoneの初期化には、identifierの指定と、GMTからの時間差で指定する方法があり、
どちらにしても結果的に時間を +9時間調整するので、好きな方を使って構いません。

identifier
TimeZone(identifier: "Asia/Tokyo")
GMT
TimeZone(secondsFromGMT: 9 * 60 * 60) // 9時間分の秒数

TimeZone - Foundation | Apple Developer Documentation
https://developer.apple.com/documentation/foundation/timezone

TimeZoneの動作検証

と思ってたんですが、
実は、それぞれの指定で時間の表現に差が出てしまうのです。。。

試しに、UIDatePickerで設定した時間をラベルに表示しながら確認してみましょう。。

1970 年 1 月 1 日

では、まず UNIX時間の下限値である 1970年から。
1970/1/1 00:00:00

上部のラベルは、DatePickerで選択した日付をDateFormatterでStringに変換して
表示しています。

また、DatePickerのTimeZoneはidentifier: "Asia/Tokyo"で指定しており、
DateFormatterはsecondsFromGMT: 9 * 60 * 60で指定しています。

どちらも+9時間調整なので、表記は当然1970/1/1 00:00:00となっています。

1955 年 2 月 24 日

更に時をさかのぼり、iPhoneを作ったスティーブジョブスの誕生日にしてみましょう。
1955/2/24 00:00:00

こちらも、問題なく1955/2/24 00:00:00となっています。

1951 年 9 月 8 日

次はApple本社付近のサンフランシスコにあやかり、
サンフランシスコ講和条約が締結された日にさかのぼってみます。
1951/9/8 00:00:00

!!??
なんということでしょう!!
ラベルへの変換が1951/9/7 23:00:00となってしましました・・・

どちらも日本時間なのはずなのに。。

日本におけるサマータイム

というわけで、identifierでの指定とsecondsFromGMTの指定で
1時間の差異がでてしまいました。

実は 1950年あたりで日本ではサマータイムが採用されていたため、
この差が生まれるようです。

70年前の日本で実施されていた「サマータイム」が3年で廃止された理由
https://www.buzzfeed.com/jp/kotahatachi/summer-time-1948

具体的に説明すると、identifierでの指定の場合、日本という地域での指定になります。
一方、GMTでの指定の場合、標準時との時間差での指定になるので、日本という情報がありません。
そのため、サマータイム調整が発生しないようです。

サマータイムの期間

サマータイムの具体的な期間については、国立天文台のwikiにまとめてありました。

期間
昭和23年 (1948) 5/1 12:00 ~ 9/12 0:00
昭和24年 (1949) 4/2 12:00 ~ 9/11 0:00
昭和25年 (1950) 5/6 12:00 ~ 9/10 0:00
昭和26年 (1951) 5/5 12:00 ~ 9/9 0:00

暦Wiki/時刻/夏時刻 - 国立天文台暦計算室
https://eco.mtk.nao.ac.jp/koyomi/wiki/BBFEB9EF2FB2C6BBFEB9EF.html

identifier: "Asia/Tokyo"でこの期間を利用した場合、
サマータイムと認識され、1時間マイナスされてしまいます。。
(サマータイム期間なので1時間足されているはずという前提で 結果1時間引いた値になる)

以前、2020年の東京オリンピックに向けてサマータイムを導入しようと言う議論がありましたが、
実はすでに日本で採用された実績があり、結果的に辞めて欲しい意見が多く4年で終了していたんですね。。

なぜこれをまた採用しようと思ったのか・・・

と、それはどうでもよく、日本にはサマータイムがあったため、
TimeZoneの指定には気をつけましょうという話でした。

特別な理由がなければ、TimeZoneの初期化時はGMTで統一しておいた方が
変な問題に悩まされなくて良いかもですね。

今回実験したViewControllerのコード

ViewController.swift

import UIKit

class ViewController: UIViewController {

    let adjustSecondsForJapan : TimeInterval = 9 * 60 * 60

    @IBOutlet weak var datePicker: UIDatePicker!
    @IBOutlet weak var label: UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()

        setupDatePicker()
    }

    private func setupDatePicker() {
        datePicker.datePickerMode = .date
        datePicker.timeZone = TimeZone(identifier: "Asia/Tokyo")
        datePicker.date = Date(timeIntervalSince1970: -adjustSecondsForJapan)
        datePicker.locale = Locale(identifier: "ja-JP")
    }

    private func getDateStrings(from date: Date) -> String {
        let formatter = DateFormatter()
        formatter.timeZone = TimeZone(secondsFromGMT: Int(adjustSecondsForJapan))
        formatter.dateFormat = "yyyy/MM/dd HH:mm:ss"
        return formatter.string(from: date)

    }

    @IBAction func didChangeDatePicker(_ sender: UIDatePicker) {

        label.text = getDateStrings(from: sender.date)

    }
}

github

becky3/timezone_summertime_test: 日本におけるサマータイムの影響について確認するプロジェクト
https://github.com/becky3/timezone_summertime_test