Skip to content
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

Set json encoding precision #162

Merged
merged 5 commits into from Feb 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 5 additions & 1 deletion types.go
Expand Up @@ -49,9 +49,13 @@ func newNumericDateFromSeconds(f float64) *NumericDate {
// MarshalJSON is an implementation of the json.RawMessage interface and serializes the UNIX epoch
// represented in NumericDate to a byte array, using the precision specified in TimePrecision.
func (date NumericDate) MarshalJSON() (b []byte, err error) {
var prec int
if TimePrecision < time.Second {
prec = int(math.Log10(float64(time.Second) / float64(TimePrecision)))
}
f := float64(date.Truncate(TimePrecision).UnixNano()) / float64(time.Second)

return []byte(strconv.FormatFloat(f, 'f', -1, 64)), nil
return []byte(strconv.FormatFloat(f, 'f', prec, 64)), nil
}

// UnmarshalJSON is an implementation of the json.RawMessage interface and deserializses a
Expand Down
54 changes: 49 additions & 5 deletions types_test.go
Expand Up @@ -18,12 +18,10 @@ func TestNumericDate(t *testing.T) {

jwt.TimePrecision = time.Microsecond

raw := `{"iat":1516239022,"exp":1516239022.12345}`
raw := `{"iat":1516239022.000000,"exp":1516239022.123450}`
Copy link
Member Author

Choose a reason for hiding this comment

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

I suppose this makes sense.

I personally haven't encountered a need to set the precision in any of the projects I've worked on, and second granularity (without precision) has been sufficient.

Maybe we're overthinking it, and the alternative solution is to always set the default precision to 0.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yes it does make sense. We are setting precision to microsecond and are serialising as microsecond. Sounds good for me.


err := json.Unmarshal([]byte(raw), &s)

if err != nil {
t.Errorf("Unexpected error: %s", err)
if err := json.Unmarshal([]byte(raw), &s); err != nil {
t.Fatalf("Unexpected error: %s", err)
}

b, _ := json.Marshal(s)
Expand Down Expand Up @@ -65,3 +63,49 @@ func TestSingleArrayMarshal(t *testing.T) {
t.Errorf("Serialized format of string array mismatch. Expecting: %s Got: %s", string(expected), string(b))
}
}

func TestNumericDate_MarshalJSON(t *testing.T) {
// Do not run this test in parallel because it's changing
// global state.
oldPrecision := jwt.TimePrecision
t.Cleanup(func() {
jwt.TimePrecision = oldPrecision
})

tt := []struct {
in time.Time
want string
precision time.Duration
}{
{time.Unix(5243700879, 0), "5243700879", time.Second},
{time.Unix(5243700879, 0), "5243700879.000", time.Millisecond},
{time.Unix(5243700879, 0), "5243700879.000001", time.Microsecond},
{time.Unix(5243700879, 0), "5243700879.000000954", time.Nanosecond},
//
{time.Unix(4239425898, 0), "4239425898", time.Second},
{time.Unix(4239425898, 0), "4239425898.000", time.Millisecond},
{time.Unix(4239425898, 0), "4239425898.000000", time.Microsecond},
{time.Unix(4239425898, 0), "4239425898.000000000", time.Nanosecond},
//
{time.Unix(0, 1644285000210402000), "1644285000", time.Second},
{time.Unix(0, 1644285000210402000), "1644285000.210", time.Millisecond},
{time.Unix(0, 1644285000210402000), "1644285000.210402", time.Microsecond},
{time.Unix(0, 1644285000210402000), "1644285000.210402012", time.Nanosecond},
//
{time.Unix(0, 1644285315063096000), "1644285315", time.Second},
{time.Unix(0, 1644285315063096000), "1644285315.063", time.Millisecond},
{time.Unix(0, 1644285315063096000), "1644285315.063096", time.Microsecond},
{time.Unix(0, 1644285315063096000), "1644285315.063096046", time.Nanosecond},
}

for i, tc := range tt {
jwt.TimePrecision = tc.precision
by, err := jwt.NewNumericDate(tc.in).MarshalJSON()
if err != nil {
t.Fatal(err)
}
if got := string(by); got != tc.want {
t.Errorf("[%d]: failed encoding: got %q want %q", i, got, tc.want)
}
}
}