Coverage Report

Created: 2026-11-09 07:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgres/src/backend/utils/adt/datetime.c
Line
Count
Source
1
/*-------------------------------------------------------------------------
2
 *
3
 * datetime.c
4
 *    Support functions for date/time types.
5
 *
6
 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7
 * Portions Copyright (c) 1994, Regents of the University of California
8
 *
9
 *
10
 * IDENTIFICATION
11
 *    src/backend/utils/adt/datetime.c
12
 *
13
 *-------------------------------------------------------------------------
14
 */
15
#include "postgres.h"
16
17
#include <ctype.h>
18
#include <limits.h>
19
#include <math.h>
20
21
#include "access/htup_details.h"
22
#include "access/xact.h"
23
#include "common/int.h"
24
#include "common/string.h"
25
#include "funcapi.h"
26
#include "miscadmin.h"
27
#include "nodes/nodeFuncs.h"
28
#include "parser/scansup.h"
29
#include "utils/builtins.h"
30
#include "utils/date.h"
31
#include "utils/datetime.h"
32
#include "utils/guc.h"
33
#include "utils/tuplestore.h"
34
#include "utils/tzparser.h"
35
36
static int  DecodeNumber(int flen, char *str, bool haveTextMonth,
37
             int fmask, int *tmask,
38
             struct pg_tm *tm, fsec_t *fsec, bool *is2digits);
39
static int  DecodeNumberField(int len, char *str,
40
                int fmask, int *tmask,
41
                struct pg_tm *tm, fsec_t *fsec, bool *is2digits);
42
static int  DecodeTimeCommon(char *str, int fmask, int range,
43
               int *tmask, struct pg_itm *itm);
44
static int  DecodeTime(char *str, int fmask, int range,
45
             int *tmask, struct pg_tm *tm, fsec_t *fsec);
46
static int  DecodeTimeForInterval(char *str, int fmask, int range,
47
                  int *tmask, struct pg_itm_in *itm_in);
48
static const datetkn *datebsearch(const char *key, const datetkn *base, int nel);
49
static int  DecodeDate(char *str, int fmask, int *tmask, bool *is2digits,
50
             struct pg_tm *tm);
51
static char *AppendSeconds(char *cp, int sec, fsec_t fsec,
52
               int precision, bool fillzeros);
53
static bool int64_multiply_add(int64 val, int64 multiplier, int64 *sum);
54
static bool AdjustFractMicroseconds(double frac, int64 scale,
55
                  struct pg_itm_in *itm_in);
56
static bool AdjustFractDays(double frac, int scale,
57
              struct pg_itm_in *itm_in);
58
static bool AdjustFractYears(double frac, int scale,
59
               struct pg_itm_in *itm_in);
60
static bool AdjustMicroseconds(int64 val, double fval, int64 scale,
61
                 struct pg_itm_in *itm_in);
62
static bool AdjustDays(int64 val, int scale,
63
             struct pg_itm_in *itm_in);
64
static bool AdjustMonths(int64 val, struct pg_itm_in *itm_in);
65
static bool AdjustYears(int64 val, int scale,
66
            struct pg_itm_in *itm_in);
67
static int  DetermineTimeZoneOffsetInternal(struct pg_tm *tm, pg_tz *tzp,
68
                      pg_time_t *tp);
69
static bool DetermineTimeZoneAbbrevOffsetInternal(pg_time_t t,
70
                          const char *abbr, pg_tz *tzp,
71
                          int *offset, int *isdst);
72
static pg_tz *FetchDynamicTimeZone(TimeZoneAbbrevTable *tbl, const datetkn *tp,
73
                   DateTimeErrorExtra *extra);
74
75
76
const int day_tab[2][13] =
77
{
78
  {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0},
79
  {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0}
80
};
81
82
const char *const months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
83
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL};
84
85
const char *const days[] = {"Sunday", "Monday", "Tuesday", "Wednesday",
86
"Thursday", "Friday", "Saturday", NULL};
87
88
89
/*****************************************************************************
90
 *   PRIVATE ROUTINES                            *
91
 *****************************************************************************/
92
93
/*
94
 * datetktbl holds date/time keywords.
95
 *
96
 * Note that this table must be strictly alphabetically ordered to allow an
97
 * O(ln(N)) search algorithm to be used.
98
 *
99
 * The token field must be NUL-terminated; we truncate entries to TOKMAXLEN
100
 * characters to fit.
101
 *
102
 * The static table contains no TZ, DTZ, or DYNTZ entries; rather those
103
 * are loaded from configuration files and stored in zoneabbrevtbl, whose
104
 * abbrevs[] field has the same format as the static datetktbl.
105
 */
106
static const datetkn datetktbl[] = {
107
  /* token, type, value */
108
  {"+infinity", RESERV, DTK_LATE},  /* same as "infinity" */
109
  {EARLY, RESERV, DTK_EARLY}, /* "-infinity" reserved for "early time" */
110
  {DA_D, ADBC, AD},     /* "ad" for years > 0 */
111
  {"allballs", RESERV, DTK_ZULU}, /* 00:00:00 */
112
  {"am", AMPM, AM},
113
  {"apr", MONTH, 4},
114
  {"april", MONTH, 4},
115
  {"at", IGNORE_DTF, 0},    /* "at" (throwaway) */
116
  {"aug", MONTH, 8},
117
  {"august", MONTH, 8},
118
  {DB_C, ADBC, BC},     /* "bc" for years <= 0 */
119
  {"d", UNITS, DTK_DAY},    /* "day of month" for ISO input */
120
  {"dec", MONTH, 12},
121
  {"december", MONTH, 12},
122
  {"dow", UNITS, DTK_DOW},  /* day of week */
123
  {"doy", UNITS, DTK_DOY},  /* day of year */
124
  {"dst", DTZMOD, SECS_PER_HOUR},
125
  {EPOCH, RESERV, DTK_EPOCH}, /* "epoch" reserved for system epoch time */
126
  {"feb", MONTH, 2},
127
  {"february", MONTH, 2},
128
  {"fri", DOW, 5},
129
  {"friday", DOW, 5},
130
  {"h", UNITS, DTK_HOUR},   /* "hour" */
131
  {LATE, RESERV, DTK_LATE}, /* "infinity" reserved for "late time" */
132
  {"isodow", UNITS, DTK_ISODOW},  /* ISO day of week, Sunday == 7 */
133
  {"isoyear", UNITS, DTK_ISOYEAR},  /* year in terms of the ISO week date */
134
  {"j", UNITS, DTK_JULIAN},
135
  {"jan", MONTH, 1},
136
  {"january", MONTH, 1},
137
  {"jd", UNITS, DTK_JULIAN},
138
  {"jul", MONTH, 7},
139
  {"julian", UNITS, DTK_JULIAN},
140
  {"july", MONTH, 7},
141
  {"jun", MONTH, 6},
142
  {"june", MONTH, 6},
143
  {"m", UNITS, DTK_MONTH},  /* "month" for ISO input */
144
  {"mar", MONTH, 3},
145
  {"march", MONTH, 3},
146
  {"may", MONTH, 5},
147
  {"mm", UNITS, DTK_MINUTE},  /* "minute" for ISO input */
148
  {"mon", DOW, 1},
149
  {"monday", DOW, 1},
150
  {"nov", MONTH, 11},
151
  {"november", MONTH, 11},
152
  {NOW, RESERV, DTK_NOW},   /* current transaction time */
153
  {"oct", MONTH, 10},
154
  {"october", MONTH, 10},
155
  {"on", IGNORE_DTF, 0},    /* "on" (throwaway) */
156
  {"pm", AMPM, PM},
157
  {"s", UNITS, DTK_SECOND}, /* "seconds" for ISO input */
158
  {"sat", DOW, 6},
159
  {"saturday", DOW, 6},
160
  {"sep", MONTH, 9},
161
  {"sept", MONTH, 9},
162
  {"september", MONTH, 9},
163
  {"sun", DOW, 0},
164
  {"sunday", DOW, 0},
165
  {"t", ISOTIME, DTK_TIME}, /* Filler for ISO time fields */
166
  {"thu", DOW, 4},
167
  {"thur", DOW, 4},
168
  {"thurs", DOW, 4},
169
  {"thursday", DOW, 4},
170
  {TODAY, RESERV, DTK_TODAY}, /* midnight */
171
  {TOMORROW, RESERV, DTK_TOMORROW}, /* tomorrow midnight */
172
  {"tue", DOW, 2},
173
  {"tues", DOW, 2},
174
  {"tuesday", DOW, 2},
175
  {"wed", DOW, 3},
176
  {"wednesday", DOW, 3},
177
  {"weds", DOW, 3},
178
  {"y", UNITS, DTK_YEAR},   /* "year" for ISO input */
179
  {YESTERDAY, RESERV, DTK_YESTERDAY}  /* yesterday midnight */
180
};
181
182
static const int szdatetktbl = sizeof datetktbl / sizeof datetktbl[0];
183
184
/*
185
 * deltatktbl: same format as datetktbl, but holds keywords used to represent
186
 * time units (eg, for intervals, and for EXTRACT).
187
 */
188
static const datetkn deltatktbl[] = {
189
  /* token, type, value */
190
  {"@", IGNORE_DTF, 0},   /* postgres relative prefix */
191
  {DAGO, AGO, 0},       /* "ago" indicates negative time offset */
192
  {"c", UNITS, DTK_CENTURY},  /* "century" relative */
193
  {"cent", UNITS, DTK_CENTURY}, /* "century" relative */
194
  {"centuries", UNITS, DTK_CENTURY},  /* "centuries" relative */
195
  {DCENTURY, UNITS, DTK_CENTURY}, /* "century" relative */
196
  {"d", UNITS, DTK_DAY},    /* "day" relative */
197
  {DDAY, UNITS, DTK_DAY},   /* "day" relative */
198
  {"days", UNITS, DTK_DAY}, /* "days" relative */
199
  {"dec", UNITS, DTK_DECADE}, /* "decade" relative */
200
  {DDECADE, UNITS, DTK_DECADE}, /* "decade" relative */
201
  {"decades", UNITS, DTK_DECADE}, /* "decades" relative */
202
  {"decs", UNITS, DTK_DECADE},  /* "decades" relative */
203
  {"h", UNITS, DTK_HOUR},   /* "hour" relative */
204
  {DHOUR, UNITS, DTK_HOUR}, /* "hour" relative */
205
  {"hours", UNITS, DTK_HOUR}, /* "hours" relative */
206
  {"hr", UNITS, DTK_HOUR},  /* "hour" relative */
207
  {"hrs", UNITS, DTK_HOUR}, /* "hours" relative */
208
  {"m", UNITS, DTK_MINUTE}, /* "minute" relative */
209
  {"microsecon", UNITS, DTK_MICROSEC},  /* "microsecond" relative */
210
  {"mil", UNITS, DTK_MILLENNIUM}, /* "millennium" relative */
211
  {"millennia", UNITS, DTK_MILLENNIUM}, /* "millennia" relative */
212
  {DMILLENNIUM, UNITS, DTK_MILLENNIUM}, /* "millennium" relative */
213
  {"millisecon", UNITS, DTK_MILLISEC},  /* relative */
214
  {"mils", UNITS, DTK_MILLENNIUM},  /* "millennia" relative */
215
  {"min", UNITS, DTK_MINUTE}, /* "minute" relative */
216
  {"mins", UNITS, DTK_MINUTE},  /* "minutes" relative */
217
  {DMINUTE, UNITS, DTK_MINUTE}, /* "minute" relative */
218
  {"minutes", UNITS, DTK_MINUTE}, /* "minutes" relative */
219
  {"mon", UNITS, DTK_MONTH},  /* "months" relative */
220
  {"mons", UNITS, DTK_MONTH}, /* "months" relative */
221
  {DMONTH, UNITS, DTK_MONTH}, /* "month" relative */
222
  {"months", UNITS, DTK_MONTH},
223
  {"ms", UNITS, DTK_MILLISEC},
224
  {"msec", UNITS, DTK_MILLISEC},
225
  {DMILLISEC, UNITS, DTK_MILLISEC},
226
  {"mseconds", UNITS, DTK_MILLISEC},
227
  {"msecs", UNITS, DTK_MILLISEC},
228
  {"qtr", UNITS, DTK_QUARTER},  /* "quarter" relative */
229
  {DQUARTER, UNITS, DTK_QUARTER}, /* "quarter" relative */
230
  {"s", UNITS, DTK_SECOND},
231
  {"sec", UNITS, DTK_SECOND},
232
  {DSECOND, UNITS, DTK_SECOND},
233
  {"seconds", UNITS, DTK_SECOND},
234
  {"secs", UNITS, DTK_SECOND},
235
  {DTIMEZONE, UNITS, DTK_TZ}, /* "timezone" time offset */
236
  {"timezone_h", UNITS, DTK_TZ_HOUR}, /* timezone hour units */
237
  {"timezone_m", UNITS, DTK_TZ_MINUTE}, /* timezone minutes units */
238
  {"us", UNITS, DTK_MICROSEC},  /* "microsecond" relative */
239
  {"usec", UNITS, DTK_MICROSEC},  /* "microsecond" relative */
240
  {DMICROSEC, UNITS, DTK_MICROSEC}, /* "microsecond" relative */
241
  {"useconds", UNITS, DTK_MICROSEC},  /* "microseconds" relative */
242
  {"usecs", UNITS, DTK_MICROSEC}, /* "microseconds" relative */
243
  {"w", UNITS, DTK_WEEK},   /* "week" relative */
244
  {DWEEK, UNITS, DTK_WEEK}, /* "week" relative */
245
  {"weeks", UNITS, DTK_WEEK}, /* "weeks" relative */
246
  {"y", UNITS, DTK_YEAR},   /* "year" relative */
247
  {DYEAR, UNITS, DTK_YEAR}, /* "year" relative */
248
  {"years", UNITS, DTK_YEAR}, /* "years" relative */
249
  {"yr", UNITS, DTK_YEAR},  /* "year" relative */
250
  {"yrs", UNITS, DTK_YEAR}  /* "years" relative */
251
};
252
253
static const int szdeltatktbl = sizeof deltatktbl / sizeof deltatktbl[0];
254
255
static TimeZoneAbbrevTable *zoneabbrevtbl = NULL;
256
257
/* Caches of recent lookup results in the above tables */
258
259
static const datetkn *datecache[MAXDATEFIELDS] = {NULL};
260
261
static const datetkn *deltacache[MAXDATEFIELDS] = {NULL};
262
263
/* Cache for results of timezone abbreviation lookups */
264
265
typedef struct TzAbbrevCache
266
{
267
  char    abbrev[TOKMAXLEN + 1];  /* always NUL-terminated */
268
  char    ftype;      /* TZ, DTZ, or DYNTZ */
269
  int     offset;     /* GMT offset, if fixed-offset */
270
  pg_tz    *tz;       /* relevant zone, if variable-offset */
271
} TzAbbrevCache;
272
273
static TzAbbrevCache tzabbrevcache[MAXDATEFIELDS];
274
275
276
/*
277
 * Calendar time to Julian date conversions.
278
 * Julian date is commonly used in astronomical applications,
279
 *  since it is numerically accurate and computationally simple.
280
 * The algorithms here will accurately convert between Julian day
281
 *  and calendar date for all non-negative Julian days
282
 *  (i.e. from Nov 24, -4713 on).
283
 *
284
 * Rewritten to eliminate overflow problems. This now allows the
285
 * routines to work correctly for all Julian day counts from
286
 * 0 to 2147483647  (Nov 24, -4713 to Jun 3, 5874898) assuming
287
 * a 32-bit integer. Longer types should also work to the limits
288
 * of their precision.
289
 *
290
 * Actually, date2j() will work sanely, in the sense of producing
291
 * valid negative Julian dates, significantly before Nov 24, -4713.
292
 * We rely on it to do so back to Nov 1, -4713; see IS_VALID_JULIAN()
293
 * and associated commentary in timestamp.h.
294
 */
295
296
int
297
date2j(int year, int month, int day)
298
0
{
299
0
  int     julian;
300
0
  int     century;
301
302
0
  if (month > 2)
303
0
  {
304
0
    month += 1;
305
0
    year += 4800;
306
0
  }
307
0
  else
308
0
  {
309
0
    month += 13;
310
0
    year += 4799;
311
0
  }
312
313
0
  century = year / 100;
314
0
  julian = year * 365 - 32167;
315
0
  julian += year / 4 - century + century / 4;
316
0
  julian += 7834 * month / 256 + day;
317
318
0
  return julian;
319
0
}                /* date2j() */
320
321
void
322
j2date(int jd, int *year, int *month, int *day)
323
0
{
324
0
  unsigned int julian;
325
0
  unsigned int quad;
326
0
  unsigned int extra;
327
0
  int     y;
328
329
0
  julian = jd;
330
0
  julian += 32044;
331
0
  quad = julian / 146097;
332
0
  extra = (julian - quad * 146097) * 4 + 3;
333
0
  julian += 60 + quad * 3 + extra / 146097;
334
0
  quad = julian / 1461;
335
0
  julian -= quad * 1461;
336
0
  y = julian * 4 / 1461;
337
0
  julian = ((y != 0) ? ((julian + 305) % 365) : ((julian + 306) % 366))
338
0
    + 123;
339
0
  y += quad * 4;
340
0
  *year = y - 4800;
341
0
  quad = julian * 2141 / 65536;
342
0
  *day = julian - 7834 * quad / 256;
343
0
  *month = (quad + 10) % MONTHS_PER_YEAR + 1;
344
0
}                /* j2date() */
345
346
347
/*
348
 * j2day - convert Julian date to day-of-week (0..6 == Sun..Sat)
349
 *
350
 * Note: various places use the locution j2day(date - 1) to produce a
351
 * result according to the convention 0..6 = Mon..Sun.  This is a bit of
352
 * a crock, but will work as long as the computation here is just a modulo.
353
 */
354
int
355
j2day(int date)
356
0
{
357
0
  date += 1;
358
0
  date %= 7;
359
  /* Cope if division truncates towards zero, as it probably does */
360
0
  if (date < 0)
361
0
    date += 7;
362
363
0
  return date;
364
0
}                /* j2day() */
365
366
367
/*
368
 * GetCurrentDateTime()
369
 *
370
 * Get the transaction start time ("now()") broken down as a struct pg_tm,
371
 * converted according to the session timezone setting.
372
 *
373
 * This is just a convenience wrapper for GetCurrentTimeUsec, to cover the
374
 * case where caller doesn't need either fractional seconds or tz offset.
375
 */
376
void
377
GetCurrentDateTime(struct pg_tm *tm)
378
0
{
379
0
  fsec_t    fsec;
380
381
0
  GetCurrentTimeUsec(tm, &fsec, NULL);
382
0
}
383
384
/*
385
 * GetCurrentTimeUsec()
386
 *
387
 * Get the transaction start time ("now()") broken down as a struct pg_tm,
388
 * including fractional seconds and timezone offset.  The time is converted
389
 * according to the session timezone setting.
390
 *
391
 * Callers may pass tzp = NULL if they don't need the offset, but this does
392
 * not affect the conversion behavior (unlike timestamp2tm()).
393
 *
394
 * Internally, we cache the result, since this could be called many times
395
 * in a transaction, within which now() doesn't change.
396
 */
397
void
398
GetCurrentTimeUsec(struct pg_tm *tm, fsec_t *fsec, int *tzp)
399
0
{
400
0
  TimestampTz cur_ts = GetCurrentTransactionStartTimestamp();
401
402
  /*
403
   * The cache key must include both current time and current timezone.  By
404
   * representing the timezone by just a pointer, we're assuming that
405
   * distinct timezone settings could never have the same pointer value.
406
   * This is true by virtue of the hashtable used inside pg_tzset();
407
   * however, it might need another look if we ever allow entries in that
408
   * hash to be recycled.
409
   */
410
0
  static TimestampTz cache_ts = 0;
411
0
  static pg_tz *cache_timezone = NULL;
412
0
  static struct pg_tm cache_tm;
413
0
  static fsec_t cache_fsec;
414
0
  static int  cache_tz;
415
416
0
  if (cur_ts != cache_ts || session_timezone != cache_timezone)
417
0
  {
418
    /*
419
     * Make sure cache is marked invalid in case of error after partial
420
     * update within timestamp2tm.
421
     */
422
0
    cache_timezone = NULL;
423
424
    /*
425
     * Perform the computation, storing results into cache.  We do not
426
     * really expect any error here, since current time surely ought to be
427
     * within range, but check just for sanity's sake.
428
     */
429
0
    if (timestamp2tm(cur_ts, &cache_tz, &cache_tm, &cache_fsec,
430
0
             NULL, session_timezone) != 0)
431
0
      ereport(ERROR,
432
0
          (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
433
0
           errmsg("timestamp out of range")));
434
435
    /* OK, so mark the cache valid. */
436
0
    cache_ts = cur_ts;
437
0
    cache_timezone = session_timezone;
438
0
  }
439
440
0
  *tm = cache_tm;
441
0
  *fsec = cache_fsec;
442
0
  if (tzp != NULL)
443
0
    *tzp = cache_tz;
444
0
}
445
446
447
/*
448
 * Append seconds and fractional seconds (if any) at *cp.
449
 *
450
 * precision is the max number of fraction digits, fillzeros says to
451
 * pad to two integral-seconds digits.
452
 *
453
 * Returns a pointer to the new end of string.  No NUL terminator is put
454
 * there; callers are responsible for NUL terminating str themselves.
455
 *
456
 * Note that any sign is stripped from the input sec and fsec values.
457
 */
458
static char *
459
AppendSeconds(char *cp, int sec, fsec_t fsec, int precision, bool fillzeros)
460
0
{
461
0
  Assert(precision >= 0);
462
463
0
  if (fillzeros)
464
0
    cp = pg_ultostr_zeropad(cp, abs(sec), 2);
465
0
  else
466
0
    cp = pg_ultostr(cp, abs(sec));
467
468
  /* fsec_t is just an int32 */
469
0
  if (fsec != 0)
470
0
  {
471
0
    int32   value = abs(fsec);
472
0
    char     *end = &cp[precision + 1];
473
0
    bool    gotnonzero = false;
474
475
0
    *cp++ = '.';
476
477
    /*
478
     * Append the fractional seconds part.  Note that we don't want any
479
     * trailing zeros here, so since we're building the number in reverse
480
     * we'll skip appending zeros until we've output a non-zero digit.
481
     */
482
0
    while (precision--)
483
0
    {
484
0
      int32   oldval = value;
485
0
      int32   remainder;
486
487
0
      value /= 10;
488
0
      remainder = oldval - value * 10;
489
490
      /* check if we got a non-zero */
491
0
      if (remainder)
492
0
        gotnonzero = true;
493
494
0
      if (gotnonzero)
495
0
        cp[precision] = '0' + remainder;
496
0
      else
497
0
        end = &cp[precision];
498
0
    }
499
500
    /*
501
     * If we still have a non-zero value then precision must have not been
502
     * enough to print the number.  We punt the problem to pg_ultostr(),
503
     * which will generate a correct answer in the minimum valid width.
504
     */
505
0
    if (value)
506
0
      return pg_ultostr(cp, abs(fsec));
507
508
0
    return end;
509
0
  }
510
0
  else
511
0
    return cp;
512
0
}
513
514
515
/*
516
 * Variant of above that's specialized to timestamp case.
517
 *
518
 * Returns a pointer to the new end of string.  No NUL terminator is put
519
 * there; callers are responsible for NUL terminating str themselves.
520
 */
521
static char *
522
AppendTimestampSeconds(char *cp, struct pg_tm *tm, fsec_t fsec)
523
0
{
524
0
  return AppendSeconds(cp, tm->tm_sec, fsec, MAX_TIMESTAMP_PRECISION, true);
525
0
}
526
527
528
/*
529
 * Add val * multiplier to *sum.
530
 * Returns true if successful, false on overflow.
531
 */
532
static bool
533
int64_multiply_add(int64 val, int64 multiplier, int64 *sum)
534
0
{
535
0
  int64   product;
536
537
0
  if (pg_mul_s64_overflow(val, multiplier, &product) ||
538
0
    pg_add_s64_overflow(*sum, product, sum))
539
0
    return false;
540
0
  return true;
541
0
}
542
543
/*
544
 * Multiply frac by scale (to produce microseconds) and add to itm_in->tm_usec.
545
 * Returns true if successful, false if itm_in overflows.
546
 */
547
static bool
548
AdjustFractMicroseconds(double frac, int64 scale,
549
            struct pg_itm_in *itm_in)
550
0
{
551
0
  int64   usec;
552
553
  /* Fast path for common case */
554
0
  if (frac == 0)
555
0
    return true;
556
557
  /*
558
   * We assume the input frac has abs value less than 1, so overflow of frac
559
   * or usec is not an issue for interesting values of scale.
560
   */
561
0
  frac *= scale;
562
0
  usec = (int64) frac;
563
564
  /* Round off any fractional microsecond */
565
0
  frac -= usec;
566
0
  if (frac > 0.5)
567
0
    usec++;
568
0
  else if (frac < -0.5)
569
0
    usec--;
570
571
0
  return !pg_add_s64_overflow(itm_in->tm_usec, usec, &itm_in->tm_usec);
572
0
}
573
574
/*
575
 * Multiply frac by scale (to produce days).  Add the integral part of the
576
 * result to itm_in->tm_mday, the fractional part to itm_in->tm_usec.
577
 * Returns true if successful, false if itm_in overflows.
578
 */
579
static bool
580
AdjustFractDays(double frac, int scale,
581
        struct pg_itm_in *itm_in)
582
0
{
583
0
  int     extra_days;
584
585
  /* Fast path for common case */
586
0
  if (frac == 0)
587
0
    return true;
588
589
  /*
590
   * We assume the input frac has abs value less than 1, so overflow of frac
591
   * or extra_days is not an issue.
592
   */
593
0
  frac *= scale;
594
0
  extra_days = (int) frac;
595
596
  /* ... but this could overflow, if tm_mday is already nonzero */
597
0
  if (pg_add_s32_overflow(itm_in->tm_mday, extra_days, &itm_in->tm_mday))
598
0
    return false;
599
600
  /* Handle any fractional day */
601
0
  frac -= extra_days;
602
0
  return AdjustFractMicroseconds(frac, USECS_PER_DAY, itm_in);
603
0
}
604
605
/*
606
 * Multiply frac by scale (to produce years), then further scale up to months.
607
 * Add the integral part of the result to itm_in->tm_mon, discarding any
608
 * fractional part.
609
 * Returns true if successful, false if itm_in overflows.
610
 */
611
static bool
612
AdjustFractYears(double frac, int scale,
613
         struct pg_itm_in *itm_in)
614
0
{
615
  /*
616
   * As above, we assume abs(frac) < 1, so this can't overflow for any
617
   * interesting value of scale.
618
   */
619
0
  int     extra_months = (int) rint(frac * scale * MONTHS_PER_YEAR);
620
621
0
  return !pg_add_s32_overflow(itm_in->tm_mon, extra_months, &itm_in->tm_mon);
622
0
}
623
624
/*
625
 * Add (val + fval) * scale to itm_in->tm_usec.
626
 * Returns true if successful, false if itm_in overflows.
627
 */
628
static bool
629
AdjustMicroseconds(int64 val, double fval, int64 scale,
630
           struct pg_itm_in *itm_in)
