EdgeAIG's picture
download
raw
40.9 kB
/**
* Represents immutable spans of time.
*
* A `Duration` can be finite, positive infinity, or negative infinity. It is
* the standard representation for delays, timeouts, intervals, and
* time-to-live values across Effect APIs. This module includes constructors
* from common input shapes, unit conversions, comparisons, arithmetic,
* formatting, and reusable reducer or combiner helpers.
*
* @since 2.0.0
*/
import * as Combiner from "./Combiner.js";
import * as Equal from "./Equal.js";
import { dual, identity } from "./Function.js";
import * as Hash from "./Hash.js";
import { NodeInspectSymbol } from "./Inspectable.js";
import * as Option from "./Option.js";
import * as order from "./Order.js";
import { pipeArguments } from "./Pipeable.js";
import { hasProperty, isNumber } from "./Predicate.js";
import * as Reducer from "./Reducer.js";
const TypeId = "~effect/time/Duration";
const bigint0 = /*#__PURE__*/BigInt(0);
const bigint1 = /*#__PURE__*/BigInt(1);
const bigint24 = /*#__PURE__*/BigInt(24);
const bigint60 = /*#__PURE__*/BigInt(60);
const bigint1e3 = /*#__PURE__*/BigInt(1_000);
const bigint1e6 = /*#__PURE__*/BigInt(1_000_000);
const bigint1e9 = /*#__PURE__*/BigInt(1_000_000_000);
const roundTiesAwayFromZero = input => BigInt(input < 0 ? Math.ceil(input - 0.5) : Math.floor(input + 0.5));
const roundMillisToNanos = millis => roundTiesAwayFromZero(millis * 1_000_000);
const parseNanos = (input, scale) => input.includes(".") ? roundTiesAwayFromZero(Number(input) * Number(scale)) : BigInt(input) * scale;
const nanosToHrTime = nanos => {
const sign = nanos < bigint0 ? -bigint1 : bigint1;
const absolute = nanos < bigint0 ? -nanos : nanos;
return [Number(sign * (absolute / bigint1e9)), Number(sign * (absolute % bigint1e9))];
};
const DURATION_REGEXP = /^(-?\d+(?:\.\d+)?)\s+(nanos?|micros?|millis?|seconds?|minutes?|hours?|days?|weeks?)$/;
/**
* Decodes a `Duration.Input` into a `Duration`.
*
* **When to use**
*
* Use when the input has already been validated or comes from a trusted source
* and throwing is acceptable for invalid duration syntax.
*
* **Gotchas**
*
* If the input is not a valid `Duration.Input`, it throws an error.
*
* **Example** (Decoding duration inputs)
*
* ```ts
* import { Duration } from "effect"
*
* const duration1 = Duration.fromInputUnsafe(1000) // 1000 milliseconds
* const duration2 = Duration.fromInputUnsafe("5 seconds")
* const duration3 = Duration.fromInputUnsafe("Infinity")
* const duration4 = Duration.fromInputUnsafe([2, 500_000_000]) // 2 seconds and 500ms
* ```
*
* @category constructors
* @since 4.0.0
*/
export const fromInputUnsafe = input => {
switch (typeof input) {
case "number":
return millis(input);
case "bigint":
return nanos(input);
case "string":
{
if (input === "Infinity") {
return infinity;
}
if (input === "-Infinity") {
return negativeInfinity;
}
const match = DURATION_REGEXP.exec(input);
if (!match) break;
const [_, valueStr, unit] = match;
if (unit === "nano" || unit === "nanos") {
return nanos(parseNanos(valueStr, bigint1));
}
if (unit === "micro" || unit === "micros") {
return nanos(parseNanos(valueStr, bigint1e3));
}
const value = Number(valueStr);
switch (unit) {
case "milli":
case "millis":
return millis(value);
case "second":
case "seconds":
return seconds(value);
case "minute":
case "minutes":
return minutes(value);
case "hour":
case "hours":
return hours(value);
case "day":
case "days":
return days(value);
case "week":
case "weeks":
return weeks(value);
}
break;
}
case "object":
{
if (input === null) break;
if (TypeId in input) return input;
if (Array.isArray(input)) {
if (input.length !== 2 || !input.every(isNumber)) {
return invalid(input);
}
if (Number.isNaN(input[0]) || Number.isNaN(input[1])) {
return zero;
}
if (input[0] === -Infinity || input[1] === -Infinity) {
return negativeInfinity;
}
if (input[0] === Infinity || input[1] === Infinity) {
return infinity;
}
return make(roundTiesAwayFromZero(input[0] * 1_000_000_000 + input[1]));
}
const obj = input;
let millis = 0;
// we can use truthy checks here, because 0 can be ignored
if (obj.weeks) millis += obj.weeks * 604_800_000;
if (obj.days) millis += obj.days * 86_400_000;
if (obj.hours) millis += obj.hours * 3_600_000;
if (obj.minutes) millis += obj.minutes * 60_000;
if (obj.seconds) millis += obj.seconds * 1_000;
if (obj.milliseconds) millis += obj.milliseconds;
if (!obj.microseconds && !obj.nanoseconds) return make(millis);
return make(roundTiesAwayFromZero(millis * 1_000_000 + (obj.microseconds ?? 0) * 1_000 + (obj.nanoseconds ?? 0)));
}
}
return invalid(input);
};
const invalid = input => {
throw new Error(`Invalid Input: ${input}`);
};
/**
* Decodes a `Input` value into a `Duration` safely, returning
* `Option.none()` if decoding fails.
*
* **Example** (Safely decoding duration inputs)
*
* ```ts
* import { Duration, Option } from "effect"
*
* Duration.fromInput(1000).pipe(Option.map(Duration.toSeconds)) // Some(1)
*
* Duration.fromInput("invalid" as any) // None
* ```
*
* @category constructors
* @since 4.0.0
*/
export const fromInput = /*#__PURE__*/Option.liftThrowable(fromInputUnsafe);
const zeroDurationValue = {
_tag: "Millis",
millis: 0
};
const infinityDurationValue = {
_tag: "Infinity"
};
const negativeInfinityDurationValue = {
_tag: "NegativeInfinity"
};
const DurationProto = {
[TypeId]: TypeId,
[Hash.symbol]() {
return Hash.structure(this.value);
},
[Equal.symbol](that) {
return isDuration(that) && equals(this, that);
},
toString() {
switch (this.value._tag) {
case "Infinity":
return "Infinity";
case "NegativeInfinity":
return "-Infinity";
case "Nanos":
return `${this.value.nanos} nanos`;
case "Millis":
return `${this.value.millis} millis`;
}
},
toJSON() {
switch (this.value._tag) {
case "Millis":
return {
_id: "Duration",
_tag: "Millis",
millis: this.value.millis
};
case "Nanos":
return {
_id: "Duration",
_tag: "Nanos",
nanos: String(this.value.nanos)
};
case "Infinity":
return {
_id: "Duration",
_tag: "Infinity"
};
case "NegativeInfinity":
return {
_id: "Duration",
_tag: "NegativeInfinity"
};
}
},
[NodeInspectSymbol]() {
return this.toJSON();
},
pipe() {
return pipeArguments(this, arguments);
}
};
const make = input => {
const duration = Object.create(DurationProto);
if (typeof input === "number") {
if (isNaN(input) || input === 0 || Object.is(input, -0)) {
duration.value = zeroDurationValue;
} else if (!Number.isFinite(input)) {
duration.value = input > 0 ? infinityDurationValue : negativeInfinityDurationValue;
} else if (!Number.isInteger(input)) {
duration.value = {
_tag: "Nanos",
nanos: roundMillisToNanos(input)
};
} else {
duration.value = {
_tag: "Millis",
millis: input
};
}
} else if (input === bigint0) {
duration.value = zeroDurationValue;
} else {
duration.value = {
_tag: "Nanos",
nanos: input
};
}
return duration;
};
/**
* Checks whether a value is a Duration.
*
* **Example** (Checking for durations)
*
* ```ts
* import { Duration } from "effect"
*
* console.log(Duration.isDuration(Duration.seconds(1))) // true
* console.log(Duration.isDuration(1000)) // false
* ```
*
* @category guards
* @since 2.0.0
*/
export const isDuration = u => hasProperty(u, TypeId);
/**
* Checks whether a Duration is finite (not infinite).
*
* **Example** (Checking finite durations)
*
* ```ts
* import { Duration } from "effect"
*
* console.log(Duration.isFinite(Duration.seconds(5))) // true
* console.log(Duration.isFinite(Duration.infinity)) // false
* ```
*
* @category guards
* @since 2.0.0
*/
export const isFinite = self => self.value._tag !== "Infinity" && self.value._tag !== "NegativeInfinity";
/**
* Checks whether a Duration is zero.
*
* **Example** (Checking for zero durations)
*
* ```ts
* import { Duration } from "effect"
*
* console.log(Duration.isZero(Duration.zero)) // true
* console.log(Duration.isZero(Duration.seconds(1))) // false
* ```
*
* @category guards
* @since 3.5.0
*/
export const isZero = self => {
switch (self.value._tag) {
case "Millis":
return self.value.millis === 0;
case "Nanos":
return self.value.nanos === bigint0;
case "Infinity":
case "NegativeInfinity":
return false;
}
};
/**
* Returns `true` if the duration is negative (strictly less than zero).
*
* **Example** (Checking for negative durations)
*
* ```ts
* import { Duration } from "effect"
*
* console.log(Duration.isNegative(Duration.seconds(-5))) // true
* console.log(Duration.isNegative(Duration.zero)) // false
* console.log(Duration.isNegative(Duration.negativeInfinity)) // true
* ```
*
* @category guards
* @since 4.0.0
*/
export const isNegative = self => {
switch (self.value._tag) {
case "Millis":
return self.value.millis < 0;
case "Nanos":
return self.value.nanos < bigint0;
case "NegativeInfinity":
return true;
case "Infinity":
return false;
}
};
/**
* Returns `true` if the duration is positive (strictly greater than zero).
*
* **Example** (Checking for positive durations)
*
* ```ts
* import { Duration } from "effect"
*
* console.log(Duration.isPositive(Duration.seconds(5))) // true
* console.log(Duration.isPositive(Duration.zero)) // false
* console.log(Duration.isPositive(Duration.infinity)) // true
* ```
*
* @category guards
* @since 4.0.0
*/
export const isPositive = self => {
switch (self.value._tag) {
case "Millis":
return self.value.millis > 0;
case "Nanos":
return self.value.nanos > bigint0;
case "Infinity":
return true;
case "NegativeInfinity":
return false;
}
};
/**
* Returns the absolute value of the duration.
*
* **Example** (Taking absolute duration values)
*
* ```ts
* import { Duration } from "effect"
*
* Duration.toMillis(Duration.abs(Duration.seconds(-5))) // 5000
* Duration.abs(Duration.negativeInfinity) === Duration.infinity // true
* ```
*
* @category math
* @since 4.0.0
*/
export const abs = self => {
switch (self.value._tag) {
case "Infinity":
case "NegativeInfinity":
return infinity;
case "Millis":
return self.value.millis < 0 ? make(-self.value.millis) : self;
case "Nanos":
return self.value.nanos < bigint0 ? make(-self.value.nanos) : self;
}
};
/**
* Returns the negated duration.
*
* **Example** (Negating durations)
*
* ```ts
* import { Duration } from "effect"
*
* Duration.toMillis(Duration.negate(Duration.seconds(5))) // -5000
* Duration.negate(Duration.infinity) === Duration.negativeInfinity // true
* ```
*
* @category math
* @since 4.0.0
*/
export const negate = self => {
switch (self.value._tag) {
case "Infinity":
return negativeInfinity;
case "NegativeInfinity":
return infinity;
case "Millis":
return self.value.millis === 0 ? self : make(-self.value.millis);
case "Nanos":
return self.value.nanos === bigint0 ? self : make(-self.value.nanos);
}
};
/**
* A Duration representing zero time.
*
* **Example** (Using the zero duration)
*
* ```ts
* import { Duration } from "effect"
*
* console.log(Duration.toMillis(Duration.zero)) // 0
* ```
*
* @category constructors
* @since 2.0.0
*/
export const zero = /*#__PURE__*/make(0);
/**
* A Duration representing infinite time.
*
* **Example** (Using infinite duration)
*
* ```ts
* import { Duration } from "effect"
*
* console.log(Duration.toMillis(Duration.infinity)) // Infinity
* ```
*
* @category constructors
* @since 2.0.0
*/
export const infinity = /*#__PURE__*/make(Infinity);
/**
* A Duration representing negative infinite time.
*
* **Example** (Using negative infinite duration)
*
* ```ts
* import { Duration } from "effect"
*
* console.log(Duration.toMillis(Duration.negativeInfinity)) // -Infinity
* ```
*
* @category constructors
* @since 4.0.0
*/
export const negativeInfinity = /*#__PURE__*/make(-Infinity);
/**
* Creates a Duration from nanoseconds.
*
* **Example** (Creating durations from nanoseconds)
*
* ```ts
* import { Duration } from "effect"
*
* const duration = Duration.nanos(BigInt(500_000_000))
* console.log(Duration.toMillis(duration)) // 500
* ```
*
* @category constructors
* @since 2.0.0
*/
export const nanos = nanos => make(nanos);
/**
* Creates a Duration from microseconds.
*
* **Example** (Creating durations from microseconds)
*
* ```ts
* import { Duration } from "effect"
*
* const duration = Duration.micros(BigInt(500_000))
* console.log(Duration.toMillis(duration)) // 500
* ```
*
* @category constructors
* @since 2.0.0
*/
export const micros = micros => make(micros * bigint1e3);
/**
* Creates a Duration from milliseconds.
*
* **Example** (Creating durations from milliseconds)
*
* ```ts
* import { Duration } from "effect"
*
* const duration = Duration.millis(1000)
* console.log(Duration.toMillis(duration)) // 1000
* ```
*
* @category constructors
* @since 2.0.0
*/
export const millis = millis => make(millis);
/**
* Creates a Duration from seconds.
*
* **Example** (Creating durations from seconds)
*
* ```ts
* import { Duration } from "effect"
*
* const duration = Duration.seconds(30)
* console.log(Duration.toMillis(duration)) // 30000
* ```
*
* @category constructors
* @since 2.0.0
*/
export const seconds = seconds => make(seconds * 1000);
/**
* Creates a Duration from minutes.
*
* **Example** (Creating durations from minutes)
*
* ```ts
* import { Duration } from "effect"
*
* const duration = Duration.minutes(5)
* console.log(Duration.toMillis(duration)) // 300000
* ```
*
* @category constructors
* @since 2.0.0
*/
export const minutes = minutes => make(minutes * 60_000);
/**
* Creates a Duration from hours.
*
* **Example** (Creating durations from hours)
*
* ```ts
* import { Duration } from "effect"
*
* const duration = Duration.hours(2)
* console.log(Duration.toMillis(duration)) // 7200000
* ```
*
* @category constructors
* @since 2.0.0
*/
export const hours = hours => make(hours * 3_600_000);
/**
* Creates a Duration from days.
*
* **Example** (Creating durations from days)
*
* ```ts
* import { Duration } from "effect"
*
* const duration = Duration.days(1)
* console.log(Duration.toMillis(duration)) // 86400000
* ```
*
* @category constructors
* @since 2.0.0
*/
export const days = days => make(days * 86_400_000);
/**
* Creates a Duration from weeks.
*
* **Example** (Creating durations from weeks)
*
* ```ts
* import { Duration } from "effect"
*
* const duration = Duration.weeks(1)
* console.log(Duration.toMillis(duration)) // 604800000
* ```
*
* @category constructors
* @since 2.0.0
*/
export const weeks = weeks => make(weeks * 604_800_000);
/**
* Converts a Duration to milliseconds.
*
* **Example** (Converting durations to milliseconds)
*
* ```ts
* import { Duration } from "effect"
*
* console.log(Duration.toMillis(Duration.seconds(5))) // 5000
* console.log(Duration.toMillis(Duration.minutes(2))) // 120000
* ```
*
* @category getters
* @since 2.0.0
*/
export const toMillis = self => match(fromInputUnsafe(self), {
onMillis: identity,
onNanos: nanos => Number(nanos) / 1_000_000,
onInfinity: () => Infinity,
onNegativeInfinity: () => -Infinity
});
/**
* Converts a Duration to seconds.
*
* **Example** (Converting durations to seconds)
*
* ```ts
* import { Duration } from "effect"
*
* console.log(Duration.toSeconds(Duration.millis(5000))) // 5
* console.log(Duration.toSeconds(Duration.minutes(2))) // 120
* ```
*
* @category getters
* @since 2.0.0
*/
export const toSeconds = self => match(fromInputUnsafe(self), {
onMillis: millis => millis / 1_000,
onNanos: nanos => Number(nanos) / 1_000_000_000,
onInfinity: () => Infinity,
onNegativeInfinity: () => -Infinity
});
/**
* Converts a Duration to minutes.
*
* **Example** (Converting durations to minutes)
*
* ```ts
* import { Duration } from "effect"
*
* console.log(Duration.toMinutes(Duration.seconds(120))) // 2
* console.log(Duration.toMinutes(Duration.hours(1))) // 60
* ```
*
* @category getters
* @since 3.8.0
*/
export const toMinutes = self => match(fromInputUnsafe(self), {
onMillis: millis => millis / 60_000,
onNanos: nanos => Number(nanos) / 60_000_000_000,
onInfinity: () => Infinity,
onNegativeInfinity: () => -Infinity
});
/**
* Converts a Duration to hours.
*
* **Example** (Converting durations to hours)
*
* ```ts
* import { Duration } from "effect"
*
* console.log(Duration.toHours(Duration.minutes(120))) // 2
* console.log(Duration.toHours(Duration.days(1))) // 24
* ```
*
* @category getters
* @since 3.8.0
*/
export const toHours = self => match(fromInputUnsafe(self), {
onMillis: millis => millis / 3_600_000,
onNanos: nanos => Number(nanos) / 3_600_000_000_000,
onInfinity: () => Infinity,
onNegativeInfinity: () => -Infinity
});
/**
* Converts a Duration to days.
*
* **Example** (Converting durations to days)
*
* ```ts
* import { Duration } from "effect"
*
* console.log(Duration.toDays(Duration.hours(48))) // 2
* console.log(Duration.toDays(Duration.weeks(1))) // 7
* ```
*
* @category getters
* @since 3.8.0
*/
export const toDays = self => match(fromInputUnsafe(self), {
onMillis: millis => millis / 86_400_000,
onNanos: nanos => Number(nanos) / 86_400_000_000_000,
onInfinity: () => Infinity,
onNegativeInfinity: () => -Infinity
});
/**
* Converts a Duration to weeks.
*
* **Example** (Converting durations to weeks)
*
* ```ts
* import { Duration } from "effect"
*
* console.log(Duration.toWeeks(Duration.days(14))) // 2
* console.log(Duration.toWeeks(Duration.days(7))) // 1
* ```
*
* @category getters
* @since 3.8.0
*/
export const toWeeks = self => match(fromInputUnsafe(self), {
onMillis: millis => millis / 604_800_000,
onNanos: nanos => Number(nanos) / 604_800_000_000_000,
onInfinity: () => Infinity,
onNegativeInfinity: () => -Infinity
});
/**
* Gets the duration in nanoseconds as a bigint.
*
* **When to use**
*
* Use when the duration is known to be finite and you need the nanosecond value
* as a `bigint`.
*
* **Details**
*
* Millisecond-backed fractional durations are rounded to the nearest
* nanosecond, with ties away from zero.
*
* **Gotchas**
*
* If the duration is infinite, it throws an error.
*
* **Example** (Reading nanoseconds unsafely)
*
* ```ts
* import { Duration } from "effect"
*
* const duration = Duration.seconds(2)
* const nanos = Duration.toNanosUnsafe(duration)
* console.log(nanos) // 2000000000n
*
* // Duration.toNanosUnsafe(Duration.infinity)
* // throws Error: "Cannot convert infinite duration to nanos"
* ```
*
* @category getters
* @since 4.0.0
*/
export const toNanosUnsafe = input => {
const self = fromInputUnsafe(input);
switch (self.value._tag) {
case "Infinity":
case "NegativeInfinity":
throw new Error("Cannot convert infinite duration to nanos");
case "Nanos":
return self.value.nanos;
case "Millis":
return roundMillisToNanos(self.value.millis);
}
};
/**
* Gets the duration in nanoseconds safely as an `Option<bigint>`.
*
* **Details**
*
* If the duration is infinite, returns `Option.none()`.
*
* **Example** (Safely reading nanoseconds)
*
* ```ts
* import { Duration, Option } from "effect"
*
* Duration.toNanos(Duration.seconds(1)) // Some(1000000000n)
*
* Duration.toNanos(Duration.infinity) // None
* Option.getOrUndefined(Duration.toNanos(Duration.infinity)) // undefined
* ```
*
* @category getters
* @since 2.0.0
*/
export const toNanos = /*#__PURE__*/Option.liftThrowable(toNanosUnsafe);
/**
* Converts a Duration to high-resolution time format [seconds, nanoseconds].
*
* **Example** (Converting durations to high-resolution time)
*
* ```ts
* import { Duration } from "effect"
*
* const duration = Duration.millis(1500)
* const hrtime = Duration.toHrTime(duration)
* console.log(hrtime) // [1, 500000000]
* ```
*
* @category getters
* @since 2.0.0
*/
export const toHrTime = input => {
const self = fromInputUnsafe(input);
switch (self.value._tag) {
case "Infinity":
return [Infinity, 0];
case "NegativeInfinity":
return [-Infinity, 0];
case "Nanos":
return nanosToHrTime(self.value.nanos);
case "Millis":
return nanosToHrTime(roundMillisToNanos(self.value.millis));
}
};
/**
* Pattern matches on the representation of a `Duration`.
*
* **Details**
*
* Provide handlers for millisecond-backed values, nanosecond-backed values,
* and positive infinity. Use `onNegativeInfinity` to handle negative infinity
* separately; otherwise negative infinity is handled by `onInfinity`.
*
* **Example** (Pattern matching on duration representations)
*
* ```ts
* import { Duration } from "effect"
*
* const result = Duration.match(Duration.seconds(5), {
* onMillis: (millis) => `${millis} milliseconds`,
* onNanos: (nanos) => `${nanos} nanoseconds`,
* onInfinity: () => "infinite"
* })
* console.log(result) // "5000 milliseconds"
* ```
*
* @category pattern matching
* @since 2.0.0
*/
export const match = /*#__PURE__*/dual(2, (self, options) => {
switch (self.value._tag) {
case "Millis":
return options.onMillis(self.value.millis);
case "Nanos":
return options.onNanos(self.value.nanos);
case "Infinity":
return options.onInfinity();
case "NegativeInfinity":
return (options.onNegativeInfinity ?? options.onInfinity)();
}
});
/**
* Pattern matches on two `Duration`s, providing handlers that receive both values.
*
* **Example** (Pattern matching on duration pairs)
*
* ```ts
* import { Duration } from "effect"
*
* const sum = Duration.matchPair(Duration.seconds(3), Duration.seconds(2), {
* onMillis: (a, b) => a + b,
* onNanos: (a, b) => Number(a + b),
* onInfinity: () => Infinity
* })
* console.log(sum) // 5000
* ```
*
* @category pattern matching
* @since 4.0.0
*/
export const matchPair = /*#__PURE__*/dual(3, (self, that, options) => {
if (self.value._tag === "Infinity" || self.value._tag === "NegativeInfinity" || that.value._tag === "Infinity" || that.value._tag === "NegativeInfinity") return options.onInfinity(self, that);
if (self.value._tag === "Millis") {
return that.value._tag === "Millis" ? options.onMillis(self.value.millis, that.value.millis) : options.onNanos(toNanosUnsafe(self), that.value.nanos);
} else {
return options.onNanos(self.value.nanos, toNanosUnsafe(that));
}
});
/**
* Provides an `Order` instance for comparing `Duration` values.
*
* **Details**
*
* `NegativeInfinity` < any finite value < `Infinity`.
*
* **Example** (Sorting durations)
*
* ```ts
* import { Duration } from "effect"
*
* const durations = [
* Duration.seconds(3),
* Duration.seconds(1),
* Duration.seconds(2)
* ]
* const sorted = durations.sort((a, b) => Duration.Order(a, b))
* console.log(sorted.map(Duration.toSeconds)) // [1, 2, 3]
* ```
*
* @category instances
* @since 2.0.0
*/
export const Order = /*#__PURE__*/order.make((self, that) => matchPair(self, that, {
onMillis: (self, that) => self < that ? -1 : self > that ? 1 : 0,
onNanos: (self, that) => self < that ? -1 : self > that ? 1 : 0,
onInfinity: (self, that) => {
if (self.value._tag === that.value._tag) return 0;
if (self.value._tag === "Infinity") return 1;
if (self.value._tag === "NegativeInfinity") return -1;
// self is finite
if (that.value._tag === "Infinity") return -1;
// that is NegativeInfinity
return 1;
}
}));
/**
* Returns `true` if a `Duration` is greater than or equal to `minimum` and
* less than or equal to `maximum`, according to `Duration.Order`.
*
* **When to use**
*
* Use to test whether a duration is inside an inclusive range.
*
* **Details**
*
* Both bounds are inclusive and compared with `Duration.Order`.
*
* **Gotchas**
*
* The bounds are not normalized. If `minimum` is greater than `maximum`, the
* predicate returns `false` for every duration.
*
* **Example** (Checking duration ranges)
*
* ```ts
* import { Duration } from "effect"
*
* const isInRange = Duration.between(Duration.seconds(3), {
* minimum: Duration.seconds(2),
* maximum: Duration.seconds(5)
* })
* console.log(isInRange) // true
* ```
*
* @see {@link clamp} for constraining a duration to a range
* @see {@link isGreaterThanOrEqualTo} for checking only the lower bound
* @see {@link isLessThanOrEqualTo} for checking only the upper bound
*
* @category predicates
* @since 2.0.0
*/
export const between = /*#__PURE__*/order.isBetween(Order);
/**
* Provides an `Equivalence` instance for comparing `Duration` values.
*
* **Example** (Comparing durations for equivalence)
*
* ```ts
* import { Duration } from "effect"
*
* const isEqual = Duration.Equivalence(Duration.seconds(5), Duration.millis(5000))
* console.log(isEqual) // true
* ```
*
* @category instances
* @since 2.0.0
*/
export const Equivalence = (self, that) => matchPair(self, that, {
onMillis: (self, that) => self === that,
onNanos: (self, that) => self === that,
onInfinity: (self, that) => self.value._tag === that.value._tag
});
/**
* Returns the smaller of two Durations.
*
* **Example** (Selecting the shorter duration)
*
* ```ts
* import { Duration } from "effect"
*
* const shorter = Duration.min(Duration.seconds(5), Duration.seconds(3))
* console.log(Duration.toSeconds(shorter)) // 3
* ```
*
* @category ordering
* @since 2.0.0
*/
export const min = /*#__PURE__*/order.min(Order);
/**
* Returns the larger of two Durations.
*
* **Example** (Selecting the longer duration)
*
* ```ts
* import { Duration } from "effect"
*
* const longer = Duration.max(Duration.seconds(5), Duration.seconds(3))
* console.log(Duration.toSeconds(longer)) // 5
* ```
*
* @category ordering
* @since 2.0.0
*/
export const max = /*#__PURE__*/order.max(Order);
/**
* Returns a `Duration` constrained between a minimum and maximum value.
*
* **Example** (Clamping durations to a range)
*
* ```ts
* import { Duration } from "effect"
*
* const clamped = Duration.clamp(Duration.seconds(10), {
* minimum: Duration.seconds(2),
* maximum: Duration.seconds(5)
* })
* console.log(Duration.toSeconds(clamped)) // 5
* ```
*
* @category ordering
* @since 2.0.0
*/
export const clamp = /*#__PURE__*/order.clamp(Order);
/**
* Divides a `Duration` by a finite, non-zero number safely.
*
* **Details**
*
* Returns `Option.none()` for zero, negative zero, or non-finite divisors. For
* nanosecond-backed durations, also returns `Option.none()` when the divisor
* cannot be converted to a `bigint`, such as a fractional divisor.
*
* **Example** (Safely dividing durations)
*
* ```ts
* import { Duration, Option } from "effect"
*
* const d = Duration.divide(Duration.seconds(10), 2)
* console.log(Option.map(d, Duration.toSeconds)) // Some(5)
*
* Duration.divide(Duration.seconds(10), 0) // None
* ```
*
* @category math
* @since 2.4.19
*/
export const divide = /*#__PURE__*/dual(2, (self, by) => {
if (!Number.isFinite(by)) return Option.none();
if (by === 0 || Object.is(by, -0)) return Option.none();
return match(self, {
onMillis: millis => Option.some(make(millis / by)),
onNanos: nanos => {
try {
return Option.some(make(nanos / BigInt(by)));
} catch {
return Option.none();
}
},
onInfinity: () => Option.some(by > 0 ? infinity : negativeInfinity),
onNegativeInfinity: () => Option.some(by > 0 ? negativeInfinity : infinity)
});
});
/**
* Divides a `Duration` by a number using fallback rules instead of returning
* an `Option`.
*
* **When to use**
*
* Use when dividing a `Duration` should return `Duration.zero` or signed
* infinity for invalid cases instead of forcing callers to handle `Option.none`.
*
* **Details**
*
* Non-finite divisors return `Duration.zero`. Division by positive or negative
* zero can produce signed infinity for non-zero finite durations, while zero
* or infinite durations divided by zero produce `Duration.zero`.
* Nanosecond-backed durations return `Duration.zero` when the divisor cannot
* be converted to a `bigint`.
*
* **Example** (Dividing durations unsafely)
*
* ```ts
* import { Duration } from "effect"
*
* const half = Duration.divideUnsafe(Duration.seconds(10), 2)
* console.log(Duration.toSeconds(half)) // 5
*
* const infinite = Duration.divideUnsafe(Duration.seconds(10), 0)
* console.log(Duration.toMillis(infinite)) // Infinity
* ```
*
* @category math
* @since 4.0.0
*/
export const divideUnsafe = /*#__PURE__*/dual(2, (self, by) => {
if (!Number.isFinite(by)) return zero;
return match(self, {
onMillis: millis => make(millis / by),
onNanos: nanos => {
if (Object.is(by, 0) || Object.is(by, -0)) {
if (nanos === bigint0) return zero;
// match IEEE 754: same sign → +infinity, different sign → -infinity
const positiveNanos = nanos > bigint0;
const positiveZero = Object.is(by, 0);
return positiveNanos === positiveZero ? infinity : negativeInfinity;
}
try {
return make(nanos / BigInt(by));
} catch {
return zero;
}
},
onInfinity: () => by > 0 ? infinity : by < 0 ? negativeInfinity : zero,
onNegativeInfinity: () => by > 0 ? negativeInfinity : by < 0 ? infinity : zero
});
});
/**
* Returns a `Duration` multiplied by a number.
*
* **Details**
*
* For nanosecond-backed durations, the multiplier must be convertible to a
* `bigint`; fractional or non-finite multipliers can throw. Infinite
* durations return positive infinity, negative infinity, or zero depending on
* the multiplier sign.
*
* **Example** (Multiplying durations)
*
* ```ts
* import { Duration } from "effect"
*
* const doubled = Duration.times(Duration.seconds(5), 2)
* console.log(Duration.toSeconds(doubled)) // 10
* ```
*
* @category math
* @since 2.0.0
*/
export const times = /*#__PURE__*/dual(2, (self, times) => match(self, {
onMillis: millis => make(millis * times),
onNanos: nanos => make(nanos * BigInt(times)),
onInfinity: () => times > 0 ? infinity : times < 0 ? negativeInfinity : zero,
onNegativeInfinity: () => times > 0 ? negativeInfinity : times < 0 ? infinity : zero
}));
/**
* Subtracts one Duration from another. The result can be negative.
*
* **Details**
*
* Infinity subtraction follows signed-infinity arithmetic. Subtracting the
* same infinity from itself returns zero. Positive infinity minus negative
* infinity or any finite duration remains positive infinity. Negative infinity
* minus positive infinity or any finite duration remains negative infinity.
* Finite durations minus positive infinity produce negative infinity, and
* finite durations minus negative infinity produce positive infinity.
*
* **Example** (Subtracting durations)
*
* ```ts
* import { Duration } from "effect"
*
* const result = Duration.subtract(Duration.seconds(10), Duration.seconds(3))
* console.log(Duration.toSeconds(result)) // 7
* ```
*
* @category math
* @since 2.0.0
*/
export const subtract = /*#__PURE__*/dual(2, (self, that) => matchPair(self, that, {
onMillis: (self, that) => make(self - that),
onNanos: (self, that) => make(self - that),
onInfinity: (self, that) => {
const s = self.value._tag;
const t = that.value._tag;
if (s === "Infinity") return t === "Infinity" ? zero : infinity;
if (s === "NegativeInfinity") return t === "NegativeInfinity" ? zero : negativeInfinity;
return t === "Infinity" ? negativeInfinity : infinity;
}
}));
/**
* Adds two Durations together.
*
* **Details**
*
* Infinity addition follows these rules:
*
* - infinity + infinity = infinity
* - infinity + negativeInfinity = zero
* - infinity + finite = infinity
* - negativeInfinity + negativeInfinity = negativeInfinity
* - negativeInfinity + finite = negativeInfinity
*
* **Example** (Adding durations)
*
* ```ts
* import { Duration } from "effect"
*
* const total = Duration.sum(Duration.seconds(5), Duration.seconds(3))
* console.log(Duration.toSeconds(total)) // 8
* ```
*
* @category math
* @since 2.0.0
*/
export const sum = /*#__PURE__*/dual(2, (self, that) => matchPair(self, that, {
onMillis: (self, that) => make(self + that),
onNanos: (self, that) => make(self + that),
onInfinity: (self, that) => {
const s = self.value._tag;
const t = that.value._tag;
if (s === "Infinity" && t === "NegativeInfinity") return zero;
if (s === "NegativeInfinity" && t === "Infinity") return zero;
if (s === "Infinity" || t === "Infinity") return infinity;
if (s === "NegativeInfinity" || t === "NegativeInfinity") return negativeInfinity;
// unreachable, but satisfy TS
return zero;
}
}));
/**
* Checks whether the first Duration is less than the second.
*
* **Example** (Comparing durations with less than)
*
* ```ts
* import { Duration } from "effect"
*
* const isLess = Duration.isLessThan(Duration.seconds(3), Duration.seconds(5))
* console.log(isLess) // true
* ```
*
* @category predicates
* @since 4.0.0
*/
export const isLessThan = /*#__PURE__*/order.isLessThan(Order);
/**
* Checks whether the first Duration is less than or equal to the second.
*
* **Example** (Comparing durations with less than or equal)
*
* ```ts
* import { Duration } from "effect"
*
* const isLessOrEqual = Duration.isLessThanOrEqualTo(
* Duration.seconds(5),
* Duration.seconds(5)
* )
* console.log(isLessOrEqual) // true
* ```
*
* @category predicates
* @since 4.0.0
*/
export const isLessThanOrEqualTo = /*#__PURE__*/order.isLessThanOrEqualTo(Order);
/**
* Checks whether the first Duration is greater than the second.
*
* **Example** (Comparing durations with greater than)
*
* ```ts
* import { Duration } from "effect"
*
* const isGreater = Duration.isGreaterThan(Duration.seconds(5), Duration.seconds(3))
* console.log(isGreater) // true
* ```
*
* @category predicates
* @since 4.0.0
*/
export const isGreaterThan = /*#__PURE__*/order.isGreaterThan(Order);
/**
* Checks whether the first Duration is greater than or equal to the second.
*
* **Example** (Comparing durations with greater than or equal)
*
* ```ts
* import { Duration } from "effect"
*
* const isGreaterOrEqual = Duration.isGreaterThanOrEqualTo(
* Duration.seconds(5),
* Duration.seconds(5)
* )
* console.log(isGreaterOrEqual) // true
* ```
*
* @category predicates
* @since 4.0.0
*/
export const isGreaterThanOrEqualTo = /*#__PURE__*/order.isGreaterThanOrEqualTo(Order);
/**
* Checks whether two Durations are equal.
*
* **Example** (Checking duration equality)
*
* ```ts
* import { Duration } from "effect"
*
* const isEqual = Duration.equals(Duration.seconds(5), Duration.millis(5000))
* console.log(isEqual) // true
* ```
*
* @category predicates
* @since 2.0.0
*/
export const equals = /*#__PURE__*/dual(2, (self, that) => Equivalence(self, that));
/**
* Decomposes a `Duration` into normalized signed components.
*
* **Details**
*
* Finite durations are returned as `{ days, hours, minutes, seconds, millis,
* nanos }`. Infinite durations return every component as `Infinity` or
* `-Infinity`.
*
* **Example** (Decomposing durations into parts)
*
* ```ts
* import { Duration } from "effect"
*
* // Create a complex duration by adding multiple parts
* const duration = Duration.sum(
* Duration.sum(
* Duration.sum(Duration.days(1), Duration.hours(2)),
* Duration.sum(Duration.minutes(30), Duration.seconds(45))
* ),
* Duration.millis(123)
* )
* const components = Duration.parts(duration)
* console.log(components)
* // {
* // days: 1,
* // hours: 2,
* // minutes: 30,
* // seconds: 45,
* // millis: 123,
* // nanos: 0
* // }
*
* const complex = Duration.sum(Duration.hours(25), Duration.minutes(90))
* const complexParts = Duration.parts(complex)
* console.log(complexParts)
* // {
* // days: 1,
* // hours: 2,
* // minutes: 30,
* // seconds: 0,
* // millis: 0,
* // nanos: 0
* // }
* ```
*
* @category converting
* @since 3.8.0
*/
export const parts = self => {
if (self.value._tag === "Infinity") {
return {
days: Infinity,
hours: Infinity,
minutes: Infinity,
seconds: Infinity,
millis: Infinity,
nanos: Infinity
};
}
if (self.value._tag === "NegativeInfinity") {
return {
days: -Infinity,
hours: -Infinity,
minutes: -Infinity,
seconds: -Infinity,
millis: -Infinity,
nanos: -Infinity
};
}
const n = toNanosUnsafe(self);
const neg = n < bigint0;
const a = neg ? -n : n;
const ms = a / bigint1e6;
const sec = ms / bigint1e3;
const min = sec / bigint60;
const hr = min / bigint60;
const d = hr / bigint24;
const sign = neg ? -1 : 1;
return {
days: sign * Number(d),
hours: sign * Number(hr % bigint24),
minutes: sign * Number(min % bigint60),
seconds: sign * Number(sec % bigint60),
millis: sign * Number(ms % bigint1e3),
nanos: sign * Number(a % bigint1e6)
};
};
/**
* Converts a `Duration` to a human readable string.
*
* **Example** (Formatting durations)
*
* ```ts
* import { Duration } from "effect"
*
* Duration.format(Duration.millis(1000)) // "1s"
* Duration.format(Duration.millis(1001)) // "1s 1ms"
* ```
*
* @category converting
* @since 2.0.0
*/
export const format = self => {
if (self.value._tag === "Infinity") {
return "Infinity";
}
if (self.value._tag === "NegativeInfinity") {
return "-Infinity";
}
if (isZero(self)) {
return "0";
}
if (isNegative(self)) {
return "-" + format(abs(self));
}
const fragments = parts(self);
const pieces = [];
if (fragments.days !== 0) {
pieces.push(`${fragments.days}d`);
}
if (fragments.hours !== 0) {
pieces.push(`${fragments.hours}h`);
}
if (fragments.minutes !== 0) {
pieces.push(`${fragments.minutes}m`);
}
if (fragments.seconds !== 0) {
pieces.push(`${fragments.seconds}s`);
}
if (fragments.millis !== 0) {
pieces.push(`${fragments.millis}ms`);
}
if (fragments.nanos !== 0) {
pieces.push(`${fragments.nanos}ns`);
}
return pieces.join(" ");
};
/**
* Reducer for summing `Duration`s.
*
* **When to use**
*
* Use to sum many `Duration` values through APIs that consume a `Reducer`.
*
* **Details**
*
* `ReducerSum` uses `sum` and starts from `zero`, so `combineAll([])` returns
* `zero`.
*
* @see {@link sum} for adding two duration values directly
* @see {@link CombinerMax} for keeping the longest duration instead of summing
* @see {@link CombinerMin} for keeping the shortest duration instead of summing
*
* @category math
* @since 4.0.0
*/
export const ReducerSum = /*#__PURE__*/Reducer.make(sum, zero);
/**
* Combiner that returns the maximum `Duration`.
*
* **When to use**
*
* Use to keep the longest `Duration` when an API consumes a `Combiner`.
*
* @see {@link CombinerMin} for keeping the shortest `Duration`
* @see {@link max} for comparing two `Duration` values directly
*
* @category math
* @since 4.0.0
*/
export const CombinerMax = /*#__PURE__*/Combiner.max(Order);
/**
* Combiner that returns the minimum `Duration`.
*
* **When to use**
*
* Use to keep the shortest `Duration` through APIs that consume a `Combiner`.
*
* @see {@link CombinerMax} for keeping the longest `Duration`
* @see {@link min} for comparing two `Duration` values directly
*
* @category math
* @since 4.0.0
*/
export const CombinerMin = /*#__PURE__*/Combiner.min(Order);
//# sourceMappingURL=Duration.js.map

Xet Storage Details

Size:
40.9 kB
·
Xet hash:
c52edeb7c6045399534411bc9f6ac9d124d4683c0bffa5880c0ab91a3c37f722

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.