Skip to content

قالب‌بندی زمان

زمان در یک رابط کاربری به سه شکل گوناگون ظاهر می‌شود، و درهم آمیختن آن‌ها سرچشمهٔ همیشگی سردرگمی است. ranuts برای هر کدام تابعی جداگانه دارد:

پرسشی که خواننده داردتابعنمونهٔ خروجی
این دقیقاً کِی رخ داده است؟formatDate2026-07-25 14:05:09
این چقدر طول می‌کشد؟formatDuration01:01:01
چند وقت پیش بوده است؟formatRelative3 days ago، 5m

formatDuration

شمار ثانیه‌های سپری‌شده را به شکل ساعتِ جداشده با دونقطه درمی‌آورد؛ همان شکلی که یک پخش‌کننده برای نشانگر پخش به کار می‌برد: mm:ss که از یک ساعت که بگذرد به hh:mm:ss گشوده می‌شود.

پارامترها

پارامترتوضیحنوعپیش‌فرض
secondsثانیه‌های سپری‌شده؛ مقدارهای منفی به ۰ چسبانده می‌شوندnumberالزامی

Returns

string: خودِ مدت، یا '' اگر ورودی عددی متناهی نباشد.

js
import { formatDuration } from 'ranuts/utils';

formatDuration(0); // '00:00'
formatDuration(65); // '01:05'
formatDuration(3661); // '01:01:01'
formatDuration(NaN); // ''

بازگرداندن رشتهٔ تهی در برابر NaN عمدی است: پخش‌کننده پیش از بارگیری فراداده سراغ video.duration می‌رود و NaN می‌گیرد، و آنجا یک برچسب خالی بهتر از NaN:NaN خوانده می‌شود.

نامش عوض شد

این تابع پیش‌تر timeFormat نام داشت. آن نام همچنان به‌عنوان نامی منسوخ باقی است و رفتارش هم دقیقاً همان است، اما هیچ نشانی نمی‌داد که کدام‌یک از آن سه قالب زمان را می‌سازد.

formatRelative

یک لحظه را نسبت به لحظه‌ای دیگر توصیف می‌کند: «۳ روز پیش»، «۲ ساعت دیگر».

بومی‌سازی به Intl.RelativeTimeFormat خودِ بستر سپرده می‌شود که از ۲۰۲۰ در همهٔ مرورگرهای مهم هست و قاعده‌های جمع و صرف هر زبان را از پیش می‌داند. formatRelative تنها همان بخشی را می‌آورد که Intl عمداً کنار گذاشته است: اینکه فاصله را با کدام یکا بیان کنیم.

مانند خودِ Intl تنها یک یکا گزارش می‌کند: فاصلهٔ ۳ روز و ۶ ساعت می‌شود «۳ روز پیش»، و هرگز «۳ روز و ۶ ساعت پیش» نمی‌شود.

پارامترها

پارامترتوضیحنوعپیش‌فرض
valueلحظه‌ای که می‌خواهی توصیف شودnumber | string | Dateالزامی
optionsپایین‌تر ببینFormatRelativeOptions{}
گزینهتوضیحنوعپیش‌فرض
nowآنچه فاصله نسبت به آن سنجیده می‌شودnumber | string | Dateزمان کنونی
localeبرچسب یا برچسب‌های BCP 47؛ سبک compact نادیده‌شان می‌گیردstring | string[]زبان محیط اجرا
style'long' | 'short' | 'narrow' | 'compact'RelativeStyle'long'
numericبا 'auto' تعبیرهایی مانند yesterday جایگزین می‌شوند؛ با 'always' عددها سر جایشان می‌مانند'always' | 'auto''auto'

Returns

string — همان توصیف، یا '' هرگاه یکی از دو سر قابل خواندن نباشد.

js
import { formatRelative } from 'ranuts/utils';

const twoHoursAgo = Date.now() - 2 * 3600_000;

