如何检查时间戳是否处于夏令时的节约日光时间?

4
问题在于,在某些时区,例如凌晨2:00或凌晨2:59,在秋季会重复出现,而在春季的夏令时节约变化期间则不存在。
如果我运行一个PHP循环,通过整个年份的每一分钟,如何在循环中捕获DST时间节约小时?(在当前时区中,由date_default_timezone_set设置)
我该如何完成一个兼容PHP 5.2的函数,例如:
<?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
}

所以我可以使用

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

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

(时区请参见:http://www.timeanddate.com/worldclock/clockchange.html?n=224&year=2013


编辑:
我发现如何检测春季夏令时:

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

为什么时间翻倍首先是个问题? - Pekka
1
因为我想在特定的时间给药,而且病人在那个晚上不应该加倍剂量并带着一张便条拿药 :) - rubo77
那很有道理 :) - Pekka
你能否编辑这两个示例并包含你想要检查的特定日期字符串吗? - Jeff Sisson
我刚刚编辑了我的答案,以接受日期字符串并可选择使用date_default_timezone_set。我还更改了检查的间隔为31分钟(而不是之前的小时),以便更精确地与您描述的30分钟间隔数组相关联。 - Jeff Sisson
答案已更新为按分钟工作。 - Jeff Sisson
2个回答

3
如果您使用PHP的date_default_timezone_set来解析我假设是Unix时间戳的内容,那么时间戳将会相对于该时区进行解析。要确定是否有夏令时偏移(如果有),请使用DateTimeZone :: getTransitions。一个类似于您示例中的函数可能如下所示:
<?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)

那么strtotime是如何返回2:30的呢?当我将其输入到一个使用PHP内置时区EST的DateTime类中时,我得到的结果是:“2013-03-10 01:30:00”。 - Jeff Sisson
据我所知,在夏令时转换期间,时间会从凌晨1:59变为凌晨3:00(至少在春季如此)。 - Jeff Sisson
我认为你的方法不可行。更好的方法是从输入日期字符串创建一个时间戳,然后将其转换回日期字符串“Y-m-d H:i”,并比较是否有变化。因为初始的 strtotime 已经表明了输入时间不存在。 - rubo77
它是以什么方式无法工作?你运行代码并查看结果了吗?我的方法检查当前分钟,看看夏令时是否在接下来的一分钟发生。这满足了您最初的问题,但是您此后多次更改了问题。如果它不起作用并且您有更好或不同的方法,请发布它。 - Jeff Sisson
我尝试了你的代码,01:59 可以运行,但 01:58 却不行。我再次检查了一下:三月份的夏令时是从2点到3点。我的错误。但是你的代码还是有点问题,如果在其他时间运行也会出错。我没有改变问题,只是澄清了一下。我希望现在我的意图清楚了。 - rubo77
显示剩余5条评论

0

这适用于所有时间

date_default_timezone_set("America/New_York");

只有一个bug,如果我调用欧洲的话,它会显示早了一小时:

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

(欧洲的夏令时节约时间是从凌晨2点到3点)

目前为止,这是结论:

<?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;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接