۲۵ ژوئیه ۲۰۲۲

تاریخ و زمان

بیایید یک شیء درون‌ساخت جدید را بشناسیم: Date. این شیء تاریخ و زمان را ذخیره می‌کند و متدهایی را برای مدیریت تاریخ/زمان فراهم می‌کند.

برای مثال، ما می‌توانیم از آن برای ذخیره‌سازی زمان ساختن/تغییردادن، اندازه‌گیری زمان یا فقط برای نمایش دادن زمان کنونی استفاده کنیم.

ایجاد

برای ایجاد یک شیء Date جدید باید new Date() را با یکی از آرگومان‌های زیر صدا بزنیم:

new Date()

بدون آرگومان – یک شیء Date برای تاریخ و زمان کنونی می‌سازد:

let now = new Date();
alert( now ); // تاریخ/زمان کنونی را نمایش می‌دهد
new Date(milliseconds)

یک شیء Date با زمانی برابر با تعداد میلی‌ثانیه‌هایی (milliseconds، 1/1000 ثانیه) که از اول ژانویه سال 1970 میلادی با UTC+0 گذشته است می‌سازد.

// 01.01.1970 UTC+0 صفر یعنی
let Jan01_1970 = new Date(0);
alert( Jan01_1970 );

// 02.01.1970 UTC+0 :حالا 24 ساعت اضافه می‌کنیم و تاریخی که دریافت می‌کنیم
let Jan02_1970 = new Date(24 * 3600 * 1000);
alert( Jan02_1970 );

عدد صحیحی که تعداد میلی‌ثانیه‌های گذشته از شروع 1970 را نمایش می‌دهد را برچسب زمان (timestamp) می‌گویند.

برچسب زمان یک نمایش آسان از تاریخ است. ما همیشه می‌توانیم با استفاده از new Date(timestamp) یک تاریخ را از یک برچسب زمان بسازیم و شیء Date موجود را با استفاده از متد date.getTime() به برچسب زمان تبدیل کنیم (ادامه متن را ببینید).

:تاریخ‌های قبل از 01.01.1970 برچسب زمان منفی دارند، برای مثال

// سی و یک دسامبر 1969
let Dec31_1969 = new Date(-24 * 3600 * 1000);
alert( Dec31_1969 );
new Date(datestring)

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

let date = new Date("2017-01-26");
alert(date);
// باشد GMT زمان تنظیم نشده است پس فرض می‌شود که نیمه شب
// و با توجه به منطقه‌زمانی‌ای که کد در آن اجرا می‌شود تنظیم می‌شود
// :پس نتیجه می‌تواند این باشد
// Thu Jan 26 2017 11:00:00 GMT+1100 (Australian Eastern Daylight Time)
// یا
// Wed Jan 25 2017 16:00:00 GMT-0800 (Pacific Standard Time)
new Date(year, month, date, hours, minutes, seconds, ms)

یک تاریخ با مؤلفه‎های داده شده در منطقه‌زمانی محلی می‌سازد. فقط دو آرگومان اول ضروری هستند.

  • پارامتر year باید 4 رقم باشد: برای سازگاری، 2 رقم هم مورد قبول است و به صورت 19xx فرض می‌شود، برای مثال 98 با 1998 یکسان است اما همیشه استفاده از 4 رقم پیشنهاد می‌شود.
  • شمارش پارامتر month از 0 (ژانویه) تا 11 (دسامبر) است.
  • پارامتر date در واقع روز ماه است، اگر وارد نشود 1 فرض می‌شود.
  • اگر hours/minutes/seconds/ms وارد نشوند،برای آنها 0 در نظر گرفته می‌شود.

برای مثال:

new Date(2011, 0, 1, 0, 0, 0, 0); // اول ژانویه 2011، ساعت 00:00:00
new Date(2011, 0, 1); // یکسان است، ساعت و بقیه پارامترها به طور پیش‌فرض 0 هستند

:بیشترین دقت 1 میلی‌ثانیه (1/1000 ثانیه) است

let date = new Date(2011, 0, 1, 2, 3, 4, 567);
alert( date ); // 1.01.2011, 02:03:04.567

دسترسی به اجزاء تاریخ

متدهایی برای دسترسی به سال، ماه و بقیه اجزاء در شیء Date وجود دارد:

