주어진 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구조체는 값 범위가 아닌 하나의 값만 저장합니다. MinValueMaxValue인스턴스에 대한 가능한 값의 범위를 유지 정적 필드이다 DateTime구조. 이 필드는 정적이며의 특정 인스턴스와 관련이 없습니다 DateTime. 그것들은 DateTime유형 자체 와 관련이 있습니다.

추천 독서 : 정적 인 (C # 참고)

업데이트 : 월 범위 가져 오기 :

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

16
나는 여기 까다롭게 해요 알고 있지만,하지 말아야 lastDayofMonthfirstDayOfMonth.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.