Skip to main content

Ralsina.Me — Roberto Alsina's website

Posts about programming (old posts, page 75)

Javascript Makes Me Cry: Turning a Date into a String

Work­ing late last night in Al­va I want­ed to do some­thing that sound­ed triv­ial:

When the page load­s, get the cur­rent date and time, and if a cer­tain in­put is emp­ty, put it there like this:

28/05/2013 23:45

So, how hard can that be, right? Well not hard, but...

Getting the current date-time is easy: now = new Date(); So, is there something like strftime in Javascript? Of course not. You can get code from the usual places and have a untested, perhaps broken, limited version of it. And I am not about to add a strftime implementation to use it once. Sure, there are a number of Date methods that convert to strings, but none of them lets you specify the output format. So, let's try to do this The Javascript Way, right?

To get the el­e­ments I want to put in the val­ue, I used ac­ces­sor meth­od­s. So, ob­vi­ous­ly, these should give me what I want for the string, right?

now.get­Day(), now.get­Mon­th(), now.getYear(), now.getH­our() now.get­Min­ute()

Well, they are, at the date men­tioned above, re­spec­tive­ly: 2, 4, 113, er­ror, er­ror

Ok, the errors are easy to fix from the docs. It's actually getHours() and getMinutes(), so now we have 2, 4, 113, 23, 45 and of those five things, the last two are what one would expect, at least. Let's go over the other three and see why they are so weird:

Date.getDay() returned 2 instead of 28

Because getDay() gives you the week day and not the day of the month. Which is absolutely idiotic. So, you have to use getDate() instead. Which means the name is a lie, becasue the logical thing for getDate() to return is the whole date.

Date.getMonth() returned 4 instead of 5

Because getMonth() returns months in the [0,11] range. Which is beyond idiotic and bordering in evil. Come on, Javascript, people have been referring to may as "5" for nearly two thousand years now! What other language does this? Anyone knows one?

Date.getYear() returned 113 instead of 2013

Because it uses offset-from-1900. Which is amazing, and I had never heard of a language doing in a standard type. Because why? So, use getFullYear() instead.

Now, armed with the right 5 num­ber­s, let's for­mat it. Does Javascript have the equiv­a­lent of sprintf or for­mat ? Of course not. In JavaScrip­t, with­out 3rd par­ty mod­ules, you cre­ate strings by ad­di­tion, like a cave­man. Again, I know I could add a for­mat method to the String pro­to­type and make this work, but I am not adding an im­ple­men­ta­tion of for­mat or sprintf just to use it on­ce!

So, this pro­duces that I wan­t:

now.getDate()+'/'+(now.getMonth()+1)+'/'+now.getFullYear()+' '+now.getHours()+':'+now.getMinutes()

Un­less... the day or month are low­er than 10, in which case it's miss­ing the left­-­padding ze­ro. Luck­i­ly, for the pur­pose I was us­ing it, it worked any­way. Be­cause OF COURSE there's no in­clud­ed func­tion to left­-­pad a string. You have to do it by ad­di­tion. Or, of course, add a 3rd par­ty func­tion that's out there, in the in­ter­net, some­where.

Nothing Ever Really Goes Away On The Internet: ra-plugins

I used to man­age a large num­ber of QMail in­stal­la­tion­s. And be­cause Qmail was ... weird­ly li­censed, I wrote a set of plug­ins that ran on top of a patch called Qmail-SP­P. I pret­ty much stopped do­ing that years ago be­cause life took me in oth­er di­rec­tion­s, and for­got all about it.

That col­lec­tion is called ra-­plu­g­ins and I had not touched it since late 2008.

And to­day... I got a patch with two whole plug­ins to add to it so that it makes Qmail han­dle email ad­dress­es more like Gmail does (alias­es us­ing user+­foo and mak­ing us­er.­foo the same as user­foo).

So, I got them, added them, fixed a few sim­ple build­ing is­sues, up­dat­ed the lib­smtp it us­es in­ter­nal­ly for one of the plug­ins to a lat­er ver­sion, and there it stays, per­haps not to be touched un­til 2018.

Qt Mac Tips

My team has been work­ing on port­ing some PyQt stuff to Mac OS­X, and we have run in­to sev­er­al Qt bugs, sad­ly. Here are two, and the work­arounds we found.

Na­tive di­alogs are bro­ken.

Us­ing QFile­Di­a­log.ge­tEx­ist­ingDi­rec­to­ry we no­ticed the fol­low­ing symp­tom­s:

  • If you do noth­ing, the di­a­log went away on its own af­ter about 20 sec­ond­s.

  • Af­ter you used it on­ce, it may pop up and dis­­ap­­pear im­me­di­ate­­ly. Or not.

