Formatting string dates with String.Format() - c#

Just curious...
So I get that if I convert the string version of the date to a DateTime object and pass it into the String.Format() method, then I"ll get the desired results.
String.Format("The date is {0:MMMM dd, yyyy}", DateTime.Parse("05-22-2012"));
"The date is May 22, 2012"
But why doesn't this work?
String.Format("The date is {0:MMMM dd, yyyy}", "05-22-2012")
"The date is 05-22-2012"
Sorry if this is a stupid question, but I'm just trying to understand how this works.
Thanks

The other answers here hit on salient points, but let's put them all together an examine how String.Format works.
It has five overloads, but we're going to talk only about the one that they all redirect to (this is not the actual code, if you want to see it with Reflector or ILSpy, you will find it in StringBuilder.AppendFormat). This is simplified for easy understanding.
public static string Format(IFormatProvider provider, string format, params object[] args)
{
StringBuilder sb = new StringBuilder();
// Break up the format string into an array of tokens
Token[] tokens = ParseFormatString(format);
foreach (Token token in tokens)
{
switch (token.TokenType)
{
// A Text token is just some text to output directly
case TokenType.Text:
sb.Append(token.Text);
break;
// An Index token represents something like {0} or {2:format}
// token.Index is the argument index
// token.FormatText is the format string inside ('' in the first example, 'format' in the second example)
case TokenType.Index:
{
object arg = args[token.Index];
IFormattable formattable = arg as IFormattable;
if (formattable != null && token.FormatText.Length > 0)
{
// If the argument is IFormattable we pass it the format string specified with the index
sb.Append(formattable.ToString(token.FormatText, provider));
}
else
{
// Otherwise we just use Object.ToString
sb.Append(arg.ToString());
}
}
break;
}
}
return sb.ToString();
}
In your question you ask why the format string doesn't get applied when you pass "05-22-2012". As Guffa said, that is not a DateTime object, it is a String object.
As GSerjo said, a String is not IFormattable. Strings are not formattable because formatting is the process of converting something into a String. A string is already a string!
So you can see that when the Format method gets to indexer, arg will not be IFormattable and it will simply call ToString. Calling ToString on a string simply returns itself, it's already a string.
In summary, if your format string contains an index with an inner-format string (e.g. {0:format}), that inner-format string will only be applied if the associated argument is IFormattable and it knows what to do with the format string you give it.

A custom datetime format only works on a DateTime value. If you are using a string instead, the format will be ignored because there is only one way to output a string.

Because "05-22-2012" is not IFormattable, DateTime.Parse("05-22-2012") it's DateTime
please look here for more examples
String Format for DateTime
Custom Date and Time Format Strings

Related

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.

Convert DateTime to string using another string as a template

I have a string that contains a date & time in some format. For example:
13:53:56 20.08.2014
This string is parsed from a file that user uploads to my service. The exact date & time format is not known. It can change to pretty much any date & time format you know. I do not have a list a expected formats or anything like that.
I want to develop an algorithm that somehow extracts the format from the initial string and applies it to, say, DateTime.Now.
Is there any way to do this simply and elegantly?
If you know the list of expected formats, then define them and use DateTime.TryParseExact() to find out the matching format of your input string. Once you have the matching format, you can simply use the matching format with DateTime.Now.ToString(format).
If this is a web application, perhaps you can "cheat a bit" by sniffing the user's regional settings before they input the string.
The MSDN article "How to: Display Localized Date and Time Information to Web Users" has useful information.
Inspired by a good suggestion by Mani, I've created the following extension method for my needs:
public static bool TryFormatLike(this DateTime dateTime, string another, out string result)
{
var allCultures = CultureInfo.GetCultures(CultureTypes.AllCultures | CultureTypes.UserCustomCulture | CultureTypes.ReplacementCultures);
foreach (var culture in allCultures)
{
var allPatterns = culture.DateTimeFormat.GetAllDateTimePatterns();
foreach (var pattern in allPatterns)
{
DateTime parsedAnother;
if (DateTime.TryParseExact(another, pattern, culture.DateTimeFormat, DateTimeStyles.AllowWhiteSpaces, out parsedAnother))
{
result = dateTime.ToString(pattern);
return true;
}
}
}
result = string.Empty;
return false;
}
You can use it like that:
string formattedNow;
if (DateTime.Now.TryFormatLike("13.02.2015 16:14:43", out formattedNow))
{
Console.WriteLine(formattedNow);
}
This will print
10.03.2015 23:37:08
Unfortunately, some date & time formats cannot be parsed by all patters in all cultures (for instance, string 16:14:43 13.02.2015 will not be parsed).
Anyway, thank you for your comments & answers. Maybe this method will be helpful to someone.

Format placeholder in string.format to pick substring of an argument

