Skip to content

Commit 339bffb

Browse files
liulangrenaaaRbb666
authored andcommitted
[fix][libc] fix timegm month overflow calculation
The original overflow logic used __spm[tm_mon] as the current month's length when normalizing tm_mday. This produced incorrect dates after crossing month boundaries and left zero or negative days unnormalized. Use the difference between adjacent cumulative day offsets with leap-year handling. Normalize overflow in tm_sec, tm_min, tm_hour, tm_mday, and tm_mon to keep the affected broken-down fields normalized. Add regression tests for month boundaries, leap years, large offsets, zero and negative days, negative time fields, combined overflow, and boundary carry cases. Close RT-Thread#11686 Signed-off-by: Hui Su <3164683437@qq.com>
1 parent 7d1e551 commit 339bffb

7 files changed

Lines changed: 381 additions & 8 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# dependencies
2+
CONFIG_RT_CONSOLEBUF_SIZE=1024
3+
CONFIG_RT_USING_CI_ACTION=y
4+
CONFIG_RT_USING_POSIX_CLOCK=y
5+
CONFIG_RT_UTEST_LIBC_TIME=y

.github/workflows/utest_auto_run.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,8 @@ jobs:
150150
config_file: "components/sal.cfg"
151151
- platform: { UTEST: "A9", RTT_BSP: "bsp/qemu-vexpress-a9", QEMU_ARCH: "arm", QEMU_MACHINE: "vexpress-a9", SD_FILE: "sd.bin", KERNEL: "standard", "SMP_RUN":"" }
152152
config_file: "components/dfs.cfg"
153+
- platform: { UTEST: "A9", RTT_BSP: "bsp/qemu-vexpress-a9", QEMU_ARCH: "arm", QEMU_MACHINE: "vexpress-a9", SD_FILE: "sd.bin", KERNEL: "standard", "SMP_RUN":"" }
154+
config_file: "components/libc.cfg"
153155

154156
env:
155157
TEST_QEMU_ARCH: ${{ matrix.platform.QEMU_ARCH }}

components/libc/compilers/common/ctime.c

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -390,45 +390,83 @@ int stime(const time_t *t)
390390
}
391391
RTM_EXPORT(stime);
392392

393+
/* Normalize the time fields and convert the result to a UTC timestamp. */
393394
time_t timegm(struct tm * const t)
394395
{
395396
time_t day;
396397
time_t i;
397398
time_t years;
399+
int days_in_month;
398400

399401
if(t == RT_NULL)
400402
{
401403
rt_set_errno(EFAULT);
402404
return (time_t)-1;
403405
}
404406

405-
if (t->tm_sec > 60) /* seconds after the minute - [0, 60] including leap second */
407+
if (t->tm_sec < 0 || t->tm_sec > 60) /* seconds after the minute - [0, 60] including leap second */
406408
{
407409
t->tm_min += t->tm_sec / 60;
408410
t->tm_sec %= 60;
411+
if (t->tm_sec < 0)
412+
{
413+
t->tm_sec += 60;
414+
--t->tm_min;
415+
}
409416
}
410-
if (t->tm_min >= 60) /* minutes after the hour - [0, 59] */
417+
if (t->tm_min < 0 || t->tm_min >= 60) /* minutes after the hour - [0, 59] */
411418
{
412419
t->tm_hour += t->tm_min / 60;
413420
t->tm_min %= 60;
421+
if (t->tm_min < 0)
422+
{
423+
t->tm_min += 60;
424+
--t->tm_hour;
425+
}
414426
}
415-
if (t->tm_hour >= 24) /* hours since midnight - [0, 23] */
427+
if (t->tm_hour < 0 || t->tm_hour >= 24) /* hours since midnight - [0, 23] */
416428
{
417429
t->tm_mday += t->tm_hour / 24;
418430
t->tm_hour %= 24;
431+
if (t->tm_hour < 0)
432+
{
433+
t->tm_hour += 24;
434+
--t->tm_mday;
435+
}
419436
}
420-
if (t->tm_mon >= 12) /* months since January - [0, 11] */
437+
if (t->tm_mon < 0 || t->tm_mon >= 12) /* months since January - [0, 11] */
421438
{
422439
t->tm_year += t->tm_mon / 12;
423440
t->tm_mon %= 12;
441+
if (t->tm_mon < 0)
442+
{
443+
t->tm_mon += 12;
444+
--t->tm_year;
445+
}
424446
}
425-
while (t->tm_mday > __spm[1 + t->tm_mon])
447+
while (t->tm_mday <= 0)
426448
{
427-
if (t->tm_mon == 1 && __isleap(t->tm_year + 1900))
449+
if (t->tm_mon == 0)
428450
{
429-
--t->tm_mday;
451+
t->tm_mon = 11;
452+
--t->tm_year;
453+
}
454+
else
455+
{
456+
--t->tm_mon;
457+
}
458+
t->tm_mday += __spm[t->tm_mon + 1] - __spm[t->tm_mon] +
459+
(__isleap(t->tm_year + 1900) && t->tm_mon == 1);
460+
}
461+
while (1)
462+
{
463+
days_in_month = __spm[t->tm_mon + 1] - __spm[t->tm_mon] +
464+
(__isleap(t->tm_year + 1900) && t->tm_mon == 1);
465+
if (t->tm_mday <= days_in_month)
466+
{
467+
break;
430468
}
431-
t->tm_mday -= __spm[t->tm_mon];
469+
t->tm_mday -= days_in_month;
432470
++t->tm_mon;
433471
if (t->tm_mon > 11)
434472
{
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
menu "C Library Test"
2+
3+
config RT_UTEST_LIBC_TIME
4+
bool "Time Conversion Test"
5+
default n
6+
depends on RT_USING_POSIX_CLOCK
7+
8+
endmenu
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Import('rtconfig')
2+
from building import *
3+
4+
cwd = GetCurrentDir()
5+
src = []
6+
CPPPATH = [cwd]
7+
8+
if GetDepend(['RT_UTEST_LIBC_TIME']):
9+
src += ['time_tc.c']
10+
11+
group = DefineGroup('utestcases', src, depend = ['RT_USING_UTESTCASES'], CPPPATH = CPPPATH)
12+
13+
Return('group')

0 commit comments

Comments
 (0)