From 04070ebcaf0578c703b475574477efeb91bba412 Mon Sep 17 00:00:00 2001 From: Kelly Dwan Date: Fri, 11 Oct 2024 17:07:29 -0400 Subject: [PATCH] WC Post Types: Fix timezone offset when sessions happen after DST MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change updates how the session dates are converted when the site and client timezones are different. Rather than creating a fixed timezone offset on page load, this converts the times into the server timezone for the given date — this way, the offset is correctly applied if a future date is in a different timezone (DST starts or ends). Fixes #1385 --- .../plugins/wc-post-types/css/editor.css | 1 - .../js/src/components/date-control/index.js | 57 ++++++++++++++----- .../plugins/wc-post-types/package.json | 4 +- yarn.lock | 10 ++++ 4 files changed, 57 insertions(+), 15 deletions(-) diff --git a/public_html/wp-content/plugins/wc-post-types/css/editor.css b/public_html/wp-content/plugins/wc-post-types/css/editor.css index e2b8ff78f6..f6593060e4 100644 --- a/public_html/wp-content/plugins/wc-post-types/css/editor.css +++ b/public_html/wp-content/plugins/wc-post-types/css/editor.css @@ -9,7 +9,6 @@ .wordcamp-panel-session-info .components-datetime__time .components-button, .wordcamp-panel-session-info .components-datetime__time input[type="number"], .wordcamp-panel-session-info .components-datetime__time select { - height: 30px; margin-bottom: 0; margin-top: 0; } diff --git a/public_html/wp-content/plugins/wc-post-types/js/src/components/date-control/index.js b/public_html/wp-content/plugins/wc-post-types/js/src/components/date-control/index.js index e7c9e8815d..ab49b4113e 100644 --- a/public_html/wp-content/plugins/wc-post-types/js/src/components/date-control/index.js +++ b/public_html/wp-content/plugins/wc-post-types/js/src/components/date-control/index.js @@ -1,11 +1,18 @@ +/** + * External dependencies + */ +import { format } from 'date-fns'; +import { TZDate } from '@date-fns/tz'; + /** * WordPress dependencies */ -// eslint-disable-next-line @wordpress/no-unsafe-wp-apis -- Date settings OK. -import { __experimentalGetSettings, dateI18n } from '@wordpress/date'; +import { getSettings } from '@wordpress/date'; import { BaseControl, TimePicker } from '@wordpress/components'; import { sprintf } from '@wordpress/i18n'; +const TIMEZONELESS_FORMAT = "yyyy-MM-dd'T'HH:mm:ss"; + /** * Using the site settings, generate the offset in ISO 8601 format (`+00:00`). * @@ -24,8 +31,8 @@ function getTimezoneOffset( { offset = 0 } ) { ); } -export default function( { date, label, onChange } ) { - const settings = __experimentalGetSettings(); +export default function ( { date, label, onChange } ) { + const settings = getSettings(); const is12HourTime = /a(?!\\)/i.test( settings.formats.time .toLowerCase() // Test only the lower case a @@ -35,20 +42,44 @@ export default function( { date, label, onChange } ) { .join( '' ) // Reverse the string and test for "a" not followed by a slash ); - // Remove the timezone info from the date. `TimePicker` uses an instance of moment that does not know about - // the site timezone, so passing it in causes an unexpected offset. - const dateNoTZ = dateI18n( 'Y-m-d\\TH:i:s', date, 'WP' ); + // The `TimePicker` component is timezone-agnostic, so `currentTime` should be a + // date-time string without a timezone (but in the server timezone for display). + + // First get the server timezone from date settings. This could be a string + // like "America/New_York" or an offset like "-1". + let serverTimezone = settings.timezone.string; + if ( ! serverTimezone ) { + // If it's a offset, convert it to ISO 8601 format. + serverTimezone = getTimezoneOffset( settings.timezone ); + } + + // Because this date is a timestamp, it's understood to be a UTC value, and + // can be simply created with the correct timezone. + const dateServerTZ = new TZDate( date, serverTimezone ); return ( { label } { - // dateValue is a tz-less string, so we need to add the site-timezone offset. - // Otherwise, `dateI18n` tries to use the browser timezone, which might not be the same. - const offset = getTimezoneOffset( settings.timezone ); - const value = dateI18n( 'U', dateValue + offset ); + currentTime={ format( dateServerTZ, TIMEZONELESS_FORMAT ) } + onChange={ ( newDate ) => { + // Parse the date with the server timezone. This will be an incorrect date, because + // the date is first converted to the client timezone, then the server timezone data + // is added. For example, if the TimePicker reads 9:00 UTC-8 (server timezone), but + // the client timezone is UTC-5, when created, the timestamp for the date will be + // 9:00 UTC-5. Instead, we should create a new date with the server timezone appended, + // so that it will create the correct UTC timestamp. + // This `_date` is only used to get the timezone in the correct format. + const _date = new TZDate( newDate, serverTimezone ); + + // XXX returns the timezone (ISO-8601 w/ Z), e.g. -08:00. + const tz = format( _date, 'XXX' ); + + // Now create a new date with the correct timezone. + const newDateTZ = new Date( newDate + tz ); + + // Save value in seconds format for post meta. + const value = format( newDateTZ, 't' ); onChange( value ); } } is12Hour={ is12HourTime } diff --git a/public_html/wp-content/plugins/wc-post-types/package.json b/public_html/wp-content/plugins/wc-post-types/package.json index 3afb454846..4810e75aa1 100644 --- a/public_html/wp-content/plugins/wc-post-types/package.json +++ b/public_html/wp-content/plugins/wc-post-types/package.json @@ -12,6 +12,7 @@ "url": "https://github.com/WordPress/wordcamp.org/issues?q=label%3A%22%5BComponent%5D+WC-Post-Types%22" }, "devDependencies": { + "@date-fns/tz": "1.1.2", "@wordpress/api-fetch": "6.2.0", "@wordpress/components": "19.7.0", "@wordpress/compose": "5.3.0", @@ -22,7 +23,8 @@ "@wordpress/eslint-plugin": "11.1.0", "@wordpress/i18n": "4.5.0", "@wordpress/plugins": "4.3.0", - "@wordpress/scripts": "22.3.0" + "@wordpress/scripts": "22.3.0", + "date-fns": "4.1.0" }, "eslintConfig": { "extends": "../../../../.eslintrc.js", diff --git a/yarn.lock b/yarn.lock index c269dceac0..3d66ebd068 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2318,6 +2318,11 @@ resolved "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-3.0.0.tgz" integrity sha512-hBI9tfBtuPIi885ZsZ32IMEU/5nlZH/KOVYJCOh7gyMxaVLGmLedYqFN6Ui1LXkI8JlC8IsuC0rF0btcRZKd5g== +"@date-fns/tz@1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@date-fns/tz/-/tz-1.1.2.tgz#c8036db48ae9e7165f40a951942a14070e0c6ec1" + integrity sha512-Xmg2cPmOPQieCLAdf62KtFPU9y7wbQDq1OAzrs/bEQFvhtCPXDiks1CHDE/sTXReRfh/MICVkw/vY6OANHUGiA== + "@discoveryjs/json-ext@0.5.7": version "0.5.7" resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" @@ -7424,6 +7429,11 @@ data-urls@^3.0.2: whatwg-mimetype "^3.0.0" whatwg-url "^11.0.0" +date-fns@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-4.1.0.tgz#64b3d83fff5aa80438f5b1a633c2e83b8a1c2d14" + integrity sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg== + debounce@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.1.tgz#38881d8f4166a5c5848020c11827b834bcb3e0a5"