time library in python

# time library in python

The time library is one of the first libraries everyone learns about when learning python, usually for the `time()` or `sleep()` function. In this article, I'll go over those functions and a couple of other functions, native to this library.

For the rest of the article, suppose we imported the `time` library before each line of code:
`import time`

## Getting the date and time

### time() and time_ns()

Let's start with the most used function and its variant: the `time()` and `time_ns()` functions.
The `time()` function returns the seconds since the Unix epoch. This epoch is the Jan 1st 1970 00:00:00 UTC. At the time I write this article, it is the 1st of September 2022, around midday. So if I run the code, I'll get:

`print(time.time())`
Output: `1662028168.0563478`
Given that there are 31536000 seconds in a normal year, we can calculate:

1662028168/31536000 = 52.7 which means that we are 52.7 years after the Unix epoch started, which coincides with 1970.

`time.time_ns()` returns the exact same thing but in nanoseconds instead of seconds, for example:

``````print("time in seconds:", time.time())
print("time in nanoseconds:", time.time_ns())``````

Output:

``````time in seconds: 1662028564.7729084
time in nanoseconds: 1662028564772957118``````

These functions can also be used to measure the duration of a process:

``````t_start = time.time()
i = 0
while i < 100000:
i += 1
t_end = time.time()
print("elapsed time:", t_end-t_start)``````

Output:

``elapsed time: 0.01086282730102539``

However, as we'll see towards the end, there are more suited functions to measure the time performance.

### localtime()

This function takes an argument of time expressed as seconds after the Unix epoch (basically like the `time()` function) and returns a struc_time type object. If there are no arguments, it takes `time.time()` by default (current time).
The struct_time has several pieces of information :

• tm_year: year
• tm_mon: current month (1 is January, 2 is February, etc...)
• tm_mday: day of the month (1 to 28, 29, 30, or 31 depending on the month)
• tm_hour: the hour of the day
• tm_min: minute in the current hour
• tm_sec: second in the current minute
• tm_wday: day of the week (1 to 7)
• tm_yday: day of the year (1 to 365 or 366)

Here are some examples:

``````print("without argument:", time.localtime())
print("with argument :", time.localtime(now))
print("Unix epoch:", time.localtime(0))
print("One year after epoch:", time.localtime(one_year))
print("current year:", time.localtime().tm_year)``````

Output:

``````without argument: time.struct_time(tm_year=2022, tm_mon=9, tm_mday=1, tm_hour=12, tm_min=56, tm_sec=19, tm_wday=3, tm_yday=244, tm_isdst=1)
with argument : time.struct_time(tm_year=2022, tm_mon=9, tm_mday=1, tm_hour=12, tm_min=56, tm_sec=19, tm_wday=3, tm_yday=244, tm_isdst=1)
Unix epoch: time.struct_time(tm_year=1970, tm_mon=1, tm_mday=1, tm_hour=1, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=1, tm_isdst=0)
One year after epoch: time.struct_time(tm_year=1971, tm_mon=1, tm_mday=1, tm_hour=1, tm_min=0, tm_sec=0, tm_wday=4, tm_yday=1, tm_isdst=0)
current year: 2022
``````

### ctime()

This function takes an argument in the `time.time()` format, just like the previous function, but returns a 'readable' date and time format:
W_DAY MONTH DAY HH:MM:SS YEAR
W_DAY is the name of the day and DAY is the number. If the number is only one digit, it is padded with a space. Using the same code as previously, but only changing the function, here are the results:

``````now = time.time()
one_year = 60*60*24*365 #One year in seconds

print("without argument:", time.ctime())
print("with argument :", time.ctime(now))
print("Unix epoch:", time.ctime(0))
print("One year after epoch:", time.ctime(one_year))``````

Output:

``````without argument: Thu Sep  1 13:02:08 2022
with argument : Thu Sep  1 13:02:08 2022
Unix epoch: Thu Jan  1 01:00:00 1970
One year after epoch: Fri Jan  1 01:00:00 1971``````

## Measuring time

### monotonic() and monotonic_ns()

The official description of the monotonic function is: Monotonic clock, cannot go backward. This is because the `time.time()` uses the system clock and can be nonlinear (leap seconds, external calibrations, etc...) furthermore, the system clock can actually go backward if a system clock reset happened between two calls.
This is where the `time.monotonic()` function comes in. This function uses a clock with no reference point (unlike the `time.time()` function which has the Unix epoch as its reference), this means that the only meaningful use of this function is calculating the difference between two different points in time:

``````print("referenced time:", time.time())
print("no reference:", time.monotonic())``````

Output:

``````referenced time: 1662055531.2217774
no reference: 100209.906``````

The referenced time returned by the `time.time()` function has a meaning, while the value returned by the `time.monotonic()` function has no meaning on its own.

For the reasons listed above, however, it is a very good function to use to measure time performance:

``````t_start = time.monotonic()
i = 0
while i<100000:
i+=1
t_end = time.monotonic()
print("elapsed time:", t_end-t_start)``````

Output:

``elapsed time: 0.014999999999417923``

The `time.monotonic_ns()` is the same function but returns values in nanoseconds instead of seconds.

### perf_counter() and perf_counter_ns()

Finally, the real king of the time-measuring functions. `time.perf_counter()` is the to-go function to measure time performances. Why's that? Well, just like `monotonic()`, the clock has no reference so the only meaningful use is the difference between two measures of time. However, it runs on the highest resolution clock available, way higher than `time()` or `monotonic()`. I'll write an article to show this difference in performance but for now, you'll have to take my word for it!
Here is an example of it measuring a very small duration:

``````t_start = time.perf_counter()
i = 0
while i<100:
i+=1
t_end = time.perf_counter()
print("elapsed time:", t_end-t_start)``````

Output:

``elapsed time: 9.400013368576765e-06``

Use `perf_counter_ns()` to get the result in nanoseconds.

### sleep()

Finally, a function a majority of people are familiar with, I'll still go quickly over it.
This function takes an amount of seconds as an argument and suspends the current thread for that amount of seconds (it can take fractional values).
Here is an example:

``````t_start = time.perf_counter()
time.sleep(3)
t_end = time.perf_counter()
print("paused time:", t_end-t_start)``````

Output:

``paused time: 3.0006820000126027``