The current date and time under Linux is stored in the so-called timestamp. A timestamp
is the number of seconds that have elapsed since midnight on January 1, 1970 in
UTC
(GMT) time. The timestamp is
obtained using the sys_time system function (number 13), and a
new time can be set using the sys_stime system function (number 25).
If we don't want to use any libraries, then this format is inconvenient for use.
This is why I'm going to show here a method for converting a timestamp to traditional time
format and the other way round.
This article was written based on the source code of the GNU C library (glibc), strictly speaking - the glibc/time/offtime.c file.
remainderfrom now on
The EXTRA procedure has the following steps:
This big, complicated algorithm is shown in the following program (FASM syntax):
; Program calculates the current time based on the current timestamp. ; The program DOES NOT DISPLAY ANYTHING. ; ; Author: Bogdan D., bogdandr (at) op.pl ; ; assembly: ; fasm dataczas.fasm format ELF executable segment executable entry main SEK_NA_GODZ = (60 * 60) ; number of seconds in an hour SEK_NA_DZIEN = (SEK_NA_GODZ * 24) ; number of seconds in a day LETNI = 1 ; 0, if winter time, 1 if summer time PRZES_GMT = 1*SEK_NA_GODZ + LETNI*SEK_NA_GODZ ; offset from GMT main: mov eax, 13 xor ebx, ebx int 80h ; get the current time in seconds mov [czas], eax mov ebx, SEK_NA_DZIEN xor edx, edx idiv ebx ; number of seconds / number of seconds in a day = ; number of days add edx, PRZES_GMT ; add time zone ; if remaining number of seconds < 0, increase it by the number of ; seconds in a day, each time decreasing the number of days (EAX) spr_reszte: cmp edx, 0 jge reszta_ok add edx, SEK_NA_DZIEN sub eax, 1 jmp spr_reszte reszta_ok: ; if remaining number of seconds > number of seconds in a day, ; decrease it by the number of seconds in a day, each time increasing ; the number of days (EAX) spr_reszte2: cmp edx, SEK_NA_DZIEN jl reszta_ok2 sub edx, SEK_NA_DZIEN add eax, 1 jmp spr_reszte2 reszta_ok2: mov [l_dni], eax mov [reszta], edx mov eax, edx ; EAX = remainder mov ebx, SEK_NA_GODZ xor edx, edx idiv ebx ; EAX = hour, remainder - minutes+seconds mov [godz], al ; save the hour mov [reszta], edx ; and the new remainder mov eax, edx mov ecx, 60 xor edx, edx idiv ecx ; divide the new remainder by 60 mov [min], al ; quotients is the number of minutes mov [sek], dl ; remainder is the number of seconds ; find the weekday mov eax, [l_dni] add eax, 4 ; 1970-1-1 was a Thursday mov ebx, 7 xor edx, edx idiv ebx ; EAX = day of week cmp dl, 0 jge dzient_ok add dl, 7 ; add 7, if negative dzient_ok: mov [dzient], dl ; beginning of the loop in point 9 spr_dni: mov eax, [y] call czy_przest ; ECX = 0, if Y is leap. cmp dword [l_dni], 0 jl zmien_dni ; check if number of days < 0 mov esi, 365 test ecx, ecx jnz .przest_ok add esi, 1 ; add 1 day in a leap year .przest_ok: cmp [l_dni], esi jl koniec_spr_dni ; check if number of days >= 365/366 zmien_dni: mov esi, 365 mov eax, [l_dni] xor edx, edx idiv esi ; EAX = number of days/365 mov ecx, eax ; save to ECX cmp edx, 0 jge .edx_ok1 sub ecx, 1 ; if remainder < 0, subtract 1 .edx_ok1: add ecx, [y] ; ECX = number of days/365 + Y +1 or +0 mov [yg], ecx ; save into YG sub ecx, [y] imul ecx, ecx, 365 ; ECX = (YG-Y)*365 push ecx mov eax, [yg] sub eax, 1 call dodatek ; calculate the EXTRA on YG-1 and save the ; result in [przest] pop ecx add ecx, [przest] ; ECX = (YG-Y)*365 + EXTRA(YG-1) push ecx mov eax, [y] sub eax, 1 call dodatek ; calculate the EXTRA on Y-1 and save the ; result in [przest] pop ecx sub ecx, [przest] ; ECX=(YG-Y)*365 + EXTRA(YG-1) - EXTRA(Y-1) sub [l_dni], ecx ; subtract the whole from the number of days mov eax, [yg] mov [y], eax ; store YG into Y jmp spr_dni ; go to the beginning of the loop koniec_spr_dni: mov eax, [y] ;sub eax, 1900 mov [rok], ax ; save the calculated year call czy_przest ; ECX = 0, if leap mov eax, [l_dni] mov [dzienr], ax ; save the number of the day in the year ; check which month the day belongs to xor esi, esi ; assume non-leap year mov ebx, 2 ; start with the first month test ecx, ecx jnz .nie_przest add esi, 13*2 ; if leap, take the second group of numbers .nie_przest: ; look for the month. EAX = number of the day in the year cmp ax, [dni1+esi+ebx] ; compare the day number with the sum of ; days until the NEXT month jbe mies_juz ; stop if already less add ebx, 2 ; check the next month jmp .nie_przest mies_juz: ; to get the day number in a month, subtract the sum of all ;days in all PREVIOUS months (hence the -2) from the day number sub ax, [dni1+esi+ebx-2] inc al ; and add 1, so we don't count from zero mov [dzien], al ; save the day of month shr ebx, 1 ; divide the month number by 2, because there are 2 ; bytes per month mov [mies], bl ; and save the result mov eax, 1 xor ebx, ebx int 80h ; exit the program ; the EXTRA procedure. Year (the parameter) is given in EAX dodatek: push eax push ebx push ecx push edx push esi push edi mov esi, 4 mov edi, 100 mov ebx, 400 and eax, 0ffffh push eax xor edx, edx idiv esi ; divide EAX by 4 mov ecx, eax ; save the result cmp edx, 0 ; check the remainder jge .edx_ok1 sub ecx, 1 ; if remainder < 0, decrease the result by 1 .edx_ok1: pop eax push eax xor edx, edx idiv edi ; divide EAX by 100 sub ecx, eax ; subtract from the current result cmp edx, 0 ; check the remainder jge .edx_ok2 add ecx, 1 ; if remainder < 0, decrease the result by 1 .edx_ok2: pop eax xor edx, edx idiv ebx ; divide EAX by 400 add ecx, eax ; add to the current result cmp edx, 0 ; check the remainder jge .edx_ok3 sub ecx, 1 ; if remainder < 0, decrease the result by 1 .edx_ok3: mov [przest], ecx ; save the result pop edi pop esi pop edx pop ecx pop ebx pop eax ret ; returns 0 in ECX if the year given in EAX is leap, 1 - if it is not czy_przest: push eax push ebx push edx xor ecx, ecx push eax xor edx, edx mov ebx, 4 idiv ebx ; divide EAX by 4 pop eax test edx, edx jnz .nie_jest ; non-zero remainder means the year isn't evenly ; divisible, so it can't be leap ; here we know the year is evenly divisible by 4 push eax xor edx, edx mov ebx, 100 idiv ebx ; divide EAX by 100 pop eax test edx, edx jnz .jest ; non-zero remainder means the year isn't evenly ; divisible by 100, but is divisible by 4, so ; it is a leap year ; here we know the year is evenly divisible by 4 and 100 push eax xor edx, edx mov ebx, 400 idiv ebx ; divide EAX by 400 pop eax test edx, edx jz .jest ; zero remainder means the year is evenly ; divisible by 400, so it is leap .nie_jest: mov ecx, 1 .jest: pop edx pop ebx pop eax ret segment readable writeable l_dni dd 0 ; calculated number of days reszta dd 0 ; remainder from the divisions y dd 1970 ; starting Y value yg dd 0 ; YG variable przest dd 0 ; extra czas dd 0 ; timestamp rok dw 0 ; current year mies db 0 ; current month dzien db 0 ; current day of month dzient db 0 ; current day of week dzienr dw 0 ; current day of year godz db 0 ; current hour min db 0 ; current minute sek db 0 ; current second ; number of days preceding each month in a non-leap and leap years dni1 dw 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 dw 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366
This algorithm is much simpler. Namely:
Timestamp = SECONDS + MINUTE*60 + HOUR*60*60 + DAY_OF_YEAR*60*60*24 + YEARS_SINCE_1970*60*60*24*365
+ LEAP_YEARS_SINCE_1970*60*60*24
All you have to do is calculate, which day of the year is the current day (knowing the day of month, use the arrays in the above program and add the day number to the right number) and how many leap years were there since 1970 until the current year (according to the known rules; all you have to do is call the above czy_przest procedure for each year).
Just notice that the number of leap years shows the number of days, not years, to add.