Hitung tanggal dari nomor minggu


Jawaban:


258

Saya memiliki masalah dengan solusi oleh @HenkHolterman bahkan dengan perbaikan oleh @RobinAndersson.

Membaca tentang standar ISO 8601 menyelesaikan masalah dengan baik. Gunakan hari Kamis pertama sebagai target dan bukan hari Senin. Kode di bawah ini juga akan berfungsi untuk Minggu 53 tahun 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);
}       

3
Saya telah mencoba hampir setiap solusi yang diberikan di sini, yang satu ini adalah satu-satunya yang bekerja dengan benar untuk saya saat ini. Saat ini, tanggal 7 Februari 2012. Nomor minggu adalah # 6. Kode ini memberi saya tanggal 6 Februari dengan benar sebagai tanggal mulai minggu ini. Solusi lain semuanya memberi saya tanggal 13 Februari, yang sebenarnya merupakan tanggal mulai minggu # 7.
HaukurHaf

1
Bekerja Seperti pesona .. menguji beberapa data: pastebin.com/mfx8s1vq Semua bekerja dengan sempurna! Terima kasih Mikael!
Mittchel

1
@ RobinWassén-Andersson Untung Anda meninjau kembali pertanyaan ini kemudian: D 6 suara lagi dan saya akan mengikatnya dengan jawaban yang "tidak" benar hehe.
Mikael Svenson

2
Saran: Jadikan "ISO8601" sebagai nama metode, karena tidak ada jawaban 'benar' yang universal.
Henk Holterman

1
@ Muflix Saya tidak cukup tahu tentang SQL dan tanggal / kalender untuk diketahui - tetapi melakukan CLR akan berhasil.
Mikael Svenson

36

Saya suka solusi yang diberikan oleh Henk Holterman. Tetapi untuk menjadi budaya yang lebih mandiri, Anda harus mendapatkan hari pertama dalam seminggu untuk budaya saat ini (tidak selalu hari Senin):

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

Saya telah menambahkan ini ke Mannex, sebagai metode ekstensi ke Calendar dan DateTimeFormatInfo. Saya juga telah mereferensikan jawaban ini untuk kredit.
Atif Aziz

1
Tidak berfungsi dengan baik di mesin saya. Ini menunjukkan tanggal dengan 0010, bukan 2010. Saya tidak tahu apakah itu masalah dalam kerangka .net atau dalam fungsi ini. Usaha yang bagus, bagaimanapun ...
Eduardo Xavier

6
firstMondayadalah nama variabel yang buruk untuk sesuatu yang mungkin bukan hari Senin. =)
mflodin

14

UPDATE : .NET Core 3.0 dan .NET Standard 2.1 telah dikirimkan dengan tipe ini.

Kabar baik! Sebuah permintaan tarik menambahkan System.Globalization.ISOWeekke NET Core hanya bergabung dan saat ini dijadwalkan untuk rilis 3.0. Mudah-mudahan ini akan menyebar ke platform .NET lainnya dalam waktu yang tidak terlalu lama.

Anda harus bisa menggunakan ISOWeek.ToDateTime(int year, int week, DayOfWeek dayOfWeek)metode untuk menghitung ini.

Anda dapat menemukan kode sumbernya di sini .


9

Cara termudah mungkin adalah menemukan hari Senin pertama dalam setahun, dan kemudian menambahkan jumlah minggu yang relevan. Berikut beberapa contoh kode. Ini mengasumsikan angka minggu mulai dari 1, dengan cara:

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

4
Ya, tapi hari senin pertama tahun ini bisa jadi termasuk minggu 52 | 53 tahun sebelumnya.
Henk Holterman

1
Itu tergantung bagaimana Anda ingin mendefinisikan sesuatu. Sayangnya kami tidak memiliki banyak informasi untuk dibahas di sini ... Saya berharap ini berguna.
Jon Skeet

3

Secara pribadi, saya akan memanfaatkan info budaya untuk mengetahui hari dalam seminggu dan kembali ke hari pertama budaya dalam seminggu. Saya tidak yakin apakah saya menjelaskannya dengan benar, berikut ini contohnya:

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

3

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

2

Menurut ISO 8601: 1988 yang digunakan di Swedia , minggu pertama dalam setahun adalah minggu pertama yang memiliki setidaknya empat hari dalam tahun baru.

Jadi, jika minggu Anda dimulai pada hari Senin, Kamis pertama setiap tahun dalam minggu pertama. Anda dapat DateAdd atau DateDiff dari itu.


2

Dengan asumsi angka minggu dimulai dari 1

DateTime dt =  new DateTime(YearNumber, 1, 1).AddDays((WeekNumber - 1) * 7 - (WeekNumber == 1 ? 0 : 1));
return dt.AddDays(-(int)dt.DayOfWeek);

Ini akan memberi Anda hari pertama dalam minggu tertentu. Saya belum melakukan banyak pengujian, tapi sepertinya berhasil. Ini solusi yang lebih kecil daripada kebanyakan solusi lain yang saya temukan di web, jadi saya ingin berbagi.


