65.9K
CodeProject 正在变化。 阅读更多。
Home

Time64 - 32 位 Windows 应用程序的 64 位时间

starIconstarIconstarIconstarIconemptyStarIcon

4.00/5 (6投票s)

2003年8月1日

5分钟阅读

viewsIcon

112001

downloadIcon

961

Windows 的 C 时间例程的 64 位版本

1:43:38 PM Saturday, July 30, 2095 GMT Daylight Time

引言

C 语言的时间例程,例如 time()localtime() 等,用自 1970 年 1 月 1 日起的秒数来表示时间。变量类型 time_t 被定义为 32 位有符号整数——允许负数表示两个时间之间的秒差。

这意味着,如果您在 32 位构建中使用它们,那么通常,可以表示的最大时间是 2038 年 1 月 19 日星期二 03:14:07。这是 C 语言中的“千年虫”问题,但更容易解决。您只需要执行 64 位构建即可解决它。

但是,也许您想为 32 位操作系统执行 32 位构建。也许还想让您的程序在 2038 年之后无需重新构建也能正常运行,以防万一在那些日子里有人仍将其作为遗留应用程序使用。嗯——这个库可以解决这个问题。Windows 操作系统内部使用 64 位时间(以一种相当奇特的方式,即自 1601 年 1 月 1 日起的 100 纳秒间隔)。因此,只需正确地与这些时间进行接口,就可以制作 C 例程的 64 位版本,用于 Windows。

它适用于 MSVC 或支持 __int64 数据类型的任何编译器。如果您的编译器具有本地 64 位整数类型,但其名称有所不同,则需要相应地编辑源代码。

我是一名纯 C 语言程序员,没有使用过 CTime,但也许如果您包含这个库,CTime 也会按预期运行——或者可能需要更多工作。欢迎 C++ 程序员评论。有人有兴趣将此库转换为 C++ 吗?

Using the Code

将源代码 time64.ctime64.cpp(对于 C++ 程序)作为您程序的一部分包含进来。在每个使用时间例程的文件头部放置 time64.h 头文件。您所有的 time_t 变量现在都将是 __int64。但是,如果您需要在其他例程中使用它们,可以将其转换为 UINT,这样仍然有效直到本世纪末及以后。特别是,UINT 可用于文件输入和输出。

这就是它的全部内容。

一探究竟

下载的 zip 文件包含一个 MSVC 项目,用于制作一个微型应用程序,该程序仅显示当前时间。您可以测试它以了解其工作原理——但首先需要注意一些警告。如果您更改计算机上的日历,有时可能会引起一些问题。

一些具有 30 天试用期的程序,如果您更改系统时间,可能会表现异常——即使您将时间调回原样,它也可能告诉您试用期已结束——因此,在执行此操作时,请确保没有任何此类程序正在运行。

其他程序可能会在您超出 2038 年时崩溃。这是因为对于大多数时间例程的 Windows 实现,time_t 必须为非负数。如果您用负值调用它们,可能会遇到访问冲突。例如,请参阅 “日期晚于 2038 年 1 月 18 日时运行 Windows Media Player 7 或 7.1 时可能遇到的问题”

如果您更改日期,其他程序可能会表现异常。例如,最好关闭您的浏览器,以防其历史记录文件夹混乱。因此,在更改日历日期以测试此库之前,请关闭任何可能对日期敏感的程序。如有疑问,请使用 Ctrl + Alt + Delete 并关闭除 Explorer 之外的所有内容。

查看我的 Work Log 程序,其中有一个使用此库的应用程序示例。

关注点

此库使用 Windows 的 FILETIME 文件类型,它是 64 位,表示自 1601 年 1 月 1 日以来的 100 纳秒间隔的时间。因此,任务是为这种类型创建一个包装器,以便可以处理 64 位 time_t 变量。

有关 Windows 中各种时间例程的介绍,请参阅 CodeProject 页面 C++ 中的日期和时间

阅读代码时需要注意的主要一点是,FILETIME 不能直接使用,而必须先转换为 __int64 变量,然后才能对其进行算术运算。然后,Windows 中 tm 结构的等效项是 SYSTEMTIME。大多数字段相同,但月份从 1 开始而不是 0,并且 tm 结构中的年份表示自 1900 年以来的年数,因此您需要减去 1900 才能将 SYSTEMTIME 转换为 tm,反之亦然。

此外,要将 FILETIME 转换为 time_t,您需要减去 1601 年 1 月 1 日和 1970 年 1 月 1 日之间的秒数。幸运的是,我们可以通过创建一个表示 1970 年 1 月 1 日的 SYSTEMTIME 来轻松完成此操作,然后使用 SystemTimeToFileTime 例程来查找此日期以秒计的值。

time_t 的 64 位变量类型在库内部称为 t64。因此,这足以理解代码。这是核心部分——各种数据类型在哪里被相互转换。

#define SECS_TO_FT_MULT 10000000
static void T64ToFileTime(t64 *pt,FILETIME *pft)
{
 LARGE_INTEGER li;    
 *pt*=SECS_TO_FT_MULT;
 li.QuadPart=*pt;
 pft->dwLowDateTime=li.LowPart;
 pft->dwHighDateTime=li.HighPart;
}

static void FileTimeToT64(FILETIME *pft,t64 *pt)
{
 LARGE_INTEGER li;    
 li.LowPart = pft->dwLowDateTime;
 li.HighPart = pft->dwHighDateTime;
 *pt=li.QuadPart;
 *pt/=SECS_TO_FT_MULT;
}

static t64 FindTimeTBase(void)
{
 // Find 1st Jan 1970 as a FILETIME 
 t64 tbase;
 SYSTEMTIME st;
 FILETIME ft;
 memset(&st,0,sizeof(st));
 st.wYear=1970;
 st.wMonth=1;
 st.wDay=1;
 SystemTimeToFileTime(&st, &ft);
 FileTimeToT64(&ft,&tbase);
 return tbase;
}

static void SystemTimeToT64(SYSTEMTIME *pst,t64 *pt)
{
 FILETIME ft;
 SystemTimeToFileTime(pst, &ft);
 FileTimeToT64(&ft,pt);
 *pt-=FindTimeTBase();
}

static void T64ToSystemTime(t64 *pt,SYSTEMTIME *pst)
{
 FILETIME ft;
 t64 t=*pt;
 t+=FindTimeTBase();
 T64ToFileTime(&t,&ft);
 FileTimeToSystemTime(&ft,pst);
}

t64 time_64(t64 *pt)
{
 t64 t;
 SYSTEMTIME st;
 GetSystemTime(&st);
 SystemTimeToT64(&st,&t);
 return t;
}

然后,我们还需要执行上述转换,以从 SYSTEMTIME 转换到 tm,反之亦然。这很容易做到——如果您想了解更多信息,我将把它留给您查看 zip 文件中的文件。

历史

  • 2003 年 7 月 30 日:首次发布

许可证

本文未附加明确的许可证,但可能在文章文本或下载文件本身中包含使用条款。如有疑问,请通过下面的讨论区联系作者。

作者可能使用的许可证列表可以在此处找到。

© . All rights reserved.