Why datetime cannot compare? - c#

my C# unit test has the following statement:
Assert.AreEqual(logoutTime, log.First().Timestamp);
Why it is failed with following information:
Assert.AreEqual failed. Expected:<4/28/2010 2:30:37 PM>. Actual:<4/28/2010 2:30:37 PM>.
Are they not the same?
Update:
Use this if you only care to second:
Assert.AreEqual(logoutTime.ToString(), log.First().Timestamp.ToString());

Have you verified that the number of ticks/milliseconds are equal?
If you do DateTime.Now() twice back to back, they will appear to be the same number down to the minute and probably even down to the second, but they will often vary by ticks. If you want to check equality only to the minute, compare each DateTime only to that degree. For information on rounding DateTimes, see here
A note about resolution:
The Now property is frequently used to measure performance. However, because of its low resolution, it is not suitable for use as a benchmarking tool. A better alternative is to use the Stopwatch class.

Try something like Assert.AreEqual(logoutTime.Ticks, log.First().Timestamp.Ticks)

The Assert fail method is probably calling ToString() on the DateTime which returns a truncated, human-readable form of the date without the milliseconds component. This is why it appears they are equal when, in fact, the DateTime object has a precision of a 100-nanosecond unit (known as a Tick). That means it is highly unlikely two DateTime objects will have the exact same value. To compare you probably want to truncate the value, perhaps by formatting the date to the fidelity you require.

Using entity framework, if you fetch from the database using .AsNoTracking() the DateTime property will be rounded ever so slightly, whereas it won't necessarily be rounded without .AsNoTracking() if the original value is still in memory. Thus for integration tests involving a round-trip to the database, I guess it's best to use .ToString() because the database will reduce the precision slightly.

Are you sure that logoutTime and log.First().Timestamp are both typed as DateTime?
If so, they might also have different values for the more specific time infomation (e.g., milliseconds).

Assuming that logoutTime and log.First().Timestamp are both of type DateTime, you should try using this instead:
Assert.AreEqual(logoutTime.Ticks, log.First().Timestamp.Ticks);

While working on unit test, I found below steps very useful to compare some date with mock date.
Mock date field as below:
mockDate = new DateTime(2020, 10, 10)
Call service method.
Assertion can be done like this:
Assert.AreEqual("10/10/2020 12:00:00 AM", _service.startDate.ToString());
Note while doing assertion:
We have to provide date like : 10/10/2020 12:00:00 AM
Then on service method date item we need to apply ToString(), this will convert date time into string value for comparison
If we just have to do assertion with datetime today date
Assert.AreEqual(DateTime.Today, _service.startDate);

I suppose Assert.AreEqual<T> uses Object.Equals() to determine equality of the objects but not the values.
Probably this statement is comparing two different objects and therefore is returning false.

Related

DateTime Overflow in .NET

We have a Scala/Java back end that is generating the equivalent of DateTime.MaxValue in .NET.
I am sent the following date as a string "9999-12-31T23:59:59.999999999Z".
If I used DateTime.TryParse("9999-12-31T23:59:59.999999999Z", out var dateTime), then it throws an ArgumentOutOfRangeException (The added or subtracted value results in an un-representable DateTime.Parameter name: value).
I didn't expect this, since I was calling TryParse. Perhaps returning false would have been more intuitive?
If I reduce the year, I can see .NET is rolling the date over to the following day, which obviously wont work on a max date/time!
DateTime.TryParse("9998-12-31T23:59:59.999999999Z", out var dateTime);
dateTime.ToString().Dump();
Outputs: 01/01/9999 00:00:00
If I reduce the precision of the ms by 2, then it works:
DateTime.TryParse("9998-12-31T23:59:59.9999999Z", out var dateTime);
dateTime.ToString().Dump();
Outputs: 31/12/9998 23:59:59
This really looks like a bug in .NET? Is this expected behaviour?
Passing Min/Max/Infinity and etc. values between different platforms is a bad idea. Each platform might have its own representation of special values (not only dates). Therefore the only valid option is to pass epoch values (milliseconds options are preferable in most cases), since they are known to the both parties.
If the above is impossible for some reason then you have two ugly options:
Replace special values in your Scala/Java output with your own "encoding". For example as "MaxValue", or take your pick as your see fit. On the .Net side you will detect special values and translate them accordingly.
Insert some simple preprocessing into your .Net code. For example check
"9999-12-31T23:59:59.999999999".StartsWith("9999")
for max values.
You have too many nines in your string. The exception you observing is the precision issue.
Try doing the following:
DateTime.MaxValue.ToString("o")
it will result in "9999-12-31T23:59:59.9999999" rather than "9999-12-31T23:59:59.999999999Z", i.e. two less nines at the end.
Use "9999-12-31T23:59:59.9999999Z" as an input and it will parse successfully into DateTime.MaxValue.
PS: TryParse will also convert this value to your local timezone, which I assume is not somewhat you would anticipate. Use extended version instead:
DateTime.TryParse("9999-12-31T23:59:59.9999999Z", CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out var dateTime);
Adding DateTimeStyles.AdjustToUniversal as argument fix it for .NET Framework
e.g. for Windows Powershell:
$d = [DateTime]::Parse("9999-12-31T23:59:59.9999999Z", $null, 16)
Get-Date $d

