Um tipo de data apenas em C # - por que não há tipo de data?

107

Em nosso projeto C #, temos a necessidade de representar uma data sem hora. Eu sei da existência do DateTime, no entanto, ele incorpora uma hora do dia também. Quero deixar explícito que certas variáveis ​​e argumentos de método são baseados em datas . Portanto, não posso usar a DateTime.Datepropriedade

Quais são as abordagens padrão para este problema? Certamente não sou o primeiro a encontrar isso? Por que não há Dateclasse em C #?

Alguém tem uma boa implementação usando uma estrutura e talvez alguns métodos de extensão em DateTime e talvez implementando alguns operadores como == e <,>?

Carlo V. Dango
fonte
1
Embora eu entenda que deseja uma semântica clara e explícita, quais problemas específicos isso DateTimecria?
Jeff Sternal,
15
1 Preciso lembrar de remover as horas no início do método. 2 não comunica bem que funciona apenas em datas. Isso é importante, por exemplo, ao armazenar e carregar do Db, onde um tipo estreito será suficiente. Programar é comunhão para pessoas, não computadores
Carlo V. Dango
5
Eu só queria dizer que a falta de uma aula para encontros É um grande problema e usar o DateTime não é nada bom. Assim que você armazena suas "datas" como data e hora, você fica refém dos problemas de economia diurna de local / fuso horário. Jogar fora a parte do tempo pode fazer com que todas as suas datas voltem um dia quando os relógios mudarem (!). E os usuários em fusos horários diferentes verão datas diferentes quando tentarem converter os horários de data. As datas são boas para representar momentos precisos no tempo (instantes de algum ponto ou o que for), mas são muito inadequadas para representar uma data abstrata.
TheMathemagician
2
Uma pergunta semelhante posterior stackoverflow.com/questions/7167710/… , e Jon Skeet diz que deve haver uma data.
goodeye
8
Um tipo de dados apenas de data é para DateTime, enquanto um tipo de dados inteiro é para um decimal. Aqueles que argumentam que não precisamos de uma data porque você pode simplesmente descartar a parte do tempo é o mesmo que dizer que não precisamos de números inteiros, pois podemos descartar a parte decimal. Nosso mundo tem um conceito de data que não inclui hora. 5 de março não é 5 de março 00:00:00.
Vago

Respostas:

55

Permita-me adicionar uma atualização a esta pergunta clássica:

  • A biblioteca Noda Time de Jon Skeet agora está bastante madura e tem um tipo apenas para data chamado LocalDate. (Local neste caso significa apenas local para alguém , não necessariamente local para o computador onde o código está sendo executado.)

  • Um tipo apenas de data chamado Dateé uma adição proposta ao .NET Core, por meio do projeto corefxlab . Você o encontrará no System.Timepacote, junto com um TimeOfDaytipo e vários métodos de extensão para os tipos existentes.

Estudei esse problema de maneira significativa, então também compartilharei vários motivos para a necessidade desses tipos:

  1. Há uma discrepância lógica entre um valor apenas para data e um valor para data à meia-noite.

    • Nem todo dia local tem meia-noite em todos os fusos horários. Exemplo: a transição do horário de verão na primavera para o futuro no Brasil move o relógio de 11:59:59 para 01:00:00.

    • Uma data e hora sempre se refere a uma hora específica dentro do dia, enquanto uma data apenas pode se referir ao início do dia, ao final do dia ou a todo o intervalo do dia.

  2. Anexar uma hora a uma data pode fazer com que a data mude conforme o valor é passado de um ambiente para outro, se os fusos horários não forem monitorados com muito cuidado. Isso normalmente ocorre em JavaScript (cujo Dateobjeto é realmente uma data + hora), mas pode acontecer facilmente em .NET também, ou na serialização conforme os dados são passados ​​entre JavaScript e .NET.

  3. Serializar um DateTimecom XML ou JSON (e outros) sempre incluirá a hora, mesmo que não seja importante. Isso é muito confuso, especialmente considerando coisas como datas de nascimento e aniversários, em que o tempo é irrelevante.

  4. Arquitetonicamente, DateTimeé um objeto-valor DDD , mas viola o Princípio da Responsabilidade Única de várias maneiras:

    • Ele é projetado como um tipo de data + hora, mas geralmente é usado apenas como data (ignorando a hora) ou apenas hora do dia (ignorando a data). ( TimeSpantambém é frequentemente usado para a hora do dia, mas isso é outro tópico.)

    • O DateTimeKindvalor anexado à .Kindpropriedade divide o tipo único em três. O Unspecifiedtipo é realmente a intenção original da estrutura e deve ser usado dessa forma. O Utctipo alinha o valor especificamente com UTC, e o Localtipo alinha o valor com o fuso horário local do ambiente.

      O problema de ter um sinalizador separado para o tipo é que toda vez que você consome um DateTime, você deve verificar .Kindpara decidir qual comportamento tomar. Todos os métodos do framework fazem isso, mas outros geralmente esquecem. Isso é realmente uma violação SRP, pois o tipo agora tem dois motivos diferentes para mudar (o valor e o tipo).

    • Os dois levam a usos de API que compilam, mas geralmente são absurdos ou têm casos extremos estranhos causados ​​por efeitos colaterais. Considerar:

      // nonsensical, caused by mixing types
      DateTime dt = DateTime.Today - TimeSpan.FromHours(3);  // when on today??
      
      // strange edge cases, caused by impact of Kind
      var london = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
      var paris = TimeZoneInfo.FindSystemTimeZoneById("Romance Standard Time");
      var dt = new DateTime(2016, 3, 27, 2, 0, 0);  // unspecified kind
      var delta = paris.GetUtcOffset(dt) - london.GetUtcOffset(dt);  // side effect!
      Console.WriteLine(delta.TotalHours); // 0, when should be 1 !!!

