Milliseconds value are not getting saved in SQL Server DateTime column - c#

I need to store a DateTime value which is sent to the Database from a C# application which is using DateTime.UtcNow. If I save it to the DateTime column, the milliseconds value are always 000. But while debugging from the application, the milliseconds value is sent from the application to the database.
What am I missing?

It might happen because datetime column rounds milliseconds part. From documentation;
Accuracy
Rounded to increments of .000, .003, or .007 seconds
Since you didn't show us how often you store your UtcNow value (I assume you store it as a DateTime, not it's string representation because parameterless ToString and other standard formats does not generates millisecond part usually), this may happen but if you do it in short time intervals, it would be really weird always rounding to 000 as milliseconds part. But of course, we can't know that, yet.
On the other hand, datetime2 type does not do any rounding. And it's accuracy is 100 nanoseconds.
Accuracy
100 nanoseconds

Here's no pleasant way to use Datetime because
SQL Server only stores time to approximately 1/300th of a second.
These always fall on the 0, 3 and 7 milliseconds
SQL Server 2008 has much more precision available. The datetime2 datatype will accurately store values like this: 2008-12-19 09:31:38.5670514
See reference documentation

It looks to me like something/someone is doing a default-ToString instead of using ToString with the proper ISO-standard format.
Proper ISO-format for date with time is
yyyy-MM-ddTHH:mm:ss.fff
while date-only ISO-format is
yyyyMMdd
You can check:
static void Main(string[] args)
{
System.DateTime cur = System.DateTime.UtcNow;
string strDefault = cur.ToString();
string str = cur.ToString("yyyy-MM-ddTHH:mm:ss.fff", System.Globalization.CultureInfo.InvariantCulture);
System.Console.WriteLine(str);
System.Console.WriteLine(strDefault);
}
This outputs
2016-03-03T08:31:27.324
03.03.2016 08:31:27
You might also want to use the HEX-format, as this enhances precision, or at least preserves a value that already was in SQL-Server. If you don't use hex-representation, you can get a 23:59:59.997 value from SQL-server and resave, and you'll have 00:00:00 with day+1. Using the hex-format preserves .997, while saving an ISO-string will yield day+1.
public static string GetTimeAsHex(System.DateTime dt)
{
System.DateTime zero = new System.DateTime(1900, 1, 1);
System.TimeSpan ts = dt - zero;
System.TimeSpan ms = ts.Subtract(new System.TimeSpan(ts.Days, 0, 0, 0));
double x = System.Math.Floor(ms.TotalMilliseconds / 3.3333333333);
string hex = "0x" + ts.Days.ToString("X8") + System.Convert.ToInt32(x).ToString("X8");
return hex;
}
Anyway, as others have already told you, SQL-server datetime is only precise to within a 4 ms error margin. This is why you should use datetime2, as it fixes many issues (bugs/"features") in datetime, including insufficient precision in the milliseconds range.

If you are building your INSERT statement as a string, try specifying the output of milliseconds in the string conversion
DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss.fff", CultureInfo.InvariantCulture);

Related

How to convert a string formatted like 2018-12-27T02:23:29 to Unix Timestamp in C#

I'm assuming I should just parse the string into a DateTime and go from there... But is there a better way of doing this?
You can use the DateTimeOffset struct, which has a ToUnixTimeSeconds (or ToUnixTimeMilliseconds) method you can use:
long unixTimestamp = DateTimeOffset.Parse("2018-12-27T02:23:29").ToUnixTimeSeconds();
If you're curious how it's done, the source is here: https://referencesource.microsoft.com/#mscorlib/system/datetimeoffset.cs,8e1e87bf153c720e
You should parse it to a normal DateTime object using something from the DateTime.Parse/ParseExact family of functions, and then call a method like this:
public int ToUnixTime(DateTime d)
{
var epoch = new DateTime(1970,1,1);
return (int)(d - epoch).TotalSeconds;
}
DateTime interally stores the "Ticks" (a invented Time unit) since "12:00:00 midnight, January 1, 0001 (0:00:00 UTC on January 1, 0001), in the Gregorian calendar". As a Int64/Long Number. DateTime thus beats UnixTime easily in possible values. Converting to DateTime should be lossless.
Any ToString() call, any other Property call will simply calculate the values based on those Ticks (and culture/Timezone settings for ToString()). Everything else is just a interpretation of the Tick value.
You should parse to DateTime. And getting from Ticks to something as inprecise as the UnixTime is easy math. See Joels Answer for that.
Do note however the DateTimes preccision and accuaracy do not match fully: https://blogs.msdn.microsoft.com/ericlippert/2010/04/08/precision-and-accuracy-of-datetime/ DateTime.Now will usually give you only return values in 18 ms steps. And even the Stopwatch has issues with values < 1 ms.

