Annotation of parser3/src/types/pa_vdate.h, revision 1.53

1.1       parser      1: /** @file
                      2:        Parser: @b date parser class decl.
                      3: 
1.53    ! moko        4:        Copyright (c) 2001-2012 Art. Lebedev Studio (http://www.artlebedev.com)
1.8       paf         5:        Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)
1.1       parser      6: */
                      7: 
                      8: #ifndef PA_VDATE_H
                      9: #define PA_VDATE_H
1.11      paf        10: 
1.53    ! moko       11: #define IDENT_PA_VDATE_H "$Id: 2012-03-06 07:38:28 $"
1.1       parser     12: 
                     13: #include "classes.h"
                     14: #include "pa_common.h"
                     15: #include "pa_vstateless_object.h"
                     16: #include "pa_vint.h"
                     17: 
1.9       paf        18: // defines
                     19: 
                     20: #define VDATE_TYPE "date"
1.20      paf        21: 
1.9       paf        22: // externs
                     23: 
1.25      paf        24: extern Methoded* date_class;
1.1       parser     25: 
1.27      paf        26: inline void set_tz(const char* tz, char* buf, size_t buf_size) {
1.43      paf        27: #ifndef WIN32
                     28:        if(tz && *tz){
                     29: #endif
                     30:                snprintf(buf, buf_size, "TZ=%s", tz);
                     31:                putenv(buf);
                     32: #ifndef WIN32
                     33:        } else
1.45      misha      34: #ifdef HAVE_UNSETENV
                     35:         unsetenv("TZ");
                     36: #else
1.43      paf        37:                putenv("TZ");
                     38: #endif
1.45      misha      39: #endif
1.27      paf        40:        tzset();
                     41: }
                     42: 
1.1       parser     43: /// value of type 'date'. implemented with @c time_t
1.25      paf        44: class VDate: public VStateless_object {
1.1       parser     45: public: // Value
                     46: 
1.25      paf        47:        override const char* type() const { return VDATE_TYPE; }
                     48:        override VStateless_class *get_class() { return date_class; }
1.10      paf        49:        
1.51      misha      50:        /// VDate: json-string
                     51:        override const String* get_json_string(Json_options* options=0) {
                     52:                String* result=new String();
                     53:                switch(options->date){
                     54:                        case Json_options::D_SQL:
                     55:                                result->append_quoted(get_sql_string());
                     56:                                break;
                     57:                        case Json_options::D_GMT:
                     58:                                result->append_quoted(get_gmt_string());
                     59:                                break;
                     60:                        case Json_options::D_TIMESTAMP:
                     61:                                *result << format((int)ftime, 0);
                     62:                                break;
                     63:                }
                     64:                return result;
                     65:        }
                     66: 
1.1       parser     67:        /// VDate: ftime -> float days
1.28      paf        68:        override Value& as_expr_result(bool /*return_string_as_is=false*/) { return *new VDouble(as_double()); }
1.1       parser     69: 
1.46      misha      70:        /// VDate: true
                     71:        override bool is_evaluated_expr() const { return true; }
                     72: 
1.1       parser     73:        /// VDate: ftime -> float days
1.25      paf        74:        override double as_double() const { return ((double)ftime)/ SECS_PER_DAY; }
1.46      misha      75: 
1.1       parser     76:        /// VDate: 0 or !0
1.25      paf        77:        override bool as_bool() const { return ftime!=0; }
1.1       parser     78: 
1.41      paf        79:        /// @TODO 'static' approach is NOT thread safe!
1.40      paf        80:        tm& get_localtime()
1.35      paf        81:        {
1.41      paf        82:                char saved_tz[MAX_STRING];
1.48      misha      83:                static char saved_tz_pair[MAX_STRING]; // @TODO: this is NOT thread safe!
1.35      paf        84:                static char temp_tz_pair[MAX_STRING];
                     85:                if(ftz_cstr) {
1.41      paf        86:                        if(const char* ltz=getenv("TZ")) {
                     87:                                strncpy(saved_tz, ltz, sizeof(saved_tz)-1);
                     88:                                saved_tz[sizeof(saved_tz)-1]=0;
                     89:                        } else 
                     90:                                saved_tz[0]=0;
                     91:                        
1.35      paf        92:                        ::set_tz(ftz_cstr, temp_tz_pair, sizeof(temp_tz_pair));
                     93:                }
                     94:                tm *result=::localtime(&ftime);
1.38      paf        95:                if(ftz_cstr) {
1.35      paf        96:                        ::set_tz(saved_tz, saved_tz_pair, sizeof(saved_tz_pair));
1.38      paf        97:                }                       
1.35      paf        98:                if(!result)
1.47      misha      99:                        throw Exception(DATE_RANGE_EXCEPTION_TYPE,
1.35      paf       100:                                0,
                    101:                                "invalid datetime (after changing TZ)");
                    102: 
1.40      paf       103:                return *result;
1.35      paf       104:        }
                    105: 
1.52      misha     106:        enum sql_string_type {sql_string_datetime, sql_string_date, sql_string_time};
                    107: 
                    108:        const String* get_sql_string(sql_string_type format = sql_string_datetime) {
                    109:                static const char* formats[]={
                    110:                        "%Y-%m-%d %H:%M:%S",
                    111:                        "%Y-%m-%d",
                    112:                        "%H:%M:%S"
                    113:                };
                    114:                static int sizes[]={
                    115:                        4+1+2+1+2 +1+ 2+1+2+1+2 +1/*terminator*/,
                    116:                        4+1+2+1+2 +1/*terminator*/,
                    117:                        2+1+2+1+2 +1/*terminator*/
                    118:                };
                    119:                size_t size=sizes[format];
1.50      misha     120:                char *buf=new(PointerFreeGC) char[size];
1.52      misha     121:                strftime(buf, size, formats[format], &get_localtime());
                    122: 
1.50      misha     123:                return new String(buf);
                    124:        }
                    125: 
                    126:        const String* get_gmt_string(){
                    127:                return new String(date_gmt_string(gmtime(&ftime)));
                    128:        }
1.1       parser    129: 
1.15      paf       130:        /// VDate: method,field
1.49      misha     131:        override Value* get_element(const String& aname) {
1.15      paf       132:                // $method
1.49      misha     133:                if(Value* result=VStateless_object::get_element(aname))
1.1       parser    134:                        return result;
                    135: 
1.27      paf       136:                // $TZ
                    137:                if(aname=="TZ") 
1.37      paf       138:                        return ftz? new VString(*ftz): new VString();
1.27      paf       139: 
1.1       parser    140:                // $year month day  hour minute second  weekday
1.40      paf       141:                tm& tms=get_localtime();
1.27      paf       142: 
1.1       parser    143:                int result;
1.40      paf       144:                if(aname=="year") result=1900+tms.tm_year;
                    145:                else if(aname=="month") result=1+tms.tm_mon;
                    146:                else if(aname=="day") result=tms.tm_mday;
                    147:                else if(aname=="hour") result=tms.tm_hour;
                    148:                else if(aname=="minute") result=tms.tm_min;
                    149:                else if(aname=="second") result=tms.tm_sec;
                    150:                else if(aname=="weekday") result=tms.tm_wday;
                    151:                else if(aname=="yearday") result=tms.tm_yday;
                    152:                else if(aname=="daylightsaving") result=tms.tm_isdst;
1.44      misha     153:                else if(aname=="week") {
                    154:                        yw week = CalcWeek(tms);
                    155:                        result=week.week;
                    156:                }
                    157:                else if(aname=="weekyear") {
                    158:                        yw week = CalcWeek(tms);
                    159:                        result=1900+week.year;
                    160:                } else { return bark("%s field not found", &aname); }
1.25      paf       161:                return new VInt(result);
1.1       parser    162:        }
                    163: 
                    164: public: // usage
                    165: 
1.27      paf       166:        VDate(time_t adate) : 
                    167:                ftz(0),
                    168:                ftz_cstr(0) {
1.33      paf       169:                set_time(adate);
1.27      paf       170:        }
1.1       parser    171: 
1.32      paf       172:        VDate(tm tmIn) : 
                    173:                ftime(0),
                    174:                ftz(0),
                    175:                ftz_cstr(0) {
                    176:                set_time(tmIn);
                    177:        }
                    178: 
1.19      paf       179:        time_t get_time() const { return ftime; }
1.32      paf       180:        void set_time(time_t atime) { 
                    181:                if(atime<0)
1.47      misha     182:                        throw Exception(DATE_RANGE_EXCEPTION_TYPE,
1.32      paf       183:                                0,
                    184:                                "invalid datetime");
                    185:                ftime=atime; 
                    186:        }
                    187:        void set_time(tm tmIn) { 
                    188:                time_t t=mktime(&tmIn);
                    189:                if(t<0) {
                    190:                        // on some platforms mktime does not fix spring daylightsaving time hole
                    191:                        // in russia -- last sunday of march, 2am->3am hole
                    192:                        // trying to recover:
                    193:                        tmIn.tm_hour--;
                    194:                        t=mktime(&tmIn);
                    195:                }
                    196:                set_time(t);
                    197:        }
1.27      paf       198:        void set_tz(const String* atz) { 
1.30      paf       199:                if((ftz=atz))
1.27      paf       200:                        ftz_cstr=ftz->cstr();
1.40      paf       201:        }
                    202: 
1.44      misha     203:        struct yw {
                    204:                int year;
                    205:                int week;
                    206:        }; 
                    207:        
                    208:        static yw CalcWeek(tm& tms) {
                    209:                yw week = {tms.tm_year, 0};
                    210: 
1.40      paf       211:                // http://www.merlyn.demon.co.uk/weekinfo.htm
1.44      misha     212:                static const unsigned 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};
                    213:                int diff = tms.tm_yday-(FirstThurs[(tms.tm_year+1900) % 28]-4);
                    214:                if (diff < 0){
                    215:                        tms.tm_mday = diff;
1.45      misha     216:                        mktime(&tms); // normalize
1.44      misha     217:                        week = CalcWeek(tms);
                    218:                } else {
                    219:                        week.week = 1 + diff/7;
                    220:                        if ( week.week > 52 && ISOWeekCount(week.year) < week.week ){
                    221:                                week.year++;
                    222:                                week.week = 1;
                    223:                        }
                    224:        }
                    225:                return week;
                    226:        }
                    227: 
                    228:        static int ISOWeekCount (int year) {
                    229:                static const unsigned int YearWeeks[] = {
                    230:                        52,52,52,52,53, 52,52,52,52,52,
                    231:                        53,52,52,52,52, 52,53,52,52,52,
                    232:                        52,53,52,52,52, 52,52,53
                    233:                };
                    234:                return YearWeeks[(year+1900) % 28];
1.27      paf       235:        }
1.1       parser    236: 
                    237: private:
                    238:        time_t ftime;
1.27      paf       239:        const String* ftz;
                    240:        const char* ftz_cstr;
1.1       parser    241: 
                    242: };
                    243: 
                    244: #endif

E-mail: