从日期算起一年又一周

时间:2012-04-29 10:47:04

标签: c#

我需要返回给定日期的年份和星期。听起来很简单。但是要正确2012-01-01必须返回2011-52,因为2012年第1周将从1月2日开始。

要查找周,我使用:

GregorianCalendar calw = new GregorianCalendar(GregorianCalendarTypes.Localized);
return calw.GetWeekOfYear(DateTime.Parse("2012-01-01"), CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday).ToString();

此回报52.(正确)

但我怎样才能获得这一年?

编辑:

在此处提供帮助:http://codebetter.com/petervanooijen/2005/09/26/iso-weeknumbers-of-a-date-a-c-implementation/

这似乎有效:

 private int weekYear(DateTime fromDate)
    {
        GregorianCalendar cal = new GregorianCalendar(GregorianCalendarTypes.Localized);
        int week = weekNumber(fromDate);
        int month = cal.GetMonth(fromDate);
        int year = cal.GetYear(fromDate);

        //week starts after 31st december
        if (week > 50 && month == 1)
            year = year - 1;
        //week starts before 1st January
        if (week < 5 && month == 12)
            year = year + 1;

        return year;
    }
private int weekNumber(DateTime fromDate)
{
    // Get jan 1st of the year
    DateTime startOfYear = fromDate.AddDays(-fromDate.Day + 1).AddMonths(-fromDate.Month + 1);
    // Get dec 31st of the year
    DateTime endOfYear = startOfYear.AddYears(1).AddDays(-1);
    // ISO 8601 weeks start with Monday 
    // The first week of a year includes the first Thursday 
    // DayOfWeek returns 0 for sunday up to 6 for saterday
    int[] iso8601Correction = { 6, 7, 8, 9, 10, 4, 5 };
    int nds = fromDate.Subtract(startOfYear).Days + iso8601Correction[(int)startOfYear.DayOfWeek];
    int wk = nds / 7;
    switch (wk)
    {
        case 0:
            // Return weeknumber of dec 31st of the previous year
            return weekNumber(startOfYear.AddDays(-1));
        case 53:
            // If dec 31st falls before thursday it is week 01 of next year
            if (endOfYear.DayOfWeek < DayOfWeek.Thursday)
                return 1;
            else
                return wk;
        default: return wk;
    }
}

5 个答案:

答案 0 :(得分:9)

Noda Time很容易为您处理:

Noda Time v1.x

using System;
using NodaTime;

public class Test
{
    static void Main()
    {
        LocalDate date = new LocalDate(2012, 1, 1);
        Console.WriteLine($"WeekYear: {date.WeekYear}");             // 2011
        Console.WriteLine($"WeekOfWeekYear: {date.WeekOfWeekYear}"); // 52
    }
}

Noda Time v2.x

using System;
using NodaTime;
using NodaTime.Calendars;

public class Test
{
    static void Main()
    {
        LocalDate date = new LocalDate(2012, 1, 1);
        IWeekYearRule rule = WeekYearRules.Iso;
        Console.WriteLine($"WeekYear: {rule.GetWeekYear(date)}"); // 2011
        Console.WriteLine($"WeekOfWeekYear: {rule.GetWeekOfWeekYear(date)}"); // 52
    }
}

那是使用ISO日历系统,其中第一周的第一周开始,同一年至少有4天。 (与CalendarWeekRule.FirstFourDayWeek类似。)如果您需要其他日历系统,请在LocalDate构造函数中指定它。在1.x和2.x之间,周年规则的处理方式略有不同。

编辑:请注意,这为这种情况提供了正确的价值(周年少于比日历年)和一年中另一年的情况,其中一周 - 年可以更多比日历年。例如,2012年12月31日是2013年第1周。

让图书馆为你做这件事的美妙之处在于:它的工作就是了解这类事情。您的代码不应该担心它。你应该只能问你想要什么。

答案 1 :(得分:4)

您可以通过这种方式根据CalendarWeekRule获取周数:

var d = new DateTime(2012, 01, 01);
System.Globalization.CultureInfo cul = System.Globalization.CultureInfo.CurrentCulture;
var firstDayWeek = cul.Calendar.GetWeekOfYear(
    d,
    System.Globalization.CalendarWeekRule.FirstDay,
    DayOfWeek.Monday);
