From 07eb925e791ff0107458a0a70f000d5615cf8b97 Mon Sep 17 00:00:00 2001 From: Christoph Lipka Date: Thu, 29 Jul 2021 23:21:13 +0200 Subject: [PATCH 1/2] Fix for #432. To work around Boost's msec-precision UTC timer erroneously reporting local time instead of UTC on some systems, we're now also probing the sec-precision UTC timer, which should be more reliable in terms of time zone: The rounded difference between the two should be able to give us the time zone offset (if any) in the msec-precision timer, which we then compensate for accordingly. --- source/parser/parser_expressions.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/source/parser/parser_expressions.cpp b/source/parser/parser_expressions.cpp index 1d4f56bfb..194dbf8f6 100644 --- a/source/parser/parser_expressions.cpp +++ b/source/parser/parser_expressions.cpp @@ -1115,7 +1115,22 @@ void Parser::Parse_Num_Factor (EXPRESS& Express,int *Terms) { static boost::posix_time::ptime y2k(boost::gregorian::date(2000,1,1)); boost::posix_time::ptime now(boost::posix_time::microsec_clock::universal_time()); - Val = (now-y2k).total_microseconds() * (1.0e-6) / (24*60*60); + + // Due to a bug in `boost::posix_time::microsec_clock::universal_time()`, + // the value returned may actually be local time rather than UTC. We try to fix this + // by comparing with `boost::posix_time::second_clock::universal_time()`, which is + // less precise but more reliable in terms of time zone behavior. + boost::posix_time::ptime lowPrecisionNow(boost::posix_time::second_clock::universal_time()); + // The difference between the two timers, rounded to quarters of an hour, + // should correspond to the time zone offset (in seconds in this case). + const auto tzPrecisionInSeconds = 15 * 60; + int_least32_t tzOffset = std::lround(float((now - lowPrecisionNow).total_seconds()) / tzPrecisionInSeconds) * tzPrecisionInSeconds; + // Unless someone paused the code in between the above statements, the resulting difference + // should be our time zone offset. + now -= boost::posix_time::time_duration(0, 0, tzOffset); + + const auto daysPerMicrosecond = 1.0e-6 / (24 * 60 * 60); + Val = (now - y2k).total_microseconds() * daysPerMicrosecond; } break; } From 6e021b39e7082867f6dbea74928034f3800263cb Mon Sep 17 00:00:00 2001 From: Christoph Lipka Date: Mon, 9 Aug 2021 02:04:47 +0200 Subject: [PATCH 2/2] Make `now` behavior depend on `#version` setting. --- source/parser/parser_expressions.cpp | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/source/parser/parser_expressions.cpp b/source/parser/parser_expressions.cpp index 194dbf8f6..165229278 100644 --- a/source/parser/parser_expressions.cpp +++ b/source/parser/parser_expressions.cpp @@ -1120,14 +1120,33 @@ void Parser::Parse_Num_Factor (EXPRESS& Express,int *Terms) // the value returned may actually be local time rather than UTC. We try to fix this // by comparing with `boost::posix_time::second_clock::universal_time()`, which is // less precise but more reliable in terms of time zone behavior. + // (NB: While we could theoretically compute the offset once, and then re-use it every time + // `new` is invoked, this would cause issues when rendering a scene during transition to or + // from daylight savings time, or other events affecting the time zone. The current approach + // is immune to such issues.) boost::posix_time::ptime lowPrecisionNow(boost::posix_time::second_clock::universal_time()); // The difference between the two timers, rounded to quarters of an hour, // should correspond to the time zone offset (in seconds in this case). const auto tzPrecisionInSeconds = 15 * 60; int_least32_t tzOffset = std::lround(float((now - lowPrecisionNow).total_seconds()) / tzPrecisionInSeconds) * tzPrecisionInSeconds; // Unless someone paused the code in between the above statements, the resulting difference - // should be our time zone offset. - now -= boost::posix_time::time_duration(0, 0, tzOffset); + // should be our time zone offset in seconds (unless we're running on a system that's not + // subject to the bug, in which case the resulting difference should be zero). + if (tzOffset != 0) + { + if (sceneData->EffectiveLanguageVersion() < 380) + { + WarningOnce(kWarningGeneral, HERE, + "In POV-Ray v3.7, on some platforms 'now' erroneously evaluated to days since " + "2000-01-01 00:00 local time instead of UTC. For backward compatibility, this " + "bug is reproduced in legacy scenes, but the scene may produce different " + "results on other platforms."); + } + else + { + now -= boost::posix_time::time_duration(0, 0, tzOffset); + } + } const auto daysPerMicrosecond = 1.0e-6 / (24 * 60 * 60); Val = (now - y2k).total_microseconds() * daysPerMicrosecond;