@bgord/tools 1.0.4 → 1.1.0

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.
Files changed (60) hide show
  1. package/dist/age.vo.d.ts +4 -4
  2. package/dist/age.vo.js +6 -5
  3. package/dist/clock.vo.d.ts +2 -2
  4. package/dist/clock.vo.js +7 -7
  5. package/dist/date-calculator.service.d.ts +3 -3
  6. package/dist/date-calculator.service.js +3 -3
  7. package/dist/date-range.vo.d.ts +6 -6
  8. package/dist/date-range.vo.js +4 -4
  9. package/dist/day.vo.d.ts +3 -4
  10. package/dist/day.vo.js +10 -14
  11. package/dist/hour.vo.d.ts +2 -2
  12. package/dist/hour.vo.js +1 -1
  13. package/dist/index.d.ts +1 -1
  14. package/dist/index.js +1 -1
  15. package/dist/minute.vo.d.ts +2 -2
  16. package/dist/minute.vo.js +1 -1
  17. package/dist/month.vo.d.ts +3 -3
  18. package/dist/month.vo.js +6 -6
  19. package/dist/quarter.vo.d.ts +3 -3
  20. package/dist/quarter.vo.js +5 -5
  21. package/dist/rate-limiter.service.d.ts +2 -2
  22. package/dist/rate-limiter.service.js +3 -3
  23. package/dist/relative-date.vo.d.ts +5 -4
  24. package/dist/relative-date.vo.js +2 -2
  25. package/dist/stopwatch.service.d.ts +3 -3
  26. package/dist/stopwatch.service.js +4 -4
  27. package/dist/timestamp-value.vo.d.ts +6 -0
  28. package/dist/timestamp-value.vo.js +7 -0
  29. package/dist/timestamp.vo.d.ts +18 -6
  30. package/dist/timestamp.vo.js +43 -7
  31. package/dist/tsconfig.tsbuildinfo +1 -1
  32. package/dist/week.vo.d.ts +3 -3
  33. package/dist/week.vo.js +7 -7
  34. package/dist/weekday.vo.d.ts +2 -2
  35. package/dist/weekday.vo.js +1 -1
  36. package/dist/year.vo.d.ts +3 -3
  37. package/dist/year.vo.js +7 -7
  38. package/package.json +4 -4
  39. package/readme.md +1 -1
  40. package/src/age.vo.ts +8 -8
  41. package/src/clock.vo.ts +7 -7
  42. package/src/date-calculator.service.ts +6 -6
  43. package/src/date-range.vo.ts +11 -11
  44. package/src/day.vo.ts +15 -19
  45. package/src/hour.vo.ts +3 -3
  46. package/src/index.ts +1 -1
  47. package/src/minute.vo.ts +3 -3
  48. package/src/month.vo.ts +9 -9
  49. package/src/quarter.vo.ts +8 -8
  50. package/src/rate-limiter.service.ts +6 -6
  51. package/src/relative-date.vo.ts +7 -6
  52. package/src/stopwatch.service.ts +3 -3
  53. package/src/timestamp-value.vo.ts +11 -0
  54. package/src/timestamp.vo.ts +51 -8
  55. package/src/week.vo.ts +10 -10
  56. package/src/weekday.vo.ts +3 -3
  57. package/src/year.vo.ts +10 -10
  58. package/dist/time.service.d.ts +0 -8
  59. package/dist/time.service.js +0 -13
  60. package/src/time.service.ts +0 -15
package/dist/week.vo.js CHANGED
@@ -4,8 +4,8 @@ import { Timestamp } from "./timestamp.vo";
4
4
  import { WeekIsoId } from "./week-iso-id.vo";
