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.