10 - Curso de Python - Datetime

From ChuWiki
Revision as of 16:56, 31 July 2022 by Chudiang (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Cualquier duda suelo atender en este foro de python

Todo el código de este curso de Python gratuito está en github https://github.com/chuidiang/chuidiang-ejemplos/tree/master/PYTHON/curso-python. En línea comandos python tienes como arrancar la consola de comandos de python por si quieres ir probando lo relativo a variables y tipos de datos en python.

Anterior: 09 - Curso de Python - Clases en Python -- Índice: Curso de Python -- Siguiente: 11 - Curso de Python - Bases de datos.

Datetime[edit]

Las funciones de fecha y hora se obtienen importando el módulo datetime en python. Como hay muchas funciones, vamos a importarlas todas de golpe y así podemos ir explicándolas poco a poco.

>>> from datetime import *
>>>

Con esto importamos montondes de cosas. Vamos poco a poco. Importamos tres clases date, time y datetime. La primera representa una fecha, la segunda una hora y la tercera el conjunto de fecha y hora.

Ejemplos con date[edit]

>>> un_dia=date(2022,7,25)
>>> un_dia
datetime.date(2022, 7, 25)
>>> un_dia=date(2022,6,31)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: day is out of range for month
>>> un_dia=date(2022,7,31)

Podemos crear una fecha pasando tres parámetros en el constructor de la clase date: el año, el mes y el día. El año va entre las constantes MINYEAR y MAXYEAR que tembién importamos del módulo. Valen 1 y 9999 respectivamente

>>> MINYEAR
1
>>> MAXYEAR
9999

Si seguimos con el ejemplo de creación de fecha, vemos que el date de python es listo, sabe que Junio no tiene día 31 y por eso poner el 31 de Junio de un error. No así si ponemos 31 de Julio.

Podemos comparar fechas, como en el siguiente ejemplo

>>> un_dia=date(2021,7,6)
>>> otro_dia=date(2022,7,6)
>>> un_dia<otro_dia
True

y podemos restar fechas, pero no sumarlas.

>>> un_dia-otro_dia
datetime.timedelta(days=-365)

Devuelve un timedelta que es una clase que veremos un poco más adalente, pero que representa un período de tiempo, -365 días en este caso, ya que hemos restado la fecha más antigua de la mas moderna.

date.today() nos devuelve la fecha de hoy.

>>> date.today()
datetime.date(2022, 7, 25)

Ejemplos con time[edit]

De la misma forma que date para fechas, tenemos time. Aunque teóricamente debería ser similar a date, no tiene tantas funciones. Veamos ejemplos

>>> hora1 = time(16,53,45,123456)
>>> hora1
datetime.time(16, 53, 45, 123456)
>>> hora2 = time(13,43,21,213452)
>>> hora2
datetime.time(13, 43, 21, 213452)
>>> hora1 < hora2
False

Podemos crear time pasando como parámetros hora (de 0 a 23), minutos y segundos (de 0 a 59) y los microsegundos (de 0 a 999999). Hay más parámetros, pero los mencionaremos más adelante. Podemos comparar horas para saber si una es mayor que otra. Pero no podemos restar horas ni tampoco podemos obtener con esta clase la hora actual.

Ejemplos con python datetime[edit]

La clase de python datetime tiene la suma de date y time. Podemos ponerle fecha y hora. Veamos ejemplos

>>> datetime1 = datetime(2022,7,25,16,43,32,123456)
>>> datetime1
datetime.datetime(2022, 7, 25, 16, 43, 32, 123456)
>>> datetime2 = datetime(2022,6,3,21, 3, 5, 332233)
>>> datetime1-datetime2
datetime.timedelta(days=51, seconds=70826, microseconds=791223)
>>> datetime1 > datetime2
True

Creamos dos datetime con determinados valores y luego podemos restarlos o comparar si uno es mayor que otro. La resta nos da una instancia de timedelta con diferencia en dias (por el date, segundos y microsegundos (por el time. Podemos obtener la fecha y hora actual con datetime.now()

>>> datetime.now()
datetime.datetime(2022, 7, 25, 17, 1, 37, 730464)

Vimos que para time no se podía obtener la hora actual. A partir de datetime.now() podemos sacar la hora actual de esta manera

>>> datetime.now().time()
datetime.time(17, 4, 3, 266235)

Es decir, obteniendo la parte time() del datetime.

Datetime format[edit]

Normalmente querremos presentar la fecha en un foramto legible, otras veces quizás queramos leerla de una cadena de texto que esté en un fichero o haya introducido el usuario. Es decir, convertir datetime a string o convertir string a datetime. Python tiene una conversión por defecto de datetime a string y de string a datetime

>>> ahora = datetime.now()
>>> str(ahora)
'2022-07-25 17:07:52.007419'
>>> ahora.isoformat()
'2022-07-25T17:07:52.007419'

Podemos usar la función str() de Python que vale para convertir cualqueir valor a string. O podemos usar la función isoformat() que convierte el datetime a string en el formato que se ve en el ejemplo.

>>> otro_ahora = datetime.fromisoformat('2022-07-25T17:07:52.007419')
>>> otro_ahora
datetime.datetime(2022, 7, 25, 17, 7, 52, 7419)

La función fromisoformat() convierte de string a datetime, siempre que el string sea de ese formato.

Pero claro, quizás no siempre tenemos o queremos el fomato ISO, lo tenemos o lo queremos de otra forma. Tenemos entonces las funciones strftime() y strptime(). La primera permite convertir datatiem a string, indicando nosotros como queremos que sea el string. La segunda permite convertir de string a datetime indicando, nuevamente nosotros, como va a ser el string que recibimos.

El formato sería un string adicional donde a base de caracteres especiales, indicamos los trozos de datetime en el string. La tabla con todas las posiblidades es un poco compleja pero la ponemos aquí sólo como referencia. Echale un ojo rápido para ver, por ejemplo, que podriamos poner el día de la semana como texto abreviado, o completo, igual con el mes, etc. Vemos detrás de la tabla algunos ejemplos concretos para que veas cómo se usa todo esto.

Directiva Significado Ejemplo
%a Día de la semana abreviado según idioma Sun, Mon, …, Sat (inglés)
%A Día de la semana completo según idioma. Sunday, Monday, …, Saturday (inglés)
%w Día de la semana como un número, 0 es domingo y 6 es Sábado 0, 1, …, 6
%d Día del mes con dos cifras, cero delante si hace falta 01, 02, …, 31
%b Nombre del mes abreviado según idioma Jan, Feb, …, Dec (inglés)
%B Nombre del mes completo según idioma January, February,…, December (inglés)
%m Número del mes con dos cifras, cero delante si hace falta 01, 02, …, 12
%y Año con dos cifras, con cero delante si hace falta 00, 01, …, 99
%Y Año con cuatro cifras, ceros delante si hacen falta 0001, 0002, …, 2013,2014, …, 9998, 9999
%H Hora de 0 a 24 con dos cifras, cero delante si hace falta 00, 01, …, 23
%I Hora de 0 1 12 con dos cifras, cero delante si hace falta 01, 02, …, 12
%p AM o PM según idioma AM, PM (inglés)
%M Minutos con dos cifras, ceros delante si hacen falta. 00, 01, …, 59
%S Segundos con dos cifras, ceros delante si hacen falta 00, 01, …, 59
%f microsegundos con seis cifras, ceros delante si hacen falta 000000, 000001, …,999999
%z Offset respecto a la hora UTC en el formato ±HHMM[SS[.ffffff]]. Cadena vacía si no hay dato de zona horaria (nada), +0000, -0400, +1030, +063415, -030712.345216
%Z Zona horaria. Cadena vacía si no hay dato de zona horaria (nada), UTC, GMT
%j Día del año con tres cifras, ceros delante si hacen falta 001, 002, …, 366
%U Número de semana del año con dos cifras suponiendo que la semana empieza el Domingo, ceros delante si hacen falta. Los días del año anterior al primer Domingo del año se consideran de la semana 0. 00, 01, …, 53
%W Número de semana del año con dos cifras suponiendo que la semana empieza el Lunes, ceros delante si hacen falta. Los días del año anterior al primer Lunes del año se consideran de la semana 0. 00, 01, …, 53
%c Fecha y hora de la forma estándar que se suele usar en un país Tue Aug 16 21:30:00 (inglés Estados Unidos)
%x Fecha en la forma estándar que se suele usar en un país 08/16/88 (ningún país en concreto); 08/16/1988 (inglés Estados Unidos);
%X Hora en la forma estándar que se suele usar en un país 21:30:00 (inglés Estados Unidos)
%% El caracter %. %

Bueno, la tabla es un poco larga y hay alguna opción más. Esta tabla corresponde a un estándar llamado 1989 C standard. También hay algunas "letras" más para el estándar ISO 8601. La diferencia entre los dos es cuándo consideran la semana 0. El primero, el de la tabla que hemos puesto, considera que la semana 0 son aquellos días del año que están antes del primer Lunes o Domingo del año. Mientras que la ISO 8601 considera que la semana 1 es la que contenga el primer jueves del año.

Vamos a ver algún ejemplo sobre como sacar la fecha con distintos formatos. No vamos a complicarnos la vida con formatos raros, solo algunos ejemplos sencillos paa que veas el funcionamiento.

>>> ahora = datetime.now()

>>> # %A da el dia de el nombre del dia de la semana completo
>>> ahora.strftime('%A')
'Monday'

>>> # Toda una frase
>>> ahora.strftime('El dia %j del año %Y es %A')
'El dia 206 del año 2022 es Monday'

>>> # Un formato estándar para un pais concreto. Ahora veremos esto del pais.
>>> ahora.strftime('%c')
'Mon Jul 25 20:27:57 2022'

Bueno, ves que podemos hacer frase completas como queramos y mostrar la parte del datetime que nos interese en el formato que nos interese. La función strptime hace lo contrario. Veamos algunos ejemplos también, eso sí, no damos datos suficientes, no saldrá bien la fecha. Por ejemplo ignora el día de la semana, pero podemos dar por ejemplo el día del año y el año. Un par de ejemplos, el primer parámetro es el string con los datos del datetime concreto y el segundo parámetro el formato que quremos usar.

>>> # Usando el formato específico del país.
>>> datetime.strptime('Mon Jul 25 20:27:57 2022','%c')
datetime.datetime(2022, 7, 25, 20, 27, 57)

>>> # Usando el día del año y el año. El día 13 del año 2022 es el 13 de Enero de 2022.
>>> datetime.strptime('13 2022','%j %Y')
datetime.datetime(2022, 1, 13, 0, 0)

Datetime Locale[edit]

Si te has fijado arriba, decíamos cosas como que el mes o el día de la semana se pondrían en el del país o idioma concreto que se esté usando. También decíamos que se podía presentar el datetime en el formato estándar de un país concreto. En los ejemplos nos sale todo en inglés. ¿Por qué?. Porque no hemos dicho en ningún momento qué idioma queremos, por defecto, inglés.

Para indicar el idioma, tenemos que importar el módulo locale y ahí llamar a locale.setlocale(clave,valor). La clave puede ser cualquiera de una serie de constantes predefinidas. Por ejemplo, para cambair el símbolo de la moneda (dolares $, euros €, etc). Para cambiar el idioma de textos como día de la semana o nombre de mes, para indicar si queremos comas o puntos decimales en los números, separadores de miles, etc. Sin embargo, tenemos una forma cómoda de cambiar todo de golpe que es usar como clave locale.LC_ALL. El segundo parámetro es el idioma y país usando una codificación estándar y separado por un guión bajo. Por ejemplo, inglés de Estados Unidos es en_US y el de inglaterra sería en_UK. El de español de españa es_ES y el de Argentina es_AR. Si pasas como segundo parámetro una cadena vacía , cogerá el idioma del sistema operativo.

Vamos a ver ejemplos


>>> import locale
>>> locale.setlocale(locale.LC_ALL, '')
'Spanish_Spain.1252'
>>> ahora.strftime('%c')
'25/07/2022 20:27:57'
>>> ahora.strftime("%A %d de %B del %Y - %H:%M")
'lunes 25 de julio del 2022 - 20:27'

>>> locale.setlocale(locale.LC_ALL, 'de_DE')
'de_DE'
>>> ahora.strftime('%c')
'25.07.2022 20:27:57'
>>> ahora.strftime("%A %d de %B del %Y - %H:%M")
'Montag 25 de Juli del 2022 - 20:27'

Como puedes ver, mi ordenador está en español de España, así que es lo que ha cogido locale.LC_ALL con la cadena vacía ''. Luego lo hemos pasado a Alemán.

Timedelta[edit]

Hemos visto arriba que podemos restar date y datetime. El resultado es un timedelta. Este tiemdelta es un incremento de datetime y podemos usarlo para sumar o restar intervalos de tiempo a datetime concretos. El constructor de timedelta admite los siguientes parámetros

 datetime.timedelta(days=0, seconds=0, microseconds=0, milliseconds=0, minutes=0, hours=0, weeks=0)

por lo que podremos construir intervalos de tiempo usando cualquiera de ellos para luego sumarlo o restarlo de un datetime. Vemos ejemplos

>>> from datetime import *
>>> una_semana = timedelta(weeks=1)
>>> ahora = datetime.now()
>>> ahora
datetime.datetime(2022, 7, 26, 20, 7, 20, 55527)
>>> ahora-una_semana
datetime.datetime(2022, 7, 19, 20, 7, 20, 55527)

Hemos restado una semana al datetime actual.

>>> una_hora = timedelta(seconds=3600)
>>> ahora-una_hora
datetime.datetime(2022, 7, 26, 19, 7, 20, 55527)

Ahora hemos restado una hora. Vamos con un date. Al no llevar hora, debemos restar o sumar días completos para ver algo.

>>> hoy=date.today()
>>> un_dia=timedelta(days=1)
>>> hoy-un_dia
datetime.date(2022, 7, 25)

Con timedelta también se pueden hacer operaciones. Por ejemplo, es posible sumar o restar dos timedelta. También es posible multiplicar o dividir un timedelta por un entero o un número con decimales. Por ejemplo

>>> un_dia = timedelta(days=1)
>>> un_dia
datetime.timedelta(days=1)
>>> medio_dia = un_dia/2
>>> medio_dia
datetime.timedelta(seconds=43200)
>>> dos_dias = un_dia*2
>>> dos_dias
datetime.timedelta(days=2)

Medio día son 43200 segundos. Habría que echar la cuenta, pero es así. Usando la función str() obtenemos una representación más amigable.

>>> str(medio_dia)
'12:00:00'
>>> str(dos_dias)
'2 days, 0:00:00'

timezone y módulo pytz[edit]

Hay un tema que no hemos mencionado hasta ahora. Cuando hablamos de una fecha y sobre todo, de una hora, debemos indicar también en que parte del mundo es esa fecha y hora. Sabes que hay husos horarios, que no todos los países tienen la misma hora a la vez y que incluso un mismo país, extenso, puede tener distintas horas en distintas regiones, sin ir más lejos, España y Canarias.

Así que cuando se trabaja con fechas, son importantes dos conceptos:

  • timezone o zona horaria del datetime que estamos indicando.
  • timestamp. Se conoce como timestamp al número de segundos (o milisegundos) transcurridos desde el 1 de Enero de 1970 a las 0 horas en el meridiano de Greenwich (hora GMT).

El timezone vemos ahora detalles. Ampliamos el concepto de timestamp. Los ordenadores, de forma interna, usan este número para saber la fecha/hora actual. Es simplemente un número sin decimales que expresa el número de milisegundos que han pasado desde una fecha/hora concreta en un sitio concreto, por lo que ese número es igual para todos los ordenadores del mundo, independientemente de en qué país estén. Ya es cuestión del sistema operativo o de la aplicación convertir ese número a la fecha/hora concreta del país donde esté el ordenador. Y para esa conversión, le viene bien saber la zona horaria o timezone. Aunque no es exactamente lo mismo, GMT también se conoce como UTC (Tiempo Universal Coordinado) o como hora Zulu.

Ahora sí, vamos con ejemplos.

>>> datetime.now()
datetime.datetime(2022, 7, 26, 20, 33, 56, 528303)
>>> datetime.now(timezone.utc)
datetime.datetime(2022, 7, 26, 18, 34, 3, 789950, tzinfo=datetime.timezone.utc)

El método now() de datetime admite un parámetro que es el timezone. Si no le pasamos nada, da el del sistema operativo, en mi caso, España. Si le pasamos como parámetro timezone.utc nos daría la fecha/hora UTC, es decir, en el meridiano de Greenwich según el sol, sin tener en cuenta horarios de verano/invierno o ajustes de horas europeas para que todos los países de europa tengan la misma hora. En este ejemplo, España, en verano, 2 horas de diferencia. La diferencia de minutos / segundos que ves se deben al tiempo que he tardado en ejecutar un comando y otro.

Fijate que al poner timezone, la clase datetime guarda esta información como tzinfo. Podemos acceder a esta info si es necesario

>>> hoy=datetime.now(timezone.utc)
>>> hoy.tzinfo
datetime.timezone.utc

Pero vamos a probar a ver qué pasa si intentamos acceder a ello si no hemos fijado el timezone a posta.

>>> hoy = datetime.now()
>>> print(hoy.tzinfo)
None

No está relleno. Lo ideal sería que estuviera relleno con la timezone de nuestro ordenador. Para ello, tenemos la función astimezone() que convierte este datetime en un datetime con el timezone relleno a nuestra zona horaria.

>>> hoy = datetime.now().astimezone()
>>> print(hoy.tzinfo)
Hora de verano romance

Esto es lo que sale en mi caso, España, en horario de verano.

¿Cómo obtenemos la hora actual en otros países?. Los módulos estándar de python se quedan un poco escasos en este sentido, ya que solo sabe de UTC/GMT y de la zona local de tu ordenador. Pero hay módulos adicionales que te puedes instalar si los necesitas. Entre ellos, el módulo pytz, abreviatura de "python timezone". La instalación es fácil. Desde una ventana de comandos o terminal de linux ejecuta el comando pip install pytz. pip es un ejecutable que vienen en la instalación de python y que sirve para instalar módulos adicionales.

C:\Users\fjabe>pip install pytz
Collecting pytz
  Downloading pytz-2022.1-py2.py3-none-any.whl (503 kB)
     ---------------------------------------- 503.5/503.5 kB 15.9 MB/s eta 0:00:00
Installing collected packages: pytz
Successfully installed pytz-2022.1

Una vez que tengas esto y ya desde el interprete de comandos de python, puedes ver qué zonas horarias tienes diponibles con el siguiente comando

>>> import pytz
>>> pytz.all_timezones
['Africa/Abidjan', 'Africa/Accra', ... 
...
'US/Pacific', 'US/Samoa', 'UTC', 'Universal', 'W-SU', 'WET', 'Zulu']

Salen un montón. Estos textos son los que te permiten saber la hora actual en cualquier sitio del mundo. Ejemplos

>>> datetime.now(pytz.timezone('America/New_York'))
datetime.datetime(2022, 7, 30, 2, 43, 56, 551152, tzinfo=<DstTzInfo 'America/New_York' EDT-1 day, 20:00:00 DST>)
>>> datetime.now(pytz.timezone('Europe/Madrid'))
datetime.datetime(2022, 7, 30, 8, 44, 2, 924587, tzinfo=<DstTzInfo 'Europe/Madrid' CEST+2:00:00 DST>)
>>> datetime.now(pytz.timezone('CET'))
datetime.datetime(2022, 7, 30, 8, 44, 13, 512663, tzinfo=<DstTzInfo 'CET' CEST+2:00:00 DST>)
>>> datetime.now(pytz.timezone('Zulu'))
datetime.datetime(2022, 7, 30, 6, 44, 18, 40771, tzinfo=<StaticTzInfo 'Zulu'>)

Fijate que la hora de New Yory es las 2:43, en Madrid las 8:44 y la Zulu 6:44. Las diferencias que veas de segundos o incluso minutos se deben al tiempo que he tardado en escribir y ejecutar los comandos.

Si tenemos una fecha/hora en una zona horaria, podemos saber que fecha/hora era en otra zona horaria de la siguiente forma

>>> zulu = datetime(2022,7,4,5,6,7,123456,pytz.timezone('Zulu'))
>>> zulu
datetime.datetime(2022, 7, 4, 5, 6, 7, 123456, tzinfo=<StaticTzInfo 'Zulu'>)
>>> madrid = zulu.astimezone(pytz.timezone('Europe/Madrid'))
>>> madrid
datetime.datetime(2022, 7, 4, 7, 6, 7, 123456, tzinfo=<DstTzInfo 'Europe/Madrid' CEST+2:00:00 DST>)

Hemos creado una hora Zulu y luego, con el método astimezone() pasándole como parámetro la timezon que queremos (Europa/Madrid) obtenemos qué hora sería en Madrid. Resumiendo, el módulo pytz nos da como texto un montón de zonas horarias. Usando pytz.tiemezon('nombre zona horaria') obtenemos una instancia de timezone para esa zona horaria. Y esta instancia podemos usarla en los métodos de datetime que lo admitan para obtener o cambiar horas entre zonas horarias.

Timestamp[edit]

Vemos que todo esto de las horas y las zonas horarias es un poco lio. Por ello, en general, no se suele usar de forma interna. Unicamente para presentar al usuario la hora en su zona horaria local o si las necesidades del usuario lo requieren, en otras zonas horarias. Lo habitual es guardar internamente en las variables o en base de datos el timestamp. Hemos comentado antes que es el número de segundos que han pasado desde el 1 de Enero de 1970 a las 0 horas, 0 minutos y 0 segundos en el meridiano de Greenwich. Este timestamp es por tanto solo un número. Veamos un ejemplo

>>> now = datetime.now()
>>> now.timestamp()
1659164736.831705

Han pasado 1659164736.831705 segundos desde que era el 1 de Enero de 1970 a las 00:00:00 en el meridiano de Greenwich. Este número es igual para todo el mundo, da igual dónde estés. Por ello, este número se puede guardar internamente en nuestro programa en vez de una fecha hora de un huso horario concreto. Para presentárselo al usuario solo necesitamos convertirlo otra vez a datetime

>>> timestamp = now.timestamp()
>>> datetime.fromtimestamp(timestamp)
datetime.datetime(2022, 7, 30, 9, 5, 36, 831705)
>>> datetime.fromtimestamp(timestamp,pytz.timezone('Zulu'))
datetime.datetime(2022, 7, 30, 7, 5, 36, 831705, tzinfo=<StaticTzInfo 'Zulu'>)
>>> datetime.fromtimestamp(timestamp,pytz.timezone('Europe/Madrid'))
datetime.datetime(2022, 7, 30, 9, 5, 36, 831705, tzinfo=<DstTzInfo 'Europe/Madrid' CEST+2:00:00 DST>)
>>> datetime.fromtimestamp(timestamp,pytz.timezone('America/New_York'))
datetime.datetime(2022, 7, 30, 3, 5, 36, 831705, tzinfo=<DstTzInfo 'America/New_York' EDT-1 day, 20:00:00 DST>)

A partir del timestamp hemos obtenido las fechas/horas local (sin pasar timezone), en mi caso Madrid, la hora Zulu, la de New York y la de Madrid otra vez, pero esta vez pasando el timezone.

Si no necesitas saber las horas de otros husos horarios, sino que te vale con el local de tu país, o del país donde se ejecute tu programa, no necesitas todo esto. El módulo por defecto datetime tiene todo lo necesario. Aunque no lo necesites, puede ser buena idea guaradar internamente los timestamp en vez de los datetime. Estrictamente no lo necesitas, pero no sabes si en algún momento tu programa puede requerirlo y no cuesta mucho. Y finalmente, si necesitas convertir horas de un huso horario a otro, instalar el módulo pytz es de mucha ayuda si quieres usar nombres de husos horarios en vez de andar con incrementos de horas.

Anterior: 09 - Curso de Python - Clases en Python -- Índice: Curso de Python -- Siguiente: 11 - Curso de Python - Bases de datos.