CARVIEW |
Select Language
HTTP/2 301
location: https://raw.githubusercontent.com/MatthewMueller/date/master/dist/date.js
accept-ranges: bytes
age: 0
date: Fri, 25 Jul 2025 20:03:09 GMT
via: 1.1 varnish
x-served-by: cache-bom-vanm7210023-BOM
x-cache: MISS
x-cache-hits: 0
x-timer: S1753473788.041240,VS0,VE1239
vary: Accept-Encoding
x-fastly-request-id: c4a55897a8050d7e09cb862f80566650afb4b66b
content-length: 0
HTTP/2 200
cache-control: max-age=300
content-security-policy: default-src 'none'; style-src 'unsafe-inline'; sandbox
content-type: text/plain; charset=utf-8
etag: W/"b1a79639110c926851593cebea74f55dabe76cf55ef47b214c04436027d83da3"
strict-transport-security: max-age=31536000
x-content-type-options: nosniff
x-frame-options: deny
x-xss-protection: 1; mode=block
x-github-request-id: 3651:2AA1AF:C650:28F0D:6883E2FA
content-encoding: gzip
accept-ranges: bytes
date: Fri, 25 Jul 2025 20:03:09 GMT
via: 1.1 varnish
x-served-by: cache-bom-vanm7210072-BOM
x-cache: MISS
x-cache-hits: 0
x-timer: S1753473789.337518,VS0,VE263
vary: Authorization,Accept-Encoding
access-control-allow-origin: *
cross-origin-resource-policy: cross-origin
x-fastly-request-id: f016d5efc777cb7bf82aa69b4df87b2953a2c1d6
expires: Fri, 25 Jul 2025 20:08:09 GMT
source-age: 0
content-length: 25777
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.date = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i 0) --n
diff += (7 * n)
this.update(diff * _day)
return this
}
/**
* Update the date
*
* @param {Number} ms
* @return {Date}
* @api private
*/
date.prototype.update = function (ms) {
this.date = new Date(this.date.getTime() + ms)
return this
}
/**
* leap year
*
* @param {Number} yr
* @return {Boolean}
*/
function leapyear (yr) {
return (yr % 4 === 0 && yr % 100 !== 0) || yr % 400 === 0
}
},{"debug":10}],3:[function(require,module,exports){
module.exports={
"op": {
"plus": ["and", "plus", "+", "add", "on"],
"minus": ["minus", "subtract"],
"times": ["times", "multiply"],
"divide": ["divide"]
},
"o": {
"plus": ["at", "in", "past", "late", "later", "after", "next", "from", "start", "starting", "since", "coming"],
"minus": ["last", "minus", "subtract", "ago", "before", "from"]
},
"n": {
"0.25": ["quarter"],
"0.5": ["half", "1/2", "half an"],
"0": ["zero"],
"1": ["one", "a", "an", "first"],
"2": ["two", "second"],
"3": ["three", "third"],
"4": ["four", "fourth"],
"5": ["five", "fifth"],
"6": ["six", "sixth"],
"7": ["seven", "seventh"],
"8": ["eight", "eighth"],
"9": ["nine", "ninth"],
"10": ["ten", "tenth"],
"11": ["eleven", "eleventh"],
"12": ["twelve", "twelveth"],
"13": ["thirteen", "thirteenth"],
"14": ["fourteen", "fourteenth"],
"15": ["fifteen", "fifteenth"],
"16": ["sixteen", "sixteenth"],
"17": ["seventeen", "seventeenth"],
"18": ["eighteen", "eighteenth"],
"19": ["nineteen", "nineteenth"],
"20": ["twenty", "twentieth"],
"30": ["thirty", "thirtieth"],
"40": ["fourty", "fourtieth"],
"50": ["fifty", "fiftieth"],
"60": ["sixty", "sixtieth"],
"70": ["seventy", "seventieth"],
"80": ["eighty", "eightieth"],
"90": ["ninety", "ninetieth"],
"100": ["hundred", "hundreds", "hundredth"],
"1000": ["thousand", "thousands", "thousandth", "k", "K"]
},
"t": {
},
"dt": {
"s": ["second", "seconds", "s", "sec", "secs"],
"m": ["minute", "minutes", "m", "min", "mins"],
"h": ["hour", "hours", "h", "hr", "hrs"],
"d": ["day", "days", "d", "dai"],
"w": ["week", "weeks", "w", "wk", "wks"],
"M": ["month", "months", "monthes", "M", "mo", "moon", "moons"],
"y": ["year", "years", "y", "yr", "yrs"]
},
"T": {
"t:,dt:=3h": ["later", "soon"],
"t:=1d,dt:": ["st", "nd", "rd", "th", "st day", "nd day", "rd day", "th day"],
"t:,dt:1w": ["st week", "nd week", "rd week", "th week"],
"t:,dt:14d": ["day", "fortnight"],
"t:=0h=0m=0s1mer,dt:": ["pm", "p.m", "p.m.", "noon"],
"t:,dt:1d": ["tomorrow", "tmr"],
"t:,dt:-1d": ["yesterday", "ytd"],
"t:,0dt:": ["today"],
"t:=2h=0m=0s1mer,dt:": ["afternoon"],
"t:=6h=0m=0s0mer,dt:": ["dawn"],
"t:=7h=0m=0s0mer,dt:": ["am", "a.m", "a.m."],
"t:=7h=0m=0s1mer,dt:": ["evening"],
"t:=8h=0m=0s0mer,dt:": ["morning"],
"t:=9h=0m=0s1mer,dt:": ["tonight", "night"],
"t:=0h=0m=0s0mer,dt:1d": ["midnight"],
"t:,dt:=0w0wd": ["sunday", "sun"],
"t:,dt:=0w1wd": ["monday", "mon"],
"t:,dt:=0w2wd": ["tuesday", "tue", "tues"],
"t:,dt:=0w3wd": ["wednesday", "wed"],
"t:,dt:=0w4wd": ["thursday", "thu", "thur", "thurs"],
"t:,dt:=0w5wd": ["friday", "fri"],
"t:,dt:=0w6wd": ["saturday", "sat"],
"t:1M=1d,dt:": ["january", "jan"],
"t:2M=1d,dt:": ["february", "feb"],
"t:3M=1d,dt:": ["march", "mar"],
"t:4M=1d,dt:": ["april", "apr"],
"t:5M=1d,dt:": ["may"],
"t:6M=1d,dt:": ["june", "jun"],
"t:7M=1d,dt:": ["july", "jul"],
"t:8M=1d,dt:": ["august", "aug"],
"t:9M=1d,dt:": ["september", "sept", "sep"],
"t:10M=1d,dt:": ["october", "oct"],
"t:11M=1d,dt:": ["november", "nov"],
"t:12M=1d,dt:": ["december", "dec"],
"t:12M25d,dt:": ["christmas"]
},
"f": {
"1": ["once"],
"2": ["twice"]
}
}
},{}],4:[function(require,module,exports){
// Production rule module for the CFG
// !leap year
// !proper carry considering # of days per month
/**
* Module Dependencies
*/
var _ = require('./subdash')
var util = require('./util')
var symbol = require('./symbol')
var tokenize = require('./tokenize')
/**
* Export `norm`
*/
module.exports = norm
// a partial implementation of norm
/**
* Preprocess a string using the human language for time CFG, return a triple of original str, preprocessed tokens, and the normal forms (extracted dates in normal forms)
*/
function norm (str, offset) {
try {
// Production rules: CFG algorithm for human language for time
var tokObj = tokenize(str)
// console.log('p#0: parse normal forms', tokObj)
var syms = pickTokens(tokObj.symbols) || []
// console.log('p#0: remove nulls, pick tokens', syms)
syms = reduce(syms, ['n', 'n'])
// console.log('p#1: arithmetics: [] ~ , + if n1 > n2, * else', syms)
syms = nTnRedistribute(syms)
// console.log('p#2: redistribute, [] ~ [] ', syms)
syms = reduce(syms, ['o', 'o'])
// console.log('p#3: ~ *', syms)
// preprocessing ends, now format output
var restored = restoreTokens(syms, tokObj)
return restored
} catch (e) {
return {
str: str,
tokens: [],
normals: []
}
}
}
/**
* format a preprocessed array of symbols back into string, using some info from tokObj
*/
function restoreTokens (syms, tokObj) {
var tokens = [],
normals = [],
tokensOut = tokObj.tokensOut,
tokensIn = tokObj.tokensIn
syms = util.removeTnPlus(syms)
for (var i = 0; i < syms.length; i++) {
var s = syms[i],
sName = util.sName(s),
token = ''
switch (sName) {
case 'n':
// if token is already numeric, use it
token = (s.token.match(/^\s*[\d\.\-\+]+\s*$/)) ? s.token.trim() : s.value.toString()
break
case 'T':
// handles shits like 1 am ~ t:1h00m,dt:, am (token returned)
token = restoreNormal(s)
break
default:
// the other cases like op, o, cron, range
token = s.token.toString()
}
// extract the protected normal string
if (typeof token == 'string') {
tokens.push(token)
} else {
// get protected normal forms
normals.push(token.normal)
}
}
return {
tokens: tokens,
str: tokens.join(' ').replace(/\s+/g, ' '),
normals: normals
}
}
/**
* Given a T symbol, try to restore its normal form (return wrapped in JSON if it's a complete date string {normal: }), or just return the plain string as token
*/
function restoreNormal (T) {
var token = T.token
if (token.match(util.reT)) {
// if it is normal form, convert back into the normal1 or normal2 strings
var split = util.splitT(token)
if (_.includes(split, undefined)) {
// if it's normal2 form
// either it's a date or time
var dateArr = split.slice(0, 3),
timeArr = split.slice(3)
if (timeArr[0] != undefined) {
// check time first, it's first signature (hour) is defined
// return hh:mm
return util.TtoStdT(token).match(/(\d+\:\d+)/)[1]
} else {
// else it's a date, parse arr and return complete stdT instead
// return wrapped in JSON if it's a complete date string
return { normal: util.TtoStdT(token) }
}
} else {
// if it's normal1 form, use TtoStd
// return wrapped in JSON if it's a complete date string
return { normal: util.TtoStdT(token) }
}
} else if (!util.has_t(T) && util.has_dt(T) && util.has_pureTimeUnit(T)) {
// handle pure dt: T that are purel displacement, e.g. week, fortnight
var dtStr = '',
units = _.keys(T.dt),
dt = T.dt
// accumulate dtStr
for (var i = 0; i < units.length; i++) {
var u = units[i],
kval = parseFloat(dt[u]),
// set number has default, or is 0, 1
numStr = (kval != dt[u] || kval == 0 || Math.abs(kval) == 1) ? '' : dt[u].toString() + ' '
// set canon from lemma only if it exists, and key is word, else use u
var canon = u
if (T.canon != undefined) {
// and if it's also a timeUnit
canon = T.canon
} else {
// get the lemma for u, its canon and key
var lemma = util.lemma(u),
lemmaCanon = lemma.canon,
lemmaKey = lemma.value
if (lemmaKey && lemmaKey.match(/^\w+$/)) { canon = lemmaCanon }
}
// set the units, number, and canonical form of the unit
dtStr = dtStr + numStr + canon + ' '
}
return dtStr
} else {
// else it's just plain english, return
return token
}
}
// var fakes = { t: { h: '1', m: '00' }, dt: {}, token: 't:1h00m,dt:' }
// var fakes = { t: { M: '12', d: '25', m: '00' }, dt: {}, token: 't:12M25d00m,dt:' }
// console.log(restoreNormal(fakes))
/**
* !Backburner for future extension: Main method: Run the CFG algorithm to parse the string, return JSON of {input, output, diffStr}. Normalize the string before Matt's algorithm runs it.
* @example
* var str = 'having lunch today at 3 hours after 9am'
* norm(str)
* // => { input: 'having lunch today at 3 hours after 9am',
* output: '2016-03-04T05:00:09Z',
* difference: 'having lunch' }
*/
function CFGproduce (str, offset) {
// try all the below till all is elegantly fixed
var diffStr = str,
finalStr = null,
output = str
// Production rules: CFG algorithm for human language for time
// p#0: tokenize, remove nulls, pick tokens
var tokObj = tokenize(str)
var syms = pickTokens(tokObj.symbols)
// console.log('p#0: parse normal forms, remove nulls, pick tokens', tokObj)
try {
syms = reduce(syms, ['n', 'n'])
// console.log('p#1: arithmetics: [] ~ , + if n1 > n2, * else', syms)
syms = nTnRedistribute(syms)
// console.log('p#2: redistribute, [] ~ [] ', syms)
output = util.tokenToStr(syms)
// !okay replace back the normal forms in the str
// // !Till future completion: Mute from below
// syms = reduce(syms, ['n', 'T'])
// // console.log('p#3: [] ~ , * if dt, + if t', syms)
// syms = reduce(syms, ['T', 'T'])
// // console.log('p#4: [] ~ ', syms)
// syms = nDefTSyms(syms)
// // console.log('p#5: defaulter ~ , d defaults to t:h', syms)
// syms = reduce(syms, ['o', 'o'])
// // console.log('p#6: ~ *', syms)
// syms = autoHourModding(syms)
// syms = weekModding(syms, offset)
// // console.log('p#7: modding: meridiem, weeks', syms)
// syms = optReduce(syms, ['T', 'T'], ['o'], null, symbol(util.nowT(offset)))
// // console.log('p#8: ~ ', syms)
// // !future:
// // syms = reduce(syms, ['T'], ['r'])
// // syms = reduce(syms, ['f', 'T', 'rT'], ['c'])
// console.log('tokObj', tokObj)
syms = finalizeT(syms, offset)
// console.log('p#9: finalizeT with origin', syms)
finalStr = symsToStdT(syms, offset)
// console.log('finalStr', finalStr)
} catch (e) {}
// extract the tokens for difference string later
// diffStr = util.unparsedStr(tokObj.str, tokObj.symbols)
// console.log('diffStr', diffStr)
// !convert dt into proper terms
return {
input: str,
// output: new Date(finalStr),
output: output,
difference: diffStr
}
}
/**
* Production rule #0: pick tokens, remove nulls.
* 1. break into chunks of arrs delimited by triple-null-or-more
* 2. reorder chunks by arr length
* 3.1 init candidate = []
* 3.2 pull and push the chunks not containing into candidate
* 3.3 pull and push the chunks containing into candidate
* 4. pick the last candidate
*/
function pickTokens (syms) {
// 1. 2. 3.
var delimited = util.delimSyms(syms),
chunks = util.splitSyms(delimited, 'trinull'),
candidates = util.orderChunks(chunks)
// 4.
return candidates.pop()
}
/**
* Reduce an array of symbols with binary operations between permissible symbols.
* @param {Array} syms Array of input symbols
* @param {Array} varArr String names of permissible variables.
* @param {Array} opArr String names of permissible operations.
* @return {Array} The reduced result.
*/
function reduce (syms, varArr, opArr) {
if (syms.length < 2) {
return syms
}
// the operator arrays
var opArr = opArr || ['op']
// endmark for handling last symbol
syms.push('null')
// the result, past-pointer(previous non-null symbol), default-op, current-op, and whether current-op is inter-symbol op, i.e. will not be used up
var res = [],
past = null,
defOp = null,
op = defOp,
interOp = false
for (var i = 0; i < syms.length; i++) {
var s = syms[i]
if (!past || !s) {
// edge case or null
if (i == 0) { past = s; }
} else if (util.isSym(s, opArr)) {
// s is an op. mark op as won't be used yet
op = s
interOp = true
// the nDefT for when past = 'n', s = 'o'
} else if (util.isSym(past, [varArr[0]]) && util.isSym(s, [varArr[1]])) {
// s and past are operable variables specified by varArr
past = execOp(past, op, s)
// reset after op is used
op = defOp
interOp = false
} else {
// no further legal operation made, push and continue
// change of class, past is finalized, push to res
res.push(past)
if (Array.isArray(past)) {
// if past was returned from execOp as array (not executed), then flatten it and dont push op to res, since it's already included in op
res = _.flatten(res)
} else {
// if inter-op (not used), push a clone (prevent overwrite later)
if (interOp) { res.push(symbol(op.value)) }
}
// reset
op = defOp
interOp = false
past = s
}
}
return res
}
/**
* Optional reduce: similar to reduce() but either argument is optional.
* algorithm: return a T
* 1. for each t, dt, do:
* 2. for each key in union of keys for Lt, Rt, do:
* 3. _Rt = _Rt op _Lt
* @param {Array} syms Array of input symbols
* @param {Array} varArr String names of permissible variables.
* @param {Array} opArr String names of permissible operations.
* @param {symbol} Ldef default for left argument
* @param {symbol} Rdef default for right argument
* @return {Array} The reduced result.
*/
function optReduce (syms, varArr, opArr, Ldef, Rdef) {
if (syms.length < 2) {
return syms
}
// use peek
var res = [],
sum = null,
L = null,
R = null
for (var i = 0; i < syms.length; i++) {
var s = syms[i]
if (util.isSym(s, opArr)) {
if (sum == null) {
L = syms[i - 1]
sum = (util.isSym(L, [varArr[0]])) ? L : Ldef
}
R = syms[i + 1]
// if is var skip it since will be consumed
if (util.isSym(R, [varArr[1]])) { i++; }
// else reset to default
else { R = Rdef; }
// compute:
sum = execOp(sum, s, R)
// before loop quits due to possible i++, push the last
if (i == syms.length - 1) {
res.push(sum)
}
} else {
// s is not opArr, can't have been varArr either
// edge case: at first dont push
if (i > 0) {
res.push(sum)
res.push(s)
sum = null
}
}
}
return res
}
/**
* Execute non-commutative operation between 2 argument symbols and an op symbol; carry out respective ops according to symbol names.
* @param {symbol} L Left argument
* @param {symbol} op operation
* @param {symbol} R Right argument
* @param {str} offset The time origin offset
* @return {symbol} Result
*/
function execOp (L, op, R, offset) {
var otype = util.opType(L, op, R),
res = null
if (_.includes(['nn'], otype)) {
res = nnOp(L, op, R)
} else if (_.includes(['nT'], otype)) {
res = nTOp(L, op, R)
} else if (_.includes(['TT'], otype)) {
res = TTOp(L, op, R)
} else if (_.includes(['ToT', 'oT', 'To'], otype)) {
res = ToTOp(L, op, R, offset)
} else if (_.includes(['oo'], otype)) {
res = ooOp(L, R)
} else if (_.includes(['rT', 'TrT'], otype)) {
// has optional arg
res = rTOp(L, R)
} else if (_.includes(['cT', 'fcT', 'crT', 'fcrT'], otype)) {
// has optional arg
res = cTOp(L, R)
} else {
// not executable, e.g. not in the right order, return fully
res = (op == null) ? [L, R] : [L, op, R]
}
return res
}
/**
* Atomic binary arithmetic operation on the numerical level, with default overriding the argument prepended with '='.
* @param {string|Number} Lval The left argument value.
* @param {symbol} op The op symbol
* @param {string|Number} Rval The right argument value.
* @return {Number} Result from the operation.
*/
function atomicOp (Lval, op, Rval, dontOp) {
dontOp = dontOp || false
var oName = op.value
if (Lval == undefined) {
// if L is missing, R must exist tho
return (oName == 'minus') ? Rval.toString().replace(/(\d)/, '-$1') : Rval
} else if (Rval == undefined) {
// if L exists, be it def or not, R missing
return Lval
} else {
// or R exist or is default (parse to NaN), L can be default too but ignore then
var defL = Lval.toString().match(/^=/),
defR = Rval.toString().match(/^=/)
var l = parseFloat(Lval.toString().replace(/^=/, '')),
r = parseFloat(Rval.toString().replace(/^=/, ''))
if (defL && defR) {
// if both are default, return r 'last come last serve'
return r
} else if (defL && !defR) {
// if either default, return the non-default
return r
} else if (!defL && defR) {
return l
} else {
// none default
if (dontOp) {
// if is a don't operate together, i.e. for t, just return l
// 'first come first serve'
return l
} else {
// make the into proper floats first
if (oName == 'minus') {
return l - r
} else if (oName == 'plus') {
return l + r
} else if (oName == 'times') {
return l * r
} else if (oName == 'divide') {
return l / r
}
}
}
}
}
/**
* p#1: arithmetics: [] ~ , + if n1 > n2, * else
*/
function nnOp (L, op, R) {
var l = L.value,
r = R.value
// set the default op according to value in nn op
if (l > r) {
op = op || symbol('plus')
} else {
op = op || symbol('times')
}
var res = atomicOp(l, op, r)
return symbol(res)
}
/**
* p#2: redistribute, [] ~ []
* algorithm: note that from previous steps no 's can occur adjacently
* 1. scan array L to R, on each found:
* 2.1 if its R is , continue
* 2.2 else, this is the target. do:
* 3.1 init carry = []. remove and push into carry,
* 3.2 if its L is , remove and prepend into carry,
* 4.1 find the first to the left, if not , drop the carry and continue
* 4.2 else merge the carry after the
* 5. At the end of loop, rerun production rule #1
*/
function nTnRedistribute (syms) {
if (syms.length < 2) {
return syms
}
// 1.
for (var i = 0; i < syms.length; i++) {
var s = syms[i]
if (util.sName(s) != 'n') {
continue
}
// 1.
var R = syms[i + 1]
if (util.sName(R) == 'T') {
continue
}
// 2.2
// 3.1 prepare the carry
var carry = []
// 3.2 the Left symbol
var L = syms[i - 1],
Li = -1
if (util.sName(L) == 'op') {
// if L is an 'op', remember to pull it later
Li = i - 1
}
// 4.1
// find L...L of L that is 'n'
var LLi = _.findLastIndex(syms.slice(0, i - 1), function (Ls) {
return util.sName(Ls) == 'n'
})
if (!syms[LLi] || util.sName(syms[LLi + 1]) != 'T') {
// if can't find 'n' (index = -1), or the R of 'n' isn't T, abort mission
// syms.splice(i, 0, carry)
} else {
// 4.2
// else, pull s at [i], optional L at [Li], and push at LLi+1
carry.push(_.pullAt(syms, i)[0])
if (Li != -1) {
carry.unshift(_.pullAt(syms, Li)[0])
}
syms.splice(LLi + 1, 0, carry)
syms = _.flatten(syms)
}
}
// 5. redo the op
syms = reduce(syms, ['n', 'n'])
return syms
}
/**
* p#3: [] ~ , * if dt, + if t
* 1. if t can be overidden, start from the highest unit set to n, then return.
* 2. otherwise, if not empty, = *, then return
* 3. else, if not empty, = +, then return
*/
function nTOp (nL, op, TR) {
var tOverrideUnit = util.highestOverride(TR.t)
if (tOverrideUnit) {
// 1.
TR.t[tOverrideUnit] = nL.value
} else if (_.keys(TR.dt).length) {
// 2.
op = op || symbol('times')
for (var k in TR.dt) {
if (k == 'wd') {
continue
}
TR.dt[k] = atomicOp(nL.value, op, TR.dt[k])
}
} else if (_.keys(TR.t).length) {
// 3.
op = op || symbol('plus')
for (var k in TR.t) {
TR.t[k] = atomicOp(nL.value, op, TR.t[k])
}
}
return TR
}
/**
* p#4: [] ~
*/
function TTOp (TL, op, TR) {
// set the default op
op = op || symbol('plus')
// util.sName
// mutate into TL
for (var k in TR.t) {
// okay done add absolute time, just as you don't add origins together put u take gradual specificity, the 'true' param for dontOp if exist, return r
// override default tho, taken care of by atomic
TL.t[k] = atomicOp(TL.t[k], op, TR.t[k], true)
}
for (var k in TR.dt) {
if (k == 'wd') {
continue
}
TL.dt[k] = atomicOp(TL.dt[k], op, TR.dt[k])
}
return TL
}
/**
* p#5: defaulter ~ , d defaults to t:h
*/
function nDefTSyms (syms) {
var res = []
for (var i = 0; i < syms.length; i++) {
var s = syms[i]
res.push(util.isSym(s, ['n']) ? nDefT(s) : s)
}
return res
}
/**
* Helper: default a singlet n to T, i.e. next available hour
*/
function nDefT (n) {
var deft = symbol('t:1h,dt:')
var nVal = n.value
var currentHour = new Date().getHours()
var nextnVal = Math.floor(currentHour / 12) * 12 + nVal
var tHour = execOp(symbol(nextnVal), symbol('times'), deft)
return tHour
}
/**
* ~ *
* To handle 'before next' etc.
*/
function ooOp (L, R) {
var Lsign = (L.value == 'plus') ? +1 : -1,
Rsign = (R.value == 'plus') ? +1 : -1,
LRsign = Lsign * Rsign
return (LRsign > 0) ? symbol('after') : symbol('before')
}
/**
* Next available T', given an offset, by incrementing in dt the next unit ++1 from the current largest unit in t.
*/
function nextAvailable (T, offset) {
// find the current largest and next largest unit
var nextUnit = util.nextLargestUnit(T)
// first finalized T
var finT1 = finalizeT([T], offset)[0],
stdStr1 = util.TtoStdT(finT1),
UTC1 = Date.parse(stdStr1),
UTCnow = Date.parse(new Date()),
UTCdiff = UTC1 - UTCnow
// if UTC1 is not in the future, add next unit
if (UTCdiff < 0) {
T.dt[nextUnit] = (T.dt[nextUnit] || 0) + 1
var finT2 = finalizeT([T], offset)[0]
return finT2
} else {
return finT1
}
}
/**
* p#6: ~
*/
function ToTOp (L, op, R, offset) {
if (L && !R) {
// if R is missing, set to now
R = symbol(util.nowT(offset))
} else if (!L && R) {
// if L missing
if (util.has_t(R)) {
// if R has t => part of origin, so L shd be the according dt
var nextUnit = util.nextLargestUnit(R)
R = nextAvailable(R, offset)
// so arbitrarily set as 0.5 * next largest unit
L = execOp(symbol(0.5), symbol('times'), symbol(nextUnit))
} else {
// R has dt only, make L an origin then
L = symbol(util.nowT(offset))
}
} else if (!L && !R) {
L = symbol(util.nowT(offset))
R = symbol(util.nowT(offset))
}
var Ttype = ['t', 'dt']
for (var i = 0; i < Ttype.length; i++) {
var _Ttype = Ttype[i],
// the dontOp for 't'
dontOp = (_Ttype == 't')
var concatKeys = _.keys(L[_Ttype]).concat(_.keys(R[_Ttype]))
var keys = _.unique(concatKeys)
for (var j = 0; j < keys.length; j++) {
var k = keys[j]
// run atomic op, note the reversed order of R op L
R[_Ttype][k] = atomicOp(R[_Ttype][k], op, L[_Ttype][k], dontOp)
}
}
return R
}
/**
* p#7: auto-hour-modding: t:h mod 12
* then add the meridiem to t:h if exist
*/
function autoHourModding (syms) {
for (var i = 0; i < syms.length; i++) {
var s = syms[i]
if (util.isSym(s, ['T'])) {
if (syms[i]['t']['h']) {
// if t has 'h', mod it
var value = syms[i]['t']['h'].toString()
var isDefault = (value.match(/^=/) || [])[0] || ''
value = parseFloat(value.replace(/^=/, ''))
value = value > 12 ? value % 12 : value
syms[i]['t']['h'] = isDefault + value
}
// apply the non-0 meridiem after modding:
if (syms[i]['t']['mer']) {
var dt_h = (syms[i]['dt']['h'] || '0').toString()
// dump default at last
dt_h = dt_h.replace(/^=/, '')
if (syms[i]['t']['mer'] == 1) {
syms[i]['dt']['h'] = parseFloat(dt_h) + 12
}
// delete mer
delete syms[i]['t']['mer']
}
}
}
return syms
}
// do it at last, to use like '2nd week of march'
function weekModding (syms, offset) {
// weekday of the offset to calculate dt:d
var offsetWD = new Date(util.TtoStdT(util.nowT())).getDay()
for (var i = 0; i < syms.length; i++) {
var s = syms[i]
if (util.isSym(s, ['T'])) {
if (syms[i]['dt']['wd']) {
// if dt has 'wd', mod it and turn into dt:d + %wd
var WD = parseInt(syms[i]['dt']['wd'])
var diffWD = (WD - offsetWD) % 7
if (diffWD < 0) { diffWD = diffWD + 7 }
syms[i]['dt']['d'] = (syms[i]['dt']['d'] || 0) + diffWD
delete syms[i]['dt']['wd']
}
}
}
return syms
}
/**
* p#8: Finalize each T in syms array:
* 1. remove defaults from T
* 2. add origin symbol.nowT() with given T.t, override missing units
* 3. add t and dt
*/
function finalizeT (syms, offset) {
// remove defaults
for (var i = 0; i < syms.length; i++) {
syms[i] = removeDefaults(syms[i])
}
// default with origin at end
syms.push(symbol(util.nowT(offset)))
syms = reduce(syms, ['T', 'T'])
// combine t and dt
var newSyms = []
for (var i = 0; i < syms.length; i++) {
var s = syms[i],
sum = tdtAdd(s)
sum.token = util.TtoStr(sum)
newSyms.push(tdtAdd(s))
}
return syms
}
/**
* remove the defaults before adding with origin
*/
function removeDefaults (T) {
for (var k in T.dt) {
T.dt[k] = T.dt[k].toString().replace(/^=/, '')
}
for (var k in T.t) {
T.t[k] = T.t[k].toString().replace(/^=/, '')
}
// delete meridiem too
delete T['t']['mer']
return T
}
/**
* add t and dt within a T together, delete the dt keys
*/
function tdtAdd (T) {
// guard for non-T
if (!util.isSym(T, ['T'])) {
return T
}
for (var k in T.dt) {
// absolute add, disregard defaults
var t_k = (T.t[k] == undefined) ? 0 : T.t[k],
dt_k = T.dt[k]
// cleanup the default
t_k = t_k.toString().replace(/^=/, '')
dt_k = dt_k.toString().replace(/^=/, '')
var sum = parseFloat(t_k) + parseFloat(dt_k)
// set the result, remove used dt
T.t[k] = sum
delete T.dt[k]
}
return T
}
/**
* p#9: Convert an array of symbols to normalized stdT strings.
* if token was normal form already, parse into stdT.
* if is n: return n.value
* else return org token
*/
function symsToStdT (syms, offset) {
var tokens = []
for (var i = 0; i < syms.length; i++) {
var s = syms[i],
token = s.token.toString()
// default, don't switch unless:
if (util.isSym(s, ['n'])) {
token = s.value
} else if (token.match(util.reT)) {
// is normal T form
token = util.TtoStdT(token, offset)
}
tokens.push(token)
}
return tokens.join(' ')
}
/**
* !to be implemented for range
*/
function rTOp (L, R) {
var start, end
if (!R) {
start = symbol(util.nowT())
end = L
} else {
start = L
end = R
}
return symbol({ start: start, end: end })
}
/**
* !to be implemented for cron
*/
function cTOp (L, R) {}
},{"./subdash":6,"./symbol":7,"./tokenize":8,"./util":9}],5:[function(require,module,exports){
/**
* Module Dependencies
*/
var debug = require('debug')('date:parser')
var date = require('./date')
var norm = require('./norm')
/**
* Days
*/
var days = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday']
var months = ['january', 'february', 'march', 'april', 'may', 'june', 'july', 'august', 'september',
'october', 'november', 'december'
]
/**
* Regexs
*/
// 5, 05, 5:30, 5.30, 05:30:10, 05:30.10, 05.30.10, at 5
var rMeridiem = /^(\d{1,2})([:.](\d{1,2}))?([:.](\d{1,2}))?\s*([ap]m)/
var rHourMinute = /^(\d{1,2})([:.](\d{1,2}))([:.](\d{1,2}))?/
var rAtHour = /^at\s?(\d{1,2})$/
var rDays = /\b(sun(day)?|mon(day)?|tues(day)?|wed(nesday)?|thur(sday|s)?|fri(day)?|sat(urday)?)s?\b/
var rMonths = /^((\d{1,2})\s*(st|nd|rd|th))\s(day\s)?(of\s)?(january|february|march|april|may|june|july|august|september|october|november|december)/i
var rPast = /\b(last|yesterday|ago)\b/
var rDayMod = /\b(morning|noon|afternoon|night|evening|midnight)\b/
var rAgo = /^(\d*)\s?\b(second|minute|hour|day|week|month|year)[s]?\b\s?ago$/
/**
* Expose `parser`
*/
module.exports = parser
/**
* Initialize `parser`
*
* @param {String} str
* @return {Date}
* @api publics
*/
function parser (str, offset) {
if (!(this instanceof parser)) return new parser(str, offset)
if (typeof offset == 'string') offset = parser(offset)
// CFG preprocessing into normalized format,
// get {str, tokens, normals}
// !future: return multiple parsed times, some from it
var prepro = norm(str, offset)
// console.log(prepro)
// reset the str to prepro str
str = prepro.str
// if proprocessed doesn't leave any str to be processed (non-date-time) format, check normals
if (!str) {
if (prepro.normals.length) {
// if there's normal date parsed already,
// !return the first
return new Date(prepro.normals[0])
} else {
// otherwise go back to below to return proper Error
str = str
}
}
var d = offset || new Date
this.date = new date(d)
this.original = str
this.str = str.toLowerCase()
this.stash = []
this.tokens = []
while (this.advance() !== 'eos')
debug('tokens %j', this.tokens)
this.nextTime(d)
if (this.date.date == d) throw new Error('Invalid date')
return this.date.date
}
/**
* Advance a token
*/
parser.prototype.advance = function () {
var tok = this.eos()
|| this.space()
|| this._next()
|| this.last()
|| this.dayByName()
|| this.monthByName()
|| this.timeAgo()
|| this.ago()
|| this.yesterday()
|| this.tomorrow()
|| this.noon()
|| this.midnight()
|| this.night()
|| this.evening()
|| this.afternoon()
|| this.morning()
|| this.tonight()
|| this.meridiem()
|| this.hourminute()
|| this.athour()
|| this.week()
|| this.month()
|| this.year()
|| this.second()
|| this.minute()
|| this.hour()
|| this.day()
|| this.number()
|| this.string()
|| this.other()
this.tokens.push(tok)
return tok
}
/**
* Lookahead `n` tokens.
*
* @param {Number} n
* @return {Object}
* @api private
*/
parser.prototype.lookahead = function (n) {
var fetch = n - this.stash.length
if (fetch == 0) return this.lookahead(++n)
while (fetch-- > 0) this.stash.push(this.advance())
return this.stash[--n]
}
/**
* Lookahead a single token.
*
* @return {Token}
* @api private
*/
parser.prototype.peek = function () {
return this.lookahead(1)
}
/**
* Fetch next token including those stashed by peek.
*
* @return {Token}
* @api private
*/
parser.prototype.next = function () {
var tok = this.stashed() || this.advance()
return tok
}
/**
* Return the next possibly stashed token.
*
* @return {Token}
* @api private
*/
parser.prototype.stashed = function () {
var stashed = this.stash.shift()
return stashed
}
/**
* Consume the given `len`.
*
* @param {Number|Array} len
* @api private
*/
parser.prototype.skip = function (len) {
this.str = this.str.substr(Array.isArray(len) ? len[0].length : len)
}
/**
* EOS
*/
parser.prototype.eos = function () {
if (this.str.length) return
return 'eos'
}
/**
* Space
*/
parser.prototype.space = function () {
var captures
if (captures = /^([ \t]+)/.exec(this.str)) {
this.skip(captures)
return this.advance()
}
}
/**
* Second
*/
parser.prototype.second = function () {
var captures
if (captures = /^s(ec|econd)?s?/.exec(this.str)) {
this.skip(captures)
return 'second'
}
}
/**
* Minute
*/
parser.prototype.minute = function () {
var captures
if (captures = /^m(in|inute)?s?/.exec(this.str)) {
this.skip(captures)
return 'minute'
}
}
/**
* Hour
*/
parser.prototype.hour = function () {
var captures
if (captures = /^h(r|our)s?/.exec(this.str)) {
this.skip(captures)
return 'hour'
}
}
/**
* Day
*/
parser.prototype.day = function () {
var captures
if (captures = /^d(ay)?s?/.exec(this.str)) {
this.skip(captures)
return 'day'
}
}
/**
* Day by name
*/
parser.prototype.dayByName = function () {
var captures
var r = new RegExp('^' + rDays.source)
if (captures = r.exec(this.str)) {
var day = captures[1]
this.skip(captures)
this.date[day](1)
return captures[1]
}
}
/**
* Month by name
*/
parser.prototype.monthByName = function () {
var captures
if (captures = rMonths.exec(this.str)) {
var day = captures[2]
var month = captures[6]
this.date.date.setMonth((months.indexOf(month)))
if (day) this.date.date.setDate(parseInt(day))
this.skip(captures)
return captures[0]
}
}
parser.prototype.timeAgo = function () {
var captures
if (captures = rAgo.exec(this.str)) {
var num = captures[1]
var mod = captures[2]
this.date[mod](-num)
this.skip(captures)
return 'timeAgo'
}
}
/**
* Week
*/
parser.prototype.week = function () {
var captures
if (captures = /^w(k|eek)s?/.exec(this.str)) {
this.skip(captures)
return 'week'
}
}
/**
* Month
*/
parser.prototype.month = function () {
var captures
if (captures = /^mon(th)?(es|s)?\b/.exec(this.str)) {
this.skip(captures)
return 'month'
}
}
/**
* Week
*/
parser.prototype.year = function () {
var captures
if (captures = /^y(r|ear)s?/.exec(this.str)) {
this.skip(captures)
return 'year'
}
}
/**
* Meridiem am/pm
*/
parser.prototype.meridiem = function () {
var captures
if (captures = rMeridiem.exec(this.str)) {
this.skip(captures)
this.time(captures[1], captures[3], captures[5], captures[6])
return 'meridiem'
}
}
/**
* Hour Minute (ex. 12:30)
*/
parser.prototype.hourminute = function () {
var captures
if (captures = rHourMinute.exec(this.str)) {
this.skip(captures)
this.time(captures[1], captures[3], captures[5], this._meridiem)
return 'hourminute'
}
}
/**
* At Hour (ex. at 5)
*/
parser.prototype.athour = function () {
var captures
if (captures = rAtHour.exec(this.str)) {
this.skip(captures)
this.time(captures[1], 0, 0, this._meridiem)
this._meridiem = null
return 'athour'
}
}
/**
* Time set helper
*/
parser.prototype.time = function (h, m, s, meridiem) {
var d = this.date
var before = d.clone()
if (meridiem) {
// convert to 24 hour
h = ('pm' == meridiem && 12 > h) ? +h + 12 : h; // 6pm => 18
h = ('am' == meridiem && 12 == h) ? 0 : h; // 12am => 0
}
m = (!m && d.changed('minutes')) ? false : m
s = (!s && d.changed('seconds')) ? false : s
d.time(h, m, s)
}
/**
* Best attempt to pick the next time this date will occur
*
* TODO: place at the end of the parsing
*/
parser.prototype.nextTime = function (before) {
var d = this.date
var orig = this.original
if (before <= d.date || rPast.test(orig)) return this
// If time is in the past, we need to guess at the next time
if (rDays.test(orig)) {
d.day(7)
} else if ((before - d.date) / 1000 > 60) {
// If it is a month in the past, don't add a day
if (rMonths.test(orig)) {
d.day(0)
} else {
d.day(1)
}
}
return this
}
/**
* Yesterday
*/
parser.prototype.yesterday = function () {
var captures
if (captures = /^(yes(terday)?)/.exec(this.str)) {
this.skip(captures)
this.date.day(-1)
return 'yesterday'
}
}
/**
* Tomorrow
*/
parser.prototype.tomorrow = function () {
var captures
if (captures = /^tom(orrow)?/.exec(this.str)) {
this.skip(captures)
this.date.day(1)
return 'tomorrow'
}
}
/**
* Noon
*/
parser.prototype.noon = function () {
var captures
if (captures = /^noon\b/.exec(this.str)) {
this.skip(captures)
var before = this.date.clone()
this.date.date.setHours(12, 0, 0)
return 'noon'
}
}
/**
* Midnight
*/
parser.prototype.midnight = function () {
var captures
if (captures = /^midnight\b/.exec(this.str)) {
this.skip(captures)
var before = this.date.clone()
this.date.date.setHours(0, 0, 0)
return 'midnight'
}
}
/**
* Night (arbitrarily set at 7pm)
*/
parser.prototype.night = function () {
var captures
if (captures = /^night\b/.exec(this.str)) {
this.skip(captures)
this._meridiem = 'pm'
var before = this.date.clone()
this.date.date.setHours(19, 0, 0)
return 'night'
}
}
/**
* Evening (arbitrarily set at 5pm)
*/
parser.prototype.evening = function () {
var captures
if (captures = /^evening\b/.exec(this.str)) {
this.skip(captures)
this._meridiem = 'pm'
var before = this.date.clone()
this.date.date.setHours(17, 0, 0)
return 'evening'
}
}
/**
* Afternoon (arbitrarily set at 2pm)
*/
parser.prototype.afternoon = function () {
var captures
if (captures = /^afternoon\b/.exec(this.str)) {
this.skip(captures)
this._meridiem = 'pm'
var before = this.date.clone()
if (this.date.changed('hours')) return 'afternoon'
this.date.date.setHours(14, 0, 0)
return 'afternoon'
}
}
/**
* Morning (arbitrarily set at 8am)
*/
parser.prototype.morning = function () {
var captures
if (captures = /^morning\b/.exec(this.str)) {
this.skip(captures)
this._meridiem = 'am'
var before = this.date.clone()
if (!this.date.changed('hours')) this.date.date.setHours(8, 0, 0)
return 'morning'
}
}
/**
* Tonight
*/
parser.prototype.tonight = function () {
var captures
if (captures = /^tonight\b/.exec(this.str)) {
this.skip(captures)
this._meridiem = 'pm'
return 'tonight'
}
}
/**
* Next time
*/
parser.prototype._next = function () {
var captures
if (captures = /^next/.exec(this.str)) {
this.skip(captures)
var d = new Date(this.date.date)
var mod = this.peek()
// If we have a defined modifier, then update
if (this.date[mod]) {
this.next()
// slight hack to modify already modified
this.date = date(d)
this.date[mod](1)
} else if (rDayMod.test(mod)) {
this.date.day(1)
}
return 'next'
}
}
/**
* Last time
*/
parser.prototype.last = function () {
var captures
if (captures = /^last/.exec(this.str)) {
this.skip(captures)
var d = new Date(this.date.date)
var mod = this.peek()
// If we have a defined modifier, then update
if (this.date[mod]) {
this.next()
// slight hack to modify already modified
this.date = date(d)
this.date[mod](-1)
} else if (rDayMod.test(mod)) {
this.date.day(-1)
}
return 'last'
}
}
/**
* Ago
*/
parser.prototype.ago = function () {
var captures
if (captures = /^ago\b/.exec(this.str)) {
this.skip(captures)
return 'ago'
}
}
/**
* Number
*/
parser.prototype.number = function () {
var captures
if (captures = /^(\d+)/.exec(this.str)) {
var n = captures[1]
this.skip(captures)
var mod = this.peek()
// If we have a defined modifier, then update
if (this.date[mod]) {
if ('ago' == this.peek()) n = -n
this.date[mod](n)
} else if (this._meridiem) {
// when we don't have meridiem, possibly use context to guess
this.time(n, 0, 0, this._meridiem)
this._meridiem = null
} else if (this.original.indexOf('at') > -1) {
this.time(n, 0, 0, this._meridiem)
this._meridiem = null
}
return 'number'
}
}
/**
* String
*/
parser.prototype.string = function () {
var captures
if (captures = /^\w+/.exec(this.str)) {
this.skip(captures)
return 'string'
}
}
/**
* Other
*/
parser.prototype.other = function () {
var captures
if (captures = /^./.exec(this.str)) {
this.skip(captures)
return 'other'
}
}
},{"./date":2,"./norm":4,"debug":10}],6:[function(require,module,exports){
/**
* Substitutes for lodash methods
*/
exports.difference = function (bigArr, smallArr) {
var diff = []
for (var i = 0; i < bigArr.length; i++) {
var ele = bigArr[i]
if (smallArr.indexOf(ele) == -1) {
diff.push(ele)
}
}
return diff
}
exports.flatten = function (arr) {
return [].concat.apply([], arr)
}
exports.find = function (arr, fn) {
var found = null
for (var i = 0; i < arr.length; i++) {
if (fn(arr[i])) {
found = arr[i]
break
}
}
return found
}
exports.findLastIndex = function (arr, fn) {
var found = -1
for (var i = arr.length - 1; i >= 0; i--) {
if (fn(arr[i])) {
found = i
break
}
}
return found
}
exports.includes = function (arr, item) {
var found = false
for (var i = 0; i < arr.length; i++) {
if (arr[i] === item) {
found = true
break
}
}
return found
}
exports.isNaN = function (n) {
return Number.isNaN(n)
}
exports.keys = function (obj) {
return Object.keys(obj)
}
exports.pullAt = function (arr, i) {
var res = arr.splice(i, 1)
return res
}
exports.unique = function (arr, i) {
return arr.filter(function (elem, pos) {
return arr.indexOf(elem) == pos
})
}
},{}],7:[function(require,module,exports){
// Module to enumerate all CFG symbols for the human language for time
/**
* Module Dependencies
*/
var maps = require('./maps.json')
var util = require('./util')
/**
* Constructors for all types of symbols
*/
var symbolConstructors = {
op: op,
c: c,
r: r,
n: n,
t: T,
dt: T,
T: T,
f: f,
o: o,
rT: rT,
cT: cT,
}
/**
* Export `symbol`
*/
module.exports = symbol
/**
* The symbol constructor, given a string, lemmatize it, then return a symbol from {∅=null,op,c,r,n,t,dt,T,f}.
* i.e. str -> parseFloat(str) -> new n(str) -> return
* or str -> lemma(str) -> new (symbol-value) -> return
* @param {string} str the input string
* @return {*} The object from the class of symbols
* @example
* symbol('90')
* // => n { value: 10 }
* symbol('hour')
* // a time difference object
* // => dt { h: '1' }
* symbol('tonight')
* // or equivalently, takes the T string too
* symbol('t:=9h,dt:12h')
* // a T object containing ,
* // => T { t: t { h: '=9' }, dt: dt { h: '12' } }
* symbol('unrecognized')
* // an unrecognized string yields the null symbol ∅
* // => null
*/
function symbol (str) {
var s
if (str == null) {
// null gets null
s = null
} else if (str['start'] && str['end']) {
// range: with 'start' and 'end'
s = new symbolConstructors['rT'](str)
} else if (parseFloat(str) == str) {
// 'n'
s = new symbolConstructors['n'](str)
} else if (str.match(util.reT)) {
// if is of the T string format t:,dt:
s = str.match(/\s+/g) ? null : new symbolConstructors['T'](str)
} else {
var lem = util.lemma(str)
s = lem.name ? new symbolConstructors[lem.name](lem.value, lem.name) : null
// set the canonical word from lemma
if (s) { s.canon = lem.canon }
// set the original token for reference
}
if (s) { s.token = str }
return s
}
// console.log(symbol('10'))
// console.log(symbol('hour'))
// console.log(symbol('tonight'))
// console.log(symbol('t:=9h,dt:12h'))
// console.log(symbol('unrecognized'))
// ///////////////////
// the CFG symbols //
// ///////////////////
/**
* The op for arithmetic operator.
* note that since scaling(*,/) is very rare, we omit its implementation for now.
*/
function op (value) {
this.value = value
}
/**
* The origin operator.
*/
function o (value) {
this.value = value
}
/**
* The range operator.
*/
function r (value) {
this.value = value
}
/**
* The cron operator.
*/
function c (value) {
this.value = value
}
/**
* The n number. Calls parseFloat.
*/
function n (value) {
this.value = parseFloat(value)
}
/**
* The t for time t, i.e. a point in the timeline
* units: ms, s, m, h, d, w, M, y
* All values are string, to represent the "=" default in the units. so when performing numerical operation, use parseFloat.
* @example
* new t(undefined)
* new t("")
* // => t {}
* new t("7h30m")
* // => t { h: '7', m: '30' }
* new t("7h=30m")
* // => t { h: '7', m: '=30' }
*/
function t (value) {
// guard against falsy input
if (!value) {
return null
}
// 1. see if unit is prepended with "=" for default, or set to ''
// 2. then consume chunks of like "30m"
while (value) {
var isDefault = (value.match(/^=/) || [])[0] || ''
value = value.replace(/^=/, '')
// default number is "1"
var number = (value.match(/^\-?\d+(\.\d+)?/) || [])[0] || '1'
value = value.replace(/^\-?\d+(\.\d+)?/, '')
var unit = (value.match(/^[a-zA-Z]+/) || [])[0]
value = value.replace(/^[a-zA-Z]+/, '')
// prepend the number (string) with isDefault, i.e. "=" or ""
this[unit] = isDefault + number
}
}
/**
* The dt for time t, i.e. a displacement in the timeline
* units: ms, s, m, h, d, w, M, y
* All values are string, to represent the "=" default in the units. so when performing numerical operation, use parseFloat.
* Same keys as to allow for component-wise operation, e.g. t + dt = { ms+(d)ms, s+(d)s, ... }
*/
function dt (value) {
// guard against falsy input
if (!value) {
return null
}
// 1. see if unit is prepended with "=" for default, or set to ''
// 2. then consume chunks of like "30m"
while (value) {
var isDefault = (value.match(/^=/) || [])[0] || ''
value = value.replace(/^=/, '')
// default number is "1"
var number = (value.match(/^\-?\d+(\.\d+)?/) || [])[0] || '1'
value = value.replace(/^\-?\d+(\.\d+)?/, '')
var unit = (value.match(/^[a-zA-Z]+/) || [])[0]
value = value.replace(/^[a-zA-Z]+/, '')
// prepend the number (string) with isDefault, i.e. "=" or ""
this[unit] = isDefault + number
}
}
// console.log(new t(undefined))
// console.log(new t(""))
// console.log(new t("7h30m"))
// console.log(new t("=7h30m"))
// console.log(new t().constructor.name)
/**
* The T, implementation-specific, is a linear combination of and .
* Used to capture the human Ts, e.g. noon, afternoon, dawn, evening, today, tonight, Sunday, fortnight, weekdays, weekends, christmas, spring, summer, holidays etc.
* To specify T in maps.json, follow the syntax:
* `:` means "set", `=` means "default", use t:,dt: for the symbol-value, e.g. "t:=7h,dt:0h"
* evening ~ t:=7h,dt:12h, read as "t set to default 7h, dt set to 12h"
* later ~ t:,dt:=3h, read as "t set to nothing, dt set to default 3h"
* beware, "" and "0" are diferent, the former is empty, the later a numerical value.
* @param {string} value from the Symbol.
* @param {string} [name] from the Symbol.
* @example
* var T = new symbol("t:=7h,dt:0h")
* // => T { t: t { h: '=7' }, dt: dt { h: '0' } }
* T.t
* // => t { h: '=7' }
* T.dt
* // => t { h: '0' }
*/
function T (value, name) {
if (name == 't') {
this.t = new t(value)
this.dt = new dt()
} else if (name == 'dt') {
this.t = new t()
this.dt = new dt(value)
} else {
var split = value.split(','),
_t = split[0].split(':').pop(),
_dt = split[1].split(':').pop()
this.t = new t(_t)
this.dt = new dt(_dt)
}
}
// var T = new T("t:=7h,dt:0h")
// console.log(T.t)
// console.log(T.dt)
/**
* The product of , gives a time interval
*/
function rT (interval) {
this.start = interval.start
this.end = interval.end
}
/**
* The f to capture frequency for .
*/
function f (value) {
this.value = value
}
/**
* The product of or