-
Notifications
You must be signed in to change notification settings - Fork 13
Open
Labels
Description
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 parseZ/±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.