/** @file  tst_psTime_01.c
 *
 *  @brief Test driver for psTime functions
 *
 *  This test driver contains the following tests for psTime:
 *     1) Allocate psTime structure
 *     2) Get current time
 *     3) Get UT1 UTC delta
 *     4) Convert psTime to MJD
 *     5) Convert psTime to JD
 *     6) Convert psTime to ISO
 *     7) Convert psTime to timeval
 *     8) Create psTime from MJD
 *     9) Create psTime from JD
 *    10) Create psTime from ISO
 *    11) Create psTime from timeval
 *    12) Create psTime from TM
 *    13) Convert time between different types
 *
 *     O) Convert psTime time to LMST
 *
 *  @author  Ross Harman, MHPCC
 *  @author  Eric Van Alst, MHPCC
 *
 *  @version $Revision: 1.27 $  $Name: not supported by cvs2svn $
 *  @date  $Date: 2005-06-10 01:41:35 $
 *
 *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
 *
 */

#include "pslib_strict.h"
#include "psTest.h"
#include <string.h>

#define ERROR_TOL    0.0001

static psS32 testTimeAlloc(void);
static psS32 testTimeGetNow(void);
static psS32 testTimeGetUT1Delta(void);
static psS32 testTimeToMJD(void);
static psS32 testTimeToJD(void);
static psS32 testTimeToISO(void);
static psS32 testTimeToTimeval(void);
static psS32 testTimeFromMJD(void);
static psS32 testTimeFromJD(void);
static psS32 testTimeFromISO(void);
static psS32 testTimeFromTimeval(void);
static psS32 testTimeFromTM(void);
static psS32 testTimeConvert(void);

testDescription tests[] = {
                              {testTimeAlloc,000,"psTimeAlloc",0,false},
                              {testTimeGetNow,000,"psTimeGetNow",0,false},
                              {testTimeGetUT1Delta,000,"psTimeGetUT1Delta",0,false},
                              {testTimeToMJD,000,"psTimeToMJD",0,false},
                              {testTimeToJD,000,"psTimeToJD",0,false},
                              {testTimeToISO,000,"psTimeToISO",0,false},
                              {testTimeToTimeval,000,"psTimeToTimeval",0,false},
                              {testTimeFromMJD,000,"psTimeFromMJD",0,false},
                              {testTimeFromJD,000,"psTimeFromJD",0,false},
                              {testTimeFromISO,000,"psTimeFromISO",0,false},
                              {testTimeFromTimeval,000,"psTimeFromTimeval",0,false},
                              {testTimeFromTM,000,"p_psTimeFromTM",0,false},
                              {testTimeConvert,000,"psTimeConvert",0,false},
                              {NULL}
                          };

// Test Time 1 : July 21, 2004  18:22:24.3
//               MJD = 53207.765559
//               JD = 2453208.265559
// UTC Test Time 1
const psS64 testTime1SecondsUTC     = 1090434144;
const psU32 testTime1NanosecondsUTC = 272044000;
// TAI Test Time 1
const psS64 testTime1SecondsTAI     = 1090434176;
const psU32 testTime1NanosecondsTAI = 272044000;
const psF64 testTime1MJDTAI         = 53207.76592937;
const psF64 testTime1JDTAI          = 2453208.26592937;

// TT Test Time 1
const psS64 testTime1SecondsTT      = 1090434208;
const psU32 testTime1NanosecondsTT  = 456044000;
// Expected UT1-UTC IERS A & B
const psF64 testTime1UT1DeltaBullA  = -0.457233186;
const psF64 testTime1UT1DeltaBullB  = -0.457227;
// UT1 Test Time 1
const psS64 testTime1SecondsUT1     = 1090434143;
const psU32 testTime1NanosecondsUT1 = 814810814;
// Expected MJD & JD
const psF64 testTime1MJD            = 53207.765559;
const psF64 testTime1JD             = 2453208.265559;
// Expected ISO string
const char* testTime1Str     = "2004-07-21T18:22:24,2Z";
const char* testTime1StrLeap = "2004-07-21T18:22:60,2Z";
// Expected timeval values
const psS32 testTime1TimevalSec = 1090434144;
const psS32 testTime1TimevalUsec = 272044;

// Test Time 2 : Jan. 1, 1973 00:00:00.0000
//               MJD = 41683.0000
//               JD = 2441683.5000
const psS64 testTime2SecondsUTC     = 94694400;
const psU32 testTime2NanosecondsUTC = 0;

// Expected UT1-UTC IERS A & B
const psF64 testTime2UT1DeltaBullA  = 0.000000;
const psF64 testTime2UT1DeltaBullB  = 0.000000;

// Test Time 3 : Sept. 21, 2006 00:00:00.0000
//               MJD = 53999
//               JD = 2453999.5
const psS64 testTime3SecondsUTC     = 1158796800;
const psU32 testTime3NanosecondsUTC = 0;
// Expected UT1-UTC IERS A & B
const psF64 testTime3UT1DeltaBullA  = -0.63574;
const psF64 testTime3UT1DeltaBullB  = -0.63574;

// Test Time 4 : Jan. 1, 1969 00:00:00.0000
//               MJD = 40222
//               JD = 2440222.5
const psS64 testTime4SecondsUTC     = -31536000;
const psU32 testTime4NanosecondsUTC = 0;
// Expected MJD and JD
const psF64 testTime4MJD            = 40222.0;
const psF64 testTime4JD             = 2440222.5;

// Test Time 5 : Dec 31, 0001 BC 23:59:59
//               MJD = -1397755
//               JD = 1002245.4999
const psS64 testTime5SecondsUTC     = -62125920001;
const psU32 testTime5NanosecondsUTC  = 0;

