Calcola la data dal numero della settimana


Risposte:


251

Ho avuto problemi con la soluzione di @HenkHolterman anche con la correzione di @RobinAndersson.

Leggere sullo standard ISO 8601 risolve bene il problema. Usa il primo giovedì come obiettivo e non il lunedì. Il codice seguente funzionerà anche per la settimana 53 del 2009.

public static DateTime FirstDateOfWeekISO8601(int year, int weekOfYear)
{
    DateTime jan1 = new DateTime(year, 1, 1);
    int daysOffset = DayOfWeek.Thursday - jan1.DayOfWeek;

    // Use first Thursday in January to get first week of the year as
    // it will never be in Week 52/53
    DateTime firstThursday = jan1.AddDays(daysOffset);
    var cal = CultureInfo.CurrentCulture.Calendar;
    int firstWeek = cal.GetWeekOfYear(firstThursday, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);

    var weekNum = weekOfYear;
    // As we're adding days to a date in Week 1,
    // we need to subtract 1 in order to get the right date for week #1
    if (firstWeek == 1)
    {
        weekNum -= 1;
    }

    // Using the first Thursday as starting week ensures that we are starting in the right year
    // then we add number of weeks multiplied with days
    var result = firstThursday.AddDays(weekNum * 7);

    // Subtract 3 days from Thursday to get Monday, which is the first weekday in ISO8601
    return result.AddDays(-3);
}       

3
Ho provato quasi tutte le soluzioni fornite qui, questa è l'unica che funziona correttamente per me in questo momento. Attualmente è il 7 febbraio 2012. La settimana n. È la 6. Questo codice mi dà correttamente il 6 febbraio come data di inizio della settimana. Le altre soluzioni mi hanno dato il 13 febbraio, che in realtà è la data di inizio della settimana n. 7.
HaukurHaf

1
Funziona come un fascino .. testato alcuni dati: pastebin.com/mfx8s1vq Tutto funziona perfettamente! Grazie Mikael!
Mittchel,

1
@ RobinWassén-Andersson Per fortuna hai rivisitato questa domanda allora: D 6 voti in più e legherò con la risposta "non" corretta hehe.
Mikael Svenson,

2
Suggerimento: rendere "ISO8601" pert il nome del metodo, poiché non esiste semplicemente una risposta "corretta" universale.
Henk Holterman,

1
@Muflix Non so abbastanza su SQL e date / calendari da sapere - ma fare CLR funzionerà.
Mikael Svenson,

36

Mi piace la soluzione fornita da Henk Holterman. Ma per essere un po 'più indipendente dalla cultura, devi ottenere il primo giorno della settimana per la cultura attuale (non è sempre lunedì):

using System.Globalization;

static DateTime FirstDateOfWeek(int year, int weekOfYear)
{
  DateTime jan1 = new DateTime(year, 1, 1);

  int daysOffset = (int)CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek - (int)jan1.DayOfWeek;

  DateTime firstMonday = jan1.AddDays(daysOffset);

  int firstWeek = CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(jan1, CultureInfo.CurrentCulture.DateTimeFormat.CalendarWeekRule, CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek);

  if (firstWeek <= 1)
  {
    weekOfYear -= 1;
  }

  return firstMonday.AddDays(weekOfYear * 7);
}

Ho aggiunto questo a Mannex, come metodo di estensione a Calendar e DateTimeFormatInfo. Ho anche fatto riferimento incrociato a questa risposta per il merito.
Atif Aziz,

1
Non funziona correttamente sulla mia macchina. Mostra una data con 0010 invece del 2010. Non so se sia un problema nel framework .net o in questa funzione. Bel tentativo, comunque ...
Eduardo Xavier,

6
firstMondayè un nome variabile errato per qualcosa che potrebbe non essere un lunedì. =)
mflodin,

11

AGGIORNAMENTO : .NET Core 3.0 e .NET Standard 2.1 sono stati forniti con questo tipo.

Buone notizie! UN richiesta pull aggiunta System.Globalization.ISOWeeka .NET Core è stata appena unita ed è attualmente prevista per la versione 3.0. Spero che si propaghi alle altre piattaforme .NET in un futuro non troppo lontano.

