/****************************************************************************/
/* Convert unix binary time to RFC 1123 time format:
 *  	Wdy, DD Mon YY HH:MM:SS TIMEZONE
 *
 * If DATE_FORMAT_850 defined, use older format.
 *  	Weekday, DD-Mon-YY HH:MM:SS TIMEZONE
 *
 */
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <descrip.h>

extern int lib$emul(), lib$ediv(), lib$addx(), lib$subx(), sys$bintim();
static unsigned long base_time[2], zero_time[2];
static int base_initialized = 0;

static void init_base_time() {
    int status, tdiff, conv, offset;
    unsigned long diff_time[2];
    $DESCRIPTOR ( unix_zero, "1-JAN-1970 00:00:00.0" );
    char *tzone;
    /*
     * Convert unix base time to VMS format.
     */
    status = sys$bintim ( &unix_zero, zero_time );
    /*
     * Get offset from GMT. and adjust base time.
     */
    tzone = getenv ( "SYS$TIMEZONE_DIFFERENTIAL" );
    if ( tzone ) {
	tdiff = atoi ( tzone );
	conv = -10000000;
	offset = 0;
	lib$emul ( &tdiff, &conv, &offset, diff_time );
	lib$addx ( diff_time, zero_time, base_time );
    } else {
	base_time[0] = zero_time[0];
	base_time[1] = zero_time[1];
    }
    base_initialized = 1;
}
char *tf_format_time ( unsigned long bintim, char *buffer )
{
#ifdef DATE_FORMAT_850
    static char *weekday[] = {"Thursday", "Friday", "Saturday", "Sunday", 
		"Monday", "Tuesday", "Wednesday"};
#define MONTH_DELIMITER '-'
#else
    static char *weekday[] = {"Thu", "Fri", "Sat", "Sun", "Mon", "Tue", "Wed"};
#define MONTH_DELIMITER ' '
#endif
    static char *month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
    unsigned long offset, conv, quad_time[2], final_time[2];
    int status, i, wdy, mon, sys$numtim();
    unsigned short num_time[7];
    /*
     * Initialize constants used in conversion.
     */
    if ( !base_initialized ) init_base_time();
    /*
     * Convert Unix time (seconds since 1970) to VMS binary time, base
     * time is adjust to result time in UTC (GMT) timezone.
     */
    offset = 0;
    conv = 10000000;		/* 100 nanosecond ticks to seconds */
    lib$emul ( &bintim, &conv, &offset, quad_time );
    lib$addx ( quad_time, base_time, final_time );
    status = sys$numtim ( num_time, final_time );
    /*
     * Get day of week.
     */
    wdy = bintim / 86400;	/* day-number since 1970 */
    wdy = wdy % 7;		/* Day of week */
    for ( i = 0; weekday[wdy][i]; i++ ) buffer[i] = weekday[wdy][i];
    buffer[i++] = ','; buffer[i++] = ' ';
    /*
     * Add date;
     */
    buffer[i] = (char) (48+(num_time[2]/10)); /* Day of month */
    i++;					/* always include leading 0 */
    buffer[i++] = (char) (48+(num_time[2]%10));
    buffer[i++] = MONTH_DELIMITER;

    mon = num_time[1] - 1;		/* Month of year */
    buffer[i++] = month[mon][0]; buffer[i++] = month[mon][1];
    buffer[i++] = month[mon][2]; buffer[i++] = MONTH_DELIMITER;

#ifndef DATE_FORMAT_850
    buffer[i++] = (char) (48+(num_time[0]/1000));  /* high order digit */
    num_time[0] = num_time[0]%1000;		   /* Get rid of millenia */
    buffer[i++] = (char) (48+(num_time[0]/100));   /* century digit */
#endif
    num_time[0] = num_time[0]%100;		/* Get rid of centuries */
    buffer[i++] = (char) (48+(num_time[0]/10));   /* Show last 2 digits of year*/
    buffer[i++] = (char) (48+(num_time[0]%10));
    buffer[i++] = ' ';
    /*
     * add time.
     */
    buffer[i++] = (char) (48+(num_time[3]/10));	/* hours */
    buffer[i++] = (char) (48+(num_time[3]%10));
    buffer[i++] = ':';

    buffer[i++] = (char) (48+(num_time[4]/10)); /* minutes */
    buffer[i++] = (char) (48+(num_time[4]%10));
    buffer[i++] = ':';

    buffer[i++] = (char) (48+(num_time[5]/10)); /* seconds */
    buffer[i++] = (char) (48+(num_time[5]%10));
    buffer[i++] = ' '; 
    /*
     * add timezone.
     */
    buffer[i++] = 'G'; buffer[i++] = 'M'; buffer[i++] = 'T';
    buffer[i++] = '\0';

    return buffer;
}
/****************************************************************************/
/* Convert text time into binary time.  Input format may be 1 of following:
 *    sun,06nov199408:49:37GMT		; RFC 822 (1123)
 *    sunday,6-nov-9408:49:37GMT	; RFC 850 (1036)
 *    sunnov608:49:371994		; asctime() function.
 *
 * Return value is zero on format error.
 */
unsigned long tf_decode_time ( char *string )
{
    int i, comma, hyphen1, hyphen2, colon1, colon2, status;
    struct { long l; char *s; } dx;
    unsigned long vms_time[2], offset[2], seconds, remainder, conv;
    char asctim[32];
    /*
     * Scan string for landmarks.
     */
    comma = hyphen1 = hyphen2 = colon1 = colon2 = -1;
    for ( i = 0; string[i]; i++ ) {
	char c;
	c = string[i];
	if ( c == ',' ) comma = i;
	else if ( c == '-' ) {
	    if ( hyphen1 < 0 ) hyphen1 = i; else hyphen2 = i;
	} else if ( c == ':' ) {
	    if ( colon1 < 0 ) colon1 = i; else colon2 = i;
	}
    }
    /*
     * Do some sanity checks on landmarks.
     */
#ifdef DEBUG
    tlog_putlog(1,"h1: !SL, h2: !SL, c1: !SL, c2: !SL, comma: !SL!/", hyphen1,
    	hyphen2, colon1, colon2, comma );
#endif
    if ( (hyphen1 > hyphen2) || (colon1 > colon2) || (hyphen2 > colon1) ||
	(comma > colon1) || (colon1+3 != colon2) || (colon2+5 >= i) ) return 0;
    /*
     * Rearrange components into VMS absolute time string:
     *      "DD-MMM-YYYY HH:MM:SS"
     *       012345678901234567890
     */
    strcpy ( asctim, "01-JAN-1970 00:00:00.00" );
    asctim[19] = string[colon2+2];
    asctim[18] = string[colon2+1];
    asctim[16] = string[colon2-1];
    asctim[15] = string[colon2-2];
    asctim[13] = string[colon1-1];
    asctim[12] = string[colon1-2];		/* Finished HH::MM::SS */

    if ( comma >= 0 ) {
	asctim[10] = string[colon1-3];
	asctim[9] = string[colon1-4];
	if ( hyphen2 != colon1-5 ) {
	    /*
	     * No hyphens in time string, synthesize them.
	     */
	    asctim[8] = string[colon1-5];
	    asctim[7] = string[colon1-6];	/* 4 digit year */
	    hyphen2 = colon1 - 6;
	    hyphen1 = colon1 - 9;
	}
	asctim[5] = string[hyphen2-1];
	asctim[4] = string[hyphen2-2];
	asctim[3] = string[hyphen2-3];
	asctim[1] = string[hyphen1-1];
	asctim[0] = string[hyphen1-2];

    } else {	/* ctime() */
	asctim[1] = string[colon1-3];
	if ( (string[colon1-4] >= '0') && (string[colon1-4] <= '9') ) {
	    if ( colon1 < 7 ) return 0;
	    asctim[0] = string[colon1-4];
	    asctim[3] = string[colon1-7];
	    asctim[4] = string[colon1-6];
	    asctim[5] = string[colon1-5];
	} else {
	    if ( colon1 < 6 ) return 0;
	    asctim[3] = string[colon1-6];
	    asctim[4] = string[colon1-5];
	    asctim[5] = string[colon1-4];
	}
	asctim[10] = string[colon2+6];
	asctim[9] = string[colon2+5];
	asctim[8] = string[colon2+4];
	asctim[7] = string[colon2+3];
    }
    /*
     * Convert string to binary time.  Month must be in upper case.
     */
    dx.l = 23;
    dx.s = asctim;
    for ( i = 0; asctim[i]; i++ ) asctim[i] = _toupper(asctim[i]);
    status = sys$bintim ( &dx, vms_time );
    if ( (status&1) == 0 ) return 0;		/* Invalid time */
    /*
     * Convert to delta seconds since unix base time.
     */
    if ( !base_initialized ) init_base_time();
    lib$subx ( vms_time, base_time, offset );
    conv = 10000000;		/* 100 nanosecond ticks to seconds */
    lib$ediv ( &conv, &offset, &seconds, &remainder );
    return seconds;
}
