diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc
index a9ea984..2d59fb5 100644
--- a/core/includes/bootstrap.inc
+++ b/core/includes/bootstrap.inc
@@ -945,7 +945,7 @@ function drupal_serve_page_from_cache(Response $response, Request $request) {
  * variable text such as user names or link URLs into translated text. Variable
  * substitution looks like this:
  * @code
- * $text = t("@name's blog", array('@name' => user_format_name($account)));
+ * $text = t("@name's blog", array('@name' => $account->getDisplayName()));
  * @endcode
  * Basically, you can put variables like @name into your string, and t() will
  * substitute their sanitized values at translation time. (See the
diff --git a/core/lib/Drupal/Core/Session/AccountInterface.php b/core/lib/Drupal/Core/Session/AccountInterface.php
index 93d5969..e09f4aa 100644
--- a/core/lib/Drupal/Core/Session/AccountInterface.php
+++ b/core/lib/Drupal/Core/Session/AccountInterface.php
@@ -112,7 +112,17 @@ public function getPreferredLangcode($default = NULL);
   public function getPreferredAdminLangcode($default = NULL);
 
   /**
-   * Returns the username of this account.
+   * Returns the unique username of this account.
+   *
+   * @return string
+   *   An unsanitized string with the unique username. The code receiving this
+   *   result must ensure that \Drupal\Component\Utility\String::checkPlain()
+   *   is called on it before it is printed to the page.
+   */
+  public function getUsername();
+
+  /**
+   * Returns the display name of this account.
    *
    * By default, the passed-in object's 'name' property is used if it exists, or
    * else, the site-defined value for the 'anonymous' variable. However, a module
@@ -121,13 +131,13 @@ public function getPreferredAdminLangcode($default = NULL);
    *
    * @see hook_user_format_name_alter()
    *
-   * @return
-   *   An unsanitized string with the username to display. The code receiving
+   * @return string
+   *   An unsanitized string with the user name to display. The code receiving
    *   this result must ensure that \Drupal\Component\Utility\String::checkPlain()
    *   is called on it before it is
    *   printed to the page.
    */
-  public function getUsername();
+  public function getDisplayName();
 
   /**
    * Returns the email address of this account.
diff --git a/core/lib/Drupal/Core/Session/AccountProxy.php b/core/lib/Drupal/Core/Session/AccountProxy.php
index 9f00218..7ac446b 100644
--- a/core/lib/Drupal/Core/Session/AccountProxy.php
+++ b/core/lib/Drupal/Core/Session/AccountProxy.php
@@ -166,6 +166,13 @@ public function getUsername() {
   /**
    * {@inheritdoc}
    */
+  public function getDisplayName() {
+    return $this->getAccount()->getDisplayName();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function getEmail() {
     return $this->getAccount()->getEmail();
   }
diff --git a/core/lib/Drupal/Core/Session/UserSession.php b/core/lib/Drupal/Core/Session/UserSession.php
index da4f26d..26908ab 100644
--- a/core/lib/Drupal/Core/Session/UserSession.php
+++ b/core/lib/Drupal/Core/Session/UserSession.php
@@ -217,6 +217,13 @@ function getPreferredAdminLangcode($default = NULL) {
    * {@inheritdoc}
    */
   public function getUsername() {
+    return $this->name ?: '';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDisplayName() {
     $name = $this->name ?: \Drupal::config('user.settings')->get('anonymous');
     \Drupal::moduleHandler()->alter('user_format_name', $name, $this);
     return $name;
diff --git a/core/modules/action/src/Plugin/Action/EmailAction.php b/core/modules/action/src/Plugin/Action/EmailAction.php
index c6589a6..e2f2d2b 100644
--- a/core/modules/action/src/Plugin/Action/EmailAction.php
+++ b/core/modules/action/src/Plugin/Action/EmailAction.php
@@ -135,7 +135,7 @@ public function buildConfigurationForm(array $form, array &$form_state) {
       '#default_value' => $this->configuration['message'],
       '#cols' => '80',
       '#rows' => '20',
-      '#description' => t('The message that should be sent. You may include placeholders like [node:title], [user:name], and [comment:body] to represent data that will be different each time message is sent. Not all placeholders will be available in all contexts.'),
+      '#description' => t('The message that should be sent. You may include placeholders like [node:title], [user:name], [user:username] and [comment:body] to represent data that will be different each time message is sent. Not all placeholders will be available in all contexts.'),
     );
     return $form;
   }
diff --git a/core/modules/action/src/Plugin/Action/MessageAction.php b/core/modules/action/src/Plugin/Action/MessageAction.php
index cc15650..aaa29bb 100644
--- a/core/modules/action/src/Plugin/Action/MessageAction.php
+++ b/core/modules/action/src/Plugin/Action/MessageAction.php
@@ -75,7 +75,7 @@ public function buildConfigurationForm(array $form, array &$form_state) {
       '#default_value' => $this->configuration['message'],
       '#required' => TRUE,
       '#rows' => '8',
-      '#description' => t('The message to be displayed to the current user. You may include placeholders like [node:title], [user:name], and [comment:body] to represent data that will be different each time message is sent. Not all placeholders will be available in all contexts.'),
+      '#description' => t('The message to be displayed to the current user. You may include placeholders like [node:title], [user:name], [user:username] and [comment:body] to represent data that will be different each time message is sent. Not all placeholders will be available in all contexts.'),
     );
     return $form;
   }
diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module
index 1ab6691..a7689e5 100644
--- a/core/modules/comment/comment.module
+++ b/core/modules/comment/comment.module
@@ -1175,22 +1175,6 @@ function comment_preview(CommentInterface $comment, array &$form_state) {
 
   if (!form_get_errors($form_state)) {
     // Attach the user and time information.
-    $author_name = $comment->getAuthorName();
-    if (!empty($author_name)) {
-      $account = user_load_by_name($author_name);
-    }
-    elseif (\Drupal::currentUser()->isAuthenticated() && empty($comment->is_anonymous)) {
-      $account = \Drupal::currentUser();
-    }
-
-    if (!empty($account) && $account->isAuthenticated()) {
-      $comment->setOwner($account);
-      $comment->setAuthorName(String::checkPlain($account->getUsername()));
-    }
-    elseif (empty($author_name)) {
-      $comment->setAuthorName(\Drupal::config('user.settings')->get('anonymous'));
-    }
-
     $created_time = !is_null($comment->getCreatedTime()) ? $comment->getCreatedTime() : REQUEST_TIME;
     $comment->setCreatedTime($created_time);
     $comment->changed->value = REQUEST_TIME;
diff --git a/core/modules/comment/src/CommentForm.php b/core/modules/comment/src/CommentForm.php
index 27d4a93..3312873 100644
--- a/core/modules/comment/src/CommentForm.php
+++ b/core/modules/comment/src/CommentForm.php
@@ -220,6 +220,12 @@ public function form(array $form, array &$form_state) {
       '#value' => ($comment->id() ? !$comment->getOwnerId() : $this->currentUser->isAnonymous()),
     );
 
+    // Set the uid on new comments to the current user.
+    $form['uid'] = array(
+      '#type' => 'value',
+      '#value' => $comment->id() ? $comment->getOwnerId() : $this->currentUser->id(),
+    );
+
     return parent::form($form, $form_state, $comment);
   }
 
@@ -316,13 +322,7 @@ public function submit(array $form, array &$form_state) {
     /** @var \Drupal\comment\CommentInterface $comment */
     $comment = parent::submit($form, $form_state);
 
-    // If the comment was posted by a registered user, assign the author's ID.
-    // @todo Too fragile. Should be prepared and stored in comment_form()
-    // already.
     $author_name = $comment->getAuthorName();
-    if (!$comment->is_anonymous && !empty($author_name) && ($account = user_load_by_name($author_name))) {
-      $comment->setOwner($account);
-    }
     // If the comment was posted by an anonymous user and no author name was
     // required, use "Anonymous" by default.
     if ($comment->is_anonymous && (!isset($author_name) || $author_name === '')) {
diff --git a/core/modules/contact/contact.module b/core/modules/contact/contact.module
index 812c6df..62a4598 100644
--- a/core/modules/contact/contact.module
+++ b/core/modules/contact/contact.module
@@ -129,7 +129,7 @@ function contact_mail($key, &$message, $params) {
     '!subject' => $contact_message->getSubject(),
     '!category' => !empty($params['contact_category']) ? $params['contact_category']->label() : NULL,
     '!form-url' => url(current_path(), array('absolute' => TRUE, 'language' => $language)),
-    '!sender-name' => user_format_name($sender),
+    '!sender-name' => $sender->getDisplayName(),
   );
   if ($sender->isAuthenticated()) {
     $variables['!sender-url'] = $sender->url('canonical', array('absolute' => TRUE, 'language' => $language));
@@ -157,7 +157,7 @@ function contact_mail($key, &$message, $params) {
     case 'user_mail':
     case 'user_copy':
       $variables += array(
-        '!recipient-name' => user_format_name($params['recipient']),
+        '!recipient-name' => $params['recipient']->getDisplayName(),
         '!recipient-edit-url' => url('user/' . $params['recipient']->id() . '/edit', array('absolute' => TRUE, 'language' => $language)),
       );
       $message['subject'] .= t('[!site-name] !subject', $variables, $options);
diff --git a/core/modules/contact/src/Controller/ContactController.php b/core/modules/contact/src/Controller/ContactController.php
index d5db7e3..5c6d4c9 100644
--- a/core/modules/contact/src/Controller/ContactController.php
+++ b/core/modules/contact/src/Controller/ContactController.php
@@ -116,7 +116,7 @@ public function contactPersonalPage(UserInterface $user) {
     ));
 
     $form = $this->entityFormBuilder()->getForm($message);
-    $form['#title'] = $this->t('Contact @username', array('@username' => $user->getUsername()));
+    $form['#title'] = $this->t('Contact @username', array('@username' => $user->getDisplayName()));
     return $form;
   }
 
diff --git a/core/modules/file/src/Tests/FileFieldPathTest.php b/core/modules/file/src/Tests/FileFieldPathTest.php
index b9134d3..8afdc8b 100644
--- a/core/modules/file/src/Tests/FileFieldPathTest.php
+++ b/core/modules/file/src/Tests/FileFieldPathTest.php
@@ -60,7 +60,7 @@ function testUploadPath() {
     // Do token replacement using the same user which uploaded the file, not
     // the user running the test case.
     $data = array('user' => $this->admin_user);
-    $subdirectory = \Drupal::token()->replace('[user:uid]/[user:name]', $data);
+    $subdirectory = \Drupal::token()->replace('[user:uid]/[user:username]', $data);
     $this->assertPathMatch('public://' . $subdirectory . '/' . $test_file->getFilename(), $node_file->getFileUri(), format_string('The file %file was uploaded to the correct path with token replacements.', array('%file' => $node_file->getFileUri())));
   }
 
diff --git a/core/modules/file/src/Tests/FileTokenReplaceTest.php b/core/modules/file/src/Tests/FileTokenReplaceTest.php
index 3198681..a40963f 100644
--- a/core/modules/file/src/Tests/FileTokenReplaceTest.php
+++ b/core/modules/file/src/Tests/FileTokenReplaceTest.php
@@ -57,7 +57,7 @@ function testFileTokenReplacement() {
     $tests['[file:created:short]'] = format_date($file->getCreatedTime(), 'short', '', NULL, $language_interface->id);
     $tests['[file:changed]'] = format_date($file->getChangedTime(), 'medium', '', NULL, $language_interface->id);
     $tests['[file:changed:short]'] = format_date($file->getChangedTime(), 'short', '', NULL, $language_interface->id);
-    $tests['[file:owner]'] = String::checkPlain(user_format_name($this->admin_user));
+    $tests['[file:owner]'] = String::checkPlain($this->admin_user->getDisplayName());
     $tests['[file:owner:uid]'] = $file->getOwnerId();
 
     // Test to make sure that we generated something for each token.
diff --git a/core/modules/forum/forum.module b/core/modules/forum/forum.module
index af041fc..c7751c8 100644
--- a/core/modules/forum/forum.module
+++ b/core/modules/forum/forum.module
@@ -577,7 +577,7 @@ function template_preprocess_forums(&$variables) {
         }
         $forum_submitted = array('#theme' => 'forum_submitted', '#topic' => (object) array(
           'uid' => $topic->getOwnerId(),
-          'name' => $topic->getOwner()->getUsername(),
+          'name' => $topic->getOwner()->getDisplayName(),
           'created' => $topic->getCreatedTime(),
         ));
         $variables['topics'][$id]->submitted = drupal_render($forum_submitted);
diff --git a/core/modules/node/src/Plugin/views/row/Rss.php b/core/modules/node/src/Plugin/views/row/Rss.php
index 9bb5574..67161cb 100644
--- a/core/modules/node/src/Plugin/views/row/Rss.php
+++ b/core/modules/node/src/Plugin/views/row/Rss.php
@@ -120,7 +120,7 @@ public function render($row) {
       ),
       array(
         'key' => 'dc:creator',
-        'value' => $node->getOwner()->getUsername(),
+        'value' => $node->getOwner()->getDisplayName(),
       ),
       array(
         'key' => 'guid',
diff --git a/core/modules/rdf/rdf.module b/core/modules/rdf/rdf.module
index a66af78..9ddc9e9 100644
--- a/core/modules/rdf/rdf.module
+++ b/core/modules/rdf/rdf.module
@@ -363,7 +363,7 @@ function rdf_preprocess_user(&$variables) {
         '#attributes' => array(
           'about' => $account->url(),
           'property' => $name_mapping['properties'],
-          'content' => $account->getUsername(),
+          'content' => $account->getDisplayName(),
           'lang' => '',
         ),
       );
diff --git a/core/modules/shortcut/shortcut.admin.inc b/core/modules/shortcut/shortcut.admin.inc
index 52783d4..f0a0dd9 100644
--- a/core/modules/shortcut/shortcut.admin.inc
+++ b/core/modules/shortcut/shortcut.admin.inc
@@ -144,7 +144,7 @@ function shortcut_set_switch_submit($form, &$form_state) {
     ));
     $set->save();
     $replacements = array(
-      '%user' => $account->getUsername(),
+      '%user' => $account->getDisplayName(),
       '%set_name' => $set->label(),
       '@switch-url' => url(current_path()),
     );
@@ -167,7 +167,7 @@ function shortcut_set_switch_submit($form, &$form_state) {
     // Switch to a different shortcut set.
     $set = ShortcutSet::load($form_state['values']['set']);
     $replacements = array(
-      '%user' => $account->getUsername(),
+      '%user' => $account->getDisplayName(),
       '%set_name' => $set->label(),
     );
     drupal_set_message($account->id() == $user->id() ? t('You are now using the %set_name shortcut set.', $replacements) : t('%user is now using the %set_name shortcut set.', $replacements));
diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php
index a7a49ad..f4116f9 100644
--- a/core/modules/system/system.api.php
+++ b/core/modules/system/system.api.php
@@ -1263,7 +1263,7 @@ function hook_mail($key, &$message, $params) {
   $context = $params['context'];
   $variables = array(
     '%site_name' => \Drupal::config('system.site')->get('name'),
-    '%username' => user_format_name($account),
+    '%username' => $account->getDisplayName(),
   );
   if ($context['hook'] == 'taxonomy') {
     $entity = $params['entity'];
diff --git a/core/modules/user/config/install/user.mail.yml b/core/modules/user/config/install/user.mail.yml
index 436ff53..836ec81 100644
--- a/core/modules/user/config/install/user.mail.yml
+++ b/core/modules/user/config/install/user.mail.yml
@@ -5,10 +5,10 @@ password_reset:
   body: "[user:name],\n\nA request to reset the password for your account has been made at [site:name].\n\nYou may now log in by clicking this link or copying and pasting it to your browser:\n\n[user:one-time-login-url]\n\nThis link can only be used once to log in and will lead you to a page where you can set your password. It expires after one day and nothing will happen if it's not used.\n\n--  [site:name] team"
   subject: 'Replacement login information for [user:name] at [site:name]'
 register_admin_created:
-  body: "[user:name],\n\nA site administrator at [site:name] has created an account for you. You may now log in by clicking this link or copying and pasting it to your browser:\n\n[user:one-time-login-url]\n\nThis link can only be used once to log in and will lead you to a page where you can set your password.\n\nAfter setting your password, you will be able to log in at [site:login-url] in the future using:\n\nusername: [user:name]\npassword: Your password\n\n--  [site:name] team"
+  body: "[user:name],\n\nA site administrator at [site:name] has created an account for you. You may now log in by clicking this link or copying and pasting it to your browser:\n\n[user:one-time-login-url]\n\nThis link can only be used once to log in and will lead you to a page where you can set your password.\n\nAfter setting your password, you will be able to log in at [site:login-url] in the future using:\n\nusername: [user:username]\npassword: Your password\n\n--  [site:name] team"
   subject: 'An administrator created an account for you at [site:name]'
 register_no_approval_required:
-  body: "[user:name],\n\nThank you for registering at [site:name]. You may now log in by clicking this link or copying and pasting it to your browser:\n\n[user:one-time-login-url]\n\nThis link can only be used once to log in and will lead you to a page where you can set your password.\n\nAfter setting your password, you will be able to log in at [site:login-url] in the future using:\n\nusername: [user:name]\npassword: Your password\n\n--  [site:name] team"
+  body: "[user:name],\n\nThank you for registering at [site:name]. You may now log in by clicking this link or copying and pasting it to your browser:\n\n[user:one-time-login-url]\n\nThis link can only be used once to log in and will lead you to a page where you can set your password.\n\nAfter setting your password, you will be able to log in at [site:login-url] in the future using:\n\nusername: [user:username]\npassword: Your password\n\n--  [site:name] team"
   subject: 'Account details for [user:name] at [site:name]'
 register_pending_approval:
   body: "[user:name],\n\nThank you for registering at [site:name]. Your application for an account is currently pending approval. Once it has been approved, you will receive another email containing information about how to log in, set your password, and other details.\n\n\n--  [site:name] team"
@@ -17,7 +17,7 @@ register_pending_approval_admin:
   body: "[user:name] has applied for an account.\n\n[user:edit-url]"
   subject: 'Account details for [user:name] at [site:name] (pending admin approval)'
 status_activated:
-  body: "[user:name],\n\nYour account at [site:name] has been activated.\n\nYou may now log in by clicking this link or copying and pasting it into your browser:\n\n[user:one-time-login-url]\n\nThis link can only be used once to log in and will lead you to a page where you can set your password.\n\nAfter setting your password, you will be able to log in at [site:login-url] in the future using:\n\nusername: [user:name]\npassword: Your password\n\n--  [site:name] team"
+  body: "[user:name],\n\nYour account at [site:name] has been activated.\n\nYou may now log in by clicking this link or copying and pasting it into your browser:\n\n[user:one-time-login-url]\n\nThis link can only be used once to log in and will lead you to a page where you can set your password.\n\nAfter setting your password, you will be able to log in at [site:login-url] in the future using:\n\nusername: [user:username]\npassword: Your password\n\n--  [site:name] team"
   subject: 'Account details for [user:name] at [site:name] (approved)'
 status_blocked:
   body: "[user:name],\n\nYour account on [site:name] has been blocked.\n\n--  [site:name] team"
diff --git a/core/modules/user/src/AccountSettingsForm.php b/core/modules/user/src/AccountSettingsForm.php
index 94c4bae..0c7df24 100644
--- a/core/modules/user/src/AccountSettingsForm.php
+++ b/core/modules/user/src/AccountSettingsForm.php
@@ -181,7 +181,7 @@ public function buildForm(array $form, array &$form_state) {
     );
     // These email tokens are shared for all settings, so just define
     // the list once to help ensure they stay in sync.
-    $email_token_help = $this->t('Available variables are: [site:name], [site:url], [user:name], [user:mail], [site:login-url], [site:url-brief], [user:edit-url], [user:one-time-login-url], [user:cancel-url].');
+    $email_token_help = $this->t('Available variables are: [site:name], [site:url], [user:name], [user:username], [user:mail], [site:login-url], [site:url-brief], [user:edit-url], [user:one-time-login-url], [user:cancel-url].');
 
     $form['email_admin_created'] = array(
       '#type' => 'details',
diff --git a/core/modules/user/src/Controller/UserController.php b/core/modules/user/src/Controller/UserController.php
index c27e3c9..09fc8ff 100644
--- a/core/modules/user/src/Controller/UserController.php
+++ b/core/modules/user/src/Controller/UserController.php
@@ -52,7 +52,7 @@ public function userPage(Request $request) {
    *   The user account name.
    */
   public function userTitle(UserInterface $user = NULL) {
-    return $user ? Xss::filter($user->getUsername()) : '';
+    return $user ? Xss::filter($user->getDisplayName()) : '';
   }
 
   /**
diff --git a/core/modules/user/src/Entity/User.php b/core/modules/user/src/Entity/User.php
index 3acb5b4..5fc9d95 100644
--- a/core/modules/user/src/Entity/User.php
+++ b/core/modules/user/src/Entity/User.php
@@ -424,7 +424,14 @@ public function isAnonymous() {
    * {@inheritdoc}
    */
   public function getUsername() {
-    $name = $this->get('name')->value ?: \Drupal::config('user.settings')->get('anonymous');
+    return $this->get('name')->value ?: '';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDisplayName() {
+    $name = $this->getUsername() ?: \Drupal::config('user.settings')->get('anonymous');
     \Drupal::moduleHandler()->alter('user_format_name', $name, $this);
     return $name;
   }
diff --git a/core/modules/user/src/Plugin/Search/UserSearch.php b/core/modules/user/src/Plugin/Search/UserSearch.php
index ce456b7..fe28da2 100644
--- a/core/modules/user/src/Plugin/Search/UserSearch.php
+++ b/core/modules/user/src/Plugin/Search/UserSearch.php
@@ -139,7 +139,7 @@ public function execute() {
 
     foreach ($accounts as $account) {
       $result = array(
-        'title' => $account->getUsername(),
+        'title' => $account->getDisplayName(),
         'link' => url('user/' . $account->id(), array('absolute' => TRUE)),
       );
       if ($this->currentUser->hasPermission('administer users')) {
diff --git a/core/modules/user/src/Plugin/views/field/Name.php b/core/modules/user/src/Plugin/views/field/Name.php
index fa924d7..4f46a50 100644
--- a/core/modules/user/src/Plugin/views/field/Name.php
+++ b/core/modules/user/src/Plugin/views/field/Name.php
@@ -23,18 +23,8 @@
 class Name extends User {
 
   /**
-   * Overrides \Drupal\user\Plugin\views\field\User::init().
-   *
-   * Add uid in the query so we can test for anonymous if needed.
+   * {@inheritdoc}
    */
-  public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
-    parent::init($view, $display, $options);
-
-    if (!empty($this->options['overwrite_anonymous']) || !empty($this->options['format_username'])) {
-      $this->additional_fields['uid'] = 'uid';
-    }
-  }
-
   protected function defineOptions() {
     $options = parent::defineOptions();
 
@@ -79,26 +69,29 @@ public function buildOptionsForm(&$form, &$form_state) {
    * {@inheritdoc}
    */
   protected function renderLink($data, ResultRow $values) {
-    $account = entity_create('user');
-    $account->uid = $this->getValue($values, 'uid');
-    $account->name = $this->getValue($values);
-    if (!empty($this->options['link_to_user']) || !empty($this->options['overwrite_anonymous'])) {
-      if (!empty($this->options['overwrite_anonymous']) && !$account->id()) {
-        // This is an anonymous user, and we're overriting the text.
-        return String::checkPlain($this->options['anonymous_text']);
-      }
-      elseif (!empty($this->options['link_to_user'])) {
-        $account->name = $this->getValue($values);
+    $account = $this->getEntity($values);
+
+    if (!empty($this->options['overwrite_anonymous']) && !$account->id()) {
+      // This is an anonymous user, and we're overwriting the text.
+      return String::checkPlain($this->options['anonymous_text']);
+    }
+
+    if (!empty($this->options['link_to_user'])) {
+      if (!empty($this->options['format_username'])) {
         $username = array(
           '#theme' => 'username',
           '#account' => $account,
         );
         return drupal_render($username);
       }
+      else {
+        return parent::renderLink($data, $values);
+      }
     }
+
     // If we want a formatted username, do that.
     if (!empty($this->options['format_username'])) {
-      return user_format_name($account);
+      return $account->getDisplayName();
     }
     // Otherwise, there's no special handling, so return the data directly.
     return $data;
diff --git a/core/modules/user/src/Tests/UserEditedOwnAccountTest.php b/core/modules/user/src/Tests/UserEditedOwnAccountTest.php
index 2437aec..c8d2d0b 100644
--- a/core/modules/user/src/Tests/UserEditedOwnAccountTest.php
+++ b/core/modules/user/src/Tests/UserEditedOwnAccountTest.php
@@ -14,6 +14,13 @@
  */
 class UserEditedOwnAccountTest extends WebTestBase {
 
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('user_form_test');
+
   public static function getInfo() {
     return array(
       'name' => 'User edited own account',
diff --git a/core/modules/user/src/Tests/UserEntityCallbacksTest.php b/core/modules/user/src/Tests/UserEntityCallbacksTest.php
index 2b90534..bd8db2d 100644
--- a/core/modules/user/src/Tests/UserEntityCallbacksTest.php
+++ b/core/modules/user/src/Tests/UserEntityCallbacksTest.php
@@ -19,13 +19,18 @@ class UserEntityCallbacksTest extends WebTestBase {
    *
    * @var array
    */
-  public static $modules = array('user');
+  public static $modules = array('user', 'user_name_test');
 
   /**
    * @var \Drupal\user\UserInterface
    */
   protected $account;
 
+  /**
+   * @var \Drupal\user\UserInterface
+   */
+  protected $anonymous;
+
   public static function getInfo() {
     return array(
       'name' => 'User entity callback tests',
@@ -51,6 +56,13 @@ function testLabelCallback() {
     $name = $this->randomName();
     \Drupal::config('user.settings')->set('anonymous', $name)->save();
     $this->assertEqual($this->anonymous->label(), $name, 'The variable anonymous should be used for name of uid 0');
+    $this->assertEqual($this->anonymous->getUserName(), '', 'The raw anonymous user name should be empty string');
+
+    // Set to test the altered username.
+    \Drupal::state()->set('user_name_test_altered_name', 'altered');
+
+    $this->assertEqual($this->account->getDisplayName(), $this->account->name->value . 'altered', 'The user display name should be altered.');
+    $this->assertEqual($this->account->getUsername(), $this->account->name->value, 'The user name should not be altered.');
   }
 
   /**
diff --git a/core/modules/user/src/Tests/UserTokenReplaceTest.php b/core/modules/user/src/Tests/UserTokenReplaceTest.php
index 1f8f023..6fba5bf 100644
--- a/core/modules/user/src/Tests/UserTokenReplaceTest.php
+++ b/core/modules/user/src/Tests/UserTokenReplaceTest.php
@@ -63,7 +63,8 @@ function testUserTokenReplacement() {
     // Generate and test sanitized tokens.
     $tests = array();
     $tests['[user:uid]'] = $account->id();
-    $tests['[user:name]'] = String::checkPlain(user_format_name($account));
+    $tests['[user:name]'] = String::checkPlain($account->getDisplayName());
+    $tests['[user:username]'] = String::checkPlain($account->getUsername());
     $tests['[user:mail]'] = String::checkPlain($account->getEmail());
     $tests['[user:url]'] = url("user/" . $account->id(), $url_options);
     $tests['[user:edit-url]'] = url("user/" . $account->id() . "/edit", $url_options);
@@ -71,7 +72,8 @@ function testUserTokenReplacement() {
     $tests['[user:last-login:short]'] = format_date($account->getLastLoginTime(), 'short', '', NULL, $language_interface->id);
     $tests['[user:created]'] = format_date($account->getCreatedTime(), 'medium', '', NULL, $language_interface->id);
     $tests['[user:created:short]'] = format_date($account->getCreatedTime(), 'short', '', NULL, $language_interface->id);
-    $tests['[current-user:name]'] = String::checkPlain(user_format_name($global_account));
+    $tests['[current-user:name]'] = String::checkPlain($global_account->getDisplayName());
+    $tests['[current-user:username]'] = String::checkPlain($global_account->getUsername());
 
     // Test to make sure that we generated something for each token.
     $this->assertFalse(in_array(0, array_map('strlen', $tests)), 'No empty tokens generated.');
@@ -82,9 +84,11 @@ function testUserTokenReplacement() {
     }
 
     // Generate and test unsanitized tokens.
-    $tests['[user:name]'] = user_format_name($account);
+    $tests['[user:name]'] = $account->getDisplayName();
+    $tests['[user:username]'] = $account->getUsername();
     $tests['[user:mail]'] = $account->getEmail();
-    $tests['[current-user:name]'] = user_format_name($global_account);
+    $tests['[current-user:name]'] = $global_account->getDisplayName();
+    $tests['[current-user:username]'] = $global_account->getUsername();
 
     foreach ($tests as $input => $expected) {
       $output = $token_service->replace($input, array('user' => $account), array('langcode' => $language_interface->id, 'sanitize' => FALSE));
diff --git a/core/modules/user/src/Tests/Views/HandlerFieldUserNameTest.php b/core/modules/user/src/Tests/Views/HandlerFieldUserNameTest.php
index 4b1e9f9..c0a7167 100644
--- a/core/modules/user/src/Tests/Views/HandlerFieldUserNameTest.php
+++ b/core/modules/user/src/Tests/Views/HandlerFieldUserNameTest.php
@@ -32,29 +32,28 @@ public static function getInfo() {
   }
 
   public function testUserName() {
-    $this->drupalLogin($this->drupalCreateUser(array('access user profiles')));
+    $new_user = $this->drupalCreateUser(array('access user profiles'));
+    $this->drupalLogin($new_user);
 
     $view = Views::getView('test_views_handler_field_user_name');
     $this->executeView($view);
 
-    $view->row_index = 0;
-
     $view->field['name']->options['link_to_user'] = TRUE;
-    $username = $view->result[0]->users_name = $this->randomName();
-    $view->result[0]->uid = 1;
-    $render = $view->field['name']->advancedRender($view->result[0]);
+    $username = $view->result[2]->users_name;
+    $render = $view->field['name']->advancedRender($view->result[2]);
     $this->assertTrue(strpos($render, $username) !== FALSE, 'If link to user is checked the username should be part of the output.');
-    $this->assertTrue(strpos($render, 'user/1') !== FALSE, 'If link to user is checked the link to the user should appear as well.');
+    $this->assertTrue(strpos($render, 'user/' . $new_user->id()) !== FALSE, 'If link to user is checked the link to the user should appear as well.');
+
+    $view->destroy();
+    $this->executeView($view);
 
     $view->field['name']->options['link_to_user'] = FALSE;
-    $username = $view->result[0]->users_name = $this->randomName();
-    $view->result[0]->uid = 1;
-    $render = $view->field['name']->advancedRender($view->result[0]);
+    $username = $view->result[2]->users_name;
+    $render = $view->field['name']->advancedRender($view->result[2]);
     $this->assertIdentical($render, $username, 'If the user is not linked the username should be printed out for a normal user.');
 
-    $view->result[0]->uid = 0;
     $anon_name = \Drupal::config('user.settings')->get('anonymous');
-    $view->result[0]->users_name = '';
+    $username = $view->result[0]->users_name;
     $render = $view->field['name']->advancedRender($view->result[0]);
     $this->assertIdentical($render, $anon_name , 'For user0 it should use the default anonymous name by default.');
 
diff --git a/core/modules/user/src/UserAutocomplete.php b/core/modules/user/src/UserAutocomplete.php
index c831c78..fb18067 100644
--- a/core/modules/user/src/UserAutocomplete.php
+++ b/core/modules/user/src/UserAutocomplete.php
@@ -91,7 +91,7 @@ public function getMatches($string, $include_anonymous = FALSE) {
 
       $controller = $this->entityManager->getStorage('user');
       foreach ($controller->loadMultiple($uids) as $account) {
-        $matches[] = array('value' => $account->getUsername(), 'label' => String::checkPlain($account->getUsername()));
+        $matches[] = array('value' => $account->getUsername(), 'label' => String::checkPlain($account->getDisplayName()));
       }
     }
 
diff --git a/core/modules/user/tests/modules/user_name_test/user_name_test.info.yml b/core/modules/user/tests/modules/user_name_test/user_name_test.info.yml
new file mode 100644
index 0000000..8a5e1cc
--- /dev/null
+++ b/core/modules/user/tests/modules/user_name_test/user_name_test.info.yml
@@ -0,0 +1,7 @@
+name: 'User name tests'
+type: module
+description: 'Support module for user name testing.'
+package: Testing
+version: VERSION
+core: 8.x
+hidden: true
diff --git a/core/modules/user/tests/modules/user_name_test/user_name_test.module b/core/modules/user/tests/modules/user_name_test/user_name_test.module
new file mode 100644
index 0000000..3603465
--- /dev/null
+++ b/core/modules/user/tests/modules/user_name_test/user_name_test.module
@@ -0,0 +1,12 @@
+<?php
+/**
+ * @file
+ * User name tests bootstrap file.
+ */
+
+/**
+ * Implements hook_user_format_name_alter().
+ */
+function user_name_test_user_format_name_alter(&$name, $account) {
+  $name .= \Drupal::state()->get('user_name_test_altered_name');
+}
diff --git a/core/modules/user/user.api.php b/core/modules/user/user.api.php
index ab6e2b6..5f9f8e4 100644
--- a/core/modules/user/user.api.php
+++ b/core/modules/user/user.api.php
@@ -182,17 +182,17 @@ function hook_user_cancel_methods_alter(&$methods) {
 /**
  * Alter the username that is displayed for a user.
  *
- * Called by user_format_name() to allow modules to alter the username that's
+ * Called by $user->getDisplayName() to allow modules to alter the username that's
  * displayed. Can be used to ensure user privacy in situations where
  * $account->name is too revealing.
  *
  * @param $name
- *   The string that user_format_name() will return.
+ *   The string that $user->getDisplayName() will return.
  *
  * @param $account
- *   The account object passed to user_format_name().
+ *   The account object.
  *
- * @see user_format_name()
+ * @see $account->getDisplayName()
  */
 function hook_user_format_name_alter(&$name, $account) {
   // Display the user's uid instead of name.
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index 5e072cb..a7c230d 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -590,7 +590,7 @@ function user_preprocess_block(&$variables) {
  *   Use \Drupal\Core\Session\Interface::getUsername().
  */
 function user_format_name(AccountInterface $account) {
-  return $account->getUsername();
+  return $account->getDisplayName();
 }
 
 /**
@@ -640,7 +640,8 @@ function template_preprocess_username(&$variables) {
   // unsanitized version, in case other preprocess functions want to implement
   // their own shortening logic or add markup. If they do so, they must ensure
   // that $variables['name'] is safe for printing.
-  $name = $variables['name_raw'] = $account->getUsername();
+  $name  = $account->getDisplayName();
+  $variables['name_raw'] = $account->getUsername();
   if (drupal_strlen($name) > 20) {
     $name = Unicode::truncate($name, 15, FALSE, TRUE);
     $variables['truncated'] = TRUE;
@@ -967,8 +968,8 @@ function _user_cancel($edit, $account, $method) {
       }
       $account->block();
       $account->save();
-      drupal_set_message(t('%name has been disabled.', array('%name' => $account->getUsername())));
-      watchdog('user', 'Blocked user: %name %email.', array('%name' => $account->getUsername(), '%email' => '<' . $account->getEmail() . '>'), WATCHDOG_NOTICE);
+      drupal_set_message(t('%name has been disabled.', array('%name' => $account->getDisplayName())));
+      watchdog('user', 'Blocked user: %name %email.', array('%name' => $account->getDisplayName(), '%email' => '<' . $account->getEmail() . '>'), WATCHDOG_NOTICE);
       break;
 
     case 'user_cancel_reassign':
@@ -978,8 +979,8 @@ function _user_cancel($edit, $account, $method) {
         _user_mail_notify('status_canceled', $account);
       }
       $account->delete();
-      drupal_set_message(t('%name has been deleted.', array('%name' => $account->getUsername())));
-      watchdog('user', 'Deleted user: %name %email.', array('%name' => $account->getUsername(), '%email' => '<' . $account->getEmail() . '>'), WATCHDOG_NOTICE);
+      drupal_set_message(t('%name has been deleted.', array('%name' => $account->getDisplayName())));
+      watchdog('user', 'Deleted user: %name %email.', array('%name' => $account->getDisplayName(), '%email' => '<' . $account->getEmail() . '>'), WATCHDOG_NOTICE);
       break;
   }
 
@@ -1652,7 +1653,7 @@ function user_toolbar() {
     '#type' => 'toolbar_item',
     'tab' => array(
       '#type' => 'link',
-      '#title' => $user->getUsername(),
+      '#title' => $user->getDisplayName(),
       '#href' => 'user',
       '#attributes' => array(
         'title' => t('My account'),
@@ -1686,7 +1687,7 @@ function user_toolbar() {
 function user_logout() {
   $user = \Drupal::currentUser();
 
-  watchdog('user', 'Session closed for %name.', array('%name' => $user->getUsername()));
+  watchdog('user', 'Session closed for %name.', array('%name' => $user->getDisplayName()));
 
   \Drupal::moduleHandler()->invokeAll('user_logout', array($user));
 
diff --git a/core/modules/user/user.pages.inc b/core/modules/user/user.pages.inc
index b276e57..432d01c 100644
--- a/core/modules/user/user.pages.inc
+++ b/core/modules/user/user.pages.inc
@@ -26,14 +26,14 @@ function user_pass_reset($form, &$form_state, $uid, $timestamp, $hashed_pass, $a
   if ($user->isAuthenticated()) {
     // The existing user is already logged in.
     if ($user->id() == $uid) {
-      drupal_set_message(t('You are logged in as %user. <a href="!user_edit">Change your password.</a>', array('%user' => $user->getUsername(), '!user_edit' => url("user/" . $user->id() . "/edit"))));
+      drupal_set_message(t('You are logged in as %user. <a href="!user_edit">Change your password.</a>', array('%user' => $user->getDisplayName(), '!user_edit' => url("user/" . $user->id() . "/edit"))));
     }
     // A different user is already logged in on the computer.
     else {
       $reset_link_account = user_load($uid);
       if (!empty($reset_link_account)) {
         drupal_set_message(t('Another user (%other_user) is already logged into the site on this computer, but you tried to use a one-time link for user %resetting_user. Please <a href="!logout">logout</a> and try using the link again.',
-          array('%other_user' => $user->getUsername(), '%resetting_user' => $reset_link_account->getUsername(), '!logout' => url('user/logout'))));
+          array('%other_user' => $user->getDisplayName(), '%resetting_user' => $reset_link_account->getDisplayName(), '!logout' => url('user/logout'))));
       } else {
         // Invalid one-time link specifies an unknown user.
         drupal_set_message(t('The one-time login link you clicked is invalid.'));
@@ -60,7 +60,7 @@ function user_pass_reset($form, &$form_state, $uid, $timestamp, $hashed_pass, $a
           // user_login_finalize() also updates the login timestamp of the
           // user, which invalidates further use of the one-time login link.
           user_login_finalize($account);
-          watchdog('user', 'User %name used one-time login link at time %timestamp.', array('%name' => $account->getUsername(), '%timestamp' => $timestamp));
+          watchdog('user', 'User %name used one-time login link at time %timestamp.', array('%name' => $account->getDisplayName(), '%timestamp' => $timestamp));
           drupal_set_message(t('You have just used your one-time login link. It is no longer necessary to use this link to log in. Please change your password.'));
           // Let the user's password be changed without the current password check.
           $token = Crypt::randomBytesBase64(55);
@@ -73,10 +73,10 @@ function user_pass_reset($form, &$form_state, $uid, $timestamp, $hashed_pass, $a
         else {
           if (!$account->getLastLoginTime()) {
             // No expiration for first time login.
-            $form['message'] = array('#markup' => t('<p>This is a one-time login for %user_name.</p><p>Click on this button to log in to the site and change your password.</p>', array('%user_name' => $account->getUsername())));
+            $form['message'] = array('#markup' => t('<p>This is a one-time login for %user_name.</p><p>Click on this button to log in to the site and change your password.</p>', array('%user_name' => $account->getDisplayName())));
           }
           else {
-            $form['message'] = array('#markup' => t('<p>This is a one-time login for %user_name and will expire on %expiration_date.</p><p>Click on this button to log in to the site and change your password.</p>', array('%user_name' => $account->getUsername(), '%expiration_date' => format_date($timestamp + $timeout))));
+            $form['message'] = array('#markup' => t('<p>This is a one-time login for %user_name and will expire on %expiration_date.</p><p>Click on this button to log in to the site and change your password.</p>', array('%user_name' => $account->getDisplayName(), '%expiration_date' => format_date($timestamp + $timeout))));
           }
           $form['help'] = array('#markup' => '<p>' . t('This login can be used only once.') . '</p>');
           $form['actions'] = array('#type' => 'actions');
diff --git a/core/modules/user/user.tokens.inc b/core/modules/user/user.tokens.inc
index 51579be..c94ab0d 100644
--- a/core/modules/user/user.tokens.inc
+++ b/core/modules/user/user.tokens.inc
@@ -27,7 +27,11 @@ function user_token_info() {
     'description' => t("The unique ID of the user account."),
   );
   $user['name'] = array(
-    'name' => t("Name"),
+    'name' => t("Display Name"),
+    'description' => t("The display name of the user account."),
+  );
+  $user['username'] = array(
+    'name' => t("Login Name"),
     'description' => t("The login name of the user account."),
   );
   $user['mail'] = array(
@@ -89,10 +93,15 @@ function user_tokens($type, $tokens, array $data = array(), array $options = arr
           break;
 
         case 'name':
-          $name = user_format_name($account);
+          $name = $account->getDisplayName();
           $replacements[$original] = $sanitize ? String::checkPlain($name) : $name;
           break;
 
+        case 'username':
+          $username = $account->getUsername();
+          $replacements[$original] = $sanitize ? check_plain($username) : $username;
+          break;
+
         case 'mail':
           $replacements[$original] = $sanitize ? String::checkPlain($account->getEmail()) : $account->getEmail();
           break;
