Datetime - pobierz następny wtorek

154

Jak mogę sprawdzić datę następnego wtorku?

W PHP, to takie proste strtotime('next tuesday');.

Jak mogę osiągnąć coś podobnego w .NET

brenjt
źródło
15
ASP.NET to zestaw technologii internetowych. C # to język. Naprawdę musisz pomyśleć o tym w kategoriach zwykłego .NET. A teraz „następny wtorek” - czy to „pierwszy wtorek po dzisiejszym dniu”? Gdyby był poniedziałek i ktoś powiedziałby „do zobaczenia w przyszły wtorek”, spodziewałbym się, że będzie to oznaczać 8 dni zamiast 1. A co jeśli dzisiaj jest wtorek? O jakiej porze dnia potrzebujesz?
Jon Skeet
Jeśli dzisiaj jest wtorek, chcesz znaleźć datę, kiedy jest następny wtorek? Lub dzisiaj jest poniedziałek, chcesz znaleźć drugi wtorek od poniedziałku?
FIre Panda
Najbliższy wtorek, który kiedykolwiek się pojawił.
brenjt
2
@brenjtL: A jeśli jest już wtorek?
Jon Skeet
Jeśli już we wtorek, to tego samego dnia
brenjt

Odpowiedzi:

371

Jak wspomniałem w komentarzach, jest wiele rzeczy, które możesz rozumieć przez „następny wtorek”, ale ten kod podaje „następny wtorek, który ma nastąpić lub dzisiaj, jeśli jest już wtorek”:

DateTime today = DateTime.Today;
// The (... + 7) % 7 ensures we end up with a value in the range [0, 6]
int daysUntilTuesday = ((int) DayOfWeek.Tuesday - (int) today.DayOfWeek + 7) % 7;
DateTime nextTuesday = today.AddDays(daysUntilTuesday);

Jeśli chcesz podać „tydzień czasu”, jeśli jest już wtorek, możesz użyć:

// This finds the next Monday (or today if it's Monday) and then adds a day... so the
// result is in the range [1-7]
int daysUntilTuesday = (((int) DayOfWeek.Monday - (int) today.DayOfWeek + 7) % 7) + 1;

... lub możesz użyć oryginalnej formuły, ale od jutra:

DateTime tomorrow = DateTime.Today.AddDays(1);
// The (... + 7) % 7 ensures we end up with a value in the range [0, 6]
int daysUntilTuesday = ((int) DayOfWeek.Tuesday - (int) tomorrow.DayOfWeek + 7) % 7;
DateTime nextTuesday = tomorrow.AddDays(daysUntilTuesday);

EDYCJA: Po prostu, aby to było ładne i wszechstronne:

public static DateTime GetNextWeekday(DateTime start, DayOfWeek day)
{
    // The (... + 7) % 7 ensures we end up with a value in the range [0, 6]
    int daysToAdd = ((int) day - (int) start.DayOfWeek + 7) % 7;
    return start.AddDays(daysToAdd);
}

Aby uzyskać wartość dla „dzisiaj lub w ciągu najbliższych 6 dni”:

DateTime nextTuesday = GetNextWeekday(DateTime.Today, DayOfWeek.Tuesday);

Aby uzyskać wartość dla „następnego wtorku z wyłączeniem dzisiaj”:

