I’m having trouble finding documentation on how I can convert a ZonedDateTime object to a BSON property on a BSON Document. I’m using driver version 4.5.0.
Encoding a ZonedDateTime: '2022-12-16T19:35:59.038918-05:00[America/New_York]' failed with the following exception:
Failed to encode 'ZonedDateTime'. Encoding 'zone' errored with: An exception occurred when encoding using the AutomaticPojoCodec.
Encoding a ZoneRegion: 'America/New_York' failed with the following exception:
Unable to get value for property 'id' in ZoneRegion
A custom Codec or PojoCodec may need to be explicitly configured and registered to handle this type.
A custom Codec or PojoCodec may need to be explicitly configured and registered to handle this type.
I’ve seen anything like this before. ZonedDateTime is not handled by the driver? If I were to write a PojoCodec, what format would I be converting that for MongoDB to see it as a date?
Well, I guess that what I’m trying to figure out. I need to retain time zone information, but also would like it if it’s a mongo date. Does mongo have any such type?
MongoDB does not support any such type directly. The closed thing is the BSON date type, which does not have time zone info. You might want to represent it as some sort of nested document that contains all the information you need. Something like:
{
"date" : <BSON Date value>, // assuming you only need millisecond precision>
"zoneId" : <BSON String value representing the zone>
}
If something like that works, I can show you how to write a custom Codec to accomplish the goal.
import org.bson.BsonDocument;
import org.bson.BsonDocumentReader;
import org.bson.BsonDocumentWriter;
import org.bson.BsonReader;
import org.bson.BsonWriter;
import org.bson.codecs.Codec;
import org.bson.codecs.DecoderContext;
import org.bson.codecs.EncoderContext;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
public class ZonedDateTimeCodec implements Codec<ZonedDateTime> {
@Override
public ZonedDateTime decode(BsonReader reader, DecoderContext decoderContext) {
reader.readStartDocument();
// There is some risk in assuming that the order of these fields in the document never changes.
// The order _could_ change in some circumstances, like if you update one of the fields independently
// of the other, or if you use some other code in some cases to create these documents. If you're
// concerned about the risk, then it's not that hard to re-write this code to remove the assumption
// that the "date" field is always first
long date = reader.readDateTime("date");
String zoneId = reader.readString("zoneId");
reader.readEndDocument();
return ZonedDateTime.ofInstant(Instant.ofEpochMilli(date), ZoneId.of(zoneId));
}
@Override
public void encode(BsonWriter writer, ZonedDateTime value, EncoderContext encoderContext) {
writer.writeStartDocument();
writer.writeDateTime("date",value.toInstant().toEpochMilli());
writer.writeString("zoneId", value.getZone().getId());
writer.writeEndDocument();
}
@Override
public Class<ZonedDateTime> getEncoderClass() {
return ZonedDateTime.class;
}
// Refactor this into some actual unit tests :)
public static void main(String[] args) {
var subject = ZonedDateTime.now();
System.out.println(subject);
var doc = new BsonDocument();
var writer = new BsonDocumentWriter(doc);
var codec = new ZonedDateTimeCodec();
codec.encode(writer, subject, EncoderContext.builder().build());
System.out.println(doc.toJson());
var reader = new BsonDocumentReader(doc);
var decoded = codec.decode(reader, DecoderContext.builder().build());
System.out.println(decoded);
}
}