Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

DateTimeOffset Same Day comparison

I ran into interesting issue with the following requirement: Test if a process had run in the same day, if not run the process. The dates are stored as DataTimeOffset.

My original approach was to:

  1. Convert both values to UTC, because these dates could have been created in different time zones and have different offsets.
  2. View the Date value of each value. This is done after converting to UTC because the Date method ignores the offset.

Most scenarios this worked but I came across one case that the logic would fail. If one of the values had a time that was close to the previous/next day so that the when converting to UTC it would change the date. If the other value didn't have a time that also converted to the previous/next day then the date comparison failed.

So I ended up with the following logic to include that scenario:

public static bool SameDate(DateTimeOffset first, DateTimeOffset second)
{
    bool returnValue = false;
    DateTime firstAdjusted = first.ToUniversalTime().Date;
    DateTime secondAdjusted = second.ToUniversalTime().Date;

    // If date is now a day ahead after conversion, than add/deduct a day to other date if that date hasn't advanced
    if (first.Date < firstAdjusted.Date && second.Date == secondAdjusted.Date)
        secondAdjusted = secondAdjusted.Date.AddDays(1);
    if (first.Date > firstAdjusted.Date && second.Date == secondAdjusted.Date)
        secondAdjusted = secondAdjusted.Date.AddDays(-1);

    if (second.Date < secondAdjusted.Date && first.Date == firstAdjusted.Date)
        firstAdjusted = firstAdjusted.Date.AddDays(1);
    if (second.Date > secondAdjusted.Date && first.Date == firstAdjusted.Date)
        firstAdjusted = firstAdjusted.Date.AddDays(-1);

    if (DateTime.Compare(firstAdjusted, secondAdjusted) == 0)
        returnValue = true;

    return returnValue;
}

Here is the Unit Tests that were failing that now pass:

 [TestMethod()]
 public void SameDateTest()
 {
 DateTimeOffset current = DateTimeOffset.Now;
 DateTimeOffset first = current;
 DateTimeOffset second = current;

 // 23 hours later, next day, with negative offset (EST) -- First rolls over
 first = new DateTimeOffset(2014, 1, 1, 19, 0, 0, new TimeSpan(-5, 0, 0));
 second = new DateTimeOffset(2014, 1, 2, 18, 0, 0, new TimeSpan(-5, 0, 0));
 Assert.IsFalse(Common.SameDate(first, second));

 // 23 hours earlier, next day, with postive offset -- First rollovers
 first = new DateTimeOffset(2014, 1, 1, 4, 0, 0, new TimeSpan(5, 0, 0));
 second = new DateTimeOffset(2014, 1, 2, 5, 0, 0, new TimeSpan(5, 0, 0));
 Assert.IsFalse(Common.SameDate(first, second));

 // 23 hours later, next day, with negative offset (EST) -- Second rolls over
 first = new DateTimeOffset(2014, 1, 2, 18, 0, 0, new TimeSpan(-5, 0, 0));
 second = new DateTimeOffset(2014, 1, 1, 19, 0, 0, new TimeSpan(-5, 0, 0));
 Assert.IsFalse(Common.SameDate(first, second));

 // 23 hours earlier, next day, with postive offset -- Second rolls over
 first = new DateTimeOffset(2014, 1, 2, 5, 0, 0, new TimeSpan(5, 0, 0));
 second = new DateTimeOffset(2014, 1, 1, 4, 0, 0, new TimeSpan(5, 0, 0));
 Assert.IsFalse(Common.SameDate(first, second));
}

My gut feeling is that there is a cleaner approach than to increment/decrement based on the other value. Is there a better approach?

The primary criteria:

  1. Adjust the both dates to have the same offset.
  2. Return true only if both first and second dates occur in the same calendar day, not within 24 hours.
like image 852
Josh Avatar asked Dec 08 '25 04:12

Josh


2 Answers

Adjust the one of the dates for the difference in both dates:

public static bool SameDate(DateTimeOffset first, DateTimeOffset second)
{
    bool returnValue = false;
    DateTime firstAdjusted = first.ToUniversalTime().Date;
    DateTime secondAdjusted = second.ToUniversalTime().Date;

    // calculate the total diference between the dates   
    int diff = first.Date.CompareTo(firstAdjusted) - second.Date.CompareTo(secondAdjusted);
    // the firstAdjusted date is corected for the difference in BOTH dates.
    firstAdjusted = firstAdjusted.AddDays(diff);

    if (DateTime.Compare(firstAdjusted, secondAdjusted) == 0)
        returnValue = true;

    return returnValue;
}

In this function I am asuming that the offset will never be more than 24 hours. IE the difference between a date and it's adjusted date will not be two or more days. If this is not the case, then you can use time span comparison.

like image 54
martijn Avatar answered Dec 10 '25 17:12

martijn


The general methodology you describe (convert to common time-zone then compare date portion) is reasonable. The problem here is actually one of deciding on the frame of reference. You have arbitrarily chosen UTC as your frame of reference. At first gloss it doesn't matter so long as they are compared in the same time zone, but as you have found this can put them on either side of a day boundary.

I think you need to refine your specification. Ask yourself which of the following you are trying to determine.

  • Whether the values occur on the same calendar day for a specified time zone.
  • Whether the values are no more than 12 hours apart (+/- 12hrs is a 24hr period).
  • Whether the values are no more than 24 hours apart.

It might also be something else. The definition as implemented (but rejected by you) is "Whether the values occur on the same UTC calendar day".

like image 27
Peter Wone Avatar answered Dec 10 '25 17:12

Peter Wone



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!