Is there a format string for the C# string.Format method that picks a substring from the corresponding argument? Like so:
var lang1 = "EN";
var lang2 = "FR";
var shortFormat = "Foo-{0:0-0}.pdf";
var longFormat = "Foo-{0:0-1}.pdf";
string.Format(shortFormat, lang1) // Foo-E.pdf
string.Format(shortFormat, lang2) // Foo-F.pdf
string.Format(longFormat, lang1) // Foo-EN.pdf
string.Format(longFormat, lang2) // Foo-FR.pdf
To anticipate a few comments: Yes, I know the Substring method. I have also read that string.Format is slower than a simple Substring. The example above is heavily simplified. Imagine that the string.Format statement resides in one place, while the lang1/lang2 argument is an input from another place and the shortFormat/longFormat is defined in a resx file.
That is, in the place where the format is to be defined we don't know anything about the value being formatted (lang1/lang2 in the example) nor do we have any means to execute C# code. Hence we can't call any method such as Substring on the value. At the place where the formatting code runs, in turn, we take the format as a parameter, so we can't simply perform a Substring on the value because we don't know whether the format requires it or not (except if we inspect the format).
No, the string.Format does not have this feature, which is better explained here: Can maximum number of characters be defined in C# format strings like in C printf?
If you don't want to use Substring I would create an extension class for string like this: http://msdn.microsoft.com/en-us/library/bb311042.aspx
namespace CustomExtensions
{
public static class StringExtension
{
public static string ShortFormat(this string str)
{
// manipulate and return str here
}
public static string LongFormat(this string str)
{
// manipulate and return str here
}
}
}
XSLT formatting can be an option: user gets ability to provide almost everything in configuration file and even execute custom c# code in your domain if it is required.
Please also consider that changes of format can be restricted to relatively small amount of actions: crop, pad or insert one or two things in some positions. Each one can be set as individual function and provided with own parameters.
There are two ways to provide custom formatting. You can either implement IFormattable on a custom type to control how that type is always formatted, or implement IFormatProvider to override how other types are formatted in specific cases.
In your case I would suggest creating a new type to encapsulate how your software deals with language codes;
public struct LanguageCode : IFormattable {
public readonly string Code;
public LanguageCode(string code) {
Code = code;
}
public override string ToString()
=> this.ToString("L", CultureInfo.CurrentCulture);
public string ToString(string format)
=> this.ToString(format, CultureInfo.CurrentCulture);
public string ToString(string format, IFormatProvider provider){
if (String.IsNullOrEmpty(format))
format = "L";
if (provider == null)
provider = CultureInfo.CurrentCulture;
switch (format.ToUpperInvariant()){
case "L": // Long
return Code.ToString(provider);
case "S": // Short
return Code.SubString(0,1).ToString(provider);
default:
throw new FormatException($"The {format} format string is not supported.");
}
}
public static implicit operator LanguageCode(string code)
=> new LanguageCode(code);
public static implicit operator string(LanguageCode language)
=> language.Code;
}
Then from your example;
var lang1 = (LanguageCode)"EN";
LanguageCode lang2 = "FR";
var shortFormat = "Foo-{0:S}.pdf";
var longFormat = "Foo-{0:L}.pdf";

replace a JSON date in a string to a more readable date

