Annotation of parser3/src/classes/date.C, revision 1.42.2.8
1.1 parser 1: /** @file
2: Parser: @b date parser class.
3:
1.42.2.3 paf 4: Copyright (c) 2001-2003 ArtLebedev Group (http://www.artlebedev.com)
1.16 paf 5: Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)
1.32 paf 6: */
1.1 parser 7:
1.42.2.8! paf 8: static const char* IDENT_DATE_C="$Date: 2003/02/04 15:59:04 $";
1.1 parser 9:
10: #include "classes.h"
1.42.2.5 paf 11: #include "pa_vmethod_frame.h"
12:
1.1 parser 13: #include "pa_request.h"
14: #include "pa_vdouble.h"
15: #include "pa_vdate.h"
1.10 parser 16: #include "pa_vtable.h"
1.1 parser 17:
18: // class
19:
20: class MDate : public Methoded {
21: public: // VStateless_class
1.42.2.2 paf 22: ValuePtr create_new_value() { return ValuePtr(new VDate(0)); }
1.1 parser 23:
24: public:
1.42.2.6 paf 25: MDate();
1.1 parser 26: public: // Methoded
27: bool used_directly() { return true; }
28: };
1.42.2.1 paf 29:
1.42.2.6 paf 30: // global variable
31:
32: MethodedPtr date_class(new MDate);
33:
1.42.2.1 paf 34: // helpers
35:
1.42.2.7 paf 36: class Date_calendar_table_template_columns: public ArrayString {
1.42.2.6 paf 37: Pool pool;
38: public:
1.42.2.7 paf 39: Date_calendar_table_template_columns(): ArrayString(6+2) {
1.42.2.1 paf 40: for(int i=0; i<=6; i++) {
1.42.2.6 paf 41: char *cname=new(pool) char[1/*strlen("6")*/+1/*terminating 0*/];
42: StringPtr sname(new String(cname, sprintf(cname, "%d", i))); // .i column name
43: *this+=sname;
1.42.2.1 paf 44: }
1.42.2.6 paf 45: *this+=StringPtr(new String("week"));
46: *this+=StringPtr(new String("year"));
1.42.2.1 paf 47: }
1.42.2.6 paf 48: };
49:
50:
51: TablePtr date_calendar_table_template(new Table(
52: Exception::undefined_source,
53: Table::columns_type(new Date_calendar_table_template_columns)));
1.1 parser 54:
55: // methods
56:
1.42.2.4 paf 57: static void _now(Request& r, StringPtr method_name, MethodParams& params) {
1.42.2.6 paf 58: VDate& vdate=GET_SELF(r, VDate);
1.23 paf 59:
60: time_t t=time(0);
1.42.2.4 paf 61: if(params.count()==1) // ^now(offset)
62: t+=(time_t)round(params.as_double(0, "offset must be double", r)*SECS_PER_DAY);
1.23 paf 63:
1.42.2.6 paf 64: vdate.set_time(t);
1.1 parser 65: }
66:
1.28 paf 67: static int NN_year_to_NNNN(int year) {
68: if(year<70) // 0..69 -> 100..169 [2000..2069]
69: year+=100;
70: if(year>=1900)
71: year-=1900;
72: return year;
73: }
74:
1.41 paf 75: // 2002-04-25 18:14:00
76: // 18:14:00
77: // 2002:04:25 [+maybe time]
1.42.2.6 paf 78: time_t cstr_to_time_t(char *cstr, StringPtr report_error_origin) { // used in image.C
1.41 paf 79: char *cur=cstr;
80: int date_delim=isdigit(cur[0])&&isdigit(cur[1])&&isdigit(cur[2])&&isdigit(cur[3])&&cur[4]==':'?':'
81: :'-';
1.42.2.3 paf 82: const char* year=lsplit(&cur, date_delim);
83: const char* month=lsplit(&cur, date_delim);
84: const char* mday=lsplit(&cur, ' ');
1.41 paf 85: if(!month)
86: cur=cstr;
1.42.2.3 paf 87: const char* hour=lsplit(&cur, ':');
88: const char* min=lsplit(&cur, ':');
89: const char* sec=cur;
1.41 paf 90:
91: tm tmIn={0};
92: tmIn.tm_isdst=-1;
93: if(!month)
94: if(min) {
95: year=mday=0; // HH:MM
96: time_t t=time(0);
97: tm *tmNow=localtime(&t);
98: tmIn.tm_year=tmNow->tm_year;
99: tmIn.tm_mon=tmNow->tm_mon;
100: tmIn.tm_mday=tmNow->tm_mday;
101: goto date_part_set;
102: } else
103: hour=min=sec=0; // not YYYY- & not HH: = just YYYY
104: tmIn.tm_year=NN_year_to_NNNN(atoi(year));
105: tmIn.tm_mon=month?atoi(month)-1:0;
106: tmIn.tm_mday=mday?atoi(mday):1;
107: date_part_set:
108: tmIn.tm_hour=hour?atoi(hour):0;
109: tmIn.tm_min=min?atoi(min):0;
110: tmIn.tm_sec=sec?atoi(sec):0;
111: time_t result=mktime(&tmIn);
112: if(result<0)
113: if(report_error_origin)
114: throw Exception(0,
115: report_error_origin,
116: "invalid datetime");
117:
118: return result;
119: }
120:
1.42.2.4 paf 121: static void _create(Request& r, StringPtr method_name, MethodParams& params) {
1.1 parser 122: Pool& pool=r.pool();
1.42.2.6 paf 123: VDate& vdate=GET_SELF(r, VDate);
1.1 parser 124:
1.23 paf 125: time_t t;
1.42.2.4 paf 126: if(params.count()==1) {
1.31 paf 127: // ^create[2002-04-25 18:14:00]
128: // ^create[18:14:00]
1.42.2.6 paf 129: if(StringPtr sdate=params.get(0)->get_string(&pool))
1.41 paf 130: t=cstr_to_time_t(sdate->cstr(), sdate);
131: else { // ^create(float days)
1.42.2.4 paf 132: t=(time_t)round(params.as_double(0, "float days must be double", r)*SECS_PER_DAY);
1.28 paf 133: if(t<0 || !localtime(&t))
134: throw Exception(0,
1.42.2.6 paf 135: method_name,
1.28 paf 136: "invalid datetime");
137: }
1.42.2.4 paf 138: } else if(params.count()>=2) { // ^create(y;m;d[;h[;m[;s]]])
1.1 parser 139: tm tmIn={0};
140: tmIn.tm_isdst=-1;
1.42.2.4 paf 141: tmIn.tm_year=NN_year_to_NNNN(params.as_int(0, "year must be int", r));
142: tmIn.tm_mon=params.as_int(1, "month must be int", r)-1;
143: tmIn.tm_mday=params.count()>2?params.as_int(2, "mday must be int", r):1;
144: if(params.count()>3) tmIn.tm_hour=params.as_int(3, "hour must be int", r);
145: if(params.count()>4) tmIn.tm_min=params.as_int(4, "minutes must be int", r);
146: if(params.count()>5) tmIn.tm_sec=params.as_int(5, "seconds must be int", r);
1.23 paf 147: t=mktime(&tmIn);
148: if(t<0)
1.22 paf 149: throw Exception(0,
1.42.2.6 paf 150: method_name,
1.1 parser 151: "invalid datetime");
152: } else
1.22 paf 153: throw Exception("parser.runtime",
1.42.2.6 paf 154: method_name,
1.24 paf 155: "invalid params count, must be 1 or >=2");
1.42.2.6 paf 156: vdate.set_time(t);
1.1 parser 157: }
158:
1.42.2.4 paf 159: static void _sql_string(Request& r, StringPtr method_name, MethodParams& ) {
1.1 parser 160: Pool& pool=r.pool();
1.42.2.6 paf 161: VDate& vdate=GET_SELF(r, VDate);
1.2 parser 162: int size=1+ 4+1+2+1+2 +1+ 2+1+2+1+2 +1 +1;
1.42.2.8! paf 163: char *buf=new(pool) char[size];
1.42.2.6 paf 164: time_t time=vdate.get_time();
1.14 paf 165: size=strftime(buf, size, "%Y-%m-%d %H:%M:%S", localtime(&time));
1.1 parser 166:
1.42.2.6 paf 167: String string;
1.4 parser 168: string.APPEND_CLEAN(buf, size,
1.42.2.6 paf 169: method_name->origin().file,
170: method_name->origin().line);
1.25 paf 171: r.write_assign_lang(string);
1.1 parser 172: }
173:
174:
1.42.2.4 paf 175: static void _roll(Request& r, StringPtr method_name, MethodParams& params) {
1.1 parser 176: Pool& pool=r.pool();
1.42.2.6 paf 177: VDate& vdate=GET_SELF(r, VDate);
1.1 parser 178:
1.42.2.6 paf 179: StringPtr what=params.as_string(0, "'what' must be string");
1.1 parser 180: int oyear=0;
181: int omonth=0;
182: int oday=0;
183: int *offset;
1.42.2.6 paf 184: if(*what=="year") offset=&oyear;
185: else if(*what=="month") offset=&omonth;
186: else if(*what=="day") offset=&oday;
1.1 parser 187: else
1.22 paf 188: throw Exception("parser.runtime",
1.42.2.6 paf 189: what,
1.1 parser 190: "must be year|month|day");
191:
1.42.2.4 paf 192: *offset=params.as_int(1, "offset must be int", r);
1.1 parser 193:
1.42.2.6 paf 194: time_t self_time=vdate.get_time();
1.13 paf 195: tm tmIn=*localtime(&self_time);
196: tm tmSaved=tmIn;
197:
1.21 paf 198: tmIn.tm_year+=oyear;
199: tmIn.tm_mon+=omonth;
200: tmIn.tm_mday+=oday;
201: tmIn.tm_hour=24/2;
202: tmIn.tm_min=0;
203: tmIn.tm_sec=0;
204: time_t t=mktime/*normalizetime*/(&tmIn);
205: if(t<0)
1.22 paf 206: throw Exception(0,
1.42.2.6 paf 207: method_name,
1.21 paf 208: "bad resulting time (after roll)");
1.24 paf 209: if(oday==0 && tmIn.tm_mday!=tmSaved.tm_mday)
210: throw Exception(0,
1.42.2.6 paf 211: method_name,
1.24 paf 212: "bad resulting time (day hole)", t);
1.21 paf 213:
1.13 paf 214: tm *tmOut=localtime(&t);
215: if(!tmOut)
1.22 paf 216: throw Exception(0,
1.42.2.6 paf 217: method_name,
1.13 paf 218: "bad resulting time (seconds from epoch=%ld)", t);
219:
220: tmOut->tm_hour=tmSaved.tm_hour;
221: tmOut->tm_min=tmSaved.tm_min;
222: tmOut->tm_sec=tmSaved.tm_sec;
1.20 paf 223: tmOut->tm_isdst=-1;
1.13 paf 224: {
1.21 paf 225: time_t t=mktime/*normalizetime*/(tmOut);
226: if(
227: tmOut->tm_hour!=tmSaved.tm_hour
228: ||tmOut->tm_min!=tmSaved.tm_min)
1.22 paf 229: throw Exception(0,
1.42.2.6 paf 230: method_name,
1.24 paf 231: "bad resulting time (hour hole)");
1.21 paf 232:
1.13 paf 233: if(t<0)
1.22 paf 234: throw Exception(0,
1.42.2.6 paf 235: method_name,
1.21 paf 236: "bad resulting time (after reconstruction)");
1.13 paf 237:
1.42.2.6 paf 238: vdate.set_time(t);
1.13 paf 239: }
1.1 parser 240: }
241:
1.42.2.6 paf 242: static TablePtr fill_month_days(Request& r,
1.42.2.4 paf 243: StringPtr method_name, MethodParams& params, bool rus){
1.10 parser 244: Pool& pool=r.pool();
1.42.2.6 paf 245: TablePtr result(new Table(*date_calendar_table_template));
1.10 parser 246:
1.42.2.4 paf 247: int year=params.as_int(1, "year must be int", r);
248: int month=max(1, min(params.as_int(2, "month must be int", r), 12)) -1;
1.10 parser 249:
250: tm tmIn={0, 0, 0, 1, month, year-1900};
251: time_t t=mktime(&tmIn);
252: if(t<0)
1.22 paf 253: throw Exception(0,
1.42.2.6 paf 254: method_name,
1.10 parser 255: "invalid date");
256: tm *tmOut=localtime(&t);
257:
258: int weekDay1=tmOut->tm_wday;
259: if(rus)
260: weekDay1=weekDay1?weekDay1-1:6; //sunday last
261: int monthDays=getMonthDays(year, month);
262:
263: for(int _day=1-weekDay1; _day<=monthDays;) {
1.42.2.7 paf 264: Table::element_type row(new ArrayString(7));
1.34 paf 265: // calculating year week no [1..54]
266: char *weekno_buf;
267: size_t weekno_size;
1.36 paf 268: int weekyear;
1.34 paf 269: // 0..6 week days-cells fill with month days
1.10 parser 270: for(int wday=0; wday<7; wday++, _day++) {
1.42.2.6 paf 271: StringPtr cell(new String);
1.10 parser 272: if(_day>=1 && _day<=monthDays) {
1.42.2.6 paf 273: char *buf=new(pool) char[2+1];
1.10 parser 274: cell->APPEND_CLEAN(buf, sprintf(buf, "%02d", _day),
1.42.2.6 paf 275: method_name->origin().file, method_name->origin().line);
1.10 parser 276: }
1.42.2.6 paf 277: *row+=cell;
1.34 paf 278:
279: if(wday==(rus?3:4)/*thursday*/) {
280: tm tms={0,0,0, _day, month, year-1900};
281: /*normalize*/mktime(&tms);
282:
1.36 paf 283: weekyear=tms.tm_year+1900;
284:
1.35 paf 285: const int weekno_buf_size=2+1/*for stupid snprintfs*/ +1;
1.34 paf 286:
287: // http://www.merlyn.demon.co.uk/weekinfo.htm
288: const int FirstThurs[] = {7,5,4,3,2,7,6,5,4,2,1,7,6,4,3,2,1,6,5,4,3,1,7,6,5,3,2,1};
1.36 paf 289: int n=1 + (tms.tm_yday-(FirstThurs[weekyear % 28]-3))/7;
1.42.2.8! paf 290: weekno_buf=new(pool) char[weekno_buf_size];
1.34 paf 291: weekno_size=snprintf(weekno_buf, weekno_buf_size, "%02d", n);
292: }
293:
1.10 parser 294: }
1.34 paf 295: // appending year week no
296: {
1.42.2.6 paf 297: StringPtr cell(new String);
1.34 paf 298: cell->APPEND_CLEAN(weekno_buf, weekno_size,
1.42.2.6 paf 299: method_name->origin().file, method_name->origin().line);
300: *row+=cell;
1.36 paf 301: }
302: // appending year week year
303: {
1.42.2.6 paf 304: StringPtr cell(new String);
305: char *buf=new(pool) char[4+1];
1.36 paf 306: cell->APPEND_CLEAN(buf, sprintf(buf, "%02d", weekyear),
1.42.2.6 paf 307: method_name->origin().file, method_name->origin().line);
308: *row+=cell;
1.34 paf 309: }
1.42.2.6 paf 310: *result+=row;
1.10 parser 311: }
312:
313: return result;
314: }
315:
1.42.2.6 paf 316: static TablePtr fill_week_days(Request& r,
1.42.2.4 paf 317: StringPtr method_name, MethodParams& params, bool rus){
1.10 parser 318: Pool& pool=r.pool();
1.42.2.6 paf 319: Table::columns_type columns(new Table::columns_type::element_type(4));
320: *columns+=StringPtr(new String("year"));
321: *columns+=StringPtr(new String("month"));
322: *columns+=StringPtr(new String("day"));
323: *columns+=StringPtr(new String("weekday"));
324: TablePtr result(new Table(method_name, columns));
1.10 parser 325:
1.42.2.4 paf 326: int year=params.as_int(1, "year must be int", r);
327: int month=max(1, min(params.as_int(2, "month must be int", r), 12)) -1;
328: int day=params.as_int(3, "day must be int", r);
1.10 parser 329:
330: tm tmIn={0, 0, 18, day, month, year-1900};
331: time_t t=mktime(&tmIn);
332: if(t<0)
1.22 paf 333: throw Exception(0,
1.42.2.6 paf 334: method_name,
1.10 parser 335: "invalid date");
336: tm *tmOut=localtime(&t);
337:
338: int baseWeekDay=tmOut->tm_wday;
339: if(rus)
340: baseWeekDay=baseWeekDay?baseWeekDay-1:6; //sunday last
341:
342: t-=baseWeekDay*SECS_PER_DAY;
343:
344: for(int curWeekDay=0; curWeekDay<7; curWeekDay++, t+=SECS_PER_DAY) {
345: tm *tmOut=localtime(&t);
1.42.2.7 paf 346: Table::element_type row(new ArrayString(4));
1.10 parser 347: #define WDFILL(size, value) { \
1.42.2.6 paf 348: char *buf=new(pool) char[size+1]; \
349: StringPtr cell(new String); \
1.10 parser 350: cell->APPEND_CLEAN(buf, sprintf(buf, "%0"#size"d", value), \
1.42.2.6 paf 351: method_name->origin().file, \
352: method_name->origin().line); \
353: *row+=cell; \
1.10 parser 354: }
355: WDFILL(4, 1900+tmOut->tm_year);
356: WDFILL(2, 1+tmOut->tm_mon);
357: WDFILL(2, tmOut->tm_mday);
358: WDFILL(2, tmOut->tm_wday);
1.42.2.6 paf 359: *result+=row;
1.10 parser 360: }
361:
362: return result;
363: }
364:
1.42.2.4 paf 365: static void _calendar(Request& r, StringPtr method_name, MethodParams& params) {
1.42.2.6 paf 366: StringPtr what=params.as_string(0, "format must be strig");
1.10 parser 367: bool rus=false;
1.42.2.6 paf 368: if(*what=="rus")
1.10 parser 369: rus=true;
1.42.2.6 paf 370: else if(*what=="eng")
1.10 parser 371: rus=false;
372: else
1.22 paf 373: throw Exception("parser.runtime",
1.42.2.6 paf 374: what,
1.10 parser 375: "must be rus|eng");
376:
1.42.2.6 paf 377: TablePtr table;
1.42.2.4 paf 378: if(params.count()==1+2)
1.10 parser 379: table=fill_month_days(r, method_name, params, rus);
380: else // 1+3
381: table=fill_week_days(r, method_name, params, rus);
382:
1.42.2.6 paf 383: r.write_no_lang(ValuePtr(new VTable(table)));
1.10 parser 384: }
385:
1.1 parser 386: // constructor
387:
1.42.2.6 paf 388: MDate::MDate(): Methoded("date") {
1.1 parser 389: // ^now[]
1.23 paf 390: add_native_method("now", Method::CT_DYNAMIC, _now, 0, 1);
1.1 parser 391:
1.30 paf 392: // ^create(float days)
1.17 paf 393: add_native_method("create", Method::CT_DYNAMIC, _create, 1, 6);
1.28 paf 394: // old name for compatibility with <= v1.17 2002/2/18 12:13:42 paf
1.17 paf 395: add_native_method("set", Method::CT_DYNAMIC, _create, 1, 6);
1.1 parser 396:
1.6 parser 397: // ^sql-string[]
1.5 parser 398: add_native_method("sql-string", Method::CT_DYNAMIC, _sql_string, 0, 0);
1.1 parser 399:
400: // ^roll(year|month|day;+/- 1)
401: add_native_method("roll", Method::CT_DYNAMIC, _roll, 2, 2);
1.10 parser 402:
403: // ^date:calendar[month|montheng;year;month] = table
404: // ^date:calendar[week|weekeng;year;month;day] = table
405: add_native_method("calendar", Method::CT_STATIC, _calendar, 3, 4);
1.1 parser 406:
407: }
E-mail: