Skip to content

Commit 11e6cb2

Browse files
committed
feat(modelType): modelType specifies the data type to use when storing data in the model
Accepts any string value, but the following values have special meaning (these values are case sensitive) : * 'Date' stores a Date instance in the model. Will accept Date, moment, milliseconds, and ISO 8601 strings as initial input from the model * 'moment'stores a moment instance in the model. Accepts the same initial values as ```Date``` * 'milliseconds' store the epoch milliseconds (since 1-1-1970) in the model. Accepts the same initial values as ```Date``` -Any other value is considered a format string. -When accepting values from, and saving values to the model, this directive tries to be as flexible as possible. Dates, moments, and milliseconds are all accepted as input no matter what you specify for ```modelType```. However, strings are problematic and often lose timezone information, so use caution when storing strings. -If you must use a string, be aware that the format stored in the model must exactly match the format specified in ```modelType```. For example, the value in the model is ```'2015-12-31'``` then using ```modelType: 'MM-DD-YYYY'``` will cause an exception. -I predict that I will regret adding this feature. closes #241, closes #190, closes #39
1 parent a9d04e8 commit 11e6cb2

File tree

7 files changed

+442
-49
lines changed

7 files changed

+442
-49
lines changed

README.md

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Angular bootstrap date & time picker version: 0.3.15
1+
# Angular bootstrap date & time picker version: 0.4.0
22

33
Native AngularJS datetime picker directive styled by Twitter Bootstrap 3
44

@@ -147,33 +147,33 @@ which is specified in the data-datetimepicker-config attribute.
147147

148148
### startView
149149

150-
String. Default: 'day'
150+
String. Default: ```'day'```
151151

152152
The view that the datetimepicker should show when it is opened.
153153
Accepts values of :
154-
* 'minute' for the minute view
155-
* 'hour' for the hour view
156-
* 'day' for the day view (the default)
157-
* 'month' for the 12-month view
158-
* 'year' for the 10-year overview. Useful for date-of-birth datetimepickers.
154+
* ```'minute'``` for the minute view
155+
* ```'hour'``` for the hour view
156+
* ```'day'``` for the day view (the default)
157+
* ```'month'``` for the 12-month view
158+
* ```'year'``` for the 10-year overview. Useful for date-of-birth datetimepickers.
159159

160160
### minView
161161

162-
String. 'minute'
162+
String. ```'minute'```
163163

164164
The lowest view that the datetimepicker should show.
165165

166166
Accepts the same values as startView.
167167

168168
### minuteStep
169169

170-
Number. Default: 5
170+
Number. Default: ```5```
171171

172172
The increment used to build the hour view. A button is created for each <code>minuteStep</code> minutes.
173173

174174
### configureOn
175175

176-
String. Default: null
176+
String. Default: ```null```
177177

178178
Causes the date/time picker to re-read its configuration when the specified event is received.
179179

@@ -182,13 +182,38 @@ new configuration to be used. You can $broadcast the event to cause this directi
182182

183183
### renderOn
184184

185-
String. Default: null
185+
String. Default: ```null```
186186

187187
Causes the date/time picker to re-render its view when the specified event is received.
188188

189189
For example, if you want to disable any dates or times that are in the past.
190190
You can $broadcast the event at an interval to disable times in the past (or any other time valid dates change).
191191

192+
### modelType
193+
194+
String. Default: ```'Date'```
195+
196+
Specifies the data type to use when storing the selected date in the model.
197+
198+
Accepts any string value, but the following values have special meaning (these values are case sensitive) :
199+
* ```'Date'``` stores a Date instance in the model. Will accept Date, moment, milliseconds, and ISO 8601 strings as initial input from the model
200+
* ```'moment'``` stores a moment instance in the model. Accepts the same initial values as ```Date```
201+
* ```'milliseconds'``` store the epoch milliseconds (since 1-1-1970) in the model. Accepts the same initial values as ```Date```
202+
203+
Any other value is considered a format string.
204+
205+
When accepting values from, and saving values to the model, this directive tries to be as flexible as possible.
206+
Dates, moments, and milliseconds are all accepted as input no matter what you specify for ```modelType```.
207+
However, strings are problematic and often lose timezone information, so use caution when storing strings.
208+
209+
If you must use a string, be aware that the format stored in the model must exactly match the format specified in ```modelType```.
210+
For example, the value in the model is ```'2015-12-31'``` then using ```modelType: 'MM-DD-YYYY'``` will cause an exception.
211+
212+
NOTA BENE: If the only reason you are storing strings is to have it properly formatted when displaying to the user,
213+
please review the documentation on ngModelController $formatters and $parsers. These allow you to store a value in the model
214+
but display it formatted as you like in the view. In other words, stop it! =)
215+
216+
192217
### dropdownSelector
193218

