create.js (4335B)
1import { Duration, isDuration } from './constructor'; 2import isNumber from '../utils/is-number'; 3import toInt from '../utils/to-int'; 4import absRound from '../utils/abs-round'; 5import hasOwnProp from '../utils/has-own-prop'; 6import { DATE, HOUR, MINUTE, SECOND, MILLISECOND } from '../units/constants'; 7import { cloneWithOffset } from '../units/offset'; 8import { createLocal } from '../create/local'; 9import { createInvalid as invalid } from './valid'; 10 11// ASP.NET json date format regex 12var aspNetRegex = /^(-|\+)?(?:(\d*)[. ])?(\d+):(\d+)(?::(\d+)(\.\d*)?)?$/, 13 // from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html 14 // somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere 15 // and further modified to allow for strings containing both week and day 16 isoRegex = 17 /^(-|\+)?P(?:([-+]?[0-9,.]*)Y)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)W)?(?:([-+]?[0-9,.]*)D)?(?:T(?:([-+]?[0-9,.]*)H)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)S)?)?$/; 18 19export function createDuration(input, key) { 20 var duration = input, 21 // matching against regexp is expensive, do it on demand 22 match = null, 23 sign, 24 ret, 25 diffRes; 26 27 if (isDuration(input)) { 28 duration = { 29 ms: input._milliseconds, 30 d: input._days, 31 M: input._months, 32 }; 33 } else if (isNumber(input) || !isNaN(+input)) { 34 duration = {}; 35 if (key) { 36 duration[key] = +input; 37 } else { 38 duration.milliseconds = +input; 39 } 40 } else if ((match = aspNetRegex.exec(input))) { 41 sign = match[1] === '-' ? -1 : 1; 42 duration = { 43 y: 0, 44 d: toInt(match[DATE]) * sign, 45 h: toInt(match[HOUR]) * sign, 46 m: toInt(match[MINUTE]) * sign, 47 s: toInt(match[SECOND]) * sign, 48 ms: toInt(absRound(match[MILLISECOND] * 1000)) * sign, // the millisecond decimal point is included in the match 49 }; 50 } else if ((match = isoRegex.exec(input))) { 51 sign = match[1] === '-' ? -1 : 1; 52 duration = { 53 y: parseIso(match[2], sign), 54 M: parseIso(match[3], sign), 55 w: parseIso(match[4], sign), 56 d: parseIso(match[5], sign), 57 h: parseIso(match[6], sign), 58 m: parseIso(match[7], sign), 59 s: parseIso(match[8], sign), 60 }; 61 } else if (duration == null) { 62 // checks for null or undefined 63 duration = {}; 64 } else if ( 65 typeof duration === 'object' && 66 ('from' in duration || 'to' in duration) 67 ) { 68 diffRes = momentsDifference( 69 createLocal(duration.from), 70 createLocal(duration.to) 71 ); 72 73 duration = {}; 74 duration.ms = diffRes.milliseconds; 75 duration.M = diffRes.months; 76 } 77 78 ret = new Duration(duration); 79 80 if (isDuration(input) && hasOwnProp(input, '_locale')) { 81 ret._locale = input._locale; 82 } 83 84 if (isDuration(input) && hasOwnProp(input, '_isValid')) { 85 ret._isValid = input._isValid; 86 } 87 88 return ret; 89} 90 91createDuration.fn = Duration.prototype; 92createDuration.invalid = invalid; 93 94function parseIso(inp, sign) { 95 // We'd normally use ~~inp for this, but unfortunately it also 96 // converts floats to ints. 97 // inp may be undefined, so careful calling replace on it. 98 var res = inp && parseFloat(inp.replace(',', '.')); 99 // apply sign while we're at it 100 return (isNaN(res) ? 0 : res) * sign; 101} 102 103function positiveMomentsDifference(base, other) { 104 var res = {}; 105 106 res.months = 107 other.month() - base.month() + (other.year() - base.year()) * 12; 108 if (base.clone().add(res.months, 'M').isAfter(other)) { 109 --res.months; 110 } 111 112 res.milliseconds = +other - +base.clone().add(res.months, 'M'); 113 114 return res; 115} 116 117function momentsDifference(base, other) { 118 var res; 119 if (!(base.isValid() && other.isValid())) { 120 return { milliseconds: 0, months: 0 }; 121 } 122 123 other = cloneWithOffset(other, base); 124 if (base.isBefore(other)) { 125 res = positiveMomentsDifference(base, other); 126 } else { 127 res = positiveMomentsDifference(other, base); 128 res.milliseconds = -res.milliseconds; 129 res.months = -res.months; 130 } 131 132 return res; 133}