This means it treats all dates as if the Gregorian calendar always has existed and always will exist.
I tried to replace most division instructions with a multiply/shift combination to speed things up a bit.
It's not a replacement for the procedures I posted in this other thread
http://www.purebasic.fr/english/viewtop ... 12&t=56031
Mainly because the code is slower and I don't plan to built any procedures around it.
If someone else wants to do that or convert it into a module, feel free to do so.
The code contains no dependancies or lookup tables and should be cross platform compatible.
Code: Select all
; >> Proleptic Gregorian calendar <<
; Years are numbered according to astronomical year numbering
; which means the year 1 is preceded by the year 0.
; So the year 0 is the same as 1 BC, the year -1 as 2 BC etc.
; Supported date range :
; 08:29:52 UTC, January 27, 292,277,022,658 BC (-292,277,022,657)
; 15:30:07 UTC, December 4, 292,277,026,596 AD
; Last modified : August 18, 2017
Structure DateComponents ; (16 bytes, don't alter!)
year.q ; offset 0 : year
yday.w ; offset 8 : day of year [1-366]
month.b ; offset 10 : month [1-12]
day.b ; offset 11 : day [1-31]
wday.b ; offset 12 : days since Sunday [0-6]
hour.b ; offset 13 : hours [0-23]
min.b ; offset 14 : minute [0-59]
sec.b ; offset 15 : second [0-59]
EndStructure
Procedure.q DateCompose(year.q, month.i=1, day.i=1, hour.i=0, minute.i=0, second.i=0)
Protected.q date
CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
!mov rax, [p.v_second] ; First handle the time
!mov rcx, [p.v_minute] ; components and day.
!mov rdx, [p.v_hour]
!mov r8, [p.v_day]
!imul rcx, 60
!imul rdx, 3600
!add rax, rcx
!add rax, rdx
!imul r8, 86400
!add rax, r8
!mov [p.v_date], rax
!mov r8, 0x5555555555555556
!mov rax, [p.v_month] ; Convert the month range
!mov rcx, rax ; to [3, 14]
!cqo
!add rdx, r8
!imul rdx
!sar rdx, 2
!imul rax, rdx, 12
!sub rcx, rax
!cmp rcx, 3
!sbb rax, rax
!add rdx, rax
!and rax, 12
!add rcx, rax
!mov rax, rdx
!add [p.v_year], rax
CompilerElse
!mov ecx, [p.v_second] ; First handle the time
!mov eax, [p.v_minute] ; components and day.
!mov edx, 60
!imul edx
!add eax, ecx
!adc edx, 0
!mov [p.v_date], eax
!mov [p.v_date+4], edx
!mov eax, [p.v_hour]
!mov edx, 3600
!imul edx
!add [p.v_date], eax
!adc [p.v_date+4], edx
!mov eax, [p.v_day]
!mov edx, 86400
!imul edx
!add [p.v_date], eax
!adc [p.v_date+4], edx
!mov eax, [p.v_month] ; Convert the month range
!mov ecx, eax ; to [3, 14]
!cdq
!add edx, 0x55555556
!imul edx
!sar edx, 2
!imul eax, edx, 12
!sub ecx, eax
!cmp ecx, 3
!sbb eax, eax
!add edx, eax
!and eax, 12
!add ecx, eax
!mov eax, edx
!cdq
!add [p.v_year], eax
!adc [p.v_year+4], edx
CompilerEndIf
!lea eax, [ecx+1] ; Calculate the month
!imul eax, 7835 ; contribution to the date.
!shr eax, 8
!sub eax, 719591
!mov edx, 86400
!imul edx
!add [p.v_date], eax ; Add it to the date.
!adc [p.v_date+4], edx
!mov edx, [p.v_year+4] ; Continue with handling the
!mov eax, [p.v_year] ; year.
!shl edx, 24
!sar edx, 24
!mov ecx, 400 ; Split into 400 year
!idiv ecx ; periods to make things
!mov ecx, edx ; easier to handle.
!sar ecx, 31
!add eax, ecx
!and ecx, 400
!add ecx, edx
!mov edx, 0x5e0c0b3 ; Add the contribution of
!imul edx ; the 400 year periods to
!shld edx, eax, 7 ; the date.
!shl eax, 7
!add [p.v_date], eax
!adc [p.v_date+4], edx
!imul eax, ecx, 1461 ; Handle the remaining years
!shr eax, 2 ; [0, 399], add to the date
!imul ecx, 0x28f5d ; and return the result.
!shr ecx, 24
!sub eax, ecx
CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
!imul rax, 86400
!add rax, [p.v_date]
CompilerElse
!mov edx, 86400
!mul edx
!add eax, [p.v_date]
!adc edx, [p.v_date+4]
CompilerEndIf
ProcedureReturn
EndProcedure
Procedure DateDecompose(date.q, *dc.DateComponents)
Protected.l y2800, y, c
; >> 2800 year cycle <<
!mov eax, [p.v_date] ; First divide the date
!mov edx, [p.v_date+4] ; by 88.359.465.600 to get
!shrd eax, edx, 7 ; a 2800 year period and a
!sar edx, 7 ; smaller remainder to work
!mov ecx, 0x292544e5 ; with.
!idiv ecx
!sar edx, 31
!add eax, edx
!mov [p.v_y2800], eax ; [tmp store y2800 period]
!imul ecx
!shld edx, eax, 7
!shl eax, 7
!sub [p.v_date], eax ; [tmp store remainder]
!sbb [p.v_date+4], edx
; >> split date/time <<
!mov eax, [p.v_date] ; Divide the remainder by
!mov edx, [p.v_date+4] ; 86400 to get a day number
!mov ecx, 86400 ; and the time in seconds.
!div ecx
; >> time <<
; hour
!imul ecx, edx, 0x91a3 ; Divide the time by 3600
!shr ecx, 27 ; to ge the hour.
!mov [p.v_date+1], cl
!imul ecx, 3600
!sub edx, ecx
; min
!imul ecx, edx, 0x44445 ; Divide the 0-3599
!shr ecx, 24 ; remaining seconds by 60
!mov [p.v_date+2], cl ; to get the minute.
!imul ecx, 60
!sub edx, ecx
; sec
!mov [p.v_date+3], dl ; What's left is the second.
; >> date <<
; weekday
!mov ecx, eax ; The weekday is easy.
!add eax, 4 ; After 2800 years everything
!mov edx, 0x24924925 ; repeats so we can use the
!mul edx ; smaller date and do
!imul edx, 7 ; wday = (day number + 4) % 7
!lea eax, [ecx+4]
!sub eax, edx
!mov [p.v_date], al
; >> to structure << ; [copy time and wday to *date]
!mov eax, [p.v_date]
CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
!mov rdx, [p.p_dc]
!mov [rdx+12], eax
CompilerElse
!mov edx, [p.p_dc]
!mov [edx+12], eax
CompilerEndIf
; year (part 1)
!add ecx, 719162 ; Divide the day number by
!lea eax, [4*ecx+3] ; 36524.25 to get the century
!mov edx, 0xe5ac1af4 ; (within the 2800 year period).
!mul edx
!shr edx, 17
!mov [p.v_c], edx
!imul edx, 146097
!shr edx, 2
!sub ecx, edx
!lea eax, [4*ecx+3] ; Divide the remaining days by
!mov edx, 0x2cdb61 ; 365.25 to get the year within
!mul edx ; the century.
!mov [p.v_y], edx
; day of year
!imul edx, 1461 ; yday is the remaining days+1
!shr edx, 2
!sub ecx, edx
!lea eax, [ecx+1]
!mov [p.v_date], eax ; [tmp store yday]
; year (part 2)
!mov ecx, [p.v_c] ; Now we combine the 2800 year
!imul ecx, 100 ; period with the century and
!mov eax, [p.v_y2800] ; the last two year digits into
!add ecx, [p.v_y] ; one big 64 bit year number.
!mov edx, 2800
!imul edx
!add ecx, 1
!add eax, ecx
!adc edx, 0
; >> to structure << ; [copy year and yday to *date]
CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
!mov rcx, [p.p_dc]
!mov [rcx], eax
!mov [rcx+4], edx
!mov edx, [p.v_date]
!mov [rcx+8], dx
CompilerElse
!mov ecx, [p.p_dc]
!mov [ecx], eax
!mov [ecx+4], edx
!mov edx, [p.v_date]
!mov [ecx+8], dx
CompilerEndIf
; leap year check
!mov eax, [p.v_y] ; To continue with the month
!sub eax, 99 ; and day of month, we first
!sar eax, 31 ; need to find out if it's a
!or eax, [p.v_c] ; leap year.
!and eax, [p.v_y]
!mov ecx, eax
!shr ecx, 1
!and eax, ecx
!and eax, 1
; convert day number
!sub edx, eax ; For calculating the month
!sub edx, 60 ; and day of month, it is
!sbb ecx, ecx ; easier to let March 1 be
!add eax, 365 ; day number 1. So we convert
!and ecx, eax ; it before continuing.
!lea ecx, [ecx+edx+1]
; month and day
!lea eax, [ecx*5-3] ; m=(5*d-3)/153
!imul ecx, eax, 0x1ac6
!shr ecx, 20
!imul edx, ecx, 153 ; d=(5*d-3)-153*m
!sub eax, edx
!add eax, 5 ; d=(d+5)/5
!imul eax, 0x33334
!shr eax, 20
!add ecx, 3 ; m+3
!mov edx, 12 ; if m>12, m-12
!sub edx, ecx
!sar edx, 31
!and edx, 12
!sub ecx, edx
; >> to structure << ; [copy month and day to *date]
CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
!mov rdx, [p.p_dc]
!mov [rdx+10], cl
!mov [rdx+11], al
CompilerElse
!mov edx, [p.p_dc]
!mov [edx+10], cl
!mov [edx+11], al
CompilerEndIf
EndProcedure