You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

汇编语言中如何用GetSystemTime获取正确本地时间(不使用GetLocalTime)

问题解决:用GetSystemTime手动转本地时间(不许用GetLocalTime)

为啥会慢3小时?

GetSystemTime返回的是UTC国际标准时间,你看到的时间比本地晚3小时,是因为你的本地时区是UTC+3(比如东欧、莫斯科时区)。要显示正确的本地时间,就得把UTC时间加上时区偏移量,同时还要处理时间溢出导致的日期、星期变更问题。

手动转本地时间的步骤

1. 获取时区偏移量

用GetTimeZoneInformation函数获取本地时区信息,结构体TIME_ZONE_INFORMATION里的Bias字段是UTC与本地时间的差值(单位:分钟),换算公式:
本地时间 = UTC时间 - Bias
(说白了Bias是负数,实际等价于 UTC + |Bias|÷60 小时)

2. 调整时间字段

把UTC的时、分、秒加上偏移量,处理进位/借位:

  • 先把总偏移拆成小时和分钟,分别加到对应字段
  • 秒超过59就减60,给分钟进位1;分钟超过59就减60,给小时进位1
  • 小时超过24就减24,日期加1;小时为负数就加24,日期减1

3. 调整日期与星期

  • 日期变更后,要校验是否符合当月天数(还要考虑闰年二月),不符合就调整月份、年份
  • 星期跟着日期走:日期加1星期就加1,日期减1星期就减1,循环模7(毕竟星期范围是0-6)

修改后的代码实现

先更新.inc文件(如果WINDOWS.inc已包含相关结构可跳过)

include WINDOWS.inc

include user32.inc
include kernel32.inc

includelib user32.lib
includelib kernel32.lib

.data
    Time_title  db ' Lab_2',0
    FORMAT_STRING  \
    db '    system time:',0dh,0ah,0dh,0ah
    db '                 Date:       %ld %s %ld року',0dh,0ah  ; 修复原乱码的"року"
    db '                 Time:       %ld : %ld : %ld',0dh,0ah
    db '                 Day of the week:    %s',0dh,0ah
    db 0

    Month_ \
    db  'грудня',4 dup(0),\ 
        'січеня',5 dup(0),\
        'лютого',4 dup(0),\
        'березня', 3 dup(0),\
        'квітня',4 dup(0),\
        'травня',4 dup(0),\
        'червня',4 dup(0),\  ; 修正原拼写错误"червяня"
        'липня',5 dup(0),\
        'серпня',4 dup(0),\
        'вересеня',3 dup(0),\
        'жовтня',4 dup(0),\
        'листопада',0  ; 修正原拼写错误"листопадв"
    
    Day_of_week \
    db  'Неділя',4 dup(0),\
        'Понеділок',0,\
        'Вівторок',2 dup(0),\
        'Середа',4 dup(0),\
        'Четвер',3 dup(0),\
        'П''ятниця',2 dup(0),\  ; 修正单引号转义方式
        'Субота',0

    ; 非闰年各月天数
    MonthDays db 31,28,31,30,31,30,31,31,30,31,30,31

.data?
    Save_esp dd ?
    tzi TIME_ZONE_INFORMATION <?>  ; 存储时区信息的结构体

修改后的.asm文件代码

.386
.model flat,STDCALL
option casemap :none  ;区分大小写
include Second.inc
include RADbg.inc

; 判断闰年的宏:能被4整除但不能被100整除,或能被400整除则为闰年
IS_LEAP_YEAR macro year
    xor edx,edx
    mov eax,year
    mov ecx,4
    div ecx
    test edx,edx
    jnz @F
    xor edx,edx
    mov eax,year
    mov ecx,100
    div ecx
    test edx,edx
    jz @@Check400
@@:
    mov eax,0
    jmp @@EndLeapCheck
@@Check400:
    xor edx,edx
    mov eax,year
    mov ecx,400
    div ecx
    test edx,edx
    jnz @@NotLeap
    mov eax,1
    jmp @@EndLeapCheck
@@NotLeap:
    mov eax,0
@@EndLeapCheck:
endm

.code
Begin:
    call main
    invoke ExitProcess,NULL

main proc
LOCAL Time:SYSTEMTIME
LOCAL Buf[256]:BYTE  ; 扩大缓冲区避免溢出
LOCAL totalMinutes:DWORD
LOCAL biasHours:DWORD
LOCAL biasMins:DWORD
LOCAL leap:DWORD
    mov Save_esp,esp

    ; 1. 获取UTC时间
    invoke GetSystemTime,addr Time
    ; 2. 获取时区偏移
    invoke GetTimeZoneInformation,addr tzi
    mov eax,tzi.Bias
    neg eax  ; 本地时间 = UTC - Bias → 等价于 UTC + (-Bias)
    mov totalMinutes,eax

    ; 将总偏移拆分为小时和分钟
    xor edx,edx
    mov eax,totalMinutes
    mov ecx,60
    div ecx
    mov biasHours,eax
    mov biasMins,edx

    ; 调整秒数
    mov ax,Time.wSecond
    add ax,word ptr biasMins
    cmp ax,60
    jl @NoSecCarry
    sub ax,60
    inc biasHours  ; 秒溢出,向分钟进位
