使用 java.util.Calendar 添加天数会产生奇怪的结果

发布于 2024-12-08 12:10:59 字数 3076 浏览 0 评论 0原文

使用 java.util.Calendar 向日期添加一天,并使用 SimpleDateFormat 显示结果,有时似乎会丢失一天(通常在三月),有时会跳过一天(十一月)。

下面的程序及其输出说明了这个问题。请注意,我只是一次添加一天,然后跳过几个月并再添加几天。您将看到 2008-03-09 被打印两次,但 2008-11-02 被跳过。同样的事情在其他年份也会发生,但在不同的日子。我必须尝试找出导致问题的日子。

如果我没有在 SimpleDateFormat 中将时区设置为 UTC,则不会出现问题。我在美国中部时区的一台机器上运行了这个。

这看起来确实像是 Calendar 或 SimpleDateFormat 中的错误,但我无法在任何地方找到它的记录。有人对这里发生的事情有解释吗?

程序:

package mab;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;

public class CalendarHiccup2 {

    public static void main(String[] args) {
        addDays("2008-03-08");
        addDays("2009-03-07");
        addDays("2010-03-13");
    }

    public static void addDays(String dateString) {
        System.out.println("Got dateString: " + dateString);

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        sdf.setTimeZone(TimeZone.getTimeZone("UTC"));

        Calendar calendar = Calendar.getInstance();
        try {
            calendar.setTime(sdf.parse(dateString));
            Date day1 = calendar.getTime();
            System.out.println("  day1 = " + sdf.format(day1));

            calendar.add(java.util.Calendar.DAY_OF_MONTH, 1);
            Date day2 = calendar.getTime();
            System.out.println("  day2 = " + sdf.format(day2));

            calendar.add(java.util.Calendar.DAY_OF_MONTH, 1);
            Date day3 = calendar.getTime();
            System.out.println("  day3 = " + sdf.format(day3));

            calendar.add(java.util.Calendar.DAY_OF_MONTH, 1);
            Date day4 = calendar.getTime();
            System.out.println("  day4 = " + sdf.format(day4));

            // Skipping a few days ahead:
            calendar.add(java.util.Calendar.DAY_OF_MONTH, 235);
            Date day5 = calendar.getTime();
            System.out.println("  day5 = " + sdf.format(day5));

            calendar.add(java.util.Calendar.DAY_OF_MONTH, 1);
            Date day6 = calendar.getTime();
            System.out.println("  day6 = " + sdf.format(day6));

            calendar.add(java.util.Calendar.DAY_OF_MONTH, 1);
            Date day7 = calendar.getTime();
            System.out.println("  day7 = " + sdf.format(day7));

            calendar.add(java.util.Calendar.DAY_OF_MONTH, 1);
            Date day8 = calendar.getTime();
            System.out.println("  day8 = " + sdf.format(day8));

        } catch (Exception e) {
        }
    }

}

输出:

Got dateString: 2008-03-08
  day1 = 2008-03-08
  day2 = 2008-03-09
  day3 = 2008-03-09
  day4 = 2008-03-10
  day5 = 2008-10-31
  day6 = 2008-11-01
  day7 = 2008-11-03
  day8 = 2008-11-04
Got dateString: 2009-03-07
  day1 = 2009-03-07
  day2 = 2009-03-08
  day3 = 2009-03-08
  day4 = 2009-03-09
  day5 = 2009-10-30
  day6 = 2009-10-31
  day7 = 2009-11-02
  day8 = 2009-11-03
Got dateString: 2010-03-13
  day1 = 2010-03-13
  day2 = 2010-03-14
  day3 = 2010-03-14
  day4 = 2010-03-15
  day5 = 2010-11-05
  day6 = 2010-11-06
  day7 = 2010-11-08
  day8 = 2010-11-09

Using java.util.Calendar to add a single day to a Date, and SimpleDateFormat to display the result, sometimes seems to lose a day (generally in March) and sometimes skips a day (in November).

The program below, with output, illustrates the issue. Notice that I'm just adding one day at a time, then skipping a few months and adding a few more days. You'll see that 2008-03-09 gets printed twice, but 2008-11-02 is skipped. The same thing happens in other years, but on different days. I had to experiment to find the days that cause the problem.

If I don't set the timezone to UTC in the SimpleDateFormat then the problem does not occur. I ran this on a machine in the US Central Time Zone.

This certainly looks like a bug in Calendar or SimpleDateFormat, but I have not been able to find it documented anywhere. Anybody have an explanation of what is happening here?

The program:

package mab;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;

public class CalendarHiccup2 {

    public static void main(String[] args) {
        addDays("2008-03-08");
        addDays("2009-03-07");
        addDays("2010-03-13");
    }

