Qual è il modo migliore per analizzare un orario in un oggetto Date dall'input dell'utente in Javascript?

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

Domanda

Sto lavorando a un widget modulo per consentire agli utenti di inserire un'ora del giorno in un input di testo (per un'applicazione di calendario). Usando JavaScript (stiamo usando jQuery FWIW), voglio trovare il modo migliore per analizzare il testo che l'utente inserisce in un oggetto JavaScript Date () in modo da poter facilmente eseguire confronti e altre cose su di esso .

Ho provato il metodo parse () ed è un po 'troppo esigente per le mie esigenze. Mi aspetto che sia in grado di analizzare correttamente i seguenti tempi di input di esempio (oltre ad altri formati di tempo logicamente simili) dello stesso oggetto Date () :

  • 13:00
  • 13:00
  • 1:00 p
  • 01:00
  • 01:00 p.m.
  • 1: 00p
  • 13:00
  • 13:00
  • 1 p
  • 01:00
  • 1 pm.
  • 1p
  • 13:00
  • 13

Sto pensando che potrei usare espressioni regolari per dividere l'input ed estrarre le informazioni che voglio usare per creare il mio oggetto Date () . Qual è il modo migliore per farlo?

È stato utile?

Soluzione

Una soluzione rapida che funziona sull'input specificato:

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

Dovrebbe funzionare anche per alcune altre varietà (anche se viene utilizzato a.m., funzionerà comunque, ad esempio). Ovviamente questo è piuttosto grezzo ma è anche piuttosto leggero (molto più economico da usare rispetto ad una libreria completa, per esempio).

  

Avviso: il codice non funziona con le 12:00, ecc.

Altri suggerimenti

Tutti gli esempi forniti non funzionano per periodi dalle 12:00 alle 00:59. Inoltre, generano un errore se il regex non corrisponde a un orario. Ciò che segue gestisce questo:

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

Funzionerà con le stringhe che contengono un tempo ovunque all'interno di esse. Quindi " abcde12: 00 pmdef" sarebbe analizzato e ritorno 12 pm. Se il risultato desiderato è che ritorni solo un tempo in cui la stringa contiene solo un tempo in essi, è possibile utilizzare la seguente espressione regolare a condizione che tu sostituisca "tempo [4]" con " time [6] " ;.

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

Non preoccuparti di farlo da solo, usa datejs .

La maggior parte delle soluzioni regex qui generano errori quando la stringa non può essere analizzata e non molte di esse spiegano stringhe come 1330 o 130pm . Anche se questi formati non sono stati specificati dal PO, li trovo fondamentali per l'analisi delle date inserite dagli umani.

Tutto ciò mi ha fatto pensare che l'uso di un'espressione regolare potrebbe non essere l'approccio migliore per questo.

La mia soluzione è una funzione che non solo analizza il tempo, ma consente anche di specificare un formato di output e un passaggio (intervallo) in cui arrotondare i minuti. A circa 70 linee, è ancora leggero e analizza tutti i formati di cui sopra e quelli senza due punti.

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

Ecco un miglioramento su Versione di Joe . Sentiti libero di modificarlo ulteriormente.

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

Modifiche:

  • Aggiunto il parametro radix alle chiamate parseInt () (quindi jslint non si lamenterà).
  • Ha reso il regex insensibile al maiuscolo così "2: 23 PM" funziona come " 14: 23 pm "

Mi sono imbattuto in un paio di nodi nell'implementazione della soluzione di John Resig. Ecco la funzione modificata che ho usato in base alla sua risposta:

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 può analizzare date / orari in molti formati diversi:

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

Il pacchetto time ha una dimensione di 0.9kbs. Disponibile con NPM e gestori pacchetti bower.

Ecco un esempio direttamente da 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

Questo è un approccio più solido che tiene conto del modo in cui gli utenti intendono utilizzare questo tipo di input. Ad esempio, se un utente immette "12", si aspetterebbe che siano le 12:00 (mezzogiorno) e non le 12:00. La funzione seguente gestisce tutto questo. È disponibile anche qui: 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() );
}

Questo è un prototipo di stringa, quindi puoi usarlo in questo modo:

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

Ecco una soluzione in più per tutti coloro che utilizzano un orologio 24h che supporta:

  • 0820 - > 08:20
  • 32 - > 03:02
  • 124 - > 00: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]) );
}

Ho apportato alcune modifiche alla funzione sopra per supportare alcuni altri formati.

  • 1400 - > 14:00
  • 1.30 - > 13:30
  • 1: 30a - > 1:30 AM
  • 100 - > 1:00 AM

Non l'ho ancora pulito, ma funziona per tutto ciò che mi viene in mente.

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

