Claude Code for datetime: Dates and Times in Python — Claude Skills 360 Blog
Blog / AI / Claude Code for datetime: Dates and Times in Python
AI

Claude Code for datetime: Dates and Times in Python

Published: July 9, 2028
Read time: 5 min read
By: Claude Skills 360

Python’s datetime module represents dates, times, and durations. from datetime import date, time, datetime, timedelta, timezone, UTC. date: d = date(2024, 1, 15); d.year, d.month, d.day; date.today(). time: t = time(10, 30, 0). datetime: dt = datetime(2024, 1, 15, 10, 30, 0). now: datetime.now(); datetime.now(UTC) — UTC-aware. utcnow: datetime.utcnow() — naive UTC (prefer datetime.now(UTC)). fromisoformat: datetime.fromisoformat("2024-01-15T10:30:00"). isoformat: dt.isoformat() → “2024-01-15T10:30:00”. strftime: dt.strftime("%Y-%m-%d %H:%M:%S"). strptime: datetime.strptime("01/15/2024", "%m/%d/%Y"). timedelta: td = timedelta(days=7, hours=2, minutes=30); dt + td; dt - td; td.total_seconds(). replace: dt.replace(hour=0, minute=0, second=0, microsecond=0) — start of day. combine: datetime.combine(date(2024,1,15), time(10,30)). timestamp: dt.timestamp() — Unix epoch float. fromtimestamp: datetime.fromtimestamp(ts, tz=UTC). astimezone: dt.astimezone(target_tz). UTC: from datetime import UTC (Python 3.11+) or timezone.utc. zoneinfo: from zoneinfo import ZoneInfo; dt.replace(tzinfo=ZoneInfo("America/New_York")). is_aware: dt.tzinfo is not None. weekday: d.weekday() → 0=Mon. date.fromordinal/toordinal. date arithmetic: (date.today() - date(2000,1,1)).days. Claude Code generates date range iterators, business-day calculators, ISO 8601 parsers, and timezone converters.

CLAUDE.md for datetime

## datetime Stack
- Stdlib: from datetime import date, time, datetime, timedelta, timezone, UTC
- Timezone: from zoneinfo import ZoneInfo  — IANA zones (Python 3.9+)
- Now (aware): datetime.now(UTC)  — always prefer over utcnow() (naive)
- Parse: datetime.fromisoformat(s) | datetime.strptime(s, fmt)
- Format: dt.isoformat() | dt.strftime("%Y-%m-%d") | dt.timestamp()
- Arithmetic: dt + timedelta(days=N) | (dt2 - dt1).total_seconds()

datetime Date and Time Pipeline

# app/dtutil.py — datetime, timedelta, timezone, zoneinfo, formatters, ranges
from __future__ import annotations

import calendar
from datetime import UTC, date, datetime, time, timedelta, timezone
from typing import Iterator

try:
    from zoneinfo import ZoneInfo
except ImportError:                         # Python < 3.9 fallback
    ZoneInfo = None  # type: ignore[assignment,misc]


# ─────────────────────────────────────────────────────────────────────────────
# 1. Aware datetime helpers
# ─────────────────────────────────────────────────────────────────────────────

def now_utc() -> datetime:
    """
    Return current time as UTC-aware datetime.
    Prefer over datetime.utcnow() which returns a naive datetime.

    Example:
        ts = now_utc()
        print(ts.isoformat())   # 2024-01-15T10:30:00+00:00
    """
    return datetime.now(UTC)


def now_local() -> datetime:
    """Return current local time with UTC offset attached."""
    return datetime.now().astimezone()


def as_utc(dt: datetime) -> datetime:
    """
    Convert a naive or aware datetime to UTC.
    Naive datetimes are assumed to already be UTC.

    Example:
        as_utc(datetime(2024, 1, 15, 10, 30))   # → 2024-01-15T10:30:00+00:00
    """
    if dt.tzinfo is None:
        return dt.replace(tzinfo=UTC)
    return dt.astimezone(UTC)


def to_timezone(dt: datetime, tz_name: str) -> datetime:
    """
    Convert datetime to a named IANA timezone.

    Example:
        ny_time = to_timezone(now_utc(), "America/New_York")
        print(ny_time.strftime("%Y-%m-%d %H:%M %Z"))
    """
    if ZoneInfo is None:
        raise RuntimeError("zoneinfo requires Python 3.9+ or the backports.zoneinfo package")
    tz = ZoneInfo(tz_name)
    return as_utc(dt).astimezone(tz)


