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
|
|
|
|