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

Если я запускаю следующую программу, которая анализирует две строки даты, ссылающиеся на раз в 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
6579
задан 16 November 2018 в 08:16

10 ответов

Это - изменение часового пояса 31-го декабря в Шанхае.

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

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

РЕДАКТИРОВАНИЕ: нажатие Остановки! Изменения истории...

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

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

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

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

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

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

10552
ответ дан 31 October 2019 в 12:55

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

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

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

1553
ответ дан 31 October 2019 в 12:55

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

  • даты и время Использования в UTC по мере возможности.
  • , Если Вы не можете отобразить дату или время в UTC, всегда указывайте на часовой пояс.
  • , Если Вы не можете потребовать входной даты/времени в UTC, потребуйте явно обозначенного часового пояса.
635
ответ дан 31 October 2019 в 12:55

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

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

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

350
ответ дан 31 October 2019 в 12:55

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

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

И видите, что результат:

1
296
ответ дан 31 October 2019 в 12:55

Мне жаль говорить, но разрыв времени переместился немного в

JDK 6 два года назад, и в JDK 7 просто недавно в обновление 25 .

Урок для изучения: избегайте времен не-UTC любой ценой, кроме, возможно, для дисплея.

216
ответ дан 31 October 2019 в 12:55

Как объяснили другие, там существует разрыв времени. Существует два возможных смещения часового пояса для 1927-12-31 23:54:08 в Asia/Shanghai, но только одно смещение для 1927-12-31 23:54:07. Так, в зависимости от которого используется смещение, существует или одно второе различие или 5 минуты и различие 53 секунд.

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

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

новое java.time пакет на Java 8 позволил использованию видеть это более ясно и обеспечить инструменты для обработки его. Данный:

DateTimeFormatterBuilder dtfb = new DateTimeFormatterBuilder();
dtfb.append(DateTimeFormatter.ISO_LOCAL_DATE);
dtfb.appendLiteral(' ');
dtfb.append(DateTimeFormatter.ISO_LOCAL_TIME);
DateTimeFormatter dtf = dtfb.toFormatter();
ZoneId shanghai = ZoneId.of("Asia/Shanghai");

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

ZonedDateTime zdt3 = LocalDateTime.parse(str3, dtf).atZone(shanghai);
ZonedDateTime zdt4 = LocalDateTime.parse(str4, dtf).atZone(shanghai);

Duration durationAtEarlierOffset = Duration.between(zdt3.withEarlierOffsetAtOverlap(), zdt4.withEarlierOffsetAtOverlap());

Duration durationAtLaterOffset = Duration.between(zdt3.withLaterOffsetAtOverlap(), zdt4.withLaterOffsetAtOverlap());

Затем durationAtEarlierOffset будет одна секунда, в то время как durationAtLaterOffset будут пять минут и 53 секунды.

кроме того, эти два смещения являются тем же:

// Both have offsets +08:05:52
ZoneOffset zo3Earlier = zdt3.withEarlierOffsetAtOverlap().getOffset();
ZoneOffset zo3Later = zdt3.withLaterOffsetAtOverlap().getOffset();

, Но эти два отличаются:

// +08:05:52
ZoneOffset zo4Earlier = zdt4.withEarlierOffsetAtOverlap().getOffset();

// +08:00
ZoneOffset zo4Later = zdt4.withLaterOffsetAtOverlap().getOffset();

Вы видите, что та же проблема соответствует 1927-12-31 23:59:59 [1 111], тем не менее, в этом случае, это ранее смещение, которые производят более долгое расхождение, и это - более ранняя дата, которая имеет два возможных смещения.

Другой способ приблизиться это должно проверить, существует ли продолжение перехода. Мы можем сделать это как это:

// Null
ZoneOffsetTransition zot3 = shanghai.getRules().getTransition(ld3.toLocalDateTime);

// An overlap transition
ZoneOffsetTransition zot4 = shanghai.getRules().getTransition(ld3.toLocalDateTime);

можно проверить, является ли переход перекрытием - в этом случае существует больше чем одно допустимое смещение для той даты/времени - или разрыв - в этом случае, что дата/время не допустима для того идентификатора зоны - при помощи isOverlap() и isGap() методы на [1 114].

я надеюсь, что это помогает людям обработать этот вид проблемы, после того как Java 8 становится широко доступным, или тем, которые используют Java 7, кто принимает бэкпорт JSR 310.

190
ответ дан 31 October 2019 в 12:55

По моему скромному мнению, распространяющееся, неявный локализация в Java является своим единственным самым большим недостатком дизайна. Можно предназначить для пользовательских интерфейсов, но откровенно говоря, кто действительно использует Java для пользовательских интерфейсов сегодня за исключением некоторых IDE, где можно в основном проигнорировать локализацию, потому что программисты не являются точно целевой аудиторией для него. Можно зафиксировать его (особенно на серверах Linux):

  • LC_ALL=C TZ=UTC
  • экспорта
  • установил Ваши системные часы на UTC
  • , никогда не используют локализованные реализации, если абсолютно необходимый (т.е. только для дисплея)

К Процесс Сообщества Java участники я не рекомендую:

  • делают локализованные методы не значением по умолчанию, но требуют, чтобы пользователь явно запросил локализацию.
  • использование UTF-8/UTC в качестве ЗАФИКСИРОВАЛО значение по умолчанию вместо этого, потому что это - просто значение по умолчанию сегодня. Нет никакой причины сделать что-то еще, кроме того, если Вы хотите произвести потоки как это.

я имею в виду, продвиньтесь, разве глобальные статические переменные не являются шаблоном анти-OO? Ничто иное не те распространяющиеся значения по умолчанию, данные некоторыми элементарными переменными среды.......

157
ответ дан 31 October 2019 в 12:55

Как сказанные другие, это - изменение времени в 1927 в Шанхае.

В основном, это 23:54:07 в Шанхае, Стандартное местное время, после того, как затем некоторое время это обратилось к следующему дню в 00:00:00. Но затем это переключилось назад на 23:54:08 в течение Стандартного местного времени. Так, вот почему различием составляют 343 секунды не 1 секунда.

Это также может испортить с другими местами как США. В США у них есть Летнее время. Когда Летнее время запускается, время продвигается на 1 час. Но через некоторое время концы Летнего времени это идет назад 1 час назад к стандартному часовому поясу. Таким образом, иногда при сравнении времен в США различие - приблизительно 3600 секунды не 1 секунда.

лучше использовать UTC, где время не изменяется, если в случае необходимости не использовать время не-UTC как в дисплее.

10
ответ дан 31 October 2019 в 12:55
import java.util.TimeZone;

public class Demo {
    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);
            }
        }
    }
}
-2
ответ дан 31 October 2019 в 12:55

Другие вопросы по тегам:

Похожие вопросы: