Calculating past datetime in C# - c#

I am working on an algorithm in C# to calculate a past DateTime based on an input string with the following characteristics:
The string contains an integer followed by either 'D', 'M' or 'Y', such as "1D" or "90M".
The output will be DateTime.Now minus the corresponding number of days, months or years.
The issue I am having is that if, for instance, I switch the input string on a Regex (D, M or Y) and subtract the corresponding TimeSpan from DateTime.Now, the new TimeSpan() constructor does not accept months or years, only days.
if (new Regex(#"[0-9]+D").IsMatch(value))
{
newDate = DateTime.Now - TimeSpan(Int32.Parse(value.Replace("D", "")), 0, 0);
}
This logic is fine if the input string is in days, but the constructor for TimeSpan does not accept months or years, and it would be incredibly inaccurate if I assumed each month had 30 days, or each year had 365 days.
Does anyone have thoughts on how to implement this algorithm?
Thanks!

DateTime has AddMonths, AddDays and AddYears methods. Use them with minus to substract

Could you not rather try using the AddDays/AddMonths/AddYears but with negative numbers?
From DateTime.AddDays Method
The value parameter can be negative or
positive.
And then maybe just implement a switch stament to apply the appropriate Add method.

To subtract months, I create a new DateTime and just evaluate month/year. So 1/2010 - 6 months would be 6/2010... once you have the month/year established, you can look at the original datetime day component, and ensure it fits within the month.
That's what I did. Year was evaluated the same way. Subtracting days is easy; use the TimeSpan component to do it.

Remember that you can add negative amounts as well and check out this method and this one.

http://msdn.microsoft.com/en-us/library/3z48198e.aspx
TimeSpan.TryParse accepts very close to your string as long as you can fits its formatting OR convert from yours to its.

Related

How to show DateTime objects and their differences in a logical way?

I'm working on a C# application, where I'm doing some things and I want to display both the start, intermediate and end timestamps, and now I would like to add their time differences.
I figured it would be easy:
Console.WriteLine($"Start time: {DT_Start.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
Console.WriteLine($"Intermediate time: {DT_Intermediate.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
Console.WriteLine($"End time: {DT_End.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
This is working great. So I thought it would be equally easy to show the differences, so I started with:
Console.WriteLine($"Elapsed times: [{(DT_Intermediate - DT_Start).ToString("HH:mm:ss.fff")}] " +
$"and [{(DT_End - DT_Intermediate).ToString("HH:mm:ss.fff")}]");
I had dropped the year, month and day because everything is done in the same day. This did not work, so I decided to add those entries, but it still does not work:
Console.WriteLine($"Elapsed times: [{(DT_Intermediate - DT_Start).ToString("yyyy-MM-dd HH:mm:ss.fff")}] " +
$"and [{(DT_End - DT_Intermediate).ToString("yyyy-MM-dd HH:mm:ss.fff")}]");
So, in C#, you can show datetime objects and you can subtract them. The results, when debugging, are very similar but if you try to show that information in the same way, you get the error message System.FormatException: 'Input string was not in the correct format.'.
Is there a format I can use for both DateTime and TimeSpan objects? (I've seen that the difference between two DateTime objects would be a TimeSpan object)
As you have discovered yourself, the difference between two DateTime objects is a TimeSpan which just represents the difference of time that has passed. Since a TimeSpan is not linked to a calendar date, you cannot format it using calendar specific things like months and years.
However, your initial approach of only showing hours, minutes and seconds does work just fine. However, you will need to escape the colon and dot when wanting to use it in a TimeSpan format string. And also, the HH for the hours in the DateTime is written as lower-case hh for TimeSpan:
Console.WriteLine((DT_Intermediate - DT_Start).ToString("hh\\:mm\\:ss\\.fff"));
// ^^^^ ^^ ^^
// lower-case hh and escaped characters
So in your example, this should work:
Console.WriteLine($"Elapsed times: [{(DT_Intermediate - DT_Start).ToString("hh\\:mm\\:ss\\.fff")}] " +
$"and [{(DT_End - DT_Intermediate).ToString("hh\\:mm\\:ss\\.fff")}]");
Note that the TimeSpan also supports days as part of the difference, so if the number of hours in your difference surpasses 24, you will be missing this difference until you also include the number of days using the format specifier d in your result.
You can read more about formatting TimeSpan in the documentation about custom format strings.
You can use the DateTime.Subtract Method, it accepts DateTime and TimeSpan Object input.
https://learn.microsoft.com/de-de/dotnet/api/system.datetime.subtract?view=net-7.0

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

Excel functions to C# code

So I'm trying to convert this Excel cell calculation into C#, but I can't really make sense of what it's doing. I read over the EDATE definition and it still doesn't make too much sense.
IF(EDATE(B25,-12)>A25,((EDATE(B25,-12)-A25)/(EDATE(B25,-12)-EDATE(EDATE(B25,-12),-12)))+1,(B25-A25)/(B25-EDATE(B25,-12)))
B25 = End Date
A25 = Start Date
It's essentially trying to calculation a fraction of a year. Should be very easy to do, but I'm not entirely sure what this EDATE thing does.
According to EDATE, you're mostly dealing with 12 months before the End Date (B25).
Given that, this seems to say:
If the start date is more than 12 months before the end date then:
(The amount of time that the start date is prior to the year before the end date divided by one year) + 1
Else:
The amount of time that the start date is prior to the end date divided by one year.
I really don't know how Excel handles date arithmetic or what the point of this function is, but that's my pseudo at a glance.
Really, it's just checking the if condition I mentioned, then offsetting the entire arithmetic by a year if the condition is true.
Edit
Okay, some quick research shows that Excel does date arithmetic purely as days, so then 12/1/1900 - 1/1/1900 = 335 days. Putting a time on either date makes it a fraction of a day.
Given that, this Excel formula appears to attempt to calculate the fractional year difference between the two dates.
This is a rough piece of code that should provide it:
TimeSpan span = endDate.Subtract(startDate);
double years = span.Days / 365.25;

Getting number of months based on days in 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;
}

How can I convert VC++'s DATE Type To C#'s DateTime?

I got a binary file, and one of record field stored some date infomation.
The save program is written by VC++ and using the DATE type.
How can read it into C#'s DateTime type ?
BTW: I tried DateTime.FromBinary, but it looks wrong.
Try DateTime.FromOADate.
As noted in Vinay's answer, the VC++ DATE is really a double. Just read in a double and then convert based on the description.
Here is some sample code for the conversion:
double CDate = 8.50; //The DATE from C++
DateTime newDate = new DateTime(1899, 12, 30); //Start at 12/30/1899
int days = (int)Math.Floor(CDate); //Get the days (integer part)
double hours = (CDate-days) * 24; //Get the hours (fraction part)
newDate = newDate.AddDays(days).AddHours(hours);
EDIT
Or use DateTime.FromOADate as suggested by Joe.
The beauty of SO, you get to learn new things.
If it's an OLE DATE, use the information available from here:
The DATE type is implemented using an
8-byte floating-point number. Days are
represented by whole number increments
starting with 30 December 1899,
midnight as time zero. Time values are
expressed as fractional part. Time
precision is 1 second. This type is
also used to represent date-only (the
fractional part is 0) or time-only
(the whole number part is 0) values.
Using this, you can convert to a CLR DateTime.

Categories

Resources