631
0
{
632
  /* Handle the integer part */
633
0
  if (!int64_multiply_add(val, scale, &itm_in->tm_usec))
634
0
    return false;
635
  /* Handle the float part */
636
0
  return AdjustFractMicroseconds(fval, scale, itm_in);
637
0
}
638
639
/*
640
 * Multiply val by scale (to produce days) and add to itm_in->tm_mday.
641
 * Returns true if successful, false if itm_in overflows.
642
 */
643
static bool
644
AdjustDays(int64 val, int scale, struct pg_itm_in *itm_in)
645
0
{
646
0
  int     days;
647
648
0
  if (val < INT_MIN || val > INT_MAX)
649
0
    return false;
650
0
  return !pg_mul_s32_overflow((int32) val, scale, &days) &&
651
0
    !pg_add_s32_overflow(itm_in->tm_mday, days, &itm_in->tm_mday);
652
0
}
653
654
/*
655
 * Add val to itm_in->tm_mon (no need for scale here, as val is always
656
 * in months already).
657
 * Returns true if successful, false if itm_in overflows.
658
 */
659
static bool
660
AdjustMonths(int64 val, struct pg_itm_in *itm_in)
661
0
{
662
0
  if (val < INT_MIN || val > INT_MAX)
663
0
    return false;
664
0
  return !pg_add_s32_overflow(itm_in->tm_mon, (int32) val, &itm_in->tm_mon);
665
0
}
666
667
/*
668
 * Multiply val by scale (to produce years) and add to itm_in->tm_year.
669
 * Returns true if successful, false if itm_in overflows.
670
 */
671
static bool
672
AdjustYears(int64 val, int scale,
673
      struct pg_itm_in *itm_in)
674
0
{
675
0
  int     years;
676
677
0
  if (val < INT_MIN || val > INT_MAX)
678
0
    return false;
679
0
  return !pg_mul_s32_overflow((int32) val, scale, &years) &&
680
0
    !pg_add_s32_overflow(itm_in->tm_year, years, &itm_in->tm_year);
681
0
}
682
683
684
/*
685
 * Parse the fractional part of a number (decimal point and optional digits,
686
 * followed by end of string).  Returns the fractional value into *frac.
687
 *
688
 * Returns 0 if successful, DTERR code if bogus input detected.
689
 */
690
static int
691
ParseFraction(char *cp, double *frac)
692
0
{
693
  /* Caller should always pass the start of the fraction part */
694
0
  Assert(*cp == '.');
695
696
  /*
697
   * We want to allow just "." with no digits, but some versions of strtod
698
   * will report EINVAL for that, so special-case it.
699
   */
700
0
  if (cp[1] == '\0')
701
0
  {
702
0
    *frac = 0;
703
0
  }
704
0
  else
705
0
  {
706
    /*
707
     * On the other hand, let's reject anything that's not digits after
708
     * the ".".  strtod is happy with input like ".123e9", but that'd
709
     * break callers' expectation that the result is in 0..1.  (It's quite
710
     * difficult to get here with such input, but not impossible.)
711
     */
712
0
    if (strspn(cp + 1, "0123456789") != strlen(cp + 1))
713
0
      return DTERR_BAD_FORMAT;
714
715
0
    errno = 0;
716
0
    *frac = strtod(cp, &cp);
717
    /* check for parse failure (probably redundant given prior check) */
718
0
    if (*cp != '\0' || errno != 0)
719
0
      return DTERR_BAD_FORMAT;
720
0
  }
721
0
  return 0;
722
0
}
723
724
/*
725
 * Fetch a fractional-second value with suitable error checking.
726
 * Same as ParseFraction except we convert the result to integer microseconds.
727
 */
728
static int
729
ParseFractionalSecond(char *cp, fsec_t *fsec)
730
0
{
731
0
  double    frac;
732
0
  int     dterr;
733
734
0
  dterr = ParseFraction(cp, &frac);
735
0
  if (dterr)
736
0
    return dterr;
737
0
  *fsec = rint(frac * 1000000);
738
0
  return 0;
739
0
}
740
741
742
/*
743
 * ParseDateTime()
744
 *  Break string into tokens based on a date/time context.
745
 *  Returns 0 if successful, DTERR code if bogus input detected.
746
 *
747
 * timestr - the input string
748
 * workbuf - workspace for field string storage. This must be
749
 *   larger than the largest legal input for this datetime type --
750
 *   some additional space will be needed to NUL terminate fields.
751
 * buflen - the size of workbuf
752
 * field[] - pointers to field strings are returned in this array
753
 * ftype[] - field type indicators are returned in this array
754
 * maxfields - dimensions of the above two arrays
755
 * *numfields - set to the actual number of fields detected
756
 *
757
 * The fields extracted from the input are stored as separate,
758
 * null-terminated strings in the workspace at workbuf. Any text is
759
 * converted to lower case.
760
 *
761
 * Several field types are assigned:
762
 *  DTK_NUMBER - digits and (possibly) a decimal point
763
 *  DTK_DATE - digits and two delimiters, or digits and text
764
 *  DTK_TIME - digits, colon delimiters, and possibly a decimal point
765
 *  DTK_STRING - text (no digits or punctuation)
766
 *  DTK_SPECIAL - leading "+" or "-" followed by text
767
 *  DTK_TZ - leading "+" or "-" followed by digits (also eats ':', '.', '-')
768
 *
769
 * Note that some field types can hold unexpected items:
770
 *  DTK_NUMBER can hold date fields (yy.ddd)
771
 *  DTK_STRING can hold months (January) and time zones (PST)
772
 *  DTK_DATE can hold time zone names (America/New_York, GMT-8)
773
 */
774
int
775
ParseDateTime(const char *timestr, char *workbuf, size_t buflen,
776
        char **field, int *ftype, int maxfields, int *numfields)
777
0
{
778
0
  int     nf = 0;
779
0
  const char *cp = timestr;
780
0
  char     *bufp = workbuf;
781
0
  const char *bufend = workbuf + buflen;
782
783
  /*
784
   * Set the character pointed-to by "bufptr" to "newchar", and increment
785
   * "bufptr". "end" gives the end of the buffer -- we return an error if
786
   * there is no space left to append a character to the buffer. Note that
787
   * "bufptr" is evaluated twice.
788
   */
789
0
#define APPEND_CHAR(bufptr, end, newchar)   \
790
0
  do                      \
791
0
  {                     \
792
0
    if (((bufptr) + 1) >= (end))     \
793
0
      return DTERR_BAD_FORMAT;     \
794
0
    *(bufptr)++ = newchar;          \
795
0
  } while (0)
796
797
  /* outer loop through fields */
798
0
  while (*cp != '\0')
799
0
  {
800
    /* Ignore spaces between fields */
801
0
    if (isspace((unsigned char) *cp))
802
0
    {
803
0
      cp++;
804
0
      continue;
805
0
    }
806
807
    /* Record start of current field */
808
0
    if (nf >= maxfields)
809
0
      return DTERR_BAD_FORMAT;
810
0
    field[nf] = bufp;
811
812
    /* leading digit? then date or time */
813
0
    if (isdigit((unsigned char) *cp))
814
0
    {
815
0
      APPEND_CHAR(bufp, bufend, *cp++);
816
0
      while (isdigit((unsigned char) *cp))
817
0
        APPEND_CHAR(bufp, bufend, *cp++);
818
819
      /* time field? */
820
0
      if (*cp == ':')
821
0
      {
822
0
        ftype[nf] = DTK_TIME;
823
0
        APPEND_CHAR(bufp, bufend, *cp++);
824
0
        while (isdigit((unsigned char) *cp) ||
825
0
             (*cp == ':') || (*cp == '.'))
826
0
          APPEND_CHAR(bufp, bufend, *cp++);
827
0
      }
828
      /* date field? allow embedded text month */
829
0
      else if (*cp == '-' || *cp == '/' || *cp == '.')
830
0
      {
831
        /* save delimiting character to use later */
832
0
        char    delim = *cp;
833
834
0
        APPEND_CHAR(bufp, bufend, *cp++);
835
        /* second field is all digits? then no embedded text month */
836
0
        if (isdigit((unsigned char) *cp))
837
0
        {
838
0
          ftype[nf] = ((delim == '.') ? DTK_NUMBER : DTK_DATE);
839
0
          while (isdigit((unsigned char) *cp))
840
0
            APPEND_CHAR(bufp, bufend, *cp++);
841
842
          /*
843
           * insist that the delimiters match to get a three-field
844
           * date.
845
           */
846
0
          if (*cp == delim)
847
0
          {
848
0
            ftype[nf] = DTK_DATE;
849
0
            APPEND_CHAR(bufp, bufend, *cp++);
850
0
            while (isdigit((unsigned char) *cp) || *cp == delim)
851
0
              APPEND_CHAR(bufp, bufend, *cp++);
852
0
          }
853
0
        }
854
0
        else
855
0
        {
856
0
          ftype[nf] = DTK_DATE;
857
0
          while (isalnum((unsigned char) *cp) || *cp == delim)
858
0
            APPEND_CHAR(bufp, bufend, pg_tolower((unsigned char) *cp++));
859
0
        }
860
0
      }
861
862
      /*
863
       * otherwise, number only and will determine year, month, day, or
864
       * concatenated fields later...
865
       */
866
0
      else
867
0
        ftype[nf] = DTK_NUMBER;
868
0
    }
869
    /* Leading decimal point? Then fractional seconds... */
870
0
    else if (*cp == '.')
871
0
    {
872
0
      APPEND_CHAR(bufp, bufend, *cp++);
873
0
      while (isdigit((unsigned char) *cp))
874
0
        APPEND_CHAR(bufp, bufend, *cp++);
875
876
0
      ftype[nf] = DTK_NUMBER;
877
0
    }
878
879
    /*
880
     * text? then date string, month, day of week, special, or timezone
881
     */
882
0
    else if (isalpha((unsigned char) *cp))
883
0
    {
884
0
      bool    is_date;
885
886
0
      ftype[nf] = DTK_STRING;
887
0
      APPEND_CHAR(bufp, bufend, pg_tolower((unsigned char) *cp++));
888
0
      while (isalpha((unsigned char) *cp))
889
0
        APPEND_CHAR(bufp, bufend, pg_tolower((unsigned char) *cp++));
890
891
      /*
892
       * Dates can have embedded '-', '/', or '.' separators.  It could
893
       * also be a timezone name containing embedded '/', '+', '-', '_',
894
       * or ':' (but '_' or ':' can't be the first punctuation). If the
895
       * next character is a digit or '+', we need to check whether what
896
       * we have so far is a recognized non-timezone keyword --- if so,
897
       * don't believe that this is the start of a timezone.
898
       */
899
0
      is_date = false;
900
0
      if (*cp == '-' || *cp == '/' || *cp == '.')
901
0
        is_date = true;
902
0
      else if (*cp == '+' || isdigit((unsigned char) *cp))
903
0
      {
904
0
        *bufp = '\0'; /* null-terminate current field value */
905
        /* we need search only the core token table, not TZ names */
906
0
        if (datebsearch(field[nf], datetktbl, szdatetktbl) == NULL)
907
0
          is_date = true;
908
0
      }
909
0
      if (is_date)
910
0
      {
911
0
        ftype[nf] = DTK_DATE;
912
0
        do
913
0
        {
914
0
          APPEND_CHAR(bufp, bufend, pg_tolower((unsigned char) *cp++));
915
0
        } while (*cp == '+' || *cp == '-' ||
916
0
             *cp == '/' || *cp == '_' ||
917
0
             *cp == '.' || *cp == ':' ||
918
0
             isalnum((unsigned char) *cp));
919
0
      }
920
0
    }
921
    /* sign? then special or numeric timezone */
922
0
    else if (*cp == '+' || *cp == '-')
923
0
    {
924
0
      APPEND_CHAR(bufp, bufend, *cp++);
925
      /* soak up leading whitespace */
926
0
      while (isspace((unsigned char) *cp))
927
0
        cp++;
928
      /* numeric timezone? */
929
      /* note that "DTK_TZ" could also be a signed float or yyyy-mm */
930
0
      if (isdigit((unsigned char) *cp))
931
0
      {
932
0
        ftype[nf] = DTK_TZ;
933
0
        APPEND_CHAR(bufp, bufend, *cp++);
934
0
        while (isdigit((unsigned char) *cp) ||
935
0
             *cp == ':' || *cp == '.' || *cp == '-')
936
0
          APPEND_CHAR(bufp, bufend, *cp++);
937
0
      }
938
      /* special? */
939
0
      else if (isalpha((unsigned char) *cp))
940
0
      {
941
0
        ftype[nf] = DTK_SPECIAL;
942
0
        APPEND_CHAR(bufp, bufend, pg_tolower((unsigned char) *cp++));
943
0
        while (isalpha((unsigned char) *cp))
944
0
          APPEND_CHAR(bufp, bufend, pg_tolower((unsigned char) *cp++));
945
0
      }
946
      /* otherwise something wrong... */
947
0
      else
948
0
        return DTERR_BAD_FORMAT;
949
0
    }
950
    /* ignore other punctuation but use as delimiter */
951
0
    else if (ispunct((unsigned char) *cp))
952
0
    {
953
0
      cp++;
954
0
      continue;
955
0
    }
956
    /* otherwise, something is not right... */
957
0
    else
958
0
      return DTERR_BAD_FORMAT;
959
960
    /* force in a delimiter after each field */
961
0
    *bufp++ = '\0';
962
0
    nf++;
963
0
  }
964
965
0
  *numfields = nf;
966
967
0
  return 0;
968
0
}
969
970
971
/*
972
 * DecodeDateTime()
973
 * Interpret previously parsed fields for general date and time.
974
 * Return 0 if full date, 1 if only time, and negative DTERR code if problems.
975
 * (Currently, all callers treat 1 as an error return too.)
976
 *
977
 * Inputs are field[] and ftype[] arrays, of length nf.
978
 * Other arguments are outputs.
979
 *
980
 *    External format(s):
981
 *        "<weekday> <month>-<day>-<year> <hour>:<minute>:<second>"
982
 *        "Fri Feb-7-1997 15:23:27"
983
 *        "Feb-7-1997 15:23:27"
984
 *        "2-7-1997 15:23:27"
985
 *        "1997-2-7 15:23:27"
986
 *        "1997.038 15:23:27"   (day of year 1-366)
987
 *    Also supports input in compact time:
988
 *        "970207 152327"
989
 *        "97038 152327"
990
 *        "20011225T040506.789-07"
991
 *
992
 * Use the system-provided functions to get the current time zone
993
 * if not specified in the input string.
994
 *
995
 * If the date is outside the range of pg_time_t (in practice that could only
996
 * happen if pg_time_t is just 32 bits), then assume UTC time zone - thomas
997
 * 1997-05-27
998
 */
999
int
1000
DecodeDateTime(char **field, int *ftype, int nf,
1001
         int *dtype, struct pg_tm *tm, fsec_t *fsec, int *tzp,
1002
         DateTimeErrorExtra *extra)
