Parsing horrible timestamp C# - c#

I have a timestamp from a server of the form 20220505 17:36:29 - it has 2 whitespaces, and I do not trust the sender to always send the same number of whitespaces in future revisions - ideally would like to handle any number of whitespaces the same.
I tried this with DateTime.ParseExact but failed:
var horribleTimestamp = "20220505 17:36:29";
var timestamp = DateTime.ParseExact(horribleTimestamp, "yyyyMMdd hh:mm:ss", CultureInfo.InvariantCulture)
// throws `System.FormatException: String '20220505 17:36:29' was not recognized as a valid DateTime.`
To save my headaches with timezones later how can I achieve this with Nodatime as i think makes sense to switch to that already.
The time is local from my PC and I would like to convert this to a global timestamp (which I believe should be Instant?) for a given local timezone?

If you want to handle any amount of whitespace, there are two options:
Use a regular expression (or similar) to get it into a canonical format with a single space
Split on spaces and then parse the first and last parts separately. (Or split on spaces, recombine the first and last parts and parse...)
In Noda Time, the value you've got represents a LocalDateTime, so that's what you should parse it to. Here's a complete example using the regex approach:
using NodaTime;
using NodaTime.Text;
using System.Text.RegularExpressions;
// Lots of spaces just to check the canonicalization
string text = "20220505 17:36:29";
// Replace multiple spaces with a single space.
string canonicalized = Regex.Replace(text, " +", " ");
// Note: patterns are immutable; you should generally store them in
// static readonly fields. Note that "uuuu" represents an absolute year number,
// whereas "yyyy" would be "year of era".
LocalDateTimePattern pattern =
LocalDateTimePattern.CreateWithInvariantCulture("uuuuMMdd HH:mm:ss");
ParseResult<LocalDateTime> result = pattern.Parse(canonicalized);
// Note: if you're happy with an exception being thrown on a parsing failure,
// juse use result.Value unconditionally. The approach below shows what to do
// if you want to handle parse failures without throwing an exception (or with
// extra behavior).
if (result.Success)
{
LocalDateTime value = result.Value;
Console.WriteLine(value);
}
else
{
// You can also access an exception with more information
Console.WriteLine("Parsing failed");
}

You can pass multiple formats to ParseExact as an array
var horribleTimestamp = "20220505 17:36:29";
var formats = new[]{"yyyyMMdd HH:mm:ss","yyyyMMdd HH:mm:ss","yyyyMMdd HH:mm:ss"};
var timestamp = DateTime.ParseExact(horribleTimestamp, formats, CultureInfo.InvariantCulture, 0);
dotnetfiddle

You have an error in your format. use HH instead of hh. See updated code below
var horribleTimestamp = "20220505 17:36:29";
var timestamp = DateTime.ParseExact(horribleTimestamp, "yyyyMMdd HH:mm:ss", CultureInfo.InvariantCulture)
Here is y link that explains what you can use in a format -> https://www.c-sharpcorner.com/blogs/date-and-time-format-in-c-sharp-programming1

You can solve the problem with the whitespaces in this way:
var horribleTimestamp = "20220505 17:36:29";
var date = horribleTimestamp.Substring(0, 8);
var index = horribleTimestamp.LastIndexOf(' ') + 1;
var time = horribleTimestamp.Substring(index, horribleTimestamp.Length - index);
var timestamp = DateTime.ParseExact($"{date} {time}", "yyyyMMdd HH:mm:ss", CultureInfo.InvariantCulture);
I suppose that date has always 8 characters and that space is always present. In other case, check index == -1.

Related

Parsing double with dot to comma

I am working with doubles. In the Netherlands we make use of 51,3 instead of 51.3. I did write a piece of code that works with dots instead of commas. But the result of the previously written code returns a double the English way, with a dot. I am encountering some strange errors.
Here is what I have:
var calResult = 15.2d;
var calResultString = calResult.ToString(CultureInfo.GetCultureInfo("nl-NL"));
var result = double.Parse(calResultString);
calResult == "15.2" -> as expected
calResultString == "15,2" -> as expected
result == "152" -> here I expect a comma.
A also did try to add the cultureinfo also in the double.Parse. This resulted in a "15.2".
TLDR: I need to convert an English/American double to a Dutch(or similar rules) one.
Thanks in advance! :)
P.S
I hope this is not a duplicate question, but didn't found anything this specific.
You, probably, should either provide "nl-NL" whenever you work with Netherlands' culture
var calResult = 15.2d;
var calResultString = calResult.ToString(CultureInfo.GetCultureInfo("nl-NL"));
// We should parse with "nl-NL", not with CurrentCulture which seems to be "en-US"
var result = double.Parse(calResultString, CultureInfo.GetCultureInfo("nl-NL"));
Or specify CurrentCulture (default culture)
CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("nl-NL");
var calResult = 15.2d;
// now CultureInfo.GetCultureInfo("nl-NL") is redundant
var calResultString = calResult.ToString();
var result = double.Parse(calResultString);
Finally, if you have a string which represents some floating point value in en-US culture, and you want the same value but be a string in nl-NL format:
string source = "123.456";
string result = double
.Parse(source, CultureInfo.GetCultureInfo("en-US"))
.ToString(CultureInfo.GetCultureInfo("nl-NL"));
Numbers and strings don't contain any culture information, instead you specify the culture when you convert between numbers and strings.
result == "152" -> here I expect a comma
What happened is that you asked the operating system to parse "15,2" into a double, and didn't specify a culture. It defaulted to US culture and ignored the comma.
If you'd specified a culture:
var result = double.Parse(calResultString, CultureInfo.GetCultureInfo("nl-NL"));
it would have given you the right value (15.2), and that might even have been displayed as 15,2 if your computer was configured to the right number format (and the debugger used your preference).
Ideally you don't hard-code the culture, but use the culture that the user has chosen.
I've written a simple method that will check for the coma character in your input and replace it with a dot. I believe the best way is to take an input as a string value. this way you can manipulate it and then you can parse it and return a double or a string if you wish:
var input = Console.ReadLine();
double parsedDouble;
if (input.Contains(","))
{
input = input.ToString().Replace(",", ".");
}
if (!Double.TryParse(input, out parsedDouble))
{
Console.WriteLine("Error parsing input");
}
else
{
Console.WriteLine(parsedDouble);
}
Console.ReadLine();
edit: the answers from Robin Bennett/Dmitry Bychenko are much better than mine, as mine is just more manual. I wasn't aware of the overload of parse that he had provided.
I'll leave my solution, cause it does solve this issue, even if it's a bit more... brute ;)
var calResult = 15.2d;
var calResultString = calResult.ToString();
string result = double.Parse(calResultString).ToString(CultureInfo.GetCultureInfo("nl-NL"));

Custom number group (thousand separator) for 1000,000 format in c#

My code is as below I am trying to get custom grouping on number
example: 1000000 should be 1000,000
I have tried many search result that allows thousand separator and also custom grouping but each one gives a result like 10,00,000
decimal value = decimal.Parse(txtamount.Text.Trim());
txtamount.Text = value.ToString("#,###0");
In this code I have tried below code also
txtamount.Text = String.Format("{0:n0}", value);
txtamount.Text = value.ToString("#,##0", new System.Globalization.CultureInfo("en-US"));
txtamount.Text = value.ToString("0,0.0",CultureInfo.InvariantCulture); //-->1,000,000.0
It is not a normal thousand separator grouping of a number.
I am trying to get output in a format 1000,000
To just have one grouping separator, you can use this:
// create a writable copy of the culture of your choice
var ci = (CultureInfo)CultureInfo.GetCultureInfo("en-us").Clone();
// change the group sizes: the first one after 3 digits (counting backwards)
// NO second (third, ..) one!
ci.NumberFormat.NumberGroupSizes = new[]{3,0};
// and use it
// the , in the format means "use a group separator", it does NOT specify a position
var millionString1 = 1_000_000.ToString("#,#", ci); // 1000,000
var millionString2 = 1_000_000.ToString("N0", ci); // also 1000,000
But do note that 10 million would now become 10000,000.
See docs.
Since it's a custom format, you'll need to escape your comma ,
string.Format("{0:####\\,###}",value)
Doesn't seem possible without a custom formatter, but the comma can be inserted after. For example :
string value = "1234567.890"
string result = Regex.Replace(value, #"(\d+)(\d\d\d)", "$1,$2"); // result = "1234,567.890"

Contains doen't check in the date range

I have a date range come like this,
string ActualReleaseDates ="7/8/2016, 7/9/2016, 7/11/2016,7/3/2016,7/10/2016,7/17/2016,7/24/2016,7/31/2016";
string NewsReleasedDate ="07/11/2016";
I want to check NewsReleaseDate is inside the ActualReleaseDates
But in the following code it return as a false.
if (ActualReleaseDates.Split(',').Contains(NewsReleasedDate.TrimStart(new Char[] { '0' })))
{
//some code here
}
The immediate problem is that after splitting your ActualReleaseDates string, there isn't an entry of "7/11/2016"... instead, there's an entry of " 7/11/2016"... note the space.
But more fundamentally, just trimming the start of NewsReleasedDate won't help if the value is something like "07/08/2016"... what you should be doing is handling these values as dates, rather than as strings:
Split ActualReleaseDates by comma, then parse each value (after trimming whitespace) in an appropriate format (which I suspect is M/d/yyyy) so that you get a List<DateTime>.
Parse NewsReleasedDate in the appropriate format, which I suspect is MM/dd/yyyy, so you get a DateTime.
See whether the parsed value from the second step occurs in the list from the first step.
(I'd personally recommend using Noda Time and parsing to LocalDate values, but I'm biased...)
Fundamentally, you're trying to see whether one date occurs in a list of dates... so make sure you get your data into its most appropriate representation as early as possible. Ideally, avoid using strings for this at all... we don't know where your data has come from, but if it started off in another representation and was converted into text, see if you can avoid that conversion.
The white space problem. You can use trim() and ' 7/11/2016' will be '7/11/2016'
var ActualReleaseDates = "7/8/2016, 7/9/2016, 7/11/2016,7/3/2016,7/10/2016,7/17/2016,7/24/2016,7/31/2016";
var NewsReleasedDate = "07/11/2016";
var splitActualReleaseDates = ActualReleaseDates.Split(',').Select(x => x.Trim());
if (splitActualReleaseDates.Contains(NewsReleasedDate.TrimStart(new Char[] { '0' })))
{
}
You can use linq to convert your strings into DateTime objects and compare them instead of strings
string ActualReleaseDates ="7/8/2016,7/9/2016,7/11/2016,7/3/2016,7/10/2016,7/17/2016,7/24/2016,7/31/2016";
string NewsReleasedDate ="07/11/2016";
var releaseDates = ActualReleaseDates.Split(',').Select(x => DateTime.Parse(x));
var newsReleased = DateTime.Parse(NewsReleaseDate);
if (releaseDates.Contains(newsReleased))
{
//some code here
}
please note that DateTime is parsed respectively to the current Culture. You can use DateTime.ParseExact if you want to specify exact date format.
You can Prase to DateTime before doing the query like this:
(I think this is the most accurate and guaranteed way to compare dates)
Func<string, DateTime> stringToDate = s => DateTime.ParseExact(s.Trim(), "M/d/yyyy",
CultureInfo.InvariantCulture);
DateTime newReleaseDateTime = stringToDate(NewsReleasedDate);
bool result = ActualReleaseDates.Split(',').Select(x => stringToDate(x))
.Contains(newReleaseDateTime);
It returns false because of the date 07/11/2016 stored in NewsReleasedDate is stored as string with a '0' at the begining. And in the ActualReleaseDates string you have white spaces between the ',' and numbers.
Try to rewrite theese strings like this :
ActualReleaseDates ="7/8/2016,7/9/2016,7/11/2016,7/3/2016,7/10/2016,7/17/2016,7/24/2016,7/31/2016"; // white spaces removed.
and the variable like this :
NewsReleasedDate ="7/11/2016"; // 0 removed
This is my code example :
string ActualReleaseDates = "7/8/2016,7/9/2016,7/11/2016,7/3/2016,7/10/2016,7/17/2016,7/24/2016,7/31/2016";
string NewsReleasedDate = "7/11/2016";
string[] dates = ActualReleaseDates.Split(',');
Console.WriteLine(dates.Contains(NewsReleasedDate));
This is not the best way to compare dates, you can use Date class which is usefull to do this kind of comparations.

Parsing TimeSpan with optional minus sign

MSDN says:
The styles parameter affects the interpretation of strings parsed using custom format strings. It determines whether input is interpreted as a negative time interval only if a negative sign is present (TimeSpanStyles.None), or whether it is always interpreted as a negative time interval (TimeSpanStyles.AssumeNegative). If TimeSpanStyles.AssumeNegative is not used, format must include a literal negative sign symbol (such as "-") to successfully parse a negative time interval.
I have try the following:
TimeSpan.ParseExact("-0700", #"\-hhmm", null, TimeSpanStyles.None)
However it returns 07:00:00. And fails for "0700".
If I try:
TimeSpan.ParseExact("-0700", "hhmm", null, TimeSpanStyles.None)
It fails too.
TimeSpan.ParseExact("0700", new string [] { "hhmm", #"\-hhmm" }, null, TimeSpanStyles.None)
Does not fail for both "0700" and "-0700", but always return the positive 07:00:00.
How is it supposed to be used?
It looks like this isn't supported. From the custom TimeSpan format strings page:
Custom TimeSpan format specifiers also do not include a sign symbol that enables you to differentiate between negative and positive time intervals. To include a sign symbol, you have to construct a format string by using conditional logic. The Other Characters section includes an example.
This does seem really odd though. Ick.
As mentioned in my comment, you could use my Noda Time project's Duration parsing for this; overkill for just this case, but if you had other date/time work in the project, it could be useful.
For example:
var pattern = DurationPattern.CreateWithInvariantCulture("-hhmm");
var timeSpan = pattern.Parse("-0700").Value.ToTimeSpan();
It does look like you annoyingly need to check yourself if it begins with a leading -
// tsPos = 07:00:00
string pos = "0700";
TimeSpan tsPos = TimeSpan.ParseExact(pos, new string [] { "hhmm", #"\-hhmm" }, null,
pos[0] == '-' ? TimeSpanStyles.AssumeNegative : TimeSpanStyles.None);
// tsNeg = -07:00:00
string neg = "-0700";
TimeSpan tsNeg = TimeSpan.ParseExact(neg, new string [] { "hhmm", #"\-hhmm" }, null,
neg[0] == '-' ? TimeSpanStyles.AssumeNegative : TimeSpanStyles.None);

Can't get DateTime.TryParseExact to work

I have the following code:
string[] format = { "yyyy/MM/dd", "MM/dd/yyyy" };
DateTime valueDate;
value = value.Replace("-", "/");
if (DateTime.TryParseExact(value, format, CultureInfo.InvariantCulture, DateTimeStyles.None, out valueDate))
{
value = "TO_DATE(" + valueDate + ", 'yyyy-mm-dd')";
}
else
{
throw new Exception("Could not parse incoming date: " + valueDate);
}
So now I have a test case.
And value = '2013/01/21' after the replace statement replacing "-" with "/".
This should match the first format in the format string array.
But TryParseExact is not working and always goes to the else path.
Can anyone see any errors in this code?
It's not TryParseExact that's the problem, it's the value of your string. I say that because this little scriptcs script:
using System.Globalization;
string[] format = { "yyyy/MM/dd", "MM/dd/yyyy" };
DateTime valueDate;
var value = "2013/01/21";
if (DateTime.TryParseExact(value, format, CultureInfo.InvariantCulture, DateTimeStyles.None, out valueDate))
{
Console.WriteLine("Success!");
}
else
{
Console.WriteLine("Failure!");
}
prints Success!. So, in other words, this statement:
And value = '2013/01/21' after the replace statement replacing "-" with "/".
literally cannot be correct.
As stated by James, it's very possible there is whitespace in the actual string value. There are a couple solutions: remove the whitespace or allow whitespace. To allow whitespace you could use DateTimeStyles.AllowWhiteSpaces rather than DateTimeStyles.None.
Don't know if it's your issue, but make sure you always escape out your forward-slashes when using date formats. A / character is not a literal slash, but rather your local system's date separator (which is usually the forward-slash, but not always).
When using a format string, you escape a forward-slash with a real backslash, like:
DateTime.ParseExact("2012/12/31", "yyyy\\/MM\\/dd", null);
// or
DateTime.ParseExact("2012/12/31", #"yyyy\/MM\/dd", null);

Categories

Resources