Отримання першого та останнього дня місяця за допомогою заданого об’єкта DateTime


198

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

Якщо я використовую функцію вибору часу, я можу сказати

var maxDay = dtpAttendance.MaxDate.Day;

Але я намагаюся отримати це від об'єкта DateTime. Тож якщо я маю це ...

DateTime dt = DateTime.today;

Як отримати перший день і останній день місяця dt?


Не ясно, що ви просите. У _Dateзмінній зберігається одне значення . Які "хв і макс" ви намагаєтеся отримати від цього значення?
Девід

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

Краще запитати, що ти хочеш робити? не так, як ти хочеш робити? може інше використання може запропонувати вам правильно.
Shell

2
@ Чхатхуранга ти знаєш, яка буде мінімальна дата в будь-якому місяці .... але питання в тому, яка остання дата поточного місяця .. ти можеш отримати таке .. додай 1 місяць у поточну дату і мінус 1 день від цього дата .. зараз у вас буде остання дата ур поточного місяця
Shell

Відповіді:


480

DateTimeструктура зберігає лише одне значення, а не діапазон значень. MinValueі MaxValueє статичними полями, які містять діапазон можливих значень для екземплярів DateTimeструктури. Ці поля є статичними і не стосуються конкретного примірника DateTime. Вони стосуються DateTimeсамого типу.

Пропоноване читання: статичне (C # Reference)

ОНОВЛЕННЯ: Отримання діапазону місяця:

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

16
Я знаю, що я тут прискіпливий, але не повинен lastDayofMonthбути firstDayOfMonth.AddMonths(1).AddSeconds(-1);?
Karl Gjertsen

36
@KarlGjertsen ви недостатньо вибагливі :) Ідеальне рішення буде AddTicks(-1), але якщо ми не піклуємося про частину часу і думаємо лише про частину дати, тоді дні прекрасно працюють
Сергій Березовський

7
Тепер це вибагливо! ;-) Питання не говорить про те, як будуть використовуватися значення, тому я схильний кодувати захисно.
Карл Ґерцен

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

2
@KarlGjertsen, ти хочеш побачити прискіпливого ... Я особисто це роблю < firstDayOfNextMonthзамість <= lastDayOfMonth. Таким чином, він завжди працюватиме незалежно від деталізації. (Я впевнений, що кліщі будуть добре, але хто знає, що майбутнє приносить ... нанотики?)
adam0101

100

Це більше довгий коментар до відповідей @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. Це швидше, ніж в інших версіях, як ви очікували, але, якщо вам не потрібна відчайдушна потреба у швидкості, я не бачу сенсу мати цей особливий випадок у своєму арсеналі.

Висновок

Ось клас методу розширення з моїм вибором, і я взагалі погоджуюся з @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

15

Отримання місячного діапазону за допомогою API .Net (просто інший спосіб):

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

Це повністю провалиться в грудні dudeeeeeee. він спробує створити дату з місяцем "13" і викине виключення, що такої дати немає.
Pawel

З тих пір, коли 13 грудня? Всього 12 місяців. Який календар ви використовуєте?
Віталій

Якщо ваша дата - грудень, вона спробує додати один місяць, що призведе до виключення. новий DateTime (date.Year, 12+ 1, 1, 0, 0, 0, date.Kind) .AddDays (-1); Вилучення одного дня з нього буде зроблено після створення дати, тому для грудня вона не вдасться при спробі дати місяця "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

Спробуйте це. В основному він обчислює кількість днів, що пройшли 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

Для перської культури

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

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".
Вазнер

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