mirror of
https://github.com/openembedded/meta-openembedded.git
synced 2026-07-15 16:07:26 +00:00
poco: fix timezone and data time parser issue
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>
Signed-off-by: Anuj Mittal <anuj.mittal@oss.qualcomm.com>
This commit is contained in:
committed by
Anuj Mittal
parent
a43f0d532c
commit
3521444a71
+240
@@ -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();
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user