From 8507ddb9c63bc341e52c82acf52770ecd423a994 Mon Sep 17 00:00:00 2001
From: "Thomas G. Lockhart" <lockhart@fourpalms.org>
Date: Tue, 1 Jul 1997 00:22:46 +0000
Subject: [PATCH] Use common parser and encoder for timestamp data type. Remove
 older date and time code (retain NEW_DATE_CODE and NEW_TIME_CODE). Use common
 encoder for date and time. Fix datetime +/- timespan math bug.

---
 src/backend/utils/adt/datetime.c  |  28 +-
 src/backend/utils/adt/dt.c        | 606 ++++++++++++++++++++++++++++--
 src/backend/utils/adt/timestamp.c |  24 +-
 3 files changed, 625 insertions(+), 33 deletions(-)

diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c
index 9690dd7fe76..2cf7324275b 100644
--- a/src/backend/utils/adt/datetime.c
+++ b/src/backend/utils/adt/datetime.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *    $Header: /cvsroot/pgsql/src/backend/utils/adt/datetime.c,v 1.9 1997/06/23 14:47:26 thomas Exp $
+ *    $Header: /cvsroot/pgsql/src/backend/utils/adt/datetime.c,v 1.10 1997/07/01 00:22:40 thomas Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -114,15 +114,24 @@ char *
 date_out(DateADT date)
 {
     char *result;
+    struct tm tt, *tm = &tt;
     char buf[MAXDATELEN+1];
+
+#if FALSE
     int year, month, day;
+#endif
 
-    j2date( (date + date2j(2000,1,1)), &year, &month, &day);
+    j2date( (date + date2j(2000,1,1)),
+      &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
 
+    EncodeDateOnly( tm, DateStyle, buf);
+
+#if FALSE
     if (EuroDates == 1) /* Output European-format dates */
         sprintf(buf, "%02d-%02d-%04d", day, month, year);
     else
         sprintf(buf, "%02d-%02d-%04d", month, day, year);
+#endif
 
     result = PALLOC(strlen(buf)+1);
 
@@ -445,19 +454,25 @@ char *
 time_out(TimeADT *time)
 {
     char *result;
+    struct tm tt, *tm = &tt;
+#if FALSE
     int hour, min, sec;
+#endif
     double fsec;
-    char buf[32];
+    char buf[MAXDATELEN+1];
 
     if (!PointerIsValid(time))
 	return NULL;
 
-    hour = (*time / (60*60));
-    min = (((int) (*time / 60)) % 60);
-    sec = (((int) *time) % 60);
+    tm->tm_hour = (*time / (60*60));
+    tm->tm_min = (((int) (*time / 60)) % 60);
+    tm->tm_sec = (((int) *time) % 60);
 
     fsec = 0;
 
+    EncodeTimeOnly( tm, fsec, DateStyle, buf);
+
+#if FALSE
     if (sec == 0.0) {
 	sprintf(buf, "%02d:%02d", hour, min);
 
@@ -468,6 +483,7 @@ time_out(TimeADT *time)
 	    sprintf(buf, "%02d:%02d:%05.2f", hour, min, (sec+fsec));
 	};
     };
+#endif
 
     result = PALLOC(strlen(buf)+1);
 
diff --git a/src/backend/utils/adt/dt.c b/src/backend/utils/adt/dt.c
index 68051df2162..809e39c749a 100644
--- a/src/backend/utils/adt/dt.c
+++ b/src/backend/utils/adt/dt.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *    $Header: /cvsroot/pgsql/src/backend/utils/adt/Attic/dt.c,v 1.26 1997/06/23 14:50:56 thomas Exp $
+ *    $Header: /cvsroot/pgsql/src/backend/utils/adt/Attic/dt.c,v 1.27 1997/07/01 00:22:43 thomas Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -722,6 +722,7 @@ printf( "datetime_add_span- date was %04d-%02d-%02d %02d:%02d:%02d\n",
 			tm->tm_mday = mdays[ tm->tm_mon-1];
 		    };
 		};
+
 #ifdef DATEDEBUG
 printf( "datetime_add_span- date becomes %04d-%02d-%02d %02d:%02d:%02d\n",
  tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
@@ -897,6 +898,125 @@ timespan_sub(TimeSpan *span1, TimeSpan *span2)
 } /* timespan_sub() */
 
 
+/* datetime_age()
+ * Calculate time difference while retaining year/month fields.
+ * Note that this does not result in an accurate absolute time span
+ *  since year and month are out of context once the arithmetic
+ *  is done.
+ */
+TimeSpan *
+datetime_age(DateTime *datetime1, DateTime *datetime2)
+{
+    TimeSpan *result;
+
+    DateTime dt1, dt2;
+    double fsec, fsec1, fsec2;
+    struct tm tt, *tm = &tt;
+    struct tm tt1, *tm1 = &tt1;
+    struct tm tt2, *tm2 = &tt2;
+
+    if (!PointerIsValid(datetime1) || !PointerIsValid(datetime2))
+	return NULL;
+
+    result = PALLOCTYPE(TimeSpan);
+
+    dt1 = *datetime1;
+    dt2 = *datetime2;
+
+    if (DATETIME_IS_RELATIVE(dt1)) dt1 = SetDateTime(dt1);
+    if (DATETIME_IS_RELATIVE(dt2)) dt2 = SetDateTime(dt2);
+
+    if (DATETIME_IS_INVALID(dt1)
+     || DATETIME_IS_INVALID(dt2)) {
+	DATETIME_INVALID( result->time);
+
+    } else if ((datetime2tm( dt1, NULL, tm1, &fsec1, NULL) == 0)
+     &&(datetime2tm( dt2, NULL, tm2, &fsec2, NULL) == 0)) {
+	fsec = (fsec1 - fsec2);
+	tm->tm_sec = (tm1->tm_sec - tm2->tm_sec);
+	tm->tm_min = (tm1->tm_min - tm2->tm_min);
+	tm->tm_hour = (tm1->tm_hour - tm2->tm_hour);
+	tm->tm_mday = (tm1->tm_mday - tm2->tm_mday);
+	tm->tm_mon = (tm1->tm_mon - tm2->tm_mon);
+	tm->tm_year = (tm1->tm_year - tm2->tm_year);
+
+	/* flip sign if necessary... */
+	if (dt1 < dt2) {
+	    fsec = -fsec;
+	    tm->tm_sec = -tm->tm_sec;
+	    tm->tm_min = -tm->tm_min;
+	    tm->tm_hour = -tm->tm_hour;
+	    tm->tm_mday = -tm->tm_mday;
+	    tm->tm_mon = -tm->tm_mon;
+	    tm->tm_year = -tm->tm_year;
+	};
+
+	if (tm->tm_sec < 0) {
+	    tm->tm_sec += 60;
+	    tm->tm_min--;
+	};
+
+	if (tm->tm_min < 0) {
+	    tm->tm_min += 60;
+	    tm->tm_hour--;
+	};
+
+	if (tm->tm_hour < 0) {
+	    tm->tm_hour += 24;
+	    tm->tm_mday--;
+	};
+
+	if (tm->tm_mday < 0) {
+	    if (dt1 < dt2) {
+		tm->tm_mday += mdays[tm1->tm_mon-1];
+		if (isleap(tm1->tm_year) && (tm1->tm_mon == 2)) tm->tm_mday++;
+		tm->tm_mon--;
+	    } else {
+		tm->tm_mday += mdays[tm2->tm_mon-1];
+		if (isleap(tm2->tm_year) && (tm2->tm_mon == 2)) tm->tm_mday++;
+		tm->tm_mon--;
+	    };
+	};
+
+	if (tm->tm_mon < 0) {
+	    tm->tm_mon += 12;
+	    tm->tm_year--;
+	};
+
+	/* recover sign if necessary... */
+	if (dt1 < dt2) {
+	    fsec = -fsec;
+	    tm->tm_sec = -tm->tm_sec;
+	    tm->tm_min = -tm->tm_min;
+	    tm->tm_hour = -tm->tm_hour;
+	    tm->tm_mday = -tm->tm_mday;
+	    tm->tm_mon = -tm->tm_mon;
+	    tm->tm_year = -tm->tm_year;
+	};
+
+	if (tm2timespan(tm, fsec, result) != 0) {
+	    elog(WARN,"Unable to decode datetime",NULL);
+	};
+
+#if FALSE
+	result->time = (fsec2 - fsec1);
+	result->time += (tm2->tm_sec - tm1->tm_sec);
+	result->time += 60*(tm2->tm_min - tm1->tm_min);
+	result->time += 3600*(tm2->tm_hour - tm1->tm_hour);
+	result->time += 86400*(tm2->tm_mday - tm1->tm_mday);
+
+	result->month = 12*(tm2->tm_year - tm1->tm_year);
+	result->month += (tm2->tm_mon - tm1->tm_mon);
+#endif
+
+    } else {
+	elog(WARN,"Unable to decode datetime",NULL);
+    };
+
+    return(result);
+} /* datetime_age() */
+
+
 /*----------------------------------------------------------
  *  Conversion operators.
  *---------------------------------------------------------*/
@@ -1016,6 +1136,242 @@ text_timespan(text *str)
 } /* text_timespan() */
 
 
