Repository navigation
Conversation
gmtime_r() split the timestamp with C division, which truncates toward zero:
work = *timep % (24*60*60); /* negative remainder for *timep < 0 */
work = (int)(*timep / (24*60*60));
For a negative timestamp both the day count and the remainder come out
negative, and the year loop below could only walk forward from 1970, so the
result was returned as a valid struct tm holding impossible fields:
-1 -> 1970-01-01 00:00:-1 (want 1969-12-31 23:59:59)
-86400 -> 1970-01-00 00:00:00 (want 1969-12-31 00:00:00)
-31536000 -> 1970-01--364 00:00:00 (want 1969-01-01 00:00:00)
-2208988800 -> 1970-01--25566 00:00:00 (want 1900-01-01 00:00:00)
Normalise to floor division first (borrow one day when the remainder is
negative), compute tm_wday from the normalised day count while keeping the
modulo result non-negative, and let the year walk run backwards for negative
day counts.
Verified on bsp/qemu-vexpress-a9 (QEMU 11.0.0, arm-none-eabi-gcc 15.2.1,
sizeof(time_t) = 8), same probe run against master and against this patch:
t master patched
0 1970-01-01 00:00:00 wday=4 1970-01-01 00:00:00 wday=4
86399 1970-01-01 23:59:59 wday=4 1970-01-01 23:59:59 wday=4
86400 1970-01-02 00:00:00 wday=5 1970-01-02 00:00:00 wday=5
-1 1970-01-01 00:00:-1 wday=4 1969-12-31 23:59:59 wday=3
-86400 1970-01-00 00:00:00 wday=3 1969-12-31 00:00:00 wday=3
-31536000 1970-01--364 wday=-4 1969-01-01 00:00:00 wday=3
-2208988800 1970-01--25566 wday=-6 1900-01-01 00:00:00 wday=1
Non-negative timestamps are unchanged; 1900-01-01 correctly reports Monday.
Refs RT-Thread#11923
|
👋 感谢您对 RT-Thread 的贡献!Thank you for your contribution to RT-Thread! 为确保代码符合 RT-Thread 的编码规范,请在你的仓库中执行以下步骤运行代码格式化工作流(如果格式化CI运行失败)。 🛠 操作步骤 | Steps
完成后,提交将自动更新至 如有问题欢迎联系我们,再次感谢您的贡献!💐 |
📌 Code Review Assignment🏷️ Tag: componentsReviewers: @Maihuanyi Changed Files (Click to expand)
🏷️ Tag: components_libcReviewers: @GorrayLi @mysterywolf Changed Files (Click to expand)
📊 Current Review Status (Last Updated: 2026-10-26 14:38 CST)
📝 Review Instructions
|
|
|
可以增加一下utest的测试。 |
Requested in the review of RT-Thread#11929: pin down gmtime_r() behaviour before 1970. Adds five cases to the existing components.libc.time testcase and checks every field the function fills in, including tm_wday and tm_yday: timestamp expected 0 1970-01-01 00:00:00 wday=4 yday=0 -1 1969-12-31 23:59:59 wday=3 yday=364 -86400 1969-12-31 00:00:00 wday=3 yday=364 -86401 1969-12-30 23:59:59 wday=2 yday=363 -58060800 1968-02-29 00:00:00 wday=4 yday=59 (1968 leap day) Without the fix in the previous commit the new unit fails on every one of the five cases, with all eight field assertions reported, so the test does pin the behaviour down rather than passing by accident: t = -1 1970-01-01 00:00:-1 wday=4 yday=0 t = -86400 1970-01-00 00:00:00 wday=3 yday=-1 t = -86401 1970-01-00 00:00:-1 wday=3 yday=-1 Verified on bsp/qemu-vexpress-a9 with RT_UTEST_LIBC_TIME enabled: with the fix : [ PASSED ] testcase (components.libc.time) without the fix : all five cases FAILED Refs RT-Thread#11923 Refs RT-Thread#11929
|
已按你的要求补上 utest,提交 加在哪
覆盖的五个点按你说的清单逐条对应:
全部日期字段都断言,不只看年月日:
验证:用例确实有鉴别力这是我觉得更需要说明的一点——新用例在未修复的 master 上会失败,不是恒真断言: 带修复: 不带修复(仅把 五个用例每个都是八条断言全失败,说明用例确实锁住了这个缺陷。 环境: 另外之前 issue 里提到的 |
拉取/合并请求描述:(PR description)
[
为什么提交这份PR (why to submit this PR)
修复 #11923。
gmtime_r()用 C 的除法拆时间戳,而 C 的除法向零截断:于是负时间戳会返回一个非 NULL、但字段非法的结构体:
1970-01-01 00:00:-1(wday=4 yday=0)1969-12-31 23:59:591970-01-00 00:00:00(yday=-1)1969-12-31 00:00:001970-01--364 00:00:00(wday=-4 yday=-365)1969-01-01 00:00:001970-01--25566 00:00:00(wday=-6 yday=-25567)1900-01-01 00:00:00不只是秒为负,
tm_mday和tm_wday也会变成明显的非法值。你的解决方案是什么 (what is your solution)
只改
gmtime_r()一个函数,三步:tm_sec/tm_min/tm_hour非负tm_wday用规范化后的天数计算,并保证结果落在 0~6(原来(4 + work) % 7在天数为负时返回负值)while (work < 0) { --i; work += 闰年?366:365; },让早于 1970 的时间戳也能定位到正确年份__isleap()对 1970 之前的年份本身就有效,直接复用。请提供验证的bsp和config (provide the config and bsp)
bsp/qemu-vexpress-a9(sizeof(time_t)=8)1c801efa同一份固件、同一个探针,改前/改后各跑一次:
非负时间戳行为完全未变;1900-01-01 的 wday=1(周一)也对。
两点未包含的内容
int work的类型没改。天数在进入年份循环时仍转int,对 64 位time_t的极端值(超过约 ±580 万年)依然会截断。glibc 在那种情况下返回EOVERFLOW,但这与 [Bug] [libc] gmtime_r 对负时间戳返回非法日历字段 #11923 是两件事,没有混进本次改动。utest 暂未加入。可以在
components/libc/compilers/common/utest/time_tc.c里挂 -1 与 -86400 两个用例,但需要先确认它归属哪个 Kconfig、以及哪些跑 utest 的 BSP 会执行到。若维护者认可当前改法,我可以补上用例后更新本 PR。]
当前拉取/合并请求的状态 Intent for your PR
必须选择一项 Choose one (Mandatory):
代码质量 Code Quality:
我在这个拉取/合并请求中已经考虑了 As part of this pull request, I've considered the following:
#if 0代码,不包含已经被注释了的代码 All redundant code is removed and cleaned up