getFullYear()
دریافت سال (4 رقم)
getMonth()
دریافت ماه، از 0 تا 11.
getDate()
دریافت روز ماه، از 1 تا 31، اسم متد واقعا کمی عجیب بنظر می‌رسد.
getHours()، getMinutes()، getSeconds()، getMilliseconds()
جزء متناظر با خود را دریافت می‌کنند یعنی به ترتیب: ساعت، دقیقه، ثانیه و میلی‌ثانیه.
متد getYear() درست نیست بلکه getFullYear() درست است

بسیاری از موتورهای جاوااسکریپت یک متد غیر استاندارد getYear() را پیاده‌سازی می‌کنند. این متد منسوخ شده است. بعضی اوقات یک سال 2 رقمی را برمی‌گرداند. لطفا هیچ‌وقت از آن استفاده نکنید. متد getFullYear() برای سال وجود دارد.

علاوه بر این، ما می‌توانیم روز هفته را هم دریافت کنیم:

getDay()
دریافت روز هفته، از 0 (Sunday، یکشنبه) تا 6 (Saturday، شنبه). اولین روز همیشه Sunday (یکشنبه) است و در بعضی از کشورها اینگونه نیست اما نمی‌توان آن را تغییر داد.

تمام متدهای بالا اجزاء را با توجه به منطقه زمانی محلی برمی‌گردانند.

همچنین نقطه مقابل آنها در UTC هم وجود دارد که روز، ماه، سال و بقیه را برای منطقه زمانی UTC+0 برمی‌گرداند: getUTCFullYear()، getUTCMonth()، getUTCDay(). فقط "UTC" را بعد از "get" اضافه کنید.

اگر منطقه زمانی شما نسبت به UTC متفاوت باشد، کد پایین ساعت‌های متفاوت را نشان می‌دهد:

// تاریخ کنونی
let date = new Date();

// ساعت در منطقه زمانی کنونی شما
alert( date.getHours() );

// (زمان شهر لندن بدون ساعت تابستانی) UTC+0 ساعت در منطقه زمانی
alert( date.getUTCHours() );

علاوه بر متدهای داده شده، دو متد خاص هم وجود دارند که نوع UTC برای آنها وجود ندارد:

getTime()

برچسب زمان را برای تاریخ برمی‌گرداند – عددی برابر با میلی‌ثانیه‌هایی که از اول ژانویه 1970 میلادی با UTC+0 گذشته است.

getTimezoneOffset()

تقاوت بین UTC و منطقه زمانی محلی را به دقیقه برمی‌گرداند.

// باشید، خروجی 60 می‌دهد UTC-1 اگر شما در منطقه زمانی
// باشید، خروجی 180- می‌دهد UTC+3 اگر در منطقه زمانی
alert( new Date().getTimezoneOffset() );

تنظیم کردن اجزاء تاریخ

متدهای زیر به ما اجازه تنظیم کردن اجزاء تاریخ را می‌دهند:

تمام آنها به جز setTime() یک نوع UTC دارند، برای مثال: setUTCHours().

همانطور که می‌بینیم، بعضی از متدها می‌توانند چند جزء را همزمان تنظیم کنند، برای مثال setHours. اجزائی که ذکر نشوند تغییر داده نمی‌شوند.

برای مثال:

let today = new Date();

today.setHours(0);
alert(today); // .هنوز امروز است اما ساعت به 0 تغییر داده شد
today.setHours(0, 0, 0, 0);
alert(today); // .هنوز هم امروز است، الان ساعت دقیقا 00:00:00 است

تصحیح خودکار

تصحیح خودکار یک خاصیت بسیار کاربردی شیءهای Date است. ما می‌توانیم مقدارهای خارج از محدوده را قرار دهیم و شیء Date به طور خودکار خودش را تنظیم می‌کند.

برای مثال:

let date = new Date(2013, 0, 32); // 32 Jan 2013 ?!?
alert(date); // !برابر است با اول فوریه 2013...

اجزاء خارج از محدوده تاریخ به طور خودکار توزیع می‌شوند.

بیایید فرض کنیم که ما نیاز داریم تاریخ «28 فوریه 2016» را دو روز به جلو ببریم. ممکن است برابر با «دوم مارس» یا اگر سال کبیسه باشد «اول مارس» شود. ما نیازی نداریم که به آن فکر کنیم. فقط 2 روز به آن اضافه کنید. شیء Date بقیه کار را انجام می‌دهد:

let date = new Date(2016, 1, 28);
date.setDate(date.getDate() + 2);

alert( date ); // اول مارس 2016

این ویژگی اغلب برای دریافت تاریخ بعد از مدت زمان داده شده استفاده می‌شود. برای مثال بیایید تاریخ «70 ثانیه پس از الان» را دریافت کنیم:

let date = new Date();
date.setSeconds(date.getSeconds() + 70);

alert( date ); // تاریخ درست را نمایش می‌دهد

همچنین ما می‌توانیم صفر یا مقدارهای منفی را قرار بدهیم. برای مثال:

let date = new Date(2016, 0, 2); // دوم ژانویه 2016

date.setDate(1); // روز اول ماه را تنظیم کنید
alert( date );

date.setDate(0); // کمترین روز 1 است، پس آخرین روز ماه قبل فرض می‌شود
alert( date ); // سی و یک دسامبر 2015

تبدیل تاریخ به عدد، تقاوت تاریخ

زمانی که یک شیء Date به عدد تبدیل می‌شود، در واقع به برچسب زمان تبدیل می‌شود درست مانند date.getTime():

let date = new Date();
alert(+date); // date.getTime() تعداد میلی‌ثانیه‌ها، مانند

اثر جانبی مهم: تاریخ‌ها می‌توانند از هم کم شوند، نتیجه برابر با تفاوت آنها در میلی‌ثانیه است.

می‌توان از آن برای اندازه‌گیری زمان استفاده کرد:

let start = new Date(); // شروع به اندازه‌گیری زمان

// کارتان را انجام دهید
for (let i = 0; i < 100000; i++) {
  let doSomething = i * i * i;
}

let end = new Date(); // اندازه‌گیری زمان را به پایان برسانید

alert( `حلقه ${end - start} میلی‌ثانیه طول کشید` );

متد Date.now()

اگر ما فقط بخواهیم که زمان را اندازه‌گیری کنیم، احتیاجی به شیء Date نداریم.

یک متد خاص Date.now() وجود دارد که برچسب زمان کنونی را برمی‌گرداند.

از لحاظ معنا برابر با new Date().getTime() است اما یک شیء واسطه Date نمی‌سازد. پس سریع‌تر است و به زباله‌روبی فشاری وارد نمی‌کند.

اغلب اوقات برای راحتی یا زمانی که عملکرد اهمیت دارد استفاده می‌شود، مانند بازی‌های جاوااسکریپت یا برنامه‌های تخصصی دیگر.

پس احتمالا این بهتر است:

let start = Date.now(); // تعداد میلی‌ثانیه‌ها از اول ژانویه 1970

// کارتان را انجام دهید
for (let i = 0; i < 100000; i++) {
  let doSomething = i * i * i;
}

let end = Date.now(); // تمام

alert( `The loop took ${end - start} ms` ); // اعداد را از هم کم کنید نه تاریخ‌ها را

بِنچمارک‌گیری (Benchmarking)

اگر ما یک بنچمارک قابل اطمینان از تابعی که پردازنده (CPU) را خیلی مشغول می‌کند بخواهیم، باشد مراقب باشیم.

برای مثال، بیایید دو تابع که تفاوت بین دو تاریخ را محاسبه می‌کنند را اندازه‌گیری کنیم: کدام یک از آنها سریع‌تر است؟

چنین اندازه‌گیری‌های عملکردی را «بِنچمارک» می‌گویند.

// را داریم، کدام تابع تفاوت آنها به میلی‌ثانیه را سریع‌تر برمی‌گرداند؟ date2 و date1 ما
function diffSubtract(date1, date2) {
  return date2 - date1;
}

// یا
function diffGetTime(date1, date2) {
  return date2.getTime() - date1.getTime();
}

این دو تابع دقیقا یک کار را انجام می‌دهند اما یکی از آنها برای گرفتن تاریخ به میلی‌ثانیه از متد صریح date.getTime() استفاده می‌کند، و دیگری به تبدیل تاریخ به عدد متکی است. نتیجه آنها همیشه یکسان است.

خب، کدام یک سریع‌تر است؟

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

بیایید اندازه بگیریم:

function diffSubtract(date1, date2) {
  return date2 - date1;
}

function diffGetTime(date1, date2) {
  return date2.getTime() - date1.getTime();
}

