The time module provides access to several different types of clocks, each useful for different purposes. The standard system calls like time() report the system “wall clock” time. The monotonic() clock can be used to measure elapsed time in a long-running process because it is guaranteed never to move backwards, even if the system time is changed. For performance testing, perf_counter() provides access to the clock with the highest available resolution to make short time measurements more accurate. The CPU time is available through clock(), and process_time() returns the combined processor time and system tim

Comparing clocks

Implementation details for the clocks varies by platform. Use get_clock_info() to access basic information about the current implementation, including the clock’s resolution.


In [1]:
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())
    )


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

monotonic:
    adjustable    : False
    implementation: clock_gettime(CLOCK_MONOTONIC)
    monotonic     : True
    resolution    : 1e-09
    current       : 28403.77271287

perf_counter:
    adjustable    : False
    implementation: clock_gettime(CLOCK_MONOTONIC)
    monotonic     : True
    resolution    : 1e-09
    current       : 28403.773025282

process_time:
    adjustable    : False
    implementation: clock_gettime(CLOCK_PROCESS_CPUTIME_ID)
    monotonic     : True
    resolution    : 1e-09
    current       : 0.946722253

time:
    adjustable    : True
    implementation: clock_gettime(CLOCK_REALTIME)
    monotonic     : False
    resolution    : 1e-09
    current       : 1508825387.078691

Wall Clock Time

One of the core functions of the time module is time(), which returns the number of seconds since the start of the “epoch” as a floating point value.


In [2]:
import time

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


The time is: 1508825674.2591293

The epoch is the start of measurement for time, which for Unix systems is 0:00 on January 1, 1970. Although the value is always a float, actual precision is platform-dependent.

The float representation is useful when storing or comparing dates, but not as useful for producing human readable representations. For logging or printing time ctime() can be more useful.


In [3]:
import time

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


The time is      : Tue Oct 24 14:16:03 2017
15 secs from now : Tue Oct 24 14:16:18 2017

Monotonic Clocks

Because time() looks at the system clock, and the system clock can be changed by the user or system services for synchronizing clocks across multiple computers, calling time() repeatedly may produce values that go forwards and backwards. This can result in unexpected behavior when trying to measure durations or otherwise use those times for computation. Avoid those situations by using monotonic(), which always returns values that go forward.

Note:

The start point for the monotonic clock is not defined, so return values are only useful for doing calculations with other clock values. In this example the duration of the sleep is measured using monotonic().


In [5]:
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))


start :  28911.54
end   :  28911.64
span  :      0.10

Processor Clock Time

While time() returns a wall clock time, clock() returns processor clock time. The values returned from clock() reflect the actual time used by the program as it runs.


In [7]:
import hashlib
import time

# Data to use to calculate md5 checksums
data = open("README.md", '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()


Tue Oct 24 14:19:25 2017 : 1508825965.774 1.255
Tue Oct 24 14:19:26 2017 : 1508825966.278 1.759
Tue Oct 24 14:19:26 2017 : 1508825966.769 2.251
Tue Oct 24 14:19:27 2017 : 1508825967.254 2.734
Tue Oct 24 14:19:27 2017 : 1508825967.738 3.215

In this example, the formatted ctime() is printed along with the floating point values from time(), and clock() for each iteration through the loop.

Typically, the processor clock does not tick if a program is not doing anything.


In [8]:
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())
    )


Tue Oct 24 14:21:00 2017 - 1508826060.54 - 3.74
Sleeping 3
Tue Oct 24 14:21:03 2017 - 1508826063.54 - 3.75
Sleeping 2
Tue Oct 24 14:21:05 2017 - 1508826065.54 - 3.75
Sleeping 1
Tue Oct 24 14:21:06 2017 - 1508826066.55 - 3.76

In this example, the loop does very little work by going to sleep after each iteration. The time() value increases even while the application is asleep, but the clock() value does not.

Calling sleep() yields control from the current thread and asks it to wait for the system to wake it back up. If a program has only one thread, this effectively blocks the app and it does no work.

Performance Counter

It is important to have a high-resolution monotonic clock for measuring performance. Determining the best clock data source requires platform-specific knowledge, which Python provides in perf_counter().


In [10]:
import hashlib
import time

# Data to use to calculate md5 checksums
data = open("README.md", '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))


Tue Oct 24 16:43:57 2017 : 0.495 0.495
Tue Oct 24 16:43:58 2017 : 0.484 0.980
Tue Oct 24 16:43:58 2017 : 0.485 1.465
Tue Oct 24 16:43:59 2017 : 0.486 1.951
Tue Oct 24 16:43:59 2017 : 0.476 2.426

As with monotonic(), the epoch for perf_counter() is undefined, and the values are meant to be used for comparing and computing values, not as absolute times.

Time Components

Storing times as elapsed seconds is useful in some situations, but there are times when a program needs to have access to the individual fields of a date (year, month, etc.). The time module defines struct_time for holding date and time values with components broken out so they are easy to access. There are several functions that work with struct_time values instead of floats.


In [11]:
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()))


gmtime:
  tm_year : 2017
  tm_mon  : 10
  tm_mday : 24
  tm_hour : 8
  tm_min  : 47
  tm_sec  : 5
  tm_wday : 1
  tm_yday : 297
  tm_isdst: 0

localtime:
  tm_year : 2017
  tm_mon  : 10
  tm_mday : 24
  tm_hour : 16
  tm_min  : 47
  tm_sec  : 5
  tm_wday : 1
  tm_yday : 297
  tm_isdst: 0

mktime: 1508834825.0
  • The gmtime() function returns the current time in UTC.

  • localtime() returns the current time with the current time zone applied.

  • mktime() takes a struct_time and converts it to the floating point representation.

Working with Time Zones

The functions for determining the current time depend on having the time zone set, either by the program or by using a default time zone set for the system. Changing the time zone does not change the actual time, just the way it is represented.

To change the time zone, set the environment variable TZ, then call tzset(). The time zone can be specified with a lot of detail, right down to the start and stop times for daylight savings time. It is usually easier to use the time zone name and let the underlying libraries derive the other information, though.

This example program changes the time zone to a few different values and shows how the changes affect other settings in the time module.


In [12]:
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()


Default :
  TZ    : (not set)
  tzname: ('CST', 'CST')
  Zone  : -28800 (-8.0)
  DST   : 0
  Time  : Tue Oct 24 16:56:53 2017

GMT :
  TZ    : GMT
  tzname: ('GMT', 'GMT')
  Zone  : 0 (0.0)
  DST   : 0
  Time  : Tue Oct 24 08:56:53 2017

Europe/Amsterdam :
  TZ    : Europe/Amsterdam
  tzname: ('CET', 'CEST')
  Zone  : -3600 (-1.0)
  DST   : 1
  Time  : Tue Oct 24 10:56:53 2017

The default time zone on the system used to prepare the examples is US/Eastern. The other zones in the example change the tzname, daylight flag, and timezone offset value.

Parsing and Formatting Times

The two functions strptime() and strftime() convert between struct_time and string representations of time values. There is a long list of formatting instructions available to support input and output in different styles. The complete list is documented in the library documentation for the time module.

This example converts the current time from a string to a struct_time instance and back to a string.


In [15]:
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)
print("tyoe of now:", type(now))

parsed = time.strptime(now)
print('\nParsed:')
show_struct(parsed)
print("tyoe of parsed:", type(parsed))

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


Now: Mon Jan  2 22:17:27 2017
tyoe of now: <class 'str'>

Parsed:
  tm_year : 2017
  tm_mon  : 1
  tm_mday : 2
  tm_hour : 22
  tm_min  : 17
  tm_sec  : 27
  tm_wday : 0
  tm_yday : 2
  tm_isdst: -1
tyoe of parsed: <class 'time.struct_time'>

Formatted: Mon Jan 02 22:17:27 2017

In [ ]: