Created
March 3, 2016 20:59
-
-
Save NoMan2000/a262a96b159164882cd7 to your computer and use it in GitHub Desktop.
Translating between TimeZones and Country Regions
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace CountryCodesAndAbbr; | |
require_once dirname(dirname(__DIR__)) . '/init.php'; | |
use GuzzleHttp\Client; | |
use GuzzleHttp\ClientInterface; | |
use cellControl\Traits\StatusCodeTraits; | |
use Exception; | |
/** | |
* Class CountryCodesAndAbbr | |
* @package cellControl\Translations | |
*/ | |
class CountryCodesAndAbbr | |
{ | |
use StatusCodeTraits; | |
/** | |
* @var Client | |
*/ | |
protected $client; | |
/** | |
* @var array | |
*/ | |
protected $fileList = [ | |
'countryISO2JSON' => __DIR__ . '/Storage/names.json', | |
'countryISO3JSON' => __DIR__ . '/Storage/iso3.json', | |
'languageCodesJSON' => __DIR__ . '/Storage/language-codes-3b2.json', | |
'languageCodesIETFJSON' => __DIR__ . '/Storage/ietf-language-tags.json', | |
'phoneCodesJSON' => __DIR__ . '/Storage/phone.json', | |
'continentJSON' => __DIR__ . '/Storage/continent.json', | |
'continentAndCountryJSON' => __DIR__ . '/Storage/continentAndCountry.json', | |
]; | |
/** | |
* @desc current URLs for internationalization purposes. Note that this may change in the future. | |
* @var array | |
*/ | |
protected $urlList = [ | |
'countryCodesISO2URL' => 'http://country.io/names.json', | |
'countryCodesISO3URL' => 'http://country.io/iso3.json', | |
'languageCodesURL' => 'http://data.okfn.org/data/core/language-codes/r/language-codes-3b2.json', | |
'languageCodesIETFURL' => 'http://data.okfn.org/data/core/language-codes/r/ietf-language-tags.json', | |
'phoneCodesURL' => 'http://country.io/phone.json', | |
'continentURL' => 'http://country.io/continent.json', | |
'continentAndCountryURL' => 'http://api.geonames.org/countryInfoJSON?username=dperic' | |
]; | |
/** | |
* CountryCodesAndAbbr constructor. | |
* @param ClientInterface|null $client | |
*/ | |
public function __construct(ClientInterface $client = null) | |
{ | |
$this->client = $client ?: new Client(); | |
$fileList = $this->fileList; | |
foreach ($fileList as $file) { | |
if (!file_exists($file)) { | |
touch($file); | |
} | |
} | |
} | |
/** | |
* @return ClientInterface | |
*/ | |
public function getClient() | |
{ | |
return $this->client; | |
} | |
/** | |
* @desc ISO 3166-1 AKA ISO2 | |
* This is how PHP internally understands ISO codes | |
*/ | |
public function getCountryCodesISO2() | |
{ | |
return $this->getJSONFileInformation( | |
array_get($this->getUrlList(), 'countryCodesISO2URL'), | |
array_get($this->getFileList(), 'countryISO2JSON') | |
); | |
} | |
/** | |
* @desc ISO 3166-1 alpha-3 | |
* @return array | |
*/ | |
public function getCountryCodesISO3() | |
{ | |
return $this->getJSONFileInformation( | |
array_get($this->getUrlList(), 'countryCodesISO3URL'), | |
array_get($this->getFileList(), 'countryISO3JSON') | |
); | |
} | |
/** | |
* @return array | |
*/ | |
public function getContinent() | |
{ | |
return $this->getJSONFileInformation( | |
array_get($this->getUrlList(), 'continentURL'), | |
array_get($this->getFileList(), 'continentJSON') | |
); | |
} | |
/** | |
* @return array | |
*/ | |
public function getContinentAndCountry() | |
{ | |
return $this->getJSONFileInformation( | |
array_get($this->getUrlList(), 'continentAndCountryURL'), | |
array_get($this->getFileList(), 'continentAndCountryJSON') | |
); | |
} | |
/** | |
* @return mixed | |
*/ | |
public function getFileList() | |
{ | |
return $this->fileList; | |
} | |
/** | |
* @desc This is the IETF standard for language codes, which is how Google Translate and MS understand | |
* languages. | |
* @return array | |
*/ | |
public function getLanguageCodesIETF() | |
{ | |
return $this->getJSONFileInformation( | |
array_get($this->getUrlList(), 'languageCodesIETFURL'), | |
array_get($this->getFileList(), 'languageCodesIETFJSON') | |
); | |
} | |
/** | |
* @desc This is the appropriate ISO language codes by | |
* ISO 639-1 AND ISO 639-2. | |
* @return array | |
*/ | |
public function getLanguageCodesISO() | |
{ | |
return $this->getJSONFileInformation( | |
array_get($this->getUrlList(), 'languageCodesURL'), | |
array_get($this->getFileList(), 'languageCodesJSON') | |
); | |
} | |
/** | |
* @return array | |
*/ | |
public function getPhoneCodes() | |
{ | |
return $this->getJSONFileInformation( | |
array_get($this->getUrlList(), 'phoneCodesURL'), | |
array_get($this->getFileList(), 'phoneCodesJSON') | |
); | |
} | |
/** | |
* @return mixed | |
*/ | |
public function getUrlList() | |
{ | |
return $this->urlList; | |
} | |
/** | |
* @param ClientInterface $client | |
* @return $this | |
*/ | |
public function setClient(ClientInterface $client) | |
{ | |
$this->client = $client; | |
return $this; | |
} | |
/** | |
* @param mixed $fileList | |
* @return CountryCodesAndAbbr | |
*/ | |
public function setFileList($fileList) | |
{ | |
$this->fileList = $fileList; | |
return $this; | |
} | |
/** | |
* @param mixed $urlList | |
* @return CountryCodesAndAbbr | |
*/ | |
public function setUrlList($urlList) | |
{ | |
$this->urlList = $urlList; | |
return $this; | |
} | |
/** | |
* @param $url | |
* @param $filePath | |
* @return array | |
*/ | |
protected function getJSONFileInformation($url, $filePath) | |
{ | |
$hasJSON = filesize($filePath); | |
if (!$hasJSON) { | |
$client = $this->getClient(); | |
$response = $client->request('GET', $url); | |
if (!$this->isBadStatusCode()) { | |
file_put_contents($filePath, $response->getBody()->getContents()); | |
} | |
if ($this->isBadStatusCode()) { | |
throw new \Exception($response->getReasonPhrase()); | |
} | |
} | |
return json_decode(file_get_contents($filePath), true); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<select class="timeZoneList input-width"> | |
<optgroup label="HST"> | |
<option label="Aleutian Islands" value="America/Adak">America/Adak</option> | |
<option label="Hawaii" value="Pacific/Honolulu">Pacific/Honolulu</option> | |
</optgroup> | |
<optgroup label="PST"> | |
<option label="Pacific Time" value="America/Los_Angeles">America/Los_Angeles</option> | |
<option label="Pacific Standard Time - Annette Island, Alaska" value="America/Metlakatla">America/Metlakatla</option> | |
</optgroup> | |
<optgroup label="MST"> | |
<option label="Mountain Time - south Idaho & east Oregon" value="America/Boise">America/Boise</option> | |
<option label="Mountain Time" value="America/Denver">America/Denver</option> | |
<option label="Mountain Standard Time - Arizona (except Navajo)" value="America/Phoenix">America/Phoenix</option> | |
</optgroup> | |
<optgroup label="AKST"> | |
<option label="Alaska Time" value="America/Anchorage">America/Anchorage</option> | |
<option label="Alaska Time - Alaska panhandle" value="America/Juneau">America/Juneau</option> | |
<option label="Alaska Time - west Alaska" value="America/Nome">America/Nome</option> | |
<option label="Alaska Time - southeast Alaska panhandle" value="America/Sitka">America/Sitka</option> | |
<option label="Alaska Time - Alaska panhandle neck" value="America/Yakutat">America/Yakutat</option> | |
</optgroup> | |
<optgroup label="CST"> | |
<option label="Central Time" value="America/Chicago">America/Chicago</option> | |
<option label="Central Time - Indiana - Starke County" value="America/Indiana/Knox">America/Indiana/Knox</option> | |
<option label="Central Time - Indiana - Perry County" value="America/Indiana/Tell_City">America/Indiana/Tell_City</option> | |
<option label="Central Time - Michigan - Dickinson, Gogebic, Iron & Menominee Counties" value="America/Menominee">America/Menominee</option> | |
<option label="Central Time - North Dakota - Mercer County" value="America/North_Dakota/Beulah">America/North_Dakota/Beulah</option> | |
<option label="Central Time - North Dakota - Oliver County" value="America/North_Dakota/Center">America/North_Dakota/Center</option> | |
<option label="Central Time - North Dakota - Morton County (except Mandan area)" value="America/North_Dakota/New_Salem">America/North_Dakota/New_Salem</option> | |
</optgroup> | |
<optgroup label="EST"> | |
<option label="Eastern Time - Michigan - most locations" value="America/Detroit">America/Detroit</option> | |
<option label="Eastern Time - Indiana - most locations" value="America/Indiana/Indianapolis">America/Indiana/Indianapolis</option> | |
<option label="Eastern Time - Indiana - Crawford County" value="America/Indiana/Marengo">America/Indiana/Marengo</option> | |
<option label="Eastern Time - Indiana - Pike County" value="America/Indiana/Petersburg">America/Indiana/Petersburg</option> | |
<option label="Eastern Time - Indiana - Switzerland County" value="America/Indiana/Vevay">America/Indiana/Vevay</option> | |
<option label="Eastern Time - Indiana - Daviess, Dubois, Knox & Martin Counties" value="America/Indiana/Vincennes">America/Indiana/Vincennes</option> | |
<option label="Eastern Time - Indiana - Pulaski County" value="America/Indiana/Winamac">America/Indiana/Winamac</option> | |
<option label="Eastern Time - Kentucky - Louisville area" value="America/Kentucky/Louisville">America/Kentucky/Louisville</option> | |
<option label="Eastern Time - Kentucky - Wayne County" value="America/Kentucky/Monticello">America/Kentucky/Monticello</option> | |
<option label="Eastern Time" value="America/New_York">America/New_York</option> | |
</optgroup> | |
<optgroup label="SAST"> | |
<option label="" value="Africa/Johannesburg">Africa/Johannesburg</option> | |
</optgroup> | |
<optgroup label="GMT"> | |
<option label="" value="Europe/London">Europe/London</option> | |
</optgroup> | |
<optgroup label="MIST"> | |
<option label="Macquarie Island" value="Antarctica/Macquarie">Antarctica/Macquarie</option> | |
</optgroup> | |
<optgroup label="AWST"> | |
<option label="Western Australia - most locations" value="Australia/Perth">Australia/Perth</option> | |
</optgroup> | |
<optgroup label="ACST"> | |
<option label="Northern Territory" value="Australia/Darwin">Australia/Darwin</option> | |
</optgroup> | |
<optgroup label="ACWST"> | |
<option label="Western Australia - Eucla area" value="Australia/Eucla">Australia/Eucla</option> | |
</optgroup> | |
<optgroup label="LHDT"> | |
<option label="Lord Howe Island" value="Australia/Lord_Howe">Australia/Lord_Howe</option> | |
</optgroup> | |
<optgroup label="ACDT"> | |
<option label="South Australia" value="Australia/Adelaide">Australia/Adelaide</option> | |
<option label="New South Wales - Yancowinna" value="Australia/Broken_Hill">Australia/Broken_Hill</option> | |
</optgroup> | |
<optgroup label="AEST"> | |
<option label="Queensland - most locations" value="Australia/Brisbane">Australia/Brisbane</option> | |
<option label="Queensland - Holiday Islands" value="Australia/Lindeman">Australia/Lindeman</option> | |
</optgroup> | |
<optgroup label="AEDT"> | |
<option label="Tasmania - King Island" value="Australia/Currie">Australia/Currie</option> | |
<option label="Tasmania - most locations" value="Australia/Hobart">Australia/Hobart</option> | |
<option label="Victoria" value="Australia/Melbourne">Australia/Melbourne</option> | |
<option label="New South Wales - most locations" value="Australia/Sydney">Australia/Sydney</option> | |
</optgroup> | |
</select> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use TimeFormatter\TimeFormatter; | |
use CountryCodesAndAbbr\CountryCodesAndAbbr; | |
$codes = new CountryCodesAndAbbr(); | |
$time = new TimeFormatter($codes); | |
$timeZoneByRegion = $time->getTimezonesByCountryCodeLookup(); | |
$continentList = "<select class='timeZoneList input-width'>"; | |
foreach ($timeZoneByRegion['timeZoneValues'] as $continentHolderName => $continentItemHolder) { | |
$continentList .= "<optgroup label='$continentHolderName'>"; | |
foreach ($continentItemHolder as $tzAbbr => $tzList) { | |
$continentList .= "<option label='$tzList' value='$tzAbbr'>$tzAbbr</option>"; | |
} | |
$continentList .= "</optgroup>"; | |
} | |
$continentList .= "</select>"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace TimeFormatter; | |
require_once dirname(dirname(__DIR__)) . '/init.php'; | |
use DateTime; | |
use DateTimeZone; | |
use LengthException; | |
use CountryCodesAndAbbr\CountryCodesAndAbbr; | |
/** | |
* Class TimeFormatter | |
* @package TimeFormatter | |
*/ | |
class TimeFormatter | |
{ | |
/** | |
* @var array | |
*/ | |
static $regions = [ | |
DateTimeZone::AFRICA, | |
DateTimeZone::AMERICA, | |
DateTimeZone::ANTARCTICA, | |
DateTimeZone::ASIA, | |
DateTimeZone::ATLANTIC, | |
DateTimeZone::AUSTRALIA, | |
DateTimeZone::EUROPE, | |
DateTimeZone::INDIAN, | |
DateTimeZone::PACIFIC, | |
]; | |
/** | |
* | |
*/ | |
const USA_ISO = 'US'; | |
/** | |
* | |
*/ | |
const SOUTH_AFRICA_ISO = 'ZA'; | |
/** | |
* | |
*/ | |
const UNITED_KINGDOM_ISO = 'GB'; | |
/** | |
* | |
*/ | |
const AUSTRALIA_ISO = 'AU'; | |
/** | |
* @var array | |
*/ | |
protected $abbr; | |
/** | |
* @var CountryCodesAndAbbr | |
*/ | |
protected $countryCodeModel; | |
/** | |
* @var array | |
*/ | |
protected $timezones; | |
/** | |
* TimeFormatter constructor. | |
* @param CountryCodesAndAbbr $countryModel | |
*/ | |
public function __construct(CountryCodesAndAbbr $countryModel = null) | |
{ | |
$this->countryCodeModel = $countryModel ?: new CountryCodesAndAbbr(); | |
} | |
/** | |
* @return array | |
*/ | |
public function generateTimezoneList() | |
{ | |
$timezones = []; | |
foreach (static::$regions as $region) { | |
$timezones = array_merge($timezones, DateTimeZone::listIdentifiers($region)); | |
} | |
$timezoneOffsets = []; | |
foreach ($timezones as $timezone) { | |
$tz = new DateTimeZone($timezone); | |
$timezoneOffsets[$timezone] = $tz->getOffset(new DateTime); | |
} | |
// sort timezone by offset | |
asort($timezoneOffsets); | |
$timezone_list = []; | |
foreach ($timezoneOffsets as $timezone => $offset) { | |
$tz = new DateTimeZone($timezone); | |
$abbr = $tz->getTransitions(time(), time())[0]['abbr']; | |
$offsetPrefix = $offset < 0 ? '-' : '+'; | |
$offsetFormatted = gmdate('H:i', abs($offset)); | |
$pretty_offset = "UTC{$offsetPrefix}{$offsetFormatted}"; | |
$timezone_list[$timezone] = "$abbr - ({$pretty_offset}) $timezone"; | |
} | |
return $this->timezones = $timezone_list; | |
} | |
/** | |
* @return array | |
*/ | |
public function getAbbr() | |
{ | |
return $this->abbr = $this->abbr ?: $this->getShortCodeGrouping(); | |
} | |
/** | |
* @param $timeZoneLocation | |
*/ | |
public function getCountryByTimeZone($timeZoneLocation) | |
{ | |
$countryByLocation = []; | |
$countryList = $this->getCountryCodeModel()->getCountryCodesISO2(); | |
if ($this->isTimezone($timeZoneLocation)) { | |
$tz = new DateTimeZone($timeZoneLocation); | |
$countryCode = $tz->getLocation()['country_code']; | |
$countryName = key_exists($countryCode, $countryList) ? $countryList[$countryCode] : null; | |
if (!$countryName) { | |
throw new LengthException('Unable to find country'); | |
} | |
$countryByLocation[$countryCode] = $countryName; | |
} | |
return $countryByLocation; | |
} | |
/** | |
* @return array | |
*/ | |
public function getContinentFull() | |
{ | |
$continentList = $this->getCountryCodeModel()->getContinentAndCountry()['geonames']; | |
$continentSort = []; | |
foreach ($continentList as $arrayList) { | |
$continentSort[$arrayList['continentName']][] = [ | |
'continentAbbr' => $arrayList['continent'], | |
'continentName' => $arrayList['continentName'], | |
'countryCode' => $arrayList['countryCode'], | |
'countryName' => $arrayList['countryName'] | |
]; | |
} | |
ksort($continentSort); | |
return $continentSort; | |
} | |
/** | |
* @param string|null $region | |
* @return array | |
*/ | |
public function getTimezonesByCountryCodeLookup($region = null) | |
{ | |
if (null === $region) { | |
return array_merge_recursive( | |
$this->getTimezonesByCountryCodeLookup(self::USA_ISO), | |
$this->getTimezonesByCountryCodeLookup(self::SOUTH_AFRICA_ISO), | |
$this->getTimezonesByCountryCodeLookup(self::UNITED_KINGDOM_ISO), | |
$this->getTimezonesByCountryCodeLookup(self::AUSTRALIA_ISO) | |
); | |
} | |
$region = mb_strtoupper($region); | |
if (!$this->isRegion($region)) { | |
throw new LengthException("No region called $region exists"); | |
} | |
$fullList = $this->getContinentFull(); | |
$timeZoneList = $this->groupByRegion($region); | |
$partialList = []; | |
foreach ($fullList as $countryIdentifiers) { | |
foreach ($countryIdentifiers as $countryValues) { | |
if ($countryValues['countryCode'] === $region) { | |
$partialList = [ | |
'countryValues' => $countryValues, | |
'timeZoneValues' => $timeZoneList, | |
]; | |
} | |
} | |
} | |
return $partialList; | |
} | |
/** | |
* @return CountryCodesAndAbbr | |
*/ | |
public function getCountryCodeModel() | |
{ | |
return $this->countryCodeModel; | |
} | |
/** | |
* | |
*/ | |
public function getShortCodeGrouping() | |
{ | |
$this->timezones = $this->getTimezones(); | |
foreach ($this->timezones as $timeZoneName => $timeZoneValue) { | |
$tz = new DateTimeZone($timeZoneName); | |
$abbr = $tz->getTransitions(time(), time())[0]['abbr']; | |
$this->abbr[$abbr][$timeZoneName] = $timeZoneValue; | |
} | |
return $this->abbr; | |
} | |
/** | |
* @return array | |
*/ | |
public function getTimezones() | |
{ | |
return $this->timezones = $this->timezones ?: $this->generateTimezoneList(); | |
} | |
/** | |
* @param $region | |
*/ | |
public function groupByRegion($region) | |
{ | |
$region = mb_strtoupper($region); | |
if (!$this->isRegion($region)) { | |
throw new LengthException("No region called $region exists"); | |
} | |
$timeZones = DateTimeZone::listIdentifiers(DateTimeZone::PER_COUNTRY, $region); | |
$locationList = []; | |
foreach ($timeZones as $zoneName) { | |
$tz = new DateTimeZone($zoneName); | |
$loc = $tz->getLocation(); | |
$name = $tz->getName(); | |
$abbr = $tz->getTransitions(time(), time())[0]['abbr']; | |
$locationList[$abbr][$name] = $loc['comments']; | |
} | |
asort($locationList); | |
return $locationList; | |
} | |
/** | |
* @param $key | |
* @return bool | |
*/ | |
public function isRegion($key) | |
{ | |
return key_exists($key, $this->getCountryCodeModel()->getCountryCodesISO2()); | |
} | |
/** | |
* @param $key | |
* @return bool | |
*/ | |
public function isTimezone($key) | |
{ | |
return key_exists($key, $this->getTimezones()); | |
} | |
/** | |
* @param $key | |
* @return mixed | |
*/ | |
public function offset($key) | |
{ | |
$timezones = $this->getTimezones(); | |
if ($this->isTimezone($key)) { | |
return $timezones[$key]; | |
} | |
return null; | |
} | |
/** | |
* @param array $timezones | |
* @return TimeFormatter | |
*/ | |
public function setTimezones($timezones) | |
{ | |
$this->timezones = $timezones; | |
return $this; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment