Как обрабатывать jodatime Недопустимый момент из-за перехода смещения часового пояса

Я хочу настроить 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();

Спасибо.


person michal.kreuzman    schedule 27.03.2011    source источник
comment
соответствующий: joda-interest.219941. n2.nabble.com/   -  person andersoj    schedule 27.03.2011
comment
Мне всегда кажется, что эти вопросы намного сложнее, чем они должны быть. Внутри Joda представляет мгновенные значения как длинные целые числа UTC в стиле POSIX. Поскольку этот формат времени является простым кумулятивным подсчетом миллисекунд и никогда не корректируется для перехода на летнее время, его внутреннее использование для даты и времени всегда избавляет от проблем с преобразованием летнего времени. Проблема с летним временем амортизируется в функциях преобразования, которые преобразуют момент в локально отформатированные строки с учетом часового пояса, когда его необходимо отобразить пользователю. Такой подход кажется мне гораздо более простым.   -  person scottb    schedule 12.07.2013
comment
посмотрите на мой ответ stackoverflow. ком/вопросы/17665921/   -  person shareef    schedule 24.03.2015


Ответы (5)


Похоже, вы пытаетесь перейти от определенного местного времени к экземпляру DateTime и хотите, чтобы это было устойчивым к переходу на летнее время. Попробуйте это... (обратите внимание, что я нахожусь в США/Востоке, поэтому наша дата перехода была 13 марта 11; мне нужно было найти правильную дату, чтобы получить исключение, которое вы получили сегодня. Обновлен мой код ниже для 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
person andersoj    schedule 27.03.2011
comment
Добавлять час не всегда правильно. В мире есть зоны, которые используют полчаса для перехода на летнее время, и в прошлом были зоны, которые использовали 2 часа. Вероятно, в истории есть и другие разовые прыжки разного масштаба. DateTimeZone.getOffsetFromLocal(long) позволяет использовать базу данных зоны для получения правильного исправления. - person Jeremy Huiskamp; 09.04.2013
comment
Да, это правда. Я был сосредоточен на том, чтобы отметить источник проблемы... Решение этой проблемы зависит от типа выполняемых вами вычислений, контекста приложения и конкретного часового пояса. - person andersoj; 09.04.2013
comment
if (dtz.isLocalDateTimeGap(ldt)){ ldt = ldt.plusHours(1); } - person jet ma; 04.05.2017

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

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

MutableDateTime now = new MutableDateTime(DateTimeZone.UTC);
person josh3736    schedule 27.03.2011
comment
Тогда выбранный ответ может вызвать проблемы? - person falsarella; 25.09.2012
comment
@falsarella: я не уверен, что вы пытаетесь спросить. - person josh3736; 26.09.2012
comment
Вы сказали, что в центральноевропейском времени тоже летнее время. Так что ответ от @andersoj (см. выше) тоже вызовет проблемы! Таким образом, это не решит ситуацию ОП (и мою). Имея это в виду, единственный правильный ответ - ваш, используя часовой пояс даты UTC. - person falsarella; 26.09.2012
comment
ОП подразумевал, что их намерение состояло в том, чтобы конкретно установить местное время. Хотя вы правы, выполнение действий из UTC позволяет избежать особенностей перемещения локальных смещений, но не дает ответа на вопрос, как что-то сделать в 2 часа ночи в МОЕМ часовом поясе. - person andersoj; 09.04.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);
}

Я проверял это в восточном и западном полушариях, а также в зоне острова Лорд-Хау, где время летнего времени получасовое.

Было бы неплохо, если бы форматировщики joda поддерживали setStrict (boolean), чтобы они позаботились об этом для вас...

person Jeremy Huiskamp    schedule 12.07.2013

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

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

Работал для меня отлично. Может кому понадобится.

person Crushnik    schedule 13.12.2016

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

DateTimeFormatter formatter = DateTimeFormat.forPattern("dd/MM/yyyy");
LocalDate.parse(date, formatter);
person Jorge Washington    schedule 13.10.2017