date-format 2.1.0 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- package/lib/index.js +48 -24
- package/package.json +1 -1
- package/test/date_format-test.js +4 -7
- package/test/parse-test.js +105 -62
package/lib/index.js
CHANGED
@@ -30,10 +30,6 @@ function offset(timezoneOffset) {
|
|
30
30
|
return timezoneOffset < 0 ? "+" + h + m : "-" + h + m;
|
31
31
|
}
|
32
32
|
|
33
|
-
function datePart(date, displayUTC, part) {
|
34
|
-
return displayUTC ? date["getUTC" + part]() : date["get" + part]();
|
35
|
-
}
|
36
|
-
|
37
33
|
function asString(format, date) {
|
38
34
|
if (typeof format !== "string") {
|
39
35
|
date = format;
|
@@ -43,20 +39,19 @@ function asString(format, date) {
|
|
43
39
|
date = module.exports.now();
|
44
40
|
}
|
45
41
|
|
46
|
-
|
42
|
+
// Issue # 14 - Per ISO8601 standard, the time string should be local time
|
43
|
+
// with timezone info.
|
44
|
+
// See https://en.wikipedia.org/wiki/ISO_8601 section "Time offsets from UTC"
|
47
45
|
|
48
|
-
var vDay = addZero(
|
49
|
-
var vMonth = addZero(
|
50
|
-
var vYearLong = addZero(
|
46
|
+
var vDay = addZero(date.getDate());
|
47
|
+
var vMonth = addZero(date.getMonth() + 1);
|
48
|
+
var vYearLong = addZero(date.getFullYear());
|
51
49
|
var vYearShort = addZero(vYearLong.substring(2, 4));
|
52
50
|
var vYear = format.indexOf("yyyy") > -1 ? vYearLong : vYearShort;
|
53
|
-
var vHour = addZero(
|
54
|
-
var vMinute = addZero(
|
55
|
-
var vSecond = addZero(
|
56
|
-
var vMillisecond = padWithZeros(
|
57
|
-
datePart(date, displayUTC, "Milliseconds"),
|
58
|
-
3
|
59
|
-
);
|
51
|
+
var vHour = addZero(date.getHours());
|
52
|
+
var vMinute = addZero(date.getMinutes());
|
53
|
+
var vSecond = addZero(date.getSeconds());
|
54
|
+
var vMillisecond = padWithZeros(date.getMilliseconds(), 3);
|
60
55
|
var vTimeZone = offset(date.getTimezoneOffset());
|
61
56
|
var formatted = format
|
62
57
|
.replace(/dd/g, vDay)
|
@@ -70,55 +65,63 @@ function asString(format, date) {
|
|
70
65
|
return formatted;
|
71
66
|
}
|
72
67
|
|
68
|
+
function setDatePart(date, part, value, local) {
|
69
|
+
date['set' + (local ? '' : 'UTC') + part](value);
|
70
|
+
}
|
71
|
+
|
73
72
|
function extractDateParts(pattern, str, missingValuesDate) {
|
73
|
+
// Javascript Date object doesn't support custom timezone. Sets all felds as
|
74
|
+
// GMT based to begin with. If the timezone offset is provided, then adjust
|
75
|
+
// it using provided timezone, otherwise, adjust it with the system timezone.
|
76
|
+
var local = pattern.indexOf('O') < 0;
|
74
77
|
var matchers = [
|
75
78
|
{
|
76
79
|
pattern: /y{1,4}/,
|
77
80
|
regexp: "\\d{1,4}",
|
78
81
|
fn: function(date, value) {
|
79
|
-
date
|
82
|
+
setDatePart(date, 'FullYear', value, local);
|
80
83
|
}
|
81
84
|
},
|
82
85
|
{
|
83
86
|
pattern: /MM/,
|
84
87
|
regexp: "\\d{1,2}",
|
85
88
|
fn: function(date, value) {
|
86
|
-
date
|
89
|
+
setDatePart(date, 'Month', (value - 1), local);
|
87
90
|
}
|
88
91
|
},
|
89
92
|
{
|
90
93
|
pattern: /dd/,
|
91
94
|
regexp: "\\d{1,2}",
|
92
95
|
fn: function(date, value) {
|
93
|
-
date
|
96
|
+
setDatePart(date, 'Date', value, local);
|
94
97
|
}
|
95
98
|
},
|
96
99
|
{
|
97
100
|
pattern: /hh/,
|
98
101
|
regexp: "\\d{1,2}",
|
99
102
|
fn: function(date, value) {
|
100
|
-
date
|
103
|
+
setDatePart(date, 'Hours', value, local);
|
101
104
|
}
|
102
105
|
},
|
103
106
|
{
|
104
107
|
pattern: /mm/,
|
105
108
|
regexp: "\\d\\d",
|
106
109
|
fn: function(date, value) {
|
107
|
-
date
|
110
|
+
setDatePart(date, 'Minutes', value, local);
|
108
111
|
}
|
109
112
|
},
|
110
113
|
{
|
111
114
|
pattern: /ss/,
|
112
115
|
regexp: "\\d\\d",
|
113
116
|
fn: function(date, value) {
|
114
|
-
date
|
117
|
+
setDatePart(date, 'Seconds', value, local);
|
115
118
|
}
|
116
119
|
},
|
117
120
|
{
|
118
121
|
pattern: /SSS/,
|
119
122
|
regexp: "\\d\\d\\d",
|
120
123
|
fn: function(date, value) {
|
121
|
-
date
|
124
|
+
setDatePart(date, 'Milliseconds', value, local);
|
122
125
|
}
|
123
126
|
},
|
124
127
|
{
|
@@ -129,8 +132,28 @@ function extractDateParts(pattern, str, missingValuesDate) {
|
|
129
132
|
value = 0;
|
130
133
|
}
|
131
134
|
var offset = Math.abs(value);
|
132
|
-
var
|
133
|
-
|
135
|
+
var timezoneOffset = (value > 0 ? -1 : 1 ) * ((offset % 100) + Math.floor(offset / 100) * 60);
|
136
|
+
// Per ISO8601 standard: UTC = local time - offset
|
137
|
+
//
|
138
|
+
// For example, 2000-01-01T01:00:00-0700
|
139
|
+
// local time: 2000-01-01T01:00:00
|
140
|
+
// ==> UTC : 2000-01-01T08:00:00 ( 01 - (-7) = 8 )
|
141
|
+
//
|
142
|
+
// To make it even more confusing, the date.getTimezoneOffset() is
|
143
|
+
// opposite sign of offset string in the ISO8601 standard. So if offset
|
144
|
+
// is '-0700' the getTimezoneOffset() would be (+)420. The line above
|
145
|
+
// calculates timezoneOffset to matche Javascript's behavior.
|
146
|
+
//
|
147
|
+
// The date/time of the input is actually the local time, so the date
|
148
|
+
// object that was constructed is actually local time even thought the
|
149
|
+
// UTC setters are used. This means the date object's internal UTC
|
150
|
+
// representation was wrong. It needs to be fixed by substracting the
|
151
|
+
// offset (or adding the offset minutes as they are opposite sign).
|
152
|
+
//
|
153
|
+
// Note: the time zone has to be processed after all other fileds are
|
154
|
+
// set. The result would be incorrect if the offset was calculated
|
155
|
+
// first then overriden by the other filed setters.
|
156
|
+
date.setUTCMinutes(date.getUTCMinutes() + timezoneOffset);
|
134
157
|
}
|
135
158
|
}
|
136
159
|
];
|
@@ -162,6 +185,7 @@ function extractDateParts(pattern, str, missingValuesDate) {
|
|
162
185
|
dateFns.forEach(function(f, i) {
|
163
186
|
f.fn(date, matches[i + 1]);
|
164
187
|
});
|
188
|
+
|
165
189
|
return date;
|
166
190
|
}
|
167
191
|
|
package/package.json
CHANGED
package/test/date_format-test.js
CHANGED
@@ -29,23 +29,21 @@ describe('date_format', function() {
|
|
29
29
|
|
30
30
|
it('should provide a ISO8601 with timezone offset format', function() {
|
31
31
|
var tzDate = createFixedDate();
|
32
|
-
tzDate.setMinutes(tzDate.getMinutes() - tzDate.getTimezoneOffset() - 660);
|
33
32
|
tzDate.getTimezoneOffset = function () {
|
34
33
|
return -660;
|
35
34
|
};
|
36
35
|
|
37
|
-
// when tz offset is in the pattern, the date should be in
|
36
|
+
// when tz offset is in the pattern, the date should be in local time
|
38
37
|
dateFormat.asString(dateFormat.ISO8601_WITH_TZ_OFFSET_FORMAT, tzDate)
|
39
|
-
.should.eql('2010-01-
|
38
|
+
.should.eql('2010-01-11T14:31:30.005+1100');
|
40
39
|
|
41
40
|
tzDate = createFixedDate();
|
42
|
-
tzDate.setMinutes((tzDate.getMinutes() - tzDate.getTimezoneOffset()) + 120);
|
43
41
|
tzDate.getTimezoneOffset = function () {
|
44
42
|
return 120;
|
45
43
|
};
|
46
44
|
|
47
45
|
dateFormat.asString(dateFormat.ISO8601_WITH_TZ_OFFSET_FORMAT, tzDate)
|
48
|
-
.should.eql('2010-01-
|
46
|
+
.should.eql('2010-01-11T14:31:30.005-0200');
|
49
47
|
});
|
50
48
|
|
51
49
|
it('should provide a just-the-time format', function() {
|
@@ -54,11 +52,10 @@ describe('date_format', function() {
|
|
54
52
|
|
55
53
|
it('should provide a custom format', function() {
|
56
54
|
var customDate = createFixedDate();
|
57
|
-
customDate.setMinutes((customDate.getMinutes() - customDate.getTimezoneOffset()) + 120);
|
58
55
|
customDate.getTimezoneOffset = function () {
|
59
56
|
return 120;
|
60
57
|
};
|
61
58
|
|
62
|
-
dateFormat.asString('O.SSS.ss.mm.hh.dd.MM.yy', customDate).should.eql('-0200.005.30.31.
|
59
|
+
dateFormat.asString('O.SSS.ss.mm.hh.dd.MM.yy', customDate).should.eql('-0200.005.30.31.14.11.01.10');
|
63
60
|
});
|
64
61
|
});
|
package/test/parse-test.js
CHANGED
@@ -33,21 +33,19 @@ describe("dateFormat.parse", function() {
|
|
33
33
|
|
34
34
|
it("should return the correct date if the string matches", function() {
|
35
35
|
var testDate = new Date();
|
36
|
-
testDate.
|
37
|
-
testDate.
|
38
|
-
testDate.
|
39
|
-
testDate.
|
40
|
-
testDate.
|
41
|
-
testDate.
|
42
|
-
testDate.
|
43
|
-
testDate.getTimezoneOffset = function() {
|
44
|
-
return 600;
|
45
|
-
};
|
36
|
+
testDate.setUTCFullYear(2018);
|
37
|
+
testDate.setUTCMonth(8);
|
38
|
+
testDate.setUTCDate(13);
|
39
|
+
testDate.setUTCHours(18);
|
40
|
+
testDate.setUTCMinutes(10);
|
41
|
+
testDate.setUTCSeconds(12);
|
42
|
+
testDate.setUTCMilliseconds(392);
|
46
43
|
|
47
44
|
dateFormat
|
48
|
-
.parse(pattern, "2018-09-
|
45
|
+
.parse(pattern, "2018-09-14 04:10:12.392+1000")
|
49
46
|
.getTime()
|
50
|
-
.should.eql(testDate.getTime())
|
47
|
+
.should.eql(testDate.getTime())
|
48
|
+
;
|
51
49
|
});
|
52
50
|
|
53
51
|
it("should throw if the string does not match", function() {
|
@@ -65,7 +63,10 @@ describe("dateFormat.parse", function() {
|
|
65
63
|
return testDate;
|
66
64
|
};
|
67
65
|
|
68
|
-
|
66
|
+
/**
|
67
|
+
* If there's no timezone in the format, then we verify against the local date
|
68
|
+
*/
|
69
|
+
function verifyLocalDate(actual, expected) {
|
69
70
|
actual.getFullYear().should.eql(expected.year || testDate.getFullYear());
|
70
71
|
actual.getMonth().should.eql(expected.month || testDate.getMonth());
|
71
72
|
actual.getDate().should.eql(expected.day || testDate.getDate());
|
@@ -77,19 +78,34 @@ describe("dateFormat.parse", function() {
|
|
77
78
|
.should.eql(expected.milliseconds || testDate.getMilliseconds());
|
78
79
|
}
|
79
80
|
|
81
|
+
/**
|
82
|
+
* If a timezone is specified, let's verify against the UTC time it is supposed to be
|
83
|
+
*/
|
84
|
+
function verifyDate(actual, expected) {
|
85
|
+
actual.getUTCFullYear().should.eql(expected.year || testDate.getUTCFullYear());
|
86
|
+
actual.getUTCMonth().should.eql(expected.month || testDate.getUTCMonth());
|
87
|
+
actual.getUTCDate().should.eql(expected.day || testDate.getUTCDate());
|
88
|
+
actual.getUTCHours().should.eql(expected.hours || testDate.getUTCHours());
|
89
|
+
actual.getUTCMinutes().should.eql(expected.minutes || testDate.getUTCMinutes());
|
90
|
+
actual.getUTCSeconds().should.eql(expected.seconds || testDate.getUTCSeconds());
|
91
|
+
actual
|
92
|
+
.getMilliseconds()
|
93
|
+
.should.eql(expected.milliseconds || testDate.getMilliseconds());
|
94
|
+
}
|
95
|
+
|
80
96
|
it("should return a date with missing values defaulting to current time", function() {
|
81
97
|
var date = dateFormat.parse("yyyy-MM", "2015-09");
|
82
|
-
|
98
|
+
verifyLocalDate(date, { year: 2015, month: 8 });
|
83
99
|
});
|
84
100
|
|
85
101
|
it("should use a passed in date for missing values", function() {
|
86
|
-
var missingValueDate = new Date(2010, 1,
|
102
|
+
var missingValueDate = new Date(2010, 1, 8, 22, 30, 12, 100);
|
87
103
|
var date = dateFormat.parse("yyyy-MM", "2015-09", missingValueDate);
|
88
|
-
|
104
|
+
verifyLocalDate(date, {
|
89
105
|
year: 2015,
|
90
106
|
month: 8,
|
91
|
-
day:
|
92
|
-
hours:
|
107
|
+
day: 8,
|
108
|
+
hours: 22,
|
93
109
|
minutes: 30,
|
94
110
|
seconds: 12,
|
95
111
|
milliseconds: 100
|
@@ -98,80 +114,107 @@ describe("dateFormat.parse", function() {
|
|
98
114
|
|
99
115
|
it("should handle variations on the same pattern", function() {
|
100
116
|
var date = dateFormat.parse("MM-yyyy", "09-2015");
|
101
|
-
|
117
|
+
verifyLocalDate(date, { year: 2015, month: 8 });
|
102
118
|
|
103
119
|
date = dateFormat.parse("yyyy MM", "2015 09");
|
104
|
-
|
120
|
+
verifyLocalDate(date, { year: 2015, month: 8 });
|
105
121
|
|
106
122
|
date = dateFormat.parse("MM, yyyy.", "09, 2015.");
|
107
|
-
|
123
|
+
verifyLocalDate(date, { year: 2015, month: 8 });
|
108
124
|
});
|
109
125
|
|
110
|
-
|
111
|
-
|
112
|
-
|
126
|
+
describe("should match all the date parts", function() {
|
127
|
+
it("works with dd", function() {
|
128
|
+
var date = dateFormat.parse("dd", "21");
|
129
|
+
verifyLocalDate(date, { day: 21 });
|
130
|
+
});
|
113
131
|
|
114
|
-
|
115
|
-
|
132
|
+
it("works with hh", function() {
|
133
|
+
var date = dateFormat.parse("hh", "12");
|
134
|
+
verifyLocalDate(date, { hours: 12 });
|
135
|
+
});
|
116
136
|
|
117
|
-
|
118
|
-
|
137
|
+
it("works with mm", function() {
|
138
|
+
var date = dateFormat.parse("mm", "34");
|
139
|
+
verifyLocalDate(date, { minutes: 34 });
|
140
|
+
});
|
119
141
|
|
120
|
-
|
121
|
-
|
142
|
+
it("works with ss", function() {
|
143
|
+
var date = dateFormat.parse("ss", "59");
|
144
|
+
verifyLocalDate(date, { seconds: 59 });
|
145
|
+
});
|
122
146
|
|
123
|
-
|
124
|
-
|
147
|
+
it("works with ss.SSS", function() {
|
148
|
+
var date = dateFormat.parse("ss.SSS", "23.452");
|
149
|
+
verifyLocalDate(date, { seconds: 23, milliseconds: 452 });
|
150
|
+
});
|
125
151
|
|
126
|
-
|
127
|
-
|
152
|
+
it("works with hh:mm O (+1000)", function() {
|
153
|
+
var date = dateFormat.parse("hh:mm O", "05:23 +1000");
|
154
|
+
verifyDate(date, { hours: 19, minutes: 23 });
|
155
|
+
});
|
128
156
|
|
129
|
-
|
130
|
-
|
157
|
+
it("works with hh:mm O (-200)", function() {
|
158
|
+
var date = dateFormat.parse("hh:mm O", "05:23 -200");
|
159
|
+
verifyDate(date, { hours: 7, minutes: 23 });
|
160
|
+
});
|
131
161
|
|
132
|
-
|
133
|
-
|
162
|
+
it("works with hh:mm O (+0930)", function() {
|
163
|
+
var date = dateFormat.parse("hh:mm O", "05:23 +0930");
|
164
|
+
verifyDate(date, { hours: 19, minutes: 53 });
|
165
|
+
});
|
134
166
|
});
|
135
167
|
});
|
136
168
|
|
137
169
|
describe("with a date formatted by this library", function() {
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
170
|
+
describe("should format and then parse back to the same date", function() {
|
171
|
+
function testDateInitWithUTC() {
|
172
|
+
var td = new Date();
|
173
|
+
td.setUTCFullYear(2018);
|
174
|
+
td.setUTCMonth(8);
|
175
|
+
td.setUTCDate(13);
|
176
|
+
td.setUTCHours(18);
|
177
|
+
td.setUTCMinutes(10);
|
178
|
+
td.setUTCSeconds(12);
|
179
|
+
td.setUTCMilliseconds(392);
|
180
|
+
return td;
|
181
|
+
}
|
182
|
+
|
183
|
+
it("works with ISO8601_WITH_TZ_OFFSET_FORMAT", function() {
|
184
|
+
// For this test case to work, the date object must be initialized with
|
185
|
+
// UTC timezone
|
186
|
+
var td = testDateInitWithUTC();
|
187
|
+
var d = dateFormat(dateFormat.ISO8601_WITH_TZ_OFFSET_FORMAT, td);
|
188
|
+
dateFormat.parse(dateFormat.ISO8601_WITH_TZ_OFFSET_FORMAT, d)
|
189
|
+
.should.eql(td);
|
190
|
+
});
|
154
191
|
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
)
|
160
|
-
|
192
|
+
it("works with ISO8601_FORMAT", function() {
|
193
|
+
var td = new Date();
|
194
|
+
var d = dateFormat(dateFormat.ISO8601_FORMAT, td);
|
195
|
+
var actual = dateFormat.parse(dateFormat.ISO8601_FORMAT, d);
|
196
|
+
actual.should.eql(td);
|
197
|
+
});
|
161
198
|
|
162
|
-
|
199
|
+
it("works with DATETIME_FORMAT", function() {
|
200
|
+
var testDate = new Date();
|
201
|
+
dateFormat
|
163
202
|
.parse(
|
164
203
|
dateFormat.DATETIME_FORMAT,
|
165
204
|
dateFormat(dateFormat.DATETIME_FORMAT, testDate)
|
166
205
|
)
|
167
206
|
.should.eql(testDate);
|
207
|
+
});
|
168
208
|
|
169
|
-
|
209
|
+
it("works with ABSOLUTETIME_FORMAT", function() {
|
210
|
+
var testDate = new Date();
|
211
|
+
dateFormat
|
170
212
|
.parse(
|
171
213
|
dateFormat.ABSOLUTETIME_FORMAT,
|
172
214
|
dateFormat(dateFormat.ABSOLUTETIME_FORMAT, testDate)
|
173
215
|
)
|
174
216
|
.should.eql(testDate);
|
217
|
+
});
|
175
218
|
});
|
176
219
|
});
|
177
220
|
});
|