Skip to content

Commit

Permalink
Make new constructions with years between 0-100 make dates in 1900s
Browse files Browse the repository at this point in the history
This is consistent with how the builtin Date object works
Fix padding in ISO strings to be consistent with Date objects
  • Loading branch information
lwileczek committed Oct 25, 2024
1 parent 0f4351f commit 1c3d968
Show file tree
Hide file tree
Showing 6 changed files with 198 additions and 123 deletions.
212 changes: 106 additions & 106 deletions lib/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,110 +1,110 @@
export default DateOnly;
/** A date object with without time */
export class DateOnly {
/**
* Crete a DateOnly Object from a number, numbers represent the days since
* 1 Jan 1970
* @param {number} n The number to be converted to a DateOnly Object
*/
static fromNumber(n: number): DateOnly;
/**
* Create a DateOnly Object from some string. Wraps new Date(str)
* @param {string} s The string to read the date info from
*/
static fromString(s: string): DateOnly;
/**
* Create a DateOnly Object from some string. Wraps new Date(str)
* @param {Date} d The date to use in creating a new DateOnly object
* using the date's UTC values
*/
static fromDate(d: Date): DateOnly;
/**
* Create a new DateOnly object for the current date
* @returns {DateOnly} A donly only object with the current local date
*/
static now(): DateOnly;
/**
* @private
* Check if an input returns a valid date object
* @param {*} val some input to be passed to new Date and checked for validity
*/
private static "__#1@#isDate";
/**
* @private
* Create an invaid DateOnly object
* @returns {DateOnly} A date only object with it's invalid property set true
*/
private static "__#1@#createInvalidDate";
/**
* @constructor
* Create a new DateOnly object
* @param {number} [year=1970] The year the of the date
* @param {number} [date=1] The year the of the date
* @param {number} [month=1] The year the of the date
*/
constructor(year?: number, month?: number, date?: number);
/**
* Return the year only from the DateOnly
* @returns {number}
*/
getFullYear(): number;
/**
* Return the month value from the DateOnly.
* Indexed from 1, 12 is december
* @returns {number}
*/
getMonth(): number;
/**
* Return the date value, or the day of the month
* Indexed from 1, max of 31 depending the month
* @returns {number}
*/
getDate(): number;
/**
* Return the year only from the DateOnly
* @param {number} v The new desired value for the year
*/
setFullYear(v: number): void;
/**
* Get day of the week
*/
getDay(): number;
/**
* Return the month value from the DateOnly.
* Indexed from 1, 12 is december
* @param {number} v The new desired value for the value
*/
setMonth(v: number): void;
/**
* Set the date value, or the day of the month
* @param {number} v The new desired value for the date
*/
setDate(v: number): void;
/**
* Set the date value, or the day of the month
* @param {string} [sep=-] The seperator to use between date components
* @returns {string} the date represented as a string
*/
toString(sep?: string): string;
/**
* Return a string in ISO8601 format
* All time values are set to zero
* @returns {string} the date represented as an ISO8601 string
*/
toISOString(): string;
/**
* JSON serializes the date to an ISO8601 date
* @returns {string} the date represented as an ISO8601 string
*/
toJSON(): string;
/**
* Returns a Date stirng
*/
toDateString(): string;
/**
* Returns if the current date time object is valid or not
*/
isValid(): boolean;
#private;
/**
* Crete a DateOnly Object from a number, numbers represent the days since
* 1 Jan 1970
* @param {number} n The number to be converted to a DateOnly Object
*/
static fromNumber(n: number): DateOnly;
/**
* Create a DateOnly Object from some string. Wraps new Date(str)
* @param {string} s The string to read the date info from
*/
static fromString(s: string): DateOnly;
/**
* Create a DateOnly Object from some string. Wraps new Date(str)
* @param {Date} d The date to use in creating a new DateOnly object
* using the date's UTC values
*/
static fromDate(d: Date): DateOnly;
/**
* Create a new DateOnly object for the current date
* @returns {DateOnly} A donly only object with the current local date
*/
static now(): DateOnly;
/**
* @private
* Check if an input returns a valid date object
* @param {*} val some input to be passed to new Date and checked for validity
*/
private static "__#1@#isDate";
/**
* @private
* Create an invaid DateOnly object
* @returns {DateOnly} A date only object with it's invalid property set true
*/
private static "__#1@#createInvalidDate";
/**
* @constructor
* Create a new DateOnly object
* @param {number} [year=1970] The year the of the date
* @param {number} [date=1] The year the of the date
* @param {number} [month=1] The year the of the date
*/
constructor(year?: number, month?: number, date?: number);
/**
* Return the year only from the DateOnly
* @returns {number}
*/
getFullYear(): number;
/**
* Return the month value from the DateOnly.
* Indexed from 1, 12 is december
* @returns {number}
*/
getMonth(): number;
/**
* Return the date value, or the day of the month
* Indexed from 1, max of 31 depending the month
* @returns {number}
*/
getDate(): number;
/**
* Return the year only from the DateOnly
* @param {number} v The new desired value for the year
*/
setFullYear(v: number): void;
/**
* Get day of the week
*/
getDay(): number;
/**
* Return the month value from the DateOnly.
* Indexed from 1, 12 is december
* @param {number} v The new desired value for the value
*/
setMonth(v: number): void;
/**
* Set the date value, or the day of the month
* @param {number} v The new desired value for the date
*/
setDate(v: number): void;
/**
* Set the date value, or the day of the month
* @param {string} [sep=-] The seperator to use between date components
* @returns {string} the date represented as a string
*/
toString(sep?: string): string;
/**
* Return a string in ISO8601 format
* All time values are set to zero
* @returns {string} the date represented as an ISO8601 string
*/
toISOString(): string;
/**
* JSON serializes the date to an ISO8601 date
* @returns {string} the date represented as an ISO8601 string
*/
toJSON(): string;
/**
* Returns a Date stirng
*/
toDateString(): string;
/**
* Returns if the current date time object is valid or not
*/
isValid(): boolean;
#private;
}
//# sourceMappingURL=index.d.ts.map
//# sourceMappingURL=index.d.ts.map
2 changes: 1 addition & 1 deletion lib/index.d.ts.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

