; Work out what the day of the week is for a given date

; Optimized x86 ASM by Rickey Bowers Jr. (bitRAKE)
;
; C Algorithm by Tomohiko Sakamoto
;int dayofweek(int d, int m, int y) {
; static int t[] = { 0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4 };
; y -= m < 3;
; return ( y + y/4 - y/100 + y/400 + t[m-1] + d) % 7;
;}


DayOfWeek PROC year:DWORD, month:DWORD, day:DWORD
; year = [0, 9999+]
; month = [1, 12]
; day = [1, 31]
CONST SEGMENT
t db 6,2,2, 5,0,3, 5,1,4, 6,2,4
CONST ENDS

mov eax, 3E147AE1h ; 97/400 * 2^32
mov ecx, month
mul year
movzx eax, BYTE PTR [t + ecx - 1]
; mov eax, [t + ecx*4 - 4] ; change data to dword size
add edx, year
add eax, day
mov cl, 7 ; mov ecx, 7
add eax, edx
cdq
div ecx
mov eax, edx
; remainder is day [0 - 6]
; 0 = Sunday
ret
DayOfWeek ENDP
The optimization is a comfortable combination of speed/size - it can be faster, but those would be special situations.

A little macro makes the code quite condensed,
and doesn't pollute the label space. :)
cDATA MACRO y:VARARG

LOCAL sym
CONST segment
IFIDNI <y>,<>
.ERR "cDATA!"
ELSE
sym y
ENDIF
CONST ends
EXITM <OFFSET sym>
ENDM


DayOfWeek PROC year:DWORD, month:DWORD, day:DWORD
mov eax, 3E147AE1h ; 97/400 * 2^32
mov ecx, month
mul year
movzx eax, BYTE PTR [cDATA(db 6,2,2, 5,0,3, 5,1,4, 6,2,4) + ecx - 1]
add edx, year
add eax, day
mov cl, 7
add eax, edx
cdq
div ecx
mov eax, edx
ret
DayOfWeek ENDP
Posted on 2002-03-21 21:25:25 by bitRAKE
I don't see how do you use month in ecx in your second code.
Posted on 2002-03-22 04:04:27 by The Svin
This does give the correct day for today:
Friday March 22nd, 2002
eax= 5 (for friday)



.586
.model flat, stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\user32.inc
include \masm32\include\masm32.inc
include \masm32\include\debug.inc

includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\masm32.lib
includelib \masm32\lib\debug.lib

cDATA MACRO y:VARARG
LOCAL sym
CONST segment
IFIDNI <y>,<>
.ERR "cDATA!"
ELSE
sym y
ENDIF
CONST ends
EXITM <OFFSET sym>
ENDM


DayOfWeek PROC year:DWORD, month:DWORD, day:DWORD
mov eax, 3E147AE1h ; 97/400 * 2^32
mov ecx, month
mul year
movzx eax, BYTE PTR [cDATA(db 6,2,2, 5,0,3, 5,1,4, 6,2,4) + ecx - 1]
add edx, year
add eax, day
mov cl, 7
add eax, edx
cdq
div ecx
mov eax, edx
ret
DayOfWeek ENDP

.code

start:
invoke DayOfWeek, 2002,3,22
PrintDec eax
invoke ExitProcess,0
end start
Posted on 2002-03-22 04:15:11 by Sliver
I just didn't see "+ ecx - 1]" in my brouser.
Having the code copied\pasted I saw it :)
Posted on 2002-03-22 04:38:23 by The Svin
This little idea might make your algo 3 times faster:


DayOfWeek PROC year:DWORD, month:DWORD, day:DWORD
mov eax, 3E147AE1h ; 97/400 * 2^32
mov ecx, month
mul year
movzx eax, BYTE PTR [cDATA(db 6,2,2, 5,0,3, 5,1,4, 6,2,4) + ecx - 1]
add edx, year
add eax, day
add eax, edx
MOV EDX,92492492h
push eax
inc eax
mul edx
shr edx,2
pop eax
mov ecx,edx
lea edx,[edx*2][edx]
lea edx,[ecx*4][edx]
sub eax,edx
ret
DayOfWeek ENDP
Posted on 2002-03-22 07:27:31 by The Svin
Next one for BiS? to post in Thomas library :)
First under BiS? I posted LongStrLen.
What do you think, Rickey?
Posted on 2002-03-22 11:04:37 by The Svin
It's already posted, Svin. :)
I'll add your improvements.
Posted on 2002-03-22 11:43:48 by bitRAKE
I didn't see changes in your snippet so post another one with
div instruction removed.
Left you as the author.
Posted on 2002-03-23 05:31:42 by The Svin
I was shocked. Where elementary logic theory? Where testing before sending of the code?
- + it is not equal [97*y/100]
2004/4-2004/100+2004/400=501-20+5=486 < > 485=97*2004/100
Posted on 2002-03-24 03:41:21 by Nexo
Calm down!
y/4-y/100+y/400=[(y*100)/(4*100)]-[(y*4)/(100*4)]+=
[100y/400]-[4y/400]+=(100y-4y+y)/400=97y/400
Where did you see 97y/100?! It's 97y/400 !.

I think, maybe it's a font in your brouser played bad jock to you?
So you took 4 as 1?
Posted on 2002-03-24 06:48:00 by The Svin
I shall not calm down! It is obvious, that "485=97*2004/400". I have told I is shocked. :confused:
----
y/4-y/100+y/400=[(y*100)/(4*100)]-[(y*4)/(100*4)]+=
[100y/400]-[4y/400]+=(100y-4y+y)/400=97y/400
---
Fractional parts should not add for y/4, -y/100 and y/400.
Once again. I show an error of mathematical manipulation:
-+ it is not equal [97*y/400]
2004/4-2004/100+2004/400=501-20+5=486<>485=97*2004/400
In second, expression y-=m<3 as I have understood, "is optimized" in the table. It is correct for calculation of a difference of day of week of the first number of months (table), but is not taken into account for calculation about usage of year.
Where there was a stage of debugging? The erratic code began to be published too frequently. All right, have overlooked.

I can offer the code from Stepan Polovnikov library. But only it works for dates from March, 1 1900 till February, 28 2100.

K=306
label tblT dword
irp I,<14,15,4,5,6,7,8,9,10,11,12,13>
dd (((K*I)/10-694066 mod 7) mod 7) shl 2
endm

mov edx,
mov ecx,
cmp edx,3
sbb eax,eax
add ecx,eax
imul ecx,1461
add ecx,
add ecx,
lea eax,
mov edx,49249249h
mul edx
shr edx,1
neg edx
lea eax,
lea eax,
add eax,ecx
Posted on 2002-03-24 09:01:46 by Nexo
I was buffled why in first post you wrote:
- + it is not equal [97*y/100]

I mean 97y/100 instead of 97y/400.
And after that also the same error:
2004/4-2004/100+2004/400=501-20+5=486 < > 485=97*2004/100


So this double typo (was it a typo?) confused me.

It's funny to see end of Stepan algo (or it was added after all?)
I mean way to divide by 7, looks like we think as twins with him :)

As to debuggin, bitRake algo gives correct results with many values so it's hard probability to catch error at once.
Posted on 2002-03-24 09:25:07 by The Svin
Nexo, you are correct - the conversion was sloppy and incorrect. I did think about the rounding, but totally missed the incorrect year with table optimization. Thank you, I will try again.
Posted on 2002-03-24 10:01:48 by bitRAKE
2 The Svin:
Stepan ALWAYS uses multiplying instead of division as far as possible.
Your code:
MOV EDX, 92492492h
inc eax
mul edx
shr edx, 2
Him code:
lea eax,
mov edx, 49249249h
mul edx
shr edx, 1
He uses signless division, that on one byte is shorter.
The DayOfWeek-algorithm assumes testing main points which include dates in period till March, after March for leap and normal years. Then all dates of January and February (y-= m < 3 bug), and also some year (2004,2008,2012... - addition of fractions bug) will produce an error in calculation.
For example, implementation from Stepan was tested on all a valid band of entry values.

2 bitRAKE:
Roundoff of numbers has not solved a problem in any case. It mathematically is not true. To turn out offset of erratic years at calculation (approximately on 2003,2007,20011...).
Posted on 2002-03-24 11:06:06 by Nexo
Stepan ALWAYS uses multiplying instead of division as far as possible

The same with me, that just proves my words right :)
I think, the fact that we offten discussed algos reflected to our way to seek solutions in simular directions.
Posted on 2002-03-24 13:50:25 by The Svin
Fix?:
DayOfWeek PROC year:DWORD, month:DWORD, day:DWORD

xor eax,eax
cmp month,3
sbb year,eax

mov eax, 3E147AE1h ; 97/400 * 2^32
mov ecx, month
mul year
movzx eax, BYTE PTR [cDATA(db 0,3,2, 5,0,3, 5,1,4, 6,2,4) + ecx - 1]
add edx, year
add eax, day
add eax, edx

push eax
mov edx,92492492h
inc eax
mul edx
shr edx,2
pop eax
lea ecx,[edx*2][edx]
lea edx,[edx*4][ecx]
sub eax,edx
ret
DayOfWeek ENDP
Please, forward bugs.
Thanks to Nexo for pointing out the error in the previous algo.
Posted on 2002-07-22 11:46:36 by bitRAKE
Well, well...
Some GUI to wrap this proc in...
Check it out ;-)
Posted on 2002-07-23 05:30:11 by Andycar
bitRAKE,
I played with your algo and here is the result:


.data
cDATA db 0,3,2, 5,0,3, 5,1,4, 6,2,4
.code
OPTION PROLOGUE:NONE ; turn it off
OPTION EPILOGUE:NONE ;
Align 16 ; Align 16 before the proc
DayOfWeek PROC year:DWORD, month:DWORD, day:DWORD
mov ecx, [esp+8] ; ecx=month
mov eax, 3E147AE1h ; 97/400 * 2^32
mov edx, [esp+4] ; edx=year
cmp ecx, 3
sbb edx, 0
movzx ecx, byte ptr [cDATA + ecx - 1]
add ecx, edx
mul edx ; edx=year
add ecx, [esp+12] ; add day ;covered by mul
lea edx, [edx+ecx+1]
mov eax, 92492492h
mov ecx, edx
mul edx
dec ecx ; covered by mul
shr edx, 2
mov eax, ecx
lea ecx, [edx*2][edx]
lea edx, [edx*4][ecx]
sub eax, edx
ret 3*4
DayOfWeek ENDP
OPTION PROLOGUE:PROLOGUEDEF ; turn back on the defaults
OPTION EPILOGUE:EPILOGUEDEF ;


Regards,
Lingo
Posted on 2003-05-04 20:49:27 by lingo12
lingo12, thanks for sharing! I've been meaning to test this proc exhaustively against Nexo's post to confirm it's correctness - one day I'll get around to it. I haven't had a need for it lately.
Posted on 2003-05-04 22:38:57 by bitRAKE
bitRAKE,
Thanks, but I have problem with leap years
Somethng in your algo is wrong

Regards,
Lingo
Posted on 2003-05-05 12:54:11 by lingo12