+/* datetime_trunc()
+ * Extract specified field from datetime.
+ */
+DateTime *
+datetime_trunc(text *units, DateTime *datetime)
+{
+    DateTime *result;
+
+    DateTime dt;
+    int tz;
+    int type, val;
+    int i;
+    char *up, *lp, lowunits[MAXDATELEN+1];
+    double fsec;
+    char *tzn;
+    struct tm tt, *tm = &tt;
+
+    if ((!PointerIsValid(units)) || (!PointerIsValid(datetime)))
+	return NULL;
+
+    result = PALLOCTYPE(DateTime);
+
+    up = VARDATA(units);
+    lp = lowunits;
+    for (i = 0; i < (VARSIZE(units)-VARHDRSZ); i++) *lp++ = tolower( *up++);
+    *lp = '\0';
+
+    type = DecodeUnits( 0, lowunits, &val);
+#if FALSE
+    if (type == IGNORE) {
+	type = DecodeSpecial( 0, lowunits, &val);
+    };
+#endif
+
+#ifdef DATEDEBUG
+if (type == IGNORE) strcpy(lowunits, "(unknown)");
+printf( "datetime_trunc- units %s type=%d value=%d\n", lowunits, type, val); 
+#endif
+
+    if (DATETIME_NOT_FINITE(*datetime)) {
+#if FALSE
+/* should return null but Postgres doesn't like that currently. - tgl 97/06/12 */
+	elog(WARN,"Datetime is not finite",NULL);
+#endif
+	*result = 0;
+
+    } else {
+	dt = (DATETIME_IS_RELATIVE(*datetime)? SetDateTime(*datetime): *datetime); 
+
+	if ((type == UNITS) && (datetime2tm( dt, &tz, tm, &fsec, &tzn) == 0)) {
+	    switch (val) {
+	    case DTK_MILLENIUM:
+		tm->tm_year = (tm->tm_year/1000)*1000;
+	    case DTK_CENTURY:
+		tm->tm_year = (tm->tm_year/100)*100;
+	    case DTK_DECADE:
+		tm->tm_year = (tm->tm_year/10)*10;
+	    case DTK_YEAR:
+		tm->tm_mon = 1;
+	    case DTK_QUARTER:
+		tm->tm_mon = (3*(tm->tm_mon/4))+1;
+	    case DTK_MONTH:
+		tm->tm_mday = 1;
+	    case DTK_DAY:
+		tm->tm_hour = 0;
+	    case DTK_HOUR:
+		tm->tm_min = 0;
+	    case DTK_MINUTE:
+		tm->tm_sec = 0;
+	    case DTK_SECOND:
+		fsec = 0;
+		break;
+
+	    case DTK_MILLISEC:
+		fsec = rint(fsec*1000)/1000;
+		break;
+
+	    case DTK_MICROSEC:
+		fsec = rint(fsec*1000)/1000;
+		break;
+
+	    default:
+		elog(WARN,"Datetime units %s not supported",lowunits);
+		result = NULL;
+	    };
+
+	    if (IS_VALID_UTIME( tm->tm_year, tm->tm_mon, tm->tm_mday)) {
+#ifdef USE_POSIX_TIME
+		tm->tm_isdst = -1;
+		tm->tm_year -= 1900;
+		tm->tm_mon -= 1;
+		tm->tm_isdst = -1;
+		mktime(tm);
+		tm->tm_year += 1900;
+		tm->tm_mon += 1;
+
+#ifdef HAVE_INT_TIMEZONE
+		tz = ((tm->tm_isdst > 0)? (timezone - 3600): timezone);
+
+#else /* !HAVE_INT_TIMEZONE */
+		tz = -(tm->tm_gmtoff); /* tm_gmtoff is Sun/DEC-ism */
+#endif
+
+#else /* !USE_POSIX_TIME */
+		tz = CTimeZone;
+#endif
+	    } else {
+		tm->tm_isdst = 0;
+		tz = 0;
+	    };
+
+	    if (tm2datetime( tm, fsec, &tz, result) != 0)
+		elog(WARN,"Unable to truncate datetime to %s",lowunits);
+
+#if FALSE
+	} else if ((type == RESERV) && (val == DTK_EPOCH)) {
+	    DATETIME_EPOCH(*result);
+	    *result = dt - SetDateTime(*result);
+#endif
+
+	} else {
+	    elog(WARN,"Datetime units %s not recognized",lowunits);
+	    result = NULL;
+	};
+    };
+
+    return(result);
+} /* datetime_trunc() */
+
+/* timespan_trunc()
+ * Extract specified field from timespan.
+ */
+TimeSpan *
+timespan_trunc(text *units, TimeSpan *timespan)
+{
+    TimeSpan *result;
+
+    int type, val;
+    int i;
+    char *up, *lp, lowunits[MAXDATELEN+1];
+    double fsec;
+    struct tm tt, *tm = &tt;
+
+    if ((!PointerIsValid(units)) || (!PointerIsValid(timespan)))
+	return NULL;
+
+    result = PALLOCTYPE(TimeSpan);
+
+    up = VARDATA(units);
+    lp = lowunits;
+    for (i = 0; i < (VARSIZE(units)-VARHDRSZ); i++) *lp++ = tolower( *up++);
+    *lp = '\0';
+
+    type = DecodeUnits( 0, lowunits, &val);
+#if FALSE
+    if (type == IGNORE) {
+	type = DecodeSpecial( 0, lowunits, &val);
+    };
+#endif
+
+#ifdef DATEDEBUG
+if (type == IGNORE) strcpy(lowunits, "(unknown)");
+printf( "timespan_trunc- units %s type=%d value=%d\n", lowunits, type, val); 
+#endif
+
+    if (TIMESPAN_IS_INVALID(*timespan)) {
+#if FALSE
+	elog(WARN,"Timespan is not finite",NULL);
+#endif
+	result = NULL;
+
+    } else if (type == UNITS) {
+
+	if (timespan2tm(*timespan, tm, &fsec) == 0) {
+	    switch (val) {
+	    case DTK_MILLENIUM:
+		tm->tm_year = (tm->tm_year/1000)*1000;
+	    case DTK_CENTURY:
+		tm->tm_year = (tm->tm_year/100)*100;
+	    case DTK_DECADE:
+		tm->tm_year = (tm->tm_year/10)*10;
+	    case DTK_YEAR:
+		tm->tm_mon = 0;
+	    case DTK_QUARTER:
+		tm->tm_mon = (3*(tm->tm_mon/4));
+	    case DTK_MONTH:
+		tm->tm_mday = 0;
+	    case DTK_DAY:
+		tm->tm_hour = 0;
+	    case DTK_HOUR:
+		tm->tm_min = 0;
+	    case DTK_MINUTE:
+		tm->tm_sec = 0;
+	    case DTK_SECOND:
+		fsec = 0;
+		break;
+
+	    case DTK_MILLISEC:
+		fsec = rint(fsec*1000)/1000;
+		break;
+
+	    case DTK_MICROSEC:
+		fsec = rint(fsec*1000)/1000;
+		break;
+
+	    default:
+		elog(WARN,"Timespan units %s not supported",lowunits);
+		result = NULL;
+	    };
+
+	    if (tm2timespan(tm, fsec, result) != 0)
+		elog(WARN,"Unable to truncate timespan to %s",lowunits);
+
+	} else {
+	    elog(NOTICE,"Timespan out of range",NULL);
+	    result = NULL;
+	};
+
+#if FALSE
+    } else if ((type == RESERV) && (val == DTK_EPOCH)) {
+	*result = timespan->time;
+	if (timespan->month != 0) {
+	    *result += ((365.25*86400)*(timespan->month / 12));
+	    *result += ((30*86400)*(timespan->month % 12));
+	};
+#endif
+
+    } else {
+	elog(WARN,"Timespan units %s not recognized",units);
+	result = NULL;
+    };
+
+    return(result);
+} /* timespan_trunc() */
+
+
 /* datetime_part()
  * Extract specified field from datetime.
  */
@@ -1122,9 +1478,24 @@ printf( "datetime_part- units %s type=%d value=%d\n", lowunits, type, val);
 		*result = 0;
 	    };
 
-	} else if ((type == RESERV) && (val == DTK_EPOCH)) {
-	    DATETIME_EPOCH(*result);
-	    *result = dt - SetDateTime(*result);
+	} else if (type == RESERV) {
+	    switch (val) {
+	    case DTK_EPOCH:
+		DATETIME_EPOCH(*result);
+		*result = dt - SetDateTime(*result);
+		break;
+
+	    case DTK_DOW:
+		if (datetime2tm( dt, &tz, tm, &fsec, &tzn) != 0)
+		    elog(WARN,"Unable to encode datetime",NULL);
+
+		*result = j2day( date2j( tm->tm_year, tm->tm_mon, tm->tm_mday));
+		break;
+
+	    default:
+		elog(WARN,"Datetime units %s not supported",lowunits);
+		*result = 0;
+	    };
 
 	} else {
 	    elog(WARN,"Datetime units %s not recognized",lowunits);
@@ -1254,6 +1625,79 @@ printf( "timespan_part- units %s type=%d value=%d\n", lowunits, type, val);
 } /* timespan_part() */
 
 
+/* datetime_zone()
+ * Encode datetime type with specified time zone.
+ */
+text *
+datetime_zone(text *zone, DateTime *datetime)
+{
+    text *result;
+
+    DateTime dt;
+    int tz;
+    int type, val;
+    int i;
+    char *up, *lp, lowzone[MAXDATELEN+1];
+    char *tzn, upzone[MAXDATELEN+1];
+    double fsec;
+    struct tm tt, *tm = &tt;
+    char buf[MAXDATELEN+1];
+    int len;
+
+    if ((!PointerIsValid(zone)) || (!PointerIsValid(datetime)))
+	return NULL;
+
+    up = VARDATA(zone);
+    lp = lowzone;
+    for (i = 0; i < (VARSIZE(zone)-VARHDRSZ); i++) *lp++ = tolower( *up++);
+    *lp = '\0';
+
+    type = DecodeSpecial( 0, lowzone, &val);
+
+#ifdef DATEDEBUG
+if (type == IGNORE) strcpy(lowzone, "(unknown)");
+printf( "datetime_zone- zone %s type=%d value=%d\n", lowzone, type, val); 
+#endif
+
+    if (DATETIME_NOT_FINITE(*datetime)) {
+	/* could return null but Postgres doesn't like that currently. - tgl 97/06/12 */
+	elog(WARN,"Datetime is not finite",NULL);
+	result = NULL;
+
+    } else if ((type == TZ) || (type == DTZ)) {
+	tm->tm_isdst = ((type == DTZ)? 1: 0);
+	tz = val * 60;
+
+	dt = (DATETIME_IS_RELATIVE(*datetime)? SetDateTime(*datetime): *datetime); 
+	dt = dt2local( dt, tz);
+
+	if (datetime2tm( dt, NULL, tm, &fsec, NULL) != 0)
+	    elog(WARN,"Datetime not legal",NULL);
+
+	up = upzone;
+	lp = lowzone;
+	for (i = 0; *lp != '\0'; i++) *up++ = toupper( *lp++);
+	*up = '\0';
+
+	tzn = upzone;
+	EncodeDateTime(tm, fsec, &tz, &tzn, DateStyle, buf);
+
+	len = (strlen(buf)+VARHDRSZ);
+
+	result = PALLOC(len);
+
+	VARSIZE(result) = len;
+	memmove(VARDATA(result), buf, (len-VARHDRSZ));
+
+    } else {
+	elog(WARN,"Time zone %s not recognized",lowzone);
+	result = NULL;
+    };
+
+    return(result);
+} /* datetime_zone() */
+
+
 /***************************************************************************** 
  *   PRIVATE ROUTINES                                                        *
  *****************************************************************************/