function bench(f) {
  let date1 = new Date(0);
  let date2 = new Date();

  let start = Date.now();
  for (let i = 0; i < 100000; i++) f(date1, date2);
  return Date.now() - start;
}

alert( 'Time of diffSubtract: ' + bench(diffSubtract) + 'ms' );
alert( 'Time of diffGetTime: ' + bench(diffGetTime) + 'ms' );

عجب! استفاده از getTime() خیلی سریع‌تر است! به این دلیل که هیچ تبدیلی برای نوع انجام نمی‌شود و از لحاظ بهینه‌سازی برای موتورها خیلی سریع‌تر است.

خب این از هیچی بهتر است. اما هنوز یک بنچمارک خوب نیست.

فرض کنید که در حین اجرای bench(diffSubtract) پردازنده همزمان درحال انجام کاری بود و منابع اشغال می‌شدند. و تا قبل از اجرای bech(diffGetTime) آن کار تمام می‌شد.

یک سناریو کاملا واقعی برای یک سیستم‌عامل چند وظیفه‌ای مدرن.

در نتیجه، بنچمارک اول منابع پردازنده کمتری نسبت به دومی در اختیار خواهد داشت. اینگونه ممکن است نتایج اشتباه پدید بیایند.

برای بنچمارک قابل اطمینان‌تر، تمام بنچمارک‌ها باشد برای چند بار دوباره اجرا شوند.

برای مثال، اینگونه:

function diffSubtract(date1, date2) {
  return date2 - date1;
}

function diffGetTime(date1, date2) {
  return date2.getTime() - date1.getTime();
}

function bench(f) {
  let date1 = new Date(0);
  let date2 = new Date();

  let start = Date.now();
  for (let i = 0; i < 100000; i++) f(date1, date2);
  return Date.now() - start;
}

let time1 = 0;
let time2 = 0;

// را به طور متناوب 10 بار اجرا کن bench(diffSubtract) و bench(diffGetTime) هر کدام از
for (let i = 0; i < 10; i++) {
  time1 += bench(diffSubtract);
  time2 += bench(diffGetTime);
}

alert( 'Total time for diffSubtract: ' + time1 );
alert( 'Total time for diffGetTime: ' + time2 );

موتورهای مدرن جاوااسکریپت فقط روی «کدهای خاص» که به تعداد زیادی اجرا می‌شوند، بهینه‌سازی‌های پیشرفته را اعمال می‌کنند (نیازی به بهینه‌سازی چیزهایی که کم اجرا می‌شوند نیست). پس در مثال بالا، اجراهای اولیه کد خوب بهینه‌سازی نمی‌شوند. شاید بخواهیم یک اجرای آماده‌سازی اضافه کنیم:

// برای «آماده‌سازی» قبل از حلقه اصلی اضافه شدند
bench(diffSubtract);
bench(diffGetTime);

// حالا بنچمارک
for (let i = 0; i < 10; i++) {
  time1 += bench(diffSubtract);
  time2 += bench(diffGetTime);
}
در حین انجام بنچمارک‌های کوچک مراقب باشید

موتورهای مدرن جاوااسکریپت بهینه‌سازی‌های زیادی انجام می‌دهند. آنها ممکن است نتایج «آزمایش‌های ساختگی» را نسبت به «استفاده معمولی» تغییر دهند، مخصوصا زمانی که ما از چیزی بسیار کوچک مانند اینکه یک عملگر یا یک تابع درون‌ساخت چگونه کار می‌کند را بنچمارک بگیریم. پس اگر به طور جدی می‌خواهید عملکرد را متوجه شوید، لطفا درباره اینکه موتورهای جاوااسکریپت چگونه کار می‌کنند مطالعه کنید. سپس ممکن است کلا به بنچمارک‌های کوچک احتیاجی نداشته باشید.

می‌توانید یک دسته از مقاله‌های عالی درباره V8 را در http://mrale.ph پیدا کنید.

تجزیه رشته به تاریخ با Date.parse

متد Date.parse(str) می‌تواند یک تاریخ را از یک رشته بخواند.

