0

Recently i've tried to use milliseconds representation of current date as a primary key for a database (Realm).

I could get date as a string and store it, but since i needed to perform fast ascending search on big amount of data i decided to store data in milliseconds format.

To achieve this i followed these steps:

1) Initialize a GregorianCalendar instance and pass current date

    Calendar c = new GregorianCalendar();
    c.setTime(new Date());

2) Set time to match exactly a midnight of current day

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

3) Convert results to milliseconds

    c.getTime().getTime()

Later i tested it by inserting objects to database. Below is the log output. I calculated the same date several times, all by code above without any changes.

01-03 06:59:38.607 16256-16256/com.example.bizarre.bindaccess D/Day: Long: 1 422 528 534 032 Date: 29.01.15
01-03 06:59:38.611 16256-16256/com.example.bizarre.bindaccess D/Day: Long: 1 422 528 647 420 Date: 29.01.15
01-03 06:59:38.611 16256-16256/com.example.bizarre.bindaccess D/Day: Long: 1 422 528 669 794 Date: 29.01.15
01-03 06:59:38.611 16256-16256/com.example.bizarre.bindaccess D/Day: Long: 1 422 528 707 566 Date: 29.01.15
01-03 06:59:38.615 16256-16256/com.example.bizarre.bindaccess D/Day: Long: 1 422 532 686 557 Date: 29.01.15
01-03 06:59:38.615 16256-16256/com.example.bizarre.bindaccess D/Day: Long: 1 422 532 726 052 Date: 29.01.15
01-03 06:59:38.615 16256-16256/com.example.bizarre.bindaccess D/Day: Long: 1 422 532 754 147 Date: 29.01.15

Despite of my actions to zero hours, mins, secs and millis and by that get a cleared midnight value in millis everytime i get slightly different value.

My suggestions are:

1) It depends on device time scheme.

2) It has something to do with android by itself.

3) A trouble is on my side.

Would be really glad if someone could help me out.

P.S. I also used a site http://www.fileformat.info/tip/java/date2millis.htm to get millis of 29.01.15, result was 1 422 489 600 000.

Updated #1. 1) All inserts were on the same device, local timezone was not changed, so environment was not affected anyhow. 2) As i have stated above, maybe it wasn't clear - i save data in millis, in a class field of type long. That field is also not affected anyhow.

Updated #2. Actually, my question was: "How could behaviour i encountered be explained?". But since it might be too broad or unclear, i focus this on what i initially wanted to achieve - "How to get the current date in local (on-device) time zone in a form of consistent milliseconds value (type of long)?"

Kirill Starostin
  • 868
  • 1
  • 7
  • 16
  • How are you saving and retrieving the calendar from the database? If you set the time to zero, it shouldn't vary the millis value –  Aug 07 '17 at 11:58
  • 1
    do all devices use the same timezone? – k3b Aug 07 '17 at 13:18
  • @k3b updated answer providing info you requested. – Kirill Starostin Aug 07 '17 at 18:16
  • @Hugo updated answer providing info you requested. – Kirill Starostin Aug 07 '17 at 18:16
  • Is your question: *How to take a count-from-epoch number and alter it to represent 00:00:00 time-of-day for that date in UTC?* – Basil Bourque Aug 07 '17 at 18:33
  • @BasilBourque Thanks for your detailed answer, i will look through it closely. Sorry for being unclear with a question - i updated info. – Kirill Starostin Aug 07 '17 at 18:45
  • @KirillStarostin Very well. Do study the many other Questions and Answers Stack Overflow on this topic – your Question is basically a duplicate of many others. – Basil Bourque Aug 07 '17 at 19:01
  • By the way, for a unique identifier, you might consider a [UUID](https://en.wikipedia.org/wiki/Universally_unique_identifier) as suggested in [the Realm documentation](https://realm.io/docs/objc/latest/#limitations-models). Some databases such as Postgres store a UUID value efficiently as its true 128-bit value. I do not know about Realm, and its doc seems to mention storing as text, presumably the 36 (or 32) character canonical hexadecimal string representation of those 128 bits which would be less efficient. – Basil Bourque Feb 10 '18 at 06:36
  • @BasilBourque Currently i'm using a 'dayDate' field represented in long as an unique identifier (thanks to your great answer i made it to work). Also, due to the apk's optimization i changed the database to SQLite. But thanks, this piece of knowledge will come in handy!:) – Kirill Starostin Feb 11 '18 at 08:59

