Annotation of parser3/src/types/pa_vdate.C, revision 1.6
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.6 ! moko 12: volatile const char * IDENT_PA_PA_VDATE_C="$Id: pa_vdate.C,v 1.5 2015/09/04 10:40:26 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: #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();
1.6 ! moko 37: return -timezone;
1.3 moko 38: #else
39: time_t t=time(0);
40: tm *tm=localtime(&t);
41: #if defined(HAVE_TM_GMTOFF)
1.6 ! moko 42: return tm->tm_gmtoff;
1.3 moko 43: #elif defined(HAVE_TM_TZADJ)
1.6 ! moko 44: return -tm->tm_tzadj;
1.3 moko 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){
1.5 moko 97: time_t itime=(time_t)atime;
1.3 moko 98: tmIn=*localtime(&itime);
99: } else {
1.6 ! moko 100: pa_gmtime(atime+gmt_offset(), &tmIn);
1.3 moko 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;
1.6 ! moko 113: return pa_mktime(&tmIn)-gmt_offset();
1.3 moko 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() {
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.6 ! moko 162: const String* VDate::get_iso_string() {
! 163: Temp_tz temp_tz(ftz_cstr);
! 164: /// http://www.w3.org/TR/NOTE-datetime
! 165: if(int offset=gmt_offset()){
! 166: char sign=offset>0 ? '+':'-';
! 167: offset=abs(offset);
! 168: static const char *format="%.4d-%.2d-%.2dT%.2d:%.2d:%.2d%c%d:%.2d";
! 169: static int size=4+1+2+1+2 +1+ 2+1+2+1+2 +1+2+1+2 +1/*zero-teminator*/+1/*for faulty snprintfs*/;
! 170: char *buf=new(PointerFreeGC) char[size];
! 171: snprintf(buf, size, format, ftm.tm_year+1900, ftm.tm_mon+1, ftm.tm_mday, ftm.tm_hour, ftm.tm_min, ftm.tm_sec,
! 172: sign, offset/3600, (offset/60)%60);
! 173: return new String(buf);
! 174: } else {
! 175: static const char *format="%.4d-%.2d-%.2dT%.2d:%.2d:%.2dZ";
! 176: static int size=4+1+2+1+2 +1+ 2+1+2+1+2 +1 +1/*zero-teminator*/+1/*for faulty snprintfs*/;
! 177: char *buf=new(PointerFreeGC) char[size];
! 178: snprintf(buf, size, format, ftm.tm_year+1900, ftm.tm_mon+1, ftm.tm_mday, ftm.tm_hour, ftm.tm_min, ftm.tm_sec);
! 179: return new String(buf);
! 180: }
! 181: }
! 182:
1.1 moko 183: override Value* VDate::get_element(const String& aname) {
184: // $method
185: if(Value* result=VStateless_object::get_element(aname))
186: return result;
187:
188: // $TZ
1.3 moko 189: if(aname=="TZ")
190: return ftz_cstr ? new VString(*new String(ftz_cstr)): new VString();
1.1 moko 191:
192: int result;
1.3 moko 193: if(aname=="year") result=1900+ftm.tm_year;
194: else if(aname=="month") result=1+ftm.tm_mon;
195: else if(aname=="day") result=ftm.tm_mday;
196: else if(aname=="hour") result=ftm.tm_hour;
197: else if(aname=="minute") result=ftm.tm_min;
198: else if(aname=="second") result=ftm.tm_sec;
199: else if(aname=="weekday") result=ftm.tm_wday;
200: else if(aname=="yearday") result=ftm.tm_yday;
201: else if(aname=="daylightsaving") result=ftm.tm_isdst;
1.1 moko 202: else if(aname=="week") {
1.3 moko 203: yw week = CalcWeek(ftm);
1.1 moko 204: result=week.week;
205: }
206: else if(aname=="weekyear") {
1.3 moko 207: yw week = CalcWeek(ftm);
1.1 moko 208: result=1900+week.year;
209: } else { return bark("%s field not found", &aname); }
210: return new VInt(result);
211: }
212:
213: const String* VDate::get_json_string(Json_options& options) {
214: String* result=new String();
215: switch(options.date){
216: case Json_options::D_SQL:
217: result->append_quoted(get_sql_string());
218: break;
219: case Json_options::D_GMT:
220: result->append_quoted(get_gmt_string());
221: break;
1.6 ! moko 222: case Json_options::D_ISO:
! 223: result->append_quoted(get_iso_string());
! 224: break;
1.1 moko 225: case Json_options::D_TIMESTAMP:
226: *result << format((int)ftime, 0);
227: break;
228: }
229: return result;
230: }
1.2 moko 231:
1.3 moko 232: void VDate::validate() {
233: if((ftm.tm_year==-1901) && (ftm.tm_mon==10) && (ftm.tm_mday==30)){
234: ftm.tm_year=-1900;
235: ftm.tm_mon=-1;
236: ftm.tm_mday=0;
237: }
238: if((ftm.tm_year+1900)<0 || (ftm.tm_year+1900)>9999){
239: throw Exception(DATE_RANGE_EXCEPTION_TYPE, 0, "year '%d' is out of range 0..9999", ftm.tm_year+1900);
240: }
241: }
242:
243: void VDate::set_time(pa_time_t atime) {
1.2 moko 244: if(atime==-1)
245: throw Exception(DATE_RANGE_EXCEPTION_TYPE, 0, "invalid datetime");
1.3 moko 246: if(atime<ZERO_DATE || atime>MAX_DATE)
247: throw Exception(DATE_RANGE_EXCEPTION_TYPE, 0, "unix time %.15g is out of range 0..9999 year", (double)atime);
1.2 moko 248: ftime=atime;
1.3 moko 249: pa_localtime(ftz_cstr, ftime, ftm);
250: validate();
251: }
252:
253: void VDate::set_tm(tm &tmIn) {
1.4 moko 254: pa_time_t atime=pa_mktime(ftz_cstr, tmIn);
255: if(atime==-1)
256: throw Exception(DATE_RANGE_EXCEPTION_TYPE, 0, "invalid datetime '%04d-%02d-%02d'", tmIn.tm_year+1900, tmIn.tm_mon+1, tmIn.tm_mday);
257: ftime=atime;
1.3 moko 258: ftm=tmIn;
259: validate();
1.2 moko 260: }
261:
1.6 ! moko 262: void VDate::set_tz(const char* atz) {
! 263: ftz_cstr=atz && atz[0] ? atz : 0; // ftm should be updated afterwards
1.3 moko 264: }
265:
1.6 ! moko 266: void VDate::set_default_tz(const char* atz) {
! 267: Temp_tz::default_tz=atz && atz[0] ? atz : 0;
1.2 moko 268: }
269:
270: static int ISOWeekCount (int year) {
271: static const unsigned int YearWeeks[] = {
272: 52,52,52,52,53, 52,52,52,52,52,
273: 53,52,52,52,52, 52,53,52,52,52,
274: 52,53,52,52,52, 52,52,53
275: };
276: return YearWeeks[(year+1900) % 28];
277: }
278:
279: VDate::yw VDate::CalcWeek(tm& tms) {
280: yw week = {tms.tm_year, 0};
281:
282: // http://www.merlyn.demon.co.uk/weekinfo.htm
283: 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};
284: int diff = tms.tm_yday-(FirstThurs[(tms.tm_year+1900) % 28]-4);
285: if (diff < 0){
286: tms.tm_mday = diff;
1.3 moko 287: pa_mktime(0, tms); // normalize
1.2 moko 288: week = CalcWeek(tms);
289: } else {
290: week.week = 1 + diff/7;
291: if ( week.week > 52 && ISOWeekCount(week.year) < week.week ){
292: week.year++;
293: week.week = 1;
294: }
295: }
296: return week;
297: }
1.3 moko 298:
299: #ifndef PA_DATE64
300:
301: /*
302: * gmtime_r.c
303: * Original Author: Adapted from tzcode maintained by Arthur David Olson.
304: * Modifications:
305: * - Changed to mktm_r and added __tzcalc_limits - 04/10/02, Jeff Johnston
306: * - Fixed bug in mday computations - 08/12/04, Alex Mogilnikov <alx@intellectronika.ru>
307: * - Fixed bug in __tzcalc_limits - 08/12/04, Alex Mogilnikov <alx@intellectronika.ru>
308: * - Move code from _mktm_r() to gmtime_r() - 05/09/14, Freddie Chopin <freddie_chopin@op.pl>
309: * - Fixed bug in calculations for dates after year 2069 or before year 1901. Ideas for
310: * solution taken from musl's __secs_to_tm() - 07/12/2014, Freddie Chopin
311: * <freddie_chopin@op.pl>
312: * - Use faster algorithm from civil_from_days() by Howard Hinnant - 12/06/2014,
313: * Freddie Chopin <freddie_chopin@op.pl>
314: *
315: * Converts the calendar time pointed to by tim_p into a broken-down time
316: * expressed as local time. Returns a pointer to a structure containing the
317: * broken-down time.
318: */
319:
320: /* Move epoch from 01.01.1970 to 01.03.0000 (yes, Year 0) - this is the first
321: * day of a 400-year long "era", right after additional day of leap year.
322: * This adjustment is required only for date calculation, so instead of
323: * modifying time_t value (which would require 64-bit operations to work
324: * correctly) it's enough to adjust the calculated number of days since epoch.
325: */
326:
327: #define SECS_PER_HOUR 3600
328: #define SECS_PER_MIN 60
329: #define DAYS_PER_WEEK 7
330: #define YEAR_BASE 1900
331:
332: #define EPOCH_ADJUSTMENT_DAYS 719468L
333: /* year to which the adjustment was made */
334: #define ADJUSTED_EPOCH_YEAR 0
335: /* 1st March of year 0 is Wednesday */
336: #define ADJUSTED_EPOCH_WDAY 3
337: /* there are 97 leap years in 400-year periods. ((400 - 97) * 365 + 97 * 366) */
338: #define DAYS_PER_ERA 146097L
339: /* there are 24 leap years in 100-year periods. ((100 - 24) * 365 + 24 * 366) */
340: #define DAYS_PER_CENTURY 36524L
341: /* there is one leap year every 4 years */
342: #define DAYS_PER_4_YEARS (3 * 365 + 366)
343: /* number of days in a non-leap year */
344: #define DAYS_PER_YEAR 365
345: /* number of days in January */
346: #define DAYS_IN_JANUARY 31
347: /* number of days in non-leap February */
348: #define DAYS_IN_FEBRUARY 28
349: /* number of years per era */
350: #define YEARS_PER_ERA 400
351:
352: static void pa_gmtime(pa_time_t lcltime, struct tm *res) {
353: long days, rem;
354: int era, weekday, year;
355: unsigned erayear, yearday, month, day;
356: unsigned long eraday;
357:
1.5 moko 358: days = (long)(lcltime / SECS_PER_DAY);
359: rem = (long)(lcltime - (pa_time_t)days * SECS_PER_DAY);
1.3 moko 360: days += EPOCH_ADJUSTMENT_DAYS;
361: if (rem < 0)
362: {
363: rem += SECS_PER_DAY;
364: --days;
365: }
366:
367: /* compute hour, min, and sec */
368: res->tm_hour = (int) (rem / SECS_PER_HOUR);
369: rem %= SECS_PER_HOUR;
370: res->tm_min = (int) (rem / SECS_PER_MIN);
371: res->tm_sec = (int) (rem % SECS_PER_MIN);
372:
373: /* compute day of week */
374: if ((weekday = ((ADJUSTED_EPOCH_WDAY + days) % DAYS_PER_WEEK)) < 0)
375: weekday += DAYS_PER_WEEK;
376: res->tm_wday = weekday;
377:
378: /* compute year, month, day & day of year */
379: /* for description of this algorithm see
380: * http://howardhinnant.github.io/date_algorithms.html#civil_from_days */
381: era = (days >= 0 ? days : days - (DAYS_PER_ERA - 1)) / DAYS_PER_ERA;
382: eraday = days - era * DAYS_PER_ERA; /* [0, 146096] */
383: erayear = (eraday - eraday / (DAYS_PER_4_YEARS - 1) + eraday / DAYS_PER_CENTURY -
384: eraday / (DAYS_PER_ERA - 1)) / 365; /* [0, 399] */
385: yearday = eraday - (DAYS_PER_YEAR * erayear + erayear / 4 - erayear / 100); /* [0, 365] */
386: month = (5 * yearday + 2) / 153; /* [0, 11] */
387: day = yearday - (153 * month + 2) / 5 + 1; /* [1, 31] */
388: month += month < 10 ? 2 : -10;
389: year = ADJUSTED_EPOCH_YEAR + erayear + era * YEARS_PER_ERA + (month <= 1);
390:
391: res->tm_yday = yearday >= DAYS_PER_YEAR - DAYS_IN_JANUARY - DAYS_IN_FEBRUARY ?
392: yearday - (DAYS_PER_YEAR - DAYS_IN_JANUARY - DAYS_IN_FEBRUARY) :
393: yearday + DAYS_IN_JANUARY + DAYS_IN_FEBRUARY + IS_LEAP(erayear);
394: res->tm_year = year - YEAR_BASE;
395: res->tm_mon = month;
396: res->tm_mday = day;
397:
398: res->tm_isdst = 0;
399: }
400:
401:
402: /*
403: * mktime.c
404: * Original Author: G. Haley
405: *
406: * Converts the broken-down time, expressed as local time, in the structure
407: * pointed to by tim_p into a calendar time value. The original values of the
408: * tm_wday and tm_yday fields of the structure are ignored, and the original
409: * values of the other fields have no restrictions. On successful completion
410: * the fields of the structure are set to represent the specified calendar
411: * time. Returns the specified calendar time. If the calendar time can not be
412: * represented, returns the value (time_t) -1.
413: */
414:
415: #define _DAYS_IN_MONTH(x) ((x == 1) ? days_in_feb : DAYS_IN_MONTH[x])
416: #define _DAYS_IN_YEAR(year) (IS_LEAP(year) ? 366 : 365)
417:
418: static void validate_structure(struct tm *tim_p) {
419: div_t res;
420: int days_in_feb = 28;
421:
422: /* calculate time & date to account for out of range values */
423: if (tim_p->tm_sec < 0 || tim_p->tm_sec > 59)
424: {
425: res = div (tim_p->tm_sec, 60);
426: tim_p->tm_min += res.quot;
427: if ((tim_p->tm_sec = res.rem) < 0)
428: {
429: tim_p->tm_sec += 60;
430: --tim_p->tm_min;
431: }
432: }
433:
434: if (tim_p->tm_min < 0 || tim_p->tm_min > 59)
435: {
436: res = div (tim_p->tm_min, 60);
437: tim_p->tm_hour += res.quot;
438: if ((tim_p->tm_min = res.rem) < 0)
439: {
440: tim_p->tm_min += 60;
441: --tim_p->tm_hour;
442: }
443: }
444:
445: if (tim_p->tm_hour < 0 || tim_p->tm_hour > 23)
446: {
447: res = div (tim_p->tm_hour, 24);
448: tim_p->tm_mday += res.quot;
449: if ((tim_p->tm_hour = res.rem) < 0)
450: {
451: tim_p->tm_hour += 24;
452: --tim_p->tm_mday;
453: }
454: }
455:
456: if (tim_p->tm_mon < 0 || tim_p->tm_mon > 11)
457: {
458: res = div (tim_p->tm_mon, 12);
459: tim_p->tm_year += res.quot;
460: if ((tim_p->tm_mon = res.rem) < 0)
461: {
462: tim_p->tm_mon += 12;
463: --tim_p->tm_year;
464: }
465: }
466:
467: if (_DAYS_IN_YEAR (tim_p->tm_year) == 366)
468: days_in_feb = 29;
469:
470: if (tim_p->tm_mday <= 0)
471: {
472: while (tim_p->tm_mday <= 0)
473: {
474: if (--tim_p->tm_mon == -1)
475: {
476: tim_p->tm_year--;
477: tim_p->tm_mon = 11;
478: days_in_feb =
479: ((_DAYS_IN_YEAR (tim_p->tm_year) == 366) ?
480: 29 : 28);
481: }
482: tim_p->tm_mday += _DAYS_IN_MONTH (tim_p->tm_mon);
483: }
484: }
485: else
486: {
487: while (tim_p->tm_mday > _DAYS_IN_MONTH (tim_p->tm_mon))
488: {
489: tim_p->tm_mday -= _DAYS_IN_MONTH (tim_p->tm_mon);
490: if (++tim_p->tm_mon == 12)
491: {
492: tim_p->tm_year++;
493: tim_p->tm_mon = 0;
494: days_in_feb =
495: ((_DAYS_IN_YEAR (tim_p->tm_year) == 366) ?
496: 29 : 28);
497: }
498: }
499: }
500: }
501:
502: static pa_time_t pa_mktime(struct tm *tim_p) {
503: pa_time_t tim = 0;
504: long days = 0;
505: int year;
506:
507: /* validate structure */
508: validate_structure (tim_p);
509:
510: /* compute hours, minutes, seconds */
511: tim += tim_p->tm_sec + (tim_p->tm_min * SECS_PER_MIN) +
512: (tim_p->tm_hour * SECS_PER_HOUR);
513:
514: /* compute days in year */
515: days += tim_p->tm_mday - 1;
516: days += DAYS_BEFORE_MONTH[tim_p->tm_mon];
517: if (tim_p->tm_mon > 1 && _DAYS_IN_YEAR (tim_p->tm_year) == 366)
518: days++;
519:
520: /* compute day of the year */
521: tim_p->tm_yday = days;
522:
523: if (tim_p->tm_year > 10000 || tim_p->tm_year < -10000)
524: return (time_t) -1;
525:
526: /* compute days in other years */
527: if ((year = tim_p->tm_year) > 70)
528: {
529: for (year = 70; year < tim_p->tm_year; year++)
530: days += _DAYS_IN_YEAR (year);
531: }
532: else if (year < 70)
533: {
534: for (year = 69; year > tim_p->tm_year; year--)
535: days -= _DAYS_IN_YEAR (year);
536: days -= _DAYS_IN_YEAR (year);
537: }
538:
539: /* compute total seconds */
540: tim += (pa_time_t)days * SECS_PER_DAY;
541:
542: /* compute day of the week */
543: if ((tim_p->tm_wday = (days + 4) % 7) < 0)
544: tim_p->tm_wday += 7;
545:
546: return tim;
547: }
548:
549: #endif
E-mail: