Python Dates: datetime, strftime and Date Arithmetic - Python with AI tutorial chapter 26
Python

Python Dates: datetime, strftime and Date Arithmetic

Python with AI Tutorial · Chapter 26 of 48

Python dates live in the built-in datetime module. It gives you date for a calendar day, datetime for a day plus time, timedelta for a duration, and strftime / strptime to convert between dates and text. Master those four and every due date, ageing report and month-end calculation becomes a few lines.

Dates look simple and are a leading source of business bugs: 03/04/2026 means April 3rd in the US and March 4th in India, months have different lengths, and a server in another timezone can shift an invoice into the wrong day. This chapter shows the standard-library tools that keep all of that under control.

Getting today and now

Import the classes you need from the module. date.today() gives the current day; datetime.now() adds the time down to microseconds. Both come from your computer clock, so the output below will differ when you run it.

from datetime import date, datetime

today = date.today()
now = datetime.now()

print("Today:", today)
print("Now:", now)
print("Year:", today.year, "Month:", today.month, "Day:", today.day)
Output (depends on when you run it):
Today: 2026-09-05
Now: 2026-09-05 10:42:17.318204
Year: 2026 Month: 9 Day: 5

Creating a specific date

Pass year, month, day (and optionally hour, minute, second) to build a fixed date. Every attribute is then available, including the weekday, where Monday is 0 and Sunday is 6.

from datetime import datetime

invoice_date = datetime(2026, 3, 31, 14, 30)

