chronoshift
Version:
A tiny library for shifting time with timezones
272 lines (271 loc) • 7.75 kB
JavaScript
import { fromDate, startOfWeek } from '@internationalized/date';
function adjustDay(day) {
return (day + 6) % 7;
}
function floorTo(n, roundTo) {
return Math.floor(n / roundTo) * roundTo;
}
function timeShifterFiller(tm) {
const { floor, shift } = tm;
return Object.assign(Object.assign({}, tm), { ceil: (dt, tz) => {
const floored = floor(dt, tz);
if (floored.valueOf() === dt.valueOf())
return dt;
return shift(floored, tz, 1);
} });
}
export const second = timeShifterFiller({
canonicalLength: 1000,
siblings: 60,
floor: (dt, _tz) => {
dt = new Date(dt.valueOf());
dt.setUTCMilliseconds(0);
return dt;
},
round: (dt, roundTo, _tz) => {
const cur = dt.getUTCSeconds();
const adj = floorTo(cur, roundTo);
if (cur !== adj)
dt.setUTCSeconds(adj);
return dt;
},
shift: (dt, _tz, step) => {
dt = new Date(dt.valueOf());
dt.setUTCSeconds(dt.getUTCSeconds() + step);
return dt;
},
});
export const minute = timeShifterFiller({
canonicalLength: 60000,
siblings: 60,
floor: (dt, _tz) => {
dt = new Date(dt.valueOf());
dt.setUTCSeconds(0, 0);
return dt;
},
round: (dt, roundTo, _tz) => {
const cur = dt.getUTCMinutes();
const adj = floorTo(cur, roundTo);
if (cur !== adj)
dt.setUTCMinutes(adj);
return dt;
},
shift: (dt, _tz, step) => {
dt = new Date(dt.valueOf());
dt.setUTCMinutes(dt.getUTCMinutes() + step);
return dt;
},
});
function hourMove(dt, _tz, step) {
dt = new Date(dt.valueOf());
dt.setUTCHours(dt.getUTCHours() + step);
return dt;
}
export const hour = timeShifterFiller({
canonicalLength: 3600000,
siblings: 24,
floor: (dt, tz) => {
if (tz.isUTC()) {
dt = new Date(dt.valueOf());
dt.setUTCMinutes(0, 0, 0);
return dt;
}
else {
return fromDate(dt, tz.toString()).set({ second: 0, minute: 0, millisecond: 0 }).toDate();
}
},
shift: hourMove,
round: (dt, roundTo, tz) => {
if (tz.isUTC()) {
const cur = dt.getUTCHours();
const adj = floorTo(cur, roundTo);
if (cur !== adj)
dt.setUTCHours(adj);
}
else {
const cur = fromDate(dt, tz.toString()).hour;
const adj = floorTo(cur, roundTo);
if (cur !== adj)
return hourMove(dt, tz, adj - cur);
}
return dt;
},
});
export const day = timeShifterFiller({
canonicalLength: 24 * 3600000,
siblings: 30,
floor: (dt, tz) => {
if (tz.isUTC()) {
dt = new Date(dt.valueOf());
dt.setUTCHours(0, 0, 0, 0);
return dt;
}
else {
return fromDate(dt, tz.toString())
.set({ hour: 0, second: 0, minute: 0, millisecond: 0 })
.toDate();
}
},
shift: (dt, tz, step) => {
if (tz.isUTC()) {
dt = new Date(dt.valueOf());
dt.setUTCDate(dt.getUTCDate() + step);
return dt;
}
else {
return fromDate(dt, tz.toString()).add({ days: step }).toDate();
}
},
round: (dt, roundTo, tz) => {
if (tz.isUTC()) {
const cur = dt.getUTCDate() - 1;
const adj = floorTo(cur, roundTo);
if (cur !== adj)
dt.setUTCDate(adj + 1);
}
else {
const cur = fromDate(dt, tz.toString()).day - 1;
const adj = floorTo(cur, roundTo);
if (cur !== adj)
return fromDate(dt, tz.toString())
.set({ day: adj + 1 })
.toDate();
}
return dt;
},
});
export const week = timeShifterFiller({
canonicalLength: 7 * 24 * 3600000,
siblings: 52,
floor: (dt, tz) => {
if (tz.isUTC()) {
dt = new Date(dt.valueOf());
dt.setUTCHours(0, 0, 0, 0);
dt.setUTCDate(dt.getUTCDate() - adjustDay(dt.getUTCDay()));
}
else {
const zd = fromDate(dt, tz.toString());
return startOfWeek(zd.set({ hour: 0, second: 0, minute: 0, millisecond: 0 }), 'fr-FR').toDate();
}
return dt;
},
shift: (dt, tz, step) => {
if (tz.isUTC()) {
dt = new Date(dt.valueOf());
dt.setUTCDate(dt.getUTCDate() + step * 7);
return dt;
}
else {
return fromDate(dt, tz.toString()).add({ weeks: step }).toDate();
}
},
round: () => {
throw new Error('missing week round');
},
});
function monthShift(dt, tz, step) {
if (tz.isUTC()) {
dt = new Date(dt.valueOf());
dt.setUTCMonth(dt.getUTCMonth() + step);
return dt;
}
else {
return fromDate(dt, tz.toString()).add({ months: step }).toDate();
}
}
export const month = timeShifterFiller({
canonicalLength: 30 * 24 * 3600000,
siblings: 12,
floor: (dt, tz) => {
if (tz.isUTC()) {
dt = new Date(dt.valueOf());
dt.setUTCHours(0, 0, 0, 0);
dt.setUTCDate(1);
return dt;
}
else {
return fromDate(dt, tz.toString())
.set({ day: 1, hour: 0, second: 0, minute: 0, millisecond: 0 })
.toDate();
}
},
round: (dt, roundTo, tz) => {
if (tz.isUTC()) {
const cur = dt.getUTCMonth();
const adj = floorTo(cur, roundTo);
if (cur !== adj)
dt.setUTCMonth(adj);
}
else {
const cur = fromDate(dt, tz.toString()).month - 1;
const adj = floorTo(cur, roundTo);
if (cur !== adj)
return monthShift(dt, tz, adj - cur);
}
return dt;
},
shift: monthShift,
});
function yearShift(dt, tz, step) {
if (tz.isUTC()) {
dt = new Date(dt.valueOf());
dt.setUTCFullYear(dt.getUTCFullYear() + step);
return dt;
}
else {
return fromDate(dt, tz.toString()).add({ years: step }).toDate();
}
}
export const year = timeShifterFiller({
canonicalLength: 365 * 24 * 3600000,
siblings: 1000,
floor: (dt, tz) => {
if (tz.isUTC()) {
dt = new Date(dt.valueOf());
dt.setUTCHours(0, 0, 0, 0);
dt.setUTCMonth(0, 1);
return dt;
}
else {
return fromDate(dt, tz.toString())
.set({ month: 1, day: 1, hour: 0, second: 0, minute: 0, millisecond: 0 })
.toDate();
}
},
round: (dt, roundTo, tz) => {
if (tz.isUTC()) {
const cur = dt.getUTCFullYear();
const adj = floorTo(cur, roundTo);
if (cur !== adj)
dt.setUTCFullYear(adj);
}
else {
const cur = fromDate(dt, tz.toString()).year;
const adj = floorTo(cur, roundTo);
if (cur !== adj)
return yearShift(dt, tz, adj - cur);
}
return dt;
},
shift: yearShift,
});
export function getMultiDayShifter(numDays) {
return timeShifterFiller({
canonicalLength: 24 * 3600000 * numDays,
siblings: 1,
floor: day.floor,
shift: (dt, tz, step) => day.shift(dt, tz, step * numDays),
round: () => {
throw new Error('missing day round');
},
});
}
export const shifters = {
second,
minute,
hour,
day,
week,
month,
year,
};