From patchwork Thu Aug 22 23:49:52 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Alejandro Colomar X-Patchwork-Id: 1975893 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=kernel.org header.i=@kernel.org header.a=rsa-sha256 header.s=k20201202 header.b=PF3gTzud; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=sourceware.org (client-ip=8.43.85.97; helo=server2.sourceware.org; envelope-from=libc-alpha-bounces~incoming=patchwork.ozlabs.org@sourceware.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [8.43.85.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4Wqrcy49bqz1yNm for ; Fri, 23 Aug 2024 17:03:14 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id CFA653860762 for ; Fri, 23 Aug 2024 07:03:12 +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 E44363860C2C for ; Fri, 23 Aug 2024 07:02:50 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org E44363860C2C 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 E44363860C2C 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=1724396573; cv=none; b=pnZKkVZyswA+DgmdKiRVeJB/2peERstqKb9b0/bAtdqB2fcPqCiOKCL4bVkdxwdSEYO2hbj7kJW/K1rT3t5RJOSPGLhaf8asOHQR34lnckQ7MInM109+sdmpndkyFPVQownHTcJ0bN2AfNQVT6vGQyKLOKuD65Watnw0i4opGxI= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1724396573; c=relaxed/simple; bh=T4JvnyraaKof3CIbv1mF/+r/o1D68I4jM9cZcaViYQg=; h=DKIM-Signature:Date:From:To:Subject:Message-ID:MIME-Version; b=HDeXqpfv5ovkayNqc4jH0FZjRz6brTr6umdMhDxwoV4TZTPLlcQ7AKO/k+9yD+yx1e0JDzZLpQpVc/RH9x6cTDrp8rS56mnIBQpEOKKTPPQvhBMQbuVKRSCgz6NIyqG+IgMPGh6G9p7loJy4n8zq/ggaa1UsvhYhlfZJOj8uOHo= 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 8570D6129A for ; Fri, 23 Aug 2024 07:02:50 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id C03A1C32786 for ; Fri, 23 Aug 2024 07:02:49 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1724396570; bh=T4JvnyraaKof3CIbv1mF/+r/o1D68I4jM9cZcaViYQg=; h=Resent-From:Resent-Date:Resent-To:Date:From:To:Cc:Subject: References:In-Reply-To:From; b=PF3gTzud9YE7pbyGhN943FrMeiOUR7EnyKJqC7BRUmWI5FF64QsBGL/LG4EuagXN/ nt7lkZa6QuSdxzmYtiV4l0RrJ7mgzcoFIwYcvu3rdgh5raJ5Ct80heceQtuc0SBDi4 Aa7ZCRTanJKi+Cx+I1oT1Dxm6B3FnSvc+nYwHy97y1yG5W3DTY1cthe8nOJ/vhfSuI ta55WAGy141rTsM++PYcHG/AAFnvZ65+7udZIzE8EsZTEJpcUJzMlQ7VxAklzlUUJ+ 65eRCq7r+JSiWq++62e9Rl2Ce8qaStJSm5OtwTqcLxrA2RlpuF3OMkdq2+AzN+cklZ 215cQa4IRq10A== Resent-From: Alejandro Colomar Resent-Date: Fri, 23 Aug 2024 09:02:47 +0200 Resent-Message-ID: Resent-To: libc-alpha@sourceware.org Date: Fri, 23 Aug 2024 01:49:52 +0200 From: Alejandro Colomar To: Alejandro Colomar , DJ Delorie , Paul Eggert Cc: linux-man@vger.kernel.org, carlos@redhat.com Subject: [PATCH v3] ctime.3: EXAMPLES: Add example program Message-ID: X-Mailer: git-send-email 2.45.2 References: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: 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~incoming=patchwork.ozlabs.org@sourceware.org This example documents the corner cases of mktime(3), such as what happens during DST transitions, and other jumps in the calendar. Link: Reported-by: DJ Delorie Cc: Paul Eggert Signed-off-by: Alejandro Colomar --- Hi DJ, Paul! Below is the rendered text. I've tested this program with several "weird" times, and it all makes sense. Please review. I call it v3, since it supersedes DJ's patches. Have a lovely night! Alex EXAMPLES Passing an invalid time to mktime() or an invalid tm‐>tm_isdst value yields unspecified results. Also, passing the value -1 in tm‐>tm_isdst will result in an ambiguous time during some DST transitions, which will also yield an unspecified result. The program below uses a wrapper that allows detecting invalid and ambiguous times, with EINVAL and ENOTUNIQ, respectively. The following shell session shows sample runs of the program: $ export TZ=Europe/Madrid; $ $ ./a.out 2024 08 23 00 17 53 -1; 1724365073 $ ./a.out 2024 08 23 00 17 53 0; a.out: mktime: Invalid argument 1724368673 $ ./a.out 2024 08 23 00 17 53 1; 1724365073 $ $ ./a.out 2024 02 23 00 17 53 -1; 1708643873 $ ./a.out 2024 02 23 00 17 53 0; 1708643873 $ ./a.out 2024 02 23 00 17 53 1; a.out: mktime: Invalid argument 1708640273 $ $ ./a.out 2024 03 26 02 17 53 -1; a.out: mktime: Invalid argument 1679793473 $ $ ./a.out 2024 10 29 02 17 53 -1; a.out: mktime: Name not unique on network 1698542273 $ ./a.out 2024 10 29 02 17 53 0; 1698542273 $ ./a.out 2024 10 29 02 17 53 1; 1698538673 $ $ ./a.out 2024 02 29 12 00 00 -1; a.out: mktime: Invalid argument 1677668400 Program source: mktime.c #include #include #include #include #include #include time_t my_mktime(struct tm *tp); 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\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++); errno = 0; t = my_mktime(&tm); if (errno == EOVERFLOW) err(EXIT_FAILURE, "mktime"); if (errno == EINVAL || errno == ENOTUNIQ) warn("mktime"); printf("%ju\n", (uintmax_t) t); exit(EXIT_SUCCESS); } time_t my_mktime(struct tm *tp) { int e, isdst; time_t t; struct tm tm; e = errno; errno = 0; tm = *tp; isdst = tp->tm_isdst; t = mktime(tp); if (t == -1 && errno == EOVERFLOW) 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; if (mktime(&tm) == -1 && errno == EOVERFLOW) goto out; if (tm.tm_isdst != tp->tm_isdst) { errno = ENOTUNIQ; return t; } out: errno = e; return t; } man/man3/ctime.3 | 157 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 157 insertions(+) base-commit: 0813c125d8bf754c40015aa1b31f23e0650584ac diff --git a/man/man3/ctime.3 b/man/man3/ctime.3 index 5aec51b79..5ab881978 100644 --- a/man/man3/ctime.3 +++ b/man/man3/ctime.3 @@ -412,6 +412,163 @@ .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 EXAMPLES +Passing an invalid time to +.BR mktime () +or an invalid +.I tm->tm_isdst +value +yields unspecified results. +Also, +passing the value +.I \-1 +in +.I tm->tm_isdst +will result in an ambiguous time during some DST transitions, +which will also yield an unspecified result. +.P +The program below uses 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 +.EX +.RB $\~ "export TZ=Europe/Madrid" ; +$ +.RB $\~ "./a.out 2024 08 23 00 17 53 \-1" ; +1724365073 +.RB $\~ "./a.out 2024 08 23 00 17 53 0" ; +a.out: mktime: Invalid argument +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" ; +a.out: mktime: Invalid argument +1708640273 +$ +.RB $\~ "./a.out 2024 03 26 02 17 53 \-1" ; +a.out: mktime: Invalid argument +1679793473 +$ +.RB $\~ "./a.out 2024 10 29 02 17 53 \-1" ; +a.out: mktime: Name not unique on network +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" ; +a.out: mktime: Invalid argument +1677668400 +.EE +.SS Program source: mktime.c +\& +.\" SRC BEGIN (mktime.c) +.EX +#include +#include +#include +#include +#include +#include +\& +time_t my_mktime(struct tm *tp); +\& +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++); +\& + errno = 0; + t = my_mktime(&tm); + if (errno == EOVERFLOW) + err(EXIT_FAILURE, "mktime"); + if (errno == EINVAL || errno == ENOTUNIQ) + warn("mktime"); +\& + printf("%ju\[rs]n", (uintmax_t) t); + exit(EXIT_SUCCESS); +} +\& +time_t +my_mktime(struct tm *tp) +{ + int e, isdst; + time_t t; + struct tm tm; +\& + e = errno; + errno = 0; +\& + tm = *tp; + isdst = tp\->tm_isdst; +\& + t = mktime(tp); + if (t == \-1 && errno == EOVERFLOW) + 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; +\& + if (mktime(&tm) == \-1 && errno == EOVERFLOW) + goto out; +\& + if (tm.tm_isdst != tp\->tm_isdst) { + errno = ENOTUNIQ; + return t; + } +out: + errno = e; + return t; +} +.EE +.\" SRC END .SH SEE ALSO .BR date (1), .BR gettimeofday (2),