Java 通过calendsr或SimpleDataFormatter解析datetime的结果不同
我现在使用java 1.6,遇到了奇怪的行为,可能是bug,下面是代码:Java 通过calendsr或SimpleDataFormatter解析datetime的结果不同,java,date,parsing,datetime,calendar,Java,Date,Parsing,Datetime,Calendar,我现在使用java 1.6,遇到了奇怪的行为,可能是bug,下面是代码: import org.junit.Test; import javax.xml.bind.DatatypeConverter; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; public class TestDate { @Test public void testConvert() throws
import org.junit.Test;
import javax.xml.bind.DatatypeConverter;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
public class TestDate {
@Test
public void testConvert() throws Exception {
Calendar parsedCalendar = DatatypeConverter.parseDateTime("0001-01-01T00:00:00");
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
Date sdfDate = simpleDateFormat.parse("0001-01-01T00:00:00");
Calendar parsedCalendar2 = DatatypeConverter.parseDateTime("1980-03-01T00:00:00");
SimpleDateFormat simpleDateFormat2 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
Date sdfDate2 = simpleDateFormat2.parse("1980-03-01T00:00:00");
System.out.println("parsedCalendar: " + parsedCalendar.getTimeInMillis());
System.out.println("parsedCalendar TZ: " + parsedCalendar.getTimeZone());
System.out.println("parsedCalendar Date: " + parsedCalendar.getTime());
System.out.println("sdfDate: " + sdfDate);
System.out.println("sdfDate millis: " + sdfDate.getTime());
System.out.println("parsedCalendar2: " + parsedCalendar2.getTimeInMillis());
System.out.println("parsedCalendar2 TZ: " + parsedCalendar2.getTimeZone());
System.out.println("parsedCalendar2 Date: " + parsedCalendar2.getTime());
System.out.println("sdfDate2: " + sdfDate2);
System.out.println("sdfDate2 millis: " + sdfDate2.getTime());
}
}
问题是:
输出:
parsedCalendar: -62135622000000
parsedCalendar TZ: sun.util.calendar.ZoneInfo[id="Asia/Novosibirsk",offset=25200000,dstSavings=0,useDaylight=false,transitions=67,lastRule=null]
parsedCalendar Date: Mon Jan 03 00:00:00 NOVT 1
sdfDate: Sat Jan 01 00:00:00 NOVT 1
sdfDate millis: -62135794800000
parsedCalendar2: 320691600000
parsedCalendar2 TZ: sun.util.calendar.ZoneInfo[id="Asia/Novosibirsk",offset=25200000,dstSavings=0,useDaylight=false,transitions=67,lastRule=null]
parsedCalendar2 Date: Sat Mar 01 00:00:00 NOVT 1980
sdfDate2: Sat Mar 01 00:00:00 NOVT 1980
sdfDate2 millis: 320691600000
调试:
parsedCalendar.getTimeInMillis() = -62135622000000
sdfDate.getTime() = -62135794800000
parsedCalendar.getTime() = {Date@790} "Mon Jan 03 00:00:00 NOVT 1"
sdfDate = {Date@759} "Sat Jan 01 00:00:00 NOVT 1"
parsedCalendar2.getTimeInMillis() = 320691600000
sdfDate2.getTime() = 320691600000
parsedCalendar2.getTimeZone() = {ZoneInfo@755} "sun.util.calendar.ZoneInfo[id="Asia/Novosibirsk",offset=25200000,dstSavings=0,useDaylight=false,transitions=67,lastRule=null]"
parsedCalendar.getTimeZone() = {ZoneInfo@756} "sun.util.calendar.ZoneInfo[id="Asia/Novosibirsk",offset=25200000,dstSavings=0,useDaylight=false,transitions=67,lastRule=null]"
simpleDateFormat.getTimeZone() = {ZoneInfo@757} "sun.util.calendar.ZoneInfo[id="Asia/Novosibirsk",offset=25200000,dstSavings=0,useDaylight=false,transitions=67,lastRule=null]"
simpleDateFormat2.getTimeZone() = {ZoneInfo@758} "sun.util.calendar.ZoneInfo[id="Asia/Novosibirsk",offset=25200000,dstSavings=0,useDaylight=false,transitions=67,lastRule=null]"
正如您在解析0001 dateTime时所看到的,ms中存在差异!在1980年,情况并非如此。
谁能解释原因呢?这是由于儒略历和公历的不同
SimpleDataFormat
使用默认的日历系统,我相信在您和我的系统上都是GregoriaCalendar
GregorianCalendar
(尽管名称不同)基于gregorianChange
属性在公历系统和儒略历系统之间切换。它假设在该转换之后提供的任何日期都是格里高利日,而在该转换之前提供的任何日期都是朱利安日。默认切换为1582
DatatypeConverter
使用纯公历,因为这是
这意味着,如果你在日历切换之前解析一个值,你会看到一个很大的差异——随着时间的推移,这个差异会越来越小,每400年会有3天的差异。(不可被400整除的三个世纪,在朱利安历法中闰年也是如此,但在公历中却不是。)
如果先将SimpleDataFormat
中的日历设置为GregorianCalendar
,您称之为setGregorianChange(Long.MIN_值)
,则两者将一致
下面的代码可以让您更轻松地探索差异:
import javax.xml.bind.DatatypeConverter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.concurrent.TimeUnit;
public class Test {
public static void main(String[] args) throws ParseException {
convert("0001-01-01T00:00:00");
convert("1000-01-01T00:00:00");
convert("1580-01-01T00:00:00");
convert("1590-01-01T00:00:00");
convert("1980-03-01T00:00:00");
}
private static void convert(String input) throws ParseException {
Calendar datatypeConverterResult = DatatypeConverter.parseDateTime(input);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
Date sdfResult = simpleDateFormat.parse(input);
System.out.println("Input: " + input);
long datatypeConverterMillis = datatypeConverterResult.getTimeInMillis();
long sdfResultMillis = sdfResult.getTime();
long days = TimeUnit.MILLISECONDS.toDays(datatypeConverterMillis - sdfResultMillis);
System.out.println("DatatypeConverter epoch millis: " + datatypeConverterMillis);
System.out.println("SimpleDateTime epoch millis: " + sdfResultMillis);
System.out.println("Difference in days: " + days);
System.out.println("Parsed calendar time zone: " + datatypeConverterResult.getTimeZone().getID());
System.out.println();
}
}
注意,在Java9上,需要显式指定模块。这是使用java.se.ee完成的最简单的操作:
$ javac Test.java --add-modules java.se.ee
$ java --add-modules java.se.ee Test
“我的盒子”上的输出:
Input: 0001-01-01T00:00:00
DatatypeConverter epoch millis: -62135596800000
SimpleDateTime epoch millis: -62135769600000
Difference in days: 2
Parsed calendar time zone: Europe/London
Input: 1000-01-01T00:00:00
DatatypeConverter epoch millis: -30610224000000
SimpleDateTime epoch millis: -30609792000000
Difference in days: -5
Parsed calendar time zone: Europe/London
Input: 1580-01-01T00:00:00
DatatypeConverter epoch millis: -12307248000000
SimpleDateTime epoch millis: -12306384000000
Difference in days: -10
Parsed calendar time zone: Europe/London
Input: 1590-01-01T00:00:00
DatatypeConverter epoch millis: -11991628800000
SimpleDateTime epoch millis: -11991628800000
Difference in days: 0
Parsed calendar time zone: Europe/London
Input: 1980-03-01T00:00:00
DatatypeConverter epoch millis: 320716800000
SimpleDateTime epoch millis: 320716800000
Difference in days: 0
Parsed calendar time zone: Europe/London
如果字符串中没有偏移量,我强烈怀疑
DatatypeConverter
假定时区为UTC,而SimpleDateFormat
将默认为系统默认时区。我建议您查看parsedCalendar.getTimeZone()
。如果TZ差异中的问题将出现在这两种情况下。我添加了具有相同参数的第二种情况,正好说明了这一点。“如果TZ差异中的问题在这两种情况下都会出现。”否-如果系统默认时区在1980-03-01T00:00:00时的UTC偏移量为0,但在0001-01-01T00:00:00时的偏移量为非零,则您将完全看到您描述的行为。与其说我一定错了,不如像我建议的那样看看parsedCalendar.getTimeZone()
(如果你能告诉我们你观察到的系统默认时区,这样我们就可以重现这个问题,这也会有帮助。)我的意思是,它们没有在你的代码中打印出来。如果我们复制/粘贴/运行您的代码,它打印出来的只是“某物”。相反,你应该让你的例子打印出所有相关的信息,然后在你的问题中包含该程序的准确输出。哈,实际上,当我发布我的第一个问题/例子时,我期望的答案就像你的第一段或第一段和第二段一样。。。