Access TimeZoneInfo from SQL 2005 Server - asp.net

The .NET TimeZoneInfo class is great and I thought it would answer all my issues with recording data from multiple time zones in my SQL 2005 database.
To convert a UTC datetime in the database to any other time zone i'd just get the time zone into a TimeZoneInfo class using TimeZoneInfo.FindSystemTimeZoneById() and then call the TimeZoneInfo.ConvertTimeFromUtc(). Brilliant! I'd just call this from the SQL .NET CLR!
BUT...TimeZoneInfo has a Host Protection Attribute of MayLeakOnAbort.
When I use VS 2008 to create an SQL function or stored procedure, I cannot even see the system.TimeZoneInfo class nevermind use it. I'm assuming also that even if I could somehow reference the TimeZoneInfo class, I would probably get some sort of security exception if I tried to register the assembly in SQL Sever 2005.
Help! Is there any way to access TimeZoneInfo class and all its riches from SQL Server 2005?
NB: I've just added this caveat after the first Answer:
We have sites at different locations around the world. We need to store local time and UTC time in the database against events which may require trending at Site level. A trend may consist of over 52,000 data points over a year, so, for efficiency, I cannot just store times in UTC in the DB and convert every datapoint on the client. Thus I need the ability, within the DB to convert a local time in any timezone to and from UTC time.

I just finished doing this on a SQL 2008 database.
First I had to set the DB to trustworthy and verify the owner was correct.
use [myDB]
go
alter database [myDB] set trustworthy on
go
exec sp_changedbowner 'sa'
go
Next, I created a .NET solution
Imports System
Imports System.Data
Imports System.Data.SqlClient
Imports System.Data.SqlTypes
Imports Microsoft.SqlServer.Server
Imports System.Collections.ObjectModel
Imports System.Runtime.InteropServices
Partial Public Class StoredProcedures
<Microsoft.SqlServer.Server.SqlProcedure()> _
Public Shared Sub sp_ConvertTime(ByVal UTCTime As DateTime, ByVal ZoneID As String, <Out()> ByRef Output As DateTime)
Dim sp As SqlPipe = SqlContext.Pipe
Dim ConvertedTime As DateTime
Dim tzUTC = TimeZoneInfo.FindSystemTimeZoneById("UTC")
Dim tzNew = TimeZoneInfo.FindSystemTimeZoneById(ZoneID)
ConvertedTime = TimeZoneInfo.ConvertTime(UTCTime, tzUTC, tzNew)
Output = ConvertedTime
sp.Send(ConvertedTime)
ConvertedTime = Nothing
tzUTC = Nothing
tzNew = Nothing
sp = Nothing
End Sub
End Class
Before deployment I set the Permission level to Unsafe.
Next I deployed it out I checked the Output window for Build errors and corrected those.
Here is the SQL Test
DECLARE #UTCTime datetime
DECLARE #ZoneID varchar(21)
DECLARE #NewTime datetime
SET #UTCTime = GETUTCDATE()
SET #ZoneID = 'Central Standard Time'
exec sp_ConvertTime #UTCTime, #ZoneID, #NewTime OUTPUT
select #NewTime AS NewTime

I don't know if you can access the TimeZoneInfo class from SQL, but it's generally considered good practice to stick to UTC in the database, with the client doing the translation into the local time.
So every time you write to the database, you translate from the local time into UTC, every time you read from the database, you translate from UTC into the local time.
EDIT: From what I understand of the problem, the solution that I would still recommend is:
1) Store the dates in UTC in the database.
2) Calculate the local time in the client.
2 can be done in a number of ways. The recommended way is to set the DateTimeMode to Local (or Utc) in the DataColumn class (*). If you need a report in local time, use Local, if you need it in UTC, use UTC.
(*) Please note that there are problems with the designer in Visual Studio, see blog post: http://bethmassi.blogspot.com/2006/01/serializing-data-across-time-zones-in.html,
bug report: http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=96118

I ran into this same problem because I wanted to convert between local and UTC in a query that was being utilized by reporting services. I went through what seems to be the same exact struggles you are going through with this. My solution...
I started out writing a stand alone app that went through the TimeZoneInfo object and wrote entries to a TimeZoneInfo table in my database. I stored all the offsets (including daylight savings offsets) for every year between a start year and end year (these were arguments to the stand alone app).
From this table, I was then able to create some sql functions that would take a date in utc or local and the timezone, use the TimeZoneInfo lookup table to get the right offset for the right time of year and timezone, and return the datetime converted to UTC or local.
Unfortunately, I still wasn't done yet. I had to create a CLR function that returned the current timezone of the system using a library that WAS safe for SQL Server (unlike the TimeZoneInfo object). I don't have access to my code at the moment, but I beleive I used the TimeZone object.
http://msdn.microsoft.com/en-us/library/system.timezone_members.aspx
To summarize, I had a CLR function that returned the system's time zone, an app that produced a timezone look up table with DLS specific information for a range of years. I topped it all off with a stored procedure that took in a timezone and a date to convert and its been working beautifully since.
I understand this is a HUGE work around to do something that seemed quite simple, but it got the job done in a safe way.

Here's a solution:
Create a CLR stored proc or UDF, which wraps the functionality of the TimeZoneInfo class. Follow this guide: http://www.codeproject.com/KB/cs/CLR_Stored_Procedure.aspx
TimeZoneInfo requires .NET 3.5. However, System.Core v3.5 will not pass verification in Sql Server 2005. So you have to do a CREATE ASSEMBLY for System.Core. See details: http://weblogs.asp.net/paulomorgado/archive/2009/06/13/playing-with-sql-server-clr-integration-part-iv-deploying-to-sql-server-2005.aspx
Note that you need to register System.Core as UNSAFE... so you DBA might have issues with it.
Furthermore, even if you were deploying to Sql Server 2008 (which includes .NET 3.5), your custom assembly would have to be UNSAFE since it uses unsafe methods from TimeZoneInfo:
http://social.msdn.microsoft.com/Forums/en/sqlnetfx/thread/d0515862-eb87-4a13-bab4-0e343983823a
I tried this and got a message regarding MayLeakOnAbort.
If UNSAFE is ok in you environment, you should be able to do it.

We've been looking for this for a while but haven't been able to find a good way to do it. I think that was on the list of things to be added in SQL 2008 if I remember from looking at it a while back.

Have you checked out this article on Channel9? Seems to do what you are looking for in a CLR function... although I don't think you are going to get ALL of the goodies you want access to... but it is a start.
http://channel9.msdn.com/playground/Sandbox/139123/
Problem that one of the posters on that thread mentions though is that it is an unsafe assembly because of the p/invoke.

After fighting with the same problem for years, I finally decided to build a solution for SQL Server Time Zone Support. The project uses the standard IANA time zones, as listed here.
For example:
SELECT Tzdb.UtcToLocal('2015-07-01 00:00:00', 'America/Los_Angeles')
I realize this isn't exactly what was asked, but I think it will solve the problem equally well.

Related

How can I get SignalR to stop changing my date/time values?

I have a simple class that contains a DateTime property. When I set the value of this property using DateTime.Now() the value is correct. However, when I pass this class object as a parameter via SignalR, on the receiving end SignalR has changed the DateTime so that it no longer matches the original DateTime. How can I get SignalR to stop manipulating my DateTime values? This issue seems to have started with a recent update to the latest SignalR nuget packages.
This seems to be a problem when the hub and the client are in two different time zones. It seems Microsoft is trying to help, by adjusting the date/time to the local time zone, but I want the original value, not the value Microsoft "thinks" I want.
When you want two systems to communicate with each other I would recommend using always the DateTime in UTC for various reasons. In your case you have two options here:
1 - Send the date as string string date = DateTime.Now.ToString(CultureInfo.InvariantCulture); so on the client side you just need to parse the datetime like DateTime.Parse(date, CultureInfo.InvariantCulture);.
2 - Send the date in UTC like DateTime.UtcNow; so event if the SignalR tries to change the date, it will have the DateTime.Kind as UTC. In this case or you will get the current date correctly, or you just adjust on the client side to the local time like receivedDate.ToLocalTime();
This one was a real head scratcher as I had the exact same issue. The client was storing the correct time but by the time it hit the Hub... it had changed. I'm really glad you posted this as I wouldn't have imagined this issue being possible. #Kiril1512 posted the correct resolution. In my case I used his second option as I didn't want to change my model... although simply converting to a string would have been simpler. While I did follow his suggestion and convert everything to DateTime.UtcNow()... I am thinking this was unnecessary as I noticed even previously stored dates converted correctly. This makes me think that either this isn't necessary to do, or dates are converted to Utc when they hit the Hub automatically which may be what the issue was to begin with?
Either way I posted this as I discovered that converting this date back to Local time was a little more involved. Here is how I ended up doing the conversion which resolved this issue for me that I gathered from this resource:
DateTime convertedDate = DateTime.SpecifyKind(
DateTime.Parse(msg.CreatedOn.ToString()),
DateTimeKind.Utc);
var kind = convertedDate.Kind;
DateTime dt = convertedDate.ToLocalTime();

spring.jackson.date-format add extra 4 hours to given input

I want to format java.util.Date.I get year month and day correctly but when I insert data to database I get 4 more hour.For example if I insert 2017-12-18 12:00
then inserted row will be 2017-12-18 16:00
My controller:
#RequestMapping(value = "/add", method = RequestMethod.POST)
public String insertRequest(#RequestBody EmployeeRequest employeeRequest) {
System.out.println(employeeRequest.getDate());
requestService.insertRequest(employeeRequest);
return "inserted and sent mail";
}
My model :
#NotNull
private Date date;
My application.properties
spring.jackson.date-format=yyyy-MM-dd HH:mm
Thanks in advance
This is not a date formatting error, this is very likely related to TimeZone. Depending on how your Database and Application Server are configured it appears that they are using different default timezone's.
Have you tried reading the data back OUT of the database into Java? If the database is properly storing the timezone then it should Just Work when you read it back into your java object. If not, i'd suggest first updating your Date/Time format to include the timezone e.g. spring.jackson.date-format=yyyy-MM-dd HH:mm:ssZ. Then the database should at least have access to the appropriate timezone.
If this doens't work, the proper mitigation really depends on your application and how it needs to make use of time / timezones. It could be that what you have is actually fine, it's just that when you manually inspect the database it's displaying to you the time in the default timezone. You could also just set your database and app servers to use the same timezone, but this can be a configuration nightmare.
Side note: as some general advice, I'd high recommend using either java.time.* or jodatime libraries instead of java.util.Date if at all possible. This isn't related to this specific issue, but they are much more robust date/time libraries than the legacy java.util.Date

Date/DateTime object type in AmbientDataFramework

I'm having a bit of an issue with the Date (java) / DateTime (.net) type when using the Ambient Data Framework in SDL Tridion 2009.
I'm setting a claim value in java like this:
Date myDate = rs.getDate("DATE_FIELD_IN_DB");
store.put(CLAIM_URI_DATE, myDate);
This is great and life is fantastic :)
In my web application (which is .net) I need to get this value from the claim store, when I try to do so, it fails:
My code is:
if (_store.Contains(new Uri("taf:claim:company:date"))) {
DateTime claimdata = _store.Get<DateTime>("taf:claim:company:date");
Response.Write(claimdata.ToString());
}
And the error message is:
[RuntimeException] Codemesh.JuggerNET.NTypeValue.Throw(Int64 inst)
+373 Codemesh.JuggerNET.JavaClass.ThrowTypedException(Int64 inst) +1365 Codemesh.JuggerNET.JavaMethod.CallInt(JavaProxy jpo) +233 Tridion.ContentDelivery.AmbientData.JuggerNetTypeHelper.FromProxyObject(Object
proxyObject) +738
Tridion.ContentDelivery.AmbientData.ClaimStore.Get(Uri claimUri) +109
I've also tried obtaining as an object, which i then cast to DateTime. I'm obviously making a really basic error here, any help would be greatly appreciated.
I have also read the SDL documentation here : http://sdllivecontent.sdl.com/LiveContent/content/en-US/SDL_Tridion_2011_SPONE/concept_26FDE76C277D43F893175E512EFDF09A which shows the java Data object is converted to the .net DateTime.
Thanks
John
UPDATE
As per the suggestions of Peter, i've check and ensured the correct jar files are in place.
It might be worth mentioning that the date is stored in a SQL database as a 'Date' type, a typical value doesn't include a timestamp, for example '2011-09-03'. Still this all functions great in Java and I can work with the value as a date. Still when getting the value in .Net the typed exception is thrown.
It should convert it automatically. Are you sure you are not using a different Date object than java.util.Date, though? There's quite a few of them out there...
If you really cannot get it to work, you can work around it by storing the date as a string (in ISO 8601 format) and then using DateTime.Parse on the .NET side.
But like I said: it normally works fine so there's probably something else going wrong.
Is your ADF working fine for other things in .NET? Maybe it's missing a setup step for the .NET side of things...
A bit of a long shot, but can you verify that the right JDBC driver is installed as mentioned in this question/answer?
Exception loading CustomMeta from Tridion Broker Service (2009 SP1)
The Date value, does it contain the time? Can you ensure that it does when you get the value from the store?