شکل رشته باید به این فرمت باشد: YYYY-MM-DDTHH:mm:ss.sssZ، با این شرایط:

  • YYYY-MM-DD – تاریخ است: روز-ماه-سال
  • کاراکتر "T" به عنوان جداکننده استفاده می‌شود.
  • HH:mm:ss.sss – زمان است: میلی‌ثانیه‌ها، ثانیه‌ها، دقیقه‌ها و ساعت‌ها.
  • قسمت اختیاری 'Z' نشان‌دهنده منطقه زمانی به شکل +-hh:mm است. حرف تنهای Z به معنای UTC+0 است.

انواع کوتاه‌تر رشته هم وجود دارند مثل YYYY-MM-DD یا YYYY-MM یا حتی YYYY.

فراخوانی Date.parse(str) رشته را به فرمت داده شده تجزیه می‌کند و برچسب زمان (تعداد میلی‌ثانیه‌های گذشته از اول ژانویه 1970 با UTC+0) را برمی‌گرداند. اگر فرمت قابل قبول نباشد، NaN را برمی‌گرداند.

برای مثال:

let ms = Date.parse('2012-01-26T13:51:50.417-07:00');

alert(ms); // 1327611110417  (برچسب زمان)

ما بلافاصله می‌توانیم از برچسب زمان یک شیء new Date بسازیم:

let date = new Date( Date.parse('2012-01-26T13:51:50.417-07:00') );

alert(date);

خلاصه

  • تاریخ و زمان در جاوااسکریپت با شیء Date نمایش داده می‌شوند. ما نمی‌توانیم «فقط تاریخ» یا «فقط زمان» ایجاد کنیم: شیءهای Date همیشه هر دو را دارند.
  • ماه‌ها از صفر شمرده می‌شوند (بله، ژانویه ماه صفر است).
  • روزهای هفته در getDay() هم از صفر شمرده می‌شوند (که می‌شود یکشنبه).
  • شیء Date زمانی که اجزاء خارج از محدوده تنظیم شوند، خودش را به طور خودکار تصحیح می‌کند. این ویژگی برای اضافه‌کردن/کم‌کردن روزها/ماه‌ها/ساعت‌ها خوب است.
  • تاریخ‌ها می‌توانند از هم کم شوند که برابر با تفاوت آنها به میلی‌ثانیه است. به این دلیل که یک Date زمانی که به عدد تبدیل می‌شود، در واقع به برچسب زمان تبدیل می‌شود.
  • برای گرفتن سریع برچسب زمان کنونی از Date.now() استفاده کنید.

در نظر داشته باشید که برخلاف بسیاری از سیستم‌های دیگر، برچسب زمان در جاوااسکریپت به میلی‌ثانیه است نه ثانیه.

بعضی اوقات ما به اندازه‌گیری‌های دقیق‌تر زمان نیاز داریم. خود جاوااسکریپت راهی برای اندازه‌گیری زمان به میلی‌ثانیه ندارد اما اکثر محیط‌های اجرا آن را مهیا می‌کنند. برای مثال، مرورگر performance.now() را دارد که تعداد میلی‌ثانیه‌ها را با دقت میکروثانیه (3 رقم بعد از اعشار) از شروع بارگیری صفحه می‌دهد:

alert(`بارگیری ${performance.now()} میلی‌ثانیه پیش شروع شد`);
// «چیزی شبیه به این: «بارگیری 34731.26000000001 میلی‌ثانیه پیش شروع شد
// قسمت 26. میکروثانیه است (260 میکروثانیه)
// بیشتر از 3 رقم بعد از اعشار ارورهای دقت اندازه‌گیری است، فقط 3 رقم اول درست هستند

محیط Node.js ماژول microtime و راه‌های دیگری دارد. به طور فنی، تقریبا هر دستگاه و محیطی به ما امکان گرفتن دقت بیشتر را می‌دهد فقط در Date وجود ندارد.

تمارین

اهمیت: 5

یک شیء Date برای این تاریخ بسازید: 20 فوریه 2012، ساعت 3:12 قبل از ظهر. منطقه زمانی محلی است.

با استفاده از alert آن را نمایش دهید.

تابع سازنده new Date از منطقه زمانی محلی استفاده می‌کند. پس تنها چیز مهم به یاد داشتن این است که ماه‌ها از صفر شروع می‌شوند.

پس فوریه عدد 1 را دارد.

اینجا یک مثال داریم که دارای اعداد برای اجزاء تاریخ است:

//new Date(year, month, date, hour, minute, second, millisecond)
let d1 = new Date(2012, 1, 20, 3, 12);
alert( d1 );

