fix: harden backend stability and test production utilities
This commit is contained in:
92
backend/utils/datetime.js
Normal file
92
backend/utils/datetime.js
Normal file
@@ -0,0 +1,92 @@
|
||||
function parseDateTimeValue(value) {
|
||||
if (!value || typeof value !== 'string') return null;
|
||||
|
||||
const directDate = new Date(value);
|
||||
if (!Number.isNaN(directDate.getTime())) return directDate;
|
||||
|
||||
const normalizedDate = new Date(value.replace(' ', 'T'));
|
||||
return Number.isNaN(normalizedDate.getTime()) ? null : normalizedDate;
|
||||
}
|
||||
|
||||
function formatDateTimeForSqlite(date = new Date()) {
|
||||
const target = date instanceof Date ? date : new Date(date);
|
||||
const year = target.getFullYear();
|
||||
const month = String(target.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(target.getDate()).padStart(2, '0');
|
||||
const hours = String(target.getHours()).padStart(2, '0');
|
||||
const minutes = String(target.getMinutes()).padStart(2, '0');
|
||||
const seconds = String(target.getSeconds()).padStart(2, '0');
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||||
}
|
||||
|
||||
function getDateKeyFromDate(date = new Date()) {
|
||||
const target = date instanceof Date ? date : new Date(date);
|
||||
if (Number.isNaN(target.getTime())) return null;
|
||||
const year = target.getFullYear();
|
||||
const month = String(target.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(target.getDate()).padStart(2, '0');
|
||||
return `${year}-${month}-${day}`;
|
||||
}
|
||||
|
||||
function getRecentDateKeys(days = 30, now = new Date()) {
|
||||
const safeDays = Math.max(1, Math.floor(Number(days) || 30));
|
||||
const keys = [];
|
||||
for (let i = safeDays - 1; i >= 0; i -= 1) {
|
||||
const date = new Date(now.getTime());
|
||||
date.setDate(date.getDate() - i);
|
||||
const key = getDateKeyFromDate(date);
|
||||
if (key) keys.push(key);
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
function getNextDownloadResetTime(lastResetAt, resetCycle) {
|
||||
const baseDate = parseDateTimeValue(lastResetAt);
|
||||
if (!baseDate) return null;
|
||||
|
||||
const next = new Date(baseDate.getTime());
|
||||
if (resetCycle === 'daily') next.setDate(next.getDate() + 1);
|
||||
else if (resetCycle === 'weekly') next.setDate(next.getDate() + 7);
|
||||
else if (resetCycle === 'monthly') next.setMonth(next.getMonth() + 1);
|
||||
else return null;
|
||||
return next;
|
||||
}
|
||||
|
||||
function normalizeTimeHHmm(value) {
|
||||
if (typeof value !== 'string') return null;
|
||||
const match = value.trim().match(/^(\d{2}):(\d{2})$/);
|
||||
if (!match) return null;
|
||||
const hours = Number(match[1]);
|
||||
const minutes = Number(match[2]);
|
||||
if (hours < 0 || hours > 23 || minutes < 0 || minutes > 59) return null;
|
||||
return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`;
|
||||
}
|
||||
|
||||
function toMinutesOfDay(hhmm) {
|
||||
const normalized = normalizeTimeHHmm(hhmm);
|
||||
if (!normalized) return null;
|
||||
const [hours, minutes] = normalized.split(':').map(Number);
|
||||
return hours * 60 + minutes;
|
||||
}
|
||||
|
||||
function isCurrentTimeInWindow(startTime, endTime, now = new Date()) {
|
||||
const start = toMinutesOfDay(startTime);
|
||||
const end = toMinutesOfDay(endTime);
|
||||
if (start === null || end === null) return true;
|
||||
|
||||
const nowMinutes = now.getHours() * 60 + now.getMinutes();
|
||||
if (start === end) return true;
|
||||
if (start < end) return nowMinutes >= start && nowMinutes < end;
|
||||
return nowMinutes >= start || nowMinutes < end;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
parseDateTimeValue,
|
||||
formatDateTimeForSqlite,
|
||||
getDateKeyFromDate,
|
||||
getRecentDateKeys,
|
||||
getNextDownloadResetTime,
|
||||
normalizeTimeHHmm,
|
||||
toMinutesOfDay,
|
||||
isCurrentTimeInWindow
|
||||
};
|
||||
Reference in New Issue
Block a user