Làm thế nào để tôi lặp qua một phạm vi ngày?


196

Tôi thậm chí không chắc chắn làm thế nào để làm điều này mà không sử dụng một số giải pháp khủng khiếp cho vòng lặp / bộ đếm. Đây là vấn đề:

Tôi được đưa ra hai ngày, ngày bắt đầu và ngày kết thúc và trong một khoảng thời gian xác định tôi cần thực hiện một số hành động. Ví dụ: cho mỗi ngày từ 3/10/2009 vào mỗi ngày thứ ba cho đến 26/03/2009 tôi cần tạo một mục trong Danh sách. Vì vậy, đầu vào của tôi sẽ là:

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

và đầu ra của tôi sẽ là một danh sách có các ngày sau:

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

Vì vậy, làm thế quái nào tôi sẽ làm một cái gì đó như thế này? Tôi đã nghĩ về việc sử dụng một vòng lặp for sẽ lặp lại giữa mỗi ngày trong phạm vi với một bộ đếm riêng như vậy:

int count = 0;

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

}

Nhưng có vẻ như có thể có một cách tốt hơn?


1
Tôi đoán C # có cấu trúc dữ liệu cho ngày mà bạn có thể sử dụng.
Anna

Câu trả lời:


468

Chà, bạn sẽ cần phải lặp đi lặp lại theo cách này hay cách khác. Tôi thích xác định một phương thức như thế này:

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

Sau đó, bạn có thể sử dụng nó như thế này:

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

Theo cách này, bạn có thể đánh mỗi ngày, mỗi ngày thứ ba, chỉ các ngày trong tuần, v.v. Ví dụ, để trở về mỗi ngày thứ ba bắt đầu bằng ngày "bắt đầu", bạn chỉ có thể gọi AddDays(3)trong vòng lặp thay vì AddDays(1).


18
Bạn thậm chí có thể thêm một tham số khác cho khoảng thời gian.
Justin Drury

Điều này sẽ bao gồm ngày đầu tiên. Nếu bạn không muốn điều đó, chỉ cần thay đổi 'var day = from.Date' thành 'var day = from.Date.AddDays (dayInterval)'
SwDevMan81

3
Một giải pháp thực sự tốt đẹp cho một vấn đề từ thú vị và thực sự. Tôi thích cách này cho thấy khá nhiều kỹ thuật hữu ích của ngôn ngữ. Và điều này nhắc nhở tôi rằng vòng lặp for không chỉ dành cho (int i = 0; ...) (-.
Audrius

9
Làm cho điều này trở thành một phương thức mở rộng thành datetime có thể làm cho nó thậm chí còn tốt hơn.
Mờ

1
Hãy xem câu trả lời của tôi cho các phần mở rộng trong nhiều ngày và tháng;) Chỉ vì niềm vui của bạn: D
Jacob Sobus 5/2/2015

30

Tôi có một Rangelớp học ở MiscUtil mà bạn có thể thấy hữu ích. Kết hợp với các phương thức mở rộng khác nhau, bạn có thể làm:

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

(Bạn có thể muốn hoặc không muốn loại trừ kết thúc - Tôi chỉ nghĩ tôi sẽ cung cấp nó làm ví dụ.)

Về cơ bản, đây là một hình thức giải pháp sẵn sàng (và có mục đích chung hơn).


2
Chắc chắn chỉ là vấn đề của hương vị cho dù bạn có thích những thứ như vậy là phương pháp mở rộng hay không. ExcludeEnd()dễ thương.
mqp

Tất nhiên, bạn có thể làm tất cả điều đó mà không cần sử dụng các phương thức mở rộng. Nó sẽ trở nên xấu hơn và khó đọc hơn IMO :)
Jon Skeet

1
Wow - thật là một tài nguyên tuyệt vời MiscUtil là - cảm ơn câu trả lời của bạn!
onekidney

1
trong trường hợp có ai khác ngoài tôi nhầm DayInterval là struct / class, nó thực sự là một số nguyên trong mẫu này. Tất nhiên là rõ ràng nếu bạn đọc kỹ câu hỏi, điều mà tôi đã không làm.
marc.d

