Quel est le meilleur moyen d’analyser une heure dans un objet Date à partir de la saisie utilisateur en Javascript?

StackOverflow https://stackoverflow.com/questions/141348

Question

Je travaille sur un widget de formulaire permettant aux utilisateurs d'entrer une heure dans une entrée de texte (pour une application de calendrier). En utilisant JavaScript (nous utilisons jQuery FWIW), je souhaite trouver le meilleur moyen d’analyser le texte saisi par l’utilisateur dans un objet JavaScript Date () afin de pouvoir effectuer facilement des comparaisons et autres opérations sur celui-ci. .

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

  • 13h00
  • 13h00
  • 1:00 p
  • 13h00
  • 13h00
  • 1: 00p
  • 13 heures
  • 13 heures
  • 1 p
  • 13h
  • 13 heures
  • 1p
  • 13:00
  • 13

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

Était-ce utile?

La solution

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

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

Cela devrait également fonctionner pour quelques autres variétés (même si on utilise l'heure, cela fonctionnera quand même - par exemple). Évidemment, c’est plutôt brutal, mais c’est aussi assez léger (beaucoup moins cher à utiliser qu’une bibliothèque complète, par exemple).

  

Avertissement: le code ne fonctionne pas à midi, etc.

Autres conseils

Tous les exemples fournis ne fonctionnent pas de 12h00 à 12h59. Ils génèrent également une erreur si l'expression régulière ne correspond pas à une heure. Ce qui suit gère ceci:

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

Ceci fonctionnera pour les chaînes qui contiennent une heure n'importe où à l'intérieur. So " abcde12: 00 pmdef" serait analysé et de retour 12 heures. Si le résultat souhaité est qu'il renvoie uniquement une heure lorsque la chaîne ne contient qu'une heure, l'expression régulière suivante peut être utilisée, à condition de remplacer "time [4]". avec "temps [6]".

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

Ne vous embêtez pas, utilisez simplement datejs .

La plupart des solutions d'expression rationnelle ici génèrent des erreurs lorsque la chaîne ne peut pas être analysée, et peu d'entre elles rendent compte de chaînes telles que 1330 ou 130pm . Bien que ces formats n'aient pas été spécifiés par l'OP, je les trouve essentiels pour l'analyse des dates saisies par les humains.

Tout cela m'a amené à penser qu'utiliser une expression régulière n'était peut-être pas la meilleure approche pour cela.

Ma solution est une fonction qui non seulement analyse l'heure, mais vous permet également de spécifier un format de sortie et une étape (intervalle) auxquels arrondir les minutes. Avec environ 70 lignes, il reste léger et analyse tous les formats susmentionnés, ainsi que ceux sans colons.

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

Voici une amélioration de la version de Joe . N'hésitez pas à le modifier davantage.

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

Modifications:

  • Ajout du paramètre radix aux appels parseInt () (pour que jslint ne se plaint pas).
  • Faites en sorte que la regex ne prenne pas en compte la phrase "14 h 23" fonctionne comme "14 h 23"

J'ai rencontré quelques problèmes lors de 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()

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

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

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

Le package time a une taille de 0,9 Ko. Disponible avec les gestionnaires de packages NPM et bower.

Voici un exemple tiré directement 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

Il s'agit d'une approche plus robuste qui prend en compte la manière dont les utilisateurs envisagent d'utiliser ce type d'entrée. Par exemple, si un utilisateur entre "12", il s’attend à ce que ce 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;
}

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

Ceci est un prototype de chaîne, vous pouvez donc l'utiliser comme suit:

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

Voici une solution supplémentaire pour tous ceux qui utilisent une horloge 24h qui prend en charge:

  • 0820 - > 08h20
  • 32 - > 03:02
  • 124 - > 12h04

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

J'ai apporté quelques modifications à la fonction ci-dessus pour prendre en charge quelques formats supplémentaires.

  • 1400 - > 14h00
  • 1,30 - > 13h30
  • 1: 30a - > 01h30
  • 100 - > 1h00

Je ne l'ai pas encore nettoyé, mais cela fonctionne pour tout ce à quoi 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;
}

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

Voici une autre approche qui couvre la réponse initiale, tout nombre raisonnable de chiffres, la saisie de données par les chats et les erreurs logiques. L'algorithme suit:

  1. Déterminez si le méridien est post meridiem .
  2. Convertissez les chiffres d'entrée en une valeur entière.
  3. Temps entre 0 et 24: heure est l'heure, pas de minutes (heures 12 est PM).
  4. Temps entre 100 et 2359: heure div 100 est l'heure, minutes mod 100 reste.
  5. Heure à partir de 24 h 00: l'heure est minuit et il ne reste que quelques minutes.
  6. Lorsque le nombre d'heures est supérieur à 12, soustrayez 12 et forcez post post meridiem true.
  7. Lorsque le nombre de minutes dépasse 59, force à 59.

