Как преобразовать время в объект Date из пользовательского ввода в JavaScript?

Я работаю над виджетом формы, чтобы пользователи могли вводить время дня в текстовый ввод (для календарного приложения). Используя JavaScript (мы используем jQuery FWIW), я хочу найти лучший способ проанализировать текст, который пользователь вводит в объект JavaScript Date(), чтобы я мог легко выполнять с ним сравнения и другие вещи.

Я попробовал метод parse(), и он слишком разборчив для моих нужд. Я ожидал, что он сможет успешно проанализировать следующие примеры времени ввода (в дополнение к другим логически схожим форматам времени) как тот же объект Date():

  • 1:00 вечера
  • 1:00 вечера.
  • 1:00 п
  • 1:00 вечера
  • 1:00 вечера.
  • 1:00
  • 13:00
  • 13:00
  • 1 шт.
  • 13:00
  • 13:00
  • 1p
  • 13:00
  • 13

Я думаю, что могу использовать регулярные выражения для разделения ввода и извлечения информации, которую я хочу использовать для создания моего объекта Date(). Как лучше всего это сделать?

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
70
0
63 329
21
Перейти к ответу Данный вопрос помечен как решенный

Ответы 21

Не трудитесь делать это самостоятельно, просто используйте datejs.

25КБ только на свидания?!?! Я имею в виду, без сомнения, хорошая библиотека, и если бы мне понадобилась функция обработки психологических дат, она была бы единственной. Но 25 КБ больше, чем все ядро ​​jQuery !!!

Jason Bunting 26.09.2008 23:30

Учитывая диапазон ввода, который вы хотите принять, я бы тоже выбрал datejs. Кажется, он обрабатывает большинство из них, за исключением того, которое представляет собой просто число, которое он принимает как день месяца.

Jonny Buchanan 26.09.2008 23:39

Да, я мог бы просто использовать datejs. Я могу обойти ввод одного числа, который рассматривается как месяц, добавив к строке «1/1/2000», когда я анализирую время.

Joe Lencioni 26.09.2008 23:43

Огромный +1! Что касается размера - на самом деле это 25 КБ на локаль. Но по крайней мере он поддерживает локали! Вместо того, чтобы писать собственные процедуры, используйте то, что доступно (или напишите лучшую библиотеку и поделитесь ею). Кроме того, хотя пробелы удаляются из JS, они не кажутся мне минимизированными, поэтому вы можете сохранить там несколько байтов. Если это так важно для тебя.

johndodo 20.04.2012 11:59

Пробовал на datejs.com (в разделе «Mad Skillz…») «12 ср 2020» ==> «Понедельник, 3 декабря 2012 г., 12:00:00» - ват? 2012 ??

Jonas N 06.10.2012 02:48

Я человек и не знаю, что означает «12 ср 2020». Есть несколько способов интерпретировать это. Если бы я угадал, что делает DateJS, я бы сказал, что он интерпретировал «12» как день месяца и искал следующую среду, выпавшую на 12-е число, то есть в декабре. Единственное, что меня удивило, это то, что он выбросил «2020» вместо того, чтобы интерпретировать его как 20:20.

Jim 07.10.2012 20:22

@SebastianMach Даже на скорости Венесуэлы 25 КБ занимает всего 1/8 секунды.

jwg 27.03.2017 10:30

@ Джим: Я просто говорю о потенциальных возможностях. Никогда не говори никогда, особенно с амбициозными владельцами продукта.

Sebastian Mach 08.04.2017 08:34

Почему бы не использовать проверку, чтобы сузить то, что пользователь может ввести, и упростить список, включив только форматы, которые можно анализировать (или анализировать после некоторой настройки).

Я не думаю, что требуется слишком много, чтобы пользователь указывал время в поддерживаемом формате.

dd: dd A (м) / P (м)

dd A (м) / P (м)

дд

