(root)/
coreutils-9.4/
gnulib-tests/
test-nstrftime.c
       1  /* Test that nstrftime works as required.
       2     Copyright (C) 2011-2023 Free Software Foundation, Inc.
       3  
       4     This program is free software: you can redistribute it and/or modify
       5     it under the terms of the GNU General Public License as published by
       6     the Free Software Foundation, either version 3 of the License, or
       7     (at your option) any later version.
       8  
       9     This program is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12     GNU General Public License for more details.
      13  
      14     You should have received a copy of the GNU General Public License
      15     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      16  
      17  /* Written by Jim Meyering.  */
      18  
      19  #include <config.h>
      20  
      21  #include "strftime.h"
      22  
      23  #include "intprops.h"
      24  
      25  #include <errno.h>
      26  #include <limits.h>
      27  #include <stdio.h>
      28  #include <string.h>
      29  #include <time.h>
      30  #include <unistd.h>
      31  
      32  #include "macros.h"
      33  #define STREQ(a, b) (strcmp (a, b) == 0)
      34  
      35  /* Support for settings like TZ='<+00>0' was added in IEEE Std 1003.1-2001.  */
      36  #define TZ_ANGLE_BRACKETS_SHOULD_WORK (200112 <= _POSIX_VERSION)
      37  
      38  struct posixtm_test
      39  {
      40    time_t in;
      41    int in_ns;
      42    char const *fmt;
      43    char const *exp;
      44  };
      45  
      46  static struct posixtm_test const T[] =
      47    {
      48      { 1300000000, 0,            "%F", "2011-03-13" },
      49      { 0,          10,           "%T.%N", "00:00:00.000000010" },
      50      { 56,         123456789,    "%T.%12N", "00:00:56.123456789000" },
      51      { 0,          0,            NULL, NULL }
      52    };
      53  
      54  static int
      55  posixtm_test (void)
      56  {
      57    int fail = 0;
      58    unsigned int i;
      59  
      60    for (i = 0; T[i].fmt; i++)
      61      {
      62        char buf[1000];
      63        time_t t = T[i].in;
      64        struct tm *tm = gmtime (&t);
      65        size_t n;
      66  
      67        ASSERT (tm);
      68  
      69        n = nstrftime (buf, sizeof buf, T[i].fmt, tm, 0, T[i].in_ns);
      70        if (n == 0)
      71          {
      72            fail = 1;
      73            printf ("nstrftime failed with format %s\n", T[i].fmt);
      74          }
      75  
      76        if (! STREQ (buf, T[i].exp))
      77          {
      78            fail = 1;
      79            printf ("%s: result mismatch: got %s, expected %s\n",
      80                    T[i].fmt, buf, T[i].exp);
      81          }
      82      }
      83  
      84    return fail;
      85  }
      86  
      87  struct tzalloc_test
      88  {
      89    timezone_t tz;
      90    char const *setting;
      91  };
      92  
      93  static struct tzalloc_test TZ[] =
      94    {
      95  #define Pacific 0
      96      { 0, "PST8PDT,M3.2.0,M11.1.0"      },
      97  #define Arizona 1
      98      { 0, "MST7"                        },
      99  #define UTC 2
     100      { 0, 0                             },
     101  #define CentEur 3
     102      { 0, "CET-1CEST,M3.5.0,M10.5.0/3"  },
     103  #define Japan 4
     104      { 0, "JST-9"                       },
     105  #define NZ 5
     106      { 0, "NZST-12NZDT,M9.5.0,M4.1.0/3" },
     107  #define Unknown 6
     108      { 0, "<-00>0" },
     109      { 0 }
     110    };
     111  
     112  struct localtime_rz_test
     113  {
     114    /* Input parameters.  */
     115    struct tzalloc_test *tza;
     116    time_t t;
     117  
     118    /* Expected result.  */
     119    char const *exp;
     120  
     121    /* Determines if an incorrectly unset tm_isdst
     122       results in failure or just a warning.  */
     123    int ahistorical;
     124  };
     125  
     126  static struct localtime_rz_test LT[] =
     127    {
     128      { TZ+Pacific,          0, "1969-12-31 16:00:00 -0800 (PST)",  0 },
     129      { TZ+Arizona,          0, "1969-12-31 17:00:00 -0700 (MST)",  0 },
     130      { TZ+UTC    ,          0, "1970-01-01 00:00:00 +0000 (UTC)",  0 },
     131      { TZ+CentEur,          0, "1970-01-01 01:00:00 +0100 (CET)",  0 },
     132      { TZ+Japan  ,          0, "1970-01-01 09:00:00 +0900 (JST)",  0 },
     133      { TZ+NZ     ,          0, "1970-01-01 13:00:00 +1300 (NZDT)", 1 },
     134      { TZ+Pacific,  500000001, "1985-11-04 16:53:21 -0800 (PST)",  0 },
     135      { TZ+Arizona,  500000001, "1985-11-04 17:53:21 -0700 (MST)",  0 },
     136      { TZ+UTC    ,  500000001, "1985-11-05 00:53:21 +0000 (UTC)",  0 },
     137      { TZ+CentEur,  500000001, "1985-11-05 01:53:21 +0100 (CET)",  1 },
     138      { TZ+Japan  ,  500000001, "1985-11-05 09:53:21 +0900 (JST)",  0 },
     139      { TZ+NZ     ,  500000001, "1985-11-05 13:53:21 +1300 (NZDT)", 0 },
     140      { TZ+Pacific, 1000000002, "2001-09-08 18:46:42 -0700 (PDT)",  0 },
     141      { TZ+Arizona, 1000000002, "2001-09-08 18:46:42 -0700 (MST)",  0 },
     142      { TZ+UTC    , 1000000002, "2001-09-09 01:46:42 +0000 (UTC)",  0 },
     143      { TZ+CentEur, 1000000002, "2001-09-09 03:46:42 +0200 (CEST)", 0 },
     144      { TZ+Japan  , 1000000002, "2001-09-09 10:46:42 +0900 (JST)",  0 },
     145      { TZ+NZ     , 1000000002, "2001-09-09 13:46:42 +1200 (NZST)", 0 },
     146  #if TZ_ANGLE_BRACKETS_SHOULD_WORK
     147      { TZ+Unknown,          0, "1970-01-01 00:00:00 -0000 (-00)",  0 },
     148      { TZ+Unknown,  500000001, "1985-11-05 00:53:21 -0000 (-00)",  0 },
     149      { TZ+Unknown, 1000000002, "2001-09-09 01:46:42 -0000 (-00)",  0 },
     150  #endif
     151      { 0 }
     152    };
     153  
     154  static int
     155  tzalloc_test (void)
     156  {
     157    int fail = 0;
     158    int i;
     159  
     160    for (i = 0; LT[i].tza; i++)
     161      {
     162        struct tzalloc_test *tza = LT[i].tza;
     163        long lt = LT[i].t;
     164        timezone_t tz = tza->tz;
     165        char const *setting;
     166        static char const format[] = "%Y-%m-%d %H:%M:%S %z (%Z)";
     167        char buf[1000];
     168        struct tm tm;
     169        size_t n;
     170  
     171        if (!tz && tza->setting)
     172          {
     173            tz = tzalloc (tza->setting);
     174            if (!tz)
     175              {
     176                fail = 1;
     177                printf ("%s: tzalloc: %s\n", TZ[i].setting, strerror (errno));
     178                continue;
     179              }
     180            tza->tz = tz;
     181          }
     182  
     183        setting = tza->setting ? tza->setting : "UTC0";
     184  
     185        if (!localtime_rz (tz, &LT[i].t, &tm))
     186          {
     187            fail = 1;
     188            printf ("%s: %ld: localtime_rz: %s\n", setting, lt,
     189                    strerror (errno));
     190            continue;
     191          }
     192  
     193        n = nstrftime (buf, sizeof buf, format, &tm, tz, 0);
     194        if (n == 0)
     195          {
     196            fail = 1;
     197            printf ("%s: %ld: nstrftime failed\n", setting, lt);
     198            continue;
     199          }
     200  
     201        if (! (STREQ (buf, LT[i].exp)
     202               || (!tz && n == strlen (LT[i].exp)
     203                   && memcmp (buf, LT[i].exp, n - sizeof "(GMT)" + 1) == 0
     204                   && STREQ (buf + n - sizeof "(GMT)" + 1, "(GMT)"))))
     205          {
     206            /* Don't fail for unhandled dst in ahistorical entries,
     207               as gnulib doesn't currently fix that issue, seen on Darwin 14.  */
     208            if (!LT[i].ahistorical || tm.tm_isdst)
     209              fail = 1;
     210            printf ("%s: expected \"%s\", got \"%s\"\n",
     211                    setting, LT[i].exp, buf);
     212          }
     213      }
     214  
     215    return fail;
     216  }
     217  
     218  
     219  static int
     220  quarter_test (void)
     221  {
     222    int result = 0;
     223    size_t mon;
     224  
     225    /* Check %q.  */
     226    for (mon = 1; mon <= 12; mon++)
     227      {
     228        char out[2];
     229        char exp[2] = {0,};
     230        struct tm qtm = { .tm_mon = mon - 1 };
     231        char fmt[3] = {'%','q','\0'};
     232  
     233        size_t r = nstrftime (out, sizeof (out), fmt, &qtm, 0, 0);
     234        if (r == 0)
     235          {
     236            puts ("nstrftime(\"%q\") failed");
     237            result = 1;
     238            break;
     239          }
     240  
     241        exp[0] = mon < 4 ? '1' : mon < 7 ? '2' : mon < 10 ? '3' : '4';
     242        if (strcmp (out, exp) != 0)
     243          {
     244            printf ("nstrftime %%q: expected \"%s\", got \"%s\"\n", exp, out);
     245            result = 1;
     246            break;
     247          }
     248      }
     249  
     250    return result;
     251  }
     252  
     253  static int
     254  errno_test (void)
     255  {
     256    int fail = 0;
     257    struct tm tm = { .tm_year = 2020 - 1900, .tm_mday = 1 };
     258    char buf[INT_BUFSIZE_BOUND (time_t)];
     259    size_t n;
     260    int bigyear = LLONG_MAX - 1900 < INT_MAX ? LLONG_MAX - 1900 : INT_MAX;
     261  
     262    errno = 0;
     263    n = nstrftime (buf, 0, "%m", &tm, 0, 0);
     264    if (! (n == 0 && errno == ERANGE))
     265      {
     266        fail = 1;
     267        printf ("nstrftime failed to set errno = ERANGE\n");
     268      }
     269  
     270    errno = 0;
     271    n = nstrftime (buf, sizeof buf, "", &tm, 0, 0);
     272    if (! (n == 0 && errno == 0))
     273      {
     274        fail = 1;
     275        printf ("nstrftime failed to leave errno alone\n");
     276      }
     277  
     278  
     279    tm.tm_year = bigyear;
     280    errno = 0;
     281    n = nstrftime (buf, sizeof buf, "%s", &tm, 0, 0);
     282    if (n == 0)
     283      {
     284        if (errno != EOVERFLOW)
     285          {
     286            fail = 1;
     287            printf ("nstrftime failed to set errno = EOVERFLOW\n");
     288          }
     289  
     290        if (mktime_z (0, &tm) != (time_t) -1)
     291          {
     292            fail = 1;
     293            printf ("nstrftime %%s failed but mktime_z worked for tm_year=%d\n",
     294                    bigyear);
     295          }
     296      }
     297    else
     298      {
     299        long long int text_seconds = atoll (buf);
     300        if (text_seconds <= (LLONG_MAX - 1 < TYPE_MAXIMUM (time_t)
     301                             ? LLONG_MAX - 1 : TYPE_MAXIMUM (time_t)))
     302          {
     303            time_t bigtime = text_seconds;
     304            struct tm *tmp = gmtime (&bigtime);
     305            if (!tmp)
     306              {
     307                fail = 1;
     308                printf ("gmtime failed on nstrftime result\n");
     309              }
     310            else
     311              {
     312                char buf1[sizeof buf];
     313                size_t n1 = nstrftime (buf1, sizeof buf1, "%s", tmp, 0, 0);
     314                buf1[n1] = '\0';
     315                if (! STREQ (buf, buf1))
     316                  {
     317                    fail = 1;
     318                    printf ("nstrftime %%s first returned '%s', then '%s'\n",
     319                            buf, buf1);
     320                  }
     321              }
     322          }
     323      }
     324  
     325    return fail;
     326  }
     327  
     328  int
     329  main (void)
     330  {
     331    int fail = 0;
     332    fail |= posixtm_test ();
     333    fail |= tzalloc_test ();
     334    fail |= quarter_test ();
     335    fail |= errno_test ();
     336    return fail;
     337  }
     338  
     339  /*
     340  Local Variables:
     341  indent-tabs-mode: nil
     342  End:
     343  */