MJDtime Routines

 

QSAS Support Team

Cluster Science Centre, Imperial College

csc-support-dl@imperial.ac.uk

 

 

This software is provided under the LGPL in January 2009.

Copyright (C) 2009  Imperial College, London

See the GNU Lesser General Public License for more details.

 

These routines convert between various date and time formats. The date and time is stored internally as a structure , MJDtime, containing an integer, as the integer part of the MJD date, and a double representing the seconds offset from the start of this day. Routines exist to also set and get MJD, JD, formatted date/time strings and NASA CDF epoch date/times from this structure.

 

Modified Julian Date (MJD) measures days (and fractional days) since the start of 17 Nov 1858 CE in Universal Time (UTC). Julian Date (JD) measures days (and fractional days) since noon on 1 January, 4713 BCE in Universal Time (UTC).

 

Modified Julian Date (MJD) = Julian Date (JD) - 2400000.5

 

Common Era (CE) and Before Common Era (BCE) are also often called AD and BC respectively.

 

By default these utilities use the Gregorian calendar after 4 Oct 1582 (Julian) i.e. from 15 Oct 1582 (Gregorian). Note Unix libraries use Gregorian only from 14 Sept 1752 and will yield different results between years 1582 and 1752. The MJD value from which the Gregorian calendar is used can be set through a call to setGregorianStartMJD(double GregorianMJD).

 

 More detailed discussion can be found at http://aa.usno.navy.mil/data/docs/JulianDate.php

These routines have been compared with the results of the US Naval Observatory online converter.

             

 In all routines, specifying a day, hour, minute or second field greater than would be valid is

 handled with modulo arithmetic and is safe.

Thus 2006-12-32 00:62:00.0 will be safely, and correctly, treated as 2007-01-01 01:02:00.0

             

MJD starts at 0h, so truncating MJD always gives the same day whatever the time of day (unlike JD). The seconds offset may take any value, so that any date/time may be expressed in terms of an offset from the same MJD day. The seconds field thus may exceed a single day, and may also be negative.

 

typedef struct MJDtimeStruct

{

            int base_day; /* integer part of MJD  */

            double time_sec; /* seconds from start of base_day */

           

}MJDtime;

 

 

 

 

Routines

 

void setFromUT  (int year, int month, int day, int hour, int min, double sec, MJDtime *MJD);

 

void setFromDOY (int year, int dofy, int hour, int min, double sec, MJDtime *MJD);

 

void setFromBCE  (int yearBCE, int month, int day, int hour, int min, double sec, MJDtime *MJD);

 

void setFromMJD  (double ModifiedJulianDate, MJDtime *MJD);

 

void setFromJD  (double JulianDate, MJDtime *MJD);

 

int setFromISOstring(const char* ISOstring, MJDtime *MJD);

 

void setFromCDFepoch  (double cdfepoch, MJDtime *MJD);

 

int breakDownMJD  (int *year, int *month, int *day, int *hour, int *min, double *sec,  const MJDtime *MJD);

 

double getMJD  (MJDtime *MJD);

 

double getJD  (MJDtime *MJD);

 

double getDiffDays  (MJDtime *MJD1, MJDtime *MJD2);

 

double getDiffSecs  (MJDtime *MJD1, MJDtime *MJD2);

 

double getCDFepoch  (MJDtime *MJD);

 

const char * getISOString  (MJDtime *MJD, int delim);

 

const char * getDayOfWeek  (const MJDtime *MJD);

 

const char * getLongDayOfWeek  (const MJDtime *MJD);

 

const char * getMonth  (int m);

 

const char * getLongMonth  (int m);

 

void getYandD  (int *year, int *isLeapyear, int *dofy, const MJDtime *MJD);

 

size_t strfMJD  (char * buf, size_t len, const char *format, const MJDtime *MJD);

 

void setGregorianStartMJD(double GregorianMJD);

 

void normalize_MJD(MJDtime *MJDout, const MJDtime *MJDin);

 

 

 

 

 

 

 

 

 

Detailed Descriptions

 

#include <MJDtime.h>

 

void setFromUT  (int year, int month, int day, int hour, int min, double sec, MJDtime *MJD);

 

DESCRIPTION

The setFromUT() function constructs an MJDtime structure from the broken down year, month, day, hour, minute and seconds. The Gregorian calendar is used from the day following  4 Oct 1582 (Julian) i.e. from 15 Oct 1582 (Gregorian) unless changed using setGregorianStartMJD().  Note Unix libraries use Gregorian only from 14 Sept 1752 onwards.

 

 

void setFromDOY (int year, int dofy, int hour, int min, double sec, MJDtime *MJD);

 

DESCRIPTION

The setFromDOY() function constructs an MJDtime structure from the broken down year,  day of year, hour, minute and seconds. The Gregorian calendar is used from the day following  4 Oct 1582 (Julian) i.e. from 15 Oct 1582 (Gregorian) unless changed using setGregorianStartMJD().  Note Unix libraries use the Gregorian calendar only from 14 Sept 1752 onwards.

 

 

void setFromBCE  (int yearBCE, int month, int day, int hour, int min, double sec, MJDtime *MJD);

 

DESCRIPTION

The setFromBCE() function constructs an MJDtime structure from the broken down year (BCE),  month, day, hour, minute and seconds. Note BCE years start from 0 CE, so year CE = 1 – year BCE. Julian Calendar is always used in setFromBCE().

 

 

void setFromMJD  (double ModifiedJulianDate, MJDtime *MJD);

 

DESCRIPTION

The setFromMJD() function constructs an MJDtime structure from Modified Julian Date as a double. MJD is calendar independent and forms a continuous sequence of days and fractions of days. Time is measured in UTC.

 

 

void setFromJD  (double JulianDate, MJDtime *MJD);

 

DESCRIPTION

The setFromJD() function constructs an MJDtime structure from Julian Date as a double. JD is calendar independent and forms a continuous sequence of days and fractions of days. Time is measured in UTC. MJD = JD – 2400000.5 since JD starts at noon.

 

int setFromISOstring(const char* ISOstring, MJDtime *MJD);

 

 

DESCRIPTION

The setFromISOstring() function constructs an MJDtime structure from an ISO standard format Date and time string in UTC. Gregorian Calendar is assumed from 15 Oct 1582.

The ISO format is of the form "1995-01-23 02:33:17.235" or "1995-01-23T02:33:17.235Z". Both the ÔTÕ separator and the trailing ÔZÕ are optional, and any number of decimal places after the seconds field are allowed.

 

void setFromCDFepoch  (double cdfepoch, MJDtime *MJD);

 

DESCRIPTION

The setFromCDFepoch() function constructs an MJDtime structure from the NASA CDF Epoch as a double. CDF Epoch measures milliseconds from 0 CE (1 BCE) on the Gregorian Calendar. It is intended for use with space missions in the Common Era and will give misleading dates on the Julian Calendar. Much faster routines exist for handling conversion of CDF epochs to time strings in the modern restricted interval of applicability, but the algorithm here is consistent with the rest of the routines at all dates.

 

 

int breakDownMJD  (int *year, int *month, int *day, int *hour, int *min, double *sec,  const MJDtime *MJD);

 

DESCRIPTION

The breakDownMJD() function converts an MJDtime structure into the broken down year, month, day, hour, minute and seconds. It also returns the day of year as an integer. The Gregorian calendar is used from the day following  4 Oct 1582 (Julian) i.e. from 15 Oct 1582 (Gregorian) unless changed using setGregorianStartMJD().  Note Unix libraries use Gregorian only from 14 Sept 1752 onwards.

 

 

double getMJD  (MJDtime *MJD);

 

DESCRIPTION

The getMJD() function converts an MJDtime structure into a Modified Julian Date as a double. MJD is calendar independent and forms a continuous sequence of days and fractions of days. Time is measured in UTC.

 

 

double getJD  (MJDtime *MJD);

 

DESCRIPTION

The getJD() function converts an MJDtime structure into a Julian Date as a double. JD is calendar independent and forms a continuous sequence of days and fractions of days. Time is measured in UTC.

 

 

double getCDFepoch  (MJDtime *MJD);

 

DESCRIPTION

The getCDFepoch() function converts an MJDtime structure into the NASA CDF Epoch as a double. CDF Epoch measures milliseconds from 0 CE (1 BCE) on the Gregorian Calendar. It is intended for use with space missions in the Common Era and will give misleading dates on the Julian Calendar. Much faster routines exist for handling conversion of CDF epochs to time strings in the modern restricted interval of applicability, but the algorithm here is consistent with the rest of the routines at all dates.

 

 

const char * getISOString  (MJDtime *MJD, int delim);

 

DESCRIPTION

The getISOString() function converts an MJDtime structure into an ISO date/time string measured in UTC. Uses the default change over date for Julian to Gregorian calendars, which is 15 Oct 1582 unless changed with setGregorianStartMJD(). If delim is 1 then the ÔTÕ and ÔZÕ delimiters are used, otherwise the date and time part are space separated. The resulting string is of the form

