web-dev-qa-db-ja.com

タイムゾーンオフセット遷移によるjodatimeの不正なインスタントの処理方法

今日の午前2時にjoda DateTimeをセットアップしたい(以下のサンプルコードを参照)。しかし、私はこの例外を受け取っています:

Exception in thread "main" org.joda.time.IllegalFieldValueException: Value 2 for hourOfDay is not supported: Illegal instant due to time zone offset transition: 2011-03-27T02:52:05.239 (Europe/Prague)
at org.joda.time.chrono.ZonedChronology$ZonedDateTimeField.set(ZonedChronology.Java:469)
at org.joda.time.MutableDateTime.setHourOfDay(MutableDateTime.Java:702)

上記の例外を処理する、または特定の時間にDateTimeを作成する正しい方法は何ですか?

サンプルコード:

MutableDateTime now = new MutableDateTime();
now.setHourOfDay(2);
now.setMinuteOfHour(0);
now.setSecondOfMinute(0);
now.setMillisOfSecond(0);
DateTime myDate = now.toDateTime();

ありがとう。

43
michal.kreuzman

特定の現地時間からDateTimeインスタンスに到達しようとしているようで、それを夏時間に対して堅牢にしたいようです。これを試してみてください(私は米国/東部にいるため、移行日は11 3月13日でした。今日の例外を取得するために適切な日付を見つけなければなりませんでした。 )ここでの洞察は、Jodaが LocalDateTime を提供して、ローカルのウォールクロック設定について、またタイムゾーンで合法かどうかを判断できるようにすることです。この場合、時間が存在しない場合は1時間を追加します(これが適切なポリシーであるかどうかはアプリケーションで判断する必要があります)。

import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.LocalDateTime;

class TestTz {

  public static void main(String[] args)
  {
     final DateTimeZone dtz = DateTimeZone.forID("CET");
     LocalDateTime ldt = new LocalDateTime(dtz)
       .withYear(2011)
       .withMonthOfYear(3)
       .withDayOfMonth(27)
       .withHourOfDay(2);

    // this is just here to illustrate I'm solving the problem; 
    // don't need in operational code
    try {
      DateTime myDateBorken = ldt.toDateTime(dtz);
    } catch (IllegalArgumentException iae) {
      System.out.println("Sure enough, invalid instant due to time zone offset transition!");
    }

    if (dtz.isLocalDateTimeGap(ldt)) {
      ldt = ldt.withHourOfDay(3);
    }

    DateTime myDate = ldt.toDateTime(dtz);
    System.out.println("No problem: "+myDate);
  }

}

このコードは以下を生成します。

タイムゾーンオフセットの移行のため、案の定、無効な瞬間!
問題なし:2011-03-27T03:00:00.000 + 02:00 
35
andersoj

[〜#〜] cet [〜#〜] 3月の最終日曜日にDST(夏時間)に切り替わります。時間は1:59:59から3:00:00になりました。2はないため、例外です。

この種のタイムゾーンの問題を回避するには、現地時間ではなくUTCを使用する必要があります。

MutableDateTime now = new MutableDateTime(DateTimeZone.UTC);
12
josh3736

多くの場合、jodaがこれを自動的に修正してくれると思います。ギャップのサイズはゾーンと年に依存するため、ギャップの日付を修正する正しい方法がわからないことがよくあります(もちろん通常は1時間です)。

この1つの例は、制御していないソースからのタイムスタンプを解析する場合です。たとえば、ネットワーク。タイムスタンプの送信者に古いゾーンファイルがある場合、これが発生する可能性があります。 (古いゾーンファイルがある場合、ほとんど混乱しています)。

これを行う方法を次に示しますが、それは少し複雑です。私たちの環境ではたまたま1.6にとどまっているため、joda 1.6と2.xで動作するようにしました。

質問のように他の入力から日付を作成する場合は、UTC日付または上記のLocalDateで開始し、これを調整してオフセットを自動的に修正できます。特別なソースはDateTimeZone.convertLocalToUTC

危険な:

public DateTime parse(String str) {
    formatter.parseDateTime(gapdate)
}

安全:

public DateTime parse(String str) {
    // separate date from zone; you may need to adjust the pattern,
    // depending on what input formats you support
    String[] parts = str.split("(?=[-+])");
    String datepart = parts[0];
    DateTimeZone zone = (parts.length == 2) ?
        DateTimeZone.forID(parts[1]) : formatter.getZone();

    // parsing in utc is safe, there are no gaps
    // parsing a LocalDate would also be appropriate, 
    // but joda 1.6 doesn't support that
    DateTime utc = formatter.withZone(DateTimeZone.UTC).parseDateTime(datepart);

    // false means don't be strict, joda will Nudge the result forward by the
    // size of the gap.  The method is somewhat confusingly named, we're
    // actually going from UTC to local
    long millis = zone.convertLocalToUTC(utc.getMillis(), false);

    return new DateTime(millis, zone);
}

これは、東半球と西半球、およびロードハウアイランドゾーンでテストしました。

JodaフォーマッタがsetStrict(boolean)をサポートしていれば、それはあなたに代わってこれを処理してくれます。

7
Jeremy Huiskamp

文字列から日付を解析する必要がある場合:

final DateTimeZone dtz = DateTimeZone.getDefault(); //DateTimeZone.forID("Europe/Warsaw")
LocalDateTime ldt = new LocalDateTime("1946-04-14", dtz);
if (dtz.isLocalDateTimeGap(ldt)){
    ldt = ldt.plusHours(1);
}
DateTime date = ldt.toDateTime();
Date date = date.toDate();

私のために完璧に働いた。たぶん誰かがそれを必要とするでしょう。

4
Crushnik

Jodatime 2.1に更新し、LocalDate.parse()を使用します。

DateTimeFormatter formatter = DateTimeFormat.forPattern("dd/MM/yyyy");
LocalDate.parse(date, formatter);
0