@@ -1307,6 +1751,7 @@ static datetkn datetktbl[] = {
 {	"dec",		MONTH,	12},
 {	"december",	MONTH,	12},
 {	"dnt",		TZ,	6},		/* Dansk Normal Tid */
+{	"dow",		RESERV,	DTK_DOW},	/* day of week */
 {	"dst",		DTZMOD,	6},
 {	"east",		TZ,	NEG(60)},	/* East Australian Std Time */
 {	"edt",		DTZ,	NEG(24)},	/* Eastern Daylight Time */
@@ -2156,6 +2601,9 @@ printf( " %02d:%02d:%02d\n", tm->tm_hour, tm->tm_min, tm->tm_sec);
     if ((*dtype == DTK_DATE) && ((fmask & DTK_DATE_M) == DTK_DATE_M)
       && (tzp != NULL) && (! (fmask & DTK_M(TZ)))) {
 
+	/* daylight savings time modifier but no standard timezone? then error */
+	if (fmask & DTK_M(DTZMOD)) return -1;
+
 	if (IS_VALID_UTIME( tm->tm_year, tm->tm_mon, tm->tm_mday)) {
 #ifdef USE_POSIX_TIME
 	    tm->tm_year -= 1900;
@@ -2669,10 +3117,16 @@ DecodeSpecial(int field, char *lowtoken, int *val)
 	*val = 0;
     } else {
 	type = tp->type;
-	if ((type == TZ) || (type == DTZ) || (type == DTZMOD)) {
+	switch (type) {
+	case TZ:
+	case DTZ:
+	case DTZMOD:
 	    *val = FROMVAL(tp);
-	} else {
+	    break;
+
+	default:
 	    *val = tp->value;
+	    break;
 	};
     };
 
@@ -2990,15 +3444,125 @@ printf( "EncodeSpecialDateTime- unrecognized date\n");
 } /* EncodeSpecialDateTime() */
 
 
+/* EncodeDateOnly()
+ * Encode date as local time.
+ */
+int EncodeDateOnly(struct tm *tm, int style, char *str)
+{
+#if FALSE
+    int day;
+#endif
+
+    if ((tm->tm_mon < 1) || (tm->tm_mon > 12))
+	return -1;
+
+    /* compatible with ISO date formats */
+    if (style == USE_ISO_DATES) {
+	if (tm->tm_year > 0) {
+	    sprintf( str, "%04d-%02d-%02d",
+	      tm->tm_year, tm->tm_mon, tm->tm_mday);
+
+	} else {
+	    sprintf( str, "%04d-%02d-%02d %s",
+	      -(tm->tm_year-1), tm->tm_mon, tm->tm_mday, "BC");
+	};
+
+    /* compatible with Oracle/Ingres date formats */
+    } else if (style == USE_SQL_DATES) {
+	if (EuroDates) {
+	    sprintf( str, "%02d/%02d", tm->tm_mday, tm->tm_mon);
+	} else {
+	    sprintf( str, "%02d/%02d", tm->tm_mon, tm->tm_mday);
+	};
+	if (tm->tm_year > 0) {
+	    sprintf( (str+5), "/%04d", tm->tm_year);
+
+	} else {
+	    sprintf( (str+5), "/%04d %s", -(tm->tm_year-1), "BC");
+	};
+
+    /* backward-compatible with traditional Postgres abstime dates */
+    } else { /* if (style == USE_POSTGRES_DATES) */
+
+#if FALSE
+	day = date2j( tm->tm_year, tm->tm_mon, tm->tm_mday);
+#ifdef DATEDEBUG
+printf( "EncodeDateOnly- day is %d\n", day);
+#endif
+	tm->tm_wday = j2day( day);
+
+	strncpy( str, days[tm->tm_wday], 3);
+	strcpy( (str+3), " ");
+
+	if (EuroDates) {
+	    sprintf( (str+4), "%02d %3s", tm->tm_mday, months[tm->tm_mon-1]);
+	} else {
+	    sprintf( (str+4), "%3s %02d", months[tm->tm_mon-1], tm->tm_mday);
+	};
+	if (tm->tm_year > 0) {
+	    sprintf( (str+10), " %04d", tm->tm_year);
+
+	} else {
+	    sprintf( (str+10), " %04d %s", -(tm->tm_year-1), "BC");
+	};
+#endif
+
+	/* traditional date-only style for Postgres */
+	if (EuroDates) {
+	    sprintf( str, "%02d-%02d", tm->tm_mday, tm->tm_mon);
+	} else {
+	    sprintf( str, "%02d-%02d", tm->tm_mon, tm->tm_mday);
+	};
+	if (tm->tm_year > 0) {
+	    sprintf( (str+5), "-%04d", tm->tm_year);
+
+	} else {
+	    sprintf( (str+5), "-%04d %s", -(tm->tm_year-1), "BC");
+	};
+    };
+
+#ifdef DATEDEBUG
+printf( "EncodeDateOnly- date result is %s\n", str);
+#endif
+
+    return(TRUE);
+} /* EncodeDateOnly() */
+
+
+/* EncodeTimeOnly()
+ * Encode time fields only.
+ */
+int EncodeTimeOnly(struct tm *tm, double fsec, int style, char *str)
+{
+    double sec;
+
+    if ((tm->tm_hour < 0) || (tm->tm_hour > 24))
+	return -1;
+
+    sec = (tm->tm_sec + fsec);
+
+    sprintf( str, "%02d:%02d:", tm->tm_hour, tm->tm_min);
+    sprintf( (str+6), ((fsec != 0)? "%05.2f": "%02.0f"), sec);
+
+#ifdef DATEDEBUG
+printf( "EncodeTimeOnly- time result is %s\n", str);
+#endif
+
+    return(TRUE);
+} /* EncodeTimeOnly() */
+
+
 /* EncodeDateTime()
  * Encode date and time interpreted as local time.
  */
 int EncodeDateTime(struct tm *tm, double fsec, int *tzp, char **tzn, int style, char *str)
 {
-    char mabbrev[4], dabbrev[4];
     int day, hour, min;
     double sec;
 
+    if ((tm->tm_mon < 1) || (tm->tm_mon > 12))
+	return -1;
+
     sec = (tm->tm_sec + fsec);
 
 #ifdef DATEDEBUG
@@ -3016,20 +3580,6 @@ printf( "EncodeDateTime- timezone is %s (%s); offset is %d; daylight is %d\n",
 #endif
 #endif
 
-    day = date2j( tm->tm_year, tm->tm_mon, tm->tm_mday);
-#ifdef DATEDEBUG
-printf( "EncodeDateTime- day is %d\n", day);
-#endif
-    tm->tm_wday = j2day( day);
-
-    strncpy( dabbrev, days[tm->tm_wday], 3);
-    dabbrev[3] = '\0';
-
-    if ((tm->tm_mon < 1) || (tm->tm_mon > 12))
-	return -1;
-
-    strcpy( mabbrev, months[tm->tm_mon-1]);
-
     /* compatible with ISO date formats */
     if (style == USE_ISO_DATES) {
 	if (tm->tm_year > 0) {
@@ -3081,11 +3631,19 @@ printf( "EncodeDateTime- day is %d\n", day);
 
     /* backward-compatible with traditional Postgres abstime dates */
     } else { /* if (style == USE_POSTGRES_DATES) */
-	sprintf( str, "%3s ", dabbrev);
+	day = date2j( tm->tm_year, tm->tm_mon, tm->tm_mday);
+#ifdef DATEDEBUG
+printf( "EncodeDateTime- day is %d\n", day);
+#endif
+	tm->tm_wday = j2day( day);
+
+	strncpy( str, days[tm->tm_wday], 3);
+	strcpy( (str+3), " ");
+
 	if (EuroDates) {
-	    sprintf( (str+4), "%02d %3s", tm->tm_mday, mabbrev);
+	    sprintf( (str+4), "%02d %3s", tm->tm_mday, months[tm->tm_mon-1]);
 	} else {
-	    sprintf( (str+4), "%3s %02d", mabbrev, tm->tm_mday);
+	    sprintf( (str+4), "%3s %02d", months[tm->tm_mon-1], tm->tm_mday);
 	};
 	if (tm->tm_year > 0) {
 	    sprintf( (str+10), " %02d:%02d", tm->tm_hour, tm->tm_min);
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c
index e884f7eb08c..58113955e5a 100644
--- a/src/backend/utils/adt/timestamp.c
+++ b/src/backend/utils/adt/timestamp.c
@@ -3,8 +3,10 @@
 #include <time.h>
 #include <ctype.h>
 #include "postgres.h"
+#include "miscadmin.h"
 #include "utils/builtins.h"
 
+#if FALSE
 /* copy the next part of the string into a buffer */
 static const char *
 cpstr(const char *s, char *buf)
@@ -29,13 +31,16 @@ cpstr(const char *s, char *buf)
 	buf[in] = 0;
 	return s;
 }
+#endif
 
 /* assumes dd/mm/yyyy unless first item is month in word form */
 time_t
 timestamp_in(const char *timestamp_str)
 {
-    struct tm input_time;
     int4 result;
+
+#if FALSE
+    struct tm input_time;
 	char buf[18];
 	const char *p;
 	static const char *mstr[] = {
@@ -105,6 +110,9 @@ timestamp_in(const char *timestamp_str)
 
     /* use mktime(), but make this GMT, not local time */
     result = mktime(&input_time);
+#endif
+
+    result = nabstimein( (char *) timestamp_str);
 
     return result;
 }
@@ -113,14 +121,24 @@ char *
 timestamp_out(time_t timestamp)
 {
     char *result;
-    struct tm *time;
+    int tz;
+    double fsec = 0;
+    struct tm tt, *tm = &tt;
+    char buf[MAXDATELEN+1];
+    char zone[MAXDATELEN+1], *tzn = zone;
 
+#if FALSE
     time = localtime(&timestamp);
-    result = palloc(20);
+
     sprintf(result, "%04d-%02d-%02d %02d:%02d:%02d",
 	    time->tm_year+1900, time->tm_mon+1, time->tm_mday,
 	    time->tm_hour, time->tm_min, time->tm_sec);
+#endif
+    abstime2tm( timestamp, &tz, tm, tzn);
+    EncodeDateTime( tm, fsec, &tz, &tzn, USE_ISO_DATES, buf);
 
+    result = palloc(strlen(buf)+1);
+    strcpy( result, buf);
     return result;
 }
 
-- 
GitLab