mirror of
https://github.com/denoland/deno.git
synced 2025-09-27 12:49:10 +00:00
feat(std/datetime): add is leap and difference functions (#4857)
This commit is contained in:
parent
5bc130be27
commit
affba80454
2 changed files with 259 additions and 77 deletions
|
@ -3,6 +3,12 @@ import { assert } from "../_util/assert.ts";
|
||||||
|
|
||||||
export type DateFormat = "mm-dd-yyyy" | "dd-mm-yyyy" | "yyyy-mm-dd";
|
export type DateFormat = "mm-dd-yyyy" | "dd-mm-yyyy" | "yyyy-mm-dd";
|
||||||
|
|
||||||
|
export const SECOND = 1e3;
|
||||||
|
export const MINUTE = SECOND * 60;
|
||||||
|
export const HOUR = MINUTE * 60;
|
||||||
|
export const DAY = HOUR * 24;
|
||||||
|
export const WEEK = DAY * 7;
|
||||||
|
|
||||||
function execForce(reg: RegExp, pat: string): RegExpExecArray {
|
function execForce(reg: RegExp, pat: string): RegExpExecArray {
|
||||||
const v = reg.exec(pat);
|
const v = reg.exec(pat);
|
||||||
assert(v != null);
|
assert(v != null);
|
||||||
|
@ -96,13 +102,12 @@ export function parseDateTime(
|
||||||
* @return Number of the day in year
|
* @return Number of the day in year
|
||||||
*/
|
*/
|
||||||
export function dayOfYear(date: Date): number {
|
export function dayOfYear(date: Date): number {
|
||||||
const dayMs = 1000 * 60 * 60 * 24;
|
|
||||||
const yearStart = new Date(date.getFullYear(), 0, 0);
|
const yearStart = new Date(date.getFullYear(), 0, 0);
|
||||||
const diff =
|
const diff =
|
||||||
date.getTime() -
|
date.getTime() -
|
||||||
yearStart.getTime() +
|
yearStart.getTime() +
|
||||||
(yearStart.getTimezoneOffset() - date.getTimezoneOffset()) * 60 * 1000;
|
(yearStart.getTimezoneOffset() - date.getTimezoneOffset()) * 60 * 1000;
|
||||||
return Math.floor(diff / dayMs);
|
return Math.floor(diff / DAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -150,3 +155,128 @@ export function toIMF(date: Date): string {
|
||||||
months[date.getUTCMonth()]
|
months[date.getUTCMonth()]
|
||||||
} ${y} ${h}:${min}:${s} GMT`;
|
} ${y} ${h}:${min}:${s} GMT`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check given year is a leap year or not.
|
||||||
|
* based on : https://docs.microsoft.com/en-us/office/troubleshoot/excel/determine-a-leap-year
|
||||||
|
* @param year year in number or Date format
|
||||||
|
*/
|
||||||
|
export function isLeap(year: Date | number): boolean {
|
||||||
|
const yearNumber = year instanceof Date ? year.getFullYear() : year;
|
||||||
|
return (
|
||||||
|
(yearNumber % 4 === 0 && yearNumber % 100 !== 0) || yearNumber % 400 === 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Unit =
|
||||||
|
| "miliseconds"
|
||||||
|
| "seconds"
|
||||||
|
| "minutes"
|
||||||
|
| "hours"
|
||||||
|
| "days"
|
||||||
|
| "weeks"
|
||||||
|
| "months"
|
||||||
|
| "quarters"
|
||||||
|
| "years";
|
||||||
|
|
||||||
|
export type DifferenceFormat = Partial<Record<Unit, number>>;
|
||||||
|
|
||||||
|
export type DifferenceOptions = {
|
||||||
|
units?: Unit[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate difference between two dates.
|
||||||
|
* @param from Year to calculate difference
|
||||||
|
* @param to Year to calculate difference with
|
||||||
|
* @param options Options for determining how to respond
|
||||||
|
*
|
||||||
|
* example :
|
||||||
|
*
|
||||||
|
* ```typescript
|
||||||
|
* datetime.difference(new Date("2020/1/1"),new Date("2020/2/2"),{ units : ["days","months"] })
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export function difference(
|
||||||
|
from: Date,
|
||||||
|
to: Date,
|
||||||
|
options?: DifferenceOptions
|
||||||
|
): DifferenceFormat {
|
||||||
|
const uniqueUnits = options?.units
|
||||||
|
? [...new Set(options?.units)]
|
||||||
|
: [
|
||||||
|
"miliseconds",
|
||||||
|
"seconds",
|
||||||
|
"minutes",
|
||||||
|
"hours",
|
||||||
|
"days",
|
||||||
|
"weeks",
|
||||||
|
"months",
|
||||||
|
"quarters",
|
||||||
|
"years",
|
||||||
|
];
|
||||||
|
|
||||||
|
const bigger = Math.max(from.getTime(), to.getTime());
|
||||||
|
const smaller = Math.min(from.getTime(), to.getTime());
|
||||||
|
const differenceInMs = bigger - smaller;
|
||||||
|
|
||||||
|
const differences: DifferenceFormat = {};
|
||||||
|
|
||||||
|
for (const uniqueUnit of uniqueUnits) {
|
||||||
|
switch (uniqueUnit) {
|
||||||
|
case "miliseconds":
|
||||||
|
differences.miliseconds = differenceInMs;
|
||||||
|
break;
|
||||||
|
case "seconds":
|
||||||
|
differences.seconds = Math.floor(differenceInMs / SECOND);
|
||||||
|
break;
|
||||||
|
case "minutes":
|
||||||
|
differences.minutes = Math.floor(differenceInMs / MINUTE);
|
||||||
|
break;
|
||||||
|
case "hours":
|
||||||
|
differences.hours = Math.floor(differenceInMs / HOUR);
|
||||||
|
break;
|
||||||
|
case "days":
|
||||||
|
differences.days = Math.floor(differenceInMs / DAY);
|
||||||
|
break;
|
||||||
|
case "weeks":
|
||||||
|
differences.weeks = Math.floor(differenceInMs / WEEK);
|
||||||
|
break;
|
||||||
|
case "months":
|
||||||
|
differences.months = calculateMonthsDifference(bigger, smaller);
|
||||||
|
break;
|
||||||
|
case "quarters":
|
||||||
|
differences.quarters = Math.floor(
|
||||||
|
(typeof differences.months !== "undefined" &&
|
||||||
|
differences.months / 4) ||
|
||||||
|
calculateMonthsDifference(bigger, smaller) / 4
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case "years":
|
||||||
|
differences.years = Math.floor(
|
||||||
|
(typeof differences.months !== "undefined" &&
|
||||||
|
differences.months / 12) ||
|
||||||
|
calculateMonthsDifference(bigger, smaller) / 12
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return differences;
|
||||||
|
}
|
||||||
|
|
||||||
|
function calculateMonthsDifference(bigger: number, smaller: number): number {
|
||||||
|
const biggerDate = new Date(bigger);
|
||||||
|
const smallerDate = new Date(smaller);
|
||||||
|
const yearsDiff = biggerDate.getFullYear() - smallerDate.getFullYear();
|
||||||
|
const monthsDiff = biggerDate.getMonth() - smallerDate.getMonth();
|
||||||
|
const calendarDiffrences = Math.abs(yearsDiff * 12 + monthsDiff);
|
||||||
|
const compareResult = biggerDate > smallerDate ? 1 : -1;
|
||||||
|
biggerDate.setMonth(
|
||||||
|
biggerDate.getMonth() - compareResult * calendarDiffrences
|
||||||
|
);
|
||||||
|
const isLastMonthNotFull =
|
||||||
|
biggerDate > smallerDate ? 1 : -1 === -compareResult ? 1 : 0;
|
||||||
|
const months = compareResult * (calendarDiffrences - isLastMonthNotFull);
|
||||||
|
return months === 0 ? 0 : months;
|
||||||
|
}
|
||||||
|
|
|
@ -1,83 +1,101 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||||
import { assertEquals, assertThrows } from "../testing/asserts.ts";
|
import { assert, assertEquals, assertThrows } from "../testing/asserts.ts";
|
||||||
import * as datetime from "./mod.ts";
|
import * as datetime from "./mod.ts";
|
||||||
|
|
||||||
Deno.test("parseDateTime", function (): void {
|
Deno.test({
|
||||||
assertEquals(
|
name: "[std/datetime] parseDate",
|
||||||
datetime.parseDateTime("01-03-2019 16:30", "mm-dd-yyyy hh:mm"),
|
fn: () => {
|
||||||
new Date(2019, 0, 3, 16, 30)
|
assertEquals(
|
||||||
);
|
datetime.parseDateTime("01-03-2019 16:30", "mm-dd-yyyy hh:mm"),
|
||||||
assertEquals(
|
new Date(2019, 0, 3, 16, 30)
|
||||||
datetime.parseDateTime("03-01-2019 16:31", "dd-mm-yyyy hh:mm"),
|
);
|
||||||
new Date(2019, 0, 3, 16, 31)
|
assertEquals(
|
||||||
);
|
datetime.parseDateTime("03-01-2019 16:31", "dd-mm-yyyy hh:mm"),
|
||||||
assertEquals(
|
new Date(2019, 0, 3, 16, 31)
|
||||||
datetime.parseDateTime("2019-01-03 16:32", "yyyy-mm-dd hh:mm"),
|
);
|
||||||
new Date(2019, 0, 3, 16, 32)
|
assertEquals(
|
||||||
);
|
datetime.parseDateTime("2019-01-03 16:32", "yyyy-mm-dd hh:mm"),
|
||||||
assertEquals(
|
new Date(2019, 0, 3, 16, 32)
|
||||||
datetime.parseDateTime("16:33 01-03-2019", "hh:mm mm-dd-yyyy"),
|
);
|
||||||
new Date(2019, 0, 3, 16, 33)
|
assertEquals(
|
||||||
);
|
datetime.parseDateTime("16:33 01-03-2019", "hh:mm mm-dd-yyyy"),
|
||||||
assertEquals(
|
new Date(2019, 0, 3, 16, 33)
|
||||||
datetime.parseDateTime("16:34 03-01-2019", "hh:mm dd-mm-yyyy"),
|
);
|
||||||
new Date(2019, 0, 3, 16, 34)
|
assertEquals(
|
||||||
);
|
datetime.parseDateTime("16:34 03-01-2019", "hh:mm dd-mm-yyyy"),
|
||||||
assertEquals(
|
new Date(2019, 0, 3, 16, 34)
|
||||||
datetime.parseDateTime("16:35 2019-01-03", "hh:mm yyyy-mm-dd"),
|
);
|
||||||
new Date(2019, 0, 3, 16, 35)
|
assertEquals(
|
||||||
);
|
datetime.parseDateTime("16:35 2019-01-03", "hh:mm yyyy-mm-dd"),
|
||||||
});
|
new Date(2019, 0, 3, 16, 35)
|
||||||
|
);
|
||||||
Deno.test("invalidParseDateTimeFormatThrows", function (): void {
|
},
|
||||||
assertThrows(
|
|
||||||
(): void => {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
(datetime as any).parseDateTime("2019-01-01 00:00", "x-y-z");
|
|
||||||
},
|
|
||||||
Error,
|
|
||||||
"Invalid datetime format!"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
Deno.test("parseDate", function (): void {
|
|
||||||
assertEquals(
|
|
||||||
datetime.parseDate("01-03-2019", "mm-dd-yyyy"),
|
|
||||||
new Date(2019, 0, 3)
|
|
||||||
);
|
|
||||||
assertEquals(
|
|
||||||
datetime.parseDate("03-01-2019", "dd-mm-yyyy"),
|
|
||||||
new Date(2019, 0, 3)
|
|
||||||
);
|
|
||||||
assertEquals(
|
|
||||||
datetime.parseDate("2019-01-03", "yyyy-mm-dd"),
|
|
||||||
new Date(2019, 0, 3)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
Deno.test("invalidParseDateFormatThrows", function (): void {
|
|
||||||
assertThrows(
|
|
||||||
(): void => {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
(datetime as any).parseDate("2019-01-01", "x-y-z");
|
|
||||||
},
|
|
||||||
Error,
|
|
||||||
"Invalid date format!"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
Deno.test("DayOfYear", function (): void {
|
|
||||||
assertEquals(1, datetime.dayOfYear(new Date("2019-01-01T03:24:00")));
|
|
||||||
assertEquals(70, datetime.dayOfYear(new Date("2019-03-11T03:24:00")));
|
|
||||||
assertEquals(365, datetime.dayOfYear(new Date("2019-12-31T03:24:00")));
|
|
||||||
});
|
|
||||||
|
|
||||||
Deno.test("currentDayOfYear", function (): void {
|
|
||||||
assertEquals(datetime.currentDayOfYear(), datetime.dayOfYear(new Date()));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Deno.test({
|
Deno.test({
|
||||||
name: "[DateTime] to IMF",
|
name: "[std/datetime] invalidParseDateTimeFormatThrows",
|
||||||
|
fn: () => {
|
||||||
|
assertThrows(
|
||||||
|
(): void => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
(datetime as any).parseDateTime("2019-01-01 00:00", "x-y-z");
|
||||||
|
},
|
||||||
|
Error,
|
||||||
|
"Invalid datetime format!"
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.test({
|
||||||
|
name: "[std/datetime] parseDate",
|
||||||
|
fn: () => {
|
||||||
|
assertEquals(
|
||||||
|
datetime.parseDate("01-03-2019", "mm-dd-yyyy"),
|
||||||
|
new Date(2019, 0, 3)
|
||||||
|
);
|
||||||
|
assertEquals(
|
||||||
|
datetime.parseDate("03-01-2019", "dd-mm-yyyy"),
|
||||||
|
new Date(2019, 0, 3)
|
||||||
|
);
|
||||||
|
assertEquals(
|
||||||
|
datetime.parseDate("2019-01-03", "yyyy-mm-dd"),
|
||||||
|
new Date(2019, 0, 3)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.test({
|
||||||
|
name: "[std/datetime] invalidParseDateFormatThrows",
|
||||||
|
fn: () => {
|
||||||
|
assertThrows(
|
||||||
|
(): void => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
(datetime as any).parseDate("2019-01-01", "x-y-z");
|
||||||
|
},
|
||||||
|
Error,
|
||||||
|
"Invalid date format!"
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.test({
|
||||||
|
name: "[std/datetime] DayOfYear",
|
||||||
|
fn: () => {
|
||||||
|
assertEquals(1, datetime.dayOfYear(new Date("2019-01-01T03:24:00")));
|
||||||
|
assertEquals(70, datetime.dayOfYear(new Date("2019-03-11T03:24:00")));
|
||||||
|
assertEquals(365, datetime.dayOfYear(new Date("2019-12-31T03:24:00")));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.test({
|
||||||
|
name: "[std/datetime] currentDayOfYear",
|
||||||
|
fn: () => {
|
||||||
|
assertEquals(datetime.currentDayOfYear(), datetime.dayOfYear(new Date()));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.test({
|
||||||
|
name: "[std/datetime] to IMF",
|
||||||
fn(): void {
|
fn(): void {
|
||||||
const actual = datetime.toIMF(new Date(Date.UTC(1994, 3, 5, 15, 32)));
|
const actual = datetime.toIMF(new Date(Date.UTC(1994, 3, 5, 15, 32)));
|
||||||
const expected = "Tue, 05 Apr 1994 15:32:00 GMT";
|
const expected = "Tue, 05 Apr 1994 15:32:00 GMT";
|
||||||
|
@ -86,10 +104,44 @@ Deno.test({
|
||||||
});
|
});
|
||||||
|
|
||||||
Deno.test({
|
Deno.test({
|
||||||
name: "[DateTime] to IMF 0",
|
name: "[std/datetime] to IMF 0",
|
||||||
fn(): void {
|
fn(): void {
|
||||||
const actual = datetime.toIMF(new Date(0));
|
const actual = datetime.toIMF(new Date(0));
|
||||||
const expected = "Thu, 01 Jan 1970 00:00:00 GMT";
|
const expected = "Thu, 01 Jan 1970 00:00:00 GMT";
|
||||||
assertEquals(actual, expected);
|
assertEquals(actual, expected);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Deno.test({
|
||||||
|
name: "[std/datetime] isLeap",
|
||||||
|
fn(): void {
|
||||||
|
assert(datetime.isLeap(1992));
|
||||||
|
assert(datetime.isLeap(2000));
|
||||||
|
assert(!datetime.isLeap(2003));
|
||||||
|
assert(!datetime.isLeap(2007));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.test({
|
||||||
|
name: "[std/datetime] Difference",
|
||||||
|
fn(): void {
|
||||||
|
const denoInit = new Date("2018/5/14");
|
||||||
|
const denoRelaseV1 = new Date("2020/5/13");
|
||||||
|
let difference = datetime.difference(denoRelaseV1, denoInit, {
|
||||||
|
units: ["days", "months", "years"],
|
||||||
|
});
|
||||||
|
assertEquals(difference.days, 730);
|
||||||
|
assertEquals(difference.months, 23);
|
||||||
|
assertEquals(difference.years, 1);
|
||||||
|
|
||||||
|
const birth = new Date("1998/2/23 10:10:10");
|
||||||
|
const old = new Date("1998/2/23 11:11:11");
|
||||||
|
difference = datetime.difference(birth, old, {
|
||||||
|
units: ["miliseconds", "minutes", "seconds", "hours"],
|
||||||
|
});
|
||||||
|
assertEquals(difference.miliseconds, 3661000);
|
||||||
|
assertEquals(difference.seconds, 3661);
|
||||||
|
assertEquals(difference.minutes, 61);
|
||||||
|
assertEquals(difference.hours, 1);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue