// 
// Cforall Version 1.0.0 Copyright (C) 2018 University of Waterloo
//
// The contents of this file are covered under the licence agreement in the
// file "LICENCE" distributed with Cforall.
// 
// time.c -- 
// 
// Author           : Peter A. Buhr
// Created On       : Tue Mar 27 13:33:14 2018
// Last Modified By : Peter A. Buhr
// Last Modified On : Sun May  6 22:26:00 2018
// Update Count     : 37
// 

#include "time"
#include "iostream"
#include <stdio.h>										// snprintf
#include <assert.h>

static char * nanomsd( long int ns, char * buf ) {		// most significant digits
	snprintf( buf, 16, ".%09ld", ns );
	int i;
	for ( i = 9; buf[i] == '0' ; i -= 1 );				// find least significant digit
	buf[i + 1] = '\0';
	return buf;
} // nanomsd


//######################### Duration #########################


forall( dtype ostype | ostream( ostype ) )
ostype & ?|?( ostype & os, Duration dur ) with( dur ) {
	os | tv / TIMEGRAN;									// print seconds
	long int ns = (tv < 0 ? -tv : tv) % TIMEGRAN;		// compute nanoseconds
	if ( ns != 0 ) {									// some ?
		char buf[16];
		os | nanomsd( ns, buf );						// print nanoseconds
	} // if
	return os;
} // ?|?


//######################### Time #########################


#ifdef __CFA_DEBUG__
#define CreateFmt "Attempt to create Time( year=%d (>=1970), month=%d (1-12), day=%d (1-31), hour=%d (0-23), min=%d (0-59), sec=%d (0-60), nsec=%d (0-999_999_999), " \
	"which exceeds range 00:00:00 UTC, January 1, 1970 to 03:14:07 UTC, January 19, 2038."
#endif // __CFA_DEBUG__

void ?{}( Time & time, int year, int month, int day, int hour, int min, int sec, int nsec ) with( time ) {
	tm tm;

	tm.tm_isdst = -1;									// let mktime determine if alternate timezone is in effect
	tm.tm_year = year - 1900;							// mktime uses 1900 as its starting point
#ifdef __CFA_DEBUG__
	if ( month < 1 || 12 < month ) {
		abort( CreateFmt, year, month, day, hour, (int)min, sec, nsec );
	} // if
#endif // __CFA_DEBUG__
	tm.tm_mon = month - 1;								// mktime uses range 0-11
#ifdef __CFA_DEBUG__
	if ( day < 1 || 31 < day ) {
		abort( CreateFmt, year, month, day, hour, (int)min, sec, nsec );
	} // if
#endif // __CFA_DEBUG__
	tm.tm_mday = day;									// mktime uses range 1-31
	tm.tm_hour = hour;
	tm.tm_min = min;
	tm.tm_sec = sec;
	time_t epochsec = mktime( &tm );
#ifdef __CFA_DEBUG__
	if ( epochsec == (time_t)-1 ) {
		abort( CreateFmt, year, month, day, hour, (int)min, sec, nsec );
	} // if
#endif // __CFA_DEBUG__
	tv = (int64_t)(epochsec) * TIMEGRAN + nsec;			// convert to nanoseconds
#ifdef __CFA_DEBUG__
	if ( tv > 2147483647LL * TIMEGRAN ) {				// between 00:00:00 UTC, January 1, 1970 and 03:14:07 UTC, January 19, 2038.
		abort( CreateFmt, year, month, day, hour, (int)min, sec, nsec );
	} // if
#endif // __CFA_DEBUG__
} // ?{}

char * yy_mm_dd( Time time, char * buf ) with( time ) {
	time_t s = tv / TIMEGRAN;
	tm tm;
	gmtime_r( &s, &tm );								// tm_mon <= 11, tm_mday <= 31
#if defined(__GNUC__) && __GNUC__ >= 7
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-truncation"
#endif
	snprintf( buf, 9, "%02d/%02d/%02d", tm.tm_year % 99, tm.tm_mon + 1, tm.tm_mday );
#if defined(__GNUC__) && __GNUC__ >= 7
#pragma GCC diagnostic pop
#endif
	return buf;
} // yy_mm_dd

char * mm_dd_yy( Time time, char * buf ) with( time ) {
	time_t s = tv / TIMEGRAN;
	tm tm;
	gmtime_r( &s, &tm );								// tm_mon <= 11, tm_mday <= 31
#if defined(__GNUC__) && __GNUC__ >= 7
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-truncation"
#endif
	snprintf( buf, 9, "%02d/%02d/%02d", tm.tm_mon + 1, tm.tm_mday, tm.tm_year % 99 );
#if defined(__GNUC__) && __GNUC__ >= 7
#pragma GCC diagnostic pop
#endif
	return buf;
} // mm_dd_yy

char * dd_mm_yy( Time time, char * buf ) with( time ) {
	time_t s = tv / TIMEGRAN;
	tm tm;
	gmtime_r( &s, &tm );								// tm_mon <= 11, tm_mday <= 31
#if defined(__GNUC__) && __GNUC__ >= 7
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-truncation"
#endif
	snprintf( buf, 9, "%02d/%02d/%02d", tm.tm_mday, tm.tm_mon + 1, tm.tm_year % 99 );
#if defined(__GNUC__) && __GNUC__ >= 7
#pragma GCC diagnostic pop
#endif
	return buf;
} // dd_mm_yy

size_t strftime( char * buf, size_t size, const char * fmt, Time time ) with( time ) {
	time_t s = tv / TIMEGRAN;
	tm tm;
	gmtime_r( &s, &tm );
	return strftime( buf, size, fmt, &tm );
} // strftime

forall( dtype ostype | ostream( ostype ) )
ostype & ?|?( ostype & os, Time time ) with( time ) {
	char buf[32];										// at least 26
	time_t s = tv / TIMEGRAN;
    ctime_r( &s, (char *)&buf );						// 26 characters: "Wed Jun 30 21:49:08 1993\n"
	buf[24] = '\0';										// remove trailing '\n'
	long int ns = (tv < 0 ? -tv : tv) % TIMEGRAN;		// compute nanoseconds
	if ( ns == 0 ) {									// none ?
		os | buf;										// print date/time/year
	} else {
		buf[19] = '\0';									// truncate to "Wed Jun 30 21:49:08"
		os | buf;										// print date/time
		char buf2[16];
		nanomsd( ns, buf2 );							// compute nanoseconds
		os | buf2 | ' ' | &buf[20];						// print nanoseconds and year
	} // if
	return os;
} // ?|?

// Local Variables: //
// mode: c //
// tab-width: 4 //
// End: //
