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

I don't see how do you use month in ecx in your second code.

This does give the correct day for today:

Friday March 22nd, 2002

eax= 5 (for friday)

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

I just didn't see "+ ecx - 1]" in my brouser.

Having the code copied\pasted I saw it :)

Having the code copied\pasted I saw it :)

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

Next one for BiS? to post in Thomas library :)

First under BiS? I posted LongStrLen.

What do you think, Rickey?

First under BiS? I posted LongStrLen.

What do you think, Rickey?

It's already posted,

I'll add your improvements.

**Svin**. :)I'll add your improvements.

I didn't see changes in your snippet so post another one with

div instruction removed.

Left you as the author.

div instruction removed.

Left you as the author.

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

- + it is not equal [97*y/100]

2004/4-2004/100+2004/400=501-20+5=486 < > 485=97*2004/100

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?

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?

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

----

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

I was buffled why in first post you wrote:

I mean 97y/100 instead of 97y/400.

And after that also the same error:

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.

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

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

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

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

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.

Fix?:

Thanks to

```
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.Well, well...

Some GUI to wrap this proc in...

Check it out ;-)

Some GUI to wrap this proc in...

Check it out ;-)

bitRAKE,

I played with your algo and here is the result:

Regards,

Lingo

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

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

bitRAKE,

Thanks, but I have problem with leap years

Somethng in your algo is wrong

Regards,

Lingo

Thanks, but I have problem with leap years

Somethng in your algo is wrong

Regards,

Lingo