Skip to main content

Java Date/Calendar 如何迷失在時間裡

·272 words·2 mins·
Java Datetime Joda-Time
Author
Andreas Kung

如果問要在 Java 取得現在系統時間, 第一個想到就是 System.currentTimeMillis()。如果要印出日期, 就會想到 java.util.Datejava.util.Calendar

但是 這兩個 class 分別是 Java 1.0, Java 1.1, 快四十年前設計的東西。問題非常多, 早就應該被立法禁止使用了。

java.util.Date 沒有時區資訊
#

常有人誤以為 Date 有時區資訊, 因為印出來就有阿

val date = java.util.Date()
println(date)

//Output:
//Wed Mar 12 22:53:01 CST 2025

像這樣, 有出現 CST阿。

但是根據 Java Doc

The class Date represents a specific instant in time, with millisecond precision. 

更讓人混淆的是 constructor 可以帶日期時間

@Deprecated
public Date(int year,
            int month,
            int date,
            int hrs,
            int min,
            int sec)

Allocates a Date object and initializes it so that it represents the instant at the start of the second specified by the year, month, date, hrs, min, and sec arguments, in the local time zone.

他是根據裝置目前的 TimeZone, 來決定這個日期時間的 unix timestamp, 存在 Date 裡面。

Date 初始化/輸出 都會 implicitly 偷偷用目前的 TimeZone
#

java.util.Calendar 有儲存 TimeZone 資訊, 但是…
#

Calendar 有兩個 constructor

偷用目前的 TimeZone 跟 Locale

protected Calendar()

Constructs a Calendar with the default time zone and the default FORMAT locale.

指定 TimeZone 跟 Locale

protected Calendar(TimeZone zone,
                   Locale aLocale)

Constructs a calendar with the specified time zone and locale.

Calendar 還有一個方法是

public void setTimeZone(TimeZone value)

Sets the time zone with the given time zone value.

但是設定之後也沒有發生任何事,

val sdf = SimpleDateFormat("yyyy/MM/dd HH:mm:ss")
val calendar = Calendar.getInstance()
println(sdf.format(calendar.time))
println(calendar.time)

calendar.timeZone = TimeZone.getTimeZone("JST")

println(sdf.format(calendar.time))
println(calendar.time)

/*Output
2025/03/12 23:42:21
Wed Mar 12 23:42:21 CST 2025

2025/03/12 23:42:21
Wed Mar 12 23:42:21 CST 2025
*/

Calendar 雖然有存 TimeZone 資訊, 但是要怎麼改?
#

用 SimpleDateFormat 還是吃 Calendar 內部 Date?
#

Date/Calendar 的問題總結
#

  • Date 沒有 TimeZone 資訊
  • Calendar.time 的類型是 Date, Date.time 的類型是 Long
  • Calendar 有 TimeZone 資訊, 但是有時候是隱性參數
  • SimpleDateFormat 的 constructor 沒有辦法帶 TimeZone 參數, 而是用 TimeZone.default, 或是建立之後再 setTimeZone
  • SimpleDateFormat 是 format Date, Date 沒有時區, 所以預設用系統時區

最後看個 Computerphile 的影片