Tout le monde connaît un moyen facile d'obtenir la date du premier jour de la semaine (lundi ici en Europe). Je connais l'année et le numéro de la semaine? Je vais le faire en C #.
Tout le monde connaît un moyen facile d'obtenir la date du premier jour de la semaine (lundi ici en Europe). Je connais l'année et le numéro de la semaine? Je vais le faire en C #.
Réponses:
J'ai eu des problèmes avec la solution de @HenkHolterman même avec le correctif de @RobinAndersson.
La lecture de la norme ISO 8601 résout bien le problème. Utilisez le premier jeudi comme cible et non le lundi. Le code ci-dessous fonctionnera également pour la semaine 53 de 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);
}
J'aime la solution fournie par Henk Holterman. Mais pour être un peu plus indépendant de la culture, il faut avoir le premier jour de la semaine pour la culture actuelle (ce n'est pas toujours lundi):
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
est un mauvais nom de variable pour quelque chose qui pourrait ne pas être un lundi. =)
METTRE À JOUR : .NET Core 3.0 et .NET Standard 2.1 sont livrés avec ce type.
Bonnes nouvelles! Une pull request ajoutantSystem.Globalization.ISOWeek
à .NET Core vient d'être fusionnée et est actuellement prévue pour la version 3.0. Espérons qu'il se propagera aux autres plates-formes .NET dans un avenir pas trop lointain.
Vous devriez pouvoir utiliser le ISOWeek.ToDateTime(int year, int week, DayOfWeek dayOfWeek)
méthode pour calculer cela.
Vous pouvez trouver le code source ici .
Le moyen le plus simple est probablement de trouver le premier lundi de l'année, puis d'ajouter le nombre de semaines pertinent. Voici un exemple de code. Il suppose un numéro de semaine commençant à 1, soit dit en passant:
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);
}
}
Personnellement, je profiterais des informations sur la culture pour obtenir le jour de la semaine et revenir au premier jour de la semaine de la culture. Je ne sais pas si je l'explique correctement, voici un exemple:
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;
}
en utilisant Fluent 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);
Selon la norme ISO 8601: 1988 utilisée en Suède, la première semaine de l'année est la première semaine qui compte au moins quatre jours dans la nouvelle année.
Donc, si votre semaine commence un lundi, le premier jeudi de chaque année est dans la première semaine. Vous pouvez DateAdd ou DateDiff à partir de cela.
En supposant que le numéro de semaine commence à 1
DateTime dt = new DateTime(YearNumber, 1, 1).AddDays((WeekNumber - 1) * 7 - (WeekNumber == 1 ? 0 : 1));
return dt.AddDays(-(int)dt.DayOfWeek);
Cela devrait vous donner le premier jour d'une semaine donnée. Je n'ai pas fait beaucoup de tests dessus, mais il semble que cela fonctionne. C'est une solution plus petite que la plupart des autres que j'ai trouvées sur le Web, je voulais donc la partager.
new DateTime(1,1,YearNumber)
La bibliothèque de périodes de temps gratuite pour .NET comprend la semaine de classe conforme ISO 8601 :
// ----------------------------------------------------------------------
public static DateTime GetFirstDayOfWeek( int year, int weekOfYear )
{
return new Week( year, weekOfYear ).FirstDayStart;
} // GetFirstDayOfWeek
Celui-ci a fonctionné pour moi, il a aussi l'avantage d'attendre un cultureinfo comme paramètre pour tester la formule avec différentes cultures. S'il est vide, il obtient les informations de culture actuelles ... les valeurs valides sont comme: "it", "en-us", "fr", ... et ainsi de suite. L'astuce consiste à soustraire le numéro de semaine du premier jour de l'année, qui peut être 1 pour indiquer que le premier jour se situe dans la première semaine. J'espère que cela t'aides.
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
Voici une méthode compatible avec les numéros de semaine que Google Analytics, ainsi que le même schéma de numérotation que nous avons utilisé en interne chez Intel, et qui, j'en suis sûr, est également utilisé dans de nombreux autres contextes.
// 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);
}
J'ai essayé quelques codes ci-dessus et certains ont de petites erreurs, quand vous essayez différentes années avec différents jours de départ de la semaine, vous les verrez, j'ai pris le code de Jon Skeet, corrigez-le et ça marche, code très simple.
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
Code Mikael Svenson légèrement modifié. J'ai trouvé la semaine du premier lundi et changer le numéro de la semaine.
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 semaine 1 est définie comme étant la semaine qui commence un lundi et contient le premier jeudi de l'année.
Pour convertir dans les deux sens, voir ici: article Wikipedia sur les dates de semaine ISO
J'ai amélioré un peu la solution de Thomas avec un remplacement:
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;
}
*/
Sinon, la date précédait d'une semaine.
Merci Thomas, une aide précieuse.
J'ai utilisé l'une des solutions, mais elle m'a donné de mauvais résultats, simplement parce qu'elle compte le dimanche comme premier jour de la semaine.
J'ai changé:
var firstDay = new DateTime(DateTime.Now.Year, 1, 1).AddDays((weekNumber - 1) * 7);
var lastDay = firstDay.AddDays(6);
à:
var lastDay = new DateTime(DateTime.Now.Year, 1, 1).AddDays((weekNumber) * 7);
var firstDay = lastDay.AddDays(-6);
et maintenant cela fonctionne comme un charme.
La solution proposée n'est pas complète - elle ne fonctionne que pour CalendarWeekRule.FirstFullWeek. Les autres types de règles hebdomadaires ne fonctionnent pas. Cela peut être vu en utilisant ce cas de test:
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);
}
}
J'ai fait une version raffinée de la solution proposée qui est plus simple et paramétre le firstDayOfWeek:
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;
}
c'est ma solution quand on veut calculer une date donnée année, numéro de semaine et jour de la semaine.
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);
}
J'ai simplifié le code fourni par Mikael Svensson qui est correct pour de nombreux pays d'Europe.
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));
}
J'ai écrit et testé le code suivant et fonctionne parfaitement bien pour moi. S'il vous plaît laissez-moi savoir si quelqu'un rencontre des problèmes avec cela, j'ai également publié une question afin d'obtenir la meilleure réponse possible. Quelqu'un peut le trouver 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)
pour la semaine 29 de 2020, cela revient 07/20/2020
. Mais le premier jour de la 29e semaine était07/13/2020
Actuellement, il n'existe aucune classe C # qui gère correctement les numéros de semaine ISO 8601. Même si vous pouvez instancier une culture, rechercher la chose la plus proche et corriger cela, je pense qu'il est préférable de faire le calcul complet vous-même:
/// <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;
}