diff --git a/include/ajax.config.php b/include/ajax.config.php
index a4e11bf22ab1ea5bebbab361604edbf71e569065..c259b6028f2ab42b3d615e0e097c5f5b3db7bb1c 100644
--- a/include/ajax.config.php
+++ b/include/ajax.config.php
@@ -97,5 +97,35 @@ class ConfigAjaxAPI extends AjaxController {
 
         return $links;
     }
+
+    /**
+     * Ajax: GET /config/date-format?format=<format>
+     *
+     * Formats the user's current date and time according to the given
+     * format in INTL codes.
+     *
+     * Get-Arguments:
+     * format - (string) format string used to format the current date and
+     *      time (from the user's perspective)
+     *
+     * Returns:
+     * (string) Current sequence number, optionally formatted
+     *
+     * Throws:
+     * 403 - Not logged in
+     * 400 - ?format missing
+     */
+    function dateFormat() {
+        global $thisstaff;
+
+        if (!$thisstaff)
+            Http::response(403, 'Login required');
+        elseif (!isset($_GET['format']))
+            Http::response(400, '?format is required');
+
+        return Format::htmlchars(Format::__formatDate(
+            Misc::gmtime(), $_GET['format'], false, null, null, '', 'UTC'
+        ));
+    }
 }
 ?>
