HOPS
HOPS class reference
MHO_Clock.hh
Go to the documentation of this file.
1 #ifndef MHO_Clock_HH__
2 #define MHO_Clock_HH__
3 
4 #include <chrono>
5 #include <iomanip>
6 #include <math.h>
7 #include <sstream>
8 #include <string>
9 
10 //these can someday be replaced with STL versions in C++20
11 #include "date/date.h"
12 
13 //Leap-second / utc-tai-gps-sys clock backend selection.
14 //HOPS_USE_INTERNAL_LEAP_SECONDS (default, set by CMake): use the self-contained
15 //hardcoded leap-second table in hops_leap_seconds.hh, so no libdate-tz link,
16 //no runtime dependency on the host tz database.
17 //otherwise: fall back to the upstream compiled date/tz.h (links libdate-tz and
18 //reads the installed system tz db at runtime).
19 //Both backends expose the same utc_clock/tai_clock/gps_clock/get_leap_second_info functionality
20 //the hops_tz namespace alias below points MHO_Clock at whichever is active.
21 #ifdef HOPS_USE_INTERNAL_LEAP_SECONDS
22  #include "hops_leap_seconds.hh"
23 #else
24  #include "date/tz.h"
25 #endif
26 
27 #include "MHO_Message.hh"
28 #include "MHO_Tokenizer.hh"
29 #include "legacy_hops_date.hh"
30 
31 #define J2000_TAI_EPOCH "2000-01-01 11:59:27.816"
32 #define ISO8601_UTC_FORMAT "%FT%TZ"
33 #define HOPS_TIMESTAMP_PREFIX "HOPS-J2000"
34 #define HOPS_TIME_DELIM "|"
35 #define HOPS_TIME_UNIT "ns"
36 #define NANOSEC_TO_SEC 1e-9
37 #define SEC_TO_NANOSEC 1000000000
38 #define JD_TO_SEC 86400.0
39 #define MINUTE_TO_SEC 60.0
40 #define HOUR_TO_SEC 3600.0
41 
42 //alias the active leap-second/clock backend (see the include block above). The
43 //internal implementation lives in namespace hops; the upstream one in date. Both
44 //provide utc_clock/tai_clock/gps_clock, the utc_time/tai_time/gps_time aliases,
45 //and get_leap_second_info, so MHO_Clock refers to them only through hops_tz.
46 #ifdef HOPS_USE_INTERNAL_LEAP_SECONDS
47 namespace hops_tz = hops;
48 #else
49 namespace hops_tz = date;
50 #endif
51 
52 namespace hops
53 {
54 
66 {
67  public:
68  using duration = std::chrono::nanoseconds;
69  using rep = duration::rep;
70  using period = duration::period;
71  using time_point = std::chrono::time_point< hops_clock, std::chrono::nanoseconds >;
72  static const bool is_steady = false;
73 
80  static time_point now();
81 
88  template< typename Duration >
89  static std::chrono::time_point< hops_tz::utc_clock,
90  typename std::common_type< Duration, std::chrono::nanoseconds >::type >
91  to_utc(const std::chrono::time_point< hops_clock, Duration >&) NOEXCEPT;
92 
99  template< typename Duration >
100  static std::chrono::time_point< hops_clock, typename std::common_type< Duration, std::chrono::nanoseconds >::type >
101  from_utc(const std::chrono::time_point< hops_tz::utc_clock, Duration >&) NOEXCEPT;
102 
109  template< typename Duration >
110  static std::chrono::time_point< hops_tz::tai_clock,
111  typename std::common_type< Duration, std::chrono::nanoseconds >::type >
112  to_tai(const std::chrono::time_point< hops_clock, Duration >&) NOEXCEPT;
113 
120  template< typename Duration >
121  static std::chrono::time_point< hops_clock, typename std::common_type< Duration, std::chrono::nanoseconds >::type >
122  from_tai(const std::chrono::time_point< hops_tz::tai_clock, Duration >&) NOEXCEPT;
123 
130  template< typename Duration >
131  static std::chrono::time_point< hops_tz::gps_clock,
132  typename std::common_type< Duration, std::chrono::nanoseconds >::type >
133  to_gps(const std::chrono::time_point< hops_clock, Duration >&) NOEXCEPT;
134 
141  template< typename Duration >
142  static std::chrono::time_point< hops_clock, typename std::common_type< Duration, std::chrono::nanoseconds >::type >
143  from_gps(const std::chrono::time_point< hops_tz::gps_clock, Duration >&) NOEXCEPT;
144 
151  template< typename Duration >
152  static std::chrono::time_point< std::chrono::system_clock,
153  typename std::common_type< Duration, std::chrono::nanoseconds >::type >
154  to_sys(const std::chrono::time_point< hops_clock, Duration >&) NOEXCEPT;
155 
162  template< typename Duration >
163  static std::chrono::time_point< hops_clock, typename std::common_type< Duration, std::chrono::nanoseconds >::type >
164  from_sys(const std::chrono::time_point< std::chrono::system_clock, Duration >&) NOEXCEPT;
165 
172  template< typename Duration >
173  static std::chrono::time_point< date::local_t, typename std::common_type< Duration, std::chrono::nanoseconds >::type >
174  to_local(const std::chrono::time_point< hops_clock, Duration >&) NOEXCEPT;
175 
182  template< typename Duration >
183  static std::chrono::time_point< hops_clock, typename std::common_type< Duration, std::chrono::nanoseconds >::type >
184  from_local(const std::chrono::time_point< date::local_t, Duration >&) NOEXCEPT;
185 
193  static time_point from_iso8601_format(const std::string& timestamp);
194 
202  static std::string to_iso8601_format(const time_point& tp);
203 
211  static time_point from_hops_format(const std::string& timestamp);
212 
220  static std::string to_hops_format(const time_point& tp);
221 
230 
239 
248  static time_point from_vdif_format(int& vdif_epoch, int& vdif_seconds);
249 
259  static void to_vdif_format(const time_point& tp, int& vdif_epoch, int& vdif_second);
260 
270  static time_point from_mjd(const time_point& mjd_epoch, double epoch_offset, double mjd);
271 
281  static double to_mjd(const time_point& mjd_epoch, double epoch_offset, const time_point& tp);
282 
290  static time_point from_vex_format(const std::string& timestamp);
291 
300  static std::string to_vex_format(const time_point& tp, bool truncate_to_nearest_second = false);
301 
311  static time_point from_year_fpday(int year, double floating_point_days);
312 
322  static void to_year_fpday(const time_point& tp, int& year, double& floating_point_days);
323 
327  static hops_tz::utc_time< std::chrono::nanoseconds > get_hops_epoch_utc()
328  {
329  using namespace std::chrono;
330  //J2000 TAI epoch = 2000-01-01 11:59:27.816 (TAI wall-clock reading,
331  //see J2000_TAI_EPOCH). Build the tai_time numerically from calendar
332  //arithmetic (date::sys_days does not invovle leap seconds, no tz.h)
333  date::sys_days tai_epoch_day{date::year{1958} / date::month{1} / date::day{1}};
334  date::sys_days j2000_day{date::year{2000} / date::month{1} / date::day{1}};
335  auto days_since_tai_epoch = j2000_day - tai_epoch_day; //date::days
336  auto tod = hours{11} + minutes{59} + seconds{27} + milliseconds{816};
337  hops_tz::tai_time< nanoseconds > j2000_tai_epoch{duration_cast< nanoseconds >(days_since_tai_epoch) +
338  duration_cast< nanoseconds >(tod)};
339  return time_point_cast< nanoseconds >(hops_tz::tai_clock::to_utc(j2000_tai_epoch));
340  }
341 
346 
350  static std::chrono::seconds get_leap_seconds_between(const time_point& t_start, const time_point& t_end)
351  {
352  auto t_start_utc = to_utc(t_start);
353  auto t_end_utc = to_utc(t_end);
354  auto lp_info0 = hops_tz::get_leap_second_info(t_start_utc);
355  auto lp_info1 = hops_tz::get_leap_second_info(t_end_utc);
356  int delta = lp_info1.elapsed.count() - lp_info0.elapsed.count();
357  return std::chrono::seconds(delta);
358  }
359 
360  static bool is_vex_timestamp(const std::string& s);
361 
362  private:
363  static date::days day_of_year(date::sys_days sd)
364  {
365  using namespace date;
366  auto y = date::year_month_day{sd}.year();
367  return sd - date::sys_days{y / jan / 0};
368  }
369 
370  static date::sys_days get_year_month_day(date::year y, date::days ord_day)
371  {
372  using namespace date;
373  return date::sys_days{y / jan / 0} + ord_day;
374  }
375 
376  struct vex_date
377  {
378  int year;
379  int day_of_year;
380  int hours;
381  int minutes;
382  double seconds;
383  };
384 
385  static vex_date extract_vex_date(const std::string& timestamp);
386 
387  static std::string vex_date_to_iso8601_string(vex_date vdate);
388 
389  static vex_date vex_date_from_legacy(const legacy_hops_date& legacy_date);
390 
391  static std::string remove_trailing_zeros(std::string value)
392  {
393  std::size_t nzeros_on_end = 0;
394  for(auto rit = value.rbegin(); rit != value.rend(); rit++)
395  {
396  if(*rit != '0')
397  {
398  break;
399  }
400  nzeros_on_end++;
401  }
402  std::size_t useful_length = value.size() - nzeros_on_end;
403  std::string ret_val;
404  for(std::size_t i = 0; i < useful_length; i++)
405  {
406  ret_val.push_back(value[i]);
407  }
408  return ret_val;
409  }
410 };
411 
415 template< class Duration > using hops_time = std::chrono::time_point< hops_clock, Duration >;
416 
423 template< class Duration >
424 inline hops_tz::utc_time< typename std::common_type< Duration, std::chrono::nanoseconds >::type >
426 {
427 
428  using CD = typename std::common_type< Duration, std::chrono::nanoseconds >::type;
429  hops_tz::utc_time< std::chrono::nanoseconds > hops_epoch_start = get_hops_epoch_utc();
430  return hops_tz::utc_time< CD >(t.time_since_epoch() + hops_epoch_start.time_since_epoch());
431 }
432 
439 template< class Duration >
441 hops_clock::from_utc(const hops_tz::utc_time< Duration >& t) NOEXCEPT
442 {
443  using CD = typename std::common_type< Duration, std::chrono::nanoseconds >::type;
444  hops_tz::utc_time< std::chrono::nanoseconds > hops_epoch_start = get_hops_epoch_utc();
445  return hops_time< CD >(t.time_since_epoch() - hops_epoch_start.time_since_epoch());
446 }
447 
454 template< class Duration >
455 inline hops_tz::tai_time< typename std::common_type< Duration, std::chrono::nanoseconds >::type >
457 {
458  return hops_tz::tai_clock::from_utc(to_utc(t));
459 }
460 
467 template< class Duration >
469 hops_clock::from_tai(const hops_tz::tai_time< Duration >& t) NOEXCEPT
470 {
471  return from_utc(hops_tz::tai_clock::to_utc(t));
472 }
473 
480 template< class Duration >
481 inline hops_tz::gps_time< typename std::common_type< Duration, std::chrono::nanoseconds >::type >
483 {
484  return hops_tz::gps_clock::from_utc(to_utc(t));
485 }
486 
493 template< class Duration >
495 hops_clock::from_gps(const hops_tz::gps_time< Duration >& t) NOEXCEPT
496 {
497  return from_utc(hops_tz::gps_clock::to_utc(t));
498 }
499 
506 template< class Duration >
507 inline date::sys_time< typename std::common_type< Duration, std::chrono::nanoseconds >::type >
509 {
510  return hops_tz::utc_clock::to_sys(to_utc(t));
511 }
512 
519 template< class Duration >
521 hops_clock::from_sys(const date::sys_time< Duration >& t) NOEXCEPT
522 {
523  return from_utc(hops_tz::utc_clock::from_sys(t));
524 }
525 
532 {
533  return from_utc(hops_tz::utc_clock::now());
534 }
535 
542 template< class Duration >
543 inline date::local_time< typename std::common_type< Duration, std::chrono::nanoseconds >::type >
545 {
546  using CD = typename std::common_type< Duration, std::chrono::nanoseconds >::type;
547  hops_tz::utc_time< CD > hops_epoch_start = std::chrono::time_point_cast< CD >(get_hops_epoch_utc());
548  hops_tz::utc_time< CD > ut_time{t.time_since_epoch() +
549  std::chrono::time_point_cast< Duration >(hops_epoch_start).time_since_epoch()};
550  return hops_tz::utc_clock::to_local(ut_time);
551 }
552 
559 template< class Duration >
561 hops_clock::from_local(const date::local_time< Duration >& t) NOEXCEPT
562 {
563  using CD = typename std::common_type< Duration, std::chrono::nanoseconds >::type;
564  hops_tz::utc_time< CD > t2 = hops_tz::utc_clock::from_local(t);
565  hops_tz::utc_time< CD > hops_epoch_start = std::chrono::time_point_cast< CD >(get_hops_epoch_utc());
566  return hops_time< CD >{t2.time_since_epoch() -
567  std::chrono::time_point_cast< Duration >(hops_epoch_start).time_since_epoch()};
568 }
569 
581 template< class CharT, class Traits, class Duration >
582 std::basic_ostream< CharT, Traits >& to_stream(std::basic_ostream< CharT, Traits >& os, const CharT* fmt,
583  const hops_time< Duration >& t)
584 {
585  const std::string abbrev("HOPS");
586  CONSTDATA std::chrono::seconds offset{0};
587  return date::to_stream(os, fmt, hops_clock::to_local(t), &abbrev, &offset);
588 }
589 
603 template< class Duration, class CharT, class Traits, class Alloc = std::allocator< CharT > >
604 std::basic_istream< CharT, Traits >&
605 from_stream(std::basic_istream< CharT, Traits >& is, const CharT* fmt, hops_time< Duration >& tp,
606  std::basic_string< CharT, Traits, Alloc >* abbrev = nullptr, std::chrono::minutes* offset = nullptr)
607 {
608  date::local_time< Duration > lp;
609  from_stream(is, fmt, lp, abbrev, offset);
610  if(!is.fail())
611  tp = hops_clock::from_local(lp);
612  return is;
613 }
614 
615 template< class CharT, class Traits, class Duration >
616 std::basic_ostream< CharT, Traits >& operator<<(std::basic_ostream< CharT, Traits >& os, const hops_time< Duration >& t)
617 {
618  const CharT fmt[] = {'%', 'F', 'T', '%', 'T', 'Z', CharT{}};
619  return to_stream(os, fmt, t);
620 }
621 
628 inline hops_clock::time_point hops_clock::from_iso8601_format(const std::string& timestamp)
629 {
630  using namespace date;
631  using namespace std::chrono;
632  std::string frmt = ISO8601_UTC_FORMAT;
633  std::istringstream ss(timestamp);
634  std::istream tmp_stream(ss.rdbuf());
636  from_stream(tmp_stream, frmt.c_str(), hops_tp);
637  return hops_tp;
638 }
639 
646 inline std::string hops_clock::to_iso8601_format(const time_point& tp)
647 {
648  std::stringstream ss;
649  ss << tp;
650  return ss.str();
651 }
652 
659 inline hops_clock::time_point hops_clock::from_hops_format(const std::string& timestamp)
660 {
661  using namespace date;
662  using namespace std::chrono;
663 
664  MHO_Tokenizer tokenizer;
665  tokenizer.SetDelimiter(std::string(HOPS_TIME_DELIM));
666  std::vector< std::string > tokens;
667  tokenizer.SetString(&timestamp);
668  tokenizer.GetTokens(&tokens);
669  if(tokens.size() == 3)
670  {
671  std::string hops_prefix = tokens[0];
672  std::string unit = tokens[1];
673  std::string nanosecond_count = tokens[2];
674  if(hops_prefix == std::string(HOPS_TIMESTAMP_PREFIX) && unit == std::string(HOPS_TIME_UNIT))
675  {
676  std::stringstream ss;
677  ss << nanosecond_count;
678  int64_t ns;
679  ss >> ns;
680  return time_point(std::chrono::nanoseconds(ns));
681  }
682  }
683  msg_error("utility", "hops timestamp string not understood or supported, returning epoch start. " << eom);
684  return time_point(std::chrono::nanoseconds(0));
685 }
686 
693 inline std::string hops_clock::to_hops_format(const time_point& tp)
694 {
695  std::stringstream ss;
696  ss << HOPS_TIMESTAMP_PREFIX;
697  ss << HOPS_TIME_DELIM;
698  ss << HOPS_TIME_UNIT;
699  ss << HOPS_TIME_DELIM;
700  ss << tp.time_since_epoch().count();
701  return ss.str();
702 }
703 
705 {
706  vex_date vdate = vex_date_from_legacy(ldate);
707  std::string vex_as_iso8601 = vex_date_to_iso8601_string(vdate);
708  return hops_clock::from_iso8601_format(vex_as_iso8601);
709 }
710 
712 {
713  using namespace date;
714  using namespace std::chrono;
715 
716  //convert the time point to sys time, and extract the date
717  auto sys_tp = hops_clock::to_sys(tp);
718  auto dp = date::sys_days(floor< date::days >(sys_tp));
719 
720  //get all of the date information
721  date::year_month_day ymd{dp};
722  auto year = ymd.year();
723 
724  //get the ordinal day of the year
725  auto ordinal_day = day_of_year(dp);
726 
727  //get the time
728  date::hh_mm_ss< std::chrono::nanoseconds > time{floor< std::chrono::nanoseconds >(sys_tp - dp)};
729  auto hours = time.hours();
730  auto mins = time.minutes();
731  auto secs = time.seconds();
732  auto nanos = time.subseconds();
733 
734  legacy_hops_date ldate;
735  ldate.year = (int)year;
736  ldate.day = ordinal_day.count();
737  ldate.hour = hours.count();
738  ldate.minute = mins.count();
739  //note there may be loss of precision when converting to/from the actual legacy struct (single precision)
740  ldate.second = (double)secs.count() + ((double)(nanos.count())) * NANOSEC_TO_SEC;
741 
742  return ldate;
743 }
744 
745 //needed for ad_hoc flag files (time-stamps are given in floating-point days)
746 inline hops_clock::time_point hops_clock::from_year_fpday(int year, double floating_point_days)
747 {
748  int integer_days = (int)floating_point_days;
749  double fractional_day = floating_point_days - integer_days;
750  int integer_hours = (int)24 * fractional_day;
751  double fractional_hour = 24 * fractional_day - integer_hours;
752  int integer_minutes = (int)60 * fractional_hour;
753  double fractional_seconds = (60 * fractional_hour - integer_minutes) * 60;
754 
755  //we co-opt the legacy date format to handle this format
756  legacy_hops_date ldate;
757  ldate.year = (short)year;
758  ldate.day = (short)integer_days + 1; //note: ordinal day count starts at 1
759  ldate.hour = (short)integer_hours;
760  ldate.minute = (short)integer_minutes;
761  ldate.second = fractional_seconds;
762 
763  return from_legacy_hops_date(ldate);
764 }
765 
766 inline void hops_clock::to_year_fpday(const hops_clock::time_point& tp, int& year, double& floating_point_days)
767 {
768  using namespace date;
769  using namespace std::chrono;
770 
771  //convert the time point to sys time, and extract the date
772  auto sys_tp = hops_clock::to_sys(tp);
773  auto dp = date::sys_days(floor< date::days >(sys_tp));
774 
775  //get all of the date information
776  date::year_month_day ymd{dp};
777  auto year_value = ymd.year();
778  //get the ordinal day of the year
779  auto ordinal_day = day_of_year(dp); //note: count starts at 1
780  int integer_days = ordinal_day.count() - 1;
781  //get the time and convert to fractional day
782  date::hh_mm_ss< std::chrono::nanoseconds > time{floor< std::chrono::nanoseconds >(sys_tp - dp)};
783  int ihours = time.hours().count();
784  int imins = time.minutes().count();
785  int isecs = time.seconds().count();
786  int inanos = time.subseconds().count();
787 
788  double frac_day = (inanos * NANOSEC_TO_SEC + isecs + MINUTE_TO_SEC * imins + HOUR_TO_SEC * ihours) / (JD_TO_SEC);
789  floating_point_days = integer_days + frac_day;
790  year = static_cast<int>(year_value);
791 }
792 
793 inline hops_clock::time_point hops_clock::from_mjd(const time_point& mjd_epoch, double epoch_offset, double mjd)
794 {
795  double delta = (mjd - epoch_offset);
796  delta *= JD_TO_SEC;
797  std::chrono::duration< double > duration_seconds(delta);
798 
799  auto mjd_epoch_utc = to_utc(mjd_epoch);
800  auto utc_time_point = mjd_epoch_utc + std::chrono::duration_cast< std::chrono::nanoseconds >(duration_seconds);
801  auto hops_time_point = from_utc(utc_time_point);
802  return hops_time_point;
803 }
804 
805 inline double hops_clock::to_mjd(const time_point& mjd_epoch, double epoch_offset, const time_point& tp)
806 {
807  auto mjd_epoch_utc = to_utc(mjd_epoch);
808  auto tp_utc = to_utc(tp);
809 
810  double delta = (tp_utc - mjd_epoch_utc).count();
811  delta *= NANOSEC_TO_SEC; //convert to seconds
812  delta /= JD_TO_SEC; //convert to days
813  delta += epoch_offset; //subtract epoch offset
814  return delta;
815 }
816 
817 inline hops_clock::time_point hops_clock::from_vex_format(const std::string& timestamp)
818 {
819  vex_date vdate = hops_clock::extract_vex_date(timestamp);
820  //convert the vex date info to an ISO-8601-style year-month-day type format
821  std::string vex_as_iso8601 = vex_date_to_iso8601_string(vdate);
822  return hops_clock::from_iso8601_format(vex_as_iso8601);
823 }
824 
825 inline std::string hops_clock::to_vex_format(const time_point& tp, bool truncate_to_nearest_second)
826 {
827  using namespace date;
828  using namespace std::chrono;
829 
830  //convert the time point to sys time, and extract the date
831  auto sys_tp = hops_clock::to_sys(tp);
832  auto dp = date::sys_days(floor< date::days >(sys_tp));
833 
834  //get all of the date information
835  date::year_month_day ymd{dp};
836  auto year = ymd.year();
837  // auto month = ymd.month();
838  // auto day = ymd.day();
839 
840  //get the ordinal day of the year
841  auto ordinal_day = day_of_year(dp);
842 
843  //get the time
844  date::hh_mm_ss< std::chrono::nanoseconds > time{floor< std::chrono::nanoseconds >(sys_tp - dp)};
845  auto hours = time.hours();
846  auto mins = time.minutes();
847  auto secs = time.seconds();
848  auto nanos = time.subseconds();
849 
850  //we need to make sure that year, day-of-year, hour, minute, and integer sec
851  //are all preprended with the proper number of zeros
852 
853  std::stringstream ss;
854  ss << year;
855  ss << "y";
856  ss << std::setfill('0') << std::setw(3) << ordinal_day.count();
857  ss << "d";
858  ss << std::setfill('0') << std::setw(2) << hours.count();
859  ss << "h";
860  ss << std::setfill('0') << std::setw(2) << mins.count();
861  ss << "m";
862  ss << std::setfill('0') << std::setw(2) << secs.count();
863 
864  if(!truncate_to_nearest_second)
865  {
866  std::stringstream nss;
867  nss << std::setfill('0') << std::setw(9) << nanos.count();
868  std::string snano_sec;
869  nss >> snano_sec;
870  std::string trimmed_nanosec = remove_trailing_zeros(snano_sec);
871  if(trimmed_nanosec.size() != 0)
872  {
873  ss << ".";
874  ss << trimmed_nanosec;
875  }
876  }
877  ss << "s";
878 
879  return ss.str();
880 }
881 
882 inline void hops_clock::to_vdif_format(const time_point& tp, int& vdif_epoch, int& vdif_seconds)
883 {
884  using namespace date;
885  using namespace std::chrono;
886 
887  //convert the time point to sys time, and extract the date
888  auto sys_tp = hops_clock::to_sys(tp);
889  auto dp = date::sys_days(floor< date::days >(sys_tp));
890 
891  //get all of the date information so we can figure out the epoch
892  date::year_month_day ymd{dp};
893  auto year = ymd.year();
894  auto month = ymd.month();
895  auto day = ymd.day();
896 
897  //(we have two 6 month epochs per year, and count from start of century)
898  int iyear = static_cast< int >(year);
899  unsigned int imonth = static_cast< unsigned int >(month);
900  int epoch = (iyear % 100) * 2;
901 
902  //now figure out if we are using the Jan 1st epoch, or the July 1st epoch
903  if(imonth < 7)
904  {
905  imonth = 1;
906  }
907  else
908  {
909  epoch += 1;
910  imonth = 7;
911  }
912  //set the day to the first of the month
913  day = date::day(1);
914  int hours = 0;
915  int minutes = 0;
916  int integer_sec = 0;
917 
918  //now figure out the epoch date
919  //may want to eliminate string conversion in favor of something faster
920  std::stringstream ss;
921  ss << iyear;
922  ss << "-";
923  ss << std::setfill('0') << std::setw(2) << imonth;
924  ss << "-";
925  ss << std::setfill('0') << std::setw(2) << static_cast< unsigned int >(day);
926  ss << "T";
927  ss << std::setfill('0') << std::setw(2) << hours;
928  ss << ":";
929  ss << std::setfill('0') << std::setw(2) << minutes;
930  ss << ":";
931  ss << std::setfill('0') << std::setw(2) << integer_sec;
932  ss << "Z";
933  std::string epoch_iso8601 = ss.str();
934  auto epoch_tp = from_iso8601_format(epoch_iso8601);
935  int secs = std::chrono::duration_cast< std::chrono::seconds >(tp - epoch_tp).count();
936 
937  vdif_epoch = epoch;
938  vdif_seconds = secs;
939 }
940 
941 inline hops_clock::time_point hops_clock::from_vdif_format(int& vdif_epoch, int& vdif_seconds)
942 {
943  using namespace date;
944  using namespace std::chrono;
945 
946  int start_year = 2000;
947  int n_years = std::floor(vdif_epoch / 2);
948  int iyear = start_year + n_years;
949 
950  unsigned int imonth = 1;
951  if(vdif_epoch % 2 == 1)
952  {
953  imonth = 7;
954  } //second half of the year
955  unsigned int iday = 1;
956  int hours = 0;
957  int minutes = 0;
958  int integer_sec = 0;
959 
960  //now figure out the epoch date
961  //may want to eliminate string conversion in favor of something faster
962  std::stringstream ss;
963  ss << iyear;
964  ss << "-";
965  ss << std::setfill('0') << std::setw(2) << imonth;
966  ss << "-";
967  ss << std::setfill('0') << std::setw(2) << iday;
968  ss << "T";
969  ss << std::setfill('0') << std::setw(2) << hours;
970  ss << ":";
971  ss << std::setfill('0') << std::setw(2) << minutes;
972  ss << ":";
973  ss << std::setfill('0') << std::setw(2) << integer_sec;
974  ss << "Z";
975  std::string epoch_iso8601 = ss.str();
976  std::cout << "epoch = " << epoch_iso8601 << std::endl;
977  auto epoch_tp = from_iso8601_format(epoch_iso8601);
978  auto tp = epoch_tp + std::chrono::seconds(vdif_seconds);
979  return tp;
980 }
981 
982 inline hops_clock::vex_date hops_clock::extract_vex_date(const std::string& timestamp)
983 {
984  vex_date vdate;
985  if(timestamp.size() == 0)
986  {
987  msg_error("utilities", "cannot extract vex date from empty string." << eom);
988  return vdate;
989  }
990 
991  MHO_Tokenizer tokenizer;
992  std::vector< std::string > tokens;
993  std::stringstream ss;
994  std::string rest;
995  std::string syear, sord_day, shour, smin, ssec;
996 
997  tokenizer.SetDelimiter(std::string("y"));
998  tokenizer.SetString(&timestamp);
999  tokenizer.GetTokens(&tokens);
1000 
1001  syear = tokens[0];
1002  ss << syear;
1003  ss >> vdate.year;
1004  rest = tokens[1];
1005 
1006  tokenizer.SetDelimiter(std::string("d"));
1007  tokenizer.SetString(&rest);
1008  tokenizer.GetTokens(&tokens);
1009 
1010  sord_day = tokens[0];
1011  ss.str(std::string());
1012  ss.clear();
1013  ss << sord_day;
1014  ss >> vdate.day_of_year;
1015  rest = tokens[1];
1016 
1017  tokenizer.SetDelimiter(std::string("h"));
1018  tokenizer.SetString(&rest);
1019  tokenizer.GetTokens(&tokens);
1020 
1021  shour = tokens[0];
1022  ss.str(std::string());
1023  ss.clear();
1024  ss << shour;
1025  ss >> vdate.hours;
1026  rest = tokens[1];
1027 
1028  tokenizer.SetDelimiter(std::string("m"));
1029  tokenizer.SetString(&rest);
1030  tokenizer.GetTokens(&tokens);
1031 
1032  smin = tokens[0];
1033  ss.str(std::string());
1034  ss.clear();
1035  ss << smin;
1036  ss >> vdate.minutes;
1037  rest = tokens[1];
1038 
1039  tokenizer.SetDelimiter(std::string("s"));
1040  tokenizer.SetString(&rest);
1041  tokenizer.GetTokens(&tokens);
1042 
1043  ssec = tokens[0];
1044  ss.str(std::string());
1045  ss.clear();
1046  ss << std::setprecision(15) << ssec;
1047  ss >> vdate.seconds;
1048 
1049  return vdate;
1050 }
1051 
1052 inline std::string hops_clock::vex_date_to_iso8601_string(hops_clock::vex_date vdate)
1053 {
1054  using namespace date;
1055  using namespace std::chrono;
1056 
1057  std::stringstream ss;
1058  ss << vdate.year;
1059  ss << "-";
1060 
1061  date::year y(vdate.year);
1062  date::days ord_day(vdate.day_of_year);
1063  date::sys_days ymd = get_year_month_day(y, ord_day);
1064 
1065  //convert day-of-year to month-day
1066  auto month = date::year_month_day{ymd}.month();
1067  auto mday = date::year_month_day{ymd}.day();
1068  ss << std::setfill('0') << std::setw(2) << (unsigned)month;
1069  ss << "-";
1070  ss << std::setfill('0') << std::setw(2) << (unsigned)mday;
1071 
1072  ss << "T";
1073  ss << std::setfill('0') << std::setw(2) << vdate.hours;
1074  ss << ":";
1075  ss << std::setfill('0') << std::setw(2) << vdate.minutes;
1076  ss << ":";
1077 
1078  //nss << std::setprecision(9) << vdate.seconds;
1079 
1080  double intpart;
1081  double frac = modf(vdate.seconds, &intpart);
1082  int integer_sec = intpart;
1083 
1084  ss << std::setfill('0') << std::setw(2) << integer_sec;
1085 
1086  //now convert the fraction part into integer nano seconds
1087  //zero-pad to 9 digits so leading zeros (e.g. 256 ns -> "000000256") are
1088  //preserved; without this the round-trip through to_vex_format/ISO8601
1089  //rescales sub-second precision by powers of 10
1090  int integer_nanosec = frac * SEC_TO_NANOSEC;
1091  std::stringstream nss;
1092  nss << std::setfill('0') << std::setw(9) << integer_nanosec;
1093  std::string nanoseconds_value = nss.str();
1094  std::string trimmed_int_nanosec = remove_trailing_zeros(nanoseconds_value);
1095  if(trimmed_int_nanosec.size() != 0)
1096  {
1097  ss << ".";
1098  ss << trimmed_int_nanosec;
1099  }
1100  ss << "Z";
1101  return ss.str();
1102 }
1103 
1104 inline hops_clock::vex_date hops_clock::vex_date_from_legacy(const legacy_hops_date& legacy_date)
1105 {
1106  hops_clock::vex_date vdate;
1107  vdate.year = legacy_date.year;
1108  vdate.day_of_year = legacy_date.day;
1109  vdate.hours = legacy_date.hour;
1110  vdate.minutes = legacy_date.minute;
1111  vdate.seconds = legacy_date.second;
1112  return vdate;
1113 }
1114 
1115 
1116 // checks if a string is a valid VEX timestamp like: 2026y180d12h15m08s
1117 // note that the seconds field may include an optional fractional part like: 08.0145s
1118 inline bool hops_clock::is_vex_timestamp(const std::string& s)
1119 {
1120  std::size_t pos = 0; //location in the string
1121 
1122  //spec out the 4 fixed width fields (seconds portion may vary)
1123  const int digit_counts[4] = {4, 3, 2, 2};
1124  const char separators[4] = {'y', 'd', 'h', 'm'};
1125 
1126  //loop over year, day, hours, minutes
1127  for(int field = 0; field < 4; field++)
1128  {
1129  for(int i = 0; i < digit_counts[field]; i++) //check the digits
1130  {
1131  if(pos >= s.size() || !std::isdigit(static_cast<unsigned char>(s[pos])))
1132  {
1133  return false;
1134  }
1135  pos++;
1136  }
1137  //reached the end, so check the separator value (y,d,h,m)
1138  if( pos >= s.size() || s[pos] != separators[field])
1139  {
1140  return false;
1141  }
1142  pos++;
1143  }
1144 
1145  //now check the seconds, first 2 digits
1146  for(int i = 0; i < 2; ++i)
1147  {
1148  if (pos >= s.size() || !std::isdigit(static_cast<unsigned char>(s[pos])))
1149  {
1150  return false;
1151  }
1152  pos++;
1153  }
1154 
1155  //now check the (optional) fractional part: '.' followed by at least one digit
1156  if (pos < s.size() && s[pos] == '.')
1157  {
1158  pos++; // consume '.', then next char must be a digit
1159  if (pos >= s.size() || !std::isdigit(static_cast<unsigned char>(s[pos])))
1160  {
1161  return false;
1162  }
1163  //chew through the rest until we reach anything not a digit
1164  while (pos < s.size() && std::isdigit(static_cast<unsigned char>(s[pos])))
1165  {
1166  ++pos;
1167  }
1168  }
1169 
1170  //better only have the trailing 's' left
1171  if (pos >= s.size() || s[pos] != 's'){return false;}
1172  pos++;
1173 
1174  //must have consumed the entire string with nothing leftover
1175  return pos == s.size();
1176 }
1177 
1178 
1179 } // namespace hops
1180 
1181 #endif
#define ISO8601_UTC_FORMAT
Definition: MHO_Clock.hh:32
#define MINUTE_TO_SEC
Definition: MHO_Clock.hh:39
#define SEC_TO_NANOSEC
Definition: MHO_Clock.hh:37
#define HOPS_TIMESTAMP_PREFIX
Definition: MHO_Clock.hh:33
#define HOPS_TIME_DELIM
Definition: MHO_Clock.hh:34
#define NANOSEC_TO_SEC
Definition: MHO_Clock.hh:36
#define HOPS_TIME_UNIT
Definition: MHO_Clock.hh:35
#define JD_TO_SEC
Definition: MHO_Clock.hh:38
#define HOUR_TO_SEC
Definition: MHO_Clock.hh:40
#define msg_error(xKEY, xCONTENT)
Definition: MHO_Message.hh:246
Class MHO_Tokenizer.
Definition: MHO_Tokenizer.hh:24
void SetString(const std::string *aString)
Definition: MHO_Tokenizer.cc:65
void GetTokens(std::vector< std::string > *tokens)
Definition: MHO_Tokenizer.cc:75
void SetDelimiter(const std::string &aDelim)
Definition: MHO_Tokenizer.cc:70
a clock for hops-time stamps, measures time in (UTC) nanoseconds since J2000 epoch....
Definition: MHO_Clock.hh:66
static std::chrono::time_point< hops_tz::gps_clock, typename std::common_type< Duration, std::chrono::nanoseconds >::type > to_gps(const std::chrono::time_point< hops_clock, Duration > &) NOEXCEPT
Converts a time point to GPS clock time.
static time_point get_hops_epoch()
returns the hops_clock epoch as a hops_clock time_point
Definition: MHO_Clock.hh:345
duration::period period
Definition: MHO_Clock.hh:70
static void to_vdif_format(const time_point &tp, int &vdif_epoch, int &vdif_second)
Converts a hops_clock time_point to a VDIF (epoch, second) timestamp.
Definition: MHO_Clock.hh:882
static time_point now()
Returns current time as a time_point using hops_clock's epoch.
Definition: MHO_Clock.hh:531
std::chrono::time_point< hops_clock, std::chrono::nanoseconds > time_point
Definition: MHO_Clock.hh:71
static std::chrono::time_point< hops_tz::tai_clock, typename std::common_type< Duration, std::chrono::nanoseconds >::type > to_tai(const std::chrono::time_point< hops_clock, Duration > &) NOEXCEPT
Converts a time point from hops_clock to TAI (International Atomic Time).
static std::chrono::time_point< std::chrono::system_clock, typename std::common_type< Duration, std::chrono::nanoseconds >::type > to_sys(const std::chrono::time_point< hops_clock, Duration > &) NOEXCEPT
Converts a time point from hops_clock to system clock.
static bool is_vex_timestamp(const std::string &s)
Definition: MHO_Clock.hh:1118
static std::chrono::time_point< date::local_t, typename std::common_type< Duration, std::chrono::nanoseconds >::type > to_local(const std::chrono::time_point< hops_clock, Duration > &) NOEXCEPT
Converts a global time point to local time.
static std::string to_iso8601_format(const time_point &tp)
Converts a time_point to ISO8601 formatted string.
Definition: MHO_Clock.hh:646
static time_point from_year_fpday(int year, double floating_point_days)
Converts a year + floating point day since start of the year to a hops_clock time_point,...
Definition: MHO_Clock.hh:746
std::chrono::nanoseconds duration
Definition: MHO_Clock.hh:68
static std::chrono::time_point< hops_clock, typename std::common_type< Duration, std::chrono::nanoseconds >::type > from_sys(const std::chrono::time_point< std::chrono::system_clock, Duration > &) NOEXCEPT
Converts a system time point to UTC and returns the corresponding hops clock time.
static time_point from_hops_format(const std::string &timestamp)
Converts a timestamp string in HOPS format to a time_point object.
Definition: MHO_Clock.hh:659
static std::chrono::time_point< hops_clock, typename std::common_type< Duration, std::chrono::nanoseconds >::type > from_gps(const std::chrono::time_point< hops_tz::gps_clock, Duration > &) NOEXCEPT
Converts GPS time to Hops clock time.
static time_point from_iso8601_format(const std::string &timestamp)
Converts an ISO8601 formatted timestamp string to a hops_clock time_point object.
Definition: MHO_Clock.hh:628
duration::rep rep
Definition: MHO_Clock.hh:69
static std::chrono::time_point< hops_clock, typename std::common_type< Duration, std::chrono::nanoseconds >::type > from_utc(const std::chrono::time_point< hops_tz::utc_clock, Duration > &) NOEXCEPT
Converts UTC time point to hops_clock time point.
static double to_mjd(const time_point &mjd_epoch, double epoch_offset, const time_point &tp)
Converts a hops_clock time_point to a Modified Julian date (floating point day) timestamp,...
Definition: MHO_Clock.hh:805
static time_point from_vdif_format(int &vdif_epoch, int &vdif_seconds)
Converts a VDIF (epoch, second) timestamp to a hops_clock time_point.
Definition: MHO_Clock.hh:941
static std::string to_vex_format(const time_point &tp, bool truncate_to_nearest_second=false)
Converts a hops_clock time_point to VEX-style formatted string (e.g. 2019y106d18h30m15s)
Definition: MHO_Clock.hh:825
static std::chrono::time_point< hops_tz::utc_clock, typename std::common_type< Duration, std::chrono::nanoseconds >::type > to_utc(const std::chrono::time_point< hops_clock, Duration > &) NOEXCEPT
Converts a time point to UTC using hops_clock and Duration.
static hops_tz::utc_time< std::chrono::nanoseconds > get_hops_epoch_utc()
returns the hops_clock epoch as a utc_time time_point
Definition: MHO_Clock.hh:327
static std::string to_hops_format(const time_point &tp)
Converts a time_point to HOPS format string.
Definition: MHO_Clock.hh:693
static time_point from_vex_format(const std::string &timestamp)
Converts a VEX-style formatted string (e.g. 2019y106d18h30m15s) to a hops_clock time_point.
Definition: MHO_Clock.hh:817
static std::chrono::seconds get_leap_seconds_between(const time_point &t_start, const time_point &t_end)
calculates the number of leap seconds inserted between two hops time points (UTC based clock)
Definition: MHO_Clock.hh:350
static time_point from_mjd(const time_point &mjd_epoch, double epoch_offset, double mjd)
Converts a Modified Julian date (floating point epoch and day) timestamp to a hops_clock time_point.
Definition: MHO_Clock.hh:793
static const bool is_steady
Definition: MHO_Clock.hh:72
static std::chrono::time_point< hops_clock, typename std::common_type< Duration, std::chrono::nanoseconds >::type > from_tai(const std::chrono::time_point< hops_tz::tai_clock, Duration > &) NOEXCEPT
Converts a TAI time point to UTC and returns the corresponding Hops clock time.
static legacy_hops_date to_legacy_hops_date(const time_point &tp)
Converts a hops_clock time_point to a legacy hops data struct.
Definition: MHO_Clock.hh:711
static void to_year_fpday(const time_point &tp, int &year, double &floating_point_days)
Converts a hops_clock time_point to a floating point day since start of the year needed for ad_hoc fl...
Definition: MHO_Clock.hh:766
static time_point from_legacy_hops_date(legacy_hops_date &ldate)
Converts a legacy hops data struct to a hops_clock time_point.
Definition: MHO_Clock.hh:704
static std::chrono::time_point< hops_clock, typename std::common_type< Duration, std::chrono::nanoseconds >::type > from_local(const std::chrono::time_point< date::local_t, Duration > &) NOEXCEPT
Calculates time difference between input local time and Hops epoch in UTC.
char * field(char *, char *, int)
Definition: fourmer.c:236
Self-contained (link-free) reimplementation of the small slice of the date library's tz facilities th...
short day
Definition: mk4_typedefs.h:17
short year
Definition: mk4_typedefs.h:16
Definition: mk4_typedefs.h:15
t
Definition: picking_aedit.py:14
Definition: MHO_AdhocFlagging.hh:18
std::basic_ostream< CharT, Traits > & operator<<(std::basic_ostream< CharT, Traits > &os, const hops_time< Duration > &t)
Definition: MHO_Clock.hh:616
short hour
Definition: legacy_hops_date.hh:20
std::basic_istream< CharT, Traits > & from_stream(std::basic_istream< CharT, Traits > &is, const CharT *fmt, hops_time< Duration > &tp, std::basic_string< CharT, Traits, Alloc > *abbrev=nullptr, std::chrono::minutes *offset=nullptr)
Reads time and abbreviation from stream using given format, updates hops_time if successful.
Definition: MHO_Clock.hh:605
leap_second_info get_leap_second_info(const utc_time< Duration > &ut)
Leap-second lookup for a utc_time, matching date::get_leap_second_info.
Definition: hops_leap_seconds.hh:171
short minute
Definition: legacy_hops_date.hh:21
short day
Definition: legacy_hops_date.hh:19
std::chrono::time_point< hops_clock, Duration > hops_time
Class hops_time.
Definition: MHO_Clock.hh:415
std::basic_ostream< CharT, Traits > & to_stream(std::basic_ostream< CharT, Traits > &os, const CharT *fmt, const hops_time< Duration > &t)
Converts hops_time to stream format using given fmt and outputs to os.
Definition: MHO_Clock.hh:582
double second
Definition: legacy_hops_date.hh:22
short year
Definition: legacy_hops_date.hh:18
A struct to avoid name collisions between the mk4utils 'data' struct and the 'date' header library.
Definition: legacy_hops_date.hh:17
struct token_struct * tokens
Definition: parse_control_file.c:26
Definition: vex.h:175