#include <stdio.h>
#include <time.h>

struct pg_tm
{
	int			tm_sec;
	int			tm_min;
	int			tm_hour;
	int			tm_mday;
	int			tm_mon;			/* origin 0, not 1 */
	int			tm_year;		/* relative to 1900 */
	int			tm_wday;
	int			tm_yday;
	int			tm_isdst;
	long int	tm_gmtoff;
	const char *tm_zone;
};


char *
pg_uint2str_padding(char *str, unsigned int value, unsigned int padding)
{
	char	   *start = str;
	char	   *end = &str[padding];
	//Assert(padding > 0);
	
	*end = '\0';
	
	while (padding--)
	{
		str[padding] = value % 10 + '0';
		value /= 10;
	}
	
	return end;
}

char *
pg_uint2str(char *str, unsigned int value)
{
	char *start = str;
	char *end;
	/* Compute the result string backwards. */
	do
	{
		int		remainder;
		int		oldval = value;

		value /= 10;
		remainder = oldval - value * 10;
		*str++ = '0' + remainder;
	} while (value != 0);


	/* Add trailing NUL byte, and back up 'str' to the last character. */
	*str-- = '\0';
	end = str;
	
	/* Reverse string. */
	while (start < str)
	{
		char		swap = *start;

		*start++ = *str;
		*str-- = swap;
	}
	return end;
}


char *
timestamp_out_af(char *buffer, struct pg_tm *tm, unsigned int fsec)
{
	char *str = buffer;

	*str++ = (tm->tm_year / 1000) + '0';
	*str++ = (tm->tm_year / 100) % 10 + '0';
	*str++ = (tm->tm_year / 10) % 10 + '0';
	*str++ = tm->tm_year % 10 + '0';
	*str++ = '-';
	*str++ = (tm->tm_mon / 10) + '0';
	*str++ = tm->tm_mon % 10 + '0';
	*str++ = '-';
	*str++ = (tm->tm_mday / 10) + '0';
	*str++ = tm->tm_mday % 10 + '0';
	*str++ = ' ';
	*str++ = (tm->tm_hour / 10) + '0';
	*str++ = tm->tm_hour % 10 + '0';
	*str++ = ':';
	*str++ = (tm->tm_min / 10) + '0';
	*str++ = tm->tm_min % 10 + '0';
	*str++ = ':';
	*str++ = (tm->tm_sec / 10) + '0';
	*str++ = tm->tm_sec % 10 + '0';

	/*
	 * Yes, this is darned ugly and would look nicer in a loop,
	 * but some versions of gcc can't convert the divisions into
	 * more efficient instructions unless manually unrolled.
	 */
	if (fsec != 0)
	{
		int fseca = abs(fsec);

		*str++ = '.';

		if (fseca % 1000000 != 0)
		{
			*str++ = (fseca / 100000) + '0';

			if (fseca % 100000 != 0)
			{
				*str++ = ((fseca / 10000) % 10) + '0';

				if (fseca % 10000 != 0)
				{
					*str++ = ((fseca / 1000) % 10) + '0';

					if (fseca % 1000 != 0)
					{
						*str++ = ((fseca / 100) % 10) + '0';

						if (fseca % 100 != 0)
						{
							*str++ = ((fseca / 10) % 10) + '0';

							if (fseca % 10 != 0)
							{
								*str++ = (fseca % 10) + '0';
							}
						}
					}
				}
			}
		}
	}
	
	return buffer;
}

char *
timestamp_out(char *buffer, struct pg_tm *tm, unsigned int fsec)
{
	char *str = buffer;
	
	str = pg_uint2str_padding(str, tm->tm_year, 4);
	*str++ = '-';
	str = pg_uint2str_padding(str, tm->tm_mon, 2);
	*str++ = '-';
	str = pg_uint2str_padding(str, tm->tm_mday, 2);
	*str++ = ' ';
	str = pg_uint2str_padding(str, tm->tm_hour, 2);
	*str++ = ':';
	str = pg_uint2str_padding(str, tm->tm_min, 2);
	*str++ = ':';
	str = pg_uint2str_padding(str, tm->tm_sec, 2);
	
	if (fsec != 0)
	{
		while (fsec % 10 == 0 && fsec > 0)
			fsec /= 10;
			
		*str++ = '.';
		str = pg_uint2str(str, fsec);
	}
	
	return buffer;
}

#define MAX_TIMESTAMP_PRECISION 6
#define HAVE_INT64_TIMESTAMP
#define Abs(x)			((x) >= 0 ? (x) : -(x))

/* TrimTrailingZeros()
 * ... resulting from printing numbers with full precision.
 *
 * Before Postgres 8.4, this always left at least 2 fractional digits,
 * but conversations on the lists suggest this isn't desired
 * since showing '0.10' is misleading with values of precision(1).
 */
static void
TrimTrailingZeros(char *str)
{
	int			len = strlen(str);

	while (len > 1 && *(str + len - 1) == '0' && *(str + len - 2) != '.')
	{
		len--;
		*(str + len) = '\0';
	}
}

/*
 * Append sections and fractional seconds (if any) at *cp.
 * precision is the max number of fraction digits, fillzeros says to
 * pad to two integral-seconds digits.
 * Note that any sign is stripped from the input seconds values.
 */
static void
AppendSeconds(char *cp, int sec, unsigned int fsec, int precision, char fillzeros)
{
	if (fsec == 0)
	{
		if (fillzeros)
			sprintf(cp, "%02d", abs(sec));
		else
			sprintf(cp, "%d", abs(sec));
	}
	else
	{
#ifdef HAVE_INT64_TIMESTAMP
		if (fillzeros)
			sprintf(cp, "%02d.%0*d", abs(sec), precision, (int) Abs(fsec));
		else
			sprintf(cp, "%d.%0*d", abs(sec), precision, (int) Abs(fsec));
#else
		if (fillzeros)
			sprintf(cp, "%0*.*f", precision + 3, precision, fabs(sec + fsec));
		else
			sprintf(cp, "%.*f", precision, fabs(sec + fsec));
#endif
		TrimTrailingZeros(cp);
	}
}


/* Variant of above that's specialized to timestamp case */
static void
AppendTimestampSeconds(char *cp, struct pg_tm * tm, unsigned int fsec)
{
	/*
	 * In float mode, don't print fractional seconds before 1 AD, since it's
	 * unlikely there's any precision left ...
	 */
#ifndef HAVE_INT64_TIMESTAMP
	if (tm->tm_year <= 0)
		fsec = 0;
#endif
	AppendSeconds(cp, tm->tm_sec, fsec, MAX_TIMESTAMP_PRECISION, 1);
}

char *
timestamp_out_old(char *buffer, struct pg_tm *tm, unsigned int fsec)
{
	sprintf(buffer, "%04d-%02d-%02d %02d:%02d:",
			(tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1),
			tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min);
	AppendTimestampSeconds(buffer + strlen(buffer), tm, fsec);
	return buffer;
}


int
main(void)
{
	char buffer[100];
	clock_t starttime, endtime;
	struct pg_tm tm;
	int i;
	
	tm.tm_year = 2015;
	tm.tm_mon = 7;
	tm.tm_mday = 29;
	tm.tm_hour = 2;
	tm.tm_min = 24;
	tm.tm_sec = 33;
	
	starttime = clock();
	
	for(i = 0; i < 100000000; i++)
	{
		timestamp_out(buffer, &tm, 34000);
	}
	
	endtime = clock();
	
	printf("timestamp_out() = %s in %f\n", buffer, (double)(endtime - starttime) / CLOCKS_PER_SEC);

	starttime = clock();
	
	for(i = 0; i < 100000000; i++)
	{
		timestamp_out_old(buffer, &tm, 34000);
	}
	
	endtime = clock();
	
	printf("timestamp_out_old() = %s in %f\n", buffer, (double)(endtime - starttime) / CLOCKS_PER_SEC);

	starttime = clock();
	
	for(i = 0; i < 100000000; i++)
	{
		timestamp_out_af(buffer, &tm, 34000);
	}
	
	endtime = clock();
	
	printf("timestamp_out_af() = %s in %f\n", buffer, (double)(endtime - starttime) / CLOCKS_PER_SEC);
	
}