关于Java:为什么AWST和CCT的时间不一样?

Why the time of AWST and CCT are not the same?

我编写了一个名为"dateutils"的静态类,这里有一个名为"parsedate(string)"的静态方法,它将使用一些模式将字符串转换为日期。默认时区为亚洲/上海(我在中国)

1
public static final TimeZone SHA = TimeZone.getTimeZone("Asia/Shanghai");

它将被传递到方法timezone.setdefault(时区);并使用simpledateformat协调字符串。匹配的模式应为"eee,dd-mmm-yyyy hh:mm:ss-zzz"这里有三个测试。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
//this one is ok.
@Test
public void testParseGMTDate() throws Exception {
    Date date = DateUtils.parseDate("Thu, 02 Aug 2016 08:12:34 GMT");
    assertNotNull(date);
    Calendar cal = Calendar.getInstance();
    cal.setTime(date);
    log.debug(DateUtils.format(cal.getTime()));
    assertEquals(cal.get(Calendar.YEAR), 2016);
    assertEquals(cal.get(Calendar.MONTH), 8 - 1);
    assertEquals(cal.get(Calendar.DATE), 2);
    assertEquals(cal.get(Calendar.HOUR_OF_DAY), 8 + 8);
    assertEquals(cal.get(Calendar.MINUTE), 12);
    assertEquals(cal.get(Calendar.SECOND), 34);
    assertEquals(DateUtils.format(cal.getTime()),"2016-08-02 16:12:34");
}
// AWST is GMS+8:00 time so this one is ok.
@Test
public void testParseWSTDate() throws Exception {
    Date date = DateUtils.parseDate("Thu, 02 Aug 2016 08:12:34 AWST");
    assertNotNull(date);
    Calendar cal = Calendar.getInstance();
    cal.setTime(date);
    log.debug(DateUtils.format(cal.getTime()));
    assertEquals(cal.get(Calendar.YEAR), 2016);
    assertEquals(cal.get(Calendar.MONTH), 8 - 1);
    assertEquals(cal.get(Calendar.DATE), 2);

    assertEquals(cal.get(Calendar.HOUR_OF_DAY), 8);
    assertEquals(cal.get(Calendar.MINUTE), 12);
    assertEquals(cal.get(Calendar.SECOND), 34);
}

// junit.framework.AssertionFailedError:
// Expected :9
// Actual   :8
@Test
public void testParseDateCCT() throws Exception {
    Date date = DateUtils.parseDate("Thu, 02 Aug 2016 08:12:34 CCT");
    assertNotNull(date);
    Calendar cal = Calendar.getInstance();
    cal.setTime(date);
    log.debug(DateUtils.format(cal.getTime()));
    assertEquals(cal.get(Calendar.YEAR), 2016);
    assertEquals(cal.get(Calendar.MONTH), 8 - 1);
    assertEquals(cal.get(Calendar.DATE), 2);

    assertEquals(cal.get(Calendar.HOUR_OF_DAY), 8); //Expected: 9
    assertEquals(cal.get(Calendar.MINUTE), 12);
    assertEquals(cal.get(Calendar.SECOND), 34);
}

下面是一些dateutils的代码片段。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public static final TimeZone SHA = TimeZone.getTimeZone("Asia/Shanghai");
static {
    Calendar calendar = Calendar.getInstance();
    calendar.setTimeZone(SHA);
    calendar.set(2000, 0, 1, 0, 0, 0);
    calendar.set(MILLISECOND, 0);
    TWO_DIGIT_START = calendar.getTime();

}
public static Date parseDate(String dateValue) {
    return parseDate(dateValue, null, null, SHA);
}
public static Date parseDate(String dateValue, String[] dateFormats, Date startDate, TimeZone timeZone) {
        TimeZone.setDefault(timeZone);
        String[] localDateFormats = dateFormats != null ? dateFormats : DEFAULT_PATTERNS;
        Date localStartDate = startDate != null ? startDate : TWO_DIGIT_START;
        String v = dateValue;
        if (dateValue.length() > 1 && dateValue.startsWith("\'") && dateValue.endsWith("\'")) {
            v = dateValue.substring(1, dateValue.length() - 1);
        }

        String[] arr = localDateFormats;
        int len = localDateFormats.length;

        for (String dateFormat : DEFAULT_PATTERNS) {
            //            String dateFormat = arr[i];
            SimpleDateFormat dateParser = DateUtils.DateFormatHolder.formatFor(dateFormat);
            dateParser.set2DigitYearStart(localStartDate);
            ParsePosition pos = new ParsePosition(0);
            Date result = dateParser.parse(v, pos);
            if (pos.getIndex() != 0) {
                _LOG.debug("Date parsed using: {}", dateFormat);
                return result;
            }
        }

        _LOG.error("Can't parse data: {data:{}, formats:{}, startDate:{},tz:{}}",
                dateValue, localDateFormats, localStartDate, TimeZone.getDefault());
        return null;
    }

