momentjs-rails 1.5.0 → 1.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md CHANGED
@@ -18,9 +18,13 @@ Add the following directive to your Javascript manifest file (application.js):
18
18
 
19
19
  //= require moment
20
20
 
21
+ If you want to include a localization file, also add the following directive:
22
+
23
+ //= require moment/<locale>.js
24
+
21
25
  ## Versioning
22
26
 
23
- momentjs-rails 1.5.0 == Moment.js 1.5.0
27
+ momentjs-rails 1.6.2 == Moment.js 1.6.2
24
28
 
25
29
  Every attempt is made to mirror the currently shipping Momentum.js version number wherever possible.
26
30
  The major, minor, and patch version numbers will always represent the Momentum.js version. Should a gem
data/changelog.md CHANGED
@@ -6,3 +6,7 @@
6
6
 
7
7
  ### Version 1.5.0 (2012-03-30)
8
8
  - Upgraded Moment.js to 1.5.0
9
+
10
+ ### Version 1.6.2 (2012-06-07)
11
+ - Upgraded Moment.js to 1.6.2
12
+ - Added localization files
@@ -1,5 +1,5 @@
1
1
  // moment.js
2
- // version : 1.5.0
2
+ // version : 1.6.2
3
3
  // author : Tim Wood
4
4
  // license : MIT
5
5
  // momentjs.com
@@ -7,27 +7,63 @@
7
7
  (function (Date, undefined) {
8
8
 
9
9
  var moment,
10
- round = Math.round,
10
+ VERSION = "1.6.2",
11
+ round = Math.round, i,
12
+ // internal storage for language config files
11
13
  languages = {},
14
+ currentLanguage = 'en',
15
+
16
+ // check for nodeJS
12
17
  hasModule = (typeof module !== 'undefined'),
13
- paramsToParse = 'months|monthsShort|monthsParse|weekdays|weekdaysShort|longDateFormat|calendar|relativeTime|ordinal|meridiem'.split('|'),
14
- i,
15
- jsonRegex = /^\/?Date\((\-?\d+)/i,
16
- charactersToReplace = /(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|dddd?|do?|w[o|w]?|YYYY|YY|a|A|hh?|HH?|mm?|ss?|zz?|ZZ?|LT|LL?L?L?)/g,
17
- nonuppercaseLetters = /[^A-Z]/g,
18
- timezoneRegex = /\([A-Za-z ]+\)|:[0-9]{2} [A-Z]{3} /g,
19
- tokenCharacters = /(\\)?(MM?M?M?|dd?d?d|DD?D?D?|YYYY|YY|a|A|hh?|HH?|mm?|ss?|ZZ?|T)/g,
20
- inputCharacters = /(\\)?([0-9]+|([a-zA-Z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+|([\+\-]\d\d:?\d\d))/gi,
21
- isoRegex = /\d{4}.\d\d.\d\d(T(\d\d(.\d\d(.\d\d)?)?)?([\+\-]\d\d:?\d\d)?)?/,
18
+
19
+ // parameters to check for on the lang config
20
+ langConfigProperties = 'months|monthsShort|monthsParse|weekdays|weekdaysShort|longDateFormat|calendar|relativeTime|ordinal|meridiem'.split('|'),
21
+
22
+ // ASP.NET json date format regex
23
+ aspNetJsonRegex = /^\/?Date\((\-?\d+)/i,
24
+
25
+ // format tokens
26
+ formattingTokens = /(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|dddd?|do?|w[o|w]?|YYYY|YY|a|A|hh?|HH?|mm?|ss?|SS?S?|zz?|ZZ?|LT|LL?L?L?)/g,
27
+
28
+ // parsing tokens
29
+ parseMultipleFormatChunker = /([0-9a-zA-Z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+)/gi,
30
+
31
+ // parsing token regexes
32
+ parseTokenOneOrTwoDigits = /\d\d?/, // 0 - 99
33
+ parseTokenOneToThreeDigits = /\d{1,3}/, // 0 - 999
34
+ parseTokenThreeDigits = /\d{3}/, // 000 - 999
35
+ parseTokenFourDigits = /\d{4}/, // 0000 - 9999
36
+ parseTokenWord = /[0-9a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+/i, // any word characters or numbers
37
+ parseTokenTimezone = /Z|[\+\-]\d\d:?\d\d/i, // +00:00 -00:00 +0000 -0000 or Z
38
+ parseTokenT = /T/i, // T (ISO seperator)
39
+
40
+ // preliminary iso regex
41
+ // 0000-00-00 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000
42
+ isoRegex = /^\s*\d{4}-\d\d-\d\d(T(\d\d(:\d\d(:\d\d(\.\d\d?\d?)?)?)?)?([\+\-]\d\d:?\d\d)?)?/,
22
43
  isoFormat = 'YYYY-MM-DDTHH:mm:ssZ',
44
+
45
+ // iso time formats and regexes
23
46
  isoTimes = [
47
+ ['HH:mm:ss.S', /T\d\d:\d\d:\d\d\.\d{1,3}/],
24
48
  ['HH:mm:ss', /T\d\d:\d\d:\d\d/],
25
49
  ['HH:mm', /T\d\d:\d\d/],
26
50
  ['HH', /T\d\d/]
27
51
  ],
28
- timezoneParseRegex = /([\+\-]|\d\d)/gi,
29
- VERSION = "1.5.0",
30
- shortcuts = 'Month|Date|Hours|Minutes|Seconds|Milliseconds'.split('|');
52
+
53
+ // timezone chunker "+10:00" > ["10", "00"] or "-1530" > ["-15", "30"]
54
+ parseTimezoneChunker = /([\+\-]|\d\d)/gi,
55
+
56
+ // getter and setter names
57
+ proxyGettersAndSetters = 'Month|Date|Hours|Minutes|Seconds|Milliseconds'.split('|'),
58
+ unitMillisecondFactors = {
59
+ 'Milliseconds' : 1,
60
+ 'Seconds' : 1e3,
61
+ 'Minutes' : 6e4,
62
+ 'Hours' : 36e5,
63
+ 'Days' : 864e5,
64
+ 'Months' : 2592e6,
65
+ 'Years' : 31536e6
66
+ };
31
67
 
32
68
  // Moment prototype object
33
69
  function Moment(date, isUTC) {
@@ -35,6 +71,66 @@
35
71
  this._isUTC = !!isUTC;
36
72
  }
37
73
 
74
+ function absRound(number) {
75
+ if (number < 0) {
76
+ return Math.ceil(number);
77
+ } else {
78
+ return Math.floor(number);
79
+ }
80
+ }
81
+
82
+ // Duration Constructor
83
+ function Duration(duration) {
84
+ var data = this._data = {},
85
+ years = duration.years || duration.y || 0,
86
+ months = duration.months || duration.M || 0,
87
+ weeks = duration.weeks || duration.w || 0,
88
+ days = duration.days || duration.d || 0,
89
+ hours = duration.hours || duration.h || 0,
90
+ minutes = duration.minutes || duration.m || 0,
91
+ seconds = duration.seconds || duration.s || 0,
92
+ milliseconds = duration.milliseconds || duration.ms || 0;
93
+
94
+ // representation for dateAddRemove
95
+ this._milliseconds = milliseconds +
96
+ seconds * 1e3 + // 1000
97
+ minutes * 6e4 + // 1000 * 60
98
+ hours * 36e5; // 1000 * 60 * 60
99
+ // Because of dateAddRemove treats 24 hours as different from a
100
+ // day when working around DST, we need to store them separately
101
+ this._days = days +
102
+ weeks * 7;
103
+ // It is impossible translate months into days without knowing
104
+ // which months you are are talking about, so we have to store
105
+ // it separately.
106
+ this._months = months +
107
+ years * 12;
108
+
109
+ // The following code bubbles up values, see the tests for
110
+ // examples of what that means.
111
+ data.milliseconds = milliseconds % 1000;
112
+ seconds += absRound(milliseconds / 1000);
113
+
114
+ data.seconds = seconds % 60;
115
+ minutes += absRound(seconds / 60);
116
+
117
+ data.minutes = minutes % 60;
118
+ hours += absRound(minutes / 60);
119
+
120
+ data.hours = hours % 24;
121
+ days += absRound(hours / 24);
122
+
123
+ days += weeks * 7;
124
+ data.days = days % 30;
125
+
126
+ months += absRound(days / 30);
127
+
128
+ data.months = months % 12;
129
+ years += absRound(months / 12);
130
+
131
+ data.years = years;
132
+ }
133
+
38
134
  // left zero fill a number
39
135
  // see http://jsperf.com/left-zero-filling for performance comparison
40
136
  function leftZeroFill(number, targetLength) {
@@ -46,34 +142,24 @@
46
142
  }
47
143
 
48
144
  // helper function for _.addTime and _.subtractTime
49
- function dateAddRemove(date, _input, adding, val) {
50
- var isString = (typeof _input === 'string'),
51
- input = isString ? {} : _input,
52
- ms, d, M, currentDate;
53
- if (isString && val) {
54
- input[_input] = +val;
55
- }
56
- ms = (input.ms || input.milliseconds || 0) +
57
- (input.s || input.seconds || 0) * 1e3 + // 1000
58
- (input.m || input.minutes || 0) * 6e4 + // 1000 * 60
59
- (input.h || input.hours || 0) * 36e5; // 1000 * 60 * 60
60
- d = (input.d || input.days || 0) +
61
- (input.w || input.weeks || 0) * 7;
62
- M = (input.M || input.months || 0) +
63
- (input.y || input.years || 0) * 12;
145
+ function addOrSubtractDurationFromMoment(mom, duration, isAdding) {
146
+ var ms = duration._milliseconds,
147
+ d = duration._days,
148
+ M = duration._months,
149
+ currentDate;
150
+
64
151
  if (ms) {
65
- date.setTime(+date + ms * adding);
152
+ mom._d.setTime(+mom + ms * isAdding);
66
153
  }
67
154
  if (d) {
68
- date.setDate(date.getDate() + d * adding);
155
+ mom.date(mom.date() + d * isAdding);
69
156
  }
70
157
  if (M) {
71
- currentDate = date.getDate();
72
- date.setDate(1);
73
- date.setMonth(date.getMonth() + M * adding);
74
- date.setDate(Math.min(new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate(), currentDate));
158
+ currentDate = mom.date();
159
+ mom.date(1)
160
+ .month(mom.month() + M * isAdding)
161
+ .date(Math.min(currentDate, mom.daysInMonth()));
75
162
  }
76
- return date;
77
163
  }
78
164
 
79
165
  // check if is an array
@@ -98,6 +184,7 @@
98
184
  currentHours = m.hours(),
99
185
  currentMinutes = m.minutes(),
100
186
  currentSeconds = m.seconds(),
187
+ currentMilliseconds = m.milliseconds(),
101
188
  currentZone = -m.zone(),
102
189
  ordinal = moment.ordinal,
103
190
  meridiem = moment.meridiem;
@@ -165,9 +252,9 @@
165
252
  return currentYear;
166
253
  // AM / PM
167
254
  case 'a' :
168
- return currentHours > 11 ? meridiem.pm : meridiem.am;
255
+ return meridiem ? meridiem(currentHours, currentMinutes, false) : (currentHours > 11 ? 'pm' : 'am');
169
256
  case 'A' :
170
- return currentHours > 11 ? meridiem.PM : meridiem.AM;
257
+ return meridiem ? meridiem(currentHours, currentMinutes, true) : (currentHours > 11 ? 'PM' : 'AM');
171
258
  // 24 HOUR
172
259
  case 'H' :
173
260
  return currentHours;
@@ -188,11 +275,14 @@
188
275
  return currentSeconds;
189
276
  case 'ss' :
190
277
  return leftZeroFill(currentSeconds, 2);
278
+ // MILLISECONDS
279
+ case 'S' :
280
+ return ~~ (currentMilliseconds / 100);
281
+ case 'SS' :
282
+ return leftZeroFill(~~(currentMilliseconds / 10), 2);
283
+ case 'SSS' :
284
+ return leftZeroFill(currentMilliseconds, 3);
191
285
  // TIMEZONE
192
- case 'zz' :
193
- // depreciating 'zz' fall through to 'z'
194
- case 'z' :
195
- return (m._d.toString().match(timezoneRegex) || [''])[0].replace(nonuppercaseLetters, '');
196
286
  case 'Z' :
197
287
  return (currentZone < 0 ? '-' : '+') + leftZeroFill(~~(Math.abs(currentZone) / 60), 2) + ':' + leftZeroFill(~~(Math.abs(currentZone) % 60), 2);
198
288
  case 'ZZ' :
@@ -209,123 +299,164 @@
209
299
  return input.replace(/(^\[)|(\\)|\]$/g, "");
210
300
  }
211
301
  }
212
- return inputString.replace(charactersToReplace, replaceFunction);
302
+ return inputString.replace(formattingTokens, replaceFunction);
213
303
  }
214
304
 
215
- // date from string and format string
216
- function makeDateFromStringAndFormat(string, format) {
217
- var inArray = [0, 0, 1, 0, 0, 0, 0],
218
- timezoneHours = 0,
219
- timezoneMinutes = 0,
220
- isUsingUTC = false,
221
- inputParts = string.match(inputCharacters),
222
- formatParts = format.match(tokenCharacters),
223
- len = Math.min(inputParts.length, formatParts.length),
224
- i,
225
- isPm;
305
+ // get the regex to find the next token
306
+ function getParseRegexForToken(token) {
307
+ switch (token) {
308
+ case 'DDDD':
309
+ return parseTokenThreeDigits;
310
+ case 'YYYY':
311
+ return parseTokenFourDigits;
312
+ case 'S':
313
+ case 'SS':
314
+ case 'SSS':
315
+ case 'DDD':
316
+ return parseTokenOneToThreeDigits;
317
+ case 'MMM':
318
+ case 'MMMM':
319
+ case 'ddd':
320
+ case 'dddd':
321
+ case 'a':
322
+ case 'A':
323
+ return parseTokenWord;
324
+ case 'Z':
325
+ case 'ZZ':
326
+ return parseTokenTimezone;
327
+ case 'T':
328
+ return parseTokenT;
329
+ case 'MM':
330
+ case 'DD':
331
+ case 'dd':
332
+ case 'YY':
333
+ case 'HH':
334
+ case 'hh':
335
+ case 'mm':
336
+ case 'ss':
337
+ case 'M':
338
+ case 'D':
339
+ case 'd':
340
+ case 'H':
341
+ case 'h':
342
+ case 'm':
343
+ case 's':
344
+ return parseTokenOneOrTwoDigits;
345
+ default :
346
+ return new RegExp(token.replace('\\', ''));
347
+ }
348
+ }
226
349
 
227
- // function to convert string input to date
228
- function addTime(format, input) {
229
- var a;
230
- switch (format) {
231
- // MONTH
232
- case 'M' :
233
- // fall through to MM
234
- case 'MM' :
235
- inArray[1] = ~~input - 1;
236
- break;
237
- case 'MMM' :
238
- // fall through to MMMM
239
- case 'MMMM' :
240
- for (a = 0; a < 12; a++) {
241
- if (moment.monthsParse[a].test(input)) {
242
- inArray[1] = a;
243
- break;
244
- }
245
- }
246
- break;
247
- // DAY OF MONTH
248
- case 'D' :
249
- // fall through to DDDD
250
- case 'DD' :
251
- // fall through to DDDD
252
- case 'DDD' :
253
- // fall through to DDDD
254
- case 'DDDD' :
255
- inArray[2] = ~~input;
256
- break;
257
- // YEAR
258
- case 'YY' :
259
- input = ~~input;
260
- inArray[0] = input + (input > 70 ? 1900 : 2000);
261
- break;
262
- case 'YYYY' :
263
- inArray[0] = ~~Math.abs(input);
264
- break;
265
- // AM / PM
266
- case 'a' :
267
- // fall through to A
268
- case 'A' :
269
- isPm = (input.toLowerCase() === 'pm');
270
- break;
271
- // 24 HOUR
272
- case 'H' :
273
- // fall through to hh
274
- case 'HH' :
275
- // fall through to hh
276
- case 'h' :
277
- // fall through to hh
278
- case 'hh' :
279
- inArray[3] = ~~input;
280
- break;
281
- // MINUTE
282
- case 'm' :
283
- // fall through to mm
284
- case 'mm' :
285
- inArray[4] = ~~input;
286
- break;
287
- // SECOND
288
- case 's' :
289
- // fall through to ss
290
- case 'ss' :
291
- inArray[5] = ~~input;
292
- break;
293
- // TIMEZONE
294
- case 'Z' :
295
- // fall through to ZZ
296
- case 'ZZ' :
297
- isUsingUTC = true;
298
- a = (input || '').match(timezoneParseRegex);
299
- if (a && a[1]) {
300
- timezoneHours = ~~a[1];
301
- }
302
- if (a && a[2]) {
303
- timezoneMinutes = ~~a[2];
304
- }
305
- // reverse offsets
306
- if (a && a[0] === '+') {
307
- timezoneHours = -timezoneHours;
308
- timezoneMinutes = -timezoneMinutes;
350
+ // function to convert string input to date
351
+ function addTimeToArrayFromToken(token, input, datePartArray, config) {
352
+ var a;
353
+ //console.log('addTime', format, input);
354
+ switch (token) {
355
+ // MONTH
356
+ case 'M' : // fall through to MM
357
+ case 'MM' :
358
+ datePartArray[1] = (input == null) ? 0 : ~~input - 1;
359
+ break;
360
+ case 'MMM' : // fall through to MMMM
361
+ case 'MMMM' :
362
+ for (a = 0; a < 12; a++) {
363
+ if (moment.monthsParse[a].test(input)) {
364
+ datePartArray[1] = a;
365
+ break;
309
366
  }
310
- break;
311
367
  }
368
+ break;
369
+ // DAY OF MONTH
370
+ case 'D' : // fall through to DDDD
371
+ case 'DD' : // fall through to DDDD
372
+ case 'DDD' : // fall through to DDDD
373
+ case 'DDDD' :
374
+ datePartArray[2] = ~~input;
375
+ break;
376
+ // YEAR
377
+ case 'YY' :
378
+ input = ~~input;
379
+ datePartArray[0] = input + (input > 70 ? 1900 : 2000);
380
+ break;
381
+ case 'YYYY' :
382
+ datePartArray[0] = ~~Math.abs(input);
383
+ break;
384
+ // AM / PM
385
+ case 'a' : // fall through to A
386
+ case 'A' :
387
+ config.isPm = ((input + '').toLowerCase() === 'pm');
388
+ break;
389
+ // 24 HOUR
390
+ case 'H' : // fall through to hh
391
+ case 'HH' : // fall through to hh
392
+ case 'h' : // fall through to hh
393
+ case 'hh' :
394
+ datePartArray[3] = ~~input;
395
+ break;
396
+ // MINUTE
397
+ case 'm' : // fall through to mm
398
+ case 'mm' :
399
+ datePartArray[4] = ~~input;
400
+ break;
401
+ // SECOND
402
+ case 's' : // fall through to ss
403
+ case 'ss' :
404
+ datePartArray[5] = ~~input;
405
+ break;
406
+ // MILLISECOND
407
+ case 'S' :
408
+ case 'SS' :
409
+ case 'SSS' :
410
+ datePartArray[6] = ~~ (('0.' + input) * 1000);
411
+ break;
412
+ // TIMEZONE
413
+ case 'Z' : // fall through to ZZ
414
+ case 'ZZ' :
415
+ config.isUTC = true;
416
+ a = (input + '').match(parseTimezoneChunker);
417
+ if (a && a[1]) {
418
+ config.tzh = ~~a[1];
419
+ }
420
+ if (a && a[2]) {
421
+ config.tzm = ~~a[2];
422
+ }
423
+ // reverse offsets
424
+ if (a && a[0] === '+') {
425
+ config.tzh = -config.tzh;
426
+ config.tzm = -config.tzm;
427
+ }
428
+ break;
312
429
  }
313
- for (i = 0; i < len; i++) {
314
- addTime(formatParts[i], inputParts[i]);
430
+ }
431
+
432
+ // date from string and format string
433
+ function makeDateFromStringAndFormat(string, format) {
434
+ var datePartArray = [0, 0, 1, 0, 0, 0, 0],
435
+ config = {
436
+ tzh : 0, // timezone hour offset
437
+ tzm : 0 // timezone minute offset
438
+ },
439
+ tokens = format.match(formattingTokens),
440
+ i, parsedInput;
441
+
442
+ for (i = 0; i < tokens.length; i++) {
443
+ parsedInput = (getParseRegexForToken(tokens[i]).exec(string) || [])[0];
444
+ string = string.replace(getParseRegexForToken(tokens[i]), '');
445
+ addTimeToArrayFromToken(tokens[i], parsedInput, datePartArray, config);
315
446
  }
316
447
  // handle am pm
317
- if (isPm && inArray[3] < 12) {
318
- inArray[3] += 12;
448
+ if (config.isPm && datePartArray[3] < 12) {
449
+ datePartArray[3] += 12;
319
450
  }
320
451
  // if is 12 am, change hours to 0
321
- if (isPm === false && inArray[3] === 12) {
322
- inArray[3] = 0;
452
+ if (config.isPm === false && datePartArray[3] === 12) {
453
+ datePartArray[3] = 0;
323
454
  }
324
455
  // handle timezone
325
- inArray[3] += timezoneHours;
326
- inArray[4] += timezoneMinutes;
456
+ datePartArray[3] += config.tzh;
457
+ datePartArray[4] += config.tzm;
327
458
  // return
328
- return isUsingUTC ? new Date(Date.UTC.apply({}, inArray)) : dateFromArray(inArray);
459
+ return config.isUTC ? new Date(Date.UTC.apply({}, datePartArray)) : dateFromArray(datePartArray);
329
460
  }
330
461
 
331
462
  // compare two arrays, return the number of differences
@@ -345,18 +476,19 @@
345
476
  // date from string and array of format strings
346
477
  function makeDateFromStringAndArray(string, formats) {
347
478
  var output,
348
- inputParts = string.match(inputCharacters),
349
- scores = [],
479
+ inputParts = string.match(parseMultipleFormatChunker) || [],
480
+ formattedInputParts,
350
481
  scoreToBeat = 99,
351
482
  i,
352
- curDate,
353
- curScore;
483
+ currentDate,
484
+ currentScore;
354
485
  for (i = 0; i < formats.length; i++) {
355
- curDate = makeDateFromStringAndFormat(string, formats[i]);
356
- curScore = compareArrays(inputParts, formatMoment(new Moment(curDate), formats[i]).match(inputCharacters));
357
- if (curScore < scoreToBeat) {
358
- scoreToBeat = curScore;
359
- output = curDate;
486
+ currentDate = makeDateFromStringAndFormat(string, formats[i]);
487
+ formattedInputParts = formatMoment(new Moment(currentDate), formats[i]).match(parseMultipleFormatChunker) || [];
488
+ currentScore = compareArrays(inputParts, formattedInputParts);
489
+ if (currentScore < scoreToBeat) {
490
+ scoreToBeat = currentScore;
491
+ output = currentDate;
360
492
  }
361
493
  }
362
494
  return output;
@@ -367,22 +499,24 @@
367
499
  var format = 'YYYY-MM-DDT',
368
500
  i;
369
501
  if (isoRegex.exec(string)) {
370
- for (i = 0; i < 3; i++) {
502
+ for (i = 0; i < 4; i++) {
371
503
  if (isoTimes[i][1].exec(string)) {
372
504
  format += isoTimes[i][0];
373
505
  break;
374
506
  }
375
507
  }
376
- return makeDateFromStringAndFormat(string, format + 'Z');
508
+ return parseTokenTimezone.exec(string) ?
509
+ makeDateFromStringAndFormat(string, format + ' Z') :
510
+ makeDateFromStringAndFormat(string, format);
377
511
  }
378
512
  return new Date(string);
379
513
  }
380
514
 
381
- // helper function for _date.from() and _date.fromNow()
382
- function substituteTimeAgo(string, number, withoutSuffix) {
515
+ // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize
516
+ function substituteTimeAgo(string, number, withoutSuffix, isFuture) {
383
517
  var rt = moment.relativeTime[string];
384
518
  return (typeof rt === 'function') ?
385
- rt(number || 1, !!withoutSuffix, string) :
519
+ rt(number || 1, !!withoutSuffix, string, isFuture) :
386
520
  rt.replace(/%d/i, number || 1);
387
521
  }
388
522
 
@@ -403,6 +537,7 @@
403
537
  days < 345 && ['MM', round(days / 30)] ||
404
538
  years === 1 && ['y'] || ['yy', years];
405
539
  args[2] = withoutSuffix;
540
+ args[3] = milliseconds > 0;
406
541
  return substituteTimeAgo.apply({}, args);
407
542
  }
408
543
 
@@ -411,10 +546,12 @@
411
546
  return null;
412
547
  }
413
548
  var date,
414
- matched;
549
+ matched,
550
+ isUTC;
415
551
  // parse Moment object
416
- if (input && input._d instanceof Date) {
552
+ if (moment.isMoment(input)) {
417
553
  date = new Date(+input._d);
554
+ isUTC = input._isUTC;
418
555
  // parse string and format
419
556
  } else if (format) {
420
557
  if (isArray(format)) {
@@ -424,7 +561,7 @@
424
561
  }
425
562
  // evaluate it as a JSON-encoded date
426
563
  } else {
427
- matched = jsonRegex.exec(input);
564
+ matched = aspNetJsonRegex.exec(input);
428
565
  date = input === undefined ? new Date() :
429
566
  matched ? new Date(+matched[1]) :
430
567
  input instanceof Date ? input :
@@ -432,7 +569,7 @@
432
569
  typeof input === 'string' ? makeDateFromString(input) :
433
570
  new Date(input);
434
571
  }
435
- return new Moment(date);
572
+ return new Moment(date, isUTC);
436
573
  };
437
574
 
438
575
  // creating with utc
@@ -440,42 +577,38 @@
440
577
  if (isArray(input)) {
441
578
  return new Moment(new Date(Date.UTC.apply({}, input)), true);
442
579
  }
443
- return (format && input) ? moment(input + ' 0', format + ' Z').utc() : moment(input).utc();
580
+ return (format && input) ?
581
+ moment(input + ' +0000', format + ' Z').utc() :
582
+ moment(input && !parseTokenTimezone.exec(input) ? input + '+0000' : input).utc();
583
+ };
584
+
585
+ // creating with unix timestamp (in seconds)
586
+ moment.unix = function (input) {
587
+ return moment(input * 1000);
588
+ };
589
+
590
+ // duration
591
+ moment.duration = function (input, key) {
592
+ var isDuration = moment.isDuration(input),
593
+ isNumber = (typeof input === 'number'),
594
+ duration = (isDuration ? input._data : (isNumber ? {} : input));
595
+
596
+ if (isNumber) {
597
+ if (key) {
598
+ duration[key] = input;
599
+ } else {
600
+ duration.milliseconds = input;
601
+ }
602
+ }
603
+
604
+ return new Duration(duration);
444
605
  };
445
606
 
446
607
  // humanizeDuration
608
+ // This method is deprecated in favor of the new Duration object. Please
609
+ // see the moment.duration method.
447
610
  moment.humanizeDuration = function (num, type, withSuffix) {
448
- var difference = +num,
449
- rel = moment.relativeTime,
450
- output;
451
- switch (type) {
452
- case "seconds" :
453
- difference *= 1000; // 1000
454
- break;
455
- case "minutes" :
456
- difference *= 60000; // 60 * 1000
457
- break;
458
- case "hours" :
459
- difference *= 3600000; // 60 * 60 * 1000
460
- break;
461
- case "days" :
462
- difference *= 86400000; // 24 * 60 * 60 * 1000
463
- break;
464
- case "weeks" :
465
- difference *= 604800000; // 7 * 24 * 60 * 60 * 1000
466
- break;
467
- case "months" :
468
- difference *= 2592000000; // 30 * 24 * 60 * 60 * 1000
469
- break;
470
- case "years" :
471
- difference *= 31536000000; // 365 * 24 * 60 * 60 * 1000
472
- break;
473
- default :
474
- withSuffix = !!type;
475
- break;
476
- }
477
- output = relativeTime(difference, !withSuffix);
478
- return withSuffix ? (difference <= 0 ? rel.past : rel.future).replace(/%s/i, output) : output;
611
+ return moment.duration(num, type === true ? null : type).humanize(type === true ? true : withSuffix);
479
612
  };
480
613
 
481
614
  // version number
@@ -486,10 +619,11 @@
486
619
 
487
620
  // language switching and caching
488
621
  moment.lang = function (key, values) {
489
- var i,
490
- param,
491
- req,
622
+ var i, req,
492
623
  parse = [];
624
+ if (!key) {
625
+ return currentLanguage;
626
+ }
493
627
  if (values) {
494
628
  for (i = 0; i < 12; i++) {
495
629
  parse[i] = new RegExp('^' + values.months[i] + '|^' + values.monthsShort[i].replace('.', ''), 'i');
@@ -498,10 +632,11 @@
498
632
  languages[key] = values;
499
633
  }
500
634
  if (languages[key]) {
501
- for (i = 0; i < paramsToParse.length; i++) {
502
- param = paramsToParse[i];
503
- moment[param] = languages[key][param] || moment[param];
635
+ for (i = 0; i < langConfigProperties.length; i++) {
636
+ moment[langConfigProperties[i]] = languages[key][langConfigProperties[i]] ||
637
+ languages.en[langConfigProperties[i]];
504
638
  }
639
+ currentLanguage = key;
505
640
  } else {
506
641
  if (hasModule) {
507
642
  req = require('./lang/' + key);
@@ -523,12 +658,7 @@
523
658
  LLL : "MMMM D YYYY LT",
524
659
  LLLL : "dddd, MMMM D YYYY LT"
525
660
  },
526
- meridiem : {
527
- AM : 'AM',
528
- am : 'am',
529
- PM : 'PM',
530
- pm : 'pm'
531
- },
661
+ meridiem : false,
532
662
  calendar : {
533
663
  sameDay : '[Today at] LT',
534
664
  nextDay : '[Tomorrow at] LT',
@@ -566,6 +696,11 @@
566
696
  return obj instanceof Moment;
567
697
  };
568
698
 
699
+ // for typechecking Duration objects
700
+ moment.isDuration = function (obj) {
701
+ return obj instanceof Duration;
702
+ };
703
+
569
704
  // shortcut for prototype
570
705
  moment.fn = Moment.prototype = {
571
706
 
@@ -577,8 +712,8 @@
577
712
  return +this._d;
578
713
  },
579
714
 
580
- 'native' : function () {
581
- return this._d;
715
+ unix : function () {
716
+ return Math.floor(+this._d / 1000);
582
717
  },
583
718
 
584
719
  toString : function () {
@@ -604,17 +739,19 @@
604
739
  },
605
740
 
606
741
  add : function (input, val) {
607
- this._d = dateAddRemove(this._d, input, 1, val);
742
+ var dur = val ? moment.duration(+val, input) : moment.duration(input);
743
+ addOrSubtractDurationFromMoment(this, dur, 1);
608
744
  return this;
609
745
  },
610
746
 
611
747
  subtract : function (input, val) {
612
- this._d = dateAddRemove(this._d, input, -1, val);
748
+ var dur = val ? moment.duration(+val, input) : moment.duration(input);
749
+ addOrSubtractDurationFromMoment(this, dur, -1);
613
750
  return this;
614
751
  },
615
752
 
616
753
  diff : function (input, val, asFloat) {
617
- var inputMoment = moment(input),
754
+ var inputMoment = this._isUTC ? moment(input).utc() : moment(input).local(),
618
755
  zoneDiff = (this.zone() - inputMoment.zone()) * 6e4,
619
756
  diff = this._d - inputMoment._d - zoneDiff,
620
757
  year = this.year() - inputMoment.year(),
@@ -624,7 +761,7 @@
624
761
  if (val === 'months') {
625
762
  output = year * 12 + month + date / 30;
626
763
  } else if (val === 'years') {
627
- output = year + month / 12;
764
+ output = year + (month + date / 30) / 12;
628
765
  } else {
629
766
  output = val === 'seconds' ? diff / 1e3 : // 1000
630
767
  val === 'minutes' ? diff / 6e4 : // 1000 * 60
@@ -637,7 +774,7 @@
637
774
  },
638
775
 
639
776
  from : function (time, withoutSuffix) {
640
- return moment.humanizeDuration(this.diff(time), !withoutSuffix);
777
+ return moment.duration(this.diff(time)).humanize(!withoutSuffix);
641
778
  },
642
779
 
643
780
  fromNow : function (withoutSuffix) {
@@ -668,13 +805,13 @@
668
805
  },
669
806
 
670
807
  day : function (input) {
671
- var day = this._d.getDay();
808
+ var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay();
672
809
  return input == null ? day :
673
810
  this.add({ d : input - day });
674
811
  },
675
812
 
676
813
  sod: function () {
677
- return this.clone()
814
+ return moment(this)
678
815
  .hours(0)
679
816
  .minutes(0)
680
817
  .seconds(0)
@@ -694,12 +831,12 @@
694
831
  },
695
832
 
696
833
  daysInMonth : function () {
697
- return this.clone().month(this.month() + 1).date(0).date();
834
+ return moment(this).month(this.month() + 1).date(0).date();
698
835
  }
699
836
  };
700
837
 
701
838
  // helper for adding shortcuts
702
- function makeShortcut(name, key) {
839
+ function makeGetterAndSetter(name, key) {
703
840
  moment.fn[name] = function (input) {
704
841
  var utc = this._isUTC ? 'UTC' : '';
705
842
  if (input != null) {
@@ -712,18 +849,64 @@
712
849
  }
713
850
 
714
851
  // loop through and add shortcuts (Month, Date, Hours, Minutes, Seconds, Milliseconds)
715
- for (i = 0; i < shortcuts.length; i ++) {
716
- makeShortcut(shortcuts[i].toLowerCase(), shortcuts[i]);
852
+ for (i = 0; i < proxyGettersAndSetters.length; i ++) {
853
+ makeGetterAndSetter(proxyGettersAndSetters[i].toLowerCase(), proxyGettersAndSetters[i]);
717
854
  }
718
855
 
719
856
  // add shortcut for year (uses different syntax than the getter/setter 'year' == 'FullYear')
720
- makeShortcut('year', 'FullYear');
857
+ makeGetterAndSetter('year', 'FullYear');
858
+
859
+ moment.duration.fn = Duration.prototype = {
860
+ weeks : function () {
861
+ return absRound(this.days() / 7);
862
+ },
863
+
864
+ valueOf : function () {
865
+ return this._milliseconds +
866
+ this._days * 864e5 +
867
+ this._months * 2592e6;
868
+ },
869
+
870
+ humanize : function (withSuffix) {
871
+ var difference = +this,
872
+ rel = moment.relativeTime,
873
+ output = relativeTime(difference, !withSuffix);
874
+
875
+ if (withSuffix) {
876
+ output = (difference <= 0 ? rel.past : rel.future).replace(/%s/i, output);
877
+ }
878
+
879
+ return output;
880
+ }
881
+ };
882
+
883
+ function makeDurationGetter(name) {
884
+ moment.duration.fn[name] = function () {
885
+ return this._data[name];
886
+ };
887
+ }
888
+
889
+ function makeDurationAsGetter(name, factor) {
890
+ moment.duration.fn['as' + name] = function () {
891
+ return +this / factor;
892
+ };
893
+ }
894
+
895
+ for (i in unitMillisecondFactors) {
896
+ if (unitMillisecondFactors.hasOwnProperty(i)) {
897
+ makeDurationAsGetter(i, unitMillisecondFactors[i]);
898
+ makeDurationGetter(i.toLowerCase());
899
+ }
900
+ }
901
+
902
+ makeDurationAsGetter('Weeks', 6048e5);
721
903
 
722
904
  // CommonJS module is defined
723
905
  if (hasModule) {
724
906
  module.exports = moment;
725
907
  }
726
- if (typeof window !== 'undefined') {
908
+ /*global ender:false */
909
+ if (typeof window !== 'undefined' && typeof ender === 'undefined') {
727
910
  window.moment = moment;
728
911
  }
729
912
  /*global define:false */