GammaLib 2.2.0.dev
Loading...
Searching...
No Matches
GTime.cpp
Go to the documentation of this file.
1/***************************************************************************
2 * GTime.cpp - Time class *
3 * ----------------------------------------------------------------------- *
4 * copyright (C) 2010-2026 by Juergen Knoedlseder *
5 * ----------------------------------------------------------------------- *
6 * *
7 * This program is free software: you can redistribute it and/or modify *
8 * it under the terms of the GNU General Public License as published by *
9 * the Free Software Foundation, either version 3 of the License, or *
10 * (at your option) any later version. *
11 * *
12 * This program is distributed in the hope that it will be useful, *
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15 * GNU General Public License for more details. *
16 * *
17 * You should have received a copy of the GNU General Public License *
18 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
19 * *
20 ***************************************************************************/
21/**
22 * @file GTime.cpp
23 * @brief Time class implementation
24 * @author Juergen Knoedlseder
25 */
26
27/* __ Includes ___________________________________________________________ */
28#ifdef HAVE_CONFIG_H
29#include <config.h>
30#endif
31#include <ctime>
32#include <cstring> // std::memcpy
33#include <cstdio>
34#include "GTools.hpp"
35#include "GMath.hpp"
36#include "GException.hpp"
37#include "GTime.hpp"
38#include "GTimeReference.hpp"
39
40/* __ Constants __________________________________________________________ */
41const double mjd_ref = 55197.000766018518519; //!< MJD of time=0
42const double jd_ref = mjd_ref + 2400000.5; //!< JD of time=0
43
44/* __ Method name definitions ____________________________________________ */
45#define G_CONSTRUCT "GTime::GTime(double&, std::string&)"
46#define G_SECS_GET "GTime::secs(std::string&)"
47#define G_SECS_SET "GTime::secs(double&, std::string&)"
48#define G_UTC "GTime::utc(std::string&)"
49#define G_UTC_GET "GTime::utc(int&)"
50
51/* __ Macros _____________________________________________________________ */
52
53/* __ Coding definitions _________________________________________________ */
54
55/* __ Debug definitions __________________________________________________ */
56
57
58/*==========================================================================
59 = =
60 = Constructors/destructors =
61 = =
62 ==========================================================================*/
63
64/***********************************************************************//**
65 * @brief Void constructor
66 ***************************************************************************/
68{
69 // Initialise private members
71
72 // Return
73 return;
74}
75
76
77/***********************************************************************//**
78 * @brief Copy constructor
79 *
80 * @param[in] time Time.
81 ***************************************************************************/
82GTime::GTime(const GTime& time)
83{
84 // Initialise private members
86
87 // Copy members
88 copy_members(time);
89
90 // Return
91 return;
92}
93
94
95/***********************************************************************//**
96 * @brief Time constructor
97 *
98 * @param[in] time Time value in TT (seconds or days).
99 * @param[in] unit Time unit string.
100 *
101 * @exception GException::invalid_argument
102 * Invalid time unit specified.
103 *
104 * Constructs a GTime object by setting the time in the native reference
105 * in the TT time system in units of seconds (default) or days.
106 ***************************************************************************/
107GTime::GTime(const double& time, const std::string& unit)
108{
109 // Initialise private members
110 init_members();
111
112 // Set time according to timeunit string
113 std::string timeunit = gammalib::tolower(unit);
114 if (timeunit == "d" || timeunit == "day" || timeunit == "days") {
115 days(time);
116 }
117 else if (timeunit == "s" || timeunit == "sec" || timeunit == "secs") {
118 secs(time);
119 }
120 else {
121 std::string msg = "Invalid time unit \""+unit+"\" specified. Please "
122 "specify one of \"d\", \"day\", \"days\", \"s\", "
123 "\"sec\" or \"secs\"";
125 }
126
127 // Return
128 return;
129}
130
131
132/***********************************************************************//**
133 * @brief Time constructor
134 *
135 * @param[in] time Time in given reference system.
136 * @param[in] ref Reference system.
137 *
138 * Constructs a GTime object by setting the time to a value given in a
139 * specific reference system.
140 ***************************************************************************/
141GTime::GTime(const double& time, const GTimeReference& ref)
142{
143 // Initialise private members
144 init_members();
145
146 // Set time
147 set(time, ref);
148
149 // Return
150 return;
151}
152
153
154/***********************************************************************//**
155 * @brief Time constructor
156 *
157 * @param[in] time Time string in given reference system.
158 * @param[in] ref Reference system.
159 *
160 * Constructs a GTime object by setting the time to a string value. See the
161 * set(std::string&, GTimeReference&) method for valid time strings.
162 ***************************************************************************/
163GTime::GTime(const std::string& time, const GTimeReference& ref)
164{
165 // Initialise private members
166 init_members();
167
168 // Set time
169 set(time, ref);
170
171 // Return
172 return;
173}
174
175
176/***********************************************************************//**
177 * @brief Time constructor
178 *
179 * @param[in] time Time string.
180 *
181 * Constructs a GTime object by setting the time to a string value. See the
182 * set(const std::string& time) method for valid time strings.
183 ***************************************************************************/
184GTime::GTime(const std::string& time)
185{
186 // Initialise private members
187 init_members();
188
189 // Set time
190 set(time);
191
192 // Return
193 return;
194}
195
196
197/***********************************************************************//**
198 * @brief Destructor
199 ***************************************************************************/
201{
202 // Free members
203 free_members();
204
205 // Return
206 return;
207}
208
209
210/*==========================================================================
211 = =
212 = Operators =
213 = =
214 ==========================================================================*/
215
216/***********************************************************************//**
217 * @brief Assignment operator
218 *
219 * @param[in] time Time.
220 * @return Time.
221 ***************************************************************************/
223{
224 // Execute only if object is not identical
225 if (this != &time) {
226
227 // Free members
228 free_members();
229
230 // Initialise private members for clean destruction
231 init_members();
232
233 // Copy members
234 copy_members(time);
235
236 } // endif: object was not identical
237
238 // Return
239 return *this;
240}
241
242
243/*==========================================================================
244 = =
245 = Public methods =
246 = =
247 ==========================================================================*/
248
249/***********************************************************************//**
250 * @brief Clear time
251 ***************************************************************************/
252void GTime::clear(void)
253{
254 // Free members
255 free_members();
256
257 // Initialise members
258 init_members();
259
260 // Return
261 return;
262}
263
264
265/***********************************************************************//**
266 * @brief Clone time
267 *
268 * @return Pointer to deep copy of time.
269 ***************************************************************************/
270GTime* GTime::clone(void) const
271{
272 // Clone this image
273 return new GTime(*this);
274}
275
276
277/***********************************************************************//**
278 * @brief Return time in Julian Days (TT)
279 *
280 * @return Time in Julian Days (TT) (days).
281 *
282 * Returns the time in Julian Days (JD) in the Terrestrial Time (TT) system.
283 ***************************************************************************/
284double GTime::jd(void) const
285{
286 // Convert time from MET to JD
287 double jd = m_time * gammalib::sec2day + jd_ref;
288
289 // Return Julian Days
290 return jd;
291}
292
293
294/***********************************************************************//**
295 * @brief Return time in Julian Days for time system
296 *
297 * @param[in] timesys Time system (one of "TT", "TAI", "UTC")
298 * @return Time in Julian Days (days).
299 *
300 * Returns the time in Julian Days (JD) for the specified time system.
301 ***************************************************************************/
302double GTime::jd(const std::string& timesys) const
303{
304 // Convert time to Julian Days
305 double jd = secs(timesys) * gammalib::sec2day + jd_ref;
306
307 // Return Julian Days
308 return jd;
309}
310
311
312/***********************************************************************//**
313 * @brief Return time in Modified Julian Days (TT)
314 *
315 * @return Time in Modified Julian Days (TT) (days).
316 *
317 * Returns the time in Modified Julian Days (MJD) in the Terrestrial Time
318 * (TT) system.
319 ***************************************************************************/
320double GTime::mjd(void) const
321{
322 // Convert time to Modified Julian Days
323 double mjd = m_time * gammalib::sec2day + mjd_ref;
324
325 // Return Modified Julian Days
326 return mjd;
327}
328
329
330/***********************************************************************//**
331 * @brief Return time in Modified Julian Days for time system
332 *
333 * @param[in] timesys Time system (one of "TT", "TAI", "UTC")
334 * @return Time in Modified Julian Days (days).
335 *
336 * Returns the time in Modified Julian Days (JD) for the specified time
337 * system.
338 ***************************************************************************/
339double GTime::mjd(const std::string& timesys) const
340{
341 // Convert time to Modified Julian Days
342 double mjd = secs(timesys) * gammalib::sec2day + mjd_ref;
343
344 // Return Modified Julian Days
345 return mjd;
346}
347
348
349/***********************************************************************//**
350 * @brief Return time in seconds in native reference for time system
351 *
352 * @param[in] timesys Time system (one of "TT", "TAI", "UTC")
353 * @return Time (seconds).
354 ***************************************************************************/
355double GTime::secs(const std::string& timesys) const
356{
357 // Initialise time
358 double time;
359
360 // If system is TT then return the stored time ...
361 if (timesys == "TT") {
362 time = m_time;
363 }
364
365 // ... otherwise if the system if TAI then subtract an offset ...
366 else if (timesys == "TAI") {
367 time = m_time - gammalib::tai2tt;
368 }
369
370 // ... otherwise if the system is UTC then convert the time from TT to TAI
371 // and subtract the leap seconds. The repeated calling of the leap second
372 // method is a kluge to converge as the argument is given in the UTC system
373 // but upon start we're in the TAI system.
374 else if (timesys == "UTC") {
375 time = m_time - gammalib::tai2tt; // Time in TAI
376 double mjd = time * gammalib::sec2day + mjd_ref;
377 double leaps = leap_seconds(mjd);
378 leaps = leap_seconds(mjd - leaps * gammalib::sec2day);
379 leaps = leap_seconds(mjd - leaps * gammalib::sec2day);
380 time -= leaps;
381 }
382
383 // ... otherwise throw an exception
384 else {
385 std::string msg = "Unknown time system \""+timesys+"\". Either specify "
386 "\"TT\", \"TAI\" or \"UTC\".";
388 }
389
390 // Return time
391 return time;
392}
393
394
395/***********************************************************************//**
396 * @brief Return time in days in native reference (TT)
397 *
398 * @return Time in native reference (days).
399 ***************************************************************************/
400double GTime::days(void) const
401{
402 // Return time
403 return (m_time * gammalib::sec2day);
404}
405
406
407/***********************************************************************//**
408 * @brief Return time in days in native reference for time system
409 *
410 * @param[in] timesys Time system (one of "TT", "TAI", "UTC")
411 * @return Time in native reference (days).
412 ***************************************************************************/
413double GTime::days(const std::string& timesys) const
414{
415 // Return time
416 return (secs(timesys) * gammalib::sec2day);
417}
418
419
420/***********************************************************************//**
421 * @brief Return Julian epoch in native reference (TT)
422 *
423 * @return Julian epoch (years).
424 ***************************************************************************/
425double GTime::julian_epoch(void) const
426{
427 // Compute Julian epoch
428 double julian_epoch = 2000.0 + (jd() - 2451545.0) / 365.25;
429
430 // Return Julian epoch
431 return (julian_epoch);
432}
433
434
435/***********************************************************************//**
436 * @brief Return Julian epoch in native reference for time system
437 *
438 * @param[in] timesys Time system (one of "TT", "TAI", "UTC")
439 * @return Julian epoch (years).
440 ***************************************************************************/
441double GTime::julian_epoch(const std::string& timesys) const
442{
443 // Compute Julian epoch
444 double julian_epoch = 2000.0 + (jd(timesys) - 2451545.0) / 365.25;
445
446 // Return Julian epoch
447 return (julian_epoch);
448}
449
450
451/***********************************************************************//**
452 * @brief Return time as string in UTC time system
453 *
454 * @param[in] precision Digits of precision to show in the seconds field
455 * @return Time as string in UTC time system.
456 *
457 * Returns time in the format YYYY-MM-DDThh:mm:ss(.ss...), where YYYY is a
458 * four-digit year, MM a two-digit month, DD a two-digit day of month, hh two
459 * digits of hour (0 through 23), mm two digits of minutes, and ss(.ss...)
460 * two digits of second (ISO 8601 time standard). In case that
461 * @p precision > 0 digits in the second after the comma will be returned.
462 *
463 * The method is only valid for dates from year 1972 on.
464 ***************************************************************************/
465std::string GTime::utc(const int& precision) const
466{
467 // Check argument
468 if (precision < 0) {
469 std::string msg = "Invalid precision \""+gammalib::str(precision)+"\""
470 " encountered. Please specify a precision >= 0.";
472 }
473
474 // Define number of days per month
475 static int daymonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
476
477 // Get MJD in TAI time system
478 double mjd = this->mjd() - gammalib::tai2tt * gammalib::sec2day;
479
480 // Correct for leap seconds (repeat is a kluge to converge as the
481 // argument is given in the UTC system but upon start we're in the TAI
482 // system
483 double leaps = leap_seconds(mjd) * gammalib::sec2day;
484 leaps = leap_seconds(mjd - leaps) * gammalib::sec2day;
485 leaps = leap_seconds(mjd - leaps) * gammalib::sec2day;
486 mjd -= leaps;
487
488 // Split in day and fraction
489 int day = (int)mjd;
490 double fraction = mjd - (double)day;
491
492 // Get margin for computation. We add the margin to the seconds and
493 // subtract it later to avoid rounding to 60 seconds
494 double margin = 0.5;
495 if (precision > 0) {
496 switch (precision) {
497 case 1:
498 margin = 0.05;
499 break;
500 case 2:
501 margin = 0.005;
502 break;
503 case 3:
504 margin = 0.0005;
505 break;
506 default:
507 for (int i = 0; i < precision; ++i) {
508 margin *= 0.1;
509 }
510 break;
511 }
512 }
513
514 // Compute time in day
515 double second = fraction * gammalib::sec_in_day + margin;
516 int hour = (int)second / 3600;
517 second -= hour * 3600.0;
518 int minute = (int)second / 60;
519 second -= minute * 60.0;
520 if (hour > 23) {
521 hour -= 24;
522 day++;
523 }
524 second -= margin;
525 if (second < 0.0) {
526 second = 0.0;
527 }
528
529 // Compute year and day in the year
530 int year = 1972; // Set year to 1972
531 day -= 41317; // Subtract MJD of 1-1-1972
532 day++; // Day 0 is 1 January
533 int days = days_in_year(year);
534 while (day > days) {
535 day -= days;
536 year++;
537 days = days_in_year(year);
538 }
539 while (day < 0) {
540 year--;
541 days = days_in_year(year);
542 day += days;
543 }
544
545 // Adjust number of days per month for leap years
546 if (is_leap_year(year)) {
547 daymonth[1] = 29;
548 }
549 else {
550 daymonth[1] = 28;
551 }
552
553 // Compute month and day in the month
554 int month = 0;
555 while (month < 12) {
556 if (day <= daymonth[month]) {
557 break;
558 }
559 day -= daymonth[month];
560 month++;
561 }
562 month++;
563
564 // Calculate the width of the seconds' pattern, plus two for the integer
565 // seconds, plus one if a decimal is needed
566 int sec_width = precision + 2;
567 if (precision > 0) {
568 sec_width += 1;
569 }
570
571 // Format pattern for variable seconds precision
572 char utc_pattern[50];
573 #if ((defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L) || \
574 (defined __cplusplus && __cplusplus >= 201103L))
575 std::snprintf(utc_pattern, 50,
576 "%%4.4d-%%2.2d-%%2.2dT%%2.2d:%%2.2d:%%0%d.0%df",
577 sec_width, precision);
578 #else
579 std::sprintf(utc_pattern, "%%4.4d-%%2.2d-%%2.2dT%%2.2d:%%2.2d:%%0%d.0%df",
580 sec_width, precision);
581 #endif
582
583 // Create string
584 char utc[32];
585 #if ((defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L) || \
586 (defined __cplusplus && __cplusplus >= 201103L))
587 std::snprintf(utc, 32, utc_pattern,
588 year, month, day, hour, minute, second);
589 #else
590 std::sprintf(utc, utc_pattern,
591 year, month, day, hour, minute, second);
592 #endif
593
594 // Return
595 return (std::string(utc));
596}
597
598
599/***********************************************************************//**
600 * @brief Return Greenwich mean sidereal time in hours in a day
601 *
602 * @return Greenwich mean sidereal time (hours).
603 *
604 * See http://aa.usno.navy.mil/faq/docs/GAST.php
605 ***************************************************************************/
606double GTime::gmst(void) const
607{
608 // Get days since 1 January 2000, 12h in Universal Time
609 double d = days("UTC") + 3652.50076602;
610
611 // Compute Greenwich mean sidereal time in hours
612 double gmst = 18.697374558 + 24.06570982441908 * d;
613
614 // Put into [0,24]
615 gmst -= floor(gmst/24.0) * 24.0;
616
617 // Return Greenwich mean sidereal time in hours
618 return gmst;
619}
620
621
622/***********************************************************************//**
623 * @brief Return Greenwich apparent sidereal time in hours in a day
624 *
625 * @return Greenwich apparent sidereal time (hours).
626 *
627 * See http://aa.usno.navy.mil/faq/docs/GAST.php
628 ***************************************************************************/
629double GTime::gast(void) const
630{
631 // Get days since 1 January 2000, 12h in Universal Time
632 double d = days("UTC") + 3652.50076602;
633
634 // Compute longitude of the ascending node of the Moon in degrees
635 double Omega = 125.04 - 0.052954 * d;
636
637 // Compute mean longitude of the Sun in degrees
638 double L = 280.47 + 0.98565 * d;
639
640 // Compute nutation in longitude in hours
641 double DeltaPsi = -0.000319 * std::sin(Omega * gammalib::deg2rad) -
642 0.000024 * std::sin(2.0 * L * gammalib::deg2rad);
643
644 // Compute the obliquity in degrees
645 double epsilon = 23.4393 - 0.0000004 * d;
646
647 // Compute equation of equinoxes
648 double eqeq = DeltaPsi * std::cos(epsilon * gammalib::deg2rad);
649
650 // Compute Greenwich apparent sidereal time in hours
651 double gast = gmst() + eqeq;
652
653 // Put into [0,24]
654 gast -= floor(gast/24.0) * 24.0;
655
656 // Return Greenwich apparent sidereal time in hours
657 return gast;
658}
659
660
661/***********************************************************************//**
662 * @brief Return local mean sidereal time in hours in a day
663 *
664 * @param[in] geolon Geographic longitude West of Greenwich (degrees).
665 * @return Local mean sidereal time (hours).
666 *
667 * See http://aa.usno.navy.mil/faq/docs/GAST.php
668 ***************************************************************************/
669double GTime::lmst(const double& geolon) const
670{
671 // Compute local mean siderial time
672 double lmst = gmst() - geolon/15.0;
673
674 // Put into [0,24]
675 lmst -= floor(lmst/24.0) * 24.0;
676
677 // Return local mean sidereal time in hours
678 return lmst;
679}
680
681
682/***********************************************************************//**
683 * @brief Return local apparent sidereal time in hours in a day
684 *
685 * @param[in] geolon Geographic longitude West of Greenwich (degrees).
686 * @return Local apparent sidereal time (hours).
687 *
688 * See http://aa.usno.navy.mil/faq/docs/GAST.php
689 ***************************************************************************/
690double GTime::last(const double& geolon) const
691{
692 // Compute local apparent siderial time
693 double last = gast() - geolon/15.0;
694
695 // Put into [0,24]
696 last -= floor(last/24.0) * 24.0;
697
698 // Return local mean sidereal time in hours
699 return last;
700}
701
702
703/***********************************************************************//**
704 * @brief Return time in specified reference
705 *
706 * @return Time in specified reference.
707 *
708 * Convert the time from the native reference system into the specified
709 * reference system.
710 ***************************************************************************/
711double GTime::convert(const GTimeReference& ref) const
712{
713 // Retrieve time in native reference (TT in seconds)
714 double time = m_time;
715
716 // Compute time offset in seconds
717 double offset = (mjd_ref - ref.mjdref()) * gammalib::sec_in_day;
718
719 // Add time offset in seconds
720 time += offset;
721
722 // Subtract leap seconds in case that time is requested in UTC time
723 // system
724 if (gammalib::toupper(ref.timesys()) == "UTC") {
725
726 // Get MJD in TAI time system
727 double mjd = this->mjd() - gammalib::tai2tt * gammalib::sec2day;
728
729 // Get leap seconds (repeat is a kluge to converge as the
730 // argument is given in the UTC system but upon start we're in the TAI
731 // system
732 double leaps = leap_seconds(mjd);
733 leaps = leap_seconds(mjd - leaps * gammalib::sec2day);
734 leaps = leap_seconds(mjd - leaps * gammalib::sec2day);
735
736 // Subtract leap seconds and TAI offset
737 time -= (leaps + gammalib::tai2tt);
738
739 } // endif: UTC time system has been requested
740
741 // ... otherwise, if time is requested in TAI system then subtract the
742 // TAI offset
743 else if (gammalib::toupper(ref.timesys()) == "TAI") {
744 time -= gammalib::tai2tt;
745 }
746
747 // Convert to specified time unit
748 double to_unit = ref.unitseconds();
749 if (to_unit != 1.0) {
750 time /= to_unit;
751 }
752
753 // Return time
754 return time;
755}
756
757
758/***********************************************************************//**
759 * @brief Set time in Julian Days in native reference (TT)
760 *
761 * @param[in] time Time in Julian Days (TT) (days).
762 ***************************************************************************/
763void GTime::jd(const double& time)
764{
765 // Convert time from Julian Days to seconds in native reference
767
768 // Return
769 return;
770}
771
772
773/***********************************************************************//**
774 * @brief Set time in Julian Days in native reference for time system
775 *
776 * @param[in] time Time in Julian Days (days).
777 * @param[in] timesys Time system (one of "TT", "TAI", "UTC")
778 ***************************************************************************/
779void GTime::jd(const double& time, const std::string& timesys)
780{
781 // Convert time from Julian Days to seconds in native reference
782 double seconds = (time - jd_ref) * gammalib::sec_in_day;
783
784 // Set time according to the specified time system
785 secs(seconds, timesys);
786
787 // Return
788 return;
789}
790
791
792/***********************************************************************//**
793 * @brief Set time in Modified Julian Days in native reference (TT)
794 *
795 * @param[in] time Time in Modified Julian Days (TT) (days).
796 ***************************************************************************/
797void GTime::mjd(const double& time)
798{
799 // Convert time from Modified Julian Days to native (seconds)
801
802 // Return
803 return;
804}
805
806
807/***********************************************************************//**
808 * @brief Set time in Modified Julian Days in native reference for time
809 * system
810 *
811 * @param[in] time Time in Modified Julian Days (days).
812 * @param[in] timesys Time system (one of "TT", "TAI", "UTC")
813 ***************************************************************************/
814void GTime::mjd(const double& time, const std::string& timesys)
815{
816 // Convert time from Modified Julian Days to seconds in native reference
817 double seconds = (time - mjd_ref) * gammalib::sec_in_day;
818
819 // Set time according to the specified time system
820 secs(seconds, timesys);
821
822 // Return
823 return;
824}
825
826
827/***********************************************************************//**
828 * @brief Set time in seconds in native reference for time system
829 *
830 * @param[in] seconds Time in native reference (seconds).
831 * @param[in] timesys Time system (one of "TT", "TAI", "UTC")
832 ***************************************************************************/
833void GTime::secs(const double& seconds, const std::string& timesys)
834{
835 // If system is TT then simply set time ...
836 if (timesys == "TT") {
837 m_time = seconds;
838 }
839
840 // ... otherwise if the system if TAI then add an offset ...
841 else if (timesys == "TAI") {
842 m_time = seconds + gammalib::tai2tt;
843 }
844
845 // ... otherwise if the system is UTC then convert the time from TT to TAI
846 // and add the leap seconds ...
847 else if (timesys == "UTC") {
848 double mjd = seconds * gammalib::sec2day + mjd_ref; // Time in UTC
849 double leaps = leap_seconds(mjd);
850 m_time = seconds + gammalib::tai2tt + leaps;
851 }
852
853 // ... otherwise throw an exception
854 else {
855 std::string msg = "Unknown time system \""+timesys+"\". Either specify "
856 "\"TT\", \"TAI\" or \"UTC\".";
858 }
859
860 // Return
861 return;
862}
863
864
865/***********************************************************************//**
866 * @brief Set time in days in native reference (TT)
867 *
868 * @param[in] days Time (TT) (days).
869 ***************************************************************************/
870void GTime::days(const double& days)
871{
872 // Set time
874
875 // Return
876 return;
877}
878
879
880/***********************************************************************//**
881 * @brief Set time in days in native reference for time system
882 *
883 * @param[in] days Time (TT) (days).
884 * @param[in] timesys Time system (one of "TT", "TAI", "UTC")
885 ***************************************************************************/
886void GTime::days(const double& days, const std::string& timesys)
887{
888 // Convert time from days to seconds in native reference
889 double seconds = days * gammalib::sec_in_day;
890
891 // Set time according to the specified time system
892 secs(seconds, timesys);
893
894 // Return
895 return;
896}
897
898
899/***********************************************************************//**
900 * @brief Set time as string in UTC time system
901 *
902 * @param[in] time Time string (UTC).
903 *
904 * @exception GException::invalid_argument
905 * Invalid time string specified.
906 *
907 * The time has to be given in the format YYYY-MM-DDThh:mm:ss.s, where
908 * YYYY is a four-digit year, MM a two-digit month, DD a two-digit day of
909 * month, hh two digits of hour (0 through 23), mm two digits of minutes,
910 * ss two digits of second and s one or more digits representing a
911 * decimal fraction of a second (ISO 8601 time standard).
912 *
913 * The method is only valid for dates from year 1972 on.
914 ***************************************************************************/
915void GTime::utc(const std::string& time)
916{
917 // Define number of days per month
918 static int daymonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
919
920 // Analyse the UTC string
921 long year = 0;
922 int month = 0;
923 long day = 0;
924 long hour = 0;
925 long minute = 0;
926 double second = 0.0;
927 int n = sscanf(time.c_str(), "%ld-%d-%ldT%ld:%ld:%lg",
928 &year, &month, &day, &hour, &minute, &second);
929
930 // Adjust number of days per month for leap years
931 if (is_leap_year(year)) {
932 daymonth[1] = 29;
933 }
934 else {
935 daymonth[1] = 28;
936 }
937
938 // Check time string
939 if (n != 3 && n != 6) {
940 std::string msg = "Invalid time string \""+time+"\" encountered. "
941 "Please specify the time in the format YYYY-MM-DD "
942 "or YYYY-MM-DDThh:mm:ss.s.";
944 }
945 if (year < 1000 || year > 9999) {
946 std::string msg = "Invalid year "+gammalib::str(year)+" specified. "
947 "The year needs to be a four-digit year.";
949 }
950 if (month < 1 || month > 12) {
951 std::string msg = "Invalid month "+gammalib::str(month)+" specified. "
952 "The month needs to be comprised between 01 and 12";
954 }
955 if (day < 1 || day > daymonth[month-1]) {
956 std::string msg = "Invalid day "+gammalib::str(day)+" specified. "
957 "The day for month "+gammalib::str(month)+" needs "
958 "to be comprised between 01 and "+
959 gammalib::str(daymonth[month-1])+".";
961 }
962 if (hour < 0 || hour > 23) {
963 std::string msg = "Invalid hour "+gammalib::str(hour)+" specified. "
964 "The hour needs to be comprised between 00 and 23";
966 }
967 if (minute < 0 || minute > 59) {
968 std::string msg = "Invalid minute "+gammalib::str(minute)+" specified. "
969 "The minute needs to be comprised between 00 and 59";
971 }
972 if (second < 0 || second >= 60.0) {
973 std::string msg = "Invalid second "+gammalib::str(second)+" specified. "
974 "The second needs to be comprised between 00 and <60";
976 }
977
978 // Compute MJD day
979 month--;
980 for (int i = 0; i < month; ++i) { // Add days passed per month
981 day += daymonth[i];
982 }
983 day += (year - 1972) * 365 - 1; // Add days passed per year
984 day += (year - 1969) / 4; // Add leap days passed (every 4 years)
985 day -= (year - 1901) / 100; // Add leap days passed (not every 100 years)
986 day += (year - 1601) / 400; // Add leap days passed (every 400 years)
987 day += 41317; // Add MJD at 1972
988
989 // Compute MJD fraction (UTC)
990 double fraction = ((double)hour * 3600.0 + (double)minute * 60.0 + second) *
992
993 // Compute MJD (UTC)
994 double mjd = (double)day + fraction;
995
996 // Get conversion from UTC to TAI to TT
997 double correction = leap_seconds(mjd) + gammalib::tai2tt;
998
999 // Convert MJD from UTC to TT
1000 mjd += correction * gammalib::sec2day;
1001
1002 // Set time
1003 this->mjd(mjd);
1004
1005 // Return
1006 return;
1007}
1008
1009
1010/***********************************************************************//**
1011 * @brief Set time given in specified reference
1012 *
1013 * @param[in] time Time in given reference system.
1014 * @param[in] ref Reference system.
1015 *
1016 * Set the time to a value given in a specific reference system.
1017 ***************************************************************************/
1018void GTime::set(const double& time, const GTimeReference& ref)
1019{
1020 // Convert time to specified time unit
1021 m_time = time * ref.unitseconds();
1022
1023 // Compute time offset in seconds
1024 double offset = (mjd_ref - ref.mjdref()) * gammalib::sec_in_day;
1025
1026 // Subtract time offset in seconds
1027 m_time -= offset;
1028
1029 // Add leap seconds in case that time was given in UTC time system
1030 if (gammalib::toupper(ref.timesys()) == "UTC") {
1031
1032 // Add leap seconds and offset between TAI and TT time system
1033 m_time += leap_seconds(this->mjd());
1035
1036 } // endif: Time was given in UTC time system
1037
1038 // ... otherwise if system is TAI system then add TAI offset
1039 else if (gammalib::toupper(ref.timesys()) == "TAI") {
1041 }
1042
1043 // Return
1044 return;
1045}
1046
1047
1048/***********************************************************************//**
1049 * @brief Set time from string
1050 *
1051 * @param[in] time Time string.
1052 * @param[in] ref Reference system.
1053 *
1054 * Sets the time from a string for a given reference system. The following
1055 * strings are valid:
1056 *
1057 * "2016-10-05T15:08:56" (UTC string)
1058 * "1800.0" (MET seconds in specified reference system)
1059 * "1800.0 (TT)" (MET seconds in specified reference, TT system)
1060 * "1800.0 (UTC)" (MET seconds in specified reference, UTC time system)
1061 * "1800.0 (TAI)" (MET seconds in specified reference, TAI time system)
1062 * "MJD 54609" (Modified Julian Days, TT system)
1063 * "MJD 54609 (TT)" (Modified Julian Days, TT system)
1064 * "MJD 54609 (UTC)" (Modified Julian Days, UTC system)
1065 * "MJD 54609 (TAI)" (Modified Julian Days, TAI system)
1066 * "JD 54609" (Julian Days, TT system)
1067 * "JD 54609 (TT)" (Julian Days, TT system)
1068 * "JD 54609 (UTC)" (Julian Days, UTC system)
1069 * "JD 54609 (TAI)" (Julian Days, TAI system)
1070 *
1071 * If any other string is encountered, the numerical value is interpreted as
1072 * time is seconds using the TT system.
1073 *
1074 * Note that the TT, UTC or TAI attributes overwrite the values contained in
1075 * the specified reference system. The reference system is only used to
1076 * convert MET times in seconds.
1077 ***************************************************************************/
1078void GTime::set(const std::string& time, const GTimeReference& ref)
1079{
1080 // Strip any whitespace from string and convert it to upper case
1081 std::string str = gammalib::toupper(gammalib::strip_whitespace(time));
1082
1083 // First check if the string is a UTC string
1084 long year = 0;
1085 int month = 0;
1086 long day = 0;
1087 long hour = 0;
1088 long minute = 0;
1089 double second = 0.0;
1090 int n = sscanf(str.c_str(), "%ld-%d-%ldT%ld:%ld:%lg",
1091 &year, &month, &day, &hour, &minute, &second);
1092 if (n == 3 || n == 6) {
1093 utc(str);
1094 }
1095
1096 // ... otherwise check for Modified Julian Days
1097 else if (str.find("MJD") == 0) {
1098 double timeval = extract_timeval(str);
1099 std::string timesys = extract_timesys(str);
1100 if (timesys.empty()) {
1101 timesys = "TT";
1102 }
1103 mjd(timeval, timesys);
1104 }
1105
1106 // ... otherwise check for Julian Days
1107 else if (str.find("JD") == 0) {
1108 double timeval = extract_timeval(str);
1109 std::string timesys = extract_timesys(str);
1110 if (timesys.empty()) {
1111 timesys = "TT";
1112 }
1113 jd(timeval, timesys);
1114 }
1115
1116 // ... otherwise take time as seconds and use the specified reference
1117 // system. If no TT, UTC or TAI arrtibutes are specified use the value
1118 // specified by the reference system.
1119 else {
1120 double timeval = extract_timeval(str);
1121 std::string timesys = extract_timesys(str);
1122 if (timesys.empty()) {
1123 timesys = ref.timesys();
1124 }
1125 GTimeReference timeref(ref.mjdref(), ref.timeunit(), timesys, ref.timeref());
1126 set(timeval, timeref);
1127 }
1128
1129 // Return
1130 return;
1131}
1132
1133
1134/***********************************************************************//**
1135 * @brief Set time to current time
1136 *
1137 * Sets time to current time.
1138 ***************************************************************************/
1139void GTime::now(void)
1140{
1141 // Allocate variables
1142 struct std::tm timeStruct;
1143 std::time_t now;
1144 char buffer[100];
1145
1146 // Get time
1147 now = std::time(NULL);
1148 #ifdef HAVE_GMTIME_R
1149 std::gmtime_r(&now, &timeStruct);
1150 #else
1151 std::memcpy(&timeStruct, gmtime(&now), sizeof(struct tm));
1152 #endif
1153
1154 // Write message type, time and task name to buffer (C++98 compliant)
1155 #if ((defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L) || \
1156 (defined __cplusplus && __cplusplus >= 201103L))
1157 std::snprintf(buffer, 100, "%04d-%02d-%02dT%02d:%02d:%02d",
1158 timeStruct.tm_year + 1900,
1159 timeStruct.tm_mon + 1,
1160 timeStruct.tm_mday,
1161 timeStruct.tm_hour,
1162 timeStruct.tm_min,
1163 timeStruct.tm_sec);
1164 #else
1165 std::sprintf(buffer, "%04d-%02d-%02dT%02d:%02d:%02d",
1166 timeStruct.tm_year + 1900,
1167 timeStruct.tm_mon + 1,
1168 timeStruct.tm_mday,
1169 timeStruct.tm_hour,
1170 timeStruct.tm_min,
1171 timeStruct.tm_sec);
1172 #endif
1173
1174 // Build string from buffer
1175 std::string date = buffer;
1176
1177 // Set UTC time
1178 utc(date);
1179
1180 // Return
1181 return;
1182}
1183
1184
1185/***********************************************************************//**
1186 * @brief Returns native time reference
1187 *
1188 * @return Native time reference.
1189 *
1190 * Returns the native GammaLib time reference. The GammaLib native time
1191 * reference (i.e. time=0) is defined as January 1, 2010, 00:00:00 (TT).
1192 * The time system is Terrestrial Time (TT). Time is stored in seconds.
1193 ***************************************************************************/
1195{
1196 // Allocate native time reference
1197 GTimeReference reference(mjd_ref, "s", "TT", "LOCAL");
1198
1199 // Return reference
1200 return reference;
1201}
1202
1203
1204/***********************************************************************//**
1205 * @brief Print time
1206 *
1207 * @param[in] chatter Chattiness.
1208 * @return String containing time in seconds in native reference.
1209 *
1210 * Prints time in seconds in the native reference.
1211 ***************************************************************************/
1212std::string GTime::print(const GChatter& chatter) const
1213{
1214 // Initialise result string
1215 std::string result;
1216
1217 // Continue only if chatter is not silent
1218 if (chatter != SILENT) {
1219
1220 // Append time
1221 result.append(gammalib::str(m_time)+" s (TT)");
1222
1223 } // endif: chatter was not silent
1224
1225 // Return
1226 return result;
1227}
1228
1229
1230/*==========================================================================
1231 = =
1232 = Private methods =
1233 = =
1234 ==========================================================================*/
1235
1236/***********************************************************************//**
1237 * @brief Initialise class members
1238 ***************************************************************************/
1240{
1241 // Initialise members
1242 m_time = 0.0;
1243
1244 // Return
1245 return;
1246}
1247
1248
1249/***********************************************************************//**
1250 * @brief Copy class members
1251 *
1252 * @param[in] time Time.
1253 ***************************************************************************/
1255{
1256 // Copy time
1257 m_time = time.m_time;
1258
1259 // Return
1260 return;
1261}
1262
1263
1264/***********************************************************************//**
1265 * @brief Delete class members
1266 ***************************************************************************/
1268{
1269 // Return
1270 return;
1271}
1272
1273
1274/***********************************************************************//**
1275 * @brief Returns number of leap seconds for a given MJD
1276 *
1277 * @param[in] mjd Modified Julian Day in UTC time system.
1278 * @return Number of lead seconds.
1279 *
1280 * Return the number of leap seconds for a given MJD specified in the UTC
1281 * time system. This method returns valid number of leap seconds for the
1282 * years 1972-2017.
1283 *
1284 * See http://www.nist.gov/pml/div688/grp50/leapsecond.cfm for a table of
1285 * leap seconds.
1286 ***************************************************************************/
1287double GTime::leap_seconds(const double& mjd) const
1288{
1289 // Leap second table from 1972 on
1290 // see http://www.nist.gov/pml/div688/grp50/leapsecond.cfm
1291 // The first entry is 1-Jan-1972
1292 const long leapsmjd[] = {41317, // 1972-01-01
1293 41498, // 1972-06-30
1294 41682, // 1972-12-31
1295 42047, // 1973-12-31
1296 42412, // 1974-12-31
1297 42777, // 1975-12-31
1298 43143, // 1976-12-31
1299 43508, // 1977-12-31
1300 43873, // 1978-12-31
1301 44238, // 1979-12-31
1302 44785, // 1981-06-30
1303 45150, // 1982-06-30
1304 45515, // 1983-06-30
1305 46246, // 1985-06-30
1306 47160, // 1987-12-31
1307 47891, // 1989-12-31
1308 48256, // 1990-12-31
1309 48803, // 1992-06-30
1310 49168, // 1993-06-30
1311 49533, // 1994-06-30
1312 50082, // 1995-12-31
1313 50629, // 1997-06-30
1314 51178, // 1998-12-31
1315 53735, // 2005-12-31
1316 54831, // 2008-12-31
1317 56108, // 2012-06-30
1318 57203, // 2015-06-30
1319 57753}; // 2016-12-31
1320 const double leapsecs[] = {10.0, 11.0, 12.0, 13.0, 14.0,
1321 15.0, 16.0, 17.0, 18.0, 19.0,
1322 20.0, 21.0, 22.0, 23.0, 24.0,
1323 25.0, 26.0, 27.0, 28.0, 29.0,
1324 30.0, 31.0, 32.0, 33.0, 34.0,
1325 35.0, 36.0, 37.0};
1326 const int n_leapsecs = sizeof(leapsmjd)/sizeof(long);
1327
1328 // Extract MJD day and MJD fraction
1329 long day = (long)mjd;
1330
1331 // Find the leap second MJD that is equal or larger to the specified
1332 // day
1333 int i = n_leapsecs - 1;
1334 while ((day <= leapsmjd[i]) && i > 0) {
1335 i--;
1336 }
1337
1338 // Return leap seconds
1339 return (leapsecs[i]);
1340}
1341
1342
1343/***********************************************************************//**
1344 * @brief Extract time value from time string
1345 *
1346 * @param[in] time Time string.
1347 * @return Time value.
1348 *
1349 * Extracts the time value from a time string. The method strips any prefix
1350 * such as "MJD" or "JD" and any suffix starting with a left parentheses "("
1351 * and converts the remainder into a double precision value.
1352 ***************************************************************************/
1353double GTime::extract_timeval(const std::string& time) const
1354{
1355 // Find time
1356 size_t length = time.length();
1357 size_t start = time.find_first_of("0123456789+-.");
1358 size_t stop = time.find("(");
1359
1360 // If there is a stop position then set length to stop
1361 if (stop != std::string::npos) {
1362 length = stop;
1363 }
1364
1365 // Reduce length by start
1366 if (start != std::string::npos) {
1367 length -= start;
1368 }
1369
1370 // Get substring containing the time value
1371 std::string value = time.substr(start, length);
1372
1373 // Convert value into double precision
1374 double result = gammalib::todouble(value);
1375
1376 // Return result
1377 return result;
1378}
1379
1380
1381/***********************************************************************//**
1382 * @brief Extract time system from time string
1383 *
1384 * @param[in] time Time string.
1385 * @return Time system.
1386 *
1387 * Extracts the time system from a time string. Valid time systems are:
1388 *
1389 * "(TT)" (TT system)
1390 * "(UTC)" (UTC system)
1391 * "(TAI)" (TAI system)
1392 *
1393 * If no time system is found a blank string is returned.
1394 ***************************************************************************/
1395std::string GTime::extract_timesys(const std::string& time) const
1396{
1397 // Initialise time system with blank string
1398 std::string timesys = "";
1399
1400 // Strip any whitespace from string and convert it to upper case
1401 std::string str = gammalib::toupper(gammalib::strip_whitespace(time));
1402
1403 // Check for UTC
1404 if (str.find("(UTC)") != std::string::npos) {
1405 timesys = "UTC";
1406 }
1407 else if (str.find("(TAI)") != std::string::npos) {
1408 timesys = "TAI";
1409 }
1410 else if (str.find("(TT)") != std::string::npos) {
1411 timesys = "TT";
1412 }
1413
1414 // Return time system
1415 return timesys;
1416}
Exception handler interface definition.
Mathematical function definitions.
#define G_CONSTRUCT
Definition GModelPar.cpp:36
Time reference class interface definition.
#define G_UTC_GET
Definition GTime.cpp:49
const double jd_ref
JD of time=0.
Definition GTime.cpp:42
#define G_UTC
Definition GTime.cpp:48
#define G_SECS_GET
Definition GTime.cpp:46
const double mjd_ref
MJD of time=0.
Definition GTime.cpp:41
#define G_SECS_SET
Definition GTime.cpp:47
Time class interface definition.
Gammalib tools definition.
GChatter
Definition GTypemaps.hpp:33
@ SILENT
Definition GTypemaps.hpp:34
Implements a time reference.
double unitseconds(void) const
Return the time unit in seconds.
const double & mjdref(void) const
Return MJD reference (units: days)
const std::string & timeunit(void) const
Return time unit.
const std::string & timeref(void) const
Return time reference.
const std::string & timesys(void) const
Return time system.
Time class.
Definition GTime.hpp:55
void clear(void)
Clear time.
Definition GTime.cpp:252
virtual ~GTime(void)
Destructor.
Definition GTime.cpp:200
void copy_members(const GTime &time)
Copy class members.
Definition GTime.cpp:1254
std::string extract_timesys(const std::string &time) const
Extract time system from time string.
Definition GTime.cpp:1395
double extract_timeval(const std::string &time) const
Extract time value from time string.
Definition GTime.cpp:1353
double leap_seconds(void) const
Return number of leap seconds for current time.
Definition GTime.hpp:181
double mjd(void) const
Return time in Modified Julian Days (TT)
Definition GTime.cpp:320
GTimeReference reference(void) const
Returns native time reference.
Definition GTime.cpp:1194
bool is_leap_year(const int &year) const
Signals if year is a leap year.
Definition GTime.hpp:210
double days(void) const
Return time in days in native reference (TT)
Definition GTime.cpp:400
int days_in_year(const int &year) const
Returns number of days in year.
Definition GTime.hpp:225
double gmst(void) const
Return Greenwich mean sidereal time in hours in a day.
Definition GTime.cpp:606
std::string print(const GChatter &chatter=NORMAL) const
Print time.
Definition GTime.cpp:1212
double gast(void) const
Return Greenwich apparent sidereal time in hours in a day.
Definition GTime.cpp:629
double m_time
Time in seconds in native reference (TT)
Definition GTime.hpp:134
double convert(const GTimeReference &ref) const
Return time in specified reference.
Definition GTime.cpp:711
std::string utc(const int &precision=0) const
Return time as string in UTC time system.
Definition GTime.cpp:465
double lmst(const double &geolon) const
Return local mean sidereal time in hours in a day.
Definition GTime.cpp:669
double last(const double &geolon) const
Return local apparent sidereal time in hours in a day.
Definition GTime.cpp:690
void init_members(void)
Initialise class members.
Definition GTime.cpp:1239
void set(const double &time, const GTimeReference &ref)
Set time given in specified reference.
Definition GTime.cpp:1018
GTime * clone(void) const
Clone time.
Definition GTime.cpp:270
double jd(void) const
Return time in Julian Days (TT)
Definition GTime.cpp:284
GTime(void)
Void constructor.
Definition GTime.cpp:67
void free_members(void)
Delete class members.
Definition GTime.cpp:1267
GTime & operator=(const GTime &time)
Assignment operator.
Definition GTime.cpp:222
void now(void)
Set time to current time.
Definition GTime.cpp:1139
double julian_epoch(void) const
Return Julian epoch in native reference (TT)
Definition GTime.cpp:425
const double & secs(void) const
Return time in seconds in native reference (TT)
Definition GTime.hpp:156
std::string str(const unsigned short int &value)
Convert unsigned short integer value into string.
Definition GTools.cpp:508
double todouble(const std::string &arg)
Convert string into double precision value.
Definition GTools.cpp:919
std::string tolower(const std::string &s)
Convert string to lower case.
Definition GTools.cpp:948
const double sec2day
Definition GTools.hpp:62
std::string strip_whitespace(const std::string &arg)
Strip leading and trailing whitespace from string.
Definition GTools.cpp:99
const double deg2rad
Definition GMath.hpp:43
const double tai2tt
Definition GTools.hpp:63
const double sec_in_day
Definition GTools.hpp:61
std::string toupper(const std::string &s)
Convert string to upper case.
Definition GTools.cpp:934