Dates and Times
How to work with the various date and time components in Bits UI.
The date and time components in Bits UI leverage the @internationalized/date package, providing a unified API for working with dates and times across different locales and time zones. This package is inspired by the Temporal proposal and is designed to seamlessly integrate with the Temporal API once it becomes available.
Installation
You can install the package using your favorite package manager:
npm install @internationalized/date
It's highly recommended to familiarize yourself with the package's documentation before diving into the components. We'll cover the basics of how we use the package in Bits UI in the sections below, but their documentation provides much more detail on the various formats and how to work with them.
DateValue Types
Bits UI uses DateValue
objects from @internationalized/date
to represent dates and times consistently. These immutable objects provide specific information about the type of date they represent:
We use the DateValue
objects provided by @internationalized/date
to represent dates and times in a consistent way. These objects are immutable and provide information about the type of date they represent. The DateValue
is a union of the following three types:
Type | Description | Example |
---|---|---|
CalendarDate | Date without time component | 2024-07-10 |
CalendarDateTime | Date with time | 2024-07-10T12:30:00 |
ZonedDateTime | Date with time and timezone | 2024-07-10T21:00:00:00-04:00[America/New_York] |
Using these strongly-typed objects allows components to adapt appropriately to the date type you provide.
CalendarDate
Represents a date without a time component.
// Creating a CalendarDate
import { CalendarDate, parseDate, today, getLocalTimeZone } from "@internationalized/date";
// From year, month, day parameters
const date = new CalendarDate(2024, 7, 10);
// From ISO 8601 string
const parsedDate = parseDate("2024-07-10");
// Current date in specific timezone
const losAngelesToday = today("America/Los_Angeles");
// Current date in user's timezone
const localToday = today(getLocalTimeZone());
See the CalendarDate API Documentation for additional methods.
CalendarDateTime
Represents a date with a time component, but without timezone information.
// Creating a CalendarDateTime
import { CalendarDateTime, parseDateTime } from "@internationalized/date";
// From date and time components
const dateTime = new CalendarDateTime(2024, 7, 10, 12, 30, 0);
// From ISO 8601 string
const parsedDateTime = parseDateTime("2024-07-10T12:30:00");
See the CalendarDateTime API documentation for additional methods.
ZonedDateTime
Represents a specific date and time in a specific timezone - crucial for events that occur at an exact moment regardless of the user's location (like conferences or live broadcasts).
// Creating a ZonedDateTime
import {
ZonedDateTime,
parseZonedDateTime,
parseAbsolute,
parseAbsoluteToLocal,
} from "@internationalized/date";
const date = new ZonedDateTime(
2022,
2,
3, // Date (year, month, day)
"America/Los_Angeles", // Timezone
-28800000, // UTC offset in milliseconds
9,
15,
0 // Time (hour, minute, second)
);
// From ISO 8601 strings using different parsing functions
const date1 = parseZonedDateTime("2024-07-12T00:45[America/New_York]");
const date2 = parseAbsolute("2024-07-12T07:45:00Z", "America/New_York");
const date3 = parseAbsoluteToLocal("2024-07-12T07:45:00Z");
See the ZonedDateTime API documentation for more information.
Working with Date Ranges
For components that require date ranges, Bits UI provides a DateRange
type:
type DateRange = {
start: DateValue;
end: DateValue;
};
This type is used in components such as:
Using the Placeholder
Each date/time component in Bits UI has a bindable placeholder
prop that serves multiple important functions:
- Starting Point: Acts as the initial date when no value is selected
- Type Definition: Determines what type of date/time to display if value is absent
- Calendar Navigation: Controls the visible date range in calendar views
Example: Using Placeholder with Calendar
<script lang="ts">
import { Calendar } from "bits-ui";
import { today, getLocalTimeZone, type DateValue } from "@internationalized/date";
// Initialize placeholder with today's date
let placeholder: DateValue = $state(today(getLocalTimeZone()));
let selectedMonth: number = $state(placeholder.month);
</script>
<!-- Month selector to control calendar view -->
<select
onchange={() => {
placeholder = placeholder.set({ month: selectedMonth });
}}
bind:value={selectedMonth}
>
<option value={1}>January</option>
<option value={2}>February</option>
<!-- Additional months... -->
</select>
<Calendar.Root bind:placeholder>
<!-- Calendar components... -->
</Calendar.Root>
Updating DateValue Objects
Since DateValue
objects are immutable, you must create new instances when updating them:
// INCORRECT - will not work
let placeholder = new CalendarDate(2024, 7, 10);
placeholder.month = 8; // Error! DateValue objects are immutable
// CORRECT - using methods that return new instances
let placeholder = new CalendarDate(2024, 7, 10);
// Method 1: Using set()
placeholder = placeholder.set({ month: 8 });
// Method 2: Using add()
placeholder = placeholder.add({ months: 1 });
// Method 3: Using subtract()
placeholder = placeholder.subtract({ days: 5 });
// Method 4: Using cycle() - cycles through valid values
placeholder = placeholder.cycle("month", "forward", [1, 3, 5, 7, 9, 11]);
Formatting and Parsing
Formatting Dates for Display
For consistent, locale-aware date formatting, use the DateFormatter
class:
import { DateFormatter } from "@internationalized/date";
// Create a formatter for the current locale
const formatter = new DateFormatter("en-US", {
dateStyle: "full",
timeStyle: "short",
});
// Format a DateValue
const formattedDate = formatter.format(myDateValue.toDate("America/New_York"));
// Example output: "Wednesday, July 10, 2024 at 12:30 PM"
The DateFormatter
wraps the native Intl.DateTimeFormat API while fixing browser inconsistencies and polyfilling newer features.
Parsing Date Strings
When working with date strings from APIs or databases, use the appropriate parsing function for your needs:
import {
parseDate, // For CalendarDate
parseDateTime, // For CalendarDateTime
parseZonedDateTime, // For ZonedDateTime with timezone name
parseAbsolute, // For ZonedDateTime from UTC string + timezone
parseAbsoluteToLocal, // For ZonedDateTime in local timezone
} from "@internationalized/date";
// Examples
const date = parseDate("2024-07-10"); // CalendarDate
const dateTime = parseDateTime("2024-07-10T12:30:00"); // CalendarDateTime
const zonedDate = parseZonedDateTime("2024-07-12T00:45[America/New_York]"); // ZonedDateTime
const absoluteDate = parseAbsolute("2024-07-12T07:45:00Z", "America/New_York"); // ZonedDateTime
const localDate = parseAbsoluteToLocal("2024-07-12T07:45:00Z"); // ZonedDateTime in user's timezone
Common Gotchas and Tips
- Month Indexing: Unlike JavaScript's Date object (which is 0-indexed),
@internationalized/date
uses 1-indexed months (January = 1). - Immutability: Always reassign when modifying date objects:
date = date.add({ days: 1 })
. - Timezone Handling: Use
ZonedDateTime
for schedule-critical events like meetings or appointments. - Type Consistency: Match
placeholder
types to your needs - if you need time selection, useCalendarDateTime
notCalendarDate
. - Performance: Create
DateFormatter
instances once and reuse them rather than creating new instances on each render.