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.
mops add tidTo set up MOPS package manager, follow the instructions from the MOPS Site
import TID "mo:tid"// 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);
};
};// 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));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// 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);
};
};// TID structure
public type TID = {
timestamp : Nat; // Microseconds since UNIX epoch (max 53 bits)
clockId : Nat; // Clock identifier (max 10 bits: 0-1023)
};// 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 class
public class Generator() {
// Generate the next TID with current timestamp and incremented clock ID
public func next() : TID;
}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)
- 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
- 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
- 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
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
core: Core Motoko librariesbase-x-encoder: Base32 encoding/decodingxtended-numbers: Extended number utilitiesbuffer: Buffer utilities
This project is licensed under the MIT License - see the LICENSE file for details.