add component to get the next 'Topictreff' date
This commit is contained in:
parent
a223f843d9
commit
1b6771a2c7
|
@ -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()
|
||||
}
|
|
@ -3,5 +3,8 @@ path: "/events"
|
|||
title: "events"
|
||||
edit: "events.mdx"
|
||||
---
|
||||
import NextTopic from "../components/nextTopic.js"
|
||||
|
||||
# Events
|
||||
|
||||
**Nächstes Topic-Treff**: <NextTopic/>
|
||||
|
|
Loading…
Reference in New Issue