[PATCH 10/10] timegm: add portable implementation (Solaris support)

Subject: [PATCH 10/10] timegm: add portable implementation (Solaris support)

Date: Sat, 3 Nov 2012 20:16:02 -0700

To: notmuch@notmuchmail.org

Cc: Blake Jones

From: Blake Jones


The timegm(3) function is a non-standard extension to libc which is
available in GNU libc and on some BSDs.  Although SunOS had this
function in its libc, Solaris (unfortunately) removed it.  This patch
implements a very simple version of timegm() which is good enough for
parse-time-string.c.

Although notmuch's idiom for portability is to test for native
availability and put alternate versions in compat/, that approach led to
a compilation problem in this case.  libnotmuch.a includes a call to
parse_time_string() from parse-time-vrp.o, and parse_time_string() in
libparse-time-string.a needs to call timegm().  An attempt to create
compat/timegm.c caused the link to fail, because libparse-time-string.a
acquired a dependency on the new timegm.o in libnotmuch.a, and the
linker only does a single pass on each ".a" looking for dependencies.
This seems to be the case both for the GNU linker and the Solaris
linker.  A different possible workaround would have been to include
libnotmuch.a multiple times on the link line, but that seemed like a
brittle way to track this dependency.
---
 parse-time-string/parse-time-string.c |   37 ++++++++++++++++++++++++++++++++-
 1 file changed, 36 insertions(+), 1 deletion(-)

diff --git a/parse-time-string/parse-time-string.c b/parse-time-string/parse-time-string.c
index 584067d..28901af 100644
--- a/parse-time-string/parse-time-string.c
+++ b/parse-time-string/parse-time-string.c
@@ -1315,6 +1315,41 @@ fixup_ampm (struct state *state)
     return 0;
 }
 
+static int
+leapyear (int year)
+{
+    return ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0));
+}
+
+/*
+ * This is a simple implementation of timegm() which does what is needed
+ * by create_output() -- just turns the "struct tm" into a GMT time_t.
+ * It does not normalize any of the fields of the "struct tm", nor does
+ * it set tm_wday or tm_yday.
+ */
+static time_t
+local_timegm (struct tm *tm)
+{
+    int	monthlen[2][12] = {
+	{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
+	{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
+    };
+    int	year, month, days;
+
+    days = 365 * (tm->tm_year - 70);
+    for (year = 70; year < tm->tm_year; year++) {
+	if (leapyear(1900 + year)) {
+	    days++;
+	}
+    }
+    for (month = 0; month < tm->tm_mon; month++) {
+	days += monthlen[leapyear(1900 + year)][month];
+    }
+    days += tm->tm_mday - 1;
+
+    return ((((days * 24) + tm->tm_hour) * 60 + tm->tm_min) * 60 + tm->tm_sec);
+}
+
 /* Combine absolute and relative fields, and round. */
 static int
 create_output (struct state *state, time_t *t_out, const time_t *ref,
@@ -1465,7 +1500,7 @@ create_output (struct state *state, time_t *t_out, const time_t *ref,
     if (is_field_set (state, TM_TZ)) {
 	/* tm is in specified TZ, convert to UTC for timegm(3). */
 	tm.tm_min -= get_field (state, TM_TZ);
-	t = timegm (&tm);
+	t = local_timegm (&tm);
     } else {
 	/* tm is in local time. */
 	t = mktime (&tm);
-- 
1.7.9.2


Thread: