[PATCH v3] ctime.3: EXAMPLES: Add example program

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

 



This example documents the corner cases of mktime(3), such as what
happens during DST transitions, and other jumps in the calendar.

Link: <https://www.redhat.com/en/blog/brief-history-mktime>
Reported-by: DJ Delorie <dj@xxxxxxxxxx>
Cc: Paul Eggert <eggert@xxxxxxxxxxx>
Signed-off-by: Alejandro Colomar <alx@xxxxxxxxxx>
---

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 <err.h>
     #include <errno.h>
     #include <stdint.h>
     #include <stdio.h>
     #include <stdlib.h>
     #include <time.h>

     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(+)

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 <err.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+\&
+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),

base-commit: 0813c125d8bf754c40015aa1b31f23e0650584ac
-- 
2.45.2

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