datetime, pytz and friends

Python User Group Hamburg 1.2.2016


Christian Geier

the standard library's datetime module

In [1]:
import datetime as dt   # idiomatic import!?
In [2]:
from datetime import datetime   # Why not `from datetime import DateTime`?
In [3]:
datetime(2015, 4, 8, 19, 15)
Out[3]:
datetime.datetime(2015, 4, 8, 19, 15)
In [4]:
datetime.now()
Out[4]:
datetime.datetime(2016, 2, 1, 15, 46, 46, 635192)
In [5]:
now = datetime.now()
In [6]:
now.year
Out[6]:
2016
In [7]:
now.minute
Out[7]:
46

all the others work as well (month, day, second...)

In [8]:
now.date()  # can also be imported from the datetime module `from datetime import date`
Out[8]:
datetime.date(2016, 2, 1)
In [9]:
now.time()  # can also be imported from the datetime module `from datetime import time`
Out[9]:
datetime.time(15, 46, 46, 656856)
In [10]:
now.timetuple()
Out[10]:
time.struct_time(tm_year=2016, tm_mon=2, tm_mday=1, tm_hour=15, tm_min=46, tm_sec=46, tm_wday=0, tm_yday=32, tm_isdst=-1)

Convert from and to Unix time

(seconds since epoche, 0:00 1. Jan. 1970)

In [11]:
datetime.fromtimestamp(1234567890)  # easy!
Out[11]:
datetime.datetime(2009, 2, 14, 0, 31, 30)
In [12]:
import calendar  # why calendar? no idea...
calendar.timegm(now.timetuple())
Out[12]:
1454341606
In [13]:
now
Out[13]:
datetime.datetime(2016, 2, 1, 15, 46, 46, 656856)
In [14]:
nowts = calendar.timegm(now.timetuple())
print(nowts)
datetime.utcfromtimestamp(nowts)
1454341606
Out[14]:
datetime.datetime(2016, 2, 1, 15, 46, 46)

Be Careful!

In [15]:
datetime.fromtimestamp(nowts)  # without *utc*
Out[15]:
datetime.datetime(2016, 2, 1, 16, 46, 46)

