Chiunque conosce un modo semplice per ottenere la data del primo giorno della settimana (lunedì qui in Europa). Conosco l'anno e il numero della settimana? Lo farò in C #.
Chiunque conosce un modo semplice per ottenere la data del primo giorno della settimana (lunedì qui in Europa). Conosco l'anno e il numero della settimana? Lo farò in C #.
Risposte:
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);
}
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);
}
firstMonday
è un nome variabile errato per qualcosa che potrebbe non essere un lunedì. =)
AGGIORNAMENTO : .NET Core 3.0 e .NET Standard 2.1 sono stati forniti con questo tipo.
Buone notizie! UN richiesta pull aggiunta System.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.
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 .
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);
}
}
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;
}
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);
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.
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.
new DateTime(1,1,YearNumber)
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
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
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);
}
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
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;
}
La settimana 1 è definita come la settimana che inizia un lunedì e contiene il primo giovedì dell'anno.
Per la conversione in entrambe le direzioni, vedere qui: articolo di Wikipedia sulle date delle settimane ISO
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.
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.
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);
}
}
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;
}
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);
}
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));
}
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;
}
GetFirstDateOfWeekByWeekNumber(2020, 29)
per la settimana 29 del 2020 questo ritorna 07/20/2020
. Ma il primo giorno della 29a settimana è stato07/13/2020
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;
}