diff --git a/core/includes/errors.inc b/core/includes/errors.inc
index 215b6cb..1757acd 100644
--- a/core/includes/errors.inc
+++ b/core/includes/errors.inc
@@ -65,6 +65,11 @@ function _drupal_error_handler_real($error_level, $message, $filename, $line, $c
     $caller = Error::getLastCaller($backtrace);
 
     // We treat recoverable errors as fatal.
+    $recoverable = $error_level == E_RECOVERABLE_ERROR;
+    // As __toString() methods must not throw exceptions (recoverable errors)
+    // in PHP, we allow them to trigger a fatal error by emitting a user error
+    // using trigger_error().
+    $to_string = $error_level == E_USER_ERROR && substr($caller['function'], -strlen('__toString()')) == '__toString()';
     _drupal_log_error(array(
       '%type' => isset($types[$error_level]) ? $severity_msg : 'Unknown error',
       // The standard PHP error handler considers that the error messages
@@ -75,7 +80,7 @@ function _drupal_error_handler_real($error_level, $message, $filename, $line, $c
       '%line' => $caller['line'],
       'severity_level' => $severity_level,
       'backtrace' => $backtrace,
-    ), $error_level == E_RECOVERABLE_ERROR);
+    ), $recoverable || $to_string);
   }
 }
 
diff --git a/core/lib/Drupal/Component/Datetime/DateTimePlus.php b/core/lib/Drupal/Component/Datetime/DateTimePlus.php
index c3edf99..44c0e65 100644
--- a/core/lib/Drupal/Component/Datetime/DateTimePlus.php
+++ b/core/lib/Drupal/Component/Datetime/DateTimePlus.php
@@ -5,6 +5,7 @@
  * Contains \Drupal\Component\Datetime\DateTimePlus.
  */
 namespace Drupal\Component\Datetime;
