diff --git a/src/components/DateField/README.md b/src/components/DateField/README.md
index 76228b49..c47c3df4 100644
--- a/src/components/DateField/README.md
+++ b/src/components/DateField/README.md
@@ -347,44 +347,70 @@ LANDING_BLOCK-->
+### Custom Date Parser
+
+You can provide a custom parser function to handle pasted date strings through the `parseDateFromString` prop. This is useful when you need to support specific date formats or custom parsing logic that differs from the default behavior.
+
+
+
+```tsx
+import {dateTime} from '@gravity-ui/date-utils';
+
+const customParser = (dateStr: string, format: string, timeZone?: string) => {
+ // Custom parsing logic
+ // For example, handle DD/MM/YYYY format specifically
+ if (dateStr.match(/^\d{2}\/\d{2}\/\d{4}$/)) {
+ const [day, month, year] = dateStr.split('/');
+ return dateTime({input: `${year}-${month}-${day}`, timeZone});
+ }
+ // Fallback to default parsing
+ return dateTime({input: dateStr, format, timeZone});
+};
+
+;
+```
+
+
+
## Time zone
`timeZone` is the property to set the time zone of the value in the input. [Learn more about time zones](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List)
## Properties
-| Name | Description | Type | Default |
-| :---------------- | :------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------: | :-----------------------: |
-| aria-describedby | The control's `aria-describedby` attribute | `string` | |
-| aria-details | The control's `aria-details` attribute | `string` | |
-| aria-label | The control's `aria-label` attribute | `string` | |
-| aria-labelledby | The control's `aria-labelledby` attribute | `string` | |
-| autoFocus | The control's `autofocus` attribute | `boolean` | |
-| className | The control's wrapper class name | `string` | |
-| defaultValue | Sets the initial value for uncontrolled component. | `DateTime` | |
-| disabled | Indicates that the user cannot interact with the control | `boolean` | `false` |
-| errorMessage | Error text | `ReactNode` | |
-| format | Format of the date when rendered in the input. [Available formats](https://day.js.org/docs/en/display/format) | `string` | |
-| hasClear | Shows the icon for clearing control's value | `boolean` | `false` |
-| id | The control's `id` attribute | `string` | |
-| isDateUnavailable | Callback that is called for each date of the calendar. If it returns true, then the date is unavailable. | `((date: DateTime) => boolean)` | |
-| label | Help text rendered to the left of the input node | `string` | |
-| startContent | The user`s node rendered before label and input | `React.ReactNode` | |
-| maxValue | The maximum allowed date that a user may select. | `DateTime` | |
-| minValue | The minimum allowed date that a user may select. | `DateTime` | |
-| onBlur | Fires when the control lost focus. Provides focus event as a callback's argument | `((e: FocusEvent) => void)` | |
-| onFocus | Fires when the control gets focus. Provides focus event as a callback's argument | `((e: FocusEvent) => void)` | |
-| onKeyDown | Fires when a key is pressed. Provides keyboard event as a callback's argument | `((e: KeyboardEvent) => void)` | |
-| onKeyUp | Fires when a key is released. Provides keyboard event as a callback's argument | `((e: KeyboardEvent) => void)` | |
-| onUpdate | Fires when the value is changed by the user. Provides new value as an callback's argument | `((value: DateTime \| null) => void` | |
-| pin | Corner rounding | `string` | `'round-round'` |
-| placeholder | Text that appears in the control when it has no value set | `string` | |
-| placeholderValue | A placeholder date that controls the default values of each segment when the user first interacts with them. | `DateTime` | `today's date at midnigh` |
-| readOnly | Whether the component's value is immutable. | `boolean` | `false` |
-| endContent | User`s node rendered after the input node and clear button | `React.ReactNode` | |
-| size | The size of the control | `"s"` `"m"` `"l"` `"xl"` | `"m"` |
-| style | Sets inline style for the element. | `CSSProperties` | |
-| timeZone | Sets the time zone. [Learn more about time zones](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List) | `string` | |
-| validationState | Validation state | `"invalid"` | |
-| value | The value of the control | `DateTime` `null` | |
-| view | The view of the control | `"normal"` `"clear"` | `"normal"` |
+| Name | Description | Type | Default |
+| :------------------ | :------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------: | :-----------------------: |
+| aria-describedby | The control's `aria-describedby` attribute | `string` | |
+| aria-details | The control's `aria-details` attribute | `string` | |
+| aria-label | The control's `aria-label` attribute | `string` | |
+| aria-labelledby | The control's `aria-labelledby` attribute | `string` | |
+| autoFocus | The control's `autofocus` attribute | `boolean` | |
+| className | The control's wrapper class name | `string` | |
+| defaultValue | Sets the initial value for uncontrolled component. | `DateTime` | |
+| disabled | Indicates that the user cannot interact with the control | `boolean` | `false` |
+| errorMessage | Error text | `ReactNode` | |
+| format | Format of the date when rendered in the input. [Available formats](https://day.js.org/docs/en/display/format) | `string` | |
+| hasClear | Shows the icon for clearing control's value | `boolean` | `false` |
+| id | The control's `id` attribute | `string` | |
+| isDateUnavailable | Callback that is called for each date of the calendar. If it returns true, then the date is unavailable. | `((date: DateTime) => boolean)` | |
+| label | Help text rendered to the left of the input node | `string` | |
+| startContent | The user`s node rendered before label and input | `React.ReactNode` | |
+| maxValue | The maximum allowed date that a user may select. | `DateTime` | |
+| minValue | The minimum allowed date that a user may select. | `DateTime` | |
+| onBlur | Fires when the control lost focus. Provides focus event as a callback's argument | `((e: FocusEvent) => void)` | |
+| onFocus | Fires when the control gets focus. Provides focus event as a callback's argument | `((e: FocusEvent) => void)` | |
+| onKeyDown | Fires when a key is pressed. Provides keyboard event as a callback's argument | `((e: KeyboardEvent) => void)` | |
+| onKeyUp | Fires when a key is released. Provides keyboard event as a callback's argument | `((e: KeyboardEvent) => void)` | |
+| onUpdate | Fires when the value is changed by the user. Provides new value as an callback's argument | `((value: DateTime \| null) => void` | |
+| parseDateFromString | Custom parser function for parsing pasted date strings. If not provided, the default parser will be used. | `((dateStr: string, format: string, timeZone?: string) => DateTime)` | |
+| pin | Corner rounding | `string` | `'round-round'` |
+| placeholder | Text that appears in the control when it has no value set | `string` | |
+| placeholderValue | A placeholder date that controls the default values of each segment when the user first interacts with them. | `DateTime` | `today's date at midnigh` |
+| readOnly | Whether the component's value is immutable. | `boolean` | `false` |
+| endContent | User`s node rendered after the input node and clear button | `React.ReactNode` | |
+| size | The size of the control | `"s"` `"m"` `"l"` `"xl"` | `"m"` |
+| style | Sets inline style for the element. | `CSSProperties` | |
+| timeZone | Sets the time zone. [Learn more about time zones](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List) | `string` | |
+| validationState | Validation state | `"invalid"` | |
+| value | The value of the control | `DateTime` `null` | |
+| view | The view of the control | `"normal"` `"clear"` | `"normal"` |
diff --git a/src/components/DateField/__tests__/parseDateFromString.ts b/src/components/DateField/__tests__/parseDateFromString.ts
new file mode 100644
index 00000000..cb252e14
--- /dev/null
+++ b/src/components/DateField/__tests__/parseDateFromString.ts
@@ -0,0 +1,58 @@
+import {dateTime} from '@gravity-ui/date-utils';
+import {act, renderHook} from '@testing-library/react';
+
+import {useDateFieldState} from '../hooks/useDateFieldState';
+import {parseDateFromString} from '../utils';
+
+jest.mock('../utils', () => ({
+ ...jest.requireActual('../utils'),
+ parseDateFromString: jest.fn(),
+}));
+
+const mockedParseDateFromString = parseDateFromString as jest.MockedFunction<
+ typeof parseDateFromString
+>;
+
+describe('DateField: parseDateFromString', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ mockedParseDateFromString.mockImplementation((str, format, timeZone) => {
+ return dateTime({input: str, format, timeZone});
+ });
+ });
+
+ it('should call custom parseDateFromString when provided', () => {
+ const customParser = jest.fn().mockReturnValue(dateTime({input: '2024-01-15T00:00:00Z'}));
+
+ const {result} = renderHook(() =>
+ useDateFieldState({
+ format: 'DD.MM.YYYY',
+ parseDateFromString: customParser,
+ }),
+ );
+
+ act(() => {
+ result.current.setValueFromString('15.01.2024');
+ });
+
+ expect(customParser).toHaveBeenCalledWith('15.01.2024', 'DD.MM.YYYY', 'default');
+ expect(mockedParseDateFromString).not.toHaveBeenCalled();
+ });
+
+ it('should use default parseDateFromString when parseDateFromString is not provided', () => {
+ const validDate = dateTime({input: '2024-01-15T00:00:00Z'});
+ mockedParseDateFromString.mockReturnValue(validDate);
+
+ const {result} = renderHook(() => useDateFieldState({format: 'DD.MM.YYYY'}));
+
+ act(() => {
+ result.current.setValueFromString('15.01.2024');
+ });
+
+ expect(mockedParseDateFromString).toHaveBeenCalledWith(
+ '15.01.2024',
+ 'DD.MM.YYYY',
+ 'default',
+ );
+ });
+});
diff --git a/src/components/DateField/hooks/useDateFieldState.ts b/src/components/DateField/hooks/useDateFieldState.ts
index f47e3e6c..ea71d97d 100644
--- a/src/components/DateField/hooks/useDateFieldState.ts
+++ b/src/components/DateField/hooks/useDateFieldState.ts
@@ -194,7 +194,8 @@ export function useDateFieldState(props: DateFieldStateOptions): DateFieldState
}
function setValueFromString(str: string) {
- const date = parseDateFromString(str, format, timeZone);
+ const parseDate = props.parseDateFromString ?? parseDateFromString;
+ const date = parseDate(str, format, timeZone);
if (date.isValid()) {
handleUpdateDate(date);
return true;
diff --git a/src/components/DatePicker/README.md b/src/components/DatePicker/README.md
index 57ca10de..251049be 100644
--- a/src/components/DatePicker/README.md
+++ b/src/components/DatePicker/README.md
@@ -301,6 +301,31 @@ LANDING_BLOCK-->
+### Custom Date Parser
+
+You can provide a custom parser function to handle pasted date strings through the `parseDateFromString` prop. This is useful when you need to support specific date formats or custom parsing logic that differs from the default behavior.
+
+
+
+```tsx
+import {dateTime} from '@gravity-ui/date-utils';
+
+const customParser = (dateStr: string, format: string, timeZone?: string) => {
+ // Custom parsing logic
+ // For example, handle DD/MM/YYYY format specifically
+ if (dateStr.match(/^\d{2}\/\d{2}\/\d{4}$/)) {
+ const [day, month, year] = dateStr.split('/');
+ return dateTime({input: `${year}-${month}-${day}`, timeZone});
+ }
+ // Fallback to default parsing
+ return dateTime({input: dateStr, format, timeZone});
+};
+
+;
+```
+
+
+
## Time zone
`timeZone` is the property to set the time zone of the value in the input. [Learn more about time zones](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List)
@@ -321,36 +346,37 @@ LANDING_BLOCK-->
## Properties
-| Name | Description | Type | Default |
-| :----------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------: | :-----------------------: |
-| aria-describedby | The control's `aria-describedby`. Identifies the element (or elements) that describes the object. attribute | `string` | |
-| aria-details | The control's `aria-details`. Identifies the element (or elements) that provide a detailed, extended description for the object. attribute | `string` | |
-| aria-label | The control's `aria-label`. Defines a string value that labels the current element. attribute | `string` | |
-| aria-labelledby | The control's `aria-labelledby`. Identifies the element (or elements) that labels the current element. attribute | `string` | |
-| autoFocus | The control's `autofocus`. Whether the element should receive focus on render. attribute | `boolean` | |
-| className | The control's wrapper class name | `string` | |
-| [defaultValue](#datepicker) | Sets the initial value for uncontrolled component. | `DateTime` | |
-| [disabled](#disabled) | Indicates that the user cannot interact with the control | `boolean` | `false` |
-| [errorMessage](#error) | Error text | `ReactNode` | |
-| [format](#format) | Format of the date when rendered in the input. [Available formats](https://day.js.org/docs/en/display/format) | `string` | |
-| [hasClear](#clear-button) | Shows the icon for clearing control's value | `boolean` | `false` |
-| id | The control's `id` attribute | `string` | |
-| isDateUnavailable | Callback that is called for each date of the calendar. If it returns true, then the date is unavailable. | `((date: DateTime) => boolean)` | |
-| [label](#label) | Help text rendered to the left of the input node | `string` | |
-| [maxValue](#min-and-max-value) | The maximum allowed date that a user may select. | `DateTime` | |
-| [minValue](#min-and-max-value) | The minimum allowed date that a user may select. | `DateTime` | |
-| onBlur | Fires when the control lost focus. Provides focus event as a callback's argument | `((e: FocusEvent) => void)` | |
-| onFocus | Fires when the control gets focus. Provides focus event as a callback's argument | `((e: FocusEvent) => void)` | |
-| onKeyDown | Fires when a key is pressed. Provides keyboard event as a callback's argument | `((e: KeyboardEvent) => void)` | |
-| onKeyUp | Fires when a key is released. Provides keyboard event as a callback's argument | `((e: KeyboardEvent) => void)` | |
-| onUpdate | Fires when the value is changed by the user. Provides new value as an callback's argument | `((value: DateTime \| null) => void` | |
-| [pin](#pin) | Corner rounding | `TextInputPin` | `'round-round'` |
-| [placeholder](#placeholder) | Text that appears in the control when it has no value set | `string` | |
-| placeholderValue | A placeholder date that controls the default values of each segment when the user first interacts with them. | `DateTime` | `today's date at midnigh` |
-| [readOnly](#readonly) | Whether the component's value is immutable. | `boolean` | `false` |
-| [size](#size) | The size of the control | `"s"` `"m"` `"l"` `"xl"` | `"m"` |
-| style | Sets inline style for the element. | `CSSProperties` | |
-| [timeZone](#time-zone) | Sets the time zone. [Learn more about time zones](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List) | `string` | |
-| [validationState](#error) | Validation state | `"invalid"` | |
-| [value](#datepicker) | The value of the control | `DateTime` `null` | |
-| [view](#view) | The view of the control | `"normal"` `"clear"` | `"normal"` |
+| Name | Description | Type | Default |
+| :----------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------: | :-----------------------: |
+| aria-describedby | The control's `aria-describedby`. Identifies the element (or elements) that describes the object. attribute | `string` | |
+| aria-details | The control's `aria-details`. Identifies the element (or elements) that provide a detailed, extended description for the object. attribute | `string` | |
+| aria-label | The control's `aria-label`. Defines a string value that labels the current element. attribute | `string` | |
+| aria-labelledby | The control's `aria-labelledby`. Identifies the element (or elements) that labels the current element. attribute | `string` | |
+| autoFocus | The control's `autofocus`. Whether the element should receive focus on render. attribute | `boolean` | |
+| className | The control's wrapper class name | `string` | |
+| [defaultValue](#datepicker) | Sets the initial value for uncontrolled component. | `DateTime` | |
+| [disabled](#disabled) | Indicates that the user cannot interact with the control | `boolean` | `false` |
+| [errorMessage](#error) | Error text | `ReactNode` | |
+| [format](#format) | Format of the date when rendered in the input. [Available formats](https://day.js.org/docs/en/display/format) | `string` | |
+| [hasClear](#clear-button) | Shows the icon for clearing control's value | `boolean` | `false` |
+| id | The control's `id` attribute | `string` | |
+| isDateUnavailable | Callback that is called for each date of the calendar. If it returns true, then the date is unavailable. | `((date: DateTime) => boolean)` | |
+| [label](#label) | Help text rendered to the left of the input node | `string` | |
+| [maxValue](#min-and-max-value) | The maximum allowed date that a user may select. | `DateTime` | |
+| [minValue](#min-and-max-value) | The minimum allowed date that a user may select. | `DateTime` | |
+| onBlur | Fires when the control lost focus. Provides focus event as a callback's argument | `((e: FocusEvent) => void)` | |
+| onFocus | Fires when the control gets focus. Provides focus event as a callback's argument | `((e: FocusEvent) => void)` | |
+| onKeyDown | Fires when a key is pressed. Provides keyboard event as a callback's argument | `((e: KeyboardEvent) => void)` | |
+| onKeyUp | Fires when a key is released. Provides keyboard event as a callback's argument | `((e: KeyboardEvent) => void)` | |
+| onUpdate | Fires when the value is changed by the user. Provides new value as an callback's argument | `((value: DateTime \| null) => void` | |
+| parseDateFromString | Custom parser function for parsing pasted date strings. If not provided, the default parser will be used. | `((dateStr: string, format: string, timeZone?: string) => DateTime)` | |
+| [pin](#pin) | Corner rounding | `TextInputPin` | `'round-round'` |
+| [placeholder](#placeholder) | Text that appears in the control when it has no value set | `string` | |
+| placeholderValue | A placeholder date that controls the default values of each segment when the user first interacts with them. | `DateTime` | `today's date at midnigh` |
+| [readOnly](#readonly) | Whether the component's value is immutable. | `boolean` | `false` |
+| [size](#size) | The size of the control | `"s"` `"m"` `"l"` `"xl"` | `"m"` |
+| style | Sets inline style for the element. | `CSSProperties` | |
+| [timeZone](#time-zone) | Sets the time zone. [Learn more about time zones](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List) | `string` | |
+| [validationState](#error) | Validation state | `"invalid"` | |
+| [value](#datepicker) | The value of the control | `DateTime` `null` | |
+| [view](#view) | The view of the control | `"normal"` `"clear"` | `"normal"` |
diff --git a/src/components/RangeDateField/__tests__/parseDateFromString.ts b/src/components/RangeDateField/__tests__/parseDateFromString.ts
new file mode 100644
index 00000000..380ca868
--- /dev/null
+++ b/src/components/RangeDateField/__tests__/parseDateFromString.ts
@@ -0,0 +1,74 @@
+import {dateTime} from '@gravity-ui/date-utils';
+import {act, renderHook} from '@testing-library/react';
+
+import {parseDateFromString} from '../../DateField/utils';
+import {useRangeDateFieldState} from '../hooks/useRangeDateFieldState';
+
+jest.mock('../../DateField/utils', () => ({
+ ...jest.requireActual('../../DateField/utils'),
+ parseDateFromString: jest.fn(),
+}));
+
+const mockedParseDateFromString = parseDateFromString as jest.MockedFunction<
+ typeof parseDateFromString
+>;
+
+describe('RangeDateField: parseDateFromString', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ mockedParseDateFromString.mockImplementation((str, format, timeZone) => {
+ return dateTime({input: str, format, timeZone});
+ });
+ });
+
+ it('should call custom parseDateFromString when provided for range dates', () => {
+ const customParser = jest
+ .fn()
+ .mockReturnValueOnce(dateTime({input: '2024-01-15T00:00:00Z'}))
+ .mockReturnValueOnce(dateTime({input: '2024-01-20T00:00:00Z'}));
+
+ const {result} = renderHook(() =>
+ useRangeDateFieldState({
+ format: 'DD.MM.YYYY',
+ parseDateFromString: customParser,
+ }),
+ );
+
+ act(() => {
+ result.current.setValueFromString('15.01.2024 — 20.01.2024');
+ });
+
+ expect(customParser).toHaveBeenCalledTimes(2);
+ expect(customParser).toHaveBeenNthCalledWith(1, '15.01.2024', 'DD.MM.YYYY', 'default');
+ expect(customParser).toHaveBeenNthCalledWith(2, '20.01.2024', 'DD.MM.YYYY', 'default');
+ expect(mockedParseDateFromString).not.toHaveBeenCalled();
+ });
+
+ it('should use default parseDateFromString when parseDateFromString is not provided', () => {
+ const validStartDate = dateTime({input: '2024-01-15T00:00:00Z'});
+ const validEndDate = dateTime({input: '2024-01-20T00:00:00Z'});
+ mockedParseDateFromString
+ .mockReturnValueOnce(validStartDate)
+ .mockReturnValueOnce(validEndDate);
+
+ const {result} = renderHook(() => useRangeDateFieldState({format: 'DD.MM.YYYY'}));
+
+ act(() => {
+ result.current.setValueFromString('15.01.2024 — 20.01.2024');
+ });
+
+ expect(mockedParseDateFromString).toHaveBeenCalledTimes(2);
+ expect(mockedParseDateFromString).toHaveBeenNthCalledWith(
+ 1,
+ '15.01.2024',
+ 'DD.MM.YYYY',
+ 'default',
+ );
+ expect(mockedParseDateFromString).toHaveBeenNthCalledWith(
+ 2,
+ '20.01.2024',
+ 'DD.MM.YYYY',
+ 'default',
+ );
+ });
+});
diff --git a/src/components/RangeDateField/hooks/useRangeDateFieldState.ts b/src/components/RangeDateField/hooks/useRangeDateFieldState.ts
index 4bc88b92..494cb99e 100644
--- a/src/components/RangeDateField/hooks/useRangeDateFieldState.ts
+++ b/src/components/RangeDateField/hooks/useRangeDateFieldState.ts
@@ -224,8 +224,9 @@ export function useRangeDateFieldState(props: RangeDateFieldStateOptions): Range
function setValueFromString(str: string) {
const list = str.split(delimiter);
- const start = parseDateFromString(list?.[0], format, timeZone);
- const end = parseDateFromString(list?.[1], format, timeZone);
+ const parseDate = props.parseDateFromString ?? parseDateFromString;
+ const start = parseDate(list?.[0], format, timeZone);
+ const end = parseDate(list?.[1], format, timeZone);
const range = {start, end};
if (range.start.isValid() && range.end.isValid()) {
handleUpdateRange(range);
diff --git a/src/components/types/datePicker.ts b/src/components/types/datePicker.ts
index 57e6fd9d..eb1afed1 100644
--- a/src/components/types/datePicker.ts
+++ b/src/components/types/datePicker.ts
@@ -19,6 +19,8 @@ export interface DateFieldBase extends ValueBase, InputB
* @default The timezone of the `value` or `defaultValue` or `placeholderValue`, 'default' otherwise.
*/
timeZone?: string;
+ /** Custom parser function for parsing pasted date strings. If not provided, the default parser will be used. */
+ parseDateFromString?: (dateStr: string, format: string, timeZone?: string) => DateTime;
}
export interface PopupTriggerProps {