🗃️ Christmas countdown source code
📆 2025-12-20 18:15
Here's a tiny Bash script that outputs a festive Christmas countdown in Gemtext.
It figures out how much time is left until Christmas and formats it nicely in weeks, days, hours, minutes and seconds - perfect for a Gemini capsule or a simple CGI endpoint.
Comments in the script should help you customize it.
#!/usr/bin/env bash
# Gemini CGI script that outputs a festive Gemtext countdown to Christmas
# --- Determine today's date and the target Christmas year ---
# --- Extract year from QUERY_STRING (/?YYYY) ---
if printf '%s\n' "$QUERY_STRING" | grep -Eq '^[0-9]{4}$'; then
year="$QUERY_STRING"
else
year=$(date +%Y)
fi
# --- Check if today is Christmas ---
christmas_ymd="${year}1225"
today_ymd=$(date +%Y%m%d)
is_christmas=false
christmas_date=$(date +%m%d)
[ "$christmas_date" = "1225" ] && is_christmas=true
# If today's date is after Dec 25, switch to next year's Christmas
if [ "$today_ymd" -ge "$christmas_ymd" ]; then
year=$((year + 1))
christmas_ymd="${year}1225"
fi
# --- Build links for the next 4 Christmas years ---
future_years=()
for i in 1 2 3 4; do
future_years+=($((year + i)))
done
# --- Convert dates to Unix timestamps for arithmetic ---
now_sec=$(date +%s)
christmas_sec=$(date -d "${year}-12-25" +%s)
# --- Calculate total remaining seconds ---
diff_sec=$((christmas_sec - now_sec))
# --- Calculate remaining time in days ---
total_days=$(( diff_sec / 86400 ))
# --- Break remaining days into weeks and days ---
weeks=$(( total_days / 7 ))
days=$(( total_days % 7 ))
# --- Calculate remaining hours, minutes, and seconds within the current day ---
hours=$(( (diff_sec % 86400) / 3600 ))
minutes=$(( (diff_sec % 3600) / 60 ))
seconds=$(( diff_sec % 60 ))
# --- Simple pluralization helper ---
plural() {
[ "$1" -eq 1 ] && printf "%s" "$2" || printf "%ss" "$2"
}
# --- Build a list of non-zero time units ---
parts=()
[ "$weeks" -gt 0 ] && parts+=("${weeks} $(plural "$weeks" week)")
[ "$days" -gt 0 ] && parts+=("${days} $(plural "$days" day)")
[ "$hours" -gt 0 ] && parts+=("${hours} $(plural "$hours" hour)")
[ "$minutes" -gt 0 ] && parts+=("${minutes} $(plural "$minutes" minute)")
[ "$seconds" -gt 0 ] && parts+=("${seconds} $(plural "$seconds" second)")
# --- Join parts into a natural-language string ---
if [ "${#parts[@]}" -eq 1 ]; then
time_str="${parts[0]}"
elif [ "${#parts[@]}" -eq 2 ]; then
time_str="${parts[0]} and ${parts[1]}"
else
last=${parts[-1]}
unset 'parts[-1]'
time_str="$(printf "%s, " "${parts[@]}")and ${last}"
fi
# --- Output Gemini response headers and Gemtext body ---
printf "20 text/gemini\r\n"
printf "# Days Until Christmas %s \r\n" "$year"
printf "A lightweight Gemtext Christmas countdown.\r\n"
printf "Weeks, days, hours, minutes, and seconds until December 25th.\r\n\r\n"
if $is_christmas; then
printf "## Merry Christmas %s!\r\n" "$(date +%Y)"
printf "Today is Christmas - enjoy your gifts!\r\n"
else
printf "Today is %s.\r\n" "$(date +%Y-%m-%d)"
fi
printf "## Time remaining until Christmas %s :\r\n" "$year"
printf "> %s \r\n" "$time_str"
printf "\r\n### Future Christmas Countdowns \r\n"
for y in "${future_years[@]}"; do
printf "=> /fun-stuff/christmas-countdown/?%s Days Until Christmas %s\r\n" "$y" "$y"
done
printf "=> gemini://sava.rocks/fun-stuff/christmas-countdown/ Christmas countdown from Sava.Rocks\r\n"
View the script in action at: