Casting and Convert.ToInt32() behave different in C#? - c#

Here a simple C# piece of code:
Convert.ToInt32(TimeSpan.FromMinutes(5).TotalMilliseconds);
//which brings me 300000
(int)TimeSpan.FromMinutes(5).Milliseconds;
//which brings me 0
Why would casting (int) result is different when compared to Convert.ToInt32()?
Shouldn't both bring the same result?

In the first version you're using the TotalMilliseconds property - in the second you're using Milliseconds.
To give a simpler example, with no casting or calling to Convert.ToInt32:
TimeSpan ts = TimeSpan.FromHours(49);
Console.WriteLine(ts.Hours); // 1 (it's two days and one hour)
Console.WriteLine(ts.TotalHours); // 49 (it's 49 hours in total)

The milliseconds is just the milliseconds PORTION of the 5 seconds. Use TotalMilliseconds on the second one as well.

In you first example you use TotalMilliseconds and then just Milliseconds.

Your error is that in the second example you are calling the .Milliseconds property, not the .TotalMilliseconds property.
The former returns 5 minutes in milliseconds. The latter returns the millisecond portion of 5 minutes, which is zero.
The cast vs. convert is a red herring!

You left out "Total" from the second line. So, this works.
(int)TimeSpan.FromMinutes(5).TotalMilliseconds;

They're the same... you've used TotalMilliseconds vs Milliseconds. The first is the total number of milliseconds in 5 minutes, whereas the second is the remainder, or the value which would be displayed if you wanted to display the time IE the '000' in '00:05:00.000'

The issue is not the conversion but that you are comparing TotalMilliseconds and Milliseconds!

Related

"TimeSpan" .Net Datatype in API Endpoint not accepting more than 24 hours. throwing validation error

Has anyone ever used "TimeSpan" datatype in C#? I am not able to post a value with more than 24 hours to the C# MVC controller. My DTO has a property with "TimeSpan" datatype. I need to give end customer the flexibility of using Timespan. I am now receiving the error in response :
""Error converting value "59:42:33" to type 'System.Nullable`1[System.TimeSpan]'.
I am getting the below validation error while posting itself, not even hitting in the C# endpoint..
Response
and the Request json looks like:
{
"timeSinceStarted" : "59:42:33"
}
Well this certainly seems strange to me, but I think I have the answer.
https://learn.microsoft.com/en-us/dotnet/api/system.timespan.parse?view=net-5.0#System_TimeSpan_Parse_System_String_
For example, the value of hh, the hours component, must be between 0 and 23. Because of this, passing "23:00:00" to the Parse method returns a time interval of 23 hours. On the other hand, passing "24:00:00" returns a time interval of 24 days. Because "24" is outside the range of the hours component, it is interpreted as the days component.
Because of this very strange logic, apparently, when you specify 24 hours (which becomes 24 days), the minute field is interpreted as hours, which cannot exceed 23. Sure enough, in my test, "24:23:00" is valid (and indeed parses to 24 days, 23 hours), while "24:24:00" is invalid.
This is a bug in my book, but since it's actually documented as correct, it cannot be fixed.
Well, the simplest thing to do is to format this yourself for example code below
Instead of 59 hours, try 2 days and 11 hours (2.11:42:33)? The input probably goes to TimeSpan.TryParse() with the default format
Second way
return string.Format("{0}hr {1}mn {2}sec",
(int) span.TotalHours,
span.Minutes,
span.Seconds);
AND Third way is
string span = "35:15";
TimeSpan ts = new TimeSpan(int.Parse(span.Split(':')[0]), // hours
int.Parse(span.Split(':')[1]), // minutes
0); // seconds

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);

What is a meaningfull datatype to save hours

What is a good data-type for saving hours in .net?
Is it better to use the decimal type or is the double data-type more appropriate. With hours I mean values such as:
2 for two hours
1.5 for 90 minutes
8.25 for 8 hours and 15 minutes.
A good way to represent a number of hours is to use a TimeSpan:
TimeSpan hours = TimeSpan.FromHours(2);
Given the choice between decimal or double I'd probably go for double as there is typically no expectation that the amount of time is represented exactly. If you need an exact decimal representation of your fractional number of hours (which seems unlikely) then use decimal.
You could also consider storing it as an integer in for example seconds, milliseconds or ticks.
The best datatype to store hours is the one designed for it - TimeSpan.
It has methods that allow you to add/subtract/convert it.
As for storage in a database, it really depends on what you are using this for and what kind of resolution is required.
I would use the time datatype - as it will hold the range:
00:00:00.0000000 through 23:59:59.9999999
However, if you need to hold more than 24 hours in this field, you may want to consider a tinyint or int holding the number of minutes (assuming that is the maximum time resolution you require).
In SQL Server use INT or DECIMAL. TIME isn't really ideal for storing a duration because TIME defines a point in time within the 24 hour clock whereas duration is simply an integer or decimal value. You cannot do addition or subtraction with TIME values and there is no obvious way to use TIME to store durations greater than 24hrs.
Why don't use TIME?
You can use DATEADD with TIME to manipulate it easier:
SELECT DATEADD(minute, 30, CAST('2:00:00' AS TIME))
becomes 02:30:00.0000000. And so on..

