بیایید یک شیء درونساخت جدید را بشناسیم: Date. این شیء تاریخ و زمان را ذخیره میکند و متدهایی را برای مدیریت تاریخ/زمان فراهم میکند.
برای مثال، ما میتوانیم از آن برای ذخیرهسازی زمان ساختن/تغییردادن، اندازهگیری زمان یا فقط برای نمایش دادن زمان کنونی استفاده کنیم.
ایجاد
برای ایجاد یک شیء Date جدید باید new Date() را با یکی از آرگومانهای زیر صدا بزنیم:
new Date()-
بدون آرگومان – یک شیء
Dateبرای تاریخ و زمان کنونی میسازد: new Date(milliseconds)-
یک شیء
Dateبا زمانی برابر با تعداد میلیثانیههایی (milliseconds، 1/1000 ثانیه) که از اول ژانویه سال 1970 میلادی با UTC+0 گذشته است میسازد.عدد صحیحی که تعداد میلیثانیههای گذشته از شروع 1970 را نمایش میدهد را برچسب زمان (timestamp) میگویند.
برچسب زمان یک نمایش آسان از تاریخ است. ما همیشه میتوانیم با استفاده از
new Date(timestamp)یک تاریخ را از یک برچسب زمان بسازیم و شیءDateموجود را با استفاده از متدdate.getTime()به برچسب زمان تبدیل کنیم (ادامه متن را ببینید).:تاریخهای قبل از 01.01.1970 برچسب زمان منفی دارند، برای مثال
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 ثانیه) است
- پارامتر
دسترسی به اجزاء تاریخ
متدهایی برای دسترسی به سال، ماه و بقیه اجزاء در شیء 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 متفاوت باشد، کد پایین ساعتهای متفاوت را نشان میدهد:
علاوه بر متدهای داده شده، دو متد خاص هم وجود دارند که نوع UTC برای آنها وجود ندارد:
- getTime()
-
برچسب زمان را برای تاریخ برمیگرداند – عددی برابر با میلیثانیههایی که از اول ژانویه 1970 میلادی با UTC+0 گذشته است.
- getTimezoneOffset()
-
تقاوت بین UTC و منطقه زمانی محلی را به دقیقه برمیگرداند.
تنظیم کردن اجزاء تاریخ
متدهای زیر به ما اجازه تنظیم کردن اجزاء تاریخ را میدهند:
setFullYear(year, [month], [date])setMonth(month, [date])setDate(date)setHours(hour, [min], [sec], [ms])setMinutes(min, [sec], [ms])setSeconds(sec, [ms])setMilliseconds(ms)setTime(milliseconds)(تمام تاریخ را توسط میلیثانیههای گذشته از 01.01.1970 UTC+0 تنظیم میکند)
تمام آنها به جز setTime() یک نوع UTC دارند، برای مثال: setUTCHours().
همانطور که میبینیم، بعضی از متدها میتوانند چند جزء را همزمان تنظیم کنند، برای مثال setHours. اجزائی که ذکر نشوند تغییر داده نمیشوند.
برای مثال:
تصحیح خودکار
تصحیح خودکار یک خاصیت بسیار کاربردی شیءهای Date است. ما میتوانیم مقدارهای خارج از محدوده را قرار دهیم و شیء Date به طور خودکار خودش را تنظیم میکند.
برای مثال:
اجزاء خارج از محدوده تاریخ به طور خودکار توزیع میشوند.
بیایید فرض کنیم که ما نیاز داریم تاریخ «28 فوریه 2016» را دو روز به جلو ببریم. ممکن است برابر با «دوم مارس» یا اگر سال کبیسه باشد «اول مارس» شود. ما نیازی نداریم که به آن فکر کنیم. فقط 2 روز به آن اضافه کنید. شیء Date بقیه کار را انجام میدهد:
این ویژگی اغلب برای دریافت تاریخ بعد از مدت زمان داده شده استفاده میشود. برای مثال بیایید تاریخ «70 ثانیه پس از الان» را دریافت کنیم:
همچنین ما میتوانیم صفر یا مقدارهای منفی را قرار بدهیم. برای مثال:
تبدیل تاریخ به عدد، تقاوت تاریخ
زمانی که یک شیء Date به عدد تبدیل میشود، در واقع به برچسب زمان تبدیل میشود درست مانند date.getTime():
اثر جانبی مهم: تاریخها میتوانند از هم کم شوند، نتیجه برابر با تفاوت آنها در میلیثانیه است.
میتوان از آن برای اندازهگیری زمان استفاده کرد:
متد Date.now()
اگر ما فقط بخواهیم که زمان را اندازهگیری کنیم، احتیاجی به شیء Date نداریم.
یک متد خاص Date.now() وجود دارد که برچسب زمان کنونی را برمیگرداند.
از لحاظ معنا برابر با new Date().getTime() است اما یک شیء واسطه Date نمیسازد. پس سریعتر است و به زبالهروبی فشاری وارد نمیکند.
اغلب اوقات برای راحتی یا زمانی که عملکرد اهمیت دارد استفاده میشود، مانند بازیهای جاوااسکریپت یا برنامههای تخصصی دیگر.
پس احتمالا این بهتر است:
بِنچمارکگیری (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 را برمیگرداند.
برای مثال:
ما بلافاصله میتوانیم از برچسب زمان یک شیء new Date بسازیم:
خلاصه
- تاریخ و زمان در جاوااسکریپت با شیء Date نمایش داده میشوند. ما نمیتوانیم «فقط تاریخ» یا «فقط زمان» ایجاد کنیم: شیءهای
Dateهمیشه هر دو را دارند. - ماهها از صفر شمرده میشوند (بله، ژانویه ماه صفر است).
- روزهای هفته در
getDay()هم از صفر شمرده میشوند (که میشود یکشنبه). - شیء
Dateزمانی که اجزاء خارج از محدوده تنظیم شوند، خودش را به طور خودکار تصحیح میکند. این ویژگی برای اضافهکردن/کمکردن روزها/ماهها/ساعتها خوب است. - تاریخها میتوانند از هم کم شوند که برابر با تفاوت آنها به میلیثانیه است. به این دلیل که یک
Dateزمانی که به عدد تبدیل میشود، در واقع به برچسب زمان تبدیل میشود. - برای گرفتن سریع برچسب زمان کنونی از
Date.now()استفاده کنید.
در نظر داشته باشید که برخلاف بسیاری از سیستمهای دیگر، برچسب زمان در جاوااسکریپت به میلیثانیه است نه ثانیه.
بعضی اوقات ما به اندازهگیریهای دقیقتر زمان نیاز داریم. خود جاوااسکریپت راهی برای اندازهگیری زمان به میلیثانیه ندارد اما اکثر محیطهای اجرا آن را مهیا میکنند. برای مثال، مرورگر performance.now() را دارد که تعداد میلیثانیهها را با دقت میکروثانیه (3 رقم بعد از اعشار) از شروع بارگیری صفحه میدهد:
محیط Node.js ماژول microtime و راههای دیگری دارد. به طور فنی، تقریبا هر دستگاه و محیطی به ما امکان گرفتن دقت بیشتر را میدهد فقط در Date وجود ندارد.