Where Were You On Monday, Week 32?

Here is an interesting problem: what is the timestamp of Monday, the ISO standard for the start of a week, for any week of any year? Knowing this may be important for calendaring or time framing.

To solve this, we will need to pull on the definition of how week one of any year is defined. The rule is the first week is the first week that contains a Thursday. Going off this rule, we can conclude that January 4th is always in the first week of the year.

Our strategy will then be getting the timestamp of January 4th for any year, which is easy, and then adding on 604800 seconds for each additional week. Then, after we are at the nth week of the year, we can easily ask strtotime() for "Monday this week".

Well, almost. A week in strtotime() is from Sunday to Saturday, which is different from the ISO standard of Monday to Sunday. So, take this year, 2009, and January 4th lands on a Sunday. This means that when we talk about "this week" in strtotime(), we are accidentally referring to what would be considered next week in ISO standards. Whoops. Luckily, for the convention of weeks being Sunday to Saturday, we can assert that the 3rd of January is always in the first week of the year.

function mondayOfWeekNo($weekNo, $year) {
  return strtotime('Monday this week', mktime(0,0,0,1,3,$year) + ($weekNo-1)*604800);
}

Pretty straight-forward right? We take one from $weekNo because mktime() already lands us on the first week. Getting to the 2nd week will just require mktime() + 1*604800.

Of course, we can generalize this to make it even more useful.

function dayOfWeekNo($day, $weekNo, $year) {
  return strtotime($day.' this week', mktime(0,0,0,1,3,$year) + ($weekNo-1)*604800);
}

Now we can determine the Monday, Friday, Thursday, or whatever of any week in any year. Just make sure that your conventional weeks match the ISO standard. If they do not, you will have to adjust your strategy to get this working. Have a better way to solve this problem? Leave a comment; I want to hear about it.

Error Reporting in PHP

Many beginners to PHP are not aware of errors that may be occurring, seemingly, invisibly. This is at fault of the default error reporting setting, which excludes both notice-level and strict-level errors, and at the fault of the display_errors directive that may be turned off. To know of all the errors that are occurring in your script, ensure to put these following statements at the very beginning of your script.

ini_set('display_errors', 'on');
error_reporting(E_ALL | E_STRICT);

These can also be set in php.ini and is recommended that you do so for display_errors. If you have display_errors off in php.ini, you will not see errors outputted to the browser that prevent your script from executing, such as parse errors. If your script never executes, the call to ini_set() will never happen and display_errors will never be set to on. Therefore, for development, it is always recommended to have display_errors configured to on in php.ini. Calling error_reporting(E_ALL | E_STRICT) during your script is fine, however, so long as you do it at the beginning.

Now go make some error-free code!

When Ordered Function Arguments Are Not Enough

Function arguments in PHP are not very flexible compared to a language like Python where you have something critically useful called keyword arguments. To make up for this, PHP programmers will setup their functions to take an associative array containing a map from argument name to argument value. This technique is used to avoid what would otherwise be a mess of function arguments without any natural order.

That setup is a tad clunky to use but it is the best we've got. To ease our troubles in dealing with the associative array as our argument list, the following code example may prove useful.

function foo(array $parameters = array()) {
    $defaults = array(
        'foo' => null,
        'bar' => 1,
        'zar' => array()
    );
    $parameters = array_merge(
        $defaults, array_intersect_key($parameters, $defaults)
    );
    extract($parameters);
}

This setup allows us to emulate argument functions. Each key seen in the $defaults array will be extracted to a variable, so $foo, $bar, and $zar are available. Extra keys provided in $parameters will not be extracted which is a safety precaution. However, keys in $parameters that match in $defaults will override the value found in $defaults with their associated values. Anything that $parameters does not override will end up with the value given in $defaults.

We have extracted a variable for each argument, emulated default arguments (to a degree, it is not exact of course), and didn't remove the ability to have a variable number of arguments either. The $zar variable, as an array, can take any number of values itself and thus could be treated as storage for variable arguments. To deal with arguments that we really need passed to the function, we can trigger an error or throw an exception if it does not change from its default value. Not bad.

Now, of course, if you only have three arguments I think you could get away with leaving them in the function declaration, even if they had no natural order. For more complex argument requirements, which does happen on occasion, this approach will help keep things comprehensible.

Getting an A in C

Well, I am not learning C in school, but if I had to grade my progress I'd give it an A. Okay, yes, I was just in it for the pun. I really am learning C, though. Kernighan's and Ritchie's second edition book on the C programming language is an excellent way to get going if you have programming experience. The chapters are concise and truly useful exercises are given at the end of each to apply your new knowledge to real programming. Best book I have picked up yet for learning a programming language. I ordered my copy from Amazon.ca, because I am Canadian, but it is also available on the .com and .co.uk sites.

The investment is well worth it if you want to learn C. But, before you place the order, I want you to check out this great Reverse Polish Notation calculator I made.

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <math.h>
#define MAX_OPERANDS 100
#define MAX_NUM_LENGTH 20
#define OPERATION_NOT_SUPPORTED 1
#define ADDITION '+'
#define SUBTRACTION '-'
#define MULTIPLICATION '*'
#define DIVISION '/'

int preformOperation(int o);
int readNumber(int f);
void addOperand(double f);
double getOperand();

float operands[MAX_OPERANDS];
int operandsIndex = 0;

int main() {
    int c;
    double answer;
    while ((c = getchar()) != EOF) {
        if (isspace(c)) {
            continue;
        }
        if (isdigit(c) || c == '.') {
            if (readNumber(c) == EOF) {
                break;
            }
        } else {
            if (preformOperation(c) == OPERATION_NOT_SUPPORTED) {
                printf("Operation \"");
                putchar(c);
                printf("\" is not supported\n");
                break;
            }
        }
    }
    answer = getOperand();
    if ((int)answer == answer) {
        printf("%d\n", (int)answer);
    } else {
        printf("%lf\n", answer);
    }
}

int preformOperation(int o) {
    float temp;
    switch (o) {
        case ADDITION:
            addOperand(getOperand() + getOperand());
            break;
        case SUBTRACTION:
            temp = getOperand();
            addOperand(getOperand() - temp);
            break;
        case MULTIPLICATION:
            addOperand(getOperand() * getOperand());
            break;
        case DIVISION:
            temp = getOperand();
            addOperand(getOperand() / temp);
            break;
        default:
            return OPERATION_NOT_SUPPORTED;
    }
    return 0;
}

int readNumber(int f) {
    int c, i = 0;
    char buffer[MAX_NUM_LENGTH], *end;
    buffer[i++] = f;
    while (c = getchar()) {
        if (c == EOF || isspace(c)) {
            break;
        } else {
            buffer[i++] = c;
        }
    }
    buffer[i] = '\0';
    addOperand(strtod(buffer, &end));
    return c;
}

void addOperand(double f) {
    operands[++operandsIndex] = f;
}

double getOperand() {
    return operands[operandsIndex--];
}

This is one of the programs you may find yourself writing while going through their book. It probably won't look like this, because this exercise was more self-inspired, but they do have their own version of an RPN calculator that they ask you to tweak in various ways. I simply made one from scratch. Unfortunately, until I get to the section about malloc() and realloc(), my stack sizes are fixed. Bummer. Perhaps I'll write this calculator again when I am finished the book and see what I do differently.

 1

About

User