QIBs
Today's first 'fun task' (as Gabor puts it) is: Write a script to convert a given number (base 10) to quater-imaginary base number and vice-versa.
Quater-imaginary base numbers - henceforth qibs - are numbers written in the base 2i. So 123, for example means 1 x (2i)^2 + 2 x 2i + 3 = - 4 +4i + 3 = -1 + 4i.
I thought at first that I could just translate the Wikipedia explanation into Perl, but that turned out quite tricky. I also though about using Math::Complex, but decided - probably wrongly - that it wasn't worth the effort of relearning its rather unfamiliar syntax.
So I wrote my own functions to_qib and from_qib. It wasn't that hard though perhaps not as much fun as I might have hoped. I confined myself to complex numbers with integer coefficients, which means the qibs either end in .0 or .2.
The length of qibs increases quite rapidly and my rather clunky way of adding them means that my code won't handle them over 18 digits before the point - but a little tweaking could probably cure that.
Working days
The task is described as: 'Write a script to find the time that occurs $duration business hours after $timestamp. For the sake of this task, let us assume the working hours are 9am to 6pm, Monday to Friday. Please ignore timezone too.'
This is a task which I have often had to address as a project manager: Fred has a workpackage estimated to take 200 hours. If he starts now, when should he finish? Of course in real life there are complications - bank holidays, annual leave, sick leave, distractions, overtime and so on. However, again in real life, precision is not required, not least because the 200 hour estimate is probably far from exactly correct.
But today's task demands, I think, precision. After a few false starts my submitted algorithm looks like this.
We start with $timestamp and $duration. Let's assume that $timestamp falls within the working week (so not 2am on a Saturday, for example). A little thought suggests that it would be much easier if $timestamp were 9am on a Monday. So here goes:
Move $timestamp back to 9am on the preceding Monday, and increase $duration by the number of working hours we've moved it back. That will be 9 hours for every full day plus the time span between 9am and $timestamp. So for example, if $timestamp is 11:00 on Wednesday, we are increasing duration by 18 hours for Monday and Tuesday plus 2 hours on Wednesday.
We could then:
- divide $duration by 45 (working hours per week) to get a number of full weeks
- divide the remainder by 9 to get the number of extra days (9 hrs per day)
- and the remainder is the time period after 9am on the date that results
All that date arithmetic can easily be done using the epoch date (the seconds from 01.01.1970 used by Unix), remembering that a day is 24 * 60 * 60 seconds.
But it doesn't quite work.
It doesn't quite work because of daylight saving time. If the $duration takes us over the date when the clocks go back or forward, we'll be out by an hour. Can we rely on 'Please ignore timezone too' to ignore that?
Being of a cautious frame of mind, I adapted my algorithm slightly. Instead of just working with the epoch date, I treated the date and the time-of-day separately. I have a function date10add which takes a date (eg 2022-12-25) and adds an integer number of calendar days, and I used that to move forward by the full weeks and extra days from my algorithm above. As the working day is always 9 hours, even on the days the clocks change, we don't need to worry about that.
There is a slight anomaly to my algorithm. If the $duration ends at 6pm, the algorithm will give 9am on the next working day as the answer. This is of course strictly correct, but perhaps not what is expected.