Chapter 7, Lesson 2 Text

Lesson Two: Advanced Date and Time Management

 

Clocks in different citiesIn the last lesson, we discussed ways to build and print specific date and time values. Python has many other powerful, time-related features, and you will explore some of them in this lesson. We will show you how to combine, subtract and compare times, get the current date and time, adjust for time zones, and even ask the user for typed-in time and date values.

Comparing Dates and Times

Given any two datetime or datetime objects, you can compare them to see which is smaller or larger. In fact, all of the comparison operators (<, >, ==, !=, etc) will work! Just make sure you have the same kind of object(e.g. all date objects) throughout the logical expression.

The example below sets up a pair of time objects and a pair of datetime objects. Each pair is then used in a logical expression with a comparison operator. Study the code carefully; can you predict the output? Try it and see the results for yourself.

Try It Now

import datetime
time1 = datetime.time(12,0,0) # 12:00 AM
time2 = datetime.time(14,0,0) # 2:00 PM
if (time1 < time2):
print(str.format("{} is less than {}",time1,time2))
dt1 = datetime.datetime(2020, 6, 1) # June 1, 2020
dt2 = datetime.datetime(2019, 7, 1) # July 1, 2019
if (dt1 > dt2 ):
print(str.format("{} is greater than {}",dt1,dt2))

 

Timedelta and Differences between Dates and Times

Your program may want to find out how much time is between two date or datetime objects. You can do this simply by subtracting one object from another with the minus sign (-). The result of the subtraction is a timedelta object, which is part of the datetime module.

import datetime

date1 = datetime.date(2020, 6, 1)   # June 1, 2020
date2 = datetime.date(2019, 7, 1)   # July 1, 2019

diff = date1 - date2                # subtract one date from another to get a timedelta difference
print(diff)

timedelta object contains information about an amount of time that has passed or that is between two date or datetime objects. You can pass a timedelta object directly into a print function, as shown above.

timedelta object stores the amount of time as daysseconds and microseconds. You can read these values out of a timedelta object for special processing. To read one of these values, start with the timedelta variable name, then a dot, and then the property name (e.g. diff.days) as demonstrated below.

import datetime

date1 = datetime.date(2020, 6, 1)   # June 1, 2020
date2 = datetime.date(2019, 7, 1)   # July 1, 2019

diff = date1 - date2                # subtract one date from another to get the difference
print("Days difference = ",diff.days)        # read and use days value from timedelta
print("Seconds difference = ",diff.seconds)  # read and use seconds value from timedelta

Unfortunately, you can't directly subtract time objects - Python does not support this for some reason. If you need to find the difference between two times, you can build two datetime objects instead and just set the year, month and days to the same value in both objects. Then you can subtract the datetime objects to find the time difference.

Try It Now

import datetime
# can't subtract time objects directly,
# so create datetime instead and set year, month, day to same values
time1 = datetime.datetime(1,1,1,12,0,0) # 12:00 AM
time2 = datetime.datetime(1,1,1,14,0,0) # 2:00 PM
diff = time2 - time1
print(diff)
dt1 = datetime.datetime(2020, 6, 1) # June 1, 2020
dt2 = datetime.datetime(2019, 7, 1) # July 1, 2019
diff = dt1 - dt2
print(diff)

Adding to or Subtracting from Dates and Times

Python allows you to create new date or datetime objects by adding or subtracting a timedelta from the original object. A timedelta object can hold any amount of time such as 30 days, 12 hours and 13 seconds.

To build a timedelta object, you will use the datetime.timedelta() function, which has the following possible parameters:

timedelta(days=0, seconds=0, microseconds=0, milliseconds=0, minutes=0, hours=0, weeks=0)

So, if you wanted to create a timedelta for 3 hours, you could do it by passing in all 7 parameter values and just using 0 for the ones you don't want.

myDelta = datetime.timedelta(0,0,0,0,0,3,0)

However, it's easier to take advantage of a Python language feature called named parameters. Instead of passing in simple values that are matched by their order or position, you can pass in a parameter with a name attached! This means we could pass in "hours=3", for example, and that would send the value 3 directly to the "hours" parameter, regardless of where it is within the parameter list.

myDelta = datetime.timedelta(hours=3)

When working with timedelta objects, you will frequently see named parameters used for simplicity.

Great, let's set up a couple of sample date and datetime objects, two different timedelta objects, and see what happens when we add them together. Run the sample code below; does it produce the expected output?

Try It Now

