2011-01-31 03:24:57 +01:00
|
|
|
<?php
|
|
|
|
|
|
|
|
/*
|
2012-03-28 03:16:59 +02:00
|
|
|
* Copyright 2012 Facebook, Inc.
|
2011-01-31 03:24:57 +01:00
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
2011-06-18 22:07:02 +02:00
|
|
|
function phabricator_date($epoch, $user) {
|
2012-04-02 23:45:07 +02:00
|
|
|
return phabricator_format_local_time(
|
Improve time localization code
Summary:
- We throw on a missing date right now, in the DateTime constructor. This can
happen in reasonable cases and this is display code, so handle it more
gracefully (see T520).
- This stuff is a little slow and we sometimes render many hundreds of dates
per page. I've been seeing it in profiles on and off. Memoize timezones to
improve performance.
- Some minor code duplication that would have become less-minor with the
constructor change, consolidate the logic.
- Add some unit tests and a little documentation.
Test Plan:
- Ran unit tests.
- Profiled 1,000 calls to phabricator_datetime(), cost dropped from ~49ms to
~19ms with addition of memoization. This is still slower than I'd like but I
don't think there's an easy way to squeeze it down further.
Reviewers: ajtrichards, jungejason, nh, tuomaspelkonen, aran
Reviewed By: ajtrichards
CC: aran, ajtrichards, epriestley
Differential Revision: 966
2011-09-27 18:03:55 +02:00
|
|
|
$epoch,
|
|
|
|
$user,
|
2012-04-02 23:45:07 +02:00
|
|
|
_phabricator_date_format($epoch));
|
2011-06-18 22:07:02 +02:00
|
|
|
}
|
|
|
|
|
2012-03-28 03:16:59 +02:00
|
|
|
function phabricator_on_relative_date($epoch, $user) {
|
|
|
|
return phabricator_relative_date($epoch, $user, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
function phabricator_relative_date($epoch, $user, $on = false) {
|
|
|
|
static $today;
|
|
|
|
static $yesterday;
|
|
|
|
|
|
|
|
if (!$today || !$yesterday) {
|
|
|
|
$now = time();
|
|
|
|
$today = phabricator_date($now, $user);
|
|
|
|
$yesterday = phabricator_date($now - 86400, $user);
|
|
|
|
}
|
|
|
|
|
|
|
|
$date = phabricator_date($epoch, $user);
|
|
|
|
|
|
|
|
if ($date === $today) {
|
|
|
|
return 'today';
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($date === $yesterday) {
|
|
|
|
return 'yesterday';
|
|
|
|
}
|
|
|
|
|
|
|
|
return (($on ? 'on ' : '').$date);
|
|
|
|
}
|
|
|
|
|
2011-06-18 22:07:02 +02:00
|
|
|
function phabricator_time($epoch, $user) {
|
2012-04-02 23:45:07 +02:00
|
|
|
return phabricator_format_local_time(
|
Improve time localization code
Summary:
- We throw on a missing date right now, in the DateTime constructor. This can
happen in reasonable cases and this is display code, so handle it more
gracefully (see T520).
- This stuff is a little slow and we sometimes render many hundreds of dates
per page. I've been seeing it in profiles on and off. Memoize timezones to
improve performance.
- Some minor code duplication that would have become less-minor with the
constructor change, consolidate the logic.
- Add some unit tests and a little documentation.
Test Plan:
- Ran unit tests.
- Profiled 1,000 calls to phabricator_datetime(), cost dropped from ~49ms to
~19ms with addition of memoization. This is still slower than I'd like but I
don't think there's an easy way to squeeze it down further.
Reviewers: ajtrichards, jungejason, nh, tuomaspelkonen, aran
Reviewed By: ajtrichards
CC: aran, ajtrichards, epriestley
Differential Revision: 966
2011-09-27 18:03:55 +02:00
|
|
|
$epoch,
|
|
|
|
$user,
|
2012-06-20 21:33:09 +02:00
|
|
|
pht('g:i A'));
|
2011-06-18 22:07:02 +02:00
|
|
|
}
|
|
|
|
|
2012-06-20 21:33:09 +02:00
|
|
|
function phabricator_datetime($epoch, $user) {
|
2012-04-02 23:45:07 +02:00
|
|
|
return phabricator_format_local_time(
|
Improve time localization code
Summary:
- We throw on a missing date right now, in the DateTime constructor. This can
happen in reasonable cases and this is display code, so handle it more
gracefully (see T520).
- This stuff is a little slow and we sometimes render many hundreds of dates
per page. I've been seeing it in profiles on and off. Memoize timezones to
improve performance.
- Some minor code duplication that would have become less-minor with the
constructor change, consolidate the logic.
- Add some unit tests and a little documentation.
Test Plan:
- Ran unit tests.
- Profiled 1,000 calls to phabricator_datetime(), cost dropped from ~49ms to
~19ms with addition of memoization. This is still slower than I'd like but I
don't think there's an easy way to squeeze it down further.
Reviewers: ajtrichards, jungejason, nh, tuomaspelkonen, aran
Reviewed By: ajtrichards
CC: aran, ajtrichards, epriestley
Differential Revision: 966
2011-09-27 18:03:55 +02:00
|
|
|
$epoch,
|
|
|
|
$user,
|
2012-06-20 21:33:09 +02:00
|
|
|
pht('%s, g:i A', _phabricator_date_format($epoch)));
|
2012-03-21 23:04:49 +01:00
|
|
|
}
|
|
|
|
|
2012-04-02 23:45:07 +02:00
|
|
|
function _phabricator_date_format($epoch) {
|
2012-06-20 21:33:09 +02:00
|
|
|
$format = pht('M j Y');
|
2012-03-21 23:04:49 +01:00
|
|
|
$now = time();
|
|
|
|
if ($epoch <= $now && $epoch > $now - 30 * 24 * 60 * 60) {
|
2012-06-20 21:33:09 +02:00
|
|
|
$format = pht('D, M j');
|
2012-03-21 23:04:49 +01:00
|
|
|
}
|
|
|
|
return $format;
|
Improve time localization code
Summary:
- We throw on a missing date right now, in the DateTime constructor. This can
happen in reasonable cases and this is display code, so handle it more
gracefully (see T520).
- This stuff is a little slow and we sometimes render many hundreds of dates
per page. I've been seeing it in profiles on and off. Memoize timezones to
improve performance.
- Some minor code duplication that would have become less-minor with the
constructor change, consolidate the logic.
- Add some unit tests and a little documentation.
Test Plan:
- Ran unit tests.
- Profiled 1,000 calls to phabricator_datetime(), cost dropped from ~49ms to
~19ms with addition of memoization. This is still slower than I'd like but I
don't think there's an easy way to squeeze it down further.
Reviewers: ajtrichards, jungejason, nh, tuomaspelkonen, aran
Reviewed By: ajtrichards
CC: aran, ajtrichards, epriestley
Differential Revision: 966
2011-09-27 18:03:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2012-04-02 23:45:07 +02:00
|
|
|
* This function does not usually need to be called directly. Instead, call
|
Improve time localization code
Summary:
- We throw on a missing date right now, in the DateTime constructor. This can
happen in reasonable cases and this is display code, so handle it more
gracefully (see T520).
- This stuff is a little slow and we sometimes render many hundreds of dates
per page. I've been seeing it in profiles on and off. Memoize timezones to
improve performance.
- Some minor code duplication that would have become less-minor with the
constructor change, consolidate the logic.
- Add some unit tests and a little documentation.
Test Plan:
- Ran unit tests.
- Profiled 1,000 calls to phabricator_datetime(), cost dropped from ~49ms to
~19ms with addition of memoization. This is still slower than I'd like but I
don't think there's an easy way to squeeze it down further.
Reviewers: ajtrichards, jungejason, nh, tuomaspelkonen, aran
Reviewed By: ajtrichards
CC: aran, ajtrichards, epriestley
Differential Revision: 966
2011-09-27 18:03:55 +02:00
|
|
|
* @{function:phabricator_date}, @{function:phabricator_time}, or
|
|
|
|
* @{function:phabricator_datetime}.
|
|
|
|
*
|
|
|
|
* @param int Unix epoch timestamp.
|
|
|
|
* @param PhabricatorUser User viewing the timestamp.
|
|
|
|
* @param string Date format, as per DateTime class.
|
|
|
|
* @return string Formatted, local date/time.
|
|
|
|
*/
|
2012-04-02 23:45:07 +02:00
|
|
|
function phabricator_format_local_time($epoch, $user, $format) {
|
Improve time localization code
Summary:
- We throw on a missing date right now, in the DateTime constructor. This can
happen in reasonable cases and this is display code, so handle it more
gracefully (see T520).
- This stuff is a little slow and we sometimes render many hundreds of dates
per page. I've been seeing it in profiles on and off. Memoize timezones to
improve performance.
- Some minor code duplication that would have become less-minor with the
constructor change, consolidate the logic.
- Add some unit tests and a little documentation.
Test Plan:
- Ran unit tests.
- Profiled 1,000 calls to phabricator_datetime(), cost dropped from ~49ms to
~19ms with addition of memoization. This is still slower than I'd like but I
don't think there's an easy way to squeeze it down further.
Reviewers: ajtrichards, jungejason, nh, tuomaspelkonen, aran
Reviewed By: ajtrichards
CC: aran, ajtrichards, epriestley
Differential Revision: 966
2011-09-27 18:03:55 +02:00
|
|
|
if (!$epoch) {
|
|
|
|
// If we're missing date information for something, the DateTime class will
|
|
|
|
// throw an exception when we try to construct an object. Since this is a
|
|
|
|
// display function, just return an empty string.
|
|
|
|
return '';
|
|
|
|
}
|
|
|
|
|
|
|
|
$user_zone = $user->getTimezoneIdentifier();
|
|
|
|
|
|
|
|
static $zones = array();
|
|
|
|
if (empty($zones[$user_zone])) {
|
|
|
|
$zones[$user_zone] = new DateTimeZone($user_zone);
|
|
|
|
}
|
|
|
|
$zone = $zones[$user_zone];
|
|
|
|
|
|
|
|
// NOTE: Although DateTime takes a second DateTimeZone parameter to its
|
|
|
|
// constructor, it ignores it if the date string includes timezone
|
|
|
|
// information. Further, it treats epoch timestamps ("@946684800") as having
|
|
|
|
// a UTC timezone. Set the timezone explicitly after constructing the object.
|
2012-04-18 17:02:08 +02:00
|
|
|
try {
|
|
|
|
$date = new DateTime('@'.$epoch);
|
|
|
|
} catch (Exception $ex) {
|
|
|
|
// NOTE: DateTime throws an empty exception if the format is invalid,
|
|
|
|
// just replace it with a useful one.
|
|
|
|
throw new Exception(
|
|
|
|
"Construction of a DateTime() with epoch '{$epoch}' ".
|
|
|
|
"raised an exception.");
|
|
|
|
}
|
|
|
|
|
Use phabricator_ time functions in more places
Summary:
Replace some more date() calls with locale-aware calls.
Also, at least on my system, the DateTimeZone / DateTime stuff didn't actually
work and always rendered in UTC. Fixed that.
Test Plan:
Viewed daemon console, differential revisions, files, and maniphest timestamps
in multiple timezones.
Reviewed By: toulouse
Reviewers: toulouse, fratrik, jungejason, aran, tuomaspelkonen
CC: aran, toulouse
Differential Revision: 530
2011-06-26 18:22:52 +02:00
|
|
|
$date->setTimeZone($zone);
|
Improve time localization code
Summary:
- We throw on a missing date right now, in the DateTime constructor. This can
happen in reasonable cases and this is display code, so handle it more
gracefully (see T520).
- This stuff is a little slow and we sometimes render many hundreds of dates
per page. I've been seeing it in profiles on and off. Memoize timezones to
improve performance.
- Some minor code duplication that would have become less-minor with the
constructor change, consolidate the logic.
- Add some unit tests and a little documentation.
Test Plan:
- Ran unit tests.
- Profiled 1,000 calls to phabricator_datetime(), cost dropped from ~49ms to
~19ms with addition of memoization. This is still slower than I'd like but I
don't think there's an easy way to squeeze it down further.
Reviewers: ajtrichards, jungejason, nh, tuomaspelkonen, aran
Reviewed By: ajtrichards
CC: aran, ajtrichards, epriestley
Differential Revision: 966
2011-09-27 18:03:55 +02:00
|
|
|
|
2012-06-20 21:33:09 +02:00
|
|
|
return PhutilTranslator::getInstance()->translateDate($format, $date);
|
2011-06-18 22:07:02 +02:00
|
|
|
}
|
|
|
|
|
2011-01-31 03:24:57 +01:00
|
|
|
function phabricator_format_relative_time($duration) {
|
|
|
|
return phabricator_format_units_generic(
|
|
|
|
$duration,
|
|
|
|
array(60, 60, 24, 7),
|
|
|
|
array('s', 'm', 'h', 'd', 'w'),
|
|
|
|
$precision = 0);
|
|
|
|
}
|
|
|
|
|
2012-06-01 19:07:50 +02:00
|
|
|
/**
|
|
|
|
* Format a relative time (duration) into weeks, days, hours, minutes,
|
|
|
|
* seconds, but unlike phabricator_format_relative_time, does so for more than
|
|
|
|
* just the largest unit.
|
|
|
|
*
|
|
|
|
* @param int Duration in seconds.
|
|
|
|
* @param int Levels to render - will render the three highest levels, ie:
|
|
|
|
* 5 h, 37 m, 1 s
|
|
|
|
* @return string Human-readable description.
|
|
|
|
*/
|
|
|
|
function phabricator_format_relative_time_detailed($duration, $levels = 2) {
|
|
|
|
if ($duration == 0) {
|
|
|
|
return 'now';
|
|
|
|
}
|
|
|
|
$levels = max(1, min($levels, 5));
|
|
|
|
$remainder = 0;
|
|
|
|
|
|
|
|
$is_negative = false;
|
|
|
|
if ($duration < 0) {
|
|
|
|
$is_negative = true;
|
|
|
|
$duration = abs($duration);
|
|
|
|
}
|
|
|
|
|
|
|
|
$this_level = 1;
|
|
|
|
$detailed_relative_time = phabricator_format_units_generic(
|
|
|
|
$duration,
|
|
|
|
array(60, 60, 24, 7),
|
|
|
|
array('s', 'm', 'h', 'd', 'w'),
|
|
|
|
$precision = 0,
|
|
|
|
$remainder);
|
|
|
|
$duration = $remainder;
|
|
|
|
|
|
|
|
while ($remainder > 0 && $this_level < $levels) {
|
|
|
|
$detailed_relative_time .= ', '.phabricator_format_units_generic(
|
|
|
|
$duration,
|
|
|
|
array(60, 60, 24, 7),
|
|
|
|
array('s', 'm', 'h', 'd', 'w'),
|
|
|
|
$precision = 0,
|
|
|
|
$remainder);
|
|
|
|
$duration = $remainder;
|
|
|
|
$this_level++;
|
|
|
|
};
|
|
|
|
|
|
|
|
if ($is_negative) {
|
|
|
|
$detailed_relative_time .= ' ago';
|
|
|
|
}
|
|
|
|
|
|
|
|
return $detailed_relative_time;
|
|
|
|
}
|
2012-05-04 02:30:17 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Format a byte count for human consumption, e.g. "10MB" instead of
|
|
|
|
* "10000000".
|
|
|
|
*
|
|
|
|
* @param int Number of bytes.
|
|
|
|
* @return string Human-readable description.
|
|
|
|
*/
|
|
|
|
function phabricator_format_bytes($bytes) {
|
|
|
|
return phabricator_format_units_generic(
|
|
|
|
$bytes,
|
|
|
|
// NOTE: Using the SI version of these units rather than the 1024 version.
|
|
|
|
array(1000, 1000, 1000, 1000, 1000),
|
|
|
|
array('B', 'KB', 'MB', 'GB', 'TB', 'PB'),
|
|
|
|
$precision = 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Parse a human-readable byte description (like "6MB") into an integer.
|
|
|
|
*
|
|
|
|
* @param string Human-readable description.
|
|
|
|
* @return int Number of represented bytes.
|
|
|
|
*/
|
|
|
|
function phabricator_parse_bytes($input) {
|
|
|
|
$bytes = trim($input);
|
|
|
|
if (!strlen($bytes)) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
// NOTE: Assumes US-centric numeral notation.
|
|
|
|
$bytes = preg_replace('/[ ,]/', '', $bytes);
|
|
|
|
|
|
|
|
$matches = null;
|
|
|
|
if (!preg_match('/^(?:\d+(?:[.]\d+)?)([kmgtp]?)b?$/i', $bytes, $matches)) {
|
|
|
|
throw new Exception("Unable to parse byte size '{$input}'!");
|
|
|
|
}
|
|
|
|
|
|
|
|
$scale = array(
|
|
|
|
'k' => 1000,
|
|
|
|
'm' => 1000 * 1000,
|
|
|
|
'g' => 1000 * 1000 * 1000,
|
|
|
|
't' => 1000 * 1000 * 1000 * 1000,
|
2012-06-21 22:36:34 +02:00
|
|
|
'p' => 1000 * 1000 * 1000 * 1000 * 1000,
|
2012-05-04 02:30:17 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
$bytes = (float)$bytes;
|
|
|
|
if ($matches[1]) {
|
|
|
|
$bytes *= $scale[strtolower($matches[1])];
|
|
|
|
}
|
|
|
|
|
|
|
|
return (int)$bytes;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-01-31 03:24:57 +01:00
|
|
|
function phabricator_format_units_generic(
|
|
|
|
$n,
|
|
|
|
array $scales,
|
|
|
|
array $labels,
|
|
|
|
$precision = 0,
|
|
|
|
&$remainder = null) {
|
|
|
|
|
2011-02-22 19:24:49 +01:00
|
|
|
$is_negative = false;
|
2011-01-31 03:24:57 +01:00
|
|
|
if ($n < 0) {
|
|
|
|
$is_negative = true;
|
|
|
|
$n = abs($n);
|
|
|
|
}
|
|
|
|
|
|
|
|
$remainder = 0;
|
|
|
|
$accum = 1;
|
|
|
|
|
|
|
|
$scale = array_shift($scales);
|
|
|
|
$label = array_shift($labels);
|
2012-05-04 02:30:17 +02:00
|
|
|
while ($n >= $scale && count($labels)) {
|
2011-01-31 03:24:57 +01:00
|
|
|
$remainder += ($n % $scale) * $accum;
|
|
|
|
$n /= $scale;
|
|
|
|
$accum *= $scale;
|
|
|
|
$label = array_shift($labels);
|
|
|
|
if (!count($scales)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
$scale = array_shift($scales);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($is_negative) {
|
|
|
|
$n = -$n;
|
|
|
|
$remainder = -$remainder;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($precision) {
|
|
|
|
$num_string = number_format($n, $precision);
|
|
|
|
} else {
|
|
|
|
$num_string = (int)floor($n);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($label) {
|
|
|
|
$num_string .= ' '.$label;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $num_string;
|
|
|
|
}
|
|
|
|
|