从Javascript中的用户输入解析Date对象的时间的最佳方法是什么?

Joe*_*oni 64 javascript time datetime parsing date

我正在使用表单窗口小部件,用户可以将一天中的时间输入到文本输入中(对于日历应用程序).使用JavaScript(我们使用jQuery FWIW),我想找到解析用户输入JavaScript Date()对象的文本的最佳方法,这样我就可以轻松地对其进行比较和其他事情.

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

  • 1:00 PM
  • 1:00 PM
  • 凌晨1点
  • 1:00 PM
  • 1:00 PM.
  • 1:00P
  • 下午1点
  • 下午1点
  • 1 p
  • 下午1点
  • 下午1点
  • 1P
  • 13:00
  • 13

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

Joh*_*sig 70

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

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]) );
}
Run Code Online (Sandbox Code Playgroud)

它也适用于其他一些品种(即使使用了它,它仍然可以工作 - 例如).显然这很粗糙,但它也非常轻巧(比使用完整的库要便宜得多).

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

  • 使用它之后,我注意到它没有正确解析时间"12 pm"的变体,因为它增加了12小时数.为了解决这个问题,我将d.setHours行改为:d.setHours(parseInt(time [1])+((parseInt(time [1])<12 && time [3])?12:0)); (7认同)
  • 对ParseInt的调用需要10的基数,因为当前导0时,JS假设基数为8,如果小时大于8并且前导为0(因为08不是有效的),则小时被解释为0基数8).另外,改变"p?" "[pP]?" 当AM/PM为大写时,它将使其工作.总而言之,除非你真的确定这种方法对你有用,否则你应该使用一个库.记住,时间恨我们所有人. (7认同)
  • 使用"[pP]"的另一种方法是将"i"附加到文字的末尾.这会使匹配不区分大小写. (4认同)
  • 我也注意到parseInt在':30'或':00'之类的字符串上窒息所以我改变了正则表达式以捕获没有冒号的分钟 (3认同)
  • 对于通过谷歌发现这一点的人的忠告,就像我做的那样.不要使用它.它似乎有效,但在12点左右的时候是错的.评论/编辑并没有解决这个问题.内森的解决方案更完整. (3认同)

Nat*_*usa 50

所提供的所有示例都无法在凌晨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]) );
}
Run Code Online (Sandbox Code Playgroud)

这适用于在其中任何位置包含时间的字符串.因此"abcde12:00pmdef"将被解析并返回12 pm.如果期望的结果是它仅返回字符串仅包含时间的时间,则可以使用以下正则表达式,前提是将"time [4]"替换为"time [6]".

/^(\d+)(:(\d\d))?\s*((a|(p))m?)?$/i
Run Code Online (Sandbox Code Playgroud)

  • +1,上午10:30只缺少'1030'. (5认同)

Jim*_*Jim 34

不要费心去做,只需使用datejs.

  • 25KB只做日期?!?!我的意思是,好的图书馆毫无疑问,如果我必须有心理日期处理功能,它就是那个.但是25KB大于jQuery的所有内核! (35认同)
  • 鉴于您想要接受的输入范围,我也会选择datejs.它似乎处理了大部分,除了一个只是一个数字,它需要作为一个月的日子. (4认同)
  • 我是一个人,我不知道"十二月二十二日"是什么意思.有多种方法可以解释这一点.如果我猜测DateJS正在做什么,我会说它将"12"解释为月份的那一天,并寻找下一个星期三,即12月份的12月.让我感到惊讶的唯一一件事就是它扔掉了"2020",而不是把它解释为晚上8:20. (3认同)

cla*_*ska 13

这里的大多数正则表达式解决方案在无法解析字符串时抛出错误,并且其中很多都没有像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]) );
}
Run Code Online (Sandbox Code Playgroud)

  • 我需要那个,所以继续用肮脏的黑客来解决这个问题:http://codepen.io/anon/pen/EjrVqq应该有一个更好的解决方案,但我还是不能指责它. (3认同)

Pat*_*ney 11

这是Joe的版本的改进.随意编辑它.

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]) );
}
Run Code Online (Sandbox Code Playgroud)

变化:

  • 为parseInt()调用添加了radix参数(因此jslint不会抱怨).
  • 使正则表达式不区分大小写所以"下午2:23"的工作方式就像"下午2:23"