194219
When used within a Bootstrap dropdown and jQuery, the selector specified in dropdownSelector will toggle the dropdown when a date/time is selected.
@@ -275,7 +300,7 @@ moment.locale('en'); // English
275300
moment.locale('zh-cn'); // Simplified chinese
276301
```
277302

278-
# Screenshots
303+
# Screen shots
279304

280305
## Year view
281306

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "angular-bootstrap-datetimepicker",
3-
"version": "0.0.0-managed-by-semantic-release",
3+
"version": "0.4.0",
44
"description": "This directive allows you to add a datetime-picker to your form elements.",
55
"keywords": [
66
"anguar",
@@ -28,7 +28,7 @@
2828
"autoprefixer": "^6.1.0",
2929
"bootstrap": "^3.3.5",
3030
"bower": "latest",
31-
"commitizen": "^2.4.4",
31+
"commitizen": "^2.4.6",
3232
"csscomb": "^3.1.8",
3333
"csslint": "^0.10.0",
3434
"cz-conventional-changelog": "^1.1.4",
@@ -77,4 +77,4 @@
7777
"path": "./node_modules/cz-conventional-changelog"
7878
}
7979
}
80-
}
80+
}

paths.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ var modules = [
88
'node_modules/angular/angular.js',
99
'node_modules/angular-mocks/angular-mocks.js'
1010
];
11-
var bumpFiles = ['package.json', 'bower.json', 'README.md', 'src/js/*.js'];
11+
var bumpFiles = ['package.json', 'bower.json', 'README.md', 'src/js/*.js', 'src/scss/datetimepicker.scss'];
1212
var cssFiles = ['src/css/*.css'];
1313
var demoFiles = ['demo/**/*.js'];
1414
var miscFiles = ['GruntFile.js', 'gulpfile.js', 'karma.conf.js', 'paths.js'];
@@ -19,7 +19,7 @@ var testFiles = ['test/**/*.spec.js'];
1919
module.exports = {
2020
all: modules.concat(sourceFiles).concat(testFiles).concat(demoFiles).concat(cssFiles),
2121
app: sourceFiles,
22-
bump: bumpFiles.concat(scssFiles).concat(cssFiles),
22+
bump: bumpFiles.concat(cssFiles),
2323
css: cssFiles,
2424
lint: miscFiles.concat(sourceFiles).concat(testFiles).concat(miscFiles),
2525
scss: scssFiles,

src/css/datetimepicker.css

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/js/datetimepicker.js

Lines changed: 96 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
/*jslint vars:true */
33

44
/**
5-
* @license angular-bootstrap-datetimepicker version: 0.3.15
5+
* @license angular-bootstrap-datetimepicker version: 0.4.0
66
* Copyright 2015 Knight Rider Consulting, Inc. http://www.knightrider.com
77
* License: MIT
88
*/
@@ -32,6 +32,8 @@
3232
dropdownSelector: null,
3333
minuteStep: 5,
3434
minView: 'minute',
35+
modelType: 'Date',
36+
parseFormat: 'YYYY-MM-DDTHH:mm:ss.SSSZZ',
3537
renderOn: null,
3638
startView: 'day'
3739
})
@@ -67,6 +69,8 @@
6769
'dropdownSelector',
6870
'minuteStep',
6971
'minView',
72+
'modelType',
73+
'parseFormat',
7074
'renderOn',
7175
'startView'
7276
];
@@ -111,6 +115,16 @@
111115
if (configuration.renderOn !== null && configuration.renderOn.length < 1) {
112116
throw ('renderOn must not be an empty string');
113117
}
118+
if (configuration.modelType !== null && !angular.isString(configuration.modelType)) {
119+
throw ('modelType must be a string');
120+
}
121+
if (configuration.modelType !== null && configuration.modelType.length < 1) {
122+
throw ('modelType must not be an empty string');
123+
}
124+
if (configuration.modelType !== 'Date' && configuration.modelType !== 'moment' && configuration.modelType !== 'milliseconds') {
125+
// modelType contains string format, overriding parseFormat with modelType
126+
configuration.parseFormat = configuration.modelType;
127+
}
114128
if (configuration.dropdownSelector !== null && !angular.isString(configuration.dropdownSelector)) {
115129
throw ('dropdownSelector must be a string');
116130
}
@@ -182,21 +196,64 @@
182196
var configuration = configure();
183197

184198

185-
var startOfDecade = function startOfDecade(unixDate) {
186-
var startYear = (parseInt(moment.utc(unixDate).year() / 10, 10) * 10);
187-
return moment.utc(unixDate).year(startYear).startOf('year');
199+
var startOfDecade = function startOfDecade(milliseconds) {
200+
var startYear = (parseInt(moment.utc(milliseconds).year() / 10, 10) * 10);
201+
return moment.utc(milliseconds).year(startYear).startOf('year');
202+
};
203+
204+
var formatValue = function formatValue(timeValue, formatString) {
205+
if (timeValue) {
206+
return getMoment(timeValue).format(formatString);
207+
} else {
208+
return '';
209+
}
210+
};
211+
212+
/**
213+
* Converts a time value into a moment.
214+
*
215+
* This function is now necessary because moment logs a warning when parsing a string without a format.
216+
* @param modelValue
217+
* a time value in any of the supported formats (Date, moment, milliseconds, and string)
218+
* @returns {moment}
219+
* representing the specified time value.
220+
*/
221+
222+
var getMoment = function getMoment(modelValue) {
223+
return moment(modelValue, angular.isString(modelValue) ? configuration.parseFormat : undefined);
224+
};
225+
226+
/**
227+
* Converts a time value to UCT/GMT time.
228+
* @param modelValue
229+
* a time value in any of the supported formats (Date, moment, milliseconds, and string)
230+
* @returns {number}
231+
* number of milliseconds since 1/1/1970
232+
*/
233+
234+
var getUTCTime = function getUTCTime(modelValue) {
235+
var tempDate = new Date();
236+
if (modelValue) {
237+
var tempMoment = getMoment(modelValue);
238+
if (tempMoment.isValid()) {
239+
tempDate = tempMoment.toDate();
240+
} else {
241+
throw 'Invalid date: ' + modelValue;
242+
}
243+
}
244+
return tempDate.getTime() - (tempDate.getTimezoneOffset() * 60000);
188245
};
189246

190247
var dataFactory = {
191-
year: function year(unixDate) {
192-
var selectedDate = moment.utc(unixDate).startOf('year');
248+
year: function year(milliseconds) {
249+
var selectedDate = moment.utc(milliseconds).startOf('year');
193250
// View starts one year before the decade starts and ends one year after the decade ends
194251
// i.e. passing in a date of 1/1/2013 will give a range of 2009 to 2020
195252
// Truncate the last digit from the current year and subtract 1 to get the start of the decade
196253
var startDecade = (parseInt(selectedDate.year() / 10, 10) * 10);
197-
var startDate = moment.utc(startOfDecade(unixDate)).subtract(1, 'year').startOf('year');
254+
var startDate = moment.utc(startOfDecade(milliseconds)).subtract(1, 'year').startOf('year');
198255

199-
var activeYear = ngModelController.$modelValue ? moment(ngModelController.$modelValue).year() : 0;
256+
var activeYear = formatValue(ngModelController.$modelValue, 'YYYY');
200257

201258
var result = {
202259
'currentView': 'year',
@@ -217,7 +274,7 @@
217274
'display': yearMoment.format('YYYY'),
218275
'past': yearMoment.year() < startDecade,
219276
'future': yearMoment.year() > startDecade + 9,
220-
'active': yearMoment.year() === activeYear
277+
'active': yearMoment.format('YYYY') === activeYear
221278
};
222279

223280
result.dates.push(new DateObject(dateValue));
@@ -226,11 +283,11 @@
226283
return result;
227284
},
228285

229-
month: function month(unixDate) {
286+
month: function month(milliseconds) {
230287

231-
var startDate = moment.utc(unixDate).startOf('year');
232-
var previousViewDate = startOfDecade(unixDate);
233-
var activeDate = ngModelController.$modelValue ? moment(ngModelController.$modelValue).format('YYYY-MMM') : 0;
288+
var startDate = moment.utc(milliseconds).startOf('year');
289+
var previousViewDate = startOfDecade(milliseconds);
290+
var activeDate = formatValue(ngModelController.$modelValue, 'YYYY-MMM');
234291

235292
var result = {
236293
'previousView': 'year',
@@ -259,16 +316,16 @@
259316
return result;
260317
},
261318

262-
day: function day(unixDate) {
319+
day: function day(milliseconds) {
263320

264-
var selectedDate = moment.utc(unixDate);
321+
var selectedDate = moment.utc(milliseconds);
265322
var startOfMonth = moment.utc(selectedDate).startOf('month');
266323
var previousViewDate = moment.utc(selectedDate).startOf('year');
267324
var endOfMonth = moment.utc(selectedDate).endOf('month');
268325

269326
var startDate = moment.utc(startOfMonth).subtract(Math.abs(startOfMonth.weekday()), 'days');
270327

271-
var activeDate = ngModelController.$modelValue ? moment(ngModelController.$modelValue).format('YYYY-MMM-DD') : '';
328+
var activeDate = formatValue(ngModelController.$modelValue, 'YYYY-MMM-DD');
272329

273330
var result = {
274331
'previousView': 'month',
@@ -308,11 +365,11 @@
308365
return result;
309366
},
310367

311-
hour: function hour(unixDate) {
312-
var selectedDate = moment.utc(unixDate).startOf('day');
368+
hour: function hour(milliseconds) {
369+
var selectedDate = moment.utc(milliseconds).startOf('day');
313370
var previousViewDate = moment.utc(selectedDate).startOf('month');
314371

315-
var activeFormat = ngModelController.$modelValue ? moment(ngModelController.$modelValue).format('YYYY-MM-DD H') : '';
372+
var activeFormat = formatValue(ngModelController.$modelValue, 'YYYY-MM-DD H');
316373

317374
var result = {
318375
'previousView': 'day',
@@ -341,10 +398,10 @@
341398
return result;
342399
},
343400

344-
minute: function minute(unixDate) {
345-
var selectedDate = moment.utc(unixDate).startOf('hour');
401+
minute: function minute(milliseconds) {
402+
var selectedDate = moment.utc(milliseconds).startOf('hour');
346403
var previousViewDate = moment.utc(selectedDate).startOf('day');
347-
var activeFormat = ngModelController.$modelValue ? moment(ngModelController.$modelValue).format('YYYY-MM-DD H:mm') : '';
404+
var activeFormat = formatValue(ngModelController.$modelValue, 'YYYY-MM-DD H:mm');
348405

349406
var result = {
350407
'previousView': 'hour',
@@ -375,10 +432,24 @@
375432
return result;
376433
},
377434

378-
setTime: function setTime(unixDate) {
379-
var tempDate = new Date(unixDate);
435+
setTime: function setTime(milliseconds) {
436+
var tempDate = new Date(milliseconds);
380437
var newDate = new Date(tempDate.getUTCFullYear(), tempDate.getUTCMonth(), tempDate.getUTCDate(), tempDate.getUTCHours(), tempDate.getUTCMinutes(), tempDate.getUTCSeconds(), tempDate.getUTCMilliseconds());
381438

439+
switch (configuration.modelType) {
440+
case 'Date':
441+
// No additional work needed
442+
break;
443+
case 'moment':
444+
newDate = moment(newDate);
445+
break;
446+
case 'milliseconds':
447+
newDate = milliseconds;
448+
break;
449+
default: // It is assumed that the modelType is a formatting string.
450+
newDate = moment(newDate).format(configuration.modelType);
451+
}
452+
382453
var oldDate = ngModelController.$modelValue;
383454
ngModelController.$setViewValue(newDate);
384455

@@ -388,15 +459,10 @@
388459

389460
scope.onSetTime({newDate: newDate, oldDate: oldDate});
390461

391-
return dataFactory[configuration.startView](unixDate);
462+
return dataFactory[configuration.startView](milliseconds);
392463
}
393464
};
394465

395-
var getUTCTime = function getUTCTime(modelValue) {
396-
var tempDate = (modelValue ? moment(modelValue).toDate() : new Date());
397-
return tempDate.getTime() - (tempDate.getTimezoneOffset() * 60000);
398-
};
399-
400466
scope.changeView = function changeView(viewName, dateObject, event) {
401467
if (event) {
402468
event.stopPropagation();

src/scss/datetimepicker.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// scss-lint:disable Comment
22
/**
3-
* @license angular-bootstrap-datetimepicker version: 0.3.14
3+
* @license angular-bootstrap-datetimepicker version: 0.4.0
44
* Copyright 2015 Knight Rider Consulting, Inc. http://www.knightrider.com
55
* License: MIT
66
*/

0 commit comments

Comments
 (0)