date-format 2.1.0 → 4.0.3

Sign up to get free protection for your applications and to get access to all the features.
package/README.md CHANGED
@@ -15,18 +15,28 @@ Formatting dates as strings
15
15
 
16
16
  ```javascript
17
17
  var format = require('date-format');
18
- format.asString(); //defaults to ISO8601 format and current date.
19
- format.asString(new Date()); //defaults to ISO8601 format
20
- format.asString('hh:mm:ss.SSS', new Date()); //just the time
18
+ format.asString(); // defaults to ISO8601 format and current date
19
+ format.asString(new Date()); // defaults to ISO8601 format
20
+ format.asString('hh:mm:ss.SSS', new Date()); // just the time
21
+ format.asString(format.ISO8601_WITH_TZ_OFFSET_FORMAT, new Date()); // in ISO8601 with timezone
21
22
  ```
22
23
 
23
24
  or
24
25
 
25
26
  ```javascript
26
27
  var format = require('date-format');
27
- format(); //defaults to ISO8601 format and current date.
28
- format(new Date());
29
- format('hh:mm:ss.SSS', new Date());
28
+ format(); // defaults to ISO8601 format and current date
29
+ format(new Date()); // defaults to ISO8601 format
30
+ format('hh:mm:ss.SSS', new Date()); // just the time
31
+ format(format.ISO8601_WITH_TZ_OFFSET_FORMAT, new Date()); // in ISO8601 with timezone
32
+ ```
33
+
34
+ **output:**
35
+ ```javascript
36
+ 2017-03-14T14:10:20.391
37
+ 2017-03-14T14:10:20.391
38
+ 14:10:20.391
39
+ 2017-03-14T14:10:20.391+11:00
30
40
  ```
31
41
 
32
42
  Format string can be anything, but the following letters will be replaced (and leading zeroes added if necessary):
