为什么AWST和CCT的时间不一样?
Why the time of AWST and CCT are not the same?
我写了一个名为 class 的静态方法 "DateUtils",这里是一个名为 "parseDate(String)" 的静态方法,它将使用一些模式将字符串转换为日期。
默认时区是Asia/Shanghai(我在中国)
public static final TimeZone SHA = TimeZone.getTimeZone("Asia/Shanghai");
并将其传递给方法
TimeZone.setDefault(时区);
并使用 SimpleDateFormat 协调字符串。
匹配的模式应该是 "EEE, dd MMM yyyy HH:mm:ss zzz"
这是它的三个测试。
//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 的一些代码片段。
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
tl;博士
你犯了三个错误:
- 使用了非标准的非唯一伪时区 3-4 字母缩写
- 然后继续错误地假设它们的意思
- 使用过时的旧 Java classes 进行日期时间处理。
相反,使用 proper time zone names in the format of continent/region
. Study the documented meaning of those zones rather than assume/guess. Use only java.time classes 进行日期时间工作。
所有这些都是许多程序员常犯的错误,Stack Overflow 上的许多问题都证明了这一点。
使用正确的时区名称
CST
是 也是 Central Standard Time
in North America. One example of why you should never use these 3-4 letter abbreviations. They are not true time zones, not standardized, and not even unique(!). Use proper IANA time zone names。这些格式为 continent/region
.
java.time
您正在使用旧的麻烦的日期时间 classes。避开它们。
java.time framework is built into Java 8 and later. These classes supplant the old troublesome date-time classes such as java.util.Date
, .Calendar
, & java.text.SimpleDateFormat
. The Joda-Time 团队还建议迁移到 java.time。
要了解更多信息,请参阅 Oracle Tutorial。并在 Stack Overflow 中搜索许多示例和解释。
许多 java.time 功能被反向移植到 ThreeTen-Backport and further adapted to Android in ThreeTenABP 中的 Java 6 和 7。
Instant
Instant
class 是 UTC (GMT) with a resolution up to nanoseconds.
时间轴上的一个时刻
Instant instant = Instant.parse ( "2016-08-02T08:12:34Z" ); // 02 Aug 2016 08:12:34
ZonedDateTime
ZonedDateTime
代表一个Instant
调整到一个时区。
我假设你指的是 AWST
的澳大利亚西部标准时间,官方名称为 Australia/Perth
。
ZoneId zoneId_Perth = ZoneId.of ( "Australia/Perth" );
ZonedDateTime zdt_Perth = instant.atZone ( zoneId_Perth );
对于中国,您打算 Asia/Shanghai
。
ZoneId zoneId_Shanghai = ZoneId.of ( "Asia/Shanghai" );
ZonedDateTime zdt_Shanghai = zdt_Perth.withZoneSameInstant ( zoneId_Shanghai );
我不确定 CCT
是你的或旧日期时间 class 的意思。 This page 表示现在是“Cocos 群岛时间”,比全年 UTC 时间早六个半小时(无夏令时,DST)。同样,这是一个为什么你不应该使用 3-4 个字母缩写的例子。
ZoneId zoneId_Cocos = ZoneId.of ( "Indian/Cocos" );
ZonedDateTime zdt_Cocos = zdt_Perth.withZoneSameInstant ( zoneId_Cocos );
根据评论,您可能指的是北京时间。如果是这样,根据维基百科此列表,知道北京时间属于Asia/Shanghai
时区。
转储到控制台。您可以看到,今年 8 月,珀斯和上海都比 UTC 早 8 小时,一天中的一个小时为 16 比 8(16:12:34
比 08:12:34
)。 Cocos 群岛位于两者之间,比 UTC 时间早 14:42:34
6.5 小时。
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
class。
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.FULL );
f = f.withLocale( Locale.CHINA );
String output = zdt_Shanghai.format( f );
我写了一个名为 class 的静态方法 "DateUtils",这里是一个名为 "parseDate(String)" 的静态方法,它将使用一些模式将字符串转换为日期。 默认时区是Asia/Shanghai(我在中国)
public static final TimeZone SHA = TimeZone.getTimeZone("Asia/Shanghai");
并将其传递给方法 TimeZone.setDefault(时区); 并使用 SimpleDateFormat 协调字符串。 匹配的模式应该是 "EEE, dd MMM yyyy HH:mm:ss zzz" 这是它的三个测试。
//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 的一些代码片段。
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
tl;博士
你犯了三个错误:
- 使用了非标准的非唯一伪时区 3-4 字母缩写
- 然后继续错误地假设它们的意思
- 使用过时的旧 Java classes 进行日期时间处理。
相反,使用 proper time zone names in the format of continent/region
. Study the documented meaning of those zones rather than assume/guess. Use only java.time classes 进行日期时间工作。
所有这些都是许多程序员常犯的错误,Stack Overflow 上的许多问题都证明了这一点。
使用正确的时区名称
CST
是 也是 Central Standard Time
in North America. One example of why you should never use these 3-4 letter abbreviations. They are not true time zones, not standardized, and not even unique(!). Use proper IANA time zone names。这些格式为 continent/region
.
java.time
您正在使用旧的麻烦的日期时间 classes。避开它们。
java.time framework is built into Java 8 and later. These classes supplant the old troublesome date-time classes such as java.util.Date
, .Calendar
, & java.text.SimpleDateFormat
. The Joda-Time 团队还建议迁移到 java.time。
要了解更多信息,请参阅 Oracle Tutorial。并在 Stack Overflow 中搜索许多示例和解释。
许多 java.time 功能被反向移植到 ThreeTen-Backport and further adapted to Android in ThreeTenABP 中的 Java 6 和 7。
Instant
Instant
class 是 UTC (GMT) with a resolution up to nanoseconds.
Instant instant = Instant.parse ( "2016-08-02T08:12:34Z" ); // 02 Aug 2016 08:12:34
ZonedDateTime
ZonedDateTime
代表一个Instant
调整到一个时区。
我假设你指的是 AWST
的澳大利亚西部标准时间,官方名称为 Australia/Perth
。
ZoneId zoneId_Perth = ZoneId.of ( "Australia/Perth" );
ZonedDateTime zdt_Perth = instant.atZone ( zoneId_Perth );
对于中国,您打算 Asia/Shanghai
。
ZoneId zoneId_Shanghai = ZoneId.of ( "Asia/Shanghai" );
ZonedDateTime zdt_Shanghai = zdt_Perth.withZoneSameInstant ( zoneId_Shanghai );
我不确定 CCT
是你的或旧日期时间 class 的意思。 This page 表示现在是“Cocos 群岛时间”,比全年 UTC 时间早六个半小时(无夏令时,DST)。同样,这是一个为什么你不应该使用 3-4 个字母缩写的例子。
ZoneId zoneId_Cocos = ZoneId.of ( "Indian/Cocos" );
ZonedDateTime zdt_Cocos = zdt_Perth.withZoneSameInstant ( zoneId_Cocos );
根据评论,您可能指的是北京时间。如果是这样,根据维基百科此列表,知道北京时间属于Asia/Shanghai
时区。
转储到控制台。您可以看到,今年 8 月,珀斯和上海都比 UTC 早 8 小时,一天中的一个小时为 16 比 8(16:12:34
比 08:12:34
)。 Cocos 群岛位于两者之间,比 UTC 时间早 14:42:34
6.5 小时。
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
class。
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.FULL );
f = f.withLocale( Locale.CHINA );
String output = zdt_Shanghai.format( f );