libpqtypes - datetime.c

Home Page

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