Annotation of parser3/src/types/pa_vdate.C, revision 1.28
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.28 ! moko 13: volatile const char * IDENT_PA_PA_VDATE_C="$Id: pa_vdate.C,v 1.27 2024/10/02 22:54:03 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, >m);
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: #ifdef PA_DATE64
262: *result << pa_uitoa(ftime);
263: #else
264: *result << format_double(ftime, "%.15g");
265: #endif
1.1 moko 266: break;
267: }
268: return result;
269: }
1.2 moko 270:
1.3 moko 271: void VDate::validate() {
272: if((ftm.tm_year==-1901) && (ftm.tm_mon==10) && (ftm.tm_mday==30)){
273: ftm.tm_year=-1900;
274: ftm.tm_mon=-1;
275: ftm.tm_mday=0;
276: }
277: if((ftm.tm_year+1900)<0 || (ftm.tm_year+1900)>9999){
278: throw Exception(DATE_RANGE_EXCEPTION_TYPE, 0, "year '%d' is out of range 0..9999", ftm.tm_year+1900);
279: }
280: }
281:
282: void VDate::set_time(pa_time_t atime) {
1.2 moko 283: if(atime==-1)
284: throw Exception(DATE_RANGE_EXCEPTION_TYPE, 0, "invalid datetime");
1.3 moko 285: if(atime<ZERO_DATE || atime>MAX_DATE)
286: throw Exception(DATE_RANGE_EXCEPTION_TYPE, 0, "unix time %.15g is out of range 0..9999 year", (double)atime);
1.2 moko 287: ftime=atime;
1.3 moko 288: pa_localtime(ftz_cstr, ftime, ftm);
289: validate();
290: }
291:
292: void VDate::set_tm(tm &tmIn) {
1.4 moko 293: pa_time_t atime=pa_mktime(ftz_cstr, tmIn);
294: if(atime==-1)
295: throw Exception(DATE_RANGE_EXCEPTION_TYPE, 0, "invalid datetime '%04d-%02d-%02d'", tmIn.tm_year+1900, tmIn.tm_mon+1, tmIn.tm_mday);
296: ftime=atime;
1.3 moko 297: ftm=tmIn;
298: validate();
1.2 moko 299: }
300:
1.6 moko 301: void VDate::set_tz(const char* atz) {
302: ftz_cstr=atz && atz[0] ? atz : 0; // ftm should be updated afterwards
1.3 moko 303: }
304:
1.6 moko 305: void VDate::set_default_tz(const char* atz) {
306: Temp_tz::default_tz=atz && atz[0] ? atz : 0;
1.2 moko 307: }
308:
309: static int ISOWeekCount (int year) {
310: static const unsigned int YearWeeks[] = {
311: 52,52,52,52,53, 52,52,52,52,52,
312: 53,52,52,52,52, 52,53,52,52,52,
313: 52,53,52,52,52, 52,52,53
314: };
315: return YearWeeks[(year+1900) % 28];
316: }
317:
1.14 moko 318: VDate::yw VDate::CalcWeek(tm tms) {
1.2 moko 319: yw week = {tms.tm_year, 0};
320:
321: // http://www.merlyn.demon.co.uk/weekinfo.htm
322: 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};
323: int diff = tms.tm_yday-(FirstThurs[(tms.tm_year+1900) % 28]-4);
324: if (diff < 0){
325: tms.tm_mday = diff;
1.3 moko 326: pa_mktime(0, tms); // normalize
1.2 moko 327: week = CalcWeek(tms);
328: } else {
329: week.week = 1 + diff/7;
330: if ( week.week > 52 && ISOWeekCount(week.year) < week.week ){
331: week.year++;
332: week.week = 1;
333: }
334: }
335: return week;
336: }
1.3 moko 337:
338: #ifndef PA_DATE64
339:
340: /*
341: * gmtime_r.c
342: * Original Author: Adapted from tzcode maintained by Arthur David Olson.
343: * Modifications:
344: * - Changed to mktm_r and added __tzcalc_limits - 04/10/02, Jeff Johnston
345: * - Fixed bug in mday computations - 08/12/04, Alex Mogilnikov <alx@intellectronika.ru>
346: * - Fixed bug in __tzcalc_limits - 08/12/04, Alex Mogilnikov <alx@intellectronika.ru>
347: * - Move code from _mktm_r() to gmtime_r() - 05/09/14, Freddie Chopin <freddie_chopin@op.pl>
348: * - Fixed bug in calculations for dates after year 2069 or before year 1901. Ideas for
349: * solution taken from musl's __secs_to_tm() - 07/12/2014, Freddie Chopin
350: * <freddie_chopin@op.pl>
351: * - Use faster algorithm from civil_from_days() by Howard Hinnant - 12/06/2014,
352: * Freddie Chopin <freddie_chopin@op.pl>
353: *
354: * Converts the calendar time pointed to by tim_p into a broken-down time
355: * expressed as local time. Returns a pointer to a structure containing the
356: * broken-down time.
357: */
358:
359: /* Move epoch from 01.01.1970 to 01.03.0000 (yes, Year 0) - this is the first
360: * day of a 400-year long "era", right after additional day of leap year.
361: * This adjustment is required only for date calculation, so instead of
362: * modifying time_t value (which would require 64-bit operations to work
363: * correctly) it's enough to adjust the calculated number of days since epoch.
364: */
365:
366: #define SECS_PER_HOUR 3600
367: #define SECS_PER_MIN 60
368: #define DAYS_PER_WEEK 7
369: #define YEAR_BASE 1900
370:
371: #define EPOCH_ADJUSTMENT_DAYS 719468L
372: /* year to which the adjustment was made */
373: #define ADJUSTED_EPOCH_YEAR 0
374: /* 1st March of year 0 is Wednesday */
375: #define ADJUSTED_EPOCH_WDAY 3
376: /* there are 97 leap years in 400-year periods. ((400 - 97) * 365 + 97 * 366) */
377: #define DAYS_PER_ERA 146097L
378: /* there are 24 leap years in 100-year periods. ((100 - 24) * 365 + 24 * 366) */
379: #define DAYS_PER_CENTURY 36524L
380: /* there is one leap year every 4 years */
381: #define DAYS_PER_4_YEARS (3 * 365 + 366)
382: /* number of days in a non-leap year */
383: #define DAYS_PER_YEAR 365
384: /* number of days in January */
385: #define DAYS_IN_JANUARY 31
386: /* number of days in non-leap February */
387: #define DAYS_IN_FEBRUARY 28
388: /* number of years per era */
389: #define YEARS_PER_ERA 400
390:
1.21 moko 391: void pa_gmtime(pa_time_t lcltime, struct tm *res) {
1.3 moko 392: long days, rem;
393: int era, weekday, year;
394: unsigned erayear, yearday, month, day;
395: unsigned long eraday;
396:
1.5 moko 397: days = (long)(lcltime / SECS_PER_DAY);
398: rem = (long)(lcltime - (pa_time_t)days * SECS_PER_DAY);
1.3 moko 399: days += EPOCH_ADJUSTMENT_DAYS;
400: if (rem < 0)
401: {
402: rem += SECS_PER_DAY;
403: --days;
404: }
405:
406: /* compute hour, min, and sec */
407: res->tm_hour = (int) (rem / SECS_PER_HOUR);
408: rem %= SECS_PER_HOUR;
409: res->tm_min = (int) (rem / SECS_PER_MIN);
410: res->tm_sec = (int) (rem % SECS_PER_MIN);
411:
412: /* compute day of week */
413: if ((weekday = ((ADJUSTED_EPOCH_WDAY + days) % DAYS_PER_WEEK)) < 0)
414: weekday += DAYS_PER_WEEK;
415: res->tm_wday = weekday;
416:
417: /* compute year, month, day & day of year */
418: /* for description of this algorithm see
419: * http://howardhinnant.github.io/date_algorithms.html#civil_from_days */
420: era = (days >= 0 ? days : days - (DAYS_PER_ERA - 1)) / DAYS_PER_ERA;
421: eraday = days - era * DAYS_PER_ERA; /* [0, 146096] */
422: erayear = (eraday - eraday / (DAYS_PER_4_YEARS - 1) + eraday / DAYS_PER_CENTURY -
423: eraday / (DAYS_PER_ERA - 1)) / 365; /* [0, 399] */
424: yearday = eraday - (DAYS_PER_YEAR * erayear + erayear / 4 - erayear / 100); /* [0, 365] */
425: month = (5 * yearday + 2) / 153; /* [0, 11] */
426: day = yearday - (153 * month + 2) / 5 + 1; /* [1, 31] */
427: month += month < 10 ? 2 : -10;
428: year = ADJUSTED_EPOCH_YEAR + erayear + era * YEARS_PER_ERA + (month <= 1);
429:
430: res->tm_yday = yearday >= DAYS_PER_YEAR - DAYS_IN_JANUARY - DAYS_IN_FEBRUARY ?
431: yearday - (DAYS_PER_YEAR - DAYS_IN_JANUARY - DAYS_IN_FEBRUARY) :
432: yearday + DAYS_IN_JANUARY + DAYS_IN_FEBRUARY + IS_LEAP(erayear);
433: res->tm_year = year - YEAR_BASE;
434: res->tm_mon = month;
435: res->tm_mday = day;
436:
437: res->tm_isdst = 0;
438: }
439:
440:
441: /*
442: * mktime.c
443: * Original Author: G. Haley
444: *
445: * Converts the broken-down time, expressed as local time, in the structure
446: * pointed to by tim_p into a calendar time value. The original values of the
447: * tm_wday and tm_yday fields of the structure are ignored, and the original
448: * values of the other fields have no restrictions. On successful completion
449: * the fields of the structure are set to represent the specified calendar
450: * time. Returns the specified calendar time. If the calendar time can not be
451: * represented, returns the value (time_t) -1.
452: */
453:
454: #define _DAYS_IN_MONTH(x) ((x == 1) ? days_in_feb : DAYS_IN_MONTH[x])
455: #define _DAYS_IN_YEAR(year) (IS_LEAP(year) ? 366 : 365)
456:
457: static void validate_structure(struct tm *tim_p) {
458: div_t res;
459: int days_in_feb = 28;
460:
461: /* calculate time & date to account for out of range values */
462: if (tim_p->tm_sec < 0 || tim_p->tm_sec > 59)
463: {
464: res = div (tim_p->tm_sec, 60);
465: tim_p->tm_min += res.quot;
466: if ((tim_p->tm_sec = res.rem) < 0)
467: {
468: tim_p->tm_sec += 60;
469: --tim_p->tm_min;
470: }
471: }
472:
473: if (tim_p->tm_min < 0 || tim_p->tm_min > 59)
474: {
475: res = div (tim_p->tm_min, 60);
476: tim_p->tm_hour += res.quot;
477: if ((tim_p->tm_min = res.rem) < 0)
478: {
479: tim_p->tm_min += 60;
480: --tim_p->tm_hour;
481: }
482: }
483:
484: if (tim_p->tm_hour < 0 || tim_p->tm_hour > 23)
485: {
486: res = div (tim_p->tm_hour, 24);
487: tim_p->tm_mday += res.quot;
488: if ((tim_p->tm_hour = res.rem) < 0)
489: {
490: tim_p->tm_hour += 24;
491: --tim_p->tm_mday;
492: }
493: }
494:
495: if (tim_p->tm_mon < 0 || tim_p->tm_mon > 11)
496: {
497: res = div (tim_p->tm_mon, 12);
498: tim_p->tm_year += res.quot;
499: if ((tim_p->tm_mon = res.rem) < 0)
500: {
501: tim_p->tm_mon += 12;
502: --tim_p->tm_year;
503: }
504: }
505:
506: if (_DAYS_IN_YEAR (tim_p->tm_year) == 366)
507: days_in_feb = 29;
508:
509: if (tim_p->tm_mday <= 0)
510: {
511: while (tim_p->tm_mday <= 0)
512: {
513: if (--tim_p->tm_mon == -1)
514: {
515: tim_p->tm_year--;
516: tim_p->tm_mon = 11;
517: days_in_feb =
518: ((_DAYS_IN_YEAR (tim_p->tm_year) == 366) ?
519: 29 : 28);
520: }
521: tim_p->tm_mday += _DAYS_IN_MONTH (tim_p->tm_mon);
522: }
523: }
524: else
525: {
526: while (tim_p->tm_mday > _DAYS_IN_MONTH (tim_p->tm_mon))
527: {
528: tim_p->tm_mday -= _DAYS_IN_MONTH (tim_p->tm_mon);
529: if (++tim_p->tm_mon == 12)
530: {
531: tim_p->tm_year++;
532: tim_p->tm_mon = 0;
533: days_in_feb =
534: ((_DAYS_IN_YEAR (tim_p->tm_year) == 366) ?
535: 29 : 28);
536: }
537: }
538: }
539: }
540:
1.21 moko 541: pa_time_t pa_mktime(struct tm *tim_p) {
1.3 moko 542: pa_time_t tim = 0;
543: long days = 0;
544: int year;
545:
546: /* validate structure */
547: validate_structure (tim_p);
548:
549: /* compute hours, minutes, seconds */
550: tim += tim_p->tm_sec + (tim_p->tm_min * SECS_PER_MIN) +
551: (tim_p->tm_hour * SECS_PER_HOUR);
552:
553: /* compute days in year */
554: days += tim_p->tm_mday - 1;
555: days += DAYS_BEFORE_MONTH[tim_p->tm_mon];
556: if (tim_p->tm_mon > 1 && _DAYS_IN_YEAR (tim_p->tm_year) == 366)
557: days++;
558:
559: /* compute day of the year */
560: tim_p->tm_yday = days;
561:
562: if (tim_p->tm_year > 10000 || tim_p->tm_year < -10000)
563: return (time_t) -1;
564:
565: /* compute days in other years */
566: if ((year = tim_p->tm_year) > 70)
567: {
568: for (year = 70; year < tim_p->tm_year; year++)
569: days += _DAYS_IN_YEAR (year);
570: }
571: else if (year < 70)
572: {
573: for (year = 69; year > tim_p->tm_year; year--)
574: days -= _DAYS_IN_YEAR (year);
575: days -= _DAYS_IN_YEAR (year);
576: }
577:
578: /* compute total seconds */
579: tim += (pa_time_t)days * SECS_PER_DAY;
580:
581: /* compute day of the week */
582: if ((tim_p->tm_wday = (days + 4) % 7) < 0)
583: tim_p->tm_wday += 7;
584:
585: return tim;
586: }
587:
588: #endif
E-mail: