diff --git a/Foundation/src/DateTimeParser.cpp b/Foundation/src/DateTimeParser.cpp index 08b3494ac5..3d088be533 100644 --- a/Foundation/src/DateTimeParser.cpp +++ b/Foundation/src/DateTimeParser.cpp @@ -309,6 +309,36 @@ void DateTimeParser::parse(const std::string& fmt, const std::string& dtStr, Dat } // Skip trailing whitespace while (it != end && Ascii::isSpace(*it)) ++it; + // Skip optional fractional seconds ('.NNN' or '.NNNNNN') left unconsumed + // when the format used %S (integer seconds) but the input contains sub-second + // precision (e.g. "2013-10-07 08:23:19.120-04:00" parsed with ISO8601_FORMAT + // which uses %S not %s). + if (it != end && (*it == '.' || *it == ',')) + { + auto savedIt = it; + ++it; // skip the separator + if (it != end && Ascii::isDigit(*it)) + { + while (it != end && Ascii::isDigit(*it)) ++it; // skip digits + } + else + { + it = savedIt; // no digits after separator - not fractional seconds + } + } + // Skip optional trailing timezone designator (e.g. 'Z', '+04:00', '-04:00', + // named zones such as 'GMT') for compatibility with ISO 8601, RFC 3339 and + // ASN.1 UTCTime/GeneralizedTime formats that carry timezone information not + // matched by the format string. Use parseTZD() so that all recognised TZD + // patterns are consumed; restore the iterator if parseTZD throws (unknown or + // partially-parsed designator) so that the trailing-garbage check below still + // catches truly invalid suffixes. + if (it != end && (*it == 'Z' || *it == 'z' || *it == '+' || *it == '-' || Ascii::isAlpha(*it))) + { + auto savedIt = it; + try { parseTZD(it, end); } + catch (...) { it = savedIt; } + } // Check for unconsumed input if (it != end) throw SyntaxException("Invalid DateTimeString: " + dtStr + ", unexpected trailing characters"); diff --git a/Foundation/src/Timezone_UNIX.cpp b/Foundation/src/Timezone_UNIX.cpp index d1489e72ff..8b14f6f112 100644 --- a/Foundation/src/Timezone_UNIX.cpp +++ b/Foundation/src/Timezone_UNIX.cpp @@ -17,7 +17,9 @@ #include #include #include +#include #include +#include namespace Poco { @@ -66,13 +68,52 @@ class TZInfo { const char* tz = std::getenv("TZ"); _cachedTZ = tz ? tz : ""; + 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) + { + _localtime_ino = st.st_ino; + _localtime_mtime = st.st_mtime; + } + else + { + _localtime_ino = 0; + _localtime_mtime = 0; + } + } + + bool localtimeStatChanged() const + { + struct stat st{}; + if (::stat("/etc/localtime", &st) == 0) + { + if (st.st_ino != _localtime_ino || st.st_mtime != _localtime_mtime) + { + _localtime_ino = st.st_ino; + _localtime_mtime = st.st_mtime; + return true; + } + return false; + } + // /etc/localtime not accessible: treat as changed only if we had it before + if (_localtime_ino != 0) { + _localtime_ino = 0; + _localtime_mtime = 0; + return true; + } + return false; } static int computeTimeZone() @@ -112,6 +153,8 @@ class TZInfo std::mutex _mutex; int _tzOffset; std::string _cachedTZ; + mutable ino_t _localtime_ino = 0; + mutable time_t _localtime_mtime = 0; };