https://github.com/edjcase/motoko_tid
A Motoko library for handling TIDs (Time Identifiers) for atproto
https://github.com/edjcase/motoko_tid
Last synced: 5 months ago
JSON representation
A Motoko library for handling TIDs (Time Identifiers) for atproto
- Host: GitHub
- URL: https://github.com/edjcase/motoko_tid
- Owner: edjCase
- License: mit
- Created: 2025-07-02T21:04:27.000Z (about 1 year ago)
- Default Branch: main
- Last Pushed: 2025-07-02T22:50:12.000Z (about 1 year ago)
- Last Synced: 2025-07-02T23:27:29.141Z (about 1 year ago)
- Language: Motoko
- Size: 5.86 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Motoko TID Library
[](https://mops.one/tid)
[](https://github.com/edjcase/motoko_tid/blob/main/LICENSE)
A Motoko library for working with Time Identifiers (TIDs) - compact, sortable identifiers based on timestamps with microsecond precision and clock identifiers. TIDs are designed to be lexicographically sortable and maintain chronological ordering.
## Package
### MOPS
```bash
mops add tid
```
To set up MOPS package manager, follow the instructions from the [MOPS Site](https://mops.one)
## Quick Start
### Import
```motoko
import TID "mo:tid"
```
### Example 1: Creating and Converting TIDs
```motoko
// Create a TID manually
let tid : TID.TID = {
timestamp = 1640995200000000; // Microseconds since UNIX epoch
clockId = 42; // Clock identifier (0-1023)
};
// Convert to text representation
let tidText = TID.toText(tid);
Debug.print("TID: " # tidText); // "3jzfcijpj2z2a" (example)
// Parse TID from text
switch (TID.fromText(tidText)) {
case (#ok(parsedTid)) {
Debug.print("Parsed successfully: " # debug_show(parsedTid));
};
case (#err(error)) {
Debug.print("Parse error: " # error);
};
};
```
### Example 2: Using the TID Generator
```motoko
// Create a generator
let generator = TID.Generator();
// Generate sequential TIDs
let tid1 = generator.next(); // clockId = 0
let tid2 = generator.next(); // clockId = 1
let tid3 = generator.next(); // clockId = 2
Debug.print("TID 1: " # TID.toText(tid1));
Debug.print("TID 2: " # TID.toText(tid2));
Debug.print("TID 3: " # TID.toText(tid3));
```
### Example 3: Comparing and Sorting TIDs
```motoko
let tid1 = { timestamp = 1000; clockId = 1 };
let tid2 = { timestamp = 2000; clockId = 2 };
let tid3 = { timestamp = 2000; clockId = 3 }; // Same timestamp, different clock
// Numeric comparison
switch (TID.compare(tid1, tid2)) {
case (#less) Debug.print("tid1 < tid2");
case (#equal) Debug.print("tid1 == tid2");
case (#greater) Debug.print("tid1 > tid2");
};
// Equality check
let isEqual = TID.equal(tid1, tid2); // false
// String sorting matches numeric sorting
let text1 = TID.toText(tid1);
let text2 = TID.toText(tid2);
assert(text1 < text2); // Lexicographic ordering matches temporal ordering
```
### Example 4: Working with Nat64 Representation
```motoko
// Convert TID to 64-bit integer
let tid = { timestamp = 1640995200000000; clockId = 42 };
let nat64Value = TID.toNat64(tid);
// Convert back from 64-bit integer
switch (TID.fromNat64(nat64Value)) {
case (#ok(reconstructedTid)) {
assert(TID.equal(tid, reconstructedTid)); // Round-trip successful
};
case (#err(error)) {
Debug.print("Conversion error: " # error);
};
};
```
## API Reference
### Types
```motoko
// TID structure
public type TID = {
timestamp : Nat; // Microseconds since UNIX epoch (max 53 bits)
clockId : Nat; // Clock identifier (max 10 bits: 0-1023)
};
```
### Core Functions
```motoko
// Convert TID to base32-sortable text representation
public func toText(tid : TID) : Text;
// Parse TID from base32-sortable text
public func fromText(text : Text) : Result.Result;
// Convert TID to 64-bit integer representation
public func toNat64(tid : TID) : Nat64;
// Convert 64-bit integer to TID
public func fromNat64(value : Nat64) : Result.Result;
// Compare two TIDs for sorting
public func compare(tid1 : TID, tid2 : TID) : Order.Order;
// Check if two TIDs are equal
public func equal(tid1 : TID, tid2 : TID) : Bool;
```
### TID Generator
```motoko
// TID generator class
public class Generator() {
// Generate the next TID with current timestamp and incremented clock ID
public func next() : TID;
}
```
## TID Format
### Structure
A TID is composed of:
- **1 bit**: Always 0 (reserved for future use)
- **53 bits**: Timestamp in microseconds since UNIX epoch
- **10 bits**: Clock identifier (0-1023)
### Text Representation
- **Length**: Exactly 13 characters
- **Encoding**: Base32-sortable (using AT Protocol's sortable alphabet)
- **Character set**: `234567abcdefghijklmnopqrstuvwxyz` (no 0, 1, or uppercase)
- **Ordering**: Lexicographic string sorting matches temporal ordering
### Constraints
- **Maximum timestamp**: 2^53 - 1 (9,007,199,254,740,991) for JavaScript compatibility
- **Maximum clock ID**: 1,023 (10 bits)
- **Top bit**: Must always be 0
## Use Cases
- **Distributed systems**: Sortable identifiers across multiple nodes
- **Database keys**: Time-ordered primary keys with collision resistance
- **Event logging**: Chronologically sortable event identifiers
- **AT Protocol**: Compatible with ATProto TID specification
## Error Handling
The library provides detailed error messages for invalid inputs:
- Invalid TID length (must be exactly 13 characters)
- Invalid characters (only base32-sortable alphabet allowed)
- Invalid first character (high bit would be set)
- Timestamp or clock ID exceeding maximum values
- Invalid byte representations
## Dependencies
- `core`: Core Motoko libraries
- `base-x-encoder`: Base32 encoding/decoding
- `xtended-numbers`: Extended number utilities
- `buffer`: Buffer utilities
## License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.