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.

(skip to the other way conversion)

- divide the timestamp by the number of seconds in a day (60*60*24). Save the quotient as the number of days and the remainder from this division
- increase the remainder by the time offset of your time zone from GMT, in seconds (in Central Europe, this is 60*60 in winter time, 2*60*60 in summer time)
- if the remainder is less than zero, increase it by the number of seconds per day until the remainder is positive, each time decreasing the number of days from step 1
- if the remainder is greater than the number of seconds in a day, decrease it by the number of seconds per day until the remainder is less than the number of seconds in a day, each time increasing the number of days from step one
- divide the remainder by the number of seconds in one hour. Save the quotient as the
calculated hour of the day, save the remainder in a variable called the
remainder

from now on - divide the remainder from the previous step by the number of seconds in a minute. Save the quotient as the number of minutes in the current hour and save the remainder as the number of seconds in the current minute
- increase the number of days by 4 (as January 1 1970 was a Thursday), and divide the result by 7. The remainder (add 7 if negative) from this division should be saved as the current weekday (0 means Sunday)
- put 1970 in the Y variable
- do the following in a loop:
- check if number of days is negative or grater than the number of days in year Y.
If neither is true, exit he loop.

This step requires the check if the year Y is a leap year (has 366 days). Each year with number divisible by 4 is leap except for those divisible by 100. Additionally, each year divisible by 400 is leap. - fill a new variable, YG, with the sum of Y and the quotient from dividing the number of days by 365. If the remainder is negative, decrease YG by 1.
- decrease the number of days by the difference between YG and Y multiplied by 365
- decrease the number of days by the result of the procedure EXTRA (shown later) called with a parameter of YG-1
- increase the number of days by the result of the procedure EXTRA (shown later) called with a parameter of Y-1
- put YG into Y YG

- check if number of days is negative or grater than the number of days in year Y.
If neither is true, exit he loop.
- put the current number of days in the day-of-year variable
- check, within which month is the current day and save that month. Decrease the number of days by the total number of days in the preceding months.
- put the current number of days increased by 1 in the day-of-month variable

The EXTRA procedure has the following steps:

- divide the given year (the parameter) by 4 and save the quotient. If the remainder was negative, decrease the quotient by 1
- divide the given year (the parameter) by 100 and save the quotient. If the remainder was negative, decrease the quotient by 1
- divide the given year (the parameter) by 400 and save the quotient. If the remainder was negative, decrease the quotient by 1
- subtract the second result from the first, then add the third result and return the obtained number as a result of the procedure

This big, complicated algorithm is shown in the following program (FASM syntax):

(skip the program)

; 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.