Ancient dates
One major issue with my recent dickie-dates
package is that it can’t handle BCE (before the Common Era) dates. So I wanted to put that right.
More JavaScript weirdness
Actually it turns out that JavaScript itself has strange behaviour when it comes to BCE dates.
This isn’t unexpected. One of the reasons I started writing dickie-dates
was because I was bemused by the way JavaScript’s torturous date logic handled certain numbers, as outlined in my post on what I then called “awkward dates”.
So I wasn’t surprised when I Googled a standard way for handling BCE dates and I found a post by Dave McClure on his own experimentation and workaround on BCE dates.
I thought handling BCE dates would be simply involving adding a minus sign onto the front of the year. And it is if you’re only passing the year as the first of several numeric variables to JavaScript’s Date object:
> new Date(-1000, 0, 1)
< Wed Jan 01 -1000 00:00:00 GMT+0000 (GMT)
But if you’re trying to get JavaScript’s Date object to parse a string argument, it will simply, and weirdly, ignore the minus sign.
> new Date("-1000/01/01")
< Wed Jan 01 1000 00:00:00 GMT+0000 (GMT)
However, weirder still, McClure found that if you put two zeroes between the minus sign and the year in the string variable, it would parse correctly.
new Date("-001000/01/01")
Wed Jan 01 -1000 00:00:00 GMT+0000 (GMT)
This seems as unexpected to me as all the other behaviour I’ve seen with JavaScript’s Date objects. As an additional quirk, you can’t use it with the numerical value; the minus symbol will work but the extra zeroes will mean that JavaScript will generally interpret the number as a binary one:
> new Date(-001000, 0, 1)
< Sun Jan 01 -512 00:00:00 GMT+0000 (GMT)
> -001000
< -512
Improving Dickie Dates
My Dickie Dates package also has this problem but for different reasons. After all, within Dickie Dates I haven’t actually used the JavaScript Date object at all. For now, the package simply parses objects or strings and returns and object with a particular format.
The issue is with how the package parses the strings it receives. It’s not even looking for a minus sign. Fortunately this is easy to fix.
Writing tests
First in keeping with the BDD (behaviour driven development) approach I originally outlined through the section on “aspirational tests” in my post on building the Node package, I want to describe first what the results should look like.
In this case, the desired behaviour is pretty easy to describe because it’s so predictable. Namely, if the string starts with a minus sign, ensure that’s represented in the output.
Here are two tests, nested inside an existing suite of tests, based on an input with the year 1000 BCE:
describe("Using `dickie-dates`", function() {
describe("to parse a simple string", function () {
// other tests...
describe("like `-1000` to represent the year 1000 BCE", function () {
let result = dickiedates.parseDate("-1000")
it("should return an object with a `startDate` value of '-10000101000000'", function () {
expect(result).to.have.property("startDate", "-10000101000000")
})
it("should return an object with a `endDate` value of '-10001231235959'", function () {
expect(result).to.have.property("endDate", "-10001231235959")
})
})
})
})
Of course, running these without coding the package for the new behaviour, the tests will fail.
The idea now is to make them pass.
Coding the new behaviour
Actually getting this to work was relatively straightforward.
First the regular expression needs changing. Here’s what it was:
var datePattern = /(-?)(\d{1,4})(?:(0[1-9]|1[0-2])(?:(0[1-9]|[1-2][0-9]|3[0-1])(?:([0-1][0-9]|2[0-3])(?:([0-5][0-9])(?:([0-5][0-9]))?)?)?)?)?/
All that needs adding to pick up the minus sign is an extra capture group at the beginning with an optional minus character:
var datePattern = /(-?)(\d{1,4})(?:(0[1-9]|1[0-2])(?:(0[1-9]|[1-2][0-9]|3[0-1])(?:([0-1][0-9]|2[0-3])(?:([0-5][0-9])(?:([0-5][0-9]))?)?)?)?)?/
Of course, this not only means we have to use the capture group in order to know what to do with it:
var startDate = datePattern.exec(dateInput.startDate)
var endDate = datePattern.exec(dateInput.endDate)
var startEra = startDate[1]
var endEra = endDate[1]
It also means that the capture groups that were referenced previously need tweaking, so that what was once 1
becomes 2
, and so on:
var startEra = startDate[1]
var startYear = startDate[2]
? `${startDate[2]}${"0000".substr(startDate[2].length)}`
: ""
var startMonth = startDate[3] || "01"
Finally the code for the precision indices need changing, again adding 1
to the index of the group that is checked in each loop through the captures to see which the highest truthy capture group is:
var startPrecision = 6
while (startPrecision) {
if (startDate[startPrecision + 1]) {
break
}
startPrecision -= 1
}
This simple tweaks provide Dickie Dates with the ability to parse BCE years.
Summary
This post has covered how I made a very minor tweak to the Dickie Dates package that will nonetheless enable me to generate some different charts. The change features in v0.1.0 of the Dickie Dates package.
Carrying out this work also got me thinking about other improvements I could make to the package, which I’ll attempt to articulate in a future post.