Make WordPress Core

Opened 7 years ago

Closed 6 years ago

Last modified 20 months ago

#39595 closed enhancement (wontfix)

date_default_timezone_set in wp-settings.php is set prematurely and ignorantly, regardless of other defined settings

Reported by: mkormendy's profile mkormendy Owned by:
Milestone: Priority: normal
Severity: minor Version: 4.7.1
Component: Date/Time Keywords: reporter-feedback
Focuses: Cc:

Description

I've been having issues in WordPress with my logs and my plugins reporting either UTC or local time, or incorrect offsets, but not one consistent time code as selected in Settings > General setting in the WP Admin.

Realizing that server configurations are difficult to corral for consistency, some simple checks should be performed and reported to the user before adjusting their timezone and dates in WordPress.

This could have serious effects to many plugins as well, depending on what they previously expected, and what actually *should* happen.

First, some background…

The operating system (CentOS 7) reports this:

[mike@myserver ~]# timedatectl
      Local time: Sat 2017-01-14 21:31:47 CST
  Universal time: Sun 2017-01-15 03:31:47 UTC
        RTC time: Sun 2017-01-15 09:10:47
       Time zone: America/Chicago (CST, -0600)
     NTP enabled: yes
NTP synchronized: yes
 RTC in local TZ: no
      DST active: no
 Last DST change: DST ended at
                  Sun 2016-11-06 01:59:59 CDT
                  Sun 2016-11-06 01:00:00 CST
 Next DST change: DST begins (the clock jumps one hour forward) at
                  Sun 2017-03-12 01:59:59 CST
                  Sun 2017-03-12 03:00:00 CDT

My Apache httpd.conf has this setting (with successful expectations):

SetEnv TZ America/Chicago

My PHP php.ini file has this setting (with successful expectations):

[Date]
date.timezone = America/Chicago

And with PHP CLI, when I run the following command, PHP reports this:

[mike@myserver ~]# php -a
Interactive shell

php > echo date_default_timezone_get(time());
America/Chicago

Because MySQL naturally bases its timezone/offset settings on the operating system (I will ignore any sort of timezone database configurations).
When I log into MySQL via the command line and run the following query at the current time of 9:43pm on January 14, 2017, I get this (with successful expectations):

mysql> SELECT NOW();
       
2017-01-14 21:43:26

This verifies that my setup is configured properly on the backend.

Now to test the WordPress PHP code…

First, I would like to have WordPress operate as UTC-6 (or America/Chicago) time.
I go into the WP Admin Settings > General page and set the timezone to 'UTC-6'.

Next, I make sure all plugins are deactivated.

In the root 2017 theme, I have gone into my functions.php file to create some debugging flags. I have simply added this line to my functions file:

error_log('Get reported WordPress GMT offset: ' . get_option('gmt_offset'));
error_log('Get reported WordPress timezone: ' . get_option('timezone_string'));
error_log('Get reported PHP timezone: ' . date_default_timezone_get());

I turn on my debugging in my wp-config.php file and look to my logs to see what is outputted.

[mike@myserver ~]# tail -f -n 300/var/www/www.myserver.com/public_html/wp-content/debug.log

After loading the website homepage a few times, I see a series of entries:

[15-Jan-2017 04:03:31 UTC] Get reported WordPress GMT offset: -6
[15-Jan-2017 04:03:31 UTC] Get reported WordPress timezone: 
[15-Jan-2017 04:03:31 UTC] Get reported PHP timezone: UTC
[15-Jan-2017 04:03:31 UTC] Get reported WordPress GMT offset: -6
[15-Jan-2017 04:03:31 UTC] Get reported WordPress timezone: 
[15-Jan-2017 04:03:31 UTC] Get reported PHP timezone: UTC

The option for GMT offset is set to -6 as expected.
However, the preliminary timestamp shows a time in UTC and PHP still reports that something has set the timezone to UTC.

Then I change my timezone settings in the WP Admin under Settings > General from 'UTC-6' to 'Chicago' (under the America select dropdown group). My debug log now has this:

[15-Jan-2017 04:12:35 UTC] Get reported WordPress GMT offset: -6
[15-Jan-2017 04:12:35 UTC] Get reported WordPress timezone: America/Chicago
[15-Jan-2017 04:12:35 UTC] Get reported PHP timezone: UTC
[15-Jan-2017 04:12:35 UTC] Get reported WordPress GMT offset: -6
[15-Jan-2017 04:12:35 UTC] Get reported WordPress timezone: America/Chicago
[15-Jan-2017 04:12:35 UTC] Get reported PHP timezone: UTC

