web-dev-qa-db-ja.com

iOS 11.2.6 DateFormatter.dateは、ブラジリアの夏時間を監視する都市に対してnilを返します

新しいシングルビューアプリプロジェクトを最初から作成し、ViewControllerのviewDidLoadメソッドに次のコードのみを追加しました。

let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MM/yy"
if let date = dateFormatter.date(from: "11/20") {
    print("got the date: \(date)")
} else {
    print("failed getting the date")
}

上記のコードは、世界中のどこでも機能しますexcept観測する都市 ブラジリアサマータイム(BRST)

設定>一般>日付と時刻でデバイスのタイムゾーンを変更することにより、リストされている38のタイムゾーンすべての都市をテストしました ここ )。また、BRST都市がiOS 11.0デバイスで正常に機能することも確認しましたが、not iOS11.2.6です。

また、iOS 11.2.6でも、私が試したほぼ隔月/年の組み合わせで問題なく動作することにも注意してください。 のみ "11/20"と "11/26"は失敗しているようです。

iOS 11.2.6でBRSTを監視する都市でこのコードがnilを返すのはなぜですか?

14
Adam Johns

その1つのタイムゾーン(サンパウロ)で機能しない理由は、そのタイムゾーンに固有の考慮事項、つまり夏時間のためです。ブラジルの夏時間は 11月の第1日曜日 に始まります。これはたまたま2020年の11月1日です。

デフォルトの時刻は存在しないため、nilを返します。

フォーマッターを変更して時間を含め、日付フォーマッターがnilを返し始める正確なタイミングを確認することで、これを試すことができます。

以前に同様の混乱に遭遇したことがあります。日付とタイムゾーンには注意が必要です。

ユーザーに日付を表​​示するために日付フォーマッターを使用している場合、通常はデバイス設定を上書きしないでください。固定の日付フォーマットの使用は避けたいと考えており、日付フォーマッターがnilを返す可能性があることに注意してください。値(救助のためのSwiftオプション)。ただし、APIなどの内部日付に使用している場合は、日付フォーマッターに明示的なロケール/タイムゾーンを設定して、このようなことが起こらないようにすることを検討する必要があります。 たとえば

dateFormatter.locale = Locale(identifier: "en_US_POSIX")
dateFormatter.timeZone = TimeZone(abbreviation: "UTC")
13
shim

シムの答えは的確でしたが、もう少し明確な詳細を提供したいと思います。

2017年12月15日、ブラジルのミシェルテメル大統領は、夏時間(DST)の開始を2018年から始まる11月の第1日曜日に変更する 法令 に署名しました。iOS11.2.6はこの変更を取得するiOSの最初のバージョン。これが、この問題が以前のiOSバージョンでは見られなかった理由です。

11月の最初の日曜日に時間変更が移動する際の問題は、11月の最初の日曜日が(およびisになる可能性があることです。 )2020年と2026年の場合)11月1日。なぜこれが問題になるのですか? DSTが開始されると、ブラジル時計が「早送り」になります 深夜 (午後11時59分から午前1時まで) 。

11月1日の深夜0時に問題が発生するのはなぜですか。ユーザーが入力した月/年のみに基づいてDateオブジェクトを提供するようiOSに要求すると(例:11/20)、Dateオブジェクトはデフォルトで月の最初の日の深夜になります。これは問題があります。なぜなら、ブラジルでDSTが11月1日の深夜に開始される年、その時間は技術的には存在しないからです。したがって、iOSに11/20のDateオブジェクトを提供するように要求すると、iOSは11/01/2020 @ 00:00を返そうとしますが、その時刻が存在しないため、nilを返します。

したがって、この問題は技術的には、月の1日深夜に開始できるDSTを監視する任意のタイムゾーンで発生する可能性があります。いくつかの国を通過してDST規則を確認した後、ブラジルではDSTを月の1日の深夜に開始できるようになっていることがわかりました。

この問題を修正するために、私は効果的に次のことを行いました。

let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MM/yy"
let dateFormatterWithTime = DateFormatter()
dateFormatterWithTime.dateFormat = "MM/yy HH:mm"
if let date = dateFormatter.date(from: "11/20") ??
    dateFormatterWithTime.date(from: "11/20 03:00") {
    print("got the date: \(date)")
} else {
    print("failed getting the date")
}

03:00は、深夜からのDSTの変更を安全に考慮する必要がある将来の任意の時間です。

34
Adam Johns

これを機能させるもう1つの方法は、DateFormatterタイムゾーンをUTCに設定することです。したがって、コードは次のようになります。

let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MM/yy"
dateFormatter.timeZone = TimeZone(abbreviation: "UTC")

if let date = dateFormatter.date(from: "11/20") {
    print("got the date: \(date)")
} else {
    print("failed getting the date")
}

これを行うことにより、コードは以下を出力します。

日付を取得しました:2020-11-01 00:00:00 +0000

1
Felipe Dal Mas