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(<ime);
301 t=localtime_r(<ime,&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(<ime);
307 t=localtime(<ime);
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(<ime);
323 t=gmtime_r(<ime,&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(<ime);
329 t=gmtime(<ime);
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