Description
We are encountering challenges with converting ZonedDateTime fields when using the Couchbase SDK. Our application relies on Java’s standard ZonedDateTime type for several temporal fields, as it retains not only the date and time, but also the associated time zone and offset. This level of precision is essential to ensure a consistent user experience, as well as for auditing and business logic purposes.
For further context :
Preserving Java’s standard ZonedDateTime type-2025052409201526 (1).pdf
Then we tried to solve it, After reviewing, the converters should use CouchbaseDocument (instead of JsonObject). That’s what Couchbase uses internally when de/serializing. Adjust the converters as necessary to achieve the desired result. This also shows overriding additionalConverters (instead of overriding customConversions()).
@Override
protected void additionalConverters(List<Object> converters) {
converters.add(new ZonedDateTimeToCouchbaseDocumentConverter());
converters.add(new CouchbaseDocumentToZonedDateTimeConverter());
}
@WritingConverter
public class ZonedDateTimeToCouchbaseDocumentConverter implements Converter<ZonedDateTime, CouchbaseDocument> {
@Override public CouchbaseDocument convert(ZonedDateTime source) {
CouchbaseDocument obj = new CouchbaseDocument();
// Adjust construction of document as needed
obj.put("dateTime", source.toOffsetDateTime().toString());
obj.put("offset", source.getOffset().toString());
obj.put("zone", source.getZone().toString());
return obj;
}
}
@ReadingConverter
public class CouchbaseDocumentToZonedDateTimeConverter implements Converter<CouchbaseDocument, ZonedDateTime> {
@Override public ZonedDateTime convert(CouchbaseDocument source) {
// Adjust construction of ZonedDateTime as needed. This implementation ignores offset and zone.
ZonedDateTime obj = ZonedDateTime.parse(source.get("dateTime").toString());
return obj;
}
}
public static class ZDTEntity extends BaseEntity {
public ZonedDateTime zdt;
public ZDTEntity(ZonedDateTime zdt) {
this.zdt = zdt;
}
}
@Test
void writesZonedDateTime() {
CouchbaseDocument converted = new CouchbaseDocument();
ZDTEntity entity = new ZDTEntity(ZonedDateTime.parse("2025-01-31T10:20:49Z"));
customConverter.write(entity, converted);
Map<String, Object> result = converted.export();
Map<String,Object> attr0 = (Map<String,Object>)result.get("zdt");
assertThat(attr0.get("dateTime")).isEqualTo(entity.zdt.toOffsetDateTime().toString());
assertThat(attr0.get("offset")).isEqualTo(entity.zdt.getOffset().toString());
assertThat(attr0.get("zone")).isEqualTo(entity.zdt.getZone().toString());
assertThat(converted.getId()).isEqualTo(BaseEntity.ID);
System.err.println(attr0);
}
{dateTime=2025-01-31T10:20:49Z, offset=Z, zone=Z}
@Test
void readsZonedDateTime() {
CouchbaseDocument source = new CouchbaseDocument();
source.put("t", StringEntity.class.getName());
CouchbaseDocument attr0 = new CouchbaseDocument();
ZonedDateTime zdt = ZonedDateTime.parse("2025-01-31T10:20:49Z");;
attr0.put("dateTime", zdt.toOffsetDateTime());
attr0.put("offset", zdt.getOffset().toString());
attr0.put("zone", zdt.getZone());
source.put("zdt", attr0);
ZDTEntity converted = customConverter.read(ZDTEntity.class, source);
assertThat(converted.zdt.toOffsetDateTime()).isEqualTo(zdt.toOffsetDateTime());
System.err.println(converted.zdt);
}
But still got an error :
Actually, The code above looks almost correct for both the reading and writing converters — what’s left is just how to register them in the Couchbase configuration. it’s the section in the Couchbase config where we register the reading and writing converters that have already been defined.