123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320 |
- var isDate = require('../is_date/index.js')
- var MILLISECONDS_IN_HOUR = 3600000
- var MILLISECONDS_IN_MINUTE = 60000
- var DEFAULT_ADDITIONAL_DIGITS = 2
- var parseTokenDateTimeDelimeter = /[T ]/
- var parseTokenPlainTime = /:/
- // year tokens
- var parseTokenYY = /^(\d{2})$/
- var parseTokensYYY = [
- /^([+-]\d{2})$/, // 0 additional digits
- /^([+-]\d{3})$/, // 1 additional digit
- /^([+-]\d{4})$/ // 2 additional digits
- ]
- var parseTokenYYYY = /^(\d{4})/
- var parseTokensYYYYY = [
- /^([+-]\d{4})/, // 0 additional digits
- /^([+-]\d{5})/, // 1 additional digit
- /^([+-]\d{6})/ // 2 additional digits
- ]
- // date tokens
- var parseTokenMM = /^-(\d{2})$/
- var parseTokenDDD = /^-?(\d{3})$/
- var parseTokenMMDD = /^-?(\d{2})-?(\d{2})$/
- var parseTokenWww = /^-?W(\d{2})$/
- var parseTokenWwwD = /^-?W(\d{2})-?(\d{1})$/
- // time tokens
- var parseTokenHH = /^(\d{2}([.,]\d*)?)$/
- var parseTokenHHMM = /^(\d{2}):?(\d{2}([.,]\d*)?)$/
- var parseTokenHHMMSS = /^(\d{2}):?(\d{2}):?(\d{2}([.,]\d*)?)$/
- // timezone tokens
- var parseTokenTimezone = /([Z+-].*)$/
- var parseTokenTimezoneZ = /^(Z)$/
- var parseTokenTimezoneHH = /^([+-])(\d{2})$/
- var parseTokenTimezoneHHMM = /^([+-])(\d{2}):?(\d{2})$/
- /**
- * @category Common Helpers
- * @summary Convert the given argument to an instance of Date.
- *
- * @description
- * Convert the given argument to an instance of Date.
- *
- * If the argument is an instance of Date, the function returns its clone.
- *
- * If the argument is a number, it is treated as a timestamp.
- *
- * If an argument is a string, the function tries to parse it.
- * Function accepts complete ISO 8601 formats as well as partial implementations.
- * ISO 8601: http://en.wikipedia.org/wiki/ISO_8601
- *
- * If all above fails, the function passes the given argument to Date constructor.
- *
- * @param {Date|String|Number} argument - the value to convert
- * @param {Object} [options] - the object with options
- * @param {0 | 1 | 2} [options.additionalDigits=2] - the additional number of digits in the extended year format
- * @returns {Date} the parsed date in the local time zone
- *
- * @example
- * // Convert string '2014-02-11T11:30:30' to date:
- * var result = parse('2014-02-11T11:30:30')
- * //=> Tue Feb 11 2014 11:30:30
- *
- * @example
- * // Parse string '+02014101',
- * // if the additional number of digits in the extended year format is 1:
- * var result = parse('+02014101', {additionalDigits: 1})
- * //=> Fri Apr 11 2014 00:00:00
- */
- function parse (argument, dirtyOptions) {
- if (isDate(argument)) {
- // Prevent the date to lose the milliseconds when passed to new Date() in IE10
- return new Date(argument.getTime())
- } else if (typeof argument !== 'string') {
- return new Date(argument)
- }
- var options = dirtyOptions || {}
- var additionalDigits = options.additionalDigits
- if (additionalDigits == null) {
- additionalDigits = DEFAULT_ADDITIONAL_DIGITS
- } else {
- additionalDigits = Number(additionalDigits)
- }
- var dateStrings = splitDateString(argument)
- var parseYearResult = parseYear(dateStrings.date, additionalDigits)
- var year = parseYearResult.year
- var restDateString = parseYearResult.restDateString
- var date = parseDate(restDateString, year)
- if (date) {
- var timestamp = date.getTime()
- var time = 0
- var offset
- if (dateStrings.time) {
- time = parseTime(dateStrings.time)
- }
- if (dateStrings.timezone) {
- offset = parseTimezone(dateStrings.timezone)
- } else {
- // get offset accurate to hour in timezones that change offset
- offset = new Date(timestamp + time).getTimezoneOffset()
- offset = new Date(timestamp + time + offset * MILLISECONDS_IN_MINUTE).getTimezoneOffset()
- }
- return new Date(timestamp + time + offset * MILLISECONDS_IN_MINUTE)
- } else {
- return new Date(argument)
- }
- }
- function splitDateString (dateString) {
- var dateStrings = {}
- var array = dateString.split(parseTokenDateTimeDelimeter)
- var timeString
- if (parseTokenPlainTime.test(array[0])) {
- dateStrings.date = null
- timeString = array[0]
- } else {
- dateStrings.date = array[0]
- timeString = array[1]
- }
- if (timeString) {
- var token = parseTokenTimezone.exec(timeString)
- if (token) {
- dateStrings.time = timeString.replace(token[1], '')
- dateStrings.timezone = token[1]
- } else {
- dateStrings.time = timeString
- }
- }
- return dateStrings
- }
- function parseYear (dateString, additionalDigits) {
- var parseTokenYYY = parseTokensYYY[additionalDigits]
- var parseTokenYYYYY = parseTokensYYYYY[additionalDigits]
- var token
- // YYYY or ±YYYYY
- token = parseTokenYYYY.exec(dateString) || parseTokenYYYYY.exec(dateString)
- if (token) {
- var yearString = token[1]
- return {
- year: parseInt(yearString, 10),
- restDateString: dateString.slice(yearString.length)
- }
- }
- // YY or ±YYY
- token = parseTokenYY.exec(dateString) || parseTokenYYY.exec(dateString)
- if (token) {
- var centuryString = token[1]
- return {
- year: parseInt(centuryString, 10) * 100,
- restDateString: dateString.slice(centuryString.length)
- }
- }
- // Invalid ISO-formatted year
- return {
- year: null
- }
- }
- function parseDate (dateString, year) {
- // Invalid ISO-formatted year
- if (year === null) {
- return null
- }
- var token
- var date
- var month
- var week
- // YYYY
- if (dateString.length === 0) {
- date = new Date(0)
- date.setUTCFullYear(year)
- return date
- }
- // YYYY-MM
- token = parseTokenMM.exec(dateString)
- if (token) {
- date = new Date(0)
- month = parseInt(token[1], 10) - 1
- date.setUTCFullYear(year, month)
- return date
- }
- // YYYY-DDD or YYYYDDD
- token = parseTokenDDD.exec(dateString)
- if (token) {
- date = new Date(0)
- var dayOfYear = parseInt(token[1], 10)
- date.setUTCFullYear(year, 0, dayOfYear)
- return date
- }
- // YYYY-MM-DD or YYYYMMDD
- token = parseTokenMMDD.exec(dateString)
- if (token) {
- date = new Date(0)
- month = parseInt(token[1], 10) - 1
- var day = parseInt(token[2], 10)
- date.setUTCFullYear(year, month, day)
- return date
- }
- // YYYY-Www or YYYYWww
- token = parseTokenWww.exec(dateString)
- if (token) {
- week = parseInt(token[1], 10) - 1
- return dayOfISOYear(year, week)
- }
- // YYYY-Www-D or YYYYWwwD
- token = parseTokenWwwD.exec(dateString)
- if (token) {
- week = parseInt(token[1], 10) - 1
- var dayOfWeek = parseInt(token[2], 10) - 1
- return dayOfISOYear(year, week, dayOfWeek)
- }
- // Invalid ISO-formatted date
- return null
- }
- function parseTime (timeString) {
- var token
- var hours
- var minutes
- // hh
- token = parseTokenHH.exec(timeString)
- if (token) {
- hours = parseFloat(token[1].replace(',', '.'))
- return (hours % 24) * MILLISECONDS_IN_HOUR
- }
- // hh:mm or hhmm
- token = parseTokenHHMM.exec(timeString)
- if (token) {
- hours = parseInt(token[1], 10)
- minutes = parseFloat(token[2].replace(',', '.'))
- return (hours % 24) * MILLISECONDS_IN_HOUR +
- minutes * MILLISECONDS_IN_MINUTE
- }
- // hh:mm:ss or hhmmss
- token = parseTokenHHMMSS.exec(timeString)
- if (token) {
- hours = parseInt(token[1], 10)
- minutes = parseInt(token[2], 10)
- var seconds = parseFloat(token[3].replace(',', '.'))
- return (hours % 24) * MILLISECONDS_IN_HOUR +
- minutes * MILLISECONDS_IN_MINUTE +
- seconds * 1000
- }
- // Invalid ISO-formatted time
- return null
- }
- function parseTimezone (timezoneString) {
- var token
- var absoluteOffset
- // Z
- token = parseTokenTimezoneZ.exec(timezoneString)
- if (token) {
- return 0
- }
- // ±hh
- token = parseTokenTimezoneHH.exec(timezoneString)
- if (token) {
- absoluteOffset = parseInt(token[2], 10) * 60
- return (token[1] === '+') ? -absoluteOffset : absoluteOffset
- }
- // ±hh:mm or ±hhmm
- token = parseTokenTimezoneHHMM.exec(timezoneString)
- if (token) {
- absoluteOffset = parseInt(token[2], 10) * 60 + parseInt(token[3], 10)
- return (token[1] === '+') ? -absoluteOffset : absoluteOffset
- }
- return 0
- }
- function dayOfISOYear (isoYear, week, day) {
- week = week || 0
- day = day || 0
- var date = new Date(0)
- date.setUTCFullYear(isoYear, 0, 4)
- var fourthOfJanuaryDay = date.getUTCDay() || 7
- var diff = week * 7 + day + 1 - fourthOfJanuaryDay
- date.setUTCDate(date.getUTCDate() + diff)
- return date
- }
- module.exports = parse
|