Js2Py icon indicating copy to clipboard operation
Js2Py copied to clipboard

Not having any luck using the standard ssf.js as ssf.py with dates

Open snoopyjc opened this issue 5 years ago • 9 comments

The ssf.js module (https://raw.githubusercontent.com/SheetJS/ssf/master/ssf.js) formats objects using the Excel number format. Here is the usage:

SSF.format(fmt, val, opts) formats val using the format fmt.

I converted it to python as follows (to avoid the unicode error):

>>> js = open('ssf.js', 'r', encoding="utf-8").read()
>>> py = js2py.translate_js(js)
>>> open('ssf.py', 'w', encoding="utf-8").write(py)
142637

First of all, I get this warning with no indication of where it's coming from:

>>> import ssf
C:\Users\Joe Cool\AppData\Local\Programs\Python\Python37-32\lib\site-packages\js2py\const
ructors\time_helpers.py:44: Warning: Invalid datetime date, assumed DST time, may be inacc
urate...
  Warning)

Now, I need to figure out how to use this module from python. Help is no use:

>>> help(ssf)
Help on module ssf:

NAME
    ssf

DATA
    JS_BUILTINS = {'Array': 'function Array() { [python code] }', 'ArrayBu...
    PyJs_make_ssf_0_ = 'function make_ssf(SSF) { [python code] }'
    var = [Object Global]

FILE
    d:\python\xls2xlsx\translated\ssf.py

Not sure if this is the right way, but I did get this to sort-of work:

>>> import ssf
>>> SSF = ssf.var['SSF']
>>> format = SSF['format']
>>> format('#', 1)
'1'

Now a little more complicated:

>>> format('# ?/?', 1.5)
'1 1/2'

So far so good. Now let's try a date:

>>> from datetime import date
>>> format('mmm d, yyyy', date(2020, 1, 3))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Users\Joe Cool\AppData\Local\Programs\Python\Python37-32\lib\site-packages\js2
py\base.py", line 949, in __call__
    return self.call(self.GlobalObject, args)
  File "C:\Users\Joe Cool\AppData\Local\Programs\Python\Python37-32\lib\site-packages\js2
py\base.py", line 1464, in call
    return Js(self.code(*args))
  File "D:\python\xls2xlsx\translated\ssf.py", line 1419, in PyJsHoisted_format_
    return var.get('eval_fmt')(var.get('f').get('1'), var.get('v'), var.get('o'), var.get(
'f').get('0'))
  File "C:\Users\Joe Cool\AppData\Local\Programs\Python\Python37-32\lib\site-packages\js2
py\base.py", line 949, in __call__
    return self.call(self.GlobalObject, args)
  File "C:\Users\Joe Cool\AppData\Local\Programs\Python\Python37-32\lib\site-packages\js2
py\base.py", line 1464, in call
    return Js(self.code(*args))
  File "D:\python\xls2xlsx\translated\ssf.py", line 1279, in PyJsHoisted_eval_fmt_
    var.put('retval', var.get('out').get(var.get('i')).get('v'), '+')
  File "C:\Users\Joe Cool\AppData\Local\Programs\Python\Python37-32\lib\site-packages\js2
py\base.py", line 1127, in put
    val = getattr(self.own[lval], OP_METHODS[op])(val)
  File "C:\Users\Joe Cool\AppData\Local\Programs\Python\Python37-32\lib\site-packages\js2
py\base.py", line 776, in __add__
    b = other.to_primitive()
  File "C:\Users\Joe Cool\AppData\Local\Programs\Python\Python37-32\lib\site-packages\js2
py\base.py", line 548, in to_primitive
    return self.default_value(hint)
  File "C:\Users\Joe Cool\AppData\Local\Programs\Python\Python37-32\lib\site-packages\js2
py\base.py", line 456, in default_value
    'Cannot convert object to primitive value')
js2py.internals.simplex.JsException: TypeError: Cannot convert object to primitive value

No luck there, let's make the date a string instead:

>>> format('mmm d, yyyy', '1/3/2020')
'1/3/2020'

Not quite as expected. Let's pass in the Excel integer corresponding to a date:

>>> format('mmm d, yyyy', 47712)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Users\Joe Cool\AppData\Local\Programs\Python\Python37-32\lib\site-packages\js2
py\base.py", line 949, in __call__
    return self.call(self.GlobalObject, args)
  File "C:\Users\Joe Cool\AppData\Local\Programs\Python\Python37-32\lib\site-packages\js2
py\base.py", line 1464, in call
    return Js(self.code(*args))
  File "D:\python\xls2xlsx\translated\ssf.py", line 1419, in PyJsHoisted_format_
    return var.get('eval_fmt')(var.get('f').get('1'), var.get('v'), var.get('o'), var.get(
'f').get('0'))
  File "C:\Users\Joe Cool\AppData\Local\Programs\Python\Python37-32\lib\site-packages\js2
