{"id":30581527,"url":"https://github.com/engineering87/temporalcollections","last_synced_at":"2025-08-29T06:37:15.126Z","repository":{"id":309923488,"uuid":"1036566771","full_name":"engineering87/TemporalCollections","owner":"engineering87","description":"Thread-safe .NET collections with built-in, monotonic timestamps and a unified API for fast range queries, pruning, and temporal analytics","archived":false,"fork":false,"pushed_at":"2025-08-25T12:11:02.000Z","size":210,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-08-25T14:18:42.863Z","etag":null,"topics":["csharp","datastructures","dotnet","dotnetcore","range-queries","thread-safe","time-series","timestamp","utc"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/engineering87.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-08-12T09:06:39.000Z","updated_at":"2025-08-25T12:10:36.000Z","dependencies_parsed_at":"2025-08-14T16:35:25.196Z","dependency_job_id":null,"html_url":"https://github.com/engineering87/TemporalCollections","commit_stats":null,"previous_names":["engineering87/temporalcollections"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/engineering87/TemporalCollections","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/engineering87%2FTemporalCollections","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/engineering87%2FTemporalCollections/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/engineering87%2FTemporalCollections/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/engineering87%2FTemporalCollections/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/engineering87","download_url":"https://codeload.github.com/engineering87/TemporalCollections/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/engineering87%2FTemporalCollections/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":272642142,"owners_count":24968811,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-29T02:00:10.610Z","response_time":87,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["csharp","datastructures","dotnet","dotnetcore","range-queries","thread-safe","time-series","timestamp","utc"],"created_at":"2025-08-29T06:37:14.470Z","updated_at":"2025-08-29T06:37:15.115Z","avatar_url":"https://github.com/engineering87.png","language":"C#","readme":"# TemporalCollections\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![Nuget](https://img.shields.io/nuget/v/TemporalCollections?style=plastic)](https://www.nuget.org/packages/TemporalCollections)\n![NuGet Downloads](https://img.shields.io/nuget/dt/TemporalCollections)\n[![issues - temporalcollections](https://img.shields.io/github/issues/engineering87/TemporalCollections)](https://github.com/engineering87/TemporalCollections/issues)\n[![CodeQL](https://github.com/engineering87/TemporalCollections/actions/workflows/codeql.yml/badge.svg)](https://github.com/engineering87/TemporalCollections/actions/workflows/codeql.yml)\n[![stars - temporalcollections](https://img.shields.io/github/stars/engineering87/TemporalCollections?style=social)](https://github.com/engineering87/TemporalCollections)\n[![Sponsor me](https://img.shields.io/badge/Sponsor-❤-pink)](https://github.com/sponsors/engineering87)\n\n**TemporalCollections** is a high-performance, thread-safe .NET library providing temporal data structures. Each structure associates items with precise insertion timestamps, enabling efficient time-based querying, filtering, and cleanup.\nThis project is ideal for scenarios where you need to store, query, and manage data with temporal semantics, such as event streams, time-windowed analytics, caching with expiry, or temporal state tracking.\n\n## Table of Contents\n- [Overview](#overview)\n- [Core Concept: `TemporalItem\u003cT\u003e`](#core-concept-temporalitemt)\n- [Available Collections](#available-collections)\n- [Usage Guidance](#usage-guidance)\n- [ITimeQueryable\u003cT\u003e Interface](#itimequeryable-interface)\n  - [Key Methods](#key-methods)\n- [Getting Started with TemporalCollections](#-getting-started-with-temporalcollections)\n  - [Installation](#installation)\n  - [Basic usage](#basic-usage)\n  - [Common queries via `ITimeQueryable\u003cT\u003e`](#common-queries-via-itimequeryablet)\n- [Monotonic Timestamp Guarantee](#monotonic-timestamp-guarantee)\n- [Performance Benchmarks](#-performance-benchmarks)\n- [Threading Model \u0026 Big-O Cheatsheet](#threading-model--big-o-cheatsheet)\n- [Notes](#notes)\n- [Contributing](#contributing)\n- [License](#license)\n- [Contact](#contact)\n\n## Overview\n\nTemporalCollections provides multiple thread-safe generic collections where Each item is timestamped at insertion using a strictly monotonic UTC clock (`DateTimeOffset.UtcNow`). These collections expose interfaces for querying items based on their timestamps, removing old or expired entries efficiently, and preserving concurrency guarantees.\n\nThe key design goals are:\n\n- **Temporal semantics:** Items are stored with precise insertion timestamps\n- **Thread safety:** Suitable for concurrent multi-threaded environments\n- **Time-based querying:** Fast retrieval of items within time ranges\n- **Efficient cleanup:** Removing stale or expired data without locking entire collections for long\n\n## Core Concept: `TemporalItem\u003cT\u003e`\n\nAt the heart of all collections lies the `TemporalItem\u003cT\u003e` struct:\n\n- Wraps an immutable value `T` with a timestamp (`DateTimeOffset`) indicating the moment of insertion\n- Guarantees strictly increasing timestamps even under rapid or concurrent creation, using atomic operations\n- Provides a timestamp comparer for sorting and searching\n\n## Available Collections\n| Collection Name             | Description                                                                                          | Thread Safety | Ordering            | Key Features                                           |\n|-----------------------------|--------------------------------------------------------------------------------------------------|---------------|---------------------|-------------------------------------------------------|\n| TemporalQueue\u003cT\u003e            | Thread-safe FIFO queue with timestamped items. Supports enqueue, dequeue, peek, time-range query.| Yes           | FIFO (timestamp)    | Efficient time-range retrieval, remove old items.     |\n| TemporalStack\u003cT\u003e            | Thread-safe LIFO stack with timestamped items. Allows push, pop, peek, and time-based cleanup.   | Yes           | LIFO (timestamp)    | Time-range queries, O(n) removal of old elements.     |\n| TemporalSet\u003cT\u003e              | Thread-safe set of unique items timestamped at insertion. Supports add, contains, remove, queries.| Yes          | Unordered           | Unique items, time-range query, remove old items.     |\n| TemporalSlidingWindowSet\u003cT\u003e | Thread-safe set retaining only items within a sliding time window. Automatically cleans expired items.| Yes        | Unordered           | Sliding window expiration, efficient removal.         |\n| TemporalSortedList\u003cT\u003e       | Thread-safe sorted list of timestamped items. Maintains chronological order, supports binary search.| Yes         | Sorted by timestamp | Efficient range queries, sorted order guaranteed.     |\n| TemporalPriorityQueue\u003cT\u003e    | Thread-safe priority queue with timestamped items. Supports priority-based dequeueing and queries.| Yes           | Priority order      | Priority-based ordering with time queries.             |\n| TemporalIntervalTree\u003cT\u003e     | Thread-safe interval tree for timestamped intervals. Efficient overlap queries and interval removals.| Yes         | Interval-based      | Efficient interval overlap queries and removals.       |\n| TemporalDictionary\u003cTKey, TValue\u003e | Thread-safe dictionary where each key maps to a timestamped value. Supports add/update, remove, and time queries.| Yes | Unordered           | Key-based access with timestamp tracking and queries. |\n| TemporalCircularBuffer\u003cT\u003e   | Thread-safe fixed-size circular buffer with timestamped items. Overwrites oldest items on overflow.| Yes           | FIFO (circular)     | Fixed size, efficient overwriting and time queries.    |\n\n## Usage Guidance\n| Collection Name             | When to Use                                                                                         | When Not to Use                                            |\n|-----------------------------|--------------------------------------------------------------------------------------------------|------------------------------------------------------------|\n| TemporalQueue\u003cT\u003e            | When you need a thread-safe FIFO queue with time-based retrieval and cleanup.                     | If you need priority ordering or random access.            |\n| TemporalStack\u003cT\u003e            | When you want a thread-safe LIFO stack with timestamp tracking and time-range queries.           | If you require fast arbitrary removal or sorting by timestamp. |\n| TemporalSet\u003cT\u003e              | When you need unique timestamped items with efficient membership checks and time-based removal.  | If you require ordering of elements or priority queues.     |\n| TemporalSlidingWindowSet\u003cT\u003e | When you want to automatically retain only recent items within a fixed time window.              | If your window size is highly variable or if you need sorted access. |\n| TemporalSortedList\u003cT\u003e       | When you need a sorted collection by timestamp with efficient range queries.                      | If insertions are very frequent and performance is critical (due to list shifting). |\n| TemporalPriorityQueue\u003cT\u003e    | When priority-based ordering with timestamp tracking is required for dequeueing.                 | If you only need FIFO or LIFO semantics without priorities. |\n| TemporalIntervalTree\u003cT\u003e     | When you need efficient interval overlap queries and interval-based time operations.             | If your data are single points rather than intervals.       |\n| TemporalDictionary\u003cTKey, TValue\u003e | When key-based access combined with timestamp tracking and querying is needed.              | If ordering or range queries by timestamp are required.     |\n| TemporalCircularBuffer\u003cT\u003e   | When you want a fixed-size buffer that overwrites oldest items with timestamp tracking.          | If you need unbounded storage or complex queries.           |\n\n## ITimeQueryable\u003cT\u003e Interface\n\nAll temporal collections implement the `ITimeQueryable\u003cT\u003e` interface, which provides a common set of methods to query and manage items based on their associated timestamps. This interface enables consistent time-based operations across different collection types.\n\n### Key Methods\n\n- **GetInRange(DateTime from, DateTime to)**  \n  Returns an enumerable of temporal items whose timestamps fall within the inclusive range `[from, to]`. This allows filtering the collection by any desired time window.\n\n- **RemoveOlderThan(DateTime cutoff)**  \n  Removes all items with timestamps strictly older than the specified `cutoff` time (`Timestamp \u003c cutoff`). This method is useful for pruning outdated data and maintaining collection size.\n\n- **CountInRange(DateTime from, DateTime to)**  \n  Returns the number of items with timestamps in the inclusive range `[from, to]`. Throws if to \u003c from.\n\n- **GetTimeSpan()**  \n  Returns `latest.Timestamp - earliest.Timestamp`. Returns `TimeSpan.Zero` if the collection is empty or has a single item.\n\n- **Clear()**  \n  Removes all items from the collection.\n\n- **RemoveRange(DateTime from, DateTime to)**  \n  Removes all items with timestamps in the inclusive range `[from, to]`. Throws if `to \u003c from`.\n\n- **GetLatest()**  \n  Returns the most recent item (max timestamp), or null if empty.\n\n- **GetEarliest()**  \n  Returns the oldest item (min timestamp), or null if empty.\n\n- **GetBefore(DateTime time)**  \n  Returns all items with `Timestamp \u003c time` (strictly before), ordered by ascending timestamp.\n\n- **GetAfter(DateTime time)**  \n  Returns all items with `Timestamp \u003e time` (strictly after), ordered by ascending timestamp.\n\n- **CountSince(DateTime from)**  \n  Counts the number of items with timestamp greater than or equal to the specified cutoff.\n\n- **GetNearest(DateTime time)**  \n  Retrieves the item whose timestamp is closest to the specified `time`.\n\nThese methods collectively support efficient and thread-safe temporal queries and cleanups, allowing each collection to manage its items according to their timestamps while exposing a unified API.\n\n## 🚀 Getting Started with TemporalCollections\nThis section shows how to install and use **TemporalCollections** in your .NET projects with simple examples.\n\n### Installation\n```bash\ndotnet add package TemporalCollections\n```\n\n### Basic usage\n**TemporalQueue\u003cT\u003e**\n\n```csharp\nusing System;\nusing System.Linq;\nusing TemporalCollections.Collections;\n\nvar queue = new TemporalQueue\u003cstring\u003e();\n\n// Enqueue items (timestamps are assigned automatically)\nqueue.Enqueue(\"event-1\");\nqueue.Enqueue(\"event-2\");\n\n// Peek oldest (does not remove)\nvar oldest = queue.Peek();\nConsole.WriteLine($\"Oldest: {oldest.Value} @ {oldest.Timestamp}\");\n\n// Dequeue oldest (removes)\nvar dequeued = queue.Dequeue();\nConsole.WriteLine($\"Dequeued: {dequeued.Value} @ {dequeued.Timestamp}\");\n\n// Query by time range (inclusive)\nvar from = DateTime.UtcNow.AddMinutes(-5);\nvar to   = DateTime.UtcNow;\nvar inRange = queue.GetInRange(from, to);\nforeach (var item in inRange)\n{\n    Console.WriteLine($\"In range: {item.Value} @ {item.Timestamp}\");\n}\n```\n**TemporalSet\u003cT\u003e**\n\n```csharp\nusing System;\nusing TemporalCollections.Collections;\n\nvar set = new TemporalSet\u003cint\u003e();\n\nset.Add(1);\nset.Add(2);\nset.Add(2);\n\nConsole.WriteLine(set.Contains(1));\n\n// Remove older than a cutoff\nvar cutoff = DateTime.UtcNow.AddMinutes(-10);\nset.RemoveOlderThan(cutoff);\n\n// Snapshot of all items ordered by timestamp\nvar items = set.GetItems();\n```\n\n**TemporalDictionary\u003cTKey, TValue\u003e**\n\n```csharp\nusing System;\nusing System.Linq;\nusing TemporalCollections.Collections;\n\nvar dict = new TemporalDictionary\u003cstring, string\u003e();\n\ndict.Add(\"user:1\", \"login\");\ndict.Add(\"user:2\", \"logout\");\ndict.Add(\"user:1\", \"refresh\");\n\n// Range query across all keys\nvar from = DateTime.UtcNow.AddMinutes(-1);\nvar to   = DateTime.UtcNow.AddMinutes(1);\nvar all = dict.GetInRange(from, to);\n\n// Range query for a specific key\nvar user1 = dict.GetInRange(\"user:1\", from, to);\n\n// Compute span covered by all events\nvar span = dict.GetTimeSpan();\nConsole.WriteLine($\"Span: {span}\");\n\n// Remove a time window across all keys\ndict.RemoveRange(from, to);\n```\n\n**TemporalStack\u003cT\u003e**\n\n```csharp\nusing System;\nusing System.Linq;\nusing TemporalCollections.Collections;\n\nvar stack = new TemporalStack\u003cstring\u003e();\n\n// Push (timestamps assigned automatically, monotonic UTC)\nstack.Push(\"first\");\nstack.Push(\"second\");\n\n// Peek last pushed (does not remove)\nvar top = stack.Peek();\nConsole.WriteLine($\"Top: {top.Value} @ {top.Timestamp}\");\n\n// Pop last pushed (removes)\nvar popped = stack.Pop();\nConsole.WriteLine($\"Popped: {popped.Value}\");\n\n// Time range query (inclusive)\nvar from = DateTime.UtcNow.AddMinutes(-5);\nvar to   = DateTime.UtcNow;\nvar items = stack.GetInRange(from, to).OrderBy(i =\u003e i.Timestamp);\n\n// Remove older than cutoff\nvar cutoff = DateTime.UtcNow.AddMinutes(-10);\nstack.RemoveOlderThan(cutoff);\n```\n\n**TemporalSlidingWindowSet\u003cT\u003e**\n\n```csharp\nusing System;\nusing System.Linq;\nusing TemporalCollections.Collections;\n\nvar window = TimeSpan.FromMinutes(10);\nvar swSet = new TemporalSlidingWindowSet\u003cstring\u003e(window);\n\n// Add unique items (insertion timestamp recorded)\nswSet.Add(\"A\");\nswSet.Add(\"B\");\n\n// Periodically expire items older than the window\nswSet.RemoveExpired();\n\n// Snapshot (ordered by timestamp)\nvar snapshot = swSet.GetItems().ToList();\n\n// Query by time range\nvar from = DateTime.UtcNow.AddMinutes(-5);\nvar to   = DateTime.UtcNow;\nvar inRange = swSet.GetInRange(from, to);\n\n// Manual cleanup by cutoff (if needed)\nswSet.RemoveOlderThan(DateTime.UtcNow.AddMinutes(-30));\n```\n\n**TemporalSortedList\u003cT\u003e**\n\n```csharp\nusing System;\nusing System.Linq;\nusing TemporalCollections.Collections;\n\nvar list = new TemporalSortedList\u003cint\u003e();\n\n// Add items (kept sorted by timestamp internally)\nlist.Add(10);\nlist.Add(20);\nlist.Add(30);\n\n// Fast range query via binary search (inclusive)\nvar from = DateTime.UtcNow.AddSeconds(-30);\nvar to   = DateTime.UtcNow;\nvar inRange = list.GetInRange(from, to);\n\n// Before / After helpers\nvar before = list.GetBefore(DateTime.UtcNow);\nvar after  = list.GetAfter(DateTime.UtcNow.AddSeconds(-5));\n\n// Housekeeping\nlist.RemoveOlderThan(DateTime.UtcNow.AddMinutes(-1));\nConsole.WriteLine($\"Span: {list.GetTimeSpan()}\");\n```\n\n**TemporalPriorityQueue\u003cTPriority, TValue\u003e**\n\n```csharp\nusing System;\nusing System.Linq;\nusing TemporalCollections.Collections;\n\nvar pq = new TemporalPriorityQueue\u003cint, string\u003e();\n\n// Enqueue with explicit priority (lower number = higher priority)\npq.Enqueue(\"high\", priority: 1);\npq.Enqueue(\"low\",  priority: 10);\n\n// TryPeek (does not remove)\nif (pq.TryPeek(out var next))\n{\n    Console.WriteLine($\"Peek: {next}\");\n}\n\n// TryDequeue (removes highest-priority; stable by insertion time)\nwhile (pq.TryDequeue(out var val))\n{\n    Console.WriteLine($\"Dequeued: {val}\");\n}\n\n// Time-based queries are also available\nvar from = DateTime.UtcNow.AddMinutes(-5);\nvar to   = DateTime.UtcNow;\nvar items = pq.GetInRange(from, to);\n\nConsole.WriteLine($\"Count in range: {pq.CountInRange(from, to)}\");\n```\n\n**TemporalCircularBuffer\u003cT\u003e**\n\n```csharp\nusing System;\nusing System.Linq;\nusing TemporalCollections.Collections;\n\n// Fixed-capacity ring buffer; overwrites oldest when full\nvar buf = new TemporalCircularBuffer\u003cstring\u003e(capacity: 3);\n\nbuf.Add(\"A\");\nbuf.Add(\"B\");\nbuf.Add(\"C\");\nbuf.Add(\"D\"); // Overwrites \"A\"\n\n// Snapshot (oldest -\u003e newest)\nvar snapshot = buf.GetSnapshot();\nforeach (var it in snapshot)\n{\n    Console.WriteLine($\"{it.Value} @ {it.Timestamp}\");\n}\n\n// Range queries\nvar from = DateTime.UtcNow.AddMinutes(-5);\nvar to   = DateTime.UtcNow;\nvar inRange = buf.GetInRange(from, to);\n\n// Remove a time window\nbuf.RemoveRange(from, to);\n\n// Cleanup by cutoff (keeps \u003e= cutoff)\nbuf.RemoveOlderThan(DateTime.UtcNow.AddMinutes(-1));\n```\n\n**TemporalIntervalTree\u003cT\u003e**\n\n```csharp\nusing System;\nusing System.Linq;\nusing TemporalCollections.Collections;\n\nvar tree = new TemporalIntervalTree\u003cstring\u003e();\n\nvar now = DateTime.UtcNow;\ntree.Insert(now, now.AddMinutes(10), \"session:A\");\ntree.Insert(now.AddMinutes(5), now.AddMinutes(15), \"session:B\");\n\n// Overlap query (values only)\nvar overlapValues = tree.Query(now.AddMinutes(7), now.AddMinutes(12));\n// Overlap query (with timestamps = interval starts)\nvar overlapItems  = tree.GetInRange(now.AddMinutes(7), now.AddMinutes(12));\n\nConsole.WriteLine($\"Overlaps: {string.Join(\", \", overlapValues)}\");\n\n// Remove intervals that ended before a cutoff\ntree.RemoveOlderThan(now.AddMinutes(9));\n```\n\n### Common queries via `ITimeQueryable\u003cT\u003e`\n\n```csharp\nvar latest   = collection.GetLatest();   // most recent item or null\nvar earliest = collection.GetEarliest(); // oldest item or null\n\nvar before = collection.GetBefore(DateTime.UtcNow); // strictly \u003c\nvar after  = collection.GetAfter(DateTime.UtcNow);  // strictly \u003e\n\nvar count = collection.CountInRange(DateTime.UtcNow.AddSeconds(-30), DateTime.UtcNow);\n\nvar span = collection.GetTimeSpan(); // latest.Timestamp - earliest.Timestamp (or TimeSpan.Zero)\n```\n\n## Monotonic Timestamp Guarantee\nA key feature of the temporal collections is the guarantee that timestamps assigned to items are strictly monotonically increasing, even when multiple items are created concurrently or in rapid succession.\n\nThis is achieved through the `TemporalItem\u003cT\u003e` record, which uses an atomic compare-and-swap operation on a static internal timestamp counter. When creating a new temporal item, the current UTC timestamp in ticks (`DateTimeOffset.UtcTicks`) is retrieved and compared against the last assigned timestamp:\n\n- If the current timestamp is greater than the last one, it is used as-is.\n- If the current timestamp is less than or equal to the last assigned timestamp (e.g., due to rapid creation or clock precision limits), the timestamp is artificially incremented by one tick.\n\nThis approach ensures:\n\n- **Uniqueness:** No two items share the exact same timestamp.\n- **Strict ordering:** Timestamps always increase in time order.\n- **Thread safety:** The mechanism works correctly across multiple threads without race conditions.\n\nBy enforcing this monotonic timestamp ordering, the temporal collections can rely on consistent time-based queries and maintain correct chronological order of items.\n\n## 📈 Performance Benchmarks\nWe provide detailed performance measurements for all temporal data structures, including insertion, range queries, and removal operations.  \nThe full benchmark results are available here: [docs/benchmarks/benchmarks.md](docs/benchmarks/benchmarks.md)\nThese benchmarks help compare trade-offs between different collections and guide future optimizations.\n\n## Threading Model \u0026 Big-O Cheatsheet\n\nAll collections are thread-safe. Locking granularity and common operations (amortized):\n\n| Collection | Locking | Add/Push | Range Query | RemoveOlderThan |\n|---|---|---:|---:|---:|\n| TemporalQueue | single lock around a queue snapshot | O(1) | O(n) | O(k) from head |\n| TemporalStack | single lock; drain \u0026 rebuild for window ops | O(1) | O(n) | O(n) |\n| TemporalSet | lock-free dict + per-bucket ops | O(1) avg | O(n) | O(n) |\n| TemporalSortedList | single lock; binary search for ranges | O(n) insert | **O(log n + m)** | O(k) |\n| TemporalPriorityQueue | single lock; `SortedSet` by (priority,timestamp) | O(log n) | O(n) | O(n) |\n| TemporalIntervalTree | single lock; interval overlap pruning | O(log n) avg | **O(log n + m)** | O(n) |\n| TemporalDictionary | concurrent dict + per-list lock | O(1) avg | O(n) | O(n) |\n| TemporalCircularBuffer | single lock; ring overwrite | O(1) | O(n) | O(n) |\n\n`n` = items, `m` = matches, `k` = removed.\n\n## Notes\n- **Deterministic ordering**: query results are returned in ascending timestamp order.\n- **Snapshot semantics**: methods that return enumerables/lists provide a stable snapshot at call time.\n- **Thread-safety**: all operations are designed to be thread-safe per collection.\n- **Intervals**: for interval-based collections, the Timestamp used by this interface refers to the interval start.\n\n⚠️ **Since v1.1.0, internal timestamp storage has been migrated from DateTime to DateTimeOffset (UTC). Public APIs remain DateTime for backward compatibility, but internal semantics are now strictly UTC-aware.**\n\n### Contributing\nThank you for considering to help out with the source code!\nIf you'd like to contribute, please fork, fix, commit and send a pull request for the maintainers to review and merge into the main code base.\n\n**Getting started with Git and GitHub**\n\n * [Setting up Git](https://docs.github.com/en/get-started/getting-started-with-git/set-up-git)\n * [Fork the repository](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo)\n * [Open an issue](https://github.com/engineering87/TemporalCollections/issues) if you encounter a bug or have a suggestion for improvements/features\n\n### License\nTemporalCollections source code is available under MIT License, see license in the source.\n\n### Contact\nPlease contact at francesco.delre[at]protonmail.com for any details.\n","funding_links":["https://github.com/sponsors/engineering87"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fengineering87%2Ftemporalcollections","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fengineering87%2Ftemporalcollections","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fengineering87%2Ftemporalcollections/lists"}