// ASP.NET json date format regex
aspNetJsonRegex = /^\/?Date\((\-?\d+)/i,
- aspNetTimeSpanJsonRegex = /(\-)?(?:(\d*)\.)?(\d+)\:(\d+)\:(\d+)\.?(\d{3})?/,
+ aspNetTimeSpanJsonRegex = /(\-)?(?:(\d*)\.)?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?)?/,
+ // from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html
+ // somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere
+ // isoDurationRegex = /^(-)?(?:P)(?:(\d+(?:[,.]\d+)?)Y)?(?:(\d+(?:[,.]\d+)?)M)?(?:(\d+(?:[,.]\d+)?)W)?(?:(\d+(?:[,.]\d+)?)D)?(T(?:(\d+(?:[,.]\d+)?)H)?(?:(\d+(?:[,.]\d+)?)M)?(?:(\d+(?:[,.]\d+)?)S)?)?$/,
+ isoDurationRegex = /^(-)?P(?:(?:([0-9,.]*)Y)?(?:([0-9,.]*)M)?(?:([0-9,.]*)D)?(?:T(?:([0-9,.]*)H)?(?:([0-9,.]*)M)?(?:([0-9,.]*)S)?)?|([0-9,.]*)W)$/,
+
// format tokens
formattingTokens = /(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|mm?|ss?|SS?S?|X|zz?|ZZ?|.)/g,
localFormattingTokens = /(\[[^\[]*\])|(\\)?(LT|LL?L?L?|l{1,4})/g,
} else {
duration.milliseconds = input;
}
- } else if (matched) {
- sign = (matched[1] === "-") ? -1 : 1;
+ } else if (!!(match = aspNetTimeSpanJsonRegex.exec(input))) {
+ sign = (match[1] === "-") ? -1 : 1;
duration = {
y: 0,
- d: ~~match[2] * sign,
- h: ~~match[3] * sign,
- m: ~~match[4] * sign,
- s: ~~match[5] * sign,
- ms: ~~match[6] * sign
+ d: toInt(matched[2]) * sign,
+ h: toInt(matched[3]) * sign,
+ m: toInt(matched[4]) * sign,
+ s: toInt(matched[5]) * sign,
+ ms: toInt(matched[6]) * sign
};
+ } else if (!!(match = isoDurationRegex.exec(input))) {
+ sign = (match[1] === "-") ? -1 : 1;
+ parseIso = function (inp) {
+ // We'd normally use ~~inp for this, but unfortunately it also
+ // converts floats to ints.
+ // inp may be undefined, so careful calling replace on it.
+ var res = inp && parseFloat(inp.replace(',', '.'));
+ // apply sign while we're at it
+ return (isNaN(res) ? 0 : res) * sign;
+ };
+ duration = {
+ y: parseIso(match[2]),
+ M: parseIso(match[3]),
+ d: parseIso(match[4]),
+ h: parseIso(match[5]),
+ m: parseIso(match[6]),
+ s: parseIso(match[7]),
+ w: parseIso(match[8]),
+ };
}
ret = new Duration(duration);
return this['as' + units.charAt(0).toUpperCase() + units.slice(1) + 's']();
},
- lang : moment.fn.lang
+ lang : moment.fn.lang,
+
+ toIsoString : function () {
+ // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js
+ var years = Math.abs(this.years()),
+ months = Math.abs(this.months()),
+ days = Math.abs(this.days()),
+ hours = Math.abs(this.hours()),
+ minutes = Math.abs(this.minutes()),
+ seconds = Math.abs(this.seconds() + this.milliseconds() / 1000);
+
+ if (!this.asSeconds()) {
- // this is the same as C#'s (Noda) and python (isodate)...
++ // this is the same as C#'s (Noda) and python (isodate)...
+ // but not other JS (goog.date)
+ return 'P0D';
+ }
+
+ return (this.asSeconds() < 0 ? '-' : '') +
+ 'P' +
+ (years ? years + 'Y' : '') +
+ (months ? months + 'M' : '') +
+ (days ? days + 'D' : '') +
+ ((hours || minutes || seconds) ? 'T' : '') +
+ (hours ? hours + 'H' : '') +
+ (minutes ? minutes + 'M' : '') +
+ (seconds ? seconds + 'S' : '');
+ }
- };
+ });
function makeDurationGetter(name) {
moment.duration.fn[name] = function () {