Have you celebrated Baegil yet?

A Korean friend asked my wife if our son had celebrated Baegil or 100 days yet. Not being familiar with the celebration, we said that we hadn’t kept track of the days since his birth. I decided to write a little program in Go in order to check what day of one’s life one is currently on.

The program should take the subject’s date of birth as a string from the first command argument. This was simple enough for me to decide against using the flags package and check the command line arguments directly:

if len(os.Args) == 1 {
    os.Stderr.WriteString("Please tell me your date of birth!\n")
} else {
...
}

I wanted to save programming effort and avoid reinventing the square wheel by using a library for handling the dates. The time package from the standard library can represent times and parse strings to time structs.

The time package has a peculiar (but easy to grok) way of specifying date layouts for formatting. Rather than %Y, %h, %s and so on from the standard C library and its descendants, in Go a fixed date where days are 1 and months are 2 and so on is used. So, to parse dates such as “2016-06-20” or “1980-10-28”, we need:

var layout = "2006-01-02"
var dateOfBirth, dOBParseError = time.Parse(layout, os.Args[1])

Full details can be found on this blog post:

https://pauladamsmith.com/blog/2011/05/go_time.html

Of course, a user of the program might not enter a parseable date string, so we need a little bit more error handling:

if dOBParseError != nil {
    fmt.Fprintf(os.Stderr, "Unable to parse your date of birth: %s\n", dOBParseError)
} else {
...
}

Once we have a date of birth, we can perform our arithmetic. The time package has a Since(...) method that can give you the duration of time since a given time struct:

var age = time.Since(dateOfBirth)

For a simple program like this, methods like Since are more convenient than creating a date object of the current time and comparing the given time to it. However, this does create an issue when testing code that does anything to do with time. If your code makes direct use of the system’s clock, your functions are impure as the same input won’t always return the same output. How do you test that a given input produces the correct output? Such tests would only pass at specific times on a given date. The purely functional way to do this is to write your business logic handling functions to accept all the used time objects already created, so that appropriate mocks can be passed in during tests without calling any methods that get data from the system’s clock. Another approach is the override the methods that call the system’s clock so that they return a set time in the test. This is called monkey patching. I’ve heard of people even setting the system clock during the test, but this is likely to have unforeseen consequences, especially on a shared build server.

The time package defines an Hours function for Duration structs but not one for days. A quick and dirty solution to this is the divide the number of hours by 24 and floor the result:

var ageInDays = math.Floor(age.Hours() / 24)

This makes the assumption that every day has exactly 24 hours. What about leap seconds? A person might have a few leap seconds during a lifetime. What if this program is run within a few seconds of midnight? The result might be different from the true result. As we are not using the precise time of birth and are flooring the number of days anyway, this is not too much of a concern. However, if really precise maths is needed or you using the components of one time struct to create a new time struct, inaccuracies can creep into your code. The fun really begins when you need to handle issues like leap years, time zones and seasonal time adjustments. If I ran this code in San Francisco in Winter for someone who was born in Sydney during Daylight Saving Time, would the result be accurate? A more fully developed version of this program would probably use a library to handle the mind boggling complexity of time tracking in the real world.

Another issue to consider is off by one errors. I’m flooring the number of days in order to say, “days since”. If I enter today’s date, I will get 0 as the subject’s age has not reached one day yet. This might be confusing to someone expecting 1 on the first day and so on. How ages are reckoned changes among different cultures. How Koreans consider their ages is surprising to many foreigners.

https://en.wikipedia.org/wiki/East_Asian_age_reckoning#Korean

Koreans celebrate Baegil on the 100th day, so we would expect the program to return 99 on the appropriate day. A programmatic solution to this might be add 1 to the result and change the wording. Programmers need to consider the audience of their programs carefully and gather requirements fully.

Obviously, you can’t be born in the future and still use the program, so we need a bit more user input checking:

if ageInDays < 0 {
    os.Stderr.WriteString("Wow! I must be talking to a foetus!\n")
} else {
    ...
}

If we’ve made it this far, we can now print the age in days:

fmt.Printf("Days old: %.0f\n", ageInDays)

Putting it all together, this is my program:

package main

import (
    "fmt"
    "math"
    "os"
    "time"
)

func main() {
    if len(os.Args) == 1 {
        os.Stderr.WriteString("Please tell me your date of birth!\n")
    } else {
        var layout = "2006-01-02"
        var dateOfBirth, dOBParseError = time.Parse(layout, os.Args[1])

        if dOBParseError != nil {
            fmt.Fprintf(os.Stderr, "Unable to parse your date of birth: %s\n", dOBParseError)
        } else {
            var age = time.Since(dateOfBirth)
            var ageInDays = math.Floor(age.Hours() / 24)

            if ageInDays < 0 {
                os.Stderr.WriteString("Wow! I must be talking to a foetus!\n")
            } else {
                fmt.Printf("Days old: %.0f\n", ageInDays)
            }
        }
    }
}

Entering my son’s date of birth, I see that we still have a couple of days to go:

$ ./daysOld 2016-06-20
Days old: 97

The source code can be found at:

https://github.com/robert-impey/CodingExperiments/blob/master/Go/daysOld.go

Photos of Hong Kong, Macau and South Korea

I’ve been very remiss with uploading my photographs to Picasa for the last few months. The fact that I am leaving South Korea on Tuesday has forced me to get up to date with everything.

Some photos from Chuncheon:

2009-07-25 to 2009-07-26 Chuncheon

A very rainy day at a traditional Korean village museum:

2009-06-20 Traditional Korean Village

A day at Everland:

2009-06-21 Everland

A panorama made using autostitch from the roof of Tekno Mart:

2009-07-25 Tekno Mart

A short trip to Hong Kong and Macau:

2009-07-29 to 2009-08-01 Hong Kong

A quick hike up Achasan, where one can see much of Seoul:

2009-10-02 Achasan

A day at Seoraksan National Park with my parents:

2009-10-28 Seoraksan National Park

At Bulguksa near Gyeongju:

2009-10-30 Bulguksa near Gyeongju

A day trip to Hwaseong Fortress in Suwon:

2009-11-01 Suwon

Soju and prime numbers

Earlier this evening, I went with my girlfriend to eat Samyeopsal, a barbecue pork dish. The traditional accompaniment for this pork dish is Soju, a type of sweet vodka from Korea. One normally drinks the liquor from a two ounce shot glass, so a 360 ml bottle will almost fill seven glasses. Traditionally, one fills the glass of one’s dining partner whenever their glass is empty. If two people drink one bottle, then one ends up drinking four glasses and the other drinks three. If three people drink one bottle, then one person drinks three glasses, but the other two just have two glasses. Because it is awkward for one person to drink alone, one often ends up buying a second bottle to keep the solitary drinker company. The only time that there would not be one shot left over is a table of one (how sad!) or a table of seven (who would certainly order more than one bottle, anyway). Choosing a size of bottle than is a prime multiple of the size of a standard glass is clearly a clever trick from a marketing point of view.

I’m reminded of the periodicity of cicada migrations that Daniel C Dennett writes about in “Darwin’s Dangerous Idea”. Apparently, colonies of different species of cicadas return to different sites at different periods. However, the periods are always a prime number of years, sometimes as long as seventeen years. The explanation that he offers is that if there is a predator that returns to that site regularly (say once ever two years) then the cicadas will avoid that predator more often if the period of their return is a prime number. If they returned with a periodicity that was a composite, non-prime number of years, then one of the factors of that number of years might be the frequency that the predator returned, which would ensure that they met up regularly.

Why I hate ipods

As I was walking to work today, I was not listening to an ipod. I never do. In part, I do not use one for reasons of safety. Korean pavements can be dangerous places. Motorbike riders use the pavement as much as the main road, and it is vital that one has all the senses tuned to what’s going on. As well as motorbikes, one hears conversations and shouting, haggling and laughing, and all the other sounds that make up day-to-day life.

Today, my ears picked up on a plaintive melody from a cello soloist. There was so much other sound, from air conditioner heat sinks and the like, that I didn’t devote much thought to it at first. But the melody did not stop as I walked along the street. To walk more that a hundred metres, listening to the same sound, without the volume of the sound varying and without being able to identify the source is a peculiar experience. Listening to the conversation of a fellow walker is easily understood, as is the increasing din when approaching something far off but very loud. But a continuing piece of music, as other sounds rise and fade away, is eerily unusual.

Quickly, I realised that all of the televisions, which can be heard on the street from every shop, were on the same channel. It’s not normal for every set on a road to be on the same show. I was reminded of getting off a coach and walking through the streets of Bangkok on September 11th 2001 and wondering why there were crowds of people outside every cafe, staring in the same direction towards the TVs. My first thought, alarmist coward that I am, was that Pyongyang had done something outrageous and that the melancholic cello was to make the four minute warning somehow easier to absorb. However, the cello solo was part of former president Roh Moo-hyun’s funeral, which I think is occupying the thoughts of Koreans even more that the North’s current sabre-rattling.

A few steps later, I was back in everyday Korea, as a tune from the Wondergirls blared out from some shop’s TV. I couldn’t help asking myself why people deprive themselves of the sounds of the world. Wearing headphones and having complete control over what one hears is like looking at waxed, silicone enhanced and Photoshop-ed pornography during sex.

Daegu

I’ve just gotten back from a short trip to Daegu.

2008-12-21-Daegu

A very nice city, with remarkably friendly people. The red bean pancakes that we ate were great. Apparently, eating them on the winter solistice is a tradition to keep the body warm throughout the winter. If the temperature drops to -14C again, that will come in handy.