millis-js
Version:
A tiny and dependency-free datetime library with a chainable and immutable API
557 lines (424 loc) • 18.9 kB
Markdown
[](https://github.com/zirkelc/millis-js/actions/workflows/ci.yml)
[](https://www.npmjs.com/package/millis-js)
[](https://www.npmjs.com/package/millis-js)
# Millis.js
A tiny and dependency-free datetime library with a chainable and immutable API.
## Key Features
- 🔄 **Immutable operations** - All operations return new instances, preventing accidental state mutations
- 🔗 **Chainable API** - Fluent interface for composing multiple operations
- 📅 **UTC-based** - Works with UTC milliseconds internally, avoiding timezone complexities
- ⚡ **Zero dependencies** - Tiny footprint, built without any external dependencies
- 🎯 **Type-safe** - Written in TypeScript with full type definitions
## Install
```bash
npm install millis-js
```
## API
This library provides three classes: `DateTime`, `Duration`, and `Interval`.
### `DateTime` class
The `DateTime` class represents a millisecond timestamp.
#### Factory methods
Factory methods return a new `DateTime` instance.
- `DateTime.now(): DateTime`: Returns a new DateTime representing the current UTC time
```typescript
DateTime.now();
```
- `DateTime.from(dateTime: DateTimeLike): DateTime`: Returns a new DateTime from a date time like object
```typescript
// From milliseconds timestamp
DateTime.from(1_704_067_200_000);
// From strings
DateTime.from('2024-01-01T00:00:00.000Z'); // ISO
DateTime.from('2024-01-01'); // YYYY-MM-DD
DateTime.from('2024-001'); // YYYY-DDD
// From Date object
DateTime.from(new Date());
// From DateTime object
DateTime.from(DateTime.now());
// From calendar date components
DateTime.from({
year: 2024,
month: 1,
dayOfMonth: 1,
hour: 12, // optional
minute: 30, // optional
second: 15, // optional
millisecond: 0 // optional
});
// From ordinal date components
DateTime.from({
year: 2024,
dayOfYear: 1,
hour: 12 // optional, etc.
});
```
- `DateTime.until(dateTime: DateTimeLike): Interval`: Returns an Interval from the current time to a given date time
```typescript
// Creates interval spanning one day
DateTime.now().until(DateTime.now().plus({ days: 1 })) // Interval object
```
#### Instance methods
##### Arithmetic
DateTime accepts **both** absolute (days, hours, minutes, seconds, milliseconds) and relative (months, years) durations.
- `plus(duration: DurationLike): DateTime`: Returns a DateTime with the duration added
```ts
// Add absolute durations (days and smaller units)
DateTime.from('2024-01-01T00:00:00.000Z').plus({ days: 1, hours: 2, minutes: 30 }) // 2024-01-02T02:30:00.000Z
// Add relative durations (months/years)
DateTime.from('2024-01-31T00:00:00.000Z').plus({ months: 1 }) // 2024-02-29T00:00:00.000Z (handles leap years)
// Add Duration object
DateTime.from('2024-01-31T00:00:00.000Z').plus(Duration.hours(2)) // 2024-01-31T02:00:00.000Z
// Add milliseconds
DateTime.from('2024-01-31T00:00:00.000Z').plus(86_400_000) // 2024-02-01T00:00:00.000Z
```
- `minus(duration: DurationLike): DateTime`: Returns a DateTime with the duration subtracted
```ts
// Subtract absolute durations
DateTime.from('2024-01-01T00:00:00.000Z').minus({ days: 1, hours: 2 }) // 2023-12-30T22:00:00.000Z
// Subtract relative durations
DateTime.from('2024-03-31T00:00:00.000Z').minus({ months: 1 }) // 2024-02-29T00:00:00.000Z (handles leap years)
// Subtract Duration object
DateTime.from('2024-01-31T00:00:00.000Z').minus(Duration.hours(2)) // 2024-01-30T22:00:00.000Z
// Subtract milliseconds
DateTime.from('2024-01-31T00:00:00.000Z').minus(86_400_000) // 2023-12-31T00:00:00.000Z
```
##### Conversion
- `millis(): number`: Returns the milliseconds since Unix Epoch
```ts
DateTime.from('2024-01-01T00:00:00.000Z').millis() // 1_704_067_200_000
```
- `timestamp(): number`: Returns the seconds since Unix Epoch (floored)
```ts
DateTime.from('2024-01-01T00:00:00.500Z').timestamp() // 1_704_067_200
```
- `date(): Date`: Returns a JavaScript Date object
```ts
DateTime.from('2024-01-01T00:00:00.000Z').date() // new Date("2024-01-01T00:00:00.000Z")
```
- `year(): number`: Returns the calendar year
```ts
DateTime.from('2024-01-01T00:00:00.000Z').year() // 2024
```
- `month(): number`: Returns the calendar month (1-12)
```ts
DateTime.from('2024-01-01T00:00:00.000Z').month() // 1
```
- `dayOfMonth(): number`: Returns the day of month (1-31)
```ts
DateTime.from('2024-01-01T00:00:00.000Z').dayOfMonth() // 1
```
- `dayOfYear(): number`: Returns the day of year (1-365/366)
```ts
DateTime.from('2024-01-01T00:00:00.000Z').dayOfYear() // 1
DateTime.from('2024-12-31T00:00:00.000Z').dayOfYear() // 366 (leap year)
```
- `hour(): number`: Returns the hour of day (0-23)
```ts
DateTime.from('2024-01-01T12:00:00.000Z').hour() // 12
```
- `minute(): number`: Returns the minute of hour (0-59)
```ts
DateTime.from('2024-01-01T12:30:00.000Z').minute() // 30
```
- `second(): number`: Returns the second of minute (0-59)
```ts
DateTime.from('2024-01-01T12:30:15.000Z').second() // 15
```
##### Comparison
- `equals(dateTime: DateTimeLike): boolean`: Returns true if the current DateTime is equal to the given date time
```ts
DateTime.from('2024-01-01T12:30:15.000Z').equals(DateTime.from('2024-01-01T12:30:15.000Z')) // true
```
- `compare(dateTime: DateTimeLike): number`: Returns a comparison value of two DateTime objects to be used in sorting
```ts
[
DateTime.from('2024-01-31T00:00:00.000Z'),
DateTime.from('2024-01-02T00:00:00.000Z'),
DateTime.from('2024-01-31T00:00:00.000Z'),
].sort((a, b) => a.compare(b)) // [2024-01-01T00:00:00.000Z, 2024-01-02T00:00:00.000Z, 2024-01-31T00:00:00.000Z]
```
- `isBefore(dateTime: DateTimeLike): boolean`: Returns true if the current DateTime is before the given date time
```ts
DateTime.from('2024-01-01T12:30:15.000Z').isBefore('2024-01-01T12:30:16.000Z') // true
```
- `isAfter(dateTime: DateTimeLike): boolean`: Returns true if the current DateTime is after the given date time
```ts
DateTime.from('2024-01-01T12:30:15.000Z').isAfter('2024-01-01T12:30:14.000Z') // true
```
- `isBetween(start: DateTimeLike, end: DateTimeLike): boolean`: Returns true if the current DateTime is between the given start and end date times
```ts
DateTime.from('2024-01-01T12:30:15.000Z').isBetween('2024-01-01T12:30:14.000Z', '2024-01-01T12:30:16.000Z') // true
```
- `isSameSecond(dateTime: DateTimeLike): boolean`: Returns true if the current DateTime is in the same second as the given DateTime
```ts
DateTime.from('2024-01-01T12:30:15.123Z').isSameSecond('2024-01-01T12:30:15.456Z') // true
```
- `isSameMinute(dateTime: DateTimeLike): boolean`: Returns true if the current DateTime is in the same minute as the given DateTime
```ts
DateTime.from('2024-01-01T12:30:15.000Z').isSameMinute('2024-01-01T12:30:45.000Z') // true
```
- `isSameHour(dateTime: DateTimeLike): boolean`: Returns true if the current DateTime is in the same hour as the given DateTime
```ts
DateTime.from('2024-01-01T12:30:00.000Z').isSameHour('2024-01-01T12:45:00.000Z') // true
```
- `isSameDay(dateTime: DateTimeLike): boolean`: Returns true if the current DateTime is in the same day as the given DateTime
```ts
DateTime.from('2024-01-01T12:00:00.000Z').isSameDay('2024-01-01T23:59:59.999Z') // true
```
- `isSameMonth(dateTime: DateTimeLike): boolean`: Returns true if the current DateTime is in the same month as the given DateTime
```ts
DateTime.from('2024-01-01T00:00:00.000Z').isSameMonth('2024-01-31T23:59:59.999Z') // true
```
- `isSameYear(dateTime: DateTimeLike): boolean`: Returns true if the current DateTime is in the same year as the given DateTime
```ts
DateTime.from('2024-01-01T00:00:00.000Z').isSameYear('2024-12-31T23:59:59.999Z') // true
```
- `isStartOfYear(): boolean`: Returns true if the current DateTime is at the start of the year
```ts
DateTime.from('2024-01-01T00:00:00.000Z').isStartOfYear() // true
```
- `isStartOfMonth(): boolean`: Returns true if the current DateTime is at the start of the month
```ts
DateTime.from('2024-01-01T00:00:00.000Z').isStartOfMonth() // true
```
- `isStartOfDay(): boolean`: Returns true if the current DateTime is at the start of the day
```ts
DateTime.from('2024-03-15T00:00:00.000Z').isStartOfDay() // true
```
- `isStartOfHour(): boolean`: Returns true if the current DateTime is at the start of the hour
```ts
DateTime.from('2024-03-15T12:00:00.000Z').isStartOfHour() // true
```
- `isStartOfMinute(): boolean`: Returns true if the current DateTime is at the start of the minute
```ts
DateTime.from('2024-03-15T12:30:00.000Z').isStartOfMinute() // true
```
- `isStartOfSecond(): boolean`: Returns true if the current DateTime is at the start of the second
```ts
DateTime.from('2024-03-15T12:30:15.000Z').isStartOfSecond() // true
```
- `isEndOfYear(): boolean`: Returns true if the current DateTime is at the end of the year
```ts
DateTime.from('2024-12-31T23:59:59.999Z').isEndOfYear() // true
```
- `isEndOfMonth(): boolean`: Returns true if the current DateTime is at the end of the month
```ts
DateTime.from('2024-03-31T23:59:59.999Z').isEndOfMonth() // true
```
- `isEndOfDay(): boolean`: Returns true if the current DateTime is at the end of the day
```ts
DateTime.from('2024-03-15T23:59:59.999Z').isEndOfDay() // true
```
- `isEndOfHour(): boolean`: Returns true if the current DateTime is at the end of the hour
```ts
DateTime.from('2024-03-15T12:59:59.999Z').isEndOfHour() // true
```
- `isEndOfMinute(): boolean`: Returns true if the current DateTime is at the end of the minute
```ts
DateTime.from('2024-03-15T12:34:59.999Z').isEndOfMinute() // true
```
- `isEndOfSecond(): boolean`: Returns true if the current DateTime is at the end of the second
```ts
DateTime.from('2024-03-15T12:34:56.999Z').isEndOfSecond() // true
```
##### Formatting
- `iso(): string`: Returns the ISO string representation
```ts
DateTime.now().iso() // "2024-01-01T00:00:00.000Z"
```
- `format(format: DateTimeFormat | Intl.DateTimeFormat): string`: Format the date time using a format string
```typescript
// Built-in formats
dateTime.format('YYYY'); // "2024"
dateTime.format('YYYY-DDD'); // "2024-001"
dateTime.format('YYYY-MM-DD'); // "2024-01-01"
dateTime.format('HH:mm:ss'); // "12:30:45"
// Using Intl.DateTimeFormat
dateTime.format(new Intl.DateTimeFormat('en-US')) // "1/1/2024, 12:30:45 AM"
```
##### Manipulation
- `startOfYear(): DateTime`: Returns a new DateTime set to the start of the year
```ts
DateTime.from('2024-03-15T12:34:56.789Z').startOfYear() // 2024-01-01T00:00:00.000Z
```
- `startOfMonth(): DateTime`: Returns a new DateTime set to the start of the month
```ts
DateTime.from('2024-03-15T12:34:56.789Z').startOfMonth() // 2024-03-01T00:00:00.000Z
```
- `startOfDay(): DateTime`: Returns a new DateTime set to the start of the day
```ts
DateTime.from('2024-03-15T12:34:56.789Z').startOfDay() // 2024-03-15T00:00:00.000Z
```
- `startOfHour(): DateTime`: Returns a new DateTime set to the start of the hour
```ts
DateTime.from('2024-03-15T12:34:56.789Z').startOfHour() // 2024-03-15T12:00:00.000Z
```
- `startOfMinute(): DateTime`: Returns a new DateTime set to the start of the minute
```ts
DateTime.from('2024-03-15T12:34:56.789Z').startOfMinute() // 2024-03-15T12:34:00.000Z
```
- `startOfSecond(): DateTime`: Returns a new DateTime set to the start of the second
```ts
DateTime.from('2024-03-15T12:34:56.789Z').startOfSecond() // 2024-03-15T12:34:56.000Z
```
- `endOfYear(): DateTime`: Returns a new DateTime set to the end of the year
```ts
DateTime.from('2024-03-15T12:34:56.789Z').endOfYear() // 2024-12-31T23:59:59.999Z
```
- `endOfMonth(): DateTime`: Returns a new DateTime set to the end of the month
```ts
DateTime.from('2024-03-15T12:34:56.789Z').endOfMonth() // 2024-03-31T23:59:59.999Z
```
- `endOfDay(): DateTime`: Returns a new DateTime set to the end of the day
```ts
DateTime.from('2024-03-15T12:34:56.789Z').endOfDay() // 2024-03-15T23:59:59.999Z
```
- `endOfHour(): DateTime`: Returns a new DateTime set to the end of the hour
```ts
DateTime.from('2024-03-15T12:34:56.789Z').endOfHour() // 2024-03-15T12:59:59.999Z
```
- `endOfMinute(): DateTime`: Returns a new DateTime set to the end of the minute
```ts
DateTime.from('2024-03-15T12:34:56.789Z').endOfMinute() // 2024-03-15T12:34:59.999Z
```
- `endOfSecond(): DateTime`: Returns a new DateTime set to the end of the second
```ts
DateTime.from('2024-03-15T12:34:56.789Z').endOfSecond() // 2024-03-15T12:34:56.999Z
```
### `Duration` class
The `Duration` class represents a length of time in milliseconds.
#### Factory methods
Factory methods return a new `Duration` instance.
- `Duration.from(duration: DurationLike): Duration`: Returns a Duration from a duration like object
```typescript
// From components
Duration.from({
days: 1,
hours: 2,
minutes: 30
}) // P1DT2H30M
// From milliseconds
Duration.from(93_600_000) // P1DT2H
```
- `Duration.between(start: DateTimeLike, end: DateTimeLike): Duration`: Returns a Duration from a start and end date time
```typescript
// From ISO strings
Duration.between('2024-01-01T00:00:00.000Z', '2024-01-02T00:00:00.000Z')
// From DateTime objects
Duration.between(DateTime.now(), DateTime.now().plus({ days: 1 }))
// From milliseconds
Duration.between(1704067200000, 1704067200000 + 24 * 60 * 60 * 1000)
// From Date objects
Duration.between(new Date(), new Date().setDate(new Date().getDate() + 1))
// From datetime components
Duration.between({ year: 2024, month: 1, dayOfMonth: 1 }, { year: 2025, month: 1, dayOfMonth: 1 })
// From formatted strings
Duration.between({ 'YYYY': '2024' }, { 'YYYY': '2025' })
```
#### Instance methods
##### Arithmetic
Duration accepts **only** absolute durations (days, hours, minutes, seconds, milliseconds).
- `plus(duration: AbsoluteDuration): Duration`: Returns a Duration by adding a duration
```ts
Duration.hours(2).plus({ minutes: 30 }) // 2.5 hours
```
- `minus(duration: AbsoluteDuration): Duration`: Returns a Duration by subtracting a duration
```ts
Duration.hours(5).minus({ hours: 2, minutes: 30 }) // 2.5 hours
```
- `abs(): Duration`: Returns a Duration with the absolute value of the current duration
```ts
Duration.hours(-2).abs() // 2 hours
```
##### Conversion
- `days(): number`: Returns the duration in days
```ts
// Precise value
Duration.hours(25).days() // 1.0416666666666667
// Rounded value
Duration.hours(25).days({ round: true }) // 1
```
- `hours(): number`: Returns the duration in hours
```ts
Duration.minutes(150).hours() // 2.5
Duration.minutes(150).hours({ round: true }) // 3
```
- `minutes(): number`: Returns the duration in minutes
```ts
Duration.seconds(150).minutes() // 2.5
Duration.seconds(150).minutes({ round: true }) // 3
```
- `seconds(): number`: Returns the duration in seconds
```ts
Duration.millis(2500).seconds() // 2.5
Duration.millis(2500).seconds({ round: true }) // 3
```
- `millis(): number`: Returns the duration in milliseconds
```ts
Duration.seconds(1.5).millis() // 1500
```
- `iso(): string`: Returns the ISO duration string
```ts
Duration.from({ days: 1, hours: 2, minutes: 30 }).iso() // "P1DT2H30M"
```
### `Interval` class
The `Interval` class represents a time span between two `DateTime` instances.
#### Factory methods
Factory methods return a new `Interval` instance.
- `Interval.between(start: DateTimeLike, end: DateTimeLike): Interval`: Returns an Interval between two date times
```ts
Interval.between('2024-01-01T00:00:00.000Z','2024-01-02T00:00:00.000Z') // 2024-01-01T00:00:00.000Z/2024-01-02T00:00:00.000Z
```
- `Interval.days(days: number): Interval`: Returns an Interval spanning the specified number of days from now
```ts
// Creates interval from now to 7 days in future
Interval.days(7) // 2024-01-01T00:00:00.000Z/2024-01-08T00:00:00.000Z
// Creates interval from now to 7 days in past
Interval.days(-7) // 2024-01-01T00:00:00.000Z/2023-12-25T00:00:00.000Z
```
#### Instance methods
##### Conversion
- `duration(): Duration`: Returns a Duration representing the length of the interval
```ts
Interval.between('2024-01-01T00:00:00.000Z', '2024-01-02T23:59:59.999Z').duration() // 2 days
```
- `starts(): DateTime`: Returns a DateTime representing the start of the interval
```ts
Interval.between('2024-01-01T00:00:00.000Z', '2024-01-02T23:59:59.999Z').starts() // 2024-01-01T00:00:00.000Z
```
- `ends(): DateTime`: Returns a DateTime representing the end of the interval
```ts
Interval.between('2024-01-01T00:00:00.000Z', '2024-01-02T23:59:59.999Z').ends() // 2024-01-02T23:59:59.999Z
```
- `reverse(): Interval`: Returns a new Interval with the start and end dates reversed
```ts
Interval.between('2024-01-01T00:00:00.000Z', '2024-01-02T23:59:59.999Z').reverse() // 2024-01-02T23:59:59.999Z/2024-01-01T00:00:00.000Z
```
- `days(): Array<DateTime>`: Returns an array of DateTimes for each day in the interval
```ts
Interval.between('2024-01-01T00:00:00.000Z', '2024-01-02T23:59:59.999Z').days() // [2024-01-01T00:00:00.000Z, 2024-01-02T23:59:59.999Z]
```
- `years(): Array<DateTime>`: Returns an array of DateTimes for each year in the interval
```ts
Interval.between('2024-01-01T00:00:00.000Z', '2025-01-01T23:59:59.999Z').years() // [2024-01-01T00:00:00.000Z, 2025-01-01T23:59:59.999Z]
```
##### Comparison
- `contains(dateTime: DateTimeLike): boolean`: Returns true if the interval contains the given date time
```ts
Interval.between('2024-01-01T00:00:00.000Z', '2024-01-02T00:00:00.000Z').contains('2024-01-01T12:00:00.000Z') // true
```
- `isPositive(): boolean`: Returns true if the interval is going forward in time (start < end)
```ts
Interval.between('2024-01-01T00:00:00.000Z', '2024-01-02T00:00:00.000Z').isPositive() // true
```
- `isNegative(): boolean`: Returns true if the interval is going backward in time (start > end)
```ts
Interval.between('2024-01-02T00:00:00.000Z', '2024-01-01T00:00:00.000Z').isNegative() // true
```
##### Formatting
- `iso(): string`: Returns the ISO interval string representation
```ts
Interval.between('2024-01-01T00:00:00.000Z', '2024-01-02T00:00:00.000Z').iso() // "2024-01-01T00:00:00.000Z/2024-01-02T00:00:00.000Z"
```
## License
MIT