Convert difference in milliseconds to .MaxValue back to Date/Time

For a rowkey on Azure TableStorage entities following prefix is used:
DateTime.MaxValue.Subtract(DateTime.UtcNow).TotalMilliseconds
As far as I know should this timestamp act as a kind of "sorter" so that newer entities are on top of an list. So, this shown code line creates (as I can imagine) the amount of milliseconds of the current date/time till the DateTime.MaxValue.
Is there a simple and safe way, to convert this amount of milliseconds "back" to the date/time when the timestamp was created? I´m not so familiar with date/time conversions...
The DateTime.MaxValue is:
equivalent to 23:59:59.9999999 UTC, December 31, 9999 in the
Gregorian calendar, exactly one 100-nanosecond tick before 00:00:00
UTC, January 1, 10000.
Thus, considering roughly 10,000 years, you have:
10,000 x 365 x 24 x 60 x 60 x 1000 = 315,360,000,000,000 //Note 15-digit
And the double precision is at least 15-digit. In other words, as long as you use the first 15 digit of your TotalMilliseconds as the timestamp, then it should be fine.
I recommend to cast it to long whose integer precision is:
–9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 //note, more than 15-digit
And then use ToString("D15") as unique timestamp:
long val = (long)DateTime.MaxValue.Subtract(DateTime.UtcNow).TotalMilliseconds;
string timestamp = val.ToString("D15");
And to convert back, you could cast it back to double and use AddMilliseconds with negative sign from max.
double db = Convert.ToDouble(timestamp);
DateTime dt = DateTime.MaxValue;
dt.AddMilliseconds(-db); //this will give you the datetime back with milliseconds precision
Then you will get precision up to your milliseconds.

DateTime.Compare is not working as expected

I'm inserting customers the following way.
DateTime d = DateTime.Now;
foreach(Customer cus in CustomerList)
{
cus.EntryDate = d;
}
SaveToDatbase(CustomerList);
Date is getting saved successfully with the correct entry date, but however when I retrieve data back and compare it with variable d, it doesn't match.
foreach(Customer cus in GetFromDatabase())
{
Response.Write(DateTime.Compare(cus.EntryDate, d));
}
The output is 1 instead of 0. I checked the inserted data and it matches d variable value with milliseconds. Date is stored in sql server database. One thing I realized is if I reset milliseconds to 0 for cus.EntryDate and d, output is 0. What am I doing wrong here?
SQL Server's datetime type has a lower resolution than .NET's. As you're using DateTime.Now directly (without rounding it to the nearest second) you'll see differences in the milisecond or tick (100 nanosecond) portions.
According to MSDN, the datetime type in SQL Server has a resolution of approximately 3 miliseconds (specifically: 2015-08-18 15:49:10.000 -> 2015-08-18 15:49:10.003 -> 2015-08-18 15:49:10.007 -> 2015-08-18 15:49:10.010) whereas .NET's DateTime type has a resolution of 100 nanoseconds.
If you want to preserve some kind of equality, I suggest rounding DateTime to the nearest second before inserting into the database:
DateTime now = DateTime.Now;
now = new DateTime(
now.Ticks - ( now.Ticks % TimeSpan.TicksPerSecond ),
now.Kind
);
foreach(Customer cus in customerList) cus.EntryDate = now;
SQL Server's datetime value has a precision of 1 milicsecond, but an accuracy of [approximately] 3.33ms. Further, it doesn't actually "round" the millisecond count. Its "rounding" algorithm is...byzantine, to be kind. But it is, at least, documented. My answer to the question, "Round .NET DateTime milliseconds, so it can fit SQL Server milliseconds", which you can find at https://stackoverflow.com/a/5318635/467473, will
point you to the documentation for how the conversion is performend, and
has an datetime extension method that will convert a CLR System.DateTime value to its corresponding SQL Server datetime equivalent.
One should note that to due to the precision/accuracy difference between the two representations, this is not a round trip conversion.

Milliseconds in my DateTime changes when stored in SQL Server

