@indodev/toolkit 0.3.3 → 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/dist/index.d.cts CHANGED
@@ -4,5 +4,6 @@ export { NPWPInfo, MaskOptions as NPWPMaskOptions, formatNPWP, maskNPWP, parseNP
4
4
  export { f as formatPlate, g as getRegionFromPlate, v as validatePlate } from './utils-DDVlOusI.cjs';
5
5
  export { V as VINOptions, a as VINValidationResult, v as validateVIN } from './types-i5e6R0AS.cjs';
6
6
  export { c as EmailInfo, b as EmailMaskOptions, E as EmailValidationOptions, a as EmailValidationResult, g as getEmailInfo, m as maskEmail, n as normalizeEmail, v as validateEmail } from './email-validator-R9L5unIw.cjs';
7
- export { RupiahOptions, WordOptions, addRupiahSymbol, calculateTax, formatAccounting, formatCompact, formatRupiah, parseRupiah, roundToClean, toWords } from './currency/index.cjs';
7
+ export { R as RupiahOptions, W as WordOptions, d as addRupiahSymbol, c as calculateTax, b as formatAccounting, a as formatCompact, f as formatRupiah, p as parseRupiah, r as roundToClean, t as toWords } from './utils-OG1yMaAa.cjs';
8
8
  export { C as CompareOptions, E as ExtractOptions, m as SanitizeOptions, S as SlugifyOptions, T as TitleCaseOptions, o as TruncateOptions, c as capitalize, k as compareStrings, d as contractAbbreviation, e as expandAbbreviation, g as extractWords, j as isAlay, n as normalizeWhitespace, p as profanityFilter, r as removeAccents, h as removeStopwords, b as sanitize, l as similarity, s as slugify, i as toFormal, a as toSentenceCase, t as toTitleCase, f as truncate } from './compare-Ku_8OhuU.cjs';
9
+ export { AgeOptions, AgeResult, DAY_NAMES, DAY_NAMES_SHORT, DateStyle, InvalidDateError, InvalidDateRangeError, MONTH_NAMES, MONTH_NAMES_SHORT, TIMEZONE_MAP, VALID_UTC_OFFSETS, daysInMonth, formatDate, formatDateRange, getAge as getAgeFromDate, getIndonesianTimezone, isLeapYear, isValidDate, isWeekend, isWorkingDay, parseDate, toRelativeTime } from './datetime/index.cjs';
package/dist/index.d.ts CHANGED
@@ -4,5 +4,6 @@ export { NPWPInfo, MaskOptions as NPWPMaskOptions, formatNPWP, maskNPWP, parseNP
4
4
  export { f as formatPlate, g as getRegionFromPlate, v as validatePlate } from './utils-DDVlOusI.js';
5
5
  export { V as VINOptions, a as VINValidationResult, v as validateVIN } from './types-i5e6R0AS.js';
6
6
  export { c as EmailInfo, b as EmailMaskOptions, E as EmailValidationOptions, a as EmailValidationResult, g as getEmailInfo, m as maskEmail, n as normalizeEmail, v as validateEmail } from './email-validator-R9L5unIw.js';
7
- export { RupiahOptions, WordOptions, addRupiahSymbol, calculateTax, formatAccounting, formatCompact, formatRupiah, parseRupiah, roundToClean, toWords } from './currency/index.js';
7
+ export { R as RupiahOptions, W as WordOptions, d as addRupiahSymbol, c as calculateTax, b as formatAccounting, a as formatCompact, f as formatRupiah, p as parseRupiah, r as roundToClean, t as toWords } from './utils-OG1yMaAa.js';
8
8
  export { C as CompareOptions, E as ExtractOptions, m as SanitizeOptions, S as SlugifyOptions, T as TitleCaseOptions, o as TruncateOptions, c as capitalize, k as compareStrings, d as contractAbbreviation, e as expandAbbreviation, g as extractWords, j as isAlay, n as normalizeWhitespace, p as profanityFilter, r as removeAccents, h as removeStopwords, b as sanitize, l as similarity, s as slugify, i as toFormal, a as toSentenceCase, t as toTitleCase, f as truncate } from './compare-Ku_8OhuU.js';
9
+ export { AgeOptions, AgeResult, DAY_NAMES, DAY_NAMES_SHORT, DateStyle, InvalidDateError, InvalidDateRangeError, MONTH_NAMES, MONTH_NAMES_SHORT, TIMEZONE_MAP, VALID_UTC_OFFSETS, daysInMonth, formatDate, formatDateRange, getAge as getAgeFromDate, getIndonesianTimezone, isLeapYear, isValidDate, isWeekend, isWorkingDay, parseDate, toRelativeTime } from './datetime/index.js';
package/dist/index.js CHANGED
@@ -1332,7 +1332,7 @@ function formatRupiah(amount, options) {
1332
1332
  spaceAfterSymbol = true
1333
1333
  } = options || {};
1334
1334
  const precision = options?.precision !== void 0 ? options.precision : decimal ? 2 : 0;
1335
- const isNegative = amount < 0;
1335
+ const isNegative = amount < 0 && amount !== 0;
1336
1336
  const absAmount = Math.abs(amount);
1337
1337
  let result;
1338
1338
  if (decimal) {
@@ -1350,17 +1350,21 @@ function formatRupiah(amount, options) {
1350
1350
  const intAmount = Math.floor(absAmount);
1351
1351
  result = intAmount.toString().replace(/\B(?=(\d{3})+(?!\d))/g, separator);
1352
1352
  }
1353
- if (isNegative) {
1354
- result = `-${result}`;
1355
- }
1356
1353
  if (symbol) {
1357
1354
  const space = spaceAfterSymbol ? " " : "";
1358
- result = `Rp${space}${result}`;
1355
+ if (isNegative) {
1356
+ result = `-Rp${space}${result}`;
1357
+ } else {
1358
+ result = `Rp${space}${result}`;
1359
+ }
1360
+ } else if (isNegative) {
1361
+ result = `-${result}`;
1359
1362
  }
1360
1363
  return result;
1361
1364
  }
1362
- function formatCompact(amount) {
1363
- const isNegative = amount < 0;
1365
+ function formatCompact(amount, options) {
1366
+ const { symbol = true, spaceAfterSymbol = true } = options || {};
1367
+ const isNegative = amount < 0 && amount !== 0;
1364
1368
  const abs = Math.abs(amount);
1365
1369
  let result;
1366
1370
  if (abs >= 1e12) {
@@ -1376,10 +1380,17 @@ function formatCompact(amount) {
1376
1380
  } else {
1377
1381
  result = abs.toString();
1378
1382
  }
1379
- if (isNegative) {
1383
+ if (symbol) {
1384
+ const space = spaceAfterSymbol ? " " : "";
1385
+ if (isNegative) {
1386
+ result = `-Rp${space}${result}`;
1387
+ } else {
1388
+ result = `Rp${space}${result}`;
1389
+ }
1390
+ } else if (isNegative) {
1380
1391
  result = `-${result}`;
1381
1392
  }
1382
- return `Rp ${result}`;
1393
+ return result;
1383
1394
  }
1384
1395
  function formatCompactValue(value, unit) {
1385
1396
  const rounded = Math.round(value * 10) / 10;
@@ -1476,20 +1487,42 @@ var TENS = [
1476
1487
  "sembilan puluh"
1477
1488
  ];
1478
1489
  function toWords(amount, options) {
1479
- const { uppercase = false, withCurrency = true } = options || {};
1490
+ const {
1491
+ uppercase = false,
1492
+ withCurrency = true,
1493
+ withDecimals = false
1494
+ } = options || {};
1480
1495
  if (amount === 0) {
1481
1496
  let result = "nol";
1482
1497
  if (withCurrency) result += " rupiah";
1483
1498
  return uppercase ? capitalize(result) : result;
1484
1499
  }
1485
1500
  const isNegative = amount < 0;
1486
- const absAmount = Math.floor(Math.abs(amount));
1501
+ const absAmount = Math.abs(amount);
1502
+ const intPart = Math.floor(absAmount);
1503
+ let words = convertInteger(intPart);
1504
+ if (isNegative) {
1505
+ words = "minus " + words;
1506
+ }
1507
+ if (withCurrency) {
1508
+ words += " rupiah";
1509
+ }
1510
+ if (withDecimals) {
1511
+ const decimalPart = Math.round((absAmount - intPart) * 100);
1512
+ if (decimalPart > 0) {
1513
+ words += " koma " + convertDecimal(decimalPart);
1514
+ }
1515
+ }
1516
+ return uppercase ? capitalize(words) : words;
1517
+ }
1518
+ function convertInteger(num) {
1519
+ if (num === 0) return "nol";
1487
1520
  let words = "";
1488
- const triliun = Math.floor(absAmount / 1e12);
1489
- const miliar = Math.floor(absAmount % 1e12 / 1e9);
1490
- const juta = Math.floor(absAmount % 1e9 / 1e6);
1491
- const ribu = Math.floor(absAmount % 1e6 / 1e3);
1492
- const sisa = absAmount % 1e3;
1521
+ const triliun = Math.floor(num / 1e12);
1522
+ const miliar = Math.floor(num % 1e12 / 1e9);
1523
+ const juta = Math.floor(num % 1e9 / 1e6);
1524
+ const ribu = Math.floor(num % 1e6 / 1e3);
1525
+ const sisa = num % 1e3;
1493
1526
  if (triliun > 0) {
1494
1527
  words += convertGroup(triliun) + " triliun";
1495
1528
  }
@@ -1509,13 +1542,19 @@ function toWords(amount, options) {
1509
1542
  if (words) words += " ";
1510
1543
  words += convertGroup(sisa);
1511
1544
  }
1512
- if (isNegative) {
1513
- words = "minus " + words;
1514
- }
1515
- if (withCurrency) {
1516
- words += " rupiah";
1545
+ return words;
1546
+ }
1547
+ function convertDecimal(num) {
1548
+ if (num === 0) return "";
1549
+ if (num < 10) return BASIC_NUMBERS[num];
1550
+ if (num < 20) return TEENS[num - 10];
1551
+ const tens = Math.floor(num / 10);
1552
+ const ones = num % 10;
1553
+ let result = TENS[tens];
1554
+ if (ones > 0) {
1555
+ result += " " + BASIC_NUMBERS[ones];
1517
1556
  }
1518
- return uppercase ? capitalize(words) : words;
1557
+ return result;
1519
1558
  }
1520
1559
  function convertGroup(num) {
1521
1560
  if (num === 0) return "";
@@ -1565,15 +1604,16 @@ function formatAccounting(amount, options) {
1565
1604
  }
1566
1605
  return formatted;
1567
1606
  }
1568
- function calculateTax(amount, rate = 0.11) {
1607
+ function calculateTax(amount, rate) {
1569
1608
  return amount * rate;
1570
1609
  }
1571
1610
  function addRupiahSymbol(amount) {
1572
1611
  if (typeof amount === "number") {
1573
- return `Rp ${amount.toLocaleString("id-ID")}`;
1612
+ const formatted = amount.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ".");
1613
+ return `Rp ${formatted}`;
1574
1614
  }
1575
1615
  if (amount.trim().startsWith("Rp")) {
1576
- return amount;
1616
+ return amount.trim();
1577
1617
  }
1578
1618
  return `Rp ${amount.trim()}`;
1579
1619
  }
@@ -3280,6 +3320,425 @@ function similarity(str1, str2) {
3280
3320
  return 1 - distance / maxLength;
3281
3321
  }
3282
3322
 
3283
- export { addRupiahSymbol, calculateTax, capitalize2 as capitalize, cleanPhoneNumber, compareStrings, contractAbbreviation, expandAbbreviation, extractWords, formatAccounting, formatBirthDate, formatCompact, formatNIK, formatNPWP, formatPhoneNumber, formatPlate, formatRupiah, generateSmsLink, generateTelLink, generateWALink, getAge, getEmailInfo, getOperator, getRegionFromPlate, isAlay, isLandlineNumber, isMobileNumber, isProvider, isValidForBirthDate, isValidForGender, maskEmail, maskNIK, maskNPWP, maskPhoneNumber, normalizeEmail, normalizeWhitespace, parseNIK, parseNPWP, parsePhoneNumber, parseRupiah, profanityFilter, removeAccents, removeStopwords, roundToClean, sanitize, similarity, slugify, toE164, toFormal, toInternational, toNational, toSentenceCase, toTitleCase, toWords, truncate, validateEmail, validateNIK, validateNPWP, validatePhoneNumber, validatePlate, validateVIN };
3323
+ // src/datetime/types.ts
3324
+ var InvalidDateError = class extends Error {
3325
+ constructor(message = "Invalid date provided") {
3326
+ super(message);
3327
+ /** Error code for programmatic identification */
3328
+ this.code = "INVALID_DATE";
3329
+ this.name = "InvalidDateError";
3330
+ }
3331
+ };
3332
+ var InvalidDateRangeError = class extends Error {
3333
+ constructor(message = "End date must be after start date") {
3334
+ super(message);
3335
+ /** Error code for programmatic identification */
3336
+ this.code = "INVALID_DATE_RANGE";
3337
+ this.name = "InvalidDateRangeError";
3338
+ }
3339
+ };
3340
+
3341
+ // src/datetime/constants.ts
3342
+ var MONTH_NAMES = [
3343
+ "",
3344
+ // Placeholder for 0-index
3345
+ "Januari",
3346
+ "Februari",
3347
+ "Maret",
3348
+ "April",
3349
+ "Mei",
3350
+ "Juni",
3351
+ "Juli",
3352
+ "Agustus",
3353
+ "September",
3354
+ "Oktober",
3355
+ "November",
3356
+ "Desember"
3357
+ ];
3358
+ var MONTH_NAMES_SHORT = [
3359
+ "",
3360
+ // Placeholder for 0-index
3361
+ "Jan",
3362
+ "Feb",
3363
+ "Mar",
3364
+ "Apr",
3365
+ "Mei",
3366
+ "Jun",
3367
+ "Jul",
3368
+ "Agu",
3369
+ "Sep",
3370
+ "Okt",
3371
+ "Nov",
3372
+ "Des"
3373
+ ];
3374
+ var DAY_NAMES = [
3375
+ "Minggu",
3376
+ "Senin",
3377
+ "Selasa",
3378
+ "Rabu",
3379
+ "Kamis",
3380
+ "Jumat",
3381
+ "Sabtu"
3382
+ ];
3383
+ var DAY_NAMES_SHORT = [
3384
+ "Min",
3385
+ "Sen",
3386
+ "Sel",
3387
+ "Rab",
3388
+ "Kam",
3389
+ "Jum",
3390
+ "Sab"
3391
+ ];
3392
+ var TIMEZONE_MAP = {
3393
+ // UTC+7 - WIB
3394
+ "Asia/Jakarta": "WIB",
3395
+ "Asia/Pontianak": "WIB",
3396
+ // UTC+8 - WITA
3397
+ "Asia/Makassar": "WITA",
3398
+ "Asia/Denpasar": "WITA",
3399
+ "Asia/Manado": "WITA",
3400
+ "Asia/Palu": "WITA",
3401
+ // UTC+9 - WIT
3402
+ "Asia/Jayapura": "WIT"
3403
+ };
3404
+ var VALID_UTC_OFFSETS = [7, 8, 9];
3405
+
3406
+ // src/datetime/calc.ts
3407
+ function isLeapYear(year) {
3408
+ if (!Number.isFinite(year) || !Number.isInteger(year)) {
3409
+ return false;
3410
+ }
3411
+ return year % 4 === 0 && year % 100 !== 0 || year % 400 === 0;
3412
+ }
3413
+ function daysInMonth(month, year) {
3414
+ if (!Number.isFinite(month) || !Number.isInteger(month) || !Number.isFinite(year) || !Number.isInteger(year) || month < 1 || month > 12) {
3415
+ return 0;
3416
+ }
3417
+ if ([1, 3, 5, 7, 8, 10, 12].includes(month)) {
3418
+ return 31;
3419
+ }
3420
+ if ([4, 6, 9, 11].includes(month)) {
3421
+ return 30;
3422
+ }
3423
+ return isLeapYear(year) ? 29 : 28;
3424
+ }
3425
+ function isValidDate(date) {
3426
+ return date instanceof Date && !Number.isNaN(date.getTime());
3427
+ }
3428
+ function isWeekend(date) {
3429
+ const day = date.getDay();
3430
+ return day === 0 || day === 6;
3431
+ }
3432
+ function isWorkingDay(date) {
3433
+ const day = date.getDay();
3434
+ return day >= 1 && day <= 5;
3435
+ }
3436
+ function normalizeDate(date) {
3437
+ let result;
3438
+ if (date instanceof Date) {
3439
+ result = date;
3440
+ } else if (typeof date === "number") {
3441
+ result = new Date(date);
3442
+ } else if (typeof date === "string") {
3443
+ result = new Date(date);
3444
+ } else {
3445
+ throw new InvalidDateError("Date must be a Date, string, or number");
3446
+ }
3447
+ if (Number.isNaN(result.getTime())) {
3448
+ throw new InvalidDateError(`Unable to parse date: ${String(date)}`);
3449
+ }
3450
+ return result;
3451
+ }
3452
+ function getAge2(birthDate, options = {}) {
3453
+ const birth = normalizeDate(birthDate);
3454
+ const from = options.fromDate ? normalizeDate(options.fromDate) : /* @__PURE__ */ new Date();
3455
+ if (birth.getTime() > from.getTime()) {
3456
+ throw new InvalidDateError(
3457
+ "Birth date cannot be in the future relative to fromDate"
3458
+ );
3459
+ }
3460
+ let years = from.getFullYear() - birth.getFullYear();
3461
+ let months = from.getMonth() - birth.getMonth();
3462
+ let days = from.getDate() - birth.getDate();
3463
+ if (days < 0) {
3464
+ months--;
3465
+ const prevMonth = from.getMonth() === 0 ? 11 : from.getMonth() - 1;
3466
+ const prevMonthYear = from.getMonth() === 0 ? from.getFullYear() - 1 : from.getFullYear();
3467
+ days += daysInMonth(prevMonth + 1, prevMonthYear);
3468
+ }
3469
+ if (months < 0) {
3470
+ years--;
3471
+ months += 12;
3472
+ }
3473
+ const result = { years, months, days };
3474
+ if (options.asString) {
3475
+ return formatAgeString(result);
3476
+ }
3477
+ return result;
3478
+ }
3479
+ function formatAgeString(age) {
3480
+ const parts = [];
3481
+ if (age.years > 0) {
3482
+ parts.push(`${age.years} Tahun`);
3483
+ }
3484
+ if (age.months > 0) {
3485
+ parts.push(`${age.months} Bulan`);
3486
+ }
3487
+ if (age.days > 0) {
3488
+ parts.push(`${age.days} Hari`);
3489
+ }
3490
+ if (parts.length === 0) {
3491
+ return "0 Hari";
3492
+ }
3493
+ return parts.join(" ");
3494
+ }
3495
+
3496
+ // src/datetime/parse.ts
3497
+ function parseDate(dateStr) {
3498
+ const trimmed = dateStr.trim();
3499
+ if (!trimmed) {
3500
+ return null;
3501
+ }
3502
+ if (trimmed.includes(" ") || trimmed.includes(":")) {
3503
+ return null;
3504
+ }
3505
+ const parts = trimmed.split(/[-/.]/);
3506
+ if (parts.length !== 3) {
3507
+ return null;
3508
+ }
3509
+ const nums = parts.map((p) => parseInt(p, 10));
3510
+ if (nums.some((n) => Number.isNaN(n) || n < 0)) {
3511
+ return null;
3512
+ }
3513
+ const [first, second, third] = nums;
3514
+ let day;
3515
+ let month;
3516
+ let year;
3517
+ if (first > 999 && first > 31) {
3518
+ year = first;
3519
+ month = second;
3520
+ day = third;
3521
+ } else {
3522
+ if (third < 1e3) {
3523
+ return null;
3524
+ }
3525
+ day = first;
3526
+ month = second;
3527
+ year = third;
3528
+ }
3529
+ if (month < 1 || month > 12) {
3530
+ return null;
3531
+ }
3532
+ if (year < 1e3 || year > 9999) {
3533
+ return null;
3534
+ }
3535
+ const maxDays = daysInMonth(month, year);
3536
+ if (maxDays === 0 || day < 1 || day > maxDays) {
3537
+ return null;
3538
+ }
3539
+ const date = new Date(year, month - 1, day);
3540
+ if (date.getFullYear() !== year || date.getMonth() !== month - 1 || date.getDate() !== day) {
3541
+ return null;
3542
+ }
3543
+ return date;
3544
+ }
3545
+
3546
+ // src/datetime/format.ts
3547
+ function normalizeDate2(date) {
3548
+ let result;
3549
+ if (date instanceof Date) {
3550
+ result = date;
3551
+ } else if (typeof date === "number") {
3552
+ result = new Date(date);
3553
+ } else if (typeof date === "string") {
3554
+ result = new Date(date);
3555
+ } else {
3556
+ throw new InvalidDateError("Date must be a Date, string, or number");
3557
+ }
3558
+ if (Number.isNaN(result.getTime())) {
3559
+ throw new InvalidDateError(`Unable to parse date: ${String(date)}`);
3560
+ }
3561
+ return result;
3562
+ }
3563
+ function formatDate(date, style = "long") {
3564
+ const d = normalizeDate2(date);
3565
+ const day = d.getDate();
3566
+ const month = d.getMonth() + 1;
3567
+ const year = d.getFullYear();
3568
+ const dayOfWeek = d.getDay();
3569
+ switch (style) {
3570
+ case "full":
3571
+ return `${DAY_NAMES[dayOfWeek]}, ${day} ${MONTH_NAMES[month]} ${year}`;
3572
+ case "long":
3573
+ return `${day} ${MONTH_NAMES[month]} ${year}`;
3574
+ case "medium":
3575
+ return `${day} ${MONTH_NAMES_SHORT[month]} ${year}`;
3576
+ case "short": {
3577
+ const dd = String(day).padStart(2, "0");
3578
+ const mm = String(month).padStart(2, "0");
3579
+ return `${dd}/${mm}/${year}`;
3580
+ }
3581
+ case "weekday":
3582
+ return DAY_NAMES[dayOfWeek];
3583
+ case "month":
3584
+ return MONTH_NAMES[month];
3585
+ default:
3586
+ throw new InvalidDateError(`Unknown format style: ${style}`);
3587
+ }
3588
+ }
3589
+ function formatDateRange(start, end, style = "long") {
3590
+ const s = normalizeDate2(start);
3591
+ const e = normalizeDate2(end);
3592
+ if (e.getTime() < s.getTime()) {
3593
+ throw new InvalidDateRangeError();
3594
+ }
3595
+ if (style === "short") {
3596
+ return `${formatDate(s, "short")} - ${formatDate(e, "short")}`;
3597
+ }
3598
+ if (style === "full") {
3599
+ return `${formatDate(s, "full")} - ${formatDate(e, "full")}`;
3600
+ }
3601
+ const sDay = s.getDate();
3602
+ const eDay = e.getDate();
3603
+ const sMonth = s.getMonth() + 1;
3604
+ const eMonth = e.getMonth() + 1;
3605
+ const sYear = s.getFullYear();
3606
+ const eYear = e.getFullYear();
3607
+ if (sDay === eDay && sMonth === eMonth && sYear === eYear) {
3608
+ return formatDate(s, style);
3609
+ }
3610
+ if (sYear !== eYear) {
3611
+ return `${formatDate(s, style)} - ${formatDate(e, style)}`;
3612
+ }
3613
+ if (sMonth !== eMonth) {
3614
+ if (style === "long") {
3615
+ return `${sDay} ${MONTH_NAMES[sMonth]} - ${eDay} ${MONTH_NAMES[eMonth]} ${eYear}`;
3616
+ }
3617
+ return `${sDay} ${MONTH_NAMES_SHORT[sMonth]} - ${eDay} ${MONTH_NAMES_SHORT[eMonth]} ${eYear}`;
3618
+ }
3619
+ if (style === "long") {
3620
+ return `${sDay} - ${eDay} ${MONTH_NAMES[eMonth]} ${eYear}`;
3621
+ }
3622
+ return `${sDay} - ${eDay} ${MONTH_NAMES_SHORT[eMonth]} ${eYear}`;
3623
+ }
3624
+
3625
+ // src/datetime/relative.ts
3626
+ function normalizeDate3(date) {
3627
+ let result;
3628
+ if (date instanceof Date) {
3629
+ result = date;
3630
+ } else if (typeof date === "number") {
3631
+ result = new Date(date);
3632
+ } else if (typeof date === "string") {
3633
+ result = new Date(date);
3634
+ } else {
3635
+ throw new InvalidDateError("Date must be a Date, string, or number");
3636
+ }
3637
+ if (Number.isNaN(result.getTime())) {
3638
+ throw new InvalidDateError(`Unable to parse date: ${String(date)}`);
3639
+ }
3640
+ return result;
3641
+ }
3642
+ function toRelativeTime(date, baseDate = /* @__PURE__ */ new Date()) {
3643
+ const d = normalizeDate3(date);
3644
+ const base = normalizeDate3(baseDate);
3645
+ const diffMs = d.getTime() - base.getTime();
3646
+ const diffSec = Math.floor(diffMs / 1e3);
3647
+ const diffMin = Math.floor(diffMs / (1e3 * 60));
3648
+ const diffHour = Math.floor(diffMs / (1e3 * 60 * 60));
3649
+ const diffDay = Math.floor(diffMs / (1e3 * 60 * 60 * 24));
3650
+ if (diffMs === 0) {
3651
+ return "Sekarang";
3652
+ }
3653
+ if (diffMs > 0) {
3654
+ if (diffSec < 60) {
3655
+ return "Baru saja";
3656
+ }
3657
+ if (diffMin < 60) {
3658
+ return `${diffMin} menit lagi`;
3659
+ }
3660
+ if (diffHour < 24) {
3661
+ return `${diffHour} jam lagi`;
3662
+ }
3663
+ if (diffHour < 48) {
3664
+ return "Besok";
3665
+ }
3666
+ if (diffDay <= 30) {
3667
+ return `${diffDay} hari lagi`;
3668
+ }
3669
+ return formatDate(d, "long");
3670
+ }
3671
+ const absDiffSec = Math.abs(diffSec);
3672
+ const absDiffMin = Math.abs(diffMin);
3673
+ const absDiffHour = Math.abs(diffHour);
3674
+ const absDiffDay = Math.abs(diffDay);
3675
+ if (absDiffSec < 60) {
3676
+ return "Baru saja";
3677
+ }
3678
+ if (absDiffMin < 60) {
3679
+ return `${absDiffMin} menit yang lalu`;
3680
+ }
3681
+ if (absDiffHour < 24) {
3682
+ return `${absDiffHour} jam yang lalu`;
3683
+ }
3684
+ if (absDiffHour < 48) {
3685
+ return "Kemarin";
3686
+ }
3687
+ if (absDiffDay <= 30) {
3688
+ return `${absDiffDay} hari yang lalu`;
3689
+ }
3690
+ return formatDate(d, "long");
3691
+ }
3692
+
3693
+ // src/datetime/timezone.ts
3694
+ function getIndonesianTimezone(input) {
3695
+ if (typeof input === "number") {
3696
+ if (!Number.isFinite(input) || !Number.isInteger(input)) {
3697
+ return null;
3698
+ }
3699
+ switch (input) {
3700
+ case 7:
3701
+ return "WIB";
3702
+ case 8:
3703
+ return "WITA";
3704
+ case 9:
3705
+ return "WIT";
3706
+ default:
3707
+ return null;
3708
+ }
3709
+ }
3710
+ if (typeof input !== "string") {
3711
+ return null;
3712
+ }
3713
+ const trimmed = input.trim();
3714
+ const offsetMatch = trimmed.match(/^([+-])(\d{2}):?(\d{2})$/);
3715
+ if (offsetMatch) {
3716
+ const sign = offsetMatch[1];
3717
+ const hours = parseInt(offsetMatch[2], 10);
3718
+ const minutes = parseInt(offsetMatch[3], 10);
3719
+ if (sign === "-") {
3720
+ return null;
3721
+ }
3722
+ if (minutes !== 0) {
3723
+ return null;
3724
+ }
3725
+ switch (hours) {
3726
+ case 7:
3727
+ return "WIB";
3728
+ case 8:
3729
+ return "WITA";
3730
+ case 9:
3731
+ return "WIT";
3732
+ default:
3733
+ return null;
3734
+ }
3735
+ }
3736
+ if (TIMEZONE_MAP[trimmed]) {
3737
+ return TIMEZONE_MAP[trimmed];
3738
+ }
3739
+ return null;
3740
+ }
3741
+
3742
+ export { DAY_NAMES, DAY_NAMES_SHORT, InvalidDateError, InvalidDateRangeError, MONTH_NAMES, MONTH_NAMES_SHORT, TIMEZONE_MAP, VALID_UTC_OFFSETS, addRupiahSymbol, calculateTax, capitalize2 as capitalize, cleanPhoneNumber, compareStrings, contractAbbreviation, daysInMonth, expandAbbreviation, extractWords, formatAccounting, formatBirthDate, formatCompact, formatDate, formatDateRange, formatNIK, formatNPWP, formatPhoneNumber, formatPlate, formatRupiah, generateSmsLink, generateTelLink, generateWALink, getAge, getAge2 as getAgeFromDate, getEmailInfo, getIndonesianTimezone, getOperator, getRegionFromPlate, isAlay, isLandlineNumber, isLeapYear, isMobileNumber, isProvider, isValidDate, isValidForBirthDate, isValidForGender, isWeekend, isWorkingDay, maskEmail, maskNIK, maskNPWP, maskPhoneNumber, normalizeEmail, normalizeWhitespace, parseDate, parseNIK, parseNPWP, parsePhoneNumber, parseRupiah, profanityFilter, removeAccents, removeStopwords, roundToClean, sanitize, similarity, slugify, toE164, toFormal, toInternational, toNational, toRelativeTime, toSentenceCase, toTitleCase, toWords, truncate, validateEmail, validateNIK, validateNPWP, validatePhoneNumber, validatePlate, validateVIN };
3284
3743
  //# sourceMappingURL=index.js.map
3285
3744
  //# sourceMappingURL=index.js.map