summaryrefslogtreecommitdiff
path: root/mktime.c
diff options
context:
space:
mode:
Diffstat (limited to 'mktime.c')
-rw-r--r--mktime.c257
1 files changed, 257 insertions, 0 deletions
diff --git a/mktime.c b/mktime.c
new file mode 100644
index 0000000..0f1edc5
--- /dev/null
+++ b/mktime.c
@@ -0,0 +1,257 @@
+/* free mktime function
+ Copyright 1988, 1989 by David MacKenzie <djm@ai.mit.edu>
+ and Michael Haertel <mike@ai.mit.edu>
+ Unlimited distribution permitted provided this copyright notice is
+ retained and any functional modifications are prominently identified. */
+
+/* Revised 1997 by Christian Spieler:
+ The code was changed to get more conformance with ANSI's (resp. modern
+ UNIX releases) definition for mktime():
+ - Added adjustment for out-of-range values in the fields of struct tm.
+ - Added iterations to get the correct UTC result for input values at
+ the gaps when daylight saving time is switched on or off.
+ - Allow forcing of DST "on" or DST "off" by setting `tm_isdst' field in
+ the tm struct to positive number resp. zero. The `tm_isdst' field must
+ be negative on entrance of mktime() to enable automatic determination
+ if DST is in effect for the requested local time.
+ - Added optional check for overflowing the time_t range. */
+
+/* Note: This version of mktime is ignorant of the tzfile.
+ When the tm structure passed to mktime represents a local time that
+ is valid both as DST time and as standard time (= time value in the
+ gap when switching from DST back to standard time), the behaviour
+ for `tm_isdst < 0' depends on the current timezone: TZ east of GMT
+ assumes winter time, TZ west of GMT assumes summer time.
+ Although mktime() (resp. mkgmtime()) tries to adjust for invalid values
+ of struct tm members, this may fail for input values that are far away
+ from the valid ranges. The adjustment process does not check for overflows
+ or wrap arounds in the struct tm components. */
+
+#ifndef OF
+# ifdef __STDC__
+# define OF(a) a
+# else
+# define OF(a) ()
+# endif
+#endif
+
+#ifndef ZCONST
+# define ZCONST const
+#endif
+
+#include <time.h>
+
+time_t mkgmtime OF((struct tm *));
+time_t mktime OF((struct tm *));
+
+/* Return the equivalent in seconds past 12:00:00 a.m. Jan 1, 1970 GMT
+ of the local time and date in the exploded time structure `tm',
+ adjust out of range fields in `tm' and set `tm->tm_yday', `tm->tm_wday'.
+ If `tm->tm_isdst < 0' was passed to mktime(), the correct setting of
+ tm_isdst is determined and returned. Otherwise, mktime() assumes this
+ field as valid; its information is used when converting local time
+ to UTC.
+ Return -1 if time in `tm' cannot be represented as time_t value. */
+
+time_t
+mktime(tm)
+ struct tm *tm;
+{
+ struct tm *ltm; /* Local time. */
+ time_t loctime; /* The time_t value of local time. */
+ time_t then; /* The time to return. */
+ long tzoffset_adj; /* timezone-adjustment `remainder' */
+ int bailout_cnt; /* counter of tries for tz correction */
+ int save_isdst; /* Copy of the tm->isdst input value */
+
+ save_isdst = tm->tm_isdst;
+ loctime = mkgmtime(tm);
+ if (loctime == -1) {
+ tm->tm_isdst = save_isdst;
+ return (time_t)-1;
+ }
+
+ /* Correct for the timezone and any daylight savings time.
+ The correction is verified and repeated when not correct, to
+ take into account the rare case that a change to or from daylight
+ savings time occurs between when it is the time in `tm' locally
+ and when it is that time in Greenwich. After the second correction,
+ the "timezone & daylight" offset should be correct in all cases. To
+ be sure, we allow a third try, but then the loop is stopped. */
+ bailout_cnt = 3;
+ then = loctime;
+ do {
+ ltm = localtime(&then);
+ if (ltm == (struct tm *)NULL ||
+ (tzoffset_adj = loctime - mkgmtime(ltm)) == 0L)
+ break;
+ then += tzoffset_adj;
+ } while (--bailout_cnt > 0);
+
+ if (ltm == (struct tm *)NULL || tzoffset_adj != 0L) {
+ /* Signal failure if timezone adjustment did not converge. */
+ tm->tm_isdst = save_isdst;
+ return (time_t)-1;
+ }
+
+ if (save_isdst >= 0) {
+ if (ltm->tm_isdst && !save_isdst)
+ {
+ if (then + 3600 < then)
+ then = (time_t)-1;
+ else
+ then += 3600;
+ }
+ else if (!ltm->tm_isdst && save_isdst)
+ {
+ if (then - 3600 > then)
+ then = (time_t)-1;
+ else
+ then -= 3600;
+ }
+ ltm->tm_isdst = save_isdst;
+ }
+
+ if (tm != ltm) /* `tm' may already point to localtime's internal storage */
+ *tm = *ltm;
+
+ return then;
+}
+
+
+#ifndef NO_TIME_T_MAX
+ /* Provide default values for the upper limit of the time_t range.
+ These are the result of the decomposition into a `struct tm' for
+ the time value 0xFFFFFFFEL ( = (time_t)-2 ).
+ Note: `(time_t)-1' is reserved for "invalid time"! */
+# ifndef TM_YEAR_MAX
+# define TM_YEAR_MAX 2106
+# endif
+# ifndef TM_MON_MAX
+# define TM_MON_MAX 1 /* February */
+# endif
+# ifndef TM_MDAY_MAX
+# define TM_MDAY_MAX 7
+# endif
+# ifndef TM_HOUR_MAX
+# define TM_HOUR_MAX 6
+# endif
+# ifndef TM_MIN_MAX
+# define TM_MIN_MAX 28
+# endif
+# ifndef TM_SEC_MAX
+# define TM_SEC_MAX 14
+# endif
+#endif /* NO_TIME_T_MAX */
+
+/* Adjusts out-of-range values for `tm' field `tm_member'. */
+#define ADJUST_TM(tm_member, tm_carry, modulus) \
+ if ((tm_member) < 0) { \
+ tm_carry -= (1 - ((tm_member)+1) / (modulus)); \
+ tm_member = (modulus-1) + (((tm_member)+1) % (modulus)); \
+ } else if ((tm_member) >= (modulus)) { \
+ tm_carry += (tm_member) / (modulus); \
+ tm_member = (tm_member) % (modulus); \
+ }
+
+/* Nonzero if `y' is a leap year, else zero. */
+#define leap(y) (((y) % 4 == 0 && (y) % 100 != 0) || (y) % 400 == 0)
+
+/* Number of leap years from 1970 to `y' (not including `y' itself). */
+#define nleap(y) (((y) - 1969) / 4 - ((y) - 1901) / 100 + ((y) - 1601) / 400)
+
+/* Additional leapday in February of leap years. */
+#define leapday(m, y) ((m) == 1 && leap (y))
+
+/* Length of month `m' (0 .. 11) */
+#define monthlen(m, y) (ydays[(m)+1] - ydays[m] + leapday (m, y))
+
+/* Accumulated number of days from 01-Jan up to start of current month. */
+static ZCONST short ydays[] =
+{
+ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
+};
+
+/* Return the equivalent in seconds past 12:00:00 a.m. Jan 1, 1970 GMT
+ of the Greenwich Mean time and date in the exploded time structure `tm'.
+ This function does always put back normalized values into the `tm' struct,
+ parameter, including the calculated numbers for `tm->tm_yday',
+ `tm->tm_wday', and `tm->tm_isdst'.
+ Returns -1 if the time in the `tm' parameter cannot be represented
+ as valid `time_t' number. */
+
+time_t
+mkgmtime(tm)
+ struct tm *tm;
+{
+ int years, months, days, hours, minutes, seconds;
+
+ years = tm->tm_year + 1900; /* year - 1900 -> year */
+ months = tm->tm_mon; /* 0..11 */
+ days = tm->tm_mday - 1; /* 1..31 -> 0..30 */
+ hours = tm->tm_hour; /* 0..23 */
+ minutes = tm->tm_min; /* 0..59 */
+ seconds = tm->tm_sec; /* 0..61 in ANSI C. */
+
+ ADJUST_TM(seconds, minutes, 60)
+ ADJUST_TM(minutes, hours, 60)
+ ADJUST_TM(hours, days, 24)
+ ADJUST_TM(months, years, 12)
+ if (days < 0)
+ do {
+ if (--months < 0) {
+ --years;
+ months = 11;
+ }
+ days += monthlen(months, years);
+ } while (days < 0);
+ else
+ while (days >= monthlen(months, years)) {
+ days -= monthlen(months, years);
+ if (++months >= 12) {
+ ++years;
+ months = 0;
+ }
+ }
+
+ /* Restore adjusted values in tm structure */
+ tm->tm_year = years - 1900;
+ tm->tm_mon = months;
+ tm->tm_mday = days + 1;
+ tm->tm_hour = hours;
+ tm->tm_min = minutes;
+ tm->tm_sec = seconds;
+
+ /* Set `days' to the number of days into the year. */
+ days += ydays[months] + (months > 1 && leap (years));
+ tm->tm_yday = days;
+
+ /* Now calculate `days' to the number of days since Jan 1, 1970. */
+ days = (unsigned)days + 365 * (unsigned)(years - 1970) +
+ (unsigned)(nleap (years));
+ tm->tm_wday = ((unsigned)days + 4) % 7; /* Jan 1, 1970 was Thursday. */
+ tm->tm_isdst = 0;
+
+ if (years < 1970)
+ return (time_t)-1;
+
+#if (defined(TM_YEAR_MAX) && defined(TM_MON_MAX) && defined(TM_MDAY_MAX))
+#if (defined(TM_HOUR_MAX) && defined(TM_MIN_MAX) && defined(TM_SEC_MAX))
+ if (years > TM_YEAR_MAX ||
+ (years == TM_YEAR_MAX &&
+ (tm->tm_yday > ydays[TM_MON_MAX] + (TM_MDAY_MAX - 1) +
+ (TM_MON_MAX > 1 && leap (TM_YEAR_MAX)) ||
+ (tm->tm_yday == ydays[TM_MON_MAX] + (TM_MDAY_MAX - 1) +
+ (TM_MON_MAX > 1 && leap (TM_YEAR_MAX)) &&
+ (hours > TM_HOUR_MAX ||
+ (hours == TM_HOUR_MAX &&
+ (minutes > TM_MIN_MAX ||
+ (minutes == TM_MIN_MAX && seconds > TM_SEC_MAX) )))))))
+ return (time_t)-1;
+#endif
+#endif
+
+ return (time_t)(86400L * (unsigned long)(unsigned)days +
+ 3600L * (unsigned long)hours +
+ (unsigned long)(60 * minutes + seconds));
+}