multiple date range and check if it is sequential. c# MVC - c#

We have this project and one of the business requirement is it allows the client to input a multiple date range and check the individual dates if it is sequential/continuous or not to the others.
eq.
INPUT
startdate - enddate
10/24/2016 - 10/24/2016
10/26/2016 - 10/28/2016
OUTPUT
10/24/2016 - 10/24/2016 - NOT SEQUENTIAL
10/26/2016 - 10/26/2016 - SEQUENTIAL
10/27/2016 - 10/27/2016 - SEQUENTIAL
10/28/2016 - 10/28/2016 - SEQUENTIAL
For now I am playing around this solution
Check if date range is sequential in c#?
but i hope we i can find a better solution on how to properly do it.
Thank you and have a good day!

If by "sequential" we mean that the second date is the day after the first date then we can do the following:
private bool CheckSequential(DateTime date1, DateTime date2)
{
// strips off time portion
var d1 = date1.Date;
var d2 = date2.Date;
// add 1 to first date
d1 = d1.AddDays(1);
// compare them
if(DateTime.Compare(d1, d2) == 0)
return true;
else
return false;
}

Related

Number of days for a given year inside a date range

Say I have an year, 2017.
I then have a date range, 01/07/2017 - 01-07-2018 OR 01/07/2017 - 01-01-2017 OR 01/01/2016 - 01/01/2018 ( <- this should return 365 days)
I now need to calculate how many total days are there in the given range for the given year.
Note that dates are stored as dd/mm/yyyy with an always 00:00:00 time.
What would the best logic be considering all possible cases of ranges?
You can compute the start and end dates for a year easily:
var start2017 = new DateTime(2017,1,1);
var end2017 = new DateTime(2017,12,31);
And then you can compute the overlap between this new range and your other range1:
var startOverlap = start2017 < startOtherRange ? startOtherRange : start2017;
var endOverlap = end2017 > endOtherRange ? endOtherRange : end2017;
var totalDays = (endOverlap - startOverlap).TotalDays + 1;
The above is correct if ranges are meant to include both their start and end dates. If you want, say, an exclusive endpoint then we'd adjust the end of out 2017 computed range one day further forwards and would no longer require the +1 adjustment at the end)
(And I presume you can derive from there how to turn it into a function if required that takes year, startRange, endRange parameters and does the above with some appropriate renaming)
1I had some vague recollection of DateTime.Min(value1, value2) and similarly for Max but it's definitely not in the BCL that I can see. Those would replace the conditional operators on the following lines. Once C# has "extension everything" these functions could be written as static extensions to DateTime.

Calculate the annual use of a service starting from the date of signing

I need to calculate the annual use of a service starting from the date of signing. Something like:
select Count(*) from TABLENAME where Date >= MYDATE
MYDATE need to be calculate from a subscription date and I need to get the last year date from subscription referring to the current date
Some examples:
subscription date: 2007-06-29
if current date is : 2015-04-29 then date is: 2014-06-29
if current date is : 2015-06-29 then date is: 2015-06-29
if current date is : 2015-06-29 then date is: 2015-06-29
I'm using c# to calculate the date but it crashes in leapyear:
var date = new DateTime(DateTime.Now.Year, subscriptionDate.Month, subscriptionDate.Day);
if (DateTime.Now.Date < date)
{
date = date.AddYears(-1);
}
I was wondering if there were a clever/better way to do it in c# or mysql also handling leapyear
---UPDATE----
Running example with suggested solutions
Well, I'd do it in Noda Time, myself:
LocalDate subscriptionDate = ...;
LocalDate today = ...; // Need to take time zone into account
int years = Period.Between(subscriptionDate, today);
return subscription.PlusYears(years);
With .NET that would be slightly harder, but I'd still go for the approach of adding years (and letting it do the truncation for Feb 29th):
// Only call this *once* - otherwise you could get inconsistent results
DateTime today = DateTime.Today;
int years = today.Year - subscriptionDate.Year;
DateTime candidate = subscriptionDate.AddYears(years);
// We might have overshot, in which case lower the number of years.
return candidate <= today ? candidate : subscriptionDate.AddYears(years - 1);
Thanks to Yuri Dorokhov answer and Jon Skeet suggestion
I found a solution that works well and handle leap year:
int year = DateTime.Now.DayOfYear >= subscriptionDate.DayOfYear ?
DateTime.Now.Year : DateTime.Now.Year - 1;
var date = new DateTime(year, 1, 1).AddDays(subscriptionDate.DayOfYear - 1);
--------UPDATE------
I leave here this answer as reference but it does not handle well leap year so don't use it
Use mysql DATE_SUB function
DATE_SUB(Date, INTERVAL 1 YEAR)

