urdu-text-utils 0.1.6 → 0.1.7

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/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.7
4
+
5
+ - Add Urdu date & time formatting module (`formatUrduDate`, `timeAgoUrdu`, `getUrduMonthName`, `getUrduWeekdayName`).
6
+ - Add pattern token replacement (`YYYY`, `MMMM`, `DD`, `dddd`, `hh:mm A`) with automatic Urdu numerals and day period indicators (`صبح`, `دوپہر`, `شام`, `رات`).
7
+ - Add localized natural relative time formatting for past and future dates (*"ابھی"*, *"۵ منٹ پہلے"*, *"۳ گھنٹے پہلے"*, *"کل"*, *"پرسوں"*, *"۲ ہفتے بعد"*).
8
+ - Add canonical constants `URDU_MONTHS_GREGORIAN`, `URDU_MONTHS_HIJRI`, and `URDU_WEEKDAYS`.
9
+
3
10
  ## 0.1.6
4
11
 
5
12
  - Add dedicated minified IIFE browser bundle (`dist/index.iife.js`) exposing `window.UrduTextUtils` for seamless jsDelivr and unpkg CDN usage without a bundler.
package/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # urdu-text-utils — Urdu Text Processing Toolkit for JavaScript & TypeScript
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/urdu-text-utils?style=flat-square&color=157f52&label=npm)](https://www.npmjs.com/package/urdu-text-utils)
4
+ [![jsDelivr](https://data.jsdelivr.com/v1/package/npm/urdu-text-utils/badge?style=flat-square)](https://www.jsdelivr.com/package/npm/urdu-text-utils)
5
+ [![unpkg](https://img.shields.io/badge/unpkg-urdu--text--utils-blue?style=flat-square)](https://unpkg.com/urdu-text-utils/)
4
6
  [![CI](https://img.shields.io/github/actions/workflow/status/Zaid-maker/urdu-text-utils/ci.yml?branch=main&style=flat-square&label=CI)](https://github.com/Zaid-maker/urdu-text-utils/actions/workflows/ci.yml)
5
7
  [![minzipped size](https://img.shields.io/bundlejs/size/urdu-text-utils?style=flat-square&label=min%2Bgzip)](https://bundlejs.com/?q=urdu-text-utils)
6
8
  [![dependencies](https://img.shields.io/badge/dependencies-0-157f52?style=flat-square)](https://www.npmjs.com/package/urdu-text-utils?activeTab=dependencies)
@@ -49,6 +51,8 @@ import {
49
51
  urduSlug,
50
52
  isStopWord,
51
53
  removeStopWords,
54
+ formatUrduDate,
55
+ timeAgoUrdu,
52
56
  isUrdu,
53
57
  countWords,
54
58
  splitSentences,
@@ -147,6 +151,27 @@ parseUrduNumber("۳٫۱۴"); // 3.14
147
151
  numberToUrduWords(100000); // "ایک لاکھ" — South Asian scale, @experimental
148
152
  ```
149
153
 
154
+ ## Urdu dates & relative time
155
+
156
+ ```ts
157
+ import { formatUrduDate, timeAgoUrdu } from "urdu-text-utils";
158
+
159
+ // Format date with Urdu month and numerals
160
+ formatUrduDate(new Date(), "DD MMMM YYYY");
161
+ // "۲۲ اگست ۲۰۲۶"
162
+
163
+ // Date with weekday and 12-hour period
164
+ formatUrduDate(new Date(), "dddd، D MMMM YYYY، hh:mm A");
165
+ // "ہفتہ، ۲۲ اگست ۲۰۲۶، ۰۲:۳۰ دوپہر"
166
+
167
+ // Natural relative time (time ago)
168
+ timeAgoUrdu(Date.now() - 5 * 60 * 1000); // "۵ منٹ پہلے"
169
+ timeAgoUrdu(Date.now() - 3 * 3600 * 1000); // "۳ گھنٹے پہلے"
170
+ timeAgoUrdu(Date.now() - 86400 * 1000); // "کل"
171
+ timeAgoUrdu(Date.now() - 2 * 86400 * 1000);// "پرسوں"
172
+ timeAgoUrdu(Date.now() + 10 * 60 * 1000); // "۱۰ منٹ بعد"
173
+ ```
174
+
150
175
  ## Diacritics
151
176
 
152
177
  ```ts
package/dist/index.cjs CHANGED
@@ -3508,7 +3508,205 @@ function urduSlug(input, options = {}) {
3508
3508
  return slug;
3509
3509
  }
3510
3510
 
3511
+ // src/date.ts
3512
+ var URDU_MONTHS_GREGORIAN = [
3513
+ "\u062C\u0646\u0648\u0631\u06CC",
3514
+ "\u0641\u0631\u0648\u0631\u06CC",
3515
+ "\u0645\u0627\u0631\u0686",
3516
+ "\u0627\u067E\u0631\u06CC\u0644",
3517
+ "\u0645\u0626\u06CC",
3518
+ "\u062C\u0648\u0646",
3519
+ "\u062C\u0648\u0644\u0627\u0626\u06CC",
3520
+ "\u0627\u06AF\u0633\u062A",
3521
+ "\u0633\u062A\u0645\u0628\u0631",
3522
+ "\u0627\u06A9\u062A\u0648\u0628\u0631",
3523
+ "\u0646\u0648\u0645\u0628\u0631",
3524
+ "\u062F\u0633\u0645\u0628\u0631"
3525
+ ];
3526
+ var URDU_MONTHS_HIJRI = [
3527
+ "\u0645\u062D\u0631\u0645",
3528
+ "\u0635\u0641\u0631",
3529
+ "\u0631\u0628\u06CC\u0639 \u0627\u0644\u0627\u0648\u0644",
3530
+ "\u0631\u0628\u06CC\u0639 \u0627\u0644\u062B\u0627\u0646\u06CC",
3531
+ "\u062C\u0645\u0627\u062F\u06CC \u0627\u0644\u0627\u0648\u0644",
3532
+ "\u062C\u0645\u0627\u062F\u06CC \u0627\u0644\u062B\u0627\u0646\u06CC",
3533
+ "\u0631\u062C\u0628",
3534
+ "\u0634\u0639\u0628\u0627\u0646",
3535
+ "\u0631\u0645\u0636\u0627\u0646 \u0627\u0644\u0645\u0628\u0627\u0631\u06A9",
3536
+ "\u0634\u0648\u0627\u0644",
3537
+ "\u0630\u06CC \u0627\u0644\u0642\u0639\u062F\u06C1",
3538
+ "\u0630\u06CC \u0627\u0644\u062D\u062C\u06C1"
3539
+ ];
3540
+ var URDU_WEEKDAYS = [
3541
+ "\u0627\u062A\u0648\u0627\u0631",
3542
+ "\u067E\u06CC\u0631",
3543
+ "\u0645\u0646\u06AF\u0644",
3544
+ "\u0628\u062F\u06BE",
3545
+ "\u062C\u0645\u0639\u0631\u0627\u062A",
3546
+ "\u062C\u0645\u0639\u06C1",
3547
+ "\u06C1\u0641\u062A\u06C1"
3548
+ ];
3549
+ function parseDateInput(input) {
3550
+ if (input instanceof Date) {
3551
+ return isNaN(input.getTime()) ? null : input;
3552
+ }
3553
+ const date = new Date(input);
3554
+ return isNaN(date.getTime()) ? null : date;
3555
+ }
3556
+ function getUrduMonthName(monthIndex, calendar = "gregorian") {
3557
+ const normalizedIndex = Math.floor(monthIndex) % 12;
3558
+ const idx = normalizedIndex < 0 ? normalizedIndex + 12 : normalizedIndex;
3559
+ return calendar === "hijri" ? URDU_MONTHS_HIJRI[idx] : URDU_MONTHS_GREGORIAN[idx];
3560
+ }
3561
+ function getUrduWeekdayName(dayIndex) {
3562
+ const normalizedIndex = Math.floor(dayIndex) % 7;
3563
+ const idx = normalizedIndex < 0 ? normalizedIndex + 7 : normalizedIndex;
3564
+ return URDU_WEEKDAYS[idx];
3565
+ }
3566
+ function formatUrduDate(date, pattern = "DD MMMM YYYY", options = {}) {
3567
+ const d = parseDateInput(date);
3568
+ if (!d) return "";
3569
+ const digits = options.digits ?? "urdu";
3570
+ const calendar = options.calendar ?? "gregorian";
3571
+ const year = d.getFullYear();
3572
+ const month = d.getMonth();
3573
+ const dayOfMonth = d.getDate();
3574
+ const dayOfWeek = d.getDay();
3575
+ const hours24 = d.getHours();
3576
+ const hours12 = hours24 % 12 || 12;
3577
+ const minutes = d.getMinutes();
3578
+ const seconds = d.getSeconds();
3579
+ const pad = (n) => n < 10 ? `0${n}` : `${n}`;
3580
+ let periodDetailed;
3581
+ let periodSimple;
3582
+ if (hours24 >= 4 && hours24 < 12) {
3583
+ periodDetailed = "\u0635\u0628\u062D";
3584
+ periodSimple = "\u0635\u0628\u062D";
3585
+ } else if (hours24 >= 12 && hours24 < 16) {
3586
+ periodDetailed = "\u062F\u0648\u067E\u06C1\u0631";
3587
+ periodSimple = "\u0634\u0627\u0645";
3588
+ } else if (hours24 >= 16 && hours24 < 20) {
3589
+ periodDetailed = "\u0634\u0627\u0645";
3590
+ periodSimple = "\u0634\u0627\u0645";
3591
+ } else {
3592
+ periodDetailed = "\u0631\u0627\u062A";
3593
+ periodSimple = "\u0631\u0627\u062A";
3594
+ }
3595
+ const tokenValues = {
3596
+ YYYY: `${year}`,
3597
+ YY: `${year}`.slice(-2),
3598
+ MMMM: getUrduMonthName(month, calendar),
3599
+ MMM: getUrduMonthName(month, calendar),
3600
+ MM: pad(month + 1),
3601
+ M: `${month + 1}`,
3602
+ DD: pad(dayOfMonth),
3603
+ D: `${dayOfMonth}`,
3604
+ dddd: getUrduWeekdayName(dayOfWeek),
3605
+ ddd: getUrduWeekdayName(dayOfWeek),
3606
+ HH: pad(hours24),
3607
+ H: `${hours24}`,
3608
+ hh: pad(hours12),
3609
+ h: `${hours12}`,
3610
+ mm: pad(minutes),
3611
+ m: `${minutes}`,
3612
+ ss: pad(seconds),
3613
+ s: `${seconds}`,
3614
+ A: periodDetailed,
3615
+ a: periodSimple
3616
+ };
3617
+ const regex = /YYYY|YY|MMMM|MMM|MM|M|DD|D|dddd|ddd|HH|H|hh|h|mm|m|ss|s|A|a/g;
3618
+ const result = pattern.replace(regex, (match) => {
3619
+ const val = tokenValues[match];
3620
+ if (val === void 0) return match;
3621
+ if (digits === "urdu" && /^\d+$/.test(val)) {
3622
+ return toUrduDigits(val);
3623
+ }
3624
+ return val;
3625
+ });
3626
+ return result;
3627
+ }
3628
+ function timeAgoUrdu(date, relativeTo = /* @__PURE__ */ new Date(), options = {}) {
3629
+ const d = parseDateInput(date);
3630
+ const now = parseDateInput(relativeTo);
3631
+ if (!d || !now) return "";
3632
+ const digits = options.digits ?? "urdu";
3633
+ const addSuffix = options.addSuffix ?? true;
3634
+ const formatNum = (num) => {
3635
+ const s = `${num}`;
3636
+ return digits === "urdu" ? toUrduDigits(s) : s;
3637
+ };
3638
+ const diffMs = now.getTime() - d.getTime();
3639
+ const isPast = diffMs >= 0;
3640
+ const absDiffSeconds = Math.round(Math.abs(diffMs) / 1e3);
3641
+ if (absDiffSeconds < 45) {
3642
+ return isPast ? "\u0627\u0628\u06BE\u06CC" : "\u0686\u0646\u062F \u0644\u0645\u062D\u06D2 \u0628\u0639\u062F";
3643
+ }
3644
+ const minutes = Math.round(absDiffSeconds / 60);
3645
+ if (minutes <= 45) {
3646
+ if (minutes === 1) {
3647
+ if (!addSuffix) return "\u0627\u06CC\u06A9 \u0645\u0646\u0679";
3648
+ return isPast ? "\u0627\u06CC\u06A9 \u0645\u0646\u0679 \u067E\u06C1\u0644\u06D2" : "\u0627\u06CC\u06A9 \u0645\u0646\u0679 \u0628\u0639\u062F";
3649
+ }
3650
+ const count2 = formatNum(minutes);
3651
+ if (!addSuffix) return `${count2} \u0645\u0646\u0679`;
3652
+ return isPast ? `${count2} \u0645\u0646\u0679 \u067E\u06C1\u0644\u06D2` : `${count2} \u0645\u0646\u0679 \u0628\u0639\u062F`;
3653
+ }
3654
+ const hours = Math.round(minutes / 60);
3655
+ if (hours <= 22) {
3656
+ if (hours === 1) {
3657
+ if (!addSuffix) return "\u0627\u06CC\u06A9 \u06AF\u06BE\u0646\u0679\u06C1";
3658
+ return isPast ? "\u0627\u06CC\u06A9 \u06AF\u06BE\u0646\u0679\u06C1 \u067E\u06C1\u0644\u06D2" : "\u0627\u06CC\u06A9 \u06AF\u06BE\u0646\u0679\u06C1 \u0628\u0639\u062F";
3659
+ }
3660
+ const count2 = formatNum(hours);
3661
+ if (!addSuffix) return `${count2} \u06AF\u06BE\u0646\u0679\u06D2`;
3662
+ return isPast ? `${count2} \u06AF\u06BE\u0646\u0679\u06D2 \u067E\u06C1\u0644\u06D2` : `${count2} \u06AF\u06BE\u0646\u0679\u06D2 \u0628\u0639\u062F`;
3663
+ }
3664
+ const days = Math.round(hours / 24);
3665
+ if (days === 1) {
3666
+ return isPast ? "\u06A9\u0644" : "\u06A9\u0644";
3667
+ }
3668
+ if (days === 2) {
3669
+ return isPast ? "\u067E\u0631\u0633\u0648\u06BA" : "\u067E\u0631\u0633\u0648\u06BA";
3670
+ }
3671
+ if (days <= 6) {
3672
+ const count2 = formatNum(days);
3673
+ if (!addSuffix) return `${count2} \u062F\u0646`;
3674
+ return isPast ? `${count2} \u062F\u0646 \u067E\u06C1\u0644\u06D2` : `${count2} \u062F\u0646 \u0628\u0639\u062F`;
3675
+ }
3676
+ const weeks = Math.round(days / 7);
3677
+ if (days <= 28) {
3678
+ if (weeks === 1) {
3679
+ if (!addSuffix) return "\u0627\u06CC\u06A9 \u06C1\u0641\u062A\u06C1";
3680
+ return isPast ? "\u0627\u06CC\u06A9 \u06C1\u0641\u062A\u06C1 \u067E\u06C1\u0644\u06D2" : "\u0627\u06CC\u06A9 \u06C1\u0641\u062A\u06C1 \u0628\u0639\u062F";
3681
+ }
3682
+ const count2 = formatNum(weeks);
3683
+ if (!addSuffix) return `${count2} \u06C1\u0641\u062A\u06D2`;
3684
+ return isPast ? `${count2} \u06C1\u0641\u062A\u06D2 \u067E\u06C1\u0644\u06D2` : `${count2} \u06C1\u0641\u062A\u06D2 \u0628\u0639\u062F`;
3685
+ }
3686
+ const months = Math.round(days / 30.44);
3687
+ if (days <= 320) {
3688
+ if (months <= 1) {
3689
+ if (!addSuffix) return "\u0627\u06CC\u06A9 \u0645\u0627\u06C1";
3690
+ return isPast ? "\u0627\u06CC\u06A9 \u0645\u0627\u06C1 \u067E\u06C1\u0644\u06D2" : "\u0627\u06CC\u06A9 \u0645\u0627\u06C1 \u0628\u0639\u062F";
3691
+ }
3692
+ const count2 = formatNum(months);
3693
+ if (!addSuffix) return `${count2} \u0645\u0627\u06C1`;
3694
+ return isPast ? `${count2} \u0645\u0627\u06C1 \u067E\u06C1\u0644\u06D2` : `${count2} \u0645\u0627\u06C1 \u0628\u0639\u062F`;
3695
+ }
3696
+ const years = Math.round(days / 365.25);
3697
+ if (years <= 1) {
3698
+ if (!addSuffix) return "\u0627\u06CC\u06A9 \u0633\u0627\u0644";
3699
+ return isPast ? "\u0627\u06CC\u06A9 \u0633\u0627\u0644 \u067E\u06C1\u0644\u06D2" : "\u0627\u06CC\u06A9 \u0633\u0627\u0644 \u0628\u0639\u062F";
3700
+ }
3701
+ const count = formatNum(years);
3702
+ if (!addSuffix) return `${count} \u0633\u0627\u0644`;
3703
+ return isPast ? `${count} \u0633\u0627\u0644 \u067E\u06C1\u0644\u06D2` : `${count} \u0633\u0627\u0644 \u0628\u0639\u062F`;
3704
+ }
3705
+
3706
+ exports.URDU_MONTHS_GREGORIAN = URDU_MONTHS_GREGORIAN;
3707
+ exports.URDU_MONTHS_HIJRI = URDU_MONTHS_HIJRI;
3511
3708
  exports.URDU_STOP_WORDS = URDU_STOP_WORDS;
3709
+ exports.URDU_WEEKDAYS = URDU_WEEKDAYS;
3512
3710
  exports.analyzeUrdu = analyzeUrdu;
3513
3711
  exports.compareUrdu = compareUrdu;
3514
3712
  exports.convertNumbers = convertNumbers;
@@ -3517,6 +3715,9 @@ exports.countWords = countWords;
3517
3715
  exports.editDistance = editDistance;
3518
3716
  exports.filterStopWords = filterStopWords;
3519
3717
  exports.foldUrdu = foldUrdu;
3718
+ exports.formatUrduDate = formatUrduDate;
3719
+ exports.getUrduMonthName = getUrduMonthName;
3720
+ exports.getUrduWeekdayName = getUrduWeekdayName;
3520
3721
  exports.hasUrduSpecificLetters = hasUrduSpecificLetters;
3521
3722
  exports.highlightUrdu = highlightUrdu;
3522
3723
  exports.isStopWord = isStopWord;
@@ -3533,6 +3734,7 @@ exports.searchUrduRanked = searchUrduRanked;
3533
3734
  exports.sortUrdu = sortUrdu;
3534
3735
  exports.splitSentences = splitSentences;
3535
3736
  exports.splitWords = splitWords;
3737
+ exports.timeAgoUrdu = timeAgoUrdu;
3536
3738
  exports.toArabicIndicDigits = toArabicIndicDigits;
3537
3739
  exports.toEnglishDigits = toEnglishDigits;
3538
3740
  exports.toUrduDigits = toUrduDigits;