munotes®

Practical 10: Comparing Two Dates in DD/MM/YYYY Form

Chapter Nineteen

Syllabus topic Module 1, practical 10(a), "Write a program that compares two dates (in DD/MM/YYYY format) and prints which one is earlier"

Pages 113 to 118 of 297

Aim

To compare two dates given in DD/MM/YYYY form and print which one is earlier.

The wrong answer that looks right

first = "05/10/2026"
second = "12/03/2020"

print(f"{first} < {second} as strings? {first < second}")
print("so the string comparison says the earlier date is:",
      first if first < second else second)
print("but 2020 is plainly before 2026")
05/10/2026 < 12/03/2020 as strings? True
so the string comparison says the earlier date is: 05/10/2026
but 2020 is plainly before 2026

The string comparison is not broken; it is answering a different question. Python compares strings character by character, so it looks at "0" against "1" first, decides that "05..." sorts before "12..." and stops. It compared the days, because in DD/MM/YYYY the day comes first.

A DD/MM/YYYY string cannot be compared as a string. In YYYY-MM-DD it can, and that is exactly why that format exists, but MU's row specifies DD/MM/YYYY, so the string has to be turned into a date first.

strptime, which parses a string into a date

from datetime import datetime

parsed = datetime.strptime("05/10/2026", "%d/%m/%Y")

print("what came back    :", parsed)
print("its type          :", type(parsed).__name__)
print("just the date     :", parsed.date())
print("day, month, year  :", parsed.day, parsed.month, parsed.year)
what came back    : 2026-10-05 00:00:00
its type          : datetime
just the date     : 2026-10-05
day, month, year  : 5 10 2026

The second argument is the format string, and it has to describe the input exactly.

CodeMeansFor 05/10/2026
%dday, two digits05
%mmonth, two digits10
%Yyear, four digits2026
%yyear, two digitswould be 26
%Bmonth by nameOctober
%bmonth abbreviatedOct
%Aweekday by name
%H:%M:%Shours, minutes, seconds

%Y is four digits and %y is two. Getting them the wrong way round is a common error and the message it gives is not obvious, so it is worth remembering.

strptime returns a datetime, which carries a time as well. For a date with no time, .date() gives a plain date, which is cleaner to compare and to print.

The program MU asks for

from datetime import datetime


def parse(text):
    """A date from a DD/MM/YYYY string."""
    return datetime.strptime(text.strip(), "%d/%m/%Y").date()


first_text = input("Enter the first date  (DD/MM/YYYY): ")
second_text = input("Enter the second date (DD/MM/YYYY): ")

first = parse(first_text)
second = parse(second_text)

print(f"first  : {first_text} parsed as {first}")
print(f"second : {second_text} parsed as {second}")

if first < second:
    print(f"{first_text} is EARLIER than {second_text}")
elif second < first:
    print(f"{second_text} is EARLIER than {first_text}")
else:
    print("the two dates are the same")

print(f"the difference is {abs((first - second).days)} day(s)")
05/10/2026
12/03/2020
Enter the first date  (DD/MM/YYYY): Enter the second date (DD/MM/YYYY): first  : 05/10/2026 parsed as 2026-10-05
second : 12/03/2020 parsed as 2020-03-12
12/03/2020 is EARLIER than 05/10/2026
the difference is 2398 day(s)
munotes.in113

Practical 10: Comparing Two Dates in DD/MM/YYYY Form

Once the strings are dates, < means what you want it to mean, and so do >, ==, <= and >=. A date knows how to compare itself with another date.

Subtracting two dates gives a timedelta

from datetime import datetime, date

first = datetime.strptime("05/10/2026", "%d/%m/%Y").date()
second = datetime.strptime("12/03/2020", "%d/%m/%Y").date()