1003
0
{
1004
0
  int     fmask = 0,
1005
0
        tmask,
1006
0
        type;
1007
0
  int     ptype = 0;    /* "prefix type" for ISO and Julian formats */
1008
0
  int     i;
1009
0
  int     val;
1010
0
  int     dterr;
1011
0
  int     mer = HR24;
1012
0
  bool    haveTextMonth = false;
1013
0
  bool    isjulian = false;
1014
0
  bool    is2digits = false;
1015
0
  bool    bc = false;
1016
0
  pg_tz    *namedTz = NULL;
1017
0
  pg_tz    *abbrevTz = NULL;
1018
0
  pg_tz    *valtz;
1019
0
  char     *abbrev = NULL;
1020
0
  struct pg_tm cur_tm;
1021
1022
  /*
1023
   * We'll insist on at least all of the date fields, but initialize the
1024
   * remaining fields in case they are not set later...
1025
   */
1026
0
  *dtype = DTK_DATE;
1027
0
  tm->tm_hour = 0;
1028
0
  tm->tm_min = 0;
1029
0
  tm->tm_sec = 0;
1030
0
  *fsec = 0;
1031
  /* don't know daylight savings time status apriori */
1032
0
  tm->tm_isdst = -1;
1033
0
  if (tzp != NULL)
1034
0
    *tzp = 0;
1035
1036
0
  for (i = 0; i < nf; i++)
1037
0
  {
1038
0
    switch (ftype[i])
1039
0
    {
1040
0
      case DTK_DATE:
1041
1042
        /*
1043
         * Integral julian day with attached time zone? All other
1044
         * forms with JD will be separated into distinct fields, so we
1045
         * handle just this case here.
1046
         */
1047
0
        if (ptype == DTK_JULIAN)
1048
0
        {
1049
0
          char     *cp;
1050
0
          int     jday;
1051
1052
0
          if (tzp == NULL)
1053
0
            return DTERR_BAD_FORMAT;
1054
1055
0
          errno = 0;
1056
0
          jday = strtoint(field[i], &cp, 10);
1057
0
          if (errno == ERANGE || jday < 0)
1058
0
            return DTERR_FIELD_OVERFLOW;
1059
1060
0
          j2date(jday, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
1061
0
          isjulian = true;
1062
1063
          /* Get the time zone from the end of the string */
1064
0
          dterr = DecodeTimezone(cp, tzp);
1065
0
          if (dterr)
1066
0
            return dterr;
1067
1068
0
          tmask = DTK_DATE_M | DTK_TIME_M | DTK_M(TZ);
1069
0
          ptype = 0;
1070
0
          break;
1071
0
        }
1072
1073
        /*
1074
         * Already have a date? Then this might be a time zone name
1075
         * with embedded punctuation (e.g. "America/New_York") or a
1076
         * run-together time with trailing time zone (e.g. hhmmss-zz).
1077
         * - thomas 2001-12-25
1078
         *
1079
         * We consider it a time zone if we already have month & day.
1080
         * This is to allow the form "mmm dd hhmmss tz year", which
1081
         * we've historically accepted.
1082
         */
1083
0
        else if (ptype != 0 ||
1084
0
             ((fmask & (DTK_M(MONTH) | DTK_M(DAY))) ==
1085
0
              (DTK_M(MONTH) | DTK_M(DAY))))
1086
0
        {
1087
          /* No time zone accepted? Then quit... */
1088
0
          if (tzp == NULL)
1089
0
            return DTERR_BAD_FORMAT;
1090
1091
0
          if (isdigit((unsigned char) *field[i]) || ptype != 0)
1092
0
          {
1093
0
            char     *cp;
1094
1095
            /*
1096
             * Allow a preceding "t" field, but no other units.
1097
             */
1098
0
            if (ptype != 0)
1099
0
            {
1100
              /* Sanity check; should not fail this test */
1101
0
              if (ptype != DTK_TIME)
1102
0
                return DTERR_BAD_FORMAT;
1103
0
              ptype = 0;
1104
0
            }
1105
1106
            /*
1107
             * Starts with a digit but we already have a time
1108
             * field? Then we are in trouble with a date and time
1109
             * already...
1110
             */
1111
0
            if ((fmask & DTK_TIME_M) == DTK_TIME_M)
1112
0
              return DTERR_BAD_FORMAT;
1113
1114
0
            if ((cp = strchr(field[i], '-')) == NULL)
1115
0
              return DTERR_BAD_FORMAT;
1116
1117
            /* Get the time zone from the end of the string */
1118
0
            dterr = DecodeTimezone(cp, tzp);
1119
0
            if (dterr)
1120
0
              return dterr;
1121
0
            *cp = '\0';
1122
1123
            /*
1124
             * Then read the rest of the field as a concatenated
1125
             * time
1126
             */
1127
0
            dterr = DecodeNumberField(strlen(field[i]), field[i],
1128
0
                          fmask,
1129
0
                          &tmask, tm,
1130
0
                          fsec, &is2digits);
1131
0
            if (dterr < 0)
1132
0
              return dterr;
1133
1134
            /*
1135
             * modify tmask after returning from
1136
             * DecodeNumberField()
1137
             */
1138
0
            tmask |= DTK_M(TZ);
1139
0
          }
1140
0
          else
1141
0
          {
1142
0
            namedTz = pg_tzset(field[i]);
1143
0
            if (!namedTz)
1144
0
            {
1145
0
              extra->dtee_timezone = field[i];
1146
0
              return DTERR_BAD_TIMEZONE;
1147
0
            }
1148
            /* we'll apply the zone setting below */
1149
0
            tmask = DTK_M(TZ);
1150
0
          }
1151
0
        }
1152
0
        else
1153
0
        {
1154
0
          dterr = DecodeDate(field[i], fmask,
1155
0
                     &tmask, &is2digits, tm);
1156
0
          if (dterr)
1157
0
            return dterr;
1158
0
        }
1159
0
        break;
1160
1161
0
      case DTK_TIME:
1162
1163
        /*
1164
         * This might be an ISO time following a "t" field.
1165
         */
1166
0
        if (ptype != 0)
1167
0
        {
1168
          /* Sanity check; should not fail this test */
1169
0
          if (ptype != DTK_TIME)
1170
0
            return DTERR_BAD_FORMAT;
1171
0
          ptype = 0;
1172
0
        }
1173
0
        dterr = DecodeTime(field[i], fmask, INTERVAL_FULL_RANGE,
1174
0
                   &tmask, tm, fsec);
1175
0
        if (dterr)
1176
0
          return dterr;
1177
1178
        /* check for time overflow */
1179
0
        if (time_overflows(tm->tm_hour, tm->tm_min, tm->tm_sec,
1180
0
                   *fsec))
1181
0
          return DTERR_FIELD_OVERFLOW;
1182
0
        break;
1183
1184
0
      case DTK_TZ:
1185
0
        {
1186
0
          int     tz;
1187
1188
0
          if (tzp == NULL)
1189
0
            return DTERR_BAD_FORMAT;
1190
1191
0
          dterr = DecodeTimezone(field[i], &tz);
1192
0
          if (dterr)
1193
0
            return dterr;
1194
0
          *tzp = tz;
1195
0
          tmask = DTK_M(TZ);
1196
0
        }
1197
0
        break;
1198
1199
0
      case DTK_NUMBER:
1200
1201
        /*
1202
         * Deal with cases where previous field labeled this one
1203
         */
1204
0
        if (ptype != 0)
1205
0
        {
1206
0
          char     *cp;
1207
0
          int     value;
1208
1209
0
          errno = 0;
1210
0
          value = strtoint(field[i], &cp, 10);
1211
0
          if (errno == ERANGE)
1212
0
            return DTERR_FIELD_OVERFLOW;
1213
0
          if (*cp != '.' && *cp != '\0')
1214
0
            return DTERR_BAD_FORMAT;
1215
1216
0
          switch (ptype)
1217
0
          {
1218
0
            case DTK_JULIAN:
1219
              /* previous field was a label for "julian date" */
1220
0
              if (value < 0)
1221
0
                return DTERR_FIELD_OVERFLOW;
1222
0
              tmask = DTK_DATE_M;
1223
0
              j2date(value, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
1224
0
              isjulian = true;
1225
1226
              /* fractional Julian Day? */
1227
0
              if (*cp == '.')
1228
0
              {
1229
0
                double    time;
1230
1231
0
                dterr = ParseFraction(cp, &time);
1232
0
                if (dterr)
1233
0
                  return dterr;
1234
0
                time *= USECS_PER_DAY;
1235
0
                dt2time(time,
1236
0
                    &tm->tm_hour, &tm->tm_min,
1237
0
                    &tm->tm_sec, fsec);
1238
0
                tmask |= DTK_TIME_M;
1239
0
              }
1240
0
              break;
1241
1242
0
            case DTK_TIME:
1243
              /* previous field was "t" for ISO time */
1244
0
              dterr = DecodeNumberField(strlen(field[i]), field[i],
1245
0
                            (fmask | DTK_DATE_M),
1246
0
                            &tmask, tm,
1247
0
                            fsec, &is2digits);
1248
0
              if (dterr < 0)
1249
0
                return dterr;
1250
0
              if (tmask != DTK_TIME_M)
1251
0
                return DTERR_BAD_FORMAT;
1252
0
              break;
1253
1254
0
            default:
1255
0
              return DTERR_BAD_FORMAT;
1256
0
              break;
1257
0
          }
1258
1259
0
          ptype = 0;
1260
0
          *dtype = DTK_DATE;
1261
0
        }
1262
0
        else
1263
0
        {
1264
0
          char     *cp;
1265
0
          int     flen;
1266
1267
0
          flen = strlen(field[i]);
1268
0
          cp = strchr(field[i], '.');
1269
1270
          /* Embedded decimal and no date yet? */
1271
0
          if (cp != NULL && !(fmask & DTK_DATE_M))
1272
0
          {
1273
0
            dterr = DecodeDate(field[i], fmask,
1274
0
                       &tmask, &is2digits, tm);
1275
0
            if (dterr)
1276
0
              return dterr;
1277
0
          }
1278
          /* embedded decimal and several digits before? */
1279
0
          else if (cp != NULL && flen - strlen(cp) > 2)
1280
0
          {
1281
            /*
1282
             * Interpret as a concatenated date or time Set the
1283
             * type field to allow decoding other fields later.
1284
             * Example: 20011223 or 040506
1285
             */
1286
0
            dterr = DecodeNumberField(flen, field[i], fmask,
1287
0
                          &tmask, tm,
1288
0
                          fsec, &is2digits);
1289
0
            if (dterr < 0)
1290
0
              return dterr;
1291
0
          }
1292
1293
          /*
1294
           * Is this a YMD or HMS specification, or a year number?
1295
           * YMD and HMS are required to be six digits or more, so
1296
           * if it is 5 digits, it is a year.  If it is six or more
1297
           * digits, we assume it is YMD or HMS unless no date and
1298
           * no time values have been specified.  This forces 6+
1299
           * digit years to be at the end of the string, or to use
1300
           * the ISO date specification.
1301
           */
1302
0
          else if (flen >= 6 && (!(fmask & DTK_DATE_M) ||
1303
0
                       !(fmask & DTK_TIME_M)))
1304
0
          {
1305
0
            dterr = DecodeNumberField(flen, field[i], fmask,
1306
0
                          &tmask, tm,
1307
0
                          fsec, &is2digits);
1308
0
            if (dterr < 0)
1309
0
              return dterr;
1310
0
          }
1311
          /* otherwise it is a single date/time field... */
1312
0
          else
1313
0
          {
1314
0
            dterr = DecodeNumber(flen, field[i],
1315
0
                       haveTextMonth, fmask,
1316
0
                       &tmask, tm,
1317
0
                       fsec, &is2digits);
1318
0
            if (dterr)
1319
0
              return dterr;
1320
0
          }
1321
0
        }
1322
0
        break;
1323
1324
0
      case DTK_STRING:
1325
0
      case DTK_SPECIAL:
1326
        /* timezone abbrevs take precedence over built-in tokens */
1327
0
        dterr = DecodeTimezoneAbbrev(i, field[i],
1328
0
                       &type, &val, &valtz, extra);
1329
0
        if (dterr)
1330
0
          return dterr;
1331
0
        if (type == UNKNOWN_FIELD)
1332
0
          type = DecodeSpecial(i, field[i], &val);
1333
0
        if (type == IGNORE_DTF)
1334
0
          continue;
1335
1336
0
        tmask = DTK_M(type);
1337
0
        switch (type)
1338
0
        {
1339
0
          case RESERV:
1340
0
            switch (val)
1341
0
            {
1342
0
              case DTK_NOW:
1343
0
                tmask = (DTK_DATE_M | DTK_TIME_M | DTK_M(TZ));
1344
0
                *dtype = DTK_DATE;
1345
0
                GetCurrentTimeUsec(tm, fsec, tzp);
1346
0
                break;
1347
1348
0
              case DTK_YESTERDAY:
1349
0
                tmask = DTK_DATE_M;
1350
0
                *dtype = DTK_DATE;
1351
0
                GetCurrentDateTime(&cur_tm);
1352
0
                j2date(date2j(cur_tm.tm_year, cur_tm.tm_mon, cur_tm.tm_mday) - 1,
1353
0
                     &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
1354
0
                break;
1355
1356
0
              case DTK_TODAY:
1357
0
                tmask = DTK_DATE_M;
1358
0
                *dtype = DTK_DATE;
1359
0
                GetCurrentDateTime(&cur_tm);
1360
0
                tm->tm_year = cur_tm.tm_year;
1361
0
                tm->tm_mon = cur_tm.tm_mon;
1362
0
                tm->tm_mday = cur_tm.tm_mday;
1363
0
                break;
1364
1365
0
              case DTK_TOMORROW:
1366
0
                tmask = DTK_DATE_M;
1367
0
                *dtype = DTK_DATE;
1368
0
                GetCurrentDateTime(&cur_tm);
1369
0
                j2date(date2j(cur_tm.tm_year, cur_tm.tm_mon, cur_tm.tm_mday) + 1,
1370
0
                     &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
1371
0
                break;
1372
1373
0
              case DTK_ZULU:
1374
0
                tmask = (DTK_TIME_M | DTK_M(TZ));
1375
0
                *dtype = DTK_DATE;
1376
0
                tm->tm_hour = 0;
1377
0
                tm->tm_min = 0;
1378
0
                tm->tm_sec = 0;
1379
0
                if (tzp != NULL)
1380
0
                  *tzp = 0;
1381
0
                break;
1382
1383
0
              case DTK_EPOCH:
1384
0
              case DTK_LATE:
1385
0
              case DTK_EARLY:
1386
0
                tmask = (DTK_DATE_M | DTK_TIME_M | DTK_M(TZ));
1387
0
                *dtype = val;
1388
                /* caller ignores tm for these dtype codes */
1389
0
                break;
1390
1391
0
              default:
1392
0
                elog(ERROR, "unrecognized RESERV datetime token: %d",
1393
0
                   val);
1394
0
            }
1395
1396
0
            break;
1397
1398
0
          case MONTH:
1399
1400
            /*
1401
             * already have a (numeric) month? then see if we can
1402
             * substitute...
1403
             */
1404
0
            if ((fmask & DTK_M(MONTH)) && !haveTextMonth &&
1405
0
              !(fmask & DTK_M(DAY)) && tm->tm_mon >= 1 &&
1406
0
              tm->tm_mon <= 31)
1407
0
            {
1408
0
              tm->tm_mday = tm->tm_mon;
1409
0
              tmask = DTK_M(DAY);
1410
0
            }
1411
0
            haveTextMonth = true;
1412
0
            tm->tm_mon = val;
1413
0
            break;
1414
1415
0
          case DTZMOD:
1416
1417
            /*
1418
             * daylight savings time modifier (solves "MET DST"
1419
             * syntax)
1420
             */
1421
0
            tmask |= DTK_M(DTZ);
1422
0
            tm->tm_isdst = 1;
1423
0
            if (tzp == NULL)
1424
0
              return DTERR_BAD_FORMAT;
1425
0
            *tzp -= val;
1426
0
            break;
1427
1428
0
          case DTZ:
1429
1430
            /*
1431
             * set mask for TZ here _or_ check for DTZ later when
1432
             * getting default timezone
1433
             */
1434
0
            tmask |= DTK_M(TZ);
1435
0
            tm->tm_isdst = 1;
1436
0
            if (tzp == NULL)
1437
0
              return DTERR_BAD_FORMAT;
1438
0
            *tzp = -val;
1439
0
            break;
1440
1441
0
          case TZ:
1442
0
            tm->tm_isdst = 0;
1443
0
            if (tzp == NULL)
1444
0
              return DTERR_BAD_FORMAT;
1445
0
            *tzp = -val;
1446
0
            break;
1447
1448
0
          case DYNTZ:
1449
0
            tmask |= DTK_M(TZ);
1450
0
            if (tzp == NULL)
1451
0
              return DTERR_BAD_FORMAT;
1452
            /* we'll determine the actual offset later */
1453
0
            abbrevTz = valtz;
1454
0
            abbrev = field[i];
1455
0
            break;
1456
1457
0
          case AMPM:
1458
0
            mer = val;
1459
0
            break;
1460
1461
0
          case ADBC:
1462
0
            bc = (val == BC);
1463
0
            break;
1464
1465
0
          case DOW:
1466
0
            tm->tm_wday = val;
1467
0
            break;
1468
1469
0
          case UNITS:
1470
0
            tmask = 0;
1471
            /* reject consecutive unhandled units */
1472
0
            if (ptype != 0)
1473
0
              return DTERR_BAD_FORMAT;
1474
0
            ptype = val;
1475
0
            break;
1476
1477
0
          case ISOTIME:
1478
1479
            /*
1480
             * This is a filler field "t" indicating that the next
1481
             * field is time. Try to verify that this is sensible.
1482
             */
1483
0
            tmask = 0;
1484
1485
            /* No preceding date? Then quit... */
1486
0
            if ((fmask & DTK_DATE_M) != DTK_DATE_M)
1487
0
              return DTERR_BAD_FORMAT;
1488
1489
            /* reject consecutive unhandled units */
1490
0
            if (ptype != 0)
1491
0
              return DTERR_BAD_FORMAT;
1492
0
            ptype = val;
1493
0
            break;
1494
1495
0
          case UNKNOWN_FIELD:
1496
1497
            /*
1498
             * Before giving up and declaring error, check to see
1499
             * if it is an all-alpha timezone name.
1500
             */
1501
0
            namedTz = pg_tzset(field[i]);
1502
0
            if (!namedTz)
1503
0
              return DTERR_BAD_FORMAT;
1504
            /* we'll apply the zone setting below */
1505
0
            tmask = DTK_M(TZ);
1506
0
            break;
1507
1508
0
          default:
1509
0
            return DTERR_BAD_FORMAT;
1510
0
        }
1511
0
        break;
1512
1513
0
      default:
1514
0
        return DTERR_BAD_FORMAT;
1515
0
    }
1516
1517
0
    if (tmask & fmask)
1518
0
      return DTERR_BAD_FORMAT;
1519
0
    fmask |= tmask;
1520
0
  }              /* end loop over fields */
1521
1522
  /* reject if prefix type appeared and was never handled */
1523
0
  if (ptype != 0)
1524
0
    return DTERR_BAD_FORMAT;
1525
1526
  /* do additional checking for normal date specs (but not "infinity" etc) */
1527
0
  if (*dtype == DTK_DATE)
1528
0
  {
1529
    /* do final checking/adjustment of Y/M/D fields */
1530
0
    dterr = ValidateDate(fmask, isjulian, is2digits, bc, tm);
1531
0
    if (dterr)
1532
0
      return dterr;
1533
1534
    /* handle AM/PM */
1535
0
    if (mer != HR24 && tm->tm_hour > HOURS_PER_DAY / 2)
1536
0
      return DTERR_FIELD_OVERFLOW;
1537
0
    if (mer == AM && tm->tm_hour == HOURS_PER_DAY / 2)
1538
0
      tm->tm_hour = 0;
1539
0
    else if (mer == PM && tm->tm_hour != HOURS_PER_DAY / 2)
1540
0
      tm->tm_hour += HOURS_PER_DAY / 2;
1541
1542
    /* check for incomplete input */
1543
0
    if ((fmask & DTK_DATE_M) != DTK_DATE_M)
1544
0
    {
1545
0
      if ((fmask & DTK_TIME_M) == DTK_TIME_M)
1546
0
        return 1;
1547
0
      return DTERR_BAD_FORMAT;
1548
0
    }
1549
1550
    /*
1551
     * If we had a full timezone spec, compute the offset (we could not do
1552
     * it before, because we need the date to resolve DST status).
1553
     */
1554
0
    if (namedTz != NULL)
1555
0
    {
1556
      /* daylight savings time modifier disallowed with full TZ */
1557
0
      if (fmask & DTK_M(DTZMOD))
1558
0
        return DTERR_BAD_FORMAT;
1559
1560
0
      *tzp = DetermineTimeZoneOffset(tm, namedTz);
1561
0
    }
1562
1563
    /*
1564
     * Likewise, if we had a dynamic timezone abbreviation, resolve it
1565
     * now.
1566
     */
1567
0
    if (abbrevTz != NULL)
1568
0
    {
1569
      /* daylight savings time modifier disallowed with dynamic TZ */
1570
0
      if (fmask & DTK_M(DTZMOD))
1571
0
        return DTERR_BAD_FORMAT;
1572
1573
0
      *tzp = DetermineTimeZoneAbbrevOffset(tm, abbrev, abbrevTz);
1574
0
    }
1575
1576
    /* timezone not specified? then use session timezone */
1577
0
    if (tzp != NULL && !(fmask & DTK_M(TZ)))
1578
0
    {
1579
      /*
1580
       * daylight savings time modifier but no standard timezone? then
1581
       * error
1582
       */
1583
0
      if (fmask & DTK_M(DTZMOD))
1584
0
        return DTERR_BAD_FORMAT;
1585
1586
0
      *tzp = DetermineTimeZoneOffset(tm, session_timezone);
1587
0
    }
1588
0
  }
1589
1590
0
  return 0;
1591
0
}
1592
1593
1594
/*
1595
 * DetermineTimeZoneOffset()
1596
 *
1597
 * Given a struct pg_tm in which tm_year, tm_mon, tm_mday, tm_hour, tm_min,
1598
 * and tm_sec fields are set, and a zic-style time zone definition, determine
1599
 * the applicable GMT offset and daylight-savings status at that time.
1600
 * Set the struct pg_tm's tm_isdst field accordingly, and return the GMT
1601
 * offset as the function result.
1602
 *
1603
 * Note: if the date is out of the range we can deal with, we return zero
1604
 * as the GMT offset and set tm_isdst = 0.  We don't throw an error here,
1605
 * though probably some higher-level code will.
1606
 */
1607
int
1608
DetermineTimeZoneOffset(struct pg_tm *tm, pg_tz *tzp)
1609
0
{
1610
0
  pg_time_t t;
1611
1612
0
  return DetermineTimeZoneOffsetInternal(tm, tzp, &t);
1613
0
}
1614
1615
1616
/*
1617
 * DetermineTimeZoneOffsetInternal()
1618
 *
1619
 * As above, but also return the actual UTC time imputed to the date/time
1620
 * into *tp.
1621
 *
1622
 * In event of an out-of-range date, we punt by returning zero into *tp.
1623
 * This is okay for the immediate callers but is a good reason for not
1624
 * exposing this worker function globally.
1625
 *
1626
 * Note: it might seem that we should use mktime() for this, but bitter
1627
 * experience teaches otherwise.  This code is much faster than most versions
1628
 * of mktime(), anyway.
1629
 */
1630
static int
1631
DetermineTimeZoneOffsetInternal(struct pg_tm *tm, pg_tz *tzp, pg_time_t *tp)
1632
0
{
1633
0
  int     date,
1634
0
        sec;
1635
0
  pg_time_t day,
1636
0
        mytime,
1637
0
        prevtime,
1638
0
        boundary,
1639
0
        beforetime,
1640
0
        aftertime;
1641
0
  long int  before_gmtoff,
1642
0
        after_gmtoff;
1643
0
  int     before_isdst,
1644
0
        after_isdst;
1645
0
  int     res;
1646
1647
  /*
1648
   * First, generate the pg_time_t value corresponding to the given
1649
   * y/m/d/h/m/s taken as GMT time.  If this overflows, punt and decide the
1650
   * timezone is GMT.  (For a valid Julian date, integer overflow should be
1651
   * impossible with 64-bit pg_time_t, but let's check for safety.)
1652
   */
1653
0
  if (!IS_VALID_JULIAN(tm->tm_year, tm->tm_mon, tm->tm_mday))
1654
0
    goto overflow;
1655
0
  date = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - UNIX_EPOCH_JDATE;
1656
1657
0
  day = ((pg_time_t) date) * SECS_PER_DAY;
1658
0
  if (day / SECS_PER_DAY != date)
1659
0
    goto overflow;
1660
0
  sec = tm->tm_sec + (tm->tm_min + tm->tm_hour * MINS_PER_HOUR) * SECS_PER_MINUTE;
1661
0
  mytime = day + sec;
1662
  /* since sec >= 0, overflow could only be from +day to -mytime */
1663
0
  if (mytime < 0 && day > 0)
1664
0
    goto overflow;
1665
1666
  /*
1667
   * Find the DST time boundary just before or following the target time. We
1668
   * assume that all zones have GMT offsets less than 24 hours, and that DST
1669
   * boundaries can't be closer together than 48 hours, so backing up 24
1670
   * hours and finding the "next" boundary will work.
1671
   */
1672
0
  prevtime = mytime - SECS_PER_DAY;
1673
0
  if (mytime < 0 && prevtime > 0)
1674
0
    goto overflow;
1675
1676
0
  res = pg_next_dst_boundary(&prevtime,
1677
0
                 &before_gmtoff, &before_isdst,
1678
0
                 &boundary,
1679
0
                 &after_gmtoff, &after_isdst,
1680
0
                 tzp);
1681
0
  if (res < 0)
1682
0
    goto overflow;     /* failure? */
1683
1684
0
  if (res == 0)
1685
0
  {
1686
    /* Non-DST zone, life is simple */
1687
0
    tm->tm_isdst = before_isdst;
1688
0
    *tp = mytime - before_gmtoff;
1689
0
    return -(int) before_gmtoff;
1690
0
  }
1691
1692
  /*
1693
   * Form the candidate pg_time_t values with local-time adjustment
1694
   */
1695
0
  beforetime = mytime - before_gmtoff;
1696
0
  if ((before_gmtoff > 0 &&
1697
0
     mytime < 0 && beforetime > 0) ||
1698
0
    (before_gmtoff <= 0 &&
1699
0
     mytime > 0 && beforetime < 0))
1700
0
    goto overflow;
1701
0
  aftertime = mytime - after_gmtoff;
1702
0
  if ((after_gmtoff > 0 &&
1703
0
     mytime < 0 && aftertime > 0) ||
1704
0
    (after_gmtoff <= 0 &&
1705
0
     mytime > 0 && aftertime < 0))
1706
0
    goto overflow;
1707
1708
  /*
1709
   * If both before or both after the boundary time, we know what to do. The
1710
   * boundary time itself is considered to be after the transition, which
1711
   * means we can accept aftertime == boundary in the second case.
1712
   */
1713
0
  if (beforetime < boundary && aftertime < boundary)
1714
0
  {
1715
0
    tm->tm_isdst = before_isdst;
1716
0
    *tp = beforetime;
1717
0
    return -(int) before_gmtoff;
1718
0
  }
1719
0
  if (beforetime > boundary && aftertime >= boundary)
1720
0
  {
1721
0
    tm->tm_isdst = after_isdst;
1722
0
    *tp = aftertime;
1723
0
    return -(int) after_gmtoff;
1724
0
  }
1725
1726
  /*
1727
   * It's an invalid or ambiguous time due to timezone transition.  In a
1728
   * spring-forward transition, prefer the "before" interpretation; in a
1729
   * fall-back transition, prefer "after".  (We used to define and implement
1730
   * this test as "prefer the standard-time interpretation", but that rule
1731
   * does not help to resolve the behavior when both times are reported as
1732
   * standard time; which does happen, eg Europe/Moscow in Oct 2014.  Also,
1733
   * in some zones such as Europe/Dublin, there is widespread confusion
1734
   * about which time offset is "standard" time, so it's fortunate that our
1735
   * behavior doesn't depend on that.)
1736
   */
1737
0
  if (beforetime > aftertime)
1738
0
  {
1739
0
    tm->tm_isdst = before_isdst;
1740
0
    *tp = beforetime;
1741
0
    return -(int) before_gmtoff;
1742
0
  }
1743
0
  tm->tm_isdst = after_isdst;
1744
0
  *tp = aftertime;
1745
0
  return -(int) after_gmtoff;
1746
1747
0
overflow:
1748
  /* Given date is out of range, so assume UTC */
1749
0
  tm->tm_isdst = 0;
1750
0
  *tp = 0;
1751
0
  return 0;
1752
0
}
1753
1754
1755
/*
1756
 * DetermineTimeZoneAbbrevOffset()
1757
 *
1758
 * Determine the GMT offset and DST flag to be attributed to a dynamic
1759
 * time zone abbreviation, that is one whose meaning has changed over time.
1760
 * *tm contains the local time at which the meaning should be determined,
1761
 * and tm->tm_isdst receives the DST flag.
1762
 *
1763
 * This differs from the behavior of DetermineTimeZoneOffset() in that a
1764
 * standard-time or daylight-time abbreviation forces use of the corresponding
1765
 * GMT offset even when the zone was then in DS or standard time respectively.
1766
 * (However, that happens only if we can match the given abbreviation to some
1767
 * abbreviation that appears in the IANA timezone data.  Otherwise, we fall
1768
 * back to doing DetermineTimeZoneOffset().)
1769
 */
1770
int
1771
DetermineTimeZoneAbbrevOffset(struct pg_tm *tm, const char *abbr, pg_tz *tzp)
1772
0
{
1773
0
  pg_time_t t;
1774
0
  int     zone_offset;
1775
0
  int     abbr_offset;
1776
0
  int     abbr_isdst;
1777
1778
  /*
1779
   * Compute the UTC time we want to probe at.  (In event of overflow, we'll
1780
   * probe at the epoch, which is a bit random but probably doesn't matter.)
1781
   */
1782
0
  zone_offset = DetermineTimeZoneOffsetInternal(tm, tzp, &t);
1783
1784
  /*
1785
   * Try to match the abbreviation to something in the zone definition.
1786
   */
1787
0
  if (DetermineTimeZoneAbbrevOffsetInternal(t, abbr, tzp,
1788
0
                        &abbr_offset, &abbr_isdst))
1789
0
  {
1790
    /* Success, so use the abbrev-specific answers. */
1791
0
    tm->tm_isdst = abbr_isdst;
1792
0
    return abbr_offset;
1793
0
  }
1794
1795
  /*
1796
   * No match, so use the answers we already got from
1797
   * DetermineTimeZoneOffsetInternal.
1798
   */
1799
0
  return zone_offset;
1800
0
}
1801
1802
1803
/*
1804
 * DetermineTimeZoneAbbrevOffsetTS()
1805
 *
1806
 * As above but the probe time is specified as a TimestampTz (hence, UTC time),
1807
 * and DST status is returned into *isdst rather than into tm->tm_isdst.
1808
 */
1809
int
1810
DetermineTimeZoneAbbrevOffsetTS(TimestampTz ts, const char *abbr,
1811
                pg_tz *tzp, int *isdst)
1812
0
{
1813
0
  pg_time_t t = timestamptz_to_time_t(ts);
1814
0
  int     zone_offset;
1815
0
  int     abbr_offset;
1816
0
  int     tz;
1817
0
  struct pg_tm tm;
1818
0
  fsec_t    fsec;
1819
1820
  /*
1821
   * If the abbrev matches anything in the zone data, this is pretty easy.
1822
   */
1823
0
  if (DetermineTimeZoneAbbrevOffsetInternal(t, abbr, tzp,
1824
0
                        &abbr_offset, isdst))
1825
0
    return abbr_offset;
1826
1827
  /*
1828
   * Else, break down the timestamp so we can use DetermineTimeZoneOffset.
1829
   */
1830
0
  if (timestamp2tm(ts, &tz, &tm, &fsec, NULL, tzp) != 0)
1831
0
    ereport(ERROR,
1832
0
        (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1833
0
         errmsg("timestamp out of range")));
1834
1835
0
  zone_offset = DetermineTimeZoneOffset(&tm, tzp);
1836
0
  *isdst = tm.tm_isdst;
1837
0
  return zone_offset;
1838
0
}
1839
1840
1841
/*
1842
 * DetermineTimeZoneAbbrevOffsetInternal()
1843
 *
1844
 * Workhorse for above two functions: work from a pg_time_t probe instant.
1845
 * On success, return GMT offset and DST status into *offset and *isdst.
1846
 */
1847
static bool
1848
DetermineTimeZoneAbbrevOffsetInternal(pg_time_t t, const char *abbr, pg_tz *tzp,
1849
                    int *offset, int *isdst)
1850
0
{
1851
0
  char    upabbr[TZ_STRLEN_MAX + 1];
1852
0
  unsigned char *p;
1853
0
  long int  gmtoff;
1854
1855
  /* We need to force the abbrev to upper case */
1856
0
  strlcpy(upabbr, abbr, sizeof(upabbr));
1857
0
  for (p = (unsigned char *) upabbr; *p; p++)
1858
0
    *p = pg_toupper(*p);
1859
1860
  /* Look up the abbrev's meaning at this time in this zone */
1861
0
  if (pg_interpret_timezone_abbrev(upabbr,
1862
0
                   &t,
1863
0
                   &gmtoff,
1864
0
                   isdst,
1865
0
                   tzp))
1866
0
  {
1867
    /* Change sign to agree with DetermineTimeZoneOffset() */
1868
0
    *offset = (int) -gmtoff;
1869
0
    return true;
1870
0
  }
1871
0
  return false;
1872
0
}
1873
1874
1875
/*
1876
 * TimeZoneAbbrevIsKnown()
1877
 *
1878
 * Detect whether the given string is a time zone abbreviation that's known
1879
 * in the specified TZDB timezone, and if so whether it's fixed or varying
1880
 * meaning.  The match is not case-sensitive.
1881
 */
1882
static bool
1883
TimeZoneAbbrevIsKnown(const char *abbr, pg_tz *tzp,
1884
            bool *isfixed, int *offset, int *isdst)
1885
0
{
1886
0
  char    upabbr[TZ_STRLEN_MAX + 1];
1887
0
  unsigned char *p;
1888
0
  long int  gmtoff;
1889
1890
  /* We need to force the abbrev to upper case */
1891
0
  strlcpy(upabbr, abbr, sizeof(upabbr));
1892
0
  for (p = (unsigned char *) upabbr; *p; p++)
1893
0
    *p = pg_toupper(*p);
1894
1895
  /* Look up the abbrev's meaning in this zone */
1896
0
  if (pg_timezone_abbrev_is_known(upabbr,
1897
0
                  isfixed,
1898
0
                  &gmtoff,
1899
0
                  isdst,
1900
0
                  tzp))
1901
0
  {
1902
    /* Change sign to agree with DetermineTimeZoneOffset() */
1903
0
    *offset = (int) -gmtoff;
1904
0
    return true;
1905
0
  }
1906
0
  return false;
1907
0
}
1908
1909
1910
/*
1911
 * DecodeTimeOnly()
1912
 * Interpret parsed string as time fields only.
1913
 * Returns 0 if successful, DTERR code if bogus input detected.
1914
 *
1915
 * Inputs are field[] and ftype[] arrays, of length nf.
1916
 * Other arguments are outputs.
1917
 *
1918
 * Note that support for time zone is here for
1919
 * SQL TIME WITH TIME ZONE, but it reveals
1920
 * bogosity with SQL date/time standards, since
1921
 * we must infer a time zone from current time.
1922
 * - thomas 2000-03-10
1923
 * Allow specifying date to get a better time zone,
1924
 * if time zones are allowed. - thomas 2001-12-26
1925
 */
1926
int
1927
DecodeTimeOnly(char **field, int *ftype, int nf,
1928
         int *dtype, struct pg_tm *tm, fsec_t *fsec, int *tzp,
1929
         DateTimeErrorExtra *extra)
1930
0
{
1931
0
  int     fmask = 0,
1932
0
        tmask,
1933
0
        type;
1934
0
  int     ptype = 0;    /* "prefix type" for ISO and Julian formats */
1935
0
  int     i;
1936
0
  int     val;
1937
0
  int     dterr;
1938
0
  bool    isjulian = false;
1939
0
  bool    is2digits = false;
1940
0
  bool    bc = false;
1941
0
  int     mer = HR24;
1942
0
  pg_tz    *namedTz = NULL;
1943
0
  pg_tz    *abbrevTz = NULL;
1944
0
  char     *abbrev = NULL;
1945
0
  pg_tz    *valtz;
1946
1947
0
  *dtype = DTK_TIME;
1948
0
  tm->tm_hour = 0;
1949
0
  tm->tm_min = 0;
1950
0
  tm->tm_sec = 0;
1951
0
  *fsec = 0;
1952
  /* don't know daylight savings time status apriori */
1953
0
  tm->tm_isdst = -1;
1954
1955
0
  if (tzp != NULL)
1956
0
    *tzp = 0;
1957
1958
0
  for (i = 0; i < nf; i++)
1959
0
  {
1960
0
    switch (ftype[i])
1961
0
    {
1962
0
      case DTK_DATE:
1963
1964
        /*
1965
         * Time zone not allowed? Then should not accept dates or time
1966
         * zones no matter what else!
1967
         */
1968
0
        if (tzp == NULL)
1969
0
          return DTERR_BAD_FORMAT;
1970
1971
        /* Under limited circumstances, we will accept a date... */
1972
0
        if (i == 0 && nf >= 2 &&
1973
0
          (ftype[nf - 1] == DTK_DATE || ftype[1] == DTK_TIME))
1974
0
        {
1975
0
          dterr = DecodeDate(field[i], fmask,
1976
0
                     &tmask, &is2digits, tm);
1977
0
          if (dterr)
1978
0
            return dterr;
1979
0
        }
1980
        /* otherwise, this is a time and/or time zone */
1981
0
        else
1982
0
        {
1983
0
          if (isdigit((unsigned char) *field[i]))
1984
0
          {
1985
0
            char     *cp;
1986
1987
            /*
1988
             * Starts with a digit but we already have a time
1989
             * field? Then we are in trouble with time already...
1990
             */
1991
0
            if ((fmask & DTK_TIME_M) == DTK_TIME_M)
1992
0
              return DTERR_BAD_FORMAT;
1993
1994
            /*
1995
             * Should not get here and fail. Sanity check only...
1996
             */
1997
0
            if ((cp = strchr(field[i], '-')) == NULL)
1998
0
              return DTERR_BAD_FORMAT;
1999
2000
            /* Get the time zone from the end of the string */
2001
0
            dterr = DecodeTimezone(cp, tzp);
2002
0
            if (dterr)
2003
0
              return dterr;
2004
0
            *cp = '\0';
2005
2006
            /*
2007
             * Then read the rest of the field as a concatenated
2008
             * time
2009
             */
2010
0
            dterr = DecodeNumberField(strlen(field[i]), field[i],
2011
0
                          (fmask | DTK_DATE_M),
2012
0
                          &tmask, tm,
2013
0
                          fsec, &is2digits);
2014
0
            if (dterr < 0)
2015
0
              return dterr;
2016
0
            ftype[i] = dterr;
2017
2018
0
            tmask |= DTK_M(TZ);
2019
0
          }
2020
0
          else
2021
0
          {
2022
0
            namedTz = pg_tzset(field[i]);
2023
0
            if (!namedTz)
2024
0
            {
2025
0
              extra->dtee_timezone = field[i];
2026
0
              return DTERR_BAD_TIMEZONE;
2027
0
            }
2028
            /* we'll apply the zone setting below */
2029
0
            ftype[i] = DTK_TZ;
2030
0
            tmask = DTK_M(TZ);
2031
0
          }
2032
0
        }
2033
0
        break;
2034
2035
0
      case DTK_TIME:
2036
2037
        /*
2038
         * This might be an ISO time following a "t" field.
2039
         */
2040
0
        if (ptype != 0)
2041
0
        {
2042
0
          if (ptype != DTK_TIME)
2043
0
            return DTERR_BAD_FORMAT;
2044
0
          ptype = 0;
2045
0
        }
2046
2047
0
        dterr = DecodeTime(field[i], (fmask | DTK_DATE_M),
2048
0
                   INTERVAL_FULL_RANGE,
2049
0
                   &tmask, tm, fsec);
2050
0
        if (dterr)
2051
0
          return dterr;
2052
0
        break;
2053
2054
0
      case DTK_TZ:
2055
0
        {
2056
0
          int     tz;
2057
2058
0
          if (tzp == NULL)
2059
0
            return DTERR_BAD_FORMAT;
2060
2061
0
          dterr = DecodeTimezone(field[i], &tz);
2062
0
          if (dterr)
2063
0
            return dterr;
2064
0
          *tzp = tz;
2065
0
          tmask = DTK_M(TZ);
2066
0
        }
2067
0
        break;
2068
2069
0
      case DTK_NUMBER:
2070
2071
        /*
2072
         * Deal with cases where previous field labeled this one
2073
         */
2074
0
        if (ptype != 0)
2075
0
        {
2076
0
          char     *cp;
2077
0
          int     value;
2078
2079
0
          errno = 0;
2080
0
          value = strtoint(field[i], &cp, 10);
2081
0
          if (errno == ERANGE)
2082
0
            return DTERR_FIELD_OVERFLOW;
2083
0
          if (*cp != '.' && *cp != '\0')
2084
0
            return DTERR_BAD_FORMAT;
2085
2086
0
          switch (ptype)
2087
0
          {
2088
0
            case DTK_JULIAN:
2089
              /* previous field was a label for "julian date" */
2090
0
              if (tzp == NULL)
2091
0
                return DTERR_BAD_FORMAT;
2092
0
              if (value < 0)
2093
0
                return DTERR_FIELD_OVERFLOW;
2094
0
              tmask = DTK_DATE_M;
2095
0
              j2date(value, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
2096
0
              isjulian = true;
2097
2098
0
              if (*cp == '.')
2099
0
              {
2100
0
                double    time;
2101
2102
0
                dterr = ParseFraction(cp, &time);
2103
0
                if (dterr)
2104
0
                  return dterr;
2105
0
                time *= USECS_PER_DAY;
2106
0
                dt2time(time,
2107
0
                    &tm->tm_hour, &tm->tm_min,
2108
0
                    &tm->tm_sec, fsec);
2109
0
                tmask |= DTK_TIME_M;
2110
0
              }
2111
0
              break;
2112
2113
0
            case DTK_TIME:
2114
              /* previous field was "t" for ISO time */
2115
0
              dterr = DecodeNumberField(strlen(field[i]), field[i],
2116
0
                            (fmask | DTK_DATE_M),
2117
0
                            &tmask, tm,
2118
0
                            fsec, &is2digits);
2119
0
              if (dterr < 0)
2120
0
                return dterr;
2121
0
              ftype[i] = dterr;
2122
2123
0
              if (tmask != DTK_TIME_M)
2124
0
                return DTERR_BAD_FORMAT;
2125
0
              break;
2126
2127
0
            default:
2128
0
              return DTERR_BAD_FORMAT;
2129
0
              break;
2130
0
          }
2131
2132
0
          ptype = 0;
2133
0
          *dtype = DTK_DATE;
2134
0
        }
2135
0
        else
2136
0
        {
2137
0
          char     *cp;
2138
0
          int     flen;
2139
2140
0
          flen = strlen(field[i]);
2141
0
          cp = strchr(field[i], '.');
2142
2143
          /* Embedded decimal? */
2144
0
          if (cp != NULL)
2145
0
          {
2146
            /*
2147
             * Under limited circumstances, we will accept a
2148
             * date...
2149
             */
2150
0
            if (i == 0 && nf >= 2 && ftype[nf - 1] == DTK_DATE)
2151
0
            {
2152
0
              dterr = DecodeDate(field[i], fmask,
2153
0
                         &tmask, &is2digits, tm);
2154
0
              if (dterr)
2155
0
                return dterr;
2156
0
            }
2157
            /* embedded decimal and several digits before? */
2158
0
            else if (flen - strlen(cp) > 2)
2159
0
            {
2160
              /*
2161
               * Interpret as a concatenated date or time Set
2162
               * the type field to allow decoding other fields
2163
               * later. Example: 20011223 or 040506
2164
               */
2165
0
              dterr = DecodeNumberField(flen, field[i],
2166
0
                            (fmask | DTK_DATE_M),
2167
0
                            &tmask, tm,
2168
0
                            fsec, &is2digits);
2169
0
              if (dterr < 0)
2170
0
                return dterr;
2171
0
              ftype[i] = dterr;
2172
0
            }
2173
0
            else
2174
0
              return DTERR_BAD_FORMAT;
2175
0
          }
2176
0
          else if (flen > 4)
2177
0
          {
2178
0
            dterr = DecodeNumberField(flen, field[i],
2179
0
                          (fmask | DTK_DATE_M),
2180
0
                          &tmask, tm,
2181
0
                          fsec, &is2digits);
2182
0
            if (dterr < 0)
2183
0
              return dterr;
2184
0
            ftype[i] = dterr;
2185
0
          }
2186
          /* otherwise it is a single date/time field... */
2187
0
          else
2188
0
          {
2189
0
            dterr = DecodeNumber(flen, field[i],
2190
0
                       false,
2191
0
                       (fmask | DTK_DATE_M),
2192
0
                       &tmask, tm,
2193
0
                       fsec, &is2digits);
2194
0
            if (dterr)
2195
0
              return dterr;
2196
0
          }
2197
0
        }
2198
0
        break;
2199
2200
0
      case DTK_STRING:
2201
0
      case DTK_SPECIAL:
2202
        /* timezone abbrevs take precedence over built-in tokens */
2203
0
        dterr = DecodeTimezoneAbbrev(i, field[i],
2204
0
                       &type, &val, &valtz, extra);
2205
0
        if (dterr)
2206
0
          return dterr;
2207
0
        if (type == UNKNOWN_FIELD)
2208
0
          type = DecodeSpecial(i, field[i], &val);
2209
0
        if (type == IGNORE_DTF)
2210
0
          continue;
2211
2212
0
        tmask = DTK_M(type);
2213
0
        switch (type)
2214
0
        {
2215
0
          case RESERV:
2216
0
            switch (val)
2217
0
            {
2218
0
              case DTK_NOW:
2219
0
                tmask = DTK_TIME_M;
2220
0
                *dtype = DTK_TIME;
2221
0
                GetCurrentTimeUsec(tm, fsec, NULL);
2222
0
                break;
2223
2224
0
              case DTK_ZULU:
2225
0
                tmask = (DTK_TIME_M | DTK_M(TZ));
2226
0
                *dtype = DTK_TIME;
2227
0
                tm->tm_hour = 0;
2228
0
                tm->tm_min = 0;
2229
0
                tm->tm_sec = 0;
2230
0
                tm->tm_isdst = 0;
2231
0
                break;
2232
2233
0
              default:
2234
0
                return DTERR_BAD_FORMAT;
2235
0
            }
2236
2237
0
            break;
2238
2239
0
          case DTZMOD:
2240
2241
            /*
2242
             * daylight savings time modifier (solves "MET DST"
2243
             * syntax)
2244
             */
2245
0
            tmask |= DTK_M(DTZ);
2246
0
            tm->tm_isdst = 1;
2247
0
            if (tzp == NULL)
2248
0
              return DTERR_BAD_FORMAT;
2249
0
            *tzp -= val;
2250
0
            break;
2251
2252
0
          case DTZ:
2253
2254
            /*
2255
             * set mask for TZ here _or_ check for DTZ later when
2256
             * getting default timezone
2257
             */
2258
0
            tmask |= DTK_M(TZ);
2259
0
            tm->tm_isdst = 1;
2260
0
            if (tzp == NULL)
2261
0
              return DTERR_BAD_FORMAT;
2262
0
            *tzp = -val;
2263
0
            ftype[i] = DTK_TZ;
2264
0
            break;
2265
2266
0
          case TZ:
2267
0
            tm->tm_isdst = 0;
2268
0
            if (tzp == NULL)
2269
0
              return DTERR_BAD_FORMAT;
2270
0
            *tzp = -val;
2271
0
            ftype[i] = DTK_TZ;
2272
0
            break;
2273
2274
0
          case DYNTZ:
2275
0
            tmask |= DTK_M(TZ);
2276
0
            if (tzp == NULL)
2277
0
              return DTERR_BAD_FORMAT;
2278
            /* we'll determine the actual offset later */
2279
0
            abbrevTz = valtz;
2280
0
            abbrev = field[i];
2281
0
            ftype[i] = DTK_TZ;
2282
0
            break;
2283
2284
0
          case AMPM:
2285
0
            mer = val;
2286
0
            break;
2287
2288
0
          case ADBC:
2289
0
            bc = (val == BC);
2290
0
            break;
2291
2292
0
          case UNITS:
2293
0
            tmask = 0;
2294
            /* reject consecutive unhandled units */
2295
0
            if (ptype != 0)
2296
0
              return DTERR_BAD_FORMAT;
2297
0
            ptype = val;
2298
0
            break;
2299
2300
0
          case ISOTIME:
2301
0
            tmask = 0;
2302
            /* reject consecutive unhandled units */
2303
0
            if (ptype != 0)
2304
0
              return DTERR_BAD_FORMAT;
2305
0
            ptype = val;
2306
0
            break;
2307
2308
0
          case UNKNOWN_FIELD:
2309
2310
            /*
2311
             * Before giving up and declaring error, check to see
2312
             * if it is an all-alpha timezone name.
2313
             */
2314
0
            namedTz = pg_tzset(field[i]);
2315
0
            if (!namedTz)
2316
0
              return DTERR_BAD_FORMAT;
2317
            /* we'll apply the zone setting below */
2318
0
            tmask = DTK_M(TZ);
2319
0
            break;
2320
2321
0
          default:
2322
0
            return DTERR_BAD_FORMAT;
2323
0
        }
2324
0
        break;
2325
2326
0
      default:
2327
0
        return DTERR_BAD_FORMAT;
2328
0
    }
2329
2330
0
    if (tmask & fmask)
2331
0
      return DTERR_BAD_FORMAT;
2332
0
    fmask |= tmask;
2333
0
  }              /* end loop over fields */
2334
2335
  /* reject if prefix type appeared and was never handled */
2336
0
  if (ptype != 0)
2337
0
    return DTERR_BAD_FORMAT;
2338
2339
  /* do final checking/adjustment of Y/M/D fields */
2340
0
  dterr = ValidateDate(fmask, isjulian, is2digits, bc, tm);
2341
0
  if (dterr)
2342
0
    return dterr;
2343
2344
  /* handle AM/PM */
2345
0
  if (mer != HR24 && tm->tm_hour > HOURS_PER_DAY / 2)
2346
0
    return DTERR_FIELD_OVERFLOW;
2347
0
  if (mer == AM && tm->tm_hour == HOURS_PER_DAY / 2)
2348
0
    tm->tm_hour = 0;
2349
0
  else if (mer == PM && tm->tm_hour != HOURS_PER_DAY / 2)
2350
0
    tm->tm_hour += HOURS_PER_DAY / 2;
2351
2352
  /* check for time overflow */
2353
0
  if (time_overflows(tm->tm_hour, tm->tm_min, tm->tm_sec, *fsec))
2354
0
    return DTERR_FIELD_OVERFLOW;
2355
2356
0
  if ((fmask & DTK_TIME_M) != DTK_TIME_M)
2357
0
    return DTERR_BAD_FORMAT;
2358
2359
  /*
2360
   * If we had a full timezone spec, compute the offset (we could not do it
2361
   * before, because we may need the date to resolve DST status).
2362
   */
2363
0
  if (namedTz != NULL)
2364
0
  {
2365
0
    long int  gmtoff;
2366
2367
    /* daylight savings time modifier disallowed with full TZ */
2368
0
    if (fmask & DTK_M(DTZMOD))
2369
0
      return DTERR_BAD_FORMAT;
2370
2371
    /* if non-DST zone, we do not need to know the date */
2372
0
    if (pg_get_timezone_offset(namedTz, &gmtoff))
2373
0
    {
2374
0
      *tzp = -(int) gmtoff;
2375
0
    }
2376
0
    else
2377
0
    {
2378
      /* a date has to be specified */
2379
0
      if ((fmask & DTK_DATE_M) != DTK_DATE_M)
2380
0
        return DTERR_BAD_FORMAT;
2381
0
      *tzp = DetermineTimeZoneOffset(tm, namedTz);
2382
0
    }
2383
0
  }
2384
2385
  /*
2386
   * Likewise, if we had a dynamic timezone abbreviation, resolve it now.
2387
   */
2388
0
  if (abbrevTz != NULL)
2389
0
  {
2390
0
    struct pg_tm tt,
2391
0
           *tmp = &tt;
2392
2393
    /*
2394
     * daylight savings time modifier but no standard timezone? then error
2395
     */
2396
0
    if (fmask & DTK_M(DTZMOD))
2397
0
      return DTERR_BAD_FORMAT;
2398
2399
0
    if ((fmask & DTK_DATE_M) == 0)
2400
0
      GetCurrentDateTime(tmp);
2401
0
    else
2402
0
    {
2403
      /* a date has to be specified */
2404
0
      if ((fmask & DTK_DATE_M) != DTK_DATE_M)
2405
0
        return DTERR_BAD_FORMAT;
2406
0
      tmp->tm_year = tm->tm_year;
2407
0
      tmp->tm_mon = tm->tm_mon;
2408
0
      tmp->tm_mday = tm->tm_mday;
2409
0
    }
2410
0
    tmp->tm_hour = tm->tm_hour;
2411
0
    tmp->tm_min = tm->tm_min;
2412
0
    tmp->tm_sec = tm->tm_sec;
2413
0
    *tzp = DetermineTimeZoneAbbrevOffset(tmp, abbrev, abbrevTz);
2414
0
    tm->tm_isdst = tmp->tm_isdst;
2415
0
  }
2416
2417
  /* timezone not specified? then use session timezone */
2418
0
  if (tzp != NULL && !(fmask & DTK_M(TZ)))
2419
0
  {
2420
0
    struct pg_tm tt,
2421
0
           *tmp = &tt;
2422
2423
    /*
2424
     * daylight savings time modifier but no standard timezone? then error
2425
     */
2426
0
    if (fmask & DTK_M(DTZMOD))
2427
0
      return DTERR_BAD_FORMAT;
2428
2429
0
    if ((fmask & DTK_DATE_M) == 0)
2430
0
      GetCurrentDateTime(tmp);
2431
0
    else
2432
0
    {
2433
      /* a date has to be specified */
2434
0
      if ((fmask & DTK_DATE_M) != DTK_DATE_M)
2435
0
        return DTERR_BAD_FORMAT;
2436
0
      tmp->tm_year = tm->tm_year;
2437
0
      tmp->tm_mon = tm->tm_mon;
2438
0
      tmp->tm_mday = tm->tm_mday;
2439
0
    }
2440
0
    tmp->tm_hour = tm->tm_hour;
2441
0
    tmp->tm_min = tm->tm_min;
2442
0
    tmp->tm_sec = tm->tm_sec;
2443
0
    *tzp = DetermineTimeZoneOffset(tmp, session_timezone);
2444
0
    tm->tm_isdst = tmp->tm_isdst;
2445
0
  }
2446
2447
0
  return 0;
2448
0
}
2449
2450
/*
2451
 * DecodeDate()
2452
 * Decode date string which includes delimiters.
2453
 * Return 0 if okay, a DTERR code if not.
2454
 *
2455
 *  str: field to be parsed
2456
 *  fmask: bitmask for field types already seen
2457
 *  *tmask: receives bitmask for fields found here
2458
 *  *is2digits: set to true if we find 2-digit year
2459
 *  *tm: field values are stored into appropriate members of this struct
2460
 */
2461
static int
2462
DecodeDate(char *str, int fmask, int *tmask, bool *is2digits,
2463
       struct pg_tm *tm)
2464
0
{
2465
0
  fsec_t    fsec;
2466
0
  int     nf = 0;
2467
0
  int     i,
2468
0
        len;
2469
0
  int     dterr;
2470
0
  bool    haveTextMonth = false;
2471
0
  int     type,
2472
0
        val,
2473
0
        dmask = 0;
2474
0
  char     *field[MAXDATEFIELDS];
2475
2476
0
  *tmask = 0;
2477
2478
  /* parse this string... */
2479
0
  while (*str != '\0' && nf < MAXDATEFIELDS)
2480
0
  {
2481
    /* skip field separators */
2482
0
    while (*str != '\0' && !isalnum((unsigned char) *str))
2483
0
      str++;
2484
2485
0
    if (*str == '\0')
2486
0
      return DTERR_BAD_FORMAT; /* end of string after separator */
2487
2488
0
    field[nf] = str;
2489
0
    if (isdigit((unsigned char) *str))
2490
0
    {
2491
0
      while (isdigit((unsigned char) *str))
2492
0
        str++;
2493
0
    }
2494
0
    else if (isalpha((unsigned char) *str))
2495
0
    {
2496
0
      while (isalpha((unsigned char) *str))
2497
0
        str++;
2498
0
    }
2499
2500
    /* Just get rid of any non-digit, non-alpha characters... */
2501
0
    if (*str != '\0')
2502
0
      *str++ = '\0';
2503
0
    nf++;
2504
0
  }
2505
2506
  /* look first for text fields, since that will be unambiguous month */
2507
0
  for (i = 0; i < nf; i++)
2508
0
  {
2509
0
    if (isalpha((unsigned char) *field[i]))
2510
0
    {
2511
0
      type = DecodeSpecial(i, field[i], &val);
2512
0
      if (type == IGNORE_DTF)
2513
0
        continue;
2514
2515
0
      dmask = DTK_M(type);
2516
0
      switch (type)
2517
0
      {
2518
0
        case MONTH:
2519
0
          tm->tm_mon = val;
2520
0
          haveTextMonth = true;
2521
0
          break;
2522
2523
0
        default:
2524
0
          return DTERR_BAD_FORMAT;
2525
0
      }
2526
0
      if (fmask & dmask)
2527
0
        return DTERR_BAD_FORMAT;
2528
2529
0
      fmask |= dmask;
2530
0
      *tmask |= dmask;
2531
2532
      /* mark this field as being completed */
2533
0
      field[i] = NULL;
2534
0
    }
2535
0
  }
2536
2537
  /* now pick up remaining numeric fields */
2538
0
  for (i = 0; i < nf; i++)
2539
0
  {
2540
0
    if (field[i] == NULL)
2541
0
      continue;
2542
2543
0
    if ((len = strlen(field[i])) <= 0)
2544
0
      return DTERR_BAD_FORMAT;
2545
2546
0
    dterr = DecodeNumber(len, field[i], haveTextMonth, fmask,
2547
0
               &dmask, tm,
2548
0
               &fsec, is2digits);
2549
0
    if (dterr)
2550
0
      return dterr;
2551
2552
0
    if (fmask & dmask)
2553
0
      return DTERR_BAD_FORMAT;
2554
2555
0
    fmask |= dmask;
2556
0
    *tmask |= dmask;
2557
0
  }
2558
2559
0
  if ((fmask & ~(DTK_M(DOY) | DTK_M(TZ))) != DTK_DATE_M)
2560
0
    return DTERR_BAD_FORMAT;
2561
2562
  /* validation of the field values must wait until ValidateDate() */
2563
2564
0
  return 0;
2565
0
}
2566
2567
/*
2568
 * ValidateDate()
2569
 * Check valid year/month/day values, handle BC and DOY cases
2570
 * Return 0 if okay, a DTERR code if not.
2571
 */
2572
int
2573
ValidateDate(int fmask, bool isjulian, bool is2digits, bool bc,
2574
       struct pg_tm *tm)
2575
0
{
2576
0
  if (fmask & DTK_M(YEAR))
2577
0
  {
2578
0
    if (isjulian)
2579
0
    {
2580
      /* tm_year is correct and should not be touched */
2581
0
    }
2582
0
    else if (bc)
2583
0
    {
2584
      /* there is no year zero in AD/BC notation */
2585
0
      if (tm->tm_year <= 0)
2586
0
        return DTERR_FIELD_OVERFLOW;
2587
      /* internally, we represent 1 BC as year zero, 2 BC as -1, etc */
2588
0
      tm->tm_year = -(tm->tm_year - 1);
2589
0
    }
2590
0
    else if (is2digits)
2591
0
    {
2592
      /* process 1 or 2-digit input as 1970-2069 AD, allow '0' and '00' */
2593
0
      if (tm->tm_year < 0) /* just paranoia */
2594
0
        return DTERR_FIELD_OVERFLOW;
2595
0
      if (tm->tm_year < 70)
2596
0
        tm->tm_year += 2000;
2597
0
      else if (tm->tm_year < 100)
2598
0
        tm->tm_year += 1900;
2599
0
    }
2600
0
    else
2601
0
    {
2602
      /* there is no year zero in AD/BC notation */
2603
0
      if (tm->tm_year <= 0)
2604
0
        return DTERR_FIELD_OVERFLOW;
2605
0
    }
2606
0
  }
2607
2608
  /* now that we have correct year, decode DOY */
2609
0
  if (fmask & DTK_M(DOY))
2610
0
  {
2611
0
    j2date(date2j(tm->tm_year, 1, 1) + tm->tm_yday - 1,
2612
0
         &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
2613
0
  }
2614
2615
  /* check for valid month */
2616
0
  if (fmask & DTK_M(MONTH))
2617
0
  {
2618
0
    if (tm->tm_mon < 1 || tm->tm_mon > MONTHS_PER_YEAR)
2619
0
      return DTERR_MD_FIELD_OVERFLOW;
2620
0
  }
2621
2622
  /* minimal check for valid day */
2623
0
  if (fmask & DTK_M(DAY))
2624
0
  {
2625
0
    if (tm->tm_mday < 1 || tm->tm_mday > 31)
2626
0
      return DTERR_MD_FIELD_OVERFLOW;
2627
0
  }
2628
2629
0
  if ((fmask & DTK_DATE_M) == DTK_DATE_M)
2630
0
  {
2631
    /*
2632
     * Check for valid day of month, now that we know for sure the month
2633
     * and year.  Note we don't use MD_FIELD_OVERFLOW here, since it seems
2634
     * unlikely that "Feb 29" is a YMD-order error.
2635
     */
2636
0
    if (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1])
2637
0
      return DTERR_FIELD_OVERFLOW;
2638
0
  }
2639
2640
0
  return 0;
2641
0
}
2642
2643
2644
/*
2645
 * DecodeTimeCommon()
2646
 * Decode time string which includes delimiters.
2647
 * Return 0 if okay, a DTERR code if not.
2648
 * tmask and itm are output parameters.
2649
 *
2650
 * This code is shared between the timestamp and interval cases.
2651
 * We return a struct pg_itm (of which only the tm_usec, tm_sec, tm_min,
2652
 * and tm_hour fields are used) and let the wrapper functions below
2653
 * convert and range-check as necessary.
2654
 */
2655
static int
2656
DecodeTimeCommon(char *str, int fmask, int range,
2657
         int *tmask, struct pg_itm *itm)
2658
0
{
2659
0
  char     *cp;
2660
0
  int     dterr;
2661
0
  fsec_t    fsec = 0;
2662
2663
0
  *tmask = DTK_TIME_M;
2664
2665
0
  errno = 0;
2666
0
  itm->tm_hour = strtoi64(str, &cp, 10);
2667
0
  if (errno == ERANGE)
2668
0
    return DTERR_FIELD_OVERFLOW;
2669
0
  if (*cp != ':')
2670
0
    return DTERR_BAD_FORMAT;
2671
0
  errno = 0;
2672
0
  itm->tm_min = strtoint(cp + 1, &cp, 10);
2673
0
  if (errno == ERANGE)
2674
0
    return DTERR_FIELD_OVERFLOW;
2675
0
  if (*cp == '\0')
2676
0
  {
2677
0
    itm->tm_sec = 0;
2678
    /* If it's a MINUTE TO SECOND interval, take 2 fields as being mm:ss */
2679
0
    if (range == (INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND)))
2680
0
    {
2681
0
      if (itm->tm_hour > INT_MAX || itm->tm_hour < INT_MIN)
2682
0
        return DTERR_FIELD_OVERFLOW;
2683
0
      itm->tm_sec = itm->tm_min;
2684
0
      itm->tm_min = (int) itm->tm_hour;
2685
0
      itm->tm_hour = 0;
2686
0
    }
2687
0
  }
2688
0
  else if (*cp == '.')
2689
0
  {
2690
    /* always assume mm:ss.sss is MINUTE TO SECOND */
2691
0
    dterr = ParseFractionalSecond(cp, &fsec);
2692
0
    if (dterr)
2693
0
      return dterr;
2694
0
    if (itm->tm_hour > INT_MAX || itm->tm_hour < INT_MIN)
2695
0
      return DTERR_FIELD_OVERFLOW;
2696
0
    itm->tm_sec = itm->tm_min;
2697
0
    itm->tm_min = (int) itm->tm_hour;
2698
0
    itm->tm_hour = 0;
2699
0
  }
2700
0
  else if (*cp == ':')
2701
0
  {
2702
0
    errno = 0;
2703
0
    itm->tm_sec = strtoint(cp + 1, &cp, 10);
2704
0
    if (errno == ERANGE)
2705
0
      return DTERR_FIELD_OVERFLOW;
2706
0
    if (*cp == '.')
2707
0
    {
2708
0
      dterr = ParseFractionalSecond(cp, &fsec);
2709
0
      if (dterr)
2710
0
        return dterr;
2711
0
    }
2712
0
    else if (*cp != '\0')
2713
0
      return DTERR_BAD_FORMAT;
2714
0
  }
2715
0
  else
2716
0
    return DTERR_BAD_FORMAT;
2717
2718
  /* do a sanity check; but caller must check the range of tm_hour */
2719
0
  if (itm->tm_hour < 0 ||
2720
0
    itm->tm_min < 0 || itm->tm_min > MINS_PER_HOUR - 1 ||
2721
0
    itm->tm_sec < 0 || itm->tm_sec > SECS_PER_MINUTE ||
2722
0
    fsec < 0 || fsec > USECS_PER_SEC)
2723
0
    return DTERR_FIELD_OVERFLOW;
2724
2725
0
  itm->tm_usec = (int) fsec;
2726
2727
0
  return 0;
2728
0
}
2729
2730
/*
2731
 * DecodeTime()
2732
 * Decode time string which includes delimiters.
2733
 * Return 0 if okay, a DTERR code if not.
2734
 *
2735
 * This version is used for timestamps.  The results are returned into
2736
 * the tm_hour/tm_min/tm_sec fields of *tm, and microseconds into *fsec.
2737
 */
2738
static int
2739
DecodeTime(char *str, int fmask, int range,
2740
       int *tmask, struct pg_tm *tm, fsec_t *fsec)
2741
0
{
2742
0
  struct pg_itm itm;
2743
0
  int     dterr;
2744
2745
0
  dterr = DecodeTimeCommon(str, fmask, range,
2746
0
               tmask, &itm);
2747
0
  if (dterr)
2748
0
    return dterr;
2749
2750
0
  if (itm.tm_hour > INT_MAX)
2751
0
    return DTERR_FIELD_OVERFLOW;
2752
0
  tm->tm_hour = (int) itm.tm_hour;
2753
0
  tm->tm_min = itm.tm_min;
2754
0
  tm->tm_sec = itm.tm_sec;
2755
0
  *fsec = itm.tm_usec;
2756
2757
0
  return 0;
2758
0
}
2759
2760
/*
2761
 * DecodeTimeForInterval()
2762
 * Decode time string which includes delimiters.
2763
 * Return 0 if okay, a DTERR code if not.
2764
 *
2765
 * This version is used for intervals.  The results are returned into
2766
 * itm_in->tm_usec.
2767
 */
2768
static int
2769
DecodeTimeForInterval(char *str, int fmask, int range,
2770
            int *tmask, struct pg_itm_in *itm_in)
2771
0
{
2772
0
  struct pg_itm itm;
2773
0
  int     dterr;
2774
2775
0
  dterr = DecodeTimeCommon(str, fmask, range,
2776
0
               tmask, &itm);
2777
0
  if (dterr)
2778
0
    return dterr;
2779
2780
0
  itm_in->tm_usec = itm.tm_usec;
2781
0
  if (!int64_multiply_add(itm.tm_hour, USECS_PER_HOUR, &itm_in->tm_usec) ||
2782
0
    !int64_multiply_add(itm.tm_min, USECS_PER_MINUTE, &itm_in->tm_usec) ||
2783
0
    !int64_multiply_add(itm.tm_sec, USECS_PER_SEC, &itm_in->tm_usec))
2784
0
    return DTERR_FIELD_OVERFLOW;
2785
2786
0
  return 0;
2787
0
}
2788
2789
2790
/*
2791
 * DecodeNumber()
2792
 * Interpret plain numeric field as a date value in context.
2793
 * Return 0 if okay, a DTERR code if not.
2794
 */
2795
static int
2796
DecodeNumber(int flen, char *str, bool haveTextMonth, int fmask,
2797
       int *tmask, struct pg_tm *tm, fsec_t *fsec, bool *is2digits)
2798
0
{
2799
0
  int     val;
2800
0
  char     *cp;
2801
0
  int     dterr;
2802
2803
0
  *tmask = 0;
2804
2805
0
  errno = 0;
2806
0
  val = strtoint(str, &cp, 10);
2807
0
  if (errno == ERANGE)
2808
0
    return DTERR_FIELD_OVERFLOW;
2809
0
  if (cp == str)
2810
0
    return DTERR_BAD_FORMAT;
2811
2812
0
  if (*cp == '.')
2813
0
  {
2814
    /*
2815
     * More than two digits before decimal point? Then could be a date or
2816
     * a run-together time: 2001.360 20011225 040506.789
2817
     */
2818
0
    if (cp - str > 2)
2819
0
    {
2820
0
      dterr = DecodeNumberField(flen, str,
2821
0
                    (fmask | DTK_DATE_M),
2822
0
                    tmask, tm,
2823
0
                    fsec, is2digits);
2824
0
      if (dterr < 0)
2825
0
        return dterr;
2826
0
      return 0;
2827
0
    }
2828
2829
0
    dterr = ParseFractionalSecond(cp, fsec);
2830
0
    if (dterr)
2831
0
      return dterr;
2832
0
  }
2833
0
  else if (*cp != '\0')
2834
0
    return DTERR_BAD_FORMAT;
2835
2836
  /* Special case for day of year */
2837
0
  if (flen == 3 && (fmask & DTK_DATE_M) == DTK_M(YEAR) && val >= 1 &&
2838
0
    val <= 366)
2839
0
  {
2840
0
    *tmask = (DTK_M(DOY) | DTK_M(MONTH) | DTK_M(DAY));
2841
0
    tm->tm_yday = val;
2842
    /* tm_mon and tm_mday can't actually be set yet ... */
2843
0
    return 0;
2844
0
  }
2845
2846
  /* Switch based on what we have so far */
2847
0
  switch (fmask & DTK_DATE_M)
2848
0
  {
2849
0
    case 0:
2850
2851
      /*
2852
       * Nothing so far; make a decision about what we think the input
2853
       * is.  There used to be lots of heuristics here, but the
2854
       * consensus now is to be paranoid.  It *must* be either
2855
       * YYYY-MM-DD (with a more-than-two-digit year field), or the
2856
       * field order defined by DateOrder.
2857
       */
2858
0
      if (flen >= 3 || DateOrder == DATEORDER_YMD)
2859
0
      {
2860
0
        *tmask = DTK_M(YEAR);
2861
0
        tm->tm_year = val;
2862
0
      }
2863
0
      else if (DateOrder == DATEORDER_DMY)
2864
0
      {
2865
0
        *tmask = DTK_M(DAY);
2866
0
        tm->tm_mday = val;
2867
0
      }
2868
0
      else
2869
0
      {
2870
0
        *tmask = DTK_M(MONTH);
2871
0
        tm->tm_mon = val;
2872
0
      }
2873
0
      break;
2874
2875
0
    case (DTK_M(YEAR)):
2876
      /* Must be at second field of YY-MM-DD */
2877
0
      *tmask = DTK_M(MONTH);
2878
0
      tm->tm_mon = val;
2879
0
      break;
2880
2881
0
    case (DTK_M(MONTH)):
2882
0
      if (haveTextMonth)
2883
0
      {
2884
        /*
2885
         * We are at the first numeric field of a date that included a
2886
         * textual month name.  We want to support the variants
2887
         * MON-DD-YYYY, DD-MON-YYYY, and YYYY-MON-DD as unambiguous
2888
         * inputs.  We will also accept MON-DD-YY or DD-MON-YY in
2889
         * either DMY or MDY modes, as well as YY-MON-DD in YMD mode.
2890
         */
2891
0
        if (flen >= 3 || DateOrder == DATEORDER_YMD)
2892
0
        {
2893
0
          *tmask = DTK_M(YEAR);
2894
0
          tm->tm_year = val;
2895
0
        }
2896
0
        else
2897
0
        {
2898
0
          *tmask = DTK_M(DAY);
2899
0
          tm->tm_mday = val;
2900
0
        }
2901
0
      }
2902
0
      else
2903
0
      {
2904
        /* Must be at second field of MM-DD-YY */
2905
0
        *tmask = DTK_M(DAY);
2906
0
        tm->tm_mday = val;
2907
0
      }
2908
0
      break;
2909
2910
0
    case (DTK_M(YEAR) | DTK_M(MONTH)):
2911
0
      if (haveTextMonth)
2912
0
      {
2913
        /* Need to accept DD-MON-YYYY even in YMD mode */
2914
0
        if (flen >= 3 && *is2digits)
2915
0
        {
2916
          /* Guess that first numeric field is day was wrong */
2917
0
          *tmask = DTK_M(DAY);  /* YEAR is already set */
2918
0
          tm->tm_mday = tm->tm_year;
2919
0
          tm->tm_year = val;
2920
0
          *is2digits = false;
2921
0
        }
2922
0
        else
2923
0
        {
2924
0
          *tmask = DTK_M(DAY);
2925
0
          tm->tm_mday = val;
2926
0
        }
2927
0
      }
2928
0
      else
2929
0
      {
2930
        /* Must be at third field of YY-MM-DD */
2931
0
        *tmask = DTK_M(DAY);
2932
0
        tm->tm_mday = val;
2933
0
      }
2934
0
      break;
2935
2936
0
    case (DTK_M(DAY)):
2937
      /* Must be at second field of DD-MM-YY */
2938
0
      *tmask = DTK_M(MONTH);
2939
0
      tm->tm_mon = val;
2940
0
      break;
2941
2942
0
    case (DTK_M(MONTH) | DTK_M(DAY)):
2943
      /* Must be at third field of DD-MM-YY or MM-DD-YY */
2944
0
      *tmask = DTK_M(YEAR);
2945
0
      tm->tm_year = val;
2946
0
      break;
2947
2948
0
    case (DTK_M(YEAR) | DTK_M(MONTH) | DTK_M(DAY)):
2949
      /* we have all the date, so it must be a time field */
2950
0
      dterr = DecodeNumberField(flen, str, fmask,
2951
0
                    tmask, tm,
2952
0
                    fsec, is2digits);
2953
0
      if (dterr < 0)
2954
0
        return dterr;
2955
0
      return 0;
2956
2957
0
    default:
2958
      /* Anything else is bogus input */
2959
0
      return DTERR_BAD_FORMAT;
2960
0
  }
2961
2962
  /*
2963
   * When processing a year field, mark it for adjustment if it's only one
2964
   * or two digits.
2965
   */
2966
0
  if (*tmask == DTK_M(YEAR))
2967
0
    *is2digits = (flen <= 2);
2968
2969
0
  return 0;
2970
0
}
2971
2972
2973
/*
2974
 * DecodeNumberField()
2975
 * Interpret numeric string as a concatenated date or time field.
2976
 * Return a DTK token (>= 0) if successful, a DTERR code (< 0) if not.
2977
 *
2978
 * Use the context of previously decoded fields to help with
2979
 * the interpretation.
2980
 */
2981
static int
2982
DecodeNumberField(int len, char *str, int fmask,
2983
          int *tmask, struct pg_tm *tm, fsec_t *fsec, bool *is2digits)
2984
0
{
2985
0
  char     *cp;
2986
2987
  /*
2988
   * This function was originally meant to cope only with DTK_NUMBER fields,
2989
   * but we now sometimes abuse it to parse (parts of) DTK_DATE fields,
2990
   * which can contain letters and other punctuation.  Reject if it's not a
2991
   * valid DTK_NUMBER, that is digits and decimal point(s).  (ParseFraction
2992
   * will reject if there's more than one decimal point.)
2993
   */
2994
0
  if (strspn(str, "0123456789.") != len)
2995
0
    return DTERR_BAD_FORMAT;
2996
2997
  /*
2998
   * Have a decimal point? Then this is a date or something with a seconds
2999
   * field...
3000
   */
3001
0
  if ((cp = strchr(str, '.')) != NULL)
3002
0
  {
3003
0
    int     dterr;
3004
3005
    /* Convert the fraction and store at *fsec */
3006
0
    dterr = ParseFractionalSecond(cp, fsec);
3007
0
    if (dterr)
3008
0
      return dterr;
3009
    /* Now truncate off the fraction for further processing */
3010
0
    *cp = '\0';
3011
0
    len = strlen(str);
3012
0
  }
3013
  /* No decimal point and no complete date yet? */
3014
0
  else if ((fmask & DTK_DATE_M) != DTK_DATE_M)
3015
0
  {
3016
0
    if (len >= 6)
3017
0
    {
3018
0
      *tmask = DTK_DATE_M;
3019
3020
      /*
3021
       * Start from end and consider first 2 as Day, next 2 as Month,
3022
       * and the rest as Year.
3023
       */
3024
0
      tm->tm_mday = atoi(str + (len - 2));
3025
0
      *(str + (len - 2)) = '\0';
3026
0
      tm->tm_mon = atoi(str + (len - 4));
3027
0
      *(str + (len - 4)) = '\0';
3028
0
      tm->tm_year = atoi(str);
3029
0
      if ((len - 4) == 2)
3030
0
        *is2digits = true;
3031
3032
0
      return DTK_DATE;
3033
0
    }
3034
0
  }
3035
3036
  /* not all time fields are specified? */
3037
0
  if ((fmask & DTK_TIME_M) != DTK_TIME_M)
3038
0
  {
3039
    /* hhmmss */
3040
0
    if (len == 6)
3041
0
    {
3042
0
      *tmask = DTK_TIME_M;
3043
0
      tm->tm_sec = atoi(str + 4);
3044
0
      *(str + 4) = '\0';
3045
0
      tm->tm_min = atoi(str + 2);
3046
0
      *(str + 2) = '\0';
3047
0
      tm->tm_hour = atoi(str);
3048
3049
0
      return DTK_TIME;
3050
0
    }
3051
    /* hhmm? */
3052
0
    else if (len == 4)
3053
0
    {
3054
0
      *tmask = DTK_TIME_M;
3055
0
      tm->tm_sec = 0;
3056
0
      tm->tm_min = atoi(str + 2);
3057
0
      *(str + 2) = '\0';
3058
0
      tm->tm_hour = atoi(str);
3059
3060
0
      return DTK_TIME;
3061
0
    }
3062
0
  }
3063
3064
0
  return DTERR_BAD_FORMAT;
3065
0
}
3066
3067
3068
/*
3069
 * DecodeTimezone()
3070
 * Interpret string as a numeric timezone.
3071
 *
3072
 * Return 0 if okay (and set *tzp), a DTERR code if not okay.
3073
 */
3074
int
3075
DecodeTimezone(const char *str, int *tzp)
3076
0
{
3077
0
  int     tz;
3078
0
  int     hr,
3079
0
        min,
3080
0
        sec = 0;
3081
0
  char     *cp;
3082
3083
  /* leading character must be "+" or "-" */
3084
0
  if (*str != '+' && *str != '-')
3085
0
    return DTERR_BAD_FORMAT;
3086
3087
0
  errno = 0;
3088
0
  hr = strtoint(str + 1, &cp, 10);
3089
0
  if (errno == ERANGE)
3090
0
    return DTERR_TZDISP_OVERFLOW;
3091
3092
  /* explicit delimiter? */
3093
0
  if (*cp == ':')
3094
0
  {
3095
0
    errno = 0;
3096
0
    min = strtoint(cp + 1, &cp, 10);
3097
0
    if (errno == ERANGE)
3098
0
      return DTERR_TZDISP_OVERFLOW;
3099
0
    if (*cp == ':')
3100
0
    {
3101
0
      errno = 0;
3102
0
      sec = strtoint(cp + 1, &cp, 10);
3103
0
      if (errno == ERANGE)
3104
0
        return DTERR_TZDISP_OVERFLOW;
3105
0
    }
3106
0
  }
3107
  /* otherwise, might have run things together... */
3108
0
  else if (*cp == '\0' && strlen(str) > 3)
3109
0
  {
3110
0
    min = hr % 100;
3111
0
    hr = hr / 100;
3112
    /* we could, but don't, support a run-together hhmmss format */
3113
0
  }
3114
0
  else
3115
0
    min = 0;
3116
3117
  /* Range-check the values; see notes in datatype/timestamp.h */
3118
0
  if (hr < 0 || hr > MAX_TZDISP_HOUR)
3119
0
    return DTERR_TZDISP_OVERFLOW;
3120
0
  if (min < 0 || min >= MINS_PER_HOUR)
3121
0
    return DTERR_TZDISP_OVERFLOW;
3122
0
  if (sec < 0 || sec >= SECS_PER_MINUTE)
3123
0
    return DTERR_TZDISP_OVERFLOW;
3124
3125
0
  tz = (hr * MINS_PER_HOUR + min) * SECS_PER_MINUTE + sec;
3126
0
  if (*str == '-')
3127
0
    tz = -tz;
3128
3129
0
  *tzp = -tz;
3130
3131
0
  if (*cp != '\0')
3132
0
    return DTERR_BAD_FORMAT;
3133
3134
0
  return 0;
3135
0
}
3136
3137
3138
/*
3139
 * DecodeTimezoneAbbrev()
3140
 * Interpret string as a timezone abbreviation, if possible.
3141
 *
3142
 * Sets *ftype to an abbreviation type (TZ, DTZ, or DYNTZ), or UNKNOWN_FIELD if
3143
 * string is not any known abbreviation.  On success, set *offset and *tz to
3144
 * represent the UTC offset (for TZ or DTZ) or underlying zone (for DYNTZ).
3145
 * Note that full timezone names (such as America/New_York) are not handled
3146
 * here, mostly for historical reasons.
3147
 *
3148
 * The function result is 0 or a DTERR code; in the latter case, *extra
3149
 * is filled as needed.  Note that unknown-abbreviation is not considered
3150
 * an error case.  Also note that many callers assume that the DTERR code
3151
 * is one that DateTimeParseError does not require "str" or "datatype"
3152
 * strings for.
3153
 *
3154
 * Given string must be lowercased already.
3155
 *
3156
 * Implement a cache lookup since it is likely that dates
3157
 *  will be related in format.
3158
 */
3159
int
3160
DecodeTimezoneAbbrev(int field, const char *lowtoken,
3161
           int *ftype, int *offset, pg_tz **tz,
3162
           DateTimeErrorExtra *extra)
3163
0
{
3164
0
  TzAbbrevCache *tzc = &tzabbrevcache[field];
3165
0
  bool    isfixed;
3166
0
  int     isdst;
3167
0
  const datetkn *tp;
3168
3169
  /*
3170
   * Do we have a cached result?  Use strncmp so that we match truncated
3171
   * names, although we shouldn't really see that happen with normal
3172
   * abbreviations.
3173
   */
3174
0
  if (strncmp(lowtoken, tzc->abbrev, TOKMAXLEN) == 0)
3175
0
  {
3176
0
    *ftype = tzc->ftype;
3177
0
    *offset = tzc->offset;
3178
0
    *tz = tzc->tz;
3179
0
    return 0;
3180
0
  }
3181
3182
  /*
3183
   * See if the current session_timezone recognizes it.  Checking this
3184
   * before zoneabbrevtbl allows us to correctly handle abbreviations whose
3185
   * meaning varies across zones, such as "LMT".
3186
   */
3187
0
  if (session_timezone &&
3188
0
    TimeZoneAbbrevIsKnown(lowtoken, session_timezone,
3189
0
                &isfixed, offset, &isdst))
3190
0
  {
3191
0
    *ftype = (isfixed ? (isdst ? DTZ : TZ) : DYNTZ);
3192
0
    *tz = (isfixed ? NULL : session_timezone);
3193
    /* flip sign to agree with the convention used in zoneabbrevtbl */
3194
0
    *offset = -(*offset);
3195
    /* cache result; use strlcpy to truncate name if necessary */
3196
0
    strlcpy(tzc->abbrev, lowtoken, TOKMAXLEN + 1);
3197
0
    tzc->ftype = *ftype;
3198
0
    tzc->offset = *offset;
3199
0
    tzc->tz = *tz;
3200
0
    return 0;
3201
0
  }
3202
3203
  /* Nope, so look in zoneabbrevtbl */
3204
0
  if (zoneabbrevtbl)
3205
0
    tp = datebsearch(lowtoken, zoneabbrevtbl->abbrevs,
3206
0
             zoneabbrevtbl->numabbrevs);
3207
0
  else
3208
0
    tp = NULL;
3209
0
  if (tp == NULL)
3210
0
  {
3211
0
    *ftype = UNKNOWN_FIELD;
3212
0
    *offset = 0;
3213
0
    *tz = NULL;
3214
    /* failure results are not cached */
3215
0
  }
3216
0
  else
3217
0
  {
3218
0
    *ftype = tp->type;
3219
0
    if (tp->type == DYNTZ)
3220
0
    {
3221
0
      *offset = 0;
3222
0
      *tz = FetchDynamicTimeZone(zoneabbrevtbl, tp, extra);
3223
0
      if (*tz == NULL)
3224
0
        return DTERR_BAD_ZONE_ABBREV;
3225
0
    }
3226
0
    else
3227
0
    {
3228
0
      *offset = tp->value;
3229
0
      *tz = NULL;
3230
0
    }
3231
3232
    /* cache result; use strlcpy to truncate name if necessary */
3233
0
    strlcpy(tzc->abbrev, lowtoken, TOKMAXLEN + 1);
3234
0
    tzc->ftype = *ftype;
3235
0
    tzc->offset = *offset;
3236
0
    tzc->tz = *tz;
3237
0
  }
3238
3239
0
  return 0;
3240
0
}
3241
3242
/*
3243
 * Reset tzabbrevcache after a change in session_timezone.
3244
 */
3245
void
3246
ClearTimeZoneAbbrevCache(void)
3247
2
{
3248
2
  memset(tzabbrevcache, 0, sizeof(tzabbrevcache));
3249
2
}
3250
3251
3252
/*
3253
 * DecodeSpecial()
3254
 * Decode text string using lookup table.
3255
 *
3256
 * Recognizes the keywords listed in datetktbl.
3257
 * Note: at one time this would also recognize timezone abbreviations,
3258
 * but no more; use DecodeTimezoneAbbrev for that.
3259
 *
3260
 * Given string must be lowercased already.
3261
 *
3262
 * Implement a cache lookup since it is likely that dates
3263
 *  will be related in format.
3264
 */
3265
int
3266
DecodeSpecial(int field, const char *lowtoken, int *val)
3267
0
{
3268
0
  int     type;
3269
0
  const datetkn *tp;
3270
3271
0
  tp = datecache[field];
3272
  /* use strncmp so that we match truncated tokens */
3273
0
  if (tp == NULL || strncmp(lowtoken, tp->token, TOKMAXLEN) != 0)
3274
0
  {
3275
0
    tp = datebsearch(lowtoken, datetktbl, szdatetktbl);
3276
0
  }
3277
0
  if (tp == NULL)
3278
0
  {
3279
0
    type = UNKNOWN_FIELD;
3280
0
    *val = 0;
3281
0
  }
3282
0
  else
3283
0
  {
3284
0
    datecache[field] = tp;
3285
0
    type = tp->type;
3286
0
    *val = tp->value;
3287
0
  }
3288
3289
0
  return type;
3290
0
}
3291
3292
3293
/*
3294
 * DecodeTimezoneName()
3295
 * Interpret string as a timezone abbreviation or name.
3296
 * Throw error if the name is not recognized.
3297
 *
3298
 * The return value indicates what kind of zone identifier it is:
3299
 *  TZNAME_FIXED_OFFSET: fixed offset from UTC
3300
 *  TZNAME_DYNTZ: dynamic timezone abbreviation
3301
 *  TZNAME_ZONE: full tzdb zone name
3302
 *
3303
 * For TZNAME_FIXED_OFFSET, *offset receives the UTC offset (in seconds,
3304
 * with ISO sign convention: positive is east of Greenwich).
3305
 * For the other two cases, *tz receives the timezone struct representing
3306
 * the zone name or the abbreviation's underlying zone.
3307
 */
3308
int
3309
DecodeTimezoneName(const char *tzname, int *offset, pg_tz **tz)
3310
0
{
3311
0
  char     *lowzone;
3312
0
  int     dterr,
3313
0
        type;
3314
0
  DateTimeErrorExtra extra;
3315
3316
  /*
3317
   * First we look in the timezone abbreviation table (to handle cases like
3318
   * "EST"), and if that fails, we look in the timezone database (to handle
3319
   * cases like "America/New_York").  This matches the order in which
3320
   * timestamp input checks the cases; it's important because the timezone
3321
   * database unwisely uses a few zone names that are identical to offset
3322
   * abbreviations.
3323
   */
3324
3325
  /* DecodeTimezoneAbbrev requires lowercase input */
3326
0
  lowzone = downcase_truncate_identifier(tzname,
3327
0
                       strlen(tzname),
3328
0
                       false);
3329
3330
0
  dterr = DecodeTimezoneAbbrev(0, lowzone, &type, offset, tz, &extra);
3331
0
  if (dterr)
3332
0
    DateTimeParseError(dterr, &extra, NULL, NULL, NULL);
3333
3334
0
  if (type == TZ || type == DTZ)
3335
0
  {
3336
    /* fixed-offset abbreviation, return the offset */
3337
0
    return TZNAME_FIXED_OFFSET;
3338
0
  }
3339
0
  else if (type == DYNTZ)
3340
0
  {
3341
    /* dynamic-offset abbreviation, return its referenced timezone */
3342
0
    return TZNAME_DYNTZ;
3343
0
  }
3344
0
  else
3345
0
  {
3346
    /* try it as a full zone name */
3347
0
    *tz = pg_tzset(tzname);
3348
0
    if (*tz == NULL)
3349
0
      ereport(ERROR,
3350
0
          (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3351
0
           errmsg("time zone \"%s\" not recognized", tzname)));
3352
0
    return TZNAME_ZONE;
3353
0
  }
3354
0
}
3355
3356
/*
3357
 * DecodeTimezoneNameToTz()
3358
 * Interpret string as a timezone abbreviation or name.
3359
 * Throw error if the name is not recognized.
3360
 *
3361
 * This is a simple wrapper for DecodeTimezoneName that produces a pg_tz *
3362
 * result in all cases.
3363
 */
3364
pg_tz *
3365
DecodeTimezoneNameToTz(const char *tzname)
3366
0
{
3367
0
  pg_tz    *result;
3368
0
  int     offset;
3369
3370
0
  if (DecodeTimezoneName(tzname, &offset, &result) == TZNAME_FIXED_OFFSET)
3371
0
  {
3372
    /* fixed-offset abbreviation, get a pg_tz descriptor for that */
3373
0
    result = pg_tzset_offset(-offset);  /* flip to POSIX sign convention */
3374
0
  }
3375
0
  return result;
3376
0
}
3377
3378
/*
3379
 * DecodeTimezoneAbbrevPrefix()
3380
 * Interpret prefix of string as a timezone abbreviation, if possible.
3381
 *
3382
 * This has roughly the same functionality as DecodeTimezoneAbbrev(),
3383
 * but the API is adapted to the needs of formatting.c.  Notably,
3384
 * we will match the longest possible prefix of the given string
3385
 * rather than insisting on a complete match, and downcasing is applied
3386
 * here rather than in the caller.
3387
 *
3388
 * Returns the length of the timezone abbreviation, or -1 if not recognized.
3389
 * On success, sets *offset to the GMT offset for the abbreviation if it
3390
 * is a fixed-offset abbreviation, or sets *tz to the pg_tz struct for
3391
 * a dynamic abbreviation.
3392
 */
3393
int
3394
DecodeTimezoneAbbrevPrefix(const char *str, int *offset, pg_tz **tz)
3395
0
{
3396
0
  char    lowtoken[TOKMAXLEN + 1];
3397
0
  int     len;
3398
3399
0
  *offset = 0;        /* avoid uninitialized vars on failure */
3400
0
  *tz = NULL;
3401
3402
  /* Downcase as much of the string as we could need */
3403
0
  for (len = 0; len < TOKMAXLEN; len++)
3404
0
  {
3405
0
    if (*str == '\0' || !isalpha((unsigned char) *str))
3406
0
      break;
3407
0
    lowtoken[len] = pg_tolower((unsigned char) *str++);
3408
0
  }
3409
0
  lowtoken[len] = '\0';
3410
3411
  /*
3412
   * We could avoid doing repeated binary searches if we cared to duplicate
3413
   * datebsearch here, but it's not clear that such an optimization would be
3414
   * worth the trouble.  In common cases there's probably not anything after
3415
   * the zone abbrev anyway.  So just search with successively truncated
3416
   * strings.
3417
   */
3418
0
  while (len > 0)
3419
0
  {
3420
0
    bool    isfixed;
3421
0
    int     isdst;
3422
0
    const datetkn *tp;
3423
3424
    /* See if the current session_timezone recognizes it. */
3425
0
    if (session_timezone &&
3426
0
      TimeZoneAbbrevIsKnown(lowtoken, session_timezone,
3427
0
                  &isfixed, offset, &isdst))
3428
0
    {
3429
0
      if (isfixed)
3430
0
      {
3431
        /* flip sign to agree with the convention in zoneabbrevtbl */
3432
0
        *offset = -(*offset);
3433
0
      }
3434
0
      else
3435
0
      {
3436
        /* Caller must resolve the abbrev's current meaning */
3437
0
        *tz = session_timezone;
3438
0
      }
3439
0
      return len;
3440
0
    }
3441
3442
    /* Known in zoneabbrevtbl? */
3443
0
    if (zoneabbrevtbl)
3444
0
      tp = datebsearch(lowtoken, zoneabbrevtbl->abbrevs,
3445
0
               zoneabbrevtbl->numabbrevs);
3446
0
    else
3447
0
      tp = NULL;
3448
0
    if (tp != NULL)
3449
0
    {
3450
0
      if (tp->type == DYNTZ)
3451
0
      {
3452
0
        DateTimeErrorExtra extra;
3453
0
        pg_tz    *tzp = FetchDynamicTimeZone(zoneabbrevtbl, tp,
3454
0
                             &extra);
3455
3456
0
        if (tzp != NULL)
3457
0
        {
3458
          /* Caller must resolve the abbrev's current meaning */
3459
0
          *tz = tzp;
3460
0
          return len;
3461
0
        }
3462
0
      }
3463
0
      else
3464
0
      {
3465
        /* Fixed-offset zone abbrev, so it's easy */
3466
0
        *offset = tp->value;
3467
0
        return len;
3468
0
      }
3469
0
    }
3470
3471
    /* Nope, try the next shorter string. */
3472
0
    lowtoken[--len] = '\0';
3473
0
  }
3474
3475
  /* Did not find a match */
3476
0
  return -1;
3477
0
}
3478
3479
3480
/*
3481
 * ClearPgItmIn
3482
 *
3483
 * Zero out a pg_itm_in
3484
 */
3485
static inline void
3486
ClearPgItmIn(struct pg_itm_in *itm_in)
3487
0
{
3488
0
  itm_in->tm_usec = 0;
3489
0
  itm_in->tm_mday = 0;
3490
0
  itm_in->tm_mon = 0;
3491
0
  itm_in->tm_year = 0;
3492
0
}
3493
3494
3495
/*
3496
 * DecodeInterval()
3497
 * Interpret previously parsed fields for general time interval.
3498
 * Returns 0 if successful, DTERR code if bogus input detected.
3499
 * dtype and itm_in are output parameters.
3500
 *
3501
 * Allow "date" field DTK_DATE since this could be just
3502
 *  an unsigned floating point number. - thomas 1997-11-16
3503
 *
3504
 * Allow ISO-style time span, with implicit units on number of days
3505
 *  preceding an hh:mm:ss field. - thomas 1998-04-30
3506
 *
3507
 * itm_in remains undefined for infinite interval values for which dtype alone
3508
 * suffices.
3509
 */
3510
int
3511
DecodeInterval(char **field, int *ftype, int nf, int range,
3512
         int *dtype, struct pg_itm_in *itm_in)
3513
0
{
3514
0
  bool    force_negative = false;
3515
0
  bool    is_before = false;
3516
0
  bool    parsing_unit_val = false;
3517
0
  char     *cp;
3518
0
  int     fmask = 0,
3519
0
        tmask,
3520
0
        type,
3521
0
        uval;
3522
0
  int     i;
3523
0
  int     dterr;
3524
0
  int64   val;
3525
0
  double    fval;
3526
3527
0
  *dtype = DTK_DELTA;
3528
0
  type = IGNORE_DTF;
3529
0
  ClearPgItmIn(itm_in);
3530
3531
  /*----------
3532
   * The SQL standard defines the interval literal
3533
   *   '-1 1:00:00'
3534
   * to mean "negative 1 days and negative 1 hours", while Postgres
3535
   * traditionally treats this as meaning "negative 1 days and positive
3536
   * 1 hours".  In SQL_STANDARD intervalstyle, we apply the leading sign
3537
   * to all fields if there are no other explicit signs.
3538
   *
3539
   * We leave the signs alone if there are additional explicit signs.
3540
   * This protects us against misinterpreting postgres-style dump output,
3541
   * since the postgres-style output code has always put an explicit sign on
3542
   * all fields following a negative field.  But note that SQL-spec output
3543
   * is ambiguous and can be misinterpreted on load!  (So it's best practice
3544
   * to dump in postgres style, not SQL style.)
3545
   *----------
3546
   */
3547
0
  if (IntervalStyle == INTSTYLE_SQL_STANDARD && nf > 0 && *field[0] == '-')
3548
0
  {
3549
0
    force_negative = true;
3550
    /* Check for additional explicit signs */
3551
0
    for (i = 1; i < nf; i++)
3552
0
    {
3553
0
      if (*field[i] == '-' || *field[i] == '+')
3554
0
      {
3555
0
        force_negative = false;
3556
0
        break;
3557
0
      }
3558
0
    }
3559
0
  }
3560
3561
  /* read through list backwards to pick up units before values */
3562
0
  for (i = nf - 1; i >= 0; i--)
3563
0
  {
3564
0
    switch (ftype[i])
3565
0
    {
3566
0
      case DTK_TIME:
3567
0
        dterr = DecodeTimeForInterval(field[i], fmask, range,
3568
0
                        &tmask, itm_in);
3569
0
        if (dterr)
3570
0
          return dterr;
3571
0
        if (force_negative &&
3572
0
          itm_in->tm_usec > 0)
3573
0
          itm_in->tm_usec = -itm_in->tm_usec;
3574
0
        type = DTK_DAY;
3575
0
        parsing_unit_val = false;
3576
0
        break;
3577
3578
0
      case DTK_TZ:
3579
3580
        /*
3581
         * Timezone means a token with a leading sign character and at
3582
         * least one digit; there could be ':', '.', '-' embedded in
3583
         * it as well.
3584
         */
3585
0
        Assert(*field[i] == '-' || *field[i] == '+');
3586
3587
        /*
3588
         * Check for signed hh:mm or hh:mm:ss.  If so, process exactly
3589
         * like DTK_TIME case above, plus handling the sign.
3590
         */
3591
0
        if (strchr(field[i] + 1, ':') != NULL &&
3592
0
          DecodeTimeForInterval(field[i] + 1, fmask, range,
3593
0
                      &tmask, itm_in) == 0)
3594
0
        {
3595
0
          if (*field[i] == '-')
3596
0
          {
3597
            /* flip the sign on time field */
3598
0
            if (itm_in->tm_usec == PG_INT64_MIN)
3599
0
              return DTERR_FIELD_OVERFLOW;
3600
0
            itm_in->tm_usec = -itm_in->tm_usec;
3601
0
          }
3602
3603
0
          if (force_negative &&
3604
0
            itm_in->tm_usec > 0)
3605
0
            itm_in->tm_usec = -itm_in->tm_usec;
3606
3607
          /*
3608
           * Set the next type to be a day, if units are not
3609
           * specified. This handles the case of '1 +02:03' since we
3610
           * are reading right to left.
3611
           */
3612
0
          type = DTK_DAY;
3613
0
          parsing_unit_val = false;
3614
0
          break;
3615
0
        }
3616
3617
        /*
3618
         * Otherwise, fall through to DTK_NUMBER case, which can
3619
         * handle signed float numbers and signed year-month values.
3620
         */
3621
3622
0
        pg_fallthrough;
3623
3624
0
      case DTK_DATE:
3625
0
      case DTK_NUMBER:
3626
0
        if (type == IGNORE_DTF)
3627
0
        {
3628
          /* use typmod to decide what rightmost field is */
3629
0
          switch (range)
3630
0
          {
3631
0
            case INTERVAL_MASK(YEAR):
3632
0
              type = DTK_YEAR;
3633
0
              break;
3634
0
            case INTERVAL_MASK(MONTH):
3635
0
            case INTERVAL_MASK(YEAR) | INTERVAL_MASK(MONTH):
3636
0
              type = DTK_MONTH;
3637
0
              break;
3638
0
            case INTERVAL_MASK(DAY):
3639
0
              type = DTK_DAY;
3640
0
              break;
3641
0
            case INTERVAL_MASK(HOUR):
3642
0
            case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR):
3643
0
              type = DTK_HOUR;
3644
0
              break;
3645
0
            case INTERVAL_MASK(MINUTE):
3646
0
            case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE):
3647
0
            case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE):
3648
0
              type = DTK_MINUTE;
3649
0
              break;
3650
0
            case INTERVAL_MASK(SECOND):
3651
0
            case INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
3652
0
            case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
3653
0
            case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
3654
0
              type = DTK_SECOND;
3655
0
              break;
3656
0
            default:
3657
0
              type = DTK_SECOND;
3658
0
              break;
3659
0
          }
3660
0
        }
3661
3662
0
        errno = 0;
3663
0
        val = strtoi64(field[i], &cp, 10);
3664
0
        if (errno == ERANGE)
3665
0
          return DTERR_FIELD_OVERFLOW;
3666
3667
0
        if (*cp == '-')
3668
0
        {
3669
          /* SQL "years-months" syntax */
3670
0
          int     val2;
3671
3672
0
          val2 = strtoint(cp + 1, &cp, 10);
3673
0
          if (errno == ERANGE || val2 < 0 || val2 >= MONTHS_PER_YEAR)
3674
0
            return DTERR_FIELD_OVERFLOW;
3675
0
          if (*cp != '\0')
3676
0
            return DTERR_BAD_FORMAT;
3677
0
          type = DTK_MONTH;
3678
0
          if (*field[i] == '-')
3679
0
            val2 = -val2;
3680
0
          if (pg_mul_s64_overflow(val, MONTHS_PER_YEAR, &val))
3681
0
            return DTERR_FIELD_OVERFLOW;
3682
0
          if (pg_add_s64_overflow(val, val2, &val))
3683
0
            return DTERR_FIELD_OVERFLOW;
3684
0
          fval = 0;
3685
0
        }
3686
0
        else if (*cp == '.')
3687
0
        {
3688
0
          dterr = ParseFraction(cp, &fval);
3689
0
          if (dterr)
3690
0
            return dterr;
3691
0
          if (*field[i] == '-')
3692
0
            fval = -fval;
3693
0
        }
3694
0
        else if (*cp == '\0')
3695
0
          fval = 0;
3696
0
        else
3697
0
          return DTERR_BAD_FORMAT;
3698
3699
0
        tmask = 0;    /* DTK_M(type); */
3700
3701
0
        if (force_negative)
3702
0
        {
3703
          /* val and fval should be of same sign, but test anyway */
3704
0
          if (val > 0)
3705
0
            val = -val;
3706
0
          if (fval > 0)
3707
0
            fval = -fval;
3708
0
        }
3709
3710
0
        switch (type)
3711
0
        {
3712
0
          case DTK_MICROSEC:
3713
0
            if (!AdjustMicroseconds(val, fval, 1, itm_in))
3714
0
              return DTERR_FIELD_OVERFLOW;
3715
0
            tmask = DTK_M(MICROSECOND);
3716
0
            break;
3717
3718
0
          case DTK_MILLISEC:
3719
0
            if (!AdjustMicroseconds(val, fval, 1000, itm_in))
3720
0
              return DTERR_FIELD_OVERFLOW;
3721
0
            tmask = DTK_M(MILLISECOND);
3722
0
            break;
3723
3724
0
          case DTK_SECOND:
3725
0
            if (!AdjustMicroseconds(val, fval, USECS_PER_SEC, itm_in))
3726
0
              return DTERR_FIELD_OVERFLOW;
3727
3728
            /*
3729
             * If any subseconds were specified, consider this
3730
             * microsecond and millisecond input as well.
3731
             */
3732
0
            if (fval == 0)
3733
0
              tmask = DTK_M(SECOND);
3734
0
            else
3735
0
              tmask = DTK_ALL_SECS_M;
3736
0
            break;
3737
3738
0
          case DTK_MINUTE:
3739
0
            if (!AdjustMicroseconds(val, fval, USECS_PER_MINUTE, itm_in))
3740
0
              return DTERR_FIELD_OVERFLOW;
3741
0
            tmask = DTK_M(MINUTE);
3742
0
            break;
3743
3744
0
          case DTK_HOUR:
3745
0
            if (!AdjustMicroseconds(val, fval, USECS_PER_HOUR, itm_in))
3746
0
              return DTERR_FIELD_OVERFLOW;
3747
0
            tmask = DTK_M(HOUR);
3748
0
            type = DTK_DAY; /* set for next field */
3749
0
            break;
3750
3751
0
          case DTK_DAY:
3752
0
            if (!AdjustDays(val, 1, itm_in) ||
3753
0
              !AdjustFractMicroseconds(fval, USECS_PER_DAY, itm_in))
3754
0
              return DTERR_FIELD_OVERFLOW;
3755
0
            tmask = DTK_M(DAY);
3756
0
            break;
3757
3758
0
          case DTK_WEEK:
3759
0
            if (!AdjustDays(val, 7, itm_in) ||
3760
0
              !AdjustFractDays(fval, 7, itm_in))
3761
0
              return DTERR_FIELD_OVERFLOW;
3762
0
            tmask = DTK_M(WEEK);
3763
0
            break;
3764
3765
0
          case DTK_MONTH:
3766
0
            if (!AdjustMonths(val, itm_in) ||
3767
0
              !AdjustFractDays(fval, DAYS_PER_MONTH, itm_in))
3768
0
              return DTERR_FIELD_OVERFLOW;
3769
0
            tmask = DTK_M(MONTH);
3770
0
            break;
3771
3772
0
          case DTK_YEAR:
3773
0
            if (!AdjustYears(val, 1, itm_in) ||
3774
0
              !AdjustFractYears(fval, 1, itm_in))
3775
0
              return DTERR_FIELD_OVERFLOW;
3776
0
            tmask = DTK_M(YEAR);
3777
0
            break;
3778
3779
0
          case DTK_DECADE:
3780
0
            if (!AdjustYears(val, 10, itm_in) ||
3781
0
              !AdjustFractYears(fval, 10, itm_in))
3782
0
              return DTERR_FIELD_OVERFLOW;
3783
0
            tmask = DTK_M(DECADE);
3784
0
            break;
3785
3786
0
          case DTK_CENTURY:
3787
0
            if (!AdjustYears(val, 100, itm_in) ||
3788
0
              !AdjustFractYears(fval, 100, itm_in))
3789
0
              return DTERR_FIELD_OVERFLOW;
3790
0
            tmask = DTK_M(CENTURY);
3791
0
            break;
3792
3793
0
          case DTK_MILLENNIUM:
3794
0
            if (!AdjustYears(val, 1000, itm_in) ||
3795
0
              !AdjustFractYears(fval, 1000, itm_in))
3796
0
              return DTERR_FIELD_OVERFLOW;
3797
0
            tmask = DTK_M(MILLENNIUM);
3798
0
            break;
3799
3800
0
          default:
3801
0
            return DTERR_BAD_FORMAT;
3802
0
        }
3803
0
        parsing_unit_val = false;
3804
0
        break;
3805
3806
0
      case DTK_STRING:
3807
0
      case DTK_SPECIAL:
3808
        /* reject consecutive unhandled units */
3809
0
        if (parsing_unit_val)
3810
0
          return DTERR_BAD_FORMAT;
3811
0
        type = DecodeUnits(i, field[i], &uval);
3812
0
        if (type == UNKNOWN_FIELD)
3813
0
          type = DecodeSpecial(i, field[i], &uval);
3814
0
        if (type == IGNORE_DTF)
3815
0
          continue;
3816
3817
0
        tmask = 0;    /* DTK_M(type); */
3818
0
        switch (type)
3819
0
        {
3820
0
          case UNITS:
3821
0
            type = uval;
3822
0
            parsing_unit_val = true;
3823
0
            break;
3824
3825
0
          case AGO:
3826
3827
            /*
3828
             * "ago" is only allowed to appear at the end of the
3829
             * interval.
3830
             */
3831
0
            if (i != nf - 1)
3832
0
              return DTERR_BAD_FORMAT;
3833
0
            is_before = true;
3834
0
            type = uval;
3835
0
            break;
3836
3837
0
          case RESERV:
3838
0
            tmask = (DTK_DATE_M | DTK_TIME_M);
3839
3840
            /*
3841
             * Only reserved words corresponding to infinite
3842
             * intervals are accepted.
3843
             */
3844
0
            if (uval != DTK_LATE && uval != DTK_EARLY)
3845
0
              return DTERR_BAD_FORMAT;
3846
3847
            /*
3848
             * Infinity cannot be followed by anything else. We
3849
             * could allow "ago" to reverse the sign of infinity
3850
             * but using signed infinity is more intuitive.
3851
             */
3852
0
            if (i != nf - 1)
3853
0
              return DTERR_BAD_FORMAT;
3854
3855
0
            *dtype = uval;
3856
0
            break;
3857
3858
0
          default:
3859
0
            return DTERR_BAD_FORMAT;
3860
0
        }
3861
0
        break;
3862
3863
0
      default:
3864
0
        return DTERR_BAD_FORMAT;
3865
0
    }
3866
3867
0
    if (tmask & fmask)
3868
0
      return DTERR_BAD_FORMAT;
3869
0
    fmask |= tmask;
3870
0
  }
3871
3872
  /* ensure that at least one time field has been found */
3873
0
  if (fmask == 0)
3874
0
    return DTERR_BAD_FORMAT;
3875
3876
  /* reject if unit appeared and was never handled */
3877
0
  if (parsing_unit_val)
3878
0
    return DTERR_BAD_FORMAT;
3879
3880
  /* finally, AGO negates everything */
3881
0
  if (is_before)
3882
0
  {
3883
0
    if (itm_in->tm_usec == PG_INT64_MIN ||
3884
0
      itm_in->tm_mday == INT_MIN ||
3885
0
      itm_in->tm_mon == INT_MIN ||
3886
0
      itm_in->tm_year == INT_MIN)
3887
0
      return DTERR_FIELD_OVERFLOW;
3888
3889
0
    itm_in->tm_usec = -itm_in->tm_usec;
3890
0
    itm_in->tm_mday = -itm_in->tm_mday;
3891
0
    itm_in->tm_mon = -itm_in->tm_mon;
3892
0
    itm_in->tm_year = -itm_in->tm_year;
3893
0
  }
3894
3895
0
  return 0;
3896
0
}
3897
3898
3899
/*
3900
 * Helper functions to avoid duplicated code in DecodeISO8601Interval.
3901
 *
3902
 * Parse a decimal value and break it into integer and fractional parts.
3903
 * Set *endptr to end+1 of the parsed substring.
3904
 * Returns 0 or DTERR code.
3905
 */
3906
static int
3907
ParseISO8601Number(char *str, char **endptr, int64 *ipart, double *fpart)
3908
0
{
3909
0
  double    val;
3910
3911
  /*
3912
   * Historically this has accepted anything that strtod() would take,
3913
   * notably including "e" notation, so continue doing that.  This is
3914
   * slightly annoying because the precision of double is less than that of
3915
   * int64, so we would lose accuracy for inputs larger than 2^53 or so.
3916
   * However, historically we rejected inputs outside the int32 range,
3917
   * making that concern moot.  What we do now is reject abs(val) above
3918
   * 1.0e15 (a round number a bit less than 2^50), so that any accepted
3919
   * value will have an exact integer part, and thereby a fraction part with
3920
   * abs(*fpart) less than 1.  In the absence of field complaints it doesn't
3921
   * seem worth working harder.
3922
   */
3923
0
  if (!(isdigit((unsigned char) *str) || *str == '-' || *str == '.'))
3924
0
    return DTERR_BAD_FORMAT;
3925
0
  errno = 0;
3926
0
  val = strtod(str, endptr);
3927
  /* did we not see anything that looks like a double? */
3928
0
  if (*endptr == str || errno != 0)
3929
0
    return DTERR_BAD_FORMAT;
3930
  /* watch out for overflow, including infinities; reject NaN too */
3931
0
  if (isnan(val) || val < -1.0e15 || val > 1.0e15)
3932
0
    return DTERR_FIELD_OVERFLOW;
3933
  /* be very sure we truncate towards zero (cf dtrunc()) */
3934
0
  if (val >= 0)
3935
0
    *ipart = (int64) floor(val);
3936
0
  else
3937
0
    *ipart = (int64) -floor(-val);
3938
0
  *fpart = val - *ipart;
3939
  /* Callers expect this to hold */
3940
0
  Assert(*fpart > -1.0 && *fpart < 1.0);
3941
0
  return 0;
3942
0
}
3943
3944
/*
3945
 * Determine number of integral digits in a valid ISO 8601 number field
3946
 * (we should ignore sign and any fraction part)
3947
 */
3948
static int
3949
ISO8601IntegerWidth(char *fieldstart)
3950
0
{
3951
  /* We might have had a leading '-' */
3952
0
  if (*fieldstart == '-')
3953
0
    fieldstart++;
3954
0
  return strspn(fieldstart, "0123456789");
3955
0
}
3956
3957
3958
/*
3959
 * DecodeISO8601Interval()
3960
 *  Decode an ISO 8601 time interval of the "format with designators"
3961
 *  (section 4.4.3.2) or "alternative format" (section 4.4.3.3)
3962
 *  Examples:  P1D  for 1 day
3963
 *         PT1H for 1 hour
3964
 *         P2Y6M7DT1H30M for 2 years, 6 months, 7 days 1 hour 30 min
3965
 *         P0002-06-07T01:30:00 the same value in alternative format
3966
 *
3967
 * Returns 0 if successful, DTERR code if bogus input detected.
3968
 * Note: error code should be DTERR_BAD_FORMAT if input doesn't look like
3969
 * ISO8601, otherwise this could cause unexpected error messages.
3970
 * dtype and itm_in are output parameters.
3971
 *
3972
 *  A couple exceptions from the spec:
3973
 *   - a week field ('W') may coexist with other units
3974
 *   - allows decimals in fields other than the least significant unit.
3975
 */
3976
int
3977
DecodeISO8601Interval(char *str,
3978
            int *dtype, struct pg_itm_in *itm_in)
3979
0
{
3980
0
  bool    datepart = true;
3981
0
  bool    havefield = false;
3982
3983
0
  *dtype = DTK_DELTA;
3984
0
  ClearPgItmIn(itm_in);
3985
3986
0
  if (strlen(str) < 2 || str[0] != 'P')
3987
0
    return DTERR_BAD_FORMAT;
3988
3989
0
  str++;
3990
0
  while (*str)
3991
0
  {
3992
0
    char     *fieldstart;
3993
0
    int64   val;
3994
0
    double    fval;
3995
0
    char    unit;
3996
0
    int     dterr;
3997
3998
0
    if (*str == 'T')   /* T indicates the beginning of the time part */
3999
0
    {
4000
0
      datepart = false;
4001
0
      havefield = false;
4002
0
      str++;
4003
0
      continue;
4004
0
    }
4005
4006
0
    fieldstart = str;
4007
0
    dterr = ParseISO8601Number(str, &str, &val, &fval);
4008
0
    if (dterr)
4009
0
      return dterr;
4010
4011
    /*
4012
     * Note: we could step off the end of the string here.  Code below
4013
     * *must* exit the loop if unit == '\0'.
4014
     */
4015
0
    unit = *str++;
4016
4017
0
    if (datepart)
4018
0
    {
4019
0
      switch (unit)   /* before T: Y M W D */
4020
0
      {
4021
0
        case 'Y':
4022
0
          if (!AdjustYears(val, 1, itm_in) ||
4023
0
            !AdjustFractYears(fval, 1, itm_in))
4024
0
            return DTERR_FIELD_OVERFLOW;
4025
0
          break;
4026
0
        case 'M':
4027
0
          if (!AdjustMonths(val, itm_in) ||
4028
0
            !AdjustFractDays(fval, DAYS_PER_MONTH, itm_in))
4029
0
            return DTERR_FIELD_OVERFLOW;
4030
0
          break;
4031
0
        case 'W':
4032
0
          if (!AdjustDays(val, 7, itm_in) ||
4033
0
            !AdjustFractDays(fval, 7, itm_in))
4034
0
            return DTERR_FIELD_OVERFLOW;
4035
0
          break;
4036
0
        case 'D':
4037
0
          if (!AdjustDays(val, 1, itm_in) ||
4038
0
            !AdjustFractMicroseconds(fval, USECS_PER_DAY, itm_in))
4039
0
            return DTERR_FIELD_OVERFLOW;
4040
0
          break;
4041
0
        case 'T':   /* ISO 8601 4.4.3.3 Alternative Format / Basic */
4042
0
        case '\0':
4043
0
          if (ISO8601IntegerWidth(fieldstart) == 8 && !havefield)
4044
0
          {
4045
0
            if (!AdjustYears(val / 10000, 1, itm_in) ||
4046
0
              !AdjustMonths((val / 100) % 100, itm_in) ||
4047
0
              !AdjustDays(val % 100, 1, itm_in) ||
4048
0
              !AdjustFractMicroseconds(fval, USECS_PER_DAY, itm_in))
4049
0
              return DTERR_FIELD_OVERFLOW;
4050
0
            if (unit == '\0')
4051
0
              return 0;
4052
0
            datepart = false;
4053
0
            havefield = false;
4054
0
            continue;
4055
0
          }
4056
          /* Else fall through to extended alternative format */
4057
0
          pg_fallthrough;
4058
0
        case '-':   /* ISO 8601 4.4.3.3 Alternative Format,
4059
                 * Extended */
4060
0
          if (havefield)
4061
0
            return DTERR_BAD_FORMAT;
4062
4063
0
          if (!AdjustYears(val, 1, itm_in) ||
4064
0
            !AdjustFractYears(fval, 1, itm_in))
4065
0
            return DTERR_FIELD_OVERFLOW;
4066
0
          if (unit == '\0')
4067
0
            return 0;
4068
0
          if (unit == 'T')
4069
0
          {
4070
0
            datepart = false;
4071
0
            havefield = false;
4072
0
            continue;
4073
0
          }
4074
4075
0
          dterr = ParseISO8601Number(str, &str, &val, &fval);
4076
0
          if (dterr)
4077
0
            return dterr;
4078
0
          if (!AdjustMonths(val, itm_in) ||
4079
0
            !AdjustFractDays(fval, DAYS_PER_MONTH, itm_in))
4080
0
            return DTERR_FIELD_OVERFLOW;
4081
0
          if (*str == '\0')
4082
0
            return 0;
4083
0
          if (*str == 'T')
4084
0
          {
4085
0
            datepart = false;
4086
0
            havefield = false;
4087
0
            continue;
4088
0
          }
4089
0
          if (*str != '-')
4090
0
            return DTERR_BAD_FORMAT;
4091
0
          str++;
4092
4093
0
          dterr = ParseISO8601Number(str, &str, &val, &fval);
4094
0
          if (dterr)
4095
0
            return dterr;
4096
0
          if (!AdjustDays(val, 1, itm_in) ||
4097
0
            !AdjustFractMicroseconds(fval, USECS_PER_DAY, itm_in))
4098
0
            return DTERR_FIELD_OVERFLOW;
4099
0
          if (*str == '\0')
4100
0
            return 0;
4101
0
          if (*str == 'T')
4102
0
          {
4103
0
            datepart = false;
4104
0
            havefield = false;
4105
0
            continue;
4106
0
          }
4107
0
          return DTERR_BAD_FORMAT;
4108
0
        default:
4109
          /* not a valid date unit suffix */
4110
0
          return DTERR_BAD_FORMAT;
4111
0
      }
4112
0
    }
4113
0
    else
4114
0
    {
4115
0
      switch (unit)   /* after T: H M S */
4116
0
      {
4117
0
        case 'H':
4118
0
          if (!AdjustMicroseconds(val, fval, USECS_PER_HOUR, itm_in))
4119
0
            return DTERR_FIELD_OVERFLOW;
4120
0
          break;
4121
0
        case 'M':
4122
0
          if (!AdjustMicroseconds(val, fval, USECS_PER_MINUTE, itm_in))
4123
0
            return DTERR_FIELD_OVERFLOW;
4124
0
          break;
4125
0
        case 'S':
4126
0
          if (!AdjustMicroseconds(val, fval, USECS_PER_SEC, itm_in))
4127
0
            return DTERR_FIELD_OVERFLOW;
4128
0
          break;
4129
0
        case '\0':    /* ISO 8601 4.4.3.3 Alternative Format */
4130
0
          if (ISO8601IntegerWidth(fieldstart) == 6 && !havefield)
4131
0
          {
4132
0
            if (!AdjustMicroseconds(val / 10000, 0, USECS_PER_HOUR, itm_in) ||
4133
0
              !AdjustMicroseconds((val / 100) % 100, 0, USECS_PER_MINUTE, itm_in) ||
4134
0
              !AdjustMicroseconds(val % 100, 0, USECS_PER_SEC, itm_in) ||
4135
0
              !AdjustFractMicroseconds(fval, 1, itm_in))
4136
0
              return DTERR_FIELD_OVERFLOW;
4137
0
            return 0;
4138
0
          }
4139
          /* Else fall through to extended alternative format */
4140
0
          pg_fallthrough;
4141
0
        case ':':   /* ISO 8601 4.4.3.3 Alternative Format,
4142
                 * Extended */
4143
0
          if (havefield)
4144
0
            return DTERR_BAD_FORMAT;
4145
4146
0
          if (!AdjustMicroseconds(val, fval, USECS_PER_HOUR, itm_in))
4147
0
            return DTERR_FIELD_OVERFLOW;
4148
0
          if (unit == '\0')
4149
0
            return 0;
4150
4151
0
          dterr = ParseISO8601Number(str, &str, &val, &fval);
4152
0
          if (dterr)
4153
0
            return dterr;
4154
0
          if (!AdjustMicroseconds(val, fval, USECS_PER_MINUTE, itm_in))
4155
0
            return DTERR_FIELD_OVERFLOW;
4156
0
          if (*str == '\0')
4157
0
            return 0;
4158
0
          if (*str != ':')
4159
0
            return DTERR_BAD_FORMAT;
4160
0
          str++;
4161
4162
0
          dterr = ParseISO8601Number(str, &str, &val, &fval);
4163
0
          if (dterr)
4164
0
            return dterr;
4165
0
          if (!AdjustMicroseconds(val, fval, USECS_PER_SEC, itm_in))
4166
0
            return DTERR_FIELD_OVERFLOW;
4167
0
          if (*str == '\0')
4168
0
            return 0;
4169
0
          return DTERR_BAD_FORMAT;
4170
4171
0
        default:
4172
          /* not a valid time unit suffix */
4173
0
          return DTERR_BAD_FORMAT;
4174
0
      }
4175
0
    }
4176
4177
0
    havefield = true;
4178
0
  }
4179
4180
0
  return 0;
4181
0
}
4182
4183
4184
/*
4185
 * DecodeUnits()
4186
 * Decode text string using lookup table.
4187
 *
4188
 * This routine recognizes keywords associated with time interval units.
4189
 *
4190
 * Given string must be lowercased already.
4191
 *
4192
 * Implement a cache lookup since it is likely that dates
4193
 *  will be related in format.
4194
 */
4195
int
4196
DecodeUnits(int field, const char *lowtoken, int *val)
4197
0
{
4198
0
  int     type;
4199
0
  const datetkn *tp;
4200
4201
0
  tp = deltacache[field];
4202
  /* use strncmp so that we match truncated tokens */
4203
0
  if (tp == NULL || strncmp(lowtoken, tp->token, TOKMAXLEN) != 0)
4204
0
  {
4205
0
    tp = datebsearch(lowtoken, deltatktbl, szdeltatktbl);
4206
0
  }
4207
0
  if (tp == NULL)
4208
0
  {
4209
0
    type = UNKNOWN_FIELD;
4210
0
    *val = 0;
4211
0
  }
4212
0
  else
4213
0
  {
4214
0
    deltacache[field] = tp;
4215
0
    type = tp->type;
4216
0
    *val = tp->value;
4217
0
  }
4218
4219
0
  return type;
4220
0
}                /* DecodeUnits() */
4221
4222
/*
4223
 * Report an error detected by one of the datetime input processing routines.
4224
 *
4225
 * dterr is the error code, and *extra contains any auxiliary info we need
4226
 * for the error report.  extra can be NULL if not needed for the particular
4227
 * dterr value.
4228
 *
4229
 * str is the original input string, and datatype is the name of the datatype
4230
 * we were trying to accept.  (For some DTERR codes, these are not used and
4231
 * can be NULL.)
4232
 *
4233
 * If escontext points to an ErrorSaveContext node, that is filled instead
4234
 * of throwing an error.
4235
 *
4236
 * Note: it might seem useless to distinguish DTERR_INTERVAL_OVERFLOW and
4237
 * DTERR_TZDISP_OVERFLOW from DTERR_FIELD_OVERFLOW, but SQL99 mandates three
4238
 * separate SQLSTATE codes, so ...
4239
 */
4240
void
4241
DateTimeParseError(int dterr, DateTimeErrorExtra *extra,
4242
           const char *str, const char *datatype,
4243
           Node *escontext)
4244
0
{
4245
0
  switch (dterr)
4246
0
  {
4247
0
    case DTERR_FIELD_OVERFLOW:
4248
0
      errsave(escontext,
4249
0
          (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW),
4250
0
           errmsg("date/time field value out of range: \"%s\"",
4251
0
              str)));
4252
0
      break;
4253
0
    case DTERR_MD_FIELD_OVERFLOW:
4254
      /* <nanny>same as above, but add hint about DateStyle</nanny> */
4255
0
      errsave(escontext,
4256
0
          (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW),
4257
0
           errmsg("date/time field value out of range: \"%s\"",
4258
0
              str),
4259
0
           errhint("Perhaps you need a different \"DateStyle\" setting.")));
4260
0
      break;
4261
0
    case DTERR_INTERVAL_OVERFLOW:
4262
0
      errsave(escontext,
4263
0
          (errcode(ERRCODE_INTERVAL_FIELD_OVERFLOW),
4264
0
           errmsg("interval field value out of range: \"%s\"",
4265
0
              str)));
4266
0
      break;
4267
0
    case DTERR_TZDISP_OVERFLOW:
4268
0
      errsave(escontext,
4269
0
          (errcode(ERRCODE_INVALID_TIME_ZONE_DISPLACEMENT_VALUE),
4270
0
           errmsg("time zone displacement out of range: \"%s\"",
4271
0
              str)));
4272
0
      break;
4273
0
    case DTERR_BAD_TIMEZONE:
4274
0
      errsave(escontext,
4275
0
          (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4276
0
           errmsg("time zone \"%s\" not recognized",
4277
0
              extra->dtee_timezone)));
4278
0
      break;
4279
0
    case DTERR_BAD_ZONE_ABBREV:
4280
0
      errsave(escontext,
4281
0
          (errcode(ERRCODE_CONFIG_FILE_ERROR),
4282
0
           errmsg("time zone \"%s\" not recognized",
4283
0
              extra->dtee_timezone),
4284
0
           errdetail("This time zone name appears in the configuration file for time zone abbreviation \"%s\".",
4285
0
                 extra->dtee_abbrev)));
4286
0
      break;
4287
0
    case DTERR_BAD_FORMAT:
4288
0
    default:
4289
0
      errsave(escontext,
4290
0
          (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
4291
0
           errmsg("invalid input syntax for type %s: \"%s\"",
4292
0
              datatype, str)));
4293
0
      break;
4294
0
  }
4295
0
}
4296
4297
/*
4298
 * datebsearch()
4299
 * Binary search -- from Knuth (6.2.1) Algorithm B.  Special case like this
4300
 * is WAY faster than the generic bsearch().
4301
 */
4302
static const datetkn *
4303
datebsearch(const char *key, const datetkn *base, int nel)
4304
0
{
4305
0
  if (nel > 0)
4306
0
  {
4307
0
    const datetkn *last = base + nel - 1,
4308
0
           *position;
4309
0
    int     result;
4310
4311
0
    while (last >= base)
4312
0
    {
4313
0
      position = base + ((last - base) >> 1);
4314
      /* precheck the first character for a bit of extra speed */
4315
0
      result = (int) key[0] - (int) position->token[0];
4316
0
      if (result == 0)
4317
0
      {
4318
        /* use strncmp so that we match truncated tokens */
4319
0
        result = strncmp(key, position->token, TOKMAXLEN);
4320
0
        if (result == 0)
4321
0
          return position;
4322
0
      }
4323
0
      if (result < 0)
4324
0
        last = position - 1;
4325
0
      else
4326
0
        base = position + 1;
4327
0
    }
4328
0
  }
4329
0
  return NULL;
4330
0
}
4331
4332
/*
4333
 * EncodeTimezone()
4334
 *    Copies representation of a numeric timezone offset to str.
4335
 *
4336
 * Returns a pointer to the new end of string.  No NUL terminator is put
4337
 * there; callers are responsible for NUL terminating str themselves.
4338
 */
