datetime.cc #1

  • //
  • guest/
  • perforce_software/
  • p4/
  • support/
  • datetime.cc
  • View
  • Commits
  • Open Download .zip Download (11 KB)
/*
 * Copyright 1995, 1996 Perforce Software.  All rights reserved.
 *
 * This file is part of Perforce - the FAST SCM System.
 */

# define NEED_TIME
# define NEED_TIME_HP

# include <stdhdrs.h>
# include <charman.h>
 
# include <strbuf.h>
# include <error.h>
# include <errornum.h>
# include <msgsupp.h>

# include <datetime.h>

/*
 * DateTime - date as stored in license file (string of time(0))
 */

int
DateTimeParse( const char *&c, const char delim )
{
	int r = 0;

	for( ; *c && isAdigit( c ) && *c != delim; ++c )
	    r = r * 10 + *c - '0';

	// skip delim

	if( delim && *c == delim )
	    ++c;

	return r;
}

void
DateTime::Set( const char *date, Error *e )
{
	struct tm tm;
	const char *odate = date;
	wholeDay = 0;
	tval = 0;

	// No date?  Just clear

	if( !date )
	    return;

	// is it something simple like 'now'?

	if( !strcmp( date, "now" ) )
	{
	    tval = Now();
	    return;
	}

	// Just a time in seconds?

	tval = DateTimeParse( date, '/' );

	if( !*date )
	    return;

	// parse date
	// Allows mm/dd/yy or yyyy/mm/dd

	memset( (void *)&tm, 0, sizeof( tm ) );

	// used to be sscanf( "%d/%d/%d" ), but that crashed on HP GCC!
	// This expects yy/mm/dd.

	tm.tm_year = tval;
	tm.tm_mon = DateTimeParse( date, '/' );
	tm.tm_mday = DateTimeParse( date, ' ' );

	// Allow both : and ' ' to separate date/time

	if( *date == ':' )
	    ++date;

	// Compat with old date format (mm/dd/yy).

	if( tm.tm_mday > 31 )
	{
	    int x = tm.tm_year;
	    tm.tm_year = tm.tm_mday;
	    tm.tm_mday = tm.tm_mon;
	    tm.tm_mon = x;
	}

	// Adjust to tm-style times (year-1900, jan=0)

	--tm.tm_mon;

	if( tm.tm_year > 1900 )
	    tm.tm_year -= 1900;

	// Parse time
	// handles hh:mm:ss

	if( !( wholeDay = !*date ) )
	{
	    tm.tm_hour = DateTimeParse( date, ':' );
	    tm.tm_min = DateTimeParse( date, ':' );
	    tm.tm_sec = DateTimeParse( date, 0 );
	}

	// -1 for isdst tells mktime to figure it out

	tm.tm_isdst = -1;

	// And turn it into an int.
	// Return 0 if date no good (if any left, that is).

	int offset = ParseOffset( date, odate, e );
	if( e->Test() )
	    return;

	if( ( tval = mktime( &tm ) ) == (time_t) -1 )
	    e->Set( MsgSupp::InvalidDate ) << odate;

	if( offset )
	    tval -= ( offset - TzOffset( 0 ) );
}


// If 's' points to the start of a buffer formatted by FmtTz(), this routine
// reads the offset portion ([-]HHMM), converts it to the corresponding
// number of seconds, and returns that. If 's' points to a NUL terminator,
// this routine returns 0. If 's' points to anything else, this routine
// sets an error into 'e' and returns 0.
int	
DateTime::ParseOffset( const char *s, const char *odate, Error *e )
{
	int sign = 1;
	int seconds = 0, hours = 0, minutes = 0;

	if( !*s )
	    return 0;

	if( *s == ' ' )
	    s++;

	if( *s == '-' )
	{
	    sign = -1;
	    s++;
	}
	// 4 valid digits, then a space?
	if( isAdigit( &s[0] ) && isAdigit( &s[1] ) &&
	    isAdigit( &s[2] ) && isAdigit( &s[3] ) &&
	    s[4] == ' ' )
	{
	    hours = s[0] - '0';
	    hours = hours * 10 + s[1] - '0';
	    minutes = s[2] - '0';
	    minutes = minutes * 10 + s[3] - '0';
	    seconds = hours * 3600 + minutes * 60;
	    return seconds * sign;
	}

	e->Set( MsgSupp::InvalidDate ) << odate;
	return 0;
}

