momentjs-rails 1.5.0 → 1.6.2

Sign up to get free protection for your applications and to get access to all the features.
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 */