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

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

E-mail: