Issue around utc date - TimeZoneInfo.ConvertTimeToUtc results in date change - c#

Having an issue whereby the date I wish to save is changing from the onscreen selected date if the users selects a timezone that is ahead x number of hours.
E.g. they choose UTC+2 Athens and date of 25/02/2016 from the calendar pop-up, then date recorded will be 24/02/2016.
I've narrowed the reasoning down to the fact that the selected datetime is recorded as for example 25/02/2016 00:00:00 and with the 2 hour offset, this takes it to 24/02/2016 22:00:00
Having never worked with timezones before, or UTC dates/times, this is highly confusing.
Here is the code -
oObject.RefDate = itTimeAndDate.ParseDateAndTimeNoUTCMap(Request, TextBox_RefDate.Text);
if (!string.IsNullOrEmpty(oObject.TimeZoneDetails))
{
TimeZoneInfo oTimeZone = TimeZoneInfo.FindSystemTimeZoneById(oObject.TimeZoneDetails);
oObject.RefDate = itTimeAndDate.GetUTCUsingTimeZone(oTimeZone, oObject.RefDate);
}
RefDate would equate to something like 25/02/2016 00:00:00 once returned from ParseDateAndTimeNoUTCMap * (code below)*
static public itDateTime ParseDateAndTimeNoUTCMap(HttpRequest oTheRequest, string sValue)
{
DateTime? oResult = ParseDateAndTimeNoUTCMapNull(oTheRequest, sValue);
if (oResult != null)
return new itDateTime(oResult.Value);
return null;
}
/// <summary>
/// Translate a string that has been entered by a user to a UTC date / time - mapping using the
/// current time zone
/// </summary>
/// <param name="oTheRequest">Request context</param>
/// <param name="sValue">Date / time string entered by a user</param>
/// <returns>UTC date / time object</returns>
static public DateTime? ParseDateAndTimeNoUTCMapNull(HttpRequest oTheRequest, string sValue)
{
try
{
if (string.IsNullOrEmpty(sValue))
return null;
sValue = sValue.Trim();
if (string.IsNullOrEmpty(sValue))
return null;
if (oTheRequest != null)
{
const DateTimeStyles iStyles = DateTimeStyles.AllowInnerWhite | DateTimeStyles.AllowLeadingWhite | DateTimeStyles.AllowTrailingWhite;
// Create array of CultureInfo objects
CultureInfo[] aCultures = new CultureInfo[oTheRequest.UserLanguages.Length + 1];
for (int iCount = oTheRequest.UserLanguages.GetLowerBound(0); iCount <= oTheRequest.UserLanguages.GetUpperBound(0);
iCount++)
{
string sLocale = oTheRequest.UserLanguages[iCount];
if (!string.IsNullOrEmpty(sLocale))
{
// Remove quality specifier, if present.
if (sLocale.Contains(";"))
sLocale = sLocale.Substring(0, sLocale.IndexOf(';'));
try
{
aCultures[iCount] = new CultureInfo(sLocale, false);
}
catch (Exception) { }
}
else
{
aCultures[iCount] = CultureInfo.CurrentCulture;
}
}
aCultures[oTheRequest.UserLanguages.Length] = CultureInfo.InvariantCulture;
// Parse input using each culture.
foreach (CultureInfo culture in aCultures)
{
DateTime oInputDate;
if (DateTime.TryParse(sValue, culture.DateTimeFormat, iStyles, out oInputDate))
return oInputDate;
}
}
return DateTime.Parse(sValue);
}
catch (Exception)
{
}
return null;
}
Once returned from the above, the following lines are executed -
TimeZoneInfo oTimeZone = TimeZoneInfo.FindSystemTimeZoneById(oObject.TimeZoneDetails);
oObject.RefDate = itTimeAndDate.GetUTCUsingTimeZone(oTimeZone, oObject.RefDate);
It is within GetUTCUsingTimeZone that the problem seems to occur to me.
static public itDateTime GetUTCUsingTimeZone(TimeZoneInfo oTimeZone, itDateTime oDateTime)
{
if (oDateTime == null || oTimeZone == null)
return oDateTime;
DateTime oLocal = DateTime.SpecifyKind(oDateTime.Value, DateTimeKind.Unspecified);
DateTime oResult = TimeZoneInfo.ConvertTimeToUtc(oLocal, oTimeZone);
return new itDateTime(oResult);
}
I have checked TimezoneInfo for the offset value, and oResult always equates to the oLocal param - the offset. So 25/02/2016 00:00:00 with a 3 hour offset would equate to 24/02/2016 21:00:00
When the offset is -hours, it goes in the other direct, so oResult = oLocal + the offset, if that makes sense. So the main issue of the date changing is not occurring in those instances.
Obviously this is not what I want. I want the date to be what the user has selected, for their timezone.
Has anyone seen something like this before? Any possible solution?
I'm not entirely sure what I've done wrong.

