Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert seconds since 01-01-1900 to timestamp in Brazil

Tags:

java

datetime

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?

like image 320
Robert Kock Avatar asked Dec 23 '22 02:12

Robert Kock


1 Answers

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

  • Oracle tutorial: Date Time explaining how to use java.time.
  • Java Specification Request (JSR) 310, where java.time was first described.
  • ThreeTen Backport project, the backport of java.time to Java 6 and 7 (ThreeTen for JSR-310).
  • ThreeTenABP, Android edition of ThreeTen Backport
  • Question: How to use ThreeTenABP in Android Project, with a very thorough explanation.
like image 194
Ole V.V. Avatar answered Feb 09 '23 01:02

Ole V.V.