So­lu­tion: use the Don­tUse­N­a­tive­Di­a­log op­tion.

Wid­gets in QTreeWid­getItems don't scrol­l.

When you use Wid­gets in­side the items of a QTreeWid­get (which I know, is not a com­mon case, but hey, it hap­pen­s), the wid­gets don't scroll with the item­s.

Solution: use the -graph­ic­ssys­tem raster options. You can even inject them into argv if the platform is darwin.

Driving a Nail With a Shoe I: Do-Sheet

I had pro­posed a talk for Py­Con Ar­genti­na called "Driv­ing 3 Nails with a Shoe". I know, the ti­tle is sil­ly, but the idea was show­ing how to do things us­ing the wrong tool, in­ten­tion­al­ly. Why? Be­cause:

  1. It makes you think dif­fer­­ent

  2. It's fun

The bad side is, of course, that this talk's con­tents have to be a se­cret, or else the fun is spoiled for ev­ery­one. Since the re­view process for Py­ConAr talks is pub­lic, there was no way to ex­plain what this was about.

And since that means the re­view­ers ba­si­cal­ly have to take my word for this be­ing a good thing to have at a con­fer­ence, which is un­fair, I delet­ed the pro­pos­al. The good (may­be) news is that now ev­ery­one will see what those ideas I had were about. And here is nail num­ber 1: Writ­ing a spread­sheet us­ing doit.

This is not my first "spread­sheet". It all start­ed a long, long time ago with a fa­mous recipe by Ray­mond Het­tinger which I used again and again and again (I may even be miss­ing some post there).

Since I have been us­ing doit for Niko­la I am im­pressed by the pow­er it gives you. In short, doit lets you cre­ate tasks, and those tasks can de­pend on oth­er tasks, and op­er­ate on data, and pro­vide re­sults for oth­er tasks, etc.

See where this is go­ing?

So, here's the code, with ex­pla­na­tion­s:

cells is our spreadsheet. You can put anything there, just always use "cellname=formula" format, and the formula must be valid Python, ok?

from tokenize import generate_tokens

cells = ["A1=A3+A2", "A2=2", "A3=4"]
values = {}

task_calculate creates a task for each cell, called calculate:CELLNAME. The "action" to be performed by that task is evaluating the formula. But in order to do that successfully, we need to know what other cells have to be evaluated first!

This is im­ple­ment­ed us­ing doit's cal­cu­lat­ed de­pen­den­cies by ask­ing doit to run the task "get_de­p:­FOR­MU­LA" for this cel­l's for­mu­la.

def evaluate(name, formula):
    value = eval(formula, values)
    values[name] = value
    print "%s = %s" % (name, value)

def task_calculate():
    for cell in cells:
        name, formula = cell.split('=')
        yield {
            'name':name,
            'calc_dep': ['get_dep:%s' % formula],
            'actions': [(evaluate, (name, formula))],
            }

For example, in our test sheet, A1 depends on A3 and A2 but those depend on no other cells. To figure this out, I will use the tokenize module, and just remember what things are "names". More sophisticated approaches exist.

The task_get_dep function is a doit task that will create a task called "get_dep:CELLNAME" for every cell name in cells.

What get_dep returns is a list of doit tasks. For our A1 cell, that would be ["calculate:A2", "calculate:A3"] meaning that to calculate A1 you need to perform those tasks first.

def get_dep(formula):
    """Given a formula, return the names of the cells referenced."""
    deps = {}
    try:
        for token in generate_tokens([formula].pop):
            if token[0] == 1:  # A variable
                deps[token[1]] = None
    except IndexError:
        # It's ok
        pass
    return {
        'result_dep': ['calculate:%s' % key for key in deps.keys()]
        }

def task_get_dep():
    for cell in cells:
        name, formula = cell.split('=')
        yield {
            'name': formula,
            'actions': [(get_dep, (formula,))],
            }

And that's it. Let's see it in action. You can get your own copy here and try it out by installing doit, editing cells and then running it like this:

ralsina@perdido:~/dosheet$ doit -v2 calculate:A3
.  get_dep:4
{}
.  calculate:A3
A3 = 4
ralsina@perdido:~/dosheet$ doit -v2 calculate:A2
.  get_dep:2
{}
.  calculate:A2
A2 = 2
ralsina@perdido:~/dosheet$ doit -v2 calculate:A1
.  get_dep:A3+A2
{'A3': None, 'A2': None}
.  get_dep:4
{}
.  calculate:A3
A3 = 4
.  get_dep:2
{}
.  calculate:A2
A2 = 2
.  calculate:A1
A1 = 6