If you need to maintain the correct timezone, you should be using the DateTimeOffset type instead of DateTime type.
DateTimeOffset maintains the offset from UTC so you never lose your timezone information and has a lot of useful methods like
UtcDateTime
From the horses mouth:
https://msdn.microsoft.com/en-us/library/system.datetimeoffset(v=vs.110).aspx
https://learn.microsoft.com/en-us/dotnet/standard/datetime/choosing-between-datetime

The fix was to run the following after grabbing the value from the db and before redisplaying it -
static public itDateTime FixUTCUsingTimeZone(TimeZoneInfo oTimeZone, itDateTime oDateTime)
{
if (oDateTime == null || oTimeZone == null)
return oDateTime;
DateTime oTime = DateTime.SpecifyKind(oDateTime.Value, DateTimeKind.Unspecified);
DateTime oResult = TimeZoneInfo.ConvertTimeFromUtc(oTime, oTimeZone);
return new itDateTime(oResult);
}
So essentially just doing the reverse of the ConvertTimeToUtc performed earlier. Not sure why this wasn't done originally, but there you go.

Related

DateTime.TryParse -> "Ghost ticks"

i've the following unit test, which fails on the machine of one of our developers (He get some ticks in the result variable, while the datetime variable is at zero ticks), but runs well on every other machine.
[TestMethod]
public void DateTimeStringDateTimeMinCurrentCultureToNullableDateTimeSuccessTest()
{
var dateTime = new DateTime(1, 1, 1);
string value = dateTime.ToString();
var result = value.ToNullableDateTime();
Assert.AreEqual(dateTime, result);
}
Here's the used extension method:
/// <summary>
/// Converts a string to a nullable DateTime. If the string is a invalid dateTime returns null.
/// Uses the current culture.
/// </summary>
public static DateTime? ToNullableDateTime(this string s)
{
//Don't use CultureInfo.CurrentCulture to override user changes of the cultureinfo.
return s.ToNullableDateTime(CultureInfo.GetCultureInfo(CultureInfo.CurrentCulture.Name));
}
/// <summary>
/// Converts a string to a nullable DateTime. If the string is a invalid dateTime returns null.
/// </summary>
public static DateTime? ToNullableDateTime(this string s, CultureInfo cultureInfo)
{
if (String.IsNullOrEmpty(s)) return null;
DateTime i;
if (DateTime.TryParse(s, cultureInfo, DateTimeStyles.None, out i)) return i;
return null;
}
I think this might be related to some windows date time settings he uses. In theory the ToNullableDateTime(string) should create a new culture info, which is user machine neutral. GetCultureInfo should call new CultureInfo(name, false). The only thing I could come up with, is that there's a cached culture info, which contains some kind of user machine related modified datetime in the s_NameCachedCultures which is checked in the GetCultureInfoHelper (http://referencesource.microsoft.com/#mscorlib/system/globalization/cultureinfo.cs,5fe58d4ecbba7689).
I know, that the CreateSpecificCulture method can return a user modified datetime, if you call it with the same culture than the windows machine. But I always thought, the GetDateTime would return a unmodified datetime in any case.
So there are two questions:
Could it be possible, that a modified CultureInfo be stored in the internal cache?
If so, is the only way to get a unmodified CultureInfo by manually calling new CultrueInfo("xy", false)?
When you do
string value = dateTime.ToString();
this will use CultureInfo.CurrentCulture. You then try and parse this string using...
CultureInfo.GetCultureInfo(CultureInfo.CurrentCulture.Name);
So you are specifically using a culture to parse the string that is different to the one you've created the string with. Of course there will be instances where this doesn't pass.
I'd suggest the issue is on most peoples machines
Assert.AreEqual(CultureInfo.CurrentCulture, CultureInfo.GetCultureInfo(CultureInfo.CurrentCulture.Name));
would pass but on the machine in question it doesn't and neither do your strings.
I'd suggest you probably want to use CultureInfo.InvariantCulture. So...
[TestMethod]
public void DateTimeStringDateTimeMinCurrentCultureToNullableDateTimeSuccessTest()
{
DateTime dateTime = new DateTime(1, 1, 1);
string value = dateTime.ToStringInvariant();
var result = value.ToNullableDateTime();
Assert.AreEqual(dateTime, result);
}
public static string ToStringInvariant(this DateTime? date)
{
if (date.HasValue)
return date.Value.ToStringInvariant();
return null;
}
public static string ToStringInvariant(this DateTime date)
{
return date.ToString(CultureInfo.InvariantCulture);
}
/// <summary>
/// Converts a string to a nullable DateTime. If the string is a invalid dateTime returns null.
/// Uses the current culture.
/// </summary>
public static DateTime? ToNullableDateTime(this string s)
{
//Don't use CultureInfo.CurrentCulture to override user changes of the cultureinfo.
return s.ToNullableDateTime(CultureInfo.InvariantCulture);
}
/// <summary>
/// Converts a string to a nullable DateTime. If the string is a invalid dateTime returns null.
/// </summary>
public static DateTime? ToNullableDateTime(this string s, CultureInfo cultureInfo)
{
if (String.IsNullOrEmpty(s)) return null;
DateTime i;
if (DateTime.TryParse(s, cultureInfo, DateTimeStyles.None, out i)) return i;
return null;
}
I've overseen a little detail. The problem wasn't related to the GetCultureInfo returning a modified datetime, the problem already startet with the dateTime.ToString(); which uses the Thread.CurrentThread.CultureInfo (which equals to the windows culture, which can be modified). The developers machine translates the DateTime(1, 1, 1) to 01.01.01 00:00:00. On any other machine the output is 01.01.0001 00:00:00. So an abbreviated version of the year is used, which seems to be interpreted as the "year 1 of the current century" in the TryParse method (quick sidenode: So it's not possible for users older than 100 years to tell their birthdate with the abbreviated year version).
It's actually an interesting behaviour..
for (int i = 0; i < 100; i++)
{
var year = 1900 + i;
DateTime date = new DateTime(year, 1, 1);
var parsedDate = DateTime.ParseExact(date.ToString("yy"), "yy", CultureInfo.InvariantCulture);
Console.WriteLine("{0}: {1}", year, parsedDate.ToString("yyyy"));
}
leads to:
[...]
1928: 2028
1929: 2029
1930: 1930
1931: 1931
[...]
So, the abbreviated birth date for anyone older 86 would lead to a date in the feature.. but this moves away from the questions context ..
I think there's no real solution the actual problem (beside telling developers to not use local CultureInfos outside of UI strings and never use abbreviated dates for inputs).
We don't have such problems in our code itself, because we use CultureInfo.InvariantCulture for all internal stuff. I just think about the unit test .. I think the test itself is correct. It shows, that the function actually doesn't work correct with abbreviated dates. I will change the behaviour of the ToNullableDateTime() to throw an exception, if a datetime string with a abbreviated year is recognized.
I guess, most probably the CultureInfo is in a cache. This would be very easy to check in a unit test (AreSame).
Instead of DateTime(1, 1, 1) use DateTime(1, 2, 3) and check the ghost ticks. Are they found in the string somewhere? Did you try the same culture (hard-coded culture name) on two machines which had different results?
Also check if the difference is the offset to UTC in your culture.
You probably can use TryParseExact.

Using TimeZoneInfo to adjust a timezone's daylight-savings offset

Below is the method I use that takes in three inputs:
dateTimeInput which is a string that represents a date.
inputFormat are my format strings like this format: yyyy-MM-dd'T'HH:mm:sszzz.
timeZoneStandardName are unique timezone identifiers retrieved from var TimeZoneList = TimeZoneInfo.GetSystemTimeZones(); where the ID is retrieved via timeZoneList.Id.
I mainly used TimeZoneInfo to get my hours and minute offsets because it's very explicit as to what city/timezone it is, e.g. UTC is the input string.
public int dateTimeToUnixTime(string dateTimeInput, string inputFormat, string timeZoneStandardName)
{
DateTime result;
CultureInfo provider = CultureInfo.InvariantCulture;
TimeZoneInfo objTimeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(timeZoneStandardName);
int timeZoneHours = objTimeZoneInfo.BaseUtcOffset.Hours;
int timeZoneMinutes = objTimeZoneInfo.BaseUtcOffset.Minutes;
// if input format is "yyyy-MM-dd'T'HH:mm:sszzz"
if (inputFormat == "yyyy-MM-dd'T'HH:mm:sszzz")
{
result = DateTime.ParseExact(dateTimeInput, inputFormat, provider, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal);
int unixTime = (Int32)(result.Subtract(Epoch)).TotalSeconds;
return unixTime;
}
else
{
// if other input formats
result = DateTime.ParseExact(dateTimeInput, inputFormat, provider, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal);
int unixTime = (Int32)(result.AddHours(-timeZoneHours).AddMinutes(-timeZoneMinutes).Subtract(Epoch)).TotalSeconds;
return unixTime;
}
}
My second method takes in a Unix timestamp and a timezone specifier and outputs as a location-dependent time:
public string unixTimeToDateTime(int unixInput, string outputFormat, string timeZoneStandardName)
{
// output format is "yyyy-MM-dd'T'HH:mm:sszzz"
TimeZoneInfo objTimeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(timeZoneStandardName);
int timeZoneHours = objTimeZoneInfo.BaseUtcOffset.Hours;
int timeZoneMinutes = objTimeZoneInfo.BaseUtcOffset.Minutes;
if (outputFormat == "yyyy-MM-dd'T'HH:mm:sszzz")
{
System.DateTime dateTime = Epoch.AddSeconds(unixInput);
return dateTime.AddHours(timeZoneHours).AddMinutes(timeZoneMinutes).ToString("yyyy-MM-dd'T'HH:mm:ss") + toTimeSpan(timeZoneHours, timeZoneMinutes);
}
// output format is not
else
{
System.DateTime dateTime = Epoch.AddSeconds(unixInput).AddHours(timeZoneHours).AddMinutes(timeZoneMinutes);
return dateTime.ToString(outputFormat);
}
}
For example, here is this method in a Grasshopper component for me to show you what I mean. dateTimeToUnixTime() is my first "clock", while unixTimeToDateTime() is my second "clock"
Given an input of those list of times and a timezone marker of EST, and given those dates (mind you November 2nd 2014 1-2 AM is when we get our DST adjustments again), this method theoretically should adjust its timezone to offset an hour.
I know that TimeZoneInfo supports DST because I can use the below method to show a bool of a respective timezone's DST status.
var TimeZoneList = TimeZoneInfo.GetSystemTimeZones();
for (int i = 0; i < TimeZoneList.Count; i++)
{
Console.WriteLine(TimeZoneList[i].Id + ": " + TimeZoneList[i].SupportsDaylightSavingTime);
}
My question is, what is missing in my dateTimeToUnixTime() method that would allow automatic adjustment and offset depending on the DST conditions?
Assuming that your epoch starts at 1/1/1970, then we can use a DateTimeOffset _1970 as your epoch, add the required seconds to _1970 and then use TimeZoneInfo.ConvertTime to get a date time in the specified time zone with daylight savings adjustments (if applicable). It is important to use a DateTimeOffset and not DateTime so that the correct offset will be kept when getting the string.
private static readonly DateTimeOffset _1970 = new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero);
public string UnixTimeToDateTime(int unixInput, string outputFormat, string timeZoneStandardName)
{
var objTimeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(timeZoneStandardName);
var utcDate = _1970.AddSeconds(unixInput);
DateTimeOffset localDate = TimeZoneInfo.ConvertTime(utcDate, objTimeZoneInfo);
return localDate.ToString(outputFormat);
}
//1970-01-01T13:00:00+13:00
Console.WriteLine( UnixTimeToDateTime(0, "yyyy-MM-dd'T'HH:mm:sszzz", "New Zealand Standard Time"));
//1970-01-01T08:00:00+08:00
Console.WriteLine(UnixTimeToDateTime(0, "yyyy-MM-dd'T'HH:mm:sszzz", "Singapore Standard Time"));
//1969-12-31T19:00:00-05:00
Console.WriteLine(UnixTimeToDateTime(0, "yyyy-MM-dd'T'HH:mm:sszzz", "Eastern Standard Time"));
It is important to note that using TimeZoneInfo may not have accurate information about historical date adjustments, so if that is important to you, then you may be better using a library such as Node Time.

