10
10
'use strict' ;
11
11
12
12
var d3 = require ( 'd3' ) ;
13
- var isNumeric = require ( 'fast-isnumeric' ) ;
14
13
15
14
var logError = require ( './loggers' ) . error ;
16
15
@@ -21,6 +20,11 @@ var ONEHOUR = constants.ONEHOUR;
21
20
var ONEMIN = constants . ONEMIN ;
22
21
var ONESEC = constants . ONESEC ;
23
22
23
+ var DATETIME_REGEXP = / ^ \s * ( - ? \d \d \d \d | \d \d ) ( - ( 0 ? [ 1 - 9 ] | 1 [ 0 1 2 ] ) ( - ( [ 0 - 3 ] ? \d ) ( [ T t ] ( [ 0 1 ] ? \d | 2 [ 0 - 3 ] ) ( : ( [ 0 - 5 ] \d ) ( : ( [ 0 - 5 ] \d ( \. \d + ) ? ) ) ? ( Z | z | [ + \- ] \d \d : ? \d \d ) ? ) ? ) ? ) ? ) ? \s * $ / m;
24
+
25
+ // for 2-digit years, the first year we map them onto
26
+ var YFIRST = new Date ( ) . getFullYear ( ) - 70 ;
27
+
24
28
// is an object a javascript date?
25
29
exports . isJSDate = function ( v ) {
26
30
return typeof v === 'object' && v !== null && typeof v . getTime === 'function' ;
@@ -32,20 +36,33 @@ exports.isJSDate = function(v) {
32
36
var MIN_MS , MAX_MS ;
33
37
34
38
/**
35
- * dateTime2ms - turn a date object or string s of the form
36
- * YYYY-mm-dd HH:MM:SS.sss into milliseconds (relative to 1970-01-01,
37
- * per javascript standard)
38
- * may truncate after any full field, and sss can be any length
39
- * even >3 digits, though javascript dates truncate to milliseconds
40
- * returns BADNUM if it doesn't find a date
39
+ * dateTime2ms - turn a date object or string s into milliseconds
40
+ * (relative to 1970-01-01, per javascript standard)
41
+ *
42
+ * Returns BADNUM if it doesn't find a date
43
+ *
44
+ * strings should have the form:
45
+ *
46
+ * -?YYYY-mm-dd<sep>HH:MM:SS.sss<tzInfo>?
47
+ *
48
+ * <sep>: space (our normal standard) or T or t (ISO-8601)
49
+ * <tzInfo>: Z, z, or [+\-]HH:?MM and we THROW IT AWAY
50
+ * this format comes from https://tools.ietf.org/html/rfc3339#section-5.6
51
+ * but we allow it even with a space as the separator
52
+ *
53
+ * May truncate after any full field, and sss can be any length
54
+ * even >3 digits, though javascript dates truncate to milliseconds,
55
+ * we keep as much as javascript numeric precision can hold, but we only
56
+ * report back up to 100 microsecond precision, because most dates support
57
+ * this precision (close to 1970 support more, very far away support less)
41
58
*
42
59
* Expanded to support negative years to -9999 but you must always
43
60
* give 4 digits, except for 2-digit positive years which we assume are
44
61
* near the present time.
45
62
* Note that we follow ISO 8601:2004: there *is* a year 0, which
46
63
* is 1BC/BCE, and -1===2BC etc.
47
64
*
48
- * 2-digit to 4 -digit year conversion, where to cut off ?
65
+ * Where to cut off 2 -digit years between 1900s and 2000s ?
49
66
* from http://support.microsoft.com/kb/244664:
50
67
* 1930-2029 (the most retro of all...)
51
68
* but in my mac chrome from eg. d=new Date(Date.parse('8/19/50')):
@@ -77,89 +94,31 @@ exports.dateTime2ms = function(s) {
77
94
// otherwise only accept strings and numbers
78
95
if ( typeof s !== 'string' && typeof s !== 'number' ) return BADNUM ;
79
96
80
- var y , m , d , h ;
81
- // split date and time parts
82
- // TODO: we strip leading/trailing whitespace but not other
83
- // characters like we do for numbers - do we want to?
84
- var datetime = String ( s ) . trim ( ) . split ( ' ' ) ;
85
- if ( datetime . length > 2 ) return BADNUM ;
86
-
87
- var p = datetime [ 0 ] . split ( '-' ) ; // date part
88
-
89
- var CE = true ; // common era, ie positive year
90
- if ( p [ 0 ] === '' ) {
91
- // first part is blank: year starts with a minus sign
92
- CE = false ;
93
- p . splice ( 0 , 1 ) ;
94
- }
95
-
96
- var plen = p . length ;
97
- if ( plen > 3 || ( plen !== 3 && datetime [ 1 ] ) || ! plen ) return BADNUM ;
98
-
99
- // year
100
- if ( p [ 0 ] . length === 4 ) y = Number ( p [ 0 ] ) ;
101
- else if ( p [ 0 ] . length === 2 ) {
102
- if ( ! CE ) return BADNUM ;
103
- var yNow = new Date ( ) . getFullYear ( ) ;
104
- y = ( ( Number ( p [ 0 ] ) - yNow + 70 ) % 100 + 200 ) % 100 + yNow - 70 ;
97
+ var match = String ( s ) . match ( DATETIME_REGEXP ) ;
98
+ if ( ! match ) return BADNUM ;
99
+ var y = match [ 1 ] ,
100
+ m = Number ( match [ 3 ] || 1 ) ,
101
+ d = Number ( match [ 5 ] || 1 ) ,
102
+ H = Number ( match [ 7 ] || 0 ) ,
103
+ M = Number ( match [ 9 ] || 0 ) ,
104
+ S = Number ( match [ 11 ] || 0 ) ;
105
+ if ( y . length === 2 ) {
106
+ y = ( Number ( y ) + 2000 - YFIRST ) % 100 + YFIRST ;
105
107
}
106
- else return BADNUM ;
107
- if ( ! isNumeric ( y ) ) return BADNUM ;
108
+ else y = Number ( y ) ;
108
109
109
110
// javascript takes new Date(0..99,m,d) to mean 1900-1999, so
110
111
// to support years 0-99 we need to use setFullYear explicitly
111
- var baseDate = new Date ( 0 , 0 , 1 ) ;
112
- baseDate . setFullYear ( CE ? y : - y ) ;
113
- if ( p . length > 1 ) {
112
+ var date = new Date ( 2000 , m - 1 , d , H , M ) ;
113
+ date . setFullYear ( y ) ;
114
114
115
- // month - may be 1 or 2 digits
116
- m = Number ( p [ 1 ] ) - 1 ; // new Date() uses zero-based months
117
- if ( p [ 1 ] . length > 2 || ! ( m >= 0 && m <= 11 ) ) return BADNUM ;
118
- baseDate . setMonth ( m ) ;
115
+ if ( date . getDate ( ) !== d ) return BADNUM ;
119
116
120
- if ( p . length > 2 ) {
117
+ // does that hour exist in this day? (Daylight time!)
118
+ // (TODO: remove this check when we move to UTC)
119
+ if ( date . getHours ( ) !== H ) return BADNUM ;
121
120
122
- // day - may be 1 or 2 digits
123
- d = Number ( p [ 2 ] ) ;
124
- if ( p [ 2 ] . length > 2 || ! ( d >= 1 && d <= 31 ) ) return BADNUM ;
125
- baseDate . setDate ( d ) ;
126
-
127
- // does that date exist in this month?
128
- if ( baseDate . getDate ( ) !== d ) return BADNUM ;
129
-
130
- if ( datetime [ 1 ] ) {
131
-
132
- p = datetime [ 1 ] . split ( ':' ) ;
133
- if ( p . length > 3 ) return BADNUM ;
134
-
135
- // hour - may be 1 or 2 digits
136
- h = Number ( p [ 0 ] ) ;
137
- if ( p [ 0 ] . length > 2 || ! p [ 0 ] . length || ! ( h >= 0 && h <= 23 ) ) return BADNUM ;
138
- baseDate . setHours ( h ) ;
139
-
140
- // does that hour exist in this day? (Daylight time!)
141
- // (TODO: remove this check when we move to UTC)
142
- if ( baseDate . getHours ( ) !== h ) return BADNUM ;
143
-
144
- if ( p . length > 1 ) {
145
- d = baseDate . getTime ( ) ;
146
-
147
- // minute - must be 2 digits
148
- m = Number ( p [ 1 ] ) ;
149
- if ( p [ 1 ] . length !== 2 || ! ( m >= 0 && m <= 59 ) ) return BADNUM ;
150
- d += ONEMIN * m ;
151
- if ( p . length === 2 ) return d ;
152
-
153
- // second (and milliseconds) - must have 2-digit seconds
154
- if ( p [ 2 ] . split ( '.' ) [ 0 ] . length !== 2 ) return BADNUM ;
155
- s = Number ( p [ 2 ] ) ;
156
- if ( ! ( s >= 0 && s < 60 ) ) return BADNUM ;
157
- return d + s * ONESEC ;
158
- }
159
- }
160
- }
161
- }
162
- return baseDate . getTime ( ) ;
121
+ return date . getTime ( ) + S * ONESEC ;
163
122
} ;
164
123
165
124
MIN_MS = exports . MIN_MS = exports . dateTime2ms ( '-9999' ) ;
0 commit comments