Вы правы, это действительно не так уж много. Однако для пользователя это немного затруднительно, и я хочу сделать эту конкретную форму настолько простой в использовании, насколько это разумно. В идеале ввод должен быть достаточно гибким, чтобы интерпретировать то, что они вводят, и переформатировать его в стандартный формат.

Joe Lencioni 26.09.2008 23:46
Ответ принят как подходящий

Быстрое решение, которое работает с указанным вами вводом:

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.info( tests[i].padStart( 9, ' ' ) + " = " + parseTime(tests[i]) );
}

Он должен работать и для некоторых других разновидностей (например, даже если используется утренний час, он все равно будет работать). Очевидно, это довольно грубо, но также довольно легковесно (намного дешевле в использовании, чем, например, полная библиотека).

Warning: The code doe not work with 12:00 AM, etc.

Поработав с этим, я заметил, что он неправильно анализирует варианты времени «12 часов дня», потому что он добавляет 12 к числу часов. Чтобы исправить это, я изменил строку d.setHours следующим образом: d.setHours (parseInt (time [1]) + ((parseInt (time [1]) <12 && time [3])? 12: 0));

Joe Lencioni 30.10.2008 00:38

Я также заметил, что parseInt задыхается от строк типа ': 30' или ': 00', поэтому я изменил регулярное выражение, чтобы фиксировать минуты без двоеточия.

Joe Lencioni 31.10.2008 17:11

Лучше надеяться, что d не выпадет на день, когда вступает в силу переход на летнее время. Это также предполагает английские соглашения.

peller 10.12.2009 20:03

Для вызовов ParseInt требуется основание системы счисления 10, потому что JS предполагает основание системы счисления 8, когда есть начальный 0, в результате чего час интерпретируется как 0, если он больше 8 и имеет начальный 0 (поскольку 08 не является допустимым число по основанию 8). Кроме того, изменение "p?" на "[pP]?" заставит его работать, когда AM / PM в верхнем регистре. В общем, если вы В самом деле не уверены, что этот подход сработает для вас, вам следует использовать библиотеку. Помните, время нас всех ненавидит.

Benji York 07.08.2010 19:05

Альтернативой использованию «[pP]» было бы добавление «i» в конец литерала. Это сделало бы совпадение нечувствительным к регистру.

Chris Miller 19.05.2011 19:16

И теперь также доступен в форме функции для ленивых (вроде меня): var timeParser = функция (stringTime) {var d = new Date (); var time = stringTime.match (/ (\ d +) (? :: (\ d \ d))? \ s * (p?) /); d.setHours (parseInt (время [1]) + (время [3]? 12: 0)); d.setMinutes (parseInt (время [2]) || 0); //console.info (d); return d; }

Michael Trouw 25.03.2013 00:24

Небольшой совет всем, кто нашел это через Google, как и я. Не используйте это. Кажется, это работает, но это неправильно для времени около 12 часов утра. Комментарии / правки этого не решают. Решение Натана более полное.

braks 30.09.2015 03:01

При реализации решения Джона Ресига я столкнулся с парой изломов. Вот модифицированная функция, которую я использовал на основе его ответа:

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.info( tests[i].padStart( 9, ' ' ) + " = " + parseTime(tests[i]) );
}

Это те же ошибки, которые я отметил в ответе с наибольшим количеством голосов выше.

Benji York 07.08.2010 19:07

Вот улучшение Версия Джо. Не стесняйтесь редактировать его дальше.

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.info( tests[i].padStart( 9, ' ' ) + " = " + parseTime(tests[i]) );
}

Изменения:

  • Добавлен параметр radix в вызовы parseInt () (чтобы jslint не жаловался).
  • Регулярное выражение сделано без учета регистра, поэтому «14:23» работает как «14:23».

/(\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 );

Благодарность

Все приведенные примеры не работают с 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.info( tests[i].padStart( 9, ' ' ) + " = " + parseTime(tests[i]) );
}

