time — Hora del reloj

Propósito:Funciones para manipular la hora del reloj

El módulo time brinda acceso a varios tipos diferentes de relojes, cada uno útil para diferentes propósitos. Las llamads estándar del sistema como time() informa el tiempo de «reloj de pared» del sistema. El reloj monotonic() se puede usar para medir el tiempo transcurrido en un proceso de larga duración porque se garantiza que nunca se moverá hacia atrás, incluso si se cambia la hora del sistema. Para pruebas de rendimiento, perf_counter() proporciona acceso al reloj con la mayor resolución disponible para hacer mediciones a corto plazo más precisas. El tiempo de CPU está disponible a través de clock() y process_time() devuelve el tiempo del sistema y el tiempo del procesador combinados.

Nota

Las implementaciones exponen las funciones de la biblioteca C para manipular fechas y horas. Dado que están vinculadas a la implementación C subyacente, algunos detalles (como el inicio de la época y valor de fecha máximo admitido) son específicos de la plataforma. Referirete a documentación de la biblioteca para detalles completos.

Comparando relojes

Los detalles de implementación de los relojes varían según la plataforma. Utiliza get_clock_info() para acceder a información básica sobre la implementación actual, incluida la resolución del reloj.

time_get_clock_info.py
import textwrap
import time

available_clocks = [
    ('clock', time.clock),
    ('monotonic', time.monotonic),
    ('perf_counter', time.perf_counter),
    ('process_time', time.process_time),
    ('time', time.time),
]

for clock_name, func in available_clocks:
    print(textwrap.dedent('''\
    {name}:
        adjustable    : {info.adjustable}
        implementation: {info.implementation}
        monotonic     : {info.monotonic}
        resolution    : {info.resolution}
        current       : {current}
    ''').format(
        name=clock_name,
        info=time.get_clock_info(clock_name),
        current=func())
    )

Esta salida para macOS muestra que los relojes monotonic y perf_counter se implementan utilizando la misma llamada al sistema subyacente.

$ python3 time_get_clock_info.py

clock:
    adjustable    : False
    implementation: clock()
    monotonic     : True
    resolution    : 1e-06
    current       : 0.028399

monotonic:
    adjustable    : False
    implementation: mach_absolute_time()
    monotonic     : True
    resolution    : 1e-09
    current       : 172336.002232467

perf_counter:
    adjustable    : False
    implementation: mach_absolute_time()
    monotonic     : True
    resolution    : 1e-09
    current       : 172336.002280763

process_time:
    adjustable    : False
    implementation: getrusage(RUSAGE_SELF)
    monotonic     : True
    resolution    : 1e-06
    current       : 0.028593

time:
    adjustable    : True
    implementation: gettimeofday()
    monotonic     : False
    resolution    : 1e-06
    current       : 1471198232.045526

Tiempo de reloj de pared

Una de las funciones principales del módulo time es time(), que devuelve el número de segundos desde el inicio de la «época» como valor de coma flotante.

time_time.py
import time

print('The time is:', time.time())

La época es el comienzo de la medición por tiempo, que para los sistemas Unix es 0:00 el 1 de enero de 1970. Aunque el valor siempre es un número flotante, la precisión real depende de la plataforma.

$ python3 time_time.py

The time is: 1471198232.091589

La representación de número de coma flotante es útil al almacenar o comparar fechas, pero no es tan útil para producir representaciones legibles por humanos. Para registar o imprimir tiempo ctime() puede ser más útil.

time_ctime.py
import time

print('The time is      :', time.ctime())
later = time.time() + 15
print('15 secs from now :', time.ctime(later))

La segunda invocación de print() en este ejemplo muestra cómo usar ctime() para formatear un valor de tiempo que no sea la hora actual.

$ python3 time_ctime.py

The time is      : Sun Aug 14 14:10:32 2016
15 secs from now : Sun Aug 14 14:10:47 2016

Relojes monotónicos

Porque time() mira el reloj del sistema y éste puede ser cambiado por el usuario o los servicios del sistema para sincronizar relojes en varias computadoras, llamar time() repetidamente puede producir valores que van hacia adelante y hacia atrás. Esto puede resultar en comportamiento inesperado al tratar de medir duraciones o de otra manera usar esos tiempos para el cálculo. Evita esas situaciones usando monotonic(), que siempre devuelve valores que van hacia adelante.

time_monotonic.py
import time

start = time.monotonic()
time.sleep(0.1)
end = time.monotonic()
print('start : {:>9.2f}'.format(start))
print('end   : {:>9.2f}'.format(end))
print('span  : {:>9.2f}'.format(end - start))

El punto de inicio para el reloj monotónico no está definido, por lo tanto, los valores que devueleve solo son útiles para hacer cálculos con otros valores del reloj. En este ejemplo, la duración del sueño se mide usando monotonic().

$ python3 time_monotonic.py

