1 #include "postgres_fe.h"
7 #error -ffast-math is known to break this code
12 #include "pgtypes_timestamp.h"
13 #include "pgtypes_date.h"
16 int PGTYPEStimestamp_defmt_scan(char **, char *, timestamp *, int *, int *, int *,
17 int *, int *, int *, int *);
19 #ifdef HAVE_INT64_TIMESTAMP
21 time2t(const int hour, const int min, const int sec, const fsec_t fsec)
23 return ((((((hour * 60) + min) * 60) + sec) * INT64CONST(1000000)) + fsec);
28 time2t(const int hour, const int min, const int sec, const fsec_t fsec)
30 return ((((hour * 60) + min) * 60) + sec + fsec);
35 dt2local(timestamp dt, int tz)
37 #ifdef HAVE_INT64_TIMESTAMP
38 dt -= (tz * INT64CONST(1000000));
47 * Convert a tm structure to a timestamp data type.
48 * Note that year is _not_ 1900-based, but is an explicit full value.
49 * Also, month is one-based, _not_ zero-based.
51 * Returns -1 on failure (overflow).
54 tm2timestamp(struct tm * tm, fsec_t fsec, int *tzp, timestamp *result)
56 #ifdef HAVE_INT64_TIMESTAMP
64 /* Julian day routines are not correct for negative Julian days */
65 if (!IS_VALID_JULIAN(tm->tm_year, tm->tm_mon, tm->tm_mday))
68 dDate = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1);
69 time = time2t(tm->tm_hour, tm->tm_min, tm->tm_sec, fsec);
70 #ifdef HAVE_INT64_TIMESTAMP
71 *result = (date * INT64CONST(86400000000)) + time;
72 /* check for major overflow */
73 if ((*result - time) / INT64CONST(86400000000) != date)
75 /* check for just-barely overflow (okay except time-of-day wraps) */
76 if ((*result < 0) ? (date >= 0) : (date < 0))
79 *result = ((dDate * 86400) + time);
82 *result = dt2local(*result, -(*tzp));
85 } /* tm2timestamp() */
88 SetEpochTimestamp(void)
95 tm2timestamp(tm, 0, NULL, &dt);
97 } /* SetEpochTimestamp() */
100 dt2time(timestamp jd, int *hour, int *min, int *sec, fsec_t *fsec)
102 #ifdef HAVE_INT64_TIMESTAMP
111 #ifdef HAVE_INT64_TIMESTAMP
112 *hour = (time / INT64CONST(3600000000));
113 time -= ((*hour) * INT64CONST(3600000000));
114 *min = (time / INT64CONST(60000000));
115 time -= ((*min) * INT64CONST(60000000));
116 *sec = (time / INT64CONST(1000000));
117 *fsec = (time - (*sec * INT64CONST(1000000)));
118 *sec = (time / INT64CONST(1000000));
119 *fsec = (time - (*sec * INT64CONST(1000000)));
121 *hour = (time / 3600);
122 time -= ((*hour) * 3600);
124 time -= ((*min) * 60);
126 *fsec = JROUND(time - *sec);
132 * Convert timestamp data type to POSIX time structure.
133 * Note that year is _not_ 1900-based, but is an explicit full value.
134 * Also, month is one-based, _not_ zero-based.
139 * For dates within the system-supported time_t range, convert to the
140 * local time zone. If out of this range, leave as GMT. - tgl 97/05/27
143 timestamp2tm(timestamp dt, int *tzp, struct tm * tm, fsec_t *fsec, char **tzn)
145 #ifdef HAVE_INT64_TIMESTAMP
155 #if defined(HAVE_TM_ZONE) || defined(HAVE_INT_TIMEZONE)
159 date0 = date2j(2000, 1, 1);
162 #ifdef HAVE_INT64_TIMESTAMP
163 TMODULO(time, dDate, INT64CONST(86400000000));
165 if (time < INT64CONST(0))
167 time += INT64CONST(86400000000);
171 TMODULO(time, dDate, 86400e0);
180 /* Julian day routine does not work for negative Julian days */
184 /* add offset to go from J2000 back to standard Julian date */
187 j2date((int) dDate, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
188 dt2time(time, &tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec);
193 * Does this fall within the capabilities of the localtime()
194 * interface? Then use this to rotate to the local time zone.
196 if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday))
198 #ifdef HAVE_INT64_TIMESTAMP
199 utime = ((dt / INT64CONST(1000000))
200 + ((date0 - date2j(1970, 1, 1)) * INT64CONST(86400)));
202 utime = (dt + ((date0 - date2j(1970, 1, 1)) * 86400));
205 #if defined(HAVE_TM_ZONE) || defined(HAVE_INT_TIMEZONE)
206 tx = localtime(&utime);
207 tm->tm_year = tx->tm_year + 1900;
208 tm->tm_mon = tx->tm_mon + 1;
209 tm->tm_mday = tx->tm_mday;
210 tm->tm_hour = tx->tm_hour;
211 tm->tm_min = tx->tm_min;
212 tm->tm_isdst = tx->tm_isdst;
214 #if defined(HAVE_TM_ZONE)
215 tm->tm_gmtoff = tx->tm_gmtoff;
216 tm->tm_zone = tx->tm_zone;
218 *tzp = -(tm->tm_gmtoff); /* tm_gmtoff is Sun/DEC-ism */
220 *tzn = (char *) tm->tm_zone;
221 #elif defined(HAVE_INT_TIMEZONE)
222 *tzp = ((tm->tm_isdst > 0) ? (TIMEZONE_GLOBAL - 3600) : TIMEZONE_GLOBAL);
224 *tzn = tzname[(tm->tm_isdst > 0)];
227 #else /* not (HAVE_TM_ZONE || HAVE_INT_TIMEZONE) */
229 /* Mark this as *no* time zone available */
235 dt = dt2local(dt, *tzp);
240 /* Mark this as *no* time zone available */
254 } /* timestamp2tm() */
256 /* EncodeSpecialTimestamp()
257 * * Convert reserved timestamp data type to string.
260 EncodeSpecialTimestamp(timestamp dt, char *str)
262 if (TIMESTAMP_IS_NOBEGIN(dt))
264 else if (TIMESTAMP_IS_NOEND(dt))
270 } /* EncodeSpecialTimestamp() */
273 PGTYPEStimestamp_from_asc(char *str, char **endptr)
277 #ifdef HAVE_INT64_TIMESTAMP
281 double noresult = 0.0;
289 char *field[MAXDATEFIELDS];
290 int ftype[MAXDATEFIELDS];
291 char lowstr[MAXDATELEN + MAXDATEFIELDS];
293 char **ptr = (endptr != NULL) ? endptr : &realptr;
296 if (strlen(str) >= sizeof(lowstr))
298 errno = PGTYPES_TS_BAD_TIMESTAMP;
302 if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf, ptr) != 0)
303 || (DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tz, 0) != 0))
305 errno = PGTYPES_TS_BAD_TIMESTAMP;
312 if (tm2timestamp(tm, fsec, NULL, &result) != 0)
314 errno = PGTYPES_TS_BAD_TIMESTAMP;
320 result = SetEpochTimestamp();
324 TIMESTAMP_NOEND(result);
328 TIMESTAMP_NOBEGIN(result);
332 errno = PGTYPES_TS_BAD_TIMESTAMP;
336 errno = PGTYPES_TS_BAD_TIMESTAMP;
340 /* AdjustTimestampForTypmod(&result, typmod); */
346 PGTYPEStimestamp_to_asc(timestamp tstamp)
350 char buf[MAXDATELEN + 1];
353 int DateStyle = 1; /* this defaults to ISO_DATES, shall we
354 * make it an option? */
356 if (TIMESTAMP_NOT_FINITE(tstamp))
357 EncodeSpecialTimestamp(tstamp, buf);
358 else if (timestamp2tm(tstamp, NULL, tm, &fsec, NULL) == 0)
359 EncodeDateTime(tm, fsec, NULL, &tzn, DateStyle, buf, 0);
362 errno = PGTYPES_TS_BAD_TIMESTAMP;
365 return pgtypes_strdup(buf);
369 PGTYPEStimestamp_current(timestamp *ts)
373 GetCurrentDateTime(&tm);
374 tm2timestamp(&tm, 0, NULL, ts);
379 dttofmtasc_replace(timestamp *ts, date dDate, int dow, struct tm * tm,
380 char *output, int *pstr_len, char *fmtstr)
382 union un_fmt_comb replace_val;
393 /* fix compiler warning */
394 replace_type = PGTYPES_TYPE_NOTHING;
398 replace_val.str_val = pgtypes_date_weekdays_short[dow];
399 replace_type = PGTYPES_TYPE_STRING_CONSTANT;
402 replace_val.str_val = days[dow];
403 replace_type = PGTYPES_TYPE_STRING_CONSTANT;
407 replace_val.str_val = months[tm->tm_mon];
408 replace_type = PGTYPES_TYPE_STRING_CONSTANT;
411 replace_val.str_val = pgtypes_date_months[tm->tm_mon];
412 replace_type = PGTYPES_TYPE_STRING_CONSTANT;
418 replace_val.uint_val = tm->tm_year / 100;
419 replace_type = PGTYPES_TYPE_UINT_2_LZ;
422 replace_val.uint_val = tm->tm_mday;
423 replace_type = PGTYPES_TYPE_UINT_2_LZ;
428 * ts, dDate, dow, tm is information about the
431 * q is the start of the current output buffer
433 * pstr_len is a pointer to the remaining size of output,
436 i = dttofmtasc_replace(ts, dDate, dow, tm,
443 replace_val.uint_val = tm->tm_mday;
444 replace_type = PGTYPES_TYPE_UINT_2_LS;
454 /* XXX: fall back to strftime */
457 * strftime's month is 0 based, ours is 1 based
460 i = strftime(q, *pstr_len, tmp, tm);
469 replace_type = PGTYPES_TYPE_NOTHING;
473 /* XXX: fall back to strftime */
475 i = strftime(q, *pstr_len, "%G", tm);
484 replace_type = PGTYPES_TYPE_NOTHING;
487 /* XXX: fall back to strftime */
489 char *fmt = "%g"; /* Keep compiler quiet
490 * about 2-digit year */
493 i = strftime(q, *pstr_len, fmt, tm);
502 replace_type = PGTYPES_TYPE_NOTHING;
506 replace_val.uint_val = tm->tm_hour;
507 replace_type = PGTYPES_TYPE_UINT_2_LZ;
510 replace_val.uint_val = tm->tm_hour % 12;
511 replace_type = PGTYPES_TYPE_UINT_2_LZ;
514 replace_val.uint_val = tm->tm_yday;
515 replace_type = PGTYPES_TYPE_UINT_3_LZ;
518 replace_val.uint_val = tm->tm_hour;
519 replace_type = PGTYPES_TYPE_UINT_2_LS;
522 replace_val.uint_val = tm->tm_hour % 12;
523 replace_type = PGTYPES_TYPE_UINT_2_LS;
526 replace_val.uint_val = tm->tm_mon;
527 replace_type = PGTYPES_TYPE_UINT_2_LZ;
530 replace_val.uint_val = tm->tm_min;
531 replace_type = PGTYPES_TYPE_UINT_2_LZ;
534 replace_val.char_val = '\n';
535 replace_type = PGTYPES_TYPE_CHAR;
538 if (tm->tm_hour < 12)
539 replace_val.str_val = "AM";
541 replace_val.str_val = "PM";
542 replace_type = PGTYPES_TYPE_STRING_CONSTANT;
545 if (tm->tm_hour < 12)
546 replace_val.str_val = "am";
548 replace_val.str_val = "pm";
549 replace_type = PGTYPES_TYPE_STRING_CONSTANT;
552 i = dttofmtasc_replace(ts, dDate, dow, tm,
559 i = dttofmtasc_replace(ts, dDate, dow, tm,
566 #ifdef HAVE_INT64_TIMESTAMP
567 replace_val.int64_val = ((*ts - SetEpochTimestamp()) / 1000000e0);
568 replace_type = PGTYPES_TYPE_INT64;
570 replace_val.double_val = *ts - SetEpochTimestamp();
571 replace_type = PGTYPES_TYPE_DOUBLE_NF;
575 replace_val.uint_val = tm->tm_sec;
576 replace_type = PGTYPES_TYPE_UINT;
579 replace_val.char_val = '\t';
580 replace_type = PGTYPES_TYPE_CHAR;
583 i = dttofmtasc_replace(ts, dDate, dow, tm,
592 replace_val.uint_val = dow;
593 replace_type = PGTYPES_TYPE_UINT;
596 /* XXX: fall back to strftime */
598 i = strftime(q, *pstr_len, "%U", tm);
607 replace_type = PGTYPES_TYPE_NOTHING;
610 /* XXX: fall back to strftime */
611 i = strftime(q, *pstr_len, "%V", tm);
619 replace_type = PGTYPES_TYPE_NOTHING;
622 replace_val.uint_val = dow;
623 replace_type = PGTYPES_TYPE_UINT;
626 /* XXX: fall back to strftime */
628 i = strftime(q, *pstr_len, "%U", tm);
637 replace_type = PGTYPES_TYPE_NOTHING;
640 /* XXX: fall back to strftime */
642 char *fmt = "%x"; /* Keep compiler quiet
643 * about 2-digit year */
646 i = strftime(q, *pstr_len, fmt, tm);
655 replace_type = PGTYPES_TYPE_NOTHING;
659 /* XXX: fall back to strftime */
661 i = strftime(q, *pstr_len, "%X", tm);
670 replace_type = PGTYPES_TYPE_NOTHING;
673 replace_val.uint_val = tm->tm_year % 100;
674 replace_type = PGTYPES_TYPE_UINT_2_LZ;
677 replace_val.uint_val = tm->tm_year;
678 replace_type = PGTYPES_TYPE_UINT;
681 /* XXX: fall back to strftime */
683 i = strftime(q, *pstr_len, "%z", tm);
692 replace_type = PGTYPES_TYPE_NOTHING;
695 /* XXX: fall back to strftime */
697 i = strftime(q, *pstr_len, "%Z", tm);
706 replace_type = PGTYPES_TYPE_NOTHING;
709 replace_val.char_val = '%';
710 replace_type = PGTYPES_TYPE_CHAR;
713 /* fmtstr: blabla%' */
716 * this is not compliant to the specification
722 * if we don't know the pattern, we just copy it
746 i = pgtypes_fmt_replace(replace_val, replace_type, &q, pstr_len);
769 PGTYPEStimestamp_fmt_asc(timestamp *ts, char *output, int str_len, char *fmtstr)
776 dDate = PGTYPESdate_from_timestamp(*ts);
777 dow = PGTYPESdate_dayofweek(dDate);
778 timestamp2tm(*ts, NULL, &tm, &fsec, NULL);
780 return dttofmtasc_replace(ts, dDate, dow, &tm, output, &str_len, fmtstr);
784 PGTYPEStimestamp_sub(timestamp *ts1, timestamp *ts2, interval *iv)
786 if (TIMESTAMP_NOT_FINITE(*ts1) || TIMESTAMP_NOT_FINITE(*ts2))
787 return PGTYPES_TS_ERR_EINFTIME;
789 #ifdef HAVE_INT64_TIMESTAMP
790 iv->time = (ts1 - ts2);
792 iv->time = JROUND(ts1 - ts2);
801 PGTYPEStimestamp_defmt_asc(char *str, char *fmt, timestamp *d)
816 fmt = "%Y-%m-%d %H:%M:%S";
820 mstr = pgtypes_strdup(str);
821 mfmt = pgtypes_strdup(fmt);
824 * initialize with impossible values so that we can see if the fields
825 * where specified at all
827 /* XXX ambiguity with 1 BC for year? */
836 i = PGTYPEStimestamp_defmt_scan(&mstr, mfmt, d, &year, &month, &day, &hour, &minute, &second, &tz);