diff mbox series

[meta-oe,v2] poco: fix timezone and data time parser issue

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

Commit Message

Andrej Kozemcak June 1, 2026, 12:08 p.m. UTC
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

Comments

Ankur Tyagi June 2, 2026, 12:58 a.m. UTC | #1
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]
> -=-=-=-=-=-=-=-=-=-=-=-
>
Ankur Tyagi June 2, 2026, 3:26 a.m. UTC | #2
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 mbox series

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"