每年从3月到11月计算JDate一次

时间:2015-05-20 16:09:30

标签: java date

我写了一个方法将日期字符串转换为“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

关于导致这种异常行为的因素的任何想法?

2 个答案:

答案 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.time

如果您可以使用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