web-dev-qa-db-fra.com

Comment puis-je parcourir une plage de dates?

Je ne sais même pas comment faire cela sans utiliser une solution horrible pour le type boucle/compteur. Voici le problème:

On me donne deux dates, une date de début et une date de fin, et à un intervalle spécifié, je dois prendre des mesures. Par exemple: pour chaque date entre le 3/10/2009 tous les trois jours et jusqu'au 26/03/2009, je dois créer une entrée dans une liste. Donc, mes entrées seraient:

DateTime StartDate = "3/10/2009";
DateTime EndDate = "3/26/2009";
int DayInterval = 3;

et ma sortie serait une liste qui a les dates suivantes:

3/13/2009 3/16/2009 3/19/2009 3/22/2009 3/25/2009

Alors, comment pourrais-je faire quelque chose comme ça? J'ai pensé à utiliser une boucle for qui itérerait chaque jour de la plage avec un compteur séparé comme ceci:

int count = 0;

for(int i = 0; i < n; i++)
{
     count++;
     if(count >= DayInterval)
     {
          //take action
          count = 0;
     }

}

Mais il semble qu'il pourrait y avoir un meilleur moyen?

162
onekidney

Eh bien, vous devrez les survoler d’une manière ou d’une autre. Je préfère définir une méthode comme celle-ci:

public IEnumerable<DateTime> EachDay(DateTime from, DateTime thru)
{
    for(var day = from.Date; day.Date <= thru.Date; day = day.AddDays(1))
        yield return day;
}

Ensuite, vous pouvez l'utiliser comme ceci:

foreach (DateTime day in EachDay(StartDate, EndDate))
    // print it or whatever

De cette manière, vous pouvez toucher tous les deux jours, tous les trois jours, uniquement les jours de la semaine, etc. Par exemple, pour revenir tous les trois jours en commençant par la date de début, vous pouvez simplement appeler AddDays(3) dans la boucle au lieu de AddDays(1).

403
mquander

J'ai une classe Range dans MiscUtil que vous pourriez trouver utile. Combiné avec les différentes méthodes d'extension, vous pouvez faire:

