index.js
1 'use strict' 2 3 var DATE_TIME = /(\d{1,})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})(\.\d{1,})?.*?( BC)?$/ 4 var DATE = /^(\d{1,})-(\d{2})-(\d{2})( BC)?$/ 5 var TIME_ZONE = /([Z+-])(\d{2})?:?(\d{2})?:?(\d{2})?/ 6 var INFINITY = /^-?infinity$/ 7 8 module.exports = function parseDate (isoDate) { 9 if (INFINITY.test(isoDate)) { 10 // Capitalize to Infinity before passing to Number 11 return Number(isoDate.replace('i', 'I')) 12 } 13 var matches = DATE_TIME.exec(isoDate) 14 15 if (!matches) { 16 // Force YYYY-MM-DD dates to be parsed as local time 17 return getDate(isoDate) || null 18 } 19 20 var isBC = !!matches[8] 21 var year = parseInt(matches[1], 10) 22 if (isBC) { 23 year = bcYearToNegativeYear(year) 24 } 25 26 var month = parseInt(matches[2], 10) - 1 27 var day = matches[3] 28 var hour = parseInt(matches[4], 10) 29 var minute = parseInt(matches[5], 10) 30 var second = parseInt(matches[6], 10) 31 32 var ms = matches[7] 33 ms = ms ? 1000 * parseFloat(ms) : 0 34 35 var date 36 var offset = timeZoneOffset(isoDate) 37 if (offset != null) { 38 date = new Date(Date.UTC(year, month, day, hour, minute, second, ms)) 39 40 // Account for years from 0 to 99 being interpreted as 1900-1999 41 // by Date.UTC / the multi-argument form of the Date constructor 42 if (is0To99(year)) { 43 date.setUTCFullYear(year) 44 } 45 46 if (offset !== 0) { 47 date.setTime(date.getTime() - offset) 48 } 49 } else { 50 date = new Date(year, month, day, hour, minute, second, ms) 51 52 if (is0To99(year)) { 53 date.setFullYear(year) 54 } 55 } 56 57 return date 58 } 59 60 function getDate (isoDate) { 61 var matches = DATE.exec(isoDate) 62 if (!matches) { 63 return 64 } 65 66 var year = parseInt(matches[1], 10) 67 var isBC = !!matches[4] 68 if (isBC) { 69 year = bcYearToNegativeYear(year) 70 } 71 72 var month = parseInt(matches[2], 10) - 1 73 var day = matches[3] 74 // YYYY-MM-DD will be parsed as local time 75 var date = new Date(year, month, day) 76 77 if (is0To99(year)) { 78 date.setFullYear(year) 79 } 80 81 return date 82 } 83 84 // match timezones: 85 // Z (UTC) 86 // -05 87 // +06:30 88 function timeZoneOffset (isoDate) { 89 if (isoDate.endsWith('+00')) { 90 return 0 91 } 92 93 var zone = TIME_ZONE.exec(isoDate.split(' ')[1]) 94 if (!zone) return 95 var type = zone[1] 96 97 if (type === 'Z') { 98 return 0 99 } 100 var sign = type === '-' ? -1 : 1 101 var offset = parseInt(zone[2], 10) * 3600 + 102 parseInt(zone[3] || 0, 10) * 60 + 103 parseInt(zone[4] || 0, 10) 104 105 return offset * sign * 1000 106 } 107 108 function bcYearToNegativeYear (year) { 109 // Account for numerical difference between representations of BC years 110 // See: https://github.com/bendrucker/postgres-date/issues/5 111 return -(year - 1) 112 } 113 114 function is0To99 (num) { 115 return num >= 0 && num < 100 116 }