Datetime - Отримайте наступний вівторок


154

Як я можу отримати дату наступного вівторка?

У PHP це так просто, як strtotime('next tuesday');.

Як я можу досягти чогось подібного в .NET


15
ASP.NET - це набір веб-технологій. C # - мова. Вам дійсно потрібно продумати це з точки зору простого .NET. Тепер, для "наступного вівторка" - це "перший вівторок після сьогоднішнього дня"? Якби був понеділок, а хтось сказав "побачимось у наступний вівторок", я б очікував, що це означатиме час 8 днів, а не 1. А як бути, якщо сьогодні вівторок? Який час доби вам потрібен?
Джон Скіт

Якщо сьогодні вівторок, ви хочете знайти дату, коли наступний вівторок? Або сьогодні понеділок, ви хочете знайти другий вівторок з понеділка?
Пожежа Панда

Найближчий вівторок, що йде вперед, до якого-небудь конкретного дня.
brenjt

2
@brenjtL: А якщо вже вівторок?
Джон Скіт

Якщо вже у вівторок, то того самого дня
brenjt

Відповіді:


371

Як я вже згадував у коментарях, є різні речі, які ви могли б мати на увазі під «наступним вівторком», але цей код дає вам «наступний вівторок, або сьогодні, якщо це вже вівторок»:

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

Якщо ви хочете дати "тиждень часу", якщо вже вівторок, ви можете скористатися:

// 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;

... або ви можете використовувати оригінальну формулу, але вже від завтра:

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

EDIT: Просто, щоб зробити це приємним і універсальним:

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

Отже, щоб отримати значення для "сьогодні чи протягом наступних 6 днів":

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

Щоб отримати значення для "наступного вівторка без сьогоднішнього дня":

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

Нічого собі, мені було просто цікаво, як я міг би дістати n-й день до наступного вівторка, а потім ти оновив свою відповідь прикладом Ніцца. Дякую
brenjt

Важко було вибрати правильну відповідь. Але ваш, здається, найбільш універсальний, і ви зробили його легким для розуміння. Дякую за допомогу.
brenjt

1
@brenjt: Насправді я б сказав, що Sven's є більш універсальним, як ви можете вказати день тижня, але це ваш дзвінок :) (Я вже редагував мою, щоб дати більш узагальнену версію.)
Джон Скіт,

1
+7)%7Рішення досить приємно , хоча. Хоча я цього не використовував, це те, що це мікро-оптимізація і занадто легко помилитися (а також принести шкоду читання), звичайно.
Свен

Тест одиниці: [TestMethod] public void ShouldGetNextSaturday () {var now = DateTime.Now; var test = GetNextWeekday (DateTime.Today, DayOfWeek.Saturday); Assert.IsTrue (зараз. День <тест.День, "Очікуваний місячний день тут не буває."); Assert.IsTrue (test.DayOfWeek == DayOfWeek.Saturday, "Очікуваний тиждень не тут."); Assert.IsTrue ((test.Day - now.Day) <7, "Очікуваний інтервал дня тут немає"); }
rasx

67

Для цього слід зробити фокус:

static DateTime GetNextWeekday(DayOfWeek day)
{
    DateTime result = DateTime.Now.AddDays(1);
    while( result.DayOfWeek != day )
        result = result.AddDays(1);
    return result;
}

Прекрасна відповідь, якщо сьогодні вівторок (який він є) це повернеться сьогодні чи наступного вівторка?
brenjt

3
Це повернеться наступного вівторка. Якщо ви хочете, щоб він повернувся сьогодні, просто видаліть .AddDays(1)з першого рядка, таким чином він також перевірить DateTime.Nowсебе.
Свен

7

Існує менше багатослівних та більш розумних / елегантних рішень цієї проблеми, але наведена нижче функція C # справді добре працює в ряді ситуацій.

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

1
Єдина відповідь, яка реалізується як розширення до DateTime. Хоча інші рішення працюють, використання методу розширення створює найпростіший у використанні код.
Райан Макартур

5
DateTime nextTuesday = DateTime.Today.AddDays(((int)DateTime.Today.DayOfWeek - (int)DayOfWeek.Tuesday) + 7);

Якщо сьогодні понеділок, то відповідь, яку ви надали, дасть тиждень із вівторка, а не завтра.
Тоні

5

@Jon Skeet хороша відповідь.

За попередній день:

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

Дякую!!


Зауважте, що це рішення включає негативні числа, передані оператору модуля. У статті Вікіпедії про оператор модуля йдеться про те, що "коли або a, або n є негативним, наївне визначення розпадається, а мови програмування відрізняються тим, як визначаються ці значення". Хоча це, мабуть, працює в C #, математично більш «твердим» рішенням, щоб отримати той самий результат, було б поміняти такі DayOfWeekзначення, як це:int daysToSubtract = -(((int)dateTime.DayOfWeek - (int)day + 7) % 7);
Андре

4
DateTime nexttuesday=DateTime.Today.AddDays(1);

while(nexttuesday.DayOfWeek!=DayOfWeek.Tuesday)
   nexttuesday = nexttuesday.AddDays(1);

3

Дуже простий зразок для включення або виключення поточної дати, ви вказуєте дату та день тижня, який вас цікавить.

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

деякі тестові випадки

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

Я вніс додаткові зміни в оригінальну відповідь після деякого обширного тестування. Тепер це буде безпечно обчислювати наступний день на основі використаної дати, минулого, сьогодення та майбутнього. Всі попередні приклади, де чудово, але не вдалося за певних умов. Я не робив це односкладовим твердженням, щоб можна було зробити додаткові коментарі до того, що роблять розрахунки. Позитивний випадок Джона Скіта був чудовим, хоча справа, яку я мав, переїхала на 1 день назад від дати, але все-таки більша, ніж сьогодні, і що, якщо вона переїде на сьогодні чи на вчора ... це вирішило це.
AJB

1

Це також може бути продовженням, все залежить

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

Використання

    var today = DateTime.Today;
    foreach(var monday in today.Next(DayOfWeek.Monday))
    {
        Console.WriteLine(monday);
        Console.ReadKey();
    }

0

Тепер в ароматі oneliner - якщо вам потрібно передати його як параметр в якийсь механізм.

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

У цьому конкретному випадку:

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

-5

Ціль C Версія:

+(NSInteger) daysUntilNextWeekday: (NSDate*)startDate withTargetWeekday: (NSInteger) targetWeekday
{
    NSInteger startWeekday = [[NSCalendar currentCalendar] component:NSCalendarUnitWeekday fromDate:startDate];
    return (targetWeekday - startWeekday + 7) % 7;
}

4
Класна відповідь, але оригінальне запитання про .NET.
Адам Девіс
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.