Dart
Flutter

flutterでDateTimeとStringの変換方法とTimeZoneとLocale

概要

DateTime <=> String の変換方法を忘れるのでメモ。
あと、関係するだろうTimeZoneとLocaleについても書いときます。

設定

flutter_localizationsを設定します。
intlライブラリを記述している例がありますが、不要です。
それはflutter_localizationsで含まれます。

pubspec.yaml
  flutter:
    sdk: flutter
  flutter_localizations:
    sdk: flutter

変換方法

例えば、apiの戻り値のString日付をyyyy/MM/dd(E) HH:mm形式にしたい場合。

import "package:intl/intl.dart";
import 'package:intl/date_symbol_data_local.dart';

・・・

  final String sentDateJst;

  get sentDateFormatted  {
    initializeDateFormatting("ja_JP");

    DateTime datetime = DateTime.parse(sentDateJst); // StringからDate

    var formatter = new DateFormat('yyyy/MM/dd(E) HH:mm', "ja_JP");
    var formatted = formatter.format(datetime); // DateからString
    return formatted;
  }

initializeDateFormattingの戻り値がFutureなので迷うと思うが、上記でできます。

TimeZoneについて

dartのDateTimeはimmutableです。なので一度作成されたDateTimeのTimeZoneの変更はできません。
ios/androidで設定されているtimezoneでインスタンスされます。公式に以下と書かれています。

A DateTime object is anchored either in the UTC time zone or in the local time zone of the current computer when the object is created.

Once created, neither the value nor the time zone of a DateTime object may be changed.

例えば、JSTで設定されているOSの場合は、timeZoneNameで取得できる文字列はJSTになります。
toUTc() メソッドを使えば、UTCの DateTime が取得できます。

おそらくほとんどのAPIの結果はUTCだろうから、その場合は、timeZoneName によって分岐し、計算して、DateTime.utc() 名前付きコンストラクタでインスタンスします。(こんなやり方微妙です。他にもっと良い方法がないだろうか?)

import "package:intl/intl.dart";
import 'package:intl/date_symbol_data_local.dart';

・・・

  var utc = DateTime.utc(year, month, day);
  var local = utc.toLocal();

そして現在の端末のtimezoneで表示したい場合は toLocal() メソッドを呼び出し、DateFormatを使ってフォーマットします。

POSTしたい場合は、普通に toUTc() の結果をAPI仕様に合わせてフォーマットすれば良いでしょう。

Localeについて

MaterialAppインスタンス時に指定しておいて、

main.dart
import 'package:flutter_localizations/flutter_localizations.dart';
・・・
  new MaterialApp(
          localizationsDelegates: [
            GlobalMaterialLocalizations.delegate,
            GlobalWidgetsLocalizations.delegate,
          ],
          supportedLocales: [
            const Locale("en", "US"),
            const Locale("ja", "JP")
          ]

以下で取得できます。

Locale locale = Localizations.localeOf(context);

Localeクラスはflutterでは、UIのものっぽいので、厳格なアーキテクチャを望む場合は、enum化するロジックを書いて違うレイヤーに渡すのがベターだと思われます。

ちなみに日本語設定した時に、Androidではja_JPが取得できましたが、iosでは、常にen_USが返ってきました。
そんなバグがあるようです。