38 changes: 26 additions & 12 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,11 @@ class DateOnly {
* @param {number} [month=1] The year the of the date
*/
constructor(year = 1970, month = 1, date = 1) {
this.#year = +year;
let y = +year;
if (0 <= y && y < 100) {
y += 1900;
}
this.#year = y;
this.setMonth(+month);
this.#date = +date;
}
Expand Down Expand Up @@ -198,7 +202,7 @@ class DateOnly {
return "Invalid DateOnly";
}

return `${this.#year}${sep}${this.#month}${sep}${this.#date}`;
return `${this.#year}${sep}${this.#pad(this.#month)}${sep}${this.#pad(this.#date)}`;
}

/**
Expand All @@ -210,7 +214,17 @@ class DateOnly {
if (this.#invalid) {
return "Invalid DateOnly";
}
return `${this.#year}${sep}${this.#month}${sep}${this.#date}T:00:00:00.000Z`;

let prefix = "";
let padDigits = 4;
let y = this.#year;
if (y < 0) {
prefix = "-";
padDigits = 6;
y *= -1;
}

return `${prefix}${this.#pad(y, padDigits)}-${this.#pad(this.#month)}-${this.#pad(this.#date)}T00:00:00.000Z`;
}

/**
Expand Down Expand Up @@ -238,15 +252,15 @@ class DateOnly {
return !this.#invalid;
}

/**
* zero pad a number
* @param {number} n The number to pad
* @param {number} [d=2] The number of digits to pad
* @returns {string} the number zero padded
*/
#pad(n, d=2) {
return String(n).padStart(d, '0');
}
/**
* zero pad a number
* @param {number} n The number to pad
* @param {number} [d=2] The number of digits to pad
* @returns {string} the number zero padded
*/
#pad(n, d = 2) {
return String(n).padStart(d, "0");
}
}

export { DateOnly };
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@patient-otter/dateonly",
"version": "0.1.4",
"version": "0.1.5",
"description": "Date objects without time",
"main": "lib/index.js",
"scripts": {
Expand Down
19 changes: 16 additions & 3 deletions test/create.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,17 @@ describe("Creating DateOnly Objects", () => {

test("Create with random [in bounds] inputs", () => {
for (let k = 0; k < loops; k++) {
const y = Math.ceil(Math.random() * 4000);
let y = Math.ceil(Math.random() * 4000);
if (Math.random() < 0.2) {
y *= -1;
}

let shouldBe = y;
if (0 <= y && y < 100) {
shouldBe += 1900;
}
const d = new DateOnly(y);
expect(d.getFullYear()).toBe(y);
expect(d.getFullYear()).toBe(shouldBe);
expect(d.getMonth()).toBe(1);
expect(d.getDate()).toBe(1);
}
Expand All @@ -36,7 +44,12 @@ describe("Creating DateOnly Objects", () => {
const d = Math.ceil(Math.random() * 28);
const date = new DateOnly(`${y}`, `${m}`, `${d}`);

expect(date.getFullYear()).toBe(y);
let shouldBe = y;
if (0 <= y && y < 100) {
shouldBe += 1900;
}

expect(date.getFullYear()).toBe(shouldBe);
expect(date.getMonth()).toBe(m);
expect(date.getDate()).toBe(d);
}
Expand Down
48 changes: 48 additions & 0 deletions test/string.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { describe, expect, test } from "bun:test";
import { DateOnly } from "../lib";

describe("Creating strings from date only options", () => {
const testCases = [
{ year: 2044, month: 3, date: 9, expected: "2044-03-09" },
{ year: 2044, month: 10, date: 9, expected: "2044-10-09" },
{ year: 2044, month: 3, date: 19, expected: "2044-03-19" },
{ year: 2044, month: 11, date: 22, expected: "2044-11-22" },
{ year: 44, month: 11, date: 22, expected: "1944-11-22" },
];

for (const tc of testCases) {
test(`ToString: ${tc.expected}`, () => {
const d = new DateOnly(tc.year, tc.month, tc.date);
expect(d.toString()).toBe(tc.expected);
});
}

for (const tc of testCases) {
test(`ISO String: ${tc.expected}`, () => {
const d = new DateOnly(tc.year, tc.month, tc.date);
expect(d.toISOString()).toBe(`${tc.expected}T00:00:00.000Z`);
});
}

test("ISO String: bad DateOnly", () => {
const d = DateOnly.fromString("THIS WILL MAKE AN INVALID DateOnly kj;a!!!");
expect(d.toISOString()).toBe("Invalid DateOnly");
});

test("Date Strings", () => {
const loops = 200;
for (let k = 0; k < loops; k++) {
let y = Math.ceil(Math.random() * 4000);
const m = Math.ceil(Math.random() * 12);
const day = Math.ceil(Math.random() * 28);

if (Math.random() < 0.2) {
y *= -1;
}

const dt = new Date(Date.UTC(y, m - 1, day, 0, 0, 0));
const d = new DateOnly(y, m, day);
expect(d.toISOString()).toBe(dt.toISOString());
}
});
});

0 comments on commit 1c3d968

Please sign in to comment.