@NoSecCarry:
    mov Time.wSecond,ax

    ; 调整分钟(兼容非整小时偏移的情况)
    mov ax,Time.wMinute
    add ax,word ptr biasMins
    cmp ax,60
    jl @NoMinCarry
    sub ax,60
    inc biasHours
@NoMinCarry:
    mov Time.wMinute,ax

    ; 调整小时,处理跨天情况
    mov ax,Time.wHour
    add ax,word ptr biasHours
@AdjustHour:
    cmp ax,24
    jl @CheckHourNegative
    sub ax,24
    inc Time.wDay  ; 小时超24,日期加1
    jmp @AdjustHour
@CheckHourNegative:
    cmp ax,0
    jge @HourOK
    add ax,24
    dec Time.wDay  ; 小时为负,日期减1
    jmp @AdjustHour
@HourOK:
    mov Time.wHour,ax

    ; 调整星期:日期变化后同步更新星期
    mov ax,Time.wDayOfWeek
    add ax,1  ; 日期加1则星期加1,日期减1则星期减1
    and ax,0x07  ; 模7保证星期在0-6范围内
    mov Time.wDayOfWeek,ax

    ; 调整日期、月份、年份,处理跨月/跨年情况
@AdjustDate:
    movzx eax,Time.wMonth
    dec eax  ; 将月份转为数组索引(从0开始)
    mov bl,MonthDays[eax]
    ; 判断是否为闰年二月,若是则天数加1
    cmp Time.wMonth,2
    jne @CheckDay
    IS_LEAP_YEAR Time.wYear
    mov leap,eax
    test eax,eax
    jz @CheckDay
    inc bl
@CheckDay:
    movzx ecx,Time.wDay
    cmp ecx,bl
    jle @DateOK
    ; 日期超过当月天数,月份加1,日期减去当月天数
    sub Time.wDay,word ptr bl
    inc Time.wMonth
    cmp Time.wMonth,13
    jne @AdjustDate
    ; 月份超过12,年份加1,月份重置为1
    mov Time.wMonth,1
    inc Time.wYear
    jmp @AdjustDate
@DateOK:
    ; 处理日期为0的情况(比如日期减1后变为0)
    cmp Time.wDay,0
    jg @DateFinal
    dec Time.wMonth
    cmp Time.wMonth,0
    jne @GetPrevMonthDays
    mov Time.wMonth,12
    dec Time.wYear
@GetPrevMonthDays:
    movzx eax,Time.wMonth
    dec eax
    mov bl,MonthDays[eax]
    ; 闰年二月判断
    cmp Time.wMonth,2
    jne @SetPrevDay
    IS_LEAP_YEAR Time.wYear
    test eax,eax
    jz @SetPrevDay
    inc bl
@SetPrevDay:
    mov Time.wDay,word ptr bl
    jmp @AdjustDate
@DateFinal:

    ; 原有的格式化输出逻辑保持不变
    xor eax,eax 
    mov ax,Time.wDayOfWeek
    mov bx,10
    mul bx  
    add eax,offset Day_of_week 
    push eax
    xor eax,eax 
    mov ax,Time.wSecond 
    push eax
    mov ax,Time.wMinute 
    push eax
    mov ax,Time.wHour   
    push eax
    mov ax,Time.wYear
    push eax
    mov ax,Time.wMonth
    mul bx
    add eax,offset Month_ 
    push eax
    xor eax,eax 
    mov ax,Time.wDay    
    push eax
    push offset FORMAT_STRING
    lea eax,Buf
    push eax
    call wsprintf
    mov esp, Save_esp
    invoke MessageBox,NULL,addr Buf,addr Time_title,MB_OKCANCEL
    ret
main endp   
end Begin

额外说明

  • 如果你的时区支持夏令时,GetTimeZoneInformation会返回TIME_ZONE_ID_DAYLIGHT,此时需要额外加上DaylightBias的值,上面的代码仅处理了基础时区偏移,需要的话可添加判断逻辑。
  • 闰年判断宏遵循国际标准规则,能正确识别所有闰年情况。
  • 日期调整逻辑覆盖了跨月、跨年、日期为0等边界情况,确保最终日期合法。

内容的提问来源于stack exchange,提问作者Sollpix

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 18:39:40