Почему вычитание этих двух раз (в 1927 году)дает странный результат.

java date timezone


Если я запущу следующую программу,которая разбирает две строки даты с интервалом в 1 секунду и сравнивает их:

public static void main(String[] args) throws ParseException {
    SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  
    String str3 = "1927-12-31 23:54:07";  
    String str4 = "1927-12-31 23:54:08";  
    Date sDt3 = sf.parse(str3);  
    Date sDt4 = sf.parse(str4);  
    long ld3 = sDt3.getTime() /1000;  
    long ld4 = sDt4.getTime() /1000;
    System.out.println(ld4-ld3);
}

Выход:

353

Почему ld4-ld3 не 1 (как я ожидал бы из-за разницы во времени в одну секунду), а 353 ?

Если я изменю даты на 1 секунду позже:

String str3 = "1927-12-31 23:54:08";  
String str4 = "1927-12-31 23:54:09";  

Тогда ld4-ld3 будет 1 .


Версия Java:

java version "1.6.0_22"
Java(TM) SE Runtime Environment (build 1.6.0_22-b04)
Dynamic Code Evolution Client VM (build 0.2-b02-internal, 19.0-b04-internal, mixed mode)
Timezone(`TimeZone.getDefault()`):

sun.util.calendar.ZoneInfo[id="Asia/Shanghai",
offset=28800000,dstSavings=0,
useDaylight=false,
transitions=19,
lastRule=null]

Locale(Locale.getDefault()): zh_CN



Answer 1 Jon Skeet


Смена часового пояса 31 декабря в Шанхае.

Смотрите эту страницу для деталей 1927 года в Шанхае. В основном в полночь в конце 1927 года часы вернулись на 5 минут и 52 секунды. Таким образом, «1927-12-31 23:54:08» фактически происходило дважды, и похоже, что Java анализирует его как более поздний возможный момент для этой локальной даты / времени - отсюда и разница.

Просто еще один эпизод в часто странном и удивительном мире часовых поясов.

РЕДАКТИРОВАТЬ: Стоп пресс! История меняется ...

Исходный вопрос больше не будет демонстрировать совершенно такое же поведение, если его перестроить с версией TZDB 2013a . В 2013a результат будет 358 секунд с временем перехода 23:54:03 вместо 23:54:08.

Я заметил это только потому, что собираю подобные вопросы в Noda Time в форме модульных тестов ... Тест теперь изменен, но он просто показывает, что даже исторические данные не являются безопасными.

РЕДАКТИРОВАТЬ: История снова изменилась ...

В TZDB 2014f время изменения сместилось на 1900-12-31, и теперь это всего лишь 343 секунды (так что время между t и t+1 составляет 344 секунды, если вы понимаете, что я имею в виду).

РЕДАКТИРОВАТЬ: Чтобы ответить на вопрос о переходе в 1900 году ... похоже, что реализация часового пояса Java обрабатывает все часовые пояса как просто в их стандартное время в любой момент до начала 1900 UTC:

import java.util.TimeZone;

public class Test {
    public static void main(String[] args) throws Exception {
        long startOf1900Utc = -2208988800000L;
        for (String id : TimeZone.getAvailableIDs()) {
            TimeZone zone = TimeZone.getTimeZone(id);
            if (zone.getRawOffset() != zone.getOffset(startOf1900Utc - 1)) {
                System.out.println(id);
            }
        }
    }
}

Приведенный выше код не производит вывод на мой компьютер с Windows. Таким образом, любой часовой пояс с любым смещением, отличным от стандартного в начале 1900 года, будет считаться переходным. Сам TZDB имеет некоторые данные, возвращающиеся раньше, и не полагается на идею «фиксированного» стандартного времени (которое getRawOffset предполагает допустимым понятием), поэтому другие библиотеки не должны вводить этот искусственный переход.




Answer 2 Michael Borgwardt


Вы столкнулись с разрывом местного времени :

Когда местное стандартное время приближалось к воскресенью,1 января 1928 года,00:00:00 часы были повернуты назад с 0:05:52 часов до субботы,31.Декабрь 1927 года,23:54:08 по местному стандартному времени.

Это не особенно странно и происходило практически везде в то или иное время,так как часовые пояса менялись или менялись в связи с политическими или административными действиями.




Answer 3 Raedwald


Мораль этой странности в том:

  • Используйте даты и время по UTC везде,где это возможно.
  • Если вы не можете отобразить дату или время в UTC,всегда указывайте часовой пояс.
  • Если вы не можете потребовать ввода даты/времени в UTC,потребовать явно указанный часовой пояс.



Answer 4 PatrickO


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

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

Если вы конвертировали в UTC, добавляйте каждую секунду и переводите в местное время для отображения. Вы должны были пройти до 23:54:08 вечера LMT - 11:59:59 вечера LMT, а затем 11:54:08 вечера CST - 11:59:59 вечера CST.




Answer 5 Rajshri


Вместо того,чтобы преобразовывать каждую дату,можно использовать следующий код:

long difference = (sDt4.getTime() - sDt3.getTime()) / 1000;
System.out.println(difference);

А потом посмотрим,что получится:

1