summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAli Hasani <a.hassssani@gmail.com>2020-06-27 15:40:45 +0430
committerGitHub <noreply@github.com>2020-06-27 13:10:45 +0200
commitaffba804546607c0d26887bb5c40cba9c9be3fe6 (patch)
treed84e2a91b52adc647cf9ff5ba48db0e68a036e90
parent5bc130be2722fe54d42004cc48536b390437ede3 (diff)
feat(std/datetime): add is leap and difference functions (#4857)
-rw-r--r--std/datetime/mod.ts134
-rw-r--r--std/datetime/test.ts182
2 files changed, 249 insertions, 67 deletions
diff --git a/std/datetime/mod.ts b/std/datetime/mod.ts
index 7503eccb4..99332e046 100644
--- a/std/datetime/mod.ts
+++ b/std/datetime/mod.ts
@@ -3,6 +3,12 @@ import { assert } from "../_util/assert.ts";
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 {
const v = reg.exec(pat);
assert(v != null);
@@ -96,13 +102,12 @@ export function parseDateTime(
* @return Number of the day in year
*/
export function dayOfYear(date: Date): number {
- const dayMs = 1000 * 60 * 60 * 24;
const yearStart = new Date(date.getFullYear(), 0, 0);
const diff =
date.getTime() -
yearStart.getTime() +
(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()]
} ${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;
+}
diff --git a/std/datetime/test.ts b/std/datetime/test.ts
index c50914342..dc4c7278e 100644
--- a/std/datetime/test.ts
+++ b/std/datetime/test.ts
@@ -1,83 +1,101 @@
// 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";
-Deno.test("parseDateTime", function (): void {
- assertEquals(
- datetime.parseDateTime("01-03-2019 16:30", "mm-dd-yyyy hh:mm"),
- new Date(2019, 0, 3, 16, 30)
- );
- assertEquals(
- datetime.parseDateTime("03-01-2019 16:31", "dd-mm-yyyy hh:mm"),
- new Date(2019, 0, 3, 16, 31)
- );
- assertEquals(
- datetime.parseDateTime("2019-01-03 16:32", "yyyy-mm-dd hh:mm"),
- new Date(2019, 0, 3, 16, 32)
- );
- assertEquals(
- datetime.parseDateTime("16:33 01-03-2019", "hh:mm mm-dd-yyyy"),
- new Date(2019, 0, 3, 16, 33)
- );
- assertEquals(
- datetime.parseDateTime("16:34 03-01-2019", "hh:mm dd-mm-yyyy"),
- new Date(2019, 0, 3, 16, 34)
- );
- assertEquals(
- datetime.parseDateTime("16:35 2019-01-03", "hh:mm yyyy-mm-dd"),
- new Date(2019, 0, 3, 16, 35)
- );
+Deno.test({
+ name: "[std/datetime] parseDate",
+ fn: () => {
+ assertEquals(
+ datetime.parseDateTime("01-03-2019 16:30", "mm-dd-yyyy hh:mm"),
+ new Date(2019, 0, 3, 16, 30)
+ );
+ assertEquals(
+ datetime.parseDateTime("03-01-2019 16:31", "dd-mm-yyyy hh:mm"),
+ new Date(2019, 0, 3, 16, 31)
+ );
+ assertEquals(
+ datetime.parseDateTime("2019-01-03 16:32", "yyyy-mm-dd hh:mm"),
+ new Date(2019, 0, 3, 16, 32)
+ );
+ assertEquals(
+ datetime.parseDateTime("16:33 01-03-2019", "hh:mm mm-dd-yyyy"),
+ new Date(2019, 0, 3, 16, 33)
+ );
+ assertEquals(
+ datetime.parseDateTime("16:34 03-01-2019", "hh:mm dd-mm-yyyy"),
+ new Date(2019, 0, 3, 16, 34)
+ );
+ 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({
+ 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("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({
+ 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("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({
+ 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("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({
+ 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("currentDayOfYear", function (): void {
- assertEquals(datetime.currentDayOfYear(), datetime.dayOfYear(new Date()));
+Deno.test({
+ name: "[std/datetime] currentDayOfYear",
+ fn: () => {
+ assertEquals(datetime.currentDayOfYear(), datetime.dayOfYear(new Date()));
+ },
});
Deno.test({
- name: "[DateTime] to IMF",
+ name: "[std/datetime] to IMF",
fn(): void {
const actual = datetime.toIMF(new Date(Date.UTC(1994, 3, 5, 15, 32)));
const expected = "Tue, 05 Apr 1994 15:32:00 GMT";
@@ -86,10 +104,44 @@ Deno.test({
});
Deno.test({
- name: "[DateTime] to IMF 0",
+ name: "[std/datetime] to IMF 0",
fn(): void {
const actual = datetime.toIMF(new Date(0));
const expected = "Thu, 01 Jan 1970 00:00:00 GMT";
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);
+ },
+});