Better Javascript Date Add and Diff

When you need a snippet of javascript it seems the first approach is to see whats available. If your javascript framework doesn't have a method you need, you go to google. Well sometimes the results on google come up with either crazy solutions or something that is just not quite what you need. Well here is where I got busy with some of my own handy work. Instead of Date Add or Subtract, I created a Date Adjust function. This name just makes sense to me since it can add and subtract. Here is what I come up with:

Date.prototype.adjust = function(part, amount){
    part = part.toLowerCase();

    var map = { 
                years: 'FullYear', months: 'Month', weeks: 'Hours', days: 'Hours', hours: 'Hours', 
                minutes: 'Minutes', seconds: 'Seconds', milliseconds: 'Milliseconds',
                utcyears: 'UTCFullYear', utcmonths: 'UTCMonth', weeks: 'UTCHours', utcdays: 'UTCHours', 
                utchours: 'UTCHours', utcminutes: 'UTCMinutes', utcseconds: 'UTCSeconds', utcmilliseconds: 'UTCMilliseconds'
        mapPart = map[part];

    if(part == 'weeks' || part == 'utcweeks')
        amount *= 168;
    if(part == 'days' || part == 'utcdays')
        amount *= 24;

    this['set'+ mapPart]( this['get'+ mapPart]() + amount );

    return this;

Pretty straight forward prototype added to the native Date object. You would call it like so:

var d = new Date();

d.adjust('hour', -10);

The options go a little farther than other methods I found as you can adjust year, month, week, day, hour, minute, second, and the UTC of all of those mentioned.

Now on to more fun stuff. Much more fun. A lot of Date Diff routines simply compute one scenario, but I decided to be more adventurous and compute multiple measurements stacked. For example 450 days could be returned as 1 year, 2 months, 3 weeks, 4 days. Or you can be more boring and just return the number of days. Ok, so you want to see the code:

Date.prototype.diff = function(date2, parts){
    var d1 = new Date(this.getTime()),
        d2 = new Date(date2.getTime()),
        pm = d1 <= d2? 1 : -1,
        result = { },
        factors = { weeks: (1000*60*60*24*7), days: (1000*60*60*24), hours: (1000*60*60), minutes: (1000*60), seconds: 1000, milliseconds: 1 };

    if(parts === undefined)
        parts = ['years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds', 'milliseconds'];
    else if(typeof(parts) == "string")
        parts = [parts];

    for(var i=0, l=parts.length; i<l; i++){
        var k = parts[i];
        result[k] = 0;

        if(factors[k] === undefined){
            inaWhile: while( true ){
                d2.adjust(k, -1*pm);
                if( (pm === 1 && d1 > d2) || (pm === -1 && d1 < d2)){
                    d2.adjust(k, 1*pm);
                    break inaWhile;
            var tmpDiff = Math.abs(d2.getTime() - d1.getTime());
            result[k] = Math.floor(tmpDiff / factors[k]);
            d2.adjust(k, result[k]*-1*pm);
        result[k] *= pm;

    if(parts.length == 1)
        return result[parts[0]];
    return result;

I cheated a bit by using first the adjust method above, and secondly by a while() loop which is a little inefficient, but only for years. You are possibly also checking to see if I considered which months have 31 days and which years are leap years. Well I bypassed that by leaning on the native Date object and the "adjust" method I also provided. So, considering the javascript engine works as it should these scenarios should be handled properly. Here is how you could call this method:

d2.adjust('days', 450);

// returns integer 64

// returns { months: 14, weeks: 3, days: 2 }

Pretty nifty, just be careful that if you pass in one item you get back in integer, other wise you get back an object. Also for it to work properly you should pass in the array with the largest measurements first (years before months, months before weeks, etc.). Does this make me a Dating expert, not hardly.. But I have dabbled with time.