dd50c58d69a3ce7b9151efede19822230ca631d872efd0d091fb8a015fe9b030695ebd021202cf521f96b763392b42944bcaae325c1fd6f3e17f7f098f7ac7 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. import { get } from '../moment/get-set';
  2. import hasOwnProp from '../utils/has-own-prop';
  3. import { addFormatToken } from '../format/format';
  4. import { addUnitAlias } from './aliases';
  5. import { addUnitPriority } from './priorities';
  6. import { addRegexToken, match1to2, match2, matchWord, regexEscape } from '../parse/regex';
  7. import { addParseToken } from '../parse/token';
  8. import { hooks } from '../utils/hooks';
  9. import { MONTH } from './constants';
  10. import toInt from '../utils/to-int';
  11. import isArray from '../utils/is-array';
  12. import indexOf from '../utils/index-of';
  13. import { createUTC } from '../create/utc';
  14. import getParsingFlags from '../create/parsing-flags';
  15. export function daysInMonth(year, month) {
  16. return new Date(Date.UTC(year, month + 1, 0)).getUTCDate();
  17. }
  18. // FORMATTING
  19. addFormatToken('M', ['MM', 2], 'Mo', function () {
  20. return this.month() + 1;
  21. });
  22. addFormatToken('MMM', 0, 0, function (format) {
  23. return this.localeData().monthsShort(this, format);
  24. });
  25. addFormatToken('MMMM', 0, 0, function (format) {
  26. return this.localeData().months(this, format);
  27. });
  28. // ALIASES
  29. addUnitAlias('month', 'M');
  30. // PRIORITY
  31. addUnitPriority('month', 8);
  32. // PARSING
  33. addRegexToken('M', match1to2);
  34. addRegexToken('MM', match1to2, match2);
  35. addRegexToken('MMM', function (isStrict, locale) {
  36. return locale.monthsShortRegex(isStrict);
  37. });
  38. addRegexToken('MMMM', function (isStrict, locale) {
  39. return locale.monthsRegex(isStrict);
  40. });
  41. addParseToken(['M', 'MM'], function (input, array) {
  42. array[MONTH] = toInt(input) - 1;
  43. });
  44. addParseToken(['MMM', 'MMMM'], function (input, array, config, token) {
  45. var month = config._locale.monthsParse(input, token, config._strict);
  46. // if we didn't find a month name, mark the date as invalid.
  47. if (month != null) {
  48. array[MONTH] = month;
  49. } else {
  50. getParsingFlags(config).invalidMonth = input;
  51. }
  52. });
  53. // LOCALES
  54. var MONTHS_IN_FORMAT = /D[oD]?(\[[^\[\]]*\]|\s+)+MMMM?/;
  55. export var defaultLocaleMonths = 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_');
  56. export function localeMonths (m, format) {
  57. if (!m) {
  58. return this._months;
  59. }
  60. return isArray(this._months) ? this._months[m.month()] :
  61. this._months[(this._months.isFormat || MONTHS_IN_FORMAT).test(format) ? 'format' : 'standalone'][m.month()];
  62. }
  63. export var defaultLocaleMonthsShort = 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_');
  64. export function localeMonthsShort (m, format) {
  65. if (!m) {
  66. return this._monthsShort;
  67. }
  68. return isArray(this._monthsShort) ? this._monthsShort[m.month()] :
  69. this._monthsShort[MONTHS_IN_FORMAT.test(format) ? 'format' : 'standalone'][m.month()];
  70. }
  71. function handleStrictParse(monthName, format, strict) {
  72. var i, ii, mom, llc = monthName.toLocaleLowerCase();
  73. if (!this._monthsParse) {
  74. // this is not used
  75. this._monthsParse = [];
  76. this._longMonthsParse = [];
  77. this._shortMonthsParse = [];
  78. for (i = 0; i < 12; ++i) {
  79. mom = createUTC([2000, i]);
  80. this._shortMonthsParse[i] = this.monthsShort(mom, '').toLocaleLowerCase();
  81. this._longMonthsParse[i] = this.months(mom, '').toLocaleLowerCase();
  82. }
  83. }
  84. if (strict) {
  85. if (format === 'MMM') {
  86. ii = indexOf.call(this._shortMonthsParse, llc);
  87. return ii !== -1 ? ii : null;
  88. } else {
  89. ii = indexOf.call(this._longMonthsParse, llc);
  90. return ii !== -1 ? ii : null;
  91. }
  92. } else {
  93. if (format === 'MMM') {
  94. ii = indexOf.call(this._shortMonthsParse, llc);
  95. if (ii !== -1) {
  96. return ii;
  97. }
  98. ii = indexOf.call(this._longMonthsParse, llc);
  99. return ii !== -1 ? ii : null;
  100. } else {
  101. ii = indexOf.call(this._longMonthsParse, llc);
  102. if (ii !== -1) {
  103. return ii;
  104. }
  105. ii = indexOf.call(this._shortMonthsParse, llc);
  106. return ii !== -1 ? ii : null;
  107. }
  108. }
  109. }
  110. export function localeMonthsParse (monthName, format, strict) {
  111. var i, mom, regex;
  112. if (this._monthsParseExact) {
  113. return handleStrictParse.call(this, monthName, format, strict);
  114. }
  115. if (!this._monthsParse) {
  116. this._monthsParse = [];
  117. this._longMonthsParse = [];
  118. this._shortMonthsParse = [];
  119. }
  120. // TODO: add sorting
  121. // Sorting makes sure if one month (or abbr) is a prefix of another
  122. // see sorting in computeMonthsParse
  123. for (i = 0; i < 12; i++) {
  124. // make the regex if we don't have it already
  125. mom = createUTC([2000, i]);
  126. if (strict && !this._longMonthsParse[i]) {
  127. this._longMonthsParse[i] = new RegExp('^' + this.months(mom, '').replace('.', '') + '$', 'i');
  128. this._shortMonthsParse[i] = new RegExp('^' + this.monthsShort(mom, '').replace('.', '') + '$', 'i');
  129. }
  130. if (!strict && !this._monthsParse[i]) {
  131. regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, '');
  132. this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i');
  133. }
  134. // test the regex
  135. if (strict && format === 'MMMM' && this._longMonthsParse[i].test(monthName)) {
  136. return i;
  137. } else if (strict && format === 'MMM' && this._shortMonthsParse[i].test(monthName)) {
  138. return i;
  139. } else if (!strict && this._monthsParse[i].test(monthName)) {
  140. return i;
  141. }
  142. }
  143. }
  144. // MOMENTS
  145. export function setMonth (mom, value) {
  146. var dayOfMonth;
  147. if (!mom.isValid()) {
  148. // No op
  149. return mom;
  150. }
  151. if (typeof value === 'string') {
  152. if (/^\d+$/.test(value)) {
  153. value = toInt(value);
  154. } else {
  155. value = mom.localeData().monthsParse(value);
  156. // TODO: Another silent failure?
  157. if (typeof value !== 'number') {
  158. return mom;
  159. }
  160. }
  161. }
  162. dayOfMonth = Math.min(mom.date(), daysInMonth(mom.year(), value));
  163. mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth);
  164. return mom;
  165. }
  166. export function getSetMonth (value) {
  167. if (value != null) {
  168. setMonth(this, value);
  169. hooks.updateOffset(this, true);
  170. return this;
  171. } else {
  172. return get(this, 'Month');
  173. }
  174. }
  175. export function getDaysInMonth () {
  176. return daysInMonth(this.year(), this.month());
  177. }
  178. var defaultMonthsShortRegex = matchWord;
  179. export function monthsShortRegex (isStrict) {
  180. if (this._monthsParseExact) {
  181. if (!hasOwnProp(this, '_monthsRegex')) {
  182. computeMonthsParse.call(this);
  183. }
  184. if (isStrict) {
  185. return this._monthsShortStrictRegex;
  186. } else {
  187. return this._monthsShortRegex;
  188. }
  189. } else {
  190. if (!hasOwnProp(this, '_monthsShortRegex')) {
  191. this._monthsShortRegex = defaultMonthsShortRegex;
  192. }
  193. return this._monthsShortStrictRegex && isStrict ?
  194. this._monthsShortStrictRegex : this._monthsShortRegex;
  195. }
  196. }
  197. var defaultMonthsRegex = matchWord;
  198. export function monthsRegex (isStrict) {
  199. if (this._monthsParseExact) {
  200. if (!hasOwnProp(this, '_monthsRegex')) {
  201. computeMonthsParse.call(this);
  202. }
  203. if (isStrict) {
  204. return this._monthsStrictRegex;
  205. } else {
  206. return this._monthsRegex;
  207. }
  208. } else {
  209. if (!hasOwnProp(this, '_monthsRegex')) {
  210. this._monthsRegex = defaultMonthsRegex;
  211. }
  212. return this._monthsStrictRegex && isStrict ?
  213. this._monthsStrictRegex : this._monthsRegex;
  214. }
  215. }
  216. function computeMonthsParse () {
  217. function cmpLenRev(a, b) {
  218. return b.length - a.length;
  219. }
  220. var shortPieces = [], longPieces = [], mixedPieces = [],
  221. i, mom;
  222. for (i = 0; i < 12; i++) {
  223. // make the regex if we don't have it already
  224. mom = createUTC([2000, i]);
  225. shortPieces.push(this.monthsShort(mom, ''));
  226. longPieces.push(this.months(mom, ''));
  227. mixedPieces.push(this.months(mom, ''));
  228. mixedPieces.push(this.monthsShort(mom, ''));
  229. }
  230. // Sorting makes sure if one month (or abbr) is a prefix of another it
  231. // will match the longer piece.
  232. shortPieces.sort(cmpLenRev);
  233. longPieces.sort(cmpLenRev);
  234. mixedPieces.sort(cmpLenRev);
  235. for (i = 0; i < 12; i++) {
  236. shortPieces[i] = regexEscape(shortPieces[i]);
  237. longPieces[i] = regexEscape(longPieces[i]);
  238. }
  239. for (i = 0; i < 24; i++) {
  240. mixedPieces[i] = regexEscape(mixedPieces[i]);
  241. }
  242. this._monthsRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i');
  243. this._monthsShortRegex = this._monthsRegex;
  244. this._monthsStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i');
  245. this._monthsShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i');
  246. }