Получение первого и последнего дня месяца с использованием заданного объекта DateTime

Я хочу получить первый день и последний день месяца, в котором находится заданная дата. Дата исходит из значения в поле пользовательского интерфейса.

Если я использую таймер, я могу сказать

var maxDay = dtpAttendance.MaxDate.Day;

Но я пытаюсь получить его из объекта DateTime. Поэтому, если у меня есть это...

DateTime dt = DateTime.today;

Как получить первый и последний день месяца от dt?

Ответ 1

DateTime хранит только одно значение, а не диапазон значений. MinValue и MaxValue являются статическими полями, которые содержат диапазон возможных значений для экземпляров структуры DateTime. Эти поля являются статическими и не относятся к конкретному экземпляру DateTime. Они относятся к типу DateTime.

Рекомендуемое чтение: static (ссылка на С#)

ОБНОВЛЕНИЕ: Получение диапазона месяцев:

DateTime date = ...
var firstDayOfMonth = new DateTime(date.Year, date.Month, 1);
var lastDayOfMonth = firstDayOfMonth.AddMonths(1).AddDays(-1);

Ответ 2

Это более длинный комментарий к ответам @Sergey и @Steffen. Написав аналогичный код сам в прошлом, я решил проверить, что было больше performant, помня, что ясность также важна.

Результат

Вот пример тестового результата для 10 миллионов итераций:

2257 ms for FirstDayOfMonth_AddMethod()
2406 ms for FirstDayOfMonth_NewMethod()
6342 ms for LastDayOfMonth_AddMethod()
4037 ms for LastDayOfMonth_AddMethodWithDaysInMonth()
4160 ms for LastDayOfMonth_NewMethod()
4212 ms for LastDayOfMonth_NewMethodWithReuseOfExtMethod()
2491 ms for LastDayOfMonth_SpecialCase()

код

Я использовал LINQPad 4 (в режиме С# Program), чтобы запустить тесты с оптимизацией компилятора. Вот проверенный код, который используется как методы расширения для ясности и удобства:

public static class DateTimeDayOfMonthExtensions
{
    public static DateTime FirstDayOfMonth_AddMethod(this DateTime value)
    {
        return value.Date.AddDays(1 - value.Day);
    }

    public static DateTime FirstDayOfMonth_NewMethod(this DateTime value)
    {
        return new DateTime(value.Year, value.Month, 1);
    }

    public static DateTime LastDayOfMonth_AddMethod(this DateTime value)
    {
        return value.FirstDayOfMonth_AddMethod().AddMonths(1).AddDays(-1);
    }

    public static DateTime LastDayOfMonth_AddMethodWithDaysInMonth(this DateTime value)
    {
        return value.Date.AddDays(DateTime.DaysInMonth(value.Year, value.Month) - value.Day);
    }

    public static DateTime LastDayOfMonth_SpecialCase(this DateTime value)
    {
        return value.AddDays(DateTime.DaysInMonth(value.Year, value.Month) - 1);
    }

    public static int DaysInMonth(this DateTime value)
    {
        return DateTime.DaysInMonth(value.Year, value.Month);
    }

    public static DateTime LastDayOfMonth_NewMethod(this DateTime value)
    {
        return new DateTime(value.Year, value.Month, DateTime.DaysInMonth(value.Year, value.Month));
    }

    public static DateTime LastDayOfMonth_NewMethodWithReuseOfExtMethod(this DateTime value)
    {
        return new DateTime(value.Year, value.Month, value.DaysInMonth());
    }
}

void Main()
{
    Random rnd = new Random();
    DateTime[] sampleData = new DateTime[10000000];

    for(int i = 0; i < sampleData.Length; i++) {
        sampleData[i] = new DateTime(1970, 1, 1).AddDays(rnd.Next(0, 365 * 50));
    }

    GC.Collect();
    System.Diagnostics.Stopwatch sw = System.Diagnostics.Stopwatch.StartNew();
    for(int i = 0; i < sampleData.Length; i++) {
        DateTime test = sampleData[i].FirstDayOfMonth_AddMethod();
    }
    string.Format("{0} ms for FirstDayOfMonth_AddMethod()", sw.ElapsedMilliseconds).Dump();

    GC.Collect();
    sw.Restart();
    for(int i = 0; i < sampleData.Length; i++) {
        DateTime test = sampleData[i].FirstDayOfMonth_NewMethod();
    }
    string.Format("{0} ms for FirstDayOfMonth_NewMethod()", sw.ElapsedMilliseconds).Dump();

    GC.Collect();
    sw.Restart();
    for(int i = 0; i < sampleData.Length; i++) {
        DateTime test = sampleData[i].LastDayOfMonth_AddMethod();
    }
    string.Format("{0} ms for LastDayOfMonth_AddMethod()", sw.ElapsedMilliseconds).Dump();

    GC.Collect();
    sw.Restart();
    for(int i = 0; i < sampleData.Length; i++) {
        DateTime test = sampleData[i].LastDayOfMonth_AddMethodWithDaysInMonth();
    }
    string.Format("{0} ms for LastDayOfMonth_AddMethodWithDaysInMonth()", sw.ElapsedMilliseconds).Dump();

    GC.Collect();
    sw.Restart();
    for(int i = 0; i < sampleData.Length; i++) {
        DateTime test = sampleData[i].LastDayOfMonth_NewMethod();
    }
    string.Format("{0} ms for LastDayOfMonth_NewMethod()", sw.ElapsedMilliseconds).Dump();

    GC.Collect();
    sw.Restart();
    for(int i = 0; i < sampleData.Length; i++) {
        DateTime test = sampleData[i].LastDayOfMonth_NewMethodWithReuseOfExtMethod();
    }
    string.Format("{0} ms for LastDayOfMonth_NewMethodWithReuseOfExtMethod()", sw.ElapsedMilliseconds).Dump();

    for(int i = 0; i < sampleData.Length; i++) {
        sampleData[i] = sampleData[i].FirstDayOfMonth_AddMethod();
    }

    GC.Collect();
    sw.Restart();
    for(int i = 0; i < sampleData.Length; i++) {
        DateTime test = sampleData[i].LastDayOfMonth_SpecialCase();
    }
    string.Format("{0} ms for LastDayOfMonth_SpecialCase()", sw.ElapsedMilliseconds).Dump();

}

Анализ

Я был удивлен некоторыми из этих результатов.

Хотя в нем не так много, FirstDayOfMonth_AddMethod был немного быстрее, чем FirstDayOfMonth_NewMethod в большинстве прогонов теста. Тем не менее, я думаю, что у последнего есть несколько более четкие намерения, и поэтому я предпочитаю это.

LastDayOfMonth_AddMethod был явным неудачником против LastDayOfMonth_AddMethodWithDaysInMonth, LastDayOfMonth_NewMethod и LastDayOfMonth_NewMethodWithReuseOfExtMethod. Между самыми быстрыми тремя в нем нет ничего особенного, и это сводится к вашим личным предпочтениям. Я выбираю ясность LastDayOfMonth_NewMethodWithReuseOfExtMethod при повторном использовании другого полезного метода расширения. ИМХО, его намерение более ясное, и я готов принять небольшую стоимость исполнения.

LastDayOfMonth_SpecialCase предполагает, что вы предоставляете первый месяц в специальном случае, когда вы, возможно, уже рассчитали эту дату, и использует метод add с DateTime.DaysInMonth, чтобы получить результат. Это быстрее, чем другие версии, как и следовало ожидать, но если вы не в отчаянной потребности в скорости, я не вижу смысла иметь этот особый случай в вашем арсенале.

Заключение

Вот класс метода расширения с моими выборами и, в общем, согласен с @Steffen, я верю:

public static class DateTimeDayOfMonthExtensions
{
    public static DateTime FirstDayOfMonth(this DateTime value)
    {
        return new DateTime(value.Year, value.Month, 1);
    }

    public static int DaysInMonth(this DateTime value)
    {
        return DateTime.DaysInMonth(value.Year, value.Month);
    }

    public static DateTime LastDayOfMonth(this DateTime value)
    {
        return new DateTime(value.Year, value.Month, value.DaysInMonth());
    }
}

Если вы зашли так далеко, спасибо за время! Это было весело: ¬). Прокомментируйте, если у вас есть другие предложения для этих алгоритмов.

Ответ 3

Получение диапазона месяцев с .Net API (только другим способом):

DateTime date = ...
var firstDayOfMonth = new DateTime(date.Year, date.Month, 1);
var lastDayOfMonth = new DateTime(date.Year, date.Month, DateTime.DaysInMonth(date.Year, date.Month));

Ответ 4

DateTime dCalcDate = DateTime.Now;
dtpFromEffDate.Value = new DateTime(dCalcDate.Year, dCalcDate.Month, 1);
dptToEffDate.Value = new DateTime(dCalcDate.Year, dCalcDate.Month, DateTime.DaysInMonth(dCalcDate.Year, dCalcDate.Month));

Ответ 5

"Last day of month" на самом деле "First day of *next* month, minus 1". Итак, вот что я использую, нет необходимости в методе "DaysInMonth":

public static DateTime FirstDayOfMonth(this DateTime value)
{
    return new DateTime(value.Year, value.Month, 1);
}

public static DateTime LastDayOfMonth(this DateTime value)
{
    return value.FirstDayOfMonth()
        .AddMonths(1)
        .AddMinutes(-1);
}

Примечание: Причина, по которой я использую AddMinutes(-1), а не AddDays(-1) здесь, состоит в том, что обычно вам нужны эти функции даты для отчетности за некоторый период даты, а когда вы создаете отчет за период, "дата окончания" должна фактически быть чем-то вроде Oct 31 2015 23:59:59, чтобы ваш отчет работал правильно - включая все данные из последнего дня месяца.

т.е. вы на самом деле получаете "последний момент месяца" здесь. Не последний день.

Хорошо, я сейчас закрою.

Ответ 6

Здесь вы можете добавить один месяц в первый день текущего месяца, чем удалить 1 день с этого дня.

DateTime now = DateTime.Now;
var startDate = new DateTime(now.Year, now.Month, 1);
var endDate = startDate.AddMonths(1).AddDays(-1);

Ответ 7

Если вам нужна только дата

var firstDay = new DateTime(date.Year, date.Month, 1, 0, 0, 0, date.Kind);
var lastDay = new DateTime(date.Year, date.Month, 1, 0, 0, 0, date.Kind).AddMonths(1).AddDays(-1);

Если вы хотите сохранить время

var firstDay = new DateTime(date.Year, date.Month, 1, date.Hour, date.Minute, date.Second, date.Kind);
var lastDay = new DateTime(date.Year, date.Month, 1, date.Hour, date.Minute, date.Second, date.Kind).AddMonths(1).AddDays(-1);

Ответ 8

Принятый здесь ответ не учитывает тип экземпляра DateTime. Например, если исходный экземпляр DateTime был типом UTC, то, создав новый экземпляр DateTime, вы создадите экземпляр Unknown Kind, который затем будет обрабатываться как локальное время на основе настроек сервера. Поэтому более правильным способом получить первую и последнюю дату месяца будет следующее:

var now = DateTime.UtcNow;
var first = now.Date.AddDays(-(now.Date.Day - 1));
var last = first.AddMonths(1).AddTicks(-1);

Таким образом сохраняется исходный тип экземпляра DateTime.

Ответ 9

Я использовал это в своем сценарии (работает для меня), но мне нужна была полная дата без необходимости обрезать ее только до даты и без времени.

public DateTime GetLastDayOfTheMonth()
{
    int daysFromNow = DateTime.DaysInMonth(DateTime.Now.Year, DateTime.Now.Month) - (int)DateTime.Now.Day;
    return DateTime.Now.AddDays(daysFromNow);
}

Ответ 10

Попробуйте следующее:

string strDate = DateTime.Now.ToString("MM/01/yyyy");

Ответ 11

Для персидской культуры

PersianCalendar pc = new PersianCalendar();            

var today = pc.GetDayOfMonth(DateTime.Now);
var firstDayOfMonth = pc.GetDayOfMonth(DateTime.Now.AddDays(-(today-1)));
var lastDayOfMonth = pc.GetDayOfMonth(DateTime.Now.AddMonths(1).AddDays(-today));            
Console.WriteLine("First day "+ firstDayOfMonth);
Console.WriteLine("Last day " + lastDayOfMonth);

Ответ 12

Вы можете сделать это

DateTime dt = DateTime.Now; 
DateTime firstDayOfMonth = new DateTime(dt.Year, date.Month, 1);
DateTime lastDayOfMonth = firstDayOfMonth.AddMonths(1).AddDays(-1);

Ответ 13

простой способ сделать это

Begin = new DateTime(DateTime.Now.Year, DateTime.Now.Month,1).ToShortDateString();
End = new DataFim.Text = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.DaysInMonth(DateTime.Now.Year, DateTime.Now.Month)).ToShortDateString();

Ответ 14

DateTime dCalcDate = DateTime.Now;
var startDate = new DateTime(Convert.ToInt32(Year), Convert.ToInt32(Month), 1);
var endDate = new DateTime(Convert.ToInt32(Year), Convert.ToInt32(Month), DateTime.DaysInMonth((Convert.ToInt32(Year)), Convert.ToInt32(Month)));