aboutsummaryrefslogtreecommitdiffstats
path: root/phpBB/includes
diff options
context:
space:
mode:
Diffstat (limited to 'phpBB/includes')
-rw-r--r--phpBB/includes/datetime.php173
-rw-r--r--phpBB/includes/functions.php100
-rw-r--r--phpBB/includes/functions_profile_fields.php9
-rw-r--r--phpBB/includes/user.php94
4 files changed, 302 insertions, 74 deletions
diff --git a/phpBB/includes/datetime.php b/phpBB/includes/datetime.php
new file mode 100644
index 0000000000..15e3c8b0b7
--- /dev/null
+++ b/phpBB/includes/datetime.php
@@ -0,0 +1,173 @@
+<?php
+/**
+*
+* @package phpBB3
+* @version $Id$
+* @copyright (c) 2010 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*/
+
+/**
+* phpBB custom extensions to the PHP DateTime class
+* This handles the relative formats phpBB employs
+*/
+class phpbb_datetime extends DateTime
+{
+ /**
+ * String used to wrap the date segment which should be replaced by today/tomorrow/yesterday
+ */
+ const RELATIVE_WRAPPER = '|';
+
+ /**
+ * @var user User who is the context for this DateTime instance
+ */
+ protected $_user;
+
+ /**
+ * @var array Date formats are preprocessed by phpBB, to save constant recalculation they are cached.
+ */
+ static protected $format_cache = array();
+
+ /**
+ * Constructs a new instance of phpbb_datetime, expanded to include an argument to inject
+ * the user context and modify the timezone to the users selected timezone if one is not set.
+ *
+ * @param string $time String in a format accepted by strtotime().
+ * @param DateTimeZone $timezone Time zone of the time.
+ * @param user User object for context.
+ */
+ public function __construct($time = 'now', DateTimeZone $timezone = null, $user = null)
+ {
+ $this->_user = $user ? $user : $GLOBALS['user'];
+
+ $timezone = (!$timezone && $this->_user->tz instanceof DateTimeZone) ? $this->_user->tz : $timezone;
+
+ parent::__construct($time, $timezone);
+ }
+
+ /**
+ * Returns a UNIX timestamp representation of the date time.
+ *
+ * @internal This method is for backwards compatibility with 5.2, hence why it doesn't use our method naming standards.
+ * @return int UNIX timestamp
+ */
+ public function getTimestamp()
+ {
+ static $compat;
+
+ if (!isset($compat))
+ {
+ $compat = !method_exists('DateTime', 'getTimestamp');
+ }
+
+ return !$compat ? parent::getTimestamp() : (int) parent::format('U');
+ }
+
+ /**
+ * Formats the current date time into the specified format
+ *
+ * @param string $format Optional format to use for output, defaults to users chosen format
+ * @param boolean $force_absolute Force output of a non relative date
+ * @return string Formatted date time
+ */
+ public function format($format = '', $force_absolute = false)
+ {
+ $format = $format ? $format : $this->_user->date_format;
+ $format = self::_format_cache($format, $this->_user);
+ $relative = ($format['is_short'] && !$force_absolute);
+ $now = new self('now', $this->_user->tz, $this->_user);
+
+ $timestamp = $this->getTimestamp();
+ $now_ts = $now->getTimeStamp();
+
+ $delta = $now_ts - $timestamp;
+
+ if ($relative)
+ {
+ // Check the delta is less than or equal to 1 hour
+ // and the delta not more than a minute in the past
+ // and the delta is either greater than -5 seconds or timestamp and current time are of the same minute (they must be in the same hour already)
+ // finally check that relative dates are supported by the language pack
+ if ($delta <= 3600 && $delta > -60 && ($delta >= -5 || (($now_ts / 60) % 60) == (($timestamp / 60) % 60)) && isset($this->_user->lang['datetime']['AGO']))
+ {
+ return $this->_user->lang(array('datetime', 'AGO'), max(0, (int) floor($delta / 60)));
+ }
+ else
+ {
+ $midnight = clone $now;
+ $midnight->setTime(0, 0, 0);
+
+ $midnight = $midnight->getTimestamp();
+
+ $day = false;
+
+ if ($timestamp > $midnight + 86400)
+ {
+ $day = 'TOMORROW';
+ }
+ else if ($timestamp > $midnight)
+ {
+ $day = 'TODAY';
+ }
+ else if ($timestamp > $midnight - 86400)
+ {
+ $day = 'YESTERDAY';
+ }
+
+ if ($day !== false)
+ {
+ // Format using the short formatting and finally swap out the relative token placeholder with the correct value
+ return str_replace(self::RELATIVE_WRAPPER . self::RELATIVE_WRAPPER, $this->_user->lang['datetime'][$day], strtr(parent::format($format['format_short']), $format['lang']));
+ }
+ }
+ }
+
+ return strtr(parent::format($format['format_long']), $format['lang']);
+ }
+
+ /**
+ * Magic method to convert DateTime object to string
+ *
+ * @return Formatted date time, according to the users default settings.
+ */
+ public function __toString()
+ {
+ return $this->format();
+ }
+
+ /**
+ * Pre-processes the specified date format
+ *
+ * @param string $format Output format
+ * @param user $user User object to use for localisation
+ * @return array Processed date format
+ */
+ static protected function _format_cache($format, $user)
+ {
+ $lang = $user->lang_name;
+
+ if (!isset(self::$format_cache[$lang]))
+ {
+ self::$format_cache[$lang] = array();
+ }
+
+ if (!isset(self::$format_cache[$lang][$format]))
+ {
+ // Is the user requesting a friendly date format (i.e. 'Today 12:42')?
+ self::$format_cache[$lang][$format] = array(
+ 'is_short' => strpos($format, self::RELATIVE_WRAPPER) !== false,
+ 'format_short' => substr($format, 0, strpos($format, self::RELATIVE_WRAPPER)) . self::RELATIVE_WRAPPER . self::RELATIVE_WRAPPER . substr(strrchr($format, self::RELATIVE_WRAPPER), 1),
+ 'format_long' => str_replace(self::RELATIVE_WRAPPER, '', $format),
+ 'lang' => $user->lang['datetime'],
+ );
+
+ // Short representation of month in format? Some languages use different terms for the long and short format of May
+ if ((strpos($format, '\M') === false && strpos($format, 'M') !== false) || (strpos($format, '\r') === false && strpos($format, 'r') !== false))
+ {
+ self::$format_cache[$lang][$format]['lang']['May'] = $user->lang['datetime']['May_short'];
+ }
+ }
+
+ return self::$format_cache[$lang][$format];
+ }
+}
diff --git a/phpBB/includes/functions.php b/phpBB/includes/functions.php
index 95f2cf8d26..3ec4b76091 100644
--- a/phpBB/includes/functions.php
+++ b/phpBB/includes/functions.php
@@ -1068,30 +1068,116 @@ function style_select($default = '', $all = false)
return $style_options;
}
+function phpbb_format_timezone_offset($tz_offset)
+{
+ $sign = ($tz_offset < 0) ? '-' : '+';
+ $time_offset = abs($tz_offset);
+
+ $offset_seconds = $time_offset % 3600;
+ $offset_minutes = $offset_seconds / 60;
+ $offset_hours = ($time_offset - $offset_seconds) / 3600;
+
+ $offset_string = sprintf("%s%02d:%02d", $sign, $offset_hours, $offset_minutes);
+ return $offset_string;
+}
+
+// Compares two time zone labels.
+// Arranges them in increasing order by timezone offset.
+// Places UTC before other timezones in the same offset.
+function tz_select_compare($a, $b)
+{
+ $a_sign = $a[3];
+ $b_sign = $b[3];
+ if ($a_sign != $b_sign)
+ {
+ return $a_sign == '-' ? -1 : 1;
+ }
+
+ $a_offset = substr($a, 4, 5);
+ $b_offset = substr($b, 4, 5);
+ if ($a_offset == $b_offset)
+ {
+ $a_name = substr($a, 12);
+ $b_name = substr($b, 12);
+ if ($a_name == $b_name)
+ {
+ return 0;
+ } else if ($a_name == 'UTC')
+ {
+ return -1;
+ } else if ($b_name == 'UTC')
+ {
+ return 1;
+ }
+ else
+ {
+ return $a_name < $b_name ? -1 : 1;
+ }
+ }
+ else
+ {
+ if ($a_sign == '-')
+ {
+ return $a_offset > $b_offset ? -1 : 1;
+ }
+ else
+ {
+ return $a_offset < $b_offset ? -1 : 1;
+ }
+ }
+}
+
/**
* Pick a timezone
+* @todo Possible HTML escaping
*/
function tz_select($default = '', $truncate = false)
{
global $user;
+ static $timezones;
+
+ if (!isset($timezones))
+ {
+ $timezones = DateTimeZone::listIdentifiers();
+
+ foreach ($timezones as &$timezone)
+ {
+ $tz = new DateTimeZone($timezone);
+ $dt = new phpbb_datetime('now', $tz);
+ $offset = $dt->getOffset();
+ $offset_string = phpbb_format_timezone_offset($offset);
+ $timezone = 'GMT' . $offset_string . ' - ' . $timezone;
+ }
+ unset($timezone);
+
+ usort($timezones, 'tz_select_compare');
+ }
+
$tz_select = '';
- foreach ($user->lang['tz_zones'] as $offset => $zone)
+
+ foreach ($timezones as $timezone)
{
- if ($truncate)
+ if (isset($user->lang['timezones'][$timezone]))
{
- $zone_trunc = truncate_string($zone, 50, 255, false, '...');
+ $title = $label = $user->lang['timezones'][$timezone];
}
else
{
- $zone_trunc = $zone;
+ // No label, we'll figure one out
+ // @todo rtl languages?
+ $bits = explode('/', str_replace('_', ' ', $timezone));
+
+ $title = $label = implode(' - ', $bits);
}
- if (is_numeric($offset))
+ if ($truncate)
{
- $selected = ($offset == $default) ? ' selected="selected"' : '';
- $tz_select .= '<option title="' . $zone . '" value="' . $offset . '"' . $selected . '>' . $zone_trunc . '</option>';
+ $label = truncate_string($label, 50, 255, false, '...');
}
+
+ $selected = ($timezone === $default) ? ' selected="selected"' : '';
+ $tz_select .= '<option title="' . $title . '" value="' . $timezone . '"' . $selected . '>' . $label . '</option>';
}
return $tz_select;
diff --git a/phpBB/includes/functions_profile_fields.php b/phpBB/includes/functions_profile_fields.php
index 3399334f94..1c15ef897f 100644
--- a/phpBB/includes/functions_profile_fields.php
+++ b/phpBB/includes/functions_profile_fields.php
@@ -554,9 +554,12 @@ class custom_profile
else if ($day && $month && $year)
{
global $user;
- // Date should display as the same date for every user regardless of timezone, so remove offset
- // to compensate for the offset added by phpbb_user::format_date()
- return $user->format_date(gmmktime(0, 0, 0, $month, $day, $year) - ($user->timezone + $user->dst), $user->lang['DATE_FORMAT'], true);
+ // Date should display as the same date for every user regardless of timezone
+
+ return $user->create_datetime()
+ ->setDate($year, $month, $day)
+ ->setTime(0, 0, 0)
+ ->format($user->lang['DATE_FORMAT'], true);
}
return $value;
diff --git a/phpBB/includes/user.php b/phpBB/includes/user.php
index ce9c804f23..4c62dd93d7 100644
--- a/phpBB/includes/user.php
+++ b/phpBB/includes/user.php
@@ -29,8 +29,7 @@ class phpbb_user extends phpbb_session
var $help = array();
var $theme = array();
var $date_format;
- var $timezone;
- var $dst;
+ public $tz;
var $lang_name = false;
var $lang_id = false;
@@ -79,15 +78,13 @@ class phpbb_user extends phpbb_session
$this->lang_name = (file_exists($this->lang_path . $this->data['user_lang'] . "/common.$phpEx")) ? $this->data['user_lang'] : basename($config['default_lang']);
$this->date_format = $this->data['user_dateformat'];
- $this->timezone = $this->data['user_timezone'] * 3600;
- $this->dst = $this->data['user_dst'] * 3600;
+ $this->tz = $this->data['user_timezone'];
}
else
{
$this->lang_name = basename($config['default_lang']);
$this->date_format = $config['default_dateformat'];
- $this->timezone = $config['board_timezone'] * 3600;
- $this->dst = $config['board_dst'] * 3600;
+ $this->tz = $config['board_timezone'];
/**
* If a guest user is surfing, we try to guess his/her language first by obtaining the browser language
@@ -126,6 +123,14 @@ class phpbb_user extends phpbb_session
*/
}
+ if (is_numeric($this->tz))
+ {
+ // Might still be numeric by chance
+ $this->tz = sprintf('Etc/GMT%+d', ($this->tz + ($this->data['user_id'] != ANONYMOUS ? $this->data['user_dst'] : $config['board_dst'])));
+ }
+
+ $this->tz = new DateTimeZone($this->tz);
+
// We include common language file here to not load it every time a custom language file is included
$lang = &$this->lang;
@@ -615,70 +620,31 @@ class phpbb_user extends phpbb_session
*/
function format_date($gmepoch, $format = false, $forcedate = false)
{
- static $midnight;
- static $date_cache;
-
- $format = (!$format) ? $this->date_format : $format;
- $now = time();
- $delta = $now - $gmepoch;
-
- if (!isset($date_cache[$format]))
- {
- // Is the user requesting a friendly date format (i.e. 'Today 12:42')?
- $date_cache[$format] = array(
- 'is_short' => strpos($format, '|'),
- 'format_short' => substr($format, 0, strpos($format, '|')) . '||' . substr(strrchr($format, '|'), 1),
- 'format_long' => str_replace('|', '', $format),
- 'lang' => $this->lang['datetime'],
- );
-
- // Short representation of month in format? Some languages use different terms for the long and short format of May
- if ((strpos($format, '\M') === false && strpos($format, 'M') !== false) || (strpos($format, '\r') === false && strpos($format, 'r') !== false))
- {
- $date_cache[$format]['lang']['May'] = $this->lang['datetime']['May_short'];
- }
- }
-
- // Zone offset
- $zone_offset = $this->timezone + $this->dst;
+ static $utc;
- // Show date < 1 hour ago as 'xx min ago' but not greater than 60 seconds in the future
- // A small tolerence is given for times in the future but in the same minute are displayed as '< than a minute ago'
- if ($delta < 3600 && $delta > -60 && ($delta >= -5 || (($now / 60) % 60) == (($gmepoch / 60) % 60)) && $date_cache[$format]['is_short'] !== false && !$forcedate && isset($this->lang['datetime']['AGO']))
+ if (!isset($utc))
{
- return $this->lang(array('datetime', 'AGO'), max(0, (int) floor($delta / 60)));
+ $utc = new DateTimeZone('UTC');
}
- if (!$midnight)
- {
- list($d, $m, $y) = explode(' ', gmdate('j n Y', time() + $zone_offset));
- $midnight = gmmktime(0, 0, 0, $m, $d, $y) - $zone_offset;
- }
-
- if ($date_cache[$format]['is_short'] !== false && !$forcedate && !($gmepoch < $midnight - 86400 || $gmepoch > $midnight + 172800))
- {
- $day = false;
+ $time = new phpbb_datetime("@$gmepoch", $utc, $this);
+ $time->setTimezone($this->tz);
- if ($gmepoch > $midnight + 86400)
- {
- $day = 'TOMORROW';
- }
- else if ($gmepoch > $midnight)
- {
- $day = 'TODAY';
- }
- else if ($gmepoch > $midnight - 86400)
- {
- $day = 'YESTERDAY';
- }
-
- if ($day !== false)
- {
- return str_replace('||', $this->lang['datetime'][$day], strtr(@gmdate($date_cache[$format]['format_short'], $gmepoch + $zone_offset), $date_cache[$format]['lang']));
- }
- }
+ return $time->format($format, $forcedate);
+ }
- return strtr(@gmdate($date_cache[$format]['format_long'], $gmepoch + $zone_offset), $date_cache[$format]['lang']);
+ /**
+ * Create a phpbb_datetime object in the context of the current user
+ *
+ * @since 3.1
+ * @param string $time String in a format accepted by strtotime().
+ * @param DateTimeZone $timezone Time zone of the time.
+ * @return phpbb_datetime Date time object linked to the current users locale
+ */
+ public function create_datetime($time = 'now', DateTimeZone $timezone = null)
+ {
+ $timezone = $timezone ?: $this->tz;
+ return new phpbb_datetime($time, $timezone, $this);
}
/**