// Test Time 6 : Jan. 1, 10000 AD 00:00:00
const psS64 testTime6SecondsUTC      = 253202544001;
const psU32 testTime6NanosecondsUTC  = 0;

// Test Time 7 : Jan. 1, 2004 00:00:00,0
const psS64 testTime7Seconds         = 1072915200;
const psU32 testTime7Nanoseconds     = 0;
const psS32 testTime7TmYear          = 104;
const psS32 testTime7TmMon           = 0;
const psS32 testTime7TmDay           = 1;
const psS32 testTime7TmHour          = 0;
const psS32 testTime7TmMin           = 0;
const psS32 testTime7TmSec           = 0;

// Test Time 8 : Dec. 31, 2003 00:00:00,0
const psS64 testTime8Seconds         = 1072828800;
const psU32 testTime8Nanoseconds     = 0;
const psS32 testTime8TmYear          = 103;
const psS32 testTime8TmMon           = 11;
const psS32 testTime8TmDay           = 31;
const psS32 testTime8TmHour          = 0;
const psS32 testTime8TmMin           = 0;
const psS32 testTime8TmSec           = 0;

psS32 main(psS32 argc, char* argv[])
{
    psLogSetLevel(PS_LOG_INFO);

    // Initialize library internal structures
    psLibInit("psTime.config");

    if( !runTestSuite(stderr,"psTime",tests,argc,argv)) {
        return 1;
    }

    // Cleanup library
    psLibFinalize();

    return 0;
}

psS32 testTimeAlloc(void)
{
    psTime*  time = NULL;

    // Allocate new psTime with valid time type
    time = psTimeAlloc(PS_TIME_TAI);
    if(time == NULL) {
        psError(PS_ERR_UNKNOWN,true,"Return NULL when psTime object ptr expected");
        return 1;
    }
    // Verify members set properly
    if(time->type != PS_TIME_TAI) {
        psError(PS_ERR_UNKNOWN,true,"Member type = %d not as expected %d",time->type,PS_TIME_TAI);
        return 2;
    }
    if((time->sec != 0) && (time->nsec != 0) && (time->leapsecond != false)) {
        psError(PS_ERR_UNKNOWN,true,"Members not set appropriately");
        return 3;
    }
    psFree(time);

    // Allocate new psTime with invalid time type
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
    time = psTimeAlloc(-100);
    if(time != NULL) {
        psError(PS_ERR_UNKNOWN,true,"Did not return NULL for invalid time type");
        return 4;
    }

    return 0;
}
psS32 testTimeGetNow(void)
{
    psTime*  timeNow = NULL;

    // Get current time and verify return is psTime structure
    timeNow = psTimeGetNow(PS_TIME_TAI);
    if(timeNow == NULL) {
        psError(PS_ERR_UNKNOWN,true,"Did not return valid psTime struct");
        return 1;
    }
    if(timeNow->type != PS_TIME_TAI) {
        psError(PS_ERR_UNKNOWN,true,"Time type %d not as expected %d",
                timeNow->type, PS_TIME_TAI);
        return 2;
    }
    psFree(timeNow);

    // Attempt to get time with invalid type
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid time type");
    timeNow = psTimeGetNow(-100);
    if(timeNow != NULL) {
        psError(PS_ERR_UNKNOWN,true,"Did not return NULL for invalid time type");
        return 3;
    }

    return 0;
}

psS32 testTimeGetUT1Delta(void)
{
    psTime*    time      = NULL;
    psF64      ut1Delta  = 0.0;

    // Attempt to convert NULL time
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL time");
    ut1Delta = psTimeGetUT1Delta(time,PS_IERS_B);
    if( !isnan(ut1Delta)) {
        psError(PS_ERR_UNKNOWN,true,"Did not return NAN for NULL psTime");
        return 1;
    }

    // Attempt to convert invalid time
    time = psTimeAlloc(PS_TIME_TAI);
    time->sec = 1;
    time->nsec = 2e9;
    time->leapsecond = false;
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid time");
    ut1Delta = psTimeGetUT1Delta(time,PS_IERS_B);
    if( !isnan(ut1Delta)) {
        psError(PS_ERR_UNKNOWN,true,"Did not return NAN for invalid time");
        return 2;
    }

    // Attempt to convert time with invalid bulletin
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid bulletin");
    time->nsec = 2;
    ut1Delta = psTimeGetUT1Delta(time,-100);
    if( !isnan(ut1Delta)) {
        psError(PS_ERR_UNKNOWN,true,"Did not return NAN for invalid bulletin");
        return 3;
    }

    // Attempt to get delta with valid time and bulletin A
    time->sec  = testTime1SecondsUTC;
    time->nsec = testTime1NanosecondsUTC;
    time->type = PS_TIME_UTC;
    ut1Delta = psTimeGetUT1Delta(time,PS_IERS_A);
    if(fabs(ut1Delta - testTime1UT1DeltaBullA) > ERROR_TOL) {
        psError(PS_ERR_UNKNOWN,true,"UT1 Delta %lf not as expected %lf test time 1 bulletin A",
                ut1Delta,testTime1UT1DeltaBullA);
        return 4;
    }

    // Attempt to get delta with valid time and bulletin B
    ut1Delta = psTimeGetUT1Delta(time,PS_IERS_B);
    if(fabs(ut1Delta - testTime1UT1DeltaBullB) > ERROR_TOL) {
        psError(PS_ERR_UNKNOWN,true,"UT1 Delta %lf not as expected %lf test time 1 bulletin B",
                ut1Delta,testTime1UT1DeltaBullB);
        return 5;
    }

    // Attempt to get delta with valid time and bulletin A
    time->sec  = testTime2SecondsUTC;
    time->nsec = testTime2NanosecondsUTC;
    time->type = PS_TIME_UTC;
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate a warning message predating table");
    ut1Delta = psTimeGetUT1Delta(time,PS_IERS_A);
    if(fabs(ut1Delta - testTime2UT1DeltaBullA) > ERROR_TOL) {
        psError(PS_ERR_UNKNOWN,true,"UT1 Delta %lf not as expected %lf test time 2 bulletin A",
                ut1Delta,testTime2UT1DeltaBullA);
        return 6;
    }

    // Attempt to get delta with valid time and bulletin B
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate a warning message predating table");
    ut1Delta = psTimeGetUT1Delta(time,PS_IERS_B);
    if(fabs(ut1Delta - testTime2UT1DeltaBullB) > ERROR_TOL) {
        psError(PS_ERR_UNKNOWN,true,"UT1 Delta %lf not as expected %lf test time 2 bulletin B",
                ut1Delta,testTime2UT1DeltaBullB);
        return 7;
    }

    // Attempt to get delta with valid time and bulletin A
    time->sec  = testTime3SecondsUTC;
    time->nsec = testTime3NanosecondsUTC;
    time->type = PS_TIME_UTC;
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate a warning message postdating table");
    ut1Delta = psTimeGetUT1Delta(time,PS_IERS_A);
    if(fabs(ut1Delta - testTime3UT1DeltaBullA) > ERROR_TOL) {
        psError(PS_ERR_UNKNOWN,true,"UT1 Delta %lf not as expected %lf test time 3 bulletin A",
                ut1Delta,testTime3UT1DeltaBullA);
        return 8;
    }

    // Attempt to get delta with valid time and bulletin B
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate a warning message postdating table");
    ut1Delta = psTimeGetUT1Delta(time,PS_IERS_B);
    if(fabs(ut1Delta - testTime3UT1DeltaBullB) > ERROR_TOL) {
        psError(PS_ERR_UNKNOWN,true,"UT1 Delta %lf not as expected %lf test time 3 bulletin B",
                ut1Delta,testTime3UT1DeltaBullB);
        return 9;
    }

    psFree(time);

    return 0;
}

psS32 testTimeToMJD(void)
{
    psTime*  time = NULL;
    psF64    mjd  = 0.0;

    // Attempt to convert with time NULL
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL time");
    mjd = psTimeToMJD(NULL);
    if(!isnan(mjd)) {
        psError(PS_ERR_UNKNOWN,true,"Expected NaN for NULL time");
        return 1;
    }

    // Attempt to convert invalid time
    time = psTimeAlloc(PS_TIME_UTC);
    time->sec = 1;
    time->nsec = 2e9;
    time->leapsecond = false;
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid time");
    mjd = psTimeToMJD(time);
    if( !isnan(mjd)) {
        psError(PS_ERR_UNKNOWN,true,"Did not return NAN for invalid time");
        return 2;
    }

    // Check valid time conversion to MJD after 1/1/1970 epoch
    time->sec = testTime1SecondsUTC;
    time->nsec = testTime1NanosecondsUTC;
    mjd = psTimeToMJD(time);
    if(fabs(mjd - testTime1MJD) > ERROR_TOL) {
        psError(PS_ERR_UNKNOWN,true,"Expected MJD = %lf not as expected %lf",
                mjd, testTime1MJD);
        return 3;
    }

    // Check valid time conversion to MJD before 1/1/1970 epoch
    time->sec = testTime4SecondsUTC;
    time->nsec = testTime4NanosecondsUTC;
    mjd = psTimeToMJD(time);
    if(fabs(mjd - testTime4MJD) > ERROR_TOL) {
        psError(PS_ERR_UNKNOWN,true,"Expected MJD = %lf not as expected %lf",
                mjd, testTime4MJD);
        return 4;
    }

    psFree(time);

    return 0;
}

psS32 testTimeToJD(void)
{
    psTime*  time = NULL;
    psF64    jd  = 0.0;

    // Attempt to convert with time NULL
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL time");
    jd = psTimeToJD(NULL);
    if(!isnan(jd)) {
        psError(PS_ERR_UNKNOWN,true,"Expected NaN for NULL time");
        return 1;
    }

    // Attempt to convert invalid time
    time = psTimeAlloc(PS_TIME_UTC);
    time->sec = 1;
    time->nsec = 2e9;
    time->leapsecond = false;
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid time");
    jd = psTimeToJD(time);
    if( !isnan(jd)) {
        psError(PS_ERR_UNKNOWN,true,"Did not return NAN for invalid time");
        return 2;
    }

    // Check valid time conversion to MJD after 1/1/1970 epoch
    time->sec = testTime1SecondsUTC;
    time->nsec = testTime1NanosecondsUTC;
    jd = psTimeToJD(time);
    if(fabs(jd - testTime1JD) > ERROR_TOL) {
        psError(PS_ERR_UNKNOWN,true,"Expected JD = %lf not as expected %lf",
                jd, testTime1JD);
        return 3;
    }

    // Check valid time conversion to MJD before 1/1/1970 epoch
    time->sec = testTime4SecondsUTC;
    time->nsec = testTime4NanosecondsUTC;
    jd = psTimeToJD(time);
    if(fabs(jd - testTime4JD) > ERROR_TOL) {
        psError(PS_ERR_UNKNOWN,true,"Expected JD = %lf not as expected %lf",
                jd, testTime4JD);
        return 4;
    }

    psFree(time);

    return 0;
}

