@bereasoftware/time-guard 2.1.0 → 2.2.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.en.md +294 -0
- package/README.md +415 -2
- package/dist/calendars/index.es.js +1 -1
- package/dist/locales/index.es.js +1 -1
- package/dist/plugins/advanced-format.es.js +1 -1
- package/dist/plugins/duration.es.js +1 -1
- package/dist/plugins/relative-time.es.js +1 -1
- package/dist/time-guard.cjs +1 -1
- package/dist/time-guard.es.js +477 -67
- package/dist/time-guard.iife.js +1 -1
- package/dist/time-guard.umd.js +1 -1
- package/dist/types/index.d.ts +3 -2
- package/dist/types/time-guard.d.ts +285 -6
- package/dist/types/types/index.d.ts +280 -3
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -227,6 +227,120 @@ console.log(date.inLeapYear()); // true
|
|
|
227
227
|
|
|
228
228
|
## ⏱️ Advanced Calculations
|
|
229
229
|
|
|
230
|
+
### 🎯 Semantic API: `between()` - Zero Mental Load
|
|
231
|
+
|
|
232
|
+
The **most semantic and clear** way to calculate time differences. No need to think about `until()` vs `since()`!
|
|
233
|
+
|
|
234
|
+
```typescript
|
|
235
|
+
const start = TimeGuard.from("2024-01-15");
|
|
236
|
+
const end = TimeGuard.from("2024-03-20");
|
|
237
|
+
|
|
238
|
+
// Simple and direct reading
|
|
239
|
+
TimeGuard.between(start, end).humanize();
|
|
240
|
+
// "2 months and 5 days"
|
|
241
|
+
|
|
242
|
+
// Order doesn't matter - always positive
|
|
243
|
+
TimeGuard.between(end, start).humanize();
|
|
244
|
+
// "2 months and 5 days" (identical!)
|
|
245
|
+
|
|
246
|
+
// Access DurationParts properties
|
|
247
|
+
TimeGuard.between(start, end).months; // 2
|
|
248
|
+
TimeGuard.between(start, end).days; // 5
|
|
249
|
+
|
|
250
|
+
// All humanize() options available
|
|
251
|
+
TimeGuard.between(start, end).humanize({ locale: "es", fullBreakdown: true });
|
|
252
|
+
// "2 meses y 5 días"
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
**Semantic comparison:**
|
|
256
|
+
|
|
257
|
+
```typescript
|
|
258
|
+
// ❌ Confusing: which is which?
|
|
259
|
+
start.until(end); // is it positive?
|
|
260
|
+
end.since(start); // is it different?
|
|
261
|
+
|
|
262
|
+
// ✅ Clear: no thinking required
|
|
263
|
+
TimeGuard.between(start, end); // always positive, no confusion
|
|
264
|
+
TimeGuard.between(end, start); // same result, order doesn't matter
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### 🔗 Fluent API: `range()` - Naming Killer
|
|
268
|
+
|
|
269
|
+
**Pure technical marketing:** The most intuitive API for date ranges with **fluent method chaining**:
|
|
270
|
+
|
|
271
|
+
```typescript
|
|
272
|
+
// Clean and straightforward syntax
|
|
273
|
+
TimeGuard.range("2024-01-15", "2024-03-20").humanize();
|
|
274
|
+
// "2 months and 5 days"
|
|
275
|
+
|
|
276
|
+
TimeGuard.range("2024-01-15", "2024-03-20").inMonths();
|
|
277
|
+
// 2.1355 (precise decimal value)
|
|
278
|
+
|
|
279
|
+
TimeGuard.range("2024-01-15", "2024-03-20").toDuration();
|
|
280
|
+
// DurationResult: complete property access
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
**Business use cases:**
|
|
284
|
+
|
|
285
|
+
```typescript
|
|
286
|
+
// 📅 Rental period
|
|
287
|
+
const checkIn = new TimeGuard("2024-06-15");
|
|
288
|
+
const checkOut = new TimeGuard("2024-06-22");
|
|
289
|
+
|
|
290
|
+
const rentalDays = TimeGuard.range(checkIn, checkOut).in("day");
|
|
291
|
+
const rentalCost = rentalDays * 100; // $100 per day
|
|
292
|
+
console.log(rentalCost); // $700
|
|
293
|
+
|
|
294
|
+
// 💳 Late fee calculation
|
|
295
|
+
const invoiceDate = new TimeGuard("2024-01-01");
|
|
296
|
+
const paymentDate = new TimeGuard("2024-02-15");
|
|
297
|
+
|
|
298
|
+
const lateFeePerDay = 10;
|
|
299
|
+
const lateFee =
|
|
300
|
+
TimeGuard.range(invoiceDate, paymentDate).in("day") * lateFeePerDay;
|
|
301
|
+
console.log(lateFee); // Exact calculation with month averaging
|
|
302
|
+
|
|
303
|
+
// 📊 Session analytics
|
|
304
|
+
const sessionStart = new TimeGuard("2024-03-15T10:00:00");
|
|
305
|
+
const sessionEnd = new TimeGuard("2024-03-15T10:35:42");
|
|
306
|
+
|
|
307
|
+
const engagementMinutes = TimeGuard.range(sessionStart, sessionEnd).in(
|
|
308
|
+
"minute",
|
|
309
|
+
);
|
|
310
|
+
console.log(engagementMinutes); // For user metrics
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
**Locale support:**
|
|
314
|
+
|
|
315
|
+
```typescript
|
|
316
|
+
// Date order doesn't matter
|
|
317
|
+
TimeGuard.range("2024-03-20", "2024-01-15").humanize({ locale: "es" });
|
|
318
|
+
// "2 meses y 5 días" (identical result)
|
|
319
|
+
|
|
320
|
+
// Different languages
|
|
321
|
+
TimeGuard.range("2024-01-15", "2024-03-20").humanize({ locale: "fr" });
|
|
322
|
+
// "2 mois et 5 jours"
|
|
323
|
+
|
|
324
|
+
TimeGuard.range("2024-01-15", "2024-03-20").humanize({ locale: "de" });
|
|
325
|
+
// "2 Monate und 5 Tage"
|
|
326
|
+
|
|
327
|
+
// Full breakdown
|
|
328
|
+
TimeGuard.range("2024-01-15", "2024-03-20").humanize({
|
|
329
|
+
locale: "es",
|
|
330
|
+
fullBreakdown: true,
|
|
331
|
+
});
|
|
332
|
+
// "2 meses y 5 días"
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
**Available methods on `TimeRange`:**
|
|
336
|
+
|
|
337
|
+
| Method | Returns | Description |
|
|
338
|
+
| --------------------- | ---------------- | ------------------------------------------- |
|
|
339
|
+
| `.toDuration()` | `DurationResult` | Complete duration object with all methods |
|
|
340
|
+
| `.inMonths()` | `number` | Range in months (decimal: 2.1355) |
|
|
341
|
+
| `.humanize(options?)` | `string` | Human-readable text: "2 months and 5 days" |
|
|
342
|
+
| `.in(unit)` | `number` | Range in any unit (day, hour, minute, etc.) |
|
|
343
|
+
|
|
230
344
|
### Duration: Calculate time between dates
|
|
231
345
|
|
|
232
346
|
```typescript
|
|
@@ -247,6 +361,186 @@ console.log(duration);
|
|
|
247
361
|
// }
|
|
248
362
|
```
|
|
249
363
|
|
|
364
|
+
### Humanize: Human-Readable Duration 📝
|
|
365
|
+
|
|
366
|
+
Convert durations to natural, user-friendly text with multi-language support:
|
|
367
|
+
|
|
368
|
+
```typescript
|
|
369
|
+
const start = TimeGuard.from("2024-01-15");
|
|
370
|
+
const end = TimeGuard.from("2024-03-20");
|
|
371
|
+
|
|
372
|
+
// Simple style (Intl.RelativeTimeFormat)
|
|
373
|
+
const duration = start.until(end);
|
|
374
|
+
console.log(duration.humanize());
|
|
375
|
+
// "2 months"
|
|
376
|
+
|
|
377
|
+
// Full breakdown with multiple units
|
|
378
|
+
console.log(duration.humanize({ fullBreakdown: true }));
|
|
379
|
+
// "2 months and 5 days"
|
|
380
|
+
|
|
381
|
+
// With specific locale
|
|
382
|
+
console.log(duration.humanize({ locale: "es" }));
|
|
383
|
+
// "2 meses"
|
|
384
|
+
|
|
385
|
+
console.log(duration.humanize({ locale: "es", fullBreakdown: true }));
|
|
386
|
+
// "2 meses y 5 días"
|
|
387
|
+
|
|
388
|
+
// With French locale
|
|
389
|
+
console.log(duration.humanize({ locale: "fr" }));
|
|
390
|
+
// "2 mois"
|
|
391
|
+
|
|
392
|
+
console.log(duration.humanize({ locale: "fr", fullBreakdown: true }));
|
|
393
|
+
// "2 mois et 5 jours"
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
#### Using Inherited Locales
|
|
397
|
+
|
|
398
|
+
```typescript
|
|
399
|
+
// TimeGuard inherits locale configuration
|
|
400
|
+
const start = TimeGuard.from("2024-01-15", { locale: "es" });
|
|
401
|
+
const end = TimeGuard.from("2024-03-20");
|
|
402
|
+
|
|
403
|
+
const duration = start.until(end);
|
|
404
|
+
|
|
405
|
+
// Automatically uses the instance's locale
|
|
406
|
+
console.log(duration.humanize());
|
|
407
|
+
// "2 meses"
|
|
408
|
+
|
|
409
|
+
console.log(duration.humanize({ fullBreakdown: true }));
|
|
410
|
+
// "2 meses y 5 días"
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
#### Humanize Options
|
|
414
|
+
|
|
415
|
+
| Option | Type | Default | Description |
|
|
416
|
+
| --------------- | ------------------ | -------------- | ----------------------------------------- |
|
|
417
|
+
| `locale` | string | from TimeGuard | Locale code ('en', 'es', 'fr', etc.) |
|
|
418
|
+
| `fullBreakdown` | boolean | false | Show full breakdown vs. largest unit only |
|
|
419
|
+
| `numeric` | 'always' \| 'auto' | 'always' | Numeric format for Intl API |
|
|
420
|
+
|
|
421
|
+
#### Supported Locales
|
|
422
|
+
|
|
423
|
+
Humanize automatically supports:
|
|
424
|
+
|
|
425
|
+
- 🇬🇧 **en** - English
|
|
426
|
+
- 🇪🇸 **es** - Español
|
|
427
|
+
- 🇫🇷 **fr** - Français (French)
|
|
428
|
+
- 🇩🇪 **de** - Deutsch (German)
|
|
429
|
+
- 🇮🇹 **it** - Italiano (Italian)
|
|
430
|
+
- 🇵🇹 **pt** - Português (Portuguese)
|
|
431
|
+
|
|
432
|
+
And many more through `Intl.RelativeTimeFormat`.
|
|
433
|
+
|
|
434
|
+
### 💥 Explain Mode: Brutal Debugging and Education
|
|
435
|
+
|
|
436
|
+
**Killer feature for debugging and teaching** - Get a detailed explanation of how each duration was calculated.
|
|
437
|
+
|
|
438
|
+
Perfect for:
|
|
439
|
+
|
|
440
|
+
- 🐛 **Debugging** - Understand exactly how duration was calculated
|
|
441
|
+
- 📚 **Education** - Teach date math to students
|
|
442
|
+
- 📋 **Auditing** - Verify date-based business logic
|
|
443
|
+
- ✅ **Validation** - Confirm calculations are correct
|
|
444
|
+
|
|
445
|
+
```typescript
|
|
446
|
+
const start = TimeGuard.from("2024-01-15");
|
|
447
|
+
const end = TimeGuard.from("2024-03-20");
|
|
448
|
+
const duration = start.until(end);
|
|
449
|
+
|
|
450
|
+
// Get detailed explanation of the calculation
|
|
451
|
+
const explanation = duration.explain();
|
|
452
|
+
|
|
453
|
+
console.log(explanation);
|
|
454
|
+
// {
|
|
455
|
+
// input: [
|
|
456
|
+
// '2024-01-15T00:00:00',
|
|
457
|
+
// '2024-03-20T00:00:00'
|
|
458
|
+
// ],
|
|
459
|
+
// steps: [
|
|
460
|
+
// 'Parsed dates: 2024-01-15 (day 15 of 365) to 2024-03-20 (day 80 of 365)',
|
|
461
|
+
// '2024 is a leap year (February has 29 days)',
|
|
462
|
+
// 'Years: 0',
|
|
463
|
+
// 'Months: 2',
|
|
464
|
+
// 'Days: 5',
|
|
465
|
+
// 'Total: 2m 5d'
|
|
466
|
+
// ],
|
|
467
|
+
// breakdown: {
|
|
468
|
+
// years: 0,
|
|
469
|
+
// months: 2,
|
|
470
|
+
// weeks: 0,
|
|
471
|
+
// days: 5,
|
|
472
|
+
// hours: 0,
|
|
473
|
+
// minutes: 0,
|
|
474
|
+
// seconds: 0,
|
|
475
|
+
// milliseconds: 0
|
|
476
|
+
// },
|
|
477
|
+
// mode: 'exact',
|
|
478
|
+
// explanation: 'Calculated 2024-01-15 to 2024-03-20. 2024 is a leap year. Breakdown: 0 year(s), 2 month(s), 5 day(s). Mode: exact calculation',
|
|
479
|
+
// locale: 'en',
|
|
480
|
+
// leapYearFlags: [
|
|
481
|
+
// { year: 2024, isLeap: true, daysInFebruary: 29 }
|
|
482
|
+
// ],
|
|
483
|
+
// metadata: {
|
|
484
|
+
// calculationTimeMs: 0.5,
|
|
485
|
+
// precision: 'day'
|
|
486
|
+
// }
|
|
487
|
+
// }
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
#### Use Cases
|
|
491
|
+
|
|
492
|
+
Debugging date logic:
|
|
493
|
+
|
|
494
|
+
```typescript
|
|
495
|
+
// Why is the late fee calculation different?
|
|
496
|
+
const invoiceDate = TimeGuard.from("2024-01-05");
|
|
497
|
+
const paymentDate = TimeGuard.from("2024-02-15");
|
|
498
|
+
const lateFee = invoiceDate.until(paymentDate);
|
|
499
|
+
|
|
500
|
+
// Get the complete explanation
|
|
501
|
+
const debug = lateFee.explain();
|
|
502
|
+
|
|
503
|
+
console.log("Calculation steps:", debug.steps);
|
|
504
|
+
console.log("February is leap:", debug.leapYearFlags);
|
|
505
|
+
console.log("Breakdown:", debug.breakdown);
|
|
506
|
+
// Now you know exactly what happened!
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
Education - Teaching date calculations:
|
|
510
|
+
|
|
511
|
+
```typescript
|
|
512
|
+
// Show students how it works
|
|
513
|
+
const start = TimeGuard.from("2024-02-15");
|
|
514
|
+
const end = TimeGuard.from("2024-04-15");
|
|
515
|
+
const duration = start.until(end);
|
|
516
|
+
|
|
517
|
+
const explanation = duration.explain();
|
|
518
|
+
|
|
519
|
+
// Print all steps
|
|
520
|
+
explanation.steps.forEach((step, i) => {
|
|
521
|
+
console.log(`${i + 1}. ${step}`);
|
|
522
|
+
});
|
|
523
|
+
// 1. Parsed dates: ...
|
|
524
|
+
// 2. 2024 is a leap year...
|
|
525
|
+
// 3. Years: 0
|
|
526
|
+
// 4. Months: 2
|
|
527
|
+
// 5. Days: 0
|
|
528
|
+
// 6. Total: 2m
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
#### Explanation Properties
|
|
532
|
+
|
|
533
|
+
| Property | Type | Description |
|
|
534
|
+
| ---------------- | ------------------------ | -------------------------------- |
|
|
535
|
+
| `input` | `string[]` | Parsed input dates |
|
|
536
|
+
| `steps` | `string[]` | Human-readable calculation steps |
|
|
537
|
+
| `breakdown` | `DurationParts` | Component breakdown |
|
|
538
|
+
| `mode` | `'exact' \| 'estimated'` | Calculation type |
|
|
539
|
+
| `explanation` | `string` | Natural language summary |
|
|
540
|
+
| `locale` | `string` | Explanation language |
|
|
541
|
+
| `leapYearFlags?` | `Array` | Leap year information |
|
|
542
|
+
| `metadata?` | `Object` | Performance and precision |
|
|
543
|
+
|
|
250
544
|
### Round: Precision control
|
|
251
545
|
|
|
252
546
|
```typescript
|