使用Joda-Time解析并生成Daylight Saving 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"));
我得到以下输出:
3
30/03/2008 03:00:00
4
30/03/2008 04:00:00
当我解析小时时,我得到小时是3.在我的数据结构中,我保存了存储午夜时间的那一天,然后我在一天中的每个小时(0-23)有一些值。然后,当我写出日期时,我会重新计算午夜加上小时的完整日期时间。当我总结3个小时到我的午夜时,我得到04:00:00!如果我再次解析它,我会得到4小时!
我的错误在哪里?有什么方法可以在我解析时获得第二小时或在打印时获得第三小时?
我也试图手工建立输出:
String.format("%s %02d:00:00", date.toString("dd/MM/yyyy"), h);
但在这种情况下,小时2,我生成30/03/2008 02:00:00,这不是一个有效的日期(因为小时2不存在),不能再解析了。
提前感谢您的帮助。 菲利普
答案 0 :(得分:5)
当我和我的午夜共3小时时,我得到04:00:00!如果我再次解析它,我会得到4小时!我的错误在哪里?
您已经提到过这个日期恰好是时间的变化。所以没有错。 2010年3月30日00:00 CEST(意大利的时区)正好是2010年3月29日23:00 UTC。当您添加3小时后,您将获得2010年3月30日02:00 UTC。但是这是我们切换时间(发生在01:00 UTC)的时刻,因此当您将时间转换为当地时区时,您将获得3月30日04:00。这是正确的行为。
当我打印时,是否有某种方法可以获得第2小时或者在打印时获得第3小时?
不,因为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。 No 02:在特定日期存在xx小时。
顺便说一句。在一个星期内,你可以期待另一个“有趣”。你能说出UTC中指的是什么日期:
2010年10月31日02:15 CEST?
嗯,有趣的是,我们不知道。它可能是2010年10月31日00:15 UTC(实际时间切换之前)或2010年10月31日01:15 UTC(切换之后)。
这就是为什么你应该总是存储与UTC相关的日期和时间,并在显示之前将它们转换为本地时区,否则你可能会有歧义。
HTH。
答案 1 :(得分:4)
在夏令时期间,保存数据的数据结构不是最佳选择。你这一天的特殊日子应该只有23个小时。
如果你这样做:
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));
你得到预期的结果,时间是05:00。
我建议您更改存储日期和使用日期的方式。如果没有,您必须在存储一天中的小时时处理夏令时。
您可能会这样做: 在我们将时间向前移动一小时的情况下,在这种情况下,您必须存储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());
}
}
......或者基本上是这样的。如果你选择沿着这条路走,我会为它写一个测试。您可以更改HOUR_TO_TEST以查看它是否通过了夏令时。
答案 2 :(得分:1)
基于PawełDyda&的正确答案Knubo ...
您绝不应将(serialize)日期时间存储为您提到的格式的字符串:"30/03/2008 03:00:00"
。问题:
如果必须将日期时间值序列化为文本,请使用可靠的格式。显而易见的选择是ISO 8601标准格式。更好的是将本地时间转换为UTC (Zulu)时区,然后转换为ISO 8601格式。像这样:2013-11-01T04:48:53.044Z
Joda-Time中的midnight
方法已弃用,支持Joda-Time方法withTimeAtStartOfDay()
(请参阅doc)。有些日子会not have a midnight。
关于此源代码的一些评论:
// © 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
示例显示在意大利罗马的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 );
首先转换为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 );
运行时:
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