汇编语言中如何用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
相关产品推荐
相关产品推荐

