LEARN PYTHON | Working With Pathlib And Dates

Lesson objective

Construct explicit file paths, distinguish working-directory and script-relative locations, and validate and calculate calendar dates.

Learn

05.4 | WORKING WITH PATHLIB AND DATES

01 | A PATH DESCRIBES A LOCATION

A path is a description of a location, not an open file or the file’s contents. pathlib.Path represents paths with useful operations that adapt to the operating system. It helps avoid hand-building strings with hard-coded slash characters.

Path("data") / "expenses.json" joins path components. The / operator here is overloaded for Path objects: it joins locations rather than dividing numbers. A Path can refer to something that does not yet exist.

Use str(path) if an interface needs ordinary text. Many modern Python file functions accept Path directly, and Path.open gives a familiar file object.

from pathlib import Path

path = Path("data") / "expenses.json"
print(path.name)
print(path.stem)
print(path.suffix)

EXPECTED OUTPUT

expenses.json
expenses
.json

02 | RELATIVE TO WHICH BASE?

A relative path is normally interpreted from the process’s current working directory. Path.cwd() reports that directory. An absolute path specifies a full location. Starting a script from a different folder can therefore change where an unanchored relative filename points.

For a local project that should keep data beside its script, use Path(__file__).resolve().parent as a base in a saved script. __file__ identifies that module’s file; it may not exist in an interactive shell or every notebook environment.

The expense tracker uses this script-relative base deliberately. You can move the project folder and still keep its expenses.json beside tracker.py. This is a design choice, not a rule that every application should store data beside source code.

from pathlib import Path

# Use inside a saved .py script.
BASE_DIR = Path(__file__).resolve().parent
DATA_PATH = BASE_DIR / "expenses.json"
print(DATA_PATH.name)

EXPECTED OUTPUT

expenses.json

03 | CREATE THE FOLDER BEFORE THE FILE

Opening a file for writing can create the file but does not normally create missing parent directories. folder.mkdir(parents=True, exist_ok=True) creates necessary directories and tolerates a directory already being present.

It does not make every failure disappear. If a file already occupies the requested directory location, permissions block access or storage fails, an exception can still occur.

exists() and is_file() are useful observations, but a resource can change between a check and a later open. For correctness, still handle a failure from the operation itself.

from pathlib import Path

folder = Path("practice_data")
folder.mkdir(parents=True, exist_ok=True)
path = folder / "note.txt"
path.write_text("A saved idea\n", encoding="utf-8")
print(path.is_file())
print(path.read_text(encoding="utf-8"), end="")

EXPECTED OUTPUT

True
A saved idea

04 | CHANGE A PATH WITHOUT CHANGING THE FILE

path.with_suffix(".csv") returns another Path describing a different suffix. path.with_name("backup.json") returns another name in the same parent. These operations do not rename or copy the actual file.

To move or replace a real file, an operation such as replace() acts on the filesystem. A replacement can overwrite the destination, so the project uses it only after writing a checked candidate to a separate temporary file.

resolve() produces a resolved path, handling relative components and symbolic links where applicable. It is not a general guarantee that a file exists or that a user-supplied path is safe for every application.

from pathlib import Path

original = Path("data") / "expenses.json"
export = original.with_suffix(".csv")
print(original.name)
print(export.name)
print(original.name)

EXPECTED OUTPUT

expenses.json
expenses.csv
expenses.json

05 | DATE AND DATETIME ARE DIFFERENT

datetime.date represents a calendar date: year, month and day. datetime.datetime also represents a time of day. For an expense date, date is sufficient; no time zone or clock is required.

date(2026, 10, 1) constructs a date from integer components and rejects impossible combinations. date.today() gets the local current date, but a unit test should use a fixed date when it needs a reproducible result.

Timezone-aware and naive datetime values are a later topic. Do not assume a date-only record identifies an exact instant around the world.

from datetime import date

day = date(2026, 10, 1)
print(day.year, day.month, day.day)
print(day.isoformat())

EXPECTED OUTPUT

2026 10 1
2026-10-01

06 | PARSE AND FORMAT DELIBERATELY

date.fromisoformat converts supported ISO date text into a date. Current Python accepts some forms beyond the dashed YYYY-MM-DD used by this course. If the interface promises exactly that format, check the structure separately before parsing.

A regular expression, or regex, is a pattern describing text structure. re.fullmatch checks the entire string. [0-9]{4}-[0-9]{2}-[0-9]{2} requires four ASCII digits, a hyphen, two digits, a hyphen and two digits. It cannot independently know whether February has that many days.

The calendar parser supplies that second check. After success, isoformat returns the canonical dashed representation. strftime can produce a display such as 01/10/2026, but that display does not change the date object.

from datetime import date
import re

text = "2024-02-29"
if not re.fullmatch(r"[0-9]{4}-[0-9]{2}-[0-9]{2}", text):
    raise ValueError("Use YYYY-MM-DD")
day = date.fromisoformat(text)
print(day.isoformat())
print(day.strftime("%d/%m/%Y"))

EXPECTED OUTPUT

2024-02-29
29/02/2024

07 | SUBTRACT DATES TO GET A DURATION

Subtracting one date from another produces timedelta, a duration. Its days attribute gives the whole day difference for date-only arithmetic. Adding timedelta(days=7) moves through month and year boundaries correctly.

Do not calculate elapsed days by subtracting the day-number fields. October 31 to November 1 is one day, not 1 - 31. Calendar arithmetic handles those boundaries.

Month lengths vary, so adding thirty days is not a general equivalent of adding one calendar month. Define whether your requirement is a duration or a calendar-month shift.

from datetime import date, timedelta

start = date(2026, 10, 31)
end = date(2026, 11, 1)
print("Elapsed days:", (end - start).days)
print("A week later:", start + timedelta(days=7))

EXPECTED OUTPUT

Elapsed days: 1
A week later: 2026-11-07

08 | SORT AND FILTER CANONICAL DATES

Canonical YYYY-MM-DD strings have a consistent width and year-first order, so lexical order agrees with calendar order within the supported year range. Sorting inconsistent strings such as 1/10/2026 and 30/9/2026 would not provide that guarantee.

For the project, a validated month prefix YYYY-MM selects records whose canonical date starts with that month followed by a hyphen. Validate the month itself: 2026-13 is not made valid simply because startswith can compare it.

An alternative parses date objects and compares year and month. Choose a rule that matches your stored representation and document it.

09 | CHECK CALENDAR INPUT

The browser model enforces the same dashed structure and real calendar dates for years 0001 through 9999. The output distinguishes a format problem from an impossible calendar combination. It validates dates only, not whether an expense occurred.

10 | KEEP LOCATIONS AND TIME REPRODUCIBLE

For development tests, use a temporary folder and fixed calendar dates. Do not let a test depend on a student’s home directory, today’s changing date or an existing production save.

Explain your base path choice and show users where the data is stored. A program that appears to lose records may have opened a different relative location, rather than failed to save.

The project combines these ideas: a script-relative save path, validated canonical dates and temporary directories for storage tests. Those choices make it easier to repeat and diagnose behaviour.

PUT YOUR SKILLS TO WORK

Use the Section 5 challenges to practise these ideas independently. Work in your Python editor, predict results and keep a short test record.

Section 5 challenges Python course navigation

MATCH THE PYTHON TERM TO ITS MEANING

Terminology

Terminology

Path

An object describing a filesystem location.

pathlib

Python’s standard library for object-based path operations.

Path component

A part joined into a larger path.

Working directory

The process directory used as the base for ordinary relative paths.

Absolute path

A path specifying a full filesystem location.

Relative path

A path interpreted from a base location.

__file__

A module attribute identifying its source file where available.

Parent

The containing directory represented by a Path.

Stem

A filename without its final suffix.

Suffix

The final extension portion of a filename.

mkdir

A operation creating a directory.

date

A calendar-date object with year, month and day.

datetime

A date-and-time object.

timedelta

An object representing a duration.

ISO date

A consistent representation such as YYYY-MM-DD.

Regular expression

A pattern describing text structure.

Full match

A pattern match covering the entire supplied string.

Canonical representation

One agreed normal form for storing equivalent data.

Questions

Questions

CHECK YOUR UNDERSTANDING

Select all correct choices. Each exact set earns one point.

1. What does Path("data") / "expenses.json" do?
2. An ordinary relative path is interpreted from where?
3. Which claims about with_suffix are correct?
4. What does a date object represent?
5. Why combine strict text structure with date.fromisoformat?
6. What is date(2026,11,1) - date(2026,10,31)?
7. Which storage-test inputs are reproducible?
8. Why can canonical YYYY-MM-DD strings sort chronologically?

EXPLAIN AND COMPARE

Write your answers before revealing each sample.

1. Explain the Path / operator.

2. Compare working-directory and script-relative paths.

3. Explain why a write can fail with missing parents.

4. Distinguish constructing a new suffix from renaming a file.

5. Compare date and datetime.

6. Explain strict ISO-format validation.

7. Calculate a month-boundary duration.

8. Suggest reproducible fixtures.

CODE IT | YOUR INDEPENDENT CHALLENGE

Use pathlib for a clear data location and calculate a fixed date interval across a month boundary.

Work in your Python editor. Keep a working version, explain one design choice and record at least two tests. Use the Learn tab for worked examples before comparing your result.

Flashcards

Flashcards

Click to flip. Select the ideas you need to revisit.

0 cards selected for revision.

    Selections are kept while this page is open.

    Workbook

    Workbook

    COMING SOON

    The workbook for Working With Pathlib And Dates is coming soon. Complete the matching task and written questions in your exercise book.