Skip to content

Use std::chrono and the date library instead of boost date time

Cédric Traizet requested to merge refac_date_time into develop

Summary

Use std::chrono and the date library instead of boost date time as internal types of

Rationale

We currently use the boost::date_time library to manage timepoints (using boost::posix_time::ptime) and durations (using boost::posix_time::time_duration. Note that we define BOOST_DATE_TIME_POSIX_TIME_STD_CONFIG to ensure that boost date time classes are compiled with a nanosecond precision (as a precision of the order of 10-7s is required for some SAR computations). However, when compared to the results of OTB 7.4 that uses ossimplugin to manage timepoints and durations we observed a degradation from 50% to 100% in processing time in some applications (e.g. SAR orthorectification, SARDeburst).

boost::posix_time::ptime uses 96 bits to store the Unix time when it is compiled with a nanosecond precision, to ensure there is no overflow of the date. On the other end, ossimplugins uses a double to store the unix time. Because of the way ptime is implemented, we noticed that differences between two time points is very slow, and about half the processing time of OrthoRectification is spent performing these differences. whereas in ossim plugins the timepoints differences are efficient because double subtraction is not an expensive operation. But note that the ossim implementation is not very precise, because a double is not enough to store the Unix time with a 10-7 precision (this is why we initially choose to use boost instead of an implementation similar to the one of ossimplugin)

This merge request replaces the uses of boost::posix_time by std::chrono to manage time in SAR computations. As the chrono library functionalities are quite limited before C++ 20, the date.h header from the date library. This library was used as a draft for the extension of the chrono standard library in C++ 20. It is licensed under a MIT license and has been integrated as a third party module (OTBDate).

when templated with a nanosecond precision, std::chrono::time_point stores the unix time as a 64 bit integer. This makes the operations of time much more efficient. Note that storing the time points on a 64 bit integer means that only dates between 1677-09-21 00:12:43.145224192 and 2262-04-11 23:47:16.854775807 are supported (before overflow), which is more than enough for OTB applications.

This merge request also remove the OTB::Metadata::Time class (which is based on std::tm) that was used to store dates for optical captors by OTB::Metadata::TimePoint.

Implementation details

  • otb::MetaData::Duration is now a wrapper around std::chrono::nanoseconds
  • otb::MetaData::TimePoint is now a wrapper around std::chrono::time_point<std::chrono::system_clock, InternalDurationType>

As with the previous implementation based on boost date time, the following operators are implemented

  • Addition of two Duration
  • Subtraction of two Duration
  • Multiplication between a Duration and a scalar
  • division between a Duration and a scalar
  • ratio between two Duration
  • Addition of a TimePoint and a Duration
  • Subtraction of a Duration from a TimePoint
  • comparison (==, <, >, <=, >=) of two Duration
  • comparison (==, <, >, <=, >=) of two TimePoint

Additionally streaming operator (<< and >>) have been implemented for Duration and TimePoint. It is also possible to parse a TimePoint from a string and a format using the function

TimePoint ReadFormattedDate(const std::string & dateStr, const std::string & format = details::timePointFormat)

Supported format are documented here

The implementation from the date library write the date with the templated precision, and I don't think this is something that can be change with the to_stream parameters. This is different from the behavior of the boost datetime library. for exemple the same date is written

  • "2009-08-10T10:30:08.142149Z" by boost date time
  • "2009-08-10T10:30:08.142149000" by date (the Z could be added)

Therefore in the otb::TimePoint::Display method implementation, the result from to_stream is post-processed to return the same format as the one used in boost date time.

The implementation of GetModifiedJulianDay has been modified to perform the computation directly from the internal duration, instead of applying an offset to GetJulianDay, this modification causes slight numerical differences on some tests because of double error accumulation.

Tests

ioTvDateTest has been updated with additional unit tests for TimePoint (the already implemented unit test have not been modified)

A few baselines have been updated because of the differences produced by GetModifiedJulianDay.

Performances

On develop, an OrthoRectification of a Sentinel 1 product took 438.27s on my computer, with the modification it only took 271,13s. On the release 7.4 the same processing took 235,69s.

Copyright

The copyright owner is CNES and has signed the ORFEO ToolBox Contributor License Agreement.


Check before merging:

  • All discussions are resolved
  • At least 2 👍 votes from core developers, no 👎 vote.
  • The feature branch is (reasonably) up-to-date with the base branch
  • Dashboard is green
  • Copyright owner has signed the ORFEO ToolBox Contributor License Agreement
  • Optionally, run git diff develop... -U0 --no-color | clang-format-diff.py -p1 -i on latest changes and commit
Edited by Cédric Traizet

Merge request reports