Code:
$ tm2tm '5/7/37 15:34' '%m/%d/%y %H:%M' -8h '%Y-%m-%d %r %Z>
2037-05-07 07:34:00 AM EDT
$ tm2tm '5/7/38 15:34' '%m/%d/%y %H:%M' -8h '%Y-%m-%d %r %Z>
Error converting date-time '5/7/38 15:34' using format '%m/%d/%y %H:%M': Result too large
$ cat mysrc/tm2tm.c
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <time.h>
#include <errno.h>
#include <ctype.h>
static void usage_exit()
{
fputs(
"\n"
"Usage: tm2tm { -c | { - | <tm_str> } <in_fmt> } <delta> <out_fmt>\n"
" First, gets dates and time(s):\n"
" - when '-c' is used, from gettimeofday() (microsecond system clock)\n"
" - when '-' is used, from the front of each line of stdin per <in_fmt>\n"
" - else, from the front of <tm_str> per <in_fmt>\n"
" The default year is 2000, and for other elements, minimum values.\n"
" The <in_fmt> is either one of these or composed per strptime():\n"
" %s Absolute Unix time in integer seconds\n"
" %s.%F Unix time in integer and fractional 6 place seconds\n"
" %s.%f Unix time in integer and fractional 1-6 place seconds\n"
" Modify each time by <delta>, an optionally sign, integer number,\n"
" and optional suffix indicating the time unit (default seconds):\n"
" Y or y years m months\n"
" D or d days H or h hours\n"
" M minutes S or s seconds\n"
" Format the time by <out_fmt>, which supports all of the strftime() values,\n"
" plus the following:\n"
" %s Absolute Unix time in seconds\n"
" %F Fractional 6 place seconds\n"
" %f Fractional 1-6 place, zero suppressed seconds\n"
" Write the converted time plus any following input data to standard out.\n"
" For example: tm2tm '5/7/05 15:34' '%m/%d/%y %H:%M' -8h '%Y-%m-%d %r %Z'\n"
" prints '2005-05-07 07:34:00 AM EDT'\n",
stderr );
exit( 1 );
}
static void add_u_time( char *os, char *is, struct timeval *tv )
{
int st ;
int i ;
for ( st=0 ; *is ; os++, is++ )
{
switch ( *os = *is )
{
case '%':
st ^= 1 ;
continue ;
case 's':
if ( st )
{
st = 0 ;
os += sprintf( os - 1, "%u", tv->tv_sec ) ;
os -= 2 ;
}
continue ;
case 'f':
case 'F':
if ( st )
{
st = 0 ;
os += sprintf( os - 1, "%6.6u", tv->tv_usec );
os -= 2 ;
/* trim off up to 5 trailing 0's for %f */
for ( i = 0 ;
i < 5 && *os == '0' && *is == 'f' ;
i++ )
{
os-- ;
}
}
continue ;
default:
st = 0 ;
}
}
*os = NULL ;
}
int main( int argc, char **argv )
{
long td = 0L ;
struct tm tm ;
struct timeval tv = { 0, 0 };
int c = 0 ;
int sff = 0 ;
int dst_sav ;
char *td_sufx ;
char *in_sufx ;
char *tbufp ;
char *cp ;
char tbuf[65536];
char tbuf2[65536];
char tbuf3[65536];
if ( argc < 4 )
usage_exit();
if ( !strcmp( argv[1], "-c" ) )
{
if ( argc != 4 )
{
usage_exit();
}
c = 1 ;
in_sufx = "" ;
tbufp = "<system_clock>" ;
}
else
{
if ( argc != 5 )
{
usage_exit();
}
if ( !strcmp( argv[1], "-" ) )
{
tbufp = tbuf ;
}
else
{
tbufp = argv[1];
}
if ( !strcmp( argv[2], "%s" ) )
{
sff = 1 ;
}
else if ( !strcmp( argv[2], "%s.%F" ) )
{
sff = 2 ;
}
else if ( !strcmp( argv[2], "%s.%f" ) )
{
sff = 3 ;
}
}
td = strtol( argv[ 3 - c ], &td_sufx, 0 );
while ( *td_sufx
&& isspace( *td_sufx ) )
{
td_sufx++ ;
}
do
{
if ( c )
{
if ( gettimeofday( &tv, (void *)NULL ) )
{
perror( "gettimeofday()" );
exit( 1 );
}
localtime_r( &tv.tv_sec, &tm );
}
else
{
if ( tbufp == tbuf )
{
if ( !fgets( tbuf, sizeof( tbuf ), stdin ) )
{
if ( ferror( stdin ) )
{
perror( "stdin" );
exit( 1 );
}
exit( 0 );
}
}
if ( sff )
{
errno = 0 ;
tv.tv_sec = strtoul( tbufp, &in_sufx, 0 );
if ( errno )
{
fprintf( stderr,
"\nInput '%s' not format '%s'",
tbufp, argv[2] );
perror( "" );
continue ;
}
localtime_r( &tv.tv_sec, &tm );
if ( sff > 1 )
{
cp = in_sufx ;
errno = 0 ;
tv.tv_usec = strtoul( cp, &in_sufx, 0 );
if ( errno
|| *in_sufx != '.' )
{
fprintf( stderr,
"\nInput '%s' not format '%s'",
tbufp, argv[2] );
perror( "" );
continue ;
}
while ( ( in_sufx - cp ) < 6
&& sff > 2 )
{
tv.tv_usec *= 10 ;
cp-- ;
}
}
}
else
{
if ( !( in_sufx = strptime( tbufp,
argv[2], &tm ) ) )
{
fprintf( stderr,
"\nInput '%s' not format '%s'",
tbufp, argv[2] );
continue ;
}
if ( tm.tm_year < 69 )
{
tm.tm_year += 100 ;
}
if ( !tm.tm_mday )
{
tm.tm_mday = 1 ;
}
dst_sav = tm.tm_isdst ;
errno = 0 ;
tv.tv_sec = mktime( &tm );
if ( errno )
{
fprintf( stderr, "\n"
"Error converting date-time '%s' using format '%s': ",
tbufp, argv[ 2 ] );
perror( "" );
continue ;
}
if ( dst_sav < tm.tm_isdst )
{
tm.tm_hour-- ;
tv.tv_sec = mktime( &tm );
}
}
}
if ( td )
{
switch( *td_sufx )
{
case 'y':
case 'Y':
tm.tm_year += td ;
break ;
case 'm':
tm.tm_mon += td ;
break ;
case 'w':
case 'W':
tm.tm_mday += ( 7 * td );
break ;
case 'd':
case 'D':
tm.tm_mday += td ;
break ;
case 'h':
case 'H':
tm.tm_hour += td ;
break ;
case 'M':
tm.tm_min += td ;
break ;
case 's':
case 'S':
case NULL:
tm.tm_sec += td ;
break ;
default:
fprintf( stderr,
"\n"
"Fatal: Unknown delta unit %c in delta arg '%s'.\n",
*td_sufx, argv[ 3 - c ] );
exit( 1 );
}
errno = 0 ;
tv.tv_sec = mktime( &tm );
if ( errno )
{
fprintf( stderr, "\n"
"Error converting time + delta '%s' using ",
argv[ 3 - c ] );
perror( "mktime()" );
continue ;
}
} /* if td */
add_u_time( tbuf2, argv[ 4 - c ], &tv );
if ( !strftime( tbuf3, sizeof( tbuf3 ), tbuf2, &tm ) )
{
fprintf( stderr, "\n"
"String too long using format '%s' on time '%s'\n", tbuf2, tbufp );
continue ;
}
if ( 0 > printf( "%s%s%s", tbuf3, in_sufx,
( tbufp != tbuf ? "\n" : "" ) ) )
{
if ( ferror( stdout ) )
{
perror( "stdout" );
exit( 1 );
}
exit( 0 );
}
} while ( tbufp == tbuf ) ;
exit( 0 );
}
$