4

The problem is, that for example 2:00am or 2:59am are doubled in autumn in some timezones and don't exist in spring during the DST time-saving change.

If I run a PHP loop through every minute through a whole year, how can I catch the DST timesavings hour within the loop? (in the current Timezone, set by date_default_timezone_set)

How would I complete a PHP 5.2 compatible function like:

<?php
/** returns true if a time is skipped or doubled 
 * (for example "2013-03-10 02:30" doesen't exist in USA)
 * 
 * @param string $season
 * @param string $datestring in the form of "2013-03-10 02:30"
 * @return boolean
 **/
function checkDST($datestring,$season="any"){
    $tz=date_default_timezone_get();
    $season=strtolower(trim($season));
    if($season=="spring"){
        if(/* an hour skipped */) return true;  
    }else if($season=="autumn"){
        if(/* double hour */) return true;  
    } else if($season=="any") {
        if(/* any of both */) return true;
    }
    return false
}

so I could use

date_default_timezone_set("America/New_York");
$is_skipped=checkDST("2013-03-10 02:30","spring");  

and

$exists_two_times=checkDST(2003-11-03, 02:00,"autumn"); // or 2:59 for example

(Timezones see: http://www.timeanddate.com/worldclock/clockchange.html?n=224&year=2013 )


EDIT:
I found out how to detect the spring DST:

function checkDST($datestring,$season="any"){
    var_dump('checking '.$datestring);
    $season=strtolower(trim($season));
    $datestring=substr($datestring,0,16);
    if($season!="autumn" and date("Y-m-d H:i",strtotime($datestring))!=$datestring) {
        return true;
    }
    // check for double hours in autumn
    ...
piet.t
  • 11,718
  • 21
  • 43
  • 52
rubo77
  • 19,527
  • 31
  • 134
  • 226
  • Why is the times doubling a problem in the first place? – Pekka Nov 13 '13 at 05:31
  • 1
    Because I want to administer a medicine at certain times, and the patient shouldn't double the dose during that night and get his pills with a note :) – rubo77 Nov 13 '13 at 05:32
  • That makes plenty of sense :) – Pekka Nov 13 '13 at 05:32
  • Can you edit the two examples to include a specific date string value you want to check? – Jeff Sisson Nov 13 '13 at 15:32
  • I just edited my answer to take in a datestring and optionally use the `date_default_timezone_set`. I also changed the interval that's checked to be 31 minutes (rather than the hour previously) so that it's more precisely related to the arrays of 30 minute intervals you've described. – Jeff Sisson Nov 13 '13 at 15:47
  • Answer updated to work by-the-minute. – Jeff Sisson Nov 13 '13 at 16:30

2 Answers2

3

If you're using PHP's date_default_timezone_set to parse what I assume to be Unix timestamps, the timestamps will be parsed relative to that timezone. To then determine the Daylight Savings Time offset, if one is relevant, use DateTimeZone::getTransitions.

A function that worked similar to the one in your example might look like this:

<?php
function checkDST($datestring, $season, $tz = "America/New_York", $minutes_from_now_to_check = 1){
    $seconds_to_check = ($minutes_from_now_to_check * 60) + 30;
    if (!$tz) $tz = date_default_timezone_get();
    $timestamp = strtotime($datestring);
    $timestamp_start = new DateTime();
    $timestamp_start->setTimestamp($timestamp);
    $timestamp_end = new DateTime();
    $timestamp_end->setTimestamp($timestamp)->add(new DateInterval('PT'.$seconds_to_check.'S'));

    $timezone = new DateTimeZone($tz);
    $transitions = $timezone->getTransitions($timestamp_start->getTimestamp(), $timestamp_end->getTimestamp());
    if (count($transitions) > 1) { // there's an imminent DST transition, spring or fall
        if (strtolower($season) == "spring" && $transitions[1]["isdst"] === true){
            return true;    
        } 
        if (strtolower($season)=="autumn" && $transitions[1]["isdst"] === false){
            return true;
        }
        if (strtolower($season)=="any"){
            return true;
        }
    }
    return false;
}

$is_skipped = checkDST("2013-03-10 01:59","spring");
var_dump($is_skipped); // will display bool(true)

$is_skipped = checkDST("2013-03-10 12:30","spring");
var_dump($is_skipped); // will display bool(false)

$exists_two_times=checkDST("2013-11-03 01:59","autumn");
var_dump($exists_two_times); // bool(true)

$exists_two_times=checkDST("2013-11-03 03:00","autumn");
var_dump($exists_two_times); // bool(false)
Community
  • 1
  • 1
Jeff Sisson
  • 1,616
  • 11
  • 22
  • In what way does that `strtotime` return 2:30? When I feed that into a `DateTime` class, using php built-in timezone of EST, I get: `"2013-03-10 01:30:00"` – Jeff Sisson Nov 13 '13 at 15:24
  • ...it's also my understanding that the daylight savings transition happens at 1:59AM => 3:00 AM (in the spring at least) – Jeff Sisson Nov 13 '13 at 15:26
  • There is something wrong: `checkDST("2013-03-10 01:58","spring");` gives back false – rubo77 Nov 13 '13 at 18:59
  • I think your approach doesen't work. A better approach would be to create a timestamp from the input datestring and convert it back to a datestring "Y-m-d H:i" and compare if it changed. cause the initial `strtotime` will already reveal, that the input time didn't exist – rubo77 Nov 13 '13 at 19:06
  • In what way does it not work? Did you run the code and see the results? My method checks the current minute to see if daylight savings time happens in the following minute. This satisfied your original question, but you've since changed your question many times. If it doesn't work and you have a better or different approach, you should post it. – Jeff Sisson Nov 13 '13 at 19:12
  • I tried your code and it worked for 01:59 but not for 01:58. I checked again: the dst in march was from 2 to 3 o'clock. my mistake. but there is something not working still in your code if you run it on different times. I didn't change the question, just clarified it. I hope it is clear what I try to achieve now – rubo77 Nov 13 '13 at 19:14
  • In the function, you can pass in how many minutes into the future you want to check. So, for example, if it is 01:58, it will check 1 minute into the future if you do `checkDST("2013-03-10 01:59","spring");` (false) but two (or more) minutes into the future if you do `checkDST("2013-03-10 01:59","spring", null, 2);`. You specified that you were going to check every minute of the year, so the default is checking one minute from now. But you can check as many minutes into the future as you want. – Jeff Sisson Nov 13 '13 at 19:19
  • Ah ok, now I understand your approach. But I didn't mean **exact every** minute. the times I want to check are random. It was just an easy example. I will clarify that in the question. – rubo77 Nov 13 '13 at 19:26
  • Your approach works for the autumn change, if you set fixed `$seconds_to_check=3600;`. for the spring change I added a simple solution to my question. Thanks! Now I have it with the combination. – rubo77 Nov 13 '13 at 19:39
  • just interesting: different timezones are: http://www.worldtimezone.com/daylight.html – rubo77 May 05 '14 at 01:33
0

This works on all times in

date_default_timezone_set("America/New_York");

There is only still one bug, if I call it for europe, it sais the hour is one hour early:

date_default_timezone_set("Europe/Berlin");
var_dump(checkDST("2013-10-27 01:30","any"));
var_dump(checkDST("2013-10-27 02:30","any"));

(The savings hour in europe is from 2am till 3am)

This is the conclusion so far:

<?php
/** returns true if a time is skipped or doubled 
 * (for example "2013-03-10 02:30" doesen't exist in USA)
 * 
 * @param string $season "spring", "autumn", "fall" or any other string for any of the time changes
 * @param string $datestring in the form of "2013-03-10 02:30"
 * @return boolean
 **/
function checkDST($datestring, $season, $tz = null){
    $debug=true;
    if($debug) echo ('checking '.$season.' '.$datestring);
    $season=strtolower(trim($season));
    if($season=="fall") $season="autumn";
    $datestring=substr($datestring,0,16);
    $monthdaystring=substr($datestring,0,10);
    if(!strtotime($datestring) or date("Y-m-d",strtotime($monthdaystring))!=$monthdaystring) {
        //Error
        if($debug) echo "<br>Error: not a correct datestring: $datestring";
        return false;
    }
    if($season!="autumn" and date("Y-m-d H:i",strtotime($datestring))!=$datestring) {
        // spring or any
        if($debug) echo "<br>".(date("Y-m-d H:i",strtotime($datestring)).'!='.$datestring);
        return true;
    } else if($season=="spring") {
        // spring ends here
        return false;
    }
    // "autumn" or "any" 
    $timestamp = strtotime($datestring);

    $timestamp_start = new DateTime();
    $timestamp_start->setTimestamp($timestamp);
    $timestamp_end = new DateTime();
    $timestamp_end->setTimestamp($timestamp)->add(new DateInterval('PT3600S'));

    if (!$tz) $tz = date_default_timezone_get();
    $timezone = new DateTimeZone($tz);
    $transitions = $timezone->getTransitions($timestamp_start->getTimestamp(), $timestamp_end->getTimestamp());
    if (count($transitions) > 1) { // there's an imminent DST transition, spring or fall
        // autumn
        if($debug) echo "<br>NumTransitions: ".(count($transitions));
        if($debug) var_dump($transitions);
        return true;
    }
    return false;
}
rubo77
  • 19,527
  • 31
  • 134
  • 226