def is_aware(dt: datetime) -> bool:
    """Return True if dt has timezone info attached."""
    return dt.tzinfo is not None and dt.tzinfo.utcoffset(dt) is not None


# ─────────────────────────────────────────────────────────────────────────────
# 2. Parsing and formatting
# ─────────────────────────────────────────────────────────────────────────────

_COMMON_FORMATS = [
    "%Y-%m-%dT%H:%M:%S",
    "%Y-%m-%dT%H:%M:%SZ",
    "%Y-%m-%dT%H:%M:%S%z",
    "%Y-%m-%d %H:%M:%S",
    "%Y-%m-%d %H:%M",
    "%Y-%m-%d",
    "%d/%m/%Y %H:%M:%S",
    "%d/%m/%Y",
    "%m/%d/%Y",
]


def parse_datetime(s: str) -> datetime:
    """
    Parse a datetime string trying common formats.
    Raises ValueError if no format matches.

    Example:
        parse_datetime("2024-01-15")
        parse_datetime("2024-01-15T10:30:00Z")
        parse_datetime("01/15/2024")
    """
    # Try fromisoformat first (fastest, handles ISO 8601)
    try:
        return datetime.fromisoformat(s.rstrip("Z").replace("Z", "+00:00"))
    except ValueError:
        pass

    for fmt in _COMMON_FORMATS:
        try:
            return datetime.strptime(s, fmt)
        except ValueError:
            continue

    raise ValueError(f"Cannot parse datetime: {s!r}")


def parse_date(s: str) -> date:
    """
    Parse a date string (YYYY-MM-DD or common variants).

    Example:
        parse_date("2024-01-15")   # date(2024, 1, 15)
        parse_date("15/01/2024")   # date(2024, 1, 15)
    """
    for fmt in ["%Y-%m-%d", "%d/%m/%Y", "%m/%d/%Y", "%d-%m-%Y"]:
        try:
            return datetime.strptime(s, fmt).date()
        except ValueError:
            continue
    raise ValueError(f"Cannot parse date: {s!r}")


def fmt_iso(dt: datetime) -> str:
    """Format as compact ISO 8601 string."""
    return dt.strftime("%Y-%m-%dT%H:%M:%S")


def fmt_human(dt: datetime) -> str:
    """Format as human-readable date/time string."""
    return dt.strftime("%B %-d, %Y at %-I:%M %p")


def fmt_date(d: date) -> str:
    """Format date as YYYY-MM-DD."""
    return d.strftime("%Y-%m-%d")


def days_ago(n: int, tz: datetime = None) -> datetime:
    """
    Return datetime n days ago at midnight UTC.

    Example:
        since = days_ago(30)    # 30 days ago
    """
    base = now_utc().replace(hour=0, minute=0, second=0, microsecond=0)
    return base - timedelta(days=n)


# ─────────────────────────────────────────────────────────────────────────────
# 3. Date arithmetic
# ─────────────────────────────────────────────────────────────────────────────

def start_of_day(dt: datetime) -> datetime:
    """
    Truncate datetime to midnight (start of day), preserving timezone.

    Example:
        start_of_day(datetime(2024, 1, 15, 10, 30, tzinfo=UTC))
        # datetime(2024, 1, 15, 0, 0, tzinfo=UTC)
    """
    return dt.replace(hour=0, minute=0, second=0, microsecond=0)


def end_of_day(dt: datetime) -> datetime:
    """Return 23:59:59.999999 of the given day."""
    return dt.replace(hour=23, minute=59, second=59, microsecond=999999)


def start_of_month(dt: datetime) -> datetime:
    """Return midnight on the 1st of the given month."""
    return dt.replace(day=1, hour=0, minute=0, second=0, microsecond=0)


def end_of_month(dt: datetime) -> datetime:
    """Return the last moment of the given month."""
    last_day = calendar.monthrange(dt.year, dt.month)[1]
    return dt.replace(day=last_day, hour=23, minute=59, second=59, microsecond=999999)


