web-dev-qa-db-ja.com

LocalDateTimeで日付を解析/フォーマットする方法(Java 8)

Java 8では、日付と時刻を処理するための新しい Java.time APIが追加されました( JSR 310 )。

私は文字列として日付と時刻を持っています(例:"2014-04-08 12:30")。与えられた文字列から LocalDateTime のインスタンスを取得するにはどうすればいいですか?

LocalDateTimeオブジェクトの処理を終えた後:LocalDateTimeインスタンスを上記の形式と同じ形式の文字列に変換する方法を教えてください。

289
micha

解析日時

文字列からLocalDateTimeオブジェクトを作成するには、static LocalDateTime.parse() メソッドを使います。パラメータとして文字列と DateTimeFormatter を取ります。 DateTimeFormatterは日付/時刻パターンを指定するために使用されます。

String str = "1986-04-08 12:30";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
LocalDateTime dateTime = LocalDateTime.parse(str, formatter);

フォーマット日時

LocalDateTimeオブジェクトからフォーマットされた文字列を作成するには、format()メソッドを使います。

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
LocalDateTime dateTime = LocalDateTime.of(1986, Month.APRIL, 8, 12, 30);
String formattedDateTime = dateTime.format(formatter); // "1986-04-08 12:30"

DateTimeFormatterで定数として定義されている、よく使われる日付/時刻フォーマットがいくつかあることに注意してください。例:DateTimeFormatter.ISO_DATE_TIMEを使用して上記のLocalDateTimeインスタンスをフォーマットすると、ストリング"1986-04-08T12:30:00"が得られます。

parse()メソッドとformat()メソッドは、すべての日付/時刻関連オブジェクトに使用できます(例:LocalDateまたはZonedDateTime

461
micha

StringISO-8601 format )の場合は、パターンを指定せずにStringLocalDate.parse()またはLocalDateTime.parse()を使用することもできます。

例えば、

String strDate = "2015-08-04";
LocalDate aLD = LocalDate.parse(strDate);
System.out.println("Date: " + aLD);

String strDatewithTime = "2015-08-04T10:11:30";
LocalDateTime aLDT = LocalDateTime.parse(strDatewithTime);
System.out.println("Date with Time: " + aLDT);

出力

Date: 2015-08-04
Date with Time: 2015-08-04T10:11:30

他の日付パターンを処理する必要がある場合にのみDateTimeFormatterを使用します。例えば、dd MMM uuuuは、月の日(2桁)、3文字の月(1月、2月、3月、...)、および4桁の年:

DateTimeFormatter dTF = DateTimeFormatter.ofPattern("dd MMM uuuu");
String anotherDate = "04 Aug 2015";
LocalDate lds = LocalDate.parse(anotherDate, dTF);
System.out.println(anotherDate + " parses to " + lds);

出力

04 Aug 2015 parses to 2015-08-04

DateTimeFormatterオブジェクトは双方向です。入力を解析して出力をフォーマットすることができます。

String strDate = "2015-08-04";
LocalDate aLD = LocalDate.parse(strDate);
DateTimeFormatter dTF = DateTimeFormatter.ofPattern("dd MMM uuuu");
System.out.println(aLD + " formats as " + dTF.format(aLD));

出力

2015-08-04 formats as 04 Aug 2015

