Analizar cadena a DateTime en C#


Tengo fecha y hora en una cadena formateada como esa:

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

¿Cómo puedo analizarlo a System.DateTime?

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

Author: Matt, 2011-03-20

7 answers

DateTime.Parse() intentará averiguar el formato de la fecha dada, y por lo general hace un buen trabajo. Si puede garantizar que las fechas siempre estarán en un formato dado, 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 utilizar uno de los métodos TryParse en caso de que una fecha no esté en el formato esperado)

Asegúrese de verificar 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 (es decir, "MM" y " mm" significa cosas muy diferentes).

Otro recurso útil para cadenas de formato C# es Formato de cadena en C #

 201
Author: Mitch Wheat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-29 01:21:50

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

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

A diferencia de Parse, ParseExact etc. no lanza una excepción, y le permite comprobar a través de

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

Si la conversión fue exitosa (en este caso dt tiene un valor al que se puede acceder a través de dt.Value) o no (en este caso, es null).

Que 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.HasValue para verificar si la conversión tuvo éxito, y si no tuvo éxito, entonces year contendrá null, de lo contrario, la porción del año de la fecha. No hay excepción lanzada si la conversión falló.

Inténtalo .NetFiddle

public static class Extensions
{
    public static DateTime? toDate(this string dateTimeStr, 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"});
      const DateTimeStyles style = DateTimeStyles.AllowWhiteSpaces;
      if (dateFmt == null)
      {
        var dateInfo = System.Threading.Thread.CurrentThread.CurrentCulture.DateTimeFormat;
        dateFmt=dateInfo.GetAllDateTimePatterns();
      }
      DateTime? result = null;
      DateTime dt;
      if (DateTime.TryParseExact(dateTimeStr, dateFmt,
         CultureInfo.InvariantCulture, style, out dt)) result = dt;
      return result;
    }

    public static DateTime? toDate(this string dateTimeStr, string dateFmt=null)
    {
      // example:   var dt="2011-03-21 13:26".toDate("yyyy-MM-dd HH:mm");
      // or simply  var dt="2011-03-21 13:26".toDate();        
      // call overloaded function with string array param
      string[] dateFmtArr = dateFmt == null ? null : new string[] { dateFmt };
      return toDate(dateTimeStr, dateFmtArr);
    }
}

Te preguntarás, por qué he usado InvariantCulture llamando a TryParseExact: Esto es para forzar a la función a tratar el formato patrones siempre de la misma manera (de lo contrario, por ejemplo "."podría ser interpretado como separador decimal en inglés mientras que 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, por lo que está bien aquí.

Actualizar: .toDate() (sin parámetros) ahora usa por defecto todos los patrones de fecha/hora comunes de la cultura actual del hilo.
Note que necesitamos los result y dt juntos, porque TryParseExact no permite usar DateTime?, que pretendemos devolver. En C # Versión 7 podría simplificar un poco la función toDate 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;

(También se permitiría escribir out DateTime dt en lugar de out var dt.)

Ejemplo:

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.HasValue para ver si la conversión fue exitosa o no. Como un bono extra, TryParseExact permite especificar estricto DateTimeStyles para que sepa exactamente si un adecuado la cadena de fecha / hora se ha pasado o no.


N.B. La función sobrecargada le permite pasar una matriz de formatos válidos utilizados para analizar/convertir fechas como se muestra aquí también (TryParseExact soporta directamente esto), 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);

Sin embargo, para mantener el código corto, solo estoy usando la sobrecarga del arreglo de cadenas de TryParseExact, porque no funcionaba con un parámetro Genérico.

Ejemplo Avanzado:
Puede utilizar el ?? operador a un formato predeterminado a prueba de fallos, por ejemplo,

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

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


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, porque evita el manejo de excepciones - puede leer en el artículo de Eric Lippert sobre excepciones por qué debe usar TryParse en lugar de Analizar, lo cito sobre ese tema:2)

Esta desafortunada decisión de diseño1) [anotación: a let the Parse method throw an exception] fue tan irritante que, por supuesto el equipo de frameworks implementó TryParse en breve a partir de entonces que hace lo correcto.

Lo hace, pero TryParse y TryParseExact ambos son mucho menos cómodos de usar: te obligan a usar una variable no inicializada como un parámetro out que no debe ser nullable y mientras estás convirtiendo necesitas evaluar el valor de retorno booleano - o tienes que usar una instrucción ifinmediatamente o tienes que almacenar el valor de retorno en una variable booleana adicional para que puedas hacer la comprobación más tarde. Y no puedes simplemente use 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 exitosa), por lo que una variable de destino nullable que mantenga toda la información sería deseable y mucho más elegante, porque toda la información se almacena en un solo lugar: Eso es consistente y fácil de usar, y mucho menos propensa a errores.

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

Creo que el beneficio de .toDate(strDateFormat) es que se ve simple y limpio - tan simple como el original DateTime.Parse se suponía que debía ser - pero con la capacidad de comprobar si la conversión fue exitosa, y sin lanzar excepciones.


1) Lo que se quiere decir aquí es que manejo de excepciones (es decir, un try { ... } catch(Exception ex) { ...} bloque) - que es necesario cuando se está utilizando Parse porque lanzará una excepción si se analiza una cadena no válida-no solo es innecesario en este caso, sino también molesto, y complicando su código. TryParse evita todo esto como muestra 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 del compilador de C# durante un par de años.

 35
Author: Matt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-11 08:31:41
var dateStr = @"2011-03-21 13:26";
var dateTime = DateTime.ParseExact(dateStr, "yyyy-MM-dd HH:mm", CultureInfo.CurrentCulture);

Echa un vistazo a este enlace para otras cadenas de formato!

 13
Author: Rob,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-06-17 08:50:27

DateTime.Parse () debería funcionar bien para ese formato de cadena. Referencia:

Http://msdn.microsoft.com/en-us/library/1k1skd40.aspx#Y1240

¿Está lanzando una excepción de formato para usted?

 5
Author: cacois,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-03-20 02:09:18

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

DateTime.ParseExact("April 16, 2011 4:27 pm", "MMMM d, yyyy h:mm tt", null);
 4
Author: Zack Peterson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-04-30 01:05:58

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 diferentes opciones de análisis http://amir-shenodua.blogspot.ie/2017/06/datetime-parsing-in-net.html

 2
Author: Amir Shenouda,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-03 21:59:21

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.
 * */
 1
Author: Shivam Bharadwaj,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-10 06:33:59