// bdlt_epochutil.h -*-C++-*- #ifndef INCLUDED_BDLT_EPOCHUTIL #define INCLUDED_BDLT_EPOCHUTIL #include <bsls_ident.h> BSLS_IDENT("$Id: $") //@PURPOSE: Conversion between absolute/relative time with respect to epoch. // //@CLASSES: // bdlt::EpochUtil: non-primitive functions on epoch-related conversions // //@SEE_ALSO: bdlt_datetime, bdlt_datetimeinterval, bsls_timeinterval // //@DESCRIPTION: This component defines a namespace, 'bdlt::EpochUtil', // providing non-primitive conversions between two different concepts of time. // Clients can convert between absolute time ('bdlt::Datetime') and relative // time (such as 'bsl::time_t', 'bdlt::EpochUtil::TimeT64', // 'bsls::TimeInterval', and 'bdlt::DatetimeInterval') with respect to the Unix // standard "epoch" (1970/01/01_00:00:00.000 UTC), henceforth, simply referred // to as "the epoch". Also provided is a fast, thread-safe method, 'epoch', // for access to a pre-instantiated 'bdlt::Datetime' object whose (constant) // value is that of the epoch. // // Due to different resolutions, conversions between absolute/relative time are // possibly lossy when converting from a type with higher resolution to one // with lower resolution. The value of the type with higher resolution will be // truncated (not rounded). The following table lists the resolution of the // types involved in this component: //.. // Type Reference Resolution // --------------------- -------------- ------------ // bsl::time_t relative seconds // bdlt::EpochUtil::TimeT64 relative seconds // bdlt::Datetime absolute (UTC) microseconds // bdlt::DatetimeInterval relative microseconds // bsls::TimeInterval relative nanoseconds //.. // Note that the interfaces using 'bdlt::EpochUtil::TimeT64' can be validly // used for values before the epoch (corresponding to negative 'TimeT64' // values), whereas the interfaces using 'bsl::time_t' have undefined behavior // for such input. Furthermore, even on platforms where 'bsl::time_t' is a // 64-bit value, clients of this component are strongly encouraged to use the // 'TimeT64'-based methods to avoid limitations imposed by the // 'bsl::time_t'-based methods required to ensure identical behavior on all // supported platforms (e.g., see the function-level documentation for // 'convertToTimeT(bsl::time_t)'). // // Also note that these conversions do not take into account the leap seconds // (25 as of this writing) added to UTC by the International Earth Rotation and // Reference Systems Service, but simply regard each day as having a fixed // number of seconds (24 hours * 60 minutes per hour * 60 seconds per minute). // ///Thread Safety ///------------- // It is safe to invoke any function defined in this component in two or more // separate threads simultaneously, provided no other thread is simultaneously // modifying the argument passed by reference to a non-modifiable user-defined // type (such as 'bsls::TimeInterval' or 'bdlt::Datetime'). // ///Usage ///----- // This section illustrates intended use of this component. // ///Example 1: Converting Between Various Representations of Time ///- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // When processing date/time data, we are often required to deal with a variety // of ways in which to represent that data, and therefore we need to be able to // convert between those representations. We can use the methods contained in // 'bdlt::EpochUtil' to do this. // // First, we set up date/time input values in a variety of formats. We'll use // 900ms past midnight of January 1, 2000 as the base date and time, dropping // the 900ms if the resolution of a format doesn't support it: //.. // const bsl::time_t inputTime (946684800); // const bsls::TimeInterval inputTimeInterval (946684800, 900000000); // const bdlt::DatetimeInterval inputDatetimeInterval( // 0, 0, 0, 0, 946684800900LL); // const bdlt::Datetime inputDatetime (2000, 1, 1, 0, 0, 0, 900); //.. // Then, we set up a set of output variables to receive converted values: //.. // bsl::time_t outputTime; // bsls::TimeInterval outputTimeInterval; // bdlt::DatetimeInterval outputDatetimeInterval; // bdlt::Datetime outputDatetime; //.. // Next, because 'bdlt::EpochUtil' uses 'bdlt::Datetime' as the common format // for conversion, we will set up a pair of variables in this format to // represent the values we expect to see: //.. // const bdlt::Datetime epochDatetimeWithMs (2000, 1, 1, 0, 0, 0, 900); // const bdlt::Datetime epochDatetimeWithoutMs(2000, 1, 1, 0, 0, 0, 0); //.. // Now, we perform a set of conversions to 'bdlt::Datetime' and verify that the // results are correct. We will use the conversion methods that return by // value: //.. // outputDatetime = bdlt::EpochUtil::convertFromTimeT(inputTime); // assert(epochDatetimeWithoutMs == outputDatetime); // // outputDatetime = // bdlt::EpochUtil::convertFromTimeInterval(inputTimeInterval); // assert(epochDatetimeWithMs == outputDatetime); // // outputDatetime = // bdlt::EpochUtil::convertFromDatetimeInterval(inputDatetimeInterval); // assert(epochDatetimeWithMs == outputDatetime); //.. // Finally, we perform a set of conversions from 'bdlt::Datetime' and verify // that the results are correct. This time, for variety, we will illustrate // the conversion methods which return through an object pointer: //.. // assert(0 == bdlt::EpochUtil::convertToTimeT(&outputTime, inputDatetime)); // assert(inputTime == outputTime); // // assert(0 == bdlt::EpochUtil::convertToTimeInterval(&outputTimeInterval, // inputDatetime)); // assert(inputTimeInterval == outputTimeInterval); // // assert(0 == bdlt::EpochUtil::convertToDatetimeInterval( // &outputDatetimeInterval, // inputDatetime)); // assert(inputDatetimeInterval == outputDatetimeInterval); //.. #include <bdlscm_version.h> #include <bdlt_datetime.h> #include <bdlt_datetimeimputil.h> #include <bdlt_datetimeinterval.h> #include <bdlt_time.h> #include <bsls_assert.h> #include <bsls_review.h> #include <bsls_timeinterval.h> #include <bsls_types.h> // 'Int64', 'Uint64' #include <bsl_ctime.h> // 'bsl::time_t' namespace BloombergLP { namespace bdlt { // ================ // struct EpochUtil // ================ struct EpochUtil { // This 'struct' provides a namespace for a suite of non-primitive // functions providing conversions between absolute 'Datetime' values and // corresponding relative time intervals with respect to the Unix standard // epoch time, returned by the 'epoch' method. These methods are // alias-safe, thread-safe, and exception-neutral. Functions are provided // for returning converted values by value or through a result pointer. public: // TYPES typedef bsls::Types::Int64 TimeT64; // 'TimeT64' is an alias for a 64-bit integral type representing // seconds from the epoch in UTC. Note that, in contrast with // 'bsl::time_t', this type can be used in conversions to and from // 'Datetime' values that are less than the epoch (corresponding to // negative 'TimeT64' values). private: // CLASS DATA static const TimeT64 s_earliestAsTimeT64; // January 1, 0001 00:00:00 static const TimeT64 s_latestAsTimeT64; // December 31, 9999 23:59:59 public: // CLASS METHODS static const Datetime& epoch(); // Return a reference providing non-modifiable access to the epoch // time: midnight on January 1, 1970. Note that this value exists // before any code is executed and will continue to exist, unchanged, // until the program exits. // 'time_t'-Based Methods static Datetime convertFromTimeT(bsl::time_t time); // Return, as a 'Datetime', the absolute datetime computed as the sum // of the specified relative 'time' and the epoch. The behavior is // undefined unless '0 <= time' and, for the resultant 'Datetime' 'dt', // '0 == convertToTimeT(&time, dt)' (i.e., 'time' is representable as a // 32-bit 'int'). Note that the returned value will use Coordinated // Universal Time (UTC) as a reference. static void convertFromTimeT(Datetime *result, bsl::time_t time); // Load into the specified 'result' the absolute datetime converted to // a 'Datetime', computed as the sum of the specified relative 'time' // and the epoch. The behavior is undefined unless '0 <= time' and // '0 == convertToTimeT(&time, *result)' (i.e., 'time' is representable // as a 32-bit 'int'). Note that 'result' will use Coordinated // Universal Time (UTC) as a reference. static bsl::time_t convertToTimeT(const Datetime& datetime); // Return the relative time computed as the difference between the // specified absolute 'datetime' and the epoch. The behavior is // undefined unless 'datetime - epoch() >= DatetimeInterval()' and the // converted 'datetime' can be represented in the destination format on // all supported platforms (i.e., the resultant value is representable // as a 32-bit 'int'). Note that 'datetime' is assumed to use // Coordinated Universal Time (UTC) as a reference. Also note that if // error detection is desired, the overloaded version that loads the // converted 'datetime' into a supplied destination object should be // used. static int convertToTimeT(bsl::time_t *result, const Datetime& datetime); // Load into the specified 'result' the relative time computed as the // difference between the specified absolute 'datetime' and the epoch. // Return 0 on success, and a non-zero value (with no effect on // 'result') if 'datetime - epoch() < DatetimeInterval()' or 'datetime' // cannot be represented in the destination format on all supported // platforms (i.e., the computed '*result' is not representable as a // 32-bit 'int'). Note that 'datetime' is assumed to use Coordinated // Universal Time (UTC) as a reference. // 'TimeT64'-Based Methods static Datetime convertFromTimeT64(TimeT64 time); // Return, as a 'Datetime', the absolute datetime computed as the sum // of the specified relative 'time' and the epoch. The behavior is // undefined unless the converted 'time' can be represented in the // destination format. Note that the returned value will use // Coordinated Universal Time (UTC) as a reference. Also note that if // error detection is desired, the overloaded version that loads the // converted 'time' into a supplied destination object should be used. static int convertFromTimeT64(Datetime *result, TimeT64 time); // Load into the specified 'result' the absolute datetime converted to // a 'Datetime', computed as the sum of the specified relative 'time' // and the epoch. Return 0 on success, and a non-zero value (with no // effect on 'result') if 'time' cannot be represented in the // destination format. Note that 'result' will use Coordinated // Universal Time (UTC) as a reference. static TimeT64 convertToTimeT64(const Datetime& datetime); // Return the relative time computed as the difference between the // specified absolute 'datetime' and the epoch. Note that 'datetime' // is assumed to use Coordinated Universal Time (UTC) as a reference. static void convertToTimeT64(TimeT64 *result, const Datetime& datetime); // Load into the specified 'result' the relative time computed as the // difference between the specified absolute 'datetime' and the epoch. // Note that 'datetime' is assumed to use Coordinated Universal Time // (UTC) as a reference. // 'bsls::TimeInterval'-Based Methods static Datetime convertFromTimeInterval( const bsls::TimeInterval& timeInterval); // Return, as a 'Datetime', the absolute datetime computed as the sum // of the specified relative 'timeInterval' and the epoch. The // behavior is undefined unless the conversion result can be // represented as a 'Datetime'. Note that the conversion is // potentially lossy as the resolution of 'bsls::TimeInterval' is // greater than that of 'Datetime'. static void convertFromTimeInterval( Datetime *result, const bsls::TimeInterval& timeInterval); // Load into the specified 'result' the absolute datetime converted to // a 'Datetime', computed as the sum of the specified relative // 'timeInterval' and the epoch. The behavior is undefined unless the // conversion result can be represented as a 'Datetime'. Note that the // conversion is potentially lossy as the resolution of // 'bsls::TimeInterval' is greater than that of 'Datetime'. static bsls::TimeInterval convertToTimeInterval(const Datetime& datetime); // Return, as a 'bsls::TimeInterval', the relative time computed as the // difference between the specified absolute 'datetime' and the epoch. // 'DatetimeInterval'-Based Methods static Datetime convertFromDatetimeInterval( const DatetimeInterval& datetimeInterval); // Return, as a 'Datetime', the absolute datetime computed as the sum // of the specified relative 'datetimeInterval' and the epoch. The // behavior is undefined unless the conversion result can be // represented as a 'Datetime'. static void convertFromDatetimeInterval( Datetime *result, const DatetimeInterval& datetimeInterval); // Load into the specified 'result' the absolute datetime converted to // a 'Datetime', computed as the sum of the specified relative // 'datetimeInterval' and the epoch. The behavior is undefined unless // the conversion result can be represented as a 'Datetime'. static DatetimeInterval convertToDatetimeInterval( const Datetime& datetime); // Return, as a 'DatetimeInterval', the relative time computed as the // difference between the specified absolute 'datetime' and the epoch. // DEPRECATED CLASS METHODS static int convertToTimeInterval(bsls::TimeInterval *result, const Datetime& datetime); // !DEPRECATED!: Use 'convertToTimeInterval(datetime)' instead. // // If 'datetime - epoch() >= DatetimeInterval()', load into the // specified 'result' the relative time converted to a // 'bsls::TimeInterval', computed as the difference between the // specified absolute 'datetime' and the epoch, and return 0. // Otherwise, return a non-zero value (with no effect on 'result'). static int convertToDatetimeInterval(DatetimeInterval *result, const Datetime& datetime); // !DEPRECATED!: Use 'convertToDatetimeInterval(datetime)' instead. // // If 'datetime - epoch() >= DatetimeInterval()', load into the // specified 'result' the relative time converted to a // 'DatetimeInterval', computed as the difference between the specified // absolute 'datetime' and the epoch, and return 0. Otherwise, return // a non-zero value (with no effect on 'result'). }; // ============================================================================ // INLINE DEFINITIONS // ============================================================================ // ---------------- // struct EpochUtil // ---------------- // CLASS METHODS inline const Datetime& EpochUtil::epoch() { return *DatetimeImpUtil::epoch_1970_01_01(); } // 'time_t'-Based Methods inline Datetime EpochUtil::convertFromTimeT(bsl::time_t time) { BSLS_REVIEW(0 <= time); Datetime datetime(epoch()); datetime.addSeconds(time); return datetime; } inline void EpochUtil::convertFromTimeT(Datetime *result, bsl::time_t time) { BSLS_REVIEW(result); BSLS_REVIEW(0 <= time); *result = epoch(); result->addSeconds(time); } inline bsl::time_t EpochUtil::convertToTimeT(const Datetime& datetime) { const DatetimeInterval dti = datetime - epoch(); BSLS_REVIEW(dti >= DatetimeInterval()); BSLS_REVIEW(dti.totalSeconds() <= 0x7fffffffLL); // Note that, with safe-assertions disabled, the representation of // 'bsl::time_t' must not affect the resultant 'bsl::time_t' (i.e., in case // 'bsl::time_t' is 64-bit). return bsl::time_t(static_cast<int>(dti.totalSeconds())); } inline int EpochUtil::convertToTimeT(bsl::time_t *result, const Datetime& datetime) { BSLS_REVIEW(result); const DatetimeInterval dti = datetime - epoch(); if (dti < DatetimeInterval()) { return 1; // RETURN } const bsls::Types::Int64 seconds = dti.totalSeconds(); if (seconds > 0x7fffffffLL) { return 1; // RETURN } *result = static_cast<bsl::time_t>(seconds); return 0; } // 'TimeT64'-Based Methods inline Datetime EpochUtil::convertFromTimeT64(TimeT64 time) { BSLS_REVIEW(s_earliestAsTimeT64 <= time); BSLS_REVIEW( time <= s_latestAsTimeT64); Datetime datetime(epoch()); datetime.addSeconds(time); return datetime; } inline int EpochUtil::convertFromTimeT64(Datetime *result, TimeT64 time) { BSLS_REVIEW(result); if (time < s_earliestAsTimeT64 || time > s_latestAsTimeT64) { return 1; // RETURN } *result = epoch(); result->addSeconds(time); return 0; } inline EpochUtil::TimeT64 EpochUtil::convertToTimeT64(const Datetime& datetime) { int hour; int minute; int second; datetime.getTime(&hour, &minute, &second); Datetime dt(datetime.date()); dt.setTime(hour, minute, second); return TimeT64((dt - epoch()).totalSeconds()); } inline void EpochUtil::convertToTimeT64(TimeT64 *result, const Datetime& datetime) { BSLS_REVIEW(result); *result = EpochUtil::convertToTimeT64(datetime); } // 'bsls::TimeInterval'-Based Methods inline Datetime EpochUtil::convertFromTimeInterval( const bsls::TimeInterval& timeInterval) { return epoch() + timeInterval; } inline void EpochUtil::convertFromTimeInterval( Datetime *result, const bsls::TimeInterval& timeInterval) { BSLS_REVIEW(result); *result = epoch() + timeInterval; } inline bsls::TimeInterval EpochUtil::convertToTimeInterval(const Datetime& datetime) { const DatetimeInterval dti = datetime - epoch(); return bsls::TimeInterval(dti.totalSeconds(), dti.milliseconds() * 1000000 + dti.microseconds() * 1000); } // 'DatetimeInterval'-Based Methods inline Datetime EpochUtil::convertFromDatetimeInterval( const DatetimeInterval& datetimeInterval) { return epoch() + datetimeInterval; } inline void EpochUtil::convertFromDatetimeInterval( Datetime *result, const DatetimeInterval& datetimeInterval) { BSLS_REVIEW(result); *result = epoch() + datetimeInterval; } inline DatetimeInterval EpochUtil::convertToDatetimeInterval(const Datetime& datetime) { return datetime - epoch(); } // DEPRECATED CLASS METHODS inline int EpochUtil::convertToTimeInterval(bsls::TimeInterval *result, const Datetime& datetime) { BSLS_REVIEW(result); const DatetimeInterval dti = datetime - epoch(); if (dti < DatetimeInterval()) { return 1; // RETURN } result->setInterval(dti.totalSeconds(), dti.milliseconds() * 1000000 + dti.microseconds() * 1000); return 0; } inline int EpochUtil::convertToDatetimeInterval(DatetimeInterval *result, const Datetime& datetime) { BSLS_REVIEW(result); if (datetime - epoch() < DatetimeInterval()) { return 1; // RETURN } *result = datetime - epoch(); return 0; } } // close package namespace } // close enterprise namespace #endif // ---------------------------------------------------------------------------- // Copyright 2017 Bloomberg Finance L.P. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ----------------------------- END-OF-FILE ----------------------------------