Getting number of months based on days in c# - c#

I'm stuck trying to figure this one out..
We currently have a date criteria on our reports, that are limited by days, configurable of course, currently set to 90 days.. message says, it is limited by 90 days, however my boss wants to increase it to 13 months, unfortunately if I did that, I'd need to do it by days and it would say, 395 days..
Not a very friendly message..
Trying to figure out a way to satisfy this, my other only option is to add another settings that is limited by months as well as days. but then i still need to convert the months back to days which wont be perfect since not every month has same days..
Ideas?

You need to decide if you're going to use 13 months as the time interval, or some number of days that approximates to 13 months. If you use 13 months, then the number of days (or the end date for your report) is going to vary depending on the start date.
I would suggest making your report configurable for either months or days (storing not just the number, but the units in configuration). You can then display on the report whatever has been specified in the configuration (with the units from configuration, too) and calculate the end date for the query by adding the configured number of configured units to the start date.
If you try to do everything in days, when you're now working in months, you'll just make life difficult for yourself.
It's much easier to add 13 months to the start date to get the end date, than it is to try and (inaccurately) work out how many months in a given number of days.

Use the TimeSpan object to perform the calculations you need for your date criteria.

I would do something like this, given the number of days:
int myDays; // 390 or whatever
DateTime d1 = DateTime.Now;
DateTime d2 = d1.AddDays(myDays);
int monthsDiff = d2.Month - d1.Month + 12 * (d2.Year - d1.Year);
DateTime d3 = d1.AddMonths(monthsDiff);
TimeSpan tf = d2 - d3;
string msg = monthsDiff.ToString() + " months, " + tf.Days + " days";

TimeSpan give you duration between two DateTime objects. It can give it consistently in Days, Hours or Mins; Number of months would be different based upon actual start & end dates as different months have different number of actual days.
Having said that, you can always write a Utility method that gives you YourTimeSpan object that gives you number of Months etc based upon your calendar and StartDate / EndDates.
In your case you can make it even simpler by storing it separately in configuration, for example - ReportDuration_Years, ReportDuration_Months, ReportDuration_Days. This would allow you to create meaningful lable on your report as well as allow to identify StartDate and EndDate properly.
//Call this by passing values from configuration
private string GetNiceLookingLable(int? years, int? months, int? days){
var yearMessage = (years.HasValue)?String.Format("{0} Years", years):String.Empty;
var monthMessage = (months.HasValue)?String.Format("{0} Months", months):String.Empty;
var daysMessage = (days.HasValue)?String.Format("{0} Days", days):String.Empty;
// You probably want to concatenate them properly
return String.Format("{0} {1} {2}",yearMessage, monthMessage, daysMessage);
}
-
//Call this to get starting date
private DateTime getStartingDate(int? years, int? months,int? days){
var retDate = DateTime.Today;
if(years.HasValue){
retDate = retDate.AddYears(-1*years.Value);
}
if(months.HasValue){
retDate = retDate.AddMonths(-1*months.Value);
}
if(days.HasValue){
retDate = retDate.AddDays(-1*days.Value);
}
return retDate;
}

Related

Is it possible to get the data from a range in an easy way in C#?

I have a range of 2 dates given by my business: "we would like to have all data from July and 6 previous months". I create two dates:
_range1.MaxDateTime = new DateTime(today. Year, 7, 31);
_range1.MinDateTime = rangeLastDate.AddMonths(-6).AddDays(1);
Because the system will always perform the calculation on last 6 months. Last date is also always end of month. I have no problem to get the last day of the month. C# has the feature EndOfMonth() for this. But when I do so I always get the last day at midnight. when I create the DateTime() myself I also get a date a midnight.
In my code I use Entity Framework with LinQ. (old EF, not Core yet)
_range1_ShippedQuantities = DbContext.Job_Dtl
.Where(j=> j.LastShipDate >= _range1.MinDateTime && j.LastShipDate <= _range1.MaxDateTime)
This is not correct because my last datetime is the 31 at midnight and not the first of next month at midnight. I should do
_range1_ShippedQuantities = DbContext.Job_Dtl
.Where(j=> j.LastShipDate.Date >= _range1.MinDateTime.Date && j.LastShipDate.Date <= _range1.MaxDateTime.Date)
Or I can also manipulate the dates do force the time at 23:59:59... what I don't like. I still miss 1 second. I can add 999 milliseconds but i will still tell you I miss one millisecond. I can also add one day in a temp variable and use this temp variable in my LinQ query... yes but I have the feeling there is something better. How do you deal with these ranges?
But LinQ does not allow this. LinQ for entities cannot recognize the date.
As several people have commented on your question, the simplest and most common way to solve this problem is to choose an exclusive end date rather than an inclusive one.
var minDateInclusive = rangeLastDate.AddMonths(-6).AddDays(1);
var maxDateExclusive = rangeLastDate.AddDays(1);
DbContext.Job_Dtl
.Where(j=> j.LastShipDate >= minDateInclusive
&& j.LastShipDate < maxDateExclusive);

