diff --git a/README.md b/README.md index 21fc252..d812bad 100644 --- a/README.md +++ b/README.md @@ -12,9 +12,11 @@ Mwn works with both JavaScript and TypeScript. It is created with a design philosophy of allowing bot developers to easily and quickly write bot code, without having to deal with the MediaWiki API complications and idiosyncrasies such as logins, tokens, maxlag, query continuations and error handling. [Making raw API calls](https://mwn.toolforge.org/docs/direct-api-calls) is also supported for complete flexibility where needed. Mwn uses [JSON with formatversion 2](https://www.mediawiki.org/wiki/API:JSON_version_2#Using_the_new_JSON_results_format) by default. The [axios](https://www.npmjs.com/package/axios) library is used for HTTP requests. +This library provides TypeScript type definitions for all its functions, as well as for MediaWiki API request objects (MW core + several extensions). API responses are also typed for the common operations. + In addition to the MediaWiki Action API, the library also provides methods to talk to the Wikimedia EventStreams API, the ORES API, Pageviews API and WikiWho API. -This library uses mocha for tests and has extensive test coverage covering all commonly used code paths. Testing is automated using a CI workflow on Github Actions. +This library uses mocha for tests and has extensive test coverage. Testing is automated using a CI workflow on GitHub Actions. To install, run `npm install mwn`. diff --git a/src/date.ts b/src/date.ts index 0f07ddc..9b73c00 100644 --- a/src/date.ts +++ b/src/date.ts @@ -25,7 +25,7 @@ export interface MwnDate extends Date { * @param {number} number - should be an integer * @param {string} unit * @throws {Error} if invalid or unsupported unit is given - * @returns {XDate} + * @returns {MwnDate} */ add(number: number, unit: timeUnit): MwnDate; @@ -35,13 +35,38 @@ export interface MwnDate extends Date { * @param {number} number - should be an integer * @param {string} unit * @throws {Error} if invalid or unsupported unit is given - * @returns {XDate} + * @returns {MwnDate} */ subtract(number: number, unit: timeUnit): MwnDate; /** * Formats the date into a string per the given format string. - * Replacement syntax is a subset of that in moment.js. + * Replacement syntax is a subset of that in moment.js: + * + * | Syntax | Output | + * |--------|--------| + * | H | Hours (24-hour) | + * | HH | Hours (24-hour, padded to 2 digits) | + * | h | Hours (12-hour) | + * | hh | Hours (12-hour, padded to 2 digits) | + * | A | AM or PM | + * | m | Minutes | + * | mm | Minutes (padded to 2 digits) | + * | s | Seconds | + * | ss | Seconds (padded to 2 digits) | + * | d | Day number of the week (Sun=0) | + * | ddd | Abbreviated day name | + * | dddd | Full day name | + * | D | Date | + * | DD | Date (padded to 2 digits) | + * | M | Month number (1-indexed) | + * | MM | Month number (1-indexed, padded to 2 digits) | + * | MMM | Abbreviated month name | + * | MMMM | Full month name | + * | Y | Year | + * | YY | Final two digits of year (20 for 2020, 42 for 1942) | + * | YYYY | Year (same as `Y`) | + * * @param {string} formatstr * @param {(string|number)} [zone=utc] - 'system' (for system-default time zone), * 'utc' (for UTC), or specify a time zone as number of minutes past UTC. @@ -80,6 +105,16 @@ export interface MwnDateStatic { * Get abbreviated day name from day number (1-indexed, starting from Sunday) */ getDayNameAbbrev(dayNum: number): string; + + localeData: any; + + /** + * Customize language used for day and month names. This fetches the + * the data from MediaWiki API. By default it is English. + * @param lang - Defaults to the content language of the wiki the bot + * is logged into. + */ + populateLocaleData(lang?: string): Promise; } export default function (bot: mwn) { @@ -298,6 +333,24 @@ export default function (bot: mwn) { }, }; + /** @inheritDoc */ + static async populateLocaleData(lang?: string) { + const monthsKeys = 'january|february|march|april|may_long|june|july|august|september|october|november|december'.split( + '|', + ); + const monthsShortKeys = 'jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec'.split('|'); + const daysKeys = 'sunday|monday|tuesday|wednesday|thursday|friday|saturday'.split('|'); + const daysShortKeys = 'sun|mon|tue|wed|thu|fri|sat'.split('|'); + + const messages = await bot.getMessages([...monthsKeys, ...monthsShortKeys, ...daysKeys, ...daysShortKeys], { + amlang: lang || 'content', + }); + this.localeData.months = monthsKeys.map((key) => messages[key]); + this.localeData.monthsShort = monthsShortKeys.map((key) => messages[key]); + this.localeData.days = daysKeys.map((key) => messages[key]); + this.localeData.daysShort = daysShortKeys.map((key) => messages[key]); + } + /** @inheritDoc */ static getMonthName(monthNum: number): string { return XDate.localeData.months[monthNum - 1]; diff --git a/tests/date.test.js b/tests/date.test.js index 18c4587..e5b766b 100644 --- a/tests/date.test.js +++ b/tests/date.test.js @@ -67,4 +67,13 @@ describe('date', async function () { expect(bot.date.getMonthName(1)).to.eq('January'); expect(bot.date.getMonthNameAbbrev(1)).to.eq('Jan'); }); + + it('loads locale data using API', async function () { + bot.options.defaultParams.assert = undefined; // avoid sign-in + await bot.date.populateLocaleData('fr'); + expect(bot.date.localeData.months[0]).to.equal('janvier'); + expect(bot.date.localeData.monthsShort[0]).to.equal('janv.'); + expect(bot.date.localeData.days[0]).to.equal('dimanche'); + expect(bot.date.localeData.daysShort[0]).to.equal('dim.'); + }); }); diff --git a/website/docs/13-dates.md b/website/docs/13-dates.md index 25ba699..95ca83d 100644 --- a/website/docs/13-dates.md +++ b/website/docs/13-dates.md @@ -3,12 +3,14 @@ Mwn provides a rich wrapper around the native [Date](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) interface. The constructor support the common MediaWiki datetime formats: + ```js const date1 = new bot.date('13:45, 2 April 2020 (UTC)'); // This won't parse with JS native Date! const date2 = new bot.date('20210304134567'); // MW database timestamp format. ``` in addition to everything that native JS Date supports: + ```js const date1 = new bot.date(); const date2 = new bot.date('3 December 2020'); @@ -17,13 +19,14 @@ const date2 = new bot.date('3 December 2020'); Note that it inherits the weirdities of JS Date - even "NY 12345" gets parsed as valid date, so per [MDN recommendation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/Date#timestamp_string) you should not parse dates as strings of unknown format. All methods on native date are inherited, for instance: + ```js date.getUTCDate(); date.toISOString(); ``` +But in addition, you can get names of the months and days of the week (by default in English): -But in addition, you can get names of the months and days of the week (in English): ```js date.getMonthName(); date.getUTCMonthName(); @@ -31,7 +34,31 @@ date.getDayName(); date.getUTCDayName(); ``` -Add and subtract dates. This mutates the original date as well as returns the mutated object to allow chaining. The supported units are seconds, minutes, hours, days, weeks, months and years. +**Format dates to text**: (See for the full formatting syntax) + +```js +date.format('D MMMM YYYY'); // -> "3 December 2020" +``` + +:::info +By default, month and day names are in English. However, you can customise the language used by overwriting `bot.date.localeData` object. The easiest way to do that is via `bot.date.populateLocaleData()`. + +```js +await bot.date.populateLocaleData(); // use content language of the wiki +await bot.date.populateLocaleData('fr'); // OR explicitly set the language like this +``` + +The data is stored in the bot object. If you have multiple bots signed in to different wikis, you can use different languages for each. +::: + +Get human-readable description of dates, such as "Yesterday at 6:43 PM" or "Last Thursday at 11:45 AM": + +```js +console.log(`Last modified ${date.calendar()}`); // -> Last modified Yesterday at 6:43 PM +``` + +**Add and subtract dates**: This mutates the original date as well as returns the mutated object to allow chaining. The supported units are seconds, minutes, hours, days, weeks, months and years. + ```js date.add(1, 'hour'); date.add(4, 'hours'); @@ -39,6 +66,7 @@ date.subtract(5, 'hours').subtract(30, 'minutes'); ``` Check if a date is before or after another date (which can be either an Mwn date or a normal Date object). + ```js date.isBefore(new Date()); // boolean date.isAfter(new bot.date());