Em resumo, embora um DateTime possa ser usado apenas para uma data, ele só deve fazê-lo quando todos os lugares que o usam forem muito cuidadosos para ignorar a hora e também para não tentar converter de e para UTC ou outro fusos horários.

Matt Johnson-Pint
fonte
2
Se ao menos System.Time.Dateterminasse na estrutura .NET: /
Robert Jørgensgaard Engdahl
1
Você pode usar isso hoje, basta assinar o feed myget corefx, e você pode puxar System.Timecomo qualquer outro pacote. Só não é "oficial" ainda.
Matt Johnson-Pint
16

Eu suspeito que não haja nenhuma Dateclasse dedicada pura porque você já tem uma DateTimeque pode lidar com isso. Ter Datelevaria à duplicação e confusão.

Se você quiser a abordagem padrão, observe a DateTime.Datepropriedade que fornece apenas a parte da data de a DateTimecom o valor da hora definido para 12:00:00 meia-noite (00:00:00).

Robert MacLean
fonte
60
Uma grande vantagem de uma classe Date dedicada é que ela não sofre com as complexidades dos fusos horários e do horário de verão.
Dimitri C.
3
@DimitriC. Eu discordo - você pode usar DateTime com UTC e você não sofre dos problemas explicados, mais com um DateTime, mesmo se você quiser apenas datas, você ainda pode fazer cálculos que envolvem tempo (ou seja, me dê a data se eu subtrair 20 x 2 horas de hoje).
Robert MacLean
@Robert MacLean: Obrigado por enfatizar a conveniência de usar DateTimes UTC. Fiz alguns testes e parece que DateTimeKind.Unspecified atua como UTC em relação às subtrações. Então, de fato, se você for cuidadoso com o "tipo" de DateTimes com o qual está trabalhando, tudo sairá bem.
Dimitri C.
10
Ter que pensar sobre UTC e qualquer coisa a ver com fuso horário é apenas um desperdício de energia porque pode ser facilmente evitado por uma classe Date separada. E não vejo nenhuma confusão entre Date e DateTime.
maulik13
6
Concorde que C # realmente deve ter uma classe Date. A conversão de fuso horário não é apenas uma fonte constante de bugs submarinos, mas é simplesmente doloroso quando se lida com coisas que se baseiam em um dia útil, e não no tempo.
Julian Birch
12

Mandei um email para [email protected] e essa é a resposta

Marcos, este não é um bom lugar para fazer perguntas como essas. Tente http://stackoverflow.com A resposta curta é que você precisa de um modelo para representar um ponto no tempo, e DateTime faz isso, é o cenário mais útil na prática . O fato de os humanos usarem dois conceitos (data e hora) para marcar pontos no tempo é arbitrário e não é útil separar.

