Skip to content
beginner

Working with Dates and Times in Python

Here is the failure that teaches this lesson faster than any definition. You store a date as a string, then try to subtract it from today:

Published 2026-10-02Updated 2026-10-0411 min read
A charming harvest mouse balancing on a green clover stem with a purple flower in a natural setting.
A charming harvest mouse balancing on a green clover stem with a purple flower in a natural setting. Photo by Adriaan Westra on Pexels.

A date is not text. It is a structured object wearing a text costume.

Here is the failure that teaches this lesson faster than any definition. You store a date as a string, then try to subtract it from today:

today = "2024-03-20"
birthday = "2024-03-15"
print(today - birthday)
TypeError: unsupported operand type(s) for -: 'str' and 'str'

Python refuses. And it is right to refuse. To Python, "2024-03-15" is not a date. It is a sequence of characters — the same kind of value as "hello". You cannot subtract one word from another, and you cannot subtract one date-string from another.

You already know strings and numbers from earlier basics. Dates are a third kind of value: they look like text, but they behave like a number. You can compare them, order them, and do arithmetic on them. The datetime module exists to give dates that structure.

By the end of this tutorial, you will create dates and times, parse them from strings, format them back, measure durations, compare moments, and know when time zones matter.

Why Dates Break as Plain Strings

A string date has no parts. Python cannot tell you the month of "2024-03-15" without you slicing characters and converting them yourself. Worse, it cannot tell you whether one date comes before another.

Try comparing two date strings:

print("2024-03-15" < "2024-03-20")
print("10/03/2024" < "03/10/2024")
True
False

The first line works by accident: ISO-style dates sort correctly as text because the year comes first. The second line is a trap. Is "10/03/2024" October 3rd or March 10th? You know your own convention. Python does not. It compares character by character and gives you an answer that means nothing.

The datetime module fixes this by giving dates real structure. Four classes do most of the work:

  • date — a calendar date: year, month, day.
  • time — a time of day: hour, minute, second, microsecond.
  • datetime — both together, a specific moment.
  • timedelta — a duration, the difference between two moments.

They live in the datetime module, so you import them. The mental model to hold onto: a datetime object stores year, month, day, hour, minute, second, and microsecond as separate fields you can read, compare, and do math with. Text is only ever its costume.

Your First Runnable datetime Example

Let's get real output on screen. Create a file called dates.py:

from datetime import date, datetime

print(date.today())
print(datetime.now())

Run it:

python dates.py
2024-03-15
2024-03-15 14:30:45.123456

Your values will differ — they reflect the moment you ran the script. Notice the shape: date.today() gives you just the day, while datetime.now() gives you the day plus the time down to the microsecond.

Now read the parts. Each field is an attribute you can pull out:

from datetime import datetime

now = datetime.now()
print(now.year)
print(now.month)
print(now.day)
print(now.hour)
print(now.weekday())
2024
3
15
14
4

weekday() returns a number where 0 is Monday and 6 is Sunday. That off-by-one trips up beginners constantly, so file it away now.

You can also build a specific moment on purpose by passing the fields in order:

from datetime import datetime

new_years_eve = datetime(2024, 12, 31, 23, 59, 59)
print(new_years_eve)
2024-12-31 23:59:59

Note: The module is named datetime, and one of its classes is also named datetime. That is why the common import line is from datetime import datetime — you are pulling the class out of the module so you can use it directly.

date, time, and datetime: Which One Do You Need?

The most common beginner confusion is picking the wrong class. The decision rule is simple: match the class to the meaning you actually have.

ClassWhat it holdsUse it whenBeginner mistake
dateYear, month, dayA birthday, a due date, a daily reportUsing it for anything you will compare to a timestamp
timeHour, minute, second, microsecondA daily alarm at 09:00, a store opening timeUsing it when the calendar day matters too
datetimeDate and timeA log entry, an API timestamp, an eventUsing it when only the day matters
timedeltaA duration"30 minutes from now", "3 days between"Confusing it with a point in time

A time value is useful on its own when the calendar date is irrelevant. You build it the same way you build a datetime, just with clock fields only:

from datetime import time

opening = time(9, 0, 0)
closing = time(17, 30, 0)

print(opening)
print(closing)
print(opening < closing)
09:00:00
17:30:00
True

Notice there is no date anywhere in that output. A time answers "what time of day?" and nothing else. If you ever need to know which day that opening belongs to, you have picked the wrong class — reach for datetime instead.

A datetime can be split back into its parts when you need them:

from datetime import datetime

moment = datetime(2024, 3, 15, 14, 30, 45)
print(moment.date())
print(moment.time())
2024-03-15
14:30:45

When not to use date: if you will ever compare the value against a timestamp or convert it across time zones, use a datetime. A date has no time-of-day meaning, so it cannot answer "which came first?" when two events happen on the same day.

Parsing a Date String into a Real Date

A date string points through strptime to a structured datetime object; a return arrow labeled strftime leads back to a date string. The datetime object connects to comparison and duration operations.
Parse text into a datetime before comparing or calculating; use strftime to turn the result back into text.

This is the single most common task you will face: you received a date as text — from a file, a form, or an API — and you need to work with it. The bridge from text to object is strptime.

strptime(string, format) takes the text and a format string, and returns a datetime. The format must match the input character for character. The format codes you need right now:

CodeMeaningExample
%YFour-digit year2024
%mTwo-digit month03
%dTwo-digit day15
%HHour (24-hour)14
%MMinute30
%SSecond45
%BFull month nameMarch
%AFull weekday nameFriday

Parse an ISO-style date:

from datetime import datetime

parsed = datetime.strptime("2024-03-15", "%Y-%m-%d")
print(parsed)
print(type(parsed))
2024-03-15 00:00:00
<class 'datetime.datetime'>

Parse a written date with a month name:

from datetime import datetime

parsed = datetime.strptime("25 December, 2022", "%d %B, %Y")
print(parsed)
2022-12-25 00:00:00

Notice the output is a real datetime object, not a string. That is the whole point — you can now compare it, do arithmetic on it, and format it however you like.

Common mistake: a mismatched format raises ValueError: time data '...' does not match format '...'. When you see it, do not guess. Print the raw string and the format side by side, then fix one character at a time. The error is telling you exactly which position disagrees.

Knowledge check

Check your understanding

Answer this question before you continue.

What does this code print?
Output Prediction

Focus: Predict the datetime value produced when parsing a date-only string.

from datetime import datetime
parsed = datetime.strptime("2024-03-15", "%Y-%m-%d")
print(parsed)

Formatting a Date Back into a String

strftime is the reverse direction: it turns an object into text you control. Same object, many costumes:

from datetime import datetime

moment = datetime(2024, 3, 15, 14, 30, 45)
print(moment.strftime("%Y-%m-%d"))
print(moment.strftime("%d/%m/%Y"))
print(moment.strftime("%B %d, %Y"))
print(moment.strftime("%A, %B %d, %Y"))
2024-03-15
15/03/2024
March 15, 2024
Friday, March 15, 2024

The direction rule to memorize: strptime = string to object, strftime = object to string. One letter flips the direction.

When you need a machine-friendly, sortable string, use isoformat():

from datetime import datetime

moment = datetime(2024, 3, 15, 14, 30, 45)
print(moment.isoformat())
2024-03-15T14:30:45

ISO format sorts correctly as plain text, which makes it ideal for filenames. A file named report_2024-03-15.csv will sort in chronological order in any file browser — a small habit that saves real time when you have hundreds of dated files.

Knowledge check

Check your understanding

Answer this question before you continue.

You already have a `datetime` object and want a display string such as `15/03/2024`. Which operation matches that direction?
Misconception Check

Focus: Distinguish the direction of conversion performed by `strftime` and `strptime`.

Calculating Durations with timedelta

This is the payoff. Subtracting two datetime objects gives you a timedelta — a duration:

from datetime import datetime

start = datetime(2024, 3, 15, 9, 0, 0)
end = datetime(2024, 3, 15, 17, 30, 0)
duration = end - start

print(duration)
print(duration.total_seconds())
8:30:00
30600.0

You can also move a moment forward or backward by adding or subtracting a timedelta. Build the duration you mean instead of doing manual math:

from datetime import datetime, timedelta

meeting = datetime(2024, 3, 15, 15, 0, 0)
reminder = meeting - timedelta(minutes=30)
print(reminder)
2024-03-15 14:30:00

timedelta accepts weeks, days, hours, minutes, seconds, and more. This is where the object model earns its keep: measuring how long a script step took, or computing a due date from today, becomes one line instead of a pile of arithmetic.

Knowledge check

Check your understanding

Answer this question before you continue.

What are the two printed lines?
Output Prediction

Focus: Calculate the duration and total seconds between two `datetime` values.

from datetime import datetime
start = datetime(2024, 3, 15, 9, 0, 0)
end = datetime(2024, 3, 15, 17, 30, 0)
duration = end - start
print(duration)
print(duration.total_seconds())

Comparing Dates and Times

Because dates are objects, comparison operators work directly on them. Python compares them chronologically:

from datetime import datetime

deadline = datetime(2024, 3, 20, 17, 0, 0)
now = datetime(2024, 3, 15, 14, 30, 0)

print(now < deadline)
print(now > deadline)
print(now == deadline)
True
False
False

This is the reason the object model matters. "Has the deadline passed?" is one comparison, not a string-parsing exercise.

Warning: comparing a naive datetime (no time zone) to an aware one (with a time zone) raises TypeError. That boundary is exactly where the next section begins. Also, do not compare a date object to a datetime object and expect a meaningful result — convert first with .date().

Naive vs Aware: When Time Zones Matter

A naive datetime carries no time zone information. It is ambiguous — 14:30 in which part of the world? An aware datetime carries an offset and can be converted and compared safely across regions.

from datetime import datetime, timezone

print(datetime.now())
print(datetime.now(timezone.utc))
2024-03-15 14:30:45.123456
2024-03-15 18:30:45.123456+00:00

The first is naive. The second is aware — note the +00:00 offset.

