1 引言

SimpleDateFormat (java.text.SimpleDateFormat ) 可以将 Date (java.util.Date) 对象格式化成字符串(如 “2022-06-23 04:32:16”),也可以将字符串解析成 Date 对象(下文将这 2 种操作统称为转换时间)。Date 对象代表一个确定时刻,但不存储或代表时区,所以 SimpleDateFormat 转换时间时需要知道时区信息。即将 Date 对象格式化成字符串时,需要知道格式化成哪个时区的时间,而将字符串解析成 Date 对象时,需要知道字符串代表哪个时区的时间。

2 SimpleDateFormat 使用时区的方式

SimpleDateFormat 按以下方式使用时区来转换时间:

  • 格式化时间
    • SimpleDateFormat 对象拥有 timeZone 属性(对应 getTimeZone/setTimeZone 方法),该属性代表时区。用 setTimeZone 方法设置 timeZone 属性,则 format 方法会把 Date 对象格式化成 timeZone 属性代表的时区的时间。
    • timeZone 属性默认值为 java.util.TimeZone.getDefault() 【待核实】
  • 解析时间

    • 在时间模式中指定时区字符(z, Z, X),在时间字符串中指定时区,则 SimpleDateFormat.parse 方法会按照时间字符串中的时区来解释时间字符串中的时间。例如,对于时间模式 “yyyy-MM-dd HH:mm:ssZ”,和时间字符串 “2022-06-23 05:32:16+0100”,SimpleDateFormat.parse 方法会把时间解释成东1区 “2022-06-23 05:32:16”。
    • 若时间模式中不包含时区字符,则 SimpleDateFormat.parse 方法会按照 timeZone 属性代表的时区来解释时间字符串中的时间。

      3 示例

      源码:

      1. static void testSimpleDateFormatOnTimeZone() throws ParseException {
      2. long unixMilliSeconds = 1655958736000L;
      3. Date date = new Date(unixMilliSeconds);
      4. SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ssZ");
      5. System.out.println("【用默认时区格式化时间】");
      6. System.out.println("默认时区: " + dateFormat.getTimeZone().getID());
      7. System.out.println(date.getTime() + " -> " + dateFormat.format(date));
      8. System.out.println("\n【用指定时区格式化时间】");
      9. String[] timezones = {"GMT+00:00", "GMT+01:00", "GMT-01:00"};
      10. for (String timezone : timezones) {
      11. dateFormat.setTimeZone(TimeZone.getTimeZone(timezone));
      12. System.out.printf("%d(%s) -> %s%n", date.getTime(), timezone, dateFormat.format(date));
      13. }
      14. System.out.println("\n【解析带时区的时间】");
      15. String[] dateStrings = {"2022-06-23 04:32:16+0000", "2022-06-23 05:32:16+0100", "2022-06-23 03:32:16-0100"};
      16. for (String dateString : dateStrings) {
      17. date = dateFormat.parse(dateString);
      18. System.out.println(dateString + " -> " + date.getTime());
      19. }
      20. System.out.println("\n【用指定时区解析不带时区的时间】");
      21. dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
      22. dateStrings = new String[] {"2022-06-23 04:32:16", "2022-06-23 05:32:16", "2022-06-23 03:32:16"};
      23. for (int i = 0; i < dateStrings.length; i++) {
      24. String dateString = dateStrings[i];
      25. String timezone = timezones[i];
      26. dateFormat.setTimeZone(TimeZone.getTimeZone(timezone));
      27. date = dateFormat.parse(dateString);
      28. System.out.printf("%s(%s) -> %d%n", dateString, timezone, date.getTime());
      29. }
      30. }

      输出: ``` 【用默认时区格式化时间】 默认时区: Asia/Shanghai 1655958736000 -> 2022-06-23 12:32:16+0800

【用指定时区格式化时间】 1655958736000(GMT+00:00) -> 2022-06-23 04:32:16+0000 1655958736000(GMT+01:00) -> 2022-06-23 05:32:16+0100 1655958736000(GMT-01:00) -> 2022-06-23 03:32:16-0100

【解析带时区的时间】 2022-06-23 04:32:16+0000 -> 1655958736000 2022-06-23 05:32:16+0100 -> 1655958736000 2022-06-23 03:32:16-0100 -> 1655958736000

【用指定时区解析不带时区的时间】 2022-06-23 04:32:16(GMT+00:00) -> 1655958736000 2022-06-23 05:32:16(GMT+01:00) -> 1655958736000 2022-06-23 03:32:16(GMT-01:00) -> 1655958736000 ```