def add_months(dt: datetime, months: int) -> datetime:
    """
    Add (or subtract) months, clamping day to last valid day.

    Example:
        add_months(datetime(2024, 1, 31), 1)   # 2024-02-29 (leap year)
        add_months(datetime(2024, 3, 31), -1)  # 2024-02-29
    """
    month = dt.month - 1 + months
    year  = dt.year + month // 12
    month = month % 12 + 1
    day   = min(dt.day, calendar.monthrange(year, month)[1])
    return dt.replace(year=year, month=month, day=day)


def is_business_day(d: date) -> bool:
    """
    Return True if d is Monday–Friday (ignores public holidays).

    Example:
        is_business_day(date(2024, 1, 15))   # True (Monday)
        is_business_day(date(2024, 1, 13))   # False (Saturday)
    """
    return d.weekday() < 5


def next_business_day(d: date) -> date:
    """
    Return the next business day after d.

    Example:
        next_business_day(date(2024, 1, 12))  # 2024-01-15 (Mon)
    """
    d = d + timedelta(days=1)
    while not is_business_day(d):
        d += timedelta(days=1)
    return d


def business_days_between(start: date, end: date) -> int:
    """
    Count business days in the half-open interval [start, end).

    Example:
        business_days_between(date(2024, 1, 15), date(2024, 1, 22))  # 5
    """
    count = 0
    d = start
    while d < end:
        if is_business_day(d):
            count += 1
        d += timedelta(days=1)
    return count


# ─────────────────────────────────────────────────────────────────────────────
# 4. Date range iterators
# ─────────────────────────────────────────────────────────────────────────────

def date_range(start: date, end: date, step: int = 1) -> Iterator[date]:
    """
    Yield dates from start to end (exclusive) by step days.

    Example:
        list(date_range(date(2024,1,1), date(2024,1,5)))
        # [2024-01-01, 2024-01-02, 2024-01-03, 2024-01-04]
    """
    d = start
    delta = timedelta(days=step)
    while d < end:
        yield d
        d += delta


def month_range(year: int, month: int) -> Iterator[date]:
    """
    Yield every date in the given month.

    Example:
        days = list(month_range(2024, 2))  # all 29 days of Feb 2024
    """
    last = calendar.monthrange(year, month)[1]
    return date_range(date(year, month, 1), date(year, month, last + 1))


def week_range(d: date) -> tuple[date, date]:
    """
    Return (monday, sunday) of the ISO week containing d.

    Example:
        week_range(date(2024, 1, 17))   # (2024-01-15, 2024-01-21)
    """
    monday = d - timedelta(days=d.weekday())
    sunday = monday + timedelta(days=6)
    return monday, sunday


# ─────────────────────────────────────────────────────────────────────────────
# 5. Duration helpers
# ─────────────────────────────────────────────────────────────────────────────

def human_duration(seconds: float) -> str:
    """
    Format a duration in seconds as a human-readable string.

    Example:
        human_duration(3661)    # "1h 1m 1s"
        human_duration(90)      # "1m 30s"
        human_duration(0.5)     # "0.500s"
    """
    if seconds < 1:
        return f"{seconds:.3f}s"
    s = int(seconds)
    m, s = divmod(s, 60)
    h, m = divmod(m, 60)
    d, h = divmod(h, 24)
    parts = []
    if d:
        parts.append(f"{d}d")
    if h:
        parts.append(f"{h}h")
    if m:
        parts.append(f"{m}m")
    if s or not parts:
        parts.append(f"{s}s")
    return " ".join(parts)


def age(dt: datetime, reference: datetime | None = None) -> timedelta:
    """
    Return age (elapsed time) of a datetime relative to reference (default: now).

    Example:
        born = datetime(1990, 6, 15, tzinfo=UTC)
        print(age(born).days // 365, "years old")
    """
    ref = reference or now_utc()
    return as_utc(ref) - as_utc(dt)


# ─────────────────────────────────────────────────────────────────────────────
# Demo
# ─────────────────────────────────────────────────────────────────────────────

