@bgord/tools 1.1.0 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/age.vo.d.ts +5 -5
- package/dist/age.vo.js +5 -5
- package/dist/clock.vo.d.ts +2 -2
- package/dist/clock.vo.js +3 -3
- package/dist/date-calculator.service.d.ts +5 -5
- package/dist/date-calculator.service.js +6 -9
- package/dist/date-range.vo.d.ts +6 -6
- package/dist/date-range.vo.js +1 -1
- package/dist/day.vo.d.ts +5 -3
- package/dist/day.vo.js +8 -5
- package/dist/hour.vo.d.ts +4 -2
- package/dist/hour.vo.js +6 -2
- package/dist/minute.vo.d.ts +4 -2
- package/dist/minute.vo.js +6 -2
- package/dist/month.vo.d.ts +5 -3
- package/dist/month.vo.js +10 -7
- package/dist/quarter.vo.d.ts +5 -3
- package/dist/quarter.vo.js +9 -6
- package/dist/rate-limiter.service.d.ts +3 -3
- package/dist/rate-limiter.service.js +1 -3
- package/dist/relative-date.vo.d.ts +3 -3
- package/dist/relative-date.vo.js +1 -1
- package/dist/stopwatch.service.d.ts +3 -3
- package/dist/stopwatch.service.js +2 -3
- package/dist/timestamp.vo.d.ts +13 -12
- package/dist/timestamp.vo.js +10 -6
- package/dist/week.vo.d.ts +5 -3
- package/dist/week.vo.js +11 -8
- package/dist/weekday.vo.d.ts +4 -2
- package/dist/weekday.vo.js +6 -2
- package/dist/year.vo.d.ts +5 -3
- package/dist/year.vo.js +11 -8
- package/package.json +2 -2
- package/src/age.vo.ts +6 -6
- package/src/clock.vo.ts +4 -4
- package/src/date-calculator.service.ts +7 -11
- package/src/date-range.vo.ts +8 -8
- package/src/day.vo.ts +12 -7
- package/src/hour.vo.ts +8 -3
- package/src/minute.vo.ts +8 -3
- package/src/month.vo.ts +14 -9
- package/src/quarter.vo.ts +13 -8
- package/src/rate-limiter.service.ts +5 -7
- package/src/relative-date.vo.ts +5 -5
- package/src/stopwatch.service.ts +4 -4
- package/src/timestamp.vo.ts +20 -16
- package/src/week.vo.ts +15 -10
- package/src/weekday.vo.ts +8 -3
- package/src/year.vo.ts +15 -10
package/dist/weekday.vo.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { TimestampVO } from "./timestamp.vo";
|
|
1
2
|
export var WeekdayFormatterEnum;
|
|
2
3
|
(function (WeekdayFormatterEnum) {
|
|
3
4
|
WeekdayFormatterEnum["FULL"] = "FULL";
|
|
@@ -40,10 +41,13 @@ export class Weekday {
|
|
|
40
41
|
this.value = candidate;
|
|
41
42
|
this.formatter = formatter ?? WeekdayFormatters.FULL;
|
|
42
43
|
}
|
|
43
|
-
static
|
|
44
|
-
const dayZeroBased = new Date(timestamp.
|
|
44
|
+
static fromTimestamp(timestamp, formatter) {
|
|
45
|
+
const dayZeroBased = new Date(timestamp.ms).getUTCDay(); // 0..6
|
|
45
46
|
return new Weekday(dayZeroBased, formatter);
|
|
46
47
|
}
|
|
48
|
+
static fromTimestampValue(timestamp) {
|
|
49
|
+
return Weekday.fromTimestamp(TimestampVO.fromValue(timestamp));
|
|
50
|
+
}
|
|
47
51
|
get() {
|
|
48
52
|
return this.value;
|
|
49
53
|
}
|
package/dist/year.vo.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { DateRange } from "./date-range.vo";
|
|
2
|
-
import {
|
|
2
|
+
import { TimestampVO } from "./timestamp.vo";
|
|
3
|
+
import type { TimestampValueType } from "./timestamp-value.vo";
|
|
3
4
|
import { type YearIsoIdType } from "./year-iso-id.vo";
|
|
4
5
|
export declare class Year extends DateRange {
|
|
5
|
-
static fromTimestamp(timestamp:
|
|
6
|
-
static
|
|
6
|
+
static fromTimestamp(timestamp: TimestampVO): Year;
|
|
7
|
+
static fromTimestampValue(timestamp: TimestampValueType): Year;
|
|
8
|
+
static fromNow(now: TimestampVO): Year;
|
|
7
9
|
static fromNumber(candidate: number): Year;
|
|
8
10
|
static fromIsoId(isoId: YearIsoIdType): Year;
|
|
9
11
|
toIsoId(): YearIsoIdType;
|
package/dist/year.vo.js
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { addYears, endOfYear, getYear, startOfYear } from "date-fns";
|
|
2
2
|
import { DateRange } from "./date-range.vo";
|
|
3
|
-
import {
|
|
3
|
+
import { TimestampVO } from "./timestamp.vo";
|
|
4
4
|
import { YearIsoId } from "./year-iso-id.vo";
|
|
5
5
|
export class Year extends DateRange {
|
|
6
6
|
static fromTimestamp(timestamp) {
|
|
7
|
-
const start =
|
|
8
|
-
const end =
|
|
7
|
+
const start = TimestampVO.fromNumber(startOfYear(timestamp.ms).getTime());
|
|
8
|
+
const end = TimestampVO.fromNumber(endOfYear(timestamp.ms).getTime());
|
|
9
9
|
return new Year(start, end);
|
|
10
10
|
}
|
|
11
|
+
static fromTimestampValue(timestamp) {
|
|
12
|
+
return Year.fromTimestamp(TimestampVO.fromValue(timestamp));
|
|
13
|
+
}
|
|
11
14
|
static fromNow(now) {
|
|
12
15
|
return Year.fromTimestamp(now);
|
|
13
16
|
}
|
|
@@ -16,13 +19,13 @@ export class Year extends DateRange {
|
|
|
16
19
|
}
|
|
17
20
|
static fromIsoId(isoId) {
|
|
18
21
|
const reference = Date.UTC(Number(isoId));
|
|
19
|
-
return Year.fromTimestamp(
|
|
22
|
+
return Year.fromTimestamp(TimestampVO.fromNumber(reference));
|
|
20
23
|
}
|
|
21
24
|
toIsoId() {
|
|
22
|
-
return YearIsoId.parse(String(getYear(this.getStart().
|
|
25
|
+
return YearIsoId.parse(String(getYear(this.getStart().ms)));
|
|
23
26
|
}
|
|
24
27
|
isLeapYear() {
|
|
25
|
-
const year = getYear(this.getStart().
|
|
28
|
+
const year = getYear(this.getStart().ms);
|
|
26
29
|
return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
|
|
27
30
|
}
|
|
28
31
|
previous() {
|
|
@@ -32,8 +35,8 @@ export class Year extends DateRange {
|
|
|
32
35
|
return this.shift(1);
|
|
33
36
|
}
|
|
34
37
|
shift(count) {
|
|
35
|
-
const shifted = addYears(this.getStart().
|
|
36
|
-
return Year.fromTimestamp(
|
|
38
|
+
const shifted = addYears(this.getStart().ms, count).getTime();
|
|
39
|
+
return Year.fromTimestamp(TimestampVO.fromNumber(shifted));
|
|
37
40
|
}
|
|
38
41
|
toString() {
|
|
39
42
|
return this.toIsoId();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bgord/tools",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Bartosz Gordon",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"@types/bun": "1.3.1",
|
|
28
28
|
"@types/mime-types": "3.0.1",
|
|
29
29
|
"cspell": "9.2.2",
|
|
30
|
-
"knip": "5.
|
|
30
|
+
"knip": "5.67.0",
|
|
31
31
|
"lefthook": "2.0.2",
|
|
32
32
|
"only-allow": "1.2.1",
|
|
33
33
|
"shellcheck": "4.1.0",
|
package/src/age.vo.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { differenceInYears } from "date-fns";
|
|
2
2
|
import { AgeYears, AgeYearsConstraints, type AgeYearsType } from "./age-years.vo";
|
|
3
|
-
import {
|
|
3
|
+
import { TimestampVO } from "./timestamp.vo";
|
|
4
4
|
|
|
5
5
|
export const AgeError = { FutureBirthdate: "age.future.birthdate" } as const;
|
|
6
6
|
|
|
@@ -14,16 +14,16 @@ export class Age {
|
|
|
14
14
|
return new Age(AgeYears.parse(candidate));
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
static
|
|
17
|
+
static fromBirthdateTimestamp(params: { birthdate: TimestampVO; now: TimestampVO }): Age {
|
|
18
18
|
if (params.birthdate.isAfter(params.now)) throw new Error(AgeError.FutureBirthdate);
|
|
19
|
-
return Age.fromValue(differenceInYears(params.now.
|
|
19
|
+
return Age.fromValue(differenceInYears(params.now.ms, params.birthdate.ms));
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
static fromBirthdate(candidate: { birthdate: string; now:
|
|
23
|
-
const birthdate =
|
|
22
|
+
static fromBirthdate(candidate: { birthdate: string; now: TimestampVO }): Age {
|
|
23
|
+
const birthdate = TimestampVO.fromNumber(new Date(candidate.birthdate).getTime());
|
|
24
24
|
|
|
25
25
|
if (birthdate.isAfter(candidate.now)) throw new Error(AgeError.FutureBirthdate);
|
|
26
|
-
return Age.fromValue(differenceInYears(candidate.now.
|
|
26
|
+
return Age.fromValue(differenceInYears(candidate.now.ms, birthdate.ms));
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
get(): number {
|
package/src/clock.vo.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { Hour } from "./hour.vo";
|
|
|
3
3
|
import type { HourSchemaType } from "./hour-schema.vo";
|
|
4
4
|
import { Minute } from "./minute.vo";
|
|
5
5
|
import type { MinuteSchemaType } from "./minute-schema.vo";
|
|
6
|
-
import type {
|
|
6
|
+
import type { TimestampVO } from "./timestamp.vo";
|
|
7
7
|
|
|
8
8
|
export class Clock {
|
|
9
9
|
private readonly formatter: ClockFormatter;
|
|
@@ -16,9 +16,9 @@ export class Clock {
|
|
|
16
16
|
this.formatter = formatter ?? ClockFormatters.TWENTY_FOUR_HOURS;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
static
|
|
20
|
-
const hour = Hour.
|
|
21
|
-
const minute = Minute.
|
|
19
|
+
static fromTimestamp(timestamp: TimestampVO, formatter?: ClockFormatter): Clock {
|
|
20
|
+
const hour = Hour.fromTimestamp(timestamp);
|
|
21
|
+
const minute = Minute.fromTimestamp(timestamp);
|
|
22
22
|
|
|
23
23
|
return new Clock(hour, minute, formatter);
|
|
24
24
|
}
|
|
@@ -1,22 +1,18 @@
|
|
|
1
1
|
import { Duration } from "./duration.service";
|
|
2
|
-
import
|
|
3
|
-
import { Timestamp } from "./timestamp.vo";
|
|
2
|
+
import { TimestampVO } from "./timestamp.vo";
|
|
4
3
|
|
|
5
|
-
type GetStartOfDayTsInTzConfigType = { now:
|
|
4
|
+
type GetStartOfDayTsInTzConfigType = { now: TimestampVO; timeZoneOffset: Duration };
|
|
6
5
|
|
|
7
6
|
export class DateCalculator {
|
|
8
|
-
static getStartOfDayTsInTz(config: GetStartOfDayTsInTzConfigType):
|
|
7
|
+
static getStartOfDayTsInTz(config: GetStartOfDayTsInTzConfigType): TimestampVO {
|
|
9
8
|
const dayMs = Duration.Days(1).ms;
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
const utcMidnight = Math.floor(config.now.get() / dayMs) * dayMs;
|
|
10
|
+
const utcMidnightOfNow = TimestampVO.fromNumber(Math.floor(config.now.ms / dayMs) * dayMs);
|
|
13
11
|
|
|
14
|
-
|
|
15
|
-
let start = utcMidnight + config.timeZoneOffsetMs;
|
|
12
|
+
let startOfDayInTz = utcMidnightOfNow.add(config.timeZoneOffset);
|
|
16
13
|
|
|
17
|
-
|
|
18
|
-
if (start > config.now.get()) start -= dayMs;
|
|
14
|
+
if (startOfDayInTz.isAfter(config.now)) startOfDayInTz = startOfDayInTz.subtract(Duration.Days(1));
|
|
19
15
|
|
|
20
|
-
return
|
|
16
|
+
return startOfDayInTz;
|
|
21
17
|
}
|
|
22
18
|
}
|
package/src/date-range.vo.ts
CHANGED
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { TimestampVO } from "./timestamp.vo";
|
|
2
2
|
|
|
3
3
|
export const DateRangeError = { Invalid: "date.range.invalid" } as const;
|
|
4
4
|
|
|
5
5
|
export class DateRange {
|
|
6
6
|
constructor(
|
|
7
|
-
private readonly start:
|
|
8
|
-
private readonly end:
|
|
7
|
+
private readonly start: TimestampVO,
|
|
8
|
+
private readonly end: TimestampVO,
|
|
9
9
|
) {
|
|
10
10
|
if (start.isAfter(end)) throw new Error(DateRangeError.Invalid);
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
getStart():
|
|
13
|
+
getStart(): TimestampVO {
|
|
14
14
|
return this.start;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
getEnd():
|
|
17
|
+
getEnd(): TimestampVO {
|
|
18
18
|
return this.end;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
toRange(): [
|
|
21
|
+
toRange(): [TimestampVO, TimestampVO] {
|
|
22
22
|
return [this.start, this.end];
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
contains(timestamp:
|
|
25
|
+
contains(timestamp: TimestampVO): boolean {
|
|
26
26
|
return timestamp.isAfterOrEqual(this.start) && timestamp.isBeforeOrEqual(this.end);
|
|
27
27
|
}
|
|
28
28
|
|
|
@@ -31,6 +31,6 @@ export class DateRange {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
toJSON(): { start: number; end: number } {
|
|
34
|
-
return { start: this.getStart().
|
|
34
|
+
return { start: this.getStart().ms, end: this.getEnd().ms };
|
|
35
35
|
}
|
|
36
36
|
}
|
package/src/day.vo.ts
CHANGED
|
@@ -2,13 +2,14 @@ import { formatISO } from "date-fns";
|
|
|
2
2
|
import { DateRange } from "./date-range.vo";
|
|
3
3
|
import { DayIsoId, type DayIsoIdType } from "./day-iso-id.vo";
|
|
4
4
|
import { Duration } from "./duration.service";
|
|
5
|
-
import {
|
|
5
|
+
import { TimestampVO } from "./timestamp.vo";
|
|
6
|
+
import type { TimestampValueType } from "./timestamp-value.vo";
|
|
6
7
|
|
|
7
8
|
export class Day extends DateRange {
|
|
8
|
-
static fromTimestamp(timestamp:
|
|
9
|
-
const date = new Date(timestamp.
|
|
9
|
+
static fromTimestamp(timestamp: TimestampVO): Day {
|
|
10
|
+
const date = new Date(timestamp.ms);
|
|
10
11
|
|
|
11
|
-
const startUtc =
|
|
12
|
+
const startUtc = TimestampVO.fromNumber(
|
|
12
13
|
Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()),
|
|
13
14
|
);
|
|
14
15
|
const endUtc = startUtc.add(Duration.Days(1)).subtract(Duration.Ms(1));
|
|
@@ -16,14 +17,18 @@ export class Day extends DateRange {
|
|
|
16
17
|
return new Day(startUtc, endUtc);
|
|
17
18
|
}
|
|
18
19
|
|
|
19
|
-
static
|
|
20
|
+
static fromTimestampValue(timestamp: TimestampValueType): Day {
|
|
21
|
+
return Day.fromTimestamp(TimestampVO.fromValue(timestamp));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
static fromNow(now: TimestampVO): Day {
|
|
20
25
|
return Day.fromTimestamp(now);
|
|
21
26
|
}
|
|
22
27
|
|
|
23
28
|
static fromIsoId(isoId: DayIsoIdType): Day {
|
|
24
29
|
const [year, month, day] = DayIsoId.parse(isoId).split("-").map(Number);
|
|
25
30
|
|
|
26
|
-
const startUtc =
|
|
31
|
+
const startUtc = TimestampVO.fromNumber(Date.UTC(year, month - 1, day));
|
|
27
32
|
const endUtc = startUtc.add(Duration.Days(1)).subtract(Duration.Ms(1));
|
|
28
33
|
|
|
29
34
|
return new Day(startUtc, endUtc);
|
|
@@ -32,7 +37,7 @@ export class Day extends DateRange {
|
|
|
32
37
|
toIsoId(): DayIsoIdType {
|
|
33
38
|
const midday = this.getStart().add(Duration.Hours(12));
|
|
34
39
|
|
|
35
|
-
return DayIsoId.parse(formatISO(midday.
|
|
40
|
+
return DayIsoId.parse(formatISO(midday.ms, { representation: "date" }));
|
|
36
41
|
}
|
|
37
42
|
|
|
38
43
|
previous(): Day {
|
package/src/hour.vo.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type HourFormatter, HourFormatters } from "./hour-format.service";
|
|
2
2
|
import { HourSchema, type HourSchemaType } from "./hour-schema.vo";
|
|
3
|
-
import
|
|
3
|
+
import { TimestampVO } from "./timestamp.vo";
|
|
4
|
+
import type { TimestampValueType } from "./timestamp-value.vo";
|
|
4
5
|
|
|
5
6
|
export class Hour {
|
|
6
7
|
private readonly value: HourSchemaType;
|
|
@@ -12,8 +13,12 @@ export class Hour {
|
|
|
12
13
|
this.value = HourSchema.parse(candidate);
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
static
|
|
16
|
-
return new Hour(new Date(timestamp.
|
|
16
|
+
static fromTimestamp(timestamp: TimestampVO): Hour {
|
|
17
|
+
return new Hour(new Date(timestamp.ms).getUTCHours());
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
static fromTimestampValue(timestamp: TimestampValueType): Hour {
|
|
21
|
+
return Hour.fromTimestamp(TimestampVO.fromValue(timestamp));
|
|
17
22
|
}
|
|
18
23
|
|
|
19
24
|
get(): HourSchemaType {
|
package/src/minute.vo.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { MinuteSchema, type MinuteSchemaType } from "./minute-schema.vo";
|
|
2
|
-
import
|
|
2
|
+
import { TimestampVO } from "./timestamp.vo";
|
|
3
|
+
import type { TimestampValueType } from "./timestamp-value.vo";
|
|
3
4
|
|
|
4
5
|
export class Minute {
|
|
5
6
|
private readonly value: MinuteSchemaType;
|
|
@@ -11,8 +12,12 @@ export class Minute {
|
|
|
11
12
|
this.value = MinuteSchema.parse(candidate);
|
|
12
13
|
}
|
|
13
14
|
|
|
14
|
-
static
|
|
15
|
-
return new Minute(new Date(timestamp.
|
|
15
|
+
static fromTimestamp(timestamp: TimestampVO): Minute {
|
|
16
|
+
return new Minute(new Date(timestamp.ms).getUTCMinutes());
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
static fromTimestampValue(timestamp: TimestampValueType): Minute {
|
|
20
|
+
return Minute.fromTimestamp(TimestampVO.fromValue(timestamp));
|
|
16
21
|
}
|
|
17
22
|
|
|
18
23
|
get(): MinuteSchemaType {
|
package/src/month.vo.ts
CHANGED
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
import { endOfMonth, format, getMonth, setMonth, startOfMonth } from "date-fns";
|
|
2
2
|
import { DateRange } from "./date-range.vo";
|
|
3
3
|
import { MonthIsoId, type MonthIsoIdType } from "./month-iso-id.vo";
|
|
4
|
-
import {
|
|
4
|
+
import { TimestampVO } from "./timestamp.vo";
|
|
5
|
+
import type { TimestampValueType } from "./timestamp-value.vo";
|
|
5
6
|
|
|
6
7
|
export class Month extends DateRange {
|
|
7
|
-
static fromTimestamp(timestamp:
|
|
8
|
-
const start =
|
|
9
|
-
const end =
|
|
8
|
+
static fromTimestamp(timestamp: TimestampVO): Month {
|
|
9
|
+
const start = TimestampVO.fromNumber(startOfMonth(timestamp.ms).getTime());
|
|
10
|
+
const end = TimestampVO.fromNumber(endOfMonth(timestamp.ms).getTime());
|
|
10
11
|
|
|
11
12
|
return new Month(start, end);
|
|
12
13
|
}
|
|
13
14
|
|
|
14
|
-
static
|
|
15
|
+
static fromTimestampValue(timestamp: TimestampValueType): Month {
|
|
16
|
+
return Month.fromTimestamp(TimestampVO.fromValue(timestamp));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
static fromNow(now: TimestampVO): Month {
|
|
15
20
|
return Month.fromTimestamp(now);
|
|
16
21
|
}
|
|
17
22
|
|
|
@@ -20,11 +25,11 @@ export class Month extends DateRange {
|
|
|
20
25
|
|
|
21
26
|
const reference = setMonth(Date.UTC(year), month - 1).getTime();
|
|
22
27
|
|
|
23
|
-
return Month.fromTimestamp(
|
|
28
|
+
return Month.fromTimestamp(TimestampVO.fromNumber(reference));
|
|
24
29
|
}
|
|
25
30
|
|
|
26
31
|
toIsoId(): MonthIsoIdType {
|
|
27
|
-
return MonthIsoId.parse(format(this.getStart().
|
|
32
|
+
return MonthIsoId.parse(format(this.getStart().ms, "yyyy-MM"));
|
|
28
33
|
}
|
|
29
34
|
|
|
30
35
|
previous(): Month {
|
|
@@ -36,9 +41,9 @@ export class Month extends DateRange {
|
|
|
36
41
|
}
|
|
37
42
|
|
|
38
43
|
shift(count: number): Month {
|
|
39
|
-
const shifted = setMonth(this.getStart().
|
|
44
|
+
const shifted = setMonth(this.getStart().ms, getMonth(this.getStart().ms) + count).getTime();
|
|
40
45
|
|
|
41
|
-
return Month.fromTimestamp(
|
|
46
|
+
return Month.fromTimestamp(TimestampVO.fromNumber(shifted));
|
|
42
47
|
}
|
|
43
48
|
|
|
44
49
|
toString(): string {
|
package/src/quarter.vo.ts
CHANGED
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
import { endOfQuarter, getQuarter, getYear, setQuarter, startOfQuarter } from "date-fns";
|
|
2
2
|
import { DateRange } from "./date-range.vo";
|
|
3
3
|
import { QuarterIsoId, type QuarterIsoIdType } from "./quarter-iso-id.vo";
|
|
4
|
-
import {
|
|
4
|
+
import { TimestampVO } from "./timestamp.vo";
|
|
5
|
+
import type { TimestampValueType } from "./timestamp-value.vo";
|
|
5
6
|
|
|
6
7
|
export class Quarter extends DateRange {
|
|
7
|
-
static fromTimestamp(timestamp:
|
|
8
|
-
const start =
|
|
9
|
-
const end =
|
|
8
|
+
static fromTimestamp(timestamp: TimestampVO): Quarter {
|
|
9
|
+
const start = TimestampVO.fromNumber(startOfQuarter(timestamp.ms).getTime());
|
|
10
|
+
const end = TimestampVO.fromNumber(endOfQuarter(timestamp.ms).getTime());
|
|
10
11
|
|
|
11
12
|
return new Quarter(start, end);
|
|
12
13
|
}
|
|
13
14
|
|
|
14
|
-
static
|
|
15
|
+
static fromTimestampValue(timestamp: TimestampValueType): Quarter {
|
|
16
|
+
return Quarter.fromTimestamp(TimestampVO.fromValue(timestamp));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
static fromNow(now: TimestampVO): Quarter {
|
|
15
20
|
return Quarter.fromTimestamp(now);
|
|
16
21
|
}
|
|
17
22
|
|
|
@@ -20,12 +25,12 @@ export class Quarter extends DateRange {
|
|
|
20
25
|
|
|
21
26
|
const reference = setQuarter(Date.UTC(year), quarter).getTime();
|
|
22
27
|
|
|
23
|
-
return Quarter.fromTimestamp(
|
|
28
|
+
return Quarter.fromTimestamp(TimestampVO.fromNumber(reference));
|
|
24
29
|
}
|
|
25
30
|
|
|
26
31
|
toIsoId(): QuarterIsoIdType {
|
|
27
|
-
const year = getYear(this.getStart().
|
|
28
|
-
const quarter = getQuarter(this.getStart().
|
|
32
|
+
const year = getYear(this.getStart().ms);
|
|
33
|
+
const quarter = getQuarter(this.getStart().ms);
|
|
29
34
|
|
|
30
35
|
return QuarterIsoId.parse(`${year}-Q${quarter}`);
|
|
31
36
|
}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import { Duration } from "./duration.service";
|
|
2
|
-
import type {
|
|
1
|
+
import type { Duration } from "./duration.service";
|
|
2
|
+
import type { TimestampVO } from "./timestamp.vo";
|
|
3
3
|
|
|
4
4
|
type RateLimiterResultSuccessType = { allowed: true };
|
|
5
5
|
type RateLimiterResultErrorType = { allowed: false; remaining: Duration };
|
|
6
6
|
type RateLimiterResultType = RateLimiterResultSuccessType | RateLimiterResultErrorType;
|
|
7
7
|
|
|
8
8
|
export class RateLimiter {
|
|
9
|
-
private lastInvocation:
|
|
9
|
+
private lastInvocation: TimestampVO | null = null;
|
|
10
10
|
|
|
11
11
|
constructor(private readonly duration: Duration) {}
|
|
12
12
|
|
|
13
|
-
verify(now:
|
|
13
|
+
verify(now: TimestampVO): RateLimiterResultType {
|
|
14
14
|
if (this.lastInvocation == null) {
|
|
15
15
|
this.lastInvocation = now;
|
|
16
16
|
|
|
@@ -25,8 +25,6 @@ export class RateLimiter {
|
|
|
25
25
|
return { allowed: true };
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
return { allowed: false, remaining: Duration.Ms(remainingDelta) };
|
|
28
|
+
return { allowed: false, remaining: nextAllowedTimestamp.difference(now) };
|
|
31
29
|
}
|
|
32
30
|
}
|
package/src/relative-date.vo.ts
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import { DateFormatters } from "./date-formatter.service";
|
|
2
|
-
import type {
|
|
2
|
+
import type { TimestampVO } from "./timestamp.vo";
|
|
3
3
|
import type { TimestampValueType } from "./timestamp-value.vo";
|
|
4
4
|
import type { Falsy } from "./ts-utils";
|
|
5
5
|
|
|
6
6
|
type RelativeDateType = { raw: TimestampValueType; relative: string };
|
|
7
7
|
|
|
8
8
|
export class RelativeDate {
|
|
9
|
-
static truthy(timestamp:
|
|
9
|
+
static truthy(timestamp: TimestampVO): RelativeDateType {
|
|
10
10
|
return RelativeDate._format(timestamp);
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
static falsy(timestamp: Falsy<
|
|
13
|
+
static falsy(timestamp: Falsy<TimestampVO>): RelativeDateType | null {
|
|
14
14
|
if (!timestamp) return null;
|
|
15
15
|
return RelativeDate._format(timestamp);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
private static _format(timestamp:
|
|
19
|
-
return { raw: timestamp.
|
|
18
|
+
private static _format(timestamp: TimestampVO): RelativeDateType {
|
|
19
|
+
return { raw: timestamp.ms, relative: DateFormatters.relative(timestamp.ms) };
|
|
20
20
|
}
|
|
21
21
|
}
|
package/src/stopwatch.service.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Duration } from "./duration.service";
|
|
2
|
-
import {
|
|
1
|
+
import type { Duration } from "./duration.service";
|
|
2
|
+
import { TimestampVO } from "./timestamp.vo";
|
|
3
3
|
|
|
4
4
|
export const StopwatchError = { AlreadyStopped: "stopwatch.already.stopped" } as const;
|
|
5
5
|
|
|
@@ -13,13 +13,13 @@ export type StopwatchResultType = Duration;
|
|
|
13
13
|
export class Stopwatch {
|
|
14
14
|
private state: StopwatchState = StopwatchState.started;
|
|
15
15
|
|
|
16
|
-
constructor(private readonly start:
|
|
16
|
+
constructor(private readonly start: TimestampVO) {}
|
|
17
17
|
|
|
18
18
|
stop(): StopwatchResultType {
|
|
19
19
|
if (this.state === StopwatchState.stopped) throw new Error(StopwatchError.AlreadyStopped);
|
|
20
20
|
|
|
21
21
|
this.state = StopwatchState.stopped;
|
|
22
22
|
|
|
23
|
-
return
|
|
23
|
+
return TimestampVO.fromNumber(Date.now()).difference(this.start);
|
|
24
24
|
}
|
|
25
25
|
}
|
package/src/timestamp.vo.ts
CHANGED
|
@@ -1,46 +1,50 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Duration } from "./duration.service";
|
|
2
2
|
import { TimestampValue, type TimestampValueType } from "./timestamp-value.vo";
|
|
3
3
|
|
|
4
|
-
export class
|
|
4
|
+
export class TimestampVO {
|
|
5
5
|
constructor(private readonly value: TimestampValueType) {}
|
|
6
6
|
|
|
7
|
-
static fromValue(value: TimestampValueType):
|
|
8
|
-
return new
|
|
7
|
+
static fromValue(value: TimestampValueType): TimestampVO {
|
|
8
|
+
return new TimestampVO(value);
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
static fromNumber(value: number):
|
|
12
|
-
return new
|
|
11
|
+
static fromNumber(value: number): TimestampVO {
|
|
12
|
+
return new TimestampVO(TimestampValue.parse(value));
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
add(duration: Duration):
|
|
16
|
-
return
|
|
15
|
+
add(duration: Duration): TimestampVO {
|
|
16
|
+
return TimestampVO.fromNumber(this.value + duration.ms);
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
subtract(duration: Duration):
|
|
20
|
-
return
|
|
19
|
+
subtract(duration: Duration): TimestampVO {
|
|
20
|
+
return TimestampVO.fromNumber(this.value - duration.ms);
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
difference(another: TimestampVO): Duration {
|
|
24
|
+
return Duration.Ms(this.value - another.value);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
isBefore(another: TimestampVO): boolean {
|
|
24
28
|
return this.value < another.value;
|
|
25
29
|
}
|
|
26
30
|
|
|
27
|
-
isBeforeOrEqual(another:
|
|
31
|
+
isBeforeOrEqual(another: TimestampVO): boolean {
|
|
28
32
|
return this.value <= another.value;
|
|
29
33
|
}
|
|
30
34
|
|
|
31
|
-
isAfter(another:
|
|
35
|
+
isAfter(another: TimestampVO): boolean {
|
|
32
36
|
return this.value > another.value;
|
|
33
37
|
}
|
|
34
38
|
|
|
35
|
-
isAfterOrEqual(another:
|
|
39
|
+
isAfterOrEqual(another: TimestampVO): boolean {
|
|
36
40
|
return this.value >= another.value;
|
|
37
41
|
}
|
|
38
42
|
|
|
39
|
-
equals(another:
|
|
43
|
+
equals(another: TimestampVO): boolean {
|
|
40
44
|
return this.value === another.value;
|
|
41
45
|
}
|
|
42
46
|
|
|
43
|
-
get(): TimestampValueType {
|
|
47
|
+
get ms(): TimestampValueType {
|
|
44
48
|
return this.value;
|
|
45
49
|
}
|
|
46
50
|
|
package/src/week.vo.ts
CHANGED
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
import { addWeeks, endOfISOWeek, getISOWeek, getISOWeekYear, setISOWeek, startOfISOWeek } from "date-fns";
|
|
2
2
|
import { DateRange } from "./date-range.vo";
|
|
3
|
-
import {
|
|
3
|
+
import { TimestampVO } from "./timestamp.vo";
|
|
4
|
+
import type { TimestampValueType } from "./timestamp-value.vo";
|
|
4
5
|
import { WeekIsoId, type WeekIsoIdType } from "./week-iso-id.vo";
|
|
5
6
|
|
|
6
7
|
export class Week extends DateRange {
|
|
7
|
-
static fromTimestamp(timestamp:
|
|
8
|
-
const start =
|
|
9
|
-
const end =
|
|
8
|
+
static fromTimestamp(timestamp: TimestampVO): Week {
|
|
9
|
+
const start = TimestampVO.fromNumber(startOfISOWeek(timestamp.ms).getTime());
|
|
10
|
+
const end = TimestampVO.fromNumber(endOfISOWeek(timestamp.ms).getTime());
|
|
10
11
|
|
|
11
12
|
return new Week(start, end);
|
|
12
13
|
}
|
|
13
14
|
|
|
14
|
-
static
|
|
15
|
+
static fromTimestampValue(timestamp: TimestampValueType): Week {
|
|
16
|
+
return Week.fromTimestamp(TimestampVO.fromValue(timestamp));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
static fromNow(now: TimestampVO): Week {
|
|
15
20
|
return Week.fromTimestamp(now);
|
|
16
21
|
}
|
|
17
22
|
|
|
@@ -21,12 +26,12 @@ export class Week extends DateRange {
|
|
|
21
26
|
// ISO-8601 rule: Jan 4 is always in week 01 of the ISO week-year.
|
|
22
27
|
const reference = setISOWeek(Date.UTC(year, 0, 4), week).getTime();
|
|
23
28
|
|
|
24
|
-
return Week.fromTimestamp(
|
|
29
|
+
return Week.fromTimestamp(TimestampVO.fromNumber(reference));
|
|
25
30
|
}
|
|
26
31
|
|
|
27
32
|
toIsoId(): WeekIsoIdType {
|
|
28
|
-
const year = getISOWeekYear(this.getStart().
|
|
29
|
-
const week = getISOWeek(this.getStart().
|
|
33
|
+
const year = getISOWeekYear(this.getStart().ms);
|
|
34
|
+
const week = getISOWeek(this.getStart().ms);
|
|
30
35
|
|
|
31
36
|
return WeekIsoId.parse(`${year}-W${String(week).padStart(2, "0")}`);
|
|
32
37
|
}
|
|
@@ -40,9 +45,9 @@ export class Week extends DateRange {
|
|
|
40
45
|
}
|
|
41
46
|
|
|
42
47
|
shift(count: number): Week {
|
|
43
|
-
const shifted = addWeeks(this.getStart().
|
|
48
|
+
const shifted = addWeeks(this.getStart().ms, count).getTime();
|
|
44
49
|
|
|
45
|
-
return Week.fromTimestamp(
|
|
50
|
+
return Week.fromTimestamp(TimestampVO.fromNumber(shifted));
|
|
46
51
|
}
|
|
47
52
|
|
|
48
53
|
toString(): string {
|