|
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, |
|
|
|
|
|
|
163
|
|
|
$labels, SORT_ASC, $opts |
|
|
|
|
|
|
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
|
|
|
|