gap = first - second
print("first - second    :", gap)
print("its type          :", type(gap).__name__)
print("in days           :", gap.days)
print("in weeks          :", gap.days // 7, "weeks and", gap.days % 7, "days")
print("about years       :", round(gap.days / 365.25, 2))
print("the other way     :", (second - first).days, "which is negative")
print("always positive   :", abs((second - first).days))
first - second    : 2398 days, 0:00:00
its type          : timedelta
in days           : 2398
in weeks          : 342 weeks and 4 days
about years       : 6.57
the other way     : -2398 which is negative
always positive   : 2398

A timedelta is a length of time. Subtracting two dates gives one, and .days is the number of whole days in it. Note that subtracting the later from the earlier gives a negative number, so abs(...) is what you want when you only care how far apart they are.

Adding a timedelta to a date gives another date, which is how you answer "what is the date 90 days from now":

from datetime import date, timedelta

start = date(2026, 10, 5)

print("the day           :", start)
print("plus 1 day        :", start + timedelta(days=1))
print("plus 90 days      :", start + timedelta(days=90))
print("minus 30 days     :", start - timedelta(days=30))
print("plus 3 weeks      :", start + timedelta(weeks=3))
print("crossing a year   :", date(2026, 12, 25) + timedelta(days=10))
the day           : 2026-10-05
plus 1 day        : 2026-10-06
plus 90 days      : 2027-01-03
minus 30 days     : 2026-09-05
plus 3 weeks      : 2026-10-26
crossing a year   : 2027-01-04

timedelta takes days, weeks, hours, minutes and seconds. It does not take months or years, and the reason is worth a sentence in the journal: a month has no fixed length, so "one month after the 31st of January" has no single right answer.

What happens when the date is not valid

from datetime import datetime

try:
    print(datetime.strptime("31/02/2026", "%d/%m/%Y"))
except ValueError as error:
    print("refused, and the kind of error is", type(error).__name__)
    print("the wording of the message changed between Python 3.12 and 3.14,")
    print("so read your own; both say the day is out of range for February")
refused, and the kind of error is ValueError
the wording of the message changed between Python 3.12 and 3.14,
so read your own; both say the day is out of range for February

strptime checks the calendar, so it refuses the 31st of February. It also refuses a string that does not match the format at all:

munotes.in114

Practical 10: Comparing Two Dates in DD/MM/YYYY Form

from datetime import datetime

print(datetime.strptime("2026-10-05", "%d/%m/%Y"))
ValueError: time data '2026-10-05' does not match format '%d/%m/%Y'

The complete answer catches both:

from datetime import datetime


def parse(text):
    """A date from a DD/MM/YYYY string, or None when it cannot be read."""
    try:
        return datetime.strptime(text.strip(), "%d/%m/%Y").date()
    except ValueError:
        return None


for text in ["05/10/2026", "5/10/2026", "29/02/2024", "29/02/2023",
             "31/02/2026", "2026-10-05", "hello"]:
    answer = parse(text)
    if answer is None:
        print(f"  {text!r:<14} REFUSED")
    else:
        print(f"  {text!r:<14} -> {answer}")
  '05/10/2026'   -> 2026-10-05
  '5/10/2026'    -> 2026-10-05
  '29/02/2024'   -> 2024-02-29
  '29/02/2023'   REFUSED
  '31/02/2026'   REFUSED
  '2026-10-05'   REFUSED
  'hello'        REFUSED

In your own program print the message from the ValueError as well, because it says what was wrong. It is left out here only because its exact wording changed between Python 3.12 and 3.14 and this page has to be true on both.

Three of those lines deserve a note.

"5/10/2026" with one digit is accepted. %d is documented as zero padded, and strptime is lenient about the padding when it parses, though strftime always writes two digits.

"29/02/2024" is accepted and "29/02/2023" is not, because 2024 is a leap year and 2023 is not. That single pair is the best possible test of a date program and it belongs in the journal.

The refusals come from the library, not from our own checking. strptime applies the calendar itself, which is why the program has no leap year test in it at all.

Leap years, since the 29th of February decides this exercise

from calendar import isleap

for year in [2000, 1900, 2023, 2024, 2026, 2100]:
    divisible_by_4 = year % 4 == 0
    divisible_by_100 = year % 100 == 0
    divisible_by_400 = year % 400 == 0
    print(f"{year}  /4 {divisible_by_4!s:<5} /100 {divisible_by_100!s:<5} "
          f"/400 {divisible_by_400!s:<5} leap? {isleap(year)}")
2000  /4 True  /100 True  /400 True  leap? True
1900  /4 True  /100 True  /400 False leap? False
2023  /4 False /100 False /400 False leap? False
2024  /4 True  /100 False /400 False leap? True
2026  /4 False /100 False /400 False leap? False
2100  /4 True  /100 True  /400 False leap? False

The rule in one line. A year is a leap year when it divides by 4, except that a century year must divide by 400. So 2000 is a leap year and 1900 is not, which is the pair that catches a program written with only the divide-by-4 test. calendar.isleap applies the whole rule, so use it rather than writing the test out.

Printing a date the way you want it

from datetime import date

day = date(2026, 10, 5)

print("the default          :", day)
print("%d/%m/%Y             :", day.strftime("%d/%m/%Y"))
print("%d %B %Y             :", day.strftime("%d %B %Y"))
print("%A, %d %b %Y         :", day.strftime("%A, %d %b %Y"))
print("%Y-%m-%d, sortable   :", day.strftime("%Y-%m-%d"))
print("the ISO form         :", day.isoformat())
print("the weekday number   :", day.weekday(), "with Monday as 0")
print("or Monday as 1       :", day.isoweekday())
print("day of the year      :", day.timetuple().tm_yday)
munotes.in115

Practical 10: Comparing Two Dates in DD/MM/YYYY Form

the default          : 2026-10-05
%d/%m/%Y             : 05/10/2026
%d %B %Y             : 05 October 2026
%A, %d %b %Y         : Monday, 05 Oct 2026
%Y-%m-%d, sortable   : 2026-10-05
the ISO form         : 2026-10-05
the weekday number   : 0 with Monday as 0
or Monday as 1       : 1
day of the year      : 278

strptime reads a string into a date and strftime writes a date out as a string. The two names are almost identical and the p and the f are the only difference. P is for parse and f is for format. Getting them the wrong way round is the commonest error with dates in any language.

Sorting a list of DD/MM/YYYY dates

from datetime import datetime

dates = ["05/10/2026", "12/03/2020", "01/01/2026", "31/12/2019", "29/02/2024"]

print("sorted as STRINGS, which is wrong:")
print(" ", sorted(dates))

print("sorted as dates, which is right:")
print(" ", sorted(dates, key=lambda text: datetime.strptime(text, "%d/%m/%Y")))

parsed = [datetime.strptime(text, "%d/%m/%Y").date() for text in dates]
print("earliest :", min(parsed).strftime("%d/%m/%Y"))
print("latest   :", max(parsed).strftime("%d/%m/%Y"))
print("in order :", [d.strftime("%d/%m/%Y") for d in sorted(parsed)])
sorted as STRINGS, which is wrong:
  ['01/01/2026', '05/10/2026', '12/03/2020', '29/02/2024', '31/12/2019']
sorted as dates, which is right:
  ['31/12/2019', '12/03/2020', '29/02/2024', '01/01/2026', '05/10/2026']
earliest : 31/12/2019
latest   : 05/10/2026
in order : ['31/12/2019', '12/03/2020', '29/02/2024', '01/01/2026', '05/10/2026']

Read the two sorted lists against each other. The string sort puts the 1st of January 2026 before the 5th of October 2026 by luck and the 31st of December 2019 after both of them, which is wrong. A key that parses each string fixes it, and it is the same key= argument as in [Practical 7: a Common Member, and a Dictionary Sorted by Value].

Procedure

  1. Save as practical10a.py. First compare the two dates as plain strings and record the wrong

answer, with one sentence saying why.

  1. from datetime import datetime. Parse each string with

datetime.strptime(text, "%d/%m/%Y").date().

  1. Read both dates with input().
  2. Compare with < and print which is earlier, with an elif and an else for equal dates.
  3. Subtract them and print abs(...).days.
  4. Try an invalid date, 31/02/2026, and record the ValueError and its message on your machine.
  5. Try 29/02/2024 and 29/02/2023 and record that one is accepted and one is not.
  6. Print one of the dates back out with strftime("%d/%m/%Y").
  7. Sort a list of five such strings as strings and then with a parsing key, and compare.

Result

Comparing "05/10/2026" with "12/03/2020" as strings said the first was earlier, which is wrong, because the string comparison reaches the day before the year. Parsed with strptime and compared as dates, the program correctly reported 12/03/2020 as the earlier and printed the gap in days. strptime refused 31/02/2026 and 2026-10-05 with ValueError, accepted 29/02/2024 and refused 29/02/2023, confirming that it checks the calendar including the leap year rule. isleap gave True for 2000 and False for 1900, which is the century rule. Sorting the five strings as strings gave a wrong order and sorting with a parsing key gave the right one.

munotes.in116

Practical 10: Comparing Two Dates in DD/MM/YYYY Form

Where marks are lost

  • Comparing the strings. It reaches the day first and is wrong. This is the exercise.
  • Splitting on / and comparing the pieces one at a time without converting them to numbers,

which has the same fault.

  • %y instead of %Y. One is two digits, the other four.
  • Getting the order wrong in the format, "%m/%d/%Y", which quietly reads the 5th of October

as the 10th of May.

  • Confusing strptime with strftime. p parses, f formats.
  • No try around the parse, so one bad input ends the program with a traceback.
  • Not testing the 29th of February. It is the one test that proves the parse checks the

calendar.

  • Using timedelta(months=1), which does not exist.

For the journal

The aim in MU's words, with her DD/MM/YYYY. The string comparison first, with its wrong answer and one sentence saying it compares the day because the day comes first. Then the parse with strptime and the format string explained code by code. The program, the run for two dates you chose, and the number of days between them. Then the three refusals: 31/02/2026, the wrong format, and 29/02/2023 against 29/02/2024 accepted. One sentence on leap years: divisible by 4, except a century year must divide by 400, and calendar.isleap applies the whole rule. The conclusion: a DD/MM/YYYY string cannot be compared as a string, and once parsed into a date the ordinary comparison operators are correct.

Quick revision

  • A DD/MM/YYYY string compares wrongly as a string, because the day comes first. YYYY-MM-DD

compares correctly, which is why that format exists.

  • datetime.strptime(text, "%d/%m/%Y") parses. .date() drops the time.
  • %d day, %m month, %Y four digit year, %y two digit, %B month name, %A weekday

name.

  • strptime parses, strftime formats. p for parse, f for format.
  • Once parsed, <, >, == all work on dates.
  • first - second gives a timedelta; .days is the whole days; it can be negative, so use

abs.

  • timedelta(days=, weeks=, hours=). There is no months or years, because a month has no

fixed length.

  • strptime checks the calendar: 31/02 and 29/02 in a non leap year both raise ValueError.
  • Leap year: divisible by 4, except a century year must be divisible by 400. So 2000 yes, 1900 no.
munotes.in117

Practical 10: Comparing Two Dates in DD/MM/YYYY Form

calendar.isleap knows.

  • weekday() has Monday as 0; isoweekday() has Monday as 1.
  • Sort DD/MM/YYYY strings with key=lambda t: datetime.strptime(t, "%d/%m/%Y").

Questions you should be able to answer

1. Why can two DD/MM/YYYY strings not be compared directly? Because a string comparison goes character by character from the left, and the leftmost part is the day, so it compares the days first and never reaches the years unless the days are equal.

2. Which date format does compare correctly as a string, and why? YYYY-MM-DD, because its parts run from the largest unit to the smallest with fixed widths.

3. Write the line that turns "05/10/2026" into a date. datetime.strptime("05/10/2026", "%d/%m/%Y").date().

4. What is the difference between %Y and %y? %Y is a four digit year and %y a two digit one.

5. What is the difference between strptime and strftime? strptime parses a string into a date; strftime formats a date into a string. The p is for parse and the f for format.

6. What do you get by subtracting two dates? A timedelta, whose .days is the number of whole days. It is negative if you subtract the later from the earlier.

7. Why does timedelta have no months argument? Because a month has no fixed length, so adding one to the 31st of January has no single correct answer.

8. What does strptime("31/02/2026", "%d/%m/%Y") do? It raises ValueError, because it checks the calendar and February has no 31st.

9. State the leap year rule and give the pair that tests it. Divisible by 4, except that a century year must be divisible by 400. 2000 is a leap year and 1900 is not.

10. Sort a list of DD/MM/YYYY strings into date order. sorted(dates, key=lambda text: datetime.strptime(text, "%d/%m/%Y")).

munotes.in118

The rest of this subject

These notes are cut from the University's printed syllabus. Open the syllabus itself for the same subject.

Report or request
Done!