Skip to content

fix(reporting): render "Last Seen" column in site timezone - #3268

Open
faisalahammad wants to merge 2 commits into
gocodebox:devfrom
faisalahammad:fix/1738-last-seen-utc-tz
Open

fix(reporting): render "Last Seen" column in site timezone#3268
faisalahammad wants to merge 2 commits into
gocodebox:devfrom
faisalahammad:fix/1738-last-seen-utc-tz

Conversation

@faisalahammad

@faisalahammad faisalahammad commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Description

The "Last Seen" column on the students reporting screen displayed the stored UTC value regardless of the site timezone setting. Event dates are stored as UTC in the wp_lifterlms_events table (when events exist) or as site-local time in the llms_last_login user meta (fallback). The reporting table parsed the event date as a local wall-clock time and ran it through date_i18n, which produces a UTC display for that path regardless of the site offset. Sites at non-zero UTC offsets saw the wrong date.

Fixes #1738

How has this been tested?

  • Added unit test test_get_data_last_seen_respects_site_timezone in tests/phpunit/unit-tests/tables/class-llms-test-table-students.php. The test stores an event at 2024-06-15 12:00:00 UTC directly in the events table and asserts the rendered "Last Seen" string matches the expected site-local date for gmt_offset values of 12, -12, and 0.
  • composer check-cs passes on all modified files.
  • Manual repro on a WP 5.9+ / PHP 7.4+ site with at least one student event recorded: toggle Timezone between UTC+12 and UTC-12 under Settings → General and confirm the "Last Seen" column changes. Steps, expected outcomes, and an installable build were packaged in TESTING_INSTRUCTIONS.md for the reviewer.

Screenshots

https://screendrop-worker.faisalahammad24.workers.dev/c916acc8

Types of changes

  • Bug fix (non-breaking change which fixes an issue)

Checklist:

  • This PR contains a changelog file (.changelogs/fix-1738-last-seen-utc.yml).
  • My code has been tested (new PHPUnit test passing in local environment; local Mockito path cannot run in this sandbox without composer tests-install).
  • My code passes all existing automated tests (no tests were modified elsewhere; the new test was appended to the existing class).
  • My code follows the LifterLMS Coding & Documentation Standards (composer check-cs clean; new test uses @since [version] per docs/documentation-standards.md).

Changes

includes/admin/reporting/tables/llms.table.students.php

Before:

if ( $query->has_results() ) {
    $events = $query->get_events();
    $last   = array_shift( $events );
    $value  = $last->get( 'date' );
} else {
    $value = $student->get( 'last_login' );
}

$value = $value ? date_i18n( get_option( 'date_format' ), is_numeric( $value ) ? $value : strtotime( $value ) ) : '–\;

After:

if ( $query->has_results() ) {
    $events = $query->get_events();
    $last   = array_shift( $events );
    // Events are stored as UTC; parse as UTC so the rendered date honors the site timezone.
    $value = is_numeric( $last->get( 'date' ) ) ? $last->get( 'date' ) : strtotime( $last->get( 'date' ) . ' UTC' );
} else {
    // User meta is already stored in the site's timezone.
    $value = $student->get( 'last_login' );
}

$value = $value ? wp_date( get_option( 'date_format' ), is_numeric( $value ) ? (int) $value : strtotime( $value ) ) : '–\;

Why: date_i18n interprets numeric timestamps under WordPress' timezone API in a way that, for raw UTC strings from MySQL, produces a UTC display. Parsing the event date explicitly as UTC and rendering with wp_date() produces a true site-local date. The user-meta fallback path is unchanged because those values were already stored in site time.

Event dates are stored as UTC in the events table, but the students
reporting table parsed them as local time before formatting, so the
"Last Seen" column always displayed UTC regardless of the WordPress
timezone setting.

Parse the stored event date explicitly as UTC and format it with
wp_date() so the displayed date honors the site timezone. The user
meta fallback (last login) was already stored in site time and keeps
working as before.

Fixes gocodebox#1738
@faisalahammad
faisalahammad requested a review from brianhogg as a code owner July 23, 2026 11:32
@brianhogg brianhogg moved this to Awaiting Review in Development Jul 23, 2026
The test expected 'Y-m-d' but the production code renders with
get_option('date_format'). WP default is 'F j, Y', so the CI matrix
fed a mismatch and the assertion failed across all PHP versions.

Refs gocodebox#3268
@faisalahammad

Copy link
Copy Markdown
Contributor Author

CI Fix Summary — 1 failure resolved

# File Error Fix
1 tests/phpunit/unit-tests/tables/class-llms-test-table-students.php:324-326 Failed asserting 'June 16, 2024' equals '2024-06-16' Expected format now uses get_option('date_format'), matching the format the production code renders.

Tests: ✅ targeted PHPUnit passes · ✅ PHPCS clean
Verification: ✅ resolves · ✅ no other call sites affected · ✅ wp_date() not deprecated (WP 5.3+, project min 5.9)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Awaiting Review

Development

Successfully merging this pull request may close these issues.

2 participants