Biblioteca Online : ¡La Suscripción ENI por 9,90 € el primer mes!, con el código PRIMER9. Pulse aquí
¡Acceso ilimitado 24/7 a todos nuestros libros y vídeos! Descubra la Biblioteca Online ENI. Pulse aquí
  1. Libros
  2. Python 3
  3. Datos temporales y algoritmos aplicados
Extrait - Python 3 Los fundamentos del lenguaje (4ª edición)
Extractos del libro
Python 3 Los fundamentos del lenguaje (4ª edición) Volver a la página de compra del libro

Datos temporales y algoritmos aplicados

Gestionar una fecha del calendario

1. Noción de fecha del calendario

Una fecha es, simplemente, la combinación de un día, un mes y un año. Los tres elementos son obligatorios. No existe la noción de instante, de segundos, de minutos o de horas.

No hay nada más sencillo que gestionar una fecha, basta con crear un objeto datetime.date asignándole el año, el mes y el día.

>>> import datetime  
>>> d=datetime.date(2009, 7, 22)  
>>> d  
datetime.date(2009, 7, 22) 

En caso de existir algún error en los parámetros, se genera una excepción muy clara:

>>> d2=datetime.date(2009, 2, 30)  
Traceback (most recent call last):  
File "<stdin>", line 1, in <module>  
ValueError: day is out of range for month 

Dicho objeto proporciona:

>>> dir(datetime.date)  
['__add__', '__class__', '__delattr__', '__doc__', '__eq__',  
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', 
'__init__', '__le__', '__lt__', '__ne__', '__new__', '__radd__', 
'__reduce__', '__reduce_ex__', '__repr__', '__rsub__',  
'__setattr__', '__sizeof__', '__str__', '__sub__', 
'__subclasshook__', 'ctime', 'day', 'fromordinal', 
'fromtimestamp', 'isocalendar', 'isoformat', 'isoweekday', 'max', 
'min', 'month', 'replace', 'resolution', 'strftime', 'timetuple', 
'today', 'toordinal', 'weekday', 'year'] 

Dicho objeto posee tres propiedades que se pasan al constructor:

>>> d.day  
22  
>>> d.month  
7  
>>> d.year  
2009 

2. Trabajar con una fecha

Es posible recuperar el día de la semana según la norma española (el lunes es 0, el domingo es 6) o según la norma ISO (0 para el domingo hasta 6 para el sábado, según la norma inglesa):

>>> d.weekday()  
2  
>>>...

Gestionar un horario o un momento de la jornada

1. Noción de instante

La noción de instante cubre la noción de tiempo que transcurre en una jornada, en función de las reglas habituales y con una precisión que depende del contexto. Por ejemplo, para saber qué hora es, basta con conocer la hora y el minuto. Cuando se desea una mayor precisión, es posible agregar la noción de segundo y, por último, cuando se desea medir el tiempo con la máxima precisión, es posible descender a la millonésima de segundo.

No es posible obtener una mayor precisión, que viene dada por el hardware (y su cadencia) del sistema. Estas nociones de precisión son visibles en la firma del fabricante y en la representación del objeto:

>>> datetime.time()  
datetime.time(0, 0)  
>>> datetime.time(13, 56)  
datetime.time(13, 56)  
>>> datetime.time(13, 56, 12)  
datetime.time(13, 56, 12)  
>>> datetime.time(13, 56, 12, 54321)  
datetime.time(13, 56, 12, 54321) 

Este objeto es ideal para trabajar sobre momentos de la jornada sin considerar el día al que se aplica. Por ejemplo, para trabajar sobre una agenda, se utilizan objetos datetime.date para las columnas y datetime.time para las filas.

De este modo, es posible comparar horarios y resulta más sencillo gestionar los tramos horarios de una jornada. Esto puede, también, servir para medir los tiempos de ejecución...

Gestionar un instante absoluto

1. Noción de instante absoluto

Se trata de identificar un instante mediante el uso conjunto de una fecha y un instante de la jornada, situando un evento con una exactitud determinada.

Por ejemplo, se sabe exactamente cuándo comenzó el eclipse del 11 de agosto de 1999 (en el Atlántico Norte) y cuándo terminó (en la India):

>>> inicio_eclipse=datetime.datetime(1999, 8, 11, 8, 26, 17, 600000) 
>>> inicio_eclipse 
datetime.datetime(1999, 8, 11, 8, 26, 17, 600000) 
>>> fin_eclipse=datetime.datetime(1999, 8, 11, 13, 40, 8, 500000) 

Una vez se tienen los dos instantes absolutos, resulta muy sencillo recuperar la duración:

>>> fin_eclipse-inicio_eclipse  
datetime.timedelta(0, 18830, 900000) 

2. Relación con las nociones anteriores

El objeto datetime.datetime permite gestionar una fecha, exactamente como el objeto datetime.date.

>>> d=datetime.datetime(2009, 7, 22)  
>>> d  
datetime.datetime(2009, 7, 22, 0, 0) 

Este objeto tiene una precisión mayor:

>>> datetime.date.resolution  
datetime.timedelta(1)  
>>> datetime.datetime.resolution  
datetime.timedelta(0, 0, 1) 

Como se verá más adelante, el primer elemento se corresponde con una granularidad de un día, el segundo con un segundo y el tercero con un microsegundo.

Por otro lado, ambos objetos, datetime.datetime y datetime.date, están relacionados:

>>> type.mro(datetime.datetime) 
[<class 'datetime.datetime'>, <class 'datetime.date'>...

Gestionar una diferencia entre dos fechas o instantes

1. Noción de diferencia y de resolución

Python permite trabajar sobre la diferencia entre dos objetos datetime.date, datetime.time o datetime.datetime respetando su resolución, es decir, el intervalo más pequeño en el que tiene sentido obtener una diferencia (por ejemplo, dos objetos datetime.date con una diferencia de dos horas entre sí serán iguales si la resolución es de un día).

He aquí la firma del constructor:

datetime.timedelta([days [, seconds[, microseconds[, milliseconds[, 
minutes, [hours, [weeks]]]]]]]) 

He aquí lo que devuelve cuando se utiliza cada parámetro de manera unitaria:

>>> datetime.timedelta(1)  
datetime.timedelta(1)  
>>> datetime.timedelta(0, 1)  
datetime.timedelta(0, 1)  
>>> datetime.timedelta(0, 0, 1)  
datetime.timedelta(0, 0, 1)  
>>> datetime.timedelta(0, 0, 0, 1)  
datetime.timedelta(0, 0, 1000)  
>>> datetime.timedelta(0, 0, 0, 0, 1)  
datetime.timedelta(0, 60)  
>>> datetime.timedelta(0, 0, 0, 0, 0, 1)  
datetime.timedelta(0, 3600)  
>>> datetime.timedelta(0, 0, 0, 0, 0, 0, 1)  
datetime.timedelta(7) 

De este modo, la representación, sean cuales sean los parámetros utilizados en el constructor, se basa únicamente en la distinción entre los días, los segundos y los microsegundos. 

Esto significa que dos valores idénticos construidos de manera diferente tienen la misma representación. Además, es posible utilizar parámetros nombrados:

>>> datetime.timedelta(weeks=2) 
datetime.timedelta(14) 
>>> datetime.timedelta(days=14) ...

Especificidades de los husos horarios

Es posible gestionar el huso horario de referencia de manera muy simple:

>>> tz=datetime.timezone.utc 

Los demás se construyen conociendo sus desfases respecto a este huso horario de referencia.

>>> tz=datetime.timezone(datetime.timedelta(hours=6))  
>>> tz.tzname(datetime.datetime.now())  
'UTC+06:00'  
>>> tz.utcoffset(datetime.datetime.now())  
datetime.timedelta(0, 21600)  
>>> 21600/3600  
6.0 

He aquí los límites:

>>> datetime.timezone.min  
datetime.timezone(datetime.timedelta(-1, 60))  
>>> datetime.timezone.min.tzname(datetime.datetime.now())  
'UTC-23:59'  
>>> datetime.timezone.max  
datetime.timezone(datetime.timedelta(0, 86340))  
>>> datetime.timezone.max.tzname(datetime.datetime.now())  
'UTC+23:59' 

Si se quiere personalizar un huso horario, en particular dándole un nombre concreto o un desfase no estándar, hay que utilizar la clase datetime.tzinfo y sobrecargarla, como se muestra a continuación:

>>> class MyOffset(datetime.tzinfo):  
...     """My Offset"""  
...     def __init__(self, offset, name):  
...         self.__offset = datetime.timedelta(minutes=offset)  ...

Problemáticas de bajo nivel

1. Timestamp y struct_time

Python incluye varias formas de gestionar las fechas que tienen como resolución una jornada o un milisegundo. Se han expuesto más arriba.

Python posee, a su vez, una estructura struct_time que se parece a la estructura de C:

Índice

Clave

Mínimo

Máximo

0

tm_year

1900

1

tm_month

1

12

2

tm_day

1

31

3

tm_hour

0

23

4

tm_min

0

60

5

tm_sec

0

60

6

tm_wday

0 (lunes)

6 (domingo)

7

tm_yday

1

366

8

tm_isdst

-1

1

Los años pueden almacenarse con formatos de tres o cuatro cifras. Los valores 69 a 99, incluidos, representan los años 1969 a 1999; los valores 0 a 68, incluidos, representan los años 2000 a 2068, y los valores 100 a 1899, incluidos, están prohibidos, conforme se hace en C. Por el contrario, los meses van de 1 a 12 en Python en lugar de 0 a 11 como en C.

El atributo tm_isdst puede tomar los valores -1, 0 y 1 y permite gestionar el hecho de que la fecha sea local o UTC (DST: daylight saving time).

Existen puentes entre un timestamp y la estructura struct_time:

Función

DST

Origen

Destino

time.gmtime()

UTC

timestamp

struc_time

calendar.timegm()

UTC

struc_time

timestamp

time.localtime()

local

timestamp

struc_time

time.mktime()

local

struc_time

timestamp

Estas problemáticas son de bajo nivel y el módulo time se utiliza con poca frecuencia; datetime responde de manera natural a la mayoría de problemáticas.

2. Medidas de rendimiento

Para medir el tiempo que ha tomado un algoritmo en la realización de una operación existen dos maneras de proceder. O bien se mide el tiempo efectivo entre el inicio del algoritmo y su final, o bien se mide el tiempo que el procesador ha asignado realmente al proceso que ejecuta el algoritmo. De este modo, podemos medir una diferencia entre dos timestamps (número de segundos que han transcurrido desde el 1 de enero de 1970) o una diferencia entre dos valores de tiempo de procesador una vez iniciado el algoritmo. Por ejemplo, en la consola:

>>> time.clock()  
0.54 

El tiempo evoluciona, pero el tiempo de procesador consumido evoluciona poco:

>>> time.clock(), time.time()  
(0.54, 1314188162.305353)  
>>> time.clock(), time.time()  ...

Uso del calendario

1. Presentación del módulo calendar

Python provee un módulo que ofrece todas las funcionalidades necesarias para gestionar un calendario o una fecha en un calendario:

>>> import calendar 
>>> dir(calendar) 
['Calendar', 'EPOCH', 'FRIDAY', 'February', 'HTMLCalendar', 
'IllegalMonthError', 'IllegalWeekdayError', 'January', 
'LocaleHTMLCalendar', 'LocaleTextCalendar', 'MONDAY', 'SATURDAY', 
'SUNDAY', 'THURSDAY', 'TUESDAY', 'TextCalendar', 'WEDNESDAY', 
'_EPOCH_ORD', '__all__', '__builtins__', '__cached__', '__doc__', 
'__file__', '__name__', '__package__', '_colwidth', '_locale', 
'_localized_day', '_localized_month', '_spacing', 'c', 'calendar', 
'datetime', 'day_abbr', 'day_name', 'different_locale', 'error', 
'firstweekday', 'format', 'formatstring', 'isleap', 'leapdays', 
'main', 'mdays', 'month', 'month_abbr', 'month_name', 
'monthcalendar', 'monthrange', 'prcal', 'prmonth', 'prweek', 
'setfirstweekday', 'sys', 'timegm', 'week', 'weekday', 
'weekheader'] 

Veamos, en primer lugar, las constantes. EPOCH devuelve el año de origen del timestamp UNIX. De este modo, un timestamp de 0 se corresponde con el 1 de enero de 1970, a medianoche.

>>> calendar.EPOCH 
1970 

A continuación, tenemos los días de la semana:

>>> calendar.MONDAY 
0  
>>> calendar.TUESDAY 
1  
>>> calendar.WEDNESDAY  
2  
>>> calendar.THURSDAY  
3  
>>> calendar.FRIDAY  
4  
>>> calendar.SATURDAY  
5  
>>> calendar.SUNDAY  
6  

Y el número del mes de enero:

>>> calendar.January 
1 

Son constantes que conviene utilizar en el código, en lugar de utilizar...