iOSの設定 > 日付と時刻
には悪魔の設定があります。
そう、12時間表示です
今回はDateFormatter
を使っていて陥った12時間表示の罠と、その解決策を備忘録としてまとめました。
DateFormatterの落とし穴
わかりやすくDate型
を下記のようにDateFormatter
を使って時間だけを表示したい場合
を考えたい思います。
let formatter = DateFormatter()
formatter.timeZone = .current
formatter.locale = .current
formatter.timeStyle = .short
formatter.dateStyle = .none
let timeLabel = formatter.string(from: Date())
print(timeLabel)
// 理想の出力) "23:59"
12時間表示
ユーザが12時間表示設定している端末では、上記の出力は
//端末は12時間表示設定
let formatter = DateFormatter()
formatter.timeZone = .current
formatter.timeStyle = .short
formatter.dateStyle = .none
formatter.locale = Locale(identifier: "ja_JP")
let timeLabel = formatter.string(from: Date())
print(timeLabel)
// 出力) "午後11:59"
formatter.locale = Locale(identifier: "en_US")
let timeLabel2 = formatter.string(from: Date())
print(timeLabel2)
// 出力) "PM11:59"
なんだか余計な"午後"や"PM"といった文字がくっついてきますね。
textLabelの長さが言語によって変わってしまうのでUIによっては不便だと思います。
冒頭に書いた純正カレンダー内でも、書き込み時にGoogle Calender APIに渡すDate型の文字列
がバグってしまってることが想像できます。
"hh:mm"とか"HH:mm"とかで指定できなかったっけ?
まず考えたのが、なんかフォーマットを大文字の"HH"で指定したら24時間表示にならなかったっけ?
ということです。
実際ICU User Guideを参照すると
Symbol | Meaning | Example(s) | output |
---|---|---|---|
h | hour in am/pm (1~12) | h,hh | 7,07 |
H | hour in day (0~23) | H,HH | 0,00 |
k | hour in day (1~24) | k,kk | 24,24 |
K | hour in am/pm (0~11) | K,KK | 0,00 |
HH
やkk
なんかが使えそうです。
あとは、
formatter.dateFormat = "HH:mm"
のようにフォーマットを指定すればうまくいきそうですね。
.dateFormat
を指定するのはアンチパターンだというのは有名ですが、
時間を抜き出す目的ならば問題なさそうです。
しかし、結論から言えばこれは失敗します!
これでは午前・午後
がついたままで、出力は先ほどと同様です。(これが仕様なのかはわからないです)
解決策
.locale
にNSLocale.system
を指定することで上手くいきます。
定義をNSLocale.locale
と比べてみると
.system
A locale representing the generic root values with little localization..locale
A locale representing the user's region settings at the time the property is read.
とあり、どうやらgeneric root values
が24時間表記で固定されているようです。
//端末は12時間表示設定
let formatter = DateFormatter()
formatter.timeZone = .current
formatter.timeStyle = .short
formatter.dateStyle = .none
formatter.locale = NSLocale.system
let timeLabel = formatter.string(from: Date())
print(timeLabel)
// 出力) "11:59"