start : 172336.14
end   : 172336.24
span  :      0.10

Tiempo de reloj del procesador

Mientras time() devuelve un reloj de pared, clock() devuelve el tiempo de reloj del procesador. Los valores devueltos por clock() reflejan el tiempo real utilizado por el programa mientras se ejecuta.

time_clock.py
import hashlib
import time

# Data to use to calculate md5 checksums
data = open(__file__, 'rb').read()

for i in range(5):
    h = hashlib.sha1()
    print(time.ctime(), ': {:0.3f} {:0.3f}'.format(
        time.time(), time.clock()))
    for i in range(300000):
        h.update(data)
    cksum = h.digest()

En este ejemplo, el ctime() formateado se imprime junto con los valores de coma flotante de time() y clock() para cada iteración a través del bucle.

Nota

Si desea ejecutar el ejemplo en su sistema, es posible que deba agregar más ciclos al bucle interno o trabajar con una mayor cantidad de datos para ver realmente una diferencia en los tiempos.

$ python3 time_clock.py

Sun Aug 14 14:10:32 2016 : 1471198232.327 0.033
Sun Aug 14 14:10:32 2016 : 1471198232.705 0.409
Sun Aug 14 14:10:33 2016 : 1471198233.086 0.787
Sun Aug 14 14:10:33 2016 : 1471198233.466 1.166
Sun Aug 14 14:10:33 2016 : 1471198233.842 1.540

Normalmente, el reloj del procesador no marca si un programa no está haciendo nada.

time_clock_sleep.py
import time

template = '{} - {:0.2f} - {:0.2f}'

print(template.format(
    time.ctime(), time.time(), time.clock())
)

for i in range(3, 0, -1):
    print('Sleeping', i)
    time.sleep(i)
    print(template.format(
        time.ctime(), time.time(), time.clock())
    )

En este ejemplo, el ciclo hace muy poco trabajo yendo a dormir después de cada iteración. El valor time() aumenta incluso mientras la aplicación está dormida, pero el valor clock() no.

$ python3 -u time_clock_sleep.py

Sun Aug 14 14:10:34 2016 - 1471198234.28 - 0.03
Sleeping 3
Sun Aug 14 14:10:37 2016 - 1471198237.28 - 0.03
Sleeping 2
Sun Aug 14 14:10:39 2016 - 1471198239.29 - 0.03
Sleeping 1
Sun Aug 14 14:10:40 2016 - 1471198240.29 - 0.03

Llamar a sleep() cede el control del hilo actual y le pide que espere a que el sistema lo vuelva a activar. Si un programa tiene solo un hilo, esto efectivamente bloquea la aplicación y ésta no funciona.

Contador de rendimiento

Es importante tener un reloj monotónico de alta resolución para medir el desempeño. La determinación de la mejor fuente de datos de reloj requiere conocimiento específico de la plataforma, que Python proporciona en perf_counter().

time_perf_counter.py
import hashlib
import time

# Data to use to calculate md5 checksums
data = open(__file__, 'rb').read()

loop_start = time.perf_counter()

for i in range(5):
    iter_start = time.perf_counter()
    h = hashlib.sha1()
    for i in range(300000):
        h.update(data)
    cksum = h.digest()
    now = time.perf_counter()
    loop_elapsed = now - loop_start
    iter_elapsed = now - iter_start
    print(time.ctime(), ': {:0.3f} {:0.3f}'.format(
        iter_elapsed, loop_elapsed))

Al igual que con monotonic(), la época para perf_counter() es indefinida, y los valores están destinados a ser utilizados para comparar y calcular valores, no como tiempos absolutos.

$ python3 time_perf_counter.py

Sun Aug 14 14:10:40 2016 : 0.487 0.487
Sun Aug 14 14:10:41 2016 : 0.485 0.973
Sun Aug 14 14:10:41 2016 : 0.494 1.466
Sun Aug 14 14:10:42 2016 : 0.487 1.953
Sun Aug 14 14:10:42 2016 : 0.480 2.434

Componentes de tiempo

Almacenar tiempos como segundos transcurridos es útil en algunas situaciones, pero hay momentos en que un programa necesita tener acceso al los campos individuales de una fecha (año, mes, etc.). El módulo time define struct_time para mantener valores de fecha y hora con componentes desglosados por lo que son de fácil acceso. Hay varias funciones que funcionan con valores struct_time en lugar de números de coma flotante.

time_struct.py
import time


def show_struct(s):
    print('  tm_year :', s.tm_year)
    print('  tm_mon  :', s.tm_mon)
    print('  tm_mday :', s.tm_mday)
    print('  tm_hour :', s.tm_hour)
    print('  tm_min  :', s.tm_min)
    print('  tm_sec  :', s.tm_sec)
    print('  tm_wday :', s.tm_wday)
    print('  tm_yday :', s.tm_yday)
    print('  tm_isdst:', s.tm_isdst)


