momentjs-rails 1.7.2 → 2.0.0.1

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.
Files changed (75) hide show
  1. data/README.md +1 -1
  2. data/{changelog.md → news.md} +30 -12
  3. data/test/dummy/Rakefile +7 -0
  4. data/test/dummy/config.ru +4 -0
  5. data/test/dummy/config/application.rb +65 -0
  6. data/test/dummy/config/boot.rb +10 -0
  7. data/test/dummy/config/environment.rb +5 -0
  8. data/test/dummy/config/environments/development.rb +31 -0
  9. data/test/dummy/config/environments/production.rb +64 -0
  10. data/test/dummy/config/environments/test.rb +35 -0
  11. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  12. data/test/dummy/config/initializers/inflections.rb +15 -0
  13. data/test/dummy/config/initializers/mime_types.rb +5 -0
  14. data/test/dummy/config/initializers/secret_token.rb +7 -0
  15. data/test/dummy/config/initializers/session_store.rb +8 -0
  16. data/test/dummy/config/initializers/wrap_parameters.rb +10 -0
  17. data/test/dummy/config/locales/en.yml +5 -0
  18. data/test/dummy/config/routes.rb +2 -0
  19. data/test/dummy/log/test.log +76 -0
  20. data/test/dummy/tmp/cache/assets/D1C/680/sprockets%2F16ed4d5afdb2919596e6f8790e2116c2 +0 -0
  21. data/test/dummy/tmp/cache/assets/D35/CA0/sprockets%2F493e64bce2d302801dc01cf7cd096a58 +0 -0
  22. data/test/dummy/tmp/cache/assets/D3F/5F0/sprockets%2F4a6c53eae7c6e41a69a191578bbb6417 +0 -0
  23. data/test/dummy/tmp/cache/assets/D58/BD0/sprockets%2F4de9ddc9b725c715a54c98697b2528fe +0 -0
  24. data/test/dummy/tmp/cache/assets/D75/510/sprockets%2Fe690787ac522f47a6be024bfcc1767ee +0 -0
  25. data/test/dummy/tmp/cache/assets/E10/250/sprockets%2Fe4badb9ddfda484eb9671bf88567be61 +0 -0
  26. data/test/integration/navigation_test.rb +14 -0
  27. data/test/momentjs-rails_test.rb +7 -0
  28. data/test/test_helper.rb +15 -0
  29. data/vendor/assets/javascripts/moment.js +542 -355
  30. data/vendor/assets/javascripts/moment/ar-ma.js +46 -0
  31. data/vendor/assets/javascripts/moment/ar.js +45 -0
  32. data/vendor/assets/javascripts/moment/bg.js +71 -61
  33. data/vendor/assets/javascripts/moment/ca.js +51 -59
  34. data/vendor/assets/javascripts/moment/cs.js +145 -0
  35. data/vendor/assets/javascripts/moment/cv.js +45 -53
  36. data/vendor/assets/javascripts/moment/da.js +41 -49
  37. data/vendor/assets/javascripts/moment/de.js +41 -49
  38. data/vendor/assets/javascripts/moment/en-ca.js +44 -53
  39. data/vendor/assets/javascripts/moment/en-gb.js +48 -53
  40. data/vendor/assets/javascripts/moment/eo.js +55 -0
  41. data/vendor/assets/javascripts/moment/es.js +51 -59
  42. data/vendor/assets/javascripts/moment/et.js +44 -61
  43. data/vendor/assets/javascripts/moment/eu.js +46 -50
  44. data/vendor/assets/javascripts/moment/fi.js +84 -89
  45. data/vendor/assets/javascripts/moment/fr-ca.js +39 -49
  46. data/vendor/assets/javascripts/moment/fr.js +43 -49
  47. data/vendor/assets/javascripts/moment/gl.js +51 -59
  48. data/vendor/assets/javascripts/moment/he.js +46 -0
  49. data/vendor/assets/javascripts/moment/hu.js +77 -98
  50. data/vendor/assets/javascripts/moment/id.js +57 -0
  51. data/vendor/assets/javascripts/moment/is.js +103 -121
  52. data/vendor/assets/javascripts/moment/it.js +41 -49
  53. data/vendor/assets/javascripts/moment/ja.js +43 -56
  54. data/vendor/assets/javascripts/moment/ko.js +42 -54
  55. data/vendor/assets/javascripts/moment/lv.js +67 -0
  56. data/vendor/assets/javascripts/moment/nb.js +41 -49
  57. data/vendor/assets/javascripts/moment/ne.js +95 -0
  58. data/vendor/assets/javascripts/moment/nl.js +52 -57
  59. data/vendor/assets/javascripts/moment/pl.js +73 -70
  60. data/vendor/assets/javascripts/moment/pt-br.js +42 -54
  61. data/vendor/assets/javascripts/moment/pt.js +45 -53
  62. data/vendor/assets/javascripts/moment/ro.js +40 -49
  63. data/vendor/assets/javascripts/moment/ru.js +105 -114
  64. data/vendor/assets/javascripts/moment/sl.js +134 -0
  65. data/vendor/assets/javascripts/moment/sv.js +48 -53
  66. data/vendor/assets/javascripts/moment/th.js +48 -0
  67. data/vendor/assets/javascripts/moment/tr.js +69 -75
  68. data/vendor/assets/javascripts/moment/tzm-la.js +45 -0
  69. data/vendor/assets/javascripts/moment/tzm.js +45 -0
  70. data/vendor/assets/javascripts/moment/uk.js +122 -0
  71. data/vendor/assets/javascripts/moment/zh-cn.js +53 -62
  72. data/vendor/assets/javascripts/moment/zh-tw.js +53 -62
  73. metadata +121 -54
  74. data/vendor/assets/javascripts/moment/jp.js +0 -64
  75. data/vendor/assets/javascripts/moment/kr.js +0 -61
@@ -0,0 +1,14 @@
1
+ require 'test_helper'
2
+
3
+ class NavigationTest < ActionDispatch::IntegrationTest
4
+ test 'can access momentjs' do
5
+ get '/assets/moment.js'
6
+ assert_response :success
7
+ end
8
+
9
+ test 'can access momentjs translation' do
10
+ get 'assets/moment/fr.js'
11
+ assert_response :success
12
+ end
13
+ end
14
+
@@ -0,0 +1,7 @@
1
+ require 'test_helper'
2
+
3
+ class MomentjsRailsTest < ActiveSupport::TestCase
4
+ test "truth" do
5
+ assert_kind_of Module, Momentjs::Rails
6
+ end
7
+ end
@@ -0,0 +1,15 @@
1
+ # Configure Rails Environment
2
+ ENV["RAILS_ENV"] = "test"
3
+
4
+ require File.expand_path("../dummy/config/environment.rb", __FILE__)
5
+ require "rails/test_help"
6
+
7
+ Rails.backtrace_cleaner.remove_silencers!
8
+
9
+ # Load support files
10
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
11
+
12
+ # Load fixtures from the engine
13
+ if ActiveSupport::TestCase.method_defined?(:fixture_path=)
14
+ ActiveSupport::TestCase.fixture_path = File.expand_path("../fixtures", __FILE__)
15
+ end
@@ -1,5 +1,5 @@
1
1
  // moment.js
2
- // version : 1.7.2
2
+ // version : 2.0.0
3
3
  // author : Tim Wood
4
4
  // license : MIT
5
5
  // momentjs.com
@@ -11,27 +11,20 @@
11
11
  ************************************/
12
12
 
13
13
  var moment,
14
- VERSION = "1.7.2",
14
+ VERSION = "2.0.0",
15
15
  round = Math.round, i,
16
16
  // internal storage for language config files
17
17
  languages = {},
18
- currentLanguage = 'en',
19
18
 
20
19
  // check for nodeJS
21
20
  hasModule = (typeof module !== 'undefined' && module.exports),
22
21
 
23
- // Parameters to check for on the lang config. This list of properties
24
- // will be inherited from English if not provided in a language
25
- // definition. monthsParse is also a lang config property, but it
26
- // cannot be inherited and as such cannot be enumerated here.
27
- langConfigProperties = 'months|monthsShort|weekdays|weekdaysShort|weekdaysMin|longDateFormat|calendar|relativeTime|ordinal|meridiem'.split('|'),
28
-
29
22
  // ASP.NET json date format regex
30
23
  aspNetJsonRegex = /^\/?Date\((\-?\d+)/i,
31
24
 
32
25
  // format tokens
33
- formattingTokens = /(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|YYYY|YY|a|A|hh?|HH?|mm?|ss?|SS?S?|zz?|ZZ?|.)/g,
34
- localFormattingTokens = /(\[[^\[]*\])|(\\)?(LT|LL?L?L?)/g,
26
+ formattingTokens = /(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|YYYYY|YYYY|YY|a|A|hh?|HH?|mm?|ss?|SS?S?|X|zz?|ZZ?|.)/g,
27
+ localFormattingTokens = /(\[[^\[]*\])|(\\)?(LT|LL?L?L?|l{1,4})/g,
35
28
 
36
29
  // parsing tokens
37
30
  parseMultipleFormatChunker = /([0-9a-zA-Z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+)/gi,
@@ -41,21 +34,23 @@
41
34
  parseTokenOneToThreeDigits = /\d{1,3}/, // 0 - 999
42
35
  parseTokenThreeDigits = /\d{3}/, // 000 - 999
43
36
  parseTokenFourDigits = /\d{1,4}/, // 0 - 9999
44
- parseTokenWord = /[0-9a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+/i, // any word characters or numbers
37
+ parseTokenSixDigits = /[+\-]?\d{1,6}/, // -999,999 - 999,999
38
+ parseTokenWord = /[0-9]*[a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF]+\s*?[\u0600-\u06FF]+/i, // any word (or two) characters or numbers including two word month in arabic.
45
39
  parseTokenTimezone = /Z|[\+\-]\d\d:?\d\d/i, // +00:00 -00:00 +0000 -0000 or Z
46
40
  parseTokenT = /T/i, // T (ISO seperator)
41
+ parseTokenTimestampMs = /[\+\-]?\d+(\.\d{1,3})?/, // 123456789 123456789.123
47
42
 
48
43
  // preliminary iso regex
49
44
  // 0000-00-00 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000
50
- isoRegex = /^\s*\d{4}-\d\d-\d\d(T(\d\d(:\d\d(:\d\d(\.\d\d?\d?)?)?)?)?([\+\-]\d\d:?\d\d)?)?/,
45
+ isoRegex = /^\s*\d{4}-\d\d-\d\d((T| )(\d\d(:\d\d(:\d\d(\.\d\d?\d?)?)?)?)?([\+\-]\d\d:?\d\d)?)?/,
51
46
  isoFormat = 'YYYY-MM-DDTHH:mm:ssZ',
52
47
 
53
48
  // iso time formats and regexes
54
49
  isoTimes = [
55
- ['HH:mm:ss.S', /T\d\d:\d\d:\d\d\.\d{1,3}/],
56
- ['HH:mm:ss', /T\d\d:\d\d:\d\d/],
57
- ['HH:mm', /T\d\d:\d\d/],
58
- ['HH', /T\d\d/]
50
+ ['HH:mm:ss.S', /(T| )\d\d:\d\d:\d\d\.\d{1,3}/],
51
+ ['HH:mm:ss', /(T| )\d\d:\d\d:\d\d/],
52
+ ['HH:mm', /(T| )\d\d:\d\d/],
53
+ ['HH', /(T| )\d\d/]
59
54
  ],
60
55
 
61
56
  // timezone chunker "+10:00" > ["10", "00"] or "-1530" > ["-15", "30"]
@@ -77,57 +72,42 @@
77
72
  formatFunctions = {},
78
73
 
79
74
  // tokens to ordinalize and pad
80
- ordinalizeTokens = 'DDD w M D d'.split(' '),
81
- paddedTokens = 'M D H h m s w'.split(' '),
82
-
83
- /*
84
- * moment.fn.format uses new Function() to create an inlined formatting function.
85
- * Results are a 3x speed boost
86
- * http://jsperf.com/momentjs-cached-format-functions
87
- *
88
- * These strings are appended into a function using replaceFormatTokens and makeFormatFunction
89
- */
75
+ ordinalizeTokens = 'DDD w W M D d'.split(' '),
76
+ paddedTokens = 'M D H h m s w W'.split(' '),
77
+
90
78
  formatTokenFunctions = {
91
- // a = placeholder
92
- // b = placeholder
93
- // t = the current moment being formatted
94
- // v = getValueAtKey function
95
- // o = language.ordinal function
96
- // p = leftZeroFill function
97
- // m = language.meridiem value or function
98
79
  M : function () {
99
80
  return this.month() + 1;
100
81
  },
101
82
  MMM : function (format) {
102
- return getValueFromArray("monthsShort", this.month(), this, format);
83
+ return this.lang().monthsShort(this, format);
103
84
  },
104
85
  MMMM : function (format) {
105
- return getValueFromArray("months", this.month(), this, format);
86
+ return this.lang().months(this, format);
106
87
  },
107
88
  D : function () {
108
89
  return this.date();
109
90
  },
110
91
  DDD : function () {
111
- var a = new Date(this.year(), this.month(), this.date()),
112
- b = new Date(this.year(), 0, 1);
113
- return ~~(((a - b) / 864e5) + 1.5);
92
+ return this.dayOfYear();
114
93
  },
115
94
  d : function () {
116
95
  return this.day();
117
96
  },
118
97
  dd : function (format) {
119
- return getValueFromArray("weekdaysMin", this.day(), this, format);
98
+ return this.lang().weekdaysMin(this, format);
120
99
  },
121
100
  ddd : function (format) {
122
- return getValueFromArray("weekdaysShort", this.day(), this, format);
101
+ return this.lang().weekdaysShort(this, format);
123
102
  },
124
103
  dddd : function (format) {
125
- return getValueFromArray("weekdays", this.day(), this, format);
104
+ return this.lang().weekdays(this, format);
126
105
  },
127
106
  w : function () {
128
- var a = new Date(this.year(), this.month(), this.date() - this.day() + 5),
129
- b = new Date(a.getFullYear(), 0, 4);
130
- return ~~((a - b) / 864e5 / 7 + 1.5);
107
+ return this.week();
108
+ },
109
+ W : function () {
110
+ return this.isoWeek();
131
111
  },
132
112
  YY : function () {
133
113
  return leftZeroFill(this.year() % 100, 2);
@@ -135,6 +115,9 @@
135
115
  YYYY : function () {
136
116
  return leftZeroFill(this.year(), 4);
137
117
  },
118
+ YYYYY : function () {
119
+ return leftZeroFill(this.year(), 5);
120
+ },
138
121
  a : function () {
139
122
  return this.lang().meridiem(this.hours(), this.minutes(), true);
140
123
  },
@@ -179,14 +162,12 @@
179
162
  b = "-";
180
163
  }
181
164
  return b + leftZeroFill(~~(10 * a / 6), 4);
165
+ },
166
+ X : function () {
167
+ return this.unix();
182
168
  }
183
169
  };
184
170
 
185
- function getValueFromArray(key, index, m, format) {
186
- var lang = m.lang();
187
- return lang[key].call ? lang[key](m, format) : lang[key][index];
188
- }
189
-
190
171
  function padToken(func, count) {
191
172
  return function (a) {
192
173
  return leftZeroFill(func.call(this, a), count);
@@ -194,8 +175,7 @@
194
175
  }
195
176
  function ordinalizeToken(func) {
196
177
  return function (a) {
197
- var b = func.call(this, a);
198
- return b + this.lang().ordinal(b);
178
+ return this.lang().ordinal(func.call(this, a));
199
179
  };
200
180
  }
201
181
 
@@ -214,26 +194,26 @@
214
194
  Constructors
215
195
  ************************************/
216
196
 
197
+ function Language() {
198
+
199
+ }
217
200
 
218
201
  // Moment prototype object
219
- function Moment(date, isUTC, lang) {
220
- this._d = date;
221
- this._isUTC = !!isUTC;
222
- this._a = date._a || null;
223
- this._lang = lang || false;
202
+ function Moment(config) {
203
+ extend(this, config);
224
204
  }
225
205
 
226
206
  // Duration Constructor
227
207
  function Duration(duration) {
228
208
  var data = this._data = {},
229
- years = duration.years || duration.y || 0,
230
- months = duration.months || duration.M || 0,
231
- weeks = duration.weeks || duration.w || 0,
232
- days = duration.days || duration.d || 0,
233
- hours = duration.hours || duration.h || 0,
234
- minutes = duration.minutes || duration.m || 0,
235
- seconds = duration.seconds || duration.s || 0,
236
- milliseconds = duration.milliseconds || duration.ms || 0;
209
+ years = duration.years || duration.year || duration.y || 0,
210
+ months = duration.months || duration.month || duration.M || 0,
211
+ weeks = duration.weeks || duration.week || duration.w || 0,
212
+ days = duration.days || duration.day || duration.d || 0,
213
+ hours = duration.hours || duration.hour || duration.h || 0,
214
+ minutes = duration.minutes || duration.minute || duration.m || 0,
215
+ seconds = duration.seconds || duration.second || duration.s || 0,
216
+ milliseconds = duration.milliseconds || duration.millisecond || duration.ms || 0;
237
217
 
238
218
  // representation for dateAddRemove
239
219
  this._milliseconds = milliseconds +
@@ -273,8 +253,6 @@
273
253
  years += absRound(months / 12);
274
254
 
275
255
  data.years = years;
276
-
277
- this._lang = false;
278
256
  }
279
257
 
280
258
 
@@ -283,6 +261,15 @@
283
261
  ************************************/
284
262
 
285
263
 
264
+ function extend(a, b) {
265
+ for (var i in b) {
266
+ if (b.hasOwnProperty(i)) {
267
+ a[i] = b[i];
268
+ }
269
+ }
270
+ return a;
271
+ }
272
+
286
273
  function absRound(number) {
287
274
  if (number < 0) {
288
275
  return Math.ceil(number);
@@ -341,66 +328,169 @@
341
328
  return diffs + lengthDiff;
342
329
  }
343
330
 
344
- // convert an array to a date.
345
- // the array should mirror the parameters below
346
- // note: all values past the year are optional and will default to the lowest possible value.
347
- // [year, month, day , hour, minute, second, millisecond]
348
- function dateFromArray(input, asUTC, hoursOffset, minutesOffset) {
349
- var i, date, forValid = [];
350
- for (i = 0; i < 7; i++) {
351
- forValid[i] = input[i] = (input[i] == null) ? (i === 2 ? 1 : 0) : input[i];
352
- }
353
- // we store whether we used utc or not in the input array
354
- input[7] = forValid[7] = asUTC;
355
- // if the parser flagged the input as invalid, we pass the value along
356
- if (input[8] != null) {
357
- forValid[8] = input[8];
358
- }
359
- // add the offsets to the time to be parsed so that we can have a clean array
360
- // for checking isValid
361
- input[3] += hoursOffset || 0;
362
- input[4] += minutesOffset || 0;
363
- date = new Date(0);
364
- if (asUTC) {
365
- date.setUTCFullYear(input[0], input[1], input[2]);
366
- date.setUTCHours(input[3], input[4], input[5], input[6]);
367
- } else {
368
- date.setFullYear(input[0], input[1], input[2]);
369
- date.setHours(input[3], input[4], input[5], input[6]);
331
+
332
+ /************************************
333
+ Languages
334
+ ************************************/
335
+
336
+
337
+ Language.prototype = {
338
+ set : function (config) {
339
+ var prop, i;
340
+ for (i in config) {
341
+ prop = config[i];
342
+ if (typeof prop === 'function') {
343
+ this[i] = prop;
344
+ } else {
345
+ this['_' + i] = prop;
346
+ }
347
+ }
348
+ },
349
+
350
+ _months : "January_February_March_April_May_June_July_August_September_October_November_December".split("_"),
351
+ months : function (m) {
352
+ return this._months[m.month()];
353
+ },
354
+
355
+ _monthsShort : "Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),
356
+ monthsShort : function (m) {
357
+ return this._monthsShort[m.month()];
358
+ },
359
+
360
+ monthsParse : function (monthName) {
361
+ var i, mom, regex, output;
362
+
363
+ if (!this._monthsParse) {
364
+ this._monthsParse = [];
365
+ }
366
+
367
+ for (i = 0; i < 12; i++) {
368
+ // make the regex if we don't have it already
369
+ if (!this._monthsParse[i]) {
370
+ mom = moment([2000, i]);
371
+ regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, '');
372
+ this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i');
373
+ }
374
+ // test the regex
375
+ if (this._monthsParse[i].test(monthName)) {
376
+ return i;
377
+ }
378
+ }
379
+ },
380
+
381
+ _weekdays : "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),
382
+ weekdays : function (m) {
383
+ return this._weekdays[m.day()];
384
+ },
385
+
386
+ _weekdaysShort : "Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),
387
+ weekdaysShort : function (m) {
388
+ return this._weekdaysShort[m.day()];
389
+ },
390
+
391
+ _weekdaysMin : "Su_Mo_Tu_We_Th_Fr_Sa".split("_"),
392
+ weekdaysMin : function (m) {
393
+ return this._weekdaysMin[m.day()];
394
+ },
395
+
396
+ _longDateFormat : {
397
+ LT : "h:mm A",
398
+ L : "MM/DD/YYYY",
399
+ LL : "MMMM D YYYY",
400
+ LLL : "MMMM D YYYY LT",
401
+ LLLL : "dddd, MMMM D YYYY LT"
402
+ },
403
+ longDateFormat : function (key) {
404
+ var output = this._longDateFormat[key];
405
+ if (!output && this._longDateFormat[key.toUpperCase()]) {
406
+ output = this._longDateFormat[key.toUpperCase()].replace(/MMMM|MM|DD|dddd/g, function (val) {
407
+ return val.slice(1);
408
+ });
409
+ this._longDateFormat[key] = output;
410
+ }
411
+ return output;
412
+ },
413
+
414
+ meridiem : function (hours, minutes, isLower) {
415
+ if (hours > 11) {
416
+ return isLower ? 'pm' : 'PM';
417
+ } else {
418
+ return isLower ? 'am' : 'AM';
419
+ }
420
+ },
421
+
422
+ _calendar : {
423
+ sameDay : '[Today at] LT',
424
+ nextDay : '[Tomorrow at] LT',
425
+ nextWeek : 'dddd [at] LT',
426
+ lastDay : '[Yesterday at] LT',
427
+ lastWeek : '[last] dddd [at] LT',
428
+ sameElse : 'L'
429
+ },
430
+ calendar : function (key, mom) {
431
+ var output = this._calendar[key];
432
+ return typeof output === 'function' ? output.apply(mom) : output;
433
+ },
434
+
435
+ _relativeTime : {
436
+ future : "in %s",
437
+ past : "%s ago",
438
+ s : "a few seconds",
439
+ m : "a minute",
440
+ mm : "%d minutes",
441
+ h : "an hour",
442
+ hh : "%d hours",
443
+ d : "a day",
444
+ dd : "%d days",
445
+ M : "a month",
446
+ MM : "%d months",
447
+ y : "a year",
448
+ yy : "%d years"
449
+ },
450
+ relativeTime : function (number, withoutSuffix, string, isFuture) {
451
+ var output = this._relativeTime[string];
452
+ return (typeof output === 'function') ?
453
+ output(number, withoutSuffix, string, isFuture) :
454
+ output.replace(/%d/i, number);
455
+ },
456
+ pastFuture : function (diff, output) {
457
+ var format = this._relativeTime[diff > 0 ? 'future' : 'past'];
458
+ return typeof format === 'function' ? format(output) : format.replace(/%s/i, output);
459
+ },
460
+
461
+ ordinal : function (number) {
462
+ return this._ordinal.replace("%d", number);
463
+ },
464
+ _ordinal : "%d",
465
+
466
+ preparse : function (string) {
467
+ return string;
468
+ },
469
+
470
+ postformat : function (string) {
471
+ return string;
472
+ },
473
+
474
+ week : function (mom) {
475
+ return weekOfYear(mom, this._week.dow, this._week.doy);
476
+ },
477
+ _week : {
478
+ dow : 0, // Sunday is the first day of the week.
479
+ doy : 6 // The week that contains Jan 1st is the first week of the year.
370
480
  }
371
- date._a = forValid;
372
- return date;
373
- }
481
+ };
374
482
 
375
483
  // Loads a language definition into the `languages` cache. The function
376
484
  // takes a key and optionally values. If not in the browser and no values
377
485
  // are provided, it will load the language file module. As a convenience,
378
486
  // this function also returns the language values.
379
487
  function loadLang(key, values) {
380
- var i, m,
381
- parse = [];
382
-
383
- if (!values && hasModule) {
384
- values = require('./lang/' + key);
385
- }
386
-
387
- for (i = 0; i < langConfigProperties.length; i++) {
388
- // If a language definition does not provide a value, inherit
389
- // from English
390
- values[langConfigProperties[i]] = values[langConfigProperties[i]] ||
391
- languages.en[langConfigProperties[i]];
392
- }
393
-
394
- for (i = 0; i < 12; i++) {
395
- m = moment([2000, i]);
396
- parse[i] = new RegExp('^' + (values.months[i] || values.months(m, '')) +
397
- '|^' + (values.monthsShort[i] || values.monthsShort(m, '')).replace('.', ''), 'i');
488
+ values.abbr = key;
489
+ if (!languages[key]) {
490
+ languages[key] = new Language();
398
491
  }
399
- values.monthsParse = values.monthsParse || parse;
400
-
401
- languages[key] = values;
402
-
403
- return values;
492
+ languages[key].set(values);
493
+ return languages[key];
404
494
  }
405
495
 
406
496
  // Determines which language definition to use and returns it.
@@ -408,15 +498,15 @@
408
498
  // With no parameters, it will return the global language. If you
409
499
  // pass in a language key, such as 'en', it will return the
410
500
  // definition for 'en', so long as 'en' has already been loaded using
411
- // moment.lang. If you pass in a moment or duration instance, it
412
- // will decide the language based on that, or default to the global
413
- // language.
414
- function getLangDefinition(m) {
415
- var langKey = (typeof m === 'string') && m ||
416
- m && m._lang ||
417
- null;
418
-
419
- return langKey ? (languages[langKey] || loadLang(langKey)) : moment;
501
+ // moment.lang.
502
+ function getLangDefinition(key) {
503
+ if (!key) {
504
+ return moment.fn._lang;
505
+ }
506
+ if (!languages[key] && hasModule) {
507
+ require('./lang/' + key);
508
+ }
509
+ return languages[key];
420
510
  }
421
511
 
422
512
 
@@ -457,7 +547,7 @@
457
547
  var i = 5;
458
548
 
459
549
  function replaceLongDateFormatTokens(input) {
460
- return m.lang().longDateFormat[input] || input;
550
+ return m.lang().longDateFormat(input) || input;
461
551
  }
462
552
 
463
553
  while (i-- && localFormattingTokens.test(format)) {
@@ -484,6 +574,8 @@
484
574
  return parseTokenThreeDigits;
485
575
  case 'YYYY':
486
576
  return parseTokenFourDigits;
577
+ case 'YYYYY':
578
+ return parseTokenSixDigits;
487
579
  case 'S':
488
580
  case 'SS':
489
581
  case 'SSS':
@@ -497,6 +589,8 @@
497
589
  case 'a':
498
590
  case 'A':
499
591
  return parseTokenWord;
592
+ case 'X':
593
+ return parseTokenTimestampMs;
500
594
  case 'Z':
501
595
  case 'ZZ':
502
596
  return parseTokenTimezone;
@@ -523,8 +617,9 @@
523
617
  }
524
618
 
525
619
  // function to convert string input to date
526
- function addTimeToArrayFromToken(token, input, datePartArray, config) {
527
- var a, b;
620
+ function addTimeToArrayFromToken(token, input, config) {
621
+ var a, b,
622
+ datePartArray = config._a;
528
623
 
529
624
  switch (token) {
530
625
  // MONTH
@@ -534,16 +629,12 @@
534
629
  break;
535
630
  case 'MMM' : // fall through to MMMM
536
631
  case 'MMMM' :
537
- for (a = 0; a < 12; a++) {
538
- if (getLangDefinition().monthsParse[a].test(input)) {
539
- datePartArray[1] = a;
540
- b = true;
541
- break;
542
- }
543
- }
632
+ a = getLangDefinition(config._l).monthsParse(input);
544
633
  // if we didn't find a month name, mark the date as invalid.
545
- if (!b) {
546
- datePartArray[8] = false;
634
+ if (a != null) {
635
+ datePartArray[1] = a;
636
+ } else {
637
+ config._isValid = false;
547
638
  }
548
639
  break;
549
640
  // DAY OF MONTH
@@ -557,15 +648,16 @@
557
648
  break;
558
649
  // YEAR
559
650
  case 'YY' :
560
- datePartArray[0] = ~~input + (~~input > 70 ? 1900 : 2000);
651
+ datePartArray[0] = ~~input + (~~input > 68 ? 1900 : 2000);
561
652
  break;
562
653
  case 'YYYY' :
563
- datePartArray[0] = ~~Math.abs(input);
654
+ case 'YYYYY' :
655
+ datePartArray[0] = ~~input;
564
656
  break;
565
657
  // AM / PM
566
658
  case 'a' : // fall through to A
567
659
  case 'A' :
568
- config.isPm = ((input + '').toLowerCase() === 'pm');
660
+ config._isPm = ((input + '').toLowerCase() === 'pm');
569
661
  break;
570
662
  // 24 HOUR
571
663
  case 'H' : // fall through to hh
@@ -590,45 +682,76 @@
590
682
  case 'SSS' :
591
683
  datePartArray[6] = ~~ (('0.' + input) * 1000);
592
684
  break;
685
+ // UNIX TIMESTAMP WITH MS
686
+ case 'X':
687
+ config._d = new Date(parseFloat(input) * 1000);
688
+ break;
593
689
  // TIMEZONE
594
690
  case 'Z' : // fall through to ZZ
595
691
  case 'ZZ' :
596
- config.isUTC = true;
692
+ config._useUTC = true;
597
693
  a = (input + '').match(parseTimezoneChunker);
598
694
  if (a && a[1]) {
599
- config.tzh = ~~a[1];
695
+ config._tzh = ~~a[1];
600
696
  }
601
697
  if (a && a[2]) {
602
- config.tzm = ~~a[2];
698
+ config._tzm = ~~a[2];
603
699
  }
604
700
  // reverse offsets
605
701
  if (a && a[0] === '+') {
606
- config.tzh = -config.tzh;
607
- config.tzm = -config.tzm;
702
+ config._tzh = -config._tzh;
703
+ config._tzm = -config._tzm;
608
704
  }
609
705
  break;
610
706
  }
611
707
 
612
708
  // if the input is null, the date is not valid
613
709
  if (input == null) {
614
- datePartArray[8] = false;
710
+ config._isValid = false;
615
711
  }
616
712
  }
617
713
 
714
+ // convert an array to a date.
715
+ // the array should mirror the parameters below
716
+ // note: all values past the year are optional and will default to the lowest possible value.
717
+ // [year, month, day , hour, minute, second, millisecond]
718
+ function dateFromArray(config) {
719
+ var i, date, input = [];
720
+
721
+ if (config._d) {
722
+ return;
723
+ }
724
+
725
+ for (i = 0; i < 7; i++) {
726
+ config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i];
727
+ }
728
+
729
+ // add the offsets to the time to be parsed so that we can have a clean array for checking isValid
730
+ input[3] += config._tzh || 0;
731
+ input[4] += config._tzm || 0;
732
+
733
+ date = new Date(0);
734
+
735
+ if (config._useUTC) {
736
+ date.setUTCFullYear(input[0], input[1], input[2]);
737
+ date.setUTCHours(input[3], input[4], input[5], input[6]);
738
+ } else {
739
+ date.setFullYear(input[0], input[1], input[2]);
740
+ date.setHours(input[3], input[4], input[5], input[6]);
741
+ }
742
+
743
+ config._d = date;
744
+ }
745
+
618
746
  // date from string and format string
619
- function makeDateFromStringAndFormat(string, format) {
747
+ function makeDateFromStringAndFormat(config) {
620
748
  // This array is used to make a Date, either with `new Date` or `Date.UTC`
621
- // We store some additional data on the array for validation
622
- // datePartArray[7] is true if the Date was created with `Date.UTC` and false if created with `new Date`
623
- // datePartArray[8] is false if the Date is invalid, and undefined if the validity is unknown.
624
- var datePartArray = [0, 0, 1, 0, 0, 0, 0],
625
- config = {
626
- tzh : 0, // timezone hour offset
627
- tzm : 0 // timezone minute offset
628
- },
629
- tokens = format.match(formattingTokens),
749
+ var tokens = config._f.match(formattingTokens),
750
+ string = config._i,
630
751
  i, parsedInput;
631
752
 
753
+ config._a = [];
754
+
632
755
  for (i = 0; i < tokens.length; i++) {
633
756
  parsedInput = (getParseRegexForToken(tokens[i]).exec(string) || [])[0];
634
757
  if (parsedInput) {
@@ -636,58 +759,91 @@
636
759
  }
637
760
  // don't parse if its not a known token
638
761
  if (formatTokenFunctions[tokens[i]]) {
639
- addTimeToArrayFromToken(tokens[i], parsedInput, datePartArray, config);
762
+ addTimeToArrayFromToken(tokens[i], parsedInput, config);
640
763
  }
641
764
  }
642
765
  // handle am pm
643
- if (config.isPm && datePartArray[3] < 12) {
644
- datePartArray[3] += 12;
766
+ if (config._isPm && config._a[3] < 12) {
767
+ config._a[3] += 12;
645
768
  }
646
769
  // if is 12 am, change hours to 0
647
- if (config.isPm === false && datePartArray[3] === 12) {
648
- datePartArray[3] = 0;
770
+ if (config._isPm === false && config._a[3] === 12) {
771
+ config._a[3] = 0;
649
772
  }
650
773
  // return
651
- return dateFromArray(datePartArray, config.isUTC, config.tzh, config.tzm);
774
+ dateFromArray(config);
652
775
  }
653
776
 
654
777
  // date from string and array of format strings
655
- function makeDateFromStringAndArray(string, formats) {
656
- var output,
657
- inputParts = string.match(parseMultipleFormatChunker) || [],
658
- formattedInputParts,
778
+ function makeDateFromStringAndArray(config) {
779
+ var tempConfig,
780
+ tempMoment,
781
+ bestMoment,
782
+
659
783
  scoreToBeat = 99,
660
784
  i,
661
785
  currentDate,
662
786
  currentScore;
663
- for (i = 0; i < formats.length; i++) {
664
- currentDate = makeDateFromStringAndFormat(string, formats[i]);
665
- formattedInputParts = formatMoment(new Moment(currentDate), formats[i]).match(parseMultipleFormatChunker) || [];
666
- currentScore = compareArrays(inputParts, formattedInputParts);
787
+
788
+ while (config._f.length) {
789
+ tempConfig = extend({}, config);
790
+ tempConfig._f = config._f.pop();
791
+ makeDateFromStringAndFormat(tempConfig);
792
+ tempMoment = new Moment(tempConfig);
793
+
794
+ if (tempMoment.isValid()) {
795
+ bestMoment = tempMoment;
796
+ break;
797
+ }
798
+
799
+ currentScore = compareArrays(tempConfig._a, tempMoment.toArray());
800
+
667
801
  if (currentScore < scoreToBeat) {
668
802
  scoreToBeat = currentScore;
669
- output = currentDate;
803
+ bestMoment = tempMoment;
670
804
  }
671
805
  }
672
- return output;
806
+
807
+ extend(config, bestMoment);
673
808
  }
674
809
 
675
810
  // date from iso format
676
- function makeDateFromString(string) {
677
- var format = 'YYYY-MM-DDT',
678
- i;
811
+ function makeDateFromString(config) {
812
+ var i,
813
+ string = config._i;
679
814
  if (isoRegex.exec(string)) {
815
+ config._f = 'YYYY-MM-DDT';
680
816
  for (i = 0; i < 4; i++) {
681
817
  if (isoTimes[i][1].exec(string)) {
682
- format += isoTimes[i][0];
818
+ config._f += isoTimes[i][0];
683
819
  break;
684
820
  }
685
821
  }
686
- return parseTokenTimezone.exec(string) ?
687
- makeDateFromStringAndFormat(string, format + ' Z') :
688
- makeDateFromStringAndFormat(string, format);
822
+ if (parseTokenTimezone.exec(string)) {
823
+ config._f += " Z";
824
+ }
825
+ makeDateFromStringAndFormat(config);
826
+ } else {
827
+ config._d = new Date(string);
828
+ }
829
+ }
830
+
831
+ function makeDateFromInput(config) {
832
+ var input = config._i,
833
+ matched = aspNetJsonRegex.exec(input);
834
+
835
+ if (input === undefined) {
836
+ config._d = new Date();
837
+ } else if (matched) {
838
+ config._d = new Date(+matched[1]);
839
+ } else if (typeof input === 'string') {
840
+ makeDateFromString(config);
841
+ } else if (isArray(input)) {
842
+ config._a = input.slice(0);
843
+ dateFromArray(config);
844
+ } else {
845
+ config._d = input instanceof Date ? new Date(+input) : new Date(input);
689
846
  }
690
- return new Date(string);
691
847
  }
692
848
 
693
849
 
@@ -698,10 +854,7 @@
698
854
 
699
855
  // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize
700
856
  function substituteTimeAgo(string, number, withoutSuffix, isFuture, lang) {
701
- var rt = lang.relativeTime[string];
702
- return (typeof rt === 'function') ?
703
- rt(number || 1, !!withoutSuffix, string, isFuture) :
704
- rt.replace(/%d/i, number || 1);
857
+ return lang.relativeTime(number || 1, !!withoutSuffix, string, isFuture);
705
858
  }
706
859
 
707
860
  function relativeTime(milliseconds, withoutSuffix, lang) {
@@ -727,54 +880,85 @@
727
880
  }
728
881
 
729
882
 
883
+ /************************************
884
+ Week of Year
885
+ ************************************/
886
+
887
+
888
+ // firstDayOfWeek 0 = sun, 6 = sat
889
+ // the day of the week that starts the week
890
+ // (usually sunday or monday)
891
+ // firstDayOfWeekOfYear 0 = sun, 6 = sat
892
+ // the first week is the week that contains the first
893
+ // of this day of the week
894
+ // (eg. ISO weeks use thursday (4))
895
+ function weekOfYear(mom, firstDayOfWeek, firstDayOfWeekOfYear) {
896
+ var end = firstDayOfWeekOfYear - firstDayOfWeek,
897
+ daysToDayOfWeek = firstDayOfWeekOfYear - mom.day();
898
+
899
+
900
+ if (daysToDayOfWeek > end) {
901
+ daysToDayOfWeek -= 7;
902
+ }
903
+
904
+ if (daysToDayOfWeek < end - 7) {
905
+ daysToDayOfWeek += 7;
906
+ }
907
+
908
+ return Math.ceil(moment(mom).add('d', daysToDayOfWeek).dayOfYear() / 7);
909
+ }
910
+
911
+
730
912
  /************************************
731
913
  Top Level Functions
732
914
  ************************************/
733
915
 
916
+ function makeMoment(config) {
917
+ var input = config._i,
918
+ format = config._f;
734
919
 
735
- moment = function (input, format) {
736
920
  if (input === null || input === '') {
737
921
  return null;
738
922
  }
739
- var date,
740
- matched;
741
- // parse Moment object
923
+
924
+ if (typeof input === 'string') {
925
+ config._i = input = getLangDefinition().preparse(input);
926
+ }
927
+
742
928
  if (moment.isMoment(input)) {
743
- return new Moment(new Date(+input._d), input._isUTC, input._lang);
744
- // parse string and format
929
+ config = extend({}, input);
930
+ config._d = new Date(+input._d);
745
931
  } else if (format) {
746
932
  if (isArray(format)) {
747
- date = makeDateFromStringAndArray(input, format);
933
+ makeDateFromStringAndArray(config);
748
934
  } else {
749
- date = makeDateFromStringAndFormat(input, format);
935
+ makeDateFromStringAndFormat(config);
750
936
  }
751
- // evaluate it as a JSON-encoded date
752
937
  } else {
753
- matched = aspNetJsonRegex.exec(input);
754
- date = input === undefined ? new Date() :
755
- matched ? new Date(+matched[1]) :
756
- input instanceof Date ? input :
757
- isArray(input) ? dateFromArray(input) :
758
- typeof input === 'string' ? makeDateFromString(input) :
759
- new Date(input);
938
+ makeDateFromInput(config);
760
939
  }
761
940
 
762
- return new Moment(date);
941
+ return new Moment(config);
942
+ }
943
+
944
+ moment = function (input, format, lang) {
945
+ return makeMoment({
946
+ _i : input,
947
+ _f : format,
948
+ _l : lang,
949
+ _isUTC : false
950
+ });
763
951
  };
764
952
 
765
953
  // creating with utc
766
- moment.utc = function (input, format) {
767
- if (isArray(input)) {
768
- return new Moment(dateFromArray(input, true), true);
769
- }
770
- // if we don't have a timezone, we need to add one to trigger parsing into utc
771
- if (typeof input === 'string' && !parseTokenTimezone.exec(input)) {
772
- input += ' +0000';
773
- if (format) {
774
- format += ' Z';
775
- }
776
- }
777
- return moment(input, format).utc();
954
+ moment.utc = function (input, format, lang) {
955
+ return makeMoment({
956
+ _useUTC : true,
957
+ _isUTC : true,
958
+ _l : lang,
959
+ _i : input,
960
+ _f : format
961
+ });
778
962
  };
779
963
 
780
964
  // creating with unix timestamp (in seconds)
@@ -799,20 +983,13 @@
799
983
 
800
984
  ret = new Duration(duration);
801
985
 
802
- if (isDuration) {
986
+ if (isDuration && input.hasOwnProperty('_lang')) {
803
987
  ret._lang = input._lang;
804
988
  }
805
989
 
806
990
  return ret;
807
991
  };
808
992
 
809
- // humanizeDuration
810
- // This method is deprecated in favor of the new Duration object. Please
811
- // see the moment.duration method.
812
- moment.humanizeDuration = function (num, type, withSuffix) {
813
- return moment.duration(num, type === true ? null : type).humanize(type === true ? true : withSuffix);
814
- };
815
-
816
993
  // version number
817
994
  moment.version = VERSION;
818
995
 
@@ -826,24 +1003,23 @@
826
1003
  var i;
827
1004
 
828
1005
  if (!key) {
829
- return currentLanguage;
1006
+ return moment.fn._lang._abbr;
830
1007
  }
831
- if (values || !languages[key]) {
1008
+ if (values) {
832
1009
  loadLang(key, values);
1010
+ } else if (!languages[key]) {
1011
+ getLangDefinition(key);
833
1012
  }
834
- if (languages[key]) {
835
- // deprecated, to get the language definition variables, use the
836
- // moment.fn.lang method or the getLangDefinition function.
837
- for (i = 0; i < langConfigProperties.length; i++) {
838
- moment[langConfigProperties[i]] = languages[key][langConfigProperties[i]];
839
- }
840
- moment.monthsParse = languages[key].monthsParse;
841
- currentLanguage = key;
842
- }
1013
+ moment.duration.fn._lang = moment.fn._lang = getLangDefinition(key);
843
1014
  };
844
1015
 
845
1016
  // returns language data
846
- moment.langData = getLangDefinition;
1017
+ moment.langData = function (key) {
1018
+ if (key && key._lang && key._lang._abbr) {
1019
+ key = key._lang._abbr;
1020
+ }
1021
+ return getLangDefinition(key);
1022
+ };
847
1023
 
848
1024
  // compare moment object
849
1025
  moment.isMoment = function (obj) {
@@ -855,59 +1031,6 @@
855
1031
  return obj instanceof Duration;
856
1032
  };
857
1033
 
858
- // Set default language, other languages will inherit from English.
859
- moment.lang('en', {
860
- months : "January_February_March_April_May_June_July_August_September_October_November_December".split("_"),
861
- monthsShort : "Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),
862
- weekdays : "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),
863
- weekdaysShort : "Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),
864
- weekdaysMin : "Su_Mo_Tu_We_Th_Fr_Sa".split("_"),
865
- longDateFormat : {
866
- LT : "h:mm A",
867
- L : "MM/DD/YYYY",
868
- LL : "MMMM D YYYY",
869
- LLL : "MMMM D YYYY LT",
870
- LLLL : "dddd, MMMM D YYYY LT"
871
- },
872
- meridiem : function (hours, minutes, isLower) {
873
- if (hours > 11) {
874
- return isLower ? 'pm' : 'PM';
875
- } else {
876
- return isLower ? 'am' : 'AM';
877
- }
878
- },
879
- calendar : {
880
- sameDay : '[Today at] LT',
881
- nextDay : '[Tomorrow at] LT',
882
- nextWeek : 'dddd [at] LT',
883
- lastDay : '[Yesterday at] LT',
884
- lastWeek : '[last] dddd [at] LT',
885
- sameElse : 'L'
886
- },
887
- relativeTime : {
888
- future : "in %s",
889
- past : "%s ago",
890
- s : "a few seconds",
891
- m : "a minute",
892
- mm : "%d minutes",
893
- h : "an hour",
894
- hh : "%d hours",
895
- d : "a day",
896
- dd : "%d days",
897
- M : "a month",
898
- MM : "%d months",
899
- y : "a year",
900
- yy : "%d years"
901
- },
902
- ordinal : function (number) {
903
- var b = number % 10;
904
- return (~~ (number % 100 / 10) === 1) ? 'th' :
905
- (b === 1) ? 'st' :
906
- (b === 2) ? 'nd' :
907
- (b === 3) ? 'rd' : 'th';
908
- }
909
- });
910
-
911
1034
 
912
1035
  /************************************
913
1036
  Moment Prototype
@@ -929,13 +1052,17 @@
929
1052
  },
930
1053
 
931
1054
  toString : function () {
932
- return this._d.toString();
1055
+ return this.format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ");
933
1056
  },
934
1057
 
935
1058
  toDate : function () {
936
1059
  return this._d;
937
1060
  },
938
1061
 
1062
+ toJSON : function () {
1063
+ return moment.utc(this).format('YYYY-MM-DD[T]HH:mm:ss.SSS[Z]');
1064
+ },
1065
+
939
1066
  toArray : function () {
940
1067
  var m = this;
941
1068
  return [
@@ -945,21 +1072,19 @@
945
1072
  m.hours(),
946
1073
  m.minutes(),
947
1074
  m.seconds(),
948
- m.milliseconds(),
949
- !!this._isUTC
1075
+ m.milliseconds()
950
1076
  ];
951
1077
  },
952
1078
 
953
1079
  isValid : function () {
954
- if (this._a) {
955
- // if the parser finds that the input is invalid, it sets
956
- // the eighth item in the input array to false.
957
- if (this._a[8] != null) {
958
- return !!this._a[8];
1080
+ if (this._isValid == null) {
1081
+ if (this._a) {
1082
+ this._isValid = !compareArrays(this._a, (this._isUTC ? moment.utc(this._a) : moment(this._a)).toArray());
1083
+ } else {
1084
+ this._isValid = !isNaN(this._d.getTime());
959
1085
  }
960
- return !compareArrays(this._a, (this._a[7] ? moment.utc(this._a) : moment(this._a)).toArray());
961
1086
  }
962
- return !isNaN(this._d.getTime());
1087
+ return !!this._isValid;
963
1088
  },
964
1089
 
965
1090
  utc : function () {
@@ -973,46 +1098,65 @@
973
1098
  },
974
1099
 
975
1100
  format : function (inputString) {
976
- return formatMoment(this, inputString ? inputString : moment.defaultFormat);
1101
+ var output = formatMoment(this, inputString || moment.defaultFormat);
1102
+ return this.lang().postformat(output);
977
1103
  },
978
1104
 
979
1105
  add : function (input, val) {
980
- var dur = val ? moment.duration(+val, input) : moment.duration(input);
1106
+ var dur;
1107
+ // switch args to support add('s', 1) and add(1, 's')
1108
+ if (typeof input === 'string') {
1109
+ dur = moment.duration(+val, input);
1110
+ } else {
1111
+ dur = moment.duration(input, val);
1112
+ }
981
1113
  addOrSubtractDurationFromMoment(this, dur, 1);
982
1114
  return this;
983
1115
  },
984
1116
 
985
1117
  subtract : function (input, val) {
986
- var dur = val ? moment.duration(+val, input) : moment.duration(input);
1118
+ var dur;
1119
+ // switch args to support subtract('s', 1) and subtract(1, 's')
1120
+ if (typeof input === 'string') {
1121
+ dur = moment.duration(+val, input);
1122
+ } else {
1123
+ dur = moment.duration(input, val);
1124
+ }
987
1125
  addOrSubtractDurationFromMoment(this, dur, -1);
988
1126
  return this;
989
1127
  },
990
1128
 
991
- diff : function (input, val, asFloat) {
992
- var inputMoment = this._isUTC ? moment(input).utc() : moment(input).local(),
993
- zoneDiff = (this.zone() - inputMoment.zone()) * 6e4,
994
- diff = this._d - inputMoment._d - zoneDiff,
995
- year = this.year() - inputMoment.year(),
996
- month = this.month() - inputMoment.month(),
997
- date = this.date() - inputMoment.date(),
998
- output;
999
- if (val === 'months') {
1000
- output = year * 12 + month + date / 30;
1001
- } else if (val === 'years') {
1002
- output = year + (month + date / 30) / 12;
1129
+ diff : function (input, units, asFloat) {
1130
+ var that = this._isUTC ? moment(input).utc() : moment(input).local(),
1131
+ zoneDiff = (this.zone() - that.zone()) * 6e4,
1132
+ diff, output;
1133
+
1134
+ if (units) {
1135
+ // standardize on singular form
1136
+ units = units.replace(/s$/, '');
1137
+ }
1138
+
1139
+ if (units === 'year' || units === 'month') {
1140
+ diff = (this.daysInMonth() + that.daysInMonth()) * 432e5; // 24 * 60 * 60 * 1000 / 2
1141
+ output = ((this.year() - that.year()) * 12) + (this.month() - that.month());
1142
+ output += ((this - moment(this).startOf('month')) - (that - moment(that).startOf('month'))) / diff;
1143
+ if (units === 'year') {
1144
+ output = output / 12;
1145
+ }
1003
1146
  } else {
1004
- output = val === 'seconds' ? diff / 1e3 : // 1000
1005
- val === 'minutes' ? diff / 6e4 : // 1000 * 60
1006
- val === 'hours' ? diff / 36e5 : // 1000 * 60 * 60
1007
- val === 'days' ? diff / 864e5 : // 1000 * 60 * 60 * 24
1008
- val === 'weeks' ? diff / 6048e5 : // 1000 * 60 * 60 * 24 * 7
1147
+ diff = (this - that) - zoneDiff;
1148
+ output = units === 'second' ? diff / 1e3 : // 1000
1149
+ units === 'minute' ? diff / 6e4 : // 1000 * 60
1150
+ units === 'hour' ? diff / 36e5 : // 1000 * 60 * 60
1151
+ units === 'day' ? diff / 864e5 : // 1000 * 60 * 60 * 24
1152
+ units === 'week' ? diff / 6048e5 : // 1000 * 60 * 60 * 24 * 7
1009
1153
  diff;
1010
1154
  }
1011
- return asFloat ? output : round(output);
1155
+ return asFloat ? output : absRound(output);
1012
1156
  },
1013
1157
 
1014
1158
  from : function (time, withoutSuffix) {
1015
- return moment.duration(this.diff(time)).lang(this._lang).humanize(!withoutSuffix);
1159
+ return moment.duration(this.diff(time)).lang(this.lang()._abbr).humanize(!withoutSuffix);
1016
1160
  },
1017
1161
 
1018
1162
  fromNow : function (withoutSuffix) {
@@ -1020,16 +1164,14 @@
1020
1164
  },
1021
1165
 
1022
1166
  calendar : function () {
1023
- var diff = this.diff(moment().sod(), 'days', true),
1024
- calendar = this.lang().calendar,
1025
- allElse = calendar.sameElse,
1026
- format = diff < -6 ? allElse :
1027
- diff < -1 ? calendar.lastWeek :
1028
- diff < 0 ? calendar.lastDay :
1029
- diff < 1 ? calendar.sameDay :
1030
- diff < 2 ? calendar.nextDay :
1031
- diff < 7 ? calendar.nextWeek : allElse;
1032
- return this.format(typeof format === 'function' ? format.apply(this) : format);
1167
+ var diff = this.diff(moment().startOf('day'), 'days', true),
1168
+ format = diff < -6 ? 'sameElse' :
1169
+ diff < -1 ? 'lastWeek' :
1170
+ diff < 0 ? 'lastDay' :
1171
+ diff < 1 ? 'sameDay' :
1172
+ diff < 2 ? 'nextDay' :
1173
+ diff < 7 ? 'nextWeek' : 'sameElse';
1174
+ return this.format(this.lang().calendar(format, this));
1033
1175
  },
1034
1176
 
1035
1177
  isLeapYear : function () {
@@ -1048,16 +1190,18 @@
1048
1190
  this.add({ d : input - day });
1049
1191
  },
1050
1192
 
1051
- startOf: function (val) {
1193
+ startOf: function (units) {
1194
+ units = units.replace(/s$/, '');
1052
1195
  // the following switch intentionally omits break keywords
1053
1196
  // to utilize falling through the cases.
1054
- switch (val.replace(/s$/, '')) {
1197
+ switch (units) {
1055
1198
  case 'year':
1056
1199
  this.month(0);
1057
1200
  /* falls through */
1058
1201
  case 'month':
1059
1202
  this.date(1);
1060
1203
  /* falls through */
1204
+ case 'week':
1061
1205
  case 'day':
1062
1206
  this.hours(0);
1063
1207
  /* falls through */
@@ -1071,20 +1215,32 @@
1071
1215
  this.milliseconds(0);
1072
1216
  /* falls through */
1073
1217
  }
1218
+
1219
+ // weeks are a special case
1220
+ if (units === 'week') {
1221
+ this.day(0);
1222
+ }
1223
+
1074
1224
  return this;
1075
1225
  },
1076
1226
 
1077
- endOf: function (val) {
1078
- return this.startOf(val).add(val.replace(/s?$/, 's'), 1).subtract('ms', 1);
1227
+ endOf: function (units) {
1228
+ return this.startOf(units).add(units.replace(/s?$/, 's'), 1).subtract('ms', 1);
1079
1229
  },
1080
1230
 
1081
- sod: function () {
1082
- return this.clone().startOf('day');
1231
+ isAfter: function (input, units) {
1232
+ units = typeof units !== 'undefined' ? units : 'millisecond';
1233
+ return +this.clone().startOf(units) > +moment(input).startOf(units);
1083
1234
  },
1084
1235
 
1085
- eod: function () {
1086
- // end of day = start of day plus 1 day, minus 1 millisecond
1087
- return this.clone().endOf('day');
1236
+ isBefore: function (input, units) {
1237
+ units = typeof units !== 'undefined' ? units : 'millisecond';
1238
+ return +this.clone().startOf(units) < +moment(input).startOf(units);
1239
+ },
1240
+
1241
+ isSame: function (input, units) {
1242
+ units = typeof units !== 'undefined' ? units : 'millisecond';
1243
+ return +this.clone().startOf(units) === +moment(input).startOf(units);
1088
1244
  },
1089
1245
 
1090
1246
  zone : function () {
@@ -1095,14 +1251,29 @@
1095
1251
  return moment.utc([this.year(), this.month() + 1, 0]).date();
1096
1252
  },
1097
1253
 
1254
+ dayOfYear : function (input) {
1255
+ var dayOfYear = round((moment(this).startOf('day') - moment(this).startOf('year')) / 864e5) + 1;
1256
+ return input == null ? dayOfYear : this.add("d", (input - dayOfYear));
1257
+ },
1258
+
1259
+ isoWeek : function (input) {
1260
+ var week = weekOfYear(this, 1, 4);
1261
+ return input == null ? week : this.add("d", (input - week) * 7);
1262
+ },
1263
+
1264
+ week : function (input) {
1265
+ var week = this.lang().week(this);
1266
+ return input == null ? week : this.add("d", (input - week) * 7);
1267
+ },
1268
+
1098
1269
  // If passed a language key, it will set the language for this
1099
1270
  // instance. Otherwise, it will return the language configuration
1100
1271
  // variables for this instance.
1101
- lang : function (lang) {
1102
- if (lang === undefined) {
1103
- return getLangDefinition(this);
1272
+ lang : function (key) {
1273
+ if (key === undefined) {
1274
+ return this._lang;
1104
1275
  } else {
1105
- this._lang = lang;
1276
+ this._lang = getLangDefinition(key);
1106
1277
  return this;
1107
1278
  }
1108
1279
  }
@@ -1110,7 +1281,7 @@
1110
1281
 
1111
1282
  // helper for adding shortcuts
1112
1283
  function makeGetterAndSetter(name, key) {
1113
- moment.fn[name] = function (input) {
1284
+ moment.fn[name] = moment.fn[name + 's'] = function (input) {
1114
1285
  var utc = this._isUTC ? 'UTC' : '';
1115
1286
  if (input != null) {
1116
1287
  this._d['set' + utc + key](input);
@@ -1123,12 +1294,16 @@
1123
1294
 
1124
1295
  // loop through and add shortcuts (Month, Date, Hours, Minutes, Seconds, Milliseconds)
1125
1296
  for (i = 0; i < proxyGettersAndSetters.length; i ++) {
1126
- makeGetterAndSetter(proxyGettersAndSetters[i].toLowerCase(), proxyGettersAndSetters[i]);
1297
+ makeGetterAndSetter(proxyGettersAndSetters[i].toLowerCase().replace(/s$/, ''), proxyGettersAndSetters[i]);
1127
1298
  }
1128
1299
 
1129
1300
  // add shortcut for year (uses different syntax than the getter/setter 'year' == 'FullYear')
1130
1301
  makeGetterAndSetter('year', 'FullYear');
1131
1302
 
1303
+ // add plural methods
1304
+ moment.fn.days = moment.fn.day;
1305
+ moment.fn.weeks = moment.fn.week;
1306
+ moment.fn.isoWeeks = moment.fn.isoWeek;
1132
1307
 
1133
1308
  /************************************
1134
1309
  Duration Prototype
@@ -1148,19 +1323,13 @@
1148
1323
 
1149
1324
  humanize : function (withSuffix) {
1150
1325
  var difference = +this,
1151
- rel = this.lang().relativeTime,
1152
- output = relativeTime(difference, !withSuffix, this.lang()),
1153
- fromNow = difference <= 0 ? rel.past : rel.future;
1326
+ output = relativeTime(difference, !withSuffix, this.lang());
1154
1327
 
1155
1328
  if (withSuffix) {
1156
- if (typeof fromNow === 'function') {
1157
- output = fromNow(output);
1158
- } else {
1159
- output = fromNow.replace(/%s/i, output);
1160
- }
1329
+ output = this.lang().pastFuture(difference, output);
1161
1330
  }
1162
1331
 
1163
- return output;
1332
+ return this.lang().postformat(output);
1164
1333
  },
1165
1334
 
1166
1335
  lang : moment.fn.lang
@@ -1188,6 +1357,24 @@
1188
1357
  makeDurationAsGetter('Weeks', 6048e5);
1189
1358
 
1190
1359
 
1360
+ /************************************
1361
+ Default Lang
1362
+ ************************************/
1363
+
1364
+
1365
+ // Set default language, other languages will inherit from English.
1366
+ moment.lang('en', {
1367
+ ordinal : function (number) {
1368
+ var b = number % 10,
1369
+ output = (~~ (number % 100 / 10) === 1) ? 'th' :
1370
+ (b === 1) ? 'st' :
1371
+ (b === 2) ? 'nd' :
1372
+ (b === 3) ? 'rd' : 'th';
1373
+ return number + output;
1374
+ }
1375
+ });
1376
+
1377
+
1191
1378
  /************************************
1192
1379
  Exposing Moment
1193
1380
  ************************************/