Skip to content

Commit 845b761

Browse files
committed
feat(msgpack): add Timestamp.fromNanos() method
- Implement fromNanos() to convert nanoseconds to Timestamp - Add Payload.timestampFromNanos() convenience method - Add comprehensive tests for edge cases and negative timestamps
1 parent 7c1ce41 commit 845b761

File tree

2 files changed

+79
-1
lines changed

2 files changed

+79
-1
lines changed

src/msgpack.zig

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,19 @@ pub const Timestamp = struct {
201201
};
202202
}
203203

204+
/// Create timestamp from nanoseconds since Unix epoch
205+
/// This is useful for converting from various time sources
206+
/// Example: Timestamp.fromNanos(std.time.nanoTimestamp())
207+
pub fn fromNanos(nanos: i128) Timestamp {
208+
const ns_i64: i64 = @intCast(@divFloor(nanos, std.time.ns_per_s));
209+
const nano_remainder: i64 = @intCast(@mod(nanos, std.time.ns_per_s));
210+
const nanoseconds: u32 = @intCast(if (nano_remainder < 0) nano_remainder + std.time.ns_per_s else nano_remainder);
211+
return Timestamp{
212+
.seconds = if (nano_remainder < 0) ns_i64 - 1 else ns_i64,
213+
.nanoseconds = nanoseconds,
214+
};
215+
}
216+
204217
/// Get current timestamp (now)
205218
pub fn now() Timestamp {
206219
const ns = std.time.nanoTimestamp();
@@ -401,6 +414,13 @@ pub const Payload = union(enum) {
401414
};
402415
}
403416

417+
/// get a timestamp payload from nanoseconds since Unix epoch
418+
pub inline fn timestampFromNanos(nanos: i128) Payload {
419+
return Payload{
420+
.timestamp = Timestamp.fromNanos(nanos),
421+
};
422+
}
423+
404424
/// free all memory for this payload and sub payloads
405425
/// the allocator is payload's allocator
406426
/// This is an iterative implementation that avoids stack overflow from deep nesting

src/test.zig

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2023,7 +2023,7 @@ test "timestamp now() function" {
20232023

20242024
read_buffer = fixedBufferStream(&arr);
20252025
p = pack.init(&write_buffer, &read_buffer);
2026-
2026+
20272027
const decoded = try p.read(allocator);
20282028
defer decoded.free(allocator);
20292029

@@ -2044,6 +2044,64 @@ test "timestamp now() function" {
20442044
try expect(float2 >= float1);
20452045
}
20462046

2047+
// Test timestamp fromNanos() function
2048+
test "timestamp fromNanos() conversion" {
2049+
var arr: [0xfffff]u8 = std.mem.zeroes([0xfffff]u8);
2050+
var write_buffer = fixedBufferStream(&arr);
2051+
var read_buffer = fixedBufferStream(&arr);
2052+
var p = pack.init(&write_buffer, &read_buffer);
2053+
2054+
// Test with current time
2055+
const current_nanos = std.time.nanoTimestamp();
2056+
const ts = msgpack.Timestamp.fromNanos(current_nanos);
2057+
2058+
// Verify seconds is reasonable (after 2020-01-01 and before 2100-01-01)
2059+
const year_2020: i64 = 1577836800;
2060+
const year_2100: i64 = 4102444800;
2061+
try expect(ts.seconds > year_2020);
2062+
try expect(ts.seconds < year_2100);
2063+
2064+
// Verify nanoseconds is in valid range
2065+
try expect(ts.nanoseconds >= 0);
2066+
try expect(ts.nanoseconds <= 999_999_999);
2067+
2068+
// Test with known values
2069+
const test_nanos: i128 = 1704067200_123456789; // 2024-01-01 00:00:00.123456789 UTC
2070+
const test_ts = msgpack.Timestamp.fromNanos(test_nanos);
2071+
try expect(test_ts.seconds == 1704067200);
2072+
try expect(test_ts.nanoseconds == 123456789);
2073+
2074+
// Test with negative timestamp (before Unix epoch)
2075+
const negative_nanos: i128 = -1000000_500000000; // -1000000 seconds + 0.5 seconds
2076+
const negative_ts = msgpack.Timestamp.fromNanos(negative_nanos);
2077+
try expect(negative_ts.seconds == -1000001);
2078+
try expect(negative_ts.nanoseconds == 500000000);
2079+
2080+
// Test Payload.timestampFromNanos() convenience method
2081+
const payload = msgpack.Payload.timestampFromNanos(current_nanos);
2082+
try expect(payload == .timestamp);
2083+
try expect(payload.timestamp.seconds == ts.seconds);
2084+
try expect(payload.timestamp.nanoseconds == ts.nanoseconds);
2085+
2086+
// Test serialization and deserialization
2087+
try p.write(payload);
2088+
2089+
read_buffer = fixedBufferStream(&arr);
2090+
p = pack.init(&write_buffer, &read_buffer);
2091+
2092+
const decoded = try p.read(allocator);
2093+
defer decoded.free(allocator);
2094+
2095+
try expect(decoded == .timestamp);
2096+
try expect(decoded.timestamp.seconds == ts.seconds);
2097+
try expect(decoded.timestamp.nanoseconds == ts.nanoseconds);
2098+
2099+
// Test that the toFloat() method works correctly
2100+
const float_original = ts.toFloat();
2101+
const float_decoded = decoded.timestamp.toFloat();
2102+
try expect(@abs(float_original - float_decoded) < 0.000000001);
2103+
}
2104+
20472105
// ============================================================================
20482106
// Additional tests from test_additional.zig
20492107
// ============================================================================

0 commit comments

Comments
 (0)