Apenas desacople onde for necessário, não faça as coisas apenas por fazer as coisas às cegas. Pense desta forma: qual problema você tem que é resolvido dividindo DateTime em Date e Time? E que problemas você terá que não tem agora? Dica: se você olhar os usos de DateTime na estrutura .NET: http://referencesource.microsoft.com/#mscorlib/system/datetime.cs#df6b1eba7461813b#references Você verá que a maioria está sendo retornada de um método. Se não tivéssemos um único conceito como DateTime, você teria que usar parâmetros out ou Tuplas para retornar um par de Date e Time.

HTH, Kirill Osenkov

No meu e-mail, eu questionava se era porque DateTime usa TimeZoneInfo para obter a hora da máquina - na propriedade Now. Eu diria que é porque "as regras de negócio" são "muito acopladas", eles me confirmaram.

MVCDS
fonte
Esta postagem realmente oferece ideias sobre os pensamentos por trás da decisão de design de não ter uma classe de data integrada. Qual foi a pergunta que você enviou a eles? Não quero dizer que concordo com essa decisão pelas mesmas razões que @TheMathemagician listou acima.
Robert Jørgensgaard Engdahl
@RobertJørgensgaardEngdahl, infelizmente, não tenho mais acesso a essa conta de e-mail. Mas creio ter perguntado a eles por que uniram Time e Date na estrutura DateTime. E embora eu concorde com o TheMathemagician, acho que a MS adotou essa abordagem de design porque, como uma empresa internacional, ela compensa suas necessidades - e agora é tarde para mudá-la - enquanto a divisão dos conceitos não.
MVCDS de
2
Talvez a MS pudesse ir até o fim e implementar uma SpaceTimeaula! Ei, de acordo com Einstein, espaço e tempo estão fortemente acoplados, então não devemos precisar diferenciá-los também, certo? (!!!!!!!!!!!) Eu estou meio novo para C #, mas eu tenho que dizer, é um campo minado vindo de VB.NET, onde é, simplesmente, date, Today(), now, etc. Não DateTimelixo prefixo, não sujando sobre. (E esses pontos-e-vírgulas e essa
diferenciação entre
2
E seu próprio SQL Server tem Datetipo e o resultado deve ser do tipo Date- se fosse o Datetipo resultado esperado como string sem tempo. Por exemplo, Delphi também tem Date como DateTime, mas digite informações diferentes para Date e DateTime.
user2091150
1
Kirill Osenkov está respondendo à pergunta "Por que não ter classes de data e hora separadas em relação à classe de data e hora ?". O real Q foi "Por que não também tem data separada e aulas em tempo?". Eu entendo que a Data e a Hora devem ser combinadas em uma Classe para os muitos casos de uso concedidos do conceito de data e hora . No entanto, provavelmente há pelo menos tantos, senão mais, casos de uso válidos de apenas um conceito de data . E, claro, também existem muitos casos de uso válidos de um conceito de tempo .
Tom
4

Se você precisar executar comparações de datas, use

yourdatetime.Date;

Se você estiver exibindo na tela, use

yourdatetime.ToShortDateString();
MattP
fonte
A parte .Date é o que eu estava procurando.
Brendan Vogt,
3

Permita-me especular: talvez seja porque até o SQL Server 2008 não havia tipo de dados Date no SQL, então seria difícil armazená-los no SQL server ?? E afinal é um produto Microsoft?

Pleun
fonte
Um datetime db é diferente de um datetime C #. Um datetime do banco de dados não tem um fuso horário, portanto, eles não se referem a um instante específico. Mas o C # sabe que o instante é e armazena os ticks desde a época UTC.
artsrc
2
a discussão é sobre a DATA dedicada, não tanto sobre a parte de data e hora, então não entendo o que você está tentando fazer.
Pleun
Isso não fornece uma resposta para a pergunta. Para criticar ou solicitar esclarecimentos de um autor, deixe um comentário abaixo de sua postagem.
Barranka de
@Barranka - A pergunta contém "Por que não há classe Date em C #?"
STLDev
2

Quem sabe porque é assim. Existem muitas decisões de design incorretas no .NET framework. No entanto, acho que este é um problema bem menor. Você sempre pode ignorar a parte da hora, portanto, mesmo se algum código decidir que DateTime se refira a mais do que apenas a data, o código que se preocupa deve olhar apenas para a parte da data. Alternativamente, você pode criar um novo tipo que representa apenas uma data e usar funções em DateTime para fazer o trabalho pesado (cálculos).

