David
David

Reputation: 15350

How to get the start and end times of a day

In my C# app, I pass a string variable that is of format yyyymmdd-yyyymmdd that represents a from and to date. I want to get the start and end times for these dates respectively. Currently I have the below code but was wondering if there was more of an elegant solution?

So for pdr = 20090521-20090523 would get "20090521 00:00:00" and "20090523 23:59:59"

private void ValidateDatePeriod(string pdr, out DateTime startDate, 
                                out DateTime endDate)
{
    string[] dates = pdr.Split('-');

    if (dates.Length != 2)
    {
        throw new Exception("Date period is of incorrect format");
    }

    if (dates[0].Length != 8 || dates[1].Length != 8)
    {
        throw new Exception("Split date periods are of incorrect format");
    }

    startDate = DateTime.ParseExact(dates[0] + " 00:00:00", 
        "yyyyMMdd HH:mm:ss", null);
    endDate = DateTime.ParseExact(dates[1] + "23:59:59", 
        "yyyyMMdd HH::mm:ss", null);
}

Upvotes: 88

Views: 162725

Answers (11)

Arno Brand
Arno Brand

Reputation: 11

The issue above regarding the few milliseconds can be resolved by querying the database with the next day's start date.

For example:

SELECT * FROM temp WHERE createdDate >= fromDate AND createdDate < toDate

Using the extension methods below you could set the from and to dates to:

DateTimeOffset fromDate = DateTimeOffset.UtcNow.StartOfDay();
DateTimeOffset toDate = DateTimeOffset.UtcNow.EndOfDay();
public static class DateExtentions
{
    public static DateTimeOffset StartOfDay(this DateTimeOffset dateTime)
    {
        return new DateTimeOffset(dateTime.Year, dateTime.Month, dateTime.Day, 0, 0, 0, 0, dateTime.Offset);
    }

    public static DateTimeOffset EndOfDay(this DateTimeOffset dateTime)
    {
        return dateTime.StartOfDay().AddDays(1);
    }

    public static DateTimeOffset StartOfMonth(this DateTimeOffset dateTime)
    {
        return new DateTimeOffset(dateTime.Year, dateTime.Month, 1, 0, 0, 0, 0, dateTime.Offset);
    }

    public static DateTimeOffset EndOfMonth(this DateTimeOffset dateTime)
    {
        return dateTime.StartOfMonth().AddMonths(1);
    }

    public static DateTimeOffset StartOfYear(this DateTimeOffset dateTime)
    {
        return new DateTimeOffset(dateTime.Year, 1, 1, 0, 0, 0, 0, dateTime.Offset);
    }

    public static DateTimeOffset EndOfYear(this DateTimeOffset dateTime)
    {
        return dateTime.StartOfYear().AddYears(1);
    }
}

Upvotes: 1

KayV
KayV

Reputation: 13835

In Java 8, you can do it using LocalDate as follows:

    LocalDate localDateStart = LocalDate.now();
    Date startDate = Date.from(localDateStart.atStartOfDay(ZoneId.systemDefault()).toInstant());

    LocalDate localDateEnd = localDateStart.plusDays(1);
    Date endDate = Date.from(localDateEnd.atStartOfDay(ZoneId.systemDefault()).toInstant());

Upvotes: -3

Solarev Sergey
Solarev Sergey

Reputation: 323

public static class DateTimeExtension {        
    public static DateTime StartOfTheDay(this DateTime d) => new DateTime(d.Year, d.Month, d.Day, 0, 0,0);
    public static DateTime EndOfTheDay(this DateTime d) => new DateTime(d.Year, d.Month, d.Day, 23, 59,59);
}

Upvotes: 16

Ricardo Mu&#241;oz
Ricardo Mu&#241;oz

Reputation: 1

For SQL Server (version 2008 R2 tested) this ranges works.

StarDate '2016-01-11 00:00:01.990' EndDate '2016-01-19 23:59:59.990'

Seems like ticks is greater that the last second of day and automatically round to next day. So i test and works, i made a dummy table with two dates for check what values is sql server catching and inserting in the stored procedure those parameters.

Upvotes: 0

Tracker1
Tracker1

Reputation: 19334

If you are only worried about .Net precision...

startDate = DateTime.ParseExact(dates[0], "yyyyMMdd");
endDate = DateTime.ParseExact(dates[1], "yyyyMMdd").AddTicks(-1).AddDays(1);

You really don't need to concatenate extra values onto the string for the time portion.


As an addendum, if you are using this for a query against, for example, a database...

startDate = DateTime.ParseExact(dates[0], "yyyyMMdd");
endDate = DateTime.ParseExact(dates[1], "yyyyMMdd").AddDays(1);

With a query of...

WHERE "startDate" >= @startDate AND "endDate" < @endDate

Then the precision issues noted in the comments won't really matter. The endDate in this case would not be part of the range, but the outside boundary.

Upvotes: 29

Leon van Wyk
Leon van Wyk

Reputation: 689

I use the following in C#

public static DateTime GetStartOfDay(DateTime dateTime)
{
    return new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, 0, 0, 0, 0);
}
public static DateTime GetEndOfDay(DateTime dateTime)
{
    return new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, 23, 59, 59, 999);
}

Then in MS SQL I do the following:

if datepart(ms, @dateEnd) = 0
   set @dateEnd = dateadd(ms, -3, @dateEnd)

This will result in MS SQL time of 23:59:59.997 which is the max time before becoming the next day.

You could simply use:

new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, 23, 59, 59, 999);

Which will work in MS SQL, but this is not as accurate in .Net side.

Upvotes: 6

Chris F Carroll
Chris F Carroll

Reputation: 12360

I think we're doing it wrong. There is no such thing as the end of the day. AddTick(-1) only works under the convention that there are no time intervals smaller than a tick. Which is implementation dependent. Admittedly the question comes with a reference implementation, namely the .Net Framework DateTime class, but still we should take this as a clue that the function we really want is not EndOfDay() but StartOfNextDay()

public static DateTime StartOfNextDay(this DateTime date)
{
    return date.Date.AddDays(1);
}

Upvotes: 3

anar khalilov
anar khalilov

Reputation: 17498

I am surprised to see how an incorrect answer received so many upvotes:

Wrong value

The correct version would be as follows:

public static DateTime StartOfDay(this DateTime theDate)
{
    return theDate.Date;
}

public static DateTime EndOfDay(this DateTime theDate)
{
    return theDate.Date.AddDays(1).AddTicks(-1);
}

Upvotes: 276

Andrei R&#238;nea
Andrei R&#238;nea

Reputation: 20780

You could define two extension methods somewhere, in a utility class like so :

public static DateTime EndOfDay(this DateTime date)
{
    return new DateTime(date.Year, date.Month, date.Day, 23, 59, 59, 999);
}

public static DateTime StartOfDay(this DateTime date)
{
    return new DateTime(date.Year, date.Month, date.Day, 0, 0, 0, 0);
}

And then use them in code like so :

public DoSomething()
{
    DateTime endOfThisDay = DateTime.Now.EndOfDay();
}

Upvotes: 46

Matt Brunell
Matt Brunell

Reputation: 10389

The DateTime object has a property called Date which will return just the date portion. (The time portion is defaulted to 12:00 am).

I would recommend as a more elegant solution (IMHO) that if you want to allow any datetime on the last day, then you add 1 day to the date, and compare to allow times greater than or equal to the start date, but strictly less than the end date (plus 1 day).

// Calling code.  beginDateTime and endDateTime are already set.
// beginDateTime and endDateTime are inclusive.
// targetDateTime is the date you want to check.
beginDateTime = beginDateTime.Date;
endDateTime = endDateTime.Date.AddDays(1);

if ( beginDateTime <= targetDateTime &&
     targetDateTime < endDateTime )
   // Do something.

Upvotes: 18

Lucas
Lucas

Reputation: 17424

That's pretty much what I would do, with some small tweaks (really no big deal, just nitpicking):

  • The TryParse()/TryParseExact() methods should be used which return false instead of throwing exceptions.
  • FormatException is more specific than Exception
  • No need to check for Length == 8, because ParseExact()/TryParseExact() will do this
  • "00:00:00" and "23:59:59" are not needed
  • return true/false is you were able to parse, instead of throwing an exception (remember to check value returned from this method!)

Code:

private bool ValidateDatePeriod(string pdr, out DateTime startDate, 
                        out DateTime endDate)
{
   string[] dates = pdr.Split('-');

   if (dates.Length != 2)
   {
       return false;
   }

   // no need to check for Length == 8 because the following will do it anyway
   // no need for "00:00:00" or "23:59:59" either, I prefer AddDays(1)

   if(!DateTime.TryParseExact(dates[0], "yyyyMMdd", null, DateTimeStyles.None, out startDate))
      return false;

   if(!DateTime.TryParseExact(dates[1], "yyyyMMdd", null, DateTimeStyles.None, out endDate))
      return false;

   endDate = endDate.AddDays(1);
   return true;
}

Upvotes: 4

Related Questions