Learn Python Series (#21) - Handling Dates and Time Part 1

in #utopian-io6 years ago (edited)

Learn Python Series (#21) - Handling Dates and Time - Part 1

python_logo.png

Full additional iPython tutorial sample code file included here:

https://github.com/realScipio/learn-python-series/blob/master/datetime-01.ipynb

What Will I Learn?

  • You will learn about the existence and conceptual use cases of the built-in Python modules time, calendar and datetime, and about an external package called maya that also conveniently handles dates and times. The first two mentioned modules are in-depth discussed in this tutorial episode;
  • Regarding the time module, the most important methods will be discussed;
  • on top of that I will explain the constructs of full-9-time tuples and time_struct objects, as well as about time formatting strings directives;
  • we will then discuss a complete back-and-forth time object vs time string formatting / parsing example, with which you'll probably be able to do just about anything you want to regarding the handling of dates and times with respect to UNIX timestamps (epoch-related), time_struct objects and time strings;
  • finally we'll briefly touch upon a few convenient calendar module methods that provide some additional value.

Requirements

  • A working modern computer running macOS, Windows or Ubuntu;
  • An installed Python 3(.6) distribution, such as (for example) the Anaconda Distribution;
  • The ambition to learn Python programming;

Difficulty

Intermediate

Curriculum (of the Learn Python Series):

Learn Python Series (#21) - Handling Dates and Time Part 1

I think it's about time ;-) to discuss with you some fundamentals regarding the handling of different date & time formats and functionality. There are a lot of programming situations in which dates and times play an important role; in some of the previous Learn Python Series episodes, where we discussed the historical currency daily openings for example, dates were involved. We only created a (JSON-based) list from old to new from it, and plotted the daily price values, but what if we wanted to know "what was the price 34 days before March 18 2018?" For situations like that, some knowledge regarding how to handle dates and times is needed.

Python does not include default / native data types for dates and times (as it does for a string and a list for example), but we can make use of the functionality provided in the time, datetime and calendar modules.

  • The datetime module includes functionality for handling dates, times, and combinations, and you can perform some arithmetic and comparison with it. It can also do some basic conversion between datetime objects and formatted strings.
  • The time module focuses on time-related functions, but handles dates as well to a certain extent, and it includes some formatting functionality for switching between time_structs / 9-tuples and strings.
  • The calendar module can be used to format representations of days, weeks, months and years, and you can for example compute the day of the week on any date.

And then there's also for example the excellent external Maya package available to work with...

Lots to learn! So let's begin!

The time module

time.time()

Probably the most used time method is time.time(): it returns the number of seconds passed between "now" (the exact moment you're calling time.time()) and the epoch, being Thursday, January 1st, 1970, 00:00:00 UTC, on UNIX-like systems.

import time
localtime = time.time()
print(type(localtime), localtime)
<class 'float'> 1525031396.1437771

Nota bene: oftentimes, when calling time.time() you don't want the decimal digits but rounded seconds since the epoch. In order to do so, just convert the default float time.time() returns to an integer, like so:

localtime = int(time.time())
print(type(localtime), localtime)
<class 'int'> 1525031430

time.sleep()

This pauses (suspends) the thread running for the number of seconds passed to it as its argument.

start = time.time()
time.sleep(5)
end = time.time()
print(end-start)
5.0028228759765625

PS: the time passed in between start and end is oftentimes called the "wall time".

time.localtime()

In case you're interested in returning a time-tuple or a time.struct_time object returned, which both are like a wrapper holding the information about some point in time, use time.localtime() and pass in the amount of ticks (seconds) passed since the epoch, or don't use an argument to get the current time.

some_other_time = time.localtime(123)
print(some_other_time)

current_time = time.localtime()
print(current_time)
time.struct_time(tm_year=1970, tm_mon=1, tm_mday=1, tm_hour=1, tm_min=2, tm_sec=3, tm_wday=3, tm_yday=1, tm_isdst=0)
time.struct_time(tm_year=2018, tm_mon=5, tm_mday=1, tm_hour=19, tm_min=11, tm_sec=47, tm_wday=1, tm_yday=121, tm_isdst=1)

If you're wondering how to use such a time.struct_time object: you're able to format it to your liking into a string via time.strftime() (see below).

time.mktime()

The time.mktime() is the inverse of what time.localtime() does. As an argument, just pass in the time.struct_time object (or full 9-tuple).

time.mktime() will then return a floating point number, indicating the seconds since the epoch (as is the case with time.time()).

Full 9-time-tuple:

Index number: Meaning

  • 0: 4-digit year notation (e.g. 2018)
  • 1: Month (e.g. 2 for February)
  • 2: Day (e.g. 31 for the last day of December)
  • 3: Hour (e.g. 23 for 11 PM)
  • 4: Minute (e.g. 59 for the last minute of the hour)
  • 5: Second (can even be 60 or 61 for leap seconds!)
  • 6: Day of the Week (e.g. 0 for a Monday, 6 for a Sunday)
  • 7: Day of the Year (e.g. 1 for Jan., 1st)
  • 8: Daylight savings (-1, 0, 1, where -1 means the system library decides)

Struct_time:

Works exactly the same! :-)

PS: when passing in a 9-tuple as the argument to time.mktime() to get the epoch equivalent, you don't need to worry about which day of the week or which day of the year that specific date is, you can pass in 0 for indexes 6 and 7 without a problem. However, for the last (8th) index, just pass in -1 to let the system decide whether or not to compensate for daylight savings.

some_moment = (2018, 5, 1, 21, 20, 0, 0, 0, -1)
epoch = time.mktime(some_moment)
print(epoch)

some_moment_string = time.ctime(epoch)
print(some_moment_string)
1525202400.0
Tue May  1 21:20:00 2018

time.strftime()

time.strftime() returns a string, you can format precisely using a format string with directives and helper string components (if you want to), and by passing in a time-tuple (a time.struct_time object) as a second argument (being either the current time / no argument, or any time in the past or future).

localtime = time.localtime()

# Two examples regarding time string formatting
print(time.strftime('Today (%Y-%m-%d) it\'s a %A', localtime))
print(time.strftime('Right now: %A, %d %b %Y (%H:%M:%S)', localtime))
Today (2018-05-01) it's a Tuesday
Right now: Tuesday, 01 May 2018 (20:25:04)

PS:
The following (common) directives, and their meaning, are used a lot with time string formatting:

  • %a: abbreviated weekday name
  • %A: full weekday name
  • %b: abbreviated month name
  • %B: full month name
  • %c: date/time representation of the current locale (e.g. Sun Apr 29 22:15:37 2018)
  • %d: day of the month (01 .. 31)
  • %H: 24-hour clock hour (00 ..23)
  • %j: day of the year (001 .. 366)
  • %m: month (01 ..12)
  • %M: minute (00 ..59)
  • %S: seconds
  • %w: weekday (0 .. 6)
  • %W: week number (00 .. 53)
  • %Y: 4-digit year
  • %Z: Timezone name

time.strptime()

Works the other way around as time.strftime() does: time.strptime() parses a time-string (as first argument) which represents a certain date/time, and a format string following a specific format as a parser helper. It returns a time.struct_time object, and works with the same directives as mentioned right above regarding time.strftime().

localtime = time.strptime("14 Feb 2015", "%d %b %Y")
print(localtime)
time.struct_time(tm_year=2015, tm_mon=2, tm_mday=14, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=5, tm_yday=45, tm_isdst=-1)

time.asctime()

Returns a (current locale-based) date/time string representation of the time-tuple (or blank) passed to it as its argument.

localtime = time.asctime()
print(localtime)
Tue May  1 20:28:04 2018

time.ctime()

About the same as time.asctime() but possible to pass in nothing as an argument ("now"), or seconds since the UNIX epoch:

localtime = time.ctime()
print(localtime)
Tue May  1 20:28:15 2018
localtime = time.ctime(123)
print(localtime)
Thu Jan  1 01:02:03 1970

Let's wrap things up with a nice back-and-forth conversion example combining it all!

Say, you are working with some date/time string in a specific format. And you just want to use the time module, add some time to it, and present the new time string in the same format.

Then this is how to go about that:

# Some moment in time
x_date_string = "14 Feb 2015 09:15:00"
x_time_struct = time.strptime(x_date_string, "%d %b %Y %H:%M:%S")
x_epoch = time.mktime(x_time_struct)

# 15 minutes later
y_epoch = x_epoch + 900
y_time_struct = time.localtime(y_epoch)
y_date_string = time.strftime("%d %b %Y %H:%M:%S", y_time_struct)

print(x_date_string)
print(y_date_string)
14 Feb 2015 09:15:00
14 Feb 2015 09:30:00

The calendar module

Although I don't use the calendar module too much, personally, I still wanted to (briefly) discuss some basic features it provides. I deliberately picked a few methods that add some additional value on top of what we've discussed above regarding functionality of the time module.

calendar.calendar()

With this method, you're able to conveniently print an entire year's calendar.

import calendar
my_cal = calendar.calendar(2018)
print(my_cal)
                                  2018

      January                   February                   March
Fr Sa Su Mo Tu We Th      Fr Sa Su Mo Tu We Th      Fr Sa Su Mo Tu We Th
          1  2  3  4                         1                         1
 5  6  7  8  9 10 11       2  3  4  5  6  7  8       2  3  4  5  6  7  8
12 13 14 15 16 17 18       9 10 11 12 13 14 15       9 10 11 12 13 14 15
19 20 21 22 23 24 25      16 17 18 19 20 21 22      16 17 18 19 20 21 22
26 27 28 29 30 31         23 24 25 26 27 28         23 24 25 26 27 28 29
                                                    30 31

       April                      May                       June
Fr Sa Su Mo Tu We Th      Fr Sa Su Mo Tu We Th      Fr Sa Su Mo Tu We Th
       1  2  3  4  5                   1  2  3       1  2  3  4  5  6  7
 6  7  8  9 10 11 12       4  5  6  7  8  9 10       8  9 10 11 12 13 14
13 14 15 16 17 18 19      11 12 13 14 15 16 17      15 16 17 18 19 20 21
20 21 22 23 24 25 26      18 19 20 21 22 23 24      22 23 24 25 26 27 28
27 28 29 30               25 26 27 28 29 30 31      29 30

        July                     August                  September
Fr Sa Su Mo Tu We Th      Fr Sa Su Mo Tu We Th      Fr Sa Su Mo Tu We Th
       1  2  3  4  5                      1  2          1  2  3  4  5  6
 6  7  8  9 10 11 12       3  4  5  6  7  8  9       7  8  9 10 11 12 13
13 14 15 16 17 18 19      10 11 12 13 14 15 16      14 15 16 17 18 19 20
20 21 22 23 24 25 26      17 18 19 20 21 22 23      21 22 23 24 25 26 27
27 28 29 30 31            24 25 26 27 28 29 30      28 29 30
                          31

      October                   November                  December
Fr Sa Su Mo Tu We Th      Fr Sa Su Mo Tu We Th      Fr Sa Su Mo Tu We Th
          1  2  3  4                         1          1  2  3  4  5  6
 5  6  7  8  9 10 11       2  3  4  5  6  7  8       7  8  9 10 11 12 13
12 13 14 15 16 17 18       9 10 11 12 13 14 15      14 15 16 17 18 19 20
19 20 21 22 23 24 25      16 17 18 19 20 21 22      21 22 23 24 25 26 27
26 27 28 29 30 31         23 24 25 26 27 28 29      28 29 30 31
                          30

calendar.month()

With calendar.month() you can pass in both a year and month number as arguments, and print just that single month calender, like so:

import calendar

apr_2018 = calendar.month(2018, 4)
print(apr_2018)
     April 2018
Mo Tu We Th Fr Sa Su
                   1
 2  3  4  5  6  7  8
 9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30

calendar.monthrange()

The calendar.monthrange() method returns a tuple of two integers, where the first represents the "weekday code" for the first day of that month (monday: 0, sunday: 6), and the second returns the amount of days in that month. This can be convenient to serve as input for other functions in your code.

calendar.monthrange(2018, 4)
(6, 30)

calendar.weekday()

The calendar.weekday() method simply returns the weekday number for a given date.

calendar.weekday(2018,4,28)
5

calendar.isleap()

The calendar.isleap() method returns True or False whether the year as its argument is a leap year or not.

calendar.isleap(2018)
False

calendar.leapdays()

And as a final method (for this tutorial episode) the calendar.leapdays() method returns the number of leapdays occurring within in interval of two years.

calendar.leapdays(1978,2018)
10

What did we learn, hopefully?

In this episode, we first discussed the existance of several date/time modules within Python (time, datetime, and calendar) and we then focused on the time module specifically, going over the various built-in methods in the time module and about how to use them, providing small code examples for each method discussed.

We also discussed and listed time string formatting directives, and the index numbers and meanings of full-9-time tuples and time_struct objects, which are all needed of course to convert between time objects and strings, while in the mean time being able to do some calculations with them (using seconds as the default unit because of the UNIX timestamps nature, which are seconds as well). We then wrapped things up, considering the time module with a real-life back-and-forth conversion example; tinkering around with the format strings will probably give you all the tools you need to work with, and so some time-offsetting coding on, any kind of date/time format string.

In the next Learn Python Series episodes, we'll continue with the datetime module, and the external Maya package!

Thank you for your time!

Sort:  

import calendar

my_cal = calendar.calendar(2018)

print(my_cal)

That would have save me a lot of times when I was asked to design calendars!! xD

Wow! I didn't think about that! That's a very clever remark of yours, as to why print(calendar.calendar(2018))is useful to designers, saving them boatloads of time.

Coooool! :-) Your comment made my day!

programmers and designers are meant to work together, I think =)

It's destiny! ;-)

Thank you for the beautiful work !


Need help? Write a ticket on https://support.utopian.io.
Chat with us on Discord.

[utopian-moderator]

Thank you too! :-)

Reply to this comment and I will auto upvote and resteem your post to my 36,000+ followers. @a-0-0

We get very less articles about Python, so thank you for taking such a nice Decision for us.

Nice post @scipio! :) You should also mention datetime.timedelta (probably you are goin do so in the next post) :) it's really useful to make arithmetic operations with dates

Great informative.We do not know python resources very well.Thank you for giving us such news.Great work done.Keep it up,God bless you.

Awesome post!! Keep it up and check out THIS POST as well as I have something similar.

Awesome post!! Keep it up and check out THIS POST as well as I have something similar.

Hey @scipio

We're already looking forward to your next contribution!

Utopian Witness!

Vote for Utopian Witness! We are made of developers, system administrators, entrepreneurs, artists, content creators, thinkers. We embrace every nationality, mindset and belief.

Want to chat? Join us on Discord https://discord.gg/h52nFrV

Coin Marketplace

STEEM 0.16
TRX 0.13
JST 0.027
BTC 60696.91
ETH 2593.10
USDT 1.00
SBD 2.56