Skip to content

Commit

Permalink
add Duration.parts api (#3603)
Browse files Browse the repository at this point in the history
Co-authored-by: Tim <hello@timsmart.co>
  • Loading branch information
fubhy and tim-smart committed Sep 15, 2024
1 parent 569a801 commit eebfd29
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 23 deletions.
11 changes: 11 additions & 0 deletions .changeset/weak-moles-repair.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
"effect": minor
---

Add `Duration.parts` api

```ts
const parts = Duration.parts(Duration.sum("5 minutes", "20 seconds"))
assert.equal(parts.minutes, 5)
assert.equal(parts.seconds, 20)
```
85 changes: 62 additions & 23 deletions packages/effect/src/Duration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,7 @@ const _max = order.max(Order)

/**
* @since 2.0.0
* @category order
*/
export const max: {
(that: DurationInput): (self: DurationInput) => Duration
Expand All @@ -511,6 +512,7 @@ const _clamp = order.clamp(Order)

/**
* @since 2.0.0
* @category order
*/
export const clamp: {
(options: {
Expand Down Expand Up @@ -707,10 +709,54 @@ export const equals: {
(self: DurationInput, that: DurationInput): boolean
} = dual(2, (self: DurationInput, that: DurationInput): boolean => Equivalence(decode(self), decode(that)))

/**
* Converts a `Duration` to its parts.
*
* @since 3.8.0
* @category conversions
*/
export const parts = (self: DurationInput): {
days: number
hours: number
minutes: number
seconds: number
millis: number
nanos: number
} => {
const duration = decode(self)
if (duration.value._tag === "Infinity") {
return {
days: Infinity,
hours: Infinity,
minutes: Infinity,
seconds: Infinity,
millis: Infinity,
nanos: Infinity
}
}

const nanos = unsafeToNanos(duration)
const ms = nanos / bigint1e6
const sec = ms / bigint1e3
const min = sec / bigint60
const hr = min / bigint60
const days = hr / bigint24

return {
days: Number(days),
hours: Number(hr % bigint24),
minutes: Number(min % bigint60),
seconds: Number(sec % bigint60),
millis: Number(ms % bigint1e3),
nanos: Number(nanos % bigint1e6)
}
}

/**
* Converts a `Duration` to a human readable string.
* @since 2.0.0
*
* @since 2.0.0
* @category conversions
* @example
* import { Duration } from "effect"
*
Expand All @@ -719,42 +765,35 @@ export const equals: {
*/
export const format = (self: DurationInput): string => {
const duration = decode(self)
const parts = []

if (duration.value._tag === "Infinity") {
return "Infinity"
}

const nanos = unsafeToNanos(duration)

if (nanos % bigint1e6) {
parts.push(`${nanos % bigint1e6}ns`)
const fragments = parts(duration)
const pieces = []
if (fragments.days !== 0) {
pieces.push(`${fragments.days}d`)
}

const ms = nanos / bigint1e6
if (ms % bigint1e3 !== bigint0) {
parts.push(`${ms % bigint1e3}ms`)
if (fragments.hours !== 0) {
pieces.push(`${fragments.hours}h`)
}

const sec = ms / bigint1e3
if (sec % bigint60 !== bigint0) {
parts.push(`${sec % bigint60}s`)
if (fragments.minutes !== 0) {
pieces.push(`${fragments.minutes}m`)
}

const min = sec / bigint60
if (min % bigint60 !== bigint0) {
parts.push(`${min % bigint60}m`)
if (fragments.seconds !== 0) {
pieces.push(`${fragments.seconds}s`)
}

const hr = min / bigint60
if (hr % bigint24 !== bigint0) {
parts.push(`${hr % bigint24}h`)
if (fragments.millis !== 0) {
pieces.push(`${fragments.millis}ms`)
}

const days = hr / bigint24
if (days !== bigint0) {
parts.push(`${days}d`)
if (fragments.nanos !== 0) {
pieces.push(`${fragments.nanos}ns`)
}

return parts.reverse().join(" ")
return pieces.join(" ")
}
29 changes: 29 additions & 0 deletions packages/effect/test/Duration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,35 @@ describe("Duration", () => {
expect(Duration.format(Duration.weeks(1))).toEqual(`7d`)
})

it("format", () => {
expect(Duration.parts(Duration.infinity)).toStrictEqual({
days: Infinity,
hours: Infinity,
minutes: Infinity,
seconds: Infinity,
millis: Infinity,
nanos: Infinity
})

expect(Duration.parts(Duration.minutes(5.325))).toStrictEqual({
days: 0,
hours: 0,
minutes: 5,
seconds: 19,
millis: 500,
nanos: 0
})

expect(Duration.parts(Duration.minutes(3.11125))).toStrictEqual({
days: 0,
hours: 0,
minutes: 3,
seconds: 6,
millis: 675,
nanos: 0
})
})

it("toJSON", () => {
expect(Duration.seconds(2).toJSON()).toEqual(
{ _id: "Duration", _tag: "Millis", millis: 2000 }
Expand Down

0 comments on commit eebfd29

Please sign in to comment.