使用 Spring Boot 和 Jackson 的日期时区 [英] Date timezone with Spring boot and Jackson
问题描述
我正在开发一个处理日期的 Spring Boot 应用程序.当我提交一个具有 startDateTime
和 endDateTime
的 Appointment 对象(两者都是 java.util.Date
类型)时,我发送的格式如下这个:
I'm developing a spring boot application that handles dates. When I submit an Appointment object that has a startDateTime
and an endDateTime
(Both are of type java.util.Date
) I send a format like this:
{
"lastName": "Jhon",
"firstName": "Doe",
"email": "jhon.doe@gmail.com",
"description": "MyDescription",
"startDateTime": "2017-10-09T22:43:07.109+0300",
"endDateTime": "2017-10-09T21:40:07.109+0300",
}
当数据保存在数据库中时,它具有正确的时区,当我尝试取回我的数据时,它们在我调试时似乎是正确的,但是,一旦它们被 Jackson 序列化,我就有一个输出,这些值作为值:
When data is persisted in database it's with the correct timezone, when I try to retrieve my data back, they seem correct when I'm debugging however, once they are serialized by Jackson I have an output with these as value:
"startDateTime": "2017-10-09T19:43:07.109+0000",
"endDateTime": "2017-10-09T18:40:07.109+0000",
如何配置 Jackson 以使用存储库中数据附带的时区?
How can I configure Jackson to make usage of timezone that comes with the data from my repository?
------更新---------
------Update---------
我用 OffsetDateTime
尝试了答案,但输出很奇怪:
I tried the answer with OffsetDateTime
but the output is quite weird:
"startDateTime": {
"offset": {
"totalSeconds": 7200,
"id": "+02:00",
"rules": {
"fixedOffset": true,
"transitionRules": [],
"transitions": []
}
},
"month": "OCTOBER",
"year": 2017,
"hour": 21,
"minute": 49,
"nano": 654000000,
"second": 15,
"dayOfMonth": 9,
"dayOfWeek": "MONDAY",
"dayOfYear": 282,
"monthValue": 10
}
我想要类似的东西:
2017-10-09T22:43:07.109+0300
2017-10-09T22:43:07.109+0300
推荐答案
A java.util.Date
没有任何时区信息.一旦将 String
反序列化为 Date
,偏移量 +0300
就会丢失:日期只保留时间戳值,它不能知道它来自哪个原始时区.
A java.util.Date
doesn't have any timezone information. Once you deserialize a String
to a Date
, the offset +0300
is lost: the date keeps just the timestamp value, and it can't know what's the original timezone it came from.
如果输出必须始终在 +03:00
偏移量中,您可以使用 com.fasterxml.jackson.annotation.JsonFormat
注释:
If the output must always be in +03:00
offset, you can set it directly in the respective fields, using the com.fasterxml.jackson.annotation.JsonFormat
annotation:
@JsonFormat(timezone = "GMT+03:00")
private Date startDateTime;
@JsonFormat(timezone = "GMT+03:00")
private Date endDateTime;
这样,日期字段将始终序列化为 +03:00
偏移量:
With this, the date fields will always be serialized to +03:00
offset:
{
"startDateTime":"2017-10-09T22:43:07.109+0300",
"endDateTime":"2017-10-09T21:40:07.109+0300"
}
<小时>
如果输入可以在任何其他偏移量中(不仅是 +03:00
)并且您想保留它,java.util.Date
不是理想的类型.一种替代方法是使用 Jackson Modules Java 8,如果您使用的是 Java >= 8.
If the inputs can be in any other offset (not only +03:00
) and you want to preserve it, the java.util.Date
isn't the ideal type. One alternative is to use Jackson Modules Java 8, if you're using Java >= 8.
对于 Java 6 和 7,有 ThreeTen Backport 和相应的 Jackson 模块 - 不过我还没有测试过,但代码可能相似,因为 ThreeTen Backport 包含相同的类和方法,只有包不同 - (在 Java 8 中是 java.time
,而在 ThreeTen Backport 中是 org.threeten.bp
).
For Java 6 and 7, there's the ThreeTen Backport and the corresponding Jackson module - I haven't tested, though, but the code might be similar, as the ThreeTen Backport contains the same classes and methods, only the package is different - (in Java 8 is java.time
and in ThreeTen Backport is org.threeten.bp
).
要保留日期、时间和偏移量,最好的替代方法是 OffsetDateTime
类.所以你只需要改变字段类型并为其设置相应的格式即可:
To preserve the date, time and offset, the best alternative is the OffsetDateTime
class. So you just need to change the fields type and set the corresponding format to it:
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXX")
private OffsetDateTime startDateTime;
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXX")
private OffsetDateTime endDateTime;
在对象映射器中,您还必须注册 JavaTimeModule
并禁用 ADJUST_DATES_TO_CONTEXT_TIME_ZONE
功能,因此保留偏移量(默认行为是转换为 Jackson 上下文的时区,这可能与输入中使用的不同 - 通过禁用此功能,偏移量将被保留).
In the object mapper, you must also register the JavaTimeModule
and disable the ADJUST_DATES_TO_CONTEXT_TIME_ZONE
feature, so the offsets are preserved (the default behaviour is to convert to Jackson context's timezone, which might not be the same used in the inputs - by disabling this, the offset is preserved).
您可以使用 JacksonConfigurator
(如在本答案中解释)并进行这些配置:
You can use a JacksonConfigurator
(as explained in this answer) and do these configurations:
ObjectMapper om = new ObjectMapper();
om.registerModule(new JavaTimeModule());
om.configure(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE, false);
这个配置通常就足够了,但您也可以将SerializationFeature.WRITE_DATES_AS_TIMESTAMPS
设置为false
,以防万一.
This configuration is usually enough, but you can also set SerializationFeature.WRITE_DATES_AS_TIMESTAMPS
to false
as well, just in case.
如果您仍然需要使用 java.util.Date
,您可以使用 API 进行相互转换.在 Java 8 中,有新的 Date.from
方法:
If you still need to work with java.util.Date
, you can use the API to convert from/to it. In Java 8, there's the new Date.from
method:
// convert to java.util.Date
public Date getStartAsJavaUtilDate() {
return Date.from(startDateTime.toInstant());
}
在 ThreeTen Backport 中,有 org.threeten.bp.DateTimeUtils
类:
And in ThreeTen Backport, there's the org.threeten.bp.DateTimeUtils
class:
// convert to java.util.Date
DateTimeUtils.toDate(startDateTime.toInstant());
将 Date
转换回 OffsetDateTime
,不过,这更棘手.Date
对象没有时区信息,因此无法知道原始偏移量.一种替代方法是将原始偏移量保留在单独的变量中:
To convert a Date
back to OffsetDateTime
, though, it's more tricky. The Date
object has no timezone information, so it can't know the original offset. One alternative is to keep the original offset in a separate variable:
// keep the original offset
ZoneOffset startDateOffset = startDateTime.getOffset();
然后,您可以将Date
转换为Instant
,然后再转换为原始偏移量:
Then, you can convert the Date
to Instant
, and then convert it to the original offset:
// convert java.util.Date to original offset (Java 8)
startDateTime = date.toInstant().atOffset(startDateOffset);
// ThreeTen Backport
startDateTime = DateTimeUtils.toInstant(date).atOffset(startDateOffset);
这篇关于使用 Spring Boot 和 Jackson 的日期时区的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!