psS32 testTimeToISO(void)
{
    psTime*    time    = NULL;
    char*      timeStr = NULL;

    // Attempt to convert with NULL time
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for NULL time");
    timeStr = psTimeToISO(time);
    if(timeStr != NULL) {
        psError(PS_ERR_UNKNOWN,true,"Did not return NULL as expected");
        return 1;
    }

    // Attempt to convert invalid time
    time = psTimeAlloc(PS_TIME_UTC);
    time->sec = 1;
    time->nsec = 2e9;
    time->leapsecond = false;
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid time");
    timeStr = psTimeToISO(time);
    if( timeStr != NULL) {
        psError(PS_ERR_UNKNOWN,true,"Did not return NULL for invalid time");
        return 2;
    }

    // Verify return NULL for time prior to year 0000
    time->sec = testTime5SecondsUTC;
    time->nsec = testTime5NanosecondsUTC;
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for time prior year 0000");
    timeStr = psTimeToISO(time);
    if(timeStr != NULL) {
        psError(PS_ERR_UNKNOWN,true,"Did not return NULL for time prior to year 0000");
        return 3;
    }

    // Verify return NULL for time after to year 9999
    time->sec = testTime6SecondsUTC;
    time->nsec = testTime6NanosecondsUTC;
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for time after year 9999");
    timeStr = psTimeToISO(time);
    if(timeStr != NULL) {
        psError(PS_ERR_UNKNOWN,true,"Did not return NULL for time after to year 9999");
        return 4;
    }

    // Verify return string with valid time
    time->sec = testTime1SecondsUTC;
    time->nsec = testTime1NanosecondsUTC;
    timeStr = psTimeToISO(time);
    if(strcmp(timeStr,testTime1Str) != 0) {
        psError(PS_ERR_UNKNOWN,true,"String %s not as expected %s",
                timeStr, testTime1Str);
        return 5;
    }
    psFree(timeStr);

    // Verify return string with valid time and leap second set
    time->sec = testTime1SecondsUTC;
    time->nsec = testTime1NanosecondsUTC;
    time->leapsecond = true;
    timeStr = psTimeToISO(time);
    if(strcmp(timeStr,testTime1StrLeap) != 0) {
        psError(PS_ERR_UNKNOWN,true,"String %s not as expected %s",
                timeStr, testTime1StrLeap);
        return 6;
    }
    psFree(timeStr);
    psFree(time);

    return 0;
}

psS32 testTimeToTimeval(void)
{
    psTime*           time         = NULL;
    struct timeval*   timevalTime  = NULL;

    // Attempt to convert with NULL time
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for NULL time");
    timevalTime = psTimeToTimeval(time);
    if(timevalTime != NULL) {
        psError(PS_ERR_UNKNOWN,true,"Did not return NULL as expected");
        return 1;
    }

    // Attempt to convert invalid time
    time = psTimeAlloc(PS_TIME_UTC);
    time->sec = 1;
    time->nsec = 2e9;
    time->leapsecond = false;
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid time");
    timevalTime = psTimeToTimeval(time);
    if( timevalTime != NULL) {
        psError(PS_ERR_UNKNOWN,true,"Did not return NULL for invalid time");
        return 2;
    }

    // Attempt to convert invalid time
    time->sec = -1;
    time->nsec = 0;
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid time");
    timevalTime = psTimeToTimeval(time);
    if( timevalTime != NULL) {
        psError(PS_ERR_UNKNOWN,true,"Did not return NULL for invalid time");
        return 2;
    }

    // Verify convert to timeval with valid time
    time->sec = testTime1SecondsUTC;
    time->nsec = testTime1NanosecondsUTC;
    timevalTime = psTimeToTimeval(time);
    if(timevalTime->tv_sec != testTime1TimevalSec) {
        psError(PS_ERR_UNKNOWN,true,"Timeval seconds %ld not as expectd %ld",
                timevalTime->tv_sec,testTime1TimevalSec);
        return 4;
    }
    if(timevalTime->tv_usec != testTime1TimevalUsec) {
        psError(PS_ERR_UNKNOWN,true,"Timeval useconds %ld not as expected %ld",
                timevalTime->tv_usec,testTime1TimevalUsec);
        return 5;
    }

    psFree(timevalTime);
    psFree(time);

    return 0;
}

psS32 testTimeFromMJD(void)
{
    psTime*   time = NULL;

    // Attempt to convert valid time to psTime
    time = psTimeFromMJD(testTime1MJDTAI);
    if(time->type != PS_TIME_TAI) {
        psError(PS_ERR_UNKNOWN,true,"Type %d not as expected %d",
                time->type,PS_TIME_TAI);
        return 1;
    }
    if(time->sec != testTime1SecondsTAI) {
        psError(PS_ERR_UNKNOWN,true,"psTime seconds %ld not as expected %ld",
                (long int)time->sec,testTime1SecondsTAI);
        return 2;
    }
    psFree(time);

    // Attempt to convert valid time before 1970 epoch
    time = psTimeFromMJD(testTime4MJD);
    if(time->type != PS_TIME_TAI) {
        psError(PS_ERR_UNKNOWN,true,"Type %d not as expected %d",
                time->type,PS_TIME_TAI);
        return 3;
    }
    if(time->sec != testTime4SecondsUTC) {
        psError(PS_ERR_UNKNOWN,true,"psTime seconds %ld not as expected %ld",
                (long int)time->sec,testTime4SecondsUTC);
        return 4;
    }
    psFree(time);

    return 0;
}

psS32 testTimeFromJD(void)
{
    psTime*   time = NULL;

    // Attempt to convert valid time to psTime
    time = psTimeFromJD(testTime1JDTAI);
    if(time->type != PS_TIME_TAI) {
        psError(PS_ERR_UNKNOWN,true,"Type %d not as expected %d",
                time->type,PS_TIME_TAI);
        return 1;
    }
    if(time->sec != testTime1SecondsTAI) {
        psError(PS_ERR_UNKNOWN,true,"psTime seconds %ld not as expected %ld",
                (long int)time->sec,(long int)testTime1SecondsTAI);
        return 2;
    }
    psFree(time);

    // Attempt to convert valid time before 1970 epoch
    time = psTimeFromJD(testTime4JD);
    if(time->type != PS_TIME_TAI) {
        psError(PS_ERR_UNKNOWN,true,"Type %d not as expected %d",
                time->type,PS_TIME_TAI);
        return 3;
    }
    if(time->sec != testTime4SecondsUTC) {
        psError(PS_ERR_UNKNOWN,true,"psTime seconds %ld not as expected %ld",
                (long int)time->sec,testTime4SecondsUTC);
        return 4;
    }
    psFree(time);

    return 0;
}

psS32 testTimeFromISO(void)
{
    psTime*   time = NULL;

    // Attempt to convert NULL string
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for NULL ISO string");
    time = psTimeFromISO(NULL);
    if(time != NULL) {
        psError(PS_ERR_UNKNOWN,true,"Did not return NULL as expected for NULL ISO string");
        return 1;
    }

    // Convert valid ISO string
    time = psTimeFromISO(testTime1Str);
    if(time->type != PS_TIME_TAI) {
        psError(PS_ERR_UNKNOWN,true,"Type %d not as expected %d",
                time->type, PS_TIME_TAI);
        return 2;
    }
    if(time->sec != testTime1SecondsUTC) {
        psError(PS_ERR_UNKNOWN,true,"psTime seconds %ld not as expected %ld",
                (long int)time->sec,testTime1SecondsTAI);
        return 3;
    }
    psFree(time);

    // Attempt to convert invalid ISO string
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error for invalid ISO string");
    time = psTimeFromISO("Here I am");
    if(time != NULL) {
        psError(PS_ERR_UNKNOWN,true,"Did not return NULL as expected for invalid ISO string");
        return 4;
    }
    psFree(time);

    return 0;
}

psS32 testTimeFromTimeval(void)
{
    psTime*          time        = NULL;
    struct timeval*  timevalTime = NULL;

    // Attempt to create psTime from NULL timeval ptr
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for NULL timeval");
    time = psTimeFromTimeval(NULL);
    if(time != NULL) {
        psError(PS_ERR_UNKNOWN,true,"Did not return NULL for NULL timeval ptr");
        return 1;
    }

    // Convert valid timeval structure
    timevalTime = (struct timeval*)psAlloc(sizeof(struct timeval));
    timevalTime->tv_sec = testTime1SecondsTAI;
    timevalTime->tv_usec = testTime1NanosecondsTAI / 1000;
    time = psTimeFromTimeval(timevalTime);
    if(time == NULL) {
        psError(PS_ERR_UNKNOWN,true,"Did not expect NULL return value for valid timeval");
        return 2;
    }
    if(time->type != PS_TIME_TAI) {
        psError(PS_ERR_UNKNOWN,true,"psTime type %d not as expected %d",
                time->type, PS_TIME_TAI);
        return 3;
    }
    if(time->sec != testTime1SecondsTAI) {
        psError(PS_ERR_UNKNOWN,true,"psTime seconds %ld not as expected %ld",
                time->sec, testTime1SecondsTAI);
        return 4;
    }
    if(time->nsec != testTime1NanosecondsTAI) {
        psError(PS_ERR_UNKNOWN,true,"psTime nanosec %ld not as expected %ld",
                time->nsec, testTime1NanosecondsTAI);
        return 5;
    }
    psFree(timevalTime);
    psFree(time);

    return 0;
}

psS32 testTimeFromTM(void)
{
    psTime*     time   = NULL;
    struct tm*  tmTime = NULL;

    // Attempt to convert from NULL tm structure ptr
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message for NULL tm ptr");
    time = p_psTimeFromTM(tmTime);
    if(time != NULL) {
        psError(PS_ERR_UNKNOWN,true,"Did not return NULL for NULL tm ptr");
        return 1;
    }

    // Verify convert for valid tm structure
    tmTime = (struct tm*)psAlloc(sizeof(struct tm));
    tmTime->tm_year = testTime7TmYear;
    tmTime->tm_mon  = testTime7TmMon;
    tmTime->tm_mday = testTime7TmDay;
    tmTime->tm_hour = testTime7TmHour;
    tmTime->tm_min  = testTime7TmMin;
    tmTime->tm_sec  = testTime7TmSec;
    time = p_psTimeFromTM(tmTime);
    if(time == NULL) {
        psError(PS_ERR_UNKNOWN,true,"Did not expect NULL return value for valid tm structure");
        return 2;
    }
    if(time->sec != testTime7Seconds) {
        psError(PS_ERR_UNKNOWN,true,"psTime seconds %ld not as expected %ld",
                (long int)time->sec, (long int)testTime7Seconds);
        return 3;
    }
    if(time->nsec != testTime7Nanoseconds) {
        psError(PS_ERR_UNKNOWN,true,"psTime nanoseconds %ld not as expected %ld",
                time->nsec, testTime7Nanoseconds);
        return 4;
    }
    psFree(tmTime);
    psFree(time);

    // Verify convert for valid tm structure
    tmTime = (struct tm*)psAlloc(sizeof(struct tm));
    tmTime->tm_year = testTime8TmYear;
    tmTime->tm_mon  = testTime8TmMon;
    tmTime->tm_mday = testTime8TmDay;
    tmTime->tm_hour = testTime8TmHour;
    tmTime->tm_min  = testTime8TmMin;
    tmTime->tm_sec  = testTime8TmSec;
    time = p_psTimeFromTM(tmTime);
    if(time == NULL) {
        psError(PS_ERR_UNKNOWN,true,"Did not expect NULL return value for valid tm structure");
        return 2;
    }
    if(time->sec != testTime8Seconds) {
        psError(PS_ERR_UNKNOWN,true,"psTime seconds %ld not as expected %ld",
                (long int)time->sec, (long int)testTime8Seconds);
        return 3;
    }
    if(time->nsec != testTime8Nanoseconds) {
        psError(PS_ERR_UNKNOWN,true,"psTime nanoseconds %ld not as expected %ld",
                time->nsec, testTime8Nanoseconds);
        return 4;
    }
    psFree(tmTime);
    psFree(time);

    return 0;
}