Okay so now both the GMT offset and America/Chicago show up as expected.
But again, the timestampt and PHP report the timezone is still set to UTC.

Remember, we set PHP's initialization to date.timezone = America/Chicago and verified that it was being reported by the PHP CLI outside of any other scripts running (WordPress or otherwise).

This is misleading, as any further PHP operations WP methods or otherwise now operate in UTC time.
This also has me wondering if the WP time/date related methods actually account for this in their operation. If I am using a plugin like Gravity Forms with its own custom tables, it may not use the traditional post time/date methods for its own records yet wish to still use the overall timezone setting in WordPress (a common and natural use case for many plugins).

Where exactly is WordPress forcing the timezone to UTC?

I search from the base of my website folder through all of the PHP files recursively for the string text of the function that solely responsible for changing ANY timezone in PHP, namely: date_default_timezone_set(). My search is abstracted to a simple date_default_timezone_set string without the parenthesis to catch all instances of the string.

Of the plugins (must-use or otherwise) that contain 'date_default_timezone_set', I have completely disabled them and confirm they are not active

The final output of searched files are the following:

/var/www/www.myserver.com/public_html/wp-settings.php:51:date_default_timezone_set( 'UTC' );
/var/www/www.myserver.com/public_html/wp-admin/options-general.php:178:  date_default_timezone_set($tzstring);
/var/www/www.myserver.com/public_html/wp-admin/options-general.php:220:  date_default_timezone_set('UTC');
/var/www/www.myserver.com/public_html/wp-includes/class-phpmailer.php:3275:        date_default_timezone_set(@date_default_timezone_get());

Notably, the first and the third items in my search stand out to me.

Altering the WordPress core to produce a change…

The only change that seemed to make a lasting positive effect across the entire installation where expected was to comment out the following line in my wp-settings.php file (~line 51):

// WordPress calculates offsets from UTC.
date_default_timezone_set( 'UTC' );

to:

// WordPress calculates offsets from UTC.
// date_default_timezone_set( 'UTC' );

Refreshing and visiting pages in the browser, my logs are reporting the timestamp correctly now. As well, when I create a post, posts, custom post-types and debug logs are all reporting their time correctly now.

[15-Jan-2017 01:59:04 America/Chicago] Get reported WordPress GMT offset: -6
[15-Jan-2017 01:59:04 America/Chicago] Get reported WordPress timezone: America/Chicago
[15-Jan-2017 01:59:04 America/Chicago] Get reported PHP timezone: America/Chicago
[15-Jan-2017 01:59:04 America/Chicago] Get reported WordPress GMT offset: -6
[15-Jan-2017 01:59:04 America/Chicago] Get reported WordPress timezone: America/Chicago
[15-Jan-2017 01:59:04 America/Chicago] Get reported PHP timezone: America/Chicago

The only thing that is reporting incorrectly is the output facade timestamps in the WP Admin under Settings > General:

Universal time (UTC) is <em>2017-01-15 02:12:06</em>. Local time is <em>2017-01-14 20:12:06</em>.

The Universal time is actually displaying what is a time matching my local time, and the local time is 12 hours behind universal time (which should be only 6 hours behind).

Conclusion…

I'm not sure there is something that should be done, or if I am simply attempting to prove an issue with an edge case (which I highly doubt).

Should the core WP code wp-settings.php file be altered so that it checks for the following first before setting a timezone?:

  • check for php.ini's date.timezone = America/Chicago
  • check for the values in the DB for both gmt_offset and timezone_string

If there are changes that are valid that need to be made but possibly affect many plugins and themes in the future, maybe there could be a 'Legacy' checkbox option in the WP Admin under Settings > General to allow people to use the old timezone settings as expected for their specific installation.

For the core developers that are more familiar with the code, can you shed some light or feedback on this?

Change History (23)

#1 @dd32
7 years ago

  • Keywords reporter-feedback added

All I can say here, is yes, it's intentional that we force the PHP timezone to UTC. It's not a bug, it's not being set too early, it's intentional.
WordPress expects that the base timezone is set to UTC for it's own calculations, and has forcibly set the timezone for ~7years since [12315] / #10940

Now, Can you boil down your actual issue to something much more concise so we can understand what the actual issue you face is? Is it that the PHP logs have a UTC timestamp?

