@parischap/conversions 0.2.0 → 0.4.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.
- package/README.md +8 -1258
- package/package.json +12 -3
package/README.md
CHANGED
@@ -2,9 +2,9 @@
|
|
2
2
|
|
3
3
|
# conversions
|
4
4
|
|
5
|
-
An [`Effect`](https://effect.website/docs/introduction) library to partially replace the native javascript INTL
|
5
|
+
An [`Effect`](https://effect.website/docs/introduction) library to partially replace the native javascript INTL API.
|
6
6
|
|
7
|
-
Non machine-dependent, safe, bidirectional (implements parsing and formatting), tested
|
7
|
+
Non machine-dependent, safe, bidirectional (implements parsing and formatting), tested, documented, with lots of examples, 100% Typescript, 100% functional.
|
8
8
|
|
9
9
|
Can also come in handy to non-`Effect` users.
|
10
10
|
|
@@ -45,1261 +45,11 @@ After reading this introduction, you may take a look at the [API](https://parisc
|
|
45
45
|
|
46
46
|
This package contains:
|
47
47
|
|
48
|
-
- a [module to round numbers and
|
49
|
-
- a safe, easy-to-use [number
|
50
|
-
- an equivalent to the PHP [sprintf and sscanf functions](
|
51
|
-
- a very easy to use [
|
52
|
-
- a [
|
53
|
-
- a few [brands](
|
48
|
+
- a [module to round numbers and BigDecimal's](./readme-assets/Rounding.md) with the same rounding options as those offered by the javascript INTL namespace: Ceil, Floor, Expand, Trunc, HalfCeil...
|
49
|
+
- a safe, easy-to-use [number/BigDecimal parser/formatter](./readme-assets/NumberParserFormatter.md) with almost all the options offered by the javascript INTL namespace: choice of the thousand separator, of the fractional separator, of the minimum and maximum number of fractional digits, of the rounding mode, of the sign display mode, of whether to show or not the integer part when it's zero, of whether to use a scientific or engineering notation, of the character to use as exponent mark... It can also be used as a `Schema` instead of the `Effect.Schema.NumberFromString` transformer.
|
50
|
+
- an equivalent to the PHP [sprintf and sscanf functions](./readme-assets/Templating.md) with real typing of the placeholders. Although `Effect.Schema` does offer the [`TemplateLiteralParser` API](https://effect.website/docs/schema/basic-usage/#templateliteralparser), the latter does not provide a solution to situations such as fixed length fields (potentially padded), numbers formatted otherwise than in the English format... This module can also be used as a `Schema`.
|
51
|
+
- a very easy to use [DateTime module](./readme-assets/DateTime.md) that implements natively the Iso calendar (Iso year and Iso week). It is also faster than its `Effect` counterpart as it implements an internal state that's only used to speed up calculation times (but does not alter the result of functions; so `CVDateTime` functions can be viewed as pure from a user's perspective). It can therefore be useful in applications where time is of essence.
|
52
|
+
- a [DateTime parser/formatter](./readme-assets/DateTimeFormatter.md) which supports many of the available [unicode tokens](https://www.unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table). It can also be used as a `Schema` instead of the `Effect.Schema.Date` transformer.
|
53
|
+
- a few [brands](./readme-assets/Branding.md) which come in handy in many projects such as email, semantic versioning, integer numbers, positive integer numbers, real numbers and positive real numbers. All these brands are also defined as `Schemas`. Please read the [`Effect` documentation about Branding](https://effect.website/docs/code-style/branded-types/) if you are not familiar with this concept
|
54
54
|
|
55
55
|
Most functions of this package return an `Either` or an `Option` to signify the possibility of an error. However, if you are not an `Effect` user and do not care to learn more about it, you can simply use the `OrThrow` variant of the function. For instance, use `CVDateTime.setWeekdayOrThrow` instead of `CVDateTime.setWeekday`. As its name suggests, it will throw in case failure. Some functions return functions that return an `Either` or throw. In that case, the variant for non-`Effect` users contains the word `Throwing`, e.g. use `CVDateTimeFormat.toThrowingFormatter` instead of `CVDateTimeFormat.toFormatter`.
|
56
|
-
|
57
|
-
### <a name="RoundingModule"></a>A) Rounding module
|
58
|
-
|
59
|
-
#### 1. Usage example
|
60
|
-
|
61
|
-
```ts
|
62
|
-
import { CVRoundingMode, CVRoundingOption } from "@parischap/conversions";
|
63
|
-
import { BigDecimal } from "effect";
|
64
|
-
|
65
|
-
// Here we define our rounding options:
|
66
|
-
// the result must have three fractional digits using the HalfEven rounding mode
|
67
|
-
const roundingOption = CVRoundingOption.make({
|
68
|
-
precision: 3,
|
69
|
-
roundingMode: CVRoundingMode.Type.HalfEven,
|
70
|
-
});
|
71
|
-
|
72
|
-
// Let's define a number rounder from our options. Type: (value:number) => number
|
73
|
-
const numberRounder = CVRoundingOption.toNumberRounder(roundingOption);
|
74
|
-
// Let's define a BigDecimal rounder from our options. Type: (value:BigDecimal) => BigDecimal
|
75
|
-
const bigDecimalRounder = CVRoundingOption.toBigDecimalRounder(roundingOption);
|
76
|
-
|
77
|
-
/** Positive numbers with even last significant digit */
|
78
|
-
// Result: 12.457
|
79
|
-
console.log(numberRounder(12.4566));
|
80
|
-
|
81
|
-
// Result: 12.456
|
82
|
-
console.log(numberRounder(12.4565));
|
83
|
-
|
84
|
-
// Result: 12.456
|
85
|
-
console.log(numberRounder(12.4564));
|
86
|
-
|
87
|
-
/** Positive numbers with odd last significant digit */
|
88
|
-
// Result: 12.458
|
89
|
-
console.log(numberRounder(12.4576));
|
90
|
-
|
91
|
-
// Result: 12.458
|
92
|
-
console.log(numberRounder(12.4575));
|
93
|
-
|
94
|
-
// Result: 12.457
|
95
|
-
console.log(numberRounder(12.4574));
|
96
|
-
|
97
|
-
/** Negative numbers with even last significant digit */
|
98
|
-
// Result: -12.457
|
99
|
-
console.log(numberRounder(-12.4566));
|
100
|
-
|
101
|
-
// Result: -12.456
|
102
|
-
console.log(numberRounder(-12.4565));
|
103
|
-
|
104
|
-
// Result: -12.456
|
105
|
-
console.log(numberRounder(-12.4564));
|
106
|
-
|
107
|
-
/** Negative numbers with odd last significant digit */
|
108
|
-
// Result: -12.458
|
109
|
-
console.log(numberRounder(-12.4576));
|
110
|
-
|
111
|
-
// Result: -12.458
|
112
|
-
console.log(numberRounder(-12.4575));
|
113
|
-
|
114
|
-
// Result: -12.457
|
115
|
-
console.log(numberRounder(-12.4574));
|
116
|
-
|
117
|
-
// Result: -12.450000000000001 (javascript number loss of accuracy)
|
118
|
-
console.log(numberRounder(-12.45));
|
119
|
-
|
120
|
-
/** Diverse BigDecimal numbers */
|
121
|
-
// Result: BigDecimal.make(12457n, 3)
|
122
|
-
console.log(bigDecimalRounder(BigDecimal.make(124566n, 4)));
|
123
|
-
|
124
|
-
// Result: BigDecimal.make(-12456n, 3)
|
125
|
-
console.log(bigDecimalRounder(BigDecimal.make(-124565n, 4)));
|
126
|
-
|
127
|
-
// Result: BigDecimal.make(12450n, 3)
|
128
|
-
console.log(bigDecimalRounder(BigDecimal.make(1245n, 2)));
|
129
|
-
```
|
130
|
-
|
131
|
-
### 2. Available rounding modes
|
132
|
-
|
133
|
-
The available rounding modes are defined in module RoundingMode.ts:
|
134
|
-
|
135
|
-
```ts
|
136
|
-
export enum Type {
|
137
|
-
/** Round toward +∞. Positive values round up. Negative values round "more positive" */
|
138
|
-
Ceil = 0,
|
139
|
-
/** Round toward -∞. Positive values round down. Negative values round "more negative" */
|
140
|
-
Floor = 1,
|
141
|
-
/**
|
142
|
-
* Round away from 0. The magnitude of the value is always increased by rounding. Positive values
|
143
|
-
* round up. Negative values round "more negative"
|
144
|
-
*/
|
145
|
-
Expand = 2,
|
146
|
-
/**
|
147
|
-
* Round toward 0. The magnitude of the value is always reduced by rounding. Positive values round
|
148
|
-
* down. Negative values round "less negative"
|
149
|
-
*/
|
150
|
-
Trunc = 3,
|
151
|
-
/**
|
152
|
-
* Ties toward +∞. Values above the half-increment round like "ceil" (towards +∞), and below like
|
153
|
-
* "floor" (towards -∞). On the half-increment, values round like "ceil"
|
154
|
-
*/
|
155
|
-
HalfCeil = 4,
|
156
|
-
/**
|
157
|
-
* Ties toward -∞. Values above the half-increment round like "ceil" (towards +∞), and below like
|
158
|
-
* "floor" (towards -∞). On the half-increment, values round like "floor"
|
159
|
-
*/
|
160
|
-
HalfFloor = 5,
|
161
|
-
/**
|
162
|
-
* Ties away from 0. Values above the half-increment round like "expand" (away from zero), and
|
163
|
-
* below like "trunc" (towards 0). On the half-increment, values round like "expand"
|
164
|
-
*/
|
165
|
-
HalfExpand = 6,
|
166
|
-
/**
|
167
|
-
* Ties toward 0. Values above the half-increment round like "expand" (away from zero), and below
|
168
|
-
* like "trunc" (towards 0). On the half-increment, values round like "trunc"
|
169
|
-
*/
|
170
|
-
HalfTrunc = 7,
|
171
|
-
/**
|
172
|
-
* Ties towards the nearest even integer. Values above the half-increment round like "expand"
|
173
|
-
* (away from zero), and below like "trunc" (towards 0). On the half-increment values round
|
174
|
-
* towards the nearest even digit
|
175
|
-
*/
|
176
|
-
HalfEven = 8,
|
177
|
-
}
|
178
|
-
```
|
179
|
-
|
180
|
-
#### 3. `CVRoundingOption` instances
|
181
|
-
|
182
|
-
Instead of building your own `CVRoundingOption`, you can use the `halfExpand2` `CVRoundingOption` instance (`HalfExpand` rounding mode with a precision of two fractional digits). It will come in handy in accounting apps of most countries. For example:
|
183
|
-
|
184
|
-
```ts
|
185
|
-
import { CVRoundingOption } from "@parischap/conversions";
|
186
|
-
|
187
|
-
// Let's define a number rounder from halfExpand2. Type: (value:number) => number
|
188
|
-
const numberRounder = CVRoundingOption.toNumberRounder(
|
189
|
-
CVRoundingOption.halfExpand2,
|
190
|
-
);
|
191
|
-
|
192
|
-
/** Positive number */
|
193
|
-
// Result: 12.456
|
194
|
-
console.log(numberRounder(12.46));
|
195
|
-
|
196
|
-
/** Negative number */
|
197
|
-
// Result: -12.46
|
198
|
-
console.log(numberRounder(-12.457));
|
199
|
-
```
|
200
|
-
|
201
|
-
#### 4. Debugging and equality
|
202
|
-
|
203
|
-
`CVRoundingOption` objects implement `Effect` equivalence and equality based on equivalence and equality of the `precision` and `roundingMode` properties. They also implement a `.toString()` method. For instance:
|
204
|
-
|
205
|
-
```ts
|
206
|
-
import { CVRoundingMode, CVRoundingOption } from "@parischap/conversions";
|
207
|
-
import { Equal } from "effect";
|
208
|
-
|
209
|
-
// Result: 'HalfExpandRounderWith2Precision'
|
210
|
-
console.log(CVRoundingOption.halfExpand2);
|
211
|
-
|
212
|
-
const dummyOption1 = CVRoundingOption.make({
|
213
|
-
precision: 3,
|
214
|
-
roundingMode: CVRoundingMode.Type.HalfEven,
|
215
|
-
});
|
216
|
-
|
217
|
-
const dummyOption2 = CVRoundingOption.make({
|
218
|
-
precision: 2,
|
219
|
-
roundingMode: CVRoundingMode.Type.HalfExpand,
|
220
|
-
});
|
221
|
-
|
222
|
-
// Result: false
|
223
|
-
console.log(Equal.equals(CVRoundingOption.halfExpand2, dummyOption1));
|
224
|
-
|
225
|
-
// Result: true
|
226
|
-
console.log(Equal.equals(CVRoundingOption.halfExpand2, dummyOption2));
|
227
|
-
```
|
228
|
-
|
229
|
-
### <a name="NumberParserFormatter"></a>B) Number and `BigDecimal` parser/formatter
|
230
|
-
|
231
|
-
#### 1. Usage example
|
232
|
-
|
233
|
-
```ts
|
234
|
-
import { CVNumberBase10Format, CVReal, CVSchema } from "@parischap/conversions";
|
235
|
-
import { pipe, Schema } from "effect";
|
236
|
-
|
237
|
-
// Let's define some formats
|
238
|
-
const ukStyleUngroupedNumber = CVNumberBase10Format.ukStyleUngroupedNumber;
|
239
|
-
const ukStyleNumberWithEngineeringNotation = pipe(
|
240
|
-
CVNumberBase10Format.ukStyleNumber,
|
241
|
-
CVNumberBase10Format.withEngineeringScientificNotation,
|
242
|
-
);
|
243
|
-
|
244
|
-
const frenchStyleInteger = CVNumberBase10Format.frenchStyleInteger;
|
245
|
-
|
246
|
-
// Let's define a formatter
|
247
|
-
// Type: (value: BigDecimal | CVReal.Type) => string
|
248
|
-
const ukStyleWithEngineeringNotationFormatter =
|
249
|
-
CVNumberBase10Format.toNumberFormatter(ukStyleNumberWithEngineeringNotation);
|
250
|
-
|
251
|
-
// Let's define a parser
|
252
|
-
// Type: (value: string ) => Option.Option<CVReal.Type>
|
253
|
-
const ungroupedUkStyleParser = CVNumberBase10Format.toRealParser(
|
254
|
-
ukStyleUngroupedNumber,
|
255
|
-
);
|
256
|
-
|
257
|
-
// Let's define a parser that throws for non Effect users
|
258
|
-
// Type: (value: string ) => CVReal.Type
|
259
|
-
const throwingParser = CVNumberBase10Format.toThrowingRealParser(
|
260
|
-
ukStyleUngroupedNumber,
|
261
|
-
);
|
262
|
-
|
263
|
-
// Result: '10.341e3'
|
264
|
-
console.log(
|
265
|
-
ukStyleWithEngineeringNotationFormatter(CVReal.unsafeFromNumber(10340.548)),
|
266
|
-
);
|
267
|
-
|
268
|
-
// result: { _id: 'Option', _tag: 'Some', value: 10340.548 }
|
269
|
-
console.log(ungroupedUkStyleParser("10340.548"));
|
270
|
-
|
271
|
-
// result: { _id: 'Option', _tag: 'None' }
|
272
|
-
console.log(ungroupedUkStyleParser("10,340.548"));
|
273
|
-
|
274
|
-
// result: 10340.548
|
275
|
-
console.log(throwingParser("10340.548"));
|
276
|
-
|
277
|
-
// Using Schema
|
278
|
-
const schema = CVSchema.Real(frenchStyleInteger);
|
279
|
-
|
280
|
-
// Type: (value: string ) => Either.Either<CVReal.Type,ParseError>
|
281
|
-
const frenchStyleDecoder = Schema.decodeEither(schema);
|
282
|
-
|
283
|
-
// Type: (value: CVReal.Type ) => Either.Either<string,ParseError>
|
284
|
-
const frenchStyleEncoder = Schema.encodeEither(schema);
|
285
|
-
|
286
|
-
// Result: { _id: 'Either', _tag: 'Right', right: 1024 }
|
287
|
-
console.log(frenchStyleDecoder("1 024"));
|
288
|
-
|
289
|
-
// Error: Failed to convert string to a(n) potentially signed French-style integer
|
290
|
-
console.log(frenchStyleDecoder("1 024,56"));
|
291
|
-
|
292
|
-
// Result: { _id: 'Either', _tag: 'Right', right: '1 025' }
|
293
|
-
console.log(frenchStyleEncoder(CVReal.unsafeFromNumber(1024.56)));
|
294
|
-
```
|
295
|
-
|
296
|
-
#### 2. `CVNumberBase10Format` instances
|
297
|
-
|
298
|
-
In the previous example, we used the `ukStyleNumber`, `ukStyleUngroupedNumber` and `frenchStyleInteger` `CVNumberBase10Format` instances.
|
299
|
-
|
300
|
-
You will find in the [API](https://parischap.github.io/effect-libs/conversions/NumberBase10Format.ts) the list of all pre-defined instances.
|
301
|
-
|
302
|
-
#### 3. `CVNumberBase10Format` Instance modifiers
|
303
|
-
|
304
|
-
Sometimes, you will need to bring some small modifications to a pre-defined `CVNumberBase10Format` instance. For instance, in the previous example, we defined the `ukStyleNumberWithEngineeringNotation` instance by using the `withEngineeringScientificNotation` modifier on the `ukStyleNumber` pre-defined instance.
|
305
|
-
|
306
|
-
There are quite a few such modifiers whose list you will find in the [API](https://parischap.github.io/effect-libs/conversions/NumberBase10Format.ts).
|
307
|
-
|
308
|
-
#### 4. `CVNumberBase10Format` in more details
|
309
|
-
|
310
|
-
If you have very specific needs, you can define your own CVNumberBase10Format instance that must comply with the following interface:
|
311
|
-
|
312
|
-
```ts
|
313
|
-
export interface Type {
|
314
|
-
/**
|
315
|
-
* Thousand separator. Use an empty string for no separator. Usually a string made of at most one
|
316
|
-
* character different from `fractionalSeparator`. Will not throw otherwise but unexpected results
|
317
|
-
* might occur.
|
318
|
-
*/
|
319
|
-
readonly thousandSeparator: string;
|
320
|
-
|
321
|
-
/**
|
322
|
-
* Fractional separator. Usually a one-character string different from `thousandSeparator`. Will
|
323
|
-
* not throw otherwise but unexpected results might occur.
|
324
|
-
*/
|
325
|
-
readonly fractionalSeparator: string;
|
326
|
-
|
327
|
-
/**
|
328
|
-
* Formatting:
|
329
|
-
*
|
330
|
-
* - If `true`, numbers with a null integer part are displayed starting with `0`. Otherwise, they
|
331
|
-
* are displayed starting with `.` unless `maximumFractionalDigits===0`, in which case they are
|
332
|
-
* displayed starting wiyh `0`.
|
333
|
-
*
|
334
|
-
* Parsing
|
335
|
-
*
|
336
|
-
* - If `true`, conversion will fail for numbers starting with `.` (after an optional sign).
|
337
|
-
* - If `false`, conversion will fail for numbers starting with `0.` (after an optional sign).
|
338
|
-
*/
|
339
|
-
readonly showNullIntegerPart: boolean;
|
340
|
-
|
341
|
-
/**
|
342
|
-
* Minimim number of digits forming the fractional part of a number. Must be a positive integer
|
343
|
-
* (>=0) less than or equal to `maximumFractionalDigits`.
|
344
|
-
*
|
345
|
-
* Formatting: the string will be right-padded with `0`'s if necessary to respect the condition
|
346
|
-
*
|
347
|
-
* Parsing: will fail if the input string does not respect this condition (the string must be
|
348
|
-
* right-padded with `0`'s to respect the condition if necessary).
|
349
|
-
*/
|
350
|
-
readonly minimumFractionalDigits: number;
|
351
|
-
|
352
|
-
/**
|
353
|
-
* Maximum number of digits forming the fractional part of a number. Must be an integer value
|
354
|
-
* greater than or equal to `minimumFractionalDigits`. Can take the +Infinity value.
|
355
|
-
*
|
356
|
-
* Formatting: the number will be rounded using the roundingMode to respect the condition (unless
|
357
|
-
* `maximumFractionalDigits` is `+Infinity`).
|
358
|
-
*
|
359
|
-
* Parsing: will fail if the input string has too many fractional digits.
|
360
|
-
*/
|
361
|
-
readonly maximumFractionalDigits: number;
|
362
|
-
|
363
|
-
/**
|
364
|
-
* Possible characters to use to represent e-notation. Usually ['e','E']. Must be an array of
|
365
|
-
* one-character strings. Will not throw otherwise but unexpected results will occur. Not used if
|
366
|
-
* `scientificNotation === None`
|
367
|
-
*
|
368
|
-
* Formatting: the string at index 0 is used
|
369
|
-
*
|
370
|
-
* Parsing: the first character of the e-notation must be one of the one-character strings present
|
371
|
-
* in the array
|
372
|
-
*/
|
373
|
-
readonly eNotationChars: ReadonlyArray<string>;
|
374
|
-
|
375
|
-
/** Scientific notation options. See ScientificNotation */
|
376
|
-
readonly scientificNotation: ScientificNotation;
|
377
|
-
|
378
|
-
/** Rounding mode options. See RoundingMode.ts */
|
379
|
-
readonly roundingMode: CVRoundingMode.Type;
|
380
|
-
|
381
|
-
/** Sign display options. See SignDisplay.ts */
|
382
|
-
readonly signDisplay: SignDisplay;
|
383
|
-
}
|
384
|
-
```
|
385
|
-
|
386
|
-
To build such an instance, you will need to use the `make` constructor. For instance, this is how you could redefine the `frenchStyleNumber` instance:
|
387
|
-
|
388
|
-
```ts
|
389
|
-
const frenchStyleNumber = CVNumberBase10Format.make({
|
390
|
-
thousandSeparator: " ",
|
391
|
-
fractionalSeparator: ",",
|
392
|
-
showNullIntegerPart: true,
|
393
|
-
minimumFractionalDigits: 0,
|
394
|
-
maximumFractionalDigits: 3,
|
395
|
-
eNotationChars: ["e", "E"],
|
396
|
-
scientificNotation: ScientificNotation.None,
|
397
|
-
roundingMode: CVRoundingMode.Type.HalfExpand,
|
398
|
-
signDisplay: SignDisplay.Negative,
|
399
|
-
});
|
400
|
-
```
|
401
|
-
|
402
|
-
#### 5. Debugging and equality
|
403
|
-
|
404
|
-
`CVNumberBase10Format` objects implement a `.toString()` method and a `toDescription` destructor.
|
405
|
-
The `.toString()` method will display the name of the object and all available properties. The `toDescription` destructor will produce a short summary of the format.
|
406
|
-
|
407
|
-
For instance:
|
408
|
-
|
409
|
-
```ts
|
410
|
-
import { CVNumberBase10Format } from "@parischap/conversions";
|
411
|
-
import { pipe } from "effect";
|
412
|
-
|
413
|
-
// Result:
|
414
|
-
// {
|
415
|
-
// _id: '@parischap/conversions/NumberBase10Format/',
|
416
|
-
// thousandSeparator: '',
|
417
|
-
// fractionalSeparator: '.',
|
418
|
-
// showNullIntegerPart: true,
|
419
|
-
// minimumFractionalDigits: 0,
|
420
|
-
// maximumFractionalDigits: 3,
|
421
|
-
// eNotationChars: [ 'e', 'E' ],
|
422
|
-
// scientificNotation: 0,
|
423
|
-
// roundingMode: 6,
|
424
|
-
// signDisplay: 3
|
425
|
-
// }
|
426
|
-
console.log(CVNumberBase10Format.ukStyleUngroupedNumber);
|
427
|
-
|
428
|
-
// Result: 'signed integer'
|
429
|
-
console.log(
|
430
|
-
pipe(
|
431
|
-
CVNumberBase10Format.ukStyleUngroupedNumber,
|
432
|
-
CVNumberBase10Format.withSignDisplay,
|
433
|
-
CVNumberBase10Format.withNDecimals(0),
|
434
|
-
CVNumberBase10Format.toDescription,
|
435
|
-
),
|
436
|
-
);
|
437
|
-
```
|
438
|
-
|
439
|
-
### <a name="Templating"></a>C) Templating
|
440
|
-
|
441
|
-
#### 1. Usage example
|
442
|
-
|
443
|
-
```ts
|
444
|
-
/* eslint-disable functional/no-expression-statements */
|
445
|
-
import {
|
446
|
-
CVNumberBase10Format,
|
447
|
-
CVReal,
|
448
|
-
CVSchema,
|
449
|
-
CVTemplate,
|
450
|
-
CVTemplatePlaceholder,
|
451
|
-
CVTemplateSeparator,
|
452
|
-
} from "@parischap/conversions";
|
453
|
-
import { MRegExpString } from "@parischap/effect-lib";
|
454
|
-
import { pipe, Schema } from "effect";
|
455
|
-
|
456
|
-
// Let's define useful shortcuts
|
457
|
-
const ph = CVTemplatePlaceholder;
|
458
|
-
const sep = CVTemplateSeparator;
|
459
|
-
|
460
|
-
// Let's define a template: "#name is a #age-year old #kind."
|
461
|
-
const template = CVTemplate.make(
|
462
|
-
// field named 'name' that must be a non-empty string containing no space characters
|
463
|
-
ph.anythingBut({ name: "name", forbiddenChars: [MRegExpString.space] }),
|
464
|
-
// Immutable text
|
465
|
-
sep.make(" is a "),
|
466
|
-
// Field named 'age' that must represent an unsigned integer
|
467
|
-
ph.real({
|
468
|
-
name: "age",
|
469
|
-
numberBase10Format: pipe(
|
470
|
-
CVNumberBase10Format.integer,
|
471
|
-
CVNumberBase10Format.withoutSignDisplay,
|
472
|
-
),
|
473
|
-
}),
|
474
|
-
// Immutable text
|
475
|
-
sep.make("-year old "),
|
476
|
-
// field named 'kind' that must be a non-empty string containing no dot character
|
477
|
-
ph.anythingBut({ name: "kind", forbiddenChars: ["."] }),
|
478
|
-
// Immutable text
|
479
|
-
sep.dot,
|
480
|
-
);
|
481
|
-
|
482
|
-
// Let's define a parser. See how the return type matches the names and types of the placeholders
|
483
|
-
// Type: (value: string) => Either.Either<{
|
484
|
-
// readonly name: string;
|
485
|
-
// readonly age: CVReal.Type;
|
486
|
-
// readonly kind: string;
|
487
|
-
// }, MInputError.Type>
|
488
|
-
const parser = CVTemplate.toParser(template);
|
489
|
-
|
490
|
-
// Let's define a parser that throws for Effect users.
|
491
|
-
// Type: (value: string) => {
|
492
|
-
// readonly name: string;
|
493
|
-
// readonly age: CVReal.Type;
|
494
|
-
// readonly kind: string;
|
495
|
-
// }
|
496
|
-
const throwingParser = CVTemplate.toThrowingParser(template);
|
497
|
-
|
498
|
-
// Let's define a formatter.
|
499
|
-
// Type: (value: {
|
500
|
-
// readonly name: string;
|
501
|
-
// readonly age: CVReal.Type;
|
502
|
-
// readonly kind: string;
|
503
|
-
// }) => Either.Either<string, MInputError.Type>
|
504
|
-
const formatter = CVTemplate.toFormatter(template);
|
505
|
-
|
506
|
-
// Let's define a formatter that throws for Effect users.
|
507
|
-
// Type: (value: {
|
508
|
-
// readonly name: string;
|
509
|
-
// readonly age: CVReal.Type;
|
510
|
-
// readonly kind: string;
|
511
|
-
// }) => string, MInputError.Type
|
512
|
-
const throwingFormatter = CVTemplate.toThrowingFormatter(template);
|
513
|
-
|
514
|
-
// Result: {
|
515
|
-
// _id: 'Either',
|
516
|
-
// _tag: 'Left',
|
517
|
-
// left: {
|
518
|
-
// message: "Expected remaining text for separator at position 2 to start with ' is a '. Actual: ''",
|
519
|
-
// _tag: '@parischap/effect-lib/InputError/'
|
520
|
-
// }
|
521
|
-
// }
|
522
|
-
console.log(parser("John"));
|
523
|
-
|
524
|
-
// Result: { _id: 'Either', _tag: 'Right', right: { name: 'John', age: 47, kind: 'man' } }
|
525
|
-
console.log(parser("John is a 47-year old man."));
|
526
|
-
|
527
|
-
// Result: { name: 'John', age: 47, kind: 'man' }
|
528
|
-
console.log(throwingParser("John is a 47-year old man."));
|
529
|
-
|
530
|
-
// Result: { _id: 'Either', _tag: 'Right', right: 'Tom is a 15-year old boy.' }
|
531
|
-
console.log(
|
532
|
-
formatter({
|
533
|
-
name: "Tom",
|
534
|
-
age: CVReal.unsafeFromNumber(15),
|
535
|
-
kind: "boy",
|
536
|
-
}),
|
537
|
-
);
|
538
|
-
|
539
|
-
// Result: 'Tom is a 15-year old boy.'
|
540
|
-
console.log(
|
541
|
-
throwingFormatter({
|
542
|
-
name: "Tom",
|
543
|
-
age: CVReal.unsafeFromNumber(15),
|
544
|
-
kind: "boy",
|
545
|
-
}),
|
546
|
-
);
|
547
|
-
|
548
|
-
// Using Schema
|
549
|
-
const schema = CVSchema.Template(template);
|
550
|
-
|
551
|
-
// Type:(i: string) => Either<{
|
552
|
-
// readonly name: string;
|
553
|
-
// readonly age: CVReal.Type;
|
554
|
-
// readonly kind: string;
|
555
|
-
// }, ParseError>
|
556
|
-
const decoder = Schema.decodeEither(schema);
|
557
|
-
|
558
|
-
// Type: (a: {
|
559
|
-
// readonly name: string;
|
560
|
-
// readonly age: CVReal.Type;
|
561
|
-
// readonly kind: string;
|
562
|
-
// }) => Either<string, ParseError>
|
563
|
-
const encoder = Schema.encodeEither(schema);
|
564
|
-
|
565
|
-
// Result: { _id: 'Either', _tag: 'Right', right: { name: 'John', age: 47, kind: 'man' } }
|
566
|
-
console.log(decoder("John is a 47-year old man."));
|
567
|
-
|
568
|
-
// Result: { _id: 'Either', _tag: 'Right', right: 'Tom is a 15-year old boy.' }
|
569
|
-
console.log(
|
570
|
-
encoder({
|
571
|
-
name: "Tom",
|
572
|
-
age: CVReal.unsafeFromNumber(15),
|
573
|
-
kind: "boy",
|
574
|
-
}),
|
575
|
-
);
|
576
|
-
```
|
577
|
-
|
578
|
-
#### 2. Definitions
|
579
|
-
|
580
|
-
A template is a model of a text that has always the same structure. In such a text, there are immutable and mutable parts. Let's take the following two texts as an example:
|
581
|
-
|
582
|
-
- text1 = "John is a 47-year old man."
|
583
|
-
- text2 = "Jehnny is a 5-year old girl."
|
584
|
-
|
585
|
-
These two texts obviously share the same structure which is the template:
|
586
|
-
|
587
|
-
Placeholder1 is a Placeholder2-year old Placeholder3.
|
588
|
-
|
589
|
-
Placeholder1, Placeholder2 and Placeholder3 are the mutable parts of the template. We call them `CVTemplatePlaceholder`'s.
|
590
|
-
|
591
|
-
" is a ", "-year old " and "." are the immutable parts of the template. We call them `CVTemplateSeperator`'s.
|
592
|
-
|
593
|
-
From a text with the above structure, we can extract the values of Placeholder1, Placeholder2, and Placeholder3. In the present case:
|
594
|
-
|
595
|
-
- For text1: { Placeholder1 : 'John', Placeholder2 : '47', Placeholder3 : 'man' }
|
596
|
-
- For text2: { Placeholder1 : 'Jehnny', Placeholder2 : '5', Placeholder3 : 'girl'}
|
597
|
-
|
598
|
-
Extracting the values of placeholders from a text according to a template is called parsing. The result of parsing is an object whose properties are named after the name of the placeholders they represent.
|
599
|
-
|
600
|
-
Inversely, given a template and the values of the placeholders that compose it (provided as the properties of an object), we can generate a text. This is called formatting. In the present case, with the object:
|
601
|
-
|
602
|
-
{ Placeholder1 : 'Tom', Placeholder2 : '15', Placeholder3 : 'boy' }
|
603
|
-
|
604
|
-
we will obtain the text: "Tom is a 15-year old boy."
|
605
|
-
|
606
|
-
#### 3. `CVTemplateSeparator`'s
|
607
|
-
|
608
|
-
A `CVTemplateSeparator` represents the immutable part of a template. Upon parsing, we must check that it is present as is in the text. Upon formatting, it must be inserted as is into the text.
|
609
|
-
|
610
|
-
To create a `CVTemplateSeparator`, you usually call the `CVTemplateSeparator.make` constructor. However, the TemplateSeparator.ts module exports a series of predefined `CVTemplateSeparator` instances, such as `CVTemplateSeparator.slash` and `CVTemplateSeparator.space`. You can find the list of all predefined `CVTemplateSeparator` instances in the [API](https://parischap.github.io/effect-libs/conversions/TemplatePart.ts).
|
611
|
-
|
612
|
-
#### 4. CVTemplatePlaceholder's
|
613
|
-
|
614
|
-
A `CVTemplatePlaceholder` represents the mutable part of a template. Each `CVTemplatePlaceholder` defines a parser and a formatter:
|
615
|
-
|
616
|
-
- the parser takes a text, consumes a part of that text, optionnally converts the consumed part to a value of type T and, if successful, returns a `Right` of that value and of what has not been consumed. In case of failure, it returns a `Left`.
|
617
|
-
- the formatter takes a value of type T, converts it to a string (if T is not string), checks that the result is coherent and, if so, inserts that string into the text. Otherwise, it returns a `Left`.
|
618
|
-
|
619
|
-
There are several predefined Placeholder's:
|
620
|
-
|
621
|
-
- `fixedLength`: this Placeholder always reads/writes the same number of characters from/into the text.
|
622
|
-
- `paddedFixedLength`: same as `fixedLength` but the consumed text is trimmed off of a `fillChar` on the left or right and the written text is padded with a `fillChar` on the left or right.
|
623
|
-
- `fixedLengthToReal`: same as `fixedLength` but the parser tries to convert the consumed text into a `CVReal` using the passed `CVNumberBase10Format`. The formatter takes a `CVReal` and tries to convert and write it as an n-character string. You can pass a `fillChar` that is trimmed off the consumed text upon parsing and padded to the written text upon formatting.
|
624
|
-
- `real`: the parser of this Placeholder reads from the text all the characters that it can interpret as a number in the provided `CVNumberBase10Format` and converts the consumed text into a `CVReal`. The formatter takes a `CVReal` and converts it into a string according to the provided `CVNumberBase10Format`.
|
625
|
-
- `mappedLiterals`: this Placeholder takes as input a map that must define a bijection between a list of strings and a list of values. The parser tries to read from the text one of the strings in the list. Upon success, it returns the corresponding value. The formatter takes a value and tries to find it in the list. Upon success, it writes the corresponding string into the text.
|
626
|
-
- `realMappedLiterals`: same as `mappedLiterals` but values are assumed to be of type `CVReal` which is the most usual use case.
|
627
|
-
- `fulfilling`: the parser of this Placeholder reads as much of the text as it can that fulfills the passed regular expression. The formatter only accepts a string that matches the passed regular expression and writes it into the text.
|
628
|
-
- `anythingBut`: this is a special case of the `fulfilling` `CVTemplatePlaceholder`. The parser reads from the text until it meets one of the `forbiddenChars` passed as parameter (the result must be a non-empty string). The formatter will only accept a non-empty string that does not contain any of the forbidden chars and write it to the text.
|
629
|
-
- `toEnd`: this is another special case of the `fulfilling` `CVTemplatePlaceholder`. The parser reads all the remaining text. The formatter accepts any string and writes it. This `CVTemplatePlaceholder` should only be used as the last `CVTemplatePart` of a `CVTemplate`.
|
630
|
-
|
631
|
-
Each `CVTemplatePlaceholder` must be given a name that will be used as the name of the property of the result object of parsing or of the input object of formatting. This name needs not be unique inside a CVTemplate. The same name can appear several times. However, even if there are several `CVTemplatePlaceholder`'s with the same name, there will be only one property with that name. When parsing, this implies that all `CVTemplatePlaceholder`'s with the same name must yield the same value. When formatting, this implies that the value needs only be provided once and will be shared by all `CVTemplatePlaceholder`'s with that name.
|
632
|
-
|
633
|
-
If none of these `CVTemplatePlaceholder` instances suits you, you can define you own with the `make` constructor. You will find detailed explanations of the predefined `CVTemplatePlaceholder` instances and of the make constructor in the [API](https://parischap.github.io/effect-libs/conversions/TemplatePart.ts).
|
634
|
-
|
635
|
-
#### 5. A more complex example
|
636
|
-
|
637
|
-
```ts
|
638
|
-
import {
|
639
|
-
CVNumberBase10Format,
|
640
|
-
CVReal,
|
641
|
-
CVTemplate,
|
642
|
-
CVTemplatePlaceholder,
|
643
|
-
CVTemplateSeparator,
|
644
|
-
} from "@parischap/conversions";
|
645
|
-
|
646
|
-
// Let's define useful shortcuts
|
647
|
-
const placeholder = CVTemplatePlaceholder;
|
648
|
-
const sep = CVTemplateSeparator;
|
649
|
-
|
650
|
-
// Let's define a date template that will look like: 'Today is #weekday, day number #weekday of the week.'
|
651
|
-
// Note that weekDay appears twice, once as a realMappedLiterals placeholder, once as a real placeholder.
|
652
|
-
const template = CVTemplate.make(
|
653
|
-
// Separator
|
654
|
-
sep.make("Today is "),
|
655
|
-
// realMappedLiterals placeHolder
|
656
|
-
placeholder.realMappedLiterals({
|
657
|
-
name: "weekday",
|
658
|
-
keyValuePairs: [
|
659
|
-
["Monday", CVReal.unsafeFromNumber(1)],
|
660
|
-
["Tuesday", CVReal.unsafeFromNumber(2)],
|
661
|
-
["Wednesday", CVReal.unsafeFromNumber(3)],
|
662
|
-
["Thursday", CVReal.unsafeFromNumber(4)],
|
663
|
-
["Friday", CVReal.unsafeFromNumber(5)],
|
664
|
-
["Saturday", CVReal.unsafeFromNumber(6)],
|
665
|
-
["Sunday", CVReal.unsafeFromNumber(7)],
|
666
|
-
],
|
667
|
-
}),
|
668
|
-
// Separator
|
669
|
-
sep.make(", day number "),
|
670
|
-
// Field named 'weekday' that must represent an integer
|
671
|
-
placeholder.real({
|
672
|
-
name: "weekday",
|
673
|
-
numberBase10Format: CVNumberBase10Format.integer,
|
674
|
-
}),
|
675
|
-
// Separator
|
676
|
-
sep.make(" of the week."),
|
677
|
-
);
|
678
|
-
|
679
|
-
// Let's define a parser. Note that there is only one `weekday` property
|
680
|
-
// Type: (value: string) => Either.Either<{
|
681
|
-
// readonly weekday: CVReal.Type;
|
682
|
-
// }, MInputError.Type>>
|
683
|
-
const parser = CVTemplate.toParser(template);
|
684
|
-
|
685
|
-
// Let's define a formatter. Note that there is only one `weekday` property
|
686
|
-
// Type: (value: {
|
687
|
-
// readonly weekday: CVReal.Type;
|
688
|
-
// }) => Either.Either<string, MInputError.Type>
|
689
|
-
const formatter = CVTemplate.toFormatter(template);
|
690
|
-
|
691
|
-
// Result: { _id: 'Either', _tag: 'Right', right: { weekday: 2 } }
|
692
|
-
console.log(parser("Today is Tuesday, day number 2 of the week."));
|
693
|
-
|
694
|
-
// Result: {
|
695
|
-
// _id: 'Either',
|
696
|
-
// _tag: 'Left',
|
697
|
-
// left: {
|
698
|
-
// message: "#weekday is present more than once in template and receives differing values '4' and '2'",
|
699
|
-
// _tag: '@parischap/effect-lib/InputError/'
|
700
|
-
// }
|
701
|
-
// }
|
702
|
-
console.log(parser("Today is Thursday, day number 2 of the week."));
|
703
|
-
|
704
|
-
// Result: { _id: 'Either', _tag: 'Right', right: 'Today is Saturday, day number 6 of the week.' }
|
705
|
-
console.log(formatter({ weekday: CVReal.unsafeFromNumber(6) }));
|
706
|
-
|
707
|
-
// Result: {
|
708
|
-
// _id: 'Either',
|
709
|
-
// _tag: 'Left',
|
710
|
-
// left: {
|
711
|
-
// message: '#weekday: expected one of [1, 2, 3, 4, 5, 6, 7]. Actual: 10',
|
712
|
-
// _tag: '@parischap/effect-lib/InputError/'
|
713
|
-
// }
|
714
|
-
// }
|
715
|
-
console.log(formatter({ weekday: CVReal.unsafeFromNumber(10) }));
|
716
|
-
```
|
717
|
-
|
718
|
-
#### 6. Debugging
|
719
|
-
|
720
|
-
`CVTemplate` objects implement a `.toString()` method that displays a synthetic description of the template followed by the description of each contained `CVTemplatePlaceholder`.
|
721
|
-
|
722
|
-
For instance:
|
723
|
-
|
724
|
-
```ts
|
725
|
-
import {
|
726
|
-
CVNumberBase10Format,
|
727
|
-
CVTemplate,
|
728
|
-
CVTemplatePlaceholder,
|
729
|
-
CVTemplateSeparator,
|
730
|
-
} from "@parischap/conversions";
|
731
|
-
import { MRegExpString } from "@parischap/effect-lib";
|
732
|
-
import { pipe } from "effect";
|
733
|
-
|
734
|
-
// Let's define useful shortcuts
|
735
|
-
const ph = CVTemplatePlaceholder;
|
736
|
-
const sep = CVTemplateSeparator;
|
737
|
-
|
738
|
-
// Let's define a template: "#name is a #age-year old #kind."
|
739
|
-
const template = CVTemplate.make(
|
740
|
-
// field named 'name' that must be a non-empty string containing no space characters
|
741
|
-
ph.anythingBut({ name: "name", forbiddenChars: [MRegExpString.space] }),
|
742
|
-
// Immutable text
|
743
|
-
sep.make(" is a "),
|
744
|
-
// Field named 'age' that must represent an unsigned integer
|
745
|
-
ph.real({
|
746
|
-
name: "age",
|
747
|
-
numberBase10Format: pipe(
|
748
|
-
CVNumberBase10Format.integer,
|
749
|
-
CVNumberBase10Format.withoutSignDisplay,
|
750
|
-
),
|
751
|
-
}),
|
752
|
-
// Immutable text
|
753
|
-
sep.make("-year old "),
|
754
|
-
// field named 'kind' that must be a non-empty string containing no dot character
|
755
|
-
ph.anythingBut({ name: "kind", forbiddenChars: ["."] }),
|
756
|
-
// Immutable text
|
757
|
-
sep.dot,
|
758
|
-
);
|
759
|
-
|
760
|
-
// Result:
|
761
|
-
// #name is a #age-year old #kind.
|
762
|
-
|
763
|
-
// #name: a non-empty string containing non of the following characters: [ \s ].
|
764
|
-
// #age: unsigned integer.
|
765
|
-
// #kind: a non-empty string containing non of the following characters: [ . ]
|
766
|
-
console.log(template);
|
767
|
-
```
|
768
|
-
|
769
|
-
### <a name="DateTimeModule"></a>D) DateTime module
|
770
|
-
|
771
|
-
#### 1. Introduction
|
772
|
-
|
773
|
-
This package implements an immutable `CVDateTime` object: once created, the characteristics of a `CVDateTime` object will never change. However, the provided Setters functions allow you to get a copy of an existing `CVDateTime` object with just one charactreristic modified.
|
774
|
-
|
775
|
-
Although immutable when considered from the outer world, `CVDateTime` objects do keep an internal state that is only used to improve performance (but does not alter results). `CVDateTime` functions can therefore be regarded as pure: they will always yield the same result whatever the state the object is in.
|
776
|
-
|
777
|
-
Unlike the Javascript `Date` objects and the `Effect.DateTime` objects, `CVDateTime` objects handle both
|
778
|
-
the Gregorian and Iso calendars. So you can easily get/set the iso year and iso week of a
|
779
|
-
`CVDateTime` object.
|
780
|
-
|
781
|
-
A `CVDateTime` object has a `zoneOffset` which is the difference in hours between the time in the local zone and UTC time (e.g zoneOffset=1 for timezone +1:00). All the data in a `CVDateTime` object is zoneOffset-dependent, except `timestamp`.
|
782
|
-
|
783
|
-
You cannot create a `CVDateTime` object from a string. If this is your need, use the `CVDateTimeFormat` module.
|
784
|
-
|
785
|
-
#### 2. Usage example
|
786
|
-
|
787
|
-
```ts
|
788
|
-
import { CVDateTime } from "@parischap/conversions";
|
789
|
-
import { pipe } from "effect";
|
790
|
-
|
791
|
-
/** You can create a CVDateTime from a timestamp and timeZoneOffset expressed in hours */
|
792
|
-
// Result: '1970-01-01T05:15:00.000+05:15
|
793
|
-
console.log(CVDateTime.fromTimestampOrThrow(0, 5.25));
|
794
|
-
|
795
|
-
/**
|
796
|
-
* You can create a CVDateTime from a timestamp and timeZoneOffset expressed in hours, minutes,
|
797
|
-
* seconds
|
798
|
-
*/
|
799
|
-
// Result: '1970-01-01T05:15:00.000+05:15'
|
800
|
-
console.log(
|
801
|
-
CVDateTime.fromTimestampOrThrow(0, {
|
802
|
-
zoneHour: 5,
|
803
|
-
zoneMinute: 15,
|
804
|
-
zoneSecond: 0,
|
805
|
-
}),
|
806
|
-
);
|
807
|
-
|
808
|
-
/**
|
809
|
-
* You can create a CVDateTime from a timestamp without specifying a timeZoneOffset. In that case,
|
810
|
-
* the timeZoneOffset of the machine the code runs on is applied
|
811
|
-
*/
|
812
|
-
// Result: '1970-01-01T02:00:00.000+02:00' (Was run in Paris during summertime)
|
813
|
-
console.log(CVDateTime.fromTimestampOrThrow(0));
|
814
|
-
|
815
|
-
/**
|
816
|
-
* You can create a CVDateTime from DateTime.Parts
|
817
|
-
*
|
818
|
-
* See the documentation of function CVDateTime.fromParts to see when and how default values are
|
819
|
-
* calculated if you don't provide enough information.
|
820
|
-
*
|
821
|
-
* Unlike the native Javascript Date object, you cannot pass out-of-range data (e.g month = 13,
|
822
|
-
* monthDay=31 in April,...). If you pass too much information, all provided parameters must be
|
823
|
-
* coherent.
|
824
|
-
*
|
825
|
-
* Let's see some examples
|
826
|
-
*/
|
827
|
-
|
828
|
-
// Result: { _id: 'Either', _tag: 'Right', right: '2025-01-25T00:00:00.765+00:00' }
|
829
|
-
console.log(
|
830
|
-
CVDateTime.fromParts({
|
831
|
-
year: 2025,
|
832
|
-
month: 1,
|
833
|
-
monthDay: 25,
|
834
|
-
millisecond: 765,
|
835
|
-
zoneOffset: 0,
|
836
|
-
}),
|
837
|
-
);
|
838
|
-
|
839
|
-
// Result: { _id: 'Either', _tag: 'Right', right: '2025-12-30T11:00:00.000-12:00' }
|
840
|
-
console.log(
|
841
|
-
CVDateTime.fromParts({
|
842
|
-
isoYear: 2026,
|
843
|
-
isoWeek: 1,
|
844
|
-
weekday: 2,
|
845
|
-
hour23: 11,
|
846
|
-
zoneOffset: -12,
|
847
|
-
}),
|
848
|
-
);
|
849
|
-
|
850
|
-
// Result: {
|
851
|
-
// _id: 'Either',
|
852
|
-
// _tag: 'Left',
|
853
|
-
// left: {
|
854
|
-
// message: "Expected 'hour11' to be between 0 (included) and 11 (included). Actual: 12",
|
855
|
-
// _tag: '@parischap/effect-lib/InputError/'
|
856
|
-
// }
|
857
|
-
// }
|
858
|
-
console.log(
|
859
|
-
CVDateTime.fromParts({
|
860
|
-
isoYear: 2026,
|
861
|
-
isoWeek: 1,
|
862
|
-
weekday: 2,
|
863
|
-
hour11: 12,
|
864
|
-
zoneOffset: -12,
|
865
|
-
}),
|
866
|
-
);
|
867
|
-
|
868
|
-
// Result: {
|
869
|
-
// _id: 'Either',
|
870
|
-
// _tag: 'Left',
|
871
|
-
// left: {
|
872
|
-
// message: "Expected 'monthDay' to be between 1 (included) and 28 (included). Actual: 29",
|
873
|
-
// _tag: '@parischap/effect-lib/InputError/'
|
874
|
-
// }
|
875
|
-
// }
|
876
|
-
console.log(
|
877
|
-
CVDateTime.fromParts({ year: 2025, month: 2, monthDay: 29, zoneOffset: 0 }),
|
878
|
-
);
|
879
|
-
|
880
|
-
// Result: {
|
881
|
-
// _id: 'Either',
|
882
|
-
// _tag: 'Left',
|
883
|
-
// left: {
|
884
|
-
// message: "Expected 'isoWeek' to be: 9. Actual: 5",
|
885
|
-
// _tag: '@parischap/effect-lib/InputError/'
|
886
|
-
// }
|
887
|
-
// }
|
888
|
-
console.log(
|
889
|
-
CVDateTime.fromParts({
|
890
|
-
year: 2025,
|
891
|
-
month: 2,
|
892
|
-
monthDay: 28,
|
893
|
-
isoWeek: 5,
|
894
|
-
zoneOffset: 0,
|
895
|
-
}),
|
896
|
-
);
|
897
|
-
|
898
|
-
/**
|
899
|
-
* Once a CVDateTime is created, you can get any CVDateTime.Parts from it ising the provided
|
900
|
-
* getters. Here are a few examples (you can see the whole list of getters in the API).
|
901
|
-
*/
|
902
|
-
|
903
|
-
const aDate = CVDateTime.fromPartsOrThrow({
|
904
|
-
year: 1970,
|
905
|
-
month: 8,
|
906
|
-
monthDay: 31,
|
907
|
-
zoneOffset: 0,
|
908
|
-
});
|
909
|
-
|
910
|
-
// Result: '1970'
|
911
|
-
console.log(CVDateTime.getYear(aDate));
|
912
|
-
|
913
|
-
// Result: '36'
|
914
|
-
console.log(CVDateTime.getIsoWeek(aDate));
|
915
|
-
|
916
|
-
// DO NOT DO THIS. It works but is slower because intermediate calculations are not saved
|
917
|
-
// Result: '1970 36'
|
918
|
-
console.log(
|
919
|
-
CVDateTime.getYear(
|
920
|
-
CVDateTime.fromPartsOrThrow({
|
921
|
-
year: 1970,
|
922
|
-
month: 8,
|
923
|
-
monthDay: 31,
|
924
|
-
zoneOffset: 0,
|
925
|
-
}),
|
926
|
-
),
|
927
|
-
CVDateTime.getIsoWeek(
|
928
|
-
CVDateTime.fromPartsOrThrow({
|
929
|
-
year: 1970,
|
930
|
-
month: 8,
|
931
|
-
monthDay: 31,
|
932
|
-
zoneOffset: 0,
|
933
|
-
}),
|
934
|
-
),
|
935
|
-
);
|
936
|
-
|
937
|
-
/**
|
938
|
-
* Once a CVDateTime is created, you can modify any CVDateTime.Parts with the provided setters. Do
|
939
|
-
* keep in mind that the initial CVDateTime object is unchanged: you get a copy with the modified
|
940
|
-
* part. Here are a few examples (you can see the whole list of setters in the API).
|
941
|
-
*/
|
942
|
-
// Result: { _id: 'Either', _tag: 'Right', right: '1970-03-01T00:00:00.000+00:00' }
|
943
|
-
console.log(pipe(aDate, CVDateTime.setMonth(3)));
|
944
|
-
|
945
|
-
// result: {
|
946
|
-
// _id: 'Either',
|
947
|
-
// _tag: 'Left',
|
948
|
-
// left: {
|
949
|
-
// message: 'Month 6 of year 1970 does not have 31 days',
|
950
|
-
// _tag: '@parischap/effect-lib/InputError/'
|
951
|
-
// }
|
952
|
-
// }
|
953
|
-
console.log(pipe(aDate, CVDateTime.setMonth(6)));
|
954
|
-
|
955
|
-
// Result: { _id: 'Either', _tag: 'Right', right: '1970-08-31T05:45:00.000+05:45' }
|
956
|
-
console.log(pipe(aDate, CVDateTime.setZoneOffsetKeepTimestamp(5.75)));
|
957
|
-
|
958
|
-
// Result: { _id: 'Either', _tag: 'Right', right: '1970-08-31T00:00:00.000+05:45' }
|
959
|
-
console.log(pipe(aDate, CVDateTime.setZoneOffsetKeepParts(5.75)));
|
960
|
-
|
961
|
-
/**
|
962
|
-
* You can also modify the CVDateTime.Parts of an existing CVDateTime object with the provided
|
963
|
-
* offsetters. Do keep in mind that the initial CVDateTime object is unchanged: you get a copy with
|
964
|
-
* the modified part. Here are a few examples (you can see the whole list of offsetters in the
|
965
|
-
* API).
|
966
|
-
*/
|
967
|
-
// Result: '1970-01-01T00:00:00.000+00:00'
|
968
|
-
console.log(pipe(aDate, CVDateTime.toFirstYearDay));
|
969
|
-
|
970
|
-
// Result: {
|
971
|
-
// _id: 'Either',
|
972
|
-
// _tag: 'Left',
|
973
|
-
// left: {
|
974
|
-
// message: 'No February 29th on year 2027 which is not a leap year',
|
975
|
-
// _tag: '@parischap/effect-lib/InputError/'
|
976
|
-
// }
|
977
|
-
// }
|
978
|
-
console.log(
|
979
|
-
pipe(
|
980
|
-
CVDateTime.fromPartsOrThrow({
|
981
|
-
year: 2024,
|
982
|
-
month: 2,
|
983
|
-
monthDay: 29,
|
984
|
-
zoneOffset: 0,
|
985
|
-
}),
|
986
|
-
CVDateTime.offsetYears(3, false),
|
987
|
-
),
|
988
|
-
);
|
989
|
-
|
990
|
-
// Result: { _id: 'Either', _tag: 'Right', right: '2028-02-29T00:00:00.000+00:00' }
|
991
|
-
console.log(
|
992
|
-
pipe(
|
993
|
-
CVDateTime.fromPartsOrThrow({
|
994
|
-
year: 2024,
|
995
|
-
month: 2,
|
996
|
-
monthDay: 29,
|
997
|
-
zoneOffset: 0,
|
998
|
-
}),
|
999
|
-
CVDateTime.offsetYears(4, false),
|
1000
|
-
),
|
1001
|
-
);
|
1002
|
-
|
1003
|
-
/** And finally you can use one of the few provided predicates whose list you will find in the API */
|
1004
|
-
|
1005
|
-
// Result: true
|
1006
|
-
console.log(CVDateTime.isLastMonthDay(aDate));
|
1007
|
-
|
1008
|
-
// Result: false
|
1009
|
-
console.log(CVDateTime.isFirstMonthDay(aDate));
|
1010
|
-
```
|
1011
|
-
|
1012
|
-
### <a name="DateTimeParserFormatter"></a>E) DateTime parser/formatter
|
1013
|
-
|
1014
|
-
#### 1. Usage example
|
1015
|
-
|
1016
|
-
```ts
|
1017
|
-
import {
|
1018
|
-
CVDateTime,
|
1019
|
-
CVDateTimeFormat,
|
1020
|
-
CVDateTimeFormatContext,
|
1021
|
-
CVSchema,
|
1022
|
-
} from "@parischap/conversions";
|
1023
|
-
import { DateTime, Either, flow, Schema } from "effect";
|
1024
|
-
|
1025
|
-
// Let's define useful shortcuts
|
1026
|
-
const placeholder = CVDateTimeFormat.TemplatePart.Placeholder.make;
|
1027
|
-
const sep = CVDateTimeFormat.TemplatePart.Separator;
|
1028
|
-
|
1029
|
-
// Let's define a context
|
1030
|
-
const frenchContext = CVDateTimeFormatContext.fromLocaleOrThrow("fr-FR");
|
1031
|
-
|
1032
|
-
// Let's define a DateTimeFormat: iiii d MMMM yyyy
|
1033
|
-
const frenchFormat = CVDateTimeFormat.make({
|
1034
|
-
context: frenchContext,
|
1035
|
-
templateparts: [
|
1036
|
-
placeholder("iiii"),
|
1037
|
-
sep.space,
|
1038
|
-
placeholder("d"),
|
1039
|
-
sep.space,
|
1040
|
-
placeholder("MMMM"),
|
1041
|
-
sep.space,
|
1042
|
-
placeholder("yyyy"),
|
1043
|
-
],
|
1044
|
-
});
|
1045
|
-
|
1046
|
-
// Let's define a parser
|
1047
|
-
// Type: (dateString: string) => Either.Either<CVDateTime.Type, MInputError.Type>
|
1048
|
-
const parser = CVDateTimeFormat.toParser(frenchFormat);
|
1049
|
-
|
1050
|
-
// Let's define a formatter
|
1051
|
-
// Type: (date: CVDateTime.Type) => Either.Either<string, MInputError.Type>
|
1052
|
-
const formatter = CVDateTimeFormat.toFormatter(frenchFormat);
|
1053
|
-
|
1054
|
-
// Let's define a parser to Effect.DateTime for Effect users
|
1055
|
-
// Type: (dateString: string) => Either.Either<DateTime.Zoned, MInputError.Type>
|
1056
|
-
const effectParser = flow(parser, Either.map(CVDateTime.toEffectDateTime));
|
1057
|
-
|
1058
|
-
// Let's define a formatter from Effect.DateTime for Effect users
|
1059
|
-
// Type: (date: DateTime.Zoned) => Either.Either<string, MInputError.Type>
|
1060
|
-
const effectFormatter = flow(CVDateTime.fromEffectDateTime, formatter);
|
1061
|
-
|
1062
|
-
// Let's define a parser that returns a date or throws for non Effect users
|
1063
|
-
// Type: (dateString: string) => Date
|
1064
|
-
const jsParser = flow(
|
1065
|
-
CVDateTimeFormat.toThrowingParser(frenchFormat),
|
1066
|
-
CVDateTime.toDate,
|
1067
|
-
);
|
1068
|
-
|
1069
|
-
// Let's define a formatter that takes a date and throws for non Effect users
|
1070
|
-
// Type: (date: Date) => string
|
1071
|
-
const jsFormatter = flow(
|
1072
|
-
CVDateTime.fromDate,
|
1073
|
-
CVDateTimeFormat.toThrowingFormatter(frenchFormat),
|
1074
|
-
);
|
1075
|
-
|
1076
|
-
// Result: {
|
1077
|
-
// _id: 'Either',
|
1078
|
-
// _tag: 'Left',
|
1079
|
-
// left: {
|
1080
|
-
// message: "Expected remaining text for #weekday to start with one of [lundi, mardi, mercredi, jeudi, vendredi, samedi, dimanche]. Actual: '20201210'",
|
1081
|
-
// _tag: '@parischap/effect-lib/InputError/'
|
1082
|
-
// }
|
1083
|
-
// }
|
1084
|
-
console.log(parser("20201210"));
|
1085
|
-
|
1086
|
-
// Result: {
|
1087
|
-
// _id: 'Either',
|
1088
|
-
// _tag: 'Left',
|
1089
|
-
// left: {
|
1090
|
-
// message: "Expected 'weekday' to be: 4. Actual: 1",
|
1091
|
-
// _tag: '@parischap/effect-lib/InputError/'
|
1092
|
-
// }
|
1093
|
-
// }
|
1094
|
-
console.log(parser("lundi 4 septembre 2025"));
|
1095
|
-
|
1096
|
-
// Result: { _id: 'Either', _tag: 'Right', right: '2025-09-04T00:00:00.000+02:00' }
|
1097
|
-
console.log(parser("jeudi 4 septembre 2025"));
|
1098
|
-
|
1099
|
-
// Result: { _id: 'Either', _tag: 'Right', right: '2025-09-03T22:00:00.000Z' }
|
1100
|
-
console.log(effectParser("jeudi 4 septembre 2025"));
|
1101
|
-
|
1102
|
-
// Result: '2025-09-03T22:00:00.000Z'
|
1103
|
-
console.log(jsParser("jeudi 4 septembre 2025"));
|
1104
|
-
|
1105
|
-
// Result: { _id: 'Either', _tag: 'Right', right: 'jeudi 1 janvier 1970' }
|
1106
|
-
console.log(formatter(CVDateTime.fromTimestampOrThrow(0, 0)));
|
1107
|
-
|
1108
|
-
// Result: { _id: 'Either', _tag: 'Right', right: 'jeudi 1 janvier 1970' }
|
1109
|
-
console.log(effectFormatter(DateTime.unsafeMakeZoned(0, { timeZone: 0 })));
|
1110
|
-
|
1111
|
-
// Result: 'jeudi 1 janvier 1970'
|
1112
|
-
console.log(jsFormatter(new Date(0)));
|
1113
|
-
|
1114
|
-
// Result: {
|
1115
|
-
// _id: 'Either',
|
1116
|
-
// _tag: 'Left',
|
1117
|
-
// left: {
|
1118
|
-
// message: 'Expected length of #year to be: 4. Actual: 5',
|
1119
|
-
// _tag: '@parischap/effect-lib/InputError/'
|
1120
|
-
// }
|
1121
|
-
console.log(formatter(CVDateTime.fromPartsOrThrow({ year: 10024 })));
|
1122
|
-
|
1123
|
-
// Using Schema
|
1124
|
-
const schema = CVSchema.DateTime(frenchFormat);
|
1125
|
-
|
1126
|
-
// For Effect users
|
1127
|
-
const effectSchema = CVSchema.DateTimeZoned(frenchFormat);
|
1128
|
-
|
1129
|
-
// For non Effect users
|
1130
|
-
const jsSchema = CVSchema.Date(frenchFormat);
|
1131
|
-
|
1132
|
-
// Type: (value: string ) => Either.Either<CVDateTime.Type,ParseError>
|
1133
|
-
const decoder = Schema.decodeEither(schema);
|
1134
|
-
|
1135
|
-
// Type: (value: CVDateTime.Type ) => Either.Either<string,ParseError>
|
1136
|
-
const encoder = Schema.encodeEither(schema);
|
1137
|
-
|
1138
|
-
// Type: (value: string ) => Either.Either<DateTime.Zoned,ParseError>
|
1139
|
-
const effectDecoder = Schema.decodeEither(effectSchema);
|
1140
|
-
|
1141
|
-
// Type: (value: CVDateTime.Zoned ) => Either.Either<string,ParseError>
|
1142
|
-
const effectEncoder = Schema.encodeEither(effectSchema);
|
1143
|
-
|
1144
|
-
// Type: (value: string ) => Either.Either<Date,ParseError>
|
1145
|
-
const jsDecoder = Schema.decodeEither(jsSchema);
|
1146
|
-
|
1147
|
-
// Type: (value: Date ) => Either.Either<string,ParseError>
|
1148
|
-
const jsEncoder = Schema.encodeEither(jsSchema);
|
1149
|
-
|
1150
|
-
// Result: { _id: 'Either', _tag: 'Right', right: '2025-09-04T00:00:00.000+02:00' }
|
1151
|
-
console.log(decoder("jeudi 4 septembre 2025"));
|
1152
|
-
|
1153
|
-
// Error: Expected 'weekday' to be: 4. Actual: 1
|
1154
|
-
console.log(decoder("lundi 4 septembre 2025"));
|
1155
|
-
|
1156
|
-
// Result: { _id: 'Either', _tag: 'Right', right: 'jeudi 1 janvier 1970' }
|
1157
|
-
console.log(encoder(CVDateTime.fromTimestampOrThrow(0, 0)));
|
1158
|
-
|
1159
|
-
// Result: { _id: 'Either', _tag: 'Right', right: '2025-09-03T22:00:00.000Z' }
|
1160
|
-
console.log(effectDecoder("jeudi 4 septembre 2025"));
|
1161
|
-
|
1162
|
-
// Result: { _id: 'Either', _tag: 'Right', right: 'jeudi 1 janvier 1970' }
|
1163
|
-
console.log(effectEncoder(DateTime.unsafeMakeZoned(0, { timeZone: 0 })));
|
1164
|
-
|
1165
|
-
// Result: { _id: 'Either', _tag: 'Right', right: 2025-09-03T22:00:00.000Z }
|
1166
|
-
console.log(jsDecoder("jeudi 4 septembre 2025"));
|
1167
|
-
|
1168
|
-
// Result: { _id: 'Either', _tag: 'Right', right: 'jeudi 1 janvier 1970' }
|
1169
|
-
console.log(jsEncoder(new Date(0)));
|
1170
|
-
```
|
1171
|
-
|
1172
|
-
#### 2. Available tokens
|
1173
|
-
|
1174
|
-
Many of the available [unicode tokens](https://www.unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table) can be used to define `CVDateTimeFormat`'s. Here is a list of all currently available tokens:
|
1175
|
-
|
1176
|
-
```ts
|
1177
|
-
export type Token =
|
1178
|
-
/* Gregorian year (ex: 2005) */
|
1179
|
-
| "y"
|
1180
|
-
/* Gregorian year on 2 digits left-padded with 0's corresponding to years 2000-2099 (ex: 05 for 2005) */
|
1181
|
-
| "yy"
|
1182
|
-
/* Gregorian year on 4 digits left-padded with 0's (ex: 2005, 0965) */
|
1183
|
-
| "yyyy"
|
1184
|
-
/* Iso year (ex: 2005) */
|
1185
|
-
| "R"
|
1186
|
-
/* Iso year on 2 digits left-padded with 0's corresponding to years 2000-2099 (ex: 05 for 2005) */
|
1187
|
-
| "RR"
|
1188
|
-
/* Iso year on 4 digits left-padded with 0's (ex: 2005, 0965)*/
|
1189
|
-
| "RRRR"
|
1190
|
-
/* Month (ex: 6) */
|
1191
|
-
| "M"
|
1192
|
-
/* Month on 2 digits left-padded with 0's (ex: 06) */
|
1193
|
-
| "MM"
|
1194
|
-
/* Short month name (ex: Jun) */
|
1195
|
-
| "MMM"
|
1196
|
-
/* Long month name (ex: June) */
|
1197
|
-
| "MMMM"
|
1198
|
-
/* IsoWeek (ex: 6) */
|
1199
|
-
| "I"
|
1200
|
-
/* IsoWeek (ex: 06) */
|
1201
|
-
| "II"
|
1202
|
-
/* Day of month (ex: 5) */
|
1203
|
-
| "d"
|
1204
|
-
/* Day of month on 2 digits left-padded with 0's (ex: 05) */
|
1205
|
-
| "dd"
|
1206
|
-
/* Day of year (ex: 97) */
|
1207
|
-
| "D"
|
1208
|
-
/* Day of year on 3 digits left-padded with 0's (ex: 097) */
|
1209
|
-
| "DDD"
|
1210
|
-
/* Weekday (ex: 1 for monday, 7 for sunday) */
|
1211
|
-
| "i"
|
1212
|
-
/* Short weekday name (ex: Mon) */
|
1213
|
-
| "iii"
|
1214
|
-
/* Long weekday name (ex: Monday) */
|
1215
|
-
| "iiii"
|
1216
|
-
/* Meridiem (ex: 'AM' for 0, 'PM' for 12) */
|
1217
|
-
| "a"
|
1218
|
-
/* Hour in the range 0..23 (ex:5, 14) */
|
1219
|
-
| "H"
|
1220
|
-
/* Hour on 2 digits in the range 0..23 left-padded with 0's (ex:05, 14) */
|
1221
|
-
| "HH"
|
1222
|
-
/* Hour in the range 0..11 (ex:5, 2) */
|
1223
|
-
| "K"
|
1224
|
-
/* Hour on 2 digits in the range 0..11 left-padded with 0's (ex:05, 02) */
|
1225
|
-
| "KK"
|
1226
|
-
/* Minute (ex: 5) */
|
1227
|
-
| "m"
|
1228
|
-
/* Minute on 2 digits left-padded with 0's (ex: 05) */
|
1229
|
-
| "mm"
|
1230
|
-
/* Second (ex: 5) */
|
1231
|
-
| "s"
|
1232
|
-
/* Second on 2 digits left-padded with 0's (ex: 05) */
|
1233
|
-
| "ss"
|
1234
|
-
/* Millisecond (ex: 5) */
|
1235
|
-
| "S"
|
1236
|
-
/* Millisecond on 3 digits left-padded with 0's (ex: 005) */
|
1237
|
-
| "SSS"
|
1238
|
-
/* Hour part of the timezone offset (ex: 5) */
|
1239
|
-
| "zH"
|
1240
|
-
/* Hour part of the timezone offset on 2 digits left-padded with 0's (ex: 05) */
|
1241
|
-
| "zHzH"
|
1242
|
-
/* Minute part of the timezone offset (ex: 5) */
|
1243
|
-
| "zm"
|
1244
|
-
/* Minute part of the timezone offset on 2 digits left-padded with 0's (ex: 05) */
|
1245
|
-
| "zmzm"
|
1246
|
-
/* Second part of the timezone offset (ex: 5) */
|
1247
|
-
| "zs"
|
1248
|
-
/* Second part of the timezone offset on 2 digits left-padded with 0's (ex: 05) */
|
1249
|
-
| "zszs";
|
1250
|
-
```
|
1251
|
-
|
1252
|
-
#### 3. `CVDateTimeFormatContext`
|
1253
|
-
|
1254
|
-
Some of the available tokens are language specific. For instance the `MMMM` token is expected to display `december` in English and `décembre` in French. For this reason, you need to build a `CVDateTimeFormatContext` before building a `CVDateTimeFormat`. You can build a `CVDateTimeFormatContext` in one of the three following ways:
|
1255
|
-
|
1256
|
-
- you can use the provided `CVDateTimeFormatContext.enGB` instance (for Great Britain English language)
|
1257
|
-
- you can build a `CVDateTimeFormatContext` from the name of a locale, e.g. `const frenchContext = CVDateTimeFormatContext.fromLocaleOrThrow("fr-FR")`
|
1258
|
-
- if you have very specific needs or your locale is not available, you can build a `CVDateTimeFormatContext` by providing directly your translations to the `CVDateTimeFormatContext.fromNames` constructor.
|
1259
|
-
|
1260
|
-
#### 4. Debugging
|
1261
|
-
|
1262
|
-
`CVDateTimeFormat` objects implement a `.toString()` method which displays a synthetic description of the template followed by the description of each CVPlaceholder. For instance:
|
1263
|
-
|
1264
|
-
```ts
|
1265
|
-
import {
|
1266
|
-
CVDateTimeFormat,
|
1267
|
-
CVDateTimeFormatContext,
|
1268
|
-
} from "@parischap/conversions";
|
1269
|
-
|
1270
|
-
// Let's define useful shortcuts
|
1271
|
-
const placeholder = CVDateTimeFormat.TemplatePart.Placeholder.make;
|
1272
|
-
const sep = CVDateTimeFormat.TemplatePart.Separator;
|
1273
|
-
|
1274
|
-
// Let's define a DateTimeFormat: iiii d MMMM yyyy
|
1275
|
-
const frenchFormat = CVDateTimeFormat.make({
|
1276
|
-
context: CVDateTimeFormatContext.enGB,
|
1277
|
-
templateparts: [
|
1278
|
-
placeholder("iiii"),
|
1279
|
-
sep.space,
|
1280
|
-
placeholder("d"),
|
1281
|
-
sep.space,
|
1282
|
-
placeholder("MMMM"),
|
1283
|
-
sep.space,
|
1284
|
-
placeholder("yyyy"),
|
1285
|
-
],
|
1286
|
-
});
|
1287
|
-
|
1288
|
-
// Result: "'iiii d MMMM yyyy' in 'en-GB' context"
|
1289
|
-
console.log(frenchFormat);
|
1290
|
-
```
|
1291
|
-
|
1292
|
-
### <a name="Branding"></a>F) Branding
|
1293
|
-
|
1294
|
-
#### 1. Introduction
|
1295
|
-
|
1296
|
-
In this package you will find the following [`Brand`'s](https://effect.website/docs/code-style/branded-types/):
|
1297
|
-
|
1298
|
-
- `CVEmail`: represents a valid email string
|
1299
|
-
- `CVSemVer`: represents a valid semantic versioning string
|
1300
|
-
- `CVReal`: represents a valid floating-point number (+Infinity, Infinity, -Infinity, NaN not allowed). Can be used to represent a temperature, a height from sea-level,...
|
1301
|
-
- `CVPositiveReal`: same as `CVReal` but the number must be positive. Can be used to represent a price, a speed,...
|
1302
|
-
- `CVInteger`: same as `CVReal` but the number must be an integer. Can be used to represent a floor in a lift, a signed quantity...
|
1303
|
-
- `CVPositiveInteger`: same as `CVInteger` but the number must be positive. Can be used to represent an age, a quantity,...
|
1304
|
-
|
1305
|
-
You will also find all the functions to convert from one brand to another. Do not hesitate to take a look at the [API](https://parischap.github.io/effect-libs/docs/conversions) to learn more about what this module offers in terms of branding.
|
package/package.json
CHANGED
@@ -13,8 +13,17 @@
|
|
13
13
|
],
|
14
14
|
"keywords": [
|
15
15
|
"number",
|
16
|
-
"
|
16
|
+
"date",
|
17
|
+
"format",
|
18
|
+
"parse",
|
19
|
+
"formatting",
|
20
|
+
"parsing",
|
21
|
+
"rounding",
|
22
|
+
"templating",
|
17
23
|
"conversion",
|
24
|
+
"sscanf",
|
25
|
+
"sprintf",
|
26
|
+
"text",
|
18
27
|
"n2t",
|
19
28
|
"num2text",
|
20
29
|
"convert",
|
@@ -24,7 +33,7 @@
|
|
24
33
|
"typescript",
|
25
34
|
"functional-programming"
|
26
35
|
],
|
27
|
-
"description": "A functional library to
|
36
|
+
"description": "A functional library to replace partially the native Intl API",
|
28
37
|
"module": "./esm/index.js",
|
29
38
|
"exports": {
|
30
39
|
".": {
|
@@ -43,7 +52,7 @@
|
|
43
52
|
"directory": "packages/conversions"
|
44
53
|
},
|
45
54
|
"homepage": "https://github.com/parischap/effect-libs/tree/master/packages/conversions",
|
46
|
-
"version": "0.
|
55
|
+
"version": "0.4.0",
|
47
56
|
"main": "./cjs/index.js",
|
48
57
|
"types": "./dts/index.d.ts"
|
49
58
|
}
|