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