如何处理jodatime由于时区偏移过渡而导致的非法瞬发

时间:2011-03-27 18:01:00

标签: java 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();

感谢。

5 个答案:

答案 0 :(得分:35)

您似乎正试图从特定的本地时间到DateTime实例,并且您希望它能够抵御夏令时。试试这个...(注意我在美国/东部,所以我们的过渡日期是3月13日;我必须找到正确的日期来获得你今天获得的例外。更新了我的下面的CET代码,今天转换。这里的见解是,Joda提供LocalDateTime让您了解当地的挂钟设置,以及它是否在您的时区合法。在这种情况下,如果时间不存在,我只需添加一小时(您的应用程序必须确定这是否是正确的策略。)

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);
  }

}

此代码生成:

Sure enough, invalid instant due to time zone offset transition!
No problem: 2011-03-27T03:00:00.000+02:00

答案 1 :(得分:12)

CET在3月的最后一个星期天切换到夏令时(夏令时),恰好是今天。时间从1:59:59到3:00:00 - 没有2,因此例外。

您应该使用UTC而不是本地时间来避免这种时区问题。

MutableDateTime now = new MutableDateTime(DateTimeZone.UTC);

答案 2 :(得分:7)

我想很多时候,你会希望joda自动为你解决这个问题。您通常不知道修正缺口日期的正确方法,因为缺口的大小取决于区域和年份(当然通常是一小时)。

这方面的一个例子是,如果您正在解析来自您无法控制的来源的时间戳;例如,网络。如果时间戳的发件人具有过期的区域文件,则可能发生这种情况。 (如果你有过时的区域文件,你几乎搞砸了。)

这是一种做到这一点的方法,这个方法稍微复杂一些。我已经在joda 1.6以及2.x中使用它,因为我们恰好在我们的环境中陷入1.6。

如果您正在根据问题建立其他输入日期,则可以按照上面的建议开始使用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);
}

我在东半球和西半球以及豪勋爵岛区进行了测试,这个区恰好有半小时dst。

如果joda格式化程序支持setStrict(boolean)会让他们为你处理这个问题,那会很好...

答案 3 :(得分:4)

如果您需要从字符串解析日期:

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 :(得分:0)

更新到jodatime 2.1并使用LocalDate.parse()

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