@bereasoftware/time-guard 2.1.0 → 2.2.1

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 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