(complete DateFormatterのフォーマットと解析のためのパターンのリスト を参照)

  Symbol  Meaning                     Presentation      Examples
  ------  -------                     ------------      -------
   G       era                         text              AD; Anno Domini; A
   u       year                        year              2004; 04
   y       year-of-era                 year              2004; 04
   D       day-of-year                 number            189
   M/L     month-of-year               number/text       7; 07; Jul; July; J
   d       day-of-month                number            10

   Q/q     quarter-of-year             number/text       3; 03; Q3; 3rd quarter
   Y       week-based-year             year              1996; 96
   w       week-of-week-based-year     number            27
   W       week-of-month               number            4
   E       day-of-week                 text              Tue; Tuesday; T
   e/c     localized day-of-week       number/text       2; 02; Tue; Tuesday; T
   F       week-of-month               number            3

   a       am-pm-of-day                text              PM
   h       clock-hour-of-am-pm (1-12)  number            12
   K       hour-of-am-pm (0-11)        number            0
   k       clock-hour-of-am-pm (1-24)  number            0

   H       hour-of-day (0-23)          number            0
   m       minute-of-hour              number            30
   s       second-of-minute            number            55
   S       fraction-of-second          fraction          978
   A       milli-of-day                number            1234
   n       nano-of-second              number            987654321
   N       nano-of-day                 number            1234000000

   V       time-zone ID                zone-id           America/Los_Angeles; Z; -08:30
   z       time-zone name              zone-name         Pacific Standard Time; PST
   O       localized zone-offset       offset-O          GMT+8; GMT+08:00; UTC-08:00;
   X       zone-offset 'Z' for zero    offset-X          Z; -08; -0830; -08:30; -083015; -08:30:15;
   x       zone-offset                 offset-x          +0000; -08; -0830; -08:30; -083015; -08:30:15;
   Z       zone-offset                 offset-Z          +0000; -0800; -08:00;

   p       pad next                    pad modifier      1

   '       escape for text             delimiter
   ''      single quote                literal           '
   [       optional section start
   ]       optional section end
   #       reserved for future use
   {       reserved for future use
   }       reserved for future use
127
Sufiyan Ghori

上記の両方の答えは、文字列パターンに関する質問を非常によく説明しています。ただし、 ISO 8601 を使用している場合に備えて、LocalDateTimeはすでに用意されているので、DateTimeFormatterを適用する必要はありません。

LocalDateTimeをタイムゾーンISO8601文字列に変換する

LocalDateTime ldt = LocalDateTime.now(); 
ZonedDateTime zdt = ldt.atZone(ZoneOffset.UTC); //you might use a different zone
String iso8601 = zdt.toString();

ISO 8601文字列からLocalDateTimeへの変換

String iso8601 = "2016-02-14T18:32:04.150Z";
ZonedDateTime zdt = ZonedDateTime.parse(iso8601);
LocalDateTime ldt = zdt.toLocalDateTime();
31
Marcio Jasinski

日付と時刻を含む文字列を特定の時点(Javaでは " Instant "と呼びます)に解析することは非常に複雑です。 Javaは、これに何度か取り組んでいます。最新のJava.timeJava.time.chronoは、ほとんどすべてのニーズをカバーしています( Time Dilation :)を除く)。

ただし、その複雑さは多くの混乱をもたらします。

日付解析を理解するための鍵は次のとおりです。

Javaに日付を解析する方法がたくさんあるのはなぜですか

  1. 時間を測定するシステムはいくつかあります。たとえば、歴史的な日本のカレンダーは、それぞれの皇帝または王朝の治世の時間範囲から派生しました。そして、例えばUNIXタイムスタンプ。幸いなことに、(ビジネス)世界全体が同じものを使用することができました。
  2. 歴史的には、 さまざまな理由 のために、システムの切り替えが行われていました。例えば。 1582年にユリウス暦からグレゴリオ暦に変更されました。そのため、それより前の「西暦」の日付は別の方法で処理する必要があります。
  3. そしてもちろん、変更はすぐには起こりませんでした。カレンダーはいくつかの宗教の本部からのものであり、ヨーロッパの他の地域では他の食生活を信じていたため、たとえばドイツは1700年まで切り替わりませんでした。

...そして、なぜLocalDateTimeZonedDateTime et al。とても複雑

  1. タイムゾーン があります。タイムゾーンとは、基本的に、地球の表面の「ストライプ」* [1]であり、その当局は、いつどのオフセットを持っているかという同じルールに従います。これには夏時間の規則が含まれます。
    タイムゾーンは、主に誰が誰を征服するかに基づいて、さまざまな地域の時間とともに変化します。また、1つのタイムゾーンのルール 経時変化 も同様です。

  2. 時間オフセットがあります。タイムゾーンは、たとえばタイムゾーンである可能性があるため、タイムゾーンとは異なります。 「プラハ」ですが、夏時間と冬時間のオフセットがあります。
    タイムゾーン付きのタイムスタンプを取得する場合、オフセットは、その年のどの部分に応じて異なる場合があります。うるう時間中、タイムスタンプは2つの異なる時間を意味する場合があるため、追加情報なしで、確実に変換できません。
    注: timestamp は、「日付と時刻、またはオプションでタイムゾーンと時刻オフセットを含む文字列」を意味します。

  3. 複数のタイムゾーンが特定の期間に同じタイムオフセットを共有する場合があります。たとえば、夏時間オフセットが有効でない場合、GMT/UTCタイムゾーンは「ロンドン」タイムゾーンと同じです。

それをもう少し複雑にするために(しかし、それはあなたのユースケースにとってあまり重要ではありません):

  1. 科学者は、時間とともに変化する地球の力学を観察します。それに基づいて、個々の年の終わりに秒を追加します。 (したがって、2040-12-31 24:00:00は有効な日時になる可能性があります。)これには、システムが日付変換を正しく行うために使用するメタデータの定期的な更新が必要です。例えば。 Linuxでは、これらの新しいデータを含むJavaパッケージの定期的な更新を取得します。
  2. 更新は、過去と将来のタイムスタンプの両方で以前の動作を常に保持するとは限りません。したがって、異なるバージョンのソフトウェアで実行している場合、 を比較して、あるタイムゾーンの変更に関する2つのタイムスタンプを解析すると、異なる結果が得られる場合があります これは、影響を受けるタイムゾーンと他のタイムゾーンの比較にも適用されます。

    これによりソフトウェアにバグが発生する場合は、 UNIX timestamp などの複雑なルールのないタイムスタンプの使用を検討してください。

  3. 7のため、将来の日付について、正確に日付を変換することはできません。そのため、たとえば、8524-02-17 12:00:00の現在の解析は、将来の解析から数秒遅れることがあります。

このためのJDKのAPIは、現代のニーズとともに進化しました

  • 初期のJavaリリースにはJava.util.Dateしかありませんでしたが、年、月、日、時刻だけがあると仮定すると、少し素朴なアプローチがありました。これはすぐに十分ではありませんでした。
  • また、データベースのニーズが異なっていたため、かなり早い段階でJava.sql.Dateが導入されましたが、独自の制限がありました。
  • どちらも異なるカレンダーとタイムゾーンをうまくカバーしていないため、Calendar APIが導入されました。
  • これはまだタイムゾーンの複雑さをカバーしていませんでした。それでも、上記のAPIの組み合わせは、本当に苦労しました。したがって、Java開発者がグローバルなWebアプリケーションで作業を開始すると、JodaTimeなどのほとんどのユースケースを対象としたライブラリが急速に普及しました。 JodaTimeは約10年間、事実上の標準でした。
  • しかし、JDKはJodaTimeと統合されなかったため、JDKとの連携は少し面倒でした。そのため、問題へのアプローチ方法に関する非常に長い議論の後、 JSR-310 が作成されました 主にJodaTime に基づいて/。

JavaのJava.timeでの対処方法

タイムスタンプを解析するタイプを決定する

タイムスタンプ文字列を使用する場合、含まれる情報を知る必要があります。 これが重要なポイントです。これが正しくない場合、「インスタントを作成できません」または「ゾーンオフセットが見つかりません」または「不明なゾーンID」など.

日付と時刻が含まれていますか?

  1. 時間オフセットはありますか?
    時間オフセットは+hh:mm部分です。 +00:00は、「Zulu時間」としてZ、協定世界時としてUTC、またはグリニッジ標準時としてGMTに置き換えられる場合があります。これらはタイムゾーンも設定します。
    これらのタイムスタンプには、 OffsetDateTime を使用します。

  2. タイムゾーンはありますか?
    これらのタイムスタンプには、 ZonedDateTime を使用します。
    ゾーンは、

    • 名前(「プラハ」、「太平洋標準時」、「PST」)、または
    • 「ゾーンID」(「アメリカ/ロサンゼルス_」、「ヨーロッパ/ロンドン」)、 Java.time.ZoneId で表されます。

    タイムゾーンのリストは、ICAANが支援する "TZデータベース" によってコンパイルされます。

    ZoneIdのjavadocによると、ゾーンIDは、何らかの方法でZおよびoffsetとして指定することもできます。これが実際のゾーンにどのようにマッピングされるかわかりません。 TZのみを持つタイムスタンプが、うるう時のタイムオフセットの変化に該当する場合、それはあいまいであり、解釈はResolverStyleの対象となります。以下を参照してください。

  3. どちらにもない場合、欠落しているコンテキストが想定または無視されます。そして、消費者が決定する必要があります。したがって、LocalDateTimeとして解析し、欠落している情報を追加してOffsetDateTimeに変換する必要があります。

    • UTC時間であると仮定することができます。 0時間のUTCオフセットを追加します。
    • 変換を行っている場所の時間であると仮定することができます。システムのタイムゾーンを追加して変換します。
    • neglect して、そのまま使用できます。それは便利です2回比較または減算する( Duration を参照)場合、またはわからないが実際には関係ない場合(ローカルバスのスケジュールなど)。

部分時間情報

  • タイムスタンプに含まれるものに基づいて、LocalDateLocalTimeOffsetTimeMonthDayYear、またはYearMonthを取り出すことができます。

完全な情報がある場合は、 Java.time.Instant を取得できます。これは、OffsetDateTimeZonedDateTimeの間の変換にも内部的に使用されます。

解析方法を理解する

DateTimeFormatter には詳細なドキュメントがあり、タイムスタンプ文字列の解析と文字列へのフォーマットの両方が可能です。

pre-created DateTimeFormatters は、すべての標準タイムスタンプ形式をカバーする必要があります。たとえば、ISO_INSTANT2011-12-03T10:15:30.123457Zを解析できます。

特別な形式がある場合は、 独自のDateTimeFormatter (パーサーでもあります)を作成できます。

private static final DateTimeFormatter TIMESTAMP_PARSER = new DateTimeFormatterBuilder()
   .parseCaseInsensitive()
   .append(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SX"))
   .toFormatter();

DateTimeFormatterのソースコードを見て、DateTimeFormatterBuilderを使用してビルドする方法に触発されることをお勧めします。また、パーサーがLENIENT、SMARTまたはSTRICTのいずれであるかを制御するResolverStyleも、フォーマットとあいまいな情報に注目してください。

TemporalAccessor

現在、よくある間違いは、TemporalAccessorの複雑さです。これは、開発者がSimpleDateFormatter.parse(String)を扱うためにどのように使用されたかに由来します。そう、DateTimeFormatter.parse("...")TemporalAccessorを提供します。

// No need for this!
TemporalAccessor ta = TIMESTAMP_PARSER.parse("2011-... etc");

しかし、前のセクションからの知識を備えていれば、必要なタイプに便利に解析できます。

OffsetDateTime myTimestamp = OffsetDateTime.parse("2011-12-03T10:15:30.123457Z", TIMESTAMP_PARSER);

実際にDateTimeFormatterも必要ありません。解析するタイプには、parse(String)メソッドがあります。

OffsetDateTime myTimestamp = OffsetDateTime.parse("2011-12-03T10:15:30.123457Z");

TemporalAccessorに関しては、文字列にどのような情報があるかについて漠然とした考えがあり、実行時に決定したい場合に使用できます。

私はあなたの魂に理解の光を当てることを望みます:)

注:Java.timeからJava 6および7へのバックポートがあります: ThreeTen-Backport 。 Androidの場合、 ThreeTenABP になります。

[1]ストライプではないだけでなく、奇妙な極端な部分もあります。たとえば、 いくつかの隣接する太平洋の島々 には、+ 14:00と-11:00のタイムゾーンがあります。つまり、ある島では5月1日午後3時、別の島ではそれほど遠くない、まだ4月30日PM(正しくカウントした場合:))

4
Ondra Žižka