As you can see, it al­ways does the min­i­mum amount of ef­fort to cal­cu­late the de­sired re­sult. If you are so in­clined, there are some things that could be im­proved, and I am leav­ing as ex­er­cise for the read­er, for ex­am­ple:

  1. Use up­­­to­­date to avoid re­­cal­cu­lat­ing de­pen­­den­­cies.

  2. Get rid of the glob­al val­ues and use doit's com­put­ed val­ues in­stead.

Here is the full list­ing, en­joy!

Your Editor is Not the Bottleneck

This may cause some pal­pi­ta­tions in some friends of mine who laugh at me for us­ing kwrite, but it re­al­ly is not. Any time you spend con­fig­ur­ing, choos­ing, ad­just­ing, tweak­ing, chang­ing, im­prov­ing, patch­ing or get­ting used to your ed­i­tor is time in­vest­ed, for which you need to show a ben­e­fit, or else it's time wast­ed.

Let's look at SLOC, which while dis­cred­it­ed as a mea­sure of pro­gram­mer's pro­duc­tiv­i­ty, sure­ly does work as a mea­sure of how much a pro­gram­mer types, right?

Well, es­ti­mates of to­tal code pro­duc­tion across the life­time of a prod­uct vary (just take the SLOC of the pro­duc­t, di­vide by men/­days spen­t), but they are usu­al­ly some­thing be­tween 10 and 100 SLOC per pro­gram­mer per day. Let's be gen­er­ous and say 200.

So, 200 lines in eight hours. That's rough­ly one line ev­ery two min­utes, and the av­er­age line of code is about 45 char­ac­ter­s. Since I as­sume you are a com­pe­tent typ­ist (if you are not, shame on you!), it takes less than 20 sec­onds to type that.

So, typ­ing, which is what you of­ten tweak in your ed­i­tor, takes less than 15% of your time. And how much faster can it get? Can you get a line writ­ten in 10 sec­ond­s? Then you just saved 8% of your day. And re­al­ly, does your ed­i­tor isave you half the typ­ing time?

How much time do you lose hav­ing your eyes won­der over the side­bars, but­ton­s, tool­bars, etc?

So while yes, typ­ing faster and more ef­fi­cient­ly is an op­ti­miza­tion, it may al­so be pre­ma­ture, in that, what the hell are we do­ing the oth­er 80% of the time? Is­n't there some­thing we can do to make that huge chunck of time more ef­fi­cient in­stead of the small­er chunk?

Well, I think we spent most of that time do­ing three things:

  1. Read­­ing code

  2. Think­ing about what code to write

  3. Fix­ing what we wrote in that oth­­er 20%

The first is easy: we need bet­ter code read­ers not ed­i­tors. It's a pity that the main in­ter­face we get for look­ing at code is an ed­i­tor, with its con­stant lure to­wards just chang­ing stuff. I think there is a lost op­por­tu­ni­ty there some­where, for an app where you can look at the code in orig­i­nal or in­ter­est­ing ways, so that you un­der­stand the code bet­ter.

The sec­ond is hard­er, be­cause it's per­son­al. I walk. If you see me walk­ing while my ed­i­tor is open, I am think­ing. Af­ter I think, I write. Your mileage may vary.

The third is by far the hard­est of the three. For ex­am­ple, au­to­com­plete helps there, be­cause you won't mistype things, which is in­ter­est­ing, but more pow­er­ful ap­proach­es ex­ist, like con­stant run­ning of tests suites while you ed­it. Ev­ery time you leave a line, trig­ger the af­fect­ed parts of the suit­e.

That's much hard­er than it sound­s, since it means your tools need to cor­re­late your test suite to your code very tight­ly, so that you will see you are break­ing stuff the sec­ond you break it, not min­utes lat­er.

Al­so, it should en­able you to jump to the test with a keystroke, so that you can fix those tests if you are chang­ing be­hav­iour in your code. And of course it will mean you need tests ;-)

Which brings me to a pet peeve of mine, that ed­i­tors still treat the file as the unit of work, which makes no sense at al­l. You nev­er want to ed­it a file, you want to ed­it a func­tion, or a class, or a method, or a con­stant but nev­er a file. Know­ing this was the sheer ge­nius of an­cient Vis­ual Ba­sic, which was com­plete­ly ig­nored by all the snobs look­ing down at it.

So, in­stead of tweak­ing your ed­i­tor, get me a tool that does what I need please. I have been wait­ing for it since VB 1.0. And a sand­wich.

warn­ing

This post is 99% lies, but I want to hear the ar­gu­ments against it. If I tell you now it does­n't count as a re­al lie, I have learned from fi­nan­cial press ;-)

UP­DATE: In­ter­est­ing dis­cus­sions in red­dit and hack­er news


Contents © 2000-2020 Roberto Alsina