我写了一个名为"DateUtils"的静态类,这里有一个名"parseDate(String)"的静态方法,它将使用一些模式将字符串转换为日期。默认时区是亚洲/上海(我在中国)
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;dr
你犯了三个错误:
- 使用非标准非唯一伪时区3-4个字母缩写
- 然后错误地假设了它们的含义
- 使用过时的Java类进行日期时间处理
相反,请使用continent/region
格式的适当时区名称。研究这些区域的书面含义,而不是假设/猜测。日期时间工作只能使用java.time类。
所有这些都是许多程序员犯下的常见错误,正如Stack Overflow上的许多问题所证明的那样。
使用正确的时区名称
CCD_ 2在北美也是CCD_ 3。举个例子说明为什么你永远不应该使用这3-4个字母的缩写。它们不是真正的时区,不是标准化的,甚至不是唯一的(!)。使用正确的IANA时区名称。这些文件的格式为continent/region
。
java.time
您正在使用旧的麻烦日期时间类。避开他们。
java.time框架是在java 8及更高版本中构建的。这些类取代了诸如java.util.Date
、.Calendar
和&java.text.SimpleDateFormat
。JodaTime团队还建议迁移到java.Time.
要了解更多信息,请参阅Oracle教程。并在Stack Overflow中搜索许多示例和解释。
大部分java.time功能都是向后移植到Java6&ThreeTen背包中的7,并在ThreeTenABP中进一步适应Android。
Instant
Instant
类是UTC(GMT)时间线上的一个时刻,分辨率高达纳秒。
Instant instant = Instant.parse ( "2016-08-02T08:12:34Z" ); // 02 Aug 2016 08:12:34
ZonedDateTime
CCD_ 11表示被调整到一个时区中的CCD_。
我想你指的是澳大利亚西部标准时间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
是什么意思。该页面显示为"科科斯群岛时间",全年比UTC提前六个半小时(无夏令时)。同样,这是一个为什么你永远不应该使用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
)。科科斯群岛位于两者之间,CST
0比UTC提前6.5小时。
System.out.println ( "instant: " + instant + " | zdt_Perth: " + zdt_Perth + " | zdt_Shanghai: " + zdt_Shanghai + " | zdt_Cocos: " + zdt_Cocos );
即时:2016-08-02T08:12:34Z | zdt_Persh:2016-08-02T16:12:34+08:00[澳大利亚/珀斯]| zdt_Shanghai:2016-08-02 T16:12:32+08:00[亚洲/上海]| ztt_Cocos:2016-08-02T14:42:34+06:30[印度/可可]
DateTimeFormatter
要以此处显示的标准ISO 8601格式以外的格式生成字符串,请使用DateTimeFormatter
类。
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.FULL );
f = f.withLocale( Locale.CHINA );
String output = zdt_Shanghai.format( f );