1 /*
2 * src/interfaces/ecpg/pgtypeslib/timestamp.c
3 */
4 #include "postgres_fe.h"
5
6 #include <time.h>
7 #include <float.h>
8 #include <limits.h>
9 #include <math.h>
10
11 #ifdef __FAST_MATH__
12 #error -ffast-math is known to break this code
13 #endif
14
15 #include "extern.h"
16 #include "dt.h"
17 #include "pgtypes_timestamp.h"
18 #include "pgtypes_date.h"
19
20
21 static int64
time2t(const int hour,const int min,const int sec,const fsec_t fsec)22 time2t(const int hour, const int min, const int sec, const fsec_t fsec)
23 {
24 return (((((hour * MINS_PER_HOUR) + min) * SECS_PER_MINUTE) + sec) * USECS_PER_SEC) + fsec;
25 } /* time2t() */
26
27 static timestamp
dt2local(timestamp dt,int tz)28 dt2local(timestamp dt, int tz)
29 {
30 dt -= (tz * USECS_PER_SEC);
31 return dt;
32 } /* dt2local() */
33
34 /* tm2timestamp()
35 * Convert a tm structure to a timestamp data type.
36 * Note that year is _not_ 1900-based, but is an explicit full value.
37 * Also, month is one-based, _not_ zero-based.
38 *
39 * Returns -1 on failure (overflow).
40 */
41 int
tm2timestamp(struct tm * tm,fsec_t fsec,int * tzp,timestamp * result)42 tm2timestamp(struct tm *tm, fsec_t fsec, int *tzp, timestamp * result)
43 {
44 int dDate;
45 int64 time;
46
47 /* Prevent overflow in Julian-day routines */
48 if (!IS_VALID_JULIAN(tm->tm_year, tm->tm_mon, tm->tm_mday))
49 return -1;
50
51 dDate = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1);
52 time = time2t(tm->tm_hour, tm->tm_min, tm->tm_sec, fsec);
53 *result = (dDate * USECS_PER_DAY) + time;
54 /* check for major overflow */
55 if ((*result - time) / USECS_PER_DAY != dDate)
56 return -1;
57 /* check for just-barely overflow (okay except time-of-day wraps) */
58 /* caution: we want to allow 1999-12-31 24:00:00 */
59 if ((*result < 0 && dDate > 0) ||
60 (*result > 0 && dDate < -1))
61 return -1;
62 if (tzp != NULL)
63 *result = dt2local(*result, -(*tzp));
64
65 /* final range check catches just-out-of-range timestamps */
66 if (!IS_VALID_TIMESTAMP(*result))
67 return -1;
68
69 return 0;
70 } /* tm2timestamp() */
71
72 static timestamp
SetEpochTimestamp(void)73 SetEpochTimestamp(void)
74 {
75 int64 noresult = 0;
76 timestamp dt;
77 struct tm tt,
78 *tm = &tt;
79
80 if (GetEpochTime(tm) < 0)
81 return noresult;
82
83 tm2timestamp(tm, 0, NULL, &dt);
84 return dt;
85 } /* SetEpochTimestamp() */
86
87 /* timestamp2tm()
88 * Convert timestamp data type to POSIX time structure.
89 * Note that year is _not_ 1900-based, but is an explicit full value.
90 * Also, month is one-based, _not_ zero-based.
91 * Returns:
92 * 0 on success
93 * -1 on out of range
94 *
95 * For dates within the system-supported time_t range, convert to the
96 * local time zone. If out of this range, leave as GMT. - tgl 97/05/27
97 */
98 static int
timestamp2tm(timestamp dt,int * tzp,struct tm * tm,fsec_t * fsec,const char ** tzn)99 timestamp2tm(timestamp dt, int *tzp, struct tm *tm, fsec_t *fsec, const char **tzn)
100 {
101 int64 dDate,
102 date0;
103 int64 time;
104 #if defined(HAVE_TM_ZONE) || defined(HAVE_INT_TIMEZONE)
105 time_t utime;
106 struct tm *tx;
107 #endif
108
109 date0 = date2j(2000, 1, 1);
110
111 time = dt;
112 TMODULO(time, dDate, USECS_PER_DAY);
113
114 if (time < INT64CONST(0))
115 {
116 time += USECS_PER_DAY;
117 dDate -= 1;
118 }
119
120 /* add offset to go from J2000 back to standard Julian date */
121 dDate += date0;
122
123 /* Julian day routine does not work for negative Julian days */
124 if (dDate < 0 || dDate > (timestamp) INT_MAX)
125 return -1;
126
127 j2date((int) dDate, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
128 dt2time(time, &tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec);
129
130 if (tzp != NULL)
131 {
132 /*
133 * Does this fall within the capabilities of the localtime()
134 * interface? Then use this to rotate to the local time zone.
135 */
136 if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday))
137 {
138 #if defined(HAVE_TM_ZONE) || defined(HAVE_INT_TIMEZONE)
139
140 utime = dt / USECS_PER_SEC +
141 ((date0 - date2j(1970, 1, 1)) * INT64CONST(86400));
142
143 tx = localtime(&utime);
144 tm->tm_year = tx->tm_year + 1900;
145 tm->tm_mon = tx->tm_mon + 1;
146 tm->tm_mday = tx->tm_mday;
147 tm->tm_hour = tx->tm_hour;
148 tm->tm_min = tx->tm_min;
149 tm->tm_isdst = tx->tm_isdst;
150
151 #if defined(HAVE_TM_ZONE)
152 tm->tm_gmtoff = tx->tm_gmtoff;
153 tm->tm_zone = tx->tm_zone;
154
155 *tzp = -tm->tm_gmtoff; /* tm_gmtoff is Sun/DEC-ism */
156 if (tzn != NULL)
157 *tzn = tm->tm_zone;
158 #elif defined(HAVE_INT_TIMEZONE)
159 *tzp = (tm->tm_isdst > 0) ? TIMEZONE_GLOBAL - SECS_PER_HOUR : TIMEZONE_GLOBAL;
160 if (tzn != NULL)
161 *tzn = TZNAME_GLOBAL[(tm->tm_isdst > 0)];
162 #endif
163 #else /* not (HAVE_TM_ZONE || HAVE_INT_TIMEZONE) */
164 *tzp = 0;
165 /* Mark this as *no* time zone available */
166 tm->tm_isdst = -1;
167 if (tzn != NULL)
168 *tzn = NULL;
169 #endif
170 }
171 else
172 {
173 *tzp = 0;
174 /* Mark this as *no* time zone available */
175 tm->tm_isdst = -1;
176 if (tzn != NULL)
177 *tzn = NULL;
178 }
179 }
180 else
181 {
182 tm->tm_isdst = -1;
183 if (tzn != NULL)
184 *tzn = NULL;
185 }
186
187 tm->tm_yday = dDate - date2j(tm->tm_year, 1, 1) + 1;
188
189 return 0;
190 } /* timestamp2tm() */
191
192 /* EncodeSpecialTimestamp()
193 * * Convert reserved timestamp data type to string.
194 * */
195 static void
EncodeSpecialTimestamp(timestamp dt,char * str)196 EncodeSpecialTimestamp(timestamp dt, char *str)
197 {
198 if (TIMESTAMP_IS_NOBEGIN(dt))
199 strcpy(str, EARLY);
200 else if (TIMESTAMP_IS_NOEND(dt))
201 strcpy(str, LATE);
202 else
203 abort(); /* shouldn't happen */
204 }
205
206 timestamp
PGTYPEStimestamp_from_asc(char * str,char ** endptr)207 PGTYPEStimestamp_from_asc(char *str, char **endptr)
208 {
209 timestamp result;
210 int64 noresult = 0;
211 fsec_t fsec;
212 struct tm tt,
213 *tm = &tt;
214 int dtype;
215 int nf;
216 char *field[MAXDATEFIELDS];
217 int ftype[MAXDATEFIELDS];
218 char lowstr[MAXDATELEN + MAXDATEFIELDS];
219 char *realptr;
220 char **ptr = (endptr != NULL) ? endptr : &realptr;
221
222 if (strlen(str) > MAXDATELEN)
223 {
224 errno = PGTYPES_TS_BAD_TIMESTAMP;
225 return noresult;
226 }
227
228 if (ParseDateTime(str, lowstr, field, ftype, &nf, ptr) != 0 ||
229 DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, 0) != 0)
230 {
231 errno = PGTYPES_TS_BAD_TIMESTAMP;
232 return noresult;
233 }
234
235 switch (dtype)
236 {
237 case DTK_DATE:
238 if (tm2timestamp(tm, fsec, NULL, &result) != 0)
239 {
240 errno = PGTYPES_TS_BAD_TIMESTAMP;
241 return noresult;
242 }
243 break;
244
245 case DTK_EPOCH:
246 result = SetEpochTimestamp();
247 break;
248
249 case DTK_LATE:
250 TIMESTAMP_NOEND(result);
251 break;
252
253 case DTK_EARLY:
254 TIMESTAMP_NOBEGIN(result);
255 break;
256
257 case DTK_INVALID:
258 errno = PGTYPES_TS_BAD_TIMESTAMP;
259 return noresult;
260
261 default:
262 errno = PGTYPES_TS_BAD_TIMESTAMP;
263 return noresult;
264 }
265
266 /* AdjustTimestampForTypmod(&result, typmod); */
267
268 /*
269 * Since it's difficult to test for noresult, make sure errno is 0 if no
270 * error occurred.
271 */
272 errno = 0;
273 return result;
274 }
275
276 char *
PGTYPEStimestamp_to_asc(timestamp tstamp)277 PGTYPEStimestamp_to_asc(timestamp tstamp)
278 {
279 struct tm tt,
280 *tm = &tt;
281 char buf[MAXDATELEN + 1];
282 fsec_t fsec;
283 int DateStyle = 1; /* this defaults to ISO_DATES, shall we make
284 * it an option? */
285
286 if (TIMESTAMP_NOT_FINITE(tstamp))
287 EncodeSpecialTimestamp(tstamp, buf);
288 else if (timestamp2tm(tstamp, NULL, tm, &fsec, NULL) == 0)
289 EncodeDateTime(tm, fsec, false, 0, NULL, DateStyle, buf, 0);
290 else
291 {
292 errno = PGTYPES_TS_BAD_TIMESTAMP;
293 return NULL;
294 }
295 return pgtypes_strdup(buf);
296 }
297
298 void
PGTYPEStimestamp_current(timestamp * ts)299 PGTYPEStimestamp_current(timestamp * ts)
300 {
301 struct tm tm;
302
303 GetCurrentDateTime(&tm);
304 if (errno == 0)
305 tm2timestamp(&tm, 0, NULL, ts);
306 return;
307 }
308
309 static int
dttofmtasc_replace(timestamp * ts,date dDate,int dow,struct tm * tm,char * output,int * pstr_len,const char * fmtstr)310 dttofmtasc_replace(timestamp * ts, date dDate, int dow, struct tm *tm,
311 char *output, int *pstr_len, const char *fmtstr)
312 {
313 union un_fmt_comb replace_val;
314 int replace_type;
315 int i;
316 const char *p = fmtstr;
317 char *q = output;
318
319 while (*p)
320 {
321 if (*p == '%')
322 {
323 p++;
324 /* fix compiler warning */
325 replace_type = PGTYPES_TYPE_NOTHING;
326 switch (*p)
327 {
328 /* the abbreviated name of the day in the week */
329 /* XXX should be locale aware */
330 case 'a':
331 replace_val.str_val = pgtypes_date_weekdays_short[dow];
332 replace_type = PGTYPES_TYPE_STRING_CONSTANT;
333 break;
334 /* the full name of the day in the week */
335 /* XXX should be locale aware */
336 case 'A':
337 replace_val.str_val = days[dow];
338 replace_type = PGTYPES_TYPE_STRING_CONSTANT;
339 break;
340 /* the abbreviated name of the month */
341 /* XXX should be locale aware */
342 case 'b':
343 case 'h':
344 replace_val.str_val = months[tm->tm_mon - 1];
345 replace_type = PGTYPES_TYPE_STRING_CONSTANT;
346 break;
347 /* the full name of the month */
348 /* XXX should be locale aware */
349 case 'B':
350 replace_val.str_val = pgtypes_date_months[tm->tm_mon - 1];
351 replace_type = PGTYPES_TYPE_STRING_CONSTANT;
352 break;
353
354 /*
355 * The preferred date and time representation for
356 * the current locale.
357 */
358 case 'c':
359 /* XXX */
360 break;
361 /* the century number with leading zeroes */
362 case 'C':
363 replace_val.uint_val = tm->tm_year / 100;
364 replace_type = PGTYPES_TYPE_UINT_2_LZ;
365 break;
366 /* day with leading zeroes (01 - 31) */
367 case 'd':
368 replace_val.uint_val = tm->tm_mday;
369 replace_type = PGTYPES_TYPE_UINT_2_LZ;
370 break;
371 /* the date in the format mm/dd/yy */
372 case 'D':
373
374 /*
375 * ts, dDate, dow, tm is information about the timestamp
376 *
377 * q is the start of the current output buffer
378 *
379 * pstr_len is a pointer to the remaining size of output,
380 * i.e. the size of q
381 */
382 i = dttofmtasc_replace(ts, dDate, dow, tm,
383 q, pstr_len,
384 "%m/%d/%y");
385 if (i)
386 return i;
387 break;
388 /* day with leading spaces (01 - 31) */
389 case 'e':
390 replace_val.uint_val = tm->tm_mday;
391 replace_type = PGTYPES_TYPE_UINT_2_LS;
392 break;
393
394 /*
395 * alternative format modifier
396 */
397 case 'E':
398 {
399 char tmp[4] = "%Ex";
400
401 p++;
402 if (*p == '\0')
403 return -1;
404 tmp[2] = *p;
405
406 /*
407 * strftime's month is 0 based, ours is 1 based
408 */
409 tm->tm_mon -= 1;
410 i = strftime(q, *pstr_len, tmp, tm);
411 if (i == 0)
412 return -1;
413 while (*q)
414 {
415 q++;
416 (*pstr_len)--;
417 }
418 tm->tm_mon += 1;
419 replace_type = PGTYPES_TYPE_NOTHING;
420 break;
421 }
422
423 /*
424 * The ISO 8601 year with century as a decimal number. The
425 * 4-digit year corresponding to the ISO week number.
426 */
427 case 'G':
428 {
429 /* Keep compiler quiet - Don't use a literal format */
430 const char *fmt = "%G";
431
432 tm->tm_mon -= 1;
433 i = strftime(q, *pstr_len, fmt, tm);
434 if (i == 0)
435 return -1;
436 while (*q)
437 {
438 q++;
439 (*pstr_len)--;
440 }
441 tm->tm_mon += 1;
442 replace_type = PGTYPES_TYPE_NOTHING;
443 }
444 break;
445
446 /*
447 * Like %G, but without century, i.e., with a 2-digit year
448 * (00-99).
449 */
450 case 'g':
451 {
452 const char *fmt = "%g"; /* Keep compiler quiet about
453 * 2-digit year */
454
455 tm->tm_mon -= 1;
456 i = strftime(q, *pstr_len, fmt, tm);
457 if (i == 0)
458 return -1;
459 while (*q)
460 {
461 q++;
462 (*pstr_len)--;
463 }
464 tm->tm_mon += 1;
465 replace_type = PGTYPES_TYPE_NOTHING;
466 }
467 break;
468 /* hour (24 hour clock) with leading zeroes */
469 case 'H':
470 replace_val.uint_val = tm->tm_hour;
471 replace_type = PGTYPES_TYPE_UINT_2_LZ;
472 break;
473 /* hour (12 hour clock) with leading zeroes */
474 case 'I':
475 replace_val.uint_val = tm->tm_hour % 12;
476 replace_type = PGTYPES_TYPE_UINT_2_LZ;
477 break;
478
479 /*
480 * The day of the year as a decimal number with leading
481 * zeroes. It ranges from 001 to 366.
482 */
483 case 'j':
484 replace_val.uint_val = tm->tm_yday;
485 replace_type = PGTYPES_TYPE_UINT_3_LZ;
486 break;
487
488 /*
489 * The hour (24 hour clock). Leading zeroes will be turned
490 * into spaces.
491 */
492 case 'k':
493 replace_val.uint_val = tm->tm_hour;
494 replace_type = PGTYPES_TYPE_UINT_2_LS;
495 break;
496
497 /*
498 * The hour (12 hour clock). Leading zeroes will be turned
499 * into spaces.
500 */
501 case 'l':
502 replace_val.uint_val = tm->tm_hour % 12;
503 replace_type = PGTYPES_TYPE_UINT_2_LS;
504 break;
505 /* The month as a decimal number with a leading zero */
506 case 'm':
507 replace_val.uint_val = tm->tm_mon;
508 replace_type = PGTYPES_TYPE_UINT_2_LZ;
509 break;
510 /* The minute as a decimal number with a leading zero */
511 case 'M':
512 replace_val.uint_val = tm->tm_min;
513 replace_type = PGTYPES_TYPE_UINT_2_LZ;
514 break;
515 /* A newline character */
516 case 'n':
517 replace_val.char_val = '\n';
518 replace_type = PGTYPES_TYPE_CHAR;
519 break;
520 /* the AM/PM specifier (uppercase) */
521 /* XXX should be locale aware */
522 case 'p':
523 if (tm->tm_hour < 12)
524 replace_val.str_val = "AM";
525 else
526 replace_val.str_val = "PM";
527 replace_type = PGTYPES_TYPE_STRING_CONSTANT;
528 break;
529 /* the AM/PM specifier (lowercase) */
530 /* XXX should be locale aware */
531 case 'P':
532 if (tm->tm_hour < 12)
533 replace_val.str_val = "am";
534 else
535 replace_val.str_val = "pm";
536 replace_type = PGTYPES_TYPE_STRING_CONSTANT;
537 break;
538 /* the time in the format %I:%M:%S %p */
539 /* XXX should be locale aware */
540 case 'r':
541 i = dttofmtasc_replace(ts, dDate, dow, tm,
542 q, pstr_len,
543 "%I:%M:%S %p");
544 if (i)
545 return i;
546 break;
547 /* The time in 24 hour notation (%H:%M) */
548 case 'R':
549 i = dttofmtasc_replace(ts, dDate, dow, tm,
550 q, pstr_len,
551 "%H:%M");
552 if (i)
553 return i;
554 break;
555 /* The number of seconds since the Epoch (1970-01-01) */
556 case 's':
557 replace_val.int64_val = (*ts - SetEpochTimestamp()) / 1000000.0;
558 replace_type = PGTYPES_TYPE_INT64;
559 break;
560 /* seconds as a decimal number with leading zeroes */
561 case 'S':
562 replace_val.uint_val = tm->tm_sec;
563 replace_type = PGTYPES_TYPE_UINT_2_LZ;
564 break;
565 /* A tabulator */
566 case 't':
567 replace_val.char_val = '\t';
568 replace_type = PGTYPES_TYPE_CHAR;
569 break;
570 /* The time in 24 hour notation (%H:%M:%S) */
571 case 'T':
572 i = dttofmtasc_replace(ts, dDate, dow, tm,
573 q, pstr_len,
574 "%H:%M:%S");
575 if (i)
576 return i;
577 break;
578
579 /*
580 * The day of the week as a decimal, Monday = 1, Sunday =
581 * 7
582 */
583 case 'u':
584 replace_val.uint_val = dow;
585 if (replace_val.uint_val == 0)
586 replace_val.uint_val = 7;
587 replace_type = PGTYPES_TYPE_UINT;
588 break;
589 /* The week number of the year as a decimal number */
590 case 'U':
591 tm->tm_mon -= 1;
592 i = strftime(q, *pstr_len, "%U", tm);
593 if (i == 0)
594 return -1;
595 while (*q)
596 {
597 q++;
598 (*pstr_len)--;
599 }
600 tm->tm_mon += 1;
601 replace_type = PGTYPES_TYPE_NOTHING;
602 break;
603
604 /*
605 * The ISO 8601:1988 week number of the current year as a
606 * decimal number.
607 */
608 case 'V':
609 {
610 /* Keep compiler quiet - Don't use a literal format */
611 const char *fmt = "%V";
612
613 i = strftime(q, *pstr_len, fmt, tm);
614 if (i == 0)
615 return -1;
616 while (*q)
617 {
618 q++;
619 (*pstr_len)--;
620 }
621 replace_type = PGTYPES_TYPE_NOTHING;
622 }
623 break;
624
625 /*
626 * The day of the week as a decimal, Sunday being 0 and
627 * Monday 1.
628 */
629 case 'w':
630 replace_val.uint_val = dow;
631 replace_type = PGTYPES_TYPE_UINT;
632 break;
633 /* The week number of the year (another definition) */
634 case 'W':
635 tm->tm_mon -= 1;
636 i = strftime(q, *pstr_len, "%U", tm);
637 if (i == 0)
638 return -1;
639 while (*q)
640 {
641 q++;
642 (*pstr_len)--;
643 }
644 tm->tm_mon += 1;
645 replace_type = PGTYPES_TYPE_NOTHING;
646 break;
647
648 /*
649 * The preferred date representation for the current
650 * locale without the time.
651 */
652 case 'x':
653 {
654 const char *fmt = "%x"; /* Keep compiler quiet about
655 * 2-digit year */
656
657 tm->tm_mon -= 1;
658 i = strftime(q, *pstr_len, fmt, tm);
659 if (i == 0)
660 return -1;
661 while (*q)
662 {
663 q++;
664 (*pstr_len)--;
665 }
666 tm->tm_mon += 1;
667 replace_type = PGTYPES_TYPE_NOTHING;
668 }
669 break;
670
671 /*
672 * The preferred time representation for the current
673 * locale without the date.
674 */
675 case 'X':
676 tm->tm_mon -= 1;
677 i = strftime(q, *pstr_len, "%X", tm);
678 if (i == 0)
679 return -1;
680 while (*q)
681 {
682 q++;
683 (*pstr_len)--;
684 }
685 tm->tm_mon += 1;
686 replace_type = PGTYPES_TYPE_NOTHING;
687 break;
688 /* The year without the century (2 digits, leading zeroes) */
689 case 'y':
690 replace_val.uint_val = tm->tm_year % 100;
691 replace_type = PGTYPES_TYPE_UINT_2_LZ;
692 break;
693 /* The year with the century (4 digits) */
694 case 'Y':
695 replace_val.uint_val = tm->tm_year;
696 replace_type = PGTYPES_TYPE_UINT;
697 break;
698 /* The time zone offset from GMT */
699 case 'z':
700 tm->tm_mon -= 1;
701 i = strftime(q, *pstr_len, "%z", tm);
702 if (i == 0)
703 return -1;
704 while (*q)
705 {
706 q++;
707 (*pstr_len)--;
708 }
709 tm->tm_mon += 1;
710 replace_type = PGTYPES_TYPE_NOTHING;
711 break;
712 /* The name or abbreviation of the time zone */
713 case 'Z':
714 tm->tm_mon -= 1;
715 i = strftime(q, *pstr_len, "%Z", tm);
716 if (i == 0)
717 return -1;
718 while (*q)
719 {
720 q++;
721 (*pstr_len)--;
722 }
723 tm->tm_mon += 1;
724 replace_type = PGTYPES_TYPE_NOTHING;
725 break;
726 /* A % sign */
727 case '%':
728 replace_val.char_val = '%';
729 replace_type = PGTYPES_TYPE_CHAR;
730 break;
731 case '\0':
732 /* fmtstr: foo%' - The string ends with a % sign */
733
734 /*
735 * this is not compliant to the specification
736 */
737 return -1;
738 default:
739
740 /*
741 * if we don't know the pattern, we just copy it
742 */
743 if (*pstr_len > 1)
744 {
745 *q = '%';
746 q++;
747 (*pstr_len)--;
748 if (*pstr_len > 1)
749 {
750 *q = *p;
751 q++;
752 (*pstr_len)--;
753 }
754 else
755 {
756 *q = '\0';
757 return -1;
758 }
759 *q = '\0';
760 }
761 else
762 return -1;
763 break;
764 }
765 i = pgtypes_fmt_replace(replace_val, replace_type, &q, pstr_len);
766 if (i)
767 return i;
768 }
769 else
770 {
771 if (*pstr_len > 1)
772 {
773 *q = *p;
774 (*pstr_len)--;
775 q++;
776 *q = '\0';
777 }
778 else
779 return -1;
780 }
781 p++;
782 }
783 return 0;
784 }
785
786
787 int
PGTYPEStimestamp_fmt_asc(timestamp * ts,char * output,int str_len,const char * fmtstr)788 PGTYPEStimestamp_fmt_asc(timestamp * ts, char *output, int str_len, const char *fmtstr)
789 {
790 struct tm tm;
791 fsec_t fsec;
792 date dDate;
793 int dow;
794
795 dDate = PGTYPESdate_from_timestamp(*ts);
796 dow = PGTYPESdate_dayofweek(dDate);
797 timestamp2tm(*ts, NULL, &tm, &fsec, NULL);
798
799 return dttofmtasc_replace(ts, dDate, dow, &tm, output, &str_len, fmtstr);
800 }
801
802 int
PGTYPEStimestamp_sub(timestamp * ts1,timestamp * ts2,interval * iv)803 PGTYPEStimestamp_sub(timestamp * ts1, timestamp * ts2, interval * iv)
804 {
805 if (TIMESTAMP_NOT_FINITE(*ts1) || TIMESTAMP_NOT_FINITE(*ts2))
806 return PGTYPES_TS_ERR_EINFTIME;
807 else
808 iv->time = (*ts1 - *ts2);
809
810 iv->month = 0;
811
812 return 0;
813 }
814
815 int
PGTYPEStimestamp_defmt_asc(const char * str,const char * fmt,timestamp * d)816 PGTYPEStimestamp_defmt_asc(const char *str, const char *fmt, timestamp * d)
817 {
818 int year,
819 month,
820 day;
821 int hour,
822 minute,
823 second;
824 int tz;
825
826 int i;
827 char *mstr;
828 char *mfmt;
829
830 if (!fmt)
831 fmt = "%Y-%m-%d %H:%M:%S";
832 if (!fmt[0])
833 return 1;
834
835 mstr = pgtypes_strdup(str);
836 mfmt = pgtypes_strdup(fmt);
837
838 /*
839 * initialize with impossible values so that we can see if the fields
840 * where specified at all
841 */
842 /* XXX ambiguity with 1 BC for year? */
843 year = -1;
844 month = -1;
845 day = -1;
846 hour = 0;
847 minute = -1;
848 second = -1;
849 tz = 0;
850
851 i = PGTYPEStimestamp_defmt_scan(&mstr, mfmt, d, &year, &month, &day, &hour, &minute, &second, &tz);
852 free(mstr);
853 free(mfmt);
854 return i;
855 }
856
857 /*
858 * add an interval to a time stamp
859 *
860 * *tout = tin + span
861 *
862 * returns 0 if successful
863 * returns -1 if it fails
864 *
865 */
866
867 int
PGTYPEStimestamp_add_interval(timestamp * tin,interval * span,timestamp * tout)868 PGTYPEStimestamp_add_interval(timestamp * tin, interval * span, timestamp * tout)
869 {
870 if (TIMESTAMP_NOT_FINITE(*tin))
871 *tout = *tin;
872
873
874 else
875 {
876 if (span->month != 0)
877 {
878 struct tm tt,
879 *tm = &tt;
880 fsec_t fsec;
881
882
883 if (timestamp2tm(*tin, NULL, tm, &fsec, NULL) != 0)
884 return -1;
885 tm->tm_mon += span->month;
886 if (tm->tm_mon > MONTHS_PER_YEAR)
887 {
888 tm->tm_year += (tm->tm_mon - 1) / MONTHS_PER_YEAR;
889 tm->tm_mon = (tm->tm_mon - 1) % MONTHS_PER_YEAR + 1;
890 }
891 else if (tm->tm_mon < 1)
892 {
893 tm->tm_year += tm->tm_mon / MONTHS_PER_YEAR - 1;
894 tm->tm_mon = tm->tm_mon % MONTHS_PER_YEAR + MONTHS_PER_YEAR;
895 }
896
897
898 /* adjust for end of month boundary problems... */
899 if (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1])
900 tm->tm_mday = (day_tab[isleap(tm->tm_year)][tm->tm_mon - 1]);
901
902
903 if (tm2timestamp(tm, fsec, NULL, tin) != 0)
904 return -1;
905 }
906
907
908 *tin += span->time;
909 *tout = *tin;
910 }
911 return 0;
912
913 }
914
915
916 /*
917 * subtract an interval from a time stamp
918 *
919 * *tout = tin - span
920 *
921 * returns 0 if successful
922 * returns -1 if it fails
923 *
924 */
925
926 int
PGTYPEStimestamp_sub_interval(timestamp * tin,interval * span,timestamp * tout)927 PGTYPEStimestamp_sub_interval(timestamp * tin, interval * span, timestamp * tout)
928 {
929 interval tspan;
930
931 tspan.month = -span->month;
932 tspan.time = -span->time;
933
934
935 return PGTYPEStimestamp_add_interval(tin, &tspan, tout);
936 }
937