Java 每年3月至11月一次

Java 每年3月至11月一次,java,date,Java,Date,我编写了一个方法,将日期字符串转换为“JDate”int(1900年1月1日以来的天数,其中1900-01-01算作第1天)。然而,我注意到它并不总是返回预期值;有时它返回的值比预期值少一个。我做了一些实验,发现从每年3月的第二个星期一到11月的第一个星期日,预期值都会少一个。我在想,为什么会这样 这是我的密码: public static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd"); publi

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

你知道是什么导致了这种不寻常的行为吗?

我很确定你的问题是由夏令时(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
您可以尝试将时区设置为UTC

(几年前,我曾在多个时区处理过此类DST问题,但我的记忆不太清晰,我不得不再次挖掘一些东西。)

更新:

我尝试了对您的代码进行快速测试,为所有内容设置了一个公共时区,现在应该可以得到您期望的结果:

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"));
    ...
}
答案是正确的

顺便说一句,如果
1900-01-01
是第1天,那么您实际上是前一天,
1899-12-31

java.time 如果您可以使用Java8或更高版本,这项工作就容易多了。使用新的

与java.util.Date/.Calendar不同,java.time包提供的类仅表示日期,而不表示一天中的任何时间或时区。因此,没有夏时制或其他异常需要解决

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 );
System.out.println( "From epoch of " + epoch + " adding " + days + " days = " + output );
跑步的时候

从1899-12-31的纪元加上41579天=20131102
乔达时间 如果无法转到Java8,请使用该库进行此工作。它也提供了一个课程

转储到控制台

System.out.println( "From epoch of " + epoch + " adding " + days + " days = " + output );
System.out.println( "From epoch of " + epoch + " adding " + days + " days = " + output );
跑步的时候

从1899-12-31的纪元加上41579天=20131102

谢谢!就这样。应该意识到DST会引起问题,特别是因为我在计算毫秒数。这正是我所需要的。顺便问一下,
JDate
是什么?在什么样的上下文/库/平台中使用?一个“JDate”是一个将日期表示为自开始日期起的天数的概念(通常结束于1900年1月1日,但在某些约定中,开始于“0”年(这很尴尬,因为没有“0”年,但我离题了…)。至于上下文,我不能提供太多细节,因为这是一家公司的客户工作。太棒了!如果允许我使用Java8或Joda Time,我完全会这样做。不幸的是,对客户端工作的限制并不总是有意义的。