Use aware values when the moment crosses machines, servers, users, or regions: logs, APIs, scheduled jobs, anything stored for later. Use naive values when the time is local and self-contained — a reminder for "9 AM tomorrow" on your own laptop.

That is the boundary for today. When you need named time zones like America/New_York, the zoneinfo module handles it. That is the next step, not today's lesson.

Knowledge check

Check your understanding

Answer this question before you continue.

A script stores event timestamps that will be shared across servers in different regions. Which choice follows the article's guidance?
Single Choice

Focus: Choose when an aware `datetime` is appropriate for a timestamp that crosses systems or regions.

Practice: Build a Small Date Utility

Time to combine everything. Write a script that reads two date strings, parses both, prints the days between them, and prints the end date in a friendly format.

from datetime import datetime

start_string = "2024-03-01"
end_string = "2024-03-15"

start = datetime.strptime(start_string, "%Y-%m-%d")
end = datetime.strptime(end_string, "%Y-%m-%d")

days_between = end - start
print(f"Days between: {days_between.days}")
print(end.strftime("%A, %B %d, %Y"))
Days between: 14
Friday, March 15, 2024

For the parse step, use %Y-%m-%d. For the friendly output, %A, %B %d, %Y gives you the weekday, month name, day, and year.

A smaller drill to lock in the arithmetic: print today's date plus 30 days as an ISO string.

from datetime import datetime, timedelta

future = datetime.now() + timedelta(days=30)
print(future.isoformat())

If you want a real challenge, extend the first script to print a warning when the end date comes before the start date. That single check is the kind of guardrail that separates a script you trust from one you have to babysit.

What to Memorize and What to Look Up

Do not try to hold the whole module in your head. Hold the decisions, look up the details.

Memorize: the four class names, the import line, the strptime vs strftime direction rule, and that timedelta handles durations.

Look up: the full format-code table, time zone names, and edge cases like daylight saving transitions.

My rule when a format string fights back: print the raw string and the format side by side before changing any code. Nine times out of ten, the mismatch is visible the moment you stop guessing and start comparing.

Here is your next action. Take a CSV or log file you already have, parse its date column with strptime, and sort the rows chronologically. That one exercise turns today's syntax into a habit — and once local dates feel routine, timezone-aware handling is the natural next skill to add.

Knowledge check

Final check

Finish the article by checking the ideas you just learned.

A record must identify both the calendar day and the exact time of an event. Which class best matches that value?
Question 1 of 2Single Choice

Focus: Select the datetime class that matches whether a value needs a calendar date, a time, or both.

A utility needs to determine whether the end date comes before the start date. Both strings use the format `YYYY-MM-DD`. Which approach follows the article's date utility pattern?
Question 2 of 2Debugging

Focus: Use parsed date values and chronological comparisons rather than comparing ambiguous date strings.

References

  1. DateTime Objects — Python 3.14.8 documentationdocs.python.org
Practical resource

Want a more structured Python path?

Use the Python Starter Pack to turn scattered tutorials into a focused practice path.

View the bundle
Coming soon

Python for Artificial Intelligence Starter Pack

Build a Python foundation you can actually use. The Python for AI Starter Pack brings together a guided path through setup, core programming concepts, data structures, files, JSON, APIs, debugging, and practical projects—so you can move quickly from running your first program to understanding and building useful software.

$9
PDF BundlePythonAIBeginner
  • 264-page illustrated PDF
  • 12 guided Python chapters
  • Visual concept diagrams
  • Self-assessment quizzes
  • Bonus deep-dive sections
  • Files, JSON, APIs, debugging & projects
  • Foundation for data, automation & AI

Coming soon

Free Python bundle

Get the LearnPyFast Python for Artificial Intelligence Starter Bundle

Build a Python foundation you can actually use. The Python for Artificial Intelligence Starter Pack brings together a guided path through setup, core programming concepts, data structures, files, JSON, APIs, debugging, and practical projects—so you can move quickly from running your first program to understanding and building useful software.

You’ll receive the bundle by email. You can unsubscribe anytime.

No spam. You can unsubscribe anytime. See our Privacy policy.

Related sites

Continue beyond Python

Explore related Worldmonger sites when you want to move from Python basics into JavaScript or LLM application building.

JavaScript tutorialstutorial

LearnJSFast

Beginner-friendly JavaScript tutorials for practical web development and self-taught developers.

JavaScriptFrontendWeb development
Visit LearnJSFast
LLM tutorialstutorial

LearnLLMFast

Practical LLM tutorials for builders who want to understand prompting, workflows, agents, and AI applications.

LLMAIBuilders
Visit LearnLLMFast

Keep learning

Related tutorials

Continue with nearby Python topics and beginner-friendly explanations.

Close-up view of HTML and CSS code displayed on a computer screen, ideal for programming and technology themes.
beginner
11 min read

Basic Math in Python

Python does not make you memorize a calculator manual. It hands you a small set of operators and lets you run the calculation and read the answer…

Read tutorial
Close-up of a large pot filled with black dye used in traditional incense stick production indoors.
beginner
9 min read

Python Comments and Code Style

Comments are not for Python. They are for the next human who reads your code—and that human is often you, six weeks later. The interpreter skips every line…

Read tutorial