d9fb7121ca1595128e7f7c2069fcc8cd58fa7fab5d75225c1628649e9af7f51a3732553d988f9e322e8214fc516b061252d3fd337602f3dbff920ac1c85404 9.6 KB

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