4

This Java code, given a date as a string, is supposed to print the epoch timestamp for the same date at the midnight for the CET zone (supposing I'm not in the same zone).

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());
}

If I run the above program I should get printed:

 Epoch timestamp = 802389600000

And I can verify it's correct here:

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

Now, that works for most of the dates. However, there are some bizarre dates like "1975-09-19", where it doesn't work. In fact, It generates 180313200000 as a timestamp, which gives 1am and not midnight:

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

Can you explain why? What am I missing?

user1883212
  • 7,539
  • 11
  • 46
  • 82
  • 2
    What error do you actually see? What "doesn't work"? – markspace Mar 16 '20 at 15:17
  • It's not an error, but the timestamp is not correct and you can verify it with the link I posted – user1883212 Mar 16 '20 at 15:40
  • That website lists Europe/Malta as CEST timezone, which is GMT+2, but your code is using CET, which is GMT+1. Are you sure you've got the right timezone? – azurefrog Mar 16 '20 at 15:42
  • 1
    "CET" isn't really a time zone as such, any more than "BST", "EST" etc are. I strongly advise you to use a real time zone ID (e.g. Europe/Malta) instead of the abbreviations for "half a time zone". – Jon Skeet Mar 16 '20 at 15:50
  • I recommend you don’t use `SimpleDateFormat`, `Date` and `Calendar`. Those classes are poorly designed and long outdated, the first in particular notoriously troublesome. Instead use `ZonedDateTime` and `Instant`, both from [java.time, the modern Java date and time API](https://docs.oracle.com/javase/tutorial/datetime/). – Ole V.V. Mar 17 '20 at 06:16

2 Answers2

3

Time zone discrepancy

Your Java code uses CET, which is not really a time zone (for example because most of the areas where it’s used use CEST instead for most of the year). Java translates CET to Europe/Paris. France and Paris did not use summer time (DST) in 1975. It was reintroduced in March 1976.

Your link to the epoch converter specifies Malta time zone (Europe/Malta). Malta did use summer time in 1975: it was on CEST from 20 April to 21 September that year.

This explains the difference in your results.

In Java code

If you wanted Malta time:

    String dateStr = "1975-09-19";

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

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

This prints:

Epoch timestamp = 180309600000

And the epoch converter that you linked to is happy to agree:

Conversion results (180309600)

180309600 converts to Friday September 19, 1975 00:00:00 (am) in time zone Europe/Malta (CEST) The offset (difference to Greenwich Time/GMT) is +02:00 or in seconds 7200. This date is in daylight saving time.

In Java do use java.time, the modern Java date and time API, for your date and time work. It is so much nicer to work with compared to the old date and time classes like SimpleDateFormat, TimeZone, Date and Calendar. Also setting the hours, etc., to 0 is not the correct way to get the first moment of the day. There are cases where summer time begins at the start of the day, so the first moment of the day is 01:00:00. Java knows that, so the atStartOfDay method will give you the correct forst moment of the day in question.

And no matter if using outdated or modern classes always specify time zone in the region/city format, for example Europe/Paris or Europe/Malta. The three, four and five letter time zone abbreviations are often ambiguous and often not true time zones, so not to be relied on.

Links

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
2

There seems to be a difference concerning daylight saving time between your date examples.
If I use java.time (which should always be used since Java 8), I get results with different offsets:

  • "+02:00" for "1995-06-06" and
  • "+01:00" for "1975-09-19"

This is how I got the results:

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());
}

Output:

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

That means you might be better off using specific offsets instead of zones.

This issue could be due to the timing of the introduction of Daylight Saving Time in Malta, have a look at the following code and its output:

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());
    }
}

Output:

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

This output indicates an introduction in 1977... Is that correct?

deHaar
  • 17,687
  • 10
  • 38
  • 51