1
+ // XLSX does have "d" type for dates, but it's not commonly used.
2
+ // Instead, it prefers using "n" type for storing dates as timestamps.
3
+ //
4
+ // Whether a numeric value is a number or a date timestamp, it sometimes could be
5
+ // detected by looking at the value "format" and seeing if it's a date-specific one.
6
+ // https://github.com/catamphetamine/read-excel-file/issues/3#issuecomment-395770777
7
+ //
8
+ // The list of generic numeric value "formats":
9
+ // https://xlsxwriter.readthedocs.io/format.html#format-set-num-format
10
+ //
11
+ export default function isDateTimestamp ( value , styleId , styles , options ) {
12
+ if ( styleId ) {
13
+ const style = styles [ styleId ]
14
+ if ( ! style ) {
15
+ throw new Error ( `Cell style not found: ${ styleId } ` )
16
+ }
17
+ if (
18
+ // Whether it's a "number format" that's conventionally used for storing date timestamps.
19
+ BUILT_IN_DATE_NUMBER_FORMAT_IDS . indexOf ( parseInt ( style . numberFormat . id ) ) >= 0 ||
20
+ // Whether it's a "number format" that uses a "formatting template"
21
+ // that the developer is certain is a date formatting template.
22
+ ( options . dateFormat && style . numberFormat . template === options . dateFormat ) ||
23
+ // Whether the "smart formatting template" feature is not disabled
24
+ // and it has detected that it's a date formatting template by looking at it.
25
+ ( options . smartDateParser !== false && style . numberFormat . template && isDateTemplate ( style . numberFormat . template ) )
26
+ ) {
27
+ return true
28
+ }
29
+ }
30
+ }
31
+
32
+ // https://hexdocs.pm/xlsxir/number_styles.html
33
+ const BUILT_IN_DATE_NUMBER_FORMAT_IDS = [ 14 , 15 , 16 , 17 , 18 , 19 , 20 , 21 , 22 , 27 , 30 , 36 , 45 , 46 , 47 , 50 , 57 ]
34
+
35
+ // On some date formats, there's an "[$-414]" prefix.
36
+ // I don't have any idea what that is.
37
+ //
38
+ // https://stackoverflow.com/questions/4730152/what-indicates-an-office-open-xml-cell-contains-a-date-time-value
39
+ //
40
+ // Examples:
41
+ //
42
+ // * 27 (built-in format) "[$-404]e/m/d"
43
+ // * 164 (custom format) "[$-414]mmmm\ yyyy;@"
44
+ //
45
+ const DATE_FORMAT_WEIRD_PREFIX = / ^ \[ \$ - 4 1 4 \] /
46
+
47
+ // On some date formats, there's an ";@" postfix.
48
+ // I don't have any idea what that is.
49
+ // Examples:
50
+ //
51
+ // * 164 (custom format) "m/d/yyyy;@"
52
+ // * 164 (custom format) "[$-414]mmmm\ yyyy;@"
53
+ //
54
+ const DATE_FORMAT_WEIRD_POSTFIX = / ; @ $ /
55
+
56
+ function isDateTemplate ( template ) {
57
+ // Date format tokens could be in upper case or in lower case.
58
+ // There seems to be no single standard.
59
+ // So lowercase the template first.
60
+ template = template . toLowerCase ( )
61
+
62
+ // On some date formats, there's an "[$-414]" prefix.
63
+ // I don't have any idea what that is. Trim it.
64
+ template = template . replace ( DATE_FORMAT_WEIRD_PREFIX , '' )
65
+
66
+ // On some date formats, there's an ";@" postfix.
67
+ // I don't have any idea what that is. Trim it.
68
+ template = template . replace ( DATE_FORMAT_WEIRD_POSTFIX , '' )
69
+
70
+ const tokens = template . split ( / \W + / )
71
+ for ( const token of tokens ) {
72
+ if ( DATE_TEMPLATE_TOKENS . indexOf ( token ) < 0 ) {
73
+ return false
74
+ }
75
+ }
76
+ return true
77
+ }
78
+
79
+ // These tokens could be in upper case or in lower case.
80
+ // There seems to be no single standard, so using lower case.
81
+ const DATE_TEMPLATE_TOKENS = [
82
+ // Seconds (min two digits). Example: "05".
83
+ 'ss' ,
84
+ // Minutes (min two digits). Example: "05". Could also be "Months". Weird.
85
+ 'mm' ,
86
+ // Hours. Example: "1".
87
+ 'h' ,
88
+ // Hours (min two digits). Example: "01".
89
+ 'hh' ,
90
+ // "AM" part of "AM/PM". Lowercased just in case.
91
+ 'am' ,
92
+ // "PM" part of "AM/PM". Lowercased just in case.
93
+ 'pm' ,
94
+ // Day. Example: "1"
95
+ 'd' ,
96
+ // Day (min two digits). Example: "01"
97
+ 'dd' ,
98
+ // Month (numeric). Example: "1".
99
+ 'm' ,
100
+ // Month (numeric, min two digits). Example: "01". Could also be "Minutes". Weird.
101
+ 'mm' ,
102
+ // Month (shortened month name). Example: "Jan".
103
+ 'mmm' ,
104
+ // Month (full month name). Example: "January".
105
+ 'mmmm' ,
106
+ // Two-digit year. Example: "20".
107
+ 'yy' ,
108
+ // Full year. Example: "2020".
109
+ 'yyyy' ,
110
+
111
+ // I don't have any idea what "e" means.
112
+ // It's used in "built-in" XLSX formats:
113
+ // * 27 '[$-404]e/m/d';
114
+ // * 36 '[$-404]e/m/d';
115
+ // * 50 '[$-404]e/m/d';
116
+ // * 57 '[$-404]e/m/d';
117
+ 'e'
118
+ ] ;
0 commit comments