久しぶりにJavaのコードを書いていて、タイムゾーンを含む文字列のパース・整形のルールが分かりにくかったので調べた結果をメモに残します。
課題
以下のような日時文字列それぞれパースするとき、また出力したいとき DateTimeFormatter
として何を使えばよいのか?
-
ISO_DATE_TIME
ISO_ZONED_DATE_TIME
ISO_OFFSET_DATE_TIME
のどれかでよいのか - パターンとして
yyyy-MM-dd'T'HH:mm:ssZ
の最後のZ
の部分を指定するならば何?
2023-01-02T04:05:06Z
2023-01-02T04:05:06+0900
2023-01-02T04:05:06+09:00
結論
-
パースするときは、登場する可能性がある形式が以下のどちらかなのかでで判定してパターンを切り替える
-
+09:00
orZ
ならばZZZZZ
またはISO_*
を使う(どれでもよい)- ただし
+0900
がくると扱いがややこしい
- ただし
-
+0900
orZ
ならばX
を使う- こちらを採用しておき、
+09:00
の対応のために「入力の末尾が:
ならそれを削除する」という前処理がよさそう
- こちらを採用しておき、
-
-
整形するとき
-
+0900
と出したい時だけZ
、それ以外はZZZZZ
またはISO_*
- ただし
Z
を使うと UTCはZ
ではなく+0000
になる - つまり元の日時のタイムゾーンによって
+0900
とZ
を出し分けて出力することはできない
- ただし
-
その他
-
ZZZ
やZZZZ
を出力につかうとGMT
が出現したりする (JST
と出すことはむつかしそう)
検証コードと結果
Main.java
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
public class Main {
SortedMap<String, DateTimeFormatter> patterns = new TreeMap<>(Map.of(
"z", DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZ"),
"zz", DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZZ"),
"zzz", DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZZZ"),
"zzzz", DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZZZZ"),
"zzzzz", DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZZZZZ"),
"x", DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssX"),
"iso", DateTimeFormatter.ISO_DATE_TIME,
"iso_zoned", DateTimeFormatter.ISO_ZONED_DATE_TIME,
"iso_offset", DateTimeFormatter.ISO_OFFSET_DATE_TIME));
List<String> inputs = List.of(
"2023-01-02T04:05:06",
"2023-01-02T04:05:06Z",
"2023-01-02T04:05:06+0900",
"2023-01-02T04:05:06+09:00");
String testParse() {
var result = "";
for (var i : inputs) {
result += i + " for\n";
for (var e : patterns.entrySet()) {
result += " " + e.getKey() + " => ";
try {
result += ZonedDateTime.parse(i, e.getValue());
} catch (Exception ex) {
result += ex.toString();
}
result += "\n";
}
result += "\n";
}
return result;
}
String testFormat() {
var utc = ZonedDateTime.parse("2023-01-02T04:05:06+00:00");
var jst = ZonedDateTime.parse("2023-01-02T04:05:06+09:00");
var inputs = List.of(utc, jst);
var result = "";
for (var i : inputs) {
result += i + ":\n";
for (var e : patterns.entrySet()) {
result += " " + e.getKey() + " => " + i.format(e.getValue()) + "\n";
}
}
return result;
}
public static void main(String[] args) {
System.out.println(new Main().testParse());
System.out.println(new Main().testFormat());
}
}
結果
2023-01-02T04:05:06 for
iso => java.time.format.DateTimeParseException: Text '2023-01-02T04:05:06' could not be parsed: Unable to obtain ZonedDateTime from TemporalAccessor: {},ISO resolved to 2023-01-02T04:05:06 of type java.time.format.Parsed
iso_offset => java.time.format.DateTimeParseException: Text '2023-01-02T04:05:06' could not be parsed at index 19
iso_zoned => java.time.format.DateTimeParseException: Text '2023-01-02T04:05:06' could not be parsed at index 19
x => java.time.format.DateTimeParseException: Text '2023-01-02T04:05:06' could not be parsed at index 19
z => java.time.format.DateTimeParseException: Text '2023-01-02T04:05:06' could not be parsed at index 19
zz => java.time.format.DateTimeParseException: Text '2023-01-02T04:05:06' could not be parsed at index 19
zzz => java.time.format.DateTimeParseException: Text '2023-01-02T04:05:06' could not be parsed at index 19
zzzz => java.time.format.DateTimeParseException: Text '2023-01-02T04:05:06' could not be parsed at index 19
zzzzz => java.time.format.DateTimeParseException: Text '2023-01-02T04:05:06' could not be parsed at index 19
2023-01-02T04:05:06Z for
iso => 2023-01-02T04:05:06Z
iso_offset => 2023-01-02T04:05:06Z
iso_zoned => 2023-01-02T04:05:06Z
x => 2023-01-02T04:05:06Z
z => java.time.format.DateTimeParseException: Text '2023-01-02T04:05:06Z' could not be parsed at index 19
zz => java.time.format.DateTimeParseException: Text '2023-01-02T04:05:06Z' could not be parsed at index 19
zzz => java.time.format.DateTimeParseException: Text '2023-01-02T04:05:06Z' could not be parsed at index 19
zzzz => java.time.format.DateTimeParseException: Text '2023-01-02T04:05:06Z' could not be parsed at index 19
zzzzz => 2023-01-02T04:05:06Z
2023-01-02T04:05:06+0900 for
iso => java.time.format.DateTimeParseException: Text '2023-01-02T04:05:06+0900' could not be parsed, unparsed text found at index 19
iso_offset => java.time.format.DateTimeParseException: Text '2023-01-02T04:05:06+0900' could not be parsed, unparsed text found at index 22
iso_zoned => java.time.format.DateTimeParseException: Text '2023-01-02T04:05:06+0900' could not be parsed, unparsed text found at index 22
x => 2023-01-02T04:05:06+09:00
z => 2023-01-02T04:05:06+09:00
zz => 2023-01-02T04:05:06+09:00
zzz => 2023-01-02T04:05:06+09:00
zzzz => java.time.format.DateTimeParseException: Text '2023-01-02T04:05:06+0900' could not be parsed at index 19
zzzzz => java.time.format.DateTimeParseException: Text '2023-01-02T04:05:06+0900' could not be parsed at index 19
2023-01-02T04:05:06+09:00 for
iso => 2023-01-02T04:05:06+09:00
iso_offset => 2023-01-02T04:05:06+09:00
iso_zoned => 2023-01-02T04:05:06+09:00
x => java.time.format.DateTimeParseException: Text '2023-01-02T04:05:06+09:00' could not be parsed, unparsed text found at index 22
z => java.time.format.DateTimeParseException: Text '2023-01-02T04:05:06+09:00' could not be parsed at index 19
zz => java.time.format.DateTimeParseException: Text '2023-01-02T04:05:06+09:00' could not be parsed at index 19
zzz => java.time.format.DateTimeParseException: Text '2023-01-02T04:05:06+09:00' could not be parsed at index 19
zzzz => java.time.format.DateTimeParseException: Text '2023-01-02T04:05:06+09:00' could not be parsed at index 19
zzzzz => 2023-01-02T04:05:06+09:00
2023-01-02T04:05:06Z:
iso => 2023-01-02T04:05:06Z
iso_offset => 2023-01-02T04:05:06Z
iso_zoned => 2023-01-02T04:05:06Z
x => 2023-01-02T04:05:06Z
z => 2023-01-02T04:05:06+0000
zz => 2023-01-02T04:05:06+0000
zzz => 2023-01-02T04:05:06+0000
zzzz => 2023-01-02T04:05:06GMT
zzzzz => 2023-01-02T04:05:06Z
2023-01-02T04:05:06+09:00:
iso => 2023-01-02T04:05:06+09:00
iso_offset => 2023-01-02T04:05:06+09:00
iso_zoned => 2023-01-02T04:05:06+09:00
x => 2023-01-02T04:05:06+09
z => 2023-01-02T04:05:06+0900
zz => 2023-01-02T04:05:06+0900
zzz => 2023-01-02T04:05:06+0900
zzzz => 2023-01-02T04:05:06GMT+09:00
zzzzz => 2023-01-02T04:05:06+09:00