特定の日付の正しい週番号を取得する


222

私はたくさんグーグルしてたくさんの解決策を見つけましたが、どれも2012-12-31の正しい週番号を私に与えません。MSDN(リンク)の例でも失敗します。

2012-12-31は月曜日なので、第1週になるはずですが、試したすべての方法で53が得られます。試した方法の一部を次に示します。

MDSNライブラリから:

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

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

解決策2:

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

解決策3:

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

更新

次のメソッドは、日付が2012-12-31の場合、実際には1を返します。言い換えると、私の問題は、私の方法が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
年末の第1週はどうですか?つまり、どこで入手できるかわかります。しかし、53は私には理にかなっています。
benjer3

1
私のコードスニペットでは、CultureInfoなどを取得します。その時までに私のプログラムは私が使っているカレンダーを知っていると思いました。(ここドイツでは、2012年12月31日は2013年の第1週です)
Amberlamps

このコードは、たとえば31-dec-2016または1-jan-2016などの日付を試す必要があるため、うまく機能しません
cavej03

@ cavej03 31-dec-2016は52週目で、GetIso8601WeekOfYearは52を返すので、正しく動作すると思います。
Muflix 2016

回答:


311

このMSDNページに記載されているように、ISO8601の週番号と.Netの週番号にはわずかな違いがあります

より詳しい説明については、MSDNブログのこの記事を参照してください:「Microsoft .NetのISO 8601 Week of Year形式

簡単に言うと、.Netでは週を何年にもわたって分割できますが、ISO標準では分割できません。記事には、年の最後の週の正しいISO 8601週番号を取得する簡単な関数もあります。

更新次のメソッドは、実際には1を返します。2012-12-31これは、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);
} 

1
@il_guru週が日曜日から始まるとしたらどうでしょうか?「月曜日」をすべて「日曜日」に置き換えますか?
User2012384 2016年

2
@ User2012384 fyi、ISO8601標準に準拠する場合、firstDayOfWeekは常に月曜日でなければなりません。
Starceaker 2016

1
@il_guruもしこの「チート」さえ必要ないなら、あなたは?私が知る限り、「GetWeekOfYear(time、CalendarWeekRule.FirstFourDayWeek、DayOfWeek.Monday);」正常に動作します。
Starceaker 2016

2
2番目のリンク(Microsoft .NetのISO 8601 Week of Year形式)を見ると、Microsoftから直接質問への回答を見つけることができます。例は、2007年
12

3
私は手動でこれが2012年から2018年まで正しいことを確認しました(epochconverter.comが正しいと仮定)。2018年の第1週が実際に1月1日から始まった今年は、みんなが喜ぶはずです。
エイダン

32

年間で52週間を超える可能性があります。毎年、52週間+ 1または+2(うるう年)の日数が追加されます。彼らは53週目を補います。

  • 52週* 7日= 364日。

したがって、毎年、少なくとも1日は1日余分にあります。うるう年は2。これらの余分な日は、別の週としてカウントされますか?

何週間あるかは、実際には週の開始日に依存します。これを2012年について考えてみましょう。

  • 米国(日曜日->土曜日):2012年12月30日および2012年12月31日の場合、52週+週2日の短い週。この結果、合計53週間になります。今年の最後の2日間(日曜日+月曜日)は、独自の短い週を構成します。

現在のカルチャーの設定を確認して、カルチャーが週の最初の日として何を使用しているかを確認してください。

ご覧のように、結果として53を取得するのは正常です。

  • ヨーロッパ(月曜日->日曜日):1月2dn(2012-1-2)が最初の月曜日なので、これは最初の週の最初の日です。1月1日の週番号を尋ねると、2011年の先週の一部と見なされるため、52が返されます。

54週目を迎えることも可能です。1月1日と12月31日が別々の週として扱われる場合、28年ごとに発生します。うるう年でもあるに違いない。

たとえば、2000年は54週間でした。1月1日(土)は最初の1週間、12月31日(日)は2週間目です。

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);

プリントアウト:年:2012週:54

上記の例のCalendarWeekRuleをFirstFullWeekまたはFirstFourDayWeekに変更すると、53が返されます。ドイツを扱っているので、月曜日を開始日としておきましょう。

したがって、53週目は2012-12-31の月曜日に始まり、1日続き、その後停止します。

53が正解です。試してみたい場合は、文化をドイツに変更します。

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

53週間ある可能性があることを知っています。しかし、日曜日(12/30)と月曜日(12/31)がある場合、米国の53週目の一部として木曜日(01/01)も数えませんか?
Amberlamps

54週目を見たことがない!
Amberlamps

特定の会計ソフトウェアパッケージにはあります。
Christophe Geers

わかりました。アメリカのカレンダーでは可能ですが、ドイツのカレンダーでは起こりません。
アンバーランプ

1
問題についての深い考えに+1しますが、別の回答で提供されたブログエントリのコードスニペットは実際に私の問題を解決しました。
アンバーランプ

22

これが方法です:

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

最も重要なのはCalendarWeekRuleパラメータです。

ここを参照してください:https : //msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=IT-IT&k=k(System.Globalization.CalendarWeekRule) ; k(TargetFrameworkMoniker-.NETFramework


3
「Calendar.GetWeekOfYear()は、CalendarWeekRule.FirstFourDayWeekとDayOfWeek.Mondayが渡されたときのISO 8601週とほぼ同じであることにいくつかの人々が気づきましたが、少し異なります。具体的には、ISO 8601は常に7日週です。」blogs.msdn。 microsoft.com/shawnste/2006/01/24/...
ユハPalomäki

1
あなたのリンクはイタリア語です
radbyx

新しいDateTime(2000、12、31)の場合、52を返しますか?
Leszek P

18

朗報!プル要求の追加System.Globalization.ISOWeek.NETのコアにはちょうど合併され、現在3.0のリリースを予定しています。うまくいけば、それほど遠くない将来に他の.NETプラットフォームに伝播するでしょう。

タイプには次のシグネチャがあり、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);
    }
}

ここでソースコードを見つけることができます。

更新:これらのAPIは、.NET Standardの2.1バージョンに含まれています


v 3.0がリリースされるときのアイデアは?
ジャック

「今年後半に.NET Core 3の最初のプレビューをリリースし、2019年に最終バージョンをリリースする予定です。」(ロードマップのブログ投稿から
khellang

@khellang日時に基づいて、月の通算週をどのように取得できますか?第33週を取得する代わりに、第2週を見たいと思います(8週目は第33週なので)。stackoverflow.com/questions/58039103/...
Roxy'Pro

11

正しいISO-8601週数を生成する.Netカルチャーがないように思われるため、部分的に正しい修正を試みるのではなく、組み込みの週の決定を完全にバイパスし、手動で計算を行います結果。

私が最終的に得たのは、次の拡張メソッドです。

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

まず最初に((int)date.DayOfWeek + 6) % 7)、曜日番号を決定します。0=月曜日、6 =日曜日。

date.AddDays(-((int)date.DayOfWeek + 6) % 7) 要求された週番号に先行する月曜日の日付を決定します。

3日後は、目標とする木曜日で、週が何年かを決定します。

年内の(ゼロベースの)日数を7で割ると(切り捨て)、その年の(ゼロベースの)週数が得られます。

c#では、整数の計算結果は暗黙的に切り捨てられます。


1
これはこれまでのところ最良の解決策ですが、少し異なる方法で指摘します。DateTimeは、先発的なグレゴリオ暦に基づいています。つまり、グレゴリオ暦を1年に拡張しています。0001年1月1日は、月曜。Ticks:1月1日0001から100ナノ秒単位の数は、私たちが書くことができる日に86,400秒があることを考慮するとされるvar thursday = date.AddDays(3 - date.Ticks / 86400 / 10_000_000 % 7); return (thursday.DayOfYear - 1) / 7 + 1;
エイドリアンS

5

.NET 3.0以降では、ISOWeek.GetWeekOfDate-Methodを使用できます。

年+週番号形式の年は、DateTime週が年の境界をまたぐため、の年と異なる場合があることに注意してください。


3

上記のコードからil_guruからC#をPowershellポートに:

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
あなたのコードを説明できますか?StackOverflowではコードのみの回答は一般に受け入れられないため、削除される可能性があります。
Wai Ha Lee、

@Wai Ha Leeコードはすでに説明されています。上記のil_guruの投稿を参照してください。私は彼のコードをpowershellに移植しましたが、これまでのところpowershellには適切なソリューションがないため、他の人がpowershellでそれを使用できます。
Rainer

それがあなたがしたことなら、あなたは答え自体に元の著者を信用すべきです。あなたが移植した答えには、おそらく削除したより良いコメントがあります。また、質問はPowerShellについて言及していなかったため、これは質問に対する回答ではありません。
ワイハリー

1
@Wai Ha Leeしました。質問にはすでに回答済みのマークが付けられているため、これは他の言語で書かれた同じ解決策になります。私は、Powershell言語での解決策を探している人々に好意を示しました(Powershellでの解決策を探していましたが、C#での解決策を見つけました)。アイデアは同じままで、作者はそれに対する信用を得ます。
Rainer

2

c#とDateTimeクラスを使用して、週番号のISO 8601スタイルを決定する最も簡単な方法。

これを尋ねてください:今年の何番目の木曜日は今週の木曜日です。答えは、希望する週番号と同じです。

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;

これは、米国とロシアの両方の文化でうまく機能します。ISO 8601も正しくなります。これは、ロシアの週が月曜日から始まるためです。


1

以下は、il_guruの拡張バージョンとnull許容バージョンですの回答の。

拡張:

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

ヌル可能:

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

使用法:

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

0

問題は、1週間が2012年か2013年かをどのように定義しますか?推測では、6週間は2013年なので、今週は2013年の最初の週としてマークする必要があります。

これが正しい方法かどうかはわかりません。その週は2012年(12月31日月曜日)に開始されるため、2012年の最後の週としてマークする必要があります。したがって、それは2012年の53日になるはずです。2013年の最初の週は、月曜日の7日から開始する必要があります。

これで、曜日情報を使用して、エッジウィークの特定のケース(1年の最初と最後の週)を処理できます。それはすべてあなたの論理に依存します。


さて、私はあなたのポイントを見ることができます。問題は、私の方法のいずれかで2013/01/07を使用するときです。つまり、第2週です。つまり、2012/12/31は第53週で、2013/01/07は第2週です。第1週はないと思うかもしれません。 2013年のしかし、私は2013年1月1日にしようとすると、それは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

il_guruの回答に基づいて、年コンポーネントも返す自分のニーズに合わせてこのバージョンを作成しました。

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

私たちの週が月曜日に始まると仮定すると、これらの2つの方法が役立ちます

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

1年は52週で、ラップ年の場合は1日または2日です(52 x 7 = 364)。2012年12月31日は53週、つまり2012年はラップ年であるため2日しかない週です。


これは誤りです。年の週の最初の日は、曜日のどの曜日にあってもかまいません。週の数え方によっては、その年には54週目がある場合があります。
krowe2

-2
public int GetWeekNumber()
{
   CultureInfo ciCurr = CultureInfo.CurrentCulture;
   int weekNum = ciCurr.Calendar.GetWeekOfYear(DateTime.Now, 
   CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
   return weekNum;
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.