像Oliver Drotbohm自己在DATAMONGO-2106中所说的那样,Spring Data MongoDB从未支持使用时区保留日期时间类型。
这些是已知的解决方法:
@Component
@WritingConverter
public class zoneddatetimeToDocumentConverter implements Converter<zoneddatetime, Document> {
static final String DATE_TIME = "dateTime";
static final String ZONE = "zone";
@Override
public Document convert(@Nullable zoneddatetime zoneddatetime) {
if (zoneddatetime == null) return null;
Document document = new Document();
document.put(DATE_TIME, Date.from(zoneddatetime.toInstant()));
document.put(ZONE, zoneddatetime.getZone().getId());
document.put("offset", zoneddatetime.getOffset().toString());
return document;
}
}
@Component
@ReadingConverter
public class DocumentTozoneddatetimeConverter implements Converter<Document, zoneddatetime> {
@Override
public zoneddatetime convert(@Nullable Document document) {
if (document == null) return null;
Date dateTime = document.getDate(DATE_TIME);
String zoneId = document.getString(ZONE);
ZoneId zone = ZoneId.of(zoneId);
return zoneddatetime.ofInstant(dateTime.toInstant(), zone);
}
}
@Configuration
public class MongoConfiguration extends AbstractMongoConfiguration {
@Value("${spring.data.mongodb.database}")
private String database;
@Value("${spring.data.mongodb.host}")
private String host;
@Value("${spring.data.mongodb.port}")
private int port;
@Override
public MongoClient mongoClient() {
return new MongoClient(host, port);
}
@Override
protected String getDatabaseName() {
return database;
}
@Bean
public CustomConversions customConversions() {
return new MongoCustomConversions(asList(
new zoneddatetimeToDocumentConverter(),
new DocumentTozoneddatetimeConverter()
));
}
}
public class zoneddatetimeCodec implements Codec<zoneddatetime> {
public static final String DATE_TIME = "dateTime";
public static final String ZONE = "zone";
@Override
public void encode(final BsonWriter writer, final zoneddatetime value, final EncoderContext encoderContext) {
writer.writeStartDocument();
writer.writeDateTime(DATE_TIME, value.toInstant().getEpochSecond() * 1_000);
writer.writeString(ZONE, value.getZone().getId());
writer.writeEndDocument();
}
@Override
public zoneddatetime decode(final BsonReader reader, final DecoderContext decoderContext) {
reader.readStartDocument();
long epochSecond = reader.readDateTime(DATE_TIME);
String zoneId = reader.readString(ZONE);
reader.readEndDocument();
return zoneddatetime.ofInstant(Instant.ofEpochSecond(epochSecond / 1_000), ZoneId.of(zoneId));
}
@Override
public Class<zoneddatetime> getEncoderClass() {
return zoneddatetime.class;
}
}
@Configuration
public class MongoConfiguration extends AbstractMongoConfiguration {
@Value("${spring.data.mongodb.database}")
private String database;
@Value("${spring.data.mongodb.host}")
private String host;
@Value("${spring.data.mongodb.port}")
private int port;
@Override
public MongoClient mongoClient() {
return new MongoClient(host + ":" + port, createOptions());
}
private MongoClientOptions createOptions() {
CodecProvider pojoCodecProvider = PojoCodecProvider.builder()
.automatic(true)
.build();
CodecRegistry registry = CodecRegistries.fromRegistries(
createCustomCodecRegistry(),
MongoClient.getDefaultCodecRegistry(),
CodecRegistries.fromProviders(pojoCodecProvider)
);
return MongoClientOptions.builder()
.codecRegistry(registry)
.build();
}
private CodecRegistry createCustomCodecRegistry() {
return CodecRegistries.fromCodecs(
new zoneddatetimeCodec()
);
}
@Override
protected String getDatabaseName() {
return database;
}
}