Parse datetime in più formati

Ho creato un endpoint API. Il chiamante può chiamare l’API con il metodo POST che passa i parametri rilevanti. Nei parametri c’è un parametro che è del formato datetime .

Il problema è che quando si chiama questa API il chiamante può passare datetime in 3 formati diversi:

  1. long int – ad es. 1374755180
  2. Formato USA – ad es. “7/25/2013 6:37:31 PM” (come string )
  3. Formato del formato data e ora – ad es. “2013-07-25 14:26:00” (come string )

Devo analizzare il valore datetime e convertirlo in un DateTime o una string nel formato Timestamp.

Ho provato a utilizzare DateTime.TryParse() , DateTime.Parse() , Convert.ToDateTime() e Convert.ToDouble() ma nessuno di essi funziona in modo sicuro per me.

L’output richiesto deve essere in formato en-GB .

Modificare:

Avevo pensato di avere un blocco if-else if-else da utilizzare con TryParse 3 volte con un else per dire che la stringa non poteva essere analizzata. È questa la soluzione migliore? O ci sono soluzioni migliori di questa?

Per favore aiuto!

Dovresti considerare di richiedere un fuso orario. 1 non ne ha bisogno, ma # 2 e # 3 lo fanno.

 public DateTime ParseRequestDate() { // https://stackoverflow.com/questions/2883576/how-do-you-convert-epoch-time-in-c CultureInfo enUS = new CultureInfo("en-US"); var dt = "1374755180"; //var dt = "7/25/2013 6:37:31 PM"; //var dt = "2013-07-25 14:26:00"; DateTime dateValue; long dtLong; // Scenario #1 if (long.TryParse(dt, out dtLong)) return dtLong.FromUnixTime(); // Scenario #2 if (DateTime.TryParseExact(dt, "MM/dd/yyyy hh:mm:ss tt", enUS, DateTimeStyles.None, out dateValue)) return dateValue; // Scenario #3 if (DateTime.TryParseExact(dt, "yyyy-MM-dd hh:mm:ss", enUS, DateTimeStyles.None, out dateValue)) return dateValue; throw new SomeException("Don't know how to parse..."); } 

EDIT Come sottolinea Matt Johnson, DateTime.TryParseExact accetta una matrice di stringhe di formato. 2 e 3 potrebbero essere condensati.

 public DateTime ParseRequestDate() { // https://stackoverflow.com/questions/2883576/how-do-you-convert-epoch-time-in-c CultureInfo enUS = new CultureInfo("en-US"); var dt = "1374755180"; //var dt = "7/25/2013 6:37:31 PM"; //var dt = "2013-07-25 14:26:00"; DateTime dateValue; long dtLong; // Scenario #1 if (long.TryParse(dt, out dtLong)) return dtLong.FromUnixTime(); // Scenario #2 & #3 var formatStrings = new string[] { "MM/dd/yyyy hh:mm:ss tt", "yyyy-MM-dd hh:mm:ss" }; if (DateTime.TryParseExact(dt, formatStrings, enUS, DateTimeStyles.None, out dateValue)) return dateValue; throw new SomeException("Don't know how to parse..."); } 

L’epoca di conversione che ho preso in prestito da un’altra domanda. (Un metodo di estensione)

 public static class MyExtensions { public static DateTime FromUnixTime(this long unixTime) { var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); return epoch.AddSeconds(unixTime); } } 

Stai cercando DateTime.ParseExact ( articolo MSDN )

Che useresti in una situazione come questa:

 string[] formats= { "MM/dd/yyyy hh:mm:ss tt", "yyyy-MM-dd hh:mm:ss" } var dateTime = DateTime.ParseExact("7/25/2013 6:37:31 PM", formats, new CultureInfo("en-GB"), DateTimeStyles.None); 

Ciò consente di aggiungere tutti i formati DateTime array cui hai bisogno e il metodo eseguirà la conversione senza le istruzioni ifelse .

Se il tuo intero è in secondi da quando Unix Epoch hai aggiunto il numero di secondi al DateTime of the Epoch (01/01/1970) (.Net non ha un metodo fuori dalla scatola per questo, ma la logica è secondi ‘Epoca’):

 new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(seconds); 

Da questa domanda .

Un modo per affrontare questo problema sarebbe l’impostazione di un metodo factory che “comprenda” diversi formati e li analizzi di conseguenza.

È ansible creare una catena di ifthenelse per risolvere questo problema, ma è anche ansible eseguire un’implementazione “table-driven”: ciò di cui si ha bisogno è una serie di delegati che accettano una stringa e vi dicono due cose:

  • Se questo delegato può o meno analizzare la stringa in entrata, e
  • In caso affermativo, qual è il risultato di tale analisi, espressa come DateTime

Ecco un’implementazione di esempio:

 private static readonly DateParsers = new Func>[] { (s) => { long res; if (long.TryParse(s, out res)) { // The format was correct - make a DateTime, // and return true to indicate a successful parse return Tuple.Create(new DateTime(res), true); } else { // It does not matter what you put in the Item1 // when Item2 of the tuple is set to false return Tuple.Create(DateTime.MinValue, false); } } ... // Add similar delegates for other formats here }; 

Ora il tuo metodo factory potrebbe essere implementato come segue:

 private static bool TryParseMultiformat(string s, out DateTime res) { // Check all parsers in turn, looking for one returning success foreach (var p in DateParsers) { var tmp = p(s); if (tmp.Item2) { res = tmp.Item1; return true; } } res = DateTime.MinValue; return false; } 

Se i possibili formati sono corretti, puoi utilizzare TryParseExact

Una ansible soluzione è utilizzare TryParse , se non riesce ad ottenere la data corretta, quindi eseguire il fallback su formati noti e utilizzare TryPraseExact

Stavo affrontando lo stesso problema in un progetto in cui il mio codice verrà eseguito in ambienti diversi con vari formati di cultura.

Google mi ha mostrato questa perla nascosta . La funzione di supporto è indispensabile per analizzare automaticamente il datetime correttamente, indipendentemente dai formati di cultura

Esempi di utilizzo:

 string str = @"The last round was June 10, 2005; this time the unbroken record was held."; DateTimeRoutines.ParsedDateTime pdt; if (DateTimeRoutines.TryParseDate(str, DateTimeRoutines.DateTimeFormat.USA_DATE, out pdt)) Console.WriteLine("Date was found: " + pdt.DateTime.ToString()); 

Secondo l’autore, il codice è in grado di analizzare vari casi:

 @"Member since: 10-Feb-2008" @"Last Update: 18:16 11 Feb '08 " @"date Tue, Feb 10, 2008 at 11:06 AM" @"see at 12/31/2007 14:16:32" @"sack finish 14:16:32 November 15 2008, 1-144 app" @"Genesis Message - Wed 04 Feb 08 - 19:40" @"The day 07/31/07 14:16:32 is " @"Shipping is on us until December 24, 2008 within the US" @" 2008 within the US at 14:16:32" @"5th November, 1994, 8:15:30 pm" @"7 boxes January 31 , 14:16:32." @"the blue sky of Sept 30th 2008 14:16:32" @" eg 1997-07-16T19:20:30+01:00" @"Apr 1st, 2008 14:16:32 tufa 6767" @"wait for 07/31/07 14:16:32" @"later 12.31.08 and before 1.01.09" @"Expires: Sept 30th 2008 14:16:32" @"Offer expires Apr 1st, 2007, 14:16:32" @"Expires 14:16:32 January 31." @"Expires 14:16:32 January 31-st." @"Expires 23rd January 2010." @"Expires January 22nd, 2010." @"Expires DEC 22, 2010." 

Grazie per le tue risposte. Ho provato le opzioni suggerite in un paio di risposte e ho scoperto un approccio molto semplice che ha funzionato per me.

 public static bool ParseDate(string dateString, out DateTime dateValue) { long dtLong = 0L; bool result = false; if (long.TryParse(dateString, out dtLong)) { // I copied the epoch code here for simplicity dateValue = new DateTime(1970, 1, 1, 0, 0, 0).AddSeconds(dtLong); result = true; } // Working for US and Timestamp formats else if (DateTime.TryParse(dateString, out dateValue)) result = true; return result; } 

In precedenza stavo cercando di usare TryParse per tutti e 3 i formati che non funzionavano.

In qualche modo, TryParseExact non ha funzionato per il formato timestamp. Ha funzionato per il formato degli Stati Uniti. Questa è la ragione per cui ho dovuto scrivere la mia.

Se usi TryParseExact , solo GOD e gli sviluppatori Microsoft sanno quanti possibili formati di data e ora proveranno ad analizzare prima di abbandonare. Forse una soluzione migliore è usare una regex veloce e poi un parser appropriato. Ho cercato di rendere la regex il più semplice ansible, potreste doverla modificare leggermente

  private static readonly Regex R1 = new Regex(@"^\d+$", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline); private static readonly Regex R2 = new Regex(@"M$", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline); private static readonly Regex R3 = new Regex(@"^\d{4}-", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline); private static void Main(string[] args) { string[] stringDates = new[] { "1374755180", "2013-07-25 14:26:00", "7/25/2013 6:37:31 PM" }; foreach (var s in stringDates) { DateTime date = default(DateTime); if (R1.IsMatch(s)) date = new DateTime(long.Parse(s)); else if (R2.IsMatch(s)) date = DateTime.Parse(s); else if (R3.IsMatch(s)) date = DateTime.Parse(s); if (date != default(DateTime)) Console.WriteLine("{0}", date); } Console.WriteLine("Press ENTER to continue..."); Console.ReadLine(); }