Некоторые даты не могут быть правильно преобразованы в Java в метки времени эпохи в полночь указанного часового пояса c - PullRequest
4 голосов
/ 16 марта 2020

Этот код Java, учитывая дату в виде строки, должен печатать метку времени эпохи для той же даты в полночь для зоны CET (предположим, что я не в той же зоне).

public static void main(String[] args) throws ParseException {

    String dateStr = "1995-06-06";

    SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
    formatter.setTimeZone(TimeZone.getTimeZone("CET"));
    Date date = formatter.parse(dateStr);
    Calendar c = new GregorianCalendar();
    c.setTimeZone(TimeZone.getTimeZone("CET"));
    c.setTime(date);

    c.set(Calendar.HOUR_OF_DAY, 0);
    c.set(Calendar.MINUTE, 0);
    c.set(Calendar.SECOND, 0);
    c.set(Calendar.MILLISECOND, 0);

    System.out.println("Epoch timestamp = " + c.getTime().getTime());
}

Если я запускаю вышеупомянутую программу, мне нужно напечатать:

 Epoch timestamp = 802389600000

И я могу убедиться, что это правильно здесь:

https://www.epochconverter.com/timezones?q=802389600&tz=Europe%2FMalta

Теперь это работает для большинства дат. Тем не менее, есть некоторые странные даты, такие как «1975-09-19», где это не работает. Фактически, он генерирует 180313200000 как метку времени, которая дает 1:00, а не полночь:

https://www.epochconverter.com/timezones?q=180313200&tz=Europe%2FMalta

Вы можете объяснить, почему? Чего мне не хватает?

Ответы [ 2 ]

3 голосов
/ 17 марта 2020

Несоответствие часового пояса

Ваш код Java использует CET, который на самом деле не является часовым поясом (например, потому что в большинстве областей, где он используется, вместо этого используется CEST в течение большей части года). Java переводит CET в Европу / Париж. Франция и Париж не использовали летнее время (DST) в 1975 году. Оно было вновь введено в марте 1976 года.

В вашей ссылке на конвертер эпохи указан часовой пояс Мальты (Европа / Мальта). Мальта использовала летнее время в 1975 году: она была на CEST с 20 апреля по 21 сентября того года.

Это объясняет разницу в ваших результатах.

В Java код

Если вы хотите, чтобы мальтийское время:

    String dateStr = "1975-09-19";

    long epochTimestamp = 
            LocalDate 
            .parse(dateStr)                            
            .atStartOfDay(ZoneId.of("Europe/Malta"))
            .toInstant()
            .toEpochMilli();

    System.out.println("Epoch timestamp = " + epochTimestamp);

Это печатает:

Метка времени эпохи = 180309600000

И Конвертер эпох, с которым вы связались, счастлив согласиться:

Результаты конвертации (180309600)

180309600 преобразуются в Пятница 19 сентября 1975 г. 00:00:00 (утра) в часовом поясе Европа / Мальта (CEST) Смещение (разница по времени по Гринвичу / GMT) составляет +02: 00 или в секундах 7200. Эта дата указана в летнем времени.

В Java используйте для своей работы дату и время java .time, современный Java API даты и времени. Работать с ней намного приятнее, чем со старыми классами даты и времени, такими как SimpleDateFormat, TimeZone, Date и Calendar. Кроме того, установка часов и т. Д. c. Не является правильным способом получения первого момента дня. Есть случаи, когда летнее время начинается в начале дня, поэтому первый момент дня - 01:00:00. Java это знает, поэтому метод atStartOfDay даст вам правильный первый момент рассматриваемого дня.

И независимо от того, используются ли устаревшие или современные классы, всегда указывайте часовой пояс в регионе / city формат, например Европа / Париж или Европа / Мальта. Трех-, четырех- и пятибуквенные сокращения часовых поясов часто неоднозначны и часто не являются истинными часовыми поясами, поэтому на них нельзя полагаться.

Ссылки

2 голосов
/ 16 марта 2020

Кажется, есть разница в летнем времени между вашими примерами дат.
Если я использую java.time (который должен всегда использоваться с Java 8), я получаю результаты с различными смещениями:

  • "+02:00" для "1995-06-06" и
  • "+01:00" для "1975-09-19"

Вот как я получил результаты:

public static void main(String[] args) {
    // provide two sample dates
    String workingDateStr = "1995-06-06";
    String failingDateStr = "1975-09-19";
    // and a formatter that parses the format
    DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    // then parse them to date objects that don't know about time or zone
    LocalDate workingDate = LocalDate.parse(workingDateStr, dtf);
    LocalDate failingDate = LocalDate.parse(failingDateStr, dtf);
    /*
     *  then create an objects that are aware of time and zone
     *  by using the parsed dates, adding a time of 00:00:00 and a zone
     */
    ZonedDateTime workingZdt = ZonedDateTime.of(workingDate, LocalTime.MIN, ZoneId.of("CET"));
    ZonedDateTime failingZdt = ZonedDateTime.of(failingDate, LocalTime.MIN, ZoneId.of("CET"));

    // finally, print different representations of the results
    System.out.println(workingZdt + " ——> " + workingZdt.toInstant().toEpochMilli());
    System.out.println(failingZdt + " ——> " + failingZdt.toInstant().toEpochMilli());
}

Вывод:

1995-06-06T00:00+02:00[CET] ——> 802389600000
1975-09-19T00:00+01:00[CET] ——> 180313200000

Это означает, что вам может быть лучше использовать указанные c смещения вместо зон.

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

public static void main(String[] args) {
    // provide two sample dates
    String failingDateStr = "1975-09-19";
    // and a formatter that parses the format
    DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    // then parse them to date objects that don't know about time or zone
    LocalDate failingDate = LocalDate.parse(failingDateStr, dtf);
    /*
     *  then create an objects that are aware of time and zone
     *  by using the parsed dates, adding a time of 00:00:00 and a zone
     */
    ZonedDateTime failingZdt = ZonedDateTime.of(failingDate, LocalTime.MIN, ZoneId.of("CET"));

    // add some years to 1975 and...
    for (int year = 0; year < 4; year++) {
        // ... print the different representations of the result
        System.out.println(failingZdt.plusYears(year) + " ——> " 
                + failingZdt.plusYears(year).toInstant().toEpochMilli());
    }
}

Выход:

1975-09-19T00:00+01:00[CET] ——> 180313200000
1976-09-19T00:00+01:00[CET] ——> 211935600000
1977-09-19T00:00+02:00[CET] ——> 243468000000
1978-09-19T00:00+02:00[CET] ——> 275004000000

Этот вывод указывает на введение в 1977 году. .. Это правильно?

...