Как обрабатывать 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();
Спасибо.
Ответы
Ответ 1
Похоже, вы пытаетесь перейти с определенного локального времени к экземпляру 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
Ответ 2
CET переключается на летнее время (DST) в последнее воскресенье марта, которое сегодня и сегодня. Время шло от 1:59:59 до 3:00:00 – там нет 2, поэтому исключение.
Вы должны использовать UTC вместо локального времени, чтобы избежать такой проблемы с часовым поясом.
MutableDateTime now = new MutableDateTime(DateTimeZone.UTC);
Ответ 3
Я думаю, что много времени, вы захотите, чтобы Джода исправила это автоматически для вас. Вы часто не знаете правильного способа исправить промежуток, потому что размер пробела зависит от зоны и года (хотя, конечно, обычно час).
Одним из примеров этого является анализ синтаксиса, который исходит от источника, которого вы не контролируете; например, сети. Если отправитель метки времени имеет устаревшие файлы зон, это может произойти. (Если у вас устаревшие файлы зон, вы сильно завинчены).
Здесь способ сделать это, который, предоставленный, немного сложнее. Я сделал это в joda 1.6, а также 2.x, так как мы оказались в 1.6 в нашей среде.
Если вы строите дату с некоторых других входов, как в своем вопросе, вы можете начать с даты UTC или LocalDate
, как было предложено выше, а затем адаптировать ее для автоматического исправления смещения. Специальный соус находится в DateTimeZone.convertLocalToUTC
Dangerous:
public DateTime parse(String str) {
formatter.parseDateTime(gapdate)
}
Safe:
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 formatters поддерживал setStrict (boolean), который заставил бы их позаботиться об этом для вас...
Ответ 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();
Работал для меня отлично. Может быть, кому-то это понадобится.
Ответ 5
Обновить до jodatime 2.1 и использовать LocalDate.parse()
:
DateTimeFormatter formatter = DateTimeFormat.forPattern("dd/MM/yyyy");
LocalDate.parse(date, formatter);