diff options
Diffstat (limited to 'amiga/time_lib.c')
-rw-r--r-- | amiga/time_lib.c | 541 |
1 files changed, 541 insertions, 0 deletions
diff --git a/amiga/time_lib.c b/amiga/time_lib.c new file mode 100644 index 0000000..2804c05 --- /dev/null +++ b/amiga/time_lib.c @@ -0,0 +1,541 @@ +#define __amiga_time_lib_c + +/* ----------------------------------------------------------------------------- +This source is copyrighted by Norbert Pueschel <pueschel@imsdd.meb.uni-bonn.de> +From 'clockdaemon.readme': +(available from Aminet, main site is ftp.wustl.edu:/pub/aminet/ under + util/time/clockdaemon.lha) +"The original SAS/C functions gmtime, localtime, mktime and time do not +work correctly. The supplied link library time.lib contains replacement +functions for them." +The time.lib library consists of three parts (time.c, timezone.c and version.c), +all included here. [time.lib 1.2 (1997-04-02)] +Permission is granted to the Info-ZIP group to redistribute the time.lib source. +The use of time.lib functions in own, noncommercial programs is permitted. +It is only required to add the timezone.doc to such a distribution. +Using the time.lib library in commercial software (including Shareware) is only +permitted after prior consultation of the author. +------------------------------------------------------------------------------*/ +/* History */ +/* 30 Mar 1997, Haidinger Walter, added AVAIL_GETVAR macro to support OS <V36 */ +/* 24 May 1997, Haidinger Walter, added NO_MKTIME macro to allow use of Zip's */ +/* mktime.c. NO_MKTIME must be defined in the makefile, though. */ +/* 25 May 1997, Haidinger Walter, moved set_TZ() here from filedate.c */ +/* 20 Jul 1997, Paul Kienitz, adapted for Aztec C, added mkgmtime(), */ +/* debugged, and made New York settings default, as is common. */ +/* 30 Sep 1997, Paul Kienitz, restored real_timezone_is_set flag */ +/* 19 Oct 1997, Paul Kienitz, corrected 16 bit multiply overflow bug */ +/* 21 Oct 1997, Chr. Spieler, shortened long lines, removed more 16 bit stuff */ +/* (n.b. __stdoffset and __dstoffset now have to be long ints) */ +/* 25 Oct 1997, Paul Kienitz, cleanup, make tzset() not redo work needlessly */ +/* 29 Oct 1997, Chr. Spieler, initialized globals _TZ, real_timezone_is_set */ +/* 31 Dec 1997, Haidinger Walter, created z-time.h to overcome sas/c header */ +/* dependencies. TZ_ENVVAR macro added. Happy New Year! */ +/* 25 Apr 1998, Chr. Spieler, __timezone must always contain __stdoffset */ +/* 28 Apr 1998, Chr. Spieler, P. Kienitz, changed __daylight to standard usage */ + +#ifdef __SASC +# include <proto/dos.h> +# include <proto/locale.h> +# include <proto/exec.h> + /* this setenv() is in amiga/filedate.c */ + extern int setenv(const char *var, const char *value, int overwrite); +#else +# include <clib/dos_protos.h> +# include <clib/locale_protos.h> +# include <clib/exec_protos.h> +# include <pragmas/exec_lib.h> +# include <pragmas/dos_lib.h> +# include <pragmas/locale_lib.h> +/* Info-ZIP accesses these by their standard names: */ +# define __timezone timezone +# define __daylight daylight +# define __tzset tzset +#endif +#define NO_TIME_H +#include "amiga/z-time.h" +#include <exec/execbase.h> +#include <clib/alib_stdio_protos.h> +#include <string.h> +#include <stdlib.h> + +extern struct ExecBase *SysBase; +extern char *getenv(const char *var); + +typedef unsigned long time_t; +struct tm { + int tm_sec; /* seconds after the minute */ + int tm_min; /* minutes after the hour */ + int tm_hour; /* hours since midnight */ + int tm_mday; /* day of the month */ + int tm_mon; /* months since January */ + int tm_year; /* years since 1900 */ + int tm_wday; /* days since Sunday */ + int tm_yday; /* days since January 1 */ + int tm_isdst; /* Daylight Savings Time flag */ +}; +struct dstdate { + enum { JULIAN0, JULIAN, MWD } dd_type; + int dd_day; + int dd_week; + int dd_month; + int dd_secs; +}; +static struct dstdate __dststart; +static struct dstdate __dstend; + +#define isleapyear(y) (((y)%4==0&&(!((y)%100==0)||((y)%400==0)))?1:0) +#define yearlen(y) (isleapyear(y)?366:365) +#define weekday(d) (((d)+4)%7) +#define jan1ofyear(y) (((y)-70)*365+((y)-69)/4-((y)-1)/100+((y)+299)/400) +#define wdayofyear(y) weekday(jan1ofyear(y)) +#define AMIGA2UNIX 252460800 /* seconds between 1.1.1970 and 1.1.1978 */ +#define CHECK 300 /* min. time between checks of IXGMTOFFSET */ +#define GETVAR_REQVERS 36L /* required OS version for GetVar() */ +#define AVAIL_GETVAR (SysBase->LibNode.lib_Version >= GETVAR_REQVERS) +#ifndef TZ_ENVVAR +# define TZ_ENVVAR "TZ" /* environment variable to parse */ +#endif + +#ifdef __SASC + extern int __daylight; + extern long __timezone; + extern char *__tzname[2]; + extern char *_TZ; +#else + int __daylight; + long __timezone; + char *__tzname[2]; + char *_TZ = NULL; +#endif +int real_timezone_is_set = FALSE; /* globally visible TZ_is_valid signal */ +char __tzstn[MAXTIMEZONELEN]; +char __tzdtn[MAXTIMEZONELEN]; +/* the following 4 variables are only used internally; make them static ? */ +int __isdst; +time_t __nextdstchange; +long __stdoffset; +long __dstoffset; +#define TZLEN 64 +static char TZ[TZLEN]; +static struct tm TM; +static const unsigned short days[2][13] = { + { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, + { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } +}; +#ifndef NO_MKTIME /* only used by mktime() */ +static const unsigned short monlen[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 } +}; +#endif + +/* internal prototypes */ +static time_t dst2time(int year,struct dstdate *dst); +static void time2tm(time_t time); +static int checkdst(time_t time); +#ifndef NO_MKTIME +static void normalize(int *i,int *j,int norm); +#endif +static long gettime(char **s); +static void getdstdate(char **s,struct dstdate *dst); +#ifdef __SASC +void set_TZ(long time_zone, int day_light); +#endif + +/* prototypes for sc.lib replacement functions */ +struct tm *gmtime(const time_t *t); +struct tm *localtime(const time_t *t); +#ifndef NO_MKTIME +time_t mkgmtime(struct tm *tm); +time_t mktime(struct tm *tm); +#endif +time_t time(time_t *tm); +void __tzset(void); + + +static time_t dst2time(int year,struct dstdate *dst) +{ + int isleapyear,week,mon,mday; + mon = 0; + mday = dst->dd_day; + isleapyear = isleapyear(year); + switch(dst->dd_type) { + case JULIAN: + if(!isleapyear || dst->dd_day <= 59) break; + default: + mday++; + break; + case MWD: + mon = dst->dd_month-1; + week = dst->dd_week; + if(week == 5) { + mon++; + week = 0; + } + mday = dst->dd_day - weekday(jan1ofyear(year)+days[isleapyear][mon]); + if(mday < 0) mday += 7; + mday += (week - 1) * 7 + 1; + break; + } + return((time_t)(jan1ofyear(year)+days[isleapyear][mon]+mday-1)*(time_t)86400L+ + (time_t)dst->dd_secs); +} + +static void time2tm(time_t time) +{ + int isleapyear; + TM.tm_sec = time % 60; + time /= 60; + TM.tm_min = time % 60; + time /= 60; + TM.tm_hour = time % 24; + time /= 24; + TM.tm_year = time/365 + 70; /* guess year */ + while((TM.tm_yday = time - jan1ofyear(TM.tm_year)) < 0) TM.tm_year--; + isleapyear = isleapyear(TM.tm_year); + for(TM.tm_mon = 0; + TM.tm_yday >= days[isleapyear][TM.tm_mon+1]; + TM.tm_mon++); + TM.tm_mday = TM.tm_yday - days[isleapyear][TM.tm_mon] + 1; + TM.tm_wday = (time+4)%7; +} + +static int checkdst(time_t time) +{ + int year,day; + time_t t,u; + day = time / 86400L; + year = day / 365 + 70; /* guess year */ + while(day - jan1ofyear(year) < 0) year--; + t = dst2time(year,&__dststart) + __stdoffset; + u = dst2time(year,&__dstend) + __dstoffset; + if(u > t) { + return((time >= t && time < u)?1:0); + } + else { + return((time < u || time >= t)?1:0); + } +} + +struct tm *gmtime(const time_t *t) +{ + TM.tm_isdst = 0; + time2tm(*t); + return(&TM); +} + +struct tm *localtime(const time_t *t) +{ + if(!_TZ) __tzset(); + TM.tm_isdst = checkdst(*t); + time2tm(*t - (TM.tm_isdst ? __dstoffset : __stdoffset)); + return(&TM); +} + +#ifndef NO_MKTIME /* normalize() only used by mktime() */ +static void normalize(int *i,int *j,int norm) +{ + while(*i < 0) { + *i += norm; + (*j)--; + } + while(*i >= norm) { + *i -= norm; + (*j)++; + } +} + +time_t mkgmtime(struct tm *tm) +{ + time_t t; + normalize(&tm->tm_sec,&tm->tm_min,60); + normalize(&tm->tm_min,&tm->tm_hour,60); + normalize(&tm->tm_hour,&tm->tm_mday,24); + normalize(&tm->tm_mon,&tm->tm_year,12); + while(tm->tm_mday > monlen[isleapyear(tm->tm_year)][tm->tm_mon]) { + tm->tm_mday -= monlen[isleapyear(tm->tm_year)][tm->tm_mon]; + tm->tm_mon++; + if(tm->tm_mon == 12) { + tm->tm_mon = 0; + tm->tm_year++; + } + } + while(tm->tm_mday < 0) { + tm->tm_mon--; + if(tm->tm_mon == -1) { + tm->tm_mon = 11; + tm->tm_year--; + } + tm->tm_mday += monlen[isleapyear(tm->tm_year)][tm->tm_mon]; + } + tm->tm_yday = tm->tm_mday + days[isleapyear(tm->tm_year)][tm->tm_mon] - 1; + t = jan1ofyear(tm->tm_year) + tm->tm_yday; + tm->tm_wday = weekday(t); + if(tm->tm_year < 70) return((time_t)0); + t = t * 86400L + tm->tm_hour * 3600L + tm->tm_min * 60L + (time_t)tm->tm_sec; + return(t); +} + +time_t mktime(struct tm *tm) +{ + time_t t; + if(!_TZ) __tzset(); + t = mkgmtime(tm); + if(tm->tm_isdst < 0) tm->tm_isdst = checkdst(t); + t += tm->tm_isdst ? __dstoffset : __stdoffset; + return(t); +} +#endif /* !NO_MKTIME */ + +static long gettime(char **s) +{ + long num,time; + for(num = 0;**s >= '0' && **s <= '9';(*s)++) { + num = 10*num + (**s - '0'); + } + time = 3600L * num; + if(**s == ':') { + (*s)++; + for(num = 0;**s >= '0' && **s <= '9';(*s)++) { + num = 10*num + (**s - '0'); + } + time += 60 * num; + if(**s == ':') { + (*s)++; + for(num = 0;**s >= '0' && **s <= '9';(*s)++) { + num = 10*num + (**s - '0'); + } + time += num; + } + } + return(time); +} + +static void getdstdate(char **s,struct dstdate *dst) +{ + switch(**s) { + case 'J': + case 'j': + (*s)++; + dst->dd_type = JULIAN; + for(dst->dd_day = 0;**s >= '0' && **s <= '9';(*s)++) { + dst->dd_day = 10*dst->dd_day + (**s - '0'); + } + break; + case 'M': + case 'm': + (*s)++; + dst->dd_type = MWD; + for(dst->dd_month = 0;**s >= '0' && **s <= '9';(*s)++) { + dst->dd_month = 10*dst->dd_month + (**s - '0'); + } + if(**s != '.') return; + (*s)++; + for(dst->dd_week = 0;**s >= '0' && **s <= '9';(*s)++) { + dst->dd_week = 10*dst->dd_week + (**s - '0'); + } + if(**s != '.') return; + (*s)++; + for(dst->dd_day = 0;**s >= '0' && **s <= '9';(*s)++) { + dst->dd_day = 10*dst->dd_day + (**s - '0'); + } + break; + default: + dst->dd_type = JULIAN0; + for(dst->dd_day = 0;**s >= '0' && **s <= '9';(*s)++) { + dst->dd_day = 10*dst->dd_day + (**s - '0'); + } + break; + } + if(**s == '/') { + (*s)++; + dst->dd_secs = gettime(s); + } +} + +void __tzset(void) +{ + char *s,*t; + int minus = 0; + time_t tm; + struct Library *LocaleBase; + struct Locale *loc = NULL; + if (real_timezone_is_set) + return; + real_timezone_is_set = TRUE; + __dststart.dd_secs = __dstend.dd_secs = 7200; + __dststart.dd_type = __dstend.dd_type = MWD; + __dststart.dd_month = 4; + __dststart.dd_week = 1; + __dstend.dd_month = 10; + __dstend.dd_week = 5; + __dststart.dd_day = __dstend.dd_day = 0; /* sunday */ + _TZ = NULL; + if (AVAIL_GETVAR) { /* GetVar() available? */ + if(GetVar(TZ_ENVVAR,TZ,TZLEN,GVF_GLOBAL_ONLY) > 0) + _TZ = TZ; + } else + _TZ = getenv(TZ_ENVVAR); + if (_TZ == NULL || !_TZ[0]) { + static char gmt[MAXTIMEZONELEN] = DEFAULT_TZ_STR; + LocaleBase = OpenLibrary("locale.library",0); + if(LocaleBase) { + loc = OpenLocale(0); /* cannot return null */ + if (loc->loc_GMTOffset == -300 || loc->loc_GMTOffset == 300) { + BPTR eh; + if (eh = Lock("ENV:sys/locale.prefs", ACCESS_READ)) + UnLock(eh); + else { + real_timezone_is_set = FALSE; + loc->loc_GMTOffset = 300; /* Amigados bug: default when locale is */ + } /* not initialized can have wrong sign */ + } + sprintf(gmt, "GMT%ld:%02ld", loc->loc_GMTOffset / 60L, + labs(loc->loc_GMTOffset) % 60L); + CloseLocale(loc); + CloseLibrary(LocaleBase); + } else + real_timezone_is_set = FALSE; + _TZ = gmt; + } + for(s = _TZ,t = __tzstn;*s && *s != '+' && *s != '-' && *s != ',' && + (*s < '0' || *s > '9');s++) { + if(t-__tzstn < MAXTIMEZONELEN-1) *(t++) = *s; + } + *t = '\0'; + if(*s == '+') { + s++; + } + else { + if(*s == '-') { + minus = 1; + s++; + } + } + __stdoffset = gettime(&s); + if(minus) { + __stdoffset *= -1; + } + if(*s) { + minus = 0; + for(t = __tzdtn;*s && *s != '+' && *s != '-' && *s != ',' && + (*s < '0' || *s > '9');s++) { + if(t-__tzdtn < MAXTIMEZONELEN-1) *(t++) = *s; + } + *t = '\0'; + if(*s == '+') { + s++; + } + else { + if(*s == '-') { + minus = 1; + s++; + } + } + if(*s && *s != ',') { + __dstoffset = gettime(&s); + if(minus) { + __dstoffset *= -1; + } + } + else { + __dstoffset = __stdoffset - 3600L; + } + if(*s == ',') { + s++; + getdstdate(&s,&__dststart); + if(*s == ',') { + s++; + getdstdate(&s,&__dstend); + } + } + } + else { + __dstoffset = __stdoffset; + } + time2tm(time(&tm)); + __isdst = checkdst(tm); + __daylight = (__dstoffset != __stdoffset); + __timezone = __stdoffset; + __nextdstchange = dst2time(TM.tm_year, __isdst ? &__dstend : &__dststart); + if(tm >= __nextdstchange) { + __nextdstchange = dst2time(TM.tm_year+1, + __isdst ? &__dstend : &__dststart); + } + __tzname[0] = __tzstn; + __tzname[1] = __tzdtn; +#ifdef __SASC + if (loc) /* store TZ envvar if data read from locale */ + set_TZ(__timezone, __daylight); +#endif +} + +time_t time(time_t *tm) +{ + static time_t last_check = 0; + static struct _ixgmtoffset { + LONG Offset; + UBYTE DST; + UBYTE Null; + } ixgmtoffset; + static char *envvarstr; /* ptr to environm. string (used if !AVAIL_GETVAR) */ + struct DateStamp ds; + time_t now; + DateStamp(&ds); + now = ds.ds_Days * 86400L + ds.ds_Minute * 60L + + ds.ds_Tick / TICKS_PER_SECOND; + if(now - last_check > CHECK) { + last_check = now; + if (AVAIL_GETVAR) /* GetVar() available? */ + if(GetVar("IXGMTOFFSET",(STRPTR)&ixgmtoffset,6, + GVF_BINARY_VAR|GVF_GLOBAL_ONLY) == -1) { + __tzset(); + ixgmtoffset.Offset = __isdst ? __dstoffset : __stdoffset; + } + else + if (envvarstr=getenv("IXGMTOFFSET")) { + ixgmtoffset = *((struct _ixgmtoffset *)envvarstr); /* copy to struct */ + __tzset(); + ixgmtoffset.Offset = __isdst ? __dstoffset : __stdoffset; + } + } + now += AMIGA2UNIX; + now += ixgmtoffset.Offset; + if(tm) *tm = now; + return(now); +} + +#ifdef __SASC + +/* Stores data from timezone and daylight to ENV:TZ. */ +/* ENV:TZ is required to exist by some other SAS/C library functions, */ +/* like stat() or fstat(). */ +void set_TZ(long time_zone, int day_light) +{ + char put_tz[MAXTIMEZONELEN]; /* string for putenv: "TZ=aaabbb:bb:bbccc" */ + int offset; + void *exists; /* dummy ptr to see if global envvar TZ already exists */ + if (AVAIL_GETVAR) + exists = (void *)FindVar(TZ_ENVVAR,GVF_GLOBAL_ONLY); /* OS V36+ */ + else + exists = (void *)getenv(TZ_ENVVAR); + /* see if there is already an envvar TZ_ENVVAR. If not, create it */ + if (exists == NULL) { + /* create TZ string by pieces: */ + sprintf(put_tz, "GMT%+ld", time_zone / 3600L); + if (time_zone % 3600L) { + offset = (int) labs(time_zone % 3600L); + sprintf(put_tz + strlen(put_tz), ":%02d", offset / 60); + if (offset % 60) + sprintf(put_tz + strlen(put_tz), ":%02d", offset % 60); + } + if (day_light) + strcat(put_tz,"DST"); + if (AVAIL_GETVAR) /* store TZ to ENV:TZ. */ + SetVar(TZ_ENVVAR,put_tz,-1,GVF_GLOBAL_ONLY); /* OS V36+ */ + else + setenv(TZ_ENVVAR,put_tz, 1); + } +} +#endif /* __SASC */ |