Estou tão ridiculamente frustrado por ter que usar valores DateTime para conjuntos de dados que são realmente "apenas um dia". Os aniversários são o exemplo mais comum, mas isso aparece nos aplicativos de negócios o tempo todo.
Eu me acostumei apenas a definir a parte da Hora dos registros "somente para data" como "meio-dia" (que evita que a data seja sempre alterada, independentemente do fuso horário). Parece um hack, e estou sempre encontrando bugs de desenvolvedores juniores que tropeçam nessa questão.
O tempo é sempre relativo a um ponto fixo. 16:00 é de 4 horas após o meridiano, ou meio-dia. O ponto mais alto do sol em trânsito é observável e permite a criação de um sistema de coordenadas. 3 horas antes do meio dia (Ante Meridian), 2 horas depois do meio dia, 1441899402938 milissegundos desde 1º de janeiro de 1970. Para as pessoas criadas em um mundo cartesiano, isso é uma segunda natureza.
Mas nosso calendário é anterior a Descartes. Meu argumento é que é mais adequadamente pensado como uma enumeração sobre a qual uma função de módulo é aplicada. Segunda-feira segue domingo, e assim por diante até chegar ao fato de que domingo segue sábado. Não há positivo e negativo, é um módulo ou valor absoluto.
Da mesma forma com anos repetindo. A cada 365 dias (mais ou menos), existem vários dias especiais para mim: aniversários, aniversários, aniversários de crianças, etc. etc. nós PODEMOS mapear isso em um número de ponto flutuante e, na verdade, mapear esse número resolve muitos problemas que são realmente difíceis da maneira antiga, mas isso não significa que é a única maneira de fazê-lo.
A conscientização e o entendimento da natureza do "uso de DateTimes para armazenar datas" tornam o programador melhor na minha opinião.
Existe valor em um aplicativo explicitamente destinado a ser um aplicativo de agendamento na definição de uma classe Date ou é "definir todos os horários ao meio-dia" a melhor abordagem? Quais problemas podem ocorrer com o uso do DateTime e com a configuração do componente Hora como Meio-dia? A mudança de fuso horário pode ser considerada nessa abordagem? Eu usei o MomentJS, mas acho que essa é apenas uma classe Date melhor.
fonte
Respostas:
Antes de tudo, vamos tirar uma coisa do caminho: aniversários são uma coisa, datas de nascimento são outra. Um aniversário é um tipo de dados exótico porque não possui apenas os componentes de horas, minutos etc., mas também o componente do ano. Se você realmente deseja lidar com aniversários, recomendo inventar seu próprio tipo de dados, que contém apenas um número de mês e um número de dia, e não está relacionado a nenhum dos tipos de dados de data e hora incorporados.
Por outro lado, se você também deseja acompanhar o ano de nascimento, o que você tem não são os aniversários, são as datas de nascimento . Portanto, a pergunta agora é por que não há um tipo de dados somente para data, para que você possa representar convenientemente as datas de nascimento e, em vez disso, os idiomas populares parecem forçá-lo a usar algum tipo que também inclui um componente de tempo.
Permitam-me mencionar brevemente que não é verdade que todas as linguagens de programação oferecem apenas tipos de dados temporais que incluem um componente de tempo. Encontrei tipos de dados somente de data nos RDBMSes e em seus dialetos SQL correspondentes. Mas isso é irrelevante: o fato de esses tipos de dados existirem não significa que eles sejam bons, e os RDBMSs têm um longo histórico de confusão de armazenamento com representação.
Você entenderá por que é uma má idéia ter esses tipos de dados somente para data no momento em que você perceber que a hora é uma coordenada. A maioria das pessoas tem uma idéia muito vaga do que é o tempo, e essa idéia contém noções culturais misteriosas, como anos, meses e dias, sem perceber que essas noções são exclusivamente representativas : elas são úteis apenas para representar o tempo para um humano e para receber tempo como entrada de um ser humano. Em qualquer camada abaixo do controle da GUI real de entrada de tempo, o tempo deve ser, e geralmente é, representado como uma coordenada de tempo, que é um número único de unidades de tempo desde alguma origem.
Por exemplo, no
DateTime
tipo de dados do Microsoft Dotnet, a unidade de tempo é de 100 nanossegundos e a origem do tempo é 12:00 da meia-noite de 1º de janeiro de 0001 CEOutro exemplo de uma notação arcana, exclusivamente representacional, é a medição de ângulos usando graus, minutos de grau e segundos de grau. Obviamente, para obter qualquer cálculo útil, é necessário usar internamente radianos e, se necessário, converter de e para graus ao interagir com um usuário humano.
Portanto, não confunda a representação legível por humanos de uma medida com a natureza real da medida. Muitas vezes, o método ideal para realizar uma medida, que mais se aproxima da natureza da medida, é muito diferente da representação legível por humanos dessa medida.
À luz de tudo isso, sua solicitação de um tipo de dados temporais que representa apenas datas é semelhante a uma solicitação de um tipo de dados angular que seria capaz apenas de representar graus, impedindo explicitamente qualquer precisão maior. Esse tipo de dado seria bastante limitado e, em última análise, inútil, porque você teria que convertê-lo para e de radianos de qualquer maneira, a fim de obter algo útil com ele.
Seu problema com a data de nascimento é que você tem uma coordenada imprecisa da hora: a pessoa nasceu, é claro, em um momento específico, mas a hora e o minuto não foram registrados pelo hospital ou não se preocupam com eles. Então, o que realmente está acontecendo é que sua coordenada de data e hora do nascimento tem uma margem de erro, tolerância ou incerteza, se você desejar, e é melhor tratá-lo da seguinte maneira: coloque-o exatamente no meio do dia e considere uma incerteza implícita de 12 a 12 horas. E essa é precisamente a solução que você chegou intuitivamente.
fonte
Datas e horários são muitas coisas diferentes, dependendo do contexto, e você precisa de muitos tipos separados para cobrir todos os casos de uso.
O
DateTime
tipo presente em muitos idiomas representa um ponto preciso no tempo ("tempo instantâneo"). Além disso, temos vários conceitos relativos ou "humanos" de tempo e período de tempo, como dias de calendário, datas recorrentes, meses, anos etc. que são, em muitos casos, ambíguos e dependentes do contexto. Esses tipos não são tão universalmente úteis, mas necessários em domínios de aplicativos específicos, como calendários, ferramentas de agendamento e outros aplicativos que interagem com os conceitos humanos de tempo.Se você estiver escrevendo algo como um aplicativo de calendário, certamente se beneficiaria do uso de uma biblioteca como o Joda-time, que fornece um conjunto mais rico de tipos de tempo. Por exemplo
LocalDate
, uma data sem hora. Isso tem semântica diferente de uma comum,DateTime
com a parte do tempo definida como zero, poisDateTime
ainda indica um momento específico (meia-noite em um fuso horário específico), enquantoLocalDate
indica o dia inteiro e não está vinculado a um fuso horário específico. Isso também significa que você não pode traduzir diretamente um para o outro.LocalDate
é certamente mais simples do queDateTime
porque não precisa levar em consideração os fusos horários, mas você deve estar ciente dos outros problemas, por exemplo, que a data atual pode realmente retroceder quando você cruza um fuso horário e que o mesmo instante no tempo pode correspondem a datas diferentes em fusos horários diferentes. Se você estiver usando datas locais em aplicativos da Web ou em rede, tenha muito cuidado com esses problemas. Remover a parte do tempo de uma data não resolve o problema fundamental dos fusos horários! E se você considerar datas históricas e culturas diferentes, fica ainda mais complicado, pois a mesma data pode corresponder a instâncias totalmente diferentes no tempo, por exemplo, o calendário juliano versus o gregoriano.Agora você pergunta por que os idiomas não têm algo parecido com o
LocalDate
incorporado . Bem, primeiro, algumas linguagens como SQL e Visual Basic têm um tipo de data sem parte do tempo. E o Java também adicionou umLocalDate
na versão recente. Mas outras plataformas como .Net não. Somente os designers de linguagem podem realmente responder por que isso não está incluído na biblioteca padrão, mas meu palpite seria que "tempo instantâneo" é conceitualmente simples e universalmente útil, enquanto os outros conceitos de tempo são úteis apenas para domínios de aplicativos específicos (como calendários etc. .). Portanto, faz sentido permitir que o desenvolvedor do aplicativo escreva tipos personalizados para lidar com os casos de uso mais complexos ou que ele seja tratado por uma biblioteca de terceiros (como Joda-time).fonte
Isso provavelmente ocorre porque o calendário é complicado e usado de tantas maneiras diferentes que ninguém conseguiu imaginar uma classe que seja simples, mas geral o suficiente para ser útil em muitos campos.
O tipo de data comumente encontrado nas linguagens de programação pode ser usado para datar com precisão as transações em um sistema de computador. Outros casos de uso provavelmente exigirão uma biblioteca customizada.
Aqui está uma pequena lista de fatos sobre calendários, demonstrando sua complexidade - a maioria deles é histórica; portanto, se você estiver restringindo sua atenção a datas após 1.1.1970, não será afetado por isso. No entanto, se seu aplicativo precisar trabalhar com datas anteriores ao final do século 19, esses fatos serão importantes. Os possíveis casos de uso são bancos de dados históricos de todos os tipos (livros, genealogia), mas também ativos de grandes empresas ou organizações ainda em atividade atualmente.
Todos esses fatos são citados nas excelentes perguntas frequentes encontradas na biblioteca de calendários do OCaml, escritas por Julien Signolles.
O calendário juliano foi introduzido por Júlio César em 45 aC. Era de uso comum até os anos 1500, quando os países começaram a mudar para o calendário gregoriano (seção 2.2). No entanto, alguns países (por exemplo, Grécia e Rússia) o usaram nos anos 1900, e a igreja ortodoxa na Rússia ainda o usa, assim como algumas outras igrejas ortodoxas.
A mudança do calendário juliano para o gregoriano não ocorreu de maneira uniforme e, dependendo do ano da mudança, 10 a 13 dias foram descartados. Por exemplo, na França, 9 de dezembro de 1582 foi seguido por 20 de dezembro de 1582 e na Grécia, 9 de março de 1924, seguido por 23 de março de 1924.
Mesmo na era moderna, muitos calendários diferentes são usados (gregoriano, ortodoxo, islâmico e chinês) para citar alguns, que usam maneiras diferentes de calcular anos e aniversários ou as celebrações religiosas da data.
Agora você espera um tipo de data que inclua operações úteis para operações comerciais gerais. Acho que não existem operações gerais de negócios. Por exemplo, no mundo das finanças, precisamos calcular:
As frações do ano (como "6 meses" correspondem a "0,5"), que são usadas em conjunto com uma taxa de juros para calcular os juros reais de um empréstimo por um determinado período. Existem 6 a 10 receitas para calcular essas frações, cada uma diferente na maneira como lidam com a duração de um ano bissexto, a posição do período em relação ao último dia de fevereiro e a duração de um mês.
No lançamento da data, ao calcular aniversários, usamos um calendário comercial e uma regra (escolhida entre um conjunto de mais de 6 regras diferentes) para mudar um aniversário de um feriado para um dia útil.
Para as pessoas que trabalham no setor financeiro, qualquer tipo de calendário que não implemente todas essas funções e regras é inútil. É provável que muitos outros setores tenham outros tipos de hábitos e convenções que exigem cálculos personalizados no calendário.
Se você precisar acompanhar um único dia do calendário, a melhor maneira é provavelmente usar um número inteiro grande representando o dia juliano desse dia. Os algoritmos para conversão do dia juliano para o dia civil - descritos com ano, mês e calendário - são amplamente conhecidos e exaustivamente testados, para que você possa implementá-los facilmente em seu aplicativo - e descubra qual regra é pertinente em seu dia. caso para calcular o aniversário de um evento ocorrido em 29 de fevereiro.
fonte
Acho que Mike Nakis, em sua resposta acima, faz um trabalho melhor do que eu ao explicar como o tempo em geral é uma coordenada absoluta medida e qualquer outra comunicação, suposto estado ou persistência dessa coordenada de tempo é apenas uma representação abstraída da referida coordenada de tempo.
Você fala com essas representações ao se referir ao Dia da Semana como apenas algum tipo de módulo de representação de um momento real. Na realidade, é um pouco mais complicado que isso. Se você foi encarregado de escrever uma função que retornará o dia da semana por um determinado momento, considere as seguintes informações que você precisará como entrada para esse algoritmo. Você precisará do ponto no tempo, do calendário e do fuso horário (lembre-se de que os fusos horários mudam TODO O TEMPO, portanto, você precisa saber quando esse fuso horário efetivo começou e quando terminou em coordenadas de horário específicas. por exemplo!) e, se o horário de verão estiver em vigor, isso também mudará com o tempo. Agora considere se você recebeu um DateTime no fuso horário local,
Você pode ver como essa pergunta aparentemente simples pode ser realmente complicada.
Eu sei a dor que você está sentindo no seu lugar, corrigindo todos os erros em um aplicativo de agendamento de visitas para um produto que foi escrito por desenvolvedores inexperientes. A coisa toda teve que ser descartada.
O tempo é realmente uma coordenada, mas além de uma mera Data, considere outros dados confidenciais que podem ser necessários, como:
Duração: um intervalo de milissegundos que pode ocorrer que significa um período ou passagem de tempo sem coordenadas de tempo específicas especificadas. Um caso de uso pode ser,
Intervalo: um intervalo de tempo entre duas coordenadas de tempo específicas. Um caso de uso em que você pode considerar um intervalo.
Outro ponto rápido que eu queria destacar é que você fez um comentário sobre os números de ponto flutuante para dados com base no tempo e eu aconselho isso. A aritmética de ponto flutuante inevitavelmente leva a erros de arredondamento que podem não fornecer uma medida tão precisa quanto o tempo necessário.
Portanto, em conclusão, todas essas informações inevitavelmente levam às seguintes considerações de design:
fonte
Em resumo, porque a maioria dos tipos de horário baseados em computador está focada em lidar com o problema de fuso horário e horário corretamente.
Existem 2 casos extremos que não são bem atendidos pela abordagem usual. Definir um horário no outro lado de uma alteração do horário de verão usando a hora local, que depois é convertida no UTC por uma camada de abstração mais baixa e, em seguida, leva 1 hora mais cedo / mais tarde para a sua reunião.
O outro é (conforme a pergunta) modelando informações de datas arbitrárias, como registrar a data de nascimento de uma pessoa. Imagine o caso em que duas pessoas nascem ao mesmo tempo, uma na Nova Zelândia e outra no Havaí. A probabilidade é que eles tenham datas de nascimento diferentes em seus passaportes e, se a pessoa nascida no Havaí se mudar para a Nova Zelândia, ela será considerada um dia mais antiga que a pessoa nascida na Nova Zelândia, apesar de ter vivido exatamente no mesmo período.
A sugestão na pergunta de definir a data para o horário do meio-dia, o UTC funcionará, QUASE em todos os lugares. As compensações do UTC variam de -12 a +14; portanto, há alguns lugares no Pacífico em que essa abordagem falhará. Costumo tratar esses tipos de dados como seqüências de caracteres, em um formato aaaammdd, e se eu tiver que fazer cálculos de comparação entre duas datas, isso pode ser feito com segurança como uma comparação de cadeias. Ao fazer comparações delta (por exemplo, entre data e agora ou quanto tempo até atingirem a idade X), você precisa garantir que todas as datas sejam criadas no mesmo deslocamento UTC e, em seguida, pode usar as funções de hora padrão para realizar o trabalho.
fonte
Date
tipo de dados, apenas aString
Pelas mesmas razões, penso eu, que os valores DateTime são geralmente especificados no UTC: simplicidade e confiabilidade . O ponto de um valor DateTime é especificar um único ponto no tempo que não seja afetado pelo fuso horário, horário de verão, calendário e outros ajustes locais. Os valores DateTime especificam um instante (até o limite da resolução do tipo), não um período ou um conjunto de horários. Essas limitações tornam possível comparar valores DateTime de maneira confiável, previsível e descomplicada.
Tentar especificar uma data com um valor DateTime é como tentar usar um ponto para especificar uma área.Você pode fazer esse trabalho usando uma convenção, como "este ponto representa o centro de um círculo com um raio de 100 m", mas existem muitos problemas: todo mundo precisa usar a mesma convenção, é preciso escrever um monte de código de suporte para tornar o trabalho com o tipo errado menos doloroso, e é praticamente garantido que em algum momento você precisará especificar uma área maior ou menor que a área convencional. O mesmo ocorre com as datas: você pode usar 'meio-dia' como horário convencional para especificar datas, mas depois entra nos fusos horários porque as pessoas esperam especificar datas no horário local, em vez do UTC. E mesmo que você encontre uma maneira satisfatória de usar um DateTime para especificar uma data, precisará de mais informações para saber se é uma data absoluta ou relativa: é 4 de julho, 1776 ou todo 4 de julho? E se você quiser repetir usando outro período? E os calendários têm todos os tipos de problemas malucos: alguns meses são mais longos que outros, alguns anos são mais longos que outros, alguns dias são mais longos que outros e alguns calendários têm lacunas neles. Você não gostaria de resolver esses problemas apenas por dias inteiros, porque os mesmos problemas surgem por períodos mais curtos: você provavelmente gostaria de escrever um código que expressa "tome 1 comprimido a cada 4 horas" tão prontamente quanto "o grupo se reúne a cada terceira sexta-feira ".
Portanto, há muitas complicações envolvidas no trabalho com datas. É relativamente (sem trocadilhos) fácil fornecer um tipo que especifique um ponto no tempo e trabalhe com ele como faria com um número, mas extremamente difícil fornecer um tipo que aborde todas as maneiras pelas quais as datas são usadas.
Como outros apontaram, não são linguagens e bibliotecas que fornecem um bom suporte para datas, e muitas vezes é uma boa idéia para usá-los dado que é muito difícil conseguir o código da data relacionada exatamente correto.
fonte
Existem muitos tipos em várias bibliotecas para vários idiomas. Certamente existe um para o seu idioma atual. O pacote utilitário Java tinha uma API horrível para cálculos de tempo, mas a introdução do pacote java.time tornou a vida muito melhor. Consulte java.time.LocalDate, contendo um valor ano-mês-dia, ou java.time.MonthDay, contendo apenas um número de mês e dia.
fonte
A manipulação de calendário é um dos aspectos menos compreendidos da computação. Livros inteiros foram escritos sobre o assunto. O @MichealBlackburn está absolutamente certo ao solicitar um tipo de dados somente para data, que não seja resolvido até um ponto da linha do tempo, sujeito a reinterpretação. Historicamente, houve disputas legítimas sobre o significado de uma data. Não é preciso procurar além da adoção do calendário gregoriano para descobrir quão complexo ele pode se tornar. Além disso, os anos nem sempre começaram em 1º de janeiro, mesmo na Europa Ocidental e suas colônias ( por exemplo , a Grã-Bretanha e a América Britânica começaram o ano em 25 de março).
fonte
Em resposta a:
O motivo mais comum pode ser "porque não é necessário". Se você deseja um horário que não se importe com horas, minutos, segundos, etc., basta inicializá-lo assim:
Se você quiser, pode estender o DateTime:
Nota: ignoro intencionalmente a hora e o fuso horário padrão. Realmente não importa o que você definiu, desde que sejam iguais para todos os
Date
s. Você pode defender o UTC. Você pode usar o fuso horário em que seu servidor se encontra - de qualquer forma, não acho que seja importante representar um valor imprecisoDate
. O mesmo com o tempo padrão - você pode fazer 0, meio dia. Não importa. Se o Facebook me enviar uma notificação de aniversário às 00:01, mas eu nasci às 23:59, eu realmente não me importo, e não ficarei ofendido por terem mais de 12 horas de folga.O acima está em Java, mas funcionaria de maneira semelhante em qualquer linguagem com a
DateTime
e herança. O Java, na verdade, tem várias maneiras de resolver isso, e o acima foi descontinuado (eles querem que você useCalendar
agora). Mas como outros postou comentários, algumas línguas realmente não fornecer umaDate
classe, presumivelmente, exatamente por esse motivo.Date
Provavelmente , toda implementação é provavelmente apenas um invólucro para a linguagemDateTime
e zera o tempo. Caso contrário, você precisaria de um código duplicado para resolver problemas como o número de dias entre duas datas / datas e horários ou se doisDate
s são iguais (e 29 de fevereiro e 1 de março?). Geralmente, esse tipo de coisa é resolvido naDateTime
classe. Faz sentido reutilizar o mesmo código para aDate
.fonte
LocalDate
, ouDate
digita, e você precisa 'fazer o seu próprio'.new Date(2000, 1, 1);
enew Date(2000, 1, 1);
? Você pode dizer qual deles tem horas, minutos e segundos vazios abaixo? Se você estiver preocupado com a exibição de funções extras (como setMinutes ()), poderá seguir o caminho de terDate
wrap emDateTime
vez de herdá-lo e só expor setYear, setMonth, setDay, etc. E certamente não é menos correto que o DateTime da biblioteca que você está usando.