Как справиться с йодатимом Нелегальный момент из-за перехода смещения часового пояса - PullRequest
43 голосов
/ 27 марта 2011

Я хочу настроить joda DateTime на сегодня в 2 часа ночи (см. Пример кода ниже).Но я получаю это исключение:

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 ]

35 голосов
/ 27 марта 2011

Похоже, вы пытаетесь перейти от определенного местного времени к экземпляру DateTime и хотите, чтобы оно было устойчивым к летнему времени.Попробуйте это ... (обратите внимание, что я нахожусь в США / Восточная, поэтому наша дата перехода была 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
12 голосов
/ 27 марта 2011

CET переключается на DST (летнее время) в последнее воскресенье марта, которое происходит сегодня.Время прошло с 1:59:59 до 3:00:00 - нет 2, отсюда исключение.

Вы должны использовать UTC вместо местного времени, чтобы избежать такого рода проблем с часовым поясом.

MutableDateTime now = new MutableDateTime(DateTimeZone.UTC);
7 голосов
/ 12 июля 2013

Я думаю, что большую часть времени вы захотите, чтобы 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 (булево), которое заставило бы их позаботиться об этом за вас ...

4 голосов
/ 13 декабря 2016

Если вам нужно разобрать дату из строки:

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

Отлично сработало для меня. Может, кому-то это понадобится.

0 голосов
/ 14 октября 2017

Обновите до jodatime 2.1 и используйте LocalDate.parse():

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