WordPress.org

Make WordPress Core

Ticket #39256: 39256.2.diff

File 39256.2.diff, 10.1 KB (added by jnylen0, 3 years ago)

Fix issues with timezone conversion; add a whole bunch of tests

  • src/wp-includes/rest-api.php

    diff --git a/src/wp-includes/rest-api.php b/src/wp-includes/rest-api.php
    index ad9bf62..75f2a10 100644
    a b function rest_parse_date( $date, $force_utc = false ) { 
    768768}
    769769
    770770/**
    771  * Retrieves a local date with its GMT equivalent, in MySQL datetime format.
     771 * Parses a date into both its local and GMT equivalent, in MySQL datetime format.
    772772 *
    773773 * @since 4.4.0
    774774 *
    775775 * @see rest_parse_date()
    776776 *
    777  * @param string $date      RFC3339 timestamp.
    778  * @param bool   $force_utc Whether a UTC timestamp should be forced. Default false.
     777 * @param string $date   RFC3339 timestamp.
     778 * @param bool   $is_utc Whether the provided date should be interpreted as UTC.
    779779 * @return array|null Local and UTC datetime strings, in MySQL datetime format (Y-m-d H:i:s),
    780780 *                    null on failure.
    781781 */
    782 function rest_get_date_with_gmt( $date, $force_utc = false ) {
    783         $date = rest_parse_date( $date, $force_utc );
     782function rest_get_date_with_gmt( $date, $is_utc = false ) {
     783        // Whether or not the original date actually has a timezone string
     784        // changes the way we need to do timezone conversion.  Store this info
     785        // before parsing the date, and use it later.
     786        $has_timezone = preg_match( '#(Z|[+-]\d{2}(:\d{2})?)$#', $date );
     787
     788        $date = rest_parse_date( $date );
    784789
    785790        if ( empty( $date ) ) {
    786791                return null;
    787792        }
    788793
    789         $utc = date( 'Y-m-d H:i:s', $date );
    790         $local = get_date_from_gmt( $utc );
     794        // At this point $date could either be a local date (if we were passed a
     795        // *local* date without a timezone offset) or a UTC date (otherwise).
     796        // Timezone conversion needs to be handled differently between these two
     797        // cases.
     798        if ( ! $is_utc && ! $has_timezone ) {
     799                $local = date( 'Y-m-d H:i:s', $date );
     800                $utc = get_gmt_from_date( $local );
     801        } else {
     802                $utc = date( 'Y-m-d H:i:s', $date );
     803                $local = get_date_from_gmt( $utc );
     804        }
    791805
    792806        return array( $local, $utc );
    793807}
  • tests/phpunit/tests/rest-api.php

    diff --git a/tests/phpunit/tests/rest-api.php b/tests/phpunit/tests/rest-api.php
    index 69bf9a3..f303a01 100644
    a b class Tests_REST_API extends WP_UnitTestCase { 
    383383                $this->assertEquals( $valid, wp_check_jsonp_callback( $callback ) );
    384384        }
    385385
     386        public function rest_date_provider() {
     387                return array(
     388                        // Valid dates with timezones
     389                        array( '2017-01-16T11:30:00-05:00', gmmktime( 11, 30,  0,  1, 16, 2017 ) + 5 * HOUR_IN_SECONDS ),
     390                        array( '2017-01-16T11:30:00-05:30', gmmktime( 11, 30,  0,  1, 16, 2017 ) + 5.5 * HOUR_IN_SECONDS ),
     391                        array( '2017-01-16T11:30:00-05'   , gmmktime( 11, 30,  0,  1, 16, 2017 ) + 5 * HOUR_IN_SECONDS ),
     392                        array( '2017-01-16T11:30:00+05'   , gmmktime( 11, 30,  0,  1, 16, 2017 ) - 5 * HOUR_IN_SECONDS ),
     393                        array( '2017-01-16T11:30:00-00'   , gmmktime( 11, 30,  0,  1, 16, 2017 ) ),
     394                        array( '2017-01-16T11:30:00+00'   , gmmktime( 11, 30,  0,  1, 16, 2017 ) ),
     395                        array( '2017-01-16T11:30:00Z'     , gmmktime( 11, 30,  0,  1, 16, 2017 ) ),
     396
     397                        // Valid dates without timezones
     398                        array( '2017-01-16T11:30:00'      , gmmktime( 11, 30,  0,  1, 16, 2017 ) ),
     399
     400                        // Invalid dates (TODO: support parsing partial dates as ranges, see #38641)
     401                        array( '2017-01-16T11:30:00-5', false ),
     402                        array( '2017-01-16T11:30', false ),
     403                        array( '2017-01-16T11', false ),
     404                        array( '2017-01-16T', false ),
     405                        array( '2017-01-16', false ),
     406                        array( '2017-01', false ),
     407                        array( '2017', false ),
     408                );
     409        }
     410
     411        /**
     412         * @dataProvider rest_date_provider
     413         */
     414        public function test_rest_parse_date( $string, $value ) {
     415                $this->assertEquals( $value, rest_parse_date( $string ) );
     416        }
     417
     418        public function rest_date_force_utc_provider() {
     419                return array(
     420                        // Valid dates with timezones
     421                        array( '2017-01-16T11:30:00-05:00', gmmktime( 11, 30,  0,  1, 16, 2017 ) ),
     422                        array( '2017-01-16T11:30:00-05:30', gmmktime( 11, 30,  0,  1, 16, 2017 ) ),
     423                        array( '2017-01-16T11:30:00-05'   , gmmktime( 11, 30,  0,  1, 16, 2017 ) ),
     424                        array( '2017-01-16T11:30:00+05'   , gmmktime( 11, 30,  0,  1, 16, 2017 ) ),
     425                        array( '2017-01-16T11:30:00-00'   , gmmktime( 11, 30,  0,  1, 16, 2017 ) ),
     426                        array( '2017-01-16T11:30:00+00'   , gmmktime( 11, 30,  0,  1, 16, 2017 ) ),
     427                        array( '2017-01-16T11:30:00Z'     , gmmktime( 11, 30,  0,  1, 16, 2017 ) ),
     428
     429                        // Valid dates without timezones
     430                        array( '2017-01-16T11:30:00'      , gmmktime( 11, 30,  0,  1, 16, 2017 ) ),
     431
     432                        // Invalid dates (TODO: support parsing partial dates as ranges, see #38641)
     433                        array( '2017-01-16T11:30:00-5', false ),
     434                        array( '2017-01-16T11:30', false ),
     435                        array( '2017-01-16T11', false ),
     436                        array( '2017-01-16T', false ),
     437                        array( '2017-01-16', false ),
     438                        array( '2017-01', false ),
     439                        array( '2017', false ),
     440                );
     441        }
     442
     443        /**
     444         * @dataProvider rest_date_force_utc_provider
     445         */
     446        public function test_rest_parse_date_force_utc( $string, $value ) {
     447                $this->assertEquals( $value, rest_parse_date( $string, true ) );
     448        }
    386449}
  • tests/phpunit/tests/rest-api/rest-posts-controller.php

    diff --git a/tests/phpunit/tests/rest-api/rest-posts-controller.php b/tests/phpunit/tests/rest-api/rest-posts-controller.php
    index 67d4b0e..8dad1a5 100644
    a b class WP_Test_REST_Posts_Controller extends WP_Test_REST_Post_Type_Controller_Te 
    10931093                $this->check_create_post_response( $response );
    10941094        }
    10951095
     1096        public function post_dates_provider() {
     1097                $all_statuses = array(
     1098                        'draft',
     1099                        'publish',
     1100                        'future',
     1101                        'pending',
     1102                        'private',
     1103                );
     1104
     1105                $cases_short = array(
     1106                        'set date without timezone' => array(
     1107                                'statuses' => $all_statuses,
     1108                                'params'   => array(
     1109                                        'timezone_string' => 'America/New_York',
     1110                                        'date'            => '2016-12-12T14:00:00',
     1111                                ),
     1112                                'results' => array(
     1113                                        'date'            => '2016-12-12T14:00:00',
     1114                                        'date_gmt'        => '2016-12-12T19:00:00',
     1115                                ),
     1116                        ),
     1117                        'set date_gmt without timezone' => array(
     1118                                'statuses' => $all_statuses,
     1119                                'params'   => array(
     1120                                        'timezone_string' => 'America/New_York',
     1121                                        'date_gmt'        => '2016-12-12T19:00:00',
     1122                                ),
     1123                                'results' => array(
     1124                                        'date'            => '2016-12-12T14:00:00',
     1125                                        'date_gmt'        => '2016-12-12T19:00:00',
     1126                                ),
     1127                        ),
     1128                        'set date with timezone' => array(
     1129                                'statuses' => array( 'draft', 'publish' ),
     1130                                'params'   => array(
     1131                                        'timezone_string' => 'America/New_York',
     1132                                        'date'            => '2016-12-12T18:00:00-01:00',
     1133                                ),
     1134                                'results' => array(
     1135                                        'date'            => '2016-12-12T14:00:00',
     1136                                        'date_gmt'        => '2016-12-12T19:00:00',
     1137                                ),
     1138                        ),
     1139                        'set date_gmt with timezone' => array(
     1140                                'statuses' => array( 'draft', 'publish' ),
     1141                                'params'   => array(
     1142                                        'timezone_string' => 'America/New_York',
     1143                                        'date_gmt'        => '2016-12-12T18:00:00-01:00',
     1144                                ),
     1145                                'results' => array(
     1146                                        'date'            => '2016-12-12T14:00:00',
     1147                                        'date_gmt'        => '2016-12-12T19:00:00',
     1148                                ),
     1149                        ),
     1150                );
     1151
     1152                $cases = array();
     1153                foreach ( $cases_short as $description => $case ) {
     1154                        foreach ( $case['statuses'] as $status ) {
     1155                                $cases[ $description . ', status=' . $status ] = array(
     1156                                        $status,
     1157                                        $case['params'],
     1158                                        $case['results'],
     1159                                );
     1160                        }
     1161                }
     1162
     1163                return $cases;
     1164        }
     1165
     1166        /**
     1167         * @dataProvider post_dates_provider
     1168         */
     1169        public function test_create_post_date( $status, $params, $results ) {
     1170                wp_set_current_user( self::$editor_id );
     1171                update_option( 'timezone_string', $params['timezone_string'] );
     1172
     1173                $request = new WP_REST_Request( 'POST', '/wp/v2/posts' );
     1174                $request->set_param( 'status', $status );
     1175                $request->set_param( 'title', 'not empty' );
     1176                if ( isset( $params['date'] ) ) {
     1177                        $request->set_param( 'date', $params['date'] );
     1178                }
     1179                if ( isset( $params['date_gmt'] ) ) {
     1180                        $request->set_param( 'date_gmt', $params['date_gmt'] );
     1181                }
     1182                $response = $this->server->dispatch( $request );
     1183
     1184                update_option( 'timezone_string', '' );
     1185
     1186                $this->assertEquals( 201, $response->get_status() );
     1187                $data = $response->get_data();
     1188                $post = get_post( $data['id'] );
     1189
     1190                $this->assertEquals( $results['date'], $data['date'] );
     1191                $post_date = str_replace( 'T', ' ', $results['date'] );
     1192                $this->assertEquals( $post_date, $post->post_date );
     1193
     1194                $this->assertEquals( $results['date_gmt'], $data['date_gmt'] );
     1195                // TODO expect null here for drafts (see https://core.trac.wordpress.org/ticket/5698#comment:14)
     1196                $post_date_gmt = str_replace( 'T', ' ', $results['date_gmt'] );
     1197                $this->assertEquals( $post_date_gmt, $post->post_date_gmt );
     1198        }
     1199
    10961200        /**
    10971201         * @ticket 38698
    10981202         */
    class WP_Test_REST_Posts_Controller extends WP_Test_REST_Post_Type_Controller_Te 
    19262030                $this->assertEquals( date( 'Y-m-d', strtotime( $expected_modified ) ), date( 'Y-m-d', strtotime( $new_post->post_modified ) ) );
    19272031        }
    19282032
     2033        /**
     2034         * @dataProvider post_dates_provider
     2035         */
     2036        public function test_update_post_date( $status, $params, $results ) {
     2037                wp_set_current_user( self::$editor_id );
     2038                update_option( 'timezone_string', $params['timezone_string'] );
     2039
     2040                $post_id = $this->factory->post->create( array( 'post_status' => $status ) );
     2041
     2042                $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $post_id ) );
     2043                if ( isset( $params['date'] ) ) {
     2044                        $request->set_param( 'date', $params['date'] );
     2045                }
     2046                if ( isset( $params['date_gmt'] ) ) {
     2047                        $request->set_param( 'date_gmt', $params['date_gmt'] );
     2048                }
     2049                $response = $this->server->dispatch( $request );
     2050
     2051                update_option( 'timezone_string', '' );
     2052
     2053                $this->assertEquals( 200, $response->get_status() );
     2054                $data = $response->get_data();
     2055                $post = get_post( $data['id'] );
     2056
     2057                $this->assertEquals( $results['date'], $data['date'] );
     2058                $post_date = str_replace( 'T', ' ', $results['date'] );
     2059                $this->assertEquals( $post_date, $post->post_date );
     2060
     2061                $this->assertEquals( $results['date_gmt'], $data['date_gmt'] );
     2062                // TODO expect null here for drafts (see https://core.trac.wordpress.org/ticket/5698#comment:14)
     2063                $post_date_gmt = str_replace( 'T', ' ', $results['date_gmt'] );
     2064                $this->assertEquals( $post_date_gmt, $post->post_date_gmt );
     2065        }
     2066
    19292067        public function test_update_post_with_invalid_date() {
    19302068                wp_set_current_user( self::$editor_id );
    19312069