Arrondir les objets DateTime


105

Je veux arrondir les dates / heures à l'intervalle le plus proche pour une application de cartographie. Je voudrais une signature de méthode d'extension comme suit afin que l'arrondi puisse être obtenu pour n'importe quel niveau de précision:

static DateTime Round(this DateTime date, TimeSpan span);

L'idée est que si je passe dans un laps de temps de dix minutes, cela arrondira à l'intervalle de dix minutes le plus proche. Je n'arrive pas à comprendre l'implémentation et j'espère que l'un de vous aura écrit ou utilisé quelque chose de similaire auparavant.

Je pense qu'un plancher, un plafond ou une mise en œuvre la plus proche convient parfaitement.

Des idées?

Edit: Grâce à @tvanfosson & @ShuggyCoUk, l'implémentation ressemble à ceci:

public static class DateExtensions {
    public static DateTime Round(this DateTime date, TimeSpan span) {
        long ticks = (date.Ticks + (span.Ticks / 2) + 1)/ span.Ticks;
        return new DateTime(ticks * span.Ticks);
    }
    public static DateTime Floor(this DateTime date, TimeSpan span) {
        long ticks = (date.Ticks / span.Ticks);
        return new DateTime(ticks * span.Ticks);
    }
    public static DateTime Ceil(this DateTime date, TimeSpan span) {
        long ticks = (date.Ticks + span.Ticks - 1) / span.Ticks;
        return new DateTime(ticks * span.Ticks);
    }
}

Et s'appelle ainsi:

DateTime nearestHour = DateTime.Now.Round(new TimeSpan(1,0,0));
DateTime minuteCeiling = DateTime.Now.Ceil(new TimeSpan(0,1,0));
DateTime weekFloor = DateTime.Now.Floor(new TimeSpan(7,0,0,0));
...

À votre santé!


1
Certaines des implémentations ici pourraient également aider: stackoverflow.com/questions/766626/…
Matt Hamilton

1
duplication possible de Y a
Lance Roberts

3
N'oubliez pas d'ajouter le DateTimeKind d'origine à la date nouvellement créée ex: new DateTime (ticks * span.Ticks, date.Kind);
AM

Réponses:


130

Sol

long ticks = date.Ticks / span.Ticks;

return new DateTime( ticks * span.Ticks );

Arrondi (au milieu)

long ticks = (date.Ticks + (span.Ticks / 2) + 1)/ span.Ticks;

return new DateTime( ticks * span.Ticks );

Plafond

long ticks = (date.Ticks + span.Ticks - 1)/ span.Ticks;

return new DateTime( ticks * span.Ticks );

5
J'ai récemment rencontré un problème où le DateTimeKind n'était pas conservé. L'ajustement suivant à la dernière ligne de chaque méthode a aidé dans mon cas:return new DateTime(ticks * span.Ticks, date.Kind);
Peet

39

Cela vous permettra d'arrondir à n'importe quel intervalle donné. C'est aussi légèrement plus rapide que de diviser puis de multiplier les tiques.

public static class DateTimeExtensions
{
  public static DateTime Floor(this DateTime dateTime, TimeSpan interval)
  {
    return dateTime.AddTicks(-(dateTime.Ticks % interval.Ticks));
  }

  public static DateTime Ceiling(this DateTime dateTime, TimeSpan interval)
  {
    var overflow = dateTime.Ticks % interval.Ticks;

    return overflow == 0 ? dateTime : dateTime.AddTicks(interval.Ticks - overflow);
  }

  public static DateTime Round(this DateTime dateTime, TimeSpan interval)
  {
    var halfIntervalTicks = (interval.Ticks + 1) >> 1;

    return dateTime.AddTicks(halfIntervalTicks - ((dateTime.Ticks + halfIntervalTicks) % interval.Ticks));
  }
}

11

Vous devez également être clair si vous souhaitez que votre arrondi:

  1. être au début, à la fin ou au milieu de l'intervalle
    • le démarrage est le plus simple et souvent prévu, mais vous devez être clair dans vos spécifications initiales.
  2. Comment vous voulez arrondir les cas limites.
    • normalement seulement un problème si vous arrondissez au milieu plutôt qu'à la fin.
    • Étant donné que l'arrondissement au milieu est une tentative de réponse sans biais, vous devez utiliser quelque chose comme Bankers Rounding techniquement arrondir la moitié, même pour être vraiment exempt de biais.

Il est fort probable que vous ne vous souciez vraiment que du premier point, mais dans ces questions `` simples '', le comportement qui en résulte peut avoir des conséquences importantes lorsque vous l'utilisez dans le monde réel (souvent à des intervalles adjacents à zéro).

La solution de tvanfosson couvre tous les cas énumérés en 1. L'exemple du point médian est biaisé vers le haut. Il est peu probable que ce soit un problème d'arrondi lié au temps.


3

Utilisez simplement les ticks, en les utilisant pour diviser, sol / plafond / arrondir la valeur et multipliez-la.


-2

Si vous souhaitez simplement arrondir la valeur de l'heure à la valeur plafond

Console.WriteLine(DateTime.Now.ToString("M/d/yyyy hh:00:00"));

OP a demandé un DateTime comme objet de retour.
aj.toulan
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.