Unix Timestamps for API Debugging
APIs store time as a count of seconds, or sometimes milliseconds, since 1 January 1970 UTC. The number is precise and ugly. I convert it with the Unix timestamp tool before I decide a token is expired or a row was written "yesterday". The usual bug is not the clock. It is treating milliseconds as seconds.
Which unit you have
Ten digits is usually seconds. Thirteen digits is usually milliseconds. A value like 1730000000 sits in 2024. Multiply a seconds value by 1000 and you are in the year 56000, which is how a chart axis ends up looking haunted. Divide a millisecond value by 1000 if the converter was set to seconds and the date is centuries ahead.
JWT exp and iat claims are seconds. JavaScript's Date.now() is milliseconds. Those two facts, forgotten at the boundary between a token and a frontend, produce "expired" tokens that are actually fine, and tokens that should have expired last week and did not.
UTC first, then Pakistan time
The timestamp is an instant, not a local wall clock. Convert to UTC, then add five hours for Pakistan Standard Time. Do not add five hours twice because the converter already showed local time. Write the zone in the ticket: "2026-11-06 14:10 PKT (09:10 UTC)". A bare date starts an argument about whose midnight.
Daylight saving does not apply in Pakistan. It does apply in other countries your logs might mention. UTC keeps the argument short.
A debugging order
- Copy the number. Do not round it.
- Decide seconds or milliseconds from the digit count and from the docs, not from hope.
- Convert and read UTC.
- Compare with the time you made the request. If the token's exp is before that instant, it really was expired. If it is after, look elsewhere: signature, audience, clock skew of a minute or two.
- If you are creating a fixture, build the timestamp from the date you want and put that number in the test, with a comment of the human date.
Format the JSON around the claim so you can see exp next to the other fields. A timestamp buried in one line is easy to misread as an id.
Clock skew and the minute you should allow
Servers and laptops disagree by small amounts. A token that "expires" 20 seconds in the past may be skew, not a logic bug. A token that expired three days ago is not skew. I allow a small window in tests and I still look at the absolute time when a customer says they were locked out.
Also check the type. The string "1730000000" and the number 1730000000 often behave the same in a converter and differently in a database comparison. The JSON viewer, or just the quotes in the formatted payload, tells you which you received.
When you write one
Store seconds or milliseconds consistently at the boundary you control, and name the field createdAtSeconds if the unit is not obvious. Document it. Future you will not remember. When you display it, convert at the edge and show the zone. Raw integers are for machines and for this converter. They are a poor thing to put in a sentence to a customer.
If two services disagree, paste both numbers into the converter and then into the diff checker only if you have turned them into formatted dates. Diffing the integers is fine too: if they differ by about 18000, someone added five hours in the wrong layer. If they differ by a factor of 1000, you have the seconds-versus-milliseconds bug, and you can stop looking at the business logic.
What I write in the ticket
I paste the raw number, the UTC time, and the PKT time, and I say seconds or milliseconds. "It expired" is not a ticket. "exp 1730000000 is 2024-10-27 02:13 UTC, and the request was made an hour later" is a ticket. The next person can check the arithmetic without opening your laptop. I also note the clock on the machine if skew is even slightly plausible, because a laptop that has been asleep lies.
When I generate a timestamp for a test, I convert it back immediately. A hand-rolled number that is off by a zero will pass a test that never looks at the date and fail in production in a way that looks like authorisation. The converter is the check on my own typing. Use it before you blame the API, and use it before you tell a user their session died. The number is only mysterious until you print the date next to it. Keep the converter open while you read a log. The cost of converting is a paste. The cost of guessing the unit is an afternoon spent in the wrong layer of the stack, blaming a database that stored exactly what you sent.