effect 3.10.19 → 3.11.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 (167) hide show
  1. package/dist/cjs/BigDecimal.js +125 -24
  2. package/dist/cjs/BigDecimal.js.map +1 -1
  3. package/dist/cjs/Channel.js +44 -4
  4. package/dist/cjs/Channel.js.map +1 -1
  5. package/dist/cjs/Config.js +8 -1
  6. package/dist/cjs/Config.js.map +1 -1
  7. package/dist/cjs/Context.js +26 -1
  8. package/dist/cjs/Context.js.map +1 -1
  9. package/dist/cjs/Cron.js +75 -67
  10. package/dist/cjs/Cron.js.map +1 -1
  11. package/dist/cjs/DateTime.js +114 -664
  12. package/dist/cjs/DateTime.js.map +1 -1
  13. package/dist/cjs/Effect.js +82 -4
  14. package/dist/cjs/Effect.js.map +1 -1
  15. package/dist/cjs/Inspectable.js +8 -4
  16. package/dist/cjs/Inspectable.js.map +1 -1
  17. package/dist/cjs/JSONSchema.js.map +1 -1
  18. package/dist/cjs/Micro.js +1099 -1072
  19. package/dist/cjs/Micro.js.map +1 -1
  20. package/dist/cjs/STM.js.map +1 -1
  21. package/dist/cjs/Schema.js +57 -8
  22. package/dist/cjs/Schema.js.map +1 -1
  23. package/dist/cjs/Sink.js +9 -1
  24. package/dist/cjs/Sink.js.map +1 -1
  25. package/dist/cjs/Stream.js +25 -7
  26. package/dist/cjs/Stream.js.map +1 -1
  27. package/dist/cjs/Utils.js +7 -1
  28. package/dist/cjs/Utils.js.map +1 -1
  29. package/dist/cjs/internal/channel/channelExecutor.js +5 -9
  30. package/dist/cjs/internal/channel/channelExecutor.js.map +1 -1
  31. package/dist/cjs/internal/channel.js +156 -130
  32. package/dist/cjs/internal/channel.js.map +1 -1
  33. package/dist/cjs/internal/config.js +13 -4
  34. package/dist/cjs/internal/config.js.map +1 -1
  35. package/dist/cjs/internal/context.js +46 -3
  36. package/dist/cjs/internal/context.js.map +1 -1
  37. package/dist/cjs/internal/dateTime.js +747 -0
  38. package/dist/cjs/internal/dateTime.js.map +1 -0
  39. package/dist/cjs/internal/fiberRuntime.js +34 -11
  40. package/dist/cjs/internal/fiberRuntime.js.map +1 -1
  41. package/dist/cjs/internal/groupBy.js +9 -3
  42. package/dist/cjs/internal/groupBy.js.map +1 -1
  43. package/dist/cjs/internal/layer.js +1 -1
  44. package/dist/cjs/internal/layer.js.map +1 -1
  45. package/dist/cjs/internal/mailbox.js +1 -1
  46. package/dist/cjs/internal/mailbox.js.map +1 -1
  47. package/dist/cjs/internal/sink.js +25 -21
  48. package/dist/cjs/internal/sink.js.map +1 -1
  49. package/dist/cjs/internal/stream.js +70 -71
  50. package/dist/cjs/internal/stream.js.map +1 -1
  51. package/dist/cjs/internal/version.js +1 -1
  52. package/dist/cjs/internal/version.js.map +1 -1
  53. package/dist/dts/BigDecimal.d.ts +56 -1
  54. package/dist/dts/BigDecimal.d.ts.map +1 -1
  55. package/dist/dts/Channel.d.ts +66 -5
  56. package/dist/dts/Channel.d.ts.map +1 -1
  57. package/dist/dts/Config.d.ts +23 -1
  58. package/dist/dts/Config.d.ts.map +1 -1
  59. package/dist/dts/Context.d.ts +111 -0
  60. package/dist/dts/Context.d.ts.map +1 -1
  61. package/dist/dts/Cron.d.ts +15 -6
  62. package/dist/dts/Cron.d.ts.map +1 -1
  63. package/dist/dts/DateTime.d.ts +40 -49
  64. package/dist/dts/DateTime.d.ts.map +1 -1
  65. package/dist/dts/Effect.d.ts +88 -1
  66. package/dist/dts/Effect.d.ts.map +1 -1
  67. package/dist/dts/Inspectable.d.ts.map +1 -1
  68. package/dist/dts/JSONSchema.d.ts +1 -0
  69. package/dist/dts/JSONSchema.d.ts.map +1 -1
  70. package/dist/dts/Micro.d.ts +875 -872
  71. package/dist/dts/Micro.d.ts.map +1 -1
  72. package/dist/dts/STM.d.ts +2 -0
  73. package/dist/dts/STM.d.ts.map +1 -1
  74. package/dist/dts/Schema.d.ts +32 -0
  75. package/dist/dts/Schema.d.ts.map +1 -1
  76. package/dist/dts/Sink.d.ts +8 -0
  77. package/dist/dts/Sink.d.ts.map +1 -1
  78. package/dist/dts/Stream.d.ts +50 -32
  79. package/dist/dts/Stream.d.ts.map +1 -1
  80. package/dist/dts/Utils.d.ts +4 -0
  81. package/dist/dts/Utils.d.ts.map +1 -1
  82. package/dist/dts/internal/context.d.ts +1 -1
  83. package/dist/dts/internal/context.d.ts.map +1 -1
  84. package/dist/dts/internal/dateTime.d.ts +2 -0
  85. package/dist/dts/internal/dateTime.d.ts.map +1 -0
  86. package/dist/dts/internal/fiberRuntime.d.ts.map +1 -1
  87. package/dist/dts/internal/stream.d.ts.map +1 -1
  88. package/dist/esm/BigDecimal.js +119 -20
  89. package/dist/esm/BigDecimal.js.map +1 -1
  90. package/dist/esm/Channel.js +42 -2
  91. package/dist/esm/Channel.js.map +1 -1
  92. package/dist/esm/Config.js +7 -0
  93. package/dist/esm/Config.js.map +1 -1
  94. package/dist/esm/Context.js +25 -0
  95. package/dist/esm/Context.js.map +1 -1
  96. package/dist/esm/Cron.js +75 -67
  97. package/dist/esm/Cron.js.map +1 -1
  98. package/dist/esm/DateTime.js +112 -627
  99. package/dist/esm/DateTime.js.map +1 -1
  100. package/dist/esm/Effect.js +77 -0
  101. package/dist/esm/Effect.js.map +1 -1
  102. package/dist/esm/Inspectable.js +8 -4
  103. package/dist/esm/Inspectable.js.map +1 -1
  104. package/dist/esm/JSONSchema.js.map +1 -1
  105. package/dist/esm/Micro.js +1077 -1047
  106. package/dist/esm/Micro.js.map +1 -1
  107. package/dist/esm/STM.js.map +1 -1
  108. package/dist/esm/Schema.js +54 -0
  109. package/dist/esm/Schema.js.map +1 -1
  110. package/dist/esm/Sink.js +8 -0
  111. package/dist/esm/Sink.js.map +1 -1
  112. package/dist/esm/Stream.js +23 -5
  113. package/dist/esm/Stream.js.map +1 -1
  114. package/dist/esm/Utils.js +5 -0
  115. package/dist/esm/Utils.js.map +1 -1
  116. package/dist/esm/internal/channel/channelExecutor.js +5 -7
  117. package/dist/esm/internal/channel/channelExecutor.js.map +1 -1
  118. package/dist/esm/internal/channel.js +152 -129
  119. package/dist/esm/internal/channel.js.map +1 -1
  120. package/dist/esm/internal/config.js +11 -3
  121. package/dist/esm/internal/config.js.map +1 -1
  122. package/dist/esm/internal/context.js +42 -2
  123. package/dist/esm/internal/context.js.map +1 -1
  124. package/dist/esm/internal/dateTime.js +704 -0
  125. package/dist/esm/internal/dateTime.js.map +1 -0
  126. package/dist/esm/internal/fiberRuntime.js +31 -9
  127. package/dist/esm/internal/fiberRuntime.js.map +1 -1
  128. package/dist/esm/internal/groupBy.js +9 -3
  129. package/dist/esm/internal/groupBy.js.map +1 -1
  130. package/dist/esm/internal/layer.js +1 -1
  131. package/dist/esm/internal/layer.js.map +1 -1
  132. package/dist/esm/internal/mailbox.js +1 -1
  133. package/dist/esm/internal/mailbox.js.map +1 -1
  134. package/dist/esm/internal/sink.js +23 -20
  135. package/dist/esm/internal/sink.js.map +1 -1
  136. package/dist/esm/internal/stream.js +66 -69
  137. package/dist/esm/internal/stream.js.map +1 -1
  138. package/dist/esm/internal/version.js +1 -1
  139. package/dist/esm/internal/version.js.map +1 -1
  140. package/package.json +1 -1
  141. package/src/BigDecimal.ts +131 -21
  142. package/src/Channel.ts +81 -5
  143. package/src/Config.ts +24 -1
  144. package/src/Context.ts +119 -0
  145. package/src/Cron.ts +85 -68
  146. package/src/DateTime.ts +155 -757
  147. package/src/Effect.ts +340 -1
  148. package/src/Inspectable.ts +11 -7
  149. package/src/JSONSchema.ts +1 -0
  150. package/src/Micro.ts +2005 -1757
  151. package/src/STM.ts +2 -0
  152. package/src/Schema.ts +60 -0
  153. package/src/Sink.ts +11 -0
  154. package/src/Stream.ts +55 -44
  155. package/src/Utils.ts +8 -0
  156. package/src/internal/channel/channelExecutor.ts +37 -33
  157. package/src/internal/channel.ts +504 -467
  158. package/src/internal/config.ts +18 -6
  159. package/src/internal/context.ts +56 -4
  160. package/src/internal/dateTime.ts +1126 -0
  161. package/src/internal/fiberRuntime.ts +35 -16
  162. package/src/internal/groupBy.ts +13 -22
  163. package/src/internal/layer.ts +5 -8
  164. package/src/internal/mailbox.ts +6 -4
  165. package/src/internal/sink.ts +55 -35
  166. package/src/internal/stream.ts +299 -299
  167. package/src/internal/version.ts +1 -1
@@ -0,0 +1,1126 @@
1
+ import { IllegalArgumentException } from "../Cause.js"
2
+ import * as Clock from "../Clock.js"
3
+ import type * as DateTime from "../DateTime.js"
4
+ import * as Duration from "../Duration.js"
5
+ import type * as Effect from "../Effect.js"
6
+ import * as Either from "../Either.js"
7
+ import * as Equal from "../Equal.js"
8
+ import * as equivalence from "../Equivalence.js"
9
+ import type { LazyArg } from "../Function.js"
10
+ import { dual, pipe } from "../Function.js"
11
+ import { globalValue } from "../GlobalValue.js"
12
+ import * as Hash from "../Hash.js"
13
+ import * as Inspectable from "../Inspectable.js"
14
+ import * as Option from "../Option.js"
15
+ import * as order from "../Order.js"
16
+ import { pipeArguments } from "../Pipeable.js"
17
+ import * as Predicate from "../Predicate.js"
18
+ import type { Mutable } from "../Types.js"
19
+ import * as internalEffect from "./core-effect.js"
20
+ import * as core from "./core.js"
21
+
22
+ /** @internal */
23
+ export const TypeId: DateTime.TypeId = Symbol.for("effect/DateTime") as DateTime.TypeId
24
+
25
+ /** @internal */
26
+ export const TimeZoneTypeId: DateTime.TimeZoneTypeId = Symbol.for("effect/DateTime/TimeZone") as DateTime.TimeZoneTypeId
27
+
28
+ const Proto = {
29
+ [TypeId]: TypeId,
30
+ pipe() {
31
+ return pipeArguments(this, arguments)
32
+ },
33
+ [Inspectable.NodeInspectSymbol](this: DateTime.DateTime) {
34
+ return this.toString()
35
+ },
36
+ toJSON(this: DateTime.DateTime) {
37
+ return toDateUtc(this).toJSON()
38
+ }
39
+ }
40
+
41
+ const ProtoUtc = {
42
+ ...Proto,
43
+ _tag: "Utc",
44
+ [Hash.symbol](this: DateTime.Utc) {
45
+ return Hash.cached(this, Hash.number(this.epochMillis))
46
+ },
47
+ [Equal.symbol](this: DateTime.Utc, that: unknown) {
48
+ return isDateTime(that) && that._tag === "Utc" && this.epochMillis === that.epochMillis
49
+ },
50
+ toString(this: DateTime.Utc) {
51
+ return `DateTime.Utc(${toDateUtc(this).toJSON()})`
52
+ }
53
+ }
54
+
55
+ const ProtoZoned = {
56
+ ...Proto,
57
+ _tag: "Zoned",
58
+ [Hash.symbol](this: DateTime.Zoned) {
59
+ return pipe(
60
+ Hash.number(this.epochMillis),
61
+ Hash.combine(Hash.hash(this.zone)),
62
+ Hash.cached(this)
63
+ )
64
+ },
65
+ [Equal.symbol](this: DateTime.Zoned, that: unknown) {
66
+ return isDateTime(that) && that._tag === "Zoned" && this.epochMillis === that.epochMillis &&
67
+ Equal.equals(this.zone, that.zone)
68
+ },
69
+ toString(this: DateTime.Zoned) {
70
+ return `DateTime.Zoned(${formatIsoZoned(this)})`
71
+ }
72
+ }
73
+
74
+ const ProtoTimeZone = {
75
+ [TimeZoneTypeId]: TimeZoneTypeId,
76
+ [Inspectable.NodeInspectSymbol](this: DateTime.TimeZone) {
77
+ return this.toString()
78
+ }
79
+ }
80
+
81
+ const ProtoTimeZoneNamed = {
82
+ ...ProtoTimeZone,
83
+ _tag: "Named",
84
+ [Hash.symbol](this: DateTime.TimeZone.Named) {
85
+ return Hash.cached(this, Hash.string(`Named:${this.id}`))
86
+ },
87
+ [Equal.symbol](this: DateTime.TimeZone.Named, that: unknown) {
88
+ return isTimeZone(that) && that._tag === "Named" && this.id === that.id
89
+ },
90
+ toString(this: DateTime.TimeZone.Named) {
91
+ return `TimeZone.Named(${this.id})`
92
+ },
93
+ toJSON(this: DateTime.TimeZone.Named) {
94
+ return {
95
+ _id: "TimeZone",
96
+ _tag: "Named",
97
+ id: this.id
98
+ }
99
+ }
100
+ }
101
+
102
+ const ProtoTimeZoneOffset = {
103
+ ...ProtoTimeZone,
104
+ _tag: "Offset",
105
+ [Hash.symbol](this: DateTime.TimeZone.Offset) {
106
+ return Hash.cached(this, Hash.string(`Offset:${this.offset}`))
107
+ },
108
+ [Equal.symbol](this: DateTime.TimeZone.Offset, that: unknown) {
109
+ return isTimeZone(that) && that._tag === "Offset" && this.offset === that.offset
110
+ },
111
+ toString(this: DateTime.TimeZone.Offset) {
112
+ return `TimeZone.Offset(${offsetToString(this.offset)})`
113
+ },
114
+ toJSON(this: DateTime.TimeZone.Offset) {
115
+ return {
116
+ _id: "TimeZone",
117
+ _tag: "Offset",
118
+ offset: this.offset
119
+ }
120
+ }
121
+ }
122
+
123
+ /** @internal */
124
+ export const makeZonedProto = (
125
+ epochMillis: number,
126
+ zone: DateTime.TimeZone,
127
+ partsUtc?: DateTime.DateTime.PartsWithWeekday
128
+ ): DateTime.Zoned => {
129
+ const self = Object.create(ProtoZoned)
130
+ self.epochMillis = epochMillis
131
+ self.zone = zone
132
+ self.partsUtc = partsUtc
133
+ return self
134
+ }
135
+
136
+ // =============================================================================
137
+ // guards
138
+ // =============================================================================
139
+
140
+ /** @internal */
141
+ export const isDateTime = (u: unknown): u is DateTime.DateTime => Predicate.hasProperty(u, TypeId)
142
+
143
+ const isDateTimeArgs = (args: IArguments) => isDateTime(args[0])
144
+
145
+ /** @internal */
146
+ export const isTimeZone = (u: unknown): u is DateTime.TimeZone => Predicate.hasProperty(u, TimeZoneTypeId)
147
+
148
+ /** @internal */
149
+ export const isTimeZoneOffset = (u: unknown): u is DateTime.TimeZone.Offset => isTimeZone(u) && u._tag === "Offset"
150
+
151
+ /** @internal */
152
+ export const isTimeZoneNamed = (u: unknown): u is DateTime.TimeZone.Named => isTimeZone(u) && u._tag === "Named"
153
+
154
+ /** @internal */
155
+ export const isUtc = (self: DateTime.DateTime): self is DateTime.Utc => self._tag === "Utc"
156
+
157
+ /** @internal */
158
+ export const isZoned = (self: DateTime.DateTime): self is DateTime.Zoned => self._tag === "Zoned"
159
+
160
+ // =============================================================================
161
+ // instances
162
+ // =============================================================================
163
+
164
+ /** @internal */
165
+ export const Equivalence: equivalence.Equivalence<DateTime.DateTime> = equivalence.make((a, b) =>
166
+ a.epochMillis === b.epochMillis
167
+ )
168
+
169
+ /** @internal */
170
+ export const Order: order.Order<DateTime.DateTime> = order.make((self, that) =>
171
+ self.epochMillis < that.epochMillis ? -1 : self.epochMillis > that.epochMillis ? 1 : 0
172
+ )
173
+
174
+ /** @internal */
175
+ export const clamp: {
176
+ (options: { minimum: DateTime.DateTime; maximum: DateTime.DateTime }): (self: DateTime.DateTime) => DateTime.DateTime
177
+ (self: DateTime.DateTime, options: { minimum: DateTime.DateTime; maximum: DateTime.DateTime }): DateTime.DateTime
178
+ } = order.clamp(Order)
179
+
180
+ // =============================================================================
181
+ // constructors
182
+ // =============================================================================
183
+
184
+ const makeUtc = (epochMillis: number): DateTime.Utc => {
185
+ const self = Object.create(ProtoUtc)
186
+ self.epochMillis = epochMillis
187
+ return self
188
+ }
189
+
190
+ /** @internal */
191
+ export const unsafeFromDate = (date: Date): DateTime.Utc => {
192
+ const epochMillis = date.getTime()
193
+ if (Number.isNaN(epochMillis)) {
194
+ throw new IllegalArgumentException("Invalid date")
195
+ }
196
+ return makeUtc(epochMillis)
197
+ }
198
+
199
+ /** @internal */
200
+ export const unsafeMake = <A extends DateTime.DateTime.Input>(input: A): DateTime.DateTime.PreserveZone<A> => {
201
+ if (isDateTime(input)) {
202
+ return input as DateTime.DateTime.PreserveZone<A>
203
+ } else if (input instanceof Date) {
204
+ return unsafeFromDate(input) as DateTime.DateTime.PreserveZone<A>
205
+ } else if (typeof input === "object") {
206
+ const date = new Date(0)
207
+ setPartsDate(date, input)
208
+ return unsafeFromDate(date) as DateTime.DateTime.PreserveZone<A>
209
+ }
210
+ return unsafeFromDate(new Date(input)) as DateTime.DateTime.PreserveZone<A>
211
+ }
212
+
213
+ /** @internal */
214
+ export const unsafeMakeZoned = (input: DateTime.DateTime.Input, options?: {
215
+ readonly timeZone?: number | string | DateTime.TimeZone | undefined
216
+ readonly adjustForTimeZone?: boolean | undefined
217
+ }): DateTime.Zoned => {
218
+ if (options?.timeZone === undefined && isDateTime(input) && isZoned(input)) {
219
+ return input
220
+ }
221
+ const self = unsafeMake(input)
222
+ let zone: DateTime.TimeZone
223
+ if (options?.timeZone === undefined) {
224
+ const offset = new Date(self.epochMillis).getTimezoneOffset() * -60 * 1000
225
+ zone = zoneMakeOffset(offset)
226
+ } else if (isTimeZone(options?.timeZone)) {
227
+ zone = options.timeZone
228
+ } else if (typeof options?.timeZone === "number") {
229
+ zone = zoneMakeOffset(options.timeZone)
230
+ } else {
231
+ const parsedZone = zoneFromString(options.timeZone)
232
+ if (Option.isNone(parsedZone)) {
233
+ throw new IllegalArgumentException(`Invalid time zone: ${options.timeZone}`)
234
+ }
235
+ zone = parsedZone.value
236
+ }
237
+ if (options?.adjustForTimeZone !== true) {
238
+ return makeZonedProto(self.epochMillis, zone, self.partsUtc)
239
+ }
240
+ return makeZonedFromAdjusted(self.epochMillis, zone)
241
+ }
242
+
243
+ /** @internal */
244
+ export const makeZoned: (
245
+ input: DateTime.DateTime.Input,
246
+ options?: {
247
+ readonly timeZone?: number | string | DateTime.TimeZone | undefined
248
+ readonly adjustForTimeZone?: boolean | undefined
249
+ }
250
+ ) => Option.Option<DateTime.Zoned> = Option.liftThrowable(unsafeMakeZoned)
251
+
252
+ /** @internal */
253
+ export const make: <A extends DateTime.DateTime.Input>(input: A) => Option.Option<DateTime.DateTime.PreserveZone<A>> =
254
+ Option.liftThrowable(unsafeMake)
255
+
256
+ const zonedStringRegex = /^(.{17,35})\[(.+)\]$/
257
+
258
+ /** @internal */
259
+ export const makeZonedFromString = (input: string): Option.Option<DateTime.Zoned> => {
260
+ const match = zonedStringRegex.exec(input)
261
+ if (match === null) {
262
+ const offset = parseOffset(input)
263
+ return offset !== null ? makeZoned(input, { timeZone: offset }) : Option.none()
264
+ }
265
+ const [, isoString, timeZone] = match
266
+ return makeZoned(isoString, { timeZone })
267
+ }
268
+
269
+ /** @internal */
270
+ export const now: Effect.Effect<DateTime.Utc> = core.map(Clock.currentTimeMillis, makeUtc)
271
+
272
+ /** @internal */
273
+ export const unsafeNow: LazyArg<DateTime.Utc> = () => makeUtc(Date.now())
274
+
275
+ // =============================================================================
276
+ // time zones
277
+ // =============================================================================
278
+
279
+ /** @internal */
280
+ export const setZone: {
281
+ (zone: DateTime.TimeZone, options?: {
282
+ readonly adjustForTimeZone?: boolean | undefined
283
+ }): (self: DateTime.DateTime) => DateTime.Zoned
284
+ (self: DateTime.DateTime, zone: DateTime.TimeZone, options?: {
285
+ readonly adjustForTimeZone?: boolean | undefined
286
+ }): DateTime.Zoned
287
+ } = dual(isDateTimeArgs, (self: DateTime.DateTime, zone: DateTime.TimeZone, options?: {
288
+ readonly adjustForTimeZone?: boolean | undefined
289
+ }): DateTime.Zoned =>
290
+ options?.adjustForTimeZone === true
291
+ ? makeZonedFromAdjusted(self.epochMillis, zone)
292
+ : makeZonedProto(self.epochMillis, zone, self.partsUtc))
293
+
294
+ /** @internal */
295
+ export const setZoneOffset: {
296
+ (offset: number, options?: {
297
+ readonly adjustForTimeZone?: boolean | undefined
298
+ }): (self: DateTime.DateTime) => DateTime.Zoned
299
+ (self: DateTime.DateTime, offset: number, options?: {
300
+ readonly adjustForTimeZone?: boolean | undefined
301
+ }): DateTime.Zoned
302
+ } = dual(isDateTimeArgs, (self: DateTime.DateTime, offset: number, options?: {
303
+ readonly adjustForTimeZone?: boolean | undefined
304
+ }): DateTime.Zoned => setZone(self, zoneMakeOffset(offset), options))
305
+
306
+ const validZoneCache = globalValue("effect/DateTime/validZoneCache", () => new Map<string, DateTime.TimeZone.Named>())
307
+
308
+ const formatOptions: Intl.DateTimeFormatOptions = {
309
+ day: "numeric",
310
+ month: "numeric",
311
+ year: "numeric",
312
+ hour: "numeric",
313
+ minute: "numeric",
314
+ second: "numeric",
315
+ timeZoneName: "longOffset",
316
+ fractionalSecondDigits: 3,
317
+ hourCycle: "h23"
318
+ }
319
+
320
+ const zoneMakeIntl = (format: Intl.DateTimeFormat): DateTime.TimeZone.Named => {
321
+ const zoneId = format.resolvedOptions().timeZone
322
+ if (validZoneCache.has(zoneId)) {
323
+ return validZoneCache.get(zoneId)!
324
+ }
325
+ const zone = Object.create(ProtoTimeZoneNamed)
326
+ zone.id = zoneId
327
+ zone.format = format
328
+ validZoneCache.set(zoneId, zone)
329
+ return zone
330
+ }
331
+
332
+ /** @internal */
333
+ export const zoneUnsafeMakeNamed = (zoneId: string): DateTime.TimeZone.Named => {
334
+ if (validZoneCache.has(zoneId)) {
335
+ return validZoneCache.get(zoneId)!
336
+ }
337
+ try {
338
+ return zoneMakeIntl(
339
+ new Intl.DateTimeFormat("en-US", {
340
+ ...formatOptions,
341
+ timeZone: zoneId
342
+ })
343
+ )
344
+ } catch (_) {
345
+ throw new IllegalArgumentException(`Invalid time zone: ${zoneId}`)
346
+ }
347
+ }
348
+
349
+ /** @internal */
350
+ export const zoneMakeOffset = (offset: number): DateTime.TimeZone.Offset => {
351
+ const zone = Object.create(ProtoTimeZoneOffset)
352
+ zone.offset = offset
353
+ return zone
354
+ }
355
+
356
+ /** @internal */
357
+ export const zoneMakeNamed: (zoneId: string) => Option.Option<DateTime.TimeZone.Named> = Option.liftThrowable(
358
+ zoneUnsafeMakeNamed
359
+ )
360
+
361
+ /** @internal */
362
+ export const zoneMakeNamedEffect = (zoneId: string): Effect.Effect<DateTime.TimeZone.Named, IllegalArgumentException> =>
363
+ internalEffect.try_({
364
+ try: () => zoneUnsafeMakeNamed(zoneId),
365
+ catch: (e) => e as IllegalArgumentException
366
+ })
367
+
368
+ /** @internal */
369
+ export const zoneMakeLocal = (): DateTime.TimeZone.Named =>
370
+ zoneMakeIntl(new Intl.DateTimeFormat("en-US", formatOptions))
371
+
372
+ const offsetZoneRegex = /^(?:GMT|[+-])/
373
+
374
+ /** @internal */
375
+ export const zoneFromString = (zone: string): Option.Option<DateTime.TimeZone> => {
376
+ if (offsetZoneRegex.test(zone)) {
377
+ const offset = parseOffset(zone)
378
+ return offset === null ? Option.none() : Option.some(zoneMakeOffset(offset))
379
+ }
380
+ return zoneMakeNamed(zone)
381
+ }
382
+
383
+ /** @internal */
384
+ export const zoneToString = (self: DateTime.TimeZone): string => {
385
+ if (self._tag === "Offset") {
386
+ return offsetToString(self.offset)
387
+ }
388
+ return self.id
389
+ }
390
+
391
+ /** @internal */
392
+ export const setZoneNamed: {
393
+ (zoneId: string, options?: {
394
+ readonly adjustForTimeZone?: boolean | undefined
395
+ }): (self: DateTime.DateTime) => Option.Option<DateTime.Zoned>
396
+ (self: DateTime.DateTime, zoneId: string, options?: {
397
+ readonly adjustForTimeZone?: boolean | undefined
398
+ }): Option.Option<DateTime.Zoned>
399
+ } = dual(
400
+ isDateTimeArgs,
401
+ (self: DateTime.DateTime, zoneId: string, options?: {
402
+ readonly adjustForTimeZone?: boolean | undefined
403
+ }): Option.Option<DateTime.Zoned> => Option.map(zoneMakeNamed(zoneId), (zone) => setZone(self, zone, options))
404
+ )
405
+
406
+ /** @internal */
407
+ export const unsafeSetZoneNamed: {
408
+ (zoneId: string, options?: {
409
+ readonly adjustForTimeZone?: boolean | undefined
410
+ }): (self: DateTime.DateTime) => DateTime.Zoned
411
+ (self: DateTime.DateTime, zoneId: string, options?: {
412
+ readonly adjustForTimeZone?: boolean | undefined
413
+ }): DateTime.Zoned
414
+ } = dual(isDateTimeArgs, (self: DateTime.DateTime, zoneId: string, options?: {
415
+ readonly adjustForTimeZone?: boolean | undefined
416
+ }): DateTime.Zoned => setZone(self, zoneUnsafeMakeNamed(zoneId), options))
417
+
418
+ // =============================================================================
419
+ // comparisons
420
+ // =============================================================================
421
+
422
+ /** @internal */
423
+ export const distance: {
424
+ (other: DateTime.DateTime): (self: DateTime.DateTime) => number
425
+ (self: DateTime.DateTime, other: DateTime.DateTime): number
426
+ } = dual(2, (self: DateTime.DateTime, other: DateTime.DateTime): number => toEpochMillis(other) - toEpochMillis(self))
427
+
428
+ /** @internal */
429
+ export const distanceDurationEither: {
430
+ (other: DateTime.DateTime): (self: DateTime.DateTime) => Either.Either<Duration.Duration, Duration.Duration>
431
+ (self: DateTime.DateTime, other: DateTime.DateTime): Either.Either<Duration.Duration, Duration.Duration>
432
+ } = dual(
433
+ 2,
434
+ (self: DateTime.DateTime, other: DateTime.DateTime): Either.Either<Duration.Duration, Duration.Duration> => {
435
+ const diffMillis = distance(self, other)
436
+ return diffMillis > 0
437
+ ? Either.right(Duration.millis(diffMillis))
438
+ : Either.left(Duration.millis(-diffMillis))
439
+ }
440
+ )
441
+
442
+ /** @internal */
443
+ export const distanceDuration: {
444
+ (other: DateTime.DateTime): (self: DateTime.DateTime) => Duration.Duration
445
+ (self: DateTime.DateTime, other: DateTime.DateTime): Duration.Duration
446
+ } = dual(
447
+ 2,
448
+ (self: DateTime.DateTime, other: DateTime.DateTime): Duration.Duration =>
449
+ Duration.millis(Math.abs(distance(self, other)))
450
+ )
451
+
452
+ /** @internal */
453
+ export const min: {
454
+ (that: DateTime.DateTime): (self: DateTime.DateTime) => DateTime.DateTime
455
+ (self: DateTime.DateTime, that: DateTime.DateTime): DateTime.DateTime
456
+ } = order.min(Order)
457
+
458
+ /** @internal */
459
+ export const max: {
460
+ (that: DateTime.DateTime): (self: DateTime.DateTime) => DateTime.DateTime
461
+ (self: DateTime.DateTime, that: DateTime.DateTime): DateTime.DateTime
462
+ } = order.max(Order)
463
+
464
+ /** @internal */
465
+ export const greaterThan: {
466
+ (that: DateTime.DateTime): (self: DateTime.DateTime) => boolean
467
+ (self: DateTime.DateTime, that: DateTime.DateTime): boolean
468
+ } = order.greaterThan(Order)
469
+
470
+ /** @internal */
471
+ export const greaterThanOrEqualTo: {
472
+ (that: DateTime.DateTime): (self: DateTime.DateTime) => boolean
473
+ (self: DateTime.DateTime, that: DateTime.DateTime): boolean
474
+ } = order.greaterThanOrEqualTo(Order)
475
+
476
+ /** @internal */
477
+ export const lessThan: {
478
+ (that: DateTime.DateTime): (self: DateTime.DateTime) => boolean
479
+ (self: DateTime.DateTime, that: DateTime.DateTime): boolean
480
+ } = order.lessThan(Order)
481
+
482
+ /** @internal */
483
+ export const lessThanOrEqualTo: {
484
+ (that: DateTime.DateTime): (self: DateTime.DateTime) => boolean
485
+ (self: DateTime.DateTime, that: DateTime.DateTime): boolean
486
+ } = order.lessThanOrEqualTo(Order)
487
+
488
+ /** @internal */
489
+ export const between: {
490
+ (options: { minimum: DateTime.DateTime; maximum: DateTime.DateTime }): (self: DateTime.DateTime) => boolean
491
+ (self: DateTime.DateTime, options: { minimum: DateTime.DateTime; maximum: DateTime.DateTime }): boolean
492
+ } = order.between(Order)
493
+
494
+ /** @internal */
495
+ export const isFuture = (self: DateTime.DateTime): Effect.Effect<boolean> => core.map(now, lessThan(self))
496
+
497
+ /** @internal */
498
+ export const unsafeIsFuture = (self: DateTime.DateTime): boolean => lessThan(unsafeNow(), self)
499
+
500
+ /** @internal */
501
+ export const isPast = (self: DateTime.DateTime): Effect.Effect<boolean> => core.map(now, greaterThan(self))
502
+
503
+ /** @internal */
504
+ export const unsafeIsPast = (self: DateTime.DateTime): boolean => greaterThan(unsafeNow(), self)
505
+
506
+ // =============================================================================
507
+ // conversions
508
+ // =============================================================================
509
+
510
+ /** @internal */
511
+ export const toDateUtc = (self: DateTime.DateTime): Date => new Date(self.epochMillis)
512
+
513
+ /** @internal */
514
+ export const toDate = (self: DateTime.DateTime): Date => {
515
+ if (self._tag === "Utc") {
516
+ return new Date(self.epochMillis)
517
+ } else if (self.zone._tag === "Offset") {
518
+ return new Date(self.epochMillis + self.zone.offset)
519
+ } else if (self.adjustedEpochMillis !== undefined) {
520
+ return new Date(self.adjustedEpochMillis)
521
+ }
522
+ const parts = self.zone.format.formatToParts(self.epochMillis).filter((_) => _.type !== "literal")
523
+ const date = new Date(0)
524
+ date.setUTCFullYear(
525
+ Number(parts[2].value),
526
+ Number(parts[0].value) - 1,
527
+ Number(parts[1].value)
528
+ )
529
+ date.setUTCHours(
530
+ Number(parts[3].value),
531
+ Number(parts[4].value),
532
+ Number(parts[5].value),
533
+ Number(parts[6].value)
534
+ )
535
+ self.adjustedEpochMillis = date.getTime()
536
+ return date
537
+ }
538
+
539
+ /** @internal */
540
+ export const zonedOffset = (self: DateTime.Zoned): number => {
541
+ const date = toDate(self)
542
+ return date.getTime() - toEpochMillis(self)
543
+ }
544
+
545
+ const offsetToString = (offset: number): string => {
546
+ const abs = Math.abs(offset)
547
+ const hours = Math.floor(abs / (60 * 60 * 1000))
548
+ const minutes = Math.round((abs % (60 * 60 * 1000)) / (60 * 1000))
549
+ return `${offset < 0 ? "-" : "+"}${String(hours).padStart(2, "0")}:${String(minutes).padStart(2, "0")}`
550
+ }
551
+
552
+ /** @internal */
553
+ export const zonedOffsetIso = (self: DateTime.Zoned): string => offsetToString(zonedOffset(self))
554
+
555
+ /** @internal */
556
+ export const toEpochMillis = (self: DateTime.DateTime): number => self.epochMillis
557
+
558
+ /** @internal */
559
+ export const removeTime = (self: DateTime.DateTime): DateTime.Utc =>
560
+ withDate(self, (date) => {
561
+ date.setUTCHours(0, 0, 0, 0)
562
+ return makeUtc(date.getTime())
563
+ })
564
+
565
+ // =============================================================================
566
+ // parts
567
+ // =============================================================================
568
+
569
+ const dateToParts = (date: Date): DateTime.DateTime.PartsWithWeekday => ({
570
+ millis: date.getUTCMilliseconds(),
571
+ seconds: date.getUTCSeconds(),
572
+ minutes: date.getUTCMinutes(),
573
+ hours: date.getUTCHours(),
574
+ day: date.getUTCDate(),
575
+ weekDay: date.getUTCDay(),
576
+ month: date.getUTCMonth() + 1,
577
+ year: date.getUTCFullYear()
578
+ })
579
+
580
+ /** @internal */
581
+ export const toParts = (self: DateTime.DateTime): DateTime.DateTime.PartsWithWeekday => {
582
+ if (self._tag === "Utc") {
583
+ return toPartsUtc(self)
584
+ } else if (self.partsAdjusted !== undefined) {
585
+ return self.partsAdjusted
586
+ }
587
+ self.partsAdjusted = withDate(self, dateToParts)
588
+ return self.partsAdjusted
589
+ }
590
+
591
+ /** @internal */
592
+ export const toPartsUtc = (self: DateTime.DateTime): DateTime.DateTime.PartsWithWeekday => {
593
+ if (self.partsUtc !== undefined) {
594
+ return self.partsUtc
595
+ }
596
+ self.partsUtc = withDateUtc(self, dateToParts)
597
+ return self.partsUtc
598
+ }
599
+
600
+ /** @internal */
601
+ export const getPartUtc: {
602
+ (part: keyof DateTime.DateTime.PartsWithWeekday): (self: DateTime.DateTime) => number
603
+ (self: DateTime.DateTime, part: keyof DateTime.DateTime.PartsWithWeekday): number
604
+ } = dual(2, (self: DateTime.DateTime, part: keyof DateTime.DateTime.PartsWithWeekday): number => toPartsUtc(self)[part])
605
+
606
+ /** @internal */
607
+ export const getPart: {
608
+ (part: keyof DateTime.DateTime.PartsWithWeekday): (self: DateTime.DateTime) => number
609
+ (self: DateTime.DateTime, part: keyof DateTime.DateTime.PartsWithWeekday): number
610
+ } = dual(2, (self: DateTime.DateTime, part: keyof DateTime.DateTime.PartsWithWeekday): number => toParts(self)[part])
611
+
612
+ const setPartsDate = (date: Date, parts: Partial<DateTime.DateTime.PartsWithWeekday>): void => {
613
+ if (parts.year !== undefined) {
614
+ date.setUTCFullYear(parts.year)
615
+ }
616
+ if (parts.month !== undefined) {
617
+ date.setUTCMonth(parts.month - 1)
618
+ }
619
+ if (parts.day !== undefined) {
620
+ date.setUTCDate(parts.day)
621
+ }
622
+ if (parts.weekDay !== undefined) {
623
+ const diff = parts.weekDay - date.getUTCDay()
624
+ date.setUTCDate(date.getUTCDate() + diff)
625
+ }
626
+ if (parts.hours !== undefined) {
627
+ date.setUTCHours(parts.hours)
628
+ }
629
+ if (parts.minutes !== undefined) {
630
+ date.setUTCMinutes(parts.minutes)
631
+ }
632
+ if (parts.seconds !== undefined) {
633
+ date.setUTCSeconds(parts.seconds)
634
+ }
635
+ if (parts.millis !== undefined) {
636
+ date.setUTCMilliseconds(parts.millis)
637
+ }
638
+ }
639
+
640
+ /** @internal */
641
+ export const setParts: {
642
+ (
643
+ parts: Partial<DateTime.DateTime.PartsWithWeekday>
644
+ ): <A extends DateTime.DateTime>(self: A) => DateTime.DateTime.PreserveZone<A>
645
+ <A extends DateTime.DateTime>(
646
+ self: A,
647
+ parts: Partial<DateTime.DateTime.PartsWithWeekday>
648
+ ): DateTime.DateTime.PreserveZone<A>
649
+ } = dual(
650
+ 2,
651
+ (self: DateTime.DateTime, parts: Partial<DateTime.DateTime.PartsWithWeekday>): DateTime.DateTime =>
652
+ mutate(self, (date) => setPartsDate(date, parts))
653
+ )
654
+
655
+ /** @internal */
656
+ export const setPartsUtc: {
657
+ (
658
+ parts: Partial<DateTime.DateTime.PartsWithWeekday>
659
+ ): <A extends DateTime.DateTime>(self: A) => DateTime.DateTime.PreserveZone<A>
660
+ <A extends DateTime.DateTime>(
661
+ self: A,
662
+ parts: Partial<DateTime.DateTime.PartsWithWeekday>
663
+ ): DateTime.DateTime.PreserveZone<A>
664
+ } = dual(
665
+ 2,
666
+ (self: DateTime.DateTime, parts: Partial<DateTime.DateTime.PartsWithWeekday>): DateTime.DateTime =>
667
+ mutateUtc(self, (date) => setPartsDate(date, parts))
668
+ )
669
+
670
+ // =============================================================================
671
+ // mapping
672
+ // =============================================================================
673
+
674
+ const makeZonedFromAdjusted = (adjustedMillis: number, zone: DateTime.TimeZone): DateTime.Zoned => {
675
+ const offset = zone._tag === "Offset" ? zone.offset : calculateNamedOffset(adjustedMillis, zone)
676
+ return makeZonedProto(adjustedMillis - offset, zone)
677
+ }
678
+
679
+ const offsetRegex = /([+-])(\d{2}):(\d{2})$/
680
+ const parseOffset = (offset: string): number | null => {
681
+ const match = offsetRegex.exec(offset)
682
+ if (match === null) {
683
+ return null
684
+ }
685
+ const [, sign, hours, minutes] = match
686
+ return (sign === "+" ? 1 : -1) * (Number(hours) * 60 + Number(minutes)) * 60 * 1000
687
+ }
688
+
689
+ const calculateNamedOffset = (adjustedMillis: number, zone: DateTime.TimeZone.Named): number => {
690
+ const offset = zone.format.formatToParts(adjustedMillis).find((_) => _.type === "timeZoneName")?.value ?? ""
691
+ if (offset === "GMT") {
692
+ return 0
693
+ }
694
+ const result = parseOffset(offset)
695
+ if (result === null) {
696
+ // fallback to using the adjusted date
697
+ return zonedOffset(makeZonedProto(adjustedMillis, zone))
698
+ }
699
+ return result
700
+ }
701
+
702
+ /** @internal */
703
+ export const mutate: {
704
+ (f: (date: Date) => void): <A extends DateTime.DateTime>(self: A) => DateTime.DateTime.PreserveZone<A>
705
+ <A extends DateTime.DateTime>(self: A, f: (date: Date) => void): DateTime.DateTime.PreserveZone<A>
706
+ } = dual(2, (self: DateTime.DateTime, f: (date: Date) => void): DateTime.DateTime => {
707
+ if (self._tag === "Utc") {
708
+ const date = toDateUtc(self)
709
+ f(date)
710
+ return makeUtc(date.getTime())
711
+ }
712
+ const adjustedDate = toDate(self)
713
+ const newAdjustedDate = new Date(adjustedDate.getTime())
714
+ f(newAdjustedDate)
715
+ return makeZonedFromAdjusted(newAdjustedDate.getTime(), self.zone)
716
+ })
717
+
718
+ /** @internal */
719
+ export const mutateUtc: {
720
+ (f: (date: Date) => void): <A extends DateTime.DateTime>(self: A) => DateTime.DateTime.PreserveZone<A>
721
+ <A extends DateTime.DateTime>(self: A, f: (date: Date) => void): DateTime.DateTime.PreserveZone<A>
722
+ } = dual(2, (self: DateTime.DateTime, f: (date: Date) => void): DateTime.DateTime =>
723
+ mapEpochMillis(self, (millis) => {
724
+ const date = new Date(millis)
725
+ f(date)
726
+ return date.getTime()
727
+ }))
728
+
729
+ /** @internal */
730
+ export const mapEpochMillis: {
731
+ (f: (millis: number) => number): <A extends DateTime.DateTime>(self: A) => DateTime.DateTime.PreserveZone<A>
732
+ <A extends DateTime.DateTime>(self: A, f: (millis: number) => number): DateTime.DateTime.PreserveZone<A>
733
+ } = dual(2, (self: DateTime.DateTime, f: (millis: number) => number): DateTime.DateTime => {
734
+ const millis = f(toEpochMillis(self))
735
+ return self._tag === "Utc" ? makeUtc(millis) : makeZonedProto(millis, self.zone)
736
+ })
737
+
738
+ /** @internal */
739
+ export const withDate: {
740
+ <A>(f: (date: Date) => A): (self: DateTime.DateTime) => A
741
+ <A>(self: DateTime.DateTime, f: (date: Date) => A): A
742
+ } = dual(2, <A>(self: DateTime.DateTime, f: (date: Date) => A): A => f(toDate(self)))
743
+
744
+ /** @internal */
745
+ export const withDateUtc: {
746
+ <A>(f: (date: Date) => A): (self: DateTime.DateTime) => A
747
+ <A>(self: DateTime.DateTime, f: (date: Date) => A): A
748
+ } = dual(2, <A>(self: DateTime.DateTime, f: (date: Date) => A): A => f(toDateUtc(self)))
749
+
750
+ /** @internal */
751
+ export const match: {
752
+ <A, B>(options: {
753
+ readonly onUtc: (_: DateTime.Utc) => A
754
+ readonly onZoned: (_: DateTime.Zoned) => B
755
+ }): (self: DateTime.DateTime) => A | B
756
+ <A, B>(self: DateTime.DateTime, options: {
757
+ readonly onUtc: (_: DateTime.Utc) => A
758
+ readonly onZoned: (_: DateTime.Zoned) => B
759
+ }): A | B
760
+ } = dual(2, <A, B>(self: DateTime.DateTime, options: {
761
+ readonly onUtc: (_: DateTime.Utc) => A
762
+ readonly onZoned: (_: DateTime.Zoned) => B
763
+ }): A | B => self._tag === "Utc" ? options.onUtc(self) : options.onZoned(self))
764
+
765
+ // =============================================================================
766
+ // math
767
+ // =============================================================================
768
+
769
+ /** @internal */
770
+ export const addDuration: {
771
+ (duration: Duration.DurationInput): <A extends DateTime.DateTime>(self: A) => DateTime.DateTime.PreserveZone<A>
772
+ <A extends DateTime.DateTime>(self: A, duration: Duration.DurationInput): DateTime.DateTime.PreserveZone<A>
773
+ } = dual(
774
+ 2,
775
+ (self: DateTime.DateTime, duration: Duration.DurationInput): DateTime.DateTime =>
776
+ mapEpochMillis(self, (millis) => millis + Duration.toMillis(duration))
777
+ )
778
+
779
+ /** @internal */
780
+ export const subtractDuration: {
781
+ (duration: Duration.DurationInput): <A extends DateTime.DateTime>(self: A) => DateTime.DateTime.PreserveZone<A>
782
+ <A extends DateTime.DateTime>(self: A, duration: Duration.DurationInput): DateTime.DateTime.PreserveZone<A>
783
+ } = dual(
784
+ 2,
785
+ (self: DateTime.DateTime, duration: Duration.DurationInput): DateTime.DateTime =>
786
+ mapEpochMillis(self, (millis) => millis - Duration.toMillis(duration))
787
+ )
788
+
789
+ const addMillis = (date: Date, amount: number): void => {
790
+ date.setTime(date.getTime() + amount)
791
+ }
792
+
793
+ /** @internal */
794
+ export const add: {
795
+ (
796
+ parts: Partial<DateTime.DateTime.PartsForMath>
797
+ ): <A extends DateTime.DateTime>(self: A) => DateTime.DateTime.PreserveZone<A>
798
+ <A extends DateTime.DateTime>(
799
+ self: A,
800
+ parts: Partial<DateTime.DateTime.PartsForMath>
801
+ ): DateTime.DateTime.PreserveZone<A>
802
+ } = dual(
803
+ 2,
804
+ (self: DateTime.DateTime, parts: Partial<DateTime.DateTime.PartsForMath>): DateTime.DateTime =>
805
+ mutate(self, (date) => {
806
+ if (parts.millis) {
807
+ addMillis(date, parts.millis)
808
+ }
809
+ if (parts.seconds) {
810
+ addMillis(date, parts.seconds * 1000)
811
+ }
812
+ if (parts.minutes) {
813
+ addMillis(date, parts.minutes * 60 * 1000)
814
+ }
815
+ if (parts.hours) {
816
+ addMillis(date, parts.hours * 60 * 60 * 1000)
817
+ }
818
+ if (parts.days) {
819
+ date.setUTCDate(date.getUTCDate() + parts.days)
820
+ }
821
+ if (parts.weeks) {
822
+ date.setUTCDate(date.getUTCDate() + parts.weeks * 7)
823
+ }
824
+ if (parts.months) {
825
+ const day = date.getUTCDate()
826
+ date.setUTCMonth(date.getUTCMonth() + parts.months + 1, 0)
827
+ if (day < date.getUTCDate()) {
828
+ date.setUTCDate(day)
829
+ }
830
+ }
831
+ if (parts.years) {
832
+ const day = date.getUTCDate()
833
+ const month = date.getUTCMonth()
834
+ date.setUTCFullYear(
835
+ date.getUTCFullYear() + parts.years,
836
+ month + 1,
837
+ 0
838
+ )
839
+ if (day < date.getUTCDate()) {
840
+ date.setUTCDate(day)
841
+ }
842
+ }
843
+ })
844
+ )
845
+
846
+ /** @internal */
847
+ export const subtract: {
848
+ (
849
+ parts: Partial<DateTime.DateTime.PartsForMath>
850
+ ): <A extends DateTime.DateTime>(self: A) => DateTime.DateTime.PreserveZone<A>
851
+ <A extends DateTime.DateTime>(
852
+ self: A,
853
+ parts: Partial<DateTime.DateTime.PartsForMath>
854
+ ): DateTime.DateTime.PreserveZone<A>
855
+ } = dual(2, (self: DateTime.DateTime, parts: Partial<DateTime.DateTime.PartsForMath>): DateTime.DateTime => {
856
+ const newParts = {} as Partial<Mutable<DateTime.DateTime.PartsForMath>>
857
+ for (const key in parts) {
858
+ newParts[key as keyof DateTime.DateTime.PartsForMath] = -1 * parts[key as keyof DateTime.DateTime.PartsForMath]!
859
+ }
860
+ return add(self, newParts)
861
+ })
862
+
863
+ const startOfDate = (date: Date, part: DateTime.DateTime.UnitSingular, options?: {
864
+ readonly weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined
865
+ }) => {
866
+ switch (part) {
867
+ case "second": {
868
+ date.setUTCMilliseconds(0)
869
+ break
870
+ }
871
+ case "minute": {
872
+ date.setUTCSeconds(0, 0)
873
+ break
874
+ }
875
+ case "hour": {
876
+ date.setUTCMinutes(0, 0, 0)
877
+ break
878
+ }
879
+ case "day": {
880
+ date.setUTCHours(0, 0, 0, 0)
881
+ break
882
+ }
883
+ case "week": {
884
+ const weekStartsOn = options?.weekStartsOn ?? 0
885
+ const day = date.getUTCDay()
886
+ const diff = (day - weekStartsOn + 7) % 7
887
+ date.setUTCDate(date.getUTCDate() - diff)
888
+ date.setUTCHours(0, 0, 0, 0)
889
+ break
890
+ }
891
+ case "month": {
892
+ date.setUTCDate(1)
893
+ date.setUTCHours(0, 0, 0, 0)
894
+ break
895
+ }
896
+ case "year": {
897
+ date.setUTCMonth(0, 1)
898
+ date.setUTCHours(0, 0, 0, 0)
899
+ break
900
+ }
901
+ }
902
+ }
903
+
904
+ /** @internal */
905
+ export const startOf: {
906
+ (part: DateTime.DateTime.UnitSingular, options?: {
907
+ readonly weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined
908
+ }): <A extends DateTime.DateTime>(self: A) => DateTime.DateTime.PreserveZone<A>
909
+ <A extends DateTime.DateTime>(self: A, part: DateTime.DateTime.UnitSingular, options?: {
910
+ readonly weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined
911
+ }): DateTime.DateTime.PreserveZone<A>
912
+ } = dual(isDateTimeArgs, (self: DateTime.DateTime, part: DateTime.DateTime.UnitSingular, options?: {
913
+ readonly weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined
914
+ }): DateTime.DateTime => mutate(self, (date) => startOfDate(date, part, options)))
915
+
916
+ const endOfDate = (date: Date, part: DateTime.DateTime.UnitSingular, options?: {
917
+ readonly weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined
918
+ }) => {
919
+ switch (part) {
920
+ case "second": {
921
+ date.setUTCMilliseconds(999)
922
+ break
923
+ }
924
+ case "minute": {
925
+ date.setUTCSeconds(59, 999)
926
+ break
927
+ }
928
+ case "hour": {
929
+ date.setUTCMinutes(59, 59, 999)
930
+ break
931
+ }
932
+ case "day": {
933
+ date.setUTCHours(23, 59, 59, 999)
934
+ break
935
+ }
936
+ case "week": {
937
+ const weekStartsOn = options?.weekStartsOn ?? 0
938
+ const day = date.getUTCDay()
939
+ const diff = (day - weekStartsOn + 7) % 7
940
+ date.setUTCDate(date.getUTCDate() - diff + 6)
941
+ date.setUTCHours(23, 59, 59, 999)
942
+ break
943
+ }
944
+ case "month": {
945
+ date.setUTCMonth(date.getUTCMonth() + 1, 0)
946
+ date.setUTCHours(23, 59, 59, 999)
947
+ break
948
+ }
949
+ case "year": {
950
+ date.setUTCMonth(11, 31)
951
+ date.setUTCHours(23, 59, 59, 999)
952
+ break
953
+ }
954
+ }
955
+ }
956
+
957
+ /** @internal */
958
+ export const endOf: {
959
+ (part: DateTime.DateTime.UnitSingular, options?: {
960
+ readonly weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined
961
+ }): <A extends DateTime.DateTime>(self: A) => DateTime.DateTime.PreserveZone<A>
962
+ <A extends DateTime.DateTime>(self: A, part: DateTime.DateTime.UnitSingular, options?: {
963
+ readonly weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined
964
+ }): DateTime.DateTime.PreserveZone<A>
965
+ } = dual(isDateTimeArgs, (self: DateTime.DateTime, part: DateTime.DateTime.UnitSingular, options?: {
966
+ readonly weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined
967
+ }): DateTime.DateTime => mutate(self, (date) => endOfDate(date, part, options)))
968
+
969
+ /** @internal */
970
+ export const nearest: {
971
+ (part: DateTime.DateTime.UnitSingular, options?: {
972
+ readonly weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined
973
+ }): <A extends DateTime.DateTime>(self: A) => DateTime.DateTime.PreserveZone<A>
974
+ <A extends DateTime.DateTime>(self: A, part: DateTime.DateTime.UnitSingular, options?: {
975
+ readonly weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined
976
+ }): DateTime.DateTime.PreserveZone<A>
977
+ } = dual(isDateTimeArgs, (self: DateTime.DateTime, part: DateTime.DateTime.UnitSingular, options?: {
978
+ readonly weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined
979
+ }): DateTime.DateTime =>
980
+ mutate(self, (date) => {
981
+ if (part === "milli") return
982
+ const millis = date.getTime()
983
+ const start = new Date(millis)
984
+ startOfDate(start, part, options)
985
+ const startMillis = start.getTime()
986
+ const end = new Date(millis)
987
+ endOfDate(end, part, options)
988
+ const endMillis = end.getTime() + 1
989
+ const diffStart = millis - startMillis
990
+ const diffEnd = endMillis - millis
991
+ if (diffStart < diffEnd) {
992
+ date.setTime(startMillis)
993
+ } else {
994
+ date.setTime(endMillis)
995
+ }
996
+ }))
997
+
998
+ // =============================================================================
999
+ // formatting
1000
+ // =============================================================================
1001
+
1002
+ const intlTimeZone = (self: DateTime.TimeZone): string => {
1003
+ if (self._tag === "Named") {
1004
+ return self.id
1005
+ }
1006
+ return offsetToString(self.offset)
1007
+ }
1008
+
1009
+ /** @internal */
1010
+ export const format: {
1011
+ (
1012
+ options?:
1013
+ | Intl.DateTimeFormatOptions & {
1014
+ readonly locale?: string | undefined
1015
+ }
1016
+ | undefined
1017
+ ): (self: DateTime.DateTime) => string
1018
+ (
1019
+ self: DateTime.DateTime,
1020
+ options?:
1021
+ | Intl.DateTimeFormatOptions & {
1022
+ readonly locale?: string | undefined
1023
+ }
1024
+ | undefined
1025
+ ): string
1026
+ } = dual(isDateTimeArgs, (
1027
+ self: DateTime.DateTime,
1028
+ options?:
1029
+ | Intl.DateTimeFormatOptions & {
1030
+ readonly locale?: string | undefined
1031
+ }
1032
+ | undefined
1033
+ ): string => {
1034
+ try {
1035
+ return new Intl.DateTimeFormat(options?.locale, {
1036
+ timeZone: self._tag === "Utc" ? "UTC" : intlTimeZone(self.zone),
1037
+ ...options
1038
+ }).format(self.epochMillis)
1039
+ } catch (_) {
1040
+ return new Intl.DateTimeFormat(options?.locale, {
1041
+ timeZone: "UTC",
1042
+ ...options
1043
+ }).format(toDate(self))
1044
+ }
1045
+ })
1046
+
1047
+ /** @internal */
1048
+ export const formatLocal: {
1049
+ (
1050
+ options?:
1051
+ | Intl.DateTimeFormatOptions & {
1052
+ readonly locale?: string | undefined
1053
+ }
1054
+ | undefined
1055
+ ): (self: DateTime.DateTime) => string
1056
+ (
1057
+ self: DateTime.DateTime,
1058
+ options?:
1059
+ | Intl.DateTimeFormatOptions & {
1060
+ readonly locale?: string | undefined
1061
+ }
1062
+ | undefined
1063
+ ): string
1064
+ } = dual(isDateTimeArgs, (
1065
+ self: DateTime.DateTime,
1066
+ options?:
1067
+ | Intl.DateTimeFormatOptions & {
1068
+ readonly locale?: string | undefined
1069
+ }
1070
+ | undefined
1071
+ ): string => new Intl.DateTimeFormat(options?.locale, options).format(self.epochMillis))
1072
+
1073
+ /** @internal */
1074
+ export const formatUtc: {
1075
+ (
1076
+ options?:
1077
+ | Intl.DateTimeFormatOptions & {
1078
+ readonly locale?: string | undefined
1079
+ }
1080
+ | undefined
1081
+ ): (self: DateTime.DateTime) => string
1082
+ (
1083
+ self: DateTime.DateTime,
1084
+ options?:
1085
+ | Intl.DateTimeFormatOptions & {
1086
+ readonly locale?: string | undefined
1087
+ }
1088
+ | undefined
1089
+ ): string
1090
+ } = dual(isDateTimeArgs, (
1091
+ self: DateTime.DateTime,
1092
+ options?:
1093
+ | Intl.DateTimeFormatOptions & {
1094
+ readonly locale?: string | undefined
1095
+ }
1096
+ | undefined
1097
+ ): string =>
1098
+ new Intl.DateTimeFormat(options?.locale, {
1099
+ ...options,
1100
+ timeZone: "UTC"
1101
+ }).format(self.epochMillis))
1102
+
1103
+ /** @internal */
1104
+ export const formatIntl: {
1105
+ (format: Intl.DateTimeFormat): (self: DateTime.DateTime) => string
1106
+ (self: DateTime.DateTime, format: Intl.DateTimeFormat): string
1107
+ } = dual(2, (self: DateTime.DateTime, format: Intl.DateTimeFormat): string => format.format(self.epochMillis))
1108
+
1109
+ /** @internal */
1110
+ export const formatIso = (self: DateTime.DateTime): string => toDateUtc(self).toISOString()
1111
+
1112
+ /** @internal */
1113
+ export const formatIsoDate = (self: DateTime.DateTime): string => toDate(self).toISOString().slice(0, 10)
1114
+
1115
+ /** @internal */
1116
+ export const formatIsoDateUtc = (self: DateTime.DateTime): string => toDateUtc(self).toISOString().slice(0, 10)
1117
+
1118
+ /** @internal */
1119
+ export const formatIsoOffset = (self: DateTime.DateTime): string => {
1120
+ const date = toDate(self)
1121
+ return self._tag === "Utc" ? date.toISOString() : `${date.toISOString().slice(0, -1)}${zonedOffsetIso(self)}`
1122
+ }
1123
+
1124
+ /** @internal */
1125
+ export const formatIsoZoned = (self: DateTime.Zoned): string =>
1126
+ self.zone._tag === "Offset" ? formatIsoOffset(self) : `${formatIsoOffset(self)}[${self.zone.id}]`