Skip to content

Fix alarms firing in server timezone instead of player timezone#1514

Draft
boudekerk wants to merge 4 commits intoLMS-Community:public/9.2from
boudekerk:feature/timezone
Draft

Fix alarms firing in server timezone instead of player timezone#1514
boudekerk wants to merge 4 commits intoLMS-Community:public/9.2from
boudekerk:feature/timezone

Conversation

@boudekerk
Copy link
Contributor

When a player in a different timezone to the server sets an alarm, the alarm would fire at the correct time according to the server's local clock rather than the player's. For example, a player in America/New_York connected to a server in Europe/Amsterdam would have a 7:00 AM alarm fire at 1:00 AM local time.

The fix stores an optional Olson timezone name (_timezone) with each alarm. When present, findNextTime() uses that timezone for scheduling instead of the server's localtime(). Backward compatibility is preserved: alarms without a timezone continue to use server local time unchanged.

On the server side, the Jive alarm menu now includes the player's registered timezone in the alarm add/update action params, so it is sent automatically when the player creates or modifies an alarm via the touch UI.

When a player in a different timezone to the server sets an alarm,
the alarm would fire at the correct time according to the server's
local clock rather than the player's. For example, a player in
America/New_York connected to a server in Europe/Amsterdam would
have a 7:00 AM alarm fire at 1:00 AM local time.

The fix stores an optional Olson timezone name (_timezone) with each
alarm. When present, findNextTime() uses that timezone for scheduling
instead of the server's localtime(). Backward compatibility is
preserved: alarms without a timezone continue to use server local
time unchanged.

On the server side, the Jive alarm menu now includes the player's
registered timezone in the alarm add/update action params, so it is
sent automatically when the player creates or modifies an alarm via
the touch UI.

Signed-off-by: Bartosz Oudekerk <lot+github@unreachablehost.net>
The rescan plugin's checkScanTimer() used localtime() (server timezone) to
interpret the scheduled time, causing misfires for players in different
timezones.

- Add rescanplugin settimer command accepting time + timezone params
- Jive menu now passes the player's timezone (from playerpref) when
  scheduling a rescan time via the device UI
- checkScanTimer() overrides TZ/tzset() when a timezone is stored, falling
  back to server localtime() for existing schedules (backwards compatible)
- Web settings UI captures browser timezone via Intl.DateTimeFormat and
  stores it alongside the scheduled time

Signed-off-by: Bartosz Oudekerk <lot+github@unreachablehost.net>
…mezone

When a user sets an alarm time via the web UI, the entered time should be
interpreted in the browser's timezone (where the user and player are), not
the server's timezone.

Capture the browser timezone via Intl.DateTimeFormat and store it on the
alarm object, consistent with the approach used for scheduled rescan.

Signed-off-by: Bartosz Oudekerk <lot+github@unreachablehost.net>
@boudekerk
Copy link
Contributor Author

@michaelherger
Copy link
Member

I'm sorry, that's not going to happen. We're not adding 400+ files to solve one person's issue - which likely could be solved using an environment variable.

@boudekerk
Copy link
Contributor Author

boudekerk commented Feb 25, 2026

@michaelherger It's still work in progress. But thanks for keeping an eye on it. All comments are appreciated.

… scheduling

POSIX::tzset with $ENV{TZ} does not work on Windows, where Perl requires
Windows-native timezone names rather than Olson identifiers. This made
alarm scheduling in a player's timezone silently fall back to the server
timezone on Windows.

Replace with DateTime::TimeZone, which uses a bundled IANA timezone
database and works identically on all platforms.

- Add Slim::Utils::DateTime::localtimeInTZ($epoch, $tzName), a thin
  wrapper around DateTime::TimeZone that returns a localtime()-style
  list in the named Olson timezone, with a per-process cache and a
  graceful fallback to server timezone for unrecognised names
- Use localtimeInTZ in Slim::Utils::Alarm::findNextTime and
  Slim::Plugin::Rescan::Plugin::checkScanTimer, replacing the
  POSIX::tzset save/restore blocks
- Bundle DateTime::TimeZone 2.66 (IANA tzdata 2025c) and its
  dependencies in CPAN/

Signed-off-by: Bartosz Oudekerk <lot+github@unreachablehost.net>
@boudekerk
Copy link
Contributor Author

boudekerk commented Feb 25, 2026

@michaelherger @mw9 @ralph-irving Can I please pick your brain here?

@michaelherger commented on was trying out using DateTime::TimeZone. That will result in the cleanest code IMO. And, crucially, the Strawberry perl you're using for the Windows installer already includes this dependency anyway.

Including it explicitly in lms, it indeed pulls in 400+ files, although ~300 of those simply contain the timezone data.

I see the following options available (let me know if you see others):

  1. Use DateTime::TimeZone (most recently tried approach, works)
    1a. Include it explicitly as a dependency. Works OOTB, everywhere.
    1b. Only include the files we actually use, we can probably trim it down to around ~300-325 files.
    1c. Make it an optional dependency, with a fallback to the server timezone, logging a warning. (Works OOTB on Windows, Linux/MacOS depending on being installed by the sysadmin)
    1d. Only include the dependency for Linux/MacOS (not sure if feasible)
  2. Write our own TZ zoneinfo parser
    2a. Include binary zoneinfo files ourselves (~300 files)
    2b. Include binary zoneinfo files only for Windows in slimserver-platforms
    2c. Accept it doesn't work on Windows, simply log a warning and fall back to server timezone.

1b. is my least favourite as it will increase the maintenance burden for little value. Of the ~400 files the DateTime::TimZone dependency introduces ~300 are simply the timezone data. So trimming it down will be marginal anyway and make dependency updates harder. I'd say either we include it as a dependency or we don't, but let me know if you disagree.

My preference is actually 1a. Clean code and a clear dependency. 400 files seems like a lot, but those are included on Windows anyway and 300 of those are simply small timezone data files. Easy to update as well. My second choice would be 1c.

Please let me know which approach you would prefer and I can make the required changes and put it forward for review. Thanks.

P.S. @michaelherger Your suggestion of an env variable, won't actually work on Windows AFAIK. I'm not sure what workaround for this bug there is available there today.

@boudekerk
Copy link
Contributor Author

Another option is to have 1c with a fallback to 2.
Should work OOTB without any extra files needed on all platforms, but will use different code for both, so not my preference.

@michaelherger
Copy link
Member

Another option is to have 1c with a fallback to 2. Should work OOTB without any extra files needed on all platforms, but will use different code for both, so not my preference.

I wanted to suggest this: use DateTime::TimeZone where available (in particular Windows), fall back to POSIX for the others? As you say the former was available on Windows this should be relatively light weight?

if (main::ISWINDOWS) {
...
}

@boudekerk
Copy link
Contributor Author

@mw9 @ralph-irving Any comments? If not I'll start work next week on the multi-tiered approach as discussed with @michaelherger.

@ralph-irving
Copy link
Contributor

@ralph-irving Any comments? If not I'll start work next week on the multi-tiered approach as discussed.

No I don't really have anything to offer on these changes. I've never used the alarm features of the firmware.
As long as there is agreement that the changes included in a PR are sound for the community firmware I will include it.

@boudekerk boudekerk marked this pull request as draft March 2, 2026 08:21
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.

3 participants