262 lines
7.6 KiB
JavaScript
262 lines
7.6 KiB
JavaScript
class TimeManager {
|
|
static ALLOWED_RANGES = ['today', 'yesterday', 'last2days', 'last7days', 'last30days', 'last90days',
|
|
'previous7days', 'previous30days', 'previous90days'];
|
|
|
|
constructor(timezone = 'America/New_York', dayStartsAt = 1) {
|
|
this.timezone = timezone;
|
|
this.dayStartsAt = dayStartsAt;
|
|
}
|
|
|
|
getDayBounds(date) {
|
|
try {
|
|
const now = new Date();
|
|
const targetDate = new Date(date);
|
|
|
|
// For today
|
|
if (
|
|
targetDate.getUTCFullYear() === now.getUTCFullYear() &&
|
|
targetDate.getUTCMonth() === now.getUTCMonth() &&
|
|
targetDate.getUTCDate() === now.getUTCDate()
|
|
) {
|
|
// If current time is before day start (1 AM ET / 6 AM UTC),
|
|
// use previous day's start until now
|
|
const todayStart = new Date(Date.UTC(
|
|
now.getUTCFullYear(),
|
|
now.getUTCMonth(),
|
|
now.getUTCDate(),
|
|
this.dayStartsAt + 5,
|
|
0,
|
|
0,
|
|
0
|
|
));
|
|
|
|
if (now < todayStart) {
|
|
const yesterdayStart = new Date(todayStart);
|
|
yesterdayStart.setUTCDate(yesterdayStart.getUTCDate() - 1);
|
|
return { start: yesterdayStart, end: now };
|
|
}
|
|
|
|
return { start: todayStart, end: now };
|
|
}
|
|
|
|
// For past days, use full 24-hour period
|
|
const normalizedDate = new Date(Date.UTC(
|
|
targetDate.getUTCFullYear(),
|
|
targetDate.getUTCMonth(),
|
|
targetDate.getUTCDate()
|
|
));
|
|
|
|
const dayStart = new Date(normalizedDate);
|
|
dayStart.setUTCHours(this.dayStartsAt + 5, 0, 0, 0);
|
|
|
|
const dayEnd = new Date(dayStart);
|
|
dayEnd.setUTCDate(dayEnd.getUTCDate() + 1);
|
|
|
|
return { start: dayStart, end: dayEnd };
|
|
} catch (error) {
|
|
console.error('Error in getDayBounds:', error);
|
|
throw new Error(`Failed to calculate day bounds: ${error.message}`);
|
|
}
|
|
}
|
|
|
|
getDateRange(period) {
|
|
try {
|
|
const now = new Date();
|
|
const todayBounds = this.getDayBounds(now);
|
|
const end = new Date();
|
|
|
|
switch (period) {
|
|
case 'today':
|
|
return {
|
|
start: todayBounds.start,
|
|
end
|
|
};
|
|
case 'yesterday': {
|
|
const yesterday = new Date(now);
|
|
yesterday.setDate(yesterday.getDate() - 1);
|
|
return this.getDayBounds(yesterday);
|
|
}
|
|
case 'last2days': {
|
|
const twoDaysAgo = new Date(now);
|
|
twoDaysAgo.setDate(twoDaysAgo.getDate() - 2);
|
|
return this.getDayBounds(twoDaysAgo);
|
|
}
|
|
case 'last7days': {
|
|
const start = new Date(now);
|
|
start.setDate(start.getDate() - 6);
|
|
return {
|
|
start: this.getDayBounds(start).start,
|
|
end
|
|
};
|
|
}
|
|
case 'previous7days': {
|
|
const end = new Date(now);
|
|
end.setDate(end.getDate() - 7);
|
|
const start = new Date(end);
|
|
start.setDate(start.getDate() - 6);
|
|
return {
|
|
start: this.getDayBounds(start).start,
|
|
end: this.getDayBounds(end).end
|
|
};
|
|
}
|
|
case 'last30days': {
|
|
const start = new Date(now);
|
|
start.setDate(start.getDate() - 29);
|
|
return {
|
|
start: this.getDayBounds(start).start,
|
|
end
|
|
};
|
|
}
|
|
case 'previous30days': {
|
|
const end = new Date(now);
|
|
end.setDate(end.getDate() - 30);
|
|
const start = new Date(end);
|
|
start.setDate(start.getDate() - 29);
|
|
return {
|
|
start: this.getDayBounds(start).start,
|
|
end: this.getDayBounds(end).end
|
|
};
|
|
}
|
|
case 'last90days': {
|
|
const start = new Date(now);
|
|
start.setDate(start.getDate() - 89);
|
|
return {
|
|
start: this.getDayBounds(start).start,
|
|
end
|
|
};
|
|
}
|
|
case 'previous90days': {
|
|
const end = new Date(now);
|
|
end.setDate(end.getDate() - 90);
|
|
const start = new Date(end);
|
|
start.setDate(start.getDate() - 89);
|
|
return {
|
|
start: this.getDayBounds(start).start,
|
|
end: this.getDayBounds(end).end
|
|
};
|
|
}
|
|
default:
|
|
throw new Error(`Unsupported time period: ${period}`);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error in getDateRange:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
getPreviousPeriod(period) {
|
|
try {
|
|
const now = new Date();
|
|
|
|
switch (period) {
|
|
case 'today':
|
|
return 'yesterday';
|
|
case 'yesterday': {
|
|
// Return bounds for 2 days ago
|
|
const twoDaysAgo = new Date(now);
|
|
twoDaysAgo.setDate(twoDaysAgo.getDate() - 2);
|
|
return this.getDayBounds(twoDaysAgo);
|
|
}
|
|
case 'last7days': {
|
|
// Return bounds for previous 7 days
|
|
const end = new Date(now);
|
|
end.setDate(end.getDate() - 7);
|
|
const start = new Date(end);
|
|
start.setDate(start.getDate() - 7);
|
|
return {
|
|
start: this.getDayBounds(start).start,
|
|
end: this.getDayBounds(end).end
|
|
};
|
|
}
|
|
case 'last30days': {
|
|
const end = new Date(now);
|
|
end.setDate(end.getDate() - 30);
|
|
const start = new Date(end);
|
|
start.setDate(start.getDate() - 30);
|
|
return {
|
|
start: this.getDayBounds(start).start,
|
|
end: this.getDayBounds(end).end
|
|
};
|
|
}
|
|
case 'last90days': {
|
|
const end = new Date(now);
|
|
end.setDate(end.getDate() - 90);
|
|
const start = new Date(end);
|
|
start.setDate(start.getDate() - 90);
|
|
return {
|
|
start: this.getDayBounds(start).start,
|
|
end: this.getDayBounds(end).end
|
|
};
|
|
}
|
|
default:
|
|
throw new Error(`Unsupported time period: ${period}`);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error in getPreviousPeriod:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
getCurrentBusinessDayEnd() {
|
|
try {
|
|
const now = new Date();
|
|
const todayBounds = this.getDayBounds(now);
|
|
|
|
// If current time is before day start (1 AM ET / 6 AM UTC),
|
|
// then we're still in yesterday's business day
|
|
const todayStart = new Date(Date.UTC(
|
|
now.getUTCFullYear(),
|
|
now.getUTCMonth(),
|
|
now.getUTCDate(),
|
|
this.dayStartsAt + 5,
|
|
0,
|
|
0,
|
|
0
|
|
));
|
|
|
|
if (now < todayStart) {
|
|
const yesterdayBounds = this.getDayBounds(new Date(now.getTime() - 24 * 60 * 60 * 1000));
|
|
return yesterdayBounds.end;
|
|
}
|
|
|
|
// Return the earlier of current time or today's end
|
|
return now < todayBounds.end ? now : todayBounds.end;
|
|
} catch (error) {
|
|
console.error('Error in getCurrentBusinessDayEnd:', error);
|
|
return new Date();
|
|
}
|
|
}
|
|
|
|
isValidTimeRange(timeRange) {
|
|
return TimeManager.ALLOWED_RANGES.includes(timeRange);
|
|
}
|
|
|
|
isToday(date) {
|
|
const now = new Date();
|
|
const targetDate = new Date(date);
|
|
return (
|
|
targetDate.getUTCFullYear() === now.getUTCFullYear() &&
|
|
targetDate.getUTCMonth() === now.getUTCMonth() &&
|
|
targetDate.getUTCDate() === now.getUTCDate()
|
|
);
|
|
}
|
|
|
|
formatDate(date) {
|
|
try {
|
|
return date.toLocaleString('en-US', {
|
|
timeZone: this.timezone,
|
|
year: 'numeric',
|
|
month: '2-digit',
|
|
day: '2-digit',
|
|
hour: '2-digit',
|
|
minute: '2-digit',
|
|
second: '2-digit'
|
|
});
|
|
} catch (error) {
|
|
console.error('Error formatting date:', error);
|
|
return date.toISOString();
|
|
}
|
|
}
|
|
}
|
|
|
|
export const createTimeManager = (timezone, dayStartsAt) => new TimeManager(timezone, dayStartsAt);
|