بیایید یک شیء درونساخت جدید را بشناسیم: 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() );
تنظیم کردن اجزاء تاریخ
متدهای زیر به ما اجازه تنظیم کردن اجزاء تاریخ را میدهند:
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
. اجزائی که ذکر نشوند تغییر داده نمیشوند.
برای مثال:
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
وجود ندارد.