diff --git a/src/lib/dates.ts b/src/lib/dates.ts index 1f39f30..5047396 100644 --- a/src/lib/dates.ts +++ b/src/lib/dates.ts @@ -130,12 +130,39 @@ function unitOf(range: Range, view: ViewRange): ViewRange | null { ); } -export function shiftPeriod(range: Range, view: ViewRange, dir: 1 | -1): Range { +function wholeMonths(range: Range): number { + const after = addDays(range.end, 1); + if (!(range.start.endsWith("-01") && after.endsWith("-01"))) { + return 0; + } + const months = (value: string) => + Number(value.slice(0, 4)) * 12 + Number(value.slice(5, 7)); + return months(after) - months(range.start); +} + +function calendarShift( + range: Range, + view: ViewRange, + dir: 1 | -1 +): Range | null { const unit = unitOf(range, view); if (unit) { const anchor = dir === 1 ? periodOf(range.start, unit).end : range.start; return periodOf(addDays(anchor, dir), unit); } + const months = wholeMonths(range); + if (months === 0) { + return null; + } + const start = addMonths(range.start, dir * months); + return { end: addDays(addMonths(start, months), -1), start }; +} + +export function shiftPeriod(range: Range, view: ViewRange, dir: 1 | -1): Range { + const calendar = calendarShift(range, view, dir); + if (calendar) { + return calendar; + } const days = daysBetween(range.start, range.end) + 1; return { end: addDays(range.end, dir * days), @@ -143,6 +170,18 @@ export function shiftPeriod(range: Range, view: ViewRange, dir: 1 | -1): Range { }; } +export function stepPeriod(range: Range, view: ViewRange, dir: 1 | -1): Range { + const calendar = calendarShift(range, view, dir); + if (calendar) { + return calendar; + } + const unit = isDynamic(view) ? "1M" : view; + const edge = dir === 1 ? range.end : range.start; + const own = periodOf(edge, unit); + const reached = (dir === 1 ? own.end : own.start) === edge; + return reached ? periodOf(addDays(edge, dir), unit) : own; +} + export function daysBetween(a: string, b: string): number { return Math.round((parse(b).getTime() - parse(a).getTime()) / 86_400_000); } diff --git a/src/lib/period.svelte.ts b/src/lib/period.svelte.ts index b9bb780..b4de8e7 100644 --- a/src/lib/period.svelte.ts +++ b/src/lib/period.svelte.ts @@ -5,7 +5,7 @@ import { isDynamic, periodOf, type Range, - shiftPeriod, + stepPeriod, today, type ViewRange, } from "#lib/dates.js"; @@ -23,7 +23,7 @@ export interface Preset { function stored(): Range | null { try { - const value = JSON.parse(localStorage.getItem(STORAGE_KEY) ?? "null"); + const value = JSON.parse(sessionStorage.getItem(STORAGE_KEY) ?? "null"); return ISO_DATE.test(value?.start) && ISO_DATE.test(value?.end) ? value : null; @@ -71,7 +71,7 @@ class Period { const range = rangeFromUrl(url) ?? stored() ?? periodOf(today(), view); this.start = range.start; this.end = range.end; - localStorage.setItem(STORAGE_KEY, JSON.stringify(range)); + sessionStorage.setItem(STORAGE_KEY, JSON.stringify(range)); this.ready = true; } @@ -97,11 +97,11 @@ class Period { } this.start = range.start; this.end = range.end; - localStorage.setItem(STORAGE_KEY, JSON.stringify(range)); + sessionStorage.setItem(STORAGE_KEY, JSON.stringify(range)); } shift(dir: 1 | -1) { - this.set(shiftPeriod(this.range, this.view, dir)); + this.set(stepPeriod(this.range, this.view, dir)); } reset() { @@ -113,8 +113,8 @@ class Period { const { view } = this; const presets: Preset[] = []; if (!isDynamic(view)) { - const prev = shiftPeriod(this.range, view, -1); - const next = shiftPeriod(this.range, view, 1); + const prev = stepPeriod(this.range, view, -1); + const next = stepPeriod(this.range, view, 1); presets.push({ hint: "[", label: periodLabel(prev, view), range: prev }); presets.push({ hint: "]", label: periodLabel(next, view), range: next }); }