Date.toString () добавляет час около эпохи в Лондоне - PullRequest
0 голосов
/ 24 января 2019

Как я могу получить Date.toString () для создания вывода, который SimpleDateFormat может правильно проанализировать для дат около 1 января 1970 года (я полагаю, это также относится и к зиме 1968 и 1969 годов)следующее,

System.out.println(TimeZone.getDefault());
Date date = new Date(0);
SimpleDateFormat sdf = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy");
Date date2 = sdf.parse(date.toString());
System.out.println("date: " + date);
System.out.println("date2: " + date2);
Date date3 = sdf.parse(date2.toString());
System.out.println("date3: " + date3);

Это печатает

sun.util.calendar.ZoneInfo[id="Europe/London",offset=0,dstSavings=3600000,useDaylight=true,transitions=242,lastRule=java.util.SimpleTimeZone[id=Europe/London,offset=0,dstSavings=3600000,useDaylight=true,startYear=0,startMode=2,startMonth=2,startDay=-1,startDayOfWeek=1,startTime=3600000,startTimeMode=2,endMode=2,endMonth=9,endDay=-1,endDayOfWeek=1,endTime=3600000,endTimeMode=2]]
date: Thu Jan 01 01:00:00 GMT 1970
date2: Thu Jan 01 02:00:00 GMT 1970
date3: Thu Jan 01 03:00:00 GMT 1970

Проблема в том, что Лондон был в BST 1 января 1970 года. Поэтому правильная дата должна быть либо

date: Thu Jan 01 01:00:00 BST 1970

или

date: Thu Jan 01 00:00:00 GMT 1970

, но кажется, что их путают.

И хотя я бы хотел не поддерживать java.util.Date, для меня это не вариант.

Ответы [ 3 ]

0 голосов
/ 25 января 2019

Вот почему я ненавижу библиотеку дат.

Как подразумевается, BST следует использовать летом, и календарь определяет его как таковой.

sun.util.calendar.ZoneInfo[id="Europe/London",offset=0,dstSavings=3600000,useDaylight=true,transitions=242,lastRule=java.util.SimpleTimeZone[id=Europe/London,offset=0,dstSavings=3600000,useDaylight=true,startYear=0,startMode=2,startMonth=2,startDay=-1,startDayOfWeek=1,startTime=3600000,startTimeMode=2,endMode=2,endMonth=9,endDay=-1,endDayOfWeek=1,endTime=3600000,endTimeMode=2]]

За исключением TIL Приключения круглогодичного британского летнего времени!

Дальнейшее расследование в 1966–67 гг. Привело к тому, что правительство Гарольда Уилсона ввело эксперимент по стандартному британскому времени, с Великобританией, оставшейся в GMT + 1 в течение года. Это произошло в период с 27 октября. 1968 и 31 октября 1971 года, , когда произошел возврат к предыдущей договоренности.

Если вы тестируете даты, относящиеся к этому периоду, вы обнаружите, что даты разбиваются на час каждый раз, вплоть до точек переключения.

Временной код для Европы / Лондона - GMT, с переходом на летнее время с использованием BST.

Метод Date (дата) toString «нормализует» вывод, удаляя переход на летнее время, чтобы выбрать, какой часовой пояс печатать. Возможны варианты GMT и BST. Европейское / лондонское время 01:00:00 печатается как 01:00:00 GMT , хотя оно работает по времени GMT + 1. Другими словами, date.toString() не работает должным образом в течение этого периода времени, потому что он использует GMT в качестве часового пояса для часового пояса, который якобы не является GMT / CET. время само по себе правильно, но не часовой пояс .

«Самое простое» решение, которое я могу придумать, является относительно неприятным с точки зрения здравомыслия, но, вероятно, может быть сделано более элегантным.

private static final Date experimentEnd = new Date(1971-1900, 11-1, 11);
private static final Date experimentStart = (new Date(1968-1900, 10-1, 26));

private static boolean bstExperimentTime(Date date) {
    return date.after(experimentStart) && date.before(experimentEnd);
}

public static String forDateParsing(Date date) {
    if(bstExperimentTime(date))
        return date.toString().replace("GMT", "CET");
    return date.toString();
}

