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


211

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

Если я использую средство выбора времени, я могу сказать

var maxDay = dtpAttendance.MaxDate.Day;

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

DateTime dt = DateTime.today;

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


Непонятно, о чем вы спрашиваете. В _Dateпеременной хранится одно значение . Какие «минимальные и максимальные значения» вы пытаетесь получить из этого значения?
David

он получает отрицательные голоса, потому что люди задаются вопросом, почему вы захотите так подумать и где бы вы могли это использовать. Вы здесь ничего не говорите нам о своей первоначальной проблеме
Мо Патель

Лучше спросить, чем ты хочешь заниматься? не нравится. Как вы хотите? может другое использование может предложить вам правильно.
Shell

2
@Chathuranga, ты знаешь, какая будет минимальная дата любого месяца .... но вопрос в том, какая последняя дата текущего месяца ... ты можешь получить вот так ... добавить 1 месяц к текущей дате и минус 1 день от этого дата ... теперь вы получите последнюю дату текущего месяца
Shell

Ответы:


503

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

Рекомендуемая литература: статические (Справочник по C #)

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

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

19
Я знаю, что придираюсь, но разве не должно lastDayofMonthбыть firstDayOfMonth.AddMonths(1).AddSeconds(-1);?
Karl Gjertsen 05

42
@KarlGjertsen, вы недостаточно разборчивы :) Идеальное решение будет AddTicks(-1), но если мы не будем заботиться о части времени и думать только о части даты, тогда дни работают нормально
Сергей Березовский

7
Теперь это придирчиво! ;-) В вопросе не говорится, как будут использоваться значения, поэтому я предпочитаю кодировать в защитной манере.
Karl Gjertsen 06

@SergeyBerezovskiy Я бы не хотел поднимать этот вопрос, но разве вы не теряете информацию о часовом поясе, когда вы новичок в DateTime, как это? Информация о часовом поясе, которая была прикреплена к исходному экземпляру DateTime, теряется, когда вы создаете новый экземпляр, подобный этому.
Marko

3
@KarlGjertsen, вы хотите увидеть разборчива ... лично я < firstDayOfNextMonthвместо <= lastDayOfMonth. Таким образом, он всегда будет работать независимо от степени детализации. (Я уверен, с клещами все будет в порядке, но кто знает, что принесет будущее ... нанотики?)
adam0101

105

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

Результат

Вот пример результата тестового запуска для 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 (в режиме программы C #) для запуска тестов с включенной оптимизацией компилятора. Вот проверенный код, размеченный как методы расширения для ясности и удобства:

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с повторным использованием другого полезного метода расширения. IMHO его намерение более ясное, и я готов принять небольшую стоимость производительности.

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

Вывод

Вот класс метода расширения с моим выбором и в целом согласен с @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());
    }
}

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


5
Тем не менее, ваши усилия слишком малы. Это полезно!
Дион В.

2
Спасибо @DionV. - приятно быть оцененным! Короткие ответы хороши, когда вы спешите, но я думаю, что часто бывает полезен более глубокий анализ.
WooWaaBob

Вы пропустили другую альтернативу: LastDayOfMonth_AddMethod_SpecialCase(или что-то в этом роде). Ожидая первого дня месяца в качестве параметра, я думаю, что самое быстрое должно быть то, что LastDayOfMonth_AddMethodпроисходит! Это будет очень просто:return value.AddMonths(1).AddDays(-1);
Эндрю

1
Спасибо @Andrew, и вот мои результаты с использованием этого: 2835 мс для LastDayOfMonth_SpecialCase () и; 4685 мс для LastDayOfMonth_AddMethod_SpecialCase (). Что, вероятно, имеет смысл, если учесть время создания структуры и то, что внутреннее представление DateTime, вероятно, делает добавление дней простой операцией, а добавление месяцев - более сложным алгоритмом.
WooWaaBob 04

16

Получение диапазона месяцев с .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));

7

" 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чтобы ваш отчет работал правильно - включая все данные за последний день месяца.

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

Хорошо, я сейчас заткнусь.


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

Как правило, избегайте ответов, содержащих только код. Подумайте о добавлении, descriptionкоторое помогает объяснить ваш код. Спасибо
MickyD

3

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

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

2

Если тебя волнует только дата

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

Он полностью провалится в декабре, чувак. он попытается создать дату и время с месяцем «13» и выдаст исключение, что такой даты нет.
Павел

С каких это пор 13 декабря? Всего 12 месяцев. Какой календарь вы используете?
Виталий

Если ваша дата декабрь, он попытается добавить один месяц, что приведет к исключению. новый DateTime (date.Year, 12+ 1, 1, 0, 0, 0, date.Kind) .AddDays (-1); Вычитание одного дня из него будет сделано после того, как будет создано datetime, поэтому в декабре он не удастся при попытке установить дату для месяца «13». Можешь попробовать.
Павел

1

В принятом здесь ответе не учитывается тип экземпляра 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.



1

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

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

1

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

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

1

Попробуйте. Он в основном вычисляет количество прошедших дней DateTime.Now, затем вычитает из него единицу и использует новое значение, чтобы найти первое число текущего месяца. Оттуда он использует это DateTimeи использует .AddMonths(-1)для получения первых чисел предыдущего месяца.

Получение последнего дня прошлого месяца делает в основном то же самое, за исключением того, что добавляет единицу к количеству дней в месяце и вычитает это значение из DateTime.Now.AddDays, давая вам последний день предыдущего месяца.

int NumberofDays = DateTime.Now.Day;
int FirstDay = NumberofDays - 1;
int LastDay = NumberofDays + 1;
DateTime FirstofThisMonth = DateTime.Now.AddDays(-FirstDay);
DateTime LastDayOfLastMonth = DateTime.Now.AddDays(-LastDay);
DateTime CheckLastMonth = FirstofThisMonth.AddMonths(-1);

0

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

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

-1

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

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

Этот код не компилируется из-за «нового DataFim.Text».
Wazner 09

-1
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)));
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.