Analisar string para DateTime em C #

165

Eu tenho data e hora em uma string formatada como essa:

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

Como posso analisá-lo System.DateTime?

Desejo usar funções como DateTime.Parse()ou, DateTime.ParseExact()se possível, para poder especificar o formato da data manualmente.

Hooch
fonte
19
Então, por que você não usa DateTime.Parse?
Austin Salonen
8
Eu fui um dos que recusaram. Isso ocorreu porque sua pergunta original ( stackoverflow.com/revisions/… ) declarou que você QUERIA usar DateTime.Parse (), mas não indicou POR QUE não poderia usá-lo. Isso fez com que parecesse uma pergunta sem sentido, especialmente porque uma verificação simples deixaria claro que o cacois estava correto: sua string "2011-03-21 13:26" não é um problema para DateTime.Parse (). Por fim, você não fez nenhuma menção ao ParseExact () na sua pergunta original. Você esperou até depois da resposta de Mitch adicionar este em uma edição.
anon
4
Eu simplesmente amo essas pessoas que votam sem votar, sem dar nenhum motivo nos comentários.
21415 Hooch

Respostas:

271

DateTime.Parse()tentará descobrir o formato da data especificada e geralmente faz um bom trabalho. Se você pode garantir que as datas sempre estejam em um determinado formato, poderá usar ParseExact():

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

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

(Mas observe que geralmente é mais seguro usar um dos métodos TryParse, caso uma data não esteja no formato esperado)

Certifique-se de verificar seqüências de caracteres de formato de data e hora personalizadas ao construir uma sequência de formato, especialmente preste atenção ao número de letras e letras maiúsculas e minúsculas (por exemplo, "MM" e "mm" significam coisas muito diferentes).

Outro recurso útil para seqüências de caracteres de formato C # é a Formatação de String em C #

Mitch Wheat
fonte
5
Correção - é SEMPRE mais seguro;) Se você estiver chamando um método com uma exceção, sempre verifique primeiro a condição da exceção, se possível.
Gusdor
3
Eu diria que é mais seguro sempre passar na sua cultura. Prefiro ter uma exceção do que "01-02-2013" ser mal interpretado como o segundo de janeiro ou o primeiro de fevereiro.
Carra
1
@Carra: datas no formato ISO8601 (isto é aaaa-mm-dd '. São sempre interpretadas da maneira correta Isso, por isso usamos ISO8601 formatar datas ...
Mitch Wheat
A análise exata pode ser útil. Às vezes, eu prefiro a falha do aplicativo e o computador acender, ao invés de produzir uma saída incorreta. Depende do aplicativo.
Allen
ParseExact é excelente porque é flexível, mas tem uma desvantagem: Observe que os métodos ParseExact e Parse lançam exceções se houver um erro de sintaxe no formato de variável da data s. Portanto, é melhor usar o TryParseExcact. Eu indiquei o porquê na minha resposta abaixo.
Matt
47

Como explicarei mais adiante, sempre favoreceria os métodos TryParsee TryParseExact. Como eles são um pouco volumosos de usar, escrevi um método de extensão que facilita muito a análise:

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

Diferente de etc. Parse, ParseExactele não gera uma exceção e permite que você verifique via

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

se a conversão foi bem-sucedida (neste caso, dttem um valor que você pode acessar via dt.Value) ou não (nesse caso, é null).

Isso ainda permite usar atalhos elegantes como o operador "Elvis" ?., por exemplo:

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

Aqui você também pode usar year.HasValuepara verificar se a conversão foi bem-sucedida e, se não for bem-sucedida, yearconterá null, caso contrário, a parte do ano da data. Não há exceção lançada se a conversão falhar.


Solução:   O método de extensão .ToDate ()

Experimente .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;
    }
}

Algumas informações sobre o código

Você pode se perguntar por que usei a InvariantCulturechamada TryParseExact: Isso é forçar a função a tratar os padrões de formato sempre da mesma maneira (caso contrário, por exemplo "." Pode ser interpretado como separador decimal em inglês enquanto é um separador de grupo ou um separador de data em Alemão). Lembre-se de que já consultamos as strings de formato baseadas em cultura algumas linhas antes, então tudo bem aqui.

Atualização: .ToDate() (sem parâmetros) agora assume como padrão todos os padrões de data / hora comuns da cultura atual do encadeamento.
Observe que precisamos do resulte dtjuntos, porque TryParseExactnão permite o uso DateTime?, que pretendemos retornar. No C # versão 7, você poderia simplificar a ToDatefunção um pouco da seguinte maneira:

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

ou, se você gosta ainda mais:

 // 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?;

