Re: [PATCH v3] ctime.3: Document how to check errors from mktime(3)

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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


[Index of Archives]     [Kernel Documentation]     [Netdev]     [Linux Ethernet Bridging]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux