| Message ID | 20260601120809.7245-1-andrej.kozemcak@siemens.com |
|---|---|
| State | New |
| Headers | show |
| Series | [meta-oe,v2] poco: fix timezone and data time parser issue | expand |
For the master branch, IMO we should bump to v1.15.3[1] instead of backporting the patch. [1] https://github.com/pocoproject/poco/releases/tag/poco-1.15.3-release On Tue, Jun 2, 2026 at 12:08 AM Andrej Kozemcak via lists.openembedded.org <andrej.kozemcak=siemens.com@lists.openembedded.org> wrote: > > Patch contains tree patches which was merge to poco at once > and fix "time" issues > > 1 - fix(Foundation): Timezone: invalidate utcOffset cache when /etc/localtime changes > > Poco commit 1850dc16aabf5980a490bb1b66086d6695abb823 introduced a > TZInfo cache for the UTC offset to avoid repeated tzset() syscalls. > The cache is invalidated only when the TZ environment variable changes. > However, the TZ variable is process-local: if a different process (e.g. > a timezone configuration daemon or an init script) changes the system > timezone by updating /etc/localtime, the running process is not notified > and its TZ environment variable remains unchanged. > > On systems that switch timezone by updating /etc/localtime (a symlink) > without touching the TZ env var, the cache is therefore never invalidated > and Timezone::utcOffset() returns the stale value computed at startup. > > Fix by extending cacheTZ()/tzChanged() to also track the inode and > mtime of /etc/localtime via stat(2). When either changes the cache is > considered stale and reloaded, preserving the performance benefit for > the common case where neither TZ nor /etc/localtime changes between > calls. > > 2 - fix(Foundation): DateTimeParser: %S consume optional fractional seconds > > Parsing ISO 8601 date strings that contain both fractional seconds and a > timezone offset (e.g. "2013-10-07T08:23:19.120-04:00") with the format > "%Y-%m-%dT%H:%M:%S%z" raises a SyntaxException: > > - %S consumes the integer seconds but stops at '.', leaving > ".120-04:00" unconsumed. > - %z (parseTZD) is called next but sees '.' and returns without > consuming anything. > - The trailing-garbage check then raises SyntaxException. > > Extend the %S case to consume and discard an optional fractional-second > suffix ('.' or ',' followed by one or more digits) immediately after > parsing the integer seconds. This mirrors the existing %s behaviour and > allows %z to see the timezone designator directly, keeping the > trailing-garbage check fully effective for truly invalid input. > > 3 - test(DateTimeParserTest): add ISO8601 fractional seconds parser test > > Add testISO8601FracSeconds to verify that DateTimeParser correctly > handles fractional-second suffixes (dot and comma separated) in > ISO8601_FORMAT strings, and rejects malformed input such as a > bare decimal point with no digits. > > Signed-off-by: Andrej Kozemcak <andrej.kozemcak@siemens.com> > --- > ...er-trailing-timezone-designators-Tim.patch | 240 ++++++++++++++++++ > meta-oe/recipes-support/poco/poco_1.15.2.bb | 1 + > 2 files changed, 241 insertions(+) > create mode 100644 meta-oe/recipes-support/poco/poco/0004-Fix-DateTimeParser-trailing-timezone-designators-Tim.patch > > diff --git a/meta-oe/recipes-support/poco/poco/0004-Fix-DateTimeParser-trailing-timezone-designators-Tim.patch b/meta-oe/recipes-support/poco/poco/0004-Fix-DateTimeParser-trailing-timezone-designators-Tim.patch > new file mode 100644 > index 0000000000..d2a8666b23 > --- /dev/null > +++ b/meta-oe/recipes-support/poco/poco/0004-Fix-DateTimeParser-trailing-timezone-designators-Tim.patch > @@ -0,0 +1,240 @@ > +From a767d36fed678e874536e33731be6dbcc5fdc5d0 Mon Sep 17 00:00:00 2001 > +From: kozemcak <akozemcak@gmail.com> > +Date: Tue, 12 May 2026 16:13:12 +0200 > +Subject: [PATCH] Fix: DateTimeParser trailing timezone designators & Timezone > + UTC offset cache invalidation (#5318) > + > +* fix(Foundation): Timezone: invalidate utcOffset cache when /etc/localtime changes > + > +Poco commit 1850dc16aabf5980a490bb1b66086d6695abb823 introduced a > +TZInfo cache for the UTC offset to avoid repeated tzset() syscalls. > +The cache is invalidated only when the TZ environment variable changes. > +However, the TZ variable is process-local: if a different process (e.g. > +a timezone configuration daemon or an init script) changes the system > +timezone by updating /etc/localtime, the running process is not notified > +and its TZ environment variable remains unchanged. > + > +On systems that switch timezone by updating /etc/localtime (a symlink) > +without touching the TZ env var, the cache is therefore never invalidated > +and Timezone::utcOffset() returns the stale value computed at startup. > + > +Fix by extending cacheTZ()/tzChanged() to also track the inode and > +mtime of /etc/localtime via stat(2). When either changes the cache is > +considered stale and reloaded, preserving the performance benefit for > +the common case where neither TZ nor /etc/localtime changes between > +calls. > + > +Signed-off-by: Andrej Kozemcak <andrej.kozemcak@siemens.com> > + > +* fix(Foundation): DateTimeParser: %S consume optional fractional seconds > + > +Parsing ISO 8601 date strings that contain both fractional seconds and a > +timezone offset (e.g. "2013-10-07T08:23:19.120-04:00") with the format > +"%Y-%m-%dT%H:%M:%S%z" raises a SyntaxException: > + > + - %S consumes the integer seconds but stops at '.', leaving > + ".120-04:00" unconsumed. > + - %z (parseTZD) is called next but sees '.' and returns without > + consuming anything. > + - The trailing-garbage check then raises SyntaxException. > + > +Extend the %S case to consume and discard an optional fractional-second > +suffix ('.' or ',' followed by one or more digits) immediately after > +parsing the integer seconds. This mirrors the existing %s behaviour and > +allows %z to see the timezone designator directly, keeping the > +trailing-garbage check fully effective for truly invalid input. > + > +Fix suggested by matejk. > + > +Signed-off-by: Andrej Kozemcak <andrej.kozemcak@siemens.com> > + > +* test(DateTimeParserTest): add ISO8601 fractional seconds parser test > + > +Add testISO8601FracSeconds to verify that DateTimeParser correctly > +handles fractional-second suffixes (dot and comma separated) in > +ISO8601_FORMAT strings, and rejects malformed input such as a > +bare decimal point with no digits. > + > +--------- > + > +Upstream-Status: Backport [1.15.3, https://github.com/pocoproject/poco/commit/b8d2b50774b25a470c5a30fc91bae7cd47c251fa] > + > +Signed-off-by: Andrej Kozemcak <andrej.kozemcak@siemens.com> > +Co-authored-by: Andrej Kozemcak <andrej.kozemcak@siemens.com> > +--- > + Foundation/include/Poco/DateTimeParser.h | 6 ++++ > + Foundation/src/DateTimeParser.cpp | 12 +++++++ > + Foundation/src/Timezone_UNIX.cpp | 36 ++++++++++++++++++- > + .../testsuite/src/DateTimeParserTest.cpp | 32 +++++++++++++++++ > + Foundation/testsuite/src/DateTimeParserTest.h | 1 + > + 5 files changed, 86 insertions(+), 1 deletion(-) > + > +diff --git a/Foundation/include/Poco/DateTimeParser.h b/Foundation/include/Poco/DateTimeParser.h > +index 2d1882d83..e82a934c9 100644 > +--- a/Foundation/include/Poco/DateTimeParser.h > ++++ b/Foundation/include/Poco/DateTimeParser.h > +@@ -63,6 +63,12 @@ public: > + /// Throws a SyntaxException if the string cannot be successfully parsed. > + /// Please see DateTimeFormatter::format() for a description of the format string. > + /// Class DateTimeFormat defines format strings for various standard date/time formats. > ++ /// > ++ /// Note: The %S specifier parses whole seconds and then silently discards any > ++ /// fractional-second suffix of the form '.DDD' or ',DDD' (including a bare decimal > ++ /// point that is immediately followed by a non-digit is treated as a parse error). > ++ /// Callers that need the fractional digits captured must use %s (millis+micros), > ++ /// %i (milliseconds only), %c (centiseconds), or %F (six-digit fractional seconds). > + > + static DateTime parse(const std::string& fmt, const std::string& str, int& timeZoneDifferential); > + /// Parses a date and time in the given format from the given string. > +diff --git a/Foundation/src/DateTimeParser.cpp b/Foundation/src/DateTimeParser.cpp > +index 08b3494ac..c2fe3dfaa 100644 > +--- a/Foundation/src/DateTimeParser.cpp > ++++ b/Foundation/src/DateTimeParser.cpp > +@@ -243,6 +243,18 @@ void DateTimeParser::parse(const std::string& fmt, const std::string& dtStr, Dat > + case 'S': > + it = skipNonDigits(it, end); > + second = parseNumberN(dtStr, it, end, 2); > ++ // Consume optional fractional seconds ('.NNN' or ',NNN') so that a > ++ // subsequent %z specifier can reach the timezone designator. > ++ // A decimal point/comma not followed by a digit is an error. > ++ if (it != end && (*it == '.' || *it == ',')) > ++ { > ++ ++it; > ++ if (it == end || !Ascii::isDigit(*it)) > ++ { > ++ throw SyntaxException("Invalid DateTimeString: " + dtStr + ", missing fractional digits"); > ++ } > ++ it = skipDigits(it, end); > ++ } > + break; > + case 's': > + it = skipNonDigits(it, end); > +diff --git a/Foundation/src/Timezone_UNIX.cpp b/Foundation/src/Timezone_UNIX.cpp > +index d1489e72f..3e0d29838 100644 > +--- a/Foundation/src/Timezone_UNIX.cpp > ++++ b/Foundation/src/Timezone_UNIX.cpp > +@@ -18,6 +18,7 @@ > + #include <ctime> > + #include <cstdlib> > + #include <string> > ++#include <sys/stat.h> > + > + > + namespace Poco { > +@@ -66,13 +67,44 @@ private: > + { > + const char* tz = std::getenv("TZ"); > + _cachedTZ = tz ? tz : ""; > ++ // /etc/localtime is the system-wide zone source (updated by timedatectl etc.). > ++ // Stat'ing it lets us detect zone changes that don't touch the TZ env var; we > ++ // don't read its contents — tzset()/libc still resolves the actual zone data. > ++ cacheLocaltimeStat(); > + } > + > + bool tzChanged() const > + { > + const char* tz = std::getenv("TZ"); > + std::string currentTZ = tz ? tz : ""; > +- return currentTZ != _cachedTZ; > ++ if (currentTZ != _cachedTZ) return true; > ++ return localtimeStatChanged(); > ++ } > ++ > ++ void cacheLocaltimeStat() > ++ { > ++ struct stat st{}; > ++ if (::stat("/etc/localtime", &st) == 0) > ++ { > ++ _localtimeIno = st.st_ino; > ++ _localtimeMtime = st.st_mtime; > ++ } > ++ else > ++ { > ++ _localtimeIno = 0; > ++ _localtimeMtime = 0; > ++ } > ++ } > ++ > ++ bool localtimeStatChanged() const > ++ { > ++ struct stat st{}; > ++ if (::stat("/etc/localtime", &st) == 0) > ++ { > ++ return st.st_ino != _localtimeIno || st.st_mtime != _localtimeMtime; > ++ } > ++ // /etc/localtime not accessible: treat as changed only if we had it before > ++ return _localtimeIno != 0; > + } > + > + static int computeTimeZone() > +@@ -112,6 +144,8 @@ private: > + std::mutex _mutex; > + int _tzOffset; > + std::string _cachedTZ; > ++ ino_t _localtimeIno = 0; > ++ time_t _localtimeMtime = 0; > + }; > + > + > +diff --git a/Foundation/testsuite/src/DateTimeParserTest.cpp b/Foundation/testsuite/src/DateTimeParserTest.cpp > +index 246a0d531..0742e1e02 100644 > +--- a/Foundation/testsuite/src/DateTimeParserTest.cpp > ++++ b/Foundation/testsuite/src/DateTimeParserTest.cpp > +@@ -638,6 +638,37 @@ void DateTimeParserTest::testCustom() > + } > + > + > ++void DateTimeParserTest::testISO8601FracSeconds() > ++{ > ++ // ISO8601_FORMAT uses %S which silently discards a well-formed fractional-second > ++ // suffix so that the trailing %z can still reach the timezone designator. > ++ int tzd; > ++ > ++ // Dot-separated fractional seconds with negative timezone offset > ++ DateTime dt = DateTimeParser::parse(DateTimeFormat::ISO8601_FORMAT, "2013-10-07T08:23:19.120-04:00", tzd); > ++ assertTrue (dt.year() == 2013); > ++ assertTrue (dt.month() == 10); > ++ assertTrue (dt.day() == 7); > ++ assertTrue (dt.hour() == 8); > ++ assertTrue (dt.minute() == 23); > ++ assertTrue (dt.second() == 19); > ++ assertTrue (tzd == -4*3600); > ++ > ++ // Comma-separated fractional seconds (ISO 8601 allows ',' as the decimal sign) > ++ dt = DateTimeParser::parse(DateTimeFormat::ISO8601_FORMAT, "2013-10-07T08:23:19,120-04:00", tzd); > ++ assertTrue (dt.year() == 2013); > ++ assertTrue (dt.month() == 10); > ++ assertTrue (dt.day() == 7); > ++ assertTrue (dt.hour() == 8); > ++ assertTrue (dt.minute() == 23); > ++ assertTrue (dt.second() == 19); > ++ assertTrue (tzd == -4*3600); > ++ > ++ // A bare decimal point not followed by any digit must be rejected > ++ testBad(DateTimeFormat::ISO8601_FORMAT, "2013-10-07T08:23:19.-04:00", tzd); > ++} > ++ > ++ > + void DateTimeParserTest::testGuess() > + { > + int tzd; > +@@ -917,6 +948,7 @@ CppUnit::Test* DateTimeParserTest::suite() > + CppUnit_addTest(pSuite, DateTimeParserTest, testASCTIME); > + CppUnit_addTest(pSuite, DateTimeParserTest, testSORTABLE); > + CppUnit_addTest(pSuite, DateTimeParserTest, testCustom); > ++ CppUnit_addTest(pSuite, DateTimeParserTest, testISO8601FracSeconds); > + CppUnit_addTest(pSuite, DateTimeParserTest, testGuess); > + CppUnit_addTest(pSuite, DateTimeParserTest, testCleanup); > + CppUnit_addTest(pSuite, DateTimeParserTest, testParseMonth); > +diff --git a/Foundation/testsuite/src/DateTimeParserTest.h b/Foundation/testsuite/src/DateTimeParserTest.h > +index f25416971..07f0b6924 100644 > +--- a/Foundation/testsuite/src/DateTimeParserTest.h > ++++ b/Foundation/testsuite/src/DateTimeParserTest.h > +@@ -34,6 +34,7 @@ public: > + void testASCTIME(); > + void testSORTABLE(); > + void testCustom(); > ++ void testISO8601FracSeconds(); > + void testGuess(); > + void testCleanup(); > + void testParseMonth(); > diff --git a/meta-oe/recipes-support/poco/poco_1.15.2.bb b/meta-oe/recipes-support/poco/poco_1.15.2.bb > index 5a873a1d85..bc3d3f9180 100644 > --- a/meta-oe/recipes-support/poco/poco_1.15.2.bb > +++ b/meta-oe/recipes-support/poco/poco_1.15.2.bb > @@ -12,6 +12,7 @@ SRC_URI = "git://github.com/pocoproject/poco.git;branch=poco-${PV};protocol=http > file://0001-cppignore.lnx-Ignore-PKCS12-and-testLaunch-test.patch \ > file://0002-DataTest-disable-testSQLChannel-test.patch \ > file://0003-quill-rdtsc-fallback-for-32-bit-powerpc.patch \ > + file://0004-Fix-DateTimeParser-trailing-timezone-designators-Tim.patch \ > file://run-ptest \ > " > SRCREV = "afbb1ab68f29eec7079e2fdfa04b3efdbec6529d" > > -=-=-=-=-=-=-=-=-=-=-=- > Links: You receive all messages sent to this group. > View/Reply Online (#127314): https://lists.openembedded.org/g/openembedded-devel/message/127314 > Mute This Topic: https://lists.openembedded.org/mt/119589240/3619737 > Group Owner: openembedded-devel+owner@lists.openembedded.org > Unsubscribe: https://lists.openembedded.org/g/openembedded-devel/unsub [ankur.tyagi85@gmail.com] > -=-=-=-=-=-=-=-=-=-=-=- >
nvm, there is already a pending patch[1] to bump the version [1] https://lists.openembedded.org/g/openembedded-devel/message/127226 On Tue, Jun 2, 2026 at 12:58 PM Ankur Tyagi <ankur.tyagi85@gmail.com> wrote: > > For the master branch, IMO we should bump to v1.15.3[1] instead of > backporting the patch. > > [1] https://github.com/pocoproject/poco/releases/tag/poco-1.15.3-release > > On Tue, Jun 2, 2026 at 12:08 AM Andrej Kozemcak via > lists.openembedded.org > <andrej.kozemcak=siemens.com@lists.openembedded.org> wrote: > > > > Patch contains tree patches which was merge to poco at once > > and fix "time" issues > > > > 1 - fix(Foundation): Timezone: invalidate utcOffset cache when /etc/localtime changes > > > > Poco commit 1850dc16aabf5980a490bb1b66086d6695abb823 introduced a > > TZInfo cache for the UTC offset to avoid repeated tzset() syscalls. > > The cache is invalidated only when the TZ environment variable changes. > > However, the TZ variable is process-local: if a different process (e.g. > > a timezone configuration daemon or an init script) changes the system > > timezone by updating /etc/localtime, the running process is not notified > > and its TZ environment variable remains unchanged. > > > > On systems that switch timezone by updating /etc/localtime (a symlink) > > without touching the TZ env var, the cache is therefore never invalidated > > and Timezone::utcOffset() returns the stale value computed at startup. > > > > Fix by extending cacheTZ()/tzChanged() to also track the inode and > > mtime of /etc/localtime via stat(2). When either changes the cache is > > considered stale and reloaded, preserving the performance benefit for > > the common case where neither TZ nor /etc/localtime changes between > > calls. > > > > 2 - fix(Foundation): DateTimeParser: %S consume optional fractional seconds > > > > Parsing ISO 8601 date strings that contain both fractional seconds and a > > timezone offset (e.g. "2013-10-07T08:23:19.120-04:00") with the format > > "%Y-%m-%dT%H:%M:%S%z" raises a SyntaxException: > > > > - %S consumes the integer seconds but stops at '.', leaving > > ".120-04:00" unconsumed. > > - %z (parseTZD) is called next but sees '.' and returns without > > consuming anything. > > - The trailing-garbage check then raises SyntaxException. > > > > Extend the %S case to consume and discard an optional fractional-second > > suffix ('.' or ',' followed by one or more digits) immediately after > > parsing the integer seconds. This mirrors the existing %s behaviour and > > allows %z to see the timezone designator directly, keeping the > > trailing-garbage check fully effective for truly invalid input. > > > > 3 - test(DateTimeParserTest): add ISO8601 fractional seconds parser test > > > > Add testISO8601FracSeconds to verify that DateTimeParser correctly > > handles fractional-second suffixes (dot and comma separated) in > > ISO8601_FORMAT strings, and rejects malformed input such as a > > bare decimal point with no digits. > > > > Signed-off-by: Andrej Kozemcak <andrej.kozemcak@siemens.com> > > --- > > ...er-trailing-timezone-designators-Tim.patch | 240 ++++++++++++++++++ > > meta-oe/recipes-support/poco/poco_1.15.2.bb | 1 + > > 2 files changed, 241 insertions(+) > > create mode 100644 meta-oe/recipes-support/poco/poco/0004-Fix-DateTimeParser-trailing-timezone-designators-Tim.patch > > > > diff --git a/meta-oe/recipes-support/poco/poco/0004-Fix-DateTimeParser-trailing-timezone-designators-Tim.patch b/meta-oe/recipes-support/poco/poco/0004-Fix-DateTimeParser-trailing-timezone-designators-Tim.patch > > new file mode 100644 > > index 0000000000..d2a8666b23 > > --- /dev/null > > +++ b/meta-oe/recipes-support/poco/poco/0004-Fix-DateTimeParser-trailing-timezone-designators-Tim.patch > > @@ -0,0 +1,240 @@ > > +From a767d36fed678e874536e33731be6dbcc5fdc5d0 Mon Sep 17 00:00:00 2001 > > +From: kozemcak <akozemcak@gmail.com> > > +Date: Tue, 12 May 2026 16:13:12 +0200 > > +Subject: [PATCH] Fix: DateTimeParser trailing timezone designators & Timezone > > + UTC offset cache invalidation (#5318) > > + > > +* fix(Foundation): Timezone: invalidate utcOffset cache when /etc/localtime changes > > + > > +Poco commit 1850dc16aabf5980a490bb1b66086d6695abb823 introduced a > > +TZInfo cache for the UTC offset to avoid repeated tzset() syscalls. > > +The cache is invalidated only when the TZ environment variable changes. > > +However, the TZ variable is process-local: if a different process (e.g. > > +a timezone configuration daemon or an init script) changes the system > > +timezone by updating /etc/localtime, the running process is not notified > > +and its TZ environment variable remains unchanged. > > + > > +On systems that switch timezone by updating /etc/localtime (a symlink) > > +without touching the TZ env var, the cache is therefore never invalidated > > +and Timezone::utcOffset() returns the stale value computed at startup. > > + > > +Fix by extending cacheTZ()/tzChanged() to also track the inode and > > +mtime of /etc/localtime via stat(2). When either changes the cache is > > +considered stale and reloaded, preserving the performance benefit for > > +the common case where neither TZ nor /etc/localtime changes between > > +calls. > > + > > +Signed-off-by: Andrej Kozemcak <andrej.kozemcak@siemens.com> > > + > > +* fix(Foundation): DateTimeParser: %S consume optional fractional seconds > > + > > +Parsing ISO 8601 date strings that contain both fractional seconds and a > > +timezone offset (e.g. "2013-10-07T08:23:19.120-04:00") with the format > > +"%Y-%m-%dT%H:%M:%S%z" raises a SyntaxException: > > + > > + - %S consumes the integer seconds but stops at '.', leaving > > + ".120-04:00" unconsumed. > > + - %z (parseTZD) is called next but sees '.' and returns without > > + consuming anything. > > + - The trailing-garbage check then raises SyntaxException. > > + > > +Extend the %S case to consume and discard an optional fractional-second > > +suffix ('.' or ',' followed by one or more digits) immediately after > > +parsing the integer seconds. This mirrors the existing %s behaviour and > > +allows %z to see the timezone designator directly, keeping the > > +trailing-garbage check fully effective for truly invalid input. > > + > > +Fix suggested by matejk. > > + > > +Signed-off-by: Andrej Kozemcak <andrej.kozemcak@siemens.com> > > + > > +* test(DateTimeParserTest): add ISO8601 fractional seconds parser test > > + > > +Add testISO8601FracSeconds to verify that DateTimeParser correctly > > +handles fractional-second suffixes (dot and comma separated) in > > +ISO8601_FORMAT strings, and rejects malformed input such as a > > +bare decimal point with no digits. > > + > > +--------- > > + > > +Upstream-Status: Backport [1.15.3, https://github.com/pocoproject/poco/commit/b8d2b50774b25a470c5a30fc91bae7cd47c251fa] > > + > > +Signed-off-by: Andrej Kozemcak <andrej.kozemcak@siemens.com> > > +Co-authored-by: Andrej Kozemcak <andrej.kozemcak@siemens.com> > > +--- > > + Foundation/include/Poco/DateTimeParser.h | 6 ++++ > > + Foundation/src/DateTimeParser.cpp | 12 +++++++ > > + Foundation/src/Timezone_UNIX.cpp | 36 ++++++++++++++++++- > > + .../testsuite/src/DateTimeParserTest.cpp | 32 +++++++++++++++++ > > + Foundation/testsuite/src/DateTimeParserTest.h | 1 + > > + 5 files changed, 86 insertions(+), 1 deletion(-) > > + > > +diff --git a/Foundation/include/Poco/DateTimeParser.h b/Foundation/include/Poco/DateTimeParser.h > > +index 2d1882d83..e82a934c9 100644 > > +--- a/Foundation/include/Poco/DateTimeParser.h > > ++++ b/Foundation/include/Poco/DateTimeParser.h > > +@@ -63,6 +63,12 @@ public: > > + /// Throws a SyntaxException if the string cannot be successfully parsed. > > + /// Please see DateTimeFormatter::format() for a description of the format string. > > + /// Class DateTimeFormat defines format strings for various standard date/time formats. > > ++ /// > > ++ /// Note: The %S specifier parses whole seconds and then silently discards any > > ++ /// fractional-second suffix of the form '.DDD' or ',DDD' (including a bare decimal > > ++ /// point that is immediately followed by a non-digit is treated as a parse error). > > ++ /// Callers that need the fractional digits captured must use %s (millis+micros), > > ++ /// %i (milliseconds only), %c (centiseconds), or %F (six-digit fractional seconds). > > + > > + static DateTime parse(const std::string& fmt, const std::string& str, int& timeZoneDifferential); > > + /// Parses a date and time in the given format from the given string. > > +diff --git a/Foundation/src/DateTimeParser.cpp b/Foundation/src/DateTimeParser.cpp > > +index 08b3494ac..c2fe3dfaa 100644 > > +--- a/Foundation/src/DateTimeParser.cpp > > ++++ b/Foundation/src/DateTimeParser.cpp > > +@@ -243,6 +243,18 @@ void DateTimeParser::parse(const std::string& fmt, const std::string& dtStr, Dat > > + case 'S': > > + it = skipNonDigits(it, end); > > + second = parseNumberN(dtStr, it, end, 2); > > ++ // Consume optional fractional seconds ('.NNN' or ',NNN') so that a > > ++ // subsequent %z specifier can reach the timezone designator. > > ++ // A decimal point/comma not followed by a digit is an error. > > ++ if (it != end && (*it == '.' || *it == ',')) > > ++ { > > ++ ++it; > > ++ if (it == end || !Ascii::isDigit(*it)) > > ++ { > > ++ throw SyntaxException("Invalid DateTimeString: " + dtStr + ", missing fractional digits"); > > ++ } > > ++ it = skipDigits(it, end); > > ++ } > > + break; > > + case 's': > > + it = skipNonDigits(it, end); > > +diff --git a/Foundation/src/Timezone_UNIX.cpp b/Foundation/src/Timezone_UNIX.cpp > > +index d1489e72f..3e0d29838 100644 > > +--- a/Foundation/src/Timezone_UNIX.cpp > > ++++ b/Foundation/src/Timezone_UNIX.cpp > > +@@ -18,6 +18,7 @@ > > + #include <ctime> > > + #include <cstdlib> > > + #include <string> > > ++#include <sys/stat.h> > > + > > + > > + namespace Poco { > > +@@ -66,13 +67,44 @@ private: > > + { > > + const char* tz = std::getenv("TZ"); > > + _cachedTZ = tz ? tz : ""; > > ++ // /etc/localtime is the system-wide zone source (updated by timedatectl etc.). > > ++ // Stat'ing it lets us detect zone changes that don't touch the TZ env var; we > > ++ // don't read its contents — tzset()/libc still resolves the actual zone data. > > ++ cacheLocaltimeStat(); > > + } > > + > > + bool tzChanged() const > > + { > > + const char* tz = std::getenv("TZ"); > > + std::string currentTZ = tz ? tz : ""; > > +- return currentTZ != _cachedTZ; > > ++ if (currentTZ != _cachedTZ) return true; > > ++ return localtimeStatChanged(); > > ++ } > > ++ > > ++ void cacheLocaltimeStat() > > ++ { > > ++ struct stat st{}; > > ++ if (::stat("/etc/localtime", &st) == 0) > > ++ { > > ++ _localtimeIno = st.st_ino; > > ++ _localtimeMtime = st.st_mtime; > > ++ } > > ++ else > > ++ { > > ++ _localtimeIno = 0; > > ++ _localtimeMtime = 0; > > ++ } > > ++ } > > ++ > > ++ bool localtimeStatChanged() const > > ++ { > > ++ struct stat st{}; > > ++ if (::stat("/etc/localtime", &st) == 0) > > ++ { > > ++ return st.st_ino != _localtimeIno || st.st_mtime != _localtimeMtime; > > ++ } > > ++ // /etc/localtime not accessible: treat as changed only if we had it before > > ++ return _localtimeIno != 0; > > + } > > + > > + static int computeTimeZone() > > +@@ -112,6 +144,8 @@ private: > > + std::mutex _mutex; > > + int _tzOffset; > > + std::string _cachedTZ; > > ++ ino_t _localtimeIno = 0; > > ++ time_t _localtimeMtime = 0; > > + }; > > + > > + > > +diff --git a/Foundation/testsuite/src/DateTimeParserTest.cpp b/Foundation/testsuite/src/DateTimeParserTest.cpp > > +index 246a0d531..0742e1e02 100644 > > +--- a/Foundation/testsuite/src/DateTimeParserTest.cpp > > ++++ b/Foundation/testsuite/src/DateTimeParserTest.cpp > > +@@ -638,6 +638,37 @@ void DateTimeParserTest::testCustom() > > + } > > + > > + > > ++void DateTimeParserTest::testISO8601FracSeconds() > > ++{ > > ++ // ISO8601_FORMAT uses %S which silently discards a well-formed fractional-second > > ++ // suffix so that the trailing %z can still reach the timezone designator. > > ++ int tzd; > > ++ > > ++ // Dot-separated fractional seconds with negative timezone offset > > ++ DateTime dt = DateTimeParser::parse(DateTimeFormat::ISO8601_FORMAT, "2013-10-07T08:23:19.120-04:00", tzd); > > ++ assertTrue (dt.year() == 2013); > > ++ assertTrue (dt.month() == 10); > > ++ assertTrue (dt.day() == 7); > > ++ assertTrue (dt.hour() == 8); > > ++ assertTrue (dt.minute() == 23); > > ++ assertTrue (dt.second() == 19); > > ++ assertTrue (tzd == -4*3600); > > ++ > > ++ // Comma-separated fractional seconds (ISO 8601 allows ',' as the decimal sign) > > ++ dt = DateTimeParser::parse(DateTimeFormat::ISO8601_FORMAT, "2013-10-07T08:23:19,120-04:00", tzd); > > ++ assertTrue (dt.year() == 2013); > > ++ assertTrue (dt.month() == 10); > > ++ assertTrue (dt.day() == 7); > > ++ assertTrue (dt.hour() == 8); > > ++ assertTrue (dt.minute() == 23); > > ++ assertTrue (dt.second() == 19); > > ++ assertTrue (tzd == -4*3600); > > ++ > > ++ // A bare decimal point not followed by any digit must be rejected > > ++ testBad(DateTimeFormat::ISO8601_FORMAT, "2013-10-07T08:23:19.-04:00", tzd); > > ++} > > ++ > > ++ > > + void DateTimeParserTest::testGuess() > > + { > > + int tzd; > > +@@ -917,6 +948,7 @@ CppUnit::Test* DateTimeParserTest::suite() > > + CppUnit_addTest(pSuite, DateTimeParserTest, testASCTIME); > > + CppUnit_addTest(pSuite, DateTimeParserTest, testSORTABLE); > > + CppUnit_addTest(pSuite, DateTimeParserTest, testCustom); > > ++ CppUnit_addTest(pSuite, DateTimeParserTest, testISO8601FracSeconds); > > + CppUnit_addTest(pSuite, DateTimeParserTest, testGuess); > > + CppUnit_addTest(pSuite, DateTimeParserTest, testCleanup); > > + CppUnit_addTest(pSuite, DateTimeParserTest, testParseMonth); > > +diff --git a/Foundation/testsuite/src/DateTimeParserTest.h b/Foundation/testsuite/src/DateTimeParserTest.h > > +index f25416971..07f0b6924 100644 > > +--- a/Foundation/testsuite/src/DateTimeParserTest.h > > ++++ b/Foundation/testsuite/src/DateTimeParserTest.h > > +@@ -34,6 +34,7 @@ public: > > + void testASCTIME(); > > + void testSORTABLE(); > > + void testCustom(); > > ++ void testISO8601FracSeconds(); > > + void testGuess(); > > + void testCleanup(); > > + void testParseMonth(); > > diff --git a/meta-oe/recipes-support/poco/poco_1.15.2.bb b/meta-oe/recipes-support/poco/poco_1.15.2.bb > > index 5a873a1d85..bc3d3f9180 100644 > > --- a/meta-oe/recipes-support/poco/poco_1.15.2.bb > > +++ b/meta-oe/recipes-support/poco/poco_1.15.2.bb > > @@ -12,6 +12,7 @@ SRC_URI = "git://github.com/pocoproject/poco.git;branch=poco-${PV};protocol=http > > file://0001-cppignore.lnx-Ignore-PKCS12-and-testLaunch-test.patch \ > > file://0002-DataTest-disable-testSQLChannel-test.patch \ > > file://0003-quill-rdtsc-fallback-for-32-bit-powerpc.patch \ > > + file://0004-Fix-DateTimeParser-trailing-timezone-designators-Tim.patch \ > > file://run-ptest \ > > " > > SRCREV = "afbb1ab68f29eec7079e2fdfa04b3efdbec6529d" > > > > -=-=-=-=-=-=-=-=-=-=-=- > > Links: You receive all messages sent to this group. > > View/Reply Online (#127314): https://lists.openembedded.org/g/openembedded-devel/message/127314 > > Mute This Topic: https://lists.openembedded.org/mt/119589240/3619737 > > Group Owner: openembedded-devel+owner@lists.openembedded.org > > Unsubscribe: https://lists.openembedded.org/g/openembedded-devel/unsub [ankur.tyagi85@gmail.com] > > -=-=-=-=-=-=-=-=-=-=-=- > >
diff --git a/meta-oe/recipes-support/poco/poco/0004-Fix-DateTimeParser-trailing-timezone-designators-Tim.patch b/meta-oe/recipes-support/poco/poco/0004-Fix-DateTimeParser-trailing-timezone-designators-Tim.patch new file mode 100644 index 0000000000..d2a8666b23 --- /dev/null +++ b/meta-oe/recipes-support/poco/poco/0004-Fix-DateTimeParser-trailing-timezone-designators-Tim.patch @@ -0,0 +1,240 @@ +From a767d36fed678e874536e33731be6dbcc5fdc5d0 Mon Sep 17 00:00:00 2001 +From: kozemcak <akozemcak@gmail.com> +Date: Tue, 12 May 2026 16:13:12 +0200 +Subject: [PATCH] Fix: DateTimeParser trailing timezone designators & Timezone + UTC offset cache invalidation (#5318) + +* fix(Foundation): Timezone: invalidate utcOffset cache when /etc/localtime changes + +Poco commit 1850dc16aabf5980a490bb1b66086d6695abb823 introduced a +TZInfo cache for the UTC offset to avoid repeated tzset() syscalls. +The cache is invalidated only when the TZ environment variable changes. +However, the TZ variable is process-local: if a different process (e.g. +a timezone configuration daemon or an init script) changes the system +timezone by updating /etc/localtime, the running process is not notified +and its TZ environment variable remains unchanged. + +On systems that switch timezone by updating /etc/localtime (a symlink) +without touching the TZ env var, the cache is therefore never invalidated +and Timezone::utcOffset() returns the stale value computed at startup. + +Fix by extending cacheTZ()/tzChanged() to also track the inode and +mtime of /etc/localtime via stat(2). When either changes the cache is +considered stale and reloaded, preserving the performance benefit for +the common case where neither TZ nor /etc/localtime changes between +calls. + +Signed-off-by: Andrej Kozemcak <andrej.kozemcak@siemens.com> + +* fix(Foundation): DateTimeParser: %S consume optional fractional seconds + +Parsing ISO 8601 date strings that contain both fractional seconds and a +timezone offset (e.g. "2013-10-07T08:23:19.120-04:00") with the format +"%Y-%m-%dT%H:%M:%S%z" raises a SyntaxException: + + - %S consumes the integer seconds but stops at '.', leaving + ".120-04:00" unconsumed. + - %z (parseTZD) is called next but sees '.' and returns without + consuming anything. + - The trailing-garbage check then raises SyntaxException. + +Extend the %S case to consume and discard an optional fractional-second +suffix ('.' or ',' followed by one or more digits) immediately after +parsing the integer seconds. This mirrors the existing %s behaviour and +allows %z to see the timezone designator directly, keeping the +trailing-garbage check fully effective for truly invalid input. + +Fix suggested by matejk. + +Signed-off-by: Andrej Kozemcak <andrej.kozemcak@siemens.com> + +* test(DateTimeParserTest): add ISO8601 fractional seconds parser test + +Add testISO8601FracSeconds to verify that DateTimeParser correctly +handles fractional-second suffixes (dot and comma separated) in +ISO8601_FORMAT strings, and rejects malformed input such as a +bare decimal point with no digits. + +--------- + +Upstream-Status: Backport [1.15.3, https://github.com/pocoproject/poco/commit/b8d2b50774b25a470c5a30fc91bae7cd47c251fa] + +Signed-off-by: Andrej Kozemcak <andrej.kozemcak@siemens.com> +Co-authored-by: Andrej Kozemcak <andrej.kozemcak@siemens.com> +--- + Foundation/include/Poco/DateTimeParser.h | 6 ++++ + Foundation/src/DateTimeParser.cpp | 12 +++++++ + Foundation/src/Timezone_UNIX.cpp | 36 ++++++++++++++++++- + .../testsuite/src/DateTimeParserTest.cpp | 32 +++++++++++++++++ + Foundation/testsuite/src/DateTimeParserTest.h | 1 + + 5 files changed, 86 insertions(+), 1 deletion(-) + +diff --git a/Foundation/include/Poco/DateTimeParser.h b/Foundation/include/Poco/DateTimeParser.h +index 2d1882d83..e82a934c9 100644 +--- a/Foundation/include/Poco/DateTimeParser.h ++++ b/Foundation/include/Poco/DateTimeParser.h +@@ -63,6 +63,12 @@ public: + /// Throws a SyntaxException if the string cannot be successfully parsed. + /// Please see DateTimeFormatter::format() for a description of the format string. + /// Class DateTimeFormat defines format strings for various standard date/time formats. ++ /// ++ /// Note: The %S specifier parses whole seconds and then silently discards any ++ /// fractional-second suffix of the form '.DDD' or ',DDD' (including a bare decimal ++ /// point that is immediately followed by a non-digit is treated as a parse error). ++ /// Callers that need the fractional digits captured must use %s (millis+micros), ++ /// %i (milliseconds only), %c (centiseconds), or %F (six-digit fractional seconds). + + static DateTime parse(const std::string& fmt, const std::string& str, int& timeZoneDifferential); + /// Parses a date and time in the given format from the given string. +diff --git a/Foundation/src/DateTimeParser.cpp b/Foundation/src/DateTimeParser.cpp +index 08b3494ac..c2fe3dfaa 100644 +--- a/Foundation/src/DateTimeParser.cpp ++++ b/Foundation/src/DateTimeParser.cpp +@@ -243,6 +243,18 @@ void DateTimeParser::parse(const std::string& fmt, const std::string& dtStr, Dat + case 'S': + it = skipNonDigits(it, end); + second = parseNumberN(dtStr, it, end, 2); ++ // Consume optional fractional seconds ('.NNN' or ',NNN') so that a ++ // subsequent %z specifier can reach the timezone designator. ++ // A decimal point/comma not followed by a digit is an error. ++ if (it != end && (*it == '.' || *it == ',')) ++ { ++ ++it; ++ if (it == end || !Ascii::isDigit(*it)) ++ { ++ throw SyntaxException("Invalid DateTimeString: " + dtStr + ", missing fractional digits"); ++ } ++ it = skipDigits(it, end); ++ } + break; + case 's': + it = skipNonDigits(it, end); +diff --git a/Foundation/src/Timezone_UNIX.cpp b/Foundation/src/Timezone_UNIX.cpp +index d1489e72f..3e0d29838 100644 +--- a/Foundation/src/Timezone_UNIX.cpp ++++ b/Foundation/src/Timezone_UNIX.cpp +@@ -18,6 +18,7 @@ + #include <ctime> + #include <cstdlib> + #include <string> ++#include <sys/stat.h> + + + namespace Poco { +@@ -66,13 +67,44 @@ private: + { + const char* tz = std::getenv("TZ"); + _cachedTZ = tz ? tz : ""; ++ // /etc/localtime is the system-wide zone source (updated by timedatectl etc.). ++ // Stat'ing it lets us detect zone changes that don't touch the TZ env var; we ++ // don't read its contents — tzset()/libc still resolves the actual zone data. ++ cacheLocaltimeStat(); + } + + bool tzChanged() const + { + const char* tz = std::getenv("TZ"); + std::string currentTZ = tz ? tz : ""; +- return currentTZ != _cachedTZ; ++ if (currentTZ != _cachedTZ) return true; ++ return localtimeStatChanged(); ++ } ++ ++ void cacheLocaltimeStat() ++ { ++ struct stat st{}; ++ if (::stat("/etc/localtime", &st) == 0) ++ { ++ _localtimeIno = st.st_ino; ++ _localtimeMtime = st.st_mtime; ++ } ++ else ++ { ++ _localtimeIno = 0; ++ _localtimeMtime = 0; ++ } ++ } ++ ++ bool localtimeStatChanged() const ++ { ++ struct stat st{}; ++ if (::stat("/etc/localtime", &st) == 0) ++ { ++ return st.st_ino != _localtimeIno || st.st_mtime != _localtimeMtime; ++ } ++ // /etc/localtime not accessible: treat as changed only if we had it before ++ return _localtimeIno != 0; + } + + static int computeTimeZone() +@@ -112,6 +144,8 @@ private: + std::mutex _mutex; + int _tzOffset; + std::string _cachedTZ; ++ ino_t _localtimeIno = 0; ++ time_t _localtimeMtime = 0; + }; + + +diff --git a/Foundation/testsuite/src/DateTimeParserTest.cpp b/Foundation/testsuite/src/DateTimeParserTest.cpp +index 246a0d531..0742e1e02 100644 +--- a/Foundation/testsuite/src/DateTimeParserTest.cpp ++++ b/Foundation/testsuite/src/DateTimeParserTest.cpp +@@ -638,6 +638,37 @@ void DateTimeParserTest::testCustom() + } + + ++void DateTimeParserTest::testISO8601FracSeconds() ++{ ++ // ISO8601_FORMAT uses %S which silently discards a well-formed fractional-second ++ // suffix so that the trailing %z can still reach the timezone designator. ++ int tzd; ++ ++ // Dot-separated fractional seconds with negative timezone offset ++ DateTime dt = DateTimeParser::parse(DateTimeFormat::ISO8601_FORMAT, "2013-10-07T08:23:19.120-04:00", tzd); ++ assertTrue (dt.year() == 2013); ++ assertTrue (dt.month() == 10); ++ assertTrue (dt.day() == 7); ++ assertTrue (dt.hour() == 8); ++ assertTrue (dt.minute() == 23); ++ assertTrue (dt.second() == 19); ++ assertTrue (tzd == -4*3600); ++ ++ // Comma-separated fractional seconds (ISO 8601 allows ',' as the decimal sign) ++ dt = DateTimeParser::parse(DateTimeFormat::ISO8601_FORMAT, "2013-10-07T08:23:19,120-04:00", tzd); ++ assertTrue (dt.year() == 2013); ++ assertTrue (dt.month() == 10); ++ assertTrue (dt.day() == 7); ++ assertTrue (dt.hour() == 8); ++ assertTrue (dt.minute() == 23); ++ assertTrue (dt.second() == 19); ++ assertTrue (tzd == -4*3600); ++ ++ // A bare decimal point not followed by any digit must be rejected ++ testBad(DateTimeFormat::ISO8601_FORMAT, "2013-10-07T08:23:19.-04:00", tzd); ++} ++ ++ + void DateTimeParserTest::testGuess() + { + int tzd; +@@ -917,6 +948,7 @@ CppUnit::Test* DateTimeParserTest::suite() + CppUnit_addTest(pSuite, DateTimeParserTest, testASCTIME); + CppUnit_addTest(pSuite, DateTimeParserTest, testSORTABLE); + CppUnit_addTest(pSuite, DateTimeParserTest, testCustom); ++ CppUnit_addTest(pSuite, DateTimeParserTest, testISO8601FracSeconds); + CppUnit_addTest(pSuite, DateTimeParserTest, testGuess); + CppUnit_addTest(pSuite, DateTimeParserTest, testCleanup); + CppUnit_addTest(pSuite, DateTimeParserTest, testParseMonth); +diff --git a/Foundation/testsuite/src/DateTimeParserTest.h b/Foundation/testsuite/src/DateTimeParserTest.h +index f25416971..07f0b6924 100644 +--- a/Foundation/testsuite/src/DateTimeParserTest.h ++++ b/Foundation/testsuite/src/DateTimeParserTest.h +@@ -34,6 +34,7 @@ public: + void testASCTIME(); + void testSORTABLE(); + void testCustom(); ++ void testISO8601FracSeconds(); + void testGuess(); + void testCleanup(); + void testParseMonth(); diff --git a/meta-oe/recipes-support/poco/poco_1.15.2.bb b/meta-oe/recipes-support/poco/poco_1.15.2.bb index 5a873a1d85..bc3d3f9180 100644 --- a/meta-oe/recipes-support/poco/poco_1.15.2.bb +++ b/meta-oe/recipes-support/poco/poco_1.15.2.bb @@ -12,6 +12,7 @@ SRC_URI = "git://github.com/pocoproject/poco.git;branch=poco-${PV};protocol=http file://0001-cppignore.lnx-Ignore-PKCS12-and-testLaunch-test.patch \ file://0002-DataTest-disable-testSQLChannel-test.patch \ file://0003-quill-rdtsc-fallback-for-32-bit-powerpc.patch \ + file://0004-Fix-DateTimeParser-trailing-timezone-designators-Tim.patch \ file://run-ptest \ " SRCREV = "afbb1ab68f29eec7079e2fdfa04b3efdbec6529d"
Patch contains tree patches which was merge to poco at once and fix "time" issues 1 - fix(Foundation): Timezone: invalidate utcOffset cache when /etc/localtime changes Poco commit 1850dc16aabf5980a490bb1b66086d6695abb823 introduced a TZInfo cache for the UTC offset to avoid repeated tzset() syscalls. The cache is invalidated only when the TZ environment variable changes. However, the TZ variable is process-local: if a different process (e.g. a timezone configuration daemon or an init script) changes the system timezone by updating /etc/localtime, the running process is not notified and its TZ environment variable remains unchanged. On systems that switch timezone by updating /etc/localtime (a symlink) without touching the TZ env var, the cache is therefore never invalidated and Timezone::utcOffset() returns the stale value computed at startup. Fix by extending cacheTZ()/tzChanged() to also track the inode and mtime of /etc/localtime via stat(2). When either changes the cache is considered stale and reloaded, preserving the performance benefit for the common case where neither TZ nor /etc/localtime changes between calls. 2 - fix(Foundation): DateTimeParser: %S consume optional fractional seconds Parsing ISO 8601 date strings that contain both fractional seconds and a timezone offset (e.g. "2013-10-07T08:23:19.120-04:00") with the format "%Y-%m-%dT%H:%M:%S%z" raises a SyntaxException: - %S consumes the integer seconds but stops at '.', leaving ".120-04:00" unconsumed. - %z (parseTZD) is called next but sees '.' and returns without consuming anything. - The trailing-garbage check then raises SyntaxException. Extend the %S case to consume and discard an optional fractional-second suffix ('.' or ',' followed by one or more digits) immediately after parsing the integer seconds. This mirrors the existing %s behaviour and allows %z to see the timezone designator directly, keeping the trailing-garbage check fully effective for truly invalid input. 3 - test(DateTimeParserTest): add ISO8601 fractional seconds parser test Add testISO8601FracSeconds to verify that DateTimeParser correctly handles fractional-second suffixes (dot and comma separated) in ISO8601_FORMAT strings, and rejects malformed input such as a bare decimal point with no digits. Signed-off-by: Andrej Kozemcak <andrej.kozemcak@siemens.com> --- ...er-trailing-timezone-designators-Tim.patch | 240 ++++++++++++++++++ meta-oe/recipes-support/poco/poco_1.15.2.bb | 1 + 2 files changed, 241 insertions(+) create mode 100644 meta-oe/recipes-support/poco/poco/0004-Fix-DateTimeParser-trailing-timezone-designators-Tim.patch