如何通过JavaScript中的用户输入将时间解析为Date对象?


68

我正在使用一个表单小部件,供用户在文本输入中输入一天的时间(对于日历应用程序)。我想使用JavaScript(我们使用的是jQuery FWIW)来解析用户输入到JavaScriptDate()对象中的文本的最佳方法,以便我可以轻松地执行比较和其他操作。

我尝试了该parse()方法,但对于我的需求来说有点挑剔。我希望它能够成功地将以下示例输入时间(除了其他逻辑上相似的时间格式)解析为同一Date()对象:

  • 1:00 PM
  • 1:00 PM
  • 下午1:00
  • 1:00 PM
  • 1:00 PM。
  • 1:00p
  • 下午1点
  • 下午1点
  • 1个
  • 下午1点
  • 下午1点
  • 1p
  • 13:00
  • 13

我认为我可能会使用正则表达式来拆分输入并提取要用于创建Date()对象的信息。做这个的最好方式是什么?

Answers:


75

一种适用于您指定的输入的快速解决方案:

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]) );
}

它也应该适用于其他一些品种(例如,即使使用了am,它仍然可以使用-例如)。显然,这很粗糙,但是也很轻巧(例如,使用它比完整的库便宜得多)。

警告:该代码不适用于12:00 AM等。


8
处理完此操作后,我注意到它无法正确解析时间“ 12 pm”的变体,因为它将小时数加了12。要解决此问题,我将d.setHours行更改为:d.setHours(parseInt(time [1])+((parseInt(time [1])<12 && time [3])12:0));
Joe Lencioni

3
我还注意到,parseInt函数窒息像串“:30”或“:00”,所以我改变了正则表达式来捕捉分钟内没有冒号
乔伦乔尼

7
对ParseInt的调用需要以10为基数,因为JS在存在前导0时假定基数为8,如果小时数大于8并且具有前导0,则小时将被解释为0(因为08无效以8为底​​的数字)。另外,更改“ p?” 到“ [pP]?” 当AM / PM为大写字母时,它将使其工作。总而言之,除非您真的确定此方法对您有用,否则应使用库。记住,时间恨我们所有人。
Benji纽约,2010年

5
使用“ [pP]”的替代方法是在文字的末尾附加“ i”。这会使匹配不区分大小写。
克里斯·米勒

3
给像我一样通过Google找到它的任何人一个建议。不要使用它。它似乎可以正常工作,但是12am左右的时间是错误的。评论/编辑不能解决这个问题。内森的解决方案更加完善。
2015年

52

提供的所有示例在从12:00 am到12:59 am的时间内都无法正常工作。如果正则表达式与时间不匹配,它们也会引发错误。以下处理此问题:

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 pm。如果期望的结果是仅在字符串中包含时间时返回一个时间,则可以使用以下正则表达式,前提是您将“ time [4]”替换为“ time [6]”。

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

34

不用自己动手,只需使用datejs即可


37
只是25KB做日期?!?!我的意思是,毫无疑问,不错的图书馆,如果我必须拥有心理日期处理功能,那将是一个。但是25KB比jQuery的所有核心都大!!!
詹森·邦廷

4
给定您要接受的输入范围,我也会选择datejs。除了一个数字(一个月的某天)外,它似乎可以处理其中的大多数。
强尼·布坎南

是的,我可能会继续使用datejs。通过在解析时间时在字符串前添加“ 1/1/2000”,可以解决输入数字被视为月份的问题。
Joe Lencioni

1
巨大+1!至于大小-实际上每个区域是25KB。但至少它支持语言环境!不用编写您自己的过程,而使用可用的函数(或编写更好的库并共享它)。另外,尽管从JS中删除了空格,但对我来说,它看起来似乎并不狭窄,因此您可以在其中保存一些字节。如果这对您很重要。
johndodo

3
我是人类,我不知道“ 2020年12月12日”是什么意思。有多种解释方法。如果我想知道DateJS在做什么,我会说它将Date解释为“ 12”,并寻找12月的下一个星期三,即12月。唯一令我惊讶的是它扔掉了“ 2020”,而不是将其解释为晚上8:20。
吉姆(Jim)

16

当无法解析字符串时,此处的大多数正则表达式解决方案都会引发错误,并且其中很少有类似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.log( tests[i].padStart( 9, ' ' ) + " = " + parseTime(tests[i]) );
}


1
我必须对此进行标记。当大小很重要时,这是唯一真正有用的答案。大多数情况下,我使用momentjs,但是与该解决方案相比,这是巨大的。
彼得·沃恩

3
我需要它,因此继续进行修复,并进行了肮脏的修改:codepen.io/anon/pen/EjrVqq应该有一个更好的解决方案,但是我还不能对此付诸行动。
命名者

11

这是对乔的版本的改进。随时对其进行进一步编辑。

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]) );
}

变化:

  • 在parseInt()调用中添加了基数参数(因此jslint不会抱怨)。
  • 使正则表达式具有区分大小写的功能,因此“下午2:23”的工作方式类似于“下午2:23”

3

在实施John Resig的解决方案时,我遇到了一些麻烦。这是根据他的回答我一直在使用的修改后的函数:

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]) );
}


这与我在上面投票最高的答案中提到的错误相同。
Benji纽约,2010年

3

对于所有使用以下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]) );
}


2

时间包的大小0.9kbs。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

3
此软件包不支持24小时制,这可能是重要的限制,也可能不是重要的限制。
gruppler

@gruppler感谢您的重要提示。FWIW,有一个“拉取请求”,已完成工作以支持24小时格式。PR于2014年提交。无论如何,这是该请求的链接:github.com/zackdever/time/pull/7
Sgnl

2
此程序包不支持秒或毫秒。(这里的大多数答案都没有,但是我倾向于从“包”中获得更多的期望,而不是SO答案中的代码片段。)
Qwertie

2

这是一种更坚固的方法,它考虑了用户打算如何使用这种类型的输入。例如,如果用户输入“ 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();


1

很多答案,所以再也不会受到伤害。

/**
 * 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(含)等。


1

我对上面的功能做了一些修改,以支持更多格式。

  • 1400-> 2:00 PM
  • 1.30-> 1:30 PM
  • 1:30a-> 1:30 AM
  • 100-> 1:00 AM

还没有清除它,但可以满足我的所有想法。

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]) );
}


1

这是覆盖原始答案,任意合理位数,猫输入数据和逻辑谬论的另一种方法。该算法如下:

  1. 确定子午线是否为子午线
  2. 将输入数字转换为整数值。
  3. 0到24之间的时间:整点是小时,没有分钟(12点是下午)。
  4. 100到2359之间的时间:小时div 100是剩余时间,分钟mod 100是剩余时间。
  5. 从2400开始的时间:小时为午夜,其余时间为分钟。
  6. 当小时数超过12时,请减去12并强制执行meridiem。
  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.log( tests[i].padStart( 9, ' ' ) + " = " + tests[i].toTime() );
}

在jQuery中,新定义的String原型的用法如下:

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

1

我对其他答案不满意,所以又做了一个。这个版本:

  • 识别秒和毫秒
  • 退货 undefined无效输入,例如“ 13:00 pm”或“ 11:65”
  • 如果您提供一个本地时间,则返回本地时间 localDate参数,则返回,否则返回Unix时代(1970年1月1日)的UTC时间。
  • 支持类似的军事时间1330(要禁用,请在正则表达式中输入第一个“:”)
  • 本身允许一个小时,带有24小时的时间(即“ 7”表示上午7点)。
  • 允许将24小时作为0小时的同义词,但不允许25小时。
  • 要求时间在字符串的开头(要禁用,请删除 ^\s*在正则表达式中)
  • 具有测试代码,该代码可以实际检测何时输出不正确。

编辑:现在它是一个包含格式化程序的软件包timeToStringnpm 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.');

1

其他答案汇编表

首先,我无法相信没有内置的功能甚至没有强大的第三方库可以处理此问题。实际上,这是Web开发,因此我可以相信。

试图用所有这些不同的算法测试所有极端情况让我很头疼,所以我自由地将这个线程中的所有答案和测试编译到一个方便的表中。

代码(和结果表)太大了,无法包含内联,所以我做了一个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:00am12:00pm?)。您将必须仔细检查表格,看看哪种算法最适合您。

注意:颜色不一定表示输出的质量或“预期”,它们仅表示输出的类型:

  • red =抛出js错误

  • yellow= “falsy”值(undefinednullNaN"""invalid date"

  • green= jsDate()对象

  • light green =其他一切

Date()输出对象的情况下,我将其转换为24小时HH:mm格式以便于比较。


0

为什么不使用验证来缩小用户可以放入的内容,并简化列表,使其仅包含可以解析(或在某些调整后解析)的格式。

我认为要求用户以受支持的格式放置时间并不要求太多。

dd:dd A(m)/ P(m)

dd A(m)/ P(m)

dd


您说的没错,这确实不是要求太多。但是,这对用户来说是一个障碍,我想使这种特殊形式的使用尽可能合理。理想情况下,输入将足够灵活以解释他们键入的内容并将其重新格式化为标准格式。
Joe Lencioni

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

谢谢


0

对Patrick McElhaney解决方案的改进(他无法正确处理凌晨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]) );
}


0

如果你只想几秒钟,这里是一支班轮

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

0

在对我的其他编译答案进行了彻底的测试和调查之后,我得出结论,@ Dave Jarvis的解决方案与我认为合理的输出和边缘案例处理最为接近。作为参考,我查看了Google日历的时间输入在退出文本框后将时间重新格式化为什么时间。

即便如此,我仍然看到它无法像Google日历那样处理一些(尽管很奇怪)边缘案例。因此,我从头开始进行了重新设计,这就是我想到的。我也将其添加到我的编译答案中

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

我觉得这是我所需要的最接近的产品,但是欢迎提出建议。注意:这是以美国为中心的,对于某些模式它默认为am / pm:

  • 1=> 13:001:00pm
  • 1100=> 23:0011:00pm
  • 456=> 16:564:56pm

请注意,对于时间跟踪应用程序,1100请将其解析为11:00am。(我敢打赌我们作为数据输入的大多数人类活动都是在白天进行的。)如果您可以回忆起缺乏我的解决方案的极端情况,那么对我的回答进行评论将不胜感激。不错的总结!
Dave Jarvis'4

作为未成年人挑剔,string = String(string);string = String(string.toLowerCase())避免冗余呼叫。周围有布尔逻辑一些其他简化可以做出..
戴夫·贾维斯

其他一些要点:99*测试给不一致的结果,1000可能意味着10:00am,且-11:00pm好。但是,返回Date对象似乎超出了用例。(它假定还需要当前日期,对于输入的任何特定时间不一定是正确的。)
Dave Jarvis

很久以前,我编写了此答案,但请注意,我为Google Calendar扩展编写了此代码,因此即使该行为有问题,我也模仿了该行为。例如,1100在GCal中映射到晚上11点。回复:99*,我不知道。可能会辩论,等应解释为什么9999999但我的评论说了我的假设。在这种情况下,GCal甚至不接受52:95。最后,重新:Date。它提供了其它的一些getformat功能。还有一些库需要Date格式的东西,这就是我的案例iirc。
V. Rubinetti
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.