汇编语言Win32时间与日期函数

Win32 API 有相当多的时间和日期函数可供选择。最常见的是,用户想要用这些函数来获得和设置当前日期与时间。这里只能讨论这些函数的一小部分,不过在 Platform SDK 文档中可以查阅到下表列出的 Win32 函数。

函数 说明
CompareFileTime 比较两个 64 位的文件时间
DosDateTimeToFileTime 把 MS-DOS 日期和时间值转换为一个 64 位的文件时间
FileTimeToDosDateTime 把 64 位文件时间转换为 MS-DOS 日期和时间值
FileTimeToLocalFileTime 把 UTC(通用协调时间)文件时间转换为本地文件时间
FileTimeToSystemTime  把 64 位文件时间转换为系统时间格式
GetFileTime 检索文件创建、最后访问和最后修改的日期与时间
GetLocalTime 检索当前本地日期和时间
GetSystemTime 以 UTC 格式检索当前系统日期和时间
GetSystemTimeAdjustment  决定系统是否对其日历钟进行周期性时间调整
GetSystemTimeAsFileTime 以 UTC 格式检索当前系统日期和时间
GetTickCount 检索自系统启动后经过的毫秒数
GetTimeZoneInformation 检索当前时区参数
LocalFileTimeToFileTime 把本地文件时间转换为基于 UTC 的文件时间
SetFileTime 设置文件创建、最后访问和最后修改的日期与时间
SetLocalTime 设置当前本地时间与日期
SetSystemTime 设置当前系统时间与日期
SetSystemTimeAdjustment 启用或禁用对系统日历钟进行周期性时间调整
SetTimeZoneInformation 设置当前时区参数
SystemTimeToFileTime 把系统时间转换为文件时间
SystemTimeToTzSpecificLocalTime 把 UTC 时间转换为指定时区对应的本地时间

SYSTEMTIME 结构

SYSTEMTIME 结构由 Windows API 的日期和时间函数使用:

SYSTEMTIME STRUCT
    wYear WORD ?                   ;年(4 个数子)
    wMonth WORD ?               ;月(1 ~ 12)
    wDayOfWeek WORD ?      ;星期(0 ~ 6)
    wDay WORD ?                   ;日(1 ~ 31)
    wHour WORD ?                 ;小时(0 ~ 23)
    wMinute WORD ?             ;分钟(0 ~ 59)
    wSecond WORD ?             ;秒(0 ~ 59)
    wMilliseconds WORD ?     ;毫秒(0 ~ 999)
SYSTEMTIME ENDS

字段 wDayOfWeek 的值依序为星期天 = 0,星期一 = 1,以此类推。wMilliseconds 中的值不确定,因为系统可以与时钟源同步周期性地刷新时间。

GetLocalTime 和 SetLocalTime

函数 GetLocalTime 根据系统时钟返回日期和当前时间。时间要调整为本地时区。调用该函数时,需向其传递一个指针指向 SYSTEMTIME 结构:

GetLocalTime PROTO,
    lpSystemTime:PTR SYSTEMTIME

函数 GetLocalTime 调用示例如下:
.data
sysTime SYSTEMTIME <>
.code
INVOKE GetLocalTime, ADDR sysTime
函数 SetLocalTime 设置系统的本地日期和时间。调用时,需向其传递一个指针指向包含了期望日期和时间的 SYSTEMTIME 结构:

SetLocalTime PROTO,
    lpSystemTime:PTR SYSTEMTIME

如果函数执行成功,则返回非零整数;如果失败,则返回零。

GetTickCount 函数

函数 GetTickCount 返回从系统启动起经过的毫秒数:

GetTickCount PROTO               ; EAX 为返回值

由于返回值为一个双字,因此当系统连续运行 49.7 天后,时间将会回绕归零。可以使用这个函数监视循环经过的时间,并在达到时间限制时终止循环。

下面的程序 Timer.asm 计算两次调用 GetTickCount 之间的时间间隔。程序尝试确定计时器没有回绕(超过 49.7 天)。相似的代码可以用于各种程序:
;计算经过的时间    (Timer.asm)
;用Win32函数GetTickCount演示一个简单的秒表计时器。
INCLUDE Irvine32.inc
INCLUDE macros.inc

.data
startTime DWORD ?

.code
main PROC
    INVOKE GetTickCount         ; 获取开始时间计数
    mov    startTime,eax        ; 保存开始时间计数

; Create a useless calculation loop.
    mov    ecx,10000100h
L1:    imul    ebx
    imul    ebx
    imul    ebx
    loop    L1

    INVOKE GetTickCount         ; 获得新的时间计数
    cmp    eax,startTime        ; 比开始时间计数小
    jb    error                 ; 时间回绕
   
    sub    eax,startTime        ; 计算时间间隔
    call    WriteDec            ; 显示时间间隔
    mWrite <" milliseconds have elapsed",0dh,0ah>
    jmp    quit

error:
    mWrite "Error: GetTickCount invalid--system has "
    mWrite <"been active for more than 49.7 days",0dh,0ah>
quit:
    exit
main ENDP

END main

Sleep 函数

有些时候程序需要暂停或延迟一小段时间。虽然可以通过构造一个计算循环或忙循环来保持处理器工作,但是不同的处理器会使得执行时间不同。另外,忙循环还不必要地占用了处理器,这会降低在同一时间执行程序的速度。

Win32 函数 Sleep 按照指定毫秒数暂停当前执行的线程:

Sleep PROTO,
    dwMilliseconds:DWORD

由于本教程中汇编语言程序是单线程的,因此假设一个线程就等同于一个程序。当线程休眠时,它不会消耗处理器时间。

GetDateTime 过程

Irvine32 链接库中的过程 GetDateTime 以 100 纳秒为间隔,返回从 1601 年 1 月 1 日起经过的时间间隔数。这看起来有点奇怪,因为那个时候计算机还是未知的。对任何事件,Microsoft 都用这个值来跟踪文件日期和时间。

如果想要为日期计算准备系统日期/时间值,Win32 SDK 建议采用如下步骤:
  • 调用函数,如 GetLocalTime,填充 SYSTEMTIME 结构。
  • 调用函数 SystemTimeToFileTime,将 SYSTEMTIME 结构转换为 FILETIME 结构。
  • 将得到的 FILETIME 结构复制到 64 位的四字。

FILETIME 结构把 64 位四字分割为两个双字:

FILETIME STRUCT
    loDateTime DWORD ?
    hiDateTime DWORD ?
FILETIME ENDS

下面的 GetDateTime 过程接收一个指针,指向 64 位四字变量。它用 Win32 FILETIME 格式将当前日期和时间保存到变量中:
;--------------------------------------------------
GetDateTime PROC,
    pDateTime:PTR QWORD
    LOCAL sysTime:SYSTEMTIME, flTime:FILETIME
;
; 以64位整数形式 ( 按 Win32 FILETIME 格式 ) 获得并保存当前本地时间/日期
;--------------------------------------------------
; 获得系统本地时间
    INVOKE GetLocalTime,
      ADDR sysTime

; SYSTEMTIME 转换为 FILETIME.
    INVOKE SystemTimeToFileTime,
      ADDR sysTime,
      ADDR flTime

; 把 FILETIME 复制到一个64位整数
    mov esi,pDateTime
    mov eax,flTime.loDateTime
    mov DWORD PTR [esi],eax
    mov eax,flTime.hiDateTime
    mov DWORD PTR [esi+4],eax
    ret
GetDateTime ENDP