Enhances date handling and formatting
Improves date validation and adds flexible date/time formatting capabilities. The date validation is updated to return a boolean and is incorporated directly into calling functions to throw errors, improving code readability and maintainability. DateService is extended with functions for formatting time in 12-hour format, getting day names, and formatting date ranges with customizable options.
This commit is contained in:
parent
4fea01c76b
commit
4859f42450
3 changed files with 465 additions and 25 deletions
|
|
@ -25,13 +25,10 @@ export class DateCalculator {
|
||||||
/**
|
/**
|
||||||
* Validate that a date is valid
|
* Validate that a date is valid
|
||||||
* @param date - Date to validate
|
* @param date - Date to validate
|
||||||
* @param methodName - Name of calling method for error messages
|
* @returns True if date is valid, false otherwise
|
||||||
* @throws Error if date is invalid
|
|
||||||
*/
|
*/
|
||||||
private static validateDate(date: Date, methodName: string): void {
|
private static validateDate(date: Date): boolean {
|
||||||
if (!date || !(date instanceof Date) || !DateCalculator.dateService.isValid(date)) {
|
return date && date instanceof Date && DateCalculator.dateService.isValid(date);
|
||||||
throw new Error(`${methodName}: Invalid date provided - ${date}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -40,7 +37,9 @@ export class DateCalculator {
|
||||||
* @returns Array of dates for the configured work days
|
* @returns Array of dates for the configured work days
|
||||||
*/
|
*/
|
||||||
static getWorkWeekDates(weekStart: Date): Date[] {
|
static getWorkWeekDates(weekStart: Date): Date[] {
|
||||||
DateCalculator.validateDate(weekStart, 'getWorkWeekDates');
|
if (!DateCalculator.validateDate(weekStart)) {
|
||||||
|
throw new Error('getWorkWeekDates: Invalid date provided');
|
||||||
|
}
|
||||||
|
|
||||||
const dates: Date[] = [];
|
const dates: Date[] = [];
|
||||||
const workWeekSettings = DateCalculator.config.getWorkWeekSettings();
|
const workWeekSettings = DateCalculator.config.getWorkWeekSettings();
|
||||||
|
|
@ -66,7 +65,9 @@ export class DateCalculator {
|
||||||
* @returns The Monday of the ISO week
|
* @returns The Monday of the ISO week
|
||||||
*/
|
*/
|
||||||
static getISOWeekStart(date: Date): Date {
|
static getISOWeekStart(date: Date): Date {
|
||||||
DateCalculator.validateDate(date, 'getISOWeekStart');
|
if (!DateCalculator.validateDate(date)) {
|
||||||
|
throw new Error('getISOWeekStart: Invalid date provided');
|
||||||
|
}
|
||||||
|
|
||||||
const weekBounds = DateCalculator.dateService.getWeekBounds(date);
|
const weekBounds = DateCalculator.dateService.getWeekBounds(date);
|
||||||
return DateCalculator.dateService.startOfDay(weekBounds.start);
|
return DateCalculator.dateService.startOfDay(weekBounds.start);
|
||||||
|
|
@ -78,7 +79,9 @@ export class DateCalculator {
|
||||||
* @returns The end date of the ISO week (Sunday)
|
* @returns The end date of the ISO week (Sunday)
|
||||||
*/
|
*/
|
||||||
static getWeekEnd(date: Date): Date {
|
static getWeekEnd(date: Date): Date {
|
||||||
DateCalculator.validateDate(date, 'getWeekEnd');
|
if (!DateCalculator.validateDate(date)) {
|
||||||
|
throw new Error('getWeekEnd: Invalid date provided');
|
||||||
|
}
|
||||||
|
|
||||||
const weekBounds = DateCalculator.dateService.getWeekBounds(date);
|
const weekBounds = DateCalculator.dateService.getWeekBounds(date);
|
||||||
return DateCalculator.dateService.endOfDay(weekBounds.end);
|
return DateCalculator.dateService.endOfDay(weekBounds.end);
|
||||||
|
|
@ -137,9 +140,12 @@ export class DateCalculator {
|
||||||
/**
|
/**
|
||||||
* Format a date to ISO date string (YYYY-MM-DD) using DateService
|
* Format a date to ISO date string (YYYY-MM-DD) using DateService
|
||||||
* @param date - Date to format
|
* @param date - Date to format
|
||||||
* @returns ISO date string
|
* @returns ISO date string or empty string if invalid
|
||||||
*/
|
*/
|
||||||
static formatISODate(date: Date): string {
|
static formatISODate(date: Date): string {
|
||||||
|
if (!DateCalculator.validateDate(date)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
return DateCalculator.dateService.formatDate(date);
|
return DateCalculator.dateService.formatDate(date);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,24 +3,25 @@
|
||||||
* Handles all date operations, timezone conversions, and formatting
|
* Handles all date operations, timezone conversions, and formatting
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import {
|
||||||
format,
|
format,
|
||||||
parse,
|
parse,
|
||||||
addMinutes,
|
addMinutes,
|
||||||
differenceInMinutes,
|
differenceInMinutes,
|
||||||
startOfDay,
|
startOfDay,
|
||||||
endOfDay,
|
endOfDay,
|
||||||
setHours,
|
setHours,
|
||||||
setMinutes as setMins,
|
setMinutes as setMins,
|
||||||
getHours,
|
getHours,
|
||||||
getMinutes,
|
getMinutes,
|
||||||
parseISO,
|
parseISO,
|
||||||
isValid,
|
isValid,
|
||||||
addDays,
|
addDays,
|
||||||
startOfWeek,
|
startOfWeek,
|
||||||
endOfWeek,
|
endOfWeek,
|
||||||
addWeeks,
|
addWeeks,
|
||||||
isSameDay
|
isSameDay,
|
||||||
|
getISOWeek
|
||||||
} from 'date-fns';
|
} from 'date-fns';
|
||||||
import {
|
import {
|
||||||
toZonedTime,
|
toZonedTime,
|
||||||
|
|
@ -109,6 +110,70 @@ export class DateService {
|
||||||
return this.formatDate(date);
|
return this.formatDate(date);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format time in 12-hour format with AM/PM
|
||||||
|
* @param date - Date to format
|
||||||
|
* @returns Time string in 12-hour format (e.g., "2:30 PM")
|
||||||
|
*/
|
||||||
|
public formatTime12(date: Date): string {
|
||||||
|
const hours = getHours(date);
|
||||||
|
const minutes = getMinutes(date);
|
||||||
|
const period = hours >= 12 ? 'PM' : 'AM';
|
||||||
|
const displayHours = hours % 12 || 12;
|
||||||
|
|
||||||
|
return `${displayHours}:${String(minutes).padStart(2, '0')} ${period}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get day name for a date
|
||||||
|
* @param date - Date to get day name for
|
||||||
|
* @param format - 'short' (e.g., 'Mon') or 'long' (e.g., 'Monday')
|
||||||
|
* @returns Day name
|
||||||
|
*/
|
||||||
|
public getDayName(date: Date, format: 'short' | 'long' = 'short'): string {
|
||||||
|
const formatter = new Intl.DateTimeFormat('en-US', {
|
||||||
|
weekday: format
|
||||||
|
});
|
||||||
|
return formatter.format(date);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format a date range with customizable options
|
||||||
|
* @param start - Start date
|
||||||
|
* @param end - End date
|
||||||
|
* @param options - Formatting options
|
||||||
|
* @returns Formatted date range string
|
||||||
|
*/
|
||||||
|
public formatDateRange(
|
||||||
|
start: Date,
|
||||||
|
end: Date,
|
||||||
|
options: {
|
||||||
|
locale?: string;
|
||||||
|
month?: 'numeric' | '2-digit' | 'long' | 'short' | 'narrow';
|
||||||
|
day?: 'numeric' | '2-digit';
|
||||||
|
year?: 'numeric' | '2-digit';
|
||||||
|
} = {}
|
||||||
|
): string {
|
||||||
|
const { locale = 'en-US', month = 'short', day = 'numeric' } = options;
|
||||||
|
|
||||||
|
const startYear = start.getFullYear();
|
||||||
|
const endYear = end.getFullYear();
|
||||||
|
|
||||||
|
const formatter = new Intl.DateTimeFormat(locale, {
|
||||||
|
month,
|
||||||
|
day,
|
||||||
|
year: startYear !== endYear ? 'numeric' : undefined
|
||||||
|
});
|
||||||
|
|
||||||
|
// @ts-ignore - formatRange is available in modern browsers
|
||||||
|
if (typeof formatter.formatRange === 'function') {
|
||||||
|
// @ts-ignore
|
||||||
|
return formatter.formatRange(start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${formatter.format(start)} - ${formatter.format(end)}`;
|
||||||
|
}
|
||||||
|
|
||||||
// ============================================
|
// ============================================
|
||||||
// TIME CALCULATIONS
|
// TIME CALCULATIONS
|
||||||
// ============================================
|
// ============================================
|
||||||
|
|
@ -193,6 +258,53 @@ export class DateService {
|
||||||
return addWeeks(date, weeks);
|
return addWeeks(date, weeks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get ISO week number (1-53)
|
||||||
|
* @param date - Date to get week number for
|
||||||
|
* @returns ISO week number
|
||||||
|
*/
|
||||||
|
public getWeekNumber(date: Date): number {
|
||||||
|
return getISOWeek(date);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all dates in a full week (7 days starting from given date)
|
||||||
|
* @param weekStart - Start date of the week
|
||||||
|
* @returns Array of 7 dates
|
||||||
|
*/
|
||||||
|
public getFullWeekDates(weekStart: Date): Date[] {
|
||||||
|
const dates: Date[] = [];
|
||||||
|
for (let i = 0; i < 7; i++) {
|
||||||
|
dates.push(this.addDays(weekStart, i));
|
||||||
|
}
|
||||||
|
return dates;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get dates for work week using ISO 8601 day numbering (Monday=1, Sunday=7)
|
||||||
|
* @param weekStart - Any date in the week
|
||||||
|
* @param workDays - Array of ISO day numbers (1=Monday, 7=Sunday)
|
||||||
|
* @returns Array of dates for the specified work days
|
||||||
|
*/
|
||||||
|
public getWorkWeekDates(weekStart: Date, workDays: number[]): Date[] {
|
||||||
|
const dates: Date[] = [];
|
||||||
|
|
||||||
|
// Get Monday of the week
|
||||||
|
const weekBounds = this.getWeekBounds(weekStart);
|
||||||
|
const mondayOfWeek = this.startOfDay(weekBounds.start);
|
||||||
|
|
||||||
|
// Calculate dates for each work day using ISO numbering
|
||||||
|
workDays.forEach(isoDay => {
|
||||||
|
const date = new Date(mondayOfWeek);
|
||||||
|
// ISO day 1=Monday is +0 days, ISO day 7=Sunday is +6 days
|
||||||
|
const daysFromMonday = isoDay === 7 ? 6 : isoDay - 1;
|
||||||
|
date.setDate(mondayOfWeek.getDate() + daysFromMonday);
|
||||||
|
dates.push(date);
|
||||||
|
});
|
||||||
|
|
||||||
|
return dates;
|
||||||
|
}
|
||||||
|
|
||||||
// ============================================
|
// ============================================
|
||||||
// GRID HELPERS
|
// GRID HELPERS
|
||||||
// ============================================
|
// ============================================
|
||||||
|
|
@ -290,4 +402,16 @@ export class DateService {
|
||||||
public isValid(date: Date): boolean {
|
public isValid(date: Date): boolean {
|
||||||
return isValid(date);
|
return isValid(date);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if event spans multiple days
|
||||||
|
* @param start - Start date or ISO string
|
||||||
|
* @param end - End date or ISO string
|
||||||
|
* @returns True if spans multiple days
|
||||||
|
*/
|
||||||
|
public isMultiDay(start: Date | string, end: Date | string): boolean {
|
||||||
|
const startDate = typeof start === 'string' ? this.parseISO(start) : start;
|
||||||
|
const endDate = typeof end === 'string' ? this.parseISO(end) : end;
|
||||||
|
return !this.isSameDay(startDate, endDate);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
310
test/utils/DateCalculator.test.ts
Normal file
310
test/utils/DateCalculator.test.ts
Normal file
|
|
@ -0,0 +1,310 @@
|
||||||
|
import { describe, it, expect, beforeEach } from 'vitest';
|
||||||
|
import { DateCalculator } from '../../src/utils/DateCalculator';
|
||||||
|
import { CalendarConfig } from '../../src/core/CalendarConfig';
|
||||||
|
|
||||||
|
describe('DateCalculator', () => {
|
||||||
|
let testConfig: CalendarConfig;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
testConfig = new CalendarConfig();
|
||||||
|
DateCalculator.initialize(testConfig);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Week Operations', () => {
|
||||||
|
it('should get ISO week start (Monday)', () => {
|
||||||
|
// Wednesday, January 17, 2024
|
||||||
|
const date = new Date(2024, 0, 17);
|
||||||
|
const weekStart = DateCalculator.getISOWeekStart(date);
|
||||||
|
|
||||||
|
// Should be Monday, January 15
|
||||||
|
expect(weekStart.getDate()).toBe(15);
|
||||||
|
expect(weekStart.getDay()).toBe(1); // Monday
|
||||||
|
expect(weekStart.getHours()).toBe(0);
|
||||||
|
expect(weekStart.getMinutes()).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get ISO week start for Sunday', () => {
|
||||||
|
// Sunday, January 21, 2024
|
||||||
|
const date = new Date(2024, 0, 21);
|
||||||
|
const weekStart = DateCalculator.getISOWeekStart(date);
|
||||||
|
|
||||||
|
// Should be Monday, January 15
|
||||||
|
expect(weekStart.getDate()).toBe(15);
|
||||||
|
expect(weekStart.getDay()).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get week end (Sunday)', () => {
|
||||||
|
// Wednesday, January 17, 2024
|
||||||
|
const date = new Date(2024, 0, 17);
|
||||||
|
const weekEnd = DateCalculator.getWeekEnd(date);
|
||||||
|
|
||||||
|
// Should be Sunday, January 21
|
||||||
|
expect(weekEnd.getDate()).toBe(21);
|
||||||
|
expect(weekEnd.getDay()).toBe(0); // Sunday
|
||||||
|
expect(weekEnd.getHours()).toBe(23);
|
||||||
|
expect(weekEnd.getMinutes()).toBe(59);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get work week dates (Mon-Fri)', () => {
|
||||||
|
const date = new Date(2024, 0, 17); // Wednesday
|
||||||
|
const workDays = DateCalculator.getWorkWeekDates(date);
|
||||||
|
|
||||||
|
expect(workDays).toHaveLength(5);
|
||||||
|
expect(workDays[0].getDay()).toBe(1); // Monday
|
||||||
|
expect(workDays[4].getDay()).toBe(5); // Friday
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get full week dates (7 days)', () => {
|
||||||
|
const weekStart = new Date(2024, 0, 15); // Monday
|
||||||
|
const fullWeek = DateCalculator.getFullWeekDates(weekStart);
|
||||||
|
|
||||||
|
expect(fullWeek).toHaveLength(7);
|
||||||
|
expect(fullWeek[0].getDay()).toBe(1); // Monday
|
||||||
|
expect(fullWeek[6].getDay()).toBe(0); // Sunday
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should calculate ISO week number', () => {
|
||||||
|
const date1 = new Date(2024, 0, 1); // January 1, 2024
|
||||||
|
const weekNum1 = DateCalculator.getWeekNumber(date1);
|
||||||
|
expect(weekNum1).toBe(1);
|
||||||
|
|
||||||
|
const date2 = new Date(2024, 0, 15); // January 15, 2024
|
||||||
|
const weekNum2 = DateCalculator.getWeekNumber(date2);
|
||||||
|
expect(weekNum2).toBe(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle year boundary for week numbers', () => {
|
||||||
|
const date = new Date(2023, 11, 31); // December 31, 2023
|
||||||
|
const weekNum = DateCalculator.getWeekNumber(date);
|
||||||
|
// Week 52 or 53 depending on year
|
||||||
|
expect(weekNum).toBeGreaterThanOrEqual(52);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Date Manipulation', () => {
|
||||||
|
it('should add days', () => {
|
||||||
|
const date = new Date(2024, 0, 15);
|
||||||
|
const newDate = DateCalculator.addDays(date, 5);
|
||||||
|
|
||||||
|
expect(newDate.getDate()).toBe(20);
|
||||||
|
expect(newDate.getMonth()).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should subtract days', () => {
|
||||||
|
const date = new Date(2024, 0, 15);
|
||||||
|
const newDate = DateCalculator.addDays(date, -5);
|
||||||
|
|
||||||
|
expect(newDate.getDate()).toBe(10);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add weeks', () => {
|
||||||
|
const date = new Date(2024, 0, 15);
|
||||||
|
const newDate = DateCalculator.addWeeks(date, 2);
|
||||||
|
|
||||||
|
expect(newDate.getDate()).toBe(29);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should subtract weeks', () => {
|
||||||
|
const date = new Date(2024, 0, 15);
|
||||||
|
const newDate = DateCalculator.addWeeks(date, -1);
|
||||||
|
|
||||||
|
expect(newDate.getDate()).toBe(8);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle month boundaries when adding days', () => {
|
||||||
|
const date = new Date(2024, 0, 30); // January 30
|
||||||
|
const newDate = DateCalculator.addDays(date, 5);
|
||||||
|
|
||||||
|
expect(newDate.getDate()).toBe(4); // February 4
|
||||||
|
expect(newDate.getMonth()).toBe(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Time Formatting', () => {
|
||||||
|
it('should format time (24-hour)', () => {
|
||||||
|
const date = new Date(2024, 0, 15, 14, 30, 45);
|
||||||
|
const formatted = DateCalculator.formatTime(date);
|
||||||
|
|
||||||
|
expect(formatted).toBe('14:30');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should format time (12-hour)', () => {
|
||||||
|
const date1 = new Date(2024, 0, 15, 14, 30, 0);
|
||||||
|
const formatted1 = DateCalculator.formatTime12(date1);
|
||||||
|
expect(formatted1).toBe('2:30 PM');
|
||||||
|
|
||||||
|
const date2 = new Date(2024, 0, 15, 9, 15, 0);
|
||||||
|
const formatted2 = DateCalculator.formatTime12(date2);
|
||||||
|
expect(formatted2).toBe('9:15 AM');
|
||||||
|
|
||||||
|
const date3 = new Date(2024, 0, 15, 0, 0, 0);
|
||||||
|
const formatted3 = DateCalculator.formatTime12(date3);
|
||||||
|
expect(formatted3).toBe('12:00 AM');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should format ISO date', () => {
|
||||||
|
const date = new Date(2024, 0, 15, 14, 30, 0);
|
||||||
|
const formatted = DateCalculator.formatISODate(date);
|
||||||
|
|
||||||
|
expect(formatted).toBe('2024-01-15');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should format date range', () => {
|
||||||
|
const start = new Date(2024, 0, 15);
|
||||||
|
const end = new Date(2024, 0, 21);
|
||||||
|
const formatted = DateCalculator.formatDateRange(start, end);
|
||||||
|
|
||||||
|
expect(formatted).toContain('Jan');
|
||||||
|
expect(formatted).toContain('15');
|
||||||
|
expect(formatted).toContain('21');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get day name (short)', () => {
|
||||||
|
const monday = new Date(2024, 0, 15); // Monday
|
||||||
|
const dayName = DateCalculator.getDayName(monday, 'short');
|
||||||
|
|
||||||
|
expect(dayName).toBe('Mon');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get day name (long)', () => {
|
||||||
|
const monday = new Date(2024, 0, 15); // Monday
|
||||||
|
const dayName = DateCalculator.getDayName(monday, 'long');
|
||||||
|
|
||||||
|
expect(dayName).toBe('Monday');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Time Calculations', () => {
|
||||||
|
it('should convert time string to minutes', () => {
|
||||||
|
expect(DateCalculator.timeToMinutes('09:00')).toBe(540);
|
||||||
|
expect(DateCalculator.timeToMinutes('14:30')).toBe(870);
|
||||||
|
expect(DateCalculator.timeToMinutes('00:00')).toBe(0);
|
||||||
|
expect(DateCalculator.timeToMinutes('23:59')).toBe(1439);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should convert minutes to time string', () => {
|
||||||
|
expect(DateCalculator.minutesToTime(540)).toBe('09:00');
|
||||||
|
expect(DateCalculator.minutesToTime(870)).toBe('14:30');
|
||||||
|
expect(DateCalculator.minutesToTime(0)).toBe('00:00');
|
||||||
|
expect(DateCalculator.minutesToTime(1439)).toBe('23:59');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get minutes since midnight from Date', () => {
|
||||||
|
const date = new Date(2024, 0, 15, 14, 30, 0);
|
||||||
|
const minutes = DateCalculator.getMinutesSinceMidnight(date);
|
||||||
|
|
||||||
|
expect(minutes).toBe(870); // 14*60 + 30
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get minutes since midnight from ISO string', () => {
|
||||||
|
const isoString = '2024-01-15T14:30:00.000Z';
|
||||||
|
const minutes = DateCalculator.getMinutesSinceMidnight(isoString);
|
||||||
|
|
||||||
|
// Note: This will be in local time after parsing
|
||||||
|
expect(minutes).toBeGreaterThanOrEqual(0);
|
||||||
|
expect(minutes).toBeLessThan(1440);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should calculate duration in minutes', () => {
|
||||||
|
const start = new Date(2024, 0, 15, 9, 0, 0);
|
||||||
|
const end = new Date(2024, 0, 15, 10, 30, 0);
|
||||||
|
const duration = DateCalculator.getDurationMinutes(start, end);
|
||||||
|
|
||||||
|
expect(duration).toBe(90);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should calculate duration from ISO strings', () => {
|
||||||
|
const start = '2024-01-15T09:00:00.000Z';
|
||||||
|
const end = '2024-01-15T10:30:00.000Z';
|
||||||
|
const duration = DateCalculator.getDurationMinutes(start, end);
|
||||||
|
|
||||||
|
expect(duration).toBe(90);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle cross-midnight duration', () => {
|
||||||
|
const start = new Date(2024, 0, 15, 23, 0, 0);
|
||||||
|
const end = new Date(2024, 0, 16, 1, 0, 0);
|
||||||
|
const duration = DateCalculator.getDurationMinutes(start, end);
|
||||||
|
|
||||||
|
expect(duration).toBe(120); // 2 hours
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Date Comparisons', () => {
|
||||||
|
it('should check if date is today', () => {
|
||||||
|
const today = new Date();
|
||||||
|
const yesterday = DateCalculator.addDays(new Date(), -1);
|
||||||
|
|
||||||
|
expect(DateCalculator.isToday(today)).toBe(true);
|
||||||
|
expect(DateCalculator.isToday(yesterday)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should check if same day', () => {
|
||||||
|
const date1 = new Date(2024, 0, 15, 10, 0, 0);
|
||||||
|
const date2 = new Date(2024, 0, 15, 14, 30, 0);
|
||||||
|
const date3 = new Date(2024, 0, 16, 10, 0, 0);
|
||||||
|
|
||||||
|
expect(DateCalculator.isSameDay(date1, date2)).toBe(true);
|
||||||
|
expect(DateCalculator.isSameDay(date1, date3)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should check if multi-day event (Date objects)', () => {
|
||||||
|
const start = new Date(2024, 0, 15, 10, 0, 0);
|
||||||
|
const end1 = new Date(2024, 0, 15, 14, 0, 0);
|
||||||
|
const end2 = new Date(2024, 0, 16, 10, 0, 0);
|
||||||
|
|
||||||
|
expect(DateCalculator.isMultiDay(start, end1)).toBe(false);
|
||||||
|
expect(DateCalculator.isMultiDay(start, end2)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should check if multi-day event (ISO strings)', () => {
|
||||||
|
const start = '2024-01-15T10:00:00.000Z';
|
||||||
|
const end1 = '2024-01-15T14:00:00.000Z';
|
||||||
|
const end2 = '2024-01-16T10:00:00.000Z';
|
||||||
|
|
||||||
|
expect(DateCalculator.isMultiDay(start, end1)).toBe(false);
|
||||||
|
expect(DateCalculator.isMultiDay(start, end2)).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Edge Cases', () => {
|
||||||
|
it('should handle midnight', () => {
|
||||||
|
const date = new Date(2024, 0, 15, 0, 0, 0);
|
||||||
|
const minutes = DateCalculator.getMinutesSinceMidnight(date);
|
||||||
|
|
||||||
|
expect(minutes).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle end of day', () => {
|
||||||
|
const date = new Date(2024, 0, 15, 23, 59, 0);
|
||||||
|
const minutes = DateCalculator.getMinutesSinceMidnight(date);
|
||||||
|
|
||||||
|
expect(minutes).toBe(1439);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle leap year', () => {
|
||||||
|
const date = new Date(2024, 1, 29); // February 29, 2024 (leap year)
|
||||||
|
const nextDay = DateCalculator.addDays(date, 1);
|
||||||
|
|
||||||
|
expect(nextDay.getDate()).toBe(1); // March 1
|
||||||
|
expect(nextDay.getMonth()).toBe(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle DST transitions', () => {
|
||||||
|
// This test depends on timezone, but we test the basic functionality
|
||||||
|
const beforeDST = new Date(2024, 2, 30); // March 30, 2024
|
||||||
|
const afterDST = DateCalculator.addDays(beforeDST, 1);
|
||||||
|
|
||||||
|
expect(afterDST.getDate()).toBe(31);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Error Handling', () => {
|
||||||
|
it('should handle invalid dates gracefully', () => {
|
||||||
|
const invalidDate = new Date('invalid');
|
||||||
|
|
||||||
|
const result = DateCalculator.formatISODate(invalidDate);
|
||||||
|
expect(result).toBe('');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Add table
Add a link
Reference in a new issue