Познайомтеся з новим вбудованим об’єктом: Date. Він зберігає дату, час і надає методи для управління датою/часом.
Наприклад, ми можемо використовувати його для зберігання часу створення/модифікації, щоб виміряти час, або просто для друку поточної дати.
Створення
Щоб створити новий об’єкт Date
викличемо new Date()
з одним з наступних аргументів:
new Date()
-
Без аргументів – створює об’єкт
Date
для поточної дати та часу:let now = new Date(); alert( now ); // показує поточну дату/час
new Date(milliseconds)
-
Створює об’єкт
Date
з часом, що дорівнює кількості мілісекунд (1/1000 секунди), що минули після 1 січня 1970 UTC+0.// 0 означає 01.01.1970 UTC+0 let Jan01_1970 = new Date(0); alert( Jan01_1970 ); // тепер додамо 24 години, отримаємо 02.01.1970 UTC+0 let Jan02_1970 = new Date(24 * 3600 * 1000); alert( Jan02_1970 );
Ціле число, яке являє собою кількість мілісекунд, що пройшли з початку 1970 року, називається міткою часу (timestamp).
Це легке числове представлення дати. Ми завжди можемо створити дату з timestamp за допомогою
new Date(timestamp)
і перетворити об’єктDate
, що існує, до timestamp за допомогою методуdate.getTime()
(див. нижче).Дати до 01.01.1970 р. мають негативний timestamp, наприклад:
// 31 грудня 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 цифри.- Рахунок місяців починається з
0
(січня), до11
(грудня). - Параметр
date
насправді день місяця, якщо він відсутній, то береться1
. - Якщо
hours/minutes/seconds/ms
відсутні, вони вважаються рівними0
.
Наприклад:
new Date(2011, 0, 1, 0, 0, 0, 0); // 1 січня 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()
Багато рушіїв JavaScript реалізують нестандартний метод getYear()
. Цей метод застарілий. Іноді він повертає 2-значний рік. Будь ласка, не використовуйте його. Щоб отримати рік є getFullYear()
.
Крім того, ми можемо отримати день тижня:
- getDay()
- Отримати день тижня, від
0
(неділя) до6
(субота). Перший день завжди неділя, в деяких країнах це не так, але ми не можемо змінити поведінку методу.
Всі методи, наведені вище, повертають компоненти відносно місцевого часового поясу.
Є також їхні UTC-аналоги, що повертається день, місяць, рік і так далі за часовим поясом UTC+0: getUTCFullYear(), getUTCMonth(), getUTCDay(). Просто вставте "UTC"
відразу після "get"
.
Якщо ваш місцевий часовий пояс зміщений відносно UTC, то код нижче показує різні години:
// поточна дата
let date = new Date();
// години у поточному часовому поясі
alert( date.getHours() );
// години у часовій зоні UTC+0 (Лондонський час без літніх переводів часу)
alert( date.getUTCHours() );
Окрім наведених методів, є два спеціальних, які не мають UTC-варіанту:
- getTime()
-
Повертає timestamp для дати – кількість мілісекунд, що пройшли з 1 січня 1970 UTC+0.
- getTimezoneOffset()
-
Повертає різницю між UTC та місцевим часовим поясом, у хвилинах:
// якщо ви знаходитесь в часовому поясі UTC-1 -- видасть 60 // якщо ви знаходитесь в часовому поясі UTC+3 -- видасть -180 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)
Кожен з них, крім 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
. Ми можемо встановити данні за межами діапазону, і вони будуть автоматично налаштувати себе.
Наприклад:
let date = new Date(2013, 0, 32); // 32 січня 2013 ?!?
alert(date); // ...це 1 лютого 2013!
Компоненти поза межами діапазону розподіляються автоматично.
Скажімо, нам потрібно збільшити дату “28 лютого 2016” на 2 дні. Це може бути “2 березня” або “1 березня” у випадку високосного року. Нам не потрібно думати про це. Просто додайте 2 дні. Об’єкт Date
зробить все інше:
let date = new Date(2016, 1, 28);
date.setDate(date.getDate() + 2);
alert( date ); // 1 березня 2016
Ця особливість часто використовується для отримання дати після певного проміжку часу. Наприклад, отримаймо дату “70 секунд після зараз”:
let date = new Date();
date.setSeconds(date.getSeconds() + 70);
alert( date ); // показує правильну дату
Ми також можемо встановити нуль або навіть негативні значення. Наприклад:
let date = new Date(2016, 0, 2); // 2 січня 2016
date.setDate(1); // встановити 1 день місяця
alert( date );
date.setDate(0); // мінімальний номер дня -- 1, тому передбачається, що це останній день попереднього місяця
alert( date ); // 31 грудня 2015
Перетворення дати до числа, різниця дат
Коли Date
об’єкт перетворюється на номер, він стає timestamp так само, як 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()
, що повертає поточний timestamp.
Це семантично еквівалентно до new Date().getTime()
, але не створює проміжного об’єкта Date
. Так що цей підхід працює швидше і не навантажує збирач сміття.
Він використовується в основному для зручності або коли важлива продуктивність, як в іграх на JavaScript або інших спеціалізованих застосунках.
Отже, це, мабуть, краще:
let start = Date.now(); // кількість мілісекунд з 1 січня 1970 року
// виконується робота
for (let i = 0; i < 100000; i++) {
let doSomething = i * i * i;
}
let end = Date.now(); // готово
alert( `Цикл зайняв ${end - start} мс` ); // віднімемо цифри, а не дати
Вимірювання швидкодії (Benchmarking)
Якщо ми хочемо достовірний аналіз швидкодії функції, що залежить від процесора, ми повинні бути обережними.
Наприклад, давайте вимірюємо дві функції, які обчислюють різницю між двома датами: яка з них швидше?
Такі вимірювання продуктивності часто називаються “бенчмарками”.
// У нас є date1 і date2. Яка функція швидше поверне їхню різницю в мс?
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( 'Час diffSubtract: ' + bench(diffSubtract) + 'мс' );
alert( 'Час diffGetTime: ' + bench(diffGetTime) + 'мс' );
Оце так! Використання getTime()
набагато швидше! Це тому, що немає перетворення типу, це набагато простіше для оптимізації.
Гаразд, у нас є щось. Але це ще не хороший бенчмарк.
Уявіть собі, що під час запуску bench(diffSubtract)
процесор робив щось паралельно, і це забирало певні ресурси. І до часу роботи bench(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;
// запустіть bench(diffSubtract) і bench(diffGetTime) кожен по 10 разів
for (let i = 0; i < 10; i++) {
time1 += bench(diffSubtract);
time2 += bench(diffGetTime);
}
alert( 'Загальний час для diffSubtract: ' + time1 );
alert( 'Загальний час для diffGetTime: ' + time2 );
Сучасні рушії JavaScript починають застосовувати передові оптимізації лише до “гарячого коду”, який виконує багато разів (не потрібно оптимізувати рідко виконані речі). Отже, у наведеному вище прикладі перші виконання не оптимізовані. Ми можемо захотіти додати “нагрівання”:
// додано для "нагрівання" до основного циклу
bench(diffSubtract);
bench(diffGetTime);
// тепер бенчмарк
for (let i = 0; i < 10; i++) {
time1 += bench(diffSubtract);
time2 += bench(diffGetTime);
}
Сучасні рушії JavaScript застосовують багато оптимізацій. Вони можуть підналаштувати результати “штучних тестів” у порівнянні з “нормальним використанням”, особливо коли ми тестуємо щось дуже мале, наприклад, як працює оператор, або вбудована функція. Отже, якщо ви серйозно хочете оцінити продуктивність, то, будь ласка, дослідіть, як працює рушій JavaScript. І тоді ви, мабуть, не потребуєте мікробенчмаркінгів взагалі.
Великий набір статей про V8 можна знайти на https://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)
аналізує рядок у заданому форматі та повертає timestamp (кількість мілісекунд з 1 січня 1970 UTC+0). Якщо формат недійсний, повертає NaN
.
Наприклад:
let ms = Date.parse('2012-01-26T13:51:50.417-07:00');
alert(ms); // 1327611110417 (timestamp)
Ми можемо миттєво створити об’єкт за допомогою new Date
з timestamp:
let date = new Date( Date.parse('2012-01-26T13:51:50.417-07:00') );
alert(date);
Підсумки
- Дата та час у JavaScript представлені об’єктом Date. Ми не можемо створити “тільки дату” або “тільки час”: об’єкти
Date
завжди несуть в собі обидва значення. - Місяці рахуються з нуля (так, січень – це нульовий місяць).
- Дні тижня в
getDay()
також рахуються з нуля (з неділі). Date
автоматично виправляє себе, коли встановлено компоненти, які виходять за межі. Це добре для додавання/віднімання днів/місяців/годин.- Дати можуть відніматися, даючи їхню різницю в мілісекундах. Це тому, що
Date
стає timestamp, коли перетворюється на число. - Використовуйте
Date.now()
, щоб швидко отримати поточний timestamp.
Зауважте, що на відміну від багатьох інших систем, значенния timestamp в JavaScript вимірюється в мілісекундах, а не в секундах.
Іноді нам потрібно більш точні вимірювання часу. Сам JavaScript не має способу вимірювання часу в мікросекундах (1 мільйон у секунду), але більшість середовищ забезпечують його. Наприклад, браузер має performance.now(), що дає кількість мілісекунд з початку завантаження сторінки з мікросекундною точністю (3 цифри після крапки):
alert(`Завантаження почалося ${performance.now()}мс тому`);
// Щось на зразок: "Завантаження почалося 34731.26000000001мс тому"
// .26 це мікросекунди (260 мікросекунд)
// коректними є тільки перші три цифри після крапки, а решта -- це помилка точності
Node.js має модуль microtime
та інші способи. Технічно, майже будь-який пристрій та середовище дозволяє отримати більшу точність, просто її немає в Date
.