Ecco un altro approccio che copre la risposta originale, un numero ragionevole di cifre, l'inserimento di dati da parte di gatti e errori logici. L'algoritmo segue:

  1. Determina se meridiano è post meridiem .
  2. Converti le cifre di input in un valore intero.
  3. Tempo tra 0 e 24: l'ora è l'ora, nessun minuto (ore 12 è PM).
  4. Tempo compreso tra 100 e 2359: ore div 100 sono le ore, minuti mod 100 resto.
  5. Tempo dalle 2400 in poi: le ore sono la mezzanotte, con il resto dei minuti.
  6. Quando le ore superano 12, sottrarre 12 e forzare post meridiem true.
  7. Quando i minuti superano 59, forza su 59.

La conversione di ore, minuti e post meridiem in un oggetto Date è un esercizio per il lettore (numerose altre risposte mostrano come farlo).

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

Con jQuery, il prototipo String appena definito viene utilizzato come segue:

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

Perché non usare la convalida per restringere ciò che un utente può inserire e semplificare l'elenco per includere solo i formati che possono essere analizzati (o analizzati dopo alcune modifiche).

Non credo che richieda troppo per richiedere a un utente di impostare un orario in un formato supportato.

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

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

grazie

Molte risposte, quindi un'altra non farà male.

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

Per essere adeguatamente robusto, dovrebbe verificare che ciascun valore rientri nell'intervallo dei valori consentiti, ad esempio se le ore am / pm devono essere comprese tra 1 e 12, altrimenti tra 0 e 24 incluso, ecc.

Un miglioramento alla soluzione di Patrick McElhaney (la sua non gestisce correttamente le 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]) );
}

Non ero contento delle altre risposte, quindi ne ho fatta ancora un'altra. Questa versione:

  • Riconosce secondi e millisecondi
  • Restituisce undefined su input non validi come " 13: 00 pm" o "11: 65"
  • Restituisce l'ora locale se si fornisce un parametro localDate , altrimenti restituisce un'ora UTC in epoca Unix (1 gennaio 1970).
  • Supporta tempo militare come 1330 (per disabilitare, fai il primo ':' richiesto nella regex)
  • Permette un'ora da sola, con 24 ore (ovvero "7" significa 7am).
  • Consente l'ora 24 come sinonimo dell'ora 0, ma l'ora 25 non è consentita.
  • Richiede che l'ora sia all'inizio della stringa (per disabilitare, rimuovere ^ \ s * nella regex)
  • Ha un codice di prova che rileva effettivamente quando l'output è errato.

Modifica: ora è un pacchetto che include un formattatore timeToString : npm i semplicertime


/**
 * 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.');

Se vuoi solo secondi ecco una riga

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

Tabella di compilazione di altre risposte

Prima di tutto, non riesco a credere che non esiste una funzionalità integrata o una solida libreria di terze parti in grado di gestirlo. In realtà, è lo sviluppo web quindi posso crederci.

Cercare di testare tutti i casi limite con tutti questi diversi algoritmi mi ha fatto girare la testa, quindi mi sono preso la libertà di compilare tutte le risposte e i test in questo thread in una tabella pratica.

Il codice (e la tabella risultante) è inutilmente grande per includere inline, quindi ho creato 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

Sentiti libero di rovesciare il mio violino e aggiungere altri algoritmi e casi di test

Non ho aggiunto alcun confronto tra il risultato e il "previsto" output, perché ci sono casi in cui il "previsto" l'output potrebbe essere discusso (ad esempio, 12 deve essere interpretato come 12:00 am o 12:00 pm ?). Dovrai esaminare la tabella e vedere quale algoritmo ha più senso per te.

Nota: i colori non indicano necessariamente qualità o "attesa" " di output, indicano solo il tipo di output:

  • rosso = errore js generato

  • giallo = " falsy " valore ( undefined , null , NaN , " " , " data non valida " )

  • green = js Date () object

  • verde chiaro = tutto il resto

Laddove un oggetto Date () sia l'output, lo converto in formato 24 ore HH: mm per facilitare il confronto.

Dopo aver accuratamente testato e investigato attraverso la mia altra risposta di compilazione , ho concluso che la soluzione di @Dave Jarvis era la più vicina a ciò che pensavo fossero output ragionevoli e gestione dei casi limite. Per riferimento, ho esaminato quali input di Google Calendar hanno riformattato l'ora dopo essere usciti dalla casella di testo.

Anche ancora, ho visto che non gestiva alcuni (anche se strani) casi limite di Google Calendar. Quindi l'ho rielaborato da zero e questo è quello che mi è venuto in mente. L'ho anche aggiunto a la mia risposta alla 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;
}

Sento che questo è il più vicino che posso ottenere per le mie esigenze, ma i suggerimenti sono ben accetti. Nota: è incentrato sull'americano in quanto per impostazione predefinita è am / pm per determinati schemi:

  • 1 = > 13:00 ( 13:00 )
  • 1100 = > 23:00 ( 23:00 )
  • 456 = > 16:56 ( 16:56 )
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top