O método Any () no ListDateTime não funciona conforme a expectativa

9

Estou trabalhando em .net 4.6 em winforms (aqui o código é do aplicativo console de teste)

Em um ponto, estou tendo uma lista de DateTime e preciso descobrir se essa lista contém uma data específica ou não.

Para isso, estou tentando usar Any() na lista. Mesmo que a lista contenha a data desejada, Any() retornará false apenas.

A seguir está o código de exemplo, que também tem o mesmo comportamento. Então, se eu puder ter alguma ideia sobre esse código, acho que isso ajudará no meu código real também.

List<DateTime> dateTimeList = new List<DateTime>();
DateTime dateNow = DateTime.Now;

DateTime date = new DateTime(dateNow.Year, dateNow.Month, dateNow.Day, dateNow.Hour, dateNow.Minute, 00);
date = date.AddMinutes(-10);
while (date < dateNow.AddMinutes(10))
{
    dateTimeList.Add(date);
    date = date.AddMinutes(1);
}

dateNow = dateNow.AddSeconds(-dateNow.Second);
dateNow = dateNow.AddMilliseconds(-dateNow.Millisecond);

foreach (DateTime dateInList in dateTimeList)
    Console.WriteLine("date list List:" + dateInList.ToString("ddMMyyyy hh:mm:ss:fffz") + " - VS - desired date:" + dateNow.ToString("ddMMyyyy hh:mm:ss:fffz"));

if (dateTimeList.Any(x => x == dateNow))
    Console.WriteLine("date found");
else
    Console.WriteLine("date Not found");

if (dateTimeList.Any(x => x.ToString("ddMMyyyy hh:mm:ss:fffz") == dateNow.ToString("ddMMyyyy hh:mm:ss:fffz")))
    Console.WriteLine("date string matched");
else
    Console.WriteLine("date string didn't match");

saída:

    
por Amit 08.03.2018 в 07:54
fonte

5 respostas

23

O Ticks e < href="https://msdn.microsoft.com/pt-br/library/system.datetime.timeofday(v=vs.110).aspx"> TimeOfDay propriedades dos itens no seu dateTimeList não é igual a Ticks e TimeOfDay das propriedades de seu dateNow e dateNow tem mais limites do que o de dateTimeList . Você precisa adicionar esta linha:

dateNow = new DateTime(dateNow.Year, dateNow.Month,
           dateNow.Day, dateNow.Hour, dateNow.Minute, 00);

Isso fará com que as propriedades Ticks e TimeOfDay de seu dateNow sejam iguais às que você adicionou ao seu dateTimeList .

    
por S.Akbari 08.03.2018 / 08:14
fonte
42

Há um ditado na programação de computadores "select is not broken". Isso significa que, quando um pouco de software básico, comumente usado e muito testado parece estar quebrado, o problema é que você diagnosticou incorretamente o problema e não que a ferramenta está quebrada.

Any funciona muito bem.

O erro é que você está arredondando a data corretamente em um lugar e incorretamente na outra, e a data arredondada incorretamente não é igual à data corretamente arredondada. Use a propriedade Ticks nas datas para ver por que uma de suas técnicas de arredondamento é boa e uma delas é totalmente errada.

    
por Eric Lippert 08.03.2018 / 08:20
fonte
10

A chave para descobrir por que isso acontece é descobrir qual é a diferença entre os dois DateTime s que estamos comparando.

Se você imprimir a propriedade Ticks dos tempos de data, você encontrará algo assim:

636560893800004640
636560887800000000
636560893800004640
636560888400000000
636560893800004640
636560889000000000
636560893800004640
636560889600000000
636560893800004640
636560890200000000
636560893800004640
636560890800000000
636560893800004640
636560891400000000
636560893800004640
636560892000000000
636560893800004640
636560892600000000
636560893800004640
636560893200000000
636560893800004640
636560893800000000
636560893800004640
636560894400000000
636560893800004640
636560895000000000
636560893800004640
636560895600000000
636560893800004640
636560896200000000
636560893800004640
636560896800000000
636560893800004640
636560897400000000
636560893800004640
636560898000000000
636560893800004640
636560898600000000
636560893800004640
636560899200000000
636560893800004640
636560899800000000

Como você pode ver, essas duas linhas provavelmente são as duas DateTime s que você acha que seriam iguais, mas não:

636560893800004640
636560893800000000

O acima é o dateNow e o abaixo é o da lista.

Veja a diferença? dateNow tem mais carrapatos do que o da lista.

Por que isso acontece?

Os DateTime s na lista são criados a partir de date , que é criado usando o construtor com 6 argumentos. Isso cria um DateTime exatamente como você especificou. Isso significa que a instância criada não terá nenhum tick extra para o "restante". E eu posso ver que quando você muda seu dateNow , você tenta remover todos os componentes extras que você não se importa, como segundos e milissegundos, mas você esqueceu de ticks . Quando você compara 2 DateTime s você está realmente comparando os carrapatos.

new DateTime(1) == new DateTime(1)
true
new DateTime(1) == new DateTime(2)
false

Portanto, você precisa remover os tiques extras de seu dateNow para obter o resultado desejado ou apenas usar o construtor de 6 argumentos novamente.

    
por Sweeper 08.03.2018 / 08:17
fonte
2

Problema

Sua sincronização com AddSeconds e AddMilliseconds funciona com a precisão de fff (milissegundos), mas não com a precisão de Ticks (um décimo milionésimo de segundo). O último é necessário para a DateTime igualdade que Any() usa.

Uma solução

Sincronize precisamente a cópia DateTime com seu protótipo criando essa cópia com o DateTime construtor que leva Ticks . Em seguida, seu código encontra a data com Any() .

Aqui é o seu código melhorado como um violino funcional .

List<DateTime> dateTimeList = new List<DateTime>();
DateTime dateNow = DateTime.Now;

// use dateNow.Ticks in the constructor to create a precise, 
// synchronized DateTime clone
DateTime date = new DateTime(dateNow.Ticks);

date = date.AddMinutes(-10);
while (date < dateNow.AddMinutes(10))
{
    dateTimeList.Add(date);
    date = date.AddMinutes(1);
}

if (dateTimeList.Any(x => x == dateNow))
    Console.WriteLine("date found");
else
    Console.WriteLine("date Not found");

var format = "ddMMyyyy hh:mm:ss:fffz";
if (dateTimeList.Any(x => x.ToString(format) == dateNow.ToString(format)))
    Console.WriteLine("date string matched");
else
    Console.WriteLine("date string didn't match");

Aparte

Podemos formatar uma string de data para a precisão de tiques usando fffffff em vez de fff .

    
por Shaun Luttin 08.03.2018 / 08:40
fonte
0

DateTime usa o Relógio do sistema que é notoriamente preciso apenas para cerca de 10-15ms - conforme destacado na resposta a essa pergunta - < a href="https://stackoverflow.com/questions/16032451/get-datetime-now-with-milliseconds-precision"> Obtenha o DateTime.Now com precisão de milissegundos

Para contornar isso, você precisará substituir sua comparação de igualdade ( == ) na sua cláusula Any() por uma verificação que leve em conta a imprecisão. O código abaixo corresponde às datas separadas por menos de 20 ms ...

if (dateTimeList.Any(x => Math.Abs((dateNow - x).TotalMilliseconds) < 20)
    Console.WriteLine("date found");
else
    Console.WriteLine("date Not found");
    
por controlbox 08.03.2018 / 17:32
fonte