Stopwatch elapsed output - c#

I had some code that crunched a lot of data, so I started it on Thursday and left it running over the weekend. On Monday, I have come back and seen that it finished. I used a stopwatch function to track the length of time the code ran for. However, I ended up with
Elapsed: 2.18:57:55.xxx
I understand that it's output is normally H:M:SS, but don't understand the first digit, especially since it's been running for days. Did it just convert from hours to days? Did I leave it running so long that it broke?
EDIT: Sorry, I didn't mean that it finished on Monday. I just meant that BY Monday (when I returned to the computer), it was done.

Yes - that's the format of TimeSpan.ToString:
The returned string is formatted with the "c" format specifier and has the following format:
[-][d.]hh:mm:ss[.fffffff]
Elements in square brackets ([ and ]) may not be included in the returned string. Colons and periods (: and.) are literal characters. The non-literal elements are listed in the following table. Note that the string returned by the ToString() method is not culture-sensitive.
Since there's not a format specifier that shows total hours, you'll need to calculate it. If you want the hours to be shown as a single number then use:
TimeSpan ts = new TimeSpan(2,18,57,55);
var output = string.Format("{0}:{1}",
ts.Days*24 + ts.Hours,
ts.ToString("mm\\:ss\\.ffff"));
//output = `66:57:55.0000`

That's expected behaviour. From the docs for Timespan.ToString():
The returned string is formatted with the "c" format specifier and has the following format:
[-][d.]hh:mm:ss[.fffffff]
"d" - The number of days in the time interval. This element is omitted if the time interval is less than one day.

Related

How to show DateTime objects and their differences in a logical way?

I'm working on a C# application, where I'm doing some things and I want to display both the start, intermediate and end timestamps, and now I would like to add their time differences.
I figured it would be easy:
Console.WriteLine($"Start time: {DT_Start.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
Console.WriteLine($"Intermediate time: {DT_Intermediate.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
Console.WriteLine($"End time: {DT_End.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
This is working great. So I thought it would be equally easy to show the differences, so I started with:
Console.WriteLine($"Elapsed times: [{(DT_Intermediate - DT_Start).ToString("HH:mm:ss.fff")}] " +
$"and [{(DT_End - DT_Intermediate).ToString("HH:mm:ss.fff")}]");
I had dropped the year, month and day because everything is done in the same day. This did not work, so I decided to add those entries, but it still does not work:
Console.WriteLine($"Elapsed times: [{(DT_Intermediate - DT_Start).ToString("yyyy-MM-dd HH:mm:ss.fff")}] " +
$"and [{(DT_End - DT_Intermediate).ToString("yyyy-MM-dd HH:mm:ss.fff")}]");
So, in C#, you can show datetime objects and you can subtract them. The results, when debugging, are very similar but if you try to show that information in the same way, you get the error message System.FormatException: 'Input string was not in the correct format.'.
Is there a format I can use for both DateTime and TimeSpan objects? (I've seen that the difference between two DateTime objects would be a TimeSpan object)
As you have discovered yourself, the difference between two DateTime objects is a TimeSpan which just represents the difference of time that has passed. Since a TimeSpan is not linked to a calendar date, you cannot format it using calendar specific things like months and years.
However, your initial approach of only showing hours, minutes and seconds does work just fine. However, you will need to escape the colon and dot when wanting to use it in a TimeSpan format string. And also, the HH for the hours in the DateTime is written as lower-case hh for TimeSpan:
Console.WriteLine((DT_Intermediate - DT_Start).ToString("hh\\:mm\\:ss\\.fff"));
// ^^^^ ^^ ^^
// lower-case hh and escaped characters
So in your example, this should work:
Console.WriteLine($"Elapsed times: [{(DT_Intermediate - DT_Start).ToString("hh\\:mm\\:ss\\.fff")}] " +
$"and [{(DT_End - DT_Intermediate).ToString("hh\\:mm\\:ss\\.fff")}]");
Note that the TimeSpan also supports days as part of the difference, so if the number of hours in your difference surpasses 24, you will be missing this difference until you also include the number of days using the format specifier d in your result.
You can read more about formatting TimeSpan in the documentation about custom format strings.
You can use the DateTime.Subtract Method, it accepts DateTime and TimeSpan Object input.
https://learn.microsoft.com/de-de/dotnet/api/system.datetime.subtract?view=net-7.0

ParseExact cannot parse a string in RFC 3339 Internet Date/Time format

It seems that C# does not manage to parse a time in a valid RFC 3339 format:
DateTime.ParseExact("2019-12-31T00:00:00.123456789+01:00", "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffffffffzzz", null)
This line throws an exception, while this line works just fine:
DateTime.ParseExact("2019-12-31T00:00:00.1234567+01:00", "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffffffzzz", null)
So it seems there is a limit on milliseconds, but I cannot find out any documentation on that. Is this how it is supposed to be?
The reason want to parse this date is that I have have an input date field. We use OAS (Swagger) date-time format that quite clearly says that any date in RFC 3339 Internet Date/Time format should be valid. Now from the spec here section 5.6
time-secfrac = "." 1*DIGIT
As far as I understand this means that up to 9 digits should be allowed and to be 100% compliant we have to allow these inputs, but it does not seem that C# even supports that.
Any ideas on how to fix it?
Per MSDN specification, you can use only fffffff
The fffffff custom format specifier represents the seven most
significant digits of the seconds fraction; that is, it represents the
ten millionths of a second in a date and time value.
In your first example
DateTime.ParseExact("2019-12-31T00:00:00.123456789+01:00", "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffffffffzzz", null)
you are using fffffffff which is more precise for .NET custom date and time format strings
As far as I know, .NET supports seven most significant digits for milliseconds which is The "fffffff" custom format specifier are for.
The "fffffff" custom format specifier represents the seven most
significant digits of the seconds fraction; that is, it represents the
ten millionths of a second in a date and time value.
Although it's possible to display the ten millionths of a second
component of a time value, that value may not be meaningful. The
precision of date and time values depends on the resolution of the
system clock.
That means you are giving not meaningful data that are not supported for .NET Framework. I strongly suggest not doing that.
In addition to the information in the other answers, if you cannot change your input and you still want to parse it, you may use one of the following solutions:
If your input will always be in the same format (i.e., has 9 seconds-fraction digits), you could just remove the two extra ones and proceed to parse it:
string input = "2019-12-31T00:00:00.123456789+01:00";
input = input.Remove(27, 2);
// TODO: parse `input`.
If you don't know the number of the seconds-fraction digits beforehand, you may use something like this:
string input = "2019-12-31T00:00:00.123456789+01:00";
string format = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'FFFFFFFzzz";
var regEx = new Regex(#"^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{1,7})\d*");
input = regEx.Replace(input, "$1");
DateTime parsedDate = DateTime.ParseExact(input, format, null);

what is this format for seconds XXXXXXX.XXXXX:XXX (Win32_NetworkLoginProfile)

Querying a WMi object on the
$colItems = Get-WmiObject Win32_NetworkLoginProfile -Namespace "root\CIMV2"
| Where-Object {$_.name -match "Name"} | Select-Object name,PasswordAge
according to MSDN
PasswordAge
Data type: datetime
Access type: Read-only
Length of time a password has been in effect. This value is measured from the number of seconds elapsed since the password was last changed.
Example: 00001201000230.000000 000
I am getting
00000068235223.000000:000
So I have tried casting this to TimeSpanand DateTime no luck.
what does the colon represent how to get number hours it represent.
Thanks Adding the WMI class name to title for the next poor soul that get confused by documentation wording.
here is what works
That worked perfectly $str = "00000068235223.000000:000"
$ts = [System.Management.ManagementDateTimeConverter]::ToTimeSpan($str)
Days : 68
Hours : 23
Minutes : 52
Seconds : 23
Milliseconds : 0
Ticks : 59611430000000
TotalDays : 68.9947106481481
TotalHours : 1655.87305555556
TotalMinutes : 99352.3833333333
TotalSeconds : 5961143
TotalMilliseconds : 5961143000
This is the DMTF time interval format which is documented here. Basically it is string that encodes a time span in the form ddddddddHHMMSS.mmmmmm:000. Note that for time intervals, the ending is always :000.
From the docs:
The following example shows the format of a date-time interval.
ddddddddHHMMSS.mmmmmm:000
The following table lists the fields of the date-time interval.
Field Description
dddddddd Eight digits that represent a number of days (00000000 through 99999999).
HH Two-digit hour of the day that uses the 24-hour clock (00 through 23).
MM Two-digit minute in the hour (00 through 59).
SS Two-digit number of seconds in the minute (00 through 59).
mmmmmm Six-digit number of microseconds in the second (000000 through 999999).
I believe the documentation is somewhat misleading for this field, since it is implying the whole numbers are full seconds, but in fact it is a string format representing days, hours, minutes, seconds, etc.
You should use [System.Management.ManagementDateTimeConverter]::ToTimeSpan to convert this string to a .NET Timespan, however, for whatever reason I actually received an array instead of a string for PasswordAge, so I had to use this:
$p = gwmi Win32_NetworkLoginProfile
[System.Management.ManagementDateTimeConverter]::ToTimeSpan($p.PasswordAge[1])
To add to all the previous answers, the proper routine to make something out of this type of strings is call [System.Management.ManagementDateTimeConverter]::ToTimeSpan($string). This will produce a [System.TimeSpan] object, which you can then parse in a better way.
Credit for class to do conversion goes to this answer.
Those are CIM_DATETIME values or Interval values. The format is defined by the CIM standard that WMI is built upon.
With normal datetime values, the WMI object provides a converter function. For example:
$_.ConvertToDateTime($_.PasswordExpires);
But that doesn't work for intervals, which is what PasswordAge is.
The MS doc you point to says the value is the number of seconds, but I believe that means the precision is to the second, not that the value is literally in seconds. My current password says it's 43 years old if that were the case, for example, and that's not possible. So it must use the Interval format.
I would do this:
$PasswordAge = New-TimeSpan -Days $_.PasswordAge.Substring(0,8) `
-Hours $_.PasswordAge.Substring(8,2) `
-Minutes $_.PasswordAge.Substring(10,2) `
-Seconds $_.PasswordAge.Substring(12,2);
Remember to use $PasswordAge.TotalHours and not just $PasswordAge.Hours if you want the actual age value expressed in hours and not just the hour value of the timespan.
Edit: #Vesper is right. You can just use:
[System.Management.ManagementDateTimeConverter]::ToTimeSpan($_.PasswordAge);

DateTime.ParseExact - how to parse single- and double-digit hours with same format string?

I want to be able to parse strings of time (hours, minutes, seconds) where the hours run from 0 to 23, and where the preceding zero for one-digit hours is optional.
Examples of time strings that I want to be able to parse into valid DateTime objects:
212540
061525
94505
I am trying to use the C# method DateTime.ParseExact to manage the parsing, but I cannot for the life of it come up with a format string that can handle the "single-digit hour without preceding zero" scenario.
How should I specify the DateTime.ParseExact format string to sufficiently parse all examples above with the same line of code?
Inspired by the MSDN page on custom date and time formats, I have tried the following approaches:
DateTime.ParseExact(time_string, "Hmmss", CultureInfo.InvariantCulture);
DateTime.ParseExact(time_string, "%Hmmss", CultureInfo.InvariantCulture);
DateTime.ParseExact(time_string, "HHmmss", CultureInfo.InvariantCulture);
All these format strings work for the first two example cases above, but faced with a single-digit hour and no preceding zero, all formulations throw a FormatException.
You can insert delimiters between hours, minutes and seconds like this:
string timeString = "94505";
string formatedTimeString = Regex.Replace(str, #"\d{1,2}(?=(\d{2})+$)", "$&:");
var datetime = DateTime.ParseExact(formatedTimeString, "H:mm:ss", CultureInfo.InvariantCulture);
UPDATE:
I've found the cause of failure when parsing "94505" with format string "Hmmss":
What's happening is that H, m and s actually grabs two digits when they can, even if there won't be enough digits for the rest of the format. So the for example with the format Hmm and the digits 123, H would grab 12 and there would only be a 3 left. And mm requires two digits, so it fails.
So basically you have two options for handling the "single-digit hour without preceding zero" scenario:
Change time format: place hours to the end (for example, "ssmmH" or "mmssH") or use delimiters (for example, "H:mm:ss")
Modify the string like I've suggested earlier or like keyboardP has.
You could pad your input string if you know that you'll always have six characters.
string input = "94505";
if(input.Length < 6)
input = input.PadLeft(6, '0');
(Or use input.Length == 5 if you have other valid formats that are shorter).
What about using:
DateTime.ParseExact(time_string, "Hmmss", CultureInfo.InvariantCulture).ToString("HH:mm:ss")

DateTime.Parse error

Our webservice uses the Datetime.parse method to convert data from an xml to DateTime format. It parses Date and time strings separately and adds it together like this -
DateTime.Parse(Date_string).add(TimeSpan.Parse(Time_string)).
Code was working fine except for a few hours last week. Time was showing as 12 hours ahead of actual time. For example, 01/01/2011 10:00:00 will be parsed as 01/01/2011 22:00:00. Most of the requests during that time were processed with datetime values 12 hours ahead of actual time though some were processed correctly. It is working fine now and haven't seen it after that.
Has anyone seen a issue like this?
You say "Code was working fine except for a few hours last week", but you didn't specify exactly when that was or what time zone you are in. Any chance it was around a daylight savings time change?
You shouldn't use TimeSpan.Parse at all. A TimeSpan does NOT represent the time-of-day, despite its appearance as hh:mm:ss. A TimeSpan represents a fixed DURATION of time.
If you really are given separate date and time strings, join them together before parsing, such as:
DateTime dt = DateTime.Parse(date_string + " " + time_string);
You should also be aware of the timezone implications of the string you are sending in. See the MSDN article on DateTime.Parse for further details.

Categories

Resources