I have been recently performing code reviews on quite a large system which has users around the world.
I noticed that some developers are displaying dates using the following code:
userDOB.ToString("d")
and others are using this:
userDOB.ToShortDateString()
Is there any difference between what these 2 lines should output?
If not then i am going to push for having some consistency and using one of these formats, personally i prefer ToShortDateString() as it is more readable (unless someone has a better reason to use ToString("d")).
If you see both methods
public string ToShortDateString()
{
return DateTimeFormat.Format(this, "d", DateTimeFormatInfo.CurrentInfo);
}
and
public string ToString(string format)
{
return DateTimeFormat.Format(this, format, DateTimeFormatInfo.CurrentInfo);
}
you can see the difference for your self that in ToString you have to define format in your case "d" and in ToShortDateString the format is predefined but they both are pointing to the same method DateTimeFormat.Format
Related
Having created an own number type (actually DoubleDouble), I want to implement the IFormattable interface. So I have to somehow parse the format string.
public string ToString(string format, IFormatProvider formatProvider) {
// formatting string according to format and using formatprovider?
return formattedString;
}
The user of the class should be able to use it as a replacement for double (or any other number format).
String.Format("{0:0.##}", (DoubleDouble)123.4567);
My question is, does someone know a good tutorial about this or can give me some hints?
How to support localizing in this process?
How to parse the format string? Are there some methods to aid in this task or do I have to do it all by "hand" with regexp and such?
I really searched for help but couldn't find any, if you find something in another language (C,C++) that may help, please tell me about it.
MSDN has a nice example of a Temperature class that implements the IFormattable interface with its own custom format.
I think you know this already; anyway, today I learned that if your DoubleDouble class implemented the IFormattable interface then:
String.Format("{0:0.##}", (DoubleDouble)123.4567);
... would call the DoubleDouble class's ToString(...) implementation with the specific format "0.##" as the first parameter, which I suspect is close to what you want. You still have to parse that part of the format though.
I would hazard a guess that much of the format parsing is embedded deep in the highly optimised .Net library binaries, so you don't get any custom parsing virtual methods to help.
Maybe this can help you:
var my = DoFormat(123.0)
public static string DoFormat( double myNumber )
{
var s = string.Format("{0:0.00}", myNumber);
if ( s.EndsWith("00") )
{
return ((int)myNumber).ToString();
}
else
{
return s;
}
}
How to support localizing in this process?
Something like the following:
public string ToString(string format, IFormatProvider formatProvider)
{
CultureInfo culture = formatProvider as CultureInfo;
if (culture != null)
{
// Now you can do things like
// culture.NumberFormat.NumberDecimalSeparator, etc.
}
}
How to parse the format string? Are there some methods to aid in this
task or do I have to do it all by "hand" with regexp and such?
There's no public API in the .NET framework to do it for you. If you look at the source for the .NET primitive numeric types' implementation of IFormattable, they all eventually call external methods, so they provide no insight. You'll probably need to use something like RegEx's to parse the format string. Then you can divide by the largest power of 10 to figure out what the first digit of output will be, subtract that out, then repeat for the lesser power's of 10 until you have generated all the digits.
I had written a JavaScript number formatting function a few years ago that mimicked most of the .NET way of parsing format strings. It may be of some help for you in writing a C# version of it. At the very least it can give you a starting point.
https://github.com/flamewave/jquery-ui-numeric/blob/master/jquery-ui-numeric.js
Scroll all the way down to the bottom to the $.formatNumber function.
Note: I no longer maintain this function in favor of using the Globalize library, which also may give you some insight into parsing format strings.
I'm currently doing an app, that needs to be able to work with the US number layout (123,456.78) as well as with the German layout (123.456,78).
Now my approach is to use NumberFormatInfo.InvariantInfo about like this:
temp = temp.ToString(NumberFormatInfo.InvariantInfo);
this works great when for example reading a number from a textbox. When System is set to English format it will take the . as separator, when it's set to German it will use the ,.
So far so good....but here's the problem: I have a device that returns info in the American format, and that won't change (transmitted via RS232). So I receive something like 10.543355E-00.
Now when on German setting the . will be discarded since it's just the group separator
and the number I will end up with is 10543355....which is a lot more :)
I tried with the same technique thinking this would make the whole thing kind of 'cultureless' to be able to process it independently from the system language but it didn't work :)
I hope you can maybe help me here...I'd love to use a way without having to implement the whole culture stuff etc since all I need here is really numbers that get calculated the right way.
You should use CultureInfo.InvariantCulture when parsing strings from the device. This will cause it to use the invariant culture, which has the US rules for decimal separation.
Edit in response to comments:
The issue is not when you call .ToString(), but rather when you read the string from the device, and convert it to a number:
string inputFromRS232Device = GetDeviceInput();
double value;
// You need this when converting to the double - not when calling ToString()
bool success = double.TryParse(
inputFromRS232Device,
NumberStyles.Float,
CultureInfo.InvariantCulture,
out value);
I'm writing code with german culture settings
Nevertheless I would like to force the user to use the point as a decimal separator.
My piece of test code outputs the wrong value.
How do I detect the "wrong" comma ?(throw an exception)
string nok_str = "14,9";
string ok_str = "14.9";
double nok_Var1 = double.Parse(nok_str, CultureInfo.InvariantCulture.NumberFormat); // outputs 149.0
double nok_Var2 =Convert.ToDouble(nok_str, CultureInfo.InvariantCulture.NumberFormat); // outputs 149.0
First off, and please forgive me, Iād like to question your design decision:
How is this enhancing the user experience? The application should rather try to accept all unambiguous user input, not reject theoretically sound input.
That said, a number such as ā19,2ā will be interpreted, with an invariant culture, as having a thousands separator (which is simply discarded). This is why your code silently produces bad values. If you want to explicitly forbid this input, the easiest way to achieve this is an explicit test:
if (nok_str.Contains(","))
throw new FormatException(ā¦);
As an alternative, you can try modifying the NumberFormatInfo.NumberGroupSeparator property of a custom NumberFormatInfo object that you pass to the Parse method.
Basically the default is to include AllowThousands in the number style. If you specify the number style you want, you can prohibit this:
using System;
using System.Globalization;
class Test
{
static void Main(string[] args)
{
string text = "19,2";
double value;
bool valid = double.TryParse(text, NumberStyles.Float,
CultureInfo.InvariantCulture,
out value);
Console.WriteLine(valid); // Prints false
}
}
Note that NumberStyles.Float is a composite style for AllowLeadingWhite, AllowTrailingWhite, AllowLeadingSign, AllowDecimalPoint, and AllowExponent - but not AllowThousands.
I am not sure what is the source of your input.
If it comes from user it also depends... If it is GUI application, you may think of restricting the input to certain possible keys, excluding comma. If it is a console app, you can try regular expressions to pre-validate input strings.
If it comes from various sources (i.e. web service) maybe simply brute-force string replace will do the trick?
Last, but not least: there are reasons for parsing to be culture-sensitive and if I were you, I would encourage users to enter valid regional number format instead forcing them to provide incorrect one.
I am a little confused here.
What should I use
Console.WriteLine((val/1085).ToString("N"));
VS
Console.WriteLine(String.Format("{0:N}", (val/1085)));
Also how do I fit the InvariantCulture? ANY BEST PRACTICES :)?
Actually I prefer a third form:
Console.WriteLine("{0:N}", val / 1085);
Console.WriteLine can do the String.Format for you.
Console.WriteLine does not allow you to supply a culture. If that is what you want, you will still have to use String.Format. As in:
String.Format(CultureInfo.InvariantCulture, "{0:N}", 123456789);
I do not recommend using that because international users will have trouble reading that. To me 123,456,789.00 looks strange.
For formatting + culture I prefer:
.ToString("####0.00",CultureInfo.InvariantCulture)
or
.ToString("N",CultureInfo.InvariantCulture)
I found an invariant and generic way to solve that as follows:
Syntax:
.ToStringInvariant(format)
.ToStringInvariant()
Technically, it is a generic extension method, defined as follows:
public static class Extensions
{
private static IFormatProvider inv
= System.Globalization.CultureInfo.InvariantCulture.NumberFormat;
public static string ToStringInvariant<T>(this T obj, string format=null)
{
return (format == null) ? System.FormattableString.Invariant($"{obj}")
: String.Format(inv, $"{{0:{format}}}", obj);
}
}
Usage is simple, just use .ToStringInvariant() instead of .ToString(). The advantage is that it works for any data type.
Optionally, you can pass a format too, like for example .ToStringInvariant("N"), just as you are used to to it with .ToString("N").
Note that in that case the extension method uses String.Format internally.
You can see the difference if you have a different culture for the number format, like in Germany we have comma instead of a decimal point. That means on a PC with German culture settings:
void Main()
{
var val = 2456.5;
Console.WriteLine((val/1085).ToString("N"));
Console.WriteLine((val/1085).ToStringInvariant("N"));
Console.WriteLine((val/1085).ToStringInvariant("0.000"));
Console.WriteLine((val/1085).ToStringInvariant());
}
it would output:
2,26
2.26
2.264
2.26405529953917
which is correct, because .ToString uses the current culture (German settings), and .ToStringInvariant always uses the invariant culture, which is the English number format regardless of the Windows settings.
Note: For date formatting, I have provided a different extension method, which you can find here.
More information:
FormattableString.Invariant(FormattableString) Method
In a datetime it's okay to use both. I rather like to use and see the first solution (ofcourse with missing parenthesis).
The String.Format is much more usefull when you have some string with a gaps for some kind of parameters. Then it's a killer method, which really nicely help you to organize your code.
When doing a string comparison in C#, what is the difference between doing a
string test = "testvalue";
test.Equals("TESTVALUE", StringComparison.CurrentCultureIgnoreCase);
and
string test = "testvalue";
test.Equals("TESTVALUE", StringComparison.InvariantCultureIgnoreCase);
... and is it important to include that extra parameter, anyway?
The other posts have given good advice, but I thought it might be nice to show an example of where it definitely makes a difference:
using System;
using System.Globalization;
using System.Threading;
class Test
{
static void Main()
{
CultureInfo turkish = CultureInfo.CreateSpecificCulture("tr");
Thread.CurrentThread.CurrentCulture = turkish;
// In Turkey, "i" does odd things
string lower = "i";
string upper = "I";
// Prints False
Console.WriteLine(lower.Equals(upper,
StringComparison.CurrentCultureIgnoreCase));
// Prints True
Console.WriteLine(lower.Equals(upper,
StringComparison.InvariantCultureIgnoreCase));
}
}
(There are no doubt many other cases - this was just the first one I thought of.)
Microsoft gives some decent guidance for when to use the InvariantCulture property:
MSDN: CultureInfo.InvariantCulture Property
... an application should use the
invariant culture only for processes
that require culture-independent
results, such as formatting and
parsing data that is persisted to a
file. In other cases, it produces
results that might be linguistically
incorrect or culturally inappropriate.
Security Considerations
If a security decision will be made
based on the result of a string
comparison or case change, your
application should use an ordinal
comparison that ignores case instead
of using InvariantCulture. [...]
String Operations
If your application needs to perform a
culture-sensitive string operation
that is not affected by the value of
CurrentCulture, it should use a method
that accepts a CultureInfo parameter.
[...]
Persisting Data
The InvariantCulture property is
useful for storing data that will not
be displayed directly to users.
Storing data in a culture-independent
format guarantees a known format that
does not change. When users from
different cultures access the data, it
can be formatted appropriately based
on specific user. [...]