1
// ---------------------------------------------------------------------------
3
// - standard object library - date class implementation -
4
// ---------------------------------------------------------------------------
5
// - This program is free software; you can redistribute it and/or modify -
6
// - it provided that this copyright notice is kept intact. -
8
// - This program is distributed in the hope that it will be useful, but -
9
// - without any warranty; without even the implied warranty of -
10
// - merchantability or fitness for a particular purpose. In no event shall -
11
// - the copyright holder be liable for any direct, indirect, incidental or -
12
// - special damages arising in any way out of the use of this software. -
13
// ---------------------------------------------------------------------------
14
// - copyright (c) 1999-2007 amaury darsch -
15
// ---------------------------------------------------------------------------
20
#include "Integer.hpp"
21
#include "Utility.hpp"
22
#include "QuarkZone.hpp"
23
#include "Exception.hpp"
28
// -------------------------------------------------------------------------
29
// - private section -
30
// -------------------------------------------------------------------------
32
// week day string mapping in us/week reference
33
static const long ATC_MAX_WDAY = 7;
34
static const char* ATC_DAY_NAME[ATC_MAX_WDAY] = {
35
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
37
// months string mapping
38
static const long ATC_MAX_YMON = 12;
39
static const char* ATC_MON_NAME[ATC_MAX_YMON] = {
40
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
41
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
44
// number of gregorian days per year (normal and leap)
45
static const long ATC_YDAY_NORM = 365;
46
static const long ATC_YDAY_LEAP = 366;
47
// number of gregorian days per month (normal and leap)
48
static const long ATC_MDAY_NORM[12] = {
49
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
51
static const long ATC_MDAY_LEAP[12] = {
52
31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
55
// format the day string
56
static String atc_mapwday (const long wday) {
57
if ((wday < 0) || (wday >= ATC_MAX_WDAY))
58
throw Exception ("date-error", "day index is ot of range");
59
return ATC_DAY_NAME[wday];
62
// format the month string
63
static String atc_mapymon (const long ymon) {
64
if ((ymon < 1) || (ymon > ATC_MAX_YMON))
65
throw Exception ("time-error", "month index is out of range");
66
return ATC_MON_NAME[ymon-1];
69
// return the absolute value
70
static inline long abs (const long abs) {
71
return (abs < 0) ? -abs : abs;
73
static inline t_long abs (const t_long abs) {
74
return (abs < 0) ? -abs : abs;
77
// return true if the year is a leap year
78
static inline bool atc_isleap (const long year) {
79
if (year == 0) return true;
81
long ywrk = abs (year);
82
// 0 is a special case
83
if ((ywrk % 400) == 0) return true;
84
if ((ywrk % 100) == 0) return false;
85
if ((ywrk % 4) == 0) return true;
89
// get the number of days in a year
90
static inline t_long atc_yday_in_year (const long year) {
91
t_long yday = atc_isleap (year) ? ATC_YDAY_LEAP : ATC_YDAY_NORM;
95
// get the number of seconds in a year
96
static inline t_long atc_ysec_in_year (const long year) {
97
t_long ysec = atc_yday_in_year (year) * (t_long) Time::DSEC;
101
// get the number of days in a year month
102
static inline t_long atc_mday_in_ymon (const long year, const long ymon) {
103
if (atc_isleap (year) == true) return ATC_MDAY_LEAP[ymon];
104
return ATC_MDAY_NORM[ymon];
107
// get the number of seconds in a year month
108
static inline t_long atc_msec_in_ymon (const long year, const long ymon) {
109
t_long msec = atc_mday_in_ymon (year, ymon) * (t_long) Time::DSEC;
113
// trim the day in a year month
114
static inline long atc_trim_in_ymon (const long year, const long ymon,
116
long maxd = (long) atc_mday_in_ymon (year, ymon);
117
return (mday <= maxd) ? mday : maxd;
120
// get the number of days upto a year
121
static t_long atc_yday_to_year (const long year) {
123
long ywrk = abs (year);
124
// iterate upto the year
126
for (long i = 0; i < ywrk; i++) {
127
yday += atc_yday_in_year (i);
132
// get the number of seconds upto a year
133
static t_long atc_ysec_to_year (const long year) {
134
t_long ysec = atc_yday_to_year (year) * (t_long) Time::DSEC;
138
// get the number of days upto a month in a specific year
139
static t_long atc_yday_to_ymon (const long year, const long ymon) {
141
if (atc_isleap (year) == true) {
142
for (long i = 0; i < ymon; i++) yday += ATC_MDAY_LEAP[i];
144
for (long i = 0; i < ymon; i++) yday += ATC_MDAY_NORM[i];
149
// get the number of seconds upto a month in a specific year
150
static t_long atc_ysec_to_ymon (const long year, const long ymon) {
151
t_long ysec = atc_yday_to_ymon (year, ymon) * (t_long) Time::DSEC;
155
// number of day upto a date
156
static t_long atc_yday_to_date (const long year, const long ymon,
159
yday += atc_yday_to_year (year);
160
yday += atc_yday_to_ymon (year, ymon);
165
// number of seconds upto a date
166
static t_long atc_ysec_to_date (const long year, const long ymon,
168
t_long ysec = atc_yday_to_date (year, ymon, mday) * (t_long) Time::DSEC;
172
// find the year from an atc clock
173
static long atc_year_from_tclk (const t_long tclk) {
174
// get the absolute clock
175
t_long wclk = abs (tclk);
176
// compute the year by iteration
178
long ymax = Utility::maxlong ();
180
while (year < ymax) {
181
cclk += atc_ysec_in_year (year);
182
if (cclk > wclk) break;
188
// find the month in a year from an atc clock
189
static long atc_ymon_from_tclk (const t_long tclk, const long year) {
190
// get the absolute clock
191
t_long wclk = abs (tclk);
192
// compute the month by iteration
195
for (long i = 0; i < ATC_MAX_YMON; i++) {
196
cclk += atc_msec_in_ymon (year, i);
197
if (cclk > wclk) break;
206
long d_yday; // day in year
207
long d_ymon; // month in year [0:11]
208
long d_mday; // month in day [0:30]
209
long d_wday; // day in week [0:6]
210
// create a default date
218
// create a specific date
219
s_date (const t_long tclk) {
220
// get the absolute clock
221
t_long wclk = abs (tclk);
223
d_year = atc_year_from_tclk (wclk);
224
// update the remaining seconds
225
t_long secs = wclk - atc_ysec_to_year (d_year);
227
d_yday = (long) (secs / (t_long) Time::DSEC);
228
// get the year month
229
d_ymon = atc_ymon_from_tclk (secs, d_year);
230
// update the remaining seconds
231
secs -= atc_ysec_to_ymon (d_year, d_ymon);
233
d_mday = (long) (secs / (t_long) Time::DSEC);
235
// get the week day (0000-00-00 is a saturday)
236
d_wday = ((wclk / (t_long)Time::DSEC) + 6) % 7;
240
// get the week day (0000-00-00 is a saturday)
241
d_wday = (13 - (atc_yday_in_year (d_year) - d_yday)) % 7;
246
// -------------------------------------------------------------------------
248
// -------------------------------------------------------------------------
250
// create a current date
253
p_date = new s_date (d_tclk);
256
// create a specific date
258
Date::Date (const t_long tclk) : Time (tclk) {
259
p_date = new s_date (d_tclk);
262
// create a date by iso specification
264
Date::Date (const String& date) {
269
// create a date by specific elements
271
Date::Date (const long year, const long ymon, const long mday) : Time (0) {
273
setdate (year, ymon, mday);
276
// create a date by specific elements
278
Date::Date (const long year, const long ymon, const long mday,
279
const long hour, const long mins, const long secs) : Time (0) {
281
setdate (year, ymon, mday, hour, mins, secs);
284
// copy construct this date
286
Date::Date (const Date& that) {
289
settime (that.gettime ());
299
// return the class name
301
String Date::repr (void) const {
305
// return a clone of this object
307
Object* Date::clone (void) const {
308
return new Date (*this);
311
// set the date by specific time
313
void Date::settime (const t_long tclk) {
317
Time::settime (tclk);
318
p_date = new s_date (d_tclk);
326
// set a date by an iso specification
328
void Date::setdate (const String& date) {
331
// extract the date component
332
Regex re ("[($d$d$d$d)-($d$d)-($d$d)T($d$d):($d$d):($d$d)Z]");
334
if (re.length () != 6) {
335
throw Exception ("internal-error", "invalid parsed date", date);
338
long year = re.getint (0);
339
long ymon = re.getint (1);
340
long mday = re.getint (2);
341
long hour = re.getint (3);
342
long mins = re.getint (4);
343
long secs = re.getint (5);
345
setdate (year, ymon, mday, hour, mins, secs);
347
throw Exception ("date-error", "invalid date format", date);
356
// set the date by specific elements
358
void Date::setdate (const long year, const long ymon, const long mday) {
361
t_long tclk = atc_ysec_to_date (year, ymon-1, mday-1);
374
// set the date by specific elements
376
void Date::setdate (const long year, const long ymon, const long mday,
377
const long hour, const long mins, const long secs) {
380
// get the date clock
381
t_long tclk = atc_ysec_to_date (year, ymon-1, mday-1);
383
tclk += (t_long) (hour * HSEC + mins * MSEC + secs);
384
// set the sign and time
397
// add years to the current date
399
void Date::addyear (const long num) {
402
// get the current date
403
long year = getyear ();
404
long ymon = getymon ();
405
long mday = getmday ();
406
long hour = gethour (true);
407
long mins = getmins (true);
408
long secs = getsecs (true);
411
// check if the day is valid
412
mday = atc_trim_in_ymon (year, ymon, mday);
414
setdate (year, ymon, mday, hour, mins, secs);
422
// add months to the current date
424
void Date::addymon (const long num) {
427
// get the current date
428
long year = getyear ();
429
long ymon = getymon ();
430
long mday = getmday ();
431
long hour = gethour (true);
432
long mins = getmins (true);
433
long secs = getsecs (true);
434
// compute number of year and remaining month
435
long ynum = num / 12;
436
long mnum = num % 12;
437
// add the year and month
440
// update year to add and month
443
// check if the day is valid
444
mday = atc_trim_in_ymon (year, ymon, mday);
446
setdate (year, ymon, mday, hour, mins, secs);
454
// return the date year
456
long Date::getyear (void) const {
458
long result = p_date->d_year;
463
// return the day in the year
465
long Date::getyday (void) const {
467
long result = p_date->d_yday + 1;
472
// return the month in the year
474
long Date::getymon (void) const {
476
long result = p_date->d_ymon + 1;
481
// return the day in the month
483
long Date::getmday (void) const {
485
long result = p_date->d_mday + 1;
490
// return the day in the week
492
long Date::getwday (void) const {
494
long result = p_date->d_wday;
499
// return the base day reference time
501
t_long Date::getbday (void) const {
503
t_long result = (d_tclk / DSEC) * DSEC;
508
// return the week day name
510
String Date::mapwday (void) const {
513
long wday = getwday ();
514
String result = atc_mapwday (wday);
523
// return the month name
525
String Date::mapymon (void) const {
528
long ymon = getymon ();
529
String result = atc_mapymon (ymon);
540
String Date::todate (void) const {
544
long year = getyear ();
546
throw Exception ("date-error",
547
"ISO-8601 does not define negative year");
550
throw Exception ("date-error",
551
"ISO-8601 does not define year above 9999");
557
} else if (year < 100) {
559
} else if (year < 1000) {
564
long ymon = getymon ();
572
long mday = getmday ();
589
String Date::totime (const bool utc) const {
592
String result = Time::format (utc);
601
// format the date in a general form
603
String Date::format (const bool utc) const {
606
// format the week day
607
String result = mapwday ();
610
result += mapymon ();
613
result += getmday ();
616
result += totime (utc);
619
result += getyear ();
628
// format the date in the ISO-8601 form
630
String Date::toiso (const bool utc) const {
634
String result = todate ();
637
result += Time::toiso (utc);
646
// format the date in the RFC-2822 form
648
String Date::torfc (void) const {
652
String result = mapwday ();
654
long mday = getmday ();
663
result += mapymon ();
665
long year = getyear ();
668
} else if (year < 100) {
670
} else if (year < 1000) {
678
result += Time::torfc ();
687
// -------------------------------------------------------------------------
688
// - object section -
689
// -------------------------------------------------------------------------
692
static const long QUARK_ZONE_LENGTH = 12;
693
static QuarkZone zone (QUARK_ZONE_LENGTH);
695
// the object supported quarks
696
static const long QUARK_TODATE = zone.intern ("to-date");
697
static const long QUARK_TOTIME = zone.intern ("to-time");
698
static const long QUARK_GETYEAR = zone.intern ("year");
699
static const long QUARK_GETYDAY = zone.intern ("year-day");
700
static const long QUARK_GETYMON = zone.intern ("month");
701
static const long QUARK_GETMDAY = zone.intern ("day");
702
static const long QUARK_ADDYEAR = zone.intern ("add-years");
703
static const long QUARK_ADDYMON = zone.intern ("add-months");
704
static const long QUARK_GETWDAY = zone.intern ("week-day");
705
static const long QUARK_MAPWDAY = zone.intern ("map-day");
706
static const long QUARK_MAPYMON = zone.intern ("map-month");
707
static const long QUARK_SETDATE = zone.intern ("set-date");
708
static const long QUARK_GETBDAY = zone.intern ("get-base-day");
710
// create a new object in a generic way
712
Object* Date::mknew (Vector* argv) {
713
long argc = (argv == nilp) ? 0 : argv->length ();
714
// create a default time object
715
if (argc == 0) return new Date;
717
Object* obj = argv->get (0);
718
// check for an integer
719
Integer* iobj = dynamic_cast <Integer*> (obj);
720
if (iobj != nilp) return new Date (iobj->tointeger ());
721
// check for a string
722
String* sobj = dynamic_cast <String*> (obj);
723
if (sobj != nilp) return new Date (*sobj);
725
throw Exception ("type-error", "invalid object for date",
728
// check for 3 arguments
730
long year = argv->getint (0);
731
long ymon = argv->getint (1);
732
long mday = argv->getint (2);
733
return new Date (year, ymon, mday);
735
// check for 6 arguments
737
long year = argv->getint (0);
738
long ymon = argv->getint (1);
739
long mday = argv->getint (2);
740
long hour = argv->getint (3);
741
long mins = argv->getint (4);
742
long secs = argv->getint (5);
743
return new Date (year, ymon, mday, hour, mins, secs);
745
throw Exception ("argument-error",
746
"too many argument with date constructor");
749
// return true if the given quark is defined
751
bool Date::isquark (const long quark, const bool hflg) const {
753
if (zone.exists (quark) == true) {
757
bool result = hflg ? Time::isquark (quark, hflg) : false;
762
// apply this object with a set of arguments and a quark
764
Object* Date::apply (Runnable* robj, Nameset* nset, const long quark,
766
// get the number of arguments
767
long argc = (argv == nilp) ? 0 : argv->length ();
769
// check for 0 arguments
771
if (quark == QUARK_TODATE) return new String (todate ());
772
if (quark == QUARK_TOTIME) return new String (totime (false));
773
if (quark == QUARK_GETYEAR) return new Integer (getyear ());
774
if (quark == QUARK_GETYDAY) return new Integer (getyday ());
775
if (quark == QUARK_GETYMON) return new Integer (getymon ());
776
if (quark == QUARK_GETMDAY) return new Integer (getmday ());
777
if (quark == QUARK_GETWDAY) return new Integer (getmday ());
778
if (quark == QUARK_GETBDAY) return new Integer (getbday ());
779
if (quark == QUARK_MAPWDAY) return new String (mapwday ());
780
if (quark == QUARK_MAPYMON) return new String (mapymon ());
782
// check for 1 argument
784
if (quark == QUARK_TOTIME) {
785
bool utc = argv->getbool (0);
786
return new String (totime (utc));
788
if (quark == QUARK_ADDYEAR) {
789
long num = argv->getint (0);
793
if (quark == QUARK_ADDYMON) {
794
long num = argv->getint (0);
798
if (quark == QUARK_SETDATE) {
799
String date = argv->getstring (0);
804
// check for 3 arguments
806
if (quark == QUARK_SETDATE) {
807
long year = argv->getint (0);
808
long ymon = argv->getint (1);
809
long mday = argv->getint (2);
810
setdate (year, ymon, mday);
814
// check for 6 arguments
816
if (quark == QUARK_SETDATE) {
817
long year = argv->getint (0);
818
long ymon = argv->getint (1);
819
long mday = argv->getint (2);
820
long hour = argv->getint (3);
821
long mins = argv->getint (4);
822
long secs = argv->getint (5);
823
setdate (year, ymon, mday, hour, mins, secs);
827
// call the time method
828
return Time::apply (robj, nset, quark, argv);