/*************************************************************************** * GTime.cpp - Time class * * ----------------------------------------------------------------------- * * copyright (C) 2010-2019 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 // std::memcpy #include #include "GTools.hpp" #include "GMath.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_SECS_GET "GTime::secs(std::string&)" #define G_SECS_SET "GTime::secs(double&, std::string&)" #define G_UTC "GTime::utc(std::string&)" #define G_UTC_GET "GTime::utc(int&)" /* __ 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 string. * * @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 Time constructor * * @param[in] time Time string in given reference system. * @param[in] ref Reference system. * * Constructs a GTime object by setting the time to a string value. See the * set(std::string&, GTimeReference&) method for valid time strings. ***************************************************************************/ GTime::GTime(const std::string& time, const GTimeReference& ref) { // Initialise private members init_members(); // Set time set(time, ref); // Return return; } /***********************************************************************//** * @brief Time constructor * * @param[in] time Time string. * * Constructs a GTime object by setting the time to a string value. See the * set(const std::string& time) method for valid time strings. ***************************************************************************/ GTime::GTime(const std::string& time) { // Initialise private members init_members(); // Set time set(time); // 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) * * @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::sec2day + jd_ref; // Return Julian Days return jd; } /***********************************************************************//** * @brief Return time in Julian Days for time system * * @param[in] timesys Time system (one of "TT", "TAI", "UTC") * @return Time in Julian Days (days). * * Returns the time in Julian Days (JD) for the specified time system. ***************************************************************************/ double GTime::jd(const std::string& timesys) const { // Convert time to Julian Days double jd = secs(timesys) * gammalib::sec2day + jd_ref; // Return Julian Days return jd; } /***********************************************************************//** * @brief Return time in Modified Julian Days (TT) * * @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 Modified Julian Days double mjd = m_time * gammalib::sec2day + mjd_ref; // Return Modified Julian Days return mjd; } /***********************************************************************//** * @brief Return time in Modified Julian Days for time system * * @param[in] timesys Time system (one of "TT", "TAI", "UTC") * @return Time in Modified Julian Days (days). * * Returns the time in Modified Julian Days (JD) for the specified time * system. ***************************************************************************/ double GTime::mjd(const std::string& timesys) const { // Convert time to Modified Julian Days double mjd = secs(timesys) * gammalib::sec2day + mjd_ref; // Return Modified Julian Days return mjd; } /***********************************************************************//** * @brief Return time in seconds in native reference for time system * * @param[in] timesys Time system (one of "TT", "TAI", "UTC") * @return Time (seconds). ***************************************************************************/ double GTime::secs(const std::string& timesys) const { // Initialise time double time; // If system is TT then return the stored time ... if (timesys == "TT") { time = m_time; } // ... otherwise if the system if TAI then subtract an offset ... else if (timesys == "TAI") { time = m_time - gammalib::tai2tt; } // ... otherwise if the system is UTC then convert the time from TT to TAI // and subtract the leap seconds. The repeated calling of the leap second // method is a kluge to converge as the argument is given in the UTC system // but upon start we're in the TAI system. else if (timesys == "UTC") { time = m_time - gammalib::tai2tt; // Time in TAI double mjd = time * gammalib::sec2day + mjd_ref; double leaps = leap_seconds(mjd); leaps = leap_seconds(mjd - leaps * gammalib::sec2day); leaps = leap_seconds(mjd - leaps * gammalib::sec2day); time -= leaps; } // ... otherwise throw an exception else { std::string msg = "Unknown time system \""+timesys+"\". Either specify " "\"TT\", \"TAI\" or \"UTC\"."; throw GException::invalid_argument(G_SECS_GET, msg); } // Return time return time; } /***********************************************************************//** * @brief Return time in days in native reference (TT) * * @return Time in native reference (days). ***************************************************************************/ double GTime::days(void) const { // Return time return (m_time * gammalib::sec2day); } /***********************************************************************//** * @brief Return time in days in native reference for time system * * @param[in] timesys Time system (one of "TT", "TAI", "UTC") * @return Time in native reference (days). ***************************************************************************/ double GTime::days(const std::string& timesys) const { // Return time return (secs(timesys) * gammalib::sec2day); } /***********************************************************************//** * @brief Return Julian epoch in native reference (TT) * * @return Julian epoch (years). ***************************************************************************/ double GTime::julian_epoch(void) const { // Compute Julian epoch double julian_epoch = 2000.0 + (jd() - 2451545.0) / 365.25; // Return Julian epoch return (julian_epoch); } /***********************************************************************//** * @brief Return Julian epoch in native reference for time system * * @param[in] timesys Time system (one of "TT", "TAI", "UTC") * @return Julian epoch (years). ***************************************************************************/ double GTime::julian_epoch(const std::string& timesys) const { // Compute Julian epoch double julian_epoch = 2000.0 + (jd(timesys) - 2451545.0) / 365.25; // Return Julian epoch return (julian_epoch); } /***********************************************************************//** * @brief Return time as string in UTC time system * * @param[in] precision Digits of precision to show in the seconds field * @return Time as string in UTC time system. * * Returns time in the format YYYY-MM-DDThh:mm:ss(.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(.ss...) * two digits of second (ISO 8601 time standard). In case that * @p precision > 0 digits in the second after the comma will be returned. * * The method is only valid for dates from year 1972 on. ***************************************************************************/ std::string GTime::utc(const int& precision) const { // Check argument if (precision < 0) { std::string msg = "Invalid precision \""+gammalib::str(precision)+"\"" " encountered. Please specify a precision >= 0."; throw GException::invalid_argument(G_UTC_GET, msg); } // 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; // Get margin for computation. We add the margin to the seconds and // subtract it later to avoid rounding to 60 seconds double margin = 0.5; if (precision > 0) { switch (precision) { case 1: margin = 0.05; break; case 2: margin = 0.005; break; case 3: margin = 0.0005; break; default: for (int i = 0; i < precision; ++i) { margin *= 0.1; } break; } } // Compute time in day double second = fraction * gammalib::sec_in_day + margin; 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 -= margin; 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++; // Calculate the width of the seconds' pattern, plus two for the integer // seconds, plus one if a decimal is needed int sec_width = precision + 2; if (precision > 0) { sec_width += 1; } // Format pattern for variable seconds precision char utc_pattern[50]; sprintf(utc_pattern, "%%4.4d-%%2.2d-%%2.2dT%%2.2d:%%2.2d:%%0%d.0%df", sec_width, precision); // Create string char utc[32]; sprintf(utc, utc_pattern, year, month, day, hour, minute, second); // Return return (std::string(utc)); } /***********************************************************************//** * @brief Return Greenwich mean sidereal time in hours in a day * * @return Greenwich mean sidereal time (hours). * * See http://aa.usno.navy.mil/faq/docs/GAST.php ***************************************************************************/ double GTime::gmst(void) const { // Get days since 1 January 2000, 12h in Universal Time double d = days("UTC") + 3652.50076602; // Compute Greenwich mean sidereal time in hours double gmst = 18.697374558 + 24.06570982441908 * d; // Put into [0,24] gmst -= floor(gmst/24.0) * 24.0; // Return Greenwich mean sidereal time in hours return gmst; } /***********************************************************************//** * @brief Return Greenwich apparent sidereal time in hours in a day * * @return Greenwich apparent sidereal time (hours). * * See http://aa.usno.navy.mil/faq/docs/GAST.php ***************************************************************************/ double GTime::gast(void) const { // Get days since 1 January 2000, 12h in Universal Time double d = days("UTC") + 3652.50076602; // Compute longitude of the ascending node of the Moon in degrees double Omega = 125.04 - 0.052954 * d; // Compute mean longitude of the Sun in degrees double L = 280.47 + 0.98565 * d; // Compute nutation in longitude in hours double DeltaPsi = -0.000319 * std::sin(Omega * gammalib::deg2rad) - 0.000024 * std::sin(2.0 * L * gammalib::deg2rad); // Compute the obliquity in degrees double epsilon = 23.4393 - 0.0000004 * d; // Compute equation of equinoxes double eqeq = DeltaPsi * std::cos(epsilon * gammalib::deg2rad); // Compute Greenwich apparent sidereal time in hours double gast = gmst() + eqeq; // Put into [0,24] gast -= floor(gast/24.0) * 24.0; // Return Greenwich apparent sidereal time in hours return gast; } /***********************************************************************//** * @brief Return local mean sidereal time in hours in a day * * @param[in] geolon Geographic longitude West of Greenwich (degrees). * @return Local mean sidereal time (hours). * * See http://aa.usno.navy.mil/faq/docs/GAST.php ***************************************************************************/ double GTime::lmst(const double& geolon) const { // Compute local mean siderial time double lmst = gmst() - geolon/15.0; // Put into [0,24] lmst -= floor(lmst/24.0) * 24.0; // Return local mean sidereal time in hours return lmst; } /***********************************************************************//** * @brief Return local apparent sidereal time in hours in a day * * @param[in] geolon Geographic longitude West of Greenwich (degrees). * @return Local apparent sidereal time (hours). * * See http://aa.usno.navy.mil/faq/docs/GAST.php ***************************************************************************/ double GTime::last(const double& geolon) const { // Compute local apparent siderial time double last = gast() - geolon/15.0; // Put into [0,24] last -= floor(last/24.0) * 24.0; // Return local mean sidereal time in hours return last; } /***********************************************************************//** * @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 // ... otherwise, if time is requested in TAI system then subtract the // TAI offset else if (gammalib::toupper(ref.timesys()) == "TAI") { time -= gammalib::tai2tt; } // 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 in native reference (TT) * * @param[in] time Time in Julian Days (TT) (days). ***************************************************************************/ void GTime::jd(const double& time) { // Convert time from Julian Days to seconds in native reference m_time = (time - jd_ref) * gammalib::sec_in_day; // Return return; } /***********************************************************************//** * @brief Set time in Julian Days in native reference for time system * * @param[in] time Time in Julian Days (days). * @param[in] timesys Time system (one of "TT", "TAI", "UTC") ***************************************************************************/ void GTime::jd(const double& time, const std::string& timesys) { // Convert time from Julian Days to seconds in native reference double seconds = (time - jd_ref) * gammalib::sec_in_day; // Set time according to the specified time system secs(seconds, timesys); // Return return; } /***********************************************************************//** * @brief Set time in Modified Julian Days in native reference (TT) * * @param[in] time Time in Modified Julian Days (TT) (days). ***************************************************************************/ void GTime::mjd(const double& time) { // Convert time from Modified Julian Days to native (seconds) m_time = (time - mjd_ref) * gammalib::sec_in_day; // Return return; } /***********************************************************************//** * @brief Set time in Modified Julian Days in native reference for time * system * * @param[in] time Time in Modified Julian Days (days). * @param[in] timesys Time system (one of "TT", "TAI", "UTC") ***************************************************************************/ void GTime::mjd(const double& time, const std::string& timesys) { // Convert time from Modified Julian Days to seconds in native reference double seconds = (time - mjd_ref) * gammalib::sec_in_day; // Set time according to the specified time system secs(seconds, timesys); // Return return; } /***********************************************************************//** * @brief Set time in seconds in native reference for time system * * @param[in] seconds Time in native reference (seconds). * @param[in] timesys Time system (one of "TT", "TAI", "UTC") ***************************************************************************/ void GTime::secs(const double& seconds, const std::string& timesys) { // If system is TT then simply set time ... if (timesys == "TT") { m_time = seconds; } // ... otherwise if the system if TAI then add an offset ... else if (timesys == "TAI") { m_time = seconds + gammalib::tai2tt; } // ... otherwise if the system is UTC then convert the time from TT to TAI // and add the leap seconds ... else if (timesys == "UTC") { double mjd = seconds * gammalib::sec2day + mjd_ref; // Time in UTC double leaps = leap_seconds(mjd); m_time = seconds + gammalib::tai2tt + leaps; } // ... otherwise throw an exception else { std::string msg = "Unknown time system \""+timesys+"\". Either specify " "\"TT\", \"TAI\" or \"UTC\"."; throw GException::invalid_argument(G_SECS_SET, msg); } // Return return; } /***********************************************************************//** * @brief Set time in days in native reference (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 in days in native reference for time system * * @param[in] days Time (TT) (days). * @param[in] timesys Time system (one of "TT", "TAI", "UTC") ***************************************************************************/ void GTime::days(const double& days, const std::string& timesys) { // Convert time from days to seconds in native reference double seconds = days * gammalib::sec_in_day; // Set time according to the specified time system secs(seconds, timesys); // 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 // ... otherwise if system is TAI system then add TAI offset else if (gammalib::toupper(ref.timesys()) == "TAI") { m_time += gammalib::tai2tt; } // Return return; } /***********************************************************************//** * @brief Set time from string * * @param[in] time Time string. * @param[in] ref Reference system. * * Sets the time from a string for a given reference system. The following * strings are valid: * * "2016-10-05T15:08:56" (UTC string) * "1800.0" (MET seconds in specified reference system) * "1800.0 (TT)" (MET seconds in specified reference, TT system) * "1800.0 (UTC)" (MET seconds in specified reference, UTC time system) * "1800.0 (TAI)" (MET seconds in specified reference, TAI time system) * "MJD 54609" (Modified Julian Days, TT system) * "MJD 54609 (TT)" (Modified Julian Days, TT system) * "MJD 54609 (UTC)" (Modified Julian Days, UTC system) * "MJD 54609 (TAI)" (Modified Julian Days, TAI system) * "JD 54609" (Julian Days, TT system) * "JD 54609 (TT)" (Julian Days, TT system) * "JD 54609 (UTC)" (Julian Days, UTC system) * "JD 54609 (TAI)" (Julian Days, TAI system) * * If any other string is encountered, the numerical value is interpreted as * time is seconds using the TT system. * * Note that the TT, UTC or TAI attributes overwrite the values contained in * the specified reference system. The reference system is only used to * convert MET times in seconds. ***************************************************************************/ void GTime::set(const std::string& time, const GTimeReference& ref) { // Strip any whitespace from string and convert it to upper case std::string str = gammalib::toupper(gammalib::strip_whitespace(time)); // First check if the string is a UTC string long year = 0; int month = 0; long day = 0; long hour = 0; long minute = 0; double second = 0.0; int n = sscanf(str.c_str(), "%ld-%d-%ldT%ld:%ld:%lg", &year, &month, &day, &hour, &minute, &second); if (n == 3 || n == 6) { utc(str); } // ... otherwise check for Modified Julian Days else if (str.find("MJD") == 0) { double timeval = extract_timeval(str); std::string timesys = extract_timesys(str); if (timesys.empty()) { timesys = "TT"; } mjd(timeval, timesys); } // ... otherwise check for Julian Days else if (str.find("JD") == 0) { double timeval = extract_timeval(str); std::string timesys = extract_timesys(str); if (timesys.empty()) { timesys = "TT"; } jd(timeval, timesys); } // ... otherwise take time as seconds and use the specified reference // system. If no TT, UTC or TAI arrtibutes are specified use the value // specified by the reference system. else { double timeval = extract_timeval(str); std::string timesys = extract_timesys(str); if (timesys.empty()) { timesys = ref.timesys(); } GTimeReference timeref(ref.mjdref(), ref.timeunit(), timesys, ref.timeref()); set(timeval, timeref); } // Return return; } /***********************************************************************//** * @brief Set time to current time * * Sets time to current time. ***************************************************************************/ void GTime::now(void) { // Allocate variables struct std::tm timeStruct; std::time_t now; char buffer[100]; // Get time now = std::time(NULL); #ifdef HAVE_GMTIME_R std::gmtime_r(&now, &timeStruct); #else std::memcpy(&timeStruct, gmtime(&now), sizeof(struct tm)); #endif // Write message type, time and task name to buffer std::sprintf(buffer, "%04d-%02d-%02dT%02d:%02d:%02d", timeStruct.tm_year + 1900, timeStruct.tm_mon + 1, timeStruct.tm_mday, timeStruct.tm_hour, timeStruct.tm_min, timeStruct.tm_sec); // Build string from buffer std::string date = buffer; // Set UTC time utc(date); // 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. * @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 in UTC time system. * @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-2017. * * 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 57203, // 2015-06-30 57753}; // 2016-12-31 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, 37.0}; const int n_leapsecs = sizeof(leapsmjd)/sizeof(long); // Extract MJD day and MJD fraction long day = (long)mjd; // 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]); } /***********************************************************************//** * @brief Extract time value from time string * * @param[in] time Time string. * @return Time value. * * Extracts the time value from a time string. The method strips any prefix * such as "MJD" or "JD" and any suffix starting with a left parentheses "(" * and converts the remainder into a double precision value. ***************************************************************************/ double GTime::extract_timeval(const std::string& time) const { // Find time size_t length = time.length(); size_t start = time.find_first_of("0123456789+-."); size_t stop = time.find("("); // If there is a stop position then set length to stop if (stop != std::string::npos) { length = stop; } // Reduce length by start if (start != std::string::npos) { length -= start; } // Get substring containing the time value std::string value = time.substr(start, length); // Convert value into double precision double result = gammalib::todouble(value); // Return result return result; } /***********************************************************************//** * @brief Extract time system from time string * * @param[in] time Time string. * @return Time system. * * Extracts the time system from a time string. Valid time systems are: * * "(TT)" (TT system) * "(UTC)" (UTC system) * "(TAI)" (TAI system) * * If no time system is found a blank string is returned. ***************************************************************************/ std::string GTime::extract_timesys(const std::string& time) const { // Initialise time system with blank string std::string timesys = ""; // Strip any whitespace from string and convert it to upper case std::string str = gammalib::toupper(gammalib::strip_whitespace(time)); // Check for UTC if (str.find("(UTC)") != std::string::npos) { timesys = "UTC"; } else if (str.find("(TAI)") != std::string::npos) { timesys = "TAI"; } else if (str.find("(TT)") != std::string::npos) { timesys = "TT"; } // Return time system return timesys; }