1

I am trying to use mongodb to fetch some records with date fields, sample records are shown below, and want convert date field which has been parsed using jayway jsonpath to java.util.Date long integer. long integer converted does not match with the original one. Please help.

Sample records in tester collection:

{
    "_id" : ObjectId("5b3fe6f91e618afb473dc644"),
    "dateField" : ISODate("2018-07-06T15:46:55.819Z")
}

The Java code for getting records using jongo is as follows :

List<Tester> list=  jongo.runCommand("{aggregate : 'tester',pipeline:[],cursor : {batchSize :10}}")
          .field("cursor")
          .as(Tester.class);

for(Tester t : list)
{
    System.out.println("dateField test: : : : "+t.getDateField()+" : : : : "+t.getDateField().getTime());

    // Output is perfectly fine : dateField test: : : : Fri Jul 06 21:16:55 IST 2018 : : : : 1530892015819


    Gson gson = new Gson();
    String str = gson.toJson(t);
    DocumentContext docCtx = JsonPath.parse(str);
    JsonPath jsonPath = JsonPath.compile("$.dateField");
    Object obj = docCtx.read(jsonPath);

    System.out.println(obj);
    //After parsing with jsonPath the date is retained - Jul 6, 2018 9:16:55 PM
    SimpleDateFormat format = new SimpleDateFormat("MMM dd, yyyy hh:mm:ss aaa");
    Date d = format.parse(obj.toString());
    System.out.println(d + " : : : " + d.getTime());
    //Fri Jul 06 21:16:55 IST 2018 : : : 1530892015000 - Time is not retained       
}

Expected : t.getDateField().getTime() ==== d.getTime()

Please help

Regards

Kris

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
chiku
  • 485
  • 2
  • 8
  • 23
  • 2
    Your input into `SimpleDateFormat` does not included the fraction of a second component - `Fri Jul 06 21:16:55 IST 2018` isn't exactly the same as ` 2018-07-06T15:46:55.819Z`, it's missing `.819` of a second. You need to go back and have a look at out `jongo` is converting the `dateField`, because it appears to be wrong (or it's missing some data). If `obj` is already a `Date` object, then don't use `obj.toString()`, because it's using the wrong format – MadProgrammer Jul 07 '18 at 00:44
  • the jongo part is perfectly fine System.out.println(t.getDateField()+" : : : : "+t.getDateField().getTime()); its output is : Fri Jul 06 21:16:55 IST 2018 : : : : 1530892015819, the other part is with SimpleDateFormat("MMM dd, yyyy hh:mm:ss aaa").parse(obj.toString()).getTime() has NOT given the right answer – chiku Jul 07 '18 at 01:02
  • 2
    Then, as I said, IF `obj` is already a `Date` object, DON'T use `toString` as it's giving back the wrong format (`Fri Jul 06 21:16:55 IST 2018`). The other possible issue is `Gson` is parsing the object to an invalid format and you will need to investigate how you can provide your own format to it. The fact still remains, the data been passed to `SimpleDateFormat` is missing the fraction of a second component, so it can never be provide you the correct value – MadProgrammer Jul 07 '18 at 01:11

2 Answers2

2

tl;dr

  • Your formatting pattern omits the fractional seconds, so no milliseconds appear in the output.
  • You are using obsolete date-time classes. Use java.time instead.

Example:

Instant                                 // Represent a moment in UTC, with a resolution as fine as nanoseconds.
.parse( "2018-07-06T15:46:55.819Z" )    // Parse a string in standard ISO 8601 format. The `Z` on the end means UTC, pronounced “Zulu”.
.atZone( ZoneId.of( "Asia/Kolkata" ) )  // Adjust from UTC to a desired time zone. Same moment, same point on the timeline, different wall-clock time. Returns a `ZonedDateTime` object.
.toString()                             // Generate a String in standard ISO 8601 format. Represents the moment in our `ZonedDateTime` object.

Convert from legacy java.util.Date class to modern java.time.Instant, and back again. Example nonsense code:

java.util.Date.from(            // Convert from modern `Instant` to legacy `Date`. 
    myJavaUtilDate.toInstant()  // Convert from legacy `Date` to modern `Instant`.
)

java.time

You are using terribly troublesome old date-time classes: Date & SimpleDateFormat. These were supplanted years ago by the modern java.time classes.

Your input 2018-07-06T15:46:55.819Z is in standard ISO 8601 format. The java.time classes use the ISO 8601 formats by default when parsing or generating strings. So no need to specify a formatting pattern.

The Z on the end is pronounced Zulu and means UTC. The Instant class represents a moment in UTC.

Instant instant = Instant.parse( "2018-07-06T15:46:55.819Z" ) ; 

Generate an output string in ISO 8601 format.

String output = instant.toString() ;

2018-07-06T15:46:55.819Z

Your code ignores the crucial issue of time zone. Rather than rely implicitly on the JVM’s current default time zone, be explicit with a ZoneId even if that is ZoneId.systemDefault().

Specify a proper time zone name in the format of continent/region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 3-4 letter abbreviation such as EST or IST as they are not true time zones, not standardized, and not even unique(!). For example, your IST could mean Irish Standard Time, India Standard Time, Iran Standard Time, or something else.

After adjusting from UTC to a specific time zone, we still have the same moment, the same point on the timeline. Only the wall-clock time is different.

ZoneId z = ZoneId.of( "Asia/Kolkata" ) ;  // Or `ZoneId.systemDefault()`.
ZonedDateTime zdt = instant.atZone( z ) ; // Adjust from UTC to a specific time zone. 

Generate an output string in ISO 8601 format extended to append the name of the time zone in square brackets.

String output = zdt.toString() ;

2018-07-06T21:16:55.819+05:30[Asia/Kolkata]

Notice your fractional second (milliseconds) is still intact.

Converting

Perhaps you must interface with an java.util.Date (your Question is not clear), because of old code not yet updated to support java.time.

You will find convenient conversion methods, new methods added to the old classes.

Going from java.util.Date to java.time.Instant.

Instant myInstant = myJavaUtilDate.toInstant() ;

Proceed as shown above. Adjust into your desired time zone, and generate a String.

Going the other direction, from the modern Instant class to the legacy class Date.

java.util.Date myDate = java.util.Date.from( myInstant ) ;

Immutable objects

The java.time classes are designed to be thread-safe, and use the immutable objects pattern. Notice how the code above produces fresh objects based on the original’s values, rather than altering (“mutating”) the original.


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
1
new SimpleDateFormat("MMM dd, yyyy hh:mm:ss aaa");

You are discarding the milliseconds part of the input, which results in exactly the difference you see. Use this instead:

new SimpleDateFormat("MMM dd, yyyy hh:mm:ss.SSS aaa");
                                           ^^^^
Jim Garrison
  • 85,615
  • 20
  • 155
  • 190