; Biblioteka funkcji kalendarzowych napisana w asemblerze FASM. ; ; Możliwości: ; * funkcje obsługują odcinek czasu o długości ponad 11 milionów lat: ; - od 1.I.5843880 p.n.e. do 3.VIII.5915100 dla kalendarza ; juliańskiego, ; - od 30.XII.5844001 p.n.e. do 17.I.5915222 dla kalendarza ; gregoriańskiego, ; * wygodna konwersja dat pomiędzy kalendarzami juliańskim i gregoriańskim ; w obsługiwanym okresie czasu, ; * określanie dnia tygodnia odpowiadającego danej dacie, ; * obliczanie porządkowego numeru dnia w roku na podstawie danego numeru ; miesiąca i dnia w miesiącu, ; * określanie przestępności roku w danym kalendarzu, ; * obliczanie tzw. "absolutnego" numeru dnia odpowiadającego danej dacie, ; dzięki temu łatwo jest obliczać ilość dni które upłynęły pomiędzy ; dwiema wybranymi datami. ; ; (C) Mikołaj Hajduk, 16.06.2008. ; format PE GUI 4.0 DLL entry DllEntryPoint include 'win32a.inc' ; Definicje stałych używanych przez funkcje biblioteczne. ; C1 = 365 ; Ilość dni roku zwykłego. C4 = 4*C1 + 1 ; Ilość dni w cyklu czteroletnim (podstawowym cyklu kalendarza ; juliańskiego). C100 = 25*C4 - 1 ; Ilość dni w "normalnym" stuleciu kalendarza gregoriańskiego ; (tj. stuleciu kończącym się rokiem o długości 365 dni). C400 = 4*C100 + 1 ; Ilość dni w pełnym cyklu 400-letnim kalendarza gregoriańskiego. k = 30 J = 194796 ; Stałe J oraz G przechowują ilości pełnych lat odpowiednio kalendarzy G = 194800 ; juliańskiego i gregoriańskiego zawartych w odcinku czasu określonego ; "Wielkim Cyklem" T. section '.data' data readable writeable ; Tablica przechowująca długości miesięcy roku zwykłego i przestępnego. ; MonthLen db 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 db 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ; Tablica zawierająca wartości funkcji 'DaySum' dla wszystkich par ; (numer miesiąca, flaga przestępności roku). ; DaySum dw 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 dw 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 section '.code' code readable executable proc DllEntryPoint, hinstDLL, fdwReason, lpvReserved mov eax, TRUE ret endp ; DWORD DayOfWeek(DWORD Y, DWORD M, DWORD D, DWORD Gregorian) ; ; Funkcja określa dzień tygodnia odpowiadający danej dacie. Każdemu dniowi tygodnia przyporządkowany jest ; odpowiedni numer: 0 - niedziela, 1 - poniedziałek, 2 - wtorek, 3 - środa, 4 - czwartek, 5 - piątek, 6 - sobota. ; ; Parametry: ; Y - rok, ; M - miesiąc, ; D - dzień, ; Gregorian - rodzaj kalendarza (0 - juliański, 1 - gregoriański). ; ; Wartości zwracane: ; * 0, 1, ..., 6, jeśli data jest prawidłowa, ; * -1 dla nieprawidłowych danych. ; proc DayOfWeek, Y, M, D, Gregorian pushfd push ebx edx stdcall DateToAbsDayNum, [Y], [M], [D], [Gregorian] ; eax := N test eax, eax jz .Error mov ebx, 7 ; xor edx, edx ; add eax, 5 ; edx := (eax + 5) mod 7 = (N + 5) mod 7 adc edx, edx ; div ebx ; xchg eax, edx ; eax := edx jmp .End .Error: mov eax, -1 .End: pop edx ebx popfd ret endp ; DWORD IsLeapYear(DWORD Y, DWORD Gregorian) ; ; Funkcja określa przestępność danego roku w zależności od rodzaju kalendarza. ; ; Parametry: ; Y - rok, ; Gregorian - rodzaj kalendarza (0 - juliański, 1 - gregoriański). ; ; Wartości zwracane: ; * 1, jeśli rok Y jest przestępny, 0 - w przeciwnym wypadku, ; * -1 dla nieprawidłowych danych. ; proc IsLeapYear, Y, Gregorian pushfd push ebx edx .CheckParameters: test [Gregorian], -2 ; 0 <= Gregorian <= 1 jnz .Error ; .IsYNegative: mov eax, [Y] ; eax := Y test eax, eax jz .Error jns .CheckCalendar ; eax < 0 (Y < 0) ; inc eax ; eax := eax + 1 neg eax ; eax := -eax = -(Y + 1) = -Y - 1 = ; = |Y| - [Y < 0] = Y' .CheckCalendar: cmp [Gregorian], 0 je .mod4 .Gregorian: xor edx, edx ; eax := E(eax / 100) = E(Y' / 100) mov ebx, 100 ; edx := eax mod 100 = Y' mod 100 div ebx ; test edx, edx jz .mod4 mov eax, edx ; eax := edx = Y' mod 100 ; ; {(Y' mod 100) mod 4 = Y' mod 4} .mod4: shr eax, 1 ; eax := E(eax / 2); CF := eax mod 2 jc .Result ; shr eax, 1 ; eax := E(eax / 2); CF := eax mod 2 jmp .Result ; .Error: mov eax, -1 jmp .End .Result: setnc al ; eax := not CF movzx eax, al ; .End: pop edx ebx popfd ret endp ; DWORD MDToDayNum(DWORD M, DWORD D, DWORD LeapYearFlag) ; ; Funkcja oblicza numer porządkowy danego dnia w roku (z uwzględnieniem przestępności). ; ; Parametry: ; M - miesiąc, ; D - dzień, ; LeapYearFlag - flaga określająca przestępność roku (0 - rok normalny, 1 - rok przestępny). ; ; Wartości zwracane: ; * 1, 2, ..., 365 dla roku normalnego, 1, 2, ..., 366 dla roku przestępnego, ; * -1 dla nieprawidłowych danych. ; proc MDToDayNum, M, D, LeapYearFlag pushfd push ebx edx .LeapYearFlag: test [LeapYearFlag], -2 ; 0 <= LeapYearFlag <= 1 jnz .Error ; .Month: cmp [M], 1 ; jb .Error ; 1 <= M <= 12 cmp [M], 12 ; ja .Error ; .Day: cmp [D], 1 ; D >= 1 jb .Error ; mov ebx, [LeapYearFlag] ; ebx := LeapYearFlag lea ebx, [ebx + 2*ebx] ; ebx := 3*ebx = 3*LeapYearFlag shl ebx, 2 ; ebx := 4*ebx = 12*LeapYearFlag mov edx, [M] ; eax := MonthLen[M - 1 + 12*LeapYearFlag] movzx eax, [MonthLen - 1 + ebx + edx] ; cmp [D], eax ; D <= MonthLen[M - 1 + 12*LeapYearFlag] ja .Error ; .CalculateDayNum: shl ebx, 1 ; ebx := 2*ebx = 24*LeapYearFlag movzx eax, [DaySum - 2 + ebx + 2*edx] ; eax := DaySum(M, LeapYearFlag) add eax, [D] ; eax := eax + D = DaySum(M, LeapYearFlag) + D jmp .End .Error: mov eax, -1 .End: pop edx ebx popfd ret endp ; DWORD DayNumToMD(DWORD n, DWORD LeapYearFlag, DWORD* M, DWORD* D) ; ; Funkcja przekształca numer porządkowy danego dnia w roku w odpowiadające mu numery miesiąca ; i dnia w miesiącu. Rezultat w istotny sposób zależy od wartości flagi przestępności roku. ; ; Parametry: ; n - numer porządkowy dnia w roku, ; LeapYearFlag - flaga określająca przestępność roku (0 - rok normalny, 1 - rok przestępny), ; M - wskaźnik na zmienną, w której umieszczany jest obliczony numer miesiąca, ; D - wskaźnik na zmienną, w której umieszczany jest obliczony numer dnia. ; ; Wartości zwracane: ; * 0 dla prawidłowych danych (n, LeapYearFlag), ; * -1 w przeciwnym wypadku. ; proc DayNumToMD, n, LeapYearFlag, M, D pushfd push ebx ecx edx .CheckParameters: test [LeapYearFlag], -2 ; 0 <= LeapYearFlag <= 1 jnz .Error cmp [n], 1 ; n >= 1 jb .Error ; mov eax, 365 ; add eax, [LeapYearFlag] ; eax := 365 + LeapYearFlag cmp [n], eax ; n <= eax ja .Error ; .CalculateMD: mov ebx, [LeapYearFlag] ; ebx := LeapYearFlag lea ebx, [ebx + 2*ebx] ; ebx := 3*ebx = 3*LeapYearFlag shl ebx, 3 ; ebx := 8*ebx = 24*LeapYearFlag mov ecx, 12 ; ; .Loop: ; ecx := max{i; 1 <= i <= 12, DaySum(i, LeapYearFlag) < n} = m movzx edx, [DaySum - 2 + ebx + 2*ecx] ; cmp [n], edx ; edx := DaySum(m, LeapYearFlag) ja .LoopEnd ; loop .Loop ; .LoopEnd: mov eax, [M] ; M := ecx = m mov [eax], ecx ; mov ecx, [n] ; ecx := n sub ecx, edx ; ecx := ecx - edx = n - DaySum(m, LeapYearFlag) mov eax, [D] ; D := ecx mov [eax], ecx ; xor eax, eax jmp .End .Error: mov eax, -1 .End: pop edx ecx ebx popfd ret endp ; DWORD DateToAbsDayNum(DWORD Y, DWORD M, DWORD D, DWORD Gregorian) ; ; Funkcja oblicza absolutny numer dnia odpowiadający danej dacie. ; ; Parametry: ; Y - rok, ; M - miesiąc, ; D - dzień, ; Gregorian - rodzaj kalendarza (0 - juliański, 1 - gregoriański). ; ; Wartości zwracane: ; * 1, 2, ..., 2^32-1 dla prawidłowej daty danego kalendarza, ; * 0 dla nieprawidłowych danych. ; proc DateToAbsDayNum, Y, M, D, Gregorian pushfd push ebx ecx edx test [Gregorian], -2 ; 0 <= Gregorian <= 1 jnz .Error ; stdcall IsLeapYear, [Y], [Gregorian] ; cmp eax, -1 ; eax := IsLeapYear(Y, Gregorian) je .Error ; ; Y <> 0 mov ebx, eax ; ebx := eax stdcall MDToDayNum, [M], [D], ebx ; cmp eax, -1 ; eax := MDToDayNum(M, D, ebx) = n je .Error ; mov ecx, [Y] ; cmp ecx, 0 ; ecx := Y jg .CalculateDayNum ; inc ecx ; Y < 0 ; ecx := ecx + 1 = Y + 1 = Y + [Y < 0] .CalculateDayNum: add ecx, k*J ; cmp [Gregorian], 0 ; ecx := ecx + kJ + k(G-J)[Gregorian = 1] = je .Yprim0 ; = Y + [Y < 0] + kJ + k(G-J)[Gregorian = 1] = Y' add ecx, k*(G-J) ; .Yprim0: cmp ecx, 0 ; jne .YprimPositive ; Y' = 0 sub eax, 364 ; eax := eax - 364 = n - 364 jmp .End ; .YprimPositive: ; Y' > 0 ; dec ecx ; ecx := ecx - 1 = Y' - 1 mov ebx, eax ; ebx := eax = n mov eax, 365 ; eax := 365 mul ecx ; eax := 365 * ecx = 365(Y' - 1) shr ecx, 2 ; ecx := E(ecx / 4) = E((Y' - 1) / 4) add eax, ecx ; eax := eax + ecx = 365(Y' - 1) + E((Y' - 1) / 4) add eax, ebx ; eax := eax + ebx = eax + n = ; = 365(Y' - 1) + E((Y' - 1) / 4) + n cmp [Gregorian], 0 jz .End .Gregorian: push eax ; X := eax xor edx, edx ; mov eax, ecx ; eax := ecx = E((Y' - 1) / 4) mov ebx, 25 ; div ebx ; eax := E(eax / 25) = E(E((Y' - 1) / 4) / 25) = ; = E((Y' - 1) / 100) mov ecx, eax ; ecx := eax = E((Y' - 1) / 100) pop eax ; eax := X = 365(Y' - 1) + E((Y' - 1) / 4) + n sub eax, ecx ; eax := eax - ecx = 365(Y' - 1) + E((Y' - 1) / 4) + n - ; - E((Y' - 1) / 100) shr ecx, 2 ; ecx : = E(ecx / 4) = E(E((Y' - 1) / 100) / 4) = ; = E((Y' - 1) / 400) add eax, ecx ; eax := eax + ecx = 365(Y' - 1) + E((Y' - 1) / 4) + n - ; - E((Y' - 1) / 100) + E((Y' - 1) / 400) add eax, 2 ; eax := eax + 2 = 365(Y' - 1) + E((Y' - 1) / 4) + n - ; - E((Y' - 1) / 100) + E((Y' - 1) / 400) + 2 = ; = N jmp .End .Error: xor eax, eax .End: pop edx ecx ebx popfd ret endp ; DWORD AbsDayNumToDate(DWORD N, DWORD Gregorian, DWORD* Y, DWORD* M, DWORD* D) ; ; Funkcja przekształca absolutny numer dnia N = 1, 2, ..., 2^32-1 w odpowiadającą mu datę ; wybranego kalendarza. ; ; Parametry: ; N - absolutny numer dnia, ; Gregorian - rodzaj kalendarza (0 - juliański, 1 - gregoriański), ; Y - wskaźnik na zmienną, w której umieszczany jest obliczony numer roku, ; M - wskaźnik na zmienną, w której umieszczany jest obliczony numer miesiąca, ; D - wskaźnik na zmienną, w której umieszczany jest obliczony numer dnia. ; ; Wartości zwracane: ; * 0 dla prawidłowych danych (N, Gregorian), ; * -1 w przeciwnym wypadku. ; proc AbsDayNumToDate, N, Gregorian, Y, M, D pushfd push ebx ecx edx cmp [N], 0 ; N <> 0 je .Error ; test [Gregorian], -2 ; 0 <= Gregorian <= 1 jnz .Error ; xor ecx, ecx ; ecx := 0 mov eax, [N] ; eax := N - 1 dec eax ; cmp [Gregorian], 0 je .Julian .Gregorian: cmp eax, 1 ja .NextDays ; 0 <= eax <= 1 (1 <= N <= 2) mov ebx, [M] ; M := 12 mov dword [ebx], 12 ; add eax, 30 ; eax := eax + 30 = N - 1 + 30 = N + 29 mov ebx, [D] ; D := eax = N + 29 mov [ebx], eax ; mov ecx, -k*G - 1 ; ecx := -kG - 1 jmp .ReturnY .NextDays: ; eax > 1 (N > 2) sub eax, 2 ; eax := eax - 2 = N - 1 - 2 = N - 3 xor edx, edx ; mov ebx, C400 ; eax := E(eax / C400) = E((N - 3) / C400) div ebx ; edx := eax mod C400 = (N - 3) mod C400 lea eax, [eax + 4*eax] ; eax := 5*eax = 5*E((N - 3) / C400) lea eax, [eax + 4*eax] ; eax := 5*eax = 5*(5*E((N - 3) / C400)) = ; = 25*E((N - 3) / C400) shl eax, 4 ; eax := 16*eax = 16*(25*E((N - 3) / C400)) = ; = 400*E((N - 3) / C400) xchg ecx, eax ; ecx := eax = 400*E((N - 3) / C400) ; xchg eax, edx ; eax := edx = (N - 3) mod C400 ; .Centuries: ; cmp eax, C100 ; jb .Julian ; ; add ecx, 100 ; sub eax, C100 ; ; cmp eax, C100 ; (eax, ecx) := P(eax, ecx) = jb .Julian ; = P((N - 3) mod C400, 400*E((N - 3) / C400)) = ; = (N100, Y100) add ecx, 100 ; sub eax, C100 ; ; cmp eax, C100 ; jb .Julian ; ; add ecx, 100 ; sub eax, C100 ; .Julian: ; / ; | (N - 1, 0) ; Gregorian = 0 ; (N100, Y100) = (eax, ecx) = < ; | P((N - 3) mod C400, 400*E((N - 3) / C400)) ; Gregorian = 1 ; \ xor edx, edx ; mov ebx, C4 ; eax := E(eax / C4) = E(N100 / C4) div ebx ; edx := eax mod C4 = N100 mod C4 shl eax, 2 ; eax := 4*eax = 4*E(N100 / C4) add ecx, eax ; ecx := ecx + eax = Y100 + 4*E(N100 / C4) .Years: ; inc ecx ; cmp edx, C1 ; jb .MD ; ; sub edx, C1 ; ; inc ecx ; (edx, ecx) := Q(edx, ecx) = cmp edx, C1 ; = Q(N100 mod C4, Y100 + 4*E(N100 / C4)) = jb .MD ; = (N', Y*) ; sub edx, C1 ; ; inc ecx ; cmp edx, C1 ; jb .MD ; ; sub edx, C1 ; ; inc ecx ; .MD: inc edx ; edx := edx + 1 = N' + 1 stdcall IsLeapYear, ecx, [Gregorian] ; eax := IsLeapYear(ecx, Gregorian) = ; = IsLeapYear(Y*, Gregorian) stdcall DayNumToMD, edx, eax, [M], [D] ; eax := DayNumToMD(edx, eax, M, D) = ; = DayNumToMD(N' + 1, IsLeapYear(Y*, Gregorian), M, D) cmp [Gregorian], 0 je .JulianYears .GregorianYears: ; sub ecx, k*(G - J) ; ; ecx := ecx - kJ - k(G - J)[Gregorian = 1] = .JulianYears: ; = Y* - kJ - k(G - J)[Gregorian = 1] = sub ecx, k*J ; = Y' cmp ecx, 0 jg .ReturnY ; ecx <= 0 (Y' <= 0) dec ecx ; ecx := ecx - 1 = Y' - 1 = Y' - [Y' <= 0] .ReturnY: mov eax, [Y] ; Y := ecx mov [eax], ecx ; xor eax, eax jmp .End .Error: mov eax, -1 .End: pop edx ecx ebx popfd ret endp ; DWORD GregorianToJulian(DWORD Yg, DWORD Mg, DWORD Dg, DWORD* Yj, DWORD* Mj, DWORD* Dj) ; ; Funkcja przekształca daną datę kalendarza gregoriańskiego w odpowiadającą jej datę kalendarza juliańskiego. ; ; Parametry: ; Yg - rok daty gregoriańskiej, ; Mg - miesiąc daty gregoriańskiej, ; Dg - dzień daty gregoriańskiej, ; Yj - wskaźnik na zmienną, w której umieszczany jest numer roku daty juliańskiej, ; Mj - wskaźnik na zmienną, w której umieszczany jest numer miesiąca daty juliańskiej, ; Dj - wskaźnik na zmienną, w której umieszczany jest numer dnia daty juliańskiej. ; ; Wartości zwracane: ; * 0 dla prawidłowej daty gregoriańskiej, ; * -1 w przeciwnym wypadku. ; proc GregorianToJulian, Yg, Mg, Dg, Yj, Mj, Dj .GregorianToNum: stdcall DateToAbsDayNum, [Yg], [Mg], [Dg], 1 test eax, eax jz .Error .NumToJulian: stdcall AbsDayNumToDate, eax, 0, [Yj], [Mj], [Dj] jmp .End .Error: mov eax, -1 .End: ret endp ; DWORD JulianToGregorian(DWORD Yj, DWORD Mj, DWORD Dj, DWORD* Yg, DWORD* Mg, DWORD* Dg) ; ; Funkcja przekształca daną datę kalendarza juliańskiego w odpowiadającą jej datę kalendarza gregoriańskiego. ; ; Parametry: ; Yj - rok daty juliańskiej, ; Mj - miesiąc daty juliańskiej, ; Dj - dzień daty juliańskiej, ; Yg - wskaźnik na zmienną, w której umieszczany jest numer roku daty gregoriańskiej, ; Mg - wskaźnik na zmienną, w której umieszczany jest numer miesiąca daty gregoriańskiej, ; Dg - wskaźnik na zmienną, w której umieszczany jest numer dnia daty gregoriańskiej. ; ; Wartości zwracane: ; * 0 dla prawidłowej daty juliańskiej, ; * -1 w przeciwnym wypadku. ; proc JulianToGregorian, Yj, Mj, Dj, Yg, Mg, Dg .JulianToNum: stdcall DateToAbsDayNum, [Yj], [Mj], [Dj], 0 test eax, eax jz .Error .NumToGregorian: stdcall AbsDayNumToDate, eax, 1, [Yg], [Mg], [Dg] jmp .End .Error: mov eax, -1 .End: ret endp section '.edata' export data readable export 'Calendar.dll',\ AbsDayNumToDate, 'AbsDayNumToDate',\ DateToAbsDayNum, 'DateToAbsDayNum',\ DayNumToMD, 'DayNumToMD',\ DayOfWeek, 'DayOfWeek',\ GregorianToJulian, 'GregorianToJulian',\ IsLeapYear, 'IsLeapYear',\ JulianToGregorian, 'JulianToGregorian',\ MDToDayNum, 'MDToDayNum' section '.reloc' fixups data discardable