รับวันแรกและวันสุดท้ายของเดือนโดยใช้วัตถุ DateTime ที่กำหนด


198

ฉันต้องการรับวันแรกและวันสุดท้ายของเดือนที่มีวันที่ที่กำหนดวันที่นั้นมาจากค่าในฟิลด์ UI

ถ้าฉันใช้ตัวเลือกเวลาฉันสามารถพูดได้

var maxDay = dtpAttendance.MaxDate.Day;

แต่ฉันพยายามรับมันจากวัตถุ DateTime ดังนั้นถ้าฉันมีสิ่งนี้ ...

DateTime dt = DateTime.today;

วิธีการที่จะได้รับในวันแรกและวันสุดท้ายของเดือนจากdt?


มันไม่ชัดเจนในสิ่งที่คุณถาม มีค่าเดียวเก็บไว้ใน_Dateตัวแปร คุณพยายาม "ได้รับจำนวนสูงสุด" จากมูลค่านั้นคืออะไร
David

มันกำลังลดต่ำลงเพราะคนสงสัยว่าทำไมคุณถึงต้องการคิดเช่นนั้นและใช้สิ่งนั้นแบบไหน คุณไม่ได้บอกอะไรเราเกี่ยวกับปัญหาเริ่มแรกของคุณที่นี่
Mo Patel

มันเป็นการดีกว่าที่จะถามว่าคุณต้องการทำอะไร? ไม่เหมือนที่คุณต้องการจะทำอย่างไร? การใช้งานอื่นอาจแนะนำคุณอย่างถูกต้อง
Shell

2
@ Chathuranga คุณรู้ว่าอะไรจะเป็นวันที่ต่ำสุดของเดือนใด ๆ .... แต่คำถามคือวันสุดท้ายของเดือนปัจจุบันคืออะไร .. คุณจะได้รับเช่นนี้ .. เพิ่ม 1 เดือนในวันที่ปัจจุบันและลบ 1 วันจากที่ date .. ตอนนี้คุณจะได้รับวันสุดท้ายของเดือนปัจจุบันของคุณ
Shell

คำตอบ:


480

DateTimeโครงสร้างเก็บค่าเดียวเท่านั้นไม่ใช่ช่วงของค่า MinValueและMaxValueเป็นฟิลด์สแตติกซึ่งเก็บช่วงของค่าที่เป็นไปได้สำหรับอินสแตนซ์ของDateTimeโครงสร้าง DateTimeข้อมูลเหล่านี้จะคงที่และไม่เกี่ยวข้องกับโดยเฉพาะกรณีของ พวกเขาเกี่ยวข้องกับการDateTimeพิมพ์เอง

การอ่านที่แนะนำ: คงที่ (อ้างอิง C #)

UPDATE: รับช่วงเดือน:

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ด้วยวิธีนี้มันจะทำงานเสมอโดยไม่คำนึงถึงความละเอียด (ฉันแน่ใจเห็บจะดี แต่ใครจะรู้ว่าอนาคตนำ ... nanoticks?)
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
คุณมีเครดิตน้อยเกินไปสำหรับความพยายามของคุณแม้ว่า มันมีประโยชน์!
Dion V.

2
ขอบคุณ @DionV - ยินดีที่ได้ชื่นชม! คำตอบสั้น ๆ นั้นยอดเยี่ยมเมื่อคุณรีบ แต่ฉันคิดว่าการวิเคราะห์เชิงลึกเพื่อติดตามมักจะมีประโยชน์
WooWaaBob

คุณพลาดทางเลือกอื่น: LastDayOfMonth_AddMethod_SpecialCase(หรืออะไรทำนองนั้น) ฉันคาดหวังว่าวันแรกของเดือนเป็นพารามิเตอร์ฉันคิดว่าสิ่งที่เร็วที่สุดควรเป็นสิ่งที่LastDayOfMonth_AddMethodทำ! เพื่อให้ง่ายเหมือน:return value.AddMonths(1).AddDays(-1);
Andrew

1
ขอบคุณ @Andrew และนี่คือผลลัพธ์ของฉันโดยใช้: 2835 ms สำหรับ LastDayOfMonth_SpecialCase () และ; 4685 ms สำหรับ LastDayOfMonth_AddMethod_SpecialCase () ซึ่งอาจสมเหตุสมผลเมื่อพิจารณาเวลาการสร้าง 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));

โดยทั่วไปจะหลีกเลี่ยงคำตอบที่เป็นรหัสเท่านั้น ลองเพิ่ม a 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" และส่งข้อยกเว้นว่าไม่มีวันดังกล่าว
Pawel

ตั้งแต่เมื่อธันวาคมคือ 13 มีทั้งหมด 12 เดือน คุณใช้ปฏิทินอะไร
Vitaly

หากวันที่ของคุณคือเดือนธันวาคมมันจะพยายามเพิ่มหนึ่งเดือนซึ่งจะทำให้มีข้อยกเว้น ใหม่ DateTime (date.Year, 12+ 1, 1, 0, 0, 0, date.Kind) .AddDays (-1); การแทนที่วันหนึ่งจะทำหลังจากวันที่และเวลาจะถูกสร้างขึ้นดังนั้นในเดือนธันวาคมมันจะล้มเหลวเมื่อพยายามวันที่สำหรับเดือน "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

ฉันใช้สิ่งนี้ในสคริปต์ของฉัน (เหมาะกับฉัน) แต่ฉันต้องการวันที่เต็มโดยไม่ต้องตัดให้เหลือเพียงวันที่และเวลา

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