Skip to content

runtimes/go: keep sub-second precision when encoding time.Time params - #2629

Open
Abhishek-B-R wants to merge 1 commit into
encoredev:mainfrom
Abhishek-B-R:fix/etype-time-subsecond
Open

Abhishek-B-R wants to merge 1 commit into
encoredev:mainfrom
Abhishek-B-R:fix/etype-time-subsecond

Conversation

@Abhishek-B-R

@Abhishek-B-R Abhishek-B-R commented Oct 28, 2026 •

Copy link
Copy Markdown

Motivation:
etype.MarshalTime formats with time.RFC3339, which has no fractional
seconds. The generated code uses it for path, query and header values
(genutil.MarshalBuiltin and MarshalQueryOrHeader), so a time.Time
passed to another service in one of those places, or returned in a
response header, is silently truncated to the second. The same value in
a JSON body keeps full precision, so the behaviour depends on where the
field happens to live.

This bites things like keyset pagination: a CreatedAfter time.Time
query param holding a Postgres timestamp (microseconds) arrives as the
whole second, and the callee returns rows the caller has already seen.

Before: 2026-10-28T09:30:00.123456Z is sent as 2026-10-28T09:30:00Z.
After: it is sent unchanged.

Approach:
Format with time.RFC3339Nano. It trims trailing zeros, so whole-second
values encode exactly as they did before, and UnmarshalTime already
parses fractional seconds (Go's RFC3339 parser accepts them), so old and
new runtimes on either side of a call still understand each other.

Validation:
Added TestMarshalTime in runtimes/go/appruntime/shared/etype, covering
whole seconds, microseconds, nanoseconds and a +05:30 offset, each checked
for the exact string and for a lossless round trip through UnmarshalTime.
On main the three sub-second cases fail; with the change all pass.

  • cd runtimes/go && go test -short -tags=dev_build ./appruntime/shared/etype/ (also with TZ=Asia/Kolkata)
  • cd runtimes/go && go test -short -tags=dev_build ./... passes
  • go vet and gofmt -l clean on the package

The generated Go client (internal/gocodegen/marshalling.go, FromTime)
has the same time.RFC3339 format. I left it out to keep this to the
runtime, since changing it means regenerating the client goldens. Happy
to do it here or in a follow-up if you want it.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

etype.MarshalTime formatted values with time.RFC3339, which drops the
fractional seconds. It is used by the generated code for path, query and
header values, so a time.Time sent to another service in one of those
places (or returned in a response header) was truncated to the second,
while the same value in a JSON body kept its full precision.

Use time.RFC3339Nano instead. Whole-second values encode exactly as
before, and UnmarshalTime already accepts fractional seconds, so both
sides stay compatible.

This branch has not been deployed

No deployments
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.

1 participant