diff --git a/include/class.format.php b/include/class.format.php
index 30e5a0197572478a208d0a993d85d8df484c8b4d..d418bf762203715c8732dbb03701bf4552bb46f2 100644
--- a/include/class.format.php
+++ b/include/class.format.php
@@ -533,7 +533,12 @@ class Format {
     }
 
     function getStrftimeFormat($format) {
-        static $dateToStrftime = array(
+        static $codes, $ids;
+
+        if (!isset($codes)) {
+            // This array is flipped because of duplicated formats on the
+            // intl side due to slight differences in the libraries
+            $codes = array(
             '%d' => 'dd',
             '%a' => 'EEE',
             '%e' => 'd',
@@ -563,17 +568,24 @@ class Format {
 
             '%z' => 'ZZZ',
             '%Z' => 'z',
-        );
+            );
 
-        $flipped = array_flip($dateToStrftime);
-        krsort($flipped);
-        // Also establish a list of ids, so we can do a creative replacement
-        // without clobbering the common letters in the formats
-        $ids = array_keys($flipped);
-        $ids = array_flip($ids);
-        foreach ($flipped as $icu=>$date) {
-            $format = str_replace($date, chr($ids[$icu]), $format);
+            $flipped = array_flip($codes);
+            krsort($flipped);
+
+            // Also establish a list of ids, so we can do a creative replacement
+            // without clobbering the common letters in the formats
+            $keys = array_keys($flipped);
+            $ids = array_combine($keys, array_map('chr', array_flip($keys)));
+
+            // Now create an array from the id codes back to strftime codes
+            $codes = array_combine($ids, $flipped);
         }
+        // $ids => array(intl => #id)
+        // $codes => array(#id => strftime)
+        $format = str_replace(array_keys($ids), $ids, $format);
+        $format = str_replace($ids, $codes, $format);
+
         return preg_replace_callback('`[\x00-\x1f]`',
             function($m) use ($ids) {
                 return $ids[ord($m[0])];
diff --git a/include/staff/profile.inc.php b/include/staff/profile.inc.php
index a8ba88a3ecce4a8a2aece8139c10ba1774c67104..c942f984160efc0e6e12acc0bc87a27f5ee00d9b 100644
--- a/include/staff/profile.inc.php
+++ b/include/staff/profile.inc.php
@@ -112,6 +112,7 @@ $info['id']=$staff->getId();
             </td>
         </tr>
 <?php } ?>
+<?php if (extension_loaded('intl')) { ?>
         <tr><td width="220"><?php echo __('Preferred Locale');?>:</td>
             <td>
                 <select name="locale">
@@ -125,6 +126,7 @@ $info['id']=$staff->getId();
                 </select>
             </td>
         </tr>
+<?php } ?>
         <tr>
             <td width="180"><?php echo __('Maximum Page size');?>:</td>
             <td>
diff --git a/include/staff/settings-system.inc.php b/include/staff/settings-system.inc.php
index e9a261a213de2326fa45359c5f204f9387956d35..8063c1d15fae50ae7ed94a8165082625581faf7a 100644
--- a/include/staff/settings-system.inc.php
+++ b/include/staff/settings-system.inc.php
@@ -127,6 +127,7 @@ $gmtime = Misc::gmtime();
                 </em>
             </th>
         </tr>
+<?php if (extension_loaded('intl')) { ?>
         <tr><td width="220" class="required"><?php echo __('Default Locale');?>:</td>
             <td>
                 <select name="default_locale">
@@ -143,6 +144,7 @@ $gmtime = Misc::gmtime();
                 </select>
             </td>
         </tr>
+<?php } ?>
         <tr><td width="220" class="required"><?php echo __('Default Time Zone');?>:</td>
             <td>
                 <?php
@@ -178,26 +180,39 @@ $gmtime = Misc::gmtime();
         <tr>
             <td width="220" class="indented required"><?php echo __('Time Format');?>:</td>
             <td>
-                <input type="text" name="time_format" value="<?php echo $config['time_format']; ?>">
+                <input type="text" name="time_format" value="<?php echo $config['time_format']; ?>" class="date-format-preview">
                     &nbsp;<font class="error">*&nbsp;<?php echo $errors['time_format']; ?></font>
-                    <em><?php echo Format::time(null, false); ?></em></td>
+                    <em><?php echo Format::time(null, false); ?></em>
+                <span class="faded date-format-preview" data-for="time_format">
+                    <?php echo Format::time('now'); ?>
+                </span>
+            </td>
         </tr>
         <tr><td width="220" class="indented required"><?php echo __('Date Format');?>:</td>
-            <td><input type="text" name="date_format" value="<?php echo $config['date_format']; ?>">
+            <td><input type="text" name="date_format" value="<?php echo $config['date_format']; ?>" class="date-format-preview">
                         &nbsp;<font class="error">*&nbsp;<?php echo $errors['date_format']; ?></font>
                         <em><?php echo Format::date(null, false); ?></em>
+                <span class="faded date-format-preview" data-for="date_format">
+                    <?php echo Format::date('now'); ?>
+                </span>
             </td>
         </tr>
         <tr><td width="220" class="indented required"><?php echo __('Date and Time Format');?>:</td>
-            <td><input type="text" name="datetime_format" value="<?php echo $config['datetime_format']; ?>">
+            <td><input type="text" name="datetime_format" value="<?php echo $config['datetime_format']; ?>" class="date-format-preview">
                         &nbsp;<font class="error">*&nbsp;<?php echo $errors['datetime_format']; ?></font>
                         <em><?php echo Format::datetime(null, false); ?></em>
+                <span class="faded date-format-preview" data-for="datetime_format">
+                    <?php echo Format::datetime('now'); ?>
+                </span>
             </td>
         </tr>
         <tr><td width="220" class="indented required"><?php echo __('Day, Date and Time Format');?>:</td>
-            <td><input type="text" name="daydatetime_format" value="<?php echo $config['daydatetime_format']; ?>">
+            <td><input type="text" name="daydatetime_format" value="<?php echo $config['daydatetime_format']; ?>" class="date-format-preview">
                         &nbsp;<font class="error">*&nbsp;<?php echo $errors['daydatetime_format']; ?></font>
                         <em><?php echo Format::daydatetime(null, false); ?></em>
+                <span class="faded date-format-preview" data-for="daydatetime_format">
+                    <?php echo Format::daydatetime('now'); ?>
+                </span>
             </td>
         </tr>
     </tbody>
@@ -278,5 +293,17 @@ $(function() {
     $('#secondary_langs').sortable({
         cursor: 'move'
     });
+    var prev = [];
+    $('input.date-format-preview').keyup(function() {
+        var name = $(this).attr('name'),
+            div = $('span.date-format-preview[data-for='+name+']'),
+            current = $(this).val();
+        if (prev[name] && prev[name] == current)
+            return;
+        prev[name] = current;
+        div.text('...');
+        $.get('ajax.php/config/date-format', {format:$(this).val()})
+            .done(function(html) { div.html(html); });
+    });
 });
 </script>
diff --git a/scp/ajax.php b/scp/ajax.php
index 54e2770736488081832ac325a9a345e94bb5081d..054ef105068f6b36eb92797b4cac48467f78c066 100644
--- a/scp/ajax.php
+++ b/scp/ajax.php
@@ -50,7 +50,8 @@ $dispatcher = patterns('',
     )),
     url('^/config/', patterns('ajax.config.php:ConfigAjaxAPI',
         url_get('^scp', 'scp'),
-        url_get('^links', 'templateLinks')
+        url_get('^links', 'templateLinks'),
+        url_get('^date-format', 'dateFormat')
     )),
     url('^/form/', patterns('ajax.forms.php:DynamicFormsAjaxAPI',
         url_get('^help-topic/(?P<id>\d+)$', 'getFormsForHelpTopic'),