formatRelative(twoHoursAgo); // '2 hours ago'
formatRelative(twoHoursAgo, { style: 'short' }); // '2 hr. ago'
formatRelative(twoHoursAgo, { locale: 'zh-CN' }); // '2 小时前'
formatRelative(Date.now() + 60_000); // 'in 1 minute'
formatRelative(Date.now() - 86_400_000); // 'yesterday'
formatRelative(Date.now() - 86_400_000, { numeric: 'always' }); // '1 day ago'

سبک compact

compact همان شکل فشردهٔ نشان‌گونه است که کنار آیتم‌های یک خوراک یا فهرست دیده می‌شود:

js
formatRelative(Date.now() - 30_000, { style: 'compact' }); // '30s'
formatRelative(Date.now() - 5 * 60_000, { style: 'compact' }); // '5m'
formatRelative(Date.now() - 3 * 3600_000, { style: 'compact' }); // '3h'
formatRelative(Date.now() - 2 * 86_400_000, { style: 'compact' }); // '2d'

جهت را نشان نمی‌دهد

compact تنها یک اندازه است، پس زمانی در آینده درست مانند زمانی در گذشته نمایش داده می‌شود (هر دو 5m). این سبک برای خوراک رویدادهای گذشته ساخته شده است. هرجا خواننده باید گذشته را از آینده بازشناسد، یکی از سبک‌های دیگر را به کار ببر.

parseVttTimestamp / parseVttCueTiming

خواندن زمان‌بندی زیرنویس WebVTT: همان خط‌های hh:mm:ss.mmm --> hh:mm:ss.mmm در یک فایل .vtt.

parseVttTimestamp یک مهر زمانی را (که hh: در آن اختیاری است) به ثانیه تبدیل می‌کند؛ parseVttCueTiming یک خط زمان‌بندی کامل را می‌خواند، یعنی هر دو سوی جداشده با --> را به { start, end } تبدیل می‌کند و تنظیم‌های نشانه‌ای که در انتها بیایند (align:start line:0) را نادیده می‌گیرد.

js
import { parseVttTimestamp, parseVttCueTiming } from 'ranuts/utils';

parseVttTimestamp('00:00:05.000'); // 5
parseVttTimestamp('01:05.250'); // 65.25
parseVttTimestamp('not a timestamp'); // undefined

parseVttCueTiming('00:00:00.000 --> 00:00:05.000'); // { start: 0, end: 5 }
parseVttCueTiming('00:00:05.000 --> 00:00:10.000 align:start line:0'); // { start: 5, end: 10 }

هر دو وقتی ورودی جور درنیاید undefined برمی‌گردانند و هرگز خطا نمی‌اندازند، پس یک خط بدشکل در فایل زیرنویس را می‌توان رد کرد به‌جای آنکه کل خواندن از هم بپاشد.

یادداشت‌ها

  1. گزینش یکا: formatRelative درشت‌ترین یکایی را برمی‌دارد که فاصله واقعاً آن را پر می‌کند و سپس درون همان گرد می‌کند. هرگاه گرد کردن روی آستانهٔ یکای بعدی بنشیند (۵۹٫۶ دقیقه که به «۶۰ دقیقه» گرد می‌شود)، یک پله بالا می‌رود تا «۱ ساعت پیش» خوانده شود.
  2. گرد کردن متقارن: اندازه گرد می‌شود و سپس علامت دوباره بر آن نهاده می‌شود، چون در جاوااسکریپت Math.round(-1.5) برابر -1 است و در غیر این صورت ۹۰ دقیقه پیش «۱ ساعت پیش» خوانده می‌شد حال آنکه ۹۰ دقیقه بعد «۲ ساعت دیگر» می‌شد.
  3. استفادهٔ دوباره از قالب‌بند: نمونه‌های Intl.RelativeTimeFormat برای هر ترکیب زبان، سبک و numeric در حافظهٔ نهان نگه داشته می‌شوند، پس فهرستی که صد مهر زمانی را می‌کشد یک قالب‌بند می‌سازد، نه صد تا.
  4. راه جایگزین: در محیط اجرایی که Intl.RelativeTimeFormat ندارد، خروجی به‌جای خطا انداختن به شکل فشرده بازمی‌گردد.

منتشرشده تحت مجوز MIT.