Module Name:    src
Committed By:   snj
Date:           Sat Oct 11 16:56:21 UTC 2014

Modified Files:
        src/lib/libutil [netbsd-7]: parsedate.y

Log Message:
Pull up following revision(s) (requested by apb in ticket #135):
        lib/libutil/parsedate.y: revision 1.20
Improved handling of local times.
* A magic value USE_LOCAL_TIME (defined as 99999) may be passed as the
  Timezone to Convert(), instructing it to use mktime() to work
  in the local time zone, instead of using mktime_z to work in UTC
  (and then adding the specified timezone offset).
* Some old code is removed now that there's no need to find the local
  timezone offset.
* Allow either one or both of the now and zone arguments to
  parsedate() to be NULL, treating them independently.  Previously,
  if either one was NULL, the other was ignored.
* If the zone argument is specified, then the current date is calculated
  in the specified zone, not in local time.
Also add some disabled debug code.
This should fix PR lib/47916.


To generate a diff of this commit:
cvs rdiff -u -r1.16.6.3 -r1.16.6.4 src/lib/libutil/parsedate.y

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/lib/libutil/parsedate.y
diff -u src/lib/libutil/parsedate.y:1.16.6.3 src/lib/libutil/parsedate.y:1.16.6.4
--- src/lib/libutil/parsedate.y:1.16.6.3	Sat Oct 11 16:51:23 2014
+++ src/lib/libutil/parsedate.y	Sat Oct 11 16:56:21 2014
@@ -14,7 +14,7 @@
 
 #include <sys/cdefs.h>
 #ifdef __RCSID
-__RCSID("$NetBSD: parsedate.y,v 1.16.6.3 2014/10/11 16:51:23 snj Exp $");
+__RCSID("$NetBSD: parsedate.y,v 1.16.6.4 2014/10/11 16:56:21 snj Exp $");
 #endif
 
 #include <stdio.h>
@@ -42,6 +42,7 @@ __RCSID("$NetBSD: parsedate.y,v 1.16.6.3
 #define HOUR(x)		((time_t)(x) * 60)
 #define SECSPERDAY	(24L * 60L * 60L)
 
+#define USE_LOCAL_TIME	99999 /* special case for Convert() and yyTimezone */
 
 /*
 **  An entry in the lexical lookup table.
@@ -618,7 +619,8 @@ Convert(
     time_t	Hours,		/* Hour of day [0-24] */
     time_t	Minutes,	/* Minute of hour [0-59] */
     time_t	Seconds,	/* Second of minute [0-60] */
-    time_t	Timezone,	/* Timezone as minutes east of UTC */
+    time_t	Timezone,	/* Timezone as minutes east of UTC,
+				 * or USE_LOCAL_TIME special case */
     MERIDIAN	Meridian,	/* Hours are am/pm/24 hour clock */
     DSTMODE	DSTmode		/* DST on/off/maybe */
 )
@@ -638,9 +640,25 @@ Convert(
     default:     tm.tm_isdst = -1; break;
     }
 
-    /* We rely on mktime_z(NULL, ...) working in UTC, not in local time. */
-    result = mktime_z(NULL, &tm);
-    result += Timezone * 60;
+    if (Timezone == USE_LOCAL_TIME) {
+	    result = mktime(&tm);
+    } else {
+	    /* We rely on mktime_z(NULL, ...) working in UTC */
+	    result = mktime_z(NULL, &tm);
+	    result += Timezone * 60;
+    }
+
+#if PARSEDATE_DEBUG
+    fprintf(stderr, "%s(M=%jd D=%jd Y=%jd H=%jd M=%jd S=%jd Z=%jd"
+		    " mer=%d DST=%d)",
+	__func__,
+	(intmax_t)Month, (intmax_t)Day, (intmax_t)Year,
+	(intmax_t)Hours, (intmax_t)Minutes, (intmax_t)Seconds,
+	(intmax_t)Timezone, (int)Meridian, (int)DSTmode);
+    fprintf(stderr, " -> %jd", (intmax_t)result);
+    fprintf(stderr, " %s", ctime(&result));
+#endif
+
     return result;
 }
 
@@ -878,31 +896,10 @@ yylex(YYSTYPE *yylval, const char **yyIn
 
 #define TM_YEAR_ORIGIN 1900
 
-/* Yield A - B, measured in seconds.  */
-static time_t
-difftm (struct tm *a, struct tm *b)
-{
-  int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
-  int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
-  int days = (
-	      /* difference in day of year */
-	      a->tm_yday - b->tm_yday
-	      /* + intervening leap days */
-	      +  ((ay >> 2) - (by >> 2))
-	      -  (ay/100 - by/100)
-	      +  ((ay/100 >> 2) - (by/100 >> 2))
-	      /* + difference in years * 365 */
-	      +  (long)(ay-by) * 365
-	      );
-  return ((time_t)60*(60*(24*days + (a->tm_hour - b->tm_hour))
-	      + (a->tm_min - b->tm_min))
-	  + (a->tm_sec - b->tm_sec));
-}
-
 time_t
 parsedate(const char *p, const time_t *now, const int *zone)
 {
-    struct tm gmt, local, *gmt_ptr, *tm;
+    struct tm		local, *tm;
     time_t		nowt;
     int			zonet;
     time_t		Start;
@@ -913,29 +910,24 @@ parsedate(const char *p, const time_t *n
     saved_errno = errno;
     errno = 0;
 
-    if (now == NULL || zone == NULL) {
+    if (now == NULL) {
         now = &nowt;
-	zone = &zonet;
 	(void)time(&nowt);
-
-	gmt_ptr = gmtime_r(now, &gmt);
+    }
+    if (zone == NULL) {
+	zone = &zonet;
+	zonet = USE_LOCAL_TIME;
 	if ((tm = localtime_r(now, &local)) == NULL)
 	    return -1;
-
-	if (gmt_ptr != NULL)
-	    zonet = difftm(&gmt, &local) / 60;
-	else
-	    /* We are on a system like VMS, where the system clock is
-	       in local time and the system has no concept of timezones.
-	       Hopefully we can fake this out (for the case in which the
-	       user specifies no timezone) by just saying the timezone
-	       is zero.  */
-	    zonet = 0;
-
-	if (local.tm_isdst)
-	    zonet += 60;
     } else {
-	if ((tm = localtime_r(now, &local)) == NULL)
+	/*
+	 * Should use the specified zone, not localtime.
+	 * Fake it using gmtime and arithmetic.
+	 * This is good enough because we use only the year/month/day,
+	 * not other fields of struct tm.
+	 */
+	time_t fake = *now + (*zone * 60);
+	if ((tm = gmtime_r(&fake, &local)) == NULL)
 	    return -1;
     }
     param.yyYear = tm->tm_year + 1900;

Reply via email to