Это будет работать для строк, которые содержат время где угодно внутри себя. Таким образом, "abcde12: 00pmdef" будет проанализирован и вернет 12 часов вечера. Если желаемый результат состоит в том, что он возвращает только время, когда строка содержит только время, можно использовать следующее регулярное выражение при условии, что вы замените «time [4]» на «time [6]».

/^(\d+)(:(\d\d))?\s*((a|(p))m?)?$/i

Это более надежный подход, который учитывает то, как пользователи намереваются использовать этот тип ввода. Например, если пользователь ввел «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.info( tests[i].padStart( 9, ' ' ) + " = " + tests[i].parseTime() );
}

Это строковый прототип, поэтому вы можете использовать его так:

var str = '12am';
var date = str.parseTime();

AnyTime.Converter может анализировать дату / время во многих различных форматах:

http://www.ama3.com/anytime/

Я должен добавить: пропустите средство выбора в верхней 3/4 этой страницы и посмотрите раздел о преобразовании: ama3.com/anytime/#converting

Andrew M. Andrews III 04.05.2010 19:11

Улучшение решения Патрика МакЭлхейни (он неправильно обрабатывает 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.info( tests[i].padStart( 9, ' ' ) + " = " + parseTime(tests[i]) );
}

Вот еще одно решение для всех тех, кто использует 24-часовой формат, который поддерживает:

  • 08:20 -> 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.info( tests[i].padStart( 9, ' ' ) + " = " + parseTime(tests[i]) );
}

Я внес некоторые изменения в указанную выше функцию для поддержки еще нескольких форматов.

  • 1400 -> 14:00
  • 13.30 -> 13:30
  • 1:30 -> 1:30
  • 100 -> 01: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.info( tests[i].padStart( 9, ' ' ) + " = " + parseTime(tests[i]) );
}

Большинство решений с регулярными выражениями здесь выдают ошибки, когда строка не может быть проанализирована, и не многие из них учитывают такие строки, как 1330 или 130pm. Несмотря на то, что эти форматы не были указаны OP, я считаю их критически важными для анализа дат, вводимых людьми.

Все это заставило меня подумать, что использование регулярного выражения может быть не лучшим подходом для этого.

Мое решение - это функция, которая не только анализирует время, но также позволяет указать формат вывода и шаг (интервал), до которого нужно округлять минуты. Примерно 70 строк, он по-прежнему легкий и анализирует все вышеупомянутые форматы, а также форматы без двоеточий.

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.info( tests[i].padStart( 9, ' ' ) + " = " + parseTime(tests[i]) );
}

Я должен был это отметить. Это единственный действительно ответ полезный, когда размер имеет значение. В большинстве случаев я использую momentjs, но это огромно по сравнению с этим решением.

Peter Wone 09.05.2014 13:26

Мне это было нужно, поэтому я пошел дальше и исправил это с помощью грязного хака: codepen.io/anon/pen/EjrVqq должно быть лучшее решение, но я пока не мог понять его.

Nomenator 04.08.2015 22:46

Ответов много, так что еще один не повредит.

/**
 * 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.info( tests[i].padStart( 9, ' ' ) + " = " + parseTime(tests[i]) );
}

Чтобы быть должным образом устойчивым, он должен проверять, что каждое значение находится в пределах допустимого диапазона, например, если часы am / pm должны быть от 1 до 12 включительно, в противном случае от 0 до 24 включительно и т. д.

Размер пакета время составляет 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

Этот пакет не поддерживает 24-часовое время, что может быть, а может и не быть значительным ограничением.

gruppler 14.11.2017 20:16

@gruppler спасибо за это очень важное замечание. FWIW, есть Pull Request с проделанной работой для поддержки 24-часового формата. PR был представлен в 2014 году. В любом случае, вот ссылка на этот запрос на слияние: github.com/zackdever/time/pull/7

Sgnl 15.11.2017 23:32

Этот пакет не поддерживает секунды и миллисекунды. (В большинстве ответов здесь тоже нет, но я склонен ожидать большего от «пакета», чем от фрагмента кода в ответе SO.)

Qwertie 08.06.2018 22:43

Вот еще один подход, который охватывает исходный ответ, любое разумное количество цифр, ввод данных кошками и логические ошибки. Алгоритм следующий:

  1. Определите, соответствует ли меридиан после полудня.
  2. Преобразуйте входные цифры в целое число.
  3. Время от 0 до 24: час - это часы, без минут (12 часов - это PM).
  4. Время между 100 и 2359: часы div 100 - это часы, минуты mod 100 - остаток.
  5. Время с 24:00: часы - полночь, с остатком минут.
  6. Когда количество часов превышает 12, вычтите 12 и установите истинный меридием.
  7. Когда количество минут превышает 59, установите значение 59.

Преобразование часов, минут и меридием сообщений в объект Date - это упражнение для читателя (как это сделать).

"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.info( tests[i].padStart( 9, ' ' ) + " = " + tests[i].toTime() );
}

В jQuery вновь определенный прототип String используется следующим образом:

  <input type = "text" class = "time" />
  $(".time").change( function() {
    var $this = $(this);
    $(this).val( time.toTime() );
  });

Мне не понравились другие ответы, поэтому я сделал еще один. Эта версия:

  • Распознает секунды и миллисекунды
  • Возвращает undefined при недопустимом вводе, таком как «13:00 pm» или «11:65».
  • Возвращает местное время, если вы указали параметр localDate, в противном случае возвращает время в формате UTC для эпохи Unix (1 января 1970 г.).
  • Поддерживает военное время, например 1330 (чтобы отключить, сделайте первый ":" обязательным в регулярном выражении)
  • Допускается один час с 24-часовым форматом времени (т. Е. «7» означает 7 утра).
  • Позволяет час 24 как синоним часа 0, но час 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.info(`Test failed at ${key}:"${strings[i]}" with result ${result ? result.toUTCString() : 'undefined'}`);
    } else {
      passed++;
    }
  }
}
console.info(passed + ' tests passed.');

Если вам нужны только секунды, вот один лайнер

const toSeconds = s => s.split(':').map(v => parseInt(v)).reverse().reduce((acc,e,i) => acc + e * Math.pow(60,i))

Составительная таблица других ответов

Прежде всего, я не могу поверить, что нет встроенной функциональности или даже надежной сторонней библиотеки, которая могла бы справиться с этим. На самом деле, это веб-разработка, так что я могу в это поверить.

Попытка протестировать все крайние случаи с помощью всех этих различных алгоритмов кружила мне голову, поэтому я взял на себя смелость скомпилировать все ответы и тесты в этом потоке в удобную таблицу.

Код (и результирующая таблица) бессмысленно велики для включения в строку, поэтому я сделал 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 = ошибка js

  • yellow = "ложное" значение (undefined, null, NaN, "", "invalid date")

  • green = объект js Date()

  • light green = все остальное

Если на выходе объект Date(), я конвертирую его в 24-часовой формат HH:mm для простоты сравнения.

После тщательного тестирования и исследования с помощью мой другой ответ компиляции я пришел к выводу, что решение @Dave Jarvis было наиболее близким к тому, что я считал разумными выходами и обработкой крайних случаев. Для справки, я посмотрел, какие временные поля Google Calendar переформатировали время после выхода из текстового поля.

Тем не менее, я заметил, что он не справлялся с некоторыми (хотя и странными) крайними случаями, как Google Calendar. Так что я переработал его с нуля, и это то, что я придумал. Тоже добавил в мой ответ компиляции.

// attempt to parse string as time. return js date object
function 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;
}

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.info( tests[i].padStart( 9, ' ' ) + " = " + parseTime(tests[i]) );
}

Я чувствую, что это лучшее, что я могу найти для своих нужд, но предложения приветствуются. Примечание: Это ориентировано на Америку в том смысле, что для определенных шаблонов по умолчанию используется am / pm:

  • 1 => 13:00 (1:00pm)
  • 1100 => 23:00 (11:00pm)
  • 456 => 16:56 (4:56pm)

Обратите внимание, что для приложений для отслеживания времени 1100 будет разрешаться как 11:00am. (Я готов поспорить, что большая часть человеческой деятельности, которую мы вводим как данные, происходит в течение дня.) Если вы можете вспомнить крайние случаи, когда мое решение отсутствовало, комментарий к моему ответу был бы признателен. Хорошее резюме!

Dave Jarvis 07.04.2020 00:37

В качестве второстепенного нюанса string = String(string); может быть string = String(string.toLowerCase()), чтобы избежать избыточного вызова. Есть и другие упрощения логической логики, которые можно сделать.

Dave Jarvis 07.04.2020 00:40

Еще несколько моментов: тесты 99* дают противоречивые результаты, 1000 может означать 10:00am, а -1 - как 1:00pm. Однако возвращение объекта Date, похоже, выходит за рамки варианта использования. (Предполагается, что также желательна текущая дата, что не обязательно верно для любого конкретного вводимого времени.)

Dave Jarvis 07.04.2020 00:54

Я написал этот ответ очень давно, но обратите внимание, что я написал этот код для расширения Google Calendar, поэтому я сделал его имитирующим это поведение, даже если поведение было сомнительным. Например, 1100 соответствует 23:00 в GCal. Re: 99*, не знаю. Можно спорить, как следует интерпретировать 999, 9999 и т. д., Но мои комментарии говорят о моих предположениях. GCal в этом случае даже не принимает, например, 52:95. Наконец, re: Date. Он предоставляет некоторые другие приятные функции get и format. Также некоторым библиотекам нужны вещи в формате Date, в моем случае это был iirc.

V. Rubinetti 07.04.2020 03:12

Мне нужна была функция синтаксического анализа времени, и на основе некоторых ответов я получил эту функцию

 function parse(time){
  let post_meridiem = time.match(/p/i) !== null;
  let result;
  time = time.replace(/[^\d:-]/g, '');
  let hours = 0;
  let minutes = 0;
  if (!time) return;
  let parts = time.split(':');
  if (parts.length > 2) time = parts[0] + ':' + parts[1];
  if (parts[0] > 59 && parts.length === 2) time = parts[0];
  if (!parts[0] && parts[1] < 60) minutes = parts[1];
  else if (!parts[0] && parts[1] >= 60) return;
  time = time.replace(/^00/, '24');
  time = parseInt(time.replace(/\D/g, ''));
  if (time >= 2500) return;
  if (time > 0 && time < 24 && parts.length === 1) hours = time;
  else if (time < 59) minutes = time;
  else if (time >= 60 && time <= 99 && parts[0]) {
    hours = ('' + time)[0];
    minutes = ('' + time)[1];
  } else if (time >= 100 && time <= 2359) {
    hours = ~~(time / 100);
    minutes = time % 100;
  } else if (time >= 2400) {
    hours = ~~(time / 100) - 24;
    minutes = time % 100;
    post_meridiem = false;
  }
  if (hours > 59 || minutes > 59) return;
  if (post_meridiem && hours !== 0) hours += 12;
  if (minutes > 59) minutes = 59;
  if (hours > 23) hours = 0;
  result = ('' + hours).padStart(2, '0') + ':' + ('' + minutes).padStart(2, '0');
  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', '0000', '0011', '-1', 'mioaw',
  "0820",
  "32",
  "124",
  "1330",
  "130pm",
  "456",
  ":40",
  ":90",
  "12:69",
  "50:90",
  "aaa12:34aaa",
  "aaa50:00aaa",
 ];

    for ( var i = 0; i < tests.length; i++ ) {
      console.info( tests[i].padStart( 9, ' ' ) + " = " + parse(tests[i]) );
    }
also it's on Compilation table of other answers here is a fork Составительная таблица других ответов

Другие вопросы по теме