Muitos em 2020 foram vítimas de um estranho fenómeno de percepção do tempo, mas alguns sistemas de gestão de bases de dados manipulam o tempo por muito mais tempo. Percebi isso pela primeira vez quando um amigo meu em um de seus projetos ( Accord é um bot Discord popular) encontrou a seguinte exceção do conector MySQL quando usado com EF Core:
MySqlException: Incorrect TIME value: '960:00:00.000000'
Não sendo muito experiente em MySQL (já que prefiro PostgreSQL por motivos que logo se tornarão aparentes), pensei por um segundo que o número de horas estava errado. É razoável supor que os valores TIME sejam limitados a 24 horas ou que os valores que abrangem vários dias exijam uma sintaxe diferente - por exemplo,
40:00:00:00representariam 40 dias. Mas a realidade acabou sendo muito mais complicada e confusa.
A próxima etapa óbvia foi verificar a documentação do MySQL . Diz:
O MySQL recebe e exibe valores de TIME no formato 'hh: mm: ss' (ou no formato 'hhh: mm: ss' para valores horários grandes).
Até agora, está tudo bem: nosso valor de TIME problemático se encaixa bem com este formato, embora o fato de
hheles hhhserem especificados explicitamente levanta suspeitas (e quanto aos valores de clock que excedem 999?) A frase a seguir na documentação explica parcialmente tudo, ao longo do caminho estimulando um monte de perguntas como "O que ...?":
Os valores de HORA podem variar de '-838: 59: 59' a '838: 59: 59'.
Bem, ok ... Algum alcance estranho. Deve haver uma boa razão técnica para isso. 839 horas são 34,958 (3) dias e todo o intervalo é exatamente 6040798 segundos. A documentação é a seguinte:
O MySQL reconhece os valores TIME em vários formatos, alguns dos quais podem incluir segundos fracionários até 6 casas decimais (microssegundos).
Em outras palavras, o intervalo inteiro é 6.040.798.000.000 microssegundos. Novamente, algum número estranho. Está longe de ser uma potência de dois (entre 2 42 e 2 43 ), então o MySQL parece estar usando algum formato de representação interno exclusivo. Mas antes de entrar nesse assunto, deixe-me apontar o quão ruim é esse tipo.
Isso é tudo o que o MySQL tem a oferecer para medir intervalos de tempo, com o intervalo de tempo inteiro um pouco mais de um mês. Quão grande é esse "pedacinho"? Como você pode ver, não é nem um múltiplo de um número inteiro de dias.
Pior ainda, o provedor mais popular do MySQL no EF Core converte .NET
TimeSpanpara TIME por padrão , apesar do fato deTimeSpanpode conter intervalos de dezenas de milênios (usa inteiros de 64 bits e a precisão aceitável é de 10 -8 s). Compare isso com alguns meses no TIME. Outras pessoas encontraram
esse problema e a discussão na edição correspondente contém uma referência ao comportamento do SQL Server: "Isso imita o comportamento do SQL Server". Eu verifiquei - de fato, o tipo de hora do SQL Server tem um intervalo de 00: 00: 00.0000000 a 23: 59: 59.9999999, que geralmente é muito mais razoável do que o estranho intervalo de tempo. Mas vamos voltar ao MySQL. Qual é a razão para uma gama tão incomum? No manual do dispositivo MySQL
diz que na versão 5.6.4 o tipo de TEMPO mudou e há suporte para frações de segundos. Três bytes são usados para toda a parte. Se esses três bytes forem usados inteiramente para codificar segundos, isso resultará em um intervalo de tempo de mais de 2.330 horas - muito mais do que o máximo atual de 838 horas (embora mesmo isso não seja muito útil ao converter
TimeSpan'a).
Isso significa que o processo que codifica o tempo no MySQL está desperdiçando bits - talvez por uma questão de facilidade de uso (embora eu não tenha certeza sob quais circunstâncias isso é relevante). Talvez isso faça sentido se o SGBD (e a ideia dos desenvolvedores sobre o que os usuários farão com ele) for voltado para trabalhar com strings e os desenvolvedores quiserem acelerar a apresentação
hh:mm:ss.
Então veja:
1 — (1 = , 0 = )
1 ( )
10 — (0-838)
6 — (0-59)
6 — (0-59)
— 24 = 3
Isso explica tudo, não é? Bem, vamos dar uma olhada mais de perto. 10 bits por horas ... e o intervalo é de zero a 838. Apresso-me em lembrar que 2 10 = 1024, não 838. A intriga está ganhando impulso ...
Claro, não fui a primeira pessoa a fazer essa pergunta (já perguntei sobre isso no StackOverflow ). Tudo parece estar afirmado na resposta "aceita" lá, no entanto, a estranha escolha de 838 horas é primeiro explicada pela "compatibilidade com aplicativos que foram escritos há muito tempo", e só então é mencionado que isso tem algo a ver com compatibilidade com MySQL 3 - a propósito O Windows 98 era então considerado uma novidade e o Linux não tinha nem 10 anos.
No MySQL 3, o tipo TIME também usava 3 bytes, só que de uma maneira completamente diferente. Um dos bits também foi reservado para o sinal, mas os 23 bits restantes corresponderam a inteiros, obtidos da seguinte forma: horas × 10.000 + minutos × 100 + segundos. Em outras palavras, os dois dígitos menos significativos eram segundos, os próximos dois eram minutos e os dois restantes eram horas. 2 * 23 é 83888608, que é 838: 86: 08, portanto, o valor máximo de tempo válido neste formato é 838: 59: 59.
Este formato é ainda menos conveniente do que o atual, uma vez que requer multiplicação e divisão para quase qualquer operação de tempo (com exceção da formatação e análise de strings - o que mais uma vez prova que o MySQL dá muita atenção à string IO e realmente não se preocupa com a presença de tipos. que seria conveniente para operações internas e protocolos não baseados em string).
Os desenvolvedores do MySQL foram capazes de corrigir esse tipo muitas vezes, ou pelo menos fornecer uma alternativa que é livre das limitações existentes. O tipo TIME mudou duas vezes desde o MySQL 3 até hoje, mas a cada vez o intervalo estranho permaneceu o mesmo - talvez por razões de compatibilidade.
Não consigo imaginar uma situação em que expandir o intervalo de um valor para um tipo poderia quebrar a compatibilidade do aplicativo: os tipos no MySQL têm comportamento de estouro específico? Que programador lógico confiaria nas restrições de tipo de banco de dados interno para validar qualquer coisa em seu aplicativo? Se tal pessoa existe, por que diabos ela de repente decidiu transferir esse limite ridículo de 838 horas para o modelo de dados de seu aplicativo sem qualquer alteração? Para ser sincero, nem quero saber as respostas para essas perguntas.
Apesar de algumas transformações importantes na história do MySQL, o tipo TIME ainda é estranho e limitado. E o destaque do programa aqui, na minha opinião, é o bit não utilizado “reservado para futuras extensões”. Espero que, a longo prazo, aponte para o antigo valor TIME legado e, então, o MySQL e / ou MariaDB terá um tipo temporal sensível, como INTERVAL no PostgreSQL , que tem um intervalo de ± 178.000.000 anos e um microssegundo precisão.
PS do tradutor
Leia também em nosso blog: