diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 30a4a15..dc21697 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -17,7 +17,7 @@ Before contributing code, I strongly recommend reviewing my [architecture docume 2. Clone your fork: `git clone https://github.com/[your-username]/calendar-card-pro.git` 3. Install dependencies: `npm install` 4. Start development mode: `npm run dev` -5. The compiled card will be available in `dist/calendar-card-pro.js` +5. The compiled card will be available in `dist/calendar-card-pro-dev.js` 6. For testing in Home Assistant, follow the [testing instructions](#testing-in-home-assistant) ## Branch Structure diff --git a/README.md b/README.md index 848f33a..96cb656 100644 --- a/README.md +++ b/README.md @@ -708,6 +708,7 @@ Configure how event times and locations are displayed: show_time: true # Show event start/end times show_single_allday_time: false # Hide time for single-day all-day events time_24h: false # Use 12-hour format (AM/PM) +two_digit_hours: false # Use 2 digits in hours show_end_time: true # Show event end time time_font_size: '12px' time_color: 'var(--secondary-text-color)' @@ -1204,6 +1205,7 @@ These examples demonstrate how Calendar Card Pro can be customized to match any | `show_time` | boolean | `true` | Whether to show event times | | `show_single_allday_time` | boolean | `true` | Whether to show time display for all-day single-day events | | `time_24h` | boolean | `System` | Whether to use 24-hour time format (auto-detects from HA) | +| `two_digit_hours` | boolean | `false` | Whether to use 2 digits in hours | | `show_end_time` | boolean | `true` | Whether to show event end times | | `time_icon_size` | string | `14px` | Clock icon size (replaces time_location_icon_size) | | `time_font_size` | string | `12px` | Event time font size | @@ -1342,6 +1344,8 @@ start_date: '2025-07-01' days_to_show: 10 compact_events_to_show: 10 language: en +time_24h: true +two_digit_hours: false # Header title: 📅 Full Calendar Demo @@ -1373,7 +1377,6 @@ month_color: '#baf1ff' show_past_events: false event_font_size: 14px event_color: '#baf1ff' -time_24h: true show_end_time: true time_font_size: 12px time_color: '#baf1ff' diff --git a/src/config/config.ts b/src/config/config.ts index 7541221..ad22a1e 100644 --- a/src/config/config.ts +++ b/src/config/config.ts @@ -92,6 +92,7 @@ export const DEFAULT_CONFIG: Types.Config = { show_time: true, show_single_allday_time: true, time_24h: 'system', + two_digit_hours: false, show_end_time: true, time_font_size: '12px', time_color: 'var(--secondary-text-color)', diff --git a/src/config/types.ts b/src/config/types.ts index c01de44..f95b692 100644 --- a/src/config/types.ts +++ b/src/config/types.ts @@ -89,6 +89,7 @@ export interface Config { show_time: boolean; show_single_allday_time: boolean; time_24h: boolean | 'system'; + two_digit_hours: boolean; show_end_time: boolean; time_font_size: string; time_color: string; diff --git a/src/rendering/editor.ts b/src/rendering/editor.ts index c94e44e..8879409 100755 --- a/src/rendering/editor.ts +++ b/src/rendering/editor.ts @@ -675,6 +675,7 @@ export class CalendarCardProEditor extends LitElement { { value: 'true', label: this._getTranslation('24h') }, { value: 'false', label: this._getTranslation('12h') }, ])} + ${this.addBooleanField('two_digit_hours', this._getTranslation('two_digit_hours'))} `, )} diff --git a/src/translations/languages/en.json b/src/translations/languages/en.json index fbe6123..1ce16b0 100644 --- a/src/translations/languages/en.json +++ b/src/translations/languages/en.json @@ -84,6 +84,7 @@ "custom": "Custom", "12h": "12-hour", "24h": "24-hour", + "two_digit_hours": "2 digit hours", "appearance_layout": "Appearance & Layout", "title_styling": "Title Styling", diff --git a/src/translations/languages/nb.json b/src/translations/languages/nb.json index 59607c5..17f71c7 100644 --- a/src/translations/languages/nb.json +++ b/src/translations/languages/nb.json @@ -84,6 +84,7 @@ "custom": "Egendefinert", "12h": "12-timer", "24h": "24-timer", + "two_digit_hours": "2 sifre i timer", "appearance_layout": "Utseende og layout", "title_styling": "Tittelformatering", diff --git a/src/utils/format.ts b/src/utils/format.ts index 7fee483..3ff64b0 100644 --- a/src/utils/format.ts +++ b/src/utils/format.ts @@ -80,6 +80,7 @@ export function formatEventTime( translations, useNativeFormatting, use24h, + config.two_digit_hours, hass, ), ); @@ -93,6 +94,7 @@ export function formatEventTime( config.show_end_time, useNativeFormatting, use24h, + config.two_digit_hours, hass, ), ); @@ -103,8 +105,7 @@ export function formatEventTime( * Uses dayjs for consistent, localized relative time formatting * * @param event Calendar event to generate countdown for - * @param hass Home Assistant instance (used to extract language) - * @param config Card configuration options + * @param language Language to use * @returns Countdown string or null if event is past or empty day */ export function getCountdownString( @@ -204,19 +205,27 @@ export function getLocalDateKey(date: Date): string { * * @param date Date object to format * @param use24h Whether to use 24-hour format + * @param twoDigitHours Whether to use 2 digits in hours * @returns Formatted time string */ -export function formatTime(date: Date, use24h = true): string { +export function formatTime(date: Date, use24h = true, twoDigitHours = false): string { let hours = date.getHours(); const minutes = date.getMinutes(); if (!use24h) { const ampm = hours >= 12 ? 'PM' : 'AM'; hours = hours % 12 || 12; - return `${hours}:${minutes.toString().padStart(2, '0')} ${ampm}`; + return `${twoDigitHours ? pad(hours) : hours}:${pad(minutes)} ${ampm}`; } - return `${hours}:${minutes.toString().padStart(2, '0')}`; + return `${twoDigitHours ? pad(hours) : hours}:${pad(minutes)}`; +} + +/** + * Pad with 0 if only one digit. + */ +function pad(n: number): string { + return n.toString().padStart(2, '0'); } /** @@ -238,9 +247,7 @@ export function getISOWeekNumber(date: Date): number { const yearStart = new Date(d.getFullYear(), 0, 1); // Calculate full weeks to nearest Thursday - const weekNumber = Math.ceil(((d.getTime() - yearStart.getTime()) / 86400000 + 1) / 7); - - return weekNumber; + return Math.ceil(((d.getTime() - yearStart.getTime()) / 86400000 + 1) / 7); } /** @@ -265,9 +272,7 @@ export function getSimpleWeekNumber(date: Date, firstDayOfWeek: number = 0): num const dayOfWeekOffset = (startOfYear.getDay() - firstDayOfWeek + 7) % 7; // Calculate week number (adding 1 because we want weeks to start from 1) - const weekNumber = Math.ceil((days + dayOfWeekOffset + 1) / 7); - - return weekNumber; + return Math.ceil((days + dayOfWeekOffset + 1) / 7); } /** @@ -340,7 +345,10 @@ export function getWeekNumber( * @param startDate Start date of the event * @param endDate End date of the event * @param showEndTime Whether to show end time - * @param time24h Whether to use 24-hour format + * @param useNativeFormatting Wheter to use native formatting + * @param use24h Whether to use 24-hour format + * @param twoDigitHours Whether to use 2 digits in hours + * @param hass Home Assistant * @returns Formatted time string */ function formatSingleDayTime( @@ -349,6 +357,7 @@ function formatSingleDayTime( showEndTime: boolean, useNativeFormatting: boolean, use24h: boolean = true, + twoDigitHours: boolean = false, hass?: Types.Hass | null, ): string { if (useNativeFormatting && hass?.locale) { @@ -357,14 +366,14 @@ function formatSingleDayTime( // Use our formatter with the detected format preference return showEndTime - ? `${formatTime(startDate, use24hFormat)} - ${formatTime(endDate, use24hFormat)}` - : formatTime(startDate, use24hFormat); + ? `${formatTime(startDate, use24hFormat, twoDigitHours)} - ${formatTime(endDate, use24hFormat, twoDigitHours)}` + : formatTime(startDate, use24hFormat, twoDigitHours); } // For explicit settings, use our formatter with the specified format return showEndTime - ? `${formatTime(startDate, use24h)} - ${formatTime(endDate, use24h)}` - : formatTime(startDate, use24h); + ? `${formatTime(startDate, use24h, twoDigitHours)} - ${formatTime(endDate, use24h, twoDigitHours)}` + : formatTime(startDate, use24h, twoDigitHours); } /** @@ -374,7 +383,10 @@ function formatSingleDayTime( * @param endDate End date of the event * @param language Language code for translations * @param translations Translations object - * @param time24h Whether to use 24-hour format + * @param useNativeFormatting Wheter to use native formatting + * @param use24h Whether to use 24-hour format + * @param twoDigitHours Whether to use 2 digits in hours + * @param hass Home Assistant * @returns Formatted time string */ function formatMultiDayTime( @@ -384,6 +396,7 @@ function formatMultiDayTime( translations: Types.Translations, useNativeFormatting: boolean, use24h: boolean = true, + twoDigitHours: boolean = false, hass?: Types.Hass | null, ): string { const now = new Date(); @@ -396,9 +409,9 @@ function formatMultiDayTime( if (useNativeFormatting && hass?.locale) { // Use the helper to determine time format preference const use24hFormat = Helpers.getTimeFormat24h(hass.locale, use24h); - return formatTime(date, use24hFormat); + return formatTime(date, use24hFormat, twoDigitHours); } - return formatTime(date, use24h); + return formatTime(date, use24h, twoDigitHours); }; // Format the end time part based on when the event ends