public static String forDatePrinting(Date date) {
     if(bstExperimentTime(date))
        return date.toString().replace("GMT", "BST");
    return date.toString();
}

Любая дата, необходимая для разбора с часовым поясом по умолчанию "Европа / Лондон", который необходимо пропустить через форматер разбора для преобразования GMT -> CET, что является правильным GMT + 1.

Любая дата, которую вам нужно напечатать с часовым поясом по умолчанию "Европа / Лондон", необходимо пропустить через форматер синтаксического анализа, чтобы преобразовать GMT -> BST, что является правильным отображением.

0 голосов
/ 25 января 2019

tl; dr

Ваш ввод недействителен , так как BST (Британское летнее время) зимой не действовал.

BST не может бытьНадежно проанализирован, так как это нестандартная неуникальная псевдозона.

Нет необходимости возиться с SimpleDateFormat.Пусть современные java.time классы делают тяжелую работу.

И хотя я хотел бы не поддерживать java.util.Date, для меня это не вариант.

По краям вашего кода, конвертировать в-из старых и современных классов.

// Convert from legacy to modern.
Instant instant = myJavaUtilDate.toInstant() ;

// Convert from modern to legacy.
java.util.Date myJavaUtilDate = Date.from( instant ) ;

Нет BST зимой

Видимо, «B» в вашем BST означаетбыть британцемНо BST в этом контексте означает Британское летнее время .Это означает Летнее время (DST) , которое задействовано летом, а не зимой.Так что ваша входная строка январской даты в сочетании с BST бессмысленна .

Double-Summertime

Есть еще одно осложнение в вашем примере с моментом в 1970 годус британским часовым поясом.

Практика перехода на летнее время в Великобритании с использованием смещения на один час впереди UTC (+01: 00) летом и смещения нуля (+00: 00) зимойСтандартное время составляет текущая практика.Это не всегда имело место.

В 1970 году Великобритания испытывала "двойное летнее время" .В этом эксперименте 1968–1971 годов зимнее время было на один час впереди UTC, а не на ноль, а летнее время было на два часов впереди UTC вместо одного часа, используемого в настоящее время.Это привело к тому, что британское время стало больше похожим на континентальную Европу, и мы надеялись сократить количество аварий.В то время как в январе 2019 года мы не ожидаем скачка, время в Британии будет таким же, как в UTC (смещение от UTC, равное нулю, часам, минутам и секундам).

Избегать псевдозон

Избегать этих 2-4-символьных псевдозон, таких как BST.Они не стандартизированы.Они даже не уникальны!Таким образом, BST можно интерпретировать как часовой пояс Pacific/Bougainville, а также British Summer Time.

Укажите правильное имя часового пояса в форматеContinent/Region, например America/Montreal, Africa/Casablanca или Pacific/Auckland.Никогда не используйте 2-4 буквенные сокращения, такие как BST или EST или IST, поскольку они не истинных часовых поясов, не стандартизированы и даже не уникальны (!).

Преобразование

Вы можете легко конвертировать между устаревшими и современными классами даты и времени.Новые методы преобразования были добавлены к старым классам.Ищите методы from, to и valueOf для соглашений об именах .

  • java.util.Datejava.time.Instant
  • java.util.GregorianCalendarjava.time.ZonedDateTime
  • java.sql.Datejava.time.LocalDate
  • java.sql.Timestampjava.time.Instant

Преобразование

1118 * Ваш входной строкой 00:00 1 января 1970 г. случается эпоха базисная дата используется как наследие и современные классы даты и времени.У нас есть константа для этого значения.
Instant epoch = Instant.EPOCH ;

instant.toString (): 1970-01-01T00: 00: 00Z

Посмотрите тот же момент через вашчасовой пояс Europe/London.

ZoneId z = ZoneId.of( "Europe/London" ) ;
ZonedDateTime zdt = epoch.atZone( z ) ;

zdt.toString (): 1970-01-01T01: 00 + 01: 00 [Европа / Лондон]

Обратите внимание, что вышеупомянутый Double-Summertime эксперимент действует тогда.Если мы попробуем тот же код для 2019 года, мы получим смещение от UTC с нулём.

ZonedDateTime zdt2019 = 
    Instant
    .parse( "2019-01-01T00:00Z" )
    .atZone( ZoneId.of( "Europe/London" ) )
