One of the many drawbacks of Windows is the huge zoo of time formats that application programmers have to deal with. As a result, a seemingly ordinary task turns into a non-trivial one, with the search for one of the many APIs. This article examines 7 types of time known to me, although the material does not pretend to be complete.
Win32API returns time in one of several formats. When necessary, we can use special functions to convert from one format to another, for example for comparison, or display on output devices (window, console, printer) . The following table summarizes the main ones:
1. System time is the current date/time of day according to Greenwich UTC. It is returned by the GetSystemTime() function in the SYSTEMTIME structure , which contains 2 bytes in a format convenient for output: year, month, day of the week, day, hour, minute, second and millisecond. System time can be set by calling SetSystemTime() . Several options for designating the same system time create confusion, so let's put everything in its place.
Today, world time is regulated by the UTC standard issued in 1972. This time is synchronized with the movement of electrons in the Cesium atom, and is accurate to a thousandth of a second (millisec = 1\1000) . When such accuracy is not required, an alternative to UTC is Greenwich Mean Time (GMT) , which uses the Earth's rotation period of 24 hours as a starting point. It is worth noting that UTC acts as a reference clock, and no adjustments for winter/summer time are applied to it.
2. Local time is the date and time for the current time zone, which we see in the right/lower corner of the Windows taskbar. GetLocalTime() under the hood takes the value from GetSystemTime() and converts it to local time based on the system time zone settings. Corrections can be requested using the GetTimeZoneInformation() structure of the same name as input function - it expects a TIME_ZONE_INFORMATION . SetLocalTime() is provided to set local time .
The table below lists the world's major cities relative to GMT(0). Since the format was invented at the University of Greenwich in England, of course they chose zero as their starting point. But given that the Earth is round and time zones are divided into vertical blocks on the world map, GMT(0) could have been assigned to anyone:
An example of calling functions to read system and local time might look like this:
3. Windows time is the number of milliseconds that have passed since the last system startup. In essence, it is the operating time of the Windows OS itself, and therefore is not related to UTC date/time. On 32-bit systems, the GetTickCount() function returned milliseconds to the register EAX, and accordingly the max. value could not exceed 0xFFFFFFFF= 4.294.967.295timer ticks. Then, using simple arithmetic, you can calculate the approximate operating time after which the counter overflowed:
4294967295 /1000 = 4294967 сек /60 = 71582 минут /60 = 1193 часов /24 = ~49 дней.
But with the advent of the x64 architecture, the bit depth EAXhas doubled, and the GetTickCount64() function now returns RAXa counter with an astronomical value - it will last for 585 million years of trouble-free operation of the system:
18446744073709551615 /1000 = 18446744073709551 сек /60 = 307445734561825 мин
307445734561825 мин /60 = 5124095576030 часов /24 = 213503982334 дней /365 = 584.942.417 лет.
functions The GetTickCount() count ticks of the system timer, the resolution of which is ~16 milliseconds.
The output shows the total operating time of the OS, including sleep mode and hibernation .
4. Interrupt time – similar to Windows time, only the output is not in milliseconds, but in 100 nanosecond blocks (1 nanosecond = 0.000000001 sec) . To get the time in seconds, you will need to divide the result by 10 million. The countdown starts from zero when the OS starts, and increases with each interruption of the clock, by the length of its ticks. The exact value of 1 tick depends on the current settings in the BIOS of the hardware (see ACPI timer) , and therefore may differ on different systems.
functions are provided for reading interrupt ticks The QueryInterruptTime() and QueryUnbiasedInterruptTime() . The unbiased "Unbiased" version returns the time when the system was in the working state, without taking into account sleep mode and hibernation . Win10 brought 2 more versions of these functions with the "Precise" suffixes for more accurate interval measurements: QueryInterruptTimePrecise() , and QueryUnbiasedInterruptTimePrecise() . They differ in that if the first ones read data from the already prepared system structure KUSER_SHARED_DATA (displayed in the user space) , then the extended versions directly from the ports of physical equipment.
5. File time is a 64-bit value representing the number of 100 nanosecond intervals that have elapsed since 01/01/1601 according to the Gregorian calendar . This type of time is the responsibility of the file system, which updates it at the time of creation, recording, and attempts to access files. NTFS stores time in UTC format without adjustments for the time zone, while FAT32 uses the local time of the computer. All file functions return it in an 8-byte FILETIME structure .
specially designed for these purposes, GetFileTime(), structures at once retrieves the file time by an open descriptor into three FILETIME , separately for the creation, recording, and access times (see the out_xx arguments below) . But this is not the only API of its kind. For example, FindFirst\NextFile() structure returns much more information about the file in its WIN32_FIND_DATA , including the three specified file time options. If we want to output it to the console or to the GUI window, we need to convert it first to the system time using the FileTimeToSystemTime() works in the opposite direction function. GetSystemTimeAsFileTime() , which automatically reads the current system time and converts it to the file time format.
6. MS-DOS date and time – is a packed 32-bit value (4 bytes) . The high 16 bits encode the date, and the low 16 bits encode the time. As in the previous case, control over this type of time lies on the shoulders of the file system – FAT updates it at the moment when a file is created or modified. MS-DOS applications retrieve this date and time using the function AH=2Ahinterruptions int-21h. As for the Win protected mode, when working with DOS files, the GetFileTime() function automatically converts the MS-DOS date and time to the UTC time format. Moreover, in clinical cases, you can call for help the DosDateTimeToFileTime() function , which will reset the Neolithic time to the FILETIME structure . For the reverse conversion, there is also FileTimeToDosDateTime() . In general, there is plenty to choose from.
7. UNIX date and time – in documentation and various utilities for collecting information, this time is known as TimeDateStamp . A very interesting character, and therefore we will pay a little more attention to it, unlike programmers from Microsoft, who decided not to wrap the problem in Win32API. As a result, all manipulations with this type of time have to be implemented manually.
The thing is that the format is used in the header of PE files, where a special field is allocated for it. As can be seen from the screenshot below, the file creation time is encoded by a 32-bit DWORD value, and since the reference point is 01.01.1970 (the so-called Unix era) , then the field overflow will be obtained already at the beginning of 2038.
The time inside the PE file header is not related to the NTFS file time - it is written by the compiler when assembling EXE\DLL\SYS. For example, an executable file can actually be created in 2000, and copied over the disk file system much later in 2023. So, the GetFileTime() function will return the date\time exactly as 2023.
Moreover, the compiler writes the creation time stamp not only in the PE file header. A separate TimeDataStamp field is also present in the headers of literally all other sections of the executable, for example: export, in IAT\Delay\Bound imports, resources, debugging, security, and others. Such a zoo allows finding infected files on the disk, since the malware created by students does not pay attention to numerous fields with a time stamp inside the binary. Ideally, the time in the headers of all sections should match, otherwise the executable file should arouse suspicion.
It was mentioned above that the standard Win package does not have library functions for converting Unix time to system UTC. However, there is a special formula by which you can manually get the desired result in the FILETIME structure . Then you just need to call FileTimeToSystemTime() and output the finished time to the console\windows. The formula itself looks like this:
FileTime = (UnixTime * 10000000) + 116444736000000000
8. Afterword
Time plays a huge role in the system, from the primitive organization of delays to the performance of the OS as a whole. If desired, you can slow down or speed up the clock, which will entail global consequences. Therefore, you need to be careful with API functions with the Set_xx() prefixes . By the way, many NIC network adapters can generate TimeDataStamp marks on the spot, directly in their equipment, reading the ticks of the network controller hardware clock (without accessing the system clock on the motherboard) . NIC uses such stamps, for example, to calculate the time for which a packet was stuck in the network stack before sending / receiving it.
Win32API returns time in one of several formats. When necessary, we can use special functions to convert from one format to another, for example for comparison, or display on output devices (window, console, printer) . The following table summarizes the main ones:
1. System time is the current date/time of day according to Greenwich UTC. It is returned by the GetSystemTime() function in the SYSTEMTIME structure , which contains 2 bytes in a format convenient for output: year, month, day of the week, day, hour, minute, second and millisecond. System time can be set by calling SetSystemTime() . Several options for designating the same system time create confusion, so let's put everything in its place.
Today, world time is regulated by the UTC standard issued in 1972. This time is synchronized with the movement of electrons in the Cesium atom, and is accurate to a thousandth of a second (millisec = 1\1000) . When such accuracy is not required, an alternative to UTC is Greenwich Mean Time (GMT) , which uses the Earth's rotation period of 24 hours as a starting point. It is worth noting that UTC acts as a reference clock, and no adjustments for winter/summer time are applied to it.
- UTC: Universal Time Coordinated – всемирный стандарт времени.
- GMT: Greenwich Mean Time – подразумевает часовой пояс на планете Земля.
- STD: Standart Time – локальное время в дефолте.
- DST: Daylight Saving Time – локальное время, с поправкой на летнее.
2. Local time is the date and time for the current time zone, which we see in the right/lower corner of the Windows taskbar. GetLocalTime() under the hood takes the value from GetSystemTime() and converts it to local time based on the system time zone settings. Corrections can be requested using the GetTimeZoneInformation() structure of the same name as input function - it expects a TIME_ZONE_INFORMATION . SetLocalTime() is provided to set local time .
The table below lists the world's major cities relative to GMT(0). Since the format was invented at the University of Greenwich in England, of course they chose zero as their starting point. But given that the Earth is round and time zones are divided into vertical blocks on the world map, GMT(0) could have been assigned to anyone:
C-like:
invoke GetSystemTime, out_lpSYSTEMTIME
invoke GetLocalTime, out_lpSYSTEMTIME
struct SYSTEMTIME
wYear dw 0
wMonth dw 0
wDayOfWeek dw 0
wDay dw 0
wHour dw 0
wMinute dw 0
wSecond dw 0
wMilliseconds dw 0
ends
;//---------------------------
invoke GetTimeZoneInformation, out_TIME_ZONE_INFORMATION
struct TIME_ZONE_INFORMATION
Bias dd 0 ;// значение поправки в минутах (UTC = LocalTime + Bias)
StandardName dw 32 dup(0) ;// Unicode-имя текущего часового пояса
StandardDate SYSTEMTIME ;// дата перехода с летнего на зимнее время
StandardBias dd 0 ;// 0
DaylightName dw 32 dup(0) ;// Unicode-имя летнего времени
DaylightDate SYSTEMTIME ;// дата перехода с зимнего на летнее время
DaylightBias dd 0 ;// 0
ends
An example of calling functions to read system and local time might look like this:
C-like:
;//----- Прочитаем системное и локальное время ----------------
invoke GetSystemTime,sTime
invoke GetLocalTime, lTime
cinvoke printf,<10,' System time: ',0>
mov rsi,sTime
call PrintDateTime
cinvoke printf,<10,' Local time: ',0>
mov rsi,lTime
call PrintDateTime
;//----- Инфа о часовом поясе ---------------------------------
invoke GetTimeZoneInformation,tzInfo
mov eax,[tzInfo.Bias]
or eax,eax
jns @f
mov byte[tZone+24],'+'
neg eax
@@: mov ecx,60
xor edx,edx
div ecx
push rax
lea rbx,[tzInfo.DaylightName]
invoke CharToOemW,rbx,buff
pop rax
cinvoke printf,tZone,rax,buff
3. Windows time is the number of milliseconds that have passed since the last system startup. In essence, it is the operating time of the Windows OS itself, and therefore is not related to UTC date/time. On 32-bit systems, the GetTickCount() function returned milliseconds to the register EAX, and accordingly the max. value could not exceed 0xFFFFFFFF= 4.294.967.295timer ticks. Then, using simple arithmetic, you can calculate the approximate operating time after which the counter overflowed:
4294967295 /1000 = 4294967 сек /60 = 71582 минут /60 = 1193 часов /24 = ~49 дней.
But with the advent of the x64 architecture, the bit depth EAXhas doubled, and the GetTickCount64() function now returns RAXa counter with an astronomical value - it will last for 585 million years of trouble-free operation of the system:
18446744073709551615 /1000 = 18446744073709551 сек /60 = 307445734561825 мин
307445734561825 мин /60 = 5124095576030 часов /24 = 213503982334 дней /365 = 584.942.417 лет.
functions The GetTickCount() count ticks of the system timer, the resolution of which is ~16 milliseconds.
The output shows the total operating time of the OS, including sleep mode and hibernation .
4. Interrupt time – similar to Windows time, only the output is not in milliseconds, but in 100 nanosecond blocks (1 nanosecond = 0.000000001 sec) . To get the time in seconds, you will need to divide the result by 10 million. The countdown starts from zero when the OS starts, and increases with each interruption of the clock, by the length of its ticks. The exact value of 1 tick depends on the current settings in the BIOS of the hardware (see ACPI timer) , and therefore may differ on different systems.
functions are provided for reading interrupt ticks The QueryInterruptTime() and QueryUnbiasedInterruptTime() . The unbiased "Unbiased" version returns the time when the system was in the working state, without taking into account sleep mode and hibernation . Win10 brought 2 more versions of these functions with the "Precise" suffixes for more accurate interval measurements: QueryInterruptTimePrecise() , and QueryUnbiasedInterruptTimePrecise() . They differ in that if the first ones read data from the already prepared system structure KUSER_SHARED_DATA (displayed in the user space) , then the extended versions directly from the ports of physical equipment.
C-like:
;//----- Время работы Windows по тикам ------------------------
invoke GetTickCount64
push rax
mov rbx,1000
xor rdx,rdx
div rbx ;// в секундах
mov rbx,60
xor rdx,rdx
div rbx ;// в минутах
xor rdx,rdx
div rbx ;// в часах
mov r10,rdx
pop rbx
@@: cinvoke printf,<10,\
10,' TickCount64: %u',\
10,' Windows time: %d hour, %02d minutes',10,0>,rbx,rax,r10
;//----- Время работы Windows по прерываниям ------------------
invoke QueryUnbiasedInterruptTime,intTime
mov rax,[intTime]
mov ebx,10000000
xor edx,edx
div rbx
mov ebx,60
xor edx,edx
div rbx
xor edx,edx
div rbx
xchg rbx,rdx
cinvoke printf,<10,' Interrupt ticks: %I64d',\
10,' Unibased time: %d hour, %02d minutes',0>,[intTime],rax,rbx
5. File time is a 64-bit value representing the number of 100 nanosecond intervals that have elapsed since 01/01/1601 according to the Gregorian calendar . This type of time is the responsibility of the file system, which updates it at the time of creation, recording, and attempts to access files. NTFS stores time in UTC format without adjustments for the time zone, while FAT32 uses the local time of the computer. All file functions return it in an 8-byte FILETIME structure .
specially designed for these purposes, GetFileTime(), structures at once retrieves the file time by an open descriptor into three FILETIME , separately for the creation, recording, and access times (see the out_xx arguments below) . But this is not the only API of its kind. For example, FindFirst\NextFile() structure returns much more information about the file in its WIN32_FIND_DATA , including the three specified file time options. If we want to output it to the console or to the GUI window, we need to convert it first to the system time using the FileTimeToSystemTime() works in the opposite direction function. GetSystemTimeAsFileTime() , which automatically reads the current system time and converts it to the file time format.
C-like:
invoke GetFileTime, in_hFile, out_lpCreate, out_lpAccess, out_lpWrite
struct FILETIME
dwLowDateTime dd 0
dwHighDateTime dd 0
ends
;//---------------------------------------
invoke FindFirstFile,<'*.exe',0>, WIN32_FIND_DATA
struct WIN32_FIND_DATA
dwFileAttributes dd 0
ftCreationTime FILETIME
ftLastAccessTime FILETIME
ftLastWriteTime FILETIME
nFileSize dq 0
dwReserved dq 0
cFileName db 256 dup(0)
cAlternateFileName db 14 dup(0)
ends
invoke FileTimeToSystemTime, in_lpFileTime, out_lpSystemTime
invoke GetSystemTimeAsFileTime, out_lpFILETIME
6. MS-DOS date and time – is a packed 32-bit value (4 bytes) . The high 16 bits encode the date, and the low 16 bits encode the time. As in the previous case, control over this type of time lies on the shoulders of the file system – FAT updates it at the moment when a file is created or modified. MS-DOS applications retrieve this date and time using the function AH=2Ahinterruptions int-21h. As for the Win protected mode, when working with DOS files, the GetFileTime() function automatically converts the MS-DOS date and time to the UTC time format. Moreover, in clinical cases, you can call for help the DosDateTimeToFileTime() function , which will reset the Neolithic time to the FILETIME structure . For the reverse conversion, there is also FileTimeToDosDateTime() . In general, there is plenty to choose from.
7. UNIX date and time – in documentation and various utilities for collecting information, this time is known as TimeDateStamp . A very interesting character, and therefore we will pay a little more attention to it, unlike programmers from Microsoft, who decided not to wrap the problem in Win32API. As a result, all manipulations with this type of time have to be implemented manually.
The thing is that the format is used in the header of PE files, where a special field is allocated for it. As can be seen from the screenshot below, the file creation time is encoded by a 32-bit DWORD value, and since the reference point is 01.01.1970 (the so-called Unix era) , then the field overflow will be obtained already at the beginning of 2038.
The time inside the PE file header is not related to the NTFS file time - it is written by the compiler when assembling EXE\DLL\SYS. For example, an executable file can actually be created in 2000, and copied over the disk file system much later in 2023. So, the GetFileTime() function will return the date\time exactly as 2023.
Moreover, the compiler writes the creation time stamp not only in the PE file header. A separate TimeDataStamp field is also present in the headers of literally all other sections of the executable, for example: export, in IAT\Delay\Bound imports, resources, debugging, security, and others. Such a zoo allows finding infected files on the disk, since the malware created by students does not pay attention to numerous fields with a time stamp inside the binary. Ideally, the time in the headers of all sections should match, otherwise the executable file should arouse suspicion.
It was mentioned above that the standard Win package does not have library functions for converting Unix time to system UTC. However, there is a special formula by which you can manually get the desired result in the FILETIME structure . Then you just need to call FileTimeToSystemTime() and output the finished time to the console\windows. The formula itself looks like this:
FileTime = (UnixTime * 10000000) + 116444736000000000
8. Afterword
Time plays a huge role in the system, from the primitive organization of delays to the performance of the OS as a whole. If desired, you can slow down or speed up the clock, which will entail global consequences. Therefore, you need to be careful with API functions with the Set_xx() prefixes . By the way, many NIC network adapters can generate TimeDataStamp marks on the spot, directly in their equipment, reading the ticks of the network controller hardware clock (without accessing the system clock on the motherboard) . NIC uses such stamps, for example, to calculate the time for which a packet was stuck in the network stack before sending / receiving it.