【问题标题】:timezone differences between utime() and stat()utime() 和 stat() 之间的时区差异
【发布时间】:2018-03-05 20:49:09
【问题描述】:

我有一个 Windows C 程序,它从 unix 服务器下载文件及其修改时间。然后它将本地副本保存在缓存中,并将其时间戳设置为与服务器上的时间戳匹配 - 因此,如果本地副本是最新的,它就不需要提取新副本。

无论如何,它使用 utime() 将时间戳设置为 unix 主机报告的二进制值,然后使用 stat() 读取时间戳以检查文件是否过期。这曾经有效,但我只是注意到它不再有效了。

对于它的价值,时间差异是 3600 秒(1 小时),所以我猜这与 utime 和/或 stat 如何处理时区有关。我只是认为 stat 会向我报告我在 utime 中设置的相同修改时间值,但显然这是(不再是?)真的。我在这里看到了另一篇文章,其中提到了 Windows 的 stat 实现会扰乱夏令时 - 可能就是这样。

在 Windows 中是否有更正确的方法来执行此操作 - 同时仍使用 POSIX 时间函数。请记住,我正在尝试获取本地文件的二进制 mod 时间(由 Windows 报告)以匹配主机文件的 mod 时间(由 unix 上的 stat 报告)。

这里有一些显示问题的示例代码。 fc->timestamp 是从服务器发送的值。我把它复制到utimes,调用utime()和stat(),statbuf.st_mtime和其他的相差3600秒:

// Set the local file's timestamp to the host's value
utimes.actime = utimes.modtime = fc->timestamp;
utime(szTempPath2, &utimes);
// Now call stat to see what time registers there.
if (stat(szTempPath2, &statbuf) == 0) {
if (fc->timestamp != statbuf.st_mtime)
        fc->timestamp = statbuf.st_mtime;
}

When I'm done the values are:
fc->timestamp = 1359772839
utimes.modtime = 1359772839
statbuf.st_mtime = 1359776439 <<== this is 3600 more than the others

【问题讨论】:

  • 好的。我发现另一篇文章解释了(以我不太明白的方式)在 NTFS 文件系统上,当夏令时无效时,stat() 返回错误的时间。正如我上面提到的那样,关闭一个小时。如果我关闭“自动调整夏令时时钟”复选框,问题就会消失。但我的问题仍然存在。有没有一种安全的方法来获取本地 Windows 文件的时间戳以匹配 unix 主机的时间戳?主机没有使用 64 位 UTC 并且具有 1970 年基准年,因此在 Windows 端切换到这些功能将不起作用 - 会吗?
  • 这些 POSIX 函数通常不是“Windows”。它们由您使用的任何 C 运行时库提供。我无法使用第一个作为公共 Windows 组件(即通用 CRT)正式分发的 C 运行时来重现这一点。我建议您检查_utime 实际设置的内容。致电 GetFileTime 以检查 lpLastWriteTime(自 1601-01-01,UTC 起为 100 ns 单位)。除以 10,000,000 并减去 11,644,473,600 以将其转换为 Unix 时间戳。
  • 我尝试了您的 GetFileTime 方法,在将 lpLastWriteTime 转换为 unix 时间戳后,它与 statbuf.st_mtime 产生的“关闭 1 小时”值匹配。所以,我想这意味着我真正需要使用的是 SetFileTime 而不是 utime() - 然后 stat 就可以工作了。
  • 是的。我刚试过。如果我反转您的时间计算(将 unix 时间戳转换为 Windows UTC)并使用 SetFileTime 设置 lpLastWriteTime,随后对 stat 的调用将返回原始 unix 文件时间,并且我的缓存时间戳检查成功。非常感谢。

标签: winapi timezone stat


【解决方案1】:

原来 utime() 是罪魁祸首。

如果您使用 utime 设置 mod 时间,如果 NTFS 夏令时问题生效,stat() 和 GetFileTime() 都将返回您的时间 + 1 小时。但是,如果您使用 SetFileTime,则 stat() 和 GetFileTime 都会返回您设置的时间 - 无论 NTFS 处理夏令时的方式如何。

因此,它就像上面 cmets 中描述的 erkysun,除了您需要反转 windows-to-unix 时间戳计算,而是将 unix 时间戳转换为自 1601 年 1 月 1 日以来的 windows UTC 微秒(使用 erkysun 提供的常量)。像这样:

ulong     unix_timestamp;
ULONGLONG win_timestamp;
ULARGE_INTEGER li;
HFILE     hFile;
FILETIME  filetime;

   if (hFile = CreateFile(szTempPath2, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 
                          FILE_ATTRIBUTE_NORMAL, NULL)) {
      // Convert the unix timestamp to Windows UTC time
      // 1/1/1970 unix base date (seconds) to 1/1/1601 windows base date
      win_timestamp = unix_timestamp + 11644473600;
      // seconds to 100's of nanoseconds
      win_timestamp *= 10000000;
      // Copy the long long int into filetime high/low and
      // use to set the access and mod times.
      li.QuadPart = win_timestamp;
      filetime.dwHighDateTime = li.HighPart;
      filetime.dwLowDateTime = li.LowPart;
      SetFileTime(hFile, NULL, &filetime, &filetime);
      CloseHandle(hFile);
  }

【讨论】:

    猜你喜欢
    • 2011-01-23
    • 2018-09-16
    • 2015-07-13
    • 2012-10-31
    • 2012-04-09
    • 2018-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多