背景
電車の時刻表のように日時が配列形式でズラーっと並んでいるようなJSONをAPIから取得する画面を実装していました。
その際に、ブラウザ上でAPIを叩いた時とFlutter側で画面表示した際の日時にズレが生じていることに気付きました。
あ、Flutter側で受け取るタイミングでDateTimeがUTC形式に変換されてるっぽい。
— うめちゃん (@umechanhika) 2019年7月23日
23日5時が22日20時になってる。
原因
json_serializableで日付をシリアライズすると内部ではDateTime.parse(String formattedString)
が呼ばれます。
このparse
メソッドですが、実装を見てみるとタイムゾーンがUTC以外の場合にはUTC形式に変換するような処理が入っていました。
static DateTime parse(String formattedString) {
// 省略
if (match[8] != null) {
// timezone part
isUtc = true;
if (match[9] != null) {
// timezone other than 'Z' and 'z'.
int sign = (match[9] == '-') ? -1 : 1;
int hourDifference = int.parse(match[10]);
int minuteDifference = parseIntOrZero(match[11]);
minuteDifference += 60 * hourDifference;
minute -= sign * minuteDifference;
}
}
// 省略
}
DateTime.parseのコメント。
— うめちゃん (@umechanhika) 2019年7月23日
英語に自信がないけど、UTC以外のタイムゾーンを指定していた場合も、UTCに変換されるってことかな? pic.twitter.com/1ycvJy9HsV
https://t.co/uGnrQ1JuwD
— うめちゃん (@umechanhika) 2019年7月23日
タイムゾーンオフセットのパースには対応しているけど、データはUTCになるよってことか。
対応
json_serializableではシリアライズ処理に独自メソッドを指定することができるので、一度シリアライズされたDateTimeを端末ローカルのタイムゾーンに変換するように修正したところ、UTCからJSTに変換することができました。
@JsonSerializable()
class SampleData {
@JsonKey(fromJson: parseDateTime)
final DateTime dateTime;
static parseDateTime(dynamic value) => DateTime.parse(value as String).toLocal();
}