    public static void addDays(String dateString) {
        System.out.println("Got dateString: " + dateString);

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        sdf.setTimeZone(TimeZone.getTimeZone("UTC"));

        Calendar calendar = Calendar.getInstance();
        try {
            calendar.setTime(sdf.parse(dateString));
            Date day1 = calendar.getTime();
            System.out.println("  day1 = " + sdf.format(day1));

            calendar.add(java.util.Calendar.DAY_OF_MONTH, 1);
            Date day2 = calendar.getTime();
            System.out.println("  day2 = " + sdf.format(day2));

            calendar.add(java.util.Calendar.DAY_OF_MONTH, 1);
            Date day3 = calendar.getTime();
            System.out.println("  day3 = " + sdf.format(day3));

            calendar.add(java.util.Calendar.DAY_OF_MONTH, 1);
            Date day4 = calendar.getTime();
            System.out.println("  day4 = " + sdf.format(day4));

            // Skipping a few days ahead:
            calendar.add(java.util.Calendar.DAY_OF_MONTH, 235);
            Date day5 = calendar.getTime();
            System.out.println("  day5 = " + sdf.format(day5));

            calendar.add(java.util.Calendar.DAY_OF_MONTH, 1);
            Date day6 = calendar.getTime();
            System.out.println("  day6 = " + sdf.format(day6));

            calendar.add(java.util.Calendar.DAY_OF_MONTH, 1);
            Date day7 = calendar.getTime();
            System.out.println("  day7 = " + sdf.format(day7));

            calendar.add(java.util.Calendar.DAY_OF_MONTH, 1);
            Date day8 = calendar.getTime();
            System.out.println("  day8 = " + sdf.format(day8));

        } catch (Exception e) {
        }
    }

}

The output:

Got dateString: 2008-03-08
  day1 = 2008-03-08
  day2 = 2008-03-09
  day3 = 2008-03-09
  day4 = 2008-03-10
  day5 = 2008-10-31
  day6 = 2008-11-01
  day7 = 2008-11-03
  day8 = 2008-11-04
Got dateString: 2009-03-07
  day1 = 2009-03-07
  day2 = 2009-03-08
  day3 = 2009-03-08
  day4 = 2009-03-09
  day5 = 2009-10-30
  day6 = 2009-10-31
  day7 = 2009-11-02
  day8 = 2009-11-03
Got dateString: 2010-03-13
  day1 = 2010-03-13
  day2 = 2010-03-14
  day3 = 2010-03-14
  day4 = 2010-03-15
  day5 = 2010-11-05
  day6 = 2010-11-06
  day7 = 2010-11-08
  day8 = 2010-11-09

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

荭秂 2024-12-15 12:10:59

这是由夏令时引起的,并且是完全正确的。

时间(北半球)通常在三月提前一小时,在十一月则向后推迟一小时。

This is caused by the daylight saving time and is completely correct.

The time (on north hemisphere) is advanced one hour typically in March and moved back in November.

孤独患者 2024-12-15 12:10:59

它看起来更像是夏令时问题,在 3 月和 11 月发生变化。您可以尝试将时间元素设置为 00:00:00 吗?如果这样做,

addDays("2008-03-08 00:00:00");
addDays("2009-03-07 00:00:00");
addDays("2010-03-13 00:00:00");

并将格式更改为,

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

您将看到它在时间元素中产生的差异。

It looks more like a day light saving time issue, which changes in Mar and Nov. Can you try setting the time element to 00:00:00? If you do,

addDays("2008-03-08 00:00:00");
addDays("2009-03-07 00:00:00");
addDays("2010-03-13 00:00:00");

and change the format to,

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

you'll see the difference it makes in the time elements.

悲喜皆因你 2024-12-15 12:10:59

当您遇到此类问题时,您必须打印日历和日期实例才能弄清楚发生了什么。

以下内容:

sdf.setTimeZone(TimeZone.getTimeZone("UTC"));

根据 JVM 本地时间(和时区)更改日历时间。 Calendar.getInstance(); 在您的 JVM 本地时间创建一个日历,当您执行 calendar.setTime(sdf.parse(".....")) 时设置 UTC 时间(考虑到您创建 sdf 的方式!)。根据可以让您度过午夜的 DoY(以及年份!),当您使用 yyyy-MM-dd 格式打印日期时,您会看到一天的差异。

打印完整的日历和完整的日期,您就会知道发生了什么!

When you have these kind of issues you have to print the Calendar and Date instances to figure out what's going on.

The following:

sdf.setTimeZone(TimeZone.getTimeZone("UTC"));