"1995-01-23 02:33:17.235" or "1995-01-23T02:33:17.235Z".  

It returns a pointer to a static string and is therefore not thread safe. The returned string must be copied if it is to be retained beyond a repeat call, for example :

printf("%s Julian =  %s Gregorian \n" , getISOString(&MJD1,1), getISOString(&MJD1,0));

will use the same string twice rather than distinct strings.

 

This is only a convenience utility for quick testing and simple use, and is equivalent to the (thread safe) strfMJD() call which is preferred for robust coding. The equivalent call would be É

   char buf[360];

   strfMJD(&(buf[0]), 360, "%Y-%m-%dT%H:%M:%S%.Z",  &MJD2, 0);

(note the decimal point before the final Z gives all available accuracy for the seconds fraction).

Also, for a specific accuracy, e.g. microseconds, use strfMJD() as inÉ

strfMJD(&(buf[0]), 360, "%Y-%m-%d %H:%M:%S%6",  &MJD2, 0);

 

 

double getDiffDays  (MJDtime *MJD1, MJDtime *MJD2);

 

DESCRIPTION

The getDiffDays() function returns the difference between two MJDtime structures measured in days as a double. The MJDtime structures do not need to have the same base day.

 

 

double getDiffSecs  (MJDtime *MJD1, MJDtime *MJD2);

 

DESCRIPTION

The getDiffSecs() function returns the difference between two MJDtime structures measured in seconds as a double. The MJDtime structures do not need to have the same base day.

 

 

const char * getDayOfWeek  (const MJDtime *MJD);

 

DESCRIPTION

The getDayOfWeek() function returns a char * pointer to a static null terminated string holding the short (3 character) text name for the day, in English, e.g. Mon, Tue, etc.

 

 

const char * getLongDayOfWeek  (const MJDtime *MJD);

 

DESCRIPTION

The getLongDayOfWeek() function returns a char * pointer to a static null terminated string holding the full text name for the day, in English, e.g. Monday, Tuesday, etc.

 

 

const char * getMonth  (int m);

 

DESCRIPTION

The getMonth() function returns a char * pointer to a static null terminated string holding the short (3 character) text name for the month, in English, e.g. Jan, Feb, etc.

 

 

const char * getLongMonth  (int m);

 

DESCRIPTION

The getLongMonth() function returns a char * pointer to a static null terminated string holding the full text name for the month, in English, e.g. January, February, etc.

 

void getYandD  (int *year, int *isLeapyear, int *dofy, const MJDtime *MJD);

 

DESCRIPTION

The getYandD() function determines the year as an int, whether the year is a leap year as an int, and day of year as an int for the current day in the MJDtime structure MJD. If seconds are negative or hold more than one day the day is adjusted accordingly, thus it is not simply the day of year for the integer part of the MJDtime structure. The Gregorian calendar is used from the day following  4 Oct, 1582 (Julian) i.e. from 15 Oct, 1582 (Gregorian) unless changed with setGregorianStartMJD(). 

The int isLeapyear is 0 if year is not a leap year and 1 if it is.

 

 

size_t  strfMJD  (char * buf, size_t len,  const char * format, const MJDtime MJD);

 

DESCRIPTION

The strfMJD () function formats the information from MJD into the

buffer buf according to the string pointed to by format using the formatting conventions of strftime();

 

The format string consists of zero or more conversion specifications and

ordinary characters.  All ordinary characters are copied directly into

the buffer.  A conversion specification consists of a percent sign

`%' and one other character.

 

No more than len characters will be placed into the array, including the terminating NULL. strfMJD () returns the number of characters written into the array, not counting the terminating NULL. The output is truncated when len - 1 characters is reached.

 

This routine differs from strftime() in that all date/times are in UTC and no locale or national variations in names are used. All names are returned in English.

 

The conversion specifications are copied to the buffer after expansion as

follows:-

 

%A   is replaced by a representation of the full weekday name.

 

%a    is replaced by a representation of the abbreviated weekday name.

 

%B    is replaced by a representation of the full month name.

 

%b    is replaced by a representation of the abbreviated month name.

 

%C   is replaced by (year / 100) as decimal number; single digits are preceded by a zero.

 

%c    is replaced by a representation of time and date.

 

%D   is equivalent to ``%m/%d/%y''.

 

%d    is replaced by the day of the month as a decimal number (01-31).

 

%e    is replaced by the day of month as a decimal number (1-31); single digits are preceded by a blank.

 

%F    is equivalent to ``%Y-%m-%d''.

 

%G   is replaced by a year as a decimal number with century.  This year is the one that contains the greater part of the week (Monday as the first day of the week).

 

%g    is replaced by the same year as in ``%G'', but as a decimal number without century (00-99).

 

%H    is replaced by the hour (24-hour clock) as a decimal number (00-23).

 

%h    the same as %b.

 

%I    is replaced by the hour (12-hour clock) as a decimal number (01-12).

 

%j    is replaced by the day of the year as a decimal number (001-366).

 

%k    is replaced by the hour (24-hour clock) as a decimal number (0-23); single digits are preceded by a blank.

 

%l    is replaced by the hour (12-hour clock) as a decimal number (1-12); single digits are preceded by a blank.

 

%M   is replaced by the minute as a decimal number (00-59).

 

%m    is replaced by the month as a decimal number (01-12).

 

%n    is replaced by a newline.

 

%p    is replaced by ÔAMÕ or ÔPMÕ as appropriate.

 

%R    is equivalent to Ô%H:%MÕ.

 

%r    is equivalent to Ô%I:%M:%S %pÕ.

 

%S    is replaced by the second as a decimal number (00-60).

 

%s    is replaced by the number of seconds since 1 Jan 1970, UTC.

 

%T    is equivalent to Ô%H:%M:%SÕ.

 

%t    is replaced by a tab.

 

%U   is replaced by the week number of the year (Sunday as the first day of the week) as a decimal number (00-53).

 

%u    is replaced by the weekday (Monday as the first day of the week) as a decimal number (1-7).

 

%V   is replaced by the week number of the year (Monday as the first day of the week) as a decimal number (01-53).  If the week containing January 1 has four or more days in the new year, then it is week 1; otherwise it is the last week of the previous year, and the next week is week 1.

 

%v    is equivalent to Ô%e-%b-%YÕ.

 

%W  is replaced by the week number of the year (Monday as the first day of the week) as a decimal number (00-53).

 

%w   is replaced by the weekday (Sunday as the first day of the week) as a decimal number (0-6).

 

%X   is replaced by a representation of the time.

 

%x   is replaced by a representation of the date.

 

%Y   is replaced by the year with century as a decimal number.

 

%y    is replaced by the year without century as a decimal number (00-99).

 

%Z    is replaced by the time zone name.

 

%z    is replaced by the time zone offset from UTC; a leading plus sign stands for east of UTC, a minus sign for west of UTC, hours and minutes follow with two digits each and no delimiter between them (always gives Ô+0000Õ).

 

%+    is replaced by a representation of the date and time of the form

Fri Jan 23 15:06:10 UTC 2009

 

%%    is replaced by Ô%Õ.

 

The following extra two option flags are also provided although they are not available in the strftime() routines.

 

%(0-9)      is replaced by the fractional part of the seconds field to the specified accuracy. Thus %S%3 would give seconds to millisecond accuracy (00.000).

 

%.     (decimal point) is replaced by the fractional part of the seconds field to available accuracy. Thus %S%. would give seconds with fractional part up to 9 decimal places if available. Spaces are removed from the end of the string but zeros are left. This may behave slightly differently on different platforms.

 

All other flags are silently ignored and not printed.

 

 

void setGregorianStartMJD(double GregorianMJD);

 

This sets the global MJD value from which the Gregorian calendar will be used. This is applied to all routines breaking down or building up an MJDtime structure and converting to dates in the form of year, month, day etc.

 

This must be set as an MJD double value for the start of a day. For 15 Oct 1582 (Gregorian) this is set with

GregorianMJD = -100840, and is equivalent to  5 Oct 1582 (Julian) i.e. the calendar will go from Thursday 4 Oct 1582 to Friday 15 Oct 1582.

 

For 14 Sept 1752 (Gregorian) (Unix, US and UK changeover) this is set with GregorianMJD = -38779.

 

To force a Gregorian proleptic calendar use a value for GregorianMJD that is before any date in the data set, and to force a Julian proleptic calendar set with a value that is after the last date in the data set to be used.

 

Note that MJD for 1 Jan 1970 is 40587 (used for the Unix start epoch) and MJD for 1 Jan 2000 is 51544 (used by QSAS internally).

 

Note also that NASA CDF epoch values are calculated as milliseconds from a nominal 0 CE (1 BCE) on the Gregorian proleptic calendar, corresponding to MJD -678943.

 

void normalize_MJD(MJDtime *MJDout, const MJDtime *MJDin);

 

This copies the MJDtime structure MJDin into the structure MJDout, but normalised such that the time_sec value corresponds to a positive value less than one full day. Thus the base_day int in the returned structure contains the MJD day value for that date and the time_sec contains the seconds in the day.