Convert `Date/Util` to TypeScript
authorAlexander Ebert <ebert@woltlab.com>
Wed, 21 Oct 2020 23:58:01 +0000 (01:58 +0200)
committerTim Düsterhus <duesterhus@woltlab.com>
Wed, 28 Oct 2020 11:38:17 +0000 (12:38 +0100)
wcfsetup/install/files/js/WoltLabSuite/Core/Date/Util.js
wcfsetup/install/files/ts/WoltLabSuite/Core/Date/Util.js [deleted file]
wcfsetup/install/files/ts/WoltLabSuite/Core/Date/Util.ts [new file with mode: 0644]

index 1765535b4df2711b912abcd86949374be85f04df..c6b3235ab083a664713b0909c392c8cc18604eb3 100644 (file)
 /**
  * Provides utility functions for date operations.
  *
- * @author     Alexander Ebert
- * @copyright  2001-2019 WoltLab GmbH
- * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- * @module     DateUtil (alias)
- * @module     WoltLabSuite/Core/Date/Util
+ * @author  Alexander Ebert
+ * @copyright  2001-2019 WoltLab GmbH
+ * @license  GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module  DateUtil (alias)
+ * @module  WoltLabSuite/Core/Date/Util
  */
-define(['Language'], function (Language) {
+var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
+    if (k2 === undefined) k2 = k;
+    Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
+}) : (function(o, m, k, k2) {
+    if (k2 === undefined) k2 = k;
+    o[k2] = m[k];
+}));
+var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
+    Object.defineProperty(o, "default", { enumerable: true, value: v });
+}) : function(o, v) {
+    o["default"] = v;
+});
+var __importStar = (this && this.__importStar) || function (mod) {
+    if (mod && mod.__esModule) return mod;
+    var result = {};
+    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
+    __setModuleDefault(result, mod);
+    return result;
+};
+define(["require", "exports", "../Language"], function (require, exports, Language) {
     "use strict";
+    Object.defineProperty(exports, "__esModule", { value: true });
+    exports.getTimezoneDate = exports.getTimeElement = exports.gmdate = exports.format = exports.formatDateTime = exports.formatTime = exports.formatDate = void 0;
+    Language = __importStar(Language);
     /**
-     * @exports        WoltLabSuite/Core/Date/Util
+     * Returns the formatted date.
      */
-    var DateUtil = {
-        /**
-         * Returns the formatted date.
-         *
-         * @param      {Date}          date            date object
-         * @returns    {string}        formatted date
-         */
-        formatDate: function (date) {
-            return this.format(date, Language.get('wcf.date.dateFormat'));
-        },
-        /**
-         * Returns the formatted time.
-         *
-         * @param      {Date}          date            date object
-         * @returns    {string}        formatted time
-         */
-        formatTime: function (date) {
-            return this.format(date, Language.get('wcf.date.timeFormat'));
-        },
-        /**
-         * Returns the formatted date time.
-         *
-         * @param      {Date}          date            date object
-         * @returns    {string}        formatted date time
-         */
-        formatDateTime: function (date) {
-            return this.format(date, Language.get('wcf.date.dateTimeFormat').replace(/%date%/, Language.get('wcf.date.dateFormat')).replace(/%time%/, Language.get('wcf.date.timeFormat')));
-        },
-        /**
-         * Formats a date using PHP's `date()` modifiers.
-         *
-         * @param      {Date}          date            date object
-         * @param      {string}        format          output format
-         * @returns    {string}        formatted date
-         */
-        format: function (date, format) {
-            var char;
-            var out = '';
-            // ISO 8601 date, best recognition by PHP's strtotime()
-            if (format === 'c') {
-                format = 'Y-m-dTH:i:sP';
-            }
-            for (var i = 0, length = format.length; i < length; i++) {
-                switch (format[i]) {
-                    // seconds
-                    case 's':
-                        // `00` through `59`
-                        char = ('0' + date.getSeconds().toString()).slice(-2);
-                        break;
-                    // minutes
-                    case 'i':
-                        // `00` through `59`
-                        char = date.getMinutes();
-                        if (char < 10)
-                            char = "0" + char;
-                        break;
-                    // hours
-                    case 'a':
-                        // `am` or `pm`
-                        char = (date.getHours() > 11) ? 'pm' : 'am';
-                        break;
-                    case 'g':
-                        // `1` through `12`
-                        char = date.getHours();
-                        if (char === 0)
-                            char = 12;
-                        else if (char > 12)
-                            char -= 12;
-                        break;
-                    case 'h':
-                        // `01` through `12`
-                        char = date.getHours();
-                        if (char === 0)
-                            char = 12;
-                        else if (char > 12)
-                            char -= 12;
-                        char = ('0' + char.toString()).slice(-2);
-                        break;
-                    case 'A':
-                        // `AM` or `PM`
-                        char = (date.getHours() > 11) ? 'PM' : 'AM';
-                        break;
-                    case 'G':
-                        // `0` through `23`
-                        char = date.getHours();
-                        break;
-                    case 'H':
-                        // `00` through `23`
-                        char = date.getHours();
-                        char = ('0' + char.toString()).slice(-2);
-                        break;
-                    // day
-                    case 'd':
-                        // `01` through `31`
-                        char = date.getDate();
-                        char = ('0' + char.toString()).slice(-2);
-                        break;
-                    case 'j':
-                        // `1` through `31`
-                        char = date.getDate();
-                        break;
-                    case 'l':
-                        // `Monday` through `Sunday` (localized)
-                        char = Language.get('__days')[date.getDay()];
-                        break;
-                    case 'D':
-                        // `Mon` through `Sun` (localized)
-                        char = Language.get('__daysShort')[date.getDay()];
-                        break;
-                    case 'S':
-                        // ignore english ordinal suffix
-                        char = '';
-                        break;
-                    // month
-                    case 'm':
-                        // `01` through `12`
-                        char = date.getMonth() + 1;
-                        char = ('0' + char.toString()).slice(-2);
-                        break;
-                    case 'n':
-                        // `1` through `12`
-                        char = date.getMonth() + 1;
-                        break;
-                    case 'F':
-                        // `January` through `December` (localized)
-                        char = Language.get('__months')[date.getMonth()];
-                        break;
-                    case 'M':
-                        // `Jan` through `Dec` (localized)
-                        char = Language.get('__monthsShort')[date.getMonth()];
-                        break;
-                    // year
-                    case 'y':
-                        // `00` through `99`
-                        char = date.getFullYear().toString().substr(2);
-                        break;
-                    case 'Y':
-                        // Examples: `1988` or `2015`
-                        char = date.getFullYear();
-                        break;
-                    // timezone
-                    case 'P':
-                        var offset = date.getTimezoneOffset();
-                        char = (offset > 0) ? '-' : '+';
-                        offset = Math.abs(offset);
-                        char += ('0' + (~~(offset / 60)).toString()).slice(-2);
-                        char += ':';
-                        char += ('0' + (offset % 60).toString()).slice(-2);
-                        break;
-                    // specials
-                    case 'r':
-                        char = date.toString();
-                        break;
-                    case 'U':
-                        char = Math.round(date.getTime() / 1000);
-                        break;
-                    // escape sequence
-                    case '\\':
-                        char = '';
-                        if (i + 1 < length) {
-                            char = format[++i];
-                        }
-                        break;
-                    default:
-                        char = format[i];
-                        break;
-                }
-                out += char;
-            }
-            return out;
-        },
-        /**
-         * Returns UTC timestamp, if date is not given, current time will be used.
-         *
-         * @param      {Date}          date    target date
-         * @return     {int}           UTC timestamp in seconds
-         */
-        gmdate: function (date) {
-            if (!(date instanceof Date)) {
-                date = new Date();
-            }
-            return Math.round(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDay(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds()) / 1000);
-        },
-        /**
-         * Returns a `time` element based on the given date just like a `time`
-         * element created by `wcf\system\template\plugin\TimeModifierTemplatePlugin`.
-         *
-         * Note: The actual content of the element is empty and is expected
-         * to be automatically updated by `WoltLabSuite/Core/Date/Time/Relative`
-         * (for dates not in the future) after the DOM change listener has been triggered.
-         *
-         * @param      {Date}          date    displayed date
-         * @return     {HTMLElement}   `time` element
-         */
-        getTimeElement: function (date) {
-            var time = elCreate('time');
-            time.className = 'datetime';
-            var formattedDate = this.formatDate(date);
-            var formattedTime = this.formatTime(date);
-            elAttr(time, 'datetime', this.format(date, 'c'));
-            elData(time, 'timestamp', (date.getTime() - date.getMilliseconds()) / 1000);
-            elData(time, 'date', formattedDate);
-            elData(time, 'time', formattedTime);
-            elData(time, 'offset', date.getTimezoneOffset() * 60); // PHP returns minutes, JavaScript returns seconds
-            if (date.getTime() > Date.now()) {
-                elData(time, 'is-future-date', 'true');
-                time.textContent = Language.get('wcf.date.dateTimeFormat').replace('%time%', formattedTime).replace('%date%', formattedDate);
+    function formatDate(date) {
+        return format(date, Language.get('wcf.date.dateFormat'));
+    }
+    exports.formatDate = formatDate;
+    /**
+     * Returns the formatted time.
+     */
+    function formatTime(date) {
+        return format(date, Language.get('wcf.date.timeFormat'));
+    }
+    exports.formatTime = formatTime;
+    /**
+     * Returns the formatted date time.
+     */
+    function formatDateTime(date) {
+        const dateTimeFormat = Language.get('wcf.date.dateTimeFormat');
+        const dateFormat = Language.get('wcf.date.dateFormat');
+        const timeFormat = Language.get('wcf.date.timeFormat');
+        return format(date, dateTimeFormat.replace(/%date%/, dateFormat).replace(/%time%/, timeFormat));
+    }
+    exports.formatDateTime = formatDateTime;
+    /**
+     * Formats a date using PHP's `date()` modifiers.
+     */
+    function format(date, format) {
+        let char;
+        let out = '';
+        // ISO 8601 date, best recognition by PHP's strtotime()
+        if (format === 'c') {
+            format = 'Y-m-dTH:i:sP';
+        }
+        for (let i = 0, length = format.length; i < length; i++) {
+            let hours;
+            switch (format[i]) {
+                // seconds
+                case 's':
+                    // `00` through `59`
+                    char = ('0' + date.getSeconds().toString()).slice(-2);
+                    break;
+                // minutes
+                case 'i':
+                    // `00` through `59`
+                    char = date.getMinutes().toString().padStart(2, '0');
+                    break;
+                // hours
+                case 'a':
+                    // `am` or `pm`
+                    char = (date.getHours() > 11) ? 'pm' : 'am';
+                    break;
+                case 'g':
+                    // `1` through `12`
+                    hours = date.getHours();
+                    if (hours === 0)
+                        char = '12';
+                    else if (hours > 12)
+                        char = (hours - 12).toString();
+                    else
+                        char = hours.toString();
+                    break;
+                case 'h':
+                    // `01` through `12`
+                    hours = date.getHours();
+                    if (hours === 0)
+                        char = '12';
+                    else if (hours > 12)
+                        char = (hours - 12).toString();
+                    else
+                        char = hours.toString();
+                    char = char.padStart(2, '0');
+                    break;
+                case 'A':
+                    // `AM` or `PM`
+                    char = (date.getHours() > 11) ? 'PM' : 'AM';
+                    break;
+                case 'G':
+                    // `0` through `23`
+                    char = date.getHours().toString();
+                    break;
+                case 'H':
+                    // `00` through `23`
+                    char = date.getHours().toString().padStart(2, '0');
+                    break;
+                // day
+                case 'd':
+                    // `01` through `31`
+                    char = date.getDate().toString().padStart(2, '0');
+                    break;
+                case 'j':
+                    // `1` through `31`
+                    char = date.getDate().toString();
+                    break;
+                case 'l':
+                    // `Monday` through `Sunday` (localized)
+                    char = Language.get('__days')[date.getDay()];
+                    break;
+                case 'D':
+                    // `Mon` through `Sun` (localized)
+                    char = Language.get('__daysShort')[date.getDay()];
+                    break;
+                case 'S':
+                    // ignore english ordinal suffix
+                    char = '';
+                    break;
+                // month
+                case 'm':
+                    // `01` through `12`
+                    char = (date.getMonth() + 1).toString().padStart(2, '0');
+                    break;
+                case 'n':
+                    // `1` through `12`
+                    char = (date.getMonth() + 1).toString();
+                    break;
+                case 'F':
+                    // `January` through `December` (localized)
+                    char = Language.get('__months')[date.getMonth()];
+                    break;
+                case 'M':
+                    // `Jan` through `Dec` (localized)
+                    char = Language.get('__monthsShort')[date.getMonth()];
+                    break;
+                // year
+                case 'y':
+                    // `00` through `99`
+                    char = date.getFullYear().toString().substr(2);
+                    break;
+                case 'Y':
+                    // Examples: `1988` or `2015`
+                    char = date.getFullYear().toString();
+                    break;
+                // timezone
+                case 'P':
+                    let offset = date.getTimezoneOffset();
+                    char = (offset > 0) ? '-' : '+';
+                    offset = Math.abs(offset);
+                    char += ('0' + (~~(offset / 60)).toString()).slice(-2);
+                    char += ':';
+                    char += ('0' + (offset % 60).toString()).slice(-2);
+                    break;
+                // specials
+                case 'r':
+                    char = date.toString();
+                    break;
+                case 'U':
+                    char = Math.round(date.getTime() / 1000).toString();
+                    break;
+                // escape sequence
+                case '\\':
+                    char = '';
+                    if (i + 1 < length) {
+                        char = format[++i];
+                    }
+                    break;
+                default:
+                    char = format[i];
+                    break;
             }
-            return time;
-        },
-        /**
-         * Returns a Date object with precise offset (including timezone and local timezone).
-         *
-         * @param      {int}           timestamp       timestamp in milliseconds
-         * @param      {int}           offset          timezone offset in milliseconds
-         * @return     {Date}          localized date
-         */
-        getTimezoneDate: function (timestamp, offset) {
-            var date = new Date(timestamp);
-            var localOffset = date.getTimezoneOffset() * 60000;
-            return new Date((timestamp + localOffset + offset));
+            out += char;
         }
-    };
-    return DateUtil;
+        return out;
+    }
+    exports.format = format;
+    /**
+     * Returns UTC timestamp, if date is not given, current time will be used.
+     */
+    function gmdate(date) {
+        if (!(date instanceof Date)) {
+            date = new Date();
+        }
+        return Math.round(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDay(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds()) / 1000);
+    }
+    exports.gmdate = gmdate;
+    /**
+     * Returns a `time` element based on the given date just like a `time`
+     * element created by `wcf\system\template\plugin\TimeModifierTemplatePlugin`.
+     *
+     * Note: The actual content of the element is empty and is expected
+     * to be automatically updated by `WoltLabSuite/Core/Date/Time/Relative`
+     * (for dates not in the future) after the DOM change listener has been triggered.
+     */
+    function getTimeElement(date) {
+        const time = document.createElement('time');
+        time.className = 'datetime';
+        const formattedDate = formatDate(date);
+        const formattedTime = formatTime(date);
+        time.setAttribute('datetime', format(date, 'c'));
+        time.dataset.timestamp = ((date.getTime() - date.getMilliseconds()) / 1000).toString();
+        time.dataset.date = formattedDate;
+        time.dataset.time = formattedTime;
+        time.dataset.offset = (date.getTimezoneOffset() * 60).toString(); // PHP returns minutes, JavaScript returns seconds
+        if (date.getTime() > Date.now()) {
+            time.dataset.isFutureDate = 'true';
+            time.textContent = Language.get('wcf.date.dateTimeFormat')
+                .replace('%time%', formattedTime)
+                .replace('%date%', formattedDate);
+        }
+        return time;
+    }
+    exports.getTimeElement = getTimeElement;
+    /**
+     * Returns a Date object with precise offset (including timezone and local timezone).
+     */
+    function getTimezoneDate(timestamp, offset) {
+        const date = new Date(timestamp);
+        const localOffset = date.getTimezoneOffset() * 60000;
+        return new Date((timestamp + localOffset + offset));
+    }
+    exports.getTimezoneDate = getTimezoneDate;
 });
diff --git a/wcfsetup/install/files/ts/WoltLabSuite/Core/Date/Util.js b/wcfsetup/install/files/ts/WoltLabSuite/Core/Date/Util.js
deleted file mode 100644 (file)
index 63991c5..0000000
+++ /dev/null
@@ -1,273 +0,0 @@
-/**
- * Provides utility functions for date operations.
- * 
- * @author     Alexander Ebert
- * @copyright  2001-2019 WoltLab GmbH
- * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- * @module     DateUtil (alias)
- * @module     WoltLabSuite/Core/Date/Util
- */
-define(['Language'], function(Language) {
-       "use strict";
-       
-       /**
-        * @exports     WoltLabSuite/Core/Date/Util
-        */
-       var DateUtil = {
-               /**
-                * Returns the formatted date.
-                * 
-                * @param       {Date}          date            date object
-                * @returns     {string}        formatted date
-                */
-               formatDate: function(date) {
-                       return this.format(date, Language.get('wcf.date.dateFormat'));
-               },
-               
-               /**
-                * Returns the formatted time.
-                * 
-                * @param       {Date}          date            date object
-                * @returns     {string}        formatted time
-                */
-               formatTime: function(date) {
-                       return this.format(date, Language.get('wcf.date.timeFormat'));
-               },
-               
-               /**
-                * Returns the formatted date time.
-                * 
-                * @param       {Date}          date            date object
-                * @returns     {string}        formatted date time
-                */
-               formatDateTime: function(date) {
-                       return this.format(date, Language.get('wcf.date.dateTimeFormat').replace(/%date%/, Language.get('wcf.date.dateFormat')).replace(/%time%/, Language.get('wcf.date.timeFormat')));
-               },
-               
-               /**
-                * Formats a date using PHP's `date()` modifiers.
-                * 
-                * @param       {Date}          date            date object
-                * @param       {string}        format          output format
-                * @returns     {string}        formatted date
-                */
-               format: function(date, format) {
-                       var char;
-                       var out = '';
-                       
-                       // ISO 8601 date, best recognition by PHP's strtotime()
-                       if (format === 'c') {
-                               format = 'Y-m-dTH:i:sP';
-                       }
-                       
-                       for (var i = 0, length = format.length; i < length; i++) {
-                               switch (format[i]) {
-                                       // seconds
-                                       case 's':
-                                               // `00` through `59`
-                                               char = ('0' + date.getSeconds().toString()).slice(-2);
-                                               break;
-                                       
-                                       // minutes
-                                       case 'i':
-                                               // `00` through `59`
-                                               char = date.getMinutes();
-                                               if (char < 10) char = "0" + char;
-                                               break;
-                                       
-                                       // hours
-                                       case 'a':
-                                               // `am` or `pm`
-                                               char = (date.getHours() > 11) ? 'pm' : 'am';
-                                               break;
-                                       case 'g':
-                                               // `1` through `12`
-                                               char = date.getHours();
-                                               if (char === 0) char = 12;
-                                               else if (char > 12) char -= 12;
-                                               break;
-                                       case 'h':
-                                               // `01` through `12`
-                                               char = date.getHours();
-                                               if (char === 0) char = 12;
-                                               else if (char > 12) char -= 12;
-                                               
-                                               char = ('0' + char.toString()).slice(-2);
-                                               break;
-                                       case 'A':
-                                               // `AM` or `PM`
-                                               char = (date.getHours() > 11) ? 'PM' : 'AM';
-                                               break;
-                                       case 'G':
-                                               // `0` through `23`
-                                               char = date.getHours();
-                                               break;
-                                       case 'H':
-                                               // `00` through `23`
-                                               char = date.getHours();
-                                               char = ('0' + char.toString()).slice(-2);
-                                               break;
-                                       
-                                       // day
-                                       case 'd':
-                                               // `01` through `31`
-                                               char = date.getDate();
-                                               char = ('0' + char.toString()).slice(-2);
-                                               break;
-                                       case 'j':
-                                               // `1` through `31`
-                                               char = date.getDate();
-                                               break;
-                                       case 'l':
-                                               // `Monday` through `Sunday` (localized)
-                                               char = Language.get('__days')[date.getDay()];
-                                               break;
-                                       case 'D':
-                                               // `Mon` through `Sun` (localized)
-                                               char = Language.get('__daysShort')[date.getDay()];
-                                               break;
-                                       case 'S':
-                                               // ignore english ordinal suffix
-                                               char = '';
-                                               break;
-                                       
-                                       // month
-                                       case 'm':
-                                               // `01` through `12`
-                                               char = date.getMonth() + 1;
-                                               char = ('0' + char.toString()).slice(-2);
-                                               break;
-                                       case 'n':
-                                               // `1` through `12`
-                                               char = date.getMonth() + 1;
-                                               break;
-                                       case 'F':
-                                               // `January` through `December` (localized)
-                                               char = Language.get('__months')[date.getMonth()];
-                                               break;
-                                       case 'M':
-                                               // `Jan` through `Dec` (localized)
-                                               char = Language.get('__monthsShort')[date.getMonth()];
-                                               break;
-                                       
-                                       // year
-                                       case 'y':
-                                               // `00` through `99`
-                                               char = date.getFullYear().toString().substr(2);
-                                               break;
-                                       case 'Y':
-                                               // Examples: `1988` or `2015`
-                                               char = date.getFullYear();
-                                               break;
-                                       
-                                       // timezone
-                                       case 'P':
-                                               var offset = date.getTimezoneOffset();
-                                               char = (offset > 0) ? '-' : '+';
-                                               
-                                               offset = Math.abs(offset);
-                                               
-                                               char += ('0' + (~~(offset / 60)).toString()).slice(-2);
-                                               char += ':';
-                                               char += ('0' + (offset % 60).toString()).slice(-2);
-                                               
-                                               break;
-                                               
-                                       // specials
-                                       case 'r':
-                                               char = date.toString();
-                                               break;
-                                       case 'U':
-                                               char = Math.round(date.getTime() / 1000);
-                                               break;
-                                               
-                                       // escape sequence
-                                       case '\\':
-                                               char = '';
-                                               if (i + 1 < length) {
-                                                       char = format[++i];
-                                               }
-                                               break;
-                                       
-                                       default:
-                                               char = format[i];
-                                               break;
-                               }
-                               
-                               out += char;
-                       }
-                       
-                       return out;
-               },
-               
-               /**
-                * Returns UTC timestamp, if date is not given, current time will be used.
-                * 
-                * @param       {Date}          date    target date
-                * @return      {int}           UTC timestamp in seconds
-                */
-               gmdate: function(date) {
-                       if (!(date instanceof Date)) {
-                               date = new Date();
-                       }
-                       
-                       return Math.round(Date.UTC(
-                               date.getUTCFullYear(),
-                               date.getUTCMonth(),
-                               date.getUTCDay(),
-                               date.getUTCHours(),
-                               date.getUTCMinutes(),
-                               date.getUTCSeconds()
-                       ) / 1000);
-               },
-               
-               /**
-                * Returns a `time` element based on the given date just like a `time`
-                * element created by `wcf\system\template\plugin\TimeModifierTemplatePlugin`.
-                * 
-                * Note: The actual content of the element is empty and is expected
-                * to be automatically updated by `WoltLabSuite/Core/Date/Time/Relative`
-                * (for dates not in the future) after the DOM change listener has been triggered.
-                * 
-                * @param       {Date}          date    displayed date
-                * @return      {HTMLElement}   `time` element
-                */
-               getTimeElement: function(date) {
-                       var time = elCreate('time');
-                       time.className = 'datetime';
-                       
-                       var formattedDate = this.formatDate(date);
-                       var formattedTime = this.formatTime(date);
-                       
-                       elAttr(time, 'datetime', this.format(date, 'c'));
-                       elData(time, 'timestamp', (date.getTime() - date.getMilliseconds()) / 1000);
-                       elData(time, 'date', formattedDate);
-                       elData(time, 'time', formattedTime);
-                       elData(time, 'offset', date.getTimezoneOffset() * 60); // PHP returns minutes, JavaScript returns seconds
-                       
-                       if (date.getTime() > Date.now()) {
-                               elData(time, 'is-future-date', 'true');
-                               
-                               time.textContent = Language.get('wcf.date.dateTimeFormat').replace('%time%', formattedTime).replace('%date%', formattedDate);
-                       }
-                       
-                       return time;
-               },
-               
-               /**
-                * Returns a Date object with precise offset (including timezone and local timezone).
-                * 
-                * @param       {int}           timestamp       timestamp in milliseconds
-                * @param       {int}           offset          timezone offset in milliseconds
-                * @return      {Date}          localized date
-                */
-               getTimezoneDate: function(timestamp, offset) {
-                       var date = new Date(timestamp);
-                       var localOffset = date.getTimezoneOffset() * 60000;
-                       
-                       return new Date((timestamp + localOffset + offset));
-               }
-       };
-       
-       return DateUtil;
-});
diff --git a/wcfsetup/install/files/ts/WoltLabSuite/Core/Date/Util.ts b/wcfsetup/install/files/ts/WoltLabSuite/Core/Date/Util.ts
new file mode 100644 (file)
index 0000000..7373b10
--- /dev/null
@@ -0,0 +1,248 @@
+/**
+ * Provides utility functions for date operations.
+ *
+ * @author  Alexander Ebert
+ * @copyright  2001-2019 WoltLab GmbH
+ * @license  GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module  DateUtil (alias)
+ * @module  WoltLabSuite/Core/Date/Util
+ */
+
+import * as Language from '../Language';
+
+/**
+ * Returns the formatted date.
+ */
+export function formatDate(date: Date): string {
+  return format(date, Language.get('wcf.date.dateFormat'));
+}
+
+/**
+ * Returns the formatted time.
+ */
+export function formatTime(date: Date): string {
+  return format(date, Language.get('wcf.date.timeFormat'));
+}
+
+/**
+ * Returns the formatted date time.
+ */
+export function formatDateTime(date: Date): string {
+  const dateTimeFormat = Language.get('wcf.date.dateTimeFormat');
+  const dateFormat = Language.get('wcf.date.dateFormat');
+  const timeFormat = Language.get('wcf.date.timeFormat');
+
+  return format(date, dateTimeFormat.replace(/%date%/, dateFormat).replace(/%time%/, timeFormat));
+}
+
+/**
+ * Formats a date using PHP's `date()` modifiers.
+ */
+export function format(date: Date, format: string): string {
+  let char: string;
+  let out = '';
+
+  // ISO 8601 date, best recognition by PHP's strtotime()
+  if (format === 'c') {
+    format = 'Y-m-dTH:i:sP';
+  }
+
+  for (let i = 0, length = format.length; i < length; i++) {
+    let hours: number;
+
+    switch (format[i]) {
+      // seconds
+      case 's':
+        // `00` through `59`
+        char = ('0' + date.getSeconds().toString()).slice(-2);
+        break;
+
+      // minutes
+      case 'i':
+        // `00` through `59`
+        char = date.getMinutes().toString().padStart(2, '0');
+        break;
+
+      // hours
+      case 'a':
+        // `am` or `pm`
+        char = (date.getHours() > 11) ? 'pm' : 'am';
+        break;
+      case 'g':
+        // `1` through `12`
+        hours = date.getHours();
+        if (hours === 0) char = '12';
+        else if (hours > 12) char = (hours - 12).toString();
+        else char = hours.toString();
+        break;
+      case 'h':
+        // `01` through `12`
+        hours = date.getHours();
+        if (hours === 0) char = '12';
+        else if (hours > 12) char = (hours - 12).toString();
+        else char = hours.toString();
+
+        char = char.padStart(2, '0');
+        break;
+      case 'A':
+        // `AM` or `PM`
+        char = (date.getHours() > 11) ? 'PM' : 'AM';
+        break;
+      case 'G':
+        // `0` through `23`
+        char = date.getHours().toString();
+        break;
+      case 'H':
+        // `00` through `23`
+        char = date.getHours().toString().padStart(2, '0');
+        break;
+
+      // day
+      case 'd':
+        // `01` through `31`
+        char = date.getDate().toString().padStart(2, '0');
+        break;
+      case 'j':
+        // `1` through `31`
+        char = date.getDate().toString();
+        break;
+      case 'l':
+        // `Monday` through `Sunday` (localized)
+        char = Language.get('__days')[date.getDay()];
+        break;
+      case 'D':
+        // `Mon` through `Sun` (localized)
+        char = Language.get('__daysShort')[date.getDay()];
+        break;
+      case 'S':
+        // ignore english ordinal suffix
+        char = '';
+        break;
+
+      // month
+      case 'm':
+        // `01` through `12`
+        char = (date.getMonth() + 1).toString().padStart(2, '0');
+        break;
+      case 'n':
+        // `1` through `12`
+        char = (date.getMonth() + 1).toString();
+        break;
+      case 'F':
+        // `January` through `December` (localized)
+        char = Language.get('__months')[date.getMonth()];
+        break;
+      case 'M':
+        // `Jan` through `Dec` (localized)
+        char = Language.get('__monthsShort')[date.getMonth()];
+        break;
+
+      // year
+      case 'y':
+        // `00` through `99`
+        char = date.getFullYear().toString().substr(2);
+        break;
+      case 'Y':
+        // Examples: `1988` or `2015`
+        char = date.getFullYear().toString();
+        break;
+
+      // timezone
+      case 'P':
+        let offset = date.getTimezoneOffset();
+        char = (offset > 0) ? '-' : '+';
+
+        offset = Math.abs(offset);
+
+        char += ('0' + (~~(offset / 60)).toString()).slice(-2);
+        char += ':';
+        char += ('0' + (offset % 60).toString()).slice(-2);
+
+        break;
+
+      // specials
+      case 'r':
+        char = date.toString();
+        break;
+      case 'U':
+        char = Math.round(date.getTime() / 1000).toString();
+        break;
+
+      // escape sequence
+      case '\\':
+        char = '';
+        if (i + 1 < length) {
+          char = format[++i];
+        }
+        break;
+
+      default:
+        char = format[i];
+        break;
+    }
+
+    out += char;
+  }
+
+  return out;
+}
+
+/**
+ * Returns UTC timestamp, if date is not given, current time will be used.
+ */
+export function gmdate(date: Date): number {
+  if (!(date instanceof Date)) {
+    date = new Date();
+  }
+
+  return Math.round(Date.UTC(
+    date.getUTCFullYear(),
+    date.getUTCMonth(),
+    date.getUTCDay(),
+    date.getUTCHours(),
+    date.getUTCMinutes(),
+    date.getUTCSeconds(),
+  ) / 1000);
+}
+
+/**
+ * Returns a `time` element based on the given date just like a `time`
+ * element created by `wcf\system\template\plugin\TimeModifierTemplatePlugin`.
+ *
+ * Note: The actual content of the element is empty and is expected
+ * to be automatically updated by `WoltLabSuite/Core/Date/Time/Relative`
+ * (for dates not in the future) after the DOM change listener has been triggered.
+ */
+export function getTimeElement(date: Date): HTMLElement {
+  const time = document.createElement('time');
+  time.className = 'datetime';
+
+  const formattedDate = formatDate(date);
+  const formattedTime = formatTime(date);
+
+  time.setAttribute('datetime', format(date, 'c'));
+  time.dataset.timestamp = ((date.getTime() - date.getMilliseconds()) / 1000).toString();
+  time.dataset.date = formattedDate;
+  time.dataset.time = formattedTime;
+  time.dataset.offset = (date.getTimezoneOffset() * 60).toString(); // PHP returns minutes, JavaScript returns seconds
+
+  if (date.getTime() > Date.now()) {
+    time.dataset.isFutureDate = 'true';
+
+    time.textContent = Language.get('wcf.date.dateTimeFormat')
+      .replace('%time%', formattedTime)
+      .replace('%date%', formattedDate);
+  }
+
+  return time;
+}
+
+/**
+ * Returns a Date object with precise offset (including timezone and local timezone).
+ */
+export function getTimezoneDate(timestamp: number, offset: number): Date {
+  const date = new Date(timestamp);
+  const localOffset = date.getTimezoneOffset() * 60000;
+
+  return new Date((timestamp + localOffset + offset));
+}