为日期反序列化设置杰克逊时区
Posted
技术标签:
【中文标题】为日期反序列化设置杰克逊时区【英文标题】:Set Jackson Timezone for Date deserialization 【发布时间】:2011-11-25 06:59:08 【问题描述】:我正在使用 Jackson(通过 Spring MVC Annotations)将字段反序列化为来自 JSON 的 java.util.Date
。 POST 看起来像 - "enrollDate":"2011-09-28T00:00:00.000Z"
,但是当 Spring & Jackson 创建对象时,它将日期设置为 "2011-09-27 20:00:00"
。
如何在杰克逊设置正确的时区? 或者如果这不是问题,我如何从 JSON 消息中发送 EST?
Javascript/jQuery:
var personDataView = enrollDate : new Date($("#enrollDate").val()),
//...other members
;
$.postJSON('/some/path/', personDataView, function(data)
//... handle the response here
);
JSON 消息:
"enrollDate":"2011-09-28T00:00:00.000Z"
弹簧控制器:
@RequestMapping(value="/", method=RequestMethod.POST)
public @ResponseBody String saveProfile(@RequestBody personDataView persondataView, HttpServletRequest request)
//...dataView has a java.util.Date enrollDate field
//...other code
【问题讨论】:
你在哪个时区?如果您在 UTC 以西 4 小时,这 2 个印章是等价的。 我在美国东部时间。那么为什么杰克逊假设我发送的传入时间是格林威治标准时间?我应该更改我的 javascript 以以不同的方式发布日期,还是更改杰克逊的设置? 时间戳末尾的 Z 是 +00:00 aka Zulu time aka UTC / GMT 的简写。 啊不知道,谢谢!我更新了这个问题,提供了有关如何构建和发送日期的更多信息。 我认为这是反序列化,不是序列化。标题应该调整? 【参考方案1】:在 Jackson 2+ 中,您还可以使用 @JsonFormat 注解:
@JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-dd'T'HH:mm:ss.SSSZ", timezone="America/Phoenix")
private Date date;
如果这样不起作用,请尝试用单引号将 Z 括起来,即 pattern="yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
【讨论】:
@JsonFormat(shape= JsonFormat.Shape.STRING, pattern="yyyy-MM-dd'T'HH:mm:ss.SSSZ", timezone="US/Arizona")【参考方案2】:您是否在您的 application.properties 中尝试过这个?
spring.jackson.time-zone= # Time zone used when formatting dates. For instance `America/Los_Angeles`
【讨论】:
感谢这在本地修复了我的项目,在 OpenShift 中我还必须设置环境变量 TZ【参考方案3】:如果你真的希望 Jackson 返回一个时区不是 UTC 的日期(我自己对此有几个很好的论据,尤其是当一些客户只是没有得到时区部分时),那么我通常会这样做:
ObjectMapper mapper = new ObjectMapper();
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
dateFormat.setTimeZone(TimeZone.getTimeZone("CET"));
mapper.getSerializationConfig().setDateFormat(dateFormat);
// ... etc
对了解时区的人没有不良影响-p
【讨论】:
似乎 mapper.getSerializationConfig().setDateFormat(dateFormat) 现在已弃用,新的方法是 mapper.setDateFormat(dateFormat)【参考方案4】:我使用的是 Jackson 1.9.7,我发现执行以下操作并不能解决我的序列化/反序列化时区问题:
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss.SSSZ");
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
objectMapper.setDateFormat(dateFormat);
在 JSON 消息中,我得到的不是“2014-02-13T20:09:09.859Z”,而是“2014-02-13T08:09:09.859+0000”,这显然是不正确的。我没有时间单步调试 Jackson 库源代码来找出发生这种情况的原因,但是我发现如果我只是将 Jackson 提供的 ISO8601DateFormat
类指定给 ObjectMapper.setDateFormat
方法,那么日期是正确的。
除了这没有把毫秒放在我想要的格式中,所以我将ISO8601DateFormat
类子分类并覆盖format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition)
方法。
/**
* Provides a ISO8601 date format implementation that includes milliseconds
*
*/
public class ISO8601DateFormatWithMillis extends ISO8601DateFormat
/**
* For serialization
*/
private static final long serialVersionUID = 2672976499021731672L;
@Override
public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition)
String value = ISO8601Utils.format(date, true);
toAppendTo.append(value);
return toAppendTo;
【讨论】:
输出与您提供的 SimpleDateFormat 完全匹配。根据SimpleDateFormat Javadoc:hh 输出 12 小时时间,kk 输出 24 时间,Z 输出时区偏移量。我想你想要一个明确的日期格式yyyy-MM-dd'T'kk:mm:ss.SSS'Z'
。
日期的格式是一个小问题,对我来说主要问题是 ObjectMapper 吐出的实际日期值不正确。仔细看我给出的例子,日期输出实际上与预期的相差 12 小时,也就是说,我得到的不是“2014-02-13T20:09:09.859Z”,而是“2014-02-13T08:09:09.859” +0000"
08:09:09 in 12 小时(缺少 am/pm 指示符)相当于 24 中的 20:09:09小时时间。
你说得对,我对 ISO8601 日期时间格式规范和 SimpleDateFormat 之间的区别感到困惑,它们是不同的。在 ISO8601 中,'hh' 表示 24 小时制,但在 SimpleDateFormat 中,它表示 12 小时制 (w3.org/TR/NOTE-datetime) 啊!所以我的解决方案仍然有效,但在我的场景中是不必要的。谢谢。【参考方案5】:
看起来较旧的答案适用于较旧的 Jackson 版本,但由于 objectMapper
具有方法 setTimeZone(tz)
,因此完全忽略在 dateFormat 上设置时区。
如何在 Jackson 2.11.0 版中正确设置时区到 ObjectMapper:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setTimeZone(TimeZone.getTimeZone("Europe/Warsaw"));
完整示例
@Test
void test() throws Exception
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.findAndRegisterModules();
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
JavaTimeModule module = new JavaTimeModule();
objectMapper.registerModule(module);
objectMapper.setTimeZone(TimeZone.getTimeZone("Europe/Warsaw"));
ZonedDateTime now = ZonedDateTime.now();
String converted = objectMapper.writeValueAsString(now);
ZonedDateTime restored = objectMapper.readValue(converted, ZonedDateTime.class);
System.out.println("serialized: " + now);
System.out.println("converted: " + converted);
System.out.println("restored: " + restored);
Assertions.assertThat(now).isEqualTo(restored);
`
【讨论】:
我使用相同的方法,但设置 JVM 时区 mapper.setTimeZone(TimeZone.getDefault()),以避免对特定时区进行硬编码。【参考方案6】:刚遇到这个问题,终于意识到LocalDateTime没有任何时区信息。如果您收到带有时区信息的日期字符串,则需要将其用作类型:
ZonedDateTime
查看link
【讨论】:
我在尝试为 LocalDateTime 指定时区时不断收到此错误,即使该模式包含时区 (pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ) .我把它改回日期,一切都开始工作了。JSON解析错误:无效格式:“2019-04-01T00:00:00+0300”在“+0300”处格式错误;嵌套异常是com.fasterxml.jackson .databind.JsonMappingException:无效格式:“2019-04-01T00:00:00+0300”在“+0300”处格式错误 @AmanicA 如何为 LocalDateTime 对象指定时区?本地时区应始终默认为您的本地时区。我认为你无法改变这一点。你最好像我在帖子中建议的那样使用 ZonedDateTime。【参考方案7】:您的日期对象可能没问题,因为您使用 GMT 时区发送了以 ISO 格式编码的日期,并且您在打印日期时处于美国东部标准时间。
请注意,日期对象在打印时执行时区转换。您可以检查您的 date
对象是否正确:
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
cal.setTime(date);
System.out.println (cal);
【讨论】:
【参考方案8】:我在日历反序列化方面遇到了同样的问题,解决了扩展 CalendarDeserializer。 它强制 UTC 时区 如果有人需要,我粘贴代码:
@JacksonStdImpl
public class UtcCalendarDeserializer extends CalendarDeserializer
TimeZone TZ_UTC = TimeZone.getTimeZone("UTC");
@Override
public Calendar deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException
JsonToken t = jp.getCurrentToken();
if (t == JsonToken.VALUE_NUMBER_INT)
Calendar cal = Calendar.getInstance(TZ_UTC);
cal.clear();
cal.setTimeInMillis(jp.getLongValue());
return cal;
return super.deserialize(jp, ctxt);
在 JSON 模型类中,只需将字段注释为:
@JsonDeserialize(using = UtcCalendarDeserializer.class)
private Calendar myCalendar;
【讨论】:
【参考方案9】:对于现在(2020 年 2 月)遇到此问题的任何人来说,以下 Medium 帖子对于我们克服它至关重要。
https://medium.com/@ttulka/spring-http-message-converters-customizing-770814eb2b55
在我们的例子中,应用程序使用了@EnableWebMvc,如果删除它会中断,因此“没有 Spring Boot 的生活”部分至关重要。这就是最终为我们解决这个问题的方法。它允许我们在序列化期间仍然使用和生成 JSON 和 XML 以及格式化我们的日期时间以满足应用程序的需求。
@Configuration
@ComponentScan("com.company.branch")
@EnableWebMvc
public class WebMvcConfig implements WebMvcConfigurer
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters)
converters.add(0, new MappingJackson2XmlHttpMessageConverter(
new Jackson2ObjectMapperBuilder()
.defaultUseWrapper(false)
.createXmlMapper(true)
.simpleDateFormat("yyyy-mm-dd'T'HH:mm:ss'Z'")
.build()
));
converters.add(1, new MappingJackson2HttpMessageConverter(
new Jackson2ObjectMapperBuilder()
.build()
));
【讨论】:
以上是关于为日期反序列化设置杰克逊时区的主要内容,如果未能解决你的问题,请参考以下文章