Skip to content

Commit

Permalink
'ZoneinfoBinaryReader' now omits ZIC sentinel transition. DRQS 175551…
Browse files Browse the repository at this point in the history
…183 (#4800)

* 'ZoneinfoBinaryReader' omits ZIC sentinel transition

* Code review comments have been addressed

* Code review comments have been addressed 2

* Code review comments have been addressed 3
  • Loading branch information
Denis Sokolov authored and GitHub Enterprise committed Jul 18, 2024
1 parent f868a74 commit 4a2d0e4
Show file tree
Hide file tree
Showing 7 changed files with 1,796 additions and 908 deletions.
104 changes: 73 additions & 31 deletions groups/bal/baltzo/baltzo_datafileloader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,24 @@ struct IsString {
;
};

enum ZoneinfoReadMode {
// Enumerate the set of modes that can be used for reading binary data.

k_READ_RAW, // Return an unadjusted raw binary data from the data
// file (this may not be a "well-formed" Zoneinfo (see
// 'baltzo_zoneinfoutil')

k_READ_NORMALIZED // Adjust the raw binary data to produce a Zoneinfo
// satisfying the first two requirements for a
// "well-formed" object by inserting a sentinel
// transition at 01-01-001 (see
// 'baltzo::ZoneinfoUtil::isWellFormed' documentation).
// At the moment, this means adding a sentinel
// transition at 01-01-0001 (the raw file format may
// place such a sentinel transition before the first
// representable 'Datetime' object)
};

// HELPER FUNCTIONS
template <class STRING>
void concatenatePath(STRING *result,
Expand Down Expand Up @@ -126,6 +144,56 @@ int loadTimeZoneFilePath_Impl(STRING *result,
return 0;
}

int loadTimeZoneImpl(baltzo::Zoneinfo *result,
const baltzo::DataFileLoader& loader,
const char *timeZoneId,
ZoneinfoReadMode mode)
// Load into the specified 'result' the time-zone information for the time
// zone identified by the specified 'timeZoneId' in accordance with the
// specified 'mode'. 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.
{
BSLS_ASSERT(result);
BSLS_ASSERT(timeZoneId);

bsl::string path;
const int rc = loader.loadTimeZoneFilePath(&path, timeZoneId);
if (0 != rc) {
BSLS_LOG_ERROR("Poorly formed time-zone identifier '%s'", timeZoneId);
return u::UNSUPPORTED_ID; // RETURN
}

// Create a file stream for the olson data file.

bsl::ifstream infile(path.c_str(),
bsl::ifstream::binary | bsl::ifstream::in);

if (!infile.is_open()) {
// Failed to open the data file for 'timeZoneId'. If the data-file
// loader is correctly configured, 'timeZoneId' is not a supported
// time-zone identifier, so return 'k_UNSUPPORTED_ID'. Otherwise, if
// the data-file loader is not correctly configured, return
// 'UNSPECIFIED_ERROR' (different from 'k_UNSUPPORTED_ID').

BSLS_LOG_ERROR("Failed to open time-zone information file '%s'",
path.c_str());

return loader.isRootPathPlausible()
? u::UNSUPPORTED_ID
: u::UNSPECIFIED_ERROR; // RETURN
}

result->setIdentifier(timeZoneId);

if (k_READ_RAW == mode) {
return baltzo::ZoneinfoBinaryReader::readRaw(result, infile); // RETURN
}

return baltzo::ZoneinfoBinaryReader::read(result, infile);
}

} // close namespace u
} // close unnamed namespace

Expand Down Expand Up @@ -196,38 +264,12 @@ int DataFileLoader::configureRootPathIfPlausible(const char *path)

int DataFileLoader::loadTimeZone(Zoneinfo *result, const char *timeZoneId)
{
BSLS_ASSERT(result);
BSLS_ASSERT(timeZoneId);

bsl::string path;
const int rc = loadTimeZoneFilePath(&path, timeZoneId);
if (0 != rc) {
BSLS_LOG_ERROR("Poorly formed time-zone identifier '%s'", timeZoneId);
return u::UNSUPPORTED_ID; // RETURN
}

// Create a file stream for the olson data file.

bsl::ifstream infile(path.c_str(),
bsl::ifstream::binary | bsl::ifstream::in);

if (!infile.is_open()) {
// Failed to open the data file for 'timeZoneId'. If the data-file
// loader is correctly configured, 'timeZoneId' is not a supported
// time-zone identifier, so return 'k_UNSUPPORTED_ID'. Otherwise, if
// the data-file loader is not correctly configured, return
// 'UNSPECIFIED_ERROR' (different from 'k_UNSUPPORTED_ID').

BSLS_LOG_ERROR("Failed to open time-zone information file '%s'",
path.c_str());

return isRootPathPlausible()
? u::UNSUPPORTED_ID
: u::UNSPECIFIED_ERROR; // RETURN
}
return loadTimeZoneImpl(result, *this, timeZoneId, u::k_READ_NORMALIZED);
}

result->setIdentifier(timeZoneId);
return ZoneinfoBinaryReader::read(result, infile);
int DataFileLoader::loadTimeZoneRaw(Zoneinfo *result, const char *timeZoneId)
{
return loadTimeZoneImpl(result, *this, timeZoneId, u::k_READ_RAW);
}

// ACCESSORS
Expand Down
20 changes: 19 additions & 1 deletion groups/bal/baltzo/baltzo_datafileloader.h
Original file line number Diff line number Diff line change
Expand Up @@ -275,12 +275,30 @@ class DataFileLoader : public Loader {

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
// time zone identified by the specified 'timeZoneId'. The 'result'
// will have a sentinel transition at 01-01-001, meeting the first two
// requirements for a "well-formed" object (see
// 'baltzo::ZoneinfoUtil::isWellFormed' documentation). 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.

int loadTimeZoneRaw(Zoneinfo *result, const char *timeZoneId);
// Load into the specified 'result' the time-zone information for the
// time zone identified by the specified 'timeZoneId' exactly in
// accordance with the original data. The 'result' may not be a
// "well-formed" object (see 'baltzo::ZoneinfoUtil::isWellFormed'
// documentation for details). 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. Note that time zone data files created by
// certain versions of the 'zic' time zone compiler will have a
// sentinel transition prior to 01-01-0001 (the first representable
// 'Datetime' value) and the 'result' will therefore be non-well formed
// (use 'loadTimeZone' instead).

// ACCESSORS
int loadTimeZoneFilePath(bsl::string *result,
const char *timeZoneId) const;
Expand Down
148 changes: 107 additions & 41 deletions groups/bal/baltzo/baltzo_datafileloader.t.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -748,80 +748,146 @@ int main(int argc, char *argv[])
} break;
case 6: {
// --------------------------------------------------------------------
// TESTING 'loadTimeZone'
// TESTING 'loadTimeZone' and 'loadTimeZoneRaw'
//
// Concerns:
//: 1 'loadTimeZone' correctly loads time-zone information when a valid
//: time zone identifier is specified.
//: 1 'loadTimeZone' and 'loadTimeZoneRaw' correctly load time-zone
//: information when a valid time zone identifier is specified.
//:
//: 2 'loadTimeZone' returns 'k_UNSUPPORTED_ID' if the specified time
//: zone identifier is invalid.
//: 2 'loadTimeZone' and 'loadTimeZoneRaw' return 'k_UNSUPPORTED_ID' if
//: the specified time zone identifier is invalid.
//:
//: 3 'loadTimeZone' returns a non-zero value different from
//: 'k_UNSUPPORTED_ID' on error reading the specified time-zone file.
//: 3 'loadTimeZone' and 'loadTimeZoneRaw' return a non-zero value
//: different from 'k_UNSUPPORTED_ID' on error reading the specified
//: time-zone file.
//
// Plan:
//: 1 Test that 'loadTimeZone' returns time-zone information when give
//: a valid time zone identifier.
//: 1 Test that 'loadTimeZone' and 'loadTimeZoneRaw' return time-zone
//: information when give a valid time zone identifier.
//:
//: 2 Test that 'loadTimeZone' returns 'k_UNSUPPORTED_ID' when
//: 'rootPath' is a plausible directory, but the time zone identifier
//: is invalid.
//: 2 Test that 'loadTimeZone' and 'loadTimeZoneRaw' return
//: 'k_UNSUPPORTED_ID' when 'rootPath' is a plausible directory, but
//: the time zone identifier is invalid.
//:
//: 3 Test that 'loadTimeZone' returns a non-zero return code other
//: than 'k_UNSUPPORTED_ID' if 'rootPath' is not plausible.
//: 3 Test that 'loadTimeZone' and 'loadTimeZoneRaw' return a non-zero
//: return code other than 'k_UNSUPPORTED_ID' if 'rootPath' is not
//: plausible.
//
// Testing:
// int loadTimeZone(Zoneinfo *result, const char *timeZoneId);
// int loadTimeZoneRaw(Zoneinfo *result, const char *timeZoneId);
// --------------------------------------------------------------------

if (verbose) cout << endl
<< "TESTING 'loadTimeZone'" << endl
<< "======================" << endl;
if (verbose) cout
<< endl
<< "TESTING 'loadTimeZone' and 'loadTimeZoneRaw'" << endl
<< "============================================" << endl;

if (verbose) cout << "\nSetup plausible directory" << endl;

ASSERT(true == Obj::isPlausibleZoneinfoRootPath(TEST_DIRECTORY));

if (verbose) cout << "\nTest non-plausible directory" << endl;
{
Obj mX;
baltzo::Zoneinfo timeZone;
typedef int (Obj::*LoadMethodPtr)(baltzo::Zoneinfo *, const char *);

for (char mode = 'a'; mode <= 'b'; ++mode) {
const bool RAW_MODE = 'a' == mode ? true : false;
const char *MODE_STR = RAW_MODE ? "RAW" : "BDE";
LoadMethodPtr loadMethod = RAW_MODE
? static_cast<LoadMethodPtr>(&Obj::loadTimeZoneRaw)
: static_cast<LoadMethodPtr>(&Obj::loadTimeZone);
if (verbose) cout << "\nTest non-plausible directory ("
<< MODE_STR
<< ")."
<< endl;
{
Obj mX;
baltzo::Zoneinfo timeZone;

mX.configureRootPath("NotPlausibleDirectory");
mX.configureRootPath("NotPlausibleDirectory");

int rc = mX.loadTimeZone(&timeZone, "A");
ASSERT(0 != rc);
ASSERT(baltzo::ErrorCode::k_UNSUPPORTED_ID != rc);
}
int rc = (mX.*loadMethod)(&timeZone, "A");
ASSERT(0 != rc);
ASSERT(baltzo::ErrorCode::k_UNSUPPORTED_ID != rc);
}

if (verbose) cout << "\nTest with plausible directory" << endl;
{
Obj mX;
baltzo::Zoneinfo timeZone;
if (verbose) cout << "\nTest with plausible directory ("
<< MODE_STR
<< ")."
<< endl;
{
Obj mX;
baltzo::Zoneinfo timeZone;

ASSERT(0 == mX.configureRootPathIfPlausible(TEST_DIRECTORY));
ASSERT(0 == mX.configureRootPathIfPlausible(TEST_DIRECTORY));

if (verbose) cout << "\n\tTest invalid identifier" << endl;
if (verbose) cout << "\n\tTest invalid identifier ("
<< MODE_STR
<< ")."
<< endl;

ASSERT(baltzo::ErrorCode::k_UNSUPPORTED_ID ==
ASSERT(baltzo::ErrorCode::k_UNSUPPORTED_ID ==
mX.loadTimeZone(&timeZone, "/"));

if (verbose) cout << "\n\tTest non-existent identifier" << endl;
if (verbose) cout << "\n\tTest non-existent identifier ("
<< MODE_STR
<< ")."
<< endl;

ASSERT(baltzo::ErrorCode::k_UNSUPPORTED_ID ==
ASSERT(baltzo::ErrorCode::k_UNSUPPORTED_ID ==
mX.loadTimeZone(&timeZone, "Non/Existent/Identifier"));

ASSERT(0 == mX.loadTimeZone(&timeZone, AMERICA_NEW_YORK_ID));
ASSERT(0 == (mX.*loadMethod)(&timeZone, AMERICA_NEW_YORK_ID));

ASSERT(AMERICA_NEW_YORK_ID == timeZone.identifier());

ASSERT(AMERICA_NEW_YORK_ID == timeZone.identifier());
// 'ZoneinfoBinaryReader::read' adds sentinel transition dated
// to the the first representable 'bdlt::Datetime' value --
// 'bdlt::Datetime(1, 1, 1)'.
// 'ZoneinfoBinaryReader::readRaw()' does not add it. So we
// expect a difference in results.

const bsl::size_t EXPECTED_NUM_TRANSITIONS = RAW_MODE
? 236 // RAW
: 237; // BDE
ASSERTV(timeZone.numTransitions(),
EXPECTED_NUM_TRANSITIONS == timeZone.numTransitions());
}
}

#ifndef BDE_OMIT_INTERNAL_DEPRECATED
ASSERT(baltzo::ErrorCode::k_UNSUPPORTED_ID ==
baltzo::ErrorCode::k_UNSUPPORTED_ID);
#endif // BDE_OMIT_INTERNAL_DEPRECATED
if (verbose) cout << "\nNegative Testing." << endl;
{
bsls::AssertTestHandlerGuard hG;

baltzo::Zoneinfo *NULL_ZI_PTR = 0;
const char *NULL_TZ_PTR = 0;

// Testing 'loadTimeZone'
{
Obj mX;
baltzo::Zoneinfo timeZone;

ASSERT(0 == mX.configureRootPathIfPlausible(TEST_DIRECTORY));

ASSERT_PASS(mX.loadTimeZone(&timeZone, AMERICA_NEW_YORK_ID));
ASSERT_FAIL(mX.loadTimeZone(NULL_ZI_PTR, AMERICA_NEW_YORK_ID));
ASSERT_FAIL(mX.loadTimeZone(&timeZone, NULL_TZ_PTR ));
}

// Testing 'loadTimeZoneRaw'
{
Obj mX;
baltzo::Zoneinfo timeZone;

ASSERT(0 == mX.configureRootPathIfPlausible(TEST_DIRECTORY));

ASSERT_PASS(mX.loadTimeZoneRaw(&timeZone,
AMERICA_NEW_YORK_ID));
ASSERT_FAIL(mX.loadTimeZoneRaw(NULL_ZI_PTR,
AMERICA_NEW_YORK_ID));
ASSERT_FAIL(mX.loadTimeZoneRaw(&timeZone,
NULL_TZ_PTR ));
}
}
} break;
case 5: {
// --------------------------------------------------------------------
Expand Down
Loading

0 comments on commit 4a2d0e4

Please sign in to comment.