Get Ticks per second and convert to String value?

How do I get number of ticks per second of DateTime.UtcNow and convert it to a String value?
BAD QUESTION: try again Get ten millionths of a second
A particular value of DateTime doesn't have a "ticks per second" associated with it; ticks are ticks no matter which DateTime they're in. Ticks are 100 nanoseconds long, so there are 10,000,000 of them per second.
Now to get that as a string is as simple as the string literal "10000000"... although in general you would obtain a number and just call ToString() on it. For instance, you could use:
string ticksPerSecond = TimeSpan.TicksPerSecond.ToString();
Your question is a slightly odd one, so I wonder whether we're missing something... could you edit the question with more details about what you're trying to do. For example, are you trying to determine the number of ticks within the particular second of a particular DateTime? That's most easily done as:
long ticks = dt.Ticks % TimeSpan.TicksPerSecond;
You find the ticks per second as a constant on TimeSpan:
TimeSpan.TicksPerSecond
Not sure what you are trying to do though...
(DateTime.UtcNow.Ticks / TimeSpan.TicksPerSecond).ToString() // Total number of seconds...
I think you may want TimeSpan.TicksPerSecond.
Console.WriteLine("tps = {0}", TimeSpan.TicksPerSecond.ToString());
The number of ticks per second in a DateTime value is always 10000000. One tick is 100 nanoseconds.
So, if you want to convert that to a string:
10000000.ToString()

Convert DateTime.Ticks to MySQL DateTime in query

I have a integer column in MySql storing the DateTime.Ticks.
A single tick represents one hundred nanoseconds or one ten-millionth of a second. There are 10,000 ticks in a millisecond.
The value of this property represents the number of 100-nanosecond intervals that have elapsed since 12:00:00 midnight, January 1, 0001
How can I convert this to a DateTime in a query? I've tried many things, but cannot get it to work.
For the ticks 634128921500016150 I hope to get the mysql datetime '2010-06-23 12:06:50'
I would have believed the following should work, but it gives '4009-06-22 12:15:50.001600'. It seems it's off by 2001 years, 1 day and 9 minutes... If the years and days is consistent, I can just fix it manually, but the minutes seems a little odd.
SELECT DATE_ADD('0000-01-01 00:00:00',
INTERVAL 634128921500016150/10000000 SECOND_MICROSECOND);
I've tried adding more zeros, but it never matches :|
I also tried Jon Skeet's suggestion, but it gives nearly the same result (some fraction of a second different)
Rather than adding using SECOND_MICROSECOND, try just adding via MICROSECOND:
SELECT DATE_ADD('0001-01-01 00:00:00',
INTERVAL 634121049314500000/10 MICROSECOND);
EDIT: I've just worked out why the years are so wrong. MySQL's minimum date is the year 1000. So I suggest you change it to:
SELECT DATE_ADD('0001-01-01 00:00:00',
INTERVAL (634121049314500000 - base_ticks)/10 MICROSECOND);
where base_ticks is the value of the ticks from new DateTime(1001, 1, 1).Ticks.
Heck, you could rebase wherever you want (e.g. 2000) - that might even work round the 9 minutes issue. It's possible that it's making up for leap seconds over the years, or something like that.
Found myself doing the same thing today. Between Jon's answer and the comments I was able to figure it out, but here it is as a function, all wrapped up with a nice bow on it:
CREATE FUNCTION TicksToDateTime(ticks BIGINT) RETURNS datetime DETERMINISTIC
RETURN CAST(DATE_ADD('2001-01-01 00:00:00',
INTERVAL (ticks - 631139040000000000)/10 MICROSECOND) AS DATETIME);
And for those of us coding against SQL Server Compact Edition, the above bow wrapped function is written in a query as:
Select DATEADD(second, (CAST(([TickField]-631139040000000000) AS
FLOAT)/10000000), '2001-01-01 00:00:00' ) From [Table]
The previous code does not work in Compact Edition. It took a while to figure out, so I thought worth including.
I suppose it would work in other SQL versions too but haven't tested it. It has the advantage of being part of a query, so no function needs to be created.
Cheers.

Categories

Resources