Ottieni il numero di settimana corretto di una determinata data


222

Ho cercato su Google molto e ho trovato molte soluzioni, ma nessuna mi ha dato il numero di settimana corretto per il 2012-12-31. Anche l'esempio su MSDN ( collegamento ) non riesce.

31-12-2012 è lunedì, quindi dovrebbe essere la settimana 1, ma ogni metodo che ho provato mi dà 53. Ecco alcuni dei metodi che ho provato:

Dalla libreria MDSN:

DateTimeFormatInfo dfi = DateTimeFormatInfo.CurrentInfo;
Calendar cal = dfi.Calendar;

return cal.GetWeekOfYear(date, dfi.CalendarWeekRule, dfi.FirstDayOfWeek);

Soluzione 2:

return new GregorianCalendar(GregorianCalendarTypes.Localized).GetWeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);

Soluzione 3:

CultureInfo ciCurr = CultureInfo.CurrentCulture;
int weekNum = ciCurr.Calendar.GetWeekOfYear(dtPassed, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
return weekNum;

Aggiornare

Il seguente metodo restituisce effettivamente 1 quando la data è 2012-12-31. In altre parole, il mio problema era che i miei metodi non seguivano lo standard ISO-8601.

// This presumes that weeks start with Monday.
// Week 1 is the 1st week of the year with a Thursday in it.
public static int GetIso8601WeekOfYear(DateTime time)
{
    // Seriously cheat.  If its Monday, Tuesday or Wednesday, then it'll 
    // be the same week# as whatever Thursday, Friday or Saturday are,
    // and we always get those right
    DayOfWeek day = CultureInfo.InvariantCulture.Calendar.GetDayOfWeek(time);
    if (day >= DayOfWeek.Monday && day <= DayOfWeek.Wednesday)
    {
        time = time.AddDays(3);
    }

    // Return the week of our adjusted day
    return CultureInfo.InvariantCulture.Calendar.GetWeekOfYear(time, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
}

4
Com'è la settimana 1 alla fine dell'anno? Voglio dire, vedo dove l'hai preso. Ma 53 ha senso per me.
benjer3,

1
Nei miei frammenti di codice ottengo CultureInfo e altro. Ho pensato che il mio programma ormai sapesse quale calendario sto usando. (Qui in Germania il 31 dicembre 2012 è nella settimana 1 del 2013)
Amberlamps

Questo codice non funziona abbastanza come dovrebbe provare le date 31-dic-2016 per esempio o 1-gen-2016
cavej03

@ cavej03 31-dic-2016 è la settimana 52 e GetIso8601WeekOfYear restituisce 52, quindi immagino che funzioni correttamente.
Muflix,

Risposte:


311

Come indicato in questa pagina MSDN, esiste una leggera differenza tra la numerazione ISO8601 della settimana e quella della settimana .Net.

È possibile fare riferimento a questo articolo nel blog MSDN per una spiegazione migliore: " Formato ISO 8601 Settimana dell'anno in Microsoft .Net "

In poche parole, .Net consente alle settimane di essere suddivise tra anni, mentre lo standard ISO no. Nell'articolo c'è anche una semplice funzione per ottenere il numero di settimana ISO 8601 corretto per l'ultima settimana dell'anno.

Aggiornamento Il seguente metodo restituisce effettivamente 1 per il 2012-12-31quale è corretto in ISO 8601 (ad es. Germania).

// This presumes that weeks start with Monday.
// Week 1 is the 1st week of the year with a Thursday in it.
public static int GetIso8601WeekOfYear(DateTime time)
{
    // Seriously cheat.  If its Monday, Tuesday or Wednesday, then it'll 
    // be the same week# as whatever Thursday, Friday or Saturday are,
    // and we always get those right
    DayOfWeek day = CultureInfo.InvariantCulture.Calendar.GetDayOfWeek(time);
    if (day >= DayOfWeek.Monday && day <= DayOfWeek.Wednesday)
    {
        time = time.AddDays(3);
    }

    // Return the week of our adjusted day
    return CultureInfo.InvariantCulture.Calendar.GetWeekOfYear(time, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
} 

1
@il_guru Cosa succede se desidero che la settimana inizi di domenica ?? Devo sostituire tutto "lunedì" con "domenica" ??
Utente2012384

2
@ User2012384 a proposito, se vuoi seguire lo standard ISO8601 il primo giorno di settimana dovrebbe essere sempre lunedì.
Starceaker il

1
@il_guru Non hai nemmeno bisogno di questo "imbroglione" se, vero? Per quanto posso dire "GetWeekOfYear (time, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);" funziona benissimo.
Starceaker il

2
Se cerchi il secondo link (formato ISO 8601 Settimana dell'anno in Microsoft .Net), potresti trovare la risposta alla tua domanda direttamente da Microsoft. Un esempio è lunedì 31/12/2007: con la funzione .Net restituirà la settimana numero 53 del 2007 mentre per lo standard ISO è la settimana numero 1 del 2008
il_guru

3
Ho verificato manualmente che questo è corretto per gli anni dal 2012 al 2018 (supponendo che epochconverter.com sia corretto). Dovremmo tutti rallegrarci quest'anno che la settimana 1 del 2018 sia effettivamente iniziata il primo gennaio per una volta!
Aidan,

32

Possono esserci più di 52 settimane in un anno. Ogni anno ha 52 settimane intere + 1 o +2 (anno bisestile) in più. Compongono una 53a settimana.

  • 52 settimane * 7 giorni = 364 giorni.

Quindi per ogni anno hai almeno un giorno in più. Due per gli anni bisestili. Questi giorni extra vengono conteggiati come settimane separate?

Quante settimane ci sono davvero dipende dal giorno di inizio della settimana. Consideriamo questo per il 2012.

  • Stati Uniti (domenica -> sabato): 52 settimane + una breve settimana di 2 giorni per il 2012-12-30 e il 2012-12-31. Ciò si traduce in un totale di 53 settimane. Gli ultimi due giorni di quest'anno (domenica + lunedì) formano la loro breve settimana.

Controlla le impostazioni della tua Cultura corrente per vedere cosa utilizza come primo giorno della settimana.

Come vedi è normale ottenere 53 di conseguenza.

  • Europa (lunedì -> domenica): 2 gennaio (2012-1-2) è il primo lunedì, quindi questo è il primo giorno della prima settimana. Chiedi il numero della settimana per il 1 ° gennaio e ne riceverai 52 poiché è considerato parte della settimana scorsa del 2011.

È anche possibile avere una 54a settimana. Accade ogni 28 anni quando il 1 ° gennaio e il 31 dicembre vengono trattati come settimane separate. Deve essere anche un anno bisestile.

Ad esempio, l'anno 2000 ha avuto 54 settimane. Il 1 ° gennaio (sabato) è stato il primo giorno di una settimana e il 31 dicembre (domenica) è stato il secondo giorno di una settimana.

var d = new DateTime(2012, 12, 31);
CultureInfo cul = CultureInfo.CurrentCulture;

var firstDayWeek = cul.Calendar.GetWeekOfYear(
    d,
    CalendarWeekRule.FirstDay,
    DayOfWeek.Monday);

int weekNum = cul.Calendar.GetWeekOfYear(
    d,
    CalendarWeekRule.FirstDay,
    DayOfWeek.Monday);

int year = weekNum == 52 && d.Month == 1 ? d.Year - 1 : d.Year;
Console.WriteLine("Year: {0} Week: {1}", year, weekNum);

Stampe: Anno: 2012 Settimana: 54

Cambia CalendarWeekRule nell'esempio sopra in FirstFullWeek o FirstFourDayWeek e tornerai 53. Manteniamo il giorno di inizio lunedì poiché abbiamo a che fare con la Germania.

Quindi la settimana 53 inizia lunedì 2012-12-31, dura un giorno e poi si ferma.

53 è la risposta corretta. Cambia la cultura in Germania se vuoi provarla.

CultureInfo cul = CultureInfo.GetCultureInfo("de-DE");

So che è possibile che ci siano 53 settimane. Ma quando hai domenica (30/12) e lunedì (31/12), non conti il ​​martedì (01/01) anche come parte della 53a settimana negli Stati Uniti?
Lampade ad incandescenza il

Non ho mai visto una 54a settimana!
Lampade ad incandescenza il

Alcuni pacchetti software fiscali li hanno.
Christophe Geers,

Ok, ho scoperto che è possibile nei calendari statunitensi, ma non accadrà nei calendari tedeschi.
Lampade ad incandescenza il

1
+1 per le tue profonde riflessioni sul problema, ma lo snippet di codice nel post di blog fornito in una risposta diversa ha risolto il mio problema.
Lampade ad incandescenza il

22

Questo è il modo:

public int GetWeekNumber()
{
    CultureInfo ciCurr = CultureInfo.CurrentCulture;
    int weekNum = ciCurr.Calendar.GetWeekOfYear(DateTime.Now, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
    return weekNum;
}

Il più importante per è il CalendarWeekRuleparametro.

Vedi qui: https://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=IT-IT&k=k(System.Globalization.CalendarWeekRule);k(TargetFrameworkMoniker-.NETFramework


3
"Diverse persone hanno notato che Calendar.GetWeekOfYear () è quasi come la settimana ISO 8601 quando sono passati CalendarWeekRule.FirstFourDayWeek e DayOfWeek.Monday, tuttavia è un po 'diverso. In particolare ISO 8601 ha sempre 7 settimane di giorni." Blogs.msdn. microsoft.com/shawnste/2006/01/24/…
Juha Palomäki

1
Il tuo link è in italiano
radbyx,

per il nuovo DateTime (2000,12,31) restituisce solo 52?
Leszek P

18

Buone notizie! Una richiesta di pull aggiungendoSystem.Globalization.ISOWeek a .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.

Il tipo ha la seguente firma, che dovrebbe coprire la maggior parte delle esigenze della settimana ISO :

namespace System.Globalization
{
    public static class ISOWeek
    {
        public static int GetWeekOfYear(DateTime date);
        public static int GetWeeksInYear(int year);
        public static int GetYear(DateTime date);
        public static DateTime GetYearEnd(int year);
        public static DateTime GetYearStart(int year);
        public static DateTime ToDateTime(int year, int week, DayOfWeek dayOfWeek);
    }
}

Puoi trovare il codice sorgente qui .

AGGIORNAMENTO : Queste API sono state incluse anche nella versione 2.1 di .NET Standard .


Qualche idea su quando verrà rilasciata la versione 3.0?
Jacques,

"Stiamo programmando di rilasciare una prima anteprima di .NET Core 3 entro la fine dell'anno e la versione finale nel 2019." (dal post sul blog della roadmap )
khellang

@khellang come posso ottenere una settimana ordinale in un mese in base al datetime? Invece di ricevere la settimana 33, vorrei vedere la settimana 2 (perché la settimana 33 è la settimana 2 di agosto). stackoverflow.com/questions/58039103/...
Roxy'Pro

11

Poiché non sembra esserci una cultura .Net che produce il numero di settimana ISO-8601 corretto, preferirei bypassare del tutto la determinazione della settimana integrata e fare il calcolo manualmente, invece di tentare di correggere un parzialmente corretto risultato.

Quello che ho finito è il seguente metodo di estensione:

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

Prima di tutto, ((int)date.DayOfWeek + 6) % 7)determina il numero del giorno della settimana, 0 = lunedì, 6 = domenica.

date.AddDays(-((int)date.DayOfWeek + 6) % 7) determina la data del lunedì che precede il numero della settimana richiesta.

Tre giorni dopo è l'obiettivo giovedì, che determina in che anno è la settimana.

Se si divide il numero del giorno (in base zero) nell'anno per sette (arrotondato per difetto), si ottiene il numero della settimana (in base zero) nell'anno.

In c #, i risultati del calcolo dei numeri interi sono arrotondati implicitamente.


1
Questa è di gran lunga la soluzione migliore, ma sottolineerò un modo leggermente diverso di farlo: DateTime si basa sul proletico calendario gregoriano, ovvero estende il calendario gregoriano fino all'anno 1. Il 1 ° gennaio 0001 sembra essere un Lunedi. Ticksè il numero di incrementi di 100 nanosecondi dal 1 ° gennaio 0001. Dato che ci sono 86.400 secondi in un giorno possiamo scrivere:var thursday = date.AddDays(3 - date.Ticks / 86400 / 10_000_000 % 7); return (thursday.DayOfYear - 1) / 7 + 1;
Adrian S

5

In .NET 3.0 e ISOWeek.GetWeekOfDateversioni successive è possibile utilizzare il -Method .

Si noti che l'anno nel formato numero anno + settimana potrebbe differire dall'anno dell'anno a DateTimecausa delle settimane che attraversano il confine dell'anno.


3

C # alla porta Powershell dal codice sopra da il_guru :

function GetWeekOfYear([datetime] $inputDate)
{
   $day = [System.Globalization.CultureInfo]::InvariantCulture.Calendar.GetDayOfWeek($inputDate)
   if (($day -ge [System.DayOfWeek]::Monday) -and ($day -le [System.DayOfWeek]::Wednesday))
   {
      $inputDate = $inputDate.AddDays(3)
   }

   # Return the week of our adjusted day
   $weekofYear = [System.Globalization.CultureInfo]::InvariantCulture.Calendar.GetWeekOfYear($inputDate, [System.Globalization.CalendarWeekRule]::FirstFourDayWeek, [System.DayOfWeek]::Monday)
   return $weekofYear
}

1
Potresti spiegare il tuo codice? Le risposte di solo codice non sono generalmente accettabili su StackOverflow e come tali possono essere eliminate.
Wai Ha Lee,

@Wai Ha Lee il codice è già stato spiegato. Vedi post sopra da il_guru. Ho portato il suo codice su PowerShell, così altre persone possono usarlo su PowerShell perché finora non esiste una buona soluzione in PowerShell.
Rainer,

Se è quello che hai fatto, dovresti accreditare l'autore originale nella risposta stessa. La risposta che hai portato ha commenti migliori che presumibilmente hai rimosso. Inoltre, questa non è una risposta alla domanda poiché la domanda non ha menzionato PowerShell.
Wai Ha Lee,

1
Ho fatto ora @Wai Ha Lee. La domanda è già contrassegnata come risposta, quindi questa sarebbe la stessa soluzione scritta in un'altra lingua. Ho fatto un favore alle persone, che sono alla ricerca di una soluzione in linguaggio Powershell (cercavo una soluzione in Powershell, ma quello che ho trovato era una soluzione in C #). L'idea rimane la stessa e l'autore ne ottiene il merito.
Rainer,

2

Il modo più semplice per determinare lo stile ISO 8601 del numero di settimana utilizzando c # e la classe DateTime.

Chiedi questo: il giovedì di quanti anni è il giovedì di questa settimana. La risposta è uguale al numero della settimana desiderata.

var dayOfWeek = (int)moment.DayOfWeek;
// Make monday the first day of the week
if (--dayOfWeek < 0)
    dayOfWeek = 6;
// The whole nr of weeks before this thursday plus one is the week number
var weekNumber = (moment.AddDays(3 - dayOfWeek).DayOfYear - 1) / 7 + 1;

1
var cultureInfo = CultureInfo.CurrentCulture;
var calendar = cultureInfo.Calendar;

var calendarWeekRule = cultureInfo.DateTimeFormat.CalendarWeekRule;
var firstDayOfWeek = cultureInfo.DateTimeFormat.FirstDayOfWeek;
var lastDayOfWeek = cultureInfo.LCID == 1033 //En-us
                    ? DayOfWeek.Saturday
                    : DayOfWeek.Sunday;

var lastDayOfYear = new DateTime(date.Year, 12, 31);

var weekNumber = calendar.GetWeekOfYear(date, calendarWeekRule, firstDayOfWeek);

 //Check if this is the last week in the year and it doesn`t occupy the whole week
return weekNumber == 53 && lastDayOfYear.DayOfWeek != lastDayOfWeek 
       ? 1  
       : weekNumber;

Funziona bene sia per le culture statunitensi che russe. Anche ISO 8601 sarà corretto, poiché la settimana russa inizia il lunedì.


1

Ecco una versione di estensione e una versione nullable della risposta di il_guru .

Estensione:

public static int GetIso8601WeekOfYear(this DateTime time)
{
    var day = CultureInfo.InvariantCulture.Calendar.GetDayOfWeek(time);
    if (day >= DayOfWeek.Monday && day <= DayOfWeek.Wednesday)
    {
        time = time.AddDays(3);
    }

    return CultureInfo.InvariantCulture.Calendar.GetWeekOfYear(time, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
}

nullable:

public static int? GetIso8601WeekOfYear(this DateTime? time)
{
    return time?.GetIso8601WeekOfYear();
}

usi:

new DateTime(2019, 03, 15).GetIso8601WeekOfYear(); //returns 11
((DateTime?) new DateTime(2019, 03, 15)).GetIso8601WeekOfYear(); //returns 11
((DateTime?) null).GetIso8601WeekOfYear(); //returns null

0

La domanda è: come si definisce se una settimana è nel 2012 o nel 2013? La tua supposizione, suppongo, è che dal momento che 6 giorni della settimana sono nel 2013, questa settimana dovrebbe essere contrassegnata come la prima settimana del 2013.

Non sono sicuro che questa sia la strada giusta da percorrere. Quella settimana è iniziata il 2012 (lunedì 31 dicembre), quindi dovrebbe essere contrassegnata come l'ultima settimana del 2012, quindi dovrebbe essere il 53 ° del 2012. La prima settimana del 2013 dovrebbe iniziare lunedì 7.

Ora puoi gestire il caso particolare delle settimane marginali (prima e ultima settimana dell'anno) usando le informazioni del giorno della settimana. Tutto dipende dalla tua logica.


Va bene, posso vedere il tuo punto. Il fatto è che quando uso 2013/01/07 in uno dei miei metodi, si dice settimana 2. Quindi, 2012/12/31 è la settimana 53 e 2013/01/07 è la settimana 2. Potresti pensare che non ci sia settimana 1 del 2013. Ma quando provo il 01/01/2013 dice la settimana 1.
Amberlamps,

0
  DateTimeFormatInfo dfi = DateTimeFormatInfo.CurrentInfo;
  DateTime date1 = new DateTime(2011, 1, 1);
  Calendar cal = dfi.Calendar;

  Console.WriteLine("{0:d}: Week {1} ({2})", date1, 
                    cal.GetWeekOfYear(date1, dfi.CalendarWeekRule, 
                                      dfi.FirstDayOfWeek),
                    cal.ToString().Substring(cal.ToString().LastIndexOf(".") + 1));      

0

Sulla base della risposta di il_guru, ho creato questa versione per le mie esigenze che restituisce anche la componente dell'anno.

    /// <summary>
    /// This presumes that weeks start with Monday.
    /// Week 1 is the 1st week of the year with a Thursday in it.
    /// </summary>
    /// <param name="time">The date to calculate the weeknumber for.</param>
    /// <returns>The year and weeknumber</returns>
    /// <remarks>
    /// Based on Stack Overflow Answer: https://stackoverflow.com/a/11155102
    /// </remarks>
    public static (short year, byte week) GetIso8601WeekOfYear(DateTime time)
    {
        // Seriously cheat.  If its Monday, Tuesday or Wednesday, then it'll
        // be the same week# as whatever Thursday, Friday or Saturday are,
        // and we always get those right
        DayOfWeek day = CultureInfo.InvariantCulture.Calendar.GetDayOfWeek(time);
        if (day >= DayOfWeek.Monday && day <= DayOfWeek.Wednesday)
        {
            time = time.AddDays(3);
        }
        // Return the week of our adjusted day
        var week = (byte)CultureInfo.InvariantCulture.Calendar.GetWeekOfYear(time, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
        return ((short)(week >= 52 & time.Month == 1 ? time.Year - 1 : time.Year), week);
    }

0

Questi due metodi aiuteranno, supponendo che la nostra settimana inizi lunedì

/// <summary>
    /// Returns the weekId
    /// </summary>
    /// <param name="DateTimeReference"></param>
    /// <returns>Returns the current week id</returns>
    public static DateTime GetDateFromWeek(int WeekReference)
    {
        //365 leap
        int DaysOffset = 0;
        if (WeekReference > 1)
        {
            DaysOffset = 7;
            WeekReference = WeekReference - 1;
        }
        DateTime DT = new DateTime(DateTime.Now.Year, 1, 1);
        int CurrentYear = DT.Year;
        DateTime SelectedDateTime = DateTime.MinValue;

        while (CurrentYear == DT.Year)
        {
            int TheWeek = WeekReportData.GetWeekId(DT);
            if (TheWeek == WeekReference)
            {
                SelectedDateTime = DT;
                break;
            }
            DT = DT.AddDays(1.0D);
        }

        if (SelectedDateTime == DateTime.MinValue)
        {
            throw new Exception("Please check week");
        }

        return SelectedDateTime.AddDays(DaysOffset);
    }
/// <summary>
    /// Returns the weekId
    /// </summary>
    /// <param name="DateTimeReference"></param>
    /// <returns>Returns the current week id</returns>
    public static int GetWeekId(DateTime DateTimeReference)
    {
        CultureInfo ciCurr = CultureInfo.InvariantCulture;
        int weekNum = ciCurr.Calendar.GetWeekOfYear(DateTimeReference,
        CalendarWeekRule.FirstFullWeek, DayOfWeek.Monday);
        return weekNum;
    }

-1

Un anno ha 52 settimane e 1 giorno o 2 in caso di un anno di giro (52 x 7 = 364). 2012-12-31 sarebbe la settimana 53, una settimana che avrebbe solo 2 giorni perché il 2012 è un anno di giro.


Questo non è corretto Il primo giorno della settimana dell'anno può cadere in qualsiasi giorno della settimana, a seconda di come conti le settimane, l'anno può avere una 54a settimana.
krowe2,

-2
public int GetWeekNumber()
{
   CultureInfo ciCurr = CultureInfo.CurrentCulture;
   int weekNum = ciCurr.Calendar.GetWeekOfYear(DateTime.Now, 
   CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
   return weekNum;
}
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.