Skip to content

Conversation

@gibber9809
Copy link
Contributor

@gibber9809 gibber9809 commented Nov 20, 2025

Description

This PR follows up on #1626, #1599, #1564, and #1385 by implementing marshalling for numeric and date-time timestamp patterns. The marshalling implementation currently uses fmt to format some parts of a timestamp, which results in some extra copying of small strings; if this ends up being a performance problem we can switch to a custom implementation that elides these small copies.

This PR may be followed up by one more PR to the clp-s::timestamp_parser library to improve support for parsing leap-seconds before a PR to fully integrate clp-s::timestamp_parser with clp-s.

Checklist

  • The PR satisfies the contribution guidelines.
  • This is a breaking change and that has been indicated in the PR title, OR this isn't a
    breaking change.
  • Necessary docs have been updated, OR no docs need to be updated.

Validation performed

  • Added assertions to verify that all timestamps that we parse in the tests can be marshalled correctly.

Summary by CodeRabbit

  • New Features

    • Added timestamp marshaling/formatting to produce date‑time and numeric epoch outputs (12/24h, AM/PM, day/month/year, day‑of‑week, timezone, subsecond precision).
  • Bug Fixes

    • Improved validation for zero‑padding and clearer errors for invalid/incompatible timestamp patterns.
  • Tests

    • Added unit tests that parse timestamps and verify round‑trip formatting for several patterns and edge cases.

✏️ Tip: You can customize this high-level summary in your review settings.

@gibber9809 gibber9809 requested a review from a team as a code owner November 20, 2025 23:05
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 20, 2025

Walkthrough

Adds timestamp marshaling (date-time and numeric) and an absolute-subsecond helper, exposes marshal_timestamp in the header (duplicate declaration present), and extends tests to assert marshaling round-trips for several patterns including AM/PM 12-hour cases.

Changes

Cohort / File(s) Summary
Marshaling implementation
components/core/src/clp_s/timestamp_parser/TimestampParser.cpp
Added extract_absolute_subsecond_nanoseconds(epochtime_t) and three marshaling functions: marshal_date_time_timestamp(), marshal_numeric_timestamp(), and dispatcher marshal_timestamp(). Implementations format date/time specifiers (Y/M/D, DOW, 12/24h, AM/PM I/p), subseconds (3/6/9/T), numeric epoch formats (E/L/C/N), timezone handling, escaping, and return ystdlib::error_handling::Result<void> with InvalidTimestampPattern/IncompatibleTimestampPattern errors.
Public API declaration
components/core/src/clp_s/timestamp_parser/TimestampParser.hpp
Added public declaration marshal_timestamp(epochtime_t, TimestampPattern const&, std::string&) -> ystdlib::error_handling::Result<void>; the same declaration appears twice, introducing a duplicate.
Tests
components/core/src/clp_s/timestamp_parser/test/test_TimestampParser.cpp
Extended tests to include AM/PM 12-hour variants (e.g. "Feb 01, 2015 01:02:03 AM", "Feb 01, 2015 12:02:03 AM", "Feb 01, 2015 12:02:03 PM"). After parsing, tests call marshal_timestamp(), assert no error, and assert the marshalled string equals the original input.

Sequence Diagram(s)

%% Accessible, subtle colours for clarity
sequenceDiagram
    participant Test as Test Suite / Caller
    participant Marshal as marshal_timestamp
    participant DateFmt as marshal_date_time_timestamp
    participant NumFmt as marshal_numeric_timestamp
    participant Buf as Output Buffer

    Test->>Marshal: marshal_timestamp(epoch, pattern, buffer)
    alt pattern contains date-time specifiers
        Marshal->>DateFmt: delegate(epoch, pattern, buffer)
        Note right of DateFmt `#DDDDFF`: build Y/M/D, DOW,\n12/24-hour, AM/PM, min/sec,\nsubseconds (3/6/9/T), timezone
        DateFmt->>Buf: append formatted string
        DateFmt-->>Marshal: Result<void>
    else pattern is numeric (E/L/C/N or subsecond)
        Marshal->>NumFmt: delegate(epoch, pattern, buffer)
        Note right of NumFmt `#DDFFDD`: compute epoch base,\nformat digits and subseconds,\nhandle negative epochs
        NumFmt->>Buf: append formatted string
        NumFmt-->>Marshal: Result<void>
    else invalid pattern
        Marshal-->>Test: InvalidTimestampPattern error
    end
    Marshal-->>Test: Result<void>
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

  • Inspect duplicate marshal_timestamp() declaration in TimestampParser.hpp.
  • Verify AM/PM (I/p) handling at 12-hour boundaries and day-of-week computation.
  • Validate numeric epoch modes (E/L/C/N), subsecond extraction/rounding (3/6/9/T), and negative-epoch behaviour.
  • Review extract_absolute_subsecond_nanoseconds() correctness and error-return paths (InvalidTimestampPattern, IncompatibleTimestampPattern).

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 55.56% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding marshalling support for timestamps in the timestamp_parser module.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 55cb147 and 562a776.

📒 Files selected for processing (1)
  • components/core/src/clp_s/timestamp_parser/test/test_TimestampParser.cpp (3 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{cpp,hpp,java,js,jsx,tpp,ts,tsx}

⚙️ CodeRabbit configuration file

  • Prefer false == <expression> rather than !<expression>.

Files:

  • components/core/src/clp_s/timestamp_parser/test/test_TimestampParser.cpp
🧬 Code graph analysis (1)
components/core/src/clp_s/timestamp_parser/test/test_TimestampParser.cpp (2)
components/core/src/clp_s/timestamp_parser/TimestampParser.cpp (2)
  • marshal_timestamp (1554-1562)
  • marshal_timestamp (1554-1555)
components/core/src/clp_s/timestamp_parser/TimestampParser.hpp (1)
  • marshal_timestamp (162-163)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (14)
  • GitHub Check: package-image
  • GitHub Check: musllinux_1_2-x86_64-static-linked-bins
  • GitHub Check: musllinux_1_2-x86_64-dynamic-linked-bins
  • GitHub Check: ubuntu-jammy-static-linked-bins
  • GitHub Check: ubuntu-jammy-lint
  • GitHub Check: centos-stream-9-dynamic-linked-bins
  • GitHub Check: centos-stream-9-static-linked-bins
  • GitHub Check: manylinux_2_28-x86_64-dynamic-linked-bins
  • GitHub Check: ubuntu-jammy-dynamic-linked-bins
  • GitHub Check: manylinux_2_28-x86_64-static-linked-bins
  • GitHub Check: build-macos (macos-15, true)
  • GitHub Check: build-macos (macos-15, false)
  • GitHub Check: lint-check (ubuntu-24.04)
  • GitHub Check: lint-check (macos-15)
🔇 Additional comments (2)
components/core/src/clp_s/timestamp_parser/test/test_TimestampParser.cpp (2)

449-451: 12-hour AM/PM cases: epochs and patterns look consistent

These entries correctly exercise \I/\l 12-hour parsing for midnight and noon, and the epoch values line up with the neighbouring 2015-02-01 cases; no changes needed.


500-501: UTC+0130 and historical negative-epoch coverage look good

The epoch for UTC+0130 matches the existing UTC+01:30 case, and the 1895 timestamp adds useful pre-epoch coverage for date-time parsing; this block looks solid as-is.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 55044b5 and d263424.

📒 Files selected for processing (3)
  • components/core/src/clp_s/timestamp_parser/TimestampParser.cpp (4 hunks)
  • components/core/src/clp_s/timestamp_parser/TimestampParser.hpp (1 hunks)
  • components/core/src/clp_s/timestamp_parser/test/test_TimestampParser.cpp (3 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{cpp,hpp,java,js,jsx,tpp,ts,tsx}

⚙️ CodeRabbit configuration file

  • Prefer false == <expression> rather than !<expression>.

Files:

  • components/core/src/clp_s/timestamp_parser/TimestampParser.hpp
  • components/core/src/clp_s/timestamp_parser/test/test_TimestampParser.cpp
  • components/core/src/clp_s/timestamp_parser/TimestampParser.cpp
🧬 Code graph analysis (2)
components/core/src/clp_s/timestamp_parser/test/test_TimestampParser.cpp (2)
components/core/src/clp_s/timestamp_parser/TimestampParser.cpp (2)
  • marshal_timestamp (1549-1557)
  • marshal_timestamp (1549-1550)
components/core/src/clp_s/timestamp_parser/TimestampParser.hpp (1)
  • marshal_timestamp (161-162)
components/core/src/clp_s/timestamp_parser/TimestampParser.cpp (1)
components/core/src/clp_s/timestamp_parser/TimestampParser.hpp (5)
  • nodiscard (42-42)
  • nodiscard (44-47)
  • nodiscard (49-51)
  • nodiscard (53-53)
  • pattern (29-30)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (14)
  • GitHub Check: musllinux_1_2-x86_64-static-linked-bins
  • GitHub Check: musllinux_1_2-x86_64-dynamic-linked-bins
  • GitHub Check: ubuntu-jammy-lint
  • GitHub Check: ubuntu-jammy-static-linked-bins
  • GitHub Check: package-image
  • GitHub Check: ubuntu-jammy-dynamic-linked-bins
  • GitHub Check: centos-stream-9-dynamic-linked-bins
  • GitHub Check: centos-stream-9-static-linked-bins
  • GitHub Check: manylinux_2_28-x86_64-static-linked-bins
  • GitHub Check: manylinux_2_28-x86_64-dynamic-linked-bins
  • GitHub Check: lint-check (ubuntu-24.04)
  • GitHub Check: lint-check (macos-15)
  • GitHub Check: build-macos (macos-15, false)
  • GitHub Check: build-macos (macos-15, true)
🔇 Additional comments (3)
components/core/src/clp_s/timestamp_parser/test/test_TimestampParser.cpp (1)

449-450: Good coverage of tricky datetime cases

The added ExpectedParsingResult entries for 12‑hour midnight/noon and the 1895 pre‑epoch timestamp look consistent with the existing expectations and nicely pin down edge behaviour.

Also applies to: 499-500

components/core/src/clp_s/timestamp_parser/TimestampParser.cpp (2)

232-236: Helper for absolute subsecond nanoseconds looks correct

extract_absolute_subsecond_nanoseconds’s use of modulo by 1e9 followed by taking the absolute value yields a non‑negative subsecond component strictly less than one second for both positive and negative timestamps, which is exactly what the numeric marshaler needs.

Also applies to: 485-493


691-786: Numeric marshalling correctly mirrors parsing semantics, including negative timestamps

The numeric marshaler cleanly handles \E, \L, \C, \N by integer‑dividing by the appropriate power of ten, and uses extract_absolute_subsecond_nanoseconds to produce the correct 3/6/9/T fractional parts for both positive and negative epochs. The explicit case '\\' emitting a backslash also matches the documented \\ literal behaviour.

Comment on lines +530 to +537
std::string marshalled_timestamp;
auto const marshal_result{marshal_timestamp(
expected_result.epoch_timestamp,
timestamp_pattern_result.value(),
marshalled_timestamp
)};
REQUIRE_FALSE(marshal_result.has_error());
REQUIRE(expected_result.timestamp == marshalled_timestamp);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Round‑trip tests are strong; consider also exercising patterns from search_known_timestamp_patterns

The new marshalling block effectively verifies that known (epoch, pattern) pairs round‑trip through marshal_timestamp. If you expect callers to marshal using patterns returned by search_known_timestamp_patterns (including those originating from CAT patterns), it may be worth adding similar assertions using the discovered pattern to catch divergences between “hand‑written” and discovered patterns.

🤖 Prompt for AI Agents
In components/core/src/clp_s/timestamp_parser/test/test_TimestampParser.cpp
around lines 530 to 537, the test verifies round‑trip marshalling using the
hardcoded expected pattern but does not exercise patterns returned by
search_known_timestamp_patterns; update the test to call
search_known_timestamp_patterns for the same input, obtain the discovered
pattern(s) (or the primary value()), use that discovered pattern to call
marshal_timestamp into a separate marshalled string, assert marshal_timestamp
succeeds, and REQUIRE that the marshalled result equals the expected timestamp
string (optionally iterate over all discovered patterns to assert each one
round‑trips).

Copy link
Member

@LinZhihao-723 LinZhihao-723 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The implementation lgtm (also I think the unit test should cover the actual functionality correctness)
Some small style changes

*/
[[nodiscard]] auto marshal_date_time_timestamp(
epochtime_t timestamp,
TimestampPattern const& timestamp_pattern,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pattern to match the docstring? lol
(I prefer pattern for clarity

*/
[[nodiscard]] auto marshal_numeric_timestamp(
epochtime_t timestamp,
TimestampPattern const& timestamp_pattern,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same (also for the implementation)

R"(\b \d \H:\M:\S UTC\z{+0130})",
1'765'602'000'000'000}
1'765'602'000'000'000},
{"1895-11-20T21:55:46,010", R"(\Y-\m-\dT\H:\M:\S,\3)", -2'338'769'053'990'000'000}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logs before semiconductor, lol

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between adb1ca9 and 55cb147.

📒 Files selected for processing (2)
  • components/core/src/clp_s/timestamp_parser/TimestampParser.cpp (14 hunks)
  • components/core/src/clp_s/timestamp_parser/test/test_TimestampParser.cpp (4 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{cpp,hpp,java,js,jsx,tpp,ts,tsx}

⚙️ CodeRabbit configuration file

  • Prefer false == <expression> rather than !<expression>.

Files:

  • components/core/src/clp_s/timestamp_parser/test/test_TimestampParser.cpp
  • components/core/src/clp_s/timestamp_parser/TimestampParser.cpp
🧬 Code graph analysis (2)
components/core/src/clp_s/timestamp_parser/test/test_TimestampParser.cpp (2)
components/core/src/clp_s/timestamp_parser/TimestampParser.cpp (2)
  • marshal_timestamp (1554-1562)
  • marshal_timestamp (1554-1555)
components/core/src/clp_s/timestamp_parser/TimestampParser.hpp (1)
  • marshal_timestamp (162-163)
components/core/src/clp_s/timestamp_parser/TimestampParser.cpp (1)
components/core/src/clp_s/timestamp_parser/TimestampParser.hpp (5)
  • nodiscard (42-42)
  • nodiscard (44-47)
  • nodiscard (49-51)
  • nodiscard (53-53)
  • pattern (29-30)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (14)
  • GitHub Check: package-image
  • GitHub Check: ubuntu-jammy-static-linked-bins
  • GitHub Check: ubuntu-jammy-lint
  • GitHub Check: ubuntu-jammy-dynamic-linked-bins
  • GitHub Check: manylinux_2_28-x86_64-static-linked-bins
  • GitHub Check: musllinux_1_2-x86_64-static-linked-bins
  • GitHub Check: musllinux_1_2-x86_64-dynamic-linked-bins
  • GitHub Check: centos-stream-9-dynamic-linked-bins
  • GitHub Check: manylinux_2_28-x86_64-dynamic-linked-bins
  • GitHub Check: centos-stream-9-static-linked-bins
  • GitHub Check: build-macos (macos-15, true)
  • GitHub Check: build-macos (macos-15, false)
  • GitHub Check: lint-check (macos-15)
  • GitHub Check: lint-check (ubuntu-24.04)
🔇 Additional comments (6)
components/core/src/clp_s/timestamp_parser/test/test_TimestampParser.cpp (1)

531-538: Marshalling round-trip verification looks good.

The marshalling verification correctly validates that timestamps can be round-tripped through marshal_timestamp. Note that there is a past review comment suggesting to also exercise patterns returned by search_known_timestamp_patterns to catch divergences between hand-written and discovered patterns.

components/core/src/clp_s/timestamp_parser/TimestampParser.cpp (5)

294-306: Zero-padding safety check is correct.

The added validation correctly rejects strings that contain additional zero-padding after stripping the expected padding character. This prevents cases like " 007" (with space padding) from being incorrectly parsed as valid when only " 7" or " 07" should be accepted.


494-501: Absolute subsecond extraction correctly handles negative timestamps.

The implementation correctly extracts the absolute value of the subsecond fractional component for both positive and negative epoch timestamps. For negative timestamps, the modulo operation yields a negative (or zero) result, which is then negated to produce the absolute subsecond value.


503-691: Date-time marshalling implementation is thorough and correct.

The implementation correctly handles all date-time format specifiers including:

  • Year, month, day variations with proper zero/space padding
  • 12-hour and 24-hour clock formats with correct modulo-12 logic for 12-hour clocks
  • Subsecond precision (milliseconds, microseconds, nanoseconds) with proper scaling
  • Variable-length subsecond formatting that strips trailing zeros
  • Timezone offset handling
  • Literal backslash escaping

The function adheres to the coding guideline of using false == <expression> rather than !<expression>.


695-790: Numeric timestamp marshalling correctly handles all epoch precisions.

The implementation correctly marshals numeric timestamps including:

  • Different epoch precisions (seconds, milliseconds, microseconds, nanoseconds)
  • Subsecond formatting with proper zero-padding
  • Variable-length subsecond formatting that strips trailing zeros
  • Correct handling of negative timestamps by using extract_absolute_subsecond_nanoseconds
  • Literal backslash escaping

The function adheres to the coding guideline of using false == <expression> rather than !<expression>.


1554-1562: Marshalling dispatcher is straightforward and correct.

The dispatcher correctly routes marshalling requests to either marshal_date_time_timestamp or marshal_numeric_timestamp based on the pattern type. Error handling is properly propagated using the error handling macros.

Copy link
Member

@LinZhihao-723 LinZhihao-723 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR title lgtm.

@gibber9809 gibber9809 merged commit 5454905 into y-scope:main Nov 26, 2025
25 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants