Bagaimana cara saya melewati rentang tanggal?


197

Saya bahkan tidak yakin bagaimana melakukan ini tanpa menggunakan beberapa jenis solusi loop / counter mengerikan. Inilah masalahnya:

Saya diberi dua tanggal, tanggal mulai dan tanggal berakhir dan pada interval yang ditentukan saya perlu mengambil tindakan. Misalnya: untuk setiap tanggal antara 3/10/2009 pada setiap hari ketiga hingga 3/26/2009 saya perlu membuat entri dalam Daftar. Jadi input saya adalah:

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

dan output saya akan menjadi daftar yang memiliki tanggal berikut:

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

Jadi bagaimana aku bisa melakukan hal seperti ini? Saya berpikir tentang menggunakan for for yang akan beralih antara setiap hari dalam rentang dengan penghitung terpisah seperti:

int count = 0;

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

}

Tapi sepertinya ada cara yang lebih baik?


1
Saya kira C # memiliki struktur data untuk tanggal yang dapat Anda gunakan.
Anna

Jawaban:


470

Nah, Anda harus mengulanginya dengan satu atau lain cara. Saya lebih suka mendefinisikan metode seperti ini:

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

Maka Anda dapat menggunakannya seperti ini:

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

Dengan cara ini, Anda dapat menekan setiap hari, setiap hari ketiga, hanya hari kerja, dll. Misalnya, untuk mengembalikan setiap hari ketiga dimulai dengan tanggal "mulai", Anda bisa menelepon saja AddDays(3)dalam loop AddDays(1).


18
Anda bahkan dapat menambahkan parameter lain untuk interval.
Justin Drury

Ini akan termasuk tanggal pertama. Jika Anda tidak menginginkannya, ubah saja 'var day = from.Date' menjadi 'var day = from.Date.AddDays (dayInterval)'
SwDevMan81

3
Solusi yang sangat bagus untuk masalah kata yang menarik dan nyata. Saya suka bagaimana ini menunjukkan beberapa teknik bahasa yang berguna. Dan ini mengingatkan saya bahwa untuk loop tidak hanya untuk (int i = 0; ...) (-.
Audrius

9
Membuat ini metode ekstensi untuk datetime mungkin membuatnya lebih baik.
Matte

1
Lihatlah jawaban saya untuk ekstensi selama berhari-hari dan berbulan-bulan;) Hanya untuk kesenangan Anda: D
Jacob Sobus

31

Saya memiliki Rangekelas di MiscUtil yang menurut Anda berguna. Dikombinasikan dengan berbagai metode ekstensi, Anda dapat melakukan:

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

(Anda mungkin atau mungkin tidak ingin mengecualikan akhir - saya hanya berpikir saya akan memberikannya sebagai contoh.)

Ini pada dasarnya adalah bentuk solusi mquander yang siap digulung (dan lebih bertujuan umum).


2
Tentunya hanya masalah selera apakah Anda suka hal seperti itu menjadi metode penyuluhan atau tidak. ExcludeEnd()imut.
mqp

Anda dapat, tentu saja, melakukan semua itu tanpa menggunakan metode ekstensi. Itu hanya akan menjadi jelek banyak dan sulit untuk dibaca IMO :)
Jon Skeet

1
Wow - betapa bagusnya sumber daya MiscUtil - terima kasih atas jawaban Anda!
onekidney

1
kalau-kalau ada orang lain kecuali saya mengira DayInterval sebagai struct / kelas, itu sebenarnya adalah bilangan bulat dalam sampel ini. itu tentu saja jelas jika Anda membaca pertanyaan dengan hati-hati, yang saya tidak lakukan.
marc.d

23

Sebagai contoh Anda dapat mencoba

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
Itu hal yang sama yang saya pikirkan (meskipun saya juga suka jawaban mquander di atas) tetapi saya tidak tahu bagaimana Anda mendapatkan sampel kode yang bagus yang diposting dengan begitu cepat!
TLiebe

3
Saya pikir kita perlu StartDate.AddDays (DayInterval); sekali dalam loop ini tidak dua kali.
Abdul Saboor

15

Kode dari @mquander dan @Yogurt The Wise digunakan dalam ekstensi:

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

Apa gunanya EachDayTodan EachMonthTo? Saya pikir saya melewatkan sesuatu di sini.
Alisson

@ Alisson itu adalah metode ekstensi yang bekerja pada objek DateFrom :) Jadi Anda dapat menggunakannya pada objek DateTime yang dibuat lebih lancar (menggunakan hanya. Setelah contoh). Lebih lanjut tentang metode ekstensi di sini: 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 Tahun kemudian, semoga membantu seseorang,

Versi ini termasuk predikat , agar lebih fleksibel.

Pemakaian

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

Setiap hari hingga ulang tahunku

var toBirthday = today.RangeTo(birthday);  

Bulanan hingga ulang tahun saya, Langkah 2 bulan

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

Setiap tahun hingga ulang tahunku

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

Gunakan RangeFromsebagai gantinya

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

Penerapan

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

Ekstra

Anda bisa melempar Pengecualian jika fromDate > toDate, tetapi saya lebih suka mengembalikan rentang kosong sebagai gantinya[]


Wow - ini sangat komprehensif. Terima kasih 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
}

Perhatikan bahwa ini tidak termasuk tanggal mulai, karena itu menambah hari saat pertama kali whileberjalan.
John Washam

2

Menurut masalah Anda dapat mencoba ini ...

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

    startDate = startDate.AddDays(1);
}

Terima kasih......


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

Anda dapat mempertimbangkan untuk menulis iterator, yang memungkinkan Anda untuk menggunakan sintaks loop normal 'untuk' seperti '++'. Saya mencari dan menemukan pertanyaan serupa dijawab di sini di StackOverflow yang memberikan petunjuk tentang membuat DateTime iterable.


1

Anda dapat menggunakan DateTime.AddDays()fungsi untuk menambahkan Anda DayIntervalke StartDatedan periksa untuk memastikan itu kurang dari EndDate.


0

Anda harus berhati-hati di sini untuk tidak melewatkan tanggal ketika dalam lingkaran solusi yang lebih baik.

ini memberi Anda tanggal pertama mulai dan menggunakannya dalam lingkaran sebelum menambahkannya dan itu akan memproses semua tanggal termasuk tanggal terakhir tanggal maka <= akhir.

jadi jawaban di atas adalah yang benar.

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

0

Anda bisa menggunakan ini.

 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
 }

Itu sangat ringkas. Bagus!
onekidney

0

Ulangi setiap 15 menit

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 dan @mquander dan @Yogurt tidak sepenuhnya benar .. Jika saya perlu hari berikutnya saya menunggu 00:00 waktu sebagian besar

    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

Inilah 2 sen saya pada tahun 2020.

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

Ini luar biasa. 👍
onekidney
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.