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
修正了錯字! –