DateTime nextTuesday = GetNextWeekday(DateTime.Today.AddDays(1), DayOfWeek.Tuesday);
Jon Skeet
źródło
Wow, zastanawiałem się tylko, jak mogę uzyskać n-te dni do następnego wtorku, a następnie zaktualizowałeś swoją odpowiedź przykładem Nice. Dzięki
brenjt
Trudno było wybrać poprawną odpowiedź. Ale Twój wydaje się być najbardziej wszechstronny i ułatwiłeś zrozumienie. Dzięki za pomoc.
brenjt
1
@brenjt: Właściwie powiedziałbym, że Sven jest bardziej wszechstronny, ponieważ możesz określić dzień tygodnia, ale to twoja decyzja :) (Teraz zredagowałem moją, aby dać bardziej ogólną wersję.)
Jon Skeet
1
+7)%7Rozwiązanie jest całkiem ładna. Chociaż powodem, dla którego go nie użyłem, jest to, że jest to trochę mikro-optymalizacja i zbyt łatwo się pomylić (a także poświęcić trochę czytelności), oczywiście imho.
Sven
Test jednostkowy: [TestMethod] public void ShouldGetNextSaturday () {var now = DateTime.Now; var test = GetNextWeekday (DateTime.Today, DayOfWeek.Saturday); Assert.IsTrue (now.Day <test.Day, "Nie ma oczekiwanego dnia miesiąca."); Assert.IsTrue (test.DayOfWeek == DayOfWeek.Saturday, "Nie ma oczekiwanego dnia tygodnia."); Assert.IsTrue ((test.Day - now.Day) <7, "Nie ma oczekiwanego przedziału dziennego."); }
rasx,
67

To powinno załatwić sprawę:

static DateTime GetNextWeekday(DayOfWeek day)
{
    DateTime result = DateTime.Now.AddDays(1);
    while( result.DayOfWeek != day )
        result = result.AddDays(1);
    return result;
}
Sven
źródło
Świetna odpowiedź, jeśli dzisiaj jest wtorek (czyli ha) to powróci dzisiaj, czy w następny wtorek?
brenjt
3
To powróci w następny wtorek. Jeśli chcesz, aby powrócił dzisiaj, po prostu usuń .AddDays(1)z pierwszej linii, w ten sposób również się sprawdzi DateTime.Now.
Sven
7

Istnieją mniej rozwlekłe i bardziej sprytne / eleganckie rozwiązania tego problemu, ale poniższa funkcja C # działa naprawdę dobrze w wielu sytuacjach.

/// <summary>
/// Find the closest weekday to the given date
/// </summary>
/// <param name="includeStartDate">if the supplied date is on the specified day of the week, return that date or continue to the next date</param>
/// <param name="searchForward">search forward or backward from the supplied date. if a null parameter is given, the closest weekday (ie in either direction) is returned</param>
public static DateTime ClosestWeekDay(this DateTime date, DayOfWeek weekday, bool includeStartDate = true, bool? searchForward=true)
{
    if (!searchForward.HasValue && !includeStartDate) 
    {
        throw new ArgumentException("if searching in both directions, start date must be a valid result");
    }
    var day = date.DayOfWeek;
    int add = ((int)weekday - (int)day);
    if (searchForward.HasValue)
    {
        if (add < 0 && searchForward.Value)
        {
            add += 7;
        }
        else if (add > 0 && !searchForward.Value)
        {
            add -= 7;
        }
        else if (add == 0 && !includeStartDate)
        {
            add = searchForward.Value ? 7 : -7;
        }
    }
    else if (add < -3) 
    {
        add += 7; 
    }
    else if (add > 3)
    {
        add -= 7;
    }
    return date.AddDays(add);
}
Brent
źródło
1
Jedyna odpowiedź, która implementuje jako rozszerzenie do DateTime. Podczas gdy wszystkie inne rozwiązania działają, użycie go jako metody rozszerzenia daje najłatwiejszy w użyciu kod.
Ryan McArthur
5
DateTime nextTuesday = DateTime.Today.AddDays(((int)DateTime.Today.DayOfWeek - (int)DayOfWeek.Tuesday) + 7);
Błazen
źródło
Jeśli dzisiaj jest poniedziałek, odpowiedź, którą podałeś, będzie miała tydzień od wtorku, a nie jutro.
Tony
5

@Jon Skeet dobra odpowiedź.

Za poprzedni dzień:

private DateTime GetPrevWeekday(DateTime start, DayOfWeek day) {
    // The (... - 7) % 7 ensures we end up with a value in the range [0, 6]
    int daysToRemove = ((int) day - (int) start.DayOfWeek - 7) % 7;
    return start.AddDays(daysToRemove);
}

Dzięki!!

Anik Islam Abhi
źródło
Zwróć uwagę, że to rozwiązanie obejmuje liczby ujemne przekazywane operatorowi modulo. Artykuł w Wikipedii dotyczący operatora modulo mówi, że „Gdy a lub n jest ujemne, naiwna definicja załamuje się, a języki programowania różnią się sposobem definiowania tych wartości”. Chociaż prawdopodobnie działa to w C #, matematycznie bardziej „solidnym” rozwiązaniem, aby uzyskać ten sam wynik, byłaby zamiana DayOfWeekwartości w następujący sposób:int daysToSubtract = -(((int)dateTime.DayOfWeek - (int)day + 7) % 7);
Andre
4
DateTime nexttuesday=DateTime.Today.AddDays(1);

while(nexttuesday.DayOfWeek!=DayOfWeek.Tuesday)
   nexttuesday = nexttuesday.AddDays(1);
Blindy
źródło
3

Bardzo prosta próbka do uwzględnienia lub wykluczenia aktualnej daty, określasz datę i dzień tygodnia, który Cię interesuje.

public static class DateTimeExtensions
{
    /// <summary>
    /// Gets the next date.
    /// </summary>
    /// <param name="date">The date to inspected.</param>
    /// <param name="dayOfWeek">The day of week you want to get.</param>
    /// <param name="exclDate">if set to <c>true</c> the current date will be excluded and include next occurrence.</param>
    /// <returns></returns>
    public static DateTime GetNextDate(this DateTime date, DayOfWeek dayOfWeek, bool exclDate = true)
    {
        //note: first we need to check if the date wants to move back by date - Today, + diff might move it forward or backwards to Today
        //eg: date - Today = 0 - 1 = -1, so have to move it forward
        var diff = dayOfWeek - date.DayOfWeek;
        var ddiff = date.Date.Subtract(DateTime.Today).Days + diff;

        //note: ddiff < 0 : date calculates to past, so move forward, even if the date is really old, it will just move 7 days from date passed in
        //note: ddiff >= (exclDate ? 6 : 7) && diff < 0 : date is into the future, so calculated future weekday, based on date
        if (ddiff < 0 || ddiff >= (exclDate ? 6 : 7) && diff < 0)
            diff += 7; 

        //note: now we can get safe values between 0 - 6, especially if past dates is being used
        diff = diff % 7;

        //note: if diff is 0 and we are excluding the date passed, we will add 7 days, eg: 1 week
        diff += diff == 0 & exclDate ? 7 : 0;

        return date.AddDays(diff);
    }
}

niektóre przypadki testowe

