1 /********************************************************************************
2 *                                                                               *
3 *                            D a t e   C l a s s                                *
4 *                                                                               *
5 *********************************************************************************
6 * Copyright (C) 2005,2020 by Jeroen van der Zijp.   All Rights Reserved.        *
7 *********************************************************************************
8 * This library is free software; you can redistribute it and/or modify          *
9 * it under the terms of the GNU Lesser General Public License as published by   *
10 * the Free Software Foundation; either version 3 of the License, or             *
11 * (at your option) any later version.                                           *
12 *                                                                               *
13 * This library is distributed in the hope that it will be useful,               *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of                *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                 *
16 * GNU Lesser General Public License for more details.                           *
17 *                                                                               *
18 * You should have received a copy of the GNU Lesser General Public License      *
19 * along with this program.  If not, see <http://www.gnu.org/licenses/>          *
20 ********************************************************************************/
21 #include "xincs.h"
22 #include "fxver.h"
23 #include "fxdefs.h"
24 #include "fxmath.h"
25 #include "FXArray.h"
26 #include "FXHash.h"
27 #include "FXStream.h"
28 #include "FXDate.h"
29 
30 
31 /*
32   Notes:
33   - Henry F. Fliegel and Thomas C. Van Flandern, "A Machine Algorithm for
34     Processing Calendar Dates". CACM, Vol. 11, No. 10, October 1968, pp 657.
35   - Major clean up and simplification was done!
36   - Added week number calculations!
37   - Start of the JD count is 0 at 12 NOON 1 JAN -4712 (4713 BC).
38   - Reminder, MJD = JD - 2400000.5.
39 */
40 
41 
42 using namespace FX;
43 
44 /*******************************************************************************/
45 
46 namespace FX {
47 
48 
49 // Many nanoseconds in a second
50 static const FXTime seconds=1000000000L;
51 
52 // Julian day number of GPS week zero (Jan 6, 1980)
53 static const FXuint GPS_EPOCH_JDAY=2444245;
54 
55 // Julian day number of UNIX epoch (Jan 1, 1970)
56 static const FXuint UNIX_EPOCH_JDAY=2440588;
57 
58 // Julian day number of J2000 (Jan 1, 2000)
59 static const FXuint J2000_EPOCH_JDAY=2451545;
60 
61 // UNIX time to GPS time offset in nanoseconds
62 static const FXTime UNIX_TO_GPS=315964800L*seconds;
63 
64 // Short month names
65 const FXchar FXDate::shortMonthName[12][4]={
66  "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"
67   };
68 
69 
70 // Long month names
71 const FXchar FXDate::longMonthName[12][10]={
72  "January","February","March","April","May","June","July","August","September","October","November","December"
73   };
74 
75 
76 // Short week day name
77 const FXchar FXDate::shortWeekDay[7][4]={
78   "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
79   };
80 
81 
82 // Long week day name
83 const FXchar FXDate::longWeekDay[7][10]={
84   "Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"
85   };
86 
87 
88 // Number of days in nomimal month
89 static const FXuchar monthDays[13]={
90   0,31,28,31,30,31,30,31,31,30,31,30,31
91   };
92 
93 
94 /*******************************************************************************/
95 
96 // Initialize with year and day of year
FXDate(FXint yr,FXint dy)97 FXDate::FXDate(FXint yr,FXint dy){
98   setDate(yr,dy);
99   }
100 
101 
102 // Initialize with year, month, and day of month
FXDate(FXint yr,FXint mo,FXint dy)103 FXDate::FXDate(FXint yr,FXint mo,FXint dy){
104   setDate(yr,mo,dy);
105   }
106 
107 
108 // Set date to year and day of year
setDate(FXint yr,FXint dy)109 void FXDate::setDate(FXint yr,FXint dy){
110   if(dy<1 || dy>366){ fxerror("FXDate::setDate: bad argument.\n"); }
111   julian=(1461*(yr+4799))/4-(3*((yr+4899)/100))/4+dy-31739;
112   }
113 
114 
115 // Get year and day of year from date
getDate(FXint & yr,FXint & dy) const116 void FXDate::getDate(FXint& yr,FXint& dy) const {
117   FXint l,n,i,j;
118   l=julian+68569;
119   n=(4*l)/146097;
120   l=l-(146097*n+3)/4;
121   i=(4000*(l+1))/1461001;
122   l=l-(1461*i)/4+31;
123   j=(80*l)/2447;
124   l=j/11;
125   yr=100*(n-49)+i+l;
126   dy=julian-(1461*(yr+4799))/4+(3*((yr+4899)/100))/4+31739;
127   }
128 
129 
130 // Set date to year, month, and day of month
setDate(FXint yr,FXint mo,FXint dy)131 void FXDate::setDate(FXint yr,FXint mo,FXint dy){
132   if(mo<1 || mo>12 || dy<1 || dy>31){ fxerror("FXDate::setDate: bad argument.\n"); }
133   julian=(1461*(yr+4800+(mo-14)/12))/4+(367*(mo-2-12*((mo-14)/12)))/12-(3*((yr+4900+(mo-14)/12)/100))/4+dy-32075;
134   }
135 
136 
137 // Get year, month, and day of month from date
getDate(FXint & yr,FXint & mo,FXint & dy) const138 void FXDate::getDate(FXint& yr,FXint& mo,FXint& dy) const {
139   FXint l,n,i,j;
140   l=julian+68569;
141   n=(4*l)/146097;
142   l=l-(146097*n+3)/4;
143   i=(4000*(l+1))/1461001;
144   l=l-(1461*i)/4+31;
145   j=(80*l)/2447;
146   dy=l-(2447*j)/80;
147   l=j/11;
148   mo=j+2-(12*l);
149   yr=100*(n-49)+i+l;
150   }
151 
152 
153 // Set date from nanoseconds since 1/1/1970
154 // Technically, Julian Day starts at noon; however we truncate
155 // incoming time to 00:00:00.
setTime(FXTime ns)156 void FXDate::setTime(FXTime ns){
157   const FXTime days=86400L*seconds;
158   julian=UNIX_EPOCH_JDAY+(((0<=ns)?ns:ns-days+1)/days);
159   }
160 
161 
162 // Get nanoseconds since 1/1/1970 from date
163 // Return time in nanoseconds at start of the day
getTime() const164 FXTime FXDate::getTime() const {
165   const FXTime days=86400L*seconds;
166   return ((FXTime)julian-(FXTime)UNIX_EPOCH_JDAY)*days;
167   }
168 
169 
170 // is value a leap year?
leapYear(FXint yr)171 FXbool FXDate::leapYear(FXint yr){
172   return ((yr%4==0) && (yr%100!=0)) || (yr%400==0);
173   }
174 
175 
176 // Return number of days in a given year
daysInYear(FXint yr)177 FXint FXDate::daysInYear(FXint yr){
178   return leapYear(yr) ? 366 : 365;
179   }
180 
181 
182 // Return number of days in the month in given year, month
daysInMonth(FXint yr,FXint mo)183 FXint FXDate::daysInMonth(FXint yr,FXint mo){
184   return (mo==2 && leapYear(yr)) ? 29 : monthDays[mo];
185   }
186 
187 
188 // Return day of the month
day() const189 FXint FXDate::day() const {
190   FXint yr,mo,dy;
191   getDate(yr,mo,dy);
192   return dy;
193   }
194 
195 
196 // Return month
month() const197 FXint FXDate::month() const {
198   FXint yr,mo,dy;
199   getDate(yr,mo,dy);
200   return mo;
201   }
202 
203 
204 // Return year
year() const205 FXint FXDate::year() const {
206   FXint yr,mo,dy;
207   getDate(yr,mo,dy);
208   return yr;
209   }
210 
211 
212 // Return day of the week
dayOfWeek() const213 FXint FXDate::dayOfWeek() const {
214   return (julian+1)%7;                  // Sunday is day 0 of week
215   }
216 
217 
218 // Return true if leap year
leapYear() const219 FXbool FXDate::leapYear() const {
220   return leapYear(year());
221   }
222 
223 
224 // Return number of days in this year
daysInYear() const225 FXint FXDate::daysInYear() const {
226   return daysInYear(year());
227   }
228 
229 
230 // Return days in this month
daysInMonth() const231 FXint FXDate::daysInMonth() const {
232   FXint yr,mo,dy;
233   getDate(yr,mo,dy);
234   return daysInMonth(yr,mo);
235   }
236 
237 
238 // Return day of year
dayOfYear() const239 FXint FXDate::dayOfYear() const {
240   FXDate s(year(),1);
241   return julian-s.julian+1;
242   }
243 
244 
245 // Return ISO8601 week number of this date
weekOfYear() const246 FXint FXDate::weekOfYear() const {
247   FXint d4=(((julian+31741-julian%7)%146097)%36524)%1461;
248   FXint L=d4/1460;
249   FXint d1=(d4-L)%365+L;
250   return 1+d1/7;
251   }
252 
253 
254 // Add d days to this date
addDays(FXint d)255 FXDate& FXDate::addDays(FXint d){
256   julian+=d;
257   return *this;
258   }
259 
260 
261 // Add m months to this date; day of month is adjusted for leap-years
addMonths(FXint m)262 FXDate& FXDate::addMonths(FXint m){
263   FXint yr,mo,dy,mx;
264   getDate(yr,mo,dy);
265   if(0<=m){
266     yr=yr+(mo-1+m)/12;
267     mo=1+(mo-1+m)%12;
268     }
269   else{
270     yr=yr+(mo-12+m)/12;
271     mo=1+(mo+2147483627+m)%12;
272     }
273   mx=daysInMonth(yr,mo);
274   if(dy>mx) dy=mx;
275   setDate(yr,mo,dy);
276   return *this;
277   }
278 
279 
280 // Add y years to this date; day of month is adjusted for leap-years
addYears(FXint y)281 FXDate& FXDate::addYears(FXint y){
282   FXint yr,mo,dy;
283   getDate(yr,mo,dy);
284   yr+=y;
285   if(dy>28 && mo==2 && !leapYear(yr)) dy=28;
286   setDate(yr,mo,dy);
287   return *this;
288   }
289 
290 
291 // Return current local date
localDate()292 FXDate FXDate::localDate(){
293 #if defined(WIN32)
294   SYSTEMTIME t;
295   GetLocalTime(&t);
296   return FXDate(t.wYear,t.wMonth,t.wDay);
297 #elif defined(HAVE_LOCALTIME_R)
298   struct tm result,*t;
299   time_t ltime;
300   time(&ltime);
301   t=localtime_r(&ltime,&result);
302   return FXDate(t->tm_year+1900,t->tm_mon+1,t->tm_mday);
303 #else
304   struct tm *t;
305   time_t ltime;
306   time(&ltime);
307   t=localtime(&ltime);
308   return FXDate(t->tm_year+1900,t->tm_mon+1,t->tm_mday);
309 #endif
310   }
311 
312 
313 // Return current universal (UTC) date
universalDate()314 FXDate FXDate::universalDate(){
315 #if defined(WIN32)
316   SYSTEMTIME t;
317   GetSystemTime(&t);
318   return FXDate(t.wYear,t.wMonth,t.wDay);
319 #elif defined(HAVE_GMTIME_R)
320   struct tm result,*t;
321   time_t ltime;
322   time(&ltime);
323   t=gmtime_r(&ltime,&result);
324   return FXDate(t->tm_year+1900,t->tm_mon+1,t->tm_mday);
325 #else
326   struct tm *t;
327   time_t ltime;
328   time(&ltime);
329   t=gmtime(&ltime);
330   return FXDate(t->tm_year+1900,t->tm_mon+1,t->tm_mday);
331 #endif
332   }
333 
334 
335 // save to store
operator <<(FXStream & store,const FXDate & d)336 FXStream& operator<<(FXStream& store,const FXDate& d){
337   store << d.julian;
338   return store;
339   }
340 
341 
342 // load from store
operator >>(FXStream & store,FXDate & d)343 FXStream& operator>>(FXStream& store,FXDate& d){
344   store >> d.julian;
345   return store;
346   }
347 
348 }
349 
350