# How sdata's Timestamp Module Handles Timezone-Aware datetime Objects and Conversions

> Discover how sdata's timestamp module handles timezone-aware datetimes and conversions. Learn about custom tzinfo classes and pytz-powered conversion functions.

- Repository: [lepy/sdata](https://github.com/lepy/sdata)
- Tags: deep-dive
- Published: 2026-03-06

---

**The sdata.timestamp module parses ISO-8601 strings into timezone-aware datetime objects using custom UTC and FixedOffset tzinfo classes, then converts them to UTC or local system time via pytz-powered helper functions.**

The sdata.timestamp module in the lepy/sdata repository provides a lightweight, self-contained solution for parsing ISO-8601 timestamps and managing timezone-aware datetime conversions. This module ensures that all datetime objects carry explicit timezone information, enabling reliable transformations between UTC and local system timezones without ambiguity.

## Core Timezone Components in sdata/timestamp.py

The module implements several key components to handle timezone-aware datetime objects. Each component is designed to work together to parse, store, and convert timestamps accurately.

### UTC and FixedOffset Classes

At **lines 99–101**, the module defines `UTC` as either a `datetime.timezone` instance (Python ≥ 3.2) or a custom `Utc` tzinfo class (Python < 3.2) representing the UTC zone. This provides a consistent UTC reference across Python versions.

For arbitrary offsets, the `FixedOffset` factory at **lines 103–108** (or the full class implementation at **lines 134–168**) creates timezone objects representing specific `±HH:MM` offsets parsed from ISO-8601 strings. These classes ensure that every parsed datetime carries explicit timezone metadata.

### Parsing Functions: parse_timezone and parse_date

The **`parse_timezone`** function at **lines 88–107** handles the timezone component of ISO-8601 strings. It recognizes three patterns:
- `"Z"` → returns the `UTC` object
- `±HH:MM` or `±HH` → returns a `FixedOffset` instance with the appropriate minute offset
- Missing timezone → returns the `default_timezone` parameter (defaults to `UTC`)

The **`parse_date`** function at **lines 110–150** orchestrates the full parsing workflow. It uses the `ISO8601_REGEX` pattern to extract date, time, and timezone components, converts fractional seconds to microseconds using `Decimal`, and constructs a fully timezone-aware `datetime.datetime` object.

### Conversion Utilities

For timezone conversions, the module provides two critical helpers:

- **`get_utc_timestamp`** (**lines 62–66**): Accepts any datetime (aware or naive) and returns a UTC-aware datetime using `dt.astimezone(pytz.UTC)`
- **`get_local_timestamp`** (**lines 69–73**): Determines the system's local timezone name via `local_tzname()`, loads it using `pytz.timezone()`, and converts the datetime to local time via `astimezone()`

### The TimeStamp Wrapper Class

The **`TimeStamp`** class at **lines 75–104** provides a convenient object-oriented interface. It stores an internal timezone-aware datetime (`self._datetime`) and exposes two key properties:
- `utc`: Returns the ISO-8601 string representation in UTC
- `local`: Returns the ISO-8601 string representation in the system's local timezone

## Step-by-Step Workflow for Timezone Handling

Understanding how sdata processes timestamps requires following the data flow from string parsing to timezone conversion.

### 1. ISO-8601 String Parsing

When `parse_date()` receives a string like `"2023-05-12T14:30:00+02:00"`, the regex extractor splits the input into year, month, day, hour, minute, second, fractional second, and timezone components. The numeric parts are safely cast to integers using `to_int()`, while fractional seconds are processed via `Decimal` for precision.

### 2. Timezone Detection and Attachment

The `parse_timezone()` helper analyzes the timezone component:
- **Z suffix**: Mapped to the global `UTC` object
- **Offset notation**: Used to instantiate `FixedOffset` with the correct minute count
- **No timezone**: Falls back to the `default_timezone` argument

The resulting `tzinfo` object is passed to the `datetime.datetime` constructor, ensuring the object is **timezone-aware** immediately upon creation.

### 3. UTC Normalization

Calling `get_utc_timestamp(dt)` invokes `astimezone(pytz.UTC)`, which handles the arithmetic to convert any offset to UTC (+00:00). This works for both initially aware datetimes and those made aware during parsing.

### 4. Local System Conversion

The `local_tzname()` helper determines the system's timezone offset using `time.timezone` and `time.altzone`, constructing a name like `"Etc/GMT+2"`. The `get_local_timestamp()` function then loads this via `pytz.timezone()` and performs the conversion, returning a datetime aware of the local system offset.

## Working with sdata.timestamp: Code Examples

The following examples demonstrate practical usage of the module's timezone handling capabilities.

### Parsing Timezone-Aware ISO-8601 Strings

```python
from sdata.timestamp import parse_date, get_utc_timestamp, get_local_timestamp, TimeStamp

# Parse a string with explicit timezone offset

dt = parse_date("2023-05-12T14:30:00+02:00")
print(dt)                     # 2023-05-12 14:30:00+02:00

print(dt.tzinfo)              # tzoffset(None, 7200)

# Convert to UTC

utc_dt = get_utc_timestamp(dt)
print(utc_dt.isoformat())     # 2023-05-12T12:30:00+00:00

# Convert to local system time

local_dt = get_local_timestamp(dt)
print(local_dt.isoformat())   # 2023-05-12T08:30:00-04:00 (example)

```

### Handling Naive Strings with Default Timezone

```python

# When no timezone is provided, defaults to UTC

naive_dt = parse_date("2023-05-12T14:30:00")
print(naive_dt.isoformat())   # 2023-05-12T14:30:00+00:00

```

### Using the TimeStamp Wrapper

```python

# Object-oriented approach with automatic conversions

ts = TimeStamp("2023-05-12T14:30:00+02:00")
print(ts.utc)                 # 2023-05-12T12:30:00+00:00

print(ts.local)               # System-dependent local representation

```

## Summary

- **sdata/timestamp.py** implements a complete ISO-8601 parser that creates timezone-aware datetime objects using custom `UTC` and `FixedOffset` classes
- The **`parse_date()`** function at lines 110–150 uses regex extraction and `Decimal` precision to handle fractional seconds and timezone offsets
- **Timezone conversion** relies on `pytz` via `get_utc_timestamp()` (lines 62–66) and `get_local_timestamp()` (lines 69–73) using `astimezone()`
- The **`TimeStamp`** wrapper class (lines 75–104) provides convenient `utc` and `local` properties for accessing standardized string representations
- Naive strings without timezone information default to UTC, ensuring all internal datetime objects remain timezone-aware

## Frequently Asked Questions

### How does sdata handle ISO-8601 strings without timezone information?

When the input string lacks timezone notation, the `parse_date()` function uses the `default_timezone` parameter, which defaults to `UTC` as implemented at lines 88–107 in the `parse_timezone()` function. This ensures consistent behavior and prevents naive datetime objects from entering the system.

### What is the difference between UTC and FixedOffset in the sdata module?

**UTC** (lines 99–101) represents the specific Coordinated Universal Time zone with zero offset, implemented as either `datetime.timezone.utc` (Python ≥ 3.2) or a custom `Utc` class. **FixedOffset** (lines 103–108 and 134–168) is a factory and class for creating timezone objects representing arbitrary `±HH:MM` offsets parsed from ISO-8601 strings, enabling support for any valid timezone offset.

### How does sdata convert datetime objects to the local system timezone?

The conversion process uses `local_tzname()` to determine the system offset via `time.timezone` and `time.altzone`, constructing a timezone name like `"Etc/GMT+5"`. The `get_local_timestamp()` function (lines 69–73) then loads this timezone using `pytz.timezone()` and calls `astimezone()` to perform the conversion, returning a datetime object aware of the local system offset.

### Does sdata support fractional seconds in ISO-8601 timestamps?

Yes, the `parse_date()` function handles fractional seconds by extracting the sub-second component with regex and converting it to microseconds using Python's `Decimal` class for precision. This occurs within the parsing logic at lines 110–150, ensuring accurate microsecond representation in the resulting timezone-aware datetime object.