2010-06-02 3 views
22

Sto facendo il confronto DateTime ma non voglio fare il confronto al secondo, al millisecondo e al livello delle zecche. Qual è il modo più elegante?Precisione confronto data/ora

Se si confronta semplicemente il DateTime, quindi sono raramente uguali a causa delle differenze di tick.

+0

Penso che non ci sia alcun proiettile di siver qui ... Dipende totalmente dalle tue preferenze ...In ogni modo, grazie per aver fatto questa domanda ... Ho imparato molte cose da questo Q. –

+1

Volevo solo sottolineare che in base alla risposta selezionata, questa domanda è sbagliata. Non stai chiedendo di confrontare * il tempo assoluto * fino al minuto, stai chiedendo di controllare se * il tempo trascorso * è inferiore a una certa soglia. – ErikE

risposta

25

Cosa succede ad usare un periodo.

if (Math.Truncate((A - B).TotalMinutes) == 0) 
{ 
    //There is less than one minute between them 
} 

Probabilmente non è il modo più elegante, ma consente per i casi che sono uno secondo di distanza e ancora diversi giorni/ore/minuti parti come andare oltre la mezzanotte.

Edit: si è verificato a me che il troncamento è unecessary ...

if (Math.Abs((A - B).TotalMinutes) < 1) 
{ 
    //There is less than one minute between them 
} 

Personalmente penso che questo sia più elegante ...

+0

Penso .. dovrebbe leggere come se ((A - B) .TotalMinutes <= 1) –

+0

Sì hai ragione, ho rimosso anche gli uguali poiché il commento dice meno di. –

+0

Affinché la tua modifica funzioni, devi chiamare 'Math.Abs' prima di controllare se è inferiore a 1, altrimenti potresti avere una differenza ** enorme ** nei 2 valori di' DateTime'. Ad esempio '(DateTime.Now - DateTime.Now.AddMinutes (10)). TotalMinutes' sarebbe inferiore a 1, ma ha una differenza totale di 10 minuti. –

6

Un approccio potrebbe essere quello di creare due nuovi DateTimes dai vostri valori che si desidera confrontare, ma ignora qualsiasi cosa, da secondi in giù e poi confrontare quelle:

DateTime compare1 = new DateTime(year1, month1, day1, hour1, minute1, 0); 
DateTime compare2 = new DateTime(year2, month2, day2, hour2, minute2, 0); 

int result = DateTime.Compare(compare1, compare2); 

sarei il primo ad ammettere che è non elegante, ma risolve il problema.

+0

Questa soluzione è utile perché rileva se è lo stesso minuto (mentre alcune altre soluzioni potrebbero restituire true per diversi minuti distanti solo alcuni secondi, ad esempio 11:59:59 e 12:00:01). E, dovrebbe funzionare meglio dei confronti basati su stringhe. –

3

È possibile convertirli in formato String e confrontare la stringa tra loro.

Questo dà anche la libertà di scegliere i parametri di confronto, che solo il tempo senza la data, ecc

if (String.Format("{0:ddMMyyyyHHmmss}", date1) == String.Format("{0:ddMMyyyyHHmmss}", date2)) 
{ 
    // success 
} 
3

ne dite di questo ComparerClass?

public class DateTimeComparer : Comparer<DateTime> 
{ 
    private string _Format; 

    public DateTimeComparer(string format) 
    { 
     _Format = format; 
    } 

    public override int Compare(DateTime x, DateTime y) 
    { 
     if(x.ToString(_Format) == y.ToString(_Format)) 
      return 0; 

     return x.CompareTo(y); 
    } 
} 

Questo può essere utilizzato da

List.Sort(new DateTimeComparer("hh:mm")); 
6

Utilizzando un periodo si ottengono tutti i granularità desiderato:

DateTime dt1, dt2; 
double d = (dt2 - dt1).TotalDays; 
double h = (dt2 - dt1).TotalHours; 
double m = (dt2 - dt1).TotalMinutes; 
double s = (dt2 - dt1).TotalSeconds; 
double ms = (dt2 - dt1).TotalMilliseconds; 
double ticks = (dt2 - dt1).Ticks; 
+0

Può essere utile a volte ... –

+0

Test mostra che un TimeSpan creato da 2 valori DateTime uguali fino allo stesso millisecondo fornisce tutti i tipi di valori> 1 per tutto tranne TotalMilliseconds. – dudeNumber4

+0

@ dudeNumber4 Puoi mostrare il tuo codice di prova? – hoang

0

ho scritto questo per aiutare me stesso:

internal class ImpreciseCompareDate : IComparer<DateTime> 
{ 
    private readonly double _Tolerance; 

    public ImpreciseCompareDate(double MillisecondsTolerance) 
    { 
     _Tolerance = MillisecondsTolerance; 
    } 

    public int Compare(DateTime x, DateTime y) 
    { 
     return Math.Abs((x - y).TotalMilliseconds) < _Tolerance ? 0 : x.CompareTo(y); 
    } 
} 

Tolleranza impostabile su (10 d/3d) per account per server SQL 1/300 di ms. Se viene superata la tolleranza, delegare al confronto predefinito. esempio

+0

Il vantaggio di questo approccio è ovviamente la possibilità di impostare una tolleranza arbitraria indipendente dai livelli già forniti da datetime (secondi, ms, ecc.) Essendo che la tolleranza del server SQL non si interrompe nemmeno su uno di questi. vale a dire. 1/300 di secondo. – Sprague

4
public class DateTimeComparer : Comparer<DateTime> 
{ 
    private Prescision _Prescision; 

    public enum Prescision : sbyte 
    { 
     Millisecons, 
     Seconds, 
     Minutes, 
     Hour, 
     Day, 
     Month, 
     Year, 
     Ticks 
    } 

    Func<DateTime, DateTime>[] actions = new Func<DateTime, DateTime>[] 
     { 
      (x) => { return x.AddMilliseconds(-x.Millisecond);}, 
      (x) => { return x.AddSeconds(-x.Second);}, 
      (x) => { return x.AddMinutes(-x.Minute);}, 
      (x) => { return x.AddHours(-x.Hour);}, 
      (x) => { return x.AddDays(-x.Day);}, 
      (x) => { return x.AddMonths(-x.Month);}, 
     }; 

    public DateTimeComparer(Prescision prescision = Prescision.Ticks) 
    { 
     _Prescision = prescision; 
    } 

    public override int Compare(DateTime x, DateTime y) 
    { 
     if (_Prescision == Prescision.Ticks) 
     { 
      return x.CompareTo(y); 
     } 

     for (sbyte i = (sbyte)(_Prescision - 1); i >= 0; i--) 
     { 
      x = actions[i](x); 
      y = actions[i](y); 
     } 

     return x.CompareTo(y); 
    } 
} 

Usage:

new DateTimeComparer(DateTimeComparer.Prescision.Day).Compare(Date1, Date2) 
0

Un altro modo è quello di convertire prima mediante la trasformazione in zecche livello con una semplice (non arrotondamento) Calcolo:

var now = DateTime.UtcNow; 
// 636340541021531973, 2017-06-26T06:08:22.1531973Z 

var millisecondsPrecision = new DateTime(now.Ticks/10000 * 10000, now.Kind); 
// 636340541021530000, 2017-06-26T06:08:22.1530000Z 

var secondsPrecision = new DateTime(now.Ticks/10000000 * 10000000, now.Kind); 
// 636340541020000000, 2017-06-26T06:08:22.0000000Z 

var minutePrecision = new DateTime(now.Ticks/(10000000*60) * (10000000*60), now.Kind); 
// 636340541000000000, 2017-06-26T06:08:00.0000000Z 
0

@ La soluzione di ALZ sembra piacevole ma è troppo complicato e ha un bug. Quindi ho deciso di combinarlo con la soluzione di @ ChrisF.

public class DateTimeComparer : Comparer<DateTime> 
    { 
     public enum Precision 
     { 
      Years = 0, 
      Months, 
      Days, 
      Hours, 
      Minutes, 
      Seconds, 
      Millisecons, 
      Ticks 
     } 

     private Precision _precision; 

     public DateTimeComparer(Precision precision = Precision.Ticks) 
     { 
      _precision = precision; 
     } 

     public override int Compare(DateTime x, DateTime y) 
     { 
      if (_precision == Precision.Ticks) 
      { 
       return x.CompareTo(y); 
      } 

      var xx = AssembleValue(x, _precision); 
      var yy = AssembleValue(y, _precision); 

      return xx.CompareTo(yy); 
     } 

     private static DateTime AssembleValue(DateTime input, Precision precision) 
     { 
      var p = (int)precision; 
      var i = 1; 
      return new DateTime(input.Year, 
           p >= i++ ? input.Month : 1, 
           p >= i++ ? input.Day : 1, 
           p >= i++ ? input.Hour : 0, 
           p >= i++ ? input.Minute : 0, 
           p >= i++ ? input.Second : 0, 
           p >= i++ ? input.Millisecond : 0); 
     } 
    }