Skip to content

fix(libkernel): report the host zone's standard offset and daylight saving amount from sceKernelGettimezone - #10499

Merged
adriadam10 merged 3 commits into
boykopovar:mainfrom
frankea:fix/kernel-gettimezone-host-zone
Oct 26, 2026
Merged

adriadam10 merged 3 commits into
boykopovar:mainfrom
frankea:fix/kernel-gettimezone-host-zone

Conversation

@frankea

@frankea frankea commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

What

sceKernelGettimezone wrote {0, 0} for every zone, a silent stub that the debt entry for the calendar conversions mentions ("sceKernelGettimezone and the gettimeofday time zone still report no offset and no daylight saving"). It now fills both fields from the host zone, with the HostZoneAt and HostDaylightAmount helpers the calendar conversions in the same file already use:

  • tz_minuteswest: the standard offset in minutes east of UTC, despite its name, without the daylight saving in effect.
  • tz_dsttime: the zone's daylight saving amount in minutes, 0 for a zone without daylight saving.
  • A null pointer still returns 0x8002000E.

Measurement: PS5 on firmware 13.40 set to US Eastern, 2026-10-19, from an elfldr payload process: sceKernelGettimezone returned 0 with tz_minuteswest -300 and tz_dsttime 60. Summer time was in effect on the run date, so -300 is the standard offset, not the current one, and 60 is the daylight saving amount, not a flag. What the console returns outside summer time, or for a zone without daylight saving, was not measured; this console is the only sample.

The probe resolves sceKernelGettimezone from libkernel in the payload process and calls it on a buffer filled with 0x55, the equivalent of:

int32_t tz[2] = {0x55555555, 0x55555555};
int r = sceKernelGettimezone(tz);
printf("0x%08x minuteswest=%d dsttime=%d\n", (unsigned)r, tz[0], tz[1]);

Its output: sceKernelGettimezone(&tz) -> 0x00000000 minuteswest=-300 dsttime=60. In the same run sceKernelConvertUtcToLocaltime gave local minus UTC of -14400 for 2024-07-01 12:00Z and -18000 for 2024-01-15 12:00Z, with the daylight amount 3600, so the console was on US Eastern. Anyone with a payload loader can repeat it in a zone without daylight saving or in winter.

The debt entry moves out of the long calendar conversion entry into one of its own, which says the values follow the host zone rather than the console's time zone setting and what was not measured. The gettimeofday time zone is unchanged; #9682 touches that one.

Tested

Windows 11 (build 26200), MinGW GCC (WinLibs 15.2) Release build with BUILD_TESTING=ON, each run three times (ctest --repeat until-fail:3).

  • Refreshed onto main ca6e29e after docs(libkernel): record what the local time conversions write on a PS5 #10198 merged (head 02eb825; the libkernel debt conflict resolved by keeping both PRs' entries): guest_freebsd_clocks passes, and so does main ca6e29e.
  • guest_freebsd_clocks gets a case that sets TZ and reads sceKernelGettimezone: UTC0 gives 0 and 0, IST-5:30 330 and 0, BRT+3 -180 and 0, EST5EDT -300 and 60, CET-1CEST 60 and 60; and a null pointer returns 0x8002000E. On Windows the expected daylight amount comes from the C runtime's _get_dstbias, as the test's existing zone cases already do, since a TZ variable does not set it.
  • This PR (4db6ed7, fabe448 with main 97d3346 merged in): guest_freebsd_clocks passes.
  • Control: main 97d3346 with only this PR's GuestFreebsdClocks.cpp: guest_freebsd_clocks aborts on the new case.
  • main 97d3346 with its own test: passes.
  • Linux: @Dom3005's run in the comments (Ubuntu 26.04, GCC 15, glibc 2.43): this PR merged onto main 39a36bb passes 3 of 3, including the TZ cases, where the daylight amount comes from glibc's zone data; the control aborts as on Windows. @TechLevelZero's fleet run (Linux, validation layer on) of 4db6ed763 merged onto main 5b85225ef: no PR-only failures in the 1941 tests tools/select_tests.py selects, on a Ryzen 7 9700X iGPU (RDNA2), an RX 9070 XT (RDNA4), Iris Xe, lavapipe on two CPUs and an NVIDIA A800.
  • tools/check_conventions.py --base origin/main reports no errors; its one warning is the length of the calendar conversion debt entry, which was already over the limit before this PR shortened it.

Overlap

#10198 (orezraey), which added a measured calendar conversion entry right after the debt entry this PR edits, merged first; this branch merges main after it and keeps both entries. #10975 (xXPBSXx) changes the same sceKernelGettimezone body to write {0, 0} through GuestCopyOut_nid_no_patch, so an unusable pointer returns EFAULT; whichever of the two lands second writes this PR's values through that copy.

Checklist

