time_ = time
+def _get_dt_and_tzinfo(dt_or_tzinfo):
+ """
+ Parse a `dt_or_tzinfo` value into a datetime and a tzinfo.
+
+ See the docs for this function's callers for semantics.
+
+ :rtype: tuple[datetime, tzinfo]
+ """
+ if dt_or_tzinfo is None:
+ dt = datetime.now()
+ tzinfo = LOCALTZ
+ elif isinstance(dt_or_tzinfo, string_types):
+ dt = None
+ tzinfo = get_timezone(dt_or_tzinfo)
+ elif isinstance(dt_or_tzinfo, integer_types):
+ dt = None
+ tzinfo = UTC
+ elif isinstance(dt_or_tzinfo, (datetime, time)):
+ dt = _get_datetime(dt_or_tzinfo)
+ if dt.tzinfo is not None:
+ tzinfo = dt.tzinfo
+ else:
+ tzinfo = UTC
+ else:
+ dt = None
+ tzinfo = dt_or_tzinfo
+ return dt, tzinfo
+
+
+def _get_datetime(instant):
+ """
+ Get a datetime out of an "instant" (date, time, datetime, number).
+
+ .. warning:: The return values of this function may depend on the system clock.
+
+ If the instant is None, the current moment is used.
+ If the instant is a time, it's augmented with today's date.
+
+ Dates are converted to naive datetimes with midnight as the time component.
+
+ >>> _get_datetime(date(2015, 1, 1))
+ datetime.datetime(2015, 1, 1, 0, 0)
+
+ UNIX timestamps are converted to datetimes.
+
+ >>> _get_datetime(1400000000)
+ datetime.datetime(2014, 5, 13, 16, 53, 20)
+
+ Other values are passed through as-is.
+
+ >>> x = datetime(2015, 1, 1)
+ >>> _get_datetime(x) is x
+ True
+
+ :param instant: date, time, datetime, integer, float or None
+ :type instant: date|time|datetime|int|float|None
+ :return: a datetime
+ :rtype: datetime
+ """
+ if instant is None:
+ return datetime_.utcnow()
+ elif isinstance(instant, integer_types) or isinstance(instant, float):
+ return datetime_.utcfromtimestamp(instant)
+ elif isinstance(instant, time):
+ return datetime_.combine(date.today(), instant)
+ elif isinstance(instant, date) and not isinstance(instant, datetime):
+ return datetime_.combine(instant, time())
+ # TODO (3.x): Add an assertion/type check for this fallthrough branch:
+ return instant
+
+
+def _ensure_datetime_tzinfo(datetime, tzinfo=None):
+ """
+ Ensure the datetime passed has an attached tzinfo.
+
+ If the datetime is tz-naive to begin with, UTC is attached.
+
+ If a tzinfo is passed in, the datetime is normalized to that timezone.
+
+ >>> _ensure_datetime_tzinfo(datetime(2015, 1, 1)).tzinfo.zone
+ 'UTC'
+
+ >>> tz = get_timezone("Europe/Stockholm")
+ >>> _ensure_datetime_tzinfo(datetime(2015, 1, 1, 13, 15, tzinfo=UTC), tzinfo=tz).hour
+ 14
+
+ :param datetime: Datetime to augment.
+ :param tzinfo: Optional tznfo.
+ :return: datetime with tzinfo
+ :rtype: datetime
+ """
+ if datetime.tzinfo is None:
+ datetime = datetime.replace(tzinfo=UTC)
+ if tzinfo is not None:
+ datetime = datetime.astimezone(get_timezone(tzinfo))
+ if hasattr(tzinfo, 'normalize'): # pytz
+ datetime = tzinfo.normalize(datetime)
+ return datetime
+
+
def get_timezone(zone=None):
"""Looks up a timezone by name and returns it. The timezone object
returned comes from ``pytz`` and corresponds to the `tzinfo` interface and
If not given the current time is assumed.
"""
zone = get_timezone(zone)
- if dt is None:
- dt = datetime.utcnow()
- else:
- dt = dt.replace(tzinfo=None)
+ dt = _get_datetime(dt).replace(tzinfo=None)
if not hasattr(zone, '_utc_transition_times'):
raise TypeError('Given timezone does not have UTC transition '
:param width: either "long" or "short"
:param locale: the `Locale` object, or a locale string
"""
- if datetime is None:
- datetime = datetime_.utcnow()
- elif isinstance(datetime, integer_types):
- datetime = datetime_.utcfromtimestamp(datetime).time()
- if datetime.tzinfo is None:
- datetime = datetime.replace(tzinfo=UTC)
+ datetime = _ensure_datetime_tzinfo(_get_datetime(datetime))
locale = Locale.parse(locale)
offset = datetime.tzinfo.utcoffset(datetime)
:param locale: the `Locale` object, or a locale string
:return: the localized timezone name using location format
"""
- if dt_or_tzinfo is None:
- dt = datetime.now()
- tzinfo = LOCALTZ
- elif isinstance(dt_or_tzinfo, string_types):
- dt = None
- tzinfo = get_timezone(dt_or_tzinfo)
- elif isinstance(dt_or_tzinfo, integer_types):
- dt = None
- tzinfo = UTC
- elif isinstance(dt_or_tzinfo, (datetime, time)):
- dt = dt_or_tzinfo
- if dt.tzinfo is not None:
- tzinfo = dt.tzinfo
- else:
- tzinfo = UTC
- else:
- dt = None
- tzinfo = dt_or_tzinfo
+ dt, tzinfo = _get_dt_and_tzinfo(dt_or_tzinfo)
locale = Locale.parse(locale)
if hasattr(tzinfo, 'zone'):
``'standard'``.
:param locale: the `Locale` object, or a locale string
"""
- if dt_or_tzinfo is None:
- dt = datetime.now()
- tzinfo = LOCALTZ
- elif isinstance(dt_or_tzinfo, string_types):
- dt = None
- tzinfo = get_timezone(dt_or_tzinfo)
- elif isinstance(dt_or_tzinfo, integer_types):
- dt = None
- tzinfo = UTC
- elif isinstance(dt_or_tzinfo, (datetime, time)):
- dt = dt_or_tzinfo
- if dt.tzinfo is not None:
- tzinfo = dt.tzinfo
- else:
- tzinfo = UTC
- else:
- dt = None
- tzinfo = dt_or_tzinfo
+ dt, tzinfo = _get_dt_and_tzinfo(dt_or_tzinfo)
locale = Locale.parse(locale)
if hasattr(tzinfo, 'zone'):
:param tzinfo: the timezone to apply to the time for display
:param locale: a `Locale` object or a locale identifier
"""
- if datetime is None:
- datetime = datetime_.utcnow()
- elif isinstance(datetime, number_types):
- datetime = datetime_.utcfromtimestamp(datetime)
- elif isinstance(datetime, time):
- datetime = datetime_.combine(date.today(), datetime)
- if datetime.tzinfo is None:
- datetime = datetime.replace(tzinfo=UTC)
- if tzinfo is not None:
- datetime = datetime.astimezone(get_timezone(tzinfo))
- if hasattr(tzinfo, 'normalize'): # pytz
- datetime = tzinfo.normalize(datetime)
+ datetime = _ensure_datetime_tzinfo(_get_datetime(datetime), tzinfo)
locale = Locale.parse(locale)
if format in ('full', 'long', 'medium', 'short'):
u'Mexiko (Mexiko-Stadt) Zeit')
tz = timezone('Europe/Berlin')
- assert (dates.get_timezone_name(tz, locale='de_DE') ==
- u'Mitteleurop\xe4ische Zeit')
+ assert (dates.get_timezone_location(tz, locale='de_DE') ==
+ u'Deutschland (Berlin) Zeit')
def test_get_timezone_name():
assert dates.get_timezone_name(tz, locale='en', width='long',
zone_variant='daylight') == u'Pacific Daylight Time'
+ assert (dates.get_timezone_name(None, locale='en_US') ==
+ dates.get_timezone_name(datetime.now().replace(tzinfo=dates.LOCALTZ), locale='en_US'))
+
+ assert (dates.get_timezone_name('Europe/Berlin', locale='en_US') == "Central European Time")
+
+ assert (dates.get_timezone_name(1400000000, locale='en_US', width='short') == "Unknown Region (GMT) Time")
+ assert (dates.get_timezone_name(time(16, 20), locale='en_US', width='short') == "+0000")
+
def test_format_date():
d = date(2007, 4, 1)
dates.format_date(date(2015, 12, 10), locale='lt_LT', format='long') ==
u'2015 m. gruodÅžio 10 d.'
)
+
+
+def test_format_current_moment(monkeypatch):
+ import datetime as datetime_module
+ frozen_instant = datetime.utcnow()
+
+ class frozen_datetime(datetime):
+ @classmethod
+ def utcnow(cls):
+ return frozen_instant
+
+ # Freeze time! Well, some of it anyway.
+ monkeypatch.setattr(datetime_module, "datetime", frozen_datetime)
+ assert dates.format_datetime(locale="en_US") == dates.format_datetime(frozen_instant, locale="en_US")