5
5
  export class Week extends DateRange {
6
6
  static fromTimestamp(timestamp) {
7
- const start = Timestamp.parse(startOfISOWeek(timestamp).getTime());
8
- const end = Timestamp.parse(endOfISOWeek(timestamp).getTime());
7
+ const start = Timestamp.fromNumber(startOfISOWeek(timestamp.get()).getTime());
8
+ const end = Timestamp.fromNumber(endOfISOWeek(timestamp.get()).getTime());
9
9
  return new Week(start, end);
10
10
  }
11
11
  static fromNow(now) {
@@ -15,11 +15,11 @@ export class Week extends DateRange {
15
15
  const [year, week] = WeekIsoId.parse(isoId).split("-W").map(Number);
16
16
  // ISO-8601 rule: Jan 4 is always in week 01 of the ISO week-year.
17
17
  const reference = setISOWeek(Date.UTC(year, 0, 4), week).getTime();
18
- return Week.fromTimestamp(Timestamp.parse(reference));
18
+ return Week.fromTimestamp(Timestamp.fromNumber(reference));
19
19
  }
20
20
  toIsoId() {
21
- const year = getISOWeekYear(this.getStart());
22
- const week = getISOWeek(this.getStart());
21
+ const year = getISOWeekYear(this.getStart().get());
22
+ const week = getISOWeek(this.getStart().get());
23
23
  return WeekIsoId.parse(`${year}-W${String(week).padStart(2, "0")}`);
24
24
  }
25
25
  previous() {
@@ -29,8 +29,8 @@ export class Week extends DateRange {
29
29
  return this.shift(1);
30
30
  }
31
31
  shift(count) {
32
- const shifted = addWeeks(this.getStart(), count).getTime();
33
- return Week.fromTimestamp(Timestamp.parse(shifted));
32
+ const shifted = addWeeks(this.getStart().get(), count).getTime();
33
+ return Week.fromTimestamp(Timestamp.fromNumber(shifted));
34
34
  }
35
35
  toString() {
36
36
  return this.toIsoId();
@@ -1,4 +1,4 @@
1
- import type { TimestampType } from "./timestamp.vo";
1
+ import type { Timestamp } from "./timestamp.vo";
2
2
  export type WeekdayFormatter = (value: Weekday["value"]) => string;
3
3
  export declare enum WeekdayFormatterEnum {
4
4
  FULL = "FULL",// "Sunday"
@@ -19,7 +19,7 @@ export declare class Weekday {
19
19
  static readonly FRIDAY: Weekday;
20
20
  static readonly SATURDAY: Weekday;
21
21
  constructor(candidate: number, formatter?: WeekdayFormatter);
22
- static fromUtcTimestamp(timestamp: TimestampType, formatter?: WeekdayFormatter): Weekday;
22
+ static fromUtcTimestamp(timestamp: Timestamp, formatter?: WeekdayFormatter): Weekday;
23
23
  get(): number;
24
24
  format(): string;
25
25
  toString(): string;
@@ -41,7 +41,7 @@ export class Weekday {
41
41
  this.formatter = formatter ?? WeekdayFormatters.FULL;
42
42
  }
43
43
  static fromUtcTimestamp(timestamp, formatter) {
44
- const dayZeroBased = new Date(timestamp).getUTCDay(); // 0..6
44
+ const dayZeroBased = new Date(timestamp.get()).getUTCDay(); // 0..6
45
45
  return new Weekday(dayZeroBased, formatter);
46
46
  }
47
47
  get() {
package/dist/year.vo.d.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import { DateRange } from "./date-range.vo";
2
- import { type TimestampType } from "./timestamp.vo";
2
+ import { Timestamp } from "./timestamp.vo";
3
3
  import { type YearIsoIdType } from "./year-iso-id.vo";
4
4
  export declare class Year extends DateRange {
5
- static fromTimestamp(timestamp: TimestampType): Year;
6
- static fromNow(now: TimestampType): Year;
5
+ static fromTimestamp(timestamp: Timestamp): Year;
6
+ static fromNow(now: Timestamp): Year;
7
7
  static fromNumber(candidate: number): Year;
8
8
  static fromIsoId(isoId: YearIsoIdType): Year;
9
9
  toIsoId(): YearIsoIdType;
package/dist/year.vo.js CHANGED
@@ -4,8 +4,8 @@ import { Timestamp } 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 = Timestamp.parse(startOfYear(timestamp).getTime());
8
- const end = Timestamp.parse(endOfYear(timestamp).getTime());
7
+ const start = Timestamp.fromNumber(startOfYear(timestamp.get()).getTime());
8
+ const end = Timestamp.fromNumber(endOfYear(timestamp.get()).getTime());
9
9
  return new Year(start, end);
10
10
  }
11
11
  static fromNow(now) {
@@ -16,13 +16,13 @@ export class Year extends DateRange {
16
16
  }
17
17
  static fromIsoId(isoId) {
18
18
  const reference = Date.UTC(Number(isoId));
19
- return Year.fromTimestamp(Timestamp.parse(reference));
19
+ return Year.fromTimestamp(Timestamp.fromNumber(reference));
20
20
  }
21
21
  toIsoId() {
22
- return YearIsoId.parse(String(getYear(this.getStart())));
22
+ return YearIsoId.parse(String(getYear(this.getStart().get())));
23
23
  }
24
24
  isLeapYear() {
25
- const year = getYear(this.getStart());
25
+ const year = getYear(this.getStart().get());
26
26
  return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
27
27
  }
28
28
  previous() {
@@ -32,8 +32,8 @@ export class Year extends DateRange {
32
32
  return this.shift(1);
33
33
  }
34
34
  shift(count) {
35
- const shifted = addYears(this.getStart(), count).getTime();
36
- return Year.fromTimestamp(Timestamp.parse(shifted));
35
+ const shifted = addYears(this.getStart().get(), count).getTime();
36
+ return Year.fromTimestamp(Timestamp.fromNumber(shifted));
37
37
  }
38
38
  toString() {
39
39
  return this.toIsoId();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bgord/tools",
3
- "version": "1.0.4",
3
+ "version": "1.1.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "author": "Bartosz Gordon",
@@ -21,14 +21,14 @@
21
21
  "preinstall": "bunx only-allow bun"
22
22
  },
23
23
  "devDependencies": {
24
- "@biomejs/biome": "2.3.1",
24
+ "@biomejs/biome": "2.3.2",
25
25
  "@commitlint/cli": "20.1.0",
26
26
  "@commitlint/config-conventional": "20.0.0",
27
27
  "@types/bun": "1.3.1",
28
28
  "@types/mime-types": "3.0.1",
29
29
  "cspell": "9.2.2",
30
- "knip": "5.66.3",
31
- "lefthook": "2.0.1",
30
+ "knip": "5.66.4",
31
+ "lefthook": "2.0.2",
32
32
  "only-allow": "1.2.1",
33
33
  "shellcheck": "4.1.0",
34
34
  "typescript": "5.9.3",
package/readme.md CHANGED
@@ -104,7 +104,7 @@ src/
104
104
  ├── sum.service.ts
105
105
  ├── thousands-separator.service.ts
106
106
  ├── time-zone-offset-value.vo.ts
107
- ├── time.service.ts
107
+ ├── timestamp-value.vo.ts
108
108
  ├── timestamp.vo.ts
109
109
  ├── timezone.vo.ts
110
110
  ├── ts-utils.ts
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 type { TimestampType } from "./timestamp.vo";
3
+ import { Timestamp } 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 fromBirthdateEpochMs(params: { birthdate: TimestampType; now: TimestampType }): Age {
18
- if (params.birthdate > params.now) throw new Error(AgeError.FutureBirthdate);
19
- return Age.fromValue(differenceInYears(params.now, params.birthdate));
17
+ static fromBirthdateEpochMs(params: { birthdate: Timestamp; now: Timestamp }): Age {
18
+ if (params.birthdate.isAfter(params.now)) throw new Error(AgeError.FutureBirthdate);
19
+ return Age.fromValue(differenceInYears(params.now.get(), params.birthdate.get()));
20
20
  }
21
21
 
22
- static fromBirthdate(candidate: { birthdate: string; now: TimestampType }): Age {
23
- const birthdateMs = new Date(candidate.birthdate).getTime();
22
+ static fromBirthdate(candidate: { birthdate: string; now: Timestamp }): Age {
23
+ const birthdate = Timestamp.fromNumber(new Date(candidate.birthdate).getTime());
24
24
 
25
- if (birthdateMs > candidate.now) throw new Error(AgeError.FutureBirthdate);
26
- return Age.fromValue(differenceInYears(candidate.now, birthdateMs));
25
+ if (birthdate.isAfter(candidate.now)) throw new Error(AgeError.FutureBirthdate);
26
+ return Age.fromValue(differenceInYears(candidate.now.get(), birthdate.get()));
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 { TimestampType } from "./timestamp.vo";
6
+ import type { Timestamp } from "./timestamp.vo";
7
7
 
8
8
  export class Clock {
9
9
  private readonly formatter: ClockFormatter;
@@ -16,7 +16,7 @@ export class Clock {
16
16
  this.formatter = formatter ?? ClockFormatters.TWENTY_FOUR_HOURS;
17
17
  }
18
18
 
19
- static fromEpochMs(timestamp: TimestampType, formatter?: ClockFormatter): Clock {
19
+ static fromEpochMs(timestamp: Timestamp, formatter?: ClockFormatter): Clock {
20
20
  const hour = Hour.fromEpochMs(timestamp);
21
21
  const minute = Minute.fromEpochMs(timestamp);
22
22
 
@@ -32,17 +32,17 @@ export class Clock {
32
32
  }
33
33
 
34
34
  equals(another: Clock): boolean {
35
- return this.hour.get() === another.hour.get() && this.minute.get() === another.minute.get();
35
+ return this.hour.equals(another.hour) && this.minute.equals(another.minute);
36
36
  }
37
37
 
38
38
  isAfter(another: Clock): boolean {
39
- if (this.hour.get() !== another.hour.get()) return this.hour.get() > another.hour.get();
40
- return this.minute.get() > another.minute.get();
39
+ if (!this.hour.equals(another.hour)) return this.hour.isAfter(another.hour);
40
+ return this.minute.isAfter(another.minute);
41
41
  }
42
42
 
43
43
  isBefore(another: Clock): boolean {
44
- if (this.hour.get() !== another.hour.get()) return this.hour.get() < another.hour.get();
45
- return this.minute.get() < another.minute.get();
44
+ if (!this.hour.equals(another.hour)) return this.hour.isBefore(another.hour);
45
+ return this.minute.isBefore(another.minute);
46
46
  }
47
47
 
48
48
  toString(): string {
@@ -1,22 +1,22 @@
1
1
  import { Duration } from "./duration.service";
2
2
  import type { TimeZoneOffsetValueType } from "./time-zone-offset-value.vo";
3
- import { Timestamp, type TimestampType } from "./timestamp.vo";
3
+ import { Timestamp } from "./timestamp.vo";
4
4
 
5
- type GetStartOfDayTsInTzConfigType = { now: TimestampType; timeZoneOffsetMs: TimeZoneOffsetValueType };
5
+ type GetStartOfDayTsInTzConfigType = { now: Timestamp; timeZoneOffsetMs: TimeZoneOffsetValueType };
6
6
 
7
7
  export class DateCalculator {
8
- static getStartOfDayTsInTz(config: GetStartOfDayTsInTzConfigType): TimestampType {
8
+ static getStartOfDayTsInTz(config: GetStartOfDayTsInTzConfigType): Timestamp {
9
9
  const dayMs = Duration.Days(1).ms;
10
10
 
11
11
  // UTC midnight for the UTC date of `now`
12
- const utcMidnight = Math.floor(config.now / dayMs) * dayMs;
12
+ const utcMidnight = Math.floor(config.now.get() / dayMs) * dayMs;
13
13
 
14
14
  // Candidate start of the local day (in UTC), anchored to the same UTC date
15
15
  let start = utcMidnight + config.timeZoneOffsetMs;
16
16
 
17
17
  // If the candidate is in the future relative to `now`, it means local midnight was "yesterday" in UTC.
18
- if (start > config.now) start -= dayMs;
18
+ if (start > config.now.get()) start -= dayMs;
19
19
 
20
- return Timestamp.parse(start);
20
+ return Timestamp.fromNumber(start);
21
21
  }
22
22
  }
@@ -1,36 +1,36 @@
1
- import type { TimestampType } from "./timestamp.vo";
1
+ import type { Timestamp } 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: TimestampType,
8
- private readonly end: TimestampType,
7
+ private readonly start: Timestamp,
8
+ private readonly end: Timestamp,
9
9
  ) {
10
- if (start > end) throw new Error(DateRangeError.Invalid);
10
+ if (start.isAfter(end)) throw new Error(DateRangeError.Invalid);
11
11
  }
12
12
 
13
- getStart(): TimestampType {
13
+ getStart(): Timestamp {
14
14
  return this.start;
15
15
  }
16
16
 
17
- getEnd(): TimestampType {
17
+ getEnd(): Timestamp {
18
18
  return this.end;
19
19
  }
20
20
 
21
- toRange(): [TimestampType, TimestampType] {
21
+ toRange(): [Timestamp, Timestamp] {
22
22
  return [this.start, this.end];
23
23
  }
24
24
 
25
- contains(timestamp: TimestampType): boolean {
26
- return timestamp >= this.start && timestamp <= this.end;
25
+ contains(timestamp: Timestamp): boolean {
26
+ return timestamp.isAfterOrEqual(this.start) && timestamp.isBeforeOrEqual(this.end);
27
27
  }
28
28
 
29
29
  equals(other: DateRange): boolean {
30
- return this.start === other.start && this.end === other.end;
30
+ return this.start.equals(other.start) && this.end.equals(other.end);
31
31
  }
32
32
 
33
33
  toJSON(): { start: number; end: number } {
34
- return { start: this.getStart(), end: this.getEnd() };
34
+ return { start: this.getStart().get(), end: this.getEnd().get() };
35
35
  }
36
36
  }
package/src/day.vo.ts CHANGED
@@ -2,39 +2,37 @@ 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 { Timestamp, type TimestampType } from "./timestamp.vo";
5
+ import { Timestamp } from "./timestamp.vo";
6
6
 
7
7
  export class Day extends DateRange {
8
- private constructor(start: TimestampType, end: TimestampType) {
9
- super(start, end);
10
- }
11
-
12
- static fromTimestamp(timestamp: TimestampType): Day {
13
- const date = new Date(timestamp);
8
+ static fromTimestamp(timestamp: Timestamp): Day {
9
+ const date = new Date(timestamp.get());
14
10
 
15
- const startUtc = Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate());
16
- const endUtc = startUtc + Duration.Days(1).ms - 1;
11
+ const startUtc = Timestamp.fromNumber(
12
+ Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()),
13
+ );
14
+ const endUtc = startUtc.add(Duration.Days(1)).subtract(Duration.Ms(1));
17
15
 
18
- return new Day(Timestamp.parse(startUtc), Timestamp.parse(endUtc));
16
+ return new Day(startUtc, endUtc);
19
17
  }
20
18
 
21
- static fromNow(now: TimestampType): Day {
19
+ static fromNow(now: Timestamp): Day {
22
20
  return Day.fromTimestamp(now);
23
21
  }
24
22
 
25
23
  static fromIsoId(isoId: DayIsoIdType): Day {
26
24
  const [year, month, day] = DayIsoId.parse(isoId).split("-").map(Number);
27
25
 
28
- const startUtc = Date.UTC(year, month - 1, day);
29
- const endUtc = startUtc + Duration.Days(1).ms - 1;
26
+ const startUtc = Timestamp.fromNumber(Date.UTC(year, month - 1, day));
27
+ const endUtc = startUtc.add(Duration.Days(1)).subtract(Duration.Ms(1));
30
28
 
31
- return new Day(Timestamp.parse(startUtc), Timestamp.parse(endUtc));
29
+ return new Day(startUtc, endUtc);
32
30
  }
33
31
 
34
32
  toIsoId(): DayIsoIdType {
35
- const midday = this.getStart() + Duration.Hours(12).ms;
33
+ const midday = this.getStart().add(Duration.Hours(12));
36
34
 
37
- return DayIsoId.parse(formatISO(midday, { representation: "date" }));
35
+ return DayIsoId.parse(formatISO(midday.get(), { representation: "date" }));
38
36
  }
39
37
 
40
38
  previous(): Day {
@@ -46,9 +44,7 @@ export class Day extends DateRange {
46
44
  }
47
45
 
48
46
  shift(count: number): Day {
49
- const timestamp = this.getStart() + count * Duration.Days(1).ms;
50
-
51
- return Day.fromTimestamp(Timestamp.parse(timestamp));
47
+ return Day.fromTimestamp(this.getStart().add(Duration.Days(count)));
52
48
  }
53
49
 
54
50
  toString(): string {
package/src/hour.vo.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { type HourFormatter, HourFormatters } from "./hour-format.service";
2
2
  import { HourSchema, type HourSchemaType } from "./hour-schema.vo";
3
- import type { TimestampType } from "./timestamp.vo";
3
+ import type { Timestamp } from "./timestamp.vo";
4
4
 
5
5
  export class Hour {
6
6
  private readonly value: HourSchemaType;
@@ -12,8 +12,8 @@ export class Hour {
12
12
  this.value = HourSchema.parse(candidate);
13
13
  }
14
14
 
15
- static fromEpochMs(timestamp: TimestampType): Hour {
16
- return new Hour(new Date(timestamp).getUTCHours());
15
+ static fromEpochMs(timestamp: Timestamp): Hour {
16
+ return new Hour(new Date(timestamp.get()).getUTCHours());
17
17
  }
18
18
 
19
19
  get(): HourSchemaType {
package/src/index.ts CHANGED
@@ -78,9 +78,9 @@ export * from "./size-bytes.vo";
78
78
  export * from "./stopwatch.service";
79
79
  export * from "./sum.service";
80
80
  export * from "./thousands-separator.service";
81
- export * from "./time.service";
82
81
  export * from "./time-zone-offset-value.vo";
83
82
  export * from "./timestamp.vo";
83
+ export * from "./timestamp-value.vo";
84
84
  export * from "./timezone.vo";
85
85
  export * from "./ts-utils";
86
86
  export * from "./visually-unambiguous-characters-generator.service";
package/src/minute.vo.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { MinuteSchema, type MinuteSchemaType } from "./minute-schema.vo";
2
- import type { TimestampType } from "./timestamp.vo";
2
+ import type { Timestamp } from "./timestamp.vo";
3
3
 
4
4
  export class Minute {
5
5
  private readonly value: MinuteSchemaType;
@@ -11,8 +11,8 @@ export class Minute {
11
11
  this.value = MinuteSchema.parse(candidate);
12
12
  }
13
13
 
14
- static fromEpochMs(timestamp: TimestampType): Minute {
15
- return new Minute(new Date(timestamp).getUTCMinutes());
14
+ static fromEpochMs(timestamp: Timestamp): Minute {
15
+ return new Minute(new Date(timestamp.get()).getUTCMinutes());
16
16
  }
17
17
 
18
18
  get(): MinuteSchemaType {
package/src/month.vo.ts CHANGED
@@ -1,17 +1,17 @@
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 { Timestamp, type TimestampType } from "./timestamp.vo";
4
+ import { Timestamp } from "./timestamp.vo";
5
5
 
6
6
  export class Month extends DateRange {
7
- static fromTimestamp(timestamp: TimestampType): Month {
8
- const start = Timestamp.parse(startOfMonth(timestamp).getTime());
9
- const end = Timestamp.parse(endOfMonth(timestamp).getTime());
7
+ static fromTimestamp(timestamp: Timestamp): Month {
8
+ const start = Timestamp.fromNumber(startOfMonth(timestamp.get()).getTime());
9
+ const end = Timestamp.fromNumber(endOfMonth(timestamp.get()).getTime());
10
10
 
11
11
  return new Month(start, end);
12
12
  }
13
13
 
14
- static fromNow(now: TimestampType): Month {
14
+ static fromNow(now: Timestamp): Month {
15
15
  return Month.fromTimestamp(now);
16
16
  }
17
17
 
@@ -20,11 +20,11 @@ export class Month extends DateRange {
20
20
 
21
21
  const reference = setMonth(Date.UTC(year), month - 1).getTime();
22
22
 
23
- return Month.fromTimestamp(Timestamp.parse(reference));
23
+ return Month.fromTimestamp(Timestamp.fromNumber(reference));
24
24
  }
25
25
 
26
26
  toIsoId(): MonthIsoIdType {
27
- return MonthIsoId.parse(format(this.getStart(), "yyyy-MM"));
27
+ return MonthIsoId.parse(format(this.getStart().get(), "yyyy-MM"));
28
28
  }
29
29
 
30
30
  previous(): Month {
@@ -36,9 +36,9 @@ export class Month extends DateRange {
36
36
  }
37
37
 
38
38
  shift(count: number): Month {
39
- const shifted = setMonth(this.getStart(), getMonth(this.getStart()) + count).getTime();
39
+ const shifted = setMonth(this.getStart().get(), getMonth(this.getStart().get()) + count).getTime();
40
40
 
41
- return Month.fromTimestamp(Timestamp.parse(shifted));
41
+ return Month.fromTimestamp(Timestamp.fromNumber(shifted));
42
42
  }
43
43
 
44
44
  toString(): string {
package/src/quarter.vo.ts CHANGED
@@ -1,17 +1,17 @@
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 { Timestamp, type TimestampType } from "./timestamp.vo";
4
+ import { Timestamp } from "./timestamp.vo";
5
5
 
6
6
  export class Quarter extends DateRange {
7
- static fromTimestamp(timestamp: TimestampType): Quarter {
8
- const start = Timestamp.parse(startOfQuarter(timestamp).getTime());
9
- const end = Timestamp.parse(endOfQuarter(timestamp).getTime());
7
+ static fromTimestamp(timestamp: Timestamp): Quarter {
8
+ const start = Timestamp.fromNumber(startOfQuarter(timestamp.get()).getTime());
9
+ const end = Timestamp.fromNumber(endOfQuarter(timestamp.get()).getTime());
10
10
 
11
11
  return new Quarter(start, end);
12
12
  }
13
13
 
14
- static fromNow(now: TimestampType): Quarter {
14
+ static fromNow(now: Timestamp): Quarter {
15
15
  return Quarter.fromTimestamp(now);
16
16
  }
17
17
 
@@ -20,12 +20,12 @@ export class Quarter extends DateRange {
20
20
 
21
21
  const reference = setQuarter(Date.UTC(year), quarter).getTime();
22
22
 
23
- return Quarter.fromTimestamp(Timestamp.parse(reference));
23
+ return Quarter.fromTimestamp(Timestamp.fromNumber(reference));
24
24
  }
25
25
 
26
26
  toIsoId(): QuarterIsoIdType {
27
- const year = getYear(this.getStart());
28
- const quarter = getQuarter(this.getStart());
27
+ const year = getYear(this.getStart().get());
28
+ const quarter = getQuarter(this.getStart().get());
29
29
 
30
30
  return QuarterIsoId.parse(`${year}-Q${quarter}`);
31
31
  }
@@ -1,31 +1,31 @@
1
1
  import { Duration } from "./duration.service";
2
- import type { TimestampType } from "./timestamp.vo";
2
+ import type { Timestamp } 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: TimestampType | null = null;
9
+ private lastInvocation: Timestamp | null = null;
10
10
 
11
11
  constructor(private readonly duration: Duration) {}
12
12
 
13
- verify(now: TimestampType): RateLimiterResultType {
13
+ verify(now: Timestamp): RateLimiterResultType {
14
14
  if (this.lastInvocation == null) {
15
15
  this.lastInvocation = now;
16
16
 
17
17
  return { allowed: true };
18
18
  }
19
19
 
20
- const nextAllowedTimestamp = this.lastInvocation + this.duration.ms;
20
+ const nextAllowedTimestamp = this.lastInvocation.add(this.duration);
21
21
 
22
- if (nextAllowedTimestamp <= now) {
22
+ if (nextAllowedTimestamp.isBeforeOrEqual(now)) {
23
23
  this.lastInvocation = now;
24
24
 
25
25
  return { allowed: true };
26
26
  }
27
27
 
28
- const remainingDelta = nextAllowedTimestamp - now;
28
+ const remainingDelta = nextAllowedTimestamp.get() - now.get();
29
29
 
30
30
  return { allowed: false, remaining: Duration.Ms(remainingDelta) };
31
31
  }
@@ -1,20 +1,21 @@
1
1
  import { DateFormatters } from "./date-formatter.service";
2
- import type { TimestampType } from "./timestamp.vo";
2
+ import type { Timestamp } from "./timestamp.vo";
3
+ import type { TimestampValueType } from "./timestamp-value.vo";
3
4
  import type { Falsy } from "./ts-utils";
4
5
 
5
- type RelativeDateType = { raw: TimestampType; relative: string };
6
+ type RelativeDateType = { raw: TimestampValueType; relative: string };
6
7
 
7
8
  export class RelativeDate {
8
- static truthy(timestamp: TimestampType): RelativeDateType {
9
+ static truthy(timestamp: Timestamp): RelativeDateType {
9
10
  return RelativeDate._format(timestamp);
10
11
  }
11
12
 
12
- static falsy(timestamp: Falsy<TimestampType>): RelativeDateType | null {
13
+ static falsy(timestamp: Falsy<Timestamp>): RelativeDateType | null {
13
14
  if (!timestamp) return null;
14
15
  return RelativeDate._format(timestamp);
15
16
  }
16
17
 
17
- private static _format(timestampMs: TimestampType): RelativeDateType {
18
- return { raw: timestampMs, relative: DateFormatters.relative(timestampMs) };
18
+ private static _format(timestamp: Timestamp): RelativeDateType {
19
+ return { raw: timestamp.get(), relative: DateFormatters.relative(timestamp.get()) };
19
20
  }
20
21
  }
@@ -1,5 +1,5 @@
1
1
  import { Duration } from "./duration.service";
2
- import { Timestamp, type TimestampType } from "./timestamp.vo";
2
+ import { Timestamp } 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 startMs: TimestampType) {}
16
+ constructor(private readonly start: Timestamp) {}
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 Duration.Ms(Timestamp.parse(Date.now() - this.startMs));
23
+ return Duration.Ms(Timestamp.fromNumber(Date.now() - this.start.get()).get());
24
24
  }
25
25
  }
@@ -0,0 +1,11 @@
1
+ import { z } from "zod/v4";
2
+
3
+ export const TimestampValueError = { Invalid: "timestamp.invalid" } as const;
4
+
5
+ export const TimestampValue = z
6
+ .number(TimestampValueError.Invalid)
7
+ .int(TimestampValueError.Invalid)
8
+ .gte(0, TimestampValueError.Invalid)
9
+ .brand("TimestampValue");
10
+
11
+ export type TimestampValueType = z.infer<typeof TimestampValue>;