@indodev/toolkit 0.3.4 → 0.4.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/dist/index.cjs CHANGED
@@ -717,7 +717,7 @@ function isMobileNumber(phone) {
717
717
  let normalized;
718
718
  if (cleaned.startsWith("+62")) {
719
719
  normalized = "0" + cleaned.substring(3);
720
- } else if (cleaned.startsWith("62")) {
720
+ } else if (cleaned.startsWith("62") && !cleaned.startsWith("620")) {
721
721
  normalized = "0" + cleaned.substring(2);
722
722
  } else {
723
723
  normalized = cleaned;
@@ -731,19 +731,70 @@ function isLandlineNumber(phone) {
731
731
  return !isMobileNumber(phone);
732
732
  }
733
733
 
734
+ // src/phone/utils.ts
735
+ function normalizePhoneNumber(phone) {
736
+ if (!phone || typeof phone !== "string") {
737
+ return "";
738
+ }
739
+ if (phone.startsWith("+62")) {
740
+ return "0" + phone.substring(3);
741
+ }
742
+ if (phone.startsWith("62") && !phone.startsWith("620")) {
743
+ return "0" + phone.substring(2);
744
+ }
745
+ if (phone.startsWith("0")) {
746
+ return phone;
747
+ }
748
+ return "";
749
+ }
750
+ function normalizeToNational(phone) {
751
+ if (phone.startsWith("+62")) {
752
+ return "0" + phone.substring(3);
753
+ }
754
+ if (phone.startsWith("62") && !phone.startsWith("620")) {
755
+ return "0" + phone.substring(2);
756
+ }
757
+ if (phone.startsWith("0")) {
758
+ return phone;
759
+ }
760
+ return "";
761
+ }
762
+ function getLandlineRegion(phone) {
763
+ if (!phone || typeof phone !== "string") {
764
+ return null;
765
+ }
766
+ const cleaned = phone.replace(/[^\d+]/g, "");
767
+ const normalized = normalizeToNational(cleaned);
768
+ if (!normalized || !normalized.startsWith("0")) {
769
+ return null;
770
+ }
771
+ if (normalized.startsWith("08")) {
772
+ return null;
773
+ }
774
+ const areaCode4 = normalized.substring(0, 4);
775
+ if (AREA_CODES[areaCode4]) {
776
+ return AREA_CODES[areaCode4];
777
+ }
778
+ const areaCode3 = normalized.substring(0, 3);
779
+ if (AREA_CODES[areaCode3]) {
780
+ return AREA_CODES[areaCode3];
781
+ }
782
+ const areaCode2 = normalized.substring(0, 2);
783
+ if (AREA_CODES[areaCode2]) {
784
+ return AREA_CODES[areaCode2];
785
+ }
786
+ return null;
787
+ }
788
+
734
789
  // src/phone/format.ts
735
790
  function formatPhoneNumber(phone, format = "national") {
736
791
  if (!validatePhoneNumber(phone)) {
737
792
  return phone;
738
793
  }
739
794
  const cleaned = cleanPhoneNumber(phone);
740
- let normalized;
741
- if (cleaned.startsWith("+62")) {
742
- normalized = "0" + cleaned.substring(3);
743
- } else if (cleaned.startsWith("62") && !cleaned.startsWith("620")) {
744
- normalized = "0" + cleaned.substring(2);
745
- } else {
746
- normalized = cleaned;
795
+ const normalized = normalizePhoneNumber(cleaned);
796
+ if (!normalized) {
797
+ return phone;
747
798
  }
748
799
  switch (format) {
749
800
  case "international":
@@ -762,7 +813,7 @@ function toInternational(phone) {
762
813
  if (!cleaned) {
763
814
  return phone;
764
815
  }
765
- const normalized = normalizeToNational(cleaned);
816
+ const normalized = normalizePhoneNumber(cleaned);
766
817
  if (!normalized) {
767
818
  return phone;
768
819
  }
@@ -788,7 +839,7 @@ function toNational(phone) {
788
839
  if (!cleaned) {
789
840
  return phone;
790
841
  }
791
- const normalized = normalizeToNational(cleaned);
842
+ const normalized = normalizePhoneNumber(cleaned);
792
843
  if (!normalized) {
793
844
  return phone;
794
845
  }
@@ -813,7 +864,7 @@ function toE164(phone) {
813
864
  if (!cleaned) {
814
865
  return phone;
815
866
  }
816
- const normalized = normalizeToNational(cleaned);
867
+ const normalized = normalizePhoneNumber(cleaned);
817
868
  if (!normalized) {
818
869
  return phone;
819
870
  }
@@ -825,16 +876,6 @@ function cleanPhoneNumber(phone) {
825
876
  }
826
877
  return phone.replace(/[^\d+]/g, "");
827
878
  }
828
- function normalizeToNational(phone) {
829
- if (phone.startsWith("+62")) {
830
- return "0" + phone.substring(3);
831
- } else if (phone.startsWith("62") && !phone.startsWith("620")) {
832
- return "0" + phone.substring(2);
833
- } else if (phone.startsWith("0")) {
834
- return phone;
835
- }
836
- return "";
837
- }
838
879
  function getAreaCodeLength(normalized) {
839
880
  const fourDigitCode = normalized.substring(0, 5);
840
881
  if (AREA_CODES[fourDigitCode]) {
@@ -860,7 +901,7 @@ function maskPhoneNumber(phone, options = {}) {
860
901
  if (isInternational) {
861
902
  toMask = cleaned;
862
903
  } else {
863
- const normalized = normalizeToNational(cleaned);
904
+ const normalized = normalizePhoneNumber(cleaned);
864
905
  toMask = normalized || cleaned;
865
906
  }
866
907
  if (toMask.length < 4) {
@@ -929,7 +970,7 @@ function parsePhoneNumber(phone) {
929
970
  return null;
930
971
  }
931
972
  const cleaned = cleanPhoneNumber(phone);
932
- const normalized = normalizeToNational2(cleaned);
973
+ const normalized = normalizePhoneNumber(cleaned);
933
974
  if (!normalized) {
934
975
  return null;
935
976
  }
@@ -942,7 +983,7 @@ function parsePhoneNumber(phone) {
942
983
  if (isMobile) {
943
984
  operator = getOperator(normalized);
944
985
  } else {
945
- region = getRegion(normalized);
986
+ region = getLandlineRegion(normalized);
946
987
  }
947
988
  return {
948
989
  countryCode,
@@ -964,7 +1005,7 @@ function getOperator(phone) {
964
1005
  return null;
965
1006
  }
966
1007
  const cleaned = cleanPhoneNumber(phone);
967
- const normalized = normalizeToNational2(cleaned);
1008
+ const normalized = normalizePhoneNumber(cleaned);
968
1009
  if (!normalized || normalized.length < 4) {
969
1010
  return null;
970
1011
  }
@@ -978,30 +1019,6 @@ function isProvider(phone, providerName) {
978
1019
  }
979
1020
  return operator.toLowerCase() === providerName.toLowerCase();
980
1021
  }
981
- function getRegion(phone) {
982
- if (!phone.startsWith("0")) {
983
- return null;
984
- }
985
- const areaCode4 = phone.substring(0, 4);
986
- if (AREA_CODES[areaCode4]) {
987
- return AREA_CODES[areaCode4];
988
- }
989
- const areaCode3 = phone.substring(0, 3);
990
- if (AREA_CODES[areaCode3]) {
991
- return AREA_CODES[areaCode3];
992
- }
993
- return null;
994
- }
995
- function normalizeToNational2(phone) {
996
- if (phone.startsWith("+62")) {
997
- return "0" + phone.substring(3);
998
- } else if (phone.startsWith("62")) {
999
- return "0" + phone.substring(2);
1000
- } else if (phone.startsWith("0")) {
1001
- return phone;
1002
- }
1003
- return "";
1004
- }
1005
1022
 
1006
1023
  // src/npwp/validate.ts
1007
1024
  function validateNPWP(npwp) {
@@ -3322,17 +3339,447 @@ function similarity(str1, str2) {
3322
3339
  return 1 - distance / maxLength;
3323
3340
  }
3324
3341
 
3342
+ // src/datetime/types.ts
3343
+ var InvalidDateError = class extends Error {
3344
+ constructor(message = "Invalid date provided") {
3345
+ super(message);
3346
+ /** Error code for programmatic identification */
3347
+ this.code = "INVALID_DATE";
3348
+ this.name = "InvalidDateError";
3349
+ }
3350
+ };
3351
+ var InvalidDateRangeError = class extends Error {
3352
+ constructor(message = "End date must be after start date") {
3353
+ super(message);
3354
+ /** Error code for programmatic identification */
3355
+ this.code = "INVALID_DATE_RANGE";
3356
+ this.name = "InvalidDateRangeError";
3357
+ }
3358
+ };
3359
+
3360
+ // src/datetime/constants.ts
3361
+ var MONTH_NAMES = [
3362
+ "",
3363
+ // Placeholder for 0-index
3364
+ "Januari",
3365
+ "Februari",
3366
+ "Maret",
3367
+ "April",
3368
+ "Mei",
3369
+ "Juni",
3370
+ "Juli",
3371
+ "Agustus",
3372
+ "September",
3373
+ "Oktober",
3374
+ "November",
3375
+ "Desember"
3376
+ ];
3377
+ var MONTH_NAMES_SHORT = [
3378
+ "",
3379
+ // Placeholder for 0-index
3380
+ "Jan",
3381
+ "Feb",
3382
+ "Mar",
3383
+ "Apr",
3384
+ "Mei",
3385
+ "Jun",
3386
+ "Jul",
3387
+ "Agu",
3388
+ "Sep",
3389
+ "Okt",
3390
+ "Nov",
3391
+ "Des"
3392
+ ];
3393
+ var DAY_NAMES = [
3394
+ "Minggu",
3395
+ "Senin",
3396
+ "Selasa",
3397
+ "Rabu",
3398
+ "Kamis",
3399
+ "Jumat",
3400
+ "Sabtu"
3401
+ ];
3402
+ var DAY_NAMES_SHORT = [
3403
+ "Min",
3404
+ "Sen",
3405
+ "Sel",
3406
+ "Rab",
3407
+ "Kam",
3408
+ "Jum",
3409
+ "Sab"
3410
+ ];
3411
+ var TIMEZONE_MAP = {
3412
+ // UTC+7 - WIB
3413
+ "Asia/Jakarta": "WIB",
3414
+ "Asia/Pontianak": "WIB",
3415
+ // UTC+8 - WITA
3416
+ "Asia/Makassar": "WITA",
3417
+ "Asia/Denpasar": "WITA",
3418
+ "Asia/Manado": "WITA",
3419
+ "Asia/Palu": "WITA",
3420
+ // UTC+9 - WIT
3421
+ "Asia/Jayapura": "WIT"
3422
+ };
3423
+ var VALID_UTC_OFFSETS = [7, 8, 9];
3424
+
3425
+ // src/datetime/calc.ts
3426
+ function isLeapYear(year) {
3427
+ if (!Number.isFinite(year) || !Number.isInteger(year)) {
3428
+ return false;
3429
+ }
3430
+ return year % 4 === 0 && year % 100 !== 0 || year % 400 === 0;
3431
+ }
3432
+ function daysInMonth(month, year) {
3433
+ if (!Number.isFinite(month) || !Number.isInteger(month) || !Number.isFinite(year) || !Number.isInteger(year) || month < 1 || month > 12) {
3434
+ return 0;
3435
+ }
3436
+ if ([1, 3, 5, 7, 8, 10, 12].includes(month)) {
3437
+ return 31;
3438
+ }
3439
+ if ([4, 6, 9, 11].includes(month)) {
3440
+ return 30;
3441
+ }
3442
+ return isLeapYear(year) ? 29 : 28;
3443
+ }
3444
+ function isValidDate(date) {
3445
+ return date instanceof Date && !Number.isNaN(date.getTime());
3446
+ }
3447
+ function isWeekend(date) {
3448
+ const day = date.getDay();
3449
+ return day === 0 || day === 6;
3450
+ }
3451
+ function isWorkingDay(date) {
3452
+ const day = date.getDay();
3453
+ return day >= 1 && day <= 5;
3454
+ }
3455
+ function normalizeDate(date) {
3456
+ let result;
3457
+ if (date instanceof Date) {
3458
+ result = date;
3459
+ } else if (typeof date === "number") {
3460
+ result = new Date(date);
3461
+ } else if (typeof date === "string") {
3462
+ result = new Date(date);
3463
+ } else {
3464
+ throw new InvalidDateError("Date must be a Date, string, or number");
3465
+ }
3466
+ if (Number.isNaN(result.getTime())) {
3467
+ throw new InvalidDateError(`Unable to parse date: ${String(date)}`);
3468
+ }
3469
+ return result;
3470
+ }
3471
+ function getAge2(birthDate, options = {}) {
3472
+ const birth = normalizeDate(birthDate);
3473
+ const from = options.fromDate ? normalizeDate(options.fromDate) : /* @__PURE__ */ new Date();
3474
+ if (birth.getTime() > from.getTime()) {
3475
+ throw new InvalidDateError(
3476
+ "Birth date cannot be in the future relative to fromDate"
3477
+ );
3478
+ }
3479
+ let years = from.getFullYear() - birth.getFullYear();
3480
+ let months = from.getMonth() - birth.getMonth();
3481
+ let days = from.getDate() - birth.getDate();
3482
+ if (days < 0) {
3483
+ months--;
3484
+ const prevMonth = from.getMonth() === 0 ? 11 : from.getMonth() - 1;
3485
+ const prevMonthYear = from.getMonth() === 0 ? from.getFullYear() - 1 : from.getFullYear();
3486
+ days += daysInMonth(prevMonth + 1, prevMonthYear);
3487
+ }
3488
+ if (months < 0) {
3489
+ years--;
3490
+ months += 12;
3491
+ }
3492
+ const result = { years, months, days };
3493
+ if (options.asString) {
3494
+ return formatAgeString(result);
3495
+ }
3496
+ return result;
3497
+ }
3498
+ function formatAgeString(age) {
3499
+ const parts = [];
3500
+ if (age.years > 0) {
3501
+ parts.push(`${age.years} Tahun`);
3502
+ }
3503
+ if (age.months > 0) {
3504
+ parts.push(`${age.months} Bulan`);
3505
+ }
3506
+ if (age.days > 0) {
3507
+ parts.push(`${age.days} Hari`);
3508
+ }
3509
+ if (parts.length === 0) {
3510
+ return "0 Hari";
3511
+ }
3512
+ return parts.join(" ");
3513
+ }
3514
+
3515
+ // src/datetime/parse.ts
3516
+ function parseDate(dateStr) {
3517
+ const trimmed = dateStr.trim();
3518
+ if (!trimmed) {
3519
+ return null;
3520
+ }
3521
+ if (trimmed.includes(" ") || trimmed.includes(":")) {
3522
+ return null;
3523
+ }
3524
+ const parts = trimmed.split(/[-/.]/);
3525
+ if (parts.length !== 3) {
3526
+ return null;
3527
+ }
3528
+ const nums = parts.map((p) => parseInt(p, 10));
3529
+ if (nums.some((n) => Number.isNaN(n) || n < 0)) {
3530
+ return null;
3531
+ }
3532
+ const [first, second, third] = nums;
3533
+ let day;
3534
+ let month;
3535
+ let year;
3536
+ if (first > 999 && first > 31) {
3537
+ year = first;
3538
+ month = second;
3539
+ day = third;
3540
+ } else {
3541
+ if (third < 1e3) {
3542
+ return null;
3543
+ }
3544
+ day = first;
3545
+ month = second;
3546
+ year = third;
3547
+ }
3548
+ if (month < 1 || month > 12) {
3549
+ return null;
3550
+ }
3551
+ if (year < 1e3 || year > 9999) {
3552
+ return null;
3553
+ }
3554
+ const maxDays = daysInMonth(month, year);
3555
+ if (maxDays === 0 || day < 1 || day > maxDays) {
3556
+ return null;
3557
+ }
3558
+ const date = new Date(year, month - 1, day);
3559
+ if (date.getFullYear() !== year || date.getMonth() !== month - 1 || date.getDate() !== day) {
3560
+ return null;
3561
+ }
3562
+ return date;
3563
+ }
3564
+
3565
+ // src/datetime/format.ts
3566
+ function normalizeDate2(date) {
3567
+ let result;
3568
+ if (date instanceof Date) {
3569
+ result = date;
3570
+ } else if (typeof date === "number") {
3571
+ result = new Date(date);
3572
+ } else if (typeof date === "string") {
3573
+ result = new Date(date);
3574
+ } else {
3575
+ throw new InvalidDateError("Date must be a Date, string, or number");
3576
+ }
3577
+ if (Number.isNaN(result.getTime())) {
3578
+ throw new InvalidDateError(`Unable to parse date: ${String(date)}`);
3579
+ }
3580
+ return result;
3581
+ }
3582
+ function formatDate(date, style = "long") {
3583
+ const d = normalizeDate2(date);
3584
+ const day = d.getDate();
3585
+ const month = d.getMonth() + 1;
3586
+ const year = d.getFullYear();
3587
+ const dayOfWeek = d.getDay();
3588
+ switch (style) {
3589
+ case "full":
3590
+ return `${DAY_NAMES[dayOfWeek]}, ${day} ${MONTH_NAMES[month]} ${year}`;
3591
+ case "long":
3592
+ return `${day} ${MONTH_NAMES[month]} ${year}`;
3593
+ case "medium":
3594
+ return `${day} ${MONTH_NAMES_SHORT[month]} ${year}`;
3595
+ case "short": {
3596
+ const dd = String(day).padStart(2, "0");
3597
+ const mm = String(month).padStart(2, "0");
3598
+ return `${dd}/${mm}/${year}`;
3599
+ }
3600
+ case "weekday":
3601
+ return DAY_NAMES[dayOfWeek];
3602
+ case "month":
3603
+ return MONTH_NAMES[month];
3604
+ default:
3605
+ throw new InvalidDateError(`Unknown format style: ${style}`);
3606
+ }
3607
+ }
3608
+ function formatDateRange(start, end, style = "long") {
3609
+ const s = normalizeDate2(start);
3610
+ const e = normalizeDate2(end);
3611
+ if (e.getTime() < s.getTime()) {
3612
+ throw new InvalidDateRangeError();
3613
+ }
3614
+ if (style === "short") {
3615
+ return `${formatDate(s, "short")} - ${formatDate(e, "short")}`;
3616
+ }
3617
+ if (style === "full") {
3618
+ return `${formatDate(s, "full")} - ${formatDate(e, "full")}`;
3619
+ }
3620
+ const sDay = s.getDate();
3621
+ const eDay = e.getDate();
3622
+ const sMonth = s.getMonth() + 1;
3623
+ const eMonth = e.getMonth() + 1;
3624
+ const sYear = s.getFullYear();
3625
+ const eYear = e.getFullYear();
3626
+ if (sDay === eDay && sMonth === eMonth && sYear === eYear) {
3627
+ return formatDate(s, style);
3628
+ }
3629
+ if (sYear !== eYear) {
3630
+ return `${formatDate(s, style)} - ${formatDate(e, style)}`;
3631
+ }
3632
+ if (sMonth !== eMonth) {
3633
+ if (style === "long") {
3634
+ return `${sDay} ${MONTH_NAMES[sMonth]} - ${eDay} ${MONTH_NAMES[eMonth]} ${eYear}`;
3635
+ }
3636
+ return `${sDay} ${MONTH_NAMES_SHORT[sMonth]} - ${eDay} ${MONTH_NAMES_SHORT[eMonth]} ${eYear}`;
3637
+ }
3638
+ if (style === "long") {
3639
+ return `${sDay} - ${eDay} ${MONTH_NAMES[eMonth]} ${eYear}`;
3640
+ }
3641
+ return `${sDay} - ${eDay} ${MONTH_NAMES_SHORT[eMonth]} ${eYear}`;
3642
+ }
3643
+
3644
+ // src/datetime/relative.ts
3645
+ function normalizeDate3(date) {
3646
+ let result;
3647
+ if (date instanceof Date) {
3648
+ result = date;
3649
+ } else if (typeof date === "number") {
3650
+ result = new Date(date);
3651
+ } else if (typeof date === "string") {
3652
+ result = new Date(date);
3653
+ } else {
3654
+ throw new InvalidDateError("Date must be a Date, string, or number");
3655
+ }
3656
+ if (Number.isNaN(result.getTime())) {
3657
+ throw new InvalidDateError(`Unable to parse date: ${String(date)}`);
3658
+ }
3659
+ return result;
3660
+ }
3661
+ function toRelativeTime(date, baseDate = /* @__PURE__ */ new Date()) {
3662
+ const d = normalizeDate3(date);
3663
+ const base = normalizeDate3(baseDate);
3664
+ const diffMs = d.getTime() - base.getTime();
3665
+ const diffSec = Math.floor(diffMs / 1e3);
3666
+ const diffMin = Math.floor(diffMs / (1e3 * 60));
3667
+ const diffHour = Math.floor(diffMs / (1e3 * 60 * 60));
3668
+ const diffDay = Math.floor(diffMs / (1e3 * 60 * 60 * 24));
3669
+ if (diffMs === 0) {
3670
+ return "Sekarang";
3671
+ }
3672
+ if (diffMs > 0) {
3673
+ if (diffSec < 60) {
3674
+ return "Baru saja";
3675
+ }
3676
+ if (diffMin < 60) {
3677
+ return `${diffMin} menit lagi`;
3678
+ }
3679
+ if (diffHour < 24) {
3680
+ return `${diffHour} jam lagi`;
3681
+ }
3682
+ if (diffHour < 48) {
3683
+ return "Besok";
3684
+ }
3685
+ if (diffDay <= 30) {
3686
+ return `${diffDay} hari lagi`;
3687
+ }
3688
+ return formatDate(d, "long");
3689
+ }
3690
+ const absDiffSec = Math.abs(diffSec);
3691
+ const absDiffMin = Math.abs(diffMin);
3692
+ const absDiffHour = Math.abs(diffHour);
3693
+ const absDiffDay = Math.abs(diffDay);
3694
+ if (absDiffSec < 60) {
3695
+ return "Baru saja";
3696
+ }
3697
+ if (absDiffMin < 60) {
3698
+ return `${absDiffMin} menit yang lalu`;
3699
+ }
3700
+ if (absDiffHour < 24) {
3701
+ return `${absDiffHour} jam yang lalu`;
3702
+ }
3703
+ if (absDiffHour < 48) {
3704
+ return "Kemarin";
3705
+ }
3706
+ if (absDiffDay <= 30) {
3707
+ return `${absDiffDay} hari yang lalu`;
3708
+ }
3709
+ return formatDate(d, "long");
3710
+ }
3711
+
3712
+ // src/datetime/timezone.ts
3713
+ function getIndonesianTimezone(input) {
3714
+ if (typeof input === "number") {
3715
+ if (!Number.isFinite(input) || !Number.isInteger(input)) {
3716
+ return null;
3717
+ }
3718
+ switch (input) {
3719
+ case 7:
3720
+ return "WIB";
3721
+ case 8:
3722
+ return "WITA";
3723
+ case 9:
3724
+ return "WIT";
3725
+ default:
3726
+ return null;
3727
+ }
3728
+ }
3729
+ if (typeof input !== "string") {
3730
+ return null;
3731
+ }
3732
+ const trimmed = input.trim();
3733
+ const offsetMatch = trimmed.match(/^([+-])(\d{2}):?(\d{2})$/);
3734
+ if (offsetMatch) {
3735
+ const sign = offsetMatch[1];
3736
+ const hours = parseInt(offsetMatch[2], 10);
3737
+ const minutes = parseInt(offsetMatch[3], 10);
3738
+ if (sign === "-") {
3739
+ return null;
3740
+ }
3741
+ if (minutes !== 0) {
3742
+ return null;
3743
+ }
3744
+ switch (hours) {
3745
+ case 7:
3746
+ return "WIB";
3747
+ case 8:
3748
+ return "WITA";
3749
+ case 9:
3750
+ return "WIT";
3751
+ default:
3752
+ return null;
3753
+ }
3754
+ }
3755
+ if (TIMEZONE_MAP[trimmed]) {
3756
+ return TIMEZONE_MAP[trimmed];
3757
+ }
3758
+ return null;
3759
+ }
3760
+
3761
+ exports.DAY_NAMES = DAY_NAMES;
3762
+ exports.DAY_NAMES_SHORT = DAY_NAMES_SHORT;
3763
+ exports.InvalidDateError = InvalidDateError;
3764
+ exports.InvalidDateRangeError = InvalidDateRangeError;
3765
+ exports.MONTH_NAMES = MONTH_NAMES;
3766
+ exports.MONTH_NAMES_SHORT = MONTH_NAMES_SHORT;
3767
+ exports.TIMEZONE_MAP = TIMEZONE_MAP;
3768
+ exports.VALID_UTC_OFFSETS = VALID_UTC_OFFSETS;
3325
3769
  exports.addRupiahSymbol = addRupiahSymbol;
3326
3770
  exports.calculateTax = calculateTax;
3327
3771
  exports.capitalize = capitalize2;
3328
3772
  exports.cleanPhoneNumber = cleanPhoneNumber;
3329
3773
  exports.compareStrings = compareStrings;
3330
3774
  exports.contractAbbreviation = contractAbbreviation;
3775
+ exports.daysInMonth = daysInMonth;
3331
3776
  exports.expandAbbreviation = expandAbbreviation;
3332
3777
  exports.extractWords = extractWords;
3333
3778
  exports.formatAccounting = formatAccounting;
3334
3779
  exports.formatBirthDate = formatBirthDate;
3335
3780
  exports.formatCompact = formatCompact;
3781
+ exports.formatDate = formatDate;
3782
+ exports.formatDateRange = formatDateRange;
3336
3783
  exports.formatNIK = formatNIK;
3337
3784
  exports.formatNPWP = formatNPWP;
3338
3785
  exports.formatPhoneNumber = formatPhoneNumber;
@@ -3342,21 +3789,28 @@ exports.generateSmsLink = generateSmsLink;
3342
3789
  exports.generateTelLink = generateTelLink;
3343
3790
  exports.generateWALink = generateWALink;
3344
3791
  exports.getAge = getAge;
3792
+ exports.getAgeFromDate = getAge2;
3345
3793
  exports.getEmailInfo = getEmailInfo;
3794
+ exports.getIndonesianTimezone = getIndonesianTimezone;
3346
3795
  exports.getOperator = getOperator;
3347
3796
  exports.getRegionFromPlate = getRegionFromPlate;
3348
3797
  exports.isAlay = isAlay;
3349
3798
  exports.isLandlineNumber = isLandlineNumber;
3799
+ exports.isLeapYear = isLeapYear;
3350
3800
  exports.isMobileNumber = isMobileNumber;
3351
3801
  exports.isProvider = isProvider;
3802
+ exports.isValidDate = isValidDate;
3352
3803
  exports.isValidForBirthDate = isValidForBirthDate;
3353
3804
  exports.isValidForGender = isValidForGender;
3805
+ exports.isWeekend = isWeekend;
3806
+ exports.isWorkingDay = isWorkingDay;
3354
3807
  exports.maskEmail = maskEmail;
3355
3808
  exports.maskNIK = maskNIK;
3356
3809
  exports.maskNPWP = maskNPWP;
3357
3810
  exports.maskPhoneNumber = maskPhoneNumber;
3358
3811
  exports.normalizeEmail = normalizeEmail;
3359
3812
  exports.normalizeWhitespace = normalizeWhitespace;
3813
+ exports.parseDate = parseDate;
3360
3814
  exports.parseNIK = parseNIK;
3361
3815
  exports.parseNPWP = parseNPWP;
3362
3816
  exports.parsePhoneNumber = parsePhoneNumber;
@@ -3372,6 +3826,7 @@ exports.toE164 = toE164;
3372
3826
  exports.toFormal = toFormal;
3373
3827
  exports.toInternational = toInternational;
3374
3828
  exports.toNational = toNational;
3829
+ exports.toRelativeTime = toRelativeTime;
3375
3830
  exports.toSentenceCase = toSentenceCase;
3376
3831
  exports.toTitleCase = toTitleCase;
3377
3832
  exports.toWords = toWords;