Page MenuHomePhorge

No OneTemporary

diff --git a/src/applications/settings/controller/PhabricatorSettingsTimezoneController.php b/src/applications/settings/controller/PhabricatorSettingsTimezoneController.php
index 6a0ba19d03..8ae2704161 100644
--- a/src/applications/settings/controller/PhabricatorSettingsTimezoneController.php
+++ b/src/applications/settings/controller/PhabricatorSettingsTimezoneController.php
@@ -1,151 +1,152 @@
<?php
final class PhabricatorSettingsTimezoneController
extends PhabricatorController {
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$client_offset = $request->getURIData('offset');
$client_offset = (int)$client_offset;
$timezones = DateTimeZone::listIdentifiers();
$now = new DateTime('@'.PhabricatorTime::getNow());
$options = array(
'ignore' => pht('Ignore Conflict'),
);
foreach ($timezones as $identifier) {
$zone = new DateTimeZone($identifier);
$offset = -($zone->getOffset($now) / 60);
if ($offset == $client_offset) {
- $options[$identifier] = $identifier;
+ $name = PhabricatorTime::getTimezoneDisplayName($identifier);
+ $options[$identifier] = $name;
}
}
$settings_help = pht(
'You can change your date and time preferences in Settings.');
$did_calibrate = false;
if ($request->isFormPost()) {
$timezone = $request->getStr('timezone');
$pref_ignore = PhabricatorTimezoneIgnoreOffsetSetting::SETTINGKEY;
$pref_timezone = PhabricatorTimezoneSetting::SETTINGKEY;
if ($timezone == 'ignore') {
$this->writeSettings(
array(
$pref_ignore => $client_offset,
));
return $this->newDialog()
->setTitle(pht('Conflict Ignored'))
->appendParagraph(
pht(
'The conflict between your browser and profile timezone '.
'settings will be ignored.'))
->appendParagraph($settings_help)
->addCancelButton('/', pht('Done'));
}
if (isset($options[$timezone])) {
$this->writeSettings(
array(
$pref_ignore => null,
$pref_timezone => $timezone,
));
$did_calibrate = true;
}
}
$server_offset = $viewer->getTimeZoneOffset();
if (($client_offset == $server_offset) || $did_calibrate) {
return $this->newDialog()
->setTitle(pht('Timezone Calibrated'))
->appendParagraph(
pht(
'Your browser timezone and profile timezone are now '.
'in agreement (%s).',
$this->formatOffset($client_offset)))
->appendParagraph($settings_help)
->addCancelButton('/', pht('Done'));
}
// If we have a guess at the timezone from the client, select it as the
// default.
$guess = $request->getStr('guess');
if (empty($options[$guess])) {
$guess = 'ignore';
}
$current_zone = $viewer->getTimezoneIdentifier();
$current_zone = phutil_tag('strong', array(), $current_zone);
$form = id(new AphrontFormView())
->appendChild(
id(new AphrontFormMarkupControl())
->setLabel(pht('Current Setting'))
->setValue($current_zone))
->appendChild(
id(new AphrontFormSelectControl())
->setName('timezone')
->setLabel(pht('New Setting'))
->setOptions($options)
->setValue($guess));
return $this->newDialog()
->setTitle(pht('Adjust Timezone'))
->setWidth(AphrontDialogView::WIDTH_FORM)
->appendParagraph(
pht(
'Your browser timezone (%s) differs from your profile timezone '.
'(%s). You can ignore this conflict or adjust your profile setting '.
'to match your client.',
$this->formatOffset($client_offset),
$this->formatOffset($server_offset)))
->appendForm($form)
->addCancelButton(pht('Cancel'))
->addSubmitButton(pht('Change Timezone'));
}
private function formatOffset($offset) {
// This controller works with client-side (Javascript) offsets, which have
// the opposite sign we might expect -- for example "UTC-3" is a positive
// offset. Invert the sign before rendering the offset.
$offset = -1 * $offset;
$hours = $offset / 60;
// Non-integer number of hours off UTC?
if ($offset % 60) {
$minutes = abs($offset % 60);
return pht('UTC%+d:%02d', $hours, $minutes);
} else {
return pht('UTC%+d', $hours);
}
}
private function writeSettings(array $map) {
$request = $this->getRequest();
$viewer = $this->getViewer();
$preferences = PhabricatorUserPreferences::loadUserPreferences($viewer);
$editor = id(new PhabricatorUserPreferencesEditor())
->setActor($viewer)
->setContentSourceFromRequest($request)
->setContinueOnNoEffect(true)
->setContinueOnMissingFields(true);
$xactions = array();
foreach ($map as $key => $value) {
$xactions[] = $preferences->newTransaction($key, $value);
}
$editor->applyTransactions($preferences, $xactions);
}
}
diff --git a/src/applications/settings/setting/PhabricatorTimezoneSetting.php b/src/applications/settings/setting/PhabricatorTimezoneSetting.php
index 52fce77428..207ad95f62 100644
--- a/src/applications/settings/setting/PhabricatorTimezoneSetting.php
+++ b/src/applications/settings/setting/PhabricatorTimezoneSetting.php
@@ -1,105 +1,112 @@
<?php
final class PhabricatorTimezoneSetting
extends PhabricatorOptionGroupSetting {
const SETTINGKEY = 'timezone';
public function getSettingName() {
return pht('Timezone');
}
public function getSettingPanelKey() {
return PhabricatorDateTimeSettingsPanel::PANELKEY;
}
protected function getSettingOrder() {
return 100;
}
protected function getControlInstructions() {
return pht('Select your local timezone.');
}
public function getSettingDefaultValue() {
return date_default_timezone_get();
}
public function assertValidValue($value) {
// NOTE: This isn't doing anything fancy, it's just a much faster
// validator than doing all the timezone calculations to build the full
// list of options.
if (!$value) {
return;
}
static $identifiers;
if ($identifiers === null) {
$identifiers = DateTimeZone::listIdentifiers();
$identifiers = array_fuse($identifiers);
}
if (isset($identifiers[$value])) {
return;
}
throw new Exception(
pht(
'Timezone "%s" is not a valid timezone identifier.',
$value));
}
protected function getSelectOptionGroups() {
$timezones = DateTimeZone::listIdentifiers();
$now = new DateTime('@'.PhabricatorTime::getNow());
$groups = array();
foreach ($timezones as $timezone) {
$zone = new DateTimeZone($timezone);
$offset = ($zone->getOffset($now) / 60);
$groups[$offset][] = $timezone;
}
ksort($groups);
$option_groups = array(
array(
'label' => pht('Default'),
'options' => array(),
),
);
foreach ($groups as $offset => $group) {
$hours = $offset / 60;
$minutes = abs($offset % 60);
if ($offset % 60) {
$label = pht('UTC%+d:%02d', $hours, $minutes);
} else {
$label = pht('UTC%+d', $hours);
}
sort($group);
+
+ $group_map = array();
+ foreach ($group as $identifier) {
+ $name = PhabricatorTime::getTimezoneDisplayName($identifier);
+ $group_map[$identifier] = $name;
+ }
+
$option_groups[] = array(
'label' => $label,
- 'options' => array_fuse($group),
+ 'options' => $group_map,
);
}
return $option_groups;
}
public function expandSettingTransaction($object, $xaction) {
// When the user changes their timezone, we also clear any ignored
// timezone offset.
return array(
$xaction,
$this->newSettingTransaction(
$object,
PhabricatorTimezoneIgnoreOffsetSetting::SETTINGKEY,
null),
);
}
}
diff --git a/src/infrastructure/time/PhabricatorTime.php b/src/infrastructure/time/PhabricatorTime.php
index b221285d13..67378ca8df 100644
--- a/src/infrastructure/time/PhabricatorTime.php
+++ b/src/infrastructure/time/PhabricatorTime.php
@@ -1,81 +1,91 @@
<?php
final class PhabricatorTime extends Phobject {
private static $stack = array();
private static $originalZone;
public static function pushTime($epoch, $timezone) {
if (empty(self::$stack)) {
self::$originalZone = date_default_timezone_get();
}
$ok = date_default_timezone_set($timezone);
if (!$ok) {
throw new Exception(pht("Invalid timezone '%s'!", $timezone));
}
self::$stack[] = array(
'epoch' => $epoch,
'timezone' => $timezone,
);
return new PhabricatorTimeGuard(last_key(self::$stack));
}
public static function popTime($key) {
if ($key !== last_key(self::$stack)) {
throw new Exception(
pht(
'%s with bad key.',
__METHOD__));
}
array_pop(self::$stack);
if (empty(self::$stack)) {
date_default_timezone_set(self::$originalZone);
} else {
$frame = end(self::$stack);
date_default_timezone_set($frame['timezone']);
}
}
public static function getNow() {
if (self::$stack) {
$frame = end(self::$stack);
return $frame['epoch'];
}
return time();
}
public static function parseLocalTime($time, PhabricatorUser $user) {
$old_zone = date_default_timezone_get();
date_default_timezone_set($user->getTimezoneIdentifier());
$timestamp = (int)strtotime($time, self::getNow());
if ($timestamp <= 0) {
$timestamp = null;
}
date_default_timezone_set($old_zone);
return $timestamp;
}
public static function getTodayMidnightDateTime($viewer) {
$timezone = new DateTimeZone($viewer->getTimezoneIdentifier());
$today = new DateTime('@'.time());
$today->setTimezone($timezone);
$year = $today->format('Y');
$month = $today->format('m');
$day = $today->format('d');
$today = new DateTime("{$year}-{$month}-{$day}", $timezone);
return $today;
}
public static function getDateTimeFromEpoch($epoch, PhabricatorUser $viewer) {
$datetime = new DateTime('@'.$epoch);
$datetime->setTimezone($viewer->getTimeZone());
return $datetime;
}
+ public static function getTimezoneDisplayName($raw_identifier) {
+
+ // Internal identifiers have names like "America/Los_Angeles", but this is
+ // just an implementation detail and we can render them in a more human
+ // readable format with spaces.
+ $name = str_replace('_', ' ', $raw_identifier);
+
+ return $name;
+ }
+
}

File Metadata

Mime Type
text/x-diff
Expires
Jan 19 2025, 19:28 (6 w, 2 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1127977
Default Alt Text
(10 KB)

Event Timeline