Trouble with DateTime.ParseExact

I am working on a reminder application. The applications stores the reminder Date, Time and DateLastShown (in different fields) in the database and pulls them out to performs checks.
All dates are in "d/MM/yyyy" format. My problem is that when i pull the dates from the DB and try to store back into DateTime format they are still being shown in "M/d/yyyy" format which is not how the app needs to be.
I essentially need to pull the values from the DB do some checks to determine if it's time to show the reminder and do so. It seems rather straight forward, maybe i am making some small error.
Below is my code with comments.
Any help really appreciated.
public void CheckReminders()
{
IQueryable<Reminder> reminders;
DateTime reminderDate;
DateTime reminderTime;
DateTime reminderLastShown;
DateTime todayDate;
DateTime timeNow;
while (true)
{
try
{
db = new StudioManagementEntities();
reminders = from r in db.Reminders
select r;
foreach (Reminder r in reminders)
{
if (r.Enabled == 1)
{
if (r.Recurring == 1)
{
// This is the code i was using before when the date was in "M/d/yyyy" format
// which seems to be default.
reminderTime = DateTime.Parse(r.Time);
timeNow = DateTime.Parse(DateTime.Now.ToLongTimeString());
if (r.DateLastShown != DateTime.Today.ToShortDateString() && timeNow >= reminderTime)
{
FrmReminder frmReminder = new FrmReminder(r.Id, true);
frmReminder.ShowDialog();
r.DateLastShown = DateTime.Today.ToShortDateString();
}
}
else
{
// Now i need to pass in "d/M/yyyy" format but the
// code seems to return in "M/d/yyyy" format.
reminderDate = DateTime.ParseExact(r.Date, "d/MM/yyyy", null);
// Even this returns in wrong format
reminderDate = DateTime.ParseExact("24/01/2013", "d/MM/yyyy", null);
// Have tried with CultureInfo.InvariantCulture too.
MessageBox.Show(reminderDate.ToString());
return;
if (
r.DateLastShown != DateTime.Today.Date.ToShortDateString() //&&
//r.Date == DateTime.ParseExact(DateTime.Today, "d/MM/yyyy", CultureInfo.InvariantCulture).ToString() //&&
//now.TimeOfDay.TotalSeconds >= reminderTime.TimeOfDay.TotalSeconds
)
{
FrmReminder frmReminder = new FrmReminder(r.Id, true);
frmReminder.ShowDialog();
r.DateLastShown = DateTime.Today.ToShortDateString();
}
}
}
}
db.SaveChanges();
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
// Check every minute
Thread.Sleep(60000);
}
}
And the DB table.
If the parsing into the date object is not erroring out, you are just having a problem with your output when you call .ToString().
From the docs:
The ToString method returns the string representation of the date and
time in the calendar used by the current culture.
If you need something other than the user's current culture settings, you can specify that using a format string in the overloaded ToString() method:
var reminderDate = DateTime.ParseExact("24/01/2013", "d/MM/yyyy", null);
MessageBox.Show(reminderDate.ToString("d/MM/yyyy"));
Also, as others have stated in comments, if possible you should be using the date data type in your database instead of storing the values as strings.

Validate DateTime before inserting it into SQL Server database

Is there any way to validate datetime field before inserting it into appropriate table?
Trying to insert with try/catch block is not a way.
Thanks,
Not sure if I'm being overly pedantic there, but DateTime.TryParse will validate whether a value is a valid DateTime object. OP asked about verifying a value before inserting into SQL Server datetime. The range of acceptable values for a SQL Server datetime is "January 1, 1753, through December 31, 9999" That does not hold true for DateTime .NET objects. This script assigns a value of "1/1/0001 12:00:00 AM" to badDateTime and it successfully parses.
DateTime d = DateTime.MinValue;
string badDateTime = DateTime.MinValue.ToString();
Console.WriteLine(badDateTime);
DateTime.TryParse(badDateTime, out d);
However, if you attempted to store that into a datetime field, it would fail with "The conversion of a varchar data type to a datetime data type resulted in an out-of-range value."
A commenter asked why I used 997 for milliseconds, this is covered under SQL Server 2008 and milliseconds but saving you a click, 997 is the largest value you can store in a datetime datatype. 998 will be rounded up to 1 second with 000 milliseconds
/// <summary>
/// An initial pass at a method to verify whether a value is
/// kosher for SQL Server datetime
/// </summary>
/// <param name="someval">A date string that may parse</param>
/// <returns>true if the parameter is valid for SQL Sever datetime</returns>
static bool IsValidSqlDatetime(string someval)
{
bool valid = false;
DateTime testDate = DateTime.MinValue;
DateTime minDateTime = DateTime.MaxValue;
DateTime maxDateTime = DateTime.MinValue;
minDateTime = new DateTime(1753, 1, 1);
maxDateTime = new DateTime(9999, 12, 31, 23, 59, 59, 997);
if (DateTime.TryParse(someval, out testDate))
{
if (testDate >= minDateTime && testDate <= maxDateTime)
{
valid = true;
}
}
return valid;
}
This is probably a better approach as this will attempt to cast the DateTime object into an actual sql datetime data type
/// <summary>
/// An better method to verify whether a value is
/// kosher for SQL Server datetime. This uses the native library
/// for checking range values
/// </summary>
/// <param name="someval">A date string that may parse</param>
/// <returns>true if the parameter is valid for SQL Sever datetime</returns>
static bool IsValidSqlDateTimeNative(string someval)
{
bool valid = false;
DateTime testDate = DateTime.MinValue;
System.Data.SqlTypes.SqlDateTime sdt;
if (DateTime.TryParse(someval, out testDate))
{
try
{
// take advantage of the native conversion
sdt = new System.Data.SqlTypes.SqlDateTime(testDate);
valid = true;
}
catch (System.Data.SqlTypes.SqlTypeException ex)
{
// no need to do anything, this is the expected out of range error
}
}
return valid;
}
Try this without hardcoding sql dateTime value:
public bool IsValidSqlDateTime(DateTime? dateTime)
{
if (dateTime == null) return true;
DateTime minValue = (DateTime)System.Data.SqlTypes.SqlDateTime.MinValue;
DateTime maxValue = (DateTime)System.Data.SqlTypes.SqlDateTime.MaxValue;
if (minValue > dateTime.Value || maxValue < dateTime.Value)
return false;
return true;
}
This is another take on billinkc's answer. However, in this method the .Value property of the min/max is used to avoid parsing and try/catch. Someone mentioned they wanted to ensure they are inserting a valid date into SQL Server. So, I took the approach of returning a date that is valid for SQL Server. This could easily be changed to a boolean method that checks to see if the dateToVerify is a valid SQL Server date.
protected DateTime EnsureValidDatabaseDate(DateTime dateToVerify)
{
if (dateToVerify < System.Data.SqlTypes.SqlDateTime.MinValue.**Value**)
{
return System.Data.SqlTypes.SqlDateTime.MinValue.Value;
}
else if (dateToVerify > System.Data.SqlTypes.SqlDateTime.MaxValue.**Value**)
{
return System.Data.SqlTypes.SqlDateTime.MaxValue.Value;
}
else
{
return dateToVerify;
}
}
<asp:RangeValidator runat="server" ID="rgvalDate" ControlToValidate="txtDate" Text="[Invalid]" Type="Date" MinimumValue="1/1/1753" MaximumValue="12/31/9999" />
OR
custom validator:
protected void cvalDOB_ServerValidate(object sender, ServerValidateEventArgs e)
{
e.IsValid = IsValidSqlDateTime(e.Value);
}
public static bool IsValidSqlDateTime(object Date)
{
try
{
System.Data.SqlTypes.SqlDateTime.Parse(Date.ToString());
return true;
}
catch
{
return false;
}
}
Here is a class with an extension method to allow a check such as if(myDateTime.IsValidSqlDateTime()) { ... }:
public static class DateTimeExtensionMethods
{
public static bool IsValidSqlDateTime(this DateTime dateTime)
{
return !(dateTime < (DateTime) SqlDateTime.MinValue ||
dateTime > (DateTime) SqlDateTime.MaxValue);
}
}
Could you provide a bt more information on where the datetime value is coming from; a web form?
You could simply add a CompareValidator as follows
<asp:CompareValidator ID="CompareValidator1" runat="server"
ControlToValidate="txtDate"
Type="Date"
ErrorMessage="CompareValidator">
</asp:CompareValidator>
If you are mentioning about server side validation of your DateTime field, use DateTime.TryParse. A quick and dirty example will be
DateTime dateValue;
string dateString = "05/01/2009 14:57:32.8";
if (DateTime.TryParse(dateString, out dateValue))
{
// valid date comes here.
// use dateValue for this
}
else
{
// valid date comes here
}
DateTime.TryParse is the best validator
DateTime temp;
if(DateTime.TryParse(txtDate.Text, out temp))
//Works
else
// Doesnt work

DataContractJsonSerializer DateTime implicit timezone conversion

I have a date time in the database and I retrieve it from the database using Entity Framework, I then pass out the data via JSON API through the DataContractJsonSerializer.
The time in the date time field appears to have been adjusted according to the local timezone of the server whilst being processed in DataContractJsonSerializer. The epoch expressed time is 1 hour ahead of the time expected. The DateTime Kind is UTC, but previously it was Unspecified and I had the same issue.
In my application, I wish to convert between timezones explicitly and on the client side, not the server, as this makes more sense. I'm surprised at this implicit functionality as my datetime values should be simple values just like an integer.
thanks
DataContractJsonSerializer will output the timezone portion (+zzzz) if your DateTime.Kind is equal to Local OR Unspecified. This behaviour differs from the XmlSerializer which only outputs the timezone portion if Kind equals Unspecified.
If curious check out the source for JsonWriterDelegator which contains the following method:
internal override void WriteDateTime(DateTime value)
{
// ToUniversalTime() truncates dates to DateTime.MaxValue or DateTime.MinValue instead of throwing
// This will break round-tripping of these dates (see bug 9690 in CSD Developer Framework)
if (value.Kind != DateTimeKind.Utc)
{
long tickCount = value.Ticks - TimeZone.CurrentTimeZone.GetUtcOffset(value).Ticks;
if ((tickCount > DateTime.MaxValue.Ticks) || (tickCount < DateTime.MinValue.Ticks))
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
XmlObjectSerializer.CreateSerializationException(SR.GetString(SR.JsonDateTimeOutOfRange), new ArgumentOutOfRangeException("value")));
}
}
writer.WriteString(JsonGlobals.DateTimeStartGuardReader);
writer.WriteValue((value.ToUniversalTime().Ticks - JsonGlobals.unixEpochTicks) / 10000);
switch (value.Kind)
{
case DateTimeKind.Unspecified:
case DateTimeKind.Local:
// +"zzzz";
TimeSpan ts = TimeZone.CurrentTimeZone.GetUtcOffset(value.ToLocalTime());
if (ts.Ticks < 0)
{
writer.WriteString("-");
}
else
{
writer.WriteString("+");
}
int hours = Math.Abs(ts.Hours);
writer.WriteString((hours < 10) ? "0" + hours : hours.ToString(CultureInfo.InvariantCulture));
int minutes = Math.Abs(ts.Minutes);
writer.WriteString((minutes < 10) ? "0" + minutes : minutes.ToString(CultureInfo.InvariantCulture));
break;
case DateTimeKind.Utc:
break;
}
writer.WriteString(JsonGlobals.DateTimeEndGuardReader);
}
I've run the following test on my machine
var jsonSerializer = new DataContractJsonSerializer(typeof(DateTime));
var date = DateTime.UtcNow;
Console.WriteLine("original date = " + date.ToString("s"));
using (var stream = new MemoryStream())
{
jsonSerializer.WriteObject(stream, date);
stream.Position = 0;
var deserializedDate = (DateTime)jsonSerializer.ReadObject(stream);
Console.WriteLine("deserialized date = " + deserializedDate.ToString("s"));
}
which produces the expected output:
original date = 2011-04-19T10:24:39
deserialized date = 2011-04-19T10:24:39
Thus at some point your Date must be Unspecified or Local.
After pulling it out of the DB convert the kind from Unspecified to Utc by calling
entity.Date = DateTime.SpecifyKind(entity.Date, DateTimeKind.Utc);
and don't forget to assign the return value of SpecifyKind back into your object like I have

Categories