Sakamoto algorithm to calculate day of week

If year is non-negative:

Sakamoto algorithm implementation in C++: 0 = sunday, 1 = monday, ..., 6 = saturday.

static int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
int dow(int y, int m, int d) {
  y -= m<3;
  return (d + t[m-1] + y + y/4 - y/100 + y/400) % 7;
}

In Javascript:

// m = 1, 2, ..., 12
// result: 0 = sun, 1 = mon, ..., 6 = sat
function dayOfWeek(y, m, d) {
  if (m<3) y--
  m = [0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4][m-1]
  return (d + m + y + ~~(y/4) - ~~(y/100) + ~~(y/400)) % 7
}

In javascript: ~ is bitwise not operator. Double bitwise not (~~) is equivalent to Math.trunc().

for (const check of [
~~2.7 === 2,
~~2.5 === 2,
~~2.3 === 2,
~~2 === 2,
~~0.5 === 0,
~~0 === 0,
~~-0 === 0,
~~-0.3 === 0,
~~-0.9 === 0,
~~-1 === -1,
~~-1.5 === -1,
]) console.log(check)
All print true

Testing script for all days from year 0 to year 3000:

for (let x = 0; x < 3000 * 365; x ++) {
  const d = new Date((x - 1970 * 365) * 24 * 60 * 60 * 1000)
  if ( d.getDay() !== dayOfWeek(d.getFullYear(), d.getMonth() + 1, d.getDate())) {
    console.log('wrong', d, d.getFullYear(), d.getMonth(), d.getDate(), d.getDay(), dayOfWeek(d.getFullYear(), d.getMonth() + 1, d.getDate()))
    break
  }
}

If year can be negative:

  • The division must be floor, not truncate
  • And, the last modulo operator must be normalized to be non-negative
static int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
int dow_full(int y, int m, int d) {
  double z = m < 3 ? y - 1 : y;
  return ((d + t[m-1] + (int)z + (int)floor(z/4) - (int)floor(z/100) + (int)floor(z/400)) % 7 + 7) % 7;
}
function dayOfWeekFull(y, m, d) {
  if (m<3) y--
  m = [0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4][m-1]
  return ((d + m + y + Math.floor(y/4) - Math.floor(y/100) + Math.floor(y/400)) % 7 + 7) % 7
}

Testing script which tests all days from year -3000 to year 3000:

for (let x = -3000; x < 3000 * 365; x ++) {
  const d = new Date((x - 1970 * 365) * 24 * 60 * 60 * 1000)
  if ( d.getDay() !== dayOfWeekFull(d.getFullYear(), d.getMonth() + 1, d.getDate())) {
    console.log('wrong', d, d.getFullYear(), d.getMonth(), d.getDate(), d.getDay(), dayOfWeek(d.dayOfWeekFull(), d.getMonth() + 1, d.getDate()))
    break
  }
}