我有一个在系统启动时创建的线程。它的工作是按需签名PDF文件。要签名,我需要获取服务器日期/时间。为了避免每次都访问服务器,在创建线程时,我获取了服务器时间和当前滴答计数(GetTickCount
),并且在需要当前日期时,我使用以下值进行计算:
CurrentDate := (FServerDate+ (GetTickCount - FInitialTick) / MSecsPerDay);
那是安全的事吗?该代码有效,但是使用这种方法是否有缺点?
答案 0 :(得分:2)
GetTickCount
具有32位分辨率,因此不建议使用它,而应使用GetTickCount64
和相关的FInitialTick: Int64
变量。如果您的操作系统会话持续40天以上,则可能会看到一些舍入错误。
以下是与XP兼容的版本(如果需要维护):
var
GetTickXP: Int64Rec;
function GetTickCount64ForXP: Int64; stdcall;
var t32: cardinal;
t64: Int64Rec absolute result;
begin // warning: GetSystemTimeAsFileTime() is fast, but not monotonic!
t32 := Windows.GetTickCount;
t64 := GetTickXP; // (almost) atomic read
if t32<t64.Lo then
inc(t64.Hi); // wrap-up overflow after 49 days
t64.Lo := t32;
GetTickXP := t64; // (almost) atomic write
end; // warning: FPC's GetTickCount64 doesn't handle 49 days wrap :(
在您的用例中,使用这样的公式是安全的,并且避免每次要求服务器返回时间戳。如果不希望在一段时间后重新启动客户端,则可以每小时或每天询问服务器时间戳,以确保双方同步。
多年来,我们成功使用a similar trick in our mORMot ORM。它只是内置在RestFul ORM中,其字段会自动填充创建或修改时间(TCreateTime
和TModTime
字段)。
最后一个提示:确保在系统中的任何地方都使用UTC日期/时间。 :)