Index: tests/phpunit/tests/date/mysql2date.php
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- tests/phpunit/tests/date/mysql2date.php	(date 1567009917964)
+++ tests/phpunit/tests/date/mysql2date.php	(date 1567009917964)
@@ -0,0 +1,60 @@
+<?php
+
+/**
+ * @group date
+ * @group datetime
+ */
+class Tests_Date_mysql2date extends WP_UnitTestCase {
+
+	function tearDown() {
+
+		date_default_timezone_set( 'UTC' );
+
+		parent::tearDown();
+	}
+
+	function test_mysql2date_should_format_time() {
+		$timezone = 'Europe/Kiev';
+		update_option( 'timezone_string', $timezone );
+		$datetime = new DateTime( 'now', new DateTimeZone( $timezone ) );
+		$rfc3339  = $datetime->format( DATE_RFC3339 );
+		$mysql    = $datetime->format( 'Y-m-d H:i:s' );
+
+		$this->assertEquals( $rfc3339, mysql2date( DATE_RFC3339, $mysql ) );
+		$this->assertEquals( $rfc3339, mysql2date( DATE_RFC3339, $mysql, false ) );
+	}
+
+	function test_mysql2date_should_format_time_with_changed_time_zone() {
+		$timezone = 'Europe/Kiev';
+		date_default_timezone_set( $timezone );
+		update_option( 'timezone_string', $timezone );
+		$datetime = new DateTime( 'now', new DateTimeZone( $timezone ) );
+		$rfc3339  = $datetime->format( DATE_RFC3339 );
+		$mysql    = $datetime->format( 'Y-m-d H:i:s' );
+
+		$this->assertEquals( $rfc3339, mysql2date( DATE_RFC3339, $mysql ) );
+		$this->assertEquals( $rfc3339, mysql2date( DATE_RFC3339, $mysql, false ) );
+	}
+
+	function test_mysql2date_should_return_wp_timestamp() {
+		$timezone = 'Europe/Kiev';
+		update_option( 'timezone_string', $timezone );
+		$datetime     = new DateTime( 'now', new DateTimeZone( $timezone ) );
+		$wp_timestamp = $datetime->getTimestamp() + $datetime->getOffset();
+		$mysql        = $datetime->format( 'Y-m-d H:i:s' );
+
+		$this->assertEquals( $wp_timestamp, mysql2date( 'U', $mysql, false ) );
+		$this->assertEquals( $wp_timestamp, mysql2date( 'G', $mysql, false ) );
+	}
+
+	function test_mysql2date_should_return_unix_timestamp_for_gmt_time() {
+		$timezone = 'Europe/Kiev';
+		update_option( 'timezone_string', $timezone );
+		$datetime  = new DateTime( 'now', new DateTimeZone( 'UTC' ) );
+		$timestamp = $datetime->getTimestamp();
+		$mysql     = $datetime->format( 'Y-m-d H:i:s' );
+
+		$this->assertEquals( $timestamp, mysql2date( 'U', $mysql, false ) );
+		$this->assertEquals( $timestamp, mysql2date( 'G', $mysql, false ) );
+	}
+}
Index: src/wp-includes/functions.php
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- src/wp-includes/functions.php	(date 1566961929000)
+++ src/wp-includes/functions.php	(date 1567010452762)
@@ -8,41 +8,48 @@
 require( ABSPATH . WPINC . '/option.php' );
 
 /**
- * Convert given date string into a different format.
+ * Convert given MySQL date string into a different format.
+ *
+ * `$format` should be a PHP date format string.
  *
- * $format should be either a PHP date format string, e.g. 'U' for a Unix
- * timestamp, or 'G' for a Unix timestamp assuming that $date is GMT.
+ * 'U' and 'G' formats will return a sum of timestamp with time zone offset.
+ *
+ * `$date` is expected to be local time in MySQL format (`Y-m-d H:i:s`).
+ *
+ * Historically UTC time could be passed to function to produce Unix timestamp.
  *
  * If $translate is true then the given date and format string will
- * be passed to date_i18n() for translation.
+ * be passed to `wp_date()` for translation.
  *
  * @since 0.71
  *
  * @param string $format    Format of the date to return.
  * @param string $date      Date string to convert.
  * @param bool   $translate Whether the return date should be translated. Default true.
- * @return string|int|bool Formatted date string or Unix timestamp. False if $date is empty.
+ *
+ * @return string|int|false Formatted date string or sum of Unix timestamp and time zone offset. False on failure.
  */
 function mysql2date( $format, $date, $translate = true ) {
 	if ( empty( $date ) ) {
 		return false;
 	}
 
-	if ( 'G' == $format ) {
-		return strtotime( $date . ' +0000' );
-	}
+	$datetime = date_create( $date, wp_timezone() );
 
-	$i = strtotime( $date );
+	if ( false === $datetime ) {
+		return false;
+	}
 
-	if ( 'U' == $format ) {
-		return $i;
+	// Returns a sum of timestamp with time zone offset. Ideally should never be used.
+	if ( 'G' === $format || 'U' === $format ) {
+		return $datetime->getTimestamp() + $datetime->getOffset();
 	}
 
 	if ( $translate ) {
-		return date_i18n( $format, $i );
-	} else {
-		return gmdate( $format, $i );
+		return wp_date( $format, $datetime->getTimestamp() );
 	}
+
+	return $datetime->format( $format );
 }
 
 /**