import datetime
date1 = datetime.date(2019,6,1) # June 1, 2019
dt1 = datetime.datetime(2019,6,1,14,30,0) # June 1, 2019, 2:30 PM
delta1 = datetime.timedelta(days=366) # 366 days
delta2 = datetime.timedelta(weeks=2,hours=1) # 2 weeks and 1 hour
print(date1 + delta1) # should be June 1, 2020
print(dt1 + delta2) # should be June 15, 2019, 3:30 PM

Try experimenting with the dates and offsets to observe different results.

Getting the Current Date and Time

Instead of creating a datetime or datetime object with specific values, you may simply want to get the current day or time. This is straightforward for the date and datetime objects, as each has a function you can call to build an object with the current date. Calling datetime.date.today() will build a date object with today's date. Similarly, datetime.datetime.now() will build a datetime object with the current date and time.

import datetime

today = datetime.date.today()           # get current date in a date object
now   = datetime.datetime.now()         # get current date and time in a datetime object

Unfortunately, the time object does not have the same easy kind of function to get the current time. Instead, one way to build a current time object is to start with a current datetime object, and then use the hourminute and second properties of the datetime object to build the time object.

Try running the code below and confirm that you get current time and date values in each case.

Try It Now

import datetime
today = datetime.date.today() # get current date in a date object
now = datetime.datetime.now() # get current date and time in a datetime object
currentTime = datetime.time(now.hour, now.minute, now.second) # build current time object
print(today.strftime("%Y-%m-%d"))
print(now.strftime("%Y-%m-%d %I:%M:%S %p"))
print(currentTime.strftime("%I:%M:%S %p"))

You will almost certainly notice that the time value seems to be off by several hours. Why is that the case? All of the date and time functions will give you current information from the computer on which the functions are running. When you click "Run Code" to try out samples in your web browser, that code is actually running on our centralized servers and the results are sent back to your browser. So, time and date functions run from within our web-based engine will always give you current time from the central server, not your local computer. If you happen to run this same code from a local Python source file and Python interpreter installed on your computer, then you would get local time according to your own computer.

Managing Time Zones

Our server's time zone is set to "UTC", which is an international standard measuring time based in Greenwich, England. However, you can ensure your time functions produce displays in your local time by setting time zone information when the time or datetime object is created.

To do this, you need to know the offset (or difference) between your local time and UTC time. You may know this already, or you can easily look it up online. The links below give you some starting pages with helpful information and time zone conversions.

https://www.timeanddate.com/time/map/

https://www.timetemperature.com/tzus/gmt_united_states.shtml

https://www.timeanddate.com/worldclock/timezone/utc

For example, the United States "Eastern" time zone is -5 hours offset from UTC normally, or -4 hours during daylight savings time. Once you know your local offset from UTC (such as -5), you can adjust your time and datetimeobjects as follows:

import datetime

myOffset   = datetime.timedelta(hours=-5)    # build offset of -5 hours
myTimezone = datetime.timezone(myOffset)     # create local time zone with offset

now         = datetime.datetime.now(myTimezone)    # get current date and time in a datetime object
currentTime = datetime.time(now.hour, now.minute, now.second, 0, myTimezone)  # current time object

First we create a timedelta object, initializing it by calling datetime.timedelta() and setting hours = -5 inside the parentheses. Next, we create a timezone object by calling datetime.timezone() and passing in our timedeltavariable.

That timezone object can then be passed in at the end of the datetime.now() function or the time() constructor. Notice for the time() function you need to specify the number of microseconds before the time zone, so we've added an extra 0 parameter in our example.

Find your own local offset and adjust the myOffset below with the correct number of hours. Then, run the code and confirm times are now printed in your local time zone!

Try It Now

import datetime
myOffset = datetime.timedelta(hours=-5) # build offset of -5 hours
myTimezone = datetime.timezone(myOffset ) # create local time zone with offset
today = datetime.date.today() # get current date in a date object
now = datetime.datetime.now(myTimezone) # get current date and time in a datetime object
currentTime = datetime.time(now.hour, now.minute, now.second, 0, myTimezone) # current time object
print(today.strftime("%Y-%m-%d"))
print(now.strftime("%Y-%m-%d %I:%M:%S %p"))
print(currentTime.strftime("%I:%M:%S %p"))
 

 

Reading Date and Time Input from the User

How might your program prompt a user to enter a date or time value? You could ask them individually for each part (year, month, day, hour, minute, second), store those values in separate variables, and then build an object with those parameters. Or, you could ask them to type in the date or time as a single string like "2019-06-01" or "14:30:00" and then scan or parse that string to understand what the user has entered.

Fortunately, Python contains a flexible function that will parse strings to get date and time information, so you don't have to do any hard work! The datetime object's strptime() function will accept two input parameters: a string to scan and a format string.

datetime.strptime("<string to scan>", <format string>)

The string you want to scan will contain the actual date or time information like "2019-06-01". The format string will tell Python how to parse that string and pull out the right values for year, month, day and so on. The format string uses exactly the same placeholders as the strftime() function discussed in the last lesson.

For example, if you use "%Y-%m-%d" as the format string, Python will expect the user to type in a 4-digit year, a dash, a 2-digit month, another dash and then a 2-digit day. Run the code below to see this powerful tool in action!

Try It Now

import datetime
userDate = input("Please enter a date in the format YYYY-MM-DD: ")
parsedDate = datetime.datetime.strptime(userDate,"%Y-%m-%d")
print("You entered: ",parsedDate )
userTime = input("Please enter a time in the 12-hour format HH:MM:SS [AM|PM]: ")
parsedTime = datetime.datetime.strptime(userTime ,"%I:%M:%S %p")
print("You entered: ",parsedTime)

The user will need to match the expected format exactly, otherwise Python will be unable to parse the string. If the user enters incorrect data, you will get a ValueError exception at run-time. In the example below, the user accidentally enters "2001/01/01" instead of "2001-01-01" as required by the format string. The last line of the exception lets you know the data did not match the expected format.

Please enter a date in the format YYYY-MM-DD: 2001/01/01
Traceback (most recent call last):
  File "code1.py", line 4, in 
    parsedDate = datetime.datetime.strptime(userDate,"%Y-%m-%d")
  File "/usr/lib/python3.5/_strptime.py", line 510, in _strptime_datetime
    tt, fraction = _strptime(data_string, format)
  File "/usr/lib/python3.5/_strptime.py", line 343, in _strptime
    (data_string, format))
ValueError: time data '2001/01/01' does not match format '%Y-%m-%d'

 

The strftime() and strptime() functions have very similar names, so don't confuse them. strftime() has an "f" in the middle, so think "format" - this function is used to format an output message. strptime() has a "p" in the middle, so think "parse" - this function will parse an input string to create a datetime object.

 

Work with Me: Your Birthday and Age

 

In the last lesson, you wrote a small program to initialize a date object with your birthday. The program then displayed a couple of output lines with a formatted date and the day of the week. Let's enhance that program to let the user type in a birthday and scan that string. We'll also get the current date, calculate the difference and display the user's age.

We have re-created most of the code from the last lesson, so you just need to follow the steps below to complete the program.

  1. Replace the ???? below with a call to datetime.datetime.strptime(). Scan the birthdayStringvariable and create a format string to match the YYYY-MM-DD format in the user prompt.
  2. Create a variable called now and initialize it with a call the datetime.datetime.now() function to get the current date and time.
  3. Create a new variable called age and initialize it with a timedelta object by subtracting myBirthdayfrom now.
  4. Create a new variable called years and initialize it by dividing the number of days in the age by 365. Remember, age is a timedelta object, so you can read the number of days by writing age.days.
  5. Call the print() function to display the message "You are <X> years old".
    • <X> should show the number of years, which will be a decimal point number because it was calculated with a division operation.
    • Use str.format() to format the display of a decimal number to just one digit past the decimal point. Hint: "{:.1f}" in the str.format() format string will format the decimal number correctly.

You may want to review Chapter 3, Lesson 3 on str.format() if you need a reminder on how to use this function!

Note that we are using datetime objects instead of date objects, even though we don't care about the hours, minutes and seconds. strptime() will return a datetime object when parsing the input string, and we need to stay consistent when subtracting and comparing date values.

Try It Now

import datetime
# create date object with your birthday
birthdayString = input("Please enter your birthday in the format YYYY-MM-DD: ")
# parse input string into datetime object
myBirthday = ????
# print out your birthday and day of the week
print(myBirthday.strftime("Your birthday is: %Y/%m/%d"))
print(myBirthday.strftime("You were born on a %A"))
# get today's date as a datetime object
# create timedelta object representing age
# calculate age in years and print with 1 decimal digit
  

Console

If your birthday is March 16, 2001, and you happened to be running the program after that date in the year 2018, then the program output would be similar to the example below.

Please enter your birthday in the format YYYY-MM-DD: 2001-03-16
Your birthday is: 2001/03/16
You were born on a Friday
You are 17.6 years old

Last modified: Sunday, 18 August 2019, 9:33 PM