Strange TimeSpan.ToString output - c#

I have a question regarding the symbol that separates days from hours in TimeSpan.ToString output.
The standard TimeSpan format strings produce different separator symbols:
"c" produces a period (".") character
"g" and "G" produce a colon (":") character
Example:
// Constant format
Console.WriteLine(TimeSpan.FromDays(42).ToString("c", CultureInfo.InvariantCulture));
// Output: 42.00:00:00 (period character between days and hours)
// General short format
Console.WriteLine(TimeSpan.FromDays(42).ToString("g", CultureInfo.InvariantCulture));
// Output: 42:0:00:00 (colon character between days and hours)
// General long format
Console.WriteLine(TimeSpan.FromDays(42).ToString("G", CultureInfo.InvariantCulture));
// Output: 42:00:00:00.0000000 (colon character between days and hours)
Does anybody know what's the logic behind it?
However TimeSpan.Parse parses all of these string successfully.

These characters are hardcoded for those formats.
For "c" standard format
[-][d.]hh:mm:ss[.fffffff]
For "g" standard format
[-][d:]h:mm:ss[.FFFFFFF]
And for "G" Format Specifier
[-]d:hh:mm:ss.fffffff
Also doc says;
Unlike the "g" and "G" format specifiers, the "c" format specifier is
not culture-sensitive. It produces the string representation of a
TimeSpan value that is invariant and that is common to all previous
versions of the .NET Framework before the .NET Framework 4. "c" is the
default TimeSpan format string; the TimeSpan.ToString() method formats
a time interval value by using the "c" format string.
Also in Custom TimeSpan Format Strings
The .NET Framework does not define a grammar for separators in time
intervals. This means that the separators between days and hours,
hours and minutes, minutes and seconds, and seconds and fractions of a
second must all be treated as character literals in a format string.
Sounds like the most important reason is consistency between all .NET Framework versions. Maybe that's why they call this format as constant :)

There is more detail on MSDN - Standard TimeSpan Format Strings.
Essentially:
"c" is the Constant format: This specifier is not culture-sensitive. Format is [d’.’]hh’:’mm’:’ss[‘.’fffffff]
"g" is the General Short format: This is culture sensitive. Format is [-][d’:’]h’:’mm’:’ss[.FFFFFFF]
"G" is the General Long format: This is culture sensitive. Format is [-]d’:’hh’:’mm’:’ss.fffffff.

Look at the MSDN
The "g" TimeSpan format specifier returns the string representation of
a TimeSpan value in a compact form by including only the elements that
are necessary.
[-][d:]h:mm:ss[.FFFFFFF]
.....................
The "c" format specifier returns the string representation of a
TimeSpan value in the following form:
[-][d.]hh:mm:ss[.fffffff]

Related

What does %(percent symbol) mean in DateTime format

Does following DateTime format "%M/%d/yyyy %H:%m:%s" will include both lines, e.g. with or without leading zero:
Line 1: 4/8/2022 7:6:3
Line 2: 04/08/2022 07:06:03
It's seems to be working but related documentation is more welcome.
The related documentation can be found here: https://learn.microsoft.com/en-us/dotnet/standard/base-types/custom-date-and-time-format-strings
Note the description of the % symbol: "Defines the following character as a custom format specifier".[1]
Since you have a custom date time format string, the symbols M, d, H, ... are custom format specifiers. This means, here % essentially becomes a no-operation without any effect, because the symbols following it are already custom format specifiers.
So, what exactly is the purpose of % if the symbols in a custom date time format string are already custom format specifiers regardless of % being there or not? The reason for % becomes understandable when you consider that there are also standard date time format strings, which consist of a single character, a single format specifier. Pertinent documentation here: https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-date-and-time-format-strings
Basically, any date time format string made of only one character is treated as a standard date time format string. And any date time format string with two or more characters is treated as a custom date time format string.
What if you want to use a custom date time format string consisting of only one custom format specifier? That one-character string will be interpreted as a standard date time format string instead. And that is a problem.
If you compare the lists of specifiers for standard and custom date time format strings, you'll notice that many of the standard date time format specifiers use symbols that are also used by custom date time format specifiers. However, standard date time format specifiers represent different data and/or formatting patterns than the respective custom date time format specifier using the same symbol. For example, the standard date time format specifier y yields year+month, while the custom date time format specifier y yields the last two digits of the year.
Therefore, if you need a functionally single-specifier custom date time format string, you gotta fatten up that string and turn it from a one-character string into a two-characters string with the help of the "no-op" specifier %, so that it will be correctly treated as a custom date time format string.
As an example, imagine you want to get just the last two digits of the year and nothing more, and you decide to use the custom format specifier y which does exactly what you want. However, the format string "y" is a standard date time format string yielding year+month. So, how do you get what you want? You turn the standard date time format string "y" into a custom date time format string by using "%y".
[1]According to that documentation, it should theoretically be possible to use contiguous sequences of multiple % in custom date time format string like "%%%%%M/%%%%%%d". Each of those % sequences should functionally collapse into a single %, as by definition according to the quoted documentation, each % defines the following % as a custom format specifier that it already is. However -- and for the better, i might add -- the DateTime formatting functions will have none of such shenanigans and throw a FormatException for you being a bad boy having even tried this...

How can I resolve the stringFormat Exception error

var date= DateTime.ParseExact("16-03-2022 1:30", "dd-MM-yyyy HH:mm", CultureInfo.InvariantCulture);
When I enter 16-03-2022 13:30, it does not give an error but when the parameter is 16-03-2022 1:30, I get an error, how can I solve it?
I feel like taking a risk to answer but..
Let's go step by step. One of the good things about .NET methods is that you can see what exceptions can be thrown on that method in their documentation.
From "Exception" section on documentation it says;
FormatException
s or format is an empty string.
-or-
s does not contain a date and time that corresponds to the pattern
specified in format.
-or-
The hour component and the AM/PM designator in s do not agree.
Your s or format is not empty, your string does not have any AM or PM designator, so the only option left is "s does not contain a date and time that corresponds to the pattern specified in format." as a reason.
Also from documentation, it says;
Converts the specified string representation of a date and time to its
DateTime equivalent using the specified format and culture-specific
format information. The format of the string representation must match
the specified format exactly.
What "format" we are talking about? There are two of them. Custom date and time format strings and Standard date and time format strings. Since we are using DateTime.ParseExact, we need to consider using custom date and time format.
Let's look at all parts can be parse in your 16-03-2022 1:30 string;
16 --> Two digit day number (dd)
03 --> Two digit month number with leading zero (MM)
2022 --> Four digit year (yyyy)
1 --> One digit hour (it can be h or H because there is no time designator in your string and we can't know it's in 12-hour clock format or 24-hour clock format)
30 --> Two digit minutes (mm)
So, the proper format of your 16-03-2022 1:30 string can be either dd-MM-yyyy H:mm or dd-MM-yyyy h:mm which depends on you. If it is 24-hour format, use H specifier, if it is 12-hour format, use h specifier.
When you see the word "Exact" in ParseExact(), it means it. Any deviation from the expected format at all will cause an exception.
In this case, the HH specifier is not an exact match for the 1 value for the hour. It would match if you had 01 instead, but just 1 isn't the same thing. To match the hours without leading zeros you need a single H, creating this format string:
dd-MM-yyyy H:mm
This will still match later hours like "10" and "11". Additionally, the capital "H" instead of lower-case means it still expects 24-hour time, so numbers like "13" up to "23" still work, too.
If you could get a mix of values, that sometimes has just the 1 and sometimes might have the full 01, then you need to use a ParseExact() overload that accepts an array of formats, and provide both versions.

How do I convert this date in to a valid date object?

I am trying to convert string dates like 2020-01-14T17:01:48.757Z and 2020-01-14T17:01:50.760Z in to C# DateTime. Looks like my parsing is failing somewhere.
DateTimeOffset.ParseExact("2020-01-14T17:01:48.757Z", "yyyy-MM-dd'T'HH:mm:sszzz", CultureInfo.InvariantCulture).DateTime;
Whats wrong with above code ? It fails with
String '2020-01-14T17:01:50.760Z' was not recognized as a valid
DateTime.
When I parse same date online https://nsdateformatter.com/ It has no issues.
I even tried using yyyy-MM-dd'T'HH:mm:ssZ but it also gives above error.
Use this date format:
DateTimeOffset.ParseExact("2020-01-14T17:01:48.757Z", "yyyy-MM-ddTHH:mm:ss.fffZ", CultureInfo.InvariantCulture).DateTime;
2020-01-14T17:01:48.757Z
yyyy-MM-ddTHH:mm:ss.fffZ
As You see format corresponds to Your provided date string.
Looks like you forget to use proper format specifier for your milliseconds part and dot (.) between your seconds and milliseconds part.
The "fff" custom format specifier
The "fff" custom format specifier represents the three most
significant digits of the seconds fraction; that is, it represents the
milliseconds in a date and time value.
DateTimeOffset.ParseExact("2020-01-14T17:01:48.757Z",
"yyyy-MM-dd'T'HH:mm:ss.fffZ",
CultureInfo.InvariantCulture)