dates behaving differently on different dbs?

I have an asp.net webpage, with a jQuery datepicker on it.
I am in the UK, so when I enter 28/02/2010, I expect it to resolve to 28th Feb 2010.
This is working as expected on my local dev env - but not on our QA or prod-like envs - or one of the other dev machines. In these cases it seems to attempt to resolve it to American date format - and fails validation as it is out of range.
The jQuery seems to generate the correct date each time - which leads me to think it may be a database issue.
I am using SQL Server 2005, my collation is Latin1_General_CI_AS, my colleagues are using collation SQL_Latin1_General_CP1_CI_AS, and a Chinese one.
Given that we don't have control over the prod SQL Server installation (just our db), what is the best way to make this work in a standard way? Change the db settings, or the code that uses it?
Thanks in advance!
- L
[EDIT to add code info]
This is my view code to call the datepicker:
<%=Html.TextBox("DateOfBirth", Model.DateOfBirth.ToShortDateString(), new { #class = "datepicker" })%>
Here is the js for the datepicker:
DatePickerSettings = {
setup: function () {
$(".datepicker").datepicker({
dateFormat: 'dd/mm/yy',
changeMonth: true,
changeYear: true
});
}
};
And this is how I specify the date in the model:
[Required]
[DisplayName("Date of Birth")]
public virtual DateTime DateOfBirth { get; set; }
The date appears correct inthe controller and repository... until it hits the db.
Thanks :)
I was hoping to wait until you'd updated the question with some more information, but as I've seen some answers suggesting that you change the string format you use to talk to the database...
Don't send dates as raw text in SQL queries.
Use a parameterized query, which means you don't need to worry about formatting the value at all. Then you've just got to make sure that you can get the date format correct between the browser and ASP.NET.
Aside from anything else, if you're including user data in SQL queries directly, you'll generally be opening yourself up to SQL injection attacks. Always use parameterized queries (unless your web app is really a "run this SQL" test tool...)
If you're already using parameterized queries, then the problem is likely to be between the browser and ASP.NET, and the database part is irrelevant. Divide and conquer the problem: chase the data as it passes through different layers (browser, jQuery, ASP.NET etc) until you find out where it's gone wrong. Don't even think about a fix until you know where it's gone wrong.
Is your page Culture aware?
You can determine UI Cutlure information for different browsers(locales) and have your ASP.NET Culture constant.
The Culture value determines the results of culture-dependent functions, such as the date, number, and currency formatting, and so on. The UICulture value determines which resources are loaded for the page
Check out this MSDN link:
How to: Set the Culture and UI Culture for ASP.NET Web Page Globalization
http://msdn.microsoft.com/en-us/library/bz9tc508(v=VS.85).aspx
Use CONVERT to change the date format to a standard that is accepted across all environments.
CAST and CONVERT
I'd have to see the code that interprets the dates to know for sure, but a likely suspect is the Region and Language settings on the machines where the code is running. Make sure it is set appropriately for your region.
However, if you can't change settings on the servers, you should probably explicitly use CAST or CONVERT in SQL Server to force it to parse it in the region specific way you expect the data will be entered.
You also need to check your ASP.Net layer, and see what it is running in.
Check the machine configuration and check they are set to run in the same date/time/region.
Change your code to use yyyymmdd format.
As far as i know it works in all the DBs
Just to add another opinion here, I find dd/mmm/yyyy the best date format to send to databases as it's completely unambiguous across cultures.

How to show a UTC time as local time in a webpage?

I have a database that holds a time as UTC. This time can be shown in a webpage, so I’ve been asked to show it as local time in the page as it can be viewed from any country. A colleague mentioned something about getting the country settings from the current thread (on the server) but I couldn’t find any details on this. Is what I want to do possible?
If you (and your website) are comfortable with javascript, there is a very easy way to accomplish this.
First, on the server side, you would have the UTC date/time formatted in RFC 3339 format (the standard for internet time used by, among other protocols, icalendar). The basic syntax of RFC 3339 is:
YYYY-MM-DDTHH:MM:SS
Such that where I am, the time would be:
2010-05-04T05:52:33
But when the time is not local, but UTC, you add a Z to the end to denote this. So in my case, since I'm at -0500 hours from GMT, the same time above would be:
2010-05-04T10:52:33Z
So, first you get the server to output the above to your web page's javascript. The javascript can then parse that timestamp and it will output the date and time adjusted to the browser's time zone (which is determined by the computer hosting the browser). You should remember that if a user is from Tokyo and is viewing your website in Spain, they will see the timestamp in Tokyo time unless they've adjusted their computer's clock.
So the javascript would be:
var time_string_utc = some_server_variable; // timestamp from server
var time_string_utc_epoch = Date.parse(time_string_utc);
var time_utc = new Date();
time_utc.setTime(time_string_utc_epoch);
At this point, you have a javascript date object set to your UTC timestamp. A quick explanation of what happens above:
The first variable assumes you have passed the timestamp string to that variable from the server.
The second variable uses the Date.parse() method to convert the string to an epoch timestamp.
The third variable creates the unset Date object.
The last line line uses setTime method, which sets a Date object from an epoch timestamp.
Now that you have the object, you can output it to the user as you see fit. As a simple experiment, you can use:
document.write(time_utc);
which, if you are in my timezone using the UTC timestamp I started off with:
2010-05-04T10:52:33Z
would give:
Tue May 04 2010 05:52:33 GMT-0500 (CST)
but you can use various javascript methods to format the time into something much more pleasant looking.
No need to guess the user's country or even adjust your timestamp, so long as you trust the user's local browser/computer time zone.
Again, the short version:
var time_string_utc = some_server_variable; // UTC time from server
var time_string_utc_epoch = Date.parse(some_server_variable);
var time_utc = new Date();
time_utc.setTime(time_string_utc_epoch);
document.write(time_utc);
Also consider reading this thread which dives deeper in the dos and donts of handling daylight saving problem across timezones
Of-course is possible. You just need to find that country settings to detect the country your user comes from, and then modify the displayed date to fit that country's time.You can also find another way to detect the user country.(maybe from his ip address).
I am sure that the best way to manage this is let your colleague know that you need more details about the country settings implementation on your project and how can you use it.
EDIT:
Thinking about it I don't think it is possible to display the local time once you have the client’s culture. I'd certainly be interested to see your colleagues suggestion.
Getting the US culture, for example, won't help as the US has many timezones.
I think using Javascript like Anthony suggests would be the way to go...
OLD:
You can override the InitializeCulture() method with code that sets the current (chosen or browser reporting) cultures:
Thread.CurrentThread.CurrentCulture =
CultureInfo.CreateSpecificCulture(selectedLanguage);
Thread.CurrentThread.CurrentUICulture = new
CultureInfo(selectedLanguage);
this is how I've done it in PHP in the login file which connects to MySQL database:
//set local timezone
date_default_timezone_set('America/Denver'); //use local TZ
Then, every time you access the DB, you are setting the timezone for that session and any input/output associated with it.

Resources