Correct Aphlict websocket URI construction after PHP8 compatibility changes
[phabricator.git] / src / applications / calendar / parser / data / PhutilCalendarAbsoluteDateTime.php
blob0129e6c05be7e43fe1f43ac76a9f45cdc2da0f9e
1 <?php
3 final class PhutilCalendarAbsoluteDateTime
4 extends PhutilCalendarDateTime {
6 private $year;
7 private $month;
8 private $day;
9 private $hour = 0;
10 private $minute = 0;
11 private $second = 0;
12 private $timezone;
14 public static function newFromISO8601($value, $timezone = 'UTC') {
15 $pattern =
16 '/^'.
17 '(?P<y>\d{4})(?P<m>\d{2})(?P<d>\d{2})'.
18 '(?:'.
19 'T(?P<h>\d{2})(?P<i>\d{2})(?P<s>\d{2})(?<z>Z)?'.
20 ')?'.
21 '\z/';
23 $matches = null;
24 $ok = preg_match($pattern, $value, $matches);
25 if (!$ok) {
26 throw new Exception(
27 pht(
28 'Expected ISO8601 datetime in the format "19990105T112233Z", '.
29 'found "%s".',
30 $value));
33 if (isset($matches['z'])) {
34 if ($timezone != 'UTC') {
35 throw new Exception(
36 pht(
37 'ISO8601 date ends in "Z" indicating UTC, but a timezone other '.
38 'than UTC ("%s") was specified.',
39 $timezone));
43 $datetime = id(new self())
44 ->setYear((int)$matches['y'])
45 ->setMonth((int)$matches['m'])
46 ->setDay((int)$matches['d'])
47 ->setTimezone($timezone);
49 if (isset($matches['h'])) {
50 $datetime
51 ->setHour((int)$matches['h'])
52 ->setMinute((int)$matches['i'])
53 ->setSecond((int)$matches['s']);
54 } else {
55 $datetime
56 ->setIsAllDay(true);
59 return $datetime;
62 public static function newFromEpoch($epoch, $timezone = 'UTC') {
63 $date = new DateTime('@'.$epoch);
65 $zone = new DateTimeZone($timezone);
66 $date->setTimezone($zone);
68 return id(new self())
69 ->setYear((int)$date->format('Y'))
70 ->setMonth((int)$date->format('m'))
71 ->setDay((int)$date->format('d'))
72 ->setHour((int)$date->format('H'))
73 ->setMinute((int)$date->format('i'))
74 ->setSecond((int)$date->format('s'))
75 ->setTimezone($timezone);
78 public static function newFromDictionary(array $dict) {
79 static $keys;
80 if ($keys === null) {
81 $keys = array_fuse(
82 array(
83 'kind',
84 'year',
85 'month',
86 'day',
87 'hour',
88 'minute',
89 'second',
90 'timezone',
91 'isAllDay',
92 ));
95 foreach ($dict as $key => $value) {
96 if (!isset($keys[$key])) {
97 throw new Exception(
98 pht(
99 'Unexpected key "%s" in datetime dictionary, expected keys: %s.',
100 $key,
101 implode(', ', array_keys($keys))));
105 if (idx($dict, 'kind') !== 'absolute') {
106 throw new Exception(
107 pht(
108 'Expected key "%s" with value "%s" in datetime dictionary.',
109 'kind',
110 'absolute'));
113 if (!isset($dict['year'])) {
114 throw new Exception(
115 pht(
116 'Expected key "%s" in datetime dictionary.',
117 'year'));
120 $datetime = id(new self())
121 ->setYear(idx($dict, 'year'))
122 ->setMonth(idx($dict, 'month', 1))
123 ->setDay(idx($dict, 'day', 1))
124 ->setHour(idx($dict, 'hour', 0))
125 ->setMinute(idx($dict, 'minute', 0))
126 ->setSecond(idx($dict, 'second', 0))
127 ->setTimezone(idx($dict, 'timezone'))
128 ->setIsAllDay((bool)idx($dict, 'isAllDay', false));
130 return $datetime;
133 public function newRelativeDateTime($duration) {
134 if (is_string($duration)) {
135 $duration = PhutilCalendarDuration::newFromISO8601($duration);
138 if (!($duration instanceof PhutilCalendarDuration)) {
139 throw new Exception(
140 pht(
141 'Expected "PhutilCalendarDuration" object or ISO8601 duration '.
142 'string.'));
145 return id(new PhutilCalendarRelativeDateTime())
146 ->setOrigin($this)
147 ->setDuration($duration);
150 public function toDictionary() {
151 return array(
152 'kind' => 'absolute',
153 'year' => (int)$this->getYear(),
154 'month' => (int)$this->getMonth(),
155 'day' => (int)$this->getDay(),
156 'hour' => (int)$this->getHour(),
157 'minute' => (int)$this->getMinute(),
158 'second' => (int)$this->getSecond(),
159 'timezone' => $this->getTimezone(),
160 'isAllDay' => (bool)$this->getIsAllDay(),
164 public function setYear($year) {
165 $this->year = $year;
166 return $this;
169 public function getYear() {
170 return $this->year;
173 public function setMonth($month) {
174 $this->month = $month;
175 return $this;
178 public function getMonth() {
179 return $this->month;
182 public function setDay($day) {
183 $this->day = $day;
184 return $this;
187 public function getDay() {
188 return $this->day;
191 public function setHour($hour) {
192 $this->hour = $hour;
193 return $this;
196 public function getHour() {
197 return $this->hour;
200 public function setMinute($minute) {
201 $this->minute = $minute;
202 return $this;
205 public function getMinute() {
206 return $this->minute;
209 public function setSecond($second) {
210 $this->second = $second;
211 return $this;
214 public function getSecond() {
215 return $this->second;
218 public function setTimezone($timezone) {
219 $this->timezone = $timezone;
220 return $this;
223 public function getTimezone() {
224 return $this->timezone;
227 private function getEffectiveTimezone() {
228 $date_timezone = $this->getTimezone();
229 $viewer_timezone = $this->getViewerTimezone();
231 // Because all-day events are always "floating", the effective timezone
232 // is the viewer timezone if it is available. Otherwise, we'll return a
233 // DateTime object with the correct values, but it will be incorrectly
234 // adjusted forward or backward to the viewer's zone later.
236 $zones = array();
237 if ($this->getIsAllDay()) {
238 $zones[] = $viewer_timezone;
239 $zones[] = $date_timezone;
240 } else {
241 $zones[] = $date_timezone;
242 $zones[] = $viewer_timezone;
244 $zones = array_filter($zones);
246 if (!$zones) {
247 throw new Exception(
248 pht(
249 'Datetime has no timezone or viewer timezone.'));
252 return head($zones);
255 public function newPHPDateTimeZone() {
256 $zone = $this->getEffectiveTimezone();
257 return new DateTimeZone($zone);
260 public function newPHPDateTime() {
261 $zone = $this->newPHPDateTimeZone();
263 $y = $this->getYear();
264 $m = $this->getMonth();
265 $d = $this->getDay();
267 if ($this->getIsAllDay()) {
268 $h = 0;
269 $i = 0;
270 $s = 0;
271 } else {
272 $h = $this->getHour();
273 $i = $this->getMinute();
274 $s = $this->getSecond();
277 $format = sprintf('%04d-%02d-%02d %02d:%02d:%02d', $y, $m, $d, $h, $i, $s);
279 return new DateTime($format, $zone);
283 public function newAbsoluteDateTime() {
284 return clone $this;