ما می‌توانستیم یک تاریخ از یک رشته بسازیم، مانند این:

//new Date(datastring)
let d2 = new Date("2012-02-20T03:12");
alert( d2 );
اهمیت: 5

یک تابع getWeekDay(date) بنویسید که روز هفته را به این شکل نشان می‌دهد: ‘MO’، ‘TU’، ‘WE’، ‘TH’، ‘FR’، ‘SA’، ‘SU’.

برای مثال:

let date = new Date(2012, 0, 3);  // 3 Jan 2012
alert( getWeekDay(date) );        // را نشان دهد "TU" باید

باز کردن یک sandbox همراه با تست‌ها.

متد date.getDay() عدد روز هفته را برمی‌گرداند که از یکشنبه شروع می‌شود.

بیایید یک آرایه از روزهای هفته بسازیم تا بتوانیم اسم روز درست را با عدد آن دریافت کنیم:

function getWeekDay(date) {
  let days = ['SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA'];

  return days[date.getDay()];
}

let date = new Date(2014, 0, 3); // 3 Jan 2014
alert( getWeekDay(date) ); // FR
function getWeekDay(date) {
  let days = ['SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA'];

  return days[date.getDay()];
}

باز کردن راه‌حل همراه با تست‌ها درون یک sandbox.

اهمیت: 5

کشورهای اروپایی روزهای هفته‌شان از دوشنبه (عدد 1) شروع می‌شود، سپس سه‌شنبه (عدد 2) و تا یکشنبه (عدد 7) ادامه دارد. یک تابع getLocalDay(date) بنویسید که روز هفته «اروپایی» را به ازای date برگرداند.

let date = new Date(2012, 0, 3);  // 3 Jan 2012
alert( getLocalDay(date) );       // سه‌شنبه، باید 2 را برگرداند

باز کردن یک sandbox همراه با تست‌ها.

function getLocalDay(date) {

  let day = date.getDay();

  if (day == 0) { // روز هفته 0 (یکشنبه) به اروپایی برابر با 7 است
    day = 7;
  }

  return day;
}

باز کردن راه‌حل همراه با تست‌ها درون یک sandbox.

اهمیت: 4

یک تابع getDateAgo(date, days) بسازید که روز ماه را به اندازه days روز قبل از date برگرداند.

برای مثال، اگر امروز 20ام باشد، سپس getDateAgo(new Date(), 1) باید 19ام را برگرداند و getDateAgo(new Date(), 2) باید 18ام باشد.

باید به ازای days=365 یا بیشتر به درستی کار کند:

let date = new Date(2015, 0, 2);

alert( getDateAgo(date, 1) ); // 1, (1 Jan 2015)
alert( getDateAgo(date, 2) ); // 31, (31 Dec 2014)
alert( getDateAgo(date, 365) ); // 2, (2 Jan 2014)

پی‌نوشت: تابع نباید date داده شده را تغییر دهد.

باز کردن یک sandbox همراه با تست‌ها.

ایده ساده‌ای است: تعداد روز داده شده را از date کم کنید:

function getDateAgo(date, days) {
  date.setDate(date.getDate() - days);
  return date.getDate();
}

…اما تابع نباید date را تغییر دهد. این مهم است چون کد بیرونی که به ما تاریخ را می‌دهد توقع ندارد که تاریخ تغییر کند.

برای پیاده‌سازی آن بیایید از تاریخ یک مشابه بسازیم، مانند این:

function getDateAgo(date, days) {
  let dateCopy = new Date(date);

  dateCopy.setDate(date.getDate() - days);
  return dateCopy.getDate();
}

let date = new Date(2015, 0, 2);

alert( getDateAgo(date, 1) ); // 1, (1 Jan 2015)
alert( getDateAgo(date, 2) ); // 31, (31 Dec 2014)
alert( getDateAgo(date, 365) ); // 2, (2 Jan 2014)
function getDateAgo(date, days) {
  let dateCopy = new Date(date);

  dateCopy.setDate(date.getDate() - days);
  return dateCopy.getDate();
}

باز کردن راه‌حل همراه با تست‌ها درون یک sandbox.

اهمیت: 5

تابع getLastDayOfMonth(year, month) را بنویسید که آخرین روز ماه را برمی‌گرداند. بعضی اوقات آخرین روز 30ام، 31ام یا حتی برای ماه فوریه 28/29ام است.

