Something went wrong. Try again.
@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
Something went wrong. Try again.
PHP
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385<?php
final class AphrontFormDateControlValue extends Phobject {
private $valueDate; private $valueTime; private $valueEnabled;
private $viewer; private $zone; private $optional;
public function getValueDate() { return $this->valueDate; }
public function getValueTime() { return $this->valueTime; }
public function isValid() { if ($this->isDisabled()) { return true; } return ($this->getEpoch() !== null); }
public function isEmpty() { if ($this->valueDate) { return false; }
if ($this->valueTime) { return false; }
return true; }
public function isDisabled() { return ($this->optional && !$this->valueEnabled); }
public function setEnabled($enabled) { $this->valueEnabled = $enabled; return $this; }
public function setOptional($optional) { $this->optional = $optional; return $this; }
public function getOptional() { return $this->optional; }
public function getViewer() { return $this->viewer; }
public static function newFromRequest(AphrontRequest $request, $key) { $value = new AphrontFormDateControlValue(); $value->viewer = $request->getViewer();
$date = $request->getStr($key.'_d', ''); $time = $request->getStr($key.'_t', '');
// If we have the individual parts, we read them preferentially. If we do // not, try to read the key as a raw value. This makes it so that HTTP // prefilling is overwritten by the control value if the user changes it. if (!strlen($date) && !strlen($time)) { $date = $request->getStr($key); $time = null; }
$value->valueDate = $date; $value->valueTime = $time;
$formatted = $value->getFormattedDateFromDate( $value->valueDate, $value->valueTime);
if ($formatted) { list($value->valueDate, $value->valueTime) = $formatted; }
$value->valueEnabled = $request->getStr($key.'_e'); return $value; }
public static function newFromEpoch(PhabricatorUser $viewer, $epoch) { $value = new AphrontFormDateControlValue(); $value->viewer = $viewer;
if (!$epoch) { return $value; }
$readable = $value->formatTime($epoch, 'Y!m!d!g:i A'); $readable = explode('!', $readable, 4);
$year = $readable[0]; $month = $readable[1]; $day = $readable[2]; $time = $readable[3];
list($value->valueDate, $value->valueTime) = $value->getFormattedDateFromParts( $year, $month, $day, $time);
return $value; }
public static function newFromDictionary( PhabricatorUser $viewer, array $dictionary) { $value = new AphrontFormDateControlValue(); $value->viewer = $viewer;
$value->valueDate = idx($dictionary, 'd'); $value->valueTime = idx($dictionary, 't');
$formatted = $value->getFormattedDateFromDate( $value->valueDate, $value->valueTime);
if ($formatted) { list($value->valueDate, $value->valueTime) = $formatted; }
$value->valueEnabled = idx($dictionary, 'e');
return $value; }
public static function newFromWild(PhabricatorUser $viewer, $wild) { if (is_array($wild)) { return self::newFromDictionary($viewer, $wild); } else if (is_numeric($wild)) { return self::newFromEpoch($viewer, $wild); } else { throw new Exception( pht( 'Unable to construct a date value from value of type "%s".', gettype($wild))); } }
public function getDictionary() { return array( 'd' => $this->valueDate, 't' => $this->valueTime, 'e' => $this->valueEnabled, ); }
public function getValueAsFormat($format) { return phabricator_format_local_time( $this->getEpoch(), $this->viewer, $format); }
private function formatTime($epoch, $format) { $date = phorge_localize_time($epoch, $this->viewer); if (!$date) { return ''; } // Call DateTime->format directly (bypassing PhutilTranslator) // so that getFormattedDateFromParts below can decode the parts // back into a DateTime return $date->format($format); }
public function getEpoch() { if ($this->isDisabled()) { return null; }
$datetime = $this->newDateTime($this->valueDate, $this->valueTime); if (!$datetime) { return null; }
return (int)$datetime->format('U'); }
private function getTimeFormat() { $viewer = $this->getViewer(); $time_key = PhabricatorTimeFormatSetting::SETTINGKEY; return $viewer->getUserSetting($time_key); }
private function getDateFormat() { $viewer = $this->getViewer(); $date_key = PhabricatorDateFormatSetting::SETTINGKEY; return $viewer->getUserSetting($date_key); }
private function getFormattedDateFromDate($date, $time) { $datetime = $this->newDateTime($date, $time); if (!$datetime) { return null; }
return array( $datetime->format($this->getDateFormat()), $datetime->format($this->getTimeFormat()), ); }
/** * Create a DateTime object including timezone * @param string $date Date, like "2024-08-20" or "2024-07-1" or such * @param string|null $time Time, like "12:00 AM" or such * @return DateTime|null */ private function newDateTime($date, $time) { $date = $this->getStandardDateFormat($date); $time = $this->getStandardTimeFormat($time);
try { // We need to provide the timezone in the constructor, and also set it // explicitly. If the date is an epoch timestamp, the timezone in the // constructor is ignored. If the date is not an epoch timestamp, it is // used to parse the date. $zone = $this->getTimezone(); $datetime = new DateTime("{$date} {$time}", $zone); $datetime->setTimezone($zone); } catch (Exception $ex) { return null; }
return $datetime; }
public function newPhutilDateTime() { $datetime = $this->getDateTime(); if (!$datetime) { return null; }
$all_day = !strlen($this->valueTime); $zone_identifier = $this->viewer->getTimezoneIdentifier();
$result = id(new PhutilCalendarAbsoluteDateTime()) ->setYear((int)$datetime->format('Y')) ->setMonth((int)$datetime->format('m')) ->setDay((int)$datetime->format('d')) ->setHour((int)$datetime->format('G')) ->setMinute((int)$datetime->format('i')) ->setSecond((int)$datetime->format('s')) ->setTimezone($zone_identifier);
if ($all_day) { $result->setIsAllDay(true); }
return $result; }
private function getFormattedDateFromParts( $year, $month, $day, $time) {
$zone = $this->getTimezone(); $date_time = new DateTime("{$year}-{$month}-{$day} {$time}", $zone);
return array( $date_time->format($this->getDateFormat()), $date_time->format($this->getTimeFormat()), ); }
private function getFormatSeparator() { $format = $this->getDateFormat(); switch ($format) { case 'n/j/Y': return '/'; default: return '-'; } }
/** * @return DateTime|null */ public function getDateTime() { return $this->newDateTime($this->valueDate, $this->valueTime); }
/** * @return DateTimeZone */ private function getTimezone() { if ($this->zone) { return $this->zone; }
$viewer_zone = $this->viewer->getTimezoneIdentifier(); $this->zone = new DateTimeZone($viewer_zone); return $this->zone; }
private function getStandardDateFormat($date) { // no value entered into the field at all if (!$date) { return null; } $colloquial = array( 'newyear' => 'January 1', 'valentine' => 'February 14', 'pi' => 'March 14', 'christma' => 'December 25', );
// Lowercase the input, then remove punctuation, a "day" suffix, and an // "s" if one is present. This allows all of these to match. This allows // variations like "New Year's Day" and "New Year" to both match. $normalized = phutil_utf8_strtolower($date); $normalized = preg_replace('/[^a-z]/', '', $normalized); $normalized = preg_replace('/day\z/', '', $normalized); $normalized = preg_replace('/s\z/', '', $normalized);
if ($normalized !== null && isset($colloquial[$normalized])) { return $colloquial[$normalized]; }
// If this looks like an epoch timestamp, prefix it with "@" so that // DateTime() reads it as one. Assume small numbers are a "Ymd" digit // string instead of an epoch timestamp for a time in 1970. if (ctype_digit($date) && ($date > 30000000)) { $date = '@'.$date; }
$separator = $this->getFormatSeparator(); $parts = preg_split('@[,./:-]@', $date); return implode($separator, $parts); }
private function getStandardTimeFormat($time) { $colloquial = array( 'crack of dawn' => '5:00 AM', 'dawn' => '6:00 AM', 'early' => '7:00 AM', 'morning' => '8:00 AM', 'elevenses' => '11:00 AM', 'morning tea' => '11:00 AM', 'noon' => '12:00 PM', 'high noon' => '12:00 PM', 'lunch' => '12:00 PM', 'afternoon' => '2:00 PM', 'tea time' => '3:00 PM', 'evening' => '7:00 PM', 'late' => '11:00 PM', 'witching hour' => '12:00 AM', 'midnight' => '12:00 AM', );
$normalized = phutil_utf8_strtolower($time); if (isset($colloquial[$normalized])) { $time = $colloquial[$normalized]; } // Convert localized times to English $am = pht('AM'); if ($am !== 'AM') { $time = preg_replace('/'.preg_quote($am).'$/', 'AM', $time); } $pm = pht('PM'); if ($pm !== 'PM') { $time = preg_replace('/'.preg_quote($pm).'$/', 'PM', $time); } return $time; }
}