注意:在发布之后,您可能需要清除浏览器缓存才能看到所作出的更改的影响。

  • Firefox或Safari:按住Shift的同时单击刷新,或按Ctrl-F5Ctrl-R(Mac为⌘-R
  • Google Chrome:Ctrl-Shift-R(Mac为⌘-Shift-R
  • Edge:按住Ctrl的同时单击刷新,或按Ctrl-F5
/**  * COMMENTS IN LOCAL TIME  *  * Description:  * Changes [[Coordinated Universal Time|UTC]]-based times and dates,  * such as those used in signatures, to be relative to local time.  *  * Documentation:  * [[Wikipedia:Comments in Local Time]]  */ $(() => {   /**    * Given a number, add a leading zero if necessary, so that the final number    * has two characters.    *    * @param {number} number Number    * @returns {string} The number with a leading zero, if necessary.    */   function addLeadingZero(number) {     const numberArg = number;      if (numberArg < 10) {       return `0${numberArg}`;     }      return numberArg;   }    function convertMonthToNumber(month) {     return new Date(`${month} 1, 2001`).getMonth();   }    function getDates(time) {     const [, oldHour, oldMinute, oldDay, oldMonth, oldYear] = time;      // Today     const today = new Date();      // Yesterday     const yesterday = new Date();      yesterday.setDate(yesterday.getDate() - 1);      // Tomorrow     const tomorrow = new Date();      tomorrow.setDate(tomorrow.getDate() + 1);      // Set the date entered.     const newTime = new Date();      newTime.setUTCFullYear(oldYear, convertMonthToNumber(oldMonth), oldDay);     newTime.setUTCHours(oldHour);     newTime.setUTCMinutes(oldMinute);      return { time: newTime, today, tomorrow, yesterday };   }    /**    * Determine whether to use the singular or plural word, and use that.    *    * @param {string} term Original term    * @param {number} count Count of items    * @param {string} plural Pluralized term    * @returns {string} The word to use    */   function pluralize(term, count, plural = null) {     let pluralArg = plural;      // No unique pluralized word is found, so just use a general one.     if (!pluralArg) {       pluralArg = `${term}s`;     }      // There's only one item, so just use the singular word.     if (count === 1) {       return term;     }      // There are multiple items, so use the plural word.     return pluralArg;   }    class CommentsInLocalTime {     constructor() {       this.language = '';       this.LocalComments = {};        /**        * Settings        */       this.settings();        this.language = this.setDefaultSetting(         'language',         this.LocalComments.language       );        // These values are also reflected in the documentation:       // https://en.wikipedia.org/wiki/Wikipedia:Comments_in_Local_Time#Default_settings       this.setDefaultSetting({         dateDifference: true,         dateFormat: 'dmy',         dayOfWeek: true,         dropDays: 0,         dropMonths: 0,         timeFirst: true,         twentyFourHours: false,       });     }      adjustTime(originalTimestamp, search) {       const { time, today, tomorrow, yesterday } = getDates(         originalTimestamp.match(search)       );        // A string matching the date pattern was found, but it cannot be       // converted to a Date object. Return it with no changes made.       if (Number.isNaN(time)) {         return [originalTimestamp, ''];       }        const date = this.determineDateText({         time,         today,         tomorrow,         yesterday,       });        const { ampm, hour } = this.getHour(time);       const minute = addLeadingZero(time.getMinutes());       const finalTime = `${hour}:${minute}${ampm}`;       let returnDate;        // Determine the time offset.       const utcValue = (-1 * time.getTimezoneOffset()) / 60;       const utcOffset =         utcValue >= 0 ? `+${utcValue}` : `−${Math.abs(utcValue.toFixed(1))}`;        if (this.LocalComments.timeFirst) {         returnDate = `${finalTime}, ${date} (UTC${utcOffset})`;       } else {         returnDate = `${date}, ${finalTime} (UTC${utcOffset})`;       }        return { returnDate, time };     }      convertNumberToMonth(number) {       return [         this.language.January,         this.language.February,         this.language.March,         this.language.April,         this.language.May,         this.language.June,         this.language.July,         this.language.August,         this.language.September,         this.language.October,         this.language.November,         this.language.December,       ][number];     }      createDateText({ day, month, time, today, year }) {       // calculate day of week       const dayNames = [         this.language.Sunday,         this.language.Monday,         this.language.Tuesday,         this.language.Wednesday,         this.language.Thursday,         this.language.Friday,         this.language.Saturday,       ];       const dayOfTheWeek = dayNames[time.getDay()];       let descriptiveDifference = '';       let last = '';        // Create a relative descriptive difference       if (this.LocalComments.dateDifference) {         ({ descriptiveDifference, last } = this.createRelativeDate(           today,           time         ));       }        const monthName = this.convertNumberToMonth(time.getMonth());        // format the date according to user preferences       let formattedDate = '';        switch (this.LocalComments.dateFormat.toLowerCase()) {         case 'dmy':           formattedDate = `${day} ${monthName} ${year}`;            break;         case 'mdy':           formattedDate = `${monthName} ${day}, ${year}`;            break;         default:           formattedDate = `${year}-${month}-${addLeadingZero(day)}`;       }        let formattedDayOfTheWeek = '';        if (this.LocalComments.dayOfWeek) {         formattedDayOfTheWeek = `, ${last}${dayOfTheWeek}`;       }        return formattedDate + formattedDayOfTheWeek + descriptiveDifference;     }      /**      * Create relative date data.      *      * @param {Date} today Today      * @param {Date} time The timestamp from a comment      * @returns {Object.<string, *>} Relative date data      */     // eslint-disable-next-line max-statements     createRelativeDate(today, time) {       /**        * The time difference from today, in milliseconds.        *        * @type {number}        */       const millisecondsAgo = today.getTime() - time.getTime();        /**        * The number of days ago, that we will display. It's not necessarily the        * total days ago.        *        * @type {number}        */       let daysAgo = Math.abs(Math.round(millisecondsAgo / 1000 / 60 / 60 / 24));       const { differenceWord, last } = this.relativeText({         daysAgo,         millisecondsAgo,       });        // This method of computing the years and months is not exact. However,       // it's better than the previous method that used 1 January + delta days.       // That was usually quite off because it mapped the second delta month to       // February, which has only 28 days. This method is usually not more than       // one day off, except perhaps over very distant dates.        /**        * The number of months ago, that we will display. It's not necessarily        * the total months ago.        *        * @type {number}        */       let monthsAgo = Math.floor((daysAgo / 365) * 12);        /**        * The total amount of time ago, in months.        *        * @type {number}        */       const totalMonthsAgo = monthsAgo;        /**        * The number of years ago that we will display. It's not necessarily the        * total years ago.        *        * @type {number}        */       let yearsAgo = Math.floor(totalMonthsAgo / 12);        if (totalMonthsAgo < this.LocalComments.dropMonths) {         yearsAgo = 0;       } else if (this.LocalComments.dropMonths > 0) {         monthsAgo = 0;       } else {         monthsAgo -= yearsAgo * 12;       }        if (daysAgo < this.LocalComments.dropDays) {         monthsAgo = 0;         yearsAgo = 0;       } else if (this.LocalComments.dropDays > 0 && totalMonthsAgo >= 1) {         daysAgo = 0;       } else {         daysAgo -= Math.floor((totalMonthsAgo * 365) / 12);       }        const descriptiveParts = [];        // There is years text to add.       if (yearsAgo > 0) {         descriptiveParts.push(           `${yearsAgo} ${pluralize(             this.language.year,             yearsAgo,             this.language.years           )}`         );       }        // There is months text to add.       if (monthsAgo > 0) {         descriptiveParts.push(           `${monthsAgo} ${pluralize(             this.language.month,             monthsAgo,             this.language.months           )}`         );       }        // There is days text to add.       if (daysAgo > 0) {         descriptiveParts.push(           `${daysAgo} ${pluralize(             this.language.day,             daysAgo,             this.language.days           )}`         );       }        return {         descriptiveDifference: ` (${descriptiveParts.join(           ', '         )} ${differenceWord})`,         last,       };     }      determineDateText({ time, today, tomorrow, yesterday }) {       // Set the date bits to output.       const year = time.getFullYear();       const month = addLeadingZero(time.getMonth() + 1);       const day = time.getDate();        // return 'today' or 'yesterday' if that is the case       if (         year === today.getFullYear() &&         month === addLeadingZero(today.getMonth() + 1) &&         day === today.getDate()       ) {         return this.language.Today;       }        if (         year === yesterday.getFullYear() &&         month === addLeadingZero(yesterday.getMonth() + 1) &&         day === yesterday.getDate()       ) {         return this.language.Yesterday;       }        if (         year === tomorrow.getFullYear() &&         month === addLeadingZero(tomorrow.getMonth() + 1) &&         day === tomorrow.getDate()       ) {         return this.language.Tomorrow;       }        return this.createDateText({ day, month, time, today, year });     }      getHour(time) {       let ampm;       let hour = parseInt(time.getHours(), 10);        if (this.LocalComments.twentyFourHours) {         ampm = '';         hour = addLeadingZero(hour);       } else {         // Output am or pm depending on the date.         ampm = hour <= 11 ? ' am' : ' pm';          if (hour > 12) {           hour -= 12;         } else if (hour === 0) {           hour = 12;         }       }        return { ampm, hour };     }      relativeText({ daysAgo, millisecondsAgo }) {       let differenceWord = '';       let last = '';        // The date is in the past.       if (millisecondsAgo >= 0) {         differenceWord = this.language.ago;          if (daysAgo <= 7) {           last = `${this.language.last} `;         }          // The date is in the future.       } else {         differenceWord = this.language['from now'];          if (daysAgo <= 7) {           last = `${this.language.this} `;         }       }        return { differenceWord, last };     }      // eslint-disable-next-line max-statements     replaceText(node, search) {       if (!node) {         return;       }        // Check if this is a text node.       if (node.nodeType === 3) {         let parent = node.parentNode;          const parentNodeName = parent.nodeName;          if (['CODE', 'PRE'].includes(parentNodeName)) {           return;         }          const value = node.nodeValue;         const matches = value.match(search);          // Stick with manipulating the DOM directly rather than using jQuery.         // I've got more than a 100% speed improvement afterward.         if (matches) {           // Only act on the first timestamp we found in this node. This is           // really a temporary fix for the situation in which there are two or           // more timestamps in the same node.           const [match] = matches;           const position = value.search(search);           const stringLength = match.toString().length;           const beforeMatch = value.substring(0, position);           const afterMatch = value.substring(position + stringLength);           const { returnDate, time } = this.adjustTime(             match.toString(),             search           );           const timestamp = time ? time.getTime() : '';            // Is the "timestamp" attribute used for microformats?           const span = document.createElement('span');            span.className = 'localcomments';           span.style.fontSize = '95%';           span.style.whiteSpace = 'nowrap';           span.setAttribute('timestamp', timestamp);           span.title = match;           span.append(document.createTextNode(returnDate));            parent = node.parentNode;           parent.replaceChild(span, node);            const before = document.createElement('span');            before.className = 'before-localcomments';           before.append(document.createTextNode(beforeMatch));            const after = document.createElement('span');            after.className = 'after-localcomments';           after.append(document.createTextNode(afterMatch));            parent.insertBefore(before, span);           parent.insertBefore(after, span.nextSibling);         }       } else {         const children = [];         let child;          [child] = node.childNodes;          while (child) {           children.push(child);           child = child.nextSibling;         }          // Loop through children and run this func on it again, recursively.         children.forEach((child2) => {           this.replaceText(child2, search);         });       }     }      run() {       if (         ['', 'MediaWiki', 'Special'].includes(           mw.config.get('wgCanonicalNamespace')         )       ) {         return;       }        // Check for disabled URLs.       const isDisabledUrl = ['action=history'].some((disabledUrl) =>         document.location.href.includes(disabledUrl)       );        if (isDisabledUrl) {         return;       }        this.replaceText(         document.querySelector('.mw-parser-output'),         /(\d{1,2}):(\d{2}), (\d{1,2}) ([A-Z][a-z]+) (\d{4}) \(UTC\)/       );     }      setDefaultSetting(...args) {       // There are no arguments.       if (args.length === 0) {         return false;       }        // The first arg is an object, so just set that data directly onto the       // settings object. like {setting 1: true, setting 2: false}       if (typeof args[0] === 'object') {         const [settings] = args;          // Loop through each setting.         Object.keys(settings).forEach((name) => {           const value = settings[name];            if (typeof this.LocalComments[name] === 'undefined') {             this.LocalComments[name] = value;           }         });          return settings;       }        // The first arg is a string, so use the first arg as the settings key,       // and the second arg as the value to set it to.       const [name, setting] = args;        if (typeof this.LocalComments[name] === 'undefined') {         this.LocalComments[name] = setting;       }        return this.LocalComments[name];     }      /**      * Set the script's settings.      *      * @returns {undefined}      */     settings() {       // The user has set custom settings, so use those.       if (window.LocalComments) {         this.LocalComments = window.LocalComments;       }        /**        * Language        *        * LOCALIZING THIS SCRIPT        * To localize this script, change the terms below,        * to the RIGHT of the colons, to the correct term used in that language.        *        * For example, in the French language,        *        * 'Today' : 'Today',        *        * would be        *        * 'Today' : "Aujourd'hui",        */       this.LocalComments.language = {         // Relative terms         Today: 'Today',         Yesterday: 'Yesterday',         Tomorrow: 'Tomorrow',         last: 'last',         this: 'this',          // Days of the week         Sunday: 'Sunday',         Monday: 'Monday',         Tuesday: 'Tuesday',         Wednesday: 'Wednesday',         Thursday: 'Thursday',         Friday: 'Friday',         Saturday: 'Saturday',          // Months of the year         January: 'January',         February: 'February',         March: 'March',         April: 'April',         May: 'May',         June: 'June',         July: 'July',         August: 'August',         September: 'September',         October: 'October',         November: 'November',         December: 'December',          // Difference words         ago: 'ago',         'from now': 'from now',          // Date phrases         year: 'year',         years: 'years',         month: 'month',         months: 'months',         day: 'day',         days: 'days',       };     }   }    // Check if we've already ran this script.   if (window.commentsInLocalTimeWasRun) {     return;   }    window.commentsInLocalTimeWasRun = true;    const commentsInLocalTime = new CommentsInLocalTime();    commentsInLocalTime.run(); });