Quelle est la meilleure façon d'analyser un temps dans un objet Date de l'entrée utilisateur en Javascript?

Je travaille sur un widget de formulaire pour permettre aux utilisateurs de saisir une heure de la journée dans une entrée de texte (pour une application de calendrier). À l'aide de JavaScript (nous utilisons jQuery FWIW), je souhaite trouver le meilleur moyen d'analyser le texte que l'utilisateur entre dans un objet Date() afin que je puisse facilement effectuer des comparaisons et d'autres choses.

J'ai essayé la méthode parse() et c'est un peu trop pointilleux pour mes besoins. Je m'attends à ce qu'il soit capable d'analyser avec succès les exemples de saisie des exemples suivants (en plus d'autres formats de temps logiquement similaires) que le même objet Date() :

  • 13h00
  • 13h00
  • 1:00 p
  • 13h00
  • 13h00.
  • 1: 00p
  • 13 heures
  • 13 heures
  • 1 p
  • 13 heures
  • 1p.m.
  • 1p
  • 13h00
  • 13

Je pense que je pourrais utiliser des expressions régulières pour diviser l'entrée et extraire les informations que je souhaite utiliser pour créer mon objet Date() . Quelle est la meilleure façon de procéder?

Une solution rapide qui fonctionne sur l'entrée que vous avez spécifiée:

 var times = ['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']; for ( var i = 0; i < times.length; i++ ) { var d = new Date(); var time = times[i].match(/(\d+)(?::(\d\d))?\s*(p?)/); d.setHours( parseInt(time[1]) + (time[3] ? 12 : 0) ); d.setMinutes( parseInt(time[2]) || 0 ); console.log( d ); } 

Tous les exemples fournis ne fonctionnent pas pour les heures de 12h00 à 12h59. Ils lancent également une erreur si le regex ne correspond pas à une heure. Ce qui suit:

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

Cela fonctionnera pour les chaînes qui contiennent un temps n'importe où en elles. Alors "abcde12: 00pmdef" serait analysé et retourné 12 heures. Si le résultat souhaité est qu'il ne retourne qu'un temps lorsque la chaîne contient seulement une heure, l'expression régulière suivante peut être utilisée, à condition de remplacer "Time [4]" par "Time [6]".

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

Ne vous embêtez pas de le faire vous-même, utilisez les dates .

La plupart des solutions regex jettent ici des erreurs lorsque la chaîne ne peut pas être analysée, et beaucoup d'entre elles ne représentent que des chaînes comme 1330 ou 130pm . Même si ces formats n'ont pas été spécifiés par l'OP, je les trouve critiques pour l'analyse des dates entrées par les humains.

Tout cela m'a permis de penser que l'utilisation d'une expression régulière pourrait ne pas être la meilleure approche pour cela.

Ma solution est une fonction qui analyse non seulement l'heure, mais aussi vous permet de spécifier un format de sortie et une étape (intervalle) à laquelle aller au tour des minutes. À environ 70 lignes, il est toujours léger et analyse tous les formats susmentionnés ainsi que ceux sans colonnes.

Demo: http://jsfiddle.net/HwwzS/1/

Code: https://gist.github.com/claviska/4744736

Et ci-dessous, au cas où les liens sombreraient un jour:

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

Voici une amélioration de la version de Joe . N'hésitez pas à l'éditer.

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

Changements:

  • Ajout du paramètre radix aux appels parseInt () (alors jslint ne se plaint pas).
  • Fait le regex insensible à la casse, alors "2:23 PM" fonctionne comme "2:23 pm"

Je me suis heurté à quelques contournements dans la mise en œuvre de la solution de John Resig. Voici la fonction modifiée que j'ai utilisée en fonction de sa réponse:

 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() 

Voici une solution pour tous ceux qui utilisent une horloge 24h.

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

Notez que cette fonction prend également en charge l'analyse de chaînes comme

  • 0820 -> 08:20
  • 32 -> 03:02
  • 124 -> 12:04

Il s'agit d'une approche plus robuste qui tient compte de la façon dont les utilisateurs ont l'intention d'utiliser ce type d'entrée. Par exemple, si un utilisateur a entré "12", ils s'attendent à ce qu'il soit 12 heures (midi) et non à 12 heures. La fonction ci-dessous gère tout cela. Il est également disponible ici: 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; } 

Il s'agit d'un prototype de chaîne, de sorte que vous pouvez l'utiliser comme tel:

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

AnyTime.Converter peut analyser dates / fois dans de nombreux formats différents:

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

Pourquoi ne pas utiliser la validation pour affiner ce qu'un utilisateur peut mettre et simplifier la liste pour inclure uniquement les formats qui peuvent être analysés (ou analysés après certains ajustements).

Je ne pense pas trop demander à un utilisateur de mettre un temps dans un format pris en charge.

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

Dd A (m) / P (m)

Jj

J'ai apporté quelques modifications à la fonction ci-dessus pour supporter quelques autres formats.

1400 -> 14h00

1.30 -> 13h30

1: 30a -> 1:30 AM

100 -> 1:00 AM

Je ne l'ai pas nettoyé mais fonctionne pour tout ce que je peux penser.

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

Merci

Une amélioration de la solution de Patrick McElhaney (son ne gère pas les 12 heures correctement)

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

Beaucoup de réponses, donc une autre ne fera pas mal.

 /** * 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; } 

Pour être correctement robuste, il devrait vérifier que chaque valeur est dans la portée des valeurs autorisées, par exemple si les heures am / pm doivent être de 1 à 12 inclus, sinon 0 à 24 inclus, etc.

Le paquet de temps est de 0,9 kg de taille. Disponible avec les gestionnaires de paquets NPM et Bower.

Voici un exemple directement à partir de 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