Skip to content

A Motoko library for handling TIDs (Time Identifiers) for atproto

License

Notifications You must be signed in to change notification settings

edjCase/motoko_tid

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

9 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Motoko TID Library

MOPS 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

mops add tid

To set up MOPS package manager, follow the instructions from the MOPS Site

Quick Start

Import

import TID "mo:tid"

Example 1: Creating and Converting TIDs

// 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

// 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

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

// 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

// 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

// 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<TID, Text>;

// 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<TID, Text>;

// 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

// 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 file for details.

About

A Motoko library for handling TIDs (Time Identifiers) for atproto

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages