Поиск первого дня календаря

Что я хочу сделать, так это создать простой календарь, и я хочу найти первый день первой недели определенного месяца. Мой календарь - понедельник → воскресный календарь, и следующий код работает, но, как вы видите, это не так приятно. У кого-нибудь есть лучшая идея о том, как получить первую дату в календаре.

var now = new DateTime(Year, Month, 1);
now = now.AddDays(1-(int)now.DayOfWeek);
now = now.Day > 15 ? now : now.AddDays(-7);

Календарь будет выглядеть следующим образом:

| <  |        Jan  2011       |  > |
------------------------------------
| Mo | Tu | We | Th | Fr | Sa | Su |
|[27]| 28 | 29 | 30 | 31 | 01 | 02 |
| 03 | 04 | 05 | 06 | 07 | 08 | 09 |
| .. | .. | .. | .. | .. | .. | .. |
| .. | .. | .. | .. | .. | .. | .. |
| 31 | 01 | 02 | 03 | 04 | 05 | 06 |

И в этом "образе" это дата [27], которую я пытаюсь найти.

Решение (Найден я лучше/очиститель, чтобы потом вычислить):

    public DateTime FirstDay()
    {
        var date = new DateTime(Date.Year, Date.Month, 1);
        while (true)
        {
            if (date.DayOfWeek == DayOfWeek.Monday)
                return date;
            date = date.AddDays(-1);
        }
        return date;
    }

    public DateTime LastDay()
    {
        var date = new DateTime(Date.Year, Date.Month, 
                                DateTime.DaysInMonth(Date.Year, Date.Month));
        while (true)
        {
            if (date.DayOfWeek == DayOfWeek.Sunday)
                return date;
            date = date.AddDays(1);
        }
        return date;
    }

/BR Andreas

Ответ 1

Я бы просто сделал это. Это так легко понять:

var firstDayOfMonth = new DateTime(year, month, 1);
DateTime startOfCalendar = 
    FirstDayOfWeekOnOrBefore(
        firstDayOfMonth,
        DayOfWeek.Monday
    );

public static DateTime FirstDayOfWeekOnOrBefore(
    DateTime date,
    DayOfWeek dayOfWeek
) {
    while(date.DayOfWeek != dayOfWeek) {
        date = date.AddDays(-1);
    }
    return date;
}

Кроме того, если вы хотите изменить свой календарь, чтобы начать с чего-то другого, кроме понедельника, теперь это тривиально. Решение, использующее модульную арифметику, не было бы таким же удобным.

Ответ 2

Вы можете использовать modulo для вычисления количества дней наполнителя без условного оператора:

DateTime firstOfMonth=new DateTime(year,month,1);
var weekDay=firstOfMonth.DayOfWeek;
int fillerDays=((int)weekDay+6)%7;
DateTime firstDayInCalendar=firstOfMonth.AddDays(-fillerDays);

Ответ 3

Вы можете попробовать это, предположив, что в первый день ур ссылается на понедельник

DateTime dt = new DateTime(2011, 2, 2);
Console.WriteLine(dt.AddDays((8 - (int)dt.DayOfWeek) % 7));

Ответ 4

Мне не нравятся циклы, потому что они дороги при использовании с LINQ
Надеюсь, что кто-то еще сможет повторно использовать этот код: (если вы находитесь в США, то просто удалите [+ 6)% 7)] в две строки)

    /// <summary>
    /// Expands the month.
    /// | <  |        Jan  2011       |  > |
    /// ------------------------------------
    /// | Mo | Tu | We | Th | Fr | Sa | Su |
    /// |[27]| 28 | 29 | 30 | 31 | 01 | 02 |
    /// | 03 | 04 | 05 | 06 | 07 | 08 | 09 |
    /// | .. | .. | .. | .. | .. | .. | .. |
    /// | .. | .. | .. | .. | .. | .. | .. |
    /// | 31 | 01 | 02 | 03 | 04 | 05 | 06 |
    /// </summary>
    /// <param name="start">Some day in the month of interest, the start date is updated to become the date of firstDayInCalendar</param>
    /// <returns>The number of days to show. This value is either (28, 35 or 42)</returns>
    public static int ExpandMonth(ref DateTime start)
    {
        DateTime first = new DateTime(start.Year, start.Month, 1);
        DateTime last = new DateTime(start.Year, start.Month, DateTime.DaysInMonth(start.Year, start.Month));
        start = first.AddDays(-((int)first.DayOfWeek + 6) % 7);
        last = last.AddDays(7 - ((int)last.DayOfWeek + 6) % 7);
        return last.Subtract(start).Days;
    }

//Томас

Ответ 5

Мне было нужно делать это довольно часто, поэтому я создал следующий метод расширения.

public static DateTime FirstDateOfCalendarMonth(this DateTime dt, DayOfWeek firstDayOfWeek = DayOfWeek.Sunday)
{
  dt = new DateTime(dt.Year, dt.Month, 1);
  while (dt.DayOfWeek != firstDayOfWeek){
    dt = dt.AddDays(-1);
  }
  return dt;
}

Используйте его так:

var firstCalDate = DateTime.Now.FirstDateOfCalendarMonth();

По умолчанию это воскресенье как первый DayOfWeek, но вы можете передать его независимо от типа DayOfWeek, например:

var firstCalDate = DateTime.Now.FirstDateOfCalendarMonth(DayOfWeek.Monday);

Ответ 6

ОБНОВЛЕНИЕ: В следующем коде есть ошибка! Используйте modulo арифметику, чтобы компенсировать это обстоятельство,.NET начинает неделю в воскресенье! См. Другие решения здесь (без функции с дополнительной функцией).

Если вам нужно найти "27" (т.е. первый день для показа месяца), просто используйте это:

DateTime monthStart = new DateTime(year, month, 1); 
DateTime monthDisplayStart = monthStart.AddDays(-((int)monthStart.DayOfWeek - 1));