/***************************************************************************
* GTime.cpp - Time class *
* ----------------------------------------------------------------------- *
* copyright (C) 2010-2016 by Juergen Knoedlseder *
* ----------------------------------------------------------------------- *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see . *
* *
***************************************************************************/
/**
* @file GTime.cpp
* @brief Time class implementation
* @author Juergen Knoedlseder
*/
/* __ Includes ___________________________________________________________ */
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include "GTools.hpp"
#include "GException.hpp"
#include "GTime.hpp"
#include "GTimeReference.hpp"
/* __ Constants __________________________________________________________ */
const double mjd_ref = 55197.000766018518519; //!< MJD of time=0
const double jd_ref = mjd_ref + 2400000.5; //!< JD of time=0
/* __ Method name definitions ____________________________________________ */
#define G_CONSTRUCT "GTime::GTime(double&, std::string&)"
#define G_TIME "GTime::time(double&, double&, std::string&, " \
"std::string&, std::string&"
#define G_UTC "GTime::utc(std::string&)"
/* __ Macros _____________________________________________________________ */
/* __ Coding definitions _________________________________________________ */
/* __ Debug definitions __________________________________________________ */
/*==========================================================================
= =
= Constructors/destructors =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Void constructor
***************************************************************************/
GTime::GTime(void)
{
// Initialise private members
init_members();
// Return
return;
}
/***********************************************************************//**
* @brief Copy constructor
*
* @param[in] time Time.
***************************************************************************/
GTime::GTime(const GTime& time)
{
// Initialise private members
init_members();
// Copy members
copy_members(time);
// Return
return;
}
/***********************************************************************//**
* @brief Time constructor
*
* @param[in] time Time value in TT (seconds or days).
* @param[in] unit Time unit (default: "secs").
*
* @exception GException::time_invalid_unit
* Invalid time unit specified.
*
* Constructs a GTime object by setting the time in the native reference
* in the TT time system in units of seconds (default) or days.
***************************************************************************/
GTime::GTime(const double& time, const std::string& unit)
{
// Initialise private members
init_members();
// Set time according to timeunit string
std::string timeunit = gammalib::tolower(unit);
if (timeunit == "d" || timeunit == "day" || timeunit == "days") {
days(time);
}
else if (timeunit == "s" || timeunit == "sec" || timeunit == "secs") {
secs(time);
}
else {
throw GException::time_invalid_unit(G_CONSTRUCT, unit,
"Valid timeunit values are: \"d\", \"day\", \"days\","
" \"s\", \"sec\" or \"secs\"");
}
// Return
return;
}
/***********************************************************************//**
* @brief Time constructor
*
* @param[in] time Time in given reference system.
* @param[in] ref Reference system.
*
* Constructs a GTime object by setting the time to a value given in a
* specific reference system.
***************************************************************************/
GTime::GTime(const double& time, const GTimeReference& ref)
{
// Initialise private members
init_members();
// Set time
set(time, ref);
// Return
return;
}
/***********************************************************************//**
* @brief Destructor
***************************************************************************/
GTime::~GTime(void)
{
// Free members
free_members();
// Return
return;
}
/*==========================================================================
= =
= Operators =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Assignment operator
*
* @param[in] time Time.
* @return Time.
***************************************************************************/
GTime& GTime::operator=(const GTime& time)
{
// Execute only if object is not identical
if (this != &time) {
// Free members
free_members();
// Initialise private members for clean destruction
init_members();
// Copy members
copy_members(time);
} // endif: object was not identical
// Return
return *this;
}
/*==========================================================================
= =
= Public methods =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Clear time
***************************************************************************/
void GTime::clear(void)
{
// Free members
free_members();
// Initialise members
init_members();
// Return
return;
}
/***********************************************************************//**
* @brief Clone time
*
* @return Pointer to deep copy of time.
***************************************************************************/
GTime* GTime::clone(void) const
{
// Clone this image
return new GTime(*this);
}
/***********************************************************************//**
* @brief Return time in Julian Days (TT) (unit: days)
*
* @return Time in Julian Days (TT) [days].
*
* Returns the time in Julian Days (JD) in the Terrestrial Time (TT) system.
***************************************************************************/
double GTime::jd(void) const
{
// Convert time from MET to JD
double jd = m_time / gammalib::sec_in_day + jd_ref;
// Return JD
return jd;
}
/***********************************************************************//**
* @brief Return time in Modified Julian Days (TT) (unit: days)
*
* @return Time in Modified Julian Days (TT) [days].
*
* Returns the time in Modified Julian Days (MJD) in the Terrestrial Time
* (TT) system.
***************************************************************************/
double GTime::mjd(void) const
{
// Convert time to MJD
double mjd = m_time / gammalib::sec_in_day + mjd_ref;
// Return MJD
return mjd;
}
/***********************************************************************//**
* @brief Return time in native reference (TT) (unit: days)
*
* @return Time in native reference [days].
***************************************************************************/
double GTime::days(void) const
{
// Return time
return m_time / gammalib::sec_in_day;
}
/***********************************************************************//**
* @brief Return time as string in UTC time system
*
* @return Time as string in UTC time system.
*
* Returns time in the format YYYY-MM-DDThh:mm:ss, where YYYY is a four-digit
* year, MM a two-digit month, DD a two-digit day of month, hh two digits of
* hour (0 through 23), mm two digits of minutes, and ss two digits of
* second (ISO 8601 time standard).
*
* The method is only valid for dates from year 1972 on.
***************************************************************************/
std::string GTime::utc(void) const
{
// Define number of days per month
static int daymonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
// Get MJD in TAI time system
double mjd = this->mjd() - gammalib::tai2tt * gammalib::sec2day;
// Correct for leap seconds (repeat is a kluge to converge as the
// argument is given in the UTC system but upon start we're in the TAI
// system
double leaps = leap_seconds(mjd) * gammalib::sec2day;
leaps = leap_seconds(mjd - leaps) * gammalib::sec2day;
leaps = leap_seconds(mjd - leaps) * gammalib::sec2day;
mjd -= leaps;
// Split in day and fraction
int day = (int)mjd;
double fraction = mjd - (double)day;
// Compute time in day. We add a margin to the seconds and subtract it
// later to avoid rounding of 59 to 60.
double second = fraction * gammalib::sec_in_day + 0.0000005;
int hour = (int)second / 3600;
second -= hour * 3600.0;
int minute = (int)second / 60;
second -= minute * 60.0;
if (hour > 23) {
hour -= 24;
day++;
}
second -= 0.0000005;
if (second < 0.0) {
second = 0.0;
}
// Compute year and day in the year
int year = 1972; // Set year to 1972
day -= 41317; // Subtract MJD of 1-1-1972
day++; // Day 0 is 1 January
int days = days_in_year(year);
while (day > days) {
day -= days;
year++;
days = days_in_year(year);
}
// Adjust number of days per month for leap years
if (is_leap_year(year)) {
daymonth[1] = 29;
}
else {
daymonth[1] = 28;
}
// Compute month and day in the month
int month = 0;
while (month < 12) {
if (day <= daymonth[month]) {
break;
}
day -= daymonth[month];
month++;
}
month++;
// Create string
char utc[32];
sprintf(utc, "%4.4d-%2.2d-%2.2dT%2.2d:%2.2d:%02.0f",
year, month, day, hour, minute, second);
// Return
return (std::string(utc));
}
/***********************************************************************//**
* @brief Return time in specified reference
*
* @return Time in specified reference.
*
* Convert the time from the native reference system into the specified
* reference system.
***************************************************************************/
double GTime::convert(const GTimeReference& ref) const
{
// Retrieve time in native reference (TT in seconds)
double time = m_time;
// Compute time offset in seconds
double offset = (mjd_ref - ref.mjdref()) * gammalib::sec_in_day;
// Add time offset in seconds
time += offset;
// Subtract leap seconds in case that time is requested in UTC time
// system
if (gammalib::toupper(ref.timesys()) == "UTC") {
// Get MJD in TAI time system
double mjd = this->mjd() - gammalib::tai2tt * gammalib::sec2day;
// Get leap seconds (repeat is a kluge to converge as the
// argument is given in the UTC system but upon start we're in the TAI
// system
double leaps = leap_seconds(mjd);
leaps = leap_seconds(mjd - leaps * gammalib::sec2day);
leaps = leap_seconds(mjd - leaps * gammalib::sec2day);
// Subtract leap seconds and TAI offset
time -= (leaps + gammalib::tai2tt);
} // endif: UTC time system has been requested
// Convert to specified time unit
double to_unit = ref.unitseconds();
if (to_unit != 1.0) {
time /= to_unit;
}
// Return time
return time;
}
/***********************************************************************//**
* @brief Set time in Julian Days (TT) (unit: days)
*
* @param[in] time Time in Julian Days (TT) [days].
***************************************************************************/
void GTime::jd(const double& time)
{
// Convert time from JD to native (seconds)
m_time = (time - jd_ref) * gammalib::sec_in_day;
// Return
return;
}
/***********************************************************************//**
* @brief Set time in Modified Julian Days (TT) (unit: days)
*
* @param[in] time Time in Modified Julian Days (TT) [days].
***************************************************************************/
void GTime::mjd(const double& time)
{
// Convert time from MJD to native (seconds)
m_time = (time - mjd_ref) * gammalib::sec_in_day;
// Return
return;
}
/***********************************************************************//**
* @brief Set time in native reference in days (TT)
*
* @param[in] days Time (TT) [days].
***************************************************************************/
void GTime::days(const double& days)
{
// Set time
m_time = days * gammalib::sec_in_day;
// Return
return;
}
/***********************************************************************//**
* @brief Set time as string in UTC time system
*
* @param[in] time Time string (UTC).
*
* @exception GException::invalid_argument
* Invalid time string specified.
*
* The time has to be given in the format YYYY-MM-DDThh:mm:ss.s, where
* YYYY is a four-digit year, MM a two-digit month, DD a two-digit day of
* month, hh two digits of hour (0 through 23), mm two digits of minutes,
* ss two digits of second and s one or more digits representing a
* decimal fraction of a second (ISO 8601 time standard).
*
* The method is only valid for dates from year 1972 on.
***************************************************************************/
void GTime::utc(const std::string& time)
{
// Define number of days per month
static int daymonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
// Analyse the UTC string
long year = 0;
int month = 0;
long day = 0;
long hour = 0;
long minute = 0;
double second = 0.0;
int n = sscanf(time.c_str(), "%ld-%d-%ldT%ld:%ld:%lg",
&year, &month, &day, &hour, &minute, &second);
// Adjust number of days per month for leap years
if (is_leap_year(year)) {
daymonth[1] = 29;
}
else {
daymonth[1] = 28;
}
// Check time string
if (n != 3 && n != 6) {
std::string msg = "Invalid time string \""+time+"\" encountered. "
"Please specify the time in the format YYYY-MM-DD "
"or YYYY-MM-DDThh:mm:ss.s.";
throw GException::invalid_argument(G_UTC, msg);
}
if (year < 1000 || year > 9999) {
std::string msg = "Invalid year "+gammalib::str(year)+" specified. "
"The year needs to be a four-digit year.";
throw GException::invalid_argument(G_UTC, msg);
}
if (month < 1 || month > 12) {
std::string msg = "Invalid month "+gammalib::str(month)+" specified. "
"The month needs to be comprised between 01 and 12";
throw GException::invalid_argument(G_UTC, msg);
}
if (day < 1 || day > daymonth[month-1]) {
std::string msg = "Invalid day "+gammalib::str(day)+" specified. "
"The day for month "+gammalib::str(month)+" needs "
"to be comprised between 01 and "+
gammalib::str(daymonth[month-1])+".";
throw GException::invalid_argument(G_UTC, msg);
}
if (hour < 0 || hour > 23) {
std::string msg = "Invalid hour "+gammalib::str(hour)+" specified. "
"The hour needs to be comprised between 00 and 23";
throw GException::invalid_argument(G_UTC, msg);
}
if (minute < 0 || minute > 59) {
std::string msg = "Invalid minute "+gammalib::str(minute)+" specified. "
"The minute needs to be comprised between 00 and 59";
throw GException::invalid_argument(G_UTC, msg);
}
if (second < 0 || second >= 60.0) {
std::string msg = "Invalid second "+gammalib::str(second)+" specified. "
"The second needs to be comprised between 00 and <60";
throw GException::invalid_argument(G_UTC, msg);
}
// Compute MJD day
month--;
for (int i = 0; i < month; ++i) { // Add days passed per month
day += daymonth[i];
}
day += (year - 1972) * 365 - 1; // Add days passed per year
day += (year - 1969) / 4; // Add leap days passed (every 4 years)
day -= (year - 1901) / 100; // Add leap days passed (not every 100 years)
day += (year - 1601) / 400; // Add leap days passed (every 400 years)
day += 41317; // Add MJD at 1972
// Compute MJD fraction (UTC)
double fraction = ((double)hour * 3600.0 + (double)minute * 60.0 + second) *
gammalib::sec2day;
// Compute MJD (UTC)
double mjd = (double)day + fraction;
// Get conversion from UTC to TAI to TT
double correction = leap_seconds(mjd) + gammalib::tai2tt;
// Convert MJD from UTC to TT
mjd += correction * gammalib::sec2day;
// Set time
this->mjd(mjd);
// Return
return;
}
/***********************************************************************//**
* @brief Set time given in specified reference
*
* @param[in] time Time in given reference system.
* @param[in] ref Reference system.
*
* Set the time to a value given in a specific reference system.
***************************************************************************/
void GTime::set(const double& time, const GTimeReference& ref)
{
// Convert time to specified time unit
m_time = time * ref.unitseconds();
// Compute time offset in seconds
double offset = (mjd_ref - ref.mjdref()) * gammalib::sec_in_day;
// Subtract time offset in seconds
m_time -= offset;
// Add leap seconds in case that time was given in UTC time system
if (gammalib::toupper(ref.timesys()) == "UTC") {
// Add leap seconds and offset between TAI and TT time system
m_time += leap_seconds(this->mjd());
m_time += gammalib::tai2tt;
} // endif: Time was given in UTC time system
// Return
return;
}
/***********************************************************************//**
* @brief Returns native time reference
*
* @return Native time reference.
*
* Returns the native GammaLib time reference. The GammaLib native time
* reference (i.e. time=0) is defined as January 1, 2010, 00:00:00 (TT).
* The time system is Terrestrial Time (TT). Time is stored in seconds.
***************************************************************************/
GTimeReference GTime::reference(void) const
{
// Allocate native time reference
GTimeReference reference(mjd_ref, "s", "TT", "LOCAL");
// Return reference
return reference;
}
/***********************************************************************//**
* @brief Print time
*
* @param[in] chatter Chattiness (defaults to NORMAL).
* @return String containing time in seconds in native reference.
*
* Prints time in seconds in the native reference.
***************************************************************************/
std::string GTime::print(const GChatter& chatter) const
{
// Initialise result string
std::string result;
// Continue only if chatter is not silent
if (chatter != SILENT) {
// Append time
result.append(gammalib::str(m_time)+" s (TT)");
} // endif: chatter was not silent
// Return
return result;
}
/*==========================================================================
= =
= Private methods =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Initialise class members
***************************************************************************/
void GTime::init_members(void)
{
// Initialise members
m_time = 0.0;
// Return
return;
}
/***********************************************************************//**
* @brief Copy class members
*
* @param[in] time Time.
***************************************************************************/
void GTime::copy_members(const GTime& time)
{
// Copy time
m_time = time.m_time;
// Return
return;
}
/***********************************************************************//**
* @brief Delete class members
***************************************************************************/
void GTime::free_members(void)
{
// Return
return;
}
/***********************************************************************//**
* @brief Returns number of leap seconds for a given MJD
*
* @param[in] mjd Modified Julian Day.
* @return Number of lead seconds.
*
* Return the number of leap seconds for a given MJD specified in the UTC
* time system. This method returns valid number of leap seconds for the
* years 1972-2015.
*
* See http://www.nist.gov/pml/div688/grp50/leapsecond.cfm for a table of
* leap seconds.
***************************************************************************/
double GTime::leap_seconds(const double& mjd) const
{
// Leap second table from 1972 on
// see http://www.nist.gov/pml/div688/grp50/leapsecond.cfm
// The first entry is 1-Jan-1972
const long leapsmjd[] = {41317, // 1972-01-01
41498, // 1972-06-30
41682, // 1972-12-31
42047, // 1973-12-31
42412, // 1974-12-31
42777, // 1975-12-31
43143, // 1976-12-31
43508, // 1977-12-31
43873, // 1978-12-31
44238, // 1979-12-31
44785, // 1981-06-30
45150, // 1982-06-30
45515, // 1983-06-30
46246, // 1985-06-30
47160, // 1987-12-31
47891, // 1989-12-31
48256, // 1990-12-31
48803, // 1992-06-30
49168, // 1993-06-30
49533, // 1994-06-30
50082, // 1995-12-31
50629, // 1997-06-30
51178, // 1998-12-31
53735, // 2005-12-31
54831, // 2008-12-31
56108, // 2012-06-30
57204}; // 2015-07-01
const double leapsecs[] = {10.0, 11.0, 12.0, 13.0, 14.0,
15.0, 16.0, 17.0, 18.0, 19.0,
20.0, 21.0, 22.0, 23.0, 24.0,
25.0, 26.0, 27.0, 28.0, 29.0,
30.0, 31.0, 32.0, 33.0, 34.0,
35.0, 36.0};
const int n_leapsecs = sizeof(leapsmjd)/sizeof(long);
// Extract MJD day and MJD fraction
long day = (long)mjd;
//double fraction = mjd - (double)day;
// Find the leap second MJD that is equal or larger to the specified
// day
int i = n_leapsecs - 1;
while ((day <= leapsmjd[i]) && i > 0) {
i--;
}
// Return leap seconds
return (leapsecs[i]);
}