home *** CD-ROM | disk | FTP | other *** search
- /* Date.c -- implementation of Gregorian calendar dates
-
- THIS SOFTWARE FITS THE DESCRIPTION IN THE U.S. COPYRIGHT ACT OF A
- "UNITED STATES GOVERNMENT WORK". IT WAS WRITTEN AS A PART OF THE
- AUTHOR'S OFFICIAL DUTIES AS A GOVERNMENT EMPLOYEE. THIS MEANS IT
- CANNOT BE COPYRIGHTED. THIS SOFTWARE IS FREELY AVAILABLE TO THE
- PUBLIC FOR USE WITHOUT A COPYRIGHT NOTICE, AND THERE ARE NO
- RESTRICTIONS ON ITS USE, NOW OR SUBSEQUENTLY.
-
- Author:
- Edward M. Persky
- Bg. 12A, Rm. 2031
- Computer Systems Laboratory
- Division of Computer Research and Technology
- National Institutes of Health
- Bethesda, Maryland 20892
- Phone: (301) 496-2963
- uucp: uunet!nih-csl!tpersky
- Internet: tpersky@alw.nih.gov
-
- Function:
- Provides an object that contains a date, stored as a Julian Day Number.
- Note: Julian Day Number for Jan. 29, 1988 is not 88029; it is different.
-
- $Log: Date.c,v $
- * Revision 3.0 90/05/20 00:19:23 kgorlen
- * Release for 1st edition.
- *
- */
-
- #include "Date.h"
- #include "String.h"
- #include <iomanip.h>
- #include <ctype.h>
- #include <string.h>
- #include <time.h>
- #include "nihclIO.h"
-
- #define THIS Date
- #define BASE Object
- #define BASE_CLASSES BASE::desc()
- #define MEMBER_CLASSES
- #define VIRTUAL_BASE_CLASSES Object::desc()
- #define PARTIAL 0
- #define FULL 1
-
- DEFINE_CLASS(Date,2,"$Header: /afs/alw.nih.gov/unix/sun4_40c/usr/local/src/nihcl-3.0/share/lib/RCS/Date.c,v 3.0 90/05/20 00:19:23 kgorlen Rel $",NULL,NULL);
-
- extern const int NIHCL_BADMODAY,NIHCL_BADDAYNAM,NIHCL_BADMONAM,NIHCL_BADMONTH,NIHCL_BADDAY,
- NIHCL_RDFAIL;
-
- static const unsigned char days_in_month[12] = {31,28,31,30,31,30,31,31,30,31,30,31 };
- static const dayTy first_day_of_month[12] = {1,32,60,91,121,152,182,213,244,274,305,335 };
- static const char* month_names[12] = {"January","February","March","April","May","June",
- "July","August","September","October","November","December" };
- static const char* uc_month_names[12] = {"JANUARY","FEBRUARY","MARCH","APRIL","MAY","JUNE",
- "JULY","AUGUST","SEPTEMBER","OCTOBER","NOVEMBER","DECEMBER" };
- static const char* week_day_names[7] = {"Monday","Tuesday","Wednesday",
- "Thursday","Friday","Saturday","Sunday" };
- static const char* uc_week_day_names[7] = {"MONDAY","TUESDAY","WEDNESDAY",
- "THURSDAY","FRIDAY","SATURDAY","SUNDAY" };
- static const unsigned int seconds_in_day = 24*60*60;
-
- julTy Date::jday(monthTy m, dayTy d, yearTy y)
- /*
- Convert Gregorian calendar date to the corresponding Julian day number
- j. Algorithm 199 from Communications of the ACM, Volume 6, No. 8,
- (Aug. 1963), p. 444. Gregorian calendar started on Sep. 14, 1752.
- This function not valid before that.
- */
- {
- unsigned long c, ya;
- if (m > 2)
- m -= 3;
- else {
- m += 9;
- y--;
- } /* else */
- c = y / 100;
- ya = y - 100*c;
- return ((146097*c)>>2) + ((1461*ya)>>2) + (153*m + 2)/5 + d + 1721119;
- } /* jday */
-
- void Date::mdy(monthTy& m, dayTy& d, yearTy& y) const
- /*
- Convert a Julian day number to its corresponding Gregorian calendar
- date. Algorithm 199 from Communications of the ACM, Volume 6, No. 8,
- (Aug. 1963), p. 444. Gregorian calendar started on Sep. 14, 1752.
- This function not valid before that.
- */
- {
- julTy j = julnum - 1721119;
- y = (yearTy) (((j<<2) - 1) / 146097);
- j = (j<<2) - 1 - 146097*y;
- d = (dayTy)(j>>2);
- j = ((d<<2) + 3) / 1461;
- d = (dayTy)((d<<2) + 3 - 1461*j);
- d = (d + 4)>>2;
- m = (5*d - 3)/153;
- d = 5*d - 3 - 153*m;
- d = (d + 5)/5;
- y = (yearTy)(100*y + j);
- if (m < 10)
- m += 3;
- else {
- m -= 9;
- y++;
- } /* else */
- } /* mdy */
-
- Date::Date()
- /*
- Construct a Date for today's date.
- */
- {
- long clk = time(0);
- const struct tm* now = localtime(&clk);
- julnum = jday(now->tm_mon+1, now->tm_mday, now->tm_year+1900);
- }
-
- Date::Date(long dayCount, yearTy referenceYear)
- /*
- The base date for this computation is Dec. 31 of the previous year. That
- is "day zero" in relation to the addition which is to take place. Therefore,
- we call jday() with the base date and then perform the addition. Note
- that dayCount may be positive or negative and that this function will
- work the same in either case.
- */
- {
- julnum = jday(12, 31, referenceYear-1) + dayCount;
- }
-
-
- Date::Date(long dayCount)
- /*
- Constructs a date with Jan. 1, 1901 as the "day zero". Date(-1) = Dec. 31, 1900
- and Date(1) = Jan. 2, 1901.
- */
- {
- julnum = jday(1, 1, 1901) + dayCount;
- }
-
- bool Date::dayWithinMonth(monthTy month, dayTy day, yearTy year)
- {
- if (day <= 0) return NO;
- unsigned daysInMonth = days_in_month[month-1];
- if (leapYear(year) && month == 2) daysInMonth++;
- if (day > daysInMonth) return NO;
- return YES;
- }
-
- Date::Date(dayTy day, const char* monthName, yearTy year)
- /*
- Construct a Date for the given day, monthName, and year.
- */
- {
- monthTy m = numberOfMonth(monthName);
- if (year <= 99) year += 1900;
- if (!dayWithinMonth(m, day, year))
- setError(NIHCL_BADMODAY,DEFAULT,this,day,monthName,year);
- julnum = jday(m, day, year);
- }
-
- static void skipDelim(istream& strm)
- {
- char c;
- if (!strm.good()) return;
- strm >> c;
- while (strm.good() && !isalnum(c)) strm >> c;
- if (strm.good()) strm.putback(c);
- }
-
- static const char* parseMonth(istream& strm)
- /*
- Parse the name of a month from input stream.
- */
- {
- static char month[10];
- register char* p = month;
- char c;
- skipDelim(strm);
- strm.get(c);
- while (strm.good() && isalpha(c) && (p != &month[10])) {
- *p++ = c;
- strm.get(c);
- }
- if (strm.good()) strm.putback(c);
- *p = '\0';
- return month;
- }
-
- julTy Date::parseDate(istream& strm)
- /*
- Parse a date from the specified input stream. The date must be in one
- of the following forms: dd-mmm-yy, mm/dd/yy, or mmm dd,yy; e.g.: 10-MAR-86,
- 3/10/86, or March 10, 1986. Any non-alphanumeric character may be used as
- a delimiter.
- */
- {
- unsigned d,m,y;
- const char* mon; // name of month
- if (strm.good()) {
- skipDelim(strm);
- strm >> m; // try to parse day or month number
- skipDelim(strm);
- if (strm.eof()) return 0;
- if (strm.fail()) { // parse <monthName><day><year>
- strm.clear();
- mon = parseMonth(strm); // parse month name
- skipDelim(strm);
- strm >> d; // parse day
- }
- else { // try to parse day number
- strm >> d;
- if (strm.eof()) return 0;
- if (strm.fail()) { // parse <day><monthName><year>
- d = m;
- strm.clear();
- mon = parseMonth(strm); // parse month name
- }
- else { // parsed <monthNumber><day><year>
- mon = nameOfMonth(m);
- }
- }
- skipDelim(strm);
- strm >> y;
- }
- if (!strm.good()) return 0;
- return Date(d,mon,y).julnum;
- }
-
- Date::Date(istream& strm) { julnum = parseDate(strm); }
-
- dayTy Date::dayOfWeek(const char* nameOfDay)
- /*
- Returns the number, 1-7, of the day of the week named nameOfDay.
- */
- {
- {
- String s(nameOfDay);
- register unsigned len = s.size();
- if (len > 2) {
- s.toUpper();
- register const char* p = s;
- for (register unsigned i =0; i<7; i++)
- if (strncmp(p,uc_week_day_names[i],len)==0) return i+1;
- }
- }
- setError(NIHCL_BADDAYNAM,DEFAULT,nameOfDay);
- return 0; // never executed
- }
-
- dayTy Date::daysInYear(yearTy year)
- /*
- How many days are in the given yearTy year?
- */
- {
- if (leapYear(year))
- return 366;
- else
- return 365;
- }
-
- monthTy Date::numberOfMonth(const char* nameOfMonth)
- /*
- Returns the number, 1-12, of the month named nameOfMonth.
- */
- {
- {
- String s(nameOfMonth);
- register unsigned len = s.size();
- if (len > 2) {
- s.toUpper();
- register const char* p = s;
- for (register unsigned i =0; i<12; i++)
- if (strncmp(p,uc_month_names[i],len)==0) return i+1;
- }
- }
- setError(NIHCL_BADMONAM,DEFAULT,nameOfMonth);
- return 0; // never executed
- }
-
- bool Date::leapYear(yearTy year)
- {
- /*
- Algorithm from K & R, "The C Programming Language", 1st ed.
- */
- if ((year&3) == 0 && year%100 != 0 || year % 400 == 0)
- return YES;
- else
- return NO;
- }
-
- bool Date::leap() const
- {
- return leapYear(year());
- }
-
- const char* Date::nameOfMonth(monthTy monthNumber)
- /*
- Returns a string name for the month number.
- */
- {
- if (monthNumber > 12)
- setError(NIHCL_BADMONTH, DEFAULT, monthNumber);
- return month_names[--monthNumber];
- }
-
- const char* Date::nameOfDay(dayTy weekDayNumber)
- /*
- Returns a string name for the weekday number.
- Monday == 1, ... , Sunday == 7
- */
- {
- if (weekDayNumber > 7)
- setError(NIHCL_BADDAY, DEFAULT, weekDayNumber);
- return week_day_names[--weekDayNumber];
- }
-
- const char* Date::nameOfMonth() const { return nameOfMonth(month()); }
-
- monthTy Date::month() const
- {
- monthTy m; dayTy d; yearTy y;
- mdy(m, d, y);
- return m;
- }
-
- dayTy Date::firstDayOfMonth(monthTy month) const
- {
- if (month > 12)
- setError(NIHCL_BADMONTH, DEFAULT, month);
- unsigned firstDay = first_day_of_month[month-1];
- if (month > 2 && leap()) firstDay++;
- return firstDay;
- }
-
- Date Date::previous(const char* nameOfDay) const
- {
- dayTy this_day_Of_Week, desired_day_Of_Week;
- julTy j;
-
- // Set the desired and current day of week to start at 0 (Monday)
- // and end at 6 (Sunday).
-
- desired_day_Of_Week = dayOfWeek(nameOfDay) - 1; // These functions return a value
- this_day_Of_Week = weekDay() - 1; // from 1-7. Subtract 1 for 0-6.
- j = julnum;
-
- // Have to determine how many days difference from current day back to
- // desired, if any. Special calculation under the 'if' statement to
- // effect the wraparound counting from Monday (0) back to Sunday (6).
-
- if (desired_day_Of_Week > this_day_Of_Week)
- this_day_Of_Week += 7 - desired_day_Of_Week;
- else
- this_day_Of_Week -= desired_day_Of_Week;
- j -= this_day_Of_Week; // Adjust j to set it at the desired day of week.
- return Date(j);
- }
-
- dayTy Date::weekDay() const
- /*
- Although this seems a little strange, it works. (julnum + 1) % 7 gives the
- value 0 for Sunday ... 6 for Saturday. Since we want the list to start at
- Monday, add 6 (mod 7) to this. Now we have Monday at 0 ... Sunday at
- 6. Simply add 1 to the result to obtain Monday (1) ... Sunday (7).
- */
- {
- return ((((julnum + 1) % 7) + 6) % 7) + 1;
- }
-
- yearTy Date::year() const
- /*
- Returns the year of this Date.
- */
- {
- monthTy m; dayTy d; yearTy y;
- mdy(m, d, y);
- return y;
- }
-
- dayTy Date::day() const
- /*
- Returns the day of the year of this Date. First we need to find what year we
- are talking about, and then subtract this julnum from the julnum for December 31
- of the preceeding year.
- */
- {
- return julnum - jday(12, 31, year()-1);
- }
-
- dayTy Date::dayOfMonth() const
- {
- monthTy m; dayTy d; yearTy y;
- mdy(m, d, y);
- return d;
- }
-
- int Date::compare(const Object& ob) const
- {
- assertArgSpecies(ob,classDesc,"compare");
- return julnum - castdown(ob).julnum;
- }
-
- void Date::deepenShallowCopy() {}
-
- unsigned Date::hash() const
- {
- return julnum;
- }
-
- bool Date::isEqual(const Object& ob) const
- {
- return ob.isSpecies(classDesc) && *this==castdown(ob);
- }
-
- const Class* Date::species() const { return &classDesc; }
-
- void Date::printOn(ostream& strm) const
- {
- strm << setfill(' ') << setw(2) << dayOfMonth() << '-';
- strm.write(nameOfMonth(), 3);
- strm << '-' << setfill('0') << setw(2) << (year() % 100);
- }
-
- void Date::scanFrom(istream& strm) { julnum = parseDate(strm); }
-
- Date::Date(OIOin& strm)
- : BASE(strm)
- {
- strm >> julnum;
- }
-
- void Date::storer(OIOout& strm) const
- {
- BASE::storer(strm);
- strm << julnum;
- }
-
- Date::Date(OIOifd& fd)
- : BASE(fd)
- {
- fd >> julnum;
- }
-
- void Date::storer(OIOofd& fd) const
- {
- BASE::storer(fd);
- fd << julnum;
- }
-
-
-
-
-