Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2894153
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Advanced/Developer...
View Handle
View Hovercard
Size
10 KB
Referenced Files
None
Subscribers
None
View Options
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
Details
Attached
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)
Attached To
Mode
rP Phorge
Attached
Detach File
Event Timeline
Log In to Comment