diff --git a/core/lib/Drupal/Core/TypedData/Primitive.php b/core/lib/Drupal/Core/TypedData/Primitive.php
deleted file mode 100644
index 393e4c7..0000000
--- a/core/lib/Drupal/Core/TypedData/Primitive.php
+++ /dev/null
@@ -1,70 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\Core\TypedData\Primitive.
- */
-
-namespace Drupal\Core\TypedData;
-
-/**
- * Class that holds constants for all primitive data types.
- */
-final class Primitive {
-
-  /**
-   * The BOOLEAN primitive data type.
-   *
-   * @see \Drupal\Core\TypedData\Type\Boolean
-   */
-  const BOOLEAN = 1;
-
-  /**
-   * The STRING primitive data type.
-   *
-   * @see \Drupal\Core\TypedData\Type\String
-   */
-  const STRING = 2;
-
-  /**
-   * The INTEGER primitive data type.
-   *
-   * @see \Drupal\Core\TypedData\Type\Integer
-   */
-  const INTEGER = 3;
-
-  /**
-   * The FLOAT primitive data type.
-   *
-   * @see \Drupal\Core\TypedData\Type\Float
-   */
-  const FLOAT = 4;
-
-  /**
-   * The DATE primitive data type.
-   *
-   * @see \Drupal\Core\TypedData\Type\Date
-   */
-  const DATE = 5;
-
-  /**
-   * The DURATION primitive data type.
-   *
-   * @see \Drupal\Core\TypedData\Type\Duration
-   */
-  const DURATION = 6;
-
-  /**
-   * The URI primitive data type.
-   *
-   * @see \Drupal\Core\TypedData\Type\Uri
-   */
-  const URI = 7;
-
-  /**
-   * The BINARY primitive data type.
-   *
-   * @see \Drupal\Core\TypedData\Type\Binary
-   */
-  const BINARY = 8;
-}
diff --git a/core/lib/Drupal/Core/TypedData/PrimitiveBase.php b/core/lib/Drupal/Core/TypedData/PrimitiveBase.php
new file mode 100644
index 0000000..1ba9319
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/PrimitiveBase.php
@@ -0,0 +1,39 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\TypedData\PrimitiveBase.
+ */
+
+namespace Drupal\Core\TypedData;
+
+/**
+ * Base class for primitive data types.
+ */
+abstract class PrimitiveBase extends TypedData implements PrimitiveInterface {
+
+  /**
+   * The data value.
+   *
+   * @var mixed
+   */
+  protected $value;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getValue() {
+    return $this->value;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setValue($value, $notify = TRUE) {
+    // Notify the parent of any changes to be made.
+    if ($notify && isset($this->parent)) {
+      $this->parent->onChange($this->name);
+    }
+    $this->value = $value;
+  }
+}
diff --git a/core/lib/Drupal/Core/TypedData/PrimitiveInterface.php b/core/lib/Drupal/Core/TypedData/PrimitiveInterface.php
new file mode 100644
index 0000000..b2293e2
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/PrimitiveInterface.php
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\TypedData\PrimitiveInterface.
+ */
+
+namespace Drupal\Core\TypedData;
+
+/**
+ * Interface for primitive data.
+ */
+interface PrimitiveInterface {
+
+  /**
+   * Gets the primitive data value.
+   *
+   * @return mixed
+   */
+  public function getValue();
+
+  /**
+   * Sets the primitive data value.
+   *
+   * @param mixed|null $value
+   *   The value to set in the format as documented for the data type or NULL to
+   *   unset the data value.
+   */
+  public function setValue($value);
+
+}
diff --git a/core/lib/Drupal/Core/TypedData/Type/Binary.php b/core/lib/Drupal/Core/TypedData/Type/Binary.php
index f9c4985..8783a98 100644
--- a/core/lib/Drupal/Core/TypedData/Type/Binary.php
+++ b/core/lib/Drupal/Core/TypedData/Type/Binary.php
@@ -7,17 +7,13 @@
 
 namespace Drupal\Core\TypedData\Type;
 
-use Drupal\Core\TypedData\TypedData;
-use InvalidArgumentException;
+use Drupal\Core\TypedData\PrimitiveBase;
+use Drupal\Core\TypedData\Type\BinaryInterface;
 
 /**
  * The binary data type.
- *
- * The plain value of binary data is a PHP file resource, see
- * http://php.net/manual/en/language.types.resource.php. For setting the value
- * a PHP file resource or a (absolute) stream resource URI may be passed.
  */
-class Binary extends TypedData {
+class Binary extends PrimitiveBase implements BinaryInterface {
 
   /**
    * The file resource URI.
diff --git a/core/lib/Drupal/Core/TypedData/Type/BinaryInterface.php b/core/lib/Drupal/Core/TypedData/Type/BinaryInterface.php
new file mode 100644
index 0000000..6f4b487
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/Type/BinaryInterface.php
@@ -0,0 +1,21 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\TypedData\Type\BinaryInterface.
+ */
+
+namespace Drupal\Core\TypedData\Type;
+
+use Drupal\Core\TypedData\PrimitiveInterface;
+
+/**
+ * Interface for binary data.
+ *
+ * The plain value of binary data is a PHP file resource, see
+ * http://php.net/manual/en/language.types.resource.php. For setting the value
+ * a PHP file resource or a (absolute) stream resource URI may be passed.
+ */
+interface BinaryInterface extends PrimitiveInterface {
+
+}
diff --git a/core/lib/Drupal/Core/TypedData/Type/Boolean.php b/core/lib/Drupal/Core/TypedData/Type/Boolean.php
index 6c357c9..3856a9f 100644
--- a/core/lib/Drupal/Core/TypedData/Type/Boolean.php
+++ b/core/lib/Drupal/Core/TypedData/Type/Boolean.php
@@ -7,7 +7,8 @@
 
 namespace Drupal\Core\TypedData\Type;
 
-use Drupal\Core\TypedData\TypedData;
+use Drupal\Core\TypedData\PrimitiveBase;
+use Drupal\Core\TypedData\Type\BooleanInterface;
 
 /**
  * The boolean data type.
@@ -15,12 +16,6 @@
  * The plain value of a boolean is a regular PHP boolean. For setting the value
  * any PHP variable that casts to a boolean may be passed.
  */
-class Boolean extends TypedData {
+class Boolean extends PrimitiveBase implements BooleanInterface {
 
-  /**
-   * The data value.
-   *
-   * @var boolean
-   */
-  protected $value;
 }
diff --git a/core/lib/Drupal/Core/TypedData/Type/BooleanInterface.php b/core/lib/Drupal/Core/TypedData/Type/BooleanInterface.php
new file mode 100644
index 0000000..bb2b888
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/Type/BooleanInterface.php
@@ -0,0 +1,17 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\TypedData\Type\BooleanInterface.
+ */
+
+namespace Drupal\Core\TypedData\Type;
+
+use Drupal\Core\TypedData\PrimitiveInterface;
+
+/**
+ * Interface for boolean data.
+ */
+interface BooleanInterface extends PrimitiveInterface {
+
+}
diff --git a/core/lib/Drupal/Core/TypedData/Type/Date.php b/core/lib/Drupal/Core/TypedData/Type/Date.php
deleted file mode 100644
index c9b8c6b..0000000
--- a/core/lib/Drupal/Core/TypedData/Type/Date.php
+++ /dev/null
@@ -1,48 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\Core\TypedData\Type\Date.
- */
-
-namespace Drupal\Core\TypedData\Type;
-
-use Drupal\Core\Datetime\DrupalDateTime;
-use Drupal\Core\TypedData\TypedData;
-use InvalidArgumentException;
-
-/**
- * The date data type.
- *
- * The plain value of a date is an instance of the DrupalDateTime class. For
- * setting the value any value supported by the __construct() of the
- * DrupalDateTime class will work, including a DateTime object, a timestamp, a
- * string date, or an array of date parts.
- */
-class Date extends TypedData {
-
-  /**
-   * The data value.
-   *
-   * @var DateTime
-   */
-  protected $value;
-
-  /**
-   * Overrides TypedData::setValue().
-   */
-  public function setValue($value, $notify = TRUE) {
-    // Notify the parent of any changes to be made.
-    if ($notify && isset($this->parent)) {
-      $this->parent->onChange($this->name);
-    }
-    // Don't try to create a date from an empty value.
-    // It would default to the current time.
-    if (!isset($value)) {
-      $this->value = $value;
-    }
-    else {
-      $this->value = $value instanceOf DrupalDateTime ? $value : new DrupalDateTime($value);
-    }
-  }
-}
diff --git a/core/lib/Drupal/Core/TypedData/Type/DateTimeInterface.php b/core/lib/Drupal/Core/TypedData/Type/DateTimeInterface.php
new file mode 100644
index 0000000..3bbcd22
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/Type/DateTimeInterface.php
@@ -0,0 +1,33 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\TypedData\Type\DateTimeInterface.
+ */
+
+namespace Drupal\Core\TypedData\Type;
+
+use Drupal\Core\Datetime\DrupalDateTime;
+
+/**
+ * Interface for dates, optionally including a time.
+ */
+interface DateTimeInterface {
+
+  /**
+   * Returns the date time object.
+   *
+   * @return \Drupal\Core\Datetime\DrupalDateTime|null
+   *   A date object or NULL if there is no date.
+   */
+  public function getDateTime();
+
+  /**
+   * Sets the date time object.
+   *
+   * @param \Drupal\Core\Datetime\DrupalDateTime $dateTime
+   *   An instance of a date time object.
+   */
+  public function setDateTime(DrupalDateTime $dateTime);
+
+}
diff --git a/core/lib/Drupal/Core/TypedData/Type/DateTimeIso8601.php b/core/lib/Drupal/Core/TypedData/Type/DateTimeIso8601.php
new file mode 100644
index 0000000..93492f5
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/Type/DateTimeIso8601.php
@@ -0,0 +1,35 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\TypedData\Type\DateTimeIso8601.
+ */
+
+namespace Drupal\Core\TypedData\Type;
+
+use Drupal\Core\Datetime\DrupalDateTime;
+use Drupal\Core\TypedData\Type\DateTimeInterface;
+
+/**
+ * A data type for ISO 8601 date strings.
+ *
+ * The plain value of this data type is a date string in ISO 8601 format.
+ */
+class DateTimeIso8601 extends String implements DateTimeInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDateTime() {
+    if ($this->value) {
+      return new DrupalDateTime($this->value);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setDateTime(DrupalDateTime $dateTime) {
+    $this->value = $dateTime->format('c');
+  }
+}
diff --git a/core/lib/Drupal/Core/TypedData/Type/Duration.php b/core/lib/Drupal/Core/TypedData/Type/Duration.php
deleted file mode 100644
index 52ba97d..0000000
--- a/core/lib/Drupal/Core/TypedData/Type/Duration.php
+++ /dev/null
@@ -1,74 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\Core\TypedData\Type\Duration.
- */
-
-namespace Drupal\Core\TypedData\Type;
-
-use Drupal\Core\TypedData\TypedData;
-use DateInterval;
-use InvalidArgumentException;
-
-/**
- * The duration data type.
- *
- * The plain value of a duration is an instance of the DateInterval class. For
- * setting the value an instance of the DateInterval class, a ISO8601 string as
- * supported by DateInterval::__construct, or an integer in seconds may be
- * passed.
- */
-class Duration extends TypedData {
-
-  /**
-   * The data value.
-   *
-   * @var \DateInterval
-   */
-  protected $value;
-
-  /**
-   * Overrides TypedData::setValue().
-   */
-  public function setValue($value, $notify = TRUE) {
-    // Notify the parent of any changes to be made.
-    if ($notify && isset($this->parent)) {
-      $this->parent->onChange($this->name);
-    }
-    // Catch any exceptions thrown due to invalid values being passed.
-    try {
-      if ($value instanceof DateInterval || !isset($value)) {
-        $this->value = $value;
-      }
-      // Treat integer values as time spans in seconds, even if supplied as PHP
-      // string.
-      elseif ((string) (int) $value === (string) $value) {
-        $this->value = new DateInterval('PT' . $value . 'S');
-      }
-      elseif (is_string($value)) {
-        // @todo: Add support for negative intervals on top of the DateInterval
-        // constructor.
-        $this->value = new DateInterval($value);
-      }
-      else {
-        // Unknown value given.
-        $this->value = $value;
-      }
-    }
-    catch (\Exception $e) {
-      // An invalid value has been given. Setting any invalid value will let
-      // validation fail.
-      $this->value = $e;
-    }
-  }
-
-  /**
-   * Overrides TypedData::getString().
-   */
-  public function getString() {
-    // Generate an ISO 8601 formatted string as supported by
-    // DateInterval::__construct() and setValue().
-    return (string) $this->getValue()->format('%rP%yY%mM%dDT%hH%mM%sS');
-  }
-}
diff --git a/core/lib/Drupal/Core/TypedData/Type/DurationInterface.php b/core/lib/Drupal/Core/TypedData/Type/DurationInterface.php
new file mode 100644
index 0000000..a39c0d5
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/Type/DurationInterface.php
@@ -0,0 +1,33 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\TypedData\Type\BinaryInterface.
+ */
+
+namespace Drupal\Core\TypedData\Type;
+
+use Drupal\Core\TypedData\PrimitiveInterface;
+
+/**
+ * Interface for binary data.
+ */
+interface DurationInterface extends PrimitiveInterface {
+
+  /**
+   * Returns the duration.
+   *
+   * @return \DateInterval|null
+   *   A DateInterval object or NULL if there is no duration.
+   */
+  public function getDuration();
+
+  /**
+   * Sets the duration.
+   *
+   * @param \DateInterval $duration
+   *   A duration to set.
+   */
+  public function setDuration(\DateInterval $duration);
+
+}
diff --git a/core/lib/Drupal/Core/TypedData/Type/DurationIso8601.php b/core/lib/Drupal/Core/TypedData/Type/DurationIso8601.php
new file mode 100644
index 0000000..ab40178
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/Type/DurationIso8601.php
@@ -0,0 +1,37 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\TypedData\Type\DurationIso8601.
+ */
+
+namespace Drupal\Core\TypedData\Type;
+
+/**
+ * The duration ISO8601 data type.
+ *
+ * The plain value of this data type is a ISO8601 duration string.
+ */
+class DurationIso8601 extends String implements DurationInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDuration() {
+    if ($this->value) {
+      // @todo: Add support for negative intervals on top of the DateInterval
+      // constructor.
+      return new \DateInterval($this->value);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setDuration(\DateInterval $duration) {
+    // Generate an ISO 8601 formatted string as supported by
+    // DateInterval::__construct() and setValue().
+    $this->value = $duration->format('%rP%yY%mM%dDT%hH%mM%sS');
+  }
+
+}
diff --git a/core/lib/Drupal/Core/TypedData/Type/Float.php b/core/lib/Drupal/Core/TypedData/Type/Float.php
index 482cad1..9f21aa2 100644
--- a/core/lib/Drupal/Core/TypedData/Type/Float.php
+++ b/core/lib/Drupal/Core/TypedData/Type/Float.php
@@ -7,20 +7,12 @@
 
 namespace Drupal\Core\TypedData\Type;
 
-use Drupal\Core\TypedData\TypedData;
+use Drupal\Core\TypedData\PrimitiveBase;
+use Drupal\Core\TypedData\Type\FloatInterface;
 
 /**
  * The float data type.
- *
- * The plain value of a float is a regular PHP float. For setting the value
- * any PHP variable that casts to a float may be passed.
  */
-class Float extends TypedData {
+class Float extends PrimitiveBase implements FloatInterface {
 
-  /**
-   * The data value.
-   *
-   * @var float
-   */
-  protected $value;
 }
diff --git a/core/lib/Drupal/Core/TypedData/Type/FloatInterface.php b/core/lib/Drupal/Core/TypedData/Type/FloatInterface.php
new file mode 100644
index 0000000..643fcbe
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/Type/FloatInterface.php
@@ -0,0 +1,20 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\TypedData\Type\FloatInterface.
+ */
+
+namespace Drupal\Core\TypedData\Type;
+
+use Drupal\Core\TypedData\PrimitiveInterface;
+
+/**
+ * Interface for floating-point numbers.
+ *
+ * The plain value of a float is a regular PHP float. For setting the value
+ * any PHP variable that casts to a float may be passed.
+ */
+interface FloatInterface extends PrimitiveInterface {
+
+}
diff --git a/core/lib/Drupal/Core/TypedData/Type/Integer.php b/core/lib/Drupal/Core/TypedData/Type/Integer.php
index eb6336e..7eb3734 100644
--- a/core/lib/Drupal/Core/TypedData/Type/Integer.php
+++ b/core/lib/Drupal/Core/TypedData/Type/Integer.php
@@ -7,20 +7,12 @@
 
 namespace Drupal\Core\TypedData\Type;
 
-use Drupal\Core\TypedData\TypedData;
+use Drupal\Core\TypedData\PrimitiveBase;
+use Drupal\Core\TypedData\Type\IntegerInterface;
 
 /**
  * The integer data type.
- *
- * The plain value of an integer is a regular PHP integer. For setting the value
- * any PHP variable that casts to an integer may be passed.
  */
-class Integer extends TypedData {
+class Integer extends PrimitiveBase implements IntegerInterface {
 
-  /**
-   * The data value.
-   *
-   * @var integer
-   */
-  protected $value;
 }
diff --git a/core/lib/Drupal/Core/TypedData/Type/IntegerInterface.php b/core/lib/Drupal/Core/TypedData/Type/IntegerInterface.php
new file mode 100644
index 0000000..4554a30
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/Type/IntegerInterface.php
@@ -0,0 +1,20 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\TypedData\Type\IntegerInterface.
+ */
+
+namespace Drupal\Core\TypedData\Type;
+
+use Drupal\Core\TypedData\PrimitiveInterface;
+
+/**
+ * Interface for integer numbers.
+ *
+ * The plain value of an integer is a regular PHP integer. For setting the value
+ * any PHP variable that casts to an integer may be passed.
+ */
+interface IntegerInterface extends PrimitiveInterface {
+
+}
diff --git a/core/lib/Drupal/Core/TypedData/Type/String.php b/core/lib/Drupal/Core/TypedData/Type/String.php
index a9d096f..e806de4 100644
--- a/core/lib/Drupal/Core/TypedData/Type/String.php
+++ b/core/lib/Drupal/Core/TypedData/Type/String.php
@@ -7,20 +7,12 @@
 
 namespace Drupal\Core\TypedData\Type;
 
-use Drupal\Core\TypedData\TypedData;
+use Drupal\Core\TypedData\PrimitiveBase;
+use Drupal\Core\TypedData\Type\StringInterface;
 
 /**
  * The string data type.
- *
- * The plain value of a string is a regular PHP string. For setting the value
- * any PHP variable that casts to a string may be passed.
  */
-class String extends TypedData {
+class String extends PrimitiveBase implements StringInterface {
 
-  /**
-   * The data value.
-   *
-   * @var string
-   */
-  protected $value;
 }
diff --git a/core/lib/Drupal/Core/TypedData/Type/StringInterface.php b/core/lib/Drupal/Core/TypedData/Type/StringInterface.php
new file mode 100644
index 0000000..edc29bc
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/Type/StringInterface.php
@@ -0,0 +1,20 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\TypedData\Type\StringInterface.
+ */
+
+namespace Drupal\Core\TypedData\Type;
+
+use Drupal\Core\TypedData\PrimitiveInterface;
+
+/**
+ * Interface for strings.
+ *
+ * The plain value of a string is a regular PHP string. For setting the value
+ * any PHP variable that casts to a string may be passed.
+ */
+interface StringInterface extends PrimitiveInterface {
+
+}
diff --git a/core/lib/Drupal/Core/TypedData/Type/TimeSpan.php b/core/lib/Drupal/Core/TypedData/Type/TimeSpan.php
new file mode 100644
index 0000000..c70a064
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/Type/TimeSpan.php
@@ -0,0 +1,44 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\TypedData\Type\TimeSpan.
+ */
+
+namespace Drupal\Core\TypedData\Type;
+
+/**
+ * The time span data type represents durations as number of seconds.
+ *
+ * The plain value is the (integer) number of seconds. Note that time spans only
+ * map correctly to durations as long as the number of seconds does not exceed
+ * a day (there is already a difference in applying a duration of a day or 24
+ * hours due to daylight savings).
+ */
+class TimeSpan extends Integer implements DurationInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDuration() {
+    if ($this->value) {
+      return new DateInterval('PT' . $this->value . 'S');
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setDuration(\DateInterval $duration) {
+    // Note that this applies the assumption of 12 month's a 30 days and
+    // each year having 365 days. There is no accurate conversion for time spans
+    // exceeding a day.
+    $this->value = ($duration->y * 365 * 24 * 60 * 60) +
+      ($duration->m * 30 * 24 * 60 * 60) +
+      ($duration->d * 24 * 60 * 60) +
+      ($duration->h * 60 * 60) +
+      ($duration->i * 60) +
+       $duration->s;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/TypedData/Type/Timestamp.php b/core/lib/Drupal/Core/TypedData/Type/Timestamp.php
new file mode 100644
index 0000000..86ac13c
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/Type/Timestamp.php
@@ -0,0 +1,40 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\TypedData\Type\Timestamp.
+ */
+
+namespace Drupal\Core\TypedData\Type;
+
+use Drupal\Core\Datetime\DrupalDateTime;
+use Drupal\Core\TypedData\Type\DateTimeInterface;
+
+/**
+ * The timestamp data type.
+ */
+class Timestamp extends Integer implements DateTimeInterface {
+
+  /**
+   * The data value as a UNIX timestamp.
+   *
+   * @var integer
+   */
+  protected $value;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDateTime() {
+    if ($this->value) {
+      return new DrupalDateTime($this->value);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setDateTime(DrupalDateTime $dateTime) {
+    $this->value = $dateTime->format('c');
+  }
+}
diff --git a/core/lib/Drupal/Core/TypedData/Type/Uri.php b/core/lib/Drupal/Core/TypedData/Type/Uri.php
index 791dec9..e10f23c 100644
--- a/core/lib/Drupal/Core/TypedData/Type/Uri.php
+++ b/core/lib/Drupal/Core/TypedData/Type/Uri.php
@@ -7,19 +7,12 @@
 
 namespace Drupal\Core\TypedData\Type;
 
-use Drupal\Core\TypedData\TypedData;
+use Drupal\Core\TypedData\PrimitiveBase;
+use Drupal\Core\TypedData\Type\UriInterface;
 
 /**
  * The URI data type.
- *
- * The plain value of a URI is an absolute URI represented as PHP string.
  */
-class Uri extends TypedData {
+class Uri extends PrimitiveBase implements UriInterface {
 
-  /**
-   * The data value.
-   *
-   * @var string
-   */
-  protected $value;
 }
diff --git a/core/lib/Drupal/Core/TypedData/Type/UriInterface.php b/core/lib/Drupal/Core/TypedData/Type/UriInterface.php
new file mode 100644
index 0000000..1034061
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/Type/UriInterface.php
@@ -0,0 +1,19 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\TypedData\Type\UriInterface.
+ */
+
+namespace Drupal\Core\TypedData\Type;
+
+use Drupal\Core\TypedData\PrimitiveInterface;
+
+/**
+ * Interface for URIs.
+ *
+ * The plain value of a URI is an absolute URI represented as PHP string.
+ */
+interface UriInterface extends PrimitiveInterface {
+
+}
diff --git a/core/lib/Drupal/Core/TypedData/TypedDataManager.php b/core/lib/Drupal/Core/TypedData/TypedDataManager.php
index 874ca6f..0d9fce6 100644
--- a/core/lib/Drupal/Core/TypedData/TypedDataManager.php
+++ b/core/lib/Drupal/Core/TypedData/TypedDataManager.php
@@ -346,9 +346,9 @@ public function getConstraints($definition) {
     $validation_manager = $this->getValidationConstraintManager();
 
     $type_definition = $this->getDefinition($definition['type']);
-    // Auto-generate a constraint for the primitive type if we have a mapping.
-    if (isset($type_definition['primitive type'])) {
-      $constraints[] = $validation_manager->create('PrimitiveType', array('type' => $type_definition['primitive type']));
+    // Auto-generate a constraint data types implementing a primitive interface.
+    if (is_subclass_of($type_definition['class'], '\Drupal\Core\TypedData\PrimitiveInterface')) {
+      $constraints[] = $validation_manager->create('PrimitiveType', array());
     }
     // Add in constraints specified by the data type.
     if (isset($type_definition['constraints'])) {
diff --git a/core/lib/Drupal/Core/TypedData/Validation/Metadata.php b/core/lib/Drupal/Core/TypedData/Validation/Metadata.php
index 106f2d0..73bfc8b 100644
--- a/core/lib/Drupal/Core/TypedData/Validation/Metadata.php
+++ b/core/lib/Drupal/Core/TypedData/Validation/Metadata.php
@@ -91,4 +91,14 @@ public function getPropertyName() {
   public function getPropertyValue($container) {
     return $this->typedData->getValue();
   }
+
+  /**
+   * Returns the typed data object.
+   *
+   * @return \Drupal\Core\TypedData\TypedDataInterface
+   *   The typed data object.
+   */
+  public function getTypedData() {
+    return $this->typedData;
+  }
 }
diff --git a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/PrimitiveTypeConstraint.php b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/PrimitiveTypeConstraint.php
index 1a85f67..41c7fee 100644
--- a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/PrimitiveTypeConstraint.php
+++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/PrimitiveTypeConstraint.php
@@ -9,19 +9,17 @@
 
 use Drupal\Component\Annotation\Plugin;
 use Drupal\Core\Annotation\Translation;
-use Symfony\Component\Validator\Constraints\Type as SymfonyConstraint;
+use Symfony\Component\Validator\Constraint;
 
 /**
  * Supports validating all primitive types.
  *
- * @todo: Move this below the TypedData core component.
- *
  * @Plugin(
  *   id = "PrimitiveType",
  *   label = @Translation("Primitive type", context = "Validation")
  * )
  */
-class PrimitiveTypeConstraint extends SymfonyConstraint {
+class PrimitiveTypeConstraint extends Constraint {
 
-  public $message = 'This value should be of type %type.';
+  public $message = 'This value should be of the correct primitive type.';
 }
diff --git a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/PrimitiveTypeConstraintValidator.php b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/PrimitiveTypeConstraintValidator.php
index df74b1c..0547991 100644
--- a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/PrimitiveTypeConstraintValidator.php
+++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/PrimitiveTypeConstraintValidator.php
@@ -8,8 +8,14 @@
 namespace Drupal\Core\Validation\Plugin\Validation\Constraint;
 
 use DateInterval;
-use Drupal\Core\TypedData\Primitive;
-use Drupal\Core\Datetime\DrupalDateTime;
+use Drupal\Core\TypedData\Type\BinaryInterface;
+use Drupal\Core\TypedData\Type\BooleanInterface;
+use Drupal\Core\TypedData\Type\DateTimeInterface;
+use Drupal\Core\TypedData\Type\DurationInterface;
+use Drupal\Core\TypedData\Type\FloatInterface;
+use Drupal\Core\TypedData\Type\IntegerInterface;
+use Drupal\Core\TypedData\Type\StringInterface;
+use Drupal\Core\TypedData\Type\UriInterface;
 use Symfony\Component\Validator\Constraint;
 use Symfony\Component\Validator\ConstraintValidator;
 
@@ -27,42 +33,38 @@ public function validate($value, Constraint $constraint) {
       return;
     }
 
-    switch ($constraint->type) {
-      case Primitive::BINARY:
-        $valid = is_resource($value);
-        break;
-      case Primitive::BOOLEAN:
-        $valid = is_bool($value) || $value === 0 || $value === '0' || $value === 1 || $value == '1';
-        break;
-      case Primitive::DATE:
-        $valid = $value instanceOf DrupalDateTime && !$value->hasErrors();
-        break;
-      case Primitive::DURATION:
-        $valid = $value instanceof DateInterval;
-        break;
-      case Primitive::FLOAT:
-        $valid = filter_var($value, FILTER_VALIDATE_FLOAT) !== FALSE;
-        break;
-      case Primitive::INTEGER:
-        $valid = filter_var($value, FILTER_VALIDATE_INT) !== FALSE;
-        break;
-      case Primitive::STRING:
-        // PHP integers, floats or booleans are valid strings also, so we
-        // cannot use is_string() here.
-        $valid = is_scalar($value);
-        break;
-      case Primitive::URI:
-        $valid = filter_var($value, FILTER_VALIDATE_URL) ;
-        break;
-      default:
-        $valid = FALSE;
-        break;
+    $typed_data = $this->context->getMetadata()->getTypedData();
+    $valid = TRUE;
+    if ($typed_data instanceof BinaryInterface && !is_resource($value)) {
+      $valid = FALSE;
+    }
+    if ($typed_data instanceof BooleanInterface && !(is_bool($value) || $value === 0 || $value === '0' || $value === 1 || $value == '1')) {
+      $valid = FALSE;
+    }
+    if ($typed_data instanceof FloatInterface && filter_var($value, FILTER_VALIDATE_FLOAT) === FALSE) {
+      $valid = FALSE;
+    }
+    if ($typed_data instanceof IntegerInterface && filter_var($value, FILTER_VALIDATE_INT) === FALSE) {
+      $valid = FALSE;
+    }
+    if ($typed_data instanceof StringInterface && !is_scalar($value)) {
+      $valid = FALSE;
+    }
+    if ($typed_data instanceof UriInterface && filter_var($value, FILTER_VALIDATE_URL) === FALSE) {
+      $valid = FALSE;
+    }
+    // @todo: Move those to separate constraint validators.
+    if ($typed_data instanceof DateTimeInterface && $typed_data->getDateTime()->hasErrors()) {
+      $valid = FALSE;
+    }
+    if ($typed_data instanceof DurationInterface && !($typed_data->getDuration() instanceof DateInterval)) {
+      $valid = FALSE;
     }
 
     if (!$valid) {
+      // @todo: Provide a good violation message for each problem.
       $this->context->addViolation($constraint->message, array(
-        '%value' => is_object($value) ? get_class($value) : (is_array($value) ? 'Array' : (string) $value),
-        '%type'  => $constraint->type,
+        '%value' => is_object($value) ? get_class($value) : (is_array($value) ? 'Array' : (string) $value)
       ));
     }
   }
diff --git a/core/modules/datetime/lib/Drupal/datetime/Type/DateTimeItem.php b/core/modules/datetime/lib/Drupal/datetime/Type/DateTimeItem.php
index 2d14e5a..86f96bc 100644
--- a/core/modules/datetime/lib/Drupal/datetime/Type/DateTimeItem.php
+++ b/core/modules/datetime/lib/Drupal/datetime/Type/DateTimeItem.php
@@ -30,7 +30,7 @@ public function getPropertyDefinitions() {
 
     if (!isset(self::$propertyDefinitions)) {
       self::$propertyDefinitions['value'] = array(
-        'type' => 'date',
+        'type' => 'datetime',
         'label' => t('Date value'),
       );
     }
diff --git a/core/modules/system/lib/Drupal/system/Tests/TypedData/TypedDataTest.php b/core/modules/system/lib/Drupal/system/Tests/TypedData/TypedDataTest.php
index 998e22e..851a476 100644
--- a/core/modules/system/lib/Drupal/system/Tests/TypedData/TypedDataTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/TypedData/TypedDataTest.php
@@ -132,47 +132,96 @@ public function testGetAndSet() {
     $typed_data->setValue('invalid');
     $this->assertEqual($typed_data->validate()->count(), 1, 'Validation detected invalid value.');
 
-    // Date type.
-    $value = new DrupalDateTime();
-    $typed_data = $this->createTypedData(array('type' => 'date'), $value);
-    $this->assertTrue($typed_data->getValue() === $value, 'Date value was fetched.');
+    // Date Time type.
+    $value = '2014-01-01T20:00:00+00:00';
+    $typed_data = $this->createTypedData(array('type' => 'datetime_iso8601'), $value);
+    $this->assertTrue($typed_data->getValue() == $value, 'Date value was fetched.');
+    $this->assertEqual($typed_data->getValue(), $typed_data->getDateTime()->format('c'), 'Value representation of a date is ISO 8601');
     $this->assertEqual($typed_data->validate()->count(), 0);
-    $new_value = REQUEST_TIME + 1;
+    $new_value = '2014-01-02T20:00:00+00:00';
     $typed_data->setValue($new_value);
-    $this->assertTrue($typed_data->getValue()->getTimestamp() === $new_value, 'Date value was changed and set by timestamp.');
+    $this->assertTrue($typed_data->getDateTime()->format('c') === $new_value, 'Date value was changed and set by an ISO8601 date.');
     $this->assertEqual($typed_data->validate()->count(), 0);
-    $typed_data->setValue('2000-01-01');
-    $this->assertTrue($typed_data->getValue()->format('Y-m-d') == '2000-01-01', 'Date value was changed and set by date string.');
-    $this->assertTrue(is_string($typed_data->getString()), 'Date value was converted to string');
+    $this->assertTrue($typed_data->getDateTime()->format('Y-m-d') == '2014-01-02', 'Date value was changed and set by date string.');
     $this->assertEqual($typed_data->validate()->count(), 0);
     $typed_data->setValue(NULL);
-    $this->assertNull($typed_data->getValue(), 'Date wrapper is null-able.');
+    $this->assertNull($typed_data->getDateTime(), 'Date wrapper is null-able.');
     $this->assertEqual($typed_data->validate()->count(), 0);
     $typed_data->setValue('invalid');
     $this->assertEqual($typed_data->validate()->count(), 1, 'Validation detected invalid value.');
+    // Check implementation of DateTimeInterface.
+    $typed_data = $this->createTypedData(array('type' => 'datetime_iso8601'), '2014-01-01T20:00:00+00:00');
+    $this->assertTrue($typed_data->getDateTime() instanceof DrupalDateTime);
+    $typed_data->setDateTime(new DrupalDateTime('2014-01-02T20:00:00+00:00'));
+    $this->assertEqual($typed_data->getValue(), '2014-01-02T20:00:00+00:00');
+    $typed_data->setValue(NULL);
+    $this->assertNull($typed_data->getDateTime());
 
-    // Duration type.
-    $value = new DateInterval('PT20S');
-    $typed_data = $this->createTypedData(array('type' => 'duration'), $value);
-    $this->assertTrue($typed_data->getValue() === $value, 'Duration value was fetched.');
+    // Timestamp type.
+    $value = REQUEST_TIME;
+    $typed_data = $this->createTypedData(array('type' => 'timestamp'), $value);
+    $this->assertTrue($typed_data->getValue() == $value, 'Timestamp value was fetched.');
+    $this->assertEqual($typed_data->validate()->count(), 0);
+    $new_value = REQUEST_TIME + 1;
+    $typed_data->setValue($new_value);
+    $this->assertTrue($typed_data->getValue() === $new_value, 'Timestamp value was changed and set.');
     $this->assertEqual($typed_data->validate()->count(), 0);
-    $typed_data->setValue(10);
-    $this->assertTrue($typed_data->getValue()->s == 10, 'Duration value was changed and set by time span in seconds.');
+    $typed_data->setValue(NULL);
+    $this->assertNull($typed_data->getDateTime(), 'Timestamp wrapper is null-able.');
+    $this->assertEqual($typed_data->validate()->count(), 0);
+    $typed_data->setValue('invalid');
+    $this->assertEqual($typed_data->validate()->count(), 1, 'Validation detected invalid value.');
+    // Check implementation of DateTimeInterface.
+    $typed_data = $this->createTypedData(array('type' => 'timestamp'), REQUEST_TIME);
+    $this->assertTrue($typed_data->getDateTime() instanceof DrupalDateTime);
+    $typed_data->setDateTime(new DrupalDateTime(REQUEST_TIME + 1));
+    $this->assertEqual($typed_data->getValue(), REQUEST_TIME + 1);
+    $typed_data->setValue(NULL);
+    $this->assertNull($typed_data->getDateTime());
+
+    // DurationIso8601 type.
+    $value = 'PT20S';
+    $typed_data = $this->createTypedData(array('type' => 'duration_iso8601'), $value);
+    $this->assertTrue($typed_data->getValue() === $value, 'DurationIso8601 value was fetched.');
     $this->assertEqual($typed_data->validate()->count(), 0);
     $typed_data->setValue('P40D');
-    $this->assertTrue($typed_data->getValue()->d == 40, 'Duration value was changed and set by duration string.');
-    $this->assertTrue(is_string($typed_data->getString()), 'Duration value was converted to string');
+    $this->assertTrue($typed_data->getValue()->d == 40, 'DurationIso8601 value was changed and set by duration string.');
+    $this->assertTrue(is_string($typed_data->getString()), 'DurationIso8601 value was converted to string');
     $this->assertEqual($typed_data->validate()->count(), 0);
-    // Test getting the string and passing it back as value.
-    $duration = $typed_data->getString();
-    $typed_data->setValue($duration);
-    $this->assertEqual($typed_data->getString(), $duration, 'Duration formatted as string can be used to set the duration value.');
+    $typed_data->setValue(NULL);
+    $this->assertNull($typed_data->getValue(), 'DurationIso8601 wrapper is null-able.');
     $this->assertEqual($typed_data->validate()->count(), 0);
+    $typed_data->setValue('invalid');
+    $this->assertEqual($typed_data->validate()->count(), 1, 'Validation detected invalid value.');
+    // Check implementation of DurationInterface.
+    $typed_data = $this->createTypedData(array('type' => 'duration_iso8601'), 'PT20S');
+    $this->assertTrue($typed_data->getDuration() instanceof DateInterval);
+    $typed_data->setDuration(new DateInterval('P40D'));
+    $this->assertEqual($typed_data->getValue(), 'P40D');
     $typed_data->setValue(NULL);
-    $this->assertNull($typed_data->getValue(), 'Duration wrapper is null-able.');
+    $this->assertNull($typed_data->getDuration());
+
+    // Time span type.
+    $value = new DateInterval(20);
+    $typed_data = $this->createTypedData(array('type' => 'timespan'), $value);
+    $this->assertTrue($typed_data->getValue() === $value, 'Time span value was fetched.');
+    $this->assertEqual($typed_data->validate()->count(), 0);
+    $typed_data->setValue(60 * 60 * 4);
+    $this->assertTrue($typed_data->getDuration()->h == 4, 'Time span was changed');
+    $this->assertTrue(is_string($typed_data->getString()), 'Time span value was converted to string');
+    $this->assertEqual($typed_data->validate()->count(), 0);
+    $typed_data->setValue(NULL);
+    $this->assertNull($typed_data->getValue(), 'Time span wrapper is null-able.');
     $this->assertEqual($typed_data->validate()->count(), 0);
     $typed_data->setValue('invalid');
     $this->assertEqual($typed_data->validate()->count(), 1, 'Validation detected invalid value.');
+    // Check implementation of DurationInterface.
+    $typed_data = $this->createTypedData(array('type' => 'timespan'), 20);
+    $this->assertTrue($typed_data->getDuration() instanceof DateInterval);
+    $typed_data->setDuration(new DateInterval('P40H'));
+    $this->assertEqual($typed_data->getValue(), 60 * 60 * 4);
+    $typed_data->setValue(NULL);
+    $this->assertNull($typed_data->getDuration());
 
     // URI type.
     $uri = 'http://example.com/foo/';
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 7a1b8d4..3eea1f2 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -9,7 +9,7 @@
 use Drupal\Core\Cache\Cache;
 use Drupal\Core\Language\Language;
 use Drupal\Core\Utility\ModuleInfo;
-use Drupal\Core\TypedData\Primitive;
+use Drupal\Core\TypedData\PrimitiveBase;
 use Drupal\system\Plugin\Block\SystemMenuBlock;
 use Symfony\Component\HttpFoundation\JsonResponse;
 use Symfony\Component\HttpFoundation\RedirectResponse;
@@ -2169,48 +2169,47 @@ function system_data_type_info() {
     'boolean' => array(
       'label' => t('Boolean'),
       'class' => '\Drupal\Core\TypedData\Type\Boolean',
-      'primitive type' => Primitive::BOOLEAN,
     ),
     'string' => array(
       'label' => t('String'),
       'class' => '\Drupal\Core\TypedData\Type\String',
-      'primitive type' => Primitive::STRING,
     ),
     'integer' => array(
       'label' => t('Integer'),
       'class' => '\Drupal\Core\TypedData\Type\Integer',
-      'primitive type' => Primitive::INTEGER,
     ),
     'float' => array(
       'label' => t('Float'),
       'class' => '\Drupal\Core\TypedData\Type\Float',
-      'primitive type' => Primitive::FLOAT,
     ),
-    'date' => array(
+    'datetime_iso8601' => array(
       'label' => t('Date'),
-      'class' => '\Drupal\Core\TypedData\Type\Date',
-      'primitive type' => Primitive::DATE,
+      'class' => '\Drupal\Core\TypedData\Type\DateTimeIso8601',
     ),
-    'duration' => array(
-      'label' => t('Duration'),
-      'class' => '\Drupal\Core\TypedData\Type\Duration',
-      'primitive type' => Primitive::DURATION,
+    'timestamp' => array(
+      'label' => t('Timestamp'),
+      'class' => '\Drupal\Core\TypedData\Type\Timestamp',
+    ),
+    'duration_iso8601' => array(
+      'label' => t('Duration Iso8601'),
+      'class' => '\Drupal\Core\TypedData\Type\DurationIso8601',
+    ),
+    'timespan' => array(
+      'label' => t('Time span in seconds'),
+      'class' => '\Drupal\Core\TypedData\Type\TimeSpan',
     ),
     'uri' => array(
       'label' => t('URI'),
       'class' => '\Drupal\Core\TypedData\Type\Uri',
-      'primitive type' => Primitive::URI,
     ),
     'email' => array(
       'label' => t('Email'),
       'class' => '\Drupal\Core\TypedData\Type\Email',
-      'primitive type' => Primitive::STRING,
       'constraints' => array('Email' => array()),
     ),
     'binary' => array(
       'label' => t('Binary'),
       'class' => '\Drupal\Core\TypedData\Type\Binary',
-      'primitive type' => Primitive::BINARY,
     ),
     'map' => array(
       'label' => t('Map'),
