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