پارامترها:

  • year – سال 4 رقمی، برای مثال 2012.
  • month – ماه، از 0 تا 11.

برای مثال، getLastDayOfMonth(2012, 1) = 29 (سال کبیسه، فوریه).

باز کردن یک sandbox همراه با تست‌ها.

بیایید یک تاریخ با استفاده از ماه بعد بسازیم اما برای روز 0 را قرار دهیم:

function getLastDayOfMonth(year, month) {
  let date = new Date(year, month + 1, 0);
  return date.getDate();
}

alert( getLastDayOfMonth(2012, 0) ); // 31
alert( getLastDayOfMonth(2012, 1) ); // 29
alert( getLastDayOfMonth(2013, 1) ); // 28

به صورت طبیعی، روزها از 1 شروع می‌شوند اما از لحاظ فنی ما می‌توانیم هر عددی را قرار دهیم، شیء date به طور خودکار خودش را تنظیم می‌کند. پس زمانی که ما 0 قرار می‌دهیم، به این معنی است که «یک روز قبل از اولین روز ماه» یا به عبارتی دیگر: «آخرین روز ماه قبل».

function getLastDayOfMonth(year, month) {
  let date = new Date(year, month + 1, 0);
  return date.getDate();
}

باز کردن راه‌حل همراه با تست‌ها درون یک sandbox.

اهمیت: 5

تابع getSecondsToday() را بنویسید که تعداد ثانیه‌هایی که از شروع روز گذشته است را برگرداند.

برای مثال، اگر الان 10:00 am (قبل از ظهر) باشد و ساعت تابستانی هم وجود نداشته باشد، سپس داریم:

getSecondsToday() == 36000 // (3600 * 10)

تابع باید به ازای هر روزی کار کند. یعنی اینکه نباید برای «امروز» کد اختصاصی داشته باشد.

برای دریافت تعداد ثانیه‌ها، ما می‌توانیم با استفاده از روز کنونی و ساعت 00:00:00 یک تاریخ بسازیم، سپس آن را از «الان» کم کنیم.

تفاضل برابر با تعداد میلی‌ثانیه‌ها از شروع امروز است که ما باید برای دریافت ثانیه‌ها آن را بر 1000 تقسیم کنیم:

function getSecondsToday() {
  let now = new Date();

  // با استفاده از سال/ماه/روز کنونی یک شیء بسازید
  let today = new Date(now.getFullYear(), now.getMonth(), now.getDate());

  let diff = now - today; // تفاضل به میلی‌ثانیه
  return Math.round(diff / 1000); // ایجاد ثانیه
}

alert( getSecondsToday() );

یک راه حل جایگزین می‌تواند این باشد که ساعت/دقیقه/ثانیه را دریافت کنیم و آنها را به ثانیه تبدیل کنیم:

function getSecondsToday() {
  let d = new Date();
  return d.getHours() * 3600 + d.getMinutes() * 60 + d.getSeconds();
}

alert( getSecondsToday() );
اهمیت: 5

تابع getSecondsToTomorrow() بسازید که تعداد ثانیه‌ها را تا فردا برمی‌گرداند.

برای مثال، اگر الان 23:00 باشد داریم:

getSecondsToTomorrow() == 3600

پی‌نوشت: تابع باشد به ازای هر روزی کار کند، «امروز» کد اختصاصی ندارد.

برای گرفتن تعداد میلی‌ثانیه‌ها تا فردا، می‌توانیم از «فردا 00:00:00» تاریخ کنونی را کم کنیم.

اول، ما «فردا» را ایجاد می‌کنیم و سپس این کار را انجام می‌دهیم:

function getSecondsToTomorrow() {
  let now = new Date();

  // تاریخ فردا
  let tomorrow = new Date(now.getFullYear(), now.getMonth(), now.getDate()+1);

  let diff = tomorrow - now; // تفاضل به میلی‌ثانیه
  return Math.round(diff / 1000); // تبدیل به ثانیه
}

راه حل جایگزین:

function getSecondsToTomorrow() {
  let now = new Date();
  let hour = now.getHours();
  let minutes = now.getMinutes();
  let seconds = now.getSeconds();
  let totalSecondsToday = (hour * 60 + minutes) * 60 + seconds;
  let totalSecondsInADay = 86400;

  return totalSecondsInADay - totalSecondsToday;
}

