Project import generated by Copybara.
GitOrigin-RevId: d9e9e3fb4e31372ec1fb43b178994ca78fa8fe70
diff --git a/framework/timestr.py b/framework/timestr.py
new file mode 100644
index 0000000..2b32e8c
--- /dev/null
+++ b/framework/timestr.py
@@ -0,0 +1,188 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed 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."""
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+
+import calendar
+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'
+HTML_DATE_WIDGET_FORMAT = '%Y-%m-%d'
+
+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 TimestampToDateWidgetStr(when):
+ """Format a timestamp int for use by HTML <input type="date">."""
+ return time.strftime(HTML_DATE_WIDGET_FORMAT, time.gmtime(when))
+
+
+def DateWidgetStrToTimestamp(val_str):
+ """Parse the HTML <input type="date"> string into a timestamp int."""
+ return int(calendar.timegm(time.strptime(val_str, HTML_DATE_WIDGET_FORMAT)))
+
+
+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.utcfromtimestamp(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.gmtime(timestamp)).replace(' 0', ' ')
+
+
+def FormatRelativeDate(timestamp, days_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 return '' so that an alternative display can be used instead.
+
+ Args:
+ timestamp: Seconds since the epoch in UTC.
+ days_only: If True, return 'N days ago' even for more than 6 days.
+ 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 days_only:
+ if d_days > 1:
+ return '%s days ago' % d_days
+ else:
+ return ''
+
+ 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 ''
+
+
+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 ('Before this year',
+ 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'