Fix some issues with X Rank season calculation and add season indexes

This commit is contained in:
Samuel Elliott 2022-08-21 20:19:22 +01:00
parent 898cb6f707
commit 67819b4af5
No known key found for this signature in database
GPG Key ID: 8420C7CDE43DC4D6
3 changed files with 98 additions and 18 deletions

View File

@ -106,7 +106,7 @@ const season_id = toXRankSeasonId(2022, 1);
### `getXRankSeasons`
Returns a generator that yields all X Rank seasons up to the current season according to the system date.
Returns an iterator that yields all X Rank seasons up to the current season according to the system date.
By default this will start from the current season and continue to the first season. Setting the first argument to `true` will cause this function to start from the first season and continue to the current season.
@ -116,11 +116,16 @@ import { getXRankSeasons, XRankSeason } from 'nxapi/splatnet2';
for (const season of getXRankSeasons()) {
// season is a plain object of type XRankSeason
}
// Alternatively this can be converted to a plain array.
// The iterator should be used instead where possible as it does not require
// keeping a reference to all Season objects.
// const seasons = [...getXRankSeasons()];
```
### `getXRankSeason`
Returns an `XRankSeason` object from a `Date`, year and month, or season ID string.
Returns an `XRankSeason` object from a `Date`, year and month, season ID string or index number.
```ts
import { getXRankSeason, XRankSeason } from 'nxapi/splatnet2';
@ -129,6 +134,7 @@ const season = getXRankSeason(2022, 1);
// season is a plain object of type XRankSeason
// season.id === '220101T00_220201T00'
// season.key === '2022_01'
// season.index === 44
// season.start is a Date object with the UTC timestamp 2022-01-01 00:00:00
// season.end is a Date object with the UTC timestamp 2022-02-01 00:00:00
// season.complete === true, assuming the system time is after 2022-02-01 00:00:00 (UTC)
@ -136,11 +142,12 @@ const season = getXRankSeason(2022, 1);
// This function can also be called like this:
const season = getXRankSeason(new Date(Date.UTC(2022, 1)));
const season = getXRankSeason('220101T00_220201T00');
const season = getXRankSeason(44);
```
### `getNextXRankSeason`
Returns an `XRankSeason` object for the season following an X Rank season from an `XRankSeason`, `Date`, year and month, or season ID string.
Returns an `XRankSeason` object for the season following an X Rank season from an `XRankSeason`, `Date`, year and month, season ID string or index number.
```ts
import { getNextXRankSeason, XRankSeason } from 'nxapi/splatnet2';
@ -149,6 +156,7 @@ const season = getNextXRankSeason(2022, 1);
// season is a plain object of type XRankSeason
// season.id === '220201T00_220301T00'
// season.key === '2022_02'
// season.index === 45
// season.start is a Date object with the UTC timestamp 2022-02-01 00:00:00
// season.end is a Date object with the UTC timestamp 2022-03-01 00:00:00
// season.complete === true, assuming the system time is after 2022-03-01 00:00:00 (UTC)
@ -156,11 +164,12 @@ const season = getNextXRankSeason(2022, 1);
// This function can also be called like this:
const season = getNextXRankSeason(new Date(Date.UTC(2022, 1)));
const season = getNextXRankSeason('220101T00_220201T00');
const season = getNextXRankSeason(44);
```
### `getPreviousXRankSeason`
Returns an `XRankSeason` object for the season following an X Rank season from an `XRankSeason`, `Date`, year and month, or season ID string.
Returns an `XRankSeason` object for the season following an X Rank season from an `XRankSeason`, `Date`, year and month, season ID string or index number.
```ts
import { getNextXRankSeason, XRankSeason } from 'nxapi/splatnet2';
@ -169,6 +178,7 @@ const season = getPreviousXRankSeason(2022, 1);
// season is a plain object of type XRankSeason
// season.id === '211201T00_220101T00'
// season.key === '2021_12'
// season.index === 43
// season.start is a Date object with the UTC timestamp 2021-12-01 00:00:00
// season.end is a Date object with the UTC timestamp 2022-01-01 00:00:00
// season.complete === true, assuming the system time is after 2022-01-01 00:00:00 (UTC)
@ -176,4 +186,5 @@ const season = getPreviousXRankSeason(2022, 1);
// This function can also be called like this:
const season = getPreviousXRankSeason(new Date(Date.UTC(2022, 1)));
const season = getPreviousXRankSeason('220101T00_220201T00');
const season = getPreviousXRankSeason(44);
```

View File

@ -9,13 +9,14 @@ export interface Season {
id: string;
/** Season ID use by the web app, e.g. "2022_05" */
key: string;
index: number;
start: Date;
end: Date;
complete: boolean;
}
export function* getAllSeasons(sort_ascending = false) {
let season = sort_ascending ? FIRST_SEASON : CURRENT_SEASON;
let season = sort_ascending ? getFirstSeason() : getCurrentSeason();
while (season) {
yield season;
@ -25,7 +26,7 @@ export function* getAllSeasons(sort_ascending = false) {
export function getSeason(year: number, month: number): Season | null
export function getSeason(start: Date): Season | null
export function getSeason(id: string): Season | null
export function getSeason(id: string | number): Season | null
export function getSeason(year: number | Date | string, month?: number): Season | null {
if (year instanceof Date) {
month = year.getUTCMonth() + 1;
@ -33,7 +34,7 @@ export function getSeason(year: number | Date | string, month?: number): Season
}
if (typeof year === 'string') {
// 180401T00_180601T00
const match = year.match(/^(\d{2,})(0\d,1[012])01T00_(\d{2,})(0\d,1[012])01T00$/);
const match = year.match(/^(\d{2,})(0\d|1[012])01T00_(\d{2,})(0\d|1[012])01T00$/);
if (!match) throw new Error('Invalid season ID');
year = 2000 + parseInt(match[1]);
@ -42,10 +43,17 @@ export function getSeason(year: number | Date | string, month?: number): Season
const nextyear = month === 12 ? year + 1 : year;
const nextmonth = year === 2018 && month === 4 ? 6 :
month === 12 ? 1 : month + 1;
if (nextyear !== parseInt(match[3]) || nextmonth !== parseInt(match[4])) throw new Error('Invalid season ID');
if (nextyear !== (2000 + parseInt(match[3])) || nextmonth !== parseInt(match[4])) throw new Error('Invalid season ID');
if (year === 2018 && month === 5) throw new Error('Invalid season ID');
}
const start = new Date(Date.UTC(year, month! - 1));
const start = typeof month === 'number' ? new Date(Date.UTC(year, month - 1)) :
getSeasonStartDateByIndex(year);
if (start.getUTCFullYear() === 2018 && start.getUTCMonth() === 3) {
start.setUTCMonth(4);
}
if (Date.now() < start.getTime()) return null;
if (start.getUTCFullYear() < 2018 || (start.getUTCFullYear() === 2018 && start.getUTCMonth() < 3)) return null;
@ -65,6 +73,7 @@ export function getSeason(year: number | Date | string, month?: number): Season
return {
id,
key,
index: getSeasonIndex(start.getUTCFullYear(), start.getUTCMonth() + 1),
start,
end,
complete: Date.now() > end.getTime(),
@ -75,13 +84,18 @@ export function getFirstSeason() {
return getSeason(2018, 5);
}
const FIRST_SEASON = getFirstSeason();
const CURRENT_SEASON = getSeason(new Date());
export function getCurrentSeason() {
return getSeason(new Date());
}
export function getNextSeason(season: Season): Season | null
export function getNextSeason(season: Season | number): Season | null
export function getNextSeason(year: number, month: number): Season | null
export function getNextSeason(season: Season | number, month?: number): Season | null {
const current_start = typeof season === 'number' ? new Date(Date.UTC(season, month!)) : season.start;
const current_start =
typeof season === 'number' && typeof month === 'number' ?
new Date(Date.UTC(season, month)) :
typeof season === 'number' ? getSeasonStartDateByIndex(season) :
new Date(season.start.getTime());
if (current_start.getUTCFullYear() === 2018 && current_start.getUTCMonth() === 3) {
current_start.setUTCMonth(4);
@ -96,7 +110,8 @@ export function getNextSeason(season: Season | number, month?: number): Season |
const end = new Date(Date.UTC(
start.getUTCFullYear() + (start.getUTCMonth() === 11 ? 1 : 0),
start.getUTCMonth() === 11 ? 0 : start.getUTCMonth() + 1,
start.getUTCFullYear() === 2018 && start.getUTCMonth() === 3 ? 5 :
start.getUTCMonth() === 11 ? 0 : start.getUTCMonth() + 1,
));
const id = toSeasonId(start.getUTCFullYear(), start.getUTCMonth() + 1);
@ -105,16 +120,21 @@ export function getNextSeason(season: Season | number, month?: number): Season |
return {
id,
key,
index: getSeasonIndex(start.getUTCFullYear(), start.getUTCMonth() + 1),
start,
end,
complete: Date.now() > end.getTime(),
};
}
export function getPreviousSeason(season: Season): Season | null
export function getPreviousSeason(season: Season | number): Season | null
export function getPreviousSeason(year: number, month: number): Season | null
export function getPreviousSeason(season: Season | number, month?: number): Season | null {
const current_start = typeof season === 'number' ? new Date(Date.UTC(season, month!)) : season.start;
const current_start =
typeof season === 'number' && typeof month === 'number' ?
new Date(Date.UTC(season, month)) :
typeof season === 'number' ? getSeasonStartDateByIndex(season) :
new Date(season.start.getTime());
const start = new Date(Date.UTC(
current_start.getUTCFullYear() - (current_start.getUTCMonth() === 0 ? 1 : 0),
@ -125,7 +145,8 @@ export function getPreviousSeason(season: Season | number, month?: number): Seas
const end = new Date(Date.UTC(
start.getUTCFullYear() + (start.getUTCMonth() === 11 ? 1 : 0),
start.getUTCMonth() === 11 ? 0 : start.getUTCMonth() + 1,
start.getUTCFullYear() === 2018 && start.getUTCMonth() === 3 ? 5 :
start.getUTCMonth() === 11 ? 0 : start.getUTCMonth() + 1,
));
const id = toSeasonId(start.getUTCFullYear(), start.getUTCMonth() + 1);
@ -138,21 +159,67 @@ export function getPreviousSeason(season: Season | number, month?: number): Seas
return {
id,
key,
index: getSeasonIndex(start.getUTCFullYear(), start.getUTCMonth() + 1),
start,
end,
complete: Date.now() > end.getTime(),
};
}
export function getSeasonIndex(year: number, month: number) {
const nextyear = month === 12 ? year + 1 : year;
if (year < 2000) throw new Error('Invalid season ID');
if (nextyear >= 2100) throw new Error('Invalid season ID');
if (month < 1) throw new Error('Invalid season ID');
if (month > 12) throw new Error('Invalid season ID');
if (year < 2018) throw new Error('Invalid season ID');
if (year === 2018 && month < 4) throw new Error('Invalid season ID');
const now = new Date();
if (year > now.getUTCFullYear()) throw new Error('Invalid season ID');
if (year === now.getUTCFullYear() && month > (now.getUTCMonth() + 1)) throw new Error('Invalid season ID');
if (year === 2018 && month === 4) month = 5;
const i = year * 12 + (month - 1);
return i - 24220;
}
export function getSeasonStartDateByIndex(index: number) {
if (index < 0) throw new Error('Invalid season index');
if (index === 0) return new Date(Date.UTC(2018, 3));
const i = index + 24220;
const year = Math.floor(i / 12);
const month = (i % 12) + 1;
if (year < 2018) throw new Error('Invalid season index');
if (year === 2018 && month < 4) throw new Error('Invalid season index');
const now = new Date();
if (year > now.getUTCFullYear()) throw new Error('Invalid season index');
if (year === now.getUTCFullYear() && month > (now.getUTCMonth() + 1)) throw new Error('Invalid season index');
return new Date(Date.UTC(year, month - 1));
}
export function toSeasonId(year: number, month: number) {
if (year === 2018 && month === 4) month = 5;
const nextyear = month === 12 ? year + 1 : year;
const nextmonth = month === 12 ? 1 : month + 1;
if (year < 2000) throw new Error('Invalid season ID');
if (nextyear >= 2100) throw new Error('Invalid season ID');
if (month < 1) throw new Error('Invalid season ID');
if (month > 12) throw new Error('Invalid season ID');
if (year < 2018) throw new Error('Invalid season ID');
if (year === 2018 && month < 5) throw new Error('Invalid season ID');
if (year === 2018 && month < 4) throw new Error('Invalid season ID');
const now = new Date();
if (year > now.getUTCFullYear()) throw new Error('Invalid season ID');

View File

@ -40,6 +40,7 @@ export async function handler(argv: ArgumentsCamelCase<Arguments>) {
const table = new Table({
head: [
'#',
'ID',
'Key',
'Start',
@ -50,6 +51,7 @@ export async function handler(argv: ArgumentsCamelCase<Arguments>) {
for (const season of getAllSeasons(sort_ascending)) {
table.push([
season.index + 1,
season.id,
season.key,
season.start.toLocaleString('en-GB'),