int weekNum = cul.Calendar.GetWeekOfYear(
    d,
    System.Globalization.CalendarWeekRule.FirstFourDayWeek,
    DayOfWeek.Monday);
int year = weekNum >= 52 && d.Month == 1 ? d.Year - 1 : d.Year;

您可能希望将CalendarWeekRule.FirstDayCalendarWeekRule.FirstFourDayWeek进行比较。通过这种方式,您可以获得周数和年份(DateTime.Year-1,如果它们不同)。

答案 2 :(得分:2)

这只是一个边缘情况,您必须为其添加特殊代码。从日期字符串中获取年份,然后如果周= 52且月份= 1,则从年份中减去一个。

答案 3 :(得分:0)

在我的方法中,我利用了这一事实,GetWeekOfYear()显示了与同一周的同一年相同年份的正确ISO-8601周数。所以我查找周四属于同一周的给定日期,然后在其上调用GetWeekOfYear()

我无法做到这一点来获得正确的一年,因为没有符合iso8601标准的方法,所以如果星期四属于与给定日期不同的年份,我会进行一年的调整。

解决方案基本上是一个三线程:

using System.Globalization;

namespace TESTS
{
    class Program
    {
        static void Main(string[] args)
        {

            //sample dates with correct week numbers in comments:
            string[] dats = new string[] { 
                 "2011-12-31","2012-01-01"  //1152
                ,"2012-12-31","2013-01-01"  //1301
                ,"2013-12-31","2014-01-01"  //1401
                ,"2014-12-31","2015-01-01"  //1501
                ,"2015-12-31", "2016-01-01" //1553
            };

            foreach (string str in dats)
            {
                Console.WriteLine("{0} {1}", str, GetCalendarWeek(DateTime.Parse(str)));
            }

            Console.ReadKey();
    }


       public static int GetCalendarWeek(DateTime dat)
        {
            CultureInfo cult = System.Globalization.CultureInfo.CurrentCulture;

            // thursday of the same week as dat.
            // value__ for Sunday is 0, so I need (true, not division remainder %) mod function to have values 0..6 for monday..sunday
            // If you don't like casting Days to int, use some other method of getting that thursday
            DateTime thursday = dat.AddDays(mod((int)DayOfWeek.Thursday-1,7) - mod((int)dat.DayOfWeek-1,7));
            //week number for thursday:
            int wk = cult.Calendar.GetWeekOfYear(thursday, cult.DateTimeFormat.CalendarWeekRule, cult.DateTimeFormat.FirstDayOfWeek);
            // year adjustment - if thursday is in different year than dat, there'll be -1 or +1:
            int yr = dat.AddYears(thursday.Year-dat.Year).Year;

            // return in yyww format:
            return 100 * (yr%100) + wk;
        }

        // true mod - helper function (-1%7=-1, I need -1 mod 7 = 6):
        public static int mod(int x, int m)
        {
            return (x % m + m) % m;
        }


}

答案 4 :(得分:0)

我已经解决了类似的问题,结果应该在&#34; YYYYWW&#34;格式。我想避免硬编码日期和使用第三方库。

我的测试用例是日期1.1.2017,应该返回201652年(Iso YearWeek)

要获得周数,我使用了线程:Get the correct week number of a given date,它返回没有年份的周数。 最后我从星期一(iso周的第一天)到达所需日期的正确年份:

    // returns only week number
    // from [Get the correct week number of a given date] thread
    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
        var week = CultureInfo.InvariantCulture.Calendar.GetWeekOfYear(time, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
        return week;
    }

    // returns int YearWeek in format "YYYYWW"
    public static int GetIso8601YearWeekOfYear(DateTime time)
    {
        var delta = (-((time.DayOfWeek - CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek + 7) % 7));
        var firstDayofWeek = time.AddDays(delta); // takeMonday
        var week = GetIso8601WeekOfYear(time);
        var yearWeek = (firstDayofWeek.Year * 100) + week;
        return yearWeek;
    }