WordPress breaks time (and how to fix it)

A presentation at WordCamp Retreat Soltau 2018 in May 2018 in 29614 Soltau, Germany by Andrey Savchenko

Slide 1

Slide 1

WP breaks time & how to fix it

Slide 2

Slide 2

2 Andrey “Rarst” Savchenko — Rarst.net — WP contractor — 1 Gs old — 310 Ms WP

Slide 3

Slide 3

3 Had you ever... changed WP time zone? Settings → General → Timezone

Slide 4

Slide 4

4 Had you ever... output localized time? date_i18n() , the_date() , etc.

Slide 5

Slide 5

5 A very brief history of time 1. solar time 2. city time 3. rail time 4. time zones 5. Universal Coordinated Time

Slide 6

Slide 6

6 Unix time 1 525 435 200 seconds since 1970–01–01 00:00:00 UTC

Slide 7

Slide 7

7 Unix time — consistent — storage–friendly — comparison–friendly — not human readable

Slide 8

Slide 8

8 PHP DateTime $dt = new DateTime(); echo $dt->getTimestamp(); 1525435200 echo $dt->format( DATE_RFC3339 ); 2018-05-04T14:00:00+02:00 echo $dt->getTimezone()->getName(); Europe/Berlin php.net/datetime

Slide 9

Slide 9

9 PHP DateTime — UTC–based — formatting — parsing — primarily English php.net/datetime

Slide 10

Slide 10

10 phpdatebook.com

Slide 11

Slide 11

Slide 12

Slide 12

12 WP time — PHP 4 code — partial PHP 5 retrofit — custom time zone logic — custom internationalization

Slide 13

Slide 13

13 WpDateTime — extends PHP DateTime classes — works around WP bugs — parses WP data github.com/Rarst/wpdatetime

Slide 14

Slide 14

14 WP time zone options setting gmt_offset timezone_string Berlin 2 'Europe/Berlin' UTC+2 '2' ''

Slide 15

Slide 15

15 WP time zone in all cases 1. get timezone_string 2. if empty — get gmt_offset 3. convert the offset number to ±00:00 (PHP 5.5+)

Slide 16

Slide 16

16 WpDateTimeZone to parse time zone $timezone = WpDateTimeZone::getWpTimezone(); setting $timezone->getName() Berlin 'Europe/Berlin' UTC+2 '+02:00'

Slide 17

Slide 17

17 date_i18n() DateTime date() date_i18n() language English English WP locale time zone arbitrary current PHP timezone_string input arbitrary timestamp “WP timestamp” formats date() date() date(), incomplete developer.wordpress.org/reference/functions/date_i18n

Slide 18

Slide 18

18 date_i18n() logic echo date_i18n( 'j. F Y, G.i \U\h\r T' );

  1. j. F Y, G.i \U\h\r T 2. j. \M\a\i Y, G.i \U\h\r T — locale 3. j. \M\a\i Y, G.i \U\h\r \C\E\S\T — zone 4. 4. Mai 2018, 14.00 Uhr CEST — date()

Slide 19

Slide 19

19 date_i18n() fails w/ gmt_offset WP set to Berlin time zone: echo date_i18n( DATE_RFC3339 ); 2018-05-04T14:00:00+02:00 WP set to UTC+2 time zone: echo date_i18n( DATE_RFC3339 ); 2018-05-04T14:00:00+00:00 #34835

Slide 20

Slide 20

20 date_i18n() fails w/ Unix timestamp echo date( DATE_RFC3339, time() ); 2018-05-04T12:00:00+00:00 echo date_i18n( DATE_RFC3339, time() ); 2018-05-04T12:00:00+02:00 #38771

Slide 21

Slide 21

21 date_i18n() fails w/ some formats echo date_i18n( DATE_RFC3339 ); // === 'Y-m-d\TH:i:sP' 2018-05-04T14:00:00+02:00 echo date_i18n( 'c' ); // === 'c' 2018-05-04T14:00:00+00:00 echo date_i18n( DATE_RFC2822 ); // === 'D, d M Y H:i:s O' Fr, 04 Mai 2018 14:00:00 +0200 echo date_i18n( 'r' ); // === 'r' Fri, 04 May 2018 14:00:00 +0000 #20973

Slide 22

Slide 22

22 WpDateTime to wrap date_i18n() WP set to UTC+2 time zone: $time = new WpDateTime( '@' . time() ); $timezone = WpDateTimeZone::getWpTimezone(); $time = $time->setTimezone( $timezone ); echo $time->formatI18n( 'c' ); 2018-05-04T14:00:00+02:00 echo $time->formatI18n( 'r' ); Fr, 04 Mai 2018 14:00:00 +0200

Slide 23

Slide 23

23 post_date post_date post_date_gmt post_title 2018-05-04 13:00:00 2018-05-04 12:00:00 A post made in London WP set to London time zone: the_date( DATE_RFC3339 ); 2018-05-04T13:00:00+01:00

Slide 24

Slide 24

24 post_date fails w/ changed time zone post_date post_date_gmt post_title 2018-05-04 13:00:00 2018-05-04 12:00:00 A post made in London WP set to Berlin time zone: the_date( DATE_RFC3339 ); 2018-05-04T13:00:00+02:00 #38774

Slide 25

Slide 25

25 post_date in all cases 1. get post_date_gmt 2. if empty — get post_date 3. get WP time zone 4. adjust post_date[_gmt] with the time zone 5. convert to desired format

Slide 26

Slide 26

26 WpDateTime to parse post date post_date post_date_gmt post_title 2018-05-04 13:00:00 2018-05-04 12:00:00 A post made in London WP set to Berlin time zone: $time = WpDateTime::createFromPost( get_post() ); echo $time->formatI18n( DATE_RFC3339 ); 2018-05-04T14:00:00+02:00

Slide 27

Slide 27

27 Takeaways — WP gets time storage and output wrong — read and use UTC time — write timestamp, UTC, or RFC 3339 — use DateTime to operate time — use WpDateTime to bridge WP and PHP

Slide 28

Slide 28

28 Thank you for your time! Questions? twitter.com/Rarst Rarst.net/slides/time

Slide 29

Slide 29

29 Image credits — Photo by Adina Voicu CC0 — Photo by Eugene Shelestov CC0