#if has_conf_h
# include <conf.h>
#else
# if HAVE_CONFIG_H
# include <config.h>
# else
# ifndef __STDC__
# define const
# endif
# endif
# include <sys/types.h>
# if HAVE_LIMITS_H
# include <limits.h>
# endif
# ifndef LONG_MIN
# define LONG_MIN (-1-2147483647L)
# endif
# if STDC_HEADERS
# include <stdlib.h>
# endif
# include <time.h>
# ifdef __STDC__
# define P(x) x
# else
# define P(x) ()
# endif
#endif
#include <partime.h>
#include <maketime.h>
char const maket_id[] =
"$Id: maketime.c,v 1.1.1.2 2000/05/06 22:44:53 wsanchez Exp $";
static int isleap P ((int));
static int month_days P ((struct tm const *));
static time_t maketime P ((struct partime const *, time_t));
#define overflow_sum_sign(a, b, sum) ((~((a) ^ (b)) & ((a) ^ (sum))) < 0)
#define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
#define MOD(a, b) ((a) % (b) + (b) * ((a) % (b) < 0))
#define Y400_DAYS (365 * 400L + 100 - 4 + 1)
#define TM_YEAR_ORIGIN 1900
static int
isleap (y)
int y;
{
return (y & 3) == 0 && (y % 100 != 0 || y % 400 == 0);
}
static int const month_yday[] =
{
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
};
static int
month_days (tm)
struct tm const *tm;
{
int m = tm->tm_mon;
return (month_yday[m + 1] - month_yday[m]
+ (m == 1 && isleap (tm->tm_year + TM_YEAR_ORIGIN)));
}
struct tm *
time2tm (unixtime, localzone)
time_t unixtime;
int localzone;
{
struct tm *tm;
#ifdef TZ_is_unset
static char const *TZ;
if (!TZ && !(TZ = getenv ("TZ")))
TZ_is_unset ("The TZ environment variable is not set; please set it to your timezone");
#endif
if (localzone || !(tm = gmtime (&unixtime)))
tm = localtime (&unixtime);
return tm;
}
time_t
difftm (a, b)
struct tm const *a;
struct tm const *b;
{
int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
int ac = DIV (ay, 100);
int bc = DIV (by, 100);
int difference_in_day_of_year = a->tm_yday - b->tm_yday;
int intervening_leap_days = (((ay >> 2) - (by >> 2))
- (ac - bc)
+ ((ac >> 2) - (bc >> 2)));
time_t difference_in_years = ay - by;
time_t difference_in_days
= (difference_in_years * 365
+ (intervening_leap_days + difference_in_day_of_year));
return (((((difference_in_days * 24
+ (a->tm_hour - b->tm_hour))
* 60)
+ (a->tm_min - b->tm_min))
* 60)
+ (a->tm_sec - b->tm_sec));
}
void
adjzone (t, seconds)
register struct tm *t;
long seconds;
{
int days = 0;
int leap_second = t->tm_sec == 60;
long sec = seconds + (t->tm_sec - leap_second);
if (sec < 0)
{
if ((t->tm_min -= (59 - sec) / 60) < 0
&& (t->tm_hour -= (59 - t->tm_min) / 60) < 0)
{
days = - ((23 - t->tm_hour) / 24);
if ((t->tm_mday += days) <= 0)
{
if (--t->tm_mon < 0)
{
--t->tm_year;
t->tm_mon = 11;
}
t->tm_mday += month_days (t);
}
}
}
else
{
if (60 <= (t->tm_min += sec / 60)
&& (24 <= (t->tm_hour += t->tm_min / 60)))
{
days = t->tm_hour / 24;
if (month_days (t) < (t->tm_mday += days))
{
if (11 < ++t->tm_mon)
{
++t->tm_year;
t->tm_mon = 0;
}
t->tm_mday = 1;
}
}
}
if (TM_DEFINED (t->tm_wday))
t->tm_wday = MOD (t->tm_wday + days, 7);
t->tm_hour = MOD (t->tm_hour, 24);
t->tm_min = MOD (t->tm_min, 60);
t->tm_sec = (int) MOD (sec, 60) + leap_second;
}
time_t
tm2time (tm, localzone)
struct tm *tm;
int localzone;
{
static time_t t_cache[2];
static struct tm tm_cache[2];
time_t d, gt;
struct tm const *gtm;
int remaining_tries = 8;
if (12 <= (unsigned) tm->tm_mon)
return -1;
tm->tm_yday = month_yday[tm->tm_mon] + tm->tm_mday
- (tm->tm_mon < 2 || !isleap (tm->tm_year + TM_YEAR_ORIGIN));
gt = t_cache[localzone];
gtm = gt ? &tm_cache[localzone] : time2tm (gt, localzone);
while ((d = difftm (tm, gtm)) != 0)
{
if (--remaining_tries == 0)
return -1;
gt += d;
gtm = time2tm (gt, localzone);
}
#define TM_DIFFER(a,b) \
( \
((a)->tm_year ^ (b)->tm_year) | \
((a)->tm_mon ^ (b)->tm_mon) | \
((a)->tm_mday ^ (b)->tm_mday) | \
((a)->tm_hour ^ (b)->tm_hour) | \
((a)->tm_min ^ (b)->tm_min) | \
((a)->tm_sec ^ (b)->tm_sec) \
)
if (TM_DIFFER (tm, gtm))
{
int yd = tm->tm_year - gtm->tm_year;
gt += yd + (yd ? 0 : tm->tm_mon - gtm->tm_mon);
gtm = time2tm (gt, localzone);
if (TM_DIFFER (tm, gtm))
return -1;
}
t_cache[localzone] = gt;
tm_cache[localzone] = *gtm;
tm->tm_wday = gtm->tm_wday;
return gt;
}
static time_t
maketime (pt, default_time)
struct partime const *pt;
time_t default_time;
{
int localzone, wday, year;
struct tm tm;
struct tm *tm0 = 0;
time_t r;
int use_ordinal_day;
tm0 = 0;
localzone = pt->zone == TM_LOCAL_ZONE;
tm = pt->tm;
year = tm.tm_year;
wday = tm.tm_wday;
use_ordinal_day = (!TM_DEFINED (tm.tm_mday)
&& TM_DEFINED (wday) && TM_DEFINED (pt->wday_ordinal));
if (use_ordinal_day || TM_DEFINED (pt->ymodulus) || !TM_DEFINED (year))
{
tm0 = time2tm (default_time, localzone);
if (!localzone)
adjzone (tm0, pt->zone);
}
if (use_ordinal_day)
tm.tm_mday = (tm0->tm_mday
+ ((wday - tm0->tm_wday + 7) % 7
+ 7 * (pt->wday_ordinal - (pt->wday_ordinal != 0))));
if (TM_DEFINED (pt->ymodulus))
{
int year0 = tm0->tm_year + TM_YEAR_ORIGIN;
int y0 = MOD (year0, pt->ymodulus);
int d = 2 * (year - y0);
year += (((year0 - y0) / pt->ymodulus
+ (pt->ymodulus < d ? -1 : d < -pt->ymodulus))
* pt->ymodulus);
}
else if (!TM_DEFINED (year))
{
year = tm0->tm_year + TM_YEAR_ORIGIN;
if (!TM_DEFINED (tm.tm_mon))
{
tm.tm_mon = tm0->tm_mon;
if (!TM_DEFINED (tm.tm_mday))
tm.tm_mday = tm0->tm_mday;
}
}
if (!TM_DEFINED (tm.tm_mon))
tm.tm_mon = 0;
if (!TM_DEFINED (tm.tm_mday))
tm.tm_mday = 1;
if (!TM_DEFINED (tm.tm_hour))
tm.tm_hour = 0;
if (!TM_DEFINED (tm.tm_min))
tm.tm_min = 0;
if (!TM_DEFINED (tm.tm_sec))
tm.tm_sec = 0;
tm.tm_year = year - TM_YEAR_ORIGIN;
if ((year < tm.tm_year) != (TM_YEAR_ORIGIN < 0))
return -1;
if (!localzone)
{
adjzone (&tm, -pt->zone);
wday = tm.tm_wday;
}
r = tm2time (&tm, localzone);
if (r == -1)
return r;
if (TM_DEFINED (wday) && wday != tm.tm_wday)
return -1;
if (pt->tmr.tm_year | pt->tmr.tm_mon | pt->tmr.tm_mday
| pt->tmr.tm_hour | pt->tmr.tm_min)
{
int years = tm.tm_year + pt->tmr.tm_year;
int mons = tm.tm_mon + pt->tmr.tm_mon;
int mdays = tm.tm_mday + pt->tmr.tm_mday;
int hours = tm.tm_hour + pt->tmr.tm_hour;
int mins = tm.tm_min + pt->tmr.tm_min;
int carried_hours = DIV (mins, 60);
int hours1 = hours + carried_hours;
int carried_days = DIV (hours1, 24);
int mdays1 = mdays + carried_days;
int mon0 = MOD (mons, 12);
int carried_years0 = DIV (mons, 12);
int year0 = years + carried_years0;
int yday0 = (month_yday[mon0]
- (mon0 < 2 || !isleap (year0 + TM_YEAR_ORIGIN)));
int yday1 = yday0 + mdays1;
int carried_years1 = DIV (yday1, Y400_DAYS) * 400;
int year1 = year0 + carried_years1;
int yday2 = MOD (yday1, Y400_DAYS);
int leap;
if (overflow_sum_sign (tm.tm_year, pt->tmr.tm_year, years)
| overflow_sum_sign (tm.tm_mon, pt->tmr.tm_mon, mons)
| overflow_sum_sign (tm.tm_mday, pt->tmr.tm_mday, mdays)
| overflow_sum_sign (tm.tm_hour, pt->tmr.tm_hour, hours)
| overflow_sum_sign (tm.tm_min, pt->tmr.tm_min, mins)
| overflow_sum_sign (hours, carried_hours, hours1)
| overflow_sum_sign (mdays, carried_days, mdays1)
| overflow_sum_sign (years, carried_years0, year0)
| overflow_sum_sign (yday0, mdays1, yday1)
| overflow_sum_sign (year0, carried_years1, year1))
return -1;
for (;;)
{
int days_per_year = 365 + (leap = isleap (year1 + TM_YEAR_ORIGIN));
if (yday2 < days_per_year)
break;
yday2 -= days_per_year;
year1++;
}
tm.tm_year = year1;
{
int mon;
for (mon = 11;
(tm.tm_mday = (yday2 - month_yday[mon] + (mon < 2 || !leap))) <= 0;
mon--)
continue;
tm.tm_mon = mon;
}
tm.tm_hour = MOD (hours1, 24);
tm.tm_min = MOD (mins, 60);
r = tm2time (&tm, localzone);
if (r == -1)
return r;
}
{
time_t rs = r + pt->tmr.tm_sec;
if ((pt->tmr.tm_sec < 0) != (rs < r))
return -1;
return rs;
}
}
time_t
str2time (source, default_time, default_zone)
char const **source;
time_t default_time;
long default_zone;
{
struct partime pt;
*source = partime (*source, &pt);
if (pt.zone == TM_UNDEFINED_ZONE)
pt.zone = default_zone;
return maketime (&pt, default_time);
}
#ifdef TEST
#include <stdio.h>
int
main (argc, argv)
int argc;
char **argv;
{
time_t default_time = time ((time_t *) 0);
long default_zone = argv[1] ? atol (argv[1]) : TM_LOCAL_ZONE;
char buf[1000];
while (fgets (buf, sizeof (buf), stdin))
{
char const *p = buf;
time_t t = str2time (&p, default_time, default_zone);
printf ("`%.*s' -> %s",
(int) (p - buf - (p[0] == '\0' && p[-1] == '\n')), buf,
asctime ((argv[1] ? gmtime : localtime) (&t)));
}
return 0;
}
#endif