我写了一个方法将日期字符串转换为“JDate”int(自1900年1月1日以来的天数,其中1900-01-01计为第1天)。但是,我注意到它并不总是返回预期值;有时它会返回一个低于预期值的值。我做了一些实验,发现从3月的第二个星期一到每年11月的第一个星期日,预期价值会减少一个。我想弄清楚,为什么会这样?
这是我的代码:
public static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
public static final SimpleDateFormat DATE_FORMAT_WITH_WEEK = new SimpleDateFormat("yyyy-MM-dd EEE");
/**
* Conversion method
*/
public static int getJDateFromyyyyMMdd(String yyyyMMdd) throws ParseException {
long millisForDate = DATE_FORMAT.parse(yyyyMMdd).getTime();
long millisFor1900 = DATE_FORMAT.parse("1900-01-01").getTime();
long millisSince1900 = millisForDate - millisFor1900;
return (int)TimeUnit.DAYS.convert(millisSince1900, TimeUnit.MILLISECONDS) + 1;
}
/**
* Method to test conversion method
*/
public static void testJDateConversion(Calendar calendar, int expectedValue) throws ParseException {
String yyyyMMdd = DATE_FORMAT.format(calendar.getTime());
String yyyyMMddEEE = DATE_FORMAT_WITH_WEEK.format(calendar.getTime());
int actualValue = getJDateFromyyyyMMdd(yyyyMMdd);
if(actualValue != expectedValue) {
System.out.printf("%s\t\tExpected %d, Actual %d\n", yyyyMMddEEE, expectedValue, actualValue);
}
}
/**
* Execute
*/
public static void main(String[] args) throws ParseException {
Calendar calendar = Calendar.getInstance();
calendar.setTime(DATE_FORMAT.parse("1900-01-01"));
Calendar futureDate = Calendar.getInstance();
futureDate.setTime(DATE_FORMAT.parse("2020-12-31"));
for(int expectedJDate = 1; calendar.before(futureDate); expectedJDate++) {
testJDateConversion(calendar, expectedJDate);
calendar.add(Calendar.DATE, 1);
}
}
以下是打印出来的一些内容:
2013-11-02 Sat Expected 41579, Actual 41578
2013-11-03 Sun Expected 41580, Actual 41579
2014-03-10 Mon Expected 41707, Actual 41706
2014-03-11 Tue Expected 41708, Actual 41707
...
2014-11-01 Sat Expected 41943, Actual 41942
2014-11-02 Sun Expected 41944, Actual 41943
2015-03-09 Mon Expected 42071, Actual 42070
2015-03-10 Tue Expected 42072, Actual 42071
...
2015-10-31 Sat Expected 42307, Actual 42306
2015-11-01 Sun Expected 42308, Actual 42307
2016-03-14 Mon Expected 42442, Actual 42441
2016-03-15 Tue Expected 42443, Actual 42442
关于导致这种异常行为的因素的任何想法?
答案 0 :(得分:2)
我很确定您的问题是由DST(夏令时)引起的。夏季时间在3月至10月/ 11月之间有效,因地区而异。
例如在我的例子中这段代码:
System.out.println(DATE_FORMAT.parse("2015-03-01"));
DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
System.out.println(DATE_FORMAT.parse("2015-03-01"));
生成此输出:
Sun Mar 01 00:00:00 CET 2015
Sun Mar 01 01:00:00 CET 2015
您可以尝试将TimeZone设置为UTC。
(几年前我曾在多个时区处理过这样的DST问题,但我的记忆并不太新鲜,我不得不再挖一些东西。)
更新:
我为您的代码尝试了一个快速测试,为所有内容设置了一个常见的TimeZone,它现在应该给出您期望的结果:
public static void main(String[] args) throws ParseException {
TimeZone utc = TimeZone.getTimeZone("UTC");
DATE_FORMAT.setTimeZone(utc);
DATE_FORMAT_WITH_WEEK.setTimeZone(utc);
Calendar calendar = Calendar.getInstance(utc);
calendar.setTime(DATE_FORMAT.parse("1900-01-01"));
Calendar futureDate = Calendar.getInstance(utc);
futureDate.setTime(DATE_FORMAT.parse("2020-12-31"));
...
}
答案 1 :(得分:1)
accepted answer是正确的。
顺便说一下,如果1900-01-01
是第1天,那么epoch实际上就是前一天1899-12-31
。
如果您可以使用Java 8或更高版本,则此工作很多更容易。使用新的java.time package。
与java.util.Date/.Calendar不同,java.time包提供LocalDate
类来表示仅限日期,没有任何时间或时区。所以没有夏令时或其他异常情况需要解决。
LocalDate epoch = LocalDate.of( 1900 , Month.JANUARY , 1 ).minusDays( 1 );
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "yyyyMMdd" );
long days = ChronoUnit.DAYS.between( epoch , LocalDate.parse( "20131102" , formatter ) );
String output = epoch.plusDays( days ).format( formatter );
转储到控制台。
System.out.println( "From epoch of " + epoch + " adding " + days + " days = " + output );
跑步时。
From epoch of 1899-12-31 adding 41579 days = 20131102
如果您无法迁移到Java 8,请使用Joda-Time库进行此项工作。它也提供LocalDate
类。
LocalDate epoch = new LocalDate( 1900 , DateTimeConstants.JANUARY , 1 ).minusDays( 1 );
DateTimeFormatter formatter = ISODateTimeFormat.basicDate();
int days = Days.daysBetween( epoch , formatter.parseLocalDate( "20131102" ) ).getDays();
String output = epoch.plusDays( days ).toString( formatter );
转储到控制台。
System.out.println( "From epoch of " + epoch + " adding " + days + " days = " + output );
跑步时。
From epoch of 1899-12-31 adding 41579 days = 20131102