datetime - Manipolazione dei Valori di Data ed Orario

Scopo: Il modulo datetime include le funzioni e le classi per analizzare, formattare e compiere operazioni aritmetiche su date e orari.

Orari

I valori di orario sono rappresentati dalla classe time. Una istanza di time ha attributi per ora, minuto, secondo e microsecondo (rispettivamente hour, minute,second e microsecond). Può anche includere informazioni circa la zona di fuso orario.

# datetime_time.py

import datetime

t = datetime.time(1, 2, 3)
print(t)
print('ora         :', t.hour)
print('minuto      :', t.minute)
print('secondo     :', t.second)
print('microsecondo:', t.microsecond)
print('zona fuso or:', t.tzinfo)

Gli argomenti per inizializzare una istanza di time sono opzionali, ma il valore predefinito (0) è piuttosto improbabile che sia corretto.

$ python3 datetime_time.py

01:02:03
ora         : 1
minuto      : 2
secondo     : 3
microsecondo: 0
zona fuso or: None

Una istanza di time contiene solo valori di orario, non una data associata a un orario.

# datetime_time_minmax.py

import datetime

print('Orario min.:', datetime.time.min)
print('Orario max :', datetime.time.max)
print('Risoluzione:', datetime.time.resolution)

Gli attributi di classe min e max rappresentano i limiti validi di orario in un singolo giorno.

$ python3 datetime_time_minmax.py

Orario min.: 00:00:00
Orario max : 23:59:59.999999
Risoluzione: 0:00:00.000001

La risoluzione per time è limitata agli interi microsecondi.

# datetime_time_resolution.py

import datetime

for m in [1, 0, 0.1, 0.6]:
    try:
        print('{:02.1f} :'.format(m),
              datetime.time(0, 0, 0, microsecond=m))
    except TypeError as err:
        print('ERRORE:', err)

Valori in virgola mobile per i microsecondi provocano un TypeError.

$ python3 datetime_time_resolution.py

1.0 : 00:00:00.000001
0.0 : 00:00:00
ERRORE: integer argument expected, got float
ERRORE: integer argument expected, got float

Date

I valori per date di calendario sono rappresentati con la classe date. Le istanze hanno attributi per anno (year), mese (month) e giorno (day). Con il metodo di classe today() è semplice creare un oggetto data che rappresenta la data corrente.

# datetime_date.py
import datetime

today = datetime.date.today()
print(today)
print('ctime  :', today.ctime())
tt = today.timetuple()
print('tupla   : tm_year  =', tt.tm_year)
print('          tm_mon   =', tt.tm_mon)
print('          tm_mday  =', tt.tm_mday)
print('          tm_hour  =', tt.tm_hour)
print('          tm_min   =', tt.tm_min)
print('          tm_sec   =', tt.tm_sec)
print('          tm_wday  =', tt.tm_wday)
print('          tm_yday  =', tt.tm_yday)
print('          tm_isdst =', tt.tm_isdst)
print('ordinale:', today.toordinal())
print('Anno   :', today.year)
print('Mese   :', today.month)
print('Giorno :', today.day)

Questo esempio stampa la data corrente di diversi formati:

$ python3 datetime_date.py

2016-06-12
ctime  : Sun Jun 12 00:00:00 2016
tupla   : tm_year  = 2016
          tm_mon   = 6
          tm_mday  = 12
          tm_hour  = 0
          tm_min   = 0
          tm_sec   = 0
          tm_wday  = 6
          tm_yday  = 164
          tm_isdst = -1
ordinale: 736127
Anno   : 2016
Mese   : 6
Giorno : 12

Ci sono anche metodi di classe per creare istanze da marche temporali POSIX (fromtimestamp()) o da interi che rappresentano valori di data dal calendario Gregoriano, dove il primo gennaio dell'anno 1 vale 1 e ogni giorno seguente incrementa il valore di 1 (fromordinal()).

# datetime_date_fromordinal.py
import datetime
import time

o = 733114
print('o               :', o)
print('fromordinal(o)  :', datetime.date.fromordinal(o))

t = time.time()
print('t               :', t)
print('fromtimestamp(t):', datetime.date.fromtimestamp(t))

Questo esempio illustra i diversi tipi di valore utilizzati da fromordinal() e da fromtimestamp().

$ python3 datetime_date_fromordinal.py

o               : 733114
fromordinal(o)  : 2008-03-13
t               : 1465745266.076346
fromtimestamp(t): 2016-06-12

Così come per time l'intervallo di valori data supportati può essere determinato utilizzando gli attributi min e max.

# datetime_date_minmax.py

import datetime

print('Minimo     :', datetime.date.min)
print('Massimo    :', datetime.date.max)
print('Risoluzione:', datetime.date.resolution)
import datetime

La risoluzione per le date è il giorno intero.

$ python3 datetime_date_minmax.py

Minimo     : 0001-01-01
Massimo    : 9999-12-31
Risoluzione: 1 day, 0:00:00

Un altro modo per creare nuove istanze di date è tramite il metodo replace() di una istanza di date esistente.

# datetime_date_replace.py
import datetime

d1 = datetime.date(2008, 3, 29)
print('d1:', d1.ctime())

d2 = d1.replace(year=2009)
print('d2:', d2.ctime())

Questo esempio modifica l'anno, lasciando mese e giorno invariati.

$ python3 datetime_date_replace.py

d1: Sat Mar 29 00:00:00 2008
d2: Sun Mar 29 00:00:00 2009

Intervalli Temporali (timedelta)

Date future e passate possono essere calcolate utilizzando operazioni aritmetiche su due oggetti datetime, oppure combinando un datetime con un timedelta. La sottrazione di date produce un timedelta; e un timedelta può essere aggiunto o sottratto da una data per produrne un'altra. I valori interi di timedelta sono conservati in giorni, secondi e microsecondi.

# datetime_timedelta.py

import datetime

print("microsecondi:", datetime.timedelta(microseconds=1))
print("millisecondi:", datetime.timedelta(milliseconds=1))
print("secondi     :", datetime.timedelta(seconds=1))
print("minuti      :", datetime.timedelta(minutes=1))
print("ore         :", datetime.timedelta(hours=1))
print("giorni      :", datetime.timedelta(days=1))
print("settimane   :", datetime.timedelta(weeks=1))

Valori di livello intermedio passati al costruttore sono convertiti in giorni, secondi e microsecondi.

$ python3 datetime_timedelta.py

microsecondi: 0:00:00.000001
millisecondi: 0:00:00.001000
secondi     : 0:00:01
minuti      : 0:01:00
ore         : 1:00:00
giorni      : 1 day, 0:00:00
settimane   : 7 days, 0:00:00

La durata completa di un intervallo temporale può essere recuperato come numero di secondi tramite total_seconds().

# datetime_timedelta_total_seconds.py

import datetime

for delta in [datetime.timedelta(microseconds=1),
              datetime.timedelta(milliseconds=1),
              datetime.timedelta(seconds=1),
              datetime.timedelta(minutes=1),
              datetime.timedelta(hours=1),
              datetime.timedelta(days=1),
              datetime.timedelta(weeks=1),
              ]:
    print('{:15} = {:8} secondi'.format(
        str(delta), delta.total_seconds())
    )

Il valore di ritorno è un numero a virgola mobile (float), per gestire durate inferiori al secondo.

$ python3 datetime_timedelta_total_seconds.py

0:00:00.000001  =    1e-06 secondi
0:00:00.001000  =    0.001 secondi
0:00:01         =      1.0 secondi
0:01:00         =     60.0 secondi
1:00:00         =   3600.0 secondi
1 day, 0:00:00  =  86400.0 secondi
7 days, 0:00:00 = 604800.0 secondi

Aritmetica sulle Date

Per compiere operazioni matematiche sulle date si utilizzano gli operatori aritmetici standard.

#!/usr/bin/env python
# -*- coding: UTF-8 -*-

import datetime

today = datetime.date.today()
print('Oggi     :', today)

one_day = datetime.timedelta(days=1)
print('Un giorno:', one_day)

yesterday = today - one_day
print('Ieri     :', yesterday)

tomorrow = today + one_day
print('Domani   :', tomorrow)

print('domani - ieri:', tomorrow - yesterday)
print('ieri - domani:', yesterday - tomorrow)

Questo esempio con oggetti date illustra l'uso di oggetti timedelta per calcolare nuove date, e per sottrarre istanze di date per produrre timedelta (incluso un intervallo temporale negativo).

$ python3 datetime_date_math.py

Oggi     : 2016-06-12
Un giorno: 1 day, 0:00:00
Ieri     : 2016-06-11
Domani   : 2016-06-13
domani - ieri: 2 days, 0:00:00
ieri - domani: -2 days, 0:00:00

Un oggetto timedelta supporta anche operazioni aritmetiche con interi, valori a virgola mobile e altre istanze di timedelta.

# datetime_timedelta_math.py

import datetime

one_day = datetime.timedelta(days=1)
print('1 giorno   :', one_day)
print('5 giorni   :', one_day * 5)
print('1.5 giorni :', one_day * 1.5)
print('1/4 giorno :', one_day / 4)

