22. Dates and Time

Warning

If you’re thinking about implementing your own time calculator or system, watch Computerophile Time & Time Zones https://www.youtube.com/watch?v=-5wpm-gesOY

22.1. Date and time formats

22.1.1. Date formats

  • Which format is a formal standard in USA?
  • Which format is a formal standard in Germany?
  • Which format is a formal standard in Poland?
12.4.1961
12.04.1961
12 IV 1961
12.IV.1961
12/4/1961
12/04/1961
12 kwietnia 1961
12 kwiecień 1961
4/12/61
April 12, 1961

22.1.2. Time formats

  • Which time is a midnight?
  • Which time is a noon?
12:00 am
12:00 pm
12:00
23:59
24:00
0:00
00:00

22.1.3. ISO 8601 Standard

  • Dates:

  • Date and time

1961-04-12T06:07:00
  • Date and time with microseconds

    1961-04-12T06:07:00.123456
    

22.2. datetime

22.2.1. Creating date and datetime objects

Code Listing 22.1. Creating date and datetime objects
from datetime import datetime, date

now = datetime.now()
today = date.today()

gagarin = date(1961, 4, 12)
armstrong = datetime(1969, 7, 21, 14, 56, 15)
midnight = datetime(1957, 10, 4)

22.2.2. Date formatting

Code Listing 22.2. Datetime formatting as string
from datetime import datetime


now = datetime.now()

print(f'Now is {now:%Y-%m-%d %H:%M}!')
# Now is 2017-12-19 17:40!

22.2.3. Parsing dates

  • Parsing - analyze (a sentence) into its parts and describe their syntactic roles.
Code Listing 22.3. Datetime parsing from string
from datetime import datetime


sputnik = '4 October 1957 19:28:34 UTC'

out = datetime.strptime(sputnik, '%d %B %Y %H:%M:%S %Z')
# datetime.datetime(1957, 10, 4, 19, 28, 34)

print(out)
# 1957-10-04 19:28:34

22.2.4. Table of date and time parsing and formatting parameters

Note

Prawie wszystkie parametry są podobne różnych językach programowania. Od czasu do czasu występują małe zmiany, np. w JavaScript minuty to i a nie M

Tab. 22.1. Tabelka parametrów formatowania i parsowania dat i czasu
Directive Example Meaning
%a Sun, Mon, …, Sat Weekday as locale’s abbreviated name
%A Sunday, Monday, …, Saturday (en_US) Weekday as locale’s full name
%w 0, 1, …, 6 Weekday as a decimal number, where 0 is Sunday and 6 is Saturday
%d 01, 02, …, 31 Day of the month as a zero-padded decimal number
%b Jan, Feb, …, Dec (en_US) Month as locale’s abbreviated name
%B January, February, …, December (en_US) Month as locale’s full name
%m 01, 02, …, 12 Month as a zero-padded decimal number
%y 00, 01, …, 99 Year without century as a zero-padded decimal number
%Y 0001, 0002, …, 2013, 2014, …, 9998, 9999 Year with century as a decimal number
%H 00, 01, …, 23 Hour (24-hour clock) as a zero-padded decimal number
%I 01, 02, …, 12 Hour (12-hour clock) as a zero-padded decimal number
%p AM, PM (en_US) Locale’s equivalent of either AM or PM
%M 00, 01, …, 59 Minute as a zero-padded decimal number
%S 00, 01, …, 59 Second as a zero-padded decimal number
%f 000000, 000001, …, 999999 Microsecond as a decimal number, zero-padded on the left
%z (empty), +0000, -0400, +1030 UTC offset in the form +HHMM or -HHMM (empty string if the object is naive)
%Z (empty), UTC, EST, CST Time zone name (empty string if the object is naive)
%j 001, 002, …, 366 Day of the year as a zero-padded decimal number
%U 00, 01, …, 53 Week number of the year (Sunday as the first day of the week) as a zero padded decimal number. All days in a new year preceding the first Sunday are considered to be in week 0
%W 00, 01, …, 53 Week number of the year (Monday as the first day of the week) as a decimal number. All days in a new year preceding the first Monday are considered to be in week 0
%c Tue Aug 16 21:30:00 1988 (en_US) Locale’s appropriate date and time representation
%x 08/16/1988 (en_US); 16.08.1988 (de_DE) Locale’s appropriate date representation
%X 21:30:00 Locale’s appropriate time representation
%% % A literal % character
%G 0001, 0002, …, 2013, 2014, …, 9998, 9999 ISO 8601 year with century representing the year that contains the greater part of the ISO week (%V)
%u 1, 2, …, 7 ISO 8601 weekday as a decimal number where 1 is Monday
%V 01, 02, …, 53 ISO 8601 week as a decimal number with Monday as the first day of the week. Week 01 is the week containing Jan 4

22.2.5. Time shifts and time deltas

Code Listing 22.4. Shifting datetimes
from datetime import datetime

gagarin = datetime(1961, 4, 12, 6, 7)
armstrong = datetime(1969, 7, 21, 14, 56, 15)

duration = armstrong - gagarin            # datetime.timedelta(3022, 31755)

duration                                  # 3022 days, 8:49:15
duration.seconds                          # 31755
duration.days                             # 3022
duration.total_seconds()                  # 261132555.0 (days * seconds per day + seconds)
Code Listing 22.5. Substract time from datetimes
from datetime import datetime, timedelta, date

# Average days a month in solar calendar
MONTH = timedelta(days=30.436875)


datetime.now() - timedelta(hours=3)
date(1961, 4, 12) - timedelta(days=3)

month_before = date(1961, 4, 12) - MONTH
# datetime.date(1961, 3, 13)
Code Listing 22.6. Diff between datetimes
from datetime import datetime

SECOND = 1
MINUTE = 60 * SECOND
HOUR = 60 * MINUTE
DAY = 24 * HOUR
MONTH = 30.436875               # Average days a month in solar calendar
YEAR = 365.2425                 # Solar calendar

gagarin = datetime(1961, 4, 12, 6, 7)
armstrong = datetime(1969, 7, 21, 14, 56, 15)
duration = armstrong - gagarin  # datetime.timedelta(3022, 31755)

years, days = divmod(duration.days, YEAR)
months, days = divmod(days, MONTH)
hours, minutes = divmod(duration.seconds, HOUR)
minutes, seconds = divmod(minutes, MINUTE)

difference = {
    'years': int(years),
    'months': int(months),
    'days': int(days),
    'hours': int(hours),
    'minutes': int(minutes),
    'seconds': int(seconds),
}

print(difference)
# {'years': 8, 'months': 3, 'days': 8, 'hours': 8, 'minutes': 49, 'seconds': 15}

22.2.6. Time zones in datetime library

  • Always keep dates and times only in UTC (important!)
  • Datetimes should be converted to localtime only when displaying to user
Code Listing 22.7. Make timezone aware object from naive datetime
from datetime import datetime, timezone

# timezone naive - nieświadome
datetime.now()
datetime.utcnow()
datetime(1957, 10, 4, 19, 28, 34)

# timezone aware - świadome
datetime.now(tz=timezone.utc)
datetime(1957, 10, 4, 19, 28, 34, tzinfo=timezone.utc)

22.3. pytz

22.3.1. List of Timezones

Code Listing 22.8. pytz brings the Olson tz database into Python.
from pytz import utc, timezone

utc = utc
eastern = timezone('US/Eastern')
warsaw = timezone('Europe/Warsaw')

22.3.2. From UTC to local time

Code Listing 22.9. From UTC to local time
from datetime import datetime
from pytz import timezone, utc


armstrong = datetime(1969, 7, 21, 14, 56, 15, tzinfo=utc)


utc_time = armstrong.astimezone(utc)
# datetime.datetime(1969, 7, 21, 14, 56, 15, tzinfo=<UTC>)

print(f'{utc_time:%Y-%m-%d %H:%M:%S %Z%z}')
# 1969-07-21 14:56:15 UTC+0000


waw_time = armstrong.astimezone(timezone('Europe/Warsaw'))
# datetime.datetime(1969, 7, 21, 15, 56, 15, tzinfo=<DstTzInfo 'Europe/Warsaw' CET+1:00:00 STD>)

print(f'{waw_time:%Y-%m-%d %H:%M:%S %Z%z}')
# 1969-07-21 15:56:15 CET+0100

22.3.3. From local to UTC

Code Listing 22.10. From local to UTC
from datetime import datetime
from pytz import utc, timezone


warsaw = timezone('Europe/Warsaw')
gagarin = datetime(1961, 4, 12, 14, 7)


in_utc = utc.localize(gagarin, is_dst=None)
# datetime.datetime(1961, 4, 12, 14, 7, tzinfo=<UTC>)

in_waw = warsaw.localize(gagarin, is_dst=None)
# datetime.datetime(1961, 4, 12, 14, 7, tzinfo=<DstTzInfo 'Europe/Warsaw' CET+1:00:00 STD>)

22.3.4. Between timezones

Code Listing 22.11. Between timezones
from datetime import datetime
from pytz import utc, timezone


gagarin = datetime(1961, 4, 12, 14, 7, tzinfo=timezone('Asia/Almaty'))
in_utc = gagarin.astimezone(utc)
in_waw = gagarin.astimezone(timezone('Europe/Warsaw'))


print(f'{in_utc:%Y-%m-%d %H:%M:%S %Z%z}')
# 1961-04-12 08:59:00 UTC+0000

print(f'{in_waw:%Y-%m-%d %H:%M:%S %Z%z}')
# 1961-04-12 09:59:00 CET+0100

22.4. calendar

Code Listing 22.12. HTML Calendar
from calendar import HTMLCalendar

html = HTMLCalendar().formatmonth(1969, 7)

print(html)
# <table border="0" cellpadding="0" cellspacing="0" class="month">
# <tr><th colspan="7" class="month">July 1969</th></tr>
# <tr><th class="mon">Mon</th><th class="tue">Tue</th><th class="wed">Wed</th><th class="thu">Thu</th><th class="fri">Fri</th><th class="sat">Sat</th><th class="sun">Sun</th></tr>
# <tr><td class="noday">&nbsp;</td><td class="tue">1</td><td class="wed">2</td><td class="thu">3</td><td class="fri">4</td><td class="sat">5</td><td class="sun">6</td></tr>
# <tr><td class="mon">7</td><td class="tue">8</td><td class="wed">9</td><td class="thu">10</td><td class="fri">11</td><td class="sat">12</td><td class="sun">13</td></tr>
# <tr><td class="mon">14</td><td class="tue">15</td><td class="wed">16</td><td class="thu">17</td><td class="fri">18</td><td class="sat">19</td><td class="sun">20</td></tr>
# <tr><td class="mon">21</td><td class="tue">22</td><td class="wed">23</td><td class="thu">24</td><td class="fri">25</td><td class="sat">26</td><td class="sun">27</td></tr>
# <tr><td class="mon">28</td><td class="tue">29</td><td class="wed">30</td><td class="thu">31</td><td class="noday">&nbsp;</td><td class="noday">&nbsp;</td><td class="noday">&nbsp;</td></tr>
# </table>

22.5. time

Code Listing 22.13. Time sleep function
import time

time.sleep(2)
Code Listing 22.14. Get timestamp
import time

time.time()
# 1496737953.0712671

time.time()
# 1496737954.3189602

22.6. timeit

22.6.1. timeit from Python script

Code Listing 22.15. Timeit simple statement
from timeit import timeit


setup = 'name="Jose Jimenez"'
stmt = 'out = f"My name... {name}"'

