Skip to content

fix: honor --time-format rfc3339 for JSON output#127

Open
digizeph wants to merge 2 commits into
mainfrom
fix/json-line-time-format-123
Open

fix: honor --time-format rfc3339 for JSON output#127
digizeph wants to merge 2 commits into
mainfrom
fix/json-line-time-format-123

Conversation

@digizeph

Copy link
Copy Markdown
Member

Fixes #123

--time-format rfc3339 was being ignored for json, json-line, and json-pretty output — the timestamp field always stayed a numeric Unix value even when you asked for RFC 3339.

Root cause was in build_json_object(), which had a hardcoded "JSON always uses Unix timestamp" comment and didn't thread the time_format parameter through. Now it does:

  • --time-format unix (default) → numeric timestamp field, unchanged for backward compat
  • --time-format rfc3339 → string timestamp field like "2024-08-23T14:15:00+00:00"

Applies to parse and search consistently. Updated the clap help text and README which previously said "non-JSON output" — that's no longer accurate.

Added tests covering both formats for json-line output.

Previously, --time-format rfc3339 was silently ignored when using
--format json, json-line, or json-pretty — the timestamp field always
remained a numeric Unix value. build_json_object() now accepts a
TimestampFormat parameter and emits a formatted RFC 3339 string when
Rfc3339 is selected, while Unix (default) preserves the numeric f64
output byte-for-byte via the existing native serialization fallback.

Fixes #123

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Fixes #123 by making JSON outputs (json, json-line, json-pretty) honor --time-format rfc3339, so the timestamp field can be emitted as an RFC3339 string instead of always being a Unix numeric value.

Changes:

  • Threaded time_format through JSON formatting so timestamp becomes number (unix) or string (rfc3339) consistently for parse/search.
  • Updated CLI help text and README to state --time-format applies to JSON formats too.
  • Added unit tests around JSON timestamp formatting.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/utils.rs Updates TimestampFormat docs to reflect behavior across JSON and non-JSON formats.
src/bin/commands/search.rs Updates clap help text for --time-format to include JSON formats.
src/bin/commands/parse.rs Updates clap help text for --time-format to include JSON formats.
src/bin/commands/elem_format.rs Threads time_format into JSON object construction and adds JSON timestamp tests.
README.md Updates documentation to reflect JSON now honors --time-format.
CHANGELOG.md Adds an Unreleased bug fix entry describing the behavior change.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/bin/commands/elem_format.rs Outdated
Comment on lines 297 to 305
// If all default parse fields are selected, no collector field, and the default
// Unix timestamp format is in use, fall back to the original element serialization
// to preserve backward-compatible output byte-for-byte.
if fields == DEFAULT_FIELDS_PARSE
&& !fields.contains(&"collector")
&& time_format == TimestampFormat::Unix
{
return json!(elem);
}
Comment on lines +542 to +546
let obj = build_json_object(&elem, DEFAULT_FIELDS_PARSE, None, TimestampFormat::Unix);
// With default fields + Unix, build_json_object should use native elem serialization
// which contains the full element (e.g. a `peer_ip` field).
assert!(obj.get("peer_ip").is_some());
}
build_json_object now always uses native BgpElem serialization for the
default-fields case, overriding only the timestamp key when rfc3339 is
selected. Previously, rfc3339 + default fields took the field-by-field
path, dropping fields like origin_asns/only_to_customer that exist in
native serialization but not in DEFAULT_FIELDS_PARSE.

Also fix test to assert origin_asns (not in DEFAULT_FIELDS_PARSE) instead
of peer_ip (which is), and add test for rfc3339 shape preservation.

Addresses PR review feedback.
@digizeph

Copy link
Copy Markdown
Member Author

Addressed both review comments in 992979b:

Line 305 (native serialization shape): build_json_object now always uses native BgpElem serialization for the default-fields case, overriding only the timestamp key when rfc3339 is selected. Previously, rfc3339 + default fields took the field-by-field path, dropping fields like origin_asns and only_to_customer that exist in native serialization but not in DEFAULT_FIELDS_PARSE.

Line 546 (test assertion): Replaced the peer_ip assertion with origin_asns — which is in native BgpElem serialization but NOT in DEFAULT_FIELDS_PARSE, so the test now actually verifies the fallback path. Also added a new test test_default_fields_rfc3339_preserves_shape_overrides_timestamp that checks both shape preservation and timestamp override.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 8 comments.

Comment on lines +464 to +487
fn test_elem() -> BgpElem {
BgpElem {
timestamp: 1234567890.0,
elem_type: ElemType::ANNOUNCE,
peer_ip: IpAddr::V4(Ipv4Addr::new(192, 0, 2, 1)),
peer_asn: 65000.into(),
prefix: NetworkPrefix::from_str("10.0.0.0/8").unwrap(),
next_hop: Some(IpAddr::V4(Ipv4Addr::new(192, 0, 2, 1))),
as_path: Some(AsPath {
segments: vec![AsPathSegment::AsSequence(vec![65000.into(), 65001.into()])],
}),
origin_asns: Some(vec![65001.into()]),
origin: Some(Origin::IGP),
local_pref: Some(100),
med: Some(0),
communities: None,
atomic: false,
aggr_asn: None,
aggr_ip: None,
only_to_customer: None,
unknown: None,
deprecated: None,
}
}
Comment on lines +493 to +496
let obj = build_json_object(&elem, &fields, None, TimestampFormat::Unix);
let ts = obj.get("timestamp").unwrap();
assert_eq!(ts.as_f64().unwrap(), 1234567890.0);
}
Comment on lines +502 to +506
let obj = build_json_object(&elem, &fields, None, TimestampFormat::Rfc3339);
let ts = obj.get("timestamp").unwrap();
assert!(ts.is_string(), "expected string for rfc3339 timestamp");
let s = ts.as_str().unwrap();
assert!(s.contains("2009"));
Comment on lines +515 to +523
let unix_out = format_elem(
&elem,
OutputFormat::JsonLine,
&fields,
None,
TimestampFormat::Unix,
)
.unwrap();
let rfc_out = format_elem(
Comment on lines +523 to +531
let rfc_out = format_elem(
&elem,
OutputFormat::JsonLine,
&fields,
None,
TimestampFormat::Rfc3339,
)
.unwrap();

Comment on lines +532 to +537
let unix_json: serde_json::Value = serde_json::from_str(&unix_out).unwrap();
let rfc_json: serde_json::Value = serde_json::from_str(&rfc_out).unwrap();

assert!(unix_json.get("timestamp").unwrap().is_number());
assert!(rfc_json.get("timestamp").unwrap().is_string());
}
Comment on lines +558 to +560
// Timestamp stays numeric for Unix
assert!(obj.get("timestamp").unwrap().is_number());
}
Comment on lines +571 to +575
// But the timestamp should be overridden to a string
let ts = obj.get("timestamp").unwrap();
assert!(ts.is_string(), "rfc3339 timestamp should be a string");
assert!(ts.as_str().unwrap().contains('T'));
}
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.

json-line output ignores --time-format rfc3339

2 participants