Activate previously added patch to improve mktime documentation and

add 3 related upstream patches including an example.

Related: RHEL-58342
This commit is contained in:
Patsy Griffin 2025-06-19 22:25:44 -04:00
parent b1d742a01a
commit f093551d4c
5 changed files with 387 additions and 7 deletions

View File

@ -0,0 +1,173 @@
commit e541b219854e6be90b5628f88cd62edb44c9e9f2
Author: Alejandro Colomar <alx@kernel.org>
Date: Fri Aug 23 14:34:00 2024 +0200
ctime.3: Document how to check errors from mktime(3)
-1 is a valid successful time_t, for one second before the Epoch. And
mktime(3) is allowed (like most libc calls) to set errno on success.
This makes it impossible to determine errors from the return value or
errno.
ISO C specifies that tp->tm_wday is unmodified after a failed call, and
puts an example where this is used to determine errors. It is indeed
the only way to check for errors from this call.
Document this detail in the RETURN VALUE section, add a CAVEATS section
that warns about this, and write an example program that shows how to
properly call this function.
Most code I've been able to find in several search engines either
doesn't check for errors after mktime(3), or checks them incorrectly, so
this documentation should help fix those.
This is guaranteed since ISO C23 and POSIX.1-2024. Prior to those
standards, there was no standard way to check for errors. However,
there are no known implementations that do not conform to this.
Link: <https://lore.kernel.org/linux-man/20240823131024.GD2713@cventin.lip.ens-lyon.fr/T/#t>
Link: <https://lore.kernel.org/linux-man/6un6baaq5tez23irtycuvzqtuh7a4sdrf2px7tnyb3y6iqoxmq@2ofln4cd27ep/T/#t>
Link: <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3147.txt>
Link: <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3148.doc>
Link: <https://austingroupbugs.net/view.php?id=1614>
Link: <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3220.pdf#subsubsection.7.29.2.3>
Reported-by: Paul Eggert <eggert@cs.ucla.edu>
Cc: Vincent Lefevre <vincent@vinc17.net>
Cc: DJ Delorie <dj@redhat.com>
Cc: Carlos O'Donell <carlos@redhat.com>
Cc: Xi Ruoyao <xry111@xry111.site>
Cc: Brian Inglis <Brian.Inglis@SystematicSW.ab.ca>
Cc: "Robert C. Seacord" <rcseacord@gmail.com>
Cc: Jens Gustedt <jens.gustedt@inria.fr>
Cc: Robert Elz <kre@munnari.oz.au>
Cc: Andrew Josey <ajosey@opengroup.org>
Cc: Geoff Clare <gwc@opengroup.org>
Cc: Hans Åberg <haberg-1@telia.com>
Cc: GNU C Library <libc-alpha@sourceware.org>
Cc: Austin Group <austin-group-l@opengroup.org>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
Conflicts:
Minor wording differences
diff -Nrup a/man3/ctime.3 b/man3/ctime.3
--- a/man3/ctime.3 2023-04-02 20:27:24.000000000 -0400
+++ b/man3/ctime.3 2025-06-18 09:39:54.782440669 -0400
@@ -247,7 +247,10 @@ expressed as a value of type
On error,
.BR mktime ()
returns the value
-.IR "(time_t)\ \-1" .
+.IR "(time_t)\ \-1" ,
+and leaves the
+.I tm->tm_wday
+member unmodified.
The remaining functions return NULL on error.
On error,
.I errno
@@ -400,6 +403,105 @@ a broken-down time structure and an arra
Execution of any of the functions may overwrite the information returned
in either of these objects by any of the other functions."
This can occur in the glibc implementation.
+.SH CAVEATS
+.SS mktime()
+.I (time_t) \-1
+can represent a valid time
+(one second before the Epoch).
+To determine whether
+.BR mktime ()
+failed,
+one must use the
+.I tm->tm_wday
+field.
+See the example program in EXAMPLES.
+.SH EXAMPLES
+The following shell session shows sample runs of the program:
+.P
+.in +4n
+.EX
+.RB $\~ "TZ=UTC ./a.out 1969 12 31 23 59 59 0" ;
+\-1
+$
+.RB $\~ "export TZ=Europe/Madrid" ;
+$
+.RB $\~ "./a.out 2147483647 2147483647 00 00 00 00 -1" ;
+a.out: mktime: Value too large for defined data type
+$
+.RB $\~ "./a.out 2024 08 23 00 17 53 \-1" ;
+1724365073
+.RB $\~ "./a.out 2024 08 23 00 17 53 0" ;
+1724368673
+.RB $\~ "./a.out 2024 08 23 00 17 53 1" ;
+1724365073
+$
+.RB $\~ "./a.out 2024 02 23 00 17 53 \-1" ;
+1708643873
+.RB $\~ "./a.out 2024 02 23 00 17 53 0" ;
+1708643873
+.RB $\~ "./a.out 2024 02 23 00 17 53 1" ;
+1708640273
+$
+.RB $\~ "./a.out 2023 03 26 02 17 53 \-1" ;
+1679793473
+$
+.RB $\~ "./a.out 2023 10 29 02 17 53 \-1" ;
+1698542273
+.RB $\~ "./a.out 2023 10 29 02 17 53 0" ;
+1698542273
+.RB $\~ "./a.out 2023 10 29 02 17 53 1" ;
+1698538673
+$
+.RB $\~ "./a.out 2023 02 29 12 00 00 \-1" ;
+1677668400
+.EE
+.SS Program source: mktime.c
+\&
+.\" SRC BEGIN (mktime.c)
+.EX
+#include <err.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+\&
+#define is_signed(T) ((T) \-1 < 1)
+\&
+int
+main(int argc, char *argv[])
+{
+ char **p;
+ time_t t;
+ struct tm tm;
+\&
+ if (argc != 8) {
+ fprintf(stderr, "Usage: %s yyyy mm dd HH MM SS isdst\[rs]n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+\&
+ p = &argv[1];
+ tm.tm_year = atoi(*p++) \- 1900;
+ tm.tm_mon = atoi(*p++) \- 1;
+ tm.tm_mday = atoi(*p++);
+ tm.tm_hour = atoi(*p++);
+ tm.tm_min = atoi(*p++);
+ tm.tm_sec = atoi(*p++);
+ tm.tm_isdst = atoi(*p++);
+\&
+ tm.tm_wday = \-1;
+ t = mktime(&tm);
+ if (tm.tm_wday == \-1)
+ err(EXIT_FAILURE, "mktime");
+\&
+ if (is_signed(time_t))
+ printf("%jd\[rs]n", (intmax_t) t);
+ else
+ printf("%ju\[rs]n", (uintmax_t) t);
+\&
+ exit(EXIT_SUCCESS);
+}
+.EE
+.\" SRC END
.SH SEE ALSO
.BR date (1),
.BR gettimeofday (2),

View File

@ -0,0 +1,32 @@
commit 6a7f1461b0e5474d50ef1920558dec103c0c058f
Author: Alejandro Colomar <alx@kernel.org>
Date: Sat Aug 24 00:20:49 2024 +0200
ctime.3: Move NOTES to a subsection within CAVEATS
Signed-off-by: Alejandro Colomar <alx@kernel.org>
Conflicts:
Minor wording differences
diff -Nrup a/man3/ctime.3 b/man3/ctime.3
--- a/man3/ctime.3 2025-06-18 16:14:51.847871469 -0400
+++ b/man3/ctime.3 2025-06-18 16:25:22.406139771 -0400
@@ -374,7 +374,8 @@ POSIX.1-2001.
POSIX.1-2001.
Marked obsolete in POSIX.1-2008 (recommending
.BR strftime (3)).
-.SH NOTES
+.SH CAVEATS
+.SS Thread safety
The four functions
.BR asctime (),
.BR ctime (),
@@ -403,7 +404,6 @@ a broken-down time structure and an arra
Execution of any of the functions may overwrite the information returned
in either of these objects by any of the other functions."
This can occur in the glibc implementation.
-.SH CAVEATS
.SS mktime()
.I (time_t) \-1
can represent a valid time

View File

@ -23,8 +23,8 @@ Signed-off-by: Alejandro Colomar <alx@kernel.org>
diff --git a/man/man3/ctime.3 b/man/man3/ctime.3
index 0ad2b530f..53abab6d9 100644
--- a/man/man3/ctime.3
+++ b/man/man3/ctime.3
--- a/man3/ctime.3
+++ b/man3/ctime.3
@@ -427,6 +427,45 @@ one must use the
.I tm->tm_wday
field.

View File

@ -0,0 +1,162 @@
commit bd576aaf5fce40100133c1d48c02eacbf25594c8
Author: Alejandro Colomar <alx@kernel.org>
Date: Sat Aug 24 00:51:55 2024 +0200
ctime.3: EXAMPLES: Document how to detect invalid or ambiguous times
This example documents how to detect some corner cases of mktime(3),
such as DST transitions and other jumps in the calendar.
Link: <https://www.redhat.com/en/blog/brief-history-mktime>
Cc: DJ Delorie <dj@redhat.com>
Cc: Carlos O'Donell <carlos@redhat.com>
Cc: Paul Eggert <eggert@cs.ucla.edu>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
diff --git a/man3/ctime.3 b/man/man3/ctime.3
index 53abab6d9..acd6f1565 100644
--- a/man3/ctime.3
+++ b/man3/ctime.3
@@ -467,6 +467,14 @@ For example, a change in the timezone definition
may cause a clock time to be repeated or skipped
without a corresponding DST change.
.SH EXAMPLES
+The program below defines a wrapper that
+allows detecting invalid and ambiguous times,
+with
+.B EINVAL
+and
+.BR ENOTUNIQ ,
+respectively.
+.P
The following shell session shows sample runs of the program:
.P
.in +4n
@@ -482,6 +490,7 @@ $
.RB $\~ "./a.out 2024 08 23 00 17 53 \-1" ;
1724365073
.RB $\~ "./a.out 2024 08 23 00 17 53 0" ;
+a.out: my_mktime: Invalid argument
1724368673
.RB $\~ "./a.out 2024 08 23 00 17 53 1" ;
1724365073
@@ -491,12 +500,15 @@ $
.RB $\~ "./a.out 2024 02 23 00 17 53 0" ;
1708643873
.RB $\~ "./a.out 2024 02 23 00 17 53 1" ;
+a.out: my_mktime: Invalid argument
1708640273
$
.RB $\~ "./a.out 2023 03 26 02 17 53 \-1" ;
+a.out: my_mktime: Invalid argument
1679793473
$
.RB $\~ "./a.out 2023 10 29 02 17 53 \-1" ;
+a.out: my_mktime: Name not unique on network
1698542273
.RB $\~ "./a.out 2023 10 29 02 17 53 0" ;
1698542273
@@ -504,6 +516,7 @@ $
1698538673
$
.RB $\~ "./a.out 2023 02 29 12 00 00 \-1" ;
+a.out: my_mktime: Invalid argument
1677668400
.EE
.SS Program source: mktime.c
@@ -511,13 +524,17 @@ $
.\" SRC BEGIN (mktime.c)
.EX
#include <err.h>
+#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include <time.h>
\&
#define is_signed(T) ((T) \-1 < 1)
\&
+time_t my_mktime(struct tm *tp);
+\&
int
main(int argc, char *argv[])
{
@@ -539,10 +556,13 @@ main(int argc, char *argv[])
tm.tm_sec = atoi(*p++);
tm.tm_isdst = atoi(*p++);
\&
+ errno = 0;
tm.tm_wday = \-1;
- t = mktime(&tm);
+ t = my_mktime(&tm);
if (tm.tm_wday == \-1)
err(EXIT_FAILURE, "mktime");
+ if (errno == EINVAL || errno == ENOTUNIQ)
+ warn("my_mktime");
\&
if (is_signed(time_t))
printf("%jd\[rs]n", (intmax_t) t);
@@ -551,6 +571,62 @@ main(int argc, char *argv[])
\&
exit(EXIT_SUCCESS);
}
+\&
+time_t
+my_mktime(struct tm *tp)
+{
+ int e, isdst;
+ time_t t;
+ struct tm tm;
+ unsigned char wday[sizeof(tp\->tm_wday)];
+\&
+ e = errno;
+\&
+ tm = *tp;
+ isdst = tp\->tm_isdst;
+\&
+ memcpy(wday, &tp\->tm_wday, sizeof(wday));
+ tp\->tm_wday = \-1;
+ t = mktime(tp);
+ if (tp\->tm_wday == \-1) {
+ memcpy(&tp\->tm_wday, wday, sizeof(wday));
+ return \-1;
+ }
+\&
+ if (isdst == \-1)
+ tm.tm_isdst = tp\->tm_isdst;
+\&
+ if ( tm.tm_sec != tp\->tm_sec
+ || tm.tm_min != tp\->tm_min
+ || tm.tm_hour != tp\->tm_hour
+ || tm.tm_mday != tp\->tm_mday
+ || tm.tm_mon != tp\->tm_mon
+ || tm.tm_year != tp\->tm_year
+ || tm.tm_isdst != tp\->tm_isdst)
+ {
+ errno = EINVAL;
+ return t;
+ }
+\&
+ if (isdst != \-1)
+ goto out;
+\&
+ tm = *tp;
+ tm.tm_isdst = !tm.tm_isdst;
+\&
+ tm.tm_wday = \-1;
+ mktime(&tm);
+ if (tm.tm_wday == \-1)
+ goto out;
+\&
+ if (tm.tm_isdst != tp\->tm_isdst) {
+ errno = ENOTUNIQ;
+ return t;
+ }
+out:
+ errno = e;
+ return t;
+}
.EE
.\" SRC END
.SH SEE ALSO

View File

@ -8,7 +8,7 @@
Summary: Linux kernel and C library user-space interface documentation
Name: man-pages
Version: 6.04
Release: 3%{?dist}
Release: 4%{?dist}
# List of licenses with examples of man-pages using them
# BSD-2-Clause: man-pages/man5/elf.5
# BSD-3-Clause: man-pages/man3/list.3
@ -62,17 +62,25 @@ Patch21: man-pages-3.42-close.patch
# Resolves: RHEL-53953
Patch22: 0001-dlinfo.3-Document-the-RTLD_DI_PHDR-request.patch
# Upstream commit: https://git.kernel.org/pub/scm/docs/man-pages/man-pages.git/commit/man/man3/ctime.3?id=9fdca71e4aba3605caa88b0c9eff3dc2c3f6fcfe
# Upstream patches to provide more info on mktime().
# Resolves: RHEL-58342
Patch23: 0002-ctime.3-CAVEATS-Add-note-about-tm_isdst-handling-in-.patch
Patch23: 0000-ctime.3-Document-how-to-check-errors-from-mktime.patch
Patch24: 0001-ctime.3-Move-NOTES-to-a-subsection-within-CAVEATS.patch
Patch25: 0002-ctime.3-CAVEATS-Add-note-about-tm_isdst-handling-in-.patch
Patch26: 0003-ctime.3-EXAMPLES-Document-how-to-detect-invalid-or-ambiguous-times.patch
%description
A large collection of manual pages from the Linux Documentation Project (LDP).
%prep
%setup -q -a 1 -a 2
%patch21 -p1
%patch22 -p1
%patch -p1 -P 21
%patch -p1 -P 22
%patch -p1 -P 23
%patch -p1 -P 24
%patch -p1 -P 25
%patch -p1 -P 26
# rename posix README so we don't have conflict
mv %{posix_name}/README %{posix_name}/%{posix_name_rel}.README
@ -138,6 +146,11 @@ fi
%{_mandir}/man*/*
%changelog
* Wed Jun 18 2025 Patsy Griffin <patsy@redhat.com> - 6.04-4
- Activate previously added patch to improve mktime documentation and
add 3 related upstream patches including an example.
Related: RHEL-58342
* Tue Oct 01 2024 Lukas Javorsky <ljavorsk@redhat.com> - 6.04-3
- Add note about tm_isdst handling in mktime(3)
- Resolves: RHEL-58342