3

I have to get the count of seconds since 12:00 am December 31, 1989, to now. The second parser comes from Garmin Fleet Management. Here is my code:

public int getDate(){
    Date d1 = new Date(1989, 12, 31, 12, 0, 0);
    Date today = new Date();
    long diff = today.getTime() - d1.getTime();
    return (int)(diff/1000);
}

Seconds from getDate() in Garmin parser shows as 08:35 pm July 28, 2021, instead of now.

Here is explained (by documentation) date time that I need

It is an unsigned 32-bit integer and its value is the number of seconds since 12:00 am December 31, 1989, UTC.

Where I made a mistake?

Nikolas Charalambidis
  • 40,893
  • 16
  • 117
  • 183
Sahee
  • 43
  • 6
  • Can you provide a link to that Garmin documentation you quoted? That epoch is not listed on [this Wikipedia page](https://en.wikipedia.org/wiki/Epoch_(reference_date)#Notable_epoch_dates_in_computing) and needs to be added. – Basil Bourque Mar 26 '16 at 00:24
  • 1
    @BasilBourque I heard about this today and got here trying to find the source myself. Here you go: https://web.archive.org/web/20180724233901/http://www.4x4brasil.com.br/forum/attachments/gps-global-positioning-system/102474d1196540308-conversor-serial-usb-intfspec.pdf – mtfurlan Jul 24 '18 at 23:42
  • @mtfurlan Thanks, section 7.3.14 of that document does indeed say the Garmin time type as an unsigned 32-bit integer counting number of seconds since 1989-12-31T00:00:00Z (the `Z` means UTC). Caveat: We cannot be certain of the authenticity of that document. This epoch reference is interesting, as the Wikipedia page listing [notable epoch reference dates](https://en.wikipedia.org/wiki/Epoch_(reference_date)#Notable_epoch_dates_in_computing) does not list this item, and claims GPS uses uses a different value, 1980-01-06. – Basil Bourque Jul 25 '18 at 01:46

5 Answers5

2

You should return long and wrap your difference to Math.abs() for the positive result expressing the difference.

public static long getDate() {
    Date d1 = new Date(1989, 12, 31, 12, 0, 0);
    Date today = new Date();
    long diff = Math.abs(today.getTime() - d1.getTime());
    return (diff/1000);
}

There is no unsigned in Java.


Morover this constructor for Date is obsolete, so using of Calendar is the better way:

public static long getDate() {
    Calendar d1 = new GregorianCalendar(1989, 11, 31, 0, 0, 0);
    Date today = new Date();
    long diff = Math.abs(today.getTime() - d1.getTime().getTime());
    return (diff/1000);
}
Nikolas Charalambidis
  • 40,893
  • 16
  • 117
  • 183
  • This isn't a very good solution. Your Date() constructor is deprecated. And you're using the local time zone rather than UTC. It would be better to use `Calendar` here rather than `Date`. – Kenster Mar 25 '16 at 13:16
  • FYI, the terribly troublesome old date-time classes such as [`java.util.Date`](https://docs.oracle.com/javase/10/docs/api/java/util/Date.html), [`java.util.Calendar`](https://docs.oracle.com/javase/10/docs/api/java/util/Calendar.html), and `java.text.SimpleDateFormat` are now [legacy](https://en.wikipedia.org/wiki/Legacy_system), supplanted by the [*java.time*](https://docs.oracle.com/javase/10/docs/api/java/time/package-summary.html) classes built into Java 8 and later. See [*Tutorial* by Oracle](https://docs.oracle.com/javase/tutorial/datetime/TOC.html). – Basil Bourque Jul 25 '18 at 01:25
  • That second part using `Calendar` & `GregorianCalendar` is **incorrect**. Your JVM’s current default time zone is being assigned implicitly. So while the Garmin epoch is midnight in UTC, if your code runs in India or New Zealand it will be several hours too early, and if running in New York or Vancouver BC will be several hours too late. The modern solution uses the `java.time.Instant` class, always in UTC by definition. – Basil Bourque Jul 25 '18 at 01:43
2

You should use Calendar instance instead of Date which constructor is deprecated:

public static long getDate(){
    Calendar calendar = new GregorianCalendar(1989, 11, 31, 0, 0, 0);
    Date today = new Date();

    long diff = today.getTime() - calendar.getTime().getTime();
    return diff / 1000;
}

Date constructor accepts year as the (current year value - 1900) format. Via Javadoc:

  • @param year the year minus 1900.
    • @param month the month between 0-11.
    • @param date the day of the month between 1-31.
    • @see java.util.Calendar
    • @deprecated As of JDK version 1.1,
    • replaced by Calendar.set(year + 1900, month, date)
    • or GregorianCalendar(year + 1900, month, date).

Also you should use long (there is no unsigned int in Java) return value

Cootri
  • 3,806
  • 20
  • 30
  • **Incorrect**, because this Answer **ignores the crucial issue of time zone**. The `GregorianCalendar` object picks up your JVM’s current default time zone if you do not specify otherwise. Also, these troublesome classes are now supplanted by the *java.time* classes. – Basil Bourque Jul 25 '18 at 01:27
1

You're using deprecated API. And year argument isn't direct year but amount of years from 1900.

@Deprecated public Date(int year, int month, int date, int hrs, int min) Deprecated. As of JDK version 1.1, replaced by Calendar.set(year + 1900, month, date, hrs, min) or GregorianCalendar(year + 1900, month, date, hrs, min). Allocates a Date object and initializes it so that it represents the instant at the start of the minute specified by the year, month, date, hrs, and min arguments, in the local time zone. Parameters: year - the year minus 1900.

Donz
  • 1,389
  • 1
  • 13
  • 21
1

tl;dr

Duration.between(                              // Represent a span-of-time unattached to the timeline.
    Instant.parse( "1989-12-31T00:00:00Z" ) ,  // Garmin epoch reference date.
    Instant.now()                              // Capture the current moment in UTC. 
)                                              // Returns a `Duration` object.
.toSeconds()                                   // This span-of-time as a total number of whole seconds. Beware of data loss: Any fractional second is ignored.

Details

The other Answers use outmoded classes.

java.time

Java 8 and later comes with the java.time framework built-in. Supplants the old troublesome date-time classes such as java.util.Date & .Calendar.

long vs int

As others mentioned, Java has no unsigned integer – only signed. So here we use a 64-bit long rather than a 32-bit int. Java 8 lets you perform some operations on int values while pretending they are unsigned, but we do not actually have an unsigned int type. See this other Question, Declaring an unsigned int in java, for more info including a couple of classes designed for unsigned integers in libraries such as Google Guava.

Instant

In java.time, an Instant is a moment on the timeline in UTC. A Duration represents a span of time as a total number of seconds plus a fraction of a second as nanoseconds.

First moment of the day

Not sure if “12:00 am December 31, 1989 UTC” meant the first moment of the day (00:00:00.0) or the end of the day. I will go with first moment as the epoch. Indeed, this PDF document Garmin Device Interface Specification, supposedly published by Garmin in 2004-09-16, states:

7.3.14 time_type

The time_type is used in some data structures to indicate an absolute time. It is an unsigned 32 bit integer and its value is the number of seconds since 12:00 am December 31, 1989 UTC.

Code.

Instant garminEpoch = Instant.parse( "1989-12-31T00:00:00Z" );
Instant now = Instant.now();
Duration duration = Duration.between( garminEpoch , now );
long seconds = duration.getSeconds(); // This span of time as a total number of seconds.

About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.

Where to obtain the java.time classes?

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
0

I stumbled over that Garmin problem too, and I think you made a mistake with the date: '12:00 am December 31, 1989' is midnight from the 30th to the 31st, or "zero hundred hours' on the 31st in military jargon and 0:00h for the rest of the world! You have it as Noon time!

This time reference is from 'Garmin Device Interface Specification, May 19, 2006, Drawing Number: 001-00063-00 Rev. C' where it says:

7.3.14 time_type

The time_type is used in some data structures to indicate an absolute time. It is an unsigned 32 bit integer and its value is the number of seconds since 12:00 am December 31, 1989 UTC.

I am not sure whether this is Garmin-specific, or a typical GPS device time issue, since the GPS time counts e.g. the leap seconds separately. At least ina Garmin record the UTC time needs to be constructed from: 1. the above reference date 2. the leap seconds (in 2018 = 18) 3. the tow (time of week in seconds from Sunday 0:00h) 4. and the wn_days (week number days)

In total, my Python3 construct is:

tref        = time.mktime((1989, 12, 31,  0,  0,  0, 0, 0, -1))                                     
tsec        = tref + wn_days * 86400. + tow - leap_scnds                                            
pvttime     = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(tsec)) 
# result: e.g. 2018-02-22 15:09:49 UTC

This gives me correct UTC time.

ullix
  • 333
  • 1
  • 3
  • 14