By the end of this tutorial, your the release date/time of your scheduled lessons will be localized to the visitor’s computer!

Timezone-adjusted course content

You can see a live demo on our test site.

A story

When I was a kid living in the DC area, my family would occasionally watch Jeopardy (RIP, Alex Trebek).

I recall it aired every weeknight at 7:30 PM. Being in the DC area, I was in the Eastern Standard Time (EST) zone.

Sometimes, we’d visit family in the Central Standard Time (CST) zone, which means the clocks move back by one hour.

Jeopoardy would still air at the same absolute time, as in I couldn’t teleport to CST and somehow get an early showing relative to EST.

Although when Jeopardy started in CST, the clock read 6:30 PM.

Why am I telling you this story about Jeopardy

Because I want to make the scope of this tutorial very clear.

This will only affect the display of time, based on the timezone according to the computer or device the online course is accessed from.

This will not have any affect on the absolute time a lesson is released. That would be very complicated and not something I would recommend without careful consideration.

Step #1: Set up your test environment

We’re going to need the following:

  • A WordPress timezone setting set to “UTC” (no offset, no matter where you happen to be located).
  • One course, with at least two scheduled lessons with specific date scheduling.
  • One non-admin user, who has not yet progressed to the first scheduled lesson referenced in the list item directly above.

When you’re done, you should see something like the following from the perspective of that non-admin user.

Pro Tip: Take advantage of Course Builder to quickly scaffold out course content, even if they’re just for test purposes

Why UTC time

When I first started this tutorial, I wrote quite a bit of code to pull out WordPress timezone settings into JavaScript variables, so I could do this:

  • Keep my WordPress timezone setting set to my local time (New York / EST)
  • Use JavaScript libraries like Moment.js to calculate the offset between my local time, and the visitor’s local time
  • Then use JavaScript to adjust the time accordingly.

I figured it out, but then I realized I was seriously overcomplicating things as modern-day JavaScript can do quite a bit out-of-the-box, just as long as you use UTC time.

In my search for a library, I found Luxon. Their documentation reads:

Don’t make servers think about local times. Configure them to use UTC and write your server’s code to work in UTC. Times can often be thought of as a simple count of epoch milliseconds; what you would call that time (e.g. 9:30) in what zone doesn’t (again, often) matter.

So when I set my WordPress timezone setting to my local time, I was going against the above guidance because I was making servers think about local times.

So I set it to UTC time, to make things simpler for our forthcoming JavaScript.

“UTC” set for our WordPress timezone setting.

Now this does have ramifications beyond our specific use case. Whenever you see time anywhere in WordPress, it’ll show you UTC time.

When it comes to the user experience of showing a student their local time when it comes to scheduled lesson releases, I believe the pro outweighs the con.

Step #2: Add a snippet

The goal here is for the user’s browser to automatically adjust the UTC time to local time.

To get started, add this PHP snippet to your WordPress site:

/* This function alters the output of time in the HTML markup
** to wrap the time in [ and ] and add the letters "UTC"
** which our JavaScript is expecting.
*/
function lmscoder_modify_time_for_timezone_adjustment( $attributes, $lesson ) {
	$wp_timezone = json_decode( json_encode( wp_timezone() ), true );

	// Check if we have the expected "UTC" timezone set in WordPress
	if ( 3 === $wp_timezone["timezone_type"] AND "UTC" === $wp_timezone["timezone"] ) {
		$lesson_access_from = $lesson["lesson_access_from"];
	
		// Update the label to what the JavaScript timezone adjustment script is expecting
		if ( ! empty( $lesson_access_from ) ) {
			$updated_label = 'Available on [' . learndash_adjust_date_time_display( $lesson_access_from ) . ' UTC]';
			$attributes[0]["label"] = $updated_label;
		}
	}

	return $attributes;
}
add_filter( 'learndash_lesson_attributes', 'lmscoder_modify_time_for_timezone_adjustment', 10, 2 );

/* This function adds JavaScript to the page that
** loops through .ld-status-waiting elements
** and converts the UTC date/time to local time with
** the toLocaleTimeString method in JavaScript.
*/
function lmscoder_timezone_adjustment() { ?>
	<script>			
		function timezoneAdjuster( element ) {
			const startChar = '[';
			const endChar = ']';
			
			const elementInnerHTML = element.innerHTML;
			const elementSplit = elementInnerHTML.split( startChar ).pop().split( endChar );
			const originalDateString = elementSplit[0];

			const utcDate = new Date( originalDateString );
			const adjustedDate = utcDate.toLocaleTimeString( [], {year: 'numeric', month: 'long', day: 'numeric', hour: 'numeric', minute: '2-digit', timeZoneName: 'short'} );
			
			element.textContent = element.textContent.replace( startChar + originalDateString + endChar, adjustedDate );
		}
			
		const waitingElements = document.querySelectorAll( '.ld-status-waiting' );
		
		waitingElements.forEach( element => timezoneAdjuster( element ) );
	</script>
<?php }
add_action( 'wp_footer', 'lmscoder_timezone_adjustment' );

Explanation of the snippet

It comes in two parts.

Part 1: A wrapper for the date/time

I needed to give JavaScript an easy way to pick out the date/time from a string that included other things.

For example, from the string “Available on May 1, 2021, 11:30 AM” I needed to pick out just the “May 1, 2021, 11:30 AM” part.

The obvious path forward would be to simply wrap that part in an HTML element.

Turned out that was a bit too tall of a task, because it would’ve involved circumventing a few levels of WordPress escaping functions, and any HTML I tried to output got escaped.

Instead, I decided to wrap the date [within bracket characters].

Pro Tip: There’s nothing special about the [ and ] characters, you could use # or $, ^ or &, as long as the “start character” and “end character” are defined accordingly in the JavaScript portion.

I also need to insert the letters “UTC” before the closing bracket character.

Basically, to cite one example, I want to change:

Available on May 1, 2021, 11:30 AM

to:

Available on [May 1, 2021, 11:30 AM UTC]

So my JavaScript can take it from there.

Part 2: The actual timezone adjustment

First, we define variables like the aforementioned “start character” ([) and the “end character” (]) to pluck out the actual date/time string.

Then we’re passing that to a Date object, then running the toLocaleTimeString method to get the locale time string.

Step #3: Confirm the snippet works

You can zero in on one time.

Timezone-adjusted course content

In my case, I literally wrote out the UTC time as my lesson title to make it easy to remember.

I like to use Wolfram Alpha for tests like this.

So I typed in “May 1, 2021 11:30 UTC in EDT” and confirmed that that time does indeed equal 7:30 AM in EDT.

Testing timezone adjustment in Wolfram Alpha.

Pretty cool!

Quick thanks to Shakeel of How-To WP for helping me test!

Time is complicated and it helps to have extra sets of eyes to make sure the timezone-adjusted strings display as expected.