add component to get the next 'Topictreff' date

This commit is contained in:
neri 2020-01-17 01:28:00 +01:00
parent a223f843d9
commit 1b6771a2c7
2 changed files with 191 additions and 0 deletions

188
src/components/nextTopic.js Normal file
View File

@ -0,0 +1,188 @@
const TUESDAY = 2
const THURSDAY = 4
const WEEK = 7
// for easier mocking in tests
var today = new Date()
export default () => {
// don't put the nextTopic date in the staticly generated html
// because it would be outdated rather quickly
const isSSR = typeof window === "undefined"
if (isSSR) {
testNextTopic()
return "unbekannt"
}
return formatDateInfo(getNextTopicDate())
}
// javascript dates are not nice
function getNextTopicDate() {
// first thursday and third tuesday in month
const nextTopic = new Date(today)
nextTopic.setHours(0)
nextTopic.setMinutes(0)
nextTopic.setSeconds(0)
nextTopic.setMilliseconds(0)
// first thursday
if (calculatePriorWeekdays(THURSDAY) === 0) {
addDays(nextTopic, getDaysUntilNext(THURSDAY, nextTopic))
return nextTopic
}
// third tuesday
const priorTuesdays = calculatePriorWeekdays(TUESDAY)
if (priorTuesdays <= 2) {
addDays(nextTopic, getDaysUntilNext(TUESDAY, nextTopic))
addDays(nextTopic, WEEK * (2 - priorTuesdays))
return nextTopic
}
// first thursday next month
const currentMonth = today.getMonth()
addDays(nextTopic, getDaysUntilNext(THURSDAY, nextTopic))
while (nextTopic.getMonth() === currentMonth) {
addDays(nextTopic, WEEK)
}
return nextTopic
}
/**
* calculate how many of the given weekday this month already had.
* for example: how many tuesdays were in this month already
*/
function calculatePriorWeekdays(weekday) {
const testDate = new Date(today)
testDate.setDate(1)
var priorWeekdays = 0
while (testDate < today) {
if (testDate.getDay() === weekday) {
priorWeekdays++
}
testDate.setDate(testDate.getDate() + 1)
}
return priorWeekdays
}
/**
* how many days are there until the next <weekday> starting from <date>
*/
function getDaysUntilNext(weekday, date) {
return mod(weekday - date.getDay(), WEEK)
}
/**
* just the modulo function, but always return the positive result
*/
function mod(n, m) {
return ((n % m) + m) % m
}
/**
* add <days> days to the <date>
* but do it in a way that ignores daylight savings time
*/
function addDays(date, days) {
date.setDate(date.getDate() + days)
if (date.getHours() > 12) {
date.setDate(date.getDate() + 1)
} else if (date.getHours() !== 0) {
date.setDate(date.getDate() - 1)
}
}
/**
* return a human readable representation of the date
*/
function formatDateInfo(date) {
const dayNames = {
"2": "Dienstag",
"4": "Donnerstag",
}
const dayName = dayNames[date.getDay()]
const isoDate = getISODateString(date)
const weeks = weeksBetween(today, date)
if (weeks === 0 && date.getDay() === today.getDay()) {
return `Heute, ${isoDate}`
} else if (weeks === 0) {
return `Diese Woche ${dayName}, ${isoDate}`
} else if (weeks === 1) {
return `Nächste Woche ${dayName}, ${isoDate}`
} else {
return `${dayName} in ${weeks} Wochen, ${isoDate}`
}
}
/**
* how many sunday to monday transitions are between the two daysTillTuesday
*/
function weeksBetween(date1, date2) {
const MILLISECONDS_IN_WEEK = 7 * 24 * 60 * 60 * 1000
var weeks = Math.floor((date2 - date1) / MILLISECONDS_IN_WEEK)
// if there is a sunday to monday transition between
if (mod(date1.getDay() - 1, WEEK) > mod(date2.getDay() - 1, WEEK)) {
weeks += 1
}
return weeks
}
function getISODateString(date) {
const year = date.getFullYear()
const month = date.getMonth() + 1
const day = date.getDate()
const monthPadded = (month < 10 ? "0" : "") + month
const dayPadded = (day < 10 ? "0" : "") + day
return `${year}-${monthPadded}-${dayPadded}`
}
// test, becuase this is complicated
function testNextTopic() {
const topicsIn2020 = [
"2020-01-02",
"2020-01-21",
"2020-02-06",
"2020-02-18",
"2020-03-05",
"2020-03-17",
"2020-04-02",
"2020-04-21",
"2020-05-07",
"2020-05-19",
"2020-06-04",
"2020-06-16",
"2020-07-02",
"2020-07-21",
"2020-08-06",
"2020-08-18",
"2020-09-03",
"2020-09-15",
"2020-10-01",
"2020-10-20",
"2020-11-05",
"2020-11-17",
"2020-12-03",
"2020-12-15",
]
today = new Date("2020-01-01")
for (const nextTopic of topicsIn2020) {
const result = getISODateString(getNextTopicDate())
console.assert(
result === nextTopic,
`starting at ${getISODateString(
today
)}: was ${result}, expected ${nextTopic}`
)
today = new Date(result)
today.setDate(today.getDate() + 1)
}
// reset to correct value
today = new Date()
}

View File

@ -3,5 +3,8 @@ path: "/events"
title: "events" title: "events"
edit: "events.mdx" edit: "events.mdx"
--- ---
import NextTopic from "../components/nextTopic.js"
# Events # Events
**Nächstes Topic-Treff**: <NextTopic/>