Setting a servers localdate to something other than UTC is a recipe for disaster IMHO, and probably not something we're going to really support in any manner of form anytime soon.

#2 @mkormendy
7 years ago

Boiling down: yes, timestamps is one reason. But I'm also noticing that some plugins (major players: gravity forms, yoast seo, contact form DB) also don't store or report times correctly either because they expect the default PHP timezone to be either what is in php.ini -OR- what is stored in the WP Admin General Settings (wp_options: gmt_offset and timezone_string).

I realized that this was likely in trac for a long time and maybe a revision may be in order.

The force in wp-settings.php just feels "hijacky" because it forces a timezone for the entire WordPress application versus just the methods where time is stored and reported. This is in conflict with the extensibility of WordPress where it often runs alongside and intermingled with other PHP applications.

I have come across a slew of client servers (shared hosting, etc) where a reasonable amount of WP-based websites might not have a choice if the server is set to the local timezone for logging purposes and not UTC.

Last edited 7 years ago by mkormendy (previous) (diff)

#3 @dd32
7 years ago

But I'm also noticing that some plugins (major players: gravity forms, yoast seo, contact form DB) also don't store or report times correctly either because they expect the default PHP timezone to be either what is in php.ini -OR- what is stored in the WP Admin General Settings (wp_options: gmt_offset and timezone_string).

I'm going to assume that these are probably more likely caused by expecting the DB time returned by NOW() or as the default for date fields to be a UTC timestamp - unless you're experiencing that when you remove the date_default_timezone_set( 'UTC' ); call in which case yes, that would probably be the expected outcome of attempting to use date() functions.
MySQL doesn't store the timezone information with timestamps, so having NOW() return a non-UTC timestamp is very confusing for an application.

I have come across a slew of client servers (shared hosting, etc) where a reasonable amount of WP-based websites might not have a choice if the server is set to the local timezone for logging purposes and not UTC.

I fail to see what the problem is here if an application chooses to base it's timezone on UTC here. You can set the logging to anything - the fact that PHP logs respect the timezone the application chooses is odd, but IMHO not the applications problem.

#4 @mkormendy
7 years ago

I don't quite understand what you're attempting to state with regards to the DB time. If the time is stored as a string based on an retrieved date() call, it will be incorrect because WordPress hijacked the entire PHP timezone, that's a problem.

There isn't a problem with applications choosing to base their methods in a UTC timezone. BUT, the fundamental problem I see is that WordPress hijacks the entire timezone for the underlying PHP processor for the rest of any further intermingled scripted processes, especially when other PHP applications running in the same script process may rely on defaults in php.ini.

I might propose that the timezone method and/or the related date methods in WordPress store the original PHP timezone (if it existed), then do it's UTC change for what it needs, then switch it back, so other processes can work with PHP's default should they want to.

That's the ideal situation. But the reverse could be done to the non-WP applications just the same I guess...

However the longer I spend on this, I realize that this will likely never change in WordPress due to the implications that would have on a global-scale.

Version 1, edited 7 years ago by mkormendy (previous) (next) (diff)

#5 @Otto42
7 years ago

Realistically, the timezone handling in WordPress is sub-optimal because at the time it was implemented, WordPress still supported PHP 4, which lacked solid timezone support.

Would it be possible to replace all the time handling in WordPress with PHP 5 methods, using the proper default timezone set to the local timezone, and then using gmdate() and date() appropriately? Sure.

The amount of effort required to do so is bordering on insanity, but there are not actually as many backwards compatibility breaks as you might think.. Any WP dependent code (plugins, themes) are using WordPress functions for date/time display for the most part, and those outputs would remain unchanged.

So the question is what would be the benefits? Well, there probably would be a noticeable speed improvement. The timezone adjustment code is implemented as a filter and it's called far too often in normal usage. A lot of the underlying time handling code could be simplified to various wrappers around date() or gmdate(), speeding them up. Many of the odder functions in WordPress related to such could be changed to simply be those native calls in the first place, or calls to date_i18n() when appropriate.

Basically, it's a lot of work, without a lot of immediate benefit, and would also necessitate a heck of a lot of unit tests be written to verify it along the way. In that respect, I think the additional unit tests being made at all would probably be the main benefit from such a venture.

So, definitely worthy of consideration for the long term, but probably pretty low priority. Perhaps a migration path forward to switch everything to native date handling is something that could be considered.

#6 @mkormendy
7 years ago

Where do I see the current timezone unit tests? I may want to take this on as a contributor to the core, and begin to add my unit tests as well as the others for all testing.

#7 @mkormendy
7 years ago

  • Severity changed from normal to minor
  • Type changed from defect (bug) to enhancement

#8 @Otto42
7 years ago

Where do I see the current timezone unit tests?

There almost certainly aren't any. That code was written way before the unit test system was added.

#9 @mkormendy
7 years ago

Yikes! I should start creating some unit tests then! Mind if I coordinate with you on some of your high overview of tests we should perform?

#10 @romulodl
6 years ago

Another British Summer Time arrived and again I have the same issue. Just realised that wordpress forces UTC times and ignore time changes. Is there any workaround for this issue?

$ vendor/bin/wp shell
wp> new DateTime
=> object(DateTime)#3368 (3) {
  ["date"]=>
  string(26) "2018-03-27 11:19:44.396909"
  ["timezone_type"]=>
  int(3)
  ["timezone"]=>
  string(3) "UTC"

wp> date_default_timezone_set('Europe/London');
=> bool(true)

wp> new DateTime()
=> object(DateTime)#3368 (3) {
  ["date"]=>
  string(26) "2018-03-27 12:20:31.151286"
  ["timezone_type"]=>
  int(3)
  ["timezone"]=>
  string(13) "Europe/London"

my php.ini file is set to Europe/London
I am running Wordpress 4.9.4

Is there any plans to change this behaviour? If so, I could have a look at it...

Thanks

#11 @Otto42
6 years ago

@romulodl WordPress has it's own Timezone selection setting. Look on the Settings->General page and set your timezone there, instead of relying on the php.ini setting.

#12 @romulodl
6 years ago

@Otto42 thanks for the quick reply! and for the tip!

I am changing this as a workaround. But I agree with @mkormendy, summer time changes should be applied automatically using php default settings. As a site administrator I should not be changing this every 6 months.

#13 follow-up: @Otto42
6 years ago

@romulodl As a site administrator, you don't have to change it at all, ever. The PHP code is supposed to use UTC throughout. WordPress applies the timezone change according to the setting in the Settings->General page. That includes adjusting for daylight savings time, if you pick the correct timezone choice on that page.

Basically, WordPress does the same job as PHP here, while leaving the default timezone as UTC for consistent time calculation throughout the rest of the system.

#14 in reply to: ↑ 13 @romulodl
6 years ago

Replying to Otto42:

@romulodl As a site administrator, you don't have to change it at all, ever. The PHP code is supposed to use UTC throughout. WordPress applies the timezone change according to the setting in the Settings->General page. That includes adjusting for daylight savings time, if you pick the correct timezone choice on that page.

Basically, WordPress does the same job as PHP here, while leaving the default timezone as UTC for consistent time calculation throughout the rest of the system.

@Otto42 Thanks for that. Just after my reply I saw that you can put London there, not just only UTC+1. Ane really really sorry for the annoyance...

Just for the sake of information. If you use new DateTime in your theme/plugin it will not get the timezone selected in wordpress settings. Always add manually the TimeZone object.

The selected timezone will be available via get_option('timezone_string')

Last edited 6 years ago by romulodl (previous) (diff)

#15 @Rarst
6 years ago

  • Milestone Awaiting Review deleted
  • Resolution set to wontfix
  • Status changed from new to closed

As of right now the situation is the following:

  1. WordPress does set PHP timezone to UTC during core load.
  2. A lot of core's Date/Time code relies on that to be the case for correct operation.
  3. We should make that code operate correctly regardless of PHP time zone (changeable by plugins and such). I am on it.
  4. Regardless of those improvements we can never stop setting it to UTC, since it would be a massive backwards compatibility break.

#16 follow-up: @Otto42
5 years ago

Regardless of those improvements we can never stop setting it to UTC, since it would be a massive backwards compatibility break.

I wouldn't go that far. We can do it eventually, but it has to be in stages, across a few releases. Unit tests likely come first. Then we have a basic test suite to ensure nothing horrible happens by mistake.

#17 in reply to: ↑ 16 @Rarst
5 years ago

Replying to Otto42:

I wouldn't go that far. We can do it eventually, but it has to be in stages, across a few releases. Unit tests likely come first. Then we have a basic test suite to ensure nothing horrible happens by mistake.

If extension code relies (knowingly or unknowingly) on UTC time zone being set it will fail if core stops doing that.

As above we can, should, and will make core operate independently of this assumption. But even after that we can't stop setting UTC without breaking code out there.

WordPress should never have done this in first place, but since it did it is stuck with it forever now.

#18 @Otto42
5 years ago

No, if extension code relies on that, then we will have to break it. Eventually.

There will need to be a backward compat break at some point. But it is not an immediate concern.

#19 @kpersson
5 years ago

I know this post is closed, I am only adding my workaround for this, in case someone googles it like I did. I added this to my themes functions.php it will return a timestamp anywere in wordpress that I call this php function, or you can use the shortcode. Then any reference to a date or time, I refer to the timestamp and it is correct for the timezone you set.

function displaydate(){

$date = new DateTime(null, new DateTimeZone('America/Los_Angeles'));
return ($date->getTimestamp() + $date->getOffset());

}
add_shortcode( 'date', 'displaydate' );

#20 @Otto42
5 years ago

I think this ticket is eligible to be reopened as an enhancement, if somebody decides to give it a go. It's a big project, but the speed benefits are indeed worthwhile, and just adding the unit testing is valuable.

When I wrote the timezone code originally, it was a hack to support PHP 5 when PHP 4 was still around. This is a reasonable thing to tackle nowadays. We're moving on to PHP 7.

We can do this, it's just a lot of effort to do it right.

#21 follow-up: @Rarst
5 years ago

I am working on this, on part about untangling core for needing the default time zone set, see #44491 for example.

However there is no status change to backwards compatibility aspect. We cannot stop setting default timezone without breaking third party code in the wild.

At the moment I still disagree that we "need" a backwards compatibility break in and it is surprising to even hear such in context of WP's backwards compat commitments.

#22 in reply to: ↑ 21 @Otto42
5 years ago

Replying to Rarst:

However there is no status change to backwards compatibility aspect. We cannot stop setting default timezone without breaking third party code in the wild.

At the moment I still disagree that we "need" a backwards compatibility break in and it is surprising to even hear such in context of WP's backwards compat commitments.

I think we do at least need to consider it, and I don't see that as a radical position to take.

WordPress is moving towards PHP 7. That is in sight. Modernizing this handling is a big step, and backwards compatibility breaks at the same time is a reasonable thing to think about. Especially if it improves the overall benefits going forward.

Yes, we have had a huge commitment to back compat. But we're charging to 5.6 only in the next release and to 7.0 only this year. Is there any potential better time to pull off that band-aid than then?

#23 @Starbuck
20 months ago

This is a temporary workaround for a specific scenario:

  • The site admin wants debug.log for a specific site to show a specific timezone.
  • The setting does not apply to any other application or site on the system.

I'm adding this comment because I have the above scenario, looked for a solution, and was led to this ticket through comments in other venues. While this isn't suitable as-is as a core solution to the challenge, it might help folks who are looking for a simple solution or a tip for how to start creating their own.


Create a new pre-setup-functions.php file in the WP root folder (perms 755). Move later after you see how this works.

<?php

set_error_handler(function ($err_severity, $err_msg, $err_file, $err_line, array $err_context) {
    // error was suppressed with the @-operator
    if (0 === error_reporting()) {
        return false;
    }
    date_default_timezone_set('America/Los_Angeles'); // change, read from file or DB, ...
    throw new ErrorException($err_msg, 0, $err_severity, $err_file, $err_line);
});

In wp-config.php, after "/* Add any custom values...", add this line (modify as required):

include_once __DIR__ . '/site-specific-functions.php';

The result is debug.log with the time in the desired timezone.

The message shows "Uncaught ErrorException". As one solution for showing the correct exception type, see this helpful comment. Replace the 'throw' line with the 'switch' block, and add all the class definitions. This results in "Uncaught UserNoticeException", for example for E_USER_NOTICE.

Pros:

  • Easy for anyone to implement, one line in wp-config and one file to copy/paste.
  • No change to core required.
  • Implemented way before functions.php, to handle errors early in the life-cycle.
  • Shows callstack, not available by default from unhandled trigger_error().
  • Versatile : Can be extended and copied into multiple sites to show each preferred timezone.

Cons:

  • May interfere with plugin-specific handling or (doesn't look to me like there are issues) with other core calls to set_error_handler.
  • If another error handler is active, it will not use the specified timezone, so debug.log may have a combination of UTC and local zone. Inelegant solutions are available for this.
  • Not-yet peer-reviewed for other possible concerns.
  • Enhancements required to get more trace frames, avoid cutting off trace text, other details.
Note: See TracTickets for help on using tickets.