*/ protected array $generalTimezones = [ 'GMT', 'UTC', ]; /** * All continents of the world. * * @var array */ 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 = ''; 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 = ''; return $output; } /** * Generate the option HTML tag. */ protected function makeOptionTag(string $display, string $value, bool $selected = false): string { $attrs = (bool) $selected ? ' selected="selected"' : ''; return ''; } /** * 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}"; } }