timezones
This commit is contained in:
parent
235bc0b405
commit
ed6e462f2c
398
app/Services/TimezoneList.php
Normal file
398
app/Services/TimezoneList.php
Normal file
@ -0,0 +1,398 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use DateTime;
|
||||
use DateTimeZone;
|
||||
|
||||
final class TimezoneList
|
||||
{
|
||||
/**
|
||||
* HTML entities.
|
||||
*/
|
||||
private const MINUS = '−';
|
||||
private const PLUS = '+';
|
||||
private const WHITESPACE = ' ';
|
||||
|
||||
/**
|
||||
* General timezones.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected array $generalTimezones = [
|
||||
'GMT',
|
||||
'UTC',
|
||||
];
|
||||
|
||||
/**
|
||||
* All continents of the world.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected array $continents = [
|
||||
'Africa' => DateTimeZone::AFRICA,
|
||||
'America' => DateTimeZone::AMERICA,
|
||||
'Antarctica' => DateTimeZone::ANTARCTICA,
|
||||
'Arctic' => DateTimeZone::ARCTIC,
|
||||
'Asia' => DateTimeZone::ASIA,
|
||||
'Atlantic' => DateTimeZone::ATLANTIC,
|
||||
'Australia' => DateTimeZone::AUSTRALIA,
|
||||
'Europe' => DateTimeZone::EUROPE,
|
||||
'Indian' => DateTimeZone::INDIAN,
|
||||
'Pacific' => DateTimeZone::PACIFIC,
|
||||
];
|
||||
|
||||
/**
|
||||
* The filter of the groups to get.
|
||||
*/
|
||||
protected array $groupsFilter = [];
|
||||
|
||||
/**
|
||||
* Status of grouping the return list.
|
||||
*/
|
||||
protected bool $splitGroup = true;
|
||||
|
||||
/**
|
||||
* Status of showing timezone offset.
|
||||
*/
|
||||
protected bool $showOffset = true;
|
||||
|
||||
/**
|
||||
* The offset prefix in list.
|
||||
*/
|
||||
protected string $offsetPrefix = 'GMT/UTC';
|
||||
|
||||
/**
|
||||
* Set the filter of the groups want to get.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function onlyGroups(array $groups = []): static
|
||||
{
|
||||
$this->groupsFilter = $groups;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the filter of the groups do not want to get.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function excludeGroups(array $groups = []): static
|
||||
{
|
||||
if (empty($groups)) {
|
||||
$this->groupsFilter = [];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
$this->groupsFilter = array_values(array_diff(array_keys($this->continents), $groups));
|
||||
|
||||
if (! in_array('General', $groups)) {
|
||||
$this->groupsFilter[] = 'General';
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decide whether to split group or not.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function splitGroup(bool $status = true): static
|
||||
{
|
||||
$this->splitGroup = (bool) $status;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decide whether to show the offset or not.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function showOffset(bool $status = true): static
|
||||
{
|
||||
$this->showOffset = (bool) $status;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return new static to reset all config.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function reset(): static
|
||||
{
|
||||
return new self;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an array of timezones.
|
||||
*/
|
||||
public function toArray(bool $htmlEncode = true): mixed
|
||||
{
|
||||
$list = [];
|
||||
|
||||
// If do not split group
|
||||
if (! $this->splitGroup) {
|
||||
if ($this->includeGeneral()) {
|
||||
foreach ($this->generalTimezones as $timezone) {
|
||||
$list[$timezone] = $this->formatTimezone($timezone, null, $htmlEncode);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->loadContinents() as $continent => $mask) {
|
||||
$timezones = DateTimeZone::listIdentifiers($mask);
|
||||
|
||||
foreach ($timezones as $timezone) {
|
||||
$list[$timezone] = $this->formatTimezone($timezone, null, $htmlEncode);
|
||||
}
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
// If split group
|
||||
if ($this->includeGeneral()) {
|
||||
foreach ($this->generalTimezones as $timezone) {
|
||||
$list['General'][$timezone] = $this->formatTimezone($timezone, null, $htmlEncode);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->loadContinents() as $continent => $mask) {
|
||||
$timezones = DateTimeZone::listIdentifiers($mask);
|
||||
|
||||
foreach ($timezones as $timezone) {
|
||||
$list[$continent][$timezone] = $this->formatTimezone($timezone, $continent, $htmlEncode);
|
||||
}
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias of the `toSelectBox()` method.
|
||||
*
|
||||
* @param string $name The name of the select tag
|
||||
* @param string|null $selected The selected value
|
||||
* @param array|string|null $attrs The HTML attributes of select tag
|
||||
* @param bool $htmlEncode Use HTML entities for values of select tag
|
||||
*
|
||||
*@deprecated 6.0.0 This method name no longer matches the semantics
|
||||
*/
|
||||
public function create(string $name, ?string $selected = null, array|string|null $attrs = null, bool $htmlEncode = true): string
|
||||
{
|
||||
return $this->toSelectBox($name, $selected, $attrs, $htmlEncode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a select box of timezones.
|
||||
*
|
||||
* @param string $name The name of the select tag
|
||||
* @param string|null $selected The selected value
|
||||
* @param array|string|null $attrs The HTML attributes of select tag
|
||||
* @param bool $htmlEncode Use HTML entities for values of select tag
|
||||
* @return string
|
||||
*/
|
||||
public function toSelectBox(string $name, ?string $selected = null, array|string|null $attrs = null, bool $htmlEncode = true)
|
||||
{
|
||||
// Attributes for select element
|
||||
$attrString = null;
|
||||
|
||||
if (! empty($attrs)) {
|
||||
if (is_array($attrs)) {
|
||||
foreach ($attrs as $attr_name => $attr_value) {
|
||||
$attrString .= ' ' . $attr_name . '="' . $attr_value . '"';
|
||||
}
|
||||
} else {
|
||||
$attrString = $attrs;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->splitGroup) {
|
||||
return $this->makeSelectTagWithGroup($name, $selected, $attrString, $htmlEncode);
|
||||
}
|
||||
|
||||
return $this->makeSelectTagWithoutGroup($name, $selected, $attrString, $htmlEncode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate select element with the optgroup tag.
|
||||
*
|
||||
* @param string $name The name of the select tag
|
||||
* @param null|string $selected The selected value
|
||||
* @param null|string $attrs The HTML attributes of select tag
|
||||
* @param bool $htmlEncode Use HTML entities for values of select tag
|
||||
* @return string
|
||||
*/
|
||||
protected function makeSelectTagWithGroup(string $name, ?string $selected = null, ?string $attrs = null, bool $htmlEncode = true)
|
||||
{
|
||||
$attrs = ! empty($attrs) ? ' ' . trim((string) $attrs) : '';
|
||||
$output = '<select name="' . (string) $name . '"' . $attrs . '>';
|
||||
|
||||
if ($this->includeGeneral()) {
|
||||
$output .= '<optgroup label="General">';
|
||||
|
||||
foreach ($this->generalTimezones as $timezone) {
|
||||
$output .= $this->makeOptionTag($this->formatTimezone($timezone, null, $htmlEncode), $timezone, ($selected == $timezone));
|
||||
}
|
||||
|
||||
$output .= '</optgroup>';
|
||||
}
|
||||
|
||||
foreach ($this->loadContinents() as $continent => $mask) {
|
||||
$timezones = DateTimeZone::listIdentifiers($mask);
|
||||
$output .= '<optgroup label="' . $continent . '">';
|
||||
|
||||
foreach ($timezones as $timezone) {
|
||||
$output .= $this->makeOptionTag($this->formatTimezone($timezone, $continent, $htmlEncode), $timezone, ($selected == $timezone));
|
||||
}
|
||||
|
||||
$output .= '</optgroup>';
|
||||
}
|
||||
|
||||
$output .= '</select>';
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate select element without the optgroup tag.
|
||||
*
|
||||
* @param string $name The name of the select tag
|
||||
* @param null|string $selected The selected value
|
||||
* @param null|string $attrs The HTML attributes of select tag
|
||||
* @param bool $htmlEncode Use HTML entities for values of select tag
|
||||
* @return string
|
||||
*/
|
||||
protected function makeSelectTagWithoutGroup(string $name, ?string $selected = null, ?string $attrs = null, bool $htmlEncode = true)
|
||||
{
|
||||
$attrs = ! empty($attrs) ? ' ' . trim((string) $attrs) : '';
|
||||
$output = '<select name="' . (string) $name . '"' . $attrs . '>';
|
||||
|
||||
if ($this->includeGeneral()) {
|
||||
foreach ($this->generalTimezones as $timezone) {
|
||||
$output .= $this->makeOptionTag($this->formatTimezone($timezone, null, $htmlEncode), $timezone, ($selected == $timezone));
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->loadContinents() as $continent => $mask) {
|
||||
$timezones = DateTimeZone::listIdentifiers($mask);
|
||||
|
||||
foreach ($timezones as $timezone) {
|
||||
$output .= $this->makeOptionTag($this->formatTimezone($timezone, null, $htmlEncode), $timezone, ($selected == $timezone));
|
||||
}
|
||||
}
|
||||
|
||||
$output .= '</select>';
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the option HTML tag.
|
||||
*/
|
||||
protected function makeOptionTag(string $display, string $value, bool $selected = false): string
|
||||
{
|
||||
$attrs = (bool) $selected ? ' selected="selected"' : '';
|
||||
|
||||
return '<option value="' . $value . '"' . $attrs . '>' . $display . '</option>';
|
||||
}
|
||||
|
||||
/**
|
||||
* DetermineCheck if the general timezones is loaded in the returned result.
|
||||
*/
|
||||
protected function includeGeneral(): bool
|
||||
{
|
||||
return empty($this->groupsFilter) || in_array('General', $this->groupsFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load filtered continents.
|
||||
*/
|
||||
protected function loadContinents(): array
|
||||
{
|
||||
if (empty($this->groupsFilter)) {
|
||||
return $this->continents;
|
||||
}
|
||||
|
||||
return array_filter($this->continents, function ($key) {
|
||||
return in_array($key, $this->groupsFilter);
|
||||
}, ARRAY_FILTER_USE_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format to display timezones.
|
||||
*/
|
||||
protected function formatTimezone(string $timezone, ?string $cutOffContinent = null, bool $htmlEncode = true): string
|
||||
{
|
||||
$displayedTimezone = empty($cutOffContinent) ? $timezone : substr($timezone, strlen($cutOffContinent) + 1);
|
||||
$normalizedTimezone = $this->normalizeTimezone($displayedTimezone, $htmlEncode);
|
||||
|
||||
if (! $this->showOffset) {
|
||||
return $normalizedTimezone;
|
||||
}
|
||||
|
||||
$offset = $this->normalizeOffset($this->getOffset($timezone), $htmlEncode);
|
||||
$separator = $this->normalizeSeparator($htmlEncode);
|
||||
|
||||
return '(' . $this->offsetPrefix . $offset . ')' . $separator . $normalizedTimezone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize the offset.
|
||||
*/
|
||||
protected function normalizeOffset(string $offset, bool $htmlEncode = true): string
|
||||
{
|
||||
$search = ['-', '+'];
|
||||
$replace = $htmlEncode ? [' ' . self::MINUS . ' ', ' ' . self::PLUS . ' '] : [' - ', ' + '];
|
||||
|
||||
return str_replace($search, $replace, $offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize the timezone.
|
||||
*/
|
||||
protected function normalizeTimezone(string $timezone, bool $htmlEncode = true): string
|
||||
{
|
||||
$search = ['St_', '/', '_'];
|
||||
$replace = ['St. ', ' / ', ' '];
|
||||
|
||||
return str_replace($search, $replace, $timezone);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize the separator between the timezone and offset.
|
||||
*/
|
||||
protected function normalizeSeparator(bool $htmlEncode = true): string
|
||||
{
|
||||
return $htmlEncode ? str_repeat(self::WHITESPACE, 5) : ' ';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the timezone offset.
|
||||
*/
|
||||
protected function getOffset(string $timezone): string
|
||||
{
|
||||
$time = new DateTime('', new DateTimeZone($timezone));
|
||||
|
||||
return $time->format('P');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the difference of timezone to Coordinated Universal Time (UTC).
|
||||
*/
|
||||
protected function getUTCOffset(string $timezone): string
|
||||
{
|
||||
$dateTimeZone = new DateTimeZone($timezone);
|
||||
$utcTime = new DateTime('', new DateTimeZone('UTC'));
|
||||
$offset = $dateTimeZone->getOffset($utcTime);
|
||||
$format = gmdate('H:i', abs($offset));
|
||||
|
||||
return $offset >= 0 ? "+{$format}" : "-{$format}";
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user