fix: correct Rockbox's local wall-clock timestamps to UTC
Rockbox has no concept of a timezone. Its clock holds local time, and it builds log timestamps with mktime(get_time()) -- but firmware/libc/mktime.c is plain calendar arithmetic applying no offset, so the RTC's local fields come out as though they were UTC. The number in the log is ahead of the real instant by whatever the offset was, and Last.fm stores UTC, so every play submitted during BST landed an hour in the future. Rockbox states this itself: its scrobbler plugin writes #TZ/UNKNOWN, and the AUDIOSCROBBLER spec allows #TZ/UTC only for a device that actually converted. The correction belongs to the consumer. Each timestamp is decoded back to its wall-clock fields and reinterpreted in the player's zone, per play rather than as one offset over the whole log, so a log spanning a daylight saving change converts each side correctly. A log declaring #TZ/UTC is left alone rather than shifted twice. The zone defaults to this machine's, overridable with --device-timezone or ROCKBOX_TIMEZONE. Deriving it needs the whole IANA name: /etc/localtime resolves into the tzdata tree, and taking only the final component yields "London", which no database holds, silently falling back to a fixed offset that is wrong for half the year. Since Rockbox cannot adjust for daylight saving on its own, the player's clock has to be changed by hand twice a year. Any play converting to a future time is now reported, which is what a forgotten adjustment looks like. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
374a17474f
commit
9942e1a920
@@ -309,6 +309,47 @@ real-time clock Rockbox writes `/.scrobbler-timeless.log` with every timestamp
|
||||
set to zero; those are counted and reported but never submitted, because
|
||||
scrobbling them would mean inventing when they happened.
|
||||
|
||||
### Timestamps are local wall clock, and are corrected here
|
||||
|
||||
Rockbox has no concept of a timezone. Its clock is set to local time, and it
|
||||
builds log timestamps with `mktime(get_time())` — but
|
||||
[its `mktime`](https://git.rockbox.org/cgit/rockbox.git/tree/firmware/libc/mktime.c)
|
||||
is plain calendar arithmetic that applies no offset, so the RTC's local fields
|
||||
come out as if they were UTC. The number in the log is therefore ahead of the
|
||||
real instant by whatever the offset was. Last.fm stores UTC, so submitting it
|
||||
raw puts every play an hour into the future for the half of the year the UK is
|
||||
on BST.
|
||||
|
||||
Rockbox is candid about this: its scrobbler plugin writes `#TZ/UNKNOWN` in the
|
||||
log header, and the AUDIOSCROBBLER spec says a device may claim `#TZ/UTC` only
|
||||
if it actually converted. The correction is the consumer's job.
|
||||
|
||||
Each timestamp is decoded back into the wall-clock fields it came from and
|
||||
reinterpreted in the player's zone. Doing it **per play** rather than applying
|
||||
one offset to the whole log matters: a week's listening can straddle a daylight
|
||||
saving change, and the two sides need different offsets. A log that declares
|
||||
`#TZ/UTC` is left alone, so a client that already converted is not shifted
|
||||
twice.
|
||||
|
||||
The zone defaults to this machine's. Set `ROCKBOX_TIMEZONE` (or pass
|
||||
`--device-timezone`) to an IANA name if the player's clock is keeping a
|
||||
different one.
|
||||
|
||||
Because Rockbox cannot adjust for daylight saving itself, **you have to change
|
||||
the player's clock by hand twice a year**. If you forget, its times are an hour
|
||||
out and no amount of zone arithmetic recovers them. The submitter reports any
|
||||
play that converts to a time in the future, which is what a forgotten
|
||||
adjustment looks like:
|
||||
|
||||
```
|
||||
37 plays are timestamped up to 58 minutes in the future, converting from
|
||||
Europe/London. Either the player's clock is wrong or that is not the zone
|
||||
it is set to.
|
||||
```
|
||||
|
||||
`--dry-run` prints each play's local time beside the epoch, so the conversion
|
||||
can be checked against when you actually remember listening.
|
||||
|
||||
### Nothing played is thrown away
|
||||
|
||||
Two separate obligations, because a play that happened and never reached
|
||||
|
||||
Reference in New Issue
Block a user