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