Repository: gpbl/react-day-picker
Pull request #2213 (2024-06-22 00:45:26)

feat: add support for UTC, Jalali calendar

github-actions[bot]: ## Pull request title linting :rotating_light:

In order to merge this pull request, the title of the pull request should be prefixed by one of the available types.

### Available types:

- **feat**: A new feature
- **fix**: A bug fix
- **docs**: Documentation only changes
- **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
- **refactor**: A code change that neither fixes a bug nor adds a feature
- **perf**: A code change that improves performance
- **test**: Adding missing tests or correcting existing tests
- **build**: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
- **ci**: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)
- **chore**: Other changes that don't modify src or test files
- **revert**: Reverts a previous commit

---

<details>
<summary>Examples</summary>

```
feat(grpc): add new endpoint
refactor: combine class A and class B
ci: update pull request linter
style: change format of strings
```

</details>

:tipping_hand_person: For more examples, visit https://www.conventionalcommits.org/en/v1.0.0/#examples.

Patch:
diff --git a/README.md b/README.md
index 0df952ca1..33162007f 100644
--- a/README.md
+++ b/README.md
@@ -6,12 +6,13 @@ DayPicker is a [React](https://react.dev) component to create date pickers, cale
 
 ## Features
 
-- Minimal design for easy customization.
-- Extensive props for configuring the calendar.
-- Supports selection of a single day, multiple days, ranges, or custom selections.
-- Can be localized in any language.
-- Complies with WCAG 2.1 AA requirements for accessibility.
-- Includes customizable internal components for complex use cases.
+- 🛠 An extensive set of props for configuring and customizing the calendar.
+- 🎨 A minimal design that can be easily styled with CSS or any CSS framework.
+- 📅 Supports selections of single day, multiple days, ranges of days, or custom selections.
+- 🌍 Can be localized into any language.
+- 📆 Supports for ISO 8601 dates, UTC dates, and Jalali Calendar.
+- ♿ Complies with WCAG 2.1 AA requirements for accessibility.
+- ⚙️ Customizable internal components for complex use cases.
 
 DayPicker is written in TypeScript and compiled to CommonJS and ESM. It requires [date-fns](https://date-fns.org) as a peer dependency.
 
diff --git a/examples/Jalali.tsx b/examples/Jalali.tsx
new file mode 100644
index 000000000..13ff5357e
--- /dev/null
+++ b/examples/Jalali.tsx
@@ -0,0 +1,8 @@
+import React from "react";
+
+import { faIR } from "date-fns/locale";
+import { DayPicker } from "react-day-picker/jalali";
+
+export function Jalali() {
+  return <DayPicker mode="single" locale={faIR} dir="rtl" />;
+}
diff --git a/examples/Utc.tsx b/examples/Utc.tsx
new file mode 100644
index 000000000..3865fcd31
--- /dev/null
+++ b/examples/Utc.tsx
@@ -0,0 +1,19 @@
+import React, { useState } from "react";
+
+import { UTCDate } from "@date-fns/utc";
+import { DayPicker } from "react-day-picker/utc";
+
+export function Utc() {
+  const [selected, setSelected] = useState<Date>(new UTCDate());
+  return (
+    <DayPicker
+      mode="single"
+      required
+      selected={selected}
+      onSelect={(date: Date) => {
+        setSelected(date);
+      }}
+      footer={selected ? `${selected.toUTCString()}` : null}
+    />
+  );
+}
diff --git a/examples/index.ts b/examples/index.ts
index c7321b47d..07755f368 100644
--- a/examples/index.ts
+++ b/examples/index.ts
@@ -22,6 +22,7 @@ export * from "./FromToMonth";
 export * from "./FromToYear";
 export * from "./HideNavigation";
 export * from "./Input";
+export * from "./Jalali";
 export * from "./InputRange";
 export * from "./InputTime";
 export * from "./Keyboard";
@@ -55,6 +56,7 @@ export * from "./StylingInline";
 export * from "./StylingModifiers";
 export * from "./TailwindCSS";
 export * from "./Testcase1567";
+export * from "./Utc";
 export * from "./TestCase2047";
 export * from "./WeekIso";
 export * from "./Weeknumber";
diff --git a/package.json b/package.json
index cf8170b8b..44458ad6a 100644
--- a/package.json
+++ b/package.json
@@ -31,6 +31,34 @@
         "default": "./dist/cjs/index.js"
       }
     },
+    "./utc": {
+      "require": {
+        "types": "./dist/cjs/utc.d.ts",
+        "default": "./dist/cjs/utc.js"
+      },
+      "import": {
+        "types": "./dist/esm/utc.d.ts",
+        "default": "./dist/esm/utc.js"
+      },
+      "default": {
+        "types": "./dist/cjs/utc.d.ts",
+        "default": "./dist/cjs/utc.js"
+      }
+    },
+    "./jalali": {
+      "require": {
+        "types": "./dist/cjs/jalali.d.ts",
+        "default": "./dist/cjs/jalali.js"
+      },
+      "import": {
+        "types": "./dist/esm/jalali.d.ts",
+        "default": "./dist/esm/jalali.js"
+      },
+      "default": {
+        "types": "./dist/cjs/jalali.d.ts",
+        "default": "./dist/cjs/jalali.js"
+      }
+    },
     "./style.css": {
       "require": "./src/style.css",
       "import": "./src/style.css",
@@ -87,6 +115,7 @@
     "tsconfig.json"
   ],
   "devDependencies": {
+    "@date-fns/utc": "^1.2.0",
     "@jest/types": "^29.6.3",
     "@testing-library/dom": "^10.1.0",
     "@testing-library/jest-dom": "^6.4.5",
@@ -100,6 +129,7 @@
     "@typescript-eslint/eslint-plugin": "^7.11.0",
     "@typescript-eslint/parser": "^7.12.0",
     "date-fns": "^3.6.0",
+    "date-fns-jalali": "3.6.0-0",
     "eslint": "^8.57.0",
     "eslint-config-prettier": "^9.1.0",
     "eslint-import-resolver-typescript": "^3.6.1",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 0798e7441..dd4bb7ea1 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -8,6 +8,9 @@ importers:
 
   .:
     devDependencies:
+      '@date-fns/utc':
+        specifier: ^1.2.0
+        version: 1.2.0
       '@jest/types':
         specifier: ^29.6.3
         version: 29.6.3
@@ -47,6 +50,9 @@ importers:
       date-fns:
         specifier: ^3.6.0
         version: 3.6.0
+      date-fns-jalali:
+        specifier: 3.6.0-0
+        version: 3.6.0-0
       eslint:
         specifier: ^8.57.0
         version: 8.57.0
@@ -116,6 +122,9 @@ importers:
 
   website:
     dependencies:
+      '@date-fns/utc':
+        specifier: ^1.2.0
+        version: 1.2.0
       '@docusaurus/core':
         specifier: 3.4.0
         version: 3.4.0(@docusaurus/types@3.4.0)(eslint@8.57.0)(react-dom@18.3.1)(react@18.3.1)(typescript@5.4.5)
@@ -131,6 +140,9 @@ importers:
       clsx:
         specifier: ^2.1.1
         version: 2.1.1
+      date-fns-jalali:
+        specifier: 3.6.0-0
+        version: 3.6.0-0
       prism-react-renderer:
         specifier: ^2.3.1
         version: 2.3.1(react@18.3.1)
@@ -2220,6 +2232,9 @@ packages:
       '@jridgewell/trace-mapping': 0.3.9
     dev: true
 
+  /@date-fns/utc@1.2.0:
+    resolution: {integrity: sha512-YLq+crMPJiBmIdkRmv9nZuZy1mVtMlDcUKlg4mvI0UsC/dZeIaGoGB5p/C4FrpeOhZ7zBTK03T58S0DFkRNMnw==}
+
   /@discoveryjs/json-ext@0.5.7:
     resolution: {integrity: sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==}
     engines: {node: '>=10.0.0'}
@@ -6064,6 +6079,9 @@ packages:
       is-data-view: 1.0.1
     dev: true
 
+  /date-fns-jalali@3.6.0-0:
+    resolution: {integrity: sha512-rcOocwhBFgEN4i+vXPoEp/irCxAmX8yloK/l/oeMOVCLpaFQfkq7jVn0vCWK91P2H9I/doSAPEN4WSWQeqwsug==}
+
   /date-fns@3.6.0:
     resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==}
 