我的问题是:根据互联网上的文档,cct&awst都是gmt+8:00时区,为什么在我的测试中,cct时间似乎是gmt+9:00。


DR

你犯了三个错误:

  • 使用非标准非唯一伪时区3-4字母缩写
  • 然后开始错误地假定它们的意思
  • 使用过时的Java类来处理日期时间。

相反,使用适当的时区名称,格式为continent/region。研究这些区域的记录意义,而不是假设/猜测。仅对日期时间工作使用java.time类。

所有这些都是许多程序员所犯的常见错误,从栈溢出的许多问题可以证明。

使用正确的时区名称

在北美,CST也是Central Standard Time。一个例子说明为什么你不应该使用这些3-4个字母的缩写。它们不是真正的时区,不是标准化的,甚至不是唯一的!!).使用正确的IANA时区名称。这些格式为continent/region

Java.时间

你用的是旧的麻烦的约会时间课程。避开它们。

JavaTimeFr框架是在Java 8和之后构建的。这些类取代了旧的麻烦的日期时间类,如java.util.Date.Calendar、&java.text.SimpleDateFormat。Joda Time团队还建议迁移到java.time。

要了解更多信息,请参阅Oracle教程。以及搜索堆栈溢出以获得许多示例和解释。

Java.TimeActudio的许多功能在3TeNeN后端移植到Java 6和7,并进一步适应了Android的三层版本。

Instant

Instant类是时间轴上的一个时刻,采用UTC(GMT),分辨率高达纳秒。

1
Instant instant = Instant.parse ("2016-08-02T08:12:34Z" ); // 02 Aug 2016 08:12:34

ZonedDateTime

ZonedDateTime表示调整为时区的Instant

我猜你的意思是"澳大利亚西部标准时间",由AWST,官方命名为Australia/Perth

1
2
ZoneId zoneId_Perth = ZoneId.of ("Australia/Perth" );
ZonedDateTime zdt_Perth = instant.atZone ( zoneId_Perth );

对于中国,你打算用Asia/Shanghai

1
2
ZoneId zoneId_Shanghai = ZoneId.of ("Asia/Shanghai" );
ZonedDateTime zdt_Shanghai = zdt_Perth.withZoneSameInstant ( zoneId_Shanghai );

我不知道你的或旧的约会时间课程是什么意思。本页显示的是"科科斯群岛时间",比UTC全年提前6个半小时(无夏令时,DST)。同样,这也是为什么你不应该使用3-4个字母缩写的一个例子。

1
2
ZoneId zoneId_Cocos = ZoneId.of ("Indian/Cocos" );
ZonedDateTime zdt_Cocos = zdt_Perth.withZoneSameInstant ( zoneId_Cocos );

根据一条评论,你可能是指北京时间。如果是这样,根据维基百科上的这个列表,知道北京时间在Asia/Shanghai时区内。

转储到控制台。你可以看到,珀斯和上海在今年8月都比联合技术公司早了8个小时,每天16小时,而8小时(16:12:3408:12:34)。科科斯群岛介于两者之间,比UTC提前6.5小时到达埃多克斯(19)。

1
System.out.println ("instant:" + instant +" | zdt_Perth:" + zdt_Perth +" | zdt_Shanghai:" + zdt_Shanghai +" | zdt_Cocos:" + zdt_Cocos );

instant: 2016-08-02T08:12:34Z | zdt_Perth: 2016-08-02T16:12:34+08:00[Australia/Perth] | zdt_Shanghai: 2016-08-02T16:12:34+08:00[Asia/Shanghai] | zdt_Cocos: 2016-08-02T14:42:34+06:30[Indian/Cocos]

DateTimeFormatter

要以此处显示的标准ISO 8601格式以外的格式生成字符串,请使用DateTimeFormatter类。

1
2
3
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.FULL );
f = f.withLocale( Locale.CHINA );
String output = zdt_Shanghai.format( f );