java Joda-Time、夏令时更改和日期时间解析

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4002973/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 04:19:24  来源:igfitidea点击:

Joda-Time, Daylight Saving Time change and date time parsing

javaparsingjodatimedst

提问by Filippo Tabusso

I have the following problem using Joda-Timefor parsing and producing date and time around Daylight Saving Time(DST) hours. Here is an example (please, note that March 30th 2008 is Daylight Saving change in Italy):

我在使用Joda-Time解析和生成夏令时(DST) 小时左右的日期和时间时遇到以下问题。这是一个示例(请注意,2008 年 3 月 30 日是意大利的夏令时更改):

DateTimeFormatter dtf = DateTimeFormat.forPattern("dd/MM/yyyy HH:mm:ss");
DateTime x = dtf.parseDateTime("30/03/2008 03:00:00");
int h = x.getHourOfDay();
System.out.println(h);
System.out.println(x.toString("dd/MM/yyyy HH:mm:ss"));
DateTime y = x.toDateMidnight().toDateTime().plusHours(h);
System.out.println(y.getHourOfDay());
System.out.println(y.toString("dd/MM/yyyy HH:mm:ss"));

I get the following output:

我得到以下输出:

3
30/03/2008 03:00:00
4
30/03/2008 04:00:00

When i parse hour I get hour is 3. In my data structure I save the day storing midnight time, and then I have some value for each hour of the day (0-23). Then, when I write out the date, I re-compute the full date time making midnight plus hour. When I sum 3 hours to my midnight I get 04:00:00! And if I parse it again, I get hour 4!

当我解析小时时,我得到小时是 3。在我的数据结构中,我保存了存储午夜时间的一天,然后我为一天中的每个小时(0-23)设置了一些值。然后,当我写出日期时,我重新计算完整的日期时间,即午夜加小时。当我总结到午夜 3 小时时,我得到 04:00:00!如果我再次解析它,我会得到 4 小时!

Where is my mistake? Is there some way to get hour 2 when I parse or get hour three when I print out?

我的错误在哪里?有什么方法可以在解析时获得第 2 小时,或者在打印时获得第 3 小时?

I have also tried to build output by hand:

我还尝试手动构建输出:

String.format("%s %02d:00:00", date.toString("dd/MM/yyyy"), h);

but in this case for hour 2, I produce 30/03/2008 02:00:00 which is not a valid date (since hour 2 does not exist) and cannot be parsed any more.

但在这种情况下,对于第 2 小时,我生成 30/03/2008 02:00:00 这不是有效日期(因为第 2 小时不存在)并且无法再解析。

Thank you in advance for your help. Filippo

预先感谢您的帮助。菲利波

采纳答案by Knubo

The data structure you are saving your data is not very optimal for the days with daylight saving time. Your day in this particular day should only have 23 hours.

您保存数据的数据结构对于夏令时的日子并不是非常理想。你在这一天的一天应该只有 23 小时。

If you do:

如果你这样做:

    DateTimeFormatter dtf = DateTimeFormat.forPattern("dd/MM/yyyy HH:mm:ss").withLocale(Locale.US);
    DateTime x = dtf.parseDateTime("30/03/2008 00:00:00");

    DateTimeFormatter parser = DateTimeFormat.fullDateTime();
    System.out.println("Start:"+parser.print(x));

    DateTime y = x.plusHours(4);

    System.out.println("After add of 4:"+parser.print(y));

You get the expected result, that the time is 05:00.

您得到预期的结果,即时间为 05:00。

I recommend that you change the way you store your day and use a date. If not, you must handle daylight saving time when storing the hour of day.

我建议您更改存储日期的方式并使用日期。如果不是,则在存储一天中的小时时必须处理夏令时。

You might do something like this: In the case where we move the time forward one hour, as this case, you must store 4 and not 5 as the time for 5. And when you calculate the time, you should use the plusHours() method to get the actual time. I think you might get away with something like:

您可能会这样做:在我们将时间向前移动一小时的情况下,在这种情况下,您必须存储 4 而不是 5 作为 5 的时间。当您计算时间时,您应该使用 plusHours()获取实际时间的方法。我想你可能会逃脱类似的事情:

public class DateTest {
    private static final int HOUR_TO_TEST = 2;

  public static void main(String[] args) {
    DateTimeFormatter dtf = DateTimeFormat.forPattern("dd/MM/yyyy HH:mm:ss");
    DateTime startOfDay = dtf.parseDateTime("30/03/2008 00:00:00");

    /* Obtained from new DateTime() in code in practice */
    DateTime actualTimeWhenStoring = startOfDay.plusHours(HOUR_TO_TEST);

    int hourOfDay = actualTimeWhenStoring.getHourOfDay();
    int hourOffset = startOfDay.plusHours(hourOfDay).getHourOfDay();

    System.out.println("Hour of day:" + hourOfDay);
    System.out.println("Offset hour:" + hourOffset);

    int timeToSave = hourOfDay;
    if (hourOffset != hourOfDay) {
        timeToSave = (hourOfDay + (hourOfDay - hourOffset));
    }
    System.out.println("Time to save:" + timeToSave);

    /* When obtaining from db: */
    DateTime recalculatedTime = startOfDay.plusHours(timeToSave);

    System.out.println("Hour of time 'read' from db:" + recalculatedTime.getHourOfDay());
  }
}