changes the time of your Calendar depending on your JVM local time (and timezone). Calendar.getInstance(); creates a Calendar in your JVM local time, when you do calendar.setTime(sdf.parse(".....")) that sets the time in UTC (given how you created the sdf!). Depending on the DoY (and the Year as well!) that can make you pass the midnight and when you print your Date with the yyyy-MM-dd format you see the one day difference.

Print the full Calendar and full Date and you'll figure out what's going on!

梦里寻她 2024-12-15 12:10:59

夏令时

Tomasz Nurkiewicz 的回答是正确的,问题是夏令时 (DST)。

当地时间在 3 月第二个星期天的 02:00 当地标准时间到 03:00 当地夏令时间更改,并在 11 月的第一个星期日 02:00 当地夏令时间到 01:00 当地标准时间恢复。

请参阅维基百科上的 中央时区

Joda-Time

Joda-Time 库使这种工作变得更加容易。

Joda-Time 中的 DateTime 知道其自己的时区。要使用 UTC/GMT(无时间偏移),请传递内置常量 DateTimeZone.UTC

要仅打印日期时间的日期部分,请使用 DateTimeFormatter

// © 2013 Basil Bourque. This source code may be used freely forever by anyone taking full responsibility for doing so.
// import org.joda.time.*;
// import org.joda.time.format.*;

String dateString = "2008-03-08";

// withTimeAtStartOfDay() is probably superfluous in this example, but I like that it self-documents our focus on the day rather than a particular time of day.
DateTime dateTime = new DateTime( dateString, DateTimeZone.UTC ).withTimeAtStartOfDay();
DateTime dateTimePlus1 = dateTime.plusDays( 1 ).withTimeAtStartOfDay();
DateTime dateTimePlus2 = dateTime.plusDays( 2 ).withTimeAtStartOfDay();
DateTime dateTimePlus3 = dateTime.plusDays( 3 ).withTimeAtStartOfDay();

转储到控制台...

System.out.println("dateTime: " + dateTime );
System.out.println("dateTime (date portion): " + ISODateTimeFormat.date().withZone( DateTimeZone.UTC ).print( dateTime ) );
System.out.println("dateTimePlus1: " + dateTimePlus1 );
System.out.println("dateTimePlus2: " + dateTimePlus2 );
System.out.println("dateTimePlus3: " + dateTimePlus3 );

运行时...

dateTime: 2008-03-08T00:00:00.000Z
dateTime (date portion): 2008-03-08
dateTimePlus1: 2008-03-09T00:00:00.000Z
dateTimePlus2: 2008-03-10T00:00:00.000Z
dateTimePlus3: 2008-03-11T00:00:00.000Z

Daylight Saving Time

The answer by Tomasz Nurkiewicz is correct, the problem is Daylight Saving Time (DST).

the local time changes at 02:00 local standard time to 03:00 local daylight time on the second Sunday in March and returns at 02:00 local daylight time to 01:00 local standard time on the first Sunday in November.

See Wikipedia on Central Time Zone

Joda-Time

The Joda-Time library makes this kind of work much easier.

A DateTime in Joda-Time knows its own time zone. To use UTC/GMT (no time offset), pass the built-in constant DateTimeZone.UTC.

To print out only the date portion of a date-time, use a DateTimeFormatter.

// © 2013 Basil Bourque. This source code may be used freely forever by anyone taking full responsibility for doing so.
// import org.joda.time.*;
// import org.joda.time.format.*;

String dateString = "2008-03-08";

// withTimeAtStartOfDay() is probably superfluous in this example, but I like that it self-documents our focus on the day rather than a particular time of day.
DateTime dateTime = new DateTime( dateString, DateTimeZone.UTC ).withTimeAtStartOfDay();
DateTime dateTimePlus1 = dateTime.plusDays( 1 ).withTimeAtStartOfDay();
DateTime dateTimePlus2 = dateTime.plusDays( 2 ).withTimeAtStartOfDay();
DateTime dateTimePlus3 = dateTime.plusDays( 3 ).withTimeAtStartOfDay();

Dump to console…

System.out.println("dateTime: " + dateTime );
System.out.println("dateTime (date portion): " + ISODateTimeFormat.date().withZone( DateTimeZone.UTC ).print( dateTime ) );
System.out.println("dateTimePlus1: " + dateTimePlus1 );
System.out.println("dateTimePlus2: " + dateTimePlus2 );
System.out.println("dateTimePlus3: " + dateTimePlus3 );

When run…

dateTime: 2008-03-08T00:00:00.000Z
dateTime (date portion): 2008-03-08
dateTimePlus1: 2008-03-09T00:00:00.000Z
dateTimePlus2: 2008-03-10T00:00:00.000Z
dateTimePlus3: 2008-03-11T00:00:00.000Z
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文