Ir al contenido principal

Ralsina.Me — El sitio web de Roberto Alsina

Publicaciones sobre programming (publicaciones antiguas, página 75)

Javascript Makes Me Cry: Turning a Date into a String

Wo­rking la­te last ni­ght in Al­va I wanted to do so­me­thing that soun­ded tri­via­l:

When the pa­ge load­s, get the cu­rrent da­te and ti­me, and if a cer­tain in­put is emp­ty, put it the­re like this:

28/05/2013 23:45

So, how hard can that be, ri­gh­t? We­ll not har­d, bu­t...

Getting the current date-time is easy: now = new Da­te(); 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 ele­men­ts I want to put in the va­lue, I us­ed ac­ce­s­sor me­tho­d­s. So, ob­vious­l­y, the­se should gi­ve me what I want for the strin­g, ri­gh­t?

no­w.­ge­tDa­y(), no­w.­ge­t­Mon­th(), no­w.­ge­tYea­r(), no­w.­ge­tHou­r() no­w.­ge­t­Mi­nu­te()

We­ll, they are, at the da­te men­tio­ned abo­ve, res­pec­ti­ve­l­y: 2, 4, 113, erro­r, error

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.

No­w, ar­med wi­th the ri­ght 5 num­ber­s, le­t's for­mat it. Does Ja­vas­cript ha­ve the equi­va­lent of sprin­tf or for­mat ? Of cour­se not. In Ja­vaS­crip­t, wi­thout 3rd par­ty mo­du­le­s, you crea­te strings by addi­tio­n, like a ca­ve­man. Agai­n, I know I could add a for­mat me­thod to the String pro­to­ty­pe and make this wo­rk, but I am not adding an im­ple­men­ta­tion of for­mat or sprin­tf just to use it on­ce!

So, this pro­du­ces that I wan­t:

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

Un­le­ss... the day or mon­th are lo­wer than 10, in whi­ch ca­se it's mis­sing the le­ft-­pa­dding ze­ro. Lu­cki­l­y, for the pur­po­se I was using it, it wo­rked an­ywa­y. Be­cau­se OF COUR­SE the­re's no in­clu­ded func­tion to le­ft-­pad a strin­g. You ha­ve to do it by addi­tio­n. Or, of cour­se, add a 3rd par­ty func­tion tha­t's out the­re, in the in­ter­ne­t, so­mewhe­re.

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

I us­ed to ma­na­ge a lar­ge num­ber of QMail ins­ta­lla­tion­s. And be­cau­se Qmail was ... weird­ly li­cen­s­e­d, I wro­te a set of plu­gins that ran on top of a pa­tch ca­lled Qmai­l-S­PP. I pre­tty mu­ch sto­pped doing that years ago be­cau­se li­fe took me in other di­rec­tion­s, and for­got all about it.

That co­llec­tion is ca­lled ra-­plu­gins and I had not tou­ched it sin­ce la­te 2008.

And to­da­y... I got a pa­tch wi­th two who­le plu­gins to add to it so that it makes Qmail hand­le email addres­ses mo­re like Gmail does (a­lia­ses using use­r+­foo and making use­r.­foo the sa­me as user­foo).

So, I got the­m, added the­m, fixed a few sim­ple buil­ding is­sues, up­dated the libs­m­tp it uses in­ter­na­lly for one of the plu­gins to a la­ter ver­sio­n, and the­re it sta­ys, perhaps not to be tou­ched un­til 2018.

Qt Mac Tips

Los diá­lo­gos na­ti­vos no an­dan. Usan­do QFi­le­Dia­lo­g.­ge­tE­xis­tin­gDi­rec­to­ry no­ta­mos és­tos sín­to­ma­s:

  • Si no ha­­cés na­­da, el diá­­lo­­­go des­a­pa­­ra­­se por su cuen­­ta en más o me­­nos 20 se­­gun­­do­­s.

  • Des­­pués de usar­­lo una ve­­z, tal vez apa­­re­z­­ca y des­a­pa­­re­z­­ca in­­me­­dia­­ta­­men­­te. O no.

So­lu­ció­n: usar la op­ción Don­tU­seNa­ti­ve­Dia­log op­tio­n.

Los wi­dge­ts en un QTreeWi­dge­tI­te­ms no se mue­ven.

Cuan­do uno po­ne wi­dge­ts aden­tro de los íte­ms de un QTreeWi­dget (que no es muy co­mú­n, pe­ro a ve­ces es úti­l), los wi­dge­ts no se mue­ven jun­to con el íte­m.

Solución, usar la opción -gra­phi­css­ys­tem ras­ter. Hasta se la puede inyectar en argv si la plataforma es darwin.

Clavando un clavo con un zapato I: Do-Sheet.

  1. Te ha­­ce pen­sar di­­fe­­ren­­te.

  2. Es di­­ve­r­­ti­­do.

