doomsday: beginnings
This commit is contained in:
parent
e4eef33c2a
commit
72cbae88e0
|
|
@ -10,25 +10,34 @@ import icon from "astro-icon";
|
|||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
site: "https://hoelting.dev",
|
||||
trailingSlash: "always",
|
||||
site: "https://hoelting.dev",
|
||||
trailingSlash: "always",
|
||||
|
||||
integrations: [mdx(), sitemap(), icon(), solidJs(), matomo({
|
||||
enabled: import.meta.env.PROD, // only enable in production
|
||||
url: "https://analytics.hoelting.dev",
|
||||
siteId: 3,
|
||||
disableCookies: true,
|
||||
enableCrossDomainLinking: true,
|
||||
domains: ["*.hoelting.dev", "*.www.hoelting.dev"],
|
||||
respectDoNotTrack: true,
|
||||
})],
|
||||
integrations: [
|
||||
mdx(),
|
||||
sitemap(),
|
||||
icon(),
|
||||
solidJs(),
|
||||
matomo({
|
||||
enabled: import.meta.env.PROD, // only enable in production
|
||||
url: "https://analytics.hoelting.dev",
|
||||
siteId: 3,
|
||||
disableCookies: true,
|
||||
enableCrossDomainLinking: true,
|
||||
domains: ["*.hoelting.dev", "*.www.hoelting.dev"],
|
||||
respectDoNotTrack: true,
|
||||
}),
|
||||
],
|
||||
|
||||
markdown: {
|
||||
remarkPlugins: [remarkMath],
|
||||
rehypePlugins: [rehypeKatex],
|
||||
},
|
||||
markdown: {
|
||||
remarkPlugins: [remarkMath],
|
||||
rehypePlugins: [rehypeKatex],
|
||||
},
|
||||
|
||||
vite: {
|
||||
plugins: [tailwindcss({ applyBaseStyles: false })],
|
||||
},
|
||||
vite: {
|
||||
plugins: [tailwindcss({ applyBaseStyles: false })],
|
||||
server: {
|
||||
watch: {},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
---
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
export interface Props {
|
||||
display:
|
||||
| "weekday"
|
||||
| "weekday-number"
|
||||
| "year"
|
||||
| "january-day"
|
||||
| "february-day";
|
||||
prefix?: string;
|
||||
suffix?: string;
|
||||
offset?: number;
|
||||
valueClass?: string;
|
||||
}
|
||||
|
||||
const {
|
||||
display,
|
||||
prefix,
|
||||
suffix,
|
||||
offset,
|
||||
valueClass: customClass,
|
||||
} = Astro.props;
|
||||
---
|
||||
|
||||
<span class={cn(`doomsday-display doomsday-${display}`)}
|
||||
>{prefix}<span
|
||||
class={cn("doomsday-value", customClass)}
|
||||
data-offset={offset}>...</span
|
||||
>{suffix}</span
|
||||
>
|
||||
|
||||
<script>
|
||||
function isLeapYear(year: number) {
|
||||
return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
|
||||
}
|
||||
function handleOffset<T>(
|
||||
element: HTMLElement,
|
||||
fn: (offset: number) => T,
|
||||
): T {
|
||||
const offset = parseInt(element.dataset.offset || "0");
|
||||
return fn(offset);
|
||||
}
|
||||
const weekdays = [
|
||||
"Sunday",
|
||||
"Monday",
|
||||
"Tuesday",
|
||||
"Wednesday",
|
||||
"Thursday",
|
||||
"Friday",
|
||||
"Saturday",
|
||||
];
|
||||
const currentYearInput = document.getElementById(
|
||||
"doomsday-year",
|
||||
) as HTMLInputElement | null;
|
||||
|
||||
function update() {
|
||||
const selectedYear = currentYearInput
|
||||
? parseInt(currentYearInput.value)
|
||||
: new Date().getFullYear();
|
||||
console.log("Selected Year:", selectedYear);
|
||||
const isSelectedYearLeap = isLeapYear(selectedYear);
|
||||
const febDoomsdayDay = 28 + (isSelectedYearLeap ? 1 : 0);
|
||||
|
||||
// Display the selected year
|
||||
const yearElements = document.querySelectorAll(
|
||||
".doomsday-display.doomsday-year > .doomsday-value",
|
||||
);
|
||||
yearElements.forEach((element) => {
|
||||
element.textContent = handleOffset(
|
||||
element as HTMLElement,
|
||||
(offset) => (selectedYear + offset).toString(),
|
||||
);
|
||||
});
|
||||
|
||||
// Display the doomsday weekday for the selected year
|
||||
const doomsdayWeekdayElements = document.querySelectorAll(
|
||||
".doomsday-display.doomsday-weekday > .doomsday-value",
|
||||
);
|
||||
const doomsdayDate = new Date(selectedYear, 2, febDoomsdayDay);
|
||||
|
||||
const doomsdayOfSelectedYear = doomsdayDate.getDay();
|
||||
const doomsdayOfSelectedYearInText = weekdays[doomsdayOfSelectedYear];
|
||||
|
||||
doomsdayWeekdayElements.forEach((element) => {
|
||||
element.textContent = handleOffset(
|
||||
element as HTMLElement,
|
||||
(offset) => {
|
||||
if (offset === 0) {
|
||||
return doomsdayOfSelectedYearInText;
|
||||
} else {
|
||||
const adjustedDoomsday =
|
||||
(doomsdayOfSelectedYear + offset) % 7;
|
||||
return weekdays[adjustedDoomsday];
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
// Display the weekday number of the doomsday for the selected year
|
||||
const doomsdayWeekdayNumberElements = document.querySelectorAll(
|
||||
".doomsday-display.doomsday-weekday-number > .doomsday-value",
|
||||
);
|
||||
|
||||
doomsdayWeekdayNumberElements.forEach((element) => {
|
||||
element.textContent = handleOffset(
|
||||
element as HTMLElement,
|
||||
(offset) => (doomsdayOfSelectedYear + offset) % 7,
|
||||
).toString();
|
||||
});
|
||||
|
||||
// Display the doomsday date for January and February
|
||||
const janDoomsdayDay = isSelectedYearLeap ? 4 : 3;
|
||||
const janDoomsdayDayElements = document.querySelectorAll(
|
||||
".doomsday-display.doomsday-january-day > .doomsday-value",
|
||||
);
|
||||
janDoomsdayDayElements.forEach((element) => {
|
||||
element.textContent = handleOffset(
|
||||
element as HTMLElement,
|
||||
(offset) => (janDoomsdayDay + offset).toString(),
|
||||
);
|
||||
});
|
||||
const febDoomsdayDayElements = document.querySelectorAll(
|
||||
".doomsday-display.doomsday-february-day > .doomsday-value",
|
||||
);
|
||||
febDoomsdayDayElements.forEach((element) => {
|
||||
element.textContent = handleOffset(
|
||||
element as HTMLElement,
|
||||
(offset) => (febDoomsdayDay + offset).toString(),
|
||||
);
|
||||
});
|
||||
}
|
||||
currentYearInput?.addEventListener("input", update);
|
||||
|
||||
update();
|
||||
</script>
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<input
|
||||
type="number"
|
||||
name="doomsday-year"
|
||||
id="doomsday-year"
|
||||
min="1583"
|
||||
class="w-16"
|
||||
/>
|
||||
|
||||
<script>
|
||||
const currentYearInput = document.getElementById(
|
||||
"doomsday-year",
|
||||
) as HTMLInputElement;
|
||||
currentYearInput.value = new Date().getFullYear().toString();
|
||||
</script>
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
---
|
||||
title: "Doomsday Algorithm"
|
||||
summary: How to compute the day of the week of an arbitrary date in your head.
|
||||
date: 2026-03-19
|
||||
tags:
|
||||
- Math Trick
|
||||
---
|
||||
|
||||
import DynamicDoomsdayDisplay from "@/components/blog/content/doomsday-algorithm/DynamicDoomsdayDisplay.astro";
|
||||
import DynamicDoomsdayYearSelector from "@/components/blog/content/doomsday-algorithm/DynamicDoomsdayYearSelector.astro";
|
||||
|
||||
Generally, the idea is that there are certain dates in a year, that always fall on the same day of the week. We call those dates Doomsdays and there are rules for finding the Doomsday of every month. We get started with the easiest months:
|
||||
|
||||
## (Most) Even Months
|
||||
|
||||
We can use (almost) any even month for determining the Doomsday of a given year. For any even-numbered month except February, the same day is the Doomsday as the months number. This means:
|
||||
|
||||
- the 4th of April (4) is a Doomsday,
|
||||
- the 6th of June (6) is a Doomsday,
|
||||
- the 10th of October (10) is a Doomsday,
|
||||
- the 12th of December (12) is a Doomsday.
|
||||
|
||||
Generally, for the Nth _even_ month, the Nth day is a Doomsday.
|
||||
|
||||
We can use this for our first weekday calculation. Let's ask ourselves, what weekday is Halloween (31st of October) this year?
|
||||
We know that the 10th of October is a <DynamicDoomsdayDisplay display="weekday" /> because it is a Doomsday. Calculating 31 - 10 = 21, we know that it is exactly three weeks afterward a Doomsday.
|
||||
Therefore, **Halloween is a Doomsday**. This year, Halloween falls on a <DynamicDoomsdayDisplay display="weekday" suffix="." />
|
||||
|
||||
To make finding the weekday easier, we can represent the weekdays as numbers ranging from 0 (Sunday) to 6 (Saturday). The number of the weekday of any given date can be computed by adding the number of the Doomsday weekday and the offset to the nearest Doomsday and then computing modulo 7 of that value.
|
||||
|
||||
Let's take a look at the remaining even month:
|
||||
|
||||
## February
|
||||
|
||||
The Doomsday is always the last day of february, e.g. **February 28 or 29**.
|
||||
|
||||
For <DynamicDoomsdayDisplay display="year" suffix="," /> February <DynamicDoomsdayDisplay display="february-day" /> is a <DynamicDoomsdayDisplay display="weekday" valueClass="font-bold" suffix="." />
|
||||
|
||||
Afterwards
|
||||
|
||||
<DynamicDoomsdayYearSelector />
|
||||
Loading…
Reference in New Issue