diff --git a/doc/README.cast.format.md b/doc/sql.extensions/README.cast.format.md similarity index 100% rename from doc/README.cast.format.md rename to doc/sql.extensions/README.cast.format.md diff --git a/src/common/CvtFormat.cpp b/src/common/CvtFormat.cpp index 301c1e25fb2..ce483c15493 100644 --- a/src/common/CvtFormat.cpp +++ b/src/common/CvtFormat.cpp @@ -1194,6 +1194,31 @@ namespace return std::string_view(str + startPoint, wordLen); } + constexpr unsigned getFractionsFromString(const char* str, FB_SIZE_T length, FB_SIZE_T& offset, FB_SIZE_T parseLength) + { + constexpr unsigned pow10[] = {1, 10, 100, 1'000, 10'000}; + static_assert(std::size(pow10) > -ISC_TIME_SECONDS_PRECISION_SCALE); + + if (parseLength > -ISC_TIME_SECONDS_PRECISION_SCALE) + parseLength = -ISC_TIME_SECONDS_PRECISION_SCALE; + int currentPrecisionScale = -ISC_TIME_SECONDS_PRECISION_SCALE; + unsigned fractions = 0; + + const FB_SIZE_T parseLengthWithOffset = offset + parseLength; + for (; offset < parseLengthWithOffset && offset < length; offset++) + { + const char symbol = str[offset]; + + if (!isDigit(symbol)) + break; + + fractions = fractions * 10 + (symbol - '0'); + --currentPrecisionScale; + } + + return fractions * pow10[currentPrecisionScale]; + } + template constexpr TIterator getPreviousOrCurrentIterator(TIterator it, TIterator begin) { @@ -1499,10 +1524,9 @@ namespace case Format::FF3: case Format::FF4: { - const int number = patternStr.back() - '0'; + const int precision = patternStr.back() - '0'; - const int fractions = getIntFromString(str, strLength, strOffset, number); - outFractions = fractions * pow(10, -ISC_TIME_SECONDS_PRECISION_SCALE - number); + outFractions = getFractionsFromString(str, strLength, strOffset, precision); break; } diff --git a/src/common/tests/CvtTest.cpp b/src/common/tests/CvtTest.cpp index a86f48d7a7b..6a685b891ed 100644 --- a/src/common/tests/CvtTest.cpp +++ b/src/common/tests/CvtTest.cpp @@ -550,25 +550,31 @@ BOOST_AUTO_TEST_CASE(CVTStringToFormatDateTime_TIME) testCVTStringToFormatDateTimeExpectTime("5", "FF1", createTimeStampTZ(0, 0, 0, 0, 0, 0, 0, 5000), cb); testCVTStringToFormatDateTimeExpectTime("9", "FF1", createTimeStampTZ(0, 0, 0, 0, 0, 0, 0, 9000), cb); - testCVTStringToFormatDateTimeExpectTime("1", "FF2", createTimeStampTZ(0, 0, 0, 0, 0, 0, 0, 100), cb); + testCVTStringToFormatDateTimeExpectTime("01", "FF2", createTimeStampTZ(0, 0, 0, 0, 0, 0, 0, 100), cb); + testCVTStringToFormatDateTimeExpectTime("1", "FF2", createTimeStampTZ(0, 0, 0, 0, 0, 0, 0, 1000), cb); testCVTStringToFormatDateTimeExpectTime("10", "FF2", createTimeStampTZ(0, 0, 0, 0, 0, 0, 0, 1000), cb); testCVTStringToFormatDateTimeExpectTime("50", "FF2", createTimeStampTZ(0, 0, 0, 0, 0, 0, 0, 5000), cb); testCVTStringToFormatDateTimeExpectTime("99", "FF2", createTimeStampTZ(0, 0, 0, 0, 0, 0, 0, 9900), cb); - testCVTStringToFormatDateTimeExpectTime("1", "FF3", createTimeStampTZ(0, 0, 0, 0, 0, 0, 0, 10), cb); - testCVTStringToFormatDateTimeExpectTime("10", "FF3", createTimeStampTZ(0, 0, 0, 0, 0, 0, 0, 100), cb); + testCVTStringToFormatDateTimeExpectTime("01", "FF3", createTimeStampTZ(0, 0, 0, 0, 0, 0, 0, 100), cb); + testCVTStringToFormatDateTimeExpectTime("001", "FF3", createTimeStampTZ(0, 0, 0, 0, 0, 0, 0, 10), cb); + testCVTStringToFormatDateTimeExpectTime("1", "FF3", createTimeStampTZ(0, 0, 0, 0, 0, 0, 0, 1000), cb); + testCVTStringToFormatDateTimeExpectTime("10", "FF3", createTimeStampTZ(0, 0, 0, 0, 0, 0, 0, 1000), cb); testCVTStringToFormatDateTimeExpectTime("100", "FF3", createTimeStampTZ(0, 0, 0, 0, 0, 0, 0, 1000), cb); testCVTStringToFormatDateTimeExpectTime("500", "FF3", createTimeStampTZ(0, 0, 0, 0, 0, 0, 0, 5000), cb); testCVTStringToFormatDateTimeExpectTime("999", "FF3", createTimeStampTZ(0, 0, 0, 0, 0, 0, 0, 9990), cb); - testCVTStringToFormatDateTimeExpectTime("1", "FF4", createTimeStampTZ(0, 0, 0, 0, 0, 0, 0, 1), cb); - testCVTStringToFormatDateTimeExpectTime("10", "FF4", createTimeStampTZ(0, 0, 0, 0, 0, 0, 0, 10), cb); - testCVTStringToFormatDateTimeExpectTime("100", "FF4", createTimeStampTZ(0, 0, 0, 0, 0, 0, 0, 100), cb); + testCVTStringToFormatDateTimeExpectTime("01", "FF4", createTimeStampTZ(0, 0, 0, 0, 0, 0, 0, 100), cb); + testCVTStringToFormatDateTimeExpectTime("001", "FF4", createTimeStampTZ(0, 0, 0, 0, 0, 0, 0, 10), cb); + testCVTStringToFormatDateTimeExpectTime("0001", "FF4", createTimeStampTZ(0, 0, 0, 0, 0, 0, 0, 1), cb); + testCVTStringToFormatDateTimeExpectTime("1", "FF4", createTimeStampTZ(0, 0, 0, 0, 0, 0, 0, 1000), cb); + testCVTStringToFormatDateTimeExpectTime("10", "FF4", createTimeStampTZ(0, 0, 0, 0, 0, 0, 0, 1000), cb); + testCVTStringToFormatDateTimeExpectTime("100", "FF4", createTimeStampTZ(0, 0, 0, 0, 0, 0, 0, 1000), cb); testCVTStringToFormatDateTimeExpectTime("1000", "FF4", createTimeStampTZ(0, 0, 0, 0, 0, 0, 0, 1000), cb); testCVTStringToFormatDateTimeExpectTime("5000", "FF4", createTimeStampTZ(0, 0, 0, 0, 0, 0, 0, 5000), cb); testCVTStringToFormatDateTimeExpectTime("9999", "FF4", createTimeStampTZ(0, 0, 0, 0, 0, 0, 0, 9999), cb); - testCVTStringToFormatDateTimeExpectTime("1 P.M. - 25 - 45 - 200", "HH P.M. MI.SS.FF4", createTimeStampTZ(0, 0, 0, 13, 25, 45, 0, 200), cb); + testCVTStringToFormatDateTimeExpectTime("1 P.M. - 25 - 45 - 2", "HH P.M. MI.SS.FF4", createTimeStampTZ(0, 0, 0, 13, 25, 45, 0, 2000), cb); testCVTStringToFormatDateTimeExpectTime("15:0:15:2", "HH24.MI.SS.FF1", createTimeStampTZ(0, 0, 0, 15, 0, 15, 0, 2000), cb); } @@ -583,7 +589,7 @@ BOOST_AUTO_TEST_CASE(CVTStringToFormatDateTime_TZ) BOOST_AUTO_TEST_CASE(CVTStringToFormatDateTime_SOLID_PATTERNS) { - testCVTStringToFormatDateTimeExpectTime("1 P.M. - 25 - 45 - 200", "HHA.M.MISSFF4", createTimeStampTZ(0, 0, 0, 13, 25, 45, 0, 200), cb); + testCVTStringToFormatDateTimeExpectTime("1 P.M. - 25 - 45 - 2", "HHA.M.MISSFF4", createTimeStampTZ(0, 0, 0, 13, 25, 45, 0, 2000), cb); testCVTStringToFormatDateTimeExpectDate("1981-8/13", "YEARMMDD", createTimeStampTZ(1981, 8, 13, 0, 0, 0, 0), cb); }