Convert time duration from String to TimeSpan - c#

I have a string that contains day of the week, time and duration. E.g. Monday,10:00 AM,45m
The duration could be in either of the following formats:
45m
1h45m
1h
Now I need to convert this into a date with time for both the start of the event and end of the event based on the duration.
I managed to convert this piece "Monday,10:00 AM" into the upcoming date and time for whatever the day of the week is so now I have a datetime as let's say "05/30/2022 10:00:00 AM".
Now I need to create a datetime object for the end time of the event by adding e.g. "45m" to the previous datetime. I don't know the format of the duration piece but it will be one of three from the list above.
How do I convert this into a standard timespan to add to the previous time? Is the above format a standard format that perhaps has a built in way to parse? It's coming from an API.
I have tried this and it works but I'm not sure how to detect and handle the formats.
\\split the original string so now I have duration
\\when I have just the hour duraton e.g. 1h
t = TimeSpan.ParseExact(durationString, "h\\h", CultureInfo.InvariantCulture);
var finalDate = dt.Add(t);
\\when I have just the minute format e.g. 45m
t = TimeSpan.ParseExact(durationString, "m\\m", CultureInfo.InvariantCulture);

Use a ParseExact method overload that accepts an array of formats.
var values = new string[] { "45m", "1h45m", "1h" };
var formats = new string[] { #"m\m", #"h\hm\m", #"h\h" };
foreach (var value in values)
{
var ts = TimeSpan.ParseExact(value, formats, CultureInfo.InvariantCulture);
Console.WriteLine(ts);
}

You can use REGEX to check for matching patterns:
using System.Text.RegularExpressions;
Regex HourOnly = new Regex("^[0-9]+h$");
Regex MinuteOnly = new Regex("^[[0-9]+m");
Regex HourAndMinute = new Regex("^[0-9]+h[0-9]+m$");
List<string> conditions = new List<string>();
string Condition1 = "Monday,10:00 AM,45m";
string Condition2 = "Monday,10:00 AM,1h45m";
string Condition3 = "Monday,10:00 AM,1h";
conditions.Add(Condition1);
conditions.Add(Condition2);
conditions.Add(Condition3);
foreach(string condition in conditions)
{
if (HourOnly.IsMatch(condition.Split(',').Last()))
{
Console.WriteLine($"Hour only: {condition}");
}
else if (HourAndMinute.IsMatch(condition.Split(',').Last()))
{
Console.WriteLine($"Hour and minute: {condition}");
}
else if (MinuteOnly.IsMatch(condition.Split(',').Last()))
{
Console.WriteLine($"Minute only: {condition}");
}
}
Granted users can enter 99999h99999m but if you are reasonably sure that won't happen the above regex should suit you just fine.
Here is some additional documentation to aid you on your quest: https://learn.microsoft.com/en-us/dotnet/standard/base-types/regular-expression-language-quick-reference

Related

Date Time Format String only for Milliseconds

for a Project i parse timestamps from filenames and each has its own format. I get the files from a partner company, so the naming of the files is non negotiable. The problem now is, that besides one, every filename has a nice time format that can easily be transferred to a c# date time format string to be parsed into a DateTime object. The one that differs, strictly uses milliseconds since initialization as it's timestamp similar to
+4311123234_12345 M.txt where the numbers after the underscore are said milliseconds.
In this example the milliseconds would be 12345 but a filename could also be +4311123234_123423402345802345 M.txt where 123423402345802345 would be said milliseconds. To me only the milliseconds part is relevant and is extracted from the filename. I now want a string such as "yyyy" (for year typically) where this number is then parsed to a DateTime Object via the TryParseExact method. It is not important from when the milliseconds are counted, since it is just used for sorting so for all i care they can be the milliseconds elapsed since 01.01.1900 00:00:00.
I did a google search but came to the conclusion, that such a date time format string doesn't really exist. I know that there is ss.fffff but since i do not know how many digits the millisecond timestamp has, i would have to generate a seperate formatstring with the according number of 'f's. This is also no option, because the format string should be a user input.
Right now i coupled it to a special keyword, so that when the input is keyword the time will not be parsed by a timeformatstring but simply by the milliseconds that i got from parsing the section in the filename to a long.
It works fine, but it really isn't ideal in my opinion...
What i forgot to say was, that the string containing the timestamp is extracted from the filename via Regex CaptureGroups, so in the code example the timestampstring is in cc[0]
private static void CheckForValidMatch(SortedList<DateTime, string> files, string file, Match match,string dateTimeFormat,int groupNumber)
{
Group group = match.Groups[groupNumber];
CaptureCollection cc = group.Captures;
DateTime dateTime;
if (cc.Count == 0)
Debug.WriteLine("group did not capture anything");
else
{
if (dateTimeFormat.Equals("keyword"))
{
if (long.TryParse(cc[0].ToString(),out var result))
{
dateTime = new DateTime(result);
files.Add(dateTime, file);
}
return;
}
if (!DateTime.TryParseExact(cc[0].ToString(),dateTimeFormat,DateTimeFormatInfo.CurrentInfo,DateTimeStyles.AssumeLocal, out dateTime))
Debug.WriteLine("parsing the date time failed");
else
{
files.Add(dateTime, file);
}
}
}
Is there a way to make this more elegant, or does someone know a formatstring only for milliseconds?
Thanks in advance!
Don't expect to parse it directly as a datetime. Instead, keep a constant epoch DateTime value for the base date, parse the file name as a long, and then do this:
var FileDate = epoch.AddMilliseconds(parsedLongValue);
So we get this:
private static void CheckForValidMatch(SortedList<DateTime, string> files, string file, Match match,string dateTimeFormat,int groupNumber)
{
var cc = match.Groups[groupNumber].Captures;
if (cc.Count == 0)
{
Debug.WriteLine("group did not capture anything");
return;
}
DateTime dateTime;
if (dateTimeFormat == "keyword")
{
// I don't know your epoch date, but Jan 1, 1970 is common (it's the unix epoch)
const DateTime epoch = new DateTime(1970, 1, 1);
if (long.TryParse(cc[0].ToString(),out var result))
{
dateTime = epoch.AddMilliseconds(result);
}
else
{
Debug.WriteLine($"parsing the date time for {dateTimeormat} failed");
return;
}
}
else if (!DateTime.TryParseExact(cc[0].ToString(),dateTimeFormat,DateTimeFormatInfo.CurrentInfo,DateTimeStyles.AssumeLocal, out dateTime))
{
Debug.WriteLine("parsing the date time failed");
return;
}
files.Add(dateTime, file);
}

How to parse a timespan in order to add it to a datetime?

I've got a string in the following format: 05/06/2019|1330|60
The output I'm looking for is: 05/06/2019T14:30:00
I'm attempting to parse out the TimeSpan portion right now:
public static string getProcedureEndingDateTime (string input) {
//05/06/2019|1330|60
string myDate = input.Split ( '|' ) [0];
DateTime myDateTime = DateTime.Parse (myDate);
string myTime = input.Split('|')[1];
string hours = myTime.Substring(0,2);
string minutes = myTime.Substring(2,2);
TimeSpan myTimeSpan = TimeSpan.Parse($"{hours}:{minutes}");
myDateTime.Add(myTimeSpan);
return myDateTime.ToString();
}
But right now, getting the following output:
To get the above output I'm calling my function like so:
Console.WriteLine (getProcedureEndingDateTime("05/06/2019|1330|60"));
How do I parse the string "1330" into a TimeSpan?
No need to us a Timespan here, just call ParseExact instead with a proper format to do it in one line.
var myDateTime = DateTime.ParseExact("05/06/2019|1330|60", "dd/MM/yyyy|HHmm|60", CultureInfo.InvariantCulture);
Console.WriteLine(myDateTime.ToString());
//this gives 2019-06-05 1:30:00 PM, format depends on your PC's locale
I don't know what the 60 part is, you can adjust the format or substring it out beforehand.
The problem is because Add() returns a new DateTime instance, which means you're currently discarding it. Store it, and return that from your function instead, like so:
var adjusted = myDateTime.Add(myTimeSpan);
return adjusted.ToString();
Try using the numeric values as exactly that, numbers.
Also, the other issue with your code is the DateTime.Add() method doesn't add to that DateTime variable. Instead it returns a new variable, which you are ignoring.
Try this:
public static string getProcedureEndingDateTime (string input) {
string[] parts = input.Split('|');
string myDate = parts[0];
DateTime myDateTime = DateTime.Parse (myDate);
string myTime = parts[1];
if (!int.TryParse(myTime.Substring(0,2), out int hours))
hours = 0;
if (!int.TryParse(myTime.Substring(2,2), out int minutes))
minutes = 0;
TimeSpan myTimeSpan = new TimeSpan(hours, minutes, 0);
myDateTime += myTimeSpan;
return myDateTime.ToString();
}
Assuming the date shown is May 6th (and not June 5th), and also assuming the 60 represents a time zone offset expressed in minutes west of GMT, and also assuming you want the corresponding UTC value, then:
public static string getProcedureEndingDateTime (string input) {
// example input: "05/06/2019|1330|60"
// separate the offset from the rest of the string
string dateTimeString = input.Substring(0, 15);
string offsetString = input.Substring(16);
// parse the DateTime as given, and parse the offset separately, inverting the sign
DateTime dt = DateTime.ParseExact(dateTimeString, "MM/dd/yyyy|HHmm", CultureInfo.InvariantCulture);
TimeSpan offset = TimeSpan.FromMinutes(-int.Parse(offsetString));
// create a DateTimeOffset from these two components
DateTimeOffset dto = new DateTimeOffset(dt, offset);
// Convert to UTC and return a string in the desired format
DateTime utcDateTime = dto.UtcDateTime;
return utcDateTime.ToString("MM/dd/yyyy'T'HH:mm:ss", CultureInfo.InvariantCulture);
}
A few additional points:
Not only is the input format strange, but so is your desired output format. It is strange to see a T separating the date and time and also see the date in the 05/06/2019 format. T almost always means to use ISO 8601, which requires year-month-day ordering and hyphen separators. I'd suggest either dropping the T if you want a locale-specific format, or keep the T and use the standard format. Don't do both.
In ISO 8601, it's also a good idea to append a Z to UTC-based values. For DateTime values, the K specifier should be used for that. In other words, you probably want the last line above to be:
return utcDateTime.ToString("yyyy-MM-dd'T'HH:mm:ssK", CultureInfo.InvariantCulture);
// outputs: "2019-05-06T14:30:00Z"
You might want to not format a string here, but instead return the DateTime or DateTimeOffset value. It's usually better to create a string only at the time of display.
Don't forget that the DateTime struct is immutable. In your question you were ignoring the return value of the Add method.

How to get the desired output by modifying string after removing / from string

I have a string like this:
30/04/2018 o/p=300418
01/03/2017 o/p=010317
10/11/2018 o/p=101118
12/11/2123 o/p=121123
1/1/2018 o/p =010118
code tried but can't get the last one 1/1/2018
string a = "31/04/2018";
string b = a.Replace("/","");
b = b.Remove(4, 2);
You should parse to a DateTime and then use the ToString to go back to a string. The following works with your given input.
var dateStrings = new []{"30/04/2018", "01/03/2017","10/11/2018","12/11/2123","1/1/2018"};
foreach(var ds in dateStrings)
{
Console.WriteLine(DateTime.ParseExact(ds, "d/M/yyyy", System.Globalization.CultureInfo.InvariantCulture).ToString("ddMMyy"));
}
The only change I made is to the first date as that is not a valid date within that month (April has 30 days, not 31). If that is going to be a problem then you should change it to TryParse instead, currently I assumed your example was faulty and not your actual data.
Your structure varies, all of the examples above use two digit month and day, while the bottom only uses a single digit month and day. Your current code basically will replace the slash with an empty string, but when you remove index four to two your output would deviate.
The simplest approach would be:
var date = DateTime.Parse("...");
var filter = $"o/p = {date:MMddyyyy}";
Obviously you may have to validate and ensure accuracy of your date conversion, but I don't know how your applications works.
If you can reasonably expect that the passed in dates are actual dates (hint: there are only 30 days in April) you should make a function that parses the string into DateTimes, then uses string formats to get the output how you want:
public static string ToDateTimeFormat(string input)
{
DateTime output;
if(DateTime.TryParse(input, out output))
{
return output.ToString("MMddyy");
}
return input; //parse fails, return original input
}
My example will still take "bad" dates, but it will not throw an exception like some of the other answers given here (TryParse() vs Parse()).
There is obviously a small bit of overhead with parsing but its negligible compared to all the logic you would need to get the proper string manipulation.
Fiddle here
Parse the string as DateTime. Then run ToString with the format you desire.
var a = "1/1/2018";
var date = DateTime.Parse(a);
var result = date.ToString("ddMMyyyy");
You can use ParseExact to parse the input, then use ToString to format the output.
For example:
private static void Main()
{
var testData = new List<string>
{
"31/04/2018",
"01/03/2017",
"10/11/2018",
"12/11/2123",
"1/1/2018",
};
foreach (var data in testData)
{
Console.WriteLine(DateTime.ParseExact(data, "d/m/yyyy", null).ToString("ddmmyy"));
}
GetKeyFromUser("\nDone! Press any key to exit...");
}
Output
You didn't specify whether these are DateTime values or just strings that look like date time values. I'll assume these are DateTime values.
Convert the string to a DateTime. Then use a string formatter. It's important to specify the culture. In this case dd/mm/yyyy is common in the UK.
var culture = new CultureInfo("en-GB");//UK uses the datetime format dd/MM/yyyy
var dates = new List<string>{"30/04/2018", "01/03/2017","10/11/2018","12/11/2123","1/1/2018"};
foreach (var date in dates)
{
//TODO: Do something with these values
DateTime.Parse(date, culture).ToString("ddMMyyyy");
}
Otherwise, running DateTime.Parse on a machine with a different culture could result in a FormatException. Parsing dates and times in .NET.

How to subtract DateTime field and Duration?

I have one field in database in this format: 2013-06-18 17:00:00.000
and second field Duration in this format: 3000 (this represents seconds, so it is 50 minutes)
I need to subtract those two fields and to set in another field result which will be: 2013-06-18 16:10:00.000
One addition is that they both can be retrieved from database in string format only. So they are both strings.
Thanks
First you need to Parse the datetime. Then subtract using AddSeconds:
var date = DateTime.Parse("2013-06-18 17:00:00.000");
var newDate = date.AddSeconds(int.Parse("-3000"));
You can use newDate.ToString() to get the date as a string.
You can find the documentation for DateTime here.
Update: Changed seconds to a string value. Which uses Parse to convert to an integer.
You can subtract to the datetime object. (if is a DateTime Type) if not, you should parse.
To handle errors, I would recommend to use DateTime.tryParse(value, out dateTime);
DateTime parsedDateFromBD;
if(DateTime.tryParse("2013-06-18 17:00:00.000", out parsedDateFromBD)
{
// do Stuff
}
else
{
// do something else
}
if you get it as a datetime from the db you can simply:
var calcDate1 = dateFromBD.addSeconds(3000); //to Add
var calcDate2 = dateFromBD.addSeconds(-3000); //to subtract
Cheers
Ricardo
In addition to the other answers here is how to parse the newDate to string that mach the required output
string date = "2013-06-18 17:00:00.000";
string duration = "-3000";
int durationSeconds = int.Parse(duration);
var newDate = DateTime.Parse(date).AddSeconds(durationSeconds).ToString("yyyy-MM-dd HH:mm:ss.fff");
The output is
//2013-06-18 16:10:00.000
Here you can find more about DateTime.ToString()

String To DateFormat Conversion

Is there any way I don't have to specify the number of digits in day/month/year?
For e.g 1/2/1991
I want a method which satisfies both 1/2/1991,11/3/1990,12/12/1991
I don't know how many digits will be there in either month, year, or days.
My code is
string copy = splittedData[0] + splittedData[1] + splittedData[2];//date+month+year
DateTime datetime = DateTime.ParseExact(copy, "ddMMyyyy", CultureInfo.InvariantCulture);
DateTime dateAndTime = datetime;
The problem is the number of digits in splitted data array are not known to me and thus the above format "ddMMyyyy" give me exception on some cases.
Since you already have the day month and year then just create a date with the three of them like so;
DateTime date = new DateTime(year, month, day);
No parsing is necessary. You already have all the fields you want to create the date, and you dont need to put it into a special format to create a date.
If you are not sure the if the input is valid, then wrap the creation in a try/catch block to catch an ArgumentOutOfRangeException should it should occur.
Since you updated your question with the code you have, you can concatenate date components with a separator like:
string copy = splittedData[0] + "/" + splittedData[1] + "/" + splittedData[2];
Later you can do:
DateTime dt = DateTime.ParseExact(copy, "d/M/yyyy", CultureInfo.InvariantCulture);
I used the format "d/M/yyyy" with single d and M which would account for both single/double digit day/month.
So it will work for dates like:
01/01/2013
1/01/2013
22/09/2013
02/9/2013
DateTime.ParseExact is specifically intended to not allow what you are asking for. DateTime.Parse will allow it, though.
You say you have the 3 parts as separate strings -- if you insert the /'s and parse, it should succeed (InvariantCulture expects the order month-day-year):
string datetimeString = string.Join("/", new[] {month, day, year});
DateTime datetime = DateTime.Parse(datetimeString, CultureInfo.InvariantCulture);
Or you could convert them to integers and construct a DateTime directly:
DateTime datetime = new DateTime(Convert.ToInt32(year), Convert.ToInt32(month), Convert.ToInt32(day));
What #n00b said. You've already got the individual components of the date: why are you globbing them back together just so you can call DateTime parsing routines? Just do something like this:
private static DateTime StringToDateTime( string year , string month , string day )
{
int yyyy = int.Parse(year) ;
int mm = int.Parse(month) ;
int dd = int.Parse(day) ;
DateTime dt = new DateTime(yyyy,mm,dd) ;
return dt ;
}
As an added bonus, The above code will probably run faster than DateTime.Parse() or DateTime.ParseExact().

Categories

Resources