1 | <?php |
||||
2 | |||||
3 | namespace jessedp\Timezones; |
||||
4 | |||||
5 | use DateTime; |
||||
6 | use DateTimeZone; |
||||
7 | |||||
8 | /** |
||||
9 | * The Timezones class. |
||||
10 | * |
||||
11 | * @author jessedp <[email protected]> |
||||
12 | */ |
||||
13 | class Timezones |
||||
14 | { |
||||
15 | /** |
||||
16 | * Whitespace seperate. |
||||
17 | */ |
||||
18 | const WHITESPACE_SEP = ' '; |
||||
19 | |||||
20 | /** |
||||
21 | * Popular timezones. |
||||
22 | * |
||||
23 | * @var array |
||||
24 | */ |
||||
25 | public static $popularTimezones = [ |
||||
26 | 'GMT' => 'GMT timezone', |
||||
27 | 'UTC' => 'UTC timezone', |
||||
28 | ]; |
||||
29 | |||||
30 | /** |
||||
31 | * The supported regions. |
||||
32 | * |
||||
33 | * @var array |
||||
34 | */ |
||||
35 | public static $regions = [ |
||||
36 | 'Africa' => DateTimeZone::AFRICA, |
||||
37 | 'America' => DateTimeZone::AMERICA, |
||||
38 | 'Antarctica' => DateTimeZone::ANTARCTICA, |
||||
39 | 'Arctic' => DateTimeZone::ARCTIC, |
||||
40 | 'Asia' => DateTimeZone::ASIA, |
||||
41 | 'Atlantic' => DateTimeZone::ATLANTIC, |
||||
42 | 'Australia' => DateTimeZone::AUSTRALIA, |
||||
43 | 'Europe' => DateTimeZone::EUROPE, |
||||
44 | 'Indian' => DateTimeZone::INDIAN, |
||||
45 | 'Pacific' => DateTimeZone::PACIFIC, |
||||
46 | ]; |
||||
47 | |||||
48 | /** |
||||
49 | * Format to display timezones. |
||||
50 | * |
||||
51 | * @param string $timezone |
||||
52 | * @param string $region |
||||
53 | * |
||||
54 | * @return array |
||||
55 | */ |
||||
56 | public static function formatTimezone($timezone, $region) |
||||
57 | { |
||||
58 | $time = new DateTime(null, new DateTimeZone($timezone)); |
||||
59 | $str_offset = $time->format('P'); |
||||
60 | |||||
61 | //clean up the html display |
||||
62 | $signs = ['-', '+']; |
||||
63 | $signs_r = [' − ', ' + ']; |
||||
64 | $offset = str_replace($signs, $signs_r, $str_offset); |
||||
65 | |||||
66 | //do this for sorting later... |
||||
67 | $dbl_offset = (float) str_replace(':', '.', $str_offset); |
||||
68 | |||||
69 | //only strip things if we're bothering with regions |
||||
70 | if (!empty($region)) { |
||||
71 | $timezone = substr($timezone, strlen($region) + 1); |
||||
72 | } |
||||
73 | |||||
74 | $timezone = str_replace('St_', 'St. ', $timezone); |
||||
75 | $timezone = str_replace('_', ' ', $timezone); |
||||
76 | |||||
77 | $formatted = '(GMT/UTC'.$offset.')'.self::WHITESPACE_SEP.$timezone; |
||||
78 | |||||
79 | return ['offset'=>$dbl_offset, 'label' => $formatted]; |
||||
80 | } |
||||
81 | |||||
82 | /** |
||||
83 | * Create a timezone HTML select element for form. |
||||
84 | * |
||||
85 | * @param string $name the name/id to be used for the element |
||||
86 | * @param string $selected selected option, defaults to UTC |
||||
87 | * @param array $opts various options to set, including: |
||||
88 | * @subparam array $attr key=>value pairs of attributes to apply to the select element |
||||
89 | * @subparam bool $with_regions whether or not to do region grouping (default=false) |
||||
90 | * @subparam array $regions the regions to include, one or more of: Africa, America, Antarctica, |
||||
91 | * Arctic, Asia, Atlantic, Australia, Europe, Indian, Pacific |
||||
92 | * @return string |
||||
93 | **/ |
||||
94 | public static function create($name, $selected = 'UTC', $opts = []) |
||||
95 | { |
||||
96 | //handle a null selected |
||||
97 | if (empty($selected)) { |
||||
98 | $selected = 'UTC'; |
||||
99 | } |
||||
100 | |||||
101 | // Attributes for select element |
||||
102 | $attrSet = ''; |
||||
103 | if (isset($opts['attr']) && is_array($opts['attr']) && !empty($opts['attr'])) { |
||||
104 | $attr = $opts['attr']; |
||||
105 | |||||
106 | foreach ($attr as $attr_name => $attr_value) { |
||||
107 | $attrSet .= ' '.$attr_name.'="'.$attr_value.'"'; |
||||
108 | } |
||||
109 | } |
||||
110 | |||||
111 | //setup grouping |
||||
112 | $with_regions = false; |
||||
113 | if (isset($opts['with_regions']) && is_bool($opts['with_regions']) && $opts['with_regions']) { |
||||
114 | $with_regions = true; |
||||
115 | } |
||||
116 | |||||
117 | $limit_regions = []; |
||||
118 | //setup specfic regions - could be better and validate them here too, but, eh... |
||||
119 | if (isset($opts['regions']) && is_array($opts['regions']) && !empty($opts['regions'])) { |
||||
120 | $limit_regions = $opts['regions']; |
||||
121 | } |
||||
122 | |||||
123 | // start select element |
||||
124 | $listbox = '<select name="'.$name.'"'.$attrSet.'>'; |
||||
125 | |||||
126 | $regions = []; |
||||
127 | if ($with_regions) { |
||||
128 | $regions = self::$regions; |
||||
129 | } elseif (!empty($limit_regions)) { |
||||
130 | foreach ($limit_regions as $region) { |
||||
131 | $regions[$region] = self::$regions[$region]; |
||||
132 | } |
||||
133 | } else { |
||||
134 | $regions = ['All'=>DateTimeZone::ALL]; |
||||
135 | } |
||||
136 | // Add all timezones of the regions |
||||
137 | // depending on with_regions, this may be one or muiltiple loops |
||||
138 | foreach ($regions as $continent => $mask) { |
||||
139 | $opts = []; |
||||
140 | $timezones = DateTimeZone::listIdentifiers($mask); |
||||
141 | |||||
142 | if ($with_regions) { |
||||
143 | // start optgroup tag |
||||
144 | $listbox .= '<optgroup label="'.$continent.'">'; |
||||
145 | } else { |
||||
146 | //when including everything, don't let formatTimeZone truncate the label. |
||||
147 | $continent = null; |
||||
148 | } |
||||
149 | |||||
150 | // create option tags |
||||
151 | $offsets = []; |
||||
152 | $labels = []; |
||||
153 | foreach ($timezones as $timezone) { |
||||
154 | $opt = self::formatTimezone($timezone, $continent); |
||||
155 | $opt['tz'] = $timezone; |
||||
156 | $opt['selected'] = ($selected == $timezone) ? ' selected="selected"' : ''; |
||||
157 | $offsets[$timezone] = $opt['offset']; |
||||
158 | $labels[$timezone] = $opt['label']; |
||||
159 | $opts[] = $opt; |
||||
160 | } |
||||
161 | |||||
162 | array_multisort($offsets, SORT_DESC, |
||||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||||
163 | $labels, SORT_ASC, $opts |
||||
0 ignored issues
–
show
jessedp\Timezones\SORT_ASC cannot be passed to array_multisort() as the parameter $rest expects a reference.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
164 | ); |
||||
165 | |||||
166 | foreach ($opts as $opt) { |
||||
167 | $listbox .= '<option value="'.$opt['tz'].'"'.$opt['selected'].'>'; |
||||
168 | $listbox .= $opt['label']; |
||||
169 | $listbox .= '</option>'; |
||||
170 | } |
||||
171 | if ($with_regions) { |
||||
172 | // end optgroup tag |
||||
173 | $listbox .= '</optgroup>'; |
||||
174 | } |
||||
175 | } |
||||
176 | |||||
177 | // end select element |
||||
178 | $listbox .= '</select>'; |
||||
179 | |||||
180 | return $listbox; |
||||
181 | } |
||||
182 | |||||
183 | /** |
||||
184 | * Create a timezone array. |
||||
185 | * |
||||
186 | * @return mixed |
||||
187 | **/ |
||||
188 | public static function toArray() |
||||
189 | { |
||||
190 | $list = []; |
||||
191 | |||||
192 | // Add popular timezones to list |
||||
193 | foreach (self::$popularTimezones as $key => $value) { |
||||
194 | $list['General'][$key] = $value; |
||||
195 | } |
||||
196 | |||||
197 | // Add all timezone of the regions to return |
||||
198 | foreach (self::$regions as $continent => $mask) { |
||||
199 | $timezones = DateTimeZone::listIdentifiers($mask); |
||||
200 | foreach ($timezones as $timezone) { |
||||
201 | $list[$continent][$timezone] = self::formatTimezone($timezone, $continent); |
||||
202 | } |
||||
203 | } |
||||
204 | |||||
205 | return $list; |
||||
206 | } |
||||
207 | |||||
208 | /** |
||||
209 | * @param int $timestamp |
||||
210 | * @param string $timezone |
||||
211 | * @param string $format |
||||
212 | * |
||||
213 | * @return string |
||||
214 | */ |
||||
215 | public static function convertFromUTC($timestamp, $timezone, $format = 'Y-m-d H:i:s') |
||||
216 | { |
||||
217 | $date = new DateTime($timestamp, new DateTimeZone('UTC')); |
||||
218 | |||||
219 | $list = DateTimeZone::listIdentifiers(); |
||||
220 | if (!in_array($timezone, $list)) { |
||||
221 | $timezone = 'UTC'; |
||||
222 | } |
||||
223 | $date->setTimezone(new DateTimeZone($timezone)); |
||||
224 | |||||
225 | return $date->format($format); |
||||
226 | } |
||||
227 | |||||
228 | /** |
||||
229 | * Convert a timestamp to UTC. |
||||
230 | * @param int $timestamp |
||||
231 | * @param string $timezone |
||||
232 | * @param string $format |
||||
233 | * |
||||
234 | * @return string |
||||
235 | */ |
||||
236 | public static function convertToUTC($timestamp, $timezone, $format = 'Y-m-d H:i:s') |
||||
237 | { |
||||
238 | $list = DateTimeZone::listIdentifiers(); |
||||
239 | if (!in_array($timezone, $list)) { |
||||
240 | $timezone = 'UTC'; |
||||
241 | } |
||||
242 | |||||
243 | $date = new DateTime($timestamp, new DateTimeZone($timezone)); |
||||
244 | |||||
245 | $date->setTimezone(new DateTimeZone('UTC')); |
||||
246 | |||||
247 | return $date->format($format); |
||||
248 | } |
||||
249 | } |
||||
250 |