I have a date time that I generate like this:
DateTime myDateTime = DateTime.Now;
I then store it in the database (in a DateTime typed column) with Entity Framework. I then retrieve it with OData (WCF Data Services).
When it goes in the TimeOfDay value is: 09:30:03.0196095
When it comes out the TimeOfDay value is: 09:30:03.0200000
The net effect of this makes it so that the Milliseconds are seen as 19 before it is saved and 20 after it is re-loaded.
So when I do a compare later in my code, it fails where it should be equal.
Does SQL Server not have as much precision as .NET? Or is it Entity Framework or OData that is messing this up?
I will just truncate off the milliseconds (I don't really need them). But I would like to know why this is happening.
This really depends on the version of SQL server you are using.
The resolution of the date time field is to 3 decimal places: For example: 2011-06-06 23:59:59.997 and is only accuracte to within 3.33 ms.
In your case, 09:30:03.0196095 is being rounded up to 09:30:03.020 on storage.
Beginning with SQL 2008, other data types were added to provide more detail, such as datetime2 which has up to 7 decimal places and is accurate to within 100ns.
See the following for more information:
http://karaszi.com/the-ultimate-guide-to-the-datetime-datatypes
I think your best bet is to provide the rounding to the second PRIOR to storing it in SQL server if the milliseconds is unimportant.
This is due to the precision of the SQL datetime type. According to msdn:
Datetime values are rounded to increments of .000, .003, or .007 seconds
Look at the Rounding of datetime Fractional Second Precision section of this msdn page and you'll understand how the rounding is done.
As indicated by others, you can use datetime2 instead of datetime to have a better precision:
datetime time range is 00:00:00 through 23:59:59.997
datetime2 time range is 00:00:00 through 23:59:59.9999999
For those who do not have the ability to use DateTime2 in SQL (ex: like me using tables that are generated by a separate system that would be expensive to change for this single issue), there is a simple code modification that will do the rounding for you.
Reference System.Data and import the System.Data.SqlTypes namespace. You can then use the SqlDateTime structure to do the conversion for you:
DateTime someDate = new SqlDateTime(DateTime.Now).Value;
This will convert the value into SQL ticks, and then back into .NET ticks, including the loss of precision. :)
A word of warning, this will lose the Kind of the original DateTime structure (i.e. Utc, Local). This conversion is also not simply rounding, there is a complete conversion including tick calculations, MaxTime changes, etc.. So don't use this if you are relying on specific indicators in DateTime as they could be lost.
The precision of DateTime in SQL Server is milliseconds (.fff). So 0.0196 would round to 0.020. If you can use datetime2, you get a higher precision.

How to get current Time with Milli second precision ( C#)

I use System.DateTime.Now , but it return like 5/28/2011 1:45:58 AM .(no Milli second precision)
I would like to save current time (or Date time ) with Milli second precision in database .
Update : Sorry , I meant Milli Second
System.DateTime manages precision to the millisecond, 5/28/2011 1:45:58 AM is just how it was formatted to a String.
To format with millisecond included use format string: "d/M/yyyy hh:mm:ss.fff tt"
If you want to store it in a SQL Server database, ADO.Net automatically converts the CLR System.DateTime datatype to a SQL Server datetime datatype (and vice-versa).
The CLR System.DateTime has 100-nanosecond precision (e.g., each tick is 100 nanoseconds; 10,000 ticks per millisecond, 10 million ticks per second.
The SQL Server datetime datatype is precise to (approximately) 3ms.
You shouldn't need to worry about it: ADO.Net will take care of it for you.
OTOH, if you really want to throw away extra nanoseconds, something like this ought to do the trick:
public static DateTime ToExactMillisecondPrecision( DateTime dt )
{
const long TICKS_PER_MILLISECOND = 10000 ;
long totalMilliseconds = dt.Ticks / TICKS_PER_MILLISECOND ;
return new DateTime( totalMilliseconds * TICKS_PER_MILLISECOND ) ;
}
Can't really see the need myself.
Look under the properties list in this link. All the different options are there.
http://msdn.microsoft.com/en-us/library/system.datetime.aspx
Including seconds, milliseconds, and ticks
The string you posted contains seconds, so I suppose you're not asking for second precision, but for more precise timing.
The value of DateTime.Now is returned with more than millisecond precision. it's just that with default formatting, the milliseconds aren't displayed. To display the value with milliseconds, you can either use the o standard format string, or write your own custom format string, that includes the millisecond format specifier fff.
Note that just because the returned value is precise, it doesn't mean it's as much accurate. The actual accuracy is not defined exactly, but tends to be in tens of milliseconds.
It should not be necessary to convert the date to string. Perhaps the real problem is that you using dynamic SQL.

Categories

Resources