siride
fonte
1
Realmente não acho que foi uma má decisão, se você quer usar apenas um encontro ou não. Eu não vou votar contra você, mas essa é minha opinião.
JonH
Não acho que tenha formulado bem. Na verdade, eu não tenho muito problema com isso, por si só, embora eu pudesse ver como ter dois ou três tipos seria mais apropriado do ponto de vista de abstração / elegância. Meu ponto realmente é que há muitas coisas no .NET framework que podem deixar você coçando a cabeça e não vale a pena ficar muito chateado, especialmente considerando que este "problema" é bem menor em comparação com algumas decisões de design flagrantes (genérico restrições).
siride
+1 porque é verdade ... este era o único problema (ou o maior) do .NET :-) :-) Quantas versões do SQL Server eles precisavam para adicionar os tipos DATE e TIME? E lá estavam eles MUITO mais úteis (pelo menos por questões de integridade)
xanatos
Devo acrescentar também que acho que "tudo começa em -100 pontos" é uma boa maneira de fazer uma estrutura péssima e essa pode ser uma das coisas que ficaram presas nesse lixo.
siride
2
Acabei de ser mordido por esse problema porque 1 parte do código deixou de usar a propriedade .Date e, portanto, não comparou corretamente. Eu definitivamente acho que há necessidade de um tipo de data que não armazene qualquer hora, para evitar esse tipo de erro
JoelFan
2

Por quê? Podemos apenas especular e isso não ajuda muito a resolver problemas de engenharia. Um bom palpite é que DateTimecontém todas as funcionalidades que tal estrutura teria.

Se for realmente importante para você, apenas envolva DateTimeem sua própria estrutura imutável que apenas expõe a data (ou observe a DateTime.Datepropriedade).

Jason
fonte
2

Além da resposta de Robert, você também tem o DateTime.ToShortDateStringmétodo. Além disso, se você realmente deseja um objeto Date, pode sempre usar o padrão Adapter e envolver o objeto DateTime, expondo apenas o que deseja (ou seja, mês, dia, ano).

Matt
fonte
2

Sempre há a DateTime.Datepropriedade que corta a parte do tempo do DateTime. Talvez você possa encapsular ou envolver DateTime em seu próprio tipo de data.

E para a pergunta por que, bem, acho que você terá que perguntar a Anders Heljsberg.

Mikael Östberg
fonte
1

Porque para saber a data, você tem que saber a hora do sistema (em ticks), que inclui a hora - então por que jogar fora essa informação?

DateTimetem uma Datepropriedade se você não se importa nem um pouco com o tempo.

Maciço
fonte
1

Sim, também System.DateTime é selado. Já vi algumas pessoas brincar com isso criando uma classe personalizada apenas para obter o valor da string de tempo, conforme mencionado em postagens anteriores, coisas como:

class CustomDate
{
    public DateTime Date { get; set; }
    public bool IsTimeOnly { get; private set; }

    public CustomDate(bool isTimeOnly)
    {
        this.IsTimeOnly = isTimeOnly;
    }

    public string GetValue()
    {
        if (IsTimeOnly)
        {
            return Date.ToShortTimeString();
        }

        else
        {
            return Date.ToString();
        }
    }
}

Isso talvez seja desnecessário, já que você poderia facilmente extrair GetShortTimeString de um tipo DateTime antigo sem uma nova classe

Ta01
fonte
0

Se você usar as propriedades Date ou Today para obter apenas a parte da data do objeto DateTime.

DateTime today = DateTime.Today;
DateTime yesterday = DateTime.Now.AddDays(-1).Date;

Em seguida, você obterá o componente de data apenas com o componente de hora definido para meia-noite.

eph_tagh
fonte
1
isso definitivamente não é o que eu queria
Carlo V. Dango
@Carlo V. Dango: Discordo. Acho que é exatamente o que você queria.
siride
1
@Carlo V. Dango: O que você está procurando fazer especificamente que essas propriedades não permitem?
eph_tagh
5
É muito fácil: uma área de cobertura da memória Date provavelmente seria apenas metade da área de cobertura da memória de um DateTime (32 em vez de 64 bits). Você teria certeza de que seu estúpido colega de trabalho não fez .AddHours (1) à sua data alterando-a, mas "mantendo o mesmo" do ponto de vista de "apenas data". Se (por um erro) o DateTime for definido como DateTimeKind.Local e a hora for normalizada para UTC, a data provavelmente mudará (aconteceu comigo através do uso de XmlSerialization e viagem de ida e volta mal feita para JSON) ... É o suficiente?
xanatos,