@codecademy/gamut
68.2.268.2.3-alpha.794432.0
dist/DatePicker/Calendar/utils/format.js+
dist/DatePicker/Calendar/utils/format.jsNew file+128
Index: package/dist/DatePicker/Calendar/utils/format.js
===================================================================
--- package/dist/DatePicker/Calendar/utils/format.js
+++ package/dist/DatePicker/Calendar/utils/format.js
@@ -0,0 +1,128 @@
+import { stringifyLocale } from '../../utils/locale';
+import { isValidDate } from './validation';
+
+/**
+ * Capitalize the first character of a string using the locale; rest unchanged (e.g. "next month" → "Next month").
+ */
+export const capitalizeFirst = (str, locale) => str.length === 0 ? str : str[0].toLocaleUpperCase(stringifyLocale(locale)) + str.slice(1);
+
+/**
+ * Format month and year for the calendar header (e.g. "February 2026").
+ */
+export const formatMonthYear = (date, locale) => {
+ return new Intl.DateTimeFormat(stringifyLocale(locale), {
+ month: 'long',
+ year: 'numeric'
+ }).format(date);
+};
+
+/**
+ * Get weekday names for column headers or abbr attributes.
+ * Column order follows `firstWeekday` (ISO 1 = Monday … 7 = Sunday), matching `Intl.Locale#getWeekInfo().firstDay`.
+ * @param format - 'short' for abbreviated (e.g. "Su", "Mo"), 'long' for full (e.g. "Sunday", "Monday")
+ */
+export const getWeekdayNames = (format, locale, firstWeekday) => {
+ const formatter = new Intl.DateTimeFormat(stringifyLocale(locale), {
+ weekday: format
+ });
+ const monday = new Date(2024, 0, 8);
+ const namesMonToSun = Array.from({
+ length: 7
+ }, (_, i) => {
+ const date = new Date(monday);
+ date.setDate(monday.getDate() + i);
+ return formatter.format(date);
+ });
+ return Array.from({
+ length: 7
+ }, (_, j) => {
+ const iso = (firstWeekday - 1 + j) % 7 + 1;
+ return namesMonToSun[iso - 1];
+ });
+};
+
+/**
+ * Get localized "next month" and "previous month" labels for calendar nav.
+ * Uses Intl.RelativeTimeFormat with numeric: "auto" (e.g. "next month", "last month").
+ */
+export const getRelativeMonthLabels = locale => {
+ const rtf = new Intl.RelativeTimeFormat(stringifyLocale(locale), {
+ numeric: 'auto'
+ });
+ return {
+ nextMonth: capitalizeFirst(rtf.format(1, 'month'), locale),
+ lastMonth: capitalizeFirst(rtf.format(-1, 'month'), locale)
+ };
+};
+
+/**
+ * Get localized "today" label (e.g. "today").
+ */
+export const getRelativeTodayLabel = locale => {
+ const rtf = new Intl.RelativeTimeFormat(stringifyLocale(locale), {
+ numeric: 'auto'
+ });
+ return capitalizeFirst(rtf.format(0, 'day'), locale);
+};
+
+/**
+ * Get the locale's short date format pattern (e.g. "MM/DD/YYYY" for en-US,
+ * "DD/MM/YYYY" for en-GB). Uses Intl.DateTimeFormat formatToParts to infer
+ * order and separators. Useful for parsing or building locale-aware inputs.
+ */
+export const getDateFormatPattern = locale => {
+ const parts = new Intl.DateTimeFormat(stringifyLocale(locale), {
+ year: 'numeric',
+ month: '2-digit',
+ day: '2-digit'
+ }).formatToParts(new Date(2025, 0, 15));
+ return parts.map(part => {
+ switch (part.type) {
+ case 'day':
+ return 'DD';
+ case 'month':
+ return 'MM';
+ case 'year':
+ return 'YYYY';
+ default:
+ return part.value;
+ }
+ }).join('');
+};
+
+/**
+ * Format a date for display in the date picker input (e.g. "2/15/2026").
+ */
+export const formatDateForInput = (date, locale) => {
+ return new Intl.DateTimeFormat(stringifyLocale(locale), {
+ month: 'numeric',
+ day: 'numeric',
+ year: 'numeric'
+ }).format(date);
+};
+
+/**
+ * Parse a string from the date input into a Date, or null if invalid.
+ * Only returns a date when the input is a complete valid date (e.g. "2/15/2026").
+ * Partial input like "1" or "2/15" returns null even though Date("1") would parse.
+ */
+
+// this logic needs some work
+export const parseDateFromInput = (value, locale) => {
+ const trimmed = value.trim();
+ if (!trimmed) return null;
+ const parsed = new Date(trimmed);
+ if (!isValidDate(parsed)) return null;
+ const formatted = formatDateForInput(parsed, locale);
+ if (formatted === trimmed) return parsed;
+ const parts = trimmed.split(/[/-]/);
+ if (parts.length >= 3) return parsed;
+ return null;
+};
+export const formatDateForAriaLabel = (date, locale) => {
+ return new Intl.DateTimeFormat(stringifyLocale(locale), {
+ month: 'long',
+ day: 'numeric',
+ year: 'numeric'
+ }).format(date);
+};
\ No newline at end of file