Потеря часа при возврате даты января 1970 года из миллисекунд - PullRequest
4 голосов
/ 28 апреля 2011

У меня есть следующий код, который принимает строку миллисекунд (будет из ленты RSS, поэтому будет строкой, пример ниже - это программа быстрого тестирования) и преобразует эти миллисы в объект Date.

public static void main(String[] args) {
    String ms = "1302805253";
    SimpleDateFormat dateFormatter = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz");
    Calendar calendar = Calendar.getInstance();
    calendar.setTimeInMillis(Long.parseLong(ms));

    try {
        String dateFormat = dateFormatter.format(calendar.getTime());
        System.out.println("Date Format = " + dateFormat);

        Date dateParse = dateFormatter.parse(dateFormatter.format(calendar.getTime()));
        System.out.println("Date Parse  = " + dateParse);
    } catch (ParseException e) {
        // TODO: handle exception
    }
}


Output:
    Date Format = Fri, 16 Jan 1970 02:53:25 GMT
    Date Parse  = Fri Jan 16 03:53:25 GMT 1970

Как видите, между форматированием объекта календаря и разбором результирующей строки теряется час. Также изменилось форматирование вывода. Кто-нибудь может мне помочь, почему это происходит, и как это обойти? Я хочу, чтобы объект Date был в том же формате, что и вывод "Формат даты".

Ответы [ 2 ]

8 голосов
/ 28 апреля 2011

Я полагаю, что это происходит, потому что Великобритания не на самом деле использовала GMT в 1970 году, и у Java есть ошибка, связанная с этим ... она будет форматировать дату в 1970 году, как будтоВеликобритания использовала GMT, но фактически не меняла смещение.Простой пример:

Date date = new Date(0);
SimpleDateFormat sdf = new SimpleDateFormat("dd MMM yyyy HH:mm:ss zzz");
sdf.setTimeZone(TimeZone.getTimeZone("Europe/London"));
System.out.println(sdf.format(date));

Результат:

01 Jan 1970 01:00:00 GMT

Обратите внимание, что он утверждает, что сейчас 1:00 по Гринвичу ... что неверно.Это было 1:00 по европейскому / лондонскому времени, но Европа / Лондон не наблюдал время по Гринвичу.

Joda Time получает это право в том смысле, что печатает BST - ноJoda Time не нравится парсинг значений с сокращениями часовых поясов.Однако вы можете использовать часовой пояс Offs вместо:

import org.joda.time.*;
import org.joda.time.format.*;

public class Test {
    public static void main(String[] args) throws Exception {
        DateTime date = new DateTime(0, DateTimeZone.forID("Europe/London"));

        DateTimeFormatter formatter = DateTimeFormat.forPattern(
            "dd MMM yyyy HH:mm:ss Z");

        String text = formatter.print(date); // 01 Jan 1970 01:00:00 +0100
        System.out.println(text);

        DateTime parsed = formatter.parseDateTime(text);
        System.out.println(parsed.equals(date)); // true
    }
}
0 голосов
/ 15 августа 2016

Ответ Джона Скита правильный.

java.time

Давайте запустим тот же самый ввод через java.time, чтобы увидеть результаты.

Укажите правильное имя часового пояса . Никогда не используйте 3-4-буквенное сокращение, такое как BST, EST или IST, поскольку они не являются истинными часовыми поясами, не стандартизированы и даже не уникальны (!). Поэтому мы используем Europe/London.

Класс Instant представляет момент на временной шкале в UTC с разрешением наносекунд .

String input = "1302805253";
long millis = Long.parseLong ( input );
Instant instant = Instant.ofEpochMilli ( millis );

Применение часового пояса для создания объекта ZonedDateTime.

ZoneId zoneId = ZoneId.of ( "Europe/London" );
ZonedDateTime zdt = instant.atZone ( zoneId );

Дамп на консоль. Мы действительно видим, что Europe/London время на час впереди UTC в тот момент. Таким образом, время суток составляет 02 часа, а не 01 часов. Оба представляют один и тот же момент времени на временной шкале, только если смотреть через линзы двух разных настенных часов .

System.out.println ( "input: " + input + " | instant: " + instant + " | zdt: " + zdt );

вход: 1302805253 | мгновенный: 1970-01-16T01: 53: 25.253Z | zdt: 1970-01-16T02: 53: 25.253 + 01: 00 [Европа / Лондон]

целых секунд

Кстати, я подозреваю, что ваша входная строка представляет целых секунд со времени 1970 UTC, а не миллисекунд . Интерпретировано, что в качестве секунд мы получаем дату в 2011 году, в месяц, когда этот вопрос был опубликован.

String output = Instant.ofEpochSecond ( Long.parseLong ( "1302805253" ) ).atZone ( ZoneId.of ( "Europe/London" ) ).toString ();

2011-04-14T19: 20: 53 + 01: 00 [Europe / London]

О java.time

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

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

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

Большая часть функций java.time перенесена в Java 6 & 7 в ThreeTen-Backport и дополнительно адаптирована для Android в ThreeTenABP .

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

...