4

I'm managing devices that report their system clock as seconds since midnight 01-01-1900.
I need to convert this into a timestamp.

So far, I'm doing this as follows:

import java.text.SimpleDateFormat;
import java.util.Calendar;

public class TestTime
{
  // Pass seconds since 01-01-1900 00:00:00 on the command line
  public static void main(String[] args)
  {
    // ---------------------
    // Create time formatter
    // ---------------------
    SimpleDateFormat format;
    format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    // ---------------------------
    // Compose 01-01-1900 00:00:00
    // ---------------------------
    Calendar cal;
    cal = Calendar.getInstance();
    cal.set(Calendar.YEAR, 1900);
    cal.set(Calendar.MONTH, Calendar.JANUARY);
    cal.set(Calendar.DAY_OF_MONTH, 1);
    cal.set(Calendar.HOUR_OF_DAY, 0);
    cal.set(Calendar.MINUTE, 0);
    cal.set(Calendar.SECOND, 0);
    cal.set(Calendar.MILLISECOND, 0);

    // -------------------
    // Show what we've got
    // -------------------
    System.out.println(format.format(cal.getTime()));

    // ---------------------------------------------
    // Add the seconds as passed on the command line
    // ---------------------------------------------
    long secs = Long.parseLong(args[0]);
    while (secs > Integer.MAX_VALUE)
    {
      cal.add(Calendar.SECOND, Integer.MAX_VALUE);
      secs -= Integer.MAX_VALUE;
    }
    cal.add(Calendar.SECOND, (int)secs);

    // -------------------
    // Show what we've got
    // -------------------
    System.out.println(args[0] + " corresponds to " + format.format(cal.getTime()));

  } // main

} // class TestTime

When running this on my local PC (Italy, Windows 7), I get the following:

java -cp . TestTime 3752388800
1900-01-01 00:00:00
3752388800 corresponds to 2018-11-28 10:13:20

This is perfectly correct.
I get the same results when running this on a Linux machine (still in Italy).

However, running the very same program on a Linux machine in Brazil, I get different results:

java -cp . TestTime 3752388800
1900-01-01 00:00:00
3752388800 corresponds to 2018-11-28 11:19:48

Whatever value I pass on the commandline, the difference is always 01:06:28.
Any idea where this difference is coming from?

BTW, I'm not concerned about the timezone. I just need a timestamp

Update 1:
The very same thing happens also when using Java 6 (which is the actual version used within our production environment in Brazil).
So, the problem does not depend on the java version

Update 2:
The problem does not occur when entering a number of seconds below 441763200 (which corresponds to 01-01-1914 00:00:00) The question remains why we get a difference for Brazil?

Karol Dowbecki
  • 43,645
  • 9
  • 78
  • 111
Robert Kock
  • 5,795
  • 1
  • 12
  • 20
  • 6
    This is tagged as Java 8 and yet you're using the awful, outdated datetime classes. Use `java.time`. – Michael Nov 28 '18 at 10:39
  • 1
    Maybe it's something like this - https://stackoverflow.com/questions/6841333/why-is-subtracting-these-two-times-in-1927-giving-a-strange-result – Meini Nov 28 '18 at 10:50
  • 2
    `Calendar.getInstance()` uses `TimeZone.getDefault()`. Try `Calendar.getInstance(TimeZone.getTimeZone("UTC"))` instead – Meini Nov 28 '18 at 10:56
  • @Michael: within our production environment we're still using java-6. I updated the answer. – Robert Kock Nov 28 '18 at 10:59
  • 4
    Since midnight 01-01-1900 in what time zone? Sorry, you cannot evade that question because the answer depends on it. – Ole V.V. Nov 28 '18 at 11:00
  • I have reproduced. When I set my JVM’s default time zone to America/Sao_Paulo, I get `3752388800 corresponds to 2018-11-28 11:19:48`. – Ole V.V. Nov 28 '18 at 11:19
  • @Meini When setting the timezone, I get rid of the strange difference but introduce a time shift due to the timezone. I can work on that. Thanks a lot. – Robert Kock Nov 28 '18 at 11:20
  • 1
    FYI, the troublesome old date-time classes such as `java.util.Date`, `java.util.Calendar`, and `java.text.SimpleDateFormat` are now legacy, supplanted by the [*java.time*](https://docs.oracle.com/javase/10/docs/api/java/time/package-summary.html) classes. Most of the *java.time* functionality is back-ported to Java 6 & Java 7 in the [***ThreeTen-Backport***](http://www.threeten.org/threetenbp/) project. Further adapted for earlier Android in the [***ThreeTenABP***](https://github.com/JakeWharton/ThreeTenABP) project. See [*How to use ThreeTenABP…*](http://stackoverflow.com/q/38922754/642706). – Basil Bourque Nov 29 '18 at 00:14

3 Answers3

4

java.time

A solution is to make sure you do your conversion in UTC:

    Instant base = LocalDate.of(1900, Month.JANUARY, 1)
            .atStartOfDay(ZoneOffset.UTC)
            .toInstant();

    String secsSince1900String = "3752388800";
    long secsSince1900 = Long.parseLong(secsSince1900String);
    Instant target = base.plusSeconds(secsSince1900);
    System.out.println(target);

Output (independent of JVM time zone):

2018-11-28T10:13:20Z

The trailing Z in the output means UTC. I have tested while setting my JVM’s default time zone to America/Sao_Paulo, it made no difference. If you want, you can formate the date and time to your liking, for example:

    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    String formattedDateTime = target.atOffset(ZoneOffset.UTC).format(formatter);
    System.out.println(formattedDateTime);

2018-11-28 10:13:20

What went wrong when running in Brazil?

There are a number of time zones across Brazil. I took São Paulo as an example and reproduced your output readily. At the turn of the century in 1900, São Paulo was at offset -03:06:28 from GMT. Your Calendar uses the JVM’s default time zone, so you really set its time of day to 03:06:28 GMT, which explains the difference.

That said, the date-time classes you were using — SimpleDateFormat and Calendar — have design problems and have fortunately been replaced by java.time, the modern Java date and time API, with Java 8 nearly 5 years ago. One trait of the modern API is we more naturally make time zone explicit, which makes it easier to avoid issues like yours, and also to fix them if we run into them anyway.

Question: Our production Java version is Java 6. Can I use java.time?

Yes, java.time works nicely on Java 6. It has been backported.

  • In Java 8 and later and on newer Android devices (from API level 26) the modern API comes built-in.
  • In Java 6 and 7 get the ThreeTen Backport, the backport of the new classes (ThreeTen for JSR 310; see the links at the bottom).
  • On (older) Android use the Android edition of ThreeTen Backport. It’s called ThreeTenABP. And make sure you import the date and time classes from org.threeten.bp with subpackages.

Links

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
  • I agree with your consideration regarding Java-8. Unfortunately I cannot change that within a production environment. – Robert Kock Nov 28 '18 at 11:43
  • 2
    @RobertKock The original date-time classes are a bloody awful wretched mess. You really should consider adding the *ThreeTen-Backport* library to your project. The library includes a utilities class [`DateTimeUtils`](https://www.threeten.org/threetenbp/apidocs/org/threeten/bp/DateTimeUtils.html) for converting back-and-forth between the modern types and the legacy types. So you can still interoperate with your existing code base, no need to revamp all the date-time handling code at once. FYI: `Date` is replaced by `Instant`, `GregorianCalendar` by `ZonedDateTime`. – Basil Bourque Nov 29 '18 at 00:18
  • @BasilBourque: Thanks. I'll check this out for sure – Robert Kock Nov 29 '18 at 08:23
3

On Jan 1st, 1914 Brazil changed the time and added 6 minutes and 28 seconds to their time moving from LMT to BRT (see Time Changes in São Paulo Over the Years, 1900-1924).

The additional one hour of difference is probably that Brazil spans 3 time zones (UTC-4, UTC-3, UTC-2) and you didn't set the time zone in your code, depending on the JVM system time zone.

Karol Dowbecki
  • 43,645
  • 9
  • 78
  • 111
3

Have a look at this site: https://www.timeanddate.com/time/zone/brazil/sao-paulo and navigate to Time zone changes for: 1900-1924. There you can see an offset of -03:06:28 to UTC before 01-01-1914. It is exactly the same reason as in Why is subtracting these two times (in 1927) giving a strange result?

Meini
  • 77
  • 3
  • 19