[PATCH] Refactor mktime and add the POSIX function timegm

"R. Diez via Newlib" <[email protected]>
Newsgroups gmane.comp.lib.newlib
Message-ID <[email protected]>
I have manually reconstructed the patch from Andrew Russell posted here:

https://sourceware.org/legacy-ml/newlib/2018/msg00824.html

Is the new implementation of __is_leap_year() right? I would rather keep the old ISLEAP implementation.

R. Diez (1):
  Refactor mktime and add the POSIX function timegm

 newlib/libc/include/time.h   |  3 ++
 newlib/libc/saber            |  1 +
 newlib/libc/time/Makefile.am |  2 +
 newlib/libc/time/Makefile.in |  9 +++++
 newlib/libc/time/local.h     |  2 +
 newlib/libc/time/mktime.c    | 78 +++++++++++++++++++++++++-----------
 newlib/libc/time/timegm.c    | 63 +++++++++++++++++++++++++++++
 7 files changed, 135 insertions(+), 23 deletions(-)
 create mode 100644 newlib/libc/time/timegm.c

-- 
2.31.1

From b935001fcc817efe1187d993c076f65a7988e468 Mon Sep 17 00:00:00 2001
From: "R. Diez" <[email protected]>
Date: Mon, 3 May 2021 10:27:32 +0200
Subject: [PATCH 1/1] Refactor mktime and add the POSIX function timegm

Original author: Andrew Russell <[email protected]>
Signed-off-by: R. Diez <[email protected]>
---
 newlib/libc/include/time.h   |  3 ++
 newlib/libc/saber            |  1 +
 newlib/libc/time/Makefile.am |  2 +
 newlib/libc/time/Makefile.in |  9 +++++
 newlib/libc/time/local.h     |  2 +
 newlib/libc/time/mktime.c    | 78 +++++++++++++++++++++++++-----------
 newlib/libc/time/timegm.c    | 63 +++++++++++++++++++++++++++++
 7 files changed, 135 insertions(+), 23 deletions(-)
 create mode 100644 newlib/libc/time/timegm.c

diff --git a/newlib/libc/include/time.h b/newlib/libc/include/time.h
index ed6cc70fe..28aa978fc 100644
--- a/newlib/libc/include/time.h
+++ b/newlib/libc/include/time.h
@@ -56,6 +56,9 @@ struct tm
 clock_t	   clock (void);
 double	   difftime (time_t _time2, time_t _time1);
 time_t	   mktime (struct tm *_timeptr);
+#if __BSD_VISIBLE || __SVID_VISIBLE || __GNU_VISIBLE
+time_t    timegm (struct tm *_timeptr);
+#endif // __BSD_VISIBLE || __SVID_VISIBLE || __GNU_VISIBLE
 time_t	   time (time_t *_timer);
 #ifndef _REENT_ONLY
 char	  *asctime (const struct tm *_tblock);
diff --git a/newlib/libc/saber b/newlib/libc/saber
index 154eddf41..bdaef28fe 100644
--- a/newlib/libc/saber
+++ b/newlib/libc/saber
@@ -122,6 +122,7 @@ time/gmtime.c
 time/localtime.c
 time/mktime.c
 time/strftime.c
+time/timegm.c
 
 
 load stdio/fiprintf.c
diff --git a/newlib/libc/time/Makefile.am b/newlib/libc/time/Makefile.am
index be040baec..5c73c3a17 100644
--- a/newlib/libc/time/Makefile.am
+++ b/newlib/libc/time/Makefile.am
@@ -17,6 +17,7 @@ LIB_SOURCES = \
 	lcltime.c	\
 	lcltime_r.c	\
 	mktime.c	\
+	timegm.c	\
 	month_lengths.c \
 	strftime.c  	\
 	strptime.c	\
@@ -54,6 +55,7 @@ CHEWOUT_FILES = \
 	gmtime.def	\
 	lcltime.def	\
 	mktime.def	\
+	timegm.def	\
 	strftime.def	\
 	time.def	\
 	tzlock.def	\
diff --git a/newlib/libc/time/Makefile.in b/newlib/libc/time/Makefile.in
index ce6b6c183..b20ae09c4 100644
--- a/newlib/libc/time/Makefile.in
+++ b/newlib/libc/time/Makefile.in
@@ -80,6 +80,7 @@ am__objects_1 = lib_a-asctime.$(OBJEXT) lib_a-asctime_r.$(OBJEXT) \
 	lib_a-lcltime_r.$(OBJEXT) lib_a-mktime.$(OBJEXT) \
 	lib_a-month_lengths.$(OBJEXT) lib_a-strftime.$(OBJEXT) \
 	lib_a-strptime.$(OBJEXT) lib_a-time.$(OBJEXT) \
+	lib_a-timegm.$(OBJEXT) \
 	lib_a-tzcalc_limits.$(OBJEXT) lib_a-tzlock.$(OBJEXT) \
 	lib_a-tzset.$(OBJEXT) lib_a-tzset_r.$(OBJEXT) \
 	lib_a-tzvars.$(OBJEXT) lib_a-wcsftime.$(OBJEXT)
@@ -91,6 +92,7 @@ am__objects_2 = asctime.lo asctime_r.lo clock.lo ctime.lo ctime_r.lo \
 	difftime.lo gettzinfo.lo gmtime.lo gmtime_r.lo lcltime.lo \
 	lcltime_r.lo mktime.lo month_lengths.lo strftime.lo \
 	strptime.lo time.lo tzcalc_limits.lo tzlock.lo tzset.lo \
+	timegm.lo \
 	tzset_r.lo tzvars.lo wcsftime.lo
 @USE_LIBTOOL_TRUE@am_libtime_la_OBJECTS = $(am__objects_2)
 libtime_la_OBJECTS = $(am_libtime_la_OBJECTS)
@@ -283,6 +285,7 @@ LIB_SOURCES = \
 	strftime.c  	\
 	strptime.c	\
 	time.c		\
+	timegm.c	\
 	tzcalc_limits.c \
 	tzlock.c	\
 	tzset.c		\
@@ -464,6 +467,12 @@ lib_a-mktime.o: mktime.c
 lib_a-mktime.obj: mktime.c
 	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lib_a_CFLAGS) $(CFLAGS) -c -o lib_a-mktime.obj `if test -f 'mktime.c'; then $(CYGPATH_W) 'mktime.c'; else $(CYGPATH_W) '$(srcdir)/mktime.c'; fi`
 
+lib_a-timegm.o: timegm.c
+	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lib_a_CFLAGS) $(CFLAGS) -c -o lib_a-timegm.o `test -f 'timegm.c' || echo '$(srcdir)/'`timegm.c
+
+lib_a-timegm.obj: timegm.c
+	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lib_a_CFLAGS) $(CFLAGS) -c -o lib_a-timegm.obj `if test -f 'timegm.c'; then $(CYGPATH_W) 'timegm.c'; else $(CYGPATH_W) '$(srcdir)/timegm.c'; fi`
+
 lib_a-month_lengths.o: month_lengths.c
 	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lib_a_CFLAGS) $(CFLAGS) -c -o lib_a-month_lengths.o `test -f 'month_lengths.c' || echo '$(srcdir)/'`month_lengths.c
 
diff --git a/newlib/libc/time/local.h b/newlib/libc/time/local.h
index bfe06e622..557187e0e 100644
--- a/newlib/libc/time/local.h
+++ b/newlib/libc/time/local.h
@@ -39,3 +39,5 @@ void _tzset_unlocked (void);
 void __tz_lock (void);
 void __tz_unlock (void);
 
+time_t __mktime_internal (struct tm *tim_p);
+void __set_tm_wday (long days, struct tm *tim_p);
diff --git a/newlib/libc/time/mktime.c b/newlib/libc/time/mktime.c
index 02032599a..c2717b08c 100644
--- a/newlib/libc/time/mktime.c
+++ b/newlib/libc/time/mktime.c
@@ -11,6 +11,8 @@
  * represented, returns the value (time_t) -1.
  *
  * Modifications:	Fixed tm_isdst usage - 27 August 2008 Craig Howland.
+ * 			Refactor code from mktime.c to share internal
+ * 			functions. - 17 July 2018 Andrew Russell.
  */
 
 /*
@@ -44,25 +46,36 @@ ANSI C requires <<mktime>>.
 
 #include <stdlib.h>
 #include <time.h>
+#include <stdint.h>
 #include "local.h"
 
 #define _SEC_IN_MINUTE 60L
 #define _SEC_IN_HOUR 3600L
 #define _SEC_IN_DAY 86400L
 
-static const int DAYS_IN_MONTH[12] =
+static const uint_least8_t DAYS_IN_MONTH[12] =
 {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
 
 #define _DAYS_IN_MONTH(x) ((x == 1) ? days_in_feb : DAYS_IN_MONTH[x])
 
-static const int _DAYS_BEFORE_MONTH[12] =
+static const uint_least16_t _DAYS_BEFORE_MONTH[12] =
 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
 
-#define _ISLEAP(y) (((y) % 4) == 0 && (((y) % 100) != 0 || (((y)+1900) % 400) == 0))
-#define _DAYS_IN_YEAR(year) (_ISLEAP(year) ? 366 : 365)
+/* returns either 0 or 1 */
+static int
+__is_leap_year (int year)
+{
+  return (year % 4) == 0 && ((year % 100) != 0 || (((year / 100) & 3) == (-(YEAR_BASE / 100)  & 3)));
+}
+
+static int
+__days_in_year (int year)
+{
+  return __is_leap_year(year) ? 366 : 365;
+}
 
-static void 
-validate_structure (struct tm *tim_p)
+static void
+__validate_tm_structure (struct tm *tim_p)
 {
   div_t res;
   int days_in_feb = 28;
@@ -112,7 +125,7 @@ validate_structure (struct tm *tim_p)
         }
     }
 
-  if (_DAYS_IN_YEAR (tim_p->tm_year) == 366)
+  if (__days_in_year(tim_p->tm_year) == 366)
     days_in_feb = 29;
 
   if (tim_p->tm_mday <= 0)
@@ -124,7 +137,7 @@ validate_structure (struct tm *tim_p)
 	      tim_p->tm_year--;
 	      tim_p->tm_mon = 11;
 	      days_in_feb =
-		((_DAYS_IN_YEAR (tim_p->tm_year) == 366) ?
+	        ((__days_in_year(tim_p->tm_year) == 366) ?
 		 29 : 28);
 	    }
 	  tim_p->tm_mday += _DAYS_IN_MONTH (tim_p->tm_mon);
@@ -140,15 +153,23 @@ validate_structure (struct tm *tim_p)
 	      tim_p->tm_year++;
 	      tim_p->tm_mon = 0;
 	      days_in_feb =
-		((_DAYS_IN_YEAR (tim_p->tm_year) == 366) ?
+	        ((__days_in_year(tim_p->tm_year) == 366) ?
 		 29 : 28);
 	    }
 	}
     }
 }
 
-time_t 
-mktime (struct tm *tim_p)
+void
+__set_tm_wday (long days, struct tm *tim_p)
+{
+  if ((tim_p->tm_wday = (days + 4) % 7) < 0)
+    tim_p->tm_wday += 7;
+}
+
+/* Assumes the time at tim_p is a UTC time and returns its arithmetic representation */
+time_t
+__mktime_internal (struct tm *tim_p)
 {
   time_t tim = 0;
   long days = 0;
@@ -156,7 +177,7 @@ mktime (struct tm *tim_p)
   __tzinfo_type *tz = __gettzinfo ();
 
   /* validate structure */
-  validate_structure (tim_p);
+  __validate_tm_structure (tim_p);
 
   /* compute hours, minutes, seconds */
   tim += tim_p->tm_sec + (tim_p->tm_min * _SEC_IN_MINUTE) +
@@ -165,7 +186,7 @@ mktime (struct tm *tim_p)
   /* compute days in year */
   days += tim_p->tm_mday - 1;
   days += _DAYS_BEFORE_MONTH[tim_p->tm_mon];
-  if (tim_p->tm_mon > 1 && _DAYS_IN_YEAR (tim_p->tm_year) == 366)
+  if (tim_p->tm_mon > 1 && __days_in_year(tim_p->tm_year) == 366)
     days++;
 
   /* compute day of the year */
@@ -178,17 +199,29 @@ mktime (struct tm *tim_p)
   if ((year = tim_p->tm_year) > 70)
     {
       for (year = 70; year < tim_p->tm_year; year++)
-	days += _DAYS_IN_YEAR (year);
+	days += __days_in_year(year);
     }
   else if (year < 70)
     {
       for (year = 69; year > tim_p->tm_year; year--)
-	days -= _DAYS_IN_YEAR (year);
-      days -= _DAYS_IN_YEAR (year);
+	days -= __days_in_year(year);
+      days -= __days_in_year(year);
     }
 
   /* compute total seconds */
-  tim += (time_t)days * _SEC_IN_DAY;
+  tim += (days * _SEC_IN_DAY);
+
+  return tim;
+}
+
+time_t
+mktime (struct tm *tim_p)
+{
+  time_t tim = __mktime_internal(tim_p);
+  long days = tim / SECSPERDAY;
+  int year = tim_p->tm_year;
+  int isdst=0;
+  __tzinfo_type *tz = __gettzinfo ();
 
   TZ_LOCK;
 
@@ -237,7 +270,7 @@ mktime (struct tm *tim_p)
 		  tim_p->tm_sec += diff;
 		  tim += diff;  /* we also need to correct our current time calculation */
 		  int mday = tim_p->tm_mday;
-		  validate_structure (tim_p);
+		  __validate_tm_structure(tim_p);
 		  mday = tim_p->tm_mday - mday;
 		  /* roll over occurred */
 		  if (mday) {
@@ -251,9 +284,9 @@ mktime (struct tm *tim_p)
 		    /* handle yday */
 		    if ((tim_p->tm_yday += mday) < 0) {
 			  --year;
-			  tim_p->tm_yday = _DAYS_IN_YEAR(year) - 1;
+			  tim_p->tm_yday = __days_in_year(year) - 1;
 		    } else {
-			  mday = _DAYS_IN_YEAR(year);
+			  mday = __days_in_year(year);
 			  if (tim_p->tm_yday > (mday - 1))
 				tim_p->tm_yday -= mday;
 		    }
@@ -275,8 +308,7 @@ mktime (struct tm *tim_p)
   tim_p->tm_isdst = isdst;
 
   /* compute day of the week */
-  if ((tim_p->tm_wday = (days + 4) % 7) < 0)
-    tim_p->tm_wday += 7;
-	
+  __set_tm_wday(days, tim_p);
+
   return tim;
 }
diff --git a/newlib/libc/time/timegm.c b/newlib/libc/time/timegm.c
new file mode 100644
index 000000000..ab50de7e1
--- /dev/null
+++ b/newlib/libc/time/timegm.c
@@ -0,0 +1,63 @@
+/*
+ * timegm.c
+ * Original Author: A. Russell
+ *
+ * Converts the broken-down time, expressed as UTC time, in the structure
+ * pointed to by tim_p into a calendar time value. The original values of the
+ * tm_wday and tm_yday fields of the structure are ignored, and the original
+ * values of the other fields have no restrictions. On successful completion
+ * the fields of the structure are set to represent the specified calendar
+ * time. Returns the specified calendar time. If the calendar time can not be
+ * represented, returns the value (time_t) -1.  These functions are nonstandard
+ * GNU extensions that are also present on the BSDs.  Avoid their use.
+ * Modifications: Refactored mktime.c to support both mktime and timegm
+                         - 23 July 2018 Andrew Russell.
+ */
+
+/*
+FUNCTION
+<<timegm>>---convert time to arithmetic representation
+
+INDEX
+ timegm
+
+SYNOPSIS
+ #include <time.h>
+ time_t timegm(struct tm *<[timp]>);
+
+DESCRIPTION
+<<timegm>> assumes the time at <[timp]> is a UTC time, and converts
+its representation from the traditional representation defined by
+<<struct tm>> into a representation suitable for arithmetic.
+
+<<timegm>> is the inverse of <<gmtime>>.
+
+RETURNS
+If the contents of the structure at <[timp]> do not form a valid
+calendar time representation, the result is <<-1>>.  Otherwise, the
+result is the time, converted to a <<time_t>> value.
+
+PORTABILITY
+<<timegm>> is a nonstandard GNU extension to POSIX also present on BSD.
+
+<<timegm>> requires no supporting OS subroutines.
+*/
+
+#include <stdlib.h>
+#include <time.h>
+#include "local.h"
+
+time_t
+timegm (struct tm *tim_p)
+{
+  time_t tim = __mktime_internal(tim_p);
+  long days = tim / SECSPERDAY;
+
+  /* set isdst flag to 0 since we are in UTC */
+  tim_p->tm_isdst = 0;
+
+  /* compute day of the week */
+  __set_tm_wday(days, tim_p);
+
+  return tim;
+}
-- 
2.31.1
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.