Вопрос-ответ

Why is subtracting these two epoch-milli Times (in year 1927) giving a strange result?

Почему вычитание этих двух миллионных эпох (в 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
Переведено автоматически
Ответ 1

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

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

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

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

Ответ 2

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


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


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

Ответ 3

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


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

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

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

Ответ 4

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

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

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

2023-11-21 00:53 java date