tisdag 7 juli 2015

Part two of the time zone tombola

So, I did actually keep the "6 months timeframe" that I promised in the last part, just over two months is a bit longer than I planned this though. So how did I go on with our time zone issue?

The main idea is that we use the CRM time fields to keep track of stuff in UTC time. Those fields will always show the user when the meeting takes place in the time zone you have set in CRM.














As you can see in the picture I have set up two time blocks, "local meeting time" and "my time". The local meeting time is the time when the meeting takes place at the actual site. If we woul
d have a meeting in London, and this is my CRM running CET there would be a difference of one hour between the two where "my time" would be one hour ahead.

As you can see I also locked the datetime fields in my time so all editing happens in "local time".

Now on to JS madness.

Onload of the appointment form I fetch the starttime and endtime of the appointmen, if those aren't set I set them as new Date(), which I then make sure is on the hour of half hour. I also fetch the duration field, which is a tricky bastard I might add, and the custom timezone field.

The endtime is set as starttime plus duration if there are no values for start and endtime on the form.

Now things got a bit trickier, and here's why I don't like time zones. I use the same way that CRM uses the timezones for the script part so I set up a timezone setting array which looks something like this:
localSettings = [0, 10, 5, 0, 2, 3, 5, 0, 1, -60];

So, this is slightly greek but this is a settings array for UTC. The first element is timezone bias, which is 0 for UTC. To make it slightly worse it's negative for CET so you take the time i CET and add the bias to end up in UTC.
The second element is standard month, which is the month in which standard time is starting, no worries, well I might mention that January is 0 in JS. 
The third element is standard day, which could be a date but it isn't  since you change back to standard time on the fourth saturday after the fifth moon after midsommer, ish. That is which number of day this is, in this case the fifth time the day appears in a month.
The fourth element is standard day of week, which means which day we are lookin at, and since 0 is the first day of the week, it's a sunday.
The fifth element, standard hour, is what time of day you return the clock one hour. Which in this case means that in UTC you return the clock one hour at two in the morning.
 The following four elements are the same for DST and the final element is daylight bias, which again is negative.
I also fetch the users timezone settings from CRM to calculate UTC time.

With this info I calculate when DST starts and ends using the following code:
 var GetDst = function (inyear, month, day, dayofweek, hour,tzBias) {

    if (tzBias == 0)
        return new Date(2011, 1, 1, 12, 0, 0, 0);

    var returnDate = new Date(inyear, (month - 1), 1, hour);
    var testDate = new Date(inyear, (month - 1), 1, hour);
    var firstDayOfMonth = new Date(inyear, (month - 1), 1);
    var firstDay = firstDayOfMonth.getDay();

    if (firstDay != dayofweek) {
        if (dayofweek == 0)
            testDate.setDate(testDate.getDate() + (7 - firstDay));
        else
            testDate.setDate(testDate.getDate() + (dayofweek - firstDay));
    }

    testDate.setDate(testDate.getDate() + ((day-1) * 7));
    if(testDate.getMonth() != (month - 1))
    {
        testDate.setDate(testDate.getDate() - 7);
    }
    return testDate;
}


Here the inputs are the year from the appointment, in the first case I want DST start time for the year the appointment is happening, month, day, dayofweek and tzbias are from the settings array

First I set a date of the year, DST start month, the first day of that month and the hour that stuff happens. I alse set up en exact copy of that date. 
I the proceed with finding out which day of the week the first day of the month is and compare that to the daylight saving start day and if the don't match I find when the first "change day" of the month happens (say first sunday).
After this I apply the number of weeks that should be added and check if we're still in the same month, if not, remove one week. SIMPLE

The time presented in "My time" is set up by UTC time plus timezone difference and DST difference so to get UTC time from the form I convert that time with the info about 
I then check if the time we get out of CRM is within DST and apply the changes to get to UTC time. 

This is done since I can never be sure that the computer's timezone setting is the same as CRM settings so I need to calculate the time in the form based on the CRM settings, yes that made me feel a bit sick too.

After I've calculated what the "my time" times are in UTC time I can then apply the timezone info on that data and I know when the meeting is taking place at the local office.

What I also do is set the submitmode to never if it's anything else than a create form. You will also need onchange scripts that sets the submitmodes to relevant values when you change an appointment.

The duration field isn't really propagated in a way you would think when you change it so that has to be made dirty with code to be sent, very annoying and a source of errors I might add.

I hope this makes some sence. It's a bit tricky and I had to work quite hard to get it right. What started out as a "Oh, we just take whatever time CRM is giving us and add an hour and we're home free" ended up in a couple of hundred lines of scripts and a little less plugin code to handle the saves.

So as my commanding officers said after an order or information speech. Any questions, too late, dismissed!

Seriously, if you would stumble upon this and have similar issues, give me a holler and I'll try to help. I'm thinking of talking about the backend code sometime later, maybe in another two months :)

Rickard Norström
Developer at CRM-Konsulterna
www.crmkonsulterna.se

Inga kommentarer:

Skicka en kommentar