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.