Skip to content
This repository was archived by the owner on Apr 27, 2026. It is now read-only.
This repository was archived by the owner on Apr 27, 2026. It is now read-only.

LiteralTimestamp #110

Description

@xmnlab

This is an initial idea from gpt, please feel free to use your own approach (maybe this idea is wrong):


Here’s a visit for LiteralTimestamp you can add to LLVMLiteIRVisitor.
It parses an ISO-style timestamp (YYYY-MM-DDTHH:MM:SS[.fffffffff] or with a space instead of T) and emits a constant struct:

{ i32 year, i32 month, i32 day, i32 hour, i32 minute, i32 second, i32 nanos }

Fractional seconds are parsed into nanoseconds (0–999,999,999). Timezones (Z or ±HH:MM) are not handled in this basic lowering and will raise.

from llvmlite import ir
from plum import dispatch

@dispatch  # type: ignore[no-redef]
def visit(self, node: astx.LiteralTimestamp) -> None:
    """
    Lower a LiteralTimestamp to LLVM IR.

    Representation:
      { i32 year, i32 month, i32 day, i32 hour, i32 minute, i32 second, i32 nanos }

    Accepted formats (no timezone):
      YYYY-MM-DDTHH:MM:SS
      YYYY-MM-DDTHH:MM:SS.sssssssss
      YYYY-MM-DD HH:MM:SS
      YYYY-MM-DD HH:MM:SS.sssssssss

    Notes:
      - Fractional seconds are parsed to nanoseconds (0..999,999,999).
      - Timezones ('Z' or ±HH:MM) are not supported here.
    """
    s = node.value.strip()

    # Split date and time by 'T' or space.
    if "T" in s:
        date_part, time_part = s.split("T", 1)
    elif " " in s:
        date_part, time_part = s.split(" ", 1)
    else:
        raise Exception(
            f"LiteralTimestamp: invalid format '{node.value}'. "
            "Expected 'YYYY-MM-DDTHH:MM:SS[.fffffffff]' (or space instead of 'T')."
        )

    # Reject timezone suffixes for now.
    if time_part.endswith("Z") or "+" in time_part or "-" in time_part[2:]:
        # (the [- in time_part[2:]] avoids the '-' of HH if someone wrote '-1', but
        # standard times won't have that anyway)
        raise Exception(
            f"LiteralTimestamp: timezone offsets not supported in '{node.value}'."
        )

    # Parse date: YYYY-MM-DD
    try:
        y_str, m_str, d_str = date_part.split("-")
        year = int(y_str)
        month = int(m_str)
        day = int(d_str)
    except Exception as exc:
        raise Exception(
            f"LiteralTimestamp: invalid date part in '{node.value}'. "
            "Expected 'YYYY-MM-DD'."
        ) from exc

    # Basic range checks (not validating month/day combinations here)
    if not (1 <= month <= 12):
        raise Exception(f"LiteralTimestamp: month out of range in '{node.value}'.")
    if not (1 <= day <= 31):
        raise Exception(f"LiteralTimestamp: day out of range in '{node.value}'.")

    # Parse time: HH:MM:SS(.fffffffff)?
    frac_ns = 0
    try:
        if "." in time_part:
            hms, frac = time_part.split(".", 1)
            # Keep only digits for fraction; cap to 9 digits (nanoseconds).
            if not frac.isdigit():
                raise ValueError("fractional seconds must be digits")
            if len(frac) > 9:
                frac = frac[:9]  # truncate extra precision
            # Scale to nanoseconds (right-pad with zeros).
            frac_ns = int(frac.ljust(9, "0"))
        else:
            hms = time_part

        h_str, m_str, s_str = hms.split(":")
        hour = int(h_str)
        minute = int(m_str)
        second = int(s_str)
    except Exception as exc:
        raise Exception(
            f"LiteralTimestamp: invalid time part in '{node.value}'. "
            "Expected 'HH:MM:SS' (optionally with '.fffffffff')."
        ) from exc

    if not (0 <= hour <= 23):
        raise Exception(f"LiteralTimestamp: hour out of range in '{node.value}'.")
    if not (0 <= minute <= 59):
        raise Exception(f"LiteralTimestamp: minute out of range in '{node.value}'.")
    if not (0 <= second <= 59):
        raise Exception(f"LiteralTimestamp: second out of range in '{node.value}'.")

    # Build constant struct
    i32 = self._llvm.INT32_TYPE
    ts_ty = ir.LiteralStructType([i32, i32, i32, i32, i32, i32, i32])

    const_ts = ir.Constant(
        ts_ty,
        [
            ir.Constant(i32, year),
            ir.Constant(i32, month),
            ir.Constant(i32, day),
            ir.Constant(i32, hour),
            ir.Constant(i32, minute),
            ir.Constant(i32, second),
            ir.Constant(i32, frac_ns),
        ],
    )

    self.result_stack.append(const_ts)

Adjustments you might want later

  • Timezone support: Add fields for UTC offset minutes (e.g., an extra i32 offset_minutes) and parse Z/±HH:MM.
  • Stricter date validation: Enforce month/day combinations and leap years.
  • Different ABI: If you prefer a scalar representation (e.g., UNIX epoch i64 + i32 nanos), compute it here and return { i64 secs, i32 nanos } instead.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions