1 2 /* 3 * datetime.c 4 * Type handler for TIME, TIMETZ, DATE, TIMESTAMP and TIMESTAMPTZ 5 * data types. Also includes public function PQlocalTZInfo. 6 * 7 * Copyright (c) 2009 eSilo, LLC. All rights reserved. 8 * This is free software; see the source for copying conditions. There is 9 * NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR 10 * PURPOSE. 11 */ 12 13 /* Microsoft went overboard with these warnings. This avoids 14 * warnings about _timezone and _tzname. The alternatives 15 * provided by microsoft are terrible. All other warnings, 16 * like strdup, snprintf, strcpy, etc.. have been avoided 17 * by using Microsoft alternate functions. This whole thing 18 * is completely moronic. 19 */ 20 #define _CRT_SECURE_NO_WARNINGS 21 22 #include "libpqtypes-int.h" 23 24 #if defined(PQT_WIN32) || defined(HAVE_TIME_H) 25 # include <time.h> 26 #endif 27 28 #if defined(HAVE_CONFIG_H) && defined(HAVE_SYS_TIME_H) 29 # include <sys/time.h> 30 #endif 31 32 #define MINGMTOFF -53940 /* -1459 */ 33 #define MAXGMTOFF 53940 /* +1459 */ 34 #define ISVALIDGMTOFF(off) ((off) >= MINGMTOFF && (off) <= MAXGMTOFF) 35 36 #define CHECKDATEVALS(_args, _d) do{ \ 37 if ((_d)->year < 0) \ 38 RERR(_args, "invalid year value ... cannot be negative"); \ 39 if ((_d)->mon < 0 || (_d)->mon > 11) \ 40 RERR(_args, "invalid month value ... range is 0 to 11"); \ 41 if ((_d)->mday < 1 || (_d)->mday > 31) \ 42 RERR(_args, "invalid day value ... range is 1 to 31"); \ 43 }while (0) 44 45 #define CHECKTIMEVALS(_args, _t, _withtz) do{ \ 46 if ((_t)->hour < 0 || (_t)->hour > 23) \ 47 RERR(_args, "invalid hour value ... range is 0 to 23"); \ 48 if ((_t)->min < 0 || (_t)->min > 59) \ 49 RERR(_args, "invalid minute value ... range is 0 to 59"); \ 50 if ((_t)->sec < 0 || (_t)->sec > 59) \ 51 RERR(_args, "invalid second value ... range is 0 to 59"); \ 52 if ((_t)->usec < 0 || (_t)->usec > 999999) \ 53 RERR(_args, "invalid microsecond value ... range is 0 to 999999"); \ 54 if ((_withtz) && !ISVALIDGMTOFF((_t)->gmtoff)) \ 55 return (_args)->errorf(_args, \ 56 "invalid gmtoff value ... range is %d to %d", MINGMTOFF, MAXGMTOFF); \ 57 }while (0) 58 59 #define MONTHS_PER_YEAR 12 60 #define SECS_PER_YEAR (36525 * 864) /* avoid floating-point computation */ 61 #define SECS_PER_DAY 86400 62 #define SECS_PER_HOUR 3600 63 #define SECS_PER_MINUTE 60 64 #define MINS_PER_HOUR 60 65 #define USECS_PER_DAY PQT_INT64CONST(86400000000) 66 #define USECS_PER_HOUR PQT_INT64CONST(3600000000) 67 #define USECS_PER_MINUTE PQT_INT64CONST(60000000) 68 #define USECS_PER_SEC PQT_INT64CONST(1000000) 69 #define UNIX_EPOCH_JDATE 2440588 /* == date2j(1970, 1, 1) */ 70 #define POSTGRES_EPOCH_JDATE 2451545 /* == date2j(2000, 1, 1) */ 71 72 #define TS_PREC_INV 1000000.0 73 #define TSROUND(j) (rint(((double) (j)) * TS_PREC_INV) / TS_PREC_INV) 74 75 /* round off to MAX_TIME_PRECISION decimal places */ 76 #define TIME_PREC_INV 10000000000.0 77 #define TIMEROUND(j) (rint(((double) (j)) * TIME_PREC_INV) / TIME_PREC_INV) 78 79 #define TMODULO(t,q,u) \ 80 do { \ 81 (q) = (int) (((t) < 0) ? ceil((t) / (u)) : floor((t) / (u))); \ 82 if ((q) != 0) (t) -= rint((q) * (u)); \ 83 } while (0) 84 85 /* Converts "1209 BC" notation to "-1208" */ 86 #define BC2YEAR(_isbc, _yr) (_isbc) ? -((_yr) - 1) : (_yr) 87 /* Converts "-1208" to "1209 BC" notation */ 88 #define YEAR2BC(_yr) ((_yr) <= 0) ? -((_yr) - 1) : (_yr) 89 90 /* date & time functions */ 91 static int date2j(int y, int m, int d); 92 static void gmtoff2name(int gmtoff, char *tzNameBuf, size_t size); 93 static void tzabbr2info(const char *tzabbr, int *gmtoff, int *isdst); 94 static int text2time(char *timestr, PGtime *time, int withtz); 95 static int text2interval(char *intvlstr, PGinterval *interval); 96 static int text2date(const char *datestr, PGdate *date, const char *datestyle); 97 static int bin2pgdate(int d, PGdate *date); 98 static int bin2pgtime(double t, int gmtoff, PGtime *time, int withtz); 99 static int bin2pginterval(double tval, int days, int mons, PGinterval *interval); 100 static int bin2pgts(double ts, PGtimestamp *pgts, int withtz); 101 static char *time2t(PGtypeArgs *args, PGtime *t, void *val); 102 103 #ifdef PQT_WINAPI 104 static char *_win32_tzabbr(const char *fullname); 105 #endif 106 107 #if defined(HAVE_CONFIG_H) && !defined(HAVE_FLOOR) 108 static double 109 floor(double x) 110 { 111 /* also works if modf always returns a positive fractional part */ 112 double val; 113 return modf(x, &val) < 0 ? val - 1.0 : val ; 114 } 115 #endif 116 117 #if defined(HAVE_CONFIG_H) && !defined(HAVE_CEIL) 118 static double 119 ceil(double x) 120 { 121 /* also works if modf always returns a positive fractional part */ 122 double val; 123 return modf(x, &val) > 0 ? val + 1.0 : val ; 124 } 125 #endif 126 127 /* MSVC and unixes w/o rint */ 128 #if defined(PQT_MSVC) || (defined(HAVE_CONFIG_H) && !defined(HAVE_RINT)) 129 static double 130 rint(double x) 131 { 132 double f, n = 0.; 133 134 f = modf(x, &n); 135 136 if (x > 0.) 137 { 138 if (f > .5) 139 n += 1.; 140 } 141 else if (x < 0.) 142 { 143 if (f < -.5) 144 n -= 1.; 145 } 146 147 return n; 148 } 149 #endif 150 151 /* 152 * time: PGtime members must be set: hour, min, sec, usec. 153 * timetz: PGtime members must be set: hour, min, sec, usec, gmtoff. 154 */ 155 static int 156 put_time2(PGtypeArgs *args, int withtz) 157 { 158 int r=8; 159 char tbuf[8]; 160 PGtime *time = va_arg(args->ap, PGtime *); 161 162 PUTNULLCHK(args, time); 163 CHECKTIMEVALS(args, time, withtz); 164 165 pqt_swap8(args->put.out, time2t(args, time, tbuf), 1); 166 167 if (withtz) 168 { 169 pqt_buf_putint4(args->put.out + 8, -time->gmtoff); 170 r += 4; 171 } 172 173 return r; 174 } 175 176 int 177 pqt_put_time(PGtypeArgs *args) 178 { 179 return put_time2(args, 0); 180 } 181 182 int 183 pqt_put_timetz(PGtypeArgs *args) 184 { 185 return put_time2(args, 1); 186 } 187 188 /* Also handles timetz */ 189 static int 190 get_time2(PGtypeArgs *args, int withtz) 191 { 192 DECLVALUE(args); 193 double t; 194 int gmtoff=0; 195 char tbuf[8]; 196 PGtime *time = va_arg(args->ap, PGtime *); 197 198 CHKGETVALS(args, time); 199 200 if (args->format == TEXTFMT) 201 { 202 if (text2time(value, time, withtz) == -1) 203 RERR(args, "invalid time format"); 204 return 0; 205 } 206 207 /* read and convert binary data */ 208 pqt_swap8(tbuf, value, 0); 209 if (withtz) 210 gmtoff = -pqt_buf_getint4(value + 8); 211 212 /* Convert 8 byte integer format to double precision. */ 213 if (args->fmtinfo->integer_datetimes) 214 { 215 PGint8 n = *(PGint8 *) tbuf; 216 t = (double) n / 1000000.0; 217 } 218 else 219 { 220 t = *(double *) tbuf; 221 } 222 223 /* convert binary data to a PGtime */ 224 if (bin2pgtime(t, gmtoff, time, withtz) == -1) 225 RERR(args, "negative julian day detected"); 226 227 return 0; 228 } 229 230 int 231 pqt_get_time(PGtypeArgs *args) 232 { 233 return get_time2(args, 0); 234 } 235 236 int 237 pqt_get_timetz(PGtypeArgs *args) 238 { 239 return get_time2(args, 1); 240 } 241 242 /* PGdate members required isbc, year, mon, mday */ 243 int 244 pqt_put_date(PGtypeArgs *args) 245 { 246 int dval; 247 PGdate *date = va_arg(args->ap, PGdate *); 248 249 PUTNULLCHK(args, date); 250 CHECKDATEVALS(args, date); 251 252 dval = date2j(BC2YEAR(date->isbc, date->year), date->mon+1, date->mday) 253 - POSTGRES_EPOCH_JDATE; 254 255 pqt_buf_putint4(args->put.out, dval); 256 return 4; 257 } 258 259 int 260 pqt_get_date(PGtypeArgs *args) 261 { 262 DECLVALUE(args); 263 int d; 264 PGdate *date = va_arg(args->ap, PGdate *); 265 266 CHKGETVALS(args, date); 267 268 if (args->format == TEXTFMT) 269 { 270 if (text2date(value, date, args->fmtinfo->datestyle) == -1) 271 RERR(args, "invalid date format"); 272 return 0; 273 } 274 275 d = pqt_buf_getint4(value); 276 if (bin2pgdate(d, date) == -1) 277 RERR(args, "binary date conversion failed"); 278 279 return 0; 280 } 281 282 /* 283 * timestamp: The timestamp is never converted from what is provided. The 284 * timezone info is dropped as pgtype 'time' has no concept of timezones. 285 * The following PGtimestamp members must be set: date.isbc, date.year, 286 * date.mon, date.mday, time.hour, time.min, time.sec, time.usec. 287 * 288 * timestamptz: The timestamptz is always converted to GMT. Its adjusted by 289 * PGtime.gmtoff, so set this value to zero if you already have GMT. 290 * The following PGtimestamp members must be set: date.isbc, date.year, 291 * date.mon, date.mday, time.hour, time.min, time.sec, time.usec, time.gmtoff. 292 */ 293 static int 294 put_timestamp2(PGtypeArgs *args, int withtz) 295 { 296 char tbuf[8]; 297 int year, mon; 298 PGtimestamp *pgts = va_arg(args->ap, PGtimestamp *); 299 PGtime *t = &pgts->time; 300 301 PUTNULLCHK(args, pgts); 302 CHECKDATEVALS(args, &pgts->date); 303 CHECKTIMEVALS(args, t, withtz); 304 305 mon = pgts->date.mon + 1; 306 year = BC2YEAR(pgts->date.isbc, pgts->date.year); 307 308 time2t(args, t, tbuf); 309 310 if (args->fmtinfo->integer_datetimes) 311 { 312 PGint8 time = *(PGint8 *) tbuf; 313 int date = date2j(year, mon, pgts->date.mday) - POSTGRES_EPOCH_JDATE; 314 PGint8 val = (PGint8) (date * USECS_PER_DAY + time); 315 316 /* check for major overflow */ 317 if ((val - time) / USECS_PER_DAY != date) 318 RERR(args, "timestamp overflow"); 319 320 /* check for just-barely overflow (okay except time-of-day wraps) */ 321 if ((val < 0 && date >= 0) || (val >= 0 && date < 0)) 322 RERR(args, "timestamp overflow"); 323 324 /* currently in localtime, convert to GMT */ 325 if (withtz) 326 val -= (t->gmtoff * USECS_PER_SEC); 327 328 pqt_swap8(args->put.out, &val, 1); 329 } 330 else 331 { 332 double time = *(double *) tbuf; 333 double date = date2j(year, mon, pgts->date.mday) - POSTGRES_EPOCH_JDATE; 334 double val = (double) (date * SECS_PER_DAY + time); 335 336 /* currently in localtime, convert to GMT */ 337 if (withtz) 338 val -= (t->gmtoff); 339 340 pqt_swap8(args->put.out, &val, 1); 341 } 342 343 return 8; 344 } 345 346 int 347 pqt_put_timestamp(PGtypeArgs *args) 348 { 349 return put_timestamp2(args, 0); 350 } 351 352 int 353 pqt_put_timestamptz(PGtypeArgs *args) 354 { 355 return put_timestamp2(args, 1); 356 } 357 358 /* Also handles timestamptz */ 359 static int 360 get_timestamp2(PGtypeArgs *args, int withtz) 361 { 362 DECLVALUE(args); 363 char tsbuf[8]; 364 double ts; 365 PGtimestamp *pgts = va_arg(args->ap, PGtimestamp *); 366 367 CHKGETVALS(args, pgts); 368 369 if (args->format == TEXTFMT) 370 { 371 double time; 372 double date; 373 int year; 374 375 if (text2date(value, &pgts->date, args->fmtinfo->datestyle) == -1) 376 RERR(args, "invalid date format"); 377 378 if (text2time(value, &pgts->time, withtz) == -1) 379 RERR(args, "invalid time format"); 380 381 /* compute ts & epoch values. */ 382 time2t(NULL, &pgts->time, &time); 383 year = BC2YEAR(pgts->date.isbc, pgts->date.year); 384 date = date2j(year, pgts->date.mon+1, pgts->date.mday) - POSTGRES_EPOCH_JDATE; 385 ts = (double) (date * SECS_PER_DAY + time); 386 387 /* currently in localtime, convert to GMT */ 388 if (pgts->time.withtz) 389 ts -= (pgts->time.gmtoff); 390 391 pgts->epoch = (PGint8) rint(ts - ((double) pgts->time.usec/1000000.0) + 392 (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY); 393 394 return 0; 395 } 396 397 pqt_swap8(tsbuf, value, 0); 398 399 /* Convert 8 byte integer format to double precision. */ 400 if (args->fmtinfo->integer_datetimes) 401 { 402 PGint8 n = *(PGint8 *) tsbuf; 403 ts = (double)n / 1000000.0; 404 } 405 else 406 { 407 ts = *(double *) tsbuf; 408 } 409 410 if (bin2pgts(ts, pgts, withtz) == -1) 411 RERR(args, "negative julian day detected"); 412 413 return 0; 414 } 415 416 int 417 pqt_get_timestamp(PGtypeArgs *args) 418 { 419 return get_timestamp2(args, 0); 420 } 421 422 int 423 pqt_get_timestamptz(PGtypeArgs *args) 424 { 425 return get_timestamp2(args, 1); 426 } 427 428 int 429 pqt_put_interval(PGtypeArgs *args) 430 { 431 int day; 432 int month; 433 PGinterval *intvl = va_arg(args->ap, PGinterval *); 434 435 PUTNULLCHK(args, intvl); 436 437 month = intvl->years * MONTHS_PER_YEAR + intvl->mons; 438 day = intvl->days; 439 440 if (args->fmtinfo->integer_datetimes) 441 { 442 PGint8 val = (PGint8) ((((((intvl->hours * PQT_INT64CONST(60)) + intvl->mins) 443 * PQT_INT64CONST(60)) + intvl->secs) * USECS_PER_SEC) + intvl->usecs); 444 445 pqt_swap8(args->put.out, &val, 1); 446 } 447 else 448 { 449 double val = (double) ((((intvl->hours * (double) MINS_PER_HOUR) + 450 intvl->mins) * (double) SECS_PER_MINUTE) + (double) intvl->secs 451 + ((double) intvl->usecs/1000000.0)); 452 453 pqt_swap8(args->put.out, &val, 1); 454 } 455 456 pqt_buf_putint4(args->put.out + 8, day); 457 pqt_buf_putint4(args->put.out + 12, month); 458 return 16; /* val(8) + day(4) + mon(4) */ 459 460 } 461 462 int 463 pqt_get_interval(PGtypeArgs *args) 464 { 465 DECLVALUE(args); 466 double tval; 467 int mons; 468 int days; 469 char tvalbuf[8]; 470 PGinterval *interval = va_arg(args->ap, PGinterval *); 471 472 CHKGETVALS(args, interval); 473 474 if (args->format == TEXTFMT) 475 { 476 if (text2interval(value, interval)== -1) 477 RERR(args, "invalid interval format"); 478 return 0; 479 } 480 481 pqt_swap8(tvalbuf, value, 0); 482 days = pqt_buf_getint4(value + 8); 483 mons = pqt_buf_getint4(value + 12); 484 485 /* Convert 8 byte integer format to double precision. */ 486 if (args->fmtinfo->integer_datetimes) 487 { 488 PGint8 n = *(PGint8 *) tvalbuf; 489 tval = (double) n / 1000000.0; 490 } 491 else 492 { 493 tval = *(double *) tvalbuf; 494 } 495 496 if (bin2pginterval(tval, days, mons, interval) == -1) 497 RERR(args, "binary interval conversion failed"); 498 return 0; 499 } 500 501 void 502 PQlocalTZInfo(time_t *t, int *gmtoff, int *isdst, char **tzabbr) 503 { 504 #if defined(HAVE_STRUCT_TM_TM_ZONE) || defined(HAVE_TM_ZONE) || defined(HAVE_TZNAME) 505 time_t tbuf; 506 struct tm *loc; 507 struct tm buf; 508 509 if (!t) 510 { 511 tbuf = time(NULL); 512 t = &tbuf; 513 } 514 515 # ifdef HAVE_LOCALTIME_R 516 /* return value inconsistent. for instance: HP-UX 10 returns an int */ 517 loc = &buf; 518 localtime_r(t, loc); 519 # else 520 buf.tm_hour = 0; /* avoid compiler warning about unreferenced variable */ 521 loc = localtime(t); 522 # endif 523 524 *gmtoff = 0; 525 *isdst = loc->tm_isdst==1 ? 1 : loc->tm_isdst==0 ? 0 : -1; 526 if (tzabbr) 527 *tzabbr = ""; 528 529 # ifdef HAVE_STRUCT_TM_TM_GMTOFF 530 *gmtoff = (int) loc->tm_gmtoff; 531 # endif 532 533 # if defined(HAVE_STRUCT_TM_TM_ZONE) || defined(HAVE_TM_ZONE) 534 if (tzabbr) 535 *tzabbr = (char *) loc->tm_zone; 536 537 /* Use global timezone and tzname variables */ 538 # elif defined(HAVE_TZNAME) 539 *gmtoff = -((loc->tm_isdst > 0) ? pqt_timezone - SECS_PER_HOUR : pqt_timezone); 540 if (tzabbr) 541 *tzabbr = pqt_tzname[(loc->tm_isdst > 0)]; 542 543 /* When winapi is defined, msvc/mingw, tzname contains the windows 544 * timezone names, like "Eastern Daylight Time" instead of EDT. This 545 * function will map these names to the standard abbrev. 546 */ 547 # ifdef PQT_WINAPI 548 if (tzabbr) 549 *tzabbr = _win32_tzabbr(*tzabbr); 550 # endif 551 # endif /* HAVE_TZNAME */ 552 553 #else 554 *gmtoff = 0; 555 *isdst = -1; 556 if (tzabbr) 557 *tzabbr = ""; 558 #endif /* HAVE_STRUCT_TM_TM_ZONE || HAVE_TM_ZONE || HAVE_TZNAME */ 559 } 560 561 static char * 562 time2t(PGtypeArgs *args, PGtime *t, void *val) 563 { 564 if (args && args->fmtinfo->integer_datetimes) 565 *(PGint8 *) val = (PGint8) (((((t->hour * MINS_PER_HOUR + t->min) * 566 SECS_PER_MINUTE) + t->sec) * USECS_PER_SEC) + t->usec); 567 else 568 *(double *) val = (double) (((t->hour * MINS_PER_HOUR + t->min) * 569 SECS_PER_MINUTE) + t->sec + (t->usec/1000000.0)); 570 return val; 571 } 572 573 static int 574 text2date(const char *datestr, PGdate *date, const char *datestyle) 575 { 576 int year; 577 578 while (isspace((int) *datestr)) 579 ++datestr; 580 581 errno = 0; 582 583 /* ISO, SQL or German ('1997-12-17', '12/17/1997', '17.12.1997') */ 584 if (isdigit((int) *datestr)) 585 { 586 int n[3]; 587 588 n[0] = (int) strtol(datestr, (char **) &datestr, 10); 589 if (errno || (*datestr!='-' && *datestr!='/' && *datestr!='.')) 590 return -1; 591 592 n[1] = (int) strtol(datestr+1, (char **) &datestr, 10); 593 if (errno || (*datestr!='-' && *datestr!='/' && *datestr!='.')) 594 return -1; 595 596 n[2] = (int) strtol(datestr+1, (char **) &datestr, 10); 597 if (errno) 598 return -1; 599 600 if (!*datestyle || strstr(datestyle, "ISO") || strstr(datestyle, "YMD")) 601 { 602 date->year = n[0]; 603 date->mon = n[1]; 604 date->mday = n[2]; 605 } 606 else if (strstr(datestyle, "DMY")) 607 { 608 date->mday = n[0]; 609 date->mon = n[1]; 610 date->year = n[2]; 611 } 612 else /* MDY */ 613 { 614 date->mon = n[0]; 615 date->mday = n[1]; 616 date->year = n[2]; 617 } 618 619 date->mon--; 620 } 621 /* Postgres style: 'Thu Dec 13 19:42:52.442126 2007 EST' */ 622 else 623 { 624 static char *monnames[] = {"jan", "feb", "mar", "apr", "may", 625 "jun", "jul", "aug", "sep", "oct","nov", "dec"}; 626 627 int i; 628 629 /* skip day of week name */ 630 if (!(datestr = strchr(datestr, ' '))) 631 return -1; 632 datestr++; 633 634 /* datestr is the day DMY */ 635 if (isdigit((int) *datestr)) 636 { 637 date->mday = (int) strtol(datestr, (char **) &datestr, 10); 638 if (errno) 639 return -1; 640 641 datestr++; 642 for (i=0; i < 12; i++) 643 { 644 if (pqt_strncasecmp(datestr, monnames[i], 3)==0) 645 { 646 date->mon = i; 647 break; 648 } 649 } 650 651 if (i == 12) 652 return -1; 653 654 datestr += 4; /* skip space after monname */ 655 } 656 /* datestr is the monname MDY */ 657 else 658 { 659 int i; 660 661 for (i=0; i < 12; i++) 662 { 663 if (pqt_strncasecmp(datestr, monnames[i], 3)==0) 664 { 665 date->mon = i; 666 break; 667 } 668 } 669 670 if (i == 12) 671 return -1; 672 673 datestr += 4; /* skip space after monname */ 674 date->mday = (int) strtol(datestr, (char **) &datestr, 10); 675 if (errno) 676 return -1; 677 datestr++; /* space after month day */ 678 } 679 680 /* skip time */ 681 if (!(datestr = strchr(datestr, ' '))) 682 return -1; 683 684 date->year = (int) strtol(datestr, NULL, 10); 685 if (errno) 686 return -1; 687 } 688 689 if (date->mday < 0 || date->mday > 31) 690 return -1; 691 692 if (date->mon < 0 || date->mon > 11) 693 return -1; 694 695 date->isbc = strstr(datestr, " BC") ? 1 : 0; 696 year = BC2YEAR(date->isbc, date->year); 697 date->jday = date2j(year, date->mon+1, date->mday); 698 date->wday = (date->jday + 1) % 7; 699 date->yday = date->jday - date2j(year, 1, 1); 700 return 0; 701 } 702 703 /* timestr can be a valid timestamp[tz] or time[tz]. */ 704 static int 705 text2time(char *timestr, PGtime *time, int withtz) 706 { 707 char *p; 708 709 memset(time, 0, sizeof(PGtime)); 710 time->isdst = -1; 711 712 if (!(p = strchr(timestr, ':'))) 713 return -1; 714 715 timestr = p - 2; 716 errno = 0; 717 718 time->hour = (int) strtol(timestr, ×tr, 10); 719 if (errno || *timestr != ':' || time->hour < 0 || time->hour > 23) 720 return -1; 721 722 time->min = (int) strtol(timestr+1, ×tr, 10); 723 if (errno || *timestr != ':' || time->min < 0 || time->min > 59) 724 return -1; 725 726 time->sec = (int) strtol(timestr+1, ×tr, 10); 727 if (errno || time->sec < 0 || time->sec > 59) 728 return -1; 729 730 if (*timestr == '.') 731 { 732 int i=0; 733 char buf[7]; 734 735 timestr++; 736 737 /* Need 6 digits for usecs to work with strtol, so pad with zeros. */ 738 memset(buf, '0', 6); 739 buf[6] = 0; 740 while (isdigit((int) *timestr)) 741 buf[i++] = *timestr++; 742 743 time->usec = (int) strtol(buf, NULL, 10); 744 if (errno || time->usec < 0 || time->usec > 999999) 745 return -1; 746 } 747 748 /* no timezone present */ 749 if (!withtz) 750 return 0; 751 752 753 /* --------------------------------- 754 * Following ISO 8601, also detecting timezone abbrev. 755 */ 756 757 time->withtz = 1; 758 759 /* UTC, using 'Z' encoding '00:00:00Z' */ 760 if (*timestr == 'Z') 761 { 762 pqt_strcpy(time->tzabbr, sizeof(time->tzabbr), "UTC"); 763 timestr++; 764 } 765 /* have gmtoff */ 766 else if (*timestr=='-' || *timestr=='+') 767 { 768 int hour = 0; 769 int min = 0; 770 int sec = 0; 771 char sign = *timestr++; 772 char buf[3]; 773 774 buf[0] = *timestr++; 775 buf[1] = *timestr++; 776 buf[2] = 0; 777 hour = (int) strtol(buf, NULL, 10); 778 if (errno) 779 return -1; 780 781 /* +/-hh:mm:ss case (support seconds) */ 782 if (*timestr == ':') 783 timestr++; 784 785 /* have minute field */ 786 if (isdigit((int) *timestr)) 787 { 788 buf[0] = *timestr++; 789 buf[1] = *timestr++; 790 min = (int) strtol(buf, NULL, 10); 791 if (errno) 792 return -1; 793 794 if (*timestr == ':') 795 timestr++; 796 797 /* have second field */ 798 if (isdigit((int) *timestr)) 799 { 800 buf[0] = *timestr++; 801 buf[1] = *timestr++; 802 sec = (int) strtol(buf, NULL, 10); 803 if (errno) 804 return -1; 805 } 806 } 807 808 time->gmtoff = (hour * 3600) + (min * 60) + sec; 809 if (sign == '-') 810 time->gmtoff = -time->gmtoff; 811 812 if (!ISVALIDGMTOFF(time->gmtoff)) 813 return -1; 814 } 815 816 /* find the beginning of tzname */ 817 while (*timestr && !isalpha((int) *timestr)) 818 timestr++; 819 820 if (*timestr) 821 { 822 /* find timezone abbrev end */ 823 if (!(p = strchr(timestr, ' '))) 824 p = timestr + strlen(timestr); 825 826 memcpy(time->tzabbr, timestr, p - timestr); 827 time->tzabbr[p - timestr] = 0; 828 829 /* BC is not a timezone, false-positive */ 830 if (strcmp(time->tzabbr, "BC")==0) 831 *time->tzabbr = 0; 832 } 833 834 if (time->gmtoff != 0 && !*time->tzabbr) 835 gmtoff2name(time->gmtoff, time->tzabbr, sizeof(time->tzabbr)); 836 else if (time->gmtoff==0 && *time->tzabbr) 837 tzabbr2info(time->tzabbr, &time->gmtoff, &time->isdst); 838 839 return 0; 840 } 841 842 static int 843 text2interval(char *istr, PGinterval *interval) 844 { 845 char *s; 846 PGtime pgtime; 847 int is_before = 0; 848 849 errno = 0; 850 memset(interval, 0, sizeof(PGinterval)); 851 852 is_before = strstr(istr, "ago") ? 1 : 0; 853 854 if ((s = strstr(istr, " year"))) 855 { 856 for (s=s-1; s > istr && !isspace((int) *s); --s) ; 857 interval->years = (int)strtol(s, NULL, 10); 858 if (errno) 859 return -1; 860 if (!is_before) 861 is_before = interval->years < 0; 862 } 863 864 if ((s = strstr(istr, " mon"))) 865 { 866 for (s=s-1; s > istr && !isspace((int) *s); --s) ; 867 interval->mons = (int)strtol(s, NULL, 10); 868 if (errno) 869 return -1; 870 if (!is_before) 871 is_before = interval->mons < 0; 872 } 873 874 if ((s = strstr(istr, " day"))) 875 { 876 for (s=s-1; s > istr && !isspace((int) *s); --s) ; 877 interval->days = (int) strtol(s, NULL, 10); 878 if (errno) 879 return -1; 880 if (!is_before) 881 is_before = interval->days < 0; 882 } 883 884 /* Means ISO DateStyle is in use, uses the 00:00:00 time format */ 885 if (text2time(istr, &pgtime, 0) == 0) 886 { 887 interval->usecs = pgtime.usec; 888 interval->secs = pgtime.sec; 889 interval->mins = pgtime.min; 890 interval->hours = pgtime.hour; 891 892 /* text2time doesn't support negative values, detect is_before here. 893 * Find first ':' and subtract two-digit year + possible minus sign. 894 */ 895 if (!is_before) 896 is_before = (s = strchr(istr, ':')) && (*(s - 3) == '-'); 897 } 898 /* Means POSTGRES, SQL or GERMAN DateStyle is in use. */ 899 else 900 { 901 if ((s = strstr(istr, " hour"))) 902 { 903 for (s=s-1; s > istr && !isspace((int) *s); --s) ; 904 interval->hours = (int) strtol(s, NULL, 10); 905 if (errno) 906 return -1; 907 } 908 909 if ((s = strstr(istr, " min"))) 910 { 911 for (s=s-1; s > istr && !isspace((int) *s); --s) ; 912 interval->mins = (int) strtol(s, NULL, 10); 913 if (errno) 914 return -1; 915 } 916 917 if ((s = strstr(istr, " sec"))) 918 { 919 for (s=s-1; s > istr && !isspace((int) *s); --s) ; 920 interval->secs = (int) strtol(s, &s, 10); 921 if (errno) 922 return -1; 923 924 /* usecs */ 925 if (*s == '.') 926 { 927 int i=0; 928 char buf[7]; 929 memset(buf, '0', 6); 930 buf[6] = 0; 931 for (s=s+1; isdigit((int) *s); s++) 932 buf[i++] = *s; 933 interval->usecs = atoi(buf); 934 } 935 } 936 } 937 938 /* flip unit signs where still needed */ 939 if (is_before) 940 { 941 if (interval->years > 0) 942 interval->years = -interval->years; 943 if (interval->mons > 0) 944 interval->mons = -interval->mons; 945 if (interval->days > 0) 946 interval->days = -interval->days; 947 if (interval->hours > 0) 948 interval->hours = -interval->hours; 949 if (interval->mins > 0) 950 interval->mins = -interval->mins; 951 if (interval->secs > 0) 952 interval->secs = -interval->secs; 953 if (interval->usecs > 0) 954 interval->usecs = -interval->usecs; 955 } 956 957 return 0; 958 } 959 960 /* Uses ISO 8601 GMT+/-hhmmss notation. See tzabbr2info as well. */ 961 static void 962 gmtoff2name(int gmtoff, char *buf, size_t size) 963 { 964 char sign; 965 int mins; 966 int secs; 967 968 if (gmtoff < 0) 969 { 970 sign = '-'; 971 gmtoff = -gmtoff; 972 } 973 else 974 { 975 sign = '+'; 976 } 977 978 mins = gmtoff % 3600; 979 secs = mins % 60; 980 981 if (secs == 0) 982 pqt_snprintf(buf, size, "GMT%c%02d%02d", sign, gmtoff/3600, mins/60); 983 else 984 pqt_snprintf(buf, size, "GMT%c%02d%02d%02d", sign, gmtoff/3600, mins/60, secs); 985 } 986 987 static int 988 date2j(int y, int m, int d) 989 { 990 int julian; 991 int century; 992 993 if (m > 2) 994 { 995 m += 1; 996 y += 4800; 997 } 998 else 999 { 1000 m += 13; 1001 y += 4799; 1002 } 1003 1004 century = y / 100; 1005 julian = y * 365 - 32167; 1006 julian += y / 4 - century + century / 4; 1007 julian += 7834 * m / 256 + d; 1008 1009 return julian; 1010 } 1011 1012 static void 1013 j2date(int jd, int *year, int *month, int *day) 1014 { 1015 unsigned int julian; 1016 unsigned int quad; 1017 unsigned int extra; 1018 int y; 1019 1020 julian = jd; 1021 julian += 32044; 1022 quad = julian / 146097; 1023 extra = (julian - quad * 146097) * 4 + 3; 1024 julian += 60 + quad * 3 + extra / 146097; 1025 quad = julian / 1461; 1026 julian -= quad * 1461; 1027 y = julian * 4 / 1461; 1028 julian = ((y != 0) ? ((julian + 305) % 365) : ((julian + 306) % 366)) 1029 + 123; 1030 y += quad * 4; 1031 *year = y - 4800; 1032 quad = julian * 2141 / 65536; 1033 *day = julian - 7834 * quad / 256; 1034 *month = (quad + 10) % 12 + 1; 1035 } 1036 1037 static void 1038 dt2time(double jd, int *hour, int *min, int *sec, double *fsec) 1039 { 1040 *hour = (int) (jd / SECS_PER_HOUR); 1041 jd -= (*hour) * SECS_PER_HOUR; 1042 *min = (int) (jd / SECS_PER_MINUTE); 1043 jd -= (*min) * SECS_PER_MINUTE; 1044 *sec = (int) jd; 1045 *fsec = jd - *sec; 1046 } 1047 1048 static int 1049 bin2pgdate(int dval, PGdate *date) 1050 { 1051 memset(date, 0, sizeof(PGdate)); 1052 1053 j2date(dval + POSTGRES_EPOCH_JDATE, &date->year, &date->mon, &date->mday); 1054 date->jday = date2j(date->year, date->mon, date->mday); 1055 date->yday = date->jday - date2j(date->year, 1, 1); 1056 date->wday = (date->jday + 1) % 7; 1057 1058 if (date->year <= 0) 1059 { 1060 date->isbc = 1; 1061 date->year = -(date->year-1); 1062 } 1063 1064 date->mon--; 1065 return 0; 1066 } 1067 1068 /* gmtoff is seconds east of GMT, adjusted prior to call. */ 1069 static int 1070 bin2pgtime(double tval, int gmtoff, PGtime *time, int withtz) 1071 { 1072 double rem; 1073 1074 memset(time, 0, sizeof(PGtime)); 1075 time->isdst = -1; /* no way of knowing */ 1076 1077 if (withtz) 1078 { 1079 time->gmtoff = gmtoff; 1080 gmtoff2name(time->gmtoff, time->tzabbr, sizeof(time->tzabbr)); 1081 time->withtz = 1; 1082 } 1083 1084 RECALC: 1085 rem = tval; 1086 TMODULO(rem, time->hour, (double) SECS_PER_HOUR); 1087 TMODULO(rem, time->min, (double) SECS_PER_MINUTE); 1088 TMODULO(rem, time->sec, 1.0); 1089 rem = TIMEROUND(rem); 1090 1091 /* roundoff may need to propagate to higher-order fields */ 1092 if (rem >= 1.0) 1093 { 1094 tval = ceil(tval); 1095 goto RECALC; 1096 } 1097 1098 time->usec = (int) (rem * 1000000.0); 1099 1100 return 0; 1101 } 1102 1103 static int 1104 breakdown_time(double time, double *datep, PGdate *pgdate, 1105 PGtime *pgtime, double *fsec) 1106 { 1107 double date; 1108 1109 TMODULO(time, date, (double) SECS_PER_DAY); 1110 1111 if (time < 0) 1112 { 1113 time += SECS_PER_DAY; 1114 date -= 1; 1115 } 1116 1117 /* add offset to go from J2000 back to standard Julian date */ 1118 date += POSTGRES_EPOCH_JDATE; 1119 1120 RECALC_DATE: 1121 /* Julian day routine does not work for negative Julian days */ 1122 if (date < 0 || date > (PGint8) INT_MAX) 1123 return -1; 1124 1125 j2date((int)date, &pgdate->year, &pgdate->mon, &pgdate->mday); 1126 1127 RECALC_TIME: 1128 dt2time(time, &pgtime->hour, &pgtime->min, &pgtime->sec, fsec); 1129 *fsec = TSROUND(*fsec); 1130 1131 /* roundoff may need to propagate to higher-order fields */ 1132 if (*fsec >= 1.0) 1133 { 1134 time = ceil(time); 1135 if (time >= (double) SECS_PER_DAY) 1136 { 1137 time = 0; 1138 date += 1; 1139 goto RECALC_DATE; 1140 } 1141 goto RECALC_TIME; 1142 } 1143 1144 if (datep) 1145 *datep = date; 1146 return 0; 1147 } 1148 1149 static int 1150 bin2pgts(double ts, PGtimestamp *pgts, int withtz) 1151 { 1152 double fsec; 1153 double date; 1154 double time; 1155 1156 memset(pgts, 0, sizeof(PGtimestamp)); 1157 pgts->time.isdst = -1; 1158 1159 time = ts; 1160 1161 /* server sent UTC, convert to localtime. Need to determine the 1162 * gmtoff for the timestamp epoch value (daylight or not). This 1163 * can differ from what zone the machine is in. If its July in 1164 * NY but the timestamp is Jan, we need to apply EST to timestamp 1165 * rather than EDT. 1166 */ 1167 if (withtz) 1168 { 1169 char *tzabbr; 1170 time_t tepoch; 1171 PGtime pgtime; 1172 PGdate pgdate; 1173 PGint8 t64 = (PGint8) rint(ts); 1174 1175 pgts->epoch = (PGint8) rint((double) t64 + 1176 (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY); 1177 1178 /* Safe range for a time_t is 0 - INT_MAX. Some system don't support 1179 * negative values at all. System with 64-bit time_t can support 1180 * wider ranges, but there is no way of determining what that is. 1181 * NOTE: 64-bit time_t systems aren't limited to 31-bit upper values. 1182 */ 1183 if (pgts->epoch < 0 || (sizeof(time_t)==4 && pgts->epoch > INT_MAX)) 1184 { 1185 struct tm tm; 1186 1187 /* pull out the date and time parts so we can issue a mktime 1188 * call, which gets us epoch. 1189 */ 1190 if (breakdown_time(ts, NULL, &pgdate, &pgtime, &fsec) < 0) 1191 return -1; 1192 1193 memset(&tm, 0, sizeof(struct tm)); 1194 tm.tm_year = 70; /* out-of-safe-range values use 1970 */ 1195 1196 /* Daylight time was first used by Germany April 30, 1916. So, 1197 * we only recognize its existence after this date. 1198 */ 1199 if (pgdate.year >= 1916) 1200 tm.tm_mon = pgdate.mon - 1; 1201 else /* Ignore month before Apr 1916, use Jan for standard time */ 1202 tm.tm_mon = 0; 1203 1204 tm.tm_mday = pgdate.mday; 1205 tm.tm_hour = pgtime.hour; 1206 tm.tm_min = pgtime.min; 1207 tm.tm_sec = pgtime.sec; 1208 tm.tm_isdst = -1; 1209 1210 tepoch = mktime(&tm); 1211 } 1212 else 1213 { 1214 tepoch = (time_t) pgts->epoch; 1215 } 1216 1217 /* Have an epoch value that can be used to get TZInfo */ 1218 PQlocalTZInfo(&tepoch, &pgts->time.gmtoff, &pgts->time.isdst, &tzabbr); 1219 pqt_strcpy(pgts->time.tzabbr, sizeof(pgts->time.tzabbr), tzabbr); 1220 1221 /* adjust postgres timestamp by gmtoff. */ 1222 time += pgts->time.gmtoff; 1223 pgts->time.withtz = 1; 1224 } 1225 1226 /* breakdown the `localtime' adjusted timestamp */ 1227 if (breakdown_time(time, &date, &pgts->date, &pgts->time, &fsec) < 0) 1228 return -1; 1229 1230 /* jullian day */ 1231 pgts->date.jday = (int) date; 1232 1233 /* get the epoch, more accurate than previous calculation */ 1234 pgts->epoch = (PGint8) rint( 1235 ts - fsec + (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY); 1236 1237 /* NOTE: dbl_epsilon is too small "2.2204460492503131e-16" */ 1238 pgts->time.usec = (int) (fsec * (1000000.0 + 1E-9)); 1239 1240 /* use julian day calculations for wday and yday */ 1241 pgts->date.wday = ((int) date + 1) % 7; 1242 pgts->date.yday = (int) date - date2j(pgts->date.year, 1, 1); 1243 1244 /* Adjust year if this is BC */ 1245 if (pgts->date.year <= 0) 1246 { 1247 pgts->date.year = -(pgts->date.year-1); 1248 pgts->date.isbc = 1; 1249 } 1250 1251 pgts->date.mon--; 1252 return 0; 1253 } 1254 1255 static int 1256 bin2pginterval(double tval, int days, int mons, PGinterval *interval) 1257 { 1258 double time; 1259 double tfrac; 1260 1261 memset(interval, 0, sizeof(PGinterval)); 1262 1263 interval->years = mons / MONTHS_PER_YEAR; 1264 interval->mons = mons % MONTHS_PER_YEAR; 1265 interval->days = days; 1266 time = tval; 1267 1268 RECALC: 1269 TMODULO(time, tfrac, (double) SECS_PER_HOUR); 1270 interval->hours = (int)tfrac; /* could overflow ... */ 1271 1272 TMODULO(time, tfrac, (double) SECS_PER_MINUTE); 1273 interval->mins = (int)tfrac; 1274 1275 TMODULO(time, tfrac, 1.0); 1276 interval->secs = (int)tfrac; 1277 1278 time = TSROUND(time); 1279 /* roundoff may need to propagate to higher-order fields */ 1280 if (time >= 1.0) 1281 { 1282 time = ceil(tval); 1283 goto RECALC; 1284 } 1285 1286 /* NOTE: dbl_epsilon is too small "2.2204460492503131e-16" */ 1287 interval->usecs = (int) (time * (1000000.0 + 1E-9)); 1288 1289 return 0; 1290 } 1291 1292 1293 /* Generated by the below query: 1294 * 1295 * SELECT '{"' || abbrev || '", ' || 1296 * ((EXTRACT(HOUR FROM utc_offset) * 3600) + 1297 * (EXTRACT(MINUTE FROM utc_offset) * 60) + 1298 * EXTRACT(SECOND FROM utc_offset)) || 1299 * ', ' || (CASE WHEN is_dst THEN 1 ELSE 0 END) || '},' 1300 * FROM pg_timezone_abbrevs; 1301 */ 1302 struct _tzmap_ 1303 { 1304 const char *abbrev; 1305 int gmtoff; 1306 unsigned char isdst; 1307 }; 1308 1309 static struct _tzmap_ tzmap[] = { 1310 {"ACSST", 37800, 1}, 1311 {"ACST", -14400, 1}, 1312 {"ACT", -18000, 0}, 1313 {"ADT", -10800, 1}, 1314 {"AESST", 39600, 1}, 1315 {"AEST", 36000, 0}, 1316 {"AFT", 16200, 0}, 1317 {"AKDT", -28800, 1}, 1318 {"AKST", -32400, 0}, 1319 {"ALMST", 25200, 1}, 1320 {"ALMT", 21600, 0}, 1321 {"AMST", 18000, 1}, 1322 {"AMT", 14400, 0}, 1323 {"ANAST", 46800, 1}, 1324 {"ANAT", 43200, 0}, 1325 {"ART", -10800, 0}, 1326 {"AST", -14400, 0}, 1327 {"AWSST", 32400, 1}, 1328 {"AWST", 28800, 0}, 1329 {"AZOST", 0, 1}, 1330 {"AZOT", -3600, 0}, 1331 {"AZST", 18000, 1}, 1332 {"AZT", 14400, 0}, 1333 {"BDST", 7200, 1}, 1334 {"BDT", 21600, 0}, 1335 {"BNT", 28800, 0}, 1336 {"BORT", 28800, 0}, 1337 {"BOT", -14400, 0}, 1338 {"BRA", -10800, 0}, 1339 {"BRST", -7200, 1}, 1340 {"BRT", -10800, 0}, 1341 {"BST", 3600, 1}, 1342 {"BTT", 21600, 0}, 1343 {"CADT", 37800, 1}, 1344 {"CAST", 34200, 0}, 1345 {"CCT", 28800, 0}, 1346 {"CDT", -18000, 1}, 1347 {"CEST", 7200, 1}, 1348 {"CET", 3600, 0}, 1349 {"CETDST", 7200, 1}, 1350 {"CHADT", 49500, 1}, 1351 {"CHAST", 45900, 0}, 1352 {"CKT", 43200, 0}, 1353 {"CLST", -10800, 1}, 1354 {"CLT", -14400, 0}, 1355 {"COT", -18000, 0}, 1356 {"CST", -21600, 0}, 1357 {"CXT", 25200, 0}, 1358 {"DAVT", 25200, 0}, 1359 {"DDUT", 36000, 0}, 1360 {"EASST", -18000, 1}, 1361 {"EAST", -21600, 0}, 1362 {"EAT", 10800, 0}, 1363 {"EDT", -14400, 1}, 1364 {"EEST", 10800, 1}, 1365 {"EET", 7200, 0}, 1366 {"EETDST", 10800, 1}, 1367 {"EGST", 0, 1}, 1368 {"EGT", -3600, 0}, 1369 {"EST", -18000, 0}, 1370 {"FJST", -46800, 1}, 1371 {"FJT", -43200, 0}, 1372 {"FKST", -10800, 1}, 1373 {"FKT", -14400, 0}, 1374 {"FNST", -3600, 1}, 1375 {"FNT", -7200, 0}, 1376 {"GALT", -21600, 0}, 1377 {"GAMT", -32400, 0}, 1378 {"GEST", 14400, 1}, 1379 {"GET", 10800, 0}, 1380 {"GFT", -10800, 0}, 1381 {"GILT", 43200, 0}, 1382 {"GMT", 0, 0}, 1383 {"GYT", -14400, 0}, 1384 {"HKT", 28800, 0}, 1385 {"HST", -36000, 0}, 1386 {"ICT", 25200, 0}, 1387 {"IOT", 21600, 0}, 1388 {"IRKST", 32400, 1}, 1389 {"IRKT", 28800, 0}, 1390 {"IRT", 12600, 0}, 1391 {"IST", 7200, 0}, 1392 {"JAYT", 32400, 0}, 1393 {"JST", 32400, 0}, 1394 {"KDT", 36000, 1}, 1395 {"KGST", 21600, 1}, 1396 {"KGT", 18000, 0}, 1397 {"KOST", 39600, 0}, 1398 {"KRAST", 28800, 1}, 1399 {"KRAT", 25200, 0}, 1400 {"KST", 32400, 0}, 1401 {"LHDT", 39600, 1}, 1402 {"LHST", 37800, 0}, 1403 {"LIGT", 36000, 0}, 1404 {"LINT", 50400, 0}, 1405 {"LKT", 21600, 0}, 1406 {"MAGST", 43200, 1}, 1407 {"MAGT", 39600, 0}, 1408 {"MART", -34200, 0}, 1409 {"MAWT", 21600, 0}, 1410 {"MDT", -21600, 1}, 1411 {"MEST", 7200, 1}, 1412 {"MET", 3600, 0}, 1413 {"METDST", 7200, 1}, 1414 {"MEZ", 3600, 0}, 1415 {"MHT", 43200, 0}, 1416 {"MMT", 23400, 0}, 1417 {"MPT", 36000, 0}, 1418 {"MSD", 14400, 1}, 1419 {"MSK", 10800, 0}, 1420 {"MST", -25200, 0}, 1421 {"MUT", 14400, 0}, 1422 {"MVT", 18000, 0}, 1423 {"MYT", 28800, 0}, 1424 {"NDT", -9000, 1}, 1425 {"NFT", -12600, 0}, 1426 {"NOVST", 25200, 1}, 1427 {"NOVT", 21600, 0}, 1428 {"NPT", 20700, 0}, 1429 {"NST", -12600, 0}, 1430 {"NUT", -39600, 0}, 1431 {"NZDT", 46800, 1}, 1432 {"NZST", 43200, 0}, 1433 {"NZT", 43200, 0}, 1434 {"OMSST", 25200, 1}, 1435 {"OMST", 21600, 0}, 1436 {"PDT", -25200, 1}, 1437 {"PET", -18000, 0}, 1438 {"PETST", 46800, 1}, 1439 {"PETT", 43200, 0}, 1440 {"PGT", 36000, 0}, 1441 {"PHOT", 46800, 0}, 1442 {"PHT", 28800, 0}, 1443 {"PKT", 18000, 0}, 1444 {"PMDT", -7200, 1}, 1445 {"PMST", -10800, 0}, 1446 {"PONT", 39600, 0}, 1447 {"PST", -28800, 0}, 1448 {"PWT", 32400, 0}, 1449 {"PYST", -10800, 1}, 1450 {"PYT", -14400, 0}, 1451 {"RET", 14400, 0}, 1452 {"SADT", 37800, 1}, 1453 {"SAST", 34200, 0}, 1454 {"SCT", 14400, 0}, 1455 {"TAHT", -36000, 0}, 1456 {"TFT", 18000, 0}, 1457 {"TJT", 18000, 0}, 1458 {"TKT", -36000, 0}, 1459 {"TMT", 18000, 0}, 1460 {"TOT", 46800, 0}, 1461 {"TRUT", 36000, 0}, 1462 {"TVT", 43200, 0}, 1463 {"UCT", 0, 0}, 1464 {"ULAST", 32400, 1}, 1465 {"ULAT", 28800, 0}, 1466 {"UT", 0, 0}, 1467 {"UTC", 0, 0}, 1468 {"UYST", -7200, 1}, 1469 {"UYT", -10800, 0}, 1470 {"UZST", 21600, 1}, 1471 {"UZT", 18000, 0}, 1472 {"VET", -14400, 0}, 1473 {"VLAST", 39600, 1}, 1474 {"VLAT", 36000, 0}, 1475 {"VUT", 39600, 0}, 1476 {"WADT", 28800, 1}, 1477 {"WAKT", 43200, 0}, 1478 {"WAST", 25200, 0}, 1479 {"WAT", 3600, 0}, 1480 {"WDT", 32400, 1}, 1481 {"WET", 0, 0}, 1482 {"WETDST", 3600, 1}, 1483 {"WFT", 43200, 0}, 1484 {"WGST", -7200, 1}, 1485 {"WGT", -10800, 0}, 1486 {"YAKST", 36000, 1}, 1487 {"YAKT", 32400, 0}, 1488 {"YAPT", 36000, 0}, 1489 {"YEKST", 21600, 1}, 1490 {"YEKT", 18000, 0}, 1491 {"Z", 0, 0}, 1492 {"ZULU", 0, 0} 1493 }; 1494 1495 static void 1496 tzabbr2info(const char *tzabbr, int *gmtoff, int *isdst) 1497 { 1498 int i=0; 1499 1500 *gmtoff = 0; 1501 *isdst = -1; 1502 1503 for (i=0; i < countof(tzmap); i++) 1504 { 1505 if (strcmp(tzabbr, tzmap[i].abbrev)==0) 1506 { 1507 *gmtoff = tzmap[i].gmtoff; 1508 *isdst = (int) tzmap[i].isdst; 1509 break; 1510 } 1511 } 1512 } 1513 1514 1515 #ifdef PQT_WINAPI 1516 /* ############################################################## 1517 TIME ZONE MAPPING 1518 1519 Windows doesn't use any abbreviated time zone names, like 1520 EST for Eastern Standard Time. The below list was required 1521 to offer these abbreviations. 1522 1523 This list was compiled from: 1524 1525 http://www.date-time-zone.com/ 1526 1527 which is the same list used by Java. It is a mapping from the windows 1528 registry: "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones" 1529 to the Olson Timezone Database: 1530 1531 http://www.twinsun.com/tz/tz-link.htm 1532 1533 NOTE: The below two time zones had a different display name in the 1534 windows registry. The list was adjusted. 1535 1536 Signapore Standard Time - Malay Peninsula Standard Time 1537 Israel Standard Time - Jerusalem Standard Time 1538 */ 1539 1540 static struct { 1541 const char *abbr; 1542 const char *fullname; 1543 } _tzmap[] = { 1544 {"EST", "AUS Eastern Standard Time" }, 1545 {"EST", "AUS Eastern Daylight Time" }, 1546 {"AFT", "Afghanistan Standard Time" }, 1547 {"AKST", "Alaskan Standard Time" }, 1548 {"AKDT", "Alaskan Daylight Time" }, 1549 {"AST", "Arab Standard Time" }, 1550 {"GST", "Arabian Standard Time" }, 1551 {"AST", "Arabic Standard Time" }, 1552 {"ADT", "Arabic Daylight Time" }, 1553 {"AST", "Atlantic Standard Time" }, 1554 {"ADT", "Atlantic Daylight Time" }, 1555 {"AZOT", "Azores Standard Time" }, 1556 {"AZOST", "Azores Daylight Time" }, 1557 {"CST", "Canada Central Standard Time" }, 1558 {"MDT", "Canada Central Daylight Time" }, 1559 {"CVT", "Cape Verde Standard Time" }, 1560 {"CVST", "Cape Verde Daylight Time" }, 1561 {"GET", "Caucasus Standard Time" }, 1562 {"GEST", "Caucasus Daylight Time" }, 1563 {"CST", "Cen. Australia Standard Time" }, 1564 {"CST", "Cen. Australia Daylight Time" }, 1565 {"CST", "Central Standard Time" }, 1566 {"CDT", "Central Daylight Time" }, 1567 {"CST", "Central Standard Time (Mexico)" }, 1568 {"CDT", "Central Daylight Time (Mexico)" }, 1569 {"CST", "Central America Standard Time" }, 1570 {"CDT", "Central America Daylight Time" }, 1571 {"BDT", "Central Asia Standard Time" }, 1572 {"CET", "Central Europe Standard Time" }, 1573 {"CEST", "Central Europe Daylight Time" }, 1574 {"CET", "Central European Standard Time" }, 1575 {"CEST", "Central European Daylight Time" }, 1576 {"MAGT", "Central Pacific Standard Time" }, 1577 {"MAGST", "Central Pacific Daylight Time" }, 1578 {"CST", "China Standard Time" }, 1579 {"CDT", "China Daylight Time" }, 1580 {"GMT+12", "Dateline Standard Time" }, 1581 {"EAT", "E. Africa Standard Time" }, 1582 {"EST", "E. Australia Standard Time" }, 1583 {"EST", "E. Australia Daylight Time" }, 1584 {"EET", "E. Europe Standard Time" }, 1585 {"EEST", "E. Europe Daylight Time" }, 1586 {"BRT", "E. South America Standard Time" }, 1587 {"BRST", "E. South America Daylight Time" }, 1588 {"EST", "Eastern Standard Time" }, 1589 {"EDT", "Eastern Daylight Time" }, 1590 {"EET", "Egypt Standard Time" }, 1591 {"EEST", "Egypt Daylight Time" }, 1592 {"YEKT", "Ekaterinburg Standard Time" }, 1593 {"YEKST", "Ekaterinburg Daylight Time" }, 1594 {"EET", "FLE Standard Time" }, 1595 {"EEST", "FLE Daylight Time" }, 1596 {"FJT", "Fiji Standard Time" }, 1597 {"FJST", "Fiji Daylight Time" }, 1598 {"GMT", "GMT Standard Time" }, 1599 {"BST", "GMT Daylight Time" }, 1600 {"EET", "GTB Standard Time" }, 1601 {"EEST", "GTB Daylight Time" }, 1602 {"WGT", "Greenland Standard Time" }, 1603 {"WGST", "Greenland Daylight Time" }, 1604 {"WET", "Greenwich Standard Time" }, 1605 {"WEST", "Greenwich Daylight Time" }, 1606 {"HST", "Hawaiian Standard Time" }, 1607 {"HPT", "Hawaiian Daylight Time" }, 1608 {"IST", "India Standard Time" }, 1609 {"IST", "India Daylight Time" }, 1610 {"IRST", "Iran Standard Time" }, 1611 {"IRDT", "Iran Daylight Time" }, 1612 {"IST", "Jerusalem Standard Time" }, 1613 {"IDT", "Jerusalem Daylight Time" }, 1614 {"KST", "Korea Standard Time" }, 1615 {"KDT", "Korea Daylight Time" }, 1616 {"CST", "Mexico Standard Time" }, 1617 {"CDT", "Mexico Daylight Time" }, 1618 {"MST", "Mexico Standard Time 2", }, 1619 {"MDT", "Mexico Daylight Time 2", }, 1620 {"FNT", "Mid-Atlantic Standard Time" }, 1621 {"FNST", "Mid-Atlantic Daylight Time" }, 1622 {"MST", "Mountain Standard Time" }, 1623 {"MDT", "Mountain Daylight Time" }, 1624 {"MST", "Mountain Standard Time (Mexico)" }, 1625 {"MDT", "Mountain Daylight Time (Mexico)" }, 1626 {"MMT", "Myanmar Standard Time" }, 1627 {"NOVT", "N. Central Asia Standard Time" }, 1628 {"NOVST", "N. Central Asia Daylight Time" }, 1629 {"NPT", "Nepal Standard Time" }, 1630 {"NZST", "New Zealand Standard Time" }, 1631 {"NZDT", "New Zealand Daylight Time" }, 1632 {"NST", "Newfoundland Standard Time" }, 1633 {"NDT", "Newfoundland Daylight Time" }, 1634 {"KRAT", "North Asia Standard Time" }, 1635 {"KRAST", "North Asia Daylight Time" }, 1636 {"IRKT", "North Asia East Standard Time" }, 1637 {"IRKST", "North Asia East Daylight Time" }, 1638 {"PST", "Pacific Standard Time" }, 1639 {"PDT", "Pacific Daylight Time" }, 1640 {"CLT", "Pacific SA Standard Time" }, 1641 {"CLST", "Pacific SA Daylight Time" }, 1642 {"CET", "Romance Standard Time" }, 1643 {"CEST", "Romance Daylight Time" }, 1644 {"MSK", "Russian Standard Time" }, 1645 {"MSD", "Russian Daylight Time" }, 1646 {"ART", "SA Eastern Standard Time" }, 1647 {"ARST", "SA Eastern Daylight Time" }, 1648 {"COT", "SA Pacific Standard Time" }, 1649 {"COST", "SA Pacific Daylight Time" }, 1650 {"VET", "SA Western Standard Time" }, 1651 {"ICT", "SE Asia Standard Time" }, 1652 {"WST", "Samoa Standard Time" }, 1653 {"SGT", "Malay Peninsula Standard Time" }, 1654 {"MALST", "Malay Peninsula Daylight Time" }, 1655 {"SAST", "South Africa Standard Time" }, 1656 {"SAST", "South Africa Daylight Time" }, 1657 {"IST", "Sri Lanka Standard Time" }, 1658 {"IST", "Sri Lanka Daylight Time" }, 1659 {"CST", "Taipei Standard Time" }, 1660 {"CDT", "Taipei Daylight Time" }, 1661 {"EST", "Tasmania Standard Time" }, 1662 {"EST", "Tasmania Daylight Time" }, 1663 {"JST", "Tokyo Standard Time" }, 1664 {"JDT", "Tokyo Daylight Time" }, 1665 {"TOT", "Tonga Standard Time" }, 1666 {"TOST", "Tonga Daylight Time" }, 1667 {"EST", "US Eastern Standard Time" }, 1668 {"EDT", "US Eastern Daylight Time" }, 1669 {"MST", "US Mountain Standard Time" }, 1670 {"MDT", "US Mountain Daylight Time" }, 1671 {"VLAT", "Vladivostok Standard Time" }, 1672 {"VLAST", "Vladivostok Daylight Time" }, 1673 {"WST", "W. Australia Standard Time" }, 1674 {"WST", "W. Australia Daylight Time" }, 1675 {"WAT", "W. Central Africa Standard Time" }, 1676 {"CET", "W. Europe Standard Time" }, 1677 {"CEST", "W. Europe Daylight Time" }, 1678 {"PKT", "West Asia Standard Time" }, 1679 {"PKST", "West Asia Daylight Time" }, 1680 {"ChST", "West Pacific Standard Time" }, 1681 {"YAKT", "Yakutsk Standard Time" }, 1682 {"YAKST", "Yakutsk Daylight Time" } 1683 }; 1684 1685 static char * 1686 _win32_tzabbr(const char *fullname) 1687 { 1688 int i; 1689 static char empty_string[1] = {0}; 1690 1691 for (i=0; i < countof(tzmap); i++) 1692 if (pqt_strcasecmp(fullname, _tzmap[i].fullname)==0) 1693 return (char *) _tzmap[i].abbr; 1694 1695 return empty_string; 1696 } 1697 1698 #endif |