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