Parsing Cultural Specific Decimal and DateTime values in .NET - c#

I have an ASP.NET MVC app that must work in both English and German. In one of my views, the user is inputting a decimal value and a date/time value.
// Get the price
string price = "1.23";
decimal priceValue = 0;
var allowedStyles = (NumberStyles.AllowDecimalPoint & NumberStyles.AllowThousands);
if (Decimal.TryParse(price, allowedStyles, CultureInfo.InvariantCulture, out priceValue))
{
model.Price = priceValue;
}
else
errors.Add("Please enter a valid price.");
// Parse the date
string date = "03/23/2015";
if (String.IsNullOrWhiteSpace(date) == false)
{
DateTime dateValue = DateTime.MinValue;
if (DateTime.TryParse(saleDate, out dateValue))
{
model.Date = dateValue;
}
else
errors.Add("Please enter a valid date.");
}
When the above code runs in the English culture, the Decimal.TryParse line returns false. When the code runs in the German culture, both the Decimal.TryParse and DateTime.TryParse lines return false. What am I doing wrong? How can I parse Decimal and DateTime values across cultures?

When the above code runs in the English culture, the Decimal.TryParse
line returns false
Because you are using bitwise AND with & operator and NumberStyles.AllowDecimalPoint & NumberStyles.AllowThousands generates NumberStyles.None which indicates no style for your element. From documentation;
Indicates that no style elements, such as leading or trailing white
space, thousands separators, or a decimal separator, can be present in
the parsed string. The string to be parsed must consist of integral
decimal digits only.
If you change & to | your Decimal.TryParse returns true.
When the code runs in the German culture, both the Decimal.TryParse
and DateTime.TryParse lines return false.
Same for Decimal.TryParse method. BUT, de-DE culture has , instead of . as a NumberDecimalSeparator. But it has . as a NumberGroupSeparator that's why it parses your 1.23 value as 123. It thinks this is a thousands separator, not a decimal separator.
For your DateTime.TryParse method, since you didn't tell us what is saleDate exactly, looks like it is not a standard date and time format for your CurrentCulture, that's why it returns false.
If you mean date instead of saleDate, that means MM/dd/yyyy is not a standard date and time format for your CurrentCulture and neither for de-DE culture.
You can use DateTime.TryParseExact or DateTime.ParseExact (preferable) method with a culture that has / as a DateSeparator like InvariantCulture like;
string date = "03/23/2015";
DateTime dt;
if(DateTime.TryParseExact(date, "MM/dd/yyyy", CultureInfo.InvariantCulture,
DateTimeStyles.None, out dt))
{
model.Date = dateValue;
}

You should not be using InvariantCulture for that. You should parsing using 1 culture at a time, then attempting the other if that fails.

Related

Convert(change) current DateTime as per culture in c#