If dealing with localized (I'll tell you about that later) times

In [16]:
import time
nowts = time.mktime(now.timetuple())
datetime.fromtimestamp(nowts)
Out[16]:
datetime.datetime(2016, 2, 1, 15, 46, 46)

this is a f**ing mess!

see http://stackoverflow.com/questions/8777753/converting-datetime-date-to-utc-timestamp-in-python and links therein

python >= 3.3:

 datetime.timestamp()

Convert from and to strings

In [17]:
now.strftime('%H:%M %d.%m.%Y')  # "similar" to man strftime(3)
Out[17]:
'15:46 01.02.2016'
In [18]:
datetime.strptime('22:42 07.04.2015', '%H:%M %d.%m.%Y')
Out[18]:
datetime.datetime(2015, 4, 7, 22, 42)

timedelta

In [19]:
a = datetime(2015, 4, 8, 19)
b = datetime(2015, 4, 8, 21)
b - a
Out[19]:
datetime.timedelta(0, 7200)
In [20]:
from datetime import timedelta
In [21]:
timedelta(days=1)
Out[21]:
datetime.timedelta(1)
In [22]:
timedelta(seconds=10)
Out[22]:
datetime.timedelta(0, 10)
In [23]:
a = datetime(2015, 4, 8, 19, 15)
b = datetime(2015, 4, 8, 21, 0)
In [24]:
diff = b - a
In [25]:
datetime.now() + diff
Out[25]:
datetime.datetime(2016, 2, 1, 17, 31, 47, 149422)

timezones (the fun part)

  • timezone: region with uniform time e.g. (Central European Time, Mitteleuropäische Zeit, MEZ)
  • change rather often, e.g. what (parts of) country observe which timezone
  • more fun daylight saving time (conversion times change even more often)
  • timezones are support by datetime but not part of the standard library
  • pytz most widely used implementation
  • uses TZDB (Olson DB), free collection of timezone data
  • there is a universal standard time, called UTC (from English Coordinated Universal Time and french Temps universel coordonné (Berlin is +01:00 or +02:00 relative to UTC)

Example from timezone database

Europe/Berlin:

    # Germany

    # From Markus Kuhn (1998-09-29):
    # The German time zone web site by the Physikalisch-Technische
    # Bundesanstalt contains DST information back to 1916.
    # [See tz-link.htm for the URL.]

    # From Jörg Schilling (2002-10-23):
    # In 1945, Berlin was switched to Moscow Summer time (GMT+4) by
    # http://www.dhm.de/lemo/html/biografien/BersarinNikolai/
    # General [Nikolai] Bersarin.

    # From Paul Eggert (2003-03-08):
    # http://www.parlament-berlin.de/pds-fraktion.nsf/727459127c8b66ee8525662300459099/defc77cb784f180ac1256c2b0030274b/$FILE/bersarint.pdf
    # says that Bersarin issued an order to use Moscow time on May 20.
    # However, Moscow did not observe daylight saving in 1945, so
    # this was equivalent to CEMT (GMT+3), not GMT+4.


    # Rule    NAME    FROM    TO    TYPE    IN    ON    AT    SAVE    LETTER/S
    Rule    Germany    1946    only    -    Apr    14    2:00s    1:00    S
    Rule    Germany    1946    only    -    Oct     7    2:00s    0    -
    Rule    Germany    1947    1949    -    Oct    Sun>=1    2:00s    0    -
    # http://www.ptb.de/de/org/4/44/441/salt.htm says the following transition
    # occurred at 3:00 MEZ, not the 2:00 MEZ given in Shanks & Pottenger.
    # Go with the PTB.
    Rule    Germany    1947    only    -    Apr     6    3:00s    1:00    S
    Rule    Germany    1947    only    -    May    11    2:00s    2:00    M
    Rule    Germany    1947    only    -    Jun    29    3:00    1:00    S
    Rule    Germany    1948    only    -    Apr    18    2:00s    1:00    S
    Rule    Germany    1949    only    -    Apr    10    2:00s    1:00    S

    Rule SovietZone    1945    only    -    May    24    2:00    2:00    M # Midsummer
    Rule SovietZone    1945    only    -    Sep    24    3:00    1:00    S
    Rule SovietZone    1945    only    -    Nov    18    2:00s    0    -

    # Zone  NAME            GMTOFF  RULES   FORMAT  [UNTIL]
    Zone    Europe/Berlin   0:53:28 -       LMT     1893 Apr
                            1:00    C-Eur   CE%sT   1945 May 24  2:00
                            1:00 SovietZone CE%sT   1946
                            1:00    Germany CE%sT   1980
                            1:00    EU      CE%sT
In [27]:
import pytz

pytz brings the Olson tz database into Python

not entirely true: pytz uses the TZFILLE(5) locally or brings its own, uses expanded transition rules

In [28]:
pytz.timezone('Europe/Berlin')
Out[28]:
<DstTzInfo 'Europe/Berlin' LMT+0:53:00 STD>
In [29]:
berlin = pytz.timezone('Europe/Berlin')
newyork = pytz.timezone('America/New_York')
In [30]:
zip(berlin._utc_transition_times, berlin._transition_info)
Out[30]:
[(datetime.datetime(1, 1, 1, 0, 0),
  (datetime.timedelta(0, 3180), datetime.timedelta(0), 'LMT')),
 (datetime.datetime(1901, 12, 13, 20, 45, 52),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(1916, 4, 30, 22, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(1916, 9, 30, 23, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(1917, 4, 16, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(1917, 9, 17, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(1918, 4, 15, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(1918, 9, 16, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(1940, 4, 1, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(1942, 11, 2, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(1943, 3, 29, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(1943, 10, 4, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(1944, 4, 3, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(1944, 10, 2, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(1945, 4, 2, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(1945, 5, 24, 0, 0),
  (datetime.timedelta(0, 10800), datetime.timedelta(0, 7200), 'CEMT')),
 (datetime.datetime(1945, 9, 24, 0, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(1945, 11, 18, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(1945, 12, 31, 23, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(1946, 4, 14, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(1946, 10, 7, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(1947, 4, 6, 2, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(1947, 5, 11, 1, 0),
  (datetime.timedelta(0, 10800), datetime.timedelta(0, 7200), 'CEMT')),
 (datetime.datetime(1947, 6, 29, 0, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(1947, 10, 5, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(1948, 4, 18, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(1948, 10, 3, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(1949, 4, 10, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(1949, 10, 2, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(1979, 12, 31, 23, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(1980, 4, 6, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(1980, 9, 28, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(1981, 3, 29, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(1981, 9, 27, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(1982, 3, 28, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(1982, 9, 26, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(1983, 3, 27, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(1983, 9, 25, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(1984, 3, 25, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(1984, 9, 30, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(1985, 3, 31, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(1985, 9, 29, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(1986, 3, 30, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(1986, 9, 28, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(1987, 3, 29, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(1987, 9, 27, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(1988, 3, 27, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(1988, 9, 25, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(1989, 3, 26, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(1989, 9, 24, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(1990, 3, 25, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(1990, 9, 30, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(1991, 3, 31, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(1991, 9, 29, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(1992, 3, 29, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(1992, 9, 27, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(1993, 3, 28, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(1993, 9, 26, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(1994, 3, 27, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(1994, 9, 25, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(1995, 3, 26, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(1995, 9, 24, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(1996, 3, 31, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(1996, 10, 27, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(1997, 3, 30, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(1997, 10, 26, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(1998, 3, 29, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(1998, 10, 25, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(1999, 3, 28, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(1999, 10, 31, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(2000, 3, 26, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(2000, 10, 29, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(2001, 3, 25, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(2001, 10, 28, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(2002, 3, 31, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(2002, 10, 27, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(2003, 3, 30, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(2003, 10, 26, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(2004, 3, 28, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(2004, 10, 31, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(2005, 3, 27, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(2005, 10, 30, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(2006, 3, 26, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(2006, 10, 29, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(2007, 3, 25, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(2007, 10, 28, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(2008, 3, 30, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(2008, 10, 26, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(2009, 3, 29, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(2009, 10, 25, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(2010, 3, 28, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(2010, 10, 31, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(2011, 3, 27, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(2011, 10, 30, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(2012, 3, 25, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(2012, 10, 28, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(2013, 3, 31, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(2013, 10, 27, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(2014, 3, 30, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(2014, 10, 26, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(2015, 3, 29, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(2015, 10, 25, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(2016, 3, 27, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(2016, 10, 30, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(2017, 3, 26, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(2017, 10, 29, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(2018, 3, 25, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(2018, 10, 28, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(2019, 3, 31, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(2019, 10, 27, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(2020, 3, 29, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(2020, 10, 25, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(2021, 3, 28, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(2021, 10, 31, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(2022, 3, 27, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(2022, 10, 30, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(2023, 3, 26, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(2023, 10, 29, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(2024, 3, 31, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(2024, 10, 27, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(2025, 3, 30, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(2025, 10, 26, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(2026, 3, 29, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(2026, 10, 25, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(2027, 3, 28, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(2027, 10, 31, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(2028, 3, 26, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(2028, 10, 29, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(2029, 3, 25, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(2029, 10, 28, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(2030, 3, 31, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(2030, 10, 27, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(2031, 3, 30, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(2031, 10, 26, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(2032, 3, 28, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(2032, 10, 31, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(2033, 3, 27, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(2033, 10, 30, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(2034, 3, 26, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(2034, 10, 29, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(2035, 3, 25, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(2035, 10, 28, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(2036, 3, 30, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(2036, 10, 26, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET')),
 (datetime.datetime(2037, 3, 29, 1, 0),
  (datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST')),
 (datetime.datetime(2037, 10, 25, 1, 0),
  (datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET'))]

Basic usage

In [31]:
berlin.localize(now)
Out[31]:
datetime.datetime(2016, 2, 1, 15, 46, 46, 656856, tzinfo=<DstTzInfo 'Europe/Berlin' CET+1:00:00 STD>)
In [32]:
now_local = berlin.localize(now)
In [33]:
now_local.tzinfo
Out[33]:
<DstTzInfo 'Europe/Berlin' CET+1:00:00 STD>
In [34]:
now.tzinfo is None
Out[34]:
True

convert to other timezones

In [35]:
newyork.localize(now_local)  # does not work
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-35-3a8cff1eaec1> in <module>()
----> 1 newyork.localize(now_local)  # does not work

/usr/home/cg/.virtualenvs/jupyter/lib/python2.7/site-packages/pytz/tzinfo.pyc in localize(self, dt, is_dst)
    302         '''
    303         if dt.tzinfo is not None:
--> 304             raise ValueError('Not naive datetime (tzinfo is already set)')
    305 
    306         # Find the two best possibilities.

ValueError: Not naive datetime (tzinfo is already set)
  • datetime objects that have no timezone information are called naive
  • datetime objects with timezone information are called localized
In [36]:
now_local.astimezone(newyork)
Out[36]:
datetime.datetime(2016, 2, 1, 9, 46, 46, 656856, tzinfo=<DstTzInfo 'America/New_York' EST-1 day, 19:00:00 STD>)

remove timezone information

In [37]:
now_ny = now_local.astimezone(newyork)
now_ny.replace(tzinfo=None)
Out[37]:
datetime.datetime(2016, 2, 1, 9, 46, 46, 656856)

Creating localized datetime instances

Don't do this:

In [38]:
datetime(2015, 4, 8, 20, 0, tzinfo=berlin)
Out[38]:
datetime.datetime(2015, 4, 8, 20, 0, tzinfo=<DstTzInfo 'Europe/Berlin' LMT+0:53:00 STD>)
In [39]:
time_local = datetime(2015, 4, 8, 20, 0, tzinfo=berlin)
In [40]:
time_local.astimezone(pytz.UTC)  # 53 min offset!
Out[40]:
datetime.datetime(2015, 4, 8, 19, 7, tzinfo=<UTC>)

Do this:

In [41]:
berlin.localize(datetime(2015, 4, 8, 20, 0))
Out[41]:
datetime.datetime(2015, 4, 8, 20, 0, tzinfo=<DstTzInfo 'Europe/Berlin' CEST+2:00:00 DST>)
In [42]:
time_local.astimezone(pytz.UTC)
Out[42]:
datetime.datetime(2015, 4, 8, 19, 7, tzinfo=<UTC>)

UTC sandwich

In [43]:
now - now_local  # error! not a good idea anyway
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-43-9d48a0f4f21f> in <module>()
----> 1 now - now_local  # error! not a good idea anyway

TypeError: can't subtract offset-naive and offset-aware datetimes

In theory:

  • convert any incoming datetimes to UTC
  • localize just before the user sees the time

(analog to the unicode sandwich)

Problems:

  • you want naive (then callend floating)and localized datetimes in parallel (e.g. go to bed at the same time no matter what timezone you are currently in, but meeting times fixed in UTC)
  • bugs!

Be careful:

In [44]:
datetime.now()
Out[44]:
datetime.datetime(2016, 2, 1, 15, 48, 8, 270238)
In [45]:
datetime.utcnow()
Out[45]:
datetime.datetime(2016, 2, 1, 14, 48, 8, 291801)

datetime's friends

tzlocal

access a tzinfo object with the local timezone information

In [51]:
import tzlocal
tzlocal.get_localzone()
Out[51]:
<DstTzInfo 'local' CET+1:00:00 STD>

dateutil

  • collection of modules for common datetime operations (e.g. its own timezone implementation)
In [52]:
from dateutil.rrule import rrule, MONTHLY, WE
In [53]:
meetings = rrule(MONTHLY, byweekday=WE(+2), byhour=19, byminute=0, count=12)  #  next 12 pyCologne Meetings, thanks Chris!
meetings
Out[53]:
<dateutil.rrule.rrule at 0x80b6cd2d0>
In [54]:
list(meetings)
Out[54]:
[datetime.datetime(2016, 2, 10, 19, 0, 10),
 datetime.datetime(2016, 3, 9, 19, 0, 10),
 datetime.datetime(2016, 4, 13, 19, 0, 10),
 datetime.datetime(2016, 5, 11, 19, 0, 10),
 datetime.datetime(2016, 6, 8, 19, 0, 10),
 datetime.datetime(2016, 7, 13, 19, 0, 10),
 datetime.datetime(2016, 8, 10, 19, 0, 10),
 datetime.datetime(2016, 9, 14, 19, 0, 10),
 datetime.datetime(2016, 10, 12, 19, 0, 10),
 datetime.datetime(2016, 11, 9, 19, 0, 10),
 datetime.datetime(2016, 12, 14, 19, 0, 10),
 datetime.datetime(2017, 1, 11, 19, 0, 10)]

rrule cannot deal with timezones

In [55]:
from dateutil.rrule import rrulestr
now_local = berlin.localize(datetime(2015, 4, 8, 19))
list(rrulestr('FREQ=MONTHLY;COUNT=12', dtstart=now_local))  # all in DST??? this is not what we wanted...
Out[55]:
[datetime.datetime(2015, 4, 8, 19, 0, tzinfo=<DstTzInfo 'Europe/Berlin' CEST+2:00:00 DST>),
 datetime.datetime(2015, 5, 8, 19, 0, tzinfo=<DstTzInfo 'Europe/Berlin' CEST+2:00:00 DST>),
 datetime.datetime(2015, 6, 8, 19, 0, tzinfo=<DstTzInfo 'Europe/Berlin' CEST+2:00:00 DST>),
 datetime.datetime(2015, 7, 8, 19, 0, tzinfo=<DstTzInfo 'Europe/Berlin' CEST+2:00:00 DST>),
 datetime.datetime(2015, 8, 8, 19, 0, tzinfo=<DstTzInfo 'Europe/Berlin' CEST+2:00:00 DST>),
 datetime.datetime(2015, 9, 8, 19, 0, tzinfo=<DstTzInfo 'Europe/Berlin' CEST+2:00:00 DST>),
 datetime.datetime(2015, 10, 8, 19, 0, tzinfo=<DstTzInfo 'Europe/Berlin' CEST+2:00:00 DST>),
 datetime.datetime(2015, 11, 8, 19, 0, tzinfo=<DstTzInfo 'Europe/Berlin' CEST+2:00:00 DST>),
 datetime.datetime(2015, 12, 8, 19, 0, tzinfo=<DstTzInfo 'Europe/Berlin' CEST+2:00:00 DST>),
 datetime.datetime(2016, 1, 8, 19, 0, tzinfo=<DstTzInfo 'Europe/Berlin' CEST+2:00:00 DST>),
 datetime.datetime(2016, 2, 8, 19, 0, tzinfo=<DstTzInfo 'Europe/Berlin' CEST+2:00:00 DST>),
 datetime.datetime(2016, 3, 8, 19, 0, tzinfo=<DstTzInfo 'Europe/Berlin' CEST+2:00:00 DST>)]
In [56]:
[dtime.astimezone(pytz.UTC).astimezone(berlin) for dtime in list(rrulestr('FREQ=MONTHLY;COUNT=12', dtstart=now_local))]
Out[56]:
[datetime.datetime(2015, 4, 8, 19, 0, tzinfo=<DstTzInfo 'Europe/Berlin' CEST+2:00:00 DST>),
 datetime.datetime(2015, 5, 8, 19, 0, tzinfo=<DstTzInfo 'Europe/Berlin' CEST+2:00:00 DST>),
 datetime.datetime(2015, 6, 8, 19, 0, tzinfo=<DstTzInfo 'Europe/Berlin' CEST+2:00:00 DST>),
 datetime.datetime(2015, 7, 8, 19, 0, tzinfo=<DstTzInfo 'Europe/Berlin' CEST+2:00:00 DST>),
 datetime.datetime(2015, 8, 8, 19, 0, tzinfo=<DstTzInfo 'Europe/Berlin' CEST+2:00:00 DST>),
 datetime.datetime(2015, 9, 8, 19, 0, tzinfo=<DstTzInfo 'Europe/Berlin' CEST+2:00:00 DST>),
 datetime.datetime(2015, 10, 8, 19, 0, tzinfo=<DstTzInfo 'Europe/Berlin' CEST+2:00:00 DST>),
 datetime.datetime(2015, 11, 8, 18, 0, tzinfo=<DstTzInfo 'Europe/Berlin' CET+1:00:00 STD>),
 datetime.datetime(2015, 12, 8, 18, 0, tzinfo=<DstTzInfo 'Europe/Berlin' CET+1:00:00 STD>),
 datetime.datetime(2016, 1, 8, 18, 0, tzinfo=<DstTzInfo 'Europe/Berlin' CET+1:00:00 STD>),
 datetime.datetime(2016, 2, 8, 18, 0, tzinfo=<DstTzInfo 'Europe/Berlin' CET+1:00:00 STD>),
 datetime.datetime(2016, 3, 8, 18, 0, tzinfo=<DstTzInfo 'Europe/Berlin' CET+1:00:00 STD>)]

workaround

In [57]:
old_tz = now_local.tzinfo
now_unlocal = now_local.replace(tzinfo=None)
[old_tz.localize(dtime) for dtime in rrulestr('FREQ=MONTHLY;COUNT=12', dtstart=now_unlocal)]
Out[57]:
[datetime.datetime(2015, 4, 8, 19, 0, tzinfo=<DstTzInfo 'Europe/Berlin' CEST+2:00:00 DST>),
 datetime.datetime(2015, 5, 8, 19, 0, tzinfo=<DstTzInfo 'Europe/Berlin' CEST+2:00:00 DST>),
 datetime.datetime(2015, 6, 8, 19, 0, tzinfo=<DstTzInfo 'Europe/Berlin' CEST+2:00:00 DST>),
 datetime.datetime(2015, 7, 8, 19, 0, tzinfo=<DstTzInfo 'Europe/Berlin' CEST+2:00:00 DST>),
 datetime.datetime(2015, 8, 8, 19, 0, tzinfo=<DstTzInfo 'Europe/Berlin' CEST+2:00:00 DST>),
 datetime.datetime(2015, 9, 8, 19, 0, tzinfo=<DstTzInfo 'Europe/Berlin' CEST+2:00:00 DST>),
 datetime.datetime(2015, 10, 8, 19, 0, tzinfo=<DstTzInfo 'Europe/Berlin' CEST+2:00:00 DST>),
 datetime.datetime(2015, 11, 8, 19, 0, tzinfo=<DstTzInfo 'Europe/Berlin' CET+1:00:00 STD>),
 datetime.datetime(2015, 12, 8, 19, 0, tzinfo=<DstTzInfo 'Europe/Berlin' CET+1:00:00 STD>),
 datetime.datetime(2016, 1, 8, 19, 0, tzinfo=<DstTzInfo 'Europe/Berlin' CET+1:00:00 STD>),
 datetime.datetime(2016, 2, 8, 19, 0, tzinfo=<DstTzInfo 'Europe/Berlin' CET+1:00:00 STD>),
 datetime.datetime(2016, 3, 8, 19, 0, tzinfo=<DstTzInfo 'Europe/Berlin' CET+1:00:00 STD>)]

happens for several other operations on datetime objects

UTC sandwich doesn't help us here

more abstract:

In [58]:
orig_tz = dtime.tzinfo
dtime = dtime.replace(tzinfo=None)

# do calculations here

orig_tz.localize(dtime)
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-58-7c52a5461682> in <module>()
      4 # do calculations here
      5 
----> 6 orig_tz.localize(dtime)

AttributeError: 'NoneType' object has no attribute 'localize'

parsedatetime

https://github.com/bear/parsedatetime

Parse human-readable date/time strings.

In [59]:
import parsedatetime
In [60]:
cal = parsedatetime.Calendar()
cal.parse('Tomorrow Evening')
Out[60]:
((2016, 2, 2, 18, 0, 0, 1, 33, -1), 3)
In [61]:
cal.parse('Next Wednesday at noon')
Out[61]:
((2016, 2, 10, 12, 0, 0, 2, 41, -1), 3)

doesn't tell us which part of the string match

In [ ]:
cal.parse('PyBonn next Thursday 19:00 at HRZ Uni Bonn')

Arrow

Better dates & times for Python

http://crsmithdev.com/arrow

  • promises to fix a lot of the mess (yes, even in python 2.7)
  • haven't used it yet, have you?

Questions?

  • Why this mess? (datetime, time, calendar)
  • More intelligent way to parse strings? Information retrieval, natural language processing etc.
In [ ]:
 
In [ ]: