Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[zend-date] properly calculate sunrise, sunset and twilight times #151

Merged
merged 2 commits into from
Dec 2, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 13 additions & 27 deletions packages/zend-date/library/Zend/Date.php
Original file line number Diff line number Diff line change
Expand Up @@ -3304,23 +3304,8 @@ private function _checkLocation($location)
throw new Zend_Date_Exception('Latitude must be between -90 and 90', 0, null, $location);
}

if (!isset($location['horizon'])){
$location['horizon'] = 'effective';
}

switch ($location['horizon']) {
case 'civil' :
return -0.104528;
break;
case 'nautic' :
return -0.207912;
break;
case 'astronomic' :
return -0.309017;
break;
default :
return -0.0145439;
break;
if (isset($location['horizon']) && !in_array($location['horizon'], array('civil', 'nautic', 'astronomic', 'effective'))) {
throw new Zend_Date_Exception('Invalid value for \'horizon\', must be one of: \'civil\', \'nautic\', \'astronomic\', \'effective\'', 0, null, $location);
}
}

Expand All @@ -3330,17 +3315,17 @@ private function _checkLocation($location)
* For a list of cities and correct locations use the class Zend_Date_Cities
*
* @param array $location location of sunrise
* ['horizon'] -> civil, nautic, astronomical, effective (default)
* ['horizon'] -> (optional) civil, nautic, astronomic, effective (default)
* ['longitude'] -> longitude of location
* ['latitude'] -> latitude of location
* @return Zend_Date
* @throws Zend_Date_Exception
*/
public function getSunrise($location)
{
$horizon = $this->_checkLocation($location);
$this->_checkLocation($location);
$result = clone $this;
$result->set($this->calcSun($location, $horizon, true), self::TIMESTAMP);
$result->set($this->calcSun($location, true), self::TIMESTAMP);
return $result;
}

Expand All @@ -3350,17 +3335,17 @@ public function getSunrise($location)
* For a list of cities and correct locations use the class Zend_Date_Cities
*
* @param array $location location of sunset
* ['horizon'] -> civil, nautic, astronomical, effective (default)
* ['horizon'] -> (optional) civil, nautic, astronomic, effective (default)
* ['longitude'] -> longitude of location
* ['latitude'] -> latitude of location
* @return Zend_Date
* @throws Zend_Date_Exception
*/
public function getSunset($location)
{
$horizon = $this->_checkLocation($location);
$this->_checkLocation($location);
$result = clone $this;
$result->set($this->calcSun($location, $horizon, false), self::TIMESTAMP);
$result->set($this->calcSun($location, false), self::TIMESTAMP);
return $result;
}

Expand All @@ -3370,7 +3355,7 @@ public function getSunset($location)
* For a list of cities and correct locations use the class Zend_Date_Cities
*
* @param array $location location of suninfo
* ['horizon'] -> civil, nautic, astronomical, effective (default)
* ['horizon'] -> (optional) civil, nautic, astronomic, effective (default)
* ['longitude'] -> longitude of location
* ['latitude'] -> latitude of location
* @return array - [sunset|sunrise][effective|civil|nautic|astronomic]
Expand All @@ -3394,12 +3379,13 @@ public function getSunInfo($location)
$location['horizon'] = 'astronomic';
break;
}
$horizon = $this->_checkLocation($location);
$this->_checkLocation($location);

$result = clone $this;
$result->set($this->calcSun($location, $horizon, true), self::TIMESTAMP);
$result->set($this->calcSun($location, true), self::TIMESTAMP);
$suninfo['sunrise'][$location['horizon']] = $result;
$result = clone $this;
$result->set($this->calcSun($location, $horizon, false), self::TIMESTAMP);
$result->set($this->calcSun($location, false), self::TIMESTAMP);
$suninfo['sunset'][$location['horizon']] = $result;
}
return $suninfo;
Expand Down
8 changes: 5 additions & 3 deletions packages/zend-date/library/Zend/Date/Cities.php
Original file line number Diff line number Diff line change
Expand Up @@ -291,18 +291,20 @@ class Zend_Date_Cities
* Returns the location from the selected city
*
* @param string $city City to get location for
* @param string $horizon Horizon to use :
* @param string|null $horizon Horizon to use :
* default: effective
* others are civil, nautic, astronomic
* @return array
* @throws Zend_Date_Exception When city is unknown
*/
public static function City($city, $horizon = false)
public static function City($city, $horizon = null)
{
foreach (self::$cities as $key => $value) {
if (strtolower($key) === strtolower($city)) {
$return = $value;
$return['horizon'] = $horizon;
if ($horizon !== null) {
$return['horizon'] = $horizon;
}
return $return;
}
}
Expand Down
48 changes: 39 additions & 9 deletions packages/zend-date/library/Zend/Date/DateObject.php
Original file line number Diff line number Diff line change
Expand Up @@ -906,20 +906,31 @@ private function _range($a, $b) {
/**
* Calculates the sunrise or sunset based on a location
*
* @param array $location Location for calculation MUST include 'latitude', 'longitude', 'horizon'
* @param bool $horizon true: sunrise; false: sunset
* @param array $location Location for calculation MUST include 'latitude', 'longitude'
* and optional 'horizon' (civil, nautic, astronomical or effective [default])
* @param bool $rise true: sunrise; false: sunset
* @return mixed - false: midnight sun, integer:
*/
protected function calcSun($location, $horizon, $rise = false)
protected function calcSun($location, $rise = false)
{
$horizon = isset($location['horizon']) ? $location['horizon'] : 'effective';

// timestamp within 32bit
if (abs($this->_unixTimestamp) <= 0x7FFFFFFF) {
if ($rise === false) {
return date_sunset($this->_unixTimestamp, SUNFUNCS_RET_TIMESTAMP, $location['latitude'],
$location['longitude'], 90 + $horizon, $this->getGmtOffset() / 3600);
$suninfo = date_sun_info($this->_unixTimestamp, $location['latitude'], $location['longitude']);
$prop = $rise ? 'sunrise' : 'sunset';
switch ($horizon) {
case 'civil':
$prop = $rise ? 'civil_twilight_begin' : 'civil_twilight_end';
break;
case 'nautic':
$prop = $rise ? 'nautical_twilight_begin' : 'nautical_twilight_end';
break;
case 'astronomic':
$prop = $rise ? 'astronomical_twilight_begin' : 'astronomical_twilight_end';
break;
}
return date_sunrise($this->_unixTimestamp, SUNFUNCS_RET_TIMESTAMP, $location['latitude'],
$location['longitude'], 90 + $horizon, $this->getGmtOffset() / 3600);
return $suninfo[$prop];
}

// self calculation - timestamp bigger than 32bit
Expand Down Expand Up @@ -965,7 +976,26 @@ protected function calcSun($location, $horizon, $rise = false)
$solDeclination /= sqrt(-$solDeclination * $solDeclination + 1);
$solDeclination = atan2($solDeclination, 1);

$solHorizon = $horizon - sin($solDeclination) * sin($radLatitude);
// Even though horizon declinations should rather be set to (minus) radian values of 0.35', 6, 12, 18 degrees respectively,
// the following values were used to be in sync with the old date_sunrise() and date_sunset() functions
// and they are taken from original sources by William C. Bell / Chris Spratt et. al.
// https://github.com/hollie/misterhouse/blob/67b4e36de6fccfb159c6b04c2f3e4b238ad23b5b/bin/sun_time#L97-L100
// and were defined exactly like that in Zend_Date::_checkLocation() - they are now moved here.
$horizonDeclination = -0.0145439;
switch ($horizon) {
case 'civil':
$horizonDeclination = -0.104528;
break;
case 'nautic':
$horizonDeclination = -0.207912;
break;
case 'astronomic':
$horizonDeclination = -0.309017;
break;
}

$solHorizon = $horizonDeclination - sin($solDeclination) * sin($radLatitude);
//$solHorizon = $horizon - sin($solDeclination) * sin($radLatitude);
$solHorizon /= cos($solDeclination) * cos($radLatitude);

// midnight sun, always night
Expand Down
Loading