# si assume un ora per il pranzo
work_day = datetime.timedelta(hours=7)
meeting_length = datetime.timedelta(hours=1)
print('riunioni al giorno :', work_day / meeting_length)

In questo esempio, sono calcolati diversi multipli per un singolo giorno, e i risultanti timedelta contengono il numero di giorni od ore appropriato. L'esempio finale dimostra come calcolare valori combinando due oggetti timedelta. In questo caso il risultato è un numero a virgola mobile.

$ python3 datetime_timedelta_math.py

1 giorno   : 1 day, 0:00:00
5 giorni   : 5 days, 0:00:00
1.5 giorni : 1 day, 12:00:00
1/4 giorno : 6:00:00
riunioni al giorno : 7.0

Confrontare Valori

I valori sia di data che di orario possono essere confrontati tramite gli operatori di confronto standard per determinare quale è il più o il meno recente.

# datetime_comparing.py

import datetime
import time

print('Orari:')
t1 = datetime.time(12, 55, 0)
print('  t1:', t1)
t2 = datetime.time(13, 5, 0)
print('  t2:', t2)
print('  t1 < t2:', t1 < t2)

print
print('Date :')
d1 = datetime.date.today()
print('  d1:', d1)
d2 = datetime.date.today() + datetime.timedelta(days=1)
print('  d2:', d2)
print('  d1 > d2:', d1 > d2)

Tutti gli operatori di confronto sono supportati.

$ python3 datetime_comparing.py

Orari:
  t1: 12:55:00
  t2: 13:05:00
  t1 < t2: True
Date :
  d1: 2016-06-19
  d2: 2016-06-20
  d1 > d2: False

Combinare Date ed Orari

Si utilizza la classe datetime per conservare valori sia di data che di orario. Così come per date ci sono parecchi metodi di classe di convenienza per la creazione di istanze di datetime derivate da altri valori comuni.

# datetime_datetime.py

import datetime

print('Oggi      ,', datetime.datetime.today())
print('UTC adesso,', datetime.datetime.utcnow())
print

FIELDS = [
    ('year', 'anno'),
    ('month', 'mese'),
    ('day', 'giorno'),
    ('hour', 'ora'),
    ('minute', 'minuto'),
    ('second', 'secondi'),
    ('microsecond', 'microsecondi')
]

d = datetime.datetime.now()
for attr, descr in FIELDS:
    print('{:15}, {}'.format(descr, getattr(d, attr)))

Come ci si potrebbe aspettare, l'istanza di datetime ha tutti gli attributi degli oggetti date e time.

$ python3 datetime_datetime.py

Oggi      , 2016-06-18 16:31:57.334704
UTC adesso, 2016-06-18 14:31:57.334757
anno           , 2016
mese           , 6
giorno         , 18
ora            , 16
minuto         , 31
secondi        , 57
microsecondi   , 334779

Proprio come con date, datetime fornisce dei metodi di classe di convenienza per la creazione di nuove istanze. Include anche fromordinal() e fromtimestamp().

# datetime_datetime_combine.py

import datetime

t = datetime.time(1, 2, 3)
print('t :', t)

d = datetime.date.today()
print('d :', d)

dt = datetime.datetime.combine(d, t)
print('dt:', dt)

combine() crea istanze di datetime da una istanza di date e una di time.

$ python3 datetime_datetime_combine.py

t : 01:02:03
d : 2016-06-18
dt: 2016-06-18 01:02:03

Formattazione ed Elaborazione

La rappresentazione stringa predefinita di un oggetto datetime utilizza il formato ISO-8601 (YYYY-MM-DDTHH:MM:SS.mmmmmm). Formati alternativi possono essere generati utilizzando strftime().

# datetime_datetime_strptime.py

import datetime

format = "%a %b %d %H:%M:%S %Y"

today = datetime.datetime.today()
print('ISO     :', today)

s = today.strftime(format)
print('strftime:', s)

d = datetime.datetime.strptime(s, format)
print('strptime:', d.strftime(format))

Si usa datetime.strptime() per convertire stringhe formattate in istanze di datetime.

$ python3 datetime_datetime_strptime.py

ISO     : 2016-06-18 16:49:00.075603
strftime: Sat Jun 18 16:49:00 2016
strptime: Sat Jun 18 16:49:00 2016

Gli stessi codici di formattazione possono essere utilizzati con il mini linguaggio di formattazione stringhe di Python piazzandoli dopo il : nella specifica del campo della stringa di formato.

# datetime_format.py

import datetime

today = datetime.datetime.today()
print('ISO     :', today)
print('format(): {:%a %b %d %H:%M:%S %Y}'.format(today))

Ciascun codice di formato di datetime deve essere preceduto da % e tutti i successivi : sono trattati come caratteri letterali da includere nell'output.

$ python3 datetime_format.py

ISO     : 2016-06-18 16:52:39.249801
format(): Sat Jun 18 16:52:39 2016

La tabella seguente mostra tutti i codici di formattazione per le ore 15.00 del pomeriggio del 5 giugno 2016 con la localizzazione italiana.

SIMBOLO SIGNIFICATO ESEMPIO
%a Giorno della settimana abbreviato 'dom'
%A Nome del giorno della settimana 'domenica'
%w Numero del giorno della settimana - da 0 (Domenica) a 6 (Sabato) '0'
%d Giorno del mese (con zero iniziali) '05'
%b Nome del mese abbreviato 'giu'
%B Nome del mese completo 'giugno'
%m Mese nell'anno '06'
%y Anno senza cifre del secolo '16'
%Y Anno completo '2016'
%H Ora su 24 '15'
%I Ora su 12 '03'
%p AM/PM ''
%M Minuti '00'
%S Secondi '00'
%f Microsecondi '000000'
%z scostamento UTC per oggetti che tengono conto della zone di fuso orario ''
%Z Nome della zona di fuso orario ''
%j Numero giorno nell'anno '157'
%W Numero settimana nell'anno '22'
%c Rappresentazione di data e ora per la localizzazione corrente '05/06/2015 15:00:00'
%x Rappresentazione di data per la localizzazione corrente '05/06/16'
%X Rappresentazione di ora per la localizzazione corrente '15:00:00'
%% Il carattere letterale % '%'

Zone di Fuso Orario

All'interno di datetime, le zone di fuso orario sono rappresentate da sottoclassi di tzinfo. Visto che tzinfo è una classe base astratta, le applicazioni devono definire una sottoclasse e fornire le implementazioni appropriate per alcuni metodi affinchè siano utilizzabili.

datetime include una implementazione, poco sofisticata se vogliamo, nella classe timezone che utilizza uno scostamento fisso rispetto all'UTC e non supporta valori di scostamento diversi per diversi giorni dell'anno, a esempio laddove viene applicata l'ora legale, oppure laddove lo scostamento dall'UTC sia mutato nel tempo.

# datetime_timezone.py

import datetime

min6 = datetime.timezone(datetime.timedelta(hours=-6))
plus6 = datetime.timezone(datetime.timedelta(hours=6))
d = datetime.datetime.now(min6)

print(min6, ':', d)
print(datetime.timezone.utc, ':',
      d.astimezone(datetime.timezone.utc))
print(plus6, ':', d.astimezone(plus6))

# conversione alla zona di fuso orario corrente del sistema
d_system = d.astimezone()
print(d_system.tzinfo, '      :', d_system)

https://it.wikipedia.org/wiki/Tempo_coordinato_universale https://it.wikipedia.org/wiki/Tempo_coordinato_universale Per convertire un valore datetime da una zona di fuso orario all'altra si utilizzi astimezone(). Nell'esempio qui sopra sono mostrate due zone di fuso orario separate da 6 ore dalle parti opposte rispetto all'UTC, viene inoltre utilizzata l'istanza utc da datetime.timezone come riferimento. L'output finale mostra il valore nella zona di fuso orario del sistema, acquisita dalla chiamata di astimezone() senza argomenti.

$ python3 datetime_timezone.py

UTC-06:00 : 2016-06-19 02:05:43.105264-06:00
UTC+00:00 : 2016-06-19 08:05:43.105264+00:00
UTC+06:00 : 2016-06-19 14:05:43.105264+06:00
Una migliore implementazione per le zone di fuso orario è fornita dal modulo di terze parti pytz. Supporta zone di fuso orario definite e il database degli scostamenti viene mantenuto aggiornato non appena le modifiche vengono ratificate dagli organi politici in tutto il mondo.

Vedere anche:

datetime
La documentazione della libreria standard per questo modulo.
Note di portabilità
Note di portabilità per datetime
calendar
Il modulo calendar
dateutil
dateutil da Labix estende il modulo datetime con funzionalità aggiuntive
pytz
Database delle zone di fuso orario nel mondo e classi per rendere gli oggetti datetime consapevoli delle zone di fuso orario
Wikipedia: Calendario Gregoriano prolettico
Una descrizione dei sistema di calendario Gregoriano
ISO 8601
La rappresentazione numerica standard di date e orari