From patchwork Fri Aug 23 14:37:54 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alejandro Colomar X-Patchwork-Id: 96400 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 85F7F3875DDB for ; Fri, 23 Aug 2024 14:38:21 +0000 (GMT) X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from dfw.source.kernel.org (dfw.source.kernel.org [IPv6:2604:1380:4641:c500::1]) by sourceware.org (Postfix) with ESMTPS id 9F11D3861014 for ; Fri, 23 Aug 2024 14:37:58 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 9F11D3861014 Authentication-Results: sourceware.org; dmarc=pass (p=quarantine dis=none) header.from=kernel.org Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=kernel.org ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 9F11D3861014 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2604:1380:4641:c500::1 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1724423880; cv=none; b=Rik4Cxhq+mH6dnPPn1EGlBXWcfyFwXFFmMsWYcJmdZudbS8fYIom/S7rym9doKkJulKypq8pFJkb8nBRpNIv4PTq2odL1ZqSKp7ka5R2ugXTkMQRktdBHxu1nyquYCHVfWiEWfggM15KTQTXAU7vtdobPCJxQ8nqAMb0mIMBLDY= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1724423880; c=relaxed/simple; bh=woRr0nYF9VkBv1Z++ziMEb/XhCyJ0gN2jBLlE4UCYIU=; h=DKIM-Signature:Date:From:To:Subject:Message-ID:MIME-Version; b=QjfkNRjDNUSfIY1njIWGL7pzw5Gk3bARNwiA1NDJC7DCBssxtxca0wqzPiz6ILxVI2ezZAcdyyPjaF8KiTiWLWDhDnhTKPplR2CY5soqnNDIggbtHmn3zdd92hNhu9dXrRbdcxz+AXeX0Ji6sV2vF+OowPclhxB7GnknvF1tvkw= ARC-Authentication-Results: i=1; server2.sourceware.org Received: from smtp.kernel.org (transwarp.subspace.kernel.org [100.75.92.58]) by dfw.source.kernel.org (Postfix) with ESMTP id 306BF6122C; Fri, 23 Aug 2024 14:37:58 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id C037EC32786; Fri, 23 Aug 2024 14:37:55 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1724423877; bh=woRr0nYF9VkBv1Z++ziMEb/XhCyJ0gN2jBLlE4UCYIU=; h=Date:From:To:Cc:Subject:In-Reply-To:From; b=hNGa0Sd2T3uIReD81OY9Sank81N4mdo4++JFyXS5E3SHl58hJNlhS8SLPQpwaUySP RNsbFBdGl9BG1mA9jy50wPRdIluhgr36rye3AQWG4ybnEjaL/SICftVI8/GLW4imQ4 GfCr3N++sL4I+DjVjYNDc6SQSlIoon98m/8Tv3TRfuwm26XAlFzXmnQIH4NthmSgbN JQKMM3Loar/I1fxVZa908TA8mNd0hiwXtnqd0Dn+Kj08swgMknNFSitFd/LfqnyzSj C3ut+H5Dggqho/telqCyZywADpHegFgvL7pSFCJpXNC4MEGfqUgPzxxL5c8ygJiAWm nlVAmLmGwoogw== Date: Fri, 23 Aug 2024 16:37:54 +0200 From: Alejandro Colomar To: Alejandro Colomar Cc: linux-man@vger.kernel.org, Paul Eggert , Vincent Lefevre , DJ Delorie , Carlos O'Donell , Xi Ruoyao , Brian Inglis , "Robert C. Seacord" , Jens Gustedt , GNU C Library Subject: [PATCH v2] ctime.3: Document how to check errors from mktime(3) Message-ID: <664cd54a8ee998fd3a07ffc6c9e6fe9d6117620f.1724423646.git.alx@kernel.org> X-Mailer: git-send-email 2.45.2 MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: <433eddc3e5fed0230183aeb178c08ccf247f3da0.1724417835.git.alx@kernel.org> X-Spam-Status: No, score=-10.1 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, SPF_HELO_NONE, SPF_PASS, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org -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: Link: Link: Link: Reported-by: Paul Eggert Cc: Vincent Lefevre Cc: DJ Delorie Cc: Carlos O'Donell Cc: Xi Ruoyao Cc: Brian Inglis Cc: "Robert C. Seacord" Cc: Jens Gustedt Cc: GNU C Library Signed-off-by: Alejandro Colomar --- Hi! This v2 changes the use of INT_MIN to just -1, which is simpler, and is blessed by POSIX. Cheers, Alex Range-diff against v1: 1: 433eddc3e ! 1: 664cd54a8 ctime.3: Document how to check errors from mktime(3) @@ Commit message 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: + Link: + Link: + Link: Reported-by: Paul Eggert + Cc: Vincent Lefevre Cc: DJ Delorie Cc: Carlos O'Donell Cc: Xi Ruoyao - Cc: Vincent Lefevre + Cc: Brian Inglis Cc: GNU C Library Signed-off-by: Alejandro Colomar @@ man/man3/ctime.3: .SH NOTES +.EX +#include +#include -+#include +#include +#include +#include @@ man/man3/ctime.3: .SH NOTES + tm.tm_sec = atoi(*p++); + tm.tm_isdst = atoi(*p++); +\& -+ tm.tm_wday = INT_MIN; ++ tm.tm_wday = \-1; + t = mktime(&tm); -+ if (tm.tm_wday == INT_MIN) ++ if (tm.tm_wday == \-1) + err(EXIT_FAILURE, "mktime"); +\& + printf("%jd\[rs]n", (intmax_t) t); man/man3/ctime.3 | 99 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 98 insertions(+), 1 deletion(-) base-commit: 0813c125d8bf754c40015aa1b31f23e0650584ac diff --git a/man/man3/ctime.3 b/man/man3/ctime.3 index 5aec51b79..68f9277b1 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,100 @@ .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 if +.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 2024 03 26 02 17 53 \-1" ; +1679793473 +$ +.RB $\~ "./a.out 2024 10 29 02 17 53 \-1" ; +1698542273 +.RB $\~ "./a.out 2024 10 29 02 17 53 0" ; +1698542273 +.RB $\~ "./a.out 2024 10 29 02 17 53 1" ; +1698538673 +$ +.RB $\~ "./a.out 2024 02 29 12 00 00 \-1" ; +1677668400 +.EE +.SS Program source: mktime.c +\& +.\" SRC BEGIN (mktime.c) +.EX +#include +#include +#include +#include +#include +#include +\& +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"); +\& + printf("%jd\[rs]n", (intmax_t) t); + exit(EXIT_SUCCESS); +} +.EE +.\" SRC END .SH SEE ALSO .BR date (1), .BR gettimeofday (2),