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