Discussion:
[PATCH] Add timegm POSIX call
Andrew Russell via newlib
2018-08-14 01:17:06 UTC
Permalink
From e182faa79c35984b667029ef7b6e4a8ce7329897 Mon Sep 17 00:00:00 2001
From: Andrew Russell <***@google.com>
Date: Fri, 10 Aug 2018 12:14:18 -0700
Subject: [PATCH 1/4] Start of mktime.c copy to timegm.c

I am proposing to add the timegm POSIX call to
Newlib. Part of this refactors some of the code in libc/time/local.h and
libc/time/mktime.c, per this discussion:

https://sourceware.org/ml/newlib/2018/msg00186.html

Thanks,
Andrew

---
newlib/libc/time/{mktime.c => timegm.c} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename newlib/libc/time/{mktime.c => timegm.c} (100%)

diff --git a/newlib/libc/time/mktime.c b/newlib/libc/time/timegm.c
similarity index 100%
rename from newlib/libc/time/mktime.c
rename to newlib/libc/time/timegm.c
--
2.18.0.597.ga71716f1ad-goog


From e0a614c6d92ee5c14cebf8eaa69ade344998fab6 Mon Sep 17 00:00:00 2001
From: Andrew Russell <***@google.com>
Date: Fri, 10 Aug 2018 12:14:58 -0700
Subject: [PATCH 2/4] Middle of mktime.c copy to timegm.c

---
newlib/libc/time/{mktime.c => mktime_backup.c} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename newlib/libc/time/{mktime.c => mktime_backup.c} (100%)

diff --git a/newlib/libc/time/mktime.c b/newlib/libc/time/mktime_backup.c
similarity index 100%
rename from newlib/libc/time/mktime.c
rename to newlib/libc/time/mktime_backup.c
--
2.18.0.597.ga71716f1ad-goog


From 078c008672c6e3edd98f04b7998397275f466734 Mon Sep 17 00:00:00 2001
From: Andrew Russell <***@google.com>
Date: Fri, 10 Aug 2018 12:16:23 -0700
Subject: [PATCH 3/4] Final commit to copy mktime.c to timegm.c

---
newlib/libc/time/{mktime_backup.c => mktime.c} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename newlib/libc/time/{mktime_backup.c => mktime.c} (100%)

diff --git a/newlib/libc/time/mktime_backup.c b/newlib/libc/time/mktime.c
similarity index 100%
rename from newlib/libc/time/mktime_backup.c
rename to newlib/libc/time/mktime.c
--
2.18.0.597.ga71716f1ad-goog


From 2a8129248f33150c4ee0af17e34de37fcdaecb97 Mon Sep 17 00:00:00 2001
From: Andrew Russell <***@google.com>
Date: Fri, 10 Aug 2018 12:23:36 -0700
Subject: [PATCH 4/4] Refactor mktime and add the POSIX function timegm

---
newlib/libc/include/time.h | 1 +
newlib/libc/saber | 1 +
newlib/libc/time/Makefile.am | 2 +
newlib/libc/time/Makefile.in | 11 ++-
newlib/libc/time/local.h | 5 ++
newlib/libc/time/mktime.c | 163 ++++------------------------------
newlib/libc/time/timegm.c | 164 ++++++++++++-----------------------
7 files changed, 90 insertions(+), 257 deletions(-)

diff --git a/newlib/libc/include/time.h b/newlib/libc/include/time.h
index a2efcc15e..8440ecb3c 100644
--- a/newlib/libc/include/time.h
+++ b/newlib/libc/include/time.h
@@ -56,6 +56,7 @@ struct tm
clock_t clock (void);
double difftime (time_t _time2, time_t _time1);
time_t mktime (struct tm *_timeptr);
+time_t timegm (struct tm *_timeptr);
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 4f16f976e..f57063f82 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 a32333234..2845f7438 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)
@@ -90,7 +91,8 @@ libtime_la_LIBADD =
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 \
+ strptime.lo time.lo timegm.lo \
+ tzcalc_limits.lo tzlock.lo tzset.lo \
tzset_r.lo tzvars.lo wcsftime.lo
@***@am_libtime_la_OBJECTS = $(am__objects_2)
libtime_la_OBJECTS = $(am_libtime_la_OBJECTS)
@@ -282,6 +284,7 @@ LIB_SOURCES = \
strftime.c \
strptime.c \
time.c \
+ timegm.c \
tzcalc_limits.c \
tzlock.c \
tzset.c \
@@ -463,6 +466,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 dce51cda2..543b9fd25 100644
--- a/newlib/libc/time/local.h
+++ b/newlib/libc/time/local.h
@@ -38,3 +38,8 @@ void _tzset_unlocked (void);
void __tz_lock (void);
void __tz_unlock (void);

+time_t __timegm_internal (struct tm *tim_p);
+void __set_tm_wday (long days, struct tm *tim_p);
+void __validate_tm_structure (struct tm *tim_p);
+
+int __days_in_year (int year);
diff --git a/newlib/libc/time/mktime.c b/newlib/libc/time/mktime.c
index 02032599a..ab47f8614 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 shared internal
+ * functions. - 17 July 2018 Andrew Russell.
*/

/*
@@ -39,157 +41,22 @@ result is the time, converted to a <<time_t>> value.
PORTABILITY
ANSI C requires <<mktime>>.

-<<mktime>> requires no supporting OS subroutines.
+<<timegm>> requires no supporting OS subroutines.
*/

#include <stdlib.h>
#include <time.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] =
-{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] =
-{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)
-
-static void
-validate_structure (struct tm *tim_p)
-{
- div_t res;
- int days_in_feb = 28;
-
- /* calculate time & date to account for out of range values */
- if (tim_p->tm_sec < 0 || tim_p->tm_sec > 59)
- {
- res = div (tim_p->tm_sec, 60);
- tim_p->tm_min += res.quot;
- if ((tim_p->tm_sec = res.rem) < 0)
- {
- tim_p->tm_sec += 60;
- --tim_p->tm_min;
- }
- }
-
- if (tim_p->tm_min < 0 || tim_p->tm_min > 59)
- {
- res = div (tim_p->tm_min, 60);
- tim_p->tm_hour += res.quot;
- if ((tim_p->tm_min = res.rem) < 0)
- {
- tim_p->tm_min += 60;
- --tim_p->tm_hour;
- }
- }
-
- if (tim_p->tm_hour < 0 || tim_p->tm_hour > 23)
- {
- res = div (tim_p->tm_hour, 24);
- tim_p->tm_mday += res.quot;
- if ((tim_p->tm_hour = res.rem) < 0)
- {
- tim_p->tm_hour += 24;
- --tim_p->tm_mday;
- }
- }
-
- if (tim_p->tm_mon < 0 || tim_p->tm_mon > 11)
- {
- res = div (tim_p->tm_mon, 12);
- tim_p->tm_year += res.quot;
- if ((tim_p->tm_mon = res.rem) < 0)
- {
- tim_p->tm_mon += 12;
- --tim_p->tm_year;
- }
- }
-
- if (_DAYS_IN_YEAR (tim_p->tm_year) == 366)
- days_in_feb = 29;
-
- if (tim_p->tm_mday <= 0)
- {
- while (tim_p->tm_mday <= 0)
- {
- if (--tim_p->tm_mon == -1)
- {
- tim_p->tm_year--;
- tim_p->tm_mon = 11;
- days_in_feb =
- ((_DAYS_IN_YEAR (tim_p->tm_year) == 366) ?
- 29 : 28);
- }
- tim_p->tm_mday += _DAYS_IN_MONTH (tim_p->tm_mon);
- }
- }
- else
- {
- while (tim_p->tm_mday > _DAYS_IN_MONTH (tim_p->tm_mon))
- {
- tim_p->tm_mday -= _DAYS_IN_MONTH (tim_p->tm_mon);
- if (++tim_p->tm_mon == 12)
- {
- tim_p->tm_year++;
- tim_p->tm_mon = 0;
- days_in_feb =
- ((_DAYS_IN_YEAR (tim_p->tm_year) == 366) ?
- 29 : 28);
- }
- }
- }
-}
-
-time_t
+time_t
mktime (struct tm *tim_p)
{
- time_t tim = 0;
- long days = 0;
- int year, isdst=0;
+ time_t tim = __timegm_internal(tim_p);
+ long days = tim / SECSPERDAY;
+ int year = tim_p->tm_year;
+ int isdst=0;
__tzinfo_type *tz = __gettzinfo ();

- /* validate structure */
- validate_structure (tim_p);
-
- /* compute hours, minutes, seconds */
- tim += tim_p->tm_sec + (tim_p->tm_min * _SEC_IN_MINUTE) +
- (tim_p->tm_hour * _SEC_IN_HOUR);
-
- /* 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)
- days++;
-
- /* compute day of the year */
- tim_p->tm_yday = days;
-
- if (tim_p->tm_year > 10000 || tim_p->tm_year < -10000)
- return (time_t) -1;
-
- /* compute days in other years */
- if ((year = tim_p->tm_year) > 70)
- {
- for (year = 70; year < tim_p->tm_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);
- }
-
- /* compute total seconds */
- tim += (time_t)days * _SEC_IN_DAY;
-
TZ_LOCK;

_tzset_unlocked ();
@@ -204,7 +71,7 @@ mktime (struct tm *tim_p)

if (y == tz->__tzyear || __tzcalc_limits (y))
{
- /* calculate start of dst in dst local time and
+ /* calculate start of dst in dst local time and
start of std in both std local time and dst local time */
time_t startdst_dst = tz->__tzrule[0].change
- (time_t) tz->__tzrule[1].offset;
@@ -237,7 +104,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 +118,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 +142,8 @@ 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
index 02032599a..3299ce228 100644
--- a/newlib/libc/time/timegm.c
+++ b/newlib/libc/time/timegm.c
@@ -1,8 +1,8 @@
/*
- * mktime.c
+ * timegm.c
* Original Author: G. Haley
*
- * Converts the broken-down time, expressed as local time, in the structure
+ * 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
@@ -11,25 +11,27 @@
* represented, returns the value (time_t) -1.
*
* Modifications: Fixed tm_isdst usage - 27 August 2008 Craig Howland.
+ * Refactored mktime.c to support both mktime and timegm
+ * - 23 July 2018 Andrew Russell.
*/

/*
FUNCTION
-<<mktime>>---convert time to arithmetic representation
+<<timegm>>---convert time to arithmetic representation

INDEX
- mktime
+ timegm

SYNOPSIS
#include <time.h>
- time_t mktime(struct tm *<[timp]>);
+ time_t timegm(struct tm *<[timp]>);

DESCRIPTION
-<<mktime>> assumes the time at <[timp]> is a local time, and converts
+<<timegm>> assumes the time at <[timp]> is UTC time, and converts
its representation from the traditional representation defined by
<<struct tm>> into a representation suitable for arithmetic.

-<<localtime>> is the inverse of <<mktime>>.
+<<timegm>> is the inverse of <<gmtime>>.

RETURNS
If the contents of the structure at <[timp]> do not form a valid
@@ -37,9 +39,9 @@ calendar time representation, the result is <<-1>>.
Otherwise, the
result is the time, converted to a <<time_t>> value.

PORTABILITY
-ANSI C requires <<mktime>>.
+<<timegm>> is a nonstandard GNU extension to POSIX also present on BSD.

-<<mktime>> requires no supporting OS subroutines.
+<<timegm>> requires no supporting OS subroutines.
*/

#include <stdlib.h>
@@ -58,11 +60,8 @@ static const int DAYS_IN_MONTH[12] =
static const int _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)
-
-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 +111,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 +123,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 +139,36 @@ 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;
+}
+
+/* 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)));
+}
+
+int
+__days_in_year (int year)
+{
+ return __is_leap_year(year) ? 366 : 365;
+}
+
+time_t
+__timegm_internal (struct tm *tim_p)
{
time_t tim = 0;
long days = 0;
@@ -156,7 +176,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 +185,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,105 +198,33 @@ 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;

- TZ_LOCK;
-
- _tzset_unlocked ();
-
- if (_daylight)
- {
- int tm_isdst;
- int y = tim_p->tm_year + YEAR_BASE;
- /* Convert user positive into 1 */
- tm_isdst = tim_p->tm_isdst > 0 ? 1 : tim_p->tm_isdst;
- isdst = tm_isdst;
-
- if (y == tz->__tzyear || __tzcalc_limits (y))
- {
- /* calculate start of dst in dst local time and
- start of std in both std local time and dst local time */
- time_t startdst_dst = tz->__tzrule[0].change
- - (time_t) tz->__tzrule[1].offset;
- time_t startstd_dst = tz->__tzrule[1].change
- - (time_t) tz->__tzrule[1].offset;
- time_t startstd_std = tz->__tzrule[1].change
- - (time_t) tz->__tzrule[0].offset;
- /* if the time is in the overlap between dst and std local times */
- if (tim >= startstd_std && tim < startstd_dst)
- ; /* we let user decide or leave as -1 */
- else
- {
- isdst = (tz->__tznorth
- ? (tim >= startdst_dst && tim < startstd_std)
- : (tim >= startdst_dst || tim < startstd_std));
- /* if user committed and was wrong, perform correction, but not
- * if the user has given a negative value (which
- * asks mktime() to determine if DST is in effect or not) */
- if (tm_isdst >= 0 && (isdst ^ tm_isdst) == 1)
- {
- /* we either subtract or add the difference between
- time zone offsets, depending on which way the user got it
- wrong. The diff is typically one hour, or 3600 seconds,
- and should fit in a 16-bit int, even though offset
- is a long to accomodate 12 hours. */
- int diff = (int) (tz->__tzrule[0].offset
- - tz->__tzrule[1].offset);
- if (!isdst)
- diff = -diff;
- 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);
- mday = tim_p->tm_mday - mday;
- /* roll over occurred */
- if (mday) {
- /* compensate for month roll overs */
- if (mday > 1)
- mday = -1;
- else if (mday < -1)
- mday = 1;
- /* update days for wday calculation */
- days += mday;
- /* handle yday */
- if ((tim_p->tm_yday += mday) < 0) {
- --year;
- tim_p->tm_yday = _DAYS_IN_YEAR(year) - 1;
- } else {
- mday = _DAYS_IN_YEAR(year);
- if (tim_p->tm_yday > (mday - 1))
- tim_p->tm_yday -= mday;
- }
- }
- }
- }
- }
- }
-
- /* add appropriate offset to put time in gmt format */
- if (isdst == 1)
- tim += (time_t) tz->__tzrule[1].offset;
- else /* otherwise assume std time */
- tim += (time_t) tz->__tzrule[0].offset;
+ return tim;
+}

- TZ_UNLOCK;
+time_t
+timegm (struct tm *tim_p)
+{
+ time_t tim = __timegm_internal(tim_p);
+ long days = tim / SECSPERDAY;

- /* reset isdst flag to what we have calculated */
- tim_p->tm_isdst = isdst;
+ /* set isdst flag to 0 since we are in UTC */
+ tim_p->tm_isdst = 0;

/* 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;
}
+
--
2.18.0.597.ga71716f1ad-goog
Corinna Vinschen
2018-08-14 15:42:31 UTC
Permalink
Post by Andrew Russell via newlib
Post by Andrew Russell via newlib
From e182faa79c35984b667029ef7b6e4a8ce7329897 Mon Sep 17 00:00:00 2001
Date: Fri, 10 Aug 2018 12:14:18 -0700
Subject: [PATCH 1/4] Start of mktime.c copy to timegm.c
I am proposing to add the timegm POSIX call to
Newlib. Part of this refactors some of the code in libc/time/local.h and
https://sourceware.org/ml/newlib/2018/msg00186.html
I'm looking for comments from other (non-Cygwin) devs here.


Thanks,
Corinna
--
Corinna Vinschen
Cygwin Maintainer
Red Hat
Joel Sherrill
2018-08-14 17:49:20 UTC
Permalink
Post by Corinna Vinschen
Post by Andrew Russell via newlib
Post by Andrew Russell via newlib
From e182faa79c35984b667029ef7b6e4a8ce7329897 Mon Sep 17 00:00:00 2001
Date: Fri, 10 Aug 2018 12:14:18 -0700
Subject: [PATCH 1/4] Start of mktime.c copy to timegm.c
I am proposing to add the timegm POSIX call to
Newlib. Part of this refactors some of the code in libc/time/local.h and
https://sourceware.org/ml/newlib/2018/msg00186.html
I'm looking for comments from other (non-Cygwin) devs here.
From my perspective, I don't mind having common methods that
are not in libc or POSIX in newlib or RTEMS. Ultimately, the wider
set of methods makes packages easier to port.

I would ask that the method is documented using the newlib markup
and that its historical origin is noted. The Linux man page is clearly
discouraging:

CONFORMING TO
These functions are nonstandard GNU extensions that are also present
on
the BSDs. Avoid their use; see NOTES.

But overall, improving cross-platform compatibility is good even
when it means adding extensions that have no impact when not
used.

--joel
Post by Corinna Vinschen
Thanks,
Corinna
--
Corinna Vinschen
Cygwin Maintainer
Red Hat
Corinna Vinschen
2018-08-15 08:54:43 UTC
Permalink
Post by Joel Sherrill
Post by Corinna Vinschen
Post by Andrew Russell via newlib
Post by Andrew Russell via newlib
From e182faa79c35984b667029ef7b6e4a8ce7329897 Mon Sep 17 00:00:00 2001
Date: Fri, 10 Aug 2018 12:14:18 -0700
Subject: [PATCH 1/4] Start of mktime.c copy to timegm.c
I am proposing to add the timegm POSIX call to
Newlib. Part of this refactors some of the code in libc/time/local.h and
https://sourceware.org/ml/newlib/2018/msg00186.html
I'm looking for comments from other (non-Cygwin) devs here.
From my perspective, I don't mind having common methods that
are not in libc or POSIX in newlib or RTEMS. Ultimately, the wider
set of methods makes packages easier to port.
I would ask that the method is documented using the newlib markup
and that its historical origin is noted. The Linux man page is clearly
CONFORMING TO
These functions are nonstandard GNU extensions that are also present
on
the BSDs. Avoid their use; see NOTES.
But overall, improving cross-platform compatibility is good even
when it means adding extensions that have no impact when not
used.
--joel
The new functions are ok to go in, I was more interested in people
testing the code. Sorry if I was unclear!


Corinna
--
Corinna Vinschen
Cygwin Maintainer
Red Hat
Andreas Köpke
2018-08-15 07:52:59 UTC
Permalink
Post by Corinna Vinschen
Post by Andrew Russell via newlib
Post by Andrew Russell via newlib
From e182faa79c35984b667029ef7b6e4a8ce7329897 Mon Sep 17 00:00:00 2001
Date: Fri, 10 Aug 2018 12:14:18 -0700
Subject: [PATCH 1/4] Start of mktime.c copy to timegm.c
I am proposing to add the timegm POSIX call to
Newlib. Part of this refactors some of the code in libc/time/local.h and
https://sourceware.org/ml/newlib/2018/msg00186.html
I'm looking for comments from other (non-Cygwin) devs here.
Thanks,
Corinna
I did not test it yet but we need this function. cygwin has it, but uses a
completely different codebase (localtime.cc).

It is always needed if your default timezone is not GMT and you need to
exchange time information with e.g. sync mechanisms like NTP or GPS. You
usually get broken down time information referenced to GMT and need to re-
assemble using GMT as reference.

Using setenv/getenv to temporarily change the timezone does not work: there is
a memory leak in the implementation. Well, at least it looks like one.

Parsing the TZ information takes time (at least on 12MHz MCUs) and stack space
(~ 600 bytes) which is not always available. So having a working
implementation would be great.

Best regards, Andreas
Corinna Vinschen
2018-08-15 08:27:08 UTC
Permalink
Post by Andreas Köpke
Post by Corinna Vinschen
Post by Andrew Russell via newlib
Post by Andrew Russell via newlib
From e182faa79c35984b667029ef7b6e4a8ce7329897 Mon Sep 17 00:00:00 2001
Date: Fri, 10 Aug 2018 12:14:18 -0700
Subject: [PATCH 1/4] Start of mktime.c copy to timegm.c
I am proposing to add the timegm POSIX call to
Newlib. Part of this refactors some of the code in libc/time/local.h and
https://sourceware.org/ml/newlib/2018/msg00186.html
I'm looking for comments from other (non-Cygwin) devs here.
Thanks,
Corinna
I did not test it yet but we need this function. cygwin has it, but uses a
completely different codebase (localtime.cc).
It is always needed if your default timezone is not GMT and you need to
exchange time information with e.g. sync mechanisms like NTP or GPS. You
usually get broken down time information referenced to GMT and need to re-
assemble using GMT as reference.
Using setenv/getenv to temporarily change the timezone does not work: there is
a memory leak in the implementation. Well, at least it looks like one.
Patch?


Corinna
--
Corinna Vinschen
Cygwin Maintainer
Red Hat
Craig Howland
2018-08-15 15:01:34 UTC
Permalink
Post by Andrew Russell via newlib
Date: Fri, 10 Aug 2018 12:14:18 -0700
Subject: [PATCH 1/4] Start of mktime.c copy to timegm.c
I am proposing to add the timegm POSIX call to
Newlib. Part of this refactors some of the code in libc/time/local.h and
https://sourceware.org/ml/newlib/2018/msg00186.html
Thanks,
Andrew
...
diff --git a/newlib/libc/include/time.h b/newlib/libc/include/time.h
index a2efcc15e..8440ecb3c 100644
--- a/newlib/libc/include/time.h
+++ b/newlib/libc/include/time.h
@@ -56,6 +56,7 @@ struct tm
clock_t clock (void);
double difftime (time_t _time2, time_t _time1);
time_t mktime (struct tm *_timeptr);
+time_t timegm (struct tm *_timeptr);
This prototype should be gated by the appropriate-version GNU extension #if. 
(According to the Linux timegm() man page the gate is _BSD_SOURCE ||
_SVID_SOURCE (although the Newlib in-header version might be different).  But
given it is called a GNU extension, you'd think _GNU_SOURCE would also be there.)
Post by Andrew Russell via newlib
time_t time (time_t *_timer);
#ifndef _REENT_ONLY
char *asctime (const struct tm *_tblock);
...
diff --git a/newlib/libc/time/mktime.c b/newlib/libc/time/mktime.c
index 02032599a..ab47f8614 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 shared internal
+ * functions. - 17 July 2018 Andrew Russell.
*/
/*
@@ -39,157 +41,22 @@ result is the time, converted to a <<time_t>> value.
PORTABILITY
ANSI C requires <<mktime>>.
-<<mktime>> requires no supporting OS subroutines.
+<<timegm>> requires no supporting OS subroutines.
Spurious change, as this file is still mktime.
Post by Andrew Russell via newlib
...
diff --git a/newlib/libc/time/timegm.c b/newlib/libc/time/timegm.c
(This diff as presented does not make sense from the point of view that this is
a new file.)
Post by Andrew Russell via newlib
index 02032599a..3299ce228 100644
--- a/newlib/libc/time/timegm.c
+++ b/newlib/libc/time/timegm.c
@@ -1,8 +1,8 @@
/*
- * mktime.c
+ * timegm.c
* Original Author: G. Haley
*
- * Converts the broken-down time, expressed as local time, in the structure
+ * 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
@@ -11,25 +11,27 @@
* represented, returns the value (time_t) -1.
*
* Modifications: Fixed tm_isdst usage - 27 August 2008 Craig Howland.
Can get rid of this fix tm_isdst comment, as not in timegm, but in mktime.
The comments that make the man page here should probably include the same "These
functions are nonstandard GNU extensions that are also present on the BSDs. 
Avoid their use; see NOTES." statement that appears in the timegm/timelocal
Linux man page (with a small tweak for being only 1 function instead of two).
Post by Andrew Russell via newlib
...
#include <stdlib.h>
@@ -58,11 +60,8 @@ static const int DAYS_IN_MONTH[12] =
static const int _DAYS_BEFORE_MONTH[12] =
{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
Someone had suggested in the thread that DAYS_IN_MONTH be changed to char to
save space.  How about int_least16_t to (possibly) save space?
Post by Andrew Russell via newlib
...
-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;
+}
+
+/* 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)));
How about "(year & 3) == 0" instead of the % to save time? (Should not matter
with a good optimizer, but it is not necessarily on.)
Post by Andrew Russell via newlib
...
+time_t
+timegm (struct tm *tim_p)
+{
+ time_t tim = __timegm_internal(tim_p);
+ long days = tim / SECSPERDAY;
- /* reset isdst flag to what we have calculated */
- tim_p->tm_isdst = isdst;
+ /* set isdst flag to 0 since we are in UTC */
+ tim_p->tm_isdst = 0;
/* 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;
}
+
--
2.18.0.597.ga71716f1ad-goog
I have not been able to try testing it, yet, but there are a few thoughts based
on examination.  See inline above as well as some here.

I suggest that the new timegm.c file should contain only the new timegm()
function and that the supporting functions all belong in mktime.c.  This comes
closest to preserving the same size on existing implementations, which otherwise
would then require the timegm object to be linked; the new timegm object will
only get linked for applications which call it.  (Not that the timegm()
function, itself, is large, but it is the new extension.)

Craig
Joel Sherrill
2018-08-15 15:27:31 UTC
Permalink
Post by Craig Howland
Post by Andrew Russell via newlib
Date: Fri, 10 Aug 2018 12:14:18 -0700
Subject: [PATCH 1/4] Start of mktime.c copy to timegm.c
I am proposing to add the timegm POSIX call to
Newlib. Part of this refactors some of the code in libc/time/local.h and
https://sourceware.org/ml/newlib/2018/msg00186.html
Thanks,
Andrew
...
diff --git a/newlib/libc/include/time.h b/newlib/libc/include/time.h
index a2efcc15e..8440ecb3c 100644
--- a/newlib/libc/include/time.h
+++ b/newlib/libc/include/time.h
@@ -56,6 +56,7 @@ struct tm
clock_t clock (void);
double difftime (time_t _time2, time_t _time1);
time_t mktime (struct tm *_timeptr);
+time_t timegm (struct tm *_timeptr);
This prototype should be gated by the appropriate-version GNU extension
#if. (According to the Linux timegm() man page the gate is _BSD_SOURCE ||
_SVID_SOURCE (although the Newlib in-header version might be different).
But given it is called a GNU extension, you'd think _GNU_SOURCE would also
be there.)
Post by Andrew Russell via newlib
time_t time (time_t *_timer);
#ifndef _REENT_ONLY
char *asctime (const struct tm *_tblock);
...
diff --git a/newlib/libc/time/mktime.c b/newlib/libc/time/mktime.c
index 02032599a..ab47f8614 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 shared internal
+ * functions. - 17 July 2018 Andrew Russell.
*/
/*
@@ -39,157 +41,22 @@ result is the time, converted to a <<time_t>> value.
PORTABILITY
ANSI C requires <<mktime>>.
-<<mktime>> requires no supporting OS subroutines.
+<<timegm>> requires no supporting OS subroutines.
Spurious change, as this file is still mktime.
Post by Andrew Russell via newlib
...
diff --git a/newlib/libc/time/timegm.c b/newlib/libc/time/timegm.c
(This diff as presented does not make sense from the point of view that
this is a new file.)
Post by Andrew Russell via newlib
index 02032599a..3299ce228 100644
--- a/newlib/libc/time/timegm.c
+++ b/newlib/libc/time/timegm.c
@@ -1,8 +1,8 @@
/*
- * mktime.c
+ * timegm.c
* Original Author: G. Haley
*
- * Converts the broken-down time, expressed as local time, in the structure
+ * 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
@@ -11,25 +11,27 @@
* represented, returns the value (time_t) -1.
*
* Modifications: Fixed tm_isdst usage - 27 August 2008 Craig Howland.
Can get rid of this fix tm_isdst comment, as not in timegm, but in mktime.
The comments that make the man page here should probably include the same
"These functions are nonstandard GNU extensions that are also present on
the BSDs. Avoid their use; see NOTES." statement that appears in the
timegm/timelocal Linux man page (with a small tweak for being only 1
function instead of two).
+1 Critical to have good documentation at the top.
Post by Craig Howland
...
Post by Andrew Russell via newlib
#include <stdlib.h>
@@ -58,11 +60,8 @@ static const int DAYS_IN_MONTH[12] =
static const int _DAYS_BEFORE_MONTH[12] =
{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
Someone had suggested in the thread that DAYS_IN_MONTH be changed to char
to save space. How about int_least16_t to (possibly) save space?
Post by Andrew Russell via newlib
...
-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;
+}
+
+/* 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)));
How about "(year & 3) == 0" instead of the % to save time? (Should not
matter with a good optimizer, but it is not necessarily on.)
Post by Andrew Russell via newlib
...
+time_t
+timegm (struct tm *tim_p)
+{
+ time_t tim = __timegm_internal(tim_p);
+ long days = tim / SECSPERDAY;
- /* reset isdst flag to what we have calculated */
- tim_p->tm_isdst = isdst;
+ /* set isdst flag to 0 since we are in UTC */
+ tim_p->tm_isdst = 0;
/* 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;
}
+
--
2.18.0.597.ga71716f1ad-goog
I have not been able to try testing it, yet, but there are a few thoughts
based on examination. See inline above as well as some here.
I suggest that the new timegm.c file should contain only the new timegm()
function and that the supporting functions all belong in mktime.c. This
comes closest to preserving the same size on existing implementations,
which otherwise would then require the timegm object to be linked; the new
timegm object will only get linked for applications which call it. (Not
that the timegm() function, itself, is large, but it is the new extension.)
Craig
Freddie Chopin
2018-08-15 19:18:19 UTC
Permalink
Post by Craig Howland
Post by Andrew Russell via newlib
+/* 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)));
How about "(year & 3) == 0" instead of the % to save time? (Should not matter
with a good optimizer, but it is not necessarily on.)
On the other hand % is exactly 1:1 with the intent, while binary
operations are not.

godbolt suggests that for x86-64 "(x % 4) == 0" and "(x & 3) == 0" give
exactly the same code (binary operations) when optimization is
explicitly _disabled_ (-O0).

Also do note that year may be negative, and "& 3" is not the same as "%
4" for negative numbers. In this particular case this probably doesn't
matter anyway, but still (;

Regards,
FCh
Freddie Chopin
2018-08-15 19:27:28 UTC
Permalink
Post by Freddie Chopin
Post by Craig Howland
Post by Andrew Russell via newlib
+/* 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)));
How about "(year & 3) == 0" instead of the % to save time? (Should not matter
with a good optimizer, but it is not necessarily on.)
On the other hand % is exactly 1:1 with the intent, while binary
operations are not.
godbolt suggests that for x86-64 "(x % 4) == 0" and "(x & 3) == 0" give
exactly the same code (binary operations) when optimization is
explicitly _disabled_ (-O0).
Same for arm-none-eabi (previously I could not find the right subpage
where arm-none-eabi) was available.

https://godbolt.org/g/MYFCkb

Regards,
FCh
Richard Earnshaw (lists)
2018-08-16 15:24:52 UTC
Permalink
Post by Freddie Chopin
Post by Craig Howland
Post by Andrew Russell via newlib
+/* 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)));
How about "(year & 3) == 0" instead of the % to save time? (Should not matter
with a good optimizer, but it is not necessarily on.)
On the other hand % is exactly 1:1 with the intent, while binary
operations are not.
godbolt suggests that for x86-64 "(x % 4) == 0" and "(x & 3) == 0" give
exactly the same code (binary operations) when optimization is
explicitly _disabled_ (-O0).
Also do note that year may be negative, and "& 3" is not the same as "%
4" for negative numbers. In this particular case this probably doesn't
matter anyway, but still (;
Regards,
FCh
"year" is signed, so % and & do not generate the same code - you need a
negative value correction. So it all depends on whether you expect
negative years (BC) to be relevant for such calculations!

R.

Loading...