Saturday, October 25, 2008 

Amazon EC2 - Now with Windows Server 2003!

Amazon EC2 now supports Windows Server 2003 based virtual machines: Amazon Elastic Compute Cloud (EC2) Running Microsoft Windows Server and SQL Server. Grab a Windows virtual machine for $USD0.125 an hour.

Quick recap: EC2 is akin to virtual private server hosting, where you pay by the hour. EC2 is administered via a service that allows you instantly create any number of virtual machines, and manage firewall rules.

I was going to write up a quick guide on firing up a Windows box - but this has already been covered well in the Getting Started Guide

Some lessons learned along the way: I had to download the new EC2 API tools - extracted into a new folder the 'ec2-describe-keypairs' knew about keys I'd created previously. Where do the keys actually get stored? ec-add-keypair explains what is happening - your public key is sent to Amazon's servers. Your private key you save to a file - in the Linux world you use this private key in Putty to connect to your server. In the Windows world you pass your private key file to ec2-get-password to get your server password from Amazon.

Labels:

Monday, October 06, 2008 

Playing with JQuery and ASP.NET MVC

UPDATE: Thanks for the comment Chris. I guess after years of wrestling with Javascript I didn't think of reading in the UI elements to set the cookie. If the cookie was storing something like timezones, I could stick the timezone key into an expando property of the div - nice!

I've updated the source code zip, and the demo.


jQuery and ASP.NET MVC have both caught my eye as interesting web technologies. ASP.NET MVC is a Model-View-Controller approach to web development in .net. I've heard it described as Microsoft thinking up another way they could've upgraded from ASP Classic to ASP.NET. jQuery is a very handy javascript library which abstracts a lot of standard client-side functionality into a cross-browser library, e.g DHTML and Ajax. Microsoft must agree on the usefulness of jQuery - as Microsoft have plans to include jQuery in their own products: jQuery and Microsoft.

To get my hands dirty with ASP.NET MVC and jQuery I'm using it to rebuild a languishing project of mine: www.time2call.net. I've developed a little spike to test a timezone picker scenario. There's a few design goals I had in mind:

  1. An ajax 'picker' - displays the results via Ajax, stores the options in a cookie. Subsequent visits can re-create the UI server-side.
  2. The same server-side code to build UI HTML for Ajax AND 'from cookie' page load. I.e no duplication of HTML build code client-side and server-side.
  3. Minimal / incredibly readable client side-code.
  4. All the whizzy Ajax loading animation people expect.
  5. Maintain Ajax context, so options appear on the page in the order they were clicked, regardless of the order of processing on the server.

See the spike: here - grab the code: MvcJQueryMuckingCode.zip. The code is built against ASP.NET MVC Preview 5.

Some notes / questions:

  • jQuery Callback Contexts - maintaining context between Ajax request / response.
  • Partial Rendering & View Engines in ASP.NET MVC - rendering the ViewUserControl for the Ajax request.
  • Passing ViewData from Controllers to Views - I don't get the "ViewData.Products" style accessor in my example - I'm accessing the collection through "ViewData.Model". Is this something that's changed in release 5?
  • To remove a color option I pick up the position of delete link:
    var index = $('.delLink').index(this);
    Then use this to get the DIV in the same position:
    $('#fldColors > div').eq(index)...
    Is there a better way of doing this?
  • When I delete an option I'm expecting the color DIVs to be in the same position as the options in the cookie. That's why I remove the DIV in the fadeout callback. So there's 500ms there where that could go wrong :(. I experimented with selecting just the 'visible' DIVs. But the the div's display style remains 'block' until it disappears.

Labels: ,

Wednesday, October 01, 2008 

Converting Timezones in .net

Ever had the need to convert a date/time to another timezone? I had to do it a while back to display publishing deadlines for a website used in Australia and New Zealand. Back then I used a wrapper for the kernel32 function SystemTimeToTzSpecificLocalTime from the CodeProject article Convert between UTC (Universal Co-ordinated Time) and local time. As of version 3.5 of the framework there is now the TimeZoneInfo class making this much easier to do in managed code. But both come with some small caveats.

The API function works with daylight saving data retrieved from the registry. To use it you extract the data in the registry key "SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\[Timezone name]\Tzi", and memcopy this into a TIME_ZONE_INFORMATION structure.

The TIME_ZONE_INFORMATION structure contains two SYSTEMTIME structures StandardDate, and DaylightDate date - which specify when the people in this timezone wind their clocks back / forward. This value in the registry is only ever the daylight rules for 'right now' - so we can run into problems with dates ~12 months outside of this range. Windows treats these rules as if they have been in effect forever - even when they are changed by a Windows Update.

As an example: lets say we are working on an app that stores ATM transactions in UTC time - we want to convert them to a local time to print onto statements for a bank in Los Angeles. A customer made withdrawals at UTC times: 31-Mar-2006 17:00, and 31-Mar-2007 17:00. The code to convert the UTC time using the wrapper from the CodeProject article looks like this:

DateTime utcTime = DateTime.Parse("2006-03-31T17:00:00");
DateTime converted = TimeZoneInformation.FromUniversalTime("Pacific Standard Time", utcTime);
Console.WriteLine(converted.ToString("s"));

utcTime = DateTime.Parse("2007-03-31T17:00:00");
converted = TimeZoneInformation.FromUniversalTime("Pacific Standard Time", utcTime);
Console.WriteLine(converted.ToString("s"));

Which gives:

UTC:2006-03-31T17:00:00 Los Angeles:2006-03-31T10:00:00
UTC:2007-03-31T17:00:00 Los Angeles:2007-03-31T10:00:00

So 5pm UTC converts to 10pm in LA - sounds about right? Well no.. I've obviously picked some dates where the daylight settings in the registry give us the wrong conversion. From 1987 to 2006 the USA East coast wound their clocks forward on the first Sunday of April, this changed in 2007 to the second Sunday of March. So on 31-Mar-2006 daylight savings hadn't kicked in, LA should've been 8 hours behind UTC. On 31-Mar-2007 daylight savings did apply - and we've correctly calculated LA time as 7 hours behind UTC. Windows is applying the second Sunday of March rule in 2006 - as this is the only rule it knows about.

That is - unless you are on Vista or Server 2008. The newer version of the Windows API is able to read the DYNAMIC_TIME_ZONE_INFORMATION - from a collection of multiple rules in the "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\[Timezone name]\Dynamic DST".

Enter the .net TimeZoneInfo class - lucky for us this class can interpret the dynamic DST data from the registry (even in XP). Here's the previous example using the TimeZoneInfo class:

TimeZoneInfo tzi = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time");
DateTime utcTime = DateTime.Parse("2006-03-31T17:00:00");
DateTime converted = TimeZoneInfo.ConvertTimeFromUtc(utcTime, tzi);
Console.WriteLine("UTC:{0:s} Los Angeles:{1:s}", utcTime, converted);

utcTime = DateTime.Parse("2007-03-31T17:00:00");
converted = TimeZoneInfo.ConvertTimeFromUtc(utcTime, tzi);
Console.WriteLine("UTC:{0:s} Los Angeles:{1:s}", utcTime, converted);

The output:

UTC:2006-03-31T17:00:00 Los Angeles:2006-03-31T09:00:00
UTC:2007-03-31T17:00:00 Los Angeles:2007-03-31T10:00:00

Seeing what I expect for the 2006 date. So everything is covered? Well no.. :)

The full list of daylight rules is VERY complicated - lots of governments worldwide seem to like messing with daylight rules. The new registry rules are not populated with historic rules. For example Sydney changed the rules for the Olympic games in 2000 - the daylight transition was on the last Sunday of August, rather than the last Sunday in October. Try converting a date in September 2000 - we should get the usual 10 hours difference plus 1 hour for daylight time. And we get:

UTC:2000-09-15T00:00:00 Sydney:2000-09-15T10:00:00

This conversion website gives the results we expect: here.

We can actually look in the TimeZoneInfo GetAdjustmentRules() and see the list of rules Windows knows about.

TimeZoneInfo tzi = TimeZoneInfo.FindSystemTimeZoneById("AUS Eastern Standard Time");
int i = 0;
foreach (TimeZoneInfo.AdjustmentRule adj in tzi.GetAdjustmentRules())
{
    i++;
    Console.WriteLine("AdjustmentRule " + i);
    Console.WriteLine("adj.DateStart = " + adj.DateStart);
    Console.WriteLine("adj.DateEnd = " + adj.DateEnd);
    Console.WriteLine(String.Format("adj.DaylightTransitionStart = Day: {0}, DayOfWeek: {1}, Month: {2}, TimeOfDay: {3}, Week: {4}", adj.DaylightTransitionStart.Day,
        adj.DaylightTransitionStart.DayOfWeek,
        adj.DaylightTransitionStart.Month,
        adj.DaylightTransitionStart.TimeOfDay.ToShortTimeString(),
        adj.DaylightTransitionStart.Week));
    Console.WriteLine(String.Format("adj.DaylightTransitionEnd = Day: {0}, DayOfWeek: {1}, Month: {2}, TimeOfDay: {3}, Week: {4}", adj.DaylightTransitionEnd.Day,
        adj.DaylightTransitionEnd.DayOfWeek,
        adj.DaylightTransitionEnd.Month,
        adj.DaylightTransitionEnd.TimeOfDay.ToShortTimeString(),
        adj.DaylightTransitionEnd.Week));
    Console.WriteLine("adj.DaylightDelta = " + adj.DaylightDelta);
    Console.WriteLine();
}

The output:

AdjustmentRule 1
adj.DateStart = 1/01/0001 12:00:00 AM
adj.DateEnd = 31/12/2007 12:00:00 AM
adj.DaylightTransitionStart = Day: 1, DayOfWeek: Sunday, Month: 10, TimeOfDay: 2:00 AM, Week: 5
adj.DaylightTransitionEnd = Day: 1, DayOfWeek: Sunday, Month: 3, TimeOfDay: 3:00 AM, Week: 5
adj.DaylightDelta = 01:00:00

AdjustmentRule 2
adj.DateStart = 1/01/2008 12:00:00 AM
adj.DateEnd = 31/12/9999 12:00:00 AM
adj.DaylightTransitionStart = Day: 1, DayOfWeek: Sunday, Month: 10, TimeOfDay: 2:00 AM, Week: 1
adj.DaylightTransitionEnd = Day: 1, DayOfWeek: Sunday, Month: 4, TimeOfDay: 3:00 AM, Week: 1
adj.DaylightDelta = 01:00:00

How would you deal with this scenario? The MSDN article for TimeZoneInfo mentions the class can be used for "Creating a new time zone that is not already defined by the operating system." The popular tz database contains a very comprehensive collection of date rules. So I was 'sort of' expecting someone would write the code to import the tz database to custom TimeZoneInfo timezones. Unfortunately a quick google doesn't come up with someone who has already done this for me. An alternative exists to use the tz database in .net: PublicDomain - described in more detail in the CodeProject article: How to Use the Olson Time Zone Database in .NET

Futher reading:

Labels: