Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members

Date.cc

Go to the documentation of this file.
00001 /*
00002  * Date.cc
00003  *
00004  * Smalltalk like class library for C++
00005  * Date manipulation.
00006  *
00007  * Copyright (c) 2005 Milan Cermak
00008  * This class is based on K. E. Gorlen's NIHCL library implementation
00009  * Copyright (c) 1992 K.E. Gorlen
00010  */
00011 /*
00012  * This library is free software; you can redistribute it and/or
00013  * modify it under the terms of the GNU Lesser General Public
00014  * License as published by the Free Software Foundation; either
00015  * version 2.1 of the License, or (at your option) any later version.
00016  *
00017  * This library is distributed in the hope that it will be useful,
00018  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00019  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00020  * Lesser General Public License for more details.
00021  *
00022  * You should have received a copy of the GNU Lesser General Public
00023  * License along with this library; if not, write to the Free Software
00024  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00025  */
00026 #include <stlib/Date.h>
00027 #include <stlib/String.h>
00028 #include <stlib/Stream.h>
00029 
00030 static const unsigned char days_in_month[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
00031 
00032 Date::Date(unsigned long julianDate)
00033 {
00034     julian_day_number = julianDate;
00035 }
00036 
00037 Date::Date(int month, int day, int year)
00038 {
00039     julian_day_number = julianDay(month, day, year);
00040 }
00041 
00042 /* Class-accessing protocol */
00043 String *Date::className(void) const
00044 {
00045     return new String("Date");
00046 }
00047 
00048 /* Instance creation protocol */
00049 Date *Date::today(void)
00050 {
00051     time_t timestamp = time(NULL);
00052     const struct tm* date = localtime(&timestamp);
00053     return new Date(date->tm_mon+1, date->tm_mday, date->tm_year+1900);
00054 }
00055 
00056 /* Accessing protocol */
00058 int Date::month(void) const
00059 {
00060     int m, d, y;
00061     gregorianDate(m, d, y);
00062     return m;
00063 }
00064 
00066 int Date::day(void) const
00067 {
00068     int m, d, y;
00069     gregorianDate(m, d, y);
00070     return d;
00071 }
00072 
00074 int Date::year(void) const
00075 {
00076     int m, d, y;
00077     gregorianDate(m, d, y);
00078     return y;
00079 }
00080 
00082 int Date::weekday(void) const
00083 /* Although this seems a little strange, it works.  (julnum + 1) % 7 gives the
00084  * value 0 for Sunday ... 6 for Saturday.  Since we want the list to start at
00085  * Monday, add 6 (mod 7) to this.  Now we have Monday at 0 ... Sunday at
00086  * 6.  Simply add 1 to the result to obtain Monday (1) ... Sunday (7).
00087  */
00088 {
00089     return ((((julian_day_number + 1) % 7) + 6) % 7) + 1;
00090 }
00091 
00093 int Date::century(void) const
00094 {
00095     return year() / 100;
00096 }
00097 
00098 int Date::daysInMonth(int month, int year)
00099 {
00100     int dayCount = days_in_month[month];
00101     if (month == 2 && isLeapYear(year))      // Take care of February
00102         dayCount = 29;
00103     return dayCount;
00104 }
00105 
00106 /* Arithmetics protocol */
00110 Date *Date::addDays(int dayCount)
00111 {
00112     return new Date(julian_day_number + dayCount);
00113 }
00114 
00120 Date *Date::addMonths(int monthCount)
00121 {
00122     int m, d, y;
00123     gregorianDate(m, d, y);
00124     m += monthCount;
00125     while (m > 12) {
00126         m -= 12; y += 1;
00127     }
00128     int dm = daysInMonth(m, y);
00129     if (d > dm) d = dm;
00130     return new Date(m, d, y);
00131 }
00132 
00136 Date *Date::addYears(int yearCount)
00137 {
00138     int m, d, y;
00139     gregorianDate(m, d, y);
00140     return new Date(m, d, y+yearCount);
00141 }
00142 
00143 Date *Date::subtractDays(int dayCount)
00144 {
00145     return new Date(julian_day_number - dayCount);
00146 }
00147 
00148 Date *Date::subtractMonths(int monthCount)
00149 {
00150     int m, d, y;
00151     gregorianDate(m, d, y);
00152     m -= monthCount;
00153     while (m <= 0) {
00154         m += 12; y -= 1;
00155     }
00156     int dm = daysInMonth(m, y);
00157     if (d > dm) d = dm;
00158     return new Date(m, d, y);
00159 }
00160 
00161 Date *Date::subtractYears(int yearCount)
00162 {
00163     int m, d, y;
00164     gregorianDate(m, d, y);
00165     return new Date(m, d, y-yearCount);
00166 }
00167 
00168 unsigned long Date::dayDifferenceFrom(Date *date)
00169 {
00170     unsigned long myDays, dateDays;
00171     myDays = asDays();
00172     dateDays = date->asDays();
00173     if (myDays > dateDays)
00174         return myDays - dateDays;
00175     return dateDays - myDays;
00176 }
00177 
00178 /* Comparing protocol */
00179 long Date::hash(void) const
00180 {
00181     return (long) julian_day_number;
00182 }
00183 
00184 bool Date::isEqual(const Object *object) const
00185 {
00186     if (!object->className()->isEqual(this->className())) return false;
00187     return object->hash() == this->hash();
00188 }
00189 
00190 /* Converting protocol */
00191 unsigned long Date::asDays(void) const
00192 {
00193     return julian_day_number;
00194 }
00195 
00196 /* Printing protocol */
00197 void Date::printOn(Stream *stream) const
00198 {
00199     /* Print self in numbers. Locale should know month names. */
00200     String *dateStr;
00201     int m, d, y;
00202     gregorianDate(m, d, y);
00203     dateStr = String::format("%u.%u.%u", d, m, y);
00204     stream->nextPutAll(dateStr);
00205 }
00206 
00207 /* Testing protocol */
00209 bool Date::isLeapYear(void) const
00210 {
00211     return isLeapYear(year());
00212 }
00213 
00215 bool Date::isLeapYear(short year)
00216 /* Algorithm from K & R, "The C Programming Language", 1st ed. */
00217 {
00218     return ((year & 3) == 0 && year % 100 != 0 || year % 400 == 0);
00219 }
00220 
00221 /* private protocol */
00222 /*
00223 Convert Gregorian calendar date to the corresponding Julian day number
00224 j.  Algorithm 199 from Communications of the ACM, Volume 6, No. 8,
00225 (Aug. 1963), p. 444.  Gregorian calendar started on Sep. 14, 1752.
00226 This function not valid before that.
00227 */
00228 unsigned long Date::julianDay(int month, int day, int year)
00229 {
00230     unsigned long c, ya;
00231     if (month > 2) month -= 3;
00232     else {
00233         month += 9;
00234         year--;
00235     }
00236     ya = year % 100;
00237     c = year / 100;
00238     return ((146097*c)>>2) + ((1461*ya)>>2) + (153*month + 2)/5 + day + 1721119;
00239 }
00240 
00241 /*
00242 Convert a Julian day number to its corresponding Gregorian calendar
00243 date.  Algorithm 199 from Communications of the ACM, Volume 6, No. 8,
00244 (Aug. 1963), p. 444.  Gregorian calendar started on Sep. 14, 1752.
00245 This function not valid before that.
00246 */
00247 void Date::gregorianDate(int &month, int &day, int &year) const
00248 {
00249     unsigned long j = julian_day_number - 1721119;
00250     unsigned long y = ((j<<2) - 1) / 146097;
00251     j = (j<<2) - 1 - 146097*y;
00252     unsigned long d = j>>2;
00253     j = ((d<<2) + 3) / 1461;
00254     d = (d<<2) + 3 - 1461*j;
00255     d = (d + 4)>>2;
00256     unsigned long m = (5*d - 3)/153;
00257     d = 5*d - 3 - 153*m;
00258     d = (d + 5)/5;
00259     y = 100*y + j;
00260     if (m < 10) m += 3;
00261     else {
00262         m -= 9;
00263         y++;
00264     }
00265     month = (int) m;
00266     day   = (int) d;
00267     year  = (int) y;
00268 }

Generated on Mon Nov 27 09:47:54 2006 for Smalltalk like C++ Class Library by  doxygen 1.4.2