diff --git a/src/DayPicker.tsx b/src/DayPicker.tsx
index b16ffeb76..a22c31013 100644
--- a/src/DayPicker.tsx
+++ b/src/DayPicker.tsx
@@ -5,8 +5,7 @@ import { ContextProviders } from "./contexts/providers";
 import type { DayPickerProps } from "./types";
 
 /**
- * DayPicker is a React component to create date pickers, calendars, and date
- * inputs for web applications.
+ * Render the date picker calendar.
  *
  * @group DayPicker
  * @see http://daypicker.dev
diff --git a/src/classes/CalendarDay.ts b/src/classes/CalendarDay.ts
index 24a379218..e5bd7029b 100644
--- a/src/classes/CalendarDay.ts
+++ b/src/classes/CalendarDay.ts
@@ -1,5 +1,5 @@
-import { isSameDay } from "date-fns/isSameDay";
-import { isSameMonth } from "date-fns/isSameMonth";
+import { dateLib as defaultDateLib } from "../lib/dateLib";
+import type { DateLib } from "../types";
 
 /**
  * Represent the day displayed in the calendar.
@@ -9,12 +9,22 @@ import { isSameMonth } from "date-fns/isSameMonth";
  * information about the day.
  */
 export class CalendarDay {
-  constructor(date: Date, displayMonth: Date) {
+  constructor(
+    date: Date,
+    displayMonth: Date,
+    dateLib: DateLib = defaultDateLib
+  ) {
     this.date = date;
     this.displayMonth = displayMonth;
-    this.outside = Boolean(displayMonth && !isSameMonth(date, displayMonth));
+    this.outside = Boolean(
+      displayMonth && !dateLib.isSameMonth(date, displayMonth)
+    );
+    this.dateLib = dateLib;
   }
 
+  /** The utility functions to manipulate dates. */
+  readonly dateLib: DateLib;
+
   /**
    * Whether the day is not belonging to the displayed month.
    *
@@ -41,8 +51,8 @@ export class CalendarDay {
    */
   isEqualTo(day: CalendarDay) {
     return (
-      isSameDay(day.date, this.date) &&
-      isSameMonth(day.displayMonth, this.displayMonth)
+      this.dateLib.isSameDay(day.date, this.date) &&
+      this.dateLib.isSameMonth(day.displayMonth, this.displayMonth)
     );
   }
 }
diff --git a/src/components/DayWrapper.tsx b/src/components/DayWrapper.tsx
index 28f3baa6d..46136edd8 100644
--- a/src/components/DayWrapper.tsx
+++ b/src/components/DayWrapper.tsx
@@ -27,6 +27,7 @@ export function DayWrapper(props: {
   const {
     classNames,
     components,
+    dateLib,
     dir,
     formatters: { formatDay },
     labels: { labelDay },
@@ -235,7 +236,7 @@ export function DayWrapper(props: {
     tabIndex: isFocused || isAutoFocusTarget ? 0 : -1,
     ["aria-colindex"]: props["aria-colindex"],
     ["aria-label"]:
-      labelDay(props.day.date, modifiers, { locale }) ?? undefined,
+      labelDay(props.day.date, modifiers, { locale }, dateLib) ?? undefined,
     ["aria-disabled"]: modifiers.disabled || undefined,
     ["aria-hidden"]: modifiers.hidden || undefined,
     ["aria-selected"]: modifiers.selected || undefined,
@@ -279,7 +280,7 @@ export function DayWrapper(props: {
       <DayDate
         day={props.day}
         modifiers={modifiers}
-        formattedDate={formatDay(props.day.date, { locale })}
+        formattedDate={formatDay(props.day.date, { locale }, dateLib)}
         rootProps={dayDateRootProps}
       />
     </Day>
diff --git a/src/components/MonthCaption.tsx b/src/components/MonthCaption.tsx
index 1e136f26c..9a323b06d 100644
--- a/src/components/MonthCaption.tsx
+++ b/src/components/MonthCaption.tsx
@@ -25,6 +25,7 @@ export function MonthCaption(props: {
   const {
     classNames,
     captionLayout,
+    dateLib,
     formatters: { formatCaption },
     labels: { labelCaption },
     locale,
@@ -53,9 +54,11 @@ export function MonthCaption(props: {
           className={classNames[UI.CaptionLabel]}
           role="status"
           aria-live="polite"
-          aria-label={labelCaption(props.month.date, { locale }) ?? undefined}
+          aria-label={
+            labelCaption(props.month.date, { locale }, dateLib) ?? undefined
+          }
         >
-          {formatCaption(props.month.date, { locale })}
+          {formatCaption(props.month.date, { locale }, dateLib)}
         </span>
       )}
     </div>
diff --git a/src/components/MonthsDropdown.tsx b/src/components/MonthsDropdown.tsx
index 33fac5a83..cef5f0b8e 100644
--- a/src/components/MonthsDropdown.tsx
+++ b/src/components/MonthsDropdown.tsx
@@ -1,9 +1,6 @@
 import React from "react";
 import type { ChangeEventHandler } from "react";
 
-import { setMonth } from "date-fns/setMonth";
-import { startOfMonth } from "date-fns/startOfMonth";
-
 import { UI } from "../UI";
 import type { CalendarMonth } from "../classes";
 import { useCalendar, useProps } from "../contexts";
@@ -26,6 +23,7 @@ export function MonthsDropdown(props: {
     classNames,
     components,
     disableNavigation,
+    dateLib: { setMonth, startOfMonth },
     labels: { labelMonthDropdown }
   } = useProps();
 
diff --git a/src/components/Week.tsx b/src/components/Week.tsx
index a39b9d64f..155fcbcc4 100644
--- a/src/components/Week.tsx
+++ b/src/components/Week.tsx
@@ -1,7 +1,5 @@
 import React from "react";
 
-import { getUnixTime } from "date-fns/getUnixTime";
-
 import { UI } from "../UI";
 import type { CalendarDay, CalendarWeek } from "../classes";
 import { useProps } from "../contexts";
@@ -18,7 +16,13 @@ import { WeekNumber as DefaultWeekNumber } from "./WeekNumber";
  * @see https://react-day-picker.js.org/advanced-guides/custom-components
  */
 export function Week(props: { ["aria-rowindex"]: number; week: CalendarWeek }) {
-  const { styles, classNames, showWeekNumber, components } = useProps();
+  const {
+    styles,
+    classNames,
+    showWeekNumber,
+    components,
+    dateLib: { getUnixTime }
+  } = useProps();
 
   const WeekNumber = components?.WeekNumber ?? DefaultWeekNumber;
 
diff --git a/src/components/WeekNumber.tsx b/src/components/WeekNumber.tsx
index dba82cf93..c99e536f1 100644
--- a/src/components/WeekNumber.tsx
+++ b/src/components/WeekNumber.tsx
@@ -43,7 +43,7 @@ export function WeekNumber(props: { week: CalendarWeek }) {
         )
       }
     >
-      {formatWeekNumber(props.week.weekNumber, { locale })}
+      {formatWeekNumber(props.week.weekNumber)}
     </div>
   );
 }
diff --git a/src/components/Weekday.tsx b/src/components/Weekday.tsx
index bdc90d80f..64f11390d 100644
--- a/src/components/Weekday.tsx
+++ b/src/components/Weekday.tsx
@@ -18,6 +18,7 @@ export function Weekday(props: {
 }) {
   const {
     classNames,
+    dateLib,
     formatters: { formatWeekdayName },
     labels: { labelWeekday, labelWeekNumberHeader },
     locale,
@@ -30,14 +31,16 @@ export function Weekday(props: {
       aria-colindex={props["aria-colindex"]}
       aria-label={
         props.weekday
-          ? labelWeekday(props.weekday, { locale })
+          ? labelWeekday(props.weekday, { locale }, dateLib)
           : labelWeekNumberHeader({ locale })
       }
       className={classNames[UI.Weekday]}
       style={styles?.[UI.Weekday]}
     >
       {!hideWeekdayRow &&
-        (props.weekday ? formatWeekdayName(props.weekday, { locale }) : "#")}
+        (props.weekday
+          ? formatWeekdayName(props.weekday, { locale }, dateLib)
+          : "#")}
     </span>
   );
 }
diff --git a/src/components/Weekdays.tsx b/src/components/Weekdays.tsx
index 45662e521..8d595bd2b 100644
--- a/src/components/Weekdays.tsx
+++ b/src/components/Weekdays.tsx
@@ -18,6 +18,7 @@ export function Weekdays() {
   const {
     classNames,
     components,
+    dateLib,
     hideWeekdayRow,
     ISOWeek,
     locale,
@@ -26,7 +27,7 @@ export function Weekdays() {
     weekStartsOn
   } = useProps();
 
-  const weekdays = getWeekdays(locale, weekStartsOn, ISOWeek);
+  const weekdays = getWeekdays(locale, weekStartsOn, ISOWeek, dateLib);
   const Weekday = components?.Weekday ?? DefaultWeekday;
 
   return (
diff --git a/src/components/YearsDropdown.tsx b/src/components/YearsDropdown.tsx
index f5c60e6ee..2a596586d 100644
--- a/src/components/YearsDropdown.tsx
+++ b/src/components/YearsDropdown.tsx
@@ -1,9 +1,6 @@
 import React from "react";
 import type { ChangeEventHandler } from "react";
 
-import { setYear } from "date-fns/setYear";
-import { startOfMonth } from "date-fns/startOfMonth";
-
 import { UI } from "../UI";
 import type { CalendarMonth } from "../classes/CalendarMonth";
 import { useCalendar, useProps } from "../contexts";
@@ -25,6 +22,7 @@ export function YearsDropdown(props: {
   const {
     classNames,
     components,
+    dateLib: { startOfMonth, setYear },
     disableNavigation,
     labels: { labelYearDropdown }
   } = useProps();
diff --git a/src/contexts/useCalendar.tsx b/src/contexts/useCalendar.tsx
index 4ed55adc4..9c46ccd89 100644
--- a/src/contexts/useCalendar.tsx
+++ b/src/contexts/useCalendar.tsx
@@ -1,7 +1,5 @@
 import React, { type ReactElement, createContext, useContext } from "react";
 
-import { startOfMonth } from "date-fns/startOfMonth";
-
 import type { CalendarWeek, CalendarDay, CalendarMonth } from "../classes";
 import type { DropdownOption } from "../components/Dropdown";
 import { getDates } from "../helpers/getDates";
@@ -86,6 +84,7 @@ export type CalendarContextValue = {
 
 function useCalendarContextValue(): CalendarContextValue {
   const props = useProps();
+  const { startOfMonth } = props.dateLib;
 
   const initialDisplayMonth = getInitialMonth(props);
 
diff --git a/src/contexts/useModifiers.tsx b/src/contexts/useModifiers.tsx
index a69cbd8c3..f02eeaafe 100644
--- a/src/contexts/useModifiers.tsx
+++ b/src/contexts/useModifiers.tsx
@@ -1,8 +1,5 @@
 import React, { type ReactElement, createContext, useContext } from "react";
 
-import { isSameDay } from "date-fns/isSameDay";
-import { isSameMonth } from "date-fns/isSameMonth";
-
 import { DayFlag, SelectionState } from "../UI";
 import { CalendarDay } from "../classes";
 import { useMulti } from "../selection/useMulti";
@@ -38,6 +35,7 @@ export type ModifiersContextValue = {
 
 function useModifiersContextValue(): ModifiersContextValue {
   const {
+    dateLib,
     disabled,
     hidden,
     modifiers,
@@ -52,6 +50,7 @@ function useModifiersContextValue(): ModifiersContextValue {
   const multi = useMulti();
   const range = useRange();
 
+  const { isSameDay, isSameMonth } = dateLib;
   const internal: Record<DayFlag, CalendarDay[]> = {
     [DayFlag.focused]: [],
     [DayFlag.outside]: [],
@@ -75,10 +74,12 @@ function useModifiersContextValue(): ModifiersContextValue {
 
     const isOutside = Boolean(displayMonth && !isSameMonth(date, displayMonth));
 
-    const isDisabled = Boolean(disabled && dateMatchModifiers(date, disabled));
+    const isDisabled = Boolean(
+      disabled && dateMatchModifiers(date, disabled, dateLib)
+    );
 
     const isHidden =
-      Boolean(hidden && dateMatchModifiers(date, hidden)) ||
+      Boolean(hidden && dateMatchModifiers(date, hidden, dateLib)) ||
       (!showOutsideDays && isOutside);
 
     const isElementInteractive = mode || onDayClick !== undefined;
@@ -116,7 +117,10 @@ function useModifiersContextValue(): ModifiersContextValue {
           isSameDay(day.date, range.selected.to)
         ) {
           selection[SelectionState.range_end].push(day);
-        } else if (range.selected && isDateInRange(day.date, range.selected)) {
+        } else if (
+          range.selected &&
+          isDateInRange(day.date, range.selected, dateLib)
+        ) {
           selection[SelectionState.range_middle].push(day);
         }
       }
@@ -127,7 +131,7 @@ function useModifiersContextValue(): ModifiersContextValue {
       Object.keys(modifiers).forEach((name) => {
         const modifierValue = modifiers?.[name];
         const isMatch = modifierValue
-          ? dateMatchModifiers(date, modifierValue)
+          ? dateMatchModifiers(date, modifierValue, dateLib)
           : false;
         if (!isMatch) return;
         if (custom[name]) {
diff --git a/src/contexts/useProps.tsx b/src/contexts/useProps.tsx
index 60d37ef71..983a673c7 100644
--- a/src/contexts/useProps.tsx
+++ b/src/contexts/useProps.tsx
@@ -6,6 +6,7 @@ import { getDefaultClassNames } from "../helpers/getDefaultClassNames";
 import { getFormatters } from "../helpers/getFormatters";
 import { getStartEndMonths } from "../helpers/getStartEndMonths";
 import * as defaultLabels from "../labels";
+import { dateLib as defaultDateLib } from "../lib";
 import type {
   ClassNames,
   CustomComponents,
@@ -13,7 +14,8 @@ import type {
   Formatters,
   Labels,
   Mode,
-  DayPickerProps
+  DayPickerProps,
+  DateLib
 } from "../types";
 
 const PropsContext = React.createContext<PropsContextValue | undefined>(
@@ -27,6 +29,7 @@ export type PropsContextValue = DayPickerProps & {
   classNames: ClassNames;
   /** The unique ID of the DayPicker instance. */
   id: string;
+  dateLib: DateLib;
   /** The data attributes to add to the calendar. */
   dataAttributes: DataAttributes;
   /** The components used in the UI. */
@@ -50,6 +53,11 @@ function usePropsContextValue(initialProps: DayPickerProps) {
 
   const { startMonth, endMonth } = getStartEndMonths(initialProps);
 
+  const dateLib = {
+    ...defaultDateLib,
+    ...initialProps.dateLib
+  };
+
   const propsContext: PropsContextValue = {
     ...initialProps,
     startMonth,
@@ -70,7 +78,8 @@ function usePropsContextValue(initialProps: DayPickerProps) {
       ...initialProps.labels
     },
     numberOfMonths: initialProps.numberOfMonths ?? 1,
-    today: initialProps.today ?? new Date()
+    today: initialProps.today ?? new dateLib.Date(),
+    dateLib
   };
 
   return propsContext;
diff --git a/src/dateUtils.ts b/src/dateUtils.ts
deleted file mode 100644
index ac402fe5a..000000000
--- a/src/dateUtils.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-export { addDays } from "date-fns/addDays";
-export { addMonths } from "date-fns/addMonths";
-export { addYears } from "date-fns/addYears";
-export { differenceInCalendarDays } from "date-fns/differenceInCalendarDays";
-export { differenceInCalendarMonths } from "date-fns/differenceInCalendarMonths";
-export { endOfISOWeek } from "date-fns/endOfISOWeek";
-export { endOfMonth } from "date-fns/endOfMonth";
-export { endOfWeek } from "date-fns/endOfWeek";
-export { endOfYear } from "date-fns/endOfYear";
-export { format } from "date-fns/format";
-export { getISOWeek } from "date-fns/getISOWeek";
-export { getUnixTime } from "date-fns/getUnixTime";
-export { getWeek } from "date-fns/getWeek";
-export { isAfter } from "date-fns/isAfter";
-export { isBefore } from "date-fns/isBefore";
-export { isDate } from "date-fns/isDate";
-export { isSameDay } from "date-fns/isSameDay";
-export { isSameMonth } from "date-fns/isSameMonth";
-export { isSameYear } from "date-fns/isSameYear";
-export { setMonth } from "date-fns/setMonth";
-export { setYear } from "date-fns/setYear";
-export { startOfISOWeek } from "date-fns/startOfISOWeek";
-export { startOfMonth } from "date-fns/startOfMonth";
-export { startOfWeek } from "date-fns/startOfWeek";
-export { startOfYear } from "date-fns/startOfYear";
diff --git a/src/formatters/formatCaption.ts b/src/formatters/formatCaption.ts
index 636216ed4..59e99b0f0 100644
--- a/src/formatters/formatCaption.ts
+++ b/src/formatters/formatCaption.ts
@@ -1,4 +1,5 @@
-import { format } from "date-fns/format";
+import { dateLib as defaultDateLib, type format } from "../lib/dateLib";
+import type { DateLib } from "../types";
 
 /**
  * The default formatter for the caption element.
@@ -7,9 +8,10 @@ import { format } from "date-fns/format";
  */
 export function formatCaption(
   month: Date,
-  options?: Parameters<typeof format>[2]
+  options?: Parameters<typeof format>[2],
+  dateLib: DateLib = defaultDateLib
 ) {
-  return format(month, "LLLL y", options);
+  return dateLib.format(month, "LLLL y", options);
 }
 
 /**
diff --git a/src/formatters/formatDay.ts b/src/formatters/formatDay.ts
index 819831345..88bf5b852 100644
--- a/src/formatters/formatDay.ts
+++ b/src/formatters/formatDay.ts
@@ -1,10 +1,16 @@
-import { format } from "date-fns/format";
+import type { format } from "../lib/dateLib";
+import { dateLib as defaultDateLib } from "../lib/dateLib";
+import type { DateLib } from "../types";
 
 /**
  * The default formatter for the day grid cell element.
  *
  * @group Formatters
  */
-export function formatDay(date: Date, options?: Parameters<typeof format>[2]) {
-  return format(date, "d", options);
+export function formatDay(
+  date: Date,
+  options?: Parameters<typeof format>[2],
+  dateLib: DateLib = defaultDateLib
+) {
+  return dateLib.format(date, "d", options);
 }
diff --git a/src/formatters/formatMonthDropdown.ts b/src/formatters/formatMonthDropdown.ts
index abe64cad6..73ae298e7 100644
--- a/src/formatters/formatMonthDropdown.ts
+++ b/src/formatters/formatMonthDropdown.ts
@@ -1,5 +1,5 @@
-import type { Month } from "date-fns";
-import { enUS } from "date-fns/locale/en-US";
+import type { DateFnsMonth } from "../lib/dateLib";
+import { enUS } from "../lib/locales";
 
 /**
  * The default formatter for the month dropdown value.
@@ -7,8 +7,13 @@ import { enUS } from "date-fns/locale/en-US";
  * @group Formatters
  */
 export function formatMonthDropdown(
+  /** The month number to format. */
   monthNumber: number,
+  /** The locale to use for formatting. */
   locale = enUS
 ): string {
-  return locale.localize?.month(monthNumber as Month) ?? monthNumber.toString();
+  return (
+    locale.localize?.month(monthNumber as DateFnsMonth) ??
+    monthNumber.toString()
+  );
 }
diff --git a/src/formatters/formatWeekNumber.ts b/src/formatters/formatWeekNumber.ts
index 63d5098db..15dede6e7 100644
--- a/src/formatters/formatWeekNumber.ts
+++ b/src/formatters/formatWeekNumber.ts
@@ -1,14 +1,8 @@
-import type { format } from "date-fns/format";
-
 /**
  * The default formatter for the week numbers.
  *
  * @group Formatters
  */
-export function formatWeekNumber(
-  weekNumber: number,
-  // eslint-disable-next-line @typescript-eslint/no-unused-vars
-  options?: Parameters<typeof format>[2]
-) {
+export function formatWeekNumber(weekNumber: number) {
   return `${weekNumber.toLocaleString()}`;
 }
diff --git a/src/formatters/formatWeekdayName.ts b/src/formatters/formatWeekdayName.ts
index 4eafe6207..672af3793 100644
--- a/src/formatters/formatWeekdayName.ts
+++ b/src/formatters/formatWeekdayName.ts
@@ -1,4 +1,6 @@
-import { format } from "date-fns/format";
+import type { format } from "../lib/dateLib";
+import { dateLib as defaultDateLib } from "../lib/dateLib";
+import type { DateLib } from "../types";
 
 /**
  * The default formatter for the name of the weekday.
@@ -7,7 +9,8 @@ import { format } from "date-fns/format";
  */
 export function formatWeekdayName(
   weekday: Date,
-  options?: Parameters<typeof format>[2]
+  options?: Parameters<typeof format>[2],
+  dateLib: DateLib = defaultDateLib
 ) {
-  return format(weekday, "cccccc", options);
+  return dateLib.format(weekday, "cccccc", options);
 }
diff --git a/src/helpers/getDates.ts b/src/helpers/getDates.ts
index e4b8442a4..7f540fa9f 100644
--- a/src/helpers/getDates.ts
+++ b/src/helpers/getDates.ts
@@ -1,43 +1,34 @@
-import { addDays } from "date-fns/addDays";
-import { differenceInCalendarDays } from "date-fns/differenceInCalendarDays";
-import { differenceInCalendarMonths } from "date-fns/differenceInCalendarMonths";
-import { endOfISOWeek } from "date-fns/endOfISOWeek";
-import { endOfMonth } from "date-fns/endOfMonth";
-import { endOfWeek } from "date-fns/endOfWeek";
-import { isAfter } from "date-fns/isAfter";
-import { startOfISOWeek } from "date-fns/startOfISOWeek";
-import { startOfWeek } from "date-fns/startOfWeek";
-
 import { PropsContextValue } from "../contexts";
 import type { Mode, DayPickerProps } from "../types";
 
 /** The number of days in a month when having 6 weeks. */
 const NrOfDaysWithFixedWeeks = 42;
 
-/**
- * Return all the dates to display in the calendar.
- *
- * @param firstMonth The first month of the calendar
- * @param lastMonth The last month of the calendar
- * @param maxDate The date to end the calendar at
- * @param props Options for the calendar
- * @param options.ISOWeek Whether or not to use ISOWeek
- * @param options.fixedWeeks Whether or not to use fixed weeks
- * @param options.locale The locale to use
- * @param options.weekStartsOn The day the week starts on
- */
+/** Return all the dates to display in the calendar. */
 export function getDates(
   displayMonths: Date[],
   maxDate: Date | undefined,
   props: Pick<
     PropsContextValue,
-    "ISOWeek" | "fixedWeeks" | "locale" | "weekStartsOn"
+    "ISOWeek" | "fixedWeeks" | "locale" | "weekStartsOn" | "dateLib"
   >
 ): Date[] {
   const firstMonth = displayMonths[0];
   const lastMonth = displayMonths[displayMonths.length - 1];
 
   const { ISOWeek, fixedWeeks, locale, weekStartsOn } = props ?? {};
+  const {
+    startOfWeek,
+    endOfWeek,
+    startOfISOWeek,
+    endOfISOWeek,
+    addDays,
+    differenceInCalendarDays,
+    differenceInCalendarMonths,
+    isAfter,
+    endOfMonth,
+    Date
+  } = props.dateLib;
 
   const startWeekFirstDate = ISOWeek
     ? startOfISOWeek(firstMonth)
diff --git a/src/helpers/getDisplayMonths.ts b/src/helpers/getDisplayMonths.ts
index f58a3970f..ff3569c0d 100644
--- a/src/helpers/getDisplayMonths.ts
+++ b/src/helpers/getDisplayMonths.ts
@@ -1,14 +1,12 @@
-import { addMonths } from "date-fns/addMonths";
-
 import type { PropsContextValue } from "../contexts";
 
 export function getDisplayMonths(
   firstDisplayedMonth: Date,
-  props: Pick<PropsContextValue, "numberOfMonths" | "endMonth">
+  props: Pick<PropsContextValue, "numberOfMonths" | "endMonth" | "dateLib">
 ) {
   const months: Date[] = [];
   for (let i = 0; i < props.numberOfMonths; i++) {
-    const month = addMonths(firstDisplayedMonth, i);
+    const month = props.dateLib.addMonths(firstDisplayedMonth, i);
     if (props.endMonth && month > props.endMonth) {
       break;
     }
diff --git a/src/helpers/getDropdownMonths.ts b/src/helpers/getDropdownMonths.ts
index c0e63375d..a6c28e24a 100644
--- a/src/helpers/getDropdownMonths.ts
+++ b/src/helpers/getDropdownMonths.ts
@@ -1,7 +1,3 @@
-import { addMonths } from "date-fns/addMonths";
-import { isBefore } from "date-fns/isBefore";
-import { startOfMonth } from "date-fns/startOfMonth";
-
 import { DropdownOption } from "../components/Dropdown";
 import { PropsContextValue } from "../contexts";
 
@@ -10,14 +6,14 @@ export function getDropdownMonths(
   displayMonth: Date,
   props: Pick<
     PropsContextValue,
-    "formatters" | "locale" | "startMonth" | "endMonth"
+    "formatters" | "locale" | "startMonth" | "endMonth" | "dateLib"
   >
 ): DropdownOption[] | undefined {
   const { startMonth, endMonth } = props;
-
   if (!startMonth) return undefined;
   if (!endMonth) return undefined;
 
+  const { addMonths, startOfMonth, isBefore, Date } = props.dateLib;
   const year = displayMonth.getFullYear();
 
   const navStartMonth = startOfMonth(startMonth);
diff --git a/src/helpers/getDropdownYears.ts b/src/helpers/getDropdownYears.ts
index 0bc0fe10a..6a2a86bb3 100644
--- a/src/helpers/getDropdownYears.ts
+++ b/src/helpers/getDropdownYears.ts
@@ -1,10 +1,3 @@
-import { addYears } from "date-fns/addYears";
-import { endOfYear } from "date-fns/endOfYear";
-import { isBefore } from "date-fns/isBefore";
-import { isSameYear } from "date-fns/isSameYear";
-import { startOfMonth } from "date-fns/startOfMonth";
-import { startOfYear } from "date-fns/startOfYear";
-
 import { DropdownOption } from "../components/Dropdown";
 import { PropsContextValue } from "../contexts";
 
@@ -13,13 +6,21 @@ export function getDropdownYears(
   displayMonth: Date,
   props: Pick<
     PropsContextValue,
-    "formatters" | "locale" | "startMonth" | "endMonth"
+    "formatters" | "locale" | "startMonth" | "endMonth" | "dateLib"
   >
 ): DropdownOption[] | undefined {
   const { startMonth, endMonth } = props;
   if (!startMonth) return undefined;
   if (!endMonth) return undefined;
-
+  const {
+    startOfMonth,
+    startOfYear,
+    endOfYear,
+    addYears,
+    isBefore,
+    isSameYear,
+    Date
+  } = props.dateLib;
   const month = displayMonth.getMonth();
   const firstNavYear = startOfYear(startMonth);
   const lastNavYear = endOfYear(endMonth);
diff --git a/src/helpers/getInitialMonth.ts b/src/helpers/getInitialMonth.ts
index bc82015d6..d6b645588 100644
--- a/src/helpers/getInitialMonth.ts
+++ b/src/helpers/getInitialMonth.ts
@@ -1,34 +1,31 @@
-import { addMonths } from "date-fns/addMonths";
-import { differenceInCalendarMonths } from "date-fns/differenceInCalendarMonths";
-import { startOfMonth } from "date-fns/startOfMonth";
-
 import type { PropsContextValue } from "../contexts";
 
 /** Return the start month based on the props passed to DayPicker. */
 export function getInitialMonth(
-  props: Partial<
-    Pick<
-      PropsContextValue,
-      | "fromYear"
-      | "toYear"
-      | "startMonth"
-      | "endMonth"
-      | "month"
-      | "defaultMonth"
-      | "today"
-      | "numberOfMonths"
-    >
+  props: Pick<
+    PropsContextValue,
+    | "fromYear"
+    | "toYear"
+    | "startMonth"
+    | "endMonth"
+    | "month"
+    | "defaultMonth"
+    | "today"
+    | "numberOfMonths"
+    | "dateLib"
   >
 ): Date {
   const {
     month,
     defaultMonth,
+    dateLib: { Date },
     today,
     numberOfMonths = 1,
     endMonth,
     startMonth
   } = props;
   let initialMonth = month || defaultMonth || today || new Date();
+  const { differenceInCalendarMonths, addMonths, startOfMonth } = props.dateLib;
 
   // Fix the initialMonth if is after the to-date
   if (endMonth && differenceInCalendarMonths(endMonth, initialMonth) < 0) {
diff --git a/src/helpers/getMonths.ts b/src/helpers/getMonths.ts
index b9c9529f1..18561cef0 100644
--- a/src/helpers/getMonths.ts
+++ b/src/helpers/getMonths.ts
@@ -1,14 +1,6 @@
-import { addDays } from "date-fns/addDays";
-import { endOfISOWeek } from "date-fns/endOfISOWeek";
-import { endOfMonth } from "date-fns/endOfMonth";
-import { endOfWeek } from "date-fns/endOfWeek";
-import { getISOWeek } from "date-fns/getISOWeek";
-import { getWeek } from "date-fns/getWeek";
-import { startOfISOWeek } from "date-fns/startOfISOWeek";
-import { startOfWeek } from "date-fns/startOfWeek";
-
 import { CalendarWeek, CalendarDay, CalendarMonth } from "../classes";
 import type { PropsContextValue } from "../contexts";
+import type { DateLib } from "../types";
 
 /** Return the months to display in the calendar. */
 export function getMonths(
@@ -19,6 +11,7 @@ export function getMonths(
   /** Options from the props context. */
   props: Pick<
     PropsContextValue,
+    | "dateLib"
     | "fixedWeeks"
     | "ISOWeek"
     | "locale"
@@ -27,6 +20,16 @@ export function getMonths(
     | "firstWeekContainsDate"
   >
 ): CalendarMonth[] {
+  const {
+    startOfWeek,
+    endOfWeek,
+    startOfISOWeek,
+    endOfISOWeek,
+    endOfMonth,
+    addDays,
+    getWeek,
+    getISOWeek
+  } = props.dateLib;
   const dayPickerMonths = displayMonths.reduce<CalendarMonth[]>(
     (months, month) => {
       const firstDateOfFirstWeek = props.ISOWeek
@@ -68,7 +71,7 @@ export function getMonths(
               });
           const week = weeks.find((week) => week.weekNumber === weekNumber);
 
-          const day = new CalendarDay(date, month);
+          const day = new CalendarDay(date, month, props.dateLib);
           if (!week) {
             weeks.push(new CalendarWeek(weekNumber, [day]));
           } else {
diff --git a/src/helpers/getNextFocus.tsx b/src/helpers/getNextFocus.tsx
index e5e16f019..91ecaf126 100644
--- a/src/helpers/getNextFocus.tsx
+++ b/src/helpers/getNextFocus.tsx
@@ -24,6 +24,7 @@ export function getNextFocus(
   focused: CalendarDay,
   options: Pick<
     PropsContextValue,
+    | "dateLib"
     | "disabled"
     | "hidden"
     | "modifiers"
@@ -48,15 +49,21 @@ export function getNextFocus(
   );
 
   const isDisabled = Boolean(
-    options.disabled && dateMatchModifiers(possibleFocusDate, options.disabled)
+    options.disabled &&
+      dateMatchModifiers(possibleFocusDate, options.disabled, options.dateLib)
   );
 
   const isHidden = Boolean(
-    options.hidden && dateMatchModifiers(possibleFocusDate, options.hidden)
+    options.hidden &&
+      dateMatchModifiers(possibleFocusDate, options.hidden, options.dateLib)
   );
 
   const targetMonth = possibleFocusDate;
-  const focusDay = new CalendarDay(possibleFocusDate, targetMonth);
+  const focusDay = new CalendarDay(
+    possibleFocusDate,
+    targetMonth,
+    options.dateLib
+  );
   if (!isDisabled && !isHidden) {
     return focusDay;
   }
diff --git a/src/helpers/getNextMonth.ts b/src/helpers/getNextMonth.ts
index 412734c54..6a00b286b 100644
--- a/src/helpers/getNextMonth.ts
+++ b/src/helpers/getNextMonth.ts
@@ -1,7 +1,3 @@
-import { addMonths } from "date-fns/addMonths";
-import { differenceInCalendarMonths } from "date-fns/differenceInCalendarMonths";
-import { startOfMonth } from "date-fns/startOfMonth";
-
 import { PropsContextValue } from "../contexts";
 
 /**
@@ -22,12 +18,14 @@ export function getNextMonth(
     | "numberOfMonths"
     | "pagedNavigation"
     | "disableNavigation"
+    | "dateLib"
   >
 ): Date | undefined {
   if (props.disableNavigation) {
     return undefined;
   }
   const { pagedNavigation, numberOfMonths } = props;
+  const { startOfMonth, addMonths, differenceInCalendarMonths } = props.dateLib;
   const offset = pagedNavigation ? numberOfMonths : 1;
   const month = startOfMonth(firstDisplayedMonth);
 
diff --git a/src/helpers/getPossibleFocusDate.ts b/src/helpers/getPossibleFocusDate.ts
index 09110ae86..7ee04886e 100644
--- a/src/helpers/getPossibleFocusDate.ts
+++ b/src/helpers/getPossibleFocusDate.ts
@@ -1,17 +1,5 @@
-import { addDays } from "date-fns/addDays";
-import { addMonths } from "date-fns/addMonths";
-import { addWeeks } from "date-fns/addWeeks";
-import { addYears } from "date-fns/addYears";
-import { endOfISOWeek } from "date-fns/endOfISOWeek";
-import { endOfWeek } from "date-fns/endOfWeek";
-import { max } from "date-fns/max";
-import { min } from "date-fns/min";
-import { startOfISOWeek } from "date-fns/startOfISOWeek";
-import { startOfWeek } from "date-fns/startOfWeek";
-
 import type { PropsContextValue } from "../contexts";
 import type { MoveFocusBy, MoveFocusDir } from "../types";
-import { Mode } from "../types";
 
 /** Return the next date that should be focused. */
 export function getPossibleFocusDate(
@@ -20,11 +8,27 @@ export function getPossibleFocusDate(
   focusedDate: Date,
   options: Pick<
     PropsContextValue,
-    "locale" | "ISOWeek" | "weekStartsOn" | "startMonth" | "endMonth"
+    | "locale"
+    | "ISOWeek"
+    | "weekStartsOn"
+    | "startMonth"
+    | "endMonth"
+    | "dateLib"
   >
 ): Date {
   const { weekStartsOn, startMonth, endMonth, locale, ISOWeek } = options;
-
+  const {
+    addDays,
+    addMonths,
+    addYears,
+    addWeeks,
+    startOfISOWeek,
+    endOfISOWeek,
+    startOfWeek,
+    endOfWeek,
+    max,
+    min
+  } = options.dateLib;
   const moveFns = {
     day: addDays,
     week: addWeeks,
diff --git a/src/helpers/getPreviousMonth.ts b/src/helpers/getPreviousMonth.ts
index 4b215d27c..55674f98a 100644
--- a/src/helpers/getPreviousMonth.ts
+++ b/src/helpers/getPreviousMonth.ts
@@ -1,7 +1,3 @@
-import { addMonths } from "date-fns/addMonths";
-import { differenceInCalendarMonths } from "date-fns/differenceInCalendarMonths";
-import { startOfMonth } from "date-fns/startOfMonth";
-
 import type { PropsContextValue } from "../contexts";
 
 /**
@@ -18,13 +14,18 @@ export function getPreviousMonth(
   firstDisplayedMonth: Date,
   props: Pick<
     PropsContextValue,
-    "startMonth" | "numberOfMonths" | "pagedNavigation" | "disableNavigation"
+    | "startMonth"
+    | "numberOfMonths"
+    | "pagedNavigation"
+    | "disableNavigation"
+    | "dateLib"
   >
 ): Date | undefined {
   if (props.disableNavigation) {
     return undefined;
   }
   const { pagedNavigation, numberOfMonths } = props;
+  const { startOfMonth, addMonths, differenceInCalendarMonths } = props.dateLib;
   const offset = pagedNavigation ? numberOfMonths : 1;
   const month = startOfMonth(firstDisplayedMonth);
   if (!props.startMonth) {
diff --git a/src/helpers/getStartEndMonths.ts b/src/helpers/getStartEndMonths.ts
index 184e219af..632bc202b 100644
--- a/src/helpers/getStartEndMonths.ts
+++ b/src/helpers/getStartEndMonths.ts
@@ -1,11 +1,5 @@
-import { addYears } from "date-fns/addYears";
-import { endOfMonth } from "date-fns/endOfMonth";
-import { endOfYear } from "date-fns/endOfYear";
-import { startOfDay } from "date-fns/startOfDay";
-import { startOfMonth } from "date-fns/startOfMonth";
-import { startOfYear } from "date-fns/startOfYear";
-
-import type { Mode, DayPickerProps } from "../types";
+import { dateLib } from "../lib";
+import type { DayPickerProps } from "../types";
 
 /**
  * Return the `fromMonth` and `toMonth` prop values values parsing the DayPicker
@@ -18,6 +12,7 @@ export function getStartEndMonths(
     | "endMonth"
     | "today"
     | "captionLayout"
+    | "dateLib"
     // Deprecated:
     | "fromYear"
     | "toYear"
@@ -26,7 +21,18 @@ export function getStartEndMonths(
   >
 ): Pick<DayPickerProps, "startMonth" | "endMonth"> {
   let { startMonth, endMonth } = props;
-
+  const {
+    startOfYear,
+    startOfDay,
+    startOfMonth,
+    endOfMonth,
+    addYears,
+    endOfYear,
+    Date
+  } = {
+    ...dateLib,
+    ...props.dateLib
+  };
   // Handle deprecated code
   const { fromYear, toYear, fromMonth, toMonth } = props;
   if (!startMonth && fromMonth) {
diff --git a/src/helpers/getWeekdays.ts b/src/helpers/getWeekdays.ts
index e729c0b95..c3a636444 100644
--- a/src/helpers/getWeekdays.ts
+++ b/src/helpers/getWeekdays.ts
@@ -1,26 +1,26 @@
-import type { Locale } from "date-fns";
-import { addDays } from "date-fns/addDays";
-import { startOfISOWeek } from "date-fns/startOfISOWeek";
-import { startOfWeek } from "date-fns/startOfWeek";
+import type { Locale } from "../lib/dateLib";
+import { dateLib as defaultDateLib } from "../lib/dateLib";
+import type { DateLib } from "../types";
 
 /**
  * Generate a series of 7 days, starting from the week, to use for formatting
  * the weekday names (Monday, Tuesday, etc.).
  */
 export function getWeekdays(
-  locale?: Locale,
+  locale?: Locale | undefined,
   /** The index of the first day of the week (0 - Sunday). */
-  weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6,
+  weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined,
   /** Use ISOWeek instead of locale/ */
-  ISOWeek?: boolean
+  ISOWeek?: boolean | undefined,
+  dateLib: DateLib = defaultDateLib
 ): Date[] {
   const start = ISOWeek
-    ? startOfISOWeek(new Date())
-    : startOfWeek(new Date(), { locale, weekStartsOn });
+    ? dateLib.startOfISOWeek(new dateLib.Date())
+    : dateLib.startOfWeek(new dateLib.Date(), { locale, weekStartsOn });
 
   const days = [];
   for (let i = 0; i < 7; i++) {
-    const day = addDays(start, i);
+    const day = dateLib.addDays(start, i);
     days.push(day);
   }
   return days;
diff --git a/src/index.ts b/src/index.ts
index 1c76196fa..3182f1e00 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -7,6 +7,8 @@ export * from "./components/custom-components";
 export * from "./contexts";
 export * from "./selection";
 
+export * from "./lib";
+
 export * from "./formatters";
 export * from "./helpers";
 export * from "./labels";
diff --git a/src/jalali.tsx b/src/jalali.tsx
new file mode 100644
index 000000000..c9c22f442
--- /dev/null
+++ b/src/jalali.tsx
@@ -0,0 +1,9 @@
+import React from "react";
+
+import * as jalaliDateLib from "date-fns-jalali";
+
+import { DayPicker as DayPickerComponent, type DayPickerProps } from "./";
+
+export function DayPicker(props: DayPickerProps) {
+  return <DayPickerComponent dateLib={jalaliDateLib} {...props} />;
+}
diff --git a/src/labels/labelCaption.ts b/src/labels/labelCaption.ts
index 70e38889f..381af55d5 100644
--- a/src/labels/labelCaption.ts
+++ b/src/labels/labelCaption.ts
@@ -1,4 +1,6 @@
-import { format } from "date-fns/format";
+import type { DateLib } from "../";
+import type { format } from "../lib/dateLib";
+import { dateLib as defaultDateLib } from "../lib/dateLib";
 
 /**
  * Return an ARIA label for the month caption. The label is used in an aria-live
@@ -8,7 +10,8 @@ import { format } from "date-fns/format";
  */
 export function labelCaption(
   date: Date,
-  options: Parameters<typeof format>[2]
+  options?: Parameters<typeof format>[2],
+  dateLib: DateLib = defaultDateLib
 ) {
   return "";
 }
diff --git a/src/labels/labelDay.ts b/src/labels/labelDay.ts
index e5e6c4c47..24dcc4d68 100644
--- a/src/labels/labelDay.ts
+++ b/src/labels/labelDay.ts
@@ -1,5 +1,6 @@
-import { format } from "date-fns/format";
-
+import type { DateLib } from "../";
+import type { format } from "../lib/dateLib";
+import { dateLib as defaultDateLib } from "../lib/dateLib";
 import type { Modifiers } from "../types";
 
 /**
@@ -14,7 +15,8 @@ import type { Modifiers } from "../types";
 export function labelDay(
   date: Date,
   modifiers: Modifiers,
-  options: Parameters<typeof format>[2]
+  options?: Parameters<typeof format>[2],
+  dateLib: DateLib = defaultDateLib
 ) {
   return "";
 }
diff --git a/src/labels/labelGrid.ts b/src/labels/labelGrid.ts
index 7ba35b612..817697857 100644
--- a/src/labels/labelGrid.ts
+++ b/src/labels/labelGrid.ts
@@ -1,10 +1,16 @@
-import { format } from "date-fns/format";
+import type { format } from "../lib/dateLib";
+import { dateLib as defaultDateLib } from "../lib/dateLib";
+import type { DateLib } from "../types";
 
 /**
  * Return the default ARIA label for the month grid.
  *
  * @group Labels
  */
-export function labelGrid(month: Date, options?: Parameters<typeof format>[2]) {
-  return format(month, "LLLL y", options);
+export function labelGrid(
+  month: Date,
+  options?: Parameters<typeof format>[2],
+  dateLib: DateLib = defaultDateLib
+) {
+  return dateLib.format(month, "LLLL y", options);
 }
diff --git a/src/labels/labelMonthDropdown.ts b/src/labels/labelMonthDropdown.ts
index ee07e274d..f6c78e46c 100644
--- a/src/labels/labelMonthDropdown.ts
+++ b/src/labels/labelMonthDropdown.ts
@@ -1,4 +1,4 @@
-import type { format } from "date-fns/format";
+import type { format } from "../lib/dateLib";
 
 /**
  * Return the default ARIA label for the WeekNumber element.
diff --git a/src/labels/labelNext.ts b/src/labels/labelNext.ts
index 6d0c30c78..bdb356351 100644
--- a/src/labels/labelNext.ts
+++ b/src/labels/labelNext.ts
@@ -1,4 +1,4 @@
-import type { format } from "date-fns/format";
+import type { format } from "../lib/dateLib";
 
 /**
  * Return the default ARIA label for next month button.
diff --git a/src/labels/labelPrevious.ts b/src/labels/labelPrevious.ts
index 095bf9eb5..0345a8018 100644
--- a/src/labels/labelPrevious.ts
+++ b/src/labels/labelPrevious.ts
@@ -1,4 +1,4 @@
-import type { format } from "date-fns/format";
+import type { format } from "../lib/dateLib";
 
 /**
  * Return the default ARIA label for next month button.
diff --git a/src/labels/labelWeekNumber.ts b/src/labels/labelWeekNumber.ts
index f29997fd4..e8ed14e93 100644
--- a/src/labels/labelWeekNumber.ts
+++ b/src/labels/labelWeekNumber.ts
@@ -1,4 +1,4 @@
-import type { format } from "date-fns/format";
+import type { format } from "../lib/dateLib";
 
 /**
  * Return the default ARIA label for the week number element.
diff --git a/src/labels/labelWeekNumberHeader.ts b/src/labels/labelWeekNumberHeader.ts
index b5586799c..95e686110 100644
--- a/src/labels/labelWeekNumberHeader.ts
+++ b/src/labels/labelWeekNumberHeader.ts
@@ -1,4 +1,4 @@
-import type { format } from "date-fns/format";
+import type { format } from "../lib/dateLib";
 
 /**
  * Return the default ARIA label for the week number header element.
diff --git a/src/labels/labelWeekday.ts b/src/labels/labelWeekday.ts
index 5fb115b04..169b0ec29 100644
--- a/src/labels/labelWeekday.ts
+++ b/src/labels/labelWeekday.ts
@@ -1,4 +1,6 @@
-import { format } from "date-fns/format";
+import type { format } from "../lib/dateLib";
+import { dateLib as defaultDateLib } from "../lib/dateLib";
+import type { DateLib } from "../types";
 
 /**
  * Return he default ARIA label for the Weekday element.
@@ -7,7 +9,8 @@ import { format } from "date-fns/format";
  */
 export function labelWeekday(
   date: Date,
-  options?: Parameters<typeof format>[2]
+  options?: Parameters<typeof format>[2],
+  dateLib: DateLib = defaultDateLib
 ): string {
-  return format(date, "cccc", options);
+  return dateLib.format(date, "cccc", options);
 }
diff --git a/src/labels/labelYearDropdown.ts b/src/labels/labelYearDropdown.ts
index 5dc3c8db0..2e54948df 100644
--- a/src/labels/labelYearDropdown.ts
+++ b/src/labels/labelYearDropdown.ts
@@ -1,4 +1,4 @@
-import type { format } from "date-fns/format";
+import type { format } from "../lib/dateLib";
 
 /**
  * Return the default ARIA label for the years dropdown.
diff --git a/src/lib/dateLib.ts b/src/lib/dateLib.ts
new file mode 100644
index 000000000..28c1fefba
--- /dev/null
+++ b/src/lib/dateLib.ts
@@ -0,0 +1,79 @@
+import { GenericDateConstructor } from "date-fns";
+import { addDays } from "date-fns/addDays";
+import { addMonths } from "date-fns/addMonths";
+import { addWeeks } from "date-fns/addWeeks";
+import { addYears } from "date-fns/addYears";
+import { differenceInCalendarDays } from "date-fns/differenceInCalendarDays";
+import { differenceInCalendarMonths } from "date-fns/differenceInCalendarMonths";
+import { endOfISOWeek } from "date-fns/endOfISOWeek";
+import { endOfMonth } from "date-fns/endOfMonth";
+import { endOfWeek } from "date-fns/endOfWeek";
+import { endOfYear } from "date-fns/endOfYear";
+import { format } from "date-fns/format";
+import { getISOWeek } from "date-fns/getISOWeek";
+import { getUnixTime } from "date-fns/getUnixTime";
+import { getWeek } from "date-fns/getWeek";
+import { isAfter } from "date-fns/isAfter";
+import { isBefore } from "date-fns/isBefore";
+import { isDate } from "date-fns/isDate";
+import { isSameDay } from "date-fns/isSameDay";
+import { isSameMonth } from "date-fns/isSameMonth";
+import { isSameYear } from "date-fns/isSameYear";
+import { max } from "date-fns/max";
+import { min } from "date-fns/min";
+import { setMonth } from "date-fns/setMonth";
+import { setYear } from "date-fns/setYear";
+import { startOfDay } from "date-fns/startOfDay";
+import { startOfISOWeek } from "date-fns/startOfISOWeek";
+import { startOfMonth } from "date-fns/startOfMonth";
+import { startOfWeek } from "date-fns/startOfWeek";
+import { startOfYear } from "date-fns/startOfYear";
+
+/** @private */
+export type { Locale } from "date-fns";
+/** @private */
+export type { format } from "date-fns";
+/** @private */
+export type { Month as DateFnsMonth } from "date-fns";
+
+/** The default date library to use with the date picker. */
+export const dateLib = {
+  /**
+   * The constructor of the date object.
+   *
+   * You can pass a `DateConstructor` such as `UTCDate` from `@date-fns/utc` to
+   * use UTC dates.
+   *
+   * @see https://react-day-picker.js.org/using-daypicker/localization#utc-dates
+   */
+  Date: Date as GenericDateConstructor,
+  addDays,
+  addMonths,
+  addWeeks,
+  addYears,
+  differenceInCalendarDays,
+  differenceInCalendarMonths,
+  endOfISOWeek,
+  endOfMonth,
+  endOfWeek,
+  endOfYear,
+  format,
+  getISOWeek,
+  getUnixTime,
+  getWeek,
+  isAfter,
+  isBefore,
+  isDate,
+  isSameDay,
+  isSameMonth,
+  isSameYear,
+  max,
+  min,
+  setMonth,
+  setYear,
+  startOfDay,
+  startOfISOWeek,
+  startOfMonth,
+  startOfWeek,
+  startOfYear
+};
diff --git a/src/lib/index.ts b/src/lib/index.ts
new file mode 100644
index 000000000..00d9486da
--- /dev/null
+++ b/src/lib/index.ts
@@ -0,0 +1,1 @@
+export * from "./dateLib";
diff --git a/src/lib/locales.ts b/src/lib/locales.ts
new file mode 100644
index 000000000..3c0f52435
--- /dev/null
+++ b/src/lib/locales.ts
@@ -0,0 +1,1 @@
+export { enUS } from "date-fns/locale/en-US";
diff --git a/src/selection/useMulti.tsx b/src/selection/useMulti.tsx
index 5d19f5949..c2982d709 100644
--- a/src/selection/useMulti.tsx
+++ b/src/selection/useMulti.tsx
@@ -1,7 +1,6 @@
 import React from "react";
 
-import { isSameDay } from "date-fns/isSameDay";
-
+import { useProps } from "../contexts";
 import type { Modifiers, PropsMulti } from "../types";
 
 export type MultiContextValue<T> = {
@@ -31,12 +30,15 @@ function useMultiContextValue<T extends PropsMulti>({
   selected,
   onSelect
 }: T): MultiContextValue<T> {
+  const {
+    dateLib: { isSameDay, Date }
+  } = useProps();
   const [dates, setDates] = React.useState<Date[] | undefined>(selected);
 
   // Update the selected date if the required flag is set.
   React.useEffect(() => {
     if (required && dates === undefined) setDates([new Date()]);
-  }, [required, dates]);
+  }, [required, dates, Date]);
 
   // Update the selected date if the initialDates changes.
   React.useEffect(() => {
diff --git a/src/selection/useRange.tsx b/src/selection/useRange.tsx
index ebb090bc0..14a913df3 100644
--- a/src/selection/useRange.tsx
+++ b/src/selection/useRange.tsx
@@ -1,7 +1,6 @@
 import React from "react";
 
-import { differenceInCalendarDays } from "date-fns/differenceInCalendarDays";
-
+import { useProps } from "../contexts";
 import { DateRange, Modifiers, PropsRange } from "../types";
 import { addToRange } from "../utils";
 import { isDateInRange } from "../utils/isDateInRange";
@@ -33,6 +32,8 @@ function useRangeContextValue<T extends PropsRange>({
   selected,
   onSelect
 }: T): RangeContextValue<T> {
+  const { dateLib } = useProps();
+  const { differenceInCalendarDays } = dateLib;
   const [range, setRange] = React.useState<DateRange | undefined>(selected);
 
   // Update the selected date if the required flag is set.
@@ -47,15 +48,17 @@ function useRangeContextValue<T extends PropsRange>({
   }, [selected]);
 
   const isSelected = required
-    ? (date: Date) => isDateInRange(date, range as DateRange)
-    : (date: Date) => range && isDateInRange(date, range);
+    ? (date: Date) => isDateInRange(date, range as DateRange, dateLib)
+    : (date: Date) => range && isDateInRange(date, range, dateLib);
 
   const setSelected = (
     triggerDate: Date,
     modifiers: Modifiers,
     e: React.MouseEvent | React.KeyboardEvent
   ) => {
-    const newRange = triggerDate ? addToRange(triggerDate, range) : undefined;
+    const newRange = triggerDate
+      ? addToRange(triggerDate, range, dateLib)
+      : undefined;
 
     if (min) {
       if (
diff --git a/src/selection/useSingle.tsx b/src/selection/useSingle.tsx
index 62730b19e..5f6ed699d 100644
--- a/src/selection/useSingle.tsx
+++ b/src/selection/useSingle.tsx
@@ -1,7 +1,6 @@
 import React from "react";
 
-import { isSameDay } from "date-fns/isSameDay";
-
+import { useProps } from "../contexts";
 import { Modifiers, PropsSingle } from "../types";
 
 export type SingleContextValue<T> = {
@@ -30,11 +29,13 @@ function useSingleContextValue<T extends PropsSingle>({
   onSelect
 }: T): SingleContextValue<T> {
   const [date, setDate] = React.useState<Date | undefined>(selected);
-
+  const {
+    dateLib: { isSameDay, Date }
+  } = useProps();
   // Update the selected date if the required flag is set.
   React.useEffect(() => {
     if (required && date === undefined) setDate(new Date());
-  }, [required, date]);
+  }, [required, date, Date]);
 
   // Update the selected date if the selected changes.
   React.useEffect(() => {
diff --git a/src/types/props.ts b/src/types/props.ts
index e384e49d0..bb2ac4c3e 100644
--- a/src/types/props.ts
+++ b/src/types/props.ts
@@ -1,6 +1,8 @@
 import React from "react";
 
-import type { Locale } from "date-fns";
+import { GenericDateConstructor } from "date-fns";
+
+import type { Locale } from "../lib/dateLib";
 
 import type {
   ClassNames,
@@ -17,7 +19,7 @@ import type {
   Modifiers,
   DateRange,
   Mode,
-  DateUtils
+  DateLib
 } from "./shared";
 
 /**
@@ -53,8 +55,6 @@ export interface PropsBase {
    */
   mode?: Mode | undefined;
 
-  utc?: boolean | undefined;
-
   /** Class name to add to the root element */
   className?: string;
   /**
@@ -326,7 +326,14 @@ export interface PropsBase {
   /** Event handler when a week number is clicked */
   onWeekNumberClick?: WeekNumberMouseEventHandler;
 
-  dateUtils?: Partial<DateUtils> | undefined;
+  /**
+   * Replace the default date library with a custom one.
+   *
+   * @private
+   * @since 9.0.0
+   * @experimental
+   */
+  dateLib?: Partial<DateLib> | undefined;
 }
 /**
  * The props when the single selection is required.
diff --git a/src/types/shared.ts b/src/types/shared.ts
index 4ac46bff1..d0041d422 100644
--- a/src/types/shared.ts
+++ b/src/types/shared.ts
@@ -10,7 +10,6 @@ import {
   SelectionState
 } from "../UI";
 import * as components from "../components/custom-components";
-import * as dateUtils from "../dateUtils";
 import {
   formatCaption,
   formatDay,
@@ -32,6 +31,7 @@ import {
   labelWeekNumberHeader,
   labelYearDropdown
 } from "../labels";
+import { dateLib } from "../lib";
 
 /**
  * Selection modes supported by DayPicker.
@@ -53,7 +53,7 @@ export type CustomComponents = {
   [key in keyof typeof components]?: (typeof components)[key];
 };
 
-export type DateUtils = typeof dateUtils;
+export type DateLib = typeof dateLib;
 
 /** Represent a map of formatters used to render localized content. */
 export type Formatters = {
diff --git a/src/utc.tsx b/src/utc.tsx
new file mode 100644
index 000000000..38838d551
--- /dev/null
+++ b/src/utc.tsx
@@ -0,0 +1,9 @@
+import React from "react";
+
+import { UTCDate } from "@date-fns/utc";
+
+import { DayPicker as DayPickerComponent, type DayPickerProps } from "./";
+
+export function DayPicker(props: DayPickerProps) {
+  return <DayPickerComponent dateLib={{ Date: UTCDate }} {...props} />;
+}
diff --git a/src/utils/addToRange.ts b/src/utils/addToRange.ts
index b995d74b2..739a21e12 100644
--- a/src/utils/addToRange.ts
+++ b/src/utils/addToRange.ts
@@ -1,8 +1,5 @@
-import { isAfter } from "date-fns/isAfter";
-import { isBefore } from "date-fns/isBefore";
-import { isSameDay } from "date-fns/isSameDay";
-
-import type { DateRange } from "../types";
+import { dateLib as defaultDateLib } from "../lib";
+import type { DateRange, DateLib } from "../types";
 
 /**
  * Add a day to an existing range.
@@ -12,8 +9,13 @@ import type { DateRange } from "../types";
  *
  * @group Utilities
  */
-export function addToRange(date: Date, range?: DateRange): DateRange {
+export function addToRange(
+  date: Date,
+  range: DateRange | undefined,
+  dateLib: DateLib = defaultDateLib
+): DateRange {
   const { from, to } = range || {};
+  const { isSameDay, isAfter, isBefore } = dateLib;
   if (from && to) {
     if (isSameDay(to, date) && isSameDay(from, date)) {
       return { from: undefined, to: undefined };
diff --git a/src/utils/dateMatchModifiers.ts b/src/utils/dateMatchModifiers.ts
index 1f1aa7fc9..2777240b6 100644
--- a/src/utils/dateMatchModifiers.ts
+++ b/src/utils/dateMatchModifiers.ts
@@ -1,9 +1,5 @@
-import { differenceInCalendarDays } from "date-fns/differenceInCalendarDays";
-import { isAfter } from "date-fns/isAfter";
-import { isDate } from "date-fns/isDate";
-import { isSameDay } from "date-fns/isSameDay";
-
-import type { Matcher } from "../types";
+import { dateLib as defaultDateLib } from "../lib";
+import type { DateLib, Matcher } from "../types";
 
 import { isDateInRange } from "./isDateInRange";
 import {
@@ -16,12 +12,12 @@ import {
 
 /** Returns true if `value` is a Date type. */
 function isDateType(value: unknown): value is Date {
-  return isDate(value);
+  return defaultDateLib.isDate(value);
 }
 
 /** Returns true if `value` is an array of valid dates. */
 function isArrayOfDates(value: unknown): value is Date[] {
-  return Array.isArray(value) && value.every(isDate);
+  return Array.isArray(value) && value.every(defaultDateLib.isDate);
 }
 
 /**
@@ -44,9 +40,11 @@ function isArrayOfDates(value: unknown): value is Date[] {
  */
 export function dateMatchModifiers(
   date: Date,
-  matchers: Matcher | Matcher[]
+  matchers: Matcher | Matcher[],
+  dateUtils: DateLib = defaultDateLib
 ): boolean {
   const matchersArr = !Array.isArray(matchers) ? [matchers] : matchers;
+  const { isSameDay, differenceInCalendarDays, isAfter } = dateUtils;
   return matchersArr.some((matcher: Matcher) => {
     if (typeof matcher === "boolean") {
       return matcher;
@@ -58,7 +56,7 @@ export function dateMatchModifiers(
       return matcher.includes(date);
     }
     if (isDateRange(matcher)) {
-      return isDateInRange(date, matcher);
+      return isDateInRange(date, matcher, dateUtils);
     }
     if (isDayOfWeekType(matcher)) {
       return matcher.dayOfWeek.includes(date.getDay());
diff --git a/src/utils/isDateInRange.ts b/src/utils/isDateInRange.ts
index 55178bc87..1b562f0f6 100644
--- a/src/utils/isDateInRange.ts
+++ b/src/utils/isDateInRange.ts
@@ -1,15 +1,18 @@
-import { differenceInCalendarDays } from "date-fns/differenceInCalendarDays";
-import { isSameDay } from "date-fns/isSameDay";
-
-import type { DateRange } from "../types";
+import { dateLib as defaultDateLib } from "../lib";
+import type { DateRange, DateLib } from "../types";
 
 /**
  * Determines whether a given date is inside a specified date range.
  *
  * @group Utilities
  */
-export function isDateInRange(date: Date, range: DateRange): boolean {
+export function isDateInRange(
+  date: Date,
+  range: DateRange,
+  dateLib: DateLib = defaultDateLib
+): boolean {
   let { from, to } = range;
+  const { differenceInCalendarDays, isSameDay } = dateLib;
   if (from && to) {
     const isRangeInverted = differenceInCalendarDays(to, from) < 0;
     if (isRangeInverted) {
diff --git a/website/docs/intro.md b/website/docs/intro.md
index a930951b0..4a1d945f4 100644
--- a/website/docs/intro.md
+++ b/website/docs/intro.md
@@ -11,12 +11,13 @@ DayPicker is a [React](https://react.dev) component to create date pickers, cale
 
 ## Features
 
-- Minimal design for easy customization.
-- Extensive props for configuring the calendar.
-- Supports selection of a single day, multiple days, ranges, or custom selections.
-- Can be localized in any language.
-- Complies with WCAG 2.1 AA requirements for accessibility.
-- Includes customizable internal components for complex use cases.
+- 🛠 An extensive set of props for [customizing](./using-daypicker/customization.mdx) the calendar.
+- 🎨 Minimal design that can be [easily styled](./using-daypicker/styling.mdx) with CSS or any CSS framework.
+- 📅 Supports [selections](./using-daypicker/selection-modes.mdx) of single day, multiple days, ranges of days, or [custom selections](./advanced-guides/custom-selections.mdx).
+- 🌍 Can be [localized](./using-daypicker/localization.mdx) into any language, supports [ISO 8601 dates](./using-daypicker/localization.mdx#iso-week-dates), [UTC dates](./using-daypicker/localization.mdx#utc-dates), and [Jalali calendar](./using-daypicker/localization.mdx#jalali-calendar).
+- ♿ Complies with WCAG 2.1 AA requirements for [accessibility](./using-daypicker/accessibility.mdx).
+- ⚙️ [Customizable components](./advanced-guides/custom-components.mdx) for more complex use cases.
+- 🔤 Unopinionated integration [with input fields](./advanced-guides/input-fields.mdx).
 
 DayPicker is written in TypeScript and compiled to CommonJS and ESM. It requires [date-fns](https://date-fns.org) as a peer dependency.
 
diff --git a/website/docs/using-daypicker/localization.mdx b/website/docs/using-daypicker/localization.mdx
index 60eb0bfa3..be9697c3f 100644
--- a/website/docs/using-daypicker/localization.mdx
+++ b/website/docs/using-daypicker/localization.mdx
@@ -75,6 +75,57 @@ Use `ISOWeek` to switch using [ISO Week Dates](https://en.wikipedia.org/wiki/ISO
   <Examples.WeeknumberIso />
 </BrowserWindow>
 
+## UTC Dates
+
+DayPicker uses the local time zone by default. To switch to UTC dates, add [@date-fns/utc](https://www.npmjs.com/package/@date-fns/utc) to your dependencies and import `DayPicker` from `react-day-picker/utc` instead.
+
+:::note Experimental feature
+
+The UTC mode is an experimental feature. [Please report](https://github.com/gpbl/react-day-picker/issues) any issues you may encounter, thanks!
+
+:::
+
+#### 1. Install the `@date-fns/utc` package
+
+```bash npm2yarn
+npm install @date-fns/utc
+```
+
+#### 2. Import `DayPicker` from `react-day-picker/utc`
+
+```diff
+- import { DayPicker } from 'react-day-picker';
++ import { DayPicker } from 'react-day-picker/utc';
+```
+
+#### 3. Use DayPicker as usual
+
+```tsx
+import { UTCDate } from "@date-fns/utc";
+import { DayPicker } from "react-day-picker/utc";
+
+export function Utc() {
+  const [selected, setSelected] = React.useState<Date>(new UTCDate());
+  return (
+    <DayPicker
+      mode="single"
+      required
+      selected={selected}
+      onSelect={(date: Date) => {
+        setSelected(date);
+      }}
+      footer={selected ? `Selected: ${selected.toUTCString()}` : null}
+    />
+  );
+}
+```
+
+Observe how dates are always GMT.
+
+<BrowserWindow>
+  <Examples.Utc />
+</BrowserWindow>
+
 ## Translate DayPicker
 
 These props will assist in translating the labels and formatters utilized in DayPicker.
@@ -185,3 +236,45 @@ export function NumberingSystemExample() {
 <BrowserWindow>
   <Examples.NumberingSystem />
 </BrowserWindow>
+
+## Jalali Calendar
+
+DayPicker supports the Jalali calendar thanks to the [date-fns-jalali](https://www.npmjs.com/package/date-fns-jalali) package. To switch to the Jalali calendar, add `date-fns-jalali` to your dependencies and import `DayPicker` from `react-day-picker/jalali` instead.
+
+:::note Experimental feature
+
+The support for the Jalali calendar is an experimental feature. [Please report](https://github.com/gpbl/react-day-picker/issues) any issues you may encounter, thanks!
+
+:::
+
+#### 1. Install the `date-fns-jalali` package
+
+```bash npm2yarn
+npm install date-fns-jalali
+```
+
+#### 2. Import `DayPicker` from `react-day-picker/jalali`
+
+```diff
+- import { DayPicker } from 'react-day-picker';
++ import { DayPicker } from 'react-day-picker/jalali';
+```
+
+#### 3. Use DayPicker as usual
+
+Note that you can set the right-to-left direction and change the locale.
+
+```tsx title="./JalaliCalendar.jsx"
+import React from "react";
+
+import { faIR } from "date-fns/locale";
+import { DayPicker } from "react-day-picker/jalali";
+
+export function Jalali() {
+  return <DayPicker mode="single" locale={faIR} dir="rtl" />;
+}
+```
+
+<BrowserWindow>
+  <Examples.Jalali />
+</BrowserWindow>
diff --git a/website/package.json b/website/package.json
index 0c18cc1c7..2d4490c09 100644
--- a/website/package.json
+++ b/website/package.json
@@ -16,11 +16,13 @@
     "typecheck-watch": "tsc --noEmit --watch"
   },
   "dependencies": {
+    "@date-fns/utc": "^1.2.0",
     "@docusaurus/core": "3.4.0",
     "@docusaurus/preset-classic": "3.4.0",
     "@docusaurus/remark-plugin-npm2yarn": "^3.4.0",
     "@mdx-js/react": "^3.0.1",
     "clsx": "^2.1.1",
+    "date-fns-jalali": "3.6.0-0",
     "prism-react-renderer": "^2.3.1",
     "react": "^18.3.1",
     "react-day-picker": "workspace:^",


Test patch:
diff --git a/examples/Controlled.test.tsx b/examples/Controlled.test.tsx
index a482c35a9..bc8fe630e 100644
--- a/examples/Controlled.test.tsx
+++ b/examples/Controlled.test.tsx
@@ -6,7 +6,13 @@ import { user } from "@/test/user";
 
 import { Controlled } from "./Controlled";
 
-jest.useFakeTimers().setSystemTime(new Date(2022, 5, 10));
+beforeAll(() => {
+  jest.setSystemTime(new Date(2022, 5, 10));
+});
+
+afterAll(() => {
+  jest.useRealTimers();
+});
 
 describe('when the "Today" button is clicked', () => {
   const todayButton = () => screen.getByRole("button", { name: "Go to Today" });
diff --git a/examples/CustomDayDate.test.tsx b/examples/CustomDayDate.test.tsx
index bbef5376d..a286c95ac 100644
--- a/examples/CustomDayDate.test.tsx
+++ b/examples/CustomDayDate.test.tsx
@@ -4,7 +4,13 @@ import { render, screen } from "@/test/render";
 
 import { CustomDayDate } from "./CustomDayDate";
 
-jest.useFakeTimers().setSystemTime(new Date(2021, 10, 25));
+beforeAll(() => {
+  jest.setSystemTime(new Date(2021, 10, 25));
+});
+
+afterAll(() => {
+  jest.useRealTimers();
+});
 
 beforeEach(() => {
   render(<CustomDayDate />);
diff --git a/examples/CustomMultiple.test.tsx b/examples/CustomMultiple.test.tsx
index a1061ddd7..6b56154a2 100644
--- a/examples/CustomMultiple.test.tsx
+++ b/examples/CustomMultiple.test.tsx
@@ -7,7 +7,14 @@ import { user } from "@/test/user";
 import { CustomMultiple } from "./CustomMultiple";
 
 const today = new Date(2021, 10, 25);
-jest.useFakeTimers().setSystemTime(today);
+
+beforeAll(() => {
+  jest.setSystemTime(today);
+});
+
+afterAll(() => {
+  jest.useRealTimers();
+});
 
 beforeEach(() => {
   render(<CustomMultiple />);
diff --git a/examples/CustomSingle.test.tsx b/examples/CustomSingle.test.tsx
index b292d60ee..900b31861 100644
--- a/examples/CustomSingle.test.tsx
+++ b/examples/CustomSingle.test.tsx
@@ -7,7 +7,14 @@ import { user } from "@/test/user";
 import { CustomSingle } from "./CustomSingle";
 
 const today = new Date(2021, 10, 25);
-jest.useFakeTimers().setSystemTime(today);
+
+beforeAll(() => {
+  jest.setSystemTime(today);
+});
+
+afterAll(() => {
+  jest.useRealTimers();
+});
 
 beforeEach(() => {
   render(<CustomSingle />);
diff --git a/examples/CustomWeek.test.tsx b/examples/CustomWeek.test.tsx
index 9607aea0b..03402c1be 100644
--- a/examples/CustomWeek.test.tsx
+++ b/examples/CustomWeek.test.tsx
@@ -7,7 +7,14 @@ import { user } from "@/test/user";
 import { CustomWeek } from "./CustomWeek";
 
 const today = new Date(2021, 10, 25);
-jest.useFakeTimers().setSystemTime(today);
+
+beforeAll(() => {
+  jest.setSystemTime(today);
+});
+
+afterAll(() => {
+  jest.useRealTimers();
+});
 
 beforeEach(() => {
   render(<CustomWeek />);
diff --git a/examples/DefaultMonth.test.tsx b/examples/DefaultMonth.test.tsx
index f73afc158..30446d764 100644
--- a/examples/DefaultMonth.test.tsx
+++ b/examples/DefaultMonth.test.tsx
@@ -6,7 +6,14 @@ import { render } from "@/test/render";
 import { DefaultMonth } from "./DefaultMonth";
 
 const today = new Date(2022, 5, 10);
-jest.useFakeTimers().setSystemTime(today);
+
+beforeAll(() => {
+  jest.setSystemTime(today);
+});
+
+afterAll(() => {
+  jest.useRealTimers();
+});
 
 test("should display September 1979", () => {
   render(<DefaultMonth />);
diff --git a/examples/Disabled.test.tsx b/examples/Disabled.test.tsx
index cd3e24ee1..e7bae464c 100644
--- a/examples/Disabled.test.tsx
+++ b/examples/Disabled.test.tsx
@@ -8,7 +8,14 @@ import { Disabled } from "./Disabled";
 
 const today = new Date(2022, 5, 10);
 const firstOfMonth = new Date(2022, 5, 1);
-jest.useFakeTimers().setSystemTime(today);
+
+beforeAll(() => {
+  jest.setSystemTime(today);
+});
+
+afterAll(() => {
+  jest.useRealTimers();
+});
 
 beforeEach(async () => {
   render(
diff --git a/examples/Dropdown.test.tsx b/examples/Dropdown.test.tsx
index 846c1245d..05c3e4618 100644
--- a/examples/Dropdown.test.tsx
+++ b/examples/Dropdown.test.tsx
@@ -7,7 +7,14 @@ import { user } from "@/test/user";
 import { Dropdown } from "./Dropdown";
 
 const today = new Date(2015, 6, 1);
-jest.useFakeTimers().setSystemTime(today);
+
+beforeAll(() => {
+  jest.setSystemTime(today);
+});
+
+afterAll(() => {
+  jest.useRealTimers();
+});
 
 beforeEach(() => {
   render(<Dropdown />);
diff --git a/examples/DropdownMultipleMonths.test.tsx b/examples/DropdownMultipleMonths.test.tsx
index 88866d069..ce38c3d1d 100644
--- a/examples/DropdownMultipleMonths.test.tsx
+++ b/examples/DropdownMultipleMonths.test.tsx
@@ -7,7 +7,14 @@ import { user } from "@/test/user";
 import { DropdownMultipleMonths } from "./DropdownMultipleMonths";
 
 const today = new Date(2023, 9, 16);
-jest.useFakeTimers().setSystemTime(today);
+
+beforeAll(() => {
+  jest.setSystemTime(today);
+});
+
+afterAll(() => {
+  jest.useRealTimers();
+});
 
 beforeEach(() => {
   render(<DropdownMultipleMonths />);
diff --git a/examples/FixedWeeks.test.tsx b/examples/FixedWeeks.test.tsx
index db01901da..7e2195940 100644
--- a/examples/FixedWeeks.test.tsx
+++ b/examples/FixedWeeks.test.tsx
@@ -5,7 +5,14 @@ import { render, screen } from "@/test/render";
 import { FixedWeeks } from "./Fixedweeks";
 
 const today = new Date(2021, 10, 25);
-jest.useFakeTimers().setSystemTime(today);
+
+beforeAll(() => {
+  jest.setSystemTime(today);
+});
+
+afterAll(() => {
+  jest.useRealTimers();
+});
 
 beforeEach(() => {
   render(<FixedWeeks />);
diff --git a/examples/FocusRecursive.test.tsx b/examples/FocusRecursive.test.tsx
index 3a844369e..fa1c40a8f 100644
--- a/examples/FocusRecursive.test.tsx
+++ b/examples/FocusRecursive.test.tsx
@@ -8,7 +8,13 @@ import { FocusRecursive } from "./FocusRecursive";
 
 const today = new Date(2022, 5, 10);
 
-jest.useFakeTimers().setSystemTime(today);
+beforeAll(() => {
+  jest.setSystemTime(today);
+});
+
+afterAll(() => {
+  jest.useRealTimers();
+});
 
 test("the first selected day should have focus", async () => {
   render(<FocusRecursive />).container;
diff --git a/examples/Formatters.test.tsx b/examples/Formatters.test.tsx
index d92b78377..df5ee22e5 100644
--- a/examples/Formatters.test.tsx
+++ b/examples/Formatters.test.tsx
@@ -5,7 +5,14 @@ import { render, screen } from "@/test/render";
 import { Formatters } from "./Formatters";
 
 const today = new Date(2021, 10, 25);
-jest.useFakeTimers().setSystemTime(today);
+
+beforeAll(() => {
+  jest.setSystemTime(today);
+});
+
+afterAll(() => {
+  jest.useRealTimers();
+});
 
 beforeEach(() => {
   render(<Formatters />);
diff --git a/examples/FromToYear.test.tsx b/examples/FromToYear.test.tsx
index 0e65b4d61..f7c773ca2 100644
--- a/examples/FromToYear.test.tsx
+++ b/examples/FromToYear.test.tsx
@@ -11,7 +11,14 @@ import { FromToYear } from "./FromToYear";
 const fromMonth = new Date(2024, 0);
 const toMonth = new Date(2026, 11);
 const today = new Date(2025, 10, 25);
-jest.useFakeTimers().setSystemTime(today);
+
+beforeAll(() => {
+  jest.setSystemTime(today);
+});
+
+afterAll(() => {
+  jest.useRealTimers();
+});
 
 beforeEach(() => {
   render(<FromToYear />);
diff --git a/examples/Jalali.test.tsx b/examples/Jalali.test.tsx
new file mode 100644
index 000000000..640d2e705
--- /dev/null
+++ b/examples/Jalali.test.tsx
@@ -0,0 +1,27 @@
+import React from "react";
+
+import { render, screen } from "@/test/render";
+
+import { Jalali } from "./Jalali";
+
+const today = new Date(2021, 10, 25);
+
+beforeAll(() => {
+  jest.setSystemTime(today);
+});
+
+afterAll(() => {
+  jest.useRealTimers();
+});
+
+beforeEach(() => {
+  render(
+    <div role="application">
+      <Jalali />
+    </div>
+  );
+});
+
+test("should match the snapshot", () => {
+  expect(screen.getByRole("application")).toMatchSnapshot();
+});
diff --git a/examples/Keyboard.test.tsx b/examples/Keyboard.test.tsx
index 7a00d3dfb..4b7c8efbf 100644
--- a/examples/Keyboard.test.tsx
+++ b/examples/Keyboard.test.tsx
@@ -24,7 +24,14 @@ import { user } from "@/test/user";
 import { Keyboard } from "./Keyboard";
 
 const today = new Date(2022, 5, 10);
-jest.useFakeTimers().setSystemTime(today);
+
+beforeAll(() => {
+  jest.setSystemTime(today);
+});
+
+afterAll(() => {
+  jest.useRealTimers();
+});
 
 describe.each(["ltr", "rtl"])("when text direction is %s", (dir: string) => {
   beforeEach(() => {
diff --git a/examples/ModifiersClassnames.test.tsx b/examples/ModifiersClassnames.test.tsx
index da9f58ebf..f8eb6cbec 100644
--- a/examples/ModifiersClassnames.test.tsx
+++ b/examples/ModifiersClassnames.test.tsx
@@ -12,7 +12,14 @@ const days = [
 ];
 
 const today = new Date(2021, 10, 25);
-jest.useFakeTimers().setSystemTime(today);
+
+beforeAll(() => {
+  jest.setSystemTime(today);
+});
+
+afterAll(() => {
+  jest.useRealTimers();
+});
 
 beforeEach(() => {
   render(<ModifiersClassnames />);
diff --git a/examples/ModifiersHidden.test.tsx b/examples/ModifiersHidden.test.tsx
index d205908c2..96b4eced7 100644
--- a/examples/ModifiersHidden.test.tsx
+++ b/examples/ModifiersHidden.test.tsx
@@ -11,7 +11,14 @@ const days = [
 ];
 
 const today = new Date(2021, 10, 25);
-jest.useFakeTimers().setSystemTime(today);
+
+beforeAll(() => {
+  jest.setSystemTime(today);
+});
+
+afterAll(() => {
+  jest.useRealTimers();
+});
 
 test.each(days)("the day %s should be hidden", (day) => {
   render(<ModifiersHidden />);
diff --git a/examples/ModifiersStyle.test.tsx b/examples/ModifiersStyle.test.tsx
index 96795873c..c28f22a2e 100644
--- a/examples/ModifiersStyle.test.tsx
+++ b/examples/ModifiersStyle.test.tsx
@@ -6,7 +6,14 @@ import { render } from "@/test/render";
 import { ModifiersStyle } from "./ModifiersStyle";
 
 const today = new Date(2021, 10, 25);
-jest.useFakeTimers().setSystemTime(today);
+
+beforeAll(() => {
+  jest.setSystemTime(today);
+});
+
+afterAll(() => {
+  jest.useRealTimers();
+});
 
 beforeEach(() => {
   render(<ModifiersStyle />);
diff --git a/examples/ModifiersToday.test.tsx b/examples/ModifiersToday.test.tsx
index d73df6827..33c4db163 100644
--- a/examples/ModifiersToday.test.tsx
+++ b/examples/ModifiersToday.test.tsx
@@ -9,7 +9,14 @@ import { user } from "@/test/user";
 import { ModifiersToday } from "./ModifiersToday";
 
 const today = new Date(2022, 5, 10);
-jest.useFakeTimers().setSystemTime(today);
+
+beforeAll(() => {
+  jest.setSystemTime(today);
+});
+
+afterAll(() => {
+  jest.useRealTimers();
+});
 
 beforeEach(() => {
   renderApp(<ModifiersToday />);
diff --git a/examples/Multiple.test.tsx b/examples/Multiple.test.tsx
index 2390b46a2..be3c0121b 100644
--- a/examples/Multiple.test.tsx
+++ b/examples/Multiple.test.tsx
@@ -7,7 +7,14 @@ import { user } from "@/test/user";
 import { Multiple } from "./Multiple";
 
 const today = new Date(2021, 10, 25);
-jest.useFakeTimers().setSystemTime(today);
+
+beforeAll(() => {
+  jest.setSystemTime(today);
+});
+
+afterAll(() => {
+  jest.useRealTimers();
+});
 
 beforeEach(() => {
   render(<Multiple />);
diff --git a/examples/MultipleMinMax.test.tsx b/examples/MultipleMinMax.test.tsx
index bbfc051cf..8e840ec5f 100644
--- a/examples/MultipleMinMax.test.tsx
+++ b/examples/MultipleMinMax.test.tsx
@@ -9,7 +9,14 @@ import { user } from "@/test/user";
 import { MultipleMinMax } from "./MultipleMinMax";
 
 const today = new Date(2021, 10, 10);
-jest.useFakeTimers().setSystemTime(today);
+
+beforeAll(() => {
+  jest.setSystemTime(today);
+});
+
+afterAll(() => {
+  jest.useRealTimers();
+});
 
 const days = [
   today,
diff --git a/examples/MultipleMonths.test.tsx b/examples/MultipleMonths.test.tsx
index 468c8fd46..c59e055fe 100644
--- a/examples/MultipleMonths.test.tsx
+++ b/examples/MultipleMonths.test.tsx
@@ -7,7 +7,14 @@ import { user } from "@/test/user";
 import { MultipleMonths } from "./MultipleMonths";
 
 const today = new Date(2023, 11, 3);
-jest.useFakeTimers().setSystemTime(today);
+
+beforeAll(() => {
+  jest.setSystemTime(today);
+});
+
+afterAll(() => {
+  jest.useRealTimers();
+});
 
 beforeEach(() => {
   render(<MultipleMonths />);
diff --git a/examples/MultipleMonthsId.test.tsx b/examples/MultipleMonthsId.test.tsx
index 5ee826e78..8eb55d499 100644
--- a/examples/MultipleMonthsId.test.tsx
+++ b/examples/MultipleMonthsId.test.tsx
@@ -6,7 +6,14 @@ import { render } from "@/test/render";
 import { MultipleMonthsId } from "./MultipleMonthsId";
 
 const today = new Date(2021, 10, 25);
-jest.useFakeTimers().setSystemTime(today);
+
+beforeAll(() => {
+  jest.setSystemTime(today);
+});
+
+afterAll(() => {
+  jest.useRealTimers();
+});
 
 test("the table ids should include the display index", () => {
   render(<MultipleMonthsId />);
diff --git a/examples/MultipleMonthsPaged.test.tsx b/examples/MultipleMonthsPaged.test.tsx
index eae35c21f..f35fa30c9 100644
--- a/examples/MultipleMonthsPaged.test.tsx
+++ b/examples/MultipleMonthsPaged.test.tsx
@@ -7,7 +7,14 @@ import { user } from "@/test/user";
 import { MultipleMonthsPaged } from "./MultipleMonthsPaged";
 
 const today = new Date(2021, 10, 25);
-jest.useFakeTimers().setSystemTime(today);
+
+beforeAll(() => {
+  jest.setSystemTime(today);
+});
+
+afterAll(() => {
+  jest.useRealTimers();
+});
 
 beforeEach(() => {
   render(<MultipleMonthsPaged />);
diff --git a/examples/NumberingSystem.test.tsx b/examples/NumberingSystem.test.tsx
index e1911a364..1d7b7ac69 100644
--- a/examples/NumberingSystem.test.tsx
+++ b/examples/NumberingSystem.test.tsx
@@ -6,7 +6,14 @@ import { render, screen } from "@/test/render";
 import { NumberingSystem } from "./NumberingSystem";
 
 const today = new Date(2021, 10, 25);
-jest.useFakeTimers().setSystemTime(today);
+
+beforeAll(() => {
+  jest.setSystemTime(today);
+});
+
+afterAll(() => {
+  jest.useRealTimers();
+});
 
 beforeEach(() => {
   render(<NumberingSystem />);
diff --git a/examples/OutsideDays.test.tsx b/examples/OutsideDays.test.tsx
index 3450e0b98..6a9d48342 100644
--- a/examples/OutsideDays.test.tsx
+++ b/examples/OutsideDays.test.tsx
@@ -5,7 +5,14 @@ import { render, screen } from "@/test/render";
 import { OutsideDays } from "./OutsideDays";
 
 const today = new Date(2021, 10, 25);
-jest.useFakeTimers().setSystemTime(today);
+
+beforeAll(() => {
+  jest.setSystemTime(today);
+});
+
+afterAll(() => {
+  jest.useRealTimers();
+});
 
 beforeEach(() => {
   render(<OutsideDays />);
diff --git a/examples/RangeShiftKey.test.tsx b/examples/RangeShiftKey.test.tsx
index a93337d40..0a63a24bb 100644
--- a/examples/RangeShiftKey.test.tsx
+++ b/examples/RangeShiftKey.test.tsx
@@ -7,7 +7,14 @@ import { user } from "@/test/user";
 import { RangeShiftKey } from "./RangeShiftKey";
 
 const today = new Date(2021, 10, 25);
-jest.useFakeTimers().setSystemTime(today);
+
+beforeAll(() => {
+  jest.setSystemTime(today);
+});
+
+afterAll(() => {
+  jest.useRealTimers();
+});
 
 beforeEach(() => render(<RangeShiftKey />).container);
 
diff --git a/examples/Rtl.test.tsx b/examples/Rtl.test.tsx
index 62a29b1b1..e78fcd334 100644
--- a/examples/Rtl.test.tsx
+++ b/examples/Rtl.test.tsx
@@ -1,5 +1,7 @@
 import React from "react";
 
+import MockDate from "mockdate";
+
 import { app, grid, nextButton, previousButton } from "@/test/elements";
 import { renderApp } from "@/test/renderApp";
 import { user } from "@/test/user";
@@ -7,7 +9,14 @@ import { user } from "@/test/user";
 import { Rtl } from "./Rtl";
 
 const today = new Date(2021, 10, 25);
-jest.useFakeTimers().setSystemTime(today);
+
+beforeAll(() => {
+  jest.setSystemTime(today);
+});
+
+afterAll(() => {
+  jest.useRealTimers();
+});
 
 beforeEach(() => {
   renderApp(<Rtl />);
diff --git a/examples/Single.test.tsx b/examples/Single.test.tsx
index bcf8b16d2..5883672fd 100644
--- a/examples/Single.test.tsx
+++ b/examples/Single.test.tsx
@@ -8,7 +8,14 @@ import { user } from "@/test/user";
 import { Single } from "./Single";
 
 const today = new Date(2021, 10, 25);
-jest.useFakeTimers().setSystemTime(today);
+
+beforeAll(() => {
+  jest.setSystemTime(today);
+});
+
+afterAll(() => {
+  jest.useRealTimers();
+});
 
 beforeEach(() => {
   render(<Single />);
diff --git a/examples/Spanish.test.tsx b/examples/Spanish.test.tsx
index 1f05d40cb..7075fdbc6 100644
--- a/examples/Spanish.test.tsx
+++ b/examples/Spanish.test.tsx
@@ -6,7 +6,14 @@ import { render } from "@/test/render";
 import { Spanish } from "./Spanish";
 
 const today = new Date(2021, 10, 25);
-jest.useFakeTimers().setSystemTime(today);
+
+beforeAll(() => {
+  jest.setSystemTime(today);
+});
+
+afterAll(() => {
+  jest.useRealTimers();
+});
 
 beforeEach(() => {
   render(<Spanish />);
diff --git a/examples/Start.test.tsx b/examples/Start.test.tsx
index 5889a68cb..9707a6982 100644
--- a/examples/Start.test.tsx
+++ b/examples/Start.test.tsx
@@ -7,7 +7,14 @@ import { user } from "@/test/user";
 import { Start } from "./Start";
 
 const today = new Date(2021, 10, 25);
-jest.useFakeTimers().setSystemTime(today);
+
+beforeAll(() => {
+  jest.setSystemTime(today);
+});
+
+afterAll(() => {
+  jest.useRealTimers();
+});
 
 beforeEach(async () => {
   render(<Start />);
diff --git a/examples/StylingCss.test.tsx b/examples/StylingCss.test.tsx
index 6da389f38..3803d3e23 100644
--- a/examples/StylingCss.test.tsx
+++ b/examples/StylingCss.test.tsx
@@ -6,7 +6,14 @@ import { renderApp } from "@/test/renderApp";
 import { StylingCss } from "./StylingCss";
 
 const today = new Date(2021, 10, 25);
-jest.useFakeTimers().setSystemTime(today);
+
+beforeAll(() => {
+  jest.setSystemTime(today);
+});
+
+afterAll(() => {
+  jest.useRealTimers();
+});
 
 beforeEach(() => {
   renderApp(<StylingCss />);
diff --git a/examples/StylingCssModules.test.tsx b/examples/StylingCssModules.test.tsx
index f4fe47e94..fd1fc648b 100644
--- a/examples/StylingCssModules.test.tsx
+++ b/examples/StylingCssModules.test.tsx
@@ -6,7 +6,14 @@ import { renderApp } from "@/test/renderApp";
 import { CssModules } from "./CssModules";
 
 const today = new Date(2021, 10, 25);
-jest.useFakeTimers().setSystemTime(today);
+
+beforeAll(() => {
+  jest.setSystemTime(today);
+});
+
+afterAll(() => {
+  jest.useRealTimers();
+});
 
 beforeEach(() => {
   renderApp(<CssModules />);
diff --git a/examples/StylingInline.test.tsx b/examples/StylingInline.test.tsx
index e5b5063f8..62fa9854e 100644
--- a/examples/StylingInline.test.tsx
+++ b/examples/StylingInline.test.tsx
@@ -5,7 +5,14 @@ import { render, screen } from "@/test/render";
 import { StylingInline } from "./StylingInline";
 
 const today = new Date(2021, 10, 25);
-jest.useFakeTimers().setSystemTime(today);
+
+beforeAll(() => {
+  jest.setSystemTime(today);
+});
+
+afterAll(() => {
+  jest.useRealTimers();
+});
 
 beforeEach(() => {
   render(<StylingInline />);
diff --git a/examples/Weeknumber.test.tsx b/examples/Weeknumber.test.tsx
index a28f43cfc..c38c23eb5 100644
--- a/examples/Weeknumber.test.tsx
+++ b/examples/Weeknumber.test.tsx
@@ -7,7 +7,14 @@ import { user } from "@/test/user";
 import { Weeknumber } from "./Weeknumber";
 
 const today = new Date(2021, 10, 25);
-jest.useFakeTimers().setSystemTime(today);
+
+beforeAll(() => {
+  jest.setSystemTime(today);
+});
+
+afterAll(() => {
+  jest.useRealTimers();
+});
 
 function getWeekButton(week: number) {
   return screen.getByRole("rowheader", {
diff --git a/examples/__snapshots__/Jalali.test.tsx.snap b/examples/__snapshots__/Jalali.test.tsx.snap
new file mode 100644
index 000000000..c3a6a7f1a
--- /dev/null
+++ b/examples/__snapshots__/Jalali.test.tsx.snap
@@ -0,0 +1,641 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should match the snapshot 1`] = `
+<div
+  role="application"
+>
+  <div
+    class="rdp-calendar"
+    dir="rtl"
+    id=":r1:"
+  >
+    <div
+      class="rdp-months"
+    >
+      <div
+        class="rdp-nav"
+        role="toolbar"
+      >
+        <button
+          aria-controls=":r1:"
+          aria-label="Previous Month"
+          class="rdp-button_previous"
+          name="previous-month"
+          type="button"
+        >
+          <svg
+            class="rdp-chevron"
+            height="24"
+            viewBox="0 0 24 24"
+            width="24"
+          >
+            <polygon
+              points="15 17.23 9.43 11.5 15 5.76 13.28 4 6 11.5 13.28 19"
+            />
+          </svg>
+        </button>
+        <button
+          aria-controls=":r1:"
+          aria-label="Next Month"
+          class="rdp-button_next"
+          name="next-month"
+          type="button"
+        >
+          <svg
+            class="rdp-chevron"
+            height="24"
+            viewBox="0 0 24 24"
+            width="24"
+          >
+            <polygon
+              points="8 18.23 13.57 12.5 8 6.76 9.72 5 17 12.5 9.72 20"
+            />
+          </svg>
+        </button>
+      </div>
+      <div
+        class="rdp-month_wrapper"
+      >
+        <div
+          class="rdp-month_caption"
+          id=":r1:-caption-0"
+        >
+          <span
+            aria-label=""
+            aria-live="polite"
+            class="rdp-caption_label"
+            role="status"
+          >
+            نوامبر 2021
+          </span>
+        </div>
+        <div
+          aria-labelledby=":r1:-caption-0"
+          aria-multiselectable="false"
+          class="rdp-month"
+          id=":r1:-grid-0"
+          role="grid"
+        >
+          <div
+            aria-rowindex="1"
+            class="rdp-weekdays"
+            role="row"
+          >
+            <span
+              aria-colindex="1"
+              aria-label="شنبه"
+              class="rdp-weekday"
+              role="columnheader"
+            >
+              ش
+            </span>
+            <span
+              aria-colindex="2"
+              aria-label="یکشنبه"
+              class="rdp-weekday"
+              role="columnheader"
+            >
+              1ش
+            </span>
+            <span
+              aria-colindex="3"
+              aria-label="دوشنبه"
+              class="rdp-weekday"
+              role="columnheader"
+            >
+              2ش
+            </span>
+            <span
+              aria-colindex="4"
+              aria-label="سه‌شنبه"
+              class="rdp-weekday"
+              role="columnheader"
+            >
+              3ش
+            </span>
+            <span
+              aria-colindex="5"
+              aria-label="چهارشنبه"
+              class="rdp-weekday"
+              role="columnheader"
+            >
+              4ش
+            </span>
+            <span
+              aria-colindex="6"
+              aria-label="پنجشنبه"
+              class="rdp-weekday"
+              role="columnheader"
+            >
+              5ش
+            </span>
+            <span
+              aria-colindex="7"
+              aria-label="جمعه"
+              class="rdp-weekday"
+              role="columnheader"
+            >
+              ج
+            </span>
+          </div>
+          <div
+            class="rdp-weeks"
+            role="rowgroup"
+          >
+            <div
+              aria-rowindex="2"
+              class="rdp-week"
+              role="row"
+            >
+              <div
+                aria-colindex="1"
+                aria-hidden="true"
+                aria-label=""
+                class="rdp-day rdp-hidden rdp-outside"
+                role="gridcell"
+                tabindex="-1"
+              >
+                <span
+                  class="rdp-day_date"
+                >
+                  30
+                </span>
+              </div>
+              <div
+                aria-colindex="2"
+                aria-hidden="true"
+                aria-label=""
+                class="rdp-day rdp-hidden rdp-outside"
+                role="gridcell"
+                tabindex="-1"
+              >
+                <span
+                  class="rdp-day_date"
+                >
+                  31
+                </span>
+              </div>
+              <div
+                aria-colindex="3"
+                aria-label=""
+                class="rdp-day rdp-focusable"
+                role="gridcell"
+                tabindex="-1"
+              >
+                <span
+                  class="rdp-day_date"
+                >
+                  1
+                </span>
+              </div>
+              <div
+                aria-colindex="4"
+                aria-label=""
+                class="rdp-day rdp-focusable"
+                role="gridcell"
+                tabindex="-1"
+              >
+                <span
+                  class="rdp-day_date"
+                >
+                  2
+                </span>
+              </div>
+              <div
+                aria-colindex="5"
+                aria-label=""
+                class="rdp-day rdp-focusable"
+                role="gridcell"
+                tabindex="-1"
+              >
+                <span
+                  class="rdp-day_date"
+                >
+                  3
+                </span>
+              </div>
+              <div
+                aria-colindex="6"
+                aria-label=""
+                class="rdp-day rdp-focusable"
+                role="gridcell"
+                tabindex="-1"
+              >
+                <span
+                  class="rdp-day_date"
+                >
+                  4
+                </span>
+              </div>
+              <div
+                aria-colindex="7"
+                aria-label=""
+                class="rdp-day rdp-focusable"
+                role="gridcell"
+                tabindex="-1"
+              >
+                <span
+                  class="rdp-day_date"
+                >
+                  5
+                </span>
+              </div>
+            </div>
+            <div
+              aria-rowindex="3"
+              class="rdp-week"
+              role="row"
+            >
+              <div
+                aria-colindex="1"
+                aria-label=""
+                class="rdp-day rdp-focusable"
+                role="gridcell"
+                tabindex="-1"
+              >
+                <span
+                  class="rdp-day_date"
+                >
+                  6
+                </span>
+              </div>
+              <div
+                aria-colindex="2"
+                aria-label=""
+                class="rdp-day rdp-focusable"
+                role="gridcell"
+                tabindex="-1"
+              >
+                <span
+                  class="rdp-day_date"
+                >
+                  7
+                </span>
+              </div>
+              <div
+                aria-colindex="3"
+                aria-label=""
+                class="rdp-day rdp-focusable"
+                role="gridcell"
+                tabindex="-1"
+              >
+                <span
+                  class="rdp-day_date"
+                >
+                  8
+                </span>
+              </div>
+              <div
+                aria-colindex="4"
+                aria-label=""
+                class="rdp-day rdp-focusable"
+                role="gridcell"
+                tabindex="-1"
+              >
+                <span
+                  class="rdp-day_date"
+                >
+                  9
+                </span>
+              </div>
+              <div
+                aria-colindex="5"
+                aria-label=""
+                class="rdp-day rdp-focusable"
+                role="gridcell"
+                tabindex="-1"
+              >
+                <span
+                  class="rdp-day_date"
+                >
+                  10
+                </span>
+              </div>
+              <div
+                aria-colindex="6"
+                aria-label=""
+                class="rdp-day rdp-focusable"
+                role="gridcell"
+                tabindex="-1"
+              >
+                <span
+                  class="rdp-day_date"
+                >
+                  11
+                </span>
+              </div>
+              <div
+                aria-colindex="7"
+                aria-label=""
+                class="rdp-day rdp-focusable"
+                role="gridcell"
+                tabindex="-1"
+              >
+                <span
+                  class="rdp-day_date"
+                >
+                  12
+                </span>
+              </div>
+            </div>
+            <div
+              aria-rowindex="4"
+              class="rdp-week"
+              role="row"
+            >
+              <div
+                aria-colindex="1"
+                aria-label=""
+                class="rdp-day rdp-focusable"
+                role="gridcell"
+                tabindex="-1"
+              >
+                <span
+                  class="rdp-day_date"
+                >
+                  13
+                </span>
+              </div>
+              <div
+                aria-colindex="2"
+                aria-label=""
+                class="rdp-day rdp-focusable"
+                role="gridcell"
+                tabindex="-1"
+              >
+                <span
+                  class="rdp-day_date"
+                >
+                  14
+                </span>
+              </div>
+              <div
+                aria-colindex="3"
+                aria-label=""
+                class="rdp-day rdp-focusable"
+                role="gridcell"
+                tabindex="-1"
+              >
+                <span
+                  class="rdp-day_date"
+                >
+                  15
+                </span>
+              </div>
+              <div
+                aria-colindex="4"
+                aria-label=""
+                class="rdp-day rdp-focusable"
+                role="gridcell"
+                tabindex="-1"
+              >
+                <span
+                  class="rdp-day_date"
+                >
+                  16
+                </span>
+              </div>
+              <div
+                aria-colindex="5"
+                aria-label=""
+                class="rdp-day rdp-focusable"
+                role="gridcell"
+                tabindex="-1"
+              >
+                <span
+                  class="rdp-day_date"
+                >
+                  17
+                </span>
+              </div>
+              <div
+                aria-colindex="6"
+                aria-label=""
+                class="rdp-day rdp-focusable"
+                role="gridcell"
+                tabindex="-1"
+              >
+                <span
+                  class="rdp-day_date"
+                >
+                  18
+                </span>
+              </div>
+              <div
+                aria-colindex="7"
+                aria-label=""
+                class="rdp-day rdp-focusable"
+                role="gridcell"
+                tabindex="-1"
+              >
+                <span
+                  class="rdp-day_date"
+                >
+                  19
+                </span>
+              </div>
+            </div>
+            <div
+              aria-rowindex="5"
+              class="rdp-week"
+              role="row"
+            >
+              <div
+                aria-colindex="1"
+                aria-label=""
+                class="rdp-day rdp-focusable"
+                role="gridcell"
+                tabindex="-1"
+              >
+                <span
+                  class="rdp-day_date"
+                >
+                  20
+                </span>
+              </div>
+              <div
+                aria-colindex="2"
+                aria-label=""
+                class="rdp-day rdp-focusable"
+                role="gridcell"
+                tabindex="-1"
+              >
+                <span
+                  class="rdp-day_date"
+                >
+                  21
+                </span>
+              </div>
+              <div
+                aria-colindex="3"
+                aria-label=""
+                class="rdp-day rdp-focusable"
+                role="gridcell"
+                tabindex="-1"
+              >
+                <span
+                  class="rdp-day_date"
+                >
+                  22
+                </span>
+              </div>
+              <div
+                aria-colindex="4"
+                aria-label=""
+                class="rdp-day rdp-focusable"
+                role="gridcell"
+                tabindex="-1"
+              >
+                <span
+                  class="rdp-day_date"
+                >
+                  23
+                </span>
+              </div>
+              <div
+                aria-colindex="5"
+                aria-label=""
+                class="rdp-day rdp-focusable"
+                role="gridcell"
+                tabindex="-1"
+              >
+                <span
+                  class="rdp-day_date"
+                >
+                  24
+                </span>
+              </div>
+              <div
+                aria-colindex="6"
+                aria-label=""
+                class="rdp-day rdp-focusable rdp-today"
+                role="gridcell"
+                tabindex="0"
+              >
+                <span
+                  class="rdp-day_date"
+                >
+                  25
+                </span>
+              </div>
+              <div
+                aria-colindex="7"
+                aria-label=""
+                class="rdp-day rdp-focusable"
+                role="gridcell"
+                tabindex="-1"
+              >
+                <span
+                  class="rdp-day_date"
+                >
+                  26
+                </span>
+              </div>
+            </div>
+            <div
+              aria-rowindex="6"
+              class="rdp-week"
+              role="row"
+            >
+              <div
+                aria-colindex="1"
+                aria-label=""
+                class="rdp-day rdp-focusable"
+                role="gridcell"
+                tabindex="-1"
+              >
+                <span
+                  class="rdp-day_date"
+                >
+                  27
+                </span>
+              </div>
+              <div
+                aria-colindex="2"
+                aria-label=""
+                class="rdp-day rdp-focusable"
+                role="gridcell"
+                tabindex="-1"
+              >
+                <span
+                  class="rdp-day_date"
+                >
+                  28
+                </span>
+              </div>
+              <div
+                aria-colindex="3"
+                aria-label=""
+                class="rdp-day rdp-focusable"
+                role="gridcell"
+                tabindex="-1"
+              >
+                <span
+                  class="rdp-day_date"
+                >
+                  29
+                </span>
+              </div>
+              <div
+                aria-colindex="4"
+                aria-label=""
+                class="rdp-day rdp-focusable"
+                role="gridcell"
+                tabindex="-1"
+              >
+                <span
+                  class="rdp-day_date"
+                >
+                  30
+                </span>
+              </div>
+              <div
+                aria-colindex="5"
+                aria-hidden="true"
+                aria-label=""
+                class="rdp-day rdp-hidden rdp-outside"
+                role="gridcell"
+                tabindex="-1"
+              >
+                <span
+                  class="rdp-day_date"
+                >
+                  1
+                </span>
+              </div>
+              <div
+                aria-colindex="6"
+                aria-hidden="true"
+                aria-label=""
+                class="rdp-day rdp-hidden rdp-outside"
+                role="gridcell"
+                tabindex="-1"
+              >
+                <span
+                  class="rdp-day_date"
+                >
+                  2
+                </span>
+              </div>
+              <div
+                aria-colindex="7"
+                aria-hidden="true"
+                aria-label=""
+                class="rdp-day rdp-hidden rdp-outside"
+                role="gridcell"
+                tabindex="-1"
+              >
+                <span
+                  class="rdp-day_date"
+                >
+                  3
+                </span>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</div>
+`;
diff --git a/src/formatters/formatCaption.test.ts b/src/formatters/formatCaption.test.ts
index 6df133a27..2606958ac 100644
--- a/src/formatters/formatCaption.test.ts
+++ b/src/formatters/formatCaption.test.ts
@@ -1,15 +1,19 @@
 import { es } from "date-fns/locale/es";
 
+import { dateLib } from "../lib";
+
 import { formatCaption } from "./formatCaption";
 
 const date = new Date(2022, 10, 21);
 
 test("should return the formatted caption", () => {
-  expect(formatCaption(date)).toEqual("November 2022");
+  expect(formatCaption(date, {}, dateLib)).toEqual("November 2022");
 });
 
 describe("when a locale is passed in", () => {
   test("should format using the locale", () => {
-    expect(formatCaption(date, { locale: es })).toEqual("noviembre 2022");
+    expect(formatCaption(date, { locale: es }, dateLib)).toEqual(
+      "noviembre 2022"
+    );
   });
 });
diff --git a/src/formatters/formatDay.test.ts b/src/formatters/formatDay.test.ts
index 07288958e..a3d6c4021 100644
--- a/src/formatters/formatDay.test.ts
+++ b/src/formatters/formatDay.test.ts
@@ -1,3 +1,5 @@
+import { dateLib } from "../lib";
+
 import { formatDay } from "./formatDay";
 
 const date = new Date(2022, 10, 21);
diff --git a/src/formatters/formatWeekdayName.test.ts b/src/formatters/formatWeekdayName.test.ts
index c625b043a..e5dc5a8ef 100644
--- a/src/formatters/formatWeekdayName.test.ts
+++ b/src/formatters/formatWeekdayName.test.ts
@@ -1,5 +1,7 @@
 import { es } from "date-fns/locale/es";
 
+import { dateLib } from "../lib";
+
 import { formatWeekdayName } from "./formatWeekdayName";
 
 const date = new Date(2022, 10, 21);
diff --git a/src/helpers/getDates.test.ts b/src/helpers/getDates.test.ts
index 3817aefb7..f4773814f 100644
--- a/src/helpers/getDates.test.ts
+++ b/src/helpers/getDates.test.ts
@@ -1,3 +1,5 @@
+import { dateLib } from "../lib";
+
 import { getDates } from "./getDates";
 
 describe("when the first month and the last month are the same", () => {
@@ -6,7 +8,8 @@ describe("when the first month and the last month are the same", () => {
     describe("when not using fixed weeks", () => {
       it("should return 42 dates", () => {
         const dates = getDates([month], undefined, {
-          fixedWeeks: false
+          fixedWeeks: false,
+          dateLib
         });
         expect(dates).toHaveLength(42);
         expect(dates[0]).toEqual(new Date(2023, 10, 26));
@@ -16,7 +19,8 @@ describe("when the first month and the last month are the same", () => {
     describe("when using fixed weeks", () => {
       it("should return 42 dates", () => {
         const dates = getDates([month], undefined, {
-          fixedWeeks: true
+          fixedWeeks: true,
+          dateLib
         });
         expect(dates).toHaveLength(42);
         expect(dates[0]).toEqual(new Date(2023, 10, 26));
@@ -29,7 +33,8 @@ describe("when the first month and the last month are the same", () => {
     describe("when not using fixed weeks", () => {
       it("should return 35 dates", () => {
         const dates = getDates([month], undefined, {
-          fixedWeeks: false
+          fixedWeeks: false,
+          dateLib
         });
         expect(dates).toHaveLength(35);
         expect(dates[0]).toEqual(new Date(2023, 3, 30));
@@ -39,7 +44,8 @@ describe("when the first month and the last month are the same", () => {
     describe("when using fixed weeks", () => {
       it("should return 42 dates", () => {
         const dates = getDates([month], undefined, {
-          fixedWeeks: true
+          fixedWeeks: true,
+          dateLib
         });
         expect(dates).toHaveLength(42);
         expect(dates[0]).toEqual(new Date(2023, 3, 30));
@@ -50,7 +56,10 @@ describe("when the first month and the last month are the same", () => {
   describe("when using Monday as first day of the week", () => {
     const month = new Date(2023, 4, 1);
     it("the first day should be Monday", () => {
-      const dates = getDates([month], undefined, { weekStartsOn: 1 });
+      const dates = getDates([month], undefined, {
+        weekStartsOn: 1,
+        dateLib
+      });
       expect(dates[0]).toBeMonday();
       expect(dates[0]).toEqual(new Date(2023, 4, 1));
       expect(dates[dates.length - 1]).toEqual(new Date(2023, 5, 4));
@@ -61,7 +70,7 @@ describe("when the first month and the last month are the same", () => {
     const maxDate = new Date(2023, 4, 15);
 
     it("the last day should be the max date", () => {
-      const dates = getDates([month], maxDate, { weekStartsOn: 1 });
+      const dates = getDates([month], maxDate, { weekStartsOn: 1, dateLib });
       expect(dates).toHaveLength(15);
       expect(dates[dates.length - 1]).toEqual(maxDate);
     });
@@ -69,7 +78,7 @@ describe("when the first month and the last month are the same", () => {
   describe("when using ISO weeks", () => {
     const month = new Date(2023, 4, 1);
     it("the first day should be Monday", () => {
-      const dates = getDates([month], undefined, { ISOWeek: true });
+      const dates = getDates([month], undefined, { ISOWeek: true, dateLib });
       expect(dates[0]).toBeMonday();
       expect(dates[0]).toEqual(new Date(2023, 4, 1));
       expect(dates[dates.length - 1]).toEqual(new Date(2023, 5, 4));
@@ -83,7 +92,8 @@ describe("when the first month and the last month are different", () => {
   describe("when not using fixed weeks", () => {
     it("should return an array of dates", () => {
       const dates = getDates([firstMonth, lastMonth], undefined, {
-        fixedWeeks: false
+        fixedWeeks: false,
+        dateLib
       });
       expect(dates).toHaveLength(252);
       expect(dates[0]).toEqual(new Date(2023, 3, 30));
@@ -97,7 +107,8 @@ describe("when the first month and the last month are different", () => {
 
     it("the last day should be the max date", () => {
       const dates = getDates([firstMonth, lastMonth], maxDate, {
-        weekStartsOn: 1
+        weekStartsOn: 1,
+        dateLib
       });
       expect(dates).toHaveLength(46);
       expect(dates[dates.length - 1]).toEqual(maxDate);
@@ -106,7 +117,7 @@ describe("when the first month and the last month are different", () => {
   describe("when using ISO weeks", () => {
     const month = new Date(2023, 4, 1);
     it("the first day should be Monday", () => {
-      const dates = getDates([month], undefined, { ISOWeek: true });
+      const dates = getDates([month], undefined, { ISOWeek: true, dateLib });
       expect(dates[0]).toBeMonday();
       expect(dates[0]).toEqual(new Date(2023, 4, 1));
       expect(dates[dates.length - 1]).toEqual(new Date(2023, 5, 4));
diff --git a/src/helpers/getDisplayMonths.test.ts b/src/helpers/getDisplayMonths.test.ts
index 5372ea2d6..ff96e0cfc 100644
--- a/src/helpers/getDisplayMonths.test.ts
+++ b/src/helpers/getDisplayMonths.test.ts
@@ -1,3 +1,5 @@
+import { dateLib } from "react-day-picker";
+
 import { getDisplayMonths } from "./getDisplayMonths";
 
 describe("getDisplayMonths", () => {
@@ -6,7 +8,8 @@ describe("getDisplayMonths", () => {
     const expectedResult = [new Date(2020, 0)];
     const result = getDisplayMonths(firstMonth, {
       numberOfMonths: 1,
-      endMonth: undefined
+      endMonth: undefined,
+      dateLib
     });
     expect(result).toEqual(expectedResult);
   });
@@ -20,7 +23,8 @@ describe("getDisplayMonths", () => {
     ];
     const result = getDisplayMonths(firstMonth, {
       numberOfMonths: 3,
-      endMonth: undefined
+      endMonth: undefined,
+      dateLib
     });
     expect(result).toEqual(expectedResult);
   });
@@ -30,7 +34,8 @@ describe("getDisplayMonths", () => {
     const expectedResult = [new Date(2020, 0), new Date(2020, 1)];
     const result = getDisplayMonths(firstMonth, {
       numberOfMonths: 3,
-      endMonth: new Date(2020, 1, 10)
+      endMonth: new Date(2020, 1, 10),
+      dateLib
     });
     expect(result).toEqual(expectedResult);
   });
diff --git a/src/helpers/getDropdownMonths.test.ts b/src/helpers/getDropdownMonths.test.ts
index b670ddf0b..213645eb7 100644
--- a/src/helpers/getDropdownMonths.test.ts
+++ b/src/helpers/getDropdownMonths.test.ts
@@ -1,5 +1,6 @@
 import { type Locale, format } from "date-fns";
 import { enUS as locale } from "date-fns/locale";
+import { dateLib } from "react-day-picker";
 
 import { getDropdownMonths } from "./getDropdownMonths";
 import { getFormatters } from "./getFormatters";
@@ -16,7 +17,8 @@ test("return correct dropdown options", () => {
     formatters,
     locale,
     startMonth,
-    endMonth
+    endMonth,
+    dateLib
   });
 
   expect(result).toEqual([
diff --git a/src/helpers/getDropdownYears.test.ts b/src/helpers/getDropdownYears.test.ts
index 0c863e877..939e09d7e 100644
--- a/src/helpers/getDropdownYears.test.ts
+++ b/src/helpers/getDropdownYears.test.ts
@@ -1,4 +1,5 @@
 import { enUS as locale } from "date-fns/locale";
+import { dateLib } from "react-day-picker";
 
 import { getDropdownYears } from "./getDropdownYears";
 import { getFormatters } from "./getFormatters";
@@ -12,13 +13,15 @@ test("return undefined if startMonth or endMonth is not provided", () => {
     formatters,
     locale,
     startMonth: undefined,
-    endMonth: new Date(2022, 11, 31)
+    endMonth: new Date(2022, 11, 31),
+    dateLib
   });
   const result2 = getDropdownYears(displayMonth, {
     formatters,
     locale,
     startMonth: new Date(2022, 0, 1),
-    endMonth: undefined
+    endMonth: undefined,
+    dateLib
   });
 
   expect(result1).toBeUndefined();
@@ -37,7 +40,8 @@ test("return correct dropdown options", () => {
     formatters,
     locale,
     startMonth,
-    endMonth
+    endMonth,
+    dateLib
   });
 
   expect(result).toEqual([
diff --git a/src/helpers/getFormatters.test.ts b/src/helpers/getFormatters.test.ts
index ceafc7e42..7fb7baf8c 100644
--- a/src/helpers/getFormatters.test.ts
+++ b/src/helpers/getFormatters.test.ts
@@ -1,4 +1,5 @@
 import * as defaultFormatters from "../formatters";
+import { dateLib } from "../lib";
 
 import { getFormatters } from "./getFormatters";
 
@@ -19,8 +20,10 @@ test("merges custom formatters with default formatters", () => {
 
 test("assigns `formatMonthCaption` to `formatCaption` if `formatCaption` is not defined", () => {
   const result = getFormatters({ formatMonthCaption: () => "customMonth" });
-  expect(result.formatCaption(new Date())).toBe("customMonth");
-  expect(result.formatMonthCaption(new Date())).toBe("customMonth");
+  expect(result.formatCaption(new Date(), {}, dateLib)).toBe("customMonth");
+  expect(result.formatMonthCaption(new Date(), {}, dateLib)).toBe(
+    "customMonth"
+  );
 });
 
 test("does not overwrite `formatCaption` if already defined", () => {
@@ -28,8 +31,10 @@ test("does not overwrite `formatCaption` if already defined", () => {
     formatMonthCaption: () => "customMonth",
     formatCaption: () => "customCaption"
   });
-  expect(result.formatCaption(new Date())).toBe("customCaption");
-  expect(result.formatMonthCaption(new Date())).toBe("customMonth");
+  expect(result.formatCaption(new Date(), {}, dateLib)).toBe("customCaption");
+  expect(result.formatMonthCaption(new Date(), {}, dateLib)).toBe(
+    "customMonth"
+  );
 });
 
 test("assigns `formatYearCaption` to `formatYearDropdown` if `formatYearDropdown` is not defined", () => {
diff --git a/src/helpers/getInitialMonth.test.ts b/src/helpers/getInitialMonth.test.ts
index 9f3a351da..02c6ea5e0 100644
--- a/src/helpers/getInitialMonth.test.ts
+++ b/src/helpers/getInitialMonth.test.ts
@@ -1,4 +1,5 @@
 import { addMonths, isSameMonth } from "date-fns";
+import { dateLib } from "react-day-picker";
 
 import { getInitialMonth } from "./getInitialMonth";
 
@@ -6,21 +7,41 @@ describe("when no endMonth is given", () => {
   describe("when month is in context", () => {
     const month = new Date(2010, 11, 12);
     it("return that month", () => {
-      const startMonth = getInitialMonth({ month });
+      const startMonth = getInitialMonth({
+        month,
+        dateLib,
+        startMonth: undefined,
+        endMonth: undefined,
+        today: new Date(),
+        numberOfMonths: 0
+      });
       expect(isSameMonth(startMonth, month)).toBe(true);
     });
   });
   describe("when defaultMonth is in context", () => {
     const defaultMonth = new Date(2010, 11, 12);
     it("return that month", () => {
-      const startMonth = getInitialMonth({ defaultMonth });
+      const startMonth = getInitialMonth({
+        defaultMonth,
+        startMonth: undefined,
+        endMonth: undefined,
+        today: new Date(),
+        numberOfMonths: 0,
+        dateLib
+      });
       expect(isSameMonth(startMonth, defaultMonth)).toBe(true);
     });
   });
   describe("when no month or defaultMonth are in context", () => {
     const today = new Date(2010, 11, 12);
     it("return the today month", () => {
-      const startMonth = getInitialMonth({ today });
+      const startMonth = getInitialMonth({
+        today,
+        startMonth: undefined,
+        endMonth: undefined,
+        numberOfMonths: 0,
+        dateLib
+      });
       expect(isSameMonth(startMonth, today)).toBe(true);
     });
   });
@@ -35,7 +56,10 @@ describe("when endMonth is given", () => {
         const startMonth = getInitialMonth({
           month,
           endMonth,
-          numberOfMonths
+          numberOfMonths,
+          startMonth: undefined,
+          today: new Date(),
+          dateLib
         });
         expect(isSameMonth(startMonth, endMonth)).toBe(true);
       });
@@ -46,7 +70,10 @@ describe("when endMonth is given", () => {
         const startMonth = getInitialMonth({
           month,
           endMonth,
-          numberOfMonths
+          numberOfMonths,
+          startMonth: undefined,
+          today: new Date(),
+          dateLib
         });
         const expectedMonth = addMonths(endMonth, -1 * (numberOfMonths - 1));
         expect(isSameMonth(startMonth, expectedMonth)).toBe(true);
diff --git a/src/helpers/getMonths.test.ts b/src/helpers/getMonths.test.ts
index 860cf561a..1d04ca826 100644
--- a/src/helpers/getMonths.test.ts
+++ b/src/helpers/getMonths.test.ts
@@ -1,5 +1,6 @@
 import { CalendarMonth } from "../classes";
 import type { PropsContextValue } from "../contexts/useProps";
+import { dateLib } from "../lib";
 
 import { getMonths } from "./getMonths";
 
@@ -21,13 +22,15 @@ const mockProps: Pick<
   | "weekStartsOn"
   | "reverseMonths"
   | "firstWeekContainsDate"
+  | "dateLib"
 > = {
   fixedWeeks: false,
   ISOWeek: false,
   locale: undefined,
   weekStartsOn: 0, // Sunday
   reverseMonths: false,
-  firstWeekContainsDate: 1
+  firstWeekContainsDate: 1,
+  dateLib
 };
 
 it("should return the correct months without ISO weeks and reverse months", () => {
diff --git a/src/helpers/getNextFocus.test.tsx b/src/helpers/getNextFocus.test.tsx
index 68ddb14dd..fe5f2b16c 100644
--- a/src/helpers/getNextFocus.test.tsx
+++ b/src/helpers/getNextFocus.test.tsx
@@ -1,25 +1,26 @@
-import React from "react";
-
 import { CalendarDay } from "../classes";
 import type { PropsContextValue } from "../contexts/useProps";
+import { dateLib } from "../lib";
 import type { MoveFocusBy, MoveFocusDir } from "../types";
 
 import { getNextFocus } from "./getNextFocus";
 
 const props: Pick<
   PropsContextValue,
-  "disabled" | "hidden" | "startMonth" | "endMonth"
+  "disabled" | "hidden" | "startMonth" | "endMonth" | "dateLib"
 > = {
   disabled: [],
   hidden: [],
   startMonth: undefined,
-  endMonth: undefined
+  endMonth: undefined,
+  dateLib
 };
 
 it("should return `undefined` if `attempt` exceeds 365", () => {
   const focusedDay = new CalendarDay(
     new Date(2020, 0, 1),
-    new Date(2020, 0, 1)
+    new Date(2020, 0, 1),
+    dateLib
   );
   const moveBy: MoveFocusBy = "day";
   const moveDir: MoveFocusDir = "after";
@@ -30,7 +31,8 @@ it("should return `undefined` if `attempt` exceeds 365", () => {
 it("should return the focus date if it is not disabled or hidden", () => {
   const focusedDay = new CalendarDay(
     new Date(2020, 0, 1),
-    new Date(2020, 0, 1)
+    new Date(2020, 0, 1),
+    dateLib
   );
   const expectedDate = new Date(2020, 0, 2);
   const result = getNextFocus("day", "after", focusedDay, props);
@@ -40,7 +42,8 @@ it("should return the focus date if it is not disabled or hidden", () => {
 it("should return the next focus date if it is disabled", () => {
   const focusedDay = new CalendarDay(
     new Date(2020, 0, 1),
-    new Date(2020, 0, 1)
+    new Date(2020, 0, 1),
+    dateLib
   );
   const disabledDate = new Date(2020, 0, 2);
   const expectedDate = new Date(2020, 0, 3);
@@ -54,7 +57,8 @@ it("should return the next focus date if it is disabled", () => {
 it("should return the next focus date if it is hidden", () => {
   const focusedDay = new CalendarDay(
     new Date(2020, 0, 1),
-    new Date(2020, 0, 1)
+    new Date(2020, 0, 1),
+    dateLib
   );
   const hiddenDate = new Date(2020, 0, 2);
   const expectedDate = new Date(2020, 0, 3);
diff --git a/src/helpers/getNextMonth.test.ts b/src/helpers/getNextMonth.test.ts
index ac4e8b3ce..2b7033347 100644
--- a/src/helpers/getNextMonth.test.ts
+++ b/src/helpers/getNextMonth.test.ts
@@ -1,4 +1,5 @@
 import { addMonths, isSameMonth } from "date-fns";
+import { dateLib } from "react-day-picker";
 
 import { getNextMonth } from "./getNextMonth";
 
@@ -12,6 +13,7 @@ describe("when number of months is 1", () => {
         numberOfMonths: 1,
         disableNavigation,
         endMonth: undefined,
+        dateLib,
         startMonth: undefined
       });
       expect(result).toBe(undefined);
@@ -23,7 +25,8 @@ describe("when number of months is 1", () => {
       const result = getNextMonth(startingMonth, {
         numberOfMonths: 1,
         endMonth,
-        startMonth: undefined
+        startMonth: undefined,
+        dateLib
       });
       const expectedNextMonth = addMonths(startingMonth, 1);
       expect(result && isSameMonth(result, expectedNextMonth)).toBeTruthy();
@@ -35,7 +38,8 @@ describe("when number of months is 1", () => {
       const result = getNextMonth(startingMonth, {
         numberOfMonths: 1,
         endMonth,
-        startMonth: undefined
+        startMonth: undefined,
+        dateLib
       });
       expect(result).toBe(undefined);
     });
@@ -50,7 +54,8 @@ describe("when displaying 3 months", () => {
         numberOfMonths,
         pagedNavigation,
         startMonth: undefined,
-        endMonth: undefined
+        endMonth: undefined,
+        dateLib
       });
       const expectedNextMonth = addMonths(startingMonth, 3);
       expect(result && isSameMonth(result, expectedNextMonth)).toBeTruthy();
@@ -61,7 +66,8 @@ describe("when displaying 3 months", () => {
           numberOfMonths,
           pagedNavigation,
           startMonth: undefined,
-          endMonth: addMonths(startingMonth, 1)
+          endMonth: addMonths(startingMonth, 1),
+          dateLib
         });
         expect(result).toBe(undefined);
       });
@@ -74,7 +80,8 @@ describe("when displaying 3 months", () => {
         numberOfMonths,
         pagedNavigation,
         endMonth: undefined,
-        startMonth: undefined
+        startMonth: undefined,
+        dateLib
       });
       const expectedNextMonth = addMonths(startingMonth, 1);
       expect(result && isSameMonth(result, expectedNextMonth)).toBeTruthy();
@@ -85,7 +92,8 @@ describe("when displaying 3 months", () => {
           numberOfMonths,
           pagedNavigation,
           startMonth: undefined,
-          endMonth: addMonths(startingMonth, 2)
+          endMonth: addMonths(startingMonth, 2),
+          dateLib
         });
         expect(result).toBe(undefined);
       });
diff --git a/src/helpers/getPossibleFocusDate.test.ts b/src/helpers/getPossibleFocusDate.test.ts
index e45988c5a..ce3e80322 100644
--- a/src/helpers/getPossibleFocusDate.test.ts
+++ b/src/helpers/getPossibleFocusDate.test.ts
@@ -11,6 +11,7 @@ import {
   endOfWeek
 } from "date-fns";
 
+import { dateLib } from "../";
 import type { PropsContextValue } from "../contexts/useProps";
 import type { MoveFocusBy, MoveFocusDir, Mode } from "../types";
 
@@ -19,13 +20,14 @@ import { getPossibleFocusDate } from "./getPossibleFocusDate";
 const baseDate = new Date(2023, 0, 1); // Jan 1, 2023
 const options: Pick<
   PropsContextValue,
-  "locale" | "ISOWeek" | "weekStartsOn" | "startMonth" | "endMonth"
+  "locale" | "ISOWeek" | "weekStartsOn" | "startMonth" | "endMonth" | "dateLib"
 > = {
   locale: undefined,
   ISOWeek: false,
   weekStartsOn: 0, // Sunday
   startMonth: new Date(2022, 0, 1), // Jan 1, 2022
-  endMonth: new Date(2024, 0, 1) // Jan 1, 2024
+  endMonth: new Date(2024, 0, 1), // Jan 1, 2024
+  dateLib
 };
 
 const testCases: {
diff --git a/src/helpers/getPreviousMonth.test.ts b/src/helpers/getPreviousMonth.test.ts
index ee00696b3..79e1e1b98 100644
--- a/src/helpers/getPreviousMonth.test.ts
+++ b/src/helpers/getPreviousMonth.test.ts
@@ -1,3 +1,5 @@
+import { dateLib } from "../";
+
 import { getPreviousMonth } from "./getPreviousMonth";
 
 it("should return undefined if navigation is disabled", () => {
@@ -6,7 +8,8 @@ it("should return undefined if navigation is disabled", () => {
     disableNavigation: true,
     pagedNavigation: false,
     numberOfMonths: 1,
-    startMonth: new Date(2022, 0, 1)
+    startMonth: new Date(2022, 0, 1),
+    dateLib
   };
 
   const result = getPreviousMonth(firstDisplayedMonth, props);
@@ -20,7 +23,8 @@ it("should return the previous month if startMonth is not provided", () => {
     disableNavigation: false,
     pagedNavigation: false,
     numberOfMonths: 1,
-    startMonth: undefined
+    startMonth: undefined,
+    dateLib
   };
 
   const result = getPreviousMonth(firstDisplayedMonth, props);
@@ -34,7 +38,8 @@ it("should return undefined if the previous month is before the startMonth", ()
     disableNavigation: false,
     pagedNavigation: false,
     numberOfMonths: 1,
-    startMonth: new Date(2022, 0, 1)
+    startMonth: new Date(2022, 0, 1),
+    dateLib: dateLib
   };
 
   const result = getPreviousMonth(firstDisplayedMonth, props);
@@ -48,7 +53,8 @@ it("should return the correct previous month when pagedNavigation is true", () =
     disableNavigation: false,
     pagedNavigation: true,
     numberOfMonths: 2,
-    startMonth: new Date(2022, 0, 1)
+    startMonth: new Date(2022, 0, 1),
+    dateLib
   };
 
   const result = getPreviousMonth(firstDisplayedMonth, props);
diff --git a/src/helpers/getWeekdays.test.ts b/src/helpers/getWeekdays.test.ts
index 3cb65a819..9a133cc7d 100644
--- a/src/helpers/getWeekdays.test.ts
+++ b/src/helpers/getWeekdays.test.ts
@@ -1,12 +1,8 @@
 import { es } from "date-fns/locale/es";
 
-import { getWeekdays } from "./getWeekdays";
-
-const today = new Date(2022, 1, 12);
-const prevSunday = new Date(2022, 1, 6);
-const prevMonday = new Date(2022, 1, 7);
+import { dateLib } from "../lib";
 
-jest.useFakeTimers().setSystemTime(today);
+import { getWeekdays } from "./getWeekdays";
 
 let result: Date[];
 
@@ -18,7 +14,7 @@ describe("when rendered without a locale", () => {
     expect(result).toHaveLength(7);
   });
   test("should return Sunday as first day", () => {
-    expect(result[0]).toEqual(prevSunday);
+    expect(result[0]).toBeSunday();
   });
 });
 
@@ -36,9 +32,9 @@ describe.each<0 | 1 | 2 | 3 | 4 | 5 | 6>([0, 1, 2, 3, 4, 5, 6])(
 
 describe("when using ISO week", () => {
   beforeEach(() => {
-    result = getWeekdays(es, 3, true);
+    result = getWeekdays(es, 3, true, dateLib);
   });
   test("should return Monday as first day", () => {
-    expect(result[0]).toEqual(prevMonday);
+    expect(result[0]).toBeMonday();
   });
 });
diff --git a/src/labels/labelWeekday.test.ts b/src/labels/labelWeekday.test.ts
index 716469ed3..8b6283e45 100644
--- a/src/labels/labelWeekday.test.ts
+++ b/src/labels/labelWeekday.test.ts
@@ -5,7 +5,7 @@ import { labelWeekday } from "./labelWeekday";
 const weekDay = new Date(2022, 10, 21);
 
 test("should return the formatted weekday name", () => {
-  expect(labelWeekday(weekDay, {})).toEqual("Monday");
+  expect(labelWeekday(weekDay)).toEqual("Monday");
 });
 
 describe("when a locale is passed in", () => {
diff --git a/src/utils/dateMatchModifiers.test.ts b/src/utils/dateMatchModifiers.test.ts
index 3c20eb45a..d9a784c63 100644
--- a/src/utils/dateMatchModifiers.test.ts
+++ b/src/utils/dateMatchModifiers.test.ts
@@ -1,5 +1,6 @@
 import { addDays, subDays } from "date-fns";
 
+import { dateLib } from "../lib";
 import {
   DateAfter,
   DateBefore,
@@ -14,14 +15,14 @@ const testDay = new Date();
 
 describe("when the matcher is a boolean", () => {
   const matcher = true;
-  const result = dateMatchModifiers(testDay, [matcher]);
+  const result = dateMatchModifiers(testDay, [matcher], dateLib);
   test("should return the boolean", () => {
     expect(result).toBe(matcher);
   });
 });
 describe("when matching the same day", () => {
   const matcher = testDay;
-  const result = dateMatchModifiers(testDay, [matcher]);
+  const result = dateMatchModifiers(testDay, [matcher], dateLib);
   test("should return true", () => {
     expect(result).toBe(true);
   });
@@ -29,7 +30,7 @@ describe("when matching the same day", () => {
 
 describe("when matching an array of dates including the day", () => {
   const matcher = [addDays(testDay, -1), testDay, addDays(testDay, 1)];
-  const result = dateMatchModifiers(testDay, [matcher]);
+  const result = dateMatchModifiers(testDay, [matcher], dateLib);
   test("should return true", () => {
     expect(result).toBe(true);
   });
@@ -40,7 +41,7 @@ describe("when matching date range", () => {
     from: testDay,
     to: addDays(testDay, 1)
   };
-  const result = dateMatchModifiers(testDay, [matcher]);
+  const result = dateMatchModifiers(testDay, [matcher], dateLib);
   test("should return true", () => {
     expect(result).toBe(true);
   });
@@ -50,7 +51,7 @@ describe("when matching the day of week", () => {
   const matcher: DayOfWeek = {
     dayOfWeek: [testDay.getDay()]
   };
-  const result = dateMatchModifiers(testDay, [matcher]);
+  const result = dateMatchModifiers(testDay, [matcher], dateLib);
   test("should return true", () => {
     expect(result).toBe(true);
   });
@@ -61,7 +62,7 @@ describe("when matching date interval (closed)", () => {
     before: addDays(testDay, 5),
     after: subDays(testDay, 3)
   };
-  const result = dateMatchModifiers(testDay, [matcher]);
+  const result = dateMatchModifiers(testDay, [matcher], dateLib);
   test("should return true for the included day", () => {
     expect(result).toBe(true);
   });
@@ -73,22 +74,22 @@ describe("when matching date interval (open)", () => {
     after: addDays(testDay, 5)
   };
   test("should return false", () => {
-    const result = dateMatchModifiers(testDay, [matcher]);
+    const result = dateMatchModifiers(testDay, [matcher], dateLib);
     expect(result).toBe(false);
   });
   test("should return true for the days before", () => {
-    const result = dateMatchModifiers(subDays(testDay, 8), [matcher]);
+    const result = dateMatchModifiers(subDays(testDay, 8), [matcher], dateLib);
     expect(result).toBe(true);
   });
   test("should return true for the days after", () => {
-    const result = dateMatchModifiers(addDays(testDay, 8), [matcher]);
+    const result = dateMatchModifiers(addDays(testDay, 8), [matcher], dateLib);
     expect(result).toBe(true);
   });
 });
 
 describe("when matching the date after", () => {
   const matcher: DateAfter = { after: addDays(testDay, -1) };
-  const result = dateMatchModifiers(testDay, [matcher]);
+  const result = dateMatchModifiers(testDay, [matcher], dateLib);
   test("should return true", () => {
     expect(result).toBe(true);
   });
@@ -96,7 +97,7 @@ describe("when matching the date after", () => {
 
 describe("when matching the date before", () => {
   const matcher: DateBefore = { before: addDays(testDay, +1) };
-  const result = dateMatchModifiers(testDay, [matcher]);
+  const result = dateMatchModifiers(testDay, [matcher], dateLib);
   test("should return true", () => {
     expect(result).toBe(true);
   });
@@ -104,7 +105,7 @@ describe("when matching the date before", () => {
 
 describe("when the matcher is a function", () => {
   const matcher = () => true;
-  const result = dateMatchModifiers(testDay, [matcher]);
+  const result = dateMatchModifiers(testDay, [matcher], dateLib);
   test("should return the result of the function", () => {
     expect(result).toBe(true);
   });
diff --git a/test/renderHook.tsx b/test/renderHook.tsx
index b3855cb16..9b401b847 100644
--- a/test/renderHook.tsx
+++ b/test/renderHook.tsx
@@ -1,8 +1,8 @@
 import React from "react";
 
 import { renderHook as testingLibraryRenderHook } from "@testing-library/react";
-import { DayPickerProps } from "react-day-picker";
 
+import type { DayPickerProps } from "../src";
 import { ContextProviders } from "../src/contexts/providers";
 
 /** Render a hook wrapped with the {@link ContextProviders} Provider. */