Hi all, On Fri, Aug 23, 2024 at 10:56:40PM GMT, Alejandro Colomar wrote: > -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. > > All the 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, > according to WG14 (which themselves refer to the Austin Group for having > researched that). > > Link: <https://lore.kernel.org/linux-man/20240823131024.GD2713@xxxxxxxxxxxxxxxxxxxxxxx/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@xxxxxxxxxxx> > Cc: Vincent Lefevre <vincent@xxxxxxxxxx> > Cc: DJ Delorie <dj@xxxxxxxxxx> > Cc: Carlos O'Donell <carlos@xxxxxxxxxx> > Cc: Xi Ruoyao <xry111@xxxxxxxxxxx> > Cc: Brian Inglis <Brian.Inglis@xxxxxxxxxxxxxxxxxx> > Cc: "Robert C. Seacord" <rcseacord@xxxxxxxxx> > Cc: Jens Gustedt <jens.gustedt@xxxxxxxx> > Cc: Robert Elz <kre@xxxxxxxxxxxxx> > Cc: Andrew Josey <ajosey@xxxxxxxxxxxxx> > Cc: Geoff Clare <gwc@xxxxxxxxxxxxx> > Cc: GNU C Library <libc-alpha@xxxxxxxxxxxxxx> > Cc: Austin Group <austin-group-l@xxxxxxxxxxxxx> > Cc: Hans Åberg <haberg-1@xxxxxxxxx> > Signed-off-by: Alejandro Colomar <alx@xxxxxxxxxx> > --- I've finally applied this patch. Thank you all for your feedback! Especially Paul for finding so many problems in my code! :) Have a lovely night! Alex > > Hi! > > v3 changes: > > - Typos in the example runs (s/2024/2023/) > - s/if/whether/ [Paul] > - Respect the sign of time_t. [Paul] > - Remove unused include. [IWYU] > - Add links and CCs. > > Cheers, > Alex > > Range-diff against v2: > 1: 664cd54a8 ! 1: 795308497 ctime.3: Document how to check errors from mktime(3) > @@ Commit message > according to WG14 (which themselves refer to the Austin Group for having > researched that). > > + Link: <https://lore.kernel.org/linux-man/20240823131024.GD2713@xxxxxxxxxxxxxxxxxxxxxxx/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> > @@ Commit message > Cc: Carlos O'Donell <carlos@xxxxxxxxxx> > Cc: Xi Ruoyao <xry111@xxxxxxxxxxx> > Cc: Brian Inglis <Brian.Inglis@xxxxxxxxxxxxxxxxxx> > + Cc: "Robert C. Seacord" <rcseacord@xxxxxxxxx> > + Cc: Jens Gustedt <jens.gustedt@xxxxxxxx> > + Cc: Robert Elz <kre@xxxxxxxxxxxxx> > + Cc: Andrew Josey <ajosey@xxxxxxxxxxxxx> > + Cc: Geoff Clare <gwc@xxxxxxxxxxxxx> > Cc: GNU C Library <libc-alpha@xxxxxxxxxxxxxx> > + Cc: Austin Group <austin-group-l@xxxxxxxxxxxxx> > + Cc: Hans Åberg <haberg-1@xxxxxxxxx> > Signed-off-by: Alejandro Colomar <alx@xxxxxxxxxx> > > ## man/man3/ctime.3 ## > @@ man/man3/ctime.3: .SH NOTES > +.I (time_t) \-1 > +can represent a valid time > +(one second before the Epoch). > -+To determine if > ++To determine whether > +.BR mktime () > +failed, > +one must use the > @@ man/man3/ctime.3: .SH NOTES > +.RB $\~ "./a.out 2024 02 23 00 17 53 1" ; > +1708640273 > +$ > -+.RB $\~ "./a.out 2024 03 26 02 17 53 \-1" ; > ++.RB $\~ "./a.out 2023 03 26 02 17 53 \-1" ; > +1679793473 > +$ > -+.RB $\~ "./a.out 2024 10 29 02 17 53 \-1" ; > ++.RB $\~ "./a.out 2023 10 29 02 17 53 \-1" ; > +1698542273 > -+.RB $\~ "./a.out 2024 10 29 02 17 53 0" ; > ++.RB $\~ "./a.out 2023 10 29 02 17 53 0" ; > +1698542273 > -+.RB $\~ "./a.out 2024 10 29 02 17 53 1" ; > ++.RB $\~ "./a.out 2023 10 29 02 17 53 1" ; > +1698538673 > +$ > -+.RB $\~ "./a.out 2024 02 29 12 00 00 \-1" ; > ++.RB $\~ "./a.out 2023 02 29 12 00 00 \-1" ; > +1677668400 > +.EE > +.SS Program source: mktime.c > @@ man/man3/ctime.3: .SH NOTES > +.\" SRC BEGIN (mktime.c) > +.EX > +#include <err.h> > -+#include <errno.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[]) > +{ > @@ man/man3/ctime.3: .SH NOTES > + if (tm.tm_wday == \-1) > + err(EXIT_FAILURE, "mktime"); > +\& > -+ printf("%jd\[rs]n", (intmax_t) t); > ++ if (is_signed(time_t)) > ++ printf("%jd\[rs]n", (intmax_t) t); > ++ else > ++ printf("%ju\[rs]n", (uintmax_t) t); > ++\& > + exit(EXIT_SUCCESS); > +} > +.EE > > man/man3/ctime.3 | 104 ++++++++++++++++++++++++++++++++++++++++++++++- > 1 file changed, 103 insertions(+), 1 deletion(-) > > diff --git a/man/man3/ctime.3 b/man/man3/ctime.3 > index 5aec51b79..84d140c71 100644 > --- a/man/man3/ctime.3 > +++ b/man/man3/ctime.3 > @@ -241,7 +241,10 @@ .SH RETURN VALUE > 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 > @@ -412,6 +415,105 @@ .SH NOTES > object types may overwrite the information in any object of the same type > pointed to by the value returned from any previous call to any of them." > 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), > > base-commit: 0813c125d8bf754c40015aa1b31f23e0650584ac > -- > 2.45.2 > -- <https://www.alejandro-colomar.es/>
Attachment:
signature.asc
Description: PGP signature