Packages
noora
0.60.0
0.84.1
0.84.0
0.83.2
0.83.1
0.83.0
0.82.7
0.82.6
0.82.5
0.82.4
0.82.3
0.82.2
0.82.1
0.82.0
0.81.4
0.81.3
0.81.2
0.81.1
0.81.0
0.80.1
0.80.0
0.79.1
0.79.0
0.78.1
0.78.0
0.77.4
0.77.3
0.77.2
0.77.1
0.77.0
0.76.0
0.75.0
0.74.0
0.73.0
0.72.0
0.71.0
0.70.0
0.69.1
0.69.0
0.68.0
0.67.0
0.66.0
0.65.0
0.64.2
0.64.1
0.64.0
0.63.2
0.63.1
0.63.0
0.62.0
0.61.0
0.60.0
0.59.0
0.58.0
0.57.0
0.56.1
0.56.0
0.55.0
0.54.0
0.53.1
0.53.0
0.52.1
0.52.0
0.51.0
0.50.1
0.50.0
0.49.0
0.48.0
0.47.0
0.46.0
0.45.0
0.44.3
0.44.2
0.44.1
0.44.0
0.43.0
0.42.0
0.41.0
0.40.6
0.40.5
0.40.4
0.40.3
0.40.2
0.40.1
0.40.0
0.39.1
0.39.0
0.38.0
0.37.0
0.36.0
0.35.0
0.34.0
0.33.0
0.32.1
0.32.0
0.31.0
0.30.0
0.29.2
0.29.1
0.29.0
0.28.5
0.28.4
0.28.3
0.28.2
0.28.1
0.28.0
0.27.0
0.26.1
0.26.0
0.25.0
0.24.0
0.23.1
0.23.0
0.22.1
0.22.0
0.21.0
0.20.0
0.19.0
0.18.0
0.17.0
0.16.0
0.15.0
0.14.0
0.13.0
0.12.1
0.12.0
0.11.2
0.11.1
0.11.0
0.10.0
0.9.0
0.8.0
0.7.0
0.6.1
0.6.0
0.5.1
0.5.0
0.4.0
0.3.2
0.3.1
0.3.0
0.2.2
0.2.0
0.1.0
0.1.0-rc.2
0.1.0-rc.1
0.1.0-alpha.6
0.1.0-alpha.5
0.1.0-alpha.4
0.1.0-alpha.3
0.1.0-alpha.2
0.1.0-alpha.1
A component library for Phoenix LiveView applications
Current section
Files
Jump to
Current section
Files
js/DatePicker/dateUtils.js
import * as datePicker from "@zag-js/date-picker";
/**
* Adjust a month by a delta, handling year wrap-around.
* @param {number} year - The year
* @param {number} month - The month (1-12)
* @param {number} delta - Amount to adjust (-1 for previous, +1 for next)
* @returns {{year: number, month: number}}
*/
export function adjustMonth(year, month, delta) {
const date = new Date(year, month - 1 + delta, 1);
return { year: date.getFullYear(), month: date.getMonth() + 1 };
}
/**
* Compare two month objects.
* @param {{year: number, month: number}} a
* @param {{year: number, month: number}} b
* @returns {number} Negative if a < b, 0 if equal, positive if a > b
*/
export function compareMonths(a, b) {
if (a.year !== b.year) return a.year - b.year;
return a.month - b.month;
}
/**
* Compare two DateValue objects.
* @param {{year: number, month: number, day: number}} a
* @param {{year: number, month: number, day: number}} b
* @returns {number} Negative if a < b, 0 if equal, positive if a > b
*/
export function compareDates(a, b) {
if (a.year !== b.year) return a.year - b.year;
if (a.month !== b.month) return a.month - b.month;
return a.day - b.day;
}
/**
* Format a Date object to ISO date string (YYYY-MM-DD).
* @param {Date} date
* @returns {string}
*/
export function toISODateString(date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
return `${year}-${month}-${day}`;
}
/**
* Calculate a date range based on a duration from now.
* @param {{amount: number, unit: string}} duration - Duration with amount and unit
* @returns {{start: Date, end: Date}}
*/
export function calculateRangeFromDuration(duration) {
const now = new Date();
const end = now;
let start;
const { amount, unit } = duration;
switch (unit) {
case "hour":
start = new Date(now.getTime() - amount * 60 * 60 * 1000);
break;
case "day":
start = new Date(now.getTime() - amount * 24 * 60 * 60 * 1000);
break;
case "week":
start = new Date(now.getTime() - amount * 7 * 24 * 60 * 60 * 1000);
break;
case "month":
start = new Date(now);
start.setMonth(start.getMonth() - amount);
break;
case "year":
start = new Date(now);
start.setFullYear(start.getFullYear() - amount);
break;
default:
start = new Date(now.getTime() - amount * 24 * 60 * 60 * 1000);
}
return { start, end };
}
/**
* Format a date into day, month, year parts.
* @param {Date|null} date
* @returns {{day: string, month: string, year: string}}
*/
export function formatDateParts(date) {
if (!date) return { day: "", month: "", year: "" };
const d = new Date(date);
return {
day: String(d.getDate()).padStart(2, "0"),
month: String(d.getMonth() + 1).padStart(2, "0"),
year: String(d.getFullYear()),
};
}
/**
* Parse day, month, year inputs into a Date.
* Uses Zag's parse function for validation.
* @param {string} day
* @param {string} month
* @param {string} year
* @returns {Date|null}
*/
export function parseDateFromParts(day, month, year) {
const d = parseInt(day, 10);
const m = parseInt(month, 10);
const y = parseInt(year, 10);
if (isNaN(d) || isNaN(m) || isNaN(y) || y < 1000) return null;
const dateStr = `${y}-${String(m).padStart(2, "0")}-${String(d).padStart(2, "0")}`;
try {
const parsed = datePicker.parse(dateStr);
if (!parsed) return null;
return new Date(parsed.year, parsed.month - 1, parsed.day);
} catch {
return null;
}
}
/**
* Parse an ISO date string (with optional time) into a DateValue-like object.
* Uses Zag's parse function for validation.
* @param {string} str - ISO date string (e.g., "2024-01-15" or "2024-01-15T10:00:00Z")
* @returns {{year: number, month: number, day: number}|null}
*/
export function parseISODate(str) {
if (!str || str.length === 0) return null;
// Extract date part (before 'T' if datetime string)
const datePart = str.split("T")[0];
try {
const parsed = datePicker.parse(datePart);
if (!parsed) return null;
return {
year: parsed.year,
month: parsed.month,
day: parsed.day,
};
} catch {
return null;
}
}
/**
* Initialize calendar months from a date range, ensuring they show different months.
* @param {{year: number, month: number, day: number}|null} startDate
* @param {{year: number, month: number, day: number}|null} endDate
* @returns {{startCalendarMonth: {year: number, month: number}, endCalendarMonth: {year: number, month: number}}}
*/
export function initCalendarMonthsFromRange(startDate, endDate) {
const now = new Date();
if (startDate && endDate) {
// Check if start and end are in the same month
const sameMonth =
startDate.year === endDate.year && startDate.month === endDate.month;
if (sameMonth) {
// Show previous month in left calendar so we have two different months
const prev = adjustMonth(endDate.year, endDate.month, -1);
return {
startCalendarMonth: prev,
endCalendarMonth: { year: endDate.year, month: endDate.month },
};
} else {
return {
startCalendarMonth: { year: startDate.year, month: startDate.month },
endCalendarMonth: { year: endDate.year, month: endDate.month },
};
}
}
// Default to previous month and current month
const currentMonth = now.getMonth() + 1;
const currentYear = now.getFullYear();
const prev = adjustMonth(currentYear, currentMonth, -1);
return {
startCalendarMonth: prev,
endCalendarMonth: { year: currentYear, month: currentMonth },
};
}
/**
* Calculate weeks for any arbitrary month.
* Returns proper DateValue objects using Zag's parse function.
* @param {number} year
* @param {number} month
* @param {number} startOfWeek - 0 for Sunday, 1 for Monday, etc.
* @param {number} weeksToDisplay - Number of weeks to calculate (default 6)
* @returns {Array<Array<object|null>>}
*/
export function calculateWeeksForMonth(
year,
month,
startOfWeek = 0,
weeksToDisplay = 6,
) {
const weeks = [];
// Get first day of month and total days
const firstDay = new Date(year, month - 1, 1);
const lastDay = new Date(year, month, 0);
const totalDays = lastDay.getDate();
// Calculate which day of week the month starts on
const dayOfWeek = firstDay.getDay();
// Adjust for start of week setting
const startOffset = (dayOfWeek - startOfWeek + 7) % 7;
// Build weeks
let currentDay = 1 - startOffset;
for (let weekIndex = 0; weekIndex < weeksToDisplay; weekIndex++) {
const week = [];
for (let dayIndex = 0; dayIndex < 7; dayIndex++) {
if (currentDay >= 1 && currentDay <= totalDays) {
// Day is in current month - create proper DateValue using Zag's parse
const dateStr = `${year}-${String(month).padStart(2, "0")}-${String(currentDay).padStart(2, "0")}`;
week.push(datePicker.parse(dateStr));
} else {
// Day is outside current month - push null (will be hidden)
week.push(null);
}
currentDay++;
}
weeks.push(week);
}
return weeks;
}
/**
* Get visible range for a given month (for Zag API compatibility).
* Returns proper DateValue objects.
* @param {number} year
* @param {number} month
* @returns {{start: object, end: object}}
*/
export function getVisibleRangeForMonth(year, month) {
const lastDay = new Date(year, month, 0).getDate();
const startStr = `${year}-${String(month).padStart(2, "0")}-01`;
const endStr = `${year}-${String(month).padStart(2, "0")}-${String(lastDay).padStart(2, "0")}`;
return {
start: datePicker.parse(startStr),
end: datePicker.parse(endStr),
};
}