time_t
DateTime::Now()
{
	return time(0);
}

void
DateTime::FmtElapsed( char *buf,  const DateTime &t2 ) 
{
	int elapsed = t2.tval - tval;
	int hours = elapsed / 3600;
	int minutes = ( elapsed - (hours*3600) ) / 60;
	int seconds = elapsed - (hours*3600) - (minutes*60);

	sprintf( buf, "%02d:%02d:%02d", hours, minutes, seconds );
}

void
DateTime::Fmt( char *buf ) const
{
	struct tm *tm = localtime( &tval );

	// Don't die for a bogus date.

	if( tm )
	{
	    sprintf( buf, "%04d/%02d/%02d %02d:%02d:%02d",
		    tm->tm_year < 1900 ? tm->tm_year + 1900 : tm->tm_year,
		    tm->tm_mon + 1,
		    tm->tm_mday,
		    tm->tm_hour,
		    tm->tm_min,
		    tm->tm_sec );
	}
	else
	{
	    strcpy( buf, "1970/01/01" );
	}
}

void
DateTime::FmtUTC( char *buf ) const
{
	struct tm *tm = gmtime( &tval );

	// Don't die for a bogus date.

	if( tm )
	{
	    sprintf( buf, "%04d/%02d/%02d %02d:%02d:%02d",
		    tm->tm_year < 1900 ? tm->tm_year + 1900 : tm->tm_year,
		    tm->tm_mon + 1,
		    tm->tm_mday,
		    tm->tm_hour,
		    tm->tm_min,
		    tm->tm_sec );
	}
	else
	{
	    strcpy( buf, "1970/01/01" );
	}
}

void
DateTime::FmtDay( char *buf ) const
{
	struct tm *tm = localtime( &tval );

	// Don't die for a bogus date.

	if( tm )
	{
	    sprintf( buf, "%04d/%02d/%02d",
		    tm->tm_year < 1900 ? tm->tm_year + 1900 : tm->tm_year,
		    tm->tm_mon + 1,
		    tm->tm_mday );
	}
	else
	{
	    strcpy( buf, "1970/01/01" );
	}
}

void
DateTime::FmtDayUTC( char *buf ) const
{
	struct tm *tm = gmtime( &tval );

	// Don't die for a bogus date.

	if( tm )
	{
	    sprintf( buf, "%04d/%02d/%02d",
		    tm->tm_year < 1900 ? tm->tm_year + 1900 : tm->tm_year,
		    tm->tm_mon + 1,
		    tm->tm_mday );
	}
	else
	{
	    strcpy( buf, "1970/01/01" );
	}
}

# if defined( OS_FREEBSD22 ) || defined( OS_MACOSX ) || \
     defined( OS_DARWIN ) || defined( OS_VMS62 )
extern char *tzname[2];
# endif

# if defined( OS_AS400 ) || defined( OS_SUNOS )
char *tzname[2] = { "PDT", "PST" };
# endif

# if defined( OS_NT ) || defined( OS_CYGWIN )
# define tzname _tzname
# endif

int
DateTime::TzOffset( int *retdst ) const
{
	int offset = 0;

	// localtime for dst active or not
	// global time for computing offset in minutes

	struct tm *tm = localtime( &tval );

	// Don't die for a bogus date.

	if( !tm )
	    return offset;

	int isdst = tm->tm_isdst;

	tm = gmtime( &tval );

	// Don't die for a bogus date.

	if( !tm )
	    return offset;

	tm->tm_isdst = isdst;

	if( retdst )
	    *retdst = isdst;

	// take gmt time, pretend it's local, and compute the offset

	offset = ( tval - mktime( tm ) );

	return offset;
}

void
DateTime::FmtTz( char *buf ) const
{
	/*
	   adjust for display reasons
	   Note: Do not be tempted to sprintf the hours and
	   minutes seporately, there be dragons.
	   Those dragons are in the land of negative offsets.
	   On FreeBSD the timezone America/St_Johns is GMT-0230.
	   If the negative offset is a multiple of an hour it is
	   ok, but if the offset is not a multiple of an hour
	   then you will get problems displaying the signs.
	   from -1 to -59 minutes you want -0030 for example
	   not +00-30 if you seporate the hours and minutes.
	   Also, if the offset is -90 minutes you want -0130 not -01-30.
	   The number 40 below is ( 100 - 60 ) and the expression
	   is counting on integer division truncating toward zero
	   (i.e. leaving hours) then multiplying by 40 and adding
	   back in changes 90 into 130 or -90 into -130 or
	   270 into 430.  This code is confusing but this is a hard
	   issue and staying in the math realm to solve these problems
	   makes the result code much smaller and forces thought
	   about the issues. -- JAA
	*/

	int isdst = 0;

	int minutesOff = TzOffset( &isdst ) / 60;
	minutesOff += 40 * ( minutesOff / 60 );

	sprintf( buf, "%+05d", minutesOff );

	// if tzname is ascii, we'll display it

	const char *t;

	for( t = tzname[ isdst ]; *t; t++ )
	    if( isAhighchar( t ) || !isprint( *t ) )
		return;

	strcat( buf, " " );
	strcat( buf, tzname[ isdst ] );
}


/*
 * format date to match the diff 'unified date format'
 * pseudo-specification
 *
 */
void
DateTime::FmtUnifiedDiff( char *buf ) const
{
	struct tm *tm = gmtime( &tval );

	// Don't die for a bogus date.

	if( tm )
	{
	    int isdst = tm->tm_isdst;

	    int minutesOff = TzOffset( &isdst ) / 60;
	    minutesOff += 40 * ( minutesOff / 60 );

	    sprintf( buf, "%04d-%02d-%02d %02d:%02d:%02d.000000000 %-.4d",
		    tm->tm_year < 1900 ? tm->tm_year + 1900 : tm->tm_year,
		    tm->tm_mon + 1,
		    tm->tm_mday,
		    tm->tm_hour,
		    tm->tm_min,
		    tm->tm_sec,
		    minutesOff );
	}
	else
	{
	    strcpy( buf, "1970/01/01 00:00:01.000000000 -0000" );
	}

}

/*
 * CentralOffset - what's time_t's offset from midnight 1/1/70 GMT?
 *
 * Perforce time is always seconds since midnight 1/1/70 GMT, but some
 * OS's (notably the mac) use different epocs.  On the mac, it actually
 * depends on the library against which you link.  So this code, due to
 * Mark Lentzner, tries to divine the offset by clever use of gmtime()
 * and mktime().
 *
 * The offset is then fed to Localize() and Centralize(), which can then
 * used to shuffle from perforce to local time_t and vice versa.  Only
 * stat() and utime() seem to need this adjustment, not mktime().
 */

static int centralOffset;
static int centralInit = 0;

static void
CentralOffset()
{
	// Compute offset to centralized time: midnight Jan 1 1970 GMT

	// Find Midnight Jan 2 1970 localtime offset.

	struct tm tm;
	tm.tm_year = 70;
	tm.tm_mon = 0;
	tm.tm_mday = 2;
	tm.tm_hour = 0;
	tm.tm_min = 0;
	tm.tm_sec = 0;
	tm.tm_isdst = 0;

	centralOffset = mktime( &tm );

	// mktime returns localtime; shift to GMT

	// these are split because the C runtime library static
	// structure returned by gmtime above may be changed by mktime

	time_t dayone = 24*60*60;
	struct tm *tml = gmtime( &dayone );
	
	// 0 = GMT
	// + = local epoc is earlier than central
	// - = local epoc is after central (unlikely)

	centralOffset -= mktime( tml );

	// printf("Central Offset %d\n", centralOffset );

	centralInit = 1;
}

time_t
DateTime::Localize( time_t centralTime )
{
	if( !centralInit ) CentralOffset();
	return centralTime - centralOffset;
}

time_t
DateTime::Centralize( time_t localTime )
{
	if( !centralInit ) CentralOffset();
	return localTime + centralOffset;
}

void
DateTimeHighPrecision::Now()
{
# if defined( HAVE_CLOCK_GETTIME )
	struct timespec ts;
	clock_gettime( CLOCK_REALTIME, &ts );
	seconds = ts.tv_sec;
	nanos   = ts.tv_nsec;
# elif defined( HAVE_GETTIMEOFDAY )
	struct timeval tv;
	gettimeofday( &tv, (struct timezone *)0 );
	seconds = tv.tv_sec;
	nanos   = tv.tv_usec * 1000;
# elif defined( HAVE_GETSYSTEMTIME ) && defined( OS_MINGW )
	// MINGW doesn't have _mkgmtime, but does have mktime.

	SYSTEMTIME st;
	struct tm l_tm;

	::GetLocalTime( &st );

	l_tm.tm_sec   = st.wSecond;
	l_tm.tm_min   = st.wMinute;
	l_tm.tm_hour  = st.wHour;
	l_tm.tm_mday  = st.wDay;
	l_tm.tm_mon   = st.wMonth - 1;
	l_tm.tm_year  = st.wYear - 1900;
	l_tm.tm_wday  = 0;
	l_tm.tm_yday  = 0;
	l_tm.tm_isdst = 0;

	seconds = DateTime::Centralize( ::mktime( &l_tm ) );
	nanos   = st.wMilliseconds * 1000000;
# elif defined( HAVE_GETSYSTEMTIME )
	SYSTEMTIME st;
	struct tm u_tm;

	::GetSystemTime( &st );

	u_tm.tm_sec   = st.wSecond;
	u_tm.tm_min   = st.wMinute;
	u_tm.tm_hour  = st.wHour;
	u_tm.tm_mday  = st.wDay;
	u_tm.tm_mon   = st.wMonth - 1;
	u_tm.tm_year  = st.wYear - 1900;
	u_tm.tm_wday  = 0;
	u_tm.tm_yday  = 0;
	u_tm.tm_isdst = 0;

	seconds = DateTime::Centralize( ::_mkgmtime( &u_tm ) );
	nanos   = st.wMilliseconds * 1000000;
# else
	// Not sure there are any systems in this category right now, but
	// just in case we find some systems that can't handle either
	// gettimeofday or clock_gettime:

	seconds = time(0);
	nanos = 0;
# endif
}

time_t
DateTimeHighPrecision::Seconds() const
{
	return seconds;
}

int
DateTimeHighPrecision::Nanos() const
{
	return nanos;
}

void
DateTimeHighPrecision::Fmt( char *buf ) const
{
	struct tm *tm = localtime( &seconds );

	// Don't die for a bogus date.

	if( tm )
	{
	    sprintf( buf, "%04d/%02d/%02d %02d:%02d:%02d %09d",
		    tm->tm_year < 1900 ? tm->tm_year + 1900 : tm->tm_year,
		    tm->tm_mon + 1,
		    tm->tm_mday,
		    tm->tm_hour,
		    tm->tm_min,
		    tm->tm_sec,
	            nanos);
	}
	else
	{
	    strcpy( buf, "1970/01/01" );
	}
}

# Change User Description Committed
#2 12188 Matt Attaway Move 'main' p4 into a release specific directory in prep for new releases
#1 9129 Matt Attaway Initial commit of the 2014.1 p4/p4api source code