Page MenuHomePhabricator

D16653.diff
No OneTemporary

D16653.diff

diff --git a/src/parser/calendar/data/PhutilCalendarAbsoluteDateTime.php b/src/parser/calendar/data/PhutilCalendarAbsoluteDateTime.php
--- a/src/parser/calendar/data/PhutilCalendarAbsoluteDateTime.php
+++ b/src/parser/calendar/data/PhutilCalendarAbsoluteDateTime.php
@@ -75,6 +75,92 @@
->setTimezone($timezone);
}
+ public static function newFromDictionary(array $dict) {
+ static $keys;
+ if ($keys === null) {
+ $keys = array_fuse(
+ array(
+ 'kind',
+ 'year',
+ 'month',
+ 'day',
+ 'hour',
+ 'minute',
+ 'second',
+ 'timezone',
+ 'isAllDay',
+ ));
+ }
+
+ foreach ($dict as $key => $value) {
+ if (!isset($keys[$key])) {
+ throw new Exception(
+ pht(
+ 'Unexpected key "%s" in datetime dictionary, expected keys: %s.',
+ $key,
+ implode(', ', array_keys($keys))));
+ }
+ }
+
+ if (idx($dict, 'kind') !== 'absolute') {
+ throw new Exception(
+ pht(
+ 'Expected key "%s" with value "%s" in datetime dictionary.',
+ 'kind',
+ 'absolute'));
+ }
+
+ if (!isset($dict['year'])) {
+ throw new Exception(
+ pht(
+ 'Expected key "%s" in datetime dictionary.',
+ 'year'));
+ }
+
+ $datetime = id(new self())
+ ->setYear(idx($dict, 'year'))
+ ->setMonth(idx($dict, 'month', 1))
+ ->setDay(idx($dict, 'day', 1))
+ ->setHour(idx($dict, 'hour', 0))
+ ->setMinute(idx($dict, 'minute', 0))
+ ->setSecond(idx($dict, 'second', 0))
+ ->setTimezone(idx($dict, 'timezone'))
+ ->setIsAllDay(idx($dict, 'isAllDay', false));
+
+ return $datetime;
+ }
+
+ public function newRelativeDateTime($duration) {
+ if (is_string($duration)) {
+ $duration = PhutilCalendarDuration::newFromISO8601($duration);
+ }
+
+ if (!($duration instanceof PhutilCalendarDuration)) {
+ throw new Exception(
+ pht(
+ 'Expected "PhutilCalendarDuration" object or ISO8601 duration '.
+ 'string.'));
+ }
+
+ return id(new PhutilCalendarRelativeDateTime())
+ ->setOrigin($this)
+ ->setDuration($duration);
+ }
+
+ public function toDictionary() {
+ return array(
+ 'kind' => 'absolute',
+ 'year' => $this->getYear(),
+ 'month' => $this->getMonth(),
+ 'day' => $this->getDay(),
+ 'hour' => $this->getHour(),
+ 'minute' => $this->getMinute(),
+ 'second' => $this->getSecond(),
+ 'timezone' => $this->getTimezone(),
+ 'isAllDay' => $this->getIsAllDay(),
+ );
+ }
+
public function setYear($year) {
$this->year = $year;
return $this;
@@ -154,12 +240,12 @@
'Datetime has no timezone or viewer timezone.'));
}
- protected function newPHPDateTimeZone() {
+ public function newPHPDateTimeZone() {
$zone = $this->getEffectiveTimezone();
return new DateTimeZone($zone);
}
- protected function newPHPDateTime() {
+ public function newPHPDateTime() {
$zone = $this->newPHPDateTimeZone();
$y = $this->getYear();
diff --git a/src/parser/calendar/data/PhutilCalendarDateTime.php b/src/parser/calendar/data/PhutilCalendarDateTime.php
--- a/src/parser/calendar/data/PhutilCalendarDateTime.php
+++ b/src/parser/calendar/data/PhutilCalendarDateTime.php
@@ -31,16 +31,31 @@
public function getISO8601() {
$datetime = $this->newPHPDateTime();
- $datetime->setTimezone(new DateTimeZone('UTC'));
if ($this->getIsAllDay()) {
return $datetime->format('Ymd');
- } else {
+ } else if ($this->getTimezone()) {
+ // With a timezone, the event occurs at a specific second universally.
+ // We return the UTC representation of that point in time.
+ $datetime->setTimezone(new DateTimeZone('UTC'));
return $datetime->format('Ymd\\THis\\Z');
+ } else {
+ // With no timezone, events are "floating" and occur at local time.
+ // We return a representation without the "Z".
+ return $datetime->format('Ymd\\THis');
}
}
- abstract protected function newPHPDateTimeZone();
- abstract protected function newPHPDateTime();
+ public function newAbsoluteDateTime() {
+ $epoch = $this->getEpoch();
+ $timezone = $this->getTimezone();
+ return PhutilCalendarAbsoluteDateTime::newFromEpoch($epoch, $timezone)
+ ->setIsAllDay($this->getIsAllDay())
+ ->setViewerTimezone($this->getViewerTimezone());
+ }
+
+ abstract public function newPHPDateTimeZone();
+ abstract public function newPHPDateTime();
+ abstract public function getTimezone();
}
diff --git a/src/parser/calendar/data/PhutilCalendarDuration.php b/src/parser/calendar/data/PhutilCalendarDuration.php
--- a/src/parser/calendar/data/PhutilCalendarDuration.php
+++ b/src/parser/calendar/data/PhutilCalendarDuration.php
@@ -3,12 +3,127 @@
final class PhutilCalendarDuration extends Phobject {
private $isNegative = false;
- private $days = 0;
private $weeks = 0;
+ private $days = 0;
private $hours = 0;
private $minutes = 0;
private $seconds = 0;
+ public static function newFromDictionary(array $dict) {
+ static $keys;
+ if ($keys === null) {
+ $keys = array_fuse(
+ array(
+ 'isNegative',
+ 'weeks',
+ 'days',
+ 'hours',
+ 'minutes',
+ 'seconds',
+ ));
+ }
+
+ foreach ($dict as $key => $value) {
+ if (!isset($keys[$key])) {
+ throw new Exception(
+ pht(
+ 'Unexpected key "%s" in duration dictionary, expected keys: %s.',
+ $key,
+ implode(', ', array_keys($keys))));
+ }
+ }
+
+ $duration = id(new self())
+ ->setIsNegative(idx($dict, 'isNegative', false))
+ ->setWeeks(idx($dict, 'weeks', 0))
+ ->setDays(idx($dict, 'days', 0))
+ ->setHours(idx($dict, 'hours', 0))
+ ->setMinutes(idx($dict, 'minutes', 0))
+ ->setSeconds(idx($dict, 'seconds', 0));
+
+ return $duration;
+ }
+
+ public function toDictionary() {
+ return array(
+ 'isNegative' => $this->getIsNegative(),
+ 'weeks' => $this->getWeeks(),
+ 'days' => $this->getDays(),
+ 'hours' => $this->getHours(),
+ 'minutes' => $this->getMinutes(),
+ 'seconds' => $this->getSeconds(),
+ );
+ }
+
+ public static function newFromISO8601($value) {
+ $pattern =
+ '/^'.
+ '(?P<sign>[+-])?'.
+ 'P'.
+ '(?:'.
+ '(?P<W>\d+)W'.
+ '|'.
+ '(?:(?:(?P<D>\d+)D)?'.
+ '(?:T(?:(?P<H>\d+)H)?(?:(?P<M>\d+)M)?(?:(?P<S>\d+)S)?)?'.
+ ')'.
+ ')'.
+ '\z/';
+
+ $matches = null;
+ $ok = preg_match($pattern, $value, $matches);
+ if (!$ok) {
+ throw new Exception(
+ pht(
+ 'Expected ISO8601 duration in the format "P12DT3H4M5S", found '.
+ '"%s".',
+ $value));
+ }
+
+ $is_negative = (idx($matches, 'sign') == '-');
+
+ return id(new self())
+ ->setIsNegative($is_negative)
+ ->setWeeks((int)idx($matches, 'W', 0))
+ ->setDays((int)idx($matches, 'D', 0))
+ ->setHours((int)idx($matches, 'H', 0))
+ ->setMinutes((int)idx($matches, 'M', 0))
+ ->setSeconds((int)idx($matches, 'S', 0));
+ }
+
+ public function toISO8601() {
+ $parts = array();
+ $parts[] = 'P';
+
+ $weeks = $this->getWeeks();
+ if ($weeks) {
+ $parts[] = $weeks.'W';
+ } else {
+ $days = $this->getDays();
+ if ($days) {
+ $parts[] = $days.'D';
+ }
+
+ $parts[] = 'T';
+
+ $hours = $this->getHours();
+ if ($hours) {
+ $parts[] = $hours.'H';
+ }
+
+ $minutes = $this->getMinutes();
+ if ($minutes) {
+ $parts[] = $minutes.'M';
+ }
+
+ $seconds = $this->getSeconds();
+ if ($seconds) {
+ $parts[] = $seconds.'S';
+ }
+ }
+
+ return implode('', $parts);
+ }
+
public function setIsNegative($is_negative) {
$this->isNegative = $is_negative;
return $this;
@@ -18,22 +133,22 @@
return $this->isNegative;
}
- public function setDays($days) {
- $this->days = $days;
+ public function setWeeks($weeks) {
+ $this->weeks = $weeks;
return $this;
}
- public function getDays() {
- return $this->days;
+ public function getWeeks() {
+ return $this->weeks;
}
- public function setWeeks($weeks) {
- $this->weeks = $weeks;
+ public function setDays($days) {
+ $this->days = $days;
return $this;
}
- public function getWeeks() {
- return $this->weeks;
+ public function getDays() {
+ return $this->days;
}
public function setHours($hours) {
diff --git a/src/parser/calendar/data/PhutilCalendarProxyDateTime.php b/src/parser/calendar/data/PhutilCalendarProxyDateTime.php
--- a/src/parser/calendar/data/PhutilCalendarProxyDateTime.php
+++ b/src/parser/calendar/data/PhutilCalendarProxyDateTime.php
@@ -32,12 +32,16 @@
return $this->getProxy()->getIsAllDay();
}
- protected function newPHPDateTimezone() {
+ public function newPHPDateTimezone() {
return $this->getProxy()->newPHPDateTimezone();
}
- protected function newPHPDateTime() {
+ public function newPHPDateTime() {
return $this->getProxy()->newPHPDateTime();
}
+ public function getTimezone() {
+ return $this->getProxy()->getTimezone();
+ }
+
}
diff --git a/src/parser/calendar/data/PhutilCalendarRecurrenceRule.php b/src/parser/calendar/data/PhutilCalendarRecurrenceRule.php
--- a/src/parser/calendar/data/PhutilCalendarRecurrenceRule.php
+++ b/src/parser/calendar/data/PhutilCalendarRecurrenceRule.php
@@ -703,6 +703,7 @@
}
$result = id(new PhutilCalendarAbsoluteDateTime())
+ ->setTimezone($this->getStartDateTime()->getTimezone())
->setViewerTimezone($this->getViewerTimezone())
->setYear($this->stateYear)
->setMonth($this->stateMonth)
diff --git a/src/parser/calendar/data/PhutilCalendarRelativeDateTime.php b/src/parser/calendar/data/PhutilCalendarRelativeDateTime.php
--- a/src/parser/calendar/data/PhutilCalendarRelativeDateTime.php
+++ b/src/parser/calendar/data/PhutilCalendarRelativeDateTime.php
@@ -22,7 +22,7 @@
return $this->duration;
}
- protected function newPHPDateTime() {
+ public function newPHPDateTime() {
$datetime = parent::newPHPDateTime();
$duration = $this->getDuration();
diff --git a/src/parser/calendar/ics/PhutilICSParser.php b/src/parser/calendar/ics/PhutilICSParser.php
--- a/src/parser/calendar/ics/PhutilICSParser.php
+++ b/src/parser/calendar/ics/PhutilICSParser.php
@@ -746,40 +746,16 @@
$value = head($value);
- $pattern =
- '/^'.
- '(?P<sign>[+-])?'.
- 'P'.
- '(?:'.
- '(?P<W>\d+)W'.
- '|'.
- '(?:(?:(?P<D>\d+)D)?'.
- '(?:T(?:(?P<H>\d+)H)?(?:(?P<M>\d+)M)?(?:(?P<S>\d+)S)?)?'.
- ')'.
- ')'.
- '\z/';
-
- $matches = null;
- $ok = preg_match($pattern, $value, $matches);
- if (!$ok) {
+ try {
+ $duration = PhutilCalendarDuration::newFromISO8601($value);
+ } catch (Exception $ex) {
$this->raiseParseFailure(
self::PARSE_BAD_DURATION,
pht(
- 'Expected DURATION in the format "P12DT3H4M5S", found '.
- '"%s".',
- $value));
+ 'Invalid DURATION: %s',
+ $ex->getMessage()));
}
- $is_negative = (idx($matches, 'sign') == '-');
-
- $duration = id(new PhutilCalendarDuration())
- ->setIsNegative($is_negative)
- ->setWeeks((int)idx($matches, 'W', 0))
- ->setDays((int)idx($matches, 'D', 0))
- ->setHours((int)idx($matches, 'H', 0))
- ->setMinutes((int)idx($matches, 'M', 0))
- ->setSeconds((int)idx($matches, 'S', 0));
-
return $duration;
}

File Metadata

Mime Type
text/plain
Expires
Sun, Nov 24, 7:12 PM (14 h, 13 m)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6783748
Default Alt Text
D16653.diff (11 KB)

Event Timeline