Who is the Culprit ?

First Day at work in 2021!

Spent most of the day understanding a single line of code. This was originally written in Typescript.

return ChronoUnit.DAYS.between(Instant.ofEpochMilli(doc.${field1}.value.millis).truncatedTo(ChronoUnit.DAYS), Instant.ofEpochMilli(doc.${field2}.value.millis).truncatedTo(ChronoUnit.DAYS))

What was the issue?

For some data, the above code was returning -1 as the result. Example:

  • field1: December 22nd 2020 16:00:21 PST
  • field2: December 23rd 2020 00:00:00 PST

The values are converted to UTC before sending to the function, so essentially we get -

  • field1: December 23rd 2020 00:00:21
  • field2: December 23rd 2020 08:00:00

Now if you check the above return statement it has truncatedTo(ChronoUnit.DAYS). This is to make sure that for dates like 2020-12-23 23:59:00 and 2020-12-24 00:01:00 , the difference should be 1 day (As part of requirements !)

So now, given the above 2 date values, it should return 0 as the difference value but it is returning -1.

How did I approach and try to understand it?

When I tested this separately in Java (not in TypeScript because that language is still alien to me!)

import java.time.LocalDate;
import java.time.Month;
import java.time.temporal.ChronoUnit;

import java.time.*; 

public class Example {
   public static void main(String[] args) {
       /* LocalDate lct = LocalDate.of(2020, 12, 24);
        LocalDate rt = LocalDate.of(2020, 12, 23);
        long noOfDaysBetween = ChronoUnit.DAYS.between (lct, rt);
        System.out.println(noOfDaysBetween);*/

         /*This is 23-12-2020 00:00:21 UTC*/
        long milliseconds = 1608681621000L; 
     /*This is 23-12-2020 08:00:00 UTC*/
        long milliseconds2 = 1608710400000L; 

        Instant instant = Instant.ofEpochMilli(milliseconds); 

        Instant instant2 = Instant.ofEpochMilli(milliseconds2); 

        System.out.println(instant);
        System.out.println(instant2);
        System.out.println("Instant: "+ instant.truncatedTo (ChronoUnit.DAYS)); 
        System.out.println("Instant2: "+ instant2.truncatedTo (ChronoUnit.DAYS));

        long res = ChronoUnit.DAYS.between (instant.truncatedTo(ChronoUnit.DAYS),instant2.truncatedTo(ChronoUnit.DAYS));

        System.out.println(res);    
    } 
}

This is giving the below output-

2020-12-23T00:00:21Z
2020-12-23T08:00:00Z
Instant: 2020-12-23T00:00:00Z
Instant2: 2020-12-23T00:00:00Z
0

So the difference I got is 0 and not -1. So now the only two functions I did not test out are value and millis-

truncatedTo(ChronoUnit.DAYS), Instant.ofEpochMilli(doc.${field2}.value.millis)

Some extra points on "Instant" class behavior:

This line of code

Instant instant = Instant.ofEpochMilli(milliseconds);

This essentially returns a date value converted to UTC. This can be confirmed by doing a small activity: Use Epoch Converter to get milli seconds for a given time in either local or UTC time

For example, take the datetime value as 23-12-2020 00:00:21.

  • For UTC Timestamp in milliseconds: 1608681621000
  • For local [IST in my case]Timestamp in milliseconds: 1608661821000

Tweak the above program

long milliseconds_UTC = 1608681621000L; 

long milliseconds_local = 1608661821000L;
Instant instant_UTC = Instant.ofEpochMilli(milliseconds_UTC); 
Instant instant_local = Instant.ofEpochMilli(milliseconds_local); 
System.out.println(instant_UTC);
System.out.println(instant_local);

The output will be -

2020-12-23T00:00:21Z
2020-12-22T18:30:21Z

As we can see,for the UTC milliseconds value the date remained the same, but for the local one i.e., IST timezone , it got changed to UTC date. For clarification, 23-12-2020 00:00:21 IST when converted to UTC is 2020-12-22 18:30:21

So what happened to the original issue?

I was not able to replicate the issue and it got assigned to another team who actually own it but I will keep an eye on how it is solved and update it here as Part-2

But I am glad I came across this and learnt new stuffs!