+use Drupal\Component\Utility\ToStringTrait;
 
 /**
  * Wraps DateTime().
@@ -28,6 +29,8 @@
  */
 class DateTimePlus {
 
+  use ToStringTrait;
+
   const FORMAT   = 'Y-m-d H:i:s';
 
   /**
@@ -271,16 +274,12 @@ public function __construct($time = 'now', $timezone = NULL, $settings = array()
   }
 
   /**
-   * Implements __toString() for dates.
+   * Renders the timezone name.
    *
-   * The DateTime class does not implement this.
-   *
-   * @see https://bugs.php.net/bug.php?id=62911
-   * @see http://www.serverphorums.com/read.php?7,555645
+   * @return string
    */
-  public function __toString() {
-    $format = static::FORMAT;
-    return $this->format($format) . ' ' . $this->getTimeZone()->getName();
+  protected function render() {
+    return $this->format(static::FORMAT) . ' ' . $this->getTimeZone()->getName();
   }
 
   /**
diff --git a/core/lib/Drupal/Component/Utility/ToStringTrait.php b/core/lib/Drupal/Component/Utility/ToStringTrait.php
new file mode 100644
index 0000000..6742958
--- /dev/null
+++ b/core/lib/Drupal/Component/Utility/ToStringTrait.php
@@ -0,0 +1,42 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Component\Utility\toStringTrait.
+ */
+
+
+namespace Drupal\Component\Utility;
+
+/**
+ * Wraps __toString in a trait to avoid some fatals.
+ */
+trait ToStringTrait {
+
+
+  /**
+   * Implements the magic __toString() method.
+   */
+  public function __toString() {
+    try {
+      return (string) $this->render();
+    }
+    catch (\Exception $e) {
+      // User errors in __toString() methods are considered fatal in the Drupal
+      // error handler.
+      trigger_error(get_class($e) . ' thrown while calling __toString on a ' . get_class($this) . ' object in ' . $e->getFile() . ' on line ' . $e->getLine() . ': ' . $e->getMessage(), E_USER_ERROR);
+      // In case we are using another error handler that did not fatal on the
+      // E_USER_ERROR, we terminate execution. However, for test purposes allow
+      // a return value.
+      return $this->_die();
+    }
+  }
+
+  /**
+   * For test purposes, wrap die() in an overridable method.
+   */
+  protected function _die() {
+    die();
+  }
+
+}
diff --git a/core/lib/Drupal/Core/StringTranslation/TranslationWrapper.php b/core/lib/Drupal/Core/StringTranslation/TranslationWrapper.php
index dffd2cf..10e9b12 100644
--- a/core/lib/Drupal/Core/StringTranslation/TranslationWrapper.php
+++ b/core/lib/Drupal/Core/StringTranslation/TranslationWrapper.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core\StringTranslation;
 
 use Drupal\Component\Utility\SafeStringInterface;
+use Drupal\Component\Utility\ToStringTrait;
 
 /**
  * Provides a class to wrap a translatable string.
@@ -19,7 +20,9 @@
  * @see \Drupal\Core\Annotation\Translation
  */
 class TranslationWrapper implements SafeStringInterface {
+
   use StringTranslationTrait;
+  use ToStringTrait;
 
   /**
    * The string to be translated.
@@ -93,14 +96,6 @@ public function getOption($name) {
   public function getOptions() {
     return $this->options;
   }
-
-  /**
-   * Implements the magic __toString() method.
-   */
-  public function __toString() {
-    return $this->render();
-  }
-
   /**
    * Renders the object as a string.
    *
@@ -128,4 +123,5 @@ public function jsonSerialize() {
     return $this->__toString();
   }
 
+
 }
diff --git a/core/tests/Drupal/Tests/Core/StringTranslation/TranslationWrapperTest.php b/core/tests/Drupal/Tests/Core/StringTranslation/TranslationWrapperTest.php
new file mode 100644
index 0000000..3facf38
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/StringTranslation/TranslationWrapperTest.php
@@ -0,0 +1,84 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\Core\StringTranslation\TranslationWrapperTest.
+ */
+
+namespace Drupal\Tests\Core\StringTranslation;
+
+use Drupal\Core\StringTranslation\TranslationInterface;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * Tests the TranslationWrapper class.
+ *
+ * @coversDefaultClass \Drupal\Core\StringTranslation\TranslationWrapper
+ * @group StringTranslation
+ */
+class TranslationWrapperTest extends UnitTestCase {
+
+  /**
+   * The error message of the last error in the error handler.
+   *
+   * @var string
+   */
+  protected $lastErrorMessage;
+
+  /**
+   * The error number of the last error in the error handler.
+   *
+   * @var int
+   */
+  protected $lastErrorNumber;
+
+  /**
+   * Custom error handler that saves the last error.
+   *
+   * We need this custom error handler because we cannot rely on the error to
+   * exception conversion as __toString is never allowed to leak any kind of
+   * exception.
+   *
+   * @param int $error_number
+   *   The error number.
+   * @param string $error_message
+   *   The error message.
+   */
+  public function errorHandler($error_number, $error_message) {
+    $this->lastErrorNumber = $error_number;
+    $this->lastErrorMessage = $error_message;
+  }
+
+  /**
+   * Tests that errors are correctly handled when a __toString() fails.
+   *
+   * @covers ::__toString
+   */
+  public function testToString() {
+    $string = 'May I have an exception please?';
+    $text = $this->getMockBuilder('Drupal\Core\StringTranslation\TranslationWrapper')
+      ->setConstructorArgs([$string])
+      ->setMethods(['_die'])
+      ->getMock();
+    $text
+      ->expects($this->once())
+      ->method('_die')
+      ->willReturn('');
+
+    $translation = $this->prophesize(TranslationInterface::class);
+    $translation->translate($string, [], [])->will(function () {
+      throw new \Exception('Yes you may.');
+    });
+    $text->setStringTranslation($translation->reveal());
+
+    // We set a custom error handler because of https://github.com/sebastianbergmann/phpunit/issues/487
+    set_error_handler([$this, 'errorHandler']);
+    // We want this to trigger an error.
+    (string) $text;
+    restore_error_handler();
+
+    $this->assertEquals(E_USER_ERROR, $this->lastErrorNumber);
+    $this->assertRegExp('/Exception thrown while calling __toString on a .*Mock_TranslationWrapper_.* object in .*TranslationWrapperTest.php on line [0-9]+: Yes you may./', $this->lastErrorMessage);
+  }
+
+}
