@@ -64,6 +64,38 @@ pub fn wrapEXT(t: i8, data: []u8) EXT {
6464 };
6565}
6666
67+ /// the Timestamp Type
68+ /// Represents an instantaneous point on the time-line in the world
69+ /// that is independent from time zones or calendars.
70+ /// Maximum precision is nanoseconds.
71+ pub const Timestamp = struct {
72+ /// seconds since 1970-01-01 00:00:00 UTC
73+ seconds : i64 ,
74+ /// nanoseconds (0-999999999)
75+ nanoseconds : u32 ,
76+
77+ /// Create a new timestamp
78+ pub fn new (seconds : i64 , nanoseconds : u32 ) Timestamp {
79+ return Timestamp {
80+ .seconds = seconds ,
81+ .nanoseconds = nanoseconds ,
82+ };
83+ }
84+
85+ /// Create timestamp from seconds only (nanoseconds = 0)
86+ pub fn fromSeconds (seconds : i64 ) Timestamp {
87+ return Timestamp {
88+ .seconds = seconds ,
89+ .nanoseconds = 0 ,
90+ };
91+ }
92+
93+ /// Get total seconds as f64 (including fractional nanoseconds)
94+ pub fn toFloat (self : Timestamp ) f64 {
95+ return @as (f64 , @floatFromInt (self .seconds )) + @as (f64 , @floatFromInt (self .nanoseconds )) / 1_000_000_000.0 ;
96+ }
97+ };
98+
6799/// the map of payload
68100pub const Map = std .StringHashMap (Payload );
69101
@@ -87,6 +119,7 @@ pub const Payload = union(enum) {
87119 arr : []Payload ,
88120 map : Map ,
89121 ext : EXT ,
122+ timestamp : Timestamp ,
90123
91124 /// get array element
92125 pub fn getArrElement (self : Payload , index : usize ) ! Payload {
@@ -222,6 +255,20 @@ pub const Payload = union(enum) {
222255 };
223256 }
224257
258+ /// get a timestamp payload
259+ pub fn timestampToPayload (seconds : i64 , nanoseconds : u32 ) Payload {
260+ return Payload {
261+ .timestamp = Timestamp .new (seconds , nanoseconds ),
262+ };
263+ }
264+
265+ /// get a timestamp payload from seconds only
266+ pub fn timestampFromSeconds (seconds : i64 ) Payload {
267+ return Payload {
268+ .timestamp = Timestamp .fromSeconds (seconds ),
269+ };
270+ }
271+
225272 /// free the all memeory for this payload and sub payloads
226273 /// the allocator is payload's allocator
227274 pub fn free (self : Payload , allocator : Allocator ) void {
@@ -847,6 +894,43 @@ pub fn Pack(
847894 }
848895 }
849896
897+ /// write timestamp
898+ fn writeTimestamp (self : Self , timestamp : Timestamp ) ! void {
899+ // According to MessagePack spec, timestamp uses extension type -1
900+ const TIMESTAMP_TYPE : i8 = -1 ;
901+
902+ // timestamp 32 format: seconds fit in 32-bit unsigned int and nanoseconds is 0
903+ if (timestamp .nanoseconds == 0 and timestamp .seconds >= 0 and timestamp .seconds <= 0xffffffff ) {
904+ var data : [4 ]u8 = undefined ;
905+ std .mem .writeInt (u32 , & data , @intCast (timestamp .seconds ), big_endian );
906+ const ext = EXT { .type = TIMESTAMP_TYPE , .data = & data };
907+ try self .writeExt (ext );
908+ return ;
909+ }
910+
911+ // timestamp 64 format: seconds fit in 34-bit and nanoseconds <= 999999999
912+ if (timestamp .seconds >= 0 and (timestamp .seconds >> 34 ) == 0 and timestamp .nanoseconds <= 999999999 ) {
913+ const data64 : u64 = (@as (u64 , timestamp .nanoseconds ) << 34 ) | @as (u64 , @intCast (timestamp .seconds ));
914+ var data : [8 ]u8 = undefined ;
915+ std .mem .writeInt (u64 , & data , data64 , big_endian );
916+ const ext = EXT { .type = TIMESTAMP_TYPE , .data = & data };
917+ try self .writeExt (ext );
918+ return ;
919+ }
920+
921+ // timestamp 96 format: full range with signed 64-bit seconds and 32-bit nanoseconds
922+ if (timestamp .nanoseconds <= 999999999 ) {
923+ var data : [12 ]u8 = undefined ;
924+ std .mem .writeInt (u32 , data [0.. 4], timestamp .nanoseconds , big_endian );
925+ std .mem .writeInt (i64 , data [4.. 12], timestamp .seconds , big_endian );
926+ const ext = EXT { .type = TIMESTAMP_TYPE , .data = & data };
927+ try self .writeExt (ext );
928+ return ;
929+ }
930+
931+ return MsGPackError .INVALID_TYPE ;
932+ }
933+
850934 /// write payload
851935 pub fn write (self : Self , payload : Payload ) ! void {
852936 switch (payload ) {
@@ -912,6 +996,9 @@ pub fn Pack(
912996 .ext = > | ext | {
913997 try self .writeExt (ext );
914998 },
999+ .timestamp = > | timestamp | {
1000+ try self .writeTimestamp (timestamp );
1001+ },
9151002 }
9161003 }
9171004
@@ -1284,6 +1371,153 @@ pub fn Pack(
12841371 };
12851372 }
12861373
1374+ /// read ext value or timestamp if it's timestamp type (-1)
1375+ fn readExtValueOrTimestamp (self : Self , marker : Markers , allocator : Allocator ) ! Payload {
1376+ const TIMESTAMP_TYPE : i8 = -1 ;
1377+
1378+ // First, check if this could be a timestamp format
1379+ if (marker == .FIXEXT4 or marker == .FIXEXT8 or marker == .EXT8 ) {
1380+ // Read and check length for EXT8
1381+ var actual_len : usize = 0 ;
1382+ if (marker == .EXT8 ) {
1383+ actual_len = try self .readV8Value ();
1384+ if (actual_len != 12 ) {
1385+ // Not timestamp 96, read as regular EXT
1386+ const ext_type = try self .readI8Value ();
1387+ const ext_data = try allocator .alloc (u8 , actual_len );
1388+ _ = try self .readFrom (ext_data );
1389+ return Payload { .ext = EXT { .type = ext_type , .data = ext_data } };
1390+ }
1391+ } else if (marker == .FIXEXT4 ) {
1392+ actual_len = 4 ;
1393+ } else if (marker == .FIXEXT8 ) {
1394+ actual_len = 8 ;
1395+ }
1396+
1397+ // Read the type
1398+ const ext_type = try self .readI8Value ();
1399+
1400+ if (ext_type == TIMESTAMP_TYPE ) {
1401+ // This is a timestamp
1402+ if (marker == .FIXEXT4 ) {
1403+ // timestamp 32
1404+ const seconds = try self .readU32Value ();
1405+ return Payload { .timestamp = Timestamp .new (@intCast (seconds ), 0 ) };
1406+ } else if (marker == .FIXEXT8 ) {
1407+ // timestamp 64
1408+ const data64 = try self .readU64Value ();
1409+ const nanoseconds : u32 = @intCast (data64 >> 34 );
1410+ const seconds : i64 = @intCast (data64 & 0x3ffffffff );
1411+ return Payload { .timestamp = Timestamp .new (seconds , nanoseconds ) };
1412+ } else if (marker == .EXT8 ) {
1413+ // timestamp 96
1414+ const nanoseconds = try self .readU32Value ();
1415+ const seconds = try self .readI64Value ();
1416+ return Payload { .timestamp = Timestamp .new (seconds , nanoseconds ) };
1417+ }
1418+ } else {
1419+ // Not a timestamp, read as regular EXT
1420+ const ext_data = try allocator .alloc (u8 , actual_len );
1421+ _ = try self .readFrom (ext_data );
1422+ return Payload { .ext = EXT { .type = ext_type , .data = ext_data } };
1423+ }
1424+ }
1425+
1426+ // Regular EXT processing
1427+ const val = try self .readExtValue (marker , allocator );
1428+ return Payload { .ext = val };
1429+ }
1430+
1431+ /// try to read timestamp from ext data, return error if not timestamp
1432+ fn tryReadTimestamp (self : Self , marker : Markers , _ : Allocator ) ! Timestamp {
1433+ const TIMESTAMP_TYPE : i8 = -1 ;
1434+
1435+ switch (marker ) {
1436+ .FIXEXT4 = > {
1437+ // timestamp 32 format
1438+ const ext_type = try self .readI8Value ();
1439+ if (ext_type != TIMESTAMP_TYPE ) {
1440+ return MsGPackError .INVALID_TYPE ;
1441+ }
1442+ const seconds = try self .readU32Value ();
1443+ return Timestamp .new (@intCast (seconds ), 0 );
1444+ },
1445+ .FIXEXT8 = > {
1446+ // timestamp 64 format
1447+ const ext_type = try self .readI8Value ();
1448+ if (ext_type != TIMESTAMP_TYPE ) {
1449+ return MsGPackError .INVALID_TYPE ;
1450+ }
1451+ const data64 = try self .readU64Value ();
1452+ const nanoseconds : u32 = @intCast (data64 >> 34 );
1453+ const seconds : i64 = @intCast (data64 & 0x3ffffffff );
1454+ return Timestamp .new (seconds , nanoseconds );
1455+ },
1456+ .EXT8 = > {
1457+ // timestamp 96 format (length should be 12)
1458+ const len = try self .readV8Value ();
1459+ if (len != 12 ) {
1460+ return MsGPackError .INVALID_TYPE ;
1461+ }
1462+ const ext_type = try self .readI8Value ();
1463+ if (ext_type != TIMESTAMP_TYPE ) {
1464+ return MsGPackError .INVALID_TYPE ;
1465+ }
1466+ const nanoseconds = try self .readU32Value ();
1467+ const seconds = try self .readI64Value ();
1468+ return Timestamp .new (seconds , nanoseconds );
1469+ },
1470+ else = > {
1471+ return MsGPackError .INVALID_TYPE ;
1472+ },
1473+ }
1474+ }
1475+
1476+ /// read timestamp from ext data
1477+ fn readTimestamp (self : Self , marker : Markers , _ : Allocator ) ! Timestamp {
1478+ const TIMESTAMP_TYPE : i8 = -1 ;
1479+
1480+ switch (marker ) {
1481+ .FIXEXT4 = > {
1482+ // timestamp 32 format
1483+ const ext_type = try self .readI8Value ();
1484+ if (ext_type != TIMESTAMP_TYPE ) {
1485+ return MsGPackError .INVALID_TYPE ;
1486+ }
1487+ const seconds = try self .readU32Value ();
1488+ return Timestamp .new (@intCast (seconds ), 0 );
1489+ },
1490+ .FIXEXT8 = > {
1491+ // timestamp 64 format
1492+ const ext_type = try self .readI8Value ();
1493+ if (ext_type != TIMESTAMP_TYPE ) {
1494+ return MsGPackError .INVALID_TYPE ;
1495+ }
1496+ const data64 = try self .readU64Value ();
1497+ const nanoseconds : u32 = @intCast (data64 >> 34 );
1498+ const seconds : i64 = @intCast (data64 & 0x3ffffffff );
1499+ return Timestamp .new (seconds , nanoseconds );
1500+ },
1501+ .EXT8 = > {
1502+ // timestamp 96 format (length should be 12)
1503+ const len = try self .readV8Value ();
1504+ if (len != 12 ) {
1505+ return MsGPackError .INVALID_TYPE ;
1506+ }
1507+ const ext_type = try self .readI8Value ();
1508+ if (ext_type != TIMESTAMP_TYPE ) {
1509+ return MsGPackError .INVALID_TYPE ;
1510+ }
1511+ const nanoseconds = try self .readU32Value ();
1512+ const seconds = try self .readI64Value ();
1513+ return Timestamp .new (seconds , nanoseconds );
1514+ },
1515+ else = > {
1516+ return MsGPackError .INVALID_TYPE ;
1517+ },
1518+ }
1519+ }
1520+
12871521 fn readExtValue (self : Self , marker : Markers , allocator : Allocator ) ! EXT {
12881522 switch (marker ) {
12891523 .FIXEXT1 = > {
@@ -1446,10 +1680,8 @@ pub fn Pack(
14461680 .EXT16 ,
14471681 .EXT32 ,
14481682 = > {
1449- const val = try self .readExtValue (marker , allocator );
1450- res = Payload {
1451- .ext = val ,
1452- };
1683+ const ext_result = try self .readExtValueOrTimestamp (marker , allocator );
1684+ res = ext_result ;
14531685 },
14541686 }
14551687 return res ;
0 commit comments