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