4#include "../../catch2/catch.hpp"
12using mx::sys::timeUtilsDetail::operations;
14int controlledDatResult = 0;
15int controlledCal2jdResult = 0;
17tm *failGmtimeR(
const time_t *, tm * )
22int controlledDat(
int,
int,
int,
double,
double *dat )
25 return controlledDatResult;
28int controlledCal2jd(
int,
int,
int,
double *djm0,
double *djm )
32 return controlledCal2jdResult;
38 operationsGuard() : m_saved( mx::sys::timeUtilsDetail::operationsInstance() )
40 controlledDatResult = 0;
41 controlledCal2jdResult = 0;
46 mx::sys::timeUtilsDetail::operationsInstance() = m_saved;
64TEST_CASE(
"Verify operation of get_curr_time",
"[timeutils]" )
66 GIVEN(
"getting the time" )
68 WHEN(
"the same timespec is used" )
73 std::cout << std::endl;
79 WHEN(
"no timespec is provided" )
83 std::cout << std::endl;
95TEST_CASE(
"Formatting MJD timestamps as ISO8601",
"[timeutils][hciReduce]" )
110TEST_CASE(
"Verify operation of thread sleep functions",
"[timeutils]" )
112 GIVEN(
"sleeping for 1 second" )
114 WHEN(
"sleeping in seconds" )
120 REQUIRE( t1 >= t0 + 1.0 );
122 WHEN(
"sleeping in milliseconds" )
128 REQUIRE( t1 >= t0 + 1.0 );
130 WHEN(
"sleeping in microseconds" )
136 REQUIRE( t1 >= t0 + 1.0 );
138 WHEN(
"sleeping in nanoseconds" )
144 REQUIRE( t1 >= t0 + 1.0 );
155TEST_CASE(
"Verify operation of timespecAddNsec",
"[timeutils]" )
157 GIVEN(
"a timespec" )
159 WHEN(
"adding less than 1e9 nanoseconds" )
166 REQUIRE( ts.tv_sec == 1 );
167 REQUIRE( ts.tv_nsec == 10 );
170 REQUIRE( ts.tv_sec == 1 );
171 REQUIRE( ts.tv_nsec == 110 );
174 REQUIRE( ts.tv_sec == 1 );
175 REQUIRE( ts.tv_nsec == 1110 );
178 REQUIRE( ts.tv_sec == 1 );
179 REQUIRE( ts.tv_nsec == 11110 );
182 REQUIRE( ts.tv_sec == 1 );
183 REQUIRE( ts.tv_nsec == 111110 );
186 REQUIRE( ts.tv_sec == 1 );
187 REQUIRE( ts.tv_nsec == 1111110 );
190 REQUIRE( ts.tv_sec == 1 );
191 REQUIRE( ts.tv_nsec == 11111110 );
194 REQUIRE( ts.tv_sec == 1 );
195 REQUIRE( ts.tv_nsec == 111111110 );
198 WHEN(
"adding more than 1e9 nanoseconds but less than 2e9" )
205 REQUIRE( ts.tv_sec == 2 );
206 REQUIRE( ts.tv_nsec == 0 );
209 REQUIRE( ts.tv_sec == 3 );
210 REQUIRE( ts.tv_nsec == 10 );
213 REQUIRE( ts.tv_sec == 4 );
214 REQUIRE( ts.tv_nsec == 110 );
217 REQUIRE( ts.tv_sec == 5 );
218 REQUIRE( ts.tv_nsec == 1110 );
221 REQUIRE( ts.tv_sec == 6 );
222 REQUIRE( ts.tv_nsec == 11110 );
225 REQUIRE( ts.tv_sec == 7 );
226 REQUIRE( ts.tv_nsec == 111110 );
229 REQUIRE( ts.tv_sec == 8 );
230 REQUIRE( ts.tv_nsec == 1111110 );
233 REQUIRE( ts.tv_sec == 9 );
234 REQUIRE( ts.tv_nsec == 11111110 );
237 REQUIRE( ts.tv_sec == 10 );
238 REQUIRE( ts.tv_nsec == 111111110 );
241 WHEN(
"adding more than 2e9" )
248 REQUIRE( ts.tv_sec == 3 );
249 REQUIRE( ts.tv_nsec == 10 );
252 WHEN(
"the existing nanoseconds cause a carry" )
254 timespec ts{ 1, 900000000 };
258 REQUIRE( ts.tv_sec == 2 );
259 REQUIRE( ts.tv_nsec == 100000000 );
272TEST_CASE(
"Verify parsing of a formatted time string",
"[timeutils]" )
274 GIVEN(
"a valid time string" )
276 WHEN(
"integer seconds" )
289 WHEN(
"floating seconds" )
299 REQUIRE_THAT( sec, Catch::Matchers::WithinAbs( 3.23, 1e-7 ) );
302 WHEN(
"negative hour" )
312 REQUIRE_THAT( sec, Catch::Matchers::WithinAbs( -3.23, 1e-7 ) );
325 REQUIRE_THAT( sec, Catch::Matchers::WithinAbs( 3.23, 1e-7 ) );
338 GIVEN(
"a valid Gregorian date" )
340 WHEN(
"integer seconds" )
343 REQUIRE( mjd == 59214.0 );
347 GIVEN(
"a valid Gregorian date" )
349 WHEN(
"floating seconds" )
352 REQUIRE_THAT( mjd, Catch::Matchers::WithinAbs( 59214.00011846875, 1e-14 ) );
363TEST_CASE(
"Verify parsing of an ISO 8601 time string",
"[timeutils]" )
365 GIVEN(
"a valid ISO 8601 time string" )
367 WHEN(
"integer seconds" )
369 int yr, mon, day, hr, min;
375 REQUIRE( yr == 2020 );
376 REQUIRE( mon == 12 );
377 REQUIRE( day == 31 );
383 WHEN(
"fractional seconds" )
385 int yr, mon, day, hr, min;
391 REQUIRE( yr == 2020 );
392 REQUIRE( mon == 12 );
393 REQUIRE( day == 31 );
396 REQUIRE_THAT( sec, Catch::Matchers::WithinAbs( 10.2357, 1e-14 ) );
400 GIVEN(
"an invalid ISO 8601 time string" )
402 WHEN(
"string too short" )
404 int yr, mon, day, hr, min;
420TEST_CASE(
"Verify conversion of an ISO 8601 time string to MJD",
"[timeutils]" )
422 GIVEN(
"a valid ISO 8601 time string" )
424 WHEN(
"integer seconds" )
428 REQUIRE( mjd == 59214.0 );
431 WHEN(
"fractional seconds" )
435 REQUIRE_THAT( mjd, Catch::Matchers::WithinAbs( 59214.00011846875, 1e-14 ) );
439 GIVEN(
"an invalid ISO 8601 time string" )
449TEST_CASE(
"Reject invalid Gregorian calendar dates",
"[timeutils]" )
460TEST_CASE(
"Format UTC timestamps as ISO8601",
"[timeutils]" )
462 const time_t epoch2000 = 946684800;
463 const timespec fractional{ epoch2000, 123456789 };
483TEST_CASE(
"Handle ISO8601 broken-down-time failures",
"[timeutils]" )
485 operationsGuard guard;
486 mx::sys::timeUtilsDetail::operationsInstance().gmtimeR = failGmtimeR;
488 const time_t epoch = 0;
489 const timespec fractional{ epoch, 1 };
499TEST_CASE(
"Format compact UTC timestamps",
"[timeutils]" )
501 timespec ts{ 946684800, 123456789 };
505 REQUIRE( stamp ==
"20000101000000123456789" );
507 operationsGuard guard;
508 mx::sys::timeUtilsDetail::operationsInstance().gmtimeR = failGmtimeR;
517TEST_CASE(
"Convert UTC timespecs to TAI MJD",
"[timeutils]" )
523 const timespec epoch2000{ 946684800, 250000000 };
525 REQUIRE( djm == 51544.0 );
526 REQUIRE_THAT( djmf, Catch::Matchers::WithinAbs( 32.25 / 86400.0, 1e-15 ) );
527 REQUIRE( brokenDown.tm_year == 100 );
528 REQUIRE( brokenDown.tm_mon == 0 );
529 REQUIRE( brokenDown.tm_mday == 1 );
531 const timespec rollover{ 1483315190, 500000000 };
533 REQUIRE( djm == 57755.0 );
534 REQUIRE_THAT( djmf, Catch::Matchers::WithinAbs( 27.5 / 86400.0, 1e-15 ) );
541TEST_CASE(
"Report UTC-to-TAI conversion failures",
"[timeutils]" )
543 operationsGuard guard;
544 const timespec ts{ 946684800, 0 };
549 mx::sys::timeUtilsDetail::operationsInstance().gmtimeR = failGmtimeR;
552 mx::sys::timeUtilsDetail::resetOperations();
553 mx::sys::timeUtilsDetail::operationsInstance().iauDat = controlledDat;
554 controlledDatResult = -3;
557 controlledDatResult = 0;
558 mx::sys::timeUtilsDetail::operationsInstance().iauCal2jd = controlledCal2jd;
559 controlledCal2jdResult = -2;
562 controlledCal2jdResult = 0;
563 controlledDatResult = 1;
574 REQUIRE( mean.tv_sec == 1 );
575 REQUIRE( mean.tv_nsec == 0 );
578 REQUIRE( mean.tv_sec == 0 );
579 REQUIRE( mean.tv_nsec == 500000000 );
582 REQUIRE( mean.tv_sec == 1 );
583 REQUIRE( mean.tv_nsec == 400000000 );
592 const timespec early{ 1, 100 };
593 const timespec laterNsec{ 1, 200 };
594 const timespec laterSec{ 2, 0 };
595 const timespec same{ 1, 100 };
597 REQUIRE( mx::sys::tscomp::operator<( early, laterNsec ) );
598 REQUIRE( mx::sys::tscomp::operator<( early, laterSec ) );
599 REQUIRE_FALSE( mx::sys::tscomp::operator<( laterSec, early ) );
600 REQUIRE( mx::sys::tscomp::operator>( laterNsec, early ) );
601 REQUIRE( mx::sys::tscomp::operator>( laterSec, early ) );
602 REQUIRE_FALSE( mx::sys::tscomp::operator>( early, laterSec ) );
603 REQUIRE( mx::sys::tscomp::operator==( early, same ) );
604 REQUIRE_FALSE( mx::sys::tscomp::operator==( early, laterNsec ) );
605 REQUIRE( mx::sys::tscomp::operator<=( early, same ) );
606 REQUIRE( mx::sys::tscomp::operator<=( early, laterNsec ) );
607 REQUIRE_FALSE( mx::sys::tscomp::operator<=( laterNsec, early ) );
608 REQUIRE( mx::sys::tscomp::operator>=( early, same ) );
609 REQUIRE( mx::sys::tscomp::operator>=( laterNsec, early ) );
610 REQUIRE_FALSE( mx::sys::tscomp::operator>=( early, laterNsec ) );
617TEST_CASE(
"Apply arithmetic offsets to timespec values",
"[timeutils]" )
619 timespec ts{ 1, 100000000 };
621 REQUIRE( result.tv_sec == 1 );
622 REQUIRE( result.tv_nsec == 350000000 );
624 ts = { 1, 900000000 };
626 REQUIRE( result.tv_sec == 2 );
627 REQUIRE( result.tv_nsec == 150000000 );
629 ts = { 1, 900000000 };
631 REQUIRE( result.tv_sec == 1 );
632 REQUIRE( result.tv_nsec == 650000000 );
634 ts = { 1, 100000000 };
636 REQUIRE( result.tv_sec == 0 );
637 REQUIRE( result.tv_nsec == 850000000 );
644TEST_CASE(
"Read a generic monotonic clock value",
"[timeutils]" )
649 REQUIRE( value > 0 );
650 REQUIRE( ts.tv_sec > 0 );
#define DJM0
Julian Date of Modified Julian Date zero (defined in the SOFA library sofam.h).
TEST_CASE("Verify operation of get_curr_time", "[timeutils]")
void nanoSleep(unsigned nsec)
Sleep for a specified period in nanoseconds.
void sleep(unsigned sec)
Sleep for a specified period in seconds.
void milliSleep(unsigned msec)
Sleep for a specified period in milliseconds.
void microSleep(unsigned usec)
Sleep for a specified period in microseconds.
int ISO8601dateBreakdown(int &yr, int &mon, int &day, int &hr, int &min, double &sec, const std::string &fdate)
Parse an ISO8601 date of the form "YYYY-MM-DDTHH:MM:SS.S" into the individual components.
std::string ISO8601DateTimeStr(const timeT &timeIn, int timeZone=0)
Get a date-time string in ISO 8601 format.
std::string ISO8601DateTimeStrMJD(const double &timeIn, int timeZone=0)
Get a date-time string in ISO 8601 format for an MJD.
int timespecUTC2TAIMJD(double &djm, double &djmf, const timespec &tsp, tm *tm0)
Convert a UTC timespec to TAI modified Julian date.
void timespecAddNsec(timespec &ts, unsigned nsec)
Adds a time offset to an existing timespec.
void parse_hms(floatT &h, floatT &m, floatT &s, const std::string &hmsstr)
typeT get_curr_time()
Get the current system time in seconds.
int timeStamp(std::string &tstamp, timespec &ts)
Get a timestamp string in the form YYYYMMDDHHMMSS.SSSSSSSSS.
double Cal2mjd(int yr, int mon, int day, int hr, int min, double sec)
Converts a Gregorian calendar date into modified Julian date (MJD).
double ISO8601date2mjd(const std::string &fdate)
Parse an ISO8601 date of the form "YYYY-MM-DDTHH:MM:SS.S" and return the modified Julian date (MJD).
Utilities for working with time.
timespec operator-(timespec ts, arithT sub)
Subtract an amount of time specified in seconds from a timespec.
timespec operator+(timespec ts, arithT add)
Add an amount of time specified in seconds to a timespec.
timespec meanTimespec(timespec ts1, timespec ts2)
Calculate the mean time of two times given by timespecs.