From d27b5b71dc0048f5a8cc555cf2ac885cc368bb43 Mon Sep 17 00:00:00 2001 From: Adarsh Singh Date: Sun, 7 Jun 2026 22:05:17 +0530 Subject: [PATCH] fix(date-range): compute the default range lazily to avoid a stale year --- utils/dateRange.test.ts | 34 ++++++++++++++++++++++++++++++++-- utils/dateRange.ts | 34 ++++++++++++++++++++-------------- 2 files changed, 52 insertions(+), 16 deletions(-) diff --git a/utils/dateRange.test.ts b/utils/dateRange.test.ts index fb25f61ff..2ad05de09 100644 --- a/utils/dateRange.test.ts +++ b/utils/dateRange.test.ts @@ -1,5 +1,5 @@ -import { describe, it, expect } from 'vitest'; -import { formatDateRange, DEFAULT_DATE_RANGE } from './dateRange'; +import { describe, it, expect, vi, afterEach } from 'vitest'; +import { formatDateRange, getDefaultDateRange, DEFAULT_DATE_RANGE } from './dateRange'; describe('formatDateRange', () => { it('returns full date range for valid 4-digit year', () => { @@ -105,9 +105,39 @@ describe('formatDateRange', () => { expect(typeof result.to).toBe('string'); }); + it('getDefaultDateRange uses the current UTC year', () => { + const currentYear = new Date().getUTCFullYear(); + const range = getDefaultDateRange(); + expect(range.from).toBe(`${currentYear}-01-01T00:00:00Z`); + expect(range.to).toBe(`${currentYear}-12-31T23:59:59Z`); + }); + it('DEFAULT_DATE_RANGE uses current UTC year', () => { const currentYear = new Date().getUTCFullYear(); expect(DEFAULT_DATE_RANGE.from).toBe(`${currentYear}-01-01T00:00:00Z`); expect(DEFAULT_DATE_RANGE.to).toBe(`${currentYear}-12-31T23:59:59Z`); }); }); + +describe('getDefaultDateRange', () => { + afterEach(() => { + vi.useRealTimers(); + }); + + it('reflects the current year lazily rather than a value captured at module load', () => { + vi.useFakeTimers(); + + vi.setSystemTime(new Date('2030-06-15T00:00:00Z')); + expect(getDefaultDateRange()).toEqual({ + from: '2030-01-01T00:00:00Z', + to: '2030-12-31T23:59:59Z', + }); + + // Crossing into a new year must be reflected on the next call. + vi.setSystemTime(new Date('2031-01-02T00:00:00Z')); + expect(getDefaultDateRange()).toEqual({ + from: '2031-01-01T00:00:00Z', + to: '2031-12-31T23:59:59Z', + }); + }); +}); diff --git a/utils/dateRange.ts b/utils/dateRange.ts index d3a17a27a..a0063e656 100644 --- a/utils/dateRange.ts +++ b/utils/dateRange.ts @@ -1,12 +1,3 @@ -/** - * Default fallback date range when year is missing or invalid. - * Uses current year as fallback. - */ -export const DEFAULT_DATE_RANGE = { - from: `${new Date().getUTCFullYear()}-01-01T00:00:00Z`, - to: `${new Date().getUTCFullYear()}-12-31T23:59:59Z`, -}; - /** * Date range with from and to ISO strings for GitHub API queries. */ @@ -15,6 +6,21 @@ export interface DateRange { to: string; } +/** + * Default fallback date range when year is missing or invalid. + * Computed lazily so the current year stays correct after a New Year rollover. + */ +export function getDefaultDateRange(): DateRange { + const year = new Date().getUTCFullYear(); + return { + from: `${year}-01-01T00:00:00Z`, + to: `${year}-12-31T23:59:59Z`, + }; +} + +/** Backward-compatible default range; runtime fallbacks use getDefaultDateRange() so they never go stale. */ +export const DEFAULT_DATE_RANGE: DateRange = getDefaultDateRange(); + /** * Formats a year into a date range for GitHub contributions query. * Returns fallback default ranges when year is missing, partial, or invalid. @@ -25,17 +31,17 @@ export interface DateRange { * @example * formatDateRange("2024") // { from: "2024-01-01T00:00:00Z", to: "2024-12-31T23:59:59Z" } * formatDateRange("24") // { from: "2024-01-01T00:00:00Z", to: "2024-12-31T23:59:59Z" } - * formatDateRange("") // returns DEFAULT_DATE_RANGE - * formatDateRange(undefined) // returns DEFAULT_DATE_RANGE + * formatDateRange("") // returns the current-year default range + * formatDateRange(undefined) // returns the current-year default range */ export function formatDateRange(year?: string | number | null): DateRange { if (year === undefined || year === null) { - return { ...DEFAULT_DATE_RANGE }; + return getDefaultDateRange(); } const trimmedYear = String(year).trim(); if (trimmedYear === '') { - return { ...DEFAULT_DATE_RANGE }; + return getDefaultDateRange(); } // Handle partial years (1 or 2 digits): assume 20xx @@ -56,7 +62,7 @@ export function formatDateRange(year?: string | number | null): DateRange { const isValidYear = !isNaN(fullYear) && fullYear >= 2008 && fullYear <= currentYear + 5; if (!isValidYear) { - return { ...DEFAULT_DATE_RANGE }; + return getDefaultDateRange(); } return {