Skip to content

Commit 9cbca5d

Browse files
asizikovCopilot
andcommitted
refactor: share ISO date validation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 8d70792 commit 9cbca5d

4 files changed

Lines changed: 33 additions & 33 deletions

File tree

src/pipeline/aggregators/reportContextAggregator.ts

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { Aggregator } from './base'
22
import type { TokenUsageHeader, TokenUsageRecord } from '../parser'
3+
import { isValidIsoDate } from '../isoDate'
34

45
export type ReportContextResult = {
56
startDate: string | null
@@ -49,19 +50,3 @@ export class ReportContextAggregator implements Aggregator<TokenUsageRecord, Rep
4950
}
5051
}
5152
}
52-
53-
function isValidIsoDate(value: string): boolean {
54-
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value)
55-
if (!match) return false
56-
57-
const year = Number(match[1])
58-
const month = Number(match[2])
59-
const day = Number(match[3])
60-
const normalized = new Date(Date.UTC(year, month - 1, day))
61-
62-
return (
63-
normalized.getUTCFullYear() === year
64-
&& normalized.getUTCMonth() === month - 1
65-
&& normalized.getUTCDate() === day
66-
)
67-
}

src/pipeline/includedCreditsPolicy.ts

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { ReportFormat, ReportFormatMetadata } from './reportAdapters'
2+
import { isValidIsoDate } from './isoDate'
23

34
export type QuotaUnit = 'pru' | 'aic'
45
export type OrganizationIncludedCreditTier = 'business' | 'enterprise'
@@ -37,7 +38,6 @@ export type ReportPeriod = {
3738
const COPILOT_BUSINESS_LABEL = 'Copilot Business'
3839
const COPILOT_ENTERPRISE_LABEL = 'Copilot Enterprise'
3940
const NATIVE_AI_CREDITS_STANDARD_POLICY_START_DATE = '2026-09-01'
40-
const ISO_DATE_PATTERN = /^(\d{4})-(\d{2})-(\d{2})$/
4141

4242
export const TRANSITION_PERIOD_INCLUDED_CREDITS_POLICY = {
4343
id: 'transition-period-billing-preview',
@@ -117,22 +117,6 @@ function getReportFormat(reportMetadataOrFormat: ReportFormat | ReportFormatMeta
117117
: reportMetadataOrFormat.format
118118
}
119119

120-
function isValidIsoDate(value: string): boolean {
121-
const match = ISO_DATE_PATTERN.exec(value)
122-
if (!match) return false
123-
124-
const year = Number(match[1])
125-
const month = Number(match[2])
126-
const day = Number(match[3])
127-
const date = new Date(Date.UTC(year, month - 1, day))
128-
129-
return (
130-
date.getUTCFullYear() === year
131-
&& date.getUTCMonth() === month - 1
132-
&& date.getUTCDate() === day
133-
)
134-
}
135-
136120
function isBeforeIsoDate(value: string | null | undefined, boundary: string): boolean {
137121
if (!value || !isValidIsoDate(value)) return false
138122

src/pipeline/isoDate.test.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { describe, expect, it } from 'vitest'
2+
3+
import { isValidIsoDate } from './isoDate'
4+
5+
describe('isValidIsoDate', () => {
6+
it('accepts only real YYYY-MM-DD calendar dates', () => {
7+
expect(isValidIsoDate('2026-03-01')).toBe(true)
8+
expect(isValidIsoDate('2024-02-29')).toBe(true)
9+
expect(isValidIsoDate('2026-02-30')).toBe(false)
10+
expect(isValidIsoDate('2026-13-01')).toBe(false)
11+
expect(isValidIsoDate('03/01/2026')).toBe(false)
12+
expect(isValidIsoDate(' 2026-03-01 ')).toBe(false)
13+
})
14+
})

src/pipeline/isoDate.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
const ISO_DATE_PATTERN = /^(\d{4})-(\d{2})-(\d{2})$/
2+
3+
export function isValidIsoDate(value: string): boolean {
4+
const match = ISO_DATE_PATTERN.exec(value)
5+
if (!match) return false
6+
7+
const year = Number(match[1])
8+
const month = Number(match[2])
9+
const day = Number(match[3])
10+
const normalized = new Date(Date.UTC(year, month - 1, day))
11+
12+
return (
13+
normalized.getUTCFullYear() === year
14+
&& normalized.getUTCMonth() === month - 1
15+
&& normalized.getUTCDate() === day
16+
)
17+
}

0 commit comments

Comments
 (0)