duration = timeit(stmt, setup, number=10000)

print(duration)
# 0.0005737080000000061
Code Listing 22.16. Timeit multiple statements with setup code
from timeit import timeit


setup = """
first_name = 'José'
last_name = 'Jiménez'
"""

TEST = dict()
TEST[0] = 'name = f"{first_name} {last_name}"'
TEST[1] = 'name = "{0} {1}".format(first_name, last_name)'
TEST[2] = 'name = first_name + " " + last_name'
TEST[3] = 'name = " ".join([first_name, last_name])'


for stmt in TEST.values():
    duration = timeit(stmt, setup, number=10000)
    print(f'{duration:.5}\t{stmt}')

# 0.00071559    name = f"{first_name} {last_name}"
# 0.0026514     name = "{0} {1}".format(first_name, last_name)
# 0.001015      name = first_name + " " + last_name
# 0.0013494     name = " ".join([first_name, last_name])
Code Listing 22.17. Timeit with globals()
from timeit import timeit


def factorial(n: int) -> int:
    if n == 0:
        return 1
    else:
        return n * factorial(n-1)


duration = timeit(
    stmt='factorial(500); factorial(400); factorial(450)',
    globals=globals(),
    number=10000,
)

duration = round(duration, 6)

print(f'factorial time: {duration} seconds')
# factorial time: 2.845382 seconds

22.6.2. timeit from terminal

Code Listing 22.18. Timeit
python3 -m timeit -n100000 -r100 --setup='name="Jose Jimenez"' 'out = f"My name... {name}"'
# 100000 loops, best of 100: 55.9 nsec per loop

python3 -m timeit -n100000 -r100 --setup='name="Jose Jimenez"' 'out = "My name... {name}".format(name=name)'
# 100000 loops, best of 100: 327 nsec per loop

python3 -m timeit -n100000 -r100 --setup='name="Jose Jimenez"' 'out = "My name... %s" % name'
# 100000 loops, best of 100: 124 nsec per loop
-n N, --number=N
how many times to execute ‘statement’

-r N, --repeat=N
how many times to repeat the timer (default 5)

-s S, --setup=S
statement to be executed once initially (default pass)

-p, --process
measure process time, not wallclock time, using time.process_time() instead of time.perf_counter(), which is the default

-u, --unit=U
specify a time unit for timer output; can select nsec, usec, msec, or sec

-v, --verbose
print raw timing results; repeat for more digits precision

-h, --help
print a short usage message and exit

22.7. Assignments

22.7.1. Date manipulation

  1. Dane są dwie następujące daty w formacie jak poniżej:

    date1 = 'April 12, 1961 2:07 local time'  # Asia/Almaty
    date2 = '"07/21/69 2:56:15 AM UTC"'
    
  2. Przedstaw daty jako obiekt datetime

  3. Wyświetl daty w formacie ISO, e.g. “1961-04-12T06:07:00.123456” w UTC

  4. Odejmij obie daty od siebie

  5. Oblicz ile lat i miesięcy minęło między wydarzeniami

  6. Od obecnej chwili (UTC) odejmij ten sam czas, który Ci wyszedł w poprzednim punkcie

  7. Wyświetl samą datę (bez czasu)

  8. Ile miałeś wtedy lat?

  9. Przyjmij:

    • rok = 365.2425 dni
    • miesiac = 30.436875 dni
About:
  • Filename: datetime_deltas.py
  • Lines of code to write: 15 lines
  • Estimated time of completion: 20 min
Zadanie z gwiazdką:
 
  • Co to za daty, które podał użytkownik?
  • Uwzględnij strefy czasowe.
  • Co robiłeś przez ten czas?
Hint:
  • Wpisz “local time” jako zwykły tekst do strptime

  • Standard ISO:

    • ‘1961-04-12’
    • ‘1961-04-12T06:07:00’
    • ‘1961-04-12T06:07:00.123456’