if __name__ == "__main__":
    print("=== datetime demo ===")

    print(f"\n--- now (UTC-aware) ---")
    print(f"  now_utc():   {now_utc().isoformat()}")
    print(f"  now_local(): {now_local().isoformat()}")

    print(f"\n--- parse_datetime ---")
    for s in ["2024-01-15", "2024-01-15T10:30:00Z", "2024-01-15 10:30", "01/15/2024"]:
        try:
            dt = parse_datetime(s)
            print(f"  {s!r:35s}{dt.isoformat()!r}")
        except ValueError as e:
            print(f"  {s!r}: {e}")

    print(f"\n--- date arithmetic ---")
    dt = datetime(2024, 1, 15, 10, 30, tzinfo=UTC)
    print(f"  start_of_day:   {start_of_day(dt).isoformat()}")
    print(f"  end_of_day:     {end_of_day(dt).isoformat()}")
    print(f"  start_of_month: {start_of_month(dt).isoformat()}")
    print(f"  add_months(+1): {add_months(dt, 1).isoformat()}")
    print(f"  add_months(+13):{add_months(dt, 13).isoformat()}")

    print(f"\n--- business days ---")
    d1 = date(2024, 1, 15)  # Monday
    d2 = date(2024, 1, 22)  # Monday
    print(f"  is_business_day(Mon): {is_business_day(d1)}")
    print(f"  is_business_day(Sat): {is_business_day(date(2024,1,13))}")
    print(f"  business_days_between: {business_days_between(d1, d2)}")
    print(f"  next_business_day(Fri): {next_business_day(date(2024,1,12))}")

    print(f"\n--- date_range ---")
    days = list(date_range(date(2024,1,1), date(2024,1,6)))
    print(f"  {[str(d) for d in days]}")

    print(f"\n--- week_range ---")
    mon, sun = week_range(date(2024, 1, 17))
    print(f"  week containing 2024-01-17: {mon}{sun}")

    print(f"\n--- human_duration ---")
    for s in [0.5, 90, 3661, 90061, 86400*3 + 7261]:
        print(f"  {s:10.1f}s → {human_duration(s)!r}")

    print(f"\n--- age ---")
    born = datetime(1990, 6, 15, tzinfo=UTC)
    ref  = datetime(2024, 6, 15, tzinfo=UTC)
    td   = age(born, ref)
    print(f"  age at 2024-06-15: {td.days} days  ({td.days//365} years)")

    print("\n=== done ===")

For the pendulum alternative — pendulum (PyPI) wraps Python datetime with a friendlier API: pendulum.now("UTC"), pendulum.parse("2024-01-15"), duration arithmetic with add(months=3), and human-readable diff_for_humans() output; Python’s stdlib datetime + zoneinfo handles timezone-aware arithmetic without dependencies — use pendulum for date-math-heavy code where add_months(), natural language output, or Period intervals matter, stdlib datetime + zoneinfo for applications where minimizing dependencies is important. For the arrow alternative — arrow (PyPI) provides a unified Arrow object combining date + time + timezone + formatting with a fluent API: arrow.utcnow().shift(days=+7).format("YYYY-MM-DD"); stdlib datetime is more verbose but is always available and integrates directly with json, csv, sqlite3, and other stdlib modules — use arrow for rapid prototyping needing elegant date shift syntax, stdlib datetime for production libraries and applications where the UTC/aware discipline in this post’s helpers is sufficient. The Claude Skills 360 bundle includes datetime skill sets covering now_utc()/now_local()/as_utc()/to_timezone()/is_aware() awareness helpers, parse_datetime()/parse_date()/fmt_iso()/fmt_human() parsing and formatting, start_of_day()/end_of_day()/start_of_month()/add_months() arithmetic, is_business_day()/next_business_day()/business_days_between() business-day logic, date_range()/month_range()/week_range() iterators, and human_duration()/age() duration utilities. Start with the free tier to try date arithmetic and datetime pipeline code generation.

Keep Reading

AI

Claude Code for email.contentmanager: Python Email Content Accessors