Wanted: DateTime.TryNew(year, month, day) or DateTime.IsValidDate(year, month, day)

The title basically says it all. I'm getting three user-supplied integers (year, month, day)1 from a legacy database (which I cannot change). Currently, I use the following code to parse those integers into a DateTime structure:
try {
return new DateTime(year, month, day);
} catch (ArgumentException ex) {
return DateTime.MinValue;
}
Sometimes, the values don't represent a valid date (yes, users enter stuff like 1999-06-31, and no, the legacy app did not verify this). Since throwing an exception when data validation fails is considered bad practice, I'd prefer to replace this with exception-less code. However, the only solution I could find was to convert the integers into one string and TryParseExact this string, which seems even uglier to me. Did I miss some obvious better solution?
1 Actually, it's one integer in the format YYYYMMDD, but converting that to year, month and day is trivial...
There is not a static function IsValidDate() so you have to write it by yourself, first naive implementation may be:
public static bool IsValidDate(int year, int month, int day)
{
if (year < DateTime.MinValue.Year || year > DateTime.MaxValue.Year)
return false;
if (month < 1 || month > 12)
return false;
return day > 0 && day <= DateTime.DaysInMonth(year, month);
}
I said this is a naive implementation because (besides arguments range) the only check to see if a date exists is for leap year. In practice this may fail because of calendar issues if you're working with non Gregorian calendars (and missing days even in Gregorian calendar that has been used to align date from Julian calendar).
Working With Calendars
These assumptions may be broken for non Gregorian calendars:
1 January 01 is smallest valid date. It's not true. Different calendars have a different smallest date. This limit is just DateTime technical limit but there may be a calendar (or an Era within a calendar) with a different minimum (and maximum) date.
Number of months in one year is less or equal than 12. It's not true, in some calendars upper bound is 13 and it's not always the same for every year.
If a date is valid (according all other rules) then it's a valid date. It's not true, a calendar may have more than one era and not all dates are valid (possibly even within era date range).
Rules to manage this are pretty complex and it's too easy to forget something so, in this case, catching an exception may not be such bad idea. A better version of previous validation function may just provide basic validation and relying on DateTime to check other rules:
public static DateTime? TryNew(int year,
int month,
int day,
Calendar calendar)
{
if (calendar == null)
calendar = new GregorianCalendar();
if (year < calendar.MinSupportedDateTime.Year)
return null;
if (year > calendar.MaxSupportedDateTime.Year)
return null;
// Note that even with this check we can't assert this is a valid
// month because one year may be "shared" for two eras moreover here
// we're assuming current era.
if (month < 1 || month > calendar.GetMonthsInYear(year))
return null;
if (day <= 0 || day > DateTime.DaysInMonth(year, month))
return null;
// Now, probably, date is valid but there may still be issues
// about era and missing days because of calendar changes.
// For all this checks we rely on DateTime implementation.
try
{
return new DateTime(year, month, day, calendar);
}
catch (ArgumentOutOfRangeException)
{
return null;
}
}
Then, given this new function, your original code should be:
return TryNew(year, month, day) ?? DateTime.MinValue;
You can use DateTime.DaysInMonth to check if date is valid. Obviously month has to be in range (1;12)

Date range falling between two dates in a LINQ query

