views:

31

answers:

2

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

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!

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

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.

Thank you in advance for your help. Filippo

+2  A: 

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.

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.

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:

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.

Knubo
Thank for your reply. Regretfully when I store the data I don't have the hour (your HOUR_TO_TEST attribute), I get it from the date time string I parse. So the only solution I have (I think) is to to check for dst change at every hour and make the correction whenever needed. I'm now modifying my code in this sense and it seems to work.
Filippo Tabusso
If you have a string I guess you can use the hourOfDay as that one, right? The people providing the string are providing it in some time zone right? If that time zone is also affected by daylight saving time then it should not be a problem, given that the producer and you as the consumer is in the same time zone (then the illegal date 02:00 will never be sent to you). If you get the date in some other time zone, for instance UTC, then the parseDateTime method has functionality for parsing other time zones than your current locale - look up the documentation.
Knubo
A: 

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?

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.

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

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.

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

October 31, 2010 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).

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.

HTH.

Paweł Dyda