[TestMethod]
    public void TestNextDate()
    {
        var date = new DateTime(2013, 7, 15);
        var start = date;
        //testing same month - forwardOnly
        Assert.AreEqual(start = start.AddDays(1), date.GetNextDate(DayOfWeek.Tuesday)); //16
        Assert.AreEqual(start = start.AddDays(1), date.GetNextDate(DayOfWeek.Wednesday)); //17
        Assert.AreEqual(start = start.AddDays(1), date.GetNextDate(DayOfWeek.Thursday)); //18
        Assert.AreEqual(start = start.AddDays(1), date.GetNextDate(DayOfWeek.Friday)); //19
        Assert.AreEqual(start = start.AddDays(1), date.GetNextDate(DayOfWeek.Saturday)); //20
        Assert.AreEqual(start = start.AddDays(1), date.GetNextDate(DayOfWeek.Sunday)); //21
        Assert.AreEqual(start.AddDays(1), date.GetNextDate(DayOfWeek.Monday)); //22

        //testing same month - include date
        Assert.AreEqual(start = date, date.GetNextDate(DayOfWeek.Monday, false)); //15
        Assert.AreEqual(start = start.AddDays(1), date.GetNextDate(DayOfWeek.Tuesday, false)); //16
        Assert.AreEqual(start.AddDays(1), date.GetNextDate(DayOfWeek.Wednesday, false)); //17

        //testing month change - forwardOnly
        date = new DateTime(2013, 7, 29);
        start = date;
        Assert.AreEqual(start = start.AddDays(1), date.GetNextDate(DayOfWeek.Tuesday)); //30
        Assert.AreEqual(start = start.AddDays(1), date.GetNextDate(DayOfWeek.Wednesday)); //31
        Assert.AreEqual(start = start.AddDays(1), date.GetNextDate(DayOfWeek.Thursday)); //2013/09/01-month increased
        Assert.AreEqual(start.AddDays(1), date.GetNextDate(DayOfWeek.Friday)); //02

        //testing year change
        date = new DateTime(2013, 12, 30);
        start = date;
        Assert.AreEqual(start = start.AddDays(1), date.GetNextDate(DayOfWeek.Tuesday)); //31
        Assert.AreEqual(start = start.AddDays(1), date.GetNextDate(DayOfWeek.Wednesday)); //2014/01/01 - year increased
        Assert.AreEqual(start = start.AddDays(1), date.GetNextDate(DayOfWeek.Thursday)); //02
    }
AJB
źródło
Po obszernych testach wprowadziłem dodatkowe zmiany w stosunku do oryginalnej odpowiedzi. To teraz bezpiecznie obliczy następny dzień na podstawie użytej daty, przeszłości, teraźniejszości i przyszłości. Wszystkie poprzednie przykłady były świetne, ale zawiodły w pewnych warunkach. Nie uczyniłem tego stwierdzeniem jednowierszowym, aby można było poczynić dodatkowe uwagi na temat tego, co robią obliczenia. Pozytywny przypadek Jona Skeeta był świetny, chociaż przypadek, który miałem, to przesunięcie się o 1 dzień wstecz od daty, ale wciąż większe niż dzisiaj, a co, jeśli przeniesie się na dziś lub na wczoraj ... to rozwiązało problem.
AJB
1

Może to być również rozszerzenie, wszystko zależy

public static class DateTimeExtensions
{
    public static IEnumerable<DateTime> Next(this DateTime date, DayOfWeek day)
    {
        // This loop feels expensive and useless, but the point is IEnumerable
        while(true)
        {
            if (date.DayOfWeek == day)
            {
                yield return date;
            }
            date = date.AddDays(1);
        }
    }
}

Stosowanie

    var today = DateTime.Today;
    foreach(var monday in today.Next(DayOfWeek.Monday))
    {
        Console.WriteLine(monday);
        Console.ReadKey();
    }
Alex Nolasco
źródło
0

Teraz w smaku oneliner - na wypadek, gdyby trzeba było przekazać go jako parametr do jakiegoś mechanizmu.

DateTime.Now.AddDays(((int)yourDate.DayOfWeek - (int)DateTime.Now.DayOfWeek + 7) % 7).Day

W tym konkretnym przypadku:

DateTime.Now.AddDays(((int)DayOfWeek.Tuesday - (int)DateTime.Now.DayOfWeek + 7) % 7).Day
Matas Vaitkevicius
źródło
-5

Wersja celu C:

+(NSInteger) daysUntilNextWeekday: (NSDate*)startDate withTargetWeekday: (NSInteger) targetWeekday
{
    NSInteger startWeekday = [[NSCalendar currentCalendar] component:NSCalendarUnitWeekday fromDate:startDate];
    return (targetWeekday - startWeekday + 7) % 7;
}
Max Hiroyuki Ueda
źródło
4
Fajna odpowiedź, ale pierwotne pytanie dotyczyło .NET.
Adam Davis