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