La conversion des heures, des minutes et du post-meridiem en un objet Date est un exercice pour le lecteur (de nombreuses autres réponses montrent comment faire cela.)

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

Avec jQuery, le prototype de chaîne nouvellement défini est utilisé comme suit:

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

Pourquoi ne pas utiliser la validation pour préciser ce qu'un utilisateur peut insérer et simplifier la liste pour n'inclure que les formats pouvant être analysés (ou analysés après quelques ajustements).

Je ne pense pas que ce soit trop demander d'exiger qu'un utilisateur mette l'heure dans un format pris en charge.

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

jj A (m) / P (m)

dd

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

Beaucoup de réponses pour qu'une autre ne fasse pas mal.

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

Pour être suffisamment robuste, il convient de vérifier que chaque valeur est dans la plage des valeurs autorisées, par exemple si am / pm hours doit être compris entre 1 et 12 inclus, sinon entre 0 et 24 inclus, etc.

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

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

Je n’étais pas satisfait des autres réponses, j’en ai donc fait une autre. Cette version:

  • Reconnaît les secondes et les millisecondes
  • Renvoie undefined sur une entrée non valide telle que " 13: 00 pm" ou "11: 65"
  • Retourne une heure locale si vous fournissez un paramètre localDate , sinon renvoie une heure UTC sur l'époque Unix (1er janvier 1970).
  • Prend en charge l'heure militaire telle que 1330 (pour le désactiver, définissez le premier ':' requis dans la regex)
  • Permet une heure seule, avec une période de 24 heures ("7" signifie 7h).
  • Autorise l'heure 24 comme synonyme pour l'heure 0, mais pas l'heure 25.
  • Requiert que le temps soit au début de la chaîne (pour le désactiver, supprimez ^ \ s * dans l'expression régulière)
  • Le code de test détecte-t-il quand la sortie est incorrecte?

Modifier: il s'agit désormais d'un package , y compris un formateur 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.log(`Test failed at ${key}:"${strings[i]}" with result ${result ? result.toUTCString() : 'undefined'}`);
    } else {
      passed++;
    }
  }
}
console.log(passed + ' tests passed.');

Si vous ne voulez que quelques secondes, voici un one-liner

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

Tableau de compilation d'autres réponses

Tout d’abord, je ne peux pas croire qu’il n’existe pas de fonctionnalité intégrée, ni même de bibliothèque tierce robuste capable de gérer cela. En fait, c’est du développement Web, donc je peux le croire.

Essayer de tester tous les cas extrêmes avec tous ces algorithmes différents me faisait tourner la tête. J'ai donc pris la liberté de compiler toutes les réponses et les tests de ce fil dans un tableau pratique.

Le code (et le tableau qui en résulte) est inutile à inclure en ligne, j'ai donc créé un 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

N'hésitez pas à bifurquer et à ajouter d'autres algorithmes et scénarios de test

Je n’ai ajouté aucune comparaison entre le résultat et "attendu". sortie, car il y a des cas où le "attendu" " la sortie pourrait faire l’objet d’un débat (par exemple, le code 12 doit-il être interprété comme 12 h 00 ou 12 h 00 ?). Vous devrez parcourir la table et voir quel algorithme vous convient le mieux.

Remarque: les couleurs n'indiquent pas nécessairement la qualité ou "l'attente". de sortie, ils indiquent uniquement le type de sortie:

  • rouge = erreur js générée

  • jaune = "faux" " valeur ( undefined , null , NaN , "quot ", "date non valide" )

  • vert = js Date () objet

  • vert clair = tout le reste

Lorsqu'un objet Date () est la sortie, je le convertis au format HH: mm sur 24 h pour faciliter la comparaison.

Après des tests approfondis et des recherches approfondies via mon autre réponse de compilation , j’ai conclu que la solution de @Dave Jarvis était la solution la plus proche de ce que j’imaginais être des résultats raisonnables et une gestion des cas délicats. À titre de référence, j’ai examiné la manière dont les entrées de temps de Google Agenda reformataient l’heure après avoir quitté la zone de texte.

Même quand même, j'ai constaté que Google Agenda ne traitait pas certains cas (bien que bizarres) que Google Agenda. Alors je l'ai retravaillé de fond en comble et c'est ce que j'ai proposé. Je l'ai également ajouté à ma réponse à la compilation .

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

J’estime que c’est le moyen le plus proche que je puisse obtenir pour mes besoins, mais les suggestions sont les bienvenues. Remarque: centrée sur les États-Unis dans la mesure où elle utilise par défaut am / pm pour certains modèles:

  • 1 = > 13:00 ( 13:00 )
  • 1100 = > 23:00 ( 23:00 )
  • 456 = > 16h56 ( 16h56 )
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top