Round DateTime using SQL DATETIME rules

Say there is some code that compares a DateTime object with a DateTime object that has been saved and returned from SQL Server, stored in a DATETIME object.
The comparison is done on hh:mm:ss-equality, but it is possible that SQL Server 'changes' the second component when saved which makes the comparisons fail ~1.5/1000 of the time or so.
This is because SQL Server will round/truncate this value when saved in the database as a DATETIME value:
datetime values are rounded to increments of .000, .003, or .007 seconds..
Is there a (standard) C#/.NET function that does the same rounding?
The primary goal of this question is to normalize the value prior to saving, for use in comparisons. That is, F(original) == F(saved) should always be true.
The final goal overall is to ensure the values are saved 'within the correct second', such that hh:mm:01.999 is stored as hh:mm:01.997. This would allow the hh:mm:ss-equality comparisons to be reliable regardless of if done to the original DateTime values or restored values. In this case, original.Second == F(original).Second should always be true as well.
For better or worse, one widely-used assumption is the comparison is done per hh:mm:ss, so a simple epsilon-compare of 2 milliseconds is out; although I wouldn't be opposed to a strongly-argued for comparison function that might also solve the final goal.
The SqlDateTime structure stores date/time values in the same way as SQL Server's datetime type. It provides conversions from and to the .NET DateTime type, and rounds when the conversion is performed.
Note that you have contradictory requirements in your question. You say you want the same rounding as SQL Server. You also say you want the rounding to never change the "second" component. You can't have it both ways. If you need the "second" component to not change, you may need to implement that yourself. You can check after the conversion has been done whether the second changed, and then restore it, or you may implement the conversion yourself to always round down.

Why is DateTime.Now.Subtract(DateTime.UtcNow) not (close to) zero?

If I try:
DateTime.Now.Subtract(DateTime.UtcNow)
I would expect the result to be very close to zero. But it's not, instead it's the time zone difference, in my case, -4 hours. There is a .Kind -- the DateTime KNOWS the timezones are different. Why doesn't it track this for me? Is there a flavor of Subtract that DOES use Kind correctly?
(For reference, a good rundown of what each one outputs can be seen at: https://stackoverflow.com/a/3229429/237091)
Eh? The Kind property does not alter date math. It is only used by time zone methods.
You get exactly the result I would expect you to get. Not sure I understand why you were expecting zero.
There is a .Kind -- the DateTime KNOWS the timezones are different. Why doesn't it track this for me?
Because DateTime is fundamentally broken (and there's more...). IMO it should complain if you try to subtract a value of one kind from another. But no, it just uses the uninterpreted date/time in each value. Very few operations actually take any notice of the Kind, unfortunately. (If you use TimeZoneInfo, those operations do take notice of it.)
Kind was hacked into .NET 2.0; before then a DateTime value didn't even know what kind it was - if you used:
dt = dt.ToLocalTime().ToLocalTime().ToLocalTime();
it would apply the same offset change several times. The BCL team found a couple of spare bits in the binary representation, and used it for Kind.
Basically, I feel your pain. Personally I would prefer it if operations like this threw an exception - subtracting a UTC DateTime from a local DateTime or vice versa makes little sense, IMO.
As an entirely biased plug, you could use Noda Time which separates the ideas of Instant, LocalDate, LocalTime, LocalDateTime, OffsetDateTime and ZonedDateTime, and doesn't let you perform non-sensical arithmetic. Our aim is to provide a saner API than the BCL one. That doesn't necessarily mean we've succeeded, of course :)
Each DateTime object represents a local time (as opposed to a UTC time plus a time zone offset). Even if the Kind property equals UTC, it's just storing the local time at the zero time zone. If it were not a local time, there would be no reason for the UtcNow property.
DateTime does not even store the timezone. If Kind equals UTC, then at least you know it's timezone is zero, but if Kind is local or unspecified, there is no way of knowing the timezone (the Kind property equals Unspecified by default).
Therefore, the Subtract method cannot incorporate the timezone into its calculation because the timezone is unknown.

A type for Date only in C# - why is there no Date type?

In our C# project we have the need for representing a date without a time.
I know of the existence of the DateTime, however, it incorporates a time of day as well.
I want to make explicit that certain variables and method-arguments are date-based.
Hence I can't use the DateTime.Date property
What are the standard approaches to this problem?
Why is there no Date class in C#?
Does anyone have a nice implementation using a struct and maybe some extensionmethods on DateTime and maybe implementing some operators such as == and <, > ?
Allow me to add an update to this classic question:
DateOnly (and TimeOnly) types have been added to .NET 6, starting with Preview 4. See my other answer here.
Jon Skeet's Noda Time library is now quite mature, and has a date-only type called LocalDate. (Local in this case just means local to someone, not necessarily local to the computer where the code is running.)
I've studied this problem significantly, so I'll also share several reasons for the necessity of these types:
There is a logical discrepancy between a date-only, and a date-at-midnight value.
Not every local day has a midnight in every time zone. Example: Brazil's spring-forward daylight saving time transition moves the clock from 11:59:59 to 01:00:00.
A date-time always refers to a specific time within the day, while a date-only may refer to the beginning of the day, the end of the day, or the entire range of the day.
Attaching a time to a date can lead to the date changing as the value is passed from one environment to another, if time zones are not watched very carefully. This commonly occurs in JavaScript (whose Date object is really a date+time), but can easily happen in .NET also, or in the serialization as data is passed between JavaScript and .NET.
Serializing a DateTime with XML or JSON (and others) will always include the time, even if it's not important. This is very confusing, especially considering things like birth dates and anniversaries, where the time is irrelevant.
Architecturally, DateTime is a DDD value-object, but it violates the Single Responsibly Principle in several ways:
It is designed as a date+time type, but often is used as date-only (ignoring the time), or time-of-day-only (ignoring the date). (TimeSpan is also often used for time-of-day, but that's another topic.)
The DateTimeKind value attached to the .Kind property splits the single type into three, The Unspecified kind is really the original intent of the structure, and should be used that way. The Utc kind aligns the value specifically with UTC, and the Local kind aligns the value with the environment's local time zone.
The problem with having a separate flag for kind is that every time you consume a DateTime, you are supposed to check .Kind to decide what behavior to take. The framework methods all do this, but others often forget. This is truly a SRP violation, as the type now has two different reasons to change (the value, and the kind).
The two of these lead to API usages that compile, but are often nonsensical, or have strange edge cases caused by side effects. Consider:
// nonsensical, caused by mixing types
DateTime dt = DateTime.Today - TimeSpan.FromHours(3); // when on today??
// strange edge cases, caused by impact of Kind
var london = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
var paris = TimeZoneInfo.FindSystemTimeZoneById("Romance Standard Time");
var dt = new DateTime(2016, 3, 27, 2, 0, 0); // unspecified kind
var delta = paris.GetUtcOffset(dt) - london.GetUtcOffset(dt); // side effect!
Console.WriteLine(delta.TotalHours); // 0, when should be 1 !!!
In summary, while a DateTime can be used for a date-only, it should only do so when when every place that uses it is very careful to ignore the time, and is also very careful not to try to convert to and from UTC or other time zones.
I suspect there is no dedicate pure Date class because you already have DateTime which can handle it. Having Date would lead to duplication and confusion.
If you want the standard approach look at the DateTime.Date property which gives just the date portion of a DateTime with the time value set to 12:00:00 midnight (00:00:00).
I've emailed refsrcfeedback#microsoft.com and that's their answer
Marcos, this is not a good place to ask questions like these. Try http://stackoverflow.com
Short answer is that you need a model to represent a point in time, and DateTime does that, it’s the most useful scenario in practice. The fact that humans use two concepts (date and time) to mark points in time is arbitrary and not useful to separate.
Only decouple where it is warranted, don’t do things just for the sake of doing things blindly. Think of it this way: what problem do you have that is solved by splitting DateTime into Date and Time? And what problems will you get that you don’t have now? Hint: if you look at DateTime usages across the .NET framework: http://referencesource.microsoft.com/#mscorlib/system/datetime.cs#df6b1eba7461813b#references
You will see that most are being returned from a method. If we didn’t have a single concept like DateTime, you would have to use out parameters or Tuples to return a pair of Date and Time.
HTH,
Kirill Osenkov
In my email I'd questioned if it was because DateTime uses TimeZoneInfo to get the time of the machine - in Now propriety. So I'd say it's because "the business rules" are "too coupled", they confimed that to me.
I created a simple Date struct for times when you need a simple date without worrying about time portion, timezones, local vs. utc, etc.
https://github.com/claycephus/csharp-date
System.DateOnly and System.TimeOnly types were recently added to .NET 6, and are available in the daily builds.
They were included with the .NET 6 Preview 4 release.
See https://github.com/dotnet/runtime/issues/49036
They are in the .NET source code here:
https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/DateOnly.cs
https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/TimeOnly.cs
I've blogged about them here.
If you need to run date comparisons then use
yourdatetime.Date;
If you are displaying to the screen use
yourdatetime.ToShortDateString();
Allow me to speculate: Maybe it is because until SQL Server 2008 there was no Date datatype in SQL so it would be hard so store it in SQL server?? And it is after all a Microsoft Product?
Who knows why it's that way. There are lots of bad design decisions in the .NET framework. However, I think this is a pretty minor one. You can always ignore the time part, so even if some code does decide to have a DateTime refer to more than just the date, the code that cares should only ever look at the date part. Alternatively, you could create a new type that represents just a date and use functions in DateTime to do the heavy lifting (calculations).
Why? We can only speculate and it doesn't do much to help solve engineering problems. A good guess is that DateTime contains all the functionality that such a struct would have.
If it really matters to you, just wrap DateTime in your own immutable struct that only exposes the date (or look at the DateTime.Date property).
In addition to Robert's answer you also have the DateTime.ToShortDateString method. Also, if you really wanted a Date object you could always use the Adapter pattern and wrap the DateTime object exposing only what you want (i.e. month, day, year).
There is always the DateTime.Date property which cuts off the time part of the DateTime. Maybe you can encapsulate or wrap DateTime in your own Date type.
And for the question why, well, I guess you'll have to ask Anders Heljsberg.
Yeah, also System.DateTime is sealed. I've seen some folks play games with this by creating a custom class just to get the string value of the time as mentioned by earlier posts, stuff like:
class CustomDate
{
public DateTime Date { get; set; }
public bool IsTimeOnly { get; private set; }
public CustomDate(bool isTimeOnly)
{
this.IsTimeOnly = isTimeOnly;
}
public string GetValue()
{
if (IsTimeOnly)
{
return Date.ToShortTimeString();
}
else
{
return Date.ToString();
}
}
}
This is maybe unnecessary, since you could easily just extract GetShortTimeString from a plain old DateTime type without a new class
Because in order to know the date, you have to know the system time (in ticks), which includes the time - so why throw away that information?
DateTime has a Date property if you don't care at all about the time.
If you use the Date or Today properties to get only the date portion from the DateTime object.
DateTime today = DateTime.Today;
DateTime yesterday = DateTime.Now.AddDays(-1).Date;
Then you will get the date component only with the time component set to midnight.

Handling and storing elapsed time

I'm having problems deciding on what is the best way is to handle and store time measurements.
I have an app that has a textbox that allows the users to input time in either hh:mm:ss or mm:ss format.
So I was planning on parsing this string, tokenizing it on the colons and creating TimeSpan (or using TimeSpan.Parse() and just adding a "00:" to the mm:ss case) for my business logic. Ok?
How do I store this as in a database though? What would the field type be? DateTime seems wrong. I don't want a time of 00:54:12 to be stored as 1901-01-01 00:54:12 that seems a bit poor?
TimeSpan has an Int64 Ticks property that you can store instead, and a constructor that takes a Ticks value.
I think the simplest is to just convert user input into a integer number of seconds. So 54:12 == 3252 seconds, so store the 3252 in your database or wherever. Then when you need to display it to the user, you can convert it back again.
For periods less than a day, just use seconds as other have said.
For longer periods, it depends on your db engine. If SQL Server, prior to version 2008 you want a datetime. It's okay- you can just ignore the default 1/1/1900 date they'll all have. If you are fortunate enough to have sql server 2008, then there are separate Date and Time datatypes you can use. The advantage with using a real datetime/time type is the use of the DateDiff function for comparing durations.
Most databases have some sort of time interval type. The answer depends on which database you're talking about. For Oracle, it's just a floating point NUMBER that represents the number of days (including fractional days). You can add/subtract that to/from any DATE type and you get the right answer.
As an integer count of seconds (or Milliseconds as appropriate)
Are you collecting both the start time and stop time? If so, you could use the "timestamp" data type, if your DBMS supports that. If not, just as a date/time type. Now, you've said you don't want the date part to be stored - but consider the case where the time period spans midnight - you start at 23:55:01 and end at 00:05:14, for example - unless you also have the date in there. There are standard build in functions to return the elapsed time (in seconds) between two date-time values.
Go with integers for seconds or minutes. Seconds is probably better. you'll never kick yourself for choosing something with too much precision. Also, for your UI, consider using multiple text inputs you don't have to worry about the user actually typing in the ":" properly. It's also much easier to add other constraints such as the minute and second values conting containing 0-59.
and int type should do it, storing it as seconds and parsing it back and forth
http://msdn.microsoft.com/en-us/library/ms187745.aspx

Categories