diff --git a/src/app/october/page.tsx b/src/app/october/page.tsx
new file mode 100644
index 0000000..2ff7887
--- /dev/null
+++ b/src/app/october/page.tsx
@@ -0,0 +1,18 @@
+import type { Metadata } from "next";
+import { Suspense } from "react";
+import { OctoberCounter } from "~/components/OctoberCounter";
+
+export const metadata: Metadata = {
+ title: "October Today | Sean O'Connor",
+ description: "See what day of October 2019 it is today.",
+};
+
+export default function OctoberPage() {
+ return (
+
+
+
+
+
+ );
+}
diff --git a/src/components/OctoberCounter.tsx b/src/components/OctoberCounter.tsx
new file mode 100644
index 0000000..9470aee
--- /dev/null
+++ b/src/components/OctoberCounter.tsx
@@ -0,0 +1,135 @@
+"use client";
+
+import { useEffect, useState } from "react";
+import Link from "next/link";
+import { useSearchParams } from "next/navigation";
+import { CircleHelp, Share2 } from "lucide-react";
+import { Button } from "~/components/ui/button";
+import { Card, CardContent } from "~/components/ui/card";
+
+const OCTOBER_FIRST_2019 = Date.UTC(2019, 9, 1);
+const MILLISECONDS_PER_DAY = 1000 * 60 * 60 * 24;
+
+function getOrdinalSuffix(number: number) {
+ if (number % 100 >= 11 && number % 100 <= 13) {
+ return "th";
+ }
+
+ switch (number % 10) {
+ case 1:
+ return "st";
+ case 2:
+ return "nd";
+ case 3:
+ return "rd";
+ default:
+ return "th";
+ }
+}
+
+function getOctoberDay(today: Date) {
+ const todayUtc = Date.UTC(
+ today.getFullYear(),
+ today.getMonth(),
+ today.getDate(),
+ );
+
+ return Math.floor((todayUtc - OCTOBER_FIRST_2019) / MILLISECONDS_PER_DAY) + 1;
+}
+
+export function OctoberCounter() {
+ const searchParams = useSearchParams();
+ const isJsonMode = searchParams.get("api") === "json";
+ const [targetDay] = useState(() => getOctoberDay(new Date()));
+ const [day, setDay] = useState(() =>
+ isJsonMode ? targetDay : Math.max(1, targetDay - 50),
+ );
+ const isAnimating = !isJsonMode && day < targetDay;
+ const ordinal = getOrdinalSuffix(targetDay);
+
+ useEffect(() => {
+ if (isJsonMode) return;
+
+ let currentNumber = Math.max(1, targetDay - 50);
+
+ const interval = window.setInterval(() => {
+ currentNumber += 1;
+ setDay(currentNumber);
+
+ if (currentNumber >= targetDay) {
+ window.clearInterval(interval);
+ document.title = `October ${targetDay}${ordinal}, 2019`;
+ }
+ }, 30);
+
+ return () => window.clearInterval(interval);
+ }, [isJsonMode, ordinal, targetDay]);
+
+ if (isJsonMode) {
+ const response = {
+ day,
+ ordinal,
+ formatted: `${day}${ordinal}`,
+ text: `happy october ${day}${ordinal}`,
+ };
+
+ return (
+
+ {JSON.stringify(response, null, 2)}
+
+ );
+ }
+
+ const handleShare = () => {
+ const message = `happy october ${day}${ordinal}`;
+ window.open(`sms:?&body=${encodeURIComponent(message)}`, "_blank");
+ };
+
+ return (
+
+
+
+
+ Today is
+
+
+
+
+ October{" "}
+
+ {day}
+
+
+ {isAnimating ? "th" : ordinal}
+
+
2019.
+
+
+
+
+
+ Share via SMS
+
+
+
+
+
+ Why?
+
+
+
+
+
+ );
+}