psS32 testTimeConvert(void)
{
    psTime*  time1 = NULL;
    psTime*  time2 = NULL;

    // Attempt to convert NULL time
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL time");
    time2 = psTimeConvert(time1,PS_TIME_TAI);
    // Verify return value is NULL
    if(time2 != NULL) {
        psError(PS_ERR_UNKNOWN,true,"Did not return NULL for NULL time specified");
        return 1;
    }

    // Attempt to convert to invalid type
    time1 = psTimeAlloc(PS_TIME_TAI);
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid type");
    time2 = psTimeConvert(time1,-100);
    // Verify return value is NULL
    if(time2 != time1) {
        psError(PS_ERR_UNKNOWN,true,"Did not return time for invalid type to convert to");
        return 2;
    }
    time1->type = PS_TIME_UTC;
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid type");
    time2 = psTimeConvert(time1,-100);
    // Verify return value is NULL
    if(time2 != time1) {
        psError(PS_ERR_UNKNOWN,true,"Did not return time for invalid type to convert to");
        return 2;
    }
    time1->type = PS_TIME_TT;
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid type");
    time2 = psTimeConvert(time1,-100);
    // Verify return value is NULL
    if(time2 != time1) {
        psError(PS_ERR_UNKNOWN,true,"Did not return time for invalid type to convert to");
        return 2;
    }
    time1->type = -100;
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid type");
    time2 = psTimeConvert(time1,PS_TIME_TAI);
    // Verify return value is NULL
    if(time2 != time1) {
        psError(PS_ERR_UNKNOWN,true,"Did not return time for invalid type to convert to");
        return 2;
    }

    // Attempt to convert with invalid time nsec > 1e9
    time1->nsec = 2e9;
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid time");
    time2 = psTimeConvert(time1,PS_TIME_TAI);
    if(time2 != time1) {
        psError(PS_ERR_UNKNOWN,true,"Did not return time for invalid time specified");
        return 3;
    }

    //Attempt to convert a time to the same type
    time2 = NULL;
    time1->sec = 1;
    time1->nsec = 2;
    time1->type = PS_TIME_TAI;
    time1->leapsecond = false;
    time2 = psTimeConvert(time1,PS_TIME_TAI);
    if(time2 != time1) {
        psError(PS_ERR_UNKNOWN,true,"Did not return time for conversion to same type");
        return 4;
    }
    // Verify time not changed
    if((time2->sec != 1) || (time2->nsec != 2) || (time2->type != PS_TIME_TAI) ||
            (time2->leapsecond != false) ) {
        psError(PS_ERR_UNKNOWN,true,"Time member changes when no change expected");
        return 5;
    }

    // Attempt to convert a UTC time to a TAI
    time2 = NULL;
    time1->sec = testTime1SecondsUTC;
    time1->nsec = testTime1NanosecondsUTC;
    time1->type = PS_TIME_UTC;
    time1->leapsecond = false;
    time2 = psTimeConvert(time1,PS_TIME_TAI);
    if(time2 != time1) {
        psError(PS_ERR_UNKNOWN,true,"Did not return time for conversion from UTC to TAI");
        return 6;
    }
    // Verify time type
    if(time2->type != PS_TIME_TAI) {
        psError(PS_ERR_UNKNOWN,true,"Returned time type %d not as expected %d",
                time2->type, PS_TIME_TAI);
        return 7;
    }
    // Verify time is TAI as expected
    if(time2->sec != testTime1SecondsTAI) {
        psError(PS_ERR_UNKNOWN,true,"TAI seconds = %ld not as expected %ld",(long int)time2->sec,
                (long int)testTime1SecondsTAI);
        return 7;
    }
    if(time2->nsec != testTime1NanosecondsTAI) {
        psError(PS_ERR_UNKNOWN,true,"TAI nanoseconds = %ld not as expected %ld",(long int)time2->nsec,
                (long int)testTime1NanosecondsTAI);
        return 8;
    }

    // Attempt to convert a UTC time to a TT
    time2 = NULL;
    time1->sec = testTime1SecondsUTC;
    time1->nsec = testTime1NanosecondsUTC;
    time1->type = PS_TIME_UTC;
    time1->leapsecond = false;
    time2 = psTimeConvert(time1,PS_TIME_TT);
    if(time2 != time1) {
        psError(PS_ERR_UNKNOWN,true,"Did not return time for conversion from UTC to TT");
        return 6;
    }
    // Verify time type
    if(time2->type != PS_TIME_TT) {
        psError(PS_ERR_UNKNOWN,true,"Returned time type %d not as expected %d",
                time2->type, PS_TIME_TT);
        return 7;
    }
    // Verify time is TT as expected
    if(time2->sec != testTime1SecondsTT) {
        psError(PS_ERR_UNKNOWN,true,"TT seconds = %ld not as expected %ld",(long int)time2->sec,
                (long int)testTime1SecondsTT);
        return 7;
    }
    if(time2->nsec != testTime1NanosecondsTT) {
        psError(PS_ERR_UNKNOWN,true,"TT nanoseconds = %ld not as expected %ld",(long int)time2->nsec,
                (long int)testTime1NanosecondsTT);
        return 8;
    }

    // Attempt to convert a UTC time to UT1
    time2 = NULL;
    time1->sec = testTime1SecondsUTC;
    time1->nsec = testTime1NanosecondsUTC;
    time1->type = PS_TIME_UTC;
    time1->leapsecond = false;
    time2 = psTimeConvert(time1,PS_TIME_UT1);
    if(time1 != time2) {
        psError(PS_ERR_UNKNOWN,true,"Did not return time for conversion from UTC to UT1");
        return 9;
    }
    // Verify time type
    if(time2->type != PS_TIME_UT1) {
        psError(PS_ERR_UNKNOWN,true,"Returned time type %d not as expected %d",
                time2->type, PS_TIME_UT1);
        return 7;
    }
    // Verify time is UT1 as expected
    if(time2->sec != testTime1SecondsUT1) {
        psError(PS_ERR_UNKNOWN,true,"UT1 seconds = %ld not as expected %ld",(long int)time2->sec,
                (long int)testTime1SecondsUT1);
        return 9;
    }
    if(time2->nsec != testTime1NanosecondsUT1) {
        psError(PS_ERR_UNKNOWN,true,"UT1 nanoseconds = %d not as expected %d",time2->nsec,
                testTime1NanosecondsUT1);
        return 10;
    }

    // Attempt to convert a TAI time to UTC
    time2 = NULL;
    time1->sec = testTime1SecondsTAI;
    time1->nsec = testTime1NanosecondsTAI;
    time1->type = PS_TIME_TAI;
    time1->leapsecond = false;
    time2 = psTimeConvert(time1,PS_TIME_UTC);
    if(time2 != time1) {
        psError(PS_ERR_UNKNOWN,true,"Did not return time for conversion from TAI to UTC");
        return 11;
    }
    // Verify time type
    if(time2->type != PS_TIME_UTC) {
        psError(PS_ERR_UNKNOWN,true,"Returned time type %d not as expected %d",
                time2->type, PS_TIME_UTC);
        return 7;
    }
    // Verify time is UTC as expected
    if(time2->sec != testTime1SecondsUTC) {
        psError(PS_ERR_UNKNOWN,true,"UTC seconds = %ld not as expected %ld",(long int)time2->sec,
                (long int)testTime1SecondsUTC);
        return 12;
    }
    if(time2->nsec != testTime1NanosecondsUTC) {
        psError(PS_ERR_UNKNOWN,true,"UTC nanoseconds = %ld not as expected %ld",(long int)time2->nsec,
                (long int)testTime1NanosecondsUTC);
        return 13;
    }

    // Attempt to convert a TAI time to TT
    time2 = NULL;
    time1->sec = testTime1SecondsTAI;
    time1->nsec = testTime1NanosecondsTAI;
    time1->type = PS_TIME_TAI;
    time1->leapsecond = false;
    time2 = psTimeConvert(time1,PS_TIME_TT);
    if(time2 != time1) {
        psError(PS_ERR_UNKNOWN,true,"Did not return time for conversion from TAI to TT");
        return 14;
    }
    // Verify time type
    if(time2->type != PS_TIME_TT) {
        psError(PS_ERR_UNKNOWN,true,"Returned time type %d not as expected %d",
                time2->type, PS_TIME_TT);
        return 7;
    }
    // Verify time is TT as expected
    if(time2->sec != testTime1SecondsTT) {
        psError(PS_ERR_UNKNOWN,true,"TT seconds = %ld not as expected %ld",(long int)time2->sec,
                (long int)testTime1SecondsTT);
        return 15;
    }
    if(time2->nsec != testTime1NanosecondsTT) {
        psError(PS_ERR_UNKNOWN,true,"TT nanoseconds = %ld not as expected %ld",(long int)time2->nsec,
                (long int)testTime1NanosecondsTT);
        return 16;
    }

    // Attempt to convert a TAI time to UT1
    time2 = NULL;
    time1->sec = testTime1SecondsTAI;
    time1->nsec = testTime1NanosecondsTAI;
    time1->type = PS_TIME_TAI;
    time1->leapsecond = false;
    time2 = psTimeConvert(time1,PS_TIME_UT1);
    if(time1 != time2) {
        psError(PS_ERR_UNKNOWN,true,"Did not return time for conversion from TAI to UT1");
        return 9;
    }
    // Verify time type
    if(time2->type != PS_TIME_UT1) {
        psError(PS_ERR_UNKNOWN,true,"Returned time type %d not as expected %d",
                time2->type, PS_TIME_UT1);
        return 7;
    }
    // Verify time is UT1 as expected
    if(time2->sec != testTime1SecondsUT1) {
        psError(PS_ERR_UNKNOWN,true,"UT1 seconds = %ld not as expected %ld",(long int)time2->sec,
                (long int)testTime1SecondsUT1);
        return 9;
    }
    if(time2->nsec != testTime1NanosecondsUT1) {
        psError(PS_ERR_UNKNOWN,true,"UT1 nanoseconds = %d not as expected %d",time2->nsec,
                testTime1NanosecondsUT1);
        return 10;
    }

    // Attempt to convert a TT time to UTC
    time2 = NULL;
    time1->sec = testTime1SecondsTT;
    time1->nsec = testTime1NanosecondsTT;
    time1->type = PS_TIME_TT;
    time1->leapsecond = false;
    time2 = psTimeConvert(time1,PS_TIME_UTC);
    if(time2 != time1) {
        psError(PS_ERR_UNKNOWN,true,"Did not return time for conversion from TT to UTC");
        return 17;
    }
    // Verify time type
    if(time2->type != PS_TIME_UTC) {
        psError(PS_ERR_UNKNOWN,true,"Returned time type %d not as expected %d",
                time2->type, PS_TIME_UTC);
        return 7;
    }
    // Verify time is UTC as expected
    if(time2->sec != testTime1SecondsUTC) {
        psError(PS_ERR_UNKNOWN,true,"UTC seconds = %ld not as expected %ld",(long int)time2->sec,
                (long int)testTime1SecondsUTC);
        return 18;
    }
    if(time2->nsec != testTime1NanosecondsUTC) {
        psError(PS_ERR_UNKNOWN,true,"UTC nanoseconds = %ld not as expected %ld",(long int)time2->nsec,
                (long int)testTime1NanosecondsUTC);
        return 19;
    }

    // Attempt to convert a TT time to TAI
    time2 = NULL;
    time1->sec = testTime1SecondsTT;
    time1->nsec = testTime1NanosecondsTT;
    time1->type = PS_TIME_TT;
    time1->leapsecond = false;
    time2 = psTimeConvert(time1,PS_TIME_TAI);
    if(time2 != time1) {
        psError(PS_ERR_UNKNOWN,true,"Did not return time for conversion from TT to TAI");
        return 20;
    }
    // Verify time type
    if(time2->type != PS_TIME_TAI) {
        psError(PS_ERR_UNKNOWN,true,"Returned time type %d not as expected %d",
                time2->type, PS_TIME_TAI);
        return 7;
    }
    // Verify time is TAI as expected
    if(time2->sec != testTime1SecondsTAI) {
        psError(PS_ERR_UNKNOWN,true,"TAI seconds = %ld not as expected %ld",(long int)time2->sec,
                (long int)testTime1SecondsTAI);
        return 21;
    }
    if(time2->nsec != testTime1NanosecondsTAI) {
        psError(PS_ERR_UNKNOWN,true,"TT nanoseconds = %ld not as expected %ld",(long int)time2->nsec,
                (long int)testTime1NanosecondsTAI);
        return 22;
    }

    // Attempt to convert a TT time to UT1
    time2 = NULL;
    time1->sec = testTime1SecondsTT;
    time1->nsec = testTime1NanosecondsTT;
    time1->type = PS_TIME_TT;
    time1->leapsecond = false;
    time2 = psTimeConvert(time1,PS_TIME_UT1);
    if(time1 != time2) {
        psError(PS_ERR_UNKNOWN,true,"Did not return time for conversion from TT to UT1");
        return 9;
    }
    // Verify time type
    if(time2->type != PS_TIME_UT1) {
        psError(PS_ERR_UNKNOWN,true,"Returned time type %d not as expected %d",
                time2->type, PS_TIME_UT1);
        return 7;
    }
    // Verify time is UT1 as expected
    if(time2->sec != testTime1SecondsUT1) {
        psError(PS_ERR_UNKNOWN,true,"UT1 seconds = %ld not as expected %ld",(long int)time2->sec,
                (long int)testTime1SecondsUT1);
        return 9;
    }
    if(time2->nsec != testTime1NanosecondsUT1) {
        psError(PS_ERR_UNKNOWN,true,"UT1 nanoseconds = %d not as expected %d",time2->nsec,
                testTime1NanosecondsUT1);
        return 10;
    }

    // Attempt to convert from UT1 to TAI, UTC, TT
    time2 = NULL;
    time1->sec = 1;
    time1->nsec = 2;
    time1->type = PS_TIME_UT1;
    time1->leapsecond = false;
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message converting from UT1");
    time2 = psTimeConvert(time1,PS_TIME_UTC);
    if(time2 != time1) {
        psError(PS_ERR_UNKNOWN,true,"Did not return time for conversion from UT1");
        return 23;
    }

    psFree(time1);

    return 0;
}

//psS32 testTimeOther(void)
//{
//    psTime *testTime;
//    char *testString = "2004-07-21T18:22:24.272Z";
//
//    psLibInit("psTime.config");
//
// Test time was taken at July 21, 2004 at 18:22:24.272044
//    testTime = (psTime*)psAlloc(sizeof(psTime));
//    testTime->sec = 1090434144;
//    testTime->nsec = 272044000;
//    testTime->type = PS_TIME_TAI;

// Test O - psTime to LMST
//    printPositiveTestHeader(stdout, "psTime", "Convert psTime time to LST");
//    char *testString2 = "2004-09-10T1:00:00.00Z";
//    psTime* testTime2 = NULL;
//    testTime2 = psTimeFromISO(testString2);
//    double dblTime = psTimeToLMST(testTime2, 1);
//    printf("LST (rad): %lf\n", dblTime);
//    psFree(testTime2);
//    printFooter(stdout, "psTime", "Convert psTime time to LST", true);


//    return 0;
//}
