ฉันจะวนลูปผ่านช่วงวันที่ได้อย่างไร


198

ฉันไม่แน่ใจด้วยซ้ำว่าจะทำเช่นนี้โดยไม่ใช้โซลูชันที่น่ากลัวสำหรับลูป / เคาน์เตอร์ นี่คือปัญหา:

ฉันได้รับวันที่สองวันที่เริ่มต้นและวันที่สิ้นสุดและตามช่วงเวลาที่ระบุฉันต้องดำเนินการบางอย่าง ตัวอย่างเช่น: สำหรับทุกวันที่ระหว่าง 3/10/2552 ในทุก ๆ วันที่สามจนถึงวันที่ 3/26/2009 ฉันต้องสร้างรายการในรายการ ดังนั้นอินพุตของฉันจะเป็น:

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

และผลลัพธ์ของฉันจะเป็นรายการที่มีวันที่ดังต่อไปนี้:

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

แล้วผมจะทำยังไงกับเรื่องนี้ ฉันคิดเกี่ยวกับการใช้ for for loop ที่จะวนซ้ำระหว่างทุกวันในช่วงที่มีตัวนับแยกเช่น:

int count = 0;

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

}

แต่ดูเหมือนว่าจะมีวิธีที่ดีกว่า


1
ฉันเดา C # มีโครงสร้างข้อมูลสำหรับวันที่คุณสามารถใช้
Anna

คำตอบ:


471

คุณจะต้องวนซ้ำพวกเขาไม่ทางใดก็ทางหนึ่ง ฉันชอบกำหนดวิธีเช่นนี้

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

จากนั้นคุณสามารถใช้สิ่งนี้:

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

ในลักษณะนี้คุณสามารถเข้าชมทุกวัน ๆ ทุกวันที่สามเพียงวันธรรมดา ฯลฯ ตัวอย่างเช่นหากต้องการกลับมาทุกวันที่สามเริ่มต้นด้วยวันที่ "เริ่มต้น" คุณสามารถโทรหาAddDays(3)ลูปแทนAddDays(1)ได้


18
คุณสามารถเพิ่มพารามิเตอร์อื่นสำหรับช่วงเวลา
จัสตินดรูรี่

ซึ่งจะรวมถึงวันแรก หากคุณไม่ต้องการให้เปลี่ยน 'var day = from.Date' เป็น 'var day = from.Date.AddDays (dayInterval)'
SwDevMan81

3
ทางออกที่ดีจริงๆสำหรับปัญหาคำศัพท์ที่น่าสนใจและแท้จริง ฉันชอบวิธีนี้แสดงให้เห็นถึงเทคนิคที่มีประโยชน์ค่อนข้างน้อยของภาษา และนี่เตือนฉันว่าสำหรับลูปไม่เพียง แต่สำหรับ (int i = 0; ... ) (-.
Audrius

9
การทำให้นี่เป็นวิธีส่วนขยายถึงวันที่และเวลาอาจทำให้ดีขึ้นได้
MatteS

1
ดูคำตอบของฉันสำหรับส่วนขยายสำหรับวันและเดือน;) เพียงเพื่อความสุขของคุณ: D
Jacob Sobus

31

ฉันมีRangeชั้นเรียนในMiscUtilซึ่งคุณจะพบว่ามีประโยชน์ เมื่อรวมกับวิธีการขยายต่าง ๆ คุณสามารถทำได้:

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

(คุณอาจหรืออาจไม่ต้องการยกเว้นจุดจบ - ฉันแค่คิดว่าฉันจะให้เป็นตัวอย่าง)

นี่เป็นรูปแบบการแก้ปัญหาของ mquander ที่พร้อมใช้งาน


2
แน่นอนว่าเป็นเรื่องของรสนิยมไม่ว่าคุณจะชอบวิธีดังกล่าวหรือไม่ก็ตาม ExcludeEnd()น่ารัก.
mqp

แน่นอนว่าคุณสามารถทำได้ทั้งหมดโดยไม่ต้องใช้วิธีการขยาย มันก็จะเป็นไม่สวยงามเท่าจำนวนมากและยากที่จะอ่าน IMO :)
จอนสกีต

1
ว้าว - MiscUtil เป็นทรัพยากรที่ยอดเยี่ยมอะไร - ขอบคุณสำหรับคำตอบของคุณ!
onekidney

1
ในกรณีที่มีคนอื่น แต่ฉันเข้าใจผิดว่า DayInterval เป็น struct / class จริง ๆ แล้วมันเป็นจำนวนเต็มในตัวอย่างนี้ แน่นอนว่าถ้าคุณอ่านคำถามอย่างระมัดระวังซึ่งฉันไม่ได้ทำ
marc.d

23

สำหรับตัวอย่างของคุณคุณสามารถลอง

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
นั่นคือสิ่งเดียวกันกับที่ฉันคิด (แม้ว่าฉันชอบคำตอบของ mquander ด้านบน) แต่ฉันไม่สามารถหาวิธีที่คุณได้รับตัวอย่างโค้ดที่ดีที่โพสต์อย่างรวดเร็ว!
TLiebe

3
ฉันคิดว่าเราต้องการ StartDate.AddDays (DayInterval); หนึ่งครั้งในลูปนี้ไม่ใช่สองครั้ง
Abdul Saboor

15

รหัสจาก @mquander และ @Yogurt The Wise ที่ใช้ในส่วนขยาย:

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

จุดประสงค์คืออะไรEachDayToและEachMonthTo? ฉันคิดว่าฉันพลาดอะไรบางอย่างที่นี่
Alisson

@Alisson เหล่านั้นเป็นวิธีการขยายการทำงานกับวัตถุ dateFrom :) ดังนั้นคุณสามารถใช้พวกเขาแล้วในวัตถุ DateTime ที่สร้างขึ้นได้อย่างคล่องแคล่ว (ใช้เพียงหลังจาก.) เพิ่มเติมเกี่ยวกับวิธีการขยายที่นี่: docs.microsoft.com/en-us/dotnet/csharp/programming-guide/…
Jacob Sobus

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 ปีต่อมาขอความช่วยเหลือจากใครบางคน

รุ่นนี้รวมถึง แสดงที่จะมีความยืดหยุ่นมากขึ้น

การใช้

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

ทุกวันถึงวันเกิดของฉัน

var toBirthday = today.RangeTo(birthday);  

รายเดือนถึงวันเกิดของฉันขั้นตอนที่ 2 เดือน

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

ประจำปีวันเกิดของฉัน

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

ใช้RangeFromแทน

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

การดำเนินงาน

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

พิเศษ

คุณสามารถโยนข้อยกเว้นfromDate > toDateได้ แต่ฉันต้องการคืนค่าช่วงที่ว่างแทน[]


ว้าว - นี่มันครอบคลุมจริงๆ ขอบคุณ 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
}

โปรดทราบว่านี่ไม่รวมวันที่เริ่มต้นเนื่องจากจะเพิ่มวันที่ครั้งแรกที่whileเรียกใช้
John Washam

2

ตามปัญหาคุณสามารถลอง ...

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

    startDate = startDate.AddDays(1);
}

ขอบคุณ ......


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

คุณอาจลองเขียนตัววนซ้ำแทนซึ่งจะอนุญาตให้คุณใช้ 'for' syntax แบบวนรอบเช่น '++' ปกติ ฉันค้นหาและพบคำถามที่คล้ายกันตอบที่นี่ใน StackOverflow ซึ่งให้พอยน์เตอร์ในการทำให้ DateTime iterable


1

คุณสามารถใช้ DateTime.AddDays()ฟังก์ชั่นการเพิ่มของคุณDayIntervalไปและตรวจสอบเพื่อให้แน่ใจว่ามันจะน้อยกว่าStartDateEndDate


0

คุณต้องระวังที่นี่อย่าพลาดวันที่เมื่ออยู่ในลูปทางออกที่ดีกว่าก็จะเป็น

สิ่งนี้จะให้วันที่เริ่มต้นวันที่เริ่มต้นของคุณและใช้ในการวนซ้ำก่อนที่จะเพิ่มขึ้นและจะประมวลผลวันที่ทั้งหมดรวมถึงวันสุดท้ายของการลงวันที่ด้วยเหตุนี้ <= enddate

ดังนั้นคำตอบข้างต้นคือคำตอบที่ถูกต้อง

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

0

คุณสามารถใช้สิ่งนี้

 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
 }

มันรวบรัดจริงๆ ดี!
onekidney

0

ทำซ้ำทุก ๆ 15 นาที

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 และ @mquander และ @Yogurt ไม่ถูกต้อง .. หากฉันต้องการในวันถัดไปฉันจะรอเวลา 00:00 ส่วนใหญ่

    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

นี่คือ 2 เซ็นต์ของฉันในปี 2020

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

นี่มันเจ๋งมาก. 👍
onekidney
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.