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





4.00/5 (6投票s)
2003年8月1日
5分钟阅读

112001

961
Windows 的 C 时间例程的 64 位版本
引言
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.c 或 time64.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 日:首次发布
许可证
本文未附加明确的许可证,但可能在文章文本或下载文件本身中包含使用条款。如有疑问,请通过下面的讨论区联系作者。
作者可能使用的许可证列表可以在此处找到。