...or basicly something like that. I'd write a test for it if you choose for going down this route. You can change the HOUR_TO_TEST to see that it moves passed the daylight saving time.

......或者基本上是这样的。如果你选择走这条路,我会为它写一个测试。您可以更改 HOUR_TO_TEST 以查看它是否已通过夏令时。

回答by Pawe? Dyda

When I sum 3 hours to my midnight I get 04:00:00! And if I parse it again, I get hour 4! Where is my mistake?

当我总结到午夜 3 小时时,我得到 04:00:00!如果我再次解析它,我会得到 4 小时!我的错误在哪里?

You mentioned already that this date is exactly when the time changes. So there is no mistake. March 30, 2010 00:00 CEST (the timezone in Italy) is precisely speaking March 29, 2010 23:00 UTC. When you add 3 hours, you will get March 30, 2010 02:00 UTC. But this is post the moment, that we switch times (which happens on 01:00 UTC), so when you convert time to local timezone you get March 30, 04:00. That's correct behavior.

你已经提到这个日期正是时间改变的时候。所以没有错误。2010 年 3 月 30 日 00:00 CEST(意大利的时区)准确地说是 2010 年 3 月 29 日 23:00 UTC。添加 3 小时后,您将获得 2010 年 3 月 30 日 02:00 UTC。但这是我们切换时间的那一刻(发生在 UTC 时间 01:00),因此当您将时间转换为本地时区时,您会得到 3 月 30 日 04:00。这是正确的行为。

Is there some way to get hour 2 when I parse or get hour three when I print out?

有什么方法可以在解析时获得第 2 小时,或者在打印时获得第 3 小时?

No, because March 30, 2010 02:00 CEST does not exist. Precisely at March 30, 2010 01:00 UTC we switch time from +1 hour to +2 hours versus UTC, so March 30, 2010 00:59 UTC is March 30, 2010: 01:59 CEST, but March 30, 2010 01:00 UTC become March 30, 2010 03:00 CEST. No 02:xx hour exist on that particular date.

不,因为 2010 年 3 月 30 日 02:00 CEST不存在。恰好在 2010 年 3 月 30 日 01:00 UTC,我们将时间从 +1 小时切换到 +2 小时与 UTC,因此 2010 年 3 月 30 日 00:59 UTC 是 2010 年 3 月 30 日:01:59 CEST,但 2010 年 3 月 30 日 01 :00 UTC 变为 2010 年 3 月 30 日 03:00 CEST。该特定日期不存在 02:xx 小时。

BTW. In a week you can expect another "fun". Can you tell what date in UTC this refers to:

顺便提一句。一周后,您可以期待另一个“乐趣”。你能说出这是指什么 UTC 日期:

October 31, 2010 02:15 CEST ?

2010 年 10 月 31 日 02:15 CEST ?

Well, the funny part is, we do not know. It could be either 0ctober 31, 2010 00:15 UTC (before actual time switch) or October 31, 2010 01:15 UTC (after the switch).

好吧,有趣的是,我们不知道。它可以是 0ctober 31, 2010 00:15 UTC(实际时间切换前)或 2010 年 10 月 31 日 01:15 UTC(切换后)。

That's exactly why you should always store date and times in relation to UTC and convert them to local time zone before displaying, otherwise you risk an ambiguity.

这正是为什么您应该始终存储与 UTC 相关的日期和时间,并在显示之前将它们转换为本地时区的原因,否则您将面临歧义的风险。

HTH.

哈。

回答by Basil Bourque

Building on the correct answers by Pawe? Dyda & Knubo…

以 Pawe 的正确答案为基础?达达 & Knubo…

ISO 8601 For String Format

字符串格式的 ISO 8601

You should never store (serialize) a date-time as a string in the format you mentioned: "30/03/2008 03:00:00". Problems:

你不应该存储(连载)日期时间为你所提到的格式的字符串:"30/03/2008 03:00:00"。问题:

  • Omitted time zone.
  • Day, Month, Year order is ambiguous.
  • Should have been translated to UTC time.
  • 省略时区。
  • 日、月、年顺序不明确。
  • 应该已转换为 UTC 时间。

If you must serialize a date-time value to text, use a reliable format. The obvious choice is the ISO 8601standard format. Even better is converting the local time to UTC (Zulu)time zone and then out to ISO 8601 format. Like this: 2013-11-01T04:48:53.044Z

如果您必须将日期时间值序列化为文本,请使用可靠的格式。显而易见的选择是ISO 8601标准格式。更好的是将本地时间转换为UTC(祖鲁语)时区,然后转换为 ISO 8601 格式。像这样:2013-11-01T04:48:53.044Z

No Midnight

没有午夜

The midnightmethods in Joda-Time are deprecated in favor of the Joda-Time method withTimeAtStartOfDay()(see doc). Some days do not have a midnight.

midnightJoda-Time 中的方法已被弃用,取而代之的是 Joda-Time 方法withTimeAtStartOfDay()(参见doc)。有些日子没有午夜

Example Code in Joda-Time 2.3

Joda-Time 2.3 中的示例代码

Some comments about this source code:

关于这个源代码的一些评论:

    // ? 2013 Basil Bourque. This source code may be used freely forevery by anyone taking full responsibility for doing so.

    // Joda-Time - The popular alternative to Sun/Oracle's notoriously bad date, time, and calendar classes bundled with Java 7 and earlier.
    // http://www.joda.org/joda-time/

    // Joda-Time will become outmoded by the JSR 310 Date and Time API introduced in Java 8.
    // JSR 310 was inspired by Joda-Time but is not directly based on it.
    // http://jcp.org/en/jsr/detail?id=310

    // By default, Joda-Time produces strings in the standard ISO 8601 format.
    // https://en.wikipedia.org/wiki/ISO_8601

Example showing 23 hours in the day of DST (Daylight Saving Time) in Rome Italy, while the day after has 24 hours. Note that the time zone (for Rome) is specified.

示例显示意大利罗马 DST(夏令时)当天的 23 小时,而后一天有 24 小时。请注意,指定了时区(罗马)。

    // Time Zone list: http://joda-time.sourceforge.net/timezones.html
    org.joda.time.DateTimeZone romeTimeZone = org.joda.time.DateTimeZone.forID("Europe/Rome");
    org.joda.time.DateTime dayOfDstChange = new org.joda.time.DateTime( 2008, 3, 30, 0, 0, romeTimeZone ) ; // Day when DST
    org.joda.time.DateTime dayAfter = dayOfDstChange.plusDays(1);

    // How many hours in this day? Should be 23 rather than 24 on day of Daylight Saving Time "springing ahead" to lose one hour.
    org.joda.time.Hours hoursObjectForDay = org.joda.time.Hours.hoursBetween(dayOfDstChange.withTimeAtStartOfDay(), dayAfter.withTimeAtStartOfDay());
    System.out.println( "Expect 23 hours, got: " + hoursObjectForDay.getHours() ); // Extract an int from object.

    // What time is 3 hours after midnight on day of DST change?
    org.joda.time.DateTime threeHoursAfterMidnightOnDayOfDst = dayOfDstChange.withTimeAtStartOfDay().plusHours(3);
    System.out.println( "Expect 4 AM (04:00) for threeHoursAfterMidnightOnDayOfDst: " + threeHoursAfterMidnightOnDayOfDst );

    // What time is 3 hours after midnight on day _after_ DST change?
    org.joda.time.DateTime threeHoursAfterMidnightOnDayAfterDst = dayAfter.withTimeAtStartOfDay().plusHours(3);
    System.out.println( "Expect 3 AM (03:00) for threeHoursAfterMidnightOnDayAfterDst: " + threeHoursAfterMidnightOnDayAfterDst );

Example of storing a date-time by first translating to UTC. Then upon restoring the date-time object, adjust to the desired time zone.

通过首先转换为 UTC 来存储日期时间的示例。然后在恢复日期时间对象时,调整到所需的时区。

    // Serialize DateTime object to text.
    org.joda.time.DateTimeZone romeTimeZone = org.joda.time.DateTimeZone.forID("Europe/Rome");
    org.joda.time.DateTime dayOfDstChangeAtThreeHoursAfterMidnight = new org.joda.time.DateTime( 2008, 3, 30, 0, 0, romeTimeZone ).withTimeAtStartOfDay().plusHours(3);
    System.out.println("dayOfDstChangeAtThreeHoursAfterMidnight: " + dayOfDstChangeAtThreeHoursAfterMidnight);
    // Usually best to first change to UTC (Zulu) time when serializing.
    String dateTimeSerialized = dayOfDstChangeAtThreeHoursAfterMidnight.toDateTime( org.joda.time.DateTimeZone.UTC ).toString();
    System.out.println( "dateTimeBeingSerialized: " + dateTimeSerialized );
    // Restore
    org.joda.time.DateTime restoredDateTime = org.joda.time.DateTime.parse( dateTimeSerialized );
    System.out.println( "restoredDateTime: " + restoredDateTime );
    // Adjust to Rome Italy time zone.
    org.joda.time.DateTime restoredDateTimeAdjustedToRomeItaly = restoredDateTime.toDateTime(romeTimeZone);
    System.out.println( "restoredDateTimeAdjustedToRomeItaly: " + restoredDateTimeAdjustedToRomeItaly );

When run:

运行时:

dayOfDstChangeAtThreeHoursAfterMidnight: 2008-03-30T04:00:00.000+02:00
dateTimeBeingSerialized: 2008-03-30T02:00:00.000Z
restoredDateTime: 2008-03-30T02:00:00.000Z
restoredDateTimeAdjustedToRomeItaly: 2008-03-30T04:00:00.000+02:00