C# Adding years and days in a single DateTime variable

Am I missing something simple?
I am trying to calculate a date 17 years and 364 days before the given date.
Is there a way to do this without converting everything into days? I am trying to avoid dealing with leap years. I am doing the following:
DateTime date = Convert.ToDateTime(tId2);
string tId4a = Convert.ToString(tId4);
var age1 = tId4a.Substring(0, 2);
int age2 = Convert.ToInt32(age1) - 1;
DateTime sub1 = date.AddYears(-age2);
I was hoping to do something simple like:
DateTime sub1 = date.AddYears(-age2) + date.AddDays(-364);
I am being told that I cannot use the '+' in the DateTime.
Sorry, but I am new to this. The reason the age2 variable is used is because at times that value will change. But, the 364 should be consistent. I am creating something to test a date boundary.
Did I overlook something simple?
Thanks.
What you do is you add the "date age2 years ago" to the "date 364 days ago".
Instead do this:
DateTime sub1 = date.AddYears(-age2).AddDays(-364)
This at first subtracts the years and then subtracts the days from the resulting value.
You can't add dates, but you can certainly chain method calls together
date.AddYears(-age2).AddDays(-364);
This is for all intents and purposes the same thing as trying to add them together.
It really sounds like you want to go with tid4 years ago, but go to the next day after that.
The way you are doing it, is that you subtract 1 from that to get age2. Then you subtract that many years, and you also subtract 364 days from your date. This will be more sensitive to leap years. If the resulting date happens to be between Jan 1 and Feb 28 of a leap year, you will end up with one day later than you wanted.
364 is a very suspect number. I tend to think you are using that to mean "the number of days in a year minus one". But the number of days in a year is not always 365. In leap years, the number of days is 366. In such years, subtracting 364 is not 1 day less than a year. It is actually 2 days less than a year, so you would be off.
What you really should do, if I am reading you correct, is to just subtract the number of years, then add one day back in.
DateTime date = Convert.ToDateTime(tId2);
string tId4a = Convert.ToString(tId4);
int age = Convert.ToInt32(tId4a.Substring(0, 2))
DateTime sub1 = date.AddYears(-age).AddDays(1);
I think that it is valuable to mention that DateTime is an object, and that .AddYears(), .AddDays(), etc all return a new DateTime object which is why you cannot add them together like primitive types. So when you run:
DateTime sub1 = date.AddYears(-age2).AddDays(-364);
date.AddYears(-age2) returns a new object, and then .AddDays(-364) is using the new DateTime object and not the date instance.
For more info:
https://msdn.microsoft.com/en-us/library/system.datetime(v=vs.110).aspx

Combining two Datetime values

I'm pretty new to coding so sorry if my question has an obvious answer but I didn't find anything like that via the search.
I'm currently trying to make a time tracker database by using Windows Forms in C#.
My oroblem is the GUI has two DateTimePicker: one displays the hours (dateTimePicker1) which you can choose, and the other one displays the minutes (dateTimePicker2) you can choose.
Both of them are starting with the current time when the form is loading.
I'm now trying to combine those two so that I get a string which gives me both hours and minutes with the chosen time.
If someone could help me here I'd be really glad because I'm currently out of ideas....
Do you mean like this?
// take hour from first datetimepicker
DateTime hour = dateTimePicker1.Value.Hour;
// take minute from second datetimepicker
DateTime minute = dateTimePicker2.Value.Minute;
// combine hour and minute into an object
DateTime hourAndMinute = hour + minute;
// convert the combined DateTime into string
hourAndMinute.ToString("hh:mm");
If you want to convert it to string with different format you can check it here http://www.dotnetperls.com/datetime-format
If you actually need to represent length of time you should use this one http://www.dotnetperls.com/timespan
Mark as an answer if it help you :)
Are you looking for this?
// myDatePicker displays date
DateTime day = myDatePicker.Value.Date;
// myTimePicker displays time
var timeOffset = myTimePicker.Value.TimeOfDay;
// combined value (day and time)
DateTime result = day + timeOffset;
In case you have three DatePickers (date, hour and minutes):
// myDatePicker displays date
DateTime day = myDatePicker.Value.Date;
DateTime result = day
.AddHours(myHourPicker.Value.Hour)
.AddMinutes(myMinutePicker.Value.Minute);
To display in required format use formatting:
// Hours and minutes
String text = result.ToString("hh:mm");

How can I determine the last day of the month [duplicate]

This question already has answers here:
Last day of the month in .NET
(5 answers)
Closed 7 years ago.
How can I determine the last day of the month, for the tested month, when the user enters a date into textbox that is further from the available last day for that month? Also how do I feed that back to the user.
When the user enters the date, for example, of April 31, 2015, the date should automatically change to April 30, 2015.
I would like to try doing this using c#
3rd EDIT: The below will get you close to what you're asking for but really just make use of a datepicker control it will help prevent the majority of invalid dates and save you a good amount of development time
2nd EDIT: So TryParse is beneficial because if the date is valid, continue and your done. If you have an invalid date, which TryParse will tell you, then you can compare the value that was input for the day value and see how close it is to the end of the month, i.e. take 31 - 30 = 1, 1 number off so they probably meant 30
EDIT: To answer your question about finding an invalid date check this out: Validate a DateTime in C#
It shows how to detect an invalid date.
To answer your question about how to "guess" which validate date they are closest to I'd say it depends on how you have your date entered. If you do something like have them enter an int value for the day value I'd check to see what the highest day value is for the selected month and then see which one they are closest to (this is just a math operation at this point).
I think this will work
DateTime lastday = new DateTime(DateTime.Today.Year, DateTime.Today.Month, DateTime.DaysInMonth(DateTime.Today.Year, DateTime.Today.Month));
This is an easy way:
//first day of next month
var dt = new DateTime(2015,4,1);
var lastDayOfMonth = dt.AddDays(-1).Day;
As far as delivering a message back to the user, it depends on the environment, technology and preferred approach.

Adding two DateTime objects together

