Annotation of parser3/src/types/pa_vdate.C, revision 1.3

1.1       moko        1: /** @file
                      2:        Parser: @b date parser class.
                      3: 
                      4:        Copyright (c) 2001-2012 Art. Lebedev Studio (http://www.artlebedev.com)
                      5: */
                      6: 
                      7: #include "pa_vstateless_object.h"
                      8: #include "pa_vdate.h"
                      9: #include "pa_vint.h"
                     10: #include "pa_vstring.h"
                     11: 
1.3     ! moko       12: volatile const char * IDENT_PA_PA_VDATE_C="$Id: pa_vdate.C,v 1.2 2015/08/15 22:51:17 moko Exp $" IDENT_PA_VDATE_H;
1.1       moko       13: 
1.3     ! moko       14: #define ZERO_DATE (-62169984000-SECS_PER_DAY) // '0000-00-00 00:00:00' - 1 day
        !            15: #define MAX_DATE (253402300799+SECS_PER_DAY) // '9999-12-31 23:59:59' + 1 day
        !            16: 
        !            17: #define MAX_32_DATE 2147483647
        !            18: 
        !            19: static const int DAYS_IN_MONTH[12] =
        !            20: {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
        !            21: 
        !            22: static const int DAYS_BEFORE_MONTH[12] =
        !            23: {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
        !            24: 
        !            25: #define IS_LEAP(y) (((y) % 4) == 0 && (((y) % 100) != 0 || (((y)+1900) % 400) == 0))
        !            26: 
        !            27: int VDate::getMonthDays(int year, int month) {
        !            28:        return (month == 1 /* january -- 0 */ && IS_LEAP(year)) ? 29 : DAYS_IN_MONTH[month];
        !            29: }
        !            30: 
        !            31: static void pa_gmtime(pa_time_t lcltime, struct tm *res);
        !            32: static pa_time_t pa_mktime(struct tm *tim_p);
        !            33: 
        !            34: static int gmt_offset() {
        !            35: #if defined(HAVE_TIMEZONE)
        !            36:        tzset();
        !            37:        return timezone;
        !            38: #else
        !            39:        time_t t=time(0);
        !            40:        tm *tm=localtime(&t);
        !            41: #if defined(HAVE_TM_GMTOFF)
        !            42:        return -tm->tm_gmtoff;
        !            43: #elif defined(HAVE_TM_TZADJ)
        !            44:        return tm->tm_tzadj;
        !            45: #else
        !            46: #error neither HAVE_TIMEZONE nor HAVE_TM_GMTOFF nor HAVE_TM_TZADJ defined
        !            47: #endif
        !            48: #endif
        !            49: }
        !            50: 
        !            51: static void pa_set_tz(const char* ntz) {
        !            52:        if(ntz && *ntz){
        !            53:                static char temp_tz_pair[MAX_STRING];
        !            54:                snprintf(temp_tz_pair, sizeof(temp_tz_pair), "TZ=%s", ntz);
        !            55:                putenv(temp_tz_pair);
        !            56:        } else {
1.1       moko       57: #ifdef HAVE_UNSETENV
1.3     ! moko       58:                unsetenv("TZ");
1.1       moko       59: #else
1.3     ! moko       60:                putenv("TZ=");
1.1       moko       61: #endif
1.3     ! moko       62:        }
1.1       moko       63: }
                     64: 
1.3     ! moko       65: ///    Auto-object used for temporarily substituting/removing timezone variable
        !            66: class Temp_tz {
        !            67:        const char* ntz;
        !            68:        char saved_tz[MAX_STRING];
        !            69: public:
        !            70:        static const char *default_tz;
        !            71: public:
        !            72:        Temp_tz(const char *atz) : ntz(atz) {
        !            73:                if(!ntz)
        !            74:                        ntz=default_tz;
        !            75:                if(!ntz)
        !            76:                        return;
        !            77:                if(const char* ctz=getenv("TZ")){
        !            78:                        strncpy(saved_tz, ctz, sizeof(saved_tz)-1);
        !            79:                } else
        !            80:                        saved_tz[0]=0;
        !            81:                pa_set_tz(ntz);
        !            82:        }
        !            83:        ~Temp_tz() {
        !            84:                if(ntz)
        !            85:                        pa_set_tz(saved_tz);
        !            86:        }
        !            87: };
        !            88: 
        !            89: const char *Temp_tz::default_tz=0;
1.1       moko       90: 
1.3     ! moko       91: static tm pa_localtime(const char *tz, pa_time_t atime, struct tm &tmIn) {
        !            92:        Temp_tz temp_tz(tz);
        !            93: #ifdef PA_DATE64
        !            94:        tmIn=*localtime(&atime);
        !            95: #else
        !            96:        if(atime >= 0 && atime <= MAX_32_DATE){
        !            97:                time_t itime=atime;
        !            98:                tmIn=*localtime(&itime);
        !            99:        } else {
        !           100:                pa_gmtime(atime-gmt_offset(), &tmIn);
        !           101:        }
        !           102: #endif
1.1       moko      103: }
                    104: 
1.3     ! moko      105: static pa_time_t pa_mktime(const char *tz, struct tm &tmIn) {
        !           106:        Temp_tz temp_tz(tz);
        !           107: #ifdef PA_DATE64
        !           108:        return mktime(&tmIn);
        !           109: #else
        !           110:        time_t result=mktime(&tmIn);
        !           111:        if(result != -1)
        !           112:                return result;
        !           113:        return pa_mktime(&tmIn)+gmt_offset();
        !           114: #endif
1.1       moko      115: }
                    116: 
1.3     ! moko      117: const String* VDate::get_sql_string(sql_string_type format) {
        !           118:        switch(format){
        !           119:                case sql_string_datetime:{
        !           120:                        static const char *format="%.4d-%.2d-%.2d %.2d:%.2d:%.2d";
        !           121:                        static int size=4+1+2+1+2 +1+ 2+1+2+1+2 +1/*zero-teminator*/+1/*for faulty snprintfs*/;
        !           122:                        char *buf=new(PointerFreeGC) char[size];
        !           123:                        snprintf(buf, size, format, ftm.tm_year+1900, ftm.tm_mon+1, ftm.tm_mday, ftm.tm_hour, ftm.tm_min, ftm.tm_sec);
        !           124:                        return new String(buf);
        !           125:                }
        !           126:                case sql_string_date:{
        !           127:                        static const char *format="%.4d-%.2d-%.2d";
        !           128:                        static int size=4+1+2+1+2 +1/*zero-teminator*/+1/*for faulty snprintfs*/;
        !           129:                        char *buf=new(PointerFreeGC) char[size];
        !           130:                        snprintf(buf, size, format, ftm.tm_year+1900, ftm.tm_mon+1, ftm.tm_mday);
        !           131:                        return new String(buf);
        !           132:                }
        !           133:                case sql_string_time:{
        !           134:                        static const char *format="%.2d:%.2d:%.2d";
        !           135:                        static int size=2+1+2+1+2 +1/*zero-teminator*/+1/*for faulty snprintfs*/;
        !           136:                        char *buf=new(PointerFreeGC) char[size];
        !           137:                        snprintf(buf, size, format, ftm.tm_hour, ftm.tm_min, ftm.tm_sec);
        !           138:                        return new String(buf);
        !           139:                }
1.1       moko      140:        }
1.3     ! moko      141: }
        !           142: 
        !           143: 
        !           144: const String* VDate::get_gmt_string() {
        !           145:        struct tm tms;
        !           146: #ifdef PA_DATE64
        !           147:        tms=*gmtime(&ftime);
        !           148: #else
        !           149:        pa_gmtime(ftime, &tms);
        !           150: #endif
        !           151: 
        !           152:        /// http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3
        !           153:        static const char month_names[12][4]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
        !           154:        static const char days[7][4]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
1.1       moko      155: 
1.3     ! moko      156:        static int size=3+1+1+2+1+3+1+4+1+2+1+2+1+2+4 +1/*zero-teminator*/+1/*for faulty snprintfs*/;
        !           157:        char *buf=new(PointerFreeGC) char[size];
        !           158:        snprintf(buf, size, "%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT",
        !           159:                days[tms.tm_wday], tms.tm_mday, month_names[tms.tm_mon], tms.tm_year+1900, tms.tm_hour, tms.tm_min, tms.tm_sec);
        !           160:        return new String(buf);
1.1       moko      161: }
                    162: 
                    163: override Value* VDate::get_element(const String& aname) {
                    164:        // $method
                    165:        if(Value* result=VStateless_object::get_element(aname))
                    166:                return result;
                    167: 
                    168:        // $TZ
1.3     ! moko      169:        if(aname=="TZ")
        !           170:                return ftz_cstr ? new VString(*new String(ftz_cstr)): new VString();
1.1       moko      171: 
                    172:        int result;
1.3     ! moko      173:        if(aname=="year") result=1900+ftm.tm_year;
        !           174:        else if(aname=="month") result=1+ftm.tm_mon;
        !           175:        else if(aname=="day") result=ftm.tm_mday;
        !           176:        else if(aname=="hour") result=ftm.tm_hour;
        !           177:        else if(aname=="minute") result=ftm.tm_min;
        !           178:        else if(aname=="second") result=ftm.tm_sec;
        !           179:        else if(aname=="weekday") result=ftm.tm_wday;
        !           180:        else if(aname=="yearday") result=ftm.tm_yday;
        !           181:        else if(aname=="daylightsaving") result=ftm.tm_isdst;
1.1       moko      182:        else if(aname=="week") {
1.3     ! moko      183:                yw week = CalcWeek(ftm);
1.1       moko      184:                result=week.week;
                    185:        }
                    186:        else if(aname=="weekyear") {
1.3     ! moko      187:                yw week = CalcWeek(ftm);
1.1       moko      188:                result=1900+week.year;
                    189:        } else { return bark("%s field not found", &aname); }
                    190:        return new VInt(result);
                    191: }
                    192: 
                    193: const String* VDate::get_json_string(Json_options& options) {
                    194:        String* result=new String();
                    195:        switch(options.date){
                    196:                case Json_options::D_SQL:
                    197:                        result->append_quoted(get_sql_string());
                    198:                        break;
                    199:                case Json_options::D_GMT:
                    200:                        result->append_quoted(get_gmt_string());
                    201:                        break;
                    202:                case Json_options::D_TIMESTAMP:
                    203:                        *result << format((int)ftime, 0);
                    204:                        break;
                    205:        }
                    206:        return result;
                    207: }
1.2       moko      208: 
1.3     ! moko      209: void VDate::validate() {
        !           210:        if((ftm.tm_year==-1901) && (ftm.tm_mon==10) && (ftm.tm_mday==30)){
        !           211:                ftm.tm_year=-1900;
        !           212:                ftm.tm_mon=-1;
        !           213:                ftm.tm_mday=0;
        !           214:        }
        !           215:        if((ftm.tm_year+1900)<0 || (ftm.tm_year+1900)>9999){
        !           216:                throw Exception(DATE_RANGE_EXCEPTION_TYPE, 0, "year '%d' is out of range 0..9999", ftm.tm_year+1900);
        !           217:        }
        !           218: }
        !           219: 
        !           220: void VDate::set_time(pa_time_t atime) {
1.2       moko      221:        if(atime==-1)
                    222:                throw Exception(DATE_RANGE_EXCEPTION_TYPE, 0, "invalid datetime");
1.3     ! moko      223:        if(atime<ZERO_DATE || atime>MAX_DATE)
        !           224:                throw Exception(DATE_RANGE_EXCEPTION_TYPE, 0, "unix time %.15g is out of range 0..9999 year", (double)atime);
1.2       moko      225:        ftime=atime;
1.3     ! moko      226:        pa_localtime(ftz_cstr, ftime, ftm);
        !           227:        validate();
        !           228: }
        !           229: 
        !           230: void VDate::set_tm(tm &tmIn) {
        !           231:        ftime=pa_mktime(ftz_cstr, tmIn);
        !           232:        ftm=tmIn;
        !           233:        if(ftime==-1)
        !           234:                throw Exception(DATE_RANGE_EXCEPTION_TYPE, 0, "invalid datetime '%04d-%02d-%02d'", tmIn.tm_year+1900, tmIn.tm_mon+1, tmIn.tm_mday);
        !           235:        validate();
1.2       moko      236: }
                    237: 
1.3     ! moko      238: void VDate::set_tz(const String* atz) {
        !           239:        ftz_cstr=atz && !atz->is_empty() ? atz->cstr() : 0;
        !           240:        pa_localtime(ftz_cstr, ftime, ftm);
        !           241:        validate();
        !           242: }
        !           243: 
        !           244: void VDate::set_default_tz(const String* atz) {
        !           245:        Temp_tz::default_tz=atz && !atz->is_empty() ? atz->cstr() : 0;
1.2       moko      246: }
                    247: 
                    248: static int ISOWeekCount (int year) {
                    249:        static const unsigned int YearWeeks[] = {
                    250:                52,52,52,52,53, 52,52,52,52,52,
                    251:                53,52,52,52,52, 52,53,52,52,52,
                    252:                52,53,52,52,52, 52,52,53
                    253:        };
                    254:        return YearWeeks[(year+1900) % 28];
                    255: }
                    256: 
                    257: VDate::yw VDate::CalcWeek(tm& tms) {
                    258:        yw week = {tms.tm_year, 0};
                    259: 
                    260:        // http://www.merlyn.demon.co.uk/weekinfo.htm
                    261:        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};
                    262:        int diff = tms.tm_yday-(FirstThurs[(tms.tm_year+1900) % 28]-4);
                    263:        if (diff < 0){
                    264:                tms.tm_mday = diff;
1.3     ! moko      265:                pa_mktime(0, tms); // normalize
1.2       moko      266:                week = CalcWeek(tms);
                    267:        } else {
                    268:                week.week = 1 + diff/7;
                    269:                if ( week.week > 52 && ISOWeekCount(week.year) < week.week ){
                    270:                        week.year++;
                    271:                        week.week = 1;
                    272:                }
                    273:        }
                    274:        return week;
                    275: }
1.3     ! moko      276: 
        !           277: #ifndef PA_DATE64
        !           278: 
        !           279: /*
        !           280:  * gmtime_r.c
        !           281:  * Original Author: Adapted from tzcode maintained by Arthur David Olson.
        !           282:  * Modifications:
        !           283:  * - Changed to mktm_r and added __tzcalc_limits - 04/10/02, Jeff Johnston
        !           284:  * - Fixed bug in mday computations - 08/12/04, Alex Mogilnikov <alx@intellectronika.ru>
        !           285:  * - Fixed bug in __tzcalc_limits - 08/12/04, Alex Mogilnikov <alx@intellectronika.ru>
        !           286:  * - Move code from _mktm_r() to gmtime_r() - 05/09/14, Freddie Chopin <freddie_chopin@op.pl>
        !           287:  * - Fixed bug in calculations for dates after year 2069 or before year 1901. Ideas for
        !           288:  *   solution taken from musl's __secs_to_tm() - 07/12/2014, Freddie Chopin
        !           289:  *   <freddie_chopin@op.pl>
        !           290:  * - Use faster algorithm from civil_from_days() by Howard Hinnant - 12/06/2014,
        !           291:  * Freddie Chopin <freddie_chopin@op.pl>
        !           292:  *
        !           293:  * Converts the calendar time pointed to by tim_p into a broken-down time
        !           294:  * expressed as local time. Returns a pointer to a structure containing the
        !           295:  * broken-down time.
        !           296:  */
        !           297: 
        !           298: /* Move epoch from 01.01.1970 to 01.03.0000 (yes, Year 0) - this is the first
        !           299:  * day of a 400-year long "era", right after additional day of leap year.
        !           300:  * This adjustment is required only for date calculation, so instead of
        !           301:  * modifying time_t value (which would require 64-bit operations to work
        !           302:  * correctly) it's enough to adjust the calculated number of days since epoch.
        !           303:  */
        !           304: 
        !           305: #define SECS_PER_HOUR 3600
        !           306: #define SECS_PER_MIN 60
        !           307: #define DAYS_PER_WEEK 7
        !           308: #define YEAR_BASE 1900
        !           309: 
        !           310: #define EPOCH_ADJUSTMENT_DAYS  719468L
        !           311: /* year to which the adjustment was made */
        !           312: #define ADJUSTED_EPOCH_YEAR    0
        !           313: /* 1st March of year 0 is Wednesday */
        !           314: #define ADJUSTED_EPOCH_WDAY    3
        !           315: /* there are 97 leap years in 400-year periods. ((400 - 97) * 365 + 97 * 366) */
        !           316: #define DAYS_PER_ERA           146097L
        !           317: /* there are 24 leap years in 100-year periods. ((100 - 24) * 365 + 24 * 366) */
        !           318: #define DAYS_PER_CENTURY       36524L
        !           319: /* there is one leap year every 4 years */
        !           320: #define DAYS_PER_4_YEARS       (3 * 365 + 366)
        !           321: /* number of days in a non-leap year */
        !           322: #define DAYS_PER_YEAR          365
        !           323: /* number of days in January */
        !           324: #define DAYS_IN_JANUARY                31
        !           325: /* number of days in non-leap February */
        !           326: #define DAYS_IN_FEBRUARY       28
        !           327: /* number of years per era */
        !           328: #define YEARS_PER_ERA          400
        !           329: 
        !           330: static void pa_gmtime(pa_time_t lcltime, struct tm *res) {
        !           331:   long days, rem;
        !           332:   int era, weekday, year;
        !           333:   unsigned erayear, yearday, month, day;
        !           334:   unsigned long eraday;
        !           335: 
        !           336:   days = lcltime / SECS_PER_DAY;
        !           337:   rem = lcltime - (pa_time_t)days * SECS_PER_DAY;
        !           338:   days += EPOCH_ADJUSTMENT_DAYS;
        !           339:   if (rem < 0)
        !           340:     {
        !           341:       rem += SECS_PER_DAY;
        !           342:       --days;
        !           343:     }
        !           344: 
        !           345:   /* compute hour, min, and sec */
        !           346:   res->tm_hour = (int) (rem / SECS_PER_HOUR);
        !           347:   rem %= SECS_PER_HOUR;
        !           348:   res->tm_min = (int) (rem / SECS_PER_MIN);
        !           349:   res->tm_sec = (int) (rem % SECS_PER_MIN);
        !           350: 
        !           351:   /* compute day of week */
        !           352:   if ((weekday = ((ADJUSTED_EPOCH_WDAY + days) % DAYS_PER_WEEK)) < 0)
        !           353:     weekday += DAYS_PER_WEEK;
        !           354:   res->tm_wday = weekday;
        !           355: 
        !           356:   /* compute year, month, day & day of year */
        !           357:   /* for description of this algorithm see
        !           358:    * http://howardhinnant.github.io/date_algorithms.html#civil_from_days */
        !           359:   era = (days >= 0 ? days : days - (DAYS_PER_ERA - 1)) / DAYS_PER_ERA;
        !           360:   eraday = days - era * DAYS_PER_ERA;  /* [0, 146096] */
        !           361:   erayear = (eraday - eraday / (DAYS_PER_4_YEARS - 1) + eraday / DAYS_PER_CENTURY -
        !           362:       eraday / (DAYS_PER_ERA - 1)) / 365;      /* [0, 399] */
        !           363:   yearday = eraday - (DAYS_PER_YEAR * erayear + erayear / 4 - erayear / 100);  /* [0, 365] */
        !           364:   month = (5 * yearday + 2) / 153;     /* [0, 11] */
        !           365:   day = yearday - (153 * month + 2) / 5 + 1;   /* [1, 31] */
        !           366:   month += month < 10 ? 2 : -10;
        !           367:   year = ADJUSTED_EPOCH_YEAR + erayear + era * YEARS_PER_ERA + (month <= 1);
        !           368: 
        !           369:   res->tm_yday = yearday >= DAYS_PER_YEAR - DAYS_IN_JANUARY - DAYS_IN_FEBRUARY ?
        !           370:       yearday - (DAYS_PER_YEAR - DAYS_IN_JANUARY - DAYS_IN_FEBRUARY) :
        !           371:       yearday + DAYS_IN_JANUARY + DAYS_IN_FEBRUARY + IS_LEAP(erayear);
        !           372:   res->tm_year = year - YEAR_BASE;
        !           373:   res->tm_mon = month;
        !           374:   res->tm_mday = day;
        !           375: 
        !           376:   res->tm_isdst = 0;
        !           377: }
        !           378: 
        !           379: 
        !           380: /*
        !           381:  * mktime.c
        !           382:  * Original Author:    G. Haley
        !           383:  *
        !           384:  * Converts the broken-down time, expressed as local time, in the structure
        !           385:  * pointed to by tim_p into a calendar time value. The original values of the
        !           386:  * tm_wday and tm_yday fields of the structure are ignored, and the original
        !           387:  * values of the other fields have no restrictions. On successful completion
        !           388:  * the fields of the structure are set to represent the specified calendar
        !           389:  * time. Returns the specified calendar time. If the calendar time can not be
        !           390:  * represented, returns the value (time_t) -1.
        !           391:  */
        !           392: 
        !           393: #define _DAYS_IN_MONTH(x) ((x == 1) ? days_in_feb : DAYS_IN_MONTH[x])
        !           394: #define _DAYS_IN_YEAR(year) (IS_LEAP(year) ? 366 : 365)
        !           395: 
        !           396: static void validate_structure(struct tm *tim_p) {
        !           397:   div_t res;
        !           398:   int days_in_feb = 28;
        !           399: 
        !           400:   /* calculate time & date to account for out of range values */
        !           401:   if (tim_p->tm_sec < 0 || tim_p->tm_sec > 59)
        !           402:     {
        !           403:       res = div (tim_p->tm_sec, 60);
        !           404:       tim_p->tm_min += res.quot;
        !           405:       if ((tim_p->tm_sec = res.rem) < 0)
        !           406:        {
        !           407:          tim_p->tm_sec += 60;
        !           408:          --tim_p->tm_min;
        !           409:        }
        !           410:     }
        !           411: 
        !           412:   if (tim_p->tm_min < 0 || tim_p->tm_min > 59)
        !           413:     {
        !           414:       res = div (tim_p->tm_min, 60);
        !           415:       tim_p->tm_hour += res.quot;
        !           416:       if ((tim_p->tm_min = res.rem) < 0)
        !           417:        {
        !           418:          tim_p->tm_min += 60;
        !           419:          --tim_p->tm_hour;
        !           420:         }
        !           421:     }
        !           422: 
        !           423:   if (tim_p->tm_hour < 0 || tim_p->tm_hour > 23)
        !           424:     {
        !           425:       res = div (tim_p->tm_hour, 24);
        !           426:       tim_p->tm_mday += res.quot;
        !           427:       if ((tim_p->tm_hour = res.rem) < 0)
        !           428:        {
        !           429:          tim_p->tm_hour += 24;
        !           430:          --tim_p->tm_mday;
        !           431:         }
        !           432:     }
        !           433: 
        !           434:   if (tim_p->tm_mon < 0 || tim_p->tm_mon > 11)
        !           435:     {
        !           436:       res = div (tim_p->tm_mon, 12);
        !           437:       tim_p->tm_year += res.quot;
        !           438:       if ((tim_p->tm_mon = res.rem) < 0)
        !           439:         {
        !           440:          tim_p->tm_mon += 12;
        !           441:          --tim_p->tm_year;
        !           442:         }
        !           443:     }
        !           444: 
        !           445:   if (_DAYS_IN_YEAR (tim_p->tm_year) == 366)
        !           446:     days_in_feb = 29;
        !           447: 
        !           448:   if (tim_p->tm_mday <= 0)
        !           449:     {
        !           450:       while (tim_p->tm_mday <= 0)
        !           451:        {
        !           452:          if (--tim_p->tm_mon == -1)
        !           453:            {
        !           454:              tim_p->tm_year--;
        !           455:              tim_p->tm_mon = 11;
        !           456:              days_in_feb =
        !           457:                ((_DAYS_IN_YEAR (tim_p->tm_year) == 366) ?
        !           458:                 29 : 28);
        !           459:            }
        !           460:          tim_p->tm_mday += _DAYS_IN_MONTH (tim_p->tm_mon);
        !           461:        }
        !           462:     }
        !           463:   else
        !           464:     {
        !           465:       while (tim_p->tm_mday > _DAYS_IN_MONTH (tim_p->tm_mon))
        !           466:        {
        !           467:          tim_p->tm_mday -= _DAYS_IN_MONTH (tim_p->tm_mon);
        !           468:          if (++tim_p->tm_mon == 12)
        !           469:            {
        !           470:              tim_p->tm_year++;
        !           471:              tim_p->tm_mon = 0;
        !           472:              days_in_feb =
        !           473:                ((_DAYS_IN_YEAR (tim_p->tm_year) == 366) ?
        !           474:                 29 : 28);
        !           475:            }
        !           476:        }
        !           477:     }
        !           478: }
        !           479: 
        !           480: static pa_time_t pa_mktime(struct tm *tim_p) {
        !           481:   pa_time_t tim = 0;
        !           482:   long days = 0;
        !           483:   int year;
        !           484: 
        !           485:   /* validate structure */
        !           486:   validate_structure (tim_p);
        !           487: 
        !           488:   /* compute hours, minutes, seconds */
        !           489:   tim += tim_p->tm_sec + (tim_p->tm_min * SECS_PER_MIN) +
        !           490:     (tim_p->tm_hour * SECS_PER_HOUR);
        !           491: 
        !           492:   /* compute days in year */
        !           493:   days += tim_p->tm_mday - 1;
        !           494:   days += DAYS_BEFORE_MONTH[tim_p->tm_mon];
        !           495:   if (tim_p->tm_mon > 1 && _DAYS_IN_YEAR (tim_p->tm_year) == 366)
        !           496:     days++;
        !           497: 
        !           498:   /* compute day of the year */
        !           499:   tim_p->tm_yday = days;
        !           500: 
        !           501:   if (tim_p->tm_year > 10000 || tim_p->tm_year < -10000)
        !           502:       return (time_t) -1;
        !           503: 
        !           504:   /* compute days in other years */
        !           505:   if ((year = tim_p->tm_year) > 70)
        !           506:     {
        !           507:       for (year = 70; year < tim_p->tm_year; year++)
        !           508:        days += _DAYS_IN_YEAR (year);
        !           509:     }
        !           510:   else if (year < 70)
        !           511:     {
        !           512:       for (year = 69; year > tim_p->tm_year; year--)
        !           513:        days -= _DAYS_IN_YEAR (year);
        !           514:       days -= _DAYS_IN_YEAR (year);
        !           515:     }
        !           516: 
        !           517:   /* compute total seconds */
        !           518:   tim += (pa_time_t)days * SECS_PER_DAY;
        !           519: 
        !           520:   /* compute day of the week */
        !           521:   if ((tim_p->tm_wday = (days + 4) % 7) < 0)
        !           522:     tim_p->tm_wday += 7;
        !           523: 
        !           524:   return tim;
        !           525: }
        !           526: 
        !           527: #endif

E-mail: