diff --git a/src/MaksIT.Core.Tests/Comb/CombGuidGeneratorTests.cs b/src/MaksIT.Core.Tests/Comb/CombGuidGeneratorTests.cs
new file mode 100644
index 0000000..e18fc8e
--- /dev/null
+++ b/src/MaksIT.Core.Tests/Comb/CombGuidGeneratorTests.cs
@@ -0,0 +1,70 @@
+using MaksIT.Core.Comb;
+
+
+namespace MaksIT.Core.Tests.Comb;
+
+public class CombGuidGeneratorTests {
+ [Theory]
+ [InlineData(CombGuidType.SqlServer)]
+ [InlineData(CombGuidType.PostgreSql)]
+ public void CreateCombGuid_WithBaseGuidAndTimestamp_EmbedsTimestampCorrectly(CombGuidType type) {
+ // Arrange
+ var baseGuid = Guid.NewGuid();
+ var timestamp = DateTime.UtcNow;
+
+ // Act
+ var combGuid = CombGuidGenerator.CreateCombGuid(baseGuid, timestamp, type);
+ var extractedTimestamp = CombGuidGenerator.ExtractTimestamp(combGuid, type);
+
+ // Assert
+ Assert.Equal(timestamp, extractedTimestamp);
+ }
+
+ [Theory]
+ [InlineData(CombGuidType.SqlServer)]
+ [InlineData(CombGuidType.PostgreSql)]
+ public void CreateCombGuid_WithTimestampOnly_GeneratesValidCombGuid(CombGuidType type) {
+ // Arrange
+ var timestamp = DateTime.UtcNow;
+
+ // Act
+ var combGuid = CombGuidGenerator.CreateCombGuid(timestamp, type);
+ var extractedTimestamp = CombGuidGenerator.ExtractTimestamp(combGuid, type);
+
+ // Assert
+ Assert.Equal(timestamp, extractedTimestamp);
+ }
+
+ [Theory]
+ [InlineData(CombGuidType.SqlServer)]
+ [InlineData(CombGuidType.PostgreSql)]
+ public void CreateCombGuid_WithBaseGuidOnly_UsesCurrentUtcTimestamp(CombGuidType type) {
+ // Arrange
+ var baseGuid = Guid.NewGuid();
+ var beforeCreation = DateTime.UtcNow;
+
+ // Act
+ var combGuid = CombGuidGenerator.CreateCombGuid(baseGuid, type);
+ var extractedTimestamp = CombGuidGenerator.ExtractTimestamp(combGuid, type);
+
+ // Assert
+ Assert.True(extractedTimestamp >= beforeCreation);
+ Assert.True(extractedTimestamp <= DateTime.UtcNow);
+ }
+
+ [Theory]
+ [InlineData(CombGuidType.SqlServer)]
+ [InlineData(CombGuidType.PostgreSql)]
+ public void ExtractTimestamp_ReturnsCorrectTimestamp(CombGuidType type) {
+ // Arrange
+ var baseGuid = Guid.NewGuid();
+ var timestamp = DateTime.UtcNow;
+ var combGuid = CombGuidGenerator.CreateCombGuid(baseGuid, timestamp, type);
+
+ // Act
+ var extractedTimestamp = CombGuidGenerator.ExtractTimestamp(combGuid, type);
+
+ // Assert
+ Assert.Equal(timestamp, extractedTimestamp);
+ }
+}
\ No newline at end of file
diff --git a/src/MaksIT.Core/Comb/CombGuidGenerator.cs b/src/MaksIT.Core/Comb/CombGuidGenerator.cs
new file mode 100644
index 0000000..5bacbee
--- /dev/null
+++ b/src/MaksIT.Core/Comb/CombGuidGenerator.cs
@@ -0,0 +1,149 @@
+using System.Buffers.Binary;
+
+
+namespace MaksIT.Core.Comb;
+
+///
+/// Specifies the layout strategy used to embed a timestamp in a COMB GUID.
+///
+public enum CombGuidType {
+ ///
+ /// COMB GUID format compatible with SQL Server (timestamp in bytes 8–15).
+ ///
+ SqlServer,
+
+ ///
+ /// COMB GUID format compatible with PostgreSQL (timestamp in bytes 0–7).
+ ///
+ PostgreSql
+}
+
+///
+/// Provides methods to generate and extract COMB GUIDs with embedded timestamps.
+/// COMB GUIDs improve index locality by combining randomness with a sortable timestamp.
+///
+public static class CombGuidGenerator {
+ private const int TimestampByteLength = 8;
+ private static readonly DateTime UnixEpoch = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
+
+ ///
+ /// Generates a COMB GUID using the specified base GUID, timestamp, and format type.
+ ///
+ /// The base GUID to embed the timestamp into.
+ /// The UTC timestamp to embed in the GUID.
+ /// The COMB GUID format to use.
+ /// The generated COMB GUID.
+ public static Guid CreateCombGuid(Guid baseGuid, DateTime timestamp, CombGuidType type) {
+ return type switch {
+ CombGuidType.SqlServer => CreateSqlServerCombGuid(baseGuid, timestamp),
+ CombGuidType.PostgreSql => CreatePostgreSqlCombGuid(baseGuid, timestamp),
+ _ => throw new ArgumentOutOfRangeException(nameof(type), "Unsupported COMB GUID type.")
+ };
+ }
+
+ ///
+ /// Generates a COMB GUID using a random GUID and a specified UTC timestamp.
+ ///
+ /// The UTC timestamp to embed in the GUID.
+ /// The COMB GUID format to use.
+ /// The generated COMB GUID.
+ public static Guid CreateCombGuid(DateTime timestamp, CombGuidType type) =>
+ CreateCombGuid(Guid.NewGuid(), timestamp, type);
+
+ ///
+ /// Generates a COMB GUID using a specified base GUID and the current UTC timestamp.
+ ///
+ /// The base GUID to embed the timestamp into.
+ /// The COMB GUID format to use.
+ /// The generated COMB GUID.
+ public static Guid CreateCombGuid(Guid baseGuid, CombGuidType type) =>
+ CreateCombGuid(baseGuid, DateTime.UtcNow, type);
+
+ ///
+ /// Extracts the embedded UTC timestamp from a COMB GUID using the specified format.
+ ///
+ /// The COMB GUID containing the timestamp.
+ /// The COMB GUID format used during creation.
+ /// The extracted UTC timestamp.
+ public static DateTime ExtractTimestamp(Guid combGuid, CombGuidType type) {
+ Span guidBytes = stackalloc byte[16];
+ combGuid.TryWriteBytes(guidBytes);
+
+ return type switch {
+ CombGuidType.SqlServer => ReadTimestampFromBytes(guidBytes.Slice(8, TimestampByteLength)),
+ CombGuidType.PostgreSql => ReadTimestampFromBytes(guidBytes.Slice(0, TimestampByteLength)),
+ _ => throw new ArgumentOutOfRangeException(nameof(type), "Unsupported COMB GUID type.")
+ };
+ }
+
+ ///
+ /// Creates a COMB GUID compatible with SQL Server by embedding the timestamp in bytes 8–15.
+ ///
+ /// The base GUID.
+ /// The UTC timestamp.
+ /// The resulting COMB GUID.
+ private static Guid CreateSqlServerCombGuid(Guid baseGuid, DateTime timestamp) {
+ Span guidBytes = stackalloc byte[16];
+ baseGuid.TryWriteBytes(guidBytes);
+ WriteTimestampBytes(guidBytes.Slice(8, TimestampByteLength), timestamp);
+ return new Guid(guidBytes);
+ }
+
+ ///
+ /// Creates a COMB GUID compatible with PostgreSQL by embedding the timestamp in bytes 0–7.
+ ///
+ /// The base GUID.
+ /// The UTC timestamp.
+ /// The resulting COMB GUID.
+ private static Guid CreatePostgreSqlCombGuid(Guid baseGuid, DateTime timestamp) {
+ Span baseBytes = stackalloc byte[16];
+ baseGuid.TryWriteBytes(baseBytes);
+
+ Span finalBytes = stackalloc byte[16];
+ // first 8 bytes = timestamp
+ WriteTimestampBytes(finalBytes.Slice(0, TimestampByteLength), timestamp);
+ // remaining 8 bytes = random tail
+ baseBytes.Slice(TimestampByteLength, 16 - TimestampByteLength)
+ .CopyTo(finalBytes.Slice(TimestampByteLength, 16 - TimestampByteLength));
+
+ return new Guid(finalBytes);
+ }
+
+ ///
+ /// Converts a DateTime into an 8-byte timestamp and writes it into the specified span.
+ ///
+ /// The span where the timestamp will be written.
+ /// The UTC timestamp to convert.
+ private static void WriteTimestampBytes(Span destination, DateTime timestamp) {
+ long ticks = timestamp.ToUniversalTime().Ticks; // full 64-bit precision
+ Span fullBytes = stackalloc byte[8];
+ BinaryPrimitives.WriteInt64BigEndian(fullBytes, ticks);
+ fullBytes.CopyTo(destination);
+ }
+
+ ///
+ /// Reads an 8-byte timestamp from the given span and converts it to a DateTime.
+ ///
+ /// The span containing the timestamp bytes.
+ /// The corresponding UTC DateTime.
+ private static DateTime ReadTimestampFromBytes(ReadOnlySpan source) {
+ long ticks = BinaryPrimitives.ReadInt64BigEndian(source);
+ return new DateTime(ticks, DateTimeKind.Utc);
+ }
+
+ ///
+ /// Converts a DateTime to Unix time in milliseconds.
+ ///
+ /// The UTC DateTime to convert.
+ /// Unix time in milliseconds.
+ private static long ConvertToUnixTimeMilliseconds(DateTime timestamp) =>
+ (long)(timestamp.ToUniversalTime() - UnixEpoch).TotalMilliseconds;
+
+ ///
+ /// Converts Unix time in milliseconds to a UTC DateTime.
+ ///
+ /// Unix time in milliseconds.
+ /// The corresponding UTC DateTime.
+ private static DateTime ConvertFromUnixTimeMilliseconds(long milliseconds) =>
+ UnixEpoch.AddMilliseconds(milliseconds);
+}
diff --git a/src/MaksIT.Core/MaksIT.Core.csproj b/src/MaksIT.Core/MaksIT.Core.csproj
index 3282493..d652e65 100644
--- a/src/MaksIT.Core/MaksIT.Core.csproj
+++ b/src/MaksIT.Core/MaksIT.Core.csproj
@@ -8,7 +8,7 @@
MaksIT.Core
- 1.4.1
+ 1.4.2
Maksym Sadovnychyy
MAKS-IT
MaksIT.Core