Search code examples
javaparsingjodatimedst

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


I have the following problem using Joda-Time for 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):

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


Solution

  • 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.