Analizar cadena a DateTime en C #

166

Tengo fecha y hora en una cadena formateada como esa:

"2011-03-21 13:26" //year-month-day hour:minute

¿Cómo puedo analizarlo System.DateTime?

Quiero usar funciones como DateTime.Parse()o DateTime.ParseExact()si es posible, para poder especificar el formato de la fecha manualmente.

Bebida alcohólica
fuente
19
Entonces, ¿por qué no usas DateTime.Parse?
Austin Salonen
8
Yo fui uno de los votantes negativos. Fue porque su pregunta original ( stackoverflow.com/revisions/… ) declaró que DESEA usar DateTime.Parse () pero no dijo POR QUÉ no podría usarlo. Esto hizo que pareciera una pregunta sin sentido, especialmente dado que una simple comprobación habría dejado en claro que cacois era correcta: su cadena "2011-03-21 13:26" no es un problema para DateTime.Parse (). Finalmente, no mencionó ParseExact () en su pregunta original. Esperó hasta después de la respuesta de Mitch para agregar esto en una edición.
anon
44
Me encanta esa pregunta de votación negativa de las personas sin dar ninguna razón en los comentarios.
Hooch

Respuestas:

272

DateTime.Parse()intentará averiguar el formato de la fecha dada, y generalmente hace un buen trabajo. Si puede garantizar que las fechas siempre estarán en un formato determinado, puede usar ParseExact():

string s = "2011-03-21 13:26";

DateTime dt = 
    DateTime.ParseExact(s, "yyyy-MM-dd HH:mm", CultureInfo.InvariantCulture);

(Pero tenga en cuenta que generalmente es más seguro usar uno de los métodos TryParse en caso de que una fecha no esté en el formato esperado)

Asegúrese de marcar Cadenas de formato de fecha y hora personalizadas al construir cadenas de formato, especialmente preste atención al número de letras y mayúsculas y minúsculas (es decir, "MM" y "mm" significan cosas muy diferentes).

Otro recurso útil para las cadenas de formato C # es el formato de cadenas en C #

