// baltzo_defaultzoneinfocache.h -*-C++-*- #ifndef INCLUDED_BALTZO_DEFAULTZONEINFOCACHE #define INCLUDED_BALTZO_DEFAULTZONEINFOCACHE #include <bsls_ident.h> BSLS_IDENT("$Id: $") //@PURPOSE: Provide facilities to manage a default Zoneinfo cache object. // //@CLASSES: // baltzo::DefaultZoneinfoCache: default Zoneinfo cache utilities // baltzo::DefaultZoneinfoCacheScopedGuard: guard for default Zoneinfo cache // //@SEE_ALSO: baltzo_zoneinfocache, baltzo_timezoneutil // //@DESCRIPTION: This component provides a namespace, // 'baltzo::DefaultZoneinfoCache', for utility functions that install and // access a default Zoneinfo cache object. In addition, this component // provides a mechanism, 'baltzo::DefaultZoneinfoCacheScopedGuard', that // facilities the temporary installation of the default Zoneinfo cache object // (for the lifetime of the guard object). // // Operations that convert a time value either from, or to, a local time // representation, commonly require access to time-zone information provided // from a 'baltzo::ZoneinfoCache' object. Because a single set of time-zone // data is generally applicable to an entire task, this component provides the // installation and use of a process-wide default 'baltzo::ZoneinfoCache' // object (i.e., a Zoneinfo cache). See 'baltzo_timezoneutil' for examples of // how operations may take advantage of this default cache. Note that the // behavior is undefined unless this facility is used before 'main' exits. // ///The Automatically-Installed Default-Cache Instance ///-------------------------------------------------- // If a 'baltzo::ZoneinfoCache' object is not explicitly configured via the // 'setDefaultCache' method, the *first* call to 'defaultCache' automatically // initializes and returns a new Zoneinfo cache object (i.e., a default // default-cache object). Subsequent calls to the 'defaultCache' method return // that same object, unless the default Zoneinfo cache object is reset via the // 'setDefaultCache' method. // // Clients are encouraged to use this automatically configured default cache // object unless there is a particular need to configure it explicitly. // ///Default Time Zone Data Location ///- - - - - - - - - - - - - - - - // The mechanism for automatically configuring a default Zoneinfo cache object // first assumes that the TZ Database files are rooted in a directory // specified by the 'BDE_ZONEINFO_ROOT_PATH' environment variable. If // 'BDE_ZONEINFO_ROOT_PATH' is unset, the 'baltzo::DefaultZoneinfoCache' will // attempt to use one of a (priority ordered) sequence of several (platform // specific) standard locations. Finally, if no standard directory exists that // contains TZ Database files, the 'baltzo::ZoneinfoCache' will use the current // working directory. // // If a TZ Database file is not available in the "configured" directory, // requests for that time-zone's data will fail. Additional information about // TZ Database files can be found in 'baltzo_datafileloader'. // ///Thread Safety ///------------- // The 'baltzo::DefaultZoneinfoCache::defaultCache' method is *thread-safe* // unless it is called concurrently with either the 'setDefaultCache' method or // the 'putenv' POSIX function. Note that the 'baltzo::ZoneinfoCache' // singleton is, itself, fully thread-safe (see 'baltzo_zoneinfocache'); it is // only the installation of the default cache that is not thread-safe. // // 'baltzo::DefaultZoneinfoCache::setDefaultCache' is *not* *thread-safe*. The // expected usage is that clients that choose to *explicitly* configure the // default cache for their application (rather than using the automatically // provided default cache) will set the default cache during the initialization // of their application (while the application has a single thread). // // The 'baltzo::DefaultTimeZoneCacheScopedGuard' class is not, even *minimally* // *thread-safe* meaning that its constructor cannot be invoked concurrently // from multiple threads. // ///Usage ///----- // The following usage examples demonstrate configuring and accessing the // default Zoneinfo cache object. // ///Example 1: Accessing the Default Zoneinfo Cache Object /// - - - - - - - - - - - - - - - - - - - - - - - - - - - // A common application of the 'baltzo::DefaultZoneinfoCache' is to determine // the appropriate time-zone information to use for an operation that accepts a // Zoneinfo cache as an optional argument (e.g., see 'baltzo_timezoneutil'). // Note that this usage pattern is also seen for the default allocator (see // 'bslma_default'). // // First, we declare a function, 'getLocalTimeDescriptor', that returns the // local-time descriptor for a given time in a particular time zone. This // method takes an optional 'baltzo::ZoneinfoCache' address argument, via the // default 'zoneinfoCache' parameter. If 'zoneinfoCache' is unspecified or 0, // the default Zoneinfo cache is used for the operation: //.. // int getLocalTimeDescriptor(baltzo::LocalTimeDescriptor *result, // const bdlt::Datetime& utcTime, // const char *timeZoneId, // baltzo::ZoneinfoCache *zoneinfoCache = 0) // // Load, into the specified 'result', the local time descriptor // // indicated by the specified 'utcTime' and the specified 'timeZoneId'. // // Return 0 on success, and a non-zero value otherwise. Optionally // // specify a 'zoneinfoCache' used to retrieve time-zone information. // // If 'zoneinfoCache' is 0, the currently installed default Zoneinfo // // cache is used. // { //.. // We call the 'baltzo::DefaultZoneinfoCache::defaultCache' method, which // returns 'zoneinfoCache' if 'zoneinfoCache' is not 0, and the currently // installed Zoneinfo cache otherwise. //.. // baltzo::ZoneinfoCache *cache = // baltzo::DefaultZoneinfoCache::defaultCache(zoneinfoCache); // BSLS_ASSERT(0 != cache); //.. // Then, now that we have a of Zoneinfo cache object, we access the time-zone // data for 'timeZoneId', and obtain the local-time descriptor for 'utcTime': //.. // const baltzo::Zoneinfo *zoneinfo = cache->getZoneinfo(timeZoneId); // if (0 == zoneinfo) { // // // Data for 'timeZoneId' is not available in the cache, so return // // an error. // // return 1; // RETURN // } //.. // Now we invoke the 'findTransitionForUtcTime' method on 'zoneInfo' to obtain // the transition holding the local time descriptor for the specified // 'utcTime': //.. // baltzo::Zoneinfo::TransitionConstIterator it = // zoneinfo->findTransitionForUtcTime(utcTime); // *result = it->descriptor(); // return 0; // } //.. // Note that 'findTransitionForUtcTime' has undefined behavior if the supplied // search time is earlier than that of the first transition in the Zoneinfo // object. However a 'baltzo::ZoneinfoCache' (from which we obtained // 'zoneinfo') is guaranteed to return a well-formed 'baltzo::Zoneinfo' (see // 'baltzo::ZoneinfoUtil::isWellFormed'), meaning that it contains a transition // at the first representable 'bdlt::Datetime' value, so the following call to // 'findTransitionForUtcTime' is guaranteed to have defined behavior. // // Finally, we call our 'getLocalDescriptor' defined above: //.. // baltzo::LocalTimeDescriptor result; // int rc = getLocalTimeDescriptor(&result, // bdlt::Datetime(bdlt::Date(2011, 03, 21), // bdlt::Time(20, 57)), // "America/New_York"); // assert(0 == rc); // assert("EDT" == result.description()); // assert(true == result.dstInEffectFlag()); // assert(-14400 == result.utcOffsetInSeconds()); //.. // Notice that since we did not specify the optional 'baltzo::ZoneinfoCache' // object, data will automatically come from the configured default // 'baltzo::ZoneinfoCache' object. // ///Example 2: Installing a Default Zoneinfo Cache Object ///- - - - - - - - - - - - - - - - - - - - - - - - - - - // In this example we demonstrate how to configure the default time-zone cache // for a process. Note that many application may not need to explicitly // configure the default cache object, but can instead use the automatically // configured default object. Also note that the default time-zone cache is // intended to be configured by the *owner* of 'main'. The default object // should be set during the initialization of an application (while the task // has a single thread) and unset just prior to termination (when there is // similarly a single thread): //.. // int main(int argc, const char *argv[]) // { // // // ... //.. // First, we create and configure a 'baltzo::DataFileLoader' object: //.. // baltzo::DataFileLoader loader; // loader.configureRootPath("./test"); //.. // Then, we use 'loader' to initialize a 'baltzo::ZoneinfoCache' object: //.. // baltzo::ZoneinfoCache cache(&loader); //.. // Next, we create a 'baltzo::DefaultZoneinfoCacheScopedGuard' to set the // default Zoneinfo cache for the lifetime of the guard, and then verify the // the address returned by the 'defaultCache' method is correct: //.. // { // baltzo::DefaultZoneinfoCacheScopedGuard guard(&cache); // assert(&cache == baltzo::DefaultZoneinfoCache::defaultCache()); // // // ... //.. // Finally, at the end of this scope the 'guard' is destroyed, and the default // Zoneinfo cache is restored to its previous value: //.. // } // assert(&cache != baltzo::DefaultZoneinfoCache::defaultCache()); // // // ... // } //.. #include <balscm_version.h> #include <baltzo_zoneinfocache.h> #include <bsls_libraryfeatures.h> #include <bsl_vector.h> #ifdef BSLS_LIBRARYFEATURES_HAS_CPP17_PMR # include <memory_resource> #endif // BSLS_LIBRARYFEATURES_HAS_CPP17_PMR #include <vector> namespace BloombergLP { namespace baltzo { // ========================== // class DefaultZoneinfoCache // ========================== struct DefaultZoneinfoCache { // This struct provides a namespace for functions that manage and access // the default time-zone data cache. private: // PRIVATE CLASS METHODS static ZoneinfoCache *instance(); // Return the address of the currently configured modifiable default // time-zone information (Zoneinfo) cache object. public: // CLASS METHODS static ZoneinfoCache *defaultCache(ZoneinfoCache *cache = 0); // Return the default 'ZoneinfoCache' object for this process if the // optionally specified 'cache' is 0, and 'cache' otherwise. If // 'cache' is 0, and the default Zoneinfo cache object has not been set // -- either explicitly with a call to 'setDefaultCache', or implicitly // through a prior call to this method -- then initialize a new // 'ZoneinfoCache' object, set the default cache object to the newly // created object, and return its address. static const char *defaultZoneinfoDataLocation(); // Return a null terminated (C-style) string describing the default // file-system path of the (expected) root of the Zoneinfo Database // time-zone information files. The returned string will be the // current value of the 'BDE_ZONEINFO_ROOT_PATH' environment variable, // if set, or else one of a (priority ordered) sequence of // (platform-specific) standard directories, if one of those // directories exists and contains a sub-set of expected files, and the // current directory, otherwise. The behavior is undefined if the // returned address is dereferenced after a subsequent call to the // 'putenv' POSIX function to set the 'BDE_ZONEINFO_ROOT_PATH' // environment variable. static void loadDefaultZoneinfoDataLocations( bsl::vector<const char *> *locations); static void loadDefaultZoneinfoDataLocations( std::vector<const char *> *locations); #ifdef BSLS_LIBRARYFEATURES_HAS_CPP17_PMR static void loadDefaultZoneinfoDataLocations( std::pmr::vector<const char *> *locations); // Load into the specified 'locations', the sequence of null // terminated C-style strings that characterizes the default paths for // Zoneinfo data, accessed by this class, on this platform. #endif static ZoneinfoCache *setDefaultCache(ZoneinfoCache *cache); // Set the address of the default 'ZoneinfoCache' object to the // specified 'cache'. Return the address of the default cache object // that was set by a previous call to this method, or 0 if no call to // this method was previously executed. The behavior is undefined // unless (1) 'cache' remains valid until the last operation is // performed on this process's default cache, or a subsequent call to // 'setDefaultCache', and (2) this method is *not* called from one // thread while another thread is attempting to access the default time // zone cache instance (i.e., this method is *not* thread-safe). Note // that this method is intended for use *only* by the *owner* of 'main' // (and for testing purposes) where the caller affirmatively takes // responsibility for the behavior of all clients of the default time // zone cache. }; // ===================================== // class DefaultZoneinfoCacheScopedGuard // ===================================== class DefaultZoneinfoCacheScopedGuard { // Upon construction, an object of this class saves the current default // Zoneinfo cache and installs the user-specified Zoneinfo cache as the // default. On destruction, the previous default Zoneinfo cache is // restored. This class is not even *minimally* *thread* *safe*. For // terminology see 'bsldoc_glossary'. // DATA ZoneinfoCache *d_previousCache_p; // original default Zoneinfo cache (to // be restored on destruction) private: // NOT IMPLEMENTED DefaultZoneinfoCacheScopedGuard(const DefaultZoneinfoCacheScopedGuard&); DefaultZoneinfoCacheScopedGuard& operator=( const DefaultZoneinfoCacheScopedGuard&); public: // CREATORS explicit DefaultZoneinfoCacheScopedGuard(ZoneinfoCache *cache); // Create a scoped guard that installs the specified 'cache' as the // default Zoneinfo cache. Note that the pre-existing default time // Zoneinfo cache is automatically restored on destruction of this // object. ~DefaultZoneinfoCacheScopedGuard(); // Restore the default Zoneinfo cache that was in place when this // scoped guard was created, and destroy this guard. }; // ============================================================================ // INLINE DEFINITIONS // ============================================================================ // ------------------- // class ZoneinfoCache // ------------------- // CLASS METHODS inline ZoneinfoCache *baltzo::DefaultZoneinfoCache::defaultCache(ZoneinfoCache *cache) { return cache ? cache : instance(); } // ------------------------------------- // class DefaultZoneinfoCacheScopedGuard // ------------------------------------- // CREATORS inline DefaultZoneinfoCacheScopedGuard::DefaultZoneinfoCacheScopedGuard( ZoneinfoCache *cache) : d_previousCache_p(DefaultZoneinfoCache::setDefaultCache(cache)) { } inline DefaultZoneinfoCacheScopedGuard::~DefaultZoneinfoCacheScopedGuard() { DefaultZoneinfoCache::setDefaultCache(d_previousCache_p); } } // close package namespace } // close enterprise namespace #endif // ---------------------------------------------------------------------------- // Copyright 2018 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 ----------------------------------