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.
- [Set a datetime](# creation)
- [Set a time interval](# timedelta)
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
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
:
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.
# string parse time
datetime.datetime.strptime('03/01/17, 10h03 PM', '%m/%d/%y, %Ih%M %p')
datetime.datetime(2017, 3, 1, 22, 3)
# string from time
datetime.datetime.strftime(xmas_NY, 'It is %A the %dth of %B of the year %Y.')
'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:
print(bastille_day.weekday()) # Tuesday (Monday is 0)
print(bastille_day.isoweekday()) # Tuesday (Monday is 1)
1 2
bastille_day.isocalendar() # year, week of the year, day of the week
(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
.
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
20 * battle_duration # 30 hours = 1 day and 21600 seconds
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.
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).
now_UTC = datetime.datetime.now(datetime.timezone.utc)
xmas_NY - now_UTC # result is (days, seconds, microseconds)
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:
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.
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:
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
.
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:
import numpy as np
np.datetime64('2005-02-25T03:30')
numpy.datetime64('2005-02-25T03:30')
np.datetime64('2009') + np.timedelta64(20, 'D')
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).
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:
np.busday_count(np.datetime64('2020'), today)
74
{{ PreviousNext("10 sys - under the hood.ipynb", "12 xml.ipynb")}}