Is there any better way to add one DateTime object to another one, than this:
DateTime first = new DateTime(2000, 1, 1);
DateTime second = new DateTime(11, 2, 5, 10, 10, 11);
DateTime result = first.AddYears(second.Year);
DateTime result = first.AddMonths(second.Month);
...
and so on...
In this example I'd like to get DateTime(2011, 3, 6, 10, 10, 11)
EDIT
After a intensive brainstorm it seems to there's no different way, but to facilitate it can be boxed inside additional class and operator+ just like in JonSkeet's answer
It doesn't make sense to add two DateTime values together. If you want to represent "11 years, 2 months, 5 days, 10 hours, 10 minutes and 11 seconds" then you should represent that. That's not the same as 0011-02-05T10:10:11. In particular, you'd never be able to add "2 months and 30 days" for example. Likewise you'd never be able to add just a single year, because you can't have 0 for month and day values within a date.
Now there's no BCL type to represent the idea of "11 years [...]" but you could create your own one reasonably easily. As an alternative, you could use my Noda Time project which has Period for precisely this purpose:
var localDateTime = new LocalDate(2000, 1, 10).AtMidnight();
var period = new PeriodBuilder {
Years = 11, Months = 2, Days = 5,
Hours = 10, Minutes = 10, Seconds = 11
}.Build();
var result = localDateTime + period;
Contrary to some other answers provided here, you cannot use TimeSpan for this purpose. TimeSpan doesn't have any concept of months and years, because they vary in length, whereas a TimeSpan represents a fixed number of ticks. (If your largest unit is days, then you're fine to use TimeSpan, but given your example, I assume you need months and years.)
If you don't want to use Noda Time, I'd recommend you fake up a Period-like class yourself. It's easy enough to do - for example:
// Untested and quickly hacked up. Lots more API you'd probably
// want, string conversions, properties etc.
public sealed class Period
{
private readonly int years, months, days, hours, minutes, seconds;
public Period(int years, int months, int days,
int hours, int minutes, int seconds)
{
this.years = years;
this.months = months;
this.days = days;
this.hours = hours;
this.minutes = minutes;
this.seconds = seconds;
}
public static DateTime operator+(DateTime lhs, Period rhs)
{
// Note: order of operations is important here.
// Consider January 1st + (1 month and 30 days)...
// what do you want the result to be?
return lhs.AddYears(rhs.years)
.AddMonths(rhs.months)
.AddDays(rhs.days)
.AddHours(rhs.hours)
.AddMinutes(rhs.minutes)
.AddSeconds(rhs.seconds);
}
}
Usage:
DateTime first = new DateTime(2000, 1, 1);
Period second = new Period(11, 2, 5, 10, 10, 11);
DateTime result = first + second;
You need to be aware of how DateTime.Add will handle impossible situations - for example adding a month to January 31st will give you February 28th/29th depending on whether or not it's a leap year.
The simple approach I've listed here, going through intermediate values, has its downsides, because that truncation can happen twice (adding years and then adding months) when it needn't - for example, "February 29th + 1 year + 1 month" might logically be "March 29th" but it will actually end up as "March 28th" as the truncation to February 28th will happen before the month is added.
Trying to work out a "right" way of doing calendrical arithmetic is fiendishly difficult, particularly as in some cases people may disagree about what the "right" answer is. In the above code I've opted for simplicity and predictability - depending on your real requirements, you may need something more complex.
DateTime first = new DateTime(2000, 1, 1);
DateTime second = new DateTime(11, 2, 5, 10, 10, 11);
DateTime result = new DateTime(first.Ticks + second.Ticks);
You have a DateTime which represents a point in time. And you want to add a number of years/months/days/hours/minutes/seconds to it.
A change in DataTime is not a point, it is a vector (a difference between points). It is really easy to mistake one for the other, as they often have similar structure. However, that kind of type error leads to lots of pain.
Avoiding it doesn't fix your pain, but it makes it manageable.
Adding two DateTime together is adding two points together. Sort of like adding the location of Los Angeles to New York.
Now, adding the "vector" of LA to NY to London makes sense -- because the travel vector is a vector, not a point. And point+vector is just a point.
So this means you need to create a time vector type. A simple time span is an option, but probably not appropriate: because you care about months, years and days, not nanoseconds or absolute time durations.
I'll dub the name of the vector a CalendarVector, as it represents movement on a Calendar, not in time itself.
An easy first pass is to create a tuple of each sub type of time -- years, months, days, etc -- then add them in some arbitrary order to your original DateTime with an overloaded operator+.
You should support:
DateTime = DateTime + CalendarVector
CalendarVector = CalendarVector + CalendarVector
CalendarVector = CalendarVector - CalendarVector
CalendarVector = int * CalendarVector
CalendarVector = - CalendarVector
DateTime = DateTime - CalendarVector
CalendarVector = DateTime - DateTime
ideally. The CalendarVector + DateTime overload is optional, but probably not needed.
However, this only gets you half way.
The big remaining problem is that CalendarVector addition does not commute. Adding 1 month to a DateTime, then adding 1 day, is different than adding 1 day then adding 1 month.
And this is fundamental.
There is the problem of "what does it mean to be 1 month after January 31st", which can be answered, but any reasonable answer to that question doesn't solve the commuting problem.
Your planned constructor -- where you feed it the number of years, months, days, hours, minutes seconds -- is thus ambiguous in what it means.
So a robust solution should not have that constructor.
A solution is to create Years, Months, Days, Hours, Minutes and Seconds types that you explicitly add together. The order they are added together is the order they are applied to the DateTime you add it to. Commuting and "simplification" is avoided until the final application on a DateTime -- so +1 year, +2 days, -1 month, -1 year, -2 days, +1 month is not the zero transformation.
There is a related problem with DateTime-DateTime -- it should return a CalendarVector v such that lhs = rhs + v, but there are multiple such vectors. The same problem can occur with spherical coordinates -- do you mean the short way around the Earth, or the long way? It doesn't matter in some contexts -- but then you halve the result to find the mid-way point. Plus, you get discontinuities as you approach "far side of the world".
So my advice would be to maintain a list of transformations on a DateTime object. 1 year is a transformation that consists of adding 1 to the year field, and then repairing the other fields so they are consistent. These transformations support negation. Addition is applying them one at a time, from left to right. Negation may also reverse the order of application, and adjacent transformations "of the same kind" may combine (so +1 month -1 month becomes the identity transformation, instead of a clamping operation based off next months' end of month), or not (so x = x+1 month,then x = x-1 month on the next line is the same as x = x + 1 month - 1 month).
Yet another approach is to insist that the user provide a policy for what to do in these exceptional circumstances (which happen ... all the time), because this problem is thorny enough that a library that "solves" the problem can at best highlight the problems and force the client programmer to think about them and make decisions.
Simplest is:
firstDateTime.AddTicks(secondDateTime.Ticks);

Categories

Resources