Lo ma­lo es, por su­pues­to, que el con­te­ni­do de la char­la tie­ne que ser se­cre­to, o no tie­ne nin­gu­na gra­cia. Co­mo el pro­ce­so de re­view pa­ra char­las de Py­Co­nAr es pú­bli­co, no te­nía ma­ne­ra de ex­pli­car de qué se tra­ta­ba.

Co­mo eso sig­ni­fi­ca que pon­go a los re­vi­so­res en el com­pro­mi­so de te­ner que acep­tar mi pa­la­bra de que es­ta char­la es al­go in­te­re­san­te, y eso es injus­to pa­ra ellos y los de­más char­la­ri­nes, can­ce­lé la pro­pues­ta.

La (tal ve­z) bue­na no­ti­cia es que aho­ra to­dos van a po­der ver de qué se tra­ta­ba la char­la. Acá es­tá el cla­vo nú­me­ro 1: Es­cri­bir una ho­ja de cál­cu­lo usan­do doi­t.

Es­ta no es mi pri­me­ra "ho­ja de cál­cu­lo­". To­do em­pe­zó ha­ce mu­cho, mu­cho tiem­po con una fa­mo­sa re­ce­ta de Ra­y­mond He­ttin­ger que he usa­do una y otra y otra vez (ca­paz que fal­ta al­gu­na).

Da­do que ven­go usan­do doit pa­ra Niko­la es­toy im­pre­sio­na­do con lo po­ten­te que es. En bre­ve, doit te per­mi­te crear ta­rea­s, y esas ta­reas de­pen­den de otra­s, ope­ran en da­to­s, dan re­sul­ta­dos que otras ta­reas usan, etc.

¿Se ve adon­de va es­to?

Acá va el có­di­go, con ex­pli­ca­cio­nes:

cells es nuestra hoja. Podés poner cualquier cosa, pero usá siempre el formato "nombre=formula" y la fórmula tiene que ser Python válido ¿ok?

from tokenize import generate_tokens

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

task_calculate crea una tarea para cada celda, llamada calculate:NOMBRE. La acción que esa tarea realiza es evaluar la fórmula. Pero para hacer eso de manera exitosa, necesitamos saber qué otras celdas hay que evaluar primero.

Eso lo im­ple­men­té usan­do las cal­cu­lated de­pen­den­cies de doi­t, con la ta­rea "ge­t_­de­p:­FOR­MU­LA" pa­ra la fór­mu­la de es­ta cel­da.

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))],
            }

Por ejemplo, nuestra A1 depende de A3 y A2 que no dependen de nada. Para parsear esto, usé el módulo tokenize, y tomo nota de cuales cosas son "nombres". Existen maneras más soisticadas ;-)

la función task_get_dep es una tarea de doit que crea tareas llamadas "get_dep:NOMBRE" para cada nombre de celda en cells.

A su vez, get_dep devuelve una lista de tareas de doit. Para nuestra celda A1, eso sería ["calculate:A2", "calculate:A3"] o sea que para poder calcular A1 primero tenemos que terminar esas tareas.

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,))],
            }

Y eso es todo. Veámoslo en acción. Podés obtener tu propia copia acá y probarlo instalando doit, editando cells y usándolo así:

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

Co­mo po­dés ve­r, siem­pre ha­ce el mí­ni­mo es­fuer­zo po­si­ble pa­ra cal­cu­lar el re­sul­ta­do de­sea­do. Si que­ré­s, hay al­gu­nas co­sas me­jo­ra­ble­s, que de­jo co­mo ejer­ci­cio pa­ra el lec­to­r. Por ejem­plo:

  1. Usar up­­to­­­da­­te pa­­ra no re­­ca­l­­cu­­lar de­­pen­­den­­cias inu­­ti­l­­men­­te.

  2. Eli­mi­nar la va­ria­ble glo­bal va­lues y usar los com­puted va­lues de doit en su lu­ga­r.

Acá es­tá el lis­ta­do com­ple­to, buen pro­ve­cho!

Tu Editor No Te Está Frenando

Mi­re­mos SLO­C, que es­tá des­acre­di­ta­da co­mo me­di­da de la pro­duc­ti­vi­dad de un pro­gra­ma­do­r, pe­ro se­gu­ra­men­te es una me­di­da ade­cua­da de lo que el pro­gra­ma­dor ti­pea.

Bue­no, las es­ti­ma­cio­nes de có­di­go to­tal pro­du­ci­do en la vi­da de un pro­duc­to son muy va­ria­bles (to­man­do las SLOC del pro­duc­to y di­vi­dien­do por la canti­dad de hom­bres/­día gas­ta­do­s), pe­ro sue­len os­ci­lar en­tre 10 y 100 SLOC por pro­gra­ma­dor por día. Sea­mos ge­ne­ro­sos y di­ga­mos 200.

En­ton­ces 200 lí­neas en 8 ho­ra­s. Eso es más o me­nos una lí­nea ca­da dos mi­nu­tos y la lí­nea pro­me­dio son unos 45 ca­rac­te­res. Asu­mien­do que sa­bés es­cri­bir al tac­to (y si no sa­bé­s, qué ver­güen­za), te pue­de lle­var 20 se­gun­do­s.

En­ton­ce­s, es­cri­bi­r, que es lo que mu­chos op­ti­mi­zan en su edi­to­r, lle­va me­nos del 15% de tu tiem­po. ¿Y cuán­to más rá­pi­do po­dés se­r? ¿Po­dés sa­car esa lí­nea en 10 se­gun­do­s? En­ton­ces aca­bás de li­be­rar el 8% de tu día. Y no creo que tu edi­tor te aho­rre la mi­tad del tiem­po de ti­pea­do.

¿Cuán­to tiem­po per­dés con los ojos flo­tan­do por los si­de­bar­s, los bo­to­nes, las tool­bar­s, etc?

En­ton­ces sí, ti­pear más rá­pi­do es una op­ti­mi­za­ció­n, pe­ro tal vez es pre­ma­tu­ra, por­que ¿Qué miérco­les es­ta­mos ha­cien­do el otro 80% del tiem­po? ¿No hay na­da que po­da­mos ha­cer pa­ra que ese pe­da­zo enor­me de tiem­po sea más efi­cien­te en vez del pe­da­zo chi­qui­to?

Creo que usa­mos gran par­te de ese tiem­po en tres co­sas:

  1. Leer có­­­di­­go

  2. Pen­sar qué có­­­di­­go es­­cri­­bir

  3. Arre­­glar el có­­­di­­go que es­­cri­­bi­­mos

la pri­me­ra es fá­ci­l, ne­ce­si­ta­mos me­jo­res lec­to­res no edi­to­res. Es una pe­na que la in­ter­fa­ce por de­fault pa­ra mi­rar có­di­go sea un edi­to­r, con su cons­tant in­vi­ta­ción a cam­biar lo que de­be­ría­mos es­tar ob­ser­van­do. Cre que hay una opor­tu­ni­dad per­di­da ahí, en al­gu­na par­te, de te­ner una apli­ca­ción en la que se pue­da ver el có­di­go de ma­ne­ras in­te­re­san­tes y ori­gi­na­la­es, pa­ra en­ten­der­lo me­jo­r.

La se­gun­da es más di­fí­ci­l, por­que es per­so­na­l. Yo ca­mino. Si me ves ca­mi­nan­do con el edi­tor abier­to, es­toy pen­san­do. Des­pués de que pien­so, es­cri­bo. A mí me sir­ve.

La ter­ce­ra es la más di­fí­ci­l. Sí, el au­to­com­ple­ta­do sir­ve por­que no te­nés tan­tos ty­po­s, pe­ro hay en­fo­ques más po­de­ro­so­s, co­mo co­rrer cons­tante­men­te test sui­tes mien­tras edi­tá­s. Ca­da vez que de­jás una lí­nea, lan­zar los tes­ts afec­ta­do­s.

Eso es su­ma­men­te di­fí­cil de im­ple­men­tar por­que tus he­rra­mien­tas de­be­rían re­la­cio­nar tu sui­te de tes­ts con tu có­di­go muy pre­ci­sa­men­te, pa­ra que veas que rom­pis­te co­sas en el se­gun­do que las rom­pis­te, no un mi­nu­to des­pué­s.

Tam­bién se de­be­ría po­der sal­tar a los tes­ts con una te­cla, pa­ra que pue­das arre­glar los tes­ts si es­tás cam­bian­do com­por­ta­mien­to en tu có­di­go. Y por su­pues­to sig­ni­fi­ca que ne­ce­si­tás tes­ts ;-)

Lo que me trar a una co­sa que me pi­ca ha­ce mu­cho, que los edi­to­res tra­tan al ar­chi­vo co­mo uni­dad de tra­ba­jo, que no tie­ne mu­cho sen­ti­do. ¿Cuan­do tu­vis­te ga­nas de edi­tar un ar­chi­vo? ¡Nun­ca! Que­rés edi­tar una cla­se, una fun­ció­n, un mé­to­do, una cons­tan­te, no un ar­chi­vo. Sa­ber eso era el ge­nio del an­ti­guo Vi­sual Ba­si­c, ig­no­ra­do por to­dos los que lo mi­ran con­des­cen­dien­te­men­te.

Así que en vez de pa­vear con tu edi­to­r, ha­cé­me una he­rra­mien­ta que ha­ga lo que ne­ce­si­to, por fa­vo­r. La ven­go es­pe­ran­do des­de VB 1.0. Eso y un san­dwi­ch.

UP­DA­TE: In­te­re­san­tes dis­cu­sio­nes en re­ddit y ha­cker news


Contents © 2000-2023 Roberto Alsina