root/maint/gnulib/lib/posixtm.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. year
  2. posix_time_parse
  3. posixtime

   1 /* Parse dates for touch and date.
   2 
   3    Copyright (C) 1989-1991, 1998, 2000-2021 Free Software Foundation, Inc.
   4 
   5    This program is free software: you can redistribute it and/or modify
   6    it under the terms of the GNU General Public License as published by
   7    the Free Software Foundation; either version 3 of the License, or
   8    (at your option) any later version.
   9 
  10    This program is distributed in the hope that it will be useful,
  11    but WITHOUT ANY WARRANTY; without even the implied warranty of
  12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13    GNU General Public License for more details.
  14 
  15    You should have received a copy of the GNU General Public License
  16    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
  17 
  18 /* Yacc-based version written by Jim Kingdon and David MacKenzie.
  19    Rewritten by Jim Meyering.  */
  20 
  21 #include <config.h>
  22 
  23 #include "posixtm.h"
  24 
  25 #include "c-ctype.h"
  26 #include "idx.h"
  27 #include "intprops.h"
  28 #include "verify.h"
  29 
  30 #include <string.h>
  31 
  32 /*
  33   POSIX requires:
  34 
  35   touch -t [[CC]YY]mmddhhmm[.ss] FILE...
  36     8, 10, or 12 digits, followed by optional .ss
  37     (PDS_CENTURY | PDS_SECONDS)
  38 
  39   touch mmddhhmm[YY] FILE... (obsoleted by POSIX 1003.1-2001)
  40     8 or 10 digits, YY (if present) must be in the range 69-99
  41     (PDS_TRAILING_YEAR | PDS_PRE_2000)
  42 
  43   date mmddhhmm[[CC]YY]
  44     8, 10, or 12 digits
  45     (PDS_TRAILING_YEAR | PDS_CENTURY)
  46 
  47 */
  48 
  49 static bool
  50 year (struct tm *tm, const int *digit_pair, idx_t n, unsigned int syntax_bits)
     /* [previous][next][first][last][top][bottom][index][help] */
  51 {
  52   switch (n)
  53     {
  54     case 1:
  55       tm->tm_year = *digit_pair;
  56       /* Deduce the century based on the year.
  57          POSIX requires that 00-68 be interpreted as 2000-2068,
  58          and that 69-99 be interpreted as 1969-1999.  */
  59       if (digit_pair[0] <= 68)
  60         {
  61           if (syntax_bits & PDS_PRE_2000)
  62             return false;
  63           tm->tm_year += 100;
  64         }
  65       break;
  66 
  67     case 2:
  68       if (! (syntax_bits & PDS_CENTURY))
  69         return false;
  70       tm->tm_year = digit_pair[0] * 100 + digit_pair[1] - 1900;
  71       break;
  72 
  73     case 0:
  74       {
  75         /* Use current year.  */
  76         time_t now = time (NULL);
  77         struct tm *tmp = localtime (&now);
  78         if (! tmp)
  79           return false;
  80         tm->tm_year = tmp->tm_year;
  81       }
  82       break;
  83 
  84     default:
  85       assume (false);
  86     }
  87 
  88   return true;
  89 }
  90 
  91 static bool
  92 posix_time_parse (struct tm *tm, const char *s, unsigned int syntax_bits)
     /* [previous][next][first][last][top][bottom][index][help] */
  93 {
  94   const char *dot = NULL;
  95   int pair[6];
  96 
  97   idx_t s_len = strlen (s);
  98   idx_t len = s_len;
  99 
 100   if (syntax_bits & PDS_SECONDS)
 101     {
 102       dot = strchr (s, '.');
 103       if (dot)
 104         {
 105           len = dot - s;
 106           if (s_len - len != 3)
 107             return false;
 108         }
 109     }
 110 
 111   if (! (8 <= len && len <= 12 && len % 2 == 0))
 112     return false;
 113 
 114   for (idx_t i = 0; i < len; i++)
 115     if (!c_isdigit (s[i]))
 116       return false;
 117 
 118   len /= 2;
 119   for (idx_t i = 0; i < len; i++)
 120     pair[i] = 10 * (s[2*i] - '0') + s[2*i + 1] - '0';
 121 
 122   int *p = pair;
 123   if (! (syntax_bits & PDS_TRAILING_YEAR))
 124     {
 125       if (! year (tm, p, len - 4, syntax_bits))
 126         return false;
 127       p += len - 4;
 128       len = 4;
 129     }
 130 
 131   /* Handle 8 digits worth of 'MMDDhhmm'.  */
 132   tm->tm_mon = *p++ - 1;
 133   tm->tm_mday = *p++;
 134   tm->tm_hour = *p++;
 135   tm->tm_min = *p++;
 136   len -= 4;
 137 
 138   /* Handle any trailing year.  */
 139   if (syntax_bits & PDS_TRAILING_YEAR)
 140     {
 141       if (! year (tm, p, len, syntax_bits))
 142         return false;
 143     }
 144 
 145   /* Handle seconds.  */
 146   if (!dot)
 147     tm->tm_sec = 0;
 148   else if (c_isdigit (dot[1]) && c_isdigit (dot[2]))
 149     tm->tm_sec = 10 * (dot[1] - '0') + dot[2] - '0';
 150   else
 151     return false;
 152 
 153   return true;
 154 }
 155 
 156 /* Parse a POSIX-style date, returning true if successful.  */
 157 
 158 bool
 159 posixtime (time_t *p, const char *s, unsigned int syntax_bits)
     /* [previous][next][first][last][top][bottom][index][help] */
 160 {
 161   struct tm tm0;
 162   bool leapsec = false;
 163 
 164   if (! posix_time_parse (&tm0, s, syntax_bits))
 165     return false;
 166 
 167   while (true)
 168     {
 169       struct tm tm1;
 170       tm1.tm_sec = tm0.tm_sec;
 171       tm1.tm_min = tm0.tm_min;
 172       tm1.tm_hour = tm0.tm_hour;
 173       tm1.tm_mday = tm0.tm_mday;
 174       tm1.tm_mon = tm0.tm_mon;
 175       tm1.tm_year = tm0.tm_year;
 176       tm1.tm_wday = -1;
 177       tm1.tm_isdst = -1;
 178       time_t t = mktime (&tm1);
 179 
 180       if (tm1.tm_wday < 0)
 181         return false;
 182 
 183       /* Reject dates like "September 31" and times like "25:61".
 184          However, allow a seconds count of 60 even in time zones that do
 185          not support leap seconds, treating it as the following second;
 186          POSIX requires this.  */
 187       if (! ((tm0.tm_year ^ tm1.tm_year)
 188              | (tm0.tm_mon ^ tm1.tm_mon)
 189              | (tm0.tm_mday ^ tm1.tm_mday)
 190              | (tm0.tm_hour ^ tm1.tm_hour)
 191              | (tm0.tm_min ^ tm1.tm_min)
 192              | (tm0.tm_sec ^ tm1.tm_sec)))
 193         {
 194           if (INT_ADD_WRAPV (t, leapsec, &t))
 195             return false;
 196           *p = t;
 197           return true;
 198         }
 199 
 200       /* Any mismatch without 60 in the tm_sec field is invalid.  */
 201       if (tm0.tm_sec != 60)
 202         return false;
 203 
 204       /* Allow times like 01:35:60 or 23:59:60.  */
 205       tm0.tm_sec = 59;
 206       leapsec = true;
 207     }
 208 }

/* [previous][next][first][last][top][bottom][index][help] */