diff --git a/core/lib/Drupal/Component/Datetime/DateTimePlus.php b/core/lib/Drupal/Component/Datetime/DateTimePlus.php
index 01c4647013..a4c71818e8 100644
--- a/core/lib/Drupal/Component/Datetime/DateTimePlus.php
+++ b/core/lib/Drupal/Component/Datetime/DateTimePlus.php
@@ -200,8 +200,21 @@ public static function createFromTimestamp($timestamp, $timezone = NULL, $settin
     if (!is_numeric($timestamp)) {
       throw new \InvalidArgumentException('The timestamp must be numeric.');
     }
-    $datetime = new static('', $timezone, $settings);
-    $datetime->setTimestamp($timestamp);
+
+    // Because of an upstream bug, @see https://bugs.php.net/bug.php?id=77103,
+    // we have to use numeric Unix timestamp. However, when you create a
+    // \DateTime using the '@' format, the $timezone parameter is ignore. So,
+    // we have to explicitly set the time zone afterwards.
+    $datetime = new static('@' . $timestamp, NULL, $settings);
+    if ($timezone !== NULL) {
+      if (is_string($timezone)) {
+        $datetime->setTimezone(new \DateTimeZone($timezone));
+      }
+      else {
+        $datetime->setTimezone($timezone);
+      }
+    }
+
     return $datetime;
   }
 
diff --git a/core/tests/Drupal/KernelTests/Core/Datetime/DateFormatterTest.php b/core/tests/Drupal/KernelTests/Core/Datetime/DateFormatterTest.php
index 8f87a6de9b..be340d9d6b 100644
--- a/core/tests/Drupal/KernelTests/Core/Datetime/DateFormatterTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Datetime/DateFormatterTest.php
@@ -110,4 +110,26 @@ public function testFormat() {
     $this->assertSame('<em>2007</em>', $formatter->format($timestamp, 'custom', '\<\e\m\>Y\<\/\e\m\>'), 'Em tags are not removed from dates.');
   }
 
+  /**
+   * Tests DateFormatter::format() when DST.
+   *
+   * @covers ::format
+   */
+  public function testFormatDST() {
+    // The 'America/Vancouver' time zone changed from DST to standard time at
+    // 2:00 AM, Nov 1, 2020 (the GMT offset is changed to UTC/GMT -8 hours).
+    $time = strtotime('2020-11-01T06:15:00+00:00');
+    $timezone = "America/Vancouver";
+    $date_formatter = \Drupal::service('date.formatter');
+    for ($i = 0; $i < 5; $i++) {
+      $timestamp = $time + $i * 3600;
+      $date = new \DateTime();
+      $date->setTimestamp($timestamp);
+      $date->setTimezone(new \DateTimeZone($timezone));
+
+      $this->assertEquals($date->format('c'), $date_formatter->format($timestamp, 'custom', 'c', $timezone));
+      $this->assertEquals($date->format('I'), $date_formatter->format($timestamp, 'custom', 'I', $timezone));
+    }
+  }
+
 }
diff --git a/core/tests/Drupal/Tests/Component/Datetime/DateTimePlusTest.php b/core/tests/Drupal/Tests/Component/Datetime/DateTimePlusTest.php
index cde8dacad8..3070e6eb8b 100644
--- a/core/tests/Drupal/Tests/Component/Datetime/DateTimePlusTest.php
+++ b/core/tests/Drupal/Tests/Component/Datetime/DateTimePlusTest.php
@@ -564,6 +564,24 @@ public function providerTestTimestamp() {
           'expected_offset' => 0,
         ],
       ],
+      // Create a date using the timestamp during a DST transition
+      [
+        'input' => 1604222100,
+        'initial' => [
+          'timezone' => 'America/Vancouver',
+          'format' => 'c I',
+          'expected_date' => '2020-11-01T01:15:00-08:00 0',
+          'expected_timezone' => 'America/Vancouver',
+          'expected_offset' => '-28800',
+        ],
+        'transform' => [
+          'timezone' => 'UTC',
+          'format' => 'c',
+          'expected_date' => '2020-11-01T09:15:00+00:00',
+          'expected_timezone' => 'UTC',
+          'expected_offset' => 0,
+        ],
+      ],
     ];
   }