Trigo Mitch
fuente
55
Corrección: SIEMPRE es más seguro;) Si llama a un método con una excepción, compruebe siempre la condición de excepción primero si es posible.
Gusdor
3
Yo diría que es más seguro pasar siempre tu cultura. Prefiero tener una excepción que tener "01-02-2013" malinterpretado como el dos de enero o el primero de febrero.
Carra
1
@Carra: las fechas en formato ISO8601 (es decir, aaaa-mm-dd 'siempre se interpretan de la manera correcta. Es por eso que usamos fechas en formato ISO8601 ...
Mitch Wheat
Parse exacto puede ser útil. A veces, preferiría que mi aplicación se bloquee y que mi computadora se prenda fuego, en lugar de producir una salida incorrecta. Depende de la aplicación.
Allen
ParseExact es excelente porque es flexible, pero tiene un inconveniente: tenga en cuenta que los métodos ParseExact y Parse arrojan excepciones si hay un error de sintaxis en el formato de fecha de la variable s. Por lo tanto, es mejor usar TryParseExcact. He señalado por qué en mi respuesta a continuación.
Matt
47

Como explicaré más adelante, siempre favorecería los métodos TryParsey TryParseExact. Debido a que son un poco voluminosos de usar, he escrito un método de extensión que facilita mucho el análisis:

var    dtStr = "2011-03-21 13:26";
DateTime? dt = dtStr.ToDate("yyyy-MM-dd HH:mm");

A diferencia Parse, ParseExactetc., no arroja una excepción y le permite verificar a través de

if (dt.HasValue) { // continue processing } else { // do error handling }

si la conversión fue exitosa (en este caso dttiene un valor al que puede acceder dt.Value) o no (en este caso, lo es null).

Eso incluso permite usar atajos elegantes como el operador "Elvis" ?., por ejemplo:

int? year = dtStr?.ToDate("yyyy-MM-dd HH:mm")?.Year;

Aquí también puede usar year.HasValuepara verificar si la conversión tuvo éxito, y si no tuvo éxito year, contendrá null, de lo contrario, la parte del año de la fecha. No se produce ninguna excepción si la conversión falla.


Solución: el   método de extensión .ToDate ()

Pruébalo en .NetFiddle

public static class Extensions
{
    // Extension method parsing a date string to a DateTime?
    // dateFmt is optional and allows to pass a parsing pattern array
    // or one or more patterns passed as string parameters
    public static DateTime? ToDate(this string dateTimeStr, params string[] dateFmt)
    {
      // example: var dt = "2011-03-21 13:26".ToDate(new string[]{"yyyy-MM-dd HH:mm", 
      //                                                  "M/d/yyyy h:mm:ss tt"});
      // or simpler: 
      // var dt = "2011-03-21 13:26".ToDate("yyyy-MM-dd HH:mm", "M/d/yyyy h:mm:ss tt");
      const DateTimeStyles style = DateTimeStyles.AllowWhiteSpaces;
      if (dateFmt == null)
      {
        var dateInfo = System.Threading.Thread.CurrentThread.CurrentCulture.DateTimeFormat;
        dateFmt=dateInfo.GetAllDateTimePatterns();
      }
      // Commented out below because it can be done shorter as shown below.
      // For older C# versions (older than C#7) you need it like that:
      // DateTime? result = null;
      // DateTime dt;
      // if (DateTime.TryParseExact(dateTimeStr, dateFmt,
      //    CultureInfo.InvariantCulture, style, out dt)) result = dt;
      // In C#7 and above, we can simply write:
      var result = DateTime.TryParseExact(dateTimeStr, dateFmt, CultureInfo.InvariantCulture,
                   style, out var dt) ? dt : null as DateTime?;
      return result;
    }
}

Alguna información sobre el código

Quizás se pregunte por qué he usado InvariantCulturellamar TryParseExact: Esto es para forzar a la función a tratar patrones de formato siempre de la misma manera (de lo contrario, por ejemplo, "." Podría interpretarse como separador decimal en inglés mientras es un separador de grupo o un separador de fecha en Alemán). Recordemos que ya hemos consultado las cadenas de formato basadas en la cultura unas pocas líneas antes, así que está bien aquí.

Actualización: .ToDate() (sin parámetros) ahora se predetermina a todos los patrones de fecha / hora comunes de la cultura actual del hilo.
Tenga en cuenta que necesitamos el resulty dtjuntos, porque TryParseExactno permite el uso DateTime?, que tenemos la intención de devolver. En C # Versión 7 , podría simplificar la ToDatefunción un poco de la siguiente manera:

 // in C#7 only: "DateTime dt;" - no longer required, declare implicitly
 if (DateTime.TryParseExact(dateTimeStr, dateFmt,
     CultureInfo.InvariantCulture, style, out var dt)) result = dt;

o, si te gusta aún más corto:

 // in C#7 only: Declaration of result as a "one-liner" ;-)
 var result = DateTime.TryParseExact(dateTimeStr, dateFmt, CultureInfo.InvariantCulture,
              style, out var dt) ? dt : null as DateTime?;

en cuyo caso no necesita las dos declaraciones DateTime? result = null;y, DateTime dt;en absoluto, puede hacerlo en una línea de código. (También se le permitiría escribir en out DateTime dtlugar de out var dtsi lo prefiere).

He simplificado el código aún más mediante el uso de la paramspalabra clave: Ahora usted no necesita el 2 nd método sobrecargado más.


Ejemplo de uso

var dtStr="2011-03-21 13:26";    
var dt=dtStr.ToDate("yyyy-MM-dd HH:mm");
if (dt.HasValue)
{
    Console.WriteLine("Successful!");
    // ... dt.Value now contains the converted DateTime ...
}
else
{
    Console.WriteLine("Invalid date format!");
}

Como puede ver, este ejemplo solo consulta dt.HasValuepara ver si la conversión fue exitosa o no. Como una ventaja adicional, TryParseExact permite especificar estricto DateTimeStylespara que sepa exactamente si se ha pasado o no una cadena de fecha / hora adecuada.


Más ejemplos de uso

La función sobrecargada le permite pasar una variedad de formatos válidos utilizados para analizar / convertir fechas como se muestra aquí también ( TryParseExactdirectamente lo admite), por ejemplo

string[] dateFmt = {"M/d/yyyy h:mm:ss tt", "M/d/yyyy h:mm tt", 
                     "MM/dd/yyyy hh:mm:ss", "M/d/yyyy h:mm:ss", 
                     "M/d/yyyy hh:mm tt", "M/d/yyyy hh tt", 
                     "M/d/yyyy h:mm", "M/d/yyyy h:mm", 
                     "MM/dd/yyyy hh:mm", "M/dd/yyyy hh:mm"};
var dtStr="5/1/2009 6:32 PM"; 
var dt=dtStr.ToDate(dateFmt);

Si solo tiene unos pocos patrones de plantilla, también puede escribir:

var dateStr = "2011-03-21 13:26";
var dt = dateStr.ToDate("yyyy-MM-dd HH:mm", "M/d/yyyy h:mm:ss tt");

Ejemplos avanzados

Puede usar el ??operador por defecto a un formato a prueba de fallas, por ej.

var dtStr = "2017-12-30 11:37:00";
var dt = (dtStr.ToDate()) ?? dtStr.ToDate("yyyy-MM-dd HH:mm:ss");

En este caso, .ToDate()usaría formatos comunes de fecha de cultura local, y si todos estos fallaran, trataría de usar el formato estándar ISO"yyyy-MM-dd HH:mm:ss" como alternativa. De esta forma, la función de extensión permite "encadenar" diferentes formatos de reserva fácilmente.

Incluso puede usar la extensión en LINQ, pruebe esto (está en el .NetFiddle anterior):

var patterns=new[] { "dd-MM-yyyy", "dd.MM.yyyy" };
(new[] { "15-01-2019", "15.01.2019" }).Select(s => s.ToDate(patterns)).Dump(); 

que convertirá las fechas en la matriz sobre la marcha utilizando los patrones y volcarlos a la consola.


Algunos antecedentes sobre TryParseExact

Finalmente, aquí hay algunos comentarios sobre el fondo (es decir, la razón por la que lo he escrito de esta manera):

Estoy prefiriendo TryParseExact en este método de extensión, ya que evita el manejo de excepciones - se puede leer en el artículo de Eric Lippert acerca de las excepciones por las que debe utilizar TryParse en lugar de Parse, lo cito de ese tema: 2)

Esta desafortunada decisión de diseño 1) [anotación: dejar que el método Parse arroje una excepción] fue tan irritante que, por supuesto, el equipo de marcos implementó TryParse poco después, lo que hace lo correcto.

