All other timezones are defined relative to UTC, and include offsets like UTC+0800 - hours to add or subtract from UTC to derive the local time. No daylight saving time occurs in UTC, making it a useful timezone to perform date arithmetic without worrying about the confusion and ambiguities caused by daylight saving time transitions, your country changing its timezone, or mobile computers that roam through multiple timezones.,You can take shortcuts when dealing with the UTC side of timezone conversions. normalize() and localize() are not really necessary when there are no daylight saving time transitions to deal with.,This library also allows you to do date arithmetic using local times, although it is more complicated than working in UTC as you need to use the normalize() method to handle daylight saving time and other timezone transitions. In this example, loc_dt is set to the instant when daylight saving time ends in the US/Eastern timezone.,The preferred way of dealing with times is to always work in UTC, converting to localtime only when generating output to be read by humans.
python setup.py install
easy_install--upgrade pytz
easy_install pytz - 2008 g - py2 .6.egg
>>> from datetime
import datetime, timedelta
>>>
from pytz
import timezone
>>>
import pytz >>>
utc = pytz.utc >>>
utc.zone 'UTC' >>>
eastern = timezone('US/Eastern') >>>
eastern.zone 'US/Eastern' >>>
amsterdam = timezone('Europe/Amsterdam') >>>
fmt = '%Y-%m-%d %H:%M:%S %Z%z'
>>> loc_dt = eastern.localize(datetime(2002, 10, 27, 6, 0, 0)) >>> print(loc_dt.strftime(fmt)) 2002 - 10 - 27 06: 00: 00 EST - 0500
>>> ams_dt = loc_dt.astimezone(amsterdam) >>>
ams_dt.strftime(fmt)
'2002-10-27 12:00:00 CET+0100'
The tzinfo has a method dst() which will return the Daylight Saving Time (DST) information if the flag is set to true.,The tzinfo class is abstract class which class provides the following methods to get the timezone information.,Using the DateTime.tzinfo class, we can get information about the date or time. The tzinfo generally has the following information.,We can convert the datetime from one timezone to another timezone using the datetime.astimezone() method. This method takes the datetime object as a parameter and returns a new datetime of a given timezone. Let's understand the following example.
The current timezone is: 21: 22: 41
Timezone naive: 2022 - 05 - 08 11: 38: 01.362134 Timezone Aware: 2022 - 05 - 08 06: 08: 01.363137 + 00: 00 US Central DateTime 2022 - 05 - 08 01: 08: 11.323668 - 05: 00
US Buenos DateTime: 2022: 05: 08 03: 52: 13 - 03 - 0300 US Adak timezone DateTime: 2022: 05: 07 21: 52: 13 HDT - 0900 US Eastern timezone DateTime: 2022: 05: 07 22: 52: 13 AKDT - 0800 US Michigan timezone DateTime: 2022: 05: 08 02: 52: 13 EDT - 0400 US Belam timezone DateTime: 2022: 05: 08 03: 52: 13 - 03 - 0300
Israel DateTime: 2022: 05: 08 10: 16: 46 IDT + 0300 Rome DateTime: 2022: 05: 08 09: 16: 46 CEST + 0200 Amsterdam DateTime: 2022: 05: 08 09: 16: 46 CEST + 0200 Hongkong DateTime: 2022: 05: 08 15: 16: 46 HKT + 0800 Jamaica DateTime: 2022: 05: 08 02: 16: 46 EST - 0500 Turkey: DateTime: 2022: 05: 08 10: 16: 46 + 03 + 0300
Europe London DateTime: 2022: 05: 08 09: 12: 09 BST + 0100 BST 1: 00: 00 1: 00: 00
Some time zones are aware of daylight savings time and some are not. For example the winter time results are the same for US/Mountain and MST, but the summer time results are not.,pandas.Timestamp and pandas.DatetimeIndex can be created in many ways. Here we focus on the time zone issues surrounding them; see the pandas documentation for more information.,You can specify the time zone using the tz keyword argument or the tz_localize method of Timestamp and DatetimeIndex objects.,pandas and pytz make this time zone handling possible because pandas stores all times as integer nanoseconds since January 1, 1970. Here is the pandas time representation of the integers 1 and 1e9.
In[1]: import datetime
In[2]: import pandas as pd
In[3]: import pytz
In[4]: len(pytz.all_timezones)
Out[4]: 586
In[5]: pytz.all_timezones[::20]
Out[5]: ['Africa/Abidjan',
'Africa/Douala',
'Africa/Mbabane',
'America/Argentina/Catamarca',
'America/Belize',
'America/Curacao',
'America/Guatemala',
'America/Kentucky/Louisville',
'America/Mexico_City',
'America/Port-au-Prince',
'America/St_Barthelemy',
'Antarctica/Davis',
'Asia/Baghdad',
'Asia/Gaza',
'Asia/Kuala_Lumpur',
'Asia/Riyadh',
'Asia/Ust-Nera',
'Australia/Brisbane',
'Australia/Yancowinna',
'EST',
'Etc/GMT-10',
'Europe/Andorra',
'Europe/Kaliningrad',
'Europe/San_Marino',
'GB',
'Indian/Reunion',
'Pacific/Bougainville',
'Pacific/Midway',
'Pacific/Yap',
'US/Samoa'
]
In[6]: pytz.country_timezones('US')
Out[6]: [u 'America/New_York',
u 'America/Detroit',
u 'America/Kentucky/Louisville',
u 'America/Kentucky/Monticello',
u 'America/Indiana/Indianapolis',
u 'America/Indiana/Vincennes',
u 'America/Indiana/Winamac',
u 'America/Indiana/Marengo',
u 'America/Indiana/Petersburg',
u 'America/Indiana/Vevay',
u 'America/Chicago',
u 'America/Indiana/Tell_City',
u 'America/Indiana/Knox',
u 'America/Menominee',
u 'America/North_Dakota/Center',
u 'America/North_Dakota/New_Salem',
u 'America/North_Dakota/Beulah',
u 'America/Denver',
u 'America/Boise',
u 'America/Phoenix',
u 'America/Los_Angeles',
u 'America/Anchorage',
u 'America/Juneau',
u 'America/Sitka',
u 'America/Metlakatla',
u 'America/Yakutat',
u 'America/Nome',
u 'America/Adak',
u 'Pacific/Honolulu'
]
In[7]: list(filter(lambda x: 'GMT' in x, pytz.all_timezones))
Out[7]: ['Etc/GMT',
'Etc/GMT+0',
'Etc/GMT+1',
'Etc/GMT+10',
'Etc/GMT+11',
'Etc/GMT+12',
'Etc/GMT+2',
'Etc/GMT+3',
'Etc/GMT+4',
'Etc/GMT+5',
'Etc/GMT+6',
'Etc/GMT+7',
'Etc/GMT+8',
'Etc/GMT+9',
'Etc/GMT-0',
'Etc/GMT-1',
'Etc/GMT-10',
'Etc/GMT-11',
'Etc/GMT-12',
'Etc/GMT-13',
'Etc/GMT-14',
'Etc/GMT-2',
'Etc/GMT-3',
'Etc/GMT-4',
'Etc/GMT-5',
'Etc/GMT-6',
'Etc/GMT-7',
'Etc/GMT-8',
'Etc/GMT-9',
'Etc/GMT0',
'GMT',
'GMT+0',
'GMT-0',
'GMT0'
]
In[8]: pd.Timestamp('2015-1-1 00:00')
Out[8]: Timestamp('2015-01-01 00:00:00')
In[9]: pd.Timestamp('2015-1-1 00:00', tz = 'America/Denver')
Out[9]: Timestamp('2015-01-01 00:00:00-0700', tz = 'America/Denver')
In[10]: pd.Timestamp('2015-1-1 00:00').tz_localize('America/Denver')
Out[10]: Timestamp('2015-01-01 00:00:00-0700', tz = 'America/Denver')
Django templates checks what the current time zone is. Like before, since we haven't explicitly set it, it is America/Los_Angeles from the TIME_ZONE setting.,Because datetime arithmetic is far more complex when we're dealing with local times and time zones instead of UTC.,Let's forget UTC exists for a moment and start developing a simple application to be used in a single time zone. To successfully do that, we define what a time zone is.,Django checks what the current time zone is. Since we have set USE_TZ to True and we haven't explicitly set a current timezone, the value in TIME_ZONE will be used: America/Los_Angeles.
And the code, written with that assumption:
from datetime
import datetime
# We receive an interesting time from the user.
# 8: 00 AM on October 5 th, 2015
user_input = datetime(
year = 2015,
month = 10,
day = 5,
hour = 8,
minute = 0
)
# Save it in the database.
interesting_times.add(user_input)
# Display all interesting times to the user.
for interesting_time in interesting_times:
print interesting_time
That might not lead to big bugs, but let's consider another effect of that time transition. What if our application wanted to show the duration between the two interesting times? Julio decides to submit 02:00 AM
, March 8th, 1992 as an interesting time because he met his future ex-wife in a local bar in Quito. That's one hour before the time transition. The wedding was set for the next day at 02:00 PM
, March 8th, 1992. Julio wants to know the duration between these two very interesting_times
. Our naïve implemenation would calculate it like so:
# 02: 00 AM, March 8 th, 1992
first_meeting_time = datetime(
year = 1992,
month = 3,
day = 8,
hour = 2,
minute = 0)
# 02: 00 PM, March 9 th, 1992
wedding_time = datetime(
year = 1992,
month = 3,
day = 8,
hour = 14,
minute = 0)
duration = wedding_time - first_meeting_time
# timedelta(hours = 12)
How do we deal with these cases? We could write if-conditions to deal with the daylight saving time transitions, since in Ecuador there were only two transitions. We shouldn't though, because Python offers a package that makes it really easy called pytz
. It corrects datetime
usage with information from the Olson Database, also called the tz database, which is basically historical data on what Time was used, when and in which timezone. It's not perfect, as I found out. It knows that there was a daylight saving time experiment in Ecuador in 1992, but no one knows the exact dates. Here's the entry:
# Ecuador
#
# Milne says the Central and South American Telegraph Company used -5:24:15.
#
# From Paul Eggert (2007-03-04):
# Apparently Ecuador had a failed experiment with DST in 1992.
#
<http: //midena.gov.ec/content/view/1261/208 /> (2007-02-27) and
# <http: //www.hoy.com.ec/NoticiaNue.asp?row_id=249856> (2006-11-06) both
# talk about "hora Sixto". Leave this alone for now, as we have no data.
#
# Zone NAME GMTOFF RULES FORMAT [UNTIL]
Zone America/Guayaquil -5:19:20 - LMT 1890
-5:14:00 - QMT 1931 # Quito Mean Time
-5:00 - ECT # Ecuador Time
Here's how we would write these solutions with pytz
.
from datetime import datetime
import pytz
user_input = datetime(
year=2015,
month=11,
day=1,
hour=1,
minute=30
)
sf_timezone = pytz.timezone('America/Los_Angeles')
# First solution: raise an exception.
# Passing in `is_dst=None` raises an `AmbiguousTimeError`.
sf_timezone.localize(user_input, is_dst=None)
# pytz.exceptions.AmbiguousTimeError: 2015-11-01 01:30:00
# Second solution: let `pytz` randomly pick the correct Time.
# We don't pass an `is_dst` flag
sf_timezone.localize(user_input)
# datetime(2015, 11, 1, 1, 30,
# tzinfo=<DstTzInfo 'America/Los_Angeles' PST-1 day, 16:00:00 STD>)
# Third solution: explicitly tell `pytz` which Time we want.
sf_timezone.localize(user_input, is_dst=True)
# datetime(2015, 11, 1, 1, 30,
# tzinfo=<DstTzInfo 'America/Los_Angeles' PDT-1 day, 17:00:00 DST>)
Here's how it works in the code:
from datetime import datetime, timedelta
import pytz
sf_timezone = pytz.timezone('America/Los_Angeles')
# 3:00:00 AM, March 8th 2015 - the first moment after
# transitioning to Pacific Daylight Saving time.
# The previous second was 01:59:59.
after_dt = sf_timezone.localize(datetime(2015, 3, 8, 3, 0))
# datetime(2015, 3, 8, 3, 0,
# tzinfo=<DstTzInfo 'America/Los_Angeles' PDT-1 day, 17:00:00 DST>)
# 2:30:00 AM on March 8th 2015 never occurred.
wrong = after_dt - timedelta(minutes=30)
# datetime(2015, 3, 8, 2, 30,
# tzinfo=<DstTzInfo 'America/Los_Angeles' PDT-1 day, 17:00:00 DST>)
# pytz.normalize returns a corrected datetime,
# 1:30:00 AM on March 8th, 2015.
corrected = sf_timezone.normalize(wrong)
# datetime(2015, 3, 8, 1, 30,
# tzinfo=<DstTzInfo 'America/Los_Angeles' PST-1 day, 16:00:00 STD>)
From Paul Eggert (2014-07-18):
We decided to use pytz internals to build a lookup table to store these. We need to use the information we get from Microsoft as the key, and the timezone name as the value. For each timezone, pytz stores an attribute _utc_transition_times
>>> tz = pytz.timezone('US/Eastern') >>>
pprint(tz._utc_transition_times[-5: ])[
datetime.datetime(2035, 11, 4, 6, 0),
datetime.datetime(2036, 3, 9, 7, 0),
datetime.datetime(2036, 11, 2, 6, 0),
datetime.datetime(2037, 3, 8, 7, 0),
datetime.datetime(2037, 11, 1, 6, 0)]
Oh cool, so we can use this to build the lookup table., right? We’ll take the first transition of a given year as the transition to DST, the second transition of a given year as the transition to ST, pick a time outside of DST and look up the UTC offset, and store a tuple of this as the key for our table!
>>> daylight_time = tz._utc_transition_times[n] >>> standard_time = tz._utc_transition_times[n + 1] >>> utc_offset = tz.utc_offset(date_not_in_dst) >>> timezone_table[(daylight_time, standard_time, utc_offset)] = timezone_name
Timezones are political—who knew? Jokes aside, I wanted to see how pytz actually deals with time. This happens in the localize
method on the timezone class. localize
attempts to cover all the bases for weird timezone transitions. To do this, it bisects _utc_transition_times
to find where the given time falls in the list of transitions. Then, it retrieves information about the transition from _transition_info
.
>>> tz = pytz.timezone('US/Eastern') >>>
pprint(tz._utc_transition_times[: 5])[
datetime.datetime(1, 1, 1, 0, 0),
datetime.datetime(1901, 12, 13, 20, 45, 52),
datetime.datetime(1918, 3, 31, 7, 0),
datetime.datetime(1918, 10, 27, 6, 0),
datetime.datetime(1919, 3, 30, 7, 0)] >>>
pprint(tz._transition_info[: 5])[
(datetime.timedelta(-1, 68640), datetime.timedelta(0), 'LMT'),
(datetime.timedelta(-1, 68400), datetime.timedelta(0), 'EST'),
(datetime.timedelta(-1, 72000), datetime.timedelta(0, 3600), 'EDT'),
(datetime.timedelta(-1, 68400), datetime.timedelta(0), 'EST'),
(datetime.timedelta(-1, 72000), datetime.timedelta(0, 3600), 'EDT')]
gives you Friday, May 12, 2017
Governments define the standard offset from UTC that a geographical position follows, effectively creating a time zone. The most common database for time zones is known as the Olson Database. This can be retrieved in Python using dateutil.tz:
>>> from dateutil.tz
import gettz
>>>
gettz("Europe/Madrid")
The result of gettz gives us an object that we can use to create time-zone-aware dates in Python:
>>> import datetime as dt >>> dt.datetime.now().isoformat() '2017-04-15T14:16:56.551778' # This is a naive datetime >>> dt.datetime.now(gettz("Europe/Madrid")).isoformat() '2017-04-15T14:17:01.256587+02:00' # This is a tz aware datetime, always prefer these
Should we want to use just plain UTC in Python 3, we don't need any external libraries:
>>> dt.datetime.now(dt.timezone.utc).isoformat()
'2017-04-15T12:22:06.637355+00:00'
This gives us days that are made of 23 or 25 hours, resulting in really interesting time arithmetic. Depending on the time and the time zone, adding a day does not necessarily mean adding 24 hours:
>>> today = dt.datetime(2017, 10, 29, tzinfo = gettz("Europe/Madrid")) >>> tomorrow = today + dt.timedelta(days = 1) >>> tomorrow.astimezone(dt.timezone.utc) - today.astimezone(dt.timezone.utc) datetime.timedelta(1, 3600) # We 've added 25 hours
The day will come that you need to send your datetime objects in JSON and you will get the following:
>>> now = dt.datetime.now(dt.timezone.utc) >>>
json.dumps(now)
TypeError: Object of type 'datetime'
is not JSON serializable