لطفا در نظر داشته باشید که بسیاری از کشورها ساعت تابستانی دارند (DST)، پس ممکن است بعضی از روزها 23 یا 25 ساعت داشته باشند. شاید بهتر است با چنین روزهایی متفاوت رفتار کنیم.

اهمیت: 4

تابع formatDate(date) بنویسید که باید date را به صورت زیر تغییر شکل دهد:

  • اگر از date کمتر از 1 ثانیه گذشته باشد، سپس شکل جدید "right now"(همین حالا) است.
  • در غیر این صورت اگر از date کمتر از 1 دقیقه گذشته باشد، سپس شکل چدید "n sec. ago"(n ثانیه قبل) است.
  • در غیر این صورت اگر کمتر از یک ساعت باشد، سپس شکل جدید "m min. ago"(m دقیقه پیش) است.
  • در غیر این صورت، تاریخ کامل با به شکل "DD.MM.YY HH:mm" باشد. یعنی: "day.month.year hours:minutes"، همه به شکل دو رقمی مانند 10:00 31.12.16.

برای مثال:

alert( formatDate(new Date(new Date - 1)) ); // "right now"

alert( formatDate(new Date(new Date - 30 * 1000)) ); // "30 sec. ago"

alert( formatDate(new Date(new Date - 5 * 60 * 1000)) ); // "5 min. ago"

// تاریخ دیروز مانند 20:00 31.12.16
alert( formatDate(new Date(new Date - 86400 * 1000)) );

باز کردن یک sandbox همراه با تست‌ها.

برای گرفتن زمان از date تا الان، بیایید تفاضل تاریخ‌ها را بیابیم.

function formatDate(date) {
  let diff = new Date() - date; // تفاضل به میلی‌ثانیه

  if (diff < 1000) { // less than 1 second
    return 'right now';
  }

  let sec = Math.floor(diff / 1000); // به ثانیه diff تبدیل

  if (sec < 60) {
    return sec + ' sec. ago';
  }

  let min = Math.floor(diff / 60000); // به دقیقه diff تبدیل
  if (min < 60) {
    return min + ' min. ago';
  }

  // تغییر دادن شکل تاریخ
  // اضافه کردن صفر به دقیقه/ساعت/ماه/روز تک رقمی
  let d = date;
  d = [
    '0' + d.getDate(),
    '0' + (d.getMonth() + 1),
    '' + d.getFullYear(),
    '0' + d.getHours(),
    '0' + d.getMinutes()
  ].map(component => component.slice(-2)); // دریافت 2 رقم آخر هر جزء

  // متصل کردن اجزاء برای ایجاد تاریخ
  return d.slice(0, 3).join('.') + ' ' + d.slice(3).join(':');
}

alert( formatDate(new Date(new Date - 1)) ); // "right now"

alert( formatDate(new Date(new Date - 30 * 1000)) ); // "30 sec. ago"

alert( formatDate(new Date(new Date - 5 * 60 * 1000)) ); // "5 min. ago"

// تاریخ دیروز مانند 20:00 31.12.2016
alert( formatDate(new Date(new Date - 86400 * 1000)) );

راه حل جایگزین:

function formatDate(date) {
  let dayOfMonth = date.getDate();
  let month = date.getMonth() + 1;
  let year = date.getFullYear();
  let hour = date.getHours();
  let minutes = date.getMinutes();
  let diffMs = new Date() - date;
  let diffSec = Math.round(diffMs / 1000);
  let diffMin = diffSec / 60;
  let diffHour = diffMin / 60;

  // تغییر دادن شکل
  year = year.toString().slice(-2);
  month = month < 10 ? '0' + month : month;
  dayOfMonth = dayOfMonth < 10 ? '0' + dayOfMonth : dayOfMonth;
  hour = hour < 10 ? '0' + hour : hour;
  minutes = minutes < 10 ? '0' + minutes : minutes;

  if (diffSec < 1) {
    return 'right now';
  } else if (diffMin < 1) {
    return `${diffSec} sec. ago`
  } else if (diffHour < 1) {
    return `${diffMin} min. ago`
  } else {
    return `${dayOfMonth}.${month}.${year} ${hour}:${minutes}`
  }
}

باز کردن راه‌حل همراه با تست‌ها درون یک sandbox.

نقشه آموزش