From e463fdde637d32ab34322c2af71fbba626b53f85 Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Thu, 24 Oct 2024 14:18:46 +0200 Subject: [PATCH 01/76] Include date and time format options in the wcpaySettings object --- includes/admin/class-wc-payments-admin.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/includes/admin/class-wc-payments-admin.php b/includes/admin/class-wc-payments-admin.php index 9c53cb48597..eb6a6e9fd7c 100644 --- a/includes/admin/class-wc-payments-admin.php +++ b/includes/admin/class-wc-payments-admin.php @@ -958,6 +958,8 @@ private function get_js_settings(): array { 'lifetimeTPV' => $this->account->get_lifetime_total_payment_volume(), 'defaultExpressCheckoutBorderRadius' => WC_Payments_Express_Checkout_Button_Handler::DEFAULT_BORDER_RADIUS_IN_PX, 'isWooPayGlobalThemeSupportEligible' => WC_Payments_Features::is_woopay_global_theme_support_eligible(), + 'dateFormat' => wc_date_format(), + 'timeFormat' => get_option( 'time_format' ), ]; return apply_filters( 'wcpay_js_settings', $this->wcpay_js_settings ); From 305a339ee47649968ccbf742b405e37d25b04d32 Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Thu, 24 Oct 2024 14:19:08 +0200 Subject: [PATCH 02/76] Add utility function to format time --- client/globals.d.ts | 2 ++ client/utils/date-time.ts | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 client/utils/date-time.ts diff --git a/client/globals.d.ts b/client/globals.d.ts index f00b521943a..9992f2faadb 100644 --- a/client/globals.d.ts +++ b/client/globals.d.ts @@ -135,6 +135,8 @@ declare global { isOverviewSurveySubmitted: boolean; lifetimeTPV: number; defaultExpressCheckoutBorderRadius: string; + dateFormat: string; + timeFormat: string; }; const wc: { diff --git a/client/utils/date-time.ts b/client/utils/date-time.ts new file mode 100644 index 00000000000..684b96f3cb3 --- /dev/null +++ b/client/utils/date-time.ts @@ -0,0 +1,38 @@ +/** + * External dependencies + */ +import { dateI18n } from '@wordpress/date'; + +type DateTimeFormat = string | null; + +interface FormatDateTimeOptions { + includeTime?: boolean; // Whether to include time in the formatted string (defaults to true) + useGmt?: boolean; // Whether to display the time in GMT/UTC (defaults to false) +} + +/** + * Formats a date and time string according to WordPress settings or a custom format. + * + * @param { string } dateTime - The date and time string from the database in UTC (e.g., '2024-10-23 15:28:26'). + * @param { DateTimeFormat } [ customFormat=null ] - Optional custom format for date and time. If not provided, uses WordPress settings. + * @param { FormatDateTimeOptions } [ options={ includeTime: true, useGmt: true } ] - Additional options to control time inclusion and whether to use GMT/UTC. + * @param { boolean } [ options.includeTime=true ] - Whether to include the time in the formatted string. Defaults to true. + * @param { boolean } [ options.useGmt=true ] - Whether to display the time in GMT/UTC. Defaults to true. + * @return { string } - The formatted date and time string. + */ +export function formatDateTime( + dateTime: string, + customFormat: DateTimeFormat = null, + options: FormatDateTimeOptions = { includeTime: true, useGmt: true } +): string { + const { includeTime = true, useGmt = true } = options; + + // Use the WordPress settings for date and time format if no custom format is provided + const dateFormat = customFormat || window.wcpaySettings.dateFormat; + const timeFormat = includeTime ? window.wcpaySettings.timeFormat : ''; + const format = `${ dateFormat }${ + includeTime ? ` / ${ timeFormat }` : '' + }`; + + return dateI18n( format, dateTime, useGmt ); +} From 16479fb3ae6e08a3b546dcd81c57a81fd040c6a4 Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Thu, 24 Oct 2024 14:19:40 +0200 Subject: [PATCH 03/76] Refactor transactions list --- client/transactions/list/deposit.tsx | 8 ++++---- client/transactions/list/index.tsx | 9 +++++---- client/transactions/list/test/deposit.tsx | 6 ++++++ client/transactions/list/test/index.tsx | 2 ++ 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/client/transactions/list/deposit.tsx b/client/transactions/list/deposit.tsx index b1889f3ad19..bf2e600522d 100644 --- a/client/transactions/list/deposit.tsx +++ b/client/transactions/list/deposit.tsx @@ -17,6 +17,7 @@ import InfoOutlineIcon from 'gridicons/dist/info-outline'; */ import { getAdminUrl } from 'utils'; import { ClickTooltip } from 'components/tooltip'; +import { formatDateTime } from 'wcpay/utils/date-time'; interface DepositProps { depositId?: string; @@ -31,12 +32,11 @@ const Deposit: React.FC< DepositProps > = ( { depositId, dateAvailable } ) => { id: depositId, } ); - const formattedDateAvailable = dateI18n( - 'M j, Y', + const formattedDateAvailable = formatDateTime( moment.utc( dateAvailable ).toISOString(), - true // TODO Change call to gmdateI18n and remove this deprecated param once WP 5.4 support ends. + null, + { includeTime: false, useGmt: true } ); - return { formattedDateAvailable }; } diff --git a/client/transactions/list/index.tsx b/client/transactions/list/index.tsx index 3893df6658a..16585a174aa 100644 --- a/client/transactions/list/index.tsx +++ b/client/transactions/list/index.tsx @@ -7,7 +7,6 @@ import React, { Fragment, useState } from 'react'; import { uniq } from 'lodash'; import { useDispatch } from '@wordpress/data'; import { useMemo } from '@wordpress/element'; -import { dateI18n } from '@wordpress/date'; import { __, _n, sprintf } from '@wordpress/i18n'; import moment from 'moment'; import { @@ -70,6 +69,7 @@ import p24BankList from '../../payment-details/payment-method/p24/bank-list'; import { HoverTooltip } from 'components/tooltip'; import { PAYMENT_METHOD_TITLES } from 'wcpay/constants/payment-method'; import { ReportingExportLanguageHook } from 'wcpay/settings/reporting-settings/interfaces'; +import { formatDateTime } from 'wcpay/utils/date-time'; interface TransactionsListProps { depositId?: string; @@ -466,9 +466,10 @@ export const TransactionsList = ( date: { value: txn.date, display: clickable( - dateI18n( - 'M j, Y / g:iA', - moment.utc( txn.date ).local().toISOString() + formatDateTime( + moment.utc( txn.date ).local().toISOString(), + null, + { useGmt: false } ) ), }, diff --git a/client/transactions/list/test/deposit.tsx b/client/transactions/list/test/deposit.tsx index 2cb87b0b248..283e101e3db 100644 --- a/client/transactions/list/test/deposit.tsx +++ b/client/transactions/list/test/deposit.tsx @@ -12,6 +12,12 @@ import { render } from '@testing-library/react'; import Deposit from '../deposit'; describe( 'Deposit', () => { + beforeEach( () => { + // Mock the window.wcpaySettings property + window.wcpaySettings.dateFormat = 'M j, Y'; + window.wcpaySettings.timeFormat = 'g:i a'; + } ); + test( 'renders with date and deposit available', () => { const { container: link } = render( diff --git a/client/transactions/list/test/index.tsx b/client/transactions/list/test/index.tsx index 8d97397ce01..0ac273c6751 100644 --- a/client/transactions/list/test/index.tsx +++ b/client/transactions/list/test/index.tsx @@ -244,6 +244,8 @@ describe( 'Transactions list', () => { exportModalDismissed: true, }, }; + window.wcpaySettings.dateFormat = 'M j, Y'; + window.wcpaySettings.timeFormat = 'g:iA'; } ); test( 'renders correctly when filtered by deposit', () => { From 92eb3b0be6f8c6b2a05fe7270e5399865eb5b17d Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Thu, 24 Oct 2024 15:21:15 +0200 Subject: [PATCH 04/76] Refactor deposits list --- client/deposits/list/index.tsx | 7 ++++--- client/deposits/list/test/index.tsx | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/client/deposits/list/index.tsx b/client/deposits/list/index.tsx index 21c48d2f631..30ce843f7aa 100644 --- a/client/deposits/list/index.tsx +++ b/client/deposits/list/index.tsx @@ -45,6 +45,7 @@ import { ReportingExportLanguageHook } from 'wcpay/settings/reporting-settings/i import './style.scss'; import { parseInt } from 'lodash'; +import { formatDateTime } from 'wcpay/utils/date-time'; const getColumns = ( sortByDate?: boolean ): DepositsTableHeader[] => [ { @@ -132,10 +133,10 @@ export const DepositsList = (): JSX.Element => { href={ getDetailsURL( deposit.id, 'deposits' ) } onClick={ () => recordEvent( 'wcpay_deposits_row_click' ) } > - { dateI18n( - 'M j, Y', + { formatDateTime( moment.utc( deposit.date ).toISOString(), - true // TODO Change call to gmdateI18n and remove this deprecated param once WP 5.4 support ends. + null, + { includeTime: false, useGmt: true } ) } ); diff --git a/client/deposits/list/test/index.tsx b/client/deposits/list/test/index.tsx index 7c6ea8f9d17..7054261cc36 100644 --- a/client/deposits/list/test/index.tsx +++ b/client/deposits/list/test/index.tsx @@ -76,6 +76,7 @@ declare const global: { reporting?: { exportModalDismissed: boolean; }; + dateFormat: string; }; }; @@ -141,6 +142,7 @@ describe( 'Deposits list', () => { reporting: { exportModalDismissed: true, }, + dateFormat: 'M j, Y', }; } ); From 65f05349d8bc89fba578f726fd1f74f946a149a6 Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Thu, 24 Oct 2024 15:28:34 +0200 Subject: [PATCH 05/76] Refactor deposits details --- client/deposits/details/index.tsx | 7 ++++--- client/deposits/details/test/index.tsx | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/client/deposits/details/index.tsx b/client/deposits/details/index.tsx index 4d47ae51d6a..fa241ee3b2e 100644 --- a/client/deposits/details/index.tsx +++ b/client/deposits/details/index.tsx @@ -37,6 +37,7 @@ import InlineNotice from 'components/inline-notice'; import { formatCurrency, formatExplicitCurrency } from 'utils/currency'; import { displayStatus } from '../strings'; import './style.scss'; +import { formatDateTime } from 'wcpay/utils/date-time'; /** * Renders the deposit status indicator UI, re-purposing the OrderStatus component from @woocommerce/components. @@ -108,10 +109,10 @@ export const DepositOverview: React.FC< DepositOverviewProps > = ( { key="depositDate" label={ `${ depositDateLabel }: ` + - dateI18n( - 'M j, Y', + formatDateTime( moment.utc( deposit.date ).toISOString(), - true // TODO Change call to gmdateI18n and remove this deprecated param once WP 5.4 support ends. + null, + { useGmt: true, includeTime: false } ) } value={ } diff --git a/client/deposits/details/test/index.tsx b/client/deposits/details/test/index.tsx index f6fd4d98116..385e896f173 100644 --- a/client/deposits/details/test/index.tsx +++ b/client/deposits/details/test/index.tsx @@ -32,6 +32,7 @@ declare const global: { connect: { country: string; }; + dateFormat: string; }; wcSettings: { countries: Record< string, string > }; }; @@ -54,6 +55,7 @@ describe( 'Deposit overview', () => { precision: 2, }, }, + dateFormat: 'M j, Y', }; } ); From 44786e35c20616517162bc2e4856848cb1548be8 Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Thu, 24 Oct 2024 16:02:07 +0200 Subject: [PATCH 06/76] Refactor deposits overview --- .../components/deposits-overview/test/index.tsx | 2 ++ client/deposits/utils/index.ts | 15 +++++++-------- client/deposits/utils/test/index.ts | 12 ++++++++++++ client/utils/date-time.ts | 6 ++---- 4 files changed, 23 insertions(+), 12 deletions(-) diff --git a/client/components/deposits-overview/test/index.tsx b/client/components/deposits-overview/test/index.tsx index 4853aff7bf5..509bf1d3d42 100644 --- a/client/components/deposits-overview/test/index.tsx +++ b/client/components/deposits-overview/test/index.tsx @@ -68,6 +68,7 @@ declare const global: { connect: { country: string; }; + dateFormat: string; }; }; @@ -231,6 +232,7 @@ describe( 'Deposits Overview information', () => { precision: 2, }, }, + dateFormat: 'F j, Y', }; mockUseDepositIncludesLoan.mockReturnValue( { includesFinancingPayout: false, diff --git a/client/deposits/utils/index.ts b/client/deposits/utils/index.ts index 3d8fd6276e1..54c70262c52 100644 --- a/client/deposits/utils/index.ts +++ b/client/deposits/utils/index.ts @@ -4,19 +4,18 @@ import { __ } from '@wordpress/i18n'; import { dateI18n } from '@wordpress/date'; import moment from 'moment'; - -const formatDate = ( format: string, date: number | string ) => - dateI18n( - format, - moment.utc( date ).toISOString(), - true // TODO Change call to gmdateI18n and remove this deprecated param once WP 5.4 support ends. - ); +import { formatDateTime } from 'wcpay/utils/date-time'; interface DepositObject { date: number | string; } + export const getDepositDate = ( deposit?: DepositObject | null ): string => - deposit ? formatDate( 'F j, Y', deposit?.date ) : '—'; + deposit + ? formatDateTime( moment.utc( deposit?.date ).toISOString(), null, { + includeTime: false, + } ) + : '—'; interface GetDepositMonthlyAnchorLabelProps { monthlyAnchor: number; diff --git a/client/deposits/utils/test/index.ts b/client/deposits/utils/test/index.ts index d0361137104..9b58bddf898 100644 --- a/client/deposits/utils/test/index.ts +++ b/client/deposits/utils/test/index.ts @@ -8,7 +8,19 @@ import momentLib from 'moment'; */ import { getDepositDate, getDepositMonthlyAnchorLabel } from '../'; +declare const global: { + wcpaySettings: { + dateFormat: string; + }; +}; + describe( 'Deposits Overview Utils / getDepositDate', () => { + beforeEach( () => { + global.wcpaySettings = { + dateFormat: 'F j, Y', + }; + } ); + test( 'returns a display value without a deposit', () => { expect( getDepositDate() ).toEqual( '—' ); } ); diff --git a/client/utils/date-time.ts b/client/utils/date-time.ts index 684b96f3cb3..f34c5f5bcfe 100644 --- a/client/utils/date-time.ts +++ b/client/utils/date-time.ts @@ -14,10 +14,8 @@ interface FormatDateTimeOptions { * Formats a date and time string according to WordPress settings or a custom format. * * @param { string } dateTime - The date and time string from the database in UTC (e.g., '2024-10-23 15:28:26'). - * @param { DateTimeFormat } [ customFormat=null ] - Optional custom format for date and time. If not provided, uses WordPress settings. - * @param { FormatDateTimeOptions } [ options={ includeTime: true, useGmt: true } ] - Additional options to control time inclusion and whether to use GMT/UTC. - * @param { boolean } [ options.includeTime=true ] - Whether to include the time in the formatted string. Defaults to true. - * @param { boolean } [ options.useGmt=true ] - Whether to display the time in GMT/UTC. Defaults to true. + * @param { DateTimeFormat } customFormat - Optional custom format for date and time. If not provided, uses WordPress settings. + * @param { FormatDateTimeOptions } options - Additional options to control time inclusion and whether to use GMT/UTC. * @return { string } - The formatted date and time string. */ export function formatDateTime( From 3ec71d3c8d34ee101d9ebfd94b9555b47a32bc68 Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Thu, 24 Oct 2024 19:23:55 +0200 Subject: [PATCH 07/76] Refactor disputes --- client/disputes/index.tsx | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/client/disputes/index.tsx b/client/disputes/index.tsx index 0832ee5515b..b2fa00e4be1 100644 --- a/client/disputes/index.tsx +++ b/client/disputes/index.tsx @@ -5,7 +5,6 @@ */ import React, { useState } from 'react'; import { recordEvent } from 'tracks'; -import { dateI18n } from '@wordpress/date'; import { _n, __, sprintf } from '@wordpress/i18n'; import moment from 'moment'; import { Button } from '@wordpress/components'; @@ -55,6 +54,7 @@ import CSVExportModal from 'components/csv-export-modal'; import { ReportingExportLanguageHook } from 'wcpay/settings/reporting-settings/interfaces'; import './style.scss'; +import { formatDateTime } from 'wcpay/utils/date-time'; const getHeaders = ( sortColumn?: string ): DisputesTableHeader[] => [ { @@ -198,9 +198,10 @@ const smartDueDate = ( dispute: CachedDispute ) => { ); } - return dateI18n( - 'M j, Y / g:iA', - moment.utc( dispute.due_by ).local().toISOString() + return formatDateTime( + moment.utc( dispute.due_by ).local().toISOString(), + null, + { useGmt: false } ); }; @@ -298,9 +299,10 @@ export const DisputesList = (): JSX.Element => { created: { value: dispute.created, display: clickable( - dateI18n( - 'M j, Y', - moment( dispute.created ).toISOString() + formatDateTime( + moment.utc( dispute.created ).local().toISOString(), + null, + { useGmt: false } ) ), }, @@ -480,17 +482,19 @@ export const DisputesList = (): JSX.Element => { { // Disputed On. ...row[ 10 ], - value: dateI18n( - 'Y-m-d', - moment( row[ 10 ].value ).toISOString() + value: formatDateTime( + moment.utc( row[ 10 ].value ).local().toISOString(), + null, + { useGmt: false, includeTime: false } ), }, { // Respond by. ...row[ 11 ], - value: dateI18n( - 'Y-m-d / g:iA', - moment( row[ 11 ].value ).toISOString() + value: formatDateTime( + moment.utc( row[ 11 ].value ).local().toISOString(), + null, + { useGmt: false } ), }, ]; From 80a05eff170b6f975dc106640ffefd33634c7eb9 Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Fri, 25 Oct 2024 11:01:17 +0200 Subject: [PATCH 08/76] Refactor disputes test --- client/disputes/test/index.tsx | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/client/disputes/test/index.tsx b/client/disputes/test/index.tsx index 1409bfc852d..056a2724fc1 100644 --- a/client/disputes/test/index.tsx +++ b/client/disputes/test/index.tsx @@ -17,13 +17,15 @@ import { useReportingExportLanguage, useSettings, } from 'data/index'; -import { formatDate, getUnformattedAmount } from 'wcpay/utils/test-utils'; +import { getUnformattedAmount } from 'wcpay/utils/test-utils'; import React from 'react'; import { CachedDispute, DisputeReason, DisputeStatus, } from 'wcpay/types/disputes'; +import { formatDateTime } from 'wcpay/utils/date-time'; +import moment from 'moment'; jest.mock( '@woocommerce/csv-export', () => { const actualModule = jest.requireActual( '@woocommerce/csv-export' ); @@ -100,6 +102,8 @@ declare const global: { reporting?: { exportModalDismissed: boolean; }; + dateFormat?: string; + timeFormat?: string; }; }; @@ -198,6 +202,8 @@ describe( 'Disputes list', () => { reporting: { exportModalDismissed: true, }, + dateFormat: 'Y-m-d', + timeFormat: 'g:iA', }; } ); @@ -363,8 +369,15 @@ describe( 'Disputes list', () => { `"${ displayFirstDispute[ 5 ] }"` ); // customer - expect( formatDate( csvFirstDispute[ 11 ], 'Y-m-d / g:iA' ) ).toBe( - formatDate( displayFirstDispute[ 6 ], 'Y-m-d / g:iA' ) + expect( csvFirstDispute[ 11 ].replace( /^"|"$/g, '' ) ).toBe( + formatDateTime( + moment + .utc( mockDisputes[ 0 ].due_by ) + .local() + .toISOString(), + null, + { useGmt: false } + ) ); // date respond by } ); } ); From 4bbea05f064eec9d87e72f3dfa42c334959248a6 Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Fri, 25 Oct 2024 11:26:29 +0200 Subject: [PATCH 09/76] Refactor documents list --- client/documents/list/index.tsx | 28 ++++++++++++++++------------ client/documents/list/test/index.tsx | 9 +++++++++ 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/client/documents/list/index.tsx b/client/documents/list/index.tsx index 307c3faf0c6..c7aa89cfbef 100644 --- a/client/documents/list/index.tsx +++ b/client/documents/list/index.tsx @@ -4,7 +4,6 @@ * External dependencies */ import React, { useCallback, useEffect, useState } from 'react'; -import { dateI18n } from '@wordpress/date'; import { __, _n, sprintf } from '@wordpress/i18n'; import moment from 'moment'; import { TableCard, TableCardColumn } from '@woocommerce/components'; @@ -21,6 +20,7 @@ import DocumentsFilters from '../filters'; import Page from '../../components/page'; import { getDocumentUrl } from 'wcpay/utils'; import VatFormModal from 'wcpay/vat/form-modal'; +import { formatDateTime } from 'wcpay/utils/date-time'; interface Column extends TableCardColumn { key: 'date' | 'type' | 'description' | 'download'; @@ -68,15 +68,18 @@ const getDocumentDescription = ( document: Document ) => { if ( document.period_from && document.period_to ) { return sprintf( __( 'VAT invoice for %s to %s', 'woocommerce-payments' ), - dateI18n( - 'M j, Y', - moment.utc( document.period_from ).toISOString(), - 'utc' + formatDateTime( + moment + .utc( document.period_from ) + .local() + .toISOString(), + null, + { useGmt: false, includeTime: false } ), - dateI18n( - 'M j, Y', - moment.utc( document.period_to ).toISOString(), - 'utc' + formatDateTime( + moment.utc( document.period_to ).local().toISOString(), + null, + { useGmt: false, includeTime: false } ) ); } @@ -180,9 +183,10 @@ export const DocumentsList = (): JSX.Element => { const data = { date: { value: document.date, - display: dateI18n( - 'M j, Y', - moment.utc( document.date ).local().toISOString() + display: formatDateTime( + moment.utc( document.date ).local().toISOString(), + null, + { useGmt: false, includeTime: false } ), }, type: { diff --git a/client/documents/list/test/index.tsx b/client/documents/list/test/index.tsx index c54cf7a02c8..0eac7e0bf61 100644 --- a/client/documents/list/test/index.tsx +++ b/client/documents/list/test/index.tsx @@ -36,6 +36,7 @@ declare const global: { accountStatus: { hasSubmittedVatData: boolean; }; + dateFormat: string; }; }; @@ -60,6 +61,11 @@ describe( 'Documents list', () => { let container: Element; let rerender: ( ui: React.ReactElement ) => void; beforeEach( () => { + global.wcpaySettings = { + accountStatus: { hasSubmittedVatData: true }, + dateFormat: 'M j, Y', + }; + mockUseDocuments.mockReturnValue( { documents: getMockDocuments(), isLoading: false, @@ -200,6 +206,7 @@ describe( 'Document download button', () => { beforeEach( () => { global.wcpaySettings = { accountStatus: { hasSubmittedVatData: true }, + dateFormat: 'M j, Y', }; render( ); @@ -223,6 +230,7 @@ describe( 'Document download button', () => { beforeEach( () => { global.wcpaySettings = { accountStatus: { hasSubmittedVatData: false }, + dateFormat: 'M j, Y', }; render( ); @@ -293,6 +301,7 @@ describe( 'Direct document download', () => { global.wcpaySettings = { accountStatus: { hasSubmittedVatData: true }, + dateFormat: 'M j, Y', }; } ); From 9f74ccc2b77ea4375412884101b040558ba40ef8 Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Fri, 25 Oct 2024 13:14:15 +0200 Subject: [PATCH 10/76] Refactor single currency settings --- .../single-currency-settings/index.js | 14 +++++--------- .../single-currency-settings/test/index.test.js | 2 ++ client/utils/date-time.ts | 11 ++++++++--- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/client/multi-currency/single-currency-settings/index.js b/client/multi-currency/single-currency-settings/index.js index 213409c9f47..9cb6f98e8b9 100644 --- a/client/multi-currency/single-currency-settings/index.js +++ b/client/multi-currency/single-currency-settings/index.js @@ -3,7 +3,6 @@ * External dependencies */ import React, { useContext, useEffect, useState } from 'react'; -import { dateI18n } from '@wordpress/date'; import { sprintf, __ } from '@wordpress/i18n'; import SettingsLayout from 'wcpay/settings/settings-layout'; import SettingsSection from 'wcpay/settings/settings-section'; @@ -26,10 +25,10 @@ import { useCurrencies, useCurrencySettings, useEnabledCurrencies, - useStoreSettings, } from 'wcpay/data'; import MultiCurrencySettingsContext from '../context'; import { LoadableBlock } from 'wcpay/components/loadable'; +import { formatDateTime } from 'wcpay/utils/date-time'; const SingleCurrencySettings = () => { const { @@ -43,7 +42,6 @@ const SingleCurrencySettings = () => { const { currencies } = useCurrencies(); const { enabledCurrencies } = useEnabledCurrencies(); - const { storeSettings } = useStoreSettings(); const { currencySettings, @@ -103,13 +101,11 @@ const SingleCurrencySettings = () => { } }, [ currencySettings, currency, initialPriceRoundingType ] ); - const dateFormat = storeSettings.date_format ?? 'M j, Y'; - const timeFormat = storeSettings.time_format ?? 'g:iA'; - const formattedLastUpdatedDateTime = targetCurrency - ? dateI18n( - `${ dateFormat } ${ timeFormat }`, - moment.unix( targetCurrency.last_updated ).toISOString() + ? formatDateTime( + moment.unix( targetCurrency.last_updated ).toISOString(), + null, + { useGmt: false, separator: '' } ) : ''; diff --git a/client/multi-currency/single-currency-settings/test/index.test.js b/client/multi-currency/single-currency-settings/test/index.test.js index 5f2597dff2c..77453b189dd 100644 --- a/client/multi-currency/single-currency-settings/test/index.test.js +++ b/client/multi-currency/single-currency-settings/test/index.test.js @@ -159,6 +159,8 @@ describe( 'Single currency settings screen', () => { precision: 2, }, }, + dateFormat: 'M j, Y', + timeFormat: 'g:iA', }; } ); diff --git a/client/utils/date-time.ts b/client/utils/date-time.ts index f34c5f5bcfe..2af43d874d0 100644 --- a/client/utils/date-time.ts +++ b/client/utils/date-time.ts @@ -8,6 +8,7 @@ type DateTimeFormat = string | null; interface FormatDateTimeOptions { includeTime?: boolean; // Whether to include time in the formatted string (defaults to true) useGmt?: boolean; // Whether to display the time in GMT/UTC (defaults to false) + separator?: string; // Separator between date and time (defaults to '/') } /** @@ -21,15 +22,19 @@ interface FormatDateTimeOptions { export function formatDateTime( dateTime: string, customFormat: DateTimeFormat = null, - options: FormatDateTimeOptions = { includeTime: true, useGmt: true } + options: FormatDateTimeOptions = { + includeTime: true, + useGmt: true, + separator: '/', + } ): string { - const { includeTime = true, useGmt = true } = options; + const { includeTime = true, useGmt = true, separator = ' /' } = options; // Use the WordPress settings for date and time format if no custom format is provided const dateFormat = customFormat || window.wcpaySettings.dateFormat; const timeFormat = includeTime ? window.wcpaySettings.timeFormat : ''; const format = `${ dateFormat }${ - includeTime ? ` / ${ timeFormat }` : '' + includeTime ? `${ separator } ${ timeFormat }` : '' }`; return dateI18n( format, dateTime, useGmt ); From 91e62cdd2203760a132ab4021948ac26284c1458 Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Fri, 25 Oct 2024 13:47:04 +0200 Subject: [PATCH 11/76] Fix error with custom format --- .../single-currency-settings/index.js | 2 +- client/utils/date-time.ts | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/client/multi-currency/single-currency-settings/index.js b/client/multi-currency/single-currency-settings/index.js index 9cb6f98e8b9..e1dbe85bbe1 100644 --- a/client/multi-currency/single-currency-settings/index.js +++ b/client/multi-currency/single-currency-settings/index.js @@ -105,7 +105,7 @@ const SingleCurrencySettings = () => { ? formatDateTime( moment.unix( targetCurrency.last_updated ).toISOString(), null, - { useGmt: false, separator: '' } + { useGmt: false, separator: ' ' } ) : ''; diff --git a/client/utils/date-time.ts b/client/utils/date-time.ts index 2af43d874d0..cbac9947940 100644 --- a/client/utils/date-time.ts +++ b/client/utils/date-time.ts @@ -28,14 +28,16 @@ export function formatDateTime( separator: '/', } ): string { - const { includeTime = true, useGmt = true, separator = ' /' } = options; + const { includeTime = true, useGmt = true, separator = ' / ' } = options; // Use the WordPress settings for date and time format if no custom format is provided - const dateFormat = customFormat || window.wcpaySettings.dateFormat; - const timeFormat = includeTime ? window.wcpaySettings.timeFormat : ''; - const format = `${ dateFormat }${ - includeTime ? `${ separator } ${ timeFormat }` : '' - }`; + const format = + customFormat || + `${ window.wcpaySettings.dateFormat }${ + includeTime + ? `${ separator }${ window.wcpaySettings.timeFormat }` + : '' + }`; return dateI18n( format, dateTime, useGmt ); } From e23a9806ad7e65af39767f2a9ba78e81c99b335f Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Fri, 25 Oct 2024 15:23:21 +0200 Subject: [PATCH 12/76] Refactor custom format --- client/deposits/details/index.tsx | 9 ++++----- client/deposits/list/index.tsx | 9 ++++----- client/deposits/utils/index.ts | 2 +- client/disputes/index.tsx | 16 +++++++--------- client/disputes/test/index.tsx | 1 - client/documents/list/index.tsx | 13 ++++++++----- .../single-currency-settings/index.js | 1 - client/transactions/list/deposit.tsx | 6 ++++-- client/transactions/list/index.tsx | 1 - client/utils/date-time.ts | 11 ++++++++--- 10 files changed, 36 insertions(+), 33 deletions(-) diff --git a/client/deposits/details/index.tsx b/client/deposits/details/index.tsx index fa241ee3b2e..4c1488ea629 100644 --- a/client/deposits/details/index.tsx +++ b/client/deposits/details/index.tsx @@ -109,11 +109,10 @@ export const DepositOverview: React.FC< DepositOverviewProps > = ( { key="depositDate" label={ `${ depositDateLabel }: ` + - formatDateTime( - moment.utc( deposit.date ).toISOString(), - null, - { useGmt: true, includeTime: false } - ) + formatDateTime( moment.utc( deposit.date ).toISOString(), { + useGmt: true, + includeTime: false, + } ) } value={ } detail={ deposit.bankAccount } diff --git a/client/deposits/list/index.tsx b/client/deposits/list/index.tsx index 30ce843f7aa..128602b2c22 100644 --- a/client/deposits/list/index.tsx +++ b/client/deposits/list/index.tsx @@ -133,11 +133,10 @@ export const DepositsList = (): JSX.Element => { href={ getDetailsURL( deposit.id, 'deposits' ) } onClick={ () => recordEvent( 'wcpay_deposits_row_click' ) } > - { formatDateTime( - moment.utc( deposit.date ).toISOString(), - null, - { includeTime: false, useGmt: true } - ) } + { formatDateTime( moment.utc( deposit.date ).toISOString(), { + includeTime: false, + useGmt: true, + } ) } ); diff --git a/client/deposits/utils/index.ts b/client/deposits/utils/index.ts index 54c70262c52..eb11a6dc4c6 100644 --- a/client/deposits/utils/index.ts +++ b/client/deposits/utils/index.ts @@ -12,7 +12,7 @@ interface DepositObject { export const getDepositDate = ( deposit?: DepositObject | null ): string => deposit - ? formatDateTime( moment.utc( deposit?.date ).toISOString(), null, { + ? formatDateTime( moment.utc( deposit?.date ).toISOString(), { includeTime: false, } ) : '—'; diff --git a/client/disputes/index.tsx b/client/disputes/index.tsx index b2fa00e4be1..181e28d038f 100644 --- a/client/disputes/index.tsx +++ b/client/disputes/index.tsx @@ -198,11 +198,9 @@ const smartDueDate = ( dispute: CachedDispute ) => { ); } - return formatDateTime( - moment.utc( dispute.due_by ).local().toISOString(), - null, - { useGmt: false } - ); + return formatDateTime( moment.utc( dispute.due_by ).local().toISOString(), { + useGmt: false, + } ); }; export const DisputesList = (): JSX.Element => { @@ -301,7 +299,6 @@ export const DisputesList = (): JSX.Element => { display: clickable( formatDateTime( moment.utc( dispute.created ).local().toISOString(), - null, { useGmt: false } ) ), @@ -484,8 +481,10 @@ export const DisputesList = (): JSX.Element => { ...row[ 10 ], value: formatDateTime( moment.utc( row[ 10 ].value ).local().toISOString(), - null, - { useGmt: false, includeTime: false } + { + useGmt: false, + includeTime: false, + } ), }, { @@ -493,7 +492,6 @@ export const DisputesList = (): JSX.Element => { ...row[ 11 ], value: formatDateTime( moment.utc( row[ 11 ].value ).local().toISOString(), - null, { useGmt: false } ), }, diff --git a/client/disputes/test/index.tsx b/client/disputes/test/index.tsx index 056a2724fc1..911222230e6 100644 --- a/client/disputes/test/index.tsx +++ b/client/disputes/test/index.tsx @@ -375,7 +375,6 @@ describe( 'Disputes list', () => { .utc( mockDisputes[ 0 ].due_by ) .local() .toISOString(), - null, { useGmt: false } ) ); // date respond by diff --git a/client/documents/list/index.tsx b/client/documents/list/index.tsx index c7aa89cfbef..8104beb513a 100644 --- a/client/documents/list/index.tsx +++ b/client/documents/list/index.tsx @@ -73,13 +73,14 @@ const getDocumentDescription = ( document: Document ) => { .utc( document.period_from ) .local() .toISOString(), - null, { useGmt: false, includeTime: false } ), formatDateTime( moment.utc( document.period_to ).local().toISOString(), - null, - { useGmt: false, includeTime: false } + { + useGmt: false, + includeTime: false, + } ) ); } @@ -185,8 +186,10 @@ export const DocumentsList = (): JSX.Element => { value: document.date, display: formatDateTime( moment.utc( document.date ).local().toISOString(), - null, - { useGmt: false, includeTime: false } + { + useGmt: false, + includeTime: false, + } ), }, type: { diff --git a/client/multi-currency/single-currency-settings/index.js b/client/multi-currency/single-currency-settings/index.js index e1dbe85bbe1..c783e9ffa36 100644 --- a/client/multi-currency/single-currency-settings/index.js +++ b/client/multi-currency/single-currency-settings/index.js @@ -104,7 +104,6 @@ const SingleCurrencySettings = () => { const formattedLastUpdatedDateTime = targetCurrency ? formatDateTime( moment.unix( targetCurrency.last_updated ).toISOString(), - null, { useGmt: false, separator: ' ' } ) : ''; diff --git a/client/transactions/list/deposit.tsx b/client/transactions/list/deposit.tsx index bf2e600522d..df85884c748 100644 --- a/client/transactions/list/deposit.tsx +++ b/client/transactions/list/deposit.tsx @@ -34,8 +34,10 @@ const Deposit: React.FC< DepositProps > = ( { depositId, dateAvailable } ) => { const formattedDateAvailable = formatDateTime( moment.utc( dateAvailable ).toISOString(), - null, - { includeTime: false, useGmt: true } + { + includeTime: false, + useGmt: true, + } ); return { formattedDateAvailable }; } diff --git a/client/transactions/list/index.tsx b/client/transactions/list/index.tsx index 16585a174aa..3023b0a2cbd 100644 --- a/client/transactions/list/index.tsx +++ b/client/transactions/list/index.tsx @@ -468,7 +468,6 @@ export const TransactionsList = ( display: clickable( formatDateTime( moment.utc( txn.date ).local().toISOString(), - null, { useGmt: false } ) ), diff --git a/client/utils/date-time.ts b/client/utils/date-time.ts index cbac9947940..d637d30622b 100644 --- a/client/utils/date-time.ts +++ b/client/utils/date-time.ts @@ -9,26 +9,31 @@ interface FormatDateTimeOptions { includeTime?: boolean; // Whether to include time in the formatted string (defaults to true) useGmt?: boolean; // Whether to display the time in GMT/UTC (defaults to false) separator?: string; // Separator between date and time (defaults to '/') + customFormat?: DateTimeFormat; // Custom format to use instead of WordPress settings } /** * Formats a date and time string according to WordPress settings or a custom format. * * @param { string } dateTime - The date and time string from the database in UTC (e.g., '2024-10-23 15:28:26'). - * @param { DateTimeFormat } customFormat - Optional custom format for date and time. If not provided, uses WordPress settings. * @param { FormatDateTimeOptions } options - Additional options to control time inclusion and whether to use GMT/UTC. * @return { string } - The formatted date and time string. */ export function formatDateTime( dateTime: string, - customFormat: DateTimeFormat = null, options: FormatDateTimeOptions = { includeTime: true, useGmt: true, separator: '/', + customFormat: null, } ): string { - const { includeTime = true, useGmt = true, separator = ' / ' } = options; + const { + customFormat = null, + includeTime = true, + useGmt = true, + separator = ' / ', + } = options; // Use the WordPress settings for date and time format if no custom format is provided const format = From dca862c17834038afe84bcf86f2e7263eec5f65c Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Fri, 25 Oct 2024 15:27:14 +0200 Subject: [PATCH 13/76] Refactor update business details modal --- .../overview/modal/update-business-details/index.tsx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/client/overview/modal/update-business-details/index.tsx b/client/overview/modal/update-business-details/index.tsx index 2c654a57f9b..2a23e7ac8dc 100644 --- a/client/overview/modal/update-business-details/index.tsx +++ b/client/overview/modal/update-business-details/index.tsx @@ -13,6 +13,7 @@ import moment from 'moment'; import strings from './strings'; import './index.scss'; import { recordEvent } from 'wcpay/tracks'; +import { formatDateTime } from 'wcpay/utils/date-time'; interface Props { errorMessages: Array< string >; @@ -57,11 +58,14 @@ const UpdateBusinessDetailsModal = ( { currentDeadline ? sprintf( strings.restrictedSoonDescription, - dateI18n( - 'ga M j, Y', + formatDateTime( moment( currentDeadline * 1000 - ).toISOString() + ).toISOString(), + { + useGmt: false, + customFormat: 'ga M j, Y', + } ) ) : strings.restrictedDescription } From 172b39ce4fb04a0910282941abcb170b0578038d Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Fri, 25 Oct 2024 16:11:56 +0200 Subject: [PATCH 14/76] Refactor task list of overview page --- .../overview/task-list/tasks/dispute-task.tsx | 19 +++++++++++++------ .../tasks/update-business-details-task.tsx | 9 ++++----- client/overview/task-list/test/tasks.js | 1 + 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/client/overview/task-list/tasks/dispute-task.tsx b/client/overview/task-list/tasks/dispute-task.tsx index fdf48c03d78..b91bf4acc0b 100644 --- a/client/overview/task-list/tasks/dispute-task.tsx +++ b/client/overview/task-list/tasks/dispute-task.tsx @@ -15,6 +15,7 @@ import { formatCurrency } from 'wcpay/utils/currency'; import { getAdminUrl } from 'wcpay/utils'; import { recordEvent } from 'tracks'; import { isDueWithin } from 'wcpay/disputes/utils'; +import { formatDateTime } from 'wcpay/utils/date-time'; /** * Returns an array of disputes that are due within the specified number of days. @@ -142,9 +143,12 @@ export const getDisputeResolutionTask = ( ? sprintf( __( 'Respond today by %s', 'woocommerce-payments' ), // Show due_by time in local timezone: e.g. "11:59 PM". - dateI18n( - 'g:i A', - moment.utc( dispute.due_by ).local().toISOString() + formatDateTime( + moment.utc( dispute.due_by ).local().toISOString(), + { + useGmt: false, + customFormat: 'g:i A', + } ) ) : sprintf( @@ -153,9 +157,12 @@ export const getDisputeResolutionTask = ( 'woocommerce-payments' ), // Show due_by date in local timezone: e.g. "Jan 1, 2021". - dateI18n( - 'M j, Y', - moment.utc( dispute.due_by ).local().toISOString() + formatDateTime( + moment.utc( dispute.due_by ).local().toISOString(), + { + useGmt: false, + includeTime: false, + } ), moment( dispute.due_by ).fromNow( true ) // E.g. "2 days". ); diff --git a/client/overview/task-list/tasks/update-business-details-task.tsx b/client/overview/task-list/tasks/update-business-details-task.tsx index a9746ec7b66..9399951521b 100644 --- a/client/overview/task-list/tasks/update-business-details-task.tsx +++ b/client/overview/task-list/tasks/update-business-details-task.tsx @@ -11,9 +11,9 @@ import { addQueryArgs } from '@wordpress/url'; */ import type { TaskItemProps } from '../types'; import UpdateBusinessDetailsModal from 'wcpay/overview/modal/update-business-details'; -import { dateI18n } from '@wordpress/date'; import moment from 'moment'; import { recordEvent } from 'wcpay/tracks'; +import { formatDateTime } from 'wcpay/utils/date-time'; export const getUpdateBusinessDetailsTask = ( errorMessages: string[], @@ -46,10 +46,9 @@ export const getUpdateBusinessDetailsTask = ( 'Update by %s to avoid a disruption in deposits.', 'woocommerce-payments' ), - dateI18n( - 'ga M j, Y', - moment( currentDeadline * 1000 ).toISOString() - ) + formatDateTime( moment( currentDeadline * 1000 ).toISOString(), { + customFormat: 'ga M j, Y', + } ) ); if ( hasSingleError ) { diff --git a/client/overview/task-list/test/tasks.js b/client/overview/task-list/test/tasks.js index 44b5de4e9f7..429ed175efb 100644 --- a/client/overview/task-list/test/tasks.js +++ b/client/overview/task-list/test/tasks.js @@ -139,6 +139,7 @@ describe( 'getTasks()', () => { precision: 2, }, }, + dateFormat: 'M j, Y', }; } ); afterEach( () => { From 619282efee289ada580fc79b7bfd8a4e2fa8add6 Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Fri, 25 Oct 2024 16:59:36 +0200 Subject: [PATCH 15/76] Refactor payment details summary --- client/payment-details/summary/index.tsx | 18 +++++++++++------- .../summary/test/index.test.tsx | 4 ++++ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/client/payment-details/summary/index.tsx b/client/payment-details/summary/index.tsx index 4350d609cd7..4662209b48c 100644 --- a/client/payment-details/summary/index.tsx +++ b/client/payment-details/summary/index.tsx @@ -61,6 +61,7 @@ import DisputeResolutionFooter from '../dispute-details/dispute-resolution-foote import ErrorBoundary from 'components/error-boundary'; import RefundModal from 'wcpay/payment-details/summary/refund-modal'; import CardNotice from 'wcpay/components/card-notice'; +import { formatDateTime } from 'wcpay/utils/date-time'; declare const window: any; @@ -107,9 +108,12 @@ const composePaymentSummaryItems = ( { { title: __( 'Date', 'woocommerce-payments' ), content: charge.created - ? dateI18n( - 'M j, Y, g:ia', - moment( charge.created * 1000 ).toISOString() + ? formatDateTime( + moment( charge.created * 1000 ).toISOString(), + { + customFormat: 'M j, Y, g:ia', + useGmt: false, + } ) : '–', }, @@ -703,12 +707,12 @@ const PaymentDetailsSummary: React.FC< PaymentDetailsSummaryProps > = ( { } ) }{ ' ' } diff --git a/client/payment-details/summary/test/index.test.tsx b/client/payment-details/summary/test/index.test.tsx index 9055d481dda..d98363a720e 100755 --- a/client/payment-details/summary/test/index.test.tsx +++ b/client/payment-details/summary/test/index.test.tsx @@ -34,6 +34,8 @@ declare const global: { featureFlags: { isAuthAndCaptureEnabled: boolean; }; + dateFormat: string; + timeFormat: string; }; }; @@ -203,6 +205,8 @@ describe( 'PaymentDetailsSummary', () => { precision: 0, }, }, + dateFormat: 'M j, Y', + timeFormat: 'g:iA', }; // mock Date.now that moment library uses to get current date for testing purposes From 47406f3d8bfc806cbf73a46128a4f8c868072efe Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Fri, 25 Oct 2024 17:03:13 +0200 Subject: [PATCH 16/76] Refactor dispute details in payment details --- .../dispute-details/dispute-due-by-date.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/client/payment-details/dispute-details/dispute-due-by-date.tsx b/client/payment-details/dispute-details/dispute-due-by-date.tsx index 18993ef2387..29c3cccc69e 100644 --- a/client/payment-details/dispute-details/dispute-due-by-date.tsx +++ b/client/payment-details/dispute-details/dispute-due-by-date.tsx @@ -2,10 +2,10 @@ * External dependencies */ import React from 'react'; -import { dateI18n } from '@wordpress/date'; import { __, _n, sprintf } from '@wordpress/i18n'; import classNames from 'classnames'; import moment from 'moment'; +import { formatDateTime } from 'wcpay/utils/date-time'; const DisputeDueByDate: React.FC< { dueBy: number; @@ -14,9 +14,11 @@ const DisputeDueByDate: React.FC< { const daysRemaining = Math.floor( moment.unix( dueBy ).diff( moment(), 'days', true ) ); - const respondByDate = dateI18n( - 'M j, Y, g:ia', - moment( dueBy * 1000 ).toISOString() + const respondByDate = formatDateTime( + moment( dueBy * 1000 ).toISOString(), + { + customFormat: 'M j, Y, g:ia', + } ); return ( From 18168974a612f74be39f2d08452d5f21014aa1cf Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Fri, 25 Oct 2024 17:13:34 +0200 Subject: [PATCH 17/76] Refactor dispute resolution in payment details --- .../dispute-resolution-footer.tsx | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/client/payment-details/dispute-details/dispute-resolution-footer.tsx b/client/payment-details/dispute-details/dispute-resolution-footer.tsx index 15fec759244..8364d85a0a7 100644 --- a/client/payment-details/dispute-details/dispute-resolution-footer.tsx +++ b/client/payment-details/dispute-details/dispute-resolution-footer.tsx @@ -3,7 +3,6 @@ */ import React from 'react'; import moment from 'moment'; -import { dateI18n } from '@wordpress/date'; import { __, sprintf } from '@wordpress/i18n'; import { Link } from '@woocommerce/components'; import { createInterpolateElement } from '@wordpress/element'; @@ -17,13 +16,13 @@ import { recordEvent } from 'tracks'; import { getAdminUrl } from 'wcpay/utils'; import { getDisputeFeeFormatted } from 'wcpay/disputes/utils'; import './style.scss'; +import { formatDateTime } from 'wcpay/utils/date-time'; const DisputeUnderReviewFooter: React.FC< { dispute: Pick< Dispute, 'id' | 'metadata' | 'status' >; } > = ( { dispute } ) => { const submissionDateFormatted = dispute.metadata.__evidence_submitted_at - ? dateI18n( - 'M j, Y', + ? formatDateTime( moment .unix( parseInt( dispute.metadata.__evidence_submitted_at, 10 ) @@ -93,8 +92,7 @@ const DisputeWonFooter: React.FC< { dispute: Pick< Dispute, 'id' | 'metadata' | 'status' >; } > = ( { dispute } ) => { const closedDateFormatted = dispute.metadata.__dispute_closed_at - ? dateI18n( - 'M j, Y', + ? formatDateTime( moment .unix( parseInt( dispute.metadata.__dispute_closed_at, 10 ) @@ -171,13 +169,13 @@ const DisputeLostFooter: React.FC< { const disputeFeeFormatted = getDisputeFeeFormatted( dispute, true ) ?? '-'; const closedDateFormatted = dispute.metadata.__dispute_closed_at - ? dateI18n( - 'M j, Y', + ? formatDateTime( moment .unix( parseInt( dispute.metadata.__dispute_closed_at, 10 ) ) - .toISOString() + .toISOString(), + { includeTime: false } ) : '-'; @@ -274,13 +272,13 @@ const InquiryUnderReviewFooter: React.FC< { dispute: Pick< Dispute, 'id' | 'metadata' | 'status' >; } > = ( { dispute } ) => { const submissionDateFormatted = dispute.metadata.__evidence_submitted_at - ? dateI18n( - 'M j, Y', + ? formatDateTime( moment .unix( parseInt( dispute.metadata.__evidence_submitted_at, 10 ) ) - .toISOString() + .toISOString(), + { includeTime: false } ) : '-'; @@ -346,13 +344,13 @@ const InquiryClosedFooter: React.FC< { } > = ( { dispute } ) => { const isSubmitted = !! dispute.metadata.__evidence_submitted_at; const closedDateFormatted = dispute.metadata.__dispute_closed_at - ? dateI18n( - 'M j, Y', + ? formatDateTime( moment .unix( parseInt( dispute.metadata.__dispute_closed_at, 10 ) ) - .toISOString() + .toISOString(), + { includeTime: false } ) : '-'; From 6e12e7a53b974616f214a84e12fada5e5f73cb26 Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Fri, 25 Oct 2024 17:16:57 +0200 Subject: [PATCH 18/76] Refactor dispute steps in payment details --- .../dispute-details/dispute-steps.tsx | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/client/payment-details/dispute-details/dispute-steps.tsx b/client/payment-details/dispute-details/dispute-steps.tsx index ccc0764f38b..86022f5ad31 100644 --- a/client/payment-details/dispute-details/dispute-steps.tsx +++ b/client/payment-details/dispute-details/dispute-steps.tsx @@ -7,7 +7,6 @@ import React from 'react'; import { __, sprintf } from '@wordpress/i18n'; import { createInterpolateElement } from '@wordpress/element'; import { ExternalLink } from '@wordpress/components'; -import { dateI18n } from '@wordpress/date'; import moment from 'moment'; import HelpOutlineIcon from 'gridicons/dist/help-outline'; @@ -20,6 +19,7 @@ import { formatExplicitCurrency } from 'utils/currency'; import { ClickTooltip } from 'wcpay/components/tooltip'; import { getDisputeFeeFormatted } from 'wcpay/disputes/utils'; import DisputeDueByDate from './dispute-due-by-date'; +import { formatDateTime } from 'wcpay/utils/date-time'; interface Props { dispute: Dispute; @@ -34,13 +34,15 @@ export const DisputeSteps: React.FC< Props > = ( { } ) => { let emailLink; if ( customer?.email ) { - const chargeDate = dateI18n( - 'Y-m-d', - moment( chargeCreated * 1000 ).toISOString() + const chargeDate = formatDateTime( + moment( chargeCreated * 1000 ).toISOString(), + { + includeTime: false, + } ); - const disputeDate = dateI18n( - 'Y-m-d', - moment( dispute.created * 1000 ).toISOString() + const disputeDate = formatDateTime( + moment( dispute.created * 1000 ).toISOString(), + { includeTime: false } ); const emailSubject = sprintf( // Translators: %1$s is the store name, %2$s is the charge date. @@ -173,12 +175,10 @@ export const InquirySteps: React.FC< Props > = ( { } ) => { let emailLink; if ( customer?.email ) { - const chargeDate = dateI18n( - 'Y-m-d', + const chargeDate = formatDateTime( moment( chargeCreated * 1000 ).toISOString() ); - const disputeDate = dateI18n( - 'Y-m-d', + const disputeDate = formatDateTime( moment( dispute.created * 1000 ).toISOString() ); const emailSubject = sprintf( From d24dcef2f3925f714fb73b774e2511339d6a5fa3 Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Fri, 25 Oct 2024 17:33:08 +0200 Subject: [PATCH 19/76] Refactor dispute summary row in dispute details --- .../dispute-details/dispute-summary-row.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/payment-details/dispute-details/dispute-summary-row.tsx b/client/payment-details/dispute-details/dispute-summary-row.tsx index ac6dada265e..d01741c50c8 100644 --- a/client/payment-details/dispute-details/dispute-summary-row.tsx +++ b/client/payment-details/dispute-details/dispute-summary-row.tsx @@ -7,7 +7,6 @@ import React from 'react'; import moment from 'moment'; import HelpOutlineIcon from 'gridicons/dist/help-outline'; import { __ } from '@wordpress/i18n'; -import { dateI18n } from '@wordpress/date'; /** * Internal dependencies @@ -20,6 +19,7 @@ import { formatStringValue } from 'wcpay/utils'; import { ClickTooltip } from 'wcpay/components/tooltip'; import Paragraphs from 'wcpay/components/paragraphs'; import DisputeDueByDate from './dispute-due-by-date'; +import { formatDateTime } from 'wcpay/utils/date-time'; interface Props { dispute: Dispute; @@ -39,9 +39,9 @@ const DisputeSummaryRow: React.FC< Props > = ( { dispute } ) => { { title: __( 'Disputed On', 'woocommerce-payments' ), content: dispute.created - ? dateI18n( - 'M j, Y, g:ia', - moment( dispute.created * 1000 ).toISOString() + ? formatDateTime( + moment( dispute.created * 1000 ).toISOString(), + { useGmt: false } ) : '–', }, From 4dbca9c5d256065a24c13e97f1c198c013fc328e Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Fri, 25 Oct 2024 17:40:08 +0200 Subject: [PATCH 20/76] Refactor dispute details --- client/payment-details/dispute-details/dispute-due-by-date.tsx | 2 +- client/payment-details/dispute-details/dispute-summary-row.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/payment-details/dispute-details/dispute-due-by-date.tsx b/client/payment-details/dispute-details/dispute-due-by-date.tsx index 29c3cccc69e..6aaaa571831 100644 --- a/client/payment-details/dispute-details/dispute-due-by-date.tsx +++ b/client/payment-details/dispute-details/dispute-due-by-date.tsx @@ -17,7 +17,7 @@ const DisputeDueByDate: React.FC< { const respondByDate = formatDateTime( moment( dueBy * 1000 ).toISOString(), { - customFormat: 'M j, Y, g:ia', + separator: ', ', } ); return ( diff --git a/client/payment-details/dispute-details/dispute-summary-row.tsx b/client/payment-details/dispute-details/dispute-summary-row.tsx index d01741c50c8..b1221bb3c67 100644 --- a/client/payment-details/dispute-details/dispute-summary-row.tsx +++ b/client/payment-details/dispute-details/dispute-summary-row.tsx @@ -41,7 +41,7 @@ const DisputeSummaryRow: React.FC< Props > = ( { dispute } ) => { content: dispute.created ? formatDateTime( moment( dispute.created * 1000 ).toISOString(), - { useGmt: false } + { useGmt: false, separator: ', ' } ) : '–', }, From 792ad58c7729d31cd28241af777fd3140beb9415 Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Fri, 25 Oct 2024 17:49:52 +0200 Subject: [PATCH 21/76] Refactor payment details timeline --- client/payment-details/timeline/map-events.js | 14 +++++++------- client/payment-details/timeline/test/index.js | 1 + client/payment-details/timeline/test/map-events.js | 1 + 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/client/payment-details/timeline/map-events.js b/client/payment-details/timeline/map-events.js index 1cb14d6aa7a..8596dfa811c 100644 --- a/client/payment-details/timeline/map-events.js +++ b/client/payment-details/timeline/map-events.js @@ -5,7 +5,6 @@ */ import { flatMap } from 'lodash'; import { __, sprintf } from '@wordpress/i18n'; -import { dateI18n } from '@wordpress/date'; import { addQueryArgs } from '@wordpress/url'; import moment from 'moment'; import { createInterpolateElement } from '@wordpress/element'; @@ -31,6 +30,7 @@ import { formatFee } from 'utils/fees'; import { getAdminUrl } from 'wcpay/utils'; import { ShieldIcon } from 'wcpay/icons'; import { fraudOutcomeRulesetMapping } from './mappings'; +import { formatDateTime } from 'wcpay/utils/date-time'; /** * Creates a timeline item about a payment status change @@ -84,9 +84,9 @@ const getDepositTimelineItem = ( 'woocommerce-payments' ), formattedAmount, - dateI18n( - 'M j, Y', - moment( event.deposit.arrival_date * 1000 ).toISOString() + formatDateTime( + moment( event.deposit.arrival_date * 1000 ).toISOString(), + { includeTime: false } ) ); const depositUrl = getAdminUrl( { @@ -143,9 +143,9 @@ const getFinancingPaydownTimelineItem = ( event, formattedAmount, body ) => { 'woocommerce-payments' ), formattedAmount, - dateI18n( - 'M j, Y', - moment( event.deposit.arrival_date * 1000 ).toISOString() + formatDateTime( + moment( event.deposit.arrival_date * 1000 ).toISOString(), + { includeTime: false } ) ); diff --git a/client/payment-details/timeline/test/index.js b/client/payment-details/timeline/test/index.js index 2529f3d673e..616c780dd69 100644 --- a/client/payment-details/timeline/test/index.js +++ b/client/payment-details/timeline/test/index.js @@ -34,6 +34,7 @@ describe( 'PaymentDetailsTimeline', () => { precision: 2, }, }, + dateFormat: 'M j, Y', }; } ); diff --git a/client/payment-details/timeline/test/map-events.js b/client/payment-details/timeline/test/map-events.js index c3e42ceae8b..73f65b94940 100644 --- a/client/payment-details/timeline/test/map-events.js +++ b/client/payment-details/timeline/test/map-events.js @@ -47,6 +47,7 @@ describe( 'mapTimelineEvents', () => { precision: 2, }, }, + dateFormat: 'M j, Y', }; } ); From 3f825a1d6a452b5de5282f8dc4d544a4cef3e3fd Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Fri, 25 Oct 2024 17:52:41 +0200 Subject: [PATCH 22/76] Refactor blocked transactions --- client/transactions/blocked/columns.tsx | 8 ++++---- client/transactions/blocked/test/columns.test.tsx | 4 ++++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/client/transactions/blocked/columns.tsx b/client/transactions/blocked/columns.tsx index 3ecf1554d06..2b24f4e77f1 100644 --- a/client/transactions/blocked/columns.tsx +++ b/client/transactions/blocked/columns.tsx @@ -2,7 +2,6 @@ * External dependencies */ import React from 'react'; -import { dateI18n } from '@wordpress/date'; import { __ } from '@wordpress/i18n'; import moment from 'moment'; import { TableCardColumn, TableCardBodyColumn } from '@woocommerce/components'; @@ -15,6 +14,7 @@ import TransactionStatusPill from 'wcpay/components/transaction-status-pill'; import { FraudOutcomeTransaction } from '../../data'; import { getDetailsURL } from '../../components/details-link'; import ClickableCell from '../../components/clickable-cell'; +import { formatDateTime } from 'wcpay/utils/date-time'; interface Column extends TableCardColumn { key: 'created' | 'amount' | 'customer' | 'status'; @@ -70,9 +70,9 @@ export const getBlockedListRowContent = ( data.payment_intent.id || data.order_id.toString(), 'transactions' ); - const formattedCreatedDate = dateI18n( - 'M j, Y / g:iA', - moment.utc( data.created ).local().toISOString() + const formattedCreatedDate = formatDateTime( + moment.utc( data.created ).local().toISOString(), + { useGmt: false } ); const clickable = ( children: JSX.Element | string ) => ( diff --git a/client/transactions/blocked/test/columns.test.tsx b/client/transactions/blocked/test/columns.test.tsx index 7ca2c3d4895..b65e2d10c12 100644 --- a/client/transactions/blocked/test/columns.test.tsx +++ b/client/transactions/blocked/test/columns.test.tsx @@ -15,6 +15,8 @@ declare const global: { connect: { country: string; }; + dateFormat: string; + timeFormat: string; }; }; const mockWcPaySettings = { @@ -23,6 +25,8 @@ const mockWcPaySettings = { connect: { country: 'US', }, + dateFormat: 'M j, Y', + timeFormat: 'g:iA', }; describe( 'Blocked fraud outcome transactions columns', () => { From 72f15df49a48725397d9a6e7b09b47d0b42f4c10 Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Fri, 25 Oct 2024 17:55:30 +0200 Subject: [PATCH 23/76] Refactor transactions risk review --- client/transactions/risk-review/columns.tsx | 7 ++++--- client/transactions/risk-review/test/columns.test.tsx | 4 ++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/client/transactions/risk-review/columns.tsx b/client/transactions/risk-review/columns.tsx index 59db13c52c8..ab1b39e9259 100644 --- a/client/transactions/risk-review/columns.tsx +++ b/client/transactions/risk-review/columns.tsx @@ -17,6 +17,7 @@ import { formatExplicitCurrency } from 'utils/currency'; import { recordEvent } from 'tracks'; import TransactionStatusPill from 'wcpay/components/transaction-status-pill'; import { FraudOutcomeTransaction } from '../../data'; +import { formatDateTime } from 'wcpay/utils/date-time'; interface Column extends TableCardColumn { key: 'created' | 'amount' | 'customer' | 'status'; @@ -76,9 +77,9 @@ export const getRiskReviewListRowContent = ( data: FraudOutcomeTransaction ): Record< string, TableCardBodyColumn > => { const detailsURL = getDetailsURL( data.payment_intent.id, 'transactions' ); - const formattedCreatedDate = dateI18n( - 'M j, Y / g:iA', - moment.utc( data.created ).local().toISOString() + const formattedCreatedDate = formatDateTime( + moment.utc( data.created ).local().toISOString(), + { useGmt: false } ); const clickable = ( children: JSX.Element | string ) => ( diff --git a/client/transactions/risk-review/test/columns.test.tsx b/client/transactions/risk-review/test/columns.test.tsx index 033f9eb7aa2..54de107e0e4 100644 --- a/client/transactions/risk-review/test/columns.test.tsx +++ b/client/transactions/risk-review/test/columns.test.tsx @@ -15,6 +15,8 @@ declare const global: { connect: { country: string; }; + dateFormat: string; + timeFormat: string; }; }; const mockWcPaySettings = { @@ -23,6 +25,8 @@ const mockWcPaySettings = { connect: { country: 'US', }, + dateFormat: 'M j, Y', + timeFormat: 'g:iA', }; describe( 'Review fraud outcome transactions columns', () => { From 31e79462f7a8f5e36a0e054832ce68f5a375c65b Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Fri, 25 Oct 2024 17:59:01 +0200 Subject: [PATCH 24/76] Refactor uncaptured transactions --- client/transactions/uncaptured/index.tsx | 26 +++++++++---------- .../uncaptured/test/index.test.tsx | 4 +++ 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/client/transactions/uncaptured/index.tsx b/client/transactions/uncaptured/index.tsx index 3cf14c956f7..174097e5cd5 100644 --- a/client/transactions/uncaptured/index.tsx +++ b/client/transactions/uncaptured/index.tsx @@ -7,7 +7,6 @@ import React, { useEffect } from 'react'; import { __ } from '@wordpress/i18n'; import { TableCard, TableCardColumn } from '@woocommerce/components'; import { onQueryChange, getQuery } from '@woocommerce/navigation'; -import { dateI18n } from '@wordpress/date'; import moment from 'moment'; /** @@ -21,6 +20,7 @@ import { formatExplicitCurrency } from 'utils/currency'; import RiskLevel, { calculateRiskMapping } from 'components/risk-level'; import { recordEvent } from 'tracks'; import CaptureAuthorizationButton from 'wcpay/components/capture-authorization-button'; +import { formatDateTime } from 'wcpay/utils/date-time'; interface Column extends TableCardColumn { key: @@ -130,35 +130,35 @@ export const AuthorizationsList = (): JSX.Element => { display: auth.payment_intent_id, }, created: { - value: dateI18n( - 'M j, Y / g:iA', - moment.utc( auth.created ).local().toISOString() + value: formatDateTime( + moment.utc( auth.created ).local().toISOString(), + { useGmt: false } ), display: clickable( - dateI18n( - 'M j, Y / g:iA', - moment.utc( auth.created ).local().toISOString() + formatDateTime( + moment.utc( auth.created ).local().toISOString(), + { useGmt: false } ) ), }, // Payments are authorized for a maximum of 7 days capture_by: { - value: dateI18n( - 'M j, Y / g:iA', + value: formatDateTime( moment .utc( auth.created ) .add( 7, 'd' ) .local() - .toISOString() + .toISOString(), + { useGmt: false } ), display: clickable( - dateI18n( - 'M j, Y / g:iA', + formatDateTime( moment .utc( auth.created ) .add( 7, 'd' ) .local() - .toISOString() + .toISOString(), + { useGmt: false } ) ), }, diff --git a/client/transactions/uncaptured/test/index.test.tsx b/client/transactions/uncaptured/test/index.test.tsx index 52f76dcc882..fc21a532b67 100644 --- a/client/transactions/uncaptured/test/index.test.tsx +++ b/client/transactions/uncaptured/test/index.test.tsx @@ -67,6 +67,8 @@ declare const global: { precision: number; }; }; + dateFormat: string; + timeFormat: string; }; }; @@ -126,6 +128,8 @@ describe( 'Authorizations list', () => { precision: 2, }, }, + dateFormat: 'M j, Y', + timeFormat: 'g:iA', }; } ); From 4b969ff1c7a6baacd67d890f2cda19c2b579eb22 Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Fri, 25 Oct 2024 18:05:34 +0200 Subject: [PATCH 25/76] Refactor account fees --- .../account-fees/expiration-description.js | 10 +++++++--- .../account-status/account-fees/test/index.js | 1 + 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/client/components/account-status/account-fees/expiration-description.js b/client/components/account-status/account-fees/expiration-description.js index a07067b6495..9dd993fbd03 100644 --- a/client/components/account-status/account-fees/expiration-description.js +++ b/client/components/account-status/account-fees/expiration-description.js @@ -4,13 +4,13 @@ * External dependencies */ import { __, sprintf } from '@wordpress/i18n'; -import { dateI18n } from '@wordpress/date'; import moment from 'moment'; /** * Internal dependencies */ import { formatCurrency } from 'utils/currency'; +import { formatDateTime } from 'wcpay/utils/date-time'; const ExpirationDescription = ( { feeData: { volume_allowance: volumeAllowance, end_time: endTime, ...rest }, @@ -26,7 +26,9 @@ const ExpirationDescription = ( { 'woocommerce-payments' ), formatCurrency( volumeAllowance, currencyCode ), - dateI18n( 'F j, Y', moment( endTime ).toISOString() ) + formatDateTime( moment( endTime ).toISOString(), { + includeTime: false, + } ) ); } else if ( volumeAllowance ) { description = sprintf( @@ -44,7 +46,9 @@ const ExpirationDescription = ( { 'Discounted base fee expires on %1$s.', 'woocommerce-payments' ), - dateI18n( 'F j, Y', moment( endTime ).toISOString() ) + formatDateTime( moment( endTime ).toISOString(), { + includeTime: false, + } ) ); } else { return null; diff --git a/client/components/account-status/account-fees/test/index.js b/client/components/account-status/account-fees/test/index.js index 5258af4ffdc..7405b33e371 100644 --- a/client/components/account-status/account-fees/test/index.js +++ b/client/components/account-status/account-fees/test/index.js @@ -46,6 +46,7 @@ describe( 'AccountFees', () => { precision: 2, }, }, + dateFormat: 'F j, Y', }; } ); From 88a43fb84c976a5658b9247cca785c613632bbba Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Fri, 25 Oct 2024 18:08:57 +0200 Subject: [PATCH 26/76] Remove unused dependency --- client/deposits/details/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/client/deposits/details/index.tsx b/client/deposits/details/index.tsx index 4c1488ea629..9236bc02eb1 100644 --- a/client/deposits/details/index.tsx +++ b/client/deposits/details/index.tsx @@ -4,7 +4,6 @@ * External dependencies */ import React from 'react'; -import { dateI18n } from '@wordpress/date'; import { __, sprintf } from '@wordpress/i18n'; import moment from 'moment'; import { From 62c13963a68dc394142f0fb0300e19c48619e685 Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Sat, 26 Oct 2024 18:04:37 +0200 Subject: [PATCH 27/76] Refactor deposits list --- client/deposits/list/index.tsx | 6 ++---- client/deposits/list/test/__snapshots__/index.tsx.snap | 8 ++++---- client/deposits/list/test/index.tsx | 6 +++--- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/client/deposits/list/index.tsx b/client/deposits/list/index.tsx index 128602b2c22..623bd5e13fe 100644 --- a/client/deposits/list/index.tsx +++ b/client/deposits/list/index.tsx @@ -7,7 +7,6 @@ import { DepositsTableHeader } from 'wcpay/types/deposits'; import React, { useState } from 'react'; import { recordEvent } from 'tracks'; import { useMemo } from '@wordpress/element'; -import { dateI18n } from '@wordpress/date'; import { __, _n, sprintf } from '@wordpress/i18n'; import moment from 'moment'; import { TableCard, Link } from '@woocommerce/components'; @@ -325,10 +324,9 @@ export const DepositsList = (): JSX.Element => { row[ 0 ], { ...row[ 1 ], - value: dateI18n( - 'Y-m-d', + value: formatDateTime( moment.utc( row[ 1 ].value ).toISOString(), - true + { useGmt: true, includeTime: false } ), }, ...row.slice( 2 ), diff --git a/client/deposits/list/test/__snapshots__/index.tsx.snap b/client/deposits/list/test/__snapshots__/index.tsx.snap index 5d6d3d1b96c..64b57b8c38d 100644 --- a/client/deposits/list/test/__snapshots__/index.tsx.snap +++ b/client/deposits/list/test/__snapshots__/index.tsx.snap @@ -345,7 +345,7 @@ exports[`Deposits list renders correctly a single deposit 1`] = ` data-link-type="wc-admin" href="admin.php?page=wc-admin&path=%2Fpayments%2Fdeposits%2Fdetails&id=po_mock1" > - Jan 2, 2020 + Jan 2 2020 - Jan 3, 2020 + Jan 3 2020 - Jan 2, 2020 + Jan 2 2020 - Jan 3, 2020 + Jan 3 2020 { reporting: { exportModalDismissed: true, }, - dateFormat: 'M j, Y', + dateFormat: 'M j Y', }; } ); @@ -310,7 +310,7 @@ describe( 'Deposits list', () => { // 2. The indexOf check in amount's expect is because the amount in CSV may not contain // trailing zeros as in the display amount. // - expect( formatDate( csvFirstDeposit[ 1 ], 'M j, Y' ) ).toBe( + expect( csvFirstDeposit[ 1 ].replace( /^"|"$/g, '' ) ).toBe( displayFirstDeposit[ 0 ] ); // date expect( csvFirstDeposit[ 2 ] ).toBe( displayFirstDeposit[ 1 ] ); // type From 563338261d176e7e27013802fa50078b72edb910 Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Sat, 26 Oct 2024 18:22:50 +0200 Subject: [PATCH 28/76] Remove unused dependency --- client/deposits/utils/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/client/deposits/utils/index.ts b/client/deposits/utils/index.ts index eb11a6dc4c6..7ba4b807271 100644 --- a/client/deposits/utils/index.ts +++ b/client/deposits/utils/index.ts @@ -2,7 +2,6 @@ * External dependencies */ import { __ } from '@wordpress/i18n'; -import { dateI18n } from '@wordpress/date'; import moment from 'moment'; import { formatDateTime } from 'wcpay/utils/date-time'; From 58c0860d3a9554ce9a98b13056dc5067a5b83d11 Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Mon, 28 Oct 2024 09:38:42 +0100 Subject: [PATCH 29/76] Remove unused dependencies --- client/overview/modal/update-business-details/index.tsx | 1 - client/overview/task-list/tasks/dispute-task.tsx | 1 - client/payment-details/summary/index.tsx | 1 - client/transactions/list/deposit.tsx | 1 - client/transactions/risk-review/columns.tsx | 1 - 5 files changed, 5 deletions(-) diff --git a/client/overview/modal/update-business-details/index.tsx b/client/overview/modal/update-business-details/index.tsx index 2a23e7ac8dc..f3185ca8a04 100644 --- a/client/overview/modal/update-business-details/index.tsx +++ b/client/overview/modal/update-business-details/index.tsx @@ -3,7 +3,6 @@ */ import React, { useState } from 'react'; import { Button, Modal, Notice } from '@wordpress/components'; -import { dateI18n } from '@wordpress/date'; import { sprintf } from '@wordpress/i18n'; import moment from 'moment'; diff --git a/client/overview/task-list/tasks/dispute-task.tsx b/client/overview/task-list/tasks/dispute-task.tsx index b91bf4acc0b..ea0b78e0017 100644 --- a/client/overview/task-list/tasks/dispute-task.tsx +++ b/client/overview/task-list/tasks/dispute-task.tsx @@ -2,7 +2,6 @@ * External dependencies */ import { __, sprintf } from '@wordpress/i18n'; -import { dateI18n } from '@wordpress/date'; import moment from 'moment'; import { getHistory } from '@woocommerce/navigation'; diff --git a/client/payment-details/summary/index.tsx b/client/payment-details/summary/index.tsx index 4662209b48c..a241bbcbfc8 100644 --- a/client/payment-details/summary/index.tsx +++ b/client/payment-details/summary/index.tsx @@ -4,7 +4,6 @@ * External dependencies */ import { __ } from '@wordpress/i18n'; -import { dateI18n } from '@wordpress/date'; import { Card, CardBody, diff --git a/client/transactions/list/deposit.tsx b/client/transactions/list/deposit.tsx index df85884c748..c4413c9c372 100644 --- a/client/transactions/list/deposit.tsx +++ b/client/transactions/list/deposit.tsx @@ -5,7 +5,6 @@ */ import React from 'react'; import moment from 'moment'; -import { dateI18n } from '@wordpress/date'; import { __ } from '@wordpress/i18n'; import interpolateComponents from '@automattic/interpolate-components'; import { ExternalLink } from '@wordpress/components'; diff --git a/client/transactions/risk-review/columns.tsx b/client/transactions/risk-review/columns.tsx index ab1b39e9259..957e016d7fb 100644 --- a/client/transactions/risk-review/columns.tsx +++ b/client/transactions/risk-review/columns.tsx @@ -2,7 +2,6 @@ * External dependencies */ import React from 'react'; -import { dateI18n } from '@wordpress/date'; import { __ } from '@wordpress/i18n'; import moment from 'moment'; import { TableCardColumn, TableCardBodyColumn } from '@woocommerce/components'; From 365a4cf9f294205a550595a9abc8d4edeaa9bb4a Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Mon, 28 Oct 2024 15:58:12 +0100 Subject: [PATCH 30/76] Refactor capital loan page --- client/capital/index.tsx | 11 ++++++++--- client/capital/test/index.test.tsx | 2 ++ .../components/active-loan-summary/index.tsx | 19 ++++++++++--------- .../test/__snapshots__/index.js.snap | 2 +- .../active-loan-summary/test/index.js | 1 + client/utils/date-time.ts | 2 +- 6 files changed, 23 insertions(+), 14 deletions(-) diff --git a/client/capital/index.tsx b/client/capital/index.tsx index 81e76ad91b4..6a51dacf8b2 100644 --- a/client/capital/index.tsx +++ b/client/capital/index.tsx @@ -25,6 +25,7 @@ import Chip from 'components/chip'; import { useLoans } from 'wcpay/data'; import { getAdminUrl } from 'wcpay/utils'; import './style.scss'; +import { formatDateTime } from 'wcpay/utils/date-time'; const columns = [ { @@ -80,7 +81,7 @@ const getLoanStatusText = ( loan: CapitalLoan ) => { return loan.fully_paid_at ? __( 'Paid off', 'woocommerce-payments' ) + ': ' + - dateI18n( 'M j, Y', loan.fully_paid_at ) + formatDateTime( loan.fully_paid_at, { includeTime: false } ) : __( 'Active', 'woocommerce-payments' ); }; @@ -112,7 +113,9 @@ const getRowsData = ( loans: CapitalLoan[] ) => const data = { paid_out_at: { value: loan.paid_out_at, - display: clickable( dateI18n( 'M j, Y', loan.paid_out_at ) ), + display: clickable( + formatDateTime( loan.paid_out_at, { includeTime: false } ) + ), }, status: { value: getLoanStatusText( loan ), @@ -150,7 +153,9 @@ const getRowsData = ( loans: CapitalLoan[] ) => value: loan.first_paydown_at, display: clickable( loan.first_paydown_at - ? dateI18n( 'M j, Y', loan.first_paydown_at ) + ? formatDateTime( loan.first_paydown_at, { + includeTime: false, + } ) : '-' ), }, diff --git a/client/capital/test/index.test.tsx b/client/capital/test/index.test.tsx index f9eb43b8a49..41ea917902a 100644 --- a/client/capital/test/index.test.tsx +++ b/client/capital/test/index.test.tsx @@ -25,6 +25,7 @@ declare const global: { accountLoans: { has_active_loan: boolean; }; + dateFormat: string; }; }; @@ -37,6 +38,7 @@ describe( 'CapitalPage', () => { }, accountLoans: { has_active_loan: true }, testMode: true, + dateFormat: 'M j, Y', }; } ); diff --git a/client/components/active-loan-summary/index.tsx b/client/components/active-loan-summary/index.tsx index 0c5059ef87c..d6cdfa2bd14 100755 --- a/client/components/active-loan-summary/index.tsx +++ b/client/components/active-loan-summary/index.tsx @@ -24,6 +24,7 @@ import { useActiveLoanSummary } from 'wcpay/data'; import { getAdminUrl } from 'wcpay/utils'; import './style.scss'; +import { formatDateTime } from 'wcpay/utils/date-time'; const Block = ( { title, @@ -210,12 +211,12 @@ const ActiveLoanSummary = (): JSX.Element => { 'Repaid this period (until %s)', 'woocommerce-payments' ), - dateI18n( - 'M j, Y', + formatDateTime( new Date( details.current_repayment_interval.due_at * 1000 - ) + ), + { includeTime: false } ) ) } > @@ -251,9 +252,9 @@ const ActiveLoanSummary = (): JSX.Element => { - { dateI18n( - 'M j, Y', - new Date( details.advance_paid_out_at * 1000 ) + { formatDateTime( + new Date( details.advance_paid_out_at * 1000 ), + { includeTime: false } ) } { - { dateI18n( - 'M j, Y', - new Date( details.repayments_begin_at * 1000 ) + { formatDateTime( + new Date( details.repayments_begin_at * 1000 ), + { includeTime: false } ) } diff --git a/client/components/active-loan-summary/test/__snapshots__/index.js.snap b/client/components/active-loan-summary/test/__snapshots__/index.js.snap index 4424415245c..4e9dd15ec13 100644 --- a/client/components/active-loan-summary/test/__snapshots__/index.js.snap +++ b/client/components/active-loan-summary/test/__snapshots__/index.js.snap @@ -74,7 +74,7 @@ exports[`Active loan summary renders correctly 1`] = `
- Repaid this period (until Feb 14, 2022) + Repaid this period (until Feb 15, 2022)
{ precision: 2, }, }, + dateFormat: 'M j, Y', }; } ); afterEach( () => { diff --git a/client/utils/date-time.ts b/client/utils/date-time.ts index d637d30622b..95aa9909660 100644 --- a/client/utils/date-time.ts +++ b/client/utils/date-time.ts @@ -20,7 +20,7 @@ interface FormatDateTimeOptions { * @return { string } - The formatted date and time string. */ export function formatDateTime( - dateTime: string, + dateTime: string | Date, options: FormatDateTimeOptions = { includeTime: true, useGmt: true, From b5aef871fd93f8df7351e37d0e59886d714ebb01 Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Mon, 28 Oct 2024 16:46:34 +0100 Subject: [PATCH 31/76] Remove unused dependencies --- client/capital/index.tsx | 1 - client/components/active-loan-summary/index.tsx | 1 - 2 files changed, 2 deletions(-) diff --git a/client/capital/index.tsx b/client/capital/index.tsx index 6a51dacf8b2..fe83d61a2df 100644 --- a/client/capital/index.tsx +++ b/client/capital/index.tsx @@ -6,7 +6,6 @@ import * as React from 'react'; import { __, _n } from '@wordpress/i18n'; import { TableCard } from '@woocommerce/components'; -import { dateI18n } from '@wordpress/date'; /** * Internal dependencies. diff --git a/client/components/active-loan-summary/index.tsx b/client/components/active-loan-summary/index.tsx index d6cdfa2bd14..e6e84aa92ca 100755 --- a/client/components/active-loan-summary/index.tsx +++ b/client/components/active-loan-summary/index.tsx @@ -13,7 +13,6 @@ import { } from '@wordpress/components'; import { __, sprintf } from '@wordpress/i18n'; import { createInterpolateElement } from '@wordpress/element'; -import { dateI18n } from '@wordpress/date'; /** * Internal dependencies. From 284592da9d965e2e712e9336c4718e9f73ab3026 Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Mon, 28 Oct 2024 16:47:13 +0100 Subject: [PATCH 32/76] Refactor disputes --- client/disputes/evidence/test/index.js | 3 +++ client/disputes/info/index.tsx | 14 +++++++------- client/disputes/info/test/index.tsx | 4 ++++ 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/client/disputes/evidence/test/index.js b/client/disputes/evidence/test/index.js index e9ccdb826cb..142ee738ab8 100644 --- a/client/disputes/evidence/test/index.js +++ b/client/disputes/evidence/test/index.js @@ -96,6 +96,7 @@ describe( 'Dispute evidence form', () => { global.wcpaySettings = { restUrl: 'http://example.com/wp-json/', + dateFormat: 'M j, Y', }; } ); afterEach( () => { @@ -190,6 +191,8 @@ describe( 'Dispute evidence page', () => { precision: 2, }, }, + dateFormat: 'M j, Y', + timeFormat: 'g:iA', }; } ); diff --git a/client/disputes/info/index.tsx b/client/disputes/info/index.tsx index 12f7ba0e64a..433a70fa1b9 100644 --- a/client/disputes/info/index.tsx +++ b/client/disputes/info/index.tsx @@ -5,7 +5,6 @@ */ import * as React from 'react'; import { __ } from '@wordpress/i18n'; -import { dateI18n } from '@wordpress/date'; import moment from 'moment'; import { Link } from '@woocommerce/components'; @@ -20,6 +19,7 @@ import { formatExplicitCurrency } from 'multi-currency/interface/functions'; import './style.scss'; import Loadable from 'components/loadable'; import { Dispute } from 'wcpay/types/disputes'; +import { formatDateTime } from 'wcpay/utils/date-time'; const fields: { key: string; label: string }[] = [ { key: 'created', label: __( 'Dispute date', 'woocommerce-payments' ) }, @@ -69,20 +69,20 @@ const Info = ( { transactionId: __( 'Transaction link', 'woocommerce-payments' ), } : { - created: dateI18n( - 'M j, Y', - moment( dispute.created * 1000 ).toISOString() + created: formatDateTime( + moment( dispute.created * 1000 ).toISOString(), + { includeTime: false, useGmt: false } ), amount: formatExplicitCurrency( dispute.amount || 0, dispute.currency || 'USD' ), dueBy: dispute.evidence_details - ? dateI18n( - 'M j, Y - g:iA', + ? formatDateTime( moment( dispute.evidence_details.due_by * 1000 - ).toISOString() + ).toISOString(), + { separator: ' - ', useGmt: false } ) : null, reason: composeDisputeReason( dispute ), diff --git a/client/disputes/info/test/index.tsx b/client/disputes/info/test/index.tsx index 1e2f5dcb8b0..7b49b588052 100644 --- a/client/disputes/info/test/index.tsx +++ b/client/disputes/info/test/index.tsx @@ -29,6 +29,8 @@ declare const global: { precision: number; }; }; + dateFormat: string; + timeFormat: string; }; }; @@ -49,6 +51,8 @@ describe( 'Dispute info', () => { precision: 2, }, }, + dateFormat: 'M j, Y', + timeFormat: 'g:iA', }; } ); From 906f0888ff2676c86556ea65a9a150ea62d00b3f Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Mon, 28 Oct 2024 18:08:22 +0100 Subject: [PATCH 33/76] Add unit test for disputed order notice --- .../test/__snapshots__/index.test.js.snap | 91 ++++++++++++++ .../disputed-order-notice/test/index.test.js | 111 ++++++++++++++++++ 2 files changed, 202 insertions(+) create mode 100644 client/components/disputed-order-notice/test/__snapshots__/index.test.js.snap create mode 100644 client/components/disputed-order-notice/test/index.test.js diff --git a/client/components/disputed-order-notice/test/__snapshots__/index.test.js.snap b/client/components/disputed-order-notice/test/__snapshots__/index.test.js.snap new file mode 100644 index 00000000000..4c3f5a3611a --- /dev/null +++ b/client/components/disputed-order-notice/test/__snapshots__/index.test.js.snap @@ -0,0 +1,91 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`DisputedOrderNoticeHandler renders regular dispute notice 1`] = ` +
+
+
+
+
+ + This order has a payment dispute for $10.00 for the reason 'Transaction unauthorized'. + + + Please respond before Oct 28, 2023. +
+ +
+
+
+
+
+
+
+`; + +exports[`DisputedOrderNoticeHandler renders urgent dispute notice 1`] = ` +
+
+
+
+
+ + Please resolve the dispute on this order of $10.00 labeled 'Transaction unauthorized' by Oct 28, 2023. + + + (Last day today) +
+ +
+
+
+
+
+
+
+`; diff --git a/client/components/disputed-order-notice/test/index.test.js b/client/components/disputed-order-notice/test/index.test.js new file mode 100644 index 00000000000..7e44da132e0 --- /dev/null +++ b/client/components/disputed-order-notice/test/index.test.js @@ -0,0 +1,111 @@ +/** + * External dependencies + */ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +/** + * Internal dependencies + */ +import DisputedOrderNoticeHandler from '../index'; +import { useCharge } from 'wcpay/data'; + +jest.mock( 'wcpay/data', () => ( { + useCharge: jest.fn(), +} ) ); + +jest.mock( 'tracks', () => ( { + recordEvent: jest.fn(), +} ) ); + +describe( 'DisputedOrderNoticeHandler', () => { + const mockCharge = { + dispute: { + status: 'needs_response', + reason: 'fraudulent', + amount: 1000, + currency: 'USD', + evidence_details: { + due_by: 1698500219, + }, + }, + }; + + beforeEach( () => { + window.wcpaySettings = { + zeroDecimalCurrencies: [], + connect: { + country: 'US', + }, + }; + useCharge.mockReturnValue( { data: mockCharge } ); + } ); + + afterEach( () => { + jest.useRealTimers(); + jest.clearAllMocks(); + } ); + + test( 'renders urgent dispute notice', () => { + const fixedDate = new Date( '2023-10-28T00:00:00Z' ); + jest.useFakeTimers(); + jest.setSystemTime( fixedDate ); + + const { container } = render( + + ); + const disputeMessages = screen.getAllByText( + /Please resolve the dispute on this order of/ + ); + expect( disputeMessages[ 0 ] ).toBeInTheDocument(); + expect( screen.getByRole( 'button' ) ).toHaveTextContent( + 'Respond today' + ); + expect( container ).toMatchSnapshot(); + } ); + + test( 'renders regular dispute notice', () => { + const fixedDate = new Date( '2023-10-20T00:00:00Z' ); + jest.useFakeTimers(); + jest.setSystemTime( fixedDate ); + + const { container } = render( + + ); + const disputeMessages = screen.getAllByText( /Please respond before/ ); + expect( disputeMessages[ 0 ] ).toBeInTheDocument(); + expect( screen.getByRole( 'button' ) ).toHaveTextContent( + 'Respond now' + ); + expect( container ).toMatchSnapshot(); + } ); + + test( 'does not render notice if no dispute', () => { + useCharge.mockReturnValue( { data: {} } ); + const { container } = render( + + ); + expect( container ).toBeEmptyDOMElement(); + } ); + + test( 'does not render notice if dispute is not awaiting response', () => { + mockCharge.dispute.status = 'won'; + render( + + ); + expect( + screen.queryByText( /Please resolve the dispute on this order of/ ) + ).not.toBeInTheDocument(); + } ); +} ); From b257e24f8e80a95f866f2f8e4da171f5fe1dc564 Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Mon, 28 Oct 2024 18:12:44 +0100 Subject: [PATCH 34/76] Add changelog --- changelog/dev-add-unit-tests-for-diputed-order-notice | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelog/dev-add-unit-tests-for-diputed-order-notice diff --git a/changelog/dev-add-unit-tests-for-diputed-order-notice b/changelog/dev-add-unit-tests-for-diputed-order-notice new file mode 100644 index 00000000000..8d88338d894 --- /dev/null +++ b/changelog/dev-add-unit-tests-for-diputed-order-notice @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Add Jest tests for the disputed order notices From 959c3d790612c7441993f7c8c5b053ba2f4574fe Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Tue, 29 Oct 2024 10:00:12 +0100 Subject: [PATCH 35/76] Refactor disputed order notice --- client/components/disputed-order-notice/index.js | 6 +++--- client/components/disputed-order-notice/test/index.test.js | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/client/components/disputed-order-notice/index.js b/client/components/disputed-order-notice/index.js index ab51a52d16e..07caeca137c 100644 --- a/client/components/disputed-order-notice/index.js +++ b/client/components/disputed-order-notice/index.js @@ -1,7 +1,6 @@ import moment from 'moment'; import React, { useEffect } from 'react'; import { __, _n, sprintf } from '@wordpress/i18n'; -import { dateI18n } from '@wordpress/date'; import { createInterpolateElement } from '@wordpress/element'; /** @@ -20,6 +19,7 @@ import { import { useCharge } from 'wcpay/data'; import { recordEvent } from 'tracks'; import './style.scss'; +import { formatDateTime } from 'wcpay/utils/date-time'; const DisputedOrderNoticeHandler = ( { chargeId, onDisableOrderRefund } ) => { const { data: charge } = useCharge( chargeId ); @@ -131,7 +131,7 @@ const UrgentDisputeNoticeBody = ( { formatString, formattedAmount, reasons[ disputeReason ].display, - dateI18n( 'M j, Y', dueBy.local().toISOString() ) + formatDateTime( dueBy.local().toISOString(), { includeTime: false } ) ); let suffix = sprintf( @@ -182,7 +182,7 @@ const RegularDisputeNoticeBody = ( { const suffix = sprintf( // Translators: %1$s is the dispute due date. __( 'Please respond before %1$s.', 'woocommerce-payments' ), - dateI18n( 'M j, Y', dueBy.local().toISOString() ) + formatDateTime( dueBy.local().toISOString(), { includeTime: false } ) ); return ( diff --git a/client/components/disputed-order-notice/test/index.test.js b/client/components/disputed-order-notice/test/index.test.js index 7e44da132e0..784092295f3 100644 --- a/client/components/disputed-order-notice/test/index.test.js +++ b/client/components/disputed-order-notice/test/index.test.js @@ -36,6 +36,7 @@ describe( 'DisputedOrderNoticeHandler', () => { connect: { country: 'US', }, + dateFormat: 'M j, Y', }; useCharge.mockReturnValue( { data: mockCharge } ); } ); From 99fa5779e7f9a6b241d88c22a46d685cc80833cf Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Tue, 29 Oct 2024 11:29:31 +0100 Subject: [PATCH 36/76] Add unit tests --- client/utils/date-time.ts | 4 +- client/utils/test/date-time.test.ts | 87 +++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 client/utils/test/date-time.test.ts diff --git a/client/utils/date-time.ts b/client/utils/date-time.ts index 95aa9909660..ef7bd6ed977 100644 --- a/client/utils/date-time.ts +++ b/client/utils/date-time.ts @@ -15,7 +15,7 @@ interface FormatDateTimeOptions { /** * Formats a date and time string according to WordPress settings or a custom format. * - * @param { string } dateTime - The date and time string from the database in UTC (e.g., '2024-10-23 15:28:26'). + * @param { string } dateTime - The date and time string or date from the database in UTC (e.g., '2024-10-23 15:28:26'). * @param { FormatDateTimeOptions } options - Additional options to control time inclusion and whether to use GMT/UTC. * @return { string } - The formatted date and time string. */ @@ -24,7 +24,7 @@ export function formatDateTime( options: FormatDateTimeOptions = { includeTime: true, useGmt: true, - separator: '/', + separator: ' / ', customFormat: null, } ): string { diff --git a/client/utils/test/date-time.test.ts b/client/utils/test/date-time.test.ts new file mode 100644 index 00000000000..066ec6a77a4 --- /dev/null +++ b/client/utils/test/date-time.test.ts @@ -0,0 +1,87 @@ +/** + * External dependencies + */ +import { dateI18n } from '@wordpress/date'; +/** + * Internal dependencies + */ +import { formatDateTime } from '../date-time'; + +jest.mock( '@wordpress/date', () => ( { + dateI18n: jest.fn(), +} ) ); + +describe( 'formatDateTime', () => { + const originalWcpaySettings = window.wcpaySettings; + const mockWcpaySettings = { + dateFormat: 'Y-m-d', + timeFormat: 'H:i', + }; + + beforeAll( () => { + window.wcpaySettings = mockWcpaySettings as typeof wcpaySettings; + } ); + + afterAll( () => { + window.wcpaySettings = originalWcpaySettings; + } ); + + it( 'should format date and time using default WordPress settings', () => { + const dateTime = '2024-10-23 15:28:26'; + formatDateTime( dateTime ); + + expect( dateI18n ).toHaveBeenCalledWith( + `${ mockWcpaySettings.dateFormat } / ${ mockWcpaySettings.timeFormat }`, + dateTime, + true + ); + } ); + + it( 'should use custom format if provided', () => { + const dateTime = '2024-10-23 15:28:26'; + const options = { customFormat: 'd-m-Y H:i:s' }; + formatDateTime( dateTime, options ); + + expect( dateI18n ).toHaveBeenCalledWith( + 'd-m-Y H:i:s', + dateTime, + true + ); + } ); + + it( 'should exclude time if includeTime is set to false', () => { + const dateTime = '2024-10-23 15:28:26'; + const options = { includeTime: false }; + formatDateTime( dateTime, options ); + + expect( dateI18n ).toHaveBeenCalledWith( + mockWcpaySettings.dateFormat, + dateTime, + true + ); + } ); + + it( 'should handle GMT/UTC setting correctly', () => { + const dateTime = '2024-10-23 15:28:26'; + const options = { useGmt: false }; + formatDateTime( dateTime, options ); + + expect( dateI18n ).toHaveBeenCalledWith( + `${ mockWcpaySettings.dateFormat } / ${ mockWcpaySettings.timeFormat }`, + dateTime, + false + ); + } ); + + it( 'should use custom separator when provided', () => { + const dateTime = '2024-10-23 15:28:26'; + const options = { separator: ' - ' }; + formatDateTime( dateTime, options ); + + expect( dateI18n ).toHaveBeenCalledWith( + `${ mockWcpaySettings.dateFormat } - ${ mockWcpaySettings.timeFormat }`, + dateTime, + true + ); + } ); +} ); From 4eae670938253b6353975b1e8453bd64ce6e8f8c Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Tue, 29 Oct 2024 11:41:52 +0100 Subject: [PATCH 37/76] Refactor function name --- client/capital/index.tsx | 10 ++++++---- .../account-fees/expiration-description.js | 6 +++--- client/components/active-loan-summary/index.tsx | 8 ++++---- .../components/disputed-order-notice/index.js | 10 +++++++--- client/deposits/details/index.tsx | 4 ++-- client/deposits/list/index.tsx | 15 +++++++++------ client/deposits/utils/index.ts | 4 ++-- client/disputes/index.tsx | 17 ++++++++++------- client/disputes/info/index.tsx | 6 +++--- client/disputes/test/index.tsx | 4 ++-- client/documents/list/index.tsx | 8 ++++---- .../modal/update-business-details/index.tsx | 4 ++-- .../overview/task-list/tasks/dispute-task.tsx | 6 +++--- .../tasks/update-business-details-task.tsx | 11 +++++++---- .../dispute-details/dispute-due-by-date.tsx | 4 ++-- .../dispute-resolution-footer.tsx | 12 ++++++------ .../dispute-details/dispute-steps.tsx | 10 +++++----- .../dispute-details/dispute-summary-row.tsx | 4 ++-- client/payment-details/summary/index.tsx | 6 +++--- client/payment-details/timeline/map-events.js | 6 +++--- client/transactions/blocked/columns.tsx | 4 ++-- client/transactions/list/deposit.tsx | 4 ++-- client/transactions/list/index.tsx | 4 ++-- client/transactions/risk-review/columns.tsx | 4 ++-- client/transactions/uncaptured/index.tsx | 10 +++++----- client/utils/date-time.ts | 2 +- client/utils/test/date-time.test.ts | 12 ++++++------ .../client/settings/single-currency/index.js | 4 ++-- 28 files changed, 107 insertions(+), 92 deletions(-) diff --git a/client/capital/index.tsx b/client/capital/index.tsx index fe83d61a2df..dea559ba74e 100644 --- a/client/capital/index.tsx +++ b/client/capital/index.tsx @@ -24,7 +24,7 @@ import Chip from 'components/chip'; import { useLoans } from 'wcpay/data'; import { getAdminUrl } from 'wcpay/utils'; import './style.scss'; -import { formatDateTime } from 'wcpay/utils/date-time'; +import { formatUserDateTime } from 'wcpay/utils/date-time'; const columns = [ { @@ -80,7 +80,7 @@ const getLoanStatusText = ( loan: CapitalLoan ) => { return loan.fully_paid_at ? __( 'Paid off', 'woocommerce-payments' ) + ': ' + - formatDateTime( loan.fully_paid_at, { includeTime: false } ) + formatUserDateTime( loan.fully_paid_at, { includeTime: false } ) : __( 'Active', 'woocommerce-payments' ); }; @@ -113,7 +113,9 @@ const getRowsData = ( loans: CapitalLoan[] ) => paid_out_at: { value: loan.paid_out_at, display: clickable( - formatDateTime( loan.paid_out_at, { includeTime: false } ) + formatUserDateTime( loan.paid_out_at, { + includeTime: false, + } ) ), }, status: { @@ -152,7 +154,7 @@ const getRowsData = ( loans: CapitalLoan[] ) => value: loan.first_paydown_at, display: clickable( loan.first_paydown_at - ? formatDateTime( loan.first_paydown_at, { + ? formatUserDateTime( loan.first_paydown_at, { includeTime: false, } ) : '-' diff --git a/client/components/account-status/account-fees/expiration-description.js b/client/components/account-status/account-fees/expiration-description.js index f7a4015bcd6..b9472c238b8 100644 --- a/client/components/account-status/account-fees/expiration-description.js +++ b/client/components/account-status/account-fees/expiration-description.js @@ -10,7 +10,7 @@ import moment from 'moment'; * Internal dependencies */ import { formatCurrency } from 'multi-currency/interface/functions'; -import { formatDateTime } from 'wcpay/utils/date-time'; +import { formatUserDateTime } from 'wcpay/utils/date-time'; const ExpirationDescription = ( { feeData: { volume_allowance: volumeAllowance, end_time: endTime, ...rest }, @@ -26,7 +26,7 @@ const ExpirationDescription = ( { 'woocommerce-payments' ), formatCurrency( volumeAllowance, currencyCode ), - formatDateTime( moment( endTime ).toISOString(), { + formatUserDateTime( moment( endTime ).toISOString(), { includeTime: false, } ) ); @@ -46,7 +46,7 @@ const ExpirationDescription = ( { 'Discounted base fee expires on %1$s.', 'woocommerce-payments' ), - formatDateTime( moment( endTime ).toISOString(), { + formatUserDateTime( moment( endTime ).toISOString(), { includeTime: false, } ) ); diff --git a/client/components/active-loan-summary/index.tsx b/client/components/active-loan-summary/index.tsx index e6e84aa92ca..a403c2e5eeb 100755 --- a/client/components/active-loan-summary/index.tsx +++ b/client/components/active-loan-summary/index.tsx @@ -23,7 +23,7 @@ import { useActiveLoanSummary } from 'wcpay/data'; import { getAdminUrl } from 'wcpay/utils'; import './style.scss'; -import { formatDateTime } from 'wcpay/utils/date-time'; +import { formatUserDateTime } from 'wcpay/utils/date-time'; const Block = ( { title, @@ -210,7 +210,7 @@ const ActiveLoanSummary = (): JSX.Element => { 'Repaid this period (until %s)', 'woocommerce-payments' ), - formatDateTime( + formatUserDateTime( new Date( details.current_repayment_interval.due_at * 1000 @@ -251,7 +251,7 @@ const ActiveLoanSummary = (): JSX.Element => { - { formatDateTime( + { formatUserDateTime( new Date( details.advance_paid_out_at * 1000 ), { includeTime: false } ) } @@ -278,7 +278,7 @@ const ActiveLoanSummary = (): JSX.Element => { - { formatDateTime( + { formatUserDateTime( new Date( details.repayments_begin_at * 1000 ), { includeTime: false } ) } diff --git a/client/components/disputed-order-notice/index.js b/client/components/disputed-order-notice/index.js index 07caeca137c..892f0313cf0 100644 --- a/client/components/disputed-order-notice/index.js +++ b/client/components/disputed-order-notice/index.js @@ -19,7 +19,7 @@ import { import { useCharge } from 'wcpay/data'; import { recordEvent } from 'tracks'; import './style.scss'; -import { formatDateTime } from 'wcpay/utils/date-time'; +import { formatUserDateTime } from 'wcpay/utils/date-time'; const DisputedOrderNoticeHandler = ( { chargeId, onDisableOrderRefund } ) => { const { data: charge } = useCharge( chargeId ); @@ -131,7 +131,9 @@ const UrgentDisputeNoticeBody = ( { formatString, formattedAmount, reasons[ disputeReason ].display, - formatDateTime( dueBy.local().toISOString(), { includeTime: false } ) + formatUserDateTime( dueBy.local().toISOString(), { + includeTime: false, + } ) ); let suffix = sprintf( @@ -182,7 +184,9 @@ const RegularDisputeNoticeBody = ( { const suffix = sprintf( // Translators: %1$s is the dispute due date. __( 'Please respond before %1$s.', 'woocommerce-payments' ), - formatDateTime( dueBy.local().toISOString(), { includeTime: false } ) + formatUserDateTime( dueBy.local().toISOString(), { + includeTime: false, + } ) ); return ( diff --git a/client/deposits/details/index.tsx b/client/deposits/details/index.tsx index 71708420559..e3f4cc0afc9 100644 --- a/client/deposits/details/index.tsx +++ b/client/deposits/details/index.tsx @@ -39,7 +39,7 @@ import { } from 'multi-currency/interface/functions'; import { displayStatus } from '../strings'; import './style.scss'; -import { formatDateTime } from 'wcpay/utils/date-time'; +import { formatUserDateTime } from 'wcpay/utils/date-time'; /** * Renders the deposit status indicator UI, re-purposing the OrderStatus component from @woocommerce/components. @@ -111,7 +111,7 @@ export const DepositOverview: React.FC< DepositOverviewProps > = ( { key="depositDate" label={ `${ depositDateLabel }: ` + - formatDateTime( moment.utc( deposit.date ).toISOString(), { + formatUserDateTime( moment.utc( deposit.date ).toISOString(), { useGmt: true, includeTime: false, } ) diff --git a/client/deposits/list/index.tsx b/client/deposits/list/index.tsx index a24eafb7e7a..cd948034acc 100644 --- a/client/deposits/list/index.tsx +++ b/client/deposits/list/index.tsx @@ -47,7 +47,7 @@ import { ReportingExportLanguageHook } from 'wcpay/settings/reporting-settings/i import './style.scss'; import { parseInt } from 'lodash'; -import { formatDateTime } from 'wcpay/utils/date-time'; +import { formatUserDateTime } from 'wcpay/utils/date-time'; const getColumns = ( sortByDate?: boolean ): DepositsTableHeader[] => [ { @@ -135,10 +135,13 @@ export const DepositsList = (): JSX.Element => { href={ getDetailsURL( deposit.id, 'deposits' ) } onClick={ () => recordEvent( 'wcpay_deposits_row_click' ) } > - { formatDateTime( moment.utc( deposit.date ).toISOString(), { - includeTime: false, - useGmt: true, - } ) } + { formatUserDateTime( + moment.utc( deposit.date ).toISOString(), + { + includeTime: false, + useGmt: true, + } + ) } ); @@ -327,7 +330,7 @@ export const DepositsList = (): JSX.Element => { row[ 0 ], { ...row[ 1 ], - value: formatDateTime( + value: formatUserDateTime( moment.utc( row[ 1 ].value ).toISOString(), { useGmt: true, includeTime: false } ), diff --git a/client/deposits/utils/index.ts b/client/deposits/utils/index.ts index 7ba4b807271..d75a5627509 100644 --- a/client/deposits/utils/index.ts +++ b/client/deposits/utils/index.ts @@ -3,7 +3,7 @@ */ import { __ } from '@wordpress/i18n'; import moment from 'moment'; -import { formatDateTime } from 'wcpay/utils/date-time'; +import { formatUserDateTime } from 'wcpay/utils/date-time'; interface DepositObject { date: number | string; @@ -11,7 +11,7 @@ interface DepositObject { export const getDepositDate = ( deposit?: DepositObject | null ): string => deposit - ? formatDateTime( moment.utc( deposit?.date ).toISOString(), { + ? formatUserDateTime( moment.utc( deposit?.date ).toISOString(), { includeTime: false, } ) : '—'; diff --git a/client/disputes/index.tsx b/client/disputes/index.tsx index 3b65b9cafec..5c9b90dbdd0 100644 --- a/client/disputes/index.tsx +++ b/client/disputes/index.tsx @@ -57,7 +57,7 @@ import CSVExportModal from 'components/csv-export-modal'; import { ReportingExportLanguageHook } from 'wcpay/settings/reporting-settings/interfaces'; import './style.scss'; -import { formatDateTime } from 'wcpay/utils/date-time'; +import { formatUserDateTime } from 'wcpay/utils/date-time'; const getHeaders = ( sortColumn?: string ): DisputesTableHeader[] => [ { @@ -201,9 +201,12 @@ const smartDueDate = ( dispute: CachedDispute ) => { ); } - return formatDateTime( moment.utc( dispute.due_by ).local().toISOString(), { - useGmt: false, - } ); + return formatUserDateTime( + moment.utc( dispute.due_by ).local().toISOString(), + { + useGmt: false, + } + ); }; export const DisputesList = (): JSX.Element => { @@ -300,7 +303,7 @@ export const DisputesList = (): JSX.Element => { created: { value: dispute.created, display: clickable( - formatDateTime( + formatUserDateTime( moment.utc( dispute.created ).local().toISOString(), { useGmt: false } ) @@ -482,7 +485,7 @@ export const DisputesList = (): JSX.Element => { { // Disputed On. ...row[ 10 ], - value: formatDateTime( + value: formatUserDateTime( moment.utc( row[ 10 ].value ).local().toISOString(), { useGmt: false, @@ -493,7 +496,7 @@ export const DisputesList = (): JSX.Element => { { // Respond by. ...row[ 11 ], - value: formatDateTime( + value: formatUserDateTime( moment.utc( row[ 11 ].value ).local().toISOString(), { useGmt: false } ), diff --git a/client/disputes/info/index.tsx b/client/disputes/info/index.tsx index 433a70fa1b9..7f530ff9cb6 100644 --- a/client/disputes/info/index.tsx +++ b/client/disputes/info/index.tsx @@ -19,7 +19,7 @@ import { formatExplicitCurrency } from 'multi-currency/interface/functions'; import './style.scss'; import Loadable from 'components/loadable'; import { Dispute } from 'wcpay/types/disputes'; -import { formatDateTime } from 'wcpay/utils/date-time'; +import { formatUserDateTime } from 'wcpay/utils/date-time'; const fields: { key: string; label: string }[] = [ { key: 'created', label: __( 'Dispute date', 'woocommerce-payments' ) }, @@ -69,7 +69,7 @@ const Info = ( { transactionId: __( 'Transaction link', 'woocommerce-payments' ), } : { - created: formatDateTime( + created: formatUserDateTime( moment( dispute.created * 1000 ).toISOString(), { includeTime: false, useGmt: false } ), @@ -78,7 +78,7 @@ const Info = ( { dispute.currency || 'USD' ), dueBy: dispute.evidence_details - ? formatDateTime( + ? formatUserDateTime( moment( dispute.evidence_details.due_by * 1000 ).toISOString(), diff --git a/client/disputes/test/index.tsx b/client/disputes/test/index.tsx index 911222230e6..cbedbe289df 100644 --- a/client/disputes/test/index.tsx +++ b/client/disputes/test/index.tsx @@ -24,7 +24,7 @@ import { DisputeReason, DisputeStatus, } from 'wcpay/types/disputes'; -import { formatDateTime } from 'wcpay/utils/date-time'; +import { formatUserDateTime } from 'wcpay/utils/date-time'; import moment from 'moment'; jest.mock( '@woocommerce/csv-export', () => { @@ -370,7 +370,7 @@ describe( 'Disputes list', () => { ); // customer expect( csvFirstDispute[ 11 ].replace( /^"|"$/g, '' ) ).toBe( - formatDateTime( + formatUserDateTime( moment .utc( mockDisputes[ 0 ].due_by ) .local() diff --git a/client/documents/list/index.tsx b/client/documents/list/index.tsx index 8104beb513a..daf505a2d59 100644 --- a/client/documents/list/index.tsx +++ b/client/documents/list/index.tsx @@ -20,7 +20,7 @@ import DocumentsFilters from '../filters'; import Page from '../../components/page'; import { getDocumentUrl } from 'wcpay/utils'; import VatFormModal from 'wcpay/vat/form-modal'; -import { formatDateTime } from 'wcpay/utils/date-time'; +import { formatUserDateTime } from 'wcpay/utils/date-time'; interface Column extends TableCardColumn { key: 'date' | 'type' | 'description' | 'download'; @@ -68,14 +68,14 @@ const getDocumentDescription = ( document: Document ) => { if ( document.period_from && document.period_to ) { return sprintf( __( 'VAT invoice for %s to %s', 'woocommerce-payments' ), - formatDateTime( + formatUserDateTime( moment .utc( document.period_from ) .local() .toISOString(), { useGmt: false, includeTime: false } ), - formatDateTime( + formatUserDateTime( moment.utc( document.period_to ).local().toISOString(), { useGmt: false, @@ -184,7 +184,7 @@ export const DocumentsList = (): JSX.Element => { const data = { date: { value: document.date, - display: formatDateTime( + display: formatUserDateTime( moment.utc( document.date ).local().toISOString(), { useGmt: false, diff --git a/client/overview/modal/update-business-details/index.tsx b/client/overview/modal/update-business-details/index.tsx index f3185ca8a04..94201394d87 100644 --- a/client/overview/modal/update-business-details/index.tsx +++ b/client/overview/modal/update-business-details/index.tsx @@ -12,7 +12,7 @@ import moment from 'moment'; import strings from './strings'; import './index.scss'; import { recordEvent } from 'wcpay/tracks'; -import { formatDateTime } from 'wcpay/utils/date-time'; +import { formatUserDateTime } from 'wcpay/utils/date-time'; interface Props { errorMessages: Array< string >; @@ -57,7 +57,7 @@ const UpdateBusinessDetailsModal = ( { currentDeadline ? sprintf( strings.restrictedSoonDescription, - formatDateTime( + formatUserDateTime( moment( currentDeadline * 1000 ).toISOString(), diff --git a/client/overview/task-list/tasks/dispute-task.tsx b/client/overview/task-list/tasks/dispute-task.tsx index f08c528e7d1..76823a141df 100644 --- a/client/overview/task-list/tasks/dispute-task.tsx +++ b/client/overview/task-list/tasks/dispute-task.tsx @@ -14,7 +14,7 @@ import { formatCurrency } from 'multi-currency/interface/functions'; import { getAdminUrl } from 'wcpay/utils'; import { recordEvent } from 'tracks'; import { isDueWithin } from 'wcpay/disputes/utils'; -import { formatDateTime } from 'wcpay/utils/date-time'; +import { formatUserDateTime } from 'wcpay/utils/date-time'; /** * Returns an array of disputes that are due within the specified number of days. @@ -142,7 +142,7 @@ export const getDisputeResolutionTask = ( ? sprintf( __( 'Respond today by %s', 'woocommerce-payments' ), // Show due_by time in local timezone: e.g. "11:59 PM". - formatDateTime( + formatUserDateTime( moment.utc( dispute.due_by ).local().toISOString(), { useGmt: false, @@ -156,7 +156,7 @@ export const getDisputeResolutionTask = ( 'woocommerce-payments' ), // Show due_by date in local timezone: e.g. "Jan 1, 2021". - formatDateTime( + formatUserDateTime( moment.utc( dispute.due_by ).local().toISOString(), { useGmt: false, diff --git a/client/overview/task-list/tasks/update-business-details-task.tsx b/client/overview/task-list/tasks/update-business-details-task.tsx index 9399951521b..2ca90519616 100644 --- a/client/overview/task-list/tasks/update-business-details-task.tsx +++ b/client/overview/task-list/tasks/update-business-details-task.tsx @@ -13,7 +13,7 @@ import type { TaskItemProps } from '../types'; import UpdateBusinessDetailsModal from 'wcpay/overview/modal/update-business-details'; import moment from 'moment'; import { recordEvent } from 'wcpay/tracks'; -import { formatDateTime } from 'wcpay/utils/date-time'; +import { formatUserDateTime } from 'wcpay/utils/date-time'; export const getUpdateBusinessDetailsTask = ( errorMessages: string[], @@ -46,9 +46,12 @@ export const getUpdateBusinessDetailsTask = ( 'Update by %s to avoid a disruption in deposits.', 'woocommerce-payments' ), - formatDateTime( moment( currentDeadline * 1000 ).toISOString(), { - customFormat: 'ga M j, Y', - } ) + formatUserDateTime( + moment( currentDeadline * 1000 ).toISOString(), + { + customFormat: 'ga M j, Y', + } + ) ); if ( hasSingleError ) { diff --git a/client/payment-details/dispute-details/dispute-due-by-date.tsx b/client/payment-details/dispute-details/dispute-due-by-date.tsx index 6aaaa571831..697485c9d63 100644 --- a/client/payment-details/dispute-details/dispute-due-by-date.tsx +++ b/client/payment-details/dispute-details/dispute-due-by-date.tsx @@ -5,7 +5,7 @@ import React from 'react'; import { __, _n, sprintf } from '@wordpress/i18n'; import classNames from 'classnames'; import moment from 'moment'; -import { formatDateTime } from 'wcpay/utils/date-time'; +import { formatUserDateTime } from 'wcpay/utils/date-time'; const DisputeDueByDate: React.FC< { dueBy: number; @@ -14,7 +14,7 @@ const DisputeDueByDate: React.FC< { const daysRemaining = Math.floor( moment.unix( dueBy ).diff( moment(), 'days', true ) ); - const respondByDate = formatDateTime( + const respondByDate = formatUserDateTime( moment( dueBy * 1000 ).toISOString(), { separator: ', ', diff --git a/client/payment-details/dispute-details/dispute-resolution-footer.tsx b/client/payment-details/dispute-details/dispute-resolution-footer.tsx index 8364d85a0a7..e6b38213abc 100644 --- a/client/payment-details/dispute-details/dispute-resolution-footer.tsx +++ b/client/payment-details/dispute-details/dispute-resolution-footer.tsx @@ -16,13 +16,13 @@ import { recordEvent } from 'tracks'; import { getAdminUrl } from 'wcpay/utils'; import { getDisputeFeeFormatted } from 'wcpay/disputes/utils'; import './style.scss'; -import { formatDateTime } from 'wcpay/utils/date-time'; +import { formatUserDateTime } from 'wcpay/utils/date-time'; const DisputeUnderReviewFooter: React.FC< { dispute: Pick< Dispute, 'id' | 'metadata' | 'status' >; } > = ( { dispute } ) => { const submissionDateFormatted = dispute.metadata.__evidence_submitted_at - ? formatDateTime( + ? formatUserDateTime( moment .unix( parseInt( dispute.metadata.__evidence_submitted_at, 10 ) @@ -92,7 +92,7 @@ const DisputeWonFooter: React.FC< { dispute: Pick< Dispute, 'id' | 'metadata' | 'status' >; } > = ( { dispute } ) => { const closedDateFormatted = dispute.metadata.__dispute_closed_at - ? formatDateTime( + ? formatUserDateTime( moment .unix( parseInt( dispute.metadata.__dispute_closed_at, 10 ) @@ -169,7 +169,7 @@ const DisputeLostFooter: React.FC< { const disputeFeeFormatted = getDisputeFeeFormatted( dispute, true ) ?? '-'; const closedDateFormatted = dispute.metadata.__dispute_closed_at - ? formatDateTime( + ? formatUserDateTime( moment .unix( parseInt( dispute.metadata.__dispute_closed_at, 10 ) @@ -272,7 +272,7 @@ const InquiryUnderReviewFooter: React.FC< { dispute: Pick< Dispute, 'id' | 'metadata' | 'status' >; } > = ( { dispute } ) => { const submissionDateFormatted = dispute.metadata.__evidence_submitted_at - ? formatDateTime( + ? formatUserDateTime( moment .unix( parseInt( dispute.metadata.__evidence_submitted_at, 10 ) @@ -344,7 +344,7 @@ const InquiryClosedFooter: React.FC< { } > = ( { dispute } ) => { const isSubmitted = !! dispute.metadata.__evidence_submitted_at; const closedDateFormatted = dispute.metadata.__dispute_closed_at - ? formatDateTime( + ? formatUserDateTime( moment .unix( parseInt( dispute.metadata.__dispute_closed_at, 10 ) diff --git a/client/payment-details/dispute-details/dispute-steps.tsx b/client/payment-details/dispute-details/dispute-steps.tsx index 9053e1d6bce..2fce833c4ec 100644 --- a/client/payment-details/dispute-details/dispute-steps.tsx +++ b/client/payment-details/dispute-details/dispute-steps.tsx @@ -19,7 +19,7 @@ import { formatExplicitCurrency } from 'multi-currency/interface/functions'; import { ClickTooltip } from 'wcpay/components/tooltip'; import { getDisputeFeeFormatted } from 'wcpay/disputes/utils'; import DisputeDueByDate from './dispute-due-by-date'; -import { formatDateTime } from 'wcpay/utils/date-time'; +import { formatUserDateTime } from 'wcpay/utils/date-time'; interface Props { dispute: Dispute; @@ -34,13 +34,13 @@ export const DisputeSteps: React.FC< Props > = ( { } ) => { let emailLink; if ( customer?.email ) { - const chargeDate = formatDateTime( + const chargeDate = formatUserDateTime( moment( chargeCreated * 1000 ).toISOString(), { includeTime: false, } ); - const disputeDate = formatDateTime( + const disputeDate = formatUserDateTime( moment( dispute.created * 1000 ).toISOString(), { includeTime: false } ); @@ -175,10 +175,10 @@ export const InquirySteps: React.FC< Props > = ( { } ) => { let emailLink; if ( customer?.email ) { - const chargeDate = formatDateTime( + const chargeDate = formatUserDateTime( moment( chargeCreated * 1000 ).toISOString() ); - const disputeDate = formatDateTime( + const disputeDate = formatUserDateTime( moment( dispute.created * 1000 ).toISOString() ); const emailSubject = sprintf( diff --git a/client/payment-details/dispute-details/dispute-summary-row.tsx b/client/payment-details/dispute-details/dispute-summary-row.tsx index bf575485125..72a71ff3491 100644 --- a/client/payment-details/dispute-details/dispute-summary-row.tsx +++ b/client/payment-details/dispute-details/dispute-summary-row.tsx @@ -19,7 +19,7 @@ import { formatStringValue } from 'wcpay/utils'; import { ClickTooltip } from 'wcpay/components/tooltip'; import Paragraphs from 'wcpay/components/paragraphs'; import DisputeDueByDate from './dispute-due-by-date'; -import { formatDateTime } from 'wcpay/utils/date-time'; +import { formatUserDateTime } from 'wcpay/utils/date-time'; interface Props { dispute: Dispute; @@ -39,7 +39,7 @@ const DisputeSummaryRow: React.FC< Props > = ( { dispute } ) => { { title: __( 'Disputed On', 'woocommerce-payments' ), content: dispute.created - ? formatDateTime( + ? formatUserDateTime( moment( dispute.created * 1000 ).toISOString(), { useGmt: false, separator: ', ' } ) diff --git a/client/payment-details/summary/index.tsx b/client/payment-details/summary/index.tsx index f178a42604e..92b994a3ccb 100644 --- a/client/payment-details/summary/index.tsx +++ b/client/payment-details/summary/index.tsx @@ -63,7 +63,7 @@ import DisputeResolutionFooter from '../dispute-details/dispute-resolution-foote import ErrorBoundary from 'components/error-boundary'; import RefundModal from 'wcpay/payment-details/summary/refund-modal'; import CardNotice from 'wcpay/components/card-notice'; -import { formatDateTime } from 'wcpay/utils/date-time'; +import { formatUserDateTime } from 'wcpay/utils/date-time'; declare const window: any; @@ -110,7 +110,7 @@ const composePaymentSummaryItems = ( { { title: __( 'Date', 'woocommerce-payments' ), content: charge.created - ? formatDateTime( + ? formatUserDateTime( moment( charge.created * 1000 ).toISOString(), { customFormat: 'M j, Y, g:ia', @@ -709,7 +709,7 @@ const PaymentDetailsSummary: React.FC< PaymentDetailsSummaryProps > = ( { } ) }{ ' ' } { 'woocommerce-payments' ), formattedAmount, - formatDateTime( + formatUserDateTime( moment( event.deposit.arrival_date * 1000 ).toISOString(), { includeTime: false } ) diff --git a/client/transactions/blocked/columns.tsx b/client/transactions/blocked/columns.tsx index 13fdfc77531..5eb03e629a7 100644 --- a/client/transactions/blocked/columns.tsx +++ b/client/transactions/blocked/columns.tsx @@ -14,7 +14,7 @@ import TransactionStatusPill from 'wcpay/components/transaction-status-pill'; import { FraudOutcomeTransaction } from '../../data'; import { getDetailsURL } from '../../components/details-link'; import ClickableCell from '../../components/clickable-cell'; -import { formatDateTime } from 'wcpay/utils/date-time'; +import { formatUserDateTime } from 'wcpay/utils/date-time'; interface Column extends TableCardColumn { key: 'created' | 'amount' | 'customer' | 'status'; @@ -70,7 +70,7 @@ export const getBlockedListRowContent = ( data.payment_intent.id || data.order_id.toString(), 'transactions' ); - const formattedCreatedDate = formatDateTime( + const formattedCreatedDate = formatUserDateTime( moment.utc( data.created ).local().toISOString(), { useGmt: false } ); diff --git a/client/transactions/list/deposit.tsx b/client/transactions/list/deposit.tsx index c4413c9c372..ff032aaab66 100644 --- a/client/transactions/list/deposit.tsx +++ b/client/transactions/list/deposit.tsx @@ -16,7 +16,7 @@ import InfoOutlineIcon from 'gridicons/dist/info-outline'; */ import { getAdminUrl } from 'utils'; import { ClickTooltip } from 'components/tooltip'; -import { formatDateTime } from 'wcpay/utils/date-time'; +import { formatUserDateTime } from 'wcpay/utils/date-time'; interface DepositProps { depositId?: string; @@ -31,7 +31,7 @@ const Deposit: React.FC< DepositProps > = ( { depositId, dateAvailable } ) => { id: depositId, } ); - const formattedDateAvailable = formatDateTime( + const formattedDateAvailable = formatUserDateTime( moment.utc( dateAvailable ).toISOString(), { includeTime: false, diff --git a/client/transactions/list/index.tsx b/client/transactions/list/index.tsx index d5f1bbd1546..06122a8e1bf 100644 --- a/client/transactions/list/index.tsx +++ b/client/transactions/list/index.tsx @@ -69,7 +69,7 @@ import p24BankList from '../../payment-details/payment-method/p24/bank-list'; import { HoverTooltip } from 'components/tooltip'; import { PAYMENT_METHOD_TITLES } from 'wcpay/constants/payment-method'; import { ReportingExportLanguageHook } from 'wcpay/settings/reporting-settings/interfaces'; -import { formatDateTime } from 'wcpay/utils/date-time'; +import { formatUserDateTime } from 'wcpay/utils/date-time'; interface TransactionsListProps { depositId?: string; @@ -466,7 +466,7 @@ export const TransactionsList = ( date: { value: txn.date, display: clickable( - formatDateTime( + formatUserDateTime( moment.utc( txn.date ).local().toISOString(), { useGmt: false } ) diff --git a/client/transactions/risk-review/columns.tsx b/client/transactions/risk-review/columns.tsx index f098b8d02e8..34db01a1de3 100644 --- a/client/transactions/risk-review/columns.tsx +++ b/client/transactions/risk-review/columns.tsx @@ -16,7 +16,7 @@ import { formatExplicitCurrency } from 'multi-currency/interface/functions'; import { recordEvent } from 'tracks'; import TransactionStatusPill from 'wcpay/components/transaction-status-pill'; import { FraudOutcomeTransaction } from '../../data'; -import { formatDateTime } from 'wcpay/utils/date-time'; +import { formatUserDateTime } from 'wcpay/utils/date-time'; interface Column extends TableCardColumn { key: 'created' | 'amount' | 'customer' | 'status'; @@ -76,7 +76,7 @@ export const getRiskReviewListRowContent = ( data: FraudOutcomeTransaction ): Record< string, TableCardBodyColumn > => { const detailsURL = getDetailsURL( data.payment_intent.id, 'transactions' ); - const formattedCreatedDate = formatDateTime( + const formattedCreatedDate = formatUserDateTime( moment.utc( data.created ).local().toISOString(), { useGmt: false } ); diff --git a/client/transactions/uncaptured/index.tsx b/client/transactions/uncaptured/index.tsx index bbf687393d9..7da7af732f3 100644 --- a/client/transactions/uncaptured/index.tsx +++ b/client/transactions/uncaptured/index.tsx @@ -20,7 +20,7 @@ import { formatExplicitCurrency } from 'multi-currency/interface/functions'; import RiskLevel, { calculateRiskMapping } from 'components/risk-level'; import { recordEvent } from 'tracks'; import CaptureAuthorizationButton from 'wcpay/components/capture-authorization-button'; -import { formatDateTime } from 'wcpay/utils/date-time'; +import { formatUserDateTime } from 'wcpay/utils/date-time'; interface Column extends TableCardColumn { key: @@ -130,12 +130,12 @@ export const AuthorizationsList = (): JSX.Element => { display: auth.payment_intent_id, }, created: { - value: formatDateTime( + value: formatUserDateTime( moment.utc( auth.created ).local().toISOString(), { useGmt: false } ), display: clickable( - formatDateTime( + formatUserDateTime( moment.utc( auth.created ).local().toISOString(), { useGmt: false } ) @@ -143,7 +143,7 @@ export const AuthorizationsList = (): JSX.Element => { }, // Payments are authorized for a maximum of 7 days capture_by: { - value: formatDateTime( + value: formatUserDateTime( moment .utc( auth.created ) .add( 7, 'd' ) @@ -152,7 +152,7 @@ export const AuthorizationsList = (): JSX.Element => { { useGmt: false } ), display: clickable( - formatDateTime( + formatUserDateTime( moment .utc( auth.created ) .add( 7, 'd' ) diff --git a/client/utils/date-time.ts b/client/utils/date-time.ts index ef7bd6ed977..a6c53db2b1e 100644 --- a/client/utils/date-time.ts +++ b/client/utils/date-time.ts @@ -19,7 +19,7 @@ interface FormatDateTimeOptions { * @param { FormatDateTimeOptions } options - Additional options to control time inclusion and whether to use GMT/UTC. * @return { string } - The formatted date and time string. */ -export function formatDateTime( +export function formatUserDateTime( dateTime: string | Date, options: FormatDateTimeOptions = { includeTime: true, diff --git a/client/utils/test/date-time.test.ts b/client/utils/test/date-time.test.ts index 066ec6a77a4..d22a126a9bd 100644 --- a/client/utils/test/date-time.test.ts +++ b/client/utils/test/date-time.test.ts @@ -5,7 +5,7 @@ import { dateI18n } from '@wordpress/date'; /** * Internal dependencies */ -import { formatDateTime } from '../date-time'; +import { formatUserDateTime } from '../date-time'; jest.mock( '@wordpress/date', () => ( { dateI18n: jest.fn(), @@ -28,7 +28,7 @@ describe( 'formatDateTime', () => { it( 'should format date and time using default WordPress settings', () => { const dateTime = '2024-10-23 15:28:26'; - formatDateTime( dateTime ); + formatUserDateTime( dateTime ); expect( dateI18n ).toHaveBeenCalledWith( `${ mockWcpaySettings.dateFormat } / ${ mockWcpaySettings.timeFormat }`, @@ -40,7 +40,7 @@ describe( 'formatDateTime', () => { it( 'should use custom format if provided', () => { const dateTime = '2024-10-23 15:28:26'; const options = { customFormat: 'd-m-Y H:i:s' }; - formatDateTime( dateTime, options ); + formatUserDateTime( dateTime, options ); expect( dateI18n ).toHaveBeenCalledWith( 'd-m-Y H:i:s', @@ -52,7 +52,7 @@ describe( 'formatDateTime', () => { it( 'should exclude time if includeTime is set to false', () => { const dateTime = '2024-10-23 15:28:26'; const options = { includeTime: false }; - formatDateTime( dateTime, options ); + formatUserDateTime( dateTime, options ); expect( dateI18n ).toHaveBeenCalledWith( mockWcpaySettings.dateFormat, @@ -64,7 +64,7 @@ describe( 'formatDateTime', () => { it( 'should handle GMT/UTC setting correctly', () => { const dateTime = '2024-10-23 15:28:26'; const options = { useGmt: false }; - formatDateTime( dateTime, options ); + formatUserDateTime( dateTime, options ); expect( dateI18n ).toHaveBeenCalledWith( `${ mockWcpaySettings.dateFormat } / ${ mockWcpaySettings.timeFormat }`, @@ -76,7 +76,7 @@ describe( 'formatDateTime', () => { it( 'should use custom separator when provided', () => { const dateTime = '2024-10-23 15:28:26'; const options = { separator: ' - ' }; - formatDateTime( dateTime, options ); + formatUserDateTime( dateTime, options ); expect( dateI18n ).toHaveBeenCalledWith( `${ mockWcpaySettings.dateFormat } - ${ mockWcpaySettings.timeFormat }`, diff --git a/multi-currency/client/settings/single-currency/index.js b/multi-currency/client/settings/single-currency/index.js index 14bab4b480d..fc063ce3211 100644 --- a/multi-currency/client/settings/single-currency/index.js +++ b/multi-currency/client/settings/single-currency/index.js @@ -30,7 +30,7 @@ import { SettingsLayout, SettingsSection, } from 'multi-currency/interface/components'; -import { formatDateTime } from 'wcpay/utils/date-time'; +import { formatUserDateTime } from 'wcpay/utils/date-time'; const SingleCurrencySettings = () => { const { @@ -104,7 +104,7 @@ const SingleCurrencySettings = () => { }, [ currencySettings, currency, initialPriceRoundingType ] ); const formattedLastUpdatedDateTime = targetCurrency - ? formatDateTime( + ? formatUserDateTime( moment.unix( targetCurrency.last_updated ).toISOString(), { useGmt: false, separator: ' ' } ) From 58af60b53f09af04d47511680eb9662db11e5a5b Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Tue, 29 Oct 2024 12:08:37 +0100 Subject: [PATCH 38/76] Tweak comments --- client/utils/date-time.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/utils/date-time.ts b/client/utils/date-time.ts index a6c53db2b1e..721ccd2781b 100644 --- a/client/utils/date-time.ts +++ b/client/utils/date-time.ts @@ -8,14 +8,14 @@ type DateTimeFormat = string | null; interface FormatDateTimeOptions { includeTime?: boolean; // Whether to include time in the formatted string (defaults to true) useGmt?: boolean; // Whether to display the time in GMT/UTC (defaults to false) - separator?: string; // Separator between date and time (defaults to '/') + separator?: string; // Separator between date and time (defaults to ' / ') customFormat?: DateTimeFormat; // Custom format to use instead of WordPress settings } /** * Formats a date and time string according to WordPress settings or a custom format. * - * @param { string } dateTime - The date and time string or date from the database in UTC (e.g., '2024-10-23 15:28:26'). + * @param { string | Date } dateTime - The date and time string (e.g., '2024-10-23 15:28:26') or a JS Date object. * @param { FormatDateTimeOptions } options - Additional options to control time inclusion and whether to use GMT/UTC. * @return { string } - The formatted date and time string. */ From b716201e738b8b3f7481b4f40d66f57f44c723ec Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Tue, 29 Oct 2024 12:36:50 +0100 Subject: [PATCH 39/76] Refactor tests --- client/utils/test/date-time.test.ts | 143 +++++++++++++++++----------- 1 file changed, 86 insertions(+), 57 deletions(-) diff --git a/client/utils/test/date-time.test.ts b/client/utils/test/date-time.test.ts index d22a126a9bd..7ca342dc5e8 100644 --- a/client/utils/test/date-time.test.ts +++ b/client/utils/test/date-time.test.ts @@ -1,17 +1,10 @@ -/** - * External dependencies - */ -import { dateI18n } from '@wordpress/date'; /** * Internal dependencies */ -import { formatUserDateTime } from '../date-time'; - -jest.mock( '@wordpress/date', () => ( { - dateI18n: jest.fn(), -} ) ); +import { formatUserDateTime } from 'wcpay/utils/date-time'; +import { dateI18n } from '@wordpress/date'; -describe( 'formatDateTime', () => { +describe( 'formatUserDateTime', () => { const originalWcpaySettings = window.wcpaySettings; const mockWcpaySettings = { dateFormat: 'Y-m-d', @@ -26,62 +19,98 @@ describe( 'formatDateTime', () => { window.wcpaySettings = originalWcpaySettings; } ); - it( 'should format date and time using default WordPress settings', () => { - const dateTime = '2024-10-23 15:28:26'; - formatUserDateTime( dateTime ); + describe( 'with string input', () => { + it( 'should format using default WordPress settings', () => { + const dateTime = '2024-10-23 15:28:26'; + const formatted = formatUserDateTime( dateTime, { useGmt: false } ); - expect( dateI18n ).toHaveBeenCalledWith( - `${ mockWcpaySettings.dateFormat } / ${ mockWcpaySettings.timeFormat }`, - dateTime, - true - ); - } ); + expect( formatted ).toBe( '2024-10-23 / 15:28' ); + } ); - it( 'should use custom format if provided', () => { - const dateTime = '2024-10-23 15:28:26'; - const options = { customFormat: 'd-m-Y H:i:s' }; - formatUserDateTime( dateTime, options ); + it( 'should use custom format if provided', () => { + const dateTime = '2024-10-23 15:28:26'; + const options = { customFormat: 'd-m-Y H:i:s', useGmt: false }; + const formatted = formatUserDateTime( dateTime, options ); - expect( dateI18n ).toHaveBeenCalledWith( - 'd-m-Y H:i:s', - dateTime, - true - ); - } ); + expect( formatted ).toBe( '23-10-2024 15:28:26' ); + } ); - it( 'should exclude time if includeTime is set to false', () => { - const dateTime = '2024-10-23 15:28:26'; - const options = { includeTime: false }; - formatUserDateTime( dateTime, options ); + it( 'should exclude time if includeTime is set to false', () => { + const dateTime = '2024-10-23 15:28:26'; + const options = { includeTime: false, useGmt: false }; + const formatted = formatUserDateTime( dateTime, options ); - expect( dateI18n ).toHaveBeenCalledWith( - mockWcpaySettings.dateFormat, - dateTime, - true - ); - } ); + expect( formatted ).toBe( '2024-10-23' ); + } ); + + it( 'should use custom separator when provided', () => { + const dateTime = '2024-10-23 15:28:26'; + const options = { separator: ' - ', useGmt: false }; + const formatted = formatUserDateTime( dateTime, options ); + + // Expect output with custom separator + expect( formatted ).toBe( '2024-10-23 - 15:28' ); + } ); - it( 'should handle GMT/UTC setting correctly', () => { - const dateTime = '2024-10-23 15:28:26'; - const options = { useGmt: false }; - formatUserDateTime( dateTime, options ); + it( 'should handle GMT/UTC setting correctly when useGmt is true', () => { + const dateTime = '2024-10-23 15:28:26Z'; + const options = { useGmt: true }; // Enforcing UTC + const formatted = formatUserDateTime( dateTime, options ); - expect( dateI18n ).toHaveBeenCalledWith( - `${ mockWcpaySettings.dateFormat } / ${ mockWcpaySettings.timeFormat }`, - dateTime, - false - ); + // Expect UTC-based output (no timezone adjustment) + expect( formatted ).toBe( '2024-10-23 / 15:28' ); + } ); } ); - it( 'should use custom separator when provided', () => { - const dateTime = '2024-10-23 15:28:26'; - const options = { separator: ' - ' }; - formatUserDateTime( dateTime, options ); + describe( 'with Date object input', () => { + it( 'should format using default WordPress settings', () => { + const dateTime = new Date( Date.UTC( 2024, 9, 23, 15, 28, 26 ) ); + const formatted = formatUserDateTime( dateTime, { useGmt: true } ); + + // Expected format based on WordPress settings and Date object + expect( formatted ).toBe( '2024-10-23 / 15:28' ); + } ); + + it( 'should use custom format if provided', () => { + const dateTime = new Date( Date.UTC( 2024, 9, 23, 15, 28, 26 ) ); + const options = { customFormat: 'd-m-Y H:i:s', useGmt: true }; + const formatted = formatUserDateTime( dateTime, options ); + + // Expected format for Date object: '23-10-2024 15:28:26' + expect( formatted ).toBe( '23-10-2024 15:28:26' ); + } ); + + it( 'should exclude time if includeTime is set to false', () => { + const dateTime = new Date( Date.UTC( 2024, 9, 23, 15, 28, 26 ) ); + const options = { includeTime: false, useGmt: true }; + const formatted = formatUserDateTime( dateTime, options ); + + // Expect output with date only + expect( formatted ).toBe( '2024-10-23' ); + } ); + + it( 'should handle GMT/UTC setting correctly', () => { + const dateTime = new Date( 2024, 9, 23, 15, 28, 26 ); // Local time (non-UTC) + const options = { useGmt: false }; // Using local timezone + const formatted = formatUserDateTime( dateTime, options ); + + // The expected format will vary based on your local timezone + const expectedFormat = dateI18n( + `${ mockWcpaySettings.dateFormat } / ${ mockWcpaySettings.timeFormat }`, + dateTime, + false + ); + + expect( formatted ).toBe( expectedFormat ); + } ); + + it( 'should use custom separator when provided', () => { + const dateTime = new Date( Date.UTC( 2024, 9, 23, 15, 28, 26 ) ); + const options = { separator: ' - ', useGmt: true }; + const formatted = formatUserDateTime( dateTime, options ); - expect( dateI18n ).toHaveBeenCalledWith( - `${ mockWcpaySettings.dateFormat } - ${ mockWcpaySettings.timeFormat }`, - dateTime, - true - ); + // Expect output with custom separator + expect( formatted ).toBe( '2024-10-23 - 15:28' ); + } ); } ); } ); From efe28cf525361b584fb6f439341b253ad18cd8ed Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Tue, 29 Oct 2024 14:03:27 +0100 Subject: [PATCH 40/76] Refactor to default to local time (useGmt: false) --- client/components/active-loan-summary/index.tsx | 6 +++--- client/deposits/utils/index.ts | 1 + client/transactions/list/index.tsx | 5 +---- client/utils/date-time.ts | 4 ++-- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/client/components/active-loan-summary/index.tsx b/client/components/active-loan-summary/index.tsx index a403c2e5eeb..af817c7331d 100755 --- a/client/components/active-loan-summary/index.tsx +++ b/client/components/active-loan-summary/index.tsx @@ -215,7 +215,7 @@ const ActiveLoanSummary = (): JSX.Element => { details.current_repayment_interval.due_at * 1000 ), - { includeTime: false } + { includeTime: false, useGmt: true } ) ) } > @@ -253,7 +253,7 @@ const ActiveLoanSummary = (): JSX.Element => { > { formatUserDateTime( new Date( details.advance_paid_out_at * 1000 ), - { includeTime: false } + { includeTime: false, useGmt: true } ) } { > { formatUserDateTime( new Date( details.repayments_begin_at * 1000 ), - { includeTime: false } + { includeTime: false, useGmt: true } ) } diff --git a/client/deposits/utils/index.ts b/client/deposits/utils/index.ts index d75a5627509..a3bf14e82a3 100644 --- a/client/deposits/utils/index.ts +++ b/client/deposits/utils/index.ts @@ -13,6 +13,7 @@ export const getDepositDate = ( deposit?: DepositObject | null ): string => deposit ? formatUserDateTime( moment.utc( deposit?.date ).toISOString(), { includeTime: false, + useGmt: true, } ) : '—'; diff --git a/client/transactions/list/index.tsx b/client/transactions/list/index.tsx index 06122a8e1bf..7ff37084ad3 100644 --- a/client/transactions/list/index.tsx +++ b/client/transactions/list/index.tsx @@ -466,10 +466,7 @@ export const TransactionsList = ( date: { value: txn.date, display: clickable( - formatUserDateTime( - moment.utc( txn.date ).local().toISOString(), - { useGmt: false } - ) + formatUserDateTime( moment.utc( txn.date ).toISOString() ) ), }, channel: { diff --git a/client/utils/date-time.ts b/client/utils/date-time.ts index 721ccd2781b..61ae1a046a0 100644 --- a/client/utils/date-time.ts +++ b/client/utils/date-time.ts @@ -23,7 +23,7 @@ export function formatUserDateTime( dateTime: string | Date, options: FormatDateTimeOptions = { includeTime: true, - useGmt: true, + useGmt: false, separator: ' / ', customFormat: null, } @@ -31,7 +31,7 @@ export function formatUserDateTime( const { customFormat = null, includeTime = true, - useGmt = true, + useGmt = false, separator = ' / ', } = options; From 655e814b4817519a754ba0a8b3b0c36981603833 Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Tue, 29 Oct 2024 15:29:16 +0100 Subject: [PATCH 41/76] Remove default option from function call --- client/disputes/index.tsx | 16 ++++----------- client/disputes/info/index.tsx | 4 ++-- client/disputes/test/index.tsx | 6 +----- client/documents/list/index.tsx | 13 ++++-------- .../modal/update-business-details/index.tsx | 9 +++++---- .../overview/task-list/tasks/dispute-task.tsx | 6 ++---- .../dispute-details/dispute-summary-row.tsx | 2 +- client/payment-details/summary/index.tsx | 1 - client/transactions/blocked/columns.tsx | 3 +-- client/transactions/risk-review/columns.tsx | 3 +-- client/transactions/uncaptured/index.tsx | 20 ++++--------------- client/utils/test/date-time.test.ts | 11 +++++----- 12 files changed, 30 insertions(+), 64 deletions(-) diff --git a/client/disputes/index.tsx b/client/disputes/index.tsx index 5c9b90dbdd0..11515987b58 100644 --- a/client/disputes/index.tsx +++ b/client/disputes/index.tsx @@ -201,12 +201,7 @@ const smartDueDate = ( dispute: CachedDispute ) => { ); } - return formatUserDateTime( - moment.utc( dispute.due_by ).local().toISOString(), - { - useGmt: false, - } - ); + return formatUserDateTime( moment.utc( dispute.due_by ).toISOString() ); }; export const DisputesList = (): JSX.Element => { @@ -304,8 +299,7 @@ export const DisputesList = (): JSX.Element => { value: dispute.created, display: clickable( formatUserDateTime( - moment.utc( dispute.created ).local().toISOString(), - { useGmt: false } + moment.utc( dispute.created ).toISOString() ) ), }, @@ -486,9 +480,8 @@ export const DisputesList = (): JSX.Element => { // Disputed On. ...row[ 10 ], value: formatUserDateTime( - moment.utc( row[ 10 ].value ).local().toISOString(), + moment.utc( row[ 10 ].value ).toISOString(), { - useGmt: false, includeTime: false, } ), @@ -497,8 +490,7 @@ export const DisputesList = (): JSX.Element => { // Respond by. ...row[ 11 ], value: formatUserDateTime( - moment.utc( row[ 11 ].value ).local().toISOString(), - { useGmt: false } + moment.utc( row[ 11 ].value ).toISOString() ), }, ]; diff --git a/client/disputes/info/index.tsx b/client/disputes/info/index.tsx index 7f530ff9cb6..a6f6ecaad06 100644 --- a/client/disputes/info/index.tsx +++ b/client/disputes/info/index.tsx @@ -71,7 +71,7 @@ const Info = ( { : { created: formatUserDateTime( moment( dispute.created * 1000 ).toISOString(), - { includeTime: false, useGmt: false } + { includeTime: false } ), amount: formatExplicitCurrency( dispute.amount || 0, @@ -82,7 +82,7 @@ const Info = ( { moment( dispute.evidence_details.due_by * 1000 ).toISOString(), - { separator: ' - ', useGmt: false } + { separator: ' - ' } ) : null, reason: composeDisputeReason( dispute ), diff --git a/client/disputes/test/index.tsx b/client/disputes/test/index.tsx index cbedbe289df..52af84da080 100644 --- a/client/disputes/test/index.tsx +++ b/client/disputes/test/index.tsx @@ -371,11 +371,7 @@ describe( 'Disputes list', () => { expect( csvFirstDispute[ 11 ].replace( /^"|"$/g, '' ) ).toBe( formatUserDateTime( - moment - .utc( mockDisputes[ 0 ].due_by ) - .local() - .toISOString(), - { useGmt: false } + moment.utc( mockDisputes[ 0 ].due_by ).toISOString() ) ); // date respond by } ); diff --git a/client/documents/list/index.tsx b/client/documents/list/index.tsx index daf505a2d59..a6aacd9c4a2 100644 --- a/client/documents/list/index.tsx +++ b/client/documents/list/index.tsx @@ -69,16 +69,12 @@ const getDocumentDescription = ( document: Document ) => { return sprintf( __( 'VAT invoice for %s to %s', 'woocommerce-payments' ), formatUserDateTime( - moment - .utc( document.period_from ) - .local() - .toISOString(), - { useGmt: false, includeTime: false } + moment.utc( document.period_from ).toISOString(), + { includeTime: false } ), formatUserDateTime( - moment.utc( document.period_to ).local().toISOString(), + moment.utc( document.period_to ).toISOString(), { - useGmt: false, includeTime: false, } ) @@ -185,9 +181,8 @@ export const DocumentsList = (): JSX.Element => { date: { value: document.date, display: formatUserDateTime( - moment.utc( document.date ).local().toISOString(), + moment.utc( document.date ).toISOString(), { - useGmt: false, includeTime: false, } ), diff --git a/client/overview/modal/update-business-details/index.tsx b/client/overview/modal/update-business-details/index.tsx index 94201394d87..fb98eca74f3 100644 --- a/client/overview/modal/update-business-details/index.tsx +++ b/client/overview/modal/update-business-details/index.tsx @@ -58,11 +58,12 @@ const UpdateBusinessDetailsModal = ( { ? sprintf( strings.restrictedSoonDescription, formatUserDateTime( - moment( - currentDeadline * 1000 - ).toISOString(), + moment + .utc( + currentDeadline * 1000 + ) + .toISOString(), { - useGmt: false, customFormat: 'ga M j, Y', } ) diff --git a/client/overview/task-list/tasks/dispute-task.tsx b/client/overview/task-list/tasks/dispute-task.tsx index 76823a141df..f7be6ec8e13 100644 --- a/client/overview/task-list/tasks/dispute-task.tsx +++ b/client/overview/task-list/tasks/dispute-task.tsx @@ -143,9 +143,8 @@ export const getDisputeResolutionTask = ( __( 'Respond today by %s', 'woocommerce-payments' ), // Show due_by time in local timezone: e.g. "11:59 PM". formatUserDateTime( - moment.utc( dispute.due_by ).local().toISOString(), + moment.utc( dispute.due_by ).toISOString(), { - useGmt: false, customFormat: 'g:i A', } ) @@ -157,9 +156,8 @@ export const getDisputeResolutionTask = ( ), // Show due_by date in local timezone: e.g. "Jan 1, 2021". formatUserDateTime( - moment.utc( dispute.due_by ).local().toISOString(), + moment.utc( dispute.due_by ).toISOString(), { - useGmt: false, includeTime: false, } ), diff --git a/client/payment-details/dispute-details/dispute-summary-row.tsx b/client/payment-details/dispute-details/dispute-summary-row.tsx index 72a71ff3491..fbe7371fd56 100644 --- a/client/payment-details/dispute-details/dispute-summary-row.tsx +++ b/client/payment-details/dispute-details/dispute-summary-row.tsx @@ -41,7 +41,7 @@ const DisputeSummaryRow: React.FC< Props > = ( { dispute } ) => { content: dispute.created ? formatUserDateTime( moment( dispute.created * 1000 ).toISOString(), - { useGmt: false, separator: ', ' } + { separator: ', ' } ) : '–', }, diff --git a/client/payment-details/summary/index.tsx b/client/payment-details/summary/index.tsx index 92b994a3ccb..d7b1790d23d 100644 --- a/client/payment-details/summary/index.tsx +++ b/client/payment-details/summary/index.tsx @@ -114,7 +114,6 @@ const composePaymentSummaryItems = ( { moment( charge.created * 1000 ).toISOString(), { customFormat: 'M j, Y, g:ia', - useGmt: false, } ) : '–', diff --git a/client/transactions/blocked/columns.tsx b/client/transactions/blocked/columns.tsx index 5eb03e629a7..c1e6cedd386 100644 --- a/client/transactions/blocked/columns.tsx +++ b/client/transactions/blocked/columns.tsx @@ -71,8 +71,7 @@ export const getBlockedListRowContent = ( 'transactions' ); const formattedCreatedDate = formatUserDateTime( - moment.utc( data.created ).local().toISOString(), - { useGmt: false } + moment.utc( data.created ).toISOString() ); const clickable = ( children: JSX.Element | string ) => ( diff --git a/client/transactions/risk-review/columns.tsx b/client/transactions/risk-review/columns.tsx index 34db01a1de3..3de9987065c 100644 --- a/client/transactions/risk-review/columns.tsx +++ b/client/transactions/risk-review/columns.tsx @@ -77,8 +77,7 @@ export const getRiskReviewListRowContent = ( ): Record< string, TableCardBodyColumn > => { const detailsURL = getDetailsURL( data.payment_intent.id, 'transactions' ); const formattedCreatedDate = formatUserDateTime( - moment.utc( data.created ).local().toISOString(), - { useGmt: false } + moment.utc( data.created ).toISOString() ); const clickable = ( children: JSX.Element | string ) => ( diff --git a/client/transactions/uncaptured/index.tsx b/client/transactions/uncaptured/index.tsx index 7da7af732f3..97581af5d91 100644 --- a/client/transactions/uncaptured/index.tsx +++ b/client/transactions/uncaptured/index.tsx @@ -131,34 +131,22 @@ export const AuthorizationsList = (): JSX.Element => { }, created: { value: formatUserDateTime( - moment.utc( auth.created ).local().toISOString(), - { useGmt: false } + moment.utc( auth.created ).toISOString() ), display: clickable( formatUserDateTime( - moment.utc( auth.created ).local().toISOString(), - { useGmt: false } + moment.utc( auth.created ).toISOString() ) ), }, // Payments are authorized for a maximum of 7 days capture_by: { value: formatUserDateTime( - moment - .utc( auth.created ) - .add( 7, 'd' ) - .local() - .toISOString(), - { useGmt: false } + moment.utc( auth.created ).add( 7, 'd' ).toISOString() ), display: clickable( formatUserDateTime( - moment - .utc( auth.created ) - .add( 7, 'd' ) - .local() - .toISOString(), - { useGmt: false } + moment.utc( auth.created ).add( 7, 'd' ).toISOString() ) ), }, diff --git a/client/utils/test/date-time.test.ts b/client/utils/test/date-time.test.ts index 7ca342dc5e8..ae1bc2e890b 100644 --- a/client/utils/test/date-time.test.ts +++ b/client/utils/test/date-time.test.ts @@ -22,14 +22,14 @@ describe( 'formatUserDateTime', () => { describe( 'with string input', () => { it( 'should format using default WordPress settings', () => { const dateTime = '2024-10-23 15:28:26'; - const formatted = formatUserDateTime( dateTime, { useGmt: false } ); + const formatted = formatUserDateTime( dateTime ); expect( formatted ).toBe( '2024-10-23 / 15:28' ); } ); it( 'should use custom format if provided', () => { const dateTime = '2024-10-23 15:28:26'; - const options = { customFormat: 'd-m-Y H:i:s', useGmt: false }; + const options = { customFormat: 'd-m-Y H:i:s' }; const formatted = formatUserDateTime( dateTime, options ); expect( formatted ).toBe( '23-10-2024 15:28:26' ); @@ -37,7 +37,7 @@ describe( 'formatUserDateTime', () => { it( 'should exclude time if includeTime is set to false', () => { const dateTime = '2024-10-23 15:28:26'; - const options = { includeTime: false, useGmt: false }; + const options = { includeTime: false }; const formatted = formatUserDateTime( dateTime, options ); expect( formatted ).toBe( '2024-10-23' ); @@ -45,7 +45,7 @@ describe( 'formatUserDateTime', () => { it( 'should use custom separator when provided', () => { const dateTime = '2024-10-23 15:28:26'; - const options = { separator: ' - ', useGmt: false }; + const options = { separator: ' - ' }; const formatted = formatUserDateTime( dateTime, options ); // Expect output with custom separator @@ -91,8 +91,7 @@ describe( 'formatUserDateTime', () => { it( 'should handle GMT/UTC setting correctly', () => { const dateTime = new Date( 2024, 9, 23, 15, 28, 26 ); // Local time (non-UTC) - const options = { useGmt: false }; // Using local timezone - const formatted = formatUserDateTime( dateTime, options ); + const formatted = formatUserDateTime( dateTime ); // The expected format will vary based on your local timezone const expectedFormat = dateI18n( From 280c82032ea7f71d913f81a2344a8be6722c9a88 Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Tue, 29 Oct 2024 16:48:00 +0100 Subject: [PATCH 42/76] Add changelog --- ...atting-arent-respected-in-react-components | 4 + mydiff | 1892 +++++++++++++++++ 2 files changed, 1896 insertions(+) create mode 100644 changelog/fix-6567-user-set-date-and-time-formatting-arent-respected-in-react-components create mode 100644 mydiff diff --git a/changelog/fix-6567-user-set-date-and-time-formatting-arent-respected-in-react-components b/changelog/fix-6567-user-set-date-and-time-formatting-arent-respected-in-react-components new file mode 100644 index 00000000000..5c69920cf26 --- /dev/null +++ b/changelog/fix-6567-user-set-date-and-time-formatting-arent-respected-in-react-components @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Apply User-Defined Date Formatting Settings to WP Admin React Components diff --git a/mydiff b/mydiff new file mode 100644 index 00000000000..c2bc12b4bf5 --- /dev/null +++ b/mydiff @@ -0,0 +1,1892 @@ +diff --git a/client/capital/index.tsx b/client/capital/index.tsx +index 81e76ad91b4..dea559ba74e 100644 +--- a/client/capital/index.tsx ++++ b/client/capital/index.tsx +@@ -6,7 +6,6 @@ + import * as React from 'react'; + import { __, _n } from '@wordpress/i18n'; + import { TableCard } from '@woocommerce/components'; +-import { dateI18n } from '@wordpress/date'; + + /** + * Internal dependencies. +@@ -25,6 +24,7 @@ import Chip from 'components/chip'; + import { useLoans } from 'wcpay/data'; + import { getAdminUrl } from 'wcpay/utils'; + import './style.scss'; ++import { formatUserDateTime } from 'wcpay/utils/date-time'; + + const columns = [ + { +@@ -80,7 +80,7 @@ const getLoanStatusText = ( loan: CapitalLoan ) => { + return loan.fully_paid_at + ? __( 'Paid off', 'woocommerce-payments' ) + + ': ' + +- dateI18n( 'M j, Y', loan.fully_paid_at ) ++ formatUserDateTime( loan.fully_paid_at, { includeTime: false } ) + : __( 'Active', 'woocommerce-payments' ); + }; + +@@ -112,7 +112,11 @@ const getRowsData = ( loans: CapitalLoan[] ) => + const data = { + paid_out_at: { + value: loan.paid_out_at, +- display: clickable( dateI18n( 'M j, Y', loan.paid_out_at ) ), ++ display: clickable( ++ formatUserDateTime( loan.paid_out_at, { ++ includeTime: false, ++ } ) ++ ), + }, + status: { + value: getLoanStatusText( loan ), +@@ -150,7 +154,9 @@ const getRowsData = ( loans: CapitalLoan[] ) => + value: loan.first_paydown_at, + display: clickable( + loan.first_paydown_at +- ? dateI18n( 'M j, Y', loan.first_paydown_at ) ++ ? formatUserDateTime( loan.first_paydown_at, { ++ includeTime: false, ++ } ) + : '-' + ), + }, +diff --git a/client/capital/test/index.test.tsx b/client/capital/test/index.test.tsx +index f9eb43b8a49..41ea917902a 100644 +--- a/client/capital/test/index.test.tsx ++++ b/client/capital/test/index.test.tsx +@@ -25,6 +25,7 @@ declare const global: { + accountLoans: { + has_active_loan: boolean; + }; ++ dateFormat: string; + }; + }; + +@@ -37,6 +38,7 @@ describe( 'CapitalPage', () => { + }, + accountLoans: { has_active_loan: true }, + testMode: true, ++ dateFormat: 'M j, Y', + }; + } ); + +diff --git a/client/components/account-status/account-fees/expiration-description.js b/client/components/account-status/account-fees/expiration-description.js +index 6be5b58681c..b9472c238b8 100644 +--- a/client/components/account-status/account-fees/expiration-description.js ++++ b/client/components/account-status/account-fees/expiration-description.js +@@ -4,13 +4,13 @@ + * External dependencies + */ + import { __, sprintf } from '@wordpress/i18n'; +-import { dateI18n } from '@wordpress/date'; + import moment from 'moment'; + + /** + * Internal dependencies + */ + import { formatCurrency } from 'multi-currency/interface/functions'; ++import { formatUserDateTime } from 'wcpay/utils/date-time'; + + const ExpirationDescription = ( { + feeData: { volume_allowance: volumeAllowance, end_time: endTime, ...rest }, +@@ -26,7 +26,9 @@ const ExpirationDescription = ( { + 'woocommerce-payments' + ), + formatCurrency( volumeAllowance, currencyCode ), +- dateI18n( 'F j, Y', moment( endTime ).toISOString() ) ++ formatUserDateTime( moment( endTime ).toISOString(), { ++ includeTime: false, ++ } ) + ); + } else if ( volumeAllowance ) { + description = sprintf( +@@ -44,7 +46,9 @@ const ExpirationDescription = ( { + 'Discounted base fee expires on %1$s.', + 'woocommerce-payments' + ), +- dateI18n( 'F j, Y', moment( endTime ).toISOString() ) ++ formatUserDateTime( moment( endTime ).toISOString(), { ++ includeTime: false, ++ } ) + ); + } else { + return null; +diff --git a/client/components/account-status/account-fees/test/index.js b/client/components/account-status/account-fees/test/index.js +index 5258af4ffdc..7405b33e371 100644 +--- a/client/components/account-status/account-fees/test/index.js ++++ b/client/components/account-status/account-fees/test/index.js +@@ -46,6 +46,7 @@ describe( 'AccountFees', () => { + precision: 2, + }, + }, ++ dateFormat: 'F j, Y', + }; + } ); + +diff --git a/client/components/active-loan-summary/index.tsx b/client/components/active-loan-summary/index.tsx +index 0c5059ef87c..af817c7331d 100755 +--- a/client/components/active-loan-summary/index.tsx ++++ b/client/components/active-loan-summary/index.tsx +@@ -13,7 +13,6 @@ import { + } from '@wordpress/components'; + import { __, sprintf } from '@wordpress/i18n'; + import { createInterpolateElement } from '@wordpress/element'; +-import { dateI18n } from '@wordpress/date'; + + /** + * Internal dependencies. +@@ -24,6 +23,7 @@ import { useActiveLoanSummary } from 'wcpay/data'; + import { getAdminUrl } from 'wcpay/utils'; + + import './style.scss'; ++import { formatUserDateTime } from 'wcpay/utils/date-time'; + + const Block = ( { + title, +@@ -210,12 +210,12 @@ const ActiveLoanSummary = (): JSX.Element => { + 'Repaid this period (until %s)', + 'woocommerce-payments' + ), +- dateI18n( +- 'M j, Y', ++ formatUserDateTime( + new Date( + details.current_repayment_interval.due_at * + 1000 +- ) ++ ), ++ { includeTime: false, useGmt: true } + ) + ) } + > +@@ -251,9 +251,9 @@ const ActiveLoanSummary = (): JSX.Element => { + +- { dateI18n( +- 'M j, Y', +- new Date( details.advance_paid_out_at * 1000 ) ++ { formatUserDateTime( ++ new Date( details.advance_paid_out_at * 1000 ), ++ { includeTime: false, useGmt: true } + ) } + + { + +- { dateI18n( +- 'M j, Y', +- new Date( details.repayments_begin_at * 1000 ) ++ { formatUserDateTime( ++ new Date( details.repayments_begin_at * 1000 ), ++ { includeTime: false, useGmt: true } + ) } + + +diff --git a/client/components/active-loan-summary/test/__snapshots__/index.js.snap b/client/components/active-loan-summary/test/__snapshots__/index.js.snap +index 4424415245c..4e9dd15ec13 100644 +--- a/client/components/active-loan-summary/test/__snapshots__/index.js.snap ++++ b/client/components/active-loan-summary/test/__snapshots__/index.js.snap +@@ -74,7 +74,7 @@ exports[`Active loan summary renders correctly 1`] = ` +
+- Repaid this period (until Feb 14, 2022) ++ Repaid this period (until Feb 15, 2022) +
+
{ + precision: 2, + }, + }, ++ dateFormat: 'M j, Y', + }; + } ); + afterEach( () => { +diff --git a/client/components/deposits-overview/test/index.tsx b/client/components/deposits-overview/test/index.tsx +index 4853aff7bf5..509bf1d3d42 100644 +--- a/client/components/deposits-overview/test/index.tsx ++++ b/client/components/deposits-overview/test/index.tsx +@@ -68,6 +68,7 @@ declare const global: { + connect: { + country: string; + }; ++ dateFormat: string; + }; + }; + +@@ -231,6 +232,7 @@ describe( 'Deposits Overview information', () => { + precision: 2, + }, + }, ++ dateFormat: 'F j, Y', + }; + mockUseDepositIncludesLoan.mockReturnValue( { + includesFinancingPayout: false, +diff --git a/client/components/disputed-order-notice/index.js b/client/components/disputed-order-notice/index.js +index ab51a52d16e..892f0313cf0 100644 +--- a/client/components/disputed-order-notice/index.js ++++ b/client/components/disputed-order-notice/index.js +@@ -1,7 +1,6 @@ + import moment from 'moment'; + import React, { useEffect } from 'react'; + import { __, _n, sprintf } from '@wordpress/i18n'; +-import { dateI18n } from '@wordpress/date'; + import { createInterpolateElement } from '@wordpress/element'; + + /** +@@ -20,6 +19,7 @@ import { + import { useCharge } from 'wcpay/data'; + import { recordEvent } from 'tracks'; + import './style.scss'; ++import { formatUserDateTime } from 'wcpay/utils/date-time'; + + const DisputedOrderNoticeHandler = ( { chargeId, onDisableOrderRefund } ) => { + const { data: charge } = useCharge( chargeId ); +@@ -131,7 +131,9 @@ const UrgentDisputeNoticeBody = ( { + formatString, + formattedAmount, + reasons[ disputeReason ].display, +- dateI18n( 'M j, Y', dueBy.local().toISOString() ) ++ formatUserDateTime( dueBy.local().toISOString(), { ++ includeTime: false, ++ } ) + ); + + let suffix = sprintf( +@@ -182,7 +184,9 @@ const RegularDisputeNoticeBody = ( { + const suffix = sprintf( + // Translators: %1$s is the dispute due date. + __( 'Please respond before %1$s.', 'woocommerce-payments' ), +- dateI18n( 'M j, Y', dueBy.local().toISOString() ) ++ formatUserDateTime( dueBy.local().toISOString(), { ++ includeTime: false, ++ } ) + ); + + return ( +diff --git a/client/components/disputed-order-notice/test/index.test.js b/client/components/disputed-order-notice/test/index.test.js +index 7e44da132e0..784092295f3 100644 +--- a/client/components/disputed-order-notice/test/index.test.js ++++ b/client/components/disputed-order-notice/test/index.test.js +@@ -36,6 +36,7 @@ describe( 'DisputedOrderNoticeHandler', () => { + connect: { + country: 'US', + }, ++ dateFormat: 'M j, Y', + }; + useCharge.mockReturnValue( { data: mockCharge } ); + } ); +diff --git a/client/deposits/details/index.tsx b/client/deposits/details/index.tsx +index 74aec3f2a7d..e3f4cc0afc9 100644 +--- a/client/deposits/details/index.tsx ++++ b/client/deposits/details/index.tsx +@@ -4,7 +4,6 @@ + * External dependencies + */ + import React from 'react'; +-import { dateI18n } from '@wordpress/date'; + import { __, sprintf } from '@wordpress/i18n'; + import moment from 'moment'; + import { +@@ -40,6 +39,7 @@ import { + } from 'multi-currency/interface/functions'; + import { displayStatus } from '../strings'; + import './style.scss'; ++import { formatUserDateTime } from 'wcpay/utils/date-time'; + + /** + * Renders the deposit status indicator UI, re-purposing the OrderStatus component from @woocommerce/components. +@@ -111,11 +111,10 @@ export const DepositOverview: React.FC< DepositOverviewProps > = ( { + key="depositDate" + label={ + `${ depositDateLabel }: ` + +- dateI18n( +- 'M j, Y', +- moment.utc( deposit.date ).toISOString(), +- true // TODO Change call to gmdateI18n and remove this deprecated param once WP 5.4 support ends. +- ) ++ formatUserDateTime( moment.utc( deposit.date ).toISOString(), { ++ useGmt: true, ++ includeTime: false, ++ } ) + } + value={ } + detail={ deposit.bankAccount } +diff --git a/client/deposits/details/test/index.tsx b/client/deposits/details/test/index.tsx +index f6fd4d98116..385e896f173 100644 +--- a/client/deposits/details/test/index.tsx ++++ b/client/deposits/details/test/index.tsx +@@ -32,6 +32,7 @@ declare const global: { + connect: { + country: string; + }; ++ dateFormat: string; + }; + wcSettings: { countries: Record< string, string > }; + }; +@@ -54,6 +55,7 @@ describe( 'Deposit overview', () => { + precision: 2, + }, + }, ++ dateFormat: 'M j, Y', + }; + } ); + +diff --git a/client/deposits/list/index.tsx b/client/deposits/list/index.tsx +index 03789c466e1..cd948034acc 100644 +--- a/client/deposits/list/index.tsx ++++ b/client/deposits/list/index.tsx +@@ -7,7 +7,6 @@ import { DepositsTableHeader } from 'wcpay/types/deposits'; + import React, { useState } from 'react'; + import { recordEvent } from 'tracks'; + import { useMemo } from '@wordpress/element'; +-import { dateI18n } from '@wordpress/date'; + import { __, _n, sprintf } from '@wordpress/i18n'; + import moment from 'moment'; + import { TableCard, Link } from '@woocommerce/components'; +@@ -48,6 +47,7 @@ import { ReportingExportLanguageHook } from 'wcpay/settings/reporting-settings/i + + import './style.scss'; + import { parseInt } from 'lodash'; ++import { formatUserDateTime } from 'wcpay/utils/date-time'; + + const getColumns = ( sortByDate?: boolean ): DepositsTableHeader[] => [ + { +@@ -135,10 +135,12 @@ export const DepositsList = (): JSX.Element => { + href={ getDetailsURL( deposit.id, 'deposits' ) } + onClick={ () => recordEvent( 'wcpay_deposits_row_click' ) } + > +- { dateI18n( +- 'M j, Y', ++ { formatUserDateTime( + moment.utc( deposit.date ).toISOString(), +- true // TODO Change call to gmdateI18n and remove this deprecated param once WP 5.4 support ends. ++ { ++ includeTime: false, ++ useGmt: true, ++ } + ) } + + ); +@@ -328,10 +330,9 @@ export const DepositsList = (): JSX.Element => { + row[ 0 ], + { + ...row[ 1 ], +- value: dateI18n( +- 'Y-m-d', ++ value: formatUserDateTime( + moment.utc( row[ 1 ].value ).toISOString(), +- true ++ { useGmt: true, includeTime: false } + ), + }, + ...row.slice( 2 ), +diff --git a/client/deposits/list/test/__snapshots__/index.tsx.snap b/client/deposits/list/test/__snapshots__/index.tsx.snap +index 5d6d3d1b96c..64b57b8c38d 100644 +--- a/client/deposits/list/test/__snapshots__/index.tsx.snap ++++ b/client/deposits/list/test/__snapshots__/index.tsx.snap +@@ -345,7 +345,7 @@ exports[`Deposits list renders correctly a single deposit 1`] = ` + data-link-type="wc-admin" + href="admin.php?page=wc-admin&path=%2Fpayments%2Fdeposits%2Fdetails&id=po_mock1" + > +- Jan 2, 2020 ++ Jan 2 2020 + + + +- Jan 3, 2020 ++ Jan 3 2020 + + + +- Jan 2, 2020 ++ Jan 2 2020 + + + +- Jan 3, 2020 ++ Jan 3 2020 + + + { + reporting: { + exportModalDismissed: true, + }, ++ dateFormat: 'M j Y', + }; + } ); + +@@ -308,7 +310,7 @@ describe( 'Deposits list', () => { + // 2. The indexOf check in amount's expect is because the amount in CSV may not contain + // trailing zeros as in the display amount. + // +- expect( formatDate( csvFirstDeposit[ 1 ], 'M j, Y' ) ).toBe( ++ expect( csvFirstDeposit[ 1 ].replace( /^"|"$/g, '' ) ).toBe( + displayFirstDeposit[ 0 ] + ); // date + expect( csvFirstDeposit[ 2 ] ).toBe( displayFirstDeposit[ 1 ] ); // type +diff --git a/client/deposits/utils/index.ts b/client/deposits/utils/index.ts +index 3d8fd6276e1..a3bf14e82a3 100644 +--- a/client/deposits/utils/index.ts ++++ b/client/deposits/utils/index.ts +@@ -2,21 +2,20 @@ + * External dependencies + */ + import { __ } from '@wordpress/i18n'; +-import { dateI18n } from '@wordpress/date'; + import moment from 'moment'; +- +-const formatDate = ( format: string, date: number | string ) => +- dateI18n( +- format, +- moment.utc( date ).toISOString(), +- true // TODO Change call to gmdateI18n and remove this deprecated param once WP 5.4 support ends. +- ); ++import { formatUserDateTime } from 'wcpay/utils/date-time'; + + interface DepositObject { + date: number | string; + } ++ + export const getDepositDate = ( deposit?: DepositObject | null ): string => +- deposit ? formatDate( 'F j, Y', deposit?.date ) : '—'; ++ deposit ++ ? formatUserDateTime( moment.utc( deposit?.date ).toISOString(), { ++ includeTime: false, ++ useGmt: true, ++ } ) ++ : '—'; + + interface GetDepositMonthlyAnchorLabelProps { + monthlyAnchor: number; +diff --git a/client/deposits/utils/test/index.ts b/client/deposits/utils/test/index.ts +index d0361137104..9b58bddf898 100644 +--- a/client/deposits/utils/test/index.ts ++++ b/client/deposits/utils/test/index.ts +@@ -8,7 +8,19 @@ import momentLib from 'moment'; + */ + import { getDepositDate, getDepositMonthlyAnchorLabel } from '../'; + ++declare const global: { ++ wcpaySettings: { ++ dateFormat: string; ++ }; ++}; ++ + describe( 'Deposits Overview Utils / getDepositDate', () => { ++ beforeEach( () => { ++ global.wcpaySettings = { ++ dateFormat: 'F j, Y', ++ }; ++ } ); ++ + test( 'returns a display value without a deposit', () => { + expect( getDepositDate() ).toEqual( '—' ); + } ); +diff --git a/client/disputes/evidence/test/index.js b/client/disputes/evidence/test/index.js +index e9ccdb826cb..142ee738ab8 100644 +--- a/client/disputes/evidence/test/index.js ++++ b/client/disputes/evidence/test/index.js +@@ -96,6 +96,7 @@ describe( 'Dispute evidence form', () => { + + global.wcpaySettings = { + restUrl: 'http://example.com/wp-json/', ++ dateFormat: 'M j, Y', + }; + } ); + afterEach( () => { +@@ -190,6 +191,8 @@ describe( 'Dispute evidence page', () => { + precision: 2, + }, + }, ++ dateFormat: 'M j, Y', ++ timeFormat: 'g:iA', + }; + } ); + +diff --git a/client/disputes/index.tsx b/client/disputes/index.tsx +index 060afccce35..11515987b58 100644 +--- a/client/disputes/index.tsx ++++ b/client/disputes/index.tsx +@@ -5,7 +5,6 @@ + */ + import React, { useState } from 'react'; + import { recordEvent } from 'tracks'; +-import { dateI18n } from '@wordpress/date'; + import { _n, __, sprintf } from '@wordpress/i18n'; + import moment from 'moment'; + import { Button } from '@wordpress/components'; +@@ -58,6 +57,7 @@ import CSVExportModal from 'components/csv-export-modal'; + import { ReportingExportLanguageHook } from 'wcpay/settings/reporting-settings/interfaces'; + + import './style.scss'; ++import { formatUserDateTime } from 'wcpay/utils/date-time'; + + const getHeaders = ( sortColumn?: string ): DisputesTableHeader[] => [ + { +@@ -201,10 +201,7 @@ const smartDueDate = ( dispute: CachedDispute ) => { + + ); + } +- return dateI18n( +- 'M j, Y / g:iA', +- moment.utc( dispute.due_by ).local().toISOString() +- ); ++ return formatUserDateTime( moment.utc( dispute.due_by ).toISOString() ); + }; + + export const DisputesList = (): JSX.Element => { +@@ -301,9 +298,8 @@ export const DisputesList = (): JSX.Element => { + created: { + value: dispute.created, + display: clickable( +- dateI18n( +- 'M j, Y', +- moment( dispute.created ).toISOString() ++ formatUserDateTime( ++ moment.utc( dispute.created ).toISOString() + ) + ), + }, +@@ -483,17 +479,18 @@ export const DisputesList = (): JSX.Element => { + { + // Disputed On. + ...row[ 10 ], +- value: dateI18n( +- 'Y-m-d', +- moment( row[ 10 ].value ).toISOString() ++ value: formatUserDateTime( ++ moment.utc( row[ 10 ].value ).toISOString(), ++ { ++ includeTime: false, ++ } + ), + }, + { + // Respond by. + ...row[ 11 ], +- value: dateI18n( +- 'Y-m-d / g:iA', +- moment( row[ 11 ].value ).toISOString() ++ value: formatUserDateTime( ++ moment.utc( row[ 11 ].value ).toISOString() + ), + }, + ]; +diff --git a/client/disputes/info/index.tsx b/client/disputes/info/index.tsx +index 12f7ba0e64a..a6f6ecaad06 100644 +--- a/client/disputes/info/index.tsx ++++ b/client/disputes/info/index.tsx +@@ -5,7 +5,6 @@ + */ + import * as React from 'react'; + import { __ } from '@wordpress/i18n'; +-import { dateI18n } from '@wordpress/date'; + import moment from 'moment'; + import { Link } from '@woocommerce/components'; + +@@ -20,6 +19,7 @@ import { formatExplicitCurrency } from 'multi-currency/interface/functions'; + import './style.scss'; + import Loadable from 'components/loadable'; + import { Dispute } from 'wcpay/types/disputes'; ++import { formatUserDateTime } from 'wcpay/utils/date-time'; + + const fields: { key: string; label: string }[] = [ + { key: 'created', label: __( 'Dispute date', 'woocommerce-payments' ) }, +@@ -69,20 +69,20 @@ const Info = ( { + transactionId: __( 'Transaction link', 'woocommerce-payments' ), + } + : { +- created: dateI18n( +- 'M j, Y', +- moment( dispute.created * 1000 ).toISOString() ++ created: formatUserDateTime( ++ moment( dispute.created * 1000 ).toISOString(), ++ { includeTime: false } + ), + amount: formatExplicitCurrency( + dispute.amount || 0, + dispute.currency || 'USD' + ), + dueBy: dispute.evidence_details +- ? dateI18n( +- 'M j, Y - g:iA', ++ ? formatUserDateTime( + moment( + dispute.evidence_details.due_by * 1000 +- ).toISOString() ++ ).toISOString(), ++ { separator: ' - ' } + ) + : null, + reason: composeDisputeReason( dispute ), +diff --git a/client/disputes/info/test/index.tsx b/client/disputes/info/test/index.tsx +index 1e2f5dcb8b0..7b49b588052 100644 +--- a/client/disputes/info/test/index.tsx ++++ b/client/disputes/info/test/index.tsx +@@ -29,6 +29,8 @@ declare const global: { + precision: number; + }; + }; ++ dateFormat: string; ++ timeFormat: string; + }; + }; + +@@ -49,6 +51,8 @@ describe( 'Dispute info', () => { + precision: 2, + }, + }, ++ dateFormat: 'M j, Y', ++ timeFormat: 'g:iA', + }; + } ); + +diff --git a/client/disputes/test/index.tsx b/client/disputes/test/index.tsx +index 1409bfc852d..52af84da080 100644 +--- a/client/disputes/test/index.tsx ++++ b/client/disputes/test/index.tsx +@@ -17,13 +17,15 @@ import { + useReportingExportLanguage, + useSettings, + } from 'data/index'; +-import { formatDate, getUnformattedAmount } from 'wcpay/utils/test-utils'; ++import { getUnformattedAmount } from 'wcpay/utils/test-utils'; + import React from 'react'; + import { + CachedDispute, + DisputeReason, + DisputeStatus, + } from 'wcpay/types/disputes'; ++import { formatUserDateTime } from 'wcpay/utils/date-time'; ++import moment from 'moment'; + + jest.mock( '@woocommerce/csv-export', () => { + const actualModule = jest.requireActual( '@woocommerce/csv-export' ); +@@ -100,6 +102,8 @@ declare const global: { + reporting?: { + exportModalDismissed: boolean; + }; ++ dateFormat?: string; ++ timeFormat?: string; + }; + }; + +@@ -198,6 +202,8 @@ describe( 'Disputes list', () => { + reporting: { + exportModalDismissed: true, + }, ++ dateFormat: 'Y-m-d', ++ timeFormat: 'g:iA', + }; + } ); + +@@ -363,8 +369,10 @@ describe( 'Disputes list', () => { + `"${ displayFirstDispute[ 5 ] }"` + ); // customer + +- expect( formatDate( csvFirstDispute[ 11 ], 'Y-m-d / g:iA' ) ).toBe( +- formatDate( displayFirstDispute[ 6 ], 'Y-m-d / g:iA' ) ++ expect( csvFirstDispute[ 11 ].replace( /^"|"$/g, '' ) ).toBe( ++ formatUserDateTime( ++ moment.utc( mockDisputes[ 0 ].due_by ).toISOString() ++ ) + ); // date respond by + } ); + } ); +diff --git a/client/documents/list/index.tsx b/client/documents/list/index.tsx +index 307c3faf0c6..a6aacd9c4a2 100644 +--- a/client/documents/list/index.tsx ++++ b/client/documents/list/index.tsx +@@ -4,7 +4,6 @@ + * External dependencies + */ + import React, { useCallback, useEffect, useState } from 'react'; +-import { dateI18n } from '@wordpress/date'; + import { __, _n, sprintf } from '@wordpress/i18n'; + import moment from 'moment'; + import { TableCard, TableCardColumn } from '@woocommerce/components'; +@@ -21,6 +20,7 @@ import DocumentsFilters from '../filters'; + import Page from '../../components/page'; + import { getDocumentUrl } from 'wcpay/utils'; + import VatFormModal from 'wcpay/vat/form-modal'; ++import { formatUserDateTime } from 'wcpay/utils/date-time'; + + interface Column extends TableCardColumn { + key: 'date' | 'type' | 'description' | 'download'; +@@ -68,15 +68,15 @@ const getDocumentDescription = ( document: Document ) => { + if ( document.period_from && document.period_to ) { + return sprintf( + __( 'VAT invoice for %s to %s', 'woocommerce-payments' ), +- dateI18n( +- 'M j, Y', ++ formatUserDateTime( + moment.utc( document.period_from ).toISOString(), +- 'utc' ++ { includeTime: false } + ), +- dateI18n( +- 'M j, Y', ++ formatUserDateTime( + moment.utc( document.period_to ).toISOString(), +- 'utc' ++ { ++ includeTime: false, ++ } + ) + ); + } +@@ -180,9 +180,11 @@ export const DocumentsList = (): JSX.Element => { + const data = { + date: { + value: document.date, +- display: dateI18n( +- 'M j, Y', +- moment.utc( document.date ).local().toISOString() ++ display: formatUserDateTime( ++ moment.utc( document.date ).toISOString(), ++ { ++ includeTime: false, ++ } + ), + }, + type: { +diff --git a/client/documents/list/test/index.tsx b/client/documents/list/test/index.tsx +index c54cf7a02c8..0eac7e0bf61 100644 +--- a/client/documents/list/test/index.tsx ++++ b/client/documents/list/test/index.tsx +@@ -36,6 +36,7 @@ declare const global: { + accountStatus: { + hasSubmittedVatData: boolean; + }; ++ dateFormat: string; + }; + }; + +@@ -60,6 +61,11 @@ describe( 'Documents list', () => { + let container: Element; + let rerender: ( ui: React.ReactElement ) => void; + beforeEach( () => { ++ global.wcpaySettings = { ++ accountStatus: { hasSubmittedVatData: true }, ++ dateFormat: 'M j, Y', ++ }; ++ + mockUseDocuments.mockReturnValue( { + documents: getMockDocuments(), + isLoading: false, +@@ -200,6 +206,7 @@ describe( 'Document download button', () => { + beforeEach( () => { + global.wcpaySettings = { + accountStatus: { hasSubmittedVatData: true }, ++ dateFormat: 'M j, Y', + }; + + render( ); +@@ -223,6 +230,7 @@ describe( 'Document download button', () => { + beforeEach( () => { + global.wcpaySettings = { + accountStatus: { hasSubmittedVatData: false }, ++ dateFormat: 'M j, Y', + }; + + render( ); +@@ -293,6 +301,7 @@ describe( 'Direct document download', () => { + + global.wcpaySettings = { + accountStatus: { hasSubmittedVatData: true }, ++ dateFormat: 'M j, Y', + }; + } ); + +diff --git a/client/globals.d.ts b/client/globals.d.ts +index f00b521943a..9992f2faadb 100644 +--- a/client/globals.d.ts ++++ b/client/globals.d.ts +@@ -135,6 +135,8 @@ declare global { + isOverviewSurveySubmitted: boolean; + lifetimeTPV: number; + defaultExpressCheckoutBorderRadius: string; ++ dateFormat: string; ++ timeFormat: string; + }; + + const wc: { +diff --git a/client/overview/modal/update-business-details/index.tsx b/client/overview/modal/update-business-details/index.tsx +index 2c654a57f9b..fb98eca74f3 100644 +--- a/client/overview/modal/update-business-details/index.tsx ++++ b/client/overview/modal/update-business-details/index.tsx +@@ -3,7 +3,6 @@ + */ + import React, { useState } from 'react'; + import { Button, Modal, Notice } from '@wordpress/components'; +-import { dateI18n } from '@wordpress/date'; + import { sprintf } from '@wordpress/i18n'; + import moment from 'moment'; + +@@ -13,6 +12,7 @@ import moment from 'moment'; + import strings from './strings'; + import './index.scss'; + import { recordEvent } from 'wcpay/tracks'; ++import { formatUserDateTime } from 'wcpay/utils/date-time'; + + interface Props { + errorMessages: Array< string >; +@@ -57,11 +57,15 @@ const UpdateBusinessDetailsModal = ( { + currentDeadline + ? sprintf( + strings.restrictedSoonDescription, +- dateI18n( +- 'ga M j, Y', +- moment( +- currentDeadline * 1000 +- ).toISOString() ++ formatUserDateTime( ++ moment ++ .utc( ++ currentDeadline * 1000 ++ ) ++ .toISOString(), ++ { ++ customFormat: 'ga M j, Y', ++ } + ) + ) + : strings.restrictedDescription } +diff --git a/client/overview/task-list/tasks/dispute-task.tsx b/client/overview/task-list/tasks/dispute-task.tsx +index 235b92696b9..f7be6ec8e13 100644 +--- a/client/overview/task-list/tasks/dispute-task.tsx ++++ b/client/overview/task-list/tasks/dispute-task.tsx +@@ -2,7 +2,6 @@ + * External dependencies + */ + import { __, sprintf } from '@wordpress/i18n'; +-import { dateI18n } from '@wordpress/date'; + import moment from 'moment'; + import { getHistory } from '@woocommerce/navigation'; + +@@ -15,6 +14,7 @@ import { formatCurrency } from 'multi-currency/interface/functions'; + import { getAdminUrl } from 'wcpay/utils'; + import { recordEvent } from 'tracks'; + import { isDueWithin } from 'wcpay/disputes/utils'; ++import { formatUserDateTime } from 'wcpay/utils/date-time'; + + /** + * Returns an array of disputes that are due within the specified number of days. +@@ -142,9 +142,11 @@ export const getDisputeResolutionTask = ( + ? sprintf( + __( 'Respond today by %s', 'woocommerce-payments' ), + // Show due_by time in local timezone: e.g. "11:59 PM". +- dateI18n( +- 'g:i A', +- moment.utc( dispute.due_by ).local().toISOString() ++ formatUserDateTime( ++ moment.utc( dispute.due_by ).toISOString(), ++ { ++ customFormat: 'g:i A', ++ } + ) + ) + : sprintf( +@@ -153,9 +155,11 @@ export const getDisputeResolutionTask = ( + 'woocommerce-payments' + ), + // Show due_by date in local timezone: e.g. "Jan 1, 2021". +- dateI18n( +- 'M j, Y', +- moment.utc( dispute.due_by ).local().toISOString() ++ formatUserDateTime( ++ moment.utc( dispute.due_by ).toISOString(), ++ { ++ includeTime: false, ++ } + ), + moment( dispute.due_by ).fromNow( true ) // E.g. "2 days". + ); +diff --git a/client/overview/task-list/tasks/update-business-details-task.tsx b/client/overview/task-list/tasks/update-business-details-task.tsx +index a9746ec7b66..2ca90519616 100644 +--- a/client/overview/task-list/tasks/update-business-details-task.tsx ++++ b/client/overview/task-list/tasks/update-business-details-task.tsx +@@ -11,9 +11,9 @@ import { addQueryArgs } from '@wordpress/url'; + */ + import type { TaskItemProps } from '../types'; + import UpdateBusinessDetailsModal from 'wcpay/overview/modal/update-business-details'; +-import { dateI18n } from '@wordpress/date'; + import moment from 'moment'; + import { recordEvent } from 'wcpay/tracks'; ++import { formatUserDateTime } from 'wcpay/utils/date-time'; + + export const getUpdateBusinessDetailsTask = ( + errorMessages: string[], +@@ -46,9 +46,11 @@ export const getUpdateBusinessDetailsTask = ( + 'Update by %s to avoid a disruption in deposits.', + 'woocommerce-payments' + ), +- dateI18n( +- 'ga M j, Y', +- moment( currentDeadline * 1000 ).toISOString() ++ formatUserDateTime( ++ moment( currentDeadline * 1000 ).toISOString(), ++ { ++ customFormat: 'ga M j, Y', ++ } + ) + ); + +diff --git a/client/overview/task-list/test/tasks.js b/client/overview/task-list/test/tasks.js +index 44b5de4e9f7..429ed175efb 100644 +--- a/client/overview/task-list/test/tasks.js ++++ b/client/overview/task-list/test/tasks.js +@@ -139,6 +139,7 @@ describe( 'getTasks()', () => { + precision: 2, + }, + }, ++ dateFormat: 'M j, Y', + }; + } ); + afterEach( () => { +diff --git a/client/payment-details/dispute-details/dispute-due-by-date.tsx b/client/payment-details/dispute-details/dispute-due-by-date.tsx +index 18993ef2387..697485c9d63 100644 +--- a/client/payment-details/dispute-details/dispute-due-by-date.tsx ++++ b/client/payment-details/dispute-details/dispute-due-by-date.tsx +@@ -2,10 +2,10 @@ + * External dependencies + */ + import React from 'react'; +-import { dateI18n } from '@wordpress/date'; + import { __, _n, sprintf } from '@wordpress/i18n'; + import classNames from 'classnames'; + import moment from 'moment'; ++import { formatUserDateTime } from 'wcpay/utils/date-time'; + + const DisputeDueByDate: React.FC< { + dueBy: number; +@@ -14,9 +14,11 @@ const DisputeDueByDate: React.FC< { + const daysRemaining = Math.floor( + moment.unix( dueBy ).diff( moment(), 'days', true ) + ); +- const respondByDate = dateI18n( +- 'M j, Y, g:ia', +- moment( dueBy * 1000 ).toISOString() ++ const respondByDate = formatUserDateTime( ++ moment( dueBy * 1000 ).toISOString(), ++ { ++ separator: ', ', ++ } + ); + return ( + +diff --git a/client/payment-details/dispute-details/dispute-resolution-footer.tsx b/client/payment-details/dispute-details/dispute-resolution-footer.tsx +index 15fec759244..e6b38213abc 100644 +--- a/client/payment-details/dispute-details/dispute-resolution-footer.tsx ++++ b/client/payment-details/dispute-details/dispute-resolution-footer.tsx +@@ -3,7 +3,6 @@ + */ + import React from 'react'; + import moment from 'moment'; +-import { dateI18n } from '@wordpress/date'; + import { __, sprintf } from '@wordpress/i18n'; + import { Link } from '@woocommerce/components'; + import { createInterpolateElement } from '@wordpress/element'; +@@ -17,13 +16,13 @@ import { recordEvent } from 'tracks'; + import { getAdminUrl } from 'wcpay/utils'; + import { getDisputeFeeFormatted } from 'wcpay/disputes/utils'; + import './style.scss'; ++import { formatUserDateTime } from 'wcpay/utils/date-time'; + + const DisputeUnderReviewFooter: React.FC< { + dispute: Pick< Dispute, 'id' | 'metadata' | 'status' >; + } > = ( { dispute } ) => { + const submissionDateFormatted = dispute.metadata.__evidence_submitted_at +- ? dateI18n( +- 'M j, Y', ++ ? formatUserDateTime( + moment + .unix( + parseInt( dispute.metadata.__evidence_submitted_at, 10 ) +@@ -93,8 +92,7 @@ const DisputeWonFooter: React.FC< { + dispute: Pick< Dispute, 'id' | 'metadata' | 'status' >; + } > = ( { dispute } ) => { + const closedDateFormatted = dispute.metadata.__dispute_closed_at +- ? dateI18n( +- 'M j, Y', ++ ? formatUserDateTime( + moment + .unix( + parseInt( dispute.metadata.__dispute_closed_at, 10 ) +@@ -171,13 +169,13 @@ const DisputeLostFooter: React.FC< { + const disputeFeeFormatted = getDisputeFeeFormatted( dispute, true ) ?? '-'; + + const closedDateFormatted = dispute.metadata.__dispute_closed_at +- ? dateI18n( +- 'M j, Y', ++ ? formatUserDateTime( + moment + .unix( + parseInt( dispute.metadata.__dispute_closed_at, 10 ) + ) +- .toISOString() ++ .toISOString(), ++ { includeTime: false } + ) + : '-'; + +@@ -274,13 +272,13 @@ const InquiryUnderReviewFooter: React.FC< { + dispute: Pick< Dispute, 'id' | 'metadata' | 'status' >; + } > = ( { dispute } ) => { + const submissionDateFormatted = dispute.metadata.__evidence_submitted_at +- ? dateI18n( +- 'M j, Y', ++ ? formatUserDateTime( + moment + .unix( + parseInt( dispute.metadata.__evidence_submitted_at, 10 ) + ) +- .toISOString() ++ .toISOString(), ++ { includeTime: false } + ) + : '-'; + +@@ -346,13 +344,13 @@ const InquiryClosedFooter: React.FC< { + } > = ( { dispute } ) => { + const isSubmitted = !! dispute.metadata.__evidence_submitted_at; + const closedDateFormatted = dispute.metadata.__dispute_closed_at +- ? dateI18n( +- 'M j, Y', ++ ? formatUserDateTime( + moment + .unix( + parseInt( dispute.metadata.__dispute_closed_at, 10 ) + ) +- .toISOString() ++ .toISOString(), ++ { includeTime: false } + ) + : '-'; + +diff --git a/client/payment-details/dispute-details/dispute-steps.tsx b/client/payment-details/dispute-details/dispute-steps.tsx +index 01f87431274..2fce833c4ec 100644 +--- a/client/payment-details/dispute-details/dispute-steps.tsx ++++ b/client/payment-details/dispute-details/dispute-steps.tsx +@@ -7,7 +7,6 @@ import React from 'react'; + import { __, sprintf } from '@wordpress/i18n'; + import { createInterpolateElement } from '@wordpress/element'; + import { ExternalLink } from '@wordpress/components'; +-import { dateI18n } from '@wordpress/date'; + import moment from 'moment'; + import HelpOutlineIcon from 'gridicons/dist/help-outline'; + +@@ -20,6 +19,7 @@ import { formatExplicitCurrency } from 'multi-currency/interface/functions'; + import { ClickTooltip } from 'wcpay/components/tooltip'; + import { getDisputeFeeFormatted } from 'wcpay/disputes/utils'; + import DisputeDueByDate from './dispute-due-by-date'; ++import { formatUserDateTime } from 'wcpay/utils/date-time'; + + interface Props { + dispute: Dispute; +@@ -34,13 +34,15 @@ export const DisputeSteps: React.FC< Props > = ( { + } ) => { + let emailLink; + if ( customer?.email ) { +- const chargeDate = dateI18n( +- 'Y-m-d', +- moment( chargeCreated * 1000 ).toISOString() ++ const chargeDate = formatUserDateTime( ++ moment( chargeCreated * 1000 ).toISOString(), ++ { ++ includeTime: false, ++ } + ); +- const disputeDate = dateI18n( +- 'Y-m-d', +- moment( dispute.created * 1000 ).toISOString() ++ const disputeDate = formatUserDateTime( ++ moment( dispute.created * 1000 ).toISOString(), ++ { includeTime: false } + ); + const emailSubject = sprintf( + // Translators: %1$s is the store name, %2$s is the charge date. +@@ -173,12 +175,10 @@ export const InquirySteps: React.FC< Props > = ( { + } ) => { + let emailLink; + if ( customer?.email ) { +- const chargeDate = dateI18n( +- 'Y-m-d', ++ const chargeDate = formatUserDateTime( + moment( chargeCreated * 1000 ).toISOString() + ); +- const disputeDate = dateI18n( +- 'Y-m-d', ++ const disputeDate = formatUserDateTime( + moment( dispute.created * 1000 ).toISOString() + ); + const emailSubject = sprintf( +diff --git a/client/payment-details/dispute-details/dispute-summary-row.tsx b/client/payment-details/dispute-details/dispute-summary-row.tsx +index 0a43cb223e0..fbe7371fd56 100644 +--- a/client/payment-details/dispute-details/dispute-summary-row.tsx ++++ b/client/payment-details/dispute-details/dispute-summary-row.tsx +@@ -7,7 +7,6 @@ import React from 'react'; + import moment from 'moment'; + import HelpOutlineIcon from 'gridicons/dist/help-outline'; + import { __ } from '@wordpress/i18n'; +-import { dateI18n } from '@wordpress/date'; + + /** + * Internal dependencies +@@ -20,6 +19,7 @@ import { formatStringValue } from 'wcpay/utils'; + import { ClickTooltip } from 'wcpay/components/tooltip'; + import Paragraphs from 'wcpay/components/paragraphs'; + import DisputeDueByDate from './dispute-due-by-date'; ++import { formatUserDateTime } from 'wcpay/utils/date-time'; + + interface Props { + dispute: Dispute; +@@ -39,9 +39,9 @@ const DisputeSummaryRow: React.FC< Props > = ( { dispute } ) => { + { + title: __( 'Disputed On', 'woocommerce-payments' ), + content: dispute.created +- ? dateI18n( +- 'M j, Y, g:ia', +- moment( dispute.created * 1000 ).toISOString() ++ ? formatUserDateTime( ++ moment( dispute.created * 1000 ).toISOString(), ++ { separator: ', ' } + ) + : '–', + }, +diff --git a/client/payment-details/summary/index.tsx b/client/payment-details/summary/index.tsx +index 4e9d9559a4b..d7b1790d23d 100644 +--- a/client/payment-details/summary/index.tsx ++++ b/client/payment-details/summary/index.tsx +@@ -4,7 +4,6 @@ + * External dependencies + */ + import { __ } from '@wordpress/i18n'; +-import { dateI18n } from '@wordpress/date'; + import { + Card, + CardBody, +@@ -64,6 +63,7 @@ import DisputeResolutionFooter from '../dispute-details/dispute-resolution-foote + import ErrorBoundary from 'components/error-boundary'; + import RefundModal from 'wcpay/payment-details/summary/refund-modal'; + import CardNotice from 'wcpay/components/card-notice'; ++import { formatUserDateTime } from 'wcpay/utils/date-time'; + + declare const window: any; + +@@ -110,9 +110,11 @@ const composePaymentSummaryItems = ( { + { + title: __( 'Date', 'woocommerce-payments' ), + content: charge.created +- ? dateI18n( +- 'M j, Y, g:ia', +- moment( charge.created * 1000 ).toISOString() ++ ? formatUserDateTime( ++ moment( charge.created * 1000 ).toISOString(), ++ { ++ customFormat: 'M j, Y, g:ia', ++ } + ) + : '–', + }, +@@ -706,12 +708,12 @@ const PaymentDetailsSummary: React.FC< PaymentDetailsSummaryProps > = ( { + } + ) }{ ' ' } + + +diff --git a/client/payment-details/summary/test/index.test.tsx b/client/payment-details/summary/test/index.test.tsx +index 9055d481dda..d98363a720e 100755 +--- a/client/payment-details/summary/test/index.test.tsx ++++ b/client/payment-details/summary/test/index.test.tsx +@@ -34,6 +34,8 @@ declare const global: { + featureFlags: { + isAuthAndCaptureEnabled: boolean; + }; ++ dateFormat: string; ++ timeFormat: string; + }; + }; + +@@ -203,6 +205,8 @@ describe( 'PaymentDetailsSummary', () => { + precision: 0, + }, + }, ++ dateFormat: 'M j, Y', ++ timeFormat: 'g:iA', + }; + + // mock Date.now that moment library uses to get current date for testing purposes +diff --git a/client/payment-details/timeline/map-events.js b/client/payment-details/timeline/map-events.js +index 64bc74d91d2..1a3ffe5546f 100644 +--- a/client/payment-details/timeline/map-events.js ++++ b/client/payment-details/timeline/map-events.js +@@ -5,7 +5,6 @@ + */ + import { flatMap } from 'lodash'; + import { __, sprintf } from '@wordpress/i18n'; +-import { dateI18n } from '@wordpress/date'; + import { addQueryArgs } from '@wordpress/url'; + import moment from 'moment'; + import { createInterpolateElement } from '@wordpress/element'; +@@ -31,6 +30,7 @@ import { formatFee } from 'utils/fees'; + import { getAdminUrl } from 'wcpay/utils'; + import { ShieldIcon } from 'wcpay/icons'; + import { fraudOutcomeRulesetMapping } from './mappings'; ++import { formatUserDateTime } from 'wcpay/utils/date-time'; + + /** + * Creates a timeline item about a payment status change +@@ -84,9 +84,9 @@ const getDepositTimelineItem = ( + 'woocommerce-payments' + ), + formattedAmount, +- dateI18n( +- 'M j, Y', +- moment( event.deposit.arrival_date * 1000 ).toISOString() ++ formatUserDateTime( ++ moment( event.deposit.arrival_date * 1000 ).toISOString(), ++ { includeTime: false } + ) + ); + const depositUrl = getAdminUrl( { +@@ -143,9 +143,9 @@ const getFinancingPaydownTimelineItem = ( event, formattedAmount, body ) => { + 'woocommerce-payments' + ), + formattedAmount, +- dateI18n( +- 'M j, Y', +- moment( event.deposit.arrival_date * 1000 ).toISOString() ++ formatUserDateTime( ++ moment( event.deposit.arrival_date * 1000 ).toISOString(), ++ { includeTime: false } + ) + ); + +diff --git a/client/payment-details/timeline/test/index.js b/client/payment-details/timeline/test/index.js +index 2529f3d673e..616c780dd69 100644 +--- a/client/payment-details/timeline/test/index.js ++++ b/client/payment-details/timeline/test/index.js +@@ -34,6 +34,7 @@ describe( 'PaymentDetailsTimeline', () => { + precision: 2, + }, + }, ++ dateFormat: 'M j, Y', + }; + } ); + +diff --git a/client/payment-details/timeline/test/map-events.js b/client/payment-details/timeline/test/map-events.js +index c3e42ceae8b..73f65b94940 100644 +--- a/client/payment-details/timeline/test/map-events.js ++++ b/client/payment-details/timeline/test/map-events.js +@@ -47,6 +47,7 @@ describe( 'mapTimelineEvents', () => { + precision: 2, + }, + }, ++ dateFormat: 'M j, Y', + }; + } ); + +diff --git a/client/transactions/blocked/columns.tsx b/client/transactions/blocked/columns.tsx +index 1d75e407cea..c1e6cedd386 100644 +--- a/client/transactions/blocked/columns.tsx ++++ b/client/transactions/blocked/columns.tsx +@@ -2,7 +2,6 @@ + * External dependencies + */ + import React from 'react'; +-import { dateI18n } from '@wordpress/date'; + import { __ } from '@wordpress/i18n'; + import moment from 'moment'; + import { TableCardColumn, TableCardBodyColumn } from '@woocommerce/components'; +@@ -15,6 +14,7 @@ import TransactionStatusPill from 'wcpay/components/transaction-status-pill'; + import { FraudOutcomeTransaction } from '../../data'; + import { getDetailsURL } from '../../components/details-link'; + import ClickableCell from '../../components/clickable-cell'; ++import { formatUserDateTime } from 'wcpay/utils/date-time'; + + interface Column extends TableCardColumn { + key: 'created' | 'amount' | 'customer' | 'status'; +@@ -70,9 +70,8 @@ export const getBlockedListRowContent = ( + data.payment_intent.id || data.order_id.toString(), + 'transactions' + ); +- const formattedCreatedDate = dateI18n( +- 'M j, Y / g:iA', +- moment.utc( data.created ).local().toISOString() ++ const formattedCreatedDate = formatUserDateTime( ++ moment.utc( data.created ).toISOString() + ); + + const clickable = ( children: JSX.Element | string ) => ( +diff --git a/client/transactions/blocked/test/columns.test.tsx b/client/transactions/blocked/test/columns.test.tsx +index 7ca2c3d4895..b65e2d10c12 100644 +--- a/client/transactions/blocked/test/columns.test.tsx ++++ b/client/transactions/blocked/test/columns.test.tsx +@@ -15,6 +15,8 @@ declare const global: { + connect: { + country: string; + }; ++ dateFormat: string; ++ timeFormat: string; + }; + }; + const mockWcPaySettings = { +@@ -23,6 +25,8 @@ const mockWcPaySettings = { + connect: { + country: 'US', + }, ++ dateFormat: 'M j, Y', ++ timeFormat: 'g:iA', + }; + + describe( 'Blocked fraud outcome transactions columns', () => { +diff --git a/client/transactions/list/deposit.tsx b/client/transactions/list/deposit.tsx +index b1889f3ad19..ff032aaab66 100644 +--- a/client/transactions/list/deposit.tsx ++++ b/client/transactions/list/deposit.tsx +@@ -5,7 +5,6 @@ + */ + import React from 'react'; + import moment from 'moment'; +-import { dateI18n } from '@wordpress/date'; + import { __ } from '@wordpress/i18n'; + import interpolateComponents from '@automattic/interpolate-components'; + import { ExternalLink } from '@wordpress/components'; +@@ -17,6 +16,7 @@ import InfoOutlineIcon from 'gridicons/dist/info-outline'; + */ + import { getAdminUrl } from 'utils'; + import { ClickTooltip } from 'components/tooltip'; ++import { formatUserDateTime } from 'wcpay/utils/date-time'; + + interface DepositProps { + depositId?: string; +@@ -31,12 +31,13 @@ const Deposit: React.FC< DepositProps > = ( { depositId, dateAvailable } ) => { + id: depositId, + } ); + +- const formattedDateAvailable = dateI18n( +- 'M j, Y', ++ const formattedDateAvailable = formatUserDateTime( + moment.utc( dateAvailable ).toISOString(), +- true // TODO Change call to gmdateI18n and remove this deprecated param once WP 5.4 support ends. ++ { ++ includeTime: false, ++ useGmt: true, ++ } + ); +- + return { formattedDateAvailable }; + } + +diff --git a/client/transactions/list/index.tsx b/client/transactions/list/index.tsx +index e123d0319f5..7ff37084ad3 100644 +--- a/client/transactions/list/index.tsx ++++ b/client/transactions/list/index.tsx +@@ -7,7 +7,6 @@ import React, { Fragment, useState } from 'react'; + import { uniq } from 'lodash'; + import { useDispatch } from '@wordpress/data'; + import { useMemo } from '@wordpress/element'; +-import { dateI18n } from '@wordpress/date'; + import { __, _n, sprintf } from '@wordpress/i18n'; + import moment from 'moment'; + import { +@@ -70,6 +69,7 @@ import p24BankList from '../../payment-details/payment-method/p24/bank-list'; + import { HoverTooltip } from 'components/tooltip'; + import { PAYMENT_METHOD_TITLES } from 'wcpay/constants/payment-method'; + import { ReportingExportLanguageHook } from 'wcpay/settings/reporting-settings/interfaces'; ++import { formatUserDateTime } from 'wcpay/utils/date-time'; + + interface TransactionsListProps { + depositId?: string; +@@ -466,10 +466,7 @@ export const TransactionsList = ( + date: { + value: txn.date, + display: clickable( +- dateI18n( +- 'M j, Y / g:iA', +- moment.utc( txn.date ).local().toISOString() +- ) ++ formatUserDateTime( moment.utc( txn.date ).toISOString() ) + ), + }, + channel: { +diff --git a/client/transactions/list/test/deposit.tsx b/client/transactions/list/test/deposit.tsx +index 2cb87b0b248..283e101e3db 100644 +--- a/client/transactions/list/test/deposit.tsx ++++ b/client/transactions/list/test/deposit.tsx +@@ -12,6 +12,12 @@ import { render } from '@testing-library/react'; + import Deposit from '../deposit'; + + describe( 'Deposit', () => { ++ beforeEach( () => { ++ // Mock the window.wcpaySettings property ++ window.wcpaySettings.dateFormat = 'M j, Y'; ++ window.wcpaySettings.timeFormat = 'g:i a'; ++ } ); ++ + test( 'renders with date and deposit available', () => { + const { container: link } = render( + +diff --git a/client/transactions/list/test/index.tsx b/client/transactions/list/test/index.tsx +index 8d97397ce01..0ac273c6751 100644 +--- a/client/transactions/list/test/index.tsx ++++ b/client/transactions/list/test/index.tsx +@@ -244,6 +244,8 @@ describe( 'Transactions list', () => { + exportModalDismissed: true, + }, + }; ++ window.wcpaySettings.dateFormat = 'M j, Y'; ++ window.wcpaySettings.timeFormat = 'g:iA'; + } ); + + test( 'renders correctly when filtered by deposit', () => { +diff --git a/client/transactions/risk-review/columns.tsx b/client/transactions/risk-review/columns.tsx +index d7f5de95111..3de9987065c 100644 +--- a/client/transactions/risk-review/columns.tsx ++++ b/client/transactions/risk-review/columns.tsx +@@ -2,7 +2,6 @@ + * External dependencies + */ + import React from 'react'; +-import { dateI18n } from '@wordpress/date'; + import { __ } from '@wordpress/i18n'; + import moment from 'moment'; + import { TableCardColumn, TableCardBodyColumn } from '@woocommerce/components'; +@@ -17,6 +16,7 @@ import { formatExplicitCurrency } from 'multi-currency/interface/functions'; + import { recordEvent } from 'tracks'; + import TransactionStatusPill from 'wcpay/components/transaction-status-pill'; + import { FraudOutcomeTransaction } from '../../data'; ++import { formatUserDateTime } from 'wcpay/utils/date-time'; + + interface Column extends TableCardColumn { + key: 'created' | 'amount' | 'customer' | 'status'; +@@ -76,9 +76,8 @@ export const getRiskReviewListRowContent = ( + data: FraudOutcomeTransaction + ): Record< string, TableCardBodyColumn > => { + const detailsURL = getDetailsURL( data.payment_intent.id, 'transactions' ); +- const formattedCreatedDate = dateI18n( +- 'M j, Y / g:iA', +- moment.utc( data.created ).local().toISOString() ++ const formattedCreatedDate = formatUserDateTime( ++ moment.utc( data.created ).toISOString() + ); + + const clickable = ( children: JSX.Element | string ) => ( +diff --git a/client/transactions/risk-review/test/columns.test.tsx b/client/transactions/risk-review/test/columns.test.tsx +index 033f9eb7aa2..54de107e0e4 100644 +--- a/client/transactions/risk-review/test/columns.test.tsx ++++ b/client/transactions/risk-review/test/columns.test.tsx +@@ -15,6 +15,8 @@ declare const global: { + connect: { + country: string; + }; ++ dateFormat: string; ++ timeFormat: string; + }; + }; + const mockWcPaySettings = { +@@ -23,6 +25,8 @@ const mockWcPaySettings = { + connect: { + country: 'US', + }, ++ dateFormat: 'M j, Y', ++ timeFormat: 'g:iA', + }; + + describe( 'Review fraud outcome transactions columns', () => { +diff --git a/client/transactions/uncaptured/index.tsx b/client/transactions/uncaptured/index.tsx +index 17058760c19..97581af5d91 100644 +--- a/client/transactions/uncaptured/index.tsx ++++ b/client/transactions/uncaptured/index.tsx +@@ -7,7 +7,6 @@ import React, { useEffect } from 'react'; + import { __ } from '@wordpress/i18n'; + import { TableCard, TableCardColumn } from '@woocommerce/components'; + import { onQueryChange, getQuery } from '@woocommerce/navigation'; +-import { dateI18n } from '@wordpress/date'; + import moment from 'moment'; + + /** +@@ -21,6 +20,7 @@ import { formatExplicitCurrency } from 'multi-currency/interface/functions'; + import RiskLevel, { calculateRiskMapping } from 'components/risk-level'; + import { recordEvent } from 'tracks'; + import CaptureAuthorizationButton from 'wcpay/components/capture-authorization-button'; ++import { formatUserDateTime } from 'wcpay/utils/date-time'; + + interface Column extends TableCardColumn { + key: +@@ -130,35 +130,23 @@ export const AuthorizationsList = (): JSX.Element => { + display: auth.payment_intent_id, + }, + created: { +- value: dateI18n( +- 'M j, Y / g:iA', +- moment.utc( auth.created ).local().toISOString() ++ value: formatUserDateTime( ++ moment.utc( auth.created ).toISOString() + ), + display: clickable( +- dateI18n( +- 'M j, Y / g:iA', +- moment.utc( auth.created ).local().toISOString() ++ formatUserDateTime( ++ moment.utc( auth.created ).toISOString() + ) + ), + }, + // Payments are authorized for a maximum of 7 days + capture_by: { +- value: dateI18n( +- 'M j, Y / g:iA', +- moment +- .utc( auth.created ) +- .add( 7, 'd' ) +- .local() +- .toISOString() ++ value: formatUserDateTime( ++ moment.utc( auth.created ).add( 7, 'd' ).toISOString() + ), + display: clickable( +- dateI18n( +- 'M j, Y / g:iA', +- moment +- .utc( auth.created ) +- .add( 7, 'd' ) +- .local() +- .toISOString() ++ formatUserDateTime( ++ moment.utc( auth.created ).add( 7, 'd' ).toISOString() + ) + ), + }, +diff --git a/client/transactions/uncaptured/test/index.test.tsx b/client/transactions/uncaptured/test/index.test.tsx +index 52f76dcc882..fc21a532b67 100644 +--- a/client/transactions/uncaptured/test/index.test.tsx ++++ b/client/transactions/uncaptured/test/index.test.tsx +@@ -67,6 +67,8 @@ declare const global: { + precision: number; + }; + }; ++ dateFormat: string; ++ timeFormat: string; + }; + }; + +@@ -126,6 +128,8 @@ describe( 'Authorizations list', () => { + precision: 2, + }, + }, ++ dateFormat: 'M j, Y', ++ timeFormat: 'g:iA', + }; + } ); + +diff --git a/client/utils/date-time.ts b/client/utils/date-time.ts +new file mode 100644 +index 00000000000..61ae1a046a0 +--- /dev/null ++++ b/client/utils/date-time.ts +@@ -0,0 +1,48 @@ ++/** ++ * External dependencies ++ */ ++import { dateI18n } from '@wordpress/date'; ++ ++type DateTimeFormat = string | null; ++ ++interface FormatDateTimeOptions { ++ includeTime?: boolean; // Whether to include time in the formatted string (defaults to true) ++ useGmt?: boolean; // Whether to display the time in GMT/UTC (defaults to false) ++ separator?: string; // Separator between date and time (defaults to ' / ') ++ customFormat?: DateTimeFormat; // Custom format to use instead of WordPress settings ++} ++ ++/** ++ * Formats a date and time string according to WordPress settings or a custom format. ++ * ++ * @param { string | Date } dateTime - The date and time string (e.g., '2024-10-23 15:28:26') or a JS Date object. ++ * @param { FormatDateTimeOptions } options - Additional options to control time inclusion and whether to use GMT/UTC. ++ * @return { string } - The formatted date and time string. ++ */ ++export function formatUserDateTime( ++ dateTime: string | Date, ++ options: FormatDateTimeOptions = { ++ includeTime: true, ++ useGmt: false, ++ separator: ' / ', ++ customFormat: null, ++ } ++): string { ++ const { ++ customFormat = null, ++ includeTime = true, ++ useGmt = false, ++ separator = ' / ', ++ } = options; ++ ++ // Use the WordPress settings for date and time format if no custom format is provided ++ const format = ++ customFormat || ++ `${ window.wcpaySettings.dateFormat }${ ++ includeTime ++ ? `${ separator }${ window.wcpaySettings.timeFormat }` ++ : '' ++ }`; ++ ++ return dateI18n( format, dateTime, useGmt ); ++} +diff --git a/client/utils/test/date-time.test.ts b/client/utils/test/date-time.test.ts +new file mode 100644 +index 00000000000..ae1bc2e890b +--- /dev/null ++++ b/client/utils/test/date-time.test.ts +@@ -0,0 +1,115 @@ ++/** ++ * Internal dependencies ++ */ ++import { formatUserDateTime } from 'wcpay/utils/date-time'; ++import { dateI18n } from '@wordpress/date'; ++ ++describe( 'formatUserDateTime', () => { ++ const originalWcpaySettings = window.wcpaySettings; ++ const mockWcpaySettings = { ++ dateFormat: 'Y-m-d', ++ timeFormat: 'H:i', ++ }; ++ ++ beforeAll( () => { ++ window.wcpaySettings = mockWcpaySettings as typeof wcpaySettings; ++ } ); ++ ++ afterAll( () => { ++ window.wcpaySettings = originalWcpaySettings; ++ } ); ++ ++ describe( 'with string input', () => { ++ it( 'should format using default WordPress settings', () => { ++ const dateTime = '2024-10-23 15:28:26'; ++ const formatted = formatUserDateTime( dateTime ); ++ ++ expect( formatted ).toBe( '2024-10-23 / 15:28' ); ++ } ); ++ ++ it( 'should use custom format if provided', () => { ++ const dateTime = '2024-10-23 15:28:26'; ++ const options = { customFormat: 'd-m-Y H:i:s' }; ++ const formatted = formatUserDateTime( dateTime, options ); ++ ++ expect( formatted ).toBe( '23-10-2024 15:28:26' ); ++ } ); ++ ++ it( 'should exclude time if includeTime is set to false', () => { ++ const dateTime = '2024-10-23 15:28:26'; ++ const options = { includeTime: false }; ++ const formatted = formatUserDateTime( dateTime, options ); ++ ++ expect( formatted ).toBe( '2024-10-23' ); ++ } ); ++ ++ it( 'should use custom separator when provided', () => { ++ const dateTime = '2024-10-23 15:28:26'; ++ const options = { separator: ' - ' }; ++ const formatted = formatUserDateTime( dateTime, options ); ++ ++ // Expect output with custom separator ++ expect( formatted ).toBe( '2024-10-23 - 15:28' ); ++ } ); ++ ++ it( 'should handle GMT/UTC setting correctly when useGmt is true', () => { ++ const dateTime = '2024-10-23 15:28:26Z'; ++ const options = { useGmt: true }; // Enforcing UTC ++ const formatted = formatUserDateTime( dateTime, options ); ++ ++ // Expect UTC-based output (no timezone adjustment) ++ expect( formatted ).toBe( '2024-10-23 / 15:28' ); ++ } ); ++ } ); ++ ++ describe( 'with Date object input', () => { ++ it( 'should format using default WordPress settings', () => { ++ const dateTime = new Date( Date.UTC( 2024, 9, 23, 15, 28, 26 ) ); ++ const formatted = formatUserDateTime( dateTime, { useGmt: true } ); ++ ++ // Expected format based on WordPress settings and Date object ++ expect( formatted ).toBe( '2024-10-23 / 15:28' ); ++ } ); ++ ++ it( 'should use custom format if provided', () => { ++ const dateTime = new Date( Date.UTC( 2024, 9, 23, 15, 28, 26 ) ); ++ const options = { customFormat: 'd-m-Y H:i:s', useGmt: true }; ++ const formatted = formatUserDateTime( dateTime, options ); ++ ++ // Expected format for Date object: '23-10-2024 15:28:26' ++ expect( formatted ).toBe( '23-10-2024 15:28:26' ); ++ } ); ++ ++ it( 'should exclude time if includeTime is set to false', () => { ++ const dateTime = new Date( Date.UTC( 2024, 9, 23, 15, 28, 26 ) ); ++ const options = { includeTime: false, useGmt: true }; ++ const formatted = formatUserDateTime( dateTime, options ); ++ ++ // Expect output with date only ++ expect( formatted ).toBe( '2024-10-23' ); ++ } ); ++ ++ it( 'should handle GMT/UTC setting correctly', () => { ++ const dateTime = new Date( 2024, 9, 23, 15, 28, 26 ); // Local time (non-UTC) ++ const formatted = formatUserDateTime( dateTime ); ++ ++ // The expected format will vary based on your local timezone ++ const expectedFormat = dateI18n( ++ `${ mockWcpaySettings.dateFormat } / ${ mockWcpaySettings.timeFormat }`, ++ dateTime, ++ false ++ ); ++ ++ expect( formatted ).toBe( expectedFormat ); ++ } ); ++ ++ it( 'should use custom separator when provided', () => { ++ const dateTime = new Date( Date.UTC( 2024, 9, 23, 15, 28, 26 ) ); ++ const options = { separator: ' - ', useGmt: true }; ++ const formatted = formatUserDateTime( dateTime, options ); ++ ++ // Expect output with custom separator ++ expect( formatted ).toBe( '2024-10-23 - 15:28' ); ++ } ); ++ } ); ++} ); +diff --git a/includes/admin/class-wc-payments-admin.php b/includes/admin/class-wc-payments-admin.php +index 9c53cb48597..eb6a6e9fd7c 100644 +--- a/includes/admin/class-wc-payments-admin.php ++++ b/includes/admin/class-wc-payments-admin.php +@@ -958,6 +958,8 @@ private function get_js_settings(): array { + 'lifetimeTPV' => $this->account->get_lifetime_total_payment_volume(), + 'defaultExpressCheckoutBorderRadius' => WC_Payments_Express_Checkout_Button_Handler::DEFAULT_BORDER_RADIUS_IN_PX, + 'isWooPayGlobalThemeSupportEligible' => WC_Payments_Features::is_woopay_global_theme_support_eligible(), ++ 'dateFormat' => wc_date_format(), ++ 'timeFormat' => get_option( 'time_format' ), + ]; + + return apply_filters( 'wcpay_js_settings', $this->wcpay_js_settings ); +diff --git a/multi-currency/client/settings/single-currency/index.js b/multi-currency/client/settings/single-currency/index.js +index 14a3560f14d..fc063ce3211 100644 +--- a/multi-currency/client/settings/single-currency/index.js ++++ b/multi-currency/client/settings/single-currency/index.js +@@ -3,7 +3,6 @@ + * External dependencies + */ + import React, { useContext, useEffect, useState } from 'react'; +-import { dateI18n } from '@wordpress/date'; + import { sprintf, __ } from '@wordpress/i18n'; + import moment from 'moment'; + +@@ -24,7 +23,6 @@ import { + useCurrencies, + useCurrencySettings, + useEnabledCurrencies, +- useStoreSettings, + } from 'multi-currency/data'; + import MultiCurrencySettingsContext from 'multi-currency/context'; + import { +@@ -32,6 +30,7 @@ import { + SettingsLayout, + SettingsSection, + } from 'multi-currency/interface/components'; ++import { formatUserDateTime } from 'wcpay/utils/date-time'; + + const SingleCurrencySettings = () => { + const { +@@ -45,7 +44,6 @@ const SingleCurrencySettings = () => { + + const { currencies } = useCurrencies(); + const { enabledCurrencies } = useEnabledCurrencies(); +- const { storeSettings } = useStoreSettings(); + + const { + currencySettings, +@@ -105,13 +103,10 @@ const SingleCurrencySettings = () => { + } + }, [ currencySettings, currency, initialPriceRoundingType ] ); + +- const dateFormat = storeSettings.date_format ?? 'M j, Y'; +- const timeFormat = storeSettings.time_format ?? 'g:iA'; +- + const formattedLastUpdatedDateTime = targetCurrency +- ? dateI18n( +- `${ dateFormat } ${ timeFormat }`, +- moment.unix( targetCurrency.last_updated ).toISOString() ++ ? formatUserDateTime( ++ moment.unix( targetCurrency.last_updated ).toISOString(), ++ { useGmt: false, separator: ' ' } + ) + : ''; + +diff --git a/multi-currency/client/settings/single-currency/test/index.test.js b/multi-currency/client/settings/single-currency/test/index.test.js +index 0559953e54b..b9a401c3946 100644 +--- a/multi-currency/client/settings/single-currency/test/index.test.js ++++ b/multi-currency/client/settings/single-currency/test/index.test.js +@@ -159,6 +159,8 @@ describe( 'Single currency settings screen', () => { + precision: 2, + }, + }, ++ dateFormat: 'M j, Y', ++ timeFormat: 'g:iA', + }; + } ); + From 25bf46bfab5df53aa6b3a3c67dffa7110840d3bd Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Tue, 29 Oct 2024 16:55:45 +0100 Subject: [PATCH 43/76] Delete file --- mydiff | 1892 -------------------------------------------------------- 1 file changed, 1892 deletions(-) delete mode 100644 mydiff diff --git a/mydiff b/mydiff deleted file mode 100644 index c2bc12b4bf5..00000000000 --- a/mydiff +++ /dev/null @@ -1,1892 +0,0 @@ -diff --git a/client/capital/index.tsx b/client/capital/index.tsx -index 81e76ad91b4..dea559ba74e 100644 ---- a/client/capital/index.tsx -+++ b/client/capital/index.tsx -@@ -6,7 +6,6 @@ - import * as React from 'react'; - import { __, _n } from '@wordpress/i18n'; - import { TableCard } from '@woocommerce/components'; --import { dateI18n } from '@wordpress/date'; - - /** - * Internal dependencies. -@@ -25,6 +24,7 @@ import Chip from 'components/chip'; - import { useLoans } from 'wcpay/data'; - import { getAdminUrl } from 'wcpay/utils'; - import './style.scss'; -+import { formatUserDateTime } from 'wcpay/utils/date-time'; - - const columns = [ - { -@@ -80,7 +80,7 @@ const getLoanStatusText = ( loan: CapitalLoan ) => { - return loan.fully_paid_at - ? __( 'Paid off', 'woocommerce-payments' ) + - ': ' + -- dateI18n( 'M j, Y', loan.fully_paid_at ) -+ formatUserDateTime( loan.fully_paid_at, { includeTime: false } ) - : __( 'Active', 'woocommerce-payments' ); - }; - -@@ -112,7 +112,11 @@ const getRowsData = ( loans: CapitalLoan[] ) => - const data = { - paid_out_at: { - value: loan.paid_out_at, -- display: clickable( dateI18n( 'M j, Y', loan.paid_out_at ) ), -+ display: clickable( -+ formatUserDateTime( loan.paid_out_at, { -+ includeTime: false, -+ } ) -+ ), - }, - status: { - value: getLoanStatusText( loan ), -@@ -150,7 +154,9 @@ const getRowsData = ( loans: CapitalLoan[] ) => - value: loan.first_paydown_at, - display: clickable( - loan.first_paydown_at -- ? dateI18n( 'M j, Y', loan.first_paydown_at ) -+ ? formatUserDateTime( loan.first_paydown_at, { -+ includeTime: false, -+ } ) - : '-' - ), - }, -diff --git a/client/capital/test/index.test.tsx b/client/capital/test/index.test.tsx -index f9eb43b8a49..41ea917902a 100644 ---- a/client/capital/test/index.test.tsx -+++ b/client/capital/test/index.test.tsx -@@ -25,6 +25,7 @@ declare const global: { - accountLoans: { - has_active_loan: boolean; - }; -+ dateFormat: string; - }; - }; - -@@ -37,6 +38,7 @@ describe( 'CapitalPage', () => { - }, - accountLoans: { has_active_loan: true }, - testMode: true, -+ dateFormat: 'M j, Y', - }; - } ); - -diff --git a/client/components/account-status/account-fees/expiration-description.js b/client/components/account-status/account-fees/expiration-description.js -index 6be5b58681c..b9472c238b8 100644 ---- a/client/components/account-status/account-fees/expiration-description.js -+++ b/client/components/account-status/account-fees/expiration-description.js -@@ -4,13 +4,13 @@ - * External dependencies - */ - import { __, sprintf } from '@wordpress/i18n'; --import { dateI18n } from '@wordpress/date'; - import moment from 'moment'; - - /** - * Internal dependencies - */ - import { formatCurrency } from 'multi-currency/interface/functions'; -+import { formatUserDateTime } from 'wcpay/utils/date-time'; - - const ExpirationDescription = ( { - feeData: { volume_allowance: volumeAllowance, end_time: endTime, ...rest }, -@@ -26,7 +26,9 @@ const ExpirationDescription = ( { - 'woocommerce-payments' - ), - formatCurrency( volumeAllowance, currencyCode ), -- dateI18n( 'F j, Y', moment( endTime ).toISOString() ) -+ formatUserDateTime( moment( endTime ).toISOString(), { -+ includeTime: false, -+ } ) - ); - } else if ( volumeAllowance ) { - description = sprintf( -@@ -44,7 +46,9 @@ const ExpirationDescription = ( { - 'Discounted base fee expires on %1$s.', - 'woocommerce-payments' - ), -- dateI18n( 'F j, Y', moment( endTime ).toISOString() ) -+ formatUserDateTime( moment( endTime ).toISOString(), { -+ includeTime: false, -+ } ) - ); - } else { - return null; -diff --git a/client/components/account-status/account-fees/test/index.js b/client/components/account-status/account-fees/test/index.js -index 5258af4ffdc..7405b33e371 100644 ---- a/client/components/account-status/account-fees/test/index.js -+++ b/client/components/account-status/account-fees/test/index.js -@@ -46,6 +46,7 @@ describe( 'AccountFees', () => { - precision: 2, - }, - }, -+ dateFormat: 'F j, Y', - }; - } ); - -diff --git a/client/components/active-loan-summary/index.tsx b/client/components/active-loan-summary/index.tsx -index 0c5059ef87c..af817c7331d 100755 ---- a/client/components/active-loan-summary/index.tsx -+++ b/client/components/active-loan-summary/index.tsx -@@ -13,7 +13,6 @@ import { - } from '@wordpress/components'; - import { __, sprintf } from '@wordpress/i18n'; - import { createInterpolateElement } from '@wordpress/element'; --import { dateI18n } from '@wordpress/date'; - - /** - * Internal dependencies. -@@ -24,6 +23,7 @@ import { useActiveLoanSummary } from 'wcpay/data'; - import { getAdminUrl } from 'wcpay/utils'; - - import './style.scss'; -+import { formatUserDateTime } from 'wcpay/utils/date-time'; - - const Block = ( { - title, -@@ -210,12 +210,12 @@ const ActiveLoanSummary = (): JSX.Element => { - 'Repaid this period (until %s)', - 'woocommerce-payments' - ), -- dateI18n( -- 'M j, Y', -+ formatUserDateTime( - new Date( - details.current_repayment_interval.due_at * - 1000 -- ) -+ ), -+ { includeTime: false, useGmt: true } - ) - ) } - > -@@ -251,9 +251,9 @@ const ActiveLoanSummary = (): JSX.Element => { - -- { dateI18n( -- 'M j, Y', -- new Date( details.advance_paid_out_at * 1000 ) -+ { formatUserDateTime( -+ new Date( details.advance_paid_out_at * 1000 ), -+ { includeTime: false, useGmt: true } - ) } - - { - -- { dateI18n( -- 'M j, Y', -- new Date( details.repayments_begin_at * 1000 ) -+ { formatUserDateTime( -+ new Date( details.repayments_begin_at * 1000 ), -+ { includeTime: false, useGmt: true } - ) } - - -diff --git a/client/components/active-loan-summary/test/__snapshots__/index.js.snap b/client/components/active-loan-summary/test/__snapshots__/index.js.snap -index 4424415245c..4e9dd15ec13 100644 ---- a/client/components/active-loan-summary/test/__snapshots__/index.js.snap -+++ b/client/components/active-loan-summary/test/__snapshots__/index.js.snap -@@ -74,7 +74,7 @@ exports[`Active loan summary renders correctly 1`] = ` -
-- Repaid this period (until Feb 14, 2022) -+ Repaid this period (until Feb 15, 2022) -
-
{ - precision: 2, - }, - }, -+ dateFormat: 'M j, Y', - }; - } ); - afterEach( () => { -diff --git a/client/components/deposits-overview/test/index.tsx b/client/components/deposits-overview/test/index.tsx -index 4853aff7bf5..509bf1d3d42 100644 ---- a/client/components/deposits-overview/test/index.tsx -+++ b/client/components/deposits-overview/test/index.tsx -@@ -68,6 +68,7 @@ declare const global: { - connect: { - country: string; - }; -+ dateFormat: string; - }; - }; - -@@ -231,6 +232,7 @@ describe( 'Deposits Overview information', () => { - precision: 2, - }, - }, -+ dateFormat: 'F j, Y', - }; - mockUseDepositIncludesLoan.mockReturnValue( { - includesFinancingPayout: false, -diff --git a/client/components/disputed-order-notice/index.js b/client/components/disputed-order-notice/index.js -index ab51a52d16e..892f0313cf0 100644 ---- a/client/components/disputed-order-notice/index.js -+++ b/client/components/disputed-order-notice/index.js -@@ -1,7 +1,6 @@ - import moment from 'moment'; - import React, { useEffect } from 'react'; - import { __, _n, sprintf } from '@wordpress/i18n'; --import { dateI18n } from '@wordpress/date'; - import { createInterpolateElement } from '@wordpress/element'; - - /** -@@ -20,6 +19,7 @@ import { - import { useCharge } from 'wcpay/data'; - import { recordEvent } from 'tracks'; - import './style.scss'; -+import { formatUserDateTime } from 'wcpay/utils/date-time'; - - const DisputedOrderNoticeHandler = ( { chargeId, onDisableOrderRefund } ) => { - const { data: charge } = useCharge( chargeId ); -@@ -131,7 +131,9 @@ const UrgentDisputeNoticeBody = ( { - formatString, - formattedAmount, - reasons[ disputeReason ].display, -- dateI18n( 'M j, Y', dueBy.local().toISOString() ) -+ formatUserDateTime( dueBy.local().toISOString(), { -+ includeTime: false, -+ } ) - ); - - let suffix = sprintf( -@@ -182,7 +184,9 @@ const RegularDisputeNoticeBody = ( { - const suffix = sprintf( - // Translators: %1$s is the dispute due date. - __( 'Please respond before %1$s.', 'woocommerce-payments' ), -- dateI18n( 'M j, Y', dueBy.local().toISOString() ) -+ formatUserDateTime( dueBy.local().toISOString(), { -+ includeTime: false, -+ } ) - ); - - return ( -diff --git a/client/components/disputed-order-notice/test/index.test.js b/client/components/disputed-order-notice/test/index.test.js -index 7e44da132e0..784092295f3 100644 ---- a/client/components/disputed-order-notice/test/index.test.js -+++ b/client/components/disputed-order-notice/test/index.test.js -@@ -36,6 +36,7 @@ describe( 'DisputedOrderNoticeHandler', () => { - connect: { - country: 'US', - }, -+ dateFormat: 'M j, Y', - }; - useCharge.mockReturnValue( { data: mockCharge } ); - } ); -diff --git a/client/deposits/details/index.tsx b/client/deposits/details/index.tsx -index 74aec3f2a7d..e3f4cc0afc9 100644 ---- a/client/deposits/details/index.tsx -+++ b/client/deposits/details/index.tsx -@@ -4,7 +4,6 @@ - * External dependencies - */ - import React from 'react'; --import { dateI18n } from '@wordpress/date'; - import { __, sprintf } from '@wordpress/i18n'; - import moment from 'moment'; - import { -@@ -40,6 +39,7 @@ import { - } from 'multi-currency/interface/functions'; - import { displayStatus } from '../strings'; - import './style.scss'; -+import { formatUserDateTime } from 'wcpay/utils/date-time'; - - /** - * Renders the deposit status indicator UI, re-purposing the OrderStatus component from @woocommerce/components. -@@ -111,11 +111,10 @@ export const DepositOverview: React.FC< DepositOverviewProps > = ( { - key="depositDate" - label={ - `${ depositDateLabel }: ` + -- dateI18n( -- 'M j, Y', -- moment.utc( deposit.date ).toISOString(), -- true // TODO Change call to gmdateI18n and remove this deprecated param once WP 5.4 support ends. -- ) -+ formatUserDateTime( moment.utc( deposit.date ).toISOString(), { -+ useGmt: true, -+ includeTime: false, -+ } ) - } - value={ } - detail={ deposit.bankAccount } -diff --git a/client/deposits/details/test/index.tsx b/client/deposits/details/test/index.tsx -index f6fd4d98116..385e896f173 100644 ---- a/client/deposits/details/test/index.tsx -+++ b/client/deposits/details/test/index.tsx -@@ -32,6 +32,7 @@ declare const global: { - connect: { - country: string; - }; -+ dateFormat: string; - }; - wcSettings: { countries: Record< string, string > }; - }; -@@ -54,6 +55,7 @@ describe( 'Deposit overview', () => { - precision: 2, - }, - }, -+ dateFormat: 'M j, Y', - }; - } ); - -diff --git a/client/deposits/list/index.tsx b/client/deposits/list/index.tsx -index 03789c466e1..cd948034acc 100644 ---- a/client/deposits/list/index.tsx -+++ b/client/deposits/list/index.tsx -@@ -7,7 +7,6 @@ import { DepositsTableHeader } from 'wcpay/types/deposits'; - import React, { useState } from 'react'; - import { recordEvent } from 'tracks'; - import { useMemo } from '@wordpress/element'; --import { dateI18n } from '@wordpress/date'; - import { __, _n, sprintf } from '@wordpress/i18n'; - import moment from 'moment'; - import { TableCard, Link } from '@woocommerce/components'; -@@ -48,6 +47,7 @@ import { ReportingExportLanguageHook } from 'wcpay/settings/reporting-settings/i - - import './style.scss'; - import { parseInt } from 'lodash'; -+import { formatUserDateTime } from 'wcpay/utils/date-time'; - - const getColumns = ( sortByDate?: boolean ): DepositsTableHeader[] => [ - { -@@ -135,10 +135,12 @@ export const DepositsList = (): JSX.Element => { - href={ getDetailsURL( deposit.id, 'deposits' ) } - onClick={ () => recordEvent( 'wcpay_deposits_row_click' ) } - > -- { dateI18n( -- 'M j, Y', -+ { formatUserDateTime( - moment.utc( deposit.date ).toISOString(), -- true // TODO Change call to gmdateI18n and remove this deprecated param once WP 5.4 support ends. -+ { -+ includeTime: false, -+ useGmt: true, -+ } - ) } - - ); -@@ -328,10 +330,9 @@ export const DepositsList = (): JSX.Element => { - row[ 0 ], - { - ...row[ 1 ], -- value: dateI18n( -- 'Y-m-d', -+ value: formatUserDateTime( - moment.utc( row[ 1 ].value ).toISOString(), -- true -+ { useGmt: true, includeTime: false } - ), - }, - ...row.slice( 2 ), -diff --git a/client/deposits/list/test/__snapshots__/index.tsx.snap b/client/deposits/list/test/__snapshots__/index.tsx.snap -index 5d6d3d1b96c..64b57b8c38d 100644 ---- a/client/deposits/list/test/__snapshots__/index.tsx.snap -+++ b/client/deposits/list/test/__snapshots__/index.tsx.snap -@@ -345,7 +345,7 @@ exports[`Deposits list renders correctly a single deposit 1`] = ` - data-link-type="wc-admin" - href="admin.php?page=wc-admin&path=%2Fpayments%2Fdeposits%2Fdetails&id=po_mock1" - > -- Jan 2, 2020 -+ Jan 2 2020 - - - -- Jan 3, 2020 -+ Jan 3 2020 - - - -- Jan 2, 2020 -+ Jan 2 2020 - - - -- Jan 3, 2020 -+ Jan 3 2020 - - - { - reporting: { - exportModalDismissed: true, - }, -+ dateFormat: 'M j Y', - }; - } ); - -@@ -308,7 +310,7 @@ describe( 'Deposits list', () => { - // 2. The indexOf check in amount's expect is because the amount in CSV may not contain - // trailing zeros as in the display amount. - // -- expect( formatDate( csvFirstDeposit[ 1 ], 'M j, Y' ) ).toBe( -+ expect( csvFirstDeposit[ 1 ].replace( /^"|"$/g, '' ) ).toBe( - displayFirstDeposit[ 0 ] - ); // date - expect( csvFirstDeposit[ 2 ] ).toBe( displayFirstDeposit[ 1 ] ); // type -diff --git a/client/deposits/utils/index.ts b/client/deposits/utils/index.ts -index 3d8fd6276e1..a3bf14e82a3 100644 ---- a/client/deposits/utils/index.ts -+++ b/client/deposits/utils/index.ts -@@ -2,21 +2,20 @@ - * External dependencies - */ - import { __ } from '@wordpress/i18n'; --import { dateI18n } from '@wordpress/date'; - import moment from 'moment'; -- --const formatDate = ( format: string, date: number | string ) => -- dateI18n( -- format, -- moment.utc( date ).toISOString(), -- true // TODO Change call to gmdateI18n and remove this deprecated param once WP 5.4 support ends. -- ); -+import { formatUserDateTime } from 'wcpay/utils/date-time'; - - interface DepositObject { - date: number | string; - } -+ - export const getDepositDate = ( deposit?: DepositObject | null ): string => -- deposit ? formatDate( 'F j, Y', deposit?.date ) : '—'; -+ deposit -+ ? formatUserDateTime( moment.utc( deposit?.date ).toISOString(), { -+ includeTime: false, -+ useGmt: true, -+ } ) -+ : '—'; - - interface GetDepositMonthlyAnchorLabelProps { - monthlyAnchor: number; -diff --git a/client/deposits/utils/test/index.ts b/client/deposits/utils/test/index.ts -index d0361137104..9b58bddf898 100644 ---- a/client/deposits/utils/test/index.ts -+++ b/client/deposits/utils/test/index.ts -@@ -8,7 +8,19 @@ import momentLib from 'moment'; - */ - import { getDepositDate, getDepositMonthlyAnchorLabel } from '../'; - -+declare const global: { -+ wcpaySettings: { -+ dateFormat: string; -+ }; -+}; -+ - describe( 'Deposits Overview Utils / getDepositDate', () => { -+ beforeEach( () => { -+ global.wcpaySettings = { -+ dateFormat: 'F j, Y', -+ }; -+ } ); -+ - test( 'returns a display value without a deposit', () => { - expect( getDepositDate() ).toEqual( '—' ); - } ); -diff --git a/client/disputes/evidence/test/index.js b/client/disputes/evidence/test/index.js -index e9ccdb826cb..142ee738ab8 100644 ---- a/client/disputes/evidence/test/index.js -+++ b/client/disputes/evidence/test/index.js -@@ -96,6 +96,7 @@ describe( 'Dispute evidence form', () => { - - global.wcpaySettings = { - restUrl: 'http://example.com/wp-json/', -+ dateFormat: 'M j, Y', - }; - } ); - afterEach( () => { -@@ -190,6 +191,8 @@ describe( 'Dispute evidence page', () => { - precision: 2, - }, - }, -+ dateFormat: 'M j, Y', -+ timeFormat: 'g:iA', - }; - } ); - -diff --git a/client/disputes/index.tsx b/client/disputes/index.tsx -index 060afccce35..11515987b58 100644 ---- a/client/disputes/index.tsx -+++ b/client/disputes/index.tsx -@@ -5,7 +5,6 @@ - */ - import React, { useState } from 'react'; - import { recordEvent } from 'tracks'; --import { dateI18n } from '@wordpress/date'; - import { _n, __, sprintf } from '@wordpress/i18n'; - import moment from 'moment'; - import { Button } from '@wordpress/components'; -@@ -58,6 +57,7 @@ import CSVExportModal from 'components/csv-export-modal'; - import { ReportingExportLanguageHook } from 'wcpay/settings/reporting-settings/interfaces'; - - import './style.scss'; -+import { formatUserDateTime } from 'wcpay/utils/date-time'; - - const getHeaders = ( sortColumn?: string ): DisputesTableHeader[] => [ - { -@@ -201,10 +201,7 @@ const smartDueDate = ( dispute: CachedDispute ) => { - - ); - } -- return dateI18n( -- 'M j, Y / g:iA', -- moment.utc( dispute.due_by ).local().toISOString() -- ); -+ return formatUserDateTime( moment.utc( dispute.due_by ).toISOString() ); - }; - - export const DisputesList = (): JSX.Element => { -@@ -301,9 +298,8 @@ export const DisputesList = (): JSX.Element => { - created: { - value: dispute.created, - display: clickable( -- dateI18n( -- 'M j, Y', -- moment( dispute.created ).toISOString() -+ formatUserDateTime( -+ moment.utc( dispute.created ).toISOString() - ) - ), - }, -@@ -483,17 +479,18 @@ export const DisputesList = (): JSX.Element => { - { - // Disputed On. - ...row[ 10 ], -- value: dateI18n( -- 'Y-m-d', -- moment( row[ 10 ].value ).toISOString() -+ value: formatUserDateTime( -+ moment.utc( row[ 10 ].value ).toISOString(), -+ { -+ includeTime: false, -+ } - ), - }, - { - // Respond by. - ...row[ 11 ], -- value: dateI18n( -- 'Y-m-d / g:iA', -- moment( row[ 11 ].value ).toISOString() -+ value: formatUserDateTime( -+ moment.utc( row[ 11 ].value ).toISOString() - ), - }, - ]; -diff --git a/client/disputes/info/index.tsx b/client/disputes/info/index.tsx -index 12f7ba0e64a..a6f6ecaad06 100644 ---- a/client/disputes/info/index.tsx -+++ b/client/disputes/info/index.tsx -@@ -5,7 +5,6 @@ - */ - import * as React from 'react'; - import { __ } from '@wordpress/i18n'; --import { dateI18n } from '@wordpress/date'; - import moment from 'moment'; - import { Link } from '@woocommerce/components'; - -@@ -20,6 +19,7 @@ import { formatExplicitCurrency } from 'multi-currency/interface/functions'; - import './style.scss'; - import Loadable from 'components/loadable'; - import { Dispute } from 'wcpay/types/disputes'; -+import { formatUserDateTime } from 'wcpay/utils/date-time'; - - const fields: { key: string; label: string }[] = [ - { key: 'created', label: __( 'Dispute date', 'woocommerce-payments' ) }, -@@ -69,20 +69,20 @@ const Info = ( { - transactionId: __( 'Transaction link', 'woocommerce-payments' ), - } - : { -- created: dateI18n( -- 'M j, Y', -- moment( dispute.created * 1000 ).toISOString() -+ created: formatUserDateTime( -+ moment( dispute.created * 1000 ).toISOString(), -+ { includeTime: false } - ), - amount: formatExplicitCurrency( - dispute.amount || 0, - dispute.currency || 'USD' - ), - dueBy: dispute.evidence_details -- ? dateI18n( -- 'M j, Y - g:iA', -+ ? formatUserDateTime( - moment( - dispute.evidence_details.due_by * 1000 -- ).toISOString() -+ ).toISOString(), -+ { separator: ' - ' } - ) - : null, - reason: composeDisputeReason( dispute ), -diff --git a/client/disputes/info/test/index.tsx b/client/disputes/info/test/index.tsx -index 1e2f5dcb8b0..7b49b588052 100644 ---- a/client/disputes/info/test/index.tsx -+++ b/client/disputes/info/test/index.tsx -@@ -29,6 +29,8 @@ declare const global: { - precision: number; - }; - }; -+ dateFormat: string; -+ timeFormat: string; - }; - }; - -@@ -49,6 +51,8 @@ describe( 'Dispute info', () => { - precision: 2, - }, - }, -+ dateFormat: 'M j, Y', -+ timeFormat: 'g:iA', - }; - } ); - -diff --git a/client/disputes/test/index.tsx b/client/disputes/test/index.tsx -index 1409bfc852d..52af84da080 100644 ---- a/client/disputes/test/index.tsx -+++ b/client/disputes/test/index.tsx -@@ -17,13 +17,15 @@ import { - useReportingExportLanguage, - useSettings, - } from 'data/index'; --import { formatDate, getUnformattedAmount } from 'wcpay/utils/test-utils'; -+import { getUnformattedAmount } from 'wcpay/utils/test-utils'; - import React from 'react'; - import { - CachedDispute, - DisputeReason, - DisputeStatus, - } from 'wcpay/types/disputes'; -+import { formatUserDateTime } from 'wcpay/utils/date-time'; -+import moment from 'moment'; - - jest.mock( '@woocommerce/csv-export', () => { - const actualModule = jest.requireActual( '@woocommerce/csv-export' ); -@@ -100,6 +102,8 @@ declare const global: { - reporting?: { - exportModalDismissed: boolean; - }; -+ dateFormat?: string; -+ timeFormat?: string; - }; - }; - -@@ -198,6 +202,8 @@ describe( 'Disputes list', () => { - reporting: { - exportModalDismissed: true, - }, -+ dateFormat: 'Y-m-d', -+ timeFormat: 'g:iA', - }; - } ); - -@@ -363,8 +369,10 @@ describe( 'Disputes list', () => { - `"${ displayFirstDispute[ 5 ] }"` - ); // customer - -- expect( formatDate( csvFirstDispute[ 11 ], 'Y-m-d / g:iA' ) ).toBe( -- formatDate( displayFirstDispute[ 6 ], 'Y-m-d / g:iA' ) -+ expect( csvFirstDispute[ 11 ].replace( /^"|"$/g, '' ) ).toBe( -+ formatUserDateTime( -+ moment.utc( mockDisputes[ 0 ].due_by ).toISOString() -+ ) - ); // date respond by - } ); - } ); -diff --git a/client/documents/list/index.tsx b/client/documents/list/index.tsx -index 307c3faf0c6..a6aacd9c4a2 100644 ---- a/client/documents/list/index.tsx -+++ b/client/documents/list/index.tsx -@@ -4,7 +4,6 @@ - * External dependencies - */ - import React, { useCallback, useEffect, useState } from 'react'; --import { dateI18n } from '@wordpress/date'; - import { __, _n, sprintf } from '@wordpress/i18n'; - import moment from 'moment'; - import { TableCard, TableCardColumn } from '@woocommerce/components'; -@@ -21,6 +20,7 @@ import DocumentsFilters from '../filters'; - import Page from '../../components/page'; - import { getDocumentUrl } from 'wcpay/utils'; - import VatFormModal from 'wcpay/vat/form-modal'; -+import { formatUserDateTime } from 'wcpay/utils/date-time'; - - interface Column extends TableCardColumn { - key: 'date' | 'type' | 'description' | 'download'; -@@ -68,15 +68,15 @@ const getDocumentDescription = ( document: Document ) => { - if ( document.period_from && document.period_to ) { - return sprintf( - __( 'VAT invoice for %s to %s', 'woocommerce-payments' ), -- dateI18n( -- 'M j, Y', -+ formatUserDateTime( - moment.utc( document.period_from ).toISOString(), -- 'utc' -+ { includeTime: false } - ), -- dateI18n( -- 'M j, Y', -+ formatUserDateTime( - moment.utc( document.period_to ).toISOString(), -- 'utc' -+ { -+ includeTime: false, -+ } - ) - ); - } -@@ -180,9 +180,11 @@ export const DocumentsList = (): JSX.Element => { - const data = { - date: { - value: document.date, -- display: dateI18n( -- 'M j, Y', -- moment.utc( document.date ).local().toISOString() -+ display: formatUserDateTime( -+ moment.utc( document.date ).toISOString(), -+ { -+ includeTime: false, -+ } - ), - }, - type: { -diff --git a/client/documents/list/test/index.tsx b/client/documents/list/test/index.tsx -index c54cf7a02c8..0eac7e0bf61 100644 ---- a/client/documents/list/test/index.tsx -+++ b/client/documents/list/test/index.tsx -@@ -36,6 +36,7 @@ declare const global: { - accountStatus: { - hasSubmittedVatData: boolean; - }; -+ dateFormat: string; - }; - }; - -@@ -60,6 +61,11 @@ describe( 'Documents list', () => { - let container: Element; - let rerender: ( ui: React.ReactElement ) => void; - beforeEach( () => { -+ global.wcpaySettings = { -+ accountStatus: { hasSubmittedVatData: true }, -+ dateFormat: 'M j, Y', -+ }; -+ - mockUseDocuments.mockReturnValue( { - documents: getMockDocuments(), - isLoading: false, -@@ -200,6 +206,7 @@ describe( 'Document download button', () => { - beforeEach( () => { - global.wcpaySettings = { - accountStatus: { hasSubmittedVatData: true }, -+ dateFormat: 'M j, Y', - }; - - render( ); -@@ -223,6 +230,7 @@ describe( 'Document download button', () => { - beforeEach( () => { - global.wcpaySettings = { - accountStatus: { hasSubmittedVatData: false }, -+ dateFormat: 'M j, Y', - }; - - render( ); -@@ -293,6 +301,7 @@ describe( 'Direct document download', () => { - - global.wcpaySettings = { - accountStatus: { hasSubmittedVatData: true }, -+ dateFormat: 'M j, Y', - }; - } ); - -diff --git a/client/globals.d.ts b/client/globals.d.ts -index f00b521943a..9992f2faadb 100644 ---- a/client/globals.d.ts -+++ b/client/globals.d.ts -@@ -135,6 +135,8 @@ declare global { - isOverviewSurveySubmitted: boolean; - lifetimeTPV: number; - defaultExpressCheckoutBorderRadius: string; -+ dateFormat: string; -+ timeFormat: string; - }; - - const wc: { -diff --git a/client/overview/modal/update-business-details/index.tsx b/client/overview/modal/update-business-details/index.tsx -index 2c654a57f9b..fb98eca74f3 100644 ---- a/client/overview/modal/update-business-details/index.tsx -+++ b/client/overview/modal/update-business-details/index.tsx -@@ -3,7 +3,6 @@ - */ - import React, { useState } from 'react'; - import { Button, Modal, Notice } from '@wordpress/components'; --import { dateI18n } from '@wordpress/date'; - import { sprintf } from '@wordpress/i18n'; - import moment from 'moment'; - -@@ -13,6 +12,7 @@ import moment from 'moment'; - import strings from './strings'; - import './index.scss'; - import { recordEvent } from 'wcpay/tracks'; -+import { formatUserDateTime } from 'wcpay/utils/date-time'; - - interface Props { - errorMessages: Array< string >; -@@ -57,11 +57,15 @@ const UpdateBusinessDetailsModal = ( { - currentDeadline - ? sprintf( - strings.restrictedSoonDescription, -- dateI18n( -- 'ga M j, Y', -- moment( -- currentDeadline * 1000 -- ).toISOString() -+ formatUserDateTime( -+ moment -+ .utc( -+ currentDeadline * 1000 -+ ) -+ .toISOString(), -+ { -+ customFormat: 'ga M j, Y', -+ } - ) - ) - : strings.restrictedDescription } -diff --git a/client/overview/task-list/tasks/dispute-task.tsx b/client/overview/task-list/tasks/dispute-task.tsx -index 235b92696b9..f7be6ec8e13 100644 ---- a/client/overview/task-list/tasks/dispute-task.tsx -+++ b/client/overview/task-list/tasks/dispute-task.tsx -@@ -2,7 +2,6 @@ - * External dependencies - */ - import { __, sprintf } from '@wordpress/i18n'; --import { dateI18n } from '@wordpress/date'; - import moment from 'moment'; - import { getHistory } from '@woocommerce/navigation'; - -@@ -15,6 +14,7 @@ import { formatCurrency } from 'multi-currency/interface/functions'; - import { getAdminUrl } from 'wcpay/utils'; - import { recordEvent } from 'tracks'; - import { isDueWithin } from 'wcpay/disputes/utils'; -+import { formatUserDateTime } from 'wcpay/utils/date-time'; - - /** - * Returns an array of disputes that are due within the specified number of days. -@@ -142,9 +142,11 @@ export const getDisputeResolutionTask = ( - ? sprintf( - __( 'Respond today by %s', 'woocommerce-payments' ), - // Show due_by time in local timezone: e.g. "11:59 PM". -- dateI18n( -- 'g:i A', -- moment.utc( dispute.due_by ).local().toISOString() -+ formatUserDateTime( -+ moment.utc( dispute.due_by ).toISOString(), -+ { -+ customFormat: 'g:i A', -+ } - ) - ) - : sprintf( -@@ -153,9 +155,11 @@ export const getDisputeResolutionTask = ( - 'woocommerce-payments' - ), - // Show due_by date in local timezone: e.g. "Jan 1, 2021". -- dateI18n( -- 'M j, Y', -- moment.utc( dispute.due_by ).local().toISOString() -+ formatUserDateTime( -+ moment.utc( dispute.due_by ).toISOString(), -+ { -+ includeTime: false, -+ } - ), - moment( dispute.due_by ).fromNow( true ) // E.g. "2 days". - ); -diff --git a/client/overview/task-list/tasks/update-business-details-task.tsx b/client/overview/task-list/tasks/update-business-details-task.tsx -index a9746ec7b66..2ca90519616 100644 ---- a/client/overview/task-list/tasks/update-business-details-task.tsx -+++ b/client/overview/task-list/tasks/update-business-details-task.tsx -@@ -11,9 +11,9 @@ import { addQueryArgs } from '@wordpress/url'; - */ - import type { TaskItemProps } from '../types'; - import UpdateBusinessDetailsModal from 'wcpay/overview/modal/update-business-details'; --import { dateI18n } from '@wordpress/date'; - import moment from 'moment'; - import { recordEvent } from 'wcpay/tracks'; -+import { formatUserDateTime } from 'wcpay/utils/date-time'; - - export const getUpdateBusinessDetailsTask = ( - errorMessages: string[], -@@ -46,9 +46,11 @@ export const getUpdateBusinessDetailsTask = ( - 'Update by %s to avoid a disruption in deposits.', - 'woocommerce-payments' - ), -- dateI18n( -- 'ga M j, Y', -- moment( currentDeadline * 1000 ).toISOString() -+ formatUserDateTime( -+ moment( currentDeadline * 1000 ).toISOString(), -+ { -+ customFormat: 'ga M j, Y', -+ } - ) - ); - -diff --git a/client/overview/task-list/test/tasks.js b/client/overview/task-list/test/tasks.js -index 44b5de4e9f7..429ed175efb 100644 ---- a/client/overview/task-list/test/tasks.js -+++ b/client/overview/task-list/test/tasks.js -@@ -139,6 +139,7 @@ describe( 'getTasks()', () => { - precision: 2, - }, - }, -+ dateFormat: 'M j, Y', - }; - } ); - afterEach( () => { -diff --git a/client/payment-details/dispute-details/dispute-due-by-date.tsx b/client/payment-details/dispute-details/dispute-due-by-date.tsx -index 18993ef2387..697485c9d63 100644 ---- a/client/payment-details/dispute-details/dispute-due-by-date.tsx -+++ b/client/payment-details/dispute-details/dispute-due-by-date.tsx -@@ -2,10 +2,10 @@ - * External dependencies - */ - import React from 'react'; --import { dateI18n } from '@wordpress/date'; - import { __, _n, sprintf } from '@wordpress/i18n'; - import classNames from 'classnames'; - import moment from 'moment'; -+import { formatUserDateTime } from 'wcpay/utils/date-time'; - - const DisputeDueByDate: React.FC< { - dueBy: number; -@@ -14,9 +14,11 @@ const DisputeDueByDate: React.FC< { - const daysRemaining = Math.floor( - moment.unix( dueBy ).diff( moment(), 'days', true ) - ); -- const respondByDate = dateI18n( -- 'M j, Y, g:ia', -- moment( dueBy * 1000 ).toISOString() -+ const respondByDate = formatUserDateTime( -+ moment( dueBy * 1000 ).toISOString(), -+ { -+ separator: ', ', -+ } - ); - return ( - -diff --git a/client/payment-details/dispute-details/dispute-resolution-footer.tsx b/client/payment-details/dispute-details/dispute-resolution-footer.tsx -index 15fec759244..e6b38213abc 100644 ---- a/client/payment-details/dispute-details/dispute-resolution-footer.tsx -+++ b/client/payment-details/dispute-details/dispute-resolution-footer.tsx -@@ -3,7 +3,6 @@ - */ - import React from 'react'; - import moment from 'moment'; --import { dateI18n } from '@wordpress/date'; - import { __, sprintf } from '@wordpress/i18n'; - import { Link } from '@woocommerce/components'; - import { createInterpolateElement } from '@wordpress/element'; -@@ -17,13 +16,13 @@ import { recordEvent } from 'tracks'; - import { getAdminUrl } from 'wcpay/utils'; - import { getDisputeFeeFormatted } from 'wcpay/disputes/utils'; - import './style.scss'; -+import { formatUserDateTime } from 'wcpay/utils/date-time'; - - const DisputeUnderReviewFooter: React.FC< { - dispute: Pick< Dispute, 'id' | 'metadata' | 'status' >; - } > = ( { dispute } ) => { - const submissionDateFormatted = dispute.metadata.__evidence_submitted_at -- ? dateI18n( -- 'M j, Y', -+ ? formatUserDateTime( - moment - .unix( - parseInt( dispute.metadata.__evidence_submitted_at, 10 ) -@@ -93,8 +92,7 @@ const DisputeWonFooter: React.FC< { - dispute: Pick< Dispute, 'id' | 'metadata' | 'status' >; - } > = ( { dispute } ) => { - const closedDateFormatted = dispute.metadata.__dispute_closed_at -- ? dateI18n( -- 'M j, Y', -+ ? formatUserDateTime( - moment - .unix( - parseInt( dispute.metadata.__dispute_closed_at, 10 ) -@@ -171,13 +169,13 @@ const DisputeLostFooter: React.FC< { - const disputeFeeFormatted = getDisputeFeeFormatted( dispute, true ) ?? '-'; - - const closedDateFormatted = dispute.metadata.__dispute_closed_at -- ? dateI18n( -- 'M j, Y', -+ ? formatUserDateTime( - moment - .unix( - parseInt( dispute.metadata.__dispute_closed_at, 10 ) - ) -- .toISOString() -+ .toISOString(), -+ { includeTime: false } - ) - : '-'; - -@@ -274,13 +272,13 @@ const InquiryUnderReviewFooter: React.FC< { - dispute: Pick< Dispute, 'id' | 'metadata' | 'status' >; - } > = ( { dispute } ) => { - const submissionDateFormatted = dispute.metadata.__evidence_submitted_at -- ? dateI18n( -- 'M j, Y', -+ ? formatUserDateTime( - moment - .unix( - parseInt( dispute.metadata.__evidence_submitted_at, 10 ) - ) -- .toISOString() -+ .toISOString(), -+ { includeTime: false } - ) - : '-'; - -@@ -346,13 +344,13 @@ const InquiryClosedFooter: React.FC< { - } > = ( { dispute } ) => { - const isSubmitted = !! dispute.metadata.__evidence_submitted_at; - const closedDateFormatted = dispute.metadata.__dispute_closed_at -- ? dateI18n( -- 'M j, Y', -+ ? formatUserDateTime( - moment - .unix( - parseInt( dispute.metadata.__dispute_closed_at, 10 ) - ) -- .toISOString() -+ .toISOString(), -+ { includeTime: false } - ) - : '-'; - -diff --git a/client/payment-details/dispute-details/dispute-steps.tsx b/client/payment-details/dispute-details/dispute-steps.tsx -index 01f87431274..2fce833c4ec 100644 ---- a/client/payment-details/dispute-details/dispute-steps.tsx -+++ b/client/payment-details/dispute-details/dispute-steps.tsx -@@ -7,7 +7,6 @@ import React from 'react'; - import { __, sprintf } from '@wordpress/i18n'; - import { createInterpolateElement } from '@wordpress/element'; - import { ExternalLink } from '@wordpress/components'; --import { dateI18n } from '@wordpress/date'; - import moment from 'moment'; - import HelpOutlineIcon from 'gridicons/dist/help-outline'; - -@@ -20,6 +19,7 @@ import { formatExplicitCurrency } from 'multi-currency/interface/functions'; - import { ClickTooltip } from 'wcpay/components/tooltip'; - import { getDisputeFeeFormatted } from 'wcpay/disputes/utils'; - import DisputeDueByDate from './dispute-due-by-date'; -+import { formatUserDateTime } from 'wcpay/utils/date-time'; - - interface Props { - dispute: Dispute; -@@ -34,13 +34,15 @@ export const DisputeSteps: React.FC< Props > = ( { - } ) => { - let emailLink; - if ( customer?.email ) { -- const chargeDate = dateI18n( -- 'Y-m-d', -- moment( chargeCreated * 1000 ).toISOString() -+ const chargeDate = formatUserDateTime( -+ moment( chargeCreated * 1000 ).toISOString(), -+ { -+ includeTime: false, -+ } - ); -- const disputeDate = dateI18n( -- 'Y-m-d', -- moment( dispute.created * 1000 ).toISOString() -+ const disputeDate = formatUserDateTime( -+ moment( dispute.created * 1000 ).toISOString(), -+ { includeTime: false } - ); - const emailSubject = sprintf( - // Translators: %1$s is the store name, %2$s is the charge date. -@@ -173,12 +175,10 @@ export const InquirySteps: React.FC< Props > = ( { - } ) => { - let emailLink; - if ( customer?.email ) { -- const chargeDate = dateI18n( -- 'Y-m-d', -+ const chargeDate = formatUserDateTime( - moment( chargeCreated * 1000 ).toISOString() - ); -- const disputeDate = dateI18n( -- 'Y-m-d', -+ const disputeDate = formatUserDateTime( - moment( dispute.created * 1000 ).toISOString() - ); - const emailSubject = sprintf( -diff --git a/client/payment-details/dispute-details/dispute-summary-row.tsx b/client/payment-details/dispute-details/dispute-summary-row.tsx -index 0a43cb223e0..fbe7371fd56 100644 ---- a/client/payment-details/dispute-details/dispute-summary-row.tsx -+++ b/client/payment-details/dispute-details/dispute-summary-row.tsx -@@ -7,7 +7,6 @@ import React from 'react'; - import moment from 'moment'; - import HelpOutlineIcon from 'gridicons/dist/help-outline'; - import { __ } from '@wordpress/i18n'; --import { dateI18n } from '@wordpress/date'; - - /** - * Internal dependencies -@@ -20,6 +19,7 @@ import { formatStringValue } from 'wcpay/utils'; - import { ClickTooltip } from 'wcpay/components/tooltip'; - import Paragraphs from 'wcpay/components/paragraphs'; - import DisputeDueByDate from './dispute-due-by-date'; -+import { formatUserDateTime } from 'wcpay/utils/date-time'; - - interface Props { - dispute: Dispute; -@@ -39,9 +39,9 @@ const DisputeSummaryRow: React.FC< Props > = ( { dispute } ) => { - { - title: __( 'Disputed On', 'woocommerce-payments' ), - content: dispute.created -- ? dateI18n( -- 'M j, Y, g:ia', -- moment( dispute.created * 1000 ).toISOString() -+ ? formatUserDateTime( -+ moment( dispute.created * 1000 ).toISOString(), -+ { separator: ', ' } - ) - : '–', - }, -diff --git a/client/payment-details/summary/index.tsx b/client/payment-details/summary/index.tsx -index 4e9d9559a4b..d7b1790d23d 100644 ---- a/client/payment-details/summary/index.tsx -+++ b/client/payment-details/summary/index.tsx -@@ -4,7 +4,6 @@ - * External dependencies - */ - import { __ } from '@wordpress/i18n'; --import { dateI18n } from '@wordpress/date'; - import { - Card, - CardBody, -@@ -64,6 +63,7 @@ import DisputeResolutionFooter from '../dispute-details/dispute-resolution-foote - import ErrorBoundary from 'components/error-boundary'; - import RefundModal from 'wcpay/payment-details/summary/refund-modal'; - import CardNotice from 'wcpay/components/card-notice'; -+import { formatUserDateTime } from 'wcpay/utils/date-time'; - - declare const window: any; - -@@ -110,9 +110,11 @@ const composePaymentSummaryItems = ( { - { - title: __( 'Date', 'woocommerce-payments' ), - content: charge.created -- ? dateI18n( -- 'M j, Y, g:ia', -- moment( charge.created * 1000 ).toISOString() -+ ? formatUserDateTime( -+ moment( charge.created * 1000 ).toISOString(), -+ { -+ customFormat: 'M j, Y, g:ia', -+ } - ) - : '–', - }, -@@ -706,12 +708,12 @@ const PaymentDetailsSummary: React.FC< PaymentDetailsSummaryProps > = ( { - } - ) }{ ' ' } - - -diff --git a/client/payment-details/summary/test/index.test.tsx b/client/payment-details/summary/test/index.test.tsx -index 9055d481dda..d98363a720e 100755 ---- a/client/payment-details/summary/test/index.test.tsx -+++ b/client/payment-details/summary/test/index.test.tsx -@@ -34,6 +34,8 @@ declare const global: { - featureFlags: { - isAuthAndCaptureEnabled: boolean; - }; -+ dateFormat: string; -+ timeFormat: string; - }; - }; - -@@ -203,6 +205,8 @@ describe( 'PaymentDetailsSummary', () => { - precision: 0, - }, - }, -+ dateFormat: 'M j, Y', -+ timeFormat: 'g:iA', - }; - - // mock Date.now that moment library uses to get current date for testing purposes -diff --git a/client/payment-details/timeline/map-events.js b/client/payment-details/timeline/map-events.js -index 64bc74d91d2..1a3ffe5546f 100644 ---- a/client/payment-details/timeline/map-events.js -+++ b/client/payment-details/timeline/map-events.js -@@ -5,7 +5,6 @@ - */ - import { flatMap } from 'lodash'; - import { __, sprintf } from '@wordpress/i18n'; --import { dateI18n } from '@wordpress/date'; - import { addQueryArgs } from '@wordpress/url'; - import moment from 'moment'; - import { createInterpolateElement } from '@wordpress/element'; -@@ -31,6 +30,7 @@ import { formatFee } from 'utils/fees'; - import { getAdminUrl } from 'wcpay/utils'; - import { ShieldIcon } from 'wcpay/icons'; - import { fraudOutcomeRulesetMapping } from './mappings'; -+import { formatUserDateTime } from 'wcpay/utils/date-time'; - - /** - * Creates a timeline item about a payment status change -@@ -84,9 +84,9 @@ const getDepositTimelineItem = ( - 'woocommerce-payments' - ), - formattedAmount, -- dateI18n( -- 'M j, Y', -- moment( event.deposit.arrival_date * 1000 ).toISOString() -+ formatUserDateTime( -+ moment( event.deposit.arrival_date * 1000 ).toISOString(), -+ { includeTime: false } - ) - ); - const depositUrl = getAdminUrl( { -@@ -143,9 +143,9 @@ const getFinancingPaydownTimelineItem = ( event, formattedAmount, body ) => { - 'woocommerce-payments' - ), - formattedAmount, -- dateI18n( -- 'M j, Y', -- moment( event.deposit.arrival_date * 1000 ).toISOString() -+ formatUserDateTime( -+ moment( event.deposit.arrival_date * 1000 ).toISOString(), -+ { includeTime: false } - ) - ); - -diff --git a/client/payment-details/timeline/test/index.js b/client/payment-details/timeline/test/index.js -index 2529f3d673e..616c780dd69 100644 ---- a/client/payment-details/timeline/test/index.js -+++ b/client/payment-details/timeline/test/index.js -@@ -34,6 +34,7 @@ describe( 'PaymentDetailsTimeline', () => { - precision: 2, - }, - }, -+ dateFormat: 'M j, Y', - }; - } ); - -diff --git a/client/payment-details/timeline/test/map-events.js b/client/payment-details/timeline/test/map-events.js -index c3e42ceae8b..73f65b94940 100644 ---- a/client/payment-details/timeline/test/map-events.js -+++ b/client/payment-details/timeline/test/map-events.js -@@ -47,6 +47,7 @@ describe( 'mapTimelineEvents', () => { - precision: 2, - }, - }, -+ dateFormat: 'M j, Y', - }; - } ); - -diff --git a/client/transactions/blocked/columns.tsx b/client/transactions/blocked/columns.tsx -index 1d75e407cea..c1e6cedd386 100644 ---- a/client/transactions/blocked/columns.tsx -+++ b/client/transactions/blocked/columns.tsx -@@ -2,7 +2,6 @@ - * External dependencies - */ - import React from 'react'; --import { dateI18n } from '@wordpress/date'; - import { __ } from '@wordpress/i18n'; - import moment from 'moment'; - import { TableCardColumn, TableCardBodyColumn } from '@woocommerce/components'; -@@ -15,6 +14,7 @@ import TransactionStatusPill from 'wcpay/components/transaction-status-pill'; - import { FraudOutcomeTransaction } from '../../data'; - import { getDetailsURL } from '../../components/details-link'; - import ClickableCell from '../../components/clickable-cell'; -+import { formatUserDateTime } from 'wcpay/utils/date-time'; - - interface Column extends TableCardColumn { - key: 'created' | 'amount' | 'customer' | 'status'; -@@ -70,9 +70,8 @@ export const getBlockedListRowContent = ( - data.payment_intent.id || data.order_id.toString(), - 'transactions' - ); -- const formattedCreatedDate = dateI18n( -- 'M j, Y / g:iA', -- moment.utc( data.created ).local().toISOString() -+ const formattedCreatedDate = formatUserDateTime( -+ moment.utc( data.created ).toISOString() - ); - - const clickable = ( children: JSX.Element | string ) => ( -diff --git a/client/transactions/blocked/test/columns.test.tsx b/client/transactions/blocked/test/columns.test.tsx -index 7ca2c3d4895..b65e2d10c12 100644 ---- a/client/transactions/blocked/test/columns.test.tsx -+++ b/client/transactions/blocked/test/columns.test.tsx -@@ -15,6 +15,8 @@ declare const global: { - connect: { - country: string; - }; -+ dateFormat: string; -+ timeFormat: string; - }; - }; - const mockWcPaySettings = { -@@ -23,6 +25,8 @@ const mockWcPaySettings = { - connect: { - country: 'US', - }, -+ dateFormat: 'M j, Y', -+ timeFormat: 'g:iA', - }; - - describe( 'Blocked fraud outcome transactions columns', () => { -diff --git a/client/transactions/list/deposit.tsx b/client/transactions/list/deposit.tsx -index b1889f3ad19..ff032aaab66 100644 ---- a/client/transactions/list/deposit.tsx -+++ b/client/transactions/list/deposit.tsx -@@ -5,7 +5,6 @@ - */ - import React from 'react'; - import moment from 'moment'; --import { dateI18n } from '@wordpress/date'; - import { __ } from '@wordpress/i18n'; - import interpolateComponents from '@automattic/interpolate-components'; - import { ExternalLink } from '@wordpress/components'; -@@ -17,6 +16,7 @@ import InfoOutlineIcon from 'gridicons/dist/info-outline'; - */ - import { getAdminUrl } from 'utils'; - import { ClickTooltip } from 'components/tooltip'; -+import { formatUserDateTime } from 'wcpay/utils/date-time'; - - interface DepositProps { - depositId?: string; -@@ -31,12 +31,13 @@ const Deposit: React.FC< DepositProps > = ( { depositId, dateAvailable } ) => { - id: depositId, - } ); - -- const formattedDateAvailable = dateI18n( -- 'M j, Y', -+ const formattedDateAvailable = formatUserDateTime( - moment.utc( dateAvailable ).toISOString(), -- true // TODO Change call to gmdateI18n and remove this deprecated param once WP 5.4 support ends. -+ { -+ includeTime: false, -+ useGmt: true, -+ } - ); -- - return { formattedDateAvailable }; - } - -diff --git a/client/transactions/list/index.tsx b/client/transactions/list/index.tsx -index e123d0319f5..7ff37084ad3 100644 ---- a/client/transactions/list/index.tsx -+++ b/client/transactions/list/index.tsx -@@ -7,7 +7,6 @@ import React, { Fragment, useState } from 'react'; - import { uniq } from 'lodash'; - import { useDispatch } from '@wordpress/data'; - import { useMemo } from '@wordpress/element'; --import { dateI18n } from '@wordpress/date'; - import { __, _n, sprintf } from '@wordpress/i18n'; - import moment from 'moment'; - import { -@@ -70,6 +69,7 @@ import p24BankList from '../../payment-details/payment-method/p24/bank-list'; - import { HoverTooltip } from 'components/tooltip'; - import { PAYMENT_METHOD_TITLES } from 'wcpay/constants/payment-method'; - import { ReportingExportLanguageHook } from 'wcpay/settings/reporting-settings/interfaces'; -+import { formatUserDateTime } from 'wcpay/utils/date-time'; - - interface TransactionsListProps { - depositId?: string; -@@ -466,10 +466,7 @@ export const TransactionsList = ( - date: { - value: txn.date, - display: clickable( -- dateI18n( -- 'M j, Y / g:iA', -- moment.utc( txn.date ).local().toISOString() -- ) -+ formatUserDateTime( moment.utc( txn.date ).toISOString() ) - ), - }, - channel: { -diff --git a/client/transactions/list/test/deposit.tsx b/client/transactions/list/test/deposit.tsx -index 2cb87b0b248..283e101e3db 100644 ---- a/client/transactions/list/test/deposit.tsx -+++ b/client/transactions/list/test/deposit.tsx -@@ -12,6 +12,12 @@ import { render } from '@testing-library/react'; - import Deposit from '../deposit'; - - describe( 'Deposit', () => { -+ beforeEach( () => { -+ // Mock the window.wcpaySettings property -+ window.wcpaySettings.dateFormat = 'M j, Y'; -+ window.wcpaySettings.timeFormat = 'g:i a'; -+ } ); -+ - test( 'renders with date and deposit available', () => { - const { container: link } = render( - -diff --git a/client/transactions/list/test/index.tsx b/client/transactions/list/test/index.tsx -index 8d97397ce01..0ac273c6751 100644 ---- a/client/transactions/list/test/index.tsx -+++ b/client/transactions/list/test/index.tsx -@@ -244,6 +244,8 @@ describe( 'Transactions list', () => { - exportModalDismissed: true, - }, - }; -+ window.wcpaySettings.dateFormat = 'M j, Y'; -+ window.wcpaySettings.timeFormat = 'g:iA'; - } ); - - test( 'renders correctly when filtered by deposit', () => { -diff --git a/client/transactions/risk-review/columns.tsx b/client/transactions/risk-review/columns.tsx -index d7f5de95111..3de9987065c 100644 ---- a/client/transactions/risk-review/columns.tsx -+++ b/client/transactions/risk-review/columns.tsx -@@ -2,7 +2,6 @@ - * External dependencies - */ - import React from 'react'; --import { dateI18n } from '@wordpress/date'; - import { __ } from '@wordpress/i18n'; - import moment from 'moment'; - import { TableCardColumn, TableCardBodyColumn } from '@woocommerce/components'; -@@ -17,6 +16,7 @@ import { formatExplicitCurrency } from 'multi-currency/interface/functions'; - import { recordEvent } from 'tracks'; - import TransactionStatusPill from 'wcpay/components/transaction-status-pill'; - import { FraudOutcomeTransaction } from '../../data'; -+import { formatUserDateTime } from 'wcpay/utils/date-time'; - - interface Column extends TableCardColumn { - key: 'created' | 'amount' | 'customer' | 'status'; -@@ -76,9 +76,8 @@ export const getRiskReviewListRowContent = ( - data: FraudOutcomeTransaction - ): Record< string, TableCardBodyColumn > => { - const detailsURL = getDetailsURL( data.payment_intent.id, 'transactions' ); -- const formattedCreatedDate = dateI18n( -- 'M j, Y / g:iA', -- moment.utc( data.created ).local().toISOString() -+ const formattedCreatedDate = formatUserDateTime( -+ moment.utc( data.created ).toISOString() - ); - - const clickable = ( children: JSX.Element | string ) => ( -diff --git a/client/transactions/risk-review/test/columns.test.tsx b/client/transactions/risk-review/test/columns.test.tsx -index 033f9eb7aa2..54de107e0e4 100644 ---- a/client/transactions/risk-review/test/columns.test.tsx -+++ b/client/transactions/risk-review/test/columns.test.tsx -@@ -15,6 +15,8 @@ declare const global: { - connect: { - country: string; - }; -+ dateFormat: string; -+ timeFormat: string; - }; - }; - const mockWcPaySettings = { -@@ -23,6 +25,8 @@ const mockWcPaySettings = { - connect: { - country: 'US', - }, -+ dateFormat: 'M j, Y', -+ timeFormat: 'g:iA', - }; - - describe( 'Review fraud outcome transactions columns', () => { -diff --git a/client/transactions/uncaptured/index.tsx b/client/transactions/uncaptured/index.tsx -index 17058760c19..97581af5d91 100644 ---- a/client/transactions/uncaptured/index.tsx -+++ b/client/transactions/uncaptured/index.tsx -@@ -7,7 +7,6 @@ import React, { useEffect } from 'react'; - import { __ } from '@wordpress/i18n'; - import { TableCard, TableCardColumn } from '@woocommerce/components'; - import { onQueryChange, getQuery } from '@woocommerce/navigation'; --import { dateI18n } from '@wordpress/date'; - import moment from 'moment'; - - /** -@@ -21,6 +20,7 @@ import { formatExplicitCurrency } from 'multi-currency/interface/functions'; - import RiskLevel, { calculateRiskMapping } from 'components/risk-level'; - import { recordEvent } from 'tracks'; - import CaptureAuthorizationButton from 'wcpay/components/capture-authorization-button'; -+import { formatUserDateTime } from 'wcpay/utils/date-time'; - - interface Column extends TableCardColumn { - key: -@@ -130,35 +130,23 @@ export const AuthorizationsList = (): JSX.Element => { - display: auth.payment_intent_id, - }, - created: { -- value: dateI18n( -- 'M j, Y / g:iA', -- moment.utc( auth.created ).local().toISOString() -+ value: formatUserDateTime( -+ moment.utc( auth.created ).toISOString() - ), - display: clickable( -- dateI18n( -- 'M j, Y / g:iA', -- moment.utc( auth.created ).local().toISOString() -+ formatUserDateTime( -+ moment.utc( auth.created ).toISOString() - ) - ), - }, - // Payments are authorized for a maximum of 7 days - capture_by: { -- value: dateI18n( -- 'M j, Y / g:iA', -- moment -- .utc( auth.created ) -- .add( 7, 'd' ) -- .local() -- .toISOString() -+ value: formatUserDateTime( -+ moment.utc( auth.created ).add( 7, 'd' ).toISOString() - ), - display: clickable( -- dateI18n( -- 'M j, Y / g:iA', -- moment -- .utc( auth.created ) -- .add( 7, 'd' ) -- .local() -- .toISOString() -+ formatUserDateTime( -+ moment.utc( auth.created ).add( 7, 'd' ).toISOString() - ) - ), - }, -diff --git a/client/transactions/uncaptured/test/index.test.tsx b/client/transactions/uncaptured/test/index.test.tsx -index 52f76dcc882..fc21a532b67 100644 ---- a/client/transactions/uncaptured/test/index.test.tsx -+++ b/client/transactions/uncaptured/test/index.test.tsx -@@ -67,6 +67,8 @@ declare const global: { - precision: number; - }; - }; -+ dateFormat: string; -+ timeFormat: string; - }; - }; - -@@ -126,6 +128,8 @@ describe( 'Authorizations list', () => { - precision: 2, - }, - }, -+ dateFormat: 'M j, Y', -+ timeFormat: 'g:iA', - }; - } ); - -diff --git a/client/utils/date-time.ts b/client/utils/date-time.ts -new file mode 100644 -index 00000000000..61ae1a046a0 ---- /dev/null -+++ b/client/utils/date-time.ts -@@ -0,0 +1,48 @@ -+/** -+ * External dependencies -+ */ -+import { dateI18n } from '@wordpress/date'; -+ -+type DateTimeFormat = string | null; -+ -+interface FormatDateTimeOptions { -+ includeTime?: boolean; // Whether to include time in the formatted string (defaults to true) -+ useGmt?: boolean; // Whether to display the time in GMT/UTC (defaults to false) -+ separator?: string; // Separator between date and time (defaults to ' / ') -+ customFormat?: DateTimeFormat; // Custom format to use instead of WordPress settings -+} -+ -+/** -+ * Formats a date and time string according to WordPress settings or a custom format. -+ * -+ * @param { string | Date } dateTime - The date and time string (e.g., '2024-10-23 15:28:26') or a JS Date object. -+ * @param { FormatDateTimeOptions } options - Additional options to control time inclusion and whether to use GMT/UTC. -+ * @return { string } - The formatted date and time string. -+ */ -+export function formatUserDateTime( -+ dateTime: string | Date, -+ options: FormatDateTimeOptions = { -+ includeTime: true, -+ useGmt: false, -+ separator: ' / ', -+ customFormat: null, -+ } -+): string { -+ const { -+ customFormat = null, -+ includeTime = true, -+ useGmt = false, -+ separator = ' / ', -+ } = options; -+ -+ // Use the WordPress settings for date and time format if no custom format is provided -+ const format = -+ customFormat || -+ `${ window.wcpaySettings.dateFormat }${ -+ includeTime -+ ? `${ separator }${ window.wcpaySettings.timeFormat }` -+ : '' -+ }`; -+ -+ return dateI18n( format, dateTime, useGmt ); -+} -diff --git a/client/utils/test/date-time.test.ts b/client/utils/test/date-time.test.ts -new file mode 100644 -index 00000000000..ae1bc2e890b ---- /dev/null -+++ b/client/utils/test/date-time.test.ts -@@ -0,0 +1,115 @@ -+/** -+ * Internal dependencies -+ */ -+import { formatUserDateTime } from 'wcpay/utils/date-time'; -+import { dateI18n } from '@wordpress/date'; -+ -+describe( 'formatUserDateTime', () => { -+ const originalWcpaySettings = window.wcpaySettings; -+ const mockWcpaySettings = { -+ dateFormat: 'Y-m-d', -+ timeFormat: 'H:i', -+ }; -+ -+ beforeAll( () => { -+ window.wcpaySettings = mockWcpaySettings as typeof wcpaySettings; -+ } ); -+ -+ afterAll( () => { -+ window.wcpaySettings = originalWcpaySettings; -+ } ); -+ -+ describe( 'with string input', () => { -+ it( 'should format using default WordPress settings', () => { -+ const dateTime = '2024-10-23 15:28:26'; -+ const formatted = formatUserDateTime( dateTime ); -+ -+ expect( formatted ).toBe( '2024-10-23 / 15:28' ); -+ } ); -+ -+ it( 'should use custom format if provided', () => { -+ const dateTime = '2024-10-23 15:28:26'; -+ const options = { customFormat: 'd-m-Y H:i:s' }; -+ const formatted = formatUserDateTime( dateTime, options ); -+ -+ expect( formatted ).toBe( '23-10-2024 15:28:26' ); -+ } ); -+ -+ it( 'should exclude time if includeTime is set to false', () => { -+ const dateTime = '2024-10-23 15:28:26'; -+ const options = { includeTime: false }; -+ const formatted = formatUserDateTime( dateTime, options ); -+ -+ expect( formatted ).toBe( '2024-10-23' ); -+ } ); -+ -+ it( 'should use custom separator when provided', () => { -+ const dateTime = '2024-10-23 15:28:26'; -+ const options = { separator: ' - ' }; -+ const formatted = formatUserDateTime( dateTime, options ); -+ -+ // Expect output with custom separator -+ expect( formatted ).toBe( '2024-10-23 - 15:28' ); -+ } ); -+ -+ it( 'should handle GMT/UTC setting correctly when useGmt is true', () => { -+ const dateTime = '2024-10-23 15:28:26Z'; -+ const options = { useGmt: true }; // Enforcing UTC -+ const formatted = formatUserDateTime( dateTime, options ); -+ -+ // Expect UTC-based output (no timezone adjustment) -+ expect( formatted ).toBe( '2024-10-23 / 15:28' ); -+ } ); -+ } ); -+ -+ describe( 'with Date object input', () => { -+ it( 'should format using default WordPress settings', () => { -+ const dateTime = new Date( Date.UTC( 2024, 9, 23, 15, 28, 26 ) ); -+ const formatted = formatUserDateTime( dateTime, { useGmt: true } ); -+ -+ // Expected format based on WordPress settings and Date object -+ expect( formatted ).toBe( '2024-10-23 / 15:28' ); -+ } ); -+ -+ it( 'should use custom format if provided', () => { -+ const dateTime = new Date( Date.UTC( 2024, 9, 23, 15, 28, 26 ) ); -+ const options = { customFormat: 'd-m-Y H:i:s', useGmt: true }; -+ const formatted = formatUserDateTime( dateTime, options ); -+ -+ // Expected format for Date object: '23-10-2024 15:28:26' -+ expect( formatted ).toBe( '23-10-2024 15:28:26' ); -+ } ); -+ -+ it( 'should exclude time if includeTime is set to false', () => { -+ const dateTime = new Date( Date.UTC( 2024, 9, 23, 15, 28, 26 ) ); -+ const options = { includeTime: false, useGmt: true }; -+ const formatted = formatUserDateTime( dateTime, options ); -+ -+ // Expect output with date only -+ expect( formatted ).toBe( '2024-10-23' ); -+ } ); -+ -+ it( 'should handle GMT/UTC setting correctly', () => { -+ const dateTime = new Date( 2024, 9, 23, 15, 28, 26 ); // Local time (non-UTC) -+ const formatted = formatUserDateTime( dateTime ); -+ -+ // The expected format will vary based on your local timezone -+ const expectedFormat = dateI18n( -+ `${ mockWcpaySettings.dateFormat } / ${ mockWcpaySettings.timeFormat }`, -+ dateTime, -+ false -+ ); -+ -+ expect( formatted ).toBe( expectedFormat ); -+ } ); -+ -+ it( 'should use custom separator when provided', () => { -+ const dateTime = new Date( Date.UTC( 2024, 9, 23, 15, 28, 26 ) ); -+ const options = { separator: ' - ', useGmt: true }; -+ const formatted = formatUserDateTime( dateTime, options ); -+ -+ // Expect output with custom separator -+ expect( formatted ).toBe( '2024-10-23 - 15:28' ); -+ } ); -+ } ); -+} ); -diff --git a/includes/admin/class-wc-payments-admin.php b/includes/admin/class-wc-payments-admin.php -index 9c53cb48597..eb6a6e9fd7c 100644 ---- a/includes/admin/class-wc-payments-admin.php -+++ b/includes/admin/class-wc-payments-admin.php -@@ -958,6 +958,8 @@ private function get_js_settings(): array { - 'lifetimeTPV' => $this->account->get_lifetime_total_payment_volume(), - 'defaultExpressCheckoutBorderRadius' => WC_Payments_Express_Checkout_Button_Handler::DEFAULT_BORDER_RADIUS_IN_PX, - 'isWooPayGlobalThemeSupportEligible' => WC_Payments_Features::is_woopay_global_theme_support_eligible(), -+ 'dateFormat' => wc_date_format(), -+ 'timeFormat' => get_option( 'time_format' ), - ]; - - return apply_filters( 'wcpay_js_settings', $this->wcpay_js_settings ); -diff --git a/multi-currency/client/settings/single-currency/index.js b/multi-currency/client/settings/single-currency/index.js -index 14a3560f14d..fc063ce3211 100644 ---- a/multi-currency/client/settings/single-currency/index.js -+++ b/multi-currency/client/settings/single-currency/index.js -@@ -3,7 +3,6 @@ - * External dependencies - */ - import React, { useContext, useEffect, useState } from 'react'; --import { dateI18n } from '@wordpress/date'; - import { sprintf, __ } from '@wordpress/i18n'; - import moment from 'moment'; - -@@ -24,7 +23,6 @@ import { - useCurrencies, - useCurrencySettings, - useEnabledCurrencies, -- useStoreSettings, - } from 'multi-currency/data'; - import MultiCurrencySettingsContext from 'multi-currency/context'; - import { -@@ -32,6 +30,7 @@ import { - SettingsLayout, - SettingsSection, - } from 'multi-currency/interface/components'; -+import { formatUserDateTime } from 'wcpay/utils/date-time'; - - const SingleCurrencySettings = () => { - const { -@@ -45,7 +44,6 @@ const SingleCurrencySettings = () => { - - const { currencies } = useCurrencies(); - const { enabledCurrencies } = useEnabledCurrencies(); -- const { storeSettings } = useStoreSettings(); - - const { - currencySettings, -@@ -105,13 +103,10 @@ const SingleCurrencySettings = () => { - } - }, [ currencySettings, currency, initialPriceRoundingType ] ); - -- const dateFormat = storeSettings.date_format ?? 'M j, Y'; -- const timeFormat = storeSettings.time_format ?? 'g:iA'; -- - const formattedLastUpdatedDateTime = targetCurrency -- ? dateI18n( -- `${ dateFormat } ${ timeFormat }`, -- moment.unix( targetCurrency.last_updated ).toISOString() -+ ? formatUserDateTime( -+ moment.unix( targetCurrency.last_updated ).toISOString(), -+ { useGmt: false, separator: ' ' } - ) - : ''; - -diff --git a/multi-currency/client/settings/single-currency/test/index.test.js b/multi-currency/client/settings/single-currency/test/index.test.js -index 0559953e54b..b9a401c3946 100644 ---- a/multi-currency/client/settings/single-currency/test/index.test.js -+++ b/multi-currency/client/settings/single-currency/test/index.test.js -@@ -159,6 +159,8 @@ describe( 'Single currency settings screen', () => { - precision: 2, - }, - }, -+ dateFormat: 'M j, Y', -+ timeFormat: 'g:iA', - }; - } ); - From 3334417471af6b19494e09b5492d81b78836a686 Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Tue, 29 Oct 2024 16:59:51 +0100 Subject: [PATCH 44/76] Revert multi-currency changes --- .../client/settings/single-currency/index.js | 13 +++++++++---- .../settings/single-currency/test/index.test.js | 2 -- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/multi-currency/client/settings/single-currency/index.js b/multi-currency/client/settings/single-currency/index.js index fc063ce3211..14a3560f14d 100644 --- a/multi-currency/client/settings/single-currency/index.js +++ b/multi-currency/client/settings/single-currency/index.js @@ -3,6 +3,7 @@ * External dependencies */ import React, { useContext, useEffect, useState } from 'react'; +import { dateI18n } from '@wordpress/date'; import { sprintf, __ } from '@wordpress/i18n'; import moment from 'moment'; @@ -23,6 +24,7 @@ import { useCurrencies, useCurrencySettings, useEnabledCurrencies, + useStoreSettings, } from 'multi-currency/data'; import MultiCurrencySettingsContext from 'multi-currency/context'; import { @@ -30,7 +32,6 @@ import { SettingsLayout, SettingsSection, } from 'multi-currency/interface/components'; -import { formatUserDateTime } from 'wcpay/utils/date-time'; const SingleCurrencySettings = () => { const { @@ -44,6 +45,7 @@ const SingleCurrencySettings = () => { const { currencies } = useCurrencies(); const { enabledCurrencies } = useEnabledCurrencies(); + const { storeSettings } = useStoreSettings(); const { currencySettings, @@ -103,10 +105,13 @@ const SingleCurrencySettings = () => { } }, [ currencySettings, currency, initialPriceRoundingType ] ); + const dateFormat = storeSettings.date_format ?? 'M j, Y'; + const timeFormat = storeSettings.time_format ?? 'g:iA'; + const formattedLastUpdatedDateTime = targetCurrency - ? formatUserDateTime( - moment.unix( targetCurrency.last_updated ).toISOString(), - { useGmt: false, separator: ' ' } + ? dateI18n( + `${ dateFormat } ${ timeFormat }`, + moment.unix( targetCurrency.last_updated ).toISOString() ) : ''; diff --git a/multi-currency/client/settings/single-currency/test/index.test.js b/multi-currency/client/settings/single-currency/test/index.test.js index b9a401c3946..0559953e54b 100644 --- a/multi-currency/client/settings/single-currency/test/index.test.js +++ b/multi-currency/client/settings/single-currency/test/index.test.js @@ -159,8 +159,6 @@ describe( 'Single currency settings screen', () => { precision: 2, }, }, - dateFormat: 'M j, Y', - timeFormat: 'g:iA', }; } ); From f2479bd4e35048a8be3b4a07117354fa6e9ba5b7 Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Tue, 29 Oct 2024 17:05:18 +0100 Subject: [PATCH 45/76] Remove redundant comments --- client/utils/test/date-time.test.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/client/utils/test/date-time.test.ts b/client/utils/test/date-time.test.ts index ae1bc2e890b..d0329b2cad5 100644 --- a/client/utils/test/date-time.test.ts +++ b/client/utils/test/date-time.test.ts @@ -48,13 +48,12 @@ describe( 'formatUserDateTime', () => { const options = { separator: ' - ' }; const formatted = formatUserDateTime( dateTime, options ); - // Expect output with custom separator expect( formatted ).toBe( '2024-10-23 - 15:28' ); } ); it( 'should handle GMT/UTC setting correctly when useGmt is true', () => { const dateTime = '2024-10-23 15:28:26Z'; - const options = { useGmt: true }; // Enforcing UTC + const options = { useGmt: true }; const formatted = formatUserDateTime( dateTime, options ); // Expect UTC-based output (no timezone adjustment) @@ -67,7 +66,6 @@ describe( 'formatUserDateTime', () => { const dateTime = new Date( Date.UTC( 2024, 9, 23, 15, 28, 26 ) ); const formatted = formatUserDateTime( dateTime, { useGmt: true } ); - // Expected format based on WordPress settings and Date object expect( formatted ).toBe( '2024-10-23 / 15:28' ); } ); @@ -76,7 +74,6 @@ describe( 'formatUserDateTime', () => { const options = { customFormat: 'd-m-Y H:i:s', useGmt: true }; const formatted = formatUserDateTime( dateTime, options ); - // Expected format for Date object: '23-10-2024 15:28:26' expect( formatted ).toBe( '23-10-2024 15:28:26' ); } ); @@ -85,7 +82,6 @@ describe( 'formatUserDateTime', () => { const options = { includeTime: false, useGmt: true }; const formatted = formatUserDateTime( dateTime, options ); - // Expect output with date only expect( formatted ).toBe( '2024-10-23' ); } ); @@ -93,7 +89,6 @@ describe( 'formatUserDateTime', () => { const dateTime = new Date( 2024, 9, 23, 15, 28, 26 ); // Local time (non-UTC) const formatted = formatUserDateTime( dateTime ); - // The expected format will vary based on your local timezone const expectedFormat = dateI18n( `${ mockWcpaySettings.dateFormat } / ${ mockWcpaySettings.timeFormat }`, dateTime, @@ -108,7 +103,6 @@ describe( 'formatUserDateTime', () => { const options = { separator: ' - ', useGmt: true }; const formatted = formatUserDateTime( dateTime, options ); - // Expect output with custom separator expect( formatted ).toBe( '2024-10-23 - 15:28' ); } ); } ); From 3664b733f4771568ffe8de727894bc4bfcd318d6 Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Tue, 29 Oct 2024 18:12:08 +0100 Subject: [PATCH 46/76] Fix payment details summary not respecting user settings --- client/payment-details/order-details/test/index.test.tsx | 4 ++++ client/payment-details/summary/index.tsx | 6 ++---- .../summary/test/__snapshots__/index.test.tsx.snap | 4 ++-- client/payment-details/summary/test/index.test.tsx | 2 +- client/payment-details/test/index.test.tsx | 4 ++++ 5 files changed, 13 insertions(+), 7 deletions(-) diff --git a/client/payment-details/order-details/test/index.test.tsx b/client/payment-details/order-details/test/index.test.tsx index b74fa8a55af..8ca4d6dd9a5 100644 --- a/client/payment-details/order-details/test/index.test.tsx +++ b/client/payment-details/order-details/test/index.test.tsx @@ -24,6 +24,8 @@ declare const global: { connect: { country: string; }; + dateFormat: string; + timeFormat: string; }; }; @@ -141,6 +143,8 @@ describe( 'Order details page', () => { featureFlags: { paymentTimeline: true }, zeroDecimalCurrencies: [], connect: { country: 'US' }, + timeFormat: 'g:ia', + dateFormat: 'M j, Y', }; const selectMock = jest.fn( ( storeName ) => diff --git a/client/payment-details/summary/index.tsx b/client/payment-details/summary/index.tsx index d7b1790d23d..8965c5b8587 100644 --- a/client/payment-details/summary/index.tsx +++ b/client/payment-details/summary/index.tsx @@ -111,10 +111,8 @@ const composePaymentSummaryItems = ( { title: __( 'Date', 'woocommerce-payments' ), content: charge.created ? formatUserDateTime( - moment( charge.created * 1000 ).toISOString(), - { - customFormat: 'M j, Y, g:ia', - } + moment.utc( charge.created * 1000 ).toISOString(), + { separator: ', ' } ) : '–', }, diff --git a/client/payment-details/summary/test/__snapshots__/index.test.tsx.snap b/client/payment-details/summary/test/__snapshots__/index.test.tsx.snap index 8286a7941bb..4f978de51ea 100644 --- a/client/payment-details/summary/test/__snapshots__/index.test.tsx.snap +++ b/client/payment-details/summary/test/__snapshots__/index.test.tsx.snap @@ -300,7 +300,7 @@ exports[`PaymentDetailsSummary capture notification and fraud buttons renders ca this charge within the next 7 days @@ -652,7 +652,7 @@ exports[`PaymentDetailsSummary capture notification and fraud buttons renders th this charge within the next 7 days diff --git a/client/payment-details/summary/test/index.test.tsx b/client/payment-details/summary/test/index.test.tsx index d98363a720e..bd0b46f80cc 100755 --- a/client/payment-details/summary/test/index.test.tsx +++ b/client/payment-details/summary/test/index.test.tsx @@ -206,7 +206,7 @@ describe( 'PaymentDetailsSummary', () => { }, }, dateFormat: 'M j, Y', - timeFormat: 'g:iA', + timeFormat: 'g:ia', }; // mock Date.now that moment library uses to get current date for testing purposes diff --git a/client/payment-details/test/index.test.tsx b/client/payment-details/test/index.test.tsx index 0eef7173f72..c79874a2e48 100644 --- a/client/payment-details/test/index.test.tsx +++ b/client/payment-details/test/index.test.tsx @@ -20,6 +20,8 @@ declare const global: { connect: { country: string; }; + dateFormat: string; + timeFormat: string; }; }; @@ -141,6 +143,8 @@ global.wcpaySettings = { featureFlags: { paymentTimeline: true }, zeroDecimalCurrencies: [ 'usd' ], connect: { country: 'US' }, + dateFormat: 'M j, Y', + timeFormat: 'g:ia', }; describe( 'Payment details page', () => { From 70b3c921a7088370a6c7e1f503489a1b3902cb89 Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Wed, 30 Oct 2024 18:49:11 +0100 Subject: [PATCH 47/76] PR feedback: exclude time by default --- client/capital/index.tsx | 12 ++------ .../account-fees/expiration-description.js | 8 ++---- .../components/active-loan-summary/index.tsx | 6 ++-- .../components/disputed-order-notice/index.js | 8 ++---- client/deposits/details/index.tsx | 1 - client/deposits/list/index.tsx | 3 +- client/deposits/utils/index.ts | 1 - client/disputes/index.tsx | 15 +++++----- client/disputes/info/index.tsx | 5 ++-- client/disputes/test/index.tsx | 3 +- client/documents/list/index.tsx | 13 ++------- .../overview/task-list/tasks/dispute-task.tsx | 5 +--- .../dispute-details/dispute-due-by-date.tsx | 1 + .../dispute-resolution-footer.tsx | 15 +++++----- .../dispute-details/dispute-steps.tsx | 14 ++++------ .../dispute-details/dispute-summary-row.tsx | 2 +- client/payment-details/summary/index.tsx | 4 +-- client/payment-details/timeline/map-events.js | 6 ++-- client/transactions/blocked/columns.tsx | 3 +- client/transactions/list/deposit.tsx | 3 +- client/transactions/list/index.tsx | 4 ++- client/transactions/risk-review/columns.tsx | 3 +- client/transactions/uncaptured/index.tsx | 12 +++++--- client/utils/date-time.ts | 4 +-- client/utils/test/date-time.test.ts | 28 +++++++++++++------ 25 files changed, 83 insertions(+), 96 deletions(-) diff --git a/client/capital/index.tsx b/client/capital/index.tsx index dea559ba74e..8d41f418ec2 100644 --- a/client/capital/index.tsx +++ b/client/capital/index.tsx @@ -80,7 +80,7 @@ const getLoanStatusText = ( loan: CapitalLoan ) => { return loan.fully_paid_at ? __( 'Paid off', 'woocommerce-payments' ) + ': ' + - formatUserDateTime( loan.fully_paid_at, { includeTime: false } ) + formatUserDateTime( loan.fully_paid_at ) : __( 'Active', 'woocommerce-payments' ); }; @@ -112,11 +112,7 @@ const getRowsData = ( loans: CapitalLoan[] ) => const data = { paid_out_at: { value: loan.paid_out_at, - display: clickable( - formatUserDateTime( loan.paid_out_at, { - includeTime: false, - } ) - ), + display: clickable( formatUserDateTime( loan.paid_out_at ) ), }, status: { value: getLoanStatusText( loan ), @@ -154,9 +150,7 @@ const getRowsData = ( loans: CapitalLoan[] ) => value: loan.first_paydown_at, display: clickable( loan.first_paydown_at - ? formatUserDateTime( loan.first_paydown_at, { - includeTime: false, - } ) + ? formatUserDateTime( loan.first_paydown_at ) : '-' ), }, diff --git a/client/components/account-status/account-fees/expiration-description.js b/client/components/account-status/account-fees/expiration-description.js index b9472c238b8..6cca98d0df1 100644 --- a/client/components/account-status/account-fees/expiration-description.js +++ b/client/components/account-status/account-fees/expiration-description.js @@ -26,9 +26,7 @@ const ExpirationDescription = ( { 'woocommerce-payments' ), formatCurrency( volumeAllowance, currencyCode ), - formatUserDateTime( moment( endTime ).toISOString(), { - includeTime: false, - } ) + formatUserDateTime( moment( endTime ).toISOString() ) ); } else if ( volumeAllowance ) { description = sprintf( @@ -46,9 +44,7 @@ const ExpirationDescription = ( { 'Discounted base fee expires on %1$s.', 'woocommerce-payments' ), - formatUserDateTime( moment( endTime ).toISOString(), { - includeTime: false, - } ) + formatUserDateTime( moment( endTime ).toISOString() ) ); } else { return null; diff --git a/client/components/active-loan-summary/index.tsx b/client/components/active-loan-summary/index.tsx index af817c7331d..f2c87037e07 100755 --- a/client/components/active-loan-summary/index.tsx +++ b/client/components/active-loan-summary/index.tsx @@ -215,7 +215,7 @@ const ActiveLoanSummary = (): JSX.Element => { details.current_repayment_interval.due_at * 1000 ), - { includeTime: false, useGmt: true } + { useGmt: true } ) ) } > @@ -253,7 +253,7 @@ const ActiveLoanSummary = (): JSX.Element => { > { formatUserDateTime( new Date( details.advance_paid_out_at * 1000 ), - { includeTime: false, useGmt: true } + { useGmt: true } ) } { > { formatUserDateTime( new Date( details.repayments_begin_at * 1000 ), - { includeTime: false, useGmt: true } + { useGmt: true } ) } diff --git a/client/components/disputed-order-notice/index.js b/client/components/disputed-order-notice/index.js index 892f0313cf0..7b64eb01e52 100644 --- a/client/components/disputed-order-notice/index.js +++ b/client/components/disputed-order-notice/index.js @@ -131,9 +131,7 @@ const UrgentDisputeNoticeBody = ( { formatString, formattedAmount, reasons[ disputeReason ].display, - formatUserDateTime( dueBy.local().toISOString(), { - includeTime: false, - } ) + formatUserDateTime( dueBy.local().toISOString() ) ); let suffix = sprintf( @@ -184,9 +182,7 @@ const RegularDisputeNoticeBody = ( { const suffix = sprintf( // Translators: %1$s is the dispute due date. __( 'Please respond before %1$s.', 'woocommerce-payments' ), - formatUserDateTime( dueBy.local().toISOString(), { - includeTime: false, - } ) + formatUserDateTime( dueBy.local().toISOString() ) ); return ( diff --git a/client/deposits/details/index.tsx b/client/deposits/details/index.tsx index e3f4cc0afc9..eaa35f4e950 100644 --- a/client/deposits/details/index.tsx +++ b/client/deposits/details/index.tsx @@ -113,7 +113,6 @@ export const DepositOverview: React.FC< DepositOverviewProps > = ( { `${ depositDateLabel }: ` + formatUserDateTime( moment.utc( deposit.date ).toISOString(), { useGmt: true, - includeTime: false, } ) } value={ } diff --git a/client/deposits/list/index.tsx b/client/deposits/list/index.tsx index cd948034acc..e75afcc1461 100644 --- a/client/deposits/list/index.tsx +++ b/client/deposits/list/index.tsx @@ -138,7 +138,6 @@ export const DepositsList = (): JSX.Element => { { formatUserDateTime( moment.utc( deposit.date ).toISOString(), { - includeTime: false, useGmt: true, } ) } @@ -332,7 +331,7 @@ export const DepositsList = (): JSX.Element => { ...row[ 1 ], value: formatUserDateTime( moment.utc( row[ 1 ].value ).toISOString(), - { useGmt: true, includeTime: false } + { useGmt: true } ), }, ...row.slice( 2 ), diff --git a/client/deposits/utils/index.ts b/client/deposits/utils/index.ts index a3bf14e82a3..50abccfb387 100644 --- a/client/deposits/utils/index.ts +++ b/client/deposits/utils/index.ts @@ -12,7 +12,6 @@ interface DepositObject { export const getDepositDate = ( deposit?: DepositObject | null ): string => deposit ? formatUserDateTime( moment.utc( deposit?.date ).toISOString(), { - includeTime: false, useGmt: true, } ) : '—'; diff --git a/client/disputes/index.tsx b/client/disputes/index.tsx index 11515987b58..a8e7b26e0aa 100644 --- a/client/disputes/index.tsx +++ b/client/disputes/index.tsx @@ -201,7 +201,9 @@ const smartDueDate = ( dispute: CachedDispute ) => { ); } - return formatUserDateTime( moment.utc( dispute.due_by ).toISOString() ); + return formatUserDateTime( moment.utc( dispute.due_by ).toISOString(), { + includeTime: true, + } ); }; export const DisputesList = (): JSX.Element => { @@ -299,7 +301,8 @@ export const DisputesList = (): JSX.Element => { value: dispute.created, display: clickable( formatUserDateTime( - moment.utc( dispute.created ).toISOString() + moment.utc( dispute.created ).toISOString(), + { includeTime: true } ) ), }, @@ -480,17 +483,15 @@ export const DisputesList = (): JSX.Element => { // Disputed On. ...row[ 10 ], value: formatUserDateTime( - moment.utc( row[ 10 ].value ).toISOString(), - { - includeTime: false, - } + moment.utc( row[ 10 ].value ).toISOString() ), }, { // Respond by. ...row[ 11 ], value: formatUserDateTime( - moment.utc( row[ 11 ].value ).toISOString() + moment.utc( row[ 11 ].value ).toISOString(), + { includeTime: true } ), }, ]; diff --git a/client/disputes/info/index.tsx b/client/disputes/info/index.tsx index a6f6ecaad06..363a8573a4b 100644 --- a/client/disputes/info/index.tsx +++ b/client/disputes/info/index.tsx @@ -70,8 +70,7 @@ const Info = ( { } : { created: formatUserDateTime( - moment( dispute.created * 1000 ).toISOString(), - { includeTime: false } + moment( dispute.created * 1000 ).toISOString() ), amount: formatExplicitCurrency( dispute.amount || 0, @@ -82,7 +81,7 @@ const Info = ( { moment( dispute.evidence_details.due_by * 1000 ).toISOString(), - { separator: ' - ' } + { separator: ' - ', includeTime: true } ) : null, reason: composeDisputeReason( dispute ), diff --git a/client/disputes/test/index.tsx b/client/disputes/test/index.tsx index 52af84da080..40a02023687 100644 --- a/client/disputes/test/index.tsx +++ b/client/disputes/test/index.tsx @@ -371,7 +371,8 @@ describe( 'Disputes list', () => { expect( csvFirstDispute[ 11 ].replace( /^"|"$/g, '' ) ).toBe( formatUserDateTime( - moment.utc( mockDisputes[ 0 ].due_by ).toISOString() + moment.utc( mockDisputes[ 0 ].due_by ).toISOString(), + { includeTime: true } ) ); // date respond by } ); diff --git a/client/documents/list/index.tsx b/client/documents/list/index.tsx index a6aacd9c4a2..e6faa941f8c 100644 --- a/client/documents/list/index.tsx +++ b/client/documents/list/index.tsx @@ -69,14 +69,10 @@ const getDocumentDescription = ( document: Document ) => { return sprintf( __( 'VAT invoice for %s to %s', 'woocommerce-payments' ), formatUserDateTime( - moment.utc( document.period_from ).toISOString(), - { includeTime: false } + moment.utc( document.period_from ).toISOString() ), formatUserDateTime( - moment.utc( document.period_to ).toISOString(), - { - includeTime: false, - } + moment.utc( document.period_to ).toISOString() ) ); } @@ -181,10 +177,7 @@ export const DocumentsList = (): JSX.Element => { date: { value: document.date, display: formatUserDateTime( - moment.utc( document.date ).toISOString(), - { - includeTime: false, - } + moment.utc( document.date ).toISOString() ), }, type: { diff --git a/client/overview/task-list/tasks/dispute-task.tsx b/client/overview/task-list/tasks/dispute-task.tsx index f7be6ec8e13..46d4c1181bc 100644 --- a/client/overview/task-list/tasks/dispute-task.tsx +++ b/client/overview/task-list/tasks/dispute-task.tsx @@ -156,10 +156,7 @@ export const getDisputeResolutionTask = ( ), // Show due_by date in local timezone: e.g. "Jan 1, 2021". formatUserDateTime( - moment.utc( dispute.due_by ).toISOString(), - { - includeTime: false, - } + moment.utc( dispute.due_by ).toISOString() ), moment( dispute.due_by ).fromNow( true ) // E.g. "2 days". ); diff --git a/client/payment-details/dispute-details/dispute-due-by-date.tsx b/client/payment-details/dispute-details/dispute-due-by-date.tsx index 697485c9d63..d68dfa60ada 100644 --- a/client/payment-details/dispute-details/dispute-due-by-date.tsx +++ b/client/payment-details/dispute-details/dispute-due-by-date.tsx @@ -18,6 +18,7 @@ const DisputeDueByDate: React.FC< { moment( dueBy * 1000 ).toISOString(), { separator: ', ', + includeTime: true, } ); return ( diff --git a/client/payment-details/dispute-details/dispute-resolution-footer.tsx b/client/payment-details/dispute-details/dispute-resolution-footer.tsx index e6b38213abc..b20f6899e7d 100644 --- a/client/payment-details/dispute-details/dispute-resolution-footer.tsx +++ b/client/payment-details/dispute-details/dispute-resolution-footer.tsx @@ -27,7 +27,8 @@ const DisputeUnderReviewFooter: React.FC< { .unix( parseInt( dispute.metadata.__evidence_submitted_at, 10 ) ) - .toISOString() + .toISOString(), + { includeTime: true } ) : '-'; @@ -97,7 +98,8 @@ const DisputeWonFooter: React.FC< { .unix( parseInt( dispute.metadata.__dispute_closed_at, 10 ) ) - .toISOString() + .toISOString(), + { includeTime: true } ) : '-'; @@ -174,8 +176,7 @@ const DisputeLostFooter: React.FC< { .unix( parseInt( dispute.metadata.__dispute_closed_at, 10 ) ) - .toISOString(), - { includeTime: false } + .toISOString() ) : '-'; @@ -277,8 +278,7 @@ const InquiryUnderReviewFooter: React.FC< { .unix( parseInt( dispute.metadata.__evidence_submitted_at, 10 ) ) - .toISOString(), - { includeTime: false } + .toISOString() ) : '-'; @@ -349,8 +349,7 @@ const InquiryClosedFooter: React.FC< { .unix( parseInt( dispute.metadata.__dispute_closed_at, 10 ) ) - .toISOString(), - { includeTime: false } + .toISOString() ) : '-'; diff --git a/client/payment-details/dispute-details/dispute-steps.tsx b/client/payment-details/dispute-details/dispute-steps.tsx index 2fce833c4ec..c6cb8065fa2 100644 --- a/client/payment-details/dispute-details/dispute-steps.tsx +++ b/client/payment-details/dispute-details/dispute-steps.tsx @@ -35,14 +35,10 @@ export const DisputeSteps: React.FC< Props > = ( { let emailLink; if ( customer?.email ) { const chargeDate = formatUserDateTime( - moment( chargeCreated * 1000 ).toISOString(), - { - includeTime: false, - } + moment( chargeCreated * 1000 ).toISOString() ); const disputeDate = formatUserDateTime( - moment( dispute.created * 1000 ).toISOString(), - { includeTime: false } + moment( dispute.created * 1000 ).toISOString() ); const emailSubject = sprintf( // Translators: %1$s is the store name, %2$s is the charge date. @@ -176,10 +172,12 @@ export const InquirySteps: React.FC< Props > = ( { let emailLink; if ( customer?.email ) { const chargeDate = formatUserDateTime( - moment( chargeCreated * 1000 ).toISOString() + moment( chargeCreated * 1000 ).toISOString(), + { includeTime: true } ); const disputeDate = formatUserDateTime( - moment( dispute.created * 1000 ).toISOString() + moment( dispute.created * 1000 ).toISOString(), + { includeTime: true } ); const emailSubject = sprintf( // Translators: %1$s is the store name, %2$s is the charge date. diff --git a/client/payment-details/dispute-details/dispute-summary-row.tsx b/client/payment-details/dispute-details/dispute-summary-row.tsx index fbe7371fd56..298e88aaa5c 100644 --- a/client/payment-details/dispute-details/dispute-summary-row.tsx +++ b/client/payment-details/dispute-details/dispute-summary-row.tsx @@ -41,7 +41,7 @@ const DisputeSummaryRow: React.FC< Props > = ( { dispute } ) => { content: dispute.created ? formatUserDateTime( moment( dispute.created * 1000 ).toISOString(), - { separator: ', ' } + { separator: ', ', includeTime: true } ) : '–', }, diff --git a/client/payment-details/summary/index.tsx b/client/payment-details/summary/index.tsx index 8965c5b8587..d70e87386bc 100644 --- a/client/payment-details/summary/index.tsx +++ b/client/payment-details/summary/index.tsx @@ -112,7 +112,7 @@ const composePaymentSummaryItems = ( { content: charge.created ? formatUserDateTime( moment.utc( charge.created * 1000 ).toISOString(), - { separator: ', ' } + { separator: ', ', includeTime: true } ) : '–', }, @@ -711,7 +711,7 @@ const PaymentDetailsSummary: React.FC< PaymentDetailsSummaryProps > = ( { .utc( authorization.created ) .add( 7, 'days' ) .toISOString(), - { useGmt: true } + { useGmt: true, includeTime: true } ) } > diff --git a/client/payment-details/timeline/map-events.js b/client/payment-details/timeline/map-events.js index 1a3ffe5546f..4258e953195 100644 --- a/client/payment-details/timeline/map-events.js +++ b/client/payment-details/timeline/map-events.js @@ -85,8 +85,7 @@ const getDepositTimelineItem = ( ), formattedAmount, formatUserDateTime( - moment( event.deposit.arrival_date * 1000 ).toISOString(), - { includeTime: false } + moment( event.deposit.arrival_date * 1000 ).toISOString() ) ); const depositUrl = getAdminUrl( { @@ -144,8 +143,7 @@ const getFinancingPaydownTimelineItem = ( event, formattedAmount, body ) => { ), formattedAmount, formatUserDateTime( - moment( event.deposit.arrival_date * 1000 ).toISOString(), - { includeTime: false } + moment( event.deposit.arrival_date * 1000 ).toISOString() ) ); diff --git a/client/transactions/blocked/columns.tsx b/client/transactions/blocked/columns.tsx index c1e6cedd386..829167e077f 100644 --- a/client/transactions/blocked/columns.tsx +++ b/client/transactions/blocked/columns.tsx @@ -71,7 +71,8 @@ export const getBlockedListRowContent = ( 'transactions' ); const formattedCreatedDate = formatUserDateTime( - moment.utc( data.created ).toISOString() + moment.utc( data.created ).toISOString(), + { includeTime: true } ); const clickable = ( children: JSX.Element | string ) => ( diff --git a/client/transactions/list/deposit.tsx b/client/transactions/list/deposit.tsx index ff032aaab66..4c4292b88fb 100644 --- a/client/transactions/list/deposit.tsx +++ b/client/transactions/list/deposit.tsx @@ -34,8 +34,7 @@ const Deposit: React.FC< DepositProps > = ( { depositId, dateAvailable } ) => { const formattedDateAvailable = formatUserDateTime( moment.utc( dateAvailable ).toISOString(), { - includeTime: false, - useGmt: true, + useGmt: true, // TODO: should we allow user settings } ); return { formattedDateAvailable }; diff --git a/client/transactions/list/index.tsx b/client/transactions/list/index.tsx index 7ff37084ad3..dcc1e12c48f 100644 --- a/client/transactions/list/index.tsx +++ b/client/transactions/list/index.tsx @@ -466,7 +466,9 @@ export const TransactionsList = ( date: { value: txn.date, display: clickable( - formatUserDateTime( moment.utc( txn.date ).toISOString() ) + formatUserDateTime( moment.utc( txn.date ).toISOString(), { + includeTime: true, + } ) ), }, channel: { diff --git a/client/transactions/risk-review/columns.tsx b/client/transactions/risk-review/columns.tsx index 3de9987065c..a1abfeb067c 100644 --- a/client/transactions/risk-review/columns.tsx +++ b/client/transactions/risk-review/columns.tsx @@ -77,7 +77,8 @@ export const getRiskReviewListRowContent = ( ): Record< string, TableCardBodyColumn > => { const detailsURL = getDetailsURL( data.payment_intent.id, 'transactions' ); const formattedCreatedDate = formatUserDateTime( - moment.utc( data.created ).toISOString() + moment.utc( data.created ).toISOString(), + { includeTime: true } ); const clickable = ( children: JSX.Element | string ) => ( diff --git a/client/transactions/uncaptured/index.tsx b/client/transactions/uncaptured/index.tsx index 97581af5d91..7d7d993ee42 100644 --- a/client/transactions/uncaptured/index.tsx +++ b/client/transactions/uncaptured/index.tsx @@ -131,22 +131,26 @@ export const AuthorizationsList = (): JSX.Element => { }, created: { value: formatUserDateTime( - moment.utc( auth.created ).toISOString() + moment.utc( auth.created ).toISOString(), + { includeTime: true } ), display: clickable( formatUserDateTime( - moment.utc( auth.created ).toISOString() + moment.utc( auth.created ).toISOString(), + { includeTime: true } ) ), }, // Payments are authorized for a maximum of 7 days capture_by: { value: formatUserDateTime( - moment.utc( auth.created ).add( 7, 'd' ).toISOString() + moment.utc( auth.created ).add( 7, 'd' ).toISOString(), + { includeTime: true } ), display: clickable( formatUserDateTime( - moment.utc( auth.created ).add( 7, 'd' ).toISOString() + moment.utc( auth.created ).add( 7, 'd' ).toISOString(), + { includeTime: true } ) ), }, diff --git a/client/utils/date-time.ts b/client/utils/date-time.ts index 61ae1a046a0..6fc14a826b0 100644 --- a/client/utils/date-time.ts +++ b/client/utils/date-time.ts @@ -22,7 +22,7 @@ interface FormatDateTimeOptions { export function formatUserDateTime( dateTime: string | Date, options: FormatDateTimeOptions = { - includeTime: true, + includeTime: false, useGmt: false, separator: ' / ', customFormat: null, @@ -30,7 +30,7 @@ export function formatUserDateTime( ): string { const { customFormat = null, - includeTime = true, + includeTime = false, useGmt = false, separator = ' / ', } = options; diff --git a/client/utils/test/date-time.test.ts b/client/utils/test/date-time.test.ts index d0329b2cad5..a4b116bcf50 100644 --- a/client/utils/test/date-time.test.ts +++ b/client/utils/test/date-time.test.ts @@ -22,7 +22,9 @@ describe( 'formatUserDateTime', () => { describe( 'with string input', () => { it( 'should format using default WordPress settings', () => { const dateTime = '2024-10-23 15:28:26'; - const formatted = formatUserDateTime( dateTime ); + const formatted = formatUserDateTime( dateTime, { + includeTime: true, + } ); expect( formatted ).toBe( '2024-10-23 / 15:28' ); } ); @@ -37,15 +39,14 @@ describe( 'formatUserDateTime', () => { it( 'should exclude time if includeTime is set to false', () => { const dateTime = '2024-10-23 15:28:26'; - const options = { includeTime: false }; - const formatted = formatUserDateTime( dateTime, options ); + const formatted = formatUserDateTime( dateTime ); expect( formatted ).toBe( '2024-10-23' ); } ); it( 'should use custom separator when provided', () => { const dateTime = '2024-10-23 15:28:26'; - const options = { separator: ' - ' }; + const options = { separator: ' - ', includeTime: true }; const formatted = formatUserDateTime( dateTime, options ); expect( formatted ).toBe( '2024-10-23 - 15:28' ); @@ -53,7 +54,7 @@ describe( 'formatUserDateTime', () => { it( 'should handle GMT/UTC setting correctly when useGmt is true', () => { const dateTime = '2024-10-23 15:28:26Z'; - const options = { useGmt: true }; + const options = { useGmt: true, includeTime: true }; const formatted = formatUserDateTime( dateTime, options ); // Expect UTC-based output (no timezone adjustment) @@ -64,7 +65,10 @@ describe( 'formatUserDateTime', () => { describe( 'with Date object input', () => { it( 'should format using default WordPress settings', () => { const dateTime = new Date( Date.UTC( 2024, 9, 23, 15, 28, 26 ) ); - const formatted = formatUserDateTime( dateTime, { useGmt: true } ); + const formatted = formatUserDateTime( dateTime, { + useGmt: true, + includeTime: true, + } ); expect( formatted ).toBe( '2024-10-23 / 15:28' ); } ); @@ -79,7 +83,7 @@ describe( 'formatUserDateTime', () => { it( 'should exclude time if includeTime is set to false', () => { const dateTime = new Date( Date.UTC( 2024, 9, 23, 15, 28, 26 ) ); - const options = { includeTime: false, useGmt: true }; + const options = { useGmt: true }; const formatted = formatUserDateTime( dateTime, options ); expect( formatted ).toBe( '2024-10-23' ); @@ -87,7 +91,9 @@ describe( 'formatUserDateTime', () => { it( 'should handle GMT/UTC setting correctly', () => { const dateTime = new Date( 2024, 9, 23, 15, 28, 26 ); // Local time (non-UTC) - const formatted = formatUserDateTime( dateTime ); + const formatted = formatUserDateTime( dateTime, { + includeTime: true, + } ); const expectedFormat = dateI18n( `${ mockWcpaySettings.dateFormat } / ${ mockWcpaySettings.timeFormat }`, @@ -100,7 +106,11 @@ describe( 'formatUserDateTime', () => { it( 'should use custom separator when provided', () => { const dateTime = new Date( Date.UTC( 2024, 9, 23, 15, 28, 26 ) ); - const options = { separator: ' - ', useGmt: true }; + const options = { + separator: ' - ', + useGmt: true, + includeTime: true, + }; const formatted = formatUserDateTime( dateTime, options ); expect( formatted ).toBe( '2024-10-23 - 15:28' ); From 368e80e72c06e2777cb87bb5b9c40d60b62de473 Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Thu, 31 Oct 2024 11:52:31 +0100 Subject: [PATCH 48/76] Add test case for escaping characters with custom format --- client/utils/test/date-time.test.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/client/utils/test/date-time.test.ts b/client/utils/test/date-time.test.ts index a4b116bcf50..4423f40aeb2 100644 --- a/client/utils/test/date-time.test.ts +++ b/client/utils/test/date-time.test.ts @@ -60,6 +60,13 @@ describe( 'formatUserDateTime', () => { // Expect UTC-based output (no timezone adjustment) expect( formatted ).toBe( '2024-10-23 / 15:28' ); } ); + + it( 'should support escaping characters with custom format', () => { + const dateTime = '2024-10-23 15:28:26'; + const options = { customFormat: "'l \\t\\h\\e jS'" }; + const formatted = formatUserDateTime( dateTime, options ); + expect( formatted ).toBe( "'Wednesday the 23rd'" ); + } ); } ); describe( 'with Date object input', () => { From 666473d233e287747e8fc427a14b6ea327ec36ad Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Thu, 31 Oct 2024 12:00:54 +0100 Subject: [PATCH 49/76] Add test case for output unrecognized characters as-is --- client/utils/test/date-time.test.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/client/utils/test/date-time.test.ts b/client/utils/test/date-time.test.ts index 4423f40aeb2..45a1c96ee73 100644 --- a/client/utils/test/date-time.test.ts +++ b/client/utils/test/date-time.test.ts @@ -67,6 +67,13 @@ describe( 'formatUserDateTime', () => { const formatted = formatUserDateTime( dateTime, options ); expect( formatted ).toBe( "'Wednesday the 23rd'" ); } ); + + it( 'should output unrecognized characters as-is', () => { + const dateTime = '2024-10-23 15:28:26'; + const options = { customFormat: '-' }; + const formatted = formatUserDateTime( dateTime, options ); + expect( formatted ).toBe( '-' ); + } ); } ); describe( 'with Date object input', () => { From be35e50f86a78923b68b2ae1f56304f222e0873a Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Tue, 5 Nov 2024 15:30:16 +0100 Subject: [PATCH 50/76] WIP: add tooltip --- client/transactions/list/index.tsx | 35 ++++++++++++++++++++++++++++- client/transactions/list/style.scss | 7 ++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/client/transactions/list/index.tsx b/client/transactions/list/index.tsx index dcc1e12c48f..6a63893c687 100644 --- a/client/transactions/list/index.tsx +++ b/client/transactions/list/index.tsx @@ -70,6 +70,8 @@ import { HoverTooltip } from 'components/tooltip'; import { PAYMENT_METHOD_TITLES } from 'wcpay/constants/payment-method'; import { ReportingExportLanguageHook } from 'wcpay/settings/reporting-settings/interfaces'; import { formatUserDateTime } from 'wcpay/utils/date-time'; +import { Icon } from '@wordpress/components'; +import interpolateComponents from '@automattic/interpolate-components'; interface TransactionsListProps { depositId?: string; @@ -157,7 +159,38 @@ const getColumns = ( }, { key: 'date', - label: __( 'Date / Time', 'woocommerce-payments' ), + label: ( + <> + { __( 'Date / Time', 'woocommerce-payments' ) } + + ), + }, + } ) } + > + + + + ), screenReaderLabel: __( 'Date and time', 'woocommerce-payments' ), labelInCsv: __( 'Date / Time (UTC)', 'woocommerce-payments' ), required: true, diff --git a/client/transactions/list/style.scss b/client/transactions/list/style.scss index 45d8494de81..15fdfb468e3 100644 --- a/client/transactions/list/style.scss +++ b/client/transactions/list/style.scss @@ -33,6 +33,13 @@ $space-header-item: 12px; text-align: center; } + .woocommerce-table__header { + .my-icon { + font-size: 12px; + line-height: 1.4; + } + } + .woocommerce-taptopay__icon { position: relative; display: inline-block; From cfce0303761b41b168db9a9065a7473a16fb114c Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Wed, 27 Nov 2024 20:24:32 +0100 Subject: [PATCH 51/76] Add date format notice component --- .../components/date-format-notice/index.tsx | 42 +++++++++++++++++++ client/transactions/index.tsx | 2 + 2 files changed, 44 insertions(+) create mode 100644 client/components/date-format-notice/index.tsx diff --git a/client/components/date-format-notice/index.tsx b/client/components/date-format-notice/index.tsx new file mode 100644 index 00000000000..d7ded4fc1d5 --- /dev/null +++ b/client/components/date-format-notice/index.tsx @@ -0,0 +1,42 @@ +/** + * External dependencies + */ +import React, { useState } from 'react'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import BannerNotice from 'components/banner-notice'; + +const DateFormatNotice: React.FC = () => { + const [ isBannerVisible, setIsBannerVisible ] = useState( true ); + + if ( ! isBannerVisible ) { + return null; + } + + return ( + setIsBannerVisible( false ) } + actions={ [ + { + label: __( + 'Configure date settings', + 'woocommerce-payments' + ), + url: '/wp-admin/options-general.php', + }, + ] } + > + { __( + 'The date and time formats now follow your preferences. You can customize these formats in the settings.', + 'woocommerce-payments' + ) } + + ); +}; + +export default DateFormatNotice; diff --git a/client/transactions/index.tsx b/client/transactions/index.tsx index 507a972cd5a..9350404a89e 100644 --- a/client/transactions/index.tsx +++ b/client/transactions/index.tsx @@ -23,6 +23,7 @@ import { } from 'wcpay/data'; import WCPaySettingsContext from '../settings/wcpay-settings-context'; import BlockedList from './blocked'; +import DateFormatNotice from 'components/date-format-notice'; declare const window: any; @@ -107,6 +108,7 @@ export const TransactionsPage: React.FC = () => { return ( + Date: Wed, 27 Nov 2024 20:26:22 +0100 Subject: [PATCH 52/76] Revert "WIP: add tooltip" This reverts commit be35e50f86a78923b68b2ae1f56304f222e0873a. --- client/transactions/list/index.tsx | 35 +---------------------------- client/transactions/list/style.scss | 7 ------ 2 files changed, 1 insertion(+), 41 deletions(-) diff --git a/client/transactions/list/index.tsx b/client/transactions/list/index.tsx index 6a63893c687..dcc1e12c48f 100644 --- a/client/transactions/list/index.tsx +++ b/client/transactions/list/index.tsx @@ -70,8 +70,6 @@ import { HoverTooltip } from 'components/tooltip'; import { PAYMENT_METHOD_TITLES } from 'wcpay/constants/payment-method'; import { ReportingExportLanguageHook } from 'wcpay/settings/reporting-settings/interfaces'; import { formatUserDateTime } from 'wcpay/utils/date-time'; -import { Icon } from '@wordpress/components'; -import interpolateComponents from '@automattic/interpolate-components'; interface TransactionsListProps { depositId?: string; @@ -159,38 +157,7 @@ const getColumns = ( }, { key: 'date', - label: ( - <> - { __( 'Date / Time', 'woocommerce-payments' ) } - - ), - }, - } ) } - > - - - - ), + label: __( 'Date / Time', 'woocommerce-payments' ), screenReaderLabel: __( 'Date and time', 'woocommerce-payments' ), labelInCsv: __( 'Date / Time (UTC)', 'woocommerce-payments' ), required: true, diff --git a/client/transactions/list/style.scss b/client/transactions/list/style.scss index 15fdfb468e3..45d8494de81 100644 --- a/client/transactions/list/style.scss +++ b/client/transactions/list/style.scss @@ -33,13 +33,6 @@ $space-header-item: 12px; text-align: center; } - .woocommerce-table__header { - .my-icon { - font-size: 12px; - line-height: 1.4; - } - } - .woocommerce-taptopay__icon { position: relative; display: inline-block; From 857dbf015c1044529c13e58a8e478d00889f3fda Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Thu, 28 Nov 2024 17:07:59 +0100 Subject: [PATCH 53/76] Fix capital dates not converted to UTC --- client/capital/index.tsx | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/client/capital/index.tsx b/client/capital/index.tsx index 8d41f418ec2..ba954fd1381 100644 --- a/client/capital/index.tsx +++ b/client/capital/index.tsx @@ -25,6 +25,7 @@ import { useLoans } from 'wcpay/data'; import { getAdminUrl } from 'wcpay/utils'; import './style.scss'; import { formatUserDateTime } from 'wcpay/utils/date-time'; +import moment from 'moment'; const columns = [ { @@ -80,7 +81,9 @@ const getLoanStatusText = ( loan: CapitalLoan ) => { return loan.fully_paid_at ? __( 'Paid off', 'woocommerce-payments' ) + ': ' + - formatUserDateTime( loan.fully_paid_at ) + formatUserDateTime( + moment.utc( loan.fully_paid_at ).toISOString() + ) : __( 'Active', 'woocommerce-payments' ); }; @@ -112,7 +115,11 @@ const getRowsData = ( loans: CapitalLoan[] ) => const data = { paid_out_at: { value: loan.paid_out_at, - display: clickable( formatUserDateTime( loan.paid_out_at ) ), + display: clickable( + formatUserDateTime( + moment.utc( loan.paid_out_at ).toISOString() + ) + ), }, status: { value: getLoanStatusText( loan ), @@ -150,7 +157,11 @@ const getRowsData = ( loans: CapitalLoan[] ) => value: loan.first_paydown_at, display: clickable( loan.first_paydown_at - ? formatUserDateTime( loan.first_paydown_at ) + ? formatUserDateTime( + moment + .utc( loan.first_paydown_at ) + .toISOString() + ) : '-' ), }, From 18db802a581a4510eb9b0986cc1b8cf2e7e80f7b Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Thu, 28 Nov 2024 18:06:30 +0100 Subject: [PATCH 54/76] Use unix utc timestamps --- client/payment-details/timeline/map-events.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/payment-details/timeline/map-events.js b/client/payment-details/timeline/map-events.js index 4258e953195..f3a0f642523 100644 --- a/client/payment-details/timeline/map-events.js +++ b/client/payment-details/timeline/map-events.js @@ -85,7 +85,7 @@ const getDepositTimelineItem = ( ), formattedAmount, formatUserDateTime( - moment( event.deposit.arrival_date * 1000 ).toISOString() + moment.unix( event.deposit.arrival_date ).utc().toISOString() ) ); const depositUrl = getAdminUrl( { @@ -143,7 +143,7 @@ const getFinancingPaydownTimelineItem = ( event, formattedAmount, body ) => { ), formattedAmount, formatUserDateTime( - moment( event.deposit.arrival_date * 1000 ).toISOString() + moment.unix( event.deposit.arrival_date ).utc().toISOString() ) ); From d80cf69a36debe24efbb83d8ce17381434d8dd9a Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Thu, 28 Nov 2024 18:47:19 +0100 Subject: [PATCH 55/76] Convert unix utc timestamp --- client/disputes/info/index.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/client/disputes/info/index.tsx b/client/disputes/info/index.tsx index 363a8573a4b..bca4e0d9d79 100644 --- a/client/disputes/info/index.tsx +++ b/client/disputes/info/index.tsx @@ -70,7 +70,7 @@ const Info = ( { } : { created: formatUserDateTime( - moment( dispute.created * 1000 ).toISOString() + moment.unix( dispute.created ).utc().toISOString() ), amount: formatExplicitCurrency( dispute.amount || 0, @@ -78,9 +78,10 @@ const Info = ( { ), dueBy: dispute.evidence_details ? formatUserDateTime( - moment( - dispute.evidence_details.due_by * 1000 - ).toISOString(), + moment + .unix( dispute.evidence_details.due_by ) + .utc() + .toISOString(), { separator: ' - ', includeTime: true } ) : null, From ea671990473e04e63bcd89c7deff6569b5463172 Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Fri, 29 Nov 2024 10:41:25 +0100 Subject: [PATCH 56/76] Convert to utc and remove call to local --- client/components/disputed-order-notice/index.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/client/components/disputed-order-notice/index.js b/client/components/disputed-order-notice/index.js index 7b64eb01e52..43b474c7a38 100644 --- a/client/components/disputed-order-notice/index.js +++ b/client/components/disputed-order-notice/index.js @@ -84,8 +84,10 @@ const DisputedOrderNoticeHandler = ( { chargeId, onDisableOrderRefund } ) => { return null; } - const now = moment(); - const dueBy = moment.unix( dispute.evidence_details?.due_by ); + // Get current time in UTC for consistent timezone-independent comparison + const now = moment().utc(); + // Parse the Unix timestamp as UTC since it's stored that way in the API + const dueBy = moment.unix( dispute.evidence_details?.due_by ).utc(); // If the dispute is due in the past, don't show notice. if ( ! now.isBefore( dueBy ) ) { @@ -131,7 +133,7 @@ const UrgentDisputeNoticeBody = ( { formatString, formattedAmount, reasons[ disputeReason ].display, - formatUserDateTime( dueBy.local().toISOString() ) + formatUserDateTime( dueBy.toISOString() ) ); let suffix = sprintf( @@ -182,7 +184,7 @@ const RegularDisputeNoticeBody = ( { const suffix = sprintf( // Translators: %1$s is the dispute due date. __( 'Please respond before %1$s.', 'woocommerce-payments' ), - formatUserDateTime( dueBy.local().toISOString() ) + formatUserDateTime( dueBy.toISOString() ) ); return ( From fa2808d624086eca97ff5a600d30c127887e2bde Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Fri, 29 Nov 2024 11:19:35 +0100 Subject: [PATCH 57/76] Use unix to handle timestamps and utc conversion --- .../payment-details/dispute-details/dispute-due-by-date.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/payment-details/dispute-details/dispute-due-by-date.tsx b/client/payment-details/dispute-details/dispute-due-by-date.tsx index d68dfa60ada..88287a74458 100644 --- a/client/payment-details/dispute-details/dispute-due-by-date.tsx +++ b/client/payment-details/dispute-details/dispute-due-by-date.tsx @@ -12,10 +12,10 @@ const DisputeDueByDate: React.FC< { showRemainingDays?: boolean; } > = ( { dueBy, showRemainingDays = true } ) => { const daysRemaining = Math.floor( - moment.unix( dueBy ).diff( moment(), 'days', true ) + moment.unix( dueBy ).utc().diff( moment().utc(), 'days', true ) ); const respondByDate = formatUserDateTime( - moment( dueBy * 1000 ).toISOString(), + moment.unix( dueBy ).utc().toISOString(), { separator: ', ', includeTime: true, From d59ad852348632230083da9fce17286ce2323f5d Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Fri, 29 Nov 2024 11:29:41 +0100 Subject: [PATCH 58/76] Add explicit utc conversion --- .../dispute-details/dispute-resolution-footer.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/client/payment-details/dispute-details/dispute-resolution-footer.tsx b/client/payment-details/dispute-details/dispute-resolution-footer.tsx index b20f6899e7d..625bc491e98 100644 --- a/client/payment-details/dispute-details/dispute-resolution-footer.tsx +++ b/client/payment-details/dispute-details/dispute-resolution-footer.tsx @@ -27,6 +27,7 @@ const DisputeUnderReviewFooter: React.FC< { .unix( parseInt( dispute.metadata.__evidence_submitted_at, 10 ) ) + .utc() .toISOString(), { includeTime: true } ) @@ -98,6 +99,7 @@ const DisputeWonFooter: React.FC< { .unix( parseInt( dispute.metadata.__dispute_closed_at, 10 ) ) + .utc() .toISOString(), { includeTime: true } ) @@ -176,6 +178,7 @@ const DisputeLostFooter: React.FC< { .unix( parseInt( dispute.metadata.__dispute_closed_at, 10 ) ) + .utc() .toISOString() ) : '-'; @@ -278,6 +281,7 @@ const InquiryUnderReviewFooter: React.FC< { .unix( parseInt( dispute.metadata.__evidence_submitted_at, 10 ) ) + .utc() .toISOString() ) : '-'; @@ -349,6 +353,7 @@ const InquiryClosedFooter: React.FC< { .unix( parseInt( dispute.metadata.__dispute_closed_at, 10 ) ) + .utc() .toISOString() ) : '-'; From 46ef7d2ed63bfb85219dc13addef19f2abfc230b Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Fri, 29 Nov 2024 11:37:19 +0100 Subject: [PATCH 59/76] Convert unix utc timestamp --- client/payment-details/dispute-details/dispute-steps.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/payment-details/dispute-details/dispute-steps.tsx b/client/payment-details/dispute-details/dispute-steps.tsx index c6cb8065fa2..7fd24feeb9b 100644 --- a/client/payment-details/dispute-details/dispute-steps.tsx +++ b/client/payment-details/dispute-details/dispute-steps.tsx @@ -35,10 +35,10 @@ export const DisputeSteps: React.FC< Props > = ( { let emailLink; if ( customer?.email ) { const chargeDate = formatUserDateTime( - moment( chargeCreated * 1000 ).toISOString() + moment.unix( chargeCreated ).utc().toISOString() ); const disputeDate = formatUserDateTime( - moment( dispute.created * 1000 ).toISOString() + moment.unix( dispute.created ).utc().toISOString() ); const emailSubject = sprintf( // Translators: %1$s is the store name, %2$s is the charge date. @@ -172,11 +172,11 @@ export const InquirySteps: React.FC< Props > = ( { let emailLink; if ( customer?.email ) { const chargeDate = formatUserDateTime( - moment( chargeCreated * 1000 ).toISOString(), + moment.unix( chargeCreated ).utc().toISOString(), { includeTime: true } ); const disputeDate = formatUserDateTime( - moment( dispute.created * 1000 ).toISOString(), + moment.unix( dispute.created ).utc().toISOString(), { includeTime: true } ); const emailSubject = sprintf( From 4cbbb52e3868f1ae2057cf7d13f442c6d7947c9c Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Fri, 29 Nov 2024 11:47:26 +0100 Subject: [PATCH 60/76] Convert unix utc timestamp --- client/payment-details/dispute-details/dispute-summary-row.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/payment-details/dispute-details/dispute-summary-row.tsx b/client/payment-details/dispute-details/dispute-summary-row.tsx index 298e88aaa5c..3ac801ebd4a 100644 --- a/client/payment-details/dispute-details/dispute-summary-row.tsx +++ b/client/payment-details/dispute-details/dispute-summary-row.tsx @@ -40,7 +40,7 @@ const DisputeSummaryRow: React.FC< Props > = ( { dispute } ) => { title: __( 'Disputed On', 'woocommerce-payments' ), content: dispute.created ? formatUserDateTime( - moment( dispute.created * 1000 ).toISOString(), + moment.unix( dispute.created ).utc().toISOString(), { separator: ', ', includeTime: true } ) : '–', From 4ee476bc0ad7b91b420c552d8eb0b83da039993d Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Fri, 29 Nov 2024 11:54:46 +0100 Subject: [PATCH 61/76] Convert to utc --- .../account-status/account-fees/expiration-description.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/components/account-status/account-fees/expiration-description.js b/client/components/account-status/account-fees/expiration-description.js index 6cca98d0df1..1458f911f33 100644 --- a/client/components/account-status/account-fees/expiration-description.js +++ b/client/components/account-status/account-fees/expiration-description.js @@ -26,7 +26,7 @@ const ExpirationDescription = ( { 'woocommerce-payments' ), formatCurrency( volumeAllowance, currencyCode ), - formatUserDateTime( moment( endTime ).toISOString() ) + formatUserDateTime( moment.utc( endTime ).toISOString() ) ); } else if ( volumeAllowance ) { description = sprintf( @@ -44,7 +44,7 @@ const ExpirationDescription = ( { 'Discounted base fee expires on %1$s.', 'woocommerce-payments' ), - formatUserDateTime( moment( endTime ).toISOString() ) + formatUserDateTime( moment.utc( endTime ).toISOString() ) ); } else { return null; From 777f722c0ce7cb3a393c227a7c894af011f77cb2 Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Fri, 29 Nov 2024 17:25:56 +0100 Subject: [PATCH 62/76] Introduce formatDateTimeFromString and formatDateTimeFromTimestamp --- client/capital/index.tsx | 16 +--- .../account-fees/expiration-description.js | 7 +- .../components/active-loan-summary/index.tsx | 17 ++-- .../components/disputed-order-notice/index.js | 6 +- client/deposits/details/index.tsx | 4 +- client/deposits/list/index.tsx | 18 ++-- client/deposits/utils/index.ts | 4 +- client/disputes/index.tsx | 23 ++--- client/disputes/info/index.tsx | 14 +-- client/disputes/test/index.tsx | 9 +- client/documents/list/index.tsx | 14 +-- .../modal/update-business-details/index.tsx | 11 +-- .../overview/task-list/tasks/dispute-task.tsx | 17 ++-- .../tasks/update-business-details-task.tsx | 12 +-- .../dispute-details/dispute-due-by-date.tsx | 13 +-- .../dispute-resolution-footer.tsx | 47 +++------ .../dispute-details/dispute-steps.tsx | 24 ++--- .../dispute-details/dispute-summary-row.tsx | 11 +-- client/payment-details/summary/index.tsx | 16 ++-- client/payment-details/timeline/map-events.js | 11 +-- client/transactions/blocked/columns.tsx | 10 +- client/transactions/list/deposit.tsx | 7 +- client/transactions/list/index.tsx | 5 +- client/transactions/risk-review/columns.tsx | 10 +- client/transactions/uncaptured/index.tsx | 20 ++-- client/utils/date-time.ts | 58 ++++++++--- client/utils/test/date-time.test.ts | 96 ++++++++----------- 27 files changed, 214 insertions(+), 286 deletions(-) diff --git a/client/capital/index.tsx b/client/capital/index.tsx index ba954fd1381..7e6c5957db6 100644 --- a/client/capital/index.tsx +++ b/client/capital/index.tsx @@ -24,7 +24,7 @@ import Chip from 'components/chip'; import { useLoans } from 'wcpay/data'; import { getAdminUrl } from 'wcpay/utils'; import './style.scss'; -import { formatUserDateTime } from 'wcpay/utils/date-time'; +import { formatDateTimeFromString } from 'wcpay/utils/date-time'; import moment from 'moment'; const columns = [ @@ -81,9 +81,7 @@ const getLoanStatusText = ( loan: CapitalLoan ) => { return loan.fully_paid_at ? __( 'Paid off', 'woocommerce-payments' ) + ': ' + - formatUserDateTime( - moment.utc( loan.fully_paid_at ).toISOString() - ) + formatDateTimeFromString( loan.fully_paid_at ) : __( 'Active', 'woocommerce-payments' ); }; @@ -116,9 +114,7 @@ const getRowsData = ( loans: CapitalLoan[] ) => paid_out_at: { value: loan.paid_out_at, display: clickable( - formatUserDateTime( - moment.utc( loan.paid_out_at ).toISOString() - ) + formatDateTimeFromString( loan.paid_out_at ) ), }, status: { @@ -157,11 +153,7 @@ const getRowsData = ( loans: CapitalLoan[] ) => value: loan.first_paydown_at, display: clickable( loan.first_paydown_at - ? formatUserDateTime( - moment - .utc( loan.first_paydown_at ) - .toISOString() - ) + ? formatDateTimeFromString( loan.first_paydown_at ) : '-' ), }, diff --git a/client/components/account-status/account-fees/expiration-description.js b/client/components/account-status/account-fees/expiration-description.js index 1458f911f33..497f0207ef3 100644 --- a/client/components/account-status/account-fees/expiration-description.js +++ b/client/components/account-status/account-fees/expiration-description.js @@ -4,13 +4,12 @@ * External dependencies */ import { __, sprintf } from '@wordpress/i18n'; -import moment from 'moment'; /** * Internal dependencies */ import { formatCurrency } from 'multi-currency/interface/functions'; -import { formatUserDateTime } from 'wcpay/utils/date-time'; +import { formatDateTimeFromString } from 'wcpay/utils/date-time'; const ExpirationDescription = ( { feeData: { volume_allowance: volumeAllowance, end_time: endTime, ...rest }, @@ -26,7 +25,7 @@ const ExpirationDescription = ( { 'woocommerce-payments' ), formatCurrency( volumeAllowance, currencyCode ), - formatUserDateTime( moment.utc( endTime ).toISOString() ) + formatDateTimeFromString( endTime ) ); } else if ( volumeAllowance ) { description = sprintf( @@ -44,7 +43,7 @@ const ExpirationDescription = ( { 'Discounted base fee expires on %1$s.', 'woocommerce-payments' ), - formatUserDateTime( moment.utc( endTime ).toISOString() ) + formatDateTimeFromString( endTime ) ); } else { return null; diff --git a/client/components/active-loan-summary/index.tsx b/client/components/active-loan-summary/index.tsx index f2c87037e07..679e35407c8 100755 --- a/client/components/active-loan-summary/index.tsx +++ b/client/components/active-loan-summary/index.tsx @@ -23,7 +23,7 @@ import { useActiveLoanSummary } from 'wcpay/data'; import { getAdminUrl } from 'wcpay/utils'; import './style.scss'; -import { formatUserDateTime } from 'wcpay/utils/date-time'; +import { formatDateTimeFromTimestamp } from 'wcpay/utils/date-time'; const Block = ( { title, @@ -210,11 +210,8 @@ const ActiveLoanSummary = (): JSX.Element => { 'Repaid this period (until %s)', 'woocommerce-payments' ), - formatUserDateTime( - new Date( - details.current_repayment_interval.due_at * - 1000 - ), + formatDateTimeFromTimestamp( + details.current_repayment_interval.due_at, { useGmt: true } ) ) } @@ -251,8 +248,8 @@ const ActiveLoanSummary = (): JSX.Element => { - { formatUserDateTime( - new Date( details.advance_paid_out_at * 1000 ), + { formatDateTimeFromTimestamp( + details.advance_paid_out_at, { useGmt: true } ) } @@ -278,8 +275,8 @@ const ActiveLoanSummary = (): JSX.Element => { - { formatUserDateTime( - new Date( details.repayments_begin_at * 1000 ), + { formatDateTimeFromTimestamp( + details.repayments_begin_at, { useGmt: true } ) } diff --git a/client/components/disputed-order-notice/index.js b/client/components/disputed-order-notice/index.js index 43b474c7a38..c7d1db9e7d1 100644 --- a/client/components/disputed-order-notice/index.js +++ b/client/components/disputed-order-notice/index.js @@ -19,7 +19,7 @@ import { import { useCharge } from 'wcpay/data'; import { recordEvent } from 'tracks'; import './style.scss'; -import { formatUserDateTime } from 'wcpay/utils/date-time'; +import { formatDateTimeFromString } from 'wcpay/utils/date-time'; const DisputedOrderNoticeHandler = ( { chargeId, onDisableOrderRefund } ) => { const { data: charge } = useCharge( chargeId ); @@ -133,7 +133,7 @@ const UrgentDisputeNoticeBody = ( { formatString, formattedAmount, reasons[ disputeReason ].display, - formatUserDateTime( dueBy.toISOString() ) + formatDateTimeFromString( dueBy.toISOString() ) ); let suffix = sprintf( @@ -184,7 +184,7 @@ const RegularDisputeNoticeBody = ( { const suffix = sprintf( // Translators: %1$s is the dispute due date. __( 'Please respond before %1$s.', 'woocommerce-payments' ), - formatUserDateTime( dueBy.toISOString() ) + formatDateTimeFromString( dueBy.toISOString() ) ); return ( diff --git a/client/deposits/details/index.tsx b/client/deposits/details/index.tsx index eaa35f4e950..286208846bf 100644 --- a/client/deposits/details/index.tsx +++ b/client/deposits/details/index.tsx @@ -39,7 +39,7 @@ import { } from 'multi-currency/interface/functions'; import { displayStatus } from '../strings'; import './style.scss'; -import { formatUserDateTime } from 'wcpay/utils/date-time'; +import { formatDateTimeFromString } from 'wcpay/utils/date-time'; /** * Renders the deposit status indicator UI, re-purposing the OrderStatus component from @woocommerce/components. @@ -111,7 +111,7 @@ export const DepositOverview: React.FC< DepositOverviewProps > = ( { key="depositDate" label={ `${ depositDateLabel }: ` + - formatUserDateTime( moment.utc( deposit.date ).toISOString(), { + formatDateTimeFromString( deposit.date, { useGmt: true, } ) } diff --git a/client/deposits/list/index.tsx b/client/deposits/list/index.tsx index e75afcc1461..102d5a4bc73 100644 --- a/client/deposits/list/index.tsx +++ b/client/deposits/list/index.tsx @@ -47,7 +47,7 @@ import { ReportingExportLanguageHook } from 'wcpay/settings/reporting-settings/i import './style.scss'; import { parseInt } from 'lodash'; -import { formatUserDateTime } from 'wcpay/utils/date-time'; +import { formatDateTimeFromString } from 'wcpay/utils/date-time'; const getColumns = ( sortByDate?: boolean ): DepositsTableHeader[] => [ { @@ -135,12 +135,9 @@ export const DepositsList = (): JSX.Element => { href={ getDetailsURL( deposit.id, 'deposits' ) } onClick={ () => recordEvent( 'wcpay_deposits_row_click' ) } > - { formatUserDateTime( - moment.utc( deposit.date ).toISOString(), - { - useGmt: true, - } - ) } + { formatDateTimeFromString( deposit.date, { + useGmt: true, + } ) } ); @@ -329,10 +326,9 @@ export const DepositsList = (): JSX.Element => { row[ 0 ], { ...row[ 1 ], - value: formatUserDateTime( - moment.utc( row[ 1 ].value ).toISOString(), - { useGmt: true } - ), + value: formatDateTimeFromString( row[ 1 ].value as string, { + useGmt: true, + } ), }, ...row.slice( 2 ), ] ); diff --git a/client/deposits/utils/index.ts b/client/deposits/utils/index.ts index 50abccfb387..855a7e52ddc 100644 --- a/client/deposits/utils/index.ts +++ b/client/deposits/utils/index.ts @@ -3,7 +3,7 @@ */ import { __ } from '@wordpress/i18n'; import moment from 'moment'; -import { formatUserDateTime } from 'wcpay/utils/date-time'; +import { formatDateTimeFromString } from 'wcpay/utils/date-time'; interface DepositObject { date: number | string; @@ -11,7 +11,7 @@ interface DepositObject { export const getDepositDate = ( deposit?: DepositObject | null ): string => deposit - ? formatUserDateTime( moment.utc( deposit?.date ).toISOString(), { + ? formatDateTimeFromString( deposit?.date as string, { useGmt: true, } ) : '—'; diff --git a/client/disputes/index.tsx b/client/disputes/index.tsx index a8e7b26e0aa..9ab58f49a87 100644 --- a/client/disputes/index.tsx +++ b/client/disputes/index.tsx @@ -57,7 +57,7 @@ import CSVExportModal from 'components/csv-export-modal'; import { ReportingExportLanguageHook } from 'wcpay/settings/reporting-settings/interfaces'; import './style.scss'; -import { formatUserDateTime } from 'wcpay/utils/date-time'; +import { formatDateTimeFromString } from 'wcpay/utils/date-time'; const getHeaders = ( sortColumn?: string ): DisputesTableHeader[] => [ { @@ -201,7 +201,7 @@ const smartDueDate = ( dispute: CachedDispute ) => { ); } - return formatUserDateTime( moment.utc( dispute.due_by ).toISOString(), { + return formatDateTimeFromString( dispute.due_by, { includeTime: true, } ); }; @@ -300,10 +300,9 @@ export const DisputesList = (): JSX.Element => { created: { value: dispute.created, display: clickable( - formatUserDateTime( - moment.utc( dispute.created ).toISOString(), - { includeTime: true } - ) + formatDateTimeFromString( dispute.created, { + includeTime: true, + } ) ), }, dueBy: { @@ -482,16 +481,18 @@ export const DisputesList = (): JSX.Element => { { // Disputed On. ...row[ 10 ], - value: formatUserDateTime( - moment.utc( row[ 10 ].value ).toISOString() + value: formatDateTimeFromString( + row[ 10 ].value as string ), }, { // Respond by. ...row[ 11 ], - value: formatUserDateTime( - moment.utc( row[ 11 ].value ).toISOString(), - { includeTime: true } + value: formatDateTimeFromString( + row[ 11 ].value as string, + { + includeTime: true, + } ), }, ]; diff --git a/client/disputes/info/index.tsx b/client/disputes/info/index.tsx index bca4e0d9d79..84ec4aed8aa 100644 --- a/client/disputes/info/index.tsx +++ b/client/disputes/info/index.tsx @@ -5,7 +5,6 @@ */ import * as React from 'react'; import { __ } from '@wordpress/i18n'; -import moment from 'moment'; import { Link } from '@woocommerce/components'; /** @@ -19,7 +18,7 @@ import { formatExplicitCurrency } from 'multi-currency/interface/functions'; import './style.scss'; import Loadable from 'components/loadable'; import { Dispute } from 'wcpay/types/disputes'; -import { formatUserDateTime } from 'wcpay/utils/date-time'; +import { formatDateTimeFromTimestamp } from 'wcpay/utils/date-time'; const fields: { key: string; label: string }[] = [ { key: 'created', label: __( 'Dispute date', 'woocommerce-payments' ) }, @@ -69,19 +68,14 @@ const Info = ( { transactionId: __( 'Transaction link', 'woocommerce-payments' ), } : { - created: formatUserDateTime( - moment.unix( dispute.created ).utc().toISOString() - ), + created: formatDateTimeFromTimestamp( dispute.created ), amount: formatExplicitCurrency( dispute.amount || 0, dispute.currency || 'USD' ), dueBy: dispute.evidence_details - ? formatUserDateTime( - moment - .unix( dispute.evidence_details.due_by ) - .utc() - .toISOString(), + ? formatDateTimeFromTimestamp( + dispute.evidence_details.due_by, { separator: ' - ', includeTime: true } ) : null, diff --git a/client/disputes/test/index.tsx b/client/disputes/test/index.tsx index 40a02023687..3e32172907b 100644 --- a/client/disputes/test/index.tsx +++ b/client/disputes/test/index.tsx @@ -24,7 +24,7 @@ import { DisputeReason, DisputeStatus, } from 'wcpay/types/disputes'; -import { formatUserDateTime } from 'wcpay/utils/date-time'; +import { formatDateTimeFromString } from 'wcpay/utils/date-time'; import moment from 'moment'; jest.mock( '@woocommerce/csv-export', () => { @@ -370,10 +370,9 @@ describe( 'Disputes list', () => { ); // customer expect( csvFirstDispute[ 11 ].replace( /^"|"$/g, '' ) ).toBe( - formatUserDateTime( - moment.utc( mockDisputes[ 0 ].due_by ).toISOString(), - { includeTime: true } - ) + formatDateTimeFromString( mockDisputes[ 0 ].due_by, { + includeTime: true, + } ) ); // date respond by } ); } ); diff --git a/client/documents/list/index.tsx b/client/documents/list/index.tsx index e6faa941f8c..0162e2e8998 100644 --- a/client/documents/list/index.tsx +++ b/client/documents/list/index.tsx @@ -20,7 +20,7 @@ import DocumentsFilters from '../filters'; import Page from '../../components/page'; import { getDocumentUrl } from 'wcpay/utils'; import VatFormModal from 'wcpay/vat/form-modal'; -import { formatUserDateTime } from 'wcpay/utils/date-time'; +import { formatDateTimeFromString } from 'wcpay/utils/date-time'; interface Column extends TableCardColumn { key: 'date' | 'type' | 'description' | 'download'; @@ -68,12 +68,8 @@ const getDocumentDescription = ( document: Document ) => { if ( document.period_from && document.period_to ) { return sprintf( __( 'VAT invoice for %s to %s', 'woocommerce-payments' ), - formatUserDateTime( - moment.utc( document.period_from ).toISOString() - ), - formatUserDateTime( - moment.utc( document.period_to ).toISOString() - ) + formatDateTimeFromString( document.period_from ), + formatDateTimeFromString( document.period_to ) ); } return __( @@ -176,9 +172,7 @@ export const DocumentsList = (): JSX.Element => { const data = { date: { value: document.date, - display: formatUserDateTime( - moment.utc( document.date ).toISOString() - ), + display: formatDateTimeFromString( document.date ), }, type: { value: documentType, diff --git a/client/overview/modal/update-business-details/index.tsx b/client/overview/modal/update-business-details/index.tsx index fb98eca74f3..29a649ad561 100644 --- a/client/overview/modal/update-business-details/index.tsx +++ b/client/overview/modal/update-business-details/index.tsx @@ -4,7 +4,6 @@ import React, { useState } from 'react'; import { Button, Modal, Notice } from '@wordpress/components'; import { sprintf } from '@wordpress/i18n'; -import moment from 'moment'; /** * Internal dependencies @@ -12,7 +11,7 @@ import moment from 'moment'; import strings from './strings'; import './index.scss'; import { recordEvent } from 'wcpay/tracks'; -import { formatUserDateTime } from 'wcpay/utils/date-time'; +import { formatDateTimeFromTimestamp } from 'wcpay/utils/date-time'; interface Props { errorMessages: Array< string >; @@ -57,12 +56,8 @@ const UpdateBusinessDetailsModal = ( { currentDeadline ? sprintf( strings.restrictedSoonDescription, - formatUserDateTime( - moment - .utc( - currentDeadline * 1000 - ) - .toISOString(), + formatDateTimeFromTimestamp( + currentDeadline, { customFormat: 'ga M j, Y', } diff --git a/client/overview/task-list/tasks/dispute-task.tsx b/client/overview/task-list/tasks/dispute-task.tsx index 46d4c1181bc..333c15d6709 100644 --- a/client/overview/task-list/tasks/dispute-task.tsx +++ b/client/overview/task-list/tasks/dispute-task.tsx @@ -14,7 +14,7 @@ import { formatCurrency } from 'multi-currency/interface/functions'; import { getAdminUrl } from 'wcpay/utils'; import { recordEvent } from 'tracks'; import { isDueWithin } from 'wcpay/disputes/utils'; -import { formatUserDateTime } from 'wcpay/utils/date-time'; +import { formatDateTimeFromString } from 'wcpay/utils/date-time'; /** * Returns an array of disputes that are due within the specified number of days. @@ -142,12 +142,9 @@ export const getDisputeResolutionTask = ( ? sprintf( __( 'Respond today by %s', 'woocommerce-payments' ), // Show due_by time in local timezone: e.g. "11:59 PM". - formatUserDateTime( - moment.utc( dispute.due_by ).toISOString(), - { - customFormat: 'g:i A', - } - ) + formatDateTimeFromString( dispute.due_by, { + customFormat: 'g:i A', + } ) ) : sprintf( __( @@ -155,10 +152,8 @@ export const getDisputeResolutionTask = ( 'woocommerce-payments' ), // Show due_by date in local timezone: e.g. "Jan 1, 2021". - formatUserDateTime( - moment.utc( dispute.due_by ).toISOString() - ), - moment( dispute.due_by ).fromNow( true ) // E.g. "2 days". + formatDateTimeFromString( dispute.due_by ), + moment.utc( dispute.due_by ).fromNow( true ) // E.g. "2 days". ); return disputeTask; diff --git a/client/overview/task-list/tasks/update-business-details-task.tsx b/client/overview/task-list/tasks/update-business-details-task.tsx index 2ca90519616..c1d22dc57d8 100644 --- a/client/overview/task-list/tasks/update-business-details-task.tsx +++ b/client/overview/task-list/tasks/update-business-details-task.tsx @@ -11,9 +11,8 @@ import { addQueryArgs } from '@wordpress/url'; */ import type { TaskItemProps } from '../types'; import UpdateBusinessDetailsModal from 'wcpay/overview/modal/update-business-details'; -import moment from 'moment'; import { recordEvent } from 'wcpay/tracks'; -import { formatUserDateTime } from 'wcpay/utils/date-time'; +import { formatDateTimeFromTimestamp } from 'wcpay/utils/date-time'; export const getUpdateBusinessDetailsTask = ( errorMessages: string[], @@ -46,12 +45,9 @@ export const getUpdateBusinessDetailsTask = ( 'Update by %s to avoid a disruption in deposits.', 'woocommerce-payments' ), - formatUserDateTime( - moment( currentDeadline * 1000 ).toISOString(), - { - customFormat: 'ga M j, Y', - } - ) + formatDateTimeFromTimestamp( currentDeadline, { + customFormat: 'ga M j, Y', + } ) ); if ( hasSingleError ) { diff --git a/client/payment-details/dispute-details/dispute-due-by-date.tsx b/client/payment-details/dispute-details/dispute-due-by-date.tsx index 88287a74458..91255a4d786 100644 --- a/client/payment-details/dispute-details/dispute-due-by-date.tsx +++ b/client/payment-details/dispute-details/dispute-due-by-date.tsx @@ -5,7 +5,7 @@ import React from 'react'; import { __, _n, sprintf } from '@wordpress/i18n'; import classNames from 'classnames'; import moment from 'moment'; -import { formatUserDateTime } from 'wcpay/utils/date-time'; +import { formatDateTimeFromTimestamp } from 'wcpay/utils/date-time'; const DisputeDueByDate: React.FC< { dueBy: number; @@ -14,13 +14,10 @@ const DisputeDueByDate: React.FC< { const daysRemaining = Math.floor( moment.unix( dueBy ).utc().diff( moment().utc(), 'days', true ) ); - const respondByDate = formatUserDateTime( - moment.unix( dueBy ).utc().toISOString(), - { - separator: ', ', - includeTime: true, - } - ); + const respondByDate = formatDateTimeFromTimestamp( dueBy, { + separator: ', ', + includeTime: true, + } ); return ( { respondByDate } diff --git a/client/payment-details/dispute-details/dispute-resolution-footer.tsx b/client/payment-details/dispute-details/dispute-resolution-footer.tsx index 625bc491e98..a0d454e25eb 100644 --- a/client/payment-details/dispute-details/dispute-resolution-footer.tsx +++ b/client/payment-details/dispute-details/dispute-resolution-footer.tsx @@ -16,19 +16,14 @@ import { recordEvent } from 'tracks'; import { getAdminUrl } from 'wcpay/utils'; import { getDisputeFeeFormatted } from 'wcpay/disputes/utils'; import './style.scss'; -import { formatUserDateTime } from 'wcpay/utils/date-time'; +import { formatDateTimeFromTimestamp } from 'wcpay/utils/date-time'; const DisputeUnderReviewFooter: React.FC< { dispute: Pick< Dispute, 'id' | 'metadata' | 'status' >; } > = ( { dispute } ) => { const submissionDateFormatted = dispute.metadata.__evidence_submitted_at - ? formatUserDateTime( - moment - .unix( - parseInt( dispute.metadata.__evidence_submitted_at, 10 ) - ) - .utc() - .toISOString(), + ? formatDateTimeFromTimestamp( + parseInt( dispute.metadata.__evidence_submitted_at, 10 ), { includeTime: true } ) : '-'; @@ -94,13 +89,8 @@ const DisputeWonFooter: React.FC< { dispute: Pick< Dispute, 'id' | 'metadata' | 'status' >; } > = ( { dispute } ) => { const closedDateFormatted = dispute.metadata.__dispute_closed_at - ? formatUserDateTime( - moment - .unix( - parseInt( dispute.metadata.__dispute_closed_at, 10 ) - ) - .utc() - .toISOString(), + ? formatDateTimeFromTimestamp( + parseInt( dispute.metadata.__dispute_closed_at, 10 ), { includeTime: true } ) : '-'; @@ -173,13 +163,8 @@ const DisputeLostFooter: React.FC< { const disputeFeeFormatted = getDisputeFeeFormatted( dispute, true ) ?? '-'; const closedDateFormatted = dispute.metadata.__dispute_closed_at - ? formatUserDateTime( - moment - .unix( - parseInt( dispute.metadata.__dispute_closed_at, 10 ) - ) - .utc() - .toISOString() + ? formatDateTimeFromTimestamp( + parseInt( dispute.metadata.__dispute_closed_at, 10 ) ) : '-'; @@ -276,13 +261,8 @@ const InquiryUnderReviewFooter: React.FC< { dispute: Pick< Dispute, 'id' | 'metadata' | 'status' >; } > = ( { dispute } ) => { const submissionDateFormatted = dispute.metadata.__evidence_submitted_at - ? formatUserDateTime( - moment - .unix( - parseInt( dispute.metadata.__evidence_submitted_at, 10 ) - ) - .utc() - .toISOString() + ? formatDateTimeFromTimestamp( + parseInt( dispute.metadata.__evidence_submitted_at, 10 ) ) : '-'; @@ -348,13 +328,8 @@ const InquiryClosedFooter: React.FC< { } > = ( { dispute } ) => { const isSubmitted = !! dispute.metadata.__evidence_submitted_at; const closedDateFormatted = dispute.metadata.__dispute_closed_at - ? formatUserDateTime( - moment - .unix( - parseInt( dispute.metadata.__dispute_closed_at, 10 ) - ) - .utc() - .toISOString() + ? formatDateTimeFromTimestamp( + parseInt( dispute.metadata.__dispute_closed_at, 10 ) ) : '-'; diff --git a/client/payment-details/dispute-details/dispute-steps.tsx b/client/payment-details/dispute-details/dispute-steps.tsx index 7fd24feeb9b..587c0dfd27c 100644 --- a/client/payment-details/dispute-details/dispute-steps.tsx +++ b/client/payment-details/dispute-details/dispute-steps.tsx @@ -19,7 +19,7 @@ import { formatExplicitCurrency } from 'multi-currency/interface/functions'; import { ClickTooltip } from 'wcpay/components/tooltip'; import { getDisputeFeeFormatted } from 'wcpay/disputes/utils'; import DisputeDueByDate from './dispute-due-by-date'; -import { formatUserDateTime } from 'wcpay/utils/date-time'; +import { formatDateTimeFromTimestamp } from 'wcpay/utils/date-time'; interface Props { dispute: Dispute; @@ -34,12 +34,8 @@ export const DisputeSteps: React.FC< Props > = ( { } ) => { let emailLink; if ( customer?.email ) { - const chargeDate = formatUserDateTime( - moment.unix( chargeCreated ).utc().toISOString() - ); - const disputeDate = formatUserDateTime( - moment.unix( dispute.created ).utc().toISOString() - ); + const chargeDate = formatDateTimeFromTimestamp( chargeCreated ); + const disputeDate = formatDateTimeFromTimestamp( dispute.created ); const emailSubject = sprintf( // Translators: %1$s is the store name, %2$s is the charge date. __( @@ -171,14 +167,12 @@ export const InquirySteps: React.FC< Props > = ( { } ) => { let emailLink; if ( customer?.email ) { - const chargeDate = formatUserDateTime( - moment.unix( chargeCreated ).utc().toISOString(), - { includeTime: true } - ); - const disputeDate = formatUserDateTime( - moment.unix( dispute.created ).utc().toISOString(), - { includeTime: true } - ); + const chargeDate = formatDateTimeFromTimestamp( chargeCreated, { + includeTime: true, + } ); + const disputeDate = formatDateTimeFromTimestamp( dispute.created, { + includeTime: true, + } ); const emailSubject = sprintf( // Translators: %1$s is the store name, %2$s is the charge date. __( diff --git a/client/payment-details/dispute-details/dispute-summary-row.tsx b/client/payment-details/dispute-details/dispute-summary-row.tsx index 3ac801ebd4a..95119d01f82 100644 --- a/client/payment-details/dispute-details/dispute-summary-row.tsx +++ b/client/payment-details/dispute-details/dispute-summary-row.tsx @@ -4,7 +4,6 @@ * External dependencies */ import React from 'react'; -import moment from 'moment'; import HelpOutlineIcon from 'gridicons/dist/help-outline'; import { __ } from '@wordpress/i18n'; @@ -19,7 +18,7 @@ import { formatStringValue } from 'wcpay/utils'; import { ClickTooltip } from 'wcpay/components/tooltip'; import Paragraphs from 'wcpay/components/paragraphs'; import DisputeDueByDate from './dispute-due-by-date'; -import { formatUserDateTime } from 'wcpay/utils/date-time'; +import { formatDateTimeFromTimestamp } from 'wcpay/utils/date-time'; interface Props { dispute: Dispute; @@ -39,10 +38,10 @@ const DisputeSummaryRow: React.FC< Props > = ( { dispute } ) => { { title: __( 'Disputed On', 'woocommerce-payments' ), content: dispute.created - ? formatUserDateTime( - moment.unix( dispute.created ).utc().toISOString(), - { separator: ', ', includeTime: true } - ) + ? formatDateTimeFromTimestamp( dispute.created, { + separator: ', ', + includeTime: true, + } ) : '–', }, { diff --git a/client/payment-details/summary/index.tsx b/client/payment-details/summary/index.tsx index d70e87386bc..0cb659fefdc 100644 --- a/client/payment-details/summary/index.tsx +++ b/client/payment-details/summary/index.tsx @@ -63,7 +63,10 @@ import DisputeResolutionFooter from '../dispute-details/dispute-resolution-foote import ErrorBoundary from 'components/error-boundary'; import RefundModal from 'wcpay/payment-details/summary/refund-modal'; import CardNotice from 'wcpay/components/card-notice'; -import { formatUserDateTime } from 'wcpay/utils/date-time'; +import { + formatDateTimeFromString, + formatDateTimeFromTimestamp, +} from 'wcpay/utils/date-time'; declare const window: any; @@ -110,10 +113,10 @@ const composePaymentSummaryItems = ( { { title: __( 'Date', 'woocommerce-payments' ), content: charge.created - ? formatUserDateTime( - moment.utc( charge.created * 1000 ).toISOString(), - { separator: ', ', includeTime: true } - ) + ? formatDateTimeFromTimestamp( charge.created, { + separator: ', ', + includeTime: true, + } ) : '–', }, { @@ -706,7 +709,8 @@ const PaymentDetailsSummary: React.FC< PaymentDetailsSummaryProps > = ( { } ) }{ ' ' } { 'woocommerce-payments' ), formattedAmount, - formatUserDateTime( - moment.unix( event.deposit.arrival_date ).utc().toISOString() - ) + formatDateTimeFromTimestamp( event.deposit.arrival_date ) ); const depositUrl = getAdminUrl( { diff --git a/client/transactions/blocked/columns.tsx b/client/transactions/blocked/columns.tsx index 829167e077f..8e4f410ff32 100644 --- a/client/transactions/blocked/columns.tsx +++ b/client/transactions/blocked/columns.tsx @@ -3,7 +3,6 @@ */ import React from 'react'; import { __ } from '@wordpress/i18n'; -import moment from 'moment'; import { TableCardColumn, TableCardBodyColumn } from '@woocommerce/components'; /** @@ -14,7 +13,7 @@ import TransactionStatusPill from 'wcpay/components/transaction-status-pill'; import { FraudOutcomeTransaction } from '../../data'; import { getDetailsURL } from '../../components/details-link'; import ClickableCell from '../../components/clickable-cell'; -import { formatUserDateTime } from 'wcpay/utils/date-time'; +import { formatDateTimeFromString } from 'wcpay/utils/date-time'; interface Column extends TableCardColumn { key: 'created' | 'amount' | 'customer' | 'status'; @@ -70,10 +69,9 @@ export const getBlockedListRowContent = ( data.payment_intent.id || data.order_id.toString(), 'transactions' ); - const formattedCreatedDate = formatUserDateTime( - moment.utc( data.created ).toISOString(), - { includeTime: true } - ); + const formattedCreatedDate = formatDateTimeFromString( data.created, { + includeTime: true, + } ); const clickable = ( children: JSX.Element | string ) => ( { children } diff --git a/client/transactions/list/deposit.tsx b/client/transactions/list/deposit.tsx index 4c4292b88fb..a3b553fefde 100644 --- a/client/transactions/list/deposit.tsx +++ b/client/transactions/list/deposit.tsx @@ -4,7 +4,6 @@ * External dependencies */ import React from 'react'; -import moment from 'moment'; import { __ } from '@wordpress/i18n'; import interpolateComponents from '@automattic/interpolate-components'; import { ExternalLink } from '@wordpress/components'; @@ -16,7 +15,7 @@ import InfoOutlineIcon from 'gridicons/dist/info-outline'; */ import { getAdminUrl } from 'utils'; import { ClickTooltip } from 'components/tooltip'; -import { formatUserDateTime } from 'wcpay/utils/date-time'; +import { formatDateTimeFromString } from 'wcpay/utils/date-time'; interface DepositProps { depositId?: string; @@ -31,8 +30,8 @@ const Deposit: React.FC< DepositProps > = ( { depositId, dateAvailable } ) => { id: depositId, } ); - const formattedDateAvailable = formatUserDateTime( - moment.utc( dateAvailable ).toISOString(), + const formattedDateAvailable = formatDateTimeFromString( + dateAvailable, { useGmt: true, // TODO: should we allow user settings } diff --git a/client/transactions/list/index.tsx b/client/transactions/list/index.tsx index dcc1e12c48f..4266c690d89 100644 --- a/client/transactions/list/index.tsx +++ b/client/transactions/list/index.tsx @@ -8,7 +8,6 @@ import { uniq } from 'lodash'; import { useDispatch } from '@wordpress/data'; import { useMemo } from '@wordpress/element'; import { __, _n, sprintf } from '@wordpress/i18n'; -import moment from 'moment'; import { TableCard, Search, @@ -69,7 +68,7 @@ import p24BankList from '../../payment-details/payment-method/p24/bank-list'; import { HoverTooltip } from 'components/tooltip'; import { PAYMENT_METHOD_TITLES } from 'wcpay/constants/payment-method'; import { ReportingExportLanguageHook } from 'wcpay/settings/reporting-settings/interfaces'; -import { formatUserDateTime } from 'wcpay/utils/date-time'; +import { formatDateTimeFromString } from 'wcpay/utils/date-time'; interface TransactionsListProps { depositId?: string; @@ -466,7 +465,7 @@ export const TransactionsList = ( date: { value: txn.date, display: clickable( - formatUserDateTime( moment.utc( txn.date ).toISOString(), { + formatDateTimeFromString( txn.date, { includeTime: true, } ) ), diff --git a/client/transactions/risk-review/columns.tsx b/client/transactions/risk-review/columns.tsx index a1abfeb067c..c1e24c4d428 100644 --- a/client/transactions/risk-review/columns.tsx +++ b/client/transactions/risk-review/columns.tsx @@ -3,7 +3,6 @@ */ import React from 'react'; import { __ } from '@wordpress/i18n'; -import moment from 'moment'; import { TableCardColumn, TableCardBodyColumn } from '@woocommerce/components'; import { Button } from '@wordpress/components'; @@ -16,7 +15,7 @@ import { formatExplicitCurrency } from 'multi-currency/interface/functions'; import { recordEvent } from 'tracks'; import TransactionStatusPill from 'wcpay/components/transaction-status-pill'; import { FraudOutcomeTransaction } from '../../data'; -import { formatUserDateTime } from 'wcpay/utils/date-time'; +import { formatDateTimeFromString } from 'wcpay/utils/date-time'; interface Column extends TableCardColumn { key: 'created' | 'amount' | 'customer' | 'status'; @@ -76,10 +75,9 @@ export const getRiskReviewListRowContent = ( data: FraudOutcomeTransaction ): Record< string, TableCardBodyColumn > => { const detailsURL = getDetailsURL( data.payment_intent.id, 'transactions' ); - const formattedCreatedDate = formatUserDateTime( - moment.utc( data.created ).toISOString(), - { includeTime: true } - ); + const formattedCreatedDate = formatDateTimeFromString( data.created, { + includeTime: true, + } ); const clickable = ( children: JSX.Element | string ) => ( { children } diff --git a/client/transactions/uncaptured/index.tsx b/client/transactions/uncaptured/index.tsx index 7d7d993ee42..42d6d69542b 100644 --- a/client/transactions/uncaptured/index.tsx +++ b/client/transactions/uncaptured/index.tsx @@ -20,7 +20,7 @@ import { formatExplicitCurrency } from 'multi-currency/interface/functions'; import RiskLevel, { calculateRiskMapping } from 'components/risk-level'; import { recordEvent } from 'tracks'; import CaptureAuthorizationButton from 'wcpay/components/capture-authorization-button'; -import { formatUserDateTime } from 'wcpay/utils/date-time'; +import { formatDateTimeFromString } from 'wcpay/utils/date-time'; interface Column extends TableCardColumn { key: @@ -130,25 +130,23 @@ export const AuthorizationsList = (): JSX.Element => { display: auth.payment_intent_id, }, created: { - value: formatUserDateTime( - moment.utc( auth.created ).toISOString(), - { includeTime: true } - ), + value: formatDateTimeFromString( auth.created, { + includeTime: true, + } ), display: clickable( - formatUserDateTime( - moment.utc( auth.created ).toISOString(), - { includeTime: true } - ) + formatDateTimeFromString( auth.created, { + includeTime: true, + } ) ), }, // Payments are authorized for a maximum of 7 days capture_by: { - value: formatUserDateTime( + value: formatDateTimeFromString( moment.utc( auth.created ).add( 7, 'd' ).toISOString(), { includeTime: true } ), display: clickable( - formatUserDateTime( + formatDateTimeFromString( moment.utc( auth.created ).add( 7, 'd' ).toISOString(), { includeTime: true } ) diff --git a/client/utils/date-time.ts b/client/utils/date-time.ts index 6fc14a826b0..5a7e5fd2f7b 100644 --- a/client/utils/date-time.ts +++ b/client/utils/date-time.ts @@ -2,31 +2,26 @@ * External dependencies */ import { dateI18n } from '@wordpress/date'; +import moment from 'moment'; type DateTimeFormat = string | null; interface FormatDateTimeOptions { - includeTime?: boolean; // Whether to include time in the formatted string (defaults to true) + includeTime?: boolean; // Whether to include time in the formatted string (defaults to false) useGmt?: boolean; // Whether to display the time in GMT/UTC (defaults to false) separator?: string; // Separator between date and time (defaults to ' / ') customFormat?: DateTimeFormat; // Custom format to use instead of WordPress settings } /** - * Formats a date and time string according to WordPress settings or a custom format. + * Formats a date/time string in YYYY-MM-DD HH:MM:SS format according to WordPress settings. * - * @param { string | Date } dateTime - The date and time string (e.g., '2024-10-23 15:28:26') or a JS Date object. - * @param { FormatDateTimeOptions } options - Additional options to control time inclusion and whether to use GMT/UTC. - * @return { string } - The formatted date and time string. + * @param dateTimeStr - Date time string in YYYY-MM-DD HH:MM:SS format + * @param options - Formatting options */ -export function formatUserDateTime( - dateTime: string | Date, - options: FormatDateTimeOptions = { - includeTime: false, - useGmt: false, - separator: ' / ', - customFormat: null, - } +export function formatDateTimeFromString( + dateTimeStr: string, + options: FormatDateTimeOptions = {} ): string { const { customFormat = null, @@ -35,7 +30,40 @@ export function formatUserDateTime( separator = ' / ', } = options; - // Use the WordPress settings for date and time format if no custom format is provided + // Convert to UTC ISO string for consistent handling + const utcDateTime = moment.utc( dateTimeStr ).toISOString(); + + const format = + customFormat || + `${ window.wcpaySettings.dateFormat }${ + includeTime + ? `${ separator }${ window.wcpaySettings.timeFormat }` + : '' + }`; + + return dateI18n( format, utcDateTime, useGmt ); +} + +/** + * Formats a Unix timestamp according to WordPress settings. + * + * @param timestamp - Unix timestamp (seconds since epoch) + * @param options - Formatting options + */ +export function formatDateTimeFromTimestamp( + timestamp: number, + options: FormatDateTimeOptions = {} +): string { + const { + customFormat = null, + includeTime = false, + useGmt = false, + separator = ' / ', + } = options; + + // Convert to UTC ISO string for consistent handling + const utcDateTime = moment.unix( timestamp ).utc().toISOString(); + const format = customFormat || `${ window.wcpaySettings.dateFormat }${ @@ -44,5 +72,5 @@ export function formatUserDateTime( : '' }`; - return dateI18n( format, dateTime, useGmt ); + return dateI18n( format, utcDateTime, useGmt ); } diff --git a/client/utils/test/date-time.test.ts b/client/utils/test/date-time.test.ts index 45a1c96ee73..4efe9689629 100644 --- a/client/utils/test/date-time.test.ts +++ b/client/utils/test/date-time.test.ts @@ -1,28 +1,45 @@ /** - * Internal dependencies + * External dependencies */ -import { formatUserDateTime } from 'wcpay/utils/date-time'; import { dateI18n } from '@wordpress/date'; -describe( 'formatUserDateTime', () => { +/** + * Internal dependencies + */ +import { + formatDateTimeFromString, + formatDateTimeFromTimestamp, +} from 'wcpay/utils/date-time'; + +// Mock dateI18n +jest.mock( '@wordpress/date', () => ( { + dateI18n: jest.fn( ( format, date ) => { + return jest + .requireActual( '@wordpress/date' ) + .dateI18n( format, date, 'UTC' ); // Force UTC + } ), +} ) ); + +describe( 'Date/Time Formatting', () => { const originalWcpaySettings = window.wcpaySettings; const mockWcpaySettings = { dateFormat: 'Y-m-d', timeFormat: 'H:i', }; - beforeAll( () => { + beforeEach( () => { + jest.clearAllMocks(); window.wcpaySettings = mockWcpaySettings as typeof wcpaySettings; } ); - afterAll( () => { + afterEach( () => { window.wcpaySettings = originalWcpaySettings; } ); - describe( 'with string input', () => { + describe( 'formatDateTimeFromString', () => { it( 'should format using default WordPress settings', () => { const dateTime = '2024-10-23 15:28:26'; - const formatted = formatUserDateTime( dateTime, { + const formatted = formatDateTimeFromString( dateTime, { includeTime: true, } ); @@ -32,14 +49,14 @@ describe( 'formatUserDateTime', () => { it( 'should use custom format if provided', () => { const dateTime = '2024-10-23 15:28:26'; const options = { customFormat: 'd-m-Y H:i:s' }; - const formatted = formatUserDateTime( dateTime, options ); + const formatted = formatDateTimeFromString( dateTime, options ); expect( formatted ).toBe( '23-10-2024 15:28:26' ); } ); it( 'should exclude time if includeTime is set to false', () => { const dateTime = '2024-10-23 15:28:26'; - const formatted = formatUserDateTime( dateTime ); + const formatted = formatDateTimeFromString( dateTime ); expect( formatted ).toBe( '2024-10-23' ); } ); @@ -47,7 +64,7 @@ describe( 'formatUserDateTime', () => { it( 'should use custom separator when provided', () => { const dateTime = '2024-10-23 15:28:26'; const options = { separator: ' - ', includeTime: true }; - const formatted = formatUserDateTime( dateTime, options ); + const formatted = formatDateTimeFromString( dateTime, options ); expect( formatted ).toBe( '2024-10-23 - 15:28' ); } ); @@ -55,79 +72,48 @@ describe( 'formatUserDateTime', () => { it( 'should handle GMT/UTC setting correctly when useGmt is true', () => { const dateTime = '2024-10-23 15:28:26Z'; const options = { useGmt: true, includeTime: true }; - const formatted = formatUserDateTime( dateTime, options ); + const formatted = formatDateTimeFromString( dateTime, options ); - // Expect UTC-based output (no timezone adjustment) expect( formatted ).toBe( '2024-10-23 / 15:28' ); } ); - - it( 'should support escaping characters with custom format', () => { - const dateTime = '2024-10-23 15:28:26'; - const options = { customFormat: "'l \\t\\h\\e jS'" }; - const formatted = formatUserDateTime( dateTime, options ); - expect( formatted ).toBe( "'Wednesday the 23rd'" ); - } ); - - it( 'should output unrecognized characters as-is', () => { - const dateTime = '2024-10-23 15:28:26'; - const options = { customFormat: '-' }; - const formatted = formatUserDateTime( dateTime, options ); - expect( formatted ).toBe( '-' ); - } ); } ); - describe( 'with Date object input', () => { + describe( 'formatDateTimeFromTimestamp', () => { it( 'should format using default WordPress settings', () => { - const dateTime = new Date( Date.UTC( 2024, 9, 23, 15, 28, 26 ) ); - const formatted = formatUserDateTime( dateTime, { - useGmt: true, + const timestamp = 1729766906; // 2024-10-23 10:48:26 UTC + const formatted = formatDateTimeFromTimestamp( timestamp, { includeTime: true, } ); - expect( formatted ).toBe( '2024-10-23 / 15:28' ); + expect( formatted ).toBe( '2024-10-24 / 10:48' ); } ); it( 'should use custom format if provided', () => { - const dateTime = new Date( Date.UTC( 2024, 9, 23, 15, 28, 26 ) ); + const timestamp = 1729766906; // 2024-10-23 10:48:26 UTC const options = { customFormat: 'd-m-Y H:i:s', useGmt: true }; - const formatted = formatUserDateTime( dateTime, options ); + const formatted = formatDateTimeFromTimestamp( timestamp, options ); - expect( formatted ).toBe( '23-10-2024 15:28:26' ); + expect( formatted ).toBe( '24-10-2024 10:48:26' ); } ); it( 'should exclude time if includeTime is set to false', () => { - const dateTime = new Date( Date.UTC( 2024, 9, 23, 15, 28, 26 ) ); + const timestamp = 1729766906; // 2024-10-23 10:48:26 UTC const options = { useGmt: true }; - const formatted = formatUserDateTime( dateTime, options ); - - expect( formatted ).toBe( '2024-10-23' ); - } ); + const formatted = formatDateTimeFromTimestamp( timestamp, options ); - it( 'should handle GMT/UTC setting correctly', () => { - const dateTime = new Date( 2024, 9, 23, 15, 28, 26 ); // Local time (non-UTC) - const formatted = formatUserDateTime( dateTime, { - includeTime: true, - } ); - - const expectedFormat = dateI18n( - `${ mockWcpaySettings.dateFormat } / ${ mockWcpaySettings.timeFormat }`, - dateTime, - false - ); - - expect( formatted ).toBe( expectedFormat ); + expect( formatted ).toBe( '2024-10-24' ); } ); it( 'should use custom separator when provided', () => { - const dateTime = new Date( Date.UTC( 2024, 9, 23, 15, 28, 26 ) ); + const timestamp = 1729766906; // 2024-10-23 10:48:26 UTC const options = { separator: ' - ', useGmt: true, includeTime: true, }; - const formatted = formatUserDateTime( dateTime, options ); + const formatted = formatDateTimeFromTimestamp( timestamp, options ); - expect( formatted ).toBe( '2024-10-23 - 15:28' ); + expect( formatted ).toBe( '2024-10-24 - 10:48' ); } ); } ); } ); From 0a934854b640780910ff62c2f22fdf0c75c86158 Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Mon, 2 Dec 2024 11:11:17 +0100 Subject: [PATCH 63/76] Remove useGmt parameter --- client/components/active-loan-summary/test/index.js | 9 +++++++++ client/utils/date-time.ts | 3 +-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/client/components/active-loan-summary/test/index.js b/client/components/active-loan-summary/test/index.js index 6e1c9fdce34..ef9046ba00f 100644 --- a/client/components/active-loan-summary/test/index.js +++ b/client/components/active-loan-summary/test/index.js @@ -14,6 +14,15 @@ jest.mock( 'wcpay/data', () => ( { useActiveLoanSummary: jest.fn(), } ) ); +// Mock dateI18n +jest.mock( '@wordpress/date', () => ( { + dateI18n: jest.fn( ( format, date ) => { + return jest + .requireActual( '@wordpress/date' ) + .dateI18n( format, date, 'UTC' ); // Ensure UTC is used + } ), +} ) ); + describe( 'Active loan summary', () => { beforeEach( () => { global.wcpaySettings = { diff --git a/client/utils/date-time.ts b/client/utils/date-time.ts index 5a7e5fd2f7b..7b79ea36701 100644 --- a/client/utils/date-time.ts +++ b/client/utils/date-time.ts @@ -57,7 +57,6 @@ export function formatDateTimeFromTimestamp( const { customFormat = null, includeTime = false, - useGmt = false, separator = ' / ', } = options; @@ -72,5 +71,5 @@ export function formatDateTimeFromTimestamp( : '' }`; - return dateI18n( format, utcDateTime, useGmt ); + return dateI18n( format, utcDateTime ); } From 177c0ddc3c878715552f611c63fb24bfeba8fb40 Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Mon, 2 Dec 2024 14:22:47 +0100 Subject: [PATCH 64/76] Remove useGmt parameter and fix tests --- .../components/active-loan-summary/index.tsx | 9 +-- client/deposits/details/index.tsx | 5 +- client/deposits/list/index.tsx | 9 +-- client/deposits/utils/index.ts | 6 +- client/deposits/utils/test/index.ts | 10 ++++ client/payment-details/summary/index.tsx | 2 +- .../summary/test/index.test.tsx | 13 ++++- client/transactions/list/deposit.tsx | 5 +- .../list/test/__snapshots__/index.tsx.snap | 32 +++++------ client/transactions/list/test/deposit.tsx | 14 +++++ client/transactions/list/test/index.tsx | 9 +++ client/utils/date-time.ts | 4 +- client/utils/test/date-time.test.ts | 56 +++++++++++++++---- 13 files changed, 115 insertions(+), 59 deletions(-) diff --git a/client/components/active-loan-summary/index.tsx b/client/components/active-loan-summary/index.tsx index 679e35407c8..7ae902c590e 100755 --- a/client/components/active-loan-summary/index.tsx +++ b/client/components/active-loan-summary/index.tsx @@ -211,8 +211,7 @@ const ActiveLoanSummary = (): JSX.Element => { 'woocommerce-payments' ), formatDateTimeFromTimestamp( - details.current_repayment_interval.due_at, - { useGmt: true } + details.current_repayment_interval.due_at ) ) } > @@ -249,8 +248,7 @@ const ActiveLoanSummary = (): JSX.Element => { title={ __( 'Loan disbursed', 'woocommerce-payments' ) } > { formatDateTimeFromTimestamp( - details.advance_paid_out_at, - { useGmt: true } + details.advance_paid_out_at ) } { title={ __( 'First paydown', 'woocommerce-payments' ) } > { formatDateTimeFromTimestamp( - details.repayments_begin_at, - { useGmt: true } + details.repayments_begin_at ) } diff --git a/client/deposits/details/index.tsx b/client/deposits/details/index.tsx index 286208846bf..37cdf5fb32a 100644 --- a/client/deposits/details/index.tsx +++ b/client/deposits/details/index.tsx @@ -5,7 +5,6 @@ */ import React from 'react'; import { __, sprintf } from '@wordpress/i18n'; -import moment from 'moment'; import { Card, CardBody, @@ -111,9 +110,7 @@ export const DepositOverview: React.FC< DepositOverviewProps > = ( { key="depositDate" label={ `${ depositDateLabel }: ` + - formatDateTimeFromString( deposit.date, { - useGmt: true, - } ) + formatDateTimeFromString( deposit.date ) } value={ } detail={ deposit.bankAccount } diff --git a/client/deposits/list/index.tsx b/client/deposits/list/index.tsx index 102d5a4bc73..0eb9ca21608 100644 --- a/client/deposits/list/index.tsx +++ b/client/deposits/list/index.tsx @@ -8,7 +8,6 @@ import React, { useState } from 'react'; import { recordEvent } from 'tracks'; import { useMemo } from '@wordpress/element'; import { __, _n, sprintf } from '@wordpress/i18n'; -import moment from 'moment'; import { TableCard, Link } from '@woocommerce/components'; import { onQueryChange, getQuery } from '@woocommerce/navigation'; import { @@ -135,9 +134,7 @@ export const DepositsList = (): JSX.Element => { href={ getDetailsURL( deposit.id, 'deposits' ) } onClick={ () => recordEvent( 'wcpay_deposits_row_click' ) } > - { formatDateTimeFromString( deposit.date, { - useGmt: true, - } ) } + { formatDateTimeFromString( deposit.date ) } ); @@ -326,9 +323,7 @@ export const DepositsList = (): JSX.Element => { row[ 0 ], { ...row[ 1 ], - value: formatDateTimeFromString( row[ 1 ].value as string, { - useGmt: true, - } ), + value: formatDateTimeFromString( row[ 1 ].value as string ), }, ...row.slice( 2 ), ] ); diff --git a/client/deposits/utils/index.ts b/client/deposits/utils/index.ts index 855a7e52ddc..05f65c46bc3 100644 --- a/client/deposits/utils/index.ts +++ b/client/deposits/utils/index.ts @@ -10,11 +10,7 @@ interface DepositObject { } export const getDepositDate = ( deposit?: DepositObject | null ): string => - deposit - ? formatDateTimeFromString( deposit?.date as string, { - useGmt: true, - } ) - : '—'; + deposit ? formatDateTimeFromString( deposit?.date as string ) : '—'; interface GetDepositMonthlyAnchorLabelProps { monthlyAnchor: number; diff --git a/client/deposits/utils/test/index.ts b/client/deposits/utils/test/index.ts index 9b58bddf898..e1957ce4564 100644 --- a/client/deposits/utils/test/index.ts +++ b/client/deposits/utils/test/index.ts @@ -14,8 +14,18 @@ declare const global: { }; }; +// Mock dateI18n +jest.mock( '@wordpress/date', () => ( { + dateI18n: jest.fn( ( format, date ) => { + return jest + .requireActual( '@wordpress/date' ) + .dateI18n( format, date, 'UTC' ); // Ensure UTC is used + } ), +} ) ); + describe( 'Deposits Overview Utils / getDepositDate', () => { beforeEach( () => { + jest.clearAllMocks(); global.wcpaySettings = { dateFormat: 'F j, Y', }; diff --git a/client/payment-details/summary/index.tsx b/client/payment-details/summary/index.tsx index 0cb659fefdc..958d2ef1f15 100644 --- a/client/payment-details/summary/index.tsx +++ b/client/payment-details/summary/index.tsx @@ -715,7 +715,7 @@ const PaymentDetailsSummary: React.FC< PaymentDetailsSummaryProps > = ( { .utc( authorization.created ) .add( 7, 'days' ) .toISOString(), - { useGmt: true, includeTime: true } + { includeTime: true } ) } > diff --git a/client/payment-details/summary/test/index.test.tsx b/client/payment-details/summary/test/index.test.tsx index bd0b46f80cc..8c47e9b9b6a 100755 --- a/client/payment-details/summary/test/index.test.tsx +++ b/client/payment-details/summary/test/index.test.tsx @@ -17,6 +17,15 @@ import PaymentDetailsSummary from '../'; import { useAuthorization } from 'wcpay/data'; import { paymentIntentMock } from 'wcpay/data/payment-intents/test/hooks'; +// Mock dateI18n +jest.mock( '@wordpress/date', () => ( { + dateI18n: jest.fn( ( format, date ) => { + return jest + .requireActual( '@wordpress/date' ) + .dateI18n( format, date, 'UTC' ); // Ensure UTC is used + } ), +} ) ); + declare const global: { wcSettings: { locale: { @@ -76,7 +85,7 @@ const getBaseCharge = (): Charge => id: 'ch_38jdHA39KKA', payment_intent: 'pi_abc', /* Stripe data comes in seconds, instead of the default Date milliseconds */ - created: Date.parse( 'Sep 19, 2019, 5:24 pm' ) / 1000, + created: 1568913840, amount: 2000, amount_refunded: 0, application_fee_amount: 70, @@ -412,7 +421,7 @@ describe( 'PaymentDetailsSummary', () => { ).toHaveTextContent( /\$20.00/ ); expect( screen.getByText( /Disputed On/i ).nextSibling - ).toHaveTextContent( /Aug 30, 2023/ ); + ).toHaveTextContent( /Aug 31, 2023/ ); expect( screen.getByText( /Reason/i ).nextSibling ).toHaveTextContent( /Transaction unauthorized/ ); diff --git a/client/transactions/list/deposit.tsx b/client/transactions/list/deposit.tsx index a3b553fefde..e3211b43f2b 100644 --- a/client/transactions/list/deposit.tsx +++ b/client/transactions/list/deposit.tsx @@ -31,10 +31,7 @@ const Deposit: React.FC< DepositProps > = ( { depositId, dateAvailable } ) => { } ); const formattedDateAvailable = formatDateTimeFromString( - dateAvailable, - { - useGmt: true, // TODO: should we allow user settings - } + dateAvailable ); return { formattedDateAvailable }; } diff --git a/client/transactions/list/test/__snapshots__/index.tsx.snap b/client/transactions/list/test/__snapshots__/index.tsx.snap index e638122bba2..eb0f15e3d74 100644 --- a/client/transactions/list/test/__snapshots__/index.tsx.snap +++ b/client/transactions/list/test/__snapshots__/index.tsx.snap @@ -493,7 +493,7 @@ exports[`Transactions list renders correctly when can filter by several currenci href="admin.php?page=wc-admin&path=%2Fpayments%2Ftransactions%2Fdetails&id=pi_mock&transaction_id=txn_j23jda9JJa&transaction_type=refund" tabindex="-1" > - Jan 2, 2020 / 12:46PM + Jan 2, 2020 / 5:46PM - Jan 4, 2020 / 11:22PM + Jan 5, 2020 / 4:22AM - Jan 2, 2020 / 2:55PM + Jan 2, 2020 / 7:55PM - Jan 2, 2020 / 12:46PM + Jan 2, 2020 / 5:46PM - Jan 4, 2020 / 11:22PM + Jan 5, 2020 / 4:22AM - Jan 2, 2020 / 2:55PM + Jan 2, 2020 / 7:55PM - Jan 4, 2020 / 11:22PM + Jan 5, 2020 / 4:22AM - Jan 2, 2020 / 12:46PM + Jan 2, 2020 / 5:46PM - Jan 4, 2020 / 11:22PM + Jan 5, 2020 / 4:22AM - Jan 2, 2020 / 2:55PM + Jan 2, 2020 / 7:55PM - Jan 2, 2020 / 12:46PM + Jan 2, 2020 / 5:46PM - Jan 4, 2020 / 11:22PM + Jan 5, 2020 / 4:22AM - Jan 2, 2020 / 2:55PM + Jan 2, 2020 / 7:55PM - Jan 2, 2020 / 12:46PM + Jan 2, 2020 / 5:46PM - Jan 4, 2020 / 11:22PM + Jan 5, 2020 / 4:22AM - Jan 2, 2020 / 2:55PM + Jan 2, 2020 / 7:55PM ( { + dateI18n: jest.fn( ( format, date ) => { + return jest + .requireActual( '@wordpress/date' ) + .dateI18n( format, date, 'UTC' ); // Ensure UTC is used + } ), +} ) ); + describe( 'Deposit', () => { beforeEach( () => { // Mock the window.wcpaySettings property @@ -18,6 +27,11 @@ describe( 'Deposit', () => { window.wcpaySettings.timeFormat = 'g:i a'; } ); + afterEach( () => { + // Reset the mock + jest.clearAllMocks(); + } ); + test( 'renders with date and deposit available', () => { const { container: link } = render( diff --git a/client/transactions/list/test/index.tsx b/client/transactions/list/test/index.tsx index 0ac273c6751..fbedb3c852a 100644 --- a/client/transactions/list/test/index.tsx +++ b/client/transactions/list/test/index.tsx @@ -58,6 +58,15 @@ jest.mock( 'data/index', () => ( { useReportingExportLanguage: jest.fn( () => [ 'en', jest.fn() ] ), } ) ); +// Mock dateI18n +jest.mock( '@wordpress/date', () => ( { + dateI18n: jest.fn( ( format, date ) => { + return jest + .requireActual( '@wordpress/date' ) + .dateI18n( format, date, 'UTC' ); // Ensure UTC is used + } ), +} ) ); + const mockDownloadCSVFile = downloadCSVFile as jest.MockedFunction< typeof downloadCSVFile >; diff --git a/client/utils/date-time.ts b/client/utils/date-time.ts index 7b79ea36701..955523b2c17 100644 --- a/client/utils/date-time.ts +++ b/client/utils/date-time.ts @@ -8,7 +8,6 @@ type DateTimeFormat = string | null; interface FormatDateTimeOptions { includeTime?: boolean; // Whether to include time in the formatted string (defaults to false) - useGmt?: boolean; // Whether to display the time in GMT/UTC (defaults to false) separator?: string; // Separator between date and time (defaults to ' / ') customFormat?: DateTimeFormat; // Custom format to use instead of WordPress settings } @@ -26,7 +25,6 @@ export function formatDateTimeFromString( const { customFormat = null, includeTime = false, - useGmt = false, separator = ' / ', } = options; @@ -41,7 +39,7 @@ export function formatDateTimeFromString( : '' }`; - return dateI18n( format, utcDateTime, useGmt ); + return dateI18n( format, utcDateTime ); } /** diff --git a/client/utils/test/date-time.test.ts b/client/utils/test/date-time.test.ts index 4efe9689629..6b9a999568b 100644 --- a/client/utils/test/date-time.test.ts +++ b/client/utils/test/date-time.test.ts @@ -1,8 +1,3 @@ -/** - * External dependencies - */ -import { dateI18n } from '@wordpress/date'; - /** * Internal dependencies */ @@ -16,7 +11,7 @@ jest.mock( '@wordpress/date', () => ( { dateI18n: jest.fn( ( format, date ) => { return jest .requireActual( '@wordpress/date' ) - .dateI18n( format, date, 'UTC' ); // Force UTC + .dateI18n( format, date, 'UTC' ); // Force UTC by default } ), } ) ); @@ -71,11 +66,31 @@ describe( 'Date/Time Formatting', () => { it( 'should handle GMT/UTC setting correctly when useGmt is true', () => { const dateTime = '2024-10-23 15:28:26Z'; - const options = { useGmt: true, includeTime: true }; + const options = { includeTime: true }; const formatted = formatDateTimeFromString( dateTime, options ); expect( formatted ).toBe( '2024-10-23 / 15:28' ); } ); + + it( 'should handle different timezones correctly', () => { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const dateI18n = require( '@wordpress/date' ).dateI18n; + // Temporarily modify the mock to use a different timezone: America/New_York + dateI18n.mockImplementationOnce( + ( format: string, date: string | number ) => { + return jest + .requireActual( '@wordpress/date' ) + .dateI18n( format, date, 'America/New_York' ); + } + ); + + const dateTime = '2024-10-23 15:28:26'; + const formatted = formatDateTimeFromString( dateTime, { + includeTime: true, + } ); + + expect( formatted ).toBe( '2024-10-23 / 11:28' ); + } ); } ); describe( 'formatDateTimeFromTimestamp', () => { @@ -90,7 +105,7 @@ describe( 'Date/Time Formatting', () => { it( 'should use custom format if provided', () => { const timestamp = 1729766906; // 2024-10-23 10:48:26 UTC - const options = { customFormat: 'd-m-Y H:i:s', useGmt: true }; + const options = { customFormat: 'd-m-Y H:i:s' }; const formatted = formatDateTimeFromTimestamp( timestamp, options ); expect( formatted ).toBe( '24-10-2024 10:48:26' ); @@ -98,8 +113,7 @@ describe( 'Date/Time Formatting', () => { it( 'should exclude time if includeTime is set to false', () => { const timestamp = 1729766906; // 2024-10-23 10:48:26 UTC - const options = { useGmt: true }; - const formatted = formatDateTimeFromTimestamp( timestamp, options ); + const formatted = formatDateTimeFromTimestamp( timestamp ); expect( formatted ).toBe( '2024-10-24' ); } ); @@ -108,12 +122,32 @@ describe( 'Date/Time Formatting', () => { const timestamp = 1729766906; // 2024-10-23 10:48:26 UTC const options = { separator: ' - ', - useGmt: true, includeTime: true, }; const formatted = formatDateTimeFromTimestamp( timestamp, options ); expect( formatted ).toBe( '2024-10-24 - 10:48' ); } ); + + it( 'should handle different timezones correctly', () => { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const dateI18n = require( '@wordpress/date' ).dateI18n; + // Temporarily modify the mock to use a different timezone: America/New_York + dateI18n.mockImplementationOnce( + ( format: string, date: string | number ) => { + return jest + .requireActual( '@wordpress/date' ) + .dateI18n( format, date, 'America/New_York' ); + } + ); + + const timestamp = 1729766906; // 2024-10-24 10:48:26 UTC + const formatted = formatDateTimeFromTimestamp( timestamp, { + includeTime: true, + } ); + + // In New York (EDT), this should be 4 hours behind UTC + expect( formatted ).toBe( '2024-10-24 / 06:48' ); + } ); } ); } ); From a1ba648fe1d307c08734cb06104c139d10327be9 Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Mon, 2 Dec 2024 16:44:16 +0100 Subject: [PATCH 65/76] Fix test --- client/deposits/list/test/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/deposits/list/test/index.tsx b/client/deposits/list/test/index.tsx index 30edb8ba984..0455acaa12c 100644 --- a/client/deposits/list/test/index.tsx +++ b/client/deposits/list/test/index.tsx @@ -154,7 +154,7 @@ describe( 'Deposits list', () => { reporting: { exportModalDismissed: true, }, - dateFormat: 'M j Y', + dateFormat: 'M j, Y', }; } ); From 38760c8db3e08f4a07f6c5b6ba64c42aabf9bccc Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Mon, 2 Dec 2024 16:52:50 +0100 Subject: [PATCH 66/76] Enhance docs --- client/utils/date-time.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/utils/date-time.ts b/client/utils/date-time.ts index 955523b2c17..1418130125a 100644 --- a/client/utils/date-time.ts +++ b/client/utils/date-time.ts @@ -14,6 +14,7 @@ interface FormatDateTimeOptions { /** * Formats a date/time string in YYYY-MM-DD HH:MM:SS format according to WordPress settings. + * The input date string is converted to UTC for consistent handling across timezones. * * @param dateTimeStr - Date time string in YYYY-MM-DD HH:MM:SS format * @param options - Formatting options @@ -44,6 +45,7 @@ export function formatDateTimeFromString( /** * Formats a Unix timestamp according to WordPress settings. + * The input timestamp is converted to UTC for consistent handling across timezones. * * @param timestamp - Unix timestamp (seconds since epoch) * @param options - Formatting options From 281c05b7dc82424ea0872ba73a0aa8cd876010a5 Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Mon, 2 Dec 2024 17:18:33 +0100 Subject: [PATCH 67/76] Update failing snapshot --- client/deposits/list/test/__snapshots__/index.tsx.snap | 4 ++-- client/deposits/list/test/index.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/deposits/list/test/__snapshots__/index.tsx.snap b/client/deposits/list/test/__snapshots__/index.tsx.snap index df62f2bbf7e..6f106243efd 100644 --- a/client/deposits/list/test/__snapshots__/index.tsx.snap +++ b/client/deposits/list/test/__snapshots__/index.tsx.snap @@ -559,7 +559,7 @@ exports[`Deposits list renders correctly a single deposit 1`] = ` data-link-type="wc-admin" href="admin.php?page=wc-admin&path=%2Fpayments%2Fpayouts%2Fdetails&id=po_mock3" > - Jan 4, 2020 + Jan 4 2020 - Jan 4, 2020 + Jan 4 2020 { reporting: { exportModalDismissed: true, }, - dateFormat: 'M j, Y', + dateFormat: 'M j Y', }; } ); From 298fe08b0483b7ac6379167317853b6cae8a319d Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Mon, 2 Dec 2024 17:32:12 +0100 Subject: [PATCH 68/76] PR feedback: Improve docs formatting --- client/utils/date-time.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/client/utils/date-time.ts b/client/utils/date-time.ts index 1418130125a..ee177de2a0d 100644 --- a/client/utils/date-time.ts +++ b/client/utils/date-time.ts @@ -7,9 +7,12 @@ import moment from 'moment'; type DateTimeFormat = string | null; interface FormatDateTimeOptions { - includeTime?: boolean; // Whether to include time in the formatted string (defaults to false) - separator?: string; // Separator between date and time (defaults to ' / ') - customFormat?: DateTimeFormat; // Custom format to use instead of WordPress settings + /** Whether to include time in the formatted string (defaults to true) */ + includeTime?: boolean; + /** Separator between date and time (defaults to ' / ') */ + separator?: string; + /** Custom format to use instead of WordPress settings */ + customFormat?: DateTimeFormat; } /** From ff4ad7023194a9e4a9e752f2aeb81ae3306a8967 Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Mon, 2 Dec 2024 18:04:07 +0100 Subject: [PATCH 69/76] Fix leftovers --- client/capital/index.tsx | 1 - client/disputes/test/index.tsx | 1 - client/documents/list/index.tsx | 1 - .../dispute-details/dispute-resolution-footer.tsx | 1 - client/payment-details/dispute-details/dispute-steps.tsx | 1 - client/utils/test/date-time.test.ts | 8 -------- 6 files changed, 13 deletions(-) diff --git a/client/capital/index.tsx b/client/capital/index.tsx index 7e6c5957db6..dc2a1e6664a 100644 --- a/client/capital/index.tsx +++ b/client/capital/index.tsx @@ -25,7 +25,6 @@ import { useLoans } from 'wcpay/data'; import { getAdminUrl } from 'wcpay/utils'; import './style.scss'; import { formatDateTimeFromString } from 'wcpay/utils/date-time'; -import moment from 'moment'; const columns = [ { diff --git a/client/disputes/test/index.tsx b/client/disputes/test/index.tsx index 3e32172907b..37bbd2e93af 100644 --- a/client/disputes/test/index.tsx +++ b/client/disputes/test/index.tsx @@ -25,7 +25,6 @@ import { DisputeStatus, } from 'wcpay/types/disputes'; import { formatDateTimeFromString } from 'wcpay/utils/date-time'; -import moment from 'moment'; jest.mock( '@woocommerce/csv-export', () => { const actualModule = jest.requireActual( '@woocommerce/csv-export' ); diff --git a/client/documents/list/index.tsx b/client/documents/list/index.tsx index 59a6c7e7940..3223d45dcec 100644 --- a/client/documents/list/index.tsx +++ b/client/documents/list/index.tsx @@ -5,7 +5,6 @@ */ import React, { useCallback, useEffect, useState } from 'react'; import { __, _n, sprintf } from '@wordpress/i18n'; -import moment from 'moment'; import { TableCard, TableCardColumn } from '@woocommerce/components'; import { onQueryChange, getQuery } from '@woocommerce/navigation'; import { Button } from '@wordpress/components'; diff --git a/client/payment-details/dispute-details/dispute-resolution-footer.tsx b/client/payment-details/dispute-details/dispute-resolution-footer.tsx index a0d454e25eb..bc4b1e94dbd 100644 --- a/client/payment-details/dispute-details/dispute-resolution-footer.tsx +++ b/client/payment-details/dispute-details/dispute-resolution-footer.tsx @@ -2,7 +2,6 @@ * External dependencies */ import React from 'react'; -import moment from 'moment'; import { __, sprintf } from '@wordpress/i18n'; import { Link } from '@woocommerce/components'; import { createInterpolateElement } from '@wordpress/element'; diff --git a/client/payment-details/dispute-details/dispute-steps.tsx b/client/payment-details/dispute-details/dispute-steps.tsx index 1fced7859ea..28707dfc6c4 100644 --- a/client/payment-details/dispute-details/dispute-steps.tsx +++ b/client/payment-details/dispute-details/dispute-steps.tsx @@ -7,7 +7,6 @@ import React from 'react'; import { __, sprintf } from '@wordpress/i18n'; import { createInterpolateElement } from '@wordpress/element'; import { ExternalLink } from '@wordpress/components'; -import moment from 'moment'; import HelpOutlineIcon from 'gridicons/dist/help-outline'; /** diff --git a/client/utils/test/date-time.test.ts b/client/utils/test/date-time.test.ts index 6b9a999568b..caf4a2680d6 100644 --- a/client/utils/test/date-time.test.ts +++ b/client/utils/test/date-time.test.ts @@ -64,14 +64,6 @@ describe( 'Date/Time Formatting', () => { expect( formatted ).toBe( '2024-10-23 - 15:28' ); } ); - it( 'should handle GMT/UTC setting correctly when useGmt is true', () => { - const dateTime = '2024-10-23 15:28:26Z'; - const options = { includeTime: true }; - const formatted = formatDateTimeFromString( dateTime, options ); - - expect( formatted ).toBe( '2024-10-23 / 15:28' ); - } ); - it( 'should handle different timezones correctly', () => { // eslint-disable-next-line @typescript-eslint/no-var-requires const dateI18n = require( '@wordpress/date' ).dateI18n; From 933422e00040f48a13487650adc0c1615f8aa8af Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Tue, 3 Dec 2024 12:48:46 +0100 Subject: [PATCH 70/76] Add timezone support --- client/utils/date-time.ts | 8 ++++-- client/utils/test/date-time.test.ts | 40 +++++++++++++++++++++++++++-- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/client/utils/date-time.ts b/client/utils/date-time.ts index ee177de2a0d..83e4c2c2257 100644 --- a/client/utils/date-time.ts +++ b/client/utils/date-time.ts @@ -13,6 +13,8 @@ interface FormatDateTimeOptions { separator?: string; /** Custom format to use instead of WordPress settings */ customFormat?: DateTimeFormat; + /** Timezone string (e.g., 'UTC', 'America/New_York'). If undefined, uses site default */ + timezone?: string; } /** @@ -30,6 +32,7 @@ export function formatDateTimeFromString( customFormat = null, includeTime = false, separator = ' / ', + timezone = undefined, } = options; // Convert to UTC ISO string for consistent handling @@ -43,7 +46,7 @@ export function formatDateTimeFromString( : '' }`; - return dateI18n( format, utcDateTime ); + return dateI18n( format, utcDateTime, timezone ); } /** @@ -61,6 +64,7 @@ export function formatDateTimeFromTimestamp( customFormat = null, includeTime = false, separator = ' / ', + timezone = undefined, } = options; // Convert to UTC ISO string for consistent handling @@ -74,5 +78,5 @@ export function formatDateTimeFromTimestamp( : '' }`; - return dateI18n( format, utcDateTime ); + return dateI18n( format, utcDateTime, timezone ); } diff --git a/client/utils/test/date-time.test.ts b/client/utils/test/date-time.test.ts index caf4a2680d6..798c95d7755 100644 --- a/client/utils/test/date-time.test.ts +++ b/client/utils/test/date-time.test.ts @@ -8,10 +8,10 @@ import { // Mock dateI18n jest.mock( '@wordpress/date', () => ( { - dateI18n: jest.fn( ( format, date ) => { + dateI18n: jest.fn( ( format, date, timezone ) => { return jest .requireActual( '@wordpress/date' ) - .dateI18n( format, date, 'UTC' ); // Force UTC by default + .dateI18n( format, date, timezone || 'UTC' ); // Use provided timezone or fallback to UTC } ), } ) ); @@ -83,6 +83,24 @@ describe( 'Date/Time Formatting', () => { expect( formatted ).toBe( '2024-10-23 / 11:28' ); } ); + + it( 'should respect explicitly provided timezone', () => { + const dateTime = '2024-10-23 15:28:26'; + + // Test with UTC timezone + const formattedUTC = formatDateTimeFromString( dateTime, { + includeTime: true, + timezone: 'UTC', + } ); + expect( formattedUTC ).toBe( '2024-10-23 / 15:28' ); + + // Test with New York timezone + const formattedNY = formatDateTimeFromString( dateTime, { + includeTime: true, + timezone: 'America/New_York', + } ); + expect( formattedNY ).toBe( '2024-10-23 / 11:28' ); + } ); } ); describe( 'formatDateTimeFromTimestamp', () => { @@ -141,5 +159,23 @@ describe( 'Date/Time Formatting', () => { // In New York (EDT), this should be 4 hours behind UTC expect( formatted ).toBe( '2024-10-24 / 06:48' ); } ); + + it( 'should respect explicitly provided timezone', () => { + const timestamp = 1729766906; // 2024-10-24 10:48:26 UTC + + // Test with UTC timezone + const formattedUTC = formatDateTimeFromTimestamp( timestamp, { + includeTime: true, + timezone: 'UTC', + } ); + expect( formattedUTC ).toBe( '2024-10-24 / 10:48' ); + + // Test with New York timezone + const formattedNY = formatDateTimeFromTimestamp( timestamp, { + includeTime: true, + timezone: 'America/New_York', + } ); + expect( formattedNY ).toBe( '2024-10-24 / 06:48' ); + } ); } ); } ); From 0086c7ceced0ace7f324951976c186e30498fff3 Mon Sep 17 00:00:00 2001 From: Miguel Gasca Date: Wed, 4 Dec 2024 12:33:28 +0100 Subject: [PATCH 71/76] Initial implementation of notice in transactions list and overview page --- .../components/date-format-notice/index.tsx | 59 ++- client/overview/index.js | 2 + client/transactions/index.tsx | 2 - client/transactions/list/index.tsx | 2 + .../list/test/__snapshots__/index.tsx.snap | 420 ++++++++++++++++++ 5 files changed, 465 insertions(+), 20 deletions(-) diff --git a/client/components/date-format-notice/index.tsx b/client/components/date-format-notice/index.tsx index d7ded4fc1d5..45545d2f88e 100644 --- a/client/components/date-format-notice/index.tsx +++ b/client/components/date-format-notice/index.tsx @@ -3,39 +3,62 @@ */ import React, { useState } from 'react'; import { __ } from '@wordpress/i18n'; +import interpolateComponents from '@automattic/interpolate-components'; /** * Internal dependencies */ -import BannerNotice from 'components/banner-notice'; +import InlineNotice from 'components/inline-notice'; + +// eslint-disable-next-line @typescript-eslint/naming-convention +const STORAGE_KEY = 'wcpay_date_format_notice_dismissed'; const DateFormatNotice: React.FC = () => { - const [ isBannerVisible, setIsBannerVisible ] = useState( true ); + const [ isNoticeVisible, setIsNoticeVisible ] = useState( () => { + // Initialize state from localStorage + return localStorage.getItem( STORAGE_KEY ) !== 'true'; + } ); + + const handleDismiss = () => { + setIsNoticeVisible( false ); + localStorage.setItem( STORAGE_KEY, 'true' ); + }; + + const handleSettingsClick = () => { + // Optionally dismiss the notice when clicking settings + handleDismiss(); + }; - if ( ! isBannerVisible ) { + if ( ! isNoticeVisible ) { return null; } return ( - setIsBannerVisible( false ) } - actions={ [ - { - label: __( - 'Configure date settings', - 'woocommerce-payments' + onRemove={ handleDismiss } + > + { interpolateComponents( { + mixedString: __( + 'The date and time formats now match your preferences. You can update them anytime in the {{settingsLink}}settings{{/settingsLink}}.', + 'woocommerce-payments' + ), + components: { + settingsLink: ( + // eslint-disable-next-line jsx-a11y/anchor-has-content + ), - url: '/wp-admin/options-general.php', }, - ] } - > - { __( - 'The date and time formats now follow your preferences. You can customize these formats in the settings.', - 'woocommerce-payments' - ) } - + } ) } + ); }; diff --git a/client/overview/index.js b/client/overview/index.js index edb215993c7..0d56ad70893 100644 --- a/client/overview/index.js +++ b/client/overview/index.js @@ -33,6 +33,7 @@ import SandboxModeSwitchToLiveNotice from 'wcpay/components/sandbox-mode-switch- import './style.scss'; import BannerNotice from 'wcpay/components/banner-notice'; import { PayoutsRenameNotice } from 'wcpay/deposits/rename-notice'; +import DateFormatNotice from 'wcpay/components/date-format-notice'; const OverviewPageError = () => { const queryParams = getQuery(); @@ -196,6 +197,7 @@ const OverviewPage = () => { { ! accountRejected && ! accountUnderReview && ( + { showTaskList && ( diff --git a/client/transactions/index.tsx b/client/transactions/index.tsx index 9350404a89e..507a972cd5a 100644 --- a/client/transactions/index.tsx +++ b/client/transactions/index.tsx @@ -23,7 +23,6 @@ import { } from 'wcpay/data'; import WCPaySettingsContext from '../settings/wcpay-settings-context'; import BlockedList from './blocked'; -import DateFormatNotice from 'components/date-format-notice'; declare const window: any; @@ -108,7 +107,6 @@ export const TransactionsPage: React.FC = () => { return ( - ) } +
+ +
+
+
+
+ + + + + +
+
+ The date and time formats now match your preferences. You can update them anytime in the + + settings + + . +
+
+
+
+ +