if (!IsPostBack && !Page.IsCallback)
{
double OffsetHrs = GetTimeZoneOffsetFromCookie();
string dateFormat = ServiceManager.LocalizationService.GetString("AppHeaderTop", "DateFormat", "g");
CultureSelected CultureSelected = GetCultureSelected();
ASPxLabelCurrentTime.Text = DateTime.Now.ToUniversalTime().AddHours(-OffsetHrs).ToString(dateFormat);
if (CultureSelected.CultureCode != "en-US")
{
DateTimeFormatInfo usDtfi = new CultureInfo("en-US", false).DateTimeFormat;
DateTimeFormatInfo currentDtfi = new CultureInfo(CultureSelected.CultureCode, false).DateTimeFormat;
ASPxLabelCurrentTime.Text = Convert.ToDateTime(ASPxLabelCurrentTime.Text, usDtfi).ToString(currentDtfi.ShortDatePattern); //what can i Use here ?
}
Let say Output of ASPxLabelCurrentTime.Text
for en-US culture is 11/2/2015 4:14 PM (70)
If I select specific culture I want this datetime 11/2/2015 4:14 PM (70) to appear in that specific culture format.
Your question seems unclear but I try to give a shot.
First of all, what is this (70) exactly? Where is this came from? en-US culture can't parse this string without using it in a string literal delimiter with ParseExact or TryParseExact methods. On the other hand, since you assing ASPxLabelCurrentTime.Text the result of the DateTime.Now.ToUniversalTime().AddHours(-OffsetHrs).ToString(dateFormat) code, I don't believe this (70) part is really an issue on this question.
Second, If I understand clearly, the problem seems the usage of DateTime.ToString(string) method.
ASPxLabelCurrentTime.Text = Convert.ToDateTime(ASPxLabelCurrentTime.Text, usDtfi)
.ToString(currentDtfi.ShortDatePattern);
// ^^^ Problem seems here
Okey let's say you successfully parse this ASPxLabelCurrentTime.Text with usDtfi culture (which is en-US), but with this .ToString(string) method, you are not using currentDtfi settings actually, you are using CurrentCulture settings when you generate formatted string representation of your DateTime.
From DateTime.ToString(String) doc;
Converts the value of the current DateTime object to its equivalent
string representation using the specified format and the formatting
conventions of the current culture.
Since we don't know what GetCultureSelected method returns exactly, it may or may not be the same culture with currentDtfi.
I strongly suspect, you can solve this problem to using that culture as a second parameter in ToString method as;
ASPxLabelCurrentTime.Text = Convert.ToDateTime(ASPxLabelCurrentTime.Text, usDtfi)
.ToString(currentDtfi.ShortDatePattern, currentDtfi);
IF this (70) is really part of on your string, you need to ParseExact or TryParseExact methods to supply exact format of it.
string s = "11/2/2015 4:14 PM (70)";
DateTime dt;
if(DateTime.TryParseExact(s, "MM/d/yyyy h:mm tt '(70)'", CultureInfo.GetCultureInfo("en-US"),
DateTimeStyles.None, out dt))
{
ASPxLabelCurrentTime.Text = dt.ToString(currentDtfi.ShortDatePattern, currentDtfi);
}

Getting error String was not recognized as a valid DateTime

In gridview showing date on label and fetching date from that label.
Datetime Date;
Label date = gvOrderExecuted.Rows[0].FindControl("lblDate") as Label;
Date = Convert.ToDateTime(date.Text);
This code throws an error
'String was not recognized as valid Date Time'.
I am passing value for date like '3/31/2015'
Convert.ToDateTime method uses DateTime.Parse method explicitly with your CurrentCulture settings if you don't provide any IFormatProvider.
Looks like that M/dd/yyyy is not a standard date and time format of your CurrentCulture and that's why it throws FormatException.
You can use DateTime.ParseExact or DateTime.TryParseExact methods to specify your custom string format like;
string s = "3/31/2015";
DateTime dt;
if(DateTime.TryParseExact(s, "M/dd/yyyy", CultureInfo.InvariantCulture,
DateTimeStyles.None, out dt))
{
Console.WriteLine(dt); // 31/03/2015 00:00:00
}
"/" separator has a special meaning as replace me with current culture or supplied culture date separator. That means, when you parse it a string with that separator, your parsing may fail even if your string and format matches exactly. You can escape this these character as '/' without worry about it.
string s = "3/31/2015";
DateTime dt = DateTime.ParseExact(s, "M'/'dd'/'yyyy", CultureInfo.CurrentCulture);

Storing date time in culture neutral format + C#

I accept date info from the user, via date picker. I need to store them in a culture neutral way. The problem I am facing is, if I store the date as per en-US format (based on calendar settings), namely 11/20/1990 it will fail to parse when the culture is en-GB.
And vice versa happens when culture is en-US, date stored as per UK format, dd/mm/yyyy refuses to parse. How do I store date info in a culture neutral way in a file so that, I get the date to work in both locations?
DateTime.TryParse(userEnteredValue, out result);
result.ToShortDateString(); //this is what I am doing
tried this code for invariant culture
string input = "20/10/1983";
DateTime userInput;
bool result = DateTime.TryParse(input, out userInput);
string invariantCulture = userInput.Date.ToString(CultureInfo.InvariantCulture);
DateTime storedValue;
result = DateTime.TryParse(invariantCulture, out storedValue);
tried this code with en-GB calendar settings, second statement DateTime.TryParse fails infact.
#Soner Gönül's answer is spot on if you are saving the dates to a database. However, you mention that you are looking to round-trip a DateTime to and from a file.
As the file is presumably a text file you'll need to write the DateTime in a culture neutral manner. You can do this by using the "O" format specified on the DateTime.ToString method. This will output a string representation that complies with ISO 8601. The resultant string can be parsed using DateTime.Parse without the need for culture information.
As an example:
string filename = #"c:\temp\test.txt";
string usDateString = "11/18/2014 12:32"; // MM/dd/yyyy
string ukDateString = "18/11/2014 12:33"; // dd/MM/yyyy
//I'm mimicking you getting the DateTime from the user here
//I'm assuming when you receive the date(s) from the front
//end you'll know the culture - if not then all bets are off.
DateTime usDate =
DateTime.Parse(usDateString, CultureInfo.GetCultureInfo("en-US"));
DateTime ukDate =
DateTime.Parse(ukDateString, CultureInfo.GetCultureInfo("en-GB"));
//write the dates to a file using the "o" specifier
File.AppendAllText(filename, usDate.ToString("o") + Environment.NewLine);
File.AppendAllText(filename, ukDate.ToString("o") + Environment.NewLine);
//read them back in as strings
string[] contents = File.ReadAllLines(filename);
foreach (var date in contents)
{
//prove we can parse them as dates.
Console.WriteLine(DateTime.Parse(date).ToString());
}
This creates a file with the contents:
2014-11-18T12:32:00.0000000
2014-11-18T12:33:00.0000000
and on my system (in the UK) it prints:
18/11/2014 12:32:00
18/11/2014 12:33:00
if I store the date as per en-US format...
Please stop! Looks like you try to save your DateTime values with their string representations.
A DateTime doesn't have any implicit format. It has just date and time values. String representations of them can have a format. Generate your insert queries and pass your DateTime values directly with parameterized way.
Please read;
Bad habits to kick : choosing the wrong data type
If you want to get string representations of your DateTime values with specific format, you can always use DateTime.ToString() method and it's overloads.
Your en-GB culture can parse MM.dd.yyyy (since you use / format specifier which replaces itself supplied culture DateSeparator) and en-US culture can parse MM/dd/yyyy as well.
But since you use .ToShortDateString() method, this represents your datetime based your CurrentCulture settings. As a solution, you can set this property which culture you want and ToShortDateString works based on it.
result = DateTime.TryParse(invariantCulture, out storedValue);
tried this code with en-UK calendar settings, second statement
DateTime.TryParse fails infact.
Because this DateTime.TryParse uses your CurrentCulture and since your invariantCulture variable is 10/20/1983 00:00:00, that means this is not a standard date and time format for your CurrentCulture.
There is no such a culture as en-UK by the way.
10/20/1983 00:00:00 is MM/dd/yyyy HH:mm:ss format. But en-GB culture doesn't have this format as a standard date and time format, that's why your method returns false.
As an alternative, you can use custom format strings like;
string s = "10/20/1983 00:00:00";
string format = "MM/dd/yyyy HH:mm:ss";
DateTime dt;
if (DateTime.TryParseExact(s, format, CultureInfo.GetCultureInfo("en-GB"),
DateTimeStyles.None, out dt))
{
Console.WriteLine(dt);
}
I bumped into this question and figured I'd bring in some other way nobody has mentioned yet:
DateTime.ToBinary() for serializing and DateTime.FromBinary(Int64) for deserialization.
What these do is the following:
ToBinary() returns a long which can be easily stored in a culture invariant way.
FromBinary(Int64) will return a DateTime object from the long parameter supplied.
(They even take the date time Kind property into consideration).
And here's some code to go with it:
DateTime d1l = DateTime.Now;
long dl = d1l.ToBinary();
DateTime d2l = DateTime.FromBinary(dl);
DateTime d1u = DateTime.UtcNow;
long du = d1u.ToBinary();
DateTime d2u = DateTime.FromBinary(du);
Console.WriteLine("Local test passed: " + (d1l == d2l).ToString());
Console.WriteLine("d2l kind: " + d2l.Kind.ToString());
Console.WriteLine("Utc test passed: " + (d1u == d2u).ToString());
Console.WriteLine("d2u kind: " + d2u.Kind.ToString());
And the console output:
Local test passed: True
d2l kind: Local
Utc test passed: True
d2u kind: Utc
I find this to be pretty neat!
String s = "24. 11. 2001";
d = DateTime.Parse(s, CultureInfo.CreateSpecificCulture("sk-SK"));
en-AU (English Austrailia): 24/11/2001
en-IA (English India): 24-11-2001
en-ZA (English South Africa): 2001/11/24
en-US (English United States): 11/24/2001
i suspect you prefer English (India) (en-IA).
But if you really can't decide what culture to use when converting dates to strings and vice-versa, and the dates are never meant to be shown to a user, then you can use the Invariant Culture:
String s = "11/24/2001" //invariant culture formatted date
d = DateTime.Parse(s, CultureInfo.InvariantCulture); //parse invariant culture date
s = d.ToString(CultureInfo.InvariantCulture); //convert to invariant culture string
I tried to figure out a solution via this approach, please let me know if its correct.
The code which I use is below.
For Saving date time I use ticks as below.
DateTime userInput;
bool result = DateTime.TryParse(this.dpSave.Text, out userInput);
if (result)
{
long ticks = userInput.Ticks;
System.IO.File.WriteAllText(#"D:\folder\Ticks.txt", ticks.ToString());
}
else
{
MessageBox.Show("Date time parse failed");
}
For loading it back, I use
if (System.IO.File.Exists(#"D:\folder\Ticks.txt"))
{
string contents = System.IO.File.ReadAllText(#"D:\Sandeep\Ticks.txt");
long ticks;
if (long.TryParse(contents, out ticks))
{
DateTime storedDateTime = new DateTime(ticks);
MessageBox.Show("Stored Date" + storedDateTime.ToShortDateString());
}
else
{
MessageBox.Show("Unable to obtain stored dates");
}
}
this seems to work, provided, I save using en-US culture and load using en-GB culture.
please let me know if this is the right approach!
a) Exchange data shall always be stored culture invariant (xml etc)
b)You've gotta to be careful with the terminology.
What you exactly mean is culture INVARIANT (and not 'culture neutral').
There are three types of cultures:
1) invariant
2) culture neutral (e.g. "en")
3) culture specific (e.g "en-US")
public DateTime getdate3()
{
CultureInfo Invc = CultureInfo.InvariantCulture; //culture
string cul = Thread.CurrentThread.CurrentCulture.Name;
CultureInfo us = new CultureInfo(cul);
string shortUsDateFormatString = us.DateTimeFormat.ShortDatePattern;//pattern
DateTime dt = Convert.ToDateTime(DateTime.Now);
TimeZoneInfo myZone = TimeZoneInfo.FindSystemTimeZoneById("India Standard Time"); //india zone
DateTime dateindia = TimeZoneInfo.ConvertTime(dt, myZone);
string dt1 = Convert.ToDateTime(dateindia).ToString(shortUsDateFormatString); //string format
}

