You’re building apps that are hosted in the cloud. Your users can be anywhere across the globe. When you’re viewing a customer service call log, and you see a customer called at 4:30pm and was really upset their service was still down, how do you know how long ago they called? Was the time relevant to your time zone, the customers, or the person who took the call? Was it 5 minutes ago, or 3 hours and 5 minutes ago? Or was it the time zone of the server where the app is hosted? Where is the app hosted? Is it in your office on the west coast, the London office, or is the app hosted in Azure. If it’s in Azure, which data center is it located? Does it matter?
Some more questions, this time in the form of a riddle. Yes, time is a theme here.
- What time never occurs?
- What time happens twice a year?
What we’re dealing with here is a concept I call global time. At any given time, humans refer to time relevant to a given location. Although time is a constant, the way we refer to time is relative.
There is a concept called UTC, in which time is relevant to the same location and would be a constant if the whole world would refer to 5:00pm as the same exact point in time no matter where you are. However, as humans we don’t think that way. We like 5:00pm represents the end of a typical work day. We like to know we all generally eat at 12:00 pm, regardless of where we are on the planet. However, when we have to think about customer call logs being created from multiple locations, at any time, it’s almost impossible to read a string of 4:30 pm, and know its true meaning in the global view of time.
What about time zones?
So we can all wake about the same time to a beautiful sunrise, we can all eat around noon, go out for drinks at sunset, or an 8:00 pm movie and it most likely be dark, time zones were created as chunks of consistent time as our planet rotates around it’s axis.
This seems to make things relatively easy, right? Everyone can refer to 9-5 as common work hours.
What about Daylight Savings Time
Time zones would have been fine if the earth were spinning consistently related to the sun. However, as we happen to spin around and around our own axis, we also spin around the sun.
And, we spin at a slight enough angle that the sun doesn’t always rise at 6am within a given time zone. In 1916, daylight savings time was started where once a year we’d spring ahead, or fall back an hour to try and get the sun to rise and fall about the same time.
To make things a bit worse daylight savings time changed in 2007 when it was felt it would improve our energy consumption.
All of this was fine, when we lived in little towns, didn’t really connect with others across the globe instantly. Even in modern times, when apps were islands upon themselves on each of our “PCs”, possibly connected via floppynet, it wasn’t a problem. When the corporate office was located in Dallas, we all knew that time was relevant to Dallas time. But, in this new world, where there may no longer be a corporate office, or the data center may no longer be located in the basement of the corporate office, we need a better solution.
Problems, problems, but what to do?
In 2008, SQL Server 2008 and .NET Framework 3.5 SP1 introduced a new Date Time datatype called DateTimeOffset. This new type aims to balance the local time relevance humans seek, with the global time needs for our applications to function in a constant.
DateTimeOffset allows humans to view a date & time as we think about it locally, but it also stores the offset relative to UTC. This combination supports a constant, and allows apps to reason over time in their time zone.
An Example
Assume we’re located in the Redmond WA office. It’s 4:35 pm. According to our call log, our upset customer called at 4:30pm. If we store this without the offset, we have no idea how this relates to where we are right now. Did the NY, Redmond or London office take the call? If the user that saved the value was on the east coast, and it used their local time, it would store 4:30pm and -5 as the offset.
Using this combination of time and offset, we can now convert this to west coast time. The app pulls the time from the database. It calculates Pacific Time which is UTC -8, and subtracts another 3 hours (ET (UTC -8) – PT (UTC -5)). Our customer called at 1:30pm Pacific time. That’s 3 hours ago, and there’s no log of activity. Our customer is likely very upset. However, if it were stored as 4:30 pm – 8 (Pacific Time), the customer called just 5 minutes ago, and we can make sure someone from service is tending to their outage.
LightSwitch, Cloud Business Apps and Global Time
With a focus on cloud apps, Visual Studio 2013 LightSwitch and Cloud Business Apps now support the DateTimeOffset data type.
Apps can now attach to databases and OData sources that use DateTimeOffset, and you can now define new intrinsic databases with DateTimeOffset.
Location, Location, Location
When building apps, there are 3 categories of use for the DateTimeOffset data type:
- Client Values
Values set from the client, user entered, or set in javascriptscreen.GlobalTime.ClientValue = new Date();
- Mid-Tier Values
Set through application logic, within the LightSwitch server pipelinepartial void GlobalTimes_Inserting(GlobalTime entity){
entity.MidTierValue = DateTimeOffset.Now;
} - Created/Modified Properties
LightSwitch and Cloud Business Apps in Visual Studio 2013 now support stamping entity rows with Created/Modified properties. These values are set in the mid-tier.
A future blog post will explain these features in more detail
Because the tiers of the application may likely be in different time zones, LightSwitch has slightly different behavior for how each category of values are set.
- Client Values
No surprise, the client values utilize the time zone, or more specifically the UTC Offset of the client. This obviously depends on the devices time, which can be changed, and does change on cell tethered devices as you travel across time zones. - Mid-Tier Values
Code written against the mid-tier, such as the Entity Pipeline, uses the UTC Offset of the servers clock. This is where it gets a little interesting as the clock varies. In your development environment, using your local machine, it’s going to be your local time zone. For me, that’s Redmond WA UTC-8. If you’re publishing to an on-premise server, your datacenter may likely use the local time zone as well. In our Dallas TX example, that would be UTC –6. However, if you’re publishing to Azure, the servers are always in UTC-0 regardless of the data center. This way your apps will always behave consistently, regardless of where the servers are located. - Created/Modified Properties
We considered making these use the Server time zone, but felt it would be better to be consistent regardless of where the server was located. This solves the problem for data that may span on-prem and the cloud. Created/Modified values are always UTC -0
A Quick Walkthrough
Using the above example, let’s look at how these values would be stored in the database, and viewed in the app from New York and Redmond WA
We’ll create a Entity/Table with the following schema:
Under the covers, LightSwitch will create a SQL Table with the following schema.
Just as noted above, we’ll create a screen, and enter values in the ClientValue on the Client, the MidTierValue on the MidTier, and LightSwitch will automatically set the Created property.
For the sake of simplicity, I normalized the values and removed the variable for how fast someone could type and press save on the client and have it be the exact time on the server. Reality often confuses a message.
Let’s assume its 1:30:30 PM on 9/19/2013, which is Daylight savings time in the pacific northwest. What values would be set for our 3 Properties?
Stored As: | Displayed In The Browser In | |||
Property | On Prem | Hosted In Azure | New York (EDT) | Redmond WA (PDT) |
ClientValue | 9/19/2013 1:30:30 PM -07:00 | 9/19/2013 1:30:30 PM -07:00 | 9/19/2013 4:30:30 PM | 9/19/2013 1:30:30 PM |
MidTierValue | 9/19/2013 1:30:30 PM -07:00 | 9/19/2013 8:30:30 PM +00:00 | 9/19/2013 4:30:30 PM | 9/19/2013 1:30:30 PM |
Created | 9/19/2013 8:30:30 PM +00:00 | 9/19/2013 8:30:30 PM +00:00 | 9/19/2013 4:30:30 PM | 9/19/2013 1:30:30 PM |
Notice the Created is always in UTC – 0. The mid tier uses the time of the server. And the browser client, displays all values consistently as the UTC Offset normalizes the times regardless of the time zone in which they were captured.
What date is it?
That wasn’t so bad, just a little math on the hour. However, let’s assume we’re working late that same night, and it’s now 10:30 PM on 9/19/2013 in Redmond WA. We’re still in daylight savings time, but what do you see different here?
Stored As: | Displayed In The Browser In | |||
Property | On Prem | Hosted In Azure | New York (EDT) | Redmond WA (PDT) |
ClientValue | 9/19/2013 10:30:00 PM -07:00 | 9/19/2013 10:30:00 PM -07:00 | 9/20/2013 1:30:00 AM | 9/19/2013 10:30:00 PM |
MidTierValue | 9/19/2013 10:30:00 PM -07:00 | 9/20/2013 5:30:00 AM +00:00 | 9/20/2013 1:30:00 AM | 9/19/2013 10:30:00 PM |
Created | 9/20/2013 5:30:00 AM +00:00 | 9/20/2013 5:30:00 AM +00:00 | 9/20/2013 1:30:00 AM | 9/19/2013 10:30:00 PM |
Although it’s 9/19 in Redmond, New York is 3 hours ahead. It’s now 1:30 AM 9/20. Also notice that our Azure servers are also working in 9/20.
Standing on the edge
It’s now the 2nd Sunday in March 2014. March 9 to be specific. This is the night before daylight savings time begins. Lets see what happens
Stored As: | Displayed In The Browser In | |||
Property | On Prem | Hosted In Azure | New York (EDT) | Redmond WA (PDT) |
ClientValue | 3/9/2014 11:30:00 PM -07:00 | 3/9/2014 11:30:00 PM -07:00 | 9/20/2013 2:30:00 AM | 9/19/2013 10:30:00 PM |
MidTierValue | 3/9/2014 11:30:00 PM -07:00 | 3/10/2014 7:30:00 AM +00:00 | 9/20/2013 2:30:00 AM | 9/19/2013 10:30:00 PM |
Created | 3/10/2014 7:30:00 AM +00:00 | 3/10/2014 7:30:00 AM +00:00 | 9/20/2013 2:30:00 AM | 9/19/2013 10:30:00 PM |
In this case, not only are the times split across dates between New York and Redmond WA, but we’ve also crossed into daylight savings time. Instead of New York being 3 hours ahead, it’s actually 4 hours ahead, for the same point in time. Ouch…
My Head Hurts
Attempting to calculate all these different time zones, daylight savings time – if it even applies to your time zone, can certainly be confusing. So, what to do? Well, that of course depends. However, just as we learned in math, we need to find the least common denominator. In most cases, if you convert to UTC, you can avoid the variables. You can use TimeSpan to calculate the differences in two DateTime periods. Then, you can re-apply the UTC offset. However, none of this would be possible if the values you’re attempting to calculate didn’t include the UTC Offset.
Thus, LightSwitch, and Cloud Business Apps now support this important element for you to calculate Dates & Times across your globally deployed application.
What about the riddles?
Ahh, you’re still here?
- What time never occurs?
2:00 am on the 2nd Sunday of March
As of 2007, the 2nd Sunday in March begins daylight savings time. At 2:00 am, the clocks will “leap” forward to 3:00 am skipping the 2:00 am hour all together - What time happens twice a year?
1:00 am, the first Sunday of November
As of 2007, the first Sunday in November ends daylight savings time. At 2:00 am, the clocks roll back to 1:00 am to end daylight savings time.
Some references
- Daylight Savings Time
- Changed in 2007
- http://en.wikipedia.org/wiki/Time_zone
- US Navy: Astronomical Applications Department - List of dates for Daylight Savings Time from 2006 forward
- DateTimeOffset
- TimeSpan
Thanks,
Steve Lasker
Program Manager, Visual Studio
http://blogs.msdn.com/SteveLasker