-
Notifications
You must be signed in to change notification settings - Fork 232
Add stream created datetime to StreamInfo #772
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Add stream created datetime to StreamInfo #772
Conversation
|
Questions/comments:
|
cd45ab3 to
cd99131
Compare
caspervonb
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR adds support for the created timestamp field in JetStream StreamInfo objects. The field captures when a stream was created and is parsed from ISO format timestamps in API responses.
- Added
createdfield toStreamInfodataclass as an optionaldatetime.datetime - Implemented ISO timestamp parsing in
StreamInfo.from_response()method - Added test assertion to verify
createdfield is populated correctly
Reviewed Changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| nats/src/nats/js/api.py | Added created field to StreamInfo and parsing logic to convert ISO timestamp to datetime |
| nats/tests/test_js.py | Added assertion to verify the created field is correctly parsed as a datetime object |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
nats/src/nats/js/api.py
Outdated
| cls._convert(resp, "cluster", ClusterInfo) | ||
|
|
||
| if "created" in resp and resp["created"]: | ||
| resp["created"] = datetime.datetime.fromisoformat(resp["created"]).astimezone(datetime.timezone.utc) |
Copilot
AI
Nov 7, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The timestamp parsing should use the _python38_iso_parsing helper method for consistency with other datetime fields in the codebase (e.g., leader_since in ClusterInfo and time in RawStreamMsg). This helper handles edge cases like 'Z' suffix replacement and trimming fractional seconds to microseconds. Consider changing to: resp['created'] = datetime.datetime.fromisoformat(cls._python38_iso_parsing(resp['created'])).astimezone(datetime.timezone.utc)
| resp["created"] = datetime.datetime.fromisoformat(resp["created"]).astimezone(datetime.timezone.utc) | |
| resp["created"] = datetime.datetime.fromisoformat( | |
| cls._python38_iso_parsing(resp["created"]) | |
| ).astimezone(datetime.timezone.utc) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@caspervonb shall I update?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes please 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done - moved _python38_iso_parsing to Base from RawStreamMsg to not duplicate the code. Btw I think it maybe fixed a bug as ClusterInfo.from_response was using _python38_iso_parsing which is imho not accessible on main:

(even more concerning is imho the fact that no test/mypy check is failing; or I'm missing something :))
The field is already exposed by NATS "$JS.API.STREAM.INFO.{name}" API: https://nats-io.github.io/nats.js/jetstream/types/StreamInfo.html
Since it's official part of protocol, let's expose it to the user.
Golang implementation already do so: https://github.com/nats-io/nats.go/blob/4ec2f446e4cd829a7be3bf9aa16c43a7fddeaed9/jetstream/stream_config.go#L32
Example API response:
```json
{
"type": "io.nats.jetstream.api.v1.stream_info_response",
"total": 0,
"offset": 0,
"limit": 0,
"created": "2025-11-02T15:34:21.730004852Z",
"config": {
"name": "stream",
"subjects": [
"test.>"
],
"retention": "limits",
"max_consumers": -1,
"max_msgs": -1,
"max_bytes": 2147483648,
"max_age": 86400000000000,
"max_msgs_per_subject": -1,
"max_msg_size": -1,
"discard": "old",
"storage": "file",
"num_replicas": 1,
"duplicate_window": 120000000000,
"compression": "s2",
"allow_direct": false,
"mirror_direct": false,
"sealed": false,
"deny_delete": false,
"deny_purge": false,
"allow_rollup_hdrs": false,
"consumer_limits": {}
},
"state": {
"messages": 1249954,
"bytes": 1036040689,
"first_seq": 3326784,
"first_ts": "2025-11-03T08:38:40.571255423Z",
"last_seq": 4576737,
"last_ts": "2025-11-03T13:34:30.52654449Z",
"num_subjects": 2,
"consumer_count": 1
},
"cluster": {
"leader": "nats-0"
},
"ts": "2025-11-03T13:34:30.530396449Z"
}
```
cd99131 to
a353232
Compare
The field is already exposed by NATS "$JS.API.STREAM.INFO.{name}" API: https://nats-io.github.io/nats.js/jetstream/types/StreamInfo.html
Since it's official part of protocol, let's expose it to the user. Golang implementation already do so: https://github.com/nats-io/nats.go/blob/4ec2f446e4cd829a7be3bf9aa16c43a7fddeaed9/jetstream/stream_config.go#L32
Example API response:
{ "type": "io.nats.jetstream.api.v1.stream_info_response", "total": 0, "offset": 0, "limit": 0, "created": "2025-11-02T15:34:21.730004852Z", "config": { "name": "stream", "subjects": [ "test.>" ], "retention": "limits", "max_consumers": -1, "max_msgs": -1, "max_bytes": 2147483648, "max_age": 86400000000000, "max_msgs_per_subject": -1, "max_msg_size": -1, "discard": "old", "storage": "file", "num_replicas": 1, "duplicate_window": 120000000000, "compression": "s2", "allow_direct": false, "mirror_direct": false, "sealed": false, "deny_delete": false, "deny_purge": false, "allow_rollup_hdrs": false, "consumer_limits": {} }, "state": { "messages": 1249954, "bytes": 1036040689, "first_seq": 3326784, "first_ts": "2025-11-03T08:38:40.571255423Z", "last_seq": 4576737, "last_ts": "2025-11-03T13:34:30.52654449Z", "num_subjects": 2, "consumer_count": 1 }, "cluster": { "leader": "nats-0" }, "ts": "2025-11-03T13:34:30.530396449Z" }