DateTime Parse to US format 'error', returns me non US format

I have a problem parsing string to DateTime to US format. Although I provide the string in format of MM/dd/yyyy it keeps returning me the DateTime in format of dd/MM/yyyy and I have tried all of the below.
string[] formats = { "MM/dd/yyyy hh:mm:ss", "MM/dd/yyyy" };
DateTime dateTime;
var ex = DateTime.TryParseExact("12/29/1989", formats, provider, DateTimeStyles.None, out dateTime);
And the above will return me the dateTime as "29/12/1989";
I have also tried:
var dt = DateTime.Parse("12/29/1989", System.Globalization.CultureInfo.GetCultureInfo("en-US"));
basicly I have tried all the DateTime Parse option and will all return me non us format.
I am based in th UK and my machine's locale is en-UK.
all return me non us format
No, they return you a DateTime value. A DateTime value doesn't have a format. It's just a date and a time. When you want to convert that back into a string, it will use the default format for your current culture unless you say otherwise.
It's important to differentiate between the data stored in a value (a date and time) and string representations of that value.
To give a similar example, what would you expect the result of this code to be?
int x = Convert.ToInt32("FF", 16);
Console.WriteLine(x);
Would you expect "255" or "FF"? It's 255, because the default format for converting an int to a string is decimal. It doesn't matter that the value was originally parsed from hex - that's not part of the value. Apply the exact same logic to DateTime.

Convert to DateTime from string containing decimal and comma millisecond separators

Given the following 2 strings notice the ".185" and ",185"
2011-09-15 17:05:37,185
2011-09-15 17:05:37.185
Reading from a file (not in my control) and I can see they have dates in both formats. I need to create a function that cater for both scenarios.
Is the '.' and ',' a culture specific?
Any suggestion for such a function?
This below is not working as I don't get a date.
class Program
{
static void Main(string[] args)
{
string date1="2011-09-15 17:05:37.185";
string date2="2011-09-15 17:05:37,185";
const string format1 = "dd/MM/yyyy HH:mm:ss.ff";
const string format2 = "dd/MM/yyyy HH:mm:ss,ff";
DateTime resultDate1;
DateTime resultDate2;
DateTime.TryParseExact(date1, format1, CultureInfo.InvariantCulture, DateTimeStyles.None, out resultDate1);
DateTime.TryParseExact(date2, format2, CultureInfo.InvariantCulture, DateTimeStyles.None, out resultDate2);
Console.WriteLine(resultDate1.ToString());
Console.WriteLine(resultDate2.ToString());
Console.Read();
}
}
Is the . and , a culture specific?
Yes. In Europe, a comma is often used instead of a period as the decimal separator.
Any suggestion for a solution?
Yes. My first thought is that the DateTime.ParseExact()/DateTime.TryParseExact() functions have an overload that allows an array of formats to test. You could include both the en-US variant and the en-GB variant. Except that I don't think this will work, as you still only get to include a single culture specifier. So instead, I recommend calling .Replace() before passing the string to ParseExact function to change any commas that might be in the string to periods.
In your updated example code, your format string just doesn't match your example dates. You should use this:
yyyy-MM-dd HH:mm:ss.fff
You should use DateTime.ParseExact or .TryParseExact as suggested in Hans Passant's comment.
DateTime d1;
string[] formats = new [] { "yyyy-MM-dd HH:mm:ss.fff", "yyyy-MM-dd HH:mm:ss,fff" };
DateTime.TryParseExact(s1, formats, CultureInfo.InvariantCulture,
DateTimeStyles.None, out d1);
You should also specify CultureInfo.InvariantCulture, as otherwise the ParseExact method may use a culture-specific date separator (in place of /, e.g. "." in Germany) or time separator (in place of ":").

Categories

Resources