py\base.py", line 949, in __call__
    return self.call(self.GlobalObject, args)
  File "C:\Users\Joe Cool\AppData\Local\Programs\Python\Python37-32\lib\site-packages\js2
py\base.py", line 1464, in call
    return Js(self.code(*args))
  File "D:\python\xls2xlsx\translated\ssf.py", line 834, in PyJsHoisted_eval_fmt_
    var.put('dt', var.get('parse_date_code')(var.get('v'), var.get('opts')))
  File "C:\Users\Joe Cool\AppData\Local\Programs\Python\Python37-32\lib\site-packages\js2
py\base.py", line 949, in __call__
    return self.call(self.GlobalObject, args)
  File "C:\Users\Joe Cool\AppData\Local\Programs\Python\Python37-32\lib\site-packages\js2
py\base.py", line 1464, in call
    return Js(self.code(*args))
  File "D:\python\xls2xlsx\translated\ssf.py", line 198, in PyJsHoisted_parse_date_code_
    var.get('d').callprop('setDate', ((var.get('d').callprop('getDate')+var.get('date'))-J
s(1.0)))
  File "C:\Users\Joe Cool\AppData\Local\Programs\Python\Python37-32\lib\site-packages\js2
py\base.py", line 995, in callprop
    cand.typeof(), repr(prop), repr(self.Class)))
js2py.internals.simplex.JsException: TypeError: 'undefined' is not a function (tried calli
ng property 'setDate' of 'Date')

Ok, here is where I gave up!

snoopyjc avatar Sep 13 '20 15:09 snoopyjc

Seems your last attempt found the problem: Date.prototype.setDate is not defined and is used in line 166 of ssf.js I'll have to look at the spec to see what should be done about it.

I made these usage examples while testing:

import js2py
js2py.translate_file('ssf.js', 'ssf.py')
from ssf import ssf
print ssf.SSF.format("General",js2py.eval_js("new Date(2020, 1, 3)"))
import js2py
js = open('ssf.js', 'r').read().decode('utf8')
context = js2py.EvalJs()
context.execute(js)
context.execute('''console.log(SSF.format("General", new Date(2017, 1, 19)))''')

worstperson avatar Sep 13 '20 21:09 worstperson

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setDate

snoopyjc avatar Sep 13 '20 23:09 snoopyjc

I made this hacky implementation for now:

    def setDate(date):
        check_date(this)
        t = UTCToLocal(this.value)
        dt = date.to_number()
        time = MakeTime(Number(HourFromTime(t)), Number(MinFromTime(t)), Number(SecFromTime(t)), Number(msFromTime(t)))
        newDate = MakeDate(MakeDay(Number(YearFromTime(t)), Number(MonthFromTime(t)), dt), time)
        u = TimeClip(LocalToUTC(newDate))
        this.value = u
        return u

It goes in js2py/constructors/jsdate.py and js2py/internals/constructors/jsdate.py just above the comment "# todo Complete all setters!". ssf then works great, but the fields returned can be off by one. Still need to find this bug.

worstperson avatar Sep 14 '20 01:09 worstperson

Thanks!!

Sounds like this should be added to your test suite with a handful of test cases!

BTW, is there any way to generate a self-contained output file, eg not dependent on js2py?

I’m trying to generate a python version of this library.

snoopyjc avatar Sep 14 '20 04:09 snoopyjc

I think it already has tests, it's just that it and a few other datetime setter functions are currently unimplemented.

No way to get around needing a js interpreter without actually porting the code, though.

worstperson avatar Sep 14 '20 04:09 worstperson

Ok but how about the “off by 1” issue you mentioned?

snoopyjc avatar Sep 14 '20 14:09 snoopyjc

It'll take a bit for me to find the issue as I need to debug the library to find exactly what triggers it. I'll reply back as soon as I have the solution.

worstperson avatar Sep 14 '20 17:09 worstperson

js2py/internals/constructors/time_helpers.py, line 45, change it the return value from 1 to 0

This just disables DST in js2py. Surely there is still a bug here, but this seems to correct the issue for now. Results are the same as in my browser.

worstperson avatar Sep 15 '20 00:09 worstperson

Thanks!

Yeah time zones are like the worst things to deal with in the land of programming. Worse than even file encodings! :-)

Thanks!

-Joe Sent from my AT&T iPad

On Sep 14, 2020, at 8:30 PM, worstperson [email protected] wrote:

 js2py/internals/constructors/time_helpers.py, line 45, change it the return value from 1 to 0

This just disables DST in js2py. Surely there is still a bug here, but this seems to correct the issue for now. Results are the same as in my browser.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or unsubscribe.

snoopyjc avatar Sep 19 '20 02:09 snoopyjc