;

zdt2019.toString (): 2019-01-01T00:00Z [Европа / Лондон]

Чтобы преобразовать в java.util.Date, нам нужен объект java.time.Instant.Instant представляет момент в UTC.Мы можем извлечь Instant из нашего ZonedDateTime объекта, эффективно адаптируясь от зоны к UTC.В тот же момент, другое время настенных часов.

Instant instant = zdt.toInstant(): 

Теперь мы должны вернуться туда, где мы начали, в базовую дату 1970-01-01T00: 00: 00Z.

мгновенно.toString (): 1970-01-01T00: 00: 00Z

Чтобы получить объект java.util.Date, вам может потребоваться взаимодействие со старым кодом, который еще не обновлен до java.time классы, используйте новый метод Date.from, добавленный к старому классу.

java.util.Date d = Date.from( instant ) ; // Same moment, but with possible data-loss as nanoseconds are truncated to milliseconds.

d.toString (): чт янв 01 00:00:00 GMT 1970

Кстати, помните о возможной потере данных при преобразованииот Instant до Date.Современные классы имеют разрешение наносекунд , тогда как устаревшие классы используют миллисекунд .Таким образом, часть вашей доли секунды может быть усечена.

См. Весь код выше run live на IdeOne.com .

Чтобы преобразовать другое направление, используйте Date::toString метод.

Instant instant = d.toInstant() ;

ISO 8601

Избегайте использования текста в пользовательских форматах для обмена значениями даты и времени.При сериализации значений времени и даты в виде удобочитаемого текста используйте только стандартные форматы ISO 8601 .Классы java.time по умолчанию используют эти форматы.

Те строки, с которыми вы экспериментировали, разбирались в формате ужасный и никогда не должны использоваться для обмена данными.


О java.time

Среда java.time встроена в Java 8 и более поздние версии.Эти классы вытесняют проблемные старые устаревшие классы даты и времени, такие как java.util.Date, Calendar, & SimpleDateFormat.

Проект Joda-Time , теперь в режиме обслуживания , рекомендует перейти на классы java.time .

Чтобы узнать больше, см. Oracle Tutorial .И поиск переполнения стека для многих примеров и объяснений.Спецификация: JSR 310 .

Вы можете обмениваться java.time объектами непосредственно с вашей базой данных.Используйте драйвер JDBC , совместимый с JDBC 4.2 или более поздней версии.Нет необходимости в строках, нет необходимости в java.sql.* классах.

Где получить классы java.time?

  • Java SE 8 , Java SE 9 , Java SE 10, Java SE 11 и более поздних версий - часть стандартного Java API с связанной реализацией.
    • Java 9 добавляет некоторые незначительные функции и исправления.
  • Java SE 6 и JavaSE 7
    • Большинство функций java.time перенесены в Java 6 & 7 в ThreeTen-Backport .
  • Android
    • Более поздние версии реализации комплекта Android java.time классы.
    • Для более ранних версий Android (<26) проект <a href="https://github.com/JakeWharton/ThreeTenABP" rel="nofollow noreferrer"> ThreeTenABP адаптируется ThreeTen-Backport (упомянуто выше).См. Как использовать ThreeTenABP… .

ThreeTen-Extra Проект расширяет java.time дополнительными классами.Этот проект является полигоном для возможных будущих дополнений к java.time.Здесь вы можете найти несколько полезных классов, таких как Interval, YearWeek, YearQuarter и more .

0 голосов
/ 24 января 2019

Могу привести https://bugs.openjdk.java.net/browse/JDK-6609362?jql=text%20~%20%22epoch%20gmt%22:

Пожалуйста, используйте Z для форматирования и анализа исторических изменений смещения часового пояса чтобы избежать путаницы с историческими изменениями названия часового пояса.


public class Test {
    public static void main(String[] args) throws ParseException {
        TimeZone.setDefault(TimeZone.getTimeZone("Europe/London"));
        SimpleDateFormat f = new SimpleDateFormat("EEE MMM dd HH:mm:ss Z yyyy");
        Date d = new Date(0);
        for (int i = 0; i < 10; i++) {
            String s = f.format(d);
            System.out.println(s);
            d = f.parse(s);
        }
    } } ```
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...