public void AddWeekOfYearsTest() { const int maxAddWeeks = 40; Action <CultureInfo> @AddWeekOfYearsAction = (culture) => { var currentCulture = culture; Enumerable .Range(2000, 20) .RunEach(year => { const int step = 10; YearAndWeek prevResult = new YearAndWeek(); var maxWeek = WeekTool.GetEndYearAndWeek(year, currentCulture); for (var week = 1; week < maxWeek.Week; week += step) { for (var addWeeks = -maxAddWeeks; addWeeks <= maxAddWeeks; addWeeks += step) { var current = new YearAndWeek(year, week); var result = WeekTool.AddWeekOfYears(current, addWeeks, currentCulture); if (addWeeks != 0 && prevResult.Week.HasValue) { if (result.Year == prevResult.Year) { Assert.AreEqual(prevResult.Week + step, result.Week, @"Prev=[{0}], Result=[{1}], addWeeks=[{2}]", prevResult, result, addWeeks); } } result.Week.Should().Have.Value(); result.Week.Value.Should().Be.GreaterThan(0); result.Week.Value.Should().Be.LessThanOrEqualTo(TimeSpec.MaxWeeksPerYear); prevResult = result; if (IsDebugEnabled) { log.Debug("Current=[{0}], Added=[{1}], AddWeeks=[{2}]", current, result, addWeeks); } } } }); }; Parallel.For(0, CultureTestData.Default.Count(), i => @AddWeekOfYearsAction(CultureTestData.Default[i])); var rule = WeekOfYearRuleKind.Iso8601; ParallelTool.ForWithStep(2000, 2020, 2, year => { const int step = 10; var prevResult = new YearAndWeek(); var maxWeek = WeekTool.GetEndYearAndWeek(year, null, rule); for (var week = 1; week < maxWeek.Week; week += step) { for (var addWeeks = -maxAddWeeks; addWeeks <= maxAddWeeks; addWeeks += step) { var current = new YearAndWeek(year, week); var result = WeekTool.AddWeekOfYears(current, addWeeks, null, rule); if (addWeeks != 0 && prevResult.Week.HasValue) { if (result.Year == prevResult.Year) { Assert.AreEqual(prevResult.Week + step, result.Week, @"Prev={0}, Result={1}, addWeeks={2}", prevResult, result, addWeeks); } } result.Week.Should().Have.Value(); result.Week.Value.Should().Be.GreaterThan(0); result.Week.Value.Should().Be.LessThanOrEqualTo(TimeSpec.MaxWeeksPerYear); prevResult = result; if (IsDebugEnabled) { log.Debug("Current=[{0}], Added=[{1}], AddWeeks=[{2}]", current, result, addWeeks); } } } }); }
/// <summary> /// 해당일자의 주차를 구한다. 문화권(Culture) 및 새해 첫주차에 대한 정의에 따라 주차가 달라진다. /// ref : http://www.simpleisbest.net/archive/2005/10/27/279.aspx /// ref : http://en.wikipedia.org/wiki/ISO_8601#Week_dates /// </summary> /// <remarks> /// <see cref="CalendarWeekRule"/> 값에 따라 WeekOfYear 가 결정된다. /// /// FirstDay : 1월1일이 포함된 주를 무조건 첫째 주로 삼는다. (우리나라, 미국 등의 기준) : .NET의 설정대로 하면 이렇게 된다. /// FirstFourDayWeek : 1월1일이 포함된 주가 4일 이상인 경우에만 그 해의 첫 번째 주로 삼는다. (ISO 8601) /// 예) 한 주의 시작 요일이 일요일이고 1월1일이 일/월/화/수 중 하나이면 1월1일이 포함된 주는 해당 해의 첫 번째 주이다. /// 예) 한 주의 시작 요일이 일요일이고 1월1일이 목/금/토 중 하나이면 1월1일이 포함된 주는 해당 해의 첫 번째 주로 간주하지 않는다. /// 예) 2005년 1월 1일은 토요일이므로 1월1일이 포함된 주는 2005년의 첫 번째 주로 간주하지 않는다. /// FirstFullWeek : 1월의 첫 번째 주가 7일이 아니면 해당 해의 첫 번째 주로 삼지 않는다. /// 예) 한 주의 시작 요일이 일요일인 경우, 1월1일이 일요일이 아니라면 1월1일이 포함된 주는 해당 해의 첫 번째 주로 간주하지 않는다. /// </remarks> /// <param name="moment">주차(WeekOfYear)를 산정하기 위한 일자</param> /// <param name="timeCalendar">주차 계산을 위한 규칙 정보를 가진 TimeCalendar 인스턴스</param> /// <returns>지정된 일자가 속한 Week Of Year를 반환</returns> public static YearAndWeek GetYearAndWeek(this DateTime moment, ITimeCalendar timeCalendar) { timeCalendar.ShouldNotBeNull("timeCalendar"); var culture = timeCalendar.Culture.GetOrCurrentCulture(); var weekRule = timeCalendar.CalendarWeekRule; var firstDayOfWeek = timeCalendar.FirstDayOfWeek; if(IsDebugEnabled) log.Debug("특정일[{0}] 의 주차를 계산합니다. culture=[{1}], weekRule=[{2}], firstDayOfWeek=[{3}]", moment, culture, weekRule, firstDayOfWeek); var week = culture.Calendar.GetWeekOfYear(moment, weekRule, firstDayOfWeek); var year = moment.Year; //!+ NOTE: .NET 라이브러리가 1월1일 기준으로는 정상작동하지만, 12월 31로 계산하면, 무조건 FirstDay 형식으로 작업해버린다. //!+ FirstFourDayWeek Rule에 따르면 12월 31일이 다음해의 첫주차에 속할 경우도 있지만, .NET에서는 53주차로 반환해 버린다. //!+ 예 12월 31일이 월요일 경우 2001년 53주차가 아니라 2002년 1주차가 되어야 한다. //!+ 이를 해결하기 위해 부가적인 작업이 들어간다. // if(weekRule == CalendarWeekRule.FirstFourDayWeek && firstDayOfWeek == DayOfWeek.Monday) { var weekRange = new TimeRange(TimeTool.StartTimeOfWeek(moment, (DayOfWeek?)firstDayOfWeek), DurationUtil.Week); if(moment.Month == 12 && weekRange.HasInside(new DateTime(year + 1, 1, 1))) { var startDate = moment.AddYears(1).StartTimeOfYear(); if((int)startDate.DayOfWeek > (int)firstDayOfWeek && (int)startDate.DayOfWeek - (int)firstDayOfWeek < 4) { year++; week = 1; } } } // NOTE : 연도 보정 (1월인데, Week가 충분히 큰 숫자 이상이라면, 전년도의 주차를 따른다는 것이다. 그러므로 Year를 전년도로 설정해준다. if(moment.Month == 1 && week > 10) year--; var result = new YearAndWeek(year, week); if(IsDebugEnabled) log.Debug("일자[{0}] 의 주차는 [{4}]입니다. culture=[{1}], weekRule=[{2}], firstDayOfWeek=[{3}]", moment, culture, weekRule, firstDayOfWeek, result); return result; }
/// <summary> /// <paramref name="yearAndWeek"/>에 <paramref name="weeks"/>만큼의 주차를 더한 주차를 계산합니다. /// </summary> /// <param name="yearAndWeek">기준 주차</param> /// <param name="weeks">더할 주차 값</param> /// <param name="timeCalendar">TimeCalendar</param> /// <returns>기준 주차에 주차를 더한 주차 정보</returns> public static YearAndWeek AddWeekOfYears(this YearAndWeek yearAndWeek, int weeks, ITimeCalendar timeCalendar) { timeCalendar.ShouldNotBeNull("timeCalendar"); if(IsDebugEnabled) log.Debug("주차 연산을 수행합니다... yearAndWeek=[{0}], weeks=[{1}], timeCalendar=[{2}]", yearAndWeek, weeks, timeCalendar); var result = new YearAndWeek(yearAndWeek.Year, yearAndWeek.Week); if(weeks == 0) return result; if(weeks > 0) { weeks += result.Week.Value; if(weeks < GetEndYearAndWeek(yearAndWeek.Year.Value, timeCalendar).Week) { result.Week = weeks; return result; } while(weeks >= 0) { var endWeek = GetEndYearAndWeek(result.Year.Value, timeCalendar); if(weeks <= endWeek.Week) { result.Week = Math.Max(weeks, 1); return result; } weeks -= endWeek.Week.Value; result.Year++; } result.Week = Math.Max(weeks, 1); } else { weeks += result.Week.Value; if(weeks == 0) { result.Year--; result.Week = GetEndYearAndWeek(result.Year.Value, timeCalendar).Week.Value; return result; } if(weeks > 0) { result.Week = weeks; return result; } while(weeks <= 0) { result.Year--; var endWeek = GetEndYearAndWeek(result.Year.Value, timeCalendar); weeks += endWeek.Week.Value; if(weeks > 0) { result.Week = Math.Max(weeks, 1); return result; } } result.Week = Math.Max(weeks, 1); } if(IsDebugEnabled) log.Debug("주차 연산을 수행했습니다!!! yearAndWeek=[{0}], weeks=[{1}], result=[{2}]", yearAndWeek, weeks, result); return result; }
public void AddWeekOfYearsTest() { const int maxAddWeeks = 40; Action<CultureInfo> @AddWeekOfYearsAction = (culture) => { var currentCulture = culture; Enumerable .Range(2000, 20) .RunEach(year => { const int step = 10; YearAndWeek prevResult = new YearAndWeek(); var maxWeek = WeekTool.GetEndYearAndWeek(year, currentCulture); for(var week = 1; week < maxWeek.Week; week += step) { for(var addWeeks = -maxAddWeeks; addWeeks <= maxAddWeeks; addWeeks += step) { var current = new YearAndWeek(year, week); var result = WeekTool.AddWeekOfYears(current, addWeeks, currentCulture); if(addWeeks != 0 && prevResult.Week.HasValue) { if(result.Year == prevResult.Year) Assert.AreEqual(prevResult.Week + step, result.Week, @"Prev=[{0}], Result=[{1}], addWeeks=[{2}]", prevResult, result, addWeeks); } result.Week.Should().Have.Value(); result.Week.Value.Should().Be.GreaterThan(0); result.Week.Value.Should().Be.LessThanOrEqualTo(TimeSpec.MaxWeeksPerYear); prevResult = result; if(IsDebugEnabled) log.Debug("Current=[{0}], Added=[{1}], AddWeeks=[{2}]", current, result, addWeeks); } } }); }; Parallel.For(0, CultureTestData.Default.Count(), i => @AddWeekOfYearsAction(CultureTestData.Default[i])); var rule = WeekOfYearRuleKind.Iso8601; ParallelTool.ForWithStep(2000, 2020, 2, year => { const int step = 10; var prevResult = new YearAndWeek(); var maxWeek = WeekTool.GetEndYearAndWeek(year, null, rule); for(var week = 1; week < maxWeek.Week; week += step) { for(var addWeeks = -maxAddWeeks; addWeeks <= maxAddWeeks; addWeeks += step) { var current = new YearAndWeek(year, week); var result = WeekTool.AddWeekOfYears(current, addWeeks, null, rule); if(addWeeks != 0 && prevResult.Week.HasValue) { if(result.Year == prevResult.Year) Assert.AreEqual(prevResult.Week + step, result.Week, @"Prev={0}, Result={1}, addWeeks={2}", prevResult, result, addWeeks); } result.Week.Should().Have.Value(); result.Week.Value.Should().Be.GreaterThan(0); result.Week.Value.Should().Be.LessThanOrEqualTo(TimeSpec.MaxWeeksPerYear); prevResult = result; if(IsDebugEnabled) log.Debug("Current=[{0}], Added=[{1}], AddWeeks=[{2}]", current, result, addWeeks); } } }); }