def _parse_date_iso8601(text, tzinfo): match = _ISO_8601_RE.match(text) if match: try: g = match.groups() years = g[0] months = g[1] or '01' days = g[2] or '01' hours, minutes, seconds = [x or '00' for x in g[3:6]] z, tzsign, tzhours, tzminutes = g[6:10] if z: tz = timedelta(hours=int(tzhours or '0'), minutes=int(tzminutes or '0')).seconds / 60 if tz == 0: tzinfo = utc else: tzinfo = FixedOffset(-tz if tzsign == '-' else tz, '%s%s:%s' % (tzsign, tzhours, tzminutes)) tm = time.strptime('%s ' * 6 % (years, months, days, hours, minutes, seconds), '%Y %m %d %H %M %S ') t = tzinfo.localize(datetime(*tm[0:6])) return tzinfo.normalize(t) except ValueError: pass return None
def _parse_date_iso8601(text, tzinfo): match = _ISO_8601_RE.match(text) if match: try: g = match.groups() years = g[0] months = g[1] or '01' days = g[2] or '01' hours, minutes, seconds, useconds = [x or '00' for x in g[3:7]] useconds = (useconds + '000000')[:6] z, tzsign, tzhours, tzminutes = g[7:11] if z: tz = timedelta(hours=int(tzhours or '0'), minutes=int(tzminutes or '0')).seconds / 60 if tz == 0: tzinfo = utc else: tzinfo = FixedOffset(-tz if tzsign == '-' else tz, '%s%s:%s' % (tzsign, tzhours, tzminutes)) tm = [int(x) for x in (years, months, days, hours, minutes, seconds, useconds)] t = tzinfo.localize(datetime(*tm)) return tzinfo.normalize(t) except ValueError: pass return None
def localize(self, dt, is_dst=False): '''Convert naive time to local time. This method should be used to construct localtimes, rather than passing a tzinfo argument to a datetime constructor. is_dst is used to determine the correct timezone in the ambigous period at the end of daylight savings time. >>> from pytz import timezone >>> fmt = '%Y-%m-%d %H:%M:%S %Z (%z)' >>> amdam = timezone('Europe/Amsterdam') >>> dt = datetime(2004, 10, 31, 2, 0, 0) >>> loc_dt1 = amdam.localize(dt, is_dst=True) >>> loc_dt2 = amdam.localize(dt, is_dst=False) >>> loc_dt1.strftime(fmt) '2004-10-31 02:00:00 CEST (+0200)' >>> loc_dt2.strftime(fmt) '2004-10-31 02:00:00 CET (+0100)' >>> str(loc_dt2 - loc_dt1) '1:00:00' Use is_dst=None to raise an AmbiguousTimeError for ambiguous times at the end of daylight savings >>> try: ... loc_dt1 = amdam.localize(dt, is_dst=None) ... except AmbiguousTimeError: ... print 'Oops' Oops >>> loc_dt1 = amdam.localize(dt, is_dst=None) Traceback (most recent call last): [...] AmbiguousTimeError: 2004-10-31 02:00:00 is_dst defaults to False >>> amdam.localize(dt) == amdam.localize(dt, False) True ''' if dt.tzinfo is not None: raise ValueError, 'Not naive datetime (tzinfo is already set)' possible_loc_dt = set() for tzinfo in self._tzinfos.values(): loc_dt = tzinfo.normalize(dt.replace(tzinfo=tzinfo)) if loc_dt.replace(tzinfo=None) == dt: possible_loc_dt.add(loc_dt) if len(possible_loc_dt) == 1: return possible_loc_dt.pop() if is_dst is None: raise AmbiguousTimeError(dt) filtered_possible_loc_dt = [ p for p in possible_loc_dt if bool(p.tzinfo._dst) == is_dst ] if len(filtered_possible_loc_dt) == 1: return filtered_possible_loc_dt[0] if len(filtered_possible_loc_dt) == 0: filtered_possible_loc_dt = list(possible_loc_dt) def mycmp(a,b): return cmp( a.replace(tzinfo=None) - a.tzinfo._utcoffset, b.replace(tzinfo=None) - b.tzinfo._utcoffset, ) filtered_possible_loc_dt.sort(mycmp) return filtered_possible_loc_dt[0]
def localize(dt, normalize=False, tz='America/New_York'): """ @param normalize: bool. If not normalize, just changes the timezone information. This changes the associated time, so use it carefully. For proper localization set normalize to TRUE """ tzinfo = pytz.timezone(tz) if normalize: return tzinfo.normalize(dt) return dt.replace(tzinfo=tzinfo)
def _parse_relative_time(text, tzinfo, now=None): if now is None: # now argument for unit tests now = datetime.now(tzinfo) if text == 'now': return now dt = None if text == 'today': dt = _time_starts['day'](now) elif text == 'yesterday': dt = _time_starts['day'](now) - timedelta(days=1) elif text == 'tomorrow': dt = _time_starts['day'](now) + timedelta(days=1) if dt is None: match = _REL_FUTURE_RE.match(text) if match: (value, interval) = match.groups() dt = now + _time_intervals[interval](float(value)) if dt is None: match = _REL_PAST_RE.match(text) if match: (value, interval) = match.groups() dt = now - _time_intervals[interval](float(value)) if dt is None: match = _TIME_START_RE.match(text) if match: (which, start) = match.groups() dt = _time_starts[start](now) if which == 'last': if start == 'month': if dt.month > 1: dt = dt.replace(month=dt.month - 1) else: dt = dt.replace(year=dt.year - 1, month=12) elif start == 'year': dt = dt.replace(year=dt.year - 1) else: dt -= _time_intervals[start](1) elif which == 'next': if start == 'month': if dt.month < 12: dt = dt.replace(month=dt.month + 1) else: dt = dt.replace(year=dt.year + 1, month=1) elif start == 'year': dt = dt.replace(year=dt.year + 1) else: dt += _time_intervals[start](1) if dt is None: return None if not dt.tzinfo: dt = tzinfo.localize(dt) return tzinfo.normalize(dt)
def _i18n_parse_date_0(text, order, regexp, period_names, month_names, tzinfo): matches = regexp.findall(text) if not matches: return None # remove am/pm markers on ahead period = None for idx, match in enumerate(matches): period = period_names.get(match) if period is not None: del matches[idx] break # for date+time, use 0 seconds if seconds are missing if 's' in order and len(matches) == 5: matches.insert(order['s'], 0) values = {} for key, idx in order.iteritems(): if idx < len(matches): value = matches[idx] if key == 'y': if len(value) == 2 and value.isdigit(): value = '20' + value values[key] = value if 'y' not in values or 'M' not in values or 'd' not in values: raise ValueError for key in ('y', 'M', 'd'): value = values[key] value = month_names.get(value) if value is not None: if key == 'M': values[key] = value else: values[key], values['M'] = values['M'], value break values = dict((key, int(value)) for key, value in values.iteritems()) values.setdefault('h', 0) values.setdefault('m', 0) values.setdefault('s', 0) if period and values['h'] <= 12: if period == 'am': values['h'] %= 12 elif period == 'pm': values['h'] = values['h'] % 12 + 12 t = tzinfo.localize(datetime(*(values[k] for k in 'yMdhms'))) return tzinfo.normalize(t)
def _libc_parse_date(text, tzinfo): for format in ('%x %X', '%x, %X', '%X %x', '%X, %x', '%x', '%c', '%b %d, %Y'): try: tm = time.strptime(text, format) dt = tzinfo.localize(datetime(*tm[0:6])) return tzinfo.normalize(dt) except ValueError: continue try: return _i18n_parse_date(text, tzinfo, None) except ValueError: pass return
def format_datetime(datetime=None, format='medium', tzinfo=None, locale=LC_TIME): """Return a date formatted according to the given pattern. >>> dt = datetime(2007, 04, 01, 15, 30) >>> format_datetime(dt, locale='en_US') u'Apr 1, 2007 3:30:00 PM' For any pattern requiring the display of the time-zone, the third-party ``pytz`` package is needed to explicitly specify the time-zone: >>> from pytz import timezone >>> format_datetime(dt, 'full', tzinfo=timezone('Europe/Paris'), ... locale='fr_FR') u'dimanche 1 avril 2007 17:30:00 HEC' >>> format_datetime(dt, "yyyy.MM.dd G 'at' HH:mm:ss zzz", ... tzinfo=timezone('US/Eastern'), locale='en') u'2007.04.01 AD at 11:30:00 EDT' :param datetime: the `datetime` object; if `None`, the current date and time is used :param format: one of "full", "long", "medium", or "short", or a custom date/time pattern :param tzinfo: the timezone to apply to the time for display :param locale: a `Locale` object or a locale identifier :rtype: `unicode` """ if datetime is None: datetime = datetime_.utcnow() elif isinstance(datetime, (int, long)): 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(tzinfo) if hasattr(tzinfo, 'normalize'): # pytz datetime = tzinfo.normalize(datetime) locale = Locale.parse(locale) if format in ('full', 'long', 'medium', 'short'): return get_datetime_format(format, locale=locale) \ .replace('{0}', format_time(datetime, format, tzinfo=None, locale=locale)) \ .replace('{1}', format_date(datetime, format, locale=locale)) else: return parse_pattern(format).apply(datetime, locale)
def to_datetime(t, tzinfo=None): """Convert `t` into a `datetime` object, using the following rules: - If `t` is already a `datetime` object and `tzinfo` is None, it is simply returned. - If `t` is already a `datetime` object and `tzinfo` is not None, it will adjust `t` to `tzinfo` timezone. - If `t` is None, the current time will be used. - If `t` is a number, it is interpreted as a timestamp. If no `tzinfo` is given, the local timezone will be used. Any other input will trigger a `TypeError`. """ if t is None: return datetime.now(tzinfo or localtz) elif isinstance(t, datetime): if tzinfo is not None: if t.tzinfo is None: t = t.replace(tzinfo=tzinfo) else: t = tzinfo.normalize(t.astimezone(tzinfo)) return t elif isinstance(t, date): tz = tzinfo or localtz t = tz.localize(datetime(t.year, t.month, t.day)) return tz.normalize(t) elif isinstance(t, (int, long, float)): if not (_min_ts <= t <= _max_ts): # Handle microsecond timestamps for 0.11 compatibility t *= 0.000001 if t < 0 and isinstance(t, float): # Work around negative fractional times bug in Python 2.4 # http://bugs.python.org/issue1646728 frac, integer = math.modf(t) return datetime.fromtimestamp(integer - 1, tzinfo or localtz) \ + timedelta(seconds=frac + 1) return datetime.fromtimestamp(t, tzinfo or localtz) raise TypeError('expecting datetime, int, long, float, or None; got %s' % type(t))
def parse_date(text, tzinfo=None, locale=None, hint='date'): tzinfo = tzinfo or localtz text = text.strip() dt = _parse_date_iso8601(text, tzinfo) if dt is None and locale != 'iso8601': if babel and locale: dt = _i18n_parse_date(text, tzinfo, locale) else: for format in ['%x %X', '%x, %X', '%X %x', '%X, %x', '%x', '%c', '%b %d, %Y']: try: tm = time.strptime(text, format) dt = tzinfo.localize(datetime(*tm[0:6])) dt = tzinfo.normalize(dt) break except ValueError: continue if dt is None: dt = _parse_relative_time(text, tzinfo) if dt is None: hint = {'datetime': get_datetime_format_hint, 'date': get_date_format_hint, 'relative': get_datetime_format_hint }.get(hint, lambda(l): hint)(locale) raise TracError(_('"%(date)s" is an invalid date, or the date format ' 'is not known. Try "%(hint)s" instead.', date=text, hint=hint), _('Invalid Date')) # Make sure we can convert it to a timestamp and back - fromtimestamp() # may raise ValueError if larger than platform C localtime() or gmtime() try: datetime.utcfromtimestamp(to_timestamp(dt)) except ValueError: raise TracError(_('The date "%(date)s" is outside valid range. ' 'Try a date closer to present time.', date=text), _('Invalid Date')) return dt
def localize(self, dt, is_dst = False): if dt.tzinfo is not None: raise ValueError, 'Not naive datetime (tzinfo is already set)' possible_loc_dt = Set() for tzinfo in self._tzinfos.values(): loc_dt = tzinfo.normalize(dt.replace(tzinfo=tzinfo)) if loc_dt.replace(tzinfo=None) == dt: possible_loc_dt.add(loc_dt) if len(possible_loc_dt) == 1: return possible_loc_dt.pop() if is_dst is None: raise AmbiguousTimeError(dt) filtered_possible_loc_dt = [ p for p in possible_loc_dt if bool(p.tzinfo._dst) == is_dst ] if len(filtered_possible_loc_dt) == 1: return filtered_possible_loc_dt[0] if len(filtered_possible_loc_dt) == 0: filtered_possible_loc_dt = list(possible_loc_dt) def mycmp(a, b): return cmp(a.replace(tzinfo=None) - a.tzinfo._utcoffset, b.replace(tzinfo=None) - b.tzinfo._utcoffset) filtered_possible_loc_dt.sort(mycmp) return filtered_possible_loc_dt[0]
def localize(self, dt, is_dst = False): """Convert naive time to local time. This method should be used to construct localtimes, rather than passing a tzinfo argument to a datetime constructor. is_dst is used to determine the correct timezone in the ambigous period at the end of daylight saving time. >>> from pytz import timezone >>> fmt = '%Y-%m-%d %H:%M:%S %Z (%z)' >>> amdam = timezone('Europe/Amsterdam') >>> dt = datetime(2004, 10, 31, 2, 0, 0) >>> loc_dt1 = amdam.localize(dt, is_dst=True) >>> loc_dt2 = amdam.localize(dt, is_dst=False) >>> loc_dt1.strftime(fmt) '2004-10-31 02:00:00 CEST (+0200)' >>> loc_dt2.strftime(fmt) '2004-10-31 02:00:00 CET (+0100)' >>> str(loc_dt2 - loc_dt1) '1:00:00' Use is_dst=None to raise an AmbiguousTimeError for ambiguous times at the end of daylight saving time >>> try: ... loc_dt1 = amdam.localize(dt, is_dst=None) ... except AmbiguousTimeError: ... print('Ambiguous') Ambiguous is_dst defaults to False >>> amdam.localize(dt) == amdam.localize(dt, False) True is_dst is also used to determine the correct timezone in the wallclock times jumped over at the start of daylight saving time. >>> pacific = timezone('US/Pacific') >>> dt = datetime(2008, 3, 9, 2, 0, 0) >>> ploc_dt1 = pacific.localize(dt, is_dst=True) >>> ploc_dt2 = pacific.localize(dt, is_dst=False) >>> ploc_dt1.strftime(fmt) '2008-03-09 02:00:00 PDT (-0700)' >>> ploc_dt2.strftime(fmt) '2008-03-09 02:00:00 PST (-0800)' >>> str(ploc_dt2 - ploc_dt1) '1:00:00' Use is_dst=None to raise a NonExistentTimeError for these skipped times. >>> try: ... loc_dt1 = pacific.localize(dt, is_dst=None) ... except NonExistentTimeError: ... print('Non-existent') Non-existent """ if dt.tzinfo is not None: raise ValueError('Not naive datetime (tzinfo is already set)') possible_loc_dt = set() for delta in [timedelta(days=-1), timedelta(days=1)]: loc_dt = dt + delta idx = max(0, bisect_right(self._utc_transition_times, loc_dt) - 1) inf = self._transition_info[idx] tzinfo = self._tzinfos[inf] loc_dt = tzinfo.normalize(dt.replace(tzinfo=tzinfo)) if loc_dt.replace(tzinfo=None) == dt: possible_loc_dt.add(loc_dt) if len(possible_loc_dt) == 1: return possible_loc_dt.pop() if len(possible_loc_dt) == 0: if is_dst is None: raise NonExistentTimeError(dt) elif is_dst: return self.localize(dt + timedelta(hours=6), is_dst=True) - timedelta(hours=6) else: return self.localize(dt - timedelta(hours=6), is_dst=False) + timedelta(hours=6) if is_dst is None: raise AmbiguousTimeError(dt) filtered_possible_loc_dt = [ p for p in possible_loc_dt if bool(p.tzinfo._dst) == is_dst ] if len(filtered_possible_loc_dt) == 1: return filtered_possible_loc_dt[0] if len(filtered_possible_loc_dt) == 0: filtered_possible_loc_dt = list(possible_loc_dt) sorting_keys = {} for local_dt in filtered_possible_loc_dt: key = local_dt.replace(tzinfo=None) - local_dt.tzinfo._utcoffset sorting_keys[key] = local_dt first_key = sorted(sorting_keys)[0] return sorting_keys[first_key]
def format_time(time=None, format='medium', tzinfo=None, locale=LC_TIME): """Return a time formatted according to the given pattern. >>> t = time(15, 30) >>> format_time(t, locale='en_US') u'3:30:00 PM' >>> format_time(t, format='short', locale='de_DE') u'15:30' If you don't want to use the locale default formats, you can specify a custom time pattern: >>> format_time(t, "hh 'o''clock' a", locale='en') u"03 o'clock PM" For any pattern requiring the display of the time-zone, the third-party ``pytz`` package is needed to explicitly specify the time-zone: >>> from pytz import timezone >>> t = datetime(2007, 4, 1, 15, 30) >>> tzinfo = timezone('Europe/Paris') >>> t = tzinfo.localize(t) >>> format_time(t, format='full', tzinfo=tzinfo, locale='fr_FR') u'15:30:00 HEC' >>> format_time(t, "hh 'o''clock' a, zzzz", tzinfo=timezone('US/Eastern'), ... locale='en') u"09 o'clock AM, Eastern Daylight Time" As that example shows, when this function gets passed a ``datetime.datetime`` value, the actual time in the formatted string is adjusted to the timezone specified by the `tzinfo` parameter. If the ``datetime`` is "naive" (i.e. it has no associated timezone information), it is assumed to be in UTC. These timezone calculations are **not** performed if the value is of type ``datetime.time``, as without date information there's no way to determine what a given time would translate to in a different timezone without information about whether daylight savings time is in effect or not. This means that time values are left as-is, and the value of the `tzinfo` parameter is only used to display the timezone name if needed: >>> t = time(15, 30) >>> format_time(t, format='full', tzinfo=timezone('Europe/Paris'), ... locale='fr_FR') u'15:30:00 HEC' >>> format_time(t, format='full', tzinfo=timezone('US/Eastern'), ... locale='en_US') u'3:30:00 PM ET' :param time: the ``time`` or ``datetime`` object; if `None`, the current time in UTC is used :param format: one of "full", "long", "medium", or "short", or a custom date/time pattern :param tzinfo: the time-zone to apply to the time for display :param locale: a `Locale` object or a locale identifier :rtype: `unicode` :note: If the pattern contains date fields, an `AttributeError` will be raised when trying to apply the formatting. This is also true if the value of ``time`` parameter is actually a ``datetime`` object, as this function automatically converts that to a ``time``. """ if time is None: time = datetime.utcnow() elif isinstance(time, (int, long)): time = datetime.utcfromtimestamp(time) if time.tzinfo is None: time = time.replace(tzinfo=UTC) if isinstance(time, datetime): if tzinfo is not None: time = time.astimezone(tzinfo) if hasattr(tzinfo, 'normalize'): # pytz time = tzinfo.normalize(time) time = time.timetz() elif tzinfo is not None: time = time.replace(tzinfo=tzinfo) locale = Locale.parse(locale) if format in ('full', 'long', 'medium', 'short'): format = get_time_format(format, locale=locale) return parse_pattern(format).apply(time, locale)
def localize(self, dt, is_dst=False): '''Convert naive time to local time. This method should be used to construct localtimes, rather than passing a tzinfo argument to a datetime constructor. is_dst is used to determine the correct timezone in the ambigous period at the end of daylight savings time. >>> from pytz import timezone >>> fmt = '%Y-%m-%d %H:%M:%S %Z (%z)' >>> amdam = timezone('Europe/Amsterdam') >>> dt = datetime(2004, 10, 31, 2, 0, 0) >>> loc_dt1 = amdam.localize(dt, is_dst=True) >>> loc_dt2 = amdam.localize(dt, is_dst=False) >>> loc_dt1.strftime(fmt) '2004-10-31 02:00:00 CEST (+0200)' >>> loc_dt2.strftime(fmt) '2004-10-31 02:00:00 CET (+0100)' >>> str(loc_dt2 - loc_dt1) '1:00:00' Use is_dst=None to raise an AmbiguousTimeError for ambiguous times at the end of daylight savings >>> try: ... loc_dt1 = amdam.localize(dt, is_dst=None) ... except AmbiguousTimeError: ... print 'Oops' Oops >>> loc_dt1 = amdam.localize(dt, is_dst=None) Traceback (most recent call last): [...] AmbiguousTimeError: 2004-10-31 02:00:00 is_dst defaults to False >>> amdam.localize(dt) == amdam.localize(dt, False) True ''' if dt.tzinfo is not None: raise ValueError, 'Not naive datetime (tzinfo is already set)' # Find the possibly correct timezones. We probably just have one, # but we might end up with two if we are in the end-of-DST # transition period. Or possibly more in some particularly confused # location... possible_loc_dt = set() for tzinfo in self._tzinfos.values(): loc_dt = tzinfo.normalize(dt.replace(tzinfo=tzinfo)) if loc_dt.replace(tzinfo=None) == dt: possible_loc_dt.add(loc_dt) if len(possible_loc_dt) == 1: return possible_loc_dt.pop() # If told to be strict, raise an exception since we have an # ambiguous case if is_dst is None: raise AmbiguousTimeError(dt) # Filter out the possiblilities that don't match the requested # is_dst filtered_possible_loc_dt = [ p for p in possible_loc_dt if bool(p.tzinfo._dst) == is_dst ] # Hopefully we only have one possibility left. Return it. if len(filtered_possible_loc_dt) == 1: return filtered_possible_loc_dt[0] if len(filtered_possible_loc_dt) == 0: filtered_possible_loc_dt = list(possible_loc_dt) # If we get this far, we have in a wierd timezone transition # where the clocks have been wound back but is_dst is the same # in both (eg. Europe/Warsaw 1915 when they switched to CET). # At this point, we just have to guess unless we allow more # hints to be passed in (such as the UTC offset or abbreviation), # but that is just getting silly. # # Choose the earliest (by UTC) applicable timezone. def mycmp(a,b): return cmp( a.replace(tzinfo=None) - a.tzinfo._utcoffset, b.replace(tzinfo=None) - b.tzinfo._utcoffset, ) filtered_possible_loc_dt.sort(mycmp) return filtered_possible_loc_dt[0]
def localize(self, dt, is_dst=False): '''Convert naive time to local time. This method should be used to construct localtimes, rather than passing a tzinfo argument to a datetime constructor. is_dst is used to determine the correct timezone in the ambigous period at the end of daylight savings time. >>> from zpt._pytz import timezone >>> fmt = '%Y-%m-%d %H:%M:%S %Z (%z)' >>> amdam = timezone('Europe/Amsterdam') >>> dt = datetime(2004, 10, 31, 2, 0, 0) >>> loc_dt1 = amdam.localize(dt, is_dst=True) >>> loc_dt2 = amdam.localize(dt, is_dst=False) >>> loc_dt1.strftime(fmt) '2004-10-31 02:00:00 CEST (+0200)' >>> loc_dt2.strftime(fmt) '2004-10-31 02:00:00 CET (+0100)' >>> str(loc_dt2 - loc_dt1) '1:00:00' Use is_dst=None to raise an AmbiguousTimeError for ambiguous times at the end of daylight savings >>> try: ... loc_dt1 = amdam.localize(dt, is_dst=None) ... except AmbiguousTimeError: ... print 'Oops' Oops >>> loc_dt1 = amdam.localize(dt, is_dst=None) Traceback (most recent call last): [...] AmbiguousTimeError: 2004-10-31 02:00:00 is_dst defaults to False >>> amdam.localize(dt) == amdam.localize(dt, False) True ''' if dt.tzinfo is not None: raise ValueError, 'Not naive datetime (tzinfo is already set)' # Find the possibly correct timezones. We probably just have one, # but we might end up with two if we are in the end-of-DST # transition period. Or possibly more in some particularly confused # location... possible_loc_dt = Set() for tzinfo in self._tzinfos.values(): loc_dt = tzinfo.normalize(dt.replace(tzinfo=tzinfo)) if loc_dt.replace(tzinfo=None) == dt: possible_loc_dt.add(loc_dt) if len(possible_loc_dt) == 1: return possible_loc_dt.pop() # If told to be strict, raise an exception since we have an # ambiguous case if is_dst is None: raise AmbiguousTimeError(dt) # Filter out the possiblilities that don't match the requested # is_dst filtered_possible_loc_dt = [ p for p in possible_loc_dt if bool(p.tzinfo._dst) == is_dst ] # Hopefully we only have one possibility left. Return it. if len(filtered_possible_loc_dt) == 1: return filtered_possible_loc_dt[0] if len(filtered_possible_loc_dt) == 0: filtered_possible_loc_dt = list(possible_loc_dt) # If we get this far, we have in a wierd timezone transition # where the clocks have been wound back but is_dst is the same # in both (eg. Europe/Warsaw 1915 when they switched to CET). # At this point, we just have to guess unless we allow more # hints to be passed in (such as the UTC offset or abbreviation), # but that is just getting silly. # # Choose the earliest (by UTC) applicable timezone. def mycmp(a,b): return cmp( a.replace(tzinfo=None) - a.tzinfo._utcoffset, b.replace(tzinfo=None) - b.tzinfo._utcoffset, ) filtered_possible_loc_dt.sort(mycmp) return filtered_possible_loc_dt[0]
def normalize(self, ts): tzinfo = ts.tzinfo return tzinfo.normalize(ts)
def format_time(time=None, format='medium', tzinfo=None, locale=LC_TIME): r"""Return a time formatted according to the given pattern. >>> t = time(15, 30) >>> format_time(t, locale='en_US') u'3:30:00 PM' >>> format_time(t, format='short', locale='de_DE') u'15:30' If you don't want to use the locale default formats, you can specify a custom time pattern: >>> format_time(t, "hh 'o''clock' a", locale='en') u"03 o'clock PM" For any pattern requiring the display of the time-zone, the third-party ``pytz`` package is needed to explicitly specify the time-zone: >>> from pytz import timezone >>> t = datetime(2007, 4, 1, 15, 30) >>> tzinfo = timezone('Europe/Paris') >>> t = tzinfo.localize(t) >>> format_time(t, format='full', tzinfo=tzinfo, locale='fr_FR') u'15:30:00 Heure avanc\xe9e de l\u2019Europe centrale' >>> format_time(t, "hh 'o''clock' a, zzzz", tzinfo=timezone('US/Eastern'), ... locale='en') u"09 o'clock AM, Eastern Daylight Time" As that example shows, when this function gets passed a ``datetime.datetime`` value, the actual time in the formatted string is adjusted to the timezone specified by the `tzinfo` parameter. If the ``datetime`` is "naive" (i.e. it has no associated timezone information), it is assumed to be in UTC. These timezone calculations are **not** performed if the value is of type ``datetime.time``, as without date information there's no way to determine what a given time would translate to in a different timezone without information about whether daylight savings time is in effect or not. This means that time values are left as-is, and the value of the `tzinfo` parameter is only used to display the timezone name if needed: >>> t = time(15, 30) >>> format_time(t, format='full', tzinfo=timezone('Europe/Paris'), ... locale='fr_FR') u'15:30:00 Heure normale de l\u2019Europe centrale' >>> format_time(t, format='full', tzinfo=timezone('US/Eastern'), ... locale='en_US') u'3:30:00 PM Eastern Standard Time' :param time: the ``time`` or ``datetime`` object; if `None`, the current time in UTC is used :param format: one of "full", "long", "medium", or "short", or a custom date/time pattern :param tzinfo: the time-zone to apply to the time for display :param locale: a `Locale` object or a locale identifier :rtype: `unicode` :note: If the pattern contains date fields, an `AttributeError` will be raised when trying to apply the formatting. This is also true if the value of ``time`` parameter is actually a ``datetime`` object, as this function automatically converts that to a ``time``. """ if time is None: time = datetime.utcnow() elif isinstance(time, (int, long)): time = datetime.utcfromtimestamp(time) if time.tzinfo is None: time = time.replace(tzinfo=UTC) if isinstance(time, datetime): if tzinfo is not None: time = time.astimezone(tzinfo) if hasattr(tzinfo, 'normalize'): # pytz time = tzinfo.normalize(time) time = time.timetz() elif tzinfo is not None: time = time.replace(tzinfo=tzinfo) locale = Locale.parse(locale) if format in ('full', 'long', 'medium', 'short'): format = get_time_format(format, locale=locale) return parse_pattern(format).apply(time, locale)
def localize(self, dt, is_dst=False): '''Convert naive time to local time. This method should be used to construct localtimes, rather than passing a tzinfo argument to a datetime constructor. is_dst is used to determine the correct timezone in the ambigous period at the end of daylight saving time. >>> from pytz import timezone >>> fmt = '%Y-%m-%d %H:%M:%S %Z (%z)' >>> amdam = timezone('Europe/Amsterdam') >>> dt = datetime(2004, 10, 31, 2, 0, 0) >>> loc_dt1 = amdam.localize(dt, is_dst=True) >>> loc_dt2 = amdam.localize(dt, is_dst=False) >>> loc_dt1.strftime(fmt) '2004-10-31 02:00:00 CEST (+0200)' >>> loc_dt2.strftime(fmt) '2004-10-31 02:00:00 CET (+0100)' >>> str(loc_dt2 - loc_dt1) '1:00:00' Use is_dst=None to raise an AmbiguousTimeError for ambiguous times at the end of daylight saving time >>> try: ... loc_dt1 = amdam.localize(dt, is_dst=None) ... except AmbiguousTimeError: ... print('Ambiguous') Ambiguous is_dst defaults to False >>> amdam.localize(dt) == amdam.localize(dt, False) True is_dst is also used to determine the correct timezone in the wallclock times jumped over at the start of daylight saving time. >>> pacific = timezone('US/Pacific') >>> dt = datetime(2008, 3, 9, 2, 0, 0) >>> ploc_dt1 = pacific.localize(dt, is_dst=True) >>> ploc_dt2 = pacific.localize(dt, is_dst=False) >>> ploc_dt1.strftime(fmt) '2008-03-09 02:00:00 PDT (-0700)' >>> ploc_dt2.strftime(fmt) '2008-03-09 02:00:00 PST (-0800)' >>> str(ploc_dt2 - ploc_dt1) '1:00:00' Use is_dst=None to raise a NonExistentTimeError for these skipped times. >>> try: ... loc_dt1 = pacific.localize(dt, is_dst=None) ... except NonExistentTimeError: ... print('Non-existent') Non-existent ''' if dt.tzinfo is not None: raise ValueError('Not naive datetime (tzinfo is already set)') # Find the two best possibilities. possible_loc_dt = set() for delta in [timedelta(days=-1), timedelta(days=1)]: loc_dt = dt + delta idx = max(0, bisect_right( self._utc_transition_times, loc_dt) - 1) inf = self._transition_info[idx] tzinfo = self._tzinfos[inf] loc_dt = tzinfo.normalize(dt.replace(tzinfo=tzinfo)) if loc_dt.replace(tzinfo=None) == dt: possible_loc_dt.add(loc_dt) if len(possible_loc_dt) == 1: return possible_loc_dt.pop() # If there are no possibly correct timezones, we are attempting # to convert a time that never happened - the time period jumped # during the start-of-DST transition period. if len(possible_loc_dt) == 0: # If we refuse to guess, raise an exception. if is_dst is None: raise NonExistentTimeError(dt) # If we are forcing the pre-DST side of the DST transition, we # obtain the correct timezone by winding the clock forward a few # hours. elif is_dst: return self.localize( dt + timedelta(hours=6), is_dst=True) - timedelta(hours=6) # If we are forcing the post-DST side of the DST transition, we # obtain the correct timezone by winding the clock back. else: return self.localize( dt - timedelta(hours=6), is_dst=False) + timedelta(hours=6) # If we get this far, we have multiple possible timezones - this # is an ambiguous case occuring during the end-of-DST transition. # If told to be strict, raise an exception since we have an # ambiguous case if is_dst is None: raise AmbiguousTimeError(dt) # Filter out the possiblilities that don't match the requested # is_dst filtered_possible_loc_dt = [ p for p in possible_loc_dt if bool(p.tzinfo._dst) == is_dst ] # Hopefully we only have one possibility left. Return it. if len(filtered_possible_loc_dt) == 1: return filtered_possible_loc_dt[0] if len(filtered_possible_loc_dt) == 0: filtered_possible_loc_dt = list(possible_loc_dt) # If we get this far, we have in a wierd timezone transition # where the clocks have been wound back but is_dst is the same # in both (eg. Europe/Warsaw 1915 when they switched to CET). # At this point, we just have to guess unless we allow more # hints to be passed in (such as the UTC offset or abbreviation), # but that is just getting silly. # # Choose the earliest (by UTC) applicable timezone. sorting_keys = {} for local_dt in filtered_possible_loc_dt: key = local_dt.replace(tzinfo=None) - local_dt.tzinfo._utcoffset sorting_keys[key] = local_dt first_key = sorted(sorting_keys)[0] return sorting_keys[first_key]
def localize(self, dt, is_dst=False): '''Convert naive time to local time. This method should be used to construct localtimes, rather than passing a tzinfo argument to a datetime constructor. is_dst is used to determine the correct timezone in the ambigous period at the end of daylight savings time. >>> from pytz import timezone >>> fmt = '%Y-%m-%d %H:%M:%S %Z (%z)' >>> amdam = timezone('Europe/Amsterdam') >>> dt = datetime(2004, 10, 31, 2, 0, 0) >>> loc_dt1 = amdam.localize(dt, is_dst=True) >>> loc_dt2 = amdam.localize(dt, is_dst=False) >>> loc_dt1.strftime(fmt) '2004-10-31 02:00:00 CEST (+0200)' >>> loc_dt2.strftime(fmt) '2004-10-31 02:00:00 CET (+0100)' >>> str(loc_dt2 - loc_dt1) '1:00:00' Use is_dst=None to raise an AmbiguousTimeError for ambiguous times at the end of daylight savings >>> loc_dt1 = amdam.localize(dt, is_dst=None) Traceback (most recent call last): [...] AmbiguousTimeError: 2004-10-31 02:00:00 is_dst defaults to False >>> amdam.localize(dt) == amdam.localize(dt, False) True is_dst is also used to determine the correct timezone in the wallclock times jumped over at the start of daylight savings time. >>> pacific = timezone('US/Pacific') >>> dt = datetime(2008, 3, 9, 2, 0, 0) >>> ploc_dt1 = pacific.localize(dt, is_dst=True) >>> ploc_dt2 = pacific.localize(dt, is_dst=False) >>> ploc_dt1.strftime(fmt) '2008-03-09 02:00:00 PDT (-0700)' >>> ploc_dt2.strftime(fmt) '2008-03-09 02:00:00 PST (-0800)' >>> str(ploc_dt2 - ploc_dt1) '1:00:00' Use is_dst=None to raise a NonExistentTimeError for these skipped times. >>> loc_dt1 = pacific.localize(dt, is_dst=None) Traceback (most recent call last): [...] NonExistentTimeError: 2008-03-09 02:00:00 ''' if dt.tzinfo is not None: raise ValueError, 'Not naive datetime (tzinfo is already set)' # Find the two best possibilities. possible_loc_dt = set() for delta in [timedelta(days=-1), timedelta(days=1)]: loc_dt = dt + delta idx = max(0, bisect_right(self._utc_transition_times, loc_dt) - 1) inf = self._transition_info[idx] tzinfo = self._tzinfos[inf] loc_dt = tzinfo.normalize(dt.replace(tzinfo=tzinfo)) if loc_dt.replace(tzinfo=None) == dt: possible_loc_dt.add(loc_dt) if len(possible_loc_dt) == 1: return possible_loc_dt.pop() # If there are no possibly correct timezones, we are attempting # to convert a time that never happened - the time period jumped # during the start-of-DST transition period. if len(possible_loc_dt) == 0: # If we refuse to guess, raise an exception. if is_dst is None: raise NonExistentTimeError(dt) # If we are forcing the pre-DST side of the DST transition, we # obtain the correct timezone by winding the clock forward a few # hours. elif is_dst: return self.localize(dt + timedelta(hours=6), is_dst=True) - timedelta(hours=6) # If we are forcing the post-DST side of the DST transition, we # obtain the correct timezone by winding the clock back. else: return self.localize(dt - timedelta(hours=6), is_dst=False) + timedelta(hours=6) # If we get this far, we have multiple possible timezones - this # is an ambiguous case occuring during the end-of-DST transition. # If told to be strict, raise an exception since we have an # ambiguous case if is_dst is None: raise AmbiguousTimeError(dt) # Filter out the possiblilities that don't match the requested # is_dst filtered_possible_loc_dt = [ p for p in possible_loc_dt if bool(p.tzinfo._dst) == is_dst ] # Hopefully we only have one possibility left. Return it. if len(filtered_possible_loc_dt) == 1: return filtered_possible_loc_dt[0] if len(filtered_possible_loc_dt) == 0: filtered_possible_loc_dt = list(possible_loc_dt) # If we get this far, we have in a wierd timezone transition # where the clocks have been wound back but is_dst is the same # in both (eg. Europe/Warsaw 1915 when they switched to CET). # At this point, we just have to guess unless we allow more # hints to be passed in (such as the UTC offset or abbreviation), # but that is just getting silly. # # Choose the earliest (by UTC) applicable timezone. def mycmp(a, b): return cmp( a.replace(tzinfo=None) - a.tzinfo._utcoffset, b.replace(tzinfo=None) - b.tzinfo._utcoffset, ) filtered_possible_loc_dt.sort(mycmp) return filtered_possible_loc_dt[0]