I'm parsing a date string from a database so that I can display it in the current culture of the UI thread. For some reason, the date is not parsing with respect to the culture - specifically, I'm parsing a en-US date to switch to a es-ES date and the month/day positions are not switching.
According to this MSDN article I should just be able to use Parse with only the date string as a parameter. I should also be able to explicitly provide a culture object. Neither works and my date remains as mm/dd instead of dd/mm. I've verified that both the thread's CurrentCulture and CurrentUICulture are set properly and if I do a new DateTime, that outputs correctly.
Am I missing something?
EDIT: Nothing fancy in the code, just the .NET API.
CultureInfo culture = CultureInfo.CreateSpecificCulture(cultureName);
Thread.CurrentThread.CurrentCulture = culture;
Thread.CurrentThread.CurrentUICulture = culture;
DateTime formattedDate = DateTime.Parse("5/9/2014");
formattedDate.ToShortDateString(); //this returns 5/9/2014
DateTime.Today.ToShortDateString(); //this returns 9/5/2014
The problem you are having is that 5/9/2014 is a perfectly valid month string in either dd/mm/yyyy or mm/dd/yyyy format so when you do DateTime.Parse("5/9/2014") it will successfully parse it as 5th September 2014 (since the es-es date format is dd/mm/yyyy).
This then explains why when you output you get something different to DateTime.Today (which is obviously 9th May).
A working version of your program would be:
var outputCulture = CultureInfo.CreateSpecificCulture("es-es");
var inputCulture = CultureInfo.CreateSpecificCulture("en-us");
Thread.CurrentThread.CurrentCulture = outputCulture;
Thread.CurrentThread.CurrentUICulture = outputCulture;
DateTime formattedDate = DateTime.Parse("5/9/2014", inputCulture);
Console.WriteLine(formattedDate.ToShortDateString()); //this returns 09/05/2014
Console.WriteLine(DateTime.Today.ToShortDateString()); //this returns 09/05/2014
As you see I am specifying the culture for input so that it knows to use the en-us culture rather than the explicitly set es-es culture for parsing.
Since you are parsing a string from a database, the only way to do this correctly is to persist the string in a standard format that does not depend on any culture specific data. ISO 8601 defines formats appropriate for this and you can use a custom format string to achieve this. You can also use .Net's 'o' format specifier for round trip. See How to: Round-trip Date and Time Values for more information.
Culture specific settings do change and cause items that used to parse, to no longer be able to parse even if you know the culture that was used to format the value with to start with.
Culture specific formatting and parsing is meant to be ephemeral and to be used to interact with the user only.
Related
I was trying to validate a date read from app.config file using DateTime.TryParse() method. However, it returned true when the input was "12/05/201". This was actually a typo, and should have been, "12/05/2018". When I stepped through the code it automatically converted the date to "12/05/0201" and returned true. However when I used DateTime.TryParseExact(), it correctly returned false for the above input. So, should we always use DateTime.TryParseExact()? I am little confused because earlier I used use DateTime.TryParse() whenever I had to validate a date string! Both the code is given below:
Boolean isValidStartDate = DateTime.TryParse(startDate, out DateTime startDateVerified);
CultureInfo enUS = new CultureInfo("en-US");
Boolean isValidStartDate = DateTime.TryParseExact(startDate,"MM/dd/yyyy",enUS, DateTimeStyles.None, out DateTime startDateVerified);
Thanks
The year 201 being invalid is business logic - if you want to have logical safeguards on your imported data (and you should), do them explicitly. With C# you can easily add an extension method to DateTime if you want, something like
public static DateTime ParseDateWithSanity(this DateTime, string date)
{
dt = DateTime.Parse(date);
if dt.Year < 1900
{
throw BadInputException()
}
}
Best way to Validate date depends upon the use case and input data source and its formate
DateTime.TryParse is parsed using formatting information in the current DateTimeFormatInfo object so let's say if you use TryParse "12/05/201" it will return the parsed data according to your current culture settings. Which is "12/05/0201" ie in date format "MM/DD/YYYY"
Its always good practice to specify date formate and culture variance while parsing date and use TryParseExact instead of TryParse
(Note: To know about current culture settings you can look for a member of classes CultureInfo.DefaultThreadCurrentCulture and CultureInfo.DefaultThreadCurrentUICulture)
I don't know format of the DateTime because web page has more than one language and more than one Datetime format are available. I had to change poland dateformat in datepicker UI to d.m.yyyy although ISO standard is YYYY-mm-dd. .NET uses ISO standard but whenever I try to use Convert.ToDateTime(stringDate) I get exception String is not recognized as valid DateTime . I tried using Convert.ToDateTime(stringDate,CultureInfo.InvariantCulture) but it does not help. How to successfully convert to DateTime?
Example:
item.arrival is string which can be in more than 10 formats. Convert.ToDateTime works for them all.
BUT:
item.arrival dateformat for poland language is not anymore yyyy-mm-dd (because I had requirement to change it) so Convert.ToDateTime not works anymore only for poland language. New format is 13.06.2015. Because of that, because format is not ISO standard for poland language .NET fails to do conversion. I hope it is more clear now.
ArrivalDate = Convert.ToDateTime(item.arrival, CultureInfo.InvariantCulture),
I had to change poland dateformat in datepicker UI to d.m.yyyy
...
Example: item.arrival is string which can be in more than 10 formats. Convert.ToDateTime works for them all. BUT: item.arrival dateformat for poland language is not anymore yyyy-mm-dd (because I had requirement to change it)
Based upon what you say, your date time conversion works for all but Polish, and you (in your code) know when it is Polish. So, handle Polish special using the format you have been required to use, and handle the other nine normally.
This is what I did. I put it inside some PageBase class so it should more general and less error prone solution than some "if-poland-date" statements around the project.
CultureInfo newCulture = (CultureInfo)System.Threading.Thread.CurrentThread.CurrentCulture;
if (newCulture.TwoLetterISOLanguageName == "pl")
{
DateTimeFormatInfo myDTFI = new CultureInfo("hr-HR", false).DateTimeFormat;
newCulture.DateTimeFormat = myDTFI;
CultureInfo ci = new CultureInfo("pl-PL");
DateTimeFormatInfo dateformat = new DateTimeFormatInfo();
ci.DateTimeFormat = myDTFI;
CultureAndRegionInfoBuilder obj = new CultureAndRegionInfoBuilder("pl-PL", CultureAndRegionModifiers.Replacement);
obj.LoadDataFromCultureInfo(ci);
CultureAndRegionInfoBuilder.Unregister("pl-PL");
obj.Register();
}
i am trying to format the date to this format. 01/20/2013 02:30PM EDT, using this
LastModified.ToString("MM/dd/yyyy HH:mmtt");
but the result is coming like this
01-20-2013 02:30PM dont know why it is showing '-' instead '/'.
Also, for timezome, it seems there is only format available like +02:00. But I want timezone as string, i could not find any format for this, how can I get is as string like EDT/PST/IST etc?
From the MSDN page on custom date and time format strings:
The "/" custom format specifier represents the date separator, which is used to differentiate years, months, and days. The appropriate localized date separator is retrieved from the DateTimeFormatInfoDateSeparator property of the current or specified culture.
If you want it to definitely use /, you should either use the invariant culture, or quote the slash ("MM'/'dd'/'yyyy hh':'mmtt"). Note that I've quoted the time separator as well, as that can vary by culture too. I've also changed it to use the 12 hour clock, as per Arshad's answer.
When using a custom date/time format, you should probably use the invariant culture anyway. (For example, it seems odd to use a UK culture to format the string in a US-centric way - today would normally be represented as 02/05/2013 in the UK, not 05/02/2013.)
In terms of a time zone specifier - I don't know any way to use the time zone abbrevation within date/time formatting. I would personally advise against using abbreviations anyway, as they can be ambiguous and confusing. I can't see anything within TimeZoneInfo which even exposes that information for you to manually add it.
(It's possible that in Noda Time we'll support formatting with the abbreviation, but probably not parsing, precisely because of the ambiguity.)
i have found one mistake is that ,HH means time in 24 HRS format. You can try
string date = "01/20/2013 02:30PM";
DateTime dtTime;
if (DateTime.TryParseExact(date, "MM/dd/yyyy hh:mmtt",
System.Globalization.CultureInfo.InvariantCulture,
System.Globalization.DateTimeStyles.None, out dtTime))
{
Console.WriteLine(dtTime);
}
Formatting of DateTime is influenced by your Culture and your format string. Your current culture (Thread.CurrentThread.CurrentCulture) uses the - as the default seperator of your date components.
The Culture of India uses - and since you are from Inda, it would make sence (see: http://en.wikipedia.org/wiki/Date_and_time_notation_in_India)
Two options:
Choose a correct Culture which uses the / by default as seperator. For example: LastModified.ToString("MM/dd/yyyy HH:mmtt", new CultureInfo("en-US"));
Or reformat your format string with the escape character \. For example: For example: LastModified.ToString("MM\/dd\/yyyy HH:mmtt");
See also: http://msdn.microsoft.com/en-us/library/8kb3ddd4.aspx
I'm trying to format the date and time on my app based on the users set culture info, however, every help resource I see keeps suggesting that I have to manually enter each culture locale in code. For example, if I wanted en-UK I would have to manually add new CultureInfo("en-UK"); with something like new CultureInfo("en-UK");.
Is there a way to just tap into the currently set culture on the phone without me having to actually type the rtc culture info in? Something that might work like "date = ConvertToLocalCultureFormat(date);"?
I don't know if this works on WinPhone7, but you could use
CultureInfo.CurrentCulture.Name
which return the name of the CurrentCulture of the current thread (en-UK or whatever your app runs in)
See for refs
However this should not be necessary.
If you convert your datetime to a string in this way:
DateTime dt = DateTime.Now;
// Converts dt, formatted using the ShortDatePattern
// and the CurrentThread.CurrentCulture.
string dateInString = dt.ToString("d");
you should get the conversion in the right CultureInfo of your phone.
To format anything using the current culture, you don't have to do anything special at all. The overloads of all formatting that doesn't include a specific format or culture uses the default culture.
The Date.ToString() method for example will call the overload with this.ToString(CultureInfo.CurrentCulture) to pick up the current culture setting of the application and use for the formatting.
Which help resources did you read that suggest you have to manually specify the current culture?
The DateTime.ToString() parameterless method automatically uses formatting information derived from the current culture.
This method uses formatting information derived from the current culture. In particular, it combines the custom format strings returned by the ShortDatePattern and LongTimePattern properties of the DateTimeFormatInfo object returned by the Thread.CurrentThread.CurrentCulture.DateTimeFormat property.
DateTime exampleDate = new DateTime(2008, 5, 1, 18, 32, 6);
string s = exampleDate.ToString();
// Gives "5/1/2008 6:32:06 PM" when the current culture is en-US.
// Gives "01/05/2008 18:32:06" when the current culture is fr-FR.
// Gives "2008/05/01 18:32:06" when the current culture is ja-JP.
I want to display some rows of data on a web page where one column is a DateTime.
I want the date format to be displayed based on the current thread culture.
Right now, I'm doing this (dt is a DateTime):
string s = dt.ToString(Thread.CurrentThread.CurrentCulture.DateTimeFormat);
It's working well, however, on some culture, months and days are represented as only one digit (hours too), for example:
8/8/2011 8:57:59 AM
I would like the date to be displayed like this:
08/08/2011 08:57:59 AM
It would be easier to read (and prettier) when there's a list of rows.
I saw that there's a String.format method I could use, but that makes the current culture irrelevant.
Is there a way to achieve what I'm trying to do?
The solution provided here might be useful.
I see only a single solution - you should obtain the current culture display format, patch it so that it meets your requirement and finally format your DateTime value using the patched format string.
Make a custom culture.
Base it on the current thread culture.
Modify the settings you want to override.
Then either set it back into the thread as the culture or use it temporarily during the format operation.
We currently do this to format all dates in an internationally unambiguous form ddMMMyyyy where MMM is only English three-letter abbreviations, yet obey local numeric formatting rules ./, etc.
The relevant properties to override would be here.
If you want to show it based on the current culture, then what is the problem? If you want a specific format, you have to specify that.
string text = myDateTime.ToString("{0:[your format]}");
I believe this defaults to the server format - but what if you try specifying "u" as the format code which will put the year first then I think two digits.
You can use
String.Format("{0:dd/MM/yyyy hh:MM PM ", yourDatetime)
The date separator / (slash) and time sepatator : (colon) will be rewritten to characters defined in the current DateTimeFormatInfo.DateSeparator and DateTimeFormatInfo.TimeSeparator.
EDIT: Forgot to add object param needed to the string.format
using System.Globalization;
private static CultureInfo defaultCulture = new CultureInfo("nl-NL");
public static CultureInfo GetCurrentCulture()
{
List<CultureInfo> badCultures = new List<CultureInfo>();
badCultures.Add(new CultureInfo("en-US"));
if (badCultures.Contains(System.Threading.Thread.CurrentThread.CurrentCulture))
return defaultCulture;
return System.Threading.Thread.CurrentThread.CurrentCulture;
}