Introduction à DateTime¶
Définir ce qu'est une date est toujours un peu compliqué. En dessous de la seconde on compte en base 10, puis pour les secondes et minutes on compte en base 60, pour les heures on passe en base 24 et ensuite pour les jours cela peut varier suivant les ans. Pour être encore plus précis il existe des années durant lesquelles on ajoute 1 seconde entre le 31 décembre à minuit et le 1er janvier à 0h. À cela il faut ajouter la notion de fuseaux horaires. Bref créer un objet DateTime est compliqué et malheureusement son utilisation l'est un peu aussi.
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
On peut aussi utiliser des chaînes de caractères ce qui est utile lorsqu'on lit un fichier. Pour cela on utilise
la bibliothèque dateutil
qui est une extension de 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
Il est possible de lire et écrire des dates dans des formats quelconques en indiquant le format avec les directives décrites dans strftime-strptime-behavior de la 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 : parfois on importe datetime avec l'alias dt (import datetime as dt
) mais cela peut poser des problèmes car dt
est parfois utilisé comme variable pour définir un intervalle de temps. Donc on redéfinit dt
sans s'en rendre compte et tout est cassé.
Il est aussi possible de connaitre le jour de la semaine ou la semaine de l'année d'une 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)
Intervalle de temps¶
Additionner deux dates n'a pas de sens par contre ajouter du temps à une date en a. Aussi nous utilisons un intervalle de temps ou 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)
Pour ajouter un mois ou un an, il faut utiliser relativedelta
de dateutil.relativedelta
. Attention à bien mettre le 's' à la fin de months ou years sinon c'est considéré comme une information absolue
à savoir month=2
veut dire février.
print(bastille_attack + dateutil.relativedelta.relativedelta(months=1))
1789-08-14 15:30:00
On doit toujours manipuler ensemble des dates de même type, avec fuseau horaire (aware) ou sans (naive).
now_UTC = datetime.datetime.now(datetime.timezone.utc)
xmas_NY - now_UTC # result is (days, seconds, microseconds)
datetime.timedelta(-477, 69002, 630841)
Attention : ajouter un intervalle de temps à une heure n'a pas de sens car si on dépasse minuit on devrait changer de jour mais l'objet time
n'a pas la notion de jour :
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'
En résumé on peut faire :
timedelta1 + timedelta2
timedelta1 - timedelta2
timedelta1 / timedelta2
timedelta1 % timedelta2
i * timedelta1
datetime2 = datetime1 + timedelta1
datetime2 = datetime1 - timedelta1
timedelta1 = datetime2 - datetime1
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)
On l'utilise souvent pour chronométrer une partie d'un programme :
start = time.time()
time.sleep(1.2)
end = time.time()
print("dt = %4.2f s" % (end - start))
dt = 1.20 s
Pour convertir une date exprimée en string
au format de time
on a strptime
avec la structure de la date en 2e argument. L'inverse se fait avec 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
S'il est tentant d'utiliser ce temps en secondes pour sa simplicité, il a aussi ses limites. Ainsi tracer une courbe en fonction de dates en secondes est illisible alors qu'avec datetime
les bibliothèques graphiques interprètent correctement les dates et font de jolis tracés.
Numpy et le temps¶
Numpy, que l'on verra plus tard, est une bibliothèque qui a aussi son système de temps. Lorsqu'on intègre des dates définies par datetime
c'est relativement transparent ce qui fait qu'on peut utiliser Numpy sans entrer dans son système de date. Si maintenant on désire faire des calculs qui font intervenir le temps alors il faut s'y mettre car Numpy
aura converti les datetime qu'on lui aura fournis en son format.
Numpy datetime propose donc :
- numpy.datetime64 (64 car sur 64 bits et parcque datetime était déjà pris)
- numpy.timedelta64
Une fois cela dit le reste marche comme pour 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')
avec comme convention pour indiquer un intervalle de temps :
code | sens |
---|---|
Y | an |
M | mois |
W | semaine |
D | jour |
h | heure |
m | minute |
s | seconde |
ms | milliseconde |
Cette convention sert aussi à définir des types plus grossiers de date. Ainsi datetime64[D]
est une date au jour près et
datetime64[m]
à la minute près. Cela est utilisé lorsqu'on définit un dtype
dans Numpy (pour arrondir une date ou pour avoir un ensemble de dates homogènes par exemple).
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 sait calculer le nombre de jours ouvrés :
np.busday_count(np.datetime64('2020'), today)
74
{{ PreviousNext("10 sys - under the hood.ipynb", "12 xml.ipynb")}}