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 facilita mucho el análisis:
var dtStr = "2011-03-21 13:26";
DateTime? dt = dtStr.ToDate("yyyy-MM-dd HH:mm");
A diferencia Parse
, ParseExact
etc., 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 dt
tiene 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.HasValue
para 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 InvariantCulture
llamar 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 result
y dt
juntos, porque TryParseExact
no permite el uso DateTime?
, que tenemos la intención de devolver. En C # Versión 7 , podría simplificar la ToDate
funció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 dt
lugar de out var dt
si lo prefiere).
He simplificado el código aún más mediante el uso de la params
palabra 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.HasValue
para ver si la conversión fue exitosa o no. Como una ventaja adicional, TryParseExact permite especificar estricto DateTimeStyles
para 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 ( TryParseExact
directamente 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 TryParse
y TryParseExact
ambos son todavía mucho menos cómodo de usar: Te obligan a utilizar una variable no inicializada como un out
pará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 if
instrucció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.Parse
se 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.