4339
static char *
4340
EncodeTimezone(char *str, int tz, int style)
4341
0
{
4342
0
  int     hour,
4343
0
        min,
4344
0
        sec;
4345
4346
0
  sec = abs(tz);
4347
0
  min = sec / SECS_PER_MINUTE;
4348
0
  sec -= min * SECS_PER_MINUTE;
4349
0
  hour = min / MINS_PER_HOUR;
4350
0
  min -= hour * MINS_PER_HOUR;
4351
4352
  /* TZ is negated compared to sign we wish to display ... */
4353
0
  *str++ = (tz <= 0 ? '+' : '-');
4354
4355
0
  if (sec != 0)
4356
0
  {
4357
0
    str = pg_ultostr_zeropad(str, hour, 2);
4358
0
    *str++ = ':';
4359
0
    str = pg_ultostr_zeropad(str, min, 2);
4360
0
    *str++ = ':';
4361
0
    str = pg_ultostr_zeropad(str, sec, 2);
4362
0
  }
4363
0
  else if (min != 0 || style == USE_XSD_DATES)
4364
0
  {
4365
0
    str = pg_ultostr_zeropad(str, hour, 2);
4366
0
    *str++ = ':';
4367
0
    str = pg_ultostr_zeropad(str, min, 2);
4368
0
  }
4369
0
  else
4370
0
    str = pg_ultostr_zeropad(str, hour, 2);
4371
0
  return str;
4372
0
}
4373
4374
/*
4375
 * EncodeDateOnly()
4376
 * Encode date as local time.
4377
 */
