From d273a28d54b3f0478e568e59b0d9159c68a8731d Mon Sep 17 00:00:00 2001 From: SheetJS Date: Sun, 7 May 2017 23:49:05 -0400 Subject: [PATCH] version bump 0.9.2: more formats - do not consume trailing currency symbol (fixes #19 h/t @wilg) - detect minutes following absolute hour (fixes #23 h/t @tedbeer) - verify the miscellaneous formats (fixes #27 h/t @reviewher) --- Makefile | 2 +- bits/01_version.js | 2 +- bits/60_number.js | 6 +- bits/63_numflt.js | 7 +- bits/65_numinth.js | 6 +- bits/66_numint.js | 14 +- bits/82_eval.js | 7 +- bits/90_main.js | 1 - package.json | 2 +- ssf.flow.js | 43 +++-- ssf.js | 35 ++-- test/comma.tsv | 32 ++-- test/valid.js | 19 ++ test/valid.tsv | 442 +++++++++++++++++++++++++++++++++++++++++++++ 14 files changed, 559 insertions(+), 59 deletions(-) create mode 100644 test/valid.js create mode 100644 test/valid.tsv diff --git a/Makefile b/Makefile index f490d39..a6f56d2 100755 --- a/Makefile +++ b/Makefile @@ -51,7 +51,7 @@ travis: ## Run test suite with minimal output .PHONY: ctest ctest: - browserify -t brfs test/{dateNF,exp,fraction,general,implied,oddities,utilities,comma}.js > ctest/test.js + browserify -t brfs test/{dateNF,exp,fraction,general,implied,oddities,utilities,comma,valid}.js > ctest/test.js .PHONY: ctestserv ctestserv: ## Start a test server on port 8000 diff --git a/bits/01_version.js b/bits/01_version.js index e54e7fd..7528349 100644 --- a/bits/01_version.js +++ b/bits/01_version.js @@ -1 +1 @@ -SSF.version = '0.9.1'; +SSF.version = '0.9.2'; diff --git a/bits/60_number.js b/bits/60_number.js index 093568c..c14a2ec 100644 --- a/bits/60_number.js +++ b/bits/60_number.js @@ -7,12 +7,14 @@ function write_num_exp(fmt/*:string*/, val/*:number*/)/*:string*/{ var o/*:string*/; var idx = fmt.indexOf("E") - fmt.indexOf(".") - 1; if(fmt.match(/^#+0.0E\+0$/)) { + if(val == 0) return "0.0E+0"; + else if(val < 0) return "-" + write_num_exp(fmt, -val); var period = fmt.indexOf("."); if(period === -1) period=fmt.indexOf('E'); - var ee = Math.floor(Math.log(Math.abs(val))*Math.LOG10E)%period; + var ee = Math.floor(Math.log(val)*Math.LOG10E)%period; if(ee < 0) ee += period; o = (val/Math.pow(10,ee)).toPrecision(idx+1+(period+ee)%period); if(o.indexOf("e") === -1) { - var fakee = Math.floor(Math.log(Math.abs(val))*Math.LOG10E); + var fakee = Math.floor(Math.log(val)*Math.LOG10E); if(o.indexOf(".") === -1) o = o.charAt(0) + "." + o.substr(1) + "E+" + (fakee - o.length+ee); else o += "E+" + (fakee - ee); while(o.substr(0,2) === "0.") { diff --git a/bits/63_numflt.js b/bits/63_numflt.js index f907f11..6644977 100644 --- a/bits/63_numflt.js +++ b/bits/63_numflt.js @@ -25,7 +25,7 @@ function write_num_flt(type/*:string*/, fmt/*:string*/, val/*:number*/)/*:string if((r = fmt.match(/^(0*)\.(#*)$/))) { return sign + rnd(aval, r[2].length).replace(/\.(\d*[1-9])0*$/,".$1").replace(/^(-?\d*)$/,"$1.").replace(/^0\./,r[1].length?"0.":"."); } - if((r = fmt.match(/^#,##0(\.?)$/))) return sign + commaify(pad0r(aval,0)); + if((r = fmt.match(/^#{1,3},##0(\.?)$/))) return sign + commaify(pad0r(aval,0)); if((r = fmt.match(/^#,##0\.([#0]*0)$/))) { return val < 0 ? "-" + write_num_flt(type, fmt, -val) : commaify(""+(Math.floor(val) + carry(val, r[1].length))) + "." + pad0(dec(val, r[1].length),r[1].length); } @@ -73,7 +73,12 @@ function write_num_flt(type/*:string*/, fmt/*:string*/, val/*:number*/)/*:string return val < 0 ? "-" + write_num_flt(type, fmt, -val) : commaify(flr(val)).replace(/^\d,\d{3}$/,"0$&").replace(/^\d*$/,function($$) { return "00," + ($$.length < 3 ? pad0(0,3-$$.length) : "") + $$; }) + "." + pad0(ri,r[1].length); } switch(fmt) { + case "###,##0.00": return write_num_flt(type, "#,##0.00", val); + case "###,###": + case "##,###": case "#,###": var x = commaify(pad0r(aval,0)); return x !== "0" ? sign + x : ""; + case "###,###.00": return write_num_flt(type, "###,##0.00",val).replace(/^0\./,"."); + case "#,###.00": return write_num_flt(type, "#,##0.00",val).replace(/^0\./,"."); default: } throw new Error("unsupported format |" + fmt + "|"); diff --git a/bits/65_numinth.js b/bits/65_numinth.js index 4a653e5..e86a5ef 100644 --- a/bits/65_numinth.js +++ b/bits/65_numinth.js @@ -11,12 +11,14 @@ function write_num_exp2(fmt/*:string*/, val/*:number*/)/*:string*/{ var o/*:string*/; var idx = fmt.indexOf("E") - fmt.indexOf(".") - 1; if(fmt.match(/^#+0.0E\+0$/)) { + if(val == 0) return "0.0E+0"; + else if(val < 0) return "-" + write_num_exp2(fmt, -val); var period = fmt.indexOf("."); if(period === -1) period=fmt.indexOf('E'); - var ee = Math.floor(Math.log(Math.abs(val))*Math.LOG10E)%period; + var ee = Math.floor(Math.log(val)*Math.LOG10E)%period; if(ee < 0) ee += period; o = (val/Math.pow(10,ee)).toPrecision(idx+1+(period+ee)%period); if(!o.match(/[Ee]/)) { - var fakee = Math.floor(Math.log(Math.abs(val))*Math.LOG10E); + var fakee = Math.floor(Math.log(val)*Math.LOG10E); if(o.indexOf(".") === -1) o = o.charAt(0) + "." + o.substr(1) + "E+" + (fakee - o.length+ee); else o += "E+" + (fakee - ee); o = o.replace(/\+-/,"-"); diff --git a/bits/66_numint.js b/bits/66_numint.js index 69399f2..21bfb9a 100644 --- a/bits/66_numint.js +++ b/bits/66_numint.js @@ -9,7 +9,7 @@ function write_num_int(type/*:string*/, fmt/*:string*/, val/*:number*/)/*:string if(fmt.indexOf('E') !== -1) return write_num_exp2(fmt, val); if(fmt.charCodeAt(0) === 36) return "$"+write_num_int(type,fmt.substr(fmt.charAt(1)==' '?2:1),val); var o; - var r, ri, ff, aval = Math.abs(val), sign = val < 0 ? "-" : ""; + var r/*:?Array*/, ri, ff, aval = Math.abs(val), sign = val < 0 ? "-" : ""; if(fmt.match(/^00+$/)) return sign + pad0(aval,fmt.length); if(fmt.match(/^[#?]+$/)) { o = (""+val); if(val === 0) o = ""; @@ -29,7 +29,7 @@ function write_num_int(type/*:string*/, fmt/*:string*/, val/*:number*/)/*:string if((r = fmt.match(/^(0*)\.(#*)$/))) { return sign + (""+aval).replace(/\.(\d*[1-9])0*$/,".$1").replace(/^(-?\d*)$/,"$1.").replace(/^0\./,r[1].length?"0.":"."); } - if((r = fmt.match(/^#,##0(\.?)$/))) return sign + commaify((""+aval)); + if((r = fmt.match(/^#{1,3},##0(\.?)$/))) return sign + commaify((""+aval)); if((r = fmt.match(/^#,##0\.([#0]*0)$/))) { return val < 0 ? "-" + write_num_int(type, fmt, -val) : commaify((""+val)) + "." + fill('0',r[1].length); } @@ -45,12 +45,12 @@ function write_num_int(type/*:string*/, fmt/*:string*/, val/*:number*/)/*:string } var oa = ""; if((r = fmt.match(/^([#0?]+)( ?)\/( ?)([#0?]+)/))) { - ri = Math.min(r[4].length,7); + ri = Math.min(/*::String(*/r[4]/*::)*/.length,7); ff = frac(aval, Math.pow(10,ri)-1, false); o = "" + sign; - oa = write_num("n", r[1], ff[1]); + oa = write_num("n", /*::String(*/r[1]/*::)*/, ff[1]); if(oa.charAt(oa.length-1) == " ") oa = oa.substr(0,oa.length-1) + "0"; - o += oa + r[2] + "/" + r[3]; + o += oa + /*::String(*/r[2]/*::)*/ + "/" + /*::String(*/r[3]/*::)*/; oa = rpad_(ff[2],ri); if(oa.length < r[4].length) oa = hashq(r[4].substr(r[4].length-oa.length)) + oa; o += oa; @@ -76,8 +76,12 @@ function write_num_int(type/*:string*/, fmt/*:string*/, val/*:number*/)/*:string return val < 0 ? "-" + write_num_int(type, fmt, -val) : commaify(""+val).replace(/^\d,\d{3}$/,"0$&").replace(/^\d*$/,function($$) { return "00," + ($$.length < 3 ? pad0(0,3-$$.length) : "") + $$; }) + "." + pad0(0,r[1].length); } switch(fmt) { + case "###,###": + case "##,###": case "#,###": var x = commaify(""+aval); return x !== "0" ? sign + x : ""; default: + if(fmt.slice(-3) == ".00") return write_num_int(type, fmt.slice(0,-3), val) + ".00"; + if(fmt.slice(-2) == ".0") return write_num_int(type, fmt.slice(0,-2), val) + ".0"; } throw new Error("unsupported format |" + fmt + "|"); } diff --git a/bits/82_eval.js b/bits/82_eval.js index 764f39f..8fc4efa 100644 --- a/bits/82_eval.js +++ b/bits/82_eval.js @@ -28,7 +28,7 @@ function eval_fmt(fmt/*:string*/, v/*:any*/, opts/*:any*/, flen/*:number*/) { if(v < 0) return ""; if(dt==null) { dt=parse_date_code(v, opts); if(dt==null) return ""; } o = c; while(++i -1) { o = (o.match(/\$([^-\[\]]*)/)||[])[1]||"$"; if(!fmt_is_date(fmt)) out[out.length] = {t:'t',v:o}; @@ -59,7 +60,7 @@ function eval_fmt(fmt/*:string*/, v/*:any*/, opts/*:any*/, flen/*:number*/) { } /* falls through */ case '0': case '#': - o = c; while(++i < fmt.length && "0#?.,E+-%".indexOf(c=fmt.charAt(i)) > -1 || c=='\\' && fmt.charAt(i+1) == "-" && "0#".indexOf(fmt.charAt(i+2))>-1) o += c; + o = c; while(++i < fmt.length && "0#?.,E+-%".indexOf(c=fmt.charAt(i)) > -1 || c=='\\' && fmt.charAt(i+1) == "-" && i < fmt.length - 2 && "0#".indexOf(fmt.charAt(i+2))>-1) o += c; out[out.length] = {t:'n', v:o}; break; case '?': o = c; while(fmt.charAt(++i) === c) o+=c; @@ -123,7 +124,7 @@ function eval_fmt(fmt/*:string*/, v/*:any*/, opts/*:any*/, flen/*:number*/) { (c=out[jj].t) === "?" || c === "D" || (c === " " || c === "t") && out[jj+1] != null && (out[jj+1].t === '?' || out[jj+1].t === "t" && out[jj+1].v === '/') || out[i].t === '(' && (c === ' ' || c === 'n' || c === ')') || - c === 't' && (out[jj].v === '/' || '$€'.indexOf(out[jj].v) > -1 || out[jj].v === ' ' && out[jj+1] != null && out[jj+1].t == '?') + c === 't' && (out[jj].v === '/' || out[jj].v === ' ' && out[jj+1] != null && out[jj+1].t == '?') )) { out[i].v += out[jj].v; out[jj] = {v:"", t:";"}; ++jj; diff --git a/bits/90_main.js b/bits/90_main.js index d06a839..a1c7b72 100644 --- a/bits/90_main.js +++ b/bits/90_main.js @@ -21,7 +21,6 @@ function choose_fmt(f/*:string*/, v) { } function format(fmt/*:string|number*/,v/*:any*/,o/*:?any*/) { if(o == null) o = {}; - //fixopts(o != null ? o : (o=[])); var sfmt = ""; switch(typeof fmt) { case "string": diff --git a/package.json b/package.json index 0840ec0..cc9ae5d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ssf", - "version": "0.9.1", + "version": "0.9.2", "author": "SheetJS", "description": "Format data using ECMA-376 spreadsheet Format Codes", "keywords": [ "format", "sprintf", "spreadsheet" ], diff --git a/ssf.flow.js b/ssf.flow.js index ae9e124..822a125 100644 --- a/ssf.flow.js +++ b/ssf.flow.js @@ -2,7 +2,7 @@ /*jshint -W041 */ var SSF = {}; var make_ssf = function make_ssf(SSF){ -SSF.version = '0.9.1'; +SSF.version = '0.9.2'; function _strrev(x/*:string*/)/*:string*/ { var o = "", i = x.length-1; while(i>=0) o += x.charAt(i--); return o; } function fill(c/*:string*/,l/*:number*/)/*:string*/ { var o = ""; while(o.length < l) o+=c; return o; } function pad0(v/*:any*/,d/*:number*/)/*:string*/{var t=""+v; return t.length>=d?t:fill('0',d-t.length)+t;} @@ -270,12 +270,14 @@ function write_num_exp(fmt/*:string*/, val/*:number*/)/*:string*/{ var o/*:string*/; var idx = fmt.indexOf("E") - fmt.indexOf(".") - 1; if(fmt.match(/^#+0.0E\+0$/)) { + if(val == 0) return "0.0E+0"; + else if(val < 0) return "-" + write_num_exp(fmt, -val); var period = fmt.indexOf("."); if(period === -1) period=fmt.indexOf('E'); - var ee = Math.floor(Math.log(Math.abs(val))*Math.LOG10E)%period; + var ee = Math.floor(Math.log(val)*Math.LOG10E)%period; if(ee < 0) ee += period; o = (val/Math.pow(10,ee)).toPrecision(idx+1+(period+ee)%period); if(o.indexOf("e") === -1) { - var fakee = Math.floor(Math.log(Math.abs(val))*Math.LOG10E); + var fakee = Math.floor(Math.log(val)*Math.LOG10E); if(o.indexOf(".") === -1) o = o.charAt(0) + "." + o.substr(1) + "E+" + (fakee - o.length+ee); else o += "E+" + (fakee - ee); while(o.substr(0,2) === "0.") { @@ -353,7 +355,7 @@ function write_num_flt(type/*:string*/, fmt/*:string*/, val/*:number*/)/*:string if((r = fmt.match(/^(0*)\.(#*)$/))) { return sign + rnd(aval, r[2].length).replace(/\.(\d*[1-9])0*$/,".$1").replace(/^(-?\d*)$/,"$1.").replace(/^0\./,r[1].length?"0.":"."); } - if((r = fmt.match(/^#,##0(\.?)$/))) return sign + commaify(pad0r(aval,0)); + if((r = fmt.match(/^#{1,3},##0(\.?)$/))) return sign + commaify(pad0r(aval,0)); if((r = fmt.match(/^#,##0\.([#0]*0)$/))) { return val < 0 ? "-" + write_num_flt(type, fmt, -val) : commaify(""+(Math.floor(val) + carry(val, r[1].length))) + "." + pad0(dec(val, r[1].length),r[1].length); } @@ -401,7 +403,12 @@ function write_num_flt(type/*:string*/, fmt/*:string*/, val/*:number*/)/*:string return val < 0 ? "-" + write_num_flt(type, fmt, -val) : commaify(flr(val)).replace(/^\d,\d{3}$/,"0$&").replace(/^\d*$/,function($$) { return "00," + ($$.length < 3 ? pad0(0,3-$$.length) : "") + $$; }) + "." + pad0(ri,r[1].length); } switch(fmt) { + case "###,##0.00": return write_num_flt(type, "#,##0.00", val); + case "###,###": + case "##,###": case "#,###": var x = commaify(pad0r(aval,0)); return x !== "0" ? sign + x : ""; + case "###,###.00": return write_num_flt(type, "###,##0.00",val).replace(/^0\./,"."); + case "#,###.00": return write_num_flt(type, "#,##0.00",val).replace(/^0\./,"."); default: } throw new Error("unsupported format |" + fmt + "|"); @@ -419,12 +426,14 @@ function write_num_exp2(fmt/*:string*/, val/*:number*/)/*:string*/{ var o/*:string*/; var idx = fmt.indexOf("E") - fmt.indexOf(".") - 1; if(fmt.match(/^#+0.0E\+0$/)) { + if(val == 0) return "0.0E+0"; + else if(val < 0) return "-" + write_num_exp2(fmt, -val); var period = fmt.indexOf("."); if(period === -1) period=fmt.indexOf('E'); - var ee = Math.floor(Math.log(Math.abs(val))*Math.LOG10E)%period; + var ee = Math.floor(Math.log(val)*Math.LOG10E)%period; if(ee < 0) ee += period; o = (val/Math.pow(10,ee)).toPrecision(idx+1+(period+ee)%period); if(!o.match(/[Ee]/)) { - var fakee = Math.floor(Math.log(Math.abs(val))*Math.LOG10E); + var fakee = Math.floor(Math.log(val)*Math.LOG10E); if(o.indexOf(".") === -1) o = o.charAt(0) + "." + o.substr(1) + "E+" + (fakee - o.length+ee); else o += "E+" + (fakee - ee); o = o.replace(/\+-/,"-"); @@ -446,7 +455,7 @@ function write_num_int(type/*:string*/, fmt/*:string*/, val/*:number*/)/*:string if(fmt.indexOf('E') !== -1) return write_num_exp2(fmt, val); if(fmt.charCodeAt(0) === 36) return "$"+write_num_int(type,fmt.substr(fmt.charAt(1)==' '?2:1),val); var o; - var r, ri, ff, aval = Math.abs(val), sign = val < 0 ? "-" : ""; + var r/*:?Array*/, ri, ff, aval = Math.abs(val), sign = val < 0 ? "-" : ""; if(fmt.match(/^00+$/)) return sign + pad0(aval,fmt.length); if(fmt.match(/^[#?]+$/)) { o = (""+val); if(val === 0) o = ""; @@ -466,7 +475,7 @@ function write_num_int(type/*:string*/, fmt/*:string*/, val/*:number*/)/*:string if((r = fmt.match(/^(0*)\.(#*)$/))) { return sign + (""+aval).replace(/\.(\d*[1-9])0*$/,".$1").replace(/^(-?\d*)$/,"$1.").replace(/^0\./,r[1].length?"0.":"."); } - if((r = fmt.match(/^#,##0(\.?)$/))) return sign + commaify((""+aval)); + if((r = fmt.match(/^#{1,3},##0(\.?)$/))) return sign + commaify((""+aval)); if((r = fmt.match(/^#,##0\.([#0]*0)$/))) { return val < 0 ? "-" + write_num_int(type, fmt, -val) : commaify((""+val)) + "." + fill('0',r[1].length); } @@ -482,12 +491,12 @@ function write_num_int(type/*:string*/, fmt/*:string*/, val/*:number*/)/*:string } var oa = ""; if((r = fmt.match(/^([#0?]+)( ?)\/( ?)([#0?]+)/))) { - ri = Math.min(r[4].length,7); + ri = Math.min(/*::String(*/r[4]/*::)*/.length,7); ff = frac(aval, Math.pow(10,ri)-1, false); o = "" + sign; - oa = write_num("n", r[1], ff[1]); + oa = write_num("n", /*::String(*/r[1]/*::)*/, ff[1]); if(oa.charAt(oa.length-1) == " ") oa = oa.substr(0,oa.length-1) + "0"; - o += oa + r[2] + "/" + r[3]; + o += oa + /*::String(*/r[2]/*::)*/ + "/" + /*::String(*/r[3]/*::)*/; oa = rpad_(ff[2],ri); if(oa.length < r[4].length) oa = hashq(r[4].substr(r[4].length-oa.length)) + oa; o += oa; @@ -513,8 +522,12 @@ function write_num_int(type/*:string*/, fmt/*:string*/, val/*:number*/)/*:string return val < 0 ? "-" + write_num_int(type, fmt, -val) : commaify(""+val).replace(/^\d,\d{3}$/,"0$&").replace(/^\d*$/,function($$) { return "00," + ($$.length < 3 ? pad0(0,3-$$.length) : "") + $$; }) + "." + pad0(0,r[1].length); } switch(fmt) { + case "###,###": + case "##,###": case "#,###": var x = commaify(""+aval); return x !== "0" ? sign + x : ""; default: + if(fmt.slice(-3) == ".00") return write_num_int(type, fmt.slice(0,-3), val) + ".00"; + if(fmt.slice(-2) == ".0") return write_num_int(type, fmt.slice(0,-2), val) + ".0"; } throw new Error("unsupported format |" + fmt + "|"); } @@ -610,7 +623,7 @@ function eval_fmt(fmt/*:string*/, v/*:any*/, opts/*:any*/, flen/*:number*/) { if(v < 0) return ""; if(dt==null) { dt=parse_date_code(v, opts); if(dt==null) return ""; } o = c; while(++i -1) { o = (o.match(/\$([^-\[\]]*)/)||[])[1]||"$"; if(!fmt_is_date(fmt)) out[out.length] = {t:'t',v:o}; @@ -641,7 +655,7 @@ function eval_fmt(fmt/*:string*/, v/*:any*/, opts/*:any*/, flen/*:number*/) { } /* falls through */ case '0': case '#': - o = c; while(++i < fmt.length && "0#?.,E+-%".indexOf(c=fmt.charAt(i)) > -1 || c=='\\' && fmt.charAt(i+1) == "-" && "0#".indexOf(fmt.charAt(i+2))>-1) o += c; + o = c; while(++i < fmt.length && "0#?.,E+-%".indexOf(c=fmt.charAt(i)) > -1 || c=='\\' && fmt.charAt(i+1) == "-" && i < fmt.length - 2 && "0#".indexOf(fmt.charAt(i+2))>-1) o += c; out[out.length] = {t:'n', v:o}; break; case '?': o = c; while(fmt.charAt(++i) === c) o+=c; @@ -705,7 +719,7 @@ function eval_fmt(fmt/*:string*/, v/*:any*/, opts/*:any*/, flen/*:number*/) { (c=out[jj].t) === "?" || c === "D" || (c === " " || c === "t") && out[jj+1] != null && (out[jj+1].t === '?' || out[jj+1].t === "t" && out[jj+1].v === '/') || out[i].t === '(' && (c === ' ' || c === 'n' || c === ')') || - c === 't' && (out[jj].v === '/' || '$€'.indexOf(out[jj].v) > -1 || out[jj].v === ' ' && out[jj+1] != null && out[jj+1].t == '?') + c === 't' && (out[jj].v === '/' || out[jj].v === ' ' && out[jj+1] != null && out[jj+1].t == '?') )) { out[i].v += out[jj].v; out[jj] = {v:"", t:";"}; ++jj; @@ -819,7 +833,6 @@ function choose_fmt(f/*:string*/, v) { } function format(fmt/*:string|number*/,v/*:any*/,o/*:?any*/) { if(o == null) o = {}; - //fixopts(o != null ? o : (o=[])); var sfmt = ""; switch(typeof fmt) { case "string": diff --git a/ssf.js b/ssf.js index 080f88e..f770131 100644 --- a/ssf.js +++ b/ssf.js @@ -2,7 +2,7 @@ /*jshint -W041 */ var SSF = {}; var make_ssf = function make_ssf(SSF){ -SSF.version = '0.9.1'; +SSF.version = '0.9.2'; function _strrev(x) { var o = "", i = x.length-1; while(i>=0) o += x.charAt(i--); return o; } function fill(c,l) { var o = ""; while(o.length < l) o+=c; return o; } function pad0(v,d){var t=""+v; return t.length>=d?t:fill('0',d-t.length)+t;} @@ -269,12 +269,14 @@ function write_num_exp(fmt, val){ var o; var idx = fmt.indexOf("E") - fmt.indexOf(".") - 1; if(fmt.match(/^#+0.0E\+0$/)) { + if(val == 0) return "0.0E+0"; + else if(val < 0) return "-" + write_num_exp(fmt, -val); var period = fmt.indexOf("."); if(period === -1) period=fmt.indexOf('E'); - var ee = Math.floor(Math.log(Math.abs(val))*Math.LOG10E)%period; + var ee = Math.floor(Math.log(val)*Math.LOG10E)%period; if(ee < 0) ee += period; o = (val/Math.pow(10,ee)).toPrecision(idx+1+(period+ee)%period); if(o.indexOf("e") === -1) { - var fakee = Math.floor(Math.log(Math.abs(val))*Math.LOG10E); + var fakee = Math.floor(Math.log(val)*Math.LOG10E); if(o.indexOf(".") === -1) o = o.charAt(0) + "." + o.substr(1) + "E+" + (fakee - o.length+ee); else o += "E+" + (fakee - ee); while(o.substr(0,2) === "0.") { @@ -352,7 +354,7 @@ function write_num_flt(type, fmt, val) { if((r = fmt.match(/^(0*)\.(#*)$/))) { return sign + rnd(aval, r[2].length).replace(/\.(\d*[1-9])0*$/,".$1").replace(/^(-?\d*)$/,"$1.").replace(/^0\./,r[1].length?"0.":"."); } - if((r = fmt.match(/^#,##0(\.?)$/))) return sign + commaify(pad0r(aval,0)); + if((r = fmt.match(/^#{1,3},##0(\.?)$/))) return sign + commaify(pad0r(aval,0)); if((r = fmt.match(/^#,##0\.([#0]*0)$/))) { return val < 0 ? "-" + write_num_flt(type, fmt, -val) : commaify(""+(Math.floor(val) + carry(val, r[1].length))) + "." + pad0(dec(val, r[1].length),r[1].length); } @@ -400,7 +402,12 @@ function write_num_flt(type, fmt, val) { return val < 0 ? "-" + write_num_flt(type, fmt, -val) : commaify(flr(val)).replace(/^\d,\d{3}$/,"0$&").replace(/^\d*$/,function($$) { return "00," + ($$.length < 3 ? pad0(0,3-$$.length) : "") + $$; }) + "." + pad0(ri,r[1].length); } switch(fmt) { + case "###,##0.00": return write_num_flt(type, "#,##0.00", val); + case "###,###": + case "##,###": case "#,###": var x = commaify(pad0r(aval,0)); return x !== "0" ? sign + x : ""; + case "###,###.00": return write_num_flt(type, "###,##0.00",val).replace(/^0\./,"."); + case "#,###.00": return write_num_flt(type, "#,##0.00",val).replace(/^0\./,"."); default: } throw new Error("unsupported format |" + fmt + "|"); @@ -418,12 +425,14 @@ function write_num_exp2(fmt, val){ var o; var idx = fmt.indexOf("E") - fmt.indexOf(".") - 1; if(fmt.match(/^#+0.0E\+0$/)) { + if(val == 0) return "0.0E+0"; + else if(val < 0) return "-" + write_num_exp2(fmt, -val); var period = fmt.indexOf("."); if(period === -1) period=fmt.indexOf('E'); - var ee = Math.floor(Math.log(Math.abs(val))*Math.LOG10E)%period; + var ee = Math.floor(Math.log(val)*Math.LOG10E)%period; if(ee < 0) ee += period; o = (val/Math.pow(10,ee)).toPrecision(idx+1+(period+ee)%period); if(!o.match(/[Ee]/)) { - var fakee = Math.floor(Math.log(Math.abs(val))*Math.LOG10E); + var fakee = Math.floor(Math.log(val)*Math.LOG10E); if(o.indexOf(".") === -1) o = o.charAt(0) + "." + o.substr(1) + "E+" + (fakee - o.length+ee); else o += "E+" + (fakee - ee); o = o.replace(/\+-/,"-"); @@ -463,7 +472,7 @@ return "." + $1 + fill("0", r[1].length-$1.length); }); if((r = fmt.match(/^(0*)\.(#*)$/))) { return sign + (""+aval).replace(/\.(\d*[1-9])0*$/,".$1").replace(/^(-?\d*)$/,"$1.").replace(/^0\./,r[1].length?"0.":"."); } - if((r = fmt.match(/^#,##0(\.?)$/))) return sign + commaify((""+aval)); + if((r = fmt.match(/^#{1,3},##0(\.?)$/))) return sign + commaify((""+aval)); if((r = fmt.match(/^#,##0\.([#0]*0)$/))) { return val < 0 ? "-" + write_num_int(type, fmt, -val) : commaify((""+val)) + "." + fill('0',r[1].length); } @@ -510,8 +519,12 @@ return "." + $1 + fill("0", r[1].length-$1.length); }); return val < 0 ? "-" + write_num_int(type, fmt, -val) : commaify(""+val).replace(/^\d,\d{3}$/,"0$&").replace(/^\d*$/,function($$) { return "00," + ($$.length < 3 ? pad0(0,3-$$.length) : "") + $$; }) + "." + pad0(0,r[1].length); } switch(fmt) { + case "###,###": + case "##,###": case "#,###": var x = commaify(""+aval); return x !== "0" ? sign + x : ""; default: + if(fmt.slice(-3) == ".00") return write_num_int(type, fmt.slice(0,-3), val) + ".00"; + if(fmt.slice(-2) == ".0") return write_num_int(type, fmt.slice(0,-2), val) + ".0"; } throw new Error("unsupported format |" + fmt + "|"); } @@ -607,7 +620,7 @@ function eval_fmt(fmt, v, opts, flen) { if(v < 0) return ""; if(dt==null) { dt=parse_date_code(v, opts); if(dt==null) return ""; } o = c; while(++i -1) { o = (o.match(/\$([^-\[\]]*)/)||[])[1]||"$"; if(!fmt_is_date(fmt)) out[out.length] = {t:'t',v:o}; @@ -638,7 +652,7 @@ function eval_fmt(fmt, v, opts, flen) { } /* falls through */ case '0': case '#': - o = c; while(++i < fmt.length && "0#?.,E+-%".indexOf(c=fmt.charAt(i)) > -1 || c=='\\' && fmt.charAt(i+1) == "-" && "0#".indexOf(fmt.charAt(i+2))>-1) o += c; + o = c; while(++i < fmt.length && "0#?.,E+-%".indexOf(c=fmt.charAt(i)) > -1 || c=='\\' && fmt.charAt(i+1) == "-" && i < fmt.length - 2 && "0#".indexOf(fmt.charAt(i+2))>-1) o += c; out[out.length] = {t:'n', v:o}; break; case '?': o = c; while(fmt.charAt(++i) === c) o+=c; @@ -699,7 +713,7 @@ out[i].v = write_date(out[i].t.charCodeAt(0), out[i].v, dt, ss0); (c=out[jj].t) === "?" || c === "D" || (c === " " || c === "t") && out[jj+1] != null && (out[jj+1].t === '?' || out[jj+1].t === "t" && out[jj+1].v === '/') || out[i].t === '(' && (c === ' ' || c === 'n' || c === ')') || - c === 't' && (out[jj].v === '/' || '$€'.indexOf(out[jj].v) > -1 || out[jj].v === ' ' && out[jj+1] != null && out[jj+1].t == '?') + c === 't' && (out[jj].v === '/' || out[jj].v === ' ' && out[jj+1] != null && out[jj+1].t == '?') )) { out[i].v += out[jj].v; out[jj] = {v:"", t:";"}; ++jj; @@ -813,7 +827,6 @@ function choose_fmt(f, v) { } function format(fmt,v,o) { if(o == null) o = {}; - //fixopts(o != null ? o : (o=[])); var sfmt = ""; switch(typeof fmt) { case "string": diff --git a/test/comma.tsv b/test/comma.tsv index 85ee283..dee043e 100644 --- a/test/comma.tsv +++ b/test/comma.tsv @@ -1,16 +1,16 @@ -value #.0000,,, #.0000,, #.0000, #,##0.0 -0.99 .0000 .0000 .0010 1.0 -1.2345 .0000 .0000 .0012 1.2 -12.345 .0000 .0000 .0123 12.3 -123.456 .0000 .0001 .1235 123.5 -1234 .0000 .0012 1.2340 1,234.0 -12345 .0000 .0123 12.3450 12,345.0 -123456 .0001 .1235 123.4560 123,456.0 -1234567 .0012 1.2346 1234.5670 1,234,567.0 -12345678 .0123 12.3457 12345.6780 12,345,678.0 -123456789 .1235 123.4568 123456.7890 123,456,789.0 -1234567890 1.2346 1234.5679 1234567.8900 1,234,567,890.0 -12345678901 12.3457 12345.6789 12345678.9010 12,345,678,901.0 -123456789012 123.4568 123456.7890 123456789.0120 123,456,789,012.0 -4321 .0000 .0043 4.3210 4,321.0 -4321234 .0043 4.3212 4321.2340 4,321,234.0 +value #.0000,,, #.0000,, #.0000, #,##0.0 ###,##0 ###,### #,###.00 +0.99 .0000 .0000 .0010 1.0 1 1 .99 +1.2345 .0000 .0000 .0012 1.2 1 1 1.23 +12.345 .0000 .0000 .0123 12.3 12 12 12.35 +123.456 .0000 .0001 .1235 123.5 123 123 123.46 +1234 .0000 .0012 1.2340 1,234.0 1,234 1,234 1,234.00 +12345 .0000 .0123 12.3450 12,345.0 12,345 12,345 12,345.00 +123456 .0001 .1235 123.4560 123,456.0 123,456 123,456 123,456.00 +1234567 .0012 1.2346 1234.5670 1,234,567.0 1,234,567 1,234,567 1,234,567.00 +12345678 .0123 12.3457 12345.6780 12,345,678.0 12,345,678 12,345,678 12,345,678.00 +123456789 .1235 123.4568 123456.7890 123,456,789.0 123,456,789 123,456,789 123,456,789.00 +1234567890 1.2346 1234.5679 1234567.8900 1,234,567,890.0 1,234,567,890 1,234,567,890 1,234,567,890.00 +12345678901 12.3457 12345.6789 12345678.9010 12,345,678,901.0 12,345,678,901 12,345,678,901 12,345,678,901.00 +123456789012 123.4568 123456.7890 123456789.0120 123,456,789,012.0 123,456,789,012 123,456,789,012 123,456,789,012.00 +4321 .0000 .0043 4.3210 4,321.0 4,321 4,321 4,321.00 +4321234 .0043 4.3212 4321.2340 4,321,234.0 4,321,234 4,321,234 4,321,234.00 diff --git a/test/valid.js b/test/valid.js new file mode 100644 index 0000000..3afbb70 --- /dev/null +++ b/test/valid.js @@ -0,0 +1,19 @@ +/* vim: set ts=2: */ +/*jshint loopfunc:true, mocha:true, node:true */ +var SSF = require('../'); +var fs = require('fs'); +var data = fs.readFileSync('./test/valid.tsv','utf8').split("\n"); +var _data = [0, 1, -2, 3.45, -67.89, "foo"]; +function doit(d) { + it(d[0], function() { + for(var w = 0; w < _data.length; ++w) { + SSF.format(d[0], _data[w]); + } + }); +} +describe('valid formats', function() { + for(var j=0;j999999]#,,"M";[>999]#,"K";# +[>999999]#.000,,"M";[>999]#.000,"K";#.000 +[>=100000]0.000\ \";[Red]0.000\ \<\ \>\ \"\ \&\ \'\ +[>=100000]0.000\ \<;[Red]0.000\ \> +[BLACK]@ +[BLUE]GENERAL +[Black]@ +[Blue]General +[CYAN]@ +[Cyan]@ +[DBNum1][$-804]AM/PMh"时"mm"分";@ +[DBNum1][$-804]General +[DBNum1][$-804]h"时"mm"分";@ +[ENG][$-1004]dddd\,\ d\ mmmm\,\ yyyy;@ +[ENG][$-101040D]d\ mmmm\ yyyy;@ +[ENG][$-101042A]d\ mmmm\ yyyy;@ +[ENG][$-140C]dddd\ "YeahWoo!"\ ddd\ mmmm\ yyyy;@ +[ENG][$-2C0A]dddd\ d" de "mmmm" de "yyyy;@ +[ENG][$-402]dd\ mmmm\ yyyy\ "г.";@ +[ENG][$-403]dddd\,\ d" / "mmmm" / "yyyy;@ +[ENG][$-405]d\.\ mmmm\ yyyy;@ +[ENG][$-408]d\ mmmm\ yyyy;@ +[ENG][$-409]d\-mmm;@ +[ENG][$-409]d\-mmm\-yy;@ +[ENG][$-409]d\-mmm\-yyyy;@ +[ENG][$-409]dd\-mmm\-yy;@ +[ENG][$-409]mmm\-yy;@ +[ENG][$-409]mmmm\ d\,\ yyyy;@ +[ENG][$-409]mmmm\-yy;@ +[ENG][$-40B]d\.\ mmmm\t\a\ yyyy;@ +[ENG][$-40C]d/mmm/yyyy;@ +[ENG][$-40E]yyyy/\ mmmm\ d\.;@ +[ENG][$-40F]dd\.\ mmmm\ yyyy;@ +[ENG][$-410]d\ mmmm\ yyyy;@ +[ENG][$-415]d\ mmmm\ yyyy;@ +[ENG][$-416]d\ \ mmmm\,\ yyyy;@ +[ENG][$-418]d\ mmmm\ yyyy;@ +[ENG][$-41A]d\.\ mmmm\ yyyy\.;@ +[ENG][$-41B]d\.\ mmmm\ yyyy;@ +[ENG][$-41D]"den "\ d\ mmmm\ yyyy;@ +[ENG][$-420]dddd\,\ dd\ mmmm\,\ yyyy;@ +[ENG][$-421]dd\ mmmm\ yyyy;@ +[ENG][$-424]dddd\,\ d\.\ mmmm\ yyyy;@ +[ENG][$-425]dddd\,\ d\.\ mmmm\ yyyy;@ +[ENG][$-426]dddd\,\ yyyy". gada "d\.\ mmmm;@ +[ENG][$-427]yyyy\ "m."\ mmmm\ d\ "d.";@ +[ENG][$-42B]dddd\,\ d\ mmmm\ yyyy;@ +[ENG][$-42C]d\ mmmm\ yyyy;@ +[ENG][$-42D]yyyy"(e)ko"\ mmmm"ren"\ d"a";@ +[ENG][$-42F]dddd\,\ dd\ mmmm\ yyyy;@ +[ENG][$-437]yyyy\ \წ\ლ\ი\ს\ dd\ mm\,\ dddd;@ +[ENG][$-438]d\.\ mmmm\ yyyy;@ +[ENG][$-43F]d\ mmmm\ yyyy\ "ж.";@ +[ENG][$-444]d\ mmmm\ yyyy;@ +[ENG][$-449]dd\ mmmm\ yyyy;@ +[ENG][$-44E]d\ mmmm\ yyyy;@ +[ENG][$-44F]dd\ mmmm\ yyyy\ dddd;@ +[ENG][$-457]dd\ mmmm\ yyyy;@ +[ENG][$-813]dddd\ d\ mmmm\ yyyy;@ +[ENG][$-81A]dddd\,\ d\.\ mmmm\ yyyy;@ +[ENG][$-82C]d\ mmmm\ yyyy;@ +[ENG][$-843]yyyy\ "й""и""л"\ d/mmmm;@ +[ENG][$-C07]dddd\,\ dd\.\ mmmm\ yyyy;@ +[ENG][$-FC19]yyyy\,\ dd\ mmmm;@ +[ENG][$-FC22]d\ mmmm\ yyyy" р.";@ +[ENG][$-FC23]d\ mmmm\ yyyy;@ +[GREEN]#,### +[Green]#,### +[HH] +[HIJ][$-2060401]d/mm/yyyy\ h:mm\ AM/PM;@ +[HIJ][$-2060401]d\ mmmm\ yyyy;@ +[H] +[JPN][$-411]gggyy"年"m"月"d"日"\ dddd;@ +[MAGENTA]0.00 +[Magenta]0.00 +[RED]#.## +[Red]#.## +[Red][<-25]General;[Blue][>25]General;[Green]General;[Yellow]General\ +[Red][<=-25]General;[Blue][>=25]General;[Green]General;[Yellow]General +[Red][<>50]General;[Blue]000 +[Red][=50]General;[Blue]000 +[SS] +[S] +[TWN][DBNum1][$-404]y"年"m"月"d"日";@ +[WHITE]0.0 +[White]0.0 +[YELLOW]@ +[Yellow]@ +[h] +[h]:mm:ss +[h]:mm:ss;@ +[h]\.mm" Uhr ";@ +[hh] +[s] +[ss] +\#\r\e\c +\$#,##0_);[Red]"($"#,##0\) +\$0.00 +\C\O\B\ \o\n\ @ +\C\R\O\N\T\A\B\ \o\n\ @ +\R\e\s\u\l\t\ \o\n\ @ +\S\Q\L\ \:\ @ +\S\Q\L\ \R\e\q\u\e\s\t\ \f\o\r\ @ +\c\c\c?????0"aaaa"0"bbbb"000000.00% +\u\n\t\i\l\ h:mm;@ +_ "¥"* #,##0.00_ "Positive";_ "¥"* \-#,##0.00_ ;_ "¥"* "-"??_ "Negtive";_ @_ \ "Zero" +_ * #,##0.00_)[$﷼-429]_ ;_ * \(#,##0.00\)[$﷼-429]_ ;_ * "-"??_)[$﷼-429]_ ;_ @_ +_ * #,##0_ ;_ * \-#,##0_ ;[Red]_ * "-"_ ;_ @_ +_("$"* #,##0.00_);_("$"* \(#,##0.00\);_("$"* "-"??_);_(@_) +_("$"* #,##0_);_("$"* \(#,##0\);_("$"* "-"??_);_(@_) +_("$"* #,##0_);_("$"* \(#,##0\);_("$"* "-"_);_(@_) +_(* #,##0.0000_);_(* \(#,##0.0000\);_(* "-"??_);_(@_) +_(* #,##0.000_);_(* \(#,##0.000\);_(* "-"??_);_(@_) +_(* #,##0.00_);_(* \(#,##0.00\);_(* "-"??_);_(@_) +_(* #,##0.0_);_(* \(#,##0.0\);_(* "-"??_);_(@_) +_(* #,##0_);_(* \(#,##0\);_(* "-"??_);_(@_) +_(* #,##0_);_(* \(#,##0\);_(* "-"_);_(@_) +_([$ANG]\ * #,##0.0_);_([$ANG]\ * \(#,##0.0\);_([$ANG]\ * "-"?_);_(@_) +_-"€"\ * #,##0.00_-;_-"€"\ * #,##0.00\-;_-"€"\ * "-"??_-;_-@_- +_-* #,##0.00" TL"_-;\-* #,##0.00" TL"_-;_-* \-??" TL"_-;_-@_- +_-* #,##0.00" €"_-;\-* #,##0.00" €"_-;_-* \-??" €"_-;_-@_- +_-* #,##0.00\ "р."_-;\-* #,##0.00\ "р."_-;_-* "-"??\ "р."_-;_-@_- +_-* #,##0.00\ "€"_-;\-* #,##0.00\ "€"_-;_-* "-"??\ "€"_-;_-@_- +_-* #,##0.00\ [$€-407]_-;\-* #,##0.00\ [$€-407]_-;_-* \-??\ [$€-407]_-;_-@_- +_-* #,##0.0\ _F_-;\-* #,##0.0\ _F_-;_-* "-"??\ _F_-;_-@_- +_-* #,##0\ "€"_-;\-* #,##0\ "€"_-;_-* "-"\ "€"_-;_-@_- +_-* #,##0_-;\-* #,##0_-;_-* "-"??_-;_-@_- +_-\$* #,##0.0_ ;_-\$* \-#,##0.0\ ;_-\$* "-"?_ ;_-@_ +d +d-mmm +d-mmm-yy +d/m +d/m/yy;@ +d/m/yyyy;@ +d/mm/yy;@ +d/mm/yyyy;@ +d\-mmm +d\-mmm\-yyyy +dd +dd"-"mmm"-"yyyy +dd/m/yyyy +dd/mm/yy +dd/mm/yy;@ +dd/mm/yy\ hh:mm +dd/mm/yyyy +dd/mm/yyyy\ hh:mm:ss +dd/mmm +dd\-mm\-yy +dd\-mmm\-yy +dd\-mmm\-yyyy\ hh:mm:ss.000 +dd\/mm\/yy +dd\/mm\/yyyy +ddd +dddd +dddd, mmmm dd, yyyy +h +h"时"mm"分"ss"秒";@ +h"時"mm"分"ss"秒";@ +h:mm +h:mm AM/PM +h:mm:ss +h:mm:ss AM/PM +h:mm:ss;@ +h:mm;@ +h\.mm" Uhr ";@ +h\.mm" h";@ +h\.mm" u.";@ +hh":"mm AM/PM +hh:mm:ss +hh:mm:ss\ AM/PM +hh\.mm" h";@ +hhm +hhmm +m"月"d"日" +m/d/yy +m/d/yy h:mm +m/d/yy;@ +m/d/yy\ h:mm +m/d/yy\ h:mm;@ +m/d/yyyy +m/d/yyyy;@ +m/d/yyyy\ h:mm:ss;@ +m/d;@ +m\/d\/yyyy +mm/dd +mm/dd/yy +mm/dd/yy;@ +mm/dd/yyyy +mm:ss +mm:ss.0;@ +mmm d, yyyy +mmm" "d", "yyyy +mmm-yy +mmm-yy;@ +mmm/yy +mmm\-yy +mmm\-yy;@ +mmm\-yyyy +mmmm\ d\,\ yyyy +mmmm\ yyyy +mmss.0 +s +ss +yy +yy/mm/dd +yy\.mm\.dd +yym +yymm +yymmm +yymmmm +yymmmmm +yyyy +yyyy"년"\ m"월"\ d"일";@ +yyyy-m-d h:mm AM/PM +yyyy-mm-dd +yyyy/mm/dd +yyyy\-m\-d\ hh:mm:ss +yyyy\-mm\-dd +yyyy\-mm\-dd;@ +yyyy\-mm\-dd\ h:mm +yyyy\-mm\-dd\Thh:mm +yyyy\-mm\-dd\Thhmmss.000