@@ -38,11 +48,11 @@ Format string can be anything, but the following letters will be replaced (and l
38
48
  * mm - `date.getMinutes()`
39
49
  * ss - `date.getSeconds()`
40
50
  * SSS - `date.getMilliseconds()`
41
- * O - timezone offset in +hm format (note that time will be in UTC if displaying offset)
51
+ * O - timezone offset in ±hh:mm format (note that time will still be local if displaying offset)
42
52
 
43
53
  Built-in formats:
44
54
  * `format.ISO8601_FORMAT` - `2017-03-14T14:10:20.391` (local time used)
45
- * `format.ISO8601_WITH_TZ_OFFSET_FORMAT` - `2017-03-14T03:10:20.391+1100` (UTC + TZ used)
55
+ * `format.ISO8601_WITH_TZ_OFFSET_FORMAT` - `2017-03-14T14:10:20.391+11:00` (local + TZ used)
46
56
  * `format.DATETIME_FORMAT` - `14 03 2017 14:10:20.391` (local time used)
47
57
  * `format.ABSOLUTETIME_FORMAT` - `14:10:20.391` (local time used)
48
58
 
@@ -54,5 +64,8 @@ The date format library has limited ability to parse strings into dates. It can
54
64
  var format = require('date-format');
55
65
  // pass in the format of the string as first argument
56
66
  format.parse(format.ISO8601_FORMAT, '2017-03-14T14:10:20.391');
67
+ format.parse(format.ISO8601_WITH_TZ_OFFSET_FORMAT, '2017-03-14T14:10:20.391+1100');
68
+ format.parse(format.ISO8601_WITH_TZ_OFFSET_FORMAT, '2017-03-14T14:10:20.391+11:00');
69
+ format.parse(format.ISO8601_WITH_TZ_OFFSET_FORMAT, '2017-03-14T03:10:20.391Z');
57
70
  // returns Date
58
71
  ```
package/lib/index.js CHANGED
@@ -21,17 +21,9 @@ function offset(timezoneOffset) {
21
21
  var os = Math.abs(timezoneOffset);
22
22
  var h = String(Math.floor(os / 60));
23
23
  var m = String(os % 60);
24
- if (h.length === 1) {
25
- h = "0" + h;
26
- }
27
- if (m.length === 1) {
28
- m = "0" + m;
29
- }
30
- return timezoneOffset < 0 ? "+" + h + m : "-" + h + m;
31
- }
32
-
33
- function datePart(date, displayUTC, part) {
34
- return displayUTC ? date["getUTC" + part]() : date["get" + part]();
24
+ h = ("0" + h).slice(-2);
25
+ m = ("0" + m).slice(-2);
26
+ return timezoneOffset === 0 ? "Z" : (timezoneOffset < 0 ? "+" : "-") + h + ":" + m;
35
27
  }
36
28
 
37
29
  function asString(format, date) {
@@ -43,20 +35,19 @@ function asString(format, date) {
43
35
  date = module.exports.now();
44
36
  }
45
37
 
46
- var displayUTC = format.indexOf("O") > -1;
38
+ // Issue # 14 - Per ISO8601 standard, the time string should be local time
39
+ // with timezone info.
40
+ // See https://en.wikipedia.org/wiki/ISO_8601 section "Time offsets from UTC"
47
41
 
48
- var vDay = addZero(datePart(date, displayUTC, "Date"));
49
- var vMonth = addZero(datePart(date, displayUTC, "Month") + 1);
50
- var vYearLong = addZero(datePart(date, displayUTC, "FullYear"));
42
+ var vDay = addZero(date.getDate());
43
+ var vMonth = addZero(date.getMonth() + 1);
44
+ var vYearLong = addZero(date.getFullYear());
51
45
  var vYearShort = addZero(vYearLong.substring(2, 4));
52
46
  var vYear = format.indexOf("yyyy") > -1 ? vYearLong : vYearShort;
53
- var vHour = addZero(datePart(date, displayUTC, "Hours"));
54
- var vMinute = addZero(datePart(date, displayUTC, "Minutes"));
55
- var vSecond = addZero(datePart(date, displayUTC, "Seconds"));
56
- var vMillisecond = padWithZeros(
57
- datePart(date, displayUTC, "Milliseconds"),
58
- 3
59
- );
47
+ var vHour = addZero(date.getHours());
48
+ var vMinute = addZero(date.getMinutes());
49
+ var vSecond = addZero(date.getSeconds());
50
+ var vMillisecond = padWithZeros(date.getMilliseconds(), 3);
60
51
  var vTimeZone = offset(date.getTimezoneOffset());
61
52
  var formatted = format
62
53
  .replace(/dd/g, vDay)
@@ -70,67 +61,98 @@ function asString(format, date) {
70
61
  return formatted;
71
62
  }
72
63
 
64
+ function setDatePart(date, part, value, local) {
65
+ date['set' + (local ? '' : 'UTC') + part](value);
66
+ }
67
+
73
68
  function extractDateParts(pattern, str, missingValuesDate) {
69
+ // Javascript Date object doesn't support custom timezone. Sets all felds as
70
+ // GMT based to begin with. If the timezone offset is provided, then adjust
71
+ // it using provided timezone, otherwise, adjust it with the system timezone.
72
+ var local = pattern.indexOf('O') < 0;
74
73
  var matchers = [
75
74
  {
76
75
  pattern: /y{1,4}/,
77
76
  regexp: "\\d{1,4}",
78
77
  fn: function(date, value) {
79
- date.setFullYear(value);
78
+ setDatePart(date, 'FullYear', value, local);
80
79
  }
81
80
  },
82
81
  {
83
82
  pattern: /MM/,
84
83
  regexp: "\\d{1,2}",
85
84
  fn: function(date, value) {
86
- date.setMonth(value - 1);
85
+ setDatePart(date, 'Month', (value - 1), local);
87
86
  }
88
87
  },
89
88
  {
90
89
  pattern: /dd/,
91
90
  regexp: "\\d{1,2}",
92
91
  fn: function(date, value) {
93
- date.setDate(value);
92
+ setDatePart(date, 'Date', value, local);
94
93
  }
95
94
  },
96
95
  {
97
96
  pattern: /hh/,
98
97
  regexp: "\\d{1,2}",
99
98
  fn: function(date, value) {
100
- date.setHours(value);
99
+ setDatePart(date, 'Hours', value, local);
101
100
  }
102
101
  },
103
102
  {
104
103
  pattern: /mm/,
105
104
  regexp: "\\d\\d",
106
105
  fn: function(date, value) {
107
- date.setMinutes(value);
106
+ setDatePart(date, 'Minutes', value, local);
108
107
  }
109
108
  },
110
109
  {
111
110
  pattern: /ss/,
112
111
  regexp: "\\d\\d",
113
112
  fn: function(date, value) {
114
- date.setSeconds(value);
113
+ setDatePart(date, 'Seconds', value, local);
115
114
  }
116
115
  },
117
116
  {
118
117
  pattern: /SSS/,
119
118
  regexp: "\\d\\d\\d",
120
119
  fn: function(date, value) {
121
- date.setMilliseconds(value);
120
+ setDatePart(date, 'Milliseconds', value, local);
122
121
  }
123
122
  },
124
123
  {
125
124
  pattern: /O/,
126
- regexp: "[+-]\\d{3,4}|Z",
125
+ regexp: "[+-]\\d{1,2}:?\\d{2}?|Z",
127
126
  fn: function(date, value) {
128
127
  if (value === "Z") {
129
128
  value = 0;
130
129
  }
130
+ else {
131
+ value = value.replace(":", "");
132
+ }
131
133
  var offset = Math.abs(value);
132
- var minutes = (offset % 100) + Math.floor(offset / 100) * 60;
133
- date.setMinutes(date.getMinutes() + (value > 0 ? minutes : -minutes));
134
+ var timezoneOffset = (value > 0 ? -1 : 1 ) * ((offset % 100) + Math.floor(offset / 100) * 60);
135
+ // Per ISO8601 standard: UTC = local time - offset
136
+ //
137
+ // For example, 2000-01-01T01:00:00-0700
138
+ // local time: 2000-01-01T01:00:00
139
+ // ==> UTC : 2000-01-01T08:00:00 ( 01 - (-7) = 8 )
140
+ //
141
+ // To make it even more confusing, the date.getTimezoneOffset() is
142
+ // opposite sign of offset string in the ISO8601 standard. So if offset
143
+ // is '-0700' the getTimezoneOffset() would be (+)420. The line above
144
+ // calculates timezoneOffset to matche Javascript's behavior.
145
+ //
146
+ // The date/time of the input is actually the local time, so the date
147
+ // object that was constructed is actually local time even thought the
148
+ // UTC setters are used. This means the date object's internal UTC
149
+ // representation was wrong. It needs to be fixed by substracting the
150
+ // offset (or adding the offset minutes as they are opposite sign).
151
+ //
152
+ // Note: the time zone has to be processed after all other fileds are
153
+ // set. The result would be incorrect if the offset was calculated
154
+ // first then overriden by the other filed setters.
155
+ date.setUTCMinutes(date.getUTCMinutes() + timezoneOffset);
134
156
  }
135
157
  }
136
158
  ];
@@ -162,6 +184,7 @@ function extractDateParts(pattern, str, missingValuesDate) {
162
184
  dateFns.forEach(function(f, i) {
163
185
  f.fn(date, matches[i + 1]);
164
186
  });
187
+
165
188
  return date;
166
189
  }
167
190
 
@@ -181,6 +204,7 @@ function parse(pattern, str, missingValuesDate) {
181
204
  /**
182
205
  * Used for testing - replace this function with a fixed date.
183
206
  */
207
+ // istanbul ignore next
184
208
  function now() {
185
209
  return new Date();
186
210
  }
package/package.json CHANGED
@@ -1,8 +1,11 @@
1
1
  {
2
2
  "name": "date-format",
3
- "version": "2.1.0",
3
+ "version": "4.0.3",
4
4
  "description": "Formatting Date objects as strings since 2013",
5
5
  "main": "lib/index.js",
6
+ "files": [
7
+ "lib"
8
+ ],
6
9
  "repository": {
7
10
  "type": "git",
8
11
  "url": "https://github.com/nomiddlename/date-format.git"
@@ -13,7 +16,7 @@
13
16
  "scripts": {
14
17
  "lint": "eslint lib/* test/*",
15
18
  "pretest": "eslint lib/* test/*",
16
- "test": "mocha"
19
+ "test": "nyc --check-coverage mocha"
17
20
  },
18
21
  "keywords": [
19
22
  "date",
@@ -25,9 +28,18 @@
25
28
  "readmeFilename": "README.md",
26
29
  "gitHead": "bf59015ab6c9e86454b179374f29debbdb403522",
27
30
  "devDependencies": {
28
- "eslint": "^5.16.0",
29
- "eslint-plugin-mocha": "^5.3.0",
30
- "mocha": "^5.2.0",
31
+ "eslint": "^8.7.0",
32
+ "eslint-plugin-mocha": "^10.0.3",
33
+ "mocha": "^9.1.4",
34
+ "nyc": "^15.1.0",
31
35
  "should": "^13.2.3"
36
+ },
37
+ "nyc": {
38
+ "include": [
39
+ "lib/**"
40
+ ],
41
+ "branches": 100,
42
+ "lines": 100,
43
+ "functions": 100
32
44
  }
33
45
  }
package/.eslintrc DELETED
@@ -1,12 +0,0 @@
1
- {
2
- "extends": [
3
- "eslint:recommended"
4
- ],
5
- "env": {
6
- "node": true,
7
- "mocha": true
8
- },
9
- "plugins": [
10
- "mocha"
11
- ]
12
- }
package/.travis.yml DELETED
@@ -1,6 +0,0 @@
1
- language: node_js
2
- sudo: false
3
- node_js:
4
- - "10"
5
- - "8"
6
- - "6"
@@ -1,64 +0,0 @@
1
- 'use strict';
2
-
3
- require('should');
4
-
5
- var dateFormat = require('../lib');
6
-
7
- function createFixedDate() {
8
- return new Date(2010, 0, 11, 14, 31, 30, 5);
9
- }
10
-
11
- describe('date_format', function() {
12
- var date = createFixedDate();
13
-
14
- it('should default to now when a date is not provided', function() {
15
- dateFormat.asString(dateFormat.DATETIME_FORMAT).should.not.be.empty();
16
- });
17
-
18
- it('should be usable directly without calling asString', function() {
19
- dateFormat(dateFormat.DATETIME_FORMAT, date).should.eql('11 01 2010 14:31:30.005');
20
- });
21
-
22
- it('should format a date as string using a pattern', function() {
23
- dateFormat.asString(dateFormat.DATETIME_FORMAT, date).should.eql('11 01 2010 14:31:30.005');
24
- });
25
-
26
- it('should default to the ISO8601 format', function() {
27
- dateFormat.asString(date).should.eql('2010-01-11T14:31:30.005');
28
- });
29
-
30
- it('should provide a ISO8601 with timezone offset format', function() {
31
- var tzDate = createFixedDate();
32
- tzDate.setMinutes(tzDate.getMinutes() - tzDate.getTimezoneOffset() - 660);
33
- tzDate.getTimezoneOffset = function () {
34
- return -660;
35
- };
36
-
37
- // when tz offset is in the pattern, the date should be in UTC
38
- dateFormat.asString(dateFormat.ISO8601_WITH_TZ_OFFSET_FORMAT, tzDate)
39
- .should.eql('2010-01-11T03:31:30.005+1100');
40
-
41
- tzDate = createFixedDate();
42
- tzDate.setMinutes((tzDate.getMinutes() - tzDate.getTimezoneOffset()) + 120);
43
- tzDate.getTimezoneOffset = function () {
44
- return 120;
45
- };
46
-
47
- dateFormat.asString(dateFormat.ISO8601_WITH_TZ_OFFSET_FORMAT, tzDate)
48
- .should.eql('2010-01-11T16:31:30.005-0200');
49
- });
50
-
51
- it('should provide a just-the-time format', function() {
52
- dateFormat.asString(dateFormat.ABSOLUTETIME_FORMAT, date).should.eql('14:31:30.005');
53
- });
54
-
55
- it('should provide a custom format', function() {
56
- var customDate = createFixedDate();
57
- customDate.setMinutes((customDate.getMinutes() - customDate.getTimezoneOffset()) + 120);
58
- customDate.getTimezoneOffset = function () {
59
- return 120;
60
- };
61
-
62
- dateFormat.asString('O.SSS.ss.mm.hh.dd.MM.yy', customDate).should.eql('-0200.005.30.31.16.11.01.10');
63
- });
64
- });
@@ -1,177 +0,0 @@
1
- "use strict";
2
-
3
- require("should");
4
- var dateFormat = require("../lib");
5
-
6
- describe("dateFormat.parse", function() {
7
- it("should require a pattern", function() {
8
- (function() {
9
- dateFormat.parse();
10
- }.should.throw(/pattern must be supplied/));
11
- (function() {
12
- dateFormat.parse(null);
13
- }.should.throw(/pattern must be supplied/));
14
- (function() {
15
- dateFormat.parse("");
16
- }.should.throw(/pattern must be supplied/));
17
- });
18
-
19
- describe("with a pattern that has no replacements", function() {
20
- it("should return a new date when the string matches", function() {
21
- dateFormat.parse("cheese", "cheese").should.be.a.Date();
22
- });
23
-
24
- it("should throw if the string does not match", function() {
25
- (function() {
26
- dateFormat.parse("cheese", "biscuits");
27
- }.should.throw(/String 'biscuits' could not be parsed as 'cheese'/));
28
- });
29
- });
30
-
31
- describe("with a full pattern", function() {
32
- var pattern = "yyyy-MM-dd hh:mm:ss.SSSO";
33
-
34
- it("should return the correct date if the string matches", function() {
35
- var testDate = new Date();
36
- testDate.setFullYear(2018);
37
- testDate.setMonth(8);
38
- testDate.setDate(13);
39
- testDate.setHours(18);
40
- testDate.setMinutes(10);
41
- testDate.setSeconds(12);
42
- testDate.setMilliseconds(392);
43
- testDate.getTimezoneOffset = function() {
44
- return 600;
45
- };
46
-
47
- dateFormat
48
- .parse(pattern, "2018-09-13 08:10:12.392+1000")
49
- .getTime()
50
- .should.eql(testDate.getTime());
51
- });
52
-
53
- it("should throw if the string does not match", function() {
54
- (function() {
55
- dateFormat.parse(pattern, "biscuits");
56
- }.should.throw(
57
- /String 'biscuits' could not be parsed as 'yyyy-MM-dd hh:mm:ss.SSSO'/
58
- ));
59
- });
60
- });
61
-
62
- describe("with a partial pattern", function() {
63
- var testDate = new Date();
64
- dateFormat.now = function() {
65
- return testDate;
66
- };
67
-
68
- function verifyDate(actual, expected) {
69
- actual.getFullYear().should.eql(expected.year || testDate.getFullYear());
70
- actual.getMonth().should.eql(expected.month || testDate.getMonth());
71
- actual.getDate().should.eql(expected.day || testDate.getDate());
72
- actual.getHours().should.eql(expected.hours || testDate.getHours());
73
- actual.getMinutes().should.eql(expected.minutes || testDate.getMinutes());
74
- actual.getSeconds().should.eql(expected.seconds || testDate.getSeconds());
75
- actual
76
- .getMilliseconds()
77
- .should.eql(expected.milliseconds || testDate.getMilliseconds());
78
- }
79
-
80
- it("should return a date with missing values defaulting to current time", function() {
81
- var date = dateFormat.parse("yyyy-MM", "2015-09");
82
- verifyDate(date, { year: 2015, month: 8 });
83
- });
84
-
85
- it("should use a passed in date for missing values", function() {
86
- var missingValueDate = new Date(2010, 1, 11, 10, 30, 12, 100);
87
- var date = dateFormat.parse("yyyy-MM", "2015-09", missingValueDate);
88
- verifyDate(date, {
89
- year: 2015,
90
- month: 8,
91
- day: 11,
92
- hours: 10,
93
- minutes: 30,
94
- seconds: 12,
95
- milliseconds: 100
96
- });
97
- });
98
-
99
- it("should handle variations on the same pattern", function() {
100
- var date = dateFormat.parse("MM-yyyy", "09-2015");
101
- verifyDate(date, { year: 2015, month: 8 });
102
-
103
- date = dateFormat.parse("yyyy MM", "2015 09");
104
- verifyDate(date, { year: 2015, month: 8 });
105
-
106
- date = dateFormat.parse("MM, yyyy.", "09, 2015.");
107
- verifyDate(date, { year: 2015, month: 8 });
108
- });
109
-
110
- it("should match all the date parts", function() {
111
- var date = dateFormat.parse("dd", "21");
112
- verifyDate(date, { day: 21 });
113
-
114
- date = dateFormat.parse("hh", "12");
115
- verifyDate(date, { hours: 12 });
116
-
117
- date = dateFormat.parse("mm", "34");
118
- verifyDate(date, { minutes: 34 });
119
-
120
- date = dateFormat.parse("ss", "59");
121
- verifyDate(date, { seconds: 59 });
122
-
123
- date = dateFormat.parse("ss.SSS", "23.452");
124
- verifyDate(date, { seconds: 23, milliseconds: 452 });
125
-
126
- date = dateFormat.parse("hh:mm O", "05:23 +1000");
127
- verifyDate(date, { hours: 15, minutes: 23 });
128
-
129
- date = dateFormat.parse("hh:mm O", "05:23 -200");
130
- verifyDate(date, { hours: 3, minutes: 23 });
131
-
132
- date = dateFormat.parse("hh:mm O", "05:23 +0930");
133
- verifyDate(date, { hours: 14, minutes: 53 });
134
- });
135
- });
136
-
137
- describe("with a date formatted by this library", function() {
138
- var testDate = new Date();
139
- testDate.setUTCFullYear(2018);
140
- testDate.setUTCMonth(8);
141
- testDate.setUTCDate(13);
142
- testDate.setUTCHours(18);
143
- testDate.setUTCMinutes(10);
144
- testDate.setUTCSeconds(12);
145
- testDate.setUTCMilliseconds(392);
146
-
147
- it("should format and then parse back to the same date", function() {
148
- dateFormat
149
- .parse(
150
- dateFormat.ISO8601_WITH_TZ_OFFSET_FORMAT,
151
- dateFormat(dateFormat.ISO8601_WITH_TZ_OFFSET_FORMAT, testDate)
152
- )
153
- .should.eql(testDate);
154
-
155
- dateFormat
156
- .parse(
157
- dateFormat.ISO8601_FORMAT,
158
- dateFormat(dateFormat.ISO8601_FORMAT, testDate)
159
- )
160
- .should.eql(testDate);
161
-
162
- dateFormat
163
- .parse(
164
- dateFormat.DATETIME_FORMAT,
165
- dateFormat(dateFormat.DATETIME_FORMAT, testDate)
166
- )
167
- .should.eql(testDate);
168
-
169
- dateFormat
170
- .parse(
171
- dateFormat.ABSOLUTETIME_FORMAT,
172
- dateFormat(dateFormat.ABSOLUTETIME_FORMAT, testDate)
173
- )
174
- .should.eql(testDate);
175
- });
176
- });
177
- });