/*==JULIAN.C==========================================================*/ /* */ /* Module : JULIAN */ /* */ /* Project : UTILS */ /* */ /* Description: General date utility for computing Julian days */ /* and a number of nice goodies (weekday, validity */ /* of a date ...) */ /* */ /* Julian calendar (introduced by Julius Caesar): every fourth */ /* year is a leap year. ("Old Style") */ /* Gregorian calendar (introduced by pope Gregor): every fourth */ /* year is a leap year with the exception of every hundredth */ /* year. But every fourhundredth year we have the exception */ /* of the exception. (1700, 1800, 1900 were no leap years, */ /* 2000 will be one). ("New Style") */ /* Julian days (introduced by Scaliger (1629): continuous */ /* count of days since the first of January 4713 BC (Old */ /* Style). */ /* */ /* See Arno Schmidt's "Julianische Tage" (in "Aus julianischen */ /* Tagen", Fischer Taschenbuch 1926, Frankfurt am Main, 1979) */ /* for details about Julian days. */ /* */ /* Eulers Analysis Infinitorum has a nice application of */ /* continued fractions to the Julian/Gregorian question. */ /* The date given as an argument is assumed to be in the */ /* "New Style". */ /* */ /* When Gregor introduced this style in 1582 he dropped 10 days */ /* (after October 4th 1582 followed October 15th). */ /* The catholic cantons of Switzerland made the switch in the */ /* year 1583. The other cantons had the year 1701 start on Jan */ /* 12th. */ /* */ /* In the functions below we assume that the dates BC are */ /* given as negative years (-1 for 1 BC) and that no year 0 */ /* was counted. We don't support dates before 4713 BC. */ /* */ /*--------------------------------------------------------------------*/ /* */ /* (c) Enter AG, Zrich, 1991 */ /* */ /*--------------------------------------------------------------------*/ /* */ /* Author(s) : Hartwig Thomas */ /* */ /* Started : 2'448'595 */ /* */ /* Finished : 2'448'599 */ /* */ /*--------------------------------------------------------------------*/ /* */ /* Modifications: */ /* */ /* AUTHOR | DATE | COMMENT */ /* ----------------|--------------|-------------------------------- */ /* | | */ /* | | */ /* */ /*====================================================================*/ #ifndef __DOS_H #include #endif #ifndef __STRING_H #include #endif #ifndef __STDIO_H #include #endif #ifndef __PROCESS_H #include #endif #ifndef JULIAN_DEF #include "julian.h" #endif #define BASEYEAR 4800 #define JULIAN_CORRECTION 32083.0 #define GREGORIAN_CORRECTION 32045.0 #define SECSPERDAY 86400l #define SECSPERHOUR 3600 #define SECSPERMINUTE 60 /*--------------------------------------------------------------------*/ /* the following stuff is a bit low level and does not really belong */ /* to a date utility but is required for determining whether PC has */ /* a real time clock. */ /*--------------------------------------------------------------------*/ typedef struct { /* see P. Norton: Programmer's Guide p. 64 */ unsigned int conflen; /* length of configuration table } unsigned char model; /* should be identical to modelptr^ */ /* FF: PC */ /* FE: PC XT 1982 */ /* FD: PCjr */ /* FC: 80286 (PC/XT Model 286, PC AT or PS/2 Model 50 or 60) */ /* FB: PC XT 1986 */ /* FA: PS/2 Model 25, Model 30 */ /* F9: PC Convertible */ /* F8: PS/2 Model 80 */ unsigned char submodel; /* submodel */ unsigned char revision; /* ROM BIOS revision level */ unsigned char feature; /* Bit 7: fixed-disk BIOS uses DMA Channel 3 */ /* Bit 6: Cascaded interrupt level 2 (IRQ2) */ /* Bit 5: Real-time clock present */ /* Bit 4: BIOS keyboard intercept implemented */ /* Bit 3: Wait for external event supported */ /* Bit 2: Extended BIOS data area allocated */ /* Bit 1: Micro Channel bus present */ /* Bit 0: Reserved */ } TPCconfiguration; typedef TPCconfiguration far *LPTPCconfiguration; /*--------------------------------------------------------------------*/ /* getpcconf gets the basic configuration of the PC through BIOS */ /* call $15 service $C0 */ /*--------------------------------------------------------------------*/ LPTPCconfiguration getpcconf(void) { union REGS regs; struct SREGS sregs; regs.h.ah = 0xC0; segread(&sregs); int86x(0x15,®s,®s,&sregs); if (regs.x.cflag) return (LPTPCconfiguration)NULL; else return (LPTPCconfiguration)MK_FP(sregs.es,regs.x.bx); } /* getpcconf */ /*====================================================================*/ /* timedatereal reports whether realtimeclock is available on system */ /*====================================================================*/ int timedatereal(void) { LPTPCconfiguration lpPCconf; int available; available = FALSE; lpPCconf = getpcconf(); if (lpPCconf) if (lpPCconf->conflen > 5) available = (lpPCconf->feature & 0x20); return available; } /* timedatereal */ /*====================================================================*/ /* datevalid returns TRUE if conversion to julian days and back is */ /* identity */ /*====================================================================*/ int datevalid(struct date *pdate, bool gregorian) { struct date d; double jdays; jdays = juliandays(pdate,gregorian); juliandate(jdays,&d,gregorian); return ((d.da_day == pdate->da_day) && (d.da_mon == pdate->da_mon) && (d.da_year == pdate -> da_year)); } /* datevalid */ /*====================================================================*/ /* dayinyear returns the day in the year (assumes gregorian date) */ /*====================================================================*/ int dayinyear(struct date *pdate) { struct date d; d.da_day = 31; d.da_mon = 12; d.da_year = pdate->da_year-1; return juliandays(pdate,TRUE) - juliandays(&d,TRUE); } /* dayinyear */ /*====================================================================*/ /* calendarweek returns the calendar week (assumes gregorian date) */ /* we believe that the official definition of the */ /* calendar week count (at least in Switzerland) is */ /* that the week number 1 is the week into which the */ /* new year date falls. Weeks begin on Mondays. */ /*====================================================================*/ int calendarweek(struct date *pdate) { struct date d; int wday; long ldays; d.da_day = 1; d.da_mon = 1; d.da_year = pdate->da_year; ldays = juliandays(&d,TRUE); /* new year */ wday = int (ldays % 7); /* weekday of new year (MON = 0) */ ldays = ldays - wday; /* Monday of the week of 1.1. */ ldays = juliandays(pdate,TRUE) - ldays; /* distance from that Monday to now */ return int (ldays / 7) + 1; } /* calendarweek */ /*====================================================================*/ /* julianweekday determines the weekday (0 = Monday, ..., 6 = Sunday) */ /*====================================================================*/ int julianweekday(double days) { long ldays; ldays = days; return int (ldays % 7); } /* julianweekday */ /*--------------------------------------------------------------------*/ /* the following low level procedures interact with the real time */ /* clock on the system. */ /*--------------------------------------------------------------------*/ void getATdate(struct date *pdate) { union REGS regs; regs.h.ah = 0x04; int86(0x1A,®s,®s); if (!regs.x.cflag) { pdate->da_year = 10*(10*(10*(regs.h.ch / 16) + (regs.h.ch % 16)) + (regs.h.cl / 16)) + (regs.h.cl % 16); pdate->da_mon = 10*(regs.h.dh / 16) + (regs.h.dh % 16); pdate->da_day = 10*(regs.h.dl / 16) + (regs.h.dl % 16); } } /* getATdate */ /*--------------------------------------------------------------------*/ void getATtime(struct time *ptime) { union REGS regs; regs.h.ah = 0x02; int86(0x1A,®s,®s); if (!regs.x.cflag) { ptime->ti_hour = 10*(regs.h.ch / 16) + (regs.h.ch % 16); ptime->ti_min = 10*(regs.h.cl / 16) + (regs.h.cl % 16); ptime->ti_sec = 10*(regs.h.dh / 16) + (regs.h.dh % 16); ptime->ti_hund = 0; } } /* getATtime */ /*---------------------------------------------------------------------*/ void setATdate(struct date *pdate) { int cent, year; union REGS regs; regs.h.ah = 0x05; cent = pdate->da_year / 100; regs.h.ch = 16*(cent / 10) + (cent % 10); year = pdate->da_year % 100; regs.h.cl = 16*(year / 10) + (year % 10); regs.h.dh = 16*(pdate->da_mon / 10) + (pdate->da_mon % 10); regs.h.dl = 16*(pdate->da_day / 10) + (pdate->da_day % 10); int86(0x1A,®s,®s); } /* setATdate */ /*---------------------------------------------------------------------*/ void setATtime(struct time *ptime) { union REGS regs; regs.h.ah = 0x03; regs.h.ch = 16*(ptime->ti_hour / 10) + (ptime->ti_hour % 10); regs.h.cl = 16*(ptime->ti_min / 10) + (ptime->ti_min % 10); regs.h.dh = 16*(ptime->ti_sec / 10) + (ptime->ti_sec % 10); regs.h.dl = 0; int86(0x1A,®s,®s); } /* setATtime */ /*====================================================================*/ /* getjuliantime gets the (real time) time and date in julian days */ /*====================================================================*/ double getjuliantime(void) { struct date d1,d2; struct time t; double days; /* some old BIOS have an error in the date switch if we don't use */ /* the AT-interrupts for getting the time. If we don't have a */ /* a real time clock we take the system time. Maybe the XT has */ /* a clock card ... */ if (timedatereal()) do { getATdate(&d1); getATtime(&t); getATdate(&d2); } while (d1.da_day != d2.da_day); /* make sure we get it right at midnight */ else do { getdate(&d1); gettime(&t); getdate(&d2); } while (d1.da_day != d2.da_day); /* same here */ days = juliandays(&d1,TRUE); /* although SCALIGER counts the Julian period from 1.1.4713 a.C. at noon */ /* we shift the beginning of the day to midnight: */ return days + (t.ti_hour + (t.ti_min + (t.ti_sec + t.ti_hund/100.0)/60.0)/60.0)/24.0; } /* getjuliantime */ /*====================================================================*/ /* setjuliantime sets the (real time) time and date from julian days */ /*====================================================================*/ void setjuliantime(double days) { long l; struct date d; struct time t; /* split off seconds of day */ l = days; l = SECSPERDAY*(days-l); /* convert seconds to time */ t.ti_hour = l / SECSPERHOUR; l = l % SECSPERHOUR; t.ti_min = l / SECSPERMINUTE; t.ti_sec = l % SECSPERMINUTE; t.ti_hund = 0; /* set both real time clock and system clock */ setATtime(&t); settime(&t); /* convert days to date */ juliandate(days,&d,TRUE); /* set real time date and system date */ setATdate(&d); setdate(&d); } /* setjuliantime */ /*====================================================================*/ /* juliantostr converts date according to country specification */ /* (timedate = 0 (USA mm/dd/yy), 1 (Europe dd.mm.yy), */ /* 2 (Japan yy:mm:dd). */ /* The boolean full selects expansion of year to include century. */ /* If ts is not NULL 'hh:mm:ss' is returned as time string ts. */ /*====================================================================*/ void juliantostr(int timedate, double days, char *ds, char *ts, bool gregorian, bool full) { long l; char s[6]; struct date d; /* convert days to date */ juliandate(days,&d,gregorian); /* convert to string according to country code */ if (timedate == DATE_JAPAN) /* japanese: yy:mm:dd */ { sprintf(s,"%d:",d.da_year); if (full) strcpy(ds,s); else strcpy(ds,&s[strlen(s)-2]); sprintf(s,"%2d:",d.da_mon); strcat(ds,s); sprintf(s,"%2d",d.da_day); strcat(ds,s); } else { if (timedate == DATE_USA) { sprintf(ds,"%2d/",d.da_mon); sprintf(s,"%2d/",d.da_day); strcat(ds,s); } else { sprintf(ds,"%2d.",d.da_day); sprintf(s,"%2d.",d.da_mon); strcat(ds,s); } sprintf(s,"%d",d.da_year); if (full) strcat(ds,s); else strcat(ds,&s[strlen(s)-2]); } /* time of day */ if (ts) { /* split off seconds of day */ l = days; l = SECSPERDAY*(days - l); sprintf(ts,"%2d:",l / SECSPERHOUR); l = l % SECSPERHOUR; sprintf(s,"%2d:",l / SECSPERMINUTE); if (s[0] == ' ') s[0] = '0'; strcat(ts,s); sprintf(s,"%2d",l % SECSPERMINUTE); if (s[0] == ' ') s[0] = '0'; strcat(ts,s); } } /* juliantostr */ /*--------------------------------------------------------------------*/ /* getnum skips non-digits and reads a number from *ppCur */ /* it returns the incremented pointer */ /*--------------------------------------------------------------------*/ int getnum(char **ppCur) { int n; /* skip non-digits */ while ((**ppCur) && ((**ppCur < '0') || (**ppCur > '9'))) (*ppCur)++; /* collect number */ n = 0; while ((**ppCur) && (**ppCur >= '0') && (**ppCur <= '9')) { n = 10*n + **ppCur - '0'; (*ppCur)++; } return n; } /* getnum */ /*====================================================================*/ /* strtojulian converts date according to country specification */ /* (timedate = 0 (USA mm/dd/yy), 1 (Europe dd.mm.yy), */ /* 2 (Japan yy:mm:dd) */ /* The boolean full selects expansion of year to include century. */ /* If ts is not NULL then the time is added as a fraction of the day. */ /*====================================================================*/ bool strtojulian(int timedate, char *ds, char *ts, double *pdays, bool gregorian, bool full) { char *pCur; double temp; struct date d1,d2; struct time t; /* parse numbers in ds */ pCur = ds; if (timedate == DATE_JAPAN) d1.da_year = getnum(&pCur); else if (timedate == DATE_EUROPE) d1.da_day = getnum(&pCur); else d1.da_mon = getnum(&pCur); if (timedate == DATE_USA) d1.da_day = getnum(&pCur); else d1.da_mon = getnum(&pCur); if (timedate == DATE_JAPAN) d1.da_day = getnum(&pCur); else d1.da_year = getnum(&pCur); /* if necessary adjust year */ if (!full) { if (d1.da_year == 0) /* no year given ? */ { *pdays = getjuliantime(); /* use current year */ juliandate(*pdays,&d2,gregorian); d1.da_year = d2.da_year; } else if (d1.da_year < 100) /* no century given ? */ { *pdays = getjuliantime(); /* use current century */ juliandate(*pdays,&d2,gregorian); d1.da_year += 100*(int)(d2.da_year/100); } } /* convert to days */ *pdays = juliandays(&d1,gregorian); /* check on correctness */ juliandate(*pdays,&d2,gregorian); if ((d1.da_day == d2.da_day) && (d1.da_mon == d2.da_mon) && (d1.da_year == d2.da_year)) { /* add time, if given */ if (ts) { /* parse numbers in ts */ pCur = ts; t.ti_hour = getnum(&pCur); t.ti_min = getnum(&pCur); t.ti_sec = getnum(&pCur); t.ti_hund = getnum(&pCur); /* calculate as fraction of day */ temp = (t.ti_hour + (t.ti_min + (t.ti_sec + t.ti_hund/100.0)/60.0)/60.0)/24.0; /* check on correctness */ if ((t.ti_hour <= 24) && (t.ti_min < 60) && (t.ti_sec < 60) && (t.ti_hund < 100) && (temp <= 1.0)) { *pdays += temp; return TRUE; } else return FALSE; } else return TRUE; } else return FALSE; } /* strtojulian */ /*--------------------------------------------------------------------*/ /* leapdays calculation returns number of leapdays for the year. */ /* base year must be a multiple of 400 (e.g. -4800) so this */ /* function yields correct results. */ /*--------------------------------------------------------------------*/ int leapdays(int year, bool gregorian) { int leaps; leaps = year / 4; if (gregorian) { leaps -= year / 100; leaps += year / 400; } return leaps; } /* leapdays */ /*====================================================================*/ /* juliandays */ /* converts a date (Julian or Gregorian) into Julian days. */ /*====================================================================*/ double juliandays(struct date *pdate, bool gregorian) { double days; int day,mon,year; /* copy input parameters */ day = pdate->da_day; mon = pdate->da_mon; year = pdate->da_year; /* convert negative years to astronomical years */ if (year < 0) year++; /* we base our calculations on the 1.3.-4800 (astr) */ /* so the leap years are correct in both cases */ year += BASEYEAR; /* for convenience treat jan/feb as months of the previous year */ if (mon > 2) mon -= 3; else { year--; mon += 9; } /* magic formula for the day in the year (from 1.3.) */ day = 30*mon + (3*mon+2) / 5 + day; /* distance from 1.3.-4800 */ days = 365.0*year + leapdays(year,gregorian) + day; if (gregorian) days -= GREGORIAN_CORRECTION; else days -= JULIAN_CORRECTION; if (days < 0) return -1; else return days; } /* juliandays */ /*====================================================================*/ /* juliandate */ /* converts a Julian day into a date. */ /*====================================================================*/ void juliandate(double days, struct date *pdate, bool gregorian) { int corr, leaps, dayinyear; /* convert illegal days to day 0 */ if (days < 0.0) days = 0.0; /* add correction */ if (gregorian) days += GREGORIAN_CORRECTION; else days += JULIAN_CORRECTION; /* get first estimate for year (based on -4800) */ pdate->da_year = days/365.0; /* the rest is first estimate for day in the year */ dayinyear = days - 365.0*pdate->da_year; /* treat the leap years with circumspection */ leaps = leapdays(pdate->da_year,gregorian); while (leaps >= dayinyear) { pdate->da_year--; dayinyear += 365; leaps = leapdays(pdate->da_year,gregorian); } dayinyear -= leaps; /* from corrected day in year get first estimate of month and day */ pdate->da_mon = dayinyear / 30; pdate->da_day = dayinyear % 30; /* calculate correction from magic formula */ corr = (3*pdate->da_mon+2) / 5; /* and apply it */ if (corr >= pdate->da_day) { pdate->da_mon--; pdate->da_day += 30; corr = (3*pdate->da_mon+2) / 5; } pdate->da_day -= corr; /* now fix up the first two months */ if (pdate->da_mon > 9) { pdate->da_mon -= 9; pdate->da_year++; } else pdate->da_mon += 3; /* now adjust year */ pdate->da_year -= BASEYEAR; if (pdate->da_year <= 0) pdate->da_year--; } /* juliandate */ /*--End JULIAN.C------------------------------------------------------*/