/* leapsecs.c ********************************************************************************
* leapsecs.c
*
* This set of functions makes it possible to maintain a list of the number of
* leap seconds. This is the value of TAI (Atomic time) - UTC (Universal time).
*
* RL_INT4 Jul_LeapSecs(dutc)
* returns the number of leap seconds prior to the beginning of the
* given year and month.
* RL_BOOL Jul_IsLeapDay(dutc)
* returns TRUE if the given day has a leap second.
* RL_INT4 Jul_DaySecs(dutc)
* returns the number of seconds in the given day.
* RL_BOOL Jul_InitLeaps(leapfile)
* initializes the table of leap seconds based on the contents of
* a specified file.
*
* Mark R. Showalter, PDS Ring-Moon Systems Node, June 1997
* Revised 6/98 by MRS to conform to RingLib naming standards.
* Revised 12/98 by MRS to include leap second of December 31, 1998.
*******************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include "julian.h"
#include "fortran.h"
/* The table below contains the default list of leap seconds. Users may add
* additional leap seconds and re-compile if they wish to do so. Simply
* increment the value for LEAP_DEFAULT_COUNT and also add a new entry to the
* leap_defaults[] array of the form {year, month, #secs}. The leap second will
* apply just before the beginning of the specified month. Note that month
* values are limited to 1 (January) and 7 (July).
*/
#define LEAP_DEFAULT_COUNT 23
static struct {RL_INT4 year; RL_INT4 month; RL_INT4 secs;}
leap_defaults[LEAP_DEFAULT_COUNT] = {
{1972, 1, 10},
{1972, 7, 11},
{1973, 1, 12},
{1974, 1, 13},
{1975, 1, 14},
{1976, 1, 15},
{1977, 1, 16},
{1978, 1, 17},
{1979, 1, 18},
{1980, 1, 19},
{1981, 7, 20},
{1982, 7, 21},
{1983, 7, 22},
{1985, 7, 23},
{1988, 1, 24},
{1990, 1, 25},
{1991, 1, 26},
{1992, 7, 27},
{1993, 7, 28},
{1994, 7, 29},
{1996, 1, 30},
{1997, 7, 31},
{1999, 1, 32}
};
/* Users should not modify these definitions */
static RL_INT4 *leap_table = NULL;
static RL_INT4 leap_size, leap_ymin, leap_ymax, leap_nmin, leap_nmax;
#define LEAPINDEX(y,m) (2*((y)-leap_ymin) + ((m)-1)/6)
/***********************************************************
* Prototypes of internal functions
***********************************************************/
static RL_INT4 ZJul_LeapSecsYM RL_PROTO((RL_INT4 year, RL_INT4 month));
/*
********************************************************************************
* EXPORTED USER ROUTINES
********************************************************************************
*$ Component_name:
* Jul_LeapSecs (leapsecs.c)
*$ Abstract:
* Returns the number of leap seconds elapsed before a given day.
*$ Keywords:
* JULIAN, TIME, UNIVERSAL_TIME, ATOMIC_TIME
* C, PUBLIC
*$ Declarations:
* RL_INT4 Jul_LeapSecs(dutc)
* RL_INT4 dutc;
*$ Inputs:
* dutc number of days relative to January 1, 2000.
*$ Outputs:
* none
*$ Returns:
* number of leap seconds before beginning of given day.
*$ Side_effects:
* The internal table of leap seconds is initialized if necessary.
*$ Detailed_description:
* This function returns the number of leap seconds elapsed before the
* beginning of a given day. This is the value for TAI (atomic time) -
* UTC (Universal time).
*$ External_references:
* Jul_YMDofDUTC(), Jul_InitLeaps()
*$ Examples:
* none
*$ Error_handling:
* none
*$ Limitations:
* none
*$ Author_and_institution:
* Mark R. Showalter
* PDS Ring-Moon Systems Node
* NASA/Ames Research Center
*$ Version_and_date:
* 1.0: November 1995
*$ Change_history:
* none
*******************************************************************************/
RL_INT4 Jul_LeapSecs(dutc)
RL_INT4 dutc;
{
RL_INT4 year, month, day;
Jul_YMDofDUTC(dutc, &year, &month, &day);
return ZJul_LeapSecsYM(year, month);
}
/*******************************************************************************
* Internal function to calculate leap seconds from year and month
*******************************************************************************/
static RL_INT4 ZJul_LeapSecsYM(year, month)
RL_INT4 year, month;
{
RL_INT4 index, nleaps;
/* Initialize table if necessary */
if (leap_table == NULL) Jul_InitLeaps(NULL);
/* Look up leap second count in table */
index = LEAPINDEX(year, month);
if (index < 0) nleaps = leap_nmin;
else if (index >= leap_size) nleaps = leap_nmax;
else nleaps = leap_table[index];
return nleaps;
}
/*
********************************************************************************
*$ Component_name:
* Jul_IsLeapDay (leapsecs.c)
*$ Abstract:
* Determines whether a specified day contains a leap second.
*$ Keywords:
* JULIAN, TIME, UNIVERSAL_TIME, ATOMIC_TIME
* C, PUBLIC
*$ Declarations:
* RL_BOOL Jul_IsLeapDay(dutc)
* RL_INT4 dutc;
*$ Inputs:
* dutc number of days relative to January 1, 2000.
*$ Outputs:
* none
*$ Returns:
* TRUE if the specified day contains a leap second; FALSE otherwise.
*$ Side_effects:
* The internal table of leap seconds is initialized if necessary.
*$ Detailed_description:
* This function determines whether a specified day contains a leap second.
* It returns TRUE if the day has a leap second; FALSE otherwise.
*$ External_references:
* Jul_YMDofDUTC(), Jul_InitLeaps()
*$ Examples:
* none
*$ Error_handling:
* none
*$ Limitations:
* none
*$ Author_and_institution:
* Mark R. Showalter
* PDS Ring-Moon Systems Node
* NASA/Ames Research Center
*$ Version_and_date:
* 1.0: November 1995
*$ Change_history:
* none
*******************************************************************************/
RL_BOOL Jul_IsLeapDay(dutc)
RL_INT4 dutc;
{
RL_INT4 year, month, day;
/* Initialize table if necessary */
if (leap_table == NULL) Jul_InitLeaps(NULL);
/* Convert to year, month and day */
Jul_YMDofDUTC(dutc, &year, &month, &day);
/* Make sure it's the last day of June or December */
if (day <= 29) {return FALSE;}
else if (day == 30) {if (month != 6) return FALSE;}
else if (day == 31) {if (month != 12) return FALSE;}
else {return FALSE;} /* Invalid date! */
/* Look for a change in the leap second count */
return (ZJul_LeapSecsYM(year,month+1) != ZJul_LeapSecsYM(year,month));
}
/*
********************************************************************************
*$ Component_name:
* Jul_DaySecs (leapsecs.c)
*$ Abstract:
* Returns the number of seconds in a given day.
*$ Keywords:
* JULIAN, TIME, UNIVERSAL_TIME, ATOMIC_TIME
* C, PUBLIC
*$ Declarations:
* RL_INT4 Jul_DaySecs(dutc)
* RL_INT4 dutc;
*$ Inputs:
* dutc number of days relative to January 1, 2000.
*$ Outputs:
* none
*$ Returns:
* number of seconds in given day, 86400 if it does not contain a leap
* second or 86401 if it does.
*$ Side_effects:
* The internal table of leap seconds is initialized if necessary.
*$ Detailed_description:
* This function returns the number of seconds on a given day. This number
* is 86400 on most days, but 86401 if the day contains a leap second.
*$ External_references:
* Jul_IsLeapDay(), Jul_InitLeaps()
*$ Examples:
* none
*$ Error_handling:
* none
*$ Limitations:
* none
*$ Author_and_institution:
* Mark R. Showalter
* PDS Ring-Moon Systems Node
* NASA/Ames Research Center
*$ Version_and_date:
* 1.0: November 1995
*$ Change_history:
* none
*******************************************************************************/
RL_INT4 Jul_DaySecs(dutc)
RL_INT4 dutc;
{
return (Jul_IsLeapDay(dutc) ? 86401:86400);
}
/*
********************************************************************************
*$ Component_name:
* Jul_InitLeaps (leapsecs.c)
*$ Abstract:
* Initializes the internal table of leap seconds, optionally based on the
* leap seconds listed in a file.
*$ Keywords:
* JULIAN, TIME, UNIVERSAL_TIME, ATOMIC_TIME
* C, PUBLIC
*$ Declarations:
* RL_BOOL Jul_InitLeaps(leapfile)
* RL_CHAR *leapfile;
*$ Inputs:
* leapfile pointer to the name of a file containing a supplementary
* list of leap seconds. If the file name is blank or the
* pointer is NULL, no file is read.
*$ Outputs:
* none
*$ Returns:
* TRUE if the initialization was successful; FALSE if an error occurred.
*$ Side_effects:
* The internal table of leap seconds is initialized.
*$ Detailed_description:
* This function initializes the internal table of leap seconds, optionally
* based on the contents of a file. If it is called, it must be called
* before any call to Jul_LeapSecs(), Jul_IsLeap() or Jul_DaySecs(). At
* minimum, an internal list of leap seconds (currently up to date through
* January 1996) is always used.
*
* The input file must contain four ASCII integers per record, separated by
* blanks. The order of values is year, month, day and total elapsed leap
* seconds at the beginning of that date. Lines of the file beginning with
* an exclamation point ("!") are ignored.
*
* Alternatively, users may add new leap seconds by simply augmenting the
* list found in file "jul_leapsecs.c" and then re-compiling.
*$ External_references:
* none
*$ Examples:
* How to read leaps seconds from an external file...
*
* RL_BOOL status;
*
* status = Jul_InitLeaps("leapseconds.dat");
* if (!status) exit(1);
*$ Error_handling:
* If an error occurs, the function prints an error message to "stderr" and
* returns a value of FALSE.
*$ Limitations:
* This routine only supports leap seconds that occur at the end of a
* December or a June. Leap second file records are limited to 131
* characters.
*$ Author_and_institution:
* Mark R. Showalter
* PDS Ring-Moon Systems Node
* NASA/Ames Research Center
*$ Version_and_date:
* 1.0: November 1995
*$ Change_history:
* none
*******************************************************************************/
#define RECORD_LEN 131
RL_BOOL Jul_InitLeaps(leapfile)
RL_CHAR *leapfile;
{
FILE *file;
RL_INT4 i, ymin, ymax, count, year, month, day, nsecs;
RL_CHAR record[RECORD_LEN+1];
/* Make sure this routine was not already called */
if (leap_table != NULL) goto EXTRA_CALL;
/* Initialize static variables */
leap_ymin = leap_defaults[0].year;
leap_nmin = leap_defaults[0].secs - 1;
leap_ymax = leap_defaults[LEAP_DEFAULT_COUNT-1].year;
leap_nmax = leap_defaults[LEAP_DEFAULT_COUNT-1].secs;
file = NULL;
/***********************************************************
* Open and read new leap seconds file if necessary
***********************************************************/
if (leapfile != NULL && *leapfile != '\0') {
/* Open file */
file = fopen(leapfile, "r");
if (file == NULL) goto OPEN_FAILURE;
/* Get year range and leap second count */
while (NULL != fgets(record, RECORD_LEN+1, file)) {
if (record[0] == '!') continue;
count = sscanf(record, "%d %d %d %d",
&year, &month, &day, &nsecs);
if (count == 0) continue;
if (count < 4) goto SYNTAX_ERROR;
if (year < leap_ymin) goto UNSUPPORTED;
if (month != 1 && month != 7) goto UNSUPPORTED;
if (day != 1) goto UNSUPPORTED;
if (nsecs > 255) goto UNSUPPORTED;
leap_ymax = year;
leap_nmax = nsecs;
}
}
/***********************************************************
* Allocate and initialize the leap seconds table
***********************************************************/
leap_size = 2 * (leap_ymax - leap_ymin + 1);
leap_table = (RL_INT4 *) malloc(leap_size * sizeof(RL_INT4));
if (leap_table == NULL) goto MALLOC_ERROR;
for (i = 0; i < leap_size; i++) {
leap_table[i] = leap_nmin;
}
/************************************************************
* Enter default leap seconds into the table
************************************************************/
for (i = 0; i < LEAP_DEFAULT_COUNT; i++) {
leap_table[ LEAPINDEX(leap_defaults[i].year,
leap_defaults[i].month) ]
= leap_defaults[i].secs;
}
/***********************************************************
* Enter new leap seconds into the table, if necessary
***********************************************************/
if (file != NULL) {
rewind(file);
while (NULL != fgets(record, RECORD_LEN+1, file)) {
count = sscanf(record, "%d %d %d %d",
&year, &month, &day, &nsecs);
if (count == 0) continue;
leap_table[LEAPINDEX(year,month)] = nsecs;
}
fclose(file);
}
/***********************************************************
* Fill gaps in the table
***********************************************************/
nsecs = leap_nmin;
for (i = 0; i < leap_size; i++) {
if (leap_table[i] == leap_nmin) {
leap_table[i] = nsecs;
}
else {
if (leap_table[i] != nsecs+1) goto STEP_ERROR;
nsecs = leap_table[i];
}
}
return TRUE;
EXTRA_CALL:
fprintf(stderr, "Leap seconds table was already initialized\n");
return FALSE;
OPEN_FAILURE:
fprintf(stderr, "Open failure on file \"%s\"\n", leapfile);
return FALSE;
UNSUPPORTED:
fprintf(stderr, "Unsupported leap seconds date: %s\n", record);
return FALSE;
SYNTAX_ERROR:
fprintf(stderr, "Syntax error in leap seconds file: %s\n", record);
return FALSE;
MALLOC_ERROR:
fprintf(stderr, "Unable to allocate memory for leap seconds table\n");
return FALSE;
STEP_ERROR:
fprintf(stderr, "Illegal leap second step from %d to %d\n",
nsecs, leap_table[i]);
return FALSE;
}
/*
********************************************************************************
* FORTRAN INTERFACE ROUTINES
********************************************************************************
*$ Component_name:
* FJul_LeapSecs (leapsecs.c)
*$ Abstract:
* Returns the number of leap seconds elapsed before a given day.
*$ Keywords:
* JULIAN, TIME, UNIVERSAL_TIME, ATOMIC_TIME
* FORTRAN, PUBLIC
*$ Declarations:
* integer*4 function FJul_LeapSecs(dutc)
* integer*4 dutc
*$ Inputs:
* dutc number of days relative to January 1, 2000.
*$ Outputs:
* none
*$ Returns:
* number of leap seconds before beginning of given day.
*$ Side_effects:
* The internal table of leap seconds is initialized if necessary.
*$ Detailed_description:
* This function returns the number of leap seconds elapsed before the
* beginning of a given day. This is the value for TAI (atomic time) -
* UTC (Universal time).
*$ External_references:
* Jul_LeapSecs()
*$ Examples:
* none
*$ Error_handling:
* none
*$ Limitations:
* none
*$ Author_and_institution:
* Mark R. Showalter
* PDS Ring-Moon Systems Node
* NASA/Ames Research Center
*$ Version_and_date:
* 1.0: November 1995
*$ Change_history:
* none
*******************************************************************************/
RL_INT4 FORTRAN_NAME(fjul_leapsecs) (dutc)
RL_INT4 *dutc;
{
return Jul_LeapSecs(*dutc);
}
/*
********************************************************************************
*$ Component_name:
* FJul_IsLeapDay (leapsecs.c)
*$ Abstract:
* Determines whether a specified day contains a leap second.
*$ Keywords:
* JULIAN, TIME, UNIVERSAL_TIME, ATOMIC_TIME
* FORTRAN, PUBLIC
*$ Declarations:
* logical*4 function FJul_IsLeapDay(dutc)
* integer*4 dutc
*$ Inputs:
* dutc number of days relative to January 1, 2000.
*$ Outputs:
* none
*$ Returns:
* .TRUE. if the specified day contains a leap second; .FALSE. otherwise.
*$ Side_effects:
* The internal table of leap seconds is initialized if necessary.
*$ Detailed_description:
* This function determines whether a specified day contains a leap second.
* It returns .TRUE. if the day has a leap second; .FALSE. otherwise.
*$ External_references:
* Jul_IsLeapDay()
*$ Examples:
* none
*$ Error_handling:
* none
*$ Limitations:
* none
*$ Author_and_institution:
* Mark R. Showalter
* PDS Ring-Moon Systems Node
* NASA/Ames Research Center
*$ Version_and_date:
* 1.0: November 1995
*$ Change_history:
* none
*******************************************************************************/
RL_INT4 FORTRAN_NAME(fjul_isleapday) (dutc)
RL_INT4 *dutc;
{
return (Jul_IsLeapDay(*dutc) ? FTRUE:FFALSE);
}
/*
********************************************************************************
*$ Component_name:
* FJul_DaySecs (leapsecs.c)
*$ Abstract:
* Returns the number of seconds in a given day.
*$ Keywords:
* JULIAN, TIME, UNIVERSAL_TIME, ATOMIC_TIME
* FORTRAN, PUBLIC
*$ Declarations:
* integer*4 function FJul_DaySecs(dutc)
* integer*4 dutc
*$ Inputs:
* dutc number of days relative to January 1, 2000.
*$ Outputs:
* none
*$ Returns:
* number of seconds in given day, 86400 if it does not contain a leap
* second or 86401 if it does.
*$ Side_effects:
* The internal table of leap seconds is initialized if necessary.
*$ Detailed_description:
* This function returns the number of seconds on a given day. This number
* is 86400 on most days, but 86401 if the day contains a leap second.
*$ External_references:
* Jul_DaySecs()
*$ Examples:
* none
*$ Error_handling:
* none
*$ Limitations:
* none
*$ Author_and_institution:
* Mark R. Showalter
* PDS Ring-Moon Systems Node
* NASA/Ames Research Center
*$ Version_and_date:
* 1.0: November 1995
*$ Change_history:
* none
*******************************************************************************/
RL_INT4 FORTRAN_NAME(fjul_daysecs) (dutc)
RL_INT4 *dutc;
{
return Jul_DaySecs(*dutc);
}
/*
********************************************************************************
*$ Component_name:
* FJul_InitLeaps (leapsecs.c)
*$ Abstract:
* Initializes the internal table of leap seconds, optionally based on the
* leap seconds listed in a file.
*$ Keywords:
* JULIAN, TIME, LEAP_SECONDS
* C, PUBLIC
*$ Declarations:
* logical*4 function FJul_InitLeaps(leapfile)
* character*(*) leapfile
*$ Inputs:
* leapfile name of a file containing a supplementary list of leap
* seconds. If the file name is blank, no file is read.
*$ Outputs:
* none
*$ Returns:
* .TRUE. if the initialization was successful; .FALSE. if an error
* occurred.
*$ Side_effects:
* The internal table of leap seconds is initialized.
*$ Detailed_description:
* This function initializes the internal table of leap seconds, optionally
* based on the contents of a file. If it is called, it must be called
* before any call to FJul_LeapSecs(), FJul_IsLeap() or FJul_DaySecs(). At
* minimum, an internal list of leap seconds (currently up to date through
* January 1996) is always used.
*
* The input file must contain four ASCII integers per record, separated by
* blanks. The order of values is year, month, day and total elapsed leap
* seconds at the beginning of that date.
*
* Alternatively, users may augment the leap seconds list by simply adding
* them to the list found in file "jul_leapsecs.c" and then re-compiling.
*$ External_references:
* Jul_InitLeaps(), RL_Cstring()
*$ Examples:
* How to read leaps seconds from an external file...
*
* logical*4 status
*
* status = FJul_InitLeaps('leapseconds.dat')
* if (.not. status) stop
*$ Error_handling:
* If an error occurs, the function prints an error message and returns a
* value of .FALSE.
*$ Limitations:
* This routine only supports leap seconds that occur at the end of a
* December or a June. Leap second file records are limited to 131
* characters.
*$ Author_and_institution:
* Mark R. Showalter
* PDS Ring-Moon Systems Node
* NASA/Ames Research Center
*$ Version_and_date:
* 1.0: November 1995
*$ Change_history:
* none
********************************************************************************
* Note: GJul_InitLeaps() defined here is the intermediary routine between
* FJul_InitLeaps() and Jul_InitLeaps(), allowing for the fact that strings
* cannot be passed directly between FORTRAN and C. See fjulian.for for the
* rest of the code.
*
* subroutine GJul_InitLeaps(leapfile)
* byte leapfile
*******************************************************************************/
RL_INT4 FORTRAN_NAME(gjul_initleaps) (leapfile)
RL_CHAR *leapfile;
{
FORT_Init();
return (Jul_InitLeaps(leapfile) ? FTRUE:FFALSE);
}
/*******************************************************************************
*/