1 Answers1

3

tl;dr

Instant.ofEpochMilli( 1_422_528_534_032L )
       .truncatedTo( ChronoUnit.DAYS ) 

2015-01-29T00:00:00Z

Your question?

While not clear, I will interpret your Question to be: How to take a count-from-epoch number and alter it to represent 00:00:00 time-of-day for that date in UTC?

"How to get the current date in local (on-device) time zone in a form of consistent milliseconds value (type of long)?"

A count-from-epoch is nearly always in UTC (doing otherwise is troublesome and nearly crazy IMHO). So you may be confusing two different aspects: (a) count-from-epoch in UTC (an Instant), and (b) that very same moment but seen through the lens of the wall-clock time in use by people in a particular region (a ZonedDateTime).

Big big tip: As a programmer, learn to think and work in UTC. So too do your logging and storing/exchanging data all in UTC. Think of UTC as The One True Time™ and all others are mere variations.

Another tip: Do not spend your precious time and mental energy pondering the legacy Date/Calendar classes. They are a wretched mess that we can now happily abandon with the arrival of the industry-leading java.time classes.

Avoid legacy date-time classes

You are using troublesome old date-time classes now supplanted by the java.time classes. See below for Android.

Be sure to handle your count-from-epoch numbers as 64-bit long/Long rather than 32-bit int/Integer.

long input = 1_422_528_534_032L ; // Numeric literal for a `long` in modern Java.

The Instant class represents a moment on the timeline in UTC with a resolution of nanoseconds (up to nine (9) digits of a decimal fraction).

Instant instant = Instant.ofEpochMilli( input ) ;

instant.toString(): 2015-01-29T10:48:54.032Z

To effectively clear the time-of-day to 00:00:00, we can truncate by specifying ChronoUnit.DAYS.

Instant instantTruncated = instant.truncatedTo( ChronoUnit.DAYS ) ;

instantTruncated.toString(): 2015-01-29T00:00:00Z

You can extract a count-from-epoch if you want to compare.

long millisSinceEpoch = instantTruncated.toEpochMilli() ;

1422489600000

For fun, let's calculate the delta.

long delta = ( input - millisSinceEpoch ) ;

38934032

Also, let's represent that delta as a Duration where the toString method generates a string in standard ISO 8601 format.

Duration d = Duration.ofMillis( delta ) ;

d.toString(): PT10H48M54.032S

See all this code run live at IdeOne.com.

If you want to see the same moment, the point on the timeline, through the lens of the wall-clock time seen by people in a particular region, apply a time zone (ZoneId) to get a ZonedDateTime object.

ZoneId z = ZoneId.of( "Europe/Sofia" ) ; // Or "Asia/Kolkata", whatever.
ZonedDateTime zdt = instant.atZone( z ) ;  // Same moment in history, but viewed using the wall-clock time in some particular region.

By the way, while the start-of-day is always 00:00:00 in UTC, that is not always true in other time zones. Because of Daylight Saving Time (DST) and other anomalies, some dates in some zones may start at another time-of-day such as 01:00:00. Let java.time determine the start of day:

ZonedDateTime zdt = LocalDate.of( 2015 , Month.JANUARY , 29 ).atStartOfDay( ZoneId.of( "Asia/Gaza" ) ) ;
long millisFromEpoch = zdt.toInstant().toEpochMilli() ;  // Generate a count-from-epoch of UTC in milliseconds.

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.

Where to obtain the java.time classes?

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • And again, thank you for your detailed and brief explanation on the topic:) And for all of the tips! I got to solve my problem perfectly. – Kirill Starostin Aug 08 '17 at 08:21