Dovresti essere in grado di utilizzare il ISOWeek.ToDateTime(int year, int week, DayOfWeek dayOfWeek)metodo per calcolare questo.

Puoi trovare il codice sorgente qui .


9

Il modo più semplice è probabilmente quello di trovare il primo lunedì dell'anno e quindi aggiungere il numero rilevante di settimane. Ecco un po 'di codice di esempio. Presuppone un numero di settimana a partire da 1, a proposito:

using System;

class Test
{
    static void Main()
    {
        // Show the third Tuesday in 2009. Should be January 20th
        Console.WriteLine(YearWeekDayToDateTime(2009, DayOfWeek.Tuesday, 3));
    }

    static DateTime YearWeekDayToDateTime(int year, DayOfWeek day, int week)
    {
        DateTime startOfYear = new DateTime (year, 1, 1);

        // The +7 and %7 stuff is to avoid negative numbers etc.
        int daysToFirstCorrectDay = (((int)day - (int)startOfYear.DayOfWeek) + 7) % 7;

        return startOfYear.AddDays(7 * (week-1) + daysToFirstCorrectDay);
    }
}

4
Sì, ma il primo lunedì dell'anno potrebbe appartenere alla settimana 52 | 53 dell'anno precedente.
Henk Holterman,

1
Dipende da come vuoi definire le cose. Purtroppo non abbiamo molte informazioni per continuare qui ... Spero che questo sia utile.
Jon Skeet,

3

Personalmente trarrebbero vantaggio dalle informazioni sulla cultura per ottenere il giorno della settimana e tornare al primo giorno della settimana della cultura. Non sono sicuro se lo sto spiegando correttamente, ecco un esempio:

    public DateTime GetFirstDayOfWeek(int year, int weekNumber)
    {
        return GetFirstDayOfWeek(year, weekNumber, Application.CurrentCulture);
    }

    public DateTime GetFirstDayOfWeek(int year, int weekNumber,
        System.Globalization.CultureInfo culture)
    {
        System.Globalization.Calendar calendar = culture.Calendar;
        DateTime firstOfYear = new DateTime(year, 1, 1, calendar);
        DateTime targetDay = calendar.AddWeeks(firstOfYear, weekNumber);
        DayOfWeek firstDayOfWeek = culture.DateTimeFormat.FirstDayOfWeek;

        while (targetDay.DayOfWeek != firstDayOfWeek)
        {
            targetDay = targetDay.AddDays(-1);
        }

        return targetDay;
    }

3

utilizzando Fluente DateTime http://fluentdatetime.codeplex.com/

        var year = 2009;
        var firstDayOfYear = new DateTime(year, 1, 1);
        var firstMonday = firstDayOfYear.Next(DayOfWeek.Monday);
        var weeksDateTime = 12.Weeks().Since(firstMonday);

2

Secondo la ISO 8601: 1988 che viene utilizzata in Svezia la prima settimana dell'anno è la prima settimana che ha almeno quattro giorni nel nuovo anno.

Quindi, se la tua settimana inizia di lunedì, il primo giovedì di ogni anno è entro la prima settimana. Da questo puoi DateAdd o DateDiff.


2

Supponendo che il numero della settimana inizi da 1

DateTime dt =  new DateTime(YearNumber, 1, 1).AddDays((WeekNumber - 1) * 7 - (WeekNumber == 1 ? 0 : 1));
return dt.AddDays(-(int)dt.DayOfWeek);

Questo dovrebbe darti il ​​primo giorno di ogni settimana. Non ho fatto molti test su di esso, ma sembra che funzioni. È una soluzione più piccola rispetto alla maggior parte delle altre che ho trovato sul web, quindi volevo condividere.


cattiva risposta. L'uso di DateTime.Parse non è richiesto in quanto DateTime ha un costruttore che richiede anno, mese e giorno. new DateTime(1,1,YearNumber)
Jamiec,

Aggiornato il codice per creare un nuovo DateTime invece di utilizzare l'analisi. Era tardi. ;)
QuinnG

2

La libreria gratuita Time Period for .NET include la settimana di classe conforme ISO 8601 :

// ----------------------------------------------------------------------
public static DateTime GetFirstDayOfWeek( int year, int weekOfYear )
{
  return new Week( year, weekOfYear ).FirstDayStart;
} // GetFirstDayOfWeek

2

Questo ha funzionato per me, ha anche il vantaggio di aspettarsi un cultureinfo come parametro per testare la formula con culture diverse. Se vuoto, ottiene le informazioni sulla cultura corrente ... i valori validi sono come: "it", "en-us", "fr", ... e così via. Il trucco è sottrarre il numero della settimana del primo giorno dell'anno, che può essere 1 per indicare che il primo giorno è entro la prima settimana. Spero che questo ti aiuti.

Public Shared Function FirstDayOfWeek(ByVal year As Integer, ByVal weekNumber As Integer, ByVal culture As String) As Date
    Dim cInfo As System.Globalization.CultureInfo
    If culture = "" Then
        cInfo = System.Globalization.CultureInfo.CurrentCulture
    Else
        cInfo = System.Globalization.CultureInfo.CreateSpecificCulture(culture)
    End If
    Dim calendar As System.Globalization.Calendar = cInfo.Calendar
    Dim firstOfYear As DateTime = New DateTime(year, 1, 1, calendar)
    Dim firstDayWeek As Integer = calendar.GetWeekOfYear(firstOfYear, cInfo.DateTimeFormat.CalendarWeekRule, cInfo.DateTimeFormat.FirstDayOfWeek)
    weekNumber -= firstDayWeek
    Dim targetDay As DateTime = calendar.AddWeeks(firstOfYear, weekNumber)
    Dim fDayOfWeek As DayOfWeek = cInfo.DateTimeFormat.FirstDayOfWeek

    While (targetDay.DayOfWeek <> fDayOfWeek)
        targetDay = targetDay.AddDays(-1)
    End While
    Return targetDay
End Function

2

Ecco un metodo compatibile con i numeri delle settimane di Google Analytics, e anche lo stesso schema di numerazione che abbiamo usato internamente in Intel, e che sono sicuro che viene utilizzato anche in molti altri contesti.

// Google Analytics does not follow ISO standards for date.
// It numbers week 1 starting on Jan. 1, regardless what day of week it starts on.
// It treats Sunday as the first day of the week.
// The first and last weeks of a year are usually not complete weeks.
public static DateTime GetStartDateTimeFromWeekNumberInYear(int year, uint weekOfYear)
{
  if (weekOfYear == 0 || weekOfYear > 54) throw new ArgumentException("Week number must be between 1 and 54! (Yes, 54... Year 2000 had Jan. 1 on a Saturday plus 53 Sundays.)");

  // January 1 -- first week.
  DateTime firstDayInWeek = new DateTime(year, 1, 1);
  if (weekOfYear == 1) return firstDayInWeek;

  // Get second week, starting on the following Sunday.      
  do
  {
    firstDayInWeek = firstDayInWeek.AddDays(1);
  } while (firstDayInWeek.DayOfWeek != DayOfWeek.Sunday);

  if (weekOfYear == 2) return firstDayInWeek;

  // Now get the Sunday of whichever week we're looking for.
  return firstDayInWeek.AddDays((weekOfYear - 2)*7);
}

2

Ho provato alcuni codici sopra e alcuni hanno piccoli errori, quando provi diversi anni con diversi giorni di inizio della settimana li vedrai, ho preso il codice di Jon Skeet, risolto e funziona, un codice molto semplice.

Public Function YearWeekDayToDateTime(ByVal year As Integer, ByVal weekDay As Integer, ByVal week As Integer) As DateTime
   ' weekDay, day you want
    Dim startOfYear As New DateTime(year, 1, 1)
    Dim startOfYearFixDay As Integer

    If startOfYear.DayOfWeek <> DayOfWeek.Sunday Then
        startOfYearFixDay = startOfYear.DayOfWeek
    Else
        startOfYearFixDay = 7
    End If

    Return startOfYear.AddDays((7 * (week)) - startOfYearFixDay + weekDay)
End Function

1

Codice Mikael Svenson leggermente modificato. Ho trovato la settimana del primo lunedì e ho opportunamente cambiato il numero della settimana.

 DateTime GetFirstWeekDay(int year, int weekNum)
    {
        Calendar calendar = CultureInfo.CurrentCulture.Calendar;

        DateTime jan1 = new DateTime(year, 1, 1);

        int daysOffset = DayOfWeek.Monday - jan1.DayOfWeek;
        DateTime firstMonday = jan1.AddDays(daysOffset);
        int firstMondayWeekNum = calendar.GetWeekOfYear(firstMonday, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);

        DateTime firstWeekDay = firstMonday.AddDays((weekNum-firstMondayWeekNum) * 7);

        return firstWeekDay;
    }

0

La settimana 1 è definita come la settimana che inizia un lunedì e contiene il primo giovedì dell'anno.


1
Questa è una definizione, ce ne sono altre.
Henk Holterman,

1
Lo standard ISO definisce la settimana 1 come la settimana con il primo giovedì dell'anno.
RickardN,


0

Ho migliorato un po 'la soluzione di Thomas con una sostituzione:

   public static DateTime FirstDateOfWeek(int year, int weekOfYear)
    {
      return Timer.FirstDateOfWeekOfMonth(year, 1, weekOfYear);
    }

    public static DateTime FirstDateOfWeekOfMonth(int year, int month, 
    int weekOfYear)
    {
      DateTime dtFirstDayOfMonth = new DateTime(year, month, 1);

       //I also commented out this part:
      /*
      if (firstWeek <= 1)
      {
        weekOfYear -= 1;
      }
      */

Altrimenti la data era precedente di una settimana ..

Grazie Thomas, grande aiuto.


0

Ho usato una delle soluzioni ma mi ha dato risultati sbagliati, semplicemente perché conta la domenica come primo giorno della settimana.

Ho cambiato:

var firstDay = new DateTime(DateTime.Now.Year, 1, 1).AddDays((weekNumber - 1) * 7);
var lastDay = firstDay.AddDays(6);

per:

var lastDay = new DateTime(DateTime.Now.Year, 1, 1).AddDays((weekNumber) * 7);
var firstDay = lastDay.AddDays(-6);

e ora funziona come un incantesimo.


1
Ma questo risponde alla domanda del PO? È 1/1 più un numero fisso di giorni. Nessun concetto del primo giorno della settimana.
Gert Arnold,

0

La soluzione proposta non è completa: funziona solo per CalendarWeekRule.FirstFullWeek. Altri tipi di regole settimanali non funzionano. Questo può essere visto usando questo test case:

foreach (CalendarWeekRule rule in Enum.GetValues(typeof(CalendarWeekRule)))
{
    for (int year = 1900; year < 2000; year++)
    {
        DateTime date = FirstDateOfWeek(year, 1, rule);
        Assert(CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(date, rule, DayOfWeek.Monday) == 1);
        Assert(CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(date.AddDays(-1), rule, DayOfWeek.Monday) != 1);
    }
}

0

Ho realizzato una versione raffinata della soluzione proposta che è più semplice e parametrizza il primoDayOfWeek:

public static DateTime GetFirstDayOfWeek(int year, int week, DayOfWeek firstDayOfWeek)
{
    return GetWeek1Day1(year, firstDayOfWeek).AddDays(7 * (week - 1));
}

public static DateTime GetWeek1Day1(int year, DayOfWeek firstDayOfWeek)
{
    DateTime date = new DateTime(year, 1, 1);

    // Move towards firstDayOfWeek
    date = date.AddDays(firstDayOfWeek - date.DayOfWeek);

    // Either 1 or 52 or 53
    int weekOfYear = CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(date, CalendarWeekRule.FirstFullWeek, firstDayOfWeek);

    // Move forwards 1 week if week is 52 or 53
    date = date.AddDays(7 * System.Math.Sign(weekOfYear - 1));

    return date;
}

0

questa è la mia soluzione quando vogliamo calcolare una data per anno, numero di settimana e giorno della settimana.

int Year = 2014;
int Week = 48;
int DayOfWeek = 4;

DateTime FecIni = new DateTime(Year, 1, 1);
FecIni = FecIni.AddDays(7 * (Week - 1));
if ((int)FecIni.DayOfWeek > DayOfWeek)
{
    while ((int)FecIni.DayOfWeek != DayOfWeek) FecIni = FecIni.AddDays(-1);
}
else
{
    while ((int)FecIni.DayOfWeek != DayOfWeek) FecIni = FecIni.AddDays(1);
}

0

Ho semplificato il codice fornito da Mikael Svensson che è corretto per molti paesi in Europa.

public static DateTime FirstDateOfWeekIso8601(int year, int week)
{
        var firstThursdayOfYear = new DateTime(year, 1, 1);
        while (firstThursdayOfYear.DayOfWeek != DayOfWeek.Thursday)
        {
            firstThursdayOfYear = firstThursdayOfYear.AddDays(1);
        }

        var startDateOfWeekOne = firstThursdayOfYear.AddDays(-(DayOfWeek.Thursday - DayOfWeek.Monday));

        return startDateOfWeekOne.AddDays(7 * (week - 1));        
}

0

Ho scritto e testato il seguente codice e funziona perfettamente per me. Per favore fatemi sapere se qualcuno ha problemi con questo, ho anche pubblicato una domanda per ottenere la migliore risposta possibile. Qualcuno potrebbe trovarlo utile.

public static DateTime GetFirstDateOfWeekByWeekNumber(int year, int weekNumber)
        {
            var date = new DateTime(year, 01, 01);
            var firstDayOfYear = date.DayOfWeek;
            var result = date.AddDays(weekNumber * 7);

            if (firstDayOfYear == DayOfWeek.Monday)
                return result.Date;
            if (firstDayOfYear == DayOfWeek.Tuesday)
                return result.AddDays(-1).Date;
            if (firstDayOfYear == DayOfWeek.Wednesday)
                return result.AddDays(-2).Date;
            if (firstDayOfYear == DayOfWeek.Thursday)
                return result.AddDays(-3).Date;
            if (firstDayOfYear == DayOfWeek.Friday)
                return result.AddDays(-4).Date;
            if (firstDayOfYear == DayOfWeek.Saturday)
                return result.AddDays(-5).Date;
            return result.AddDays(-6).Date;
        }

Questo è sbagliato - almeno per molti paesi europei. GetFirstDateOfWeekByWeekNumber(2020, 29)per la settimana 29 del 2020 questo ritorna 07/20/2020. Ma il primo giorno della 29a settimana è stato07/13/2020
Matthias Burger il

0

Attualmente non esiste una classe C # che gestisca correttamente i numeri della settimana ISO 8601. Anche se puoi istanziare una cultura, cercare la cosa più vicina e correggerla, penso che sia meglio fare tu stesso il calcolo completo:

    /// <summary>
    /// Converts a date to a week number.
    /// ISO 8601 week 1 is the week that contains the first Thursday that year.
    /// </summary>
    public static int ToIso8601Weeknumber(this DateTime date)
    {
        var thursday = date.AddDays(3 - date.DayOfWeek.DayOffset());
        return (thursday.DayOfYear - 1) / 7 + 1;
    }

    /// <summary>
    /// Converts a week number to a date.
    /// Note: Week 1 of a year may start in the previous year.
    /// ISO 8601 week 1 is the week that contains the first Thursday that year, so
    /// if December 28 is a Monday, December 31 is a Thursday,
    /// and week 1 starts January 4.
    /// If December 28 is a later day in the week, week 1 starts earlier.
    /// If December 28 is a Sunday, it is in the same week as Thursday January 1.
    /// </summary>
    public static DateTime FromIso8601Weeknumber(int weekNumber, int? year = null, DayOfWeek day = DayOfWeek.Monday)
    {
        var dec28 = new DateTime((year ?? DateTime.Today.Year) - 1, 12, 28);
        var monday = dec28.AddDays(7 * weekNumber - dec28.DayOfWeek.DayOffset());
        return monday.AddDays(day.DayOffset());
    }

    /// <summary>
    /// Iso8601 weeks start on Monday. This returns 0 for Monday.
    /// </summary>
    private static int DayOffset(this DayOfWeek weekDay)
    {
        return ((int)weekDay + 6) % 7;
    }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.