Flutterで「〇〇をした時間を保存しておきたい」という場面があると思います。
処理としてはDateTimeで現在時刻を取得し、Stringに変換して保存、
取り出すときはStringからDateTimeに変換して利用します。
公式ドキュメントを読みつつ、実際にどういう動きをするのか確認していきます。
https://api.dart.dev/stable/3.0.3/dart-core/DateTime-class.html
DateTimeからStringへの変換方法
Create a DateTime object by using one of the constructors or by parsing a correctly formatted string, which complies with a subset of ISO 8601.
単純に訳すと
DateTimeコンストラクターを使用またはISO8601のサブセットに準拠する正しくフォーマットされた文字列を解析することによって、オブジェクトを作成します。
とあります。
ドキュメント記載のようにtoIso8601String()
というメソッドを利用するとISO8601に変換をすることができるため、こちらを用いてStringに変換します。
ISO8601変換時にタイムゾーンを考慮するには
toIso8601String()をそのまま利用するだけではタイムゾーン考慮はされません。
②のように、String変換前にtoUtc()
のメソッドを利用しUTC変換することで、タイムゾーン考慮がされます。
コードで見てみると下記のようになります。
final now = DateTime.now(); // この時点でローカルタイムのJSTが適用される
print(now.toIso8601String()); // ①2023-06-08T09:31:35.001
print(now.toUtc().toIso8601String()); // ②2023-06-08T00:31:35.001Z
①ではJSTで変換されています。
②では末尾に「Z」が入っていますので、UTCの時間帯だと言う情報が入っていることがわかります。
StringからDateTimeへの変換方法
戻す時はtoLocal()
を利用してローカルタイムに戻すことを忘れないようにしましょう。
print(DateTime.parse(now.toUtc().toIso8601String()).toLocal()); // 2023-06-08 09:49:31.199
toLocal()を忘れるとUTCが考慮されているISO8601はUTCの時間でDateTimeに変換されます。
final now = DateTime.now();
print(DateTime.parse(now.toUtc().toIso8601String())); // 2023-06-08 00:49:31.199Z
ではここでDateTime->String時にUTCへ変換しなかった場合はどうなるのでしょうか?
final now = DateTime.now();
final nowString = now.toIso8601String();
print(DateTime.parse(nowString)); // 2023-06-08 09:49:31.199
print(DateTime.parse(nowString).toLocal()); // 2023-06-08 09:49:31.199
utcでString形式にしなかった場合は
toLocal()
をつけても、つけなくても同様にローカルタイムになります。
String変換時にローカルタイムで保存される影響です。
一見これでいいのでは?と思いますが、端末の時刻に左右されるため注意が必要です。
DateTime→Stringの変換の時に端末の時刻がGMT+8(時刻は09:49:31)
String→DateTimeの変換の時に端末の時刻がGMT+9(時刻は10:49:31)だった場合は下記のようになり時間が正しく表示することができません
// GMT+8(シンガポールなど)の設定に変更する
final now = DateTime.now();
final nowString = now.toIso8601String(); // 2023-06-08 09:49:31.199
// GMT+9(日本など)の設定に変更する
print(DateTime.parse(nowString)); // ③2023-06-08 09:49:31.199
print(DateTime.parse(nowString).toLocal()); // 2023-06-08 09:49:31.199
③の時点で、GMT+9を考慮し2023-06-08 10:49:31.199にならないといけないですが09:49:31.199のままになってしまいます。String変換時にUTCを考慮していないためtoLocal()をしても同様の結果になります。
結論
DateTimeからiso8601形式にするときはtoUtc()
でutcの時間にしてから保存しましょう。
iso8601形式からDateTimeに戻すときはtoLocal()
をつけて端末の時刻を保存しましょう。