Date/Time Minutiae
- TZ consistency for DBF and others (closes #663 h/t @peeyushsrivastava) - Date1904 XLSX/XLSB/XLS/XLML consistency (fixes #175 h/t @SheetJSDev) - dateNF corrects for plaintext date parsing (fixes #658 h/t @mmancosu) - new travis tests override local time zones
This commit is contained in:
parent
118e9ad9cb
commit
aff7b95272
17
.travis.yml
17
.travis.yml
@ -1,5 +1,6 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- "8"
|
||||
- "7"
|
||||
- "6"
|
||||
# note: travis has been acting up on old versions of node
|
||||
@ -9,6 +10,22 @@ node_js:
|
||||
# - "0.10"
|
||||
# - "0.9"
|
||||
# - "0.8"
|
||||
matrix:
|
||||
include:
|
||||
- node_js: "6"
|
||||
env: TZ="America/New_York"
|
||||
- node_js: "8"
|
||||
env: TZ="America/Los_Angeles"
|
||||
- node_js: "6"
|
||||
env: TZ="Europe/London"
|
||||
- node_js: "8"
|
||||
env: TZ="Europe/Berlin"
|
||||
- node_js: "6"
|
||||
env: TZ="Asia/Kolkata"
|
||||
- node_js: "7"
|
||||
env: TZ="Asia/Shanghai"
|
||||
- node_js: "8"
|
||||
env: TZ="Asia/Seoul"
|
||||
before_install:
|
||||
- "npm install -g npm@4.3.0"
|
||||
- "npm install -g mocha@2.x voc"
|
||||
|
48
README.md
48
README.md
@ -82,6 +82,7 @@ enhancements, additional features by request, and dedicated support.
|
||||
+ [Workbook File Properties](#workbook-file-properties)
|
||||
* [Workbook-Level Attributes](#workbook-level-attributes)
|
||||
+ [Defined Names](#defined-names)
|
||||
+ [Miscellaneous Workbook Properties](#miscellaneous-workbook-properties)
|
||||
* [Document Features](#document-features)
|
||||
+ [Formulae](#formulae)
|
||||
+ [Column Properties](#column-properties)
|
||||
@ -719,6 +720,9 @@ will not be generated; the parser `sheetStubs` option must be set to `true`.
|
||||
|
||||
#### Dates
|
||||
|
||||
<details>
|
||||
<summary><b>Excel Date Code details</b> (click to show)</summary>
|
||||
|
||||
By default, Excel stores dates as numbers with a format code that specifies date
|
||||
processing. For example, the date `19-Feb-17` is stored as the number `42785`
|
||||
with a number format of `d-mmm-yy`. The `SSF` module understands number formats
|
||||
@ -730,6 +734,32 @@ string. The formatter converts the date back to a number.
|
||||
The default behavior for all parsers is to generate number cells. Setting
|
||||
`cellDates` to true will force the generators to store dates.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Time Zones and Dates</b> (click to show)</summary>
|
||||
|
||||
Excel has no native concept of universal time. All times are specified in the
|
||||
local time zone. Excel limitations prevent specifying true absolute dates.
|
||||
|
||||
Following Excel, this library treats all dates as relative to local time zone.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Epochs: 1900 and 1904</b> (click to show)</summary>
|
||||
|
||||
Excel supports two epochs (January 1 1900 and January 1 1904), see
|
||||
["1900 vs. 1904 Date System" article](http://support2.microsoft.com/kb/180162).
|
||||
The workbook's epoch can be determined by examining the workbook's
|
||||
`wb.Workbook.WBProps.date1904` property:
|
||||
|
||||
```js
|
||||
!!(((wb.Workbook||{}).WBProps||{}).date1904)
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### Sheet Objects
|
||||
|
||||
Each key that does not start with `!` maps to a cell (using `A-1` notation)
|
||||
@ -850,12 +880,7 @@ first row of the chartsheet is the underlying header.
|
||||
custom properties. Since the XLS standard properties deviate from the XLSX
|
||||
standard, XLS parsing stores core properties in both places.
|
||||
|
||||
`wb.WBProps` includes more workbook-level properties:
|
||||
|
||||
- Excel supports two epochs (January 1 1900 and January 1 1904), see
|
||||
[1900 vs. 1904 Date System](http://support2.microsoft.com/kb/180162).
|
||||
The workbook's epoch can be determined by examining the workbook's
|
||||
`wb.WBProps.date1904` property.
|
||||
`wb.Workbook` stores [workbook-level attributes](#workbook-level-attributes).
|
||||
|
||||
#### Workbook File Properties
|
||||
|
||||
@ -902,7 +927,7 @@ XLSX.write(wb, {Props:{Author:"SheetJS"}});
|
||||
|
||||
### Workbook-Level Attributes
|
||||
|
||||
`wb.Workbook` stores workbook level attributes.
|
||||
`wb.Workbook` stores workbook-level attributes.
|
||||
|
||||
#### Defined Names
|
||||
|
||||
@ -917,12 +942,21 @@ XLSX.write(wb, {Props:{Author:"SheetJS"}});
|
||||
| `Name` | Case-sensitive name. Standard rules apply ** |
|
||||
| `Ref` | A1-style Reference (e.g. `"Sheet1!$A$1:$D$20"`) |
|
||||
| `Comment` | Comment (only applicable for XLS/XLSX/XLSB) |
|
||||
|
||||
</details>
|
||||
|
||||
Excel allows two sheet-scoped defined names to share the same name. However, a
|
||||
sheet-scoped name cannot collide with a workbook-scope name. Workbook writers
|
||||
may not enforce this constraint.
|
||||
|
||||
#### Miscellaneous Workbook Properties
|
||||
|
||||
`wb.Workbook.WBProps` holds other workbook properties:
|
||||
|
||||
| Key | Description |
|
||||
|:-----------|:----------------------------------------------------|
|
||||
| `date1904` | epoch: 0/false for 1900 system, 1/true for 1904 |
|
||||
|
||||
### Document Features
|
||||
|
||||
Even for basic features like date storage, the official Excel formats store the
|
||||
|
@ -18,3 +18,30 @@ var XLMLFormatMap/*{[string]:string}*/ = ({
|
||||
"On/Off": '"Yes";"Yes";"No";@'
|
||||
}/*:any*/);
|
||||
|
||||
/* dateNF parse TODO: move to SSF */
|
||||
var dateNFregex = /[dD]+|[mM]+|[yYeE]+|[Hh]+|[Ss]+/g;
|
||||
function dateNF_regex(dateNF/*:string|number*/)/*:RegExp*/ {
|
||||
var fmt = typeof dateNF == "number" ? SSF._table[dateNF] : dateNF;
|
||||
fmt = fmt.replace(dateNFregex, "(\\d+)");
|
||||
return new RegExp("^" + fmt + "$");
|
||||
}
|
||||
function dateNF_fix(str/*:string*/, dateNF/*:string*/, match/*:Array<string>*/)/*:string*/ {
|
||||
var Y = -1, m = -1, d = -1, H = -1, M = -1, S = -1;
|
||||
(dateNF.match(dateNFregex)||[]).forEach(function(n, i) {
|
||||
var v = parseInt(match[i+1], 10);
|
||||
switch(n.toLowerCase().charAt(0)) {
|
||||
case 'y': Y = v; break; case 'd': d = v; break;
|
||||
case 'h': H = v; break; case 's': S = v; break;
|
||||
case 'm': if(H >= 0) M = v; else m = v; break;
|
||||
}
|
||||
});
|
||||
if(S >= 0 && M == -1 && m >= 0) { M = m; m = -1; }
|
||||
var datestr = (("" + (Y>=0?Y: new Date().getFullYear())).slice(-4) + "-" + ("00" + (m>=1?m:1)).slice(-2) + "-" + ("00" + (d>=1?d:1)).slice(-2));
|
||||
if(datestr.length == 7) datestr = "0" + datestr;
|
||||
if(datestr.length == 8) datestr = "20" + datestr;
|
||||
var timestr = (("00" + (H>=0?H:0)).slice(-2) + ":" + ("00" + (M>=0?M:0)).slice(-2) + ":" + ("00" + (S>=0?S:0)).slice(-2));
|
||||
if(H == -1 && M == -1 && S == -1) return datestr;
|
||||
if(Y == -1 && m == -1 && d == -1) return timestr;
|
||||
return datestr + "T" + timestr;
|
||||
}
|
||||
|
||||
|
@ -29,22 +29,17 @@ function evert_arr(obj/*:any*/)/*:EvertArrType*/ {
|
||||
return o;
|
||||
}
|
||||
|
||||
var basedate = new Date(1899, 11, 30, 0, 0, 0); // 2209161600000
|
||||
var dnthresh = basedate.getTime() + (new Date().getTimezoneOffset() - basedate.getTimezoneOffset()) * 60000;
|
||||
function datenum(v/*:Date*/, date1904/*:?boolean*/)/*:number*/ {
|
||||
var epoch = v.getTime();
|
||||
if(date1904) epoch += 1462*24*60*60*1000;
|
||||
return (epoch + 2209161600000) / (24 * 60 * 60 * 1000);
|
||||
return (epoch - dnthresh) / (24 * 60 * 60 * 1000);
|
||||
}
|
||||
function numdate(v/*:number*/)/*:Date*/ {
|
||||
var date = SSF.parse_date_code(v);
|
||||
var val = new Date();
|
||||
if(date == null) throw new Error("Bad Date Code: " + v);
|
||||
val.setUTCDate(date.d);
|
||||
val.setUTCMonth(date.m-1);
|
||||
val.setUTCFullYear(date.y);
|
||||
val.setUTCHours(date.H);
|
||||
val.setUTCMinutes(date.M);
|
||||
val.setUTCSeconds(date.S);
|
||||
return val;
|
||||
var out = new Date();
|
||||
out.setTime(v * 24 * 60 * 60 * 1000 + dnthresh);
|
||||
return out;
|
||||
}
|
||||
|
||||
/* ISO 8601 Duration */
|
||||
@ -77,9 +72,15 @@ function parse_isodur(s) {
|
||||
var good_pd_date = new Date('2017-02-19T19:06:09.000Z');
|
||||
if(isNaN(good_pd_date.getFullYear())) good_pd_date = new Date('2/19/17');
|
||||
var good_pd = good_pd_date.getFullYear() == 2017;
|
||||
function parseDate(str/*:string|Date*/)/*:Date*/ {
|
||||
/* parses aa date as a local date */
|
||||
function parseDate(str/*:string|Date*/, fixdate/*:?number*/)/*:Date*/ {
|
||||
var d = new Date(str);
|
||||
if(good_pd) return d;
|
||||
if(good_pd) {
|
||||
/*:: if(fixdate == null) fixdate = 0; */
|
||||
if(fixdate > 0) d.setTime(d.getTime() + d.getTimezoneOffset() * 60 * 1000);
|
||||
else if(fixdate < 0) d.setTime(d.getTime() - d.getTimezoneOffset() * 60 * 1000);
|
||||
return d;
|
||||
}
|
||||
if(str instanceof Date) return str;
|
||||
if(good_pd_date.getFullYear() == 1917 && !isNaN(d.getFullYear())) {
|
||||
var s = d.getFullYear();
|
||||
@ -87,7 +88,7 @@ function parseDate(str/*:string|Date*/)/*:Date*/ {
|
||||
d.setFullYear(d.getFullYear() + 100); return d;
|
||||
}
|
||||
var n = str.match(/\d+/g)||["2017","2","19","0","0","0"];
|
||||
return new Date(Date.UTC(+n[0], +n[1] - 1, +n[2], (+n[3]||0), (+n[4]||0), (+n[5]||0)));
|
||||
return new Date(+n[0], +n[1] - 1, +n[2], (+n[3]||0), (+n[4]||0), (+n[5]||0));
|
||||
}
|
||||
|
||||
function cc2str(arr/*:Array<number>*/)/*:string*/ {
|
||||
|
@ -74,7 +74,7 @@ var xlml_unfixstr/*:StringConv*/ = (function() {
|
||||
|
||||
function parsexmlbool(value/*:any*/, tag/*:?string*/)/*:boolean*/ {
|
||||
switch(value) {
|
||||
case '1': case 'true': case 'TRUE': return true;
|
||||
case 1: case true: case '1': case 'true': case 'TRUE': return true;
|
||||
/* case '0': case 'false': case 'FALSE':*/
|
||||
default: return false;
|
||||
}
|
||||
|
@ -523,6 +523,7 @@ var PRN = (function() {
|
||||
var R = 0, C = 0, v = 0;
|
||||
var start = 0, end = 0, sepcc = sep.charCodeAt(0), instr = false, cc=0;
|
||||
str = str.replace(/\r\n/mg, "\n");
|
||||
var _re/*:?RegExp*/ = o.dateNF != null ? dateNF_regex(o.dateNF) : null;
|
||||
function finish_cell() {
|
||||
var s = str.slice(start, end);
|
||||
var cell = ({}/*:any*/);
|
||||
@ -530,10 +531,12 @@ var PRN = (function() {
|
||||
else if(s == "TRUE") { cell.t = 'b'; cell.v = true; }
|
||||
else if(s == "FALSE") { cell.t = 'b'; cell.v = false; }
|
||||
else if(!isNaN(v = +s)) { cell.t = 'n'; cell.w = s; cell.v = v; }
|
||||
else if(!isNaN(fuzzydate(s).getDate())) {
|
||||
else if(!isNaN(fuzzydate(s).getDate()) || _re && s.match(_re)) {
|
||||
cell.z = o.dateNF || SSF._table[14];
|
||||
if(o.cellDates) { cell.t = 'd'; cell.v = parseDate(s); }
|
||||
else { cell.t = 'n'; cell.v = datenum(parseDate(s)); }
|
||||
var k = 0;
|
||||
if(_re && s.match(_re)){ s=dateNF_fix(s, o.dateNF, (s.match(_re)||[])); k=1; }
|
||||
if(o.cellDates) { cell.t = 'd'; cell.v = parseDate(s, k); }
|
||||
else { cell.t = 'n'; cell.v = datenum(parseDate(s, k)); }
|
||||
cell.w = SSF.format(cell.z, cell.v instanceof Date ? datenum(cell.v):cell.v);
|
||||
} else {
|
||||
cell.t = 's';
|
||||
|
@ -194,7 +194,7 @@ function write_ws_xml_cell(cell, ref, ws, opts, idx, wb) {
|
||||
case 'n': vv = ''+cell.v; break;
|
||||
case 'e': vv = BErr[cell.v]; break;
|
||||
case 'd':
|
||||
if(opts.cellDates) vv = parseDate(cell.v).toISOString();
|
||||
if(opts.cellDates) vv = parseDate(cell.v, -1).toISOString();
|
||||
else {
|
||||
cell.t = 'n';
|
||||
vv = ''+(cell.v = datenum(parseDate(cell.v)));
|
||||
@ -347,7 +347,8 @@ return function parse_ws_xml_data(sdata, s, opts, guess, themes, styles) {
|
||||
break;
|
||||
case 'b': p.v = parsexmlbool(p.v); break;
|
||||
case 'd':
|
||||
if(!opts.cellDates) { p.v = datenum(parseDate(p.v)); p.t = 'n'; }
|
||||
if(opts.cellDates) p.v = parseDate(p.v, 1);
|
||||
else { p.v = datenum(parseDate(p.v, 1)); p.t = 'n'; }
|
||||
break;
|
||||
/* error string in .w, number in .v */
|
||||
case 'e':
|
||||
@ -364,9 +365,7 @@ return function parse_ws_xml_data(sdata, s, opts, guess, themes, styles) {
|
||||
}
|
||||
}
|
||||
safe_format(p, fmtid, fillid, opts, themes, styles);
|
||||
if(opts.cellDates && do_format && p.t == 'n' && SSF.is_date(SSF._table[fmtid])) {
|
||||
var _d = SSF.parse_date_code(p.v); if(_d) { p.t = 'd'; p.v = new Date(Date.UTC(_d.y, _d.m-1,_d.d,_d.H,_d.M,_d.S,_d.u)); }
|
||||
}
|
||||
if(opts.cellDates && do_format && p.t == 'n' && SSF.is_date(SSF._table[fmtid])) { p.t = 'd'; p.v = numdate(p.v); }
|
||||
if(dense) {
|
||||
var _r = decode_cell(tag.r);
|
||||
if(!s[_r.r]) s[_r.r] = [];
|
||||
|
@ -470,7 +470,7 @@ function parse_ws_bin(data, _opts, rels, wb, themes, styles)/*:Worksheet*/ {
|
||||
if(refguess.e.r < row.r) refguess.e.r = row.r;
|
||||
if(refguess.e.c < C) refguess.e.c = C;
|
||||
if(opts.cellDates && cf && p.t == 'n' && SSF.is_date(SSF._table[cf.ifmt])) {
|
||||
var _d = SSF.parse_date_code(p.v); if(_d) { p.t = 'd'; p.v = new Date(Date.UTC(_d.y, _d.m-1,_d.d,_d.H,_d.M,_d.S,_d.u)); }
|
||||
var _d = SSF.parse_date_code(p.v); if(_d) { p.t = 'd'; p.v = new Date(_d.y, _d.m-1,_d.d,_d.H,_d.M,_d.S,_d.u); }
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1,37 +1,36 @@
|
||||
/* 18.2.28 (CT_WorkbookProtection) Defaults */
|
||||
var WBPropsDef = [
|
||||
['allowRefreshQuery', '0'],
|
||||
['autoCompressPictures', '1'],
|
||||
['backupFile', '0'],
|
||||
['checkCompatibility', '0'],
|
||||
['codeName', ''],
|
||||
['date1904', '0'],
|
||||
['dateCompatibility', '1'],
|
||||
//['defaultThemeVersion', '0'],
|
||||
['filterPrivacy', '0'],
|
||||
['hidePivotFieldList', '0'],
|
||||
['promptedSolutions', '0'],
|
||||
['publishItems', '0'],
|
||||
['refreshAllConnections', false],
|
||||
['saveExternalLinkValues', '1'],
|
||||
['showBorderUnselectedTables', '1'],
|
||||
['showInkAnnotation', '1'],
|
||||
['showObjects', 'all'],
|
||||
['showPivotChartFilter', '0']
|
||||
//['updateLinks', 'userSet']
|
||||
['allowRefreshQuery', false, "bool"],
|
||||
['autoCompressPictures', true, "bool"],
|
||||
['backupFile', false, "bool"],
|
||||
['checkCompatibility', false, "bool"],
|
||||
['codeName', ''],
|
||||
['date1904', false, "bool"],
|
||||
['defaultThemeVersion', 0, "int"],
|
||||
['filterPrivacy', false, "bool"],
|
||||
['hidePivotFieldList', false, "bool"],
|
||||
['promptedSolutions', false, "bool"],
|
||||
['publishItems', false, "bool"],
|
||||
['refreshAllConnections', false, "bool"],
|
||||
['saveExternalLinkValues', true, "bool"],
|
||||
['showBorderUnselectedTables', true, "bool"],
|
||||
['showInkAnnotation', true, "bool"],
|
||||
['showObjects', 'all'],
|
||||
['showPivotChartFilter', false, "bool"],
|
||||
['updateLinks', 'userSet']
|
||||
];
|
||||
|
||||
/* 18.2.30 (CT_BookView) Defaults */
|
||||
var WBViewDef = [
|
||||
['activeTab', '0'],
|
||||
['autoFilterDateGrouping', '1'],
|
||||
['firstSheet', '0'],
|
||||
['minimized', '0'],
|
||||
['showHorizontalScroll', '1'],
|
||||
['showSheetTabs', '1'],
|
||||
['showVerticalScroll', '1'],
|
||||
['tabRatio', '600'],
|
||||
['visibility', 'visible']
|
||||
['activeTab', 0, "int"],
|
||||
['autoFilterDateGrouping', true, "bool"],
|
||||
['firstSheet', 0, "int"],
|
||||
['minimized', false, "bool"],
|
||||
['showHorizontalScroll', true, "bool"],
|
||||
['showSheetTabs', true, "bool"],
|
||||
['showVerticalScroll', true, "bool"],
|
||||
['tabRatio', 600, "int"],
|
||||
['visibility', 'visible']
|
||||
//window{Height,Width}, {x,y}Window
|
||||
];
|
||||
|
||||
@ -80,12 +79,20 @@ function push_defaults_array(target, defaults) {
|
||||
for(var j = 0; j != target.length; ++j) { var w = target[j];
|
||||
for(var i=0; i != defaults.length; ++i) { var z = defaults[i];
|
||||
if(w[z[0]] == null) w[z[0]] = z[1];
|
||||
else switch(z[2]) {
|
||||
case "bool": if(typeof w[z[0]] == "string") w[z[0]] = parsexmlbool(w[z[0]], z[0]); break;
|
||||
case "int": if(typeof w[z[0]] == "string") w[z[0]] = parseInt(w[z[0]], 10); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function push_defaults(target, defaults) {
|
||||
for(var i = 0; i != defaults.length; ++i) { var z = defaults[i];
|
||||
if(target[z[0]] == null) target[z[0]] = z[1];
|
||||
else switch(z[2]) {
|
||||
case "bool": if(typeof target[z[0]] == "string") target[z[0]] = parsexmlbool(target[z[0]], z[0]); break;
|
||||
case "int": if(typeof target[z[0]] == "string") target[z[0]] = parseInt(target[z[0]], 10); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -99,6 +106,13 @@ function parse_wb_defaults(wb) {
|
||||
_ssfopts.date1904 = parsexmlbool(wb.WBProps.date1904, 'date1904');
|
||||
}
|
||||
|
||||
function safe1904(wb/*:Workbook*/)/*:string*/ {
|
||||
/* TODO: store date1904 somewhere else */
|
||||
if(!wb.Workbook) return "false";
|
||||
if(!wb.Workbook.WBProps) return "false";
|
||||
return parsexmlbool(wb.Workbook.WBProps.date1904) ? "true" : "false";
|
||||
}
|
||||
|
||||
var badchars = "][*?\/\\".split("");
|
||||
function check_ws_name(n/*:string*/, safe/*:?boolean*/)/*:boolean*/ {
|
||||
if(n.length > 31) { if(safe) return false; throw new Error("Sheet names cannot exceed 31 chars"); }
|
||||
|
@ -26,8 +26,17 @@ function parse_wb_xml(data, opts)/*:WorkbookFile*/ {
|
||||
case '<fileSharing': case '<fileSharing/>': break;
|
||||
|
||||
/* 18.2.28 workbookPr CT_WorkbookPr ? */
|
||||
case '<workbookPr': delete y[0]; wb.WBProps = y; break;
|
||||
case '<workbookPr/>': delete y[0]; wb.WBProps = y; break;
|
||||
case '<workbookPr':
|
||||
case '<workbookPr/>':
|
||||
WBPropsDef.forEach(function(w) {
|
||||
if(y[w[0]] == null) return;
|
||||
switch(w[2]) {
|
||||
case "bool": wb.WBProps[w[0]] = parsexmlbool(y[w[0]], w[0]); break;
|
||||
case "int": wb.WBProps[w[0]] = parseInt(y[w[0]], 10); break;
|
||||
default: wb.WBProps[w[0]] = y[w[0]];
|
||||
}
|
||||
});
|
||||
break;
|
||||
case '</workbookPr>': break;
|
||||
|
||||
/* 18.2.29 workbookProtection CT_WorkbookProtection ? */
|
||||
@ -148,14 +157,6 @@ var WB_XML_ROOT = writextag('workbook', null, {
|
||||
'xmlns:r': XMLNS.r
|
||||
});
|
||||
|
||||
function safe1904(wb/*:Workbook*/)/*:string*/ {
|
||||
/* TODO: store date1904 somewhere else */
|
||||
if(!wb.Workbook) return "false";
|
||||
if(!wb.Workbook.WBProps) return "false";
|
||||
// $FlowIgnore
|
||||
return parsexmlbool(wb.Workbook.WBProps.date1904) ? "true" : "false";
|
||||
}
|
||||
|
||||
function write_wb_xml(wb/*:Workbook*/, opts/*:?WriteOpts*/)/*:string*/ {
|
||||
var o = [XML_HEADER];
|
||||
o[o.length] = WB_XML_ROOT;
|
||||
|
@ -18,10 +18,27 @@ function write_BrtBundleSh(data, o) {
|
||||
|
||||
/* [MS-XLSB] 2.4.807 BrtWbProp */
|
||||
function parse_BrtWbProp(data, length) {
|
||||
data.read_shift(4);
|
||||
var dwThemeVersion = data.read_shift(4);
|
||||
var o = {};
|
||||
var flags = data.read_shift(4);
|
||||
o.defaultThemeVersion = data.read_shift(4);
|
||||
var strName = (length > 8) ? parse_XLWideString(data) : "";
|
||||
return [dwThemeVersion, strName];
|
||||
if(strName.length > 0) o.codeName = strName;
|
||||
o.autoCompressPictures = !!(flags & 0x10000);
|
||||
o.backupFile = !!(flags & 0x40);
|
||||
o.checkCompatibility = !!(flags & 0x1000);
|
||||
o.date1904 = !!(flags & 0x01);
|
||||
o.filterPrivacy = !!(flags & 0x08);
|
||||
o.hidePivotFieldList = !!(flags & 0x400);
|
||||
o.promptedSolutions = !!(flags & 0x10);
|
||||
o.publishItems = !!(flags & 0x800);
|
||||
o.refreshAllConnections = !!(flags & 0x40000);
|
||||
o.saveExternalLinkValues = !!(flags & 0x80);
|
||||
o.showBorderUnselectedTables = !!(flags & 0x04);
|
||||
o.showInkAnnotation = !!(flags & 0x20);
|
||||
o.showObjects = ["all", "placeholders", "none"][(flags >> 13) & 0x03];
|
||||
o.showPivotChartFilter = !!(flags & 0x8000);
|
||||
o.updateLinks = ["userSet", "never", "always"][(flags >> 8) & 0x03];
|
||||
return o;
|
||||
}
|
||||
function write_BrtWbProp(data, o) {
|
||||
if(!o) o = new_buf(72);
|
||||
@ -85,6 +102,9 @@ function parse_wb_bin(data, opts)/*:WorkbookFile*/ {
|
||||
break;
|
||||
case 0x040C: /* 'BrtNameExt' */ break;
|
||||
|
||||
case 0x0099: /* 'BrtWbProp' */
|
||||
wb.WBProps = val; break;
|
||||
|
||||
/* case 'BrtModelTimeGroupingCalcCol' */
|
||||
/* case 'BrtRevisionPtr' */
|
||||
/* case 'BrtUid' */
|
||||
@ -116,7 +136,6 @@ function parse_wb_bin(data, opts)/*:WorkbookFile*/ {
|
||||
case 0x0822: /* 'BrtTimelineCachePivotCacheID' */
|
||||
case 0x018D: /* 'BrtUserBookView' */
|
||||
case 0x009A: /* 'BrtWbFactoid' */
|
||||
case 0x0099: /* 'BrtWbProp' */
|
||||
case 0x045D: /* 'BrtWbProp14' */
|
||||
case 0x0229: /* 'BrtWebOpt' */
|
||||
case 0x082B: /* 'BrtWorkBookPr15' */
|
||||
|
@ -74,7 +74,7 @@ function safe_format_xlml(cell/*:Cell*/, nf, o) {
|
||||
var z = XLMLFormatMap[nf]||nf||"General";
|
||||
if(o.cellNF) cell.z = z;
|
||||
if(o.cellDates && cell.t == 'n' && SSF.is_date(z)) {
|
||||
var _d = SSF.parse_date_code(cell.v); if(_d) { cell.t = 'd'; cell.v = new Date(Date.UTC(_d.y, _d.m-1,_d.d,_d.H,_d.M,_d.S,_d.u)); }
|
||||
var _d = SSF.parse_date_code(cell.v); if(_d) { cell.t = 'd'; cell.v = new Date(_d.y, _d.m-1,_d.d,_d.H,_d.M,_d.S,_d.u); }
|
||||
}
|
||||
} catch(e) { if(o.WTF) throw e; }
|
||||
}
|
||||
@ -191,7 +191,7 @@ function parse_xlml_xml(d, opts)/*:Workbook*/ {
|
||||
var cstys = [], csty, seencol = false;
|
||||
var arrayf = [];
|
||||
var rowinfo = [], rowobj = {};
|
||||
var Workbook/*:WBWBProps*/ = { Sheets:[] }, wsprops = {};
|
||||
var Workbook/*:WBWBProps*/ = ({ Sheets:[], WBProps:{date1904:false} }/*:any*/), wsprops = {};
|
||||
xlmlregex.lastIndex = 0;
|
||||
str = str.replace(/<!--([^\u2603]*?)-->/mg,"");
|
||||
while((Rn = xlmlregex.exec(str))) switch(Rn[3]) {
|
||||
@ -472,6 +472,10 @@ function parse_xlml_xml(d, opts)/*:Workbook*/ {
|
||||
|
||||
/* ExcelWorkbook */
|
||||
case 'ExcelWorkbook': switch(Rn[3]) {
|
||||
case 'Date1904':
|
||||
/*:: if(!Workbook.WBProps) Workbook.WBProps = {}; */
|
||||
Workbook.WBProps.date1904 = true;
|
||||
break;
|
||||
case 'WindowHeight': break;
|
||||
case 'WindowWidth': break;
|
||||
case 'WindowTopX': break;
|
||||
@ -490,7 +494,6 @@ function parse_xlml_xml(d, opts)/*:Workbook*/ {
|
||||
case 'Dll': break;
|
||||
case 'AcceptLabelsInFormulas': break;
|
||||
case 'DoNotSaveLinkValues': break;
|
||||
case 'Date1904': break;
|
||||
case 'Iteration': break;
|
||||
case 'MaxIterations': break;
|
||||
case 'MaxChange': break;
|
||||
|
@ -71,7 +71,7 @@ function safe_format_xf(p/*:any*/, opts/*:ParseOpts*/, date1904/*:?boolean*/) {
|
||||
}
|
||||
else p.w = SSF.format(fmtid,p.v, {date1904:!!date1904});
|
||||
if(opts.cellDates && fmtid && p.t == 'n' && SSF.is_date(SSF._table[fmtid])) {
|
||||
var _d = SSF.parse_date_code(p.v); if(_d) { p.t = 'd'; p.v = new Date(Date.UTC(_d.y, _d.m-1,_d.d,_d.H,_d.M,_d.S,_d.u)); }
|
||||
var _d = SSF.parse_date_code(p.v); if(_d) { p.t = 'd'; p.v = new Date(_d.y, _d.m-1,_d.d,_d.H,_d.M,_d.S,_d.u); }
|
||||
}
|
||||
} catch(e) { if(opts.WTF) throw e; }
|
||||
}
|
||||
@ -101,7 +101,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
var cell_valid = true;
|
||||
var XFs = []; /* XF records */
|
||||
var palette = [];
|
||||
var Workbook/*:WBWBProps*/ = ({ Sheets:[] }/*:any*/), wsprops = {};
|
||||
var Workbook/*:WBWBProps*/ = ({ Sheets:[], WBProps:{date1904:false} }/*:any*/), wsprops = {};
|
||||
var get_rgb = function getrgb(icv) {
|
||||
if(icv < 8) return XLSIcv[icv];
|
||||
if(icv < 64) return palette[icv-8] || XLSIcv[icv];
|
||||
@ -213,7 +213,9 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
/* nested switch statements to workaround V8 128 limit */
|
||||
switch(Rn) {
|
||||
/* Workbook Options */
|
||||
case 'Date1904': wb.opts.Date1904 = val; break;
|
||||
case 'Date1904':
|
||||
/*:: if(!Workbook.WBProps) Workbook.WBProps = {}; */
|
||||
wb.opts.Date1904 = Workbook.WBProps.date1904 = val; break;
|
||||
case 'WriteProtect': wb.opts.WriteProtect = true; break;
|
||||
case 'FilePass':
|
||||
if(!opts.enc) blob.l = 0;
|
||||
|
@ -44,6 +44,9 @@ will not be generated; the parser `sheetStubs` option must be set to `true`.
|
||||
|
||||
#### Dates
|
||||
|
||||
<details>
|
||||
<summary><b>Excel Date Code details</b> (click to show)</summary>
|
||||
|
||||
By default, Excel stores dates as numbers with a format code that specifies date
|
||||
processing. For example, the date `19-Feb-17` is stored as the number `42785`
|
||||
with a number format of `d-mmm-yy`. The `SSF` module understands number formats
|
||||
@ -55,3 +58,29 @@ string. The formatter converts the date back to a number.
|
||||
The default behavior for all parsers is to generate number cells. Setting
|
||||
`cellDates` to true will force the generators to store dates.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Time Zones and Dates</b> (click to show)</summary>
|
||||
|
||||
Excel has no native concept of universal time. All times are specified in the
|
||||
local time zone. Excel limitations prevent specifying true absolute dates.
|
||||
|
||||
Following Excel, this library treats all dates as relative to local time zone.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Epochs: 1900 and 1904</b> (click to show)</summary>
|
||||
|
||||
Excel supports two epochs (January 1 1900 and January 1 1904), see
|
||||
["1900 vs. 1904 Date System" article](http://support2.microsoft.com/kb/180162).
|
||||
The workbook's epoch can be determined by examining the workbook's
|
||||
`wb.Workbook.WBProps.date1904` property:
|
||||
|
||||
```js
|
||||
!!(((wb.Workbook||{}).WBProps||{}).date1904)
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
|
@ -8,10 +8,5 @@
|
||||
custom properties. Since the XLS standard properties deviate from the XLSX
|
||||
standard, XLS parsing stores core properties in both places.
|
||||
|
||||
`wb.WBProps` includes more workbook-level properties:
|
||||
|
||||
- Excel supports two epochs (January 1 1900 and January 1 1904), see
|
||||
[1900 vs. 1904 Date System](http://support2.microsoft.com/kb/180162).
|
||||
The workbook's epoch can be determined by examining the workbook's
|
||||
`wb.WBProps.date1904` property.
|
||||
`wb.Workbook` stores [workbook-level attributes](#workbook-level-attributes).
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
### Workbook-Level Attributes
|
||||
|
||||
`wb.Workbook` stores workbook level attributes.
|
||||
`wb.Workbook` stores workbook-level attributes.
|
||||
|
||||
#### Defined Names
|
||||
|
||||
@ -15,9 +15,18 @@
|
||||
| `Name` | Case-sensitive name. Standard rules apply ** |
|
||||
| `Ref` | A1-style Reference (e.g. `"Sheet1!$A$1:$D$20"`) |
|
||||
| `Comment` | Comment (only applicable for XLS/XLSX/XLSB) |
|
||||
|
||||
</details>
|
||||
|
||||
Excel allows two sheet-scoped defined names to share the same name. However, a
|
||||
sheet-scoped name cannot collide with a workbook-scope name. Workbook writers
|
||||
may not enforce this constraint.
|
||||
|
||||
#### Miscellaneous Workbook Properties
|
||||
|
||||
`wb.Workbook.WBProps` holds other workbook properties:
|
||||
|
||||
| Key | Description |
|
||||
|:-----------|:----------------------------------------------------|
|
||||
| `date1904` | epoch: 0/false for 1900 system, 1/true for 1904 |
|
||||
|
||||
|
@ -30,6 +30,7 @@
|
||||
+ [Workbook File Properties](README.md#workbook-file-properties)
|
||||
* [Workbook-Level Attributes](README.md#workbook-level-attributes)
|
||||
+ [Defined Names](README.md#defined-names)
|
||||
+ [Miscellaneous Workbook Properties](README.md#miscellaneous-workbook-properties)
|
||||
* [Document Features](README.md#document-features)
|
||||
+ [Formulae](README.md#formulae)
|
||||
+ [Column Properties](README.md#column-properties)
|
||||
|
@ -26,12 +26,17 @@ type Workbook = {
|
||||
type WBWBProps = {
|
||||
Sheets: Array<WBWSProp>;
|
||||
Names?: Array<any>;
|
||||
WBProps?: WBProps;
|
||||
};
|
||||
|
||||
type WBProps = {
|
||||
date1904?: boolean;
|
||||
};
|
||||
|
||||
type WBWSProp = {
|
||||
Hidden?: number;
|
||||
name?: string;
|
||||
}
|
||||
};
|
||||
|
||||
interface CellAddress {
|
||||
r:number;
|
||||
|
@ -6,15 +6,12 @@ AutoFilter .xls .xlsb .xlsx .xml
|
||||
# note: XLML only supports sheets, ods does not support dialog
|
||||
BlankSheetTypes .xls .xlsb .xlsm
|
||||
NumberFormatCondition .xls .xlsb .xlsm .xml
|
||||
#RkNumber .xls .xlsb .xlsx .xls.xml .xlsb.xml .xlsx.xml
|
||||
RkNumber .xls .xlsb .xlsx .xls.xml
|
||||
#calendar_stress_test .xls .xlsb .xlsx .xml
|
||||
cell_style_simple .xls .xlsb .xlsx .xml
|
||||
# no-csv (newline character \r vs \n)
|
||||
#comments_stress_test .xls .xlsb .xlsx .xls.xml .xlsb.xml .xlsx.xml
|
||||
comments_stress_test .xls .xlsb .xlsx .xls.xml
|
||||
# yes-csv
|
||||
#custom_properties .xls .xlsb .xlsx .xls.xml .xlsb.xml .xlsx.xml
|
||||
custom_properties .xls .xlsb .xlsx .xls.xml
|
||||
defined_names_simple .xls .xlsb .xlsx .xml
|
||||
# no-csv (randbetween) note: ODS does not support many XLSX functions
|
||||
@ -23,27 +20,22 @@ formula_stress_test .xls .xlsb .xlsx .xls.xml
|
||||
formulae_test_simple .xls .xlsb .xlsx .xml
|
||||
hyperlink_stress_test_2011 .xls .xlsb .xlsx .xml
|
||||
#large_strings .xls .xlsb .xlsx .xls.xml .xlsb.xml .xlsx.xml
|
||||
#merge_cells .xls .xlsb .xlsx .xls.xml .xlsb.xml .xlsx.xml
|
||||
merge_cells .xls .xlsb .xlsx .xls.xml
|
||||
# no-formula (filename-references in XLSX encoding as [0])
|
||||
#named_ranges_2011 .xls .xlsb .xlsx .xls.xml .xlsb.xml .xlsx.xml
|
||||
named_ranges_2011 .xls .xlsb .xlsx .xls.xml
|
||||
# yes-formula
|
||||
# no-csv (macro serialization in xml)
|
||||
# no-formula (rounding issue in IE8)
|
||||
#number_format .xls .xlsb .xlsm .xls.xml .xlsb.xml .xlsm.xml
|
||||
number_format .xls .xlsb .xlsm .xls.xml
|
||||
# yes-formula
|
||||
# yes-csv
|
||||
number_format_entities .xls .xlsb .xlsx .xml
|
||||
pivot_table_named_range .xls .xlsb .xlsx .xml
|
||||
pivot_table_test .xls .xlsb .xlsm
|
||||
#rich_text_stress .xls .xlsb .xlsx .xls.xml .xlsb.xml .xlsx.xml
|
||||
rich_text_stress .xls .xlsb .xlsx .xls.xml
|
||||
smart_tags_2007 .xls .xlsb .xlsx .xml
|
||||
sushi .xls .xlsb .xlsx .xml
|
||||
text_and_numbers .xls .xlsb .xlsx .xml
|
||||
#time_stress_test_1 .xls .xlsb .xlsx .xml
|
||||
#xlsx-stream-d-date-cell .xls .xlsb .xlsx .xls.xml .xlsb.xml .xlsx.xml
|
||||
# no-formula (rounding issue in IE8)
|
||||
xlsx-stream-d-date-cell .xls .xlsb .xlsx .xls.xml
|
||||
|
16
test.js
16
test.js
@ -1502,10 +1502,12 @@ describe('invalid files', function() {
|
||||
});
|
||||
});
|
||||
|
||||
var basedate = new Date(1899, 11, 30, 0, 0, 0); // 2209161600000
|
||||
var dnthresh = basedate.getTime() + (new Date().getTimezoneOffset() - basedate.getTimezoneOffset()) * 60000;
|
||||
function datenum(v/*:Date*/, date1904/*:?boolean*/)/*:number*/ {
|
||||
var epoch = v.getTime();
|
||||
if(date1904) epoch += 1462*24*60*60*1000;
|
||||
return (epoch + 2209161600000) / (24 * 60 * 60 * 1000);
|
||||
return (epoch - dnthresh) / (24 * 60 * 60 * 1000);
|
||||
}
|
||||
|
||||
describe('json output', function() {
|
||||
@ -1685,7 +1687,17 @@ describe('csv', function() {
|
||||
var cell = get_cell(X.read(b, opts).Sheets.Sheet1, "C3");
|
||||
assert.equal(cell.w, '14-02-19');
|
||||
});
|
||||
|
||||
it('should interpret dateNF', function() {
|
||||
var bb = "1,2,3,\nTRUE,FALSE,,sheetjs\nfoo,bar,2/3/14,0.3\n,,,\nbaz,,qux,\n";
|
||||
var opts = {type:"binary", cellDates:true, dateNF:'m/d/yy'};
|
||||
var cell = get_cell(X.read(bb, opts).Sheets.Sheet1, "C3");
|
||||
assert.equal(cell.v.getMonth(), 1);
|
||||
assert.equal(cell.w, "2/3/14");
|
||||
opts = {type:"binary", cellDates:true, dateNF:'d/m/yy'};
|
||||
cell = get_cell(X.read(bb, opts).Sheets.Sheet1, "C3");
|
||||
assert.equal(cell.v.getMonth(), 2);
|
||||
assert.equal(cell.w, "2/3/14");
|
||||
});
|
||||
});
|
||||
describe('output', function(){
|
||||
var data, ws;
|
||||
|
9
types/index.d.ts
vendored
9
types/index.d.ts
vendored
@ -237,6 +237,15 @@ export interface WBProps {
|
||||
|
||||
/** Defined Names */
|
||||
Names?: DefinedName[];
|
||||
|
||||
/** Other Workbook Properties */
|
||||
WBProps?: WorkbookProperties;
|
||||
}
|
||||
|
||||
/** Other Workbook Properties */
|
||||
export interface WorkbookProperties {
|
||||
/** Worksheet Epoch (1904 if true, 1900 if false) */
|
||||
date1904?: boolean;
|
||||
}
|
||||
|
||||
export interface ColInfo {
|
||||
|
213
xlsx.flow.js
213
xlsx.flow.js
@ -993,6 +993,33 @@ var XLMLFormatMap/*{[string]:string}*/ = ({
|
||||
"On/Off": '"Yes";"Yes";"No";@'
|
||||
}/*:any*/);
|
||||
|
||||
/* dateNF parse TODO: move to SSF */
|
||||
var dateNFregex = /[dD]+|[mM]+|[yYeE]+|[Hh]+|[Ss]+/g;
|
||||
function dateNF_regex(dateNF/*:string|number*/)/*:RegExp*/ {
|
||||
var fmt = typeof dateNF == "number" ? SSF._table[dateNF] : dateNF;
|
||||
fmt = fmt.replace(dateNFregex, "(\\d+)");
|
||||
return new RegExp("^" + fmt + "$");
|
||||
}
|
||||
function dateNF_fix(str/*:string*/, dateNF/*:string*/, match/*:Array<string>*/)/*:string*/ {
|
||||
var Y = -1, m = -1, d = -1, H = -1, M = -1, S = -1;
|
||||
(dateNF.match(dateNFregex)||[]).forEach(function(n, i) {
|
||||
var v = parseInt(match[i+1], 10);
|
||||
switch(n.toLowerCase().charAt(0)) {
|
||||
case 'y': Y = v; break; case 'd': d = v; break;
|
||||
case 'h': H = v; break; case 's': S = v; break;
|
||||
case 'm': if(H >= 0) M = v; else m = v; break;
|
||||
}
|
||||
});
|
||||
if(S >= 0 && M == -1 && m >= 0) { M = m; m = -1; }
|
||||
var datestr = (("" + (Y>=0?Y: new Date().getFullYear())).slice(-4) + "-" + ("00" + (m>=1?m:1)).slice(-2) + "-" + ("00" + (d>=1?d:1)).slice(-2));
|
||||
if(datestr.length == 7) datestr = "0" + datestr;
|
||||
if(datestr.length == 8) datestr = "20" + datestr;
|
||||
var timestr = (("00" + (H>=0?H:0)).slice(-2) + ":" + ("00" + (M>=0?M:0)).slice(-2) + ":" + ("00" + (S>=0?S:0)).slice(-2));
|
||||
if(H == -1 && M == -1 && S == -1) return datestr;
|
||||
if(Y == -1 && m == -1 && d == -1) return timestr;
|
||||
return datestr + "T" + timestr;
|
||||
}
|
||||
|
||||
var DO_NOT_EXPORT_CFB = true;
|
||||
/*::
|
||||
declare var Base64:any;
|
||||
@ -1435,22 +1462,17 @@ function evert_arr(obj/*:any*/)/*:EvertArrType*/ {
|
||||
return o;
|
||||
}
|
||||
|
||||
var basedate = new Date(1899, 11, 30, 0, 0, 0); // 2209161600000
|
||||
var dnthresh = basedate.getTime() + (new Date().getTimezoneOffset() - basedate.getTimezoneOffset()) * 60000;
|
||||
function datenum(v/*:Date*/, date1904/*:?boolean*/)/*:number*/ {
|
||||
var epoch = v.getTime();
|
||||
if(date1904) epoch += 1462*24*60*60*1000;
|
||||
return (epoch + 2209161600000) / (24 * 60 * 60 * 1000);
|
||||
return (epoch - dnthresh) / (24 * 60 * 60 * 1000);
|
||||
}
|
||||
function numdate(v/*:number*/)/*:Date*/ {
|
||||
var date = SSF.parse_date_code(v);
|
||||
var val = new Date();
|
||||
if(date == null) throw new Error("Bad Date Code: " + v);
|
||||
val.setUTCDate(date.d);
|
||||
val.setUTCMonth(date.m-1);
|
||||
val.setUTCFullYear(date.y);
|
||||
val.setUTCHours(date.H);
|
||||
val.setUTCMinutes(date.M);
|
||||
val.setUTCSeconds(date.S);
|
||||
return val;
|
||||
var out = new Date();
|
||||
out.setTime(v * 24 * 60 * 60 * 1000 + dnthresh);
|
||||
return out;
|
||||
}
|
||||
|
||||
/* ISO 8601 Duration */
|
||||
@ -1483,9 +1505,15 @@ function parse_isodur(s) {
|
||||
var good_pd_date = new Date('2017-02-19T19:06:09.000Z');
|
||||
if(isNaN(good_pd_date.getFullYear())) good_pd_date = new Date('2/19/17');
|
||||
var good_pd = good_pd_date.getFullYear() == 2017;
|
||||
function parseDate(str/*:string|Date*/)/*:Date*/ {
|
||||
/* parses aa date as a local date */
|
||||
function parseDate(str/*:string|Date*/, fixdate/*:?number*/)/*:Date*/ {
|
||||
var d = new Date(str);
|
||||
if(good_pd) return d;
|
||||
if(good_pd) {
|
||||
/*:: if(fixdate == null) fixdate = 0; */
|
||||
if(fixdate > 0) d.setTime(d.getTime() + d.getTimezoneOffset() * 60 * 1000);
|
||||
else if(fixdate < 0) d.setTime(d.getTime() - d.getTimezoneOffset() * 60 * 1000);
|
||||
return d;
|
||||
}
|
||||
if(str instanceof Date) return str;
|
||||
if(good_pd_date.getFullYear() == 1917 && !isNaN(d.getFullYear())) {
|
||||
var s = d.getFullYear();
|
||||
@ -1493,7 +1521,7 @@ function parseDate(str/*:string|Date*/)/*:Date*/ {
|
||||
d.setFullYear(d.getFullYear() + 100); return d;
|
||||
}
|
||||
var n = str.match(/\d+/g)||["2017","2","19","0","0","0"];
|
||||
return new Date(Date.UTC(+n[0], +n[1] - 1, +n[2], (+n[3]||0), (+n[4]||0), (+n[5]||0)));
|
||||
return new Date(+n[0], +n[1] - 1, +n[2], (+n[3]||0), (+n[4]||0), (+n[5]||0));
|
||||
}
|
||||
|
||||
function cc2str(arr/*:Array<number>*/)/*:string*/ {
|
||||
@ -1681,7 +1709,7 @@ var xlml_unfixstr/*:StringConv*/ = (function() {
|
||||
|
||||
function parsexmlbool(value/*:any*/, tag/*:?string*/)/*:boolean*/ {
|
||||
switch(value) {
|
||||
case '1': case 'true': case 'TRUE': return true;
|
||||
case 1: case true: case '1': case 'true': case 'TRUE': return true;
|
||||
/* case '0': case 'false': case 'FALSE':*/
|
||||
default: return false;
|
||||
}
|
||||
@ -5666,6 +5694,7 @@ var PRN = (function() {
|
||||
var R = 0, C = 0, v = 0;
|
||||
var start = 0, end = 0, sepcc = sep.charCodeAt(0), instr = false, cc=0;
|
||||
str = str.replace(/\r\n/mg, "\n");
|
||||
var _re/*:?RegExp*/ = o.dateNF != null ? dateNF_regex(o.dateNF) : null;
|
||||
function finish_cell() {
|
||||
var s = str.slice(start, end);
|
||||
var cell = ({}/*:any*/);
|
||||
@ -5673,10 +5702,12 @@ var PRN = (function() {
|
||||
else if(s == "TRUE") { cell.t = 'b'; cell.v = true; }
|
||||
else if(s == "FALSE") { cell.t = 'b'; cell.v = false; }
|
||||
else if(!isNaN(v = +s)) { cell.t = 'n'; cell.w = s; cell.v = v; }
|
||||
else if(!isNaN(fuzzydate(s).getDate())) {
|
||||
else if(!isNaN(fuzzydate(s).getDate()) || _re && s.match(_re)) {
|
||||
cell.z = o.dateNF || SSF._table[14];
|
||||
if(o.cellDates) { cell.t = 'd'; cell.v = parseDate(s); }
|
||||
else { cell.t = 'n'; cell.v = datenum(parseDate(s)); }
|
||||
var k = 0;
|
||||
if(_re && s.match(_re)){ s=dateNF_fix(s, o.dateNF, (s.match(_re)||[])); k=1; }
|
||||
if(o.cellDates) { cell.t = 'd'; cell.v = parseDate(s, k); }
|
||||
else { cell.t = 'n'; cell.v = datenum(parseDate(s, k)); }
|
||||
cell.w = SSF.format(cell.z, cell.v instanceof Date ? datenum(cell.v):cell.v);
|
||||
} else {
|
||||
cell.t = 's';
|
||||
@ -10849,7 +10880,7 @@ function write_ws_xml_cell(cell, ref, ws, opts, idx, wb) {
|
||||
case 'n': vv = ''+cell.v; break;
|
||||
case 'e': vv = BErr[cell.v]; break;
|
||||
case 'd':
|
||||
if(opts.cellDates) vv = parseDate(cell.v).toISOString();
|
||||
if(opts.cellDates) vv = parseDate(cell.v, -1).toISOString();
|
||||
else {
|
||||
cell.t = 'n';
|
||||
vv = ''+(cell.v = datenum(parseDate(cell.v)));
|
||||
@ -11002,7 +11033,8 @@ return function parse_ws_xml_data(sdata, s, opts, guess, themes, styles) {
|
||||
break;
|
||||
case 'b': p.v = parsexmlbool(p.v); break;
|
||||
case 'd':
|
||||
if(!opts.cellDates) { p.v = datenum(parseDate(p.v)); p.t = 'n'; }
|
||||
if(opts.cellDates) p.v = parseDate(p.v, 1);
|
||||
else { p.v = datenum(parseDate(p.v, 1)); p.t = 'n'; }
|
||||
break;
|
||||
/* error string in .w, number in .v */
|
||||
case 'e':
|
||||
@ -11019,9 +11051,7 @@ return function parse_ws_xml_data(sdata, s, opts, guess, themes, styles) {
|
||||
}
|
||||
}
|
||||
safe_format(p, fmtid, fillid, opts, themes, styles);
|
||||
if(opts.cellDates && do_format && p.t == 'n' && SSF.is_date(SSF._table[fmtid])) {
|
||||
var _d = SSF.parse_date_code(p.v); if(_d) { p.t = 'd'; p.v = new Date(Date.UTC(_d.y, _d.m-1,_d.d,_d.H,_d.M,_d.S,_d.u)); }
|
||||
}
|
||||
if(opts.cellDates && do_format && p.t == 'n' && SSF.is_date(SSF._table[fmtid])) { p.t = 'd'; p.v = numdate(p.v); }
|
||||
if(dense) {
|
||||
var _r = decode_cell(tag.r);
|
||||
if(!s[_r.r]) s[_r.r] = [];
|
||||
@ -11638,7 +11668,7 @@ function parse_ws_bin(data, _opts, rels, wb, themes, styles)/*:Worksheet*/ {
|
||||
if(refguess.e.r < row.r) refguess.e.r = row.r;
|
||||
if(refguess.e.c < C) refguess.e.c = C;
|
||||
if(opts.cellDates && cf && p.t == 'n' && SSF.is_date(SSF._table[cf.ifmt])) {
|
||||
var _d = SSF.parse_date_code(p.v); if(_d) { p.t = 'd'; p.v = new Date(Date.UTC(_d.y, _d.m-1,_d.d,_d.H,_d.M,_d.S,_d.u)); }
|
||||
var _d = SSF.parse_date_code(p.v); if(_d) { p.t = 'd'; p.v = new Date(_d.y, _d.m-1,_d.d,_d.H,_d.M,_d.S,_d.u); }
|
||||
}
|
||||
break;
|
||||
|
||||
@ -12120,38 +12150,37 @@ function write_cs_bin(idx/*:number*/, opts, wb/*:Workbook*/, rels) {
|
||||
}
|
||||
/* 18.2.28 (CT_WorkbookProtection) Defaults */
|
||||
var WBPropsDef = [
|
||||
['allowRefreshQuery', '0'],
|
||||
['autoCompressPictures', '1'],
|
||||
['backupFile', '0'],
|
||||
['checkCompatibility', '0'],
|
||||
['codeName', ''],
|
||||
['date1904', '0'],
|
||||
['dateCompatibility', '1'],
|
||||
//['defaultThemeVersion', '0'],
|
||||
['filterPrivacy', '0'],
|
||||
['hidePivotFieldList', '0'],
|
||||
['promptedSolutions', '0'],
|
||||
['publishItems', '0'],
|
||||
['refreshAllConnections', false],
|
||||
['saveExternalLinkValues', '1'],
|
||||
['showBorderUnselectedTables', '1'],
|
||||
['showInkAnnotation', '1'],
|
||||
['showObjects', 'all'],
|
||||
['showPivotChartFilter', '0']
|
||||
//['updateLinks', 'userSet']
|
||||
['allowRefreshQuery', false, "bool"],
|
||||
['autoCompressPictures', true, "bool"],
|
||||
['backupFile', false, "bool"],
|
||||
['checkCompatibility', false, "bool"],
|
||||
['codeName', ''],
|
||||
['date1904', false, "bool"],
|
||||
['defaultThemeVersion', 0, "int"],
|
||||
['filterPrivacy', false, "bool"],
|
||||
['hidePivotFieldList', false, "bool"],
|
||||
['promptedSolutions', false, "bool"],
|
||||
['publishItems', false, "bool"],
|
||||
['refreshAllConnections', false, "bool"],
|
||||
['saveExternalLinkValues', true, "bool"],
|
||||
['showBorderUnselectedTables', true, "bool"],
|
||||
['showInkAnnotation', true, "bool"],
|
||||
['showObjects', 'all'],
|
||||
['showPivotChartFilter', false, "bool"],
|
||||
['updateLinks', 'userSet']
|
||||
];
|
||||
|
||||
/* 18.2.30 (CT_BookView) Defaults */
|
||||
var WBViewDef = [
|
||||
['activeTab', '0'],
|
||||
['autoFilterDateGrouping', '1'],
|
||||
['firstSheet', '0'],
|
||||
['minimized', '0'],
|
||||
['showHorizontalScroll', '1'],
|
||||
['showSheetTabs', '1'],
|
||||
['showVerticalScroll', '1'],
|
||||
['tabRatio', '600'],
|
||||
['visibility', 'visible']
|
||||
['activeTab', 0, "int"],
|
||||
['autoFilterDateGrouping', true, "bool"],
|
||||
['firstSheet', 0, "int"],
|
||||
['minimized', false, "bool"],
|
||||
['showHorizontalScroll', true, "bool"],
|
||||
['showSheetTabs', true, "bool"],
|
||||
['showVerticalScroll', true, "bool"],
|
||||
['tabRatio', 600, "int"],
|
||||
['visibility', 'visible']
|
||||
//window{Height,Width}, {x,y}Window
|
||||
];
|
||||
|
||||
@ -12200,12 +12229,20 @@ function push_defaults_array(target, defaults) {
|
||||
for(var j = 0; j != target.length; ++j) { var w = target[j];
|
||||
for(var i=0; i != defaults.length; ++i) { var z = defaults[i];
|
||||
if(w[z[0]] == null) w[z[0]] = z[1];
|
||||
else switch(z[2]) {
|
||||
case "bool": if(typeof w[z[0]] == "string") w[z[0]] = parsexmlbool(w[z[0]], z[0]); break;
|
||||
case "int": if(typeof w[z[0]] == "string") w[z[0]] = parseInt(w[z[0]], 10); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function push_defaults(target, defaults) {
|
||||
for(var i = 0; i != defaults.length; ++i) { var z = defaults[i];
|
||||
if(target[z[0]] == null) target[z[0]] = z[1];
|
||||
else switch(z[2]) {
|
||||
case "bool": if(typeof target[z[0]] == "string") target[z[0]] = parsexmlbool(target[z[0]], z[0]); break;
|
||||
case "int": if(typeof target[z[0]] == "string") target[z[0]] = parseInt(target[z[0]], 10); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -12219,6 +12256,13 @@ function parse_wb_defaults(wb) {
|
||||
_ssfopts.date1904 = parsexmlbool(wb.WBProps.date1904, 'date1904');
|
||||
}
|
||||
|
||||
function safe1904(wb/*:Workbook*/)/*:string*/ {
|
||||
/* TODO: store date1904 somewhere else */
|
||||
if(!wb.Workbook) return "false";
|
||||
if(!wb.Workbook.WBProps) return "false";
|
||||
return parsexmlbool(wb.Workbook.WBProps.date1904) ? "true" : "false";
|
||||
}
|
||||
|
||||
var badchars = "][*?\/\\".split("");
|
||||
function check_ws_name(n/*:string*/, safe/*:?boolean*/)/*:boolean*/ {
|
||||
if(n.length > 31) { if(safe) return false; throw new Error("Sheet names cannot exceed 31 chars"); }
|
||||
@ -12269,8 +12313,17 @@ function parse_wb_xml(data, opts)/*:WorkbookFile*/ {
|
||||
case '<fileSharing': case '<fileSharing/>': break;
|
||||
|
||||
/* 18.2.28 workbookPr CT_WorkbookPr ? */
|
||||
case '<workbookPr': delete y[0]; wb.WBProps = y; break;
|
||||
case '<workbookPr/>': delete y[0]; wb.WBProps = y; break;
|
||||
case '<workbookPr':
|
||||
case '<workbookPr/>':
|
||||
WBPropsDef.forEach(function(w) {
|
||||
if(y[w[0]] == null) return;
|
||||
switch(w[2]) {
|
||||
case "bool": wb.WBProps[w[0]] = parsexmlbool(y[w[0]], w[0]); break;
|
||||
case "int": wb.WBProps[w[0]] = parseInt(y[w[0]], 10); break;
|
||||
default: wb.WBProps[w[0]] = y[w[0]];
|
||||
}
|
||||
});
|
||||
break;
|
||||
case '</workbookPr>': break;
|
||||
|
||||
/* 18.2.29 workbookProtection CT_WorkbookProtection ? */
|
||||
@ -12391,14 +12444,6 @@ var WB_XML_ROOT = writextag('workbook', null, {
|
||||
'xmlns:r': XMLNS.r
|
||||
});
|
||||
|
||||
function safe1904(wb/*:Workbook*/)/*:string*/ {
|
||||
/* TODO: store date1904 somewhere else */
|
||||
if(!wb.Workbook) return "false";
|
||||
if(!wb.Workbook.WBProps) return "false";
|
||||
// $FlowIgnore
|
||||
return parsexmlbool(wb.Workbook.WBProps.date1904) ? "true" : "false";
|
||||
}
|
||||
|
||||
function write_wb_xml(wb/*:Workbook*/, opts/*:?WriteOpts*/)/*:string*/ {
|
||||
var o = [XML_HEADER];
|
||||
o[o.length] = WB_XML_ROOT;
|
||||
@ -12476,10 +12521,27 @@ function write_BrtBundleSh(data, o) {
|
||||
|
||||
/* [MS-XLSB] 2.4.807 BrtWbProp */
|
||||
function parse_BrtWbProp(data, length) {
|
||||
data.read_shift(4);
|
||||
var dwThemeVersion = data.read_shift(4);
|
||||
var o = {};
|
||||
var flags = data.read_shift(4);
|
||||
o.defaultThemeVersion = data.read_shift(4);
|
||||
var strName = (length > 8) ? parse_XLWideString(data) : "";
|
||||
return [dwThemeVersion, strName];
|
||||
if(strName.length > 0) o.codeName = strName;
|
||||
o.autoCompressPictures = !!(flags & 0x10000);
|
||||
o.backupFile = !!(flags & 0x40);
|
||||
o.checkCompatibility = !!(flags & 0x1000);
|
||||
o.date1904 = !!(flags & 0x01);
|
||||
o.filterPrivacy = !!(flags & 0x08);
|
||||
o.hidePivotFieldList = !!(flags & 0x400);
|
||||
o.promptedSolutions = !!(flags & 0x10);
|
||||
o.publishItems = !!(flags & 0x800);
|
||||
o.refreshAllConnections = !!(flags & 0x40000);
|
||||
o.saveExternalLinkValues = !!(flags & 0x80);
|
||||
o.showBorderUnselectedTables = !!(flags & 0x04);
|
||||
o.showInkAnnotation = !!(flags & 0x20);
|
||||
o.showObjects = ["all", "placeholders", "none"][(flags >> 13) & 0x03];
|
||||
o.showPivotChartFilter = !!(flags & 0x8000);
|
||||
o.updateLinks = ["userSet", "never", "always"][(flags >> 8) & 0x03];
|
||||
return o;
|
||||
}
|
||||
function write_BrtWbProp(data, o) {
|
||||
if(!o) o = new_buf(72);
|
||||
@ -12543,6 +12605,9 @@ function parse_wb_bin(data, opts)/*:WorkbookFile*/ {
|
||||
break;
|
||||
case 0x040C: /* 'BrtNameExt' */ break;
|
||||
|
||||
case 0x0099: /* 'BrtWbProp' */
|
||||
wb.WBProps = val; break;
|
||||
|
||||
/* case 'BrtModelTimeGroupingCalcCol' */
|
||||
/* case 'BrtRevisionPtr' */
|
||||
/* case 'BrtUid' */
|
||||
@ -12574,7 +12639,6 @@ function parse_wb_bin(data, opts)/*:WorkbookFile*/ {
|
||||
case 0x0822: /* 'BrtTimelineCachePivotCacheID' */
|
||||
case 0x018D: /* 'BrtUserBookView' */
|
||||
case 0x009A: /* 'BrtWbFactoid' */
|
||||
case 0x0099: /* 'BrtWbProp' */
|
||||
case 0x045D: /* 'BrtWbProp14' */
|
||||
case 0x0229: /* 'BrtWebOpt' */
|
||||
case 0x082B: /* 'BrtWorkBookPr15' */
|
||||
@ -12861,7 +12925,7 @@ function safe_format_xlml(cell/*:Cell*/, nf, o) {
|
||||
var z = XLMLFormatMap[nf]||nf||"General";
|
||||
if(o.cellNF) cell.z = z;
|
||||
if(o.cellDates && cell.t == 'n' && SSF.is_date(z)) {
|
||||
var _d = SSF.parse_date_code(cell.v); if(_d) { cell.t = 'd'; cell.v = new Date(Date.UTC(_d.y, _d.m-1,_d.d,_d.H,_d.M,_d.S,_d.u)); }
|
||||
var _d = SSF.parse_date_code(cell.v); if(_d) { cell.t = 'd'; cell.v = new Date(_d.y, _d.m-1,_d.d,_d.H,_d.M,_d.S,_d.u); }
|
||||
}
|
||||
} catch(e) { if(o.WTF) throw e; }
|
||||
}
|
||||
@ -12978,7 +13042,7 @@ function parse_xlml_xml(d, opts)/*:Workbook*/ {
|
||||
var cstys = [], csty, seencol = false;
|
||||
var arrayf = [];
|
||||
var rowinfo = [], rowobj = {};
|
||||
var Workbook/*:WBWBProps*/ = { Sheets:[] }, wsprops = {};
|
||||
var Workbook/*:WBWBProps*/ = ({ Sheets:[], WBProps:{date1904:false} }/*:any*/), wsprops = {};
|
||||
xlmlregex.lastIndex = 0;
|
||||
str = str.replace(/<!--([^\u2603]*?)-->/mg,"");
|
||||
while((Rn = xlmlregex.exec(str))) switch(Rn[3]) {
|
||||
@ -13259,6 +13323,10 @@ function parse_xlml_xml(d, opts)/*:Workbook*/ {
|
||||
|
||||
/* ExcelWorkbook */
|
||||
case 'ExcelWorkbook': switch(Rn[3]) {
|
||||
case 'Date1904':
|
||||
/*:: if(!Workbook.WBProps) Workbook.WBProps = {}; */
|
||||
Workbook.WBProps.date1904 = true;
|
||||
break;
|
||||
case 'WindowHeight': break;
|
||||
case 'WindowWidth': break;
|
||||
case 'WindowTopX': break;
|
||||
@ -13277,7 +13345,6 @@ function parse_xlml_xml(d, opts)/*:Workbook*/ {
|
||||
case 'Dll': break;
|
||||
case 'AcceptLabelsInFormulas': break;
|
||||
case 'DoNotSaveLinkValues': break;
|
||||
case 'Date1904': break;
|
||||
case 'Iteration': break;
|
||||
case 'MaxIterations': break;
|
||||
case 'MaxChange': break;
|
||||
@ -13938,7 +14005,7 @@ function safe_format_xf(p/*:any*/, opts/*:ParseOpts*/, date1904/*:?boolean*/) {
|
||||
}
|
||||
else p.w = SSF.format(fmtid,p.v, {date1904:!!date1904});
|
||||
if(opts.cellDates && fmtid && p.t == 'n' && SSF.is_date(SSF._table[fmtid])) {
|
||||
var _d = SSF.parse_date_code(p.v); if(_d) { p.t = 'd'; p.v = new Date(Date.UTC(_d.y, _d.m-1,_d.d,_d.H,_d.M,_d.S,_d.u)); }
|
||||
var _d = SSF.parse_date_code(p.v); if(_d) { p.t = 'd'; p.v = new Date(_d.y, _d.m-1,_d.d,_d.H,_d.M,_d.S,_d.u); }
|
||||
}
|
||||
} catch(e) { if(opts.WTF) throw e; }
|
||||
}
|
||||
@ -13968,7 +14035,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
var cell_valid = true;
|
||||
var XFs = []; /* XF records */
|
||||
var palette = [];
|
||||
var Workbook/*:WBWBProps*/ = ({ Sheets:[] }/*:any*/), wsprops = {};
|
||||
var Workbook/*:WBWBProps*/ = ({ Sheets:[], WBProps:{date1904:false} }/*:any*/), wsprops = {};
|
||||
var get_rgb = function getrgb(icv) {
|
||||
if(icv < 8) return XLSIcv[icv];
|
||||
if(icv < 64) return palette[icv-8] || XLSIcv[icv];
|
||||
@ -14080,7 +14147,9 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
/* nested switch statements to workaround V8 128 limit */
|
||||
switch(Rn) {
|
||||
/* Workbook Options */
|
||||
case 'Date1904': wb.opts.Date1904 = val; break;
|
||||
case 'Date1904':
|
||||
/*:: if(!Workbook.WBProps) Workbook.WBProps = {}; */
|
||||
wb.opts.Date1904 = Workbook.WBProps.date1904 = val; break;
|
||||
case 'WriteProtect': wb.opts.WriteProtect = true; break;
|
||||
case 'FilePass':
|
||||
if(!opts.enc) blob.l = 0;
|
||||
|
210
xlsx.js
210
xlsx.js
@ -967,6 +967,33 @@ var XLMLFormatMap/*{[string]:string}*/ = ({
|
||||
"On/Off": '"Yes";"Yes";"No";@'
|
||||
});
|
||||
|
||||
/* dateNF parse TODO: move to SSF */
|
||||
var dateNFregex = /[dD]+|[mM]+|[yYeE]+|[Hh]+|[Ss]+/g;
|
||||
function dateNF_regex(dateNF) {
|
||||
var fmt = typeof dateNF == "number" ? SSF._table[dateNF] : dateNF;
|
||||
fmt = fmt.replace(dateNFregex, "(\\d+)");
|
||||
return new RegExp("^" + fmt + "$");
|
||||
}
|
||||
function dateNF_fix(str, dateNF, match) {
|
||||
var Y = -1, m = -1, d = -1, H = -1, M = -1, S = -1;
|
||||
(dateNF.match(dateNFregex)||[]).forEach(function(n, i) {
|
||||
var v = parseInt(match[i+1], 10);
|
||||
switch(n.toLowerCase().charAt(0)) {
|
||||
case 'y': Y = v; break; case 'd': d = v; break;
|
||||
case 'h': H = v; break; case 's': S = v; break;
|
||||
case 'm': if(H >= 0) M = v; else m = v; break;
|
||||
}
|
||||
});
|
||||
if(S >= 0 && M == -1 && m >= 0) { M = m; m = -1; }
|
||||
var datestr = (("" + (Y>=0?Y: new Date().getFullYear())).slice(-4) + "-" + ("00" + (m>=1?m:1)).slice(-2) + "-" + ("00" + (d>=1?d:1)).slice(-2));
|
||||
if(datestr.length == 7) datestr = "0" + datestr;
|
||||
if(datestr.length == 8) datestr = "20" + datestr;
|
||||
var timestr = (("00" + (H>=0?H:0)).slice(-2) + ":" + ("00" + (M>=0?M:0)).slice(-2) + ":" + ("00" + (S>=0?S:0)).slice(-2));
|
||||
if(H == -1 && M == -1 && S == -1) return datestr;
|
||||
if(Y == -1 && m == -1 && d == -1) return timestr;
|
||||
return datestr + "T" + timestr;
|
||||
}
|
||||
|
||||
var DO_NOT_EXPORT_CFB = true;
|
||||
/* cfb.js (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
/* vim: set ts=2: */
|
||||
@ -1385,22 +1412,17 @@ function evert_arr(obj) {
|
||||
return o;
|
||||
}
|
||||
|
||||
var basedate = new Date(1899, 11, 30, 0, 0, 0); // 2209161600000
|
||||
var dnthresh = basedate.getTime() + (new Date().getTimezoneOffset() - basedate.getTimezoneOffset()) * 60000;
|
||||
function datenum(v, date1904) {
|
||||
var epoch = v.getTime();
|
||||
if(date1904) epoch += 1462*24*60*60*1000;
|
||||
return (epoch + 2209161600000) / (24 * 60 * 60 * 1000);
|
||||
return (epoch - dnthresh) / (24 * 60 * 60 * 1000);
|
||||
}
|
||||
function numdate(v) {
|
||||
var date = SSF.parse_date_code(v);
|
||||
var val = new Date();
|
||||
if(date == null) throw new Error("Bad Date Code: " + v);
|
||||
val.setUTCDate(date.d);
|
||||
val.setUTCMonth(date.m-1);
|
||||
val.setUTCFullYear(date.y);
|
||||
val.setUTCHours(date.H);
|
||||
val.setUTCMinutes(date.M);
|
||||
val.setUTCSeconds(date.S);
|
||||
return val;
|
||||
var out = new Date();
|
||||
out.setTime(v * 24 * 60 * 60 * 1000 + dnthresh);
|
||||
return out;
|
||||
}
|
||||
|
||||
/* ISO 8601 Duration */
|
||||
@ -1433,9 +1455,14 @@ function parse_isodur(s) {
|
||||
var good_pd_date = new Date('2017-02-19T19:06:09.000Z');
|
||||
if(isNaN(good_pd_date.getFullYear())) good_pd_date = new Date('2/19/17');
|
||||
var good_pd = good_pd_date.getFullYear() == 2017;
|
||||
function parseDate(str) {
|
||||
/* parses aa date as a local date */
|
||||
function parseDate(str, fixdate) {
|
||||
var d = new Date(str);
|
||||
if(good_pd) return d;
|
||||
if(good_pd) {
|
||||
if(fixdate > 0) d.setTime(d.getTime() + d.getTimezoneOffset() * 60 * 1000);
|
||||
else if(fixdate < 0) d.setTime(d.getTime() - d.getTimezoneOffset() * 60 * 1000);
|
||||
return d;
|
||||
}
|
||||
if(str instanceof Date) return str;
|
||||
if(good_pd_date.getFullYear() == 1917 && !isNaN(d.getFullYear())) {
|
||||
var s = d.getFullYear();
|
||||
@ -1443,7 +1470,7 @@ function parseDate(str) {
|
||||
d.setFullYear(d.getFullYear() + 100); return d;
|
||||
}
|
||||
var n = str.match(/\d+/g)||["2017","2","19","0","0","0"];
|
||||
return new Date(Date.UTC(+n[0], +n[1] - 1, +n[2], (+n[3]||0), (+n[4]||0), (+n[5]||0)));
|
||||
return new Date(+n[0], +n[1] - 1, +n[2], (+n[3]||0), (+n[4]||0), (+n[5]||0));
|
||||
}
|
||||
|
||||
function cc2str(arr) {
|
||||
@ -1630,7 +1657,7 @@ var xlml_unfixstr = (function() {
|
||||
|
||||
function parsexmlbool(value, tag) {
|
||||
switch(value) {
|
||||
case '1': case 'true': case 'TRUE': return true;
|
||||
case 1: case true: case '1': case 'true': case 'TRUE': return true;
|
||||
/* case '0': case 'false': case 'FALSE':*/
|
||||
default: return false;
|
||||
}
|
||||
@ -5605,6 +5632,7 @@ var PRN = (function() {
|
||||
var R = 0, C = 0, v = 0;
|
||||
var start = 0, end = 0, sepcc = sep.charCodeAt(0), instr = false, cc=0;
|
||||
str = str.replace(/\r\n/mg, "\n");
|
||||
var _re = o.dateNF != null ? dateNF_regex(o.dateNF) : null;
|
||||
function finish_cell() {
|
||||
var s = str.slice(start, end);
|
||||
var cell = ({});
|
||||
@ -5612,10 +5640,12 @@ var PRN = (function() {
|
||||
else if(s == "TRUE") { cell.t = 'b'; cell.v = true; }
|
||||
else if(s == "FALSE") { cell.t = 'b'; cell.v = false; }
|
||||
else if(!isNaN(v = +s)) { cell.t = 'n'; cell.w = s; cell.v = v; }
|
||||
else if(!isNaN(fuzzydate(s).getDate())) {
|
||||
else if(!isNaN(fuzzydate(s).getDate()) || _re && s.match(_re)) {
|
||||
cell.z = o.dateNF || SSF._table[14];
|
||||
if(o.cellDates) { cell.t = 'd'; cell.v = parseDate(s); }
|
||||
else { cell.t = 'n'; cell.v = datenum(parseDate(s)); }
|
||||
var k = 0;
|
||||
if(_re && s.match(_re)){ s=dateNF_fix(s, o.dateNF, (s.match(_re)||[])); k=1; }
|
||||
if(o.cellDates) { cell.t = 'd'; cell.v = parseDate(s, k); }
|
||||
else { cell.t = 'n'; cell.v = datenum(parseDate(s, k)); }
|
||||
cell.w = SSF.format(cell.z, cell.v instanceof Date ? datenum(cell.v):cell.v);
|
||||
} else {
|
||||
cell.t = 's';
|
||||
@ -10784,7 +10814,7 @@ function write_ws_xml_cell(cell, ref, ws, opts, idx, wb) {
|
||||
case 'n': vv = ''+cell.v; break;
|
||||
case 'e': vv = BErr[cell.v]; break;
|
||||
case 'd':
|
||||
if(opts.cellDates) vv = parseDate(cell.v).toISOString();
|
||||
if(opts.cellDates) vv = parseDate(cell.v, -1).toISOString();
|
||||
else {
|
||||
cell.t = 'n';
|
||||
vv = ''+(cell.v = datenum(parseDate(cell.v)));
|
||||
@ -10937,7 +10967,8 @@ return function parse_ws_xml_data(sdata, s, opts, guess, themes, styles) {
|
||||
break;
|
||||
case 'b': p.v = parsexmlbool(p.v); break;
|
||||
case 'd':
|
||||
if(!opts.cellDates) { p.v = datenum(parseDate(p.v)); p.t = 'n'; }
|
||||
if(opts.cellDates) p.v = parseDate(p.v, 1);
|
||||
else { p.v = datenum(parseDate(p.v, 1)); p.t = 'n'; }
|
||||
break;
|
||||
/* error string in .w, number in .v */
|
||||
case 'e':
|
||||
@ -10954,9 +10985,7 @@ return function parse_ws_xml_data(sdata, s, opts, guess, themes, styles) {
|
||||
}
|
||||
}
|
||||
safe_format(p, fmtid, fillid, opts, themes, styles);
|
||||
if(opts.cellDates && do_format && p.t == 'n' && SSF.is_date(SSF._table[fmtid])) {
|
||||
var _d = SSF.parse_date_code(p.v); if(_d) { p.t = 'd'; p.v = new Date(Date.UTC(_d.y, _d.m-1,_d.d,_d.H,_d.M,_d.S,_d.u)); }
|
||||
}
|
||||
if(opts.cellDates && do_format && p.t == 'n' && SSF.is_date(SSF._table[fmtid])) { p.t = 'd'; p.v = numdate(p.v); }
|
||||
if(dense) {
|
||||
var _r = decode_cell(tag.r);
|
||||
if(!s[_r.r]) s[_r.r] = [];
|
||||
@ -11572,7 +11601,7 @@ function parse_ws_bin(data, _opts, rels, wb, themes, styles) {
|
||||
if(refguess.e.r < row.r) refguess.e.r = row.r;
|
||||
if(refguess.e.c < C) refguess.e.c = C;
|
||||
if(opts.cellDates && cf && p.t == 'n' && SSF.is_date(SSF._table[cf.ifmt])) {
|
||||
var _d = SSF.parse_date_code(p.v); if(_d) { p.t = 'd'; p.v = new Date(Date.UTC(_d.y, _d.m-1,_d.d,_d.H,_d.M,_d.S,_d.u)); }
|
||||
var _d = SSF.parse_date_code(p.v); if(_d) { p.t = 'd'; p.v = new Date(_d.y, _d.m-1,_d.d,_d.H,_d.M,_d.S,_d.u); }
|
||||
}
|
||||
break;
|
||||
|
||||
@ -12054,38 +12083,37 @@ function write_cs_bin(idx, opts, wb, rels) {
|
||||
}
|
||||
/* 18.2.28 (CT_WorkbookProtection) Defaults */
|
||||
var WBPropsDef = [
|
||||
['allowRefreshQuery', '0'],
|
||||
['autoCompressPictures', '1'],
|
||||
['backupFile', '0'],
|
||||
['checkCompatibility', '0'],
|
||||
['codeName', ''],
|
||||
['date1904', '0'],
|
||||
['dateCompatibility', '1'],
|
||||
//['defaultThemeVersion', '0'],
|
||||
['filterPrivacy', '0'],
|
||||
['hidePivotFieldList', '0'],
|
||||
['promptedSolutions', '0'],
|
||||
['publishItems', '0'],
|
||||
['refreshAllConnections', false],
|
||||
['saveExternalLinkValues', '1'],
|
||||
['showBorderUnselectedTables', '1'],
|
||||
['showInkAnnotation', '1'],
|
||||
['showObjects', 'all'],
|
||||
['showPivotChartFilter', '0']
|
||||
//['updateLinks', 'userSet']
|
||||
['allowRefreshQuery', false, "bool"],
|
||||
['autoCompressPictures', true, "bool"],
|
||||
['backupFile', false, "bool"],
|
||||
['checkCompatibility', false, "bool"],
|
||||
['codeName', ''],
|
||||
['date1904', false, "bool"],
|
||||
['defaultThemeVersion', 0, "int"],
|
||||
['filterPrivacy', false, "bool"],
|
||||
['hidePivotFieldList', false, "bool"],
|
||||
['promptedSolutions', false, "bool"],
|
||||
['publishItems', false, "bool"],
|
||||
['refreshAllConnections', false, "bool"],
|
||||
['saveExternalLinkValues', true, "bool"],
|
||||
['showBorderUnselectedTables', true, "bool"],
|
||||
['showInkAnnotation', true, "bool"],
|
||||
['showObjects', 'all'],
|
||||
['showPivotChartFilter', false, "bool"],
|
||||
['updateLinks', 'userSet']
|
||||
];
|
||||
|
||||
/* 18.2.30 (CT_BookView) Defaults */
|
||||
var WBViewDef = [
|
||||
['activeTab', '0'],
|
||||
['autoFilterDateGrouping', '1'],
|
||||
['firstSheet', '0'],
|
||||
['minimized', '0'],
|
||||
['showHorizontalScroll', '1'],
|
||||
['showSheetTabs', '1'],
|
||||
['showVerticalScroll', '1'],
|
||||
['tabRatio', '600'],
|
||||
['visibility', 'visible']
|
||||
['activeTab', 0, "int"],
|
||||
['autoFilterDateGrouping', true, "bool"],
|
||||
['firstSheet', 0, "int"],
|
||||
['minimized', false, "bool"],
|
||||
['showHorizontalScroll', true, "bool"],
|
||||
['showSheetTabs', true, "bool"],
|
||||
['showVerticalScroll', true, "bool"],
|
||||
['tabRatio', 600, "int"],
|
||||
['visibility', 'visible']
|
||||
//window{Height,Width}, {x,y}Window
|
||||
];
|
||||
|
||||
@ -12134,12 +12162,20 @@ function push_defaults_array(target, defaults) {
|
||||
for(var j = 0; j != target.length; ++j) { var w = target[j];
|
||||
for(var i=0; i != defaults.length; ++i) { var z = defaults[i];
|
||||
if(w[z[0]] == null) w[z[0]] = z[1];
|
||||
else switch(z[2]) {
|
||||
case "bool": if(typeof w[z[0]] == "string") w[z[0]] = parsexmlbool(w[z[0]], z[0]); break;
|
||||
case "int": if(typeof w[z[0]] == "string") w[z[0]] = parseInt(w[z[0]], 10); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function push_defaults(target, defaults) {
|
||||
for(var i = 0; i != defaults.length; ++i) { var z = defaults[i];
|
||||
if(target[z[0]] == null) target[z[0]] = z[1];
|
||||
else switch(z[2]) {
|
||||
case "bool": if(typeof target[z[0]] == "string") target[z[0]] = parsexmlbool(target[z[0]], z[0]); break;
|
||||
case "int": if(typeof target[z[0]] == "string") target[z[0]] = parseInt(target[z[0]], 10); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -12153,6 +12189,13 @@ function parse_wb_defaults(wb) {
|
||||
_ssfopts.date1904 = parsexmlbool(wb.WBProps.date1904, 'date1904');
|
||||
}
|
||||
|
||||
function safe1904(wb) {
|
||||
/* TODO: store date1904 somewhere else */
|
||||
if(!wb.Workbook) return "false";
|
||||
if(!wb.Workbook.WBProps) return "false";
|
||||
return parsexmlbool(wb.Workbook.WBProps.date1904) ? "true" : "false";
|
||||
}
|
||||
|
||||
var badchars = "][*?\/\\".split("");
|
||||
function check_ws_name(n, safe) {
|
||||
if(n.length > 31) { if(safe) return false; throw new Error("Sheet names cannot exceed 31 chars"); }
|
||||
@ -12203,8 +12246,17 @@ function parse_wb_xml(data, opts) {
|
||||
case '<fileSharing': case '<fileSharing/>': break;
|
||||
|
||||
/* 18.2.28 workbookPr CT_WorkbookPr ? */
|
||||
case '<workbookPr': delete y[0]; wb.WBProps = y; break;
|
||||
case '<workbookPr/>': delete y[0]; wb.WBProps = y; break;
|
||||
case '<workbookPr':
|
||||
case '<workbookPr/>':
|
||||
WBPropsDef.forEach(function(w) {
|
||||
if(y[w[0]] == null) return;
|
||||
switch(w[2]) {
|
||||
case "bool": wb.WBProps[w[0]] = parsexmlbool(y[w[0]], w[0]); break;
|
||||
case "int": wb.WBProps[w[0]] = parseInt(y[w[0]], 10); break;
|
||||
default: wb.WBProps[w[0]] = y[w[0]];
|
||||
}
|
||||
});
|
||||
break;
|
||||
case '</workbookPr>': break;
|
||||
|
||||
/* 18.2.29 workbookProtection CT_WorkbookProtection ? */
|
||||
@ -12325,14 +12377,6 @@ var WB_XML_ROOT = writextag('workbook', null, {
|
||||
'xmlns:r': XMLNS.r
|
||||
});
|
||||
|
||||
function safe1904(wb) {
|
||||
/* TODO: store date1904 somewhere else */
|
||||
if(!wb.Workbook) return "false";
|
||||
if(!wb.Workbook.WBProps) return "false";
|
||||
// $FlowIgnore
|
||||
return parsexmlbool(wb.Workbook.WBProps.date1904) ? "true" : "false";
|
||||
}
|
||||
|
||||
function write_wb_xml(wb, opts) {
|
||||
var o = [XML_HEADER];
|
||||
o[o.length] = WB_XML_ROOT;
|
||||
@ -12410,10 +12454,27 @@ function write_BrtBundleSh(data, o) {
|
||||
|
||||
/* [MS-XLSB] 2.4.807 BrtWbProp */
|
||||
function parse_BrtWbProp(data, length) {
|
||||
data.read_shift(4);
|
||||
var dwThemeVersion = data.read_shift(4);
|
||||
var o = {};
|
||||
var flags = data.read_shift(4);
|
||||
o.defaultThemeVersion = data.read_shift(4);
|
||||
var strName = (length > 8) ? parse_XLWideString(data) : "";
|
||||
return [dwThemeVersion, strName];
|
||||
if(strName.length > 0) o.codeName = strName;
|
||||
o.autoCompressPictures = !!(flags & 0x10000);
|
||||
o.backupFile = !!(flags & 0x40);
|
||||
o.checkCompatibility = !!(flags & 0x1000);
|
||||
o.date1904 = !!(flags & 0x01);
|
||||
o.filterPrivacy = !!(flags & 0x08);
|
||||
o.hidePivotFieldList = !!(flags & 0x400);
|
||||
o.promptedSolutions = !!(flags & 0x10);
|
||||
o.publishItems = !!(flags & 0x800);
|
||||
o.refreshAllConnections = !!(flags & 0x40000);
|
||||
o.saveExternalLinkValues = !!(flags & 0x80);
|
||||
o.showBorderUnselectedTables = !!(flags & 0x04);
|
||||
o.showInkAnnotation = !!(flags & 0x20);
|
||||
o.showObjects = ["all", "placeholders", "none"][(flags >> 13) & 0x03];
|
||||
o.showPivotChartFilter = !!(flags & 0x8000);
|
||||
o.updateLinks = ["userSet", "never", "always"][(flags >> 8) & 0x03];
|
||||
return o;
|
||||
}
|
||||
function write_BrtWbProp(data, o) {
|
||||
if(!o) o = new_buf(72);
|
||||
@ -12477,6 +12538,9 @@ function parse_wb_bin(data, opts) {
|
||||
break;
|
||||
case 0x040C: /* 'BrtNameExt' */ break;
|
||||
|
||||
case 0x0099: /* 'BrtWbProp' */
|
||||
wb.WBProps = val; break;
|
||||
|
||||
/* case 'BrtModelTimeGroupingCalcCol' */
|
||||
/* case 'BrtRevisionPtr' */
|
||||
/* case 'BrtUid' */
|
||||
@ -12508,7 +12572,6 @@ function parse_wb_bin(data, opts) {
|
||||
case 0x0822: /* 'BrtTimelineCachePivotCacheID' */
|
||||
case 0x018D: /* 'BrtUserBookView' */
|
||||
case 0x009A: /* 'BrtWbFactoid' */
|
||||
case 0x0099: /* 'BrtWbProp' */
|
||||
case 0x045D: /* 'BrtWbProp14' */
|
||||
case 0x0229: /* 'BrtWebOpt' */
|
||||
case 0x082B: /* 'BrtWorkBookPr15' */
|
||||
@ -12793,7 +12856,7 @@ function safe_format_xlml(cell, nf, o) {
|
||||
var z = XLMLFormatMap[nf]||nf||"General";
|
||||
if(o.cellNF) cell.z = z;
|
||||
if(o.cellDates && cell.t == 'n' && SSF.is_date(z)) {
|
||||
var _d = SSF.parse_date_code(cell.v); if(_d) { cell.t = 'd'; cell.v = new Date(Date.UTC(_d.y, _d.m-1,_d.d,_d.H,_d.M,_d.S,_d.u)); }
|
||||
var _d = SSF.parse_date_code(cell.v); if(_d) { cell.t = 'd'; cell.v = new Date(_d.y, _d.m-1,_d.d,_d.H,_d.M,_d.S,_d.u); }
|
||||
}
|
||||
} catch(e) { if(o.WTF) throw e; }
|
||||
}
|
||||
@ -12910,7 +12973,7 @@ function parse_xlml_xml(d, opts) {
|
||||
var cstys = [], csty, seencol = false;
|
||||
var arrayf = [];
|
||||
var rowinfo = [], rowobj = {};
|
||||
var Workbook = { Sheets:[] }, wsprops = {};
|
||||
var Workbook = ({ Sheets:[], WBProps:{date1904:false} }), wsprops = {};
|
||||
xlmlregex.lastIndex = 0;
|
||||
str = str.replace(/<!--([^\u2603]*?)-->/mg,"");
|
||||
while((Rn = xlmlregex.exec(str))) switch(Rn[3]) {
|
||||
@ -13190,6 +13253,9 @@ Workbook.Names.push(_DefinedName);
|
||||
|
||||
/* ExcelWorkbook */
|
||||
case 'ExcelWorkbook': switch(Rn[3]) {
|
||||
case 'Date1904':
|
||||
Workbook.WBProps.date1904 = true;
|
||||
break;
|
||||
case 'WindowHeight': break;
|
||||
case 'WindowWidth': break;
|
||||
case 'WindowTopX': break;
|
||||
@ -13208,7 +13274,6 @@ Workbook.Names.push(_DefinedName);
|
||||
case 'Dll': break;
|
||||
case 'AcceptLabelsInFormulas': break;
|
||||
case 'DoNotSaveLinkValues': break;
|
||||
case 'Date1904': break;
|
||||
case 'Iteration': break;
|
||||
case 'MaxIterations': break;
|
||||
case 'MaxChange': break;
|
||||
@ -13868,7 +13933,7 @@ function safe_format_xf(p, opts, date1904) {
|
||||
}
|
||||
else p.w = SSF.format(fmtid,p.v, {date1904:!!date1904});
|
||||
if(opts.cellDates && fmtid && p.t == 'n' && SSF.is_date(SSF._table[fmtid])) {
|
||||
var _d = SSF.parse_date_code(p.v); if(_d) { p.t = 'd'; p.v = new Date(Date.UTC(_d.y, _d.m-1,_d.d,_d.H,_d.M,_d.S,_d.u)); }
|
||||
var _d = SSF.parse_date_code(p.v); if(_d) { p.t = 'd'; p.v = new Date(_d.y, _d.m-1,_d.d,_d.H,_d.M,_d.S,_d.u); }
|
||||
}
|
||||
} catch(e) { if(opts.WTF) throw e; }
|
||||
}
|
||||
@ -13898,7 +13963,7 @@ function parse_workbook(blob, options) {
|
||||
var cell_valid = true;
|
||||
var XFs = []; /* XF records */
|
||||
var palette = [];
|
||||
var Workbook = ({ Sheets:[] }), wsprops = {};
|
||||
var Workbook = ({ Sheets:[], WBProps:{date1904:false} }), wsprops = {};
|
||||
var get_rgb = function getrgb(icv) {
|
||||
if(icv < 8) return XLSIcv[icv];
|
||||
if(icv < 64) return palette[icv-8] || XLSIcv[icv];
|
||||
@ -14010,7 +14075,8 @@ function parse_workbook(blob, options) {
|
||||
/* nested switch statements to workaround V8 128 limit */
|
||||
switch(Rn) {
|
||||
/* Workbook Options */
|
||||
case 'Date1904': wb.opts.Date1904 = val; break;
|
||||
case 'Date1904':
|
||||
wb.opts.Date1904 = Workbook.WBProps.date1904 = val; break;
|
||||
case 'WriteProtect': wb.opts.WriteProtect = true; break;
|
||||
case 'FilePass':
|
||||
if(!opts.enc) blob.l = 0;
|
||||
|
Loading…
Reference in New Issue
Block a user