Skip to content

fix(I18n): compute correct date in Time::today(), yesterday(), and tomorrow() across timezones - #10532

Open
gr8man wants to merge 2 commits into
codeigniter4:developfrom
gr8man:fix/time-today-timezone
Open

fix(I18n): compute correct date in Time::today(), yesterday(), and tomorrow() across timezones#10532
gr8man wants to merge 2 commits into
codeigniter4:developfrom
gr8man:fix/time-today-timezone

Conversation

@gr8man

@gr8man gr8man commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Description
Time::today($timezone), Time::yesterday($timezone), and Time::tomorrow($timezone) previously used PHP's native date('Y-m-d 00:00:00') and strtotime('-1 day') / strtotime('+1 day') to generate the date string passed to the constructor.

Because date() and strtotime() evaluate in the server's default timezone (date_default_timezone_get()), the target $timezone argument was ignored when determining the calendar day. For several hours each day (when the server timezone like UTC and the requested timezone like Asia/Tokyo or America/New_York are on different calendar days), today(), yesterday(), and tomorrow() produced the wrong calendar day in that timezone.

Furthermore, passing a non-empty string from date() into new static(...) bypassed Time::setTestNow() in the constructor, preventing today(), yesterday(), and tomorrow() from respecting mocked test time.

This PR fixes both issues by using static::now($timezone, $locale)->setTime(0, 0, 0, 0) (and modify('-1 day') / modify('+1 day')). This ensures:

  1. Midnight is computed relative to the requested timezone.
  2. Time::setTestNow() mock instances are preserved and converted accurately.
  3. Unit tests cover date boundary transitions across different timezones.
  4. A changelog entry is added to v4.7.5.rst.

Checklist:

  • Securely signed commits
  • Component(s) with PHPDoc blocks, only if necessary or adds value (without duplication)
  • Unit testing, with >80% coverage
  • User guide updated
  • Conforms to style guide

…terday(), and tomorrow()

- Compute start of day relative to the requested timezone instead of the server default timezone
- Respect Time::setTestNow() mock instance in today(), yesterday(), and tomorrow()
- Add unit tests covering date boundary transitions across different timezones
- Add changelog entry in v4.7.5.rst
Comment thread user_guide_src/source/changelogs/v4.7.5.rst Outdated
Co-authored-by: neznaika0 <ozornick.ks@gmail.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

It introduces subclass compatibility changes, leaves an existing timezone test intermittent, and lacks coverage for TimeLegacy.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Fixes timezone-aware calendar calculations and mocked-time handling. Compatibility assessment assumes develop because the base branch was not provided.

Changes:

  • Computes relative dates from timezone-aware now().
  • Adds timezone-boundary regression tests.
  • Documents the fix in the changelog.

Validation: code inspection only; test execution remains for CI.

File summaries
File Description
system/I18n/TimeTrait.php Updates date helper calculations.
tests/system/I18n/TimeTest.php Adds boundary tests.
user_guide_src/source/changelogs/v4.7.5.rst Adds changelog entry.
Review details

Suppressed comments (3)

system/I18n/TimeTrait.php:161

  • This newly routes inherited yesterday() calls through an overridable now(). A third-party subclass that overrides now() but relies on the inherited calendar helper therefore changes behavior unexpectedly; construct with null directly so the fix still honors setTestNow() without coupling these extension points.
        return static::now($timezone, $locale)->modify('-1 day')->setTime(0, 0, 0, 0);

system/I18n/TimeTrait.php:175

  • This newly routes inherited tomorrow() calls through an overridable now(). A third-party subclass that overrides now() but relies on the inherited calendar helper therefore changes behavior unexpectedly; construct with null directly so the fix still honors setTestNow() without coupling these extension points.
        return static::now($timezone, $locale)->modify('+1 day')->setTime(0, 0, 0, 0);

system/I18n/TimeTrait.php:147

  • The existing testTodayLocalized() still compares Time::today('Europe/London') with server-local date(). With this fix, that assertion fails when London and the server are on different dates (for example, 23:30 UTC during BST). Freeze the clock and assert the London date, or build the expectation in the requested timezone.
        return static::now($timezone, $locale)->setTime(0, 0, 0, 0);
  • Files reviewed: 3/3 changed files
  • Comments generated: 3
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread system/I18n/TimeTrait.php
public static function today($timezone = null, ?string $locale = null)
{
return new static(date('Y-m-d 00:00:00'), $timezone, $locale);
return static::now($timezone, $locale)->setTime(0, 0, 0, 0);
$this->assertSame(date('Y-m-d 00:00:00', strtotime('+1 day')), $time->toDateTimeString());
}

public function testTodayWithTimezoneAcrossDateBoundary(): void
- **Files:** Fixed a bug where ``File::move()`` and ``UploadedFile::move()`` set executable and overly permissive file permissions (``0777 & ~umask()`` instead of ``0666 & ~umask()``), and ``UploadedFile::move()`` targeted the parent directory instead of the destination file for ``chmod()``.
- **Helpers:** Fixed a bug where ``get_dir_file_info()`` returned incomplete entries for subdirectories and missing files instead of omitting them.
- **Honeypot:** Fixed a bug where bot detection returned an HTTP 500 response instead of 403 (Forbidden).
- **I18n:** Fixed a bug where ``Time::today()``, ``Time::yesterday()``, and ``Time::tomorrow()`` ignored the specified ``$timezone``, ``$locale`` and ``setTestNow()`` when calculating the day.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants