November 2008 Archive

Add business days to a Date

By Luke Smith on November 18, 2008 8:11 PM

This morning in ##javascript, a visitor asked how to add n business days to a Date instance. Call me a sucker, but I love little tasks like this. Maybe I just get antsy when I smell a potential modulo.

I thought I would be able to find such a function pretty quickly online, but I wanted to write it first, then validate my approach against what I found. Here's what I came up with:

function addBusinessDays(d,n) {
    d = new Date(d.getTime());

    var day = d.getDay();

    d.setDate(
        d.getDate() + n +
        (day === 6 ? 2 : +!day) +
        (Math.floor((n - 1 + (day % 6 || 1)) / 5) * 2));

    return d;
}

Not terribly readable, but it works. You could omit the first line if you have no desire to preserve the input Date object. And it is hard coded to assume "business days" are between Monday and Friday. If you wanted to offset the resulting date for holidays, you could maintain an object literal keyed by a serialization of holiday dates, and in the function check against the object, rerunning the algo with n + 1 if a match was found.

Compare and contrast

It turned out after finishing the code, I had little luck finding a comparable function in the wild. Perhaps my search criteria were awful or there's not actually a market for such a function, but my findings were sparse. The closest thing I found was this gargantuan monster. I was surprised that such a function didn't show up in the DateJS lib. Regardless, both collections of functions modify the Date.prototype which I don't do.

So either I need to hone my search skills, or my efforts may have actually been worthwhile.

Here's the function minified…you know, in case you wanted to save 25b over the wire or something:

function addBusinessDays(B,C){B=new Date(B.getTime());var A=B.getDay();B.setDate(B.getDate()+C+(A===6?2:+!A)+(Math.floor((C-1+(A%6||1))/5)*2));return B;}

And a demo page showing a variety of dates and a delta range of -10 to +10.

Update 10/14/2009

I've moved this to a GitHub project and included a couple new versions. One to modify the Date in place and two others that adjust for holidays (some assembly required).

Is my image loaded?

By Luke Smith on November 5, 2008 10:33 PM

While developing on YUI's Slider widget for the upcoming 3.0 version, I came across a challenge regarding the slider's thumb image. In some cases, I need to reference offset dimensions for Slider to use to correctly place the thumb on the background rail.

There were three scenarios that I needed to address.

  1. The image is in the DOM and loaded before the js executes
  2. The image may be in the DOM but not yet loaded
  3. The image src is incorrect either in the DOM or provided via configuration

All A-grade browsers support the DOM events onload and onerror if the image hasn't already been loaded. That's the easy part. The hard part is dealing with the browser differences for successful and unsuccessful image urls that may or may not have been requested in the past.

What I found is listed here:

  • All browsers fire onload or onerror for images that haven't received a server/cache response
  • FF and Opera cache the 404 response from bad images, and treat future requests for that url as a cache pull
  • FF and Opera browsers do not fire (at least) onerror for cached 404 pulls
  • cached 404s in FF/Opera show img.complete == true
  • FF and SF3 support img.naturalHeight and img.naturalWidth, which report 0 if the image did not load (also cached 404 + img.complete)
  • Opera reports img.width and img.height of 0 for failed urls
  • All other browsers report img.width img.height as the dims of the alt text
  • IE reports img.complete == false for failed urls

Some of these are suppositions, and likely there's a lot more to it than what I've found. But at any rate, it …"inspired" me to write this little function to attach some callbacks to an <img> element (or generate one on the fly) that simulate an onload and onerror mechanism, that will execute when the image is loaded or determined to be in error. If the image is a cache pull, the callbacks execute immediately.

// First a couple helper functions
function $(id) {
    return !id || id.nodeType === 1 ? id : document.getElementById(id);
}
function isType(o,t) {    return (typeof o).indexOf(t.charAt(0).toLowerCase()) === 0;}

// Here's the meat and potatoes
function image(src,cfg) {    var img, prop, target;
    cfg = cfg || (isType(src,'o') ? src : {});

    img = $(src);
    if (img) {
        src = cfg.src || img.src;
    } else {
        img = document.createElement('img');
        src = src || cfg.src;
    }

    if (!src) {
        return null;
    }

    prop = isType(img.naturalWidth,'u') ? 'width' : 'naturalWidth';
    img.alt = cfg.alt || img.alt;

    // Add the image and insert if requested (must be on DOM to load or
    // pull from cache)
    img.src = src;

    target = $(cfg.target);
    if (target) {
        target.insertBefore(img, $(cfg.insertBefore) || null);
    }

    // Loaded?
    if (img.complete) {
        if (img[prop]) {
            if (isType(cfg.success,'f')) {
                cfg.success.call(img);
            }
        } else {
            if (isType(cfg.failure,'f')) {
                cfg.failure.call(img);
            }
        }
    } else {
        if (isType(cfg.success,'f')) {
            img.onload = cfg.success;
        }
        if (isType(cfg.failure,'f')) {
            img.onerror = cfg.failure;
        }
    }

    return img;
}

Some example usage might be

image('imgId',{
    success : function () { alert(this.width); },
    failure : function () { alert('Damn your eyes!'); },
});

image('http://somedomain.com/image/typooed_url.jpg', {
    success : function () {...},
    failure : function () {...},
    target : 'myContainerId',
    insertBefore : 'someChildOfmyContainerId'
});

And a demo here.

If I missed something, please let me know. Otherwise, I hope you find this useful.

Playing favorites

By Luke Smith on November 2, 2008 3:56 PM

I am Liam's favorite person in the world right now. Actually, for the last few months. He explodes with glee when I come home from work each night, running full steam to latch onto my legs and insisting I pick him up and join him in whatever his imagination is concocting at the moment. We play all the time, I read to him, bathe him, and he helps me do some chores (often enough, cleaning up after him).

Normally I'd say this is great, but now he does not listen to his mama, refuses food from her—insisting I feed him—and in general wants nothing to do with her when I'm around. When she asks him to come and give her a hug or kiss, he runs to me and refuses.

It breaks her heart. And mine.

ls.n

LucasSmith.name

Luke and Liam

I'm Luke. I am a front end engineer at Yahoo! on the YUI team.

Mostly I write about code stuff, but occassionally I'll mix in some real life. You've been warned.

Archives

Tags

Feeds

Subscribe to feed Recent entries

Content licensed under Creative Commons

Code licensed under BSD license

©2005 - 2010 Lucas Smith

Powered by Movable Type