Typorithm · easy

Timezone-aware windows and repeatable clocks

pythonapi-clientsautomationindependent-practice
Saved on this device
Full lesson · 4 learning steps Open any exercise for focused practice
1

Step 1 of 4 · intro card

Timezone-aware windows and repeatable clocks

0%

Concept and contract

Derive UTC boundaries from an explicit aware instant instead of mixing local clocks and naive timestamps.

A report window needs an instant and a duration, not an unrecorded call to the current clock in every helper. Offset-aware datetimes describe comparable instants; naive datetimes do not specify the timezone needed for that comparison.

This lesson receives as_of explicitly and requires its UTC offset. It returns a UTC boundary, making a fixture reproducible regardless of the computer timezone or the day you run it.

Mechanism and worked example

fromisoformat parses the supplied timestamp, utcoffset checks awareness and astimezone converts the instant to UTC.

fromisoformat parses the supplied timestamp, utcoffset checks awareness and astimezone converts the instant to UTC. Subtracting a duration after conversion expresses elapsed hours consistently.

The output keeps an explicit +00:00 offset. This lesson does not promise a strict RFC 3339 validator: Python accepts the formats documented for fromisoformat. Define a narrower external wire format separately if a particular API requires one.

Complete worked example

python

from datetime import datetime, timedelta, timezone

def cutoff(as_of, hours):
    if type(hours) is not int or not 0 <= hours <= 48:
        raise ValueError("hours must be 0..48")
    instant = datetime.fromisoformat(as_of)
    if instant.utcoffset() is None:
        raise ValueError("offset required")
    return (instant.astimezone(timezone.utc) - timedelta(hours=hours)).isoformat()
Reasoning
Derive UTC boundaries from an explicit aware instant instead of mixing local clocks and naive timestamps. Compare the stated values and failure boundary before changing the implementation.

Counterexamples and common mistakes

The counterexample discards the supplied offset by replacing tzinfo with UTC instead of converting the instant.

The counterexample discards the supplied offset by replacing tzinfo with UTC instead of converting the instant. The displayed clock fields look plausible, but the resulting instant is wrong.

Another error is accepting a naive time and assuming it means the server timezone. Tests should include a nonzero offset and a boundary crossing midnight. Merely checking a timestamp already written in UTC will not expose the conversion defect.

Counterexample: a runnable policy defect

python

from datetime import datetime, timedelta, timezone

def cutoff(as_of, hours):
    if type(hours) is not int or not 0 <= hours <= 48:
        raise ValueError("hours must be 0..48")
    instant = datetime.fromisoformat(as_of)
    if instant.utcoffset() is None:
        raise ValueError("offset required")
    return (instant.replace(tzinfo=timezone.utc) - timedelta(hours=hours)).isoformat()
Reasoning
This runnable counterexample violates the declared contract; the companion lab requires it to fail.

Independent practice: API client task

Add minutes as an optional second duration component, bounded from zero through fifty-nine, with a default of zero.

Add minutes as an optional second duration component, bounded from zero through fifty-nine, with a default of zero. Keep hours bounded and reject booleans for both numeric fields.

Start with one UTC case and then a nonzero offset crossing the previous day. You are changing the duration input, not building a calendar scheduler or handling every daylight-saving transition in a named timezone.

Step 1: Add a validated minutes component with default zero. Step 2: Keep aware-input and exact integer requirements intact.

Practice starter: solve before reading the solution

python

from datetime import datetime, timedelta, timezone

def cutoff(as_of, hours):
    # Remove this placeholder, then adapt the retained baseline:
    # Add a validated minutes component with default zero.
    raise NotImplementedError
    if type(hours) is not int or not 0 <= hours <= 48:
        raise ValueError("hours must be 0..48")
    instant = datetime.fromisoformat(as_of)
    if instant.utcoffset() is None:
        raise ValueError("offset required")
    return (instant.astimezone(timezone.utc) - timedelta(hours=hours)).isoformat()
Reasoning
Add a validated minutes component with default zero. Keep aware-input and exact integer requirements intact.
  • Hint 1: Build one timedelta with both hours and minutes.
  • Hint 2: Convert the instant to UTC before subtracting elapsed duration.

Solution, tests and trade-offs

The solution calculates one elapsed duration from validated hours and minutes and subtracts it from the converted UTC instant.

The solution calculates one elapsed duration from validated hours and minutes and subtracts it from the converted UTC instant. Cases distinguish zero from missing values and reject a naive timestamp, a negative duration and a boolean.

Expected strings are fixed in the lab, so a test cannot quietly use the same incorrect current-clock calculation as the implementation. Record the chosen as_of alongside a real generated report.

Practice solution and acceptance checks

python

from datetime import datetime, timedelta, timezone

def cutoff(as_of, hours, minutes=0):
    if type(hours) is not int or not 0 <= hours <= 48:
        raise ValueError("hours must be 0..48")
    if type(minutes) is not int or not 0 <= minutes <= 59:
        raise ValueError("minutes must be 0..59")
    instant = datetime.fromisoformat(as_of)
    if instant.utcoffset() is None:
        raise ValueError("offset required")
    return (instant.astimezone(timezone.utc) - timedelta(hours=hours, minutes=minutes)).isoformat()
Reasoning
Build one timedelta with both hours and minutes. Convert the instant to UTC before subtracting elapsed duration.

Interview reasoning and next step

Explain the difference between replacing timezone metadata and converting an instant.

Explain the difference between replacing timezone metadata and converting an instant. A business rule such as the previous local calendar day requires a named zone and calendar boundaries, which differ from an elapsed twenty-four-hour window.

Use zoneinfo for that separate requirement. Our smaller UTC contract prepares a repeatable query boundary that can later become part of an API URL and a correctly scoped cache key.

Optional stretch — after the core task works: Compare an elapsed day with a named-zone calendar day around a clock change.

Computer practice: https://applydjinn.com/typorithm/python-automation-lab-20261105.zip. Run verify.py using your selected Python interpreter, then adapt the retained exercise baseline and check it with --practice followed by the lesson id. The standard-library lab uses owned temporary 127.0.0.1 loopback servers, fictional pages and disposable files; it never scans production feeds or opens the working ApplyDjinn database.

Derive UTC boundaries from an explicit aware instant instead of mixing local clocks and naive timestamps.