We want to show some JSON to a user who is testing our application. So we call our REST service in the ASP.NET code behind file and return a string, which holds a lot of JSON.
We then put it in a PRE element in the page, call beautify to create nice readable JSON and all is good: sort of human readable content is shown.
Good but for one thing: all the dates are shown in the normal JSON format like this "/Date(1319266795390+0800)/"
What I want to do is replace those JSON dates with 'normal' dates, in the JSON (C#) string, so in the code behind that is, before I add the string to the PRE element.
I was thinking about some regex, but i couldn't figure out how...
I'v been dealing with dates in JSON string for some time now, there's no standard way for that and which is why there are so many different ways to do it! Maybe it was better if JSON specification could specify an standard format for dates in the first place!
Microsoft is doing it in its own way, counting the msecs since 1970 in UTC format this is something like "/Date(1319266795390+0800)/"
We've been changing the above string to ISO-8601 format ever since using Regular Expressions on top of ASP.Net JavaScriptSerializer output. It is a W3C standard, human readable and the way most browsers serialize Date to string, here's how:
static readonly long DATE1970_TICKS = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).Ticks;
static readonly Regex DATE_SERIALIZATION_REGEX = new Regex(#"\\/Date\((?<ticks>-?\d+)\)\\/", RegexOptions.Compiled);
static string ISO8601Serialization(string input)
{
return DATE_SERIALIZATION_REGEX.Replace(input, match =>
{
var ticks = long.Parse(match.Groups["ticks"].Value) * 10000;
return new DateTime(ticks + DATE1970_TICKS).ToLocalTime().ToString("yyyy-MM-ddTHH:mm:ss.fff");
});
}
You can easily change the format to satisfy your needs, to see custom Date and Time formats check out MSDN article here
Here's how it's used:
JavaScriptSerializer ser = new JavaScriptSerializer();
var JsonSrt = ISO8601Serialization(ser.Serialize(DateTime.Now)); // "\"2012-05-09T14:51:38.333\""
Update:
There's an alternative to tweak the JSON string returned from the server in JavaScript to more readable form using Regex:
var str = "/Date(1319266795390+0800)/";
str.replace(/\/Date\((\d+)\+\d+\)\//, function (str, date) {
return new Date(Number(date)).toString();
});
The solution is within the string shown in the question. The JavaScript Date object will parse that format and produce a readable version so Date(1319266795390+0800) returns "Wed Apr 18 2012 08:13:22 GMT-0500 (Central Daylight Time)".
To remove the forward slash from the string you could use the replace function with a regular expression: "/Date(1319266795390+0800)/".replace(/\//g, '').
You can use this:
string date = "/Date(1319266795390+0800)/";
string regex = #"/Date\((.*?)\+(.*?)\)/";
Match match = Regex.Match(date, regex);
DateTime d = new DateTime(1970, 01, 01).AddMilliseconds(long.Parse(match.Result("$1")));
suppose the class you want to serialize looks like this:
public class Something
{
public int ID;
public string Name;
public DateTime Date;
}
change it to:
public class Something
{
public int ID;
public string Name;
public DateTime Date;
public string HumanReadableDate { get { return Date.ToLongDateString(); } }
}
or, if you want that extra property to display only in test enviroment:
public class Something
{
public int ID;
public string Name;
public DateTime Date;
#if DEBUG
public string HumanReadableDate { get { return Date.ToLongDateString(); } }
#endif
}
also, instead of .ToLongDateString() you can use .ToString("yyyy-MM-dd HH:mm") or any other format
Use as regex something like:
(?<= /Date\( )
(?<ticks>[0-9]+)
((?<zonesign>[+-])
(?<zonehour>[0-9]{2})
(?<zoneminutes>[0-9]{2})
)?
(?= \)/ )
This will match the part inside the parentheses of /Date(1319266795390+0800)/. You can then call Regex.Replace on the whole JSON string to replace the numbers with a nicely formatted DateTime:
Use the Match object you get in the match evaluator delegate and extract the ticks, zonesign, zonehour and zoneminutes part, convert it to integers.
Then convert the javascript ticks to .NET ticks (should be *10000), construct the .NET DateTime out of ticks and add/substract the hours and minutes for the time zone.
Convert the DateTime to a string and return it as the replacement.
If your JSON is a serialised representation of a .NET class, maybe you could use the DataContractJsonSerializer to deserialise it on the server, or perhaps you could just define a stub class for your JSON object if you don't need a generic solution to handle multiple datasets:
string json = "{\"Test\": \"This is the content\"}";
DataContractJsonSerializer ds = new DataContractJsonSerializer(typeof(TestJson));
var deserialisedContent = ds.ReadObject(new MemoryStream(Encoding.ASCII.GetBytes(json)));
foreach (var field in typeof (TestJson).GetFields())
{
Console.WriteLine("{0}:{1}", field.Name, field.GetValue(deserialisedContent));
}
...
[DataContract]
private class TestJson
{
[DataMember]
public string Test;
}
Use Newtonsoft.JSON. You can provide your own serializers per type, and serialize dates however you want.
http://james.newtonking.com/projects/json-net.aspx
Make a string property for example dateofbirth I am defining here, and return your datetime variable as:
public string DateOfBirthString
{
get { return DateOfBirth.ToUniversalTime().ToString("yyyy-MM-dd HH:mm:ss"); }
set { DateOfBirth = string.IsNullOrEmpty(value) ? new DateTime(1900, 1, 1) : Convert.ToDateTime(value); }
}
because this will return string so it will be same at client side so and aslo take string dateTime from user and convert it.
string input = [yourjsonstring];
MatchEvaluator me = new MatchEvaluator(MTListServicePage.MatchDate);
string json = Regex.Replace(input, "\\\\/\\Date[(](-?\\d+)[)]\\\\/", me, RegexOptions.None)

Easy way to check FormatString is valid?

Is there an easy way to check if a format string is valid? For example the following is code that we use to test a number format string;
public static bool IsValidFormatStringNumber(string FormatString)
{
try
{
const decimal number = 0.056m;
var formattedNumber = number.ToString(FormatString);
return formattedNumber.Length > 0;
}
catch
{
return false;
}
}
We're trying to catch an exception or determine if the resulting string has no length. This test fails however as a format string of "hsibbur" (Any rubbish) results in a string of "hsaibbur", which has length.
We want to do the same test for Percent and Date format string.
If you just want to check for standard format strings, just check that your format strings are part of that list.
If you want to check for custom format strings (that are not "Other" or "Literal strings"), you can probably craft a regex to do it.
Other than that, since format strings can be arbitrary strings, I don't think validation even applies.
If FormatString is equal to formattedNumber, that could be another case for returning false.

Categories

Resources