주어진 DateTime 객체를 사용하여 한 달의 첫 날과 마지막 날 얻기


198

주어진 날짜가있는 달의 첫날과 마지막 날을 얻고 싶습니다. 날짜는 UI 필드의 값에서 비롯됩니다.

시간 선택기를 사용하는 경우

var maxDay = dtpAttendance.MaxDate.Day;

그러나 DateTime 객체에서 가져 오려고합니다. 이걸 가지고 있다면 ...

DateTime dt = DateTime.today;

에서 첫날과 마지막 날을 얻는 방법은 dt무엇입니까?


무엇을 요구하는지 명확하지 않습니다. _Date변수 에 단일 값이 저장되어 있습니다. 그 값에서 어떤 "최소 및 최대"를 얻으려고합니까?
— David

사람들이 왜 당신이 그런 생각을하고 싶어하는지, 그리고 당신이 그런 일을 어디에 사용할 것인지 궁금해하기 때문에 그것은 하향식입니다. 귀하는 여기에서 귀하의 초기 문제에 대해 아무 것도 말하지 않습니다
— Mo Patel

무엇을하고 싶은지 물어 보는 것이 좋습니다 ? 마음에 들지 않으면 어떻게 하시겠습니까? 다른 용도로 올바르게 제안 할 수 있습니다.
— Shell

2
@Chathuranga u는 어떤 달의 최소 날짜가 무엇인지 알고 있습니다 ....하지만 질문은 현재 달의 마지막 날짜입니다. u는 이렇게 될 수 있습니다. date .. 이제 u는 이번 달 ur의 마지막 날짜를 얻습니다
— Shell

답변:


480

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

16
나는 여기 까다롭게 해요 알고 있지만,하지 말아야 lastDayofMonth할 firstDayOfMonth.AddMonths(1).AddSeconds(-1);?
— Karl Gjertsen

36
@KarlGjertsen 당신은 충분히 까다 롭지 않습니다 :) 완벽한 해결책이 될 것입니다 AddTicks(-1), 그러나 우리가 시간 부분에 대해 신경 쓰지 않고 날짜 부분에 대해서만 생각하면 일이 잘 작동합니다.
— Sergey Berezovskiy

7
이제 까다 롭습니다! ;-) 질문은 값이 어떻게 사용 될지 말하지 않으므로 방어 적으로 코딩하는 경향이 있습니다.
— Karl Gjertsen

@SergeyBerezovskiy이 점을 높이고 싶지 않지만 DateTime을 새로 만들 때 시간대 정보를 잃지 않습니까? 원래 DateTime 인스턴스에 연결된 시간대 정보는 이와 같이 새 인스턴스를 만들 때 손실됩니다.
— Marko

2
@KarlGjertsen, 당신은 까다 롭고 싶어요 ... 나는 개인적으로 < firstDayOfNextMonth대신 <= lastDayOfMonth합니다. 이렇게하면 세분성에 관계없이 항상 작동합니다. (진드기는 괜찮을 것이라 확신하지만, 미래가 무엇을 가져올 지 누가 알겠습니까?)
— adam0101

100

이것은 @Sergey와 @Steffen의 답변에 대한 긴 의견입니다. 과거에 비슷한 코드를 직접 작성한 후에 는 선명도가 중요하다는 것을 기억하면서 가장 성능이 우수한 것을 확인하기로 결정했습니다 .

결과

1000 만 반복에 대한 테스트 실행 결과의 예는 다음과 같습니다.

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는 해당 날짜를 이미 계산했을 수있는 특수한 경우에 첫 달을 제공한다고 가정하고 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());
    }
}

당신이 이것을 멀리 가지고 있다면, 시간 내 주셔서 감사합니다! 재미있었습니다 :)). 이 알고리즘에 대한 다른 제안 사항이 있으면 의견을 보내주십시오.


5
당신은 노력에 대한 신용이 너무 적습니다. 유용합니다!
— Dion V.

2
감사합니다 @DionV. -감사하게 생각합니다! 짧은 답변은 급한 경우에 좋지만 더 깊은 분석이 종종 유용하다고 생각합니다.
— WooWaaBob

다른 대안을 놓쳤습니다 LastDayOfMonth_AddMethod_SpecialCase. 매월 첫날을 매개 변수로 예상하면 가장 빠른 것이 무엇인지 생각합니다 LastDayOfMonth_AddMethod! 따라서 다음과 같이 간단합니다.return value.AddMonths(1).AddDays(-1);
— Andrew

1
감사합니다 @Andrew 그리고 여기에 내 결과가 사용됩니다 : LastDayOfMonth_SpecialCase ()에 대해 2835 ms, 및; LastDayOfMonth_AddMethod_SpecialCase ()의 경우 4685ms struct 생성 시간을 고려할 때 아마도 DateTime의 내부 표현은 일을 간단한 작업으로 만들지 만 몇 달은 더 복잡한 알고리즘을 추가한다는 것을 의미합니다.
— WooWaaBob

15

.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 일을 삭제하는 것보다 현재 달의 1 일에 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);

12 월 dudeeeeeee에서 완전히 실패합니다. 월 "13"으로 날짜 시간을 만들고 해당 날짜가 없다는 예외를 throw합니다.
— Pawel

12 월 13 일 이래부터? 총 12 개월이 있습니다. 어떤 달력을 사용하십니까?
— Vitaly

날짜가 12 월이면 1 개월을 추가하여 예외가 발생합니다. new DateTime (날짜, 12+ 1, 1, 0, 0, 0, 날짜. 종류) .AddDays (-1); 날짜 / 시간을 생성 한 후 하루에서 빼는 작업은 12 월 동안 "13"월 날짜를 시도 할 때 실패합니다. 시도해 볼 수 있습니다.
— Pawel

1

여기에 허용 된 답변은 DateTime 인스턴스의 종류를 고려하지 않습니다. 예를 들어 원래 DateTime 인스턴스가 UTC 종류 인 경우 새 DateTime 인스턴스를 만들면 알 수없는 종류 인스턴스가 만들어지며 서버 설정에 따라 현지 시간으로 처리됩니다. 따라서 달의 첫 번째 날짜와 마지막 날짜를 얻는 더 적절한 방법은 다음과 같습니다.

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

이런 식으로 DateTime 인스턴스의 원래 종류가 유지됩니다.


1

이거 한번 해봐:

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

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

이 코드는 "new DataFim.Text"로 인해 컴파일되지 않습니다.
— Wazner

-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)));
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.