foreach (DateTime date in StartDate.To(EndDate).ExcludeEnd()
                                   .Step(DayInterval.Days())
{
    // Do something with the date
}

(Vous pouvez ou non vouloir exclure la fin - je pensais juste que je donnerais cela à titre d'exemple)

Ceci est fondamentalement une forme prête à l'emploi (et plus polyvalente) de la solution de mquander.

28
Jon Skeet

Pour votre exemple, vous pouvez essayer

DateTime StartDate = new DateTime(2009, 3, 10);
DateTime EndDate = new DateTime(2009, 3, 26);
int DayInterval = 3;

List<DateTime> dateList = new List<DateTime>();
while (StartDate.AddDays(DayInterval) <= EndDate)
{
   StartDate = StartDate.AddDays(DayInterval);
   dateList.Add(StartDate);
}
19
Adriaan Stander

Code de @mquander et @Yogurt The Wise utilisé dans les extensions:

public static IEnumerable<DateTime> EachDay(DateTime from, DateTime thru)
{
    for (var day = from.Date; day.Date <= thru.Date; day = day.AddDays(1))
        yield return day;
}

public static IEnumerable<DateTime> EachMonth(DateTime from, DateTime thru)
{
    for (var month = from.Date; month.Date <= thru.Date || month.Month == thru.Month; month = month.AddMonths(1))
        yield return month;
}

public static IEnumerable<DateTime> EachDayTo(this DateTime dateFrom, DateTime dateTo)
{
    return EachDay(dateFrom, dateTo);
}

public static IEnumerable<DateTime> EachMonthTo(this DateTime dateFrom, DateTime dateTo)
{
    return EachMonth(dateFrom, dateTo);
}
12
Jacob Sobus
DateTime startDate = new DateTime(2009, 3, 10);
DateTime stopDate = new DateTime(2009, 3, 26);
int interval = 3;

for (DateTime dateTime=startDate;
     dateTime < stopDate; 
     dateTime += TimeSpan.FromDays(interval))
{

}
7
gilbertc

1 an plus tard, que cela aide quelqu'un,

Cette version inclut un predicate, pour être plus flexible.

Usage

var today = DateTime.UtcNow;
var birthday = new DateTime(2018, 01, 01);

Tous les jours à mon anniversaire

var toBirthday = today.RangeTo(birthday);  

Mensuel à mon anniversaire, étape 2 mois

var toBirthday = today.RangeTo(birthday, x => x.AddMonths(2));

Annuel à mon anniversaire

var toBirthday = today.RangeTo(birthday, x => x.AddYears(1));

Utilisez RangeFrom à la place

// same result
var fromToday = birthday.RangeFrom(today);
var toBirthday = today.RangeTo(birthday);

La mise en oeuvre

public static class DateTimeExtensions 
{

    public static IEnumerable<DateTime> RangeTo(this DateTime from, DateTime to, Func<DateTime, DateTime> step = null)
    {
        if (step == null)
        {
            step = x => x.AddDays(1);
        }

        while (from < to)
        {
            yield return from;
            from = step(from);
        }
    }

    public static IEnumerable<DateTime> RangeFrom(this DateTime to, DateTime from, Func<DateTime, DateTime> step = null)
    {
        return from.RangeTo(to, step);
    }
}

Extras

Vous pouvez lancer une exception si le fromDate > toDate, mais je préfère retourner une plage vide à la place []

6
amd
DateTime startDate = new DateTime(2009, 3, 10);
DateTime stopDate = new DateTime(2009, 3, 26);
int interval = 3;

while ((startDate = startDate.AddDays(interval)) <= stopDate)
{
    // do your thing
}
3
devnull

Vous pouvez envisager d'écrire un itérateur à la place, ce qui vous permet d'utiliser une syntaxe de boucle "pour" telle que "++". J'ai cherché et trouvé une question similaire répondu ici sur StackOverflow qui donne des indications pour rendre DateTime itérable.

1
REDace0

Selon le problème, vous pouvez essayer ceci ...

// looping between date range    
while (startDate <= endDate)
{
    //here will be your code block...

    startDate = startDate.AddDays(1);
}

merci......

1
Rejwanul Reja

Vous pouvez utiliser la fonction DateTime.AddDays() pour ajouter votre DayInterval à la StartDate et vous assurer qu'elle est inférieure à la EndDate.

1
TLiebe
DateTime begindate = Convert.ToDateTime("01/Jan/2018");
DateTime enddate = Convert.ToDateTime("12 Feb 2018");
while (begindate < enddate)
{
    begindate= begindate.AddDays(1);
    Console.WriteLine(begindate + "  " + enddate);
}
1
Sunil Joshi

vous devez faire attention ici pour ne pas manquer les dates où une meilleure solution serait dans la boucle.

cela vous donne la première date de date de début et l'utiliser dans la boucle avant de l'incrémenter et il traitera toutes les dates, y compris la dernière date de fin, donc <= date de fin.

la réponse ci-dessus est donc la bonne.

while (startdate <= enddate)
{
    // do something with the startdate
    startdate = startdate.adddays(interval);
}
0

vous pouvez l'utiliser.

 DateTime dt0 = new DateTime(2009, 3, 10);
 DateTime dt1 = new DateTime(2009, 3, 26);

 for (; dt0.Date <= dt1.Date; dt0=dt0.AddDays(3))
 {
    //Console.WriteLine(dt0.Date.ToString("yyyy-MM-dd"));
    //take action
 }
0
porya ras

@ jacob-sobus et @mquander et @Yogurt pas tout à fait correct .. Si j'ai besoin du lendemain, j'attends surtout 00:00 heure

    public static IEnumerable<DateTime> EachDay(DateTime from, DateTime thru)
    {
        for (var day = from.Date; day.Date <= thru.Date; day = day.NextDay())
            yield return day;
    }

    public static IEnumerable<DateTime> EachMonth(DateTime from, DateTime thru)
    {
        for (var month = from.Date; month.Date <= thru.Date || month.Year == thru.Year && month.Month == thru.Month; month = month.NextMonth())
            yield return month;
    }

    public static IEnumerable<DateTime> EachYear(DateTime from, DateTime thru)
    {
        for (var year = from.Date; year.Date <= thru.Date || year.Year == thru.Year; year = year.NextYear())
            yield return year;
    }

    public static DateTime NextDay(this DateTime date)
    {
        return date.AddTicks(TimeSpan.TicksPerDay - date.TimeOfDay.Ticks);
    }

    public static DateTime NextMonth(this DateTime date)
    {
        return date.AddTicks(TimeSpan.TicksPerDay * DateTime.DaysInMonth(date.Year, date.Month) - (date.TimeOfDay.Ticks + TimeSpan.TicksPerDay * (date.Day - 1)));
    }

    public static DateTime NextYear(this DateTime date)
    {
        var yearTicks = (new DateTime(date.Year + 1, 1, 1) - new DateTime(date.Year, 1, 1)).Ticks;
        var ticks = (date - new DateTime(date.Year, 1, 1)).Ticks;
        return date.AddTicks(yearTicks - ticks);
    }

    public static IEnumerable<DateTime> EachDayTo(this DateTime dateFrom, DateTime dateTo)
    {
        return EachDay(dateFrom, dateTo);
    }

    public static IEnumerable<DateTime> EachMonthTo(this DateTime dateFrom, DateTime dateTo)
    {
        return EachMonth(dateFrom, dateTo);
    }

    public static IEnumerable<DateTime> EachYearTo(this DateTime dateFrom, DateTime dateTo)
    {
        return EachYear(dateFrom, dateTo);
    }
0
DantaliaN

Itérer toutes les 15 minutes

DateTime startDate = DateTime.Parse("2018-06-24 06:00");
        DateTime endDate = DateTime.Parse("2018-06-24 11:45");

        while (startDate.AddMinutes(15) <= endDate)
        {

            Console.WriteLine(startDate.ToString("yyyy-MM-dd HH:mm"));
            startDate = startDate.AddMinutes(15);
        }
0
McNiel Viray