delorie.com/archives/browse.cgi   search  
Mail Archives: djgpp-workers/1999/01/24/03:39:03

Date: Sun, 24 Jan 1999 10:34:49 +0200 (IST)
From: Eli Zaretskii <eliz AT is DOT elta DOT co DOT il>
X-Sender: eliz AT is
To: DJ Delorie <dj AT delorie DOT com>
cc: djgpp-workers AT delorie DOT com
Subject: ctime.c problems
Message-ID: <Pine.SUN.3.91.990124102214.4501D-100000@is>
MIME-Version: 1.0
Reply-To: djgpp-workers AT delorie DOT com

While building the latest pretest of GNU Textutils, I found quite a few 
problems with ctime.c.  These problems caused the configure script to 
decide that our mktime isn't ``working'' (ha!), and use the GNU version 
instead.  Here are the problems I found when looking into this:

	1) If the value of "TZ" in the environment changes during the 
program run, ctime.c doesn't pay attention to the change and continues to 
use the old timezone.

	2) mktime will loop for a very long time (several minutes!) when 
its argument includes some extremely large values (e.g. try tm.tm_day = 
2^30 - 1).

	3) mktime fails for arguments close to the epoch returned by 
localtime in non-GMT timezones.  For example, the value of the following
expression is false (now and lt are both time_t) in the PST8 timezone:

    ((now = (time_t)0), (lt = localtime (&now)), (mktime (lt) == now))

Here's a patch that fixes all these problems, and then some:

*** src/libc/ansi/time/ctime.c~0	Thu Jan  1 17:17:30 1998
--- src/libc/ansi/time/ctime.c	Sat Jan 23 15:07:52 1999
*************** static char sccsid[] = "@(#)ctime.c	5.23
*** 50,55 ****
--- 50,56 ----
  
  #include <libc/unconst.h>
  #include <libc/bss.h>
+ #include <libc/environ.h>
  
  #include "posixrul.h"
  
*************** static void		timesub P((const time_t * t
*** 152,157 ****
--- 153,159 ----
  				const struct state * sp, struct tm * tmp));
  static int		tmcomp P((const struct tm * atmp,
  				const struct tm * btmp));
+ static void		tmnormalize P((struct tm * tmp));
  static time_t		transtime P((time_t janfirst, int year,
  				const struct rule * rulep, long offset));
  static int		tzload P((const char * name, struct state * sp));
*************** static struct state gmtmem;
*** 171,178 ****
  #define gmtptr (&gmtmem)
  #endif /* State Farm */
  
! static int lcl_is_set;
  static int gmt_is_set;
  
  char * tzname[2] = {
    WILDABBR,
--- 173,181 ----
  #define gmtptr (&gmtmem)
  #endif /* State Farm */
  
! static int lcl_is_set;	/* 0: no, 1: set by tzset, -1: set by tzsetwall */
  static int gmt_is_set;
+ static char lcl_tzstr[512];
  
  char * tzname[2] = {
    WILDABBR,
*************** static const int year_lengths[2] = {
*** 401,406 ****
--- 404,415 ----
  DAYSPERNYEAR, DAYSPERLYEAR
  };
  
+ static const long n_year_lengths[3] = {
+   3*DAYSPERNYEAR + DAYSPERLYEAR,		 /* 4-year cycle length */
+   25*(3*DAYSPERNYEAR + DAYSPERLYEAR) - 1,	 /* 100-year cycle length */
+   4*(25*(3*DAYSPERNYEAR + DAYSPERLYEAR) - 1) + 1 /* 400-year cycle length */
+ };
+ 
  /*
  ** Given a pointer into a time zone string, scan until a character that is not
  ** a valid character in a zone name is found.  Return a pointer to that
*************** void
*** 908,921 ****
  tzset(void)
  {
    const char * name;
  
    name = getenv("TZ");
    if (name == NULL)
    {
      tzsetwall();
      return;
    }
!   lcl_is_set = TRUE;
  #ifdef ALL_STATE
    if (lclptr == NULL)
    {
--- 917,951 ----
  tzset(void)
  {
    const char * name;
+   static unsigned last_env_changed = 0;
  
+   /* If environ didn't changed since last time, don't waste time
+      looking at $TZ.  */
+   if (lcl_is_set > 0 && __environ_changed == last_env_changed)
+     return;
+ 
+   /* If environ did change, but $TZ wasn't changed since last time we
+      were called, we are all done here.  */
+   last_env_changed = __environ_changed;
    name = getenv("TZ");
+   /* Use stricmp, since if TZ points to a file name, we need to be
+      case-insensitive.  */
+   if (lcl_is_set > 0 && (name == NULL || stricmp(name, lcl_tzstr) == 0))
+     return;
+ 
+   /* On to some *real* work... */
    if (name == NULL)
    {
      tzsetwall();
      return;
    }
!   if (strlen(name) < sizeof(lcl_tzstr))
!   {
!     lcl_is_set = 1;
!     strcpy(lcl_tzstr, name);
!   }
!   else
!     lcl_is_set = 0;
  #ifdef ALL_STATE
    if (lclptr == NULL)
    {
*************** tzset(void)
*** 947,953 ****
  void
  tzsetwall(void)
  {
!   lcl_is_set = TRUE;
  #ifdef ALL_STATE
    if (lclptr == NULL)
    {
--- 977,985 ----
  void
  tzsetwall(void)
  {
!   if (lcl_is_set == -1)
!     return;
!   lcl_is_set = -1;
  #ifdef ALL_STATE
    if (lclptr == NULL)
    {
*************** localsub(const time_t * const timep, con
*** 982,989 ****
    int i;
    const time_t t = *timep;
  
!   if (!lcl_is_set)
!     tzset();
    sp = lclptr;
  #ifdef ALL_STATE
    if (sp == NULL)
--- 1014,1020 ----
    int i;
    const time_t t = *timep;
  
!   tzset();
    sp = lclptr;
  #ifdef ALL_STATE
    if (sp == NULL)
*************** gmtime(const time_t * const timep)
*** 1078,1083 ****
--- 1109,1159 ----
    return &tm;
  }
  
+ /* Return the year which is DAYS away from the year Y0.  */
+ static int
+ days_to_years(int y0, long *days)
+ {
+   int y, dir, yleap;
+ 
+   y = y0;
+   dir = *days >= 0 ? 1 : -1;
+ 
+   /* We move by 400, 100, and 4 years at a time, to quickly reduce
+      DAYS to a reasonable value.  */
+   while (*days*dir > n_year_lengths[2])
+   {
+     y += dir*400;
+     *days -= dir*n_year_lengths[2];
+   }
+   while (*days*dir > n_year_lengths[1])
+   {
+     y += dir*100;
+     *days -= dir*n_year_lengths[1];
+   }
+   while (*days*dir > n_year_lengths[0])
+   {
+     y += dir*4;
+     *days -= dir*n_year_lengths[0];
+   }
+   if (dir == 1)
+     for ( ; ; )
+     {
+       yleap = isleap(y);
+       if (*days < (long) year_lengths[yleap])
+ 	break;
+       ++y;
+       *days = *days - (long) year_lengths[yleap];
+     }
+   else
+     do {
+     --y;
+     yleap = isleap(y);
+     *days = *days + (long) year_lengths[yleap];
+   } while (*days < 0);
+ 
+   return y;
+ }
+ 
  static void
  timesub(const time_t * const timep, const long offset, const struct state * const sp, struct tm * const tmp)
  {
*************** timesub(const time_t * const timep, cons
*** 1147,1168 ****
    tmp->tm_wday = (int) ((EPOCH_WDAY + days) % DAYSPERWEEK);
    if (tmp->tm_wday < 0)
      tmp->tm_wday += DAYSPERWEEK;
!   y = EPOCH_YEAR;
!   if (days >= 0)
!     for ( ; ; )
!     {
!       yleap = isleap(y);
!       if (days < (long) year_lengths[yleap])
! 	break;
!       ++y;
!       days = days - (long) year_lengths[yleap];
!     }
!   else
!     do {
!     --y;
!     yleap = isleap(y);
!     days = days + (long) year_lengths[yleap];
!   } while (days < 0);
    tmp->tm_year = y - TM_YEAR_BASE;
    tmp->tm_yday = (int) days;
    ip = mon_lengths[yleap];
--- 1223,1230 ----
    tmp->tm_wday = (int) ((EPOCH_WDAY + days) % DAYSPERWEEK);
    if (tmp->tm_wday < 0)
      tmp->tm_wday += DAYSPERWEEK;
!   y = days_to_years(EPOCH_YEAR, &days);
!   yleap = isleap(y);
    tmp->tm_year = y - TM_YEAR_BASE;
    tmp->tm_yday = (int) days;
    ip = mon_lengths[yleap];
*************** tmcomp(const struct tm * const atmp, con
*** 1251,1256 ****
--- 1313,1358 ----
    return result;
  }
  
+ static void
+ tmnormalize(struct tm *tmp)
+ {
+   if (tmp->tm_sec >= SECSPERMIN + 2 || tmp->tm_sec < 0)
+     normalize(&tmp->tm_min, &tmp->tm_sec, SECSPERMIN);
+   normalize(&tmp->tm_hour, &tmp->tm_min, MINSPERHOUR);
+   normalize(&tmp->tm_mday, &tmp->tm_hour, HOURSPERDAY);
+   normalize(&tmp->tm_year, &tmp->tm_mon, MONSPERYEAR);
+ 
+   /* If tm_mday is negative, or positive and too large, bring it back
+      to the reasonable range [1..366].  */
+   if (tmp->tm_mday <= 0 || tmp->tm_mday > DAYSPERLYEAR)
+   {
+     long days = tmp->tm_mday;
+     int yleap = isleap(tmp->tm_year + TM_YEAR_BASE);
+ 
+     while (tmp->tm_mon--)
+       days += mon_lengths[yleap][tmp->tm_mon];
+     tmp->tm_year =
+       days_to_years(tmp->tm_year + TM_YEAR_BASE, &days) - TM_YEAR_BASE;
+     tmp->tm_mday = days;
+     tmp->tm_mon = 0;
+   }
+ 
+   /* Now correct tm_mon and tm_mday so that they are within their
+      normal ranges [0..11] and [1..mon_length[tm_mon]], respectively.  */
+   for ( ; ; )
+   {
+     int i = mon_lengths[isleap(tmp->tm_year + TM_YEAR_BASE)][tmp->tm_mon];
+     if (tmp->tm_mday <= i)
+       break;
+     tmp->tm_mday -= i;
+     if (++tmp->tm_mon >= MONSPERYEAR)
+     {
+       tmp->tm_mon = 0;
+       ++tmp->tm_year;
+     }
+   }
+ }
+ 
  static time_t
  time2(struct tm *tmp, void (*const funcp)(const time_t *const,const long,struct tm *), const long offset, int * const okayp)
  {
*************** time2(struct tm *tmp, void (*const funcp
*** 1264,1294 ****
    struct tm yourtm, mytm;
  
    *okayp = FALSE;
    yourtm = *tmp;
-   if (yourtm.tm_sec >= SECSPERMIN + 2 || yourtm.tm_sec < 0)
-     normalize(&yourtm.tm_min, &yourtm.tm_sec, SECSPERMIN);
-   normalize(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR);
-   normalize(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY);
-   normalize(&yourtm.tm_year, &yourtm.tm_mon, MONSPERYEAR);
-   while (yourtm.tm_mday <= 0)
-   {
-     --yourtm.tm_year;
-     yourtm.tm_mday +=
-       year_lengths[isleap(yourtm.tm_year + TM_YEAR_BASE)];
-   }
-   for ( ; ; )
-   {
-     i = mon_lengths[isleap(yourtm.tm_year +
- 			   TM_YEAR_BASE)][yourtm.tm_mon];
-     if (yourtm.tm_mday <= i)
-       break;
-     yourtm.tm_mday -= i;
-     if (++yourtm.tm_mon >= MONSPERYEAR)
-     {
-       yourtm.tm_mon = 0;
-       ++yourtm.tm_year;
-     }
-   }
    saved_seconds = yourtm.tm_sec;
    yourtm.tm_sec = 0;
    /*
--- 1366,1373 ----
    struct tm yourtm, mytm;
  
    *okayp = FALSE;
+   tmnormalize(tmp);
    yourtm = *tmp;
    saved_seconds = yourtm.tm_sec;
    yourtm.tm_sec = 0;
    /*
*************** time1(struct tm * const tmp, void (*cons
*** 1412,1417 ****
--- 1491,1497 ----
  time_t
  mktime(struct tm * tmp)
  {
+   struct tm save_tm = *tmp;
    int rv = time1(tmp, localsub, 0L);
    if (rv == -1)
    {
*************** mktime(struct tm * tmp)
*** 1427,1432 ****
--- 1507,1530 ----
      tmp->tm_hour -= delta;
      if (rv != -1)
        rv -= delta*60*60;
+     else
+     {
+       /* tmp might point to a time structure that's before 1/1/1970.
+ 	 This can happen if tmp is a result of a call to localtime(0)
+ 	 issued in a timezone with a negative offset, like PST8.
+ 	 Adding the timezone offset will bring the time structure into
+ 	 the valid range.  */
+       delta = 24 - tmp->tm_hour;
+       tmp->tm_hour += delta;
+       rv = time1(tmp, localsub, 0L);
+       tmp->tm_hour -= delta;
+       if (rv != -1)
+ 	rv -= delta*60*60;
+     }
+     if (rv == -1)
+       *tmp = save_tm;
+     else
+       tmnormalize(tmp);
    }
    return rv;
  }

- Raw text -


  webmaster     delorie software   privacy  
  Copyright © 2019   by DJ Delorie     Updated Jul 2019