homepage/src/components/nextTopic.js

216 lines
5.1 KiB
JavaScript

const TUESDAY = 2
const THURSDAY = 4
const WEEK = 7
// for easier mocking in tests
var today = new Date()
export default function NextTopic() {
// 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) {
test()
return "unbekannt"
}
return formatDateInfo(getNextTopicDate())
}
// javascript dates are not nice
function getNextTopicDate() {
// first thursday and third tuesday in month
const nextTopic = zeroizeTime(today)
// 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 dates
*/
function weeksBetween(datetime1, datetime2) {
const date1 = zeroizeTime(datetime1)
const date2 = zeroizeTime(datetime2)
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}`
}
function zeroizeTime(date) {
const copy = new Date(date)
copy.setHours(0)
copy.setMinutes(0)
copy.setSeconds(0)
copy.setMilliseconds(0)
return copy
}
// test, because this is complicated
function test() {
testLateSunday()
testYear2020()
// reset to correct value
today = new Date()
}
function testLateSunday() {
today = new Date("2020-01-19T23:59:59+01:00")
const result = formatDateInfo(getNextTopicDate())
console.assert(
result === "Nächste Woche Dienstag, 2020-01-21",
`starting at ${getISODateString(
today
)}: was ${result}, expected "Nächste Woche Dienstag, 2020-01-21"`
)
}
function testYear2020() {
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 = zeroizeTime(new Date("2020-01-01"))
let currentIndex = 0
while (today <= new Date("2020-12-15")) {
const result = getISODateString(getNextTopicDate())
const expect = topicsIn2020[currentIndex]
console.assert(
result === expect,
`starting at ${getISODateString(
today
)}: was ${result}, expected ${expect}`
)
if (getISODateString(today) === result) {
currentIndex++
}
addDays(today, 1)
}
}