Introducci贸n
Contenido
Uno de los muchos problemas comunes que enfrentamos en el desarrollo de software es el manejo de fechas y horas. Despu茅s de obtener una cadena de fecha y hora de una API, por ejemplo, necesitamos convertirla a un formato legible por humanos. Nuevamente, si se usa la misma API en diferentes zonas horarias, la conversi贸n ser谩 diferente. Una buena biblioteca de fecha y hora deber铆a convertir la hora seg煤n la zona horaria. Este es solo uno de los muchos matices que deben manejarse cuando se trata de fechas y horas.
Afortunadamente, Python viene con el m贸dulo incorporado datetime
para tratar con fechas y horas. Como probablemente adivin贸, viene con varias funciones para manipular fechas y horas. Con este m贸dulo, podemos analizar f谩cilmente cualquier cadena de fecha y hora y convertirla en una datetime
objeto.
Conversi贸n de cadenas con datetime
los fecha y hora El m贸dulo consta de tres tipos de objetos diferentes: date
, time
y datetime
. Obviamente el date
el objeto contiene la fecha, time
retiene el tiempo, y datetime
contiene la fecha y la hora.
Por ejemplo, el siguiente c贸digo imprimir谩 la fecha y hora actuales:
import datetime
print ('Current date/time: {}'.format(datetime.datetime.now()))
Ejecutar este c贸digo imprimir谩 algo similar a esto:
$ python3 datetime-print-1.py
Current date/time: 2018-06-29 08:15:27.243860
Cuando no se proporciona un formato personalizado, se utiliza el formato de cadena predeterminado, es decir, el formato para “2018-06-29 08: 15: 27.243860” est谩 en ISO 8601 formato (AAAA-MM-DDTHH: MM: SS.mmmmmm). Si nuestra cadena de entrada para crear un datetime
El objeto tiene el mismo formato ISO 8601, podemos analizarlo f谩cilmente en un datetime
objeto.
Echemos un vistazo al siguiente c贸digo:
import datetime
date_time_str="2018-06-29 08:15:27.243860"
date_time_obj = datetime.datetime.strptime(date_time_str, '%Y-%m-%d %H:%M:%S.%f')
print('Date:', date_time_obj.date())
print('Time:', date_time_obj.time())
print('Date-time:', date_time_obj)
Ejecutarlo imprimir谩 la fecha, hora y fecha-hora:
$ python3 datetime-print-2.py
Date: 2018-06-29
Time: 08:15:27.243860
Date-time: 2018-06-29 08:15:27.243860
En este ejemplo, estamos usando un nuevo m茅todo llamado strptime
. Este m茅todo toma dos argumentos: el primero es la representaci贸n de cadena de la fecha y hora y el segundo es el formato de la cadena de entrada. Especificar el formato de esta manera hace que el an谩lisis sea mucho m谩s r谩pido, ya que datetime
no necesita intentar interpretar el formato por s铆 solo, que es mucho m谩s costoso computacionalmente. El valor de retorno es del tipo datetime
.
En nuestro ejemplo, "2018-06-29 08:15:27.243860"
es la cadena de entrada y "%Y-%m-%d %H:%M:%S.%f"
es el formato de nuestra cadena de fecha. El retorno datetime
el valor se almacena en date_time_obj
variable. Dado que este es un datetime
objeto, podemos llamar al date()
y time()
m茅todos directamente en 茅l. Como puede ver en la salida, imprime la parte ‘fecha’ y ‘hora’ de la cadena de entrada.
Quiz谩s se pregunte cu谩l es el significado del formato "%Y-%m-%d %H:%M:%S.%f"
. Estos se conocen como tokens de formato. Cada token representa una parte diferente de la fecha y hora, como d铆a, mes, a帽o, etc. documentaci贸n strptime para ver la lista de todos los diferentes tipos de c贸digo de formato admitidos en Python. Para una referencia r谩pida, esto es lo que estamos usando en el c贸digo anterior:
%Y
: A帽o (4 d铆gitos)%m
: Mes%d
: Dia del mes%H
: Hora (24 horas)%M
: Minutos%S
: Segundos%f
: Microsegundos
Se espera que todos estos tokens, excepto el a帽o, tengan un relleno de ceros.
Entonces, si se conoce el formato de una cadena, se puede analizar f谩cilmente a un datetime
objeto usando strptime
. D茅jame mostrarte un ejemplo m谩s no trivial:
import datetime
date_time_str="Jun 28 2018 7:40AM"
date_time_obj = datetime.datetime.strptime(date_time_str, '%b %d %Y %I:%M%p')
print('Date:', date_time_obj.date())
print('Time:', date_time_obj.time())
print('Date-time:', date_time_obj)
En el siguiente resultado, puede ver que la cadena se analiz贸 correctamente, ya que el datetime
objeto aqu铆:
$ python3 datetime-print-3.py
Date: 2018-06-28
Time: 07:40:00
Date-time: 2018-06-28 07:40:00
A continuaci贸n, se muestran algunos ejemplos m谩s de formatos de tiempo de uso com煤n y los tokens utilizados para el an谩lisis:
"Jun 28 2018 at 7:40AM" -> "%b %d %Y at %I:%M%p"
"September 18, 2017, 22:19:55" -> "%B %d, %Y, %H:%M:%S"
"Sun,05/12/99,12:30PM" -> "%a,%d/%m/%y,%I:%M%p"
"Mon, 21 March, 2015" -> "%a, %d %B, %Y"
"2018-03-12T10:12:45Z" -> "%Y-%m-%dT%H:%M:%SZ"
Puede analizar una cadena de fecha y hora de cualquier formato utilizando la tabla mencionada en la documentaci贸n strptime.
Tratar con las zonas horarias y la fecha y hora
El manejo de fechas y horas se vuelve m谩s complejo al tratar con zonas horarias. Todos los ejemplos anteriores que hemos discutido son ingenuos datetime
objetos, es decir, estos objetos no contienen datos relacionados con la zona horaria. los datetime
el objeto tiene una variable que contiene la informaci贸n de la zona horaria, tzinfo
.
import datetime as dt
dtime = dt.datetime.now()
print(dtime)
print(dtime.tzinfo)
Este c贸digo imprimir谩:
$ python3 datetime-tzinfo-1.py
2018-06-29 22:16:36.132767
None
La salida de tzinfo
es None
ya que es un ingenuo datetime
objeto. Para la conversi贸n de zona horaria, una biblioteca llamada pytz
est谩 disponible para Python. Puede instalarlo como se describe en estas instrucciones. Ahora, usemos el pytz
biblioteca para convertir la marca de tiempo anterior a UTC.
import datetime as dt
import pytz
dtime = dt.datetime.now(pytz.utc)
print(dtime)
print(dtime.tzinfo)
Salida:
$ python3 datetime-tzinfo-2.py
2018-06-29 17:08:00.586525+00:00
UTC
+00:00
es la diferencia entre la hora mostrada y la hora UTC. En este ejemplo, el valor de tzinfo
pasa a ser UTC tambi茅n, de ah铆 el 00:00
compensar. En este caso, el datetime
El objeto es un objeto que reconoce la zona horaria.
De manera similar, podemos convertir cadenas de fecha y hora a cualquier otra zona horaria. Por ejemplo, podemos convertir la cadena “2018-06-29 17: 08: 00.586525 + 00: 00” a la zona horaria “America / New_York”, como se muestra a continuaci贸n:
import datetime as dt
import pytz
date_time_str="2018-06-29 17:08:00"
date_time_obj = dt.datetime.strptime(date_time_str, '%Y-%m-%d %H:%M:%S')
timezone = pytz.timezone('America/New_York')
timezone_date_time_obj = timezone.localize(date_time_obj)
print(timezone_date_time_obj)
print(timezone_date_time_obj.tzinfo)
Salida:
$ python3 datetime-tzinfo-3.py
2018-06-29 17:08:00-04:00
America/New_York
Primero, hemos convertido la cadena en un datetime
objeto, date_time_obj
. Luego lo convertimos a una zona horaria habilitada datetime
objeto, timezone_date_time_obj
. Dado que hemos establecido la zona horaria como “America / New_York”, la hora de salida muestra que est谩 4 horas por detr谩s de la hora UTC. Puedes comprobar esta p谩gina de Wikipedia para encontrar la lista completa de zonas horarias disponibles.
Conversi贸n de zonas horarias
Podemos convertir la zona horaria de un datetime
objeto de una regi贸n a otra, como se muestra en el siguiente ejemplo:
import datetime as dt
import pytz
timezone_nw = pytz.timezone('America/New_York')
nw_datetime_obj = dt.datetime.now(timezone_nw)
timezone_london = pytz.timezone('Europe/London')
london_datetime_obj = nw_datetime_obj.astimezone(timezone_london)
print('America/New_York:', nw_datetime_obj)
print('Europe/London:', london_datetime_obj)
Primero, creamos un objeto de fecha y hora con la hora actual y lo configuramos como la zona horaria “Am茅rica / New_York”. Luego usando el astimezone()
m茅todo, hemos convertido este datetime
a la zona horaria “Europa / Londres”. Ambos datetime
s imprimir谩 diferentes valores como:
$ python3 datetime-tzinfo-4.py
America/New_York: 2018-06-29 22:21:41.349491-04:00
Europe/London: 2018-06-30 03:21:41.349491+01:00
Como era de esperar, las fechas y horas son diferentes, ya que tienen una diferencia de aproximadamente 5 horas.
Uso de bibliotecas de terceros
Python datetime
m贸dulo puede convertir todos los diferentes tipos de cadenas en un datetime
objeto. Pero el problema principal es que para hacer esto necesita crear la cadena de c贸digo de formato adecuada que strptime
puede entender. La creaci贸n de esta cadena lleva tiempo y dificulta la lectura del c贸digo. En cambio, podemos usar otras bibliotecas de terceros para hacerlo m谩s f谩cil.
En algunos casos, estas bibliotecas de terceros tambi茅n tienen un mejor soporte integrado para manipular y comparar fechas y horas, y algunas incluso tienen zonas horarias integradas, por lo que no es necesario incluir un paquete adicional.
Echemos un vistazo a algunas de estas bibliotecas en las siguientes secciones.
dateutil
los m贸dulo dateutil es una extensi贸n de la datetime
m贸dulo. Una ventaja es que no necesitamos pasar ning煤n c贸digo de an谩lisis para analizar una cadena. Por ejemplo:
from dateutil.parser import parse
datetime = parse('2018-06-29 22:21:41')
print(datetime)
Esta parse
La funci贸n analizar谩 la cadena autom谩ticamente y la almacenar谩 en el datetime
variable. El an谩lisis se realiza autom谩ticamente. No es necesario que menciones ninguna cadena de formato. Intentemos analizar diferentes tipos de cadenas usando dateutil
:
from dateutil.parser import parse
date_array = [
'2018-06-29 08:15:27.243860',
'Jun 28 2018 7:40AM',
'Jun 28 2018 at 7:40AM',
'September 18, 2017, 22:19:55',
'Sun, 05/12/1999, 12:30PM',
'Mon, 21 March, 2015',
'2018-03-12T10:12:45Z',
'2018-06-29 17:08:00.586525+00:00',
'2018-06-29 17:08:00.586525+05:00',
'Tuesday , 6th September, 2017 at 4:30pm'
]
for date in date_array:
print('Parsing: ' + date)
dt = parse(date)
print(dt.date())
print(dt.time())
print(dt.tzinfo)
print('n')
Salida:
$ python3 dateutil-1.py
Parsing: 2018-06-29 08:15:27.243860
2018-06-29
08:15:27.243860
None
Parsing: Jun 28 2018 7:40AM
2018-06-28
07:40:00
None
Parsing: Jun 28 2018 at 7:40AM
2018-06-28
07:40:00
None
Parsing: September 18, 2017, 22:19:55
2017-09-18
22:19:55
None
Parsing: Sun, 05/12/1999, 12:30PM
1999-05-12
12:30:00
None
Parsing: Mon, 21 March, 2015
2015-03-21
00:00:00
None
Parsing: 2018-03-12T10:12:45Z
2018-03-12
10:12:45
tzutc()
Parsing: 2018-06-29 17:08:00.586525+00:00
2018-06-29
17:08:00.586525
tzutc()
Parsing: 2018-06-29 17:08:00.586525+05:00
2018-06-29
17:08:00.586525
tzoffset(None, 18000)
Parsing: Tuesday , 6th September, 2017 at 4:30pm
2017-09-06
16:30:00
None
Puede ver que casi cualquier tipo de cadena se puede analizar f谩cilmente utilizando el dateutil
m贸dulo.
Si bien esto es conveniente, recuerde que tener que predecir el formato hace que el c贸digo sea mucho m谩s lento, por lo que si su c贸digo requiere un alto rendimiento, entonces este podr铆a no ser el enfoque correcto para su aplicaci贸n.
maya
maya tambi茅n hace que sea muy f谩cil analizar una cadena y cambiar las zonas horarias. Aqu铆 se muestran algunos ejemplos simples:
import maya
dt = maya.parse('2018-04-29T17:45:25Z').datetime()
print(dt.date())
print(dt.time())
print(dt.tzinfo)
Salida:
$ python3 maya-1.py
2018-04-29
17:45:25
UTC
Para convertir la hora a una zona horaria diferente:
import maya
dt = maya.parse('2018-04-29T17:45:25Z').datetime(to_timezone="America/New_York", naive=False)
print(dt.date())
print(dt.time())
print(dt.tzinfo)
Salida:
$ python3 maya-2.py
2018-04-29
13:45:25
America/New_York
驴No es tan f谩cil de usar? Vamos a probar maya
con el mismo conjunto de cadenas que hemos usado con dateutil
:
import maya
date_array = [
'2018-06-29 08:15:27.243860',
'Jun 28 2018 7:40AM',
'Jun 28 2018 at 7:40AM',
'September 18, 2017, 22:19:55',
'Sun, 05/12/1999, 12:30PM',
'Mon, 21 March, 2015',
'2018-03-12T10:12:45Z',
'2018-06-29 17:08:00.586525+00:00',
'2018-06-29 17:08:00.586525+05:00',
'Tuesday , 6th September, 2017 at 4:30pm'
]
for date in date_array:
print('Parsing: ' + date)
dt = maya.parse(date).datetime()
print(dt)
print(dt.date())
print(dt.time())
print(dt.tzinfo)
Salida:
$ python3 maya-3.py
Parsing: 2018-06-29 08:15:27.243860
2018-06-29 08:15:27.243860+00:00
2018-06-29
08:15:27.243860
UTC
Parsing: Jun 28 2018 7:40AM
2018-06-28 07:40:00+00:00
2018-06-28
07:40:00
UTC
Parsing: Jun 28 2018 at 7:40AM
2018-06-28 07:40:00+00:00
2018-06-28
07:40:00
UTC
Parsing: September 18, 2017, 22:19:55
2017-09-18 22:19:55+00:00
2017-09-18
22:19:55
UTC
Parsing: Sun, 05/12/1999, 12:30PM
1999-05-12 12:30:00+00:00
1999-05-12
12:30:00
UTC
Parsing: Mon, 21 March, 2015
2015-03-21 00:00:00+00:00
2015-03-21
00:00:00
UTC
Parsing: 2018-03-12T10:12:45Z
2018-03-12 10:12:45+00:00
2018-03-12
10:12:45
UTC
Parsing: 2018-06-29 17:08:00.586525+00:00
2018-06-29 17:08:00.586525+00:00
2018-06-29
17:08:00.586525
UTC
Parsing: 2018-06-29 17:08:00.586525+05:00
2018-06-29 12:08:00.586525+00:00
2018-06-29
12:08:00.586525
UTC
Parsing: Tuesday , 6th September, 2017 at 4:30pm
2017-09-06 16:30:00+00:00
2017-09-06
16:30:00
UTC
Como puede ver, todos los formatos de fecha se analizaron correctamente.
驴Pero not贸 la diferencia? Si no proporcionamos la informaci贸n de la zona horaria, la convierte autom谩ticamente a UTC. Por lo tanto, es importante tener en cuenta que debemos proporcionar to_timezone
y naive
par谩metros si la hora no est谩 en UTC.
Flecha
Flecha es otra biblioteca para lidiar con la fecha y hora en Python. Y como antes con maya
, tambi茅n determina el formato de fecha y hora autom谩ticamente. Una vez interpretado, devuelve un Python datetime
objeto del arrow
objeto.
Probemos esto con la misma cadena de ejemplo que hemos usado para maya
:
import arrow
dt = arrow.get('2018-04-29T17:45:25Z')
print(dt.date())
print(dt.time())
print(dt.tzinfo)
Salida:
$ python3 arrow-1.py
2018-04-29
17:45:25
tzutc()
Y as铆 es como puedes usar arrow
para convertir zonas horarias usando el to
m茅todo:
import arrow
dt = arrow.get('2018-04-29T17:45:25Z').to('America/New_York')
print(dt)
print(dt.date())
print(dt.time())
Salida:
$ python3 arrow-2.py
2018-04-29T13:45:25-04:00
2018-04-29
13:45:25
Como puede ver, la cadena de fecha y hora se convierte a la regi贸n “America / New_York”.
Ahora, usemos nuevamente el mismo conjunto de cadenas que hemos usado anteriormente:
import arrow
date_array = [
'2018-06-29 08:15:27.243860',
#'Jun 28 2018 7:40AM',
#'Jun 28 2018 at 7:40AM',
#'September 18, 2017, 22:19:55',
#'Sun, 05/12/1999, 12:30PM',
#'Mon, 21 March, 2015',
'2018-03-12T10:12:45Z',
'2018-06-29 17:08:00.586525+00:00',
'2018-06-29 17:08:00.586525+05:00',
#'Tuesday , 6th September, 2017 at 4:30pm'
]
for date in date_array:
dt = arrow.get(date)
print('Parsing: ' + date)
print(dt)
print(dt.date())
print(dt.time())
print(dt.tzinfo)
Este c贸digo fallar谩 para las cadenas de fecha y hora que se han comentado, que es m谩s de la mitad de nuestros ejemplos. La salida para otras cadenas ser谩:
$ python3 arrow-3.py
Parsing: 2018-06-29 08:15:27.243860
2018-06-29T08:15:27.243860+00:00
2018-06-29
08:15:27.243860
tzutc()
Parsing: 2018-03-12T10:12:45Z
2018-03-12T10:12:45+00:00
2018-03-12
10:12:45
tzutc()
Parsing: 2018-06-29 17:08:00.586525+00:00
2018-06-29T17:08:00.586525+00:00
2018-06-29
17:08:00.586525
tzoffset(None, 0)
Parsing: 2018-06-29 17:08:00.586525+05:00
2018-06-29T17:08:00.586525+05:00
2018-06-29
17:08:00.586525
tzoffset(None, 18000)
Para analizar correctamente las cadenas de fecha y hora que he comentado, deber谩 pasar los tokens de formato correspondientes para dar a la biblioteca pistas sobre c贸mo analizarlo. Por ejemplo, “MMM” para el nombre de los meses, como “Ene, Feb, Mar”, etc. Puede marcar esta gu铆a para todos los tokens disponibles.
Conclusi贸n
En este art铆culo hemos mostrado diferentes formas de analizar una cadena a un datetime
objeto en Python. Puede optar por el Python predeterminado datetime
library o cualquiera de las bibliotecas de terceros mencionadas en este art铆culo, entre muchas otras.
El principal problema con el defecto datetime
paquete es que necesitamos especificar el c贸digo de an谩lisis manualmente para casi todos los formatos de cadena de fecha y hora. Por lo tanto, si su formato de cadena cambia en el futuro, es probable que tambi茅n tenga que cambiar su c贸digo. Pero muchas bibliotecas de terceros, como las que se mencionan aqu铆, lo manejan autom谩ticamente.
Otro problema al que nos enfrentamos es el de las zonas horarias. La mejor manera de manejarlos es siempre almacenar la hora en su base de datos como formato UTC y luego convertirla a la zona horaria local del usuario cuando sea necesario.
Estas bibliotecas no solo son buenas para analizar cadenas, sino que tambi茅n pueden usarse para muchos tipos diferentes de operaciones relacionadas con la fecha y la hora. Te animo a que revises los documentos para conocer las funcionalidades en detalle.