print(invoice_date)
print("Quarter:", (invoice_date.month - 1) // 3 + 1)
print("Weekday number:", invoice_date.weekday())
print("ISO week:", invoice_date.isocalendar().week)
Output:
2026-03-31 14:30:00
Quarter: 1
Weekday number: 1
ISO week: 14

Use date when the time of day does not matter (invoice dates, birthdays, deadlines) and datetime when it does (log entries, meeting slots). Mixing the two in one comparison raises an error, so decide early and convert with .date() when you need to.

Formatting dates with strftime

strftime (string-format-time) turns a date into text using codes that start with %. This is what you use for report headers, file names and anything a human reads.

from datetime import datetime

invoice_date = datetime(2026, 3, 31, 14, 30)

print(invoice_date.strftime("%d-%m-%Y"))
print(invoice_date.strftime("%d %b %Y"))
print(invoice_date.strftime("%A, %d %B %Y"))
print(invoice_date.strftime("%I:%M %p"))
print(invoice_date.strftime("Sales_Report_%Y%m%d.xlsx"))
Output:
31-03-2026
31 Mar 2026
Tuesday, 31 March 2026
02:30 PM
Sales_Report_20260331.xlsx
Code Meaning Example
%Y Four-digit year 2026
%y Two-digit year 26
%m Month number, zero-padded 03
%B / %b Month name, full / short March / Mar
%d Day of month, zero-padded 31
%A / %a Weekday name, full / short Tuesday / Tue
%H / %I Hour, 24-hour / 12-hour 14 / 02
%M / %S Minute / second 30 / 00
%p AM or PM PM
%j Day of year 090
%Z / %z Timezone name / offset IST / +0530

Parsing text into dates with strptime

strptime (string-parse-time) is the reverse: give it the text and the pattern it follows, and you get a real datetime you can sort, compare and add to. Text dates from CSV exports and web forms almost always need this step.

from datetime import datetime

raw = "15/08/2026"
parsed = datetime.strptime(raw, "%d/%m/%Y")

print(parsed)
print(parsed.strftime("%A"))
print(parsed.date())

iso_only = datetime.fromisoformat("2026-03-31T14:30:00")
print(iso_only.hour, iso_only.minute)
Output:
2026-08-15 00:00:00
Saturday
2026-08-15
14 30
Common mistake: the pattern must match the text exactly. Parsing "15/08/2026" with "%m/%d/%Y" raises ValueError: time data ... does not match format because there is no month 15. When a column mixes formats, try several patterns in a loop and log the rows that fail rather than guessing.

Date arithmetic with timedelta

Adding or subtracting a timedelta moves a date forward or back. Subtracting two dates returns a timedelta whose .days attribute is the gap. This is the whole basis of due-date and ageing logic.

from datetime import date, timedelta

invoice_date = date(2026, 3, 31)
due_date = invoice_date + timedelta(days=30)
paid_on = date(2026, 5, 12)

print("Due:", due_date)
print("Days late:", (paid_on - due_date).days)
print("Reminder date:", due_date - timedelta(weeks=1))
Output:
Due: 2026-04-30
Days late: 12
Reminder date: 2026-04-23

timedelta accepts days, seconds, minutes, hours and weeks, but not months or years, because those are not fixed lengths. For “same day next month” logic use the calendar module below or the third-party python-dateutil package.

Service length and month lengths

Two everyday HR and finance needs: how long has someone been employed, and how many days are in a given month. The calendar module answers the second one and also tells you the weekday the month starts on.

from datetime import date
import calendar

joined = date(2019, 7, 1)
as_of = date(2026, 9, 5)
service = as_of - joined

print("Days of service:", service.days)
print("Completed years:", service.days // 365)

first_weekday, days_in_month = calendar.monthrange(2026, 2)
print("Feb 2026 has", days_in_month, "days")
print("Month end:", date(2026, 2, days_in_month))
print("Leap year?", calendar.isleap(2026))
Output:
Days of service: 2623
Completed years: 7
Feb 2026 has 28 days
Month end: 2026-02-28
Leap year? False

Sorting and filtering a list of date strings

Text dates sort alphabetically, which is wrong for almost every format except ISO. Parse first, then sort or filter by the real date value.

from datetime import datetime, date

invoices = [
    ("INV-104", "05/09/2026"),
    ("INV-098", "28/06/2026"),
    ("INV-101", "12/08/2026"),
]

parsed = [(no, datetime.strptime(d, "%d/%m/%Y").date()) for no, d in invoices]
parsed.sort(key=lambda item: item[1])

for no, d in parsed:
    print(no, d.strftime("%d %b %Y"))

q3 = [no for no, d in parsed if date(2026, 7, 1) <= d <= date(2026, 9, 30)]
print("Q3 invoices:", q3)
Output:
INV-098 28 Jun 2026
INV-101 12 Aug 2026
INV-104 05 Sep 2026
Q3 invoices: ['INV-101', 'INV-104']

The same pattern works for any report filter: parse once into real dates, keep the parsed value next to the original record, and compare against date boundaries. It is faster and far less error-prone than slicing strings to pull out a month number.

Timezones with zoneinfo

A plain datetime is naive: it carries no timezone, so Python cannot tell whether 14:30 is Mumbai time or New York time. Attach a zone with ZoneInfo to make it aware, then convert with astimezone. Daylight saving is handled for you.

from datetime import datetime, timezone
from zoneinfo import ZoneInfo

meeting = datetime(2026, 3, 31, 14, 30, tzinfo=ZoneInfo("Asia/Kolkata"))

print(meeting)
print(meeting.astimezone(ZoneInfo("America/New_York")))
print(meeting.astimezone(timezone.utc).strftime("%H:%M %Z"))
Output:
2026-03-31 14:30:00+05:30
2026-03-31 05:00:00-04:00
09:00 UTC

On Windows the zone database is not always present. If you see ZoneInfoNotFoundError, run python -m pip install tzdata once; Mac and Linux ship the database with the operating system.

Try it with AI

Ask for a robust parser when your export mixes several date formats.

Write a Python function parse_any_date(text) that tries these formats in order: "%d/%m/%Y", "%d-%m-%Y", "%Y-%m-%d", "%d %b %Y" and "%B %d, %Y". Return a date object, or None if nothing matches, and never raise. Then show it running on this list: ["31/03/2026", "2026-04-15", "15 Aug 2026", "April 3, 2026", "bad value"].
Try it with AI

Turn a business rule about ageing buckets into working code.

I have a list of dicts with keys invoice_no, invoice_date (string dd/mm/yyyy) and amount. Using only the datetime module, write Python that calculates days outstanding as of today, assigns each invoice to a bucket (Current, 1-30, 31-60, 61-90, 90+) and prints the total amount per bucket in a neat aligned table. Explain the timedelta arithmetic in comments.

Common mistakes

  • Comparing a date with a datetime; call .date() on the datetime first or you get a TypeError.
  • Mixing naive and aware datetimes in one calculation. Pick one convention per project, ideally aware UTC in storage.
  • Assuming %m/%d when the data is %d/%m. Check a date above the 12th to know which order the source uses.
  • Adding timedelta(days=30) and calling it “one month”. Use calendar.monthrange for real month ends.
  • Sorting date strings as text. Parse to date objects before sorting.

Exercise

Given start = date(2026, 1, 1), print the last working day (Monday to Friday) of each month in 2026 in the format Jan 2026: Fri 30 Jan.

Show answer
from datetime import date, timedelta
import calendar

for month in range(1, 13):
    last = date(2026, month, calendar.monthrange(2026, month)[1])
    while last.weekday() > 4:          # 5 = Saturday, 6 = Sunday
        last -= timedelta(days=1)
    print(f"{last.strftime('%b %Y')}: {last.strftime('%a %d %b')}")

# Jan 2026: Fri 30 Jan
# Feb 2026: Fri 27 Feb
# ...

Related chapters

FAQ

What is the difference between strftime and strptime?

strftime formats a date object into text using % codes. strptime parses text back into a datetime using the same codes. Format out, parse in.

How do I add one month to a date in Python?

timedelta has no month unit. Either compute the new month and clamp the day with calendar.monthrange, or install python-dateutil and use relativedelta(months=1).

Why does Python show a timezone error on Windows?

ZoneInfo reads the IANA database, which Windows does not include. Run python -m pip install tzdata once and the error disappears.

Working with spreadsheets too? Ready-made Excel, Google Sheets and Power BI templates are at NextGenTemplates.com.

Chapter 26 of 48 · Python with AI: all 48 chapters

PK
Meet PK, the founder of NeotechNavigators.com! With over 15 years of experience in Data Visualization, Excel Automation, and dashboard creation. PK is a Microsoft Certified Professional who has a passion for all things in Excel. PK loves to explore new and innovative ways to use Excel and is always eager to share his knowledge with others. With an eye for detail and a commitment to excellence, PK has become a go-to expert in the world of Excel. Whether you're looking to create stunning visualizations or streamline your workflow with automation, PK has the skills and expertise to help you succeed. Join the many satisfied clients who have benefited from PK's services and see how he can take your data analysis skills to the next level!
https://neotechnavigators.com