Skip to content

Commit 60d413e

Browse files
committed
[fix][kernel] fix timegm month overflow calculation
When days exceed the number of days in the current month, the original code incorrectly used __spm[t->tm_mon] as the monthly offset, causing wrong date results after crossing month boundaries (e.g. adding 420 days to 2026/08/05 gave 2027/03/01 instead of 2027/08/29). Fix the overflow logic to use (__spm[mon+1] - __spm[mon]) with proper leap-year adjustment for February. Close #11686 Signed-off-by: Hui Su <3164683437@qq.com>
1 parent 991f0ff commit 60d413e

4 files changed

Lines changed: 101 additions & 7 deletions

File tree

components/libc/compilers/common/ctime.c

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,7 @@ time_t timegm(struct tm * const t)
396396
time_t i;
397397
time_t years;
398398

399+
/* 归一化时间结构中的进位字段。 */
399400
if(t == RT_NULL)
400401
{
401402
rt_set_errno(EFAULT);
@@ -422,13 +423,9 @@ time_t timegm(struct tm * const t)
422423
t->tm_year += t->tm_mon / 12;
423424
t->tm_mon %= 12;
424425
}
425-
while (t->tm_mday > __spm[1 + t->tm_mon])
426+
while (t->tm_mday > __spm[t->tm_mon + 1] - __spm[t->tm_mon] + (__isleap(t->tm_year + 1900) && t->tm_mon == 1))
426427
{
427-
if (t->tm_mon == 1 && __isleap(t->tm_year + 1900))
428-
{
429-
--t->tm_mday;
430-
}
431-
t->tm_mday -= __spm[t->tm_mon];
428+
t->tm_mday -= __spm[t->tm_mon + 1] - __spm[t->tm_mon] + (__isleap(t->tm_year + 1900) && t->tm_mon == 1);
432429
++t->tm_mon;
433430
if (t->tm_mon > 11)
434431
{

src/utest/Kconfig

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ menu "Kernel Core"
1010
default n
1111
depends on RT_USING_SMALL_MEM
1212

13+
config RT_UTEST_TIME
14+
bool "Time Conversion Test"
15+
default n
16+
depends on RT_USING_POSIX_CLOCK
17+
1318
config RT_UTEST_OBJECT
1419
select RT_USING_DEVICE
1520
select RT_USING_SEMAPHORE

src/utest/SConscript

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ if GetDepend(['RT_UTEST_MEMHEAP']):
1414
if GetDepend(['RT_UTEST_SMALL_MEM']):
1515
src += ['mem_tc.c']
1616

17+
if GetDepend(['RT_UTEST_TIME']):
18+
src += ['time_tc.c']
19+
1720
if GetDepend(['RT_UTEST_SLAB']):
1821
src += ['slab_tc.c']
1922

@@ -73,4 +76,3 @@ for item in list:
7376
group = group + SConscript(os.path.join(item, 'SConscript'))
7477

7578
Return('group')
76-

src/utest/time_tc.c

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
* Copyright (c) 2006-2026, RT-Thread Development Team
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
/**
8+
* Test Case Name: POSIX Time Conversion Test
9+
*
10+
* Test Objectives:
11+
* - Verify that timegm normalizes dates across month and year boundaries.
12+
* - Verify leap-year February handling.
13+
* - Prevent regression of GitHub Issue #11686.
14+
*/
15+
16+
#include <rtthread.h>
17+
#include <sys/time.h>
18+
#include "utest.h"
19+
20+
/* 验证跨越普通月份边界时的日期归一化。 */
21+
static void timegm_month_boundary_test(void)
22+
{
23+
struct tm time_value = { 0 };
24+
25+
time_value.tm_year = 123;
26+
time_value.tm_mon = 0;
27+
time_value.tm_mday = 32;
28+
29+
timegm(&time_value);
30+
31+
uassert_int_equal(time_value.tm_year, 123);
32+
uassert_int_equal(time_value.tm_mon, 1);
33+
uassert_int_equal(time_value.tm_mday, 1);
34+
}
35+
36+
/* 验证闰年二月的日期归一化。 */
37+
static void timegm_leap_year_test(void)
38+
{
39+
struct tm time_value = { 0 };
40+
41+
time_value.tm_year = 124;
42+
time_value.tm_mon = 0;
43+
time_value.tm_mday = 60;
44+
45+
timegm(&time_value);
46+
47+
uassert_int_equal(time_value.tm_year, 124);
48+
uassert_int_equal(time_value.tm_mon, 1);
49+
uassert_int_equal(time_value.tm_mday, 29);
50+
}
51+
52+
/* 验证大日期偏移场景的日期归一化。 */
53+
static void timegm_large_day_test(void)
54+
{
55+
struct tm time_value = { 0 };
56+
57+
time_value.tm_year = 126;
58+
time_value.tm_mon = 7;
59+
time_value.tm_mday = 425;
60+
time_value.tm_hour = 8;
61+
62+
timegm(&time_value);
63+
64+
uassert_int_equal(time_value.tm_year, 127);
65+
uassert_int_equal(time_value.tm_mon, 8);
66+
uassert_int_equal(time_value.tm_mday, 29);
67+
uassert_int_equal(time_value.tm_hour, 8);
68+
}
69+
70+
/* 初始化时间转换测试。 */
71+
static rt_err_t utest_tc_init(void)
72+
{
73+
return RT_EOK;
74+
}
75+
76+
/* 清理时间转换测试。 */
77+
static rt_err_t utest_tc_cleanup(void)
78+
{
79+
return RT_EOK;
80+
}
81+
82+
/* 执行时间转换测试用例。 */
83+
static void testcase(void)
84+
{
85+
UTEST_UNIT_RUN(timegm_month_boundary_test);
86+
UTEST_UNIT_RUN(timegm_leap_year_test);
87+
UTEST_UNIT_RUN(timegm_large_day_test);
88+
}
89+
90+
UTEST_TC_EXPORT(testcase, "core.time", utest_tc_init, utest_tc_cleanup, 10);

0 commit comments

Comments
 (0)