jawaban yang buruk. Menggunakan DateTime.Parse tidak diperlukan karena DateTime memiliki konstruktor yang membutuhkan tahun, bulan & hari. new DateTime(1,1,YearNumber)
Jamiec

Memperbarui kode untuk membuat DateTime baru daripada menggunakan parse. Sudah terlambat. ;)
QuinnG

2

Perpustakaan Periode Waktu gratis untuk .NET menyertakan kelas sesuai ISO 8601 Minggu :

// ----------------------------------------------------------------------
public static DateTime GetFirstDayOfWeek( int year, int weekOfYear )
{
  return new Week( year, weekOfYear ).FirstDayStart;
} // GetFirstDayOfWeek

2

Yang ini berhasil untuk saya, itu juga memiliki keuntungan dengan mengharapkan cultureinfo sebagai parameter untuk menguji formula dengan budaya yang berbeda. Jika kosong, ia mendapat info budaya saat ini ... nilai yang valid seperti: "it", "en-us", "fr", ... dan seterusnya. Triknya adalah dengan mengurangi angka minggu dari hari pertama tahun itu, yang mungkin 1 untuk menunjukkan bahwa hari pertama adalah dalam minggu pertama. Semoga ini membantu.

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

2

Berikut adalah metode yang kompatibel dengan angka minggu yang Google Analytics, dan juga skema penomoran yang sama yang kami gunakan secara internal di Intel, dan yang saya yakin juga digunakan dalam banyak konteks lain.

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

2

Saya mencoba beberapa kode di atas dan beberapa memiliki kesalahan kecil, ketika Anda mencoba tahun yang berbeda dengan hari awal yang berbeda dalam seminggu Anda akan melihatnya, saya mengambil kode Jon Skeet, memperbaikinya dan berfungsi, kode yang sangat sederhana.

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

1

Sedikit mengubah kode Mikael Svenson. Saya menemukan minggu pada hari Senin pertama dan mengubah nomor minggu dengan tepat.

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

0

Minggu 1 didefinisikan sebagai minggu yang dimulai pada hari Senin dan berisi hari Kamis pertama dalam setahun.


1
Itu satu definisi, ada definisi lainnya.
Henk Holterman

1
Standar ISO menetapkan minggu 1 sebagai minggu dengan hari Kamis pertama tahun itu di dalamnya.
RickardN


0

Saya sedikit meningkatkan solusi Thomas dengan penggantian:

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

Jika tidak, tanggal tersebut sebelumnya satu minggu ..

Terima kasih Thomas, bantuan besar.


0

Saya menggunakan salah satu solusi tetapi memberi saya hasil yang salah, hanya karena menghitung hari Minggu sebagai hari pertama dalam seminggu.

Aku berubah:

var firstDay = new DateTime(DateTime.Now.Year, 1, 1).AddDays((weekNumber - 1) * 7);
var lastDay = firstDay.AddDays(6);

untuk:

var lastDay = new DateTime(DateTime.Now.Year, 1, 1).AddDays((weekNumber) * 7);
var firstDay = lastDay.AddDays(-6);

dan sekarang berfungsi sebagai pesona.


1
Tapi apakah ini menjawab pertanyaan OP? Ini 1/1 ditambah jumlah hari yang tetap. Tidak ada konsep hari pertama dalam seminggu.
Gert Arnold

0

Solusi yang diusulkan tidak lengkap - ini hanya berfungsi untuk CalendarWeekRule.FirstFullWeek. Jenis aturan minggu lainnya tidak berfungsi. Ini dapat dilihat dengan menggunakan test case ini:

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

0

Saya telah membuat versi perbaikan dari solusi yang diusulkan yang lebih sederhana dan parameter 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;
}

0

ini adalah solusi saya ketika kita ingin menghitung tanggal tahun tertentu, nomor minggu dan hari dalam seminggu.

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

0

Saya menyederhanakan kode yang disediakan Mikael Svensson yang benar untuk banyak negara di Eropa.

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

0

Saya telah menulis dan menguji kode berikut dan bekerja dengan baik untuk saya. Tolong beri tahu saya jika ada yang menghadapi masalah dengan ini, saya telah memposting pertanyaan juga untuk mendapatkan jawaban terbaik. Seseorang mungkin merasa berguna.

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

Ini salah - setidaknya untuk banyak negara Eropa. GetFirstDateOfWeekByWeekNumber(2020, 29)untuk Minggu 29 tahun 2020 ini kembali 07/20/2020. Tapi hari pertama minggu ke-29 adalah07/13/2020
Matthias Burger

0

Saat ini, tidak ada kelas C # yang menangani nomor ISO 8601week dengan benar. Meskipun Anda dapat membuat contoh suatu budaya, mencari yang paling dekat dan memperbaikinya, saya pikir lebih baik melakukan perhitungan lengkap sendiri:

    /// <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;
    }
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.