libpqtypes - datetime.c

Home Page

   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, &timestr, 10);
 719   if (errno || *timestr != ':' || time->hour < 0 || time->hour > 23)
 720     return -1;
 721 
 722   time->min = (int) strtol(timestr+1, &timestr, 10);
 723   if (errno || *timestr != ':' || time->min < 0 || time->min > 59)
 724     return -1;
 725 
 726   time->sec = (int) strtol(timestr+1, &timestr, 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