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"
|
title: "events"
|
||||||
edit: "events.mdx"
|
edit: "events.mdx"
|
||||||
---
|
---
|
||||||
|
import NextTopic from "../components/nextTopic.js"
|
||||||
|
|
||||||
# Events
|
# Events
|
||||||
|
|
||||||
|
**Nächstes Topic-Treff**: <NextTopic/>
|
||||||
|
|
Loading…
Reference in New Issue