我的Java Bean中有一个字段,如下所示@JsonFormat
是杰克逊注释:
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm")
private Timestamp createdOn;
当我返回Timestamp
时,它总是在我的JSON响应中转换为UTC时间。让我们说格式化我的时间戳,我得到2017-09-13 15:30
但我的回复是2017-09-13 10:00
。
我知道这是因为默认情况下Jackson注释采用系统TimeZone并将时间戳转换为UTC。 (在我的情况下,服务器属于Asia/Calcutta
时区,因此偏移量为+05:30
,Jackson映射器减去5小时30分钟,将时间戳转换为UTC)
有没有办法按原样返回时间戳,即2017-09-13 15:30
而不是2017-09-13 10:00
?
答案 0 :(得分:4)
我在这里做了一个测试,将JVM默认时区更改为Asia/Calcutta
并创建一个带java.sql.Timestamp
字段的类:
public class TestTimestamp {
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm")
private Timestamp createdOn;
// getter and setter
}
在other question中你告诉JDK 8正在使用,所以我在相同的版本中测试过(如果你使用的是另一个版本(JDK <= 7)),请检查“Not Java底部有8“部分)。首先,我创建了一个Timestamp
,对应于 2017年9月13日 th ,UTC时间上午10点:
TestTimestamp value = new TestTimestamp();
// timestamp corresponds to 2017-09-13T10:00:00 UTC
value.setCreatedOn(Timestamp.from(Instant.parse("2017-09-13T10:00:00Z")));
然后我用杰克逊的ObjectMapper
ObjectMapper om = new ObjectMapper();
String s = om.writeValueAsString(value);
结果String
是:
{“createdOn”:“2017-09-13 10:00”}
请注意,在生成的JSON中,输出为UTC(上午10点)。如果我反序列化这个JSON:
value = om.readValue(s, TestTimestamp.class);
System.out.println(value.getCreatedOn());
这将打印:
2017-09-13 15:30:00.0
这是因为Timestamp::toString()
方法(在System.out.println
中调用的隐含)在JVM默认时区中打印时间戳。在这种情况下,默认值为Asia/Calcutta
, 10 AM(UTC )与 15:30加尔各答相同,因此会生成上面的输出。< / p>
正如我在my answer to your other question中解释的那样,Timestamp
对象没有任何时区信息。自从unix epoch(1970-01-01T00:00Z
或“1970年1月1日午夜在UTC”)以来它只有纳秒数。
使用上面的示例,如果您看到value.getCreatedOn().getTime()
的值,您会看到它是1505296800000
- 这是自纪元以来的毫秒数(为了获得纳秒精度,有方法{ {1}})。
此毫秒值对应于UTC上午10点,圣保罗上午7点,伦敦上午11点,东京时间下午7点,加尔各答15点30分等。您不会在区域之间转换getNanos()
,因为世界各地的millis值都是相同的。
但是,您可以更改的是此值的表示(特定时区中的相应日期/时间)。
在Jackson中,您可以创建自定义序列化程序和反序列化程序(通过扩展Timestamp
和com.fasterxml.jackson.databind.JsonSerializer
),这样您就可以更好地控制格式化和解析日期的方式。
首先,我创建一个将com.fasterxml.jackson.databind.JsonDeserializer
格式化为JVM默认时区的序列化程序:
Timestamp
然后我创建一个反序列化器,它读取JVM默认时区中的日期并创建public class TimestampSerializer extends JsonSerializer<Timestamp> {
private DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
@Override
public void serialize(Timestamp value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException {
// get the timestmap in the default timezone
ZonedDateTime z = value.toInstant().atZone(ZoneId.systemDefault());
String str = fmt.format(z);
gen.writeString(str);
}
}
:
Timestamp
我还要更改字段以使用这些自定义类:
public class TimestampDeserializer extends JsonDeserializer<Timestamp> {
private DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
@Override
public Timestamp deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException, JsonProcessingException {
// parse to a LocalDateTime
LocalDateTime dt = LocalDateTime.parse(jsonParser.getText(), fmt);
// the date/time is in the default timezone
return Timestamp.from(dt.atZone(ZoneId.systemDefault()).toInstant());
}
}
现在,使用上面的// remove JsonFormat annotation
@JsonSerialize(using = TimestampSerializer.class)
@JsonDeserialize(using = TimestampDeserializer.class)
private Timestamp createdOn;
时(对应Timestamp
)。序列化产生:
{“createdOn”:“2017-09-13 15:30”}
请注意,现在输出对应于JVM默认时区中的本地时间(2017-09-13T10:00:00Z
中的15:30)。
反序列化此JSON时,我返回相同的Asia/Calcutta
(对应于UTC的上午10点)。
此代码使用JVM默认时区,但它can be changed without notice, even at runtime,因此最好始终明确指出您正在使用的时区。
API使用IANA timezones names(始终采用Timestamp
格式,如Region/City
或Asia/Calcutta
),因此您可以使用Europe/Berlin
创建它们。
避免使用3个字母的缩写(例如ZoneId.of("Asia/Calcutta")
或IST
),因为它们是ambiguous and not standard。
您可以致电PST
获取可用时区列表(并选择最适合您系统的时区)。
如果您想要将输出更改为与其他时区相对应,只需将ZoneId.getAvailableZoneIds()
更改为您想要的区域即可。请记住,必须使用相同的区域来序列化和反序列化,否则您将得到错误的结果。
如果你使用Java&lt; = 7,你可以使用ThreeTen Backport,这是Java 8的新日期/时间类的一个很好的后端。
唯一的区别是包名称(在Java 8中为ZoneId.systemDefault()
而在ThreeTen Backport中为java.time
),但类和方法名称是相同的。< / p>
还有另一个区别:只有在Java 8中,org.threeten.bp
类具有方法Timestamp
和toInstant()
,因此您需要使用from()
类来制作转化:
org.threeten.bp.DateTimeUtils
答案 1 :(得分:2)
您可以使用timeZone
明确指定所需的时区:
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm", timezone="Asia/Calcutta")
private Timestamp createdOn;