Read and write EmailMessage body content with Python's email.contentmanager module and Claude Code — email contentmanager ContentManager for the class that maps content types to get and set handler functions allowing EmailMessage to support get_content and set_content with type-specific behaviour, email contentmanager raw_data_manager for the ContentManager instance that handles raw bytes and str payloads without any conversion, email contentmanager content_manager for the standard ContentManager instance used by email.policy.default that intelligently handles text plain text html multipart and binary content types, email contentmanager get_content_text for the handler that returns the decoded text payload of a text-star message part as a str, email contentmanager get_content_binary for the handler that returns the raw decoded bytes payload of a non-text message part, email contentmanager get_data_manager for the get-handler lookup used by EmailMessage get_content to find the right reader function for the content type, email contentmanager set_content text for the handler that creates and sets a text part correctly choosing charset and transfer encoding, email contentmanager set_content bytes for the handler that creates and sets a binary part with base64 encoding and optional filename Content-Disposition, email contentmanager EmailMessage get_content for the method that reads the message body using the registered content manager handlers, email contentmanager EmailMessage set_content for the method that sets the message body and MIME headers in one call, email contentmanager EmailMessage make_alternative make_mixed make_related for the methods that convert a simple message into a multipart container, email contentmanager EmailMessage add_attachment for the method that attaches a file or bytes to a multipart message, and email contentmanager integration with email.message and email.policy and email.mime and io for building high-level email readers attachment extractors text body accessors HTML readers and policy-aware MIME construction pipelines.

5 min read Feb 12, 2029
AI

Claude Code for email.charset: Python Email Charset Encoding

Control header and body encoding for international email with Python's email.charset module and Claude Code — email charset Charset for the class that wraps a character set name with the encoding rules for header encoding and body encoding describing how to encode text for that charset in email messages, email charset Charset header_encoding for the attribute specifying whether headers using this charset should use QP quoted-printable encoding BASE64 encoding or no encoding, email charset Charset body_encoding for the attribute specifying the Content-Transfer-Encoding to use for message bodies in this charset such as QP or BASE64, email charset Charset output_codec for the attribute giving the Python codec name used to encode the string to bytes for the wire format, email charset Charset input_codec for the attribute giving the Python codec name used to decode incoming bytes to str, email charset Charset get_output_charset for returning the output charset name, email charset Charset header_encode for encoding a header string using the charset's header_encoding method, email charset Charset body_encode for encoding body content using the charset's body_encoding, email charset Charset convert for converting a string from the input_codec to the output_codec, email charset add_charset for registering a new charset with custom encoding rules in the global charset registry, email charset add_alias for adding an alias name that maps to an existing registered charset, email charset add_codec for registering a codec name mapping for use by the charset machinery, and email charset integration with email.message and email.mime and email.policy and email.encoders for building international email senders non-ASCII header encoders Content-Transfer-Encoding selectors charset-aware message constructors and MIME encoding pipelines.

5 min read Feb 11, 2029
AI

Claude Code for email.utils: Python Email Address and Header Utilities

Parse and format RFC 2822 email addresses and dates with Python's email.utils module and Claude Code — email utils parseaddr for splitting a display-name plus angle-bracket address string into a realname and email address tuple, email utils formataddr for combining a realname and address string into a properly quoted RFC 2822 address with angle brackets, email utils getaddresses for parsing a list of raw address header strings each potentially containing multiple comma-separated addresses into a list of realname address tuples, email utils parsedate for parsing an RFC 2822 date string into a nine-tuple compatible with time.mktime, email utils parsedate_tz for parsing an RFC 2822 date string into a ten-tuple that includes the UTC offset timezone in seconds, email utils parsedate_to_datetime for parsing an RFC 2822 date string into an aware datetime object with timezone, email utils formatdate for formatting a POSIX timestamp or the current time as an RFC 2822 date string with optional usegmt and localtime flags, email utils format_datetime for formatting a datetime object as an RFC 2822 date string, email utils make_msgid for generating a globally unique Message-ID string with optional idstring and domain components, email utils decode_rfc2231 for decoding an RFC 2231 encoded parameter value into a tuple of charset language and value, email utils encode_rfc2231 for encoding a string as an RFC 2231 encoded parameter value, email utils collapse_rfc2231_value for collapsing a decoded RFC 2231 tuple to a Unicode string, and email utils integration with email.message and email.headerregistry and datetime and time for building address parsers date formatters message-id generators header extractors and RFC-compliant email construction utilities.

5 min read Feb 10, 2029

Put these ideas into practice

Claude Skills 360 gives you production-ready skills for everything in this article — and 2,350+ more. Start free or go all-in.

Back to Blog

Get 360 skills free