One of the tickets I worked on this week was to add a “date last updated” to some stock market data being displayed. I needed to display “As of [last update date] (30min delay)” underneath the market data table.
The data is retrieved from an xml feed that contains the last updated time in the format “HH:mm dd/MM/yyyy”. The time used is local to Sydney Australia, “AUS Eastern Standard Time”, 10 hours ahead of GMT and +11 in Summer. I wanted to ensure the last updated date was not in the future for people viewing from a different time zone; Brisbane for example, which is also +10 GMT but doesn’t have daylight savings.
The way I did this was to send the UTC time back to the client browser and use javascript to convert that to the local time zone of the client. I also wanted a fail safe and included the Sydney time as default. I created a server-side property on my ascx to convert the time I recieved in my stock data feed into GMT:
protected DateTime? LastUpdatedDateGmt
{
get
{
if (!LastUpdatedDate.HasValue) return null;
// Time comes in as Sydney time.
TimeZoneInfo sydneyTimeZone = TimeZoneInfo.GetSystemTimeZones().First(p => p.StandardName == "AUS Eastern Standard Time");
return TimeZoneInfo.ConvertTimeToUtc(LastUpdatedDate.Value, sydneyTimeZone);
}
}
The TimeZoneInfo class's GetSystemTimeZones provides an enumeration of all timezones and are Daylight Savings aware. I use the Sydney time zone of "AUS Eastern Standard Time" to convert from and into Utc (GMT 0) time. The html I sent back to the browser looks like the following:
<div class="refreshtime">
As of <span id='time' class='time' utctime='Thu Dec 01 2011 22:54:00'>2011-12-02 9:54:00</span> (30min delay)
</div>
<script type=”text/javascript”>MarketData.ConvertStockLastUpdatedToLocalTime();</script>
With the above, at a minimum a site visitor in my time zone will see last updated time 1 hour or so in the future. Not bad, but could be better. Next I use some javascript to find the span element containing the utctime attribute and extract the utc time of the last update. I can then convert this to the browsers local time zone.
var MarketData = {
ConvertStockLastUpdatedToLocalTime: function() {
var timeElements = getElementsByAttribute(document, '*', 'utctime');
if (timeElements != 'undefined' && timeElements) {
var oAttribute;
var oCurrent;
for (var i = 0; i < timeElements.length; i++) {
oCurrent = timeElements[i];
oAttribute = oCurrent.getAttribute && oCurrent.getAttribute('utctime');
var d = new Date();
var utcTime = new Date(oAttribute);
var localTime = new Date(utcTime + ‘ GMT’);
var localTimeString = localTime.getFullYear() + '-' + pad(localTime.getMonth() + 1, 2) + '-' + pad(localTime.getDate(), 2) + ' ' + pad(localTime.getHours(), 2) + ':' + pad(localTime.getMinutes(), 2) + ':' + pad(localTime.getSeconds(), 2);
timeElements[i].innerHTML = localTimeString;
}
}
}
};
The above finds all the elements in the page with the utctime attribute and extracts that value for conversion. Once extracted I just need to assign it a timezone and let the Data function take care of the conversion to local time for me. I converted for the format “yyyy-MM-dd HH:mm:ss” format by using the custom Pad function.
function getElementsByAttribute(oElm, strTagName, strAttributeName, strAttributeValue) {
var arrElements = (strTagName == "*" && oElm.all) ? oElm.all : oElm.getElementsByTagName(strTagName);
var arrReturnElements = new Array();
var oAttributeValue = (typeof strAttributeValue != "undefined") ? new RegExp("(^|\\s)" + strAttributeValue + "(\\s|$)", "i") : null;
var oCurrent;
var oAttribute;
for (var i = 0; i < arrElements.length; i++) {
oCurrent = arrElements[i];
oAttribute = oCurrent.getAttribute && oCurrent.getAttribute(strAttributeName);
if (typeof oAttribute == "string" && oAttribute.length > 0) {
if (typeof strAttributeValue == "undefined" || (oAttributeValue && oAttributeValue.test(oAttribute))) {
arrReturnElements.push(oCurrent);
}
}
}
return arrReturnElements;
}
function pad(number, length) {
var str = '' + number;
while (str.length < length) {
str = '0' + str;
}
return str;
}