Datetime - Obtenga el próximo martes

154

¿Cómo puedo obtener la fecha del próximo martes?

En PHP, es tan simple como strtotime('next tuesday');.

¿Cómo puedo lograr algo similar en .NET?

brenjt
fuente
15
ASP.NET es un conjunto de tecnologías web. C # es un lenguaje. Realmente necesita pensar en esto en términos de .NET. Ahora, para "el próximo martes", ¿es "el primer martes después de hoy"? Si fuera lunes y alguien dijera "nos vemos el próximo martes", esperaría que eso signifique 8 días en lugar de 1. ¿Qué tal si hoy es martes? ¿A qué hora del día necesitas?
Jon Skeet
Si hoy es martes, ¿quieres saber la fecha en que será el próximo martes? O hoy es lunes, ¿quieres encontrar el segundo martes del lunes?
Fuego Panda
El martes más cercano se adelanta a cualquier día en particular.
brenjt
2
@brenjtL: ¿Y si ya es martes?
Jon Skeet
Si ya es martes, ese mismo día
brenjt

Respuestas:

371

Como mencioné en los comentarios, hay varias cosas a las que se podría referir con "el próximo martes", pero este código le da "el próximo martes que ocurrirá, o hoy si ya es martes":

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

Si desea dar "una semana" si ya es martes, puede usar:

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

... o podrías usar la fórmula original, pero a partir de mañana:

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

EDITAR: Solo para hacer esto agradable y versátil:

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

Entonces, para obtener el valor de "hoy o en los próximos 6 días":

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

Para obtener el valor para "el próximo martes excluyendo hoy":

DateTime nextTuesday = GetNextWeekday(DateTime.Today.AddDays(1), DayOfWeek.Tuesday);
Jon Skeet
fuente
Wow, me preguntaba cómo podría llegar enésimo día hasta el próximo martes y luego actualizaste tu respuesta con un ejemplo Nice. Gracias
brenjt
Fue difícil elegir la respuesta correcta. Pero el tuyo parece ser el más versátil y lo hiciste fácil de entender. Gracias por tu ayuda.
brenjt
1
@brenjt: De hecho, diría que Sven's es más versátil, ya que puedes especificar el día de la semana, pero es tu decisión :) (Ahora he editado el mío para dar una versión más generalizada)
Jon Skeet
1
Sin +7)%7embargo, la solución es bastante buena. Aunque la razón por la que no utilicé eso es porque es un poco una microoptimización y es demasiado fácil equivocarse (además de sacrificar algo de legibilidad), en mi opinión, por supuesto.
Sven
Una prueba unitaria: [TestMethod] public void ShouldGetNextSaturday () {var now = DateTime.Now; prueba var = GetNextWeekday (DateTime.Today, DayOfWeek.Saturday); Assert.IsTrue (now.Day <test.Day, "El mes esperado no está aquí"); Assert.IsTrue (test.DayOfWeek == DayOfWeek.Saturday, "El día de la semana esperado no está aquí"); Assert.IsTrue ((test.Day - now.Day) <7, "El intervalo de días esperado no está aquí"); }
rasx
67

Esto debería funcionar:

static DateTime GetNextWeekday(DayOfWeek day)
{
    DateTime result = DateTime.Now.AddDays(1);
    while( result.DayOfWeek != day )
        result = result.AddDays(1);
    return result;
}
Sven
fuente
Gran respuesta, si hoy es martes (que es ja) ¿volverá esto hoy o el próximo martes?
brenjt
3
Esto volverá el próximo martes. Si desea que regrese hoy, simplemente elimine el .AddDays(1)de la primera línea, de esa manera también se comprobará DateTime.Now.
Sven
7

Hay soluciones menos detalladas y más inteligentes / elegantes para este problema, pero la siguiente función de C # funciona realmente bien para una serie de situaciones.

/// <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
fuente
1
La única respuesta que se implementa como extensión de DateTime. Si bien las otras soluciones funcionan, tenerlo como método de extensión produce el código más fácil de usar.
Ryan McArthur el
5
DateTime nextTuesday = DateTime.Today.AddDays(((int)DateTime.Today.DayOfWeek - (int)DayOfWeek.Tuesday) + 7);
Chucho
fuente
Si hoy es lunes, la respuesta que proporcionó produciría una semana a partir del martes, en lugar de mañana.
Tony
5

@ Jon Skeet buena respuesta.

Para el día anterior:

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

¡¡Gracias!!

Anik Islam Abhi
fuente
Tenga en cuenta que esta solución implica números negativos entregados al operador del módulo. El artículo de Wikipedia sobre el operador de módulo dice que "cuando a o n es negativo, la definición ingenua se descompone y los lenguajes de programación difieren en cómo se definen estos valores". Si bien esto probablemente funcione en C #, una solución matemáticamente más 'sólida' para obtener el mismo resultado sería intercambiar los DayOfWeekvalores de esta manera: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);
Ciego
fuente
3

Muestra muy simple para incluir o excluir la fecha actual, usted especifica la fecha y el día de la semana que le interesa.

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

algunos casos de prueba

[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
fuente
He realizado cambios adicionales de la respuesta original después de algunas pruebas exhaustivas. Esto ahora calculará de forma segura el día siguiente en función de la fecha de uso, pasado, presente y futuro. Todos los ejemplos anteriores fueron geniales, pero fallaron bajo ciertas condiciones. No hice una declaración de una sola línea, para que pudieran hacerse comentarios adicionales sobre lo que están haciendo los cálculos. El caso positivo de Jon Skeet fue genial, aunque el caso que tuve fue retroceder 1 día desde una fecha, pero aún mayor que Hoy, y qué pasa si se mueve hoy o ayer ... esto lo resolvió.
AJB
1

También podría ser una extensión, todo depende

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

Uso

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

Ahora en sabor de línea de papel, en caso de que necesite pasarlo como parámetro a algún mecanismo.

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

En este caso específico:

DateTime.Now.AddDays(((int)DayOfWeek.Tuesday - (int)DateTime.Now.DayOfWeek + 7) % 7).Day
Matas Vaitkevicius
fuente
-5

Versión del objetivo C:

+(NSInteger) daysUntilNextWeekday: (NSDate*)startDate withTargetWeekday: (NSInteger) targetWeekday
{
    NSInteger startWeekday = [[NSCalendar currentCalendar] component:NSCalendarUnitWeekday fromDate:startDate];
    return (targetWeekday - startWeekday + 7) % 7;
}
Max Hiroyuki Ueda
fuente
44
Buena respuesta, pero la pregunta original fue sobre .NET.
Adam Davis