Wednesday, December 29, 2010

An algorithm to find floating holidays

I've always liked dealing with code that works with dates and times. You learn a lot about our calendars and all the hacks to make up for our inaccurate decisions hundreds of years ago and for the earth's wobbles and year-to-year inconsistencies.

One task that's come up again and again is determining floating holidays. Here's a very quick and easy way I discovered to determine floating days (every 4th thursday etc) and fixed days that are observed on friday or monday when the day is a holiday.

All you need is a list or array of the 7 days of the month it falls on. The first element is the day of the month the holiday is on when the month begins on a Sunday, the second element is the day of the month the holiday is on when the month begins on a Monday, and so on. Once you find what day of the week the month starts on, you can just lookup the day you're looking for. So you know the year, the month, and the day the holiday falls on for any year of that calendar.

For example, American Independence day is celebrated on the 4th of July or the closest weekday when it falls on a weekend. Using a calendar we can see that the days of the month are [4,4,4,3,5,4,4], i.e. when July starts on a Wednesday (4th element in the list) the holiday is celebrated on the 3rd, when it starts on a Thursday (5th element in the list) the holiday is celebrated on the 5th, otherwise it's celebrated on the 4th. American Thanksgiving day is celebrated on the 4th Thursday of November so the days of the month are [26,25,24,23,22,21,27], i.e. when November starts on a Sunday the holiday is celebrated on the 26th, when November starts on a Monday the holiday is celebrated on the 25th, etc.

The java is much less verbose than the english description:


public Calendar getAmericanIndependanceDay(int year){
        return getFloatingDay(year, 6, new int[]{4,4,4,3,5,4,4});
    }

    public Calendar getAmericanThanksgivingDay(int year){
        return getFloatingDay(year, 10, new int[]{26,25,24,23,22,21,27});
    }

    private Calendar getFloatingDay(int year, int month, int[] days){
        Calendar firstDayOfTheMonth = new GregorianCalendar(year, month, 1);
        int firstDayOfTheWeek = firstDayOfTheMonth.get(Calendar.DAY_OF_WEEK) - 1;
        return new GregorianCalendar(year, month, days[firstDayOfTheWeek]);
    }

Thursday, December 23, 2010

Unit tests are not inherently good

Unit tests cause pain. Unit tests, by themselves, do not solve problems, do not make users happy, do not make maintainers happy, do not make a better product, and do not improve design. Unit tests make certain things hurt; nothing more and nothing less. But pain can indicate something is wrong and can lead you to do something else.

The first assumption behind unit tests (and bodily pain) is that if something hurts we'll pay attention and do something else instead. The second assumption is that what we do as an alternative will not only hurt less but make things better. For example, since unit tests make singletons and hard coded dependancies so painful, we can rely on dependency injection techniques and the single responsibility principal. This should cause code that is easier to test and understand and that responds to rapidly changing requirements.

So unit tests are one way to show what hurts and needs attention and avoid blundering into bad and potentially project-killing designs. The problem is when we can't or don't change how things are done. If we must rely on singletons, the filesystem, database, complex api calls, undefined behavior, or slow techniques then writing and running the unit tests will hurt. In these cases the costs of writing and maintaing unit tests can outweigh the benefits of running those tests.

Wednesday, December 8, 2010

Announcing AsciiPanel

I recently created a git repository for AsciiPanel, a java control that simulates a code page 437 ASCII terminal display. It supports all 256 characters of codepage 437, arbitrary foreground colors, arbitrary background colors, and arbitrary terminal sizes.

This is an example mini-rougelike that uses an AsciiPanel.




AsciiPanel, jar file or source, can be found at https://github.com/trystan/AsciiPanel.

[edit] The test applet seems to be missing. [/edit]