Lo hace, pero TryParsey TryParseExactambos son todavía mucho menos cómodo de usar: Te obligan a utilizar una variable no inicializada como un outparámetro que no debe ser anulable y al mismo tiempo va a convertir es necesario evaluar el valor de retorno booleano - ya sea que usted tiene para usar una ifinstrucción de inmediato o tiene que almacenar el valor de retorno en una variable booleana adicional para que pueda hacer la verificación más tarde. Y no puede usar la variable de destino sin saber si la conversión fue exitosa o no.

En la mayoría de los casos, solo desea saber si la conversión fue exitosa o no (y, por supuesto, el valor si fue exitoso) , por lo que sería deseable y mucho más elegante una variable objetivo anulable que mantenga toda la información, porque toda la información es solo almacenado en un solo lugar: eso es consistente y fácil de usar, y mucho menos propenso a errores.

El método de extensión que he escrito hace exactamente eso (también le muestra qué tipo de código tendría que escribir cada vez si no lo va a usar).

Creo que el beneficio .ToDate(strDateFormat)es que se ve simple y limpio, tan simple como DateTime.Parsese suponía que era el original , pero con la capacidad de verificar si la conversión fue exitosa y sin lanzar excepciones.


1) Lo que se quiere decir aquí es que el manejo de excepciones (es decir, un try { ... } catch(Exception ex) { ...}bloque), que es necesario cuando se utiliza Parse porque generará una excepción si se analiza una cadena no válida, no solo es innecesario en este caso, sino también molesto, y complicando tu código. TryParse evita todo esto como se muestra en el ejemplo de código que he proporcionado.


2) Eric Lippert es un famoso compañero de StackOverflow y estuvo trabajando en Microsoft como desarrollador principal en el equipo compilador de C # durante un par de años.

Mate
fuente
13
var dateStr = @"2011-03-21 13:26";
var dateTime = DateTime.ParseExact(dateStr, "yyyy-MM-dd HH:mm", CultureInfo.CurrentCulture);

¡Mira este enlace para otras cadenas de formato!

Rob
fuente
4

Ponga el valor de una cadena legible para humanos en .NET DateTime con un código como este:

DateTime.ParseExact("April 16, 2011 4:27 pm", "MMMM d, yyyy h:mm tt", null);
Zack Peterson
fuente
2

La respuesta simple y directa ->

using System;

namespace DemoApp.App

{
public class TestClassDate
{
    public static DateTime GetDate(string string_date)
    {
        DateTime dateValue;
        if (DateTime.TryParse(string_date, out dateValue))
            Console.WriteLine("Converted '{0}' to {1}.", string_date, dateValue);
        else
            Console.WriteLine("Unable to convert '{0}' to a date.", string_date);
        return dateValue;
    }
    public static void Main()
    {
        string inString = "05/01/2009 06:32:00";
        GetDate(inString);
    }
}
}

/**
 * Output:
 * Converted '05/01/2009 06:32:00' to 5/1/2009 6:32:00 AM.
 * */
Shivam Bharadwaj
fuente
Agradable @Shivam Bharadwaj, lo hice de la misma manera
Muhammad Irfan
2

También puede usar XmlConvert.ToDateString

var dateStr = "2011-03-21 13:26";
var parsedDate = XmlConvert.ToDateTime(dateStr, "yyyy-MM-dd hh:mm");

Es bueno especificar el tipo de fecha, el código es:

var anotherParsedDate = DateTime.ParseExact(dateStr, "yyyy-MM-dd hh:mm", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal);

Más detalles sobre las diferentes opciones de análisis http://amir-shenodua.blogspot.ie/2017/06/datetime-parsing-in-net.html

Amir Shenouda
fuente
0

Prueba el siguiente código

Month = Date = DateTime.Now.Month.ToString();   
Year = DateTime.Now.Year.ToString(); 
ViewBag.Today = System.Globalization.CultureInfo.InvariantCulture.DateTimeFormat.GetMonthName(Int32.Parse(Month)) + Year;
Adil Ayoub
fuente
Hola, bienvenido. Proporciona una explicación al responder una pregunta. No se recomienda simplemente publicar el código
Ali