8
votes

I have a general question about dealing with daylight saving time. I guess its not really PHP specific, but I am writing in PHP, so I figure it wouldn't hurt to include it.

I have a calendar app built using jquery fullcalendar. The user views events in their local timezone and my server stores them as UTC datetimes in mysql. (Other questions on stackoverflow suggest that this is the best way to deal with timezones.) So there is a conversion every time the user saves or views events on the calendar. This is working fine, but I am confused about how best to deal with daylight saving time.

For example, say a user in timezone EST (eastern standard time) creates an event when it is NOT daylight saving for 3pm that repeats everyday. My PHP code uses the standard DateTime (and DateTimeZone) class to convert between UTC and EST by UTC-5:00. When daylight saving is active, the clock turns ahead one hour and PHP will instead convert between UTC and EST by UTC-4:00. From the user's point of view, the event is then shifted from the original 3pm to 4pm. This is not what my users and I want. A 3pm event should stay a 3pm event regardless of daylight savings. What is the best way to deal with this? Is there a way in PHP to ignore daylight saving time?

My code:

$getDate = new DateTime($storedDate, new DateTimeZone('UTC'));
$getDate->setTimezone(new DateTimeZone('America/New_York'));
$getDateString = $getDate->format('Y-m-d H:i:s');

MORE INFO: (copied from my comments below)

-Only the first occurrence of a repeating event is stored. All other occurrences are created on the fly according to what calendar view the user requests (month,week,or day views). With the way I have it coded, it creates only the occurrences that will be visible on the view.

-The thing is, I also need to keep it constant for other timezones. To continue the original example, 3pm should stay 3pm in EST (regardless of daylight savings), but if the event is viewed in Central time, the event should also stay 2pm (regardless of daylight savings).

Essentially, I need to somehow ignore daylight saving time.

2
This is a great question. Have you looked at this: stackoverflow.com/questions/2532729/…larsAnders
I've seen the first link you gave, but the second one is new. Thanks, I'll see if I can find anything. EDIT: I didn't find anything useful. But I did notice that the first link suggests including the original time offset for each event. However, this seems to be a strange and inefficient solution.train
When you create repeating events are they all stored as individual timestamps, or do you have some kind of offset from the 1st event?vascowhite
Only the first occurrence of a repeating event is stored. All other occurrences are created on the fly according to what calendar view the user requests (month,week,or day views). With the way I have it coded, it creates only the occurrences that will be visible on the view.train

2 Answers

2
votes

Have you tried looking at DateTimeZone::getTransitions() ?

http://www.php.net/manual/en/datetimezone.gettransitions.php

In particular use the [offset] and [isdst] properties.

  • When they save the time, find the first transition before the current date that is NOT DST. (Typically one of the two values in the past year). Convert using the offset of the non-DST period
  • When retrieving the value and you are currently in a DST period use the offset of a non-DST period to translate the time, not the current offset.

Taking your EST example, in August even though you are in EDT, you save values using the EST conversion of -5.

When pulling the value back out if they view that value in January you add 5, and if you are in August you add 4.

This will work for 95% of cases I'm assuming that the switches are consistent. If Eastern decided to merge with Central, you could have transitions that run –5/–4/–5/–4/–5/–5/–6/–5/–6/–6 and that would mess things up.

There's no magic bullet for this one. I don't know the details of your app structure, you may just have to try adding 3 hours to the midnight of whatever day you are on so that any recurring daily appointment is stored as a time only.

2
votes

This is a complicated problem. I've learned the hard way that not every day is 86,400 seconds long.

When working on a calendar application of sorts, I made a design decision early on that saved me a lot of hassle. That is, every instance of an event has an entry in the database.

One table was for all of the event information (title, description, etc.). Another table held a timestamp for each instance of that event. When someone scheduled a repeating event (say 3:00PM every Wednesday), I would insert an instance at 3:00PM in their timezone (which is actually stored as UTC) every Wednesday. Now, events in theory can repeat forever. I decided that it was much simpler to put a reasonable limit (say 50 or 100 years) on the repetition than it would be to have to calculate all of the dates of an event on the fly. To the user, it looks like the event goes on forever, but in the database it doesn't. Even if you have an event scheduled every day for 100 years, that's only 36,500 records in a very narrow table.

With this approach you have to consider exceptions. Sometimes people will change the details of one instance of an event. In those cases, I simply created another event and copied over the relevant details... since it is effectively a separate event. If you wanted to tie them all together with a group ID, you could. Changing individual scheduling of the event is easy since there is a separate row for each instance.

I recommend this approach for most scenarios like this. It saved a lot of hassle for me to be able to rely on a database for all the heavy lifting, and also solves your timezone problem at the same time.