Update datetime in Database - c#

Consider this code:
var datetime = DateTime.Now;
var instantMessage = InstantMassageingManager.GetConverationMessages().FirstOrDefault();
InstantMassageingManager.UpdateConversationMessageReadDateTime(instantMessage.InstantMessageInstanceId, datetime);
var message = InstantMassageingManager.GetMessageById(instantMessage.InstantMessageInstanceId);
Assert.IsTrue(message.ReadDateTime.Value == datetime);
in the first line I get DateTime.Now
then I update a record in database: UpdateConversationMessageReadDateTime
and get the message again from the database:
Both message.ReadDateTime and datetime has same value but with different Tick.
So my test doesn't pass.
Why do I get different values for tick?

I strongly suspect that the type you're using in the database doesn't have the same level of precision - I suspect it's only accurate to the millisecond.
If you want to round-trip the time, you should truncate your "input" time to the nearest millisecond too. For example:
TimeSpan rounded = TimeSpan.FromMilliseconds(original.Ticks / 10000);
Or for a DateTime:
DateTime rounded = new DateTime(original.Year, original.Month, original.Day,
original.Hour, original.Minute, original.Second, original.Millisecond,
original.Kind);
You should also consider whether you really want to take the local time (which depends on the system time zone) or a UTC value. It depends on the context, but it's more frequently appropriate to store UTC values for timestamps, which is what it looks like you've got here.

Related

NodaTime Invalid DateTime.Kind for Instant.FromDateTimeUtc

I'm trying to get ahold of this timezone issue we are having. We would like to store all DateTimes in UTC, and then convert the DateTime to the user's timezone.
We decided to use NodaTime for this, as it seems like the right approach. However, we are experiencing an issue with it.
This is how we convert the DateTime to UTC (note - I hardcoded the usersTimeZone for now):
public static DateTime ConvertLocaltoUTC(this DateTime dateTime)
{
LocalDateTime localDateTime = LocalDateTime.FromDateTime(dateTime);
IDateTimeZoneProvider timeZoneProvider = DateTimeZoneProviders.Tzdb;
var usersTimezoneId = "Europe/Copenhagen";
var usersTimezone = timeZoneProvider[usersTimezoneId];
var zonedDbDateTime = usersTimezone.AtLeniently(localDateTime);
var returnThis = zonedDbDateTime.ToDateTimeUtc();
return zonedDbDateTime.ToDateTimeUtc();
}
And here is how we convert it back:
public static DateTime ConvertUTCtoLocal(this DateTime dateTime)
{
Instant instant = Instant.FromDateTimeUtc(dateTime);
IDateTimeZoneProvider timeZoneProvider = DateTimeZoneProviders.Tzdb;
var usersTimezoneId = "Europe/Copenhagen"; //just an example
var usersTimezone = timeZoneProvider[usersTimezoneId];
var usersZonedDateTime = instant.InZone(usersTimezone);
return usersZonedDateTime.ToDateTimeUnspecified();
}
However, when we convert it back to local time, it throws an exception:
Argument Exception: Invalid DateTime.Kind for Instant.FromDateTimeUtc
at the first line of ConvertUTCtoLocal().
An example of the DateTime could be: "9/18/2017 5:28:46 PM" - yes this has been through the ConvertLocalToUTC method.
Am I providing an incorrect format? What am I doing wrong here?
The exception you show:
Argument Exception: Invalid DateTime.Kind for Instant.FromDateTimeUtc
Is thrown from this code:
Instant instant = Instant.FromDateTimeUtc(dateTime);
It means that dateTime.Kind needs to be DateTimeKind.Utc to be convertible to an Instant, and for whatever reason it is not.
If you look at the result of your ConvertLocaltoUTC method, you'll find that it does have .Kind == DateTimeKind.Utc.
So, the problem lies elsewhere in your code, wherever you created the dateTime you're passing in to ConvertUTCtoLocal.
You may find the solution could be any of the following:
You might need to call DateTime.SpecifyKind to set the kind to UTC, but be careful to only do this when your values are actually UTC and it's just not setting the kind. For example, use this when loading a UTC-based DateTime from a database.
You might need to call .ToUniversalTime(), but be careful to only do this if the machine's local time zone is relevant to your situation. For example, do this in desktop or mobile apps where a UI control is picking a date, but you meant it to mean UTC instead of local time.
You might need to change how you parse strings into DateTime values, such as by passing DateTimeStyles.RoundTripKind to a DateTime.Parse call (or any of its variants. For example, do this if you are reading data from text, csv, etc.
If you want to avoid having to decide, don't write functions that take DateTime as input or give DateTime as output. Instead, use DateTimeOffset, or use Noda-Time types like Instant, LocalDateTime, etc. as early as possible.
This is what worked for me:
Instant instant = Instant.FromDateTimeUtc(DateTime.SpecifyKind(datetime, DateTimeKind.Utc));

Milliseconds value are not getting saved in SQL Server DateTime column

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

Taking a DateTime value and only accepting time and vice versa

I'm currently developing an application to function as a todo - list and i was wondering how do i accept a Value from a date time box, but only use the value of the date, or the value of the time. I'm currently doing it like this.
DateTime ted = appointmentDateTimeDate.Value; //The date
DateTime at = appointmentDateTimeTime.Value; //The time
should i be doing this another way?
Use DateTime.Date property for date, and DateTime.TimeOfDay for time:
DateTime ted = appointmentDateTimeDate.Date; //The date
TimeSpan at = appointmentDateTimeTime.TimeOfDay; //The time
The BCL doesn't really separate dates and times nicely.
If you're happy to take a new external dependency, I'd like to plug my Noda Time library, which will let you separate things out clearly into LocalDate and LocalTime. To perform the conversion from a date/time picker you'd probably use:
var dateAndTime = LocalDateTime.FromDateTime(appointmentDateTimeDate.Value);
LocalDate date = dateAndTime.LocalDate;
LocalTime time = dateAndTime.LocalTime;
Like others pointed out a DateTime always has both a date and a time component. So although it's possible to save both independently using two DateTime, in most cases it's recommendable to save both together in a single DateTime instance.
You should see if you really need both values separated or if your application could combine both in one property, which will make things easier.
A DateTime value ALWAYS contains both the date and the time, whether you use both or not.
You can use the .Date property of a DateTime to get "just the date". it will still have a time value, but the time value will be midnight. You can also use the .TimeOfDay property to get the time portion, which will be a TimeSpan indicating the number of ticks since midnight.
I'm taking a leap here and assuming you're trying to set the date with one control an d the time with another in the UI. Here's a sample of some code we use to do this using an Ajax CalendarExtender attached to a textbox and a custom TimePicker control.
DateTime dt;
try
{
dt = Convert.ToDateTime(txtViewDate.Text).AddHours(txtViewTime.Hour).AddMinutes(txtViewTime.Minute);
if (txtViewTime.AmPm == MKB.TimePicker.TimeSelector.AmPmSpec.PM)
{
dt = dt.AddHours(12);
}
System.Diagnostics.Debug.WriteLine(dt.ToString());
}
catch (Exception)
{
// abort processing
return;
}

Convert DateTime to long and also the other way around

I want to store dates as numbers in a table. I know how to do that but I don't know how to go back. How can I cast a long variable to ToDateTime.
DateTime now = DateTime.Now;
long t = now.ToFileTime();
DateTime today = t.ToDateTime; // I am looking for something like this line. This Method does not exist
I know there are many ways of converting DateTime to long. I don't mind which technique to use. I just want to have a way where I can convert back and forth.
To long from DateTime:
long DateTime.Ticks
To DateTime from long:
new DateTime(long)
From long to DateTime: new DateTime(long ticks)
From DateTime to long: DateTime.Ticks
use the pair long t = now.Ticks and DateTime Today = new DateTime(t)
Since you're using ToFileTime, you'll want to use FromFileTime to go the other way. But note:
Ordinarily, the FromFileTime method
restores a DateTime value that was
saved by the ToFileTime method.
However, the two values may differ
under the following conditions:
If the serialization and deserialization of the DateTime value occur in different time zones. For
example, if a DateTime value with a
time of 12:30 P.M. in the U.S. Eastern
Time zone is serialized, and then
deserialized in the U.S. Pacific Time
zone, the original value of 12:30 P.M.
is adjusted to 9:30 A.M. to reflect
the difference between the two time
zones.
If the DateTime value that is serialized represents an invalid time
in the local time zone. In this case,
the ToFileTime method adjusts the
restored DateTime value so that it
represents a valid time in the local
time zone.
If you don't care which long representation of a DateTime is stored, you can use Ticks as others have suggested (Ticks is probably preferable, depending on your requirements, since the value returned by ToFileTime seems to be in the context of the Windows filesystem API).
There are several possibilities (note that the those long values aren't the same as the Unix epoch.
For your example (to reverse ToFileTime()) just use DateTime.FromFileTime(t).
There is a DateTime constructor that takes a long.
DateTime today = new DateTime(t); // where t represents long format of dateTime
long dateTime = DateTime.Now.Ticks;
Console.WriteLine(dateTime);
Console.WriteLine(new DateTime(dateTime));
Console.ReadKey();
If you want to use seconds since 1970 instead of Ticks:
UTC:
long secondsSince1970 = DateTimeOffset.Now.ToUnixTimeSeconds();
Local time:
long secondsSince1970 = DateTime.Now.Ticks / 10000000 - 62135596800;
And back to DateTime
DateTime value = DateTime.MinValue.AddSeconds(secondsSince1970).AddYears(1969);
I will add some additional conversion from string to DateTime to long
string timeString = "2016-08-04";
DateTime date = DateTime.Parse(timeString);
long dateTime = date.Ticks;
And here is the shorthand sort of speak:
long dateTime = DateTime.Parse("2016-08-04").Ticks;
//And for the long to DateTime
DateTime date = new DateTime(dateTime);

Safely convert UTC datetimes to local time (based on TZ) for calculations?

Following from my last question which #Jon Skeet gave me a lot of help with (thanks again!)
I am now wondering how I can safely work with date/times, stored as UTC, when they are converted back to Local Date/Time.
As Jon indicated in my last question using DateTimeOffset represents an instant in time and there is no way to predict what the local time would be say a minute later. I need to be able to do calculations based on these date/times.
So how can I assure when I pull the dates from the database, convert them to local date/time and do specific calculations on them they are going to be accurate?
Scenario
My application records information sent in via email. The date/time the email is received is recorded as the submission time. Emails are pulled from exchange.
What I need to know is:
1) If these emails are coming from different countries, do I just convert the Recieved date/time of the email to UTC format and store that? e.g. Email.Received.ToUniversalTime()
No, you can't assume that. UTC time is completely linear, and you can safely do calculations on them. Once you converted it to a local time, it's no longer completely linear.
When daylight savings time changes occur, there is an overlap or gap in the local time. If you do a calculation that spans over a daylight savings time change, the result will be off by one hour (if that is how much the time changes, which is the most common).
If you do the calculation before converting the DateTime/DateTimeOffset value to local time, the result will always be correct. Note however that converting the value to a local DateTime value can make it ambiguous, if the value happens to fall inside an overlap at the daylight savings time change, it's impossible to tell if it's the first or second time that exact time occurs that day.
The safest way to handle date/time correctly is to store everything as UTC and display it in local time. All date/time math should be done in UTC as Guffa suggests. Store in UTC and convert to local time on the fly as you display it.
How to make a Time Zone aware date/time
Microsoft has an article on how to encapsulate a DateTime and TimeZoneInfo variable into a structure here.
Here's Microsoft's sample structure with 1 property added to easily get local time. This needs more work to be a fully useful, but it's a good start.
public struct TimeZoneTime
{
public TimeZoneInfo TimeZone;
public DateTimeOffset Time;
public TimeZoneTime(DateTimeOffset time)
{
this.TimeZone = TimeZone.Local;
this.Time = time;
}
public TimeZoneTime(TimeZoneInfo tz, DateTimeOffset time)
{
if (tz == null)
throw new ArgumentNullException("The time zone cannot be a null reference.");
this.TimeZone = tz;
this.Time = time;
}
public TimeZoneTime AddTime(TimeSpan interval)
{
// Convert time to UTC
DateTimeOffset utcTime = TimeZoneInfo.ConvertTime(this.Time, TimeZoneInfo.Utc);
// Add time interval to time
utcTime = utcTime.Add(interval);
// Convert time back to time in time zone
return new TimeZoneTime(this.TimeZone, TimeZoneInfo.ConvertTime(utcTime, this.TimeZone));
}
public DateTime LocalDate
{
get { return Time.ToOffset(TimeZone); }
}
}
Your Scenario
Yes, use either the mail object's ReceivedTime or SentOn and convert it to UTC for storage & calculations. This is much less complex than the samples above.
Message msg = new Message();
DateTime received = msg.ReceivedTime.ToUniversalTime();
received.AddDays(7);
Console.WriteLine(received.ToLocalTime());

Categories

Resources