diff --git a/tests/src/Kernel/TokenDateTest.php b/tests/src/Kernel/TokenDateTest.php
new file mode 100644
index 0000000..ed63c37
--- /dev/null
+++ b/tests/src/Kernel/TokenDateTest.php
@@ -0,0 +1,61 @@
+<?php
+
+namespace Drupal\Tests\token\Kernel;
+
+use Drupal\Core\Cache\Cache;
+use Drupal\Core\Render\BubbleableMetadata;
+use Drupal\KernelTests\KernelTestBase;
+
+/**
+ * Provide test coverage for date tokens.
+ *
+ * @group token
+ */
+class TokenDateTest extends KernelTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['token', 'system'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->installConfig('system');
+  }
+
+  public function testDateTokens() {
+    $token = \Drupal::token();
+
+    $bubbleable_metadata = new BubbleableMetadata();
+    $token->replace('[date:medium]', [], [], $bubbleable_metadata);
+
+    $this->assertEquals(0, $bubbleable_metadata->getCacheMaxAge());
+    $this->assertEquals(['rendered'], $bubbleable_metadata->getCacheTags());
+
+    $bubbleable_metadata = new BubbleableMetadata();
+    $token->replace('[date:medium]', ['date' => 1000], [], $bubbleable_metadata);
+
+    $this->assertEquals(Cache::PERMANENT, $bubbleable_metadata->getCacheMaxAge());
+    $this->assertEquals(['rendered'], $bubbleable_metadata->getCacheTags());
+
+    $bubbleable_metadata = new BubbleableMetadata();
+    $token->replace('[current-date:medium]', [], [], $bubbleable_metadata);
+
+    $this->assertEquals(0, $bubbleable_metadata->getCacheMaxAge());
+    $this->assertEquals(['rendered'], $bubbleable_metadata->getCacheTags());
+  }
+
+  public function testRandomTokens() {
+    $token = \Drupal::token();
+
+    $bubbleable_metadata = new BubbleableMetadata();
+    $token->replace('[random:number]', [], [], $bubbleable_metadata);
+
+    $this->assertEquals(0, $bubbleable_metadata->getCacheMaxAge());
+  }
+
+}
diff --git a/token.tokens.inc b/token.tokens.inc
index 9920139..9844fc6 100644
--- a/token.tokens.inc
+++ b/token.tokens.inc
@@ -50,14 +50,18 @@ function token_token_info_alter(&$info) {
   // Support 'url' type tokens for core tokens.
   if (isset($info['tokens']['comment']['url']) && \Drupal::moduleHandler()->moduleExists('comment')) {
     $info['tokens']['comment']['url']['type'] = 'url';
+    $info['tokens']['comment']['url']['name'] = t('Url');
   }
   if (isset($info['tokens']['node']['url']) && \Drupal::moduleHandler()->moduleExists('node')) {
     $info['tokens']['node']['url']['type'] = 'url';
+    $info['tokens']['node']['url']['name'] = t('Url');
   }
   if (isset($info['tokens']['term']['url']) && \Drupal::moduleHandler()->moduleExists('taxonomy')) {
     $info['tokens']['term']['url']['type'] = 'url';
+    $info['tokens']['term']['url']['name'] = t('Url');
   }
   $info['tokens']['user']['url']['type'] = 'url';
+  $info['tokens']['user']['url']['name'] = t('Url');
 
   // Add [token:url] tokens for any URI-able entities.
   $entities = \Drupal::entityManager()->getDefinitions();
@@ -394,12 +398,21 @@ function token_tokens($type, array $tokens, array $data = array(), array $option
 
   // Date tokens.
   if ($type == 'date') {
-    $date = !empty($data['date']) ? $data['date'] : REQUEST_TIME;
+    if (empty($data['date'])) {
+      $date = REQUEST_TIME;
+      // We depend on the current request time, so the tokens are not cacheable
+      // at all.
+      $bubbleable_metadata->setCacheMaxAge(0);
+    }
+    else {
+      $date = $data['date'];
+    }
 
     // @todo Remove when http://drupal.org/node/1173706 is fixed.
     $date_format_types = \Drupal::entityManager()->getStorage('date_format')->loadMultiple();
     foreach ($tokens as $name => $original) {
       if (isset($date_format_types[$name]) && _token_module('date', $name) == 'token') {
+        $bubbleable_metadata = $bubbleable_metadata->merge($date_format_types[$name]);
         $replacements[$original] = format_date($date, $name, '', NULL, $langcode);
       }
     }
@@ -408,6 +421,9 @@ function token_tokens($type, array $tokens, array $data = array(), array $option
   // Current date tokens.
   // @todo Remove when http://drupal.org/node/943028 is fixed.
   if ($type == 'current-date') {
+    // We depend on the current request time, so the tokens are not cacheable
+    // at all.
+    $bubbleable_metadata->setCacheMaxAge(0);
     $replacements += \Drupal::token()->generate('date', $tokens, array('date' => REQUEST_TIME), $options, $bubbleable_metadata);
   }
 
@@ -419,6 +435,7 @@ function token_tokens($type, array $tokens, array $data = array(), array $option
     // Chained token relationships.
     if (($url_tokens = \Drupal::token()->findWithPrefix($tokens, 'url'))) {
       // Add fragment to url options.
+      $bubbleable_metadata = $bubbleable_metadata->merge($comment);
       $replacements += \Drupal::token()->generate('url', $url_tokens, array('url' => $comment->urlInfo('canonical', ['fragment' => "comment-{$comment->id()}"])), $options, $bubbleable_metadata);
     }
   }
@@ -427,6 +444,7 @@ function token_tokens($type, array $tokens, array $data = array(), array $option
   if ($type == 'node' && !empty($data['node'])) {
     /* @var \Drupal\node\NodeInterface $node */
     $node = $data['node'];
+    $bubbleable_metadata = $bubbleable_metadata->merge($node);
 
     foreach ($tokens as $name => $original) {
       switch ($name) {
@@ -434,7 +452,11 @@ function token_tokens($type, array $tokens, array $data = array(), array $option
           $replacements[$original] = $sanitize ? Xss::filter($node->revision_log->value) : $node->revision_log->value;
           break;
         case 'content-type':
-          $type_name = \Drupal::entityManager()->getStorage('node_type')->load($node->getType())->label();
+          $node_type = \Drupal::entityManager()
+            ->getStorage('node_type')
+            ->load($node->getType());
+          $type_name = $node_type->label();
+          $bubbleable_metadata = $bubbleable_metadata->merge($node_type);
           $replacements[$original] = $sanitize ? SafeMarkup::checkPlain($type_name) : $type_name;
           break;
       }
@@ -456,6 +478,7 @@ function token_tokens($type, array $tokens, array $data = array(), array $option
   if ($type == 'content-type' && !empty($data['node_type'])) {
     /* @var \Drupal\node\NodeTypeInterface $node_type */
     $node_type = $data['node_type'];
+    $bubbleable_metadata = $bubbleable_metadata->merge($node_type);
 
     foreach ($tokens as $name => $original) {
       switch ($name) {
@@ -487,6 +510,7 @@ function token_tokens($type, array $tokens, array $data = array(), array $option
   if ($type == 'term' && !empty($data['term'])) {
     /* @var \Drupal\taxonomy\TermInterface $term */
     $term = $data['term'];
+    $bubbleable_metadata = $bubbleable_metadata->merge($term);
 
     /** @var \Drupal\taxonomy\TermStorageInterface $term_storage */
     $term_storage = \Drupal::entityManager()->getStorage('taxonomy_term');
@@ -535,6 +559,7 @@ function token_tokens($type, array $tokens, array $data = array(), array $option
   // Vocabulary tokens.
   if ($type == 'vocabulary' && !empty($data['vocabulary'])) {
     $vocabulary = $data['vocabulary'];
+    $bubbleable_metadata = $bubbleable_metadata->merge($vocabulary);
 
     foreach ($tokens as $name => $original) {
       switch ($name) {
@@ -552,6 +577,7 @@ function token_tokens($type, array $tokens, array $data = array(), array $option
   // File tokens.
   if ($type == 'file' && !empty($data['file'])) {
     $file = $data['file'];
+    $bubbleable_metadata = $bubbleable_metadata->merge($file);
 
     foreach ($tokens as $name => $original) {
       switch ($name) {
@@ -574,6 +600,7 @@ function token_tokens($type, array $tokens, array $data = array(), array $option
   if ($type == 'user' && !empty($data['user'])) {
     /* @var \Drupal\user\UserInterface $account */
     $account = $data['user'];
+    $bubbleable_metadata = $bubbleable_metadata->merge($account);
 
     foreach ($tokens as $name => $original) {
       switch ($name) {
@@ -613,6 +640,7 @@ function token_tokens($type, array $tokens, array $data = array(), array $option
 
   // Current user tokens.
   if ($type == 'current-user') {
+    $bubbleable_metadata->addCacheContexts(['current_user']);
     foreach ($tokens as $name => $original) {
       switch ($name) {
         case 'ip-address':
@@ -627,6 +655,7 @@ function token_tokens($type, array $tokens, array $data = array(), array $option
   if ($type == 'menu-link' && !empty($data['menu-link'])) {
     /** @var \Drupal\Core\Menu\MenuLinkInterface $link */
     $link = $data['menu-link'];
+    $bubbleable_metadata = $bubbleable_metadata->merge($link);
     /** @var \Drupal\Core\Menu\MenuLinkManagerInterface $menu_link_manager */
     $menu_link_manager = \Drupal::service('plugin.manager.menu.link');
 
@@ -689,6 +718,7 @@ function token_tokens($type, array $tokens, array $data = array(), array $option
 
   // Current page tokens.
   if ($type == 'current-page') {
+    $bubbleable_metadata->addCacheContexts(['url']);
     foreach ($tokens as $name => $original) {
       switch ($name) {
         case 'title':
@@ -800,6 +830,7 @@ function token_tokens($type, array $tokens, array $data = array(), array $option
   if (!empty($data[$type]) && $entity_type = token_get_entity_mapping('token', $type)) {
     /* @var \Drupal\Core\Entity\EntityInterface $entity */
     $entity = $data[$type];
+    $bubbleable_metadata = $bubbleable_metadata->merge($entity);
 
     foreach ($tokens as $name => $original) {
       switch ($name) {
@@ -907,6 +938,7 @@ function token_tokens($type, array $tokens, array $data = array(), array $option
 
   // Random tokens.
   if ($type == 'random') {
+    $bubbleable_metadata->setCacheMaxAge(0);
     foreach ($tokens as $name => $original) {
       switch ($name) {
         case 'number':