nesse caso, você não precisa das duas declarações DateTime? result = null;e DateTime dt;pode fazê-lo em uma linha de código. (Também seria permitido escrever em out DateTime dtvez de, out var dtse você preferir).

Eu simplificaram o código ainda mais usando a paramspalavra-chave: Agora você não precisa do 2 nd método sobrecarregado mais.


Exemplo 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 você pode ver, este exemplo apenas consulta dt.HasValuepara ver se a conversão foi bem-sucedida ou não. Como um bônus extra, o TryParseExact permite especificar strict DateTimeStylespara que você saiba exatamente se uma sequência de data / hora adequada foi passada ou não.


Mais exemplos de uso

A função sobrecarregada permite passar uma matriz de formatos válidos usados ​​para analisar / converter datas, como mostrado aqui também ( TryParseExactsuporta diretamente isso), por exemplo

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

Se você tiver apenas alguns padrões de modelo, também poderá escrever:

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

Exemplos avançados

Você pode usar o ??operador para usar como padrão um formato à prova de falhas, por exemplo

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

Nesse caso, o .ToDate()usaria formatos comuns de data da cultura local e, se tudo isso falhar, tentaria usar o formato padrão ISO"yyyy-MM-dd HH:mm:ss" como substituto. Dessa forma, a função de extensão permite "encadear" diferentes formatos de fallback facilmente.

Você pode até usar a extensão no LINQ, tente isso (está no .NetFiddle acima):

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

que converterá as datas na matriz rapidamente, usando os padrões e despejando-os no console.


Alguns antecedentes sobre o TryParseExact

Finalmente, aqui estão alguns comentários sobre o plano de fundo (ou seja, a razão pela qual escrevi dessa maneira):

Estou preferindo o TryParseExact neste método de extensão, porque você evita o tratamento de exceções - você pode ler no artigo de Eric Lippert sobre exceções por que você deve usar o TryParse em vez de Parse, cito-o sobre esse tópico: 2)

Essa infeliz decisão de design 1) [anotação: deixar o método Parse lançar uma exceção] foi tão irritante que, é claro, a equipe de estruturas implementou o TryParse logo em seguida, o que faz a coisa certa.

Sim, mas TryParsee TryParseExactambos ainda são muito menos confortáveis ​​de usar: Eles forçam você a usar uma variável não inicializada como um outparâmetro que não deve ser anulável e, enquanto você está convertendo, precisa avaliar o valor de retorno booleano - ou você para usar uma ifinstrução imediatamente ou você deve armazenar o valor de retorno em uma variável booleana adicional para poder fazer a verificação posteriormente. E você não pode simplesmente usar a variável de destino sem saber se a conversão foi bem-sucedida ou não.

Na maioria dos casos, você só quer saber se a conversão foi bem-sucedida ou não (e, é claro, o valor, se foi bem-sucedida) ; portanto, uma variável de destino anulável que mantém todas as informações seria desejável e muito mais elegante - porque toda a informação é apenas armazenado em um local: isso é consistente, fácil de usar e muito menos propenso a erros.

O método de extensão que escrevi faz exatamente isso (também mostra que tipo de código você precisaria escrever toda vez que não quiser usá-lo).

Acredito que o benefício .ToDate(strDateFormat)é que parece simples e limpo - tão simples quanto o original DateTime.Parsedeveria ser -, mas com a capacidade de verificar se a conversão foi bem-sucedida e sem gerar exceções.


1) O que se entende aqui é que o tratamento de exceções (ou seja, um try { ... } catch(Exception ex) { ...}bloco) - necessário quando você estiver usando o Parse, porque emitirá uma exceção se uma sequência inválida for analisada - não é apenas desnecessário nesse caso, mas também irritante e complicando seu código. O TryParse evita tudo isso, pois o exemplo de código que forneci está sendo exibido.


2) Eric Lippert é um famoso colega do StackOverflow e trabalha na Microsoft como desenvolvedor principal na equipe do compilador C # há alguns anos.

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

Confira este link para outras strings de formato!

Rob
fonte
4

Coloque o valor de uma sequência legível por humanos em um .NET DateTime com código como este:

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

A resposta simples e direta ->

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
fonte
Bom @Shivam Bharadwaj, eu fiz da mesma maneira
Muhammad Irfan
2

Você também pode usar XmlConvert.ToDateString

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

É bom especificar o tipo de data, o código é:

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

Mais detalhes sobre as diferentes opções de análise http://amir-shenodua.blogspot.ie/2017/06/datetime-parsing-in-net.html

Amir Shenouda
fonte
0

Experimente o seguinte 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
fonte
Olá, Bem-vindo, forneça uma explicação ao responder a uma pergunta. Apenas postar código não é recomendado
Ali