Introduction to DateTime¶

Defining what a date is is always a little complicated. Below the second one counts in base 10, then for the seconds and minutes one counts in base 60, for the hours one passes in base 24 and then for the days that can vary according to the years. To be even more specific there are years during which we add 1 second between December 31 at midnight and January 1 at 0h. To this must be added the notion of time zones. In short, creating a DateTime object is complicated and unfortunately the use of it is also a bit.

  1. [Set a datetime](# creation)
  2. [Set a time interval](# timedelta)

Define a datetime¶

First you have to load the datetime library. In this library, the first object of interest is datetime.

In [1]:
import datetime

bastille_day = datetime.datetime(1789,7,14)

print('bastille day:', bastille_day)
print('now it is:', datetime.datetime.now())

bastille_day < datetime.datetime.now()
bastille day: 1789-07-14 00:00:00
now it is: 2020-04-14 11:49:56.008806
Out[1]:
True

We can also use character strings which is useful when reading a file. For that we use the dateutil library which is an extension of datetime:

In [2]:
import dateutil

bastille_day = dateutil.parser.parse('14/07/1789')
bastille_attack = dateutil.parser.parse('1789-07-14 15:30:00')  
bastille_attack = dateutil.parser.parse('1789-07-14T15:30:00')  # ISO 8601
xmas_NY = dateutil.parser.parse('2018-12-24 23:59:59-05:00')    # UTC - 5

print('Get information:')
print(bastille_day.year, bastille_attack.weekday(), xmas_NY.hour)
print('\nExtract date and time:')
print(bastille_attack.date(), '---', bastille_attack.time())
Get information:
1789 1 23

Extract date and time:
1789-07-14 --- 15:30:00

It is possible to read and write dates in any format by specifying the format with the directives described in [strftime-strptime-behavior](https://docs.python.org/3/library/datetime.html#strftime- strptime-behavior) of the documentation.

In [3]:
# string parse time
datetime.datetime.strptime('03/01/17, 10h03 PM', '%m/%d/%y, %Ih%M %p')
Out[3]:
datetime.datetime(2017, 3, 1, 22, 3)
In [4]:
# string from time
datetime.datetime.strftime(xmas_NY, 'It is %A the %dth of %B of the year %Y.')
Out[4]:
'It is Monday the 24th of December of the year 2018.'

Note: sometimes we import datetime with the alias dt (import datetime as dt) but this can cause problems because dt is sometimes used as a variable to define a time interval. So redefining dt without realizing it may breaks everything.

It is also possible to know the day of the week or the week of the year of a date:

In [5]:
print(bastille_day.weekday())     # Tuesday (Monday is 0)
print(bastille_day.isoweekday())  # Tuesday (Monday is 1)
1
2
In [6]:
bastille_day.isocalendar()  # year, week of the year, day of the week
Out[6]:
(1789, 29, 2)

Interval of time¶

Adding two dates does not make sense, one should add a period of time to a date. To do so we use timedelta.

In [7]:
battle_duration = datetime.timedelta(hours=1, minutes=30) # we cannot add more than days
print('Battle duration: %s or %s seconds' % (battle_duration, battle_duration.total_seconds()))

end_bastille_attack = bastille_attack + battle_duration
print('Battle end:', end_bastille_attack)
Battle duration: 1:30:00 or 5400.0 seconds
Battle end: 1789-07-14 17:00:00
In [8]:
20 * battle_duration  # 30 hours = 1 day and 21600 seconds
Out[8]:
datetime.timedelta(1, 21600)

To add a month or a year, you need to use relativedelta from dateutil.relativedelta. Be careful to put the 's' at the end of months or years otherwise it is considered an absolute information i.e. month = 2 means February.

In [9]:
print(bastille_attack + dateutil.relativedelta.relativedelta(months=1))
1789-08-14 15:30:00

One must always handle together dates of the same type, with time zone (aware) or without (naive).

In [10]:
now_UTC = datetime.datetime.now(datetime.timezone.utc)
xmas_NY - now_UTC  # result is (days, seconds, microseconds)
Out[10]:
datetime.timedelta(-477, 69002, 630841)

Warning: adding a period of time to a time does not make sense because if we pass beyond midnight we should change the day but the object time does not have the notion of day:

In [11]:
bastille_attack.time() + battle_duration
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-11-5ccb98975b56> in <module>
----> 1 bastille_attack.time() + battle_duration

TypeError: unsupported operand type(s) for +: 'datetime.time' and 'datetime.timedelta'

In summary we can do:

  • timedelta1 + timedelta2* timedelta1 - timedelta2

  • timedelta1 / timedelta2* timedelta1% timedelta2

  • i* timedelta1

  • datetime2 = datetime1 + timedelta1

  • datetime2 = datetime1 - timedelta1

  • timedelta1 = datetime2 - datetime1

Epoch time¶

Under Unix the date is counted in seconds since January 1, 1970 Universal Time (UTC). So a date is a real and it becomes very simple to do operations on dates.

This date system is in the time library.

In [12]:
import time

print(time.time())  # now
print(time.ctime())
print(time.localtime())
print(time.gmtime())
1586857796.5689988
Tue Apr 14 11:49:56 2020
time.struct_time(tm_year=2020, tm_mon=4, tm_mday=14, tm_hour=11, tm_min=49, tm_sec=56, tm_wday=1, tm_yday=105, tm_isdst=1)
time.struct_time(tm_year=2020, tm_mon=4, tm_mday=14, tm_hour=9, tm_min=49, tm_sec=56, tm_wday=1, tm_yday=105, tm_isdst=0)

It is often used to time a part of a program:

In [13]:
start = time.time()
time.sleep(1.2)
end = time.time()
print("dt = %4.2f s" % (end - start))
dt = 1.20 s

To convert a date expressed in string to the format of time we use strptime with the structure of the date in 2nd argument. The opposite is done with strftime.

In [14]:
t = time.strptime('01/01/2000', '%m/%d/%Y')  # Beware, this is localtime
print(t)
print(time.mktime(t))   # to convert a struct_time in seconds
time.struct_time(tm_year=2000, tm_mon=1, tm_mday=1, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=5, tm_yday=1, tm_isdst=-1)
946681200.0

If it is tempting to use this time in seconds for its simplicity, it also has its limits. For example drawing a curve depending on dates in seconds is unreadable while with datetime graphics libraries correctly interpret the dates and make pretty plots.

Numpy and time¶

Numpy, which we will see later, is a library which also has its time system. When integrating dates defined by datetime it is relatively transparent so that you can use Numpy without understanding its own date system. However if we want to make calculations that involve time, then we have to use Numpy's dates because Numpy will have converted the datetime that will have been provided in its format.

[Numpy datetime] (https://docs.scipy.org/doc/numpy-1.15.0/reference/arrays.datetime.html) proposes:

  • numpy.datetime64 (64 because on 64 bits and datetime array was already taken)
  • numpy.timedelta64

Once that says the rest works as for datetime:

In [15]:
import numpy as np

np.datetime64('2005-02-25T03:30')
Out[15]:
numpy.datetime64('2005-02-25T03:30')
In [16]:
np.datetime64('2009') + np.timedelta64(20, 'D')
Out[16]:
numpy.datetime64('2009-01-21')

with this convention to indicate a time interval:

code meaning
Y year
M month
W week
D day
h time
m minute
s second
ms millisecond

This convention is also used to define coarser types of date. So datetime64 [D] is a date to the day and datetime64 [m] to the minute. This is used when defining a dtype in Numpy (to round a date or to have a set of homogeneous dates by examples).

In [17]:
now = np.datetime64(datetime.datetime.now())
print(now) 
today = now.astype('datetime64[D]')
print(today)
2020-04-14T11:49:58.018705
2020-04-14

Numpy can calculate the number of working days:

In [18]:
np.busday_count(np.datetime64('2020'), today)
Out[18]:
74

{{ PreviousNext("10 sys - under the hood.ipynb", "12 xml.ipynb")}}

In [ ]: