This question already has answers here:
Unit Testing: DateTime.Now
(22 answers)
Closed last year.
I have this method which is dependent on current date. It checks if today is Sun, Mon, Tue or Wed, then it gives 5 days of lead time for arrival of shipped items. If its Thur, Fri or Sat then it gives 6 days of lead time to account for the weekend.
private DateTime GetEstimatedArrivalDate()
{
DateTime estimatedDate;
if (DateTime.Now.DayOfWeek >= DayOfWeek.Thursday)
{
estimatedDate = DateTime.Now.Date.AddDays(6);
}
else
{
estimatedDate = DateTime.Now.Date.AddDays(5);
}
return estimatedDate;
}
The actual estimation logic is more complex. I have simplified it for the purpose of this question. My question is how do I write a unit test for something like this which depends on todays date?
You need to pass the current date in as a parameter:
private DateTime GetEstimatedArrivalDate(DateTime currentDate)
{
DateTime estimatedDate;
if (currentDate.DayOfWeek >= DayOfWeek.Thursday)
{
estimatedDate = currentDate.AddDays(6);
}
else
{
estimatedDate = currentDate.AddDays(5);
}
return estimatedDate;
}
In real code you call it like this:
DateTime estimatedDate = GetEstimatedArrivalDate(DateTime.Now.Date);
Then you can test it as follows:
DateTime actual = GetEstimatedArrivalDate(new DateTime(2010, 2, 10));
DateTime expected = ...;
// etc...
Note that this also fixes a potential bug in your program where the date changes between consecutive calls to DateTime.Now.
Generally speaking, you'd want to abstract the method of obtaining the current date and time behind an interface, eg:
public interface IDateTimeProvider
{
DateTime Now { get; }
}
The real service would be:
public class DateTimeProvider: IDateTimeProvider
{
public DateTime Now
{
get
{
return DateTime.Now;
}
}
}
And a test service would be:
public class TestDateTimeProvider: IDateTimeProvider
{
private DateTime timeToProvide;
public TestDateTimeProvider(DateTime timeToProvide)
{
this.timeToProvide = timeToProvide;
}
public DateTime Now
{
get
{
return timeToProvide;
}
}
}
For services that require the current time, have them take an IDateTimeProvider as a dependency. For the real thing, pass a new DateTimeProvider(); when you're a component, pass in a new TestDateTimeProvider(timeToTestFor).
Make your class take an IClock parameter (via constructor or property)
interface IClock
{
DateTime Now { get; }
}
You can then use a fake implementation for testing
class FakeClock : IClock
{
DateTime Now { get; set }
}
and a real implementation the rest of the time.
class SystemClock : IClock
{
DateTime Now { get { return DateTime.Now; } }
}
I would suggest doing this as Mark suggests, but with the addition of a overloaded call for production use that takes no parameter and uses DateTime.Now
private DateTime GetEstimatedArrivalDate()
{
return GetEstimatedArrivalDate(DateTime.Now);
}
private DateTime GetEstimatedArrivalDate(DateTime currentDate)
{
DateTime estimatedDate;
if (currentDate.DayOfWeek >= DayOfWeek.Thursday)
{
estimatedDate = currentDate.AddDays(6);
}
else
{
estimatedDate = currentDate.AddDays(5);
}
return estimatedDate;
}
One "common" way of doing so is to "fake" the current system date (that can be done in several ways) and then test your code on "known" dates.
Another interesting way is to change your implementation slightly to:
private DateTime GetEstimatedArrivalDate()
{
return GetEstimatedArrivalDate(DateTime.Now);
}
private DateTime GetEstimatedArrivalDate(DateTime forDate)
{
DateTime estimatedDate;
if (forDate.DayOfWeek >= DayOfWeek.Thursday)
{
estimatedDate = forDate.Date.AddDays(6);
}
else
{
estimatedDate = forDate.Date.AddDays(5);
}
return estimatedDate;
}
And then use the method with a parameter to test on "immediate" dates.
Seems like there are a limited enough number of cases that you could test them each explicitly. The method depends on today's date, but the output depends only on the day of week, and every date has a day of week.
You could pass in a delegate that returns DateTime.Now during normal execution, and then in your test pass in another delegate that returns a fixed date, and assert your result based on that.
I'll give the controversial answer, don't test it.
The logic is trivial and it has zero dependencies, i believe in good code coverage but not when it increases complexity for no real gain.
Related
I have to create a class that includes 3 methods.
1st - It shows current date
2nd - current date + 7days
3rd - current date - 7days.
I had completely dealing with dates in C # so i created sth that :
public class Date
{
private DateTime date = DateTime.Now;
public DateTime Now()
{
return date;
}
public DateTime AktuPlusOne ()
{
DateTime date = DateTime.Now.AddDays(7);
return date;
}
public DateTime AktuMinusOne()
{
DateTime date = DateTime.Now.AddDays(-7);
return date;
}
}
Is it ok or not ? It works fine, but I care about good habits .
No need to create additional method as framework already provides this functionality out of box DateTime.Now.AddDays(numberOfDays). However, if you really want to create a generic method for your requirement, just create one instead of three methods.
public DateTime AddDaysToToday(int days)
{
return DateTime.Now.AddDays(days);
}
DateTime today = AddDaysToToday(0);
DateTime todayPlusSeven = AddDaysToToday(7);
DateTime todayMinusSeven = AddDaysToToday(-7);
How do I Write C# code that will allow to compile the following code :
var date = 8.September(2013); // Generates a DateTime for the 8th of September 2013
You can use an extension method:
public static class MyExtensions
{
public static DateTime September(this int day, int year)
{
return new DateTime(year, 9, day);
}
}
However, this is generally bad practice, and I'd recommend against this kind of thing, especially for something as trivial as this—is new DateTime(2013, 9, 8) really so much more difficult than 8.September(2013)? There may be times where this kind of trick can be useful or fun for practice, but it should be used sparingly.
I would recommend against this, as it strikes me as very poor style. That said, if you really want to do this statically, you would need to define twelve different extension methods (one for each month name) like so:
public static class DateConstructionExtensions
{
public static DateTime January(this int day, int year)
{
return new DateTime(year, /* month: */1, day);
}
// equivalent methods for February, March, etc...
}
You could do this via extensions:
public static DateTime September(this int day, int year) {
return new DateTime(year, 9, day);
}
Of course, you'd need 12 such extensions, one for each month.
I think you can have the implementation like this:
public partial interface IMonth {
int Number {
get;
}
}
public partial class February: IMonth {
public int Number {
get {
return 2;
}
}
}
public static partial class Extensions {
public static DateTime OfMonth<T>(this int day, int year)
where T: IMonth, new() {
var month=new T();
var daysInMonth=DateTime.DaysInMonth(year, month.Number);
if(1>day||day>daysInMonth)
throw new ArgumentException();
return new DateTime(year, month.Number, day);
}
}
For the reason I declare months as classes, is because months might have different names in different cultures. You might want to provide different aliases for them.
Then, for the reason there's IMonth, is a contract that months must implement it. The extension method has the constraint new() is for avoiding IMonth itself or an abstract class be used.
This implementation also checks for the valid day number.
And you can assign the variable date as:
var date=(28).OfMonth<February>(2013);
Make sense?
I'm using Dapper to map my entities to SQL Server CE. If I save a DateTime with Kind=Utc, when I read it back I get a DateTime with Kind=Unspecified, which leads to all kind of problems.
Example:
var f = new Foo { Id = 42, ModificationDate = DateTime.UtcNow };
Console.WriteLine("{0} ({1})", f.ModificationDate, f.ModificationDate.Kind);
connection.Execute("insert into Foo(Id, ModificationDate) values(#Id, #ModificationDate)", f);
var f2 = connection.Query<Foo>("select * from Foo where Id = #Id", f).Single();
Console.WriteLine("{0} ({1})", f2.ModificationDate, f2.ModificationDate.Kind);
This code gives the following output:
20/09/2012 10:04:16 (Utc)
20/09/2012 10:04:16 (Unspecified)
I know I should be using a DateTimeOffset, but unfortunately SQL CE has no support for this type.
Is there a workaround? Can I tell Dapper to assume that all dates have DateTimeKind.Utc? And more generally, what are my options to customize the mapping?
EDIT: My current workaround is to patch the dates after Dapper has materialized the result, but it kind of smells...
var results = _connection.Query<Foo>(sql, param).Select(PatchDate);
...
static Foo PatchDate(Foo f)
{
if (f.ModificationDate.Kind == DateTimeKind.Unspecified)
f.ModificationDate = DateTime.SpecifyKind(f.ModificationDate, DateTimeKind.Utc);
return f;
}
Adding this answer for anyone else who comes looking for a simple fix. This is possible now with the addition of SqlMapper.TypeHandler in Dapper.
Add this class to convert the value from the db to a datetime with the kind specified as UTC.
public class DateTimeHandler : SqlMapper.TypeHandler<DateTime>
{
public override void SetValue(IDbDataParameter parameter, DateTime value)
{
parameter.Value = value;
}
public override DateTime Parse(object value)
{
return DateTime.SpecifyKind((DateTime)value, DateTimeKind.Utc);
}
}
Then in my Global.asax file of my Web API I add the type handler to dapper.
SqlMapper.AddTypeHandler(new DateTimeHandler());
If you need to ensure you are always inserting dates as UTC, then on the SetValue method you can use:
parameter.Value = DateTime.SpecifyKind(value, DateTimeKind.Utc);
Looked into the Dapper code. Unless mine was out of date, for value types like datetime (which is mapped to DbType.DateTime), dapper just does a simple cast from the IDataReader object.
Pseudo : yield return (DateTime)IDataReader.GetValue(0);
That's the specific case for Datetime out of a bunch of generic code and lambdas.
AFAIK, SQL datetime never stores the offset / timezone so the kind will always say "Unspecified" on any datetime you store and fetch.
So, to do it cleanly, you could touch dapper internals:
which is a pain as you'd have to touch a big IL generating method (the DataRow Deserializer) and put in an if case for DateTime.
OR
just put a setter on the DateTime props where UTC is an issue (which is kinda against POCO but is relatively sane):
class Foo
{
private DateTime _modificationDate;
public DateTime ModificationDate
{
get { return _modificationDate; }
set { _modificationDate = DateTime.SpecifyKind(value, DateTimeKind.Utc); }
}
//Ifs optional? since it's always going to be a UTC date, and any DB call will return unspecified anyways
}
Just wanted to put my full solution here for seamlessly integrating DateTimeOffset / DateTimeOffset? fields/properties with a MySQL 5.7 database (which doesn't support DbType.DateTimeOffset) - based on #matt-jenkins answer above:
public static class DapperExtensions
{
class DateTimeOffsetTypeHandler : SqlMapper.TypeHandler<DateTimeOffset>
{
public override void SetValue(IDbDataParameter parameter, DateTimeOffset value)
{
switch (parameter.DbType)
{
case DbType.DateTime:
case DbType.DateTime2:
case DbType.AnsiString: // Seems to be some MySQL type mapping here
parameter.Value = value.UtcDateTime;
break;
case DbType.DateTimeOffset:
parameter.Value = value;
break;
default:
throw new InvalidOperationException("DateTimeOffset must be assigned to a DbType.DateTime SQL field.");
}
}
public override DateTimeOffset Parse(object value)
{
switch (value)
{
case DateTime time:
return new DateTimeOffset(DateTime.SpecifyKind(time, DateTimeKind.Utc), TimeSpan.Zero);
case DateTimeOffset dto:
return dto;
default:
throw new InvalidOperationException("Must be DateTime or DateTimeOffset object to be mapped.");
}
}
}
private static int DateTimeOffsetMapperInstalled = 0;
public static void InstallDateTimeOffsetMapper()
{
// Assumes SqlMapper.ResetTypeHandlers() is never called.
if (Interlocked.CompareExchange(ref DateTimeOffsetMapperInstalled, 1, 0) == 0)
{
// First remove the default type map between typeof(DateTimeOffset) => DbType.DateTimeOffset (not valid for MySQL)
SqlMapper.RemoveTypeMap(typeof(DateTimeOffset));
SqlMapper.RemoveTypeMap(typeof(DateTimeOffset?));
// This handles nullable value types automatically e.g. DateTimeOffset?
SqlMapper.AddTypeHandler(typeof(DateTimeOffset), new DateTimeOffsetTypeHandler());
}
}
}
If you are using Dapper from source (not nuget), you could tweak the code to always force DateTimeKind of UTC. A more configurable option might be to create a new attribute for DateTime property values that allow you to specify date time kind as a hint to dapper. Dapper could look for DateTime properties with this attribute and when found could use it to specify the DateTime kind during ORM mapping. This might be a nice feature for core dapper as you are not the only one with this issue :)
What is the best way to compare all properties of two objects where some of them have different formats (e.g. DateTime in one and DateTime.ToString() with custom format in other)?
I was able to do that by using 2 assertions:
o1.ShouldHave().AllPropertiesBut(dto1 => dto1.Date).EqualTo(o2);
o1.Date.Should().Be(DateTime.Parse(o2.Date));
I would think about the following, but that does not compile because EqualTo<T>() is void.
o1.ShouldHave().AllProperties().But(d => d.Date).EqualTo(o2)
.And.Date.Should().Be((DateTime.Parse(o2.Date));
types are:
public class Dto1
{
public int ID { get { return 1; } }
public DateTime Date { get { return DateTime.Now.Date; } }
}
public class Dto2
{
public int ID { get { return 1; } }
public string Date { get { return DateTime.Now.Date.ToShortDateString(); } }
}
var o1 = new Dto1();
var o2 = new Dto2();
The first example is typically the best way. However, if you would switch o1 and o2, it might work in a single call. Fluent Assertions will try to convert (using Convert.ChangeType) the actual value of a property to the expected value of the property with the same name. In your particular example, it would try to convert the DateTime in Dto1 to a string in Dto2 before comparing the values. But since the string representation of a DateTime is dependent on the culture of the thread, it would not give you predictable results. However, if you would switch o1 and o2, I wouldn't be surprised if Convert.ChangeType would succesfully convert your short datetime back to a DateTIme object.
As a side-note, my DTOs usually just pass the DateTime to the caller without any string conversion. I believe that the actual representation of the DateTime is purely a UI responsibility.
HTH
Dennis
I have a system that takes information from an external source and then stores it to be displayed later.
One of the data items is a date. On the source system they have the concept of a fuzzy date i.e. not accurate to a specific day or sometimes not to a month as well. So I get dates in the format:
dd/mm/yyyy
mm/yyyy
yyyy
I can parse these to DateTime objects and work with these but when rendering later I need to be able to determine the accuracy of the date since parsing "2010" will result in a date of "01/01/2010". I want to show just the year so need to know it's original accuracy.
I've mocked up a quick class to deal with this:
public class FuzzyDate
{
public DateTime Date { get; set; }
public DateType Type { get; set; }
}
public enum DateType
{
DayMonthYear,
MonthYear,
Year
}
This will do the job for me and I can do something on the parse to handle it but I feel like this is probably quite a common problem and there is probably an existing cleaner solution.
Is there something built into .Net to do this? I had a look at the culture stuff but that didn't quite seem right.
Any help would be appreciated.
To answer your question: There is nothing built into .NET to handle this gracefully.
Your solution is as valid as any I've seen. You will probably wish to embellish your class with overrides to the ToString() method that will render your date appropriately based on the DateType.
Here are a couple other threads that attempt to address this question:
Strategy for Incomplete Dates
Implementing a "Partial Date" object
Good luck!
If your data type will always handle specific periods of time (i.e. the year 1972 is a specific period of time, but the 4th of July is not specific), you can store your data as a start time and time span.
If your date was "1972", the start date would be 19720101 and the time span would be 366 days.
If your date was "07/1972", the start date would be 19720701 and the time span would be 31 days.
If your date was "04/07/1972", the start date would be 19720704 and the time span would be 1 day.
Here's a possible implementation:
public struct VagueDate
{
DateTime start, end;
public DateTime Start { get { return start; } }
public DateTime End { get { return end; } }
public TimeSpan Span { get { return end - start; } }
public VagueDate(string Date)
{
if (DateTime.TryParseExact(Date, "yyyy", null, 0, out start))
end = start.AddYears(1);
else if (DateTime.TryParseExact(Date, "MM/yyyy", null, 0, out start))
end = start.AddMonths(1);
else if (DateTime.TryParseExact(Date, "dd/MM/yyyy", null, 0, out start))
end = start.AddDays(1);
else
throw new ArgumentException("Invalid format", "Date");
}
public override string ToString()
{
return Start.ToString("dd/MM/yyyy") + " plus " + Span.TotalDays + " days";
}
}
As I started to read your problem, I rapidly came to the conclusion that the answer was to implement your own FuzzyDate class. Lo and behold, that's exactly what you've done.
I can imagine that you might want to add functionality to this over time (such as comparisons that take into account the DateType).
I don't believe there's anything that will inherently help you in the .NET Framework, so I think you're doing the right thing.
I think you're going down the right route. There is no concept of a 'fuzzy' date or partial date, you will need to build your own.
You will likely need more constructor methods, for example
public FuzzyDate(int year)
{
Date = new DateTime(year,1,1); // 1 Jan yy
Type = DateType.Year;
}
public FuzzyDate(int year, int month)
{
Date = new DateTime(year, month, 1); // 1 mm yy
Type = DateType.MonthYear;
}
public FuzzyDate(int year, int month, int day)
{
Date = new DateTime(year, month, day); // dd mm yy
Type = DateType.DayMonthYear;
}
Hope this helps,
Kevin
It seems to me that your approach is right. Its true that .NET DateTime support multiple formats but I guess that given that all of them are supported with a concept of steps (nanoseconds), then will be related to specific date AND time.
One thing I would do differently is use null-able values (or use -1 for null semantics) for month and day to indicate what data was collected. Then I would have a factory method that would take a DateType param and return a DateTime. This method would throw and exception if only the year was available and the client code tried to create a DateType.DayMonthYear.
public class FuzzyDate
{
int _year;
int? _month;
int? _day;
public DateTime Date { get; set; }
public DateType Type { get; set; }
public DateTime GetDateTime(DateType dateType) { // ...
}
public enum DateType
{
DayMonthYear,
MonthYear,
Year
}
This might seem a bit over the top but the approach would explicitly store the original data and only represent "faked" DateTime objects when requested. If you were to persist a DateTime object internally along with a DateType enum you would lose some resolution.
As far as I am aware there is nothing built into .NET for this, the solution I'd go for is one based upon nullable values, something like this.
public class FuzzyDate
{
private int Year;
private int? Month;
private int? Day;
public FuzzyDate(int Year, int? Month, int? Day)
{
this.Year = Year;
this.Month = Month;
this.Day = Day;
}
public DateType DateType
{
get
{
if(Day.HasValue && Month.HasValue)
{
return DateType.DayMonthYear;
}
else if(Month.HasValue)
{
return DateType.MonthYear;
}
else
{
return DateType.Year;
}
}
}
public DateTime Date
{
get
{
return new DateTime(Year, Month.GetValueOrDefault(1), Day.GetValueOrDefault(1));
}
}
}
public enum DateType
{
DayMonthYear,
MonthYear,
Year
}
You could create your own structure (user-defined type) based on the datetime that would allow 00 for month, and 00 for day... And then also implement icomparable, so you can do math/comparrisons on it.
http://msdn.microsoft.com/en-us/library/k69kzbs1%28v=vs.71%29.aspx
http://msdn.microsoft.com/en-us/library/system.icomparable.aspx