Skip to content

Commit 5c8afac

Browse files
authored
Fix for #432. (#433)
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.
1 parent 3b43b71 commit 5c8afac

File tree

1 file changed

+35
-1
lines changed

1 file changed

+35
-1
lines changed

source/parser/parser_expressions.cpp

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1115,7 +1115,41 @@ void Parser::Parse_Num_Factor (EXPRESS& Express,int *Terms)
11151115
{
11161116
static boost::posix_time::ptime y2k(boost::gregorian::date(2000,1,1));
11171117
boost::posix_time::ptime now(boost::posix_time::microsec_clock::universal_time());
1118-
Val = (now-y2k).total_microseconds() * (1.0e-6) / (24*60*60);
1118+
1119+
// Due to a bug in `boost::posix_time::microsec_clock::universal_time()`,
1120+
// the value returned may actually be local time rather than UTC. We try to fix this
1121+
// by comparing with `boost::posix_time::second_clock::universal_time()`, which is
1122+
// less precise but more reliable in terms of time zone behavior.
1123+
// (NB: While we could theoretically compute the offset once, and then re-use it every time
1124+
// `new` is invoked, this would cause issues when rendering a scene during transition to or
1125+
// from daylight savings time, or other events affecting the time zone. The current approach
1126+
// is immune to such issues.)
1127+
boost::posix_time::ptime lowPrecisionNow(boost::posix_time::second_clock::universal_time());
1128+
// The difference between the two timers, rounded to quarters of an hour,
1129+
// should correspond to the time zone offset (in seconds in this case).
1130+
const auto tzPrecisionInSeconds = 15 * 60;
1131+
int_least32_t tzOffset = std::lround(float((now - lowPrecisionNow).total_seconds()) / tzPrecisionInSeconds) * tzPrecisionInSeconds;
1132+
// Unless someone paused the code in between the above statements, the resulting difference
1133+
// should be our time zone offset in seconds (unless we're running on a system that's not
1134+
// subject to the bug, in which case the resulting difference should be zero).
1135+
if (tzOffset != 0)
1136+
{
1137+
if (sceneData->EffectiveLanguageVersion() < 380)
1138+
{
1139+
WarningOnce(kWarningGeneral, HERE,
1140+
"In POV-Ray v3.7, on some platforms 'now' erroneously evaluated to days since "
1141+
"2000-01-01 00:00 local time instead of UTC. For backward compatibility, this "
1142+
"bug is reproduced in legacy scenes, but the scene may produce different "
1143+
"results on other platforms.");
1144+
}
1145+
else
1146+
{
1147+
now -= boost::posix_time::time_duration(0, 0, tzOffset);
1148+
}
1149+
}
1150+
1151+
const auto daysPerMicrosecond = 1.0e-6 / (24 * 60 * 60);
1152+
Val = (now - y2k).total_microseconds() * daysPerMicrosecond;
11191153
}
11201154
break;
11211155
}

0 commit comments

Comments
 (0)