Каков наилучший способ преобразовать время в объект Date из пользовательского ввода в Javascript?
-
02-07-2019 - |
Вопрос
Я работаю над виджетом формы, позволяющим пользователям вводить время суток в текстовый ввод (для приложения календаря).Используя JavaScript (мы используем jQuery FWIW), я хочу найти наилучший способ синтаксического анализа текста, который пользователь вводит в JavaScript Date()
объект, чтобы я мог легко выполнять сравнения и другие действия с ним.
Я попробовал parse()
метод, и он немного слишком требователен для моих нужд.Я бы ожидал, что он сможет успешно проанализировать следующие примеры времени ввода (в дополнение к другим логически аналогичным форматам времени) как одинаковые Date()
объект:
- 13:00 вечера
- 13:00 вечера.
- 1:00 р
- 13:00 вечера
- 1:00 пополудни.
- 1:00р.
- 1 час дня
- 13 часов дня .
- 1 р
- 1 час дня
- 1 час ночи.
- 1p
- 13:00
- 13
Я думаю, что я мог бы использовать регулярные выражения для разделения входных данных и извлечения информации, которую я хочу использовать для создания моего Date()
объект.Каков наилучший способ сделать это?
Решение
Быстрое решение, которое работает с указанными вами входными данными:
function parseTime( t ) {
var d = new Date();
var time = t.match( /(\d+)(?::(\d\d))?\s*(p?)/ );
d.setHours( parseInt( time[1]) + (time[3] ? 12 : 0) );
d.setMinutes( parseInt( time[2]) || 0 );
return d;
}
var tests = [
'1:00 pm','1:00 p.m.','1:00 p','1:00pm','1:00p.m.','1:00p','1 pm',
'1 p.m.','1 p','1pm','1p.m.', '1p', '13:00','13', '1a', '12', '12a', '12p', '12am', '12pm', '2400am', '2400pm', '2400',
'1000', '100', '123', '2459', '2359', '2359am', '1100', '123p',
'1234', '1', '9', '99', '999', '9999', '99999', '0000', '0011', '-1', 'mioaw' ];
for ( var i = 0; i < tests.length; i++ ) {
console.log( tests[i].padStart( 9, ' ' ) + " = " + parseTime(tests[i]) );
}
Это должно сработать и для нескольких других сортов (даже если утраиспользуется, он все равно будет работать - например).Очевидно, что это довольно грубо, но в то же время довольно легко (например, намного дешевле использовать это, чем полноценную библиотеку).
Предупреждение:Код не работает с 12:00 утра и т.д.
Другие советы
Все приведенные примеры не работают в период с 12:00 до 12:59.Они также выдают ошибку, если регулярное выражение не соответствует времени.Следующее обрабатывает это:
function parseTime(timeString) {
if (timeString == '') return null;
var time = timeString.match(/(\d+)(:(\d\d))?\s*(p?)/i);
if (time == null) return null;
var hours = parseInt(time[1],10);
if (hours == 12 && !time[4]) {
hours = 0;
}
else {
hours += (hours < 12 && time[4])? 12 : 0;
}
var d = new Date();
d.setHours(hours);
d.setMinutes(parseInt(time[3],10) || 0);
d.setSeconds(0, 0);
return d;
}
var tests = [
'1:00 pm','1:00 p.m.','1:00 p','1:00pm','1:00p.m.','1:00p','1 pm',
'1 p.m.','1 p','1pm','1p.m.', '1p', '13:00','13', '1a', '12', '12a', '12p', '12am', '12pm', '2400am', '2400pm', '2400',
'1000', '100', '123', '2459', '2359', '2359am', '1100', '123p',
'1234', '1', '9', '99', '999', '9999', '99999', '0000', '0011', '-1', 'mioaw' ];
for ( var i = 0; i < tests.length; i++ ) {
console.log( tests[i].padStart( 9, ' ' ) + " = " + parseTime(tests[i]) );
}
Это будет работать для строк, которые содержат время в любом месте внутри себя.Таким образом, "abcde12:00pmdef" будет проанализирован и вернет 12 часов дня.Если желаемый результат заключается в том, что он возвращает время только тогда, когда строка содержит в себе только время, можно использовать следующее регулярное выражение при условии замены "time[4]" на "time[6]".
/^(\d+)(:(\d\d))?\s*((a|(p))m?)?$/i
Не утруждайте себя тем, чтобы делать это самостоятельно, просто используйте datejs ( дата рождения ).
Большинство приведенных здесь решений с регулярными выражениями выдают ошибки, когда строка не может быть проанализирована, и не многие из них учитывают такие строки, как 1330
или 130pm
.Несмотря на то, что эти форматы не были указаны OP, я нахожу их критичными для анализа дат, введенных людьми.
Все это навело меня на мысль, что использование регулярного выражения, возможно, не самый лучший подход для этого.
Мое решение - это функция, которая не только анализирует время, но и позволяет вам указать формат вывода и шаг (интервал), до которого округлять минуты.Имея около 70 строк, он по-прежнему легкий и анализирует все вышеупомянутые форматы, а также форматы без двоеточий.
- ДЕМОНСТРАЦИЯ: http://jsfiddle.net/HwwzS/1/
- Код: https://gist.github.com/claviska/4744736
function parseTime(time, format, step) {
var hour, minute, stepMinute,
defaultFormat = 'g:ia',
pm = time.match(/p/i) !== null,
num = time.replace(/[^0-9]/g, '');
// Parse for hour and minute
switch(num.length) {
case 4:
hour = parseInt(num[0] + num[1], 10);
minute = parseInt(num[2] + num[3], 10);
break;
case 3:
hour = parseInt(num[0], 10);
minute = parseInt(num[1] + num[2], 10);
break;
case 2:
case 1:
hour = parseInt(num[0] + (num[1] || ''), 10);
minute = 0;
break;
default:
return '';
}
// Make sure hour is in 24 hour format
if( pm === true && hour > 0 && hour < 12 ) hour += 12;
// Force pm for hours between 13:00 and 23:00
if( hour >= 13 && hour <= 23 ) pm = true;
// Handle step
if( step ) {
// Step to the nearest hour requires 60, not 0
if( step === 0 ) step = 60;
// Round to nearest step
stepMinute = (Math.round(minute / step) * step) % 60;
// Do we need to round the hour up?
if( stepMinute === 0 && minute >= 30 ) {
hour++;
// Do we need to switch am/pm?
if( hour === 12 || hour === 24 ) pm = !pm;
}
minute = stepMinute;
}
// Keep within range
if( hour <= 0 || hour >= 24 ) hour = 0;
if( minute < 0 || minute > 59 ) minute = 0;
// Format output
return (format || defaultFormat)
// 12 hour without leading 0
.replace(/g/g, hour === 0 ? '12' : 'g')
.replace(/g/g, hour > 12 ? hour - 12 : hour)
// 24 hour without leading 0
.replace(/G/g, hour)
// 12 hour with leading 0
.replace(/h/g, hour.toString().length > 1 ? (hour > 12 ? hour - 12 : hour) : '0' + (hour > 12 ? hour - 12 : hour))
// 24 hour with leading 0
.replace(/H/g, hour.toString().length > 1 ? hour : '0' + hour)
// minutes with leading zero
.replace(/i/g, minute.toString().length > 1 ? minute : '0' + minute)
// simulate seconds
.replace(/s/g, '00')
// lowercase am/pm
.replace(/a/g, pm ? 'pm' : 'am')
// lowercase am/pm
.replace(/A/g, pm ? 'PM' : 'AM');
}
var tests = [
'1:00 pm','1:00 p.m.','1:00 p','1:00pm','1:00p.m.','1:00p','1 pm',
'1 p.m.','1 p','1pm','1p.m.', '1p', '13:00','13', '1a', '12', '12a', '12p', '12am', '12pm', '2400am', '2400pm', '2400',
'1000', '100', '123', '2459', '2359', '2359am', '1100', '123p',
'1234', '1', '9', '99', '999', '9999', '99999', '0000', '0011', '-1', 'mioaw' ];
for ( var i = 0; i < tests.length; i++ ) {
console.log( tests[i].padStart( 9, ' ' ) + " = " + parseTime(tests[i]) );
}
Вот улучшение по сравнению Версия Джо.Не стесняйтесь редактировать его дальше.
function parseTime(timeString)
{
if (timeString == '') return null;
var d = new Date();
var time = timeString.match(/(\d+)(:(\d\d))?\s*(p?)/i);
d.setHours( parseInt(time[1],10) + ( ( parseInt(time[1],10) < 12 && time[4] ) ? 12 : 0) );
d.setMinutes( parseInt(time[3],10) || 0 );
d.setSeconds(0, 0);
return d;
}
var tests = [
'1:00 pm','1:00 p.m.','1:00 p','1:00pm','1:00p.m.','1:00p','1 pm',
'1 p.m.','1 p','1pm','1p.m.', '1p', '13:00','13', '1a', '12', '12a', '12p', '12am', '12pm', '2400am', '2400pm', '2400',
'1000', '100', '123', '2459', '2359', '2359am', '1100', '123p',
'1234', '1', '9', '99', '999', '9999', '99999', '0000', '0011', '-1', 'mioaw' ];
for ( var i = 0; i < tests.length; i++ ) {
console.log( tests[i].padStart( 9, ' ' ) + " = " + parseTime(tests[i]) );
}
Изменения:
- Добавлен параметр radix к вызовам parseInt() (чтобы jslint не жаловался).
- Сделал регулярное выражение без учета регистра, чтобы "2: 23 PM" работало как "2: 23 pm".
Я столкнулся с парой проблем при реализации решения Джона Ресига.Вот измененная функция, которую я использовал, основываясь на его ответе:
function parseTime(timeString)
{
if (timeString == '') return null;
var d = new Date();
var time = timeString.match(/(\d+)(:(\d\d))?\s*(p?)/);
d.setHours( parseInt(time[1]) + ( ( parseInt(time[1]) < 12 && time[4] ) ? 12 : 0) );
d.setMinutes( parseInt(time[3]) || 0 );
d.setSeconds(0, 0);
return d;
} // parseTime()
var tests = [
'1:00 pm','1:00 p.m.','1:00 p','1:00pm','1:00p.m.','1:00p','1 pm',
'1 p.m.','1 p','1pm','1p.m.', '1p', '13:00','13', '1a', '12', '12a', '12p', '12am', '12pm', '2400am', '2400pm', '2400',
'1000', '100', '123', '2459', '2359', '2359am', '1100', '123p',
'1234', '1', '9', '99', '999', '9999', '99999', '0000', '0011', '-1', 'mioaw' ];
for ( var i = 0; i < tests.length; i++ ) {
console.log( tests[i].padStart( 9, ' ' ) + " = " + parseTime(tests[i]) );
}
В любое время.Конвертер может анализировать даты и время во многих различных форматах:
Тот Самый время размер упаковки составляет 0,9 килобайта.Доступно с менеджерами пакетов NPM и bower.
Вот пример прямо из README.md
:
var t = Time('2p');
t.hours(); // 2
t.minutes(); // 0
t.period(); // 'pm'
t.toString(); // '2:00 pm'
t.nextDate(); // Sep 10 2:00 (assuming it is 1 o'clock Sep 10)
t.format('hh:mm AM') // '02:00 PM'
t.isValid(); // true
Time.isValid('99:12'); // false
Это более надежный подход, который учитывает, как пользователи намерены использовать этот тип входных данных.Например, если пользователь ввел "12", он ожидал бы, что это будет 12 вечера (полдень), а не 12 утра.Приведенная ниже функция обрабатывает все это.Он также доступен здесь: http://blog.de-zwart.net/2010-02/javascript-parse-time/
/**
* Parse a string that looks like time and return a date object.
* @return Date object on success, false on error.
*/
String.prototype.parseTime = function() {
// trim it and reverse it so that the minutes will always be greedy first:
var value = this.trim().reverse();
// We need to reverse the string to match the minutes in greedy first, then hours
var timeParts = value.match(/(a|p)?\s*((\d{2})?:?)(\d{1,2})/i);
// This didnt match something we know
if (!timeParts) {
return false;
}
// reverse it:
timeParts = timeParts.reverse();
// Reverse the internal parts:
for( var i = 0; i < timeParts.length; i++ ) {
timeParts[i] = timeParts[i] === undefined ? '' : timeParts[i].reverse();
}
// Parse out the sections:
var minutes = parseInt(timeParts[1], 10) || 0;
var hours = parseInt(timeParts[0], 10);
var afternoon = timeParts[3].toLowerCase() == 'p' ? true : false;
// If meridian not set, and hours is 12, then assume afternoon.
afternoon = !timeParts[3] && hours == 12 ? true : afternoon;
// Anytime the hours are greater than 12, they mean afternoon
afternoon = hours > 12 ? true : afternoon;
// Make hours be between 0 and 12:
hours -= hours > 12 ? 12 : 0;
// Add 12 if its PM but not noon
hours += afternoon && hours != 12 ? 12 : 0;
// Remove 12 for midnight:
hours -= !afternoon && hours == 12 ? 12 : 0;
// Check number sanity:
if( minutes >= 60 || hours >= 24 ) {
return false;
}
// Return a date object with these values set.
var d = new Date();
d.setHours(hours);
d.setMinutes(minutes);
return d;
}
var tests = [
'1:00 pm','1:00 p.m.','1:00 p','1:00pm','1:00p.m.','1:00p','1 pm',
'1 p.m.','1 p','1pm','1p.m.', '1p', '13:00','13', '1a', '12', '12a', '12p', '12am', '12pm', '2400am', '2400pm', '2400',
'1000', '100', '123', '2459', '2359', '2359am', '1100', '123p',
'1234', '1', '9', '99', '999', '9999', '99999', '0000', '0011', '-1', 'mioaw' ];
for ( var i = 0; i < tests.length; i++ ) {
console.log( tests[i].padStart( 9, ' ' ) + " = " + tests[i].parseTime() );
}
Это строковый прототип, поэтому вы можете использовать его следующим образом:
var str = '12am';
var date = str.parseTime();
Вот решение больше для всех тех, кто использует 24-часовые часы, поддерживающие:
- 0820 -> 08:20
- 32 -> 03:02
- 124 -> 12:04
function parseTime(text) {
var time = text.match(/(\d?\d):?(\d?\d?)/);
var h = parseInt(time[1], 10);
var m = parseInt(time[2], 10) || 0;
if (h > 24) {
// try a different format
time = text.match(/(\d)(\d?\d?)/);
h = parseInt(time[1], 10);
m = parseInt(time[2], 10) || 0;
}
var d = new Date();
d.setHours(h);
d.setMinutes(m);
return d;
}
var tests = [
'1:00 pm','1:00 p.m.','1:00 p','1:00pm','1:00p.m.','1:00p','1 pm',
'1 p.m.','1 p','1pm','1p.m.', '1p', '13:00','13', '1a', '12', '12a', '12p', '12am', '12pm', '2400am', '2400pm', '2400',
'1000', '100', '123', '2459', '2359', '2359am', '1100', '123p',
'1234', '1', '9', '99', '999', '9999', '99999', '0000', '0011', '-1', 'mioaw' ];
for ( var i = 0; i < tests.length; i++ ) {
console.log( tests[i].padStart( 9, ' ' ) + " = " + parseTime(tests[i]) );
}
Я внес некоторые изменения в описанную выше функцию, чтобы поддерживать еще несколько форматов.
- 14.00 -> 14:00
- 1.30 -> 13:30 вечера
- 1:30 утра -> 1:30 ночи
- 100 -> 1:00 утра
Я еще не почистил его, но работает со всем, что приходит мне в голову.
function parseTime(timeString) {
if (timeString == '') return null;
var time = timeString.match(/^(\d+)([:\.](\d\d))?\s*((a|(p))m?)?$/i);
if (time == null) return null;
var m = parseInt(time[3], 10) || 0;
var hours = parseInt(time[1], 10);
if (time[4]) time[4] = time[4].toLowerCase();
// 12 hour time
if (hours == 12 && !time[4]) {
hours = 12;
}
else if (hours == 12 && (time[4] == "am" || time[4] == "a")) {
hours += 12;
}
else if (hours < 12 && (time[4] != "am" && time[4] != "a")) {
hours += 12;
}
// 24 hour time
else if(hours > 24 && hours.toString().length >= 3) {
if(hours.toString().length == 3) {
m = parseInt(hours.toString().substring(1,3), 10);
hours = parseInt(hours.toString().charAt(0), 10);
}
else if(hours.toString().length == 4) {
m = parseInt(hours.toString().substring(2,4), 10);
hours = parseInt(hours.toString().substring(0,2), 10);
}
}
var d = new Date();
d.setHours(hours);
d.setMinutes(m);
d.setSeconds(0, 0);
return d;
}
var tests = [
'1:00 pm','1:00 p.m.','1:00 p','1:00pm','1:00p.m.','1:00p','1 pm',
'1 p.m.','1 p','1pm','1p.m.', '1p', '13:00','13', '1a', '12', '12a', '12p', '12am', '12pm', '2400am', '2400pm', '2400',
'1000', '100', '123', '2459', '2359', '2359am', '1100', '123p',
'1234', '1', '9', '99', '999', '9999', '99999', '0000', '0011', '-1', 'mioaw' ];
for ( var i = 0; i < tests.length; i++ ) {
console.log( tests[i].padStart( 9, ' ' ) + " = " + parseTime(tests[i]) );
}
Вот еще один подход, который охватывает исходный ответ, любое разумное количество цифр, ввод данных кошками и логические ошибки.Алгоритм следующий:
- Определите, является ли меридиан post meridiem.
- Преобразуйте входные цифры в целое значение.
- Время между 0 и 24:час - это часы, без минут (12 часов - это пополудни).
- Время между 100 и 2359:часы div 100 - это остаток от часов, минут mod 100.
- Время с 24.00 и далее:время - полночь, осталось несколько минут.
- Когда количество часов превысит 12, вычтите 12 и установите значение post meridiem true.
- Когда количество минут превысит 59, увеличьте усилие до 59.
Преобразование часов, минут и post meridiem в объект даты - это упражнение для читателя (многочисленные другие ответы показывают, как это сделать).
"use strict";
String.prototype.toTime = function () {
var time = this;
var post_meridiem = false;
var ante_meridiem = false;
var hours = 0;
var minutes = 0;
if( time != null ) {
post_meridiem = time.match( /p/i ) !== null;
ante_meridiem = time.match( /a/i ) !== null;
// Preserve 2400h time by changing leading zeros to 24.
time = time.replace( /^00/, '24' );
// Strip the string down to digits and convert to a number.
time = parseInt( time.replace( /\D/g, '' ) );
}
else {
time = 0;
}
if( time > 0 && time < 24 ) {
// 1 through 23 become hours, no minutes.
hours = time;
}
else if( time >= 100 && time <= 2359 ) {
// 100 through 2359 become hours and two-digit minutes.
hours = ~~(time / 100);
minutes = time % 100;
}
else if( time >= 2400 ) {
// After 2400, it's midnight again.
minutes = (time % 100);
post_meridiem = false;
}
if( hours == 12 && ante_meridiem === false ) {
post_meridiem = true;
}
if( hours > 12 ) {
post_meridiem = true;
hours -= 12;
}
if( minutes > 59 ) {
minutes = 59;
}
var result =
(""+hours).padStart( 2, "0" ) + ":" + (""+minutes).padStart( 2, "0" ) +
(post_meridiem ? "PM" : "AM");
return result;
};
var tests = [
'1:00 pm','1:00 p.m.','1:00 p','1:00pm','1:00p.m.','1:00p','1 pm',
'1 p.m.','1 p','1pm','1p.m.', '1p', '13:00','13', '1a', '12', '12a', '12p', '12am', '12pm', '2400am', '2400pm', '2400',
'1000', '100', '123', '2459', '2359', '2359am', '1100', '123p',
'1234', '1', '9', '99', '999', '9999', '99999', '0000', '0011', '-1', 'mioaw' ];
for ( var i = 0; i < tests.length; i++ ) {
console.log( tests[i].padStart( 9, ' ' ) + " = " + tests[i].toTime() );
}
В jQuery вновь определенный прототип строки используется следующим образом:
<input type="text" class="time" />
$(".time").change( function() {
var $this = $(this);
$(this).val( time.toTime() );
});
Почему бы не использовать проверку, чтобы сузить то, что пользователь может ввести, и упростить список, включив в него только форматы, которые могут быть проанализированы (или проанализированы после некоторой настройки).
Я не думаю, что требовать от пользователя указывать время в поддерживаемом формате - это слишком большая просьба.
dd: dd A (m)/P(m)
dd A (m)/P(m)
дд
/(\d+)(?::(\d\d))(?::(\d\d))?\s*([pP]?)/
// added test for p or P
// added seconds
d.setHours( parseInt(time[1]) + (time[4] ? 12 : 0) ); // care with new indexes
d.setMinutes( parseInt(time[2]) || 0 );
d.setSeconds( parseInt(time[3]) || 0 );
Спасибо
Ответов много, так что еще один не повредит.
/**
* Parse a time in nearly any format
* @param {string} time - Anything like 1 p, 13, 1:05 p.m., etc.
* @returns {Date} - Date object for the current date and time set to parsed time
*/
function parseTime(time) {
var b = time.match(/\d+/g);
// return undefined if no matches
if (!b) return;
var d = new Date();
d.setHours(b[0]>12? b[0] : b[0]%12 + (/p/i.test(time)? 12 : 0), // hours
/\d/.test(b[1])? b[1] : 0, // minutes
/\d/.test(b[2])? b[2] : 0); // seconds
return d;
}
var tests = [
'1:00 pm','1:00 p.m.','1:00 p','1:00pm','1:00p.m.','1:00p','1 pm',
'1 p.m.','1 p','1pm','1p.m.', '1p', '13:00','13', '1a', '12', '2400',
'1000', '100', '123', '2459', '2359', '2359am', '1100', '123p',
'1234', '1', '9', '99', '999', '9999', '99999', '0000', '0011', '-1', 'mioaw' ];
for ( var i = 0; i < tests.length; i++ ) {
console.log( tests[i].padStart( 9, ' ' ) + " = " + parseTime(tests[i]) );
}
Чтобы быть должным образом надежным, следует проверить, что каждое значение находится в пределах допустимых значений, например, если часы am / pm должны быть от 1 до 12 включительно, в противном случае от 0 до 24 включительно и т.д.
Улучшение решения Патрика МаКэлхэни (его решение неправильно обрабатывает 12 часов ночи)
function parseTime( timeString ) {
var d = new Date();
var time = timeString.match(/(\d+)(:(\d\d))?\s*([pP]?)/i);
var h = parseInt(time[1], 10);
if (time[4])
{
if (h < 12)
h += 12;
}
else if (h == 12)
h = 0;
d.setHours(h);
d.setMinutes(parseInt(time[3], 10) || 0);
d.setSeconds(0, 0);
return d;
}
var tests = [
'1:00 pm','1:00 p.m.','1:00 p','1:00pm','1:00p.m.','1:00p','1 pm',
'1 p.m.','1 p','1pm','1p.m.', '1p', '13:00','13', '1a', '12', '2400',
'1000', '100', '123', '2459', '2359', '2359am', '1100', '123p',
'1234', '1', '9', '99', '999', '9999', '99999', '0000', '0011', '-1', 'mioaw' ];
for ( var i = 0; i < tests.length; i++ ) {
console.log( tests[i].padStart( 9, ' ' ) + " = " + parseTime(tests[i]) );
}
Другие ответы меня не устроили, поэтому я придумал еще один.Эта версия:
- Распознает секунды и миллисекунды
- ВОЗВРАТ
undefined
при неверном вводе, таком как "13:00 вечера" или "11:65" - Возвращает местное время, если вы предоставляете
localDate
параметр, в противном случае возвращает время UTC эпохи Unix (1 января 1970). - Поддерживает военное время, например
1330
(чтобы отключить, сделайте первое ':' обязательным в регулярном выражении) - Позволяет использовать один час самостоятельно, с 24-часовым временем (т.е."7" означает 7 утра).
- Разрешает использовать hour 24 в качестве синонима hour 0, но hour 25 не допускается.
- Требуется, чтобы время было в начале строки (чтобы отключить, удалите
^\s*
в регулярном выражении) - Имеет тестовый код, который фактически определяет, когда выходные данные неверны.
Редактировать:теперь это упаковка включая timeToString
форматирующее устройство: npm i simplertime
/**
* Parses a string into a Date. Supports several formats: "12", "1234",
* "12:34", "12:34pm", "12:34 PM", "12:34:56 pm", and "12:34:56.789".
* The time must be at the beginning of the string but can have leading spaces.
* Anything is allowed after the time as long as the time itself appears to
* be valid, e.g. "12:34*Z" is OK but "12345" is not.
* @param {string} t Time string, e.g. "1435" or "2:35 PM" or "14:35:00.0"
* @param {Date|undefined} localDate If this parameter is provided, setHours
* is called on it. Otherwise, setUTCHours is called on 1970/1/1.
* @returns {Date|undefined} The parsed date, if parsing succeeded.
*/
function parseTime(t, localDate) {
// ?: means non-capturing group and ?! is zero-width negative lookahead
var time = t.match(/^\s*(\d\d?)(?::?(\d\d))?(?::(\d\d))?(?!\d)(\.\d+)?\s*(pm?|am?)?/i);
if (time) {
var hour = parseInt(time[1]), pm = (time[5] || ' ')[0].toUpperCase();
var min = time[2] ? parseInt(time[2]) : 0;
var sec = time[3] ? parseInt(time[3]) : 0;
var ms = (time[4] ? parseFloat(time[4]) * 1000 : 0);
if (pm !== ' ' && (hour == 0 || hour > 12) || hour > 24 || min >= 60 || sec >= 60)
return undefined;
if (pm === 'A' && hour === 12) hour = 0;
if (pm === 'P' && hour !== 12) hour += 12;
if (hour === 24) hour = 0;
var date = new Date(localDate!==undefined ? localDate.valueOf() : 0);
var set = (localDate!==undefined ? date.setHours : date.setUTCHours);
set.call(date, hour, min, sec, ms);
return date;
}
return undefined;
}
var testSuite = {
'1300': ['1:00 pm','1:00 P.M.','1:00 p','1:00pm','1:00p.m.','1:00p','1 pm',
'1 p.m.','1 p','1pm','1p.m.', '1p', '13:00','13', '1:00:00PM', '1300', '13'],
'1100': ['11:00am', '11:00 AM', '11:00', '11:00:00', '1100'],
'1359': ['1:59 PM', '13:59', '13:59:00', '1359', '1359:00', '0159pm'],
'100': ['1:00am', '1:00 am', '0100', '1', '1a', '1 am'],
'0': ['00:00', '24:00', '12:00am', '12am', '12:00:00 AM', '0000', '1200 AM'],
'30': ['0:30', '00:30', '24:30', '00:30:00', '12:30:00 am', '0030', '1230am'],
'1435': ["2:35 PM", "14:35:00.0", "1435"],
'715.5': ["7:15:30", "7:15:30am"],
'109': ['109'], // Three-digit numbers work (I wasn't sure if they would)
'': ['12:60', '11:59:99', '-12:00', 'foo', '0660', '12345', '25:00'],
};
var passed = 0;
for (var key in testSuite) {
let num = parseFloat(key), h = num / 100 | 0;
let m = num % 100 | 0, s = (num % 1) * 60;
let expected = Date.UTC(1970, 0, 1, h, m, s); // Month is zero-based
let strings = testSuite[key];
for (let i = 0; i < strings.length; i++) {
var result = parseTime(strings[i]);
if (result === undefined ? key !== '' : key === '' || expected !== result.valueOf()) {
console.log(`Test failed at ${key}:"${strings[i]}" with result ${result ? result.toUTCString() : 'undefined'}`);
} else {
passed++;
}
}
}
console.log(passed + ' tests passed.');
Если вам нужны только секунды, вот однострочник
const toSeconds = s => s.split(':').map(v => parseInt(v)).reverse().reduce((acc,e,i) => acc + e * Math.pow(60,i))
Сводная таблица других ответов
Прежде всего, я не могу поверить что нет встроенной функциональности или даже надежной сторонней библиотеки, которая могла бы справиться с этим.На самом деле, это веб-разработка, так что я могу в это поверить.
От попыток протестировать все крайние случаи с помощью всех этих различных алгоритмов у меня закружилась голова, поэтому я взял на себя смелость собрать все ответы и тесты в этой теме в удобную таблицу.
Код (и результирующая таблица) бессмысленно велики для включения inline, поэтому я создал JSFiddle:
http://jsfiddle.net/jLv16ydb/4/show
// heres some filler code of the functions I included in the test,
// because StackOverfleaux wont let me have a jsfiddle link without code
Functions = [
JohnResig,
Qwertie,
PatrickMcElhaney,
Brad,
NathanVillaescusa,
DaveJarvis,
AndrewCetinic,
StefanHaberl,
PieterDeZwart,
JoeLencioni,
Claviska,
RobG,
DateJS,
MomentJS
];
// I didn't include `date-fns`, because it seems to have even more
// limited parsing than MomentJS or DateJS
Пожалуйста, не стесняйтесь разветвлять мою скрипку и добавлять больше алгоритмов и тестовых примеров
Я не добавлял никаких сравнений между результатом и "ожидаемым" результатом, потому что есть случаи, когда "ожидаемый" результат может быть обсужден (например, следует 12
быть истолкованным как 12:00am
или 12:00pm
?).Вам нужно будет просмотреть таблицу и посмотреть, какой алгоритм имеет наибольший смысл для вас.
Примечание: Цвета не обязательно указывают на качество или "ожидаемость" результата, они только указывают на тип результата:
red
= выдана ошибка jsyellow
= "ложное" значение (undefined
,null
,NaN
,""
,"invalid date"
)green
= jsDate()
объектlight green
= все остальное
Где а Date()
объект - это результат, я преобразую его в 24 часа HH:mm
формат для удобства сравнения.
После тщательного тестирования и расследования с помощью мой другой ответ на компиляцию, Я пришел к выводу, что решение @Dave Jarvis было наиболее близким к тому, что, по моему мнению, было разумными результатами и обработкой крайних случаев.Для справки, я посмотрел, на какое значение времени при вводе данных Google Calendar было переформатировано время после выхода из текстового поля.
Тем не менее, я видел, что он не обрабатывал некоторые (хотя и странные) крайние случаи, которые выполнял Google Calendar.Поэтому я переработал его с нуля, и вот что у меня получилось.Я также добавил его в мой ответ на компиляцию.
// attempt to parse string as time. return js date object
static parseTime(string) {
string = String(string);
var am = null;
// check if "apm" or "pm" explicitly specified, otherwise null
if (string.toLowerCase().includes("p")) am = false;
else if (string.toLowerCase().includes("a")) am = true;
string = string.replace(/\D/g, ""); // remove non-digit characters
string = string.substring(0, 4); // take only first 4 digits
if (string.length === 3) string = "0" + string; // consider eg "030" as "0030"
string = string.replace(/^00/, "24"); // add 24 hours to preserve eg "0012" as "00:12" instead of "12:00", since will be converted to integer
var time = parseInt(string); // convert to integer
// default time if all else fails
var hours = 12,
minutes = 0;
// if able to parse as int
if (Number.isInteger(time)) {
// treat eg "4" as "4:00pm" (or "4:00am" if "am" explicitly specified)
if (time >= 0 && time <= 12) {
hours = time;
minutes = 0;
// if "am" or "pm" not specified, establish from number
if (am === null) {
if (hours >= 1 && hours <= 12) am = false;
else am = true;
}
}
// treat eg "20" as "8:00pm"
else if (time >= 13 && time <= 99) {
hours = time % 24;
minutes = 0;
// if "am" or "pm" not specified, force "am"
if (am === null) am = true;
}
// treat eg "52:95" as 52 hours 95 minutes
else if (time >= 100) {
hours = Math.floor(time / 100); // take first two digits as hour
minutes = time % 100; // take last two digits as minute
// if "am" or "pm" not specified, establish from number
if (am === null) {
if (hours >= 1 && hours <= 12) am = false;
else am = true;
}
}
// add 12 hours if "pm"
if (am === false && hours !== 12) hours += 12;
// sub 12 hours if "12:00am" (midnight), making "00:00"
if (am === true && hours === 12) hours = 0;
// keep hours within 24 and minutes within 60
// eg 52 hours 95 minutes becomes 4 hours 35 minutes
hours = hours % 24;
minutes = minutes % 60;
}
// convert to js date object
var date = new Date();
date.setHours(hours);
date.setMinutes(minutes);
date.setSeconds(0);
return date;
}
Я чувствую, что это самое близкое, что я могу найти для своих нужд, но предложения приветствуются. Примечание: Это ориентировано на Америку в том смысле, что для определенных шаблонов по умолчанию используется значение am / pm:
1
=>13:00
(1:00pm
)1100
=>23:00
(11:00pm
)456
=>16:56
(4:56pm
)