23

Ví dụ của bạn, bạn có thể thử

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);
}

1
Đó là điều tương tự tôi đã nghĩ (mặc dù tôi cũng thích câu trả lời của mquander) nhưng tôi không thể hiểu làm thế nào bạn có được một mẫu mã đẹp được đăng nhanh như vậy!
TLiebe

3
Tôi nghĩ rằng chúng ta cần StartDate.AddDays (DayInterval); một lần trong vòng lặp này không hai lần.
Abdul Saboor

15

Mã từ @mquander và @Yogurt Wise được sử dụng trong các tiện ích mở rộng:

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);
}

Điểm của EachDayTovà là EachMonthTogì? Tôi nghĩ rằng tôi đã bỏ lỡ một cái gì đó ở đây.
Alisson

@Alisson đó là các phương thức mở rộng hoạt động trên đối tượng dateFrom :) Vì vậy, bạn có thể sử dụng chúng trên các đối tượng DateTime được tạo thành thạo hơn (chỉ sử dụng sau ví dụ). Tìm hiểu thêm về các phương thức mở rộng tại đây: docs.microsoft.com/en-us/dotnet/csharp/programming-guide/ mẹo
Jacob Sobus 19/12/17

8
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))
{

}

8

1 năm sau, nó có thể giúp được ai đó không

Phiên bản này bao gồm một vị ngữ , để linh hoạt hơn.

Sử dụng

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

Hàng ngày đến sinh nhật của tôi

var toBirthday = today.RangeTo(birthday);  

Hàng tháng đến sinh nhật tôi, Bước 2 tháng

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

Hàng năm đến sinh nhật của tôi

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

Sử dụng RangeFromthay thế

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

Thực hiện

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);
    }
}

Ngoài ra

Bạn có thể ném Ngoại lệ nếu fromDate > toDate, nhưng tôi thích trả về một phạm vi trống thay thế[]


Wow - điều này thực sự toàn diện. Cảm ơn Ahmad!
onekidney

3
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
}

Lưu ý rằng điều này không bao gồm ngày bắt đầu, vì nó thêm một ngày vào lần whilechạy đầu tiên .
John Washam

2

Theo vấn đề bạn có thể thử ...

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

    startDate = startDate.AddDays(1);
}

cảm ơn......


2
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

Thay vào đó, bạn có thể xem xét việc viết một trình vòng lặp, điều này cho phép bạn sử dụng cú pháp vòng lặp 'for' bình thường như '++'. Tôi đã tìm kiếm và tìm thấy một câu hỏi tương tự được trả lời ở đây trên StackOverflow, nó đưa ra các gợi ý về việc lặp lại DateTime.


1

Bạn có thể sử dụng DateTime.AddDays()chức năng để thêm của bạn DayIntervalvào StartDatevà kiểm tra để đảm bảo rằng nó nhỏ hơn EndDate.


0

bạn phải cẩn thận ở đây để không bỏ lỡ ngày khi trong vòng lặp một giải pháp tốt hơn sẽ là.

điều này cung cấp cho bạn ngày bắt đầu đầu tiên và sử dụng nó trong vòng lặp trước khi tăng nó và nó sẽ xử lý tất cả các ngày kể cả ngày kết thúc cuối cùng do đó <= enddate.

Vì vậy, câu trả lời trên là câu trả lời đúng.

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

0

bạn có thể sử dụng cái này

 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
 }

Điều đó thực sự cô đọng. Đẹp!
onekidney

0

Lặp lại cứ sau 15 phút

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

@ jacob-sobus và @mquander và @Yogurt không chính xác hoàn toàn .. Nếu tôi cần vào ngày hôm sau, tôi sẽ đợi 00:00 thời gian

    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

Đây là 2 xu của tôi vào năm 2020.

Enumerable.Range(0, (endDate - startDate).Days + 1)
.ToList()
.Select(a => startDate.AddDays(a));

Điều này thật tuyệt. 👍
onekidney
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.