diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc
index 40245d2..0d11be1 100644
--- a/core/includes/bootstrap.inc
+++ b/core/includes/bootstrap.inc
@@ -672,7 +672,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 084943b..48130b7 100644
--- a/core/lib/Drupal/Core/Session/AccountInterface.php
+++ b/core/lib/Drupal/Core/Session/AccountInterface.php
@@ -114,7 +114,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
@@ -123,13 +133,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 401a3cf..6d70c1e 100644
--- a/core/modules/comment/comment.module
+++ b/core/modules/comment/comment.module
@@ -982,22 +982,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 9d4d8fe..0a45c16 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 340b518..4276328 100644
--- a/core/modules/contact/contact.module
+++ b/core/modules/contact/contact.module
@@ -130,7 +130,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));
@@ -158,7 +158,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 df720bc..5fd93c0 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 2c1ae66..c02fd30 100644
--- a/core/modules/file/src/Tests/FileFieldPathTest.php
+++ b/core/modules/file/src/Tests/FileFieldPathTest.php
@@ -54,7 +54,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 481e8f4..9df387e 100644
--- a/core/modules/file/src/Tests/FileTokenReplaceTest.php
+++ b/core/modules/file/src/Tests/FileTokenReplaceTest.php
@@ -52,7 +52,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 23293fe..6dccde6 100644
--- a/core/modules/forum/forum.module
+++ b/core/modules/forum/forum.module
@@ -559,7 +559,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 a8f9d50..23adcfd 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 1221147..05545e3 100644
--- a/core/modules/rdf/rdf.module
+++ b/core/modules/rdf/rdf.module
@@ -362,7 +362,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/src/Form/SwitchShortcutSet.php b/core/modules/shortcut/src/Form/SwitchShortcutSet.php
index ec3180c..c8e02b7 100644
--- a/core/modules/shortcut/src/Form/SwitchShortcutSet.php
+++ b/core/modules/shortcut/src/Form/SwitchShortcutSet.php
@@ -198,7 +198,7 @@ public function submitForm(array &$form, array &$form_state) {
       ));
       $set->save();
       $replacements = array(
-        '%user' => $this->user->label(),
+        '%user' => $this->user->getDisplayName(),
         '%set_name' => $set->label(),
         '@switch-url' => $this->url($this->routeMatch->getRouteName(), array('user' => $this->user->id())),
       );
@@ -222,7 +222,7 @@ public function submitForm(array &$form, array &$form_state) {
       /* @var \Drupal\shortcut\Entity\ShortcutSet $set */
       $set = $this->shortcutSetStorage->load($form_state['values']['set']);
       $replacements = array(
-        '%user' => $this->user->label(),
+        '%user' => $this->user->getDisplayName(),
         '%set_name' => $set->label(),
       );
       drupal_set_message($account_is_user ? $this->t('You are now using the %set_name shortcut set.', $replacements) : $this->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 461c431..f73082c 100644
--- a/core/modules/system/system.api.php
+++ b/core/modules/system/system.api.php
@@ -1269,7 +1269,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..09fc8ffd 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 ec8ac32..c55323d 100644
--- a/core/modules/user/src/Entity/User.php
+++ b/core/modules/user/src/Entity/User.php
@@ -423,7 +423,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 64ebf4e..b3ecb41 100644
--- a/core/modules/user/src/Tests/UserEditedOwnAccountTest.php
+++ b/core/modules/user/src/Tests/UserEditedOwnAccountTest.php
@@ -16,6 +16,13 @@
  */
 class UserEditedOwnAccountTest extends WebTestBase {
 
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('user_form_test');
+
   function testUserEditedOwnAccount() {
     // Change account setting 'Who can register accounts?' to Administrators
     // only.
diff --git a/core/modules/user/src/Tests/UserEntityCallbacksTest.php b/core/modules/user/src/Tests/UserEntityCallbacksTest.php
index b2f21c0..eb84d4b 100644
--- a/core/modules/user/src/Tests/UserEntityCallbacksTest.php
+++ b/core/modules/user/src/Tests/UserEntityCallbacksTest.php
@@ -22,13 +22,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;
+
   function setUp() {
     parent::setUp();
 
@@ -46,6 +51,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 fa5ac1b..0c45d83 100644
--- a/core/modules/user/src/Tests/UserTokenReplaceTest.php
+++ b/core/modules/user/src/Tests/UserTokenReplaceTest.php
@@ -58,7 +58,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);
@@ -66,7 +67,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.');
@@ -77,9 +79,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 f28b7d8..575e875 100644
--- a/core/modules/user/src/Tests/Views/HandlerFieldUserNameTest.php
+++ b/core/modules/user/src/Tests/Views/HandlerFieldUserNameTest.php
@@ -25,29 +25,27 @@ class HandlerFieldUserNameTest extends UserTestBase {
   public static $testViews = array('test_views_handler_field_user_name');
 
   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[3]->users_name;
+    $render = $view->field['name']->advancedRender($view->result[3]);
     $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 = '';
     $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 34e9f7f..9baa8b4 100644
--- a/core/modules/user/user.api.php
+++ b/core/modules/user/user.api.php
@@ -107,17 +107,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 3e84dd6..0399343 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -568,7 +568,7 @@ function user_preprocess_block(&$variables) {
  *   Use \Drupal\Core\Session\Interface::getUsername().
  */
 function user_format_name(AccountInterface $account) {
-  return $account->getUsername();
+  return $account->getDisplayName();
 }
 
 /**
@@ -624,7 +624,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;
@@ -923,8 +924,8 @@ function _user_cancel($edit, $account, $method) {
       }
       $account->block();
       $account->save();
-      drupal_set_message(t('%name has been disabled.', array('%name' => $account->getUsername())));
-      $logger->notice('Blocked user: %name %email.', array('%name' => $account->getUsername(), '%email' => '<' . $account->getEmail() . '>'));
+      drupal_set_message(t('%name has been disabled.', array('%name' => $account->getDisplayName())));
+      $logger->notice('Blocked user: %name %email.', array('%name' => $account->getDisplayName(), '%email' => '<' . $account->getEmail() . '>'));
       break;
 
     case 'user_cancel_reassign':
@@ -934,8 +935,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())));
-      $logger->notice('Deleted user: %name %email.', array('%name' => $account->getUsername(), '%email' => '<' . $account->getEmail() . '>'));
+      drupal_set_message(t('%name has been deleted.', array('%name' => $account->getDisplayName())));
+      $logger->notice('Deleted user: %name %email.', array('%name' => $account->getDisplayName(), '%email' => '<' . $account->getEmail() . '>'));
       break;
   }
 
@@ -1585,7 +1586,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'),
@@ -1619,7 +1620,7 @@ function user_toolbar() {
 function user_logout() {
   $user = \Drupal::currentUser();
 
-  \Drupal::logger('user')->notice('Session closed for %name.', array('%name' => $user->getUsername()));
+  \Drupal::logger('user')->notice('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 4b6a0ae..de3ac42 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);
-          \Drupal::logger('user')->notice('User %name used one-time login link at time %timestamp.', array('%name' => $account->getUsername(), '%timestamp' => $timestamp));
+          \Drupal::logger('user')->notice('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;
