Metadata-Version: 2.4
Name: lsb32
Version: 0.2.0
Summary: Compact, sortable UTC timestamp identifiers for distributed applications
License-Expression: LGPL-3.0-or-later
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Typing :: Typed
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
License-File: COPYING
Requires-Dist: wasmtime<47,>=46
Dynamic: license-file

# LSB32 for Python

`LSB32` converts timezone-aware Python `datetime` values into compact,
sortable Crockford Base32 identifiers through a bundled WebAssembly module.

Seconds remain the default and use the original seven-character format.
Milliseconds, microseconds, and nanoseconds use 9, 11, and 13 characters.

## Installation

```console
pip install lsb32
```

The package supports Python 3.9 and newer and depends on the official
`wasmtime` runtime.

## Quick Start

```python
from datetime import datetime, timezone
from LSB32 import decode, encode

original = datetime(2026, 1, 1, tzinfo=timezone.utc)
value = encode(original)

assert value == "D110000"
assert decode(value) == original
```

## Optional Precision

Select precision with the `Precision` enum or its string value. Digits finer
than the selected precision are truncated.

```python
from datetime import datetime, timezone
from LSB32 import Precision, decode, encode

original = datetime(2026, 1, 1, 0, 0, 0, 123456, timezone.utc)

assert encode(original, precision=Precision.MILLISECONDS) == "D1100003V"
assert encode(original, precision=Precision.MICROSECONDS) == "D1100003VE8"
assert encode(original, precision=Precision.NANOSECONDS) == "D1100003VE800"
```

Python `datetime` stores microseconds. For arbitrary nanoseconds, pass the
nanosecond within the second explicitly and decode with `decode_precise`:

```python
from LSB32 import Precision, decode_precise, encode

value = encode(
    datetime(2026, 1, 1, tzinfo=timezone.utc),
    precision=Precision.NANOSECONDS,
    nanosecond=123456789,
)
restored = decode_precise(value)

assert value == "D1100003VE8RN"
assert restored.nanosecond == 123456789
assert restored.value.microsecond == 123456
```

`decode` raises `LSB32.Error` instead of silently discarding nanoseconds that
Python `datetime` cannot represent.

## Custom Epoch

The default epoch covers 2000 through 2063. Pass the same custom epoch to
producers and consumers:

```python
original = datetime(2089, 12, 31, 23, 59, 59, tzinfo=timezone.utc)
value = encode(original, epoch=2026)
assert decode(value, epoch=2026) == original
```

## API

| API | Result |
| --- | --- |
| `encode(value, epoch=2000, precision="seconds", nanosecond=None)` | Canonical `str` |
| `decode(value, epoch=2000)` | UTC `datetime` when exactly representable |
| `decode_precise(value, epoch=2000)` | `PreciseDateTime` with exact nanoseconds |
| `now(epoch=2000, precision="seconds")` | Current time at the selected precision |

`PreciseDateTime.value` is the closest exactly representable Python
`datetime`; `PreciseDateTime.nanosecond` retains the complete value within the
second, and `PreciseDateTime.precision` records the wire precision.

## Errors

`Error` extends `ValueError` and exposes the WASM status as `code`. Strict
decoding rejects unsupported lengths, lowercase or ambiguous Crockford
characters, non-zero prefix padding, invalid calendar fields, and subsecond
groups above 999.

## Development

From `wrappers/python`:

```console
python -m pip install -e .
python -m unittest discover -s tests
python -m build
python -m twine check dist/*
```

Regenerate synchronized WASM and WAT artifacts from the repository root with
`node wasm.mjs`.

## License

This package is licensed under the GNU Lesser General Public License, either
version 3 or any later version. See [LICENSE](LICENSE) and [COPYING](COPYING).