print('gmtime:')
show_struct(time.gmtime())
print('\nlocaltime:')
show_struct(time.localtime())
print('\nmktime:', time.mktime(time.localtime()))

La función gmtime() devuelve la hora actual en UTC. localtime() devuelve la hora actual con la actual con zona horaria aplicada. mktime() toma un struct_time y lo convierte en la representación de coma flotante.

$ python3 time_struct.py

gmtime:
  tm_year : 2016
  tm_mon  : 8
  tm_mday : 14
  tm_hour : 18
  tm_min  : 10
  tm_sec  : 42
  tm_wday : 6
  tm_yday : 227
  tm_isdst: 0

localtime:
  tm_year : 2016
  tm_mon  : 8
  tm_mday : 14
  tm_hour : 14
  tm_min  : 10
  tm_sec  : 42
  tm_wday : 6
  tm_yday : 227
  tm_isdst: 1

mktime: 1471198242.0

Trabajando con zonas horarias

Las funciones para determinar la hora actual dependen de tener el huso horario, ya sea por el programa o usando un huso horario predeterminado para el sistema. Cambiar el huso horario no cambia el tiempo actual, sólo cómo está representado.

Para cambiar la zona horaria, configura la variable de entorno TZ, luego llama a tzset(). La zona horaria se puede especificar con una gran cantidad de detalle, hasta el comienzo y fin para el horario de verano. Sin embargo, por lo general es más fácil usar el nombre de la zona horaria y dejar que las bibliotecas subyacentes deriven la otra información.

Este programa de ejemplo cambia la zona horaria a unos pocos valores diferentes y muestra cómo los cambios afectan a otras configuraciones en el módulo de time.

time_timezone.py
import time
import os


def show_zone_info():
    print('  TZ    :', os.environ.get('TZ', '(not set)'))
    print('  tzname:', time.tzname)
    print('  Zone  : {} ({})'.format(
        time.timezone, (time.timezone / 3600)))
    print('  DST   :', time.daylight)
    print('  Time  :', time.ctime())
    print()


print('Default :')
show_zone_info()

ZONES = [
    'GMT',
    'Europe/Amsterdam',
]

for zone in ZONES:
    os.environ['TZ'] = zone
    time.tzset()
    print(zone, ':')
    show_zone_info()

El huso horario pre determinado en el sistema utilizado para preparar los ejemplos es US/Eastern. Las otras zonas en el ejemplo cambian el tzname, el indicador daylight y valor de compensación de zona horaria.

$ python3 time_timezone.py

Default :
  TZ    : (not set)
  tzname: ('EST', 'EDT')
  Zone  : 18000 (5.0)
  DST   : 1
  Time  : Sun Aug 14 14:10:42 2016

GMT :
  TZ    : GMT
  tzname: ('GMT', 'GMT')
  Zone  : 0 (0.0)
  DST   : 0
  Time  : Sun Aug 14 18:10:42 2016

Europe/Amsterdam :
  TZ    : Europe/Amsterdam
  tzname: ('CET', 'CEST')
  Zone  : -3600 (-1.0)
  DST   : 1
  Time  : Sun Aug 14 20:10:42 2016

Analizando y formateando el tiempo

Las dos funciones strptime() y strftime() convierten entre struct_time y las representaciones de cadena de valores de tiempo. Hay una larga lista de instrucciones de formateo disponibles para soportar entrada y salida en diferentes estilos. La lista completa está documentada en la documentación de la biblioteca para el módulo time.

Este ejemplo convierte la hora actual de una cadena a una instancia struct_time y de vuelta a una cadena.

time_strptime.py
import time


def show_struct(s):
    print('  tm_year :', s.tm_year)
    print('  tm_mon  :', s.tm_mon)
    print('  tm_mday :', s.tm_mday)
    print('  tm_hour :', s.tm_hour)
    print('  tm_min  :', s.tm_min)
    print('  tm_sec  :', s.tm_sec)
    print('  tm_wday :', s.tm_wday)
    print('  tm_yday :', s.tm_yday)
    print('  tm_isdst:', s.tm_isdst)


now = time.ctime(1483391847.433716)
print('Now:', now)

parsed = time.strptime(now)
print('\nParsed:')
show_struct(parsed)

print('\nFormatted:',
      time.strftime("%a %b %d %H:%M:%S %Y", parsed))

La cadena de salida no es exactamente como la de entrada, ya que los días del mes tienen el prefijo cero.

$ python3 time_strptime.py

Now: Mon Jan  2 16:17:27 2017

Parsed:
  tm_year : 2017
  tm_mon  : 1
  tm_mday : 2
  tm_hour : 16
  tm_min  : 17
  tm_sec  : 27
  tm_wday : 0
  tm_yday : 2
  tm_isdst: -1

Formatted: Mon Jan 02 16:17:27 2017

Ver también