Skip to content

Date Time Calendar

Date Time Calendar widgets are Input Fields that let the user select a date and a time from a single interactive calendar. The user picks a day from the grid and a time from a segmented input with a drop-down of slots; the slots are generated from minTime to maxTime (inclusive) stepping minuteStep minutes.

The widget emits a local ISO date-time string (YYYY-MM-DDTHH:mm:ss) only when both the day and the time are selected, pair it with the { format: 'date-time' } validator. Picking a different day clears the time and resets the value to null, and time selection stays disabled until a day is selected. For a compact single-field variant that opens as a popover, use the Date Time Picker.

datetimecalendar.ts
import { gui } from '@golemui/gui-shared';
export default [
gui.inputs.dateTimeCalendar('appointmentAt', {
minTime: '09:00:00',
maxTime: '18:00:00',
minuteStep: 30,
label: 'Appointment',
}),
];

Use these props to customize your Date Time Calendar widget. It accepts date props (for the day grid) and time props (for the slots), all listed below. With numberOfMonths greater than 1, the time row renders on the first panel only.

proptypedescription
allowCustomTimebooleanAllows typing a time in the input; when false (default) times come only from the slot grid
disabledRangesarrayArray of { start: string, end?: string } ISO date ranges whose days render disabled
disabledTimeRangeMessagestring | LocalizableError message shown when a typed time falls inside a disabled time range
disabledTimeRangesarray{ start, end, date?, weekdays? } ISO time ranges whose slots render disabled. See below
hintstringA description to display below the calendar
hourFormat'12' | '24'Forces 12-hour or 24-hour slot labels and typing mode; when omitted the locale decides
maxDatestringLatest selectable date (ISO date, inclusive); later calendar days render disabled
maxTimestringLast selectable slot (ISO time, inclusive). Defaults to 23:59:59
maxTimeMessagestring | LocalizableError message shown when a typed time is after maxTime
minDatestringEarliest selectable date (ISO date, inclusive); earlier calendar days render disabled
minTimestringFirst selectable slot (ISO time, inclusive). Defaults to 00:00:00
minTimeMessagestring | LocalizableError message shown when a typed time is before minTime
minuteStepnumberMinutes between generated slots, also the arrow-key minute increment when typing. Defaults to 30
nextMonthAriaLabelstringARIA label for the next month button
nextMonthIconstringCustom CSS class for the “next month” button icon
noAvailableTimesMessagestring | LocalizableShown in the slot grid when the bounds yield no slots. Defaults to No available times
numberOfMonthsnumberNumber of months to display side-by-side. Defaults to 1
prevMonthAriaLabelstringARIA label for the previous month button
prevMonthIconstringCustom CSS class for the “previous month” button icon

Use the property hint to add a description.

datetimecalendar.ts
import { gui } from '@golemui/gui-shared';
export default [
gui.inputs.dateTimeCalendar('appointmentAt', {
hint: 'Pick a day first, then a time slot',
minTime: '09:00:00',
maxTime: '18:00:00',
minuteStep: 30,
label: 'Appointment',
}),
];

Use prevMonthIcon, nextMonthIcon, prevMonthAriaLabel and nextMonthAriaLabel to customize the calendar’s month-navigation buttons, exactly as in the Calendar.

datetimecalendar.ts
import { gui } from '@golemui/gui-shared';
export default [
gui.inputs.dateTimeCalendar('appointmentAt', {
prevMonthIcon: 'chevron_left',
nextMonthIcon: 'chevron_right',
prevMonthAriaLabel: 'Go to previous month',
nextMonthAriaLabel: 'Go to next month',
label: 'Appointment',
}),
];

Use the property numberOfMonths to display multiple months side-by-side. The time input and slot grid render on the first panel only.

datetimecalendar.ts
import { gui } from '@golemui/gui-shared';
export default [
gui.inputs.dateTimeCalendar('appointmentAt', {
hint: 'Two months side by side; the time row shows on the first panel only',
numberOfMonths: 2,
minTime: '09:00:00',
maxTime: '18:00:00',
minuteStep: 30,
label: 'Appointment',
}),
];

Use the property allowCustomTime to let the user type any time directly in the time field, in addition to picking a slot. Typed times are still validated against minTime and maxTime; customize (and localize) the messages with minTimeMessage and maxTimeMessage.

datetimecalendar.ts
import { gui } from '@golemui/gui-shared';
export default [
gui.inputs.dateTimeCalendar('appointmentAt', {
hint: 'Type any time or pick a 30m slot',
allowCustomTime: true,
minTime: '09:00:00',
maxTime: '18:00:00',
minuteStep: 30,
minTimeMessage: 'Office not open until 9:00',
maxTimeMessage: 'Office closed from 18:00',
label: 'Appointment',
}),
];

Use the property hourFormat to force '12'-hour (AM/PM) or '24'-hour slot labels and typing mode. When omitted, the user’s locale decides.

datetimecalendar.ts
import { gui } from '@golemui/gui-shared';
export default [
gui.inputs.dateTimeCalendar('appointmentAt', {
hint: '12-hour slot labels and typing (AM/PM)',
hourFormat: '12',
minTime: '09:00:00',
maxTime: '18:00:00',
minuteStep: 30,
label: 'Appointment',
}),
];

The date boundaries minDate, maxDate and disabledRanges grey out days. On top of that, disabledTimeRanges greys out time slots, and each entry can be scoped to specific days with date (an ISO date) or weekdays (numbers as returned by Date.prototype.getDay(): 0 = Sunday … 6 = Saturday). An unscoped entry applies every day. Because allowCustomTime is on here, typing a time inside a disabled range raises an input error whose message you set with disabledTimeRangeMessage.

datetimecalendar.ts
import { gui } from '@golemui/gui-shared';
export default [
gui.inputs.dateTimeCalendar('appointmentAt', {
hint: 'Weekday lunch break disabled; type a time to see the messages',
allowCustomTime: true,
minTime: '09:00:00',
maxTime: '18:00:00',
minuteStep: 30,
minDate: '2026-02-01',
maxDate: '2026-07-31',
disabledRanges: [
{
start: '2026-02-09',
end: '2026-02-10',
},
{
start: '2026-02-17',
},
],
disabledTimeRanges: [
{
start: '13:00:00',
end: '14:00:00',
weekdays: [1, 2, 3, 4, 5],
},
{
start: '09:00:00',
end: '10:30:00',
date: '2026-02-13',
},
],
disabledTimeRangeMessage: 'This time is not available',
label: 'Appointment',
defaultValue: '2026-02-16T09:30:00',
}),
];

When minTime and maxTime generate no slots for the chosen day, the grid shows an empty-state message. Use noAvailableTimesMessage to customize (and localize) it (it defaults to No available times).

datetimecalendar.ts
import { gui } from '@golemui/gui-shared';
export default [
gui.inputs.dateTimeCalendar('appointmentAt', {
hint: 'These bounds generate no slots, so the grid shows the empty-state message',
minTime: '18:00:00',
maxTime: '09:00:00',
noAvailableTimesMessage: 'No slots available for this day',
label: 'Appointment',
}),
];

Date Time Calendars can be styled as explained in the Styling Guide. They also use the styling variables of the day grid and the time slot grid.

Following you will find a list with the CSS Variables and a quick description of what you will style.

CSS VariableDescription
--gui-calendar-day-button-sizeDay button size, which drives the container width
--gui-calendar-time-grid-heightMaximum height of the time slot grid
--gui-calendar-time-button-heightHeight of each time slot button
--gui-space-1Gap around the time input row and slot grid
--gui-intent-primarySelected day and selected slot background color
--gui-intent-primary-hoverSlot hover background color
--gui-bg-disabledDisabled day and slot background color
--gui-text-mutedText color of the “no available times” message

This is the anatomy of the Date Time Calendar Widget in case you want to use your CSS styles.

datetimecalendar-anatomy.html
<gui-date-time-calendar>
<label for="fieldUid">
Label
<div class="gui-calendar__hint" id="fieldUid_hint">Hint</div>
</label>
<div class="gui-widget">
<div class="gui-calendar-input">
<div class="gui-calendar__container">
<div class="gui-calendar__months-grid">
<div>
<div class="gui-calendar__header">
<button class="gui-button gui-calendar__month-button gui-calendar__month-button--prev" type="button" aria-label="Previous Month"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 256 256"><path d="..."></path></svg></button>
<span aria-live="polite">March 2026</span>
<button class="gui-button gui-calendar__month-button gui-calendar__month-button--next" type="button" aria-label="Next Month"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 256 256"><path d="..."></path></svg></button>
</div>
<!-- The embedded Time Picker; disabled until a day is selected.
Its slot grid opens in place of the days grid below. -->
<div class="gui-calendar__time-input-row">
<gui-time-picker class="gui-time-picker gui-field">
<!-- See the Time Picker anatomy: gui-time + arrow + gui-time-list (grid mode) -->
</gui-time-picker>
</div>
<div class="gui-calendar__days-grid" role="grid" aria-label="March 2026">
<div class="gui-calendar__rows">
<div class="gui-calendar__weekday" role="columnheader">Su</div>
<div class="gui-calendar__weekday" role="columnheader">Mo</div>
<div class="gui-calendar__weekday" role="columnheader">Tu</div>
<div class="gui-calendar__weekday" role="columnheader">We</div>
<div class="gui-calendar__weekday" role="columnheader">Th</div>
<div class="gui-calendar__weekday" role="columnheader">Fr</div>
<div class="gui-calendar__weekday" role="columnheader">Sa</div>
</div>
<div class="gui-calendar__rows">
<button type="button" role="gridcell" class="gui-calendar__day-button other-month" tabindex="-1" disabled aria-selected="false">25</button>
<button type="button" role="gridcell" class="gui-calendar__day-button" tabindex="0" aria-selected="false">1</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</gui-date-time-calendar>