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年9月13日15:30:00.0
这是因为Timestamp::toString()
方法(这是在所谓的含蓄System.out.println
)在JVM默认时区中打印时间戳。在这种情况下,默认值为Asia/Calcutta
,上午10点(UTC)与15:30在加尔各答相同,因此上面的输出是生成的。
正如我在my answer to your other question中所解释的,Timestamp
对象没有任何时区信息。它自unix时代以来只有几纳秒(1970-01-01T00:00Z
或“1970年1月1日午夜UTC”)。
使用上面的例子,如果你看到的value.getCreatedOn().getTime()
的价值,你会看到它的1505296800000
- 这是毫秒数,因为时代(以获得毫微秒的精度,有方法getNanos()
)。
这个毫秒值对应UTC的上午10点,圣保罗的上午7点,伦敦的上午11点,东京的下午7点,加尔各答的15点30分等等。您不会在区域之间转换Timestamp
,因为世界各地的毫米值都是一样的。
但是,您可以更改的是此值的表示(特定时区中的相应日期/时间)。在杰克逊,您可以创建自定义序列化器和反序列化器(通过扩展com.fasterxml.jackson.databind.JsonSerializer
和com.fasterxml.jackson.databind.JsonDeserializer
),因此您可以更好地控制如何格式化和解析日期。
首先我创建了格式化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);
}
}
然后我创建了一个解串器读取JVM中的默认时区的日期和创建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());
}
}
我也换领域使用这些定制类: 现在,当使用上面相同的Timestamp
(即对应于2017-09-13T10:00:00Z
)。序列化它产生:
{ “createdOn”: “2017年9月13日15:30”},现在的输出对应于JVM默认的时区(本地时间15
注意: 30在Asia/Calcutta
)。
当反序列化这个JSON时,我得到相同的Timestamp
(对应于UTC的上午10点)。
该代码使用JVM默认时区,但它can be changed without notice, even at runtime,所以最好总是让它明确你使用哪一个。
API使用IANA timezones names(总是在格式Region/City
,像Asia/Calcutta
或Europe/Berlin
),这样你就可以使用他们创造ZoneId.of("Asia/Calcutta")
。 避免使用3字母缩写(如IST
或),因为它们是ambiguous and not standard。
通过调用ZoneId.getAvailableZoneIds()
,您可以获得可用时区列表(并选择最适合您系统的时区)。
如果要将输出更改为与其他时区相对应,请将ZoneId.systemDefault()
更改为所需的区域。请记住,同一区域必须用于序列化和反序列化,否则您将得到错误的结果。
不是Java 8?
如果您使用的是Java < = 7,则可以使用ThreeTen Backport,这是用于Java 8的新日期/时间类的一个很好的后端。
唯一的区别是包名(在Java中8是java.time
和ThreeTen反向移植是org.threeten.bp
),但类和方法名称是相同的。
还有另一个不同之处:只在Java 8 Timestamp
类有方法toInstant()
和from()
,所以你需要使用org.threeten.bp.DateTimeUtils
类,使转换:
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 = DateTimeUtils.toInstant(value).atZone(ZoneId.systemDefault());
String str = fmt.format(z);
gen.writeString(str);
}
}
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 DateTimeUtils.toSqlTimestamp(dt.atZone(ZoneId.systemDefault()).toInstant());
}
}
如果你的时区偏移量为5: 30,为什么在这两个时间之间只有5个小时? 30分钟去哪了? – Andreas
修正了错字! –