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 が取得できます。
timeZoneOffset の戻り値は、Duration クラスで inHours を呼べばJSTの場合は9が帰ってきます。
DateTimeのaddメソッドでは、この Durationを指定できます。

DateTime hoge = DateTime.now().add(duration);

もし、APIの戻り値で年月日を別々に保持している場合でも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を使ってフォーマットします。

UTCで保持しているAPIに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が返ってきました。
そんなバグがあるようです。