mirror of
https://github.com/gosticks/wordpress-develop.git
synced 2026-08-11 20:30:23 +00:00
Community Events: Display dates and times in the user's time zone.
Fixes #51130 Props sippis, hlashbrooke, audrasjb, Rarst, iandunn git-svn-id: https://develop.svn.wordpress.org/trunk@49146 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
@@ -266,6 +266,11 @@ jQuery( function( $ ) {
|
||||
'use strict';
|
||||
|
||||
var communityEventsData = window.communityEventsData || {},
|
||||
dateI18n = wp.date.dateI18n,
|
||||
format = wp.date.format,
|
||||
sprintf = wp.i18n.sprintf,
|
||||
__ = wp.i18n.__,
|
||||
_x = wp.i18n._x,
|
||||
app;
|
||||
|
||||
/**
|
||||
@@ -441,6 +446,7 @@ jQuery( function( $ ) {
|
||||
.fail( function() {
|
||||
app.renderEventsTemplate({
|
||||
'location' : false,
|
||||
'events' : [],
|
||||
'error' : true
|
||||
}, initiatedBy );
|
||||
});
|
||||
@@ -465,6 +471,11 @@ jQuery( function( $ ) {
|
||||
$locationMessage = $( '#community-events-location-message' ),
|
||||
$results = $( '.community-events-results' );
|
||||
|
||||
templateParams.events = app.populateDynamicEventFields(
|
||||
templateParams.events,
|
||||
communityEventsData.time_format
|
||||
);
|
||||
|
||||
/*
|
||||
* Hide all toggleable elements by default, to keep the logic simple.
|
||||
* Otherwise, each block below would have to turn hide everything that
|
||||
@@ -576,6 +587,195 @@ jQuery( function( $ ) {
|
||||
} else {
|
||||
app.toggleLocationForm( 'show' );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Populate event fields that have to be calculated on the fly.
|
||||
*
|
||||
* These can't be stored in the database, because they're dependent on
|
||||
* the user's current time zone, locale, etc.
|
||||
*
|
||||
* @since 5.6.0
|
||||
*
|
||||
* @param {Array} rawEvents The events that should have dynamic fields added to them.
|
||||
* @param {string} timeFormat A time format acceptable by `wp.date.dateI18n()`.
|
||||
*
|
||||
* @returns {Array}
|
||||
*/
|
||||
populateDynamicEventFields: function( rawEvents, timeFormat ) {
|
||||
// Clone the parameter to avoid mutating it, so that this can remain a pure function.
|
||||
var populatedEvents = JSON.parse( JSON.stringify( rawEvents ) );
|
||||
|
||||
$.each( populatedEvents, function( index, event ) {
|
||||
var timeZone = app.getTimeZone( event.start_unix_timestamp * 1000 );
|
||||
|
||||
event.user_formatted_date = app.getFormattedDate(
|
||||
event.start_unix_timestamp * 1000,
|
||||
event.end_unix_timestamp * 1000,
|
||||
timeZone
|
||||
);
|
||||
|
||||
event.user_formatted_time = dateI18n(
|
||||
timeFormat,
|
||||
event.start_unix_timestamp * 1000,
|
||||
timeZone
|
||||
);
|
||||
|
||||
event.timeZoneAbbreviation = app.getTimeZoneAbbreviation( event.start_unix_timestamp * 1000 );
|
||||
} );
|
||||
|
||||
return populatedEvents;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the user's local/browser time zone, in a form suitable for `wp.date.i18n()`.
|
||||
*
|
||||
* @since 5.6.0
|
||||
*
|
||||
* @param startTimestamp
|
||||
*
|
||||
* @returns {string|number}
|
||||
*/
|
||||
getTimeZone: function( startTimestamp ) {
|
||||
/*
|
||||
* Prefer a name like `Europe/Helsinki`, since that automatically tracks daylight savings. This
|
||||
* doesn't need to take `startTimestamp` into account for that reason.
|
||||
*/
|
||||
var timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
||||
|
||||
/*
|
||||
* Fall back to an offset for IE11, which declares the property but doesn't assign a value.
|
||||
*/
|
||||
if ( 'undefined' === typeof timeZone ) {
|
||||
/*
|
||||
* It's important to use the _event_ time, not the _current_
|
||||
* time, so that daylight savings time is accounted for.
|
||||
*/
|
||||
timeZone = app.getFlippedTimeZoneOffset( startTimestamp );
|
||||
}
|
||||
|
||||
return timeZone;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get intuitive time zone offset.
|
||||
*
|
||||
* `Data.prototype.getTimezoneOffset()` returns a positive value for time zones
|
||||
* that are _behind_ UTC, and a _negative_ value for ones that are ahead.
|
||||
*
|
||||
* See https://stackoverflow.com/questions/21102435/why-does-javascript-date-gettimezoneoffset-consider-0500-as-a-positive-off.
|
||||
*
|
||||
* @since 5.6.0
|
||||
*
|
||||
* @param {number} startTimestamp
|
||||
*
|
||||
* @returns {number}
|
||||
*/
|
||||
getFlippedTimeZoneOffset: function( startTimestamp ) {
|
||||
return new Date( startTimestamp ).getTimezoneOffset() * -1;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get a short time zone name, like `PST`.
|
||||
*
|
||||
* @since 5.6.0
|
||||
*
|
||||
* @param {number} startTimestamp
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
getTimeZoneAbbreviation: function( startTimestamp ) {
|
||||
var timeZoneAbbreviation,
|
||||
eventDateTime = new Date( startTimestamp );
|
||||
|
||||
/*
|
||||
* Leaving the `locales` argument undefined is important, so that the browser
|
||||
* displays the abbreviation that's most appropriate for the current locale. For
|
||||
* some that will be `UTC{+|-}{n}`, and for others it will be a code like `PST`.
|
||||
*
|
||||
* This doesn't need to take `startTimestamp` into account, because a name like
|
||||
* `America/Chicago` automatically tracks daylight savings.
|
||||
*/
|
||||
var shortTimeStringParts = eventDateTime.toLocaleTimeString( undefined, { timeZoneName : 'short' } ).split( ' ' );
|
||||
|
||||
if ( 3 === shortTimeStringParts.length ) {
|
||||
timeZoneAbbreviation = shortTimeStringParts[2];
|
||||
}
|
||||
|
||||
if ( 'undefined' === typeof timeZoneAbbreviation ) {
|
||||
/*
|
||||
* It's important to use the _event_ time, not the _current_
|
||||
* time, so that daylight savings time is accounted for.
|
||||
*/
|
||||
var timeZoneOffset = app.getFlippedTimeZoneOffset( startTimestamp ),
|
||||
sign = -1 === Math.sign( timeZoneOffset ) ? '' : '+';
|
||||
|
||||
// translators: Used as part of a string like `GMT+5` in the Events Widget.
|
||||
timeZoneAbbreviation = _x( 'GMT', 'Events widget offset prefix' ) + sign + ( timeZoneOffset / 60 );
|
||||
}
|
||||
|
||||
return timeZoneAbbreviation;
|
||||
},
|
||||
|
||||
/**
|
||||
* Format a start/end date in the user's local time zone and locale.
|
||||
*
|
||||
* @since 5.6.0
|
||||
*
|
||||
* @param {int} startDate The Unix timestamp in milliseconds when the the event starts.
|
||||
* @param {int} endDate The Unix timestamp in milliseconds when the the event ends.
|
||||
* @param {string} timeZone A time zone string or offset which is parsable by `wp.date.i18n()`.
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
getFormattedDate: function( startDate, endDate, timeZone ) {
|
||||
var formattedDate;
|
||||
|
||||
/*
|
||||
* The `date_format` option is not used because it's important
|
||||
* in this context to keep the day of the week in the displayed date,
|
||||
* so that users can tell at a glance if the event is on a day they
|
||||
* are available, without having to open the link.
|
||||
*
|
||||
* The case of crossing a year boundary is intentionally not handled.
|
||||
* It's so rare in practice that it's not worth the complexity
|
||||
* tradeoff. The _ending_ year should be passed to
|
||||
* `multiple_month_event`, though, just in case.
|
||||
*/
|
||||
/* translators: Date format for upcoming events on the dashboard. Include the day of the week. See https://www.php.net/manual/datetime.format.php */
|
||||
var singleDayEvent = __( 'l, M j, Y' ),
|
||||
/* translators: Date string for upcoming events. 1: Month, 2: Starting day, 3: Ending day, 4: Year. */
|
||||
multipleDayEvent = __( '%1$s %2$d–%3$d, %4$d' ),
|
||||
/* translators: Date string for upcoming events. 1: Starting month, 2: Starting day, 3: Ending month, 4: Ending day, 5: Ending year. */
|
||||
multipleMonthEvent = __( '%1$s %2$d – %3$s %4$d, %5$d' );
|
||||
|
||||
// Detect single-day events.
|
||||
if ( ! endDate || format( 'Y-m-d', startDate ) === format( 'Y-m-d', endDate ) ) {
|
||||
formattedDate = dateI18n( singleDayEvent, startDate, timeZone );
|
||||
|
||||
// Multiple day events.
|
||||
} else if ( format( 'Y-m', startDate ) === format( 'Y-m', endDate ) ) {
|
||||
formattedDate = sprintf(
|
||||
multipleDayEvent,
|
||||
dateI18n( _x( 'F', 'upcoming events month format' ), startDate, timeZone ),
|
||||
dateI18n( _x( 'j', 'upcoming events day format' ), startDate, timeZone ),
|
||||
dateI18n( _x( 'j', 'upcoming events day format' ), endDate, timeZone ),
|
||||
dateI18n( _x( 'Y', 'upcoming events year format' ), endDate, timeZone )
|
||||
);
|
||||
|
||||
// Multi-day events that cross a month boundary.
|
||||
} else {
|
||||
formattedDate = sprintf(
|
||||
multipleMonthEvent,
|
||||
dateI18n( _x( 'F', 'upcoming events month format' ), startDate, timeZone ),
|
||||
dateI18n( _x( 'j', 'upcoming events day format' ), startDate, timeZone ),
|
||||
dateI18n( _x( 'F', 'upcoming events month format' ), endDate, timeZone ),
|
||||
dateI18n( _x( 'j', 'upcoming events day format' ), endDate, timeZone ),
|
||||
dateI18n( _x( 'Y', 'upcoming events year format' ), endDate, timeZone )
|
||||
);
|
||||
}
|
||||
|
||||
return formattedDate;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user