…aving amount from sceKernelGettimezone

sceKernelGettimezone wrote {0, 0} for every zone. A PS5 on firmware
13.40 set to US Eastern returned tz_minuteswest -300 and tz_dsttime 60:
the standard offset in minutes east of UTC, without the summer time in
effect on the run date, and the zone's daylight saving amount in
minutes. Both now come from the host zone, through the same HostZoneAt
and HostDaylightAmount the calendar conversions use.

guest_freebsd_clocks checks five TZ settings (UTC0, IST-5:30, BRT+3,
EST5EDT, CET-1CEST) and the null pointer.

@harisibrar1 harisibrar1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Checked against main 6c52b64, FreeBSD 11.4 sys/sys/time.h / kern_time.c, and shadPS4 23c41333 kernel/time.cpp:

  • This merges cleanly. Main still writes {0, 0} here, and the change reuses the HostZoneAt/HostDaylightAmount helpers already on main. Reading them, the signs look right on both paths: on Windows -_get_timezone and -_get_dstbias give east and a positive DST amount, and on POSIX tm_gmtoff + timezone is the DST amount. I couldn't run the Windows path myself.
  • Worth stating plainly: this departs from FreeBSD's struct timezone, which is "minutes west" plus a DST_* type (DST_USA = 1), so US Eastern would be 300/1 rather than -300/60. shadPS4 returns _timezone/60 and _daylight on Windows, which also differs. So the console run is the only basis here, and it does fit main's existing ConvertUtcToLocaltime entry (seconds east). Could you attach the payload source or its output to the PR, so others can repeat it in a zone without DST or in winter?
  • Minor: once this lands, gettimeofday's tz still reports {0, 0}, while on FreeBSD both come from the same kernel globals. The PR already notes this, so it's fine as a follow-up.

Reviewed with AI assistance (Claude Code); the checks above were run locally on my machine.

@github-actions

github-actions Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

Other open pull requests touch the same code. Merging one will break the other, or both implement the same thing. If this one builds on them, list them in Depends on.

Pull request Conflicting files (lines on main) Same exports Same new files
PR 10975 core/libs/prx/libkernel/Time/Time.cpp L773-774

Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.

Pull request Shared files not compared
PR 4630 docs/dev/debt/libkernel.md
PR 5021 core/libs/prx/libkernel/Time/Time.cpp
docs/dev/debt/libkernel.md
PR 6149 docs/dev/debt/libkernel.md
PR 6936 docs/dev/debt/libkernel.md
PR 7088 docs/dev/debt/libkernel.md
PR 7105 docs/dev/debt/libkernel.md
PR 7121 docs/dev/debt/libkernel.md
PR 7149 docs/dev/debt/libkernel.md
PR 7192 docs/dev/debt/libkernel.md
PR 7201 core/libs/prx/libkernel/Time/Time.cpp
PR 7447 docs/dev/debt/libkernel.md
PR 7509 docs/dev/debt/libkernel.md
PR 7804 core/libs/prx/libkernel/Time/Time.cpp
docs/dev/debt/libkernel.md
PR 8605 docs/dev/debt/libkernel.md
PR 9156 docs/dev/debt/libkernel.md
PR 9199 docs/dev/debt/libkernel.md
PR 9284 docs/dev/debt/libkernel.md
PR 9293 docs/dev/debt/libkernel.md
PR 9297 docs/dev/debt/libkernel.md
PR 9302 docs/dev/debt/libkernel.md
PR 9389 docs/dev/debt/libkernel.md
PR 9557 docs/dev/debt/libkernel.md
PR 9566 docs/dev/debt/libkernel.md
PR 9575 docs/dev/debt/libkernel.md
PR 9947 docs/dev/debt/libkernel.md
PR 10442 docs/dev/debt/libkernel.md
PR 10552 docs/dev/debt/libkernel.md
PR 10621 docs/dev/debt/libkernel.md
PR 11085 docs/dev/debt/libkernel.md
PR 11264 docs/dev/debt/libkernel.md
PR 11267 docs/dev/debt/libkernel.md
PR 11341 docs/dev/debt/libkernel.md

@MarcZX10 MarcZX10 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The code and boundary tests look consistent with the measured console result and with the existing host-zone helpers. The author reports the focused Windows test and negative control; Linux and hosted build jobs did not run, and I did not rerun tests locally.

One overlap item needs an agreed order before approval: #10198 is still open, and the overlap report identifies a conflict on the same libkernel debt line this PR edits. Please coordinate with that author and declare which change lands first under Depends on (or update the other PR's checklist to establish the reverse order). #9682 is separately scoped to gettimeofday and is already called out accurately in the description.

@Dom3005

Dom3005 commented Oct 24, 2026

Copy link
Copy Markdown
Contributor

Linux run (Ubuntu 26.04 container, GCC 15, glibc 2.43, CMake + Ninja, Release, -DBUILD_TESTING=ON, ctest --repeat until-fail:3)

  • fabe44851 merged onto main 39a36bbec: guest_freebsd_clocks passes, 3 of 3. That includes the new TZ cases (UTC0, IST-5:30, BRT+3, EST5EDT, CET-1CEST), where the daylight amount comes from glibc's zone data rather than _get_dstbias.
  • Control, main 39a36bbec with only this PR's GuestFreebsdClocks.cpp: guest_freebsd_clocks aborts, as on Windows.
  • main d53b52be7 with its own test: passes.

This covers the "Not run on Linux" item.

…e-host-zone

# Conflicts:
#	core/libs/tests/GuestFreebsdClocks.cpp
@frankea

frankea commented Oct 26, 2026

Copy link
Copy Markdown
Contributor Author

@MarcZX10 This PR now lists Depends on: #10198; once #10198 merges I'll merge main again and resolve the conflict next to its entry. Since your review, #10975 has also changed sceKernelGettimezone (it writes {0, 0} through GuestCopyOut_nid_no_patch so an unusable pointer returns EFAULT); whichever of the two lands second writes this PR's values through that copy. Both are in the new Overlap section. Merged main 97d3346 (4db6ed7; the only conflict was the test's declarations, which moved to libkernel/include/Api.hpp) and retested: guest_freebsd_clocks passes three times, the control aborts on the new case, and main passes.

@harisibrar1 the probe and its output are in the Measurement paragraph now: a plain call on a 0x55-filled buffer, plus the same run's local-time conversions that show the zone.

@Dom3005 thanks for the Linux run; the body points to it.

AI-assisted: yes (Claude Code)

@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 26, 2026
@TechLevelZero

Copy link
Copy Markdown
Contributor

Tested 4db6ed763 merged onto main 5b85225ef, with the tests tools/select_tests.py selects, on main and on the merge (Linux, Vulkan validation layer on, PR-only failures rerun alone 3 times on each side):

Hardware Driver Result
Ryzen 7 9700X iGPU, RDNA2 Mesa 26.0.8-1ubuntu0.3 pass, no PR-only failures (1941 tests)
RX 9070 XT, RDNA4 Mesa 26.0.8-1ubuntu0.3 pass, no PR-only failures (1941 tests)
Iris Xe (TGL GT2), i7-1185G7 Mesa 26.0.8-1ubuntu0.3 pass, no PR-only failures (1941 tests)
lavapipe on Intel Xeon Gold 6252 Mesa 26.0.8-1ubuntu0.3 (LLVM 21.1.8) pass, no PR-only failures (1941 tests)
lavapipe on AMD EPYC 7281 Mesa 26.0.8-1ubuntu0.3 (LLVM 21.1.8) pass, no PR-only failures (1941 tests)
NVIDIA A800, RHEL 10.2 host 610.57.04 pass, no PR-only failures (1941 tests)

Test suite only; no title was run.

AI-assisted: yes (Claude Code)

@frankea

frankea commented Oct 26, 2026

Copy link
Copy Markdown
Contributor Author

@MarcZX10 A correction to my last comment: orezraey had already proposed on #10198 (10/24) that this PR land first, and #10198 lists Depends on: #10499, so with my update the two PRs each depended on the other. This PR now has Depends on: none; #10198 merges main after it. The body also cites @TechLevelZero's Linux fleet run (no PR-only failures in 1941 tests on six GPUs).

AI-assisted: yes (Claude Code)

@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 26, 2026
@frankea

frankea commented Oct 26, 2026

Copy link
Copy Markdown
Contributor Author

@MarcZX10 #10198 landed first after all (today, 15:37), so I merged main ca6e29e here (head 02eb825). The only conflict was the libkernel debt block both PRs edit; the merge keeps #10198's new measured entry and this PR's two changes (the conversion entry no longer says sceKernelGettimezone reports no offset, and the sceKernelGettimezone entry). The body's order note is updated, and guest_freebsd_clocks is rerun on the merge: the first bullet under Tested.

AI-assisted: yes (Claude Code)

@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 26, 2026
@adriadam10 adriadam10 self-assigned this Oct 26, 2026
@adriadam10
adriadam10 dismissed MarcZX10’s stale review October 26, 2026 18:36

Dismissing a stale review: the requested changes were addressed after it (new head) and the merge queue blocks on it. Please request changes again if anything is still open.

@adriadam10
adriadam10 merged commit d891568 into boykopovar:main Oct 26, 2026
12 checks passed
@adriadam10

Copy link
Copy Markdown
Collaborator

Checked HostZoneAt/HostDaylightAmount signs on both paths, the new GuestFreebsdClocks cases (they fail on main, which writes {0, 0}), the null return, and the debt entry against the measured console value; conflict with #10198 resolved on this head; merged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants