// baltzo_datafileloader.h -*-C++-*- #ifndef INCLUDED_BALTZO_DATAFILELOADER #define INCLUDED_BALTZO_DATAFILELOADER #include <bsls_ident.h> BSLS_IDENT("$Id: $") //@PURPOSE: Provide a concrete 'baltzo::Loader' for Zoneinfo binary files. // //@CLASSES: // baltzo::DataFileLoader: concrete 'baltzo::Loader' for Zoneinfo binary data // //@SEE_ALSO: baltzo_zoneinfobinaryreader, baltzo_zoneinfoutil // //@DESCRIPTION: This component provides a mechanism, 'baltzo::DataFileLoader', // that is a concrete implementation of the 'baltzo::Loader' protocol for // loading, into a 'baltzo::Zoneinfo' object, the properties of a time zone // described in a Zoneinfo binary database file. The following inheritance // hierarchy diagram shows the classes involved and their methods: //.. // ,----------------------. // ( baltzo::DataFileLoader ) // `----------------------' // | ctor // | configureRootPath // | configureRootPathIfPlausible // | loadTimeZoneFilePath // | rootPath // | isRootPathPlausible // V // ,--------------. // ( baltzo::Loader ) // `--------------' // dtor // loadTimeZone //.. // A 'baltzo::DataFileLoader' is supplied a file-system location using the // 'configureRootPath' method. This location should correspond to the root // directory of a hierarchy containing Zoneinfo binary data files, where each // Zoneinfo time-zone identifier indicates a relative path from the root // directory to the binary data file containing the information for that time // zone. Accordingly, 'baltzo::DataFileLoader' provides a method that, given a // time-zone identifier, will open the corresponding data file (relative to the // root directory tree supplied at construction), and load, into a // 'baltzo::Zoneinfo' object, the data from that file. // ///Zoneinfo (TZ Database) Files ///---------------------------- // The Zoneinfo database, also referred to as either the TZ database or the // Olson database (after its creator, Arthur Olson), is a standard // public-domain time-zone information distribution used by many software // systems (including a number of Unix variants and the Java Runtime // Environment). Information about the Zoneinfo database can be found online // at 'http://www.twinsun.com/tz/tz-link.htm', including the time-zone rules // for the supported time zones, and source code for the 'zic' compiler (for // compiling those rules into the binary representation used by this // component). See 'baltzo_zoneinfobinaryreader' for more information about // the binary file format. // ///Directory Hierarchy ///- - - - - - - - - - // Zoneinfo database files are typically held in a standard file-system // directory hierarchy. Zoneinfo time-zone identifiers (e.g., // "America/New_York") serve not only as an identifier, but as a relative path // (using the UNIX file separator, '/') to the file containing data for the // time zone. So, given a hypothetical root directory "/etc/time_zones", the // time-zone data file for "America/New_York" will be located in // "/etc/time_zones/America/New_York". // ///Thread Safety ///------------- // 'baltzo::DataFileLoader' is *const* *thread-safe*, meaning that accessors // may be invoked concurrently from different threads, but it is not safe to // access or modify a 'baltzo::DataFileLoader' in one thread while another // thread modifies the same object. // ///Usage ///----- // The following examples illustrate how to use a 'baltzo::DataFileLoader' to // load the Zoneinfo time-zone data for a time zone. // ///Example 1: Prologue: Creating a Example Data File ///- - - - - - - - - - - - - - - - - - - - - - - - - // First we need to create one time-zone data file on which to operate. In // practice, clients should *not* generate data files in this manner. Data // files are typically created using the 'zic' compiler -- a publicly available // tool provided as part of the standard Zoneinfo distribution (see // 'http://www.twinsun.com/tz/tz-link.htm') -- and deployed in a standard // directory location (see 'baltzo_defaultzoneinfocache'). // // We start by defining static binary data for "Asia/Bangkok", (chosen because // it is relatively small): //.. // const char ASIA_BANGKOK_DATA[] = { // 0x54, 0x5a, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, // 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x08, 0xa2, 0x6a, 0x67, 0xc4, // 0x01, 0x00, 0x00, 0x5e, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x62, 0x70, 0x00, // 0x04, 0x42, 0x4d, 0x54, 0x00, 0x49, 0x43, 0x54, 0x00, 0x00, 0x00, 0x00, // 0x00, 0x54, 0x5a, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0c, 0xff, 0xff, 0xff, // 0xff, 0x56, 0xb6, 0x85, 0xc4, 0xff, 0xff, 0xff, 0xff, 0xa2, 0x6a, 0x67, // 0xc4, 0x01, 0x02, 0x00, 0x00, 0x5e, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x5e, // 0x3c, 0x00, 0x04, 0x00, 0x00, 0x62, 0x70, 0x00, 0x08, 0x4c, 0x4d, 0x54, // 0x00, 0x42, 0x4d, 0x54, 0x00, 0x49, 0x43, 0x54, 0x00, 0x00, 0x00, 0x00, // 0x00, 0x00, 0x00, 0x0a, 0x49, 0x43, 0x54, 0x2d, 0x37, 0x0a // }; //.. // Then we create a testing sub-directory "test/Asia" that will hold the data // file for Bangkok. Note that "Asia/Bangkok" is the time-zone identifier for // Bangkok and "Asia/Bangkok" also serves as a relative path (from our "./test" // sub-directory) to that data file. //.. // #ifdef BSLS_PLATFORM_OS_WINDOWS // const char *TEST_DIRECTORY = "test\\Asia"; // const char *TEST_FILE = "test\\Asia\\Bangkok"; // #else // const char *TEST_DIRECTORY = "test/Asia"; // const char *TEST_FILE = "test/Asia/Bangkok"; // #endif // int rc = bdls::FileUtil::createDirectories(TEST_DIRECTORY, true); // assert(0 == rc); //.. // Now we create a file for Bangkok and write the binary time-zone data to that // file. //.. // bsl::ofstream outputFile(TEST_FILE, bsl::ofstream::binary); // assert(outputFile.is_open()); // outputFile.write(ASIA_BANGKOK_DATA, sizeof(ASIA_BANGKOK_DATA)); // assert(outputFile); // outputFile.close(); //.. // The file 'Bangkok' should now appear in the 'Asia' sub-directory, under out // 'test' directory. // ///Example 2: Using a 'baltzo::DataFileLoader' to Load a Zoneinfo File ///- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // In this example we demonstrate how to use a 'baltzo::DataFileLoader' to load // a time-zone data file into a 'baltzo::Zoneinfo' object. We start by // creating a 'baltzo::DataFileLoader' object, 'loader', and configure it with // the relative path "test" which we created in Example 1 (Prologue). //.. // baltzo::DataFileLoader loader; // loader.configureRootPath("test"); //.. // Then we use the 'loadTimeZoneFilePath' method to verify that 'loader' will // correctly locate the test data file we've created: //.. // const char *BANGKOK_ID = "Asia/Bangkok"; // bsl::string bangkokDataPath; // rc = loader.loadTimeZoneFilePath(&bangkokDataPath, BANGKOK_ID); // assert(0 == rc); // assert(TEST_FILE == bangkokDataPath); // Note 'TEST_FILE' from Example 1. //.. // Now we create a 'baltzo::Zoneinfo' object, 'timeZone', and load it using // 'loader': //.. // baltzo::Zoneinfo timeZone; // rc = loader.loadTimeZone(&timeZone, BANGKOK_ID); // assert(0 == rc); //.. // Finally we confirm that certain properties of the 'timezone' object are in // agreement with the properties defined in the binary data (see // 'baltzo_zoneinfobinaryreader'): (1) That the object's identifier is // "Asia/Bangkok", and (2) the object contains three local time descriptors, // "LMT" (Local Mean Time), "BMT" (Bangkok Mean Time) and "ICT" (Indochina // Time), in that order: //.. // assert(BANGKOK_ID == timeZone.identifier()); // baltzo::Zoneinfo::LocalTimeDescriptorConstIterator iterator = // timeZone.descriptorBegin(); // assert("LMT" == iterator->description()); // ++iterator; // assert("BMT" == iterator->description()); // ++iterator; // assert("ICT" == iterator->description()); //.. // The 'timeZone' object can now be use for time-zone calculations. See // 'baltzo_zoneinfoutil'. // ///Epilogue: Removing the Created Files /// - - - - - - - - - - - - // The file hierarchy we created Example 1 solely for Example 2, is no longer // needed, and is removed by: //.. // int rc = bdls::FileUtil::remove(TEST_DIRECTORY, true); // assert(0 == rc); //.. #include <balscm_version.h> #include <baltzo_loader.h> #include <bslma_stdallocator.h> #include <bslma_usesbslmaallocator.h> #include <bslmf_nestedtraitdeclaration.h> #include <bsls_libraryfeatures.h> #include <bsls_platform.h> #include <bsl_string.h> #ifndef BDE_DONT_ALLOW_TRANSITIVE_INCLUDES # include <bslalg_typetraits.h> #endif // BDE_DONT_ALLOW_TRANSITIVE_INCLUDES #ifdef BSLS_LIBRARYFEATURES_HAS_CPP17_PMR # include <memory_resource> #endif // BSLS_LIBRARYFEATURES_HAS_CPP17_PMR namespace BloombergLP { namespace baltzo { class Zoneinfo; // ==================== // class DataFileLoader // ==================== class DataFileLoader : public Loader { // This component provides a concrete implementation of the 'DataLoader' // protocol for loading, into a 'Zoneinfo', the properties of a time zone // defined by an Zoneinfo (TZ Database) binary file located on the file // system. // DATA bsl::string d_rootPath; // root path for time-zone data file private: // NOT IMPLEMENTED DataFileLoader(const DataFileLoader&); DataFileLoader& operator=(const DataFileLoader&); public: // TYPES typedef bsl::allocator<char> allocator_type; // TRAITS BSLMF_NESTED_TRAIT_DECLARATION(DataFileLoader, bslma::UsesBslmaAllocator); // CLASS METHODS static bool isPlausibleZoneinfoRootPath(const char *path); // Return 'true' if there currently exists a directory at the specified // file-system 'path' that appears to contain Zoneinfo time-zone // information files. This method verifies (at a minimum) that 'path' // is a valid directory, and contains files for a subset of common // time-zone identifiers, but does not *guarantee* 'path' is currently // (or will remain) a correctly configured Zoneinfo database containing // a complete set of time-zone data. // CREATORS DataFileLoader(); explicit DataFileLoader(const allocator_type& allocator); // Create an unconfigured data-file loader. Optionally specify an // 'allocator' (e.g., the address of a 'bslma::Allocator' object) to // supply memory; otherwise, the default allocator is used. virtual ~DataFileLoader(); // Destroy this data-file loader. // MANIPULATORS void configureRootPath(const char *path); // Unconditionally set, to the specified 'path', the root of the // file-system directory hierarchy from which this loader will read // Zoneinfo binary time-zone information files. int configureRootPathIfPlausible(const char *path); // Set, to the specified 'path', the root of the file-system directory // hierarchy from which this loader will read Zoneinfo binary time-zone // information files. Return 0 on success, and a non-zero value // (without no effect) if 'path' is not a directory that appears to // contain valid Zoneinfo data, as determined by calling // 'isPlausibleZoneinfoRootPath' on 'path'. virtual int loadTimeZone(Zoneinfo *result, const char *timeZoneId); // Load into the specified 'result' the time-zone information for the // time zone identified by the specified 'timeZoneId'. Return 0 on // success, and a non-zero value otherwise. A return status of // 'ErrorCode::k_UNSUPPORTED_ID' indicates that 'timeZoneId' is not // recognized. If an error occurs during this operation, 'result' will // be left in a valid, but unspecified state. // ACCESSORS int loadTimeZoneFilePath(bsl::string *result, const char *timeZoneId) const; int loadTimeZoneFilePath(std::string *result, const char *timeZoneId) const; #ifdef BSLS_LIBRARYFEATURES_HAS_CPP17_PMR int loadTimeZoneFilePath(std::pmr::string *result, const char *timeZoneId) const; #endif // Load into the specified 'result' the file-system path to the // Zoneinfo binary data file corresponding to the specified // 'timeZoneId' relative to the configured 'rootPath'. Return 0 on // success, and a non-zero value otherwise. On error, 'result' is left // in a valid, but unspecified state. The behavior is undefined unless // either 'configureRootPath' or 'configureRootPathIfValid' has been // called successfully. Note that this operation does not verify // 'result' refers to a valid file on the file system, or whether the // file (if it exists) contains valid Zoneinfo data. const bsl::string& rootPath() const; // Return the root of the directory hierarchy from which this loader // will attempt to load Zoneinfo binary data files. The behavior is // undefined unless either 'configureRootPath' has been called or // 'configureRootPathIfValid' has been called successfully. bool isRootPathPlausible() const; // Return 'true' if the directory returned by the 'rootPath' method // exists and appears to contain valid Zoneinfo time-zone information // files, as determined by calling 'isPlausibleZoneinfoRootPath' on the // value returned by the 'rootPath' method. // Aspects allocator_type get_allocator() const; // Return the allocator used by this object to supply memory. Note // that if no allocator was supplied at construction the default // allocator in effect at construction is used. }; // ============================================================================ // INLINE DEFINITIONS // ============================================================================ // -------------------- // class DataFileLoader // -------------------- // ACCESSORS inline bool DataFileLoader::isRootPathPlausible() const { return isPlausibleZoneinfoRootPath(rootPath().c_str()); } // Aspects inline DataFileLoader::allocator_type DataFileLoader::get_allocator() const { return d_rootPath.get_allocator(); } } // close package namespace } // close enterprise namespace #endif // ---------------------------------------------------------------------------- // Copyright 2020 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 ----------------------------------