I'm trying to write a select query which returns records where the input date range falls between two date fields in a LINQ query.
My inputs are:
date1 - start date
date2 - end date
My database fields are
AppointmentStart
AppointmentEnd
Additionally, I'd also like to ensure that an input of 14:00 - 15:00 doesn't return a value for 15:00-16:00.
return (from t1 in db.Appointments where (t1.AppointmentStart <= date2 && (t1.AppointmentEnd) >= date1)
If anybody can assist me with this, I'd appreciate it.
I'm not 100% clear on your requirements. In your opening line you asked for records "where the input date range falls between two date fields", but in the "Additionally" line you imply that you don't want to return records where the start date of the appointment doesn't equal the end date of your input. I take these to be two different requirements, so I'll give you two different queries.
The first query is:
from t1 in db.Appointments
where date1 >= t1.AppointmentStart
where date2 <= t1.AppointmentEnd
select t1;
The second query is:
from t1 in db.Appointments
where date2 > t1.AppointmentStart
where date1 < t1.AppointmentEnd
select t1;
The first query returns records that "contain" the input dates.
The second query returns records that "overlap" the input dates.
I think it makes more sense that you want the overlap query and this one will meet your "14:00 - 15:00 doesn't return a value for 15:00-16:00" requirement.
Let me know if I made a mistake understanding your requirements and need to make any changes.
It looks backwards to me.
if the following is true:
Date1 = start
Date2 = end
then i would think startdate after or equal to appointmentstart and enddate before or equal to appointmentend or:
return (from t1 in db.Appointments where (date1 >= t1.AppointmentStart && date2 <= t1.AppointmentEnd))
i also changed the parens because they didn't make sense to me (seemed like one was missing)

DataTable filter expression with DateTime and less-or-equal operator problem

I have the following code:
DataTable t = new DataTable();
t.Locale = CultureInfo.InvariantCulture;
t.Columns.Add("Date", typeof(DateTime));
DateTime today = DateTime.Now;
DateTime yesterday = today.AddDays(-1);
DateTime tomorow = today.AddDays(1);
t.Rows.Add(yesterday);
t.Rows.Add(today);
t.Rows.Add(tomorow);
string filter = string.Format(CultureInfo.InvariantCulture,
"Date >= #{0}# AND Date <= #{1}#", yesterday, tomorow);
t.DefaultView.RowFilter = filter;
foreach (DataRowView v in t.DefaultView)
Console.WriteLine(v["date"]);
I'm expecting that the filtered t.DefaultView now contains all three "days". But for some reason the last date from the range isn't included. It seems <= operator for DateTime type works like a <.
Where is the problem? Is that a bug? Any suggestions how to overcome that?
Update.
Got some responses about DateTime type and comparison operators. Thanks.
But now I want to direct attention to filter expression.
Ok, say I have the folloving loop:
foreach (DataRow r in t.Rows)
{
DateTime date = (DateTime)r["Date"];
if (yesterday <= date && date <= tomorow)
Console.WriteLine(date);
}
This loop should show the same result like
foreach (DataRowView v in t.DefaultView)
Console.WriteLine(v["date"]);
from the previous example, yes? No! Here <= works as I'm expecting and the result is all three days. Why?
Update #2: solution.
As Joe has noted - the problem is about fractions of a second.
If I format upper and lower bounds with Round-trip date/time pattern (to preserve fractions of a second) - everything works just fine:
string filter = string.Format(CultureInfo.InvariantCulture,
"Date >= '{0}' AND Date <= '{1}'",
yesterday.ToString("o", CultureInfo.InvariantCulture),
tomorow.ToString("o", CultureInfo.InvariantCulture));
The date comparison takes the time into account. So, for instance, "today at midday" is greater than just "today". If you use DateTime.Now, the time is included. So, if DateTime.Now is "today at midday", then tomorrow = today.AddDays(1) is less than "tomorrow at 3PM"... So you need to ignore the time part of the date. You can do that by formatting the date without the time. Also, if you want to check that a date is "less or equal than tomorrow" (regardless of the time), check that it is "strictly less than the day after tomorrow" :
string filter = string.Format(CultureInfo.InvariantCulture,
"Date >= #{0:MM/dd/yyyy}# AND Date < #{1:MM/dd/yyyy}#",
yesterday,
tomorrow.AddDays(1));
The code you posted in your update is not equivalent to the row filter.
Your row filter formats the date using the general format for the current culture. This probably does not include fractions of a second - therefore unless you happen to call DateTime.Now on a second boundary, your tomorrow value will be some fractions of a second beyond the range specified by the filter.
I.e. if your tomorrow value is '2009-12-23 01:02:03.456', your row filter is only taking values up to and including '2009-12-23 01:02:03', a few fractions of a second before the value specified by tomorrow.
If you only want to compare dates, you should use DateTime.Date to truncate the time component from your dates (and use DateTime.Today rather than DateTime.Now for the current date).
Try with
DateTime today = DateTime.Today;
if does not solve, check whether your date field contains time also. there lies your problem.
Update: your second comment.
when you compare with DateTime.Now e.g. Date <= 21.12.2009 14:35:35, it will take all before 14:35 hours and will ignore later rows. Hope this helps you.
See following article to get more idea
http://dotnetguts.blogspot.com/2007/06/understanding-datetime-and-timespan-in.html

Categories

Resources