4378
void
4379
EncodeDateOnly(struct pg_tm *tm, int style, char *str)
4380
0
{
4381
0
  Assert(tm->tm_mon >= 1 && tm->tm_mon <= MONTHS_PER_YEAR);
4382
4383
0
  switch (style)
4384
0
  {
4385
0
    case USE_ISO_DATES:
4386
0
    case USE_XSD_DATES:
4387
      /* compatible with ISO date formats */
4388
0
      str = pg_ultostr_zeropad(str,
4389
0
                   (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
4390
0
      *str++ = '-';
4391
0
      str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
4392
0
      *str++ = '-';
4393
0
      str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
4394
0
      break;
4395
4396
0
    case USE_SQL_DATES:
4397
      /* compatible with Oracle/Ingres date formats */
4398
0
      if (DateOrder == DATEORDER_DMY)
4399
0
      {
4400
0
        str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
4401
0
        *str++ = '/';
4402
0
        str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
4403
0
      }
4404
0
      else
4405
0
      {
4406
0
        str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
4407
0
        *str++ = '/';
4408
0
        str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
4409
0
      }
4410
0
      *str++ = '/';
4411
0
      str = pg_ultostr_zeropad(str,
4412
0
                   (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
4413
0
      break;
4414
4415
0
    case USE_GERMAN_DATES:
4416
      /* German-style date format */
4417
0
      str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
4418
0
      *str++ = '.';
4419
0
      str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
4420
0
      *str++ = '.';
4421
0
      str = pg_ultostr_zeropad(str,
4422
0
                   (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
4423
0
      break;
4424
4425
0
    case USE_POSTGRES_DATES:
4426
0
    default:
4427
      /* traditional date-only style for Postgres */
4428
0
      if (DateOrder == DATEORDER_DMY)
4429
0
      {
4430
0
        str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
4431
0
        *str++ = '-';
4432
0
        str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
4433
0
      }
4434
0
      else
4435
0
      {
4436
0
        str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
4437
0
        *str++ = '-';
4438
0
        str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
4439
0
      }
4440
0
      *str++ = '-';
4441
0
      str = pg_ultostr_zeropad(str,
4442
0
                   (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
4443
0
      break;
4444
0
  }
4445
4446
0
  if (tm->tm_year <= 0)
4447
0
  {
4448
0
    memcpy(str, " BC", 3);  /* Don't copy NUL */
4449
0
    str += 3;
4450
0
  }
4451
0
  *str = '\0';
4452
0
}
4453
4454
4455
/*
4456
 * EncodeTimeOnly()
4457
 * Encode time fields only.
4458
 *
4459
 * tm and fsec are the value to encode, print_tz determines whether to include
4460
 * a time zone (the difference between time and timetz types), tz is the
4461
 * numeric time zone offset, style is the date style, str is where to write the
4462
 * output.
4463
 */
4464
void
4465
EncodeTimeOnly(struct pg_tm *tm, fsec_t fsec, bool print_tz, int tz, int style, char *str)
4466
0
{
4467
0
  str = pg_ultostr_zeropad(str, tm->tm_hour, 2);
4468
0
  *str++ = ':';
4469
0
  str = pg_ultostr_zeropad(str, tm->tm_min, 2);
4470
0
  *str++ = ':';
4471
0
  str = AppendSeconds(str, tm->tm_sec, fsec, MAX_TIME_PRECISION, true);
4472
0
  if (print_tz)
4473
0
    str = EncodeTimezone(str, tz, style);
4474
0
  *str = '\0';
4475
0
}
4476
4477
4478
/*
4479
 * EncodeDateTime()
4480
 * Encode date and time interpreted as local time.
4481
 *
4482
 * tm and fsec are the value to encode, print_tz determines whether to include
4483
 * a time zone (the difference between timestamp and timestamptz types), tz is
4484
 * the numeric time zone offset, tzn is the textual time zone, which if
4485
 * specified will be used instead of tz by some styles, style is the date
4486
 * style, str is where to write the output.
4487
 *
4488
 * Supported date styles:
4489
 *  Postgres - day mon hh:mm:ss yyyy tz
4490
 *  SQL - mm/dd/yyyy hh:mm:ss.ss tz
4491
 *  ISO - yyyy-mm-dd hh:mm:ss+/-tz
4492
 *  German - dd.mm.yyyy hh:mm:ss tz
4493
 *  XSD - yyyy-mm-ddThh:mm:ss.ss+/-tz
4494
 */
4495
void
4496
EncodeDateTime(struct pg_tm *tm, fsec_t fsec, bool print_tz, int tz, const char *tzn, int style, char *str)
4497
0
{
4498
0
  int     day;
4499
4500
0
  Assert(tm->tm_mon >= 1 && tm->tm_mon <= MONTHS_PER_YEAR);
4501
4502
  /*
4503
   * Negative tm_isdst means we have no valid time zone translation.
4504
   */
4505
0
  if (tm->tm_isdst < 0)
4506
0
    print_tz = false;
4507
4508
0
  switch (style)
4509
0
  {
4510
0
    case USE_ISO_DATES:
4511
0
    case USE_XSD_DATES:
4512
      /* Compatible with ISO-8601 date formats */
4513
0
      str = pg_ultostr_zeropad(str,
4514
0
                   (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
4515
0
      *str++ = '-';
4516
0
      str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
4517
0
      *str++ = '-';
4518
0
      str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
4519
0
      *str++ = (style == USE_ISO_DATES) ? ' ' : 'T';
4520
0
      str = pg_ultostr_zeropad(str, tm->tm_hour, 2);
4521
0
      *str++ = ':';
4522
0
      str = pg_ultostr_zeropad(str, tm->tm_min, 2);
4523
0
      *str++ = ':';
4524
0
      str = AppendTimestampSeconds(str, tm, fsec);
4525
0
      if (print_tz)
4526
0
        str = EncodeTimezone(str, tz, style);
4527
0
      break;
4528
4529
0
    case USE_SQL_DATES:
4530
      /* Compatible with Oracle/Ingres date formats */
4531
0
      if (DateOrder == DATEORDER_DMY)
4532
0
      {
4533
0
        str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
4534
0
        *str++ = '/';
4535
0
        str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
4536
0
      }
4537
0
      else
4538
0
      {
4539
0
        str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
4540
0
        *str++ = '/';
4541
0
        str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
4542
0
      }
4543
0
      *str++ = '/';
4544
0
      str = pg_ultostr_zeropad(str,
4545
0
                   (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
4546
0
      *str++ = ' ';
4547
0
      str = pg_ultostr_zeropad(str, tm->tm_hour, 2);
4548
0
      *str++ = ':';
4549
0
      str = pg_ultostr_zeropad(str, tm->tm_min, 2);
4550
0
      *str++ = ':';
4551
0
      str = AppendTimestampSeconds(str, tm, fsec);
4552
4553
      /*
4554
       * Note: the uses of %.*s in this function would be risky if the
4555
       * timezone names ever contain non-ASCII characters, since we are
4556
       * not being careful to do encoding-aware clipping.  However, all
4557
       * TZ abbreviations in the IANA database are plain ASCII.
4558
       */
4559
0
      if (print_tz)
4560
0
      {
4561
0
        if (tzn)
4562
0
        {
4563
0
          sprintf(str, " %.*s", MAXTZLEN, tzn);
4564
0
          str += strlen(str);
4565
0
        }
4566
0
        else
4567
0
          str = EncodeTimezone(str, tz, style);
4568
0
      }
4569
0
      break;
4570
4571
0
    case USE_GERMAN_DATES:
4572
      /* German variant on European style */
4573
0
      str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
4574
0
      *str++ = '.';
4575
0
      str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
4576
0
      *str++ = '.';
4577
0
      str = pg_ultostr_zeropad(str,
4578
0
                   (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
4579
0
      *str++ = ' ';
4580
0
      str = pg_ultostr_zeropad(str, tm->tm_hour, 2);
4581
0
      *str++ = ':';
4582
0
      str = pg_ultostr_zeropad(str, tm->tm_min, 2);
4583
0
      *str++ = ':';
4584
0
      str = AppendTimestampSeconds(str, tm, fsec);
4585
4586
0
      if (print_tz)
4587
0
      {
4588
0
        if (tzn)
4589
0
        {
4590
0
          sprintf(str, " %.*s", MAXTZLEN, tzn);
4591
0
          str += strlen(str);
4592
0
        }
4593
0
        else
4594
0
          str = EncodeTimezone(str, tz, style);
4595
0
      }
4596
0
      break;
4597
4598
0
    case USE_POSTGRES_DATES:
4599
0
    default:
4600
      /* Backward-compatible with traditional Postgres abstime dates */
4601
0
      day = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
4602
0
      tm->tm_wday = j2day(day);
4603
0
      memcpy(str, days[tm->tm_wday], 3);
4604
0
      str += 3;
4605
0
      *str++ = ' ';
4606
0
      if (DateOrder == DATEORDER_DMY)
4607
0
      {
4608
0
        str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
4609
0
        *str++ = ' ';
4610
0
        memcpy(str, months[tm->tm_mon - 1], 3);
4611
0
        str += 3;
4612
0
      }
4613
0
      else
4614
0
      {
4615
0
        memcpy(str, months[tm->tm_mon - 1], 3);
4616
0
        str += 3;
4617
0
        *str++ = ' ';
4618
0
        str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
4619
0
      }
4620
0
      *str++ = ' ';
4621
0
      str = pg_ultostr_zeropad(str, tm->tm_hour, 2);
4622
0
      *str++ = ':';
4623
0
      str = pg_ultostr_zeropad(str, tm->tm_min, 2);
4624
0
      *str++ = ':';
4625
0
      str = AppendTimestampSeconds(str, tm, fsec);
4626
0
      *str++ = ' ';
4627
0
      str = pg_ultostr_zeropad(str,
4628
0
                   (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
4629
4630
0
      if (print_tz)
4631
0
      {
4632
0
        if (tzn)
4633
0
        {
4634
0
          sprintf(str, " %.*s", MAXTZLEN, tzn);
4635
0
          str += strlen(str);
4636
0
        }
4637
0
        else
4638
0
        {
4639
          /*
4640
           * We have a time zone, but no string version. Use the
4641
           * numeric form, but be sure to include a leading space to
4642
           * avoid formatting something which would be rejected by
4643
           * the date/time parser later. - thomas 2001-10-19
4644
           */
4645
0
          *str++ = ' ';
4646
0
          str = EncodeTimezone(str, tz, style);
4647
0
        }
4648
0
      }
4649
0
      break;
4650
0
  }
4651
4652
0
  if (tm->tm_year <= 0)
4653
0
  {
4654
0
    memcpy(str, " BC", 3);  /* Don't copy NUL */
4655
0
    str += 3;
4656
0
  }
4657
0
  *str = '\0';
4658
0
}
4659
4660
4661
/*
4662
 * Helper functions to avoid duplicated code in EncodeInterval.
4663
 */
4664
4665
/* Append an ISO-8601-style interval field, but only if value isn't zero */
4666
static char *
4667
AddISO8601IntPart(char *cp, int64 value, char units)
4668
0
{
4669
0
  if (value == 0)
4670
0
    return cp;
4671
0
  sprintf(cp, "%" PRId64 "%c", value, units);
4672
0
  return cp + strlen(cp);
4673
0
}
4674
4675
/* Append a postgres-style interval field, but only if value isn't zero */
4676
static char *
4677
AddPostgresIntPart(char *cp, int64 value, const char *units,
4678
           bool *is_zero, bool *is_before)
4679
0
{
4680
0
  if (value == 0)
4681
0
    return cp;
4682
0
  sprintf(cp, "%s%s%" PRId64 " %s%s",
4683
0
      (!*is_zero) ? " " : "",
4684
0
      (*is_before && value > 0) ? "+" : "",
4685
0
      value,
4686
0
      units,
4687
0
      (value != 1) ? "s" : "");
4688
4689
  /*
4690
   * Each nonzero field sets is_before for (only) the next one.  This is a
4691
   * tad bizarre but it's how it worked before...
4692
   */
4693
0
  *is_before = (value < 0);
4694
0
  *is_zero = false;
4695
0
  return cp + strlen(cp);
4696
0
}
4697
4698
/* Append a verbose-style interval field, but only if value isn't zero */
4699
static char *
4700
AddVerboseIntPart(char *cp, int64 value, const char *units,
4701
          bool *is_zero, bool *is_before)
4702
0
{
4703
0
  if (value == 0)
4704
0
    return cp;
4705
  /* first nonzero value sets is_before */
4706
0
  if (*is_zero)
4707
0
  {
4708
0
    *is_before = (value < 0);
4709
0
    value = i64abs(value);
4710
0
  }
4711
0
  else if (*is_before)
4712
0
    value = -value;
4713
0
  sprintf(cp, " %" PRId64 " %s%s", value, units, (value == 1) ? "" : "s");
4714
0
  *is_zero = false;
4715
0
  return cp + strlen(cp);
4716
0
}
4717
4718
4719
/*
4720
 * EncodeInterval()
4721
 * Interpret time structure as a delta time and convert to string.
4722
 *
4723
 * Support "traditional Postgres" and ISO-8601 styles.
4724
 * Actually, afaik ISO does not address time interval formatting,
4725
 *  but this looks similar to the spec for absolute date/time.
4726
 * - thomas 1998-04-30
4727
 *
4728
 * Actually, afaik, ISO 8601 does specify formats for "time
4729
 * intervals...[of the]...format with time-unit designators", which
4730
 * are pretty ugly.  The format looks something like
4731
 *     P1Y1M1DT1H1M1.12345S
4732
 * but useful for exchanging data with computers instead of humans.
4733
 * - ron 2003-07-14
4734
 *
4735
 * And ISO's SQL 2008 standard specifies standards for
4736
 * "year-month literal"s (that look like '2-3') and
4737
 * "day-time literal"s (that look like ('4 5:6:7')
4738
 */
4739
void
4740
EncodeInterval(struct pg_itm *itm, int style, char *str)
4741
0
{
4742
0
  char     *cp = str;
4743
0
  int     year = itm->tm_year;
4744
0
  int     mon = itm->tm_mon;
4745
0
  int64   mday = itm->tm_mday;  /* tm_mday could be INT_MIN */
4746
0
  int64   hour = itm->tm_hour;
4747
0
  int     min = itm->tm_min;
4748
0
  int     sec = itm->tm_sec;
4749
0
  int     fsec = itm->tm_usec;
4750
0
  bool    is_before = false;
4751
0
  bool    is_zero = true;
4752
4753
  /*
4754
   * The sign of year and month are guaranteed to match, since they are
4755
   * stored internally as "month". But we'll need to check for is_before and
4756
   * is_zero when determining the signs of day and hour/minute/seconds
4757
   * fields.
4758
   */
4759
0
  switch (style)
4760
0
  {
4761
      /* SQL Standard interval format */
4762
0
    case INTSTYLE_SQL_STANDARD:
4763
0
      {
4764
0
        bool    has_negative = year < 0 || mon < 0 ||
4765
0
          mday < 0 || hour < 0 ||
4766
0
          min < 0 || sec < 0 || fsec < 0;
4767
0
        bool    has_positive = year > 0 || mon > 0 ||
4768
0
          mday > 0 || hour > 0 ||
4769
0
          min > 0 || sec > 0 || fsec > 0;
4770
0
        bool    has_year_month = year != 0 || mon != 0;
4771
0
        bool    has_day_time = mday != 0 || hour != 0 ||
4772
0
          min != 0 || sec != 0 || fsec != 0;
4773
0
        bool    has_day = mday != 0;
4774
0
        bool    sql_standard_value = !(has_negative && has_positive) &&
4775
0
          !(has_year_month && has_day_time);
4776
4777
        /*
4778
         * SQL Standard wants only 1 "<sign>" preceding the whole
4779
         * interval ... but can't do that if mixed signs.
4780
         */
4781
0
        if (has_negative && sql_standard_value)
4782
0
        {
4783
0
          *cp++ = '-';
4784
0
          year = -year;
4785
0
          mon = -mon;
4786
0
          mday = -mday;
4787
0
          hour = -hour;
4788
0
          min = -min;
4789
0
          sec = -sec;
4790
0
          fsec = -fsec;
4791
0
        }
4792
4793
0
        if (!has_negative && !has_positive)
4794
0
        {
4795
0
          sprintf(cp, "0");
4796
0
        }
4797
0
        else if (!sql_standard_value)
4798
0
        {
4799
          /*
4800
           * For non sql-standard interval values, force outputting
4801
           * the signs to avoid ambiguities with intervals with
4802
           * mixed sign components.
4803
           */
4804
0
          char    year_sign = (year < 0 || mon < 0) ? '-' : '+';
4805
0
          char    day_sign = (mday < 0) ? '-' : '+';
4806
0
          char    sec_sign = (hour < 0 || min < 0 ||
4807
0
                      sec < 0 || fsec < 0) ? '-' : '+';
4808
4809
0
          sprintf(cp, "%c%d-%d %c%" PRId64 " %c%" PRId64 ":%02d:",
4810
0
              year_sign, abs(year), abs(mon),
4811
0
              day_sign, i64abs(mday),
4812
0
              sec_sign, i64abs(hour), abs(min));
4813
0
          cp += strlen(cp);
4814
0
          cp = AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
4815
0
          *cp = '\0';
4816
0
        }
4817
0
        else if (has_year_month)
4818
0
        {
4819
0
          sprintf(cp, "%d-%d", year, mon);
4820
0
        }
4821
0
        else if (has_day)
4822
0
        {
4823
0
          sprintf(cp, "%" PRId64 " %" PRId64 ":%02d:",
4824
0
              mday, hour, min);
4825
0
          cp += strlen(cp);
4826
0
          cp = AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
4827
0
          *cp = '\0';
4828
0
        }
4829
0
        else
4830
0
        {
4831
0
          sprintf(cp, "%" PRId64 ":%02d:", hour, min);
4832
0
          cp += strlen(cp);
4833
0
          cp = AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
4834
0
          *cp = '\0';
4835
0
        }
4836
0
      }
4837
0
      break;
4838
4839
      /* ISO 8601 "time-intervals by duration only" */
4840
0
    case INTSTYLE_ISO_8601:
4841
      /* special-case zero to avoid printing nothing */
4842
0
      if (year == 0 && mon == 0 && mday == 0 &&
4843
0
        hour == 0 && min == 0 && sec == 0 && fsec == 0)
4844
0
      {
4845
0
        sprintf(cp, "PT0S");
4846
0
        break;
4847
0
      }
4848
0
      *cp++ = 'P';
4849
0
      cp = AddISO8601IntPart(cp, year, 'Y');
4850
0
      cp = AddISO8601IntPart(cp, mon, 'M');
4851
0
      cp = AddISO8601IntPart(cp, mday, 'D');
4852
0
      if (hour != 0 || min != 0 || sec != 0 || fsec != 0)
4853
0
        *cp++ = 'T';
4854
0
      cp = AddISO8601IntPart(cp, hour, 'H');
4855
0
      cp = AddISO8601IntPart(cp, min, 'M');
4856
0
      if (sec != 0 || fsec != 0)
4857
0
      {
4858
0
        if (sec < 0 || fsec < 0)
4859
0
          *cp++ = '-';
4860
0
        cp = AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, false);
4861
0
        *cp++ = 'S';
4862
0
        *cp++ = '\0';
4863
0
      }
4864
0
      break;
4865
4866
      /* Compatible with postgresql < 8.4 when DateStyle = 'iso' */
4867
0
    case INTSTYLE_POSTGRES:
4868
0
      cp = AddPostgresIntPart(cp, year, "year", &is_zero, &is_before);
4869
4870
      /*
4871
       * Ideally we should spell out "month" like we do for "year" and
4872
       * "day".  However, for backward compatibility, we can't easily
4873
       * fix this.  bjm 2011-05-24
4874
       */
4875
0
      cp = AddPostgresIntPart(cp, mon, "mon", &is_zero, &is_before);
4876
0
      cp = AddPostgresIntPart(cp, mday, "day", &is_zero, &is_before);
4877
0
      if (is_zero || hour != 0 || min != 0 || sec != 0 || fsec != 0)
4878
0
      {
4879
0
        bool    minus = (hour < 0 || min < 0 || sec < 0 || fsec < 0);
4880
4881
0
        sprintf(cp, "%s%s%02" PRId64 ":%02d:",
4882
0
            is_zero ? "" : " ",
4883
0
            (minus ? "-" : (is_before ? "+" : "")),
4884
0
            i64abs(hour), abs(min));
4885
0
        cp += strlen(cp);
4886
0
        cp = AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
4887
0
        *cp = '\0';
4888
0
      }
4889
0
      break;
4890
4891
      /* Compatible with postgresql < 8.4 when DateStyle != 'iso' */
4892
0
    case INTSTYLE_POSTGRES_VERBOSE:
4893
0
    default:
4894
0
      strcpy(cp, "@");
4895
0
      cp++;
4896
0
      cp = AddVerboseIntPart(cp, year, "year", &is_zero, &is_before);
4897
0
      cp = AddVerboseIntPart(cp, mon, "mon", &is_zero, &is_before);
4898
0
      cp = AddVerboseIntPart(cp, mday, "day", &is_zero, &is_before);
4899
0
      cp = AddVerboseIntPart(cp, hour, "hour", &is_zero, &is_before);
4900
0
      cp = AddVerboseIntPart(cp, min, "min", &is_zero, &is_before);
4901
0
      if (sec != 0 || fsec != 0)
4902
0
      {
4903
0
        *cp++ = ' ';
4904
0
        if (sec < 0 || (sec == 0 && fsec < 0))
4905
0
        {
4906
0
          if (is_zero)
4907
0
            is_before = true;
4908
0
          else if (!is_before)
4909
0
            *cp++ = '-';
4910
0
        }
4911
0
        else if (is_before)
4912
0
          *cp++ = '-';
4913
0
        cp = AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, false);
4914
        /* We output "ago", not negatives, so use abs(). */
4915
0
        sprintf(cp, " sec%s",
4916
0
            (abs(sec) != 1 || fsec != 0) ? "s" : "");
4917
0
        is_zero = false;
4918
0
      }
4919
      /* identically zero? then put in a unitless zero... */
4920
0
      if (is_zero)
4921
0
        strcat(cp, " 0");
4922
0
      if (is_before)
4923
0
        strcat(cp, " ago");
4924
0
      break;
4925
0
  }
4926
0
}
4927
4928
4929
/*
4930
 * We've been burnt by stupid errors in the ordering of the datetkn tables
4931
 * once too often.  Arrange to check them during postmaster start.
4932
 */
4933
static bool
4934
CheckDateTokenTable(const char *tablename, const datetkn *base, int nel)
4935
{
4936
  bool    ok = true;
4937
  int     i;
4938
4939
  for (i = 0; i < nel; i++)
4940
  {
4941
    /* check for token strings that don't fit */
4942
    if (strlen(base[i].token) > TOKMAXLEN)
4943
    {
4944
      /* %.*s is safe since all our tokens are ASCII */
4945
      elog(LOG, "token too long in %s table: \"%.*s\"",
4946
         tablename,
4947
         TOKMAXLEN + 1, base[i].token);
4948
      ok = false;
4949
      break;        /* don't risk applying strcmp */
4950
    }
4951
    /* check for out of order */
4952
    if (i > 0 &&
4953
      strcmp(base[i - 1].token, base[i].token) >= 0)
4954
    {
4955
      elog(LOG, "ordering error in %s table: \"%s\" >= \"%s\"",
4956
         tablename,
4957
         base[i - 1].token,
4958
         base[i].token);
4959
      ok = false;
4960
    }
4961
  }
4962
  return ok;
4963
}
4964
4965
bool
4966
CheckDateTokenTables(void)
4967
0
{
4968
0
  bool    ok = true;
4969
4970
0
  Assert(UNIX_EPOCH_JDATE == date2j(1970, 1, 1));
4971
0
  Assert(POSTGRES_EPOCH_JDATE == date2j(2000, 1, 1));
4972
4973
0
  ok &= CheckDateTokenTable("datetktbl", datetktbl, szdatetktbl);
4974
0
  ok &= CheckDateTokenTable("deltatktbl", deltatktbl, szdeltatktbl);
4975
0
  return ok;
4976
0
}
4977
4978
/*
4979
 * Common code for temporal prosupport functions: simplify, if possible,
4980
 * a call to a temporal type's length-coercion function.
4981
 *
4982
 * Types time, timetz, timestamp and timestamptz each have a range of allowed
4983
 * precisions.  An unspecified precision is rigorously equivalent to the
4984
 * highest specifiable precision.  We can replace the function call with a
4985
 * no-op RelabelType if it is coercing to the same or higher precision as the
4986
 * input is known to have.
4987
 *
4988
 * The input Node is always a FuncExpr, but to reduce the #include footprint
4989
 * of datetime.h, we declare it as Node *.
4990
 *
4991
 * Note: timestamp_scale throws an error when the typmod is out of range, but
4992
 * we can't get there from a cast: our typmodin will have caught it already.
4993
 */
4994
Node *
4995
TemporalSimplify(int32 max_precis, Node *node)
4996
0
{
4997
0
  FuncExpr   *expr = castNode(FuncExpr, node);
4998
0
  Node     *ret = NULL;
4999
0
  Node     *typmod;
5000
5001
0
  Assert(list_length(expr->args) >= 2);
5002
5003
0
  typmod = (Node *) lsecond(expr->args);
5004
5005
0
  if (IsA(typmod, Const) && !((Const *) typmod)->constisnull)
5006
0
  {
5007
0
    Node     *source = (Node *) linitial(expr->args);
5008
0
    int32   old_precis = exprTypmod(source);
5009
0
    int32   new_precis = DatumGetInt32(((Const *) typmod)->constvalue);
5010
5011
0
    if (new_precis < 0 || new_precis == max_precis ||
5012
0
      (old_precis >= 0 && new_precis >= old_precis))
5013
0
      ret = relabel_to_typmod(source, new_precis);
5014
0
  }
5015
5016
0
  return ret;
5017
0
}
5018
5019
/*
5020
 * This function gets called during timezone config file load or reload
5021
 * to create the final array of timezone tokens.  The argument array
5022
 * is already sorted in name order.
5023
 *
5024
 * The result is a TimeZoneAbbrevTable (which must be a single guc_malloc'd
5025
 * chunk) or NULL on alloc failure.  No other error conditions are defined.
5026
 */
5027
TimeZoneAbbrevTable *
5028
ConvertTimeZoneAbbrevs(struct tzEntry *abbrevs, int n)
5029
0
{
5030
0
  TimeZoneAbbrevTable *tbl;
5031
0
  Size    tbl_size;
5032
0
  int     i;
5033
5034
  /* Space for fixed fields and datetkn array */
5035
0
  tbl_size = offsetof(TimeZoneAbbrevTable, abbrevs) +
5036
0
    n * sizeof(datetkn);
5037
0
  tbl_size = MAXALIGN(tbl_size);
5038
  /* Count up space for dynamic abbreviations */
5039
0
  for (i = 0; i < n; i++)
5040
0
  {
5041
0
    struct tzEntry *abbr = abbrevs + i;
5042
5043
0
    if (abbr->zone != NULL)
5044
0
    {
5045
0
      Size    dsize;
5046
5047
0
      dsize = offsetof(DynamicZoneAbbrev, zone) +
5048
0
        strlen(abbr->zone) + 1;
5049
0
      tbl_size += MAXALIGN(dsize);
5050
0
    }
5051
0
  }
5052
5053
  /* Alloc the result ... */
5054
0
  tbl = guc_malloc(LOG, tbl_size);
5055
0
  if (!tbl)
5056
0
    return NULL;
5057
5058
  /* ... and fill it in */
5059
0
  tbl->tblsize = tbl_size;
5060
0
  tbl->numabbrevs = n;
5061
  /* in this loop, tbl_size reprises the space calculation above */
5062
0
  tbl_size = offsetof(TimeZoneAbbrevTable, abbrevs) +
5063
0
    n * sizeof(datetkn);
5064
0
  tbl_size = MAXALIGN(tbl_size);
5065
0
  for (i = 0; i < n; i++)
5066
0
  {
5067
0
    struct tzEntry *abbr = abbrevs + i;
5068
0
    datetkn    *dtoken = tbl->abbrevs + i;
5069
5070
    /* use strlcpy to truncate name if necessary */
5071
0
    strlcpy(dtoken->token, abbr->abbrev, TOKMAXLEN + 1);
5072
0
    if (abbr->zone != NULL)
5073
0
    {
5074
      /* Allocate a DynamicZoneAbbrev for this abbreviation */
5075
0
      DynamicZoneAbbrev *dtza;
5076
0
      Size    dsize;
5077
5078
0
      dtza = (DynamicZoneAbbrev *) ((char *) tbl + tbl_size);
5079
0
      dtza->tz = NULL;
5080
0
      strcpy(dtza->zone, abbr->zone);
5081
5082
0
      dtoken->type = DYNTZ;
5083
      /* value is offset from table start to DynamicZoneAbbrev */
5084
0
      dtoken->value = (int32) tbl_size;
5085
5086
0
      dsize = offsetof(DynamicZoneAbbrev, zone) +
5087
0
        strlen(abbr->zone) + 1;
5088
0
      tbl_size += MAXALIGN(dsize);
5089
0
    }
5090
0
    else
5091
0
    {
5092
0
      dtoken->type = abbr->is_dst ? DTZ : TZ;
5093
0
      dtoken->value = abbr->offset;
5094
0
    }
5095
0
  }
5096
5097
  /* Assert the two loops above agreed on size calculations */
5098
0
  Assert(tbl->tblsize == tbl_size);
5099
5100
  /* Check the ordering, if testing */
5101
0
  Assert(CheckDateTokenTable("timezone abbreviations", tbl->abbrevs, n));
5102
5103
0
  return tbl;
5104
0
}
5105
5106
/*
5107
 * Install a TimeZoneAbbrevTable as the active table.
5108
 *
5109
 * Caller is responsible that the passed table doesn't go away while in use.
5110
 */
5111
void
5112
InstallTimeZoneAbbrevs(TimeZoneAbbrevTable *tbl)
5113
0
{
5114
0
  zoneabbrevtbl = tbl;
5115
  /* reset tzabbrevcache, which may contain results from old table */
5116
0
  memset(tzabbrevcache, 0, sizeof(tzabbrevcache));
5117
0
}
5118
5119
/*
5120
 * Helper subroutine to locate pg_tz timezone for a dynamic abbreviation.
5121
 *
5122
 * On failure, returns NULL and fills *extra for a DTERR_BAD_ZONE_ABBREV error.
5123
 */
5124
static pg_tz *
5125
FetchDynamicTimeZone(TimeZoneAbbrevTable *tbl, const datetkn *tp,
5126
           DateTimeErrorExtra *extra)
5127
0
{
5128
0
  DynamicZoneAbbrev *dtza;
5129
5130
  /* Just some sanity checks to prevent indexing off into nowhere */
5131
0
  Assert(tp->type == DYNTZ);
5132
0
  Assert(tp->value > 0 && tp->value < tbl->tblsize);
5133
5134
0
  dtza = (DynamicZoneAbbrev *) ((char *) tbl + tp->value);
5135
5136
  /* Look up the underlying zone if we haven't already */
5137
0
  if (dtza->tz == NULL)
5138
0
  {
5139
0
    dtza->tz = pg_tzset(dtza->zone);
5140
0
    if (dtza->tz == NULL)
5141
0
    {
5142
      /* Ooops, bogus zone name in config file entry */
5143
0
      extra->dtee_timezone = dtza->zone;
5144
0
      extra->dtee_abbrev = tp->token;
5145
0
    }
5146
0
  }
5147
0
  return dtza->tz;
5148
0
}
5149
5150
5151
/*
5152
 * This set-returning function reads all the time zone abbreviations
5153
 * defined by the IANA data for the current timezone setting,
5154
 * and returns a set of (abbrev, utc_offset, is_dst).
5155
 */
5156
Datum
5157
pg_timezone_abbrevs_zone(PG_FUNCTION_ARGS)
5158
0
{
5159
0
  FuncCallContext *funcctx;
5160
0
  int      *pindex;
5161
0
  Datum   result;
5162
0
  HeapTuple tuple;
5163
0
  Datum   values[3];
5164
0
  bool    nulls[3] = {0};
5165
0
  TimestampTz now = GetCurrentTransactionStartTimestamp();
5166
0
  pg_time_t t = timestamptz_to_time_t(now);
5167
0
  const char *abbrev;
5168
0
  long int  gmtoff;
5169
0
  int     isdst;
5170
0
  struct pg_itm_in itm_in;
5171
0
  Interval   *resInterval;
5172
5173
  /* stuff done only on the first call of the function */
5174
0
  if (SRF_IS_FIRSTCALL())
5175
0
  {
5176
0
    TupleDesc tupdesc;
5177
0
    MemoryContext oldcontext;
5178
5179
    /* create a function context for cross-call persistence */
5180
0
    funcctx = SRF_FIRSTCALL_INIT();
5181
5182
    /*
5183
     * switch to memory context appropriate for multiple function calls
5184
     */
5185
0
    oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
5186
5187
    /* allocate memory for user context */
5188
0
    pindex = palloc_object(int);
5189
0
    *pindex = 0;
5190
0
    funcctx->user_fctx = pindex;
5191
5192
0
    if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
5193
0
      elog(ERROR, "return type must be a row type");
5194
0
    funcctx->tuple_desc = tupdesc;
5195
5196
0
    MemoryContextSwitchTo(oldcontext);
5197
0
  }
5198
5199
  /* stuff done on every call of the function */
5200
0
  funcctx = SRF_PERCALL_SETUP();
5201
0
  pindex = (int *) funcctx->user_fctx;
5202
5203
0
  while ((abbrev = pg_get_next_timezone_abbrev(pindex,
5204
0
                         session_timezone)) != NULL)
5205
0
  {
5206
    /* Ignore abbreviations that aren't all-alphabetic */
5207
0
    if (strspn(abbrev, "ABCDEFGHIJKLMNOPQRSTUVWXYZ") != strlen(abbrev))
5208
0
      continue;
5209
5210
    /* Determine the current meaning of the abbrev */
5211
0
    if (!pg_interpret_timezone_abbrev(abbrev,
5212
0
                      &t,
5213
0
                      &gmtoff,
5214
0
                      &isdst,
5215
0
                      session_timezone))
5216
0
      continue;     /* hm, not actually used in this zone? */
5217
5218
0
    values[0] = CStringGetTextDatum(abbrev);
5219
5220
    /* Convert offset (in seconds) to an interval; can't overflow */
5221
0
    MemSet(&itm_in, 0, sizeof(struct pg_itm_in));
5222
0
    itm_in.tm_usec = (int64) gmtoff * USECS_PER_SEC;
5223
0
    resInterval = palloc_object(Interval);
5224
0
    (void) itmin2interval(&itm_in, resInterval);
5225
0
    values[1] = IntervalPGetDatum(resInterval);
5226
5227
0
    values[2] = BoolGetDatum(isdst);
5228
5229
0
    tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
5230
0
    result = HeapTupleGetDatum(tuple);
5231
5232
0
    SRF_RETURN_NEXT(funcctx, result);
5233
0
  }
5234
5235
0
  SRF_RETURN_DONE(funcctx);
5236
0
}
5237
5238
/*
5239
 * This set-returning function reads all the time zone abbreviations
5240
 * defined by the timezone_abbreviations setting,
5241
 * and returns a set of (abbrev, utc_offset, is_dst).
5242
 */
5243
Datum
5244
pg_timezone_abbrevs_abbrevs(PG_FUNCTION_ARGS)
5245
0
{
5246
0
  FuncCallContext *funcctx;
5247
0
  int      *pindex;
5248
0
  Datum   result;
5249
0
  HeapTuple tuple;
5250
0
  Datum   values[3];
5251
0
  bool    nulls[3] = {0};
5252
0
  const datetkn *tp;
5253
0
  char    buffer[TOKMAXLEN + 1];
5254
0
  int     gmtoffset;
5255
0
  bool    is_dst;
5256
0
  unsigned char *p;
5257
0
  struct pg_itm_in itm_in;
5258
0
  Interval   *resInterval;
5259
5260
  /* stuff done only on the first call of the function */
5261
0
  if (SRF_IS_FIRSTCALL())
5262
0
  {
5263
0
    TupleDesc tupdesc;
5264
0
    MemoryContext oldcontext;
5265
5266
    /* create a function context for cross-call persistence */
5267
0
    funcctx = SRF_FIRSTCALL_INIT();
5268
5269
    /*
5270
     * switch to memory context appropriate for multiple function calls
5271
     */
5272
0
    oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
5273
5274
    /* allocate memory for user context */
5275
0
    pindex = palloc_object(int);
5276
0
    *pindex = 0;
5277
0
    funcctx->user_fctx = pindex;
5278
5279
0
    if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
5280
0
      elog(ERROR, "return type must be a row type");
5281
0
    funcctx->tuple_desc = tupdesc;
5282
5283
0
    MemoryContextSwitchTo(oldcontext);
5284
0
  }
5285
5286
  /* stuff done on every call of the function */
5287
0
  funcctx = SRF_PERCALL_SETUP();
5288
0
  pindex = (int *) funcctx->user_fctx;
5289
5290
0
  if (zoneabbrevtbl == NULL ||
5291
0
    *pindex >= zoneabbrevtbl->numabbrevs)
5292
0
    SRF_RETURN_DONE(funcctx);
5293
5294
0
  tp = zoneabbrevtbl->abbrevs + *pindex;
5295
5296
0
  switch (tp->type)
5297
0
  {
5298
0
    case TZ:
5299
0
      gmtoffset = tp->value;
5300
0
      is_dst = false;
5301
0
      break;
5302
0
    case DTZ:
5303
0
      gmtoffset = tp->value;
5304
0
      is_dst = true;
5305
0
      break;
5306
0
    case DYNTZ:
5307
0
      {
5308
        /* Determine the current meaning of the abbrev */
5309
0
        pg_tz    *tzp;
5310
0
        DateTimeErrorExtra extra;
5311
0
        TimestampTz now;
5312
0
        int     isdst;
5313
5314
0
        tzp = FetchDynamicTimeZone(zoneabbrevtbl, tp, &extra);
5315
0
        if (tzp == NULL)
5316
0
          DateTimeParseError(DTERR_BAD_ZONE_ABBREV, &extra,
5317
0
                     NULL, NULL, NULL);
5318
0
        now = GetCurrentTransactionStartTimestamp();
5319
0
        gmtoffset = -DetermineTimeZoneAbbrevOffsetTS(now,
5320
0
                               tp->token,
5321
0
                               tzp,
5322
0
                               &isdst);
5323
0
        is_dst = (bool) isdst;
5324
0
        break;
5325
0
      }
5326
0
    default:
5327
0
      elog(ERROR, "unrecognized timezone type %d", (int) tp->type);
5328
0
      gmtoffset = 0;    /* keep compiler quiet */
5329
0
      is_dst = false;
5330
0
      break;
5331
0
  }
5332
5333
  /*
5334
   * Convert name to text, using upcasing conversion that is the inverse of
5335
   * what ParseDateTime() uses.
5336
   */
5337
0
  strlcpy(buffer, tp->token, sizeof(buffer));
5338
0
  for (p = (unsigned char *) buffer; *p; p++)
5339
0
    *p = pg_toupper(*p);
5340
5341
0
  values[0] = CStringGetTextDatum(buffer);
5342
5343
  /* Convert offset (in seconds) to an interval; can't overflow */
5344
0
  MemSet(&itm_in, 0, sizeof(struct pg_itm_in));
5345
0
  itm_in.tm_usec = (int64) gmtoffset * USECS_PER_SEC;
5346
0
  resInterval = palloc_object(Interval);
5347
0
  (void) itmin2interval(&itm_in, resInterval);
5348
0
  values[1] = IntervalPGetDatum(resInterval);
5349
5350
0
  values[2] = BoolGetDatum(is_dst);
5351
5352
0
  (*pindex)++;
5353
5354
0
  tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
5355
0
  result = HeapTupleGetDatum(tuple);
5356
5357
0
  SRF_RETURN_NEXT(funcctx, result);
5358
0
}
5359
5360
/*
5361
 * This set-returning function reads all the available full time zones
5362
 * and returns a set of (name, abbrev, utc_offset, is_dst).
5363
 */
5364
Datum
5365
pg_timezone_names(PG_FUNCTION_ARGS)
5366
0
{
5367
0
  ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
5368
0
  pg_tzenum  *tzenum;
5369
0
  pg_tz    *tz;
5370
0
  Datum   values[4];
5371
0
  bool    nulls[4] = {0};
5372
0
  int     tzoff;
5373
0
  struct pg_tm tm;
5374
0
  fsec_t    fsec;
5375
0
  const char *tzn;
5376
0
  Interval   *resInterval;
5377
0
  struct pg_itm_in itm_in;
5378
5379
0
  InitMaterializedSRF(fcinfo, 0);
5380
5381
  /* initialize timezone scanning code */
5382
0
  tzenum = pg_tzenumerate_start();
5383
5384
  /* search for another zone to display */
5385
0
  for (;;)
5386
0
  {
5387
0
    tz = pg_tzenumerate_next(tzenum);
5388
0
    if (!tz)
5389
0
      break;
5390
5391
    /* Convert now() to local time in this zone */
5392
0
    if (timestamp2tm(GetCurrentTransactionStartTimestamp(),
5393
0
             &tzoff, &tm, &fsec, &tzn, tz) != 0)
5394
0
      continue;     /* ignore if conversion fails */
5395
5396
    /*
5397
     * IANA's rather silly "Factory" time zone used to emit ridiculously
5398
     * long "abbreviations" such as "Local time zone must be set--see zic
5399
     * manual page" or "Local time zone must be set--use tzsetup".  While
5400
     * modern versions of tzdb emit the much saner "-00", it seems some
5401
     * benighted packagers are hacking the IANA data so that it continues
5402
     * to produce these strings.  To prevent producing a weirdly wide
5403
     * abbrev column, reject ridiculously long abbreviations.
5404
     */
5405
0
    if (tzn && strlen(tzn) > 31)
5406
0
      continue;
5407
5408
0
    values[0] = CStringGetTextDatum(pg_get_timezone_name(tz));
5409
0
    values[1] = CStringGetTextDatum(tzn ? tzn : "");
5410
5411
    /* Convert tzoff to an interval; can't overflow */
5412
0
    MemSet(&itm_in, 0, sizeof(struct pg_itm_in));
5413
0
    itm_in.tm_usec = (int64) -tzoff * USECS_PER_SEC;
5414
0
    resInterval = palloc_object(Interval);
5415
0
    (void) itmin2interval(&itm_in, resInterval);
5416
0
    values[2] = IntervalPGetDatum(resInterval);
5417
5418
0
    values[3] = BoolGetDatum(tm.tm_isdst > 0);
5419
5420
0
    tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
5421
0
  }
5422
5423
0
  pg_tzenumerate_end(tzenum);
5424
0
  return (Datum) 0;
5425
0
}