DateTime.TryParseExact Erratic results

Using the below DateTime.TryPaseExact method gives me different out puts when one is expected.
I get the following formats returned from the code below:
2014-02-10 18:32:37
1402-10-18 17:23:00 (which is clearly incorrect)
How do I just get a yyyy-MM-dd HH:mm:ss format returned?
static readonly string[] DateFormats = new string[] { "yyMMddHHmm", "yyyyMMddHHmm", "yyMMddHHmmss", "yyyy-MM-dd HH:mm:ss" };
DateTime date;
DateTime.TryParseExact("140211090915", DateFormats, CultureInfo.InvariantCulture,
DateTimeStyles.AssumeLocal, out date);
Also if someone could explain why this 140211084947 input returns the expected format and 140211090915 returns an unwanted format I would be grateful
It is clear that you are mistaken.
When the format is yyMMddHHmmss, it is taking first 2 digits as a year which in this case is 14 and is automatically converted to 2014.
When format is something like yyyy...., it is taking first 4 digits as a year which in your case is 1402.
From The "yy" Custom Format Specifier
In a parsing operation, a two-digit year that is parsed using the "yy"
custom format specifier is interpreted based on the
Calendar.TwoDigitYearMax property of the format provider's current
calendar.
From Calendar.TwoDigitYearMax property
This property allows a 2-digit year to be properly translated to a
4-digit year. For example, if this property is set to 2029, the
100-year range is from 1930 to 2029. Therefore, a 2-digit value of 30
is interpreted as 1930, while a 2-digit value of 29 is interpreted as
2029.
As Marcin mentioned, your string input doesn't match with yyMMddHHmm and yyyy-MM-dd HH:mm:ss formats.
Your 140211084947 string matches with yyMMddHHmmss format but it doesn't match with yyyyMMddHHmm format (Because 49 is not a valid hour)

TimeSpan.TryParseExact not working

I'm trying to retrieve a timespan from a string, but TryParseExact is returning false (fail).
I can't see what I'm doing wrong, can you help? I've tried 2 versions of my line in the code, both do not work.
TimeSpan.TryParseExact("04:00:01","HH:mm:ss",CultureInfo.CurrentCulture, out aTime)
and
TimeSpan.TryParseExact("04:00:01","HH:mm:ss", null, out aTime)
EDIT:
both responses here are correct, I have the wrong format for my custom timespan format - the mistake I made is to assume that the custom formats for DateTime would work for TimeSpans, but they do not.
The problem is simply in the format string for the TimeSpan, you have specified "HH:mm:ss". The specifier HH (upper case) is not valid for timespan. You should use hh. Format strings are indeed case sensitive.
The colon character (:) also needs to be escaped, so use "hh\\:mm\\:ss", #"hh\:mm\:ss" or "hh':'mm':'ss". All three forms will have the same effect.
You can review a list of valid custom format strings for TimeSpan here. and the standard format strings for TimeSpan are here.
While HH is valid for DateTime and DateTimeOffset where it represents the 24 hour clock and lower case hh represents a 12 hour clock, For TimeSpan - the hours component is always based on 24 hours. You would think that the HH format would be the one chosen, for uniformity, but nope - it's hh.
It's probably should get mentioned that you need to escape the colon character.
TryParseExact("04:00:01", "HH\\:mm\\:ss" ...
The string format which you are passing is wrong.
var res=TimeSpan.TryParseExact("04:00:01", "g", CultureInfo.CurrentCulture, out aTime);
g- General short format and is culture sensitive.
More on this here Standard Timespan Format Strings
Maybe you were using multiple formats.
public const string TimeFormat1 = "hh\\:mm";
public const string TimeFormat2 = "hh\\:mm:\\ss";
var parsed = TimeSpan.TryParseExact(time, new [] { TimeFormat1, TimeFormat2 }, CultureInfo.CurrentCulture, out TimeSpan ts1);
// parsed is always false
You might have thought you escaped your colon; but didn't, actually...
This "hh\\:mm:\\ss" won't work.
Using TimeFormat2 in ParseExact throws a FormatException...
You meant to use this "hh\\:mm\\:ss" instead.

Categories

Resources