Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
235 views
in Technique[技术] by (71.8m points)

php - date_sun_info() for a specific calendar date

One thing that has gotten me into trouble recently was PHP's date_sun_info() function. It returns an array with information about daylight such as sunrise and sunset but also various twilight phases. However, when using this function it appears as if PHP always calculates the appropriate parameters chronologically. This can create a problem depending on the timezone you want these parameters for.

Let's take these two examples (assuming we're using UTC as default timezone, i.e. date_default_timezone_set("UTC")):

date_sun_info(strtotime("2013-01-01"),31.5,35.2)

returns

sunrise: 2013-01-01 04:38:39
sunset: 2013-01-01 14:47:28
transit: 2013-01-01 09:43:03
civil_twilight_begin: 2013-01-01 04:12:02
civil_twilight_end: 2013-01-01 15:14:05
nautical_twilight_begin: 2013-01-01 03:41:47
nautical_twilight_end: 2013-01-01 15:44:20
astronomical_twilight_begin: 2013-01-01 03:12:11
astronomical_twilight_end: 2013-01-01 16:13:56

Note how all the cacluated parameters neatly fall into the calendar date that was passed to the function. But now let's look at values for the other side of the globe:

date_sun_info(strtotime("2013-01-01"),-44.5,-176.2)

which returns

sunrise: 2013-01-01 16:05:13
sunset: 2013-01-02 07:32:39
transit: 2013-01-01 23:48:56
civil_twilight_begin: 2013-01-01 15:28:55
civil_twilight_end: 2013-01-02 08:08:56
nautical_twilight_begin: 2013-01-01 14:41:04
nautical_twilight_end: 2013-01-02 08:56:47
astronomical_twilight_begin: 2013-01-01 13:40:01
astronomical_twilight_end: 2013-01-02 09:57:50

Obviously, some of the parameters do not fall into the calendar date fed to the function but are calculated for the specified timezone. I noticed that this has thrown other people off in the past as well. However, in some cases what is desired is to get the times of the different phases of the day for the specified calendar date. In the case of above example the output would have to look like this:

transit: 2013-01-01 00:48:26
sunset: 2013-01-01 08:32:38
civil_twilight_end: 2013-01-01 09:09:01
nautical_twilight_end: 2013-01-01 09:57:01
astronomical_twilight_end: 2013-01-01 10:58:26
astronomical_twilight_begin: 2013-01-01 14:39:57
nautical_twilight_begin: 2013-01-01 15:41:01
civil_twilight_begin: 2013-01-01 16:28:52
sunrise: 2013-01-01 17:05:10

I have written a custom function to get around this:

function adjustedSunInfo($date,$lat,$lon) {
    $sinfo=date_sun_info ( strtotime($date) ,$lat,$lon );
    $sinfoMinus=date_sun_info ( strtotime($date)-86400 ,$lat,$lon );
    $sinfoPlus=date_sun_info ( strtotime($date)+86400 ,$lat,$lon );

    foreach($sinfo as $key=>$val) {
        if(date("d",$val)>date("d",strtotime($date))) { 
            $sinfo[$key]=$sinfoMinus[$key];
        } else if(date("d",$val)>date("d",strtotime($date))) { 
            $sinfo[$key]=$sinfoPlus[$key];
        }
    }

    asort($sinfo);
    return $sinfo;
}

However, the question is whether there are an (undocumented) flags that can be passed to PHP - or any other tricks - to get the desired info?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I'm not sure that I understand your problem 100%, but it may be that you are overthinking this a bit. I am often guilty of that myself.

Also, I'm not sure what version of PHP you are using as date_sun_info returns an associative array of timestamps1, which doesn't match your example output.

Anyway, to answer your question:-

whether there are an (undocumented) flags that can be passed to PHP - or any other tricks - to get the desired info?

There are no flags (certainly non documented), but there are some tricks. The timestamp returned is the GMT of the event (e.g. sunrise), so all you have to do is to apply the required timezone to it. The DateTime classes will make this easy.

As an example here is how I would rewrite your function:-

/**
* Get an array of human readable times for sun events
* @param DateTime $date A DateTime Object set to the desired date
*                        and the correct timezone for the place in question
* @param float $lat The latitude of the place in question
* @param float $lon The longitude of the place in question
*
* @return array An associative array of human readable times sorted in chronological order.
*/

function adjustedSunInfo(DateTime $date,$lat,$lon) {
    $sinfo=date_sun_info ($date->getTimestamp() ,$lat,$lon );

    foreach($sinfo as $key=>$val) {
        //You should check that $val isn't 1 or empty first
        $time = new DateTime('@' . $val);
        $time->setTimezone($date->getTimeZone());
        $sinfo[$key] = $time->format('Y m d H:i:s');
    }

    asort($sinfo);
    return $sinfo;
}

$time = new DateTime('2013-01-01', new DateTimeZone('Pacific/Chatham'));
var_dump(adjustedSunInfo($time, -44.5, -176.2));

See it working.

Your example position is just off the Chatham Islands, so I have used their time zone for this. You need to know the timezone in question to get an accurate answer.

This method will not be upset by DST changes or Leap Years as yours will and, I hope you'll agree, is a lot simpler and easier to read.

I hope this goes someway towards solving your problem.

1. Sometimes 1 or an empty array element may be returned.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...