Skip to content

Conversation

@rodrigovillarbello
Copy link

Summary

Fixes #1126 by using the native timestamp package function to convert the datetime directly to a float number, avoiding the lose of microseconds precision.

A very similar change was merged at #821.

Converting directly to float should be no problem since you can already bypass this limitation by converting the datetimes to float yourselt. Code

import datetime as dt
import jwt

ts_iat = dt.datetime(2025, 1, 1, 0, 0, 0, 123456, dt.UTC).timestamp()

result = jwt.decode(jwt.encode({'iat': ts_iat}, 'secret'), 'secret', algorithms = ['HS256'])
decoded_ts_iat = result['iat']

print(f'Original timestamp: {ts_iat}')
print(f'Decoded  timestamp: {decoded_ts_iat}')

produces

Original timestamp: 1735689600.123456
Decoded  timestamp: 1735689600.123456

Copy link
Collaborator

@auvipy auvipy left a comment

Choose a reason for hiding this comment

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

please also add proper test

Copy link

Copilot AI left a 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 fixes issue #1126 by preserving microsecond precision when encoding datetime objects in JWT time claims (exp, iat, nbf). The change replaces timegm(utctimetuple()) with the native timestamp() method, which maintains the full precision of datetime objects.

Key changes:

  • Replaced timegm(payload[time_claim].utctimetuple()) with payload[time_claim].timestamp() for datetime conversion
  • Removed the unused calendar.timegm import
  • Updated the comment to reflect that datetimes are now converted to float values instead of integer values

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

# Convert datetime to a float value in known time-format claims
if isinstance(payload.get(time_claim), datetime):
payload[time_claim] = timegm(payload[time_claim].utctimetuple())
payload[time_claim] = payload[time_claim].timestamp()
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

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

The change from timegm(utctimetuple()) to timestamp() introduces a behavioral difference for naive datetime objects (datetimes without timezone information). The old implementation treated naive datetimes as UTC, while timestamp() treats them as local time. This could be a breaking change for users passing naive datetime objects. Consider adding validation to require timezone-aware datetime objects, or document this behavioral change clearly.

Copilot uses AI. Check for mistakes.
# Convert datetime to a float value in known time-format claims
if isinstance(payload.get(time_claim), datetime):
payload[time_claim] = timegm(payload[time_claim].utctimetuple())
payload[time_claim] = payload[time_claim].timestamp()
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

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

This change enables microsecond precision in timestamp encoding, but there's no test to verify this new behavior. Consider adding a test that encodes a datetime with microseconds and verifies that the microseconds are preserved in the decoded payload. For example, a test similar to test_encode_datetime but using a datetime with microseconds like datetime(2025, 1, 1, 0, 0, 0, 123456, timezone.utc).

Copilot uses AI. Check for mistakes.
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.

jwt.encode loses microseconds precision when using datetime for known time claims

2 participants