Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2368)

Unified Diff: appengine/monorail/framework/timestr.py

Issue 1868553004: Open Source Monorail (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: Rebase Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « appengine/monorail/framework/test/xsrf_test.py ('k') | appengine/monorail/framework/tokenrefresh.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: appengine/monorail/framework/timestr.py
diff --git a/appengine/monorail/framework/timestr.py b/appengine/monorail/framework/timestr.py
new file mode 100644
index 0000000000000000000000000000000000000000..5ec1ee8a69eebb8112e00ff35c4057a35159ae43
--- /dev/null
+++ b/appengine/monorail/framework/timestr.py
@@ -0,0 +1,184 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is govered by a BSD-style
+# license that can be found in the LICENSE file or at
+# https://developers.google.com/open-source/licenses/bsd
+
+"""Time-to-string and time-from-string routines."""
+
+import datetime
+import time
+
+
+class Error(Exception):
+ """Exception used to indicate problems with time routines."""
+ pass
+
+
+HTML_TIME_FMT = '%a, %d %b %Y %H:%M:%S GMT'
+
+MONTH_YEAR_FMT = '%b %Y'
+MONTH_DAY_FMT = '%b %d'
+MONTH_DAY_YEAR_FMT = '%b %d %Y'
+
+# We assume that all server clocks are synchronized within this amount.
+MAX_CLOCK_SKEW_SEC = 30
+
+
+def TimeForHTMLHeader(when=None):
+ """Return the given time (or now) in HTML header format."""
+ if when is None:
+ when = int(time.time())
+ return time.strftime(HTML_TIME_FMT, time.gmtime(when))
+
+
+def FormatAbsoluteDate(
+ timestamp, clock=datetime.datetime.utcnow,
+ recent_format=MONTH_DAY_FMT, old_format=MONTH_YEAR_FMT):
+ """Format timestamp like 'Sep 5', or 'Yesterday', or 'Today'.
+
+ Args:
+ timestamp: Seconds since the epoch in UTC.
+ clock: callable that returns a datetime.datetime object when called with no
+ arguments, giving the current time to use when computing what to display.
+ recent_format: Format string to pass to strftime to present dates between
+ six months ago and yesterday.
+ old_format: Format string to pass to strftime to present dates older than
+ six months or more than skew_tolerance in the future.
+
+ Returns:
+ If timestamp's date is today, "Today". If timestamp's date is yesterday,
+ "Yesterday". If timestamp is within six months before today, return the
+ time as formatted by recent_format. Otherwise, return the time as formatted
+ by old_format.
+ """
+ ts = datetime.datetime.fromtimestamp(timestamp)
+ now = clock()
+ month_delta = 12 * now.year + now.month - (12 * ts.year + ts.month)
+ delta = now - ts
+
+ if ts > now:
+ # If the time is slightly in the future due to clock skew, treat as today.
+ skew_tolerance = datetime.timedelta(seconds=MAX_CLOCK_SKEW_SEC)
+ if -delta <= skew_tolerance:
+ return 'Today'
+ # Otherwise treat it like an old date.
+ else:
+ fmt = old_format
+ elif month_delta > 6 or delta.days >= 365:
+ fmt = old_format
+ elif delta.days == 1:
+ return 'Yesterday'
+ elif delta.days == 0:
+ return 'Today'
+ else:
+ fmt = recent_format
+
+ return time.strftime(fmt, time.localtime(timestamp)).replace(' 0', ' ')
+
+
+def FormatRelativeDate(timestamp, recent_only=False, clock=None):
+ """Return a short string that makes timestamp more meaningful to the user.
+
+ Describe the timestamp relative to the current time, e.g., '4
+ hours ago'. In cases where the timestamp is more than 6 days ago,
+ we simply show the year, so that the combined absolute and
+ relative parts look like 'Sep 05, 2005'.
+
+ Args:
+ timestamp: Seconds since the epoch in UTC.
+ recent_only: If True, only return a description of recent relative
+ dates. Do not return the year, and do not put results inside parentheses.
+ clock: optional function to return an int time, like int(time.time()).
+
+ Returns:
+ String describing relative time.
+ """
+ if clock:
+ now = clock()
+ else:
+ now = int(time.time())
+
+ # TODO(jrobbins): i18n of date strings
+ delta = int(now - timestamp)
+ d_minutes = delta // 60
+ d_hours = d_minutes // 60
+ d_days = d_hours // 24
+ if recent_only:
+ if d_days > 6:
+ return ''
+ if d_days > 1:
+ return '%s days ago' % d_days # starts at 2 days
+ if d_hours > 1:
+ return '%s hours ago' % d_hours # starts at 2 hours
+ if d_minutes > 1:
+ return '%s minutes ago' % d_minutes
+ if d_minutes > 0:
+ return '1 minute ago'
+ if delta > -MAX_CLOCK_SKEW_SEC:
+ return 'moments ago'
+ return ''
+ else:
+ if d_days > 6:
+ return ', %s' % (time.localtime(timestamp))[0]
+ if d_days > 1:
+ return ' (%s days ago)' % d_days # starts at 2 days
+ if d_hours > 1:
+ return ' (%s hours ago)' % d_hours # starts at 2 hours
+ if d_minutes > 1:
+ return ' (%s minutes ago)' % d_minutes
+ if d_minutes > 0:
+ return ' (1 minute ago)'
+ if delta > -MAX_CLOCK_SKEW_SEC:
+ return ' (moments ago)'
+ # Only say something is in the future if it is more than just clock skew.
+ return ' (in the future)'
+
+
+def GetHumanScaleDate(timestamp, now=None):
+ """Formats a timestamp to a course-grained and fine-grained time phrase.
+
+ Args:
+ timestamp: Seconds since the epoch in UTC.
+ now: Current time in seconds since the epoch in UTC.
+
+ Returns:
+ A pair (course_grain, fine_grain) where course_grain is a string
+ such as 'Today', 'Yesterday', etc.; and fine_grained is a string describing
+ relative hours for Today and Yesterday, or an exact date for longer ago.
+ """
+ if now is None:
+ now = int(time.time())
+
+ now_year = datetime.datetime.fromtimestamp(now).year
+ then_year = datetime.datetime.fromtimestamp(timestamp).year
+ delta = int(now - timestamp)
+ delta_minutes = delta // 60
+ delta_hours = delta_minutes // 60
+ delta_days = delta_hours // 24
+
+ if 0 <= delta_hours < 24:
+ if delta_hours > 1:
+ return 'Today', '%s hours ago' % delta_hours
+ if delta_minutes > 1:
+ return 'Today', '%s min ago' % delta_minutes
+ if delta_minutes > 0:
+ return 'Today', '1 min ago'
+ if delta > 0:
+ return 'Today', 'moments ago'
+ if 0 <= delta_hours < 48:
+ return 'Yesterday', '%s hours ago' % delta_hours
+ if 0 <= delta_days < 7:
+ return 'Last 7 days', time.strftime(
+ '%b %d, %Y', (time.localtime(timestamp)))
+ if 0 <= delta_days < 30:
+ return 'Last 30 days', time.strftime(
+ '%b %d, %Y', (time.localtime(timestamp)))
+ if delta > 0:
+ if now_year == then_year:
+ return 'Earlier this year', time.strftime(
+ '%b %d, %Y', (time.localtime(timestamp)))
+ return 'Older', time.strftime('%b %d, %Y', (time.localtime(timestamp)))
+ if delta > -MAX_CLOCK_SKEW_SEC:
+ return 'Today', 'moments ago'
+ # Only say something is in the future if it is more than just clock skew.
+ return 'Future', 'Later'
« no previous file with comments | « appengine/monorail/framework/test/xsrf_test.py ('k') | appengine/monorail/framework/tokenrefresh.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698