简体   繁体   English

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

[英]How to parse a time into a Date object from user input in JavaScript?

I am working on a form widget for users to enter a time of day into a text input (for a calendar application).我正在开发一个表单小部件,供用户在文本输入中输入一天中的时间(对于日历应用程序)。 Using JavaScript (we are using jQuery FWIW), I want to find the best way to parse the text that the user enters into a JavaScript Date() object so I can easily perform comparisons and other things on it.使用 JavaScript(我们使用 jQuery FWIW),我想找到解析用户输入 JavaScript Date()对象的文本的最佳方法,以便我可以轻松地对其进行比较和其他操作。

I tried the parse() method and it is a little too picky for my needs.我尝试了parse()方法,但它对我的需求来说有点太挑剔了。 I would expect it to be able to successfully parse the following example input times (in addition to other logically similar time formats) as the same Date() object:我希望它能够将以下示例输入时间(除了其他逻辑上相似的时间格式之外)成功解析为相同的Date()对象:

  • 1:00 pm 1:00 PM
  • 1:00 pm 1:00 PM
  • 1:00 p 1:00 下午
  • 1:00pm 1:00 PM
  • 1:00p.m. 1:00 PM。
  • 1:00p 1:00p
  • 1 pm下午 1 点
  • 1 pm下午 1 点
  • 1 p 1 人
  • 1pm下午1点
  • 1p.m.下午 1 点
  • 1p 1p
  • 13:00 13:00
  • 13 13

I am thinking that I might use regular expressions to split up the input and extract the information I want to use to create my Date() object.我想我可能会使用正则表达式来拆分输入并提取我想用来创建我的Date()对象的信息。 What is the best way to do this?做这个的最好方式是什么?

A quick solution which works on the input that you've specified:适用于您指定的输入的快速解决方案:

 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 pm','1:00 p','1:00pm','1:00p.m.','1:00p','1 pm', '1 pm','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]) ); }

It should work for a few other varieties as well (even if am is used, it'll still work - for example).它也应该适用于其他一些品种(即使使用了 am,它仍然可以工作 - 例如)。 Obviously this is pretty crude but it's also pretty lightweight (much cheaper to use that than a full library, for example).显然,这很粗糙,但它也很轻巧(例如,使用它比使用完整库便宜得多)。

Warning: The code doe not work with 12:00 AM, etc.警告:该代码不适用于上午 12:00 等。

All of the examples provided fail to work for times from 12:00 am to 12:59 am.提供的所有示例都无法在上午 12:00 到上午 12:59 的时间内工作。 They also throw an error if the regex does not match a time.如果正则表达式与时间不匹配,它们也会抛出错误。 The following handles this:下面处理这个:

 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 pm','1:00 p','1:00pm','1:00p.m.','1:00p','1 pm', '1 pm','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]) ); }

This will work for strings which contain a time anywhere inside them.这适用于在其中任何地方包含时间的字符串。 So "abcde12:00pmdef" would be parsed and return 12 pm.所以“abcde12:00pmdef”将被解析并返回 12 pm。 If the desired outcome is that it only returns a time when the string only contains a time in them the following regular expression can be used provided you replace "time[4]" with "time[6]".如果所需的结果是它只返回字符串中仅包含时间的时间,则可以使用以下正则表达式,前提是您将“time[4]”替换为“time[6]”。

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

不要费心自己做,只需使用datejs

Most of the regex solutions here throw errors when the string can't be parsed, and not many of them account for strings like 1330 or 130pm .当无法解析字符串时,这里的大多数正则表达式解决方案都会抛出错误,并且其中没有多少会考虑像1330130pm这样的字符串。 Even though these formats weren't specified by the OP, I find them critical for parsing dates input by humans.尽管这些格式不是由 OP 指定的,但我发现它们对于解析人类输入的日期至关重要。

All of this got me to thinking that using a regular expression might not be the best approach for this.所有这些让我想到使用正则表达式可能不是最好的方法。

My solution is a function that not only parses the time, but also allows you to specify an output format and a step (interval) at which to round minutes to.我的解决方案是一个函数,它不仅可以解析时间,还可以指定输出格式和将分钟舍入到的步长(间隔)。 At about 70 lines, it's still lightweight and parses all of the aforementioned formats as well as ones without colons.大约 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 pm','1:00 p','1:00pm','1:00p.m.','1:00p','1 pm', '1 pm','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]) ); }

Here's an improvement on Joe's version .这是对Joe 版本的改进。 Feel free to edit it further.随意进一步编辑它。

 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 pm','1:00 p','1:00pm','1:00p.m.','1:00p','1 pm', '1 pm','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]) ); }

Changes:变化:

  • Added radix parameter to the parseInt() calls (so jslint won't complain).向 parseInt() 调用添加了基数参数(因此 jslint 不会抱怨)。
  • Made the regex case-insenstive so "2:23 PM" works like "2:23 pm"使正则表达式不区分大小写,因此“2:23 PM”的工作方式类似于“2:23 pm”

I came across a couple of kinks in implementing John Resig's solution.我在实施 John Resig 的解决方案时遇到了一些问题。 Here is the modified function that I have been using based on his answer:这是我根据他的回答使用的修改后的函数:

 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 pm','1:00 p','1:00pm','1:00p.m.','1:00p','1 pm', '1 pm','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]) ); }

Here's a solution more for all of those who are using a 24h clock that supports:对于所有使用支持的 24 小时时钟的人,这里有一个解决方案:

  • 0820 -> 08:20 0820 -> 08:20
  • 32 -> 03:02 32 -> 03:02
  • 124 -> 12:04 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 pm','1:00 p','1:00pm','1:00p.m.','1:00p','1 pm', '1 pm','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]) ); }

This is a more rugged approach that takes into account how users intend to use this type of input.这是一种更坚固的方法,它考虑了用户打算如何使用这种类型的输入。 For example, if a user entered "12", they would expect it to be 12pm (noon), and not 12am.例如,如果用户输入“12”,他们会认为是中午 12 点(中午),而不是上午 12 点。 The below function handles all of this.下面的函数处理所有这些。 It is also available here: http://blog.de-zwart.net/2010-02/javascript-parse-time/它也可以在这里找到: 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 pm','1:00 p','1:00pm','1:00p.m.','1:00p','1 pm', '1 pm','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() ); }

This is a string prototype, so you can use it like so:这是一个字符串原型,因此您可以像这样使用它:

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

The time package is 0.9kbs in size.时间包大小为 0.9kbs。 Available with NPM and bower package managers.可用于 NPM 和 Bower 包管理器。

Here's an example straight from the README.md :这是直接来自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

AnyTime.Converter can parse dates/times in many different formats: AnyTime.Converter 可以解析多种不同格式的日期/时间:

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

Lots of answers so one more won't hurt.很多答案,所以多一个不会伤害。

 /** * Parse a time in nearly any format * @param {string} time - Anything like 1 p, 13, 1:05 pm, 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 pm','1:00 p','1:00pm','1:00p.m.','1:00p','1 pm', '1 pm','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]) ); }

To be properly robust, it should check that each value is within range of allowed values, eg if am/pm hours must be 1 to 12 inclusive, otherwise 0 to 24 inclusive, etc.为了适当地健壮,它应该检查每个值是否在允许值的范围内,例如,如果 am/pm 小时必须是 1 到 12,包括 1 到 12,否则是 0 到 24,等等。

Here's another approach that covers the original answer, any reasonable number of digits, data entry by cats, and logical fallacies.这是另一种方法,涵盖原始答案、任何合理数量的数字、猫的数据输入和逻辑谬误。 The algorithm follows:算法如下:

  1. Determine whether meridian is post meridiem .确定子午线是否为子午线
  2. Convert input digits to an integer value.将输入数字转换为整数值。
  3. Time between 0 and 24: hour is the o'clock, no minutes (hours 12 is PM). 0 到 24 之间的时间:小时是点钟,没有分钟(12 小时是下午)。
  4. Time between 100 and 2359: hours div 100 is the o'clock, minutes mod 100 remainder. 100 到 2359 之间的时间:小时 div 100 是点钟,分钟 mod 100 余数。
  5. Time from 2400 on: hours is midnight, with minutes remainder.时间从 2400 开始:小时是午夜,剩余分钟。
  6. When hours exceeds 12, subtract 12 and force post meridiem true.当小时数超过 12 时,减去 12 并强制 post meridiem 为真。
  7. When minutes exceeds 59, force to 59.当分钟超过 59 时,强制到 59。

Converting the hours, minutes, and post meridiem to a Date object is an exercise for the reader (numerous other answers show how to do this).将小时、分钟和后午线转换为 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 pm','1:00 p','1:00pm','1:00p.m.','1:00p','1 pm', '1 pm','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() ); }

With jQuery, the newly defined String prototype is used as follows:在 jQuery 中,新定义的 String 原型使用​​如下:

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

I wasn't happy with the other answers so I made yet another one.我对其他答案不满意,所以我又做了一个。 This version:这个版本:

  • Recognizes seconds and milliseconds识别秒和毫秒
  • Returns undefined on invalid input such as "13:00pm" or "11:65"对无效输入返回undefined ,例如“13:00pm”或“11:65”
  • Returns a local time if you provide a localDate parameter, otherwise returns a UTC time on the Unix epoch (Jan 1, 1970).如果提供localDate参数,则返回本地时间,否则返回 Unix 纪元(1970 年 1 月 1 日)的 UTC 时间。
  • Supports military time like 1330 (to disable, make the first ':' required in the regex)支持军用时间,如1330 (要禁用,请在正则表达式中使用第一个“:”)
  • Allows an hour by itself, with 24-hour time (ie "7" means 7am).允许一个小时,时间为 24 小时(即“7”表示早上 7 点)。
  • Allows hour 24 as a synonym for hour 0, but hour 25 is not allowed.允许小时 24 作为小时 0 的同义词,但不允许小时 25。
  • Requires the time to be at the beginning of the string (to disable, remove ^\\s* in the regex)需要时间在字符串的开头(要禁用,删除正则表达式中的^\\s*
  • Has test code that actually detects when the output is incorrect.具有实际检测何时输出不正确的测试代码。

Edit: it's now a package including a timeToString formatter: npm i simplertime编辑:它现在是一个包含timeToString格式化npm i simplertimenpm 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.');

Compilation table of other answers其他答案汇编表

First of all, I can't believe that there is not a built-in functionality or even a robust third-party library that can handle this.首先,我无法相信没有内置功能甚至强大的第三方库可以处理这个问题。 Actually, it's web development so I can believe it.实际上,这是网络开发,所以我可以相信。

Trying to test all edge cases with all these different algorithms was making my head spin, so I took the liberty of compiling all the answers and tests in this thread into a handy table.试图用所有这些不同的算法测试所有边缘情况让我头晕目眩,所以我冒昧地将这个线程中的所有答案和测试编译成一个方便的表格。

The code (and resulting table) is pointlessly large to include inline, so I've made a JSFiddle:代码(和结果表)对于包含内联来说毫无意义,所以我制作了一个 JSFiddle:

http://jsfiddle.net/jLv16ydb/4/show 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

Please feel free to fork my fiddle and add more algorithms and test cases请随意 fork 我的小提琴并添加更多算法和测试用例

I didn't add any comparisons between the result and the "expected" output, because there are cases where the "expected" output could be debated (eg, should 12 be interpreted as 12:00am or 12:00pm ?).我没有在结果和“预期”输出之间添加任何比较,因为在某些情况下可能会讨论“预期”输出(例如, 12应该解释为12:00am还是12:00pm ?)。 You will have to go through the table and see which algorithm makes the most sense for you.您将不得不仔细阅读表格,看看哪种算法对您最有意义。

Note: The colors do not necessarily indicate quality or "expectedness" of output, they only indicate the type of output:注意:颜色不一定表示输出的质量或“预期”,它们仅表示输出的类型:

  • red = js error thrown red = 抛出 js 错误

  • yellow = "falsy" value ( undefined , null , NaN , "" , "invalid date" ) yellow = "falsy" 值 ( undefined , null , NaN , "" , "invalid date" )

  • green = js Date() object green = js Date()对象

  • light green = everything else light green = 其他一切

Where a Date() object is the output, I convert it to 24 hr HH:mm format for ease of comparison.其中Date()对象是输出,我将其转换为 24 小时HH:mm格式以便于比较。

I have made some modifications to the function above to support a few more formats.我对上面的函数做了一些修改以支持更多的格式。

  • 1400 -> 2:00 PM 1400 -> 下午 2:00
  • 1.30 -> 1:30 PM 1.30 -> 下午 1:30
  • 1:30a -> 1:30 AM 1:30a -> 1:30 AM
  • 100 -> 1:00 AM 100 -> 凌晨 1:00

Ain't cleaned it up yet but works for everything I can think of.还没有清理它,但适用于我能想到的一切。

 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 pm','1:00 p','1:00pm','1:00p.m.','1:00p','1 pm', '1 pm','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]) ); }

Why not use validation to narrow down what a user can put in and simplify the list to only include formats that can be parsed (or parsed after some tweaking).为什么不使用验证来缩小用户可以输入的范围并简化列表以仅包含可以解析(或经过一些调整后解析)的格式。

I don't think it's asking too much to require a user to put a time in a supported format.我认为要求用户以受支持的格式输入时间并不过分。

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

dd A(m)/P(m) dd A(米)/P(米)

dd

An improvement to Patrick McElhaney's solution (his does not handle 12am correctly)对 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 pm','1:00 p','1:00pm','1:00p.m.','1:00p','1 pm', '1 pm','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]) ); }

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

thanks谢谢

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

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

After thoroughly testing and investigating through my other compilation answer , I concluded that @Dave Jarvis's solution was the closest to what I felt were reasonable outputs and edge-case-handling.在对我的其他编译答案进行彻底测试和调查后,我得出结论,@Dave Jarvis 的解决方案最接近我认为的合理输出和边缘情况处理。 For reference, I looked at what Google Calendar's time inputs reformatted the time to after exiting the text box.作为参考,我查看了退出文本框后 Google 日历的时间输入将时间重新格式化的内容。

Even still, I saw that it didn't handle some (albeit weird) edge cases that Google Calendar did.即便如此,我还是看到它没有处理谷歌日历所做的一些(尽管很奇怪)的边缘情况。 So I reworked it from the ground up and this is what I came up with.所以我从头开始重新设计它,这就是我想出的。 I also added it to my compilation answer .我还将它添加到我的编译答案中

 // 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 pm','1:00 p','1:00pm','1:00p.m.','1:00p','1 pm', '1 pm','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]) ); }

I feel that this is the closest I can get for my needs, but suggestions are welcome.我觉得这是我能得到的最接近我的需求的方法,但欢迎提出建议。 Note: This is American-centric in that it defaults to am/pm for certain patterns:注意:这是以美国为中心的,因为它默认为某些模式的 am/pm:

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

I've needed a time parser function and based on some of the answers i ended up with this function我需要一个时间解析器函数,并根据一些答案我最终得到了这个函数

 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 pm','1:00 p','1:00pm','1:00p.m.','1:00p','1 pm', '1 pm','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.log( tests[i].padStart( 9, ' ' ) + " = " + parse(tests[i]) ); }
also it's on Compilation table of other answers here is a fork Compilation table of other answers 它也在其他答案的编译表上,这里是其他答案的分支编译表

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM