Skip to content

Instantly share code, notes, and snippets.

@NoMan2000
Created March 3, 2016 20:59
Show Gist options
  • Save NoMan2000/a262a96b159164882cd7 to your computer and use it in GitHub Desktop.
Save NoMan2000/a262a96b159164882cd7 to your computer and use it in GitHub Desktop.
Translating between TimeZones and Country Regions
<?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);
}
}
<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 &amp; 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 &amp; 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 &amp; 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>
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>";
<?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