Part of a PHP project I’m working contains a list of sites/buildings. For each site/building we monitor some data, for example its energy usage.
We decided that we wanted to generate a daily/weekly/monthly reports of the data, by aggregating the datapoints. As our sites/buildings are spread across the globe – and thus timezones – we can’t simply select data between 00:00:00 UTC
and 23:59:59 UTC
but have to use its geographical location’s “day window” to do our calculations.
Unfortunately we don’t didn’t store the timezone for a site/building, but since we do keep track of its geographical location – using a WGS84 latitude-longitude pair – it should be possible to derive its timezone, right?
Right! On StackOverflow I found this little snippet that does the job:
function get_nearest_timezone($cur_lat, $cur_long, $country_code = '') {
$timezone_ids = ($country_code) ? DateTimeZone::listIdentifiers(DateTimeZone::PER_COUNTRY, $country_code)
: DateTimeZone::listIdentifiers();
if($timezone_ids && is_array($timezone_ids) && isset($timezone_ids[0])) {
$time_zone = '';
$tz_distance = 0;
//only one identifier?
if (count($timezone_ids) == 1) {
$time_zone = $timezone_ids[0];
} else {
foreach($timezone_ids as $timezone_id) {
$timezone = new DateTimeZone($timezone_id);
$location = $timezone->getLocation();
$tz_lat = $location['latitude'];
$tz_long = $location['longitude'];
$theta = $cur_long - $tz_long;
$distance = (sin(deg2rad($cur_lat)) * sin(deg2rad($tz_lat)))
+ (cos(deg2rad($cur_lat)) * cos(deg2rad($tz_lat)) * cos(deg2rad($theta)));
$distance = acos($distance);
$distance = abs(rad2deg($distance));
// echo '<br />'.$timezone_id.' '.$distance;
if (!$time_zone || $tz_distance > $distance) {
$time_zone = $timezone_id;
$tz_distance = $distance;
}
}
}
return $time_zone;
}
return 'unknown';
}
Usage is as follows:
// Timezone for one NY coordinate
echo get_nearest_timezone(40.772222,-74.164581);
// ~> America/New_York
// Timezone for one Belgian coordinate
echo get_nearest_timezone(51.0162167, 3.7338451);
// ~> Europe/Brussels
// More faster and accurate if you can pass the country code
echo get_nearest_timezone(40.772222, -74.164581, 'US');
// ~> America/New_York
With this timezone identifier now being available, we can include it in our queries and generate our daily/weekly/monthly reports 🙂
🍻 Here’s to copying-and-pasting from StackOverflow!
On a related note: Falsehoods programmers believe about time and time zones is worth a read, especially if you’ve already dealt with time and timezones.