Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Joda-Time to form correct ISODate for Mongo insert

I am trying to update date fields in mongo that require an ISODate format. In mongo, it looks like this:

"crDt" : ISODate("2013-08-19T17:21:57.549Z")

The Java framework I am using has me restricted to using strings as my test parameters, so I am trying to use that string with a DateTimeFormatter to get it into the correct ISODateTimeFormat and then pass that into mongo. I cannot just pass in a string that looks like what I have above. Trying to do so screws up the field in mongo. The relevant bits of Joda-Time code I am using look like this:

//I can't get this right.
String crDt = "2013-01-19T15:28:58.851Z";

DateTimeFormatter parser = ISODateTimeFormat.dateHourMinuteSecondMillis();

parser.parseDateTime(crDt);

// this method updates the record in mongo. This method totally works, so no 
// point in pasting it here, I just can't get the parser object correct to be 
// in the correct format once inserted, it needs to be the correct ISODate form.
mongo.setCrDt(recordId, parser);

And when the code runs I get errors like these from the .parseDateTime method:

java.lang.IllegalArgumentException: Invalid format: "2013-01-19T15:28:58.851Z" is malformed at "Z"
    at org.joda.time.format.DateTimeFormatter.parseDateTime(DateTimeFormatter.java:866)

I can tell the string I am giving is not correct to get things parsed. I've tried leaving off the Z, I've tried other combos, but each time it says it's malformed. So basically, what does my starting string need to be to get the .parseDateTime to work and give me an object that looks correct?

EDIT:

Updated to try the suggestions provided below. The issue I run into now is an IllegalArgumentException, can't serialize class org.joda.time.DateTime. So it appears persisting joda time objects in a no-go? I also looked at the other suggestion, looking into mapper frameworks like Spring Data. It looks like there is a whole lot more that needs to go into this. Is there really no simple way to persist this into mongo?

EDIT2:

OK, I think I have it now. I might not have a total grasp of all the mechanics at play, but BasicDBObjects won't play nice with DateTime. Date objects seem to be the only way to go, at least in the implementation I'm dealing with. I did the following:

DateTimeFormatter parser = ISODateTimeFormat.dateTime();
DateTime result;
Date newResult;
result = parser.parseDateTime(crDt);
newResult = result.toDate();

I then passed in newResult for the BasicDBObject to then update the record in mongo. It works fine, and the record is updated correctly.

like image 752
Benny Avatar asked Aug 19 '13 18:08

Benny


3 Answers

Your input string format is correct, as long is that is intended to represent UTC.

Change your parser to use the one that matches this format:

DateTimeFormatter parser = ISODateTimeFormat.dateTime();

The rest of your question doesn't make much sense to me. You shouldn't pass the parser, but rather the return value from parseDateTime, which you don't appear to be capturing.

DateTime result = parser.parseDateTime(crDt);

mongo.setCrDt(recordId, result.toDate());

Whether or not that last line will work depends on what that function accepts.

like image 128
Matt Johnson-Pint Avatar answered Oct 06 '22 01:10

Matt Johnson-Pint


I solved this by adding an "Encoding Hook" in the constructor of the Service class where I do the updates to MongoDB. This will allow you to use org.joda.time.DateTime in your code and that will be saved as java.util.Date in MongoDB.

MyService.java

@Inject
public MyService(com.mongodb.Client client) {
      BSON.addEncodingHook(DateTime.class, new JodaTimeTransformer());
      BSON.addDecodingHook(Date.class, new JodaTimeTransformer());
      this.mongoClient = mongoClient;
}

JodaTimeTransformer.java

import java.util.Date;

import org.joda.time.DateTime;

public class JodaTimeTransformer implements org.bson.Transformer {

    @Override
    public Object transform(Object o) {
        if(o instanceof DateTime) {
            return ((DateTime)o).toDate();
        }
        else if(o instanceof Date) {
            return new DateTime((Date) o);
        }
        throw new IllegalArgumentException("JodaTimeTransformer can only be used with DateTime or Date");
    }

}
like image 22
Miguel Reyes Avatar answered Oct 06 '22 01:10

Miguel Reyes


The answer by Matt Johnson is correct. But it could be even simpler: Pass the (ISO 8601) string directly to constructor of DateTime. No need for a formatter.

Pay attention to time zone. A DateTime object in Joda-Time truly knows its own assigned time zone, unlike a java.util.Date object. Do you want your DateTime object to be assigned the JVM’s default time zone, no time zone (UTC), or a specific time zone?

For a date-time assigned the default time zone.

DateTime dateTime = new DateTime( "2013-01-19T15:28:58.851Z" );

For a date-time assigned UTC/GMT (no time zone offset).

DateTime dateTime = new DateTime( "2013-01-19T15:28:58.851Z", DateTimeZone.UTC );

For a date-time assigned a specific time zone.

DateTime dateTime = new DateTime( "2013-01-19T15:28:58.851Z", DateTimeZone.forId( "Europe/Paris" ) );
like image 45
Basil Bourque Avatar answered Oct 05 '22 23:10

Basil Bourque