김대선

00시 00분 자동으로 데이터를 받아와서 datas 폴더에 년.월.시간 형식으로 저장

Showing 189 changed files with 20800 additions and 7 deletions
1 const express = require('express') 1 const express = require('express')
2 +const schedule = require('node-schedule')
3 +const fs = require('fs')
2 const functions = require('./function') 4 const functions = require('./function')
3 5
4 const app = express(); 6 const app = express();
5 7
6 -const server = app.listen(3000,()=>{
7 - const host = server.address().address
8 - const port = server.address().port
9 - console.log("app listening at http://%s:%s", host, port)
10 -})
11 8
9 +const readData = async () =>{
10 + const data = []
11 + let title = ""
12 + let tags = []
13 + let url = ""
14 + const today = new Date()
15 + const string = fs.readFileSync(`./datas/${today.getFullYear()}.${today.getMonth()}.${today.getDate()}`, 'utf-8', 'r')
16 + const stringArray = string.split('\n')
17 + const size = stringArray.length
18 + for(let i = 0 ; i < size; i++){
19 + if( i % 3 == 0){
20 + title = stringArray[i].replace("title : ", "")
21 + }
22 + else if( i % 3 == 1){
23 + tags = stringArray[i].replace("tags : ", "").split(",")
24 + }
25 + else if ( i % 3 == 2){
26 + url = stringArray[i].replace("url : ", "")
27 + data.push({
28 + title : title,
29 + tags : tags,
30 + url : url
31 + })
32 + }
33 + }
34 + console.log(data)
35 +}
12 36
13 -app.get('/', async (req, res)=>{ 37 +const save = async()=> {
38 + try {
14 let string = "" 39 let string = ""
15 let data = await functions.getKakaoData() 40 let data = await functions.getKakaoData()
16 let temp = "" 41 let temp = ""
...@@ -46,6 +71,30 @@ app.get('/', async (req, res)=>{ ...@@ -46,6 +71,30 @@ app.get('/', async (req, res)=>{
46 temp = "url : " + i.url 71 temp = "url : " + i.url
47 string = string + temp + "\n" 72 string = string + temp + "\n"
48 } 73 }
74 + const today = new Date()
75 + fs.writeFile(`./datas/${today.getFullYear()}.${today.getMonth()}.${today.getDate()}`, string, 'utf-8', (err)=>{
76 + if(err){
77 + save()
78 + }
79 + else console.log("저장완료!")
80 + })
81 + } catch (error) {
82 + console.log(error)
83 + save()
84 + }
85 +}
86 +
87 +const saveData = schedule.scheduleJob('0 0 0 * * *', save)
88 +
89 +const server = app.listen(3000,()=>{
90 + const host = server.address().address
91 + const port = server.address().port
92 + console.log("app listening at http://%s:%s", host, port)
93 +})
94 +
95 +
96 +app.get('/', async (req, res)=>{
97 +
49 98
50 res.send(string) 99 res.send(string)
51 }) 100 })
...\ No newline at end of file ...\ No newline at end of file
......
1 +title : 카카오전문연구요원(전직및보충역)모집
2 +tags : #Android,#iOS,#Windows,#Webfront,#System,#Network,#Cloud,#DB,#Security,#Algorithm/ML,#Algorithm/Ranking,#Hadoopecosystem,#Server,#Statistics/Analysis,#기타
3 +url : https://careers.kakao.com/jobs/P-9349
4 +title : 악성코드및엔드포인트위협대응보안엔지니어모집(경력)
5 +tags : #Windows,#Security,#Statistics/Analysis
6 +url : https://careers.kakao.com/jobs/P-12045
7 +title : 카카오톡안티어뷰징을위한데이터/백엔드엔지니어및머신러닝전문가모집
8 +tags : #Algorithm/ML,#Hadoopecosystem,#Server,#Statistics/Analysis
9 +url : https://careers.kakao.com/jobs/P-11083
10 +title : [티스토리,브런치]프론트엔드개발자모집(경력)
11 +tags : #Webfront
12 +url : https://careers.kakao.com/jobs/P-11790
13 +title : [톡캘린더웹서비스]프론트엔드개발자모집(경력)
14 +tags : #Webfront
15 +url : https://careers.kakao.com/jobs/P-11796
16 +title : [사내플랫폼개발]프론트엔드개발자모집(경력)
17 +tags : #Webfront
18 +url : https://careers.kakao.com/jobs/P-11890
19 +title : [지도서비스/FE플랫폼팀]프론트엔드개발자모집(경력)
20 +tags : #Webfront
21 +url : https://careers.kakao.com/jobs/P-12014
22 +title : [비즈니스서비스/FE플랫폼팀]프론트엔드개발자모집(경력)
23 +tags : #Webfront
24 +url : https://careers.kakao.com/jobs/P-12016
25 +title : [포털/다음앱]콘텐츠플랫폼개발자모집(경력)
26 +tags : #Server
27 +url : https://careers.kakao.com/jobs/P-11671
28 +title : [브런치]webbackend경력개발자모집
29 +tags : #Webfront,#Server
30 +url : https://careers.kakao.com/jobs/P-11322
31 +title : [카카오톡]콘텐츠구독서비스개발자모집(경력)
32 +tags : #DB,#Server
33 +url : https://careers.kakao.com/jobs/P-11575
34 +title : [카카오맵]검색/추천개발자모집
35 +tags : #Algorithm/ML,#Algorithm/Ranking,#Server,#Statistics/Analysis
36 +url : https://careers.kakao.com/jobs/P-12022
37 +title : [동영상플랫폼]동영상패키징서버개발자모집
38 +tags : #System,#Server
39 +url : https://careers.kakao.com/jobs/P-12021
40 +title : [포털서비스]AB테스트설계/분석데이터엔지니어및서버개발자모집(경력)
41 +tags : #Cloud,#Hadoopecosystem,#Server,#Statistics/Analysis
42 +url : https://careers.kakao.com/jobs/P-11872
43 +title : [카카오맵](경력)서버개발자모집
44 +tags : #Server
45 +url : https://careers.kakao.com/jobs/P-11953
46 +title : 컨텐츠API서버개발자모집(경력)
47 +tags : #Server
48 +url : https://careers.kakao.com/jobs/P-11689?page=2
49 +title : 카카오API플랫폼개발자모집(경력)
50 +tags : #Webfront,#Server
51 +url : https://careers.kakao.com/jobs/P-11698?page=2
52 +title : [다음포털](경력)카카오미래미디어셀챗봇개발자모집
53 +tags : #Server
54 +url : https://careers.kakao.com/jobs/P-12017?page=2
55 +title : 빅데이터활용분석플랫폼개발자모집
56 +tags : #Hadoopecosystem,#Server,#Statistics/Analysis
57 +url : https://careers.kakao.com/jobs/P-10682?page=2
58 +title : AR/VRSDK개발자모집(경력)
59 +tags : #Android,#iOS,#Algorithm/ML,#기타
60 +url : https://careers.kakao.com/jobs/P-11971?page=2
61 +title : 모니터링솔루션개발자모집(경력)
62 +tags : #Android,#iOS,#Webfront,#Server
63 +url : https://careers.kakao.com/jobs/P-11720?page=2
64 +title : [멜론CIC]멜론iOS앱개발자모집
65 +tags : #iOS
66 +url : https://careers.kakao.com/jobs/P-11252?page=2
67 +title : 카카오지갑/인증플랫폼서버개발자모집
68 +tags : #Server
69 +url : https://careers.kakao.com/jobs/P-11944?page=2
70 +title : 카카오톡안드로이드클라이언트개발자모집(경력)
71 +tags : #Android
72 +url : https://careers.kakao.com/jobs/P-11713?page=2
73 +title : [카카오스토리]개발자모집(서버)
74 +tags : #Server
75 +url : https://careers.kakao.com/jobs/P-11333?page=2
76 +title : [카카오스토리]iOS개발자모집(경력)
77 +tags : #iOS
78 +url : https://careers.kakao.com/jobs/P-11967?page=2
79 +title : 데이터전문가(모델러/분석가/엔지니어)모집
80 +tags : #Algorithm/ML,#Algorithm/Ranking,#Hadoopecosystem,#Statistics/Analysis
81 +url : https://careers.kakao.com/jobs/P-12003?page=2
82 +title : 카카오톡/계정/메일서버개발자모집공고
83 +tags : #Server
84 +url : https://careers.kakao.com/jobs/P-11081?page=2
85 +title : 카카오데이터의가치를발굴할데이터분석가모집(경력)
86 +tags : #Algorithm/ML,#Statistics/Analysis
87 +url : https://careers.kakao.com/jobs/P-11899?page=2
88 +title : 데이터엔지니어및사이언티스트(경력)
89 +tags : #Algorithm/ML,#Hadoopecosystem,#Statistics/Analysis
90 +url : https://careers.kakao.com/jobs/P-11888?page=2
91 +title : [멜론CIC]멜론PC개발팀윈도우개발자모집
92 +tags : #Windows,#Webfront
93 +url : https://careers.kakao.com/jobs/P-11922?page=3
94 +title : [멜론CIC]멜론서버개발자모집
95 +tags : #Server
96 +url : https://careers.kakao.com/jobs/P-10885?page=3
97 +title : 오픈소스관리서비스(OlivePlatform)개발자모집(경력)
98 +tags : #Webfront,#Server
99 +url : https://careers.kakao.com/jobs/P-11991?page=3
100 +title : [데이터센터](경력/신입)카카오데이터센터DataScientist모집
101 +tags : #Algorithm/ML,#Algorithm/Ranking,#Hadoopecosystem,#Statistics/Analysis
102 +url : https://careers.kakao.com/jobs/P-11984?page=3
103 +title : 빅데이터플랫폼개발자모집
104 +tags : #Hadoopecosystem,#Server
105 +url : https://careers.kakao.com/jobs/P-11132?page=3
106 +title : 카카오데이터분석플랫폼Frontend개발자모집
107 +tags : #Webfront
108 +url : https://careers.kakao.com/jobs/P-11498?page=3
109 +title : 침해사고분석/대응담당자모집(경력)
110 +tags : #Security,#System,#Cloud,#Network
111 +url : https://careers.kakao.com/jobs/P-11986?page=3
112 +title : 데이터정책및데이터자산관리담당자모집(경력)
113 +tags : #Statistics/Analysis,#기타
114 +url : https://careers.kakao.com/jobs/P-11843?page=3
115 +title : 머신러닝엔지니어모집
116 +tags : #Algorithm/ML,#Algorithm/Ranking,#Statistics/Analysis,#NLP
117 +url : https://careers.kakao.com/jobs/P-11842?page=3
118 +title : 데이터인프라담당자모집(경력)
119 +tags : #Hadoopecosystem,#Server,#Statistics/Analysis
120 +url : https://careers.kakao.com/jobs/P-11841?page=3
121 +title : 광고데이터플랫폼개발자모집
122 +tags : #Webfront,#Server
123 +url : https://careers.kakao.com/jobs/P-11770?page=3
124 +title : 카카오재무시스템개발자모집
125 +tags : #Webfront,#Server
126 +url : https://careers.kakao.com/jobs/P-11166?page=3
127 +title : 비즈개발실서버개발자모집(경력)
128 +tags : #Hadoopecosystem,#Server
129 +url : https://careers.kakao.com/jobs/P-10931?page=3
130 +title : [동영상플랫폼]플랫폼서버개발자모집(경력)
131 +tags : #Webfront,#Network,#System,#Server
132 +url : https://careers.kakao.com/jobs/P-11977?page=3
133 +title : 동영상스트리밍플랫폼개발자모집(경력)
134 +tags : #System,#Cloud,#Server
135 +url : https://careers.kakao.com/jobs/P-11973?page=3
136 +title : 동영상그리드플랫폼개발자모집(경력)
137 +tags : #Network,#System,#기타
138 +url : https://careers.kakao.com/jobs/P-11974?page=4
139 +title : 챗봇및상담플랫폼서버개발자모집
140 +tags : #Hadoopecosystem,#Server
141 +url : https://careers.kakao.com/jobs/P-11133?page=4
142 +title : 추천시스템소프트웨어엔지니어
143 +tags : #System,#Network,#Cloud,#DB,#Algorithm/ML,#Algorithm/Ranking,#Server
144 +url : https://careers.kakao.com/jobs/P-10200?page=4
145 +title : 추천통계분석/데이터사이언티스트
146 +tags : #Algorithm/ML,#Statistics/Analysis
147 +url : https://careers.kakao.com/jobs/P-10913?page=4
148 +title : 빌링플랫폼개발자모집(경력)
149 +tags : #Server
150 +url : https://careers.kakao.com/jobs/P-11583?page=4
151 +title : [브런치/티스토리]iOS앱개발자모집(경력)
152 +tags : #iOS
153 +url : https://careers.kakao.com/jobs/P-11965?page=4
154 +title : [카카오맵]안드로이드지도API개발자모집
155 +tags : #Android,#기타
156 +url : https://careers.kakao.com/jobs/P-11966?page=4
157 +title : 시스템엔지니어를모십니다.
158 +tags : #System,#Network,#Cloud,#DB,#Security
159 +url : https://careers.kakao.com/jobs/P-10266?page=4
160 +title : 카카오전사QA모집(경력)
161 +tags : #QA
162 +url : https://careers.kakao.com/jobs/P-11723?page=4
163 +title : 광고어뷰징방지Platform연구및개발
164 +tags : #Algorithm/ML,#Algorithm/Ranking,#Hadoopecosystem,#Statistics/Analysis
165 +url : https://careers.kakao.com/jobs/P-10249?page=4
166 +title : 카카오SAPBC구축및운영채용(경력)
167 +tags : #기타
168 +url : https://careers.kakao.com/jobs/P-11902?page=4
169 +title : 추천시스템연구/개발
170 +tags : #Algorithm/ML,#Algorithm/Ranking,#Statistics/Analysis
171 +url : https://careers.kakao.com/jobs/P-9883?page=4
172 +title : 인사시스템개발자모집
173 +tags : #Webfront,#Server
174 +url : https://careers.kakao.com/jobs/P-9336?page=4
175 +title : 인프라서비스플랫폼개발자모집
176 +tags : #Webfront,#System,#Cloud,#Server
177 +url : https://careers.kakao.com/jobs/P-11485?page=4
178 +title : 업무지원시스템개발자모집
179 +tags : #Webfront,#DB,#Server
180 +url : https://careers.kakao.com/jobs/P-11515?page=4
181 +title : PostgreSQL운영및데이터베이스관리자모집
182 +tags : #DB,#Server
183 +url : https://careers.kakao.com/jobs/P-10791?page=5
184 +title : [멜론CIC]멜론상품구독&amp;마케팅서비스개발자모집(경력)
185 +tags : #Server
186 +url : https://careers.kakao.com/jobs/P-11375?page=5
187 +title : 카카오댓글/투표/공감웹서비스개발자모집
188 +tags : #Webfront,#Server
189 +url : https://careers.kakao.com/jobs/P-11102?page=5
190 +title : My구독서버개발자모집(경력)
191 +tags : #Server
192 +url : https://careers.kakao.com/jobs/P-11914?page=5
193 +title : 스팸및어뷰징분석/대응시스템개발자모집(경력)
194 +tags : #Server
195 +url : https://careers.kakao.com/jobs/P-11736?page=5
196 +title : 보안모니터링시스템개발및운영담당자모집(경력)
197 +tags : #Security,#Cloud,#DB,#Statistics/Analysis
198 +url : https://careers.kakao.com/jobs/P-11877?page=5
199 +title : 비즈플랫폼개발팀클라이언트개발자모집
200 +tags : #Android,#iOS
201 +url : https://careers.kakao.com/jobs/P-11164?page=5
202 +title : [API플랫폼]테크니컬어카운트매니저(TechnicalAccountManager)경력직모집
203 +tags : #Android,#iOS,#Webfront,#DB,#System,#Security,#Server
204 +url : https://careers.kakao.com/jobs/P-11541?page=5
205 +title : [티스토리/Daum블로그]Server경력개발자모집
206 +tags : #Webfront,#Server
207 +url : https://careers.kakao.com/jobs/P-11111?page=5
208 +title : 클라우드플랫폼TechnicalProductManager
209 +tags : #Cloud
210 +url : https://careers.kakao.com/jobs/P-11893?page=5
211 +title : 카카오분석플랫폼경력개발자모집
212 +tags : #Server,#Statistics/Analysis
213 +url : https://careers.kakao.com/jobs/P-11396?page=5
214 +title : 카카오서비스클라이언트Windows개발자모집(경력)
215 +tags : #Windows
216 +url : https://careers.kakao.com/jobs/P-11745?page=5
217 +title : 동영상서비스/플랫폼서버개발자모집
218 +tags : #Server
219 +url : https://careers.kakao.com/jobs/P-11395?page=5
220 +title : 동영상데이터/비즈플랫폼서버개발자모집
221 +tags : #Server
222 +url : https://careers.kakao.com/jobs/P-11430?page=5
223 +title : 테크니컬라이터(TechnicalWriter),DocumentationEngineering모집(경력)
224 +tags : #기타
225 +url : https://careers.kakao.com/jobs/P-11766?page=5
226 +title : KakaoTV동영상서비스모바일앱IOS개발자모집(경력)
227 +tags : #iOS
228 +url : https://careers.kakao.com/jobs/P-11735?page=6
229 +title : 카카오에디터SDK개발자모집(iOS)
230 +tags : #iOS
231 +url : https://careers.kakao.com/jobs/P-11323?page=6
232 +title : 사내계정플랫폼개발및운영담당자모집(경력)
233 +tags : #Webfront,#DB,#Server
234 +url : https://careers.kakao.com/jobs/P-11649?page=6
235 +title : 카카오광고서비스알고리즘및시스템개발
236 +tags : #Algorithm/Ranking
237 +url : https://careers.kakao.com/jobs/P-11011?page=6
238 +title : 카카오톡#탭,다음앱서비스앱개발자모집(Android,iOS)
239 +tags : #Android,#iOS
240 +url : https://careers.kakao.com/jobs/P-11095?page=6
241 +title : [톡캘린더]서버개발자모집(경력)
242 +tags : #Server
243 +url : https://careers.kakao.com/jobs/P-11687?page=6
244 +title : 데이터사이언티스트모집(경력)
245 +tags : #Algorithm/ML,#Algorithm/Ranking,#Statistics/Analysis
246 +url : https://careers.kakao.com/jobs/P-11816?page=6
247 +title : 융합기술개발자영입
248 +tags : #Algorithm/ML
249 +url : https://careers.kakao.com/jobs/P-10637?page=6
250 +title : [카카오톡]macOS클라이언트개발자모집(경력)
251 +tags : #iOS,#기타
252 +url : https://careers.kakao.com/jobs/P-11797?page=6
253 +title : 카카오톡보이스톡/페이스톡/라이브톡개발자모집
254 +tags : #Android,#iOS,#Windows,#Webfront,#Server,#기타
255 +url : https://careers.kakao.com/jobs/P-11082?page=6
256 +title : 미래전략추진실데이터분석플랫폼개발자모집(경력)
257 +tags : #Webfront,#Hadoopecosystem,#Server,#Statistics/Analysis
258 +url : https://careers.kakao.com/jobs/P-11694?page=6
259 +title : [KakaoTV]동영상서비스Frontend개발자모집(경력)
260 +tags : #Webfront
261 +url : https://careers.kakao.com/jobs/P-11783?page=6
262 +title : 개발자를위한기술플랫폼엔지니어모집(경력)
263 +tags : #Webfront,#Server
264 +url : https://careers.kakao.com/jobs/P-11779?page=6
265 +title : Hbase,mongoDB,kafka개발자모집(경력)
266 +tags : #Server
267 +url : https://careers.kakao.com/jobs/P-11677?page=6
268 +title : 카카오톡QA모집
269 +tags : #QA
270 +url : https://careers.kakao.com/jobs/P-11348?page=6
271 +title : [조기마감예정][Search] NLP 기술 개발 체험형 인턴십 모집
272 +tags :
273 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005599&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
274 +title : NAVER FINANCIAL 2021 상반기 경력 개발자 공채
275 +tags :
276 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005943&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
277 +title : [Papago] 파파고 머신러닝(자연어처리) 엔지니어 체험형 인턴 모집
278 +tags :
279 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005884&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
280 +title : [Platform Engineering] Front-end Engineer
281 +tags : Platform Engineering,Software Development,Front-end,경력
282 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20003277&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
283 +title : [Platform Engineering] Server Engineer
284 +tags : Platform Engineering,Software Development,Server,경력,Backend
285 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20002849&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
286 +title : [AR] Graphics Engineer
287 +tags : AR,Graphics,경력,신입
288 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20003590&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
289 +title : [AR] AR Software Engineer
290 +tags : AR,신입,경력
291 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005864&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
292 +title : [NAVER Cloud] 네이버 전사 장애/변경 관리자 경력사원 모집
293 +tags : 장애,변경,ITIL,재발방지,모니터링
294 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005368&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
295 +title : 네이버웹툰(유) SW 개발자(경력) 모집
296 +tags : BE,FE,빅데이터플랫폼,안드로이드,iOS
297 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005947&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
298 +title : 네이버웹툰(유) AI/ML 연구 개발자(경력) 모집
299 +tags : 리서처,리서치엔지니어
300 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005948&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
301 +title : Android 개발자 모집
302 +tags :
303 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005944&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
304 +title : [NAVER Cloud] 클라우드 인프라 보안 경력사원 모집
305 +tags : 클라우드 보안,클라우드 인프라 보안,인프라 보안
306 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005698&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
307 +title : [NAVER Cloud] 클라우드 개인정보보호 경력사원 모집
308 +tags : 개인정보보호,클라우드 개인정보,클라우드 정보보호
309 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005699&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
310 +title : [NAVER Cloud] 클라우드 서비스 보안 인증 대응 경력사원 모집
311 +tags : 클라우드 보안,보안 인증,보안 컨설팅
312 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005322&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
313 +title : [NAVER Cloud] 클라우드 RootCA 서비스 운영 경력사원 모집
314 +tags : RootCA,SSL,PKI,WebTrust
315 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005324&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
316 +title : [NAVER Cloud] 클라우드 보안 컨설팅 경력사원 모집
317 +tags : 클라우드 보안 컨설팅,보안 아키텍트
318 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005325&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
319 +title : [NAVER Cloud] 클라우드 보안 서비스 기획 경력사원 모집
320 +tags : 보안 서비스,클라우드 보안 기획
321 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005326&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
322 +title : [NAVER Cloud] Infrastructure Security Engineer_오피스 보안 경력사원 모집
323 +tags :
324 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005942&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
325 +title : [NAVER Cloud] MYBOX Desktop Client 개발 경력사원 모집
326 +tags : C,C++,C,Windows,Objecive-c
327 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005796&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
328 +title : [NAVER Cloud] WORKBOX(파일공유) 서버 개발 경력사원 모집
329 +tags : 백엔드,Java,Spring,ElasticSearch
330 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005798&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
331 +title : [NAVER Cloud] WORKBOX(파일공유) 모바일 앱 개발 경력사원 모집
332 +tags : 모바일,APP,iOS,Android
333 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005799&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
334 +title : [NAVER Cloud] 네이버 폼 신규 개발 경력사원 모집
335 +tags : 프론트엔드,백엔드,Javascript,node.js,typescript
336 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005818&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
337 +title : [NAVER Cloud] 클라우드 PaaS 상품 개발 (Backend) 경력사원 모집
338 +tags : Backend,Java,Spring,Cloud,PaaS
339 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005402&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
340 +title : [NAVER Cloud] PaaS-TA 서비스 개발 경력사원 모집
341 +tags : Backend,Java,PaaS-TA,CloudFoundry,BOSH
342 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005403&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
343 +title : [NAVER Cloud] Kubernetes 서비스 개발 경력사원 모집
344 +tags : Golang,kubernetes,CNI,Ansible
345 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005404&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
346 +title : [NAVER Cloud] Blockchain 서비스 개발 경력사원 모집
347 +tags : Blockchain,Hyperledger,Kubernetes
348 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005405&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
349 +title : [NAVER Cloud] API Gateway 서비스 개발 경력사원 모집
350 +tags : Java,Spring,API Gateway,RESTful
351 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005406&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
352 +title : [NAVER Cloud] Data Analytics 서비스 개발 경력사원 모집
353 +tags : Java,Spring,하둡,빅데이터,ELK
354 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005407&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
355 +title : [NAVER Cloud] Marketplace 서비스 개발 경력사원 모집
356 +tags : Backend,Java,Spring
357 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005408&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
358 +title : [NAVER Cloud] MLOps 서비스 개발 경력사원 모집
359 +tags : Backend,Java,Kubeflow,Tensorflow,ML
360 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005409&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
361 +title : [NAVER Cloud] Object Storage 서비스 개발 경력사원 모집
362 +tags : Backend,Java,C/C++,Storage
363 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005410&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
364 +title : [NAVER Cloud] Data Sandbox 서비스 개발 경력사원 모집
365 +tags : Backend,Java,Spring
366 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005411&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
367 +title : [NAVER Cloud] IoT 서비스 개발 경력사원 모집
368 +tags : Backend
369 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005412&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
370 +title : [NAVER Cloud] Cloud Functions 서비스 개발 경력사원 모집
371 +tags : Backend
372 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005414&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
373 +title : [NAVER Cloud] Video Conference 서비스 개발 경력사원 모집
374 +tags : Backend,Java,Spring,WebRT,C Media
375 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005416&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
376 +title : [NAVER Cloud] 클라우드 품질 보증 관리자 경력사원 모집
377 +tags : QA,Test,Automation,Process
378 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005417&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
379 +title : [NAVER Cloud] 클라우드 Portal 서비스 개발 (FE 직무) 경력사원 모집
380 +tags : Frontend,Javascript,Typescript,vue.js
381 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005454&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
382 +title : [NAVER Cloud] 클라우드 Console 서비스 개발 경력사원 모집
383 +tags : Frontend,Javascript,Typescript,vue.js
384 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005456&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
385 +title : [NAVER Cloud] 클라우드 상품 기술지원 엔지니어 경력사원 모집
386 +tags : Technical support,System operator,기술지원
387 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005474&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
388 +title : [NAVER Cloud] 클라우드 Portal 서비스 개발 (BE 직무) 경력사원 모집
389 +tags : Backend,Java,Spring
390 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005739&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
391 +title : [NAVER Cloud] Cloud System Engineer 경력사원 모집
392 +tags : system,engineer,Hypervisor,Linux,Windows
393 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005379&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
394 +title : [NAVER Cloud] Network Engineering 경력사원 모집
395 +tags : Network,Routing,Switching
396 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005330&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
397 +title : [NAVER Cloud] System Engineering 경력사원 모집
398 +tags : Server. DevOps. Linux. KVM. Container
399 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005377&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
400 +title : [NAVER Cloud] 클라우드 네트워크 운영 경력사원 모집
401 +tags : 네트워크 가상화,MPLS VPN,EVPN,IPSecVPN
402 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005622&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
403 +title : [NAVER Cloud] 데이터센터 엔지니어(건축) 경력사원 모집
404 +tags :
405 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005734&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
406 +title : [NAVER Cloud] Security Engineer_모의해킹 경력사원 모집
407 +tags : 모의해킹,취약점,화이트해커,해킹,CVE
408 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005894&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
409 +title : [NAVER Cloud] Security Engineer_취약점 분석 경력사원 모집
410 +tags : 취약점,보안점검,어플리케이션,소스코드
411 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005895&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
412 +title : [NAVER Cloud] 클라우드 서비스 플랫폼 테크니컬 매니저 경력사원 모집
413 +tags : Cloud Consulting,technical support,Platform 기획/관리,Customer care
414 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005309&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
415 +title : [NAVER Cloud] 클라우드 SaaS 개발
416 +tags : Java,Spring,ERP,그룹웨어
417 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005360&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
418 +title : [NAVER Cloud] Infra Security 경력사원 모집
419 +tags : security,보안솔루션
420 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005362&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
421 +title : [NAVER Cloud] CERT 경력사원 모집
422 +tags : security,침해사고,cert
423 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005363&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
424 +title : [NAVER Cloud] 가상화 코어 기술 연구 및 개발 (하드웨어 분야) 경력사원 모집
425 +tags : FPGA,Verilog,System Verilog
426 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005370&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
427 +title : [NAVER Cloud] 가상화 코어 기술 연구 및 개발 (소프트웨어 분야) 경력사원 모집
428 +tags : 백엔드,Go,C/C++,가상화
429 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005373&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
430 +title : [NAVER Cloud] Naver Cloud Platform의 hypervisor orchestration 및 RESTful api 와 관리도구 개발 경력사원 모집
431 +tags : java,go,python,shell script,linux
432 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005378&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
433 +title : [NAVER Cloud] 네이버클라우드 클라우드 데이터베이스 상품개발 (MSSQL) 경력사원 모집
434 +tags : C,PowerShell,Cloud,Cloud DB,MSSQL
435 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005382&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
436 +title : [NAVER Cloud] 네이버클라우드 빅데이터플랫폼 상품개발 경력사원 모집
437 +tags : Hadoop,Cloud,EMR,HDInsight,BigQuery
438 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005383&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
439 +title : [NAVER Cloud] MySQL 데이터베이스 엔지니어 (DBA) 경력사원 모집
440 +tags : MySQL,DBA,Database,데이터베이스,DB
441 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005384&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
442 +title : [NAVER Cloud] PostgreSQL 데이터베이스 엔지니어 (DBA) 경력사원 모집
443 +tags : PostgreSQL,DBA,Database,데이터베이스,DB
444 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005386&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
445 +title : [NAVER Cloud] MongoDB 데이터베이스 엔지니어 (DBA) 경력사원 모집
446 +tags : MongoDB,DBA,Database,데이터베이스
447 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005393&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
448 +title : [NAVER Cloud] 클라우드 데이터베이스 엔지니어 (DBA) 경력사원 모집
449 +tags : MySQL,Redis,Cloud,Cloud DB,DBA
450 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005394&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
451 +title : [NAVER Cloud] Cloud 네트워크 솔루션 및 SDN 기술 개발 경력사원 모집
452 +tags : Network,Golang,SDN
453 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005396&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
454 +title : [NAVER Cloud] 클라우드 데이터 보호 서비스 개발 경력사원 모집
455 +tags : Java,Spring,JPA,암호화
456 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005398&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
457 +title : [NAVER Cloud] 클라우드 보안 서비스 개발 경력사원 모집
458 +tags : Java,Spring,Javascript,보안
459 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005399&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
460 +title : [NAVER Cloud] 클라우드 개발자 도구 서비스 개발 경력사원 모집
461 +tags : Java,Node.js,Javascript,Typescript,DevOps
462 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005400&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
463 +title : [NAVER Cloud] NAVER Cloud Platform 빌링 경력사원 모집
464 +tags : 백엔드,java,미터링,빌링,Rest API
465 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005401&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
466 +title : [NAVER Cloud] MYBOX 서버 개발 경력사원 모집
467 +tags : 백엔드,Java,C++,Spring,ElasticSearch
468 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005267&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
469 +title : 2021 WORKS MOBILE 경력사원 인재 Pool
470 +tags :
471 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20003639&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
472 +title : 네이버 월간영입 : 기술 직군(6월)
473 +tags :
474 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005978&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
475 +title : 정보 보안 담당자 모집
476 +tags :
477 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005902&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
478 +title : NAVER D2 Startup Factory 스타트업 투자담당 경력사원 모집
479 +tags : D2,투자,경력
480 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005542&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
481 +title : 프론트엔드 개발자 상시 모집 (경력 3년 이상)
482 +tags : FE ,프론트엔드 ,상시지원 ,상시검토
483 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005030&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
484 +title : 백엔드 개발자 상시 모집 (경력 3년 이상)
485 +tags : BE ,백엔드 ,상시지원 ,상시검토
486 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005204&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
487 +title : NAVER 기술플랫폼 전략 담당자 모집
488 +tags :
489 +url : https://recruit.navercorp.com/naver/job/detail/developer?annoId=20005897&amp;classId=&amp;jobId=&amp;entTypeCd=&amp;searchTxt=&amp;searchSysComCd=
...@@ -27,7 +27,8 @@ const makeObject = (array)=>{ ...@@ -27,7 +27,8 @@ const makeObject = (array)=>{
27 else if (tempData.length == 4){ 27 else if (tempData.length == 4){
28 result.push({ 28 result.push({
29 title : tempData[0], 29 title : tempData[0],
30 - url : tempData[3] 30 + url : tempData[3],
31 + tags : []
31 }) 32 })
32 } 33 }
33 } 34 }
......
...@@ -180,6 +180,18 @@ ...@@ -180,6 +180,18 @@
180 "node": ">= 0.8" 180 "node": ">= 0.8"
181 } 181 }
182 }, 182 },
183 + "node_modules/call-bind": {
184 + "version": "1.0.2",
185 + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
186 + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
187 + "dependencies": {
188 + "function-bind": "^1.1.1",
189 + "get-intrinsic": "^1.0.2"
190 + },
191 + "funding": {
192 + "url": "https://github.com/sponsors/ljharb"
193 + }
194 + },
183 "node_modules/cheerio": { 195 "node_modules/cheerio": {
184 "version": "1.0.0-rc.9", 196 "version": "1.0.0-rc.9",
185 "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.9.tgz", 197 "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.9.tgz",
...@@ -262,6 +274,18 @@ ...@@ -262,6 +274,18 @@
262 "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 274 "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
263 "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 275 "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
264 }, 276 },
277 + "node_modules/cron-parser": {
278 + "version": "3.5.0",
279 + "resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-3.5.0.tgz",
280 + "integrity": "sha512-wyVZtbRs6qDfFd8ap457w3XVntdvqcwBGxBoTvJQH9KGVKL/fB+h2k3C8AqiVxvUQKN1Ps/Ns46CNViOpVDhfQ==",
281 + "dependencies": {
282 + "is-nan": "^1.3.2",
283 + "luxon": "^1.26.0"
284 + },
285 + "engines": {
286 + "node": ">=0.8"
287 + }
288 + },
265 "node_modules/css-select": { 289 "node_modules/css-select": {
266 "version": "4.1.2", 290 "version": "4.1.2",
267 "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.1.2.tgz", 291 "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.1.2.tgz",
...@@ -304,6 +328,17 @@ ...@@ -304,6 +328,17 @@
304 "node": ">=0.10.0" 328 "node": ">=0.10.0"
305 } 329 }
306 }, 330 },
331 + "node_modules/define-properties": {
332 + "version": "1.1.3",
333 + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
334 + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
335 + "dependencies": {
336 + "object-keys": "^1.0.12"
337 + },
338 + "engines": {
339 + "node": ">= 0.4"
340 + }
341 + },
307 "node_modules/depd": { 342 "node_modules/depd": {
308 "version": "1.1.2", 343 "version": "1.1.2",
309 "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 344 "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
...@@ -570,6 +605,24 @@ ...@@ -570,6 +605,24 @@
570 "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 605 "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
571 "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" 606 "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
572 }, 607 },
608 + "node_modules/function-bind": {
609 + "version": "1.1.1",
610 + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
611 + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
612 + },
613 + "node_modules/get-intrinsic": {
614 + "version": "1.1.1",
615 + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
616 + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==",
617 + "dependencies": {
618 + "function-bind": "^1.1.1",
619 + "has": "^1.0.3",
620 + "has-symbols": "^1.0.1"
621 + },
622 + "funding": {
623 + "url": "https://github.com/sponsors/ljharb"
624 + }
625 + },
573 "node_modules/get-stream": { 626 "node_modules/get-stream": {
574 "version": "5.2.0", 627 "version": "5.2.0",
575 "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", 628 "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
...@@ -603,6 +656,28 @@ ...@@ -603,6 +656,28 @@
603 "url": "https://github.com/sponsors/isaacs" 656 "url": "https://github.com/sponsors/isaacs"
604 } 657 }
605 }, 658 },
659 + "node_modules/has": {
660 + "version": "1.0.3",
661 + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
662 + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
663 + "dependencies": {
664 + "function-bind": "^1.1.1"
665 + },
666 + "engines": {
667 + "node": ">= 0.4.0"
668 + }
669 + },
670 + "node_modules/has-symbols": {
671 + "version": "1.0.2",
672 + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz",
673 + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==",
674 + "engines": {
675 + "node": ">= 0.4"
676 + },
677 + "funding": {
678 + "url": "https://github.com/sponsors/ljharb"
679 + }
680 + },
606 "node_modules/htmlparser2": { 681 "node_modules/htmlparser2": {
607 "version": "6.1.0", 682 "version": "6.1.0",
608 "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", 683 "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz",
...@@ -721,6 +796,21 @@ ...@@ -721,6 +796,21 @@
721 "node": ">= 0.10" 796 "node": ">= 0.10"
722 } 797 }
723 }, 798 },
799 + "node_modules/is-nan": {
800 + "version": "1.3.2",
801 + "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz",
802 + "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==",
803 + "dependencies": {
804 + "call-bind": "^1.0.0",
805 + "define-properties": "^1.1.3"
806 + },
807 + "engines": {
808 + "node": ">= 0.4"
809 + },
810 + "funding": {
811 + "url": "https://github.com/sponsors/ljharb"
812 + }
813 + },
724 "node_modules/is-plain-object": { 814 "node_modules/is-plain-object": {
725 "version": "5.0.0", 815 "version": "5.0.0",
726 "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", 816 "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
...@@ -748,6 +838,19 @@ ...@@ -748,6 +838,19 @@
748 "node": ">=8" 838 "node": ">=8"
749 } 839 }
750 }, 840 },
841 + "node_modules/long-timeout": {
842 + "version": "0.1.1",
843 + "resolved": "https://registry.npmjs.org/long-timeout/-/long-timeout-0.1.1.tgz",
844 + "integrity": "sha1-lyHXiLR+C8taJMLivuGg2lXatRQ="
845 + },
846 + "node_modules/luxon": {
847 + "version": "1.27.0",
848 + "resolved": "https://registry.npmjs.org/luxon/-/luxon-1.27.0.tgz",
849 + "integrity": "sha512-VKsFsPggTA0DvnxtJdiExAucKdAnwbCCNlMM5ENvHlxubqWd0xhZcdb4XgZ7QFNhaRhilXCFxHuoObP5BNA4PA==",
850 + "engines": {
851 + "node": "*"
852 + }
853 + },
751 "node_modules/media-typer": { 854 "node_modules/media-typer": {
752 "version": "0.3.0", 855 "version": "0.3.0",
753 "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 856 "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
...@@ -847,6 +950,19 @@ ...@@ -847,6 +950,19 @@
847 "node": "4.x || >=6.0.0" 950 "node": "4.x || >=6.0.0"
848 } 951 }
849 }, 952 },
953 + "node_modules/node-schedule": {
954 + "version": "2.0.0",
955 + "resolved": "https://registry.npmjs.org/node-schedule/-/node-schedule-2.0.0.tgz",
956 + "integrity": "sha512-cHc9KEcfiuXxYDU+HjsBVo2FkWL1jRAUoczFoMIzRBpOA4p/NRHuuLs85AWOLgKsHtSPjN8csvwIxc2SqMv+CQ==",
957 + "dependencies": {
958 + "cron-parser": "^3.1.0",
959 + "long-timeout": "0.1.1",
960 + "sorted-array-functions": "^1.3.0"
961 + },
962 + "engines": {
963 + "node": ">=6"
964 + }
965 + },
850 "node_modules/nth-check": { 966 "node_modules/nth-check": {
851 "version": "2.0.0", 967 "version": "2.0.0",
852 "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.0.tgz", 968 "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.0.tgz",
...@@ -858,6 +974,14 @@ ...@@ -858,6 +974,14 @@
858 "url": "https://github.com/fb55/nth-check?sponsor=1" 974 "url": "https://github.com/fb55/nth-check?sponsor=1"
859 } 975 }
860 }, 976 },
977 + "node_modules/object-keys": {
978 + "version": "1.1.1",
979 + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
980 + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
981 + "engines": {
982 + "node": ">= 0.4"
983 + }
984 + },
861 "node_modules/on-finished": { 985 "node_modules/on-finished": {
862 "version": "2.3.0", 986 "version": "2.3.0",
863 "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 987 "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
...@@ -1196,6 +1320,11 @@ ...@@ -1196,6 +1320,11 @@
1196 "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", 1320 "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
1197 "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" 1321 "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
1198 }, 1322 },
1323 + "node_modules/sorted-array-functions": {
1324 + "version": "1.3.0",
1325 + "resolved": "https://registry.npmjs.org/sorted-array-functions/-/sorted-array-functions-1.3.0.tgz",
1326 + "integrity": "sha512-2sqgzeFlid6N4Z2fUQ1cvFmTOLRi/sEDzSQ0OKYchqgoPmQBVyM3959qYx3fpS6Esef80KjmpgPeEr028dP3OA=="
1327 + },
1199 "node_modules/source-map": { 1328 "node_modules/source-map": {
1200 "version": "0.6.1", 1329 "version": "0.6.1",
1201 "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 1330 "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
......
1 +{
2 + "root": true,
3 +
4 + "extends": "@ljharb",
5 +
6 + "rules": {
7 + "func-name-matching": 0,
8 + "id-length": 0,
9 + "new-cap": [2, {
10 + "capIsNewExceptions": [
11 + "GetIntrinsic",
12 + ],
13 + }],
14 + "no-magic-numbers": 0,
15 + "operator-linebreak": [2, "before"],
16 + },
17 +}
1 +# These are supported funding model platforms
2 +
3 +github: [ljharb]
4 +patreon: # Replace with a single Patreon username
5 +open_collective: # Replace with a single Open Collective username
6 +ko_fi: # Replace with a single Ko-fi username
7 +tidelift: npm/call-bind
8 +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 +liberapay: # Replace with a single Liberapay username
10 +issuehunt: # Replace with a single IssueHunt username
11 +otechie: # Replace with a single Otechie username
12 +custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
1 +{
2 + "all": true,
3 + "check-coverage": false,
4 + "reporter": ["text-summary", "text", "html", "json"],
5 + "lines": 86,
6 + "statements": 85.93,
7 + "functions": 82.43,
8 + "branches": 76.06,
9 + "exclude": [
10 + "coverage",
11 + "test"
12 + ]
13 +}
1 +# Changelog
2 +
3 +All notable changes to this project will be documented in this file.
4 +
5 +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
6 +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7 +
8 +## [v1.0.2](https://github.com/ljharb/call-bind/compare/v1.0.1...v1.0.2) - 2021-01-11
9 +
10 +### Commits
11 +
12 +- [Fix] properly include the receiver in the bound length [`dbae7bc`](https://github.com/ljharb/call-bind/commit/dbae7bc676c079a0d33c0a43e9ef92cb7b01345d)
13 +
14 +## [v1.0.1](https://github.com/ljharb/call-bind/compare/v1.0.0...v1.0.1) - 2021-01-08
15 +
16 +### Commits
17 +
18 +- [Tests] migrate tests to Github Actions [`b6db284`](https://github.com/ljharb/call-bind/commit/b6db284c36f8ccd195b88a6764fe84b7223a0da1)
19 +- [meta] do not publish github action workflow files [`ec7fe46`](https://github.com/ljharb/call-bind/commit/ec7fe46e60cfa4764ee943d2755f5e5a366e578e)
20 +- [Fix] preserve original function’s length when possible [`adbceaa`](https://github.com/ljharb/call-bind/commit/adbceaa3cac4b41ea78bb19d7ccdbaaf7e0bdadb)
21 +- [Tests] gather coverage data on every job [`d69e23c`](https://github.com/ljharb/call-bind/commit/d69e23cc65f101ba1d4c19bb07fa8eb0ec624be8)
22 +- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `aud`, `tape` [`2fd3586`](https://github.com/ljharb/call-bind/commit/2fd3586c5d47b335364c14293114c6b625ae1f71)
23 +- [Deps] update `get-intrinsic` [`f23e931`](https://github.com/ljharb/call-bind/commit/f23e9318cc271c2add8bb38cfded85ee7baf8eee)
24 +- [Deps] update `get-intrinsic` [`72d9f44`](https://github.com/ljharb/call-bind/commit/72d9f44e184465ba8dd3fb48260bbcff234985f2)
25 +- [meta] fix FUNDING.yml [`e723573`](https://github.com/ljharb/call-bind/commit/e723573438c5a68dcec31fb5d96ea6b7e4a93be8)
26 +- [eslint] ignore coverage output [`15e76d2`](https://github.com/ljharb/call-bind/commit/15e76d28a5f43e504696401e5b31ebb78ee1b532)
27 +- [meta] add Automatic Rebase and Require Allow Edits workflows [`8fa4dab`](https://github.com/ljharb/call-bind/commit/8fa4dabb23ba3dd7bb92c9571c1241c08b56e4b6)
28 +
29 +## v1.0.0 - 2020-10-30
30 +
31 +### Commits
32 +
33 +- Initial commit [`306cf98`](https://github.com/ljharb/call-bind/commit/306cf98c7ec9e7ef66b653ec152277ac1381eb50)
34 +- Tests [`e10d0bb`](https://github.com/ljharb/call-bind/commit/e10d0bbdadc7a10ecedc9a1c035112d3e368b8df)
35 +- Implementation [`43852ed`](https://github.com/ljharb/call-bind/commit/43852eda0f187327b7fad2423ca972149a52bd65)
36 +- npm init [`408f860`](https://github.com/ljharb/call-bind/commit/408f860b773a2f610805fd3613d0d71bac1b6249)
37 +- [meta] add Automatic Rebase and Require Allow Edits workflows [`fb349b2`](https://github.com/ljharb/call-bind/commit/fb349b2e48defbec8b5ec8a8395cc8f69f220b13)
38 +- [meta] add `auto-changelog` [`c4001fc`](https://github.com/ljharb/call-bind/commit/c4001fc43031799ef908211c98d3b0fb2b60fde4)
39 +- [meta] add "funding"; create `FUNDING.yml` [`d4d6d29`](https://github.com/ljharb/call-bind/commit/d4d6d2974a14bc2e98830468eda7fe6d6a776717)
40 +- [Tests] add `npm run lint` [`dedfb98`](https://github.com/ljharb/call-bind/commit/dedfb98bd0ecefb08ddb9a94061bd10cde4332af)
41 +- Only apps should have lockfiles [`54ac776`](https://github.com/ljharb/call-bind/commit/54ac77653db45a7361dc153d2f478e743f110650)
42 +- [meta] add `safe-publish-latest` [`9ea8e43`](https://github.com/ljharb/call-bind/commit/9ea8e435b950ce9b705559cd651039f9bf40140f)
1 +MIT License
2 +
3 +Copyright (c) 2020 Jordan Harband
4 +
5 +Permission is hereby granted, free of charge, to any person obtaining a copy
6 +of this software and associated documentation files (the "Software"), to deal
7 +in the Software without restriction, including without limitation the rights
8 +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 +copies of the Software, and to permit persons to whom the Software is
10 +furnished to do so, subject to the following conditions:
11 +
12 +The above copyright notice and this permission notice shall be included in all
13 +copies or substantial portions of the Software.
14 +
15 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 +SOFTWARE.
1 +# call-bind
2 +Robustly `.call.bind()` a function.
1 +'use strict';
2 +
3 +var GetIntrinsic = require('get-intrinsic');
4 +
5 +var callBind = require('./');
6 +
7 +var $indexOf = callBind(GetIntrinsic('String.prototype.indexOf'));
8 +
9 +module.exports = function callBoundIntrinsic(name, allowMissing) {
10 + var intrinsic = GetIntrinsic(name, !!allowMissing);
11 + if (typeof intrinsic === 'function' && $indexOf(name, '.prototype.') > -1) {
12 + return callBind(intrinsic);
13 + }
14 + return intrinsic;
15 +};
1 +'use strict';
2 +
3 +var bind = require('function-bind');
4 +var GetIntrinsic = require('get-intrinsic');
5 +
6 +var $apply = GetIntrinsic('%Function.prototype.apply%');
7 +var $call = GetIntrinsic('%Function.prototype.call%');
8 +var $reflectApply = GetIntrinsic('%Reflect.apply%', true) || bind.call($call, $apply);
9 +
10 +var $gOPD = GetIntrinsic('%Object.getOwnPropertyDescriptor%', true);
11 +var $defineProperty = GetIntrinsic('%Object.defineProperty%', true);
12 +var $max = GetIntrinsic('%Math.max%');
13 +
14 +if ($defineProperty) {
15 + try {
16 + $defineProperty({}, 'a', { value: 1 });
17 + } catch (e) {
18 + // IE 8 has a broken defineProperty
19 + $defineProperty = null;
20 + }
21 +}
22 +
23 +module.exports = function callBind(originalFunction) {
24 + var func = $reflectApply(bind, $call, arguments);
25 + if ($gOPD && $defineProperty) {
26 + var desc = $gOPD(func, 'length');
27 + if (desc.configurable) {
28 + // original length, plus the receiver, minus any additional arguments (after the receiver)
29 + $defineProperty(
30 + func,
31 + 'length',
32 + { value: 1 + $max(0, originalFunction.length - (arguments.length - 1)) }
33 + );
34 + }
35 + }
36 + return func;
37 +};
38 +
39 +var applyBind = function applyBind() {
40 + return $reflectApply(bind, $apply, arguments);
41 +};
42 +
43 +if ($defineProperty) {
44 + $defineProperty(module.exports, 'apply', { value: applyBind });
45 +} else {
46 + module.exports.apply = applyBind;
47 +}
1 +{
2 + "name": "call-bind",
3 + "version": "1.0.2",
4 + "description": "Robustly `.call.bind()` a function",
5 + "main": "index.js",
6 + "exports": {
7 + ".": [
8 + {
9 + "default": "./index.js"
10 + },
11 + "./index.js"
12 + ],
13 + "./callBound": [
14 + {
15 + "default": "./callBound.js"
16 + },
17 + "./callBound.js"
18 + ],
19 + "./package.json": "./package.json"
20 + },
21 + "scripts": {
22 + "prepublish": "safe-publish-latest",
23 + "lint": "eslint --ext=.js,.mjs .",
24 + "pretest": "npm run lint",
25 + "tests-only": "nyc tape 'test/*'",
26 + "test": "npm run tests-only",
27 + "posttest": "aud --production",
28 + "version": "auto-changelog && git add CHANGELOG.md",
29 + "postversion": "auto-changelog && git add CHANGELOG.md && git commit --no-edit --amend && git tag -f \"v$(node -e \"console.log(require('./package.json').version)\")\""
30 + },
31 + "repository": {
32 + "type": "git",
33 + "url": "git+https://github.com/ljharb/call-bind.git"
34 + },
35 + "keywords": [
36 + "javascript",
37 + "ecmascript",
38 + "es",
39 + "js",
40 + "callbind",
41 + "callbound",
42 + "call",
43 + "bind",
44 + "bound",
45 + "call-bind",
46 + "call-bound",
47 + "function",
48 + "es-abstract"
49 + ],
50 + "author": "Jordan Harband <ljharb@gmail.com>",
51 + "funding": {
52 + "url": "https://github.com/sponsors/ljharb"
53 + },
54 + "license": "MIT",
55 + "bugs": {
56 + "url": "https://github.com/ljharb/call-bind/issues"
57 + },
58 + "homepage": "https://github.com/ljharb/call-bind#readme",
59 + "devDependencies": {
60 + "@ljharb/eslint-config": "^17.3.0",
61 + "aud": "^1.1.3",
62 + "auto-changelog": "^2.2.1",
63 + "eslint": "^7.17.0",
64 + "nyc": "^10.3.2",
65 + "safe-publish-latest": "^1.1.4",
66 + "tape": "^5.1.1"
67 + },
68 + "dependencies": {
69 + "function-bind": "^1.1.1",
70 + "get-intrinsic": "^1.0.2"
71 + },
72 + "auto-changelog": {
73 + "output": "CHANGELOG.md",
74 + "template": "keepachangelog",
75 + "unreleased": false,
76 + "commitLimit": false,
77 + "backfillLimit": false,
78 + "hideCredit": true
79 + }
80 +}
1 +'use strict';
2 +
3 +var test = require('tape');
4 +
5 +var callBound = require('../callBound');
6 +
7 +test('callBound', function (t) {
8 + // static primitive
9 + t.equal(callBound('Array.length'), Array.length, 'Array.length yields itself');
10 + t.equal(callBound('%Array.length%'), Array.length, '%Array.length% yields itself');
11 +
12 + // static non-function object
13 + t.equal(callBound('Array.prototype'), Array.prototype, 'Array.prototype yields itself');
14 + t.equal(callBound('%Array.prototype%'), Array.prototype, '%Array.prototype% yields itself');
15 + t.equal(callBound('Array.constructor'), Array.constructor, 'Array.constructor yields itself');
16 + t.equal(callBound('%Array.constructor%'), Array.constructor, '%Array.constructor% yields itself');
17 +
18 + // static function
19 + t.equal(callBound('Date.parse'), Date.parse, 'Date.parse yields itself');
20 + t.equal(callBound('%Date.parse%'), Date.parse, '%Date.parse% yields itself');
21 +
22 + // prototype primitive
23 + t.equal(callBound('Error.prototype.message'), Error.prototype.message, 'Error.prototype.message yields itself');
24 + t.equal(callBound('%Error.prototype.message%'), Error.prototype.message, '%Error.prototype.message% yields itself');
25 +
26 + // prototype function
27 + t.notEqual(callBound('Object.prototype.toString'), Object.prototype.toString, 'Object.prototype.toString does not yield itself');
28 + t.notEqual(callBound('%Object.prototype.toString%'), Object.prototype.toString, '%Object.prototype.toString% does not yield itself');
29 + t.equal(callBound('Object.prototype.toString')(true), Object.prototype.toString.call(true), 'call-bound Object.prototype.toString calls into the original');
30 + t.equal(callBound('%Object.prototype.toString%')(true), Object.prototype.toString.call(true), 'call-bound %Object.prototype.toString% calls into the original');
31 +
32 + t['throws'](
33 + function () { callBound('does not exist'); },
34 + SyntaxError,
35 + 'nonexistent intrinsic throws'
36 + );
37 + t['throws'](
38 + function () { callBound('does not exist', true); },
39 + SyntaxError,
40 + 'allowMissing arg still throws for unknown intrinsic'
41 + );
42 +
43 + /* globals WeakRef: false */
44 + t.test('real but absent intrinsic', { skip: typeof WeakRef !== 'undefined' }, function (st) {
45 + st['throws'](
46 + function () { callBound('WeakRef'); },
47 + TypeError,
48 + 'real but absent intrinsic throws'
49 + );
50 + st.equal(callBound('WeakRef', true), undefined, 'allowMissing arg avoids exception');
51 + st.end();
52 + });
53 +
54 + t.end();
55 +});
1 +'use strict';
2 +
3 +var callBind = require('../');
4 +var bind = require('function-bind');
5 +
6 +var test = require('tape');
7 +
8 +/*
9 + * older engines have length nonconfigurable
10 + * in io.js v3, it is configurable except on bound functions, hence the .bind()
11 + */
12 +var functionsHaveConfigurableLengths = !!(
13 + Object.getOwnPropertyDescriptor
14 + && Object.getOwnPropertyDescriptor(bind.call(function () {}), 'length').configurable
15 +);
16 +
17 +test('callBind', function (t) {
18 + var sentinel = { sentinel: true };
19 + var func = function (a, b) {
20 + // eslint-disable-next-line no-invalid-this
21 + return [this, a, b];
22 + };
23 + t.equal(func.length, 2, 'original function length is 2');
24 + t.deepEqual(func(), [undefined, undefined, undefined], 'unbound func with too few args');
25 + t.deepEqual(func(1, 2), [undefined, 1, 2], 'unbound func with right args');
26 + t.deepEqual(func(1, 2, 3), [undefined, 1, 2], 'unbound func with too many args');
27 +
28 + var bound = callBind(func);
29 + t.equal(bound.length, func.length + 1, 'function length is preserved', { skip: !functionsHaveConfigurableLengths });
30 + t.deepEqual(bound(), [undefined, undefined, undefined], 'bound func with too few args');
31 + t.deepEqual(bound(1, 2), [1, 2, undefined], 'bound func with right args');
32 + t.deepEqual(bound(1, 2, 3), [1, 2, 3], 'bound func with too many args');
33 +
34 + var boundR = callBind(func, sentinel);
35 + t.equal(boundR.length, func.length, 'function length is preserved', { skip: !functionsHaveConfigurableLengths });
36 + t.deepEqual(boundR(), [sentinel, undefined, undefined], 'bound func with receiver, with too few args');
37 + t.deepEqual(boundR(1, 2), [sentinel, 1, 2], 'bound func with receiver, with right args');
38 + t.deepEqual(boundR(1, 2, 3), [sentinel, 1, 2], 'bound func with receiver, with too many args');
39 +
40 + var boundArg = callBind(func, sentinel, 1);
41 + t.equal(boundArg.length, func.length - 1, 'function length is preserved', { skip: !functionsHaveConfigurableLengths });
42 + t.deepEqual(boundArg(), [sentinel, 1, undefined], 'bound func with receiver and arg, with too few args');
43 + t.deepEqual(boundArg(2), [sentinel, 1, 2], 'bound func with receiver and arg, with right arg');
44 + t.deepEqual(boundArg(2, 3), [sentinel, 1, 2], 'bound func with receiver and arg, with too many args');
45 +
46 + t.test('callBind.apply', function (st) {
47 + var aBound = callBind.apply(func);
48 + st.deepEqual(aBound(sentinel), [sentinel, undefined, undefined], 'apply-bound func with no args');
49 + st.deepEqual(aBound(sentinel, [1], 4), [sentinel, 1, undefined], 'apply-bound func with too few args');
50 + st.deepEqual(aBound(sentinel, [1, 2], 4), [sentinel, 1, 2], 'apply-bound func with right args');
51 +
52 + var aBoundArg = callBind.apply(func);
53 + st.deepEqual(aBoundArg(sentinel, [1, 2, 3], 4), [sentinel, 1, 2], 'apply-bound func with too many args');
54 + st.deepEqual(aBoundArg(sentinel, [1, 2], 4), [sentinel, 1, 2], 'apply-bound func with right args');
55 + st.deepEqual(aBoundArg(sentinel, [1], 4), [sentinel, 1, undefined], 'apply-bound func with too few args');
56 +
57 + var aBoundR = callBind.apply(func, sentinel);
58 + st.deepEqual(aBoundR([1, 2, 3], 4), [sentinel, 1, 2], 'apply-bound func with receiver and too many args');
59 + st.deepEqual(aBoundR([1, 2], 4), [sentinel, 1, 2], 'apply-bound func with receiver and right args');
60 + st.deepEqual(aBoundR([1], 4), [sentinel, 1, undefined], 'apply-bound func with receiver and too few args');
61 +
62 + st.end();
63 + });
64 +
65 + t.end();
66 +});
1 +{
2 + "rules": {
3 + "eol-last": ["error", "always"],
4 + "quotes": ["error", "single"],
5 + "semi": ["error", "always"]
6 + },
7 + "overrides": [
8 + {
9 + "files": ["test/**.js"],
10 + "parserOptions": {
11 + "ecmaVersion": 6
12 + }
13 + }
14 + ]
15 +}
1 +language: node_js
2 +node_js:
3 + - '10'
4 + - '12'
5 + - '14'
6 + - '15'
1 +The MIT License (MIT)
2 +
3 +Copyright (c) 2014-2016 Harri Siirak
4 +
5 +Permission is hereby granted, free of charge, to any person obtaining a copy
6 +of this software and associated documentation files (the "Software"), to deal
7 +in the Software without restriction, including without limitation the rights
8 +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 +copies of the Software, and to permit persons to whom the Software is
10 +furnished to do so, subject to the following conditions:
11 +
12 +The above copyright notice and this permission notice shall be included in all
13 +copies or substantial portions of the Software.
14 +
15 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 +SOFTWARE.
1 +cron-parser
2 +================
3 +
4 +[![Build Status](https://travis-ci.com/harrisiirak/cron-parser.svg?branch=master)](https://travis-ci.com/harrisiirak/cron-parser)
5 +[![NPM version](https://badge.fury.io/js/cron-parser.png)](http://badge.fury.io/js/cron-parser)
6 +
7 +Node.js library for parsing and manipulating crontab instructions. It includes support for timezones and DST transitions.
8 +
9 +__Compatibility__
10 +Node >= 0.8
11 +Typescript <= 4.2
12 +
13 +Setup
14 +========
15 +```bash
16 +npm install cron-parser
17 +```
18 +
19 +Supported format
20 +========
21 +
22 +```
23 +* * * * * *
24 +┬ ┬ ┬ ┬ ┬ ┬
25 +│ │ │ │ │ |
26 +│ │ │ │ │ └ day of week (0 - 7) (0 or 7 is Sun)
27 +│ │ │ │ └───── month (1 - 12)
28 +│ │ │ └────────── day of month (1 - 31, L)
29 +│ │ └─────────────── hour (0 - 23)
30 +│ └──────────────────── minute (0 - 59)
31 +└───────────────────────── second (0 - 59, optional)
32 +```
33 +
34 +Supports mixed use of ranges and range increments (L and W characters are not supported currently). See tests for examples.
35 +
36 +Usage
37 +========
38 +
39 +Simple expression.
40 +
41 +```javascript
42 +var parser = require('cron-parser');
43 +
44 +try {
45 + var interval = parser.parseExpression('*/2 * * * *');
46 +
47 + console.log('Date: ', interval.next().toString()); // Sat Dec 29 2012 00:42:00 GMT+0200 (EET)
48 + console.log('Date: ', interval.next().toString()); // Sat Dec 29 2012 00:44:00 GMT+0200 (EET)
49 +
50 + console.log('Date: ', interval.prev().toString()); // Sat Dec 29 2012 00:42:00 GMT+0200 (EET)
51 + console.log('Date: ', interval.prev().toString()); // Sat Dec 29 2012 00:40:00 GMT+0200 (EET)
52 +} catch (err) {
53 + console.log('Error: ' + err.message);
54 +}
55 +
56 +```
57 +
58 +Iteration with limited timespan. Also returns ES6 compatible iterator (when iterator flag is set to true).
59 +
60 +```javascript
61 +var parser = require('cron-parser');
62 +
63 +var options = {
64 + currentDate: new Date('Wed, 26 Dec 2012 12:38:53 UTC'),
65 + endDate: new Date('Wed, 26 Dec 2012 14:40:00 UTC'),
66 + iterator: true
67 +};
68 +
69 +try {
70 + var interval = parser.parseExpression('*/22 * * * *', options);
71 +
72 + while (true) {
73 + try {
74 + var obj = interval.next();
75 + console.log('value:', obj.value.toString(), 'done:', obj.done);
76 + } catch (e) {
77 + break;
78 + }
79 + }
80 +
81 + // value: Wed Dec 26 2012 14:44:00 GMT+0200 (EET) done: false
82 + // value: Wed Dec 26 2012 15:00:00 GMT+0200 (EET) done: false
83 + // value: Wed Dec 26 2012 15:22:00 GMT+0200 (EET) done: false
84 + // value: Wed Dec 26 2012 15:44:00 GMT+0200 (EET) done: false
85 + // value: Wed Dec 26 2012 16:00:00 GMT+0200 (EET) done: false
86 + // value: Wed Dec 26 2012 16:22:00 GMT+0200 (EET) done: true
87 +} catch (err) {
88 + console.log('Error: ' + err.message);
89 +}
90 +
91 +```
92 +
93 +Timezone support
94 +
95 +```javascript
96 +var parser = require('cron-parser');
97 +
98 +var options = {
99 + currentDate: '2016-03-27 00:00:01',
100 + tz: 'Europe/Athens'
101 +};
102 +
103 +try {
104 + var interval = parser.parseExpression('0 * * * *', options);
105 +
106 + console.log('Date: ', interval.next().toString()); // Date: Sun Mar 27 2016 01:00:00 GMT+0200
107 + console.log('Date: ', interval.next().toString()); // Date: Sun Mar 27 2016 02:00:00 GMT+0200
108 + console.log('Date: ', interval.next().toString()); // Date: Sun Mar 27 2016 04:00:00 GMT+0300 (Notice DST transition)
109 +} catch (err) {
110 + console.log('Error: ' + err.message);
111 +}
112 +```
113 +
114 +Manipulation
115 +
116 +```javascript
117 +var parser = require('cron-parser');
118 +
119 +var interval = parser.parseExpression('0 7 * * 0-4');
120 +var fields = JSON.parse(JSON.stringify(interval.fields)); // Fields is immutable
121 +fields.hour = [8];
122 +fields.minute = [29];
123 +fields.dayOfWeek = [1,3,4,5,6,7];
124 +var modifiedInterval = parser.fieldsToExpression(fields);
125 +var cronString = modifiedInterval.stringify();
126 +console.log(cronString); // "29 8 * * 1,3-7"
127 +```
128 +
129 +Options
130 +========
131 +
132 +* *currentDate* - Start date of the iteration
133 +* *endDate* - End date of the iteration
134 +
135 +`currentDate` and `endDate` accept `string`, `integer` and `Date` as input.
136 +
137 +In case of using `string` as input, not every string format accepted
138 +by the `Date` constructor will work correctly. The supported formats are: [`ISO8601`](http://momentjs.com/docs/#/parsing/string/) and the older
139 +[`ASP.NET JSON Date`](http://momentjs.com/docs/#/parsing/asp-net-json-date/) format. The reason being that those are the formats accepted by the
140 +[`moment`](http://momentjs.com) library which is being used to handle dates.
141 +
142 +Using `Date` as an input can be problematic specially when using the `tz` option. The issue being that, when creating a new `Date` object without
143 +any timezone information, it will be created in the timezone of the system that is running the code. This (most of times) won't be what the user
144 +will be expecting. Using one of the supported `string` formats will solve the issue(see timezone example).
145 +
146 +* *iterator* - Return ES6 compatible iterator object
147 +* *utc* - Enable UTC
148 +* *tz* - Timezone string. It won't be used in case `utc` is enabled
1 +{
2 + "name": "cron-parser",
3 + "repo": "harrisiirak/cron-parser",
4 + "description": "Node.js library for parsing crontab instructions",
5 + "version": "2.13.0",
6 + "keywords": ["cron", "crontab", "parser"],
7 + "dependencies": {},
8 + "development": {},
9 + "main": "lib/parser.js",
10 + "scripts": ["lib/parser.js", "lib/expression.js", "lib/date.js", "lib/field_compactor.js", "lib/field_stringify.js"]
11 +}
1 +export * from './types'
1 +'use strict';
2 +
3 +var luxon = require('luxon');
4 +
5 +CronDate.prototype.addYear = function() {
6 + this._date = this._date.plus({ years: 1 });
7 +};
8 +
9 +CronDate.prototype.addMonth = function() {
10 + this._date = this._date.plus({ months: 1 }).startOf('month');
11 +};
12 +
13 +CronDate.prototype.addDay = function() {
14 + this._date = this._date.plus({ days: 1 }).startOf('day');
15 +};
16 +
17 +CronDate.prototype.addHour = function() {
18 + var prev = this._date;
19 + this._date = this._date.plus({ hours: 1 }).startOf('hour');
20 + if (this._date <= prev) {
21 + this._date = this._date.plus({ hours: 1 });
22 + }
23 +};
24 +
25 +CronDate.prototype.addMinute = function() {
26 + var prev = this._date;
27 + this._date = this._date.plus({ minutes: 1 }).startOf('minute');
28 + if (this._date < prev) {
29 + this._date = this._date.plus({ hours: 1 });
30 + }
31 +};
32 +
33 +CronDate.prototype.addSecond = function() {
34 + var prev = this._date;
35 + this._date = this._date.plus({ seconds: 1 }).startOf('second');
36 + if (this._date < prev) {
37 + this._date = this._date.plus({ hours: 1 });
38 + }
39 +};
40 +
41 +CronDate.prototype.subtractYear = function() {
42 + this._date = this._date.minus({ years: 1 });
43 +};
44 +
45 +CronDate.prototype.subtractMonth = function() {
46 + this._date = this._date
47 + .minus({ months: 1 })
48 + .endOf('month')
49 + .startOf('second');
50 +};
51 +
52 +CronDate.prototype.subtractDay = function() {
53 + this._date = this._date
54 + .minus({ days: 1 })
55 + .endOf('day')
56 + .startOf('second');
57 +};
58 +
59 +CronDate.prototype.subtractHour = function() {
60 + var prev = this._date;
61 + this._date = this._date
62 + .minus({ hours: 1 })
63 + .endOf('hour')
64 + .startOf('second');
65 + if (this._date >= prev) {
66 + this._date = this._date.minus({ hours: 1 });
67 + }
68 +};
69 +
70 +CronDate.prototype.subtractMinute = function() {
71 + var prev = this._date;
72 + this._date = this._date.minus({ minutes: 1 })
73 + .endOf('minute')
74 + .startOf('second');
75 + if (this._date > prev) {
76 + this._date = this._date.minus({ hours: 1 });
77 + }
78 +};
79 +
80 +CronDate.prototype.subtractSecond = function() {
81 + var prev = this._date;
82 + this._date = this._date
83 + .minus({ seconds: 1 })
84 + .startOf('second');
85 + if (this._date > prev) {
86 + this._date = this._date.minus({ hours: 1 });
87 + }
88 +};
89 +
90 +CronDate.prototype.getDate = function() {
91 + return this._date.day;
92 +};
93 +
94 +CronDate.prototype.getFullYear = function() {
95 + return this._date.year;
96 +};
97 +
98 +CronDate.prototype.getDay = function() {
99 + var weekday = this._date.weekday;
100 + return weekday == 7 ? 0 : weekday;
101 +};
102 +
103 +CronDate.prototype.getMonth = function() {
104 + return this._date.month - 1;
105 +};
106 +
107 +CronDate.prototype.getHours = function() {
108 + return this._date.hour;
109 +};
110 +
111 +CronDate.prototype.getMinutes = function() {
112 + return this._date.minute;
113 +};
114 +
115 +CronDate.prototype.getSeconds = function() {
116 + return this._date.second;
117 +};
118 +
119 +CronDate.prototype.getMilliseconds = function() {
120 + return this._date.millisecond;
121 +};
122 +
123 +CronDate.prototype.getTime = function() {
124 + return this._date.valueOf();
125 +};
126 +
127 +CronDate.prototype.getUTCDate = function() {
128 + return this._getUTC().day;
129 +};
130 +
131 +CronDate.prototype.getUTCFullYear = function() {
132 + return this._getUTC().year;
133 +};
134 +
135 +CronDate.prototype.getUTCDay = function() {
136 + var weekday = this._getUTC().weekday;
137 + return weekday == 7 ? 0 : weekday;
138 +};
139 +
140 +CronDate.prototype.getUTCMonth = function() {
141 + return this._getUTC().month - 1;
142 +};
143 +
144 +CronDate.prototype.getUTCHours = function() {
145 + return this._getUTC().hour;
146 +};
147 +
148 +CronDate.prototype.getUTCMinutes = function() {
149 + return this._getUTC().minute;
150 +};
151 +
152 +CronDate.prototype.getUTCSeconds = function() {
153 + return this._getUTC().second;
154 +};
155 +
156 +CronDate.prototype.toISOString = function() {
157 + return this._date.toUTC().toISO();
158 +};
159 +
160 +CronDate.prototype.toJSON = function() {
161 + return this._date.toJSON();
162 +};
163 +
164 +CronDate.prototype.setDate = function(d) {
165 + this._date = this._date.set({ day: d });
166 +};
167 +
168 +CronDate.prototype.setFullYear = function(y) {
169 + this._date = this._date.set({ year: y });
170 +};
171 +
172 +CronDate.prototype.setDay = function(d) {
173 + this._date = this._date.set({ weekday: d });
174 +};
175 +
176 +CronDate.prototype.setMonth = function(m) {
177 + this._date = this._date.set({ month: m + 1 });
178 +};
179 +
180 +CronDate.prototype.setHours = function(h) {
181 + this._date = this._date.set({ hour: h });
182 +};
183 +
184 +CronDate.prototype.setMinutes = function(m) {
185 + this._date = this._date.set({ minute: m });
186 +};
187 +
188 +CronDate.prototype.setSeconds = function(s) {
189 + this._date = this._date.set({ second: s });
190 +};
191 +
192 +CronDate.prototype.setMilliseconds = function(s) {
193 + this._date = this._date.set({ millisecond: s });
194 +};
195 +
196 +CronDate.prototype._getUTC = function() {
197 + return this._date.toUTC();
198 +};
199 +
200 +CronDate.prototype.toString = function() {
201 + return this.toDate().toString();
202 +};
203 +
204 +CronDate.prototype.toDate = function() {
205 + return this._date.toJSDate();
206 +};
207 +
208 +CronDate.prototype.isLastDayOfMonth = function() {
209 + //next day
210 + var newDate = this._date.plus({ days: 1 }).startOf('day');
211 + return this._date.month !== newDate.month;
212 +};
213 +
214 +function CronDate (timestamp, tz) {
215 + var dateOpts = { zone: tz };
216 + if (!timestamp) {
217 + this._date = luxon.DateTime.local();
218 + } else if (timestamp instanceof CronDate) {
219 + this._date = timestamp._date;
220 + } else if (timestamp instanceof Date) {
221 + this._date = luxon.DateTime.fromJSDate(timestamp, dateOpts);
222 + } else if (typeof timestamp === 'number') {
223 + this._date = luxon.DateTime.fromMillis(timestamp, dateOpts);
224 + } else if (typeof timestamp === 'string') {
225 + this._date = luxon.DateTime.fromISO(timestamp, dateOpts);
226 + this._date.isValid || (this._date = luxon.DateTime.fromRFC2822(timestamp, dateOpts));
227 + this._date.isValid || (this._date = luxon.DateTime.fromSQL(timestamp, dateOpts));
228 + // RFC2822-like format without the required timezone offset (used in tests)
229 + this._date.isValid || (this._date = luxon.DateTime.fromFormat(timestamp, 'EEE, d MMM yyyy HH:mm:ss', dateOpts));
230 + }
231 +
232 + if (!this._date || !this._date.isValid) {
233 + throw new Error('CronDate: unhandled timestamp: ' + JSON.stringify(timestamp));
234 + }
235 +
236 + if (tz && tz !== this._date.zoneName) {
237 + this._date = this._date.setZone(tz);
238 + }
239 +}
240 +
241 +module.exports = CronDate;
1 +'use strict';
2 +
3 +// Load Date class extensions
4 +var CronDate = require('./date');
5 +
6 +// Get Number.isNaN or the polyfill
7 +var safeIsNaN = require('is-nan');
8 +
9 +var stringifyField = require('./field_stringify');
10 +
11 +/**
12 + * Cron iteration loop safety limit
13 + */
14 +var LOOP_LIMIT = 10000;
15 +
16 +/**
17 + * Construct a new expression parser
18 + *
19 + * Options:
20 + * currentDate: iterator start date
21 + * endDate: iterator end date
22 + *
23 + * @constructor
24 + * @private
25 + * @param {Object} fields Expression fields parsed values
26 + * @param {Object} options Parser options
27 + */
28 +function CronExpression (fields, options) {
29 + this._options = options;
30 + this._utc = options.utc || false;
31 + this._tz = this._utc ? 'UTC' : options.tz;
32 + this._currentDate = new CronDate(options.currentDate, this._tz);
33 + this._startDate = options.startDate ? new CronDate(options.startDate, this._tz) : null;
34 + this._endDate = options.endDate ? new CronDate(options.endDate, this._tz) : null;
35 + this._isIterator = options.iterator || false;
36 + this._hasIterated = false;
37 + this._nthDayOfWeek = options.nthDayOfWeek || 0;
38 + this.fields = CronExpression._freezeFields(fields);
39 +}
40 +
41 +/**
42 + * Field mappings
43 + * @type {Array}
44 + */
45 +CronExpression.map = [ 'second', 'minute', 'hour', 'dayOfMonth', 'month', 'dayOfWeek' ];
46 +
47 +/**
48 + * Prefined intervals
49 + * @type {Object}
50 + */
51 +CronExpression.predefined = {
52 + '@yearly': '0 0 1 1 *',
53 + '@monthly': '0 0 1 * *',
54 + '@weekly': '0 0 * * 0',
55 + '@daily': '0 0 * * *',
56 + '@hourly': '0 * * * *'
57 +};
58 +
59 +/**
60 + * Fields constraints
61 + * @type {Array}
62 + */
63 +CronExpression.constraints = [
64 + { min: 0, max: 59, chars: [] }, // Second
65 + { min: 0, max: 59, chars: [] }, // Minute
66 + { min: 0, max: 23, chars: [] }, // Hour
67 + { min: 1, max: 31, chars: ['L'] }, // Day of month
68 + { min: 1, max: 12, chars: [] }, // Month
69 + { min: 0, max: 7, chars: [] }, // Day of week
70 +];
71 +
72 +/**
73 + * Days in month
74 + * @type {number[]}
75 + */
76 +CronExpression.daysInMonth = [
77 + 31,
78 + 29,
79 + 31,
80 + 30,
81 + 31,
82 + 30,
83 + 31,
84 + 31,
85 + 30,
86 + 31,
87 + 30,
88 + 31
89 +];
90 +
91 +/**
92 + * Field aliases
93 + * @type {Object}
94 + */
95 +CronExpression.aliases = {
96 + month: {
97 + jan: 1,
98 + feb: 2,
99 + mar: 3,
100 + apr: 4,
101 + may: 5,
102 + jun: 6,
103 + jul: 7,
104 + aug: 8,
105 + sep: 9,
106 + oct: 10,
107 + nov: 11,
108 + dec: 12
109 + },
110 +
111 + dayOfWeek: {
112 + sun: 0,
113 + mon: 1,
114 + tue: 2,
115 + wed: 3,
116 + thu: 4,
117 + fri: 5,
118 + sat: 6
119 + }
120 +};
121 +
122 +/**
123 + * Field defaults
124 + * @type {Array}
125 + */
126 +CronExpression.parseDefaults = [ '0', '*', '*', '*', '*', '*' ];
127 +
128 +CronExpression.standardValidCharacters = /^[\d|/|*|\-|,]+$/;
129 +CronExpression.dayOfWeekValidCharacters = /^[\d|/|*|\-|,|\?]+$/;
130 +CronExpression.dayOfMonthValidCharacters = /^[\d|L|/|*|\-|,|\?]+$/;
131 +CronExpression.validCharacters = {
132 + second: CronExpression.standardValidCharacters,
133 + minute: CronExpression.standardValidCharacters,
134 + hour: CronExpression.standardValidCharacters,
135 + dayOfMonth: CronExpression.dayOfMonthValidCharacters,
136 + month: CronExpression.standardValidCharacters,
137 + dayOfWeek: CronExpression.dayOfWeekValidCharacters,
138 +};
139 +
140 +/**
141 + * Parse input interval
142 + *
143 + * @param {String} field Field symbolic name
144 + * @param {String} value Field value
145 + * @param {Array} constraints Range upper and lower constraints
146 + * @return {Array} Sequence of sorted values
147 + * @private
148 + */
149 +CronExpression._parseField = function _parseField (field, value, constraints) {
150 + // Replace aliases
151 + switch (field) {
152 + case 'month':
153 + case 'dayOfWeek':
154 + var aliases = CronExpression.aliases[field];
155 +
156 + value = value.replace(/[a-z]{1,3}/gi, function(match) {
157 + match = match.toLowerCase();
158 +
159 + if (typeof aliases[match] !== 'undefined') {
160 + return aliases[match];
161 + } else {
162 + throw new Error('Validation error, cannot resolve alias "' + match + '"');
163 + }
164 + });
165 + break;
166 + }
167 +
168 + // Check for valid characters.
169 + if (!(CronExpression.validCharacters[field].test(value))) {
170 + throw new Error('Invalid characters, got value: ' + value);
171 + }
172 +
173 + // Replace '*' and '?'
174 + if (value.indexOf('*') !== -1) {
175 + value = value.replace(/\*/g, constraints.min + '-' + constraints.max);
176 + } else if (value.indexOf('?') !== -1) {
177 + value = value.replace(/\?/g, constraints.min + '-' + constraints.max);
178 + }
179 +
180 + //
181 + // Inline parsing functions
182 + //
183 + // Parser path:
184 + // - parseSequence
185 + // - parseRepeat
186 + // - parseRange
187 +
188 + /**
189 + * Parse sequence
190 + *
191 + * @param {String} val
192 + * @return {Array}
193 + * @private
194 + */
195 + function parseSequence (val) {
196 + var stack = [];
197 +
198 + function handleResult (result) {
199 + if (result instanceof Array) { // Make sequence linear
200 + for (var i = 0, c = result.length; i < c; i++) {
201 + var value = result[i];
202 +
203 + if (typeof value === 'string' && constraints.chars.indexOf(value) > -1) {
204 + stack.push(value);
205 + continue;
206 + }
207 + // Check constraints
208 + if (typeof value !== 'number' || safeIsNaN(value) || value < constraints.min || value > constraints.max) {
209 + throw new Error(
210 + 'Constraint error, got value ' + value + ' expected range ' +
211 + constraints.min + '-' + constraints.max
212 + );
213 + }
214 +
215 + stack.push(value);
216 + }
217 + } else { // Scalar value
218 +
219 + if (typeof result === 'string' && constraints.chars.indexOf(result) > -1) {
220 + stack.push(result);
221 + return;
222 + }
223 +
224 + var numResult = +result;
225 +
226 + // Check constraints
227 + if (safeIsNaN(numResult) || numResult < constraints.min || numResult > constraints.max) {
228 + throw new Error(
229 + 'Constraint error, got value ' + result + ' expected range ' +
230 + constraints.min + '-' + constraints.max
231 + );
232 + }
233 +
234 + if (field === 'dayOfWeek') {
235 + numResult = numResult % 7;
236 + }
237 +
238 + stack.push(numResult);
239 + }
240 + }
241 +
242 + var atoms = val.split(',');
243 + if (!atoms.every(function (atom) {
244 + return atom.length > 0;
245 + })) {
246 + throw new Error('Invalid list value format');
247 + }
248 +
249 + if (atoms.length > 1) {
250 + for (var i = 0, c = atoms.length; i < c; i++) {
251 + handleResult(parseRepeat(atoms[i]));
252 + }
253 + } else {
254 + handleResult(parseRepeat(val));
255 + }
256 +
257 + stack.sort(CronExpression._sortCompareFn);
258 +
259 + return stack;
260 + }
261 +
262 + /**
263 + * Parse repetition interval
264 + *
265 + * @param {String} val
266 + * @return {Array}
267 + */
268 + function parseRepeat (val) {
269 + var repeatInterval = 1;
270 + var atoms = val.split('/');
271 +
272 + if (atoms.length > 1) {
273 + if (atoms[0] == +atoms[0]) {
274 + atoms = [atoms[0] + '-' + constraints.max, atoms[1]];
275 + }
276 + return parseRange(atoms[0], atoms[atoms.length - 1]);
277 + }
278 +
279 + return parseRange(val, repeatInterval);
280 + }
281 +
282 + /**
283 + * Parse range
284 + *
285 + * @param {String} val
286 + * @param {Number} repeatInterval Repetition interval
287 + * @return {Array}
288 + * @private
289 + */
290 + function parseRange (val, repeatInterval) {
291 + var stack = [];
292 + var atoms = val.split('-');
293 +
294 + if (atoms.length > 1 ) {
295 + // Invalid range, return value
296 + if (atoms.length < 2) {
297 + return +val;
298 + }
299 +
300 + if (!atoms[0].length) {
301 + if (!atoms[1].length) {
302 + throw new Error('Invalid range: ' + val);
303 + }
304 +
305 + return +val;
306 + }
307 +
308 + // Validate range
309 + var min = +atoms[0];
310 + var max = +atoms[1];
311 +
312 + if (safeIsNaN(min) || safeIsNaN(max) ||
313 + min < constraints.min || max > constraints.max) {
314 + throw new Error(
315 + 'Constraint error, got range ' +
316 + min + '-' + max +
317 + ' expected range ' +
318 + constraints.min + '-' + constraints.max
319 + );
320 + } else if (min >= max) {
321 + throw new Error('Invalid range: ' + val);
322 + }
323 +
324 + // Create range
325 + var repeatIndex = +repeatInterval;
326 +
327 + if (safeIsNaN(repeatIndex) || repeatIndex <= 0) {
328 + throw new Error('Constraint error, cannot repeat at every ' + repeatIndex + ' time.');
329 + }
330 +
331 + for (var index = min, count = max; index <= count; index++) {
332 + if (repeatIndex > 0 && (repeatIndex % repeatInterval) === 0) {
333 + repeatIndex = 1;
334 + stack.push(index);
335 + } else {
336 + repeatIndex++;
337 + }
338 + }
339 +
340 + return stack;
341 + }
342 +
343 + return safeIsNaN(+val) ? val : +val;
344 + }
345 +
346 + return parseSequence(value);
347 +};
348 +
349 +CronExpression._sortCompareFn = function(a, b) {
350 + var aIsNumber = typeof a === 'number';
351 + var bIsNumber = typeof b === 'number';
352 +
353 + if (aIsNumber && bIsNumber) {
354 + return a - b;
355 + }
356 +
357 + if (!aIsNumber && bIsNumber) {
358 + return 1;
359 + }
360 +
361 + if (aIsNumber && !bIsNumber) {
362 + return -1;
363 + }
364 +
365 + return a.localeCompare(b);
366 +};
367 +
368 +CronExpression._handleMaxDaysInMonth = function(mappedFields) {
369 + // Filter out any day of month value that is larger than given month expects
370 + if (mappedFields.month.length === 1) {
371 + var daysInMonth = CronExpression.daysInMonth[mappedFields.month[0] - 1];
372 +
373 + if (mappedFields.dayOfMonth[0] > daysInMonth) {
374 + throw new Error('Invalid explicit day of month definition');
375 + }
376 +
377 + return mappedFields.dayOfMonth
378 + .filter(function(dayOfMonth) {
379 + return dayOfMonth === 'L' ? true : dayOfMonth <= daysInMonth;
380 + })
381 + .sort(CronExpression._sortCompareFn);
382 + }
383 +};
384 +
385 +CronExpression._freezeFields = function(fields) {
386 + for (var i = 0, c = CronExpression.map.length; i < c; ++i) {
387 + var field = CronExpression.map[i]; // Field name
388 + var value = fields[field];
389 + fields[field] = Object.freeze(value);
390 + }
391 + return Object.freeze(fields);
392 +};
393 +
394 +CronExpression.prototype._applyTimezoneShift = function(currentDate, dateMathVerb, method) {
395 + if ((method === 'Month') || (method === 'Day')) {
396 + var prevTime = currentDate.getTime();
397 + currentDate[dateMathVerb + method]();
398 + var currTime = currentDate.getTime();
399 + if (prevTime === currTime) {
400 + // Jumped into a not existent date due to a DST transition
401 + if ((currentDate.getMinutes() === 0) &&
402 + (currentDate.getSeconds() === 0)) {
403 + currentDate.addHour();
404 + } else if ((currentDate.getMinutes() === 59) &&
405 + (currentDate.getSeconds() === 59)) {
406 + currentDate.subtractHour();
407 + }
408 + }
409 + } else {
410 + var previousHour = currentDate.getHours();
411 + currentDate[dateMathVerb + method]();
412 + var currentHour = currentDate.getHours();
413 + var diff = currentHour - previousHour;
414 + if (diff === 2) {
415 + // Starting DST
416 + if (this.fields.hour.length !== 24) {
417 + // Hour is specified
418 + this._dstStart = currentHour;
419 + }
420 + } else if ((diff === 0) &&
421 + (currentDate.getMinutes() === 0) &&
422 + (currentDate.getSeconds() === 0)) {
423 + // Ending DST
424 + if (this.fields.hour.length !== 24) {
425 + // Hour is specified
426 + this._dstEnd = currentHour;
427 + }
428 + }
429 + }
430 +};
431 +
432 +
433 +/**
434 + * Find next or previous matching schedule date
435 + *
436 + * @return {CronDate}
437 + * @private
438 + */
439 +CronExpression.prototype._findSchedule = function _findSchedule (reverse) {
440 +
441 + /**
442 + * Match field value
443 + *
444 + * @param {String} value
445 + * @param {Array} sequence
446 + * @return {Boolean}
447 + * @private
448 + */
449 + function matchSchedule (value, sequence) {
450 + for (var i = 0, c = sequence.length; i < c; i++) {
451 + if (sequence[i] >= value) {
452 + return sequence[i] === value;
453 + }
454 + }
455 +
456 + return sequence[0] === value;
457 + }
458 +
459 + /**
460 + * Helps determine if the provided date is the correct nth occurence of the
461 + * desired day of week.
462 + *
463 + * @param {CronDate} date
464 + * @param {Number} nthDayOfWeek
465 + * @return {Boolean}
466 + * @private
467 + */
468 + function isNthDayMatch(date, nthDayOfWeek) {
469 + if (nthDayOfWeek < 6) {
470 + if (
471 + date.getDate() < 8 &&
472 + nthDayOfWeek === 1 // First occurence has to happen in first 7 days of the month
473 + ) {
474 + return true;
475 + }
476 +
477 + var offset = date.getDate() % 7 ? 1 : 0; // Math is off by 1 when dayOfWeek isn't divisible by 7
478 + var adjustedDate = date.getDate() - (date.getDate() % 7); // find the first occurance
479 + var occurrence = Math.floor(adjustedDate / 7) + offset;
480 +
481 + return occurrence === nthDayOfWeek;
482 + }
483 +
484 + return false;
485 + }
486 +
487 + /**
488 + * Helper function that checks if 'L' is in the array
489 + *
490 + * @param {Array} dayOfMonth
491 + */
492 + function isLInDayOfMonth(dayOfMonth) {
493 + return dayOfMonth.length > 0 && dayOfMonth.indexOf('L') >= 0;
494 + }
495 +
496 + // Whether to use backwards directionality when searching
497 + reverse = reverse || false;
498 + var dateMathVerb = reverse ? 'subtract' : 'add';
499 +
500 + var currentDate = new CronDate(this._currentDate, this._tz);
501 + var startDate = this._startDate;
502 + var endDate = this._endDate;
503 +
504 + // Find matching schedule
505 + var startTimestamp = currentDate.getTime();
506 + var stepCount = 0;
507 +
508 + while (stepCount < LOOP_LIMIT) {
509 + stepCount++;
510 +
511 + // Validate timespan
512 + if (reverse) {
513 + if (startDate && (currentDate.getTime() - startDate.getTime() < 0)) {
514 + throw new Error('Out of the timespan range');
515 + }
516 + } else {
517 + if (endDate && (endDate.getTime() - currentDate.getTime()) < 0) {
518 + throw new Error('Out of the timespan range');
519 + }
520 + }
521 +
522 + // Day of month and week matching:
523 + //
524 + // "The day of a command's execution can be specified by two fields --
525 + // day of month, and day of week. If both fields are restricted (ie,
526 + // aren't *), the command will be run when either field matches the cur-
527 + // rent time. For example, "30 4 1,15 * 5" would cause a command to be
528 + // run at 4:30 am on the 1st and 15th of each month, plus every Friday."
529 + //
530 + // http://unixhelp.ed.ac.uk/CGI/man-cgi?crontab+5
531 + //
532 +
533 + var dayOfMonthMatch = matchSchedule(currentDate.getDate(), this.fields.dayOfMonth);
534 + if (isLInDayOfMonth(this.fields.dayOfMonth)) {
535 + dayOfMonthMatch = dayOfMonthMatch || currentDate.isLastDayOfMonth();
536 + }
537 + var dayOfWeekMatch = matchSchedule(currentDate.getDay(), this.fields.dayOfWeek);
538 + var isDayOfMonthWildcardMatch = this.fields.dayOfMonth.length >= CronExpression.daysInMonth[currentDate.getMonth()];
539 + var isDayOfWeekWildcardMatch = this.fields.dayOfWeek.length === CronExpression.constraints[5].max - CronExpression.constraints[5].min + 1;
540 + var currentHour = currentDate.getHours();
541 +
542 + // Add or subtract day if select day not match with month (according to calendar)
543 + if (!dayOfMonthMatch && !dayOfWeekMatch) {
544 + this._applyTimezoneShift(currentDate, dateMathVerb, 'Day');
545 + continue;
546 + }
547 +
548 + // Add or subtract day if not day of month is set (and no match) and day of week is wildcard
549 + if (!isDayOfMonthWildcardMatch && isDayOfWeekWildcardMatch && !dayOfMonthMatch) {
550 + this._applyTimezoneShift(currentDate, dateMathVerb, 'Day');
551 + continue;
552 + }
553 +
554 + // Add or subtract day if not day of week is set (and no match) and day of month is wildcard
555 + if (isDayOfMonthWildcardMatch && !isDayOfWeekWildcardMatch && !dayOfWeekMatch) {
556 + this._applyTimezoneShift(currentDate, dateMathVerb, 'Day');
557 + continue;
558 + }
559 +
560 + // Add or subtract day if day of week & nthDayOfWeek are set (and no match)
561 + if (
562 + this._nthDayOfWeek > 0 &&
563 + !isNthDayMatch(currentDate, this._nthDayOfWeek)
564 + ) {
565 + this._applyTimezoneShift(currentDate, dateMathVerb, 'Day');
566 + continue;
567 + }
568 +
569 + // Match month
570 + if (!matchSchedule(currentDate.getMonth() + 1, this.fields.month)) {
571 + this._applyTimezoneShift(currentDate, dateMathVerb, 'Month');
572 + continue;
573 + }
574 +
575 + // Match hour
576 + if (!matchSchedule(currentHour, this.fields.hour)) {
577 + if (this._dstStart !== currentHour) {
578 + this._dstStart = null;
579 + this._applyTimezoneShift(currentDate, dateMathVerb, 'Hour');
580 + continue;
581 + } else if (!matchSchedule(currentHour - 1, this.fields.hour)) {
582 + currentDate[dateMathVerb + 'Hour']();
583 + continue;
584 + }
585 + } else if (this._dstEnd === currentHour) {
586 + if (!reverse) {
587 + this._dstEnd = null;
588 + this._applyTimezoneShift(currentDate, 'add', 'Hour');
589 + continue;
590 + }
591 + }
592 +
593 + // Match minute
594 + if (!matchSchedule(currentDate.getMinutes(), this.fields.minute)) {
595 + this._applyTimezoneShift(currentDate, dateMathVerb, 'Minute');
596 + continue;
597 + }
598 +
599 + // Match second
600 + if (!matchSchedule(currentDate.getSeconds(), this.fields.second)) {
601 + this._applyTimezoneShift(currentDate, dateMathVerb, 'Second');
602 + continue;
603 + }
604 +
605 + // Increase a second in case in the first iteration the currentDate was not
606 + // modified
607 + if (startTimestamp === currentDate.getTime()) {
608 + if ((dateMathVerb === 'add') || (currentDate.getMilliseconds() === 0)) {
609 + this._applyTimezoneShift(currentDate, dateMathVerb, 'Second');
610 + } else {
611 + currentDate.setMilliseconds(0);
612 + }
613 +
614 + continue;
615 + }
616 +
617 + break;
618 + }
619 +
620 + if (stepCount >= LOOP_LIMIT) {
621 + throw new Error('Invalid expression, loop limit exceeded');
622 + }
623 +
624 + this._currentDate = new CronDate(currentDate, this._tz);
625 + this._hasIterated = true;
626 +
627 + return currentDate;
628 +};
629 +
630 +/**
631 + * Find next suitable date
632 + *
633 + * @public
634 + * @return {CronDate|Object}
635 + */
636 +CronExpression.prototype.next = function next () {
637 + var schedule = this._findSchedule();
638 +
639 + // Try to return ES6 compatible iterator
640 + if (this._isIterator) {
641 + return {
642 + value: schedule,
643 + done: !this.hasNext()
644 + };
645 + }
646 +
647 + return schedule;
648 +};
649 +
650 +/**
651 + * Find previous suitable date
652 + *
653 + * @public
654 + * @return {CronDate|Object}
655 + */
656 +CronExpression.prototype.prev = function prev () {
657 + var schedule = this._findSchedule(true);
658 +
659 + // Try to return ES6 compatible iterator
660 + if (this._isIterator) {
661 + return {
662 + value: schedule,
663 + done: !this.hasPrev()
664 + };
665 + }
666 +
667 + return schedule;
668 +};
669 +
670 +/**
671 + * Check if next suitable date exists
672 + *
673 + * @public
674 + * @return {Boolean}
675 + */
676 +CronExpression.prototype.hasNext = function() {
677 + var current = this._currentDate;
678 + var hasIterated = this._hasIterated;
679 +
680 + try {
681 + this._findSchedule();
682 + return true;
683 + } catch (err) {
684 + return false;
685 + } finally {
686 + this._currentDate = current;
687 + this._hasIterated = hasIterated;
688 + }
689 +};
690 +
691 +/**
692 + * Check if previous suitable date exists
693 + *
694 + * @public
695 + * @return {Boolean}
696 + */
697 +CronExpression.prototype.hasPrev = function() {
698 + var current = this._currentDate;
699 + var hasIterated = this._hasIterated;
700 +
701 + try {
702 + this._findSchedule(true);
703 + return true;
704 + } catch (err) {
705 + return false;
706 + } finally {
707 + this._currentDate = current;
708 + this._hasIterated = hasIterated;
709 + }
710 +};
711 +
712 +/**
713 + * Iterate over expression iterator
714 + *
715 + * @public
716 + * @param {Number} steps Numbers of steps to iterate
717 + * @param {Function} callback Optional callback
718 + * @return {Array} Array of the iterated results
719 + */
720 +CronExpression.prototype.iterate = function iterate (steps, callback) {
721 + var dates = [];
722 +
723 + if (steps >= 0) {
724 + for (var i = 0, c = steps; i < c; i++) {
725 + try {
726 + var item = this.next();
727 + dates.push(item);
728 +
729 + // Fire the callback
730 + if (callback) {
731 + callback(item, i);
732 + }
733 + } catch (err) {
734 + break;
735 + }
736 + }
737 + } else {
738 + for (var i = 0, c = steps; i > c; i--) {
739 + try {
740 + var item = this.prev();
741 + dates.push(item);
742 +
743 + // Fire the callback
744 + if (callback) {
745 + callback(item, i);
746 + }
747 + } catch (err) {
748 + break;
749 + }
750 + }
751 + }
752 +
753 + return dates;
754 +};
755 +
756 +/**
757 + * Reset expression iterator state
758 + *
759 + * @public
760 + */
761 +CronExpression.prototype.reset = function reset (newDate) {
762 + this._currentDate = new CronDate(newDate || this._options.currentDate);
763 +};
764 +
765 +/**
766 + * Stringify the expression
767 + *
768 + * @public
769 + * @param {Boolean} [includeSeconds] Should stringify seconds
770 + * @return {String}
771 + */
772 +CronExpression.prototype.stringify = function stringify(includeSeconds) {
773 + var resultArr = [];
774 + for (var i = includeSeconds ? 0 : 1, c = CronExpression.map.length; i < c; ++i) {
775 + var field = CronExpression.map[i];
776 + var value = this.fields[field];
777 + var constraint = CronExpression.constraints[i];
778 + resultArr.push(stringifyField(value, constraint.min, constraint.max));
779 + }
780 + return resultArr.join(' ');
781 +};
782 +
783 +/**
784 + * Parse input expression (async)
785 + *
786 + * @public
787 + * @param {String} expression Input expression
788 + * @param {Object} [options] Parsing options
789 + */
790 +CronExpression.parse = function parse(expression, options) {
791 + var self = this;
792 + if (typeof options === 'function') {
793 + options = {};
794 + }
795 +
796 + function parse (expression, options) {
797 + if (!options) {
798 + options = {};
799 + }
800 +
801 + if (typeof options.currentDate === 'undefined') {
802 + options.currentDate = new CronDate(undefined, self._tz);
803 + }
804 +
805 + // Is input expression predefined?
806 + if (CronExpression.predefined[expression]) {
807 + expression = CronExpression.predefined[expression];
808 + }
809 +
810 + // Split fields
811 + var fields = [];
812 + var atoms = (expression + '').trim().split(/\s+/);
813 +
814 + if (atoms.length > 6) {
815 + throw new Error('Invalid cron expression');
816 + }
817 +
818 + // Resolve fields
819 + var start = (CronExpression.map.length - atoms.length);
820 + for (var i = 0, c = CronExpression.map.length; i < c; ++i) {
821 + var field = CronExpression.map[i]; // Field name
822 + var value = atoms[atoms.length > c ? i : i - start]; // Field value
823 +
824 + if (i < start || !value) { // Use default value
825 + fields.push(CronExpression._parseField(
826 + field,
827 + CronExpression.parseDefaults[i],
828 + CronExpression.constraints[i]
829 + )
830 + );
831 + } else {
832 + var val = field === 'dayOfWeek' ? parseNthDay(value) : value;
833 +
834 + fields.push(CronExpression._parseField(
835 + field,
836 + val,
837 + CronExpression.constraints[i]
838 + )
839 + );
840 + }
841 + }
842 +
843 + var mappedFields = {};
844 + for (var i = 0, c = CronExpression.map.length; i < c; i++) {
845 + var key = CronExpression.map[i];
846 + mappedFields[key] = fields[i];
847 + }
848 +
849 + var dayOfMonth = CronExpression._handleMaxDaysInMonth(mappedFields);
850 + mappedFields.dayOfMonth = dayOfMonth || mappedFields.dayOfMonth;
851 + return new CronExpression(mappedFields, options);
852 +
853 + /**
854 + * Parses out the # special character for the dayOfWeek field & adds it to options.
855 + *
856 + * @param {String} val
857 + * @return {String}
858 + * @private
859 + */
860 + function parseNthDay(val) {
861 + var atoms = val.split('#');
862 + if (atoms.length > 1) {
863 + var nthValue = +atoms[atoms.length - 1];
864 + if(/,/.test(val)) {
865 + throw new Error('Constraint error, invalid dayOfWeek `#` and `,` '
866 + + 'special characters are incompatible');
867 + }
868 + if(/\//.test(val)) {
869 + throw new Error('Constraint error, invalid dayOfWeek `#` and `/` '
870 + + 'special characters are incompatible');
871 + }
872 + if(/-/.test(val)) {
873 + throw new Error('Constraint error, invalid dayOfWeek `#` and `-` '
874 + + 'special characters are incompatible');
875 + }
876 + if (atoms.length > 2 || safeIsNaN(nthValue) || (nthValue < 1 || nthValue > 5)) {
877 + throw new Error('Constraint error, invalid dayOfWeek occurrence number (#)');
878 + }
879 +
880 + options.nthDayOfWeek = nthValue;
881 + return atoms[0];
882 + }
883 + return val;
884 + }
885 + }
886 +
887 + return parse(expression, options);
888 +};
889 +
890 +/**
891 + * Convert cron fields back to Cron Expression
892 + *
893 + * @public
894 + * @param {Object} fields Input fields
895 + * @param {Object} [options] Parsing options
896 + * @return {Object}
897 + */
898 +CronExpression.fieldsToExpression = function fieldsToExpression(fields, options) {
899 + function validateConstraints (field, values, constraints) {
900 + if (!values) {
901 + throw new Error('Validation error, Field ' + field + ' is missing');
902 + }
903 + if (values.length === 0) {
904 + throw new Error('Validation error, Field ' + field + ' contains no values');
905 + }
906 + for (var i = 0, c = values.length; i < c; i++) {
907 + var value = values[i];
908 +
909 + if (typeof value === 'string' && constraints.chars.indexOf(value) > -1) {
910 + continue;
911 + }
912 + // Check constraints
913 + if (typeof value !== 'number' || safeIsNaN(value) || value < constraints.min || value > constraints.max) {
914 + throw new Error(
915 + 'Constraint error, got value ' + value + ' expected range ' +
916 + constraints.min + '-' + constraints.max
917 + );
918 + }
919 + }
920 + }
921 +
922 + var mappedFields = {};
923 + for (var i = 0, c = CronExpression.map.length; i < c; ++i) {
924 + var field = CronExpression.map[i]; // Field name
925 + var values = fields[field];
926 + validateConstraints(
927 + field,
928 + values,
929 + CronExpression.constraints[i]
930 + );
931 + var copy = [];
932 + var j = -1;
933 + while (++j < values.length) {
934 + copy[j] = values[j];
935 + }
936 + values = copy.sort(CronExpression._sortCompareFn)
937 + .filter(function(item, pos, ary) {
938 + return !pos || item !== ary[pos - 1];
939 + });
940 + if (values.length !== copy.length) {
941 + throw new Error('Validation error, Field ' + field + ' contains duplicate values');
942 + }
943 + mappedFields[field] = values;
944 + }
945 + var dayOfMonth = CronExpression._handleMaxDaysInMonth(mappedFields);
946 + mappedFields.dayOfMonth = dayOfMonth || mappedFields.dayOfMonth;
947 + return new CronExpression(mappedFields, options || {});
948 +};
949 +
950 +module.exports = CronExpression;
1 +'use strict';
2 +
3 +function buildRange(item) {
4 + return {
5 + start: item,
6 + count: 1
7 + };
8 +}
9 +
10 +function completeRangeWithItem(range, item) {
11 + range.end = item;
12 + range.step = item - range.start;
13 + range.count = 2;
14 +}
15 +
16 +function finalizeCurrentRange(results, currentRange, currentItemRange) {
17 + if (currentRange) {
18 + // Two elements do not form a range so split them into 2 single elements
19 + if (currentRange.count === 2) {
20 + results.push(buildRange(currentRange.start));
21 + results.push(buildRange(currentRange.end));
22 + } else {
23 + results.push(currentRange);
24 + }
25 + }
26 + if (currentItemRange) {
27 + results.push(currentItemRange);
28 + }
29 +}
30 +
31 +function compactField(arr) {
32 + var results = [];
33 + var currentRange = undefined;
34 +
35 + for (var i = 0; i < arr.length; i++) {
36 + var currentItem = arr[i];
37 + if (typeof currentItem !== 'number') {
38 + // String elements can't form a range
39 + finalizeCurrentRange(results, currentRange, buildRange(currentItem));
40 + currentRange = undefined;
41 + } else if (!currentRange) {
42 + // Start a new range
43 + currentRange = buildRange(currentItem);
44 + } else if (currentRange.count === 1) {
45 + // Guess that the current item starts a range
46 + completeRangeWithItem(currentRange, currentItem);
47 + } else {
48 + if (currentRange.step === currentItem - currentRange.end) {
49 + // We found another item that matches the current range
50 + currentRange.count++;
51 + currentRange.end = currentItem;
52 + } else if (currentRange.count === 2) { // The current range can't be continued
53 + // Break the first item of the current range into a single element, and try to start a new range with the second item
54 + results.push(buildRange(currentRange.start));
55 + currentRange = buildRange(currentRange.end);
56 + completeRangeWithItem(currentRange, currentItem);
57 + } else {
58 + // Persist the current range and start a new one with current item
59 + finalizeCurrentRange(results, currentRange);
60 + currentRange = buildRange(currentItem);
61 + }
62 + }
63 + }
64 +
65 + finalizeCurrentRange(results, currentRange);
66 +
67 + return results;
68 +}
69 +
70 +module.exports = compactField;
1 +'use strict';
2 +
3 +var compactField = require('./field_compactor');
4 +
5 +function stringifyField(arr, min, max) {
6 + var ranges = compactField(arr);
7 + if (ranges.length === 1) {
8 + var singleRange = ranges[0];
9 + var step = singleRange.step;
10 + if (step === 1 && singleRange.start === min && singleRange.end === max) {
11 + return '*';
12 + }
13 + if (step !== 1 && singleRange.start === min && singleRange.end === max - step + 1) {
14 + return '*/' + step;
15 + }
16 + }
17 + var resultArr = [];
18 + for (var i = 0, l = ranges.length; i < l; ++i) {
19 + var range = ranges[i];
20 + if (range.count === 1) {
21 + resultArr.push(range.start);
22 + } else {
23 + var step = range.step;
24 + if (step === 1) {
25 + resultArr.push(range.start + '-' + range.end);
26 + } else {
27 + if (range.end === max - step + 1) {
28 + resultArr.push(range.start + '/' + step);
29 + } else {
30 + resultArr.push(range.start + '-' + range.end + '/' + step);
31 + }
32 + }
33 + }
34 + }
35 + return resultArr.join(',');
36 +}
37 +
38 +module.exports = stringifyField;
1 +'use strict';
2 +
3 +var CronExpression = require('./expression');
4 +
5 +function CronParser() {}
6 +
7 +/**
8 + * Parse crontab entry
9 + *
10 + * @private
11 + * @param {String} entry Crontab file entry/line
12 + */
13 +CronParser._parseEntry = function _parseEntry (entry) {
14 + var atoms = entry.split(' ');
15 +
16 + if (atoms.length === 6) {
17 + return {
18 + interval: CronExpression.parse(entry)
19 + };
20 + } else if (atoms.length > 6) {
21 + return {
22 + interval: CronExpression.parse(
23 + atoms.slice(0, 6).join(' ')
24 + ),
25 + command: atoms.slice(6, atoms.length)
26 + };
27 + } else {
28 + throw new Error('Invalid entry: ' + entry);
29 + }
30 +};
31 +
32 +/**
33 + * Wrapper for CronExpression.parser method
34 + *
35 + * @public
36 + * @param {String} expression Input expression
37 + * @param {Object} [options] Parsing options
38 + * @return {Object}
39 + */
40 +CronParser.parseExpression = function parseExpression (expression, options) {
41 + return CronExpression.parse(expression, options);
42 +};
43 +
44 +/**
45 + * Wrapper for CronExpression.fieldsToExpression method
46 + *
47 + * @public
48 + * @param {Object} fields Input fields
49 + * @param {Object} [options] Parsing options
50 + * @return {Object}
51 + */
52 +CronParser.fieldsToExpression = function fieldsToExpression (fields, options) {
53 + return CronExpression.fieldsToExpression(fields, options);
54 +};
55 +
56 +/**
57 + * Parse content string
58 + *
59 + * @public
60 + * @param {String} data Crontab content
61 + * @return {Object}
62 + */
63 +CronParser.parseString = function parseString (data) {
64 + var blocks = data.split('\n');
65 +
66 + var response = {
67 + variables: {},
68 + expressions: [],
69 + errors: {}
70 + };
71 +
72 + for (var i = 0, c = blocks.length; i < c; i++) {
73 + var block = blocks[i];
74 + var matches = null;
75 + var entry = block.trim(); // Remove surrounding spaces
76 +
77 + if (entry.length > 0) {
78 + if (entry.match(/^#/)) { // Comment
79 + continue;
80 + } else if ((matches = entry.match(/^(.*)=(.*)$/))) { // Variable
81 + response.variables[matches[1]] = matches[2];
82 + } else { // Expression?
83 + var result = null;
84 +
85 + try {
86 + result = CronParser._parseEntry('0 ' + entry);
87 + response.expressions.push(result.interval);
88 + } catch (err) {
89 + response.errors[entry] = err;
90 + }
91 + }
92 + }
93 + }
94 +
95 + return response;
96 +};
97 +
98 +/**
99 + * Parse crontab file
100 + *
101 + * @public
102 + * @param {String} filePath Path to file
103 + * @param {Function} callback
104 + */
105 +CronParser.parseFile = function parseFile (filePath, callback) {
106 + require('fs').readFile(filePath, function(err, data) {
107 + if (err) {
108 + callback(err);
109 + return;
110 + }
111 +
112 + return callback(null, CronParser.parseString(data.toString()));
113 + });
114 +};
115 +
116 +module.exports = CronParser;
1 +{
2 + "name": "cron-parser",
3 + "version": "3.5.0",
4 + "description": "Node.js library for parsing crontab instructions",
5 + "main": "lib/parser.js",
6 + "types": "index.d.ts",
7 + "typesVersions": {
8 + "<4.1": {
9 + "*": [
10 + "types/ts3/*"
11 + ]
12 + }
13 + },
14 + "directories": {
15 + "test": "test"
16 + },
17 + "scripts": {
18 + "test:tsd": "tsd",
19 + "test:unit": "TZ=UTC tap ./test/*.js",
20 + "test:cover": "TZ=UTC tap --coverage-report=html ./test/*.js",
21 + "lint": "eslint .",
22 + "lint:fix": "eslint --fix .",
23 + "test": "npm run lint && npm run test:unit && npm run test:tsd"
24 + },
25 + "repository": {
26 + "type": "git",
27 + "url": "https://github.com/harrisiirak/cron-parser.git"
28 + },
29 + "keywords": [
30 + "cron",
31 + "crontab",
32 + "parser"
33 + ],
34 + "author": "Harri Siirak",
35 + "contributors": [
36 + "Nicholas Clawson",
37 + "Daniel Prentis <daniel@salsitasoft.com>",
38 + "Renault John Lecoultre",
39 + "Richard Astbury <richard.astbury@gmail.com>",
40 + "Meaglin Wasabi <Meaglin.wasabi@gmail.com>",
41 + "Mike Kusold <hello@mikekusold.com>",
42 + "Alex Kit <alex.kit@atmajs.com>",
43 + "Santiago Gimeno <santiago.gimeno@gmail.com>",
44 + "Daniel <darc.tec@gmail.com>",
45 + "Christian Steininger <christian.steininger.cs@gmail.com>",
46 + "Mykola Piskovyi <m.piskovyi@gmail.com>",
47 + "Brian Vaughn <brian.david.vaughn@gmail.com>",
48 + "Nicholas Clawson <nickclaw@gmail.com>",
49 + "Yasuhiroki <yasuhiroki.duck@gmail.com>",
50 + "Nicholas Clawson <nickclaw@gmail.com>",
51 + "Brendan Warkentin <faazshift@gmail.com>",
52 + "Charlie Fish <fishcharlie.code@gmail.com>",
53 + "Ian Graves <ian+diskimage@iangrav.es>",
54 + "Andy Thompson <me@andytson.com>",
55 + "Regev Brody <regevbr@gmail.com>"
56 + ],
57 + "license": "MIT",
58 + "dependencies": {
59 + "is-nan": "^1.3.2",
60 + "luxon": "^1.26.0"
61 + },
62 + "devDependencies": {
63 + "eslint": "^7.24.0",
64 + "sinon": "^10.0.0",
65 + "tap": "^15.0.3",
66 + "tsd": "^0.14.0"
67 + },
68 + "engines": {
69 + "node": ">=0.8"
70 + },
71 + "browser": {
72 + "fs": false
73 + },
74 + "tap": {
75 + "check-coverage": false
76 + },
77 + "tsd": {
78 + "directory": "test",
79 + "compilerOptions": {
80 + "lib": [
81 + "es2017",
82 + "dom"
83 + ]
84 + }
85 + }
86 +}
1 +var util = require('util');
2 +var test = require('tap').test;
3 +var expression = require('../lib/expression');
4 +
5 +test('expression 31 of month', function(t) {
6 + try {
7 + var interval = expression.parse('0 0 31 * *');
8 + var i;
9 + var d;
10 + for (i = 0; i < 20; ++i) {
11 + d = interval.next();
12 + }
13 + t.end();
14 + } catch (err) {
15 + t.error(err, 'Interval parse error');
16 + }
17 +});
1 +var util = require('util');
2 +var test = require('tap').test;
3 +var CronExpression = require('../lib/expression');
4 +var CronDate = require('../lib/date');
5 +
6 +
7 +test('parse expression as UTC', function(t) {
8 + try {
9 + var options = {
10 + utc: true
11 + };
12 +
13 + var interval = CronExpression.parse('0 0 10 * * *', options);
14 +
15 + var date = interval.next();
16 + t.equal(date.getUTCHours(), 10, 'Correct UTC hour value');
17 +
18 + interval = CronExpression.parse('0 */5 * * * *', options);
19 +
20 + var date = interval.next(), now = new Date();
21 + now.setMinutes(now.getMinutes() + 5 - (now.getMinutes() % 5));
22 + t.equal(date.getHours(), now.getUTCHours(), 'Correct local time for 5 minute interval');
23 +
24 + } catch (err) {
25 + t.error(err, 'UTC parse error');
26 + }
27 +
28 + t.end();
29 +});
1 +# Comment line (ignore)
2 +ENV1="test1"
3 +ENV2="test2"
4 +
5 +*/10 * * * * /path/to/exe
6 +*/10 * * * * /path/to/exe
7 +0 09-18 * * 1-5 /path/to/exe
...\ No newline at end of file ...\ No newline at end of file
1 +// empty around comma
2 +
3 +var test = require('tap').test;
4 +var CronExpression = require('../lib/expression');
5 +
6 +const options = {
7 + utc: true
8 +};
9 +
10 +test('both empty around comma', function (t) {
11 + t.throws(function () {
12 + CronExpression.parse('*/10 * * * * ,', options);
13 + }, new Error('Invalid list value format'));
14 + t.end();
15 +});
16 +
17 +test('one side empty around comma', function (t) {
18 + t.throws(function () {
19 + CronExpression.parse('*/10 * * * * ,2', options);
20 + }, new Error('Invalid list value format'));
21 + t.end();
22 +});
1 +var test = require('tap').test;
2 +var CronExpression = require('../lib/expression');
3 +var CronDate = require('../lib/date');
4 +
5 +test('empty expression test', function(t) {
6 + try {
7 + var interval = CronExpression.parse('');
8 + t.ok(interval, 'Interval parsed');
9 +
10 + var date = new CronDate();
11 + date.addMinute();
12 +
13 + var next = interval.next();
14 +
15 + t.ok(next, 'Found next scheduled interval');
16 + t.equal(next.getMinutes(), date.getMinutes(), 'Schedule matches');
17 +
18 + t.end();
19 + } catch (err) {
20 + t.error(err, 'Interval parse error');
21 + }
22 +});
23 +
24 +test('default expression test', function(t) {
25 + try {
26 + var interval = CronExpression.parse('* * * * *');
27 + t.ok(interval, 'Interval parsed');
28 +
29 + var date = new CronDate();
30 + date.addMinute();
31 +
32 + var next = interval.next();
33 +
34 + t.ok(next, 'Found next scheduled interval');
35 + t.equal(next.getMinutes(), date.getMinutes(), 'Schedule matches');
36 +
37 + } catch (err) {
38 + t.error(err, 'Interval parse error');
39 + }
40 +
41 + t.end();
42 +});
43 +
44 +test('default expression (tab separate) test', function(t) {
45 + try {
46 + var interval = CronExpression.parse('* * * * *');
47 + t.ok(interval, 'Interval parsed');
48 +
49 + var date = new CronDate();
50 + date.addMinute();
51 +
52 + var next = interval.next();
53 +
54 + t.ok(next, 'Found next scheduled interval');
55 + t.equal(next.getMinutes(), date.getMinutes(), 'Schedule matches');
56 +
57 + } catch (err) {
58 + t.error(err, 'Interval parse error');
59 + }
60 +
61 + t.end();
62 +});
63 +
64 +test('default expression (multi-space separated) test 1', function(t) {
65 + try {
66 + var interval = CronExpression.parse('* \t*\t\t *\t * \t\t*');
67 + t.ok(interval, 'Interval parsed');
68 +
69 + var date = new CronDate();
70 + date.addMinute();
71 +
72 + var next = interval.next();
73 +
74 + t.ok(next, 'Found next scheduled interval');
75 + t.equal(next.getMinutes(), date.getMinutes(), 'Schedule matches');
76 +
77 + } catch (err) {
78 + t.error(err, 'Interval parse error');
79 + }
80 +
81 + t.end();
82 +});
83 +
84 +
85 +test('default expression (multi-space separated) test 1', function(t) {
86 + try {
87 + var interval = CronExpression.parse('* \t *\t \t * * \t \t *');
88 + t.ok(interval, 'Interval parsed');
89 +
90 + var date = new CronDate();
91 + date.addMinute();
92 +
93 + var next = interval.next();
94 +
95 + t.ok(next, 'Found next scheduled interval');
96 + t.equal(next.getMinutes(), date.getMinutes(), 'Schedule matches');
97 +
98 + } catch (err) {
99 + t.error(err, 'Interval parse error');
100 + }
101 +
102 + t.end();
103 +});
104 +
105 +test('value out of the range', function(t) {
106 + t.throws(function() {
107 + CronExpression.parse('61 * * * * *');
108 + }, new Error('Constraint error, got value 61 expected range 0-59'));
109 +
110 + t.end();
111 +});
112 +
113 +test('second value out of the range', function(t) {
114 + t.throws(function() {
115 + CronExpression.parse('-1 * * * * *');
116 + }, new Error('Constraint error, got value -1 expected range 0-59'));
117 +
118 + t.end();
119 +});
120 +
121 +test('invalid range', function(t) {
122 + t.throws(function() {
123 + CronExpression.parse('- * * * * *');
124 + }, new Error('Invalid range: -'));
125 +
126 + t.end();
127 +});
128 +
129 +test('minute value out of the range', function(t) {
130 + t.throws(function() {
131 + CronExpression.parse('* 32,72 * * * *');
132 + }, new Error('Constraint error, got value 72 expected range 0-59'));
133 +
134 + t.end();
135 +});
136 +
137 +test('hour value out of the range', function(t) {
138 + t.throws(function() {
139 + CronExpression.parse('* * 12-36 * * *');
140 + }, new Error('Constraint error, got range 12-36 expected range 0-23'));
141 +
142 + t.end();
143 +});
144 +
145 +
146 +test('day of the month value out of the range', function(t) {
147 + t.throws(function() {
148 + CronExpression.parse('* * * 10-15,40 * *');
149 + }, ('Constraint error, got value 40 expected range 1-31'));
150 +
151 + t.end();
152 +});
153 +
154 +test('month value out of the range', function(t) {
155 + t.throws(function() {
156 + CronExpression.parse('* * * * */10,12-13 *');
157 + }, new Error('Constraint error, got range 12-13 expected range 1-12'));
158 +
159 + t.end();
160 +});
161 +
162 +test('day of the week value out of the range', function(t) {
163 + t.throws(function() {
164 + CronExpression.parse('* * * * * 9');
165 + }, new Error('Constraint error, got value 9 expected range 0-7'));
166 +
167 + t.end();
168 +});
169 +
170 +test('invalid expression that contains too many fields', function (t) {
171 + t.throws(function() {
172 + CronExpression.parse('* * * * * * * *ASD');
173 + }, new Error('Invalid cron expression'));
174 +
175 + t.end();
176 +});
177 +
178 +test('invalid explicit day of month definition', function(t) {
179 + t.throws(function() {
180 + const iter = CronExpression.parse('0 0 31 4 *');
181 + iter.next();
182 + }, new Error('Invalid explicit day of month definition'));
183 +
184 + t.end();
185 +});
186 +
187 +test('incremental minutes expression test', function(t) {
188 + try {
189 + var interval = CronExpression.parse('*/3 * * * *');
190 + t.ok(interval, 'Interval parsed');
191 +
192 + var next = interval.next();
193 +
194 + t.ok(next, 'Found next scheduled interval');
195 + t.equal(next.getMinutes() % 3, 0, 'Schedule matches');
196 + } catch (err) {
197 + t.error(err, 'Interval parse error');
198 + }
199 +
200 + t.end();
201 +});
202 +
203 +test('fixed expression test', function(t) {
204 + try {
205 + var interval = CronExpression.parse('10 2 12 8 0');
206 + t.ok(interval, 'Interval parsed');
207 +
208 + var next = interval.next();
209 +
210 + t.ok(next, 'Found next scheduled interval');
211 + t.ok(next.getDay() === 0 || next.getDate() === 12, 'Day or day of Month matches');
212 + t.equal(next.getMonth(), 7, 'Month matches');
213 + t.equal(next.getHours(), 2, 'Hour matches');
214 + t.equal(next.getMinutes(), 10, 'Minute matches');
215 + } catch (err) {
216 + t.error(err, 'Interval parse error');
217 + }
218 +
219 + t.end();
220 +});
221 +
222 +test('invalid characters test - symbol', function(t) {
223 + t.throws(function() {
224 + CronExpression.parse('10 ! 12 8 0');
225 + }, new Error('Invalid characters, got value: !'));
226 +
227 + t.end();
228 +});
229 +
230 +test('invalid characters test - letter', function(t) {
231 + t.throws(function() {
232 + CronExpression.parse('10 x 12 8 0');
233 + }, new Error('Invalid characters, got value: x'));
234 +
235 + t.end();
236 +});
237 +
238 +test('invalid characters test - parentheses', function(t) {
239 + t.throws(function() {
240 + CronExpression.parse('10 ) 12 8 0');
241 + }, new Error('Invalid characters, got value: )'));
242 +
243 + t.end();
244 +});
245 +
246 +test('interval with invalid characters test', function(t) {
247 + t.throws(function() {
248 + CronExpression.parse('10 */A 12 8 0');
249 + }, new Error('Invalid characters, got value: */A'));
250 +
251 + t.end();
252 +});
253 +
254 +test('range with invalid characters test', function(t) {
255 + t.throws(function() {
256 + CronExpression.parse('10 0-z 12 8 0');
257 + }, new Error('Invalid characters, got value: 0-z'));
258 +
259 + t.end();
260 +});
261 +
262 +test('group with invalid characters test', function(t) {
263 + t.throws(function() {
264 + CronExpression.parse('10 0,1,z 12 8 0');
265 + }, new Error('Invalid characters, got value: 0,1,z'));
266 +
267 + t.end();
268 +});
269 +
270 +test('invalid expression which has repeat 0 times', function(t) {
271 + t.throws(function() {
272 + CronExpression.parse('0 */0 * * *');
273 + }, new Error('Constraint error, cannot repeat at every 0 time.'));
274 +
275 + t.end();
276 +});
277 +
278 +test('invalid expression which has repeat negative number times', function(t) {
279 + t.throws(function() {
280 + CronExpression.parse('0 */-5 * * *');
281 + }, new Error('Constraint error, cannot repeat at every -5 time.'));
282 +
283 + t.end();
284 +});
285 +
286 +test('range test with value and repeat (second)', function(t) {
287 + var options = {
288 + currentDate: new CronDate('Wed, 26 Dec 2012 14:38:53')
289 + };
290 + var interval = CronExpression.parse('0/30 * * * * ?', options);
291 + t.ok(interval, 'Interval parsed');
292 +
293 + var next = interval.next();
294 + t.equal(next.getSeconds(), 0);
295 +
296 + next = interval.next();
297 + t.equal(next.getSeconds(), 30);
298 +
299 + next = interval.next();
300 + t.equal(next.getSeconds(), 0);
301 +
302 + t.end();
303 +});
304 +
305 +test('range test with value and repeat (minute)', function(t) {
306 + var options = {
307 + currentDate: new CronDate('Wed, 26 Dec 2012 14:38:53')
308 + };
309 + var interval = CronExpression.parse('6/23 * * * *', options);
310 + t.ok(interval, 'Interval parsed');
311 +
312 + var next = interval.next();
313 + t.equal(next.getMinutes(), 52);
314 +
315 + next = interval.next();
316 + t.equal(next.getMinutes(), 6);
317 +
318 + next = interval.next();
319 + t.equal(next.getMinutes(), 29);
320 +
321 + next = interval.next();
322 + t.equal(next.getMinutes(), 52);
323 +
324 + t.end();
325 +});
326 +
327 +test('range test with iterator', function(t) {
328 + try {
329 + var interval = CronExpression.parse('10-30 2 12 8 0');
330 + t.ok(interval, 'Interval parsed');
331 +
332 + var intervals = interval.iterate(20);
333 + t.ok(intervals, 'Found intervals');
334 +
335 + for (var i = 0, c = intervals.length; i < c; i++) {
336 + var next = intervals[i];
337 +
338 + t.ok(next, 'Found next scheduled interval');
339 + t.ok(next.getDay() === 0 || next.getDate() === 12, 'Day or day of month matches');
340 + t.equal(next.getMonth(), 7, 'Month matches');
341 + t.equal(next.getHours(), 2, 'Hour matches');
342 + t.equal(next.getMinutes(), 10 + i, 'Minute matches');
343 + }
344 + } catch (err) {
345 + t.error(err, 'Interval parse error');
346 + }
347 +
348 + t.end();
349 +});
350 +
351 +test('incremental range test with iterator', function(t) {
352 + try {
353 + var interval = CronExpression.parse('10-30/2 2 12 8 0');
354 + t.ok(interval, 'Interval parsed');
355 +
356 + var intervals = interval.iterate(10);
357 + t.ok(intervals, 'Found intervals');
358 +
359 + for (var i = 0, c = intervals.length; i < c; i++) {
360 + var next = intervals[i];
361 +
362 + t.ok(next, 'Found next scheduled interval');
363 + t.ok(next.getDay() === 0 || next.getDate() === 12, 'Day or day of month matches');
364 + t.equal(next.getMonth(), 7, 'Month matches');
365 + t.equal(next.getHours(), 2, 'Hour matches');
366 + t.equal(next.getMinutes(), 10 + (i * 2), 'Minute matches');
367 + }
368 + } catch (err) {
369 + t.error(err, 'Interval parse error');
370 + }
371 +
372 + t.end();
373 +});
374 +
375 +test('predefined expression', function(t) {
376 + try {
377 + var interval = CronExpression.parse('@yearly');
378 + t.ok(interval, 'Interval parsed');
379 +
380 + var date = new CronDate();
381 + date.addYear();
382 +
383 + var next = interval.next();
384 + t.ok(next, 'Found next scheduled interval');
385 +
386 + t.equal(next.getFullYear(), date.getFullYear(), 'Year matches');
387 + } catch (err) {
388 + t.error(err, 'Interval parse error');
389 + }
390 +
391 + t.end();
392 +});
393 +
394 +test('expression limited with start and end date', function(t) {
395 + try {
396 + var options = {
397 + currentDate: new CronDate('Wed, 26 Dec 2012 14:38:53'),
398 + startDate: new CronDate('Wed, 26 Dec 2012 12:40:00'),
399 + endDate: new CronDate('Wed, 26 Dec 2012 16:40:00')
400 + };
401 +
402 + var interval = CronExpression.parse('*/20 * * * *', options);
403 + t.ok(interval, 'Interval parsed');
404 +
405 + var dates = interval.iterate(10);
406 + t.equal(dates.length, 7, 'Dates count matches for positive iteration');
407 +
408 + interval.reset();
409 +
410 + var dates = interval.iterate(-10);
411 + t.equal(dates.length, 6, 'Dates count matches for negative iteration');
412 +
413 + interval.reset();
414 +
415 + // Forward iteration
416 + var next = interval.next();
417 + t.equal(next.getHours(), 14, 'Hour matches');
418 + t.equal(next.getMinutes(), 40, 'Minute matches');
419 +
420 + next = interval.next();
421 + t.equal(next.getHours(), 15, 'Hour matches');
422 + t.equal(next.getMinutes(), 0, 'Minute matches');
423 +
424 + next = interval.next();
425 + t.equal(next.getHours(), 15, 'Hour matches');
426 + t.equal(next.getMinutes(), 20, 'Minute matches');
427 +
428 + next = interval.next();
429 + t.equal(next.getHours(), 15, 'Hour matches');
430 + t.equal(next.getMinutes(), 40, 'Minute matches');
431 +
432 + next = interval.next();
433 + t.equal(next.getHours(), 16, 'Hour matches');
434 + t.equal(next.getMinutes(), 0, 'Minute matches');
435 +
436 + next = interval.next();
437 + t.equal(next.getHours(), 16, 'Hour matches');
438 + t.equal(next.getMinutes(), 20, 'Minute matches');
439 +
440 + next = interval.next();
441 + t.equal(next.getHours(), 16, 'Hour matches');
442 + t.equal(next.getMinutes(), 40, 'Minute matches');
443 +
444 + try {
445 + interval.next();
446 + t.ok(false, 'Should fail');
447 + } catch (e) {
448 + t.ok(true, 'Failed as expected');
449 + }
450 +
451 + next = interval.prev();
452 + t.equal(next.getHours(), 16, 'Hour matches');
453 + t.equal(next.getMinutes(), 20, 'Minute matches');
454 +
455 + interval.reset();
456 +
457 + // Backward iteration
458 + var prev = interval.prev();
459 + t.equal(prev.getHours(), 14, 'Hour matches');
460 + t.equal(prev.getMinutes(), 20, 'Minute matches');
461 +
462 + prev = interval.prev();
463 + t.equal(prev.getHours(), 14, 'Hour matches');
464 + t.equal(prev.getMinutes(), 0, 'Minute matches');
465 +
466 + prev = interval.prev();
467 + t.equal(prev.getHours(), 13, 'Hour matches');
468 + t.equal(prev.getMinutes(), 40, 'Minute matches');
469 +
470 + prev = interval.prev();
471 + t.equal(prev.getHours(), 13, 'Hour matches');
472 + t.equal(prev.getMinutes(), 20, 'Minute matches');
473 +
474 + prev = interval.prev();
475 + t.equal(prev.getHours(), 13, 'Hour matches');
476 + t.equal(prev.getMinutes(), 0, 'Minute matches');
477 +
478 + prev = interval.prev();
479 + t.equal(prev.getHours(), 12, 'Hour matches');
480 + t.equal(prev.getMinutes(), 40, 'Minute matches');
481 +
482 + try {
483 + interval.prev();
484 + t.ok(false, 'Should fail');
485 + } catch (e) {
486 + t.ok(true, 'Failed as expected');
487 + }
488 + } catch (err) {
489 + t.error(err, 'Interval parse error');
490 + }
491 +
492 + t.end();
493 +});
494 +
495 +test('reset to given date', function(t){
496 + try {
497 + var options = {
498 + currentDate: new CronDate('Wed, 26 Dec 2012 14:38:53')
499 + };
500 +
501 + var interval = CronExpression.parse('*/20 * * * *', options);
502 + t.ok(interval, 'Interval parsed');
503 +
504 + // Forward iteration
505 + var next = interval.next();
506 + t.equal(next.getHours(), 14, 'Hour matches');
507 + t.equal(next.getMinutes(), 40, 'Minute matches');
508 +
509 + interval.reset(); // defaults to initial currentDate
510 +
511 + next = interval.next();
512 + t.equal(next.getHours(), 14, 'Hour matches');
513 + t.equal(next.getMinutes(), 40, 'Minute matches');
514 +
515 + interval.reset(new CronDate('Wed, 26 Dec 2012 17:23:53'));
516 +
517 + next = interval.next();
518 + t.equal(next.getHours(), 17, 'Hour matches');
519 + t.equal(next.getMinutes(), 40, 'Minute matches');
520 +
521 + next = interval.next();
522 + t.equal(next.getHours(), 18, 'Hour matches');
523 + t.equal(next.getMinutes(), 0, 'Minute matches');
524 +
525 + interval.reset(new Date('2019-06-18T08:18:36.000'));
526 +
527 + next = interval.prev();
528 + t.equal(next.getDate(), 18, 'Date matches');
529 + t.equal(next.getHours(), 8, 'Hour matches');
530 + t.equal(next.getMinutes(), 0, 'Minute matches');
531 +
532 + next = interval.prev();
533 + t.equal(next.getDate(), 18, 'Date matches');
534 + t.equal(next.getHours(), 7, 'Hour matches');
535 + t.equal(next.getMinutes(), 40, 'Minute matches');
536 +
537 + t.end();
538 + } catch (err) {
539 + t.error(err, 'Reset error');
540 + }
541 +});
542 +
543 +test('parse expression as UTC', function(t) {
544 + try {
545 + var options = {
546 + utc: true
547 + };
548 +
549 + var interval = CronExpression.parse('0 0 10 * * *', options);
550 +
551 + var date = interval.next();
552 + t.equal(date.getUTCHours(), 10, 'Correct UTC hour value');
553 + t.equal(date.getHours(), 10, 'Correct UTC hour value');
554 +
555 + interval = CronExpression.parse('0 */5 * * * *', options);
556 +
557 + var date = interval.next(), now = new Date();
558 + now.setMinutes(now.getMinutes() + 5 - (now.getMinutes() % 5));
559 +
560 + t.equal(date.getHours(), now.getUTCHours(), 'Correct local time for 5 minute interval');
561 +
562 + } catch (err) {
563 + t.error(err, 'UTC parse error');
564 + }
565 +
566 + t.end();
567 +});
568 +
569 +test('expression using days of week strings', function(t) {
570 + try {
571 + var interval = CronExpression.parse('15 10 * * MON-TUE');
572 + t.ok(interval, 'Interval parsed');
573 +
574 + var intervals = interval.iterate(8);
575 + t.ok(intervals, 'Found intervals');
576 +
577 + for (var i = 0, c = intervals.length; i < c; i++) {
578 + var next = intervals[i];
579 + var day = next.getDay();
580 +
581 +
582 + t.ok(next, 'Found next scheduled interval');
583 + t.ok(day == 1 || day == 2, 'Day matches');
584 + t.equal(next.getHours(), 10, 'Hour matches');
585 + t.equal(next.getMinutes(), 15, 'Minute matches');
586 + }
587 + } catch (err) {
588 + t.error(err, 'Interval parse error');
589 + }
590 +
591 + t.end();
592 +});
593 +
594 +test('expression using days of week strings - wrong alias', function(t) {
595 + t.throws(function () {
596 + CronExpression.parse('15 10 * * MON-TUR');
597 + }, new Error('Validation error, cannot resolve alias "tur"'));
598 +
599 + t.end();
600 +});
601 +
602 +test('expression using mixed days of week strings', function(t) {
603 + try {
604 + var options = {
605 + currentDate: new CronDate('Wed, 26 Dec 2012 14:38:53')
606 + };
607 +
608 + var interval = CronExpression.parse('15 10 * jAn-FeB mOn-tUE', options);
609 + t.ok(interval, 'Interval parsed');
610 +
611 + var intervals = interval.iterate(8);
612 + t.ok(intervals, 'Found intervals');
613 +
614 + for (var i = 0, c = intervals.length; i < c; i++) {
615 + var next = intervals[i];
616 + var day = next.getDay();
617 + var month = next.getMonth();
618 +
619 + t.ok(next, 'Found next scheduled interval');
620 + t.ok(month == 0 || month == 2, 'Month Matches');
621 + t.ok(day == 1 || day == 2, 'Day matches');
622 + t.equal(next.getHours(), 10, 'Hour matches');
623 + t.equal(next.getMinutes(), 15, 'Minute matches');
624 + }
625 + } catch (err) {
626 + t.error(err, 'Interval parse error');
627 + }
628 +
629 + t.end();
630 +});
631 +
632 +test('expression using non-standard second field (wildcard)', function(t) {
633 + try {
634 + var options = {
635 + currentDate: new CronDate('Wed, 26 Dec 2012 14:38:00'),
636 + endDate: new CronDate('Wed, 26 Dec 2012 15:40:00')
637 + };
638 +
639 + var interval = CronExpression.parse('* * * * * *', options);
640 + t.ok(interval, 'Interval parsed');
641 +
642 + var intervals = interval.iterate(10);
643 + t.ok(intervals, 'Found intervals');
644 +
645 + for (var i = 0, c = intervals.length; i < c; i++) {
646 + var next = intervals[i];
647 + t.ok(next, 'Found next scheduled interval');
648 + t.equal(next.getSeconds(), i + 1, 'Second matches');
649 + }
650 + } catch (err) {
651 + t.error(err, 'Interval parse error');
652 + }
653 +
654 + t.end();
655 +});
656 +
657 +test('expression using non-standard second field (step)', function(t) {
658 + try {
659 + var options = {
660 + currentDate: new CronDate('Wed, 26 Dec 2012 14:38:00'),
661 + endDate: new CronDate('Wed, 26 Dec 2012 15:40:00')
662 + };
663 +
664 + var interval = CronExpression.parse('*/20 * * * * *', options);
665 + t.ok(interval, 'Interval parsed');
666 +
667 + var intervals = interval.iterate(3);
668 + t.ok(intervals, 'Found intervals');
669 +
670 + t.equal(intervals[0].getSeconds(), 20, 'Second matches');
671 + t.equal(intervals[1].getSeconds(), 40, 'Second matches');
672 + t.equal(intervals[2].getSeconds(), 0, 'Second matches');
673 + } catch (err) {
674 + t.error(err, 'Interval parse error');
675 + }
676 +
677 + t.end();
678 +});
679 +
680 +test('expression using non-standard second field (range)', function(t) {
681 + try {
682 + var options = {
683 + currentDate: new CronDate('Wed, 26 Dec 2012 14:38:00'),
684 + endDate: new CronDate('Wed, 26 Dec 2012 15:40:00')
685 + };
686 +
687 + var interval = CronExpression.parse('20-40/10 * * * * *', options);
688 + t.ok(interval, 'Interval parsed');
689 +
690 + var intervals = interval.iterate(3);
691 + t.ok(intervals, 'Found intervals');
692 +
693 + for (var i = 0, c = intervals.length; i < c; i++) {
694 + var next = intervals[i];
695 +
696 + t.ok(next, 'Found next scheduled interval');
697 + t.equal(next.getSeconds(), 20 + (i * 10), 'Second matches');
698 + }
699 + } catch (err) {
700 + t.error(err, 'Interval parse error');
701 + }
702 +
703 + t.end();
704 +});
705 +
706 +test('expression using explicit month definition and */5 day of month step', function(t) {
707 + var firstIterator = CronExpression.parse('0 12 */5 6 *', {
708 + currentDate: '2019-06-01T11:00:00.000'
709 + });
710 +
711 + var firstExpectedDates = [
712 + new CronDate('2019-06-01T12:00:00.000'),
713 + new CronDate('2019-06-06T12:00:00.000'),
714 + new CronDate('2019-06-11T12:00:00.000'),
715 + new CronDate('2019-06-16T12:00:00.000'),
716 + new CronDate('2019-06-21T12:00:00.000'),
717 + new CronDate('2019-06-26T12:00:00.000'),
718 + new CronDate('2020-06-01T12:00:00.000')
719 + ];
720 +
721 + firstExpectedDates.forEach(function(expectedDate) {
722 + t.equal(expectedDate.toISOString(), firstIterator.next().toISOString());
723 + });
724 +
725 + var secondIterator = CronExpression.parse('0 15 */5 5 *', {
726 + currentDate: '2019-05-01T11:00:00.000'
727 + });
728 +
729 + var secondExpectedDates = [
730 + new CronDate('2019-05-01T15:00:00.000'),
731 + new CronDate('2019-05-06T15:00:00.000'),
732 + new CronDate('2019-05-11T15:00:00.000'),
733 + new CronDate('2019-05-16T15:00:00.000'),
734 + new CronDate('2019-05-21T15:00:00.000'),
735 + new CronDate('2019-05-26T15:00:00.000'),
736 + new CronDate('2019-05-31T15:00:00.000'),
737 + new CronDate('2020-05-01T15:00:00.000')
738 + ];
739 +
740 + secondExpectedDates.forEach(function(expectedDate) {
741 + t.equal(expectedDate.toISOString(), secondIterator.next().toISOString());
742 + });
743 +
744 + t.end();
745 +});
746 +
747 +test('day of month and week are both set', function(t) {
748 + try {
749 + var interval = CronExpression.parse('10 2 12 8 0');
750 + t.ok(interval, 'Interval parsed');
751 +
752 + var next = interval.next();
753 +
754 + t.ok(next, 'Found next scheduled interval');
755 + t.ok(next.getDay() === 0 || next.getDate() === 12, 'Day or day of month matches');
756 + t.equal(next.getMonth(), 7, 'Month matches');
757 +
758 + next = interval.next();
759 +
760 + t.ok(next, 'Found next scheduled interval');
761 + t.ok(next.getDay() === 0 || next.getDate() === 12, 'Day or day of month matches');
762 + t.equal(next.getMonth(), 7, 'Month matches');
763 +
764 + next = interval.next();
765 +
766 + t.ok(next, 'Found next scheduled interval');
767 + t.ok(next.getDay() === 0 || next.getDate() === 12, 'Day or day of month matches');
768 + t.equal(next.getMonth(), 7, 'Month matches');
769 +
770 + next = interval.next();
771 +
772 + t.ok(next, 'Found next scheduled interval');
773 + t.ok(next.getDay() === 0 || next.getDate() === 12, 'Day or day of month matches');
774 + t.equal(next.getMonth(), 7, 'Month matches');
775 + } catch (err) {
776 + t.error(err, 'Interval parse error');
777 + }
778 +
779 + t.end();
780 +});
781 +
782 +test('day of month is unspecified', function(t) {
783 + try {
784 + var interval = CronExpression.parse('10 2 ? * 3');
785 +
786 + t.ok(interval, 'Interval parsed');
787 +
788 + var next = interval.next();
789 + t.ok(next, 'Found next scheduled interal');
790 + t.ok(next.getDay() === 3, 'day of week matches');
791 +
792 + next = interval.next();
793 + t.ok(next, 'Found next scheduled interal');
794 + t.ok(next.getDay() === 3, 'day of week matches');
795 +
796 + next = interval.next();
797 + t.ok(next, 'Found next scheduled interal');
798 + t.ok(next.getDay() === 3, 'day of week matches');
799 +
800 + next = interval.next();
801 + t.ok(next, 'Found next scheduled interal');
802 + t.ok(next.getDay() === 3, 'day of week matches');
803 +
804 + } catch (err) {
805 + t.error(err, 'Interval parse error');
806 + }
807 +
808 + t.end();
809 +});
810 +
811 +test('day of week is unspecified', function(t) {
812 + try {
813 + var interval = CronExpression.parse('10 2 3,6 * ?');
814 +
815 + t.ok(interval, 'Interval parsed');
816 +
817 + var next = interval.next();
818 + t.ok(next, 'Found next scheduled interal');
819 + t.ok(next.getDate() === 3 || next.getDate() === 6, 'date matches');
820 + var prevDate = next.getDate();
821 +
822 + next = interval.next();
823 + t.ok(next, 'Found next scheduled interal');
824 + t.ok((next.getDate() === 3 || next.getDate() === 6) &&
825 + next.getDate() !== prevDate, 'date matches and is not previous date');
826 + prevDate = next.getDate();
827 +
828 + next = interval.next();
829 + t.ok(next, 'Found next scheduled interal');
830 + t.ok((next.getDate() === 3 || next.getDate() === 6) &&
831 + next.getDate() !== prevDate, 'date matches and is not previous date');
832 + prevDate = next.getDate();
833 +
834 + next = interval.next();
835 + t.ok(next, 'Found next scheduled interal');
836 + t.ok((next.getDate() === 3 || next.getDate() === 6) &&
837 + next.getDate() !== prevDate, 'date matches and is not previous date');
838 + } catch (err) {
839 + t.error(err, 'Interval parse error');
840 + }
841 +
842 + t.end();
843 +});
844 +
845 +test('Summertime bug test', function(t) {
846 + try {
847 + var month = new CronDate().getMonth() + 1;
848 + var interval = CronExpression.parse('0 0 0 1 '+month+' *');
849 + t.ok(interval, 'Interval parsed');
850 +
851 + var next = interval.next();
852 +
853 + // Before fix the bug it was getting a timeout error if you are
854 + // in a timezone that changes the DST to ST in the hour 00:00h.
855 + t.ok(next, 'Found next scheduled interval');
856 + } catch (err) {
857 + t.error(err, 'Interval parse error');
858 + }
859 +
860 + t.end();
861 +});
862 +
863 +
864 +test('day of month and week are both set and dow is 7', function(t) {
865 + try {
866 + var interval = CronExpression.parse('10 2 12 8 7');
867 + t.ok(interval, 'Interval parsed');
868 +
869 + var next = interval.next();
870 +
871 + t.ok(next, 'Found next scheduled interval');
872 + t.ok(next.getDay() === 0 || next.getDate() === 12, 'Day or day of month matches');
873 + t.equal(next.getMonth(), 7, 'Month matches');
874 +
875 + next = interval.next();
876 +
877 + t.ok(next, 'Found next scheduled interval');
878 + t.ok(next.getDay() === 0 || next.getDate() === 12, 'Day or day of month matches');
879 + t.equal(next.getMonth(), 7, 'Month matches');
880 +
881 + next = interval.next();
882 +
883 + t.ok(next, 'Found next scheduled interval');
884 + t.ok(next.getDay() === 0 || next.getDate() === 12, 'Day or day of month matches');
885 + t.equal(next.getMonth(), 7, 'Month matches');
886 +
887 + next = interval.next();
888 +
889 + t.ok(next, 'Found next scheduled interval');
890 + t.ok(next.getDay() === 0 || next.getDate() === 12, 'Day or day of month matches');
891 + t.equal(next.getMonth(), 7, 'Month matches');
892 + } catch (err) {
893 + t.error(err, 'Interval parse error');
894 + }
895 +
896 + t.end();
897 +});
898 +
899 +test('day of month is wildcard, month and day of week are both set', function(t) {
900 + try {
901 + var options = {
902 + currentDate: new CronDate('Mon, 31 May 2021 12:00:00')
903 + };
904 + var interval = CronExpression.parse('0 0 * 6 2', options);
905 + t.ok(interval, 'Interval parsed');
906 +
907 + var expectedDayMatches = [1, 8, 15, 22, 29];
908 + expectedDayMatches.forEach(function(dayOfMonth) {
909 + var next = interval.next();
910 + t.ok(next, 'Found next scheduled interval');
911 + t.equal(next.getDay(), 2, 'Day of week matches');
912 + t.equal(next.getDate(), dayOfMonth, 'Day of month matches');
913 + t.equal(next.getMonth(), 5, 'Month matches');
914 + });
915 + } catch (err) {
916 + t.error(err, 'Interval parse error');
917 + }
918 +
919 + t.end();
920 +});
921 +
922 +test('day of month and week are both set and dow is 6,0', function(t) {
923 + try {
924 + var options = {
925 + currentDate: new CronDate('Wed, 26 Dec 2012 14:38:53')
926 + };
927 + var interval = CronExpression.parse('10 2 12 8 6,0', options);
928 + t.ok(interval, 'Interval parsed');
929 +
930 + var next = interval.next();
931 +
932 + t.ok(next, 'Found next scheduled interval');
933 + t.ok(next.getDay() === 6 || next.getDate() === 12, 'Day or day of month matches');
934 + t.equal(next.getMonth(), 7, 'Month matches');
935 +
936 + next = interval.next();
937 +
938 + t.ok(next, 'Found next scheduled interval');
939 + t.ok(next.getDay() === 0 || next.getDate() === 12, 'Day or day of month matches');
940 + t.equal(next.getMonth(), 7, 'Month matches');
941 +
942 + next = interval.next();
943 +
944 + t.ok(next, 'Found next scheduled interval');
945 + t.ok(next.getDay() === 6 || next.getDate() === 12, 'Day or day of month matches');
946 + t.equal(next.getMonth(), 7, 'Month matches');
947 +
948 + next = interval.next();
949 +
950 + t.ok(next, 'Found next scheduled interval');
951 + t.ok(next.getDay() === 0 || next.getDate() === 12, 'Day or day of month matches');
952 + t.equal(next.getMonth(), 7, 'Month matches');
953 + } catch (err) {
954 + t.error(err, 'Interval parse error');
955 + }
956 +
957 + t.end();
958 +});
959 +
960 +
961 +test('day of month and week are both set and dow is 6-7', function(t) {
962 + try {
963 + var options = {
964 + currentDate: new CronDate('Wed, 26 Dec 2012 14:38:53')
965 + };
966 + var interval = CronExpression.parse('10 2 12 8 6-7', options);
967 + t.ok(interval, 'Interval parsed');
968 +
969 + var next = interval.next();
970 +
971 + t.ok(next, 'Found next scheduled interval');
972 + t.ok(next.getDay() === 6 || next.getDate() === 12, 'Day or day of month matches');
973 + t.equal(next.getMonth(), 7, 'Month matches');
974 +
975 + next = interval.next();
976 +
977 + t.ok(next, 'Found next scheduled interval');
978 + t.ok(next.getDay() === 6 || next.getDate() === 12, 'Day or day of month matches');
979 + t.equal(next.getMonth(), 7, 'Month matches');
980 +
981 + next = interval.next();
982 +
983 + t.ok(next, 'Found next scheduled interval');
984 + t.ok(next.getDay() === 6 || next.getDate() === 12, 'Day or day of month matches');
985 + t.equal(next.getMonth(), 7, 'Month matches');
986 +
987 + // next = interval.next();
988 +
989 + t.ok(next, 'Found next scheduled interval');
990 + t.ok(next.getDay() === 6 || next.getDate() === 12, 'Day or day of month matches');
991 + t.equal(next.getMonth(), 7, 'Month matches');
992 + } catch (err) {
993 + t.error(err, 'Interval parse error');
994 + }
995 +
996 + t.end();
997 +});
998 +
999 +test('day of month validation should be ignored when day of month is wildcard and month is set', function (t) {
1000 + try {
1001 + var options = {
1002 + currentDate: new CronDate('2020-05-01T15:00:00.000')
1003 + };
1004 + var interval = CronExpression.parse('* * * * 2 *', options);
1005 + t.ok(interval, 'Interval parsed');
1006 +
1007 + var next = interval.next();
1008 +
1009 + t.ok(next, 'Found next scheduled interval');
1010 + t.equal(next.getHours(), 0, 'Hours matches');
1011 + t.equal(next.getDate(), 1, 'Day of month matches');
1012 + t.equal(next.getMonth() + 1, 2, 'Month matches');
1013 +
1014 + t.ok(next, 'Found next scheduled interval');
1015 + } catch (err) {
1016 + t.error(err, 'Interval parse error');
1017 + }
1018 +
1019 + t.end();
1020 +});
1021 +
1022 +test('day and date in week should matches', function(t){
1023 + try {
1024 + var interval = CronExpression.parse('0 1 1 1 * 1');
1025 + t.ok(interval, 'Interval parsed');
1026 +
1027 + var next = interval.next();
1028 +
1029 + t.ok(next, 'Found next scheduled interval');
1030 + t.equal(next.getHours(), 1, 'Hours matches');
1031 + t.ok(next.getDay() === 1 || next.getDate() === 1, 'Day or day of month matches');
1032 +
1033 + next = interval.next();
1034 +
1035 + t.ok(next, 'Found next scheduled interval');
1036 + t.equal(next.getHours(), 1, 'Hours matches');
1037 + t.ok(next.getDay() === 1 || next.getDate() === 1, 'Day or day of month matches');
1038 +
1039 + next = interval.next();
1040 +
1041 + t.ok(next, 'Found next scheduled interval');
1042 + t.equal(next.getHours(), 1, 'Hours matches');
1043 + t.ok(next.getDay() === 1 || next.getDate() === 1, 'Day or day of month matches');
1044 +
1045 + } catch (err) {
1046 + t.error(err, 'Interval parse error');
1047 + }
1048 +
1049 + t.end();
1050 +});
1051 +
1052 +test('should sort ranges and values in ascending order', function(t) {
1053 + var options = {
1054 + currentDate: new CronDate('Wed, 26 Dec 2012 14:38:53')
1055 + };
1056 + var interval = CronExpression.parse('0 12,13,10,1-3 * * *', options);
1057 + t.ok(interval, 'Interval parsed');
1058 +
1059 + var hours = [ 1, 2, 3, 10, 12, 13 ];
1060 + for (var i in hours) {
1061 + next = interval.next();
1062 +
1063 + t.ok(next, 'Found next scheduled interval');
1064 + t.equal(next.getHours(), hours[i], 'Hours matches');
1065 + }
1066 +
1067 + t.end();
1068 +});
1069 +
1070 +test('valid ES6 iterator should be returned if iterator options is set to true', function(t) {
1071 + try {
1072 + var options = {
1073 + currentDate: new CronDate('Wed, 26 Dec 2012 14:38:53'),
1074 + endDate: new CronDate('Wed, 26 Dec 2012 15:40:00'),
1075 + iterator: true
1076 + };
1077 +
1078 + var val = null;
1079 + var interval = CronExpression.parse('*/25 * * * *', options);
1080 + t.ok(interval, 'Interval parsed');
1081 +
1082 + val = interval.next();
1083 + t.ok(val, 'Next iteration resolved');
1084 + t.ok(val.value, 'Iterator value is set');
1085 + t.notOk(val.done, 'Iterator is not finished');
1086 +
1087 + val = interval.next();
1088 + t.ok(val, 'Next iteration resolved');
1089 + t.ok(val.value, 'Iterator value is set');
1090 + t.notOk(val.done, 'Iterator is not finished');
1091 +
1092 + val = interval.next();
1093 + t.ok(val, 'Next iteration resolved');
1094 + t.ok(val.value, 'Iterator value is set');
1095 + t.ok(val.done, 'Iterator is finished');
1096 + } catch (err) {
1097 + t.error(err, 'Interval parse error');
1098 + }
1099 +
1100 + t.end();
1101 +});
1102 +
1103 +test('dow 6,7 6,0 0,6 7,6 should be equivalent', function(t) {
1104 + try {
1105 + var options = {
1106 + currentDate: new CronDate('Wed, 26 Dec 2012 14:38:53'),
1107 + };
1108 +
1109 + var expressions = [
1110 + '30 16 * * 6,7',
1111 + '30 16 * * 6,0',
1112 + '30 16 * * 0,6',
1113 + '30 16 * * 7,6'
1114 + ];
1115 +
1116 + expressions.forEach(function(expression) {
1117 + var interval = CronExpression.parse(expression, options);
1118 + t.ok(interval, 'Interval parsed');
1119 +
1120 + var val = interval.next();
1121 + t.equal(val.getDay(), 6, 'Day matches');
1122 +
1123 + val = interval.next();
1124 + t.equal(val.getDay(), 0, 'Day matches');
1125 +
1126 + val = interval.next();
1127 + t.equal(val.getDay(), 6, 'Day matches');
1128 + });
1129 + } catch (err) {
1130 + t.error(err, 'Interval parse error');
1131 + }
1132 +
1133 + t.end();
1134 +});
1135 +
1136 +test('hour 0 9,11,1 * * * and 0 1,9,11 * * * should be equivalent', function(t) {
1137 + try {
1138 + var options = {
1139 + currentDate: new CronDate('Wed, 26 Dec 2012 00:00:00'),
1140 + };
1141 +
1142 + var expressions = [
1143 + '0 9,11,1 * * *',
1144 + '0 1,9,11 * * *'
1145 + ];
1146 +
1147 + expressions.forEach(function(expression) {
1148 + var interval = CronExpression.parse(expression, options);
1149 + t.ok(interval, 'Interval parsed');
1150 +
1151 + var val = interval.next();
1152 + t.equal(val.getHours(), 1, 'Hour matches');
1153 +
1154 + val = interval.next();
1155 + t.equal(val.getHours(), 9, 'Hour matches');
1156 +
1157 + val = interval.next();
1158 + t.equal(val.getHours(), 11, 'Hour matches');
1159 +
1160 + val = interval.next();
1161 + t.equal(val.getHours(), 1, 'Hour matches');
1162 +
1163 + val = interval.next();
1164 + t.equal(val.getHours(), 9, 'Hour matches');
1165 +
1166 + val = interval.next();
1167 + t.equal(val.getHours(), 11, 'Hour matches');
1168 + });
1169 + } catch (err) {
1170 + t.error(err, 'Interval parse error');
1171 + }
1172 +
1173 + t.end();
1174 +});
1175 +
1176 +test('it will work with #139 issue case', function(t) {
1177 + var options = {
1178 + currentDate : new Date('2018-11-15T16:15:33.522Z'),
1179 + tz: 'Europe/Madrid'
1180 + };
1181 +
1182 + var interval = CronExpression.parse('0 0 0 1,2 * *', options);
1183 + var date = interval.next();
1184 +
1185 + t.equal(date.getFullYear(), 2018);
1186 + t.equal(date.getDate(), 1);
1187 + t.equal(date.getMonth(), 11);
1188 +
1189 + t.end();
1190 +});
1191 +
1192 +test('should work for valid first/second/third/fourth/fifth occurence dayOfWeek (# char)', function(t) {
1193 + try {
1194 + var options = {
1195 + currentDate: new CronDate('2019-04-30')
1196 + };
1197 +
1198 + var expectedFirstDates = [
1199 + new CronDate('2019-05-05'),
1200 + new CronDate('2019-06-02'),
1201 + new CronDate('2019-07-07'),
1202 + new CronDate('2019-08-04')
1203 + ];
1204 + var expectedSecondDates = [
1205 + new CronDate('2019-05-12'),
1206 + new CronDate('2019-06-09'),
1207 + new CronDate('2019-07-14'),
1208 + new CronDate('2019-08-11')
1209 + ];
1210 + var expectedThirdDates = [
1211 + new CronDate('2019-05-19'),
1212 + new CronDate('2019-06-16'),
1213 + new CronDate('2019-07-21'),
1214 + new CronDate('2019-08-18')
1215 + ];
1216 + var expectedFourthDates = [
1217 + new CronDate('2019-05-26'),
1218 + new CronDate('2019-06-23'),
1219 + new CronDate('2019-07-28'),
1220 + new CronDate('2019-08-25')
1221 + ];
1222 + var expectedFifthDates = [
1223 + new CronDate('2019-06-30'),
1224 + new CronDate('2019-09-29'),
1225 + new CronDate('2019-12-29'),
1226 + new CronDate('2020-03-29')
1227 + ];
1228 +
1229 + var allExpectedDates = [
1230 + expectedFirstDates,
1231 + expectedSecondDates,
1232 + expectedThirdDates,
1233 + expectedFourthDates,
1234 + expectedFifthDates
1235 + ];
1236 + var expressions = [
1237 + '0 0 0 ? * 0#1',
1238 + '0 0 0 ? * 0#2',
1239 + '0 0 0 ? * 0#3',
1240 + '0 0 0 ? * 0#4',
1241 + '0 0 0 ? * 0#5'
1242 + ];
1243 + expressions.forEach(function(expression, index) {
1244 + var interval = CronExpression.parse(expression, options);
1245 + var expectedDates = allExpectedDates[index];
1246 +
1247 + expectedDates.forEach(function(expected) {
1248 + var date = interval.next();
1249 + t.equal(
1250 + date.toISOString(),
1251 + expected.toISOString(),
1252 + 'Expression "' + expression + '" has next() that matches expected: ' + expected.toISOString()
1253 + );
1254 + });
1255 + expectedDates
1256 + .slice(0, expectedDates.length - 1)
1257 + .reverse()
1258 + .forEach(function(expected) {
1259 + var date = interval.prev();
1260 + t.equal(
1261 + date.toISOString(),
1262 + expected.toISOString(),
1263 + 'Expression "' + expression + '" has prev() that matches expected: ' + expected.toISOString()
1264 + );
1265 + });
1266 + });
1267 + } catch (err) {
1268 + t.error(err, 'Interval parse error');
1269 + }
1270 +
1271 + t.end();
1272 +});
1273 +
1274 +test('should work for valid second sunday in may', function(t) {
1275 + try {
1276 + var options = {
1277 + currentDate: new CronDate('2019-01-30')
1278 + };
1279 + var expectedDates = [
1280 + new CronDate('2019-05-12'),
1281 + new CronDate('2020-05-10'),
1282 + new CronDate('2021-05-09'),
1283 + new CronDate('2022-05-08')
1284 + ];
1285 +
1286 + var interval = CronExpression.parse('0 0 0 ? MAY 0#2', options);
1287 + expectedDates.forEach(function(expected) {
1288 + var date = interval.next();
1289 + t.equal(
1290 + date.toISOString(),
1291 + expected.toISOString(),
1292 + 'Expression "0 0 0 ? MAY 0#2" has next() that matches expected: ' + expected.toISOString()
1293 + );
1294 + });
1295 + expectedDates
1296 + .slice(0, expectedDates.length - 1)
1297 + .reverse()
1298 + .forEach(function(expected) {
1299 + var date = interval.prev();
1300 + t.equal(
1301 + date.toISOString(),
1302 + expected.toISOString(),
1303 + 'Expression "0 0 0 ? MAY 0#2" has prev() that matches expected: ' + expected.toISOString()
1304 + );
1305 + });
1306 + } catch (err) {
1307 + t.error(err, 'Interval parse error');
1308 + }
1309 +
1310 + t.end();
1311 +});
1312 +
1313 +test('should work for valid second sunday at noon in may', function(t) {
1314 + try {
1315 + var options = {
1316 + currentDate: new CronDate('2019-05-12T11:59:00.000')
1317 + };
1318 + var expected = new CronDate('2019-05-12T12:00:00.000');
1319 +
1320 + var interval = CronExpression.parse('0 0 12 ? MAY 0#2', options);
1321 + var date = interval.next();
1322 +
1323 + t.equal(
1324 + date.toISOString(),
1325 + expected.toISOString(),
1326 + 'Expression "0 0 12 ? MAY 0#2" has next() that matches expected: ' + expected.toISOString()
1327 + );
1328 + } catch (err) {
1329 + t.error(err, 'Interval parse error');
1330 + }
1331 +
1332 + t.end();
1333 +});
1334 +
1335 +test('should work for valid second sunday at noon in may (UTC+3)', function(t) {
1336 + try {
1337 + var options = {
1338 + currentDate: new CronDate('2019-05-12T11:59:00.000', 'Europe/Sofia')
1339 + };
1340 + var expected = new CronDate('2019-05-12T12:00:00.000', 'Europe/Sofia');
1341 +
1342 + var interval = CronExpression.parse('0 0 12 ? MAY 0#2', options);
1343 + var date = interval.next();
1344 +
1345 + t.equal(
1346 + date.toISOString(),
1347 + expected.toISOString(),
1348 + 'Expression "0 0 12 ? MAY 0#2" has next() that matches expected: ' + expected.toISOString()
1349 + );
1350 + } catch (err) {
1351 + t.error(err, 'Interval parse error');
1352 + }
1353 +
1354 + t.end();
1355 +});
1356 +
1357 +test('should work with both dayOfMonth and nth occurence of dayOfWeek', function(t) {
1358 + try {
1359 + var options = {
1360 + currentDate: new CronDate('2019-04-01')
1361 + };
1362 +
1363 + var expectedDates = [
1364 + new CronDate('2019-04-16'),
1365 + new CronDate('2019-04-17'),
1366 + new CronDate('2019-04-18'),
1367 + new CronDate('2019-05-15'),
1368 + new CronDate('2019-05-16'),
1369 + new CronDate('2019-05-18'),
1370 + ];
1371 +
1372 + var interval = CronExpression.parse('0 0 0 16,18 * 3#3', options);
1373 +
1374 + expectedDates.forEach(function(expected) {
1375 + var date = interval.next();
1376 + t.equal(
1377 + date.toISOString(),
1378 + expected.toISOString(),
1379 + 'Expression "0 0 0 16,18 * 3#3" has next() that matches expected: ' + expected.toISOString()
1380 + );
1381 + });
1382 + expectedDates
1383 + .slice(0, expectedDates.length - 1)
1384 + .reverse()
1385 + .forEach(function(expected) {
1386 + var date = interval.prev();
1387 + t.equal(
1388 + date.toISOString(),
1389 + expected.toISOString(),
1390 + 'Expression "0 0 0 16,18 * 3#3" has prev() that matches expected: ' + expected.toISOString()
1391 + );
1392 + });
1393 + } catch (err) {
1394 + t.error(err, 'Interval parse error');
1395 + }
1396 +
1397 + t.end();
1398 +});
1399 +
1400 +test('should error when passed invalid occurence value', function(t) {
1401 + var expressions = [
1402 + '0 0 0 ? * 1#',
1403 + '0 0 0 ? * 1#0',
1404 + '0 0 0 ? * 4#6',
1405 + '0 0 0 ? * 0##4',
1406 + ];
1407 + expressions.forEach(function(expression) {
1408 + t.throws(function() {
1409 + CronExpression.parse(expression);
1410 + }, new Error('Constraint error, invalid dayOfWeek occurrence number (#)'), expression);
1411 + });
1412 +
1413 + t.end();
1414 +});
1415 +
1416 +// The Quartz documentation says that if the # character is used then no other expression can be used in the dayOfWeek term: http://www.quartz-scheduler.org/api/2.3.0/index.html
1417 +test('cannot combine `-` range and # occurrence special characters', function(t) {
1418 + var expression = '0 0 0 ? * 2-4#2';
1419 + t.throws(function() {
1420 + CronExpression.parse(expression);
1421 + }, new Error('Constraint error, invalid dayOfWeek `#` and `-` special characters are incompatible'));
1422 +
1423 + t.end();
1424 +});
1425 +
1426 +test('cannot combine `/` repeat interval and # occurrence special characters', function(t) {
1427 + var expression = '0 0 0 ? * 1/2#3';
1428 + t.throws(function() {
1429 + CronExpression.parse(expression);
1430 + }, new Error('Constraint error, invalid dayOfWeek `#` and `/` special characters are incompatible'));
1431 +
1432 + t.end();
1433 +});
1434 +
1435 +test('cannot combine `,` list and # occurrence special characters', function(t) {
1436 + var expression = '0 0 0 ? * 0,6#4';
1437 + t.throws(function() {
1438 + CronExpression.parse(expression);
1439 + }, new Error('Constraint error, invalid dayOfWeek `#` and `,` special characters are incompatible'));
1440 +
1441 + t.end();
1442 +});
1443 +
1 +'use strict';
2 +
3 +var test = require('tap').test;
4 +var compactField = require('../lib/field_compactor');
5 +
6 +test('compact field - empty array', function(t) {
7 + try {
8 + var result = compactField([]);
9 + t.same(result, []);
10 + } catch (err) {
11 + t.error(err, 'compact field error');
12 + }
13 + t.end();
14 +});
15 +
16 +test('compact field - single element array', function(t) {
17 + try {
18 + var result = compactField([1]);
19 + t.same(result, [{
20 + start: 1,
21 + count: 1
22 + }]);
23 + } catch (err) {
24 + t.error(err, 'compact field error');
25 + }
26 + t.end();
27 +});
28 +
29 +test('compact field - 2 elements array', function(t) {
30 + try {
31 + var result = compactField([1, 2]);
32 + t.same(result, [
33 + {
34 + start: 1,
35 + count: 1
36 + },
37 + {
38 + start: 2,
39 + count: 1
40 + }
41 + ]);
42 + } catch (err) {
43 + t.error(err, 'compact field error');
44 + }
45 + t.end();
46 +});
47 +
48 +test('compact field - 2 elements array big step', function(t) {
49 + try {
50 + var result = compactField([1, 5]);
51 + t.same(result, [
52 + {
53 + start: 1,
54 + count: 1
55 + },
56 + {
57 + start: 5,
58 + count: 1
59 + }
60 + ]);
61 + } catch (err) {
62 + t.error(err, 'compact field error');
63 + }
64 + t.end();
65 +});
66 +
67 +test('compact field - 3 elements array 1 step', function(t) {
68 + try {
69 + var result = compactField([1, 2, 3]);
70 + t.same(result, [
71 + {
72 + start: 1,
73 + end: 3,
74 + count: 3,
75 + step: 1
76 + }
77 + ]);
78 + } catch (err) {
79 + t.error(err, 'compact field error');
80 + }
81 + t.end();
82 +});
83 +
84 +test('compact field - 3 elements array 1 step, dangling extra at end', function(t) {
85 + try {
86 + var result = compactField([1, 2, 3, 5]);
87 + t.same(result, [
88 + {
89 + start: 1,
90 + end: 3,
91 + count: 3,
92 + step: 1
93 + },
94 + {
95 + start: 5,
96 + count: 1
97 + }
98 + ]);
99 + } catch (err) {
100 + t.error(err, 'compact field error');
101 + }
102 + t.end();
103 +});
104 +
105 +test('compact field - 3 elements array 1 step, dangling extra at end and beginning', function(t) {
106 + try {
107 + var result = compactField([1, 4, 5, 6, 9]);
108 + t.same(result, [
109 + {
110 + start: 1,
111 + count: 1
112 + },
113 + {
114 + start: 4,
115 + end: 6,
116 + count: 3,
117 + step: 1
118 + },
119 + {
120 + start: 9,
121 + count: 1
122 + }
123 + ]);
124 + } catch (err) {
125 + t.error(err, 'compact field error');
126 + }
127 + t.end();
128 +});
129 +
130 +test('compact field - 2 ranges with dangling in the middle', function(t) {
131 + try {
132 + var result = compactField([1, 2, 3, 6, 9, 11, 13]);
133 + t.same(result, [
134 + {
135 + start: 1,
136 + end: 3,
137 + count: 3,
138 + step: 1
139 + },
140 + {
141 + start: 6,
142 + count: 1
143 + },
144 + {
145 + start: 9,
146 + end: 13,
147 + count: 3,
148 + step: 2
149 + }
150 + ]);
151 + } catch (err) {
152 + t.error(err, 'compact field error');
153 + }
154 + t.end();
155 +});
156 +
157 +test('compact field - with chars', function(t) {
158 + try {
159 + var result = compactField(['L', 'W']);
160 + t.same(result, [
161 + {
162 + start: 'L',
163 + count: 1
164 + },
165 + {
166 + start: 'W',
167 + count: 1
168 + }
169 + ]);
170 + } catch (err) {
171 + t.error(err, 'compact field error');
172 + }
173 + t.end();
174 +});
175 +
176 +test('compact field - with chars and range', function(t) {
177 + try {
178 + var result = compactField([1, 'L', 'W']);
179 + t.same(result, [
180 + {
181 + start: 1,
182 + count: 1,
183 + },
184 + {
185 + start: 'L',
186 + count: 1
187 + },
188 + {
189 + start: 'W',
190 + count: 1
191 + }
192 + ]);
193 + } catch (err) {
194 + t.error(err, 'compact field error');
195 + }
196 + t.end();
197 +});
198 +
199 +test('compact field - with chars and range (v2)', function(t) {
200 + try {
201 + var result = compactField([1, 2, 'L', 'W']);
202 + t.same(result, [
203 + {
204 + start: 1,
205 + count: 1,
206 + },
207 + {
208 + start: 2,
209 + count: 1,
210 + },
211 + {
212 + start: 'L',
213 + count: 1
214 + },
215 + {
216 + start: 'W',
217 + count: 1
218 + }
219 + ]);
220 + } catch (err) {
221 + t.error(err, 'compact field error');
222 + }
223 + t.end();
224 +});
225 +
226 +
227 +test('compact field - with chars and range (v3)', function(t) {
228 + try {
229 + var result = compactField([1, 2, 3, 'L', 'W']);
230 + t.same(result, [
231 + {
232 + start: 1,
233 + end: 3,
234 + count: 3,
235 + step: 1
236 + },
237 + {
238 + start: 'L',
239 + count: 1
240 + },
241 + {
242 + start: 'W',
243 + count: 1
244 + }
245 + ]);
246 + } catch (err) {
247 + t.error(err, 'compact field error');
248 + }
249 + t.end();
250 +});
1 +'use strict';
2 +
3 +var test = require('tap').test;
4 +var stringifyField = require('../lib/field_stringify');
5 +
6 +test('stringify astrix', function (t) {
7 + try {
8 + var str = stringifyField([1, 2, 3, 4], 1, 4);
9 + t.equal(str, '*');
10 + } catch (err) {
11 + t.error(err, 'stringify field error');
12 + }
13 + t.end();
14 +});
15 +
16 +test('stringify astrix step', function (t) {
17 + try {
18 + var str = stringifyField([0, 2, 4, 6], 0, 7);
19 + t.equal(str, '*/2');
20 + } catch (err) {
21 + t.error(err, 'stringify field error');
22 + }
23 + t.end();
24 +});
25 +
26 +test('stringify single value', function (t) {
27 + try {
28 + var str = stringifyField([2], 0, 7);
29 + t.equal(str, '2');
30 + } catch (err) {
31 + t.error(err, 'stringify field error');
32 + }
33 + t.end();
34 +});
35 +
36 +test('stringify multiple single values', function (t) {
37 + try {
38 + var str = stringifyField([2, 5, 9], 0, 7);
39 + t.equal(str, '2,5,9');
40 + } catch (err) {
41 + t.error(err, 'stringify field error');
42 + }
43 + t.end();
44 +});
45 +
46 +test('stringify range', function (t) {
47 + try {
48 + var str = stringifyField([2, 3, 4], 0, 7);
49 + t.equal(str, '2-4');
50 + } catch (err) {
51 + t.error(err, 'stringify field error');
52 + }
53 + t.end();
54 +});
55 +
56 +test('stringify range step', function (t) {
57 + try {
58 + var str = stringifyField([2, 4, 6], 0, 8);
59 + t.equal(str, '2-6/2');
60 + } catch (err) {
61 + t.error(err, 'stringify field error');
62 + }
63 + t.end();
64 +});
65 +
66 +test('stringify semi range step', function (t) {
67 + try {
68 + var str = stringifyField([4, 6, 8], 0, 9);
69 + t.equal(str, '4/2');
70 + } catch (err) {
71 + t.error(err, 'stringify field error');
72 + }
73 + t.end();
74 +});
75 +
76 +test('stringify multi types', function (t) {
77 + try {
78 + var str = stringifyField([1, 2, 4, 5, 6, 7, 8, 9, 10, 20, 25, 30, 35, 57], 0, 59);
79 + t.equal(str, '1,2,4-10,20-35/5,57');
80 + } catch (err) {
81 + t.error(err, 'stringify field error');
82 + }
83 + t.end();
84 +});
1 +var test = require('tap').test;
2 +var CronExpression = require('../lib/expression');
3 +
4 +test('Fields are exposed', function(t){
5 + try {
6 + var interval = CronExpression.parse('0 1 2 3 * 1-3,5');
7 + t.ok(interval, 'Interval parsed');
8 +
9 + CronExpression.map.forEach(function(field) {
10 + interval.fields[field] = [];
11 + t.throws(function() {
12 + interval.fields[field].push(-1);
13 + }, /Cannot add property .*?, object is not extensible/, field + ' is frozen');
14 + delete interval.fields[field];
15 + });
16 + interval.fields.dummy = [];
17 + t.same(interval.fields.dummy, undefined, 'Fields is frozen');
18 +
19 + t.same(interval.fields.second, [0], 'Second matches');
20 + t.same(interval.fields.minute, [1], 'Minute matches');
21 + t.same(interval.fields.hour, [2], 'Hour matches');
22 + t.same(interval.fields.dayOfMonth, [3], 'Day of month matches');
23 + t.same(interval.fields.month, [1,2,3,4,5,6,7,8,9,10,11,12], 'Month matches');
24 + t.same(interval.fields.dayOfWeek, [1,2,3,5], 'Day of week matches');
25 +
26 + } catch (err) {
27 + t.error(err, 'Interval parse error');
28 + }
29 +
30 + t.end();
31 +});
32 +
1 +var util = require('util');
2 +var sinon = require('sinon');
3 +var test = require('tap').test;
4 +var CronExpression = require('../lib/expression');
5 +
6 +test('increment_on_first_itereation', function(t) {
7 + try {
8 + var clock = sinon.useFakeTimers();
9 + var fake_now = new Date('Tue Feb 21 2017 16:45:00');
10 + clock.tick(fake_now.getTime());
11 + var interval = CronExpression.parse('* * * * *');
12 + t.ok(interval, 'Interval parsed');
13 + var next = interval.next();
14 + t.ok(next, 'Found next scheduled interval');
15 + // Make sure next has incremented in 1 minute
16 + t.equal(fake_now.getTime() + 60000, next.getTime());
17 + clock.restore();
18 + t.end();
19 + } catch (err) {
20 + t.error(err, 'Interval parse error');
21 + }
22 +});
1 +import {expectAssignable, expectError, expectNotAssignable, expectType} from 'tsd';
2 +import {
3 + CronDate,
4 + CronExpression,
5 + CronFields, DateType,
6 + parseExpression,
7 + parseFile, ParserOptions,
8 + parseString,
9 + fieldsToExpression,
10 + StringResult
11 +} from '../types/ts3';
12 +
13 +const interval = parseExpression('0 1 2 3 * 1-3,5');
14 +const intervalIterator = parseExpression('0 1 2 3 * 1-3,5', {iterator: true});
15 +
16 +expectType<readonly number[]>(interval.fields.second);
17 +expectType<readonly number[]>(interval.fields.minute);
18 +expectType<readonly number[]>(interval.fields.hour);
19 +expectType<readonly (number | 'L')[]>(interval.fields.dayOfMonth);
20 +expectType<readonly number[]>(interval.fields.month);
21 +expectType<readonly number[]>(interval.fields.dayOfWeek);
22 +
23 +expectError(interval.fields = interval.fields);
24 +
25 +expectError(interval.fields.second = []);
26 +expectError(interval.fields.second.push(1));
27 +
28 +expectError(interval.fields.minute = []);
29 +expectError(interval.fields.minute.push(1));
30 +
31 +expectError(interval.fields.hour = []);
32 +expectError(interval.fields.hour.push(1));
33 +
34 +expectError(interval.fields.dayOfMonth = []);
35 +expectError(interval.fields.dayOfMonth.push(1));
36 +
37 +expectError(interval.fields.month = []);
38 +expectError(interval.fields.month.push(1));
39 +
40 +expectError(interval.fields.dayOfWeek = []);
41 +expectError(interval.fields.dayOfWeek.push(1));
42 +
43 +expectAssignable<typeof interval.fields.second[0]>(0);
44 +expectAssignable<typeof interval.fields.second[0]>(59);
45 +
46 +expectAssignable<typeof interval.fields.minute[0]>(0);
47 +expectAssignable<typeof interval.fields.minute[0]>(59);
48 +
49 +expectAssignable<typeof interval.fields.hour[0]>(0);
50 +expectAssignable<typeof interval.fields.hour[0]>(23);
51 +
52 +expectAssignable<typeof interval.fields.dayOfMonth[0]>(1);
53 +expectAssignable<typeof interval.fields.dayOfMonth[0]>(31);
54 +expectAssignable<typeof interval.fields.dayOfMonth[0]>('L');
55 +
56 +expectAssignable<typeof interval.fields.month[0]>(1);
57 +expectAssignable<typeof interval.fields.month[0]>(12);
58 +
59 +expectAssignable<typeof interval.fields.dayOfWeek[0]>(0);
60 +expectAssignable<typeof interval.fields.dayOfWeek[0]>(7);
61 +
62 +const parseOptions: ParserOptions<true> = {
63 + currentDate: 'f',
64 + startDate: 4,
65 + endDate: new Date(),
66 + iterator: true,
67 + utc: true,
68 + tz: 'f',
69 + nthDayOfWeek: 5,
70 +}
71 +expectAssignable<{
72 + currentDate?: string | number | Date
73 + startDate?: string | number | Date
74 + endDate?: string | number | Date
75 + iterator?: boolean
76 + utc?: boolean
77 + tz?: string
78 + nthDayOfWeek?: number
79 +}>(parseOptions)
80 +
81 +expectType<CronExpression>(parseExpression('0 1 2 3 * 1-3,5'))
82 +expectType<CronExpression<true>>(parseExpression('0 1 2 3 * 1-3,5', parseOptions))
83 +
84 +const fields: CronFields = {
85 + second: [1, 1],
86 + minute: [1],
87 + hour: [1],
88 + dayOfMonth: [1],
89 + month: [1],
90 + dayOfWeek: [1],
91 +}
92 +
93 +expectType<CronExpression>(fieldsToExpression(fields))
94 +expectType<CronExpression<true>>(fieldsToExpression(fields, parseOptions))
95 +
96 +expectType<string>(fieldsToExpression(fields).stringify())
97 +expectType<string>(fieldsToExpression(fields, parseOptions).stringify())
98 +expectType<string>(fieldsToExpression(fields, parseOptions).stringify(true))
99 +
100 +expectType<void>(parseFile('path', (err: any, data: StringResult) => console.log(data)))
101 +
102 +expectType<StringResult>(parseString('path'))
103 +
104 +const stringResult = parseString('path');
105 +expectType<{
106 + variables: Record<string, string>,
107 + expressions: CronExpression[],
108 + errors: Record<string, any>,
109 +}>(stringResult)
110 +
111 +expectType<CronFields>(interval.fields)
112 +expectType<CronDate>(interval.next())
113 +expectType<CronDate>(interval.prev())
114 +expectType<boolean>(interval.hasNext())
115 +expectType<boolean>(interval.hasPrev())
116 +expectType<string>(interval.stringify())
117 +expectType<string>(interval.stringify(true))
118 +expectType<void>(interval.reset())
119 +expectType<void>(interval.reset("Sdf"))
120 +expectType<void>(interval.reset(5))
121 +expectType<void>(interval.reset(new Date()))
122 +expectType<CronDate[]>(interval.iterate(5))
123 +expectType<CronDate[]>(interval.iterate(5, (item: CronDate, i: number) => {}))
124 +
125 +expectAssignable<DateType>(new Date())
126 +expectAssignable<DateType>(5)
127 +expectAssignable<DateType>("SDf")
128 +
129 +
130 +expectType<IteratorResult<CronDate, CronDate>>(intervalIterator.next())
131 +expectType<IteratorResult<CronDate, CronDate>>(intervalIterator.prev())
132 +expectType<IteratorResult<CronDate, CronDate>[]>(intervalIterator.iterate(5))
133 +expectType<IteratorResult<CronDate, CronDate>[]>(intervalIterator.iterate(5, (item: IteratorResult<CronDate, CronDate>, i: number) => {}))
1 +import {expectAssignable, expectError, expectNotAssignable, expectType} from 'tsd';
2 +import {
3 + CronDate,
4 + CronExpression,
5 + CronFields, DateType,
6 + parseExpression,
7 + parseFile, ParserOptions,
8 + parseString,
9 + fieldsToExpression,
10 + StringResult
11 +} from '../index';
12 +
13 +const interval = parseExpression('0 1 2 3 * 1-3,5');
14 +const intervalIterator = parseExpression('0 1 2 3 * 1-3,5', {iterator: true});
15 +
16 +expectError(interval.fields = interval.fields);
17 +
18 +expectError(interval.fields.second = []);
19 +expectError(interval.fields.second.push(1));
20 +
21 +expectError(interval.fields.minute = []);
22 +expectError(interval.fields.minute.push(1));
23 +
24 +expectError(interval.fields.hour = []);
25 +expectError(interval.fields.hour.push(1));
26 +
27 +expectError(interval.fields.dayOfMonth = []);
28 +expectError(interval.fields.dayOfMonth.push(1));
29 +
30 +expectError(interval.fields.month = []);
31 +expectError(interval.fields.month.push(1));
32 +
33 +expectError(interval.fields.dayOfWeek = []);
34 +expectError(interval.fields.dayOfWeek.push(1));
35 +
36 +expectAssignable<typeof interval.fields.second[0]>(0);
37 +expectAssignable<typeof interval.fields.second[0]>(59);
38 +expectNotAssignable<typeof interval.fields.second[0]>(-1);
39 +expectNotAssignable<typeof interval.fields.second[0]>(60);
40 +
41 +expectAssignable<typeof interval.fields.minute[0]>(0);
42 +expectAssignable<typeof interval.fields.minute[0]>(59);
43 +expectNotAssignable<typeof interval.fields.minute[0]>(-1);
44 +expectNotAssignable<typeof interval.fields.minute[0]>(60);
45 +
46 +expectAssignable<typeof interval.fields.hour[0]>(0);
47 +expectAssignable<typeof interval.fields.hour[0]>(23);
48 +expectNotAssignable<typeof interval.fields.hour[0]>(-1);
49 +expectNotAssignable<typeof interval.fields.hour[0]>(24);
50 +
51 +expectAssignable<typeof interval.fields.dayOfMonth[0]>(1);
52 +expectAssignable<typeof interval.fields.dayOfMonth[0]>(31);
53 +expectAssignable<typeof interval.fields.dayOfMonth[0]>('L');
54 +expectNotAssignable<typeof interval.fields.dayOfMonth[0]>(0);
55 +expectNotAssignable<typeof interval.fields.dayOfMonth[0]>(32);
56 +
57 +expectAssignable<typeof interval.fields.month[0]>(1);
58 +expectAssignable<typeof interval.fields.month[0]>(12);
59 +expectNotAssignable<typeof interval.fields.month[0]>(0);
60 +expectNotAssignable<typeof interval.fields.month[0]>(13);
61 +
62 +expectAssignable<typeof interval.fields.dayOfWeek[0]>(0);
63 +expectAssignable<typeof interval.fields.dayOfWeek[0]>(7);
64 +expectNotAssignable<typeof interval.fields.dayOfWeek[0]>(-1);
65 +expectNotAssignable<typeof interval.fields.dayOfWeek[0]>(8);
66 +
67 +const parseOptions: ParserOptions<true> = {
68 + currentDate: 'f',
69 + startDate: 4,
70 + endDate: new Date(),
71 + iterator: true,
72 + utc: true,
73 + tz: 'f',
74 + nthDayOfWeek: 5,
75 +}
76 +expectAssignable<{
77 + currentDate?: string | number | Date
78 + startDate?: string | number | Date
79 + endDate?: string | number | Date
80 + iterator?: boolean
81 + utc?: boolean
82 + tz?: string
83 + nthDayOfWeek?: number
84 +}>(parseOptions)
85 +
86 +expectType<CronExpression>(parseExpression('0 1 2 3 * 1-3,5'))
87 +expectType<CronExpression<true>>(parseExpression('0 1 2 3 * 1-3,5', parseOptions))
88 +
89 +const fields: CronFields = {
90 + second: [1, 1],
91 + minute: [1],
92 + hour: [1],
93 + dayOfMonth: [1],
94 + month: [1],
95 + dayOfWeek: [1],
96 +}
97 +
98 +expectType<CronExpression>(fieldsToExpression(fields))
99 +expectType<CronExpression<true>>(fieldsToExpression(fields, parseOptions))
100 +
101 +expectType<string>(fieldsToExpression(fields).stringify())
102 +expectType<string>(fieldsToExpression(fields, parseOptions).stringify())
103 +expectType<string>(fieldsToExpression(fields, parseOptions).stringify(true))
104 +
105 +expectType<void>(parseFile('path', (err: any, data: StringResult) => console.log(data)))
106 +
107 +expectType<StringResult>(parseString('path'))
108 +
109 +const stringResult = parseString('path');
110 +expectType<{
111 + variables: Record<string, string>,
112 + expressions: CronExpression[],
113 + errors: Record<string, any>,
114 +}>(stringResult)
115 +
116 +expectType<CronFields>(interval.fields)
117 +expectType<CronDate>(interval.next())
118 +expectType<CronDate>(interval.prev())
119 +expectType<boolean>(interval.hasNext())
120 +expectType<boolean>(interval.hasPrev())
121 +expectType<string>(interval.stringify())
122 +expectType<string>(interval.stringify(true))
123 +expectType<void>(interval.reset())
124 +expectType<void>(interval.reset("Sdf"))
125 +expectType<void>(interval.reset(5))
126 +expectType<void>(interval.reset(new Date()))
127 +expectType<CronDate[]>(interval.iterate(5))
128 +expectType<CronDate[]>(interval.iterate(5, (item: CronDate, i: number) => {}))
129 +
130 +expectAssignable<DateType>(new Date())
131 +expectAssignable<DateType>(5)
132 +expectAssignable<DateType>("SDf")
133 +
134 +
135 +expectType<IteratorResult<CronDate, CronDate>>(intervalIterator.next())
136 +expectType<IteratorResult<CronDate, CronDate>>(intervalIterator.prev())
137 +expectType<IteratorResult<CronDate, CronDate>[]>(intervalIterator.iterate(5))
138 +expectType<IteratorResult<CronDate, CronDate>[]>(intervalIterator.iterate(5, (item: IteratorResult<CronDate, CronDate>, i: number) => {}))
1 +var util = require('util');
2 +var test = require('tap').test;
3 +var expression = require('../lib/expression');
4 +
5 +test('leap year', function(t) {
6 + try {
7 + var interval = expression.parse('0 0 29 2 *');
8 + var i;
9 + var d;
10 + for (i = 0; i < 20; ++i) {
11 + d = interval.next();
12 + }
13 + t.end();
14 + } catch (err) {
15 + t.error(err, 'Interval parse error');
16 + }
17 +});
1 +var test = require('tap').test;
2 +var CronParser = require('../lib/parser');
3 +
4 +// Globals
5 +
6 +test('load crontab file', function(t) {
7 + CronParser.parseFile(__dirname + '/crontab.example', function(err, result) {
8 + t.error(err, 'File read error');
9 + t.ok(result, 'Crontab parsed parsed');
10 +
11 + t.equal(Object.keys(result.variables).length, 2, 'variables length matches');
12 + t.equal(Object.keys(result.errors).length, 0, 'errors length matches');
13 + t.equal(result.expressions.length, 3, 'expressions length matches');
14 +
15 + // Parse expressions
16 + var next = null;
17 +
18 + t.equal(result.expressions[0].hasNext(), true);
19 + next = result.expressions[0].next();
20 + t.ok(next, 'first date');
21 +
22 + next = result.expressions[1].next();
23 + t.ok(next, 'second date');
24 +
25 + next = result.expressions[2].next();
26 + t.ok(next, 'third date');
27 +
28 + t.end();
29 + });
30 +});
31 +
32 +test('no next date', function(t) {
33 + var options = {
34 + currentDate: new Date(2014, 0, 1),
35 + endDate: new Date(2014, 0, 1)
36 + };
37 +
38 + try {
39 + var interval = CronParser.parseExpression('* * 2 * *', options);
40 + t.equal(interval.hasNext(), false);
41 + } catch (err) {
42 + t.error(err, 'Parse read error');
43 + }
44 +
45 + t.end();
46 +});
1 +var luxon = require('luxon');
2 +var test = require('tap').test;
3 +var CronDate = require('../lib/date');
4 +
5 +test('parse cron date formats with local timezone', (t) => {
6 + // Some tests need the local offset to be compatible without invoking timezone management.
7 + // Local offset is dependent on what the system being tested on is
8 + var offset = new Date().getTimezoneOffset();
9 + var offsetHours = Math.abs(Math.floor(offset/60));
10 + var offsetMinutes = offset % 60;
11 + var offsetSign = offset < 0 ? '-' : '+';
12 +
13 + var expectedTime = new Date(2021, 0, 4, 10, 0, 0).toString();
14 +
15 + test('undefined date', (t) => {
16 + const realDate = new Date();
17 + var d = new CronDate();
18 +
19 + t.equal(d.toDate().toString(), realDate.toString());
20 +
21 + t.end();
22 + });
23 +
24 + test('JS Date', (t) => {
25 + var d = new CronDate(new Date(2021, 0, 4, 10, 0, 0));
26 + t.equal(d.toDate().toString(), expectedTime);
27 +
28 + t.end();
29 + });
30 +
31 + test('ISO 8601', (t) => {
32 + var d = new CronDate('2021-01-04T10:00:00');
33 + t.equal(d.toDate().toString(), expectedTime);
34 +
35 + t.end();
36 + });
37 +
38 + test('ISO 8601 date', (t) => {
39 +
40 + var d = new CronDate('2021-01-04');
41 + var expectedTime = new Date(2021, 0, 4, 0, 0, 0).toString();
42 +
43 + t.equal(d.toDate().toString(), expectedTime);
44 +
45 + t.end();
46 + });
47 +
48 + test('RFC2822', (t) => {
49 + var offsetString = offsetSign + String(offsetHours).padStart(2, 0) + String(offsetMinutes).padStart(2, 0);
50 +
51 + var d = new CronDate('Mon, 4 Jan 2021 10:00:00 ' + offsetString);
52 + t.equal(d.toDate().toString(), expectedTime);
53 +
54 + t.end();
55 + });
56 +
57 + test('RFC2822-like without timezone offset', (t) => {
58 + var d = new CronDate('Mon, 4 Jan 2021 10:00:00');
59 + t.equal(d.toDate().toString(), expectedTime);
60 +
61 + t.end();
62 + });
63 +
64 + test('SQL', (t) => {
65 + var d = new CronDate('2021-01-04 10:00:00');
66 + t.equal(d.toDate().toString(), expectedTime);
67 +
68 + t.end();
69 + });
70 +
71 + test('milliseconds', (t) => {
72 + var d = new CronDate(new Date('2021-01-04 10:00:00').valueOf());
73 + t.equal(d.toDate().toString(), expectedTime);
74 +
75 + t.end();
76 + });
77 +
78 + test('CronDate', (t) => {
79 + var date = new CronDate('Mon, 4 Jan 2021 10:00:00');
80 + var d = new CronDate(date);
81 + t.equal(d.toDate().toString(), expectedTime);
82 +
83 + t.end();
84 + });
85 +
86 + test('invalid', (t) => {
87 + t.throws(() => {
88 + var d = new CronDate('2021-01-4 10:00:00');
89 + });
90 +
91 + t.end();
92 + });
93 +
94 + t.end();
95 +});
96 +
97 +test('parse cron date formats with another timezone', (t) => {
98 + test('ISO 8601 without offset', (t) => {
99 + // implies format already in timezone
100 + var d = new CronDate('2021-01-04T10:00:00', 'Europe/Athens');
101 + t.equal(d.toISOString(), '2021-01-04T08:00:00.000Z');
102 +
103 + t.end();
104 + });
105 +
106 + test('ISO 8601 with non-local offset', (t) => {
107 + var d = new CronDate('2021-01-04T10:00:00+01:00', 'Europe/Athens');
108 + t.equal(d.toISOString(), '2021-01-04T09:00:00.000Z');
109 +
110 + t.end();
111 + });
112 +
113 + test('RFC2822 with non-local offset', (t) => {
114 + var d = new CronDate('Mon, 4 Jan 2021 10:00:00 +0100', 'Europe/Athens');
115 + t.equal(d.toISOString(), '2021-01-04T09:00:00.000Z');
116 +
117 + t.end();
118 + });
119 +
120 + test('milliseconds', (t) => {
121 + var date = luxon.DateTime.fromISO('2021-01-04T11:00:00.000+02:00').valueOf();
122 + var d = new CronDate(date, 'Europe/Athens');
123 + t.equal(d.toISOString(), '2021-01-04T09:00:00.000Z');
124 +
125 + t.end();
126 + });
127 +
128 + test('CronDate with same timezone', (t) => {
129 + var date = new CronDate('Mon, 4 Jan 2021 10:00:00', 'Europe/Athens');
130 + var d = new CronDate(date);
131 + t.equal(d.toISOString(), '2021-01-04T08:00:00.000Z');
132 +
133 + t.end();
134 + });
135 +
136 + test('CronDate with different timezone', (t) => {
137 + var date = new CronDate('Mon, 4 Jan 2021 10:00:00', 'America/New_York');
138 + var d = new CronDate(date, 'Europe/Athens');
139 + t.equal(d.toISOString(), '2021-01-04T15:00:00.000Z');
140 +
141 + t.end();
142 + });
143 +
144 + t.end('crondate input should');
145 +});
1 +var util = require('util');
2 +var test = require('tap').test;
3 +var CronParser = require('../lib/parser');
4 +
5 +test('parse cron with last day in a month', function(t) {
6 + var options = {
7 + currentDate: new Date(2014, 0, 1),
8 + endDate: new Date(2014, 10, 1)
9 + };
10 +
11 + try {
12 + var interval = CronParser.parseExpression('0 0 L * *', options);
13 + t.equal(interval.hasNext(), true);
14 +
15 + for (i = 0; i < 10; ++i) {
16 + var next = interval.next();
17 + t.ok(next, 'has a date');
18 + }
19 +
20 + } catch (err) {
21 + t.error(err, 'Parse read error');
22 + }
23 +
24 + t.end();
25 +});
26 +
27 +test('parse cron with last day in feb', function(t) {
28 + var options = {
29 + currentDate: new Date(2016, 0, 1),
30 + endDate: new Date(2016, 10, 1)
31 + };
32 +
33 + try {
34 + var interval = CronParser.parseExpression('0 0 6-20/2,L 2 *', options);
35 + t.equal(interval.hasNext(), true);
36 + var next = null;
37 + var items = 9;
38 + var i = 0;
39 + while(interval.hasNext()) {
40 + next = interval.next();
41 + i += 1;
42 + t.ok(next, 'has a date');
43 + }
44 + //leap year
45 + t.equal(next.getDate(), 29);
46 + t.equal(i, items);
47 +
48 + } catch (err) {
49 + t.error(err, 'Parse read error');
50 + }
51 +
52 + t.end();
53 +});
54 +
55 +test('parse cron with last day in feb', function(t) {
56 + var options = {
57 + currentDate: new Date(2014, 0, 1),
58 + endDate: new Date(2014, 10, 1)
59 + };
60 +
61 + try {
62 + var interval = CronParser.parseExpression('0 0 1,3,6-10,L 2 *', options);
63 + t.equal(interval.hasNext(), true);
64 + var next = null;
65 + while(interval.hasNext()) {
66 + next = interval.next();
67 + t.ok(next, 'has a date');
68 + }
69 + //common year
70 + t.equal(next.getDate(), 28);
71 +
72 + } catch (err) {
73 + t.error(err, 'Parse read error');
74 + }
75 +
76 + t.end();
77 +});
78 +
1 +var test = require('tap').test;
2 +var CronExpression = require('../lib/expression');
3 +
4 +test('prev should match correctly (issue #98) when milliseconds are greater than 0', function(t) {
5 + var options = {
6 + currentDate: new Date('2017-06-13T18:21:25.002Z')
7 + };
8 +
9 + var interval = CronExpression.parse('*/5 * * * * *', options);
10 + var prev = interval.prev();
11 + t.equal(prev.getSeconds(), 25);
12 +
13 + t.end();
14 +});
15 +
16 +test('prev should match correctly (issue #98) when milliseconds are equal to 0', function(t) {
17 + var interval = CronExpression.parse('59 59 23 * * *',{
18 + currentDate : new Date('2012-12-26 14:38:53')
19 + });
20 +
21 + [25, 24, 23, 22].forEach(function(date) {
22 + var prev = interval.prev();
23 + t.equal(prev.getFullYear(), 2012);
24 + t.equal(prev.getMonth(), 11);
25 + t.equal(prev.getDate(), date);
26 + t.equal(prev.getHours(), 23);
27 + t.equal(prev.getMinutes(), 59);
28 + t.equal(prev.getSeconds(), 59);
29 + });
30 +
31 + t.end();
32 +});
1 +'use strict';
2 +
3 +var test = require('tap').test;
4 +var CronParser = require('../lib/parser');
5 +
6 +test('stringify cron expression all stars no seconds', function (t) {
7 +
8 + try {
9 + var expected = '0 * * * * *';
10 + var interval = CronParser.parseExpression('* * * * *', {});
11 + var str = interval.stringify(true);
12 + t.equal(str, expected);
13 + str = CronParser.fieldsToExpression(interval.fields).stringify(true);
14 + t.equal(str, expected);
15 +
16 + } catch (err) {
17 + t.error(err, 'Parse read error');
18 + }
19 +
20 + t.end();
21 +});
22 +
23 +test('stringify cron expression all stars no seconds (discard seconds)', function (t) {
24 +
25 + try {
26 + var expected = '* * * * *';
27 + var interval = CronParser.parseExpression('* * * * *', {});
28 + var str = interval.stringify();
29 + t.equal(str, expected);
30 + str = CronParser.fieldsToExpression(interval.fields).stringify();
31 + t.equal(str, expected);
32 +
33 + } catch (err) {
34 + t.error(err, 'Parse read error');
35 + }
36 +
37 + t.end();
38 +});
39 +
40 +test('stringify cron expression all stars with seconds', function (t) {
41 +
42 + try {
43 + var expected = '* * * * * *';
44 + var interval = CronParser.parseExpression('* * * * * *', {});
45 + var str = interval.stringify(true);
46 + t.equal(str, expected);
47 + str = CronParser.fieldsToExpression(interval.fields).stringify(true);
48 + t.equal(str, expected);
49 +
50 + } catch (err) {
51 + t.error(err, 'Parse read error');
52 + }
53 +
54 + t.end();
55 +});
56 +
57 +test('stringify cron expression all stars with seconds (discard seconds)', function (t) {
58 +
59 + try {
60 + var expected = '* * * * *';
61 + var interval = CronParser.parseExpression('* * * * * *', {});
62 + var str = interval.stringify();
63 + t.equal(str, expected);
64 + str = CronParser.fieldsToExpression(interval.fields).stringify();
65 + t.equal(str, expected);
66 +
67 + } catch (err) {
68 + t.error(err, 'Parse read error');
69 + }
70 +
71 + t.end();
72 +});
73 +
74 +test('stringify cron expression', function (t) {
75 +
76 + try {
77 + var expected = '0 1,2,4-10,20-35/5,57 * * * *';
78 + var interval = CronParser.parseExpression('1,2,4-10,20-35/5,57 * * * *', {});
79 + var str = interval.stringify(true);
80 + t.equal(str, expected);
81 + str = CronParser.fieldsToExpression(interval.fields).stringify(true);
82 + t.equal(str, expected);
83 +
84 + } catch (err) {
85 + t.error(err, 'Parse read error');
86 + }
87 +
88 + t.end();
89 +});
90 +
91 +test('stringify cron expression (discard seconds)', function (t) {
92 +
93 + try {
94 + var expected = '1,2,4-10,20-35/5,57 * * * *';
95 + var interval = CronParser.parseExpression('1,2,4-10,20-35/5,57 * * * *', {});
96 + var str = interval.stringify();
97 + t.equal(str, expected);
98 + str = CronParser.fieldsToExpression(interval.fields).stringify();
99 + t.equal(str, expected);
100 +
101 + } catch (err) {
102 + t.error(err, 'Parse read error');
103 + }
104 +
105 + t.end();
106 +});
107 +
108 +test('stringify cron expression with star range step', function (t) {
109 +
110 + try {
111 + var expected = '0 */5 */2 * * *';
112 + var interval = CronParser.parseExpression('*/5 */2 */1 * *', {});
113 + var str = interval.stringify(true);
114 + t.equal(str, expected);
115 + str = CronParser.fieldsToExpression(interval.fields).stringify(true);
116 + t.equal(str, expected);
117 +
118 + } catch (err) {
119 + t.error(err, 'Parse read error');
120 + }
121 +
122 + t.end();
123 +});
124 +
125 +test('stringify cron expression with star range step (discard seconds)', function (t) {
126 +
127 + try {
128 + var expected = '*/5 */2 * * *';
129 + var interval = CronParser.parseExpression('*/5 */2 */1 * *', {});
130 + var str = interval.stringify();
131 + t.equal(str, expected);
132 + str = CronParser.fieldsToExpression(interval.fields).stringify();
133 + t.equal(str, expected);
134 +
135 + } catch (err) {
136 + t.error(err, 'Parse read error');
137 + }
138 +
139 + t.end();
140 +});
141 +
142 +test('stringify cron expression with semi range step', function (t) {
143 +
144 + try {
145 + var expected = '0 5/5 * * * *';
146 + var interval = CronParser.parseExpression('5/5 * * * *', {});
147 + var str = interval.stringify(true);
148 + t.equal(str, expected);
149 + str = CronParser.fieldsToExpression(interval.fields).stringify(true);
150 + t.equal(str, expected);
151 +
152 + } catch (err) {
153 + t.error(err, 'Parse read error');
154 + }
155 +
156 + t.end();
157 +});
158 +
159 +test('stringify cron expression with semi range step (discard seconds)', function (t) {
160 +
161 + try {
162 + var expected = '5/5 * * * *';
163 + var interval = CronParser.parseExpression('5/5 * * * *', {});
164 + var str = interval.stringify();
165 + t.equal(str, expected);
166 + str = CronParser.fieldsToExpression(interval.fields).stringify();
167 + t.equal(str, expected);
168 +
169 + } catch (err) {
170 + t.error(err, 'Parse read error');
171 + }
172 +
173 + t.end();
174 +});
175 +
176 +test('stringify cron expression with L', function (t) {
177 +
178 + try {
179 + var expected = '0 * * 1,4-10,L * *';
180 + var interval = CronParser.parseExpression('* * 1,4-10,L * *', {});
181 + var str = interval.stringify(true);
182 + t.equal(str, expected);
183 + str = CronParser.fieldsToExpression(interval.fields).stringify(true);
184 + t.equal(str, expected);
185 +
186 + } catch (err) {
187 + t.error(err, 'Parse read error');
188 + }
189 +
190 + t.end();
191 +});
192 +
193 +test('stringify cron expression with L (discard seconds)', function (t) {
194 +
195 + try {
196 + var expected = '* * 1,4-10,L * *';
197 + var interval = CronParser.parseExpression('* * 1,4-10,L * *', {});
198 + var str = interval.stringify();
199 + t.equal(str, expected);
200 + str = CronParser.fieldsToExpression(interval.fields).stringify();
201 + t.equal(str, expected);
202 +
203 + } catch (err) {
204 + t.error(err, 'Parse read error');
205 + }
206 +
207 + t.end();
208 +});
209 +
210 +test('stringify from fields out of order', function (t) {
211 +
212 + try {
213 + var expected = '1-5 1 1 1 1 1';
214 + var str = CronParser.fieldsToExpression({
215 + second: [5,2,1,4,3],
216 + minute: [1],
217 + hour: [1],
218 + month: [1],
219 + dayOfMonth: [1],
220 + dayOfWeek: [1],
221 + }).stringify(true);
222 + t.equal(str, expected);
223 + } catch (err) {
224 + t.error(err, 'Parse read error');
225 + }
226 +
227 + t.end();
228 +});
229 +
230 +test('stringify from fields out of order (discard seconds)', function (t) {
231 +
232 + try {
233 + var expected = '1 1 1 1 1';
234 + var str = CronParser.fieldsToExpression({
235 + second: [5,2,1,4,3],
236 + minute: [1],
237 + hour: [1],
238 + month: [1],
239 + dayOfMonth: [1],
240 + dayOfWeek: [1],
241 + }).stringify();
242 + t.equal(str, expected);
243 + } catch (err) {
244 + t.error(err, 'Parse read error');
245 + }
246 +
247 + t.end();
248 +});
249 +
250 +test('validation error - missing seconds', function (t) {
251 + t.throws(function () {
252 + CronParser.fieldsToExpression({
253 + minute: [1],
254 + hour: [1],
255 + dayOfMonth: [1],
256 + month: [1],
257 + dayOfWeek: [1],
258 + });
259 + }, new Error('Validation error, Field second is missing'));
260 +
261 + t.end();
262 +});
263 +
264 +test('validation error - empty seconds', function (t) {
265 + t.throws(function () {
266 + CronParser.fieldsToExpression({
267 + second: [],
268 + minute: [1],
269 + hour: [1],
270 + dayOfMonth: [1],
271 + month: [1],
272 + dayOfWeek: [1],
273 + });
274 + }, new Error('Validation error, Field second contains no values'));
275 +
276 + t.end();
277 +});
278 +
279 +test('validation error - missing values - empty array', function (t) {
280 + t.throws(function () {
281 + CronParser.fieldsToExpression({
282 + second: [1],
283 + minute: [],
284 + hour: [1],
285 + dayOfMonth: [1],
286 + month: [1],
287 + dayOfWeek: [1],
288 + });
289 + }, new Error('Validation error, Field minute contains no values'));
290 +
291 + t.end();
292 +});
293 +
294 +test('validation error - missing values', function (t) {
295 + t.throws(function () {
296 + CronParser.fieldsToExpression({
297 + second: [1],
298 + hour: [1],
299 + dayOfMonth: [1],
300 + month: [1],
301 + dayOfWeek: [1],
302 + });
303 + }, new Error('Validation error, Field minute is missing'));
304 +
305 + t.end();
306 +});
307 +
308 +test('validation error - range error', function (t) {
309 + t.throws(function () {
310 + CronParser.fieldsToExpression({
311 + second: [-1, 1, 0],
312 + minute: [1],
313 + hour: [1],
314 + dayOfMonth: [1],
315 + month: [1],
316 + dayOfWeek: [1],
317 + });
318 + }, new Error('Constraint error, got value -1 expected range 0-59'));
319 +
320 + t.end();
321 +});
322 +
323 +test('validation error - bad chars error', function (t) {
324 + t.throws(function () {
325 + CronParser.fieldsToExpression({
326 + second: [0, 'R'],
327 + minute: [1],
328 + hour: [1],
329 + dayOfMonth: [1],
330 + month: [1],
331 + dayOfWeek: [1],
332 + });
333 + }, new Error('Constraint error, got value R expected range 0-59'));
334 +
335 + t.end();
336 +});
337 +
338 +test('validation error - duplicates', function (t) {
339 + t.throws(function () {
340 + CronParser.fieldsToExpression({
341 + second: [1, 1],
342 + minute: [1],
343 + hour: [1],
344 + dayOfMonth: [1],
345 + month: [1],
346 + dayOfWeek: [1],
347 + });
348 + }, new Error('Validation error, Field second contains duplicate values'));
349 +
350 + t.end();
351 +});
1 +var test = require('tap').test;
2 +var CronExpression = require('../lib/expression');
3 +
4 +test('It works on DST start', function(t) {
5 + try {
6 + var options = {
7 + currentDate: '2016-03-27 02:00:01',
8 + tz: 'Europe/Athens'
9 + };
10 +
11 + var interval, date;
12 +
13 + interval = CronExpression.parse('0 * * * *', options);
14 + t.ok(interval, 'Interval parsed');
15 +
16 + date = interval.next();
17 + t.equal(date.getMinutes(), 0, '0 Minutes');
18 + t.equal(date.getHours(), 4, 'Due to DST start in Athens, 3 is skipped');
19 + t.equal(date.getDate(), 27, 'on the 27th');
20 +
21 + date = interval.next();
22 + t.equal(date.getMinutes(), 0, '0 Minutes');
23 + t.equal(date.getHours(), 5, '5 AM');
24 + t.equal(date.getDate(), 27, 'on the 27th');
25 +
26 + interval = CronExpression.parse('30 2 * * *', options);
27 + t.ok(interval, 'Interval parsed');
28 +
29 + date = interval.next();
30 + t.equal(date.getMinutes(), 30, '30 Minutes');
31 + t.equal(date.getHours(), 2, '2 AM');
32 + t.equal(date.getDate(), 27, 'on the 27th');
33 +
34 + date = interval.next();
35 + t.equal(date.getMinutes(), 30, '30 Minutes');
36 + t.equal(date.getHours(), 2, '2 AM');
37 + t.equal(date.getDate(), 28, 'on the 28th');
38 +
39 + interval = CronExpression.parse('0 3 * * *', options);
40 + t.ok(interval, 'Interval parsed');
41 +
42 + date = interval.next();
43 + t.equal(date.getMinutes(), 0, '0 Minutes');
44 + t.equal(date.getHours(), 4, 'Due to DST start in Athens, 3 is skipped');
45 + t.equal(date.getDate(), 27, 'on the 27th');
46 +
47 + date = interval.next();
48 + t.equal(date.getMinutes(), 0, '0 Minutes');
49 + t.equal(date.getHours(), 3, '3 on the 28th');
50 + t.equal(date.getDate(), 28, 'on the 28th');
51 +
52 + interval = CronExpression.parse('*/20 3 * * *', options);
53 + t.ok(interval, 'Interval parsed');
54 +
55 + date = interval.next();
56 + t.equal(date.getMinutes(), 0, '0 Minutes');
57 + t.equal(date.getHours(), 4, 'Due to DST start in Athens, 3 is skipped');
58 + t.equal(date.getDate(), 27, 'on the 27th');
59 +
60 + date = interval.next();
61 + t.equal(date.getMinutes(), 20, '20 Minutes');
62 + t.equal(date.getHours(), 4, 'Due to DST start in Athens, 3 is skipped');
63 + t.equal(date.getDate(), 27, 'on the 27th');
64 +
65 + date = interval.next();
66 + t.equal(date.getMinutes(), 40, '20 Minutes');
67 + t.equal(date.getHours(), 4, 'Due to DST start in Athens, 3 is skipped');
68 + t.equal(date.getDate(), 27, 'on the 27th');
69 +
70 + date = interval.next();
71 + t.equal(date.getMinutes(), 0, '0 Minutes');
72 + t.equal(date.getHours(), 3, '3 AM');
73 + t.equal(date.getDate(), 28, 'on the 28th');
74 +
75 + options.currentDate = '2016-03-27 00:00:01';
76 +
77 + interval = CronExpression.parse('0 * 27 * *', options);
78 + t.ok(interval, 'Interval parsed');
79 +
80 + date = interval.next();
81 + t.equal(date.getMinutes(), 0, '0 Minutes');
82 + t.equal(date.getHours(), 1, '1 AM');
83 + t.equal(date.getDate(), 27, 'on the 27th');
84 +
85 + date = interval.next();
86 + t.equal(date.getMinutes(), 0, '0 Minutes');
87 + t.equal(date.getHours(), 2, '2 AM');
88 + t.equal(date.getDate(), 27, 'on the 27th');
89 +
90 + date = interval.next();
91 + t.equal(date.getMinutes(), 0, '0 Minutes');
92 + t.equal(date.getHours(), 4, '4 AM');
93 + t.equal(date.getDate(), 27, 'on the 27th');
94 +
95 + date = interval.next();
96 + t.equal(date.getMinutes(), 0, '0 Minutes');
97 + t.equal(date.getHours(), 5, '5 AM');
98 + t.equal(date.getDate(), 27, 'on the 27th');
99 +
100 + options.currentDate = '2016-03-27 00:00:01';
101 + options.endDate = '2016-03-27 03:00:01';
102 +
103 + interval = CronExpression.parse('0 * * * *', options);
104 + t.ok(interval, 'Interval parsed');
105 +
106 + date = interval.next();
107 + t.equal(date.getMinutes(), 0, '0 Minutes');
108 + t.equal(date.getHours(), 1, '1 AM');
109 + t.equal(date.getDate(), 27, 'on the 27th');
110 +
111 + date = interval.next();
112 + t.equal(date.getMinutes(), 0, '0 Minutes');
113 + t.equal(date.getHours(), 2, '2 AM');
114 + t.equal(date.getDate(), 27, 'on the 27th');
115 +
116 + date = interval.next();
117 + t.equal(date.getMinutes(), 0, '0 Minutes');
118 + t.equal(date.getHours(), 4, '4 AM');
119 + t.equal(date.getDate(), 27, 'on the 27th');
120 +
121 + // Out of the timespan range
122 + t.throws(function() {
123 + date = interval.next();
124 + });
125 + } catch (err) {
126 + t.error(err, 'Interval parse error');
127 + }
128 +
129 + t.end();
130 +});
131 +
132 +test('It works on DST end', function(t) {
133 + try {
134 + var options = {
135 + currentDate: '2016-10-30 02:00:01',
136 + tz: 'Europe/Athens'
137 + };
138 +
139 + var interval, date;
140 +
141 + interval = CronExpression.parse('0 * * * *', options);
142 + t.ok(interval, 'Interval parsed');
143 +
144 + date = interval.next();
145 + t.equal(date.getHours(), 3, '3 AM');
146 + t.equal(date.getDate(), 30, '30th');
147 +
148 + date = interval.next();
149 + t.equal(date.getHours(), 3, 'Due to DST end in Athens (4-->3)');
150 + t.equal(date.getDate(), 30, '30th');
151 +
152 + date = interval.next();
153 + t.equal(date.getHours(), 4, '4 AM');
154 + t.equal(date.getDate(), 30, '30th');
155 +
156 + interval = CronExpression.parse('0 3 * * *', options);
157 + t.ok(interval, 'Interval parsed');
158 +
159 + date = interval.next();
160 + t.equal(date.getHours(), 3, '3 AM');
161 + t.equal(date.getDate(), 30, '30th');
162 +
163 + date = interval.next();
164 + t.equal(date.getHours(), 3, '3 AM');
165 + t.equal(date.getDate(), 31, '31st');
166 +
167 + interval = CronExpression.parse('*/20 3 * * *', options);
168 + t.ok(interval, 'Interval parsed');
169 +
170 + date = interval.next();
171 + t.equal(date.getMinutes(), 0, '0');
172 + t.equal(date.getHours(), 3, '3 AM');
173 + t.equal(date.getDate(), 30, '30th');
174 +
175 + date = interval.next();
176 + t.equal(date.getMinutes(), 20, '20');
177 + t.equal(date.getHours(), 3, '3 AM');
178 + t.equal(date.getDate(), 30, '30th');
179 +
180 + date = interval.next();
181 + t.equal(date.getMinutes(), 40, '40');
182 + t.equal(date.getHours(), 3, '3 AM');
183 + t.equal(date.getDate(), 30, '30th');
184 +
185 + date = interval.next();
186 + t.equal(date.getMinutes(), 0, '0');
187 + t.equal(date.getHours(), 3, '3 AM');
188 + t.equal(date.getDate(), 31, '31st');
189 +
190 + options.currentDate = '2016-10-30 00:00:01';
191 +
192 + interval = CronExpression.parse('0 * 30 * *', options);
193 + t.ok(interval, 'Interval parsed');
194 +
195 + date = interval.next();
196 + t.equal(date.getHours(), 1, '1 AM');
197 + t.equal(date.getDate(), 30, '30th');
198 +
199 + date = interval.next();
200 + t.equal(date.getHours(), 2, '2 AM');
201 + t.equal(date.getDate(), 30, '30th');
202 +
203 + date = interval.next();
204 + t.equal(date.getHours(), 3, '3 AM');
205 + t.equal(date.getDate(), 30, '30th');
206 +
207 + date = interval.next();
208 + t.equal(date.getHours(), 3, '3 AM');
209 + t.equal(date.getDate(), 30, '30th');
210 +
211 + date = interval.next();
212 + t.equal(date.getHours(), 4, '4 AM');
213 + t.equal(date.getDate(), 30, '30th');
214 +
215 + options.currentDate = '2016-10-30 00:00:01';
216 + // specify the DST offset via ISO 8601 format, as 3am is repeated
217 + options.endDate = '2016-10-30T03:00:01+03';
218 +
219 + interval = CronExpression.parse('0 * * * *', options);
220 + t.ok(interval, 'Interval parsed');
221 +
222 + date = interval.next();
223 + t.equal(date.getHours(), 1, '1 AM');
224 + t.equal(date.getDate(), 30, '30th');
225 +
226 + date = interval.next();
227 + t.equal(date.getHours(), 2, '2 AM');
228 + t.equal(date.getDate(), 30, '30th');
229 +
230 + date = interval.next();
231 + t.equal(date.getHours(), 3, '3 AM');
232 + t.equal(date.getDate(), 30, '30th');
233 +
234 + // Out of the timespan range
235 + t.throws(function() {
236 + date = interval.next();
237 + });
238 +
239 + options.endDate = '2016-10-30 04:00:01';
240 +
241 + interval = CronExpression.parse('0 * * * *', options);
242 + t.ok(interval, 'Interval parsed');
243 +
244 + date = interval.next();
245 + t.equal(date.getHours(), 1, '1 AM');
246 + t.equal(date.getDate(), 30, '30th');
247 +
248 + date = interval.next();
249 + t.equal(date.getHours(), 2, '2 AM');
250 + t.equal(date.getDate(), 30, '30th');
251 +
252 + date = interval.next();
253 + t.equal(date.getHours(), 3, '3 AM');
254 + t.equal(date.getDate(), 30, '30th');
255 +
256 + date = interval.next();
257 + t.equal(date.getHours(), 3, '3 AM');
258 + t.equal(date.getDate(), 30, '30th');
259 +
260 + date = interval.next();
261 + t.equal(date.getHours(), 4, '4 AM');
262 + t.equal(date.getDate(), 30, '30th');
263 +
264 + // Out of the timespan range
265 + t.throws(function() {
266 + date = interval.next();
267 + });
268 +
269 + options = {
270 + currentDate : new Date('Sun Oct 29 2016 01:00:00 GMT+0200')
271 + };
272 +
273 + interval = CronExpression.parse('0 12 * * *', options);
274 + t.ok(interval, 'Interval parsed');
275 +
276 + date = interval.next();
277 + t.equal(date.getHours(), 12, '12');
278 + t.equal(date.getDate(), 29, '29th');
279 + date = interval.next();
280 + t.equal(date.getHours(), 12, '12');
281 + t.equal(date.getDate(), 30, '30th');
282 + date = interval.next();
283 + t.equal(date.getHours(), 12, '12');
284 + t.equal(date.getDate(), 31, '31st');
285 +
286 + options = {
287 + currentDate : new Date('Sun Oct 29 2016 02:59:00 GMT+0200')
288 + };
289 +
290 + interval = CronExpression.parse('0 12 * * *', options);
291 + t.ok(interval, 'Interval parsed');
292 +
293 + date = interval.next();
294 + t.equal(date.getHours(), 12, '12');
295 + t.equal(date.getDate(), 29, '29th');
296 + date = interval.next();
297 + t.equal(date.getHours(), 12, '12');
298 + t.equal(date.getDate(), 30, '30th');
299 + date = interval.next();
300 + t.equal(date.getHours(), 12, '12');
301 + t.equal(date.getDate(), 31, '31st');
302 +
303 + options = {
304 + currentDate : new Date('Sun Oct 29 2016 02:59:59 GMT+0200')
305 + };
306 +
307 + interval = CronExpression.parse('0 12 * * *', options);
308 + t.ok(interval, 'Interval parsed');
309 +
310 + date = interval.next();
311 + t.equal(date.getHours(), 12, '12');
312 + t.equal(date.getDate(), 29, '29th');
313 + date = interval.next();
314 + t.equal(date.getHours(), 12, '12');
315 + t.equal(date.getDate(), 30, '30th');
316 + date = interval.next();
317 + t.equal(date.getHours(), 12, '12');
318 + t.equal(date.getDate(), 31, '31st');
319 +
320 + options = {
321 + currentDate : new Date('Sun Oct 30 2016 01:00:00 GMT+0200')
322 + };
323 +
324 + interval = CronExpression.parse('0 12 * * *', options);
325 + t.ok(interval, 'Interval parsed');
326 +
327 + date = interval.next();
328 + t.equal(date.getHours(), 12, '12');
329 + t.equal(date.getDate(), 30, '30th');
330 + date = interval.next();
331 + t.equal(date.getHours(), 12, '12');
332 + t.equal(date.getDate(), 31, '31st');
333 +
334 + options = {
335 + currentDate : new Date('Sun Oct 30 2016 01:59:00 GMT+0200')
336 + };
337 +
338 + interval = CronExpression.parse('0 12 * * *', options);
339 + t.ok(interval, 'Interval parsed');
340 +
341 + date = interval.next();
342 + t.equal(date.getHours(), 12, '12');
343 + t.equal(date.getDate(), 30, '30th');
344 + date = interval.next();
345 + t.equal(date.getHours(), 12, '12');
346 + t.equal(date.getDate(), 31, '31st');
347 +
348 + options = {
349 + currentDate : new Date('Sun Oct 30 2016 01:59:59 GMT+0200')
350 + };
351 +
352 + interval = CronExpression.parse('0 12 * * *', options);
353 + t.ok(interval, 'Interval parsed');
354 +
355 + date = interval.next();
356 + t.equal(date.getHours(), 12, '12');
357 + t.equal(date.getDate(), 30, '30th');
358 + date = interval.next();
359 + t.equal(date.getHours(), 12, '12');
360 + t.equal(date.getDate(), 31, '31st');
361 +
362 + options = {
363 + currentDate : new Date('Sun Oct 30 2016 02:59:00 GMT+0200')
364 + };
365 +
366 + interval = CronExpression.parse('0 12 * * *', options);
367 + t.ok(interval, 'Interval parsed');
368 +
369 + date = interval.next();
370 + t.equal(date.getHours(), 12, '12');
371 + t.equal(date.getDate(), 30, '30th');
372 + date = interval.next();
373 + t.equal(date.getHours(), 12, '12');
374 + t.equal(date.getDate(), 31, '31st');
375 + } catch (err) {
376 + t.error(err, 'Interval parse error');
377 + }
378 +
379 + t.end();
380 +});
381 +
382 +test('it will work with #131 issue case', function(t) {
383 + var options = {
384 + tz: 'America/Sao_Paulo',
385 + currentDate : new Date('Sun Oct 30 2018 02:59:00 GMT+0200')
386 + };
387 +
388 + var interval = CronExpression.parse('0 9 1 1 *', options);
389 + var date = interval.next();
390 +
391 + t.equal(date.getFullYear(), 2019);
392 + t.equal(date.getDate(), 1);
393 + t.equal(date.getMonth(), 0);
394 +
395 + date = interval.prev();
396 + t.equal(date.getFullYear(), 2018);
397 + t.equal(date.getDate(), 1);
398 + t.equal(date.getMonth(), 0);
399 +
400 + date = interval.prev();
401 + t.equal(date.getFullYear(), 2017);
402 + t.equal(date.getDate(), 1);
403 + t.equal(date.getMonth(), 0);
404 +
405 + t.end();
406 +});
407 +
408 +test('it will work with #137 issue case', function(t) {
409 + var options = {
410 + tz: 'America/New_York',
411 + currentDate : new Date('10/28/2018')
412 + };
413 +
414 + var interval = CronExpression.parse('0 12 * * 3', options);
415 + var date = interval.next();
416 +
417 + t.equal(date.getFullYear(), 2018);
418 + t.equal(date.getDate(), 31);
419 + t.equal(date.getMonth(), 9);
420 +
421 + t.end();
422 +});
1 +export type DateType = Date | number | string
2 +
3 +export interface CronDate {
4 + addYear(): void
5 +
6 + addMonth(): void
7 +
8 + addDay(): void
9 +
10 + addHour(): void
11 +
12 + addMinute(): void
13 +
14 + addSecond(): void
15 +
16 + subtractYear(): void
17 +
18 + subtractMonth(): void
19 +
20 + subtractDay(): void
21 +
22 + subtractHour(): void
23 +
24 + subtractMinute(): void
25 +
26 + subtractSecond(): void
27 +
28 + getDate(): number
29 +
30 + getFullYear(): number
31 +
32 + getDay(): number
33 +
34 + getMonth(): number
35 +
36 + getHours(): number
37 +
38 + getMinutes(): number
39 +
40 + getSeconds(): number
41 +
42 + getMilliseconds(): number
43 +
44 + getTime(): number
45 +
46 + getUTCDate(): number
47 +
48 + getUTCFullYear(): number
49 +
50 + getUTCDay(): number
51 +
52 + getUTCMonth(): number
53 +
54 + getUTCHours(): number
55 +
56 + getUTCMinutes(): number
57 +
58 + getUTCSeconds(): number
59 +
60 + toISOString(): string
61 +
62 + toJSON(): string
63 +
64 + setDate(d: number): void
65 +
66 + setFullYear(y: number): void
67 +
68 + setDay(d: number): void
69 +
70 + setMonth(m: number): void
71 +
72 + setHours(h: number): void
73 +
74 + setMinutes(m: number): void
75 +
76 + setSeconds(s: number): void
77 +
78 + setMilliseconds(s: number): void
79 +
80 + getTime(): number
81 +
82 + toString(): string
83 +
84 + toDate(): Date
85 +
86 + isLastDayOfMonth(): boolean
87 +}
88 +
89 +export interface ParserOptions<IsIterable extends boolean = false> {
90 + currentDate?: DateType
91 + startDate?: DateType
92 + endDate?: DateType
93 + utc?: boolean
94 + tz?: string
95 + nthDayOfWeek?: number
96 + iterator?: IsIterable
97 +}
98 +
99 +type IteratorResultOrCronDate<IsIterable extends boolean> = IsIterable extends true
100 + ? IteratorResult<CronDate, CronDate>
101 + : CronDate;
102 +
103 +export interface ICronExpression<CronFields, IsIterable extends boolean> {
104 + readonly fields: CronFields;
105 +
106 + /** Find next suitable date */
107 + next(): IteratorResultOrCronDate<IsIterable>
108 +
109 + /** Find previous suitable date */
110 + prev(): IteratorResultOrCronDate<IsIterable>
111 +
112 + /** Check if next suitable date exists */
113 + hasNext(): boolean
114 +
115 + /** Check if previous suitable date exists */
116 + hasPrev(): boolean
117 +
118 + /** Iterate over expression iterator */
119 + iterate(steps: number, callback?: (item: IteratorResultOrCronDate<IsIterable>, i: number) => void): IteratorResultOrCronDate<IsIterable>[]
120 +
121 + /** Reset expression iterator state */
122 + reset(resetDate?: string | number | Date): void
123 +
124 + stringify(includeSeconds?: boolean): string
125 +}
126 +
127 +export interface IStringResult<CronFields> {
128 + variables: Record<string, string>,
129 + expressions: ICronExpression<CronFields, false>[],
130 + errors: Record<string, any>,
131 +}
1 +import {
2 + CronDate,
3 + DateType,
4 + ICronExpression,
5 + IStringResult,
6 + ParserOptions,
7 +} from './common';
8 +
9 +type BuildRangeTuple<Current extends [...number[]], Count extends number> =
10 + Current["length"] extends Count
11 + ? Current
12 + : BuildRangeTuple<[number, ...Current], Count>
13 +type RangeTuple<Count extends number> = BuildRangeTuple<[], Count>
14 +type BuildRange<Current extends number, End extends number, Accu extends [...number[]]> =
15 + Accu["length"] extends End
16 + ? Current
17 + : BuildRange<Current | Accu["length"], End, [number, ...Accu]>
18 +type Range<StartInclusive extends number, EndExclusive extends number> = BuildRange<StartInclusive, EndExclusive, RangeTuple<StartInclusive>>
19 +
20 +export type SixtyRange = Range<0, 30> | Range<30, 60>; // Typescript restriction on recursion depth
21 +export type HourRange = Range<0, 24>;
22 +export type DayOfTheMonthRange = Range<1, 32> | 'L';
23 +export type MonthRange = Range<1, 13>;
24 +export type DayOfTheWeekRange = Range<0, 8>;
25 +
26 +export type CronFields = {
27 + readonly second: readonly SixtyRange[];
28 + readonly minute: readonly SixtyRange[];
29 + readonly hour: readonly HourRange[];
30 + readonly dayOfMonth: readonly DayOfTheMonthRange[];
31 + readonly month: readonly MonthRange[];
32 + readonly dayOfWeek: readonly DayOfTheWeekRange[];
33 +}
34 +
35 +export {ParserOptions, CronDate, DateType}
36 +export type CronExpression<IsIterable extends boolean = false> = ICronExpression<CronFields, IsIterable>
37 +export type StringResult = IStringResult<CronFields>
38 +
39 +export function parseExpression<IsIterable extends boolean = false>(expression: string, options?: ParserOptions<IsIterable>): CronExpression<IsIterable>;
40 +
41 +export function fieldsToExpression<IsIterable extends boolean = false>(fields: CronFields, options?: ParserOptions<IsIterable>): CronExpression<IsIterable>;
42 +
43 +export function parseFile(filePath: string, callback: (err: any, data: StringResult) => any): void;
44 +
45 +export function parseString(data: string): StringResult;
1 +import {
2 + CronDate,
3 + DateType,
4 + ICronExpression,
5 + IStringResult,
6 + ParserOptions,
7 +} from '../common';
8 +
9 +export type CronFields = {
10 + readonly second: readonly number[];
11 + readonly minute: readonly number[];
12 + readonly hour: readonly number[];
13 + readonly dayOfMonth: readonly (number | 'L')[];
14 + readonly month: readonly number[];
15 + readonly dayOfWeek: readonly number[];
16 +}
17 +
18 +export {ParserOptions, CronDate, DateType}
19 +export type CronExpression<IsIterable extends boolean = false> = ICronExpression<CronFields, IsIterable>
20 +export type StringResult = IStringResult<CronFields>
21 +
22 +export function parseExpression<IsIterable extends boolean = false>(expression: string, options?: ParserOptions<IsIterable>): CronExpression<IsIterable>;
23 +
24 +export function fieldsToExpression<IsIterable extends boolean = false>(fields: CronFields, options?: ParserOptions<IsIterable>): CronExpression<IsIterable>;
25 +
26 +export function parseFile(filePath: string, callback: (err: any, data: StringResult) => any): void;
27 +
28 +export function parseString(data: string): StringResult;
1 +root = true
2 +
3 +[*]
4 +indent_style = tab;
5 +insert_final_newline = true;
6 +quote_type = auto;
7 +space_after_anonymous_functions = true;
8 +space_after_control_statements = true;
9 +spaces_around_operators = true;
10 +trim_trailing_whitespace = true;
11 +spaces_in_brackets = false;
12 +end_of_line = lf;
13 +
1 +{
2 + "root": true,
3 +
4 + "extends": "@ljharb",
5 +
6 + "rules": {
7 + "id-length": [2, { "min": 1, "max": 35 }],
8 + "max-lines-per-function": [2, 100],
9 + "max-params": [2, 4],
10 + "max-statements": [2, 13]
11 + }
12 +}
1 +{
2 + "es3": true,
3 +
4 + "additionalRules": [],
5 +
6 + "requireSemicolons": true,
7 +
8 + "disallowMultipleSpaces": true,
9 +
10 + "disallowIdentifierNames": [],
11 +
12 + "requireCurlyBraces": {
13 + "allExcept": [],
14 + "keywords": ["if", "else", "for", "while", "do", "try", "catch"]
15 + },
16 +
17 + "requireSpaceAfterKeywords": ["if", "else", "for", "while", "do", "switch", "return", "try", "catch", "function"],
18 +
19 + "disallowSpaceAfterKeywords": [],
20 +
21 + "disallowSpaceBeforeComma": true,
22 + "disallowSpaceAfterComma": false,
23 + "disallowSpaceBeforeSemicolon": true,
24 +
25 + "disallowNodeTypes": [
26 + "DebuggerStatement",
27 + "LabeledStatement",
28 + "SwitchCase",
29 + "SwitchStatement",
30 + "WithStatement"
31 + ],
32 +
33 + "requireObjectKeysOnNewLine": { "allExcept": ["sameLine"] },
34 +
35 + "requireSpacesInAnonymousFunctionExpression": { "beforeOpeningRoundBrace": true, "beforeOpeningCurlyBrace": true },
36 + "requireSpacesInNamedFunctionExpression": { "beforeOpeningCurlyBrace": true },
37 + "disallowSpacesInNamedFunctionExpression": { "beforeOpeningRoundBrace": true },
38 + "requireSpacesInFunctionDeclaration": { "beforeOpeningCurlyBrace": true },
39 + "disallowSpacesInFunctionDeclaration": { "beforeOpeningRoundBrace": true },
40 +
41 + "requireSpaceBetweenArguments": true,
42 +
43 + "disallowSpacesInsideParentheses": true,
44 +
45 + "disallowSpacesInsideArrayBrackets": true,
46 +
47 + "disallowQuotedKeysInObjects": { "allExcept": ["reserved"] },
48 +
49 + "disallowSpaceAfterObjectKeys": true,
50 +
51 + "requireCommaBeforeLineBreak": true,
52 +
53 + "disallowSpaceAfterPrefixUnaryOperators": ["++", "--", "+", "-", "~", "!"],
54 + "requireSpaceAfterPrefixUnaryOperators": [],
55 +
56 + "disallowSpaceBeforePostfixUnaryOperators": ["++", "--"],
57 + "requireSpaceBeforePostfixUnaryOperators": [],
58 +
59 + "disallowSpaceBeforeBinaryOperators": [],
60 + "requireSpaceBeforeBinaryOperators": ["+", "-", "/", "*", "=", "==", "===", "!=", "!=="],
61 +
62 + "requireSpaceAfterBinaryOperators": ["+", "-", "/", "*", "=", "==", "===", "!=", "!=="],
63 + "disallowSpaceAfterBinaryOperators": [],
64 +
65 + "disallowImplicitTypeConversion": ["binary", "string"],
66 +
67 + "disallowKeywords": ["with", "eval"],
68 +
69 + "requireKeywordsOnNewLine": [],
70 + "disallowKeywordsOnNewLine": ["else"],
71 +
72 + "requireLineFeedAtFileEnd": true,
73 +
74 + "disallowTrailingWhitespace": true,
75 +
76 + "disallowTrailingComma": true,
77 +
78 + "excludeFiles": ["node_modules/**", "vendor/**"],
79 +
80 + "disallowMultipleLineStrings": true,
81 +
82 + "requireDotNotation": { "allExcept": ["keywords"] },
83 +
84 + "requireParenthesesAroundIIFE": true,
85 +
86 + "validateLineBreaks": "LF",
87 +
88 + "validateQuoteMarks": {
89 + "escape": true,
90 + "mark": "'"
91 + },
92 +
93 + "disallowOperatorBeforeLineBreak": [],
94 +
95 + "requireSpaceBeforeKeywords": [
96 + "do",
97 + "for",
98 + "if",
99 + "else",
100 + "switch",
101 + "case",
102 + "try",
103 + "catch",
104 + "finally",
105 + "while",
106 + "with",
107 + "return"
108 + ],
109 +
110 + "validateAlignedFunctionParameters": {
111 + "lineBreakAfterOpeningBraces": true,
112 + "lineBreakBeforeClosingBraces": true
113 + },
114 +
115 + "requirePaddingNewLinesBeforeExport": true,
116 +
117 + "validateNewlineAfterArrayElements": {
118 + "maximum": 3
119 + },
120 +
121 + "requirePaddingNewLinesAfterUseStrict": true,
122 +
123 + "disallowArrowFunctions": true,
124 +
125 + "disallowMultiLineTernary": true,
126 +
127 + "validateOrderInObjectKeys": "asc-insensitive",
128 +
129 + "disallowIdenticalDestructuringNames": true,
130 +
131 + "disallowNestedTernaries": { "maxLevel": 1 },
132 +
133 + "requireSpaceAfterComma": { "allExcept": ["trailing"] },
134 + "requireAlignedMultilineParams": false,
135 +
136 + "requireSpacesInGenerator": {
137 + "afterStar": true
138 + },
139 +
140 + "disallowSpacesInGenerator": {
141 + "beforeStar": true
142 + },
143 +
144 + "disallowVar": false,
145 +
146 + "requireArrayDestructuring": false,
147 +
148 + "requireEnhancedObjectLiterals": false,
149 +
150 + "requireObjectDestructuring": false,
151 +
152 + "requireEarlyReturn": false,
153 +
154 + "requireCapitalizedConstructorsNew": {
155 + "allExcept": ["Function", "String", "Object", "Symbol", "Number", "Date", "RegExp", "Error", "Boolean", "Array"]
156 + },
157 +
158 + "requireImportAlphabetized": false,
159 +
160 + "requireSpaceBeforeObjectValues": true,
161 + "requireSpaceBeforeDestructuredValues": true,
162 +
163 + "disallowSpacesInsideTemplateStringPlaceholders": true,
164 +
165 + "disallowArrayDestructuringReturn": false,
166 +
167 + "requireNewlineBeforeSingleStatementsInIf": false,
168 +
169 + "disallowUnusedVariables": true,
170 +
171 + "requireSpacesInsideImportedObjectBraces": true,
172 +
173 + "requireUseStrict": true
174 +}
175 +
1 +language: node_js
2 +os:
3 + - linux
4 +node_js:
5 + - "10.8"
6 + - "9.11"
7 + - "8.11"
8 + - "7.10"
9 + - "6.14"
10 + - "5.12"
11 + - "4.9"
12 + - "iojs-v3.3"
13 + - "iojs-v2.5"
14 + - "iojs-v1.8"
15 + - "0.12"
16 + - "0.10"
17 + - "0.8"
18 +before_install:
19 + - 'case "${TRAVIS_NODE_VERSION}" in 0.*) export NPM_CONFIG_STRICT_SSL=false ;; esac'
20 + - 'nvm install-latest-npm'
21 +install:
22 + - 'if [ "${TRAVIS_NODE_VERSION}" = "0.6" ] || [ "${TRAVIS_NODE_VERSION}" = "0.9" ]; then nvm install --latest-npm 0.8 && npm install && nvm use "${TRAVIS_NODE_VERSION}"; else npm install; fi;'
23 +script:
24 + - 'if [ -n "${PRETEST-}" ]; then npm run pretest ; fi'
25 + - 'if [ -n "${POSTTEST-}" ]; then npm run posttest ; fi'
26 + - 'if [ -n "${COVERAGE-}" ]; then npm run coverage ; fi'
27 + - 'if [ -n "${TEST-}" ]; then npm run tests-only ; fi'
28 +sudo: false
29 +env:
30 + - TEST=true
31 +matrix:
32 + fast_finish: true
33 + include:
34 + - node_js: "lts/*"
35 + env: PRETEST=true
36 + - node_js: "lts/*"
37 + env: POSTTEST=true
38 + - node_js: "4"
39 + env: COVERAGE=true
40 + - node_js: "10.7"
41 + env: TEST=true ALLOW_FAILURE=true
42 + - node_js: "10.6"
43 + env: TEST=true ALLOW_FAILURE=true
44 + - node_js: "10.5"
45 + env: TEST=true ALLOW_FAILURE=true
46 + - node_js: "10.4"
47 + env: TEST=true ALLOW_FAILURE=true
48 + - node_js: "10.3"
49 + env: TEST=true ALLOW_FAILURE=true
50 + - node_js: "10.2"
51 + env: TEST=true ALLOW_FAILURE=true
52 + - node_js: "10.1"
53 + env: TEST=true ALLOW_FAILURE=true
54 + - node_js: "10.0"
55 + env: TEST=true ALLOW_FAILURE=true
56 + - node_js: "9.10"
57 + env: TEST=true ALLOW_FAILURE=true
58 + - node_js: "9.9"
59 + env: TEST=true ALLOW_FAILURE=true
60 + - node_js: "9.8"
61 + env: TEST=true ALLOW_FAILURE=true
62 + - node_js: "9.7"
63 + env: TEST=true ALLOW_FAILURE=true
64 + - node_js: "9.6"
65 + env: TEST=true ALLOW_FAILURE=true
66 + - node_js: "9.5"
67 + env: TEST=true ALLOW_FAILURE=true
68 + - node_js: "9.4"
69 + env: TEST=true ALLOW_FAILURE=true
70 + - node_js: "9.3"
71 + env: TEST=true ALLOW_FAILURE=true
72 + - node_js: "9.2"
73 + env: TEST=true ALLOW_FAILURE=true
74 + - node_js: "9.1"
75 + env: TEST=true ALLOW_FAILURE=true
76 + - node_js: "9.0"
77 + env: TEST=true ALLOW_FAILURE=true
78 + - node_js: "8.10"
79 + env: TEST=true ALLOW_FAILURE=true
80 + - node_js: "8.9"
81 + env: TEST=true ALLOW_FAILURE=true
82 + - node_js: "8.8"
83 + env: TEST=true ALLOW_FAILURE=true
84 + - node_js: "8.7"
85 + env: TEST=true ALLOW_FAILURE=true
86 + - node_js: "8.6"
87 + env: TEST=true ALLOW_FAILURE=true
88 + - node_js: "8.5"
89 + env: TEST=true ALLOW_FAILURE=true
90 + - node_js: "8.4"
91 + env: TEST=true ALLOW_FAILURE=true
92 + - node_js: "8.3"
93 + env: TEST=true ALLOW_FAILURE=true
94 + - node_js: "8.2"
95 + env: TEST=true ALLOW_FAILURE=true
96 + - node_js: "8.1"
97 + env: TEST=true ALLOW_FAILURE=true
98 + - node_js: "8.0"
99 + env: TEST=true ALLOW_FAILURE=true
100 + - node_js: "7.9"
101 + env: TEST=true ALLOW_FAILURE=true
102 + - node_js: "7.8"
103 + env: TEST=true ALLOW_FAILURE=true
104 + - node_js: "7.7"
105 + env: TEST=true ALLOW_FAILURE=true
106 + - node_js: "7.6"
107 + env: TEST=true ALLOW_FAILURE=true
108 + - node_js: "7.5"
109 + env: TEST=true ALLOW_FAILURE=true
110 + - node_js: "7.4"
111 + env: TEST=true ALLOW_FAILURE=true
112 + - node_js: "7.3"
113 + env: TEST=true ALLOW_FAILURE=true
114 + - node_js: "7.2"
115 + env: TEST=true ALLOW_FAILURE=true
116 + - node_js: "7.1"
117 + env: TEST=true ALLOW_FAILURE=true
118 + - node_js: "7.0"
119 + env: TEST=true ALLOW_FAILURE=true
120 + - node_js: "6.13"
121 + env: TEST=true ALLOW_FAILURE=true
122 + - node_js: "6.12"
123 + env: TEST=true ALLOW_FAILURE=true
124 + - node_js: "6.11"
125 + env: TEST=true ALLOW_FAILURE=true
126 + - node_js: "6.10"
127 + env: TEST=true ALLOW_FAILURE=true
128 + - node_js: "6.9"
129 + env: TEST=true ALLOW_FAILURE=true
130 + - node_js: "6.8"
131 + env: TEST=true ALLOW_FAILURE=true
132 + - node_js: "6.7"
133 + env: TEST=true ALLOW_FAILURE=true
134 + - node_js: "6.6"
135 + env: TEST=true ALLOW_FAILURE=true
136 + - node_js: "6.5"
137 + env: TEST=true ALLOW_FAILURE=true
138 + - node_js: "6.4"
139 + env: TEST=true ALLOW_FAILURE=true
140 + - node_js: "6.3"
141 + env: TEST=true ALLOW_FAILURE=true
142 + - node_js: "6.2"
143 + env: TEST=true ALLOW_FAILURE=true
144 + - node_js: "6.1"
145 + env: TEST=true ALLOW_FAILURE=true
146 + - node_js: "6.0"
147 + env: TEST=true ALLOW_FAILURE=true
148 + - node_js: "5.11"
149 + env: TEST=true ALLOW_FAILURE=true
150 + - node_js: "5.10"
151 + env: TEST=true ALLOW_FAILURE=true
152 + - node_js: "5.9"
153 + env: TEST=true ALLOW_FAILURE=true
154 + - node_js: "5.8"
155 + env: TEST=true ALLOW_FAILURE=true
156 + - node_js: "5.7"
157 + env: TEST=true ALLOW_FAILURE=true
158 + - node_js: "5.6"
159 + env: TEST=true ALLOW_FAILURE=true
160 + - node_js: "5.5"
161 + env: TEST=true ALLOW_FAILURE=true
162 + - node_js: "5.4"
163 + env: TEST=true ALLOW_FAILURE=true
164 + - node_js: "5.3"
165 + env: TEST=true ALLOW_FAILURE=true
166 + - node_js: "5.2"
167 + env: TEST=true ALLOW_FAILURE=true
168 + - node_js: "5.1"
169 + env: TEST=true ALLOW_FAILURE=true
170 + - node_js: "5.0"
171 + env: TEST=true ALLOW_FAILURE=true
172 + - node_js: "4.8"
173 + env: TEST=true ALLOW_FAILURE=true
174 + - node_js: "4.7"
175 + env: TEST=true ALLOW_FAILURE=true
176 + - node_js: "4.6"
177 + env: TEST=true ALLOW_FAILURE=true
178 + - node_js: "4.5"
179 + env: TEST=true ALLOW_FAILURE=true
180 + - node_js: "4.4"
181 + env: TEST=true ALLOW_FAILURE=true
182 + - node_js: "4.3"
183 + env: TEST=true ALLOW_FAILURE=true
184 + - node_js: "4.2"
185 + env: TEST=true ALLOW_FAILURE=true
186 + - node_js: "4.1"
187 + env: TEST=true ALLOW_FAILURE=true
188 + - node_js: "4.0"
189 + env: TEST=true ALLOW_FAILURE=true
190 + - node_js: "iojs-v3.2"
191 + env: TEST=true ALLOW_FAILURE=true
192 + - node_js: "iojs-v3.1"
193 + env: TEST=true ALLOW_FAILURE=true
194 + - node_js: "iojs-v3.0"
195 + env: TEST=true ALLOW_FAILURE=true
196 + - node_js: "iojs-v2.4"
197 + env: TEST=true ALLOW_FAILURE=true
198 + - node_js: "iojs-v2.3"
199 + env: TEST=true ALLOW_FAILURE=true
200 + - node_js: "iojs-v2.2"
201 + env: TEST=true ALLOW_FAILURE=true
202 + - node_js: "iojs-v2.1"
203 + env: TEST=true ALLOW_FAILURE=true
204 + - node_js: "iojs-v2.0"
205 + env: TEST=true ALLOW_FAILURE=true
206 + - node_js: "iojs-v1.7"
207 + env: TEST=true ALLOW_FAILURE=true
208 + - node_js: "iojs-v1.6"
209 + env: TEST=true ALLOW_FAILURE=true
210 + - node_js: "iojs-v1.5"
211 + env: TEST=true ALLOW_FAILURE=true
212 + - node_js: "iojs-v1.4"
213 + env: TEST=true ALLOW_FAILURE=true
214 + - node_js: "iojs-v1.3"
215 + env: TEST=true ALLOW_FAILURE=true
216 + - node_js: "iojs-v1.2"
217 + env: TEST=true ALLOW_FAILURE=true
218 + - node_js: "iojs-v1.1"
219 + env: TEST=true ALLOW_FAILURE=true
220 + - node_js: "iojs-v1.0"
221 + env: TEST=true ALLOW_FAILURE=true
222 + - node_js: "0.11"
223 + env: TEST=true ALLOW_FAILURE=true
224 + - node_js: "0.9"
225 + env: TEST=true ALLOW_FAILURE=true
226 + - node_js: "0.6"
227 + env: TEST=true ALLOW_FAILURE=true
228 + - node_js: "0.4"
229 + env: TEST=true ALLOW_FAILURE=true
230 + allow_failures:
231 + - os: osx
232 + - env: TEST=true ALLOW_FAILURE=true
233 + - env: COVERAGE=true
1 +1.1.3 / 2018-08-14
2 +=================
3 + * [Refactor] use a for loop instead of `foreach` to make for smaller bundle sizes
4 + * [Robustness] cache `Array.prototype.concat` and `Object.defineProperty`
5 + * [Deps] update `object-keys`
6 + * [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `nsp`, `tape`, `jscs`; remove unused eccheck script + dep
7 + * [Tests] use pretest/posttest for linting/security
8 + * [Tests] fix npm upgrades on older nodes
9 +
10 +1.1.2 / 2015-10-14
11 +=================
12 + * [Docs] Switch from vb.teelaun.ch to versionbadg.es for the npm version badge SVG
13 + * [Deps] Update `object-keys`
14 + * [Dev Deps] update `jscs`, `tape`, `eslint`, `@ljharb/eslint-config`, `nsp`
15 + * [Tests] up to `io.js` `v3.3`, `node` `v4.2`
16 +
17 +1.1.1 / 2015-07-21
18 +=================
19 + * [Deps] Update `object-keys`
20 + * [Dev Deps] Update `tape`, `eslint`
21 + * [Tests] Test on `io.js` `v2.4`
22 +
23 +1.1.0 / 2015-07-01
24 +=================
25 + * [New] Add support for symbol-valued properties.
26 + * [Dev Deps] Update `nsp`, `eslint`
27 + * [Tests] Test up to `io.js` `v2.3`
28 +
29 +1.0.3 / 2015-05-30
30 +=================
31 + * Using a more reliable check for supported property descriptors.
32 +
33 +1.0.2 / 2015-05-23
34 +=================
35 + * Test up to `io.js` `v2.0`
36 + * Update `tape`, `jscs`, `nsp`, `eslint`, `object-keys`, `editorconfig-tools`, `covert`
37 +
38 +1.0.1 / 2015-01-06
39 +=================
40 + * Update `object-keys` to fix ES3 support
41 +
42 +1.0.0 / 2015-01-04
43 +=================
44 + * v1.0.0
1 +The MIT License (MIT)
2 +
3 +Copyright (C) 2015 Jordan Harband
4 +
5 +Permission is hereby granted, free of charge, to any person obtaining a copy
6 +of this software and associated documentation files (the "Software"), to deal
7 +in the Software without restriction, including without limitation the rights
8 +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 +copies of the Software, and to permit persons to whom the Software is
10 +furnished to do so, subject to the following conditions:
11 +
12 +The above copyright notice and this permission notice shall be included in
13 +all copies or substantial portions of the Software.
14 +
15 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 +THE SOFTWARE.
...\ No newline at end of file ...\ No newline at end of file
1 +#define-properties <sup>[![Version Badge][npm-version-svg]][package-url]</sup>
2 +
3 +[![Build Status][travis-svg]][travis-url]
4 +[![dependency status][deps-svg]][deps-url]
5 +[![dev dependency status][dev-deps-svg]][dev-deps-url]
6 +[![License][license-image]][license-url]
7 +[![Downloads][downloads-image]][downloads-url]
8 +
9 +[![npm badge][npm-badge-png]][package-url]
10 +
11 +[![browser support][testling-svg]][testling-url]
12 +
13 +Define multiple non-enumerable properties at once. Uses `Object.defineProperty` when available; falls back to standard assignment in older engines.
14 +Existing properties are not overridden. Accepts a map of property names to a predicate that, when true, force-overrides.
15 +
16 +## Example
17 +
18 +```js
19 +var define = require('define-properties');
20 +var assert = require('assert');
21 +
22 +var obj = define({ a: 1, b: 2 }, {
23 + a: 10,
24 + b: 20,
25 + c: 30
26 +});
27 +assert(obj.a === 1);
28 +assert(obj.b === 2);
29 +assert(obj.c === 30);
30 +if (define.supportsDescriptors) {
31 + assert.deepEqual(Object.keys(obj), ['a', 'b']);
32 + assert.deepEqual(Object.getOwnPropertyDescriptor(obj, 'c'), {
33 + configurable: true,
34 + enumerable: false,
35 + value: 30,
36 + writable: false
37 + });
38 +}
39 +```
40 +
41 +Then, with predicates:
42 +```js
43 +var define = require('define-properties');
44 +var assert = require('assert');
45 +
46 +var obj = define({ a: 1, b: 2, c: 3 }, {
47 + a: 10,
48 + b: 20,
49 + c: 30
50 +}, {
51 + a: function () { return false; },
52 + b: function () { return true; }
53 +});
54 +assert(obj.a === 1);
55 +assert(obj.b === 20);
56 +assert(obj.c === 3);
57 +if (define.supportsDescriptors) {
58 + assert.deepEqual(Object.keys(obj), ['a', 'c']);
59 + assert.deepEqual(Object.getOwnPropertyDescriptor(obj, 'b'), {
60 + configurable: true,
61 + enumerable: false,
62 + value: 20,
63 + writable: false
64 + });
65 +}
66 +```
67 +
68 +## Tests
69 +Simply clone the repo, `npm install`, and run `npm test`
70 +
71 +[package-url]: https://npmjs.org/package/define-properties
72 +[npm-version-svg]: http://versionbadg.es/ljharb/define-properties.svg
73 +[travis-svg]: https://travis-ci.org/ljharb/define-properties.svg
74 +[travis-url]: https://travis-ci.org/ljharb/define-properties
75 +[deps-svg]: https://david-dm.org/ljharb/define-properties.svg
76 +[deps-url]: https://david-dm.org/ljharb/define-properties
77 +[dev-deps-svg]: https://david-dm.org/ljharb/define-properties/dev-status.svg
78 +[dev-deps-url]: https://david-dm.org/ljharb/define-properties#info=devDependencies
79 +[testling-svg]: https://ci.testling.com/ljharb/define-properties.png
80 +[testling-url]: https://ci.testling.com/ljharb/define-properties
81 +[npm-badge-png]: https://nodei.co/npm/define-properties.png?downloads=true&stars=true
82 +[license-image]: http://img.shields.io/npm/l/define-properties.svg
83 +[license-url]: LICENSE
84 +[downloads-image]: http://img.shields.io/npm/dm/define-properties.svg
85 +[downloads-url]: http://npm-stat.com/charts.html?package=define-properties
86 +
1 +'use strict';
2 +
3 +var keys = require('object-keys');
4 +var hasSymbols = typeof Symbol === 'function' && typeof Symbol('foo') === 'symbol';
5 +
6 +var toStr = Object.prototype.toString;
7 +var concat = Array.prototype.concat;
8 +var origDefineProperty = Object.defineProperty;
9 +
10 +var isFunction = function (fn) {
11 + return typeof fn === 'function' && toStr.call(fn) === '[object Function]';
12 +};
13 +
14 +var arePropertyDescriptorsSupported = function () {
15 + var obj = {};
16 + try {
17 + origDefineProperty(obj, 'x', { enumerable: false, value: obj });
18 + // eslint-disable-next-line no-unused-vars, no-restricted-syntax
19 + for (var _ in obj) { // jscs:ignore disallowUnusedVariables
20 + return false;
21 + }
22 + return obj.x === obj;
23 + } catch (e) { /* this is IE 8. */
24 + return false;
25 + }
26 +};
27 +var supportsDescriptors = origDefineProperty && arePropertyDescriptorsSupported();
28 +
29 +var defineProperty = function (object, name, value, predicate) {
30 + if (name in object && (!isFunction(predicate) || !predicate())) {
31 + return;
32 + }
33 + if (supportsDescriptors) {
34 + origDefineProperty(object, name, {
35 + configurable: true,
36 + enumerable: false,
37 + value: value,
38 + writable: true
39 + });
40 + } else {
41 + object[name] = value;
42 + }
43 +};
44 +
45 +var defineProperties = function (object, map) {
46 + var predicates = arguments.length > 2 ? arguments[2] : {};
47 + var props = keys(map);
48 + if (hasSymbols) {
49 + props = concat.call(props, Object.getOwnPropertySymbols(map));
50 + }
51 + for (var i = 0; i < props.length; i += 1) {
52 + defineProperty(object, props[i], map[props[i]], predicates[props[i]]);
53 + }
54 +};
55 +
56 +defineProperties.supportsDescriptors = !!supportsDescriptors;
57 +
58 +module.exports = defineProperties;
1 +{
2 + "name": "define-properties",
3 + "version": "1.1.3",
4 + "author": "Jordan Harband",
5 + "description": "Define multiple non-enumerable properties at once. Uses `Object.defineProperty` when available; falls back to standard assignment in older engines.",
6 + "license": "MIT",
7 + "main": "index.js",
8 + "scripts": {
9 + "pretest": "npm run --silent lint",
10 + "test": "npm run --silent tests-only",
11 + "posttest": "npm run --silent security",
12 + "tests-only": "node test/index.js",
13 + "coverage": "covert test/*.js",
14 + "coverage-quiet": "covert test/*.js --quiet",
15 + "lint": "npm run --silent jscs && npm run --silent eslint",
16 + "jscs": "jscs test/*.js *.js",
17 + "eslint": "eslint test/*.js *.js",
18 + "security": "nsp check"
19 + },
20 + "repository": {
21 + "type": "git",
22 + "url": "git://github.com/ljharb/define-properties.git"
23 + },
24 + "keywords": [
25 + "Object.defineProperty",
26 + "Object.defineProperties",
27 + "object",
28 + "property descriptor",
29 + "descriptor",
30 + "define",
31 + "ES5"
32 + ],
33 + "dependencies": {
34 + "object-keys": "^1.0.12"
35 + },
36 + "devDependencies": {
37 + "@ljharb/eslint-config": "^13.0.0",
38 + "covert": "^1.1.0",
39 + "eslint": "^5.3.0",
40 + "jscs": "^3.0.7",
41 + "nsp": "^3.2.1",
42 + "tape": "^4.9.0"
43 + },
44 + "testling": {
45 + "files": "test/index.js",
46 + "browsers": [
47 + "iexplore/6.0..latest",
48 + "firefox/3.0..6.0",
49 + "firefox/15.0..latest",
50 + "firefox/nightly",
51 + "chrome/4.0..10.0",
52 + "chrome/20.0..latest",
53 + "chrome/canary",
54 + "opera/10.0..latest",
55 + "opera/next",
56 + "safari/4.0..latest",
57 + "ipad/6.0..latest",
58 + "iphone/6.0..latest",
59 + "android-browser/4.2"
60 + ]
61 + },
62 + "engines": {
63 + "node": ">= 0.4"
64 + }
65 +}
1 +'use strict';
2 +
3 +var define = require('../');
4 +var test = require('tape');
5 +var keys = require('object-keys');
6 +
7 +var arePropertyDescriptorsSupported = function () {
8 + var obj = { a: 1 };
9 + try {
10 + Object.defineProperty(obj, 'x', { value: obj });
11 + return obj.x === obj;
12 + } catch (e) { /* this is IE 8. */
13 + return false;
14 + }
15 +};
16 +var descriptorsSupported = !!Object.defineProperty && arePropertyDescriptorsSupported();
17 +
18 +var hasSymbols = typeof Symbol === 'function' && typeof Symbol('foo') === 'symbol';
19 +
20 +test('defineProperties', function (dt) {
21 + dt.test('with descriptor support', { skip: !descriptorsSupported }, function (t) {
22 + var getDescriptor = function (value) {
23 + return {
24 + configurable: true,
25 + enumerable: false,
26 + value: value,
27 + writable: true
28 + };
29 + };
30 +
31 + var obj = {
32 + a: 1,
33 + b: 2,
34 + c: 3
35 + };
36 + t.deepEqual(keys(obj), ['a', 'b', 'c'], 'all literal-set keys start enumerable');
37 + define(obj, {
38 + b: 3,
39 + c: 4,
40 + d: 5
41 + });
42 + t.deepEqual(obj, {
43 + a: 1,
44 + b: 2,
45 + c: 3
46 + }, 'existing properties were not overridden');
47 + t.deepEqual(Object.getOwnPropertyDescriptor(obj, 'd'), getDescriptor(5), 'new property "d" was added and is not enumerable');
48 + t.deepEqual(['a', 'b', 'c'], keys(obj), 'new keys are not enumerable');
49 +
50 + define(obj, {
51 + a: 2,
52 + b: 3,
53 + c: 4
54 + }, {
55 + a: function () { return true; },
56 + b: function () { return false; }
57 + });
58 + t.deepEqual(obj, {
59 + b: 2,
60 + c: 3
61 + }, 'properties only overriden when predicate exists and returns true');
62 + t.deepEqual(Object.getOwnPropertyDescriptor(obj, 'd'), getDescriptor(5), 'existing property "d" remained and is not enumerable');
63 + t.deepEqual(Object.getOwnPropertyDescriptor(obj, 'a'), getDescriptor(2), 'existing property "a" was overridden and is not enumerable');
64 + t.deepEqual(['b', 'c'], keys(obj), 'overridden keys are not enumerable');
65 +
66 + t.end();
67 + });
68 +
69 + dt.test('without descriptor support', { skip: descriptorsSupported }, function (t) {
70 + var obj = {
71 + a: 1,
72 + b: 2,
73 + c: 3
74 + };
75 + define(obj, {
76 + b: 3,
77 + c: 4,
78 + d: 5
79 + });
80 + t.deepEqual(obj, {
81 + a: 1,
82 + b: 2,
83 + c: 3,
84 + d: 5
85 + }, 'existing properties were not overridden, new properties were added');
86 +
87 + define(obj, {
88 + a: 2,
89 + b: 3,
90 + c: 4
91 + }, {
92 + a: function () { return true; },
93 + b: function () { return false; }
94 + });
95 + t.deepEqual(obj, {
96 + a: 2,
97 + b: 2,
98 + c: 3,
99 + d: 5
100 + }, 'properties only overriden when predicate exists and returns true');
101 +
102 + t.end();
103 + });
104 +
105 + dt.end();
106 +});
107 +
108 +test('symbols', { skip: !hasSymbols }, function (t) {
109 + var sym = Symbol('foo');
110 + var obj = {};
111 + var aValue = {};
112 + var bValue = {};
113 + var properties = { a: aValue };
114 + properties[sym] = bValue;
115 +
116 + define(obj, properties);
117 +
118 + t.deepEqual(Object.keys(obj), [], 'object has no enumerable keys');
119 + t.deepEqual(Object.getOwnPropertyNames(obj), ['a'], 'object has non-enumerable "a" key');
120 + t.deepEqual(Object.getOwnPropertySymbols(obj), [sym], 'object has non-enumerable symbol key');
121 + t.equal(obj.a, aValue, 'string keyed value is defined');
122 + t.equal(obj[sym], bValue, 'symbol keyed value is defined');
123 +
124 + t.end();
125 +});
1 +root = true
2 +
3 +[*]
4 +indent_style = tab
5 +indent_size = 4
6 +end_of_line = lf
7 +charset = utf-8
8 +trim_trailing_whitespace = true
9 +insert_final_newline = true
10 +max_line_length = 120
11 +
12 +[CHANGELOG.md]
13 +indent_style = space
14 +indent_size = 2
15 +
16 +[*.json]
17 +max_line_length = off
18 +
19 +[Makefile]
20 +max_line_length = off
1 +{
2 + "root": true,
3 +
4 + "extends": "@ljharb",
5 +
6 + "rules": {
7 + "func-name-matching": 0,
8 + "indent": [2, 4],
9 + "max-nested-callbacks": [2, 3],
10 + "max-params": [2, 3],
11 + "max-statements": [2, 20],
12 + "no-new-func": [1],
13 + "strict": [0]
14 + }
15 +}
1 +{
2 + "es3": true,
3 +
4 + "additionalRules": [],
5 +
6 + "requireSemicolons": true,
7 +
8 + "disallowMultipleSpaces": true,
9 +
10 + "disallowIdentifierNames": [],
11 +
12 + "requireCurlyBraces": {
13 + "allExcept": [],
14 + "keywords": ["if", "else", "for", "while", "do", "try", "catch"]
15 + },
16 +
17 + "requireSpaceAfterKeywords": ["if", "else", "for", "while", "do", "switch", "return", "try", "catch", "function"],
18 +
19 + "disallowSpaceAfterKeywords": [],
20 +
21 + "disallowSpaceBeforeComma": true,
22 + "disallowSpaceAfterComma": false,
23 + "disallowSpaceBeforeSemicolon": true,
24 +
25 + "disallowNodeTypes": [
26 + "DebuggerStatement",
27 + "ForInStatement",
28 + "LabeledStatement",
29 + "SwitchCase",
30 + "SwitchStatement",
31 + "WithStatement"
32 + ],
33 +
34 + "requireObjectKeysOnNewLine": { "allExcept": ["sameLine"] },
35 +
36 + "requireSpacesInAnonymousFunctionExpression": { "beforeOpeningRoundBrace": true, "beforeOpeningCurlyBrace": true },
37 + "requireSpacesInNamedFunctionExpression": { "beforeOpeningCurlyBrace": true },
38 + "disallowSpacesInNamedFunctionExpression": { "beforeOpeningRoundBrace": true },
39 + "requireSpacesInFunctionDeclaration": { "beforeOpeningCurlyBrace": true },
40 + "disallowSpacesInFunctionDeclaration": { "beforeOpeningRoundBrace": true },
41 +
42 + "requireSpaceBetweenArguments": true,
43 +
44 + "disallowSpacesInsideParentheses": true,
45 +
46 + "disallowSpacesInsideArrayBrackets": true,
47 +
48 + "disallowQuotedKeysInObjects": { "allExcept": ["reserved"] },
49 +
50 + "disallowSpaceAfterObjectKeys": true,
51 +
52 + "requireCommaBeforeLineBreak": true,
53 +
54 + "disallowSpaceAfterPrefixUnaryOperators": ["++", "--", "+", "-", "~", "!"],
55 + "requireSpaceAfterPrefixUnaryOperators": [],
56 +
57 + "disallowSpaceBeforePostfixUnaryOperators": ["++", "--"],
58 + "requireSpaceBeforePostfixUnaryOperators": [],
59 +
60 + "disallowSpaceBeforeBinaryOperators": [],
61 + "requireSpaceBeforeBinaryOperators": ["+", "-", "/", "*", "=", "==", "===", "!=", "!=="],
62 +
63 + "requireSpaceAfterBinaryOperators": ["+", "-", "/", "*", "=", "==", "===", "!=", "!=="],
64 + "disallowSpaceAfterBinaryOperators": [],
65 +
66 + "disallowImplicitTypeConversion": ["binary", "string"],
67 +
68 + "disallowKeywords": ["with", "eval"],
69 +
70 + "requireKeywordsOnNewLine": [],
71 + "disallowKeywordsOnNewLine": ["else"],
72 +
73 + "requireLineFeedAtFileEnd": true,
74 +
75 + "disallowTrailingWhitespace": true,
76 +
77 + "disallowTrailingComma": true,
78 +
79 + "excludeFiles": ["node_modules/**", "vendor/**"],
80 +
81 + "disallowMultipleLineStrings": true,
82 +
83 + "requireDotNotation": { "allExcept": ["keywords"] },
84 +
85 + "requireParenthesesAroundIIFE": true,
86 +
87 + "validateLineBreaks": "LF",
88 +
89 + "validateQuoteMarks": {
90 + "escape": true,
91 + "mark": "'"
92 + },
93 +
94 + "disallowOperatorBeforeLineBreak": [],
95 +
96 + "requireSpaceBeforeKeywords": [
97 + "do",
98 + "for",
99 + "if",
100 + "else",
101 + "switch",
102 + "case",
103 + "try",
104 + "catch",
105 + "finally",
106 + "while",
107 + "with",
108 + "return"
109 + ],
110 +
111 + "validateAlignedFunctionParameters": {
112 + "lineBreakAfterOpeningBraces": true,
113 + "lineBreakBeforeClosingBraces": true
114 + },
115 +
116 + "requirePaddingNewLinesBeforeExport": true,
117 +
118 + "validateNewlineAfterArrayElements": {
119 + "maximum": 8
120 + },
121 +
122 + "requirePaddingNewLinesAfterUseStrict": true,
123 +
124 + "disallowArrowFunctions": true,
125 +
126 + "disallowMultiLineTernary": true,
127 +
128 + "validateOrderInObjectKeys": "asc-insensitive",
129 +
130 + "disallowIdenticalDestructuringNames": true,
131 +
132 + "disallowNestedTernaries": { "maxLevel": 1 },
133 +
134 + "requireSpaceAfterComma": { "allExcept": ["trailing"] },
135 + "requireAlignedMultilineParams": false,
136 +
137 + "requireSpacesInGenerator": {
138 + "afterStar": true
139 + },
140 +
141 + "disallowSpacesInGenerator": {
142 + "beforeStar": true
143 + },
144 +
145 + "disallowVar": false,
146 +
147 + "requireArrayDestructuring": false,
148 +
149 + "requireEnhancedObjectLiterals": false,
150 +
151 + "requireObjectDestructuring": false,
152 +
153 + "requireEarlyReturn": false,
154 +
155 + "requireCapitalizedConstructorsNew": {
156 + "allExcept": ["Function", "String", "Object", "Symbol", "Number", "Date", "RegExp", "Error", "Boolean", "Array"]
157 + },
158 +
159 + "requireImportAlphabetized": false,
160 +
161 + "requireSpaceBeforeObjectValues": true,
162 + "requireSpaceBeforeDestructuredValues": true,
163 +
164 + "disallowSpacesInsideTemplateStringPlaceholders": true,
165 +
166 + "disallowArrayDestructuringReturn": false,
167 +
168 + "requireNewlineBeforeSingleStatementsInIf": false,
169 +
170 + "disallowUnusedVariables": true,
171 +
172 + "requireSpacesInsideImportedObjectBraces": true,
173 +
174 + "requireUseStrict": true
175 +}
176 +
1 +# gitignore
2 +.DS_Store
3 +.monitor
4 +.*.swp
5 +.nodemonignore
6 +releases
7 +*.log
8 +*.err
9 +fleet.json
10 +public/browserify
11 +bin/*.json
12 +.bin
13 +build
14 +compile
15 +.lock-wscript
16 +coverage
17 +node_modules
18 +
19 +# Only apps should have lockfiles
20 +npm-shrinkwrap.json
21 +package-lock.json
22 +yarn.lock
1 +language: node_js
2 +os:
3 + - linux
4 +node_js:
5 + - "8.4"
6 + - "7.10"
7 + - "6.11"
8 + - "5.12"
9 + - "4.8"
10 + - "iojs-v3.3"
11 + - "iojs-v2.5"
12 + - "iojs-v1.8"
13 + - "0.12"
14 + - "0.10"
15 + - "0.8"
16 +before_install:
17 + - 'if [ "${TRAVIS_NODE_VERSION}" = "0.6" ]; then npm install -g npm@1.3 ; elif [ "${TRAVIS_NODE_VERSION}" != "0.9" ]; then case "$(npm --version)" in 1.*) npm install -g npm@1.4.28 ;; 2.*) npm install -g npm@2 ;; esac ; fi'
18 + - 'if [ "${TRAVIS_NODE_VERSION}" != "0.6" ] && [ "${TRAVIS_NODE_VERSION}" != "0.9" ]; then if [ "${TRAVIS_NODE_VERSION%${TRAVIS_NODE_VERSION#[0-9]}}" = "0" ] || [ "${TRAVIS_NODE_VERSION:0:4}" = "iojs" ]; then npm install -g npm@4.5 ; else npm install -g npm; fi; fi'
19 +install:
20 + - 'if [ "${TRAVIS_NODE_VERSION}" = "0.6" ]; then nvm install 0.8 && npm install -g npm@1.3 && npm install -g npm@1.4.28 && npm install -g npm@2 && npm install && nvm use "${TRAVIS_NODE_VERSION}"; else npm install; fi;'
21 +script:
22 + - 'if [ -n "${PRETEST-}" ]; then npm run pretest ; fi'
23 + - 'if [ -n "${POSTTEST-}" ]; then npm run posttest ; fi'
24 + - 'if [ -n "${COVERAGE-}" ]; then npm run coverage ; fi'
25 + - 'if [ -n "${TEST-}" ]; then npm run tests-only ; fi'
26 +sudo: false
27 +env:
28 + - TEST=true
29 +matrix:
30 + fast_finish: true
31 + include:
32 + - node_js: "node"
33 + env: PRETEST=true
34 + - node_js: "4"
35 + env: COVERAGE=true
36 + - node_js: "8.3"
37 + env: TEST=true ALLOW_FAILURE=true
38 + - node_js: "8.2"
39 + env: TEST=true ALLOW_FAILURE=true
40 + - node_js: "8.1"
41 + env: TEST=true ALLOW_FAILURE=true
42 + - node_js: "8.0"
43 + env: TEST=true ALLOW_FAILURE=true
44 + - node_js: "7.9"
45 + env: TEST=true ALLOW_FAILURE=true
46 + - node_js: "7.8"
47 + env: TEST=true ALLOW_FAILURE=true
48 + - node_js: "7.7"
49 + env: TEST=true ALLOW_FAILURE=true
50 + - node_js: "7.6"
51 + env: TEST=true ALLOW_FAILURE=true
52 + - node_js: "7.5"
53 + env: TEST=true ALLOW_FAILURE=true
54 + - node_js: "7.4"
55 + env: TEST=true ALLOW_FAILURE=true
56 + - node_js: "7.3"
57 + env: TEST=true ALLOW_FAILURE=true
58 + - node_js: "7.2"
59 + env: TEST=true ALLOW_FAILURE=true
60 + - node_js: "7.1"
61 + env: TEST=true ALLOW_FAILURE=true
62 + - node_js: "7.0"
63 + env: TEST=true ALLOW_FAILURE=true
64 + - node_js: "6.10"
65 + env: TEST=true ALLOW_FAILURE=true
66 + - node_js: "6.9"
67 + env: TEST=true ALLOW_FAILURE=true
68 + - node_js: "6.8"
69 + env: TEST=true ALLOW_FAILURE=true
70 + - node_js: "6.7"
71 + env: TEST=true ALLOW_FAILURE=true
72 + - node_js: "6.6"
73 + env: TEST=true ALLOW_FAILURE=true
74 + - node_js: "6.5"
75 + env: TEST=true ALLOW_FAILURE=true
76 + - node_js: "6.4"
77 + env: TEST=true ALLOW_FAILURE=true
78 + - node_js: "6.3"
79 + env: TEST=true ALLOW_FAILURE=true
80 + - node_js: "6.2"
81 + env: TEST=true ALLOW_FAILURE=true
82 + - node_js: "6.1"
83 + env: TEST=true ALLOW_FAILURE=true
84 + - node_js: "6.0"
85 + env: TEST=true ALLOW_FAILURE=true
86 + - node_js: "5.11"
87 + env: TEST=true ALLOW_FAILURE=true
88 + - node_js: "5.10"
89 + env: TEST=true ALLOW_FAILURE=true
90 + - node_js: "5.9"
91 + env: TEST=true ALLOW_FAILURE=true
92 + - node_js: "5.8"
93 + env: TEST=true ALLOW_FAILURE=true
94 + - node_js: "5.7"
95 + env: TEST=true ALLOW_FAILURE=true
96 + - node_js: "5.6"
97 + env: TEST=true ALLOW_FAILURE=true
98 + - node_js: "5.5"
99 + env: TEST=true ALLOW_FAILURE=true
100 + - node_js: "5.4"
101 + env: TEST=true ALLOW_FAILURE=true
102 + - node_js: "5.3"
103 + env: TEST=true ALLOW_FAILURE=true
104 + - node_js: "5.2"
105 + env: TEST=true ALLOW_FAILURE=true
106 + - node_js: "5.1"
107 + env: TEST=true ALLOW_FAILURE=true
108 + - node_js: "5.0"
109 + env: TEST=true ALLOW_FAILURE=true
110 + - node_js: "4.7"
111 + env: TEST=true ALLOW_FAILURE=true
112 + - node_js: "4.6"
113 + env: TEST=true ALLOW_FAILURE=true
114 + - node_js: "4.5"
115 + env: TEST=true ALLOW_FAILURE=true
116 + - node_js: "4.4"
117 + env: TEST=true ALLOW_FAILURE=true
118 + - node_js: "4.3"
119 + env: TEST=true ALLOW_FAILURE=true
120 + - node_js: "4.2"
121 + env: TEST=true ALLOW_FAILURE=true
122 + - node_js: "4.1"
123 + env: TEST=true ALLOW_FAILURE=true
124 + - node_js: "4.0"
125 + env: TEST=true ALLOW_FAILURE=true
126 + - node_js: "iojs-v3.2"
127 + env: TEST=true ALLOW_FAILURE=true
128 + - node_js: "iojs-v3.1"
129 + env: TEST=true ALLOW_FAILURE=true
130 + - node_js: "iojs-v3.0"
131 + env: TEST=true ALLOW_FAILURE=true
132 + - node_js: "iojs-v2.4"
133 + env: TEST=true ALLOW_FAILURE=true
134 + - node_js: "iojs-v2.3"
135 + env: TEST=true ALLOW_FAILURE=true
136 + - node_js: "iojs-v2.2"
137 + env: TEST=true ALLOW_FAILURE=true
138 + - node_js: "iojs-v2.1"
139 + env: TEST=true ALLOW_FAILURE=true
140 + - node_js: "iojs-v2.0"
141 + env: TEST=true ALLOW_FAILURE=true
142 + - node_js: "iojs-v1.7"
143 + env: TEST=true ALLOW_FAILURE=true
144 + - node_js: "iojs-v1.6"
145 + env: TEST=true ALLOW_FAILURE=true
146 + - node_js: "iojs-v1.5"
147 + env: TEST=true ALLOW_FAILURE=true
148 + - node_js: "iojs-v1.4"
149 + env: TEST=true ALLOW_FAILURE=true
150 + - node_js: "iojs-v1.3"
151 + env: TEST=true ALLOW_FAILURE=true
152 + - node_js: "iojs-v1.2"
153 + env: TEST=true ALLOW_FAILURE=true
154 + - node_js: "iojs-v1.1"
155 + env: TEST=true ALLOW_FAILURE=true
156 + - node_js: "iojs-v1.0"
157 + env: TEST=true ALLOW_FAILURE=true
158 + - node_js: "0.11"
159 + env: TEST=true ALLOW_FAILURE=true
160 + - node_js: "0.9"
161 + env: TEST=true ALLOW_FAILURE=true
162 + - node_js: "0.6"
163 + env: TEST=true ALLOW_FAILURE=true
164 + - node_js: "0.4"
165 + env: TEST=true ALLOW_FAILURE=true
166 + allow_failures:
167 + - os: osx
168 + - env: TEST=true ALLOW_FAILURE=true
1 +Copyright (c) 2013 Raynos.
2 +
3 +Permission is hereby granted, free of charge, to any person obtaining a copy
4 +of this software and associated documentation files (the "Software"), to deal
5 +in the Software without restriction, including without limitation the rights
6 +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 +copies of the Software, and to permit persons to whom the Software is
8 +furnished to do so, subject to the following conditions:
9 +
10 +The above copyright notice and this permission notice shall be included in
11 +all copies or substantial portions of the Software.
12 +
13 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 +THE SOFTWARE.
20 +
1 +# function-bind
2 +
3 +<!--
4 + [![build status][travis-svg]][travis-url]
5 + [![NPM version][npm-badge-svg]][npm-url]
6 + [![Coverage Status][5]][6]
7 + [![gemnasium Dependency Status][7]][8]
8 + [![Dependency status][deps-svg]][deps-url]
9 + [![Dev Dependency status][dev-deps-svg]][dev-deps-url]
10 +-->
11 +
12 +<!-- [![browser support][11]][12] -->
13 +
14 +Implementation of function.prototype.bind
15 +
16 +## Example
17 +
18 +I mainly do this for unit tests I run on phantomjs.
19 +PhantomJS does not have Function.prototype.bind :(
20 +
21 +```js
22 +Function.prototype.bind = require("function-bind")
23 +```
24 +
25 +## Installation
26 +
27 +`npm install function-bind`
28 +
29 +## Contributors
30 +
31 + - Raynos
32 +
33 +## MIT Licenced
34 +
35 + [travis-svg]: https://travis-ci.org/Raynos/function-bind.svg
36 + [travis-url]: https://travis-ci.org/Raynos/function-bind
37 + [npm-badge-svg]: https://badge.fury.io/js/function-bind.svg
38 + [npm-url]: https://npmjs.org/package/function-bind
39 + [5]: https://coveralls.io/repos/Raynos/function-bind/badge.png
40 + [6]: https://coveralls.io/r/Raynos/function-bind
41 + [7]: https://gemnasium.com/Raynos/function-bind.png
42 + [8]: https://gemnasium.com/Raynos/function-bind
43 + [deps-svg]: https://david-dm.org/Raynos/function-bind.svg
44 + [deps-url]: https://david-dm.org/Raynos/function-bind
45 + [dev-deps-svg]: https://david-dm.org/Raynos/function-bind/dev-status.svg
46 + [dev-deps-url]: https://david-dm.org/Raynos/function-bind#info=devDependencies
47 + [11]: https://ci.testling.com/Raynos/function-bind.png
48 + [12]: https://ci.testling.com/Raynos/function-bind
1 +'use strict';
2 +
3 +/* eslint no-invalid-this: 1 */
4 +
5 +var ERROR_MESSAGE = 'Function.prototype.bind called on incompatible ';
6 +var slice = Array.prototype.slice;
7 +var toStr = Object.prototype.toString;
8 +var funcType = '[object Function]';
9 +
10 +module.exports = function bind(that) {
11 + var target = this;
12 + if (typeof target !== 'function' || toStr.call(target) !== funcType) {
13 + throw new TypeError(ERROR_MESSAGE + target);
14 + }
15 + var args = slice.call(arguments, 1);
16 +
17 + var bound;
18 + var binder = function () {
19 + if (this instanceof bound) {
20 + var result = target.apply(
21 + this,
22 + args.concat(slice.call(arguments))
23 + );
24 + if (Object(result) === result) {
25 + return result;
26 + }
27 + return this;
28 + } else {
29 + return target.apply(
30 + that,
31 + args.concat(slice.call(arguments))
32 + );
33 + }
34 + };
35 +
36 + var boundLength = Math.max(0, target.length - args.length);
37 + var boundArgs = [];
38 + for (var i = 0; i < boundLength; i++) {
39 + boundArgs.push('$' + i);
40 + }
41 +
42 + bound = Function('binder', 'return function (' + boundArgs.join(',') + '){ return binder.apply(this,arguments); }')(binder);
43 +
44 + if (target.prototype) {
45 + var Empty = function Empty() {};
46 + Empty.prototype = target.prototype;
47 + bound.prototype = new Empty();
48 + Empty.prototype = null;
49 + }
50 +
51 + return bound;
52 +};
1 +'use strict';
2 +
3 +var implementation = require('./implementation');
4 +
5 +module.exports = Function.prototype.bind || implementation;
1 +{
2 + "name": "function-bind",
3 + "version": "1.1.1",
4 + "description": "Implementation of Function.prototype.bind",
5 + "keywords": [
6 + "function",
7 + "bind",
8 + "shim",
9 + "es5"
10 + ],
11 + "author": "Raynos <raynos2@gmail.com>",
12 + "repository": "git://github.com/Raynos/function-bind.git",
13 + "main": "index",
14 + "homepage": "https://github.com/Raynos/function-bind",
15 + "contributors": [
16 + {
17 + "name": "Raynos"
18 + },
19 + {
20 + "name": "Jordan Harband",
21 + "url": "https://github.com/ljharb"
22 + }
23 + ],
24 + "bugs": {
25 + "url": "https://github.com/Raynos/function-bind/issues",
26 + "email": "raynos2@gmail.com"
27 + },
28 + "dependencies": {},
29 + "devDependencies": {
30 + "@ljharb/eslint-config": "^12.2.1",
31 + "covert": "^1.1.0",
32 + "eslint": "^4.5.0",
33 + "jscs": "^3.0.7",
34 + "tape": "^4.8.0"
35 + },
36 + "license": "MIT",
37 + "scripts": {
38 + "pretest": "npm run lint",
39 + "test": "npm run tests-only",
40 + "posttest": "npm run coverage -- --quiet",
41 + "tests-only": "node test",
42 + "coverage": "covert test/*.js",
43 + "lint": "npm run jscs && npm run eslint",
44 + "jscs": "jscs *.js */*.js",
45 + "eslint": "eslint *.js */*.js"
46 + },
47 + "testling": {
48 + "files": "test/index.js",
49 + "browsers": [
50 + "ie/8..latest",
51 + "firefox/16..latest",
52 + "firefox/nightly",
53 + "chrome/22..latest",
54 + "chrome/canary",
55 + "opera/12..latest",
56 + "opera/next",
57 + "safari/5.1..latest",
58 + "ipad/6.0..latest",
59 + "iphone/6.0..latest",
60 + "android-browser/4.2..latest"
61 + ]
62 + }
63 +}
1 +{
2 + "rules": {
3 + "array-bracket-newline": 0,
4 + "array-element-newline": 0,
5 + "max-statements-per-line": [2, { "max": 2 }],
6 + "no-invalid-this": 0,
7 + "no-magic-numbers": 0,
8 + }
9 +}
1 +// jscs:disable requireUseStrict
2 +
3 +var test = require('tape');
4 +
5 +var functionBind = require('../implementation');
6 +var getCurrentContext = function () { return this; };
7 +
8 +test('functionBind is a function', function (t) {
9 + t.equal(typeof functionBind, 'function');
10 + t.end();
11 +});
12 +
13 +test('non-functions', function (t) {
14 + var nonFunctions = [true, false, [], {}, 42, 'foo', NaN, /a/g];
15 + t.plan(nonFunctions.length);
16 + for (var i = 0; i < nonFunctions.length; ++i) {
17 + try { functionBind.call(nonFunctions[i]); } catch (ex) {
18 + t.ok(ex instanceof TypeError, 'throws when given ' + String(nonFunctions[i]));
19 + }
20 + }
21 + t.end();
22 +});
23 +
24 +test('without a context', function (t) {
25 + t.test('binds properly', function (st) {
26 + var args, context;
27 + var namespace = {
28 + func: functionBind.call(function () {
29 + args = Array.prototype.slice.call(arguments);
30 + context = this;
31 + })
32 + };
33 + namespace.func(1, 2, 3);
34 + st.deepEqual(args, [1, 2, 3]);
35 + st.equal(context, getCurrentContext.call());
36 + st.end();
37 + });
38 +
39 + t.test('binds properly, and still supplies bound arguments', function (st) {
40 + var args, context;
41 + var namespace = {
42 + func: functionBind.call(function () {
43 + args = Array.prototype.slice.call(arguments);
44 + context = this;
45 + }, undefined, 1, 2, 3)
46 + };
47 + namespace.func(4, 5, 6);
48 + st.deepEqual(args, [1, 2, 3, 4, 5, 6]);
49 + st.equal(context, getCurrentContext.call());
50 + st.end();
51 + });
52 +
53 + t.test('returns properly', function (st) {
54 + var args;
55 + var namespace = {
56 + func: functionBind.call(function () {
57 + args = Array.prototype.slice.call(arguments);
58 + return this;
59 + }, null)
60 + };
61 + var context = namespace.func(1, 2, 3);
62 + st.equal(context, getCurrentContext.call(), 'returned context is namespaced context');
63 + st.deepEqual(args, [1, 2, 3], 'passed arguments are correct');
64 + st.end();
65 + });
66 +
67 + t.test('returns properly with bound arguments', function (st) {
68 + var args;
69 + var namespace = {
70 + func: functionBind.call(function () {
71 + args = Array.prototype.slice.call(arguments);
72 + return this;
73 + }, null, 1, 2, 3)
74 + };
75 + var context = namespace.func(4, 5, 6);
76 + st.equal(context, getCurrentContext.call(), 'returned context is namespaced context');
77 + st.deepEqual(args, [1, 2, 3, 4, 5, 6], 'passed arguments are correct');
78 + st.end();
79 + });
80 +
81 + t.test('called as a constructor', function (st) {
82 + var thunkify = function (value) {
83 + return function () { return value; };
84 + };
85 + st.test('returns object value', function (sst) {
86 + var expectedReturnValue = [1, 2, 3];
87 + var Constructor = functionBind.call(thunkify(expectedReturnValue), null);
88 + var result = new Constructor();
89 + sst.equal(result, expectedReturnValue);
90 + sst.end();
91 + });
92 +
93 + st.test('does not return primitive value', function (sst) {
94 + var Constructor = functionBind.call(thunkify(42), null);
95 + var result = new Constructor();
96 + sst.notEqual(result, 42);
97 + sst.end();
98 + });
99 +
100 + st.test('object from bound constructor is instance of original and bound constructor', function (sst) {
101 + var A = function (x) {
102 + this.name = x || 'A';
103 + };
104 + var B = functionBind.call(A, null, 'B');
105 +
106 + var result = new B();
107 + sst.ok(result instanceof B, 'result is instance of bound constructor');
108 + sst.ok(result instanceof A, 'result is instance of original constructor');
109 + sst.end();
110 + });
111 +
112 + st.end();
113 + });
114 +
115 + t.end();
116 +});
117 +
118 +test('with a context', function (t) {
119 + t.test('with no bound arguments', function (st) {
120 + var args, context;
121 + var boundContext = {};
122 + var namespace = {
123 + func: functionBind.call(function () {
124 + args = Array.prototype.slice.call(arguments);
125 + context = this;
126 + }, boundContext)
127 + };
128 + namespace.func(1, 2, 3);
129 + st.equal(context, boundContext, 'binds a context properly');
130 + st.deepEqual(args, [1, 2, 3], 'supplies passed arguments');
131 + st.end();
132 + });
133 +
134 + t.test('with bound arguments', function (st) {
135 + var args, context;
136 + var boundContext = {};
137 + var namespace = {
138 + func: functionBind.call(function () {
139 + args = Array.prototype.slice.call(arguments);
140 + context = this;
141 + }, boundContext, 1, 2, 3)
142 + };
143 + namespace.func(4, 5, 6);
144 + st.equal(context, boundContext, 'binds a context properly');
145 + st.deepEqual(args, [1, 2, 3, 4, 5, 6], 'supplies bound and passed arguments');
146 + st.end();
147 + });
148 +
149 + t.test('returns properly', function (st) {
150 + var boundContext = {};
151 + var args;
152 + var namespace = {
153 + func: functionBind.call(function () {
154 + args = Array.prototype.slice.call(arguments);
155 + return this;
156 + }, boundContext)
157 + };
158 + var context = namespace.func(1, 2, 3);
159 + st.equal(context, boundContext, 'returned context is bound context');
160 + st.notEqual(context, getCurrentContext.call(), 'returned context is not lexical context');
161 + st.deepEqual(args, [1, 2, 3], 'passed arguments are correct');
162 + st.end();
163 + });
164 +
165 + t.test('returns properly with bound arguments', function (st) {
166 + var boundContext = {};
167 + var args;
168 + var namespace = {
169 + func: functionBind.call(function () {
170 + args = Array.prototype.slice.call(arguments);
171 + return this;
172 + }, boundContext, 1, 2, 3)
173 + };
174 + var context = namespace.func(4, 5, 6);
175 + st.equal(context, boundContext, 'returned context is bound context');
176 + st.notEqual(context, getCurrentContext.call(), 'returned context is not lexical context');
177 + st.deepEqual(args, [1, 2, 3, 4, 5, 6], 'passed arguments are correct');
178 + st.end();
179 + });
180 +
181 + t.test('passes the correct arguments when called as a constructor', function (st) {
182 + var expected = { name: 'Correct' };
183 + var namespace = {
184 + Func: functionBind.call(function (arg) {
185 + return arg;
186 + }, { name: 'Incorrect' })
187 + };
188 + var returned = new namespace.Func(expected);
189 + st.equal(returned, expected, 'returns the right arg when called as a constructor');
190 + st.end();
191 + });
192 +
193 + t.test('has the new instance\'s context when called as a constructor', function (st) {
194 + var actualContext;
195 + var expectedContext = { foo: 'bar' };
196 + var namespace = {
197 + Func: functionBind.call(function () {
198 + actualContext = this;
199 + }, expectedContext)
200 + };
201 + var result = new namespace.Func();
202 + st.equal(result instanceof namespace.Func, true);
203 + st.notEqual(actualContext, expectedContext);
204 + st.end();
205 + });
206 +
207 + t.end();
208 +});
209 +
210 +test('bound function length', function (t) {
211 + t.test('sets a correct length without thisArg', function (st) {
212 + var subject = functionBind.call(function (a, b, c) { return a + b + c; });
213 + st.equal(subject.length, 3);
214 + st.equal(subject(1, 2, 3), 6);
215 + st.end();
216 + });
217 +
218 + t.test('sets a correct length with thisArg', function (st) {
219 + var subject = functionBind.call(function (a, b, c) { return a + b + c; }, {});
220 + st.equal(subject.length, 3);
221 + st.equal(subject(1, 2, 3), 6);
222 + st.end();
223 + });
224 +
225 + t.test('sets a correct length without thisArg and first argument', function (st) {
226 + var subject = functionBind.call(function (a, b, c) { return a + b + c; }, undefined, 1);
227 + st.equal(subject.length, 2);
228 + st.equal(subject(2, 3), 6);
229 + st.end();
230 + });
231 +
232 + t.test('sets a correct length with thisArg and first argument', function (st) {
233 + var subject = functionBind.call(function (a, b, c) { return a + b + c; }, {}, 1);
234 + st.equal(subject.length, 2);
235 + st.equal(subject(2, 3), 6);
236 + st.end();
237 + });
238 +
239 + t.test('sets a correct length without thisArg and too many arguments', function (st) {
240 + var subject = functionBind.call(function (a, b, c) { return a + b + c; }, undefined, 1, 2, 3, 4);
241 + st.equal(subject.length, 0);
242 + st.equal(subject(), 6);
243 + st.end();
244 + });
245 +
246 + t.test('sets a correct length with thisArg and too many arguments', function (st) {
247 + var subject = functionBind.call(function (a, b, c) { return a + b + c; }, {}, 1, 2, 3, 4);
248 + st.equal(subject.length, 0);
249 + st.equal(subject(), 6);
250 + st.end();
251 + });
252 +});
1 +{
2 + "root": true,
3 +
4 + "extends": "@ljharb",
5 +
6 + "env": {
7 + "es6": true,
8 + "es2017": true,
9 + "es2020": true,
10 + "es2021": true,
11 + },
12 +
13 + "globals": {
14 + "AggregateError": false,
15 + },
16 +
17 + "rules": {
18 + "array-bracket-newline": 0,
19 + "array-element-newline": 0,
20 + "complexity": 0,
21 + "eqeqeq": [2, "allow-null"],
22 + "func-name-matching": 0,
23 + "id-length": 0,
24 + "max-lines-per-function": [2, 80],
25 + "max-params": [2, 4],
26 + "max-statements": 0,
27 + "max-statements-per-line": [2, { "max": 2 }],
28 + "multiline-comment-style": 0,
29 + "no-magic-numbers": 0,
30 + "operator-linebreak": [2, "before"],
31 + "sort-keys": 0,
32 + },
33 +
34 + "overrides": [
35 + {
36 + "files": "test/**",
37 + "rules": {
38 + "max-lines-per-function": 0,
39 + "new-cap": 0,
40 + },
41 + },
42 + ],
43 +}
1 +# These are supported funding model platforms
2 +
3 +github: [ljharb]
4 +patreon: # Replace with a single Patreon username
5 +open_collective: # Replace with a single Open Collective username
6 +ko_fi: # Replace with a single Ko-fi username
7 +tidelift: npm/get-intrinsic
8 +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 +liberapay: # Replace with a single Liberapay username
10 +issuehunt: # Replace with a single IssueHunt username
11 +otechie: # Replace with a single Otechie username
12 +custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
1 +{
2 + "all": true,
3 + "check-coverage": false,
4 + "reporter": ["text-summary", "text", "html", "json"],
5 + "lines": 86,
6 + "statements": 85.93,
7 + "functions": 82.43,
8 + "branches": 76.06,
9 + "exclude": [
10 + "coverage",
11 + "test"
12 + ]
13 +}
1 +# Changelog
2 +
3 +All notable changes to this project will be documented in this file.
4 +
5 +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
6 +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7 +
8 +## [v1.1.1](https://github.com/ljharb/get-intrinsic/compare/v1.1.0...v1.1.1) - 2021-02-03
9 +
10 +### Fixed
11 +
12 +- [meta] export `./package.json` [`#9`](https://github.com/ljharb/get-intrinsic/issues/9)
13 +
14 +### Commits
15 +
16 +- [readme] flesh out the readme; use `evalmd` [`d12f12c`](https://github.com/ljharb/get-intrinsic/commit/d12f12c15345a0a0772cc65a7c64369529abd614)
17 +- [eslint] set up proper globals config [`5a8c098`](https://github.com/ljharb/get-intrinsic/commit/5a8c0984e3319d1ac0e64b102f8ec18b64e79f36)
18 +- [Dev Deps] update `eslint` [`7b9a5c0`](https://github.com/ljharb/get-intrinsic/commit/7b9a5c0d31a90ca1a1234181c74988fb046701cd)
19 +
20 +## [v1.1.0](https://github.com/ljharb/get-intrinsic/compare/v1.0.2...v1.1.0) - 2021-01-25
21 +
22 +### Fixed
23 +
24 +- [Refactor] delay `Function` eval until syntax-derived values are requested [`#3`](https://github.com/ljharb/get-intrinsic/issues/3)
25 +
26 +### Commits
27 +
28 +- [Tests] migrate tests to Github Actions [`2ab762b`](https://github.com/ljharb/get-intrinsic/commit/2ab762b48164aea8af37a40ba105bbc8246ab8c4)
29 +- [meta] do not publish github action workflow files [`5e7108e`](https://github.com/ljharb/get-intrinsic/commit/5e7108e4768b244d48d9567ba4f8a6cab9c65b8e)
30 +- [Tests] add some coverage [`01ac7a8`](https://github.com/ljharb/get-intrinsic/commit/01ac7a87ac29738567e8524cd8c9e026b1fa8cb3)
31 +- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `call-bind`, `es-abstract`, `tape`; add `call-bind` [`911b672`](https://github.com/ljharb/get-intrinsic/commit/911b672fbffae433a96924c6ce013585e425f4b7)
32 +- [Refactor] rearrange evalled constructors a bit [`7e7e4bf`](https://github.com/ljharb/get-intrinsic/commit/7e7e4bf583f3799c8ac1c6c5e10d2cb553957347)
33 +- [meta] add Automatic Rebase and Require Allow Edits workflows [`0199968`](https://github.com/ljharb/get-intrinsic/commit/01999687a263ffce0a3cb011dfbcb761754aedbc)
34 +
35 +## [v1.0.2](https://github.com/ljharb/get-intrinsic/compare/v1.0.1...v1.0.2) - 2020-12-17
36 +
37 +### Commits
38 +
39 +- [Fix] Throw for non‑existent intrinsics [`68f873b`](https://github.com/ljharb/get-intrinsic/commit/68f873b013c732a05ad6f5fc54f697e55515461b)
40 +- [Fix] Throw for non‑existent segments in the intrinsic path [`8325dee`](https://github.com/ljharb/get-intrinsic/commit/8325deee43128f3654d3399aa9591741ebe17b21)
41 +- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `aud`, `has-bigints`, `object-inspect` [`0c227a7`](https://github.com/ljharb/get-intrinsic/commit/0c227a7d8b629166f25715fd242553892e458525)
42 +- [meta] do not lint coverage output [`70d2419`](https://github.com/ljharb/get-intrinsic/commit/70d24199b620043cd9110fc5f426d214ebe21dc9)
43 +
44 +## [v1.0.1](https://github.com/ljharb/get-intrinsic/compare/v1.0.0...v1.0.1) - 2020-10-30
45 +
46 +### Commits
47 +
48 +- [Tests] gather coverage data on every job [`d1d280d`](https://github.com/ljharb/get-intrinsic/commit/d1d280dec714e3f0519cc877dbcb193057d9cac6)
49 +- [Fix] add missing dependencies [`5031771`](https://github.com/ljharb/get-intrinsic/commit/5031771bb1095b38be88ce7c41d5de88718e432e)
50 +- [Tests] use `es-value-fixtures` [`af48765`](https://github.com/ljharb/get-intrinsic/commit/af48765a23c5323fb0b6b38dbf00eb5099c7bebc)
51 +
52 +## v1.0.0 - 2020-10-29
53 +
54 +### Commits
55 +
56 +- Implementation [`bbce57c`](https://github.com/ljharb/get-intrinsic/commit/bbce57c6f33d05b2d8d3efa273ceeb3ee01127bb)
57 +- Tests [`17b4f0d`](https://github.com/ljharb/get-intrinsic/commit/17b4f0d56dea6b4059b56fc30ef3ee4d9500ebc2)
58 +- Initial commit [`3153294`](https://github.com/ljharb/get-intrinsic/commit/31532948de363b0a27dd9fd4649e7b7028ec4b44)
59 +- npm init [`fb326c4`](https://github.com/ljharb/get-intrinsic/commit/fb326c4d2817c8419ec31de1295f06bb268a7902)
60 +- [meta] add Automatic Rebase and Require Allow Edits workflows [`48862fb`](https://github.com/ljharb/get-intrinsic/commit/48862fb2508c8f6a57968e6d08b7c883afc9d550)
61 +- [meta] add `auto-changelog` [`5f28ad0`](https://github.com/ljharb/get-intrinsic/commit/5f28ad019e060a353d8028f9f2591a9cc93074a1)
62 +- [meta] add "funding"; create `FUNDING.yml` [`c2bbdde`](https://github.com/ljharb/get-intrinsic/commit/c2bbddeba73a875be61484ee4680b129a6d4e0a1)
63 +- [Tests] add `npm run lint` [`0a84b98`](https://github.com/ljharb/get-intrinsic/commit/0a84b98b22b7cf7a748666f705b0003a493c35fd)
64 +- Only apps should have lockfiles [`9586c75`](https://github.com/ljharb/get-intrinsic/commit/9586c75866c1ee678e4d5d4dbbdef6997e511b05)
1 +MIT License
2 +
3 +Copyright (c) 2020 Jordan Harband
4 +
5 +Permission is hereby granted, free of charge, to any person obtaining a copy
6 +of this software and associated documentation files (the "Software"), to deal
7 +in the Software without restriction, including without limitation the rights
8 +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 +copies of the Software, and to permit persons to whom the Software is
10 +furnished to do so, subject to the following conditions:
11 +
12 +The above copyright notice and this permission notice shall be included in all
13 +copies or substantial portions of the Software.
14 +
15 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 +SOFTWARE.
1 +# get-intrinsic <sup>[![Version Badge][npm-version-svg]][package-url]</sup>
2 +
3 +[![dependency status][deps-svg]][deps-url]
4 +[![dev dependency status][dev-deps-svg]][dev-deps-url]
5 +[![License][license-image]][license-url]
6 +[![Downloads][downloads-image]][downloads-url]
7 +
8 +[![npm badge][npm-badge-png]][package-url]
9 +
10 +Get and robustly cache all JS language-level intrinsics at first require time.
11 +
12 +See the syntax described [in the JS spec](https://tc39.es/ecma262/#sec-well-known-intrinsic-objects) for reference.
13 +
14 +## Example
15 +
16 +```js
17 +var GetIntrinsic = require('get-intrinsic');
18 +var assert = require('assert');
19 +
20 +// static methods
21 +assert.equal(GetIntrinsic('%Math.pow%'), Math.pow);
22 +assert.equal(Math.pow(2, 3), 8);
23 +assert.equal(GetIntrinsic('%Math.pow%')(2, 3), 8);
24 +delete Math.pow;
25 +assert.equal(GetIntrinsic('%Math.pow%')(2, 3), 8);
26 +
27 +// instance methods
28 +var arr = [1];
29 +assert.equal(GetIntrinsic('%Array.prototype.push%'), Array.prototype.push);
30 +assert.deepEqual(arr, [1]);
31 +
32 +arr.push(2);
33 +assert.deepEqual(arr, [1, 2]);
34 +
35 +GetIntrinsic('%Array.prototype.push%').call(arr, 3);
36 +assert.deepEqual(arr, [1, 2, 3]);
37 +
38 +delete Array.prototype.push;
39 +GetIntrinsic('%Array.prototype.push%').call(arr, 4);
40 +assert.deepEqual(arr, [1, 2, 3, 4]);
41 +
42 +// missing features
43 +delete JSON.parse; // to simulate a real intrinsic that is missing in the environment
44 +assert.throws(() => GetIntrinsic('%JSON.parse%'));
45 +assert.equal(undefined, GetIntrinsic('%JSON.parse%', true));
46 +```
47 +
48 +## Tests
49 +Simply clone the repo, `npm install`, and run `npm test`
50 +
51 +## Security
52 +
53 +Please email [@ljharb](https://github.com/ljharb) or see https://tidelift.com/security if you have a potential security vulnerability to report.
54 +
55 +[package-url]: https://npmjs.org/package/get-intrinsic
56 +[npm-version-svg]: http://versionbadg.es/ljharb/get-intrinsic.svg
57 +[deps-svg]: https://david-dm.org/ljharb/get-intrinsic.svg
58 +[deps-url]: https://david-dm.org/ljharb/get-intrinsic
59 +[dev-deps-svg]: https://david-dm.org/ljharb/get-intrinsic/dev-status.svg
60 +[dev-deps-url]: https://david-dm.org/ljharb/get-intrinsic#info=devDependencies
61 +[npm-badge-png]: https://nodei.co/npm/get-intrinsic.png?downloads=true&stars=true
62 +[license-image]: https://img.shields.io/npm/l/get-intrinsic.svg
63 +[license-url]: LICENSE
64 +[downloads-image]: https://img.shields.io/npm/dm/get-intrinsic.svg
65 +[downloads-url]: https://npm-stat.com/charts.html?package=get-intrinsic
1 +'use strict';
2 +
3 +var undefined;
4 +
5 +var $SyntaxError = SyntaxError;
6 +var $Function = Function;
7 +var $TypeError = TypeError;
8 +
9 +// eslint-disable-next-line consistent-return
10 +var getEvalledConstructor = function (expressionSyntax) {
11 + try {
12 + return $Function('"use strict"; return (' + expressionSyntax + ').constructor;')();
13 + } catch (e) {}
14 +};
15 +
16 +var $gOPD = Object.getOwnPropertyDescriptor;
17 +if ($gOPD) {
18 + try {
19 + $gOPD({}, '');
20 + } catch (e) {
21 + $gOPD = null; // this is IE 8, which has a broken gOPD
22 + }
23 +}
24 +
25 +var throwTypeError = function () {
26 + throw new $TypeError();
27 +};
28 +var ThrowTypeError = $gOPD
29 + ? (function () {
30 + try {
31 + // eslint-disable-next-line no-unused-expressions, no-caller, no-restricted-properties
32 + arguments.callee; // IE 8 does not throw here
33 + return throwTypeError;
34 + } catch (calleeThrows) {
35 + try {
36 + // IE 8 throws on Object.getOwnPropertyDescriptor(arguments, '')
37 + return $gOPD(arguments, 'callee').get;
38 + } catch (gOPDthrows) {
39 + return throwTypeError;
40 + }
41 + }
42 + }())
43 + : throwTypeError;
44 +
45 +var hasSymbols = require('has-symbols')();
46 +
47 +var getProto = Object.getPrototypeOf || function (x) { return x.__proto__; }; // eslint-disable-line no-proto
48 +
49 +var needsEval = {};
50 +
51 +var TypedArray = typeof Uint8Array === 'undefined' ? undefined : getProto(Uint8Array);
52 +
53 +var INTRINSICS = {
54 + '%AggregateError%': typeof AggregateError === 'undefined' ? undefined : AggregateError,
55 + '%Array%': Array,
56 + '%ArrayBuffer%': typeof ArrayBuffer === 'undefined' ? undefined : ArrayBuffer,
57 + '%ArrayIteratorPrototype%': hasSymbols ? getProto([][Symbol.iterator]()) : undefined,
58 + '%AsyncFromSyncIteratorPrototype%': undefined,
59 + '%AsyncFunction%': needsEval,
60 + '%AsyncGenerator%': needsEval,
61 + '%AsyncGeneratorFunction%': needsEval,
62 + '%AsyncIteratorPrototype%': needsEval,
63 + '%Atomics%': typeof Atomics === 'undefined' ? undefined : Atomics,
64 + '%BigInt%': typeof BigInt === 'undefined' ? undefined : BigInt,
65 + '%Boolean%': Boolean,
66 + '%DataView%': typeof DataView === 'undefined' ? undefined : DataView,
67 + '%Date%': Date,
68 + '%decodeURI%': decodeURI,
69 + '%decodeURIComponent%': decodeURIComponent,
70 + '%encodeURI%': encodeURI,
71 + '%encodeURIComponent%': encodeURIComponent,
72 + '%Error%': Error,
73 + '%eval%': eval, // eslint-disable-line no-eval
74 + '%EvalError%': EvalError,
75 + '%Float32Array%': typeof Float32Array === 'undefined' ? undefined : Float32Array,
76 + '%Float64Array%': typeof Float64Array === 'undefined' ? undefined : Float64Array,
77 + '%FinalizationRegistry%': typeof FinalizationRegistry === 'undefined' ? undefined : FinalizationRegistry,
78 + '%Function%': $Function,
79 + '%GeneratorFunction%': needsEval,
80 + '%Int8Array%': typeof Int8Array === 'undefined' ? undefined : Int8Array,
81 + '%Int16Array%': typeof Int16Array === 'undefined' ? undefined : Int16Array,
82 + '%Int32Array%': typeof Int32Array === 'undefined' ? undefined : Int32Array,
83 + '%isFinite%': isFinite,
84 + '%isNaN%': isNaN,
85 + '%IteratorPrototype%': hasSymbols ? getProto(getProto([][Symbol.iterator]())) : undefined,
86 + '%JSON%': typeof JSON === 'object' ? JSON : undefined,
87 + '%Map%': typeof Map === 'undefined' ? undefined : Map,
88 + '%MapIteratorPrototype%': typeof Map === 'undefined' || !hasSymbols ? undefined : getProto(new Map()[Symbol.iterator]()),
89 + '%Math%': Math,
90 + '%Number%': Number,
91 + '%Object%': Object,
92 + '%parseFloat%': parseFloat,
93 + '%parseInt%': parseInt,
94 + '%Promise%': typeof Promise === 'undefined' ? undefined : Promise,
95 + '%Proxy%': typeof Proxy === 'undefined' ? undefined : Proxy,
96 + '%RangeError%': RangeError,
97 + '%ReferenceError%': ReferenceError,
98 + '%Reflect%': typeof Reflect === 'undefined' ? undefined : Reflect,
99 + '%RegExp%': RegExp,
100 + '%Set%': typeof Set === 'undefined' ? undefined : Set,
101 + '%SetIteratorPrototype%': typeof Set === 'undefined' || !hasSymbols ? undefined : getProto(new Set()[Symbol.iterator]()),
102 + '%SharedArrayBuffer%': typeof SharedArrayBuffer === 'undefined' ? undefined : SharedArrayBuffer,
103 + '%String%': String,
104 + '%StringIteratorPrototype%': hasSymbols ? getProto(''[Symbol.iterator]()) : undefined,
105 + '%Symbol%': hasSymbols ? Symbol : undefined,
106 + '%SyntaxError%': $SyntaxError,
107 + '%ThrowTypeError%': ThrowTypeError,
108 + '%TypedArray%': TypedArray,
109 + '%TypeError%': $TypeError,
110 + '%Uint8Array%': typeof Uint8Array === 'undefined' ? undefined : Uint8Array,
111 + '%Uint8ClampedArray%': typeof Uint8ClampedArray === 'undefined' ? undefined : Uint8ClampedArray,
112 + '%Uint16Array%': typeof Uint16Array === 'undefined' ? undefined : Uint16Array,
113 + '%Uint32Array%': typeof Uint32Array === 'undefined' ? undefined : Uint32Array,
114 + '%URIError%': URIError,
115 + '%WeakMap%': typeof WeakMap === 'undefined' ? undefined : WeakMap,
116 + '%WeakRef%': typeof WeakRef === 'undefined' ? undefined : WeakRef,
117 + '%WeakSet%': typeof WeakSet === 'undefined' ? undefined : WeakSet
118 +};
119 +
120 +var doEval = function doEval(name) {
121 + var value;
122 + if (name === '%AsyncFunction%') {
123 + value = getEvalledConstructor('async function () {}');
124 + } else if (name === '%GeneratorFunction%') {
125 + value = getEvalledConstructor('function* () {}');
126 + } else if (name === '%AsyncGeneratorFunction%') {
127 + value = getEvalledConstructor('async function* () {}');
128 + } else if (name === '%AsyncGenerator%') {
129 + var fn = doEval('%AsyncGeneratorFunction%');
130 + if (fn) {
131 + value = fn.prototype;
132 + }
133 + } else if (name === '%AsyncIteratorPrototype%') {
134 + var gen = doEval('%AsyncGenerator%');
135 + if (gen) {
136 + value = getProto(gen.prototype);
137 + }
138 + }
139 +
140 + INTRINSICS[name] = value;
141 +
142 + return value;
143 +};
144 +
145 +var LEGACY_ALIASES = {
146 + '%ArrayBufferPrototype%': ['ArrayBuffer', 'prototype'],
147 + '%ArrayPrototype%': ['Array', 'prototype'],
148 + '%ArrayProto_entries%': ['Array', 'prototype', 'entries'],
149 + '%ArrayProto_forEach%': ['Array', 'prototype', 'forEach'],
150 + '%ArrayProto_keys%': ['Array', 'prototype', 'keys'],
151 + '%ArrayProto_values%': ['Array', 'prototype', 'values'],
152 + '%AsyncFunctionPrototype%': ['AsyncFunction', 'prototype'],
153 + '%AsyncGenerator%': ['AsyncGeneratorFunction', 'prototype'],
154 + '%AsyncGeneratorPrototype%': ['AsyncGeneratorFunction', 'prototype', 'prototype'],
155 + '%BooleanPrototype%': ['Boolean', 'prototype'],
156 + '%DataViewPrototype%': ['DataView', 'prototype'],
157 + '%DatePrototype%': ['Date', 'prototype'],
158 + '%ErrorPrototype%': ['Error', 'prototype'],
159 + '%EvalErrorPrototype%': ['EvalError', 'prototype'],
160 + '%Float32ArrayPrototype%': ['Float32Array', 'prototype'],
161 + '%Float64ArrayPrototype%': ['Float64Array', 'prototype'],
162 + '%FunctionPrototype%': ['Function', 'prototype'],
163 + '%Generator%': ['GeneratorFunction', 'prototype'],
164 + '%GeneratorPrototype%': ['GeneratorFunction', 'prototype', 'prototype'],
165 + '%Int8ArrayPrototype%': ['Int8Array', 'prototype'],
166 + '%Int16ArrayPrototype%': ['Int16Array', 'prototype'],
167 + '%Int32ArrayPrototype%': ['Int32Array', 'prototype'],
168 + '%JSONParse%': ['JSON', 'parse'],
169 + '%JSONStringify%': ['JSON', 'stringify'],
170 + '%MapPrototype%': ['Map', 'prototype'],
171 + '%NumberPrototype%': ['Number', 'prototype'],
172 + '%ObjectPrototype%': ['Object', 'prototype'],
173 + '%ObjProto_toString%': ['Object', 'prototype', 'toString'],
174 + '%ObjProto_valueOf%': ['Object', 'prototype', 'valueOf'],
175 + '%PromisePrototype%': ['Promise', 'prototype'],
176 + '%PromiseProto_then%': ['Promise', 'prototype', 'then'],
177 + '%Promise_all%': ['Promise', 'all'],
178 + '%Promise_reject%': ['Promise', 'reject'],
179 + '%Promise_resolve%': ['Promise', 'resolve'],
180 + '%RangeErrorPrototype%': ['RangeError', 'prototype'],
181 + '%ReferenceErrorPrototype%': ['ReferenceError', 'prototype'],
182 + '%RegExpPrototype%': ['RegExp', 'prototype'],
183 + '%SetPrototype%': ['Set', 'prototype'],
184 + '%SharedArrayBufferPrototype%': ['SharedArrayBuffer', 'prototype'],
185 + '%StringPrototype%': ['String', 'prototype'],
186 + '%SymbolPrototype%': ['Symbol', 'prototype'],
187 + '%SyntaxErrorPrototype%': ['SyntaxError', 'prototype'],
188 + '%TypedArrayPrototype%': ['TypedArray', 'prototype'],
189 + '%TypeErrorPrototype%': ['TypeError', 'prototype'],
190 + '%Uint8ArrayPrototype%': ['Uint8Array', 'prototype'],
191 + '%Uint8ClampedArrayPrototype%': ['Uint8ClampedArray', 'prototype'],
192 + '%Uint16ArrayPrototype%': ['Uint16Array', 'prototype'],
193 + '%Uint32ArrayPrototype%': ['Uint32Array', 'prototype'],
194 + '%URIErrorPrototype%': ['URIError', 'prototype'],
195 + '%WeakMapPrototype%': ['WeakMap', 'prototype'],
196 + '%WeakSetPrototype%': ['WeakSet', 'prototype']
197 +};
198 +
199 +var bind = require('function-bind');
200 +var hasOwn = require('has');
201 +var $concat = bind.call(Function.call, Array.prototype.concat);
202 +var $spliceApply = bind.call(Function.apply, Array.prototype.splice);
203 +var $replace = bind.call(Function.call, String.prototype.replace);
204 +var $strSlice = bind.call(Function.call, String.prototype.slice);
205 +
206 +/* adapted from https://github.com/lodash/lodash/blob/4.17.15/dist/lodash.js#L6735-L6744 */
207 +var rePropName = /[^%.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|%$))/g;
208 +var reEscapeChar = /\\(\\)?/g; /** Used to match backslashes in property paths. */
209 +var stringToPath = function stringToPath(string) {
210 + var first = $strSlice(string, 0, 1);
211 + var last = $strSlice(string, -1);
212 + if (first === '%' && last !== '%') {
213 + throw new $SyntaxError('invalid intrinsic syntax, expected closing `%`');
214 + } else if (last === '%' && first !== '%') {
215 + throw new $SyntaxError('invalid intrinsic syntax, expected opening `%`');
216 + }
217 + var result = [];
218 + $replace(string, rePropName, function (match, number, quote, subString) {
219 + result[result.length] = quote ? $replace(subString, reEscapeChar, '$1') : number || match;
220 + });
221 + return result;
222 +};
223 +/* end adaptation */
224 +
225 +var getBaseIntrinsic = function getBaseIntrinsic(name, allowMissing) {
226 + var intrinsicName = name;
227 + var alias;
228 + if (hasOwn(LEGACY_ALIASES, intrinsicName)) {
229 + alias = LEGACY_ALIASES[intrinsicName];
230 + intrinsicName = '%' + alias[0] + '%';
231 + }
232 +
233 + if (hasOwn(INTRINSICS, intrinsicName)) {
234 + var value = INTRINSICS[intrinsicName];
235 + if (value === needsEval) {
236 + value = doEval(intrinsicName);
237 + }
238 + if (typeof value === 'undefined' && !allowMissing) {
239 + throw new $TypeError('intrinsic ' + name + ' exists, but is not available. Please file an issue!');
240 + }
241 +
242 + return {
243 + alias: alias,
244 + name: intrinsicName,
245 + value: value
246 + };
247 + }
248 +
249 + throw new $SyntaxError('intrinsic ' + name + ' does not exist!');
250 +};
251 +
252 +module.exports = function GetIntrinsic(name, allowMissing) {
253 + if (typeof name !== 'string' || name.length === 0) {
254 + throw new $TypeError('intrinsic name must be a non-empty string');
255 + }
256 + if (arguments.length > 1 && typeof allowMissing !== 'boolean') {
257 + throw new $TypeError('"allowMissing" argument must be a boolean');
258 + }
259 +
260 + var parts = stringToPath(name);
261 + var intrinsicBaseName = parts.length > 0 ? parts[0] : '';
262 +
263 + var intrinsic = getBaseIntrinsic('%' + intrinsicBaseName + '%', allowMissing);
264 + var intrinsicRealName = intrinsic.name;
265 + var value = intrinsic.value;
266 + var skipFurtherCaching = false;
267 +
268 + var alias = intrinsic.alias;
269 + if (alias) {
270 + intrinsicBaseName = alias[0];
271 + $spliceApply(parts, $concat([0, 1], alias));
272 + }
273 +
274 + for (var i = 1, isOwn = true; i < parts.length; i += 1) {
275 + var part = parts[i];
276 + var first = $strSlice(part, 0, 1);
277 + var last = $strSlice(part, -1);
278 + if (
279 + (
280 + (first === '"' || first === "'" || first === '`')
281 + || (last === '"' || last === "'" || last === '`')
282 + )
283 + && first !== last
284 + ) {
285 + throw new $SyntaxError('property names with quotes must have matching quotes');
286 + }
287 + if (part === 'constructor' || !isOwn) {
288 + skipFurtherCaching = true;
289 + }
290 +
291 + intrinsicBaseName += '.' + part;
292 + intrinsicRealName = '%' + intrinsicBaseName + '%';
293 +
294 + if (hasOwn(INTRINSICS, intrinsicRealName)) {
295 + value = INTRINSICS[intrinsicRealName];
296 + } else if (value != null) {
297 + if (!(part in value)) {
298 + if (!allowMissing) {
299 + throw new $TypeError('base intrinsic for ' + name + ' exists, but the property is not available.');
300 + }
301 + return void undefined;
302 + }
303 + if ($gOPD && (i + 1) >= parts.length) {
304 + var desc = $gOPD(value, part);
305 + isOwn = !!desc;
306 +
307 + // By convention, when a data property is converted to an accessor
308 + // property to emulate a data property that does not suffer from
309 + // the override mistake, that accessor's getter is marked with
310 + // an `originalValue` property. Here, when we detect this, we
311 + // uphold the illusion by pretending to see that original data
312 + // property, i.e., returning the value rather than the getter
313 + // itself.
314 + if (isOwn && 'get' in desc && !('originalValue' in desc.get)) {
315 + value = desc.get;
316 + } else {
317 + value = value[part];
318 + }
319 + } else {
320 + isOwn = hasOwn(value, part);
321 + value = value[part];
322 + }
323 +
324 + if (isOwn && !skipFurtherCaching) {
325 + INTRINSICS[intrinsicRealName] = value;
326 + }
327 + }
328 + }
329 + return value;
330 +};
1 +{
2 + "name": "get-intrinsic",
3 + "version": "1.1.1",
4 + "description": "Get and robustly cache all JS language-level intrinsics at first require time",
5 + "main": "index.js",
6 + "exports": {
7 + ".": [
8 + {
9 + "default": "./index.js"
10 + },
11 + "./index.js"
12 + ],
13 + "./package.json": "./package.json"
14 + },
15 + "scripts": {
16 + "prelint": "evalmd README.md",
17 + "lint": "eslint --ext=.js,.mjs .",
18 + "pretest": "npm run lint",
19 + "tests-only": "nyc tape 'test/**/*.js'",
20 + "test": "npm run tests-only",
21 + "posttest": "aud --production",
22 + "version": "auto-changelog && git add CHANGELOG.md",
23 + "postversion": "auto-changelog && git add CHANGELOG.md && git commit --no-edit --amend && git tag -f \"v$(node -e \"console.log(require('./package.json').version)\")\""
24 + },
25 + "repository": {
26 + "type": "git",
27 + "url": "git+https://github.com/ljharb/get-intrinsic.git"
28 + },
29 + "keywords": [
30 + "javascript",
31 + "ecmascript",
32 + "es",
33 + "js",
34 + "intrinsic",
35 + "getintrinsic",
36 + "es-abstract"
37 + ],
38 + "author": "Jordan Harband <ljharb@gmail.com>",
39 + "funding": {
40 + "url": "https://github.com/sponsors/ljharb"
41 + },
42 + "license": "MIT",
43 + "bugs": {
44 + "url": "https://github.com/ljharb/get-intrinsic/issues"
45 + },
46 + "homepage": "https://github.com/ljharb/get-intrinsic#readme",
47 + "devDependencies": {
48 + "@ljharb/eslint-config": "^17.5.0",
49 + "aud": "^1.1.3",
50 + "auto-changelog": "^2.2.1",
51 + "call-bind": "^1.0.2",
52 + "es-abstract": "^1.18.0-next.2",
53 + "es-value-fixtures": "^1.0.0",
54 + "eslint": "^7.19.0",
55 + "evalmd": "^0.0.19",
56 + "foreach": "^2.0.5",
57 + "has-bigints": "^1.0.1",
58 + "make-async-function": "^1.0.0",
59 + "make-async-generator-function": "^1.0.0",
60 + "make-generator-function": "^2.0.0",
61 + "nyc": "^10.3.2",
62 + "object-inspect": "^1.9.0",
63 + "tape": "^5.1.1"
64 + },
65 + "auto-changelog": {
66 + "output": "CHANGELOG.md",
67 + "template": "keepachangelog",
68 + "unreleased": false,
69 + "commitLimit": false,
70 + "backfillLimit": false,
71 + "hideCredit": true
72 + },
73 + "dependencies": {
74 + "function-bind": "^1.1.1",
75 + "has": "^1.0.3",
76 + "has-symbols": "^1.0.1"
77 + }
78 +}
1 +'use strict';
2 +
3 +var GetIntrinsic = require('../');
4 +
5 +var test = require('tape');
6 +var forEach = require('foreach');
7 +var debug = require('object-inspect');
8 +var generatorFns = require('make-generator-function')();
9 +var asyncFns = require('make-async-function').list();
10 +var asyncGenFns = require('make-async-generator-function')();
11 +
12 +var callBound = require('call-bind/callBound');
13 +var v = require('es-value-fixtures');
14 +var $gOPD = require('es-abstract/helpers/getOwnPropertyDescriptor');
15 +var defineProperty = require('es-abstract/test/helpers/defineProperty');
16 +
17 +var $isProto = callBound('%Object.prototype.isPrototypeOf%');
18 +
19 +test('export', function (t) {
20 + t.equal(typeof GetIntrinsic, 'function', 'it is a function');
21 + t.equal(GetIntrinsic.length, 2, 'function has length of 2');
22 +
23 + t.end();
24 +});
25 +
26 +test('throws', function (t) {
27 + t['throws'](
28 + function () { GetIntrinsic('not an intrinsic'); },
29 + SyntaxError,
30 + 'nonexistent intrinsic throws a syntax error'
31 + );
32 +
33 + t['throws'](
34 + function () { GetIntrinsic(''); },
35 + TypeError,
36 + 'empty string intrinsic throws a type error'
37 + );
38 +
39 + t['throws'](
40 + function () { GetIntrinsic('.'); },
41 + SyntaxError,
42 + '"just a dot" intrinsic throws a syntax error'
43 + );
44 +
45 + t['throws'](
46 + function () { GetIntrinsic('%String'); },
47 + SyntaxError,
48 + 'Leading % without trailing % throws a syntax error'
49 + );
50 +
51 + t['throws'](
52 + function () { GetIntrinsic('String%'); },
53 + SyntaxError,
54 + 'Trailing % without leading % throws a syntax error'
55 + );
56 +
57 + t['throws'](
58 + function () { GetIntrinsic("String['prototype]"); },
59 + SyntaxError,
60 + 'Dynamic property access is disallowed for intrinsics (unterminated string)'
61 + );
62 +
63 + t['throws'](
64 + function () { GetIntrinsic('%Proxy.prototype.undefined%'); },
65 + TypeError,
66 + "Throws when middle part doesn't exist (%Proxy.prototype.undefined%)"
67 + );
68 +
69 + forEach(v.nonStrings, function (nonString) {
70 + t['throws'](
71 + function () { GetIntrinsic(nonString); },
72 + TypeError,
73 + debug(nonString) + ' is not a String'
74 + );
75 + });
76 +
77 + forEach(v.nonBooleans, function (nonBoolean) {
78 + t['throws'](
79 + function () { GetIntrinsic('%', nonBoolean); },
80 + TypeError,
81 + debug(nonBoolean) + ' is not a Boolean'
82 + );
83 + });
84 +
85 + forEach([
86 + 'toString',
87 + 'propertyIsEnumerable',
88 + 'hasOwnProperty'
89 + ], function (objectProtoMember) {
90 + t['throws'](
91 + function () { GetIntrinsic(objectProtoMember); },
92 + SyntaxError,
93 + debug(objectProtoMember) + ' is not an intrinsic'
94 + );
95 + });
96 +
97 + t.end();
98 +});
99 +
100 +test('base intrinsics', function (t) {
101 + t.equal(GetIntrinsic('%Object%'), Object, '%Object% yields Object');
102 + t.equal(GetIntrinsic('Object'), Object, 'Object yields Object');
103 + t.equal(GetIntrinsic('%Array%'), Array, '%Array% yields Array');
104 + t.equal(GetIntrinsic('Array'), Array, 'Array yields Array');
105 +
106 + t.end();
107 +});
108 +
109 +test('dotted paths', function (t) {
110 + t.equal(GetIntrinsic('%Object.prototype.toString%'), Object.prototype.toString, '%Object.prototype.toString% yields Object.prototype.toString');
111 + t.equal(GetIntrinsic('Object.prototype.toString'), Object.prototype.toString, 'Object.prototype.toString yields Object.prototype.toString');
112 + t.equal(GetIntrinsic('%Array.prototype.push%'), Array.prototype.push, '%Array.prototype.push% yields Array.prototype.push');
113 + t.equal(GetIntrinsic('Array.prototype.push'), Array.prototype.push, 'Array.prototype.push yields Array.prototype.push');
114 +
115 + test('underscore paths are aliases for dotted paths', { skip: !Object.isFrozen || Object.isFrozen(Object.prototype) }, function (st) {
116 + var original = GetIntrinsic('%ObjProto_toString%');
117 +
118 + forEach([
119 + '%Object.prototype.toString%',
120 + 'Object.prototype.toString',
121 + '%ObjectPrototype.toString%',
122 + 'ObjectPrototype.toString',
123 + '%ObjProto_toString%',
124 + 'ObjProto_toString'
125 + ], function (name) {
126 + defineProperty(Object.prototype, 'toString', {
127 + value: function toString() {
128 + return original.apply(this, arguments);
129 + }
130 + });
131 + st.equal(GetIntrinsic(name), original, name + ' yields original Object.prototype.toString');
132 + });
133 +
134 + defineProperty(Object.prototype, 'toString', { value: original });
135 + st.end();
136 + });
137 +
138 + test('dotted paths cache', { skip: !Object.isFrozen || Object.isFrozen(Object.prototype) }, function (st) {
139 + var original = GetIntrinsic('%Object.prototype.propertyIsEnumerable%');
140 +
141 + forEach([
142 + '%Object.prototype.propertyIsEnumerable%',
143 + 'Object.prototype.propertyIsEnumerable',
144 + '%ObjectPrototype.propertyIsEnumerable%',
145 + 'ObjectPrototype.propertyIsEnumerable'
146 + ], function (name) {
147 + // eslint-disable-next-line no-extend-native
148 + Object.prototype.propertyIsEnumerable = function propertyIsEnumerable() {
149 + return original.apply(this, arguments);
150 + };
151 + st.equal(GetIntrinsic(name), original, name + ' yields cached Object.prototype.propertyIsEnumerable');
152 + });
153 +
154 + // eslint-disable-next-line no-extend-native
155 + Object.prototype.propertyIsEnumerable = original;
156 + st.end();
157 + });
158 +
159 + test('dotted path reports correct error', function (st) {
160 + st['throws'](function () {
161 + GetIntrinsic('%NonExistentIntrinsic.prototype.property%');
162 + }, /%NonExistentIntrinsic%/, 'The base intrinsic of %NonExistentIntrinsic.prototype.property% is %NonExistentIntrinsic%');
163 +
164 + st['throws'](function () {
165 + GetIntrinsic('%NonExistentIntrinsicPrototype.property%');
166 + }, /%NonExistentIntrinsicPrototype%/, 'The base intrinsic of %NonExistentIntrinsicPrototype.property% is %NonExistentIntrinsicPrototype%');
167 +
168 + st.end();
169 + });
170 +
171 + t.end();
172 +});
173 +
174 +test('accessors', { skip: !$gOPD || typeof Map !== 'function' }, function (t) {
175 + var actual = $gOPD(Map.prototype, 'size');
176 + t.ok(actual, 'Map.prototype.size has a descriptor');
177 + t.equal(typeof actual.get, 'function', 'Map.prototype.size has a getter function');
178 + t.equal(GetIntrinsic('%Map.prototype.size%'), actual.get, '%Map.prototype.size% yields the getter for it');
179 + t.equal(GetIntrinsic('Map.prototype.size'), actual.get, 'Map.prototype.size yields the getter for it');
180 +
181 + t.end();
182 +});
183 +
184 +test('generator functions', { skip: !generatorFns.length }, function (t) {
185 + var $GeneratorFunction = GetIntrinsic('%GeneratorFunction%');
186 + var $GeneratorFunctionPrototype = GetIntrinsic('%Generator%');
187 + var $GeneratorPrototype = GetIntrinsic('%GeneratorPrototype%');
188 +
189 + forEach(generatorFns, function (genFn) {
190 + var fnName = genFn.name;
191 + fnName = fnName ? "'" + fnName + "'" : 'genFn';
192 +
193 + t.ok(genFn instanceof $GeneratorFunction, fnName + ' instanceof %GeneratorFunction%');
194 + t.ok($isProto($GeneratorFunctionPrototype, genFn), '%Generator% is prototype of ' + fnName);
195 + t.ok($isProto($GeneratorPrototype, genFn.prototype), '%GeneratorPrototype% is prototype of ' + fnName + '.prototype');
196 + });
197 +
198 + t.end();
199 +});
200 +
201 +test('async functions', { skip: !asyncFns.length }, function (t) {
202 + var $AsyncFunction = GetIntrinsic('%AsyncFunction%');
203 + var $AsyncFunctionPrototype = GetIntrinsic('%AsyncFunctionPrototype%');
204 +
205 + forEach(asyncFns, function (asyncFn) {
206 + var fnName = asyncFn.name;
207 + fnName = fnName ? "'" + fnName + "'" : 'asyncFn';
208 +
209 + t.ok(asyncFn instanceof $AsyncFunction, fnName + ' instanceof %AsyncFunction%');
210 + t.ok($isProto($AsyncFunctionPrototype, asyncFn), '%AsyncFunctionPrototype% is prototype of ' + fnName);
211 + });
212 +
213 + t.end();
214 +});
215 +
216 +test('async generator functions', { skip: asyncGenFns.length === 0 }, function (t) {
217 + var $AsyncGeneratorFunction = GetIntrinsic('%AsyncGeneratorFunction%');
218 + var $AsyncGeneratorFunctionPrototype = GetIntrinsic('%AsyncGenerator%');
219 + var $AsyncGeneratorPrototype = GetIntrinsic('%AsyncGeneratorPrototype%');
220 +
221 + forEach(asyncGenFns, function (asyncGenFn) {
222 + var fnName = asyncGenFn.name;
223 + fnName = fnName ? "'" + fnName + "'" : 'asyncGenFn';
224 +
225 + t.ok(asyncGenFn instanceof $AsyncGeneratorFunction, fnName + ' instanceof %AsyncGeneratorFunction%');
226 + t.ok($isProto($AsyncGeneratorFunctionPrototype, asyncGenFn), '%AsyncGenerator% is prototype of ' + fnName);
227 + t.ok($isProto($AsyncGeneratorPrototype, asyncGenFn.prototype), '%AsyncGeneratorPrototype% is prototype of ' + fnName + '.prototype');
228 + });
229 +
230 + t.end();
231 +});
232 +
233 +test('%ThrowTypeError%', function (t) {
234 + var $ThrowTypeError = GetIntrinsic('%ThrowTypeError%');
235 +
236 + t.equal(typeof $ThrowTypeError, 'function', 'is a function');
237 + t['throws'](
238 + $ThrowTypeError,
239 + TypeError,
240 + '%ThrowTypeError% throws a TypeError'
241 + );
242 +
243 + t.end();
244 +});
245 +
246 +test('allowMissing', { skip: asyncGenFns.length > 0 }, function (t) {
247 + t['throws'](
248 + function () { GetIntrinsic('%AsyncGeneratorPrototype%'); },
249 + TypeError,
250 + 'throws when missing'
251 + );
252 +
253 + t.equal(
254 + GetIntrinsic('%AsyncGeneratorPrototype%', true),
255 + undefined,
256 + 'does not throw when allowMissing'
257 + );
258 +
259 + t.end();
260 +});
1 +{
2 + "root": true,
3 +
4 + "extends": "@ljharb",
5 +
6 + "rules": {
7 + "max-statements-per-line": [2, { "max": 2 }],
8 + "no-magic-numbers": 0,
9 + "multiline-comment-style": 0,
10 + }
11 +}
1 +# These are supported funding model platforms
2 +
3 +github: [ljharb]
4 +patreon: # Replace with a single Patreon username
5 +open_collective: # Replace with a single Open Collective username
6 +ko_fi: # Replace with a single Ko-fi username
7 +tidelift: npm/has-symbols
8 +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 +liberapay: # Replace with a single Liberapay username
10 +issuehunt: # Replace with a single IssueHunt username
11 +otechie: # Replace with a single Otechie username
12 +custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
1 +{
2 + "all": true,
3 + "check-coverage": false,
4 + "reporter": ["text-summary", "text", "html", "json"],
5 + "lines": 86,
6 + "statements": 85.93,
7 + "functions": 82.43,
8 + "branches": 76.06,
9 + "exclude": [
10 + "coverage",
11 + "test"
12 + ]
13 +}
1 +# Changelog
2 +
3 +All notable changes to this project will be documented in this file.
4 +
5 +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
6 +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7 +
8 +## [v1.0.2](https://github.com/inspect-js/has-symbols/compare/v1.0.1...v1.0.2) - 2021-02-27
9 +
10 +### Fixed
11 +
12 +- [Fix] use a universal way to get the original Symbol [`#11`](https://github.com/inspect-js/has-symbols/issues/11)
13 +
14 +### Commits
15 +
16 +- [Tests] migrate tests to Github Actions [`90ae798`](https://github.com/inspect-js/has-symbols/commit/90ae79820bdfe7bc703d67f5f3c5e205f98556d3)
17 +- [meta] do not publish github action workflow files [`29e60a1`](https://github.com/inspect-js/has-symbols/commit/29e60a1b7c25c7f1acf7acff4a9320d0d10c49b4)
18 +- [Tests] run `nyc` on all tests [`8476b91`](https://github.com/inspect-js/has-symbols/commit/8476b915650d360915abe2522505abf4b0e8f0ae)
19 +- [readme] fix repo URLs, remove defunct badges [`126288e`](https://github.com/inspect-js/has-symbols/commit/126288ecc1797c0a40247a6b78bcb2e0bc5d7036)
20 +- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `aud`, `auto-changelog`, `core-js`, `get-own-property-symbols` [`d84bdfa`](https://github.com/inspect-js/has-symbols/commit/d84bdfa48ac5188abbb4904b42614cd6c030940a)
21 +- [Tests] fix linting errors [`0df3070`](https://github.com/inspect-js/has-symbols/commit/0df3070b981b6c9f2ee530c09189a7f5c6def839)
22 +- [actions] add "Allow Edits" workflow [`1e6bc29`](https://github.com/inspect-js/has-symbols/commit/1e6bc29b188f32b9648657b07eda08504be5aa9c)
23 +- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `tape` [`36cea2a`](https://github.com/inspect-js/has-symbols/commit/36cea2addd4e6ec435f35a2656b4e9ef82498e9b)
24 +- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `aud`, `tape` [`1278338`](https://github.com/inspect-js/has-symbols/commit/127833801865fbc2cc8979beb9ca869c7bfe8222)
25 +- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `aud`, `tape` [`1493254`](https://github.com/inspect-js/has-symbols/commit/1493254eda13db5fb8fc5e4a3e8324b3d196029d)
26 +- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `core-js` [`b090bf2`](https://github.com/inspect-js/has-symbols/commit/b090bf214d3679a30edc1e2d729d466ab5183e1d)
27 +- [actions] switch Automatic Rebase workflow to `pull_request_target` event [`4addb7a`](https://github.com/inspect-js/has-symbols/commit/4addb7ab4dc73f927ae99928d68817554fc21dc0)
28 +- [Dev Deps] update `auto-changelog`, `tape` [`81d0baf`](https://github.com/inspect-js/has-symbols/commit/81d0baf3816096a89a8558e8043895f7a7d10d8b)
29 +- [Dev Deps] update `auto-changelog`; add `aud` [`1a4e561`](https://github.com/inspect-js/has-symbols/commit/1a4e5612c25d91c3a03d509721d02630bc4fe3da)
30 +- [readme] remove unused testling URLs [`3000941`](https://github.com/inspect-js/has-symbols/commit/3000941f958046e923ed8152edb1ef4a599e6fcc)
31 +- [Tests] only audit prod deps [`692e974`](https://github.com/inspect-js/has-symbols/commit/692e9743c912410e9440207631a643a34b4741a1)
32 +- [Dev Deps] update `@ljharb/eslint-config` [`51c946c`](https://github.com/inspect-js/has-symbols/commit/51c946c7f6baa793ec5390bb5a45cdce16b4ba76)
33 +
34 +## [v1.0.1](https://github.com/inspect-js/has-symbols/compare/v1.0.0...v1.0.1) - 2019-11-16
35 +
36 +### Commits
37 +
38 +- [Tests] use shared travis-ci configs [`ce396c9`](https://github.com/inspect-js/has-symbols/commit/ce396c9419ff11c43d0da5d05cdbb79f7fb42229)
39 +- [Tests] up to `node` `v12.4`, `v11.15`, `v10.15`, `v9.11`, `v8.15`, `v7.10`, `v6.17`, `v4.9`; use `nvm install-latest-npm` [`0690732`](https://github.com/inspect-js/has-symbols/commit/0690732801f47ab429f39ba1962f522d5c462d6b)
40 +- [meta] add `auto-changelog` [`2163d0b`](https://github.com/inspect-js/has-symbols/commit/2163d0b7f36343076b8f947cd1667dd1750f26fc)
41 +- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `core-js`, `safe-publish-latest`, `tape` [`8e0951f`](https://github.com/inspect-js/has-symbols/commit/8e0951f1a7a2e52068222b7bb73511761e6e4d9c)
42 +- [actions] add automatic rebasing / merge commit blocking [`b09cdb7`](https://github.com/inspect-js/has-symbols/commit/b09cdb7cd7ee39e7a769878f56e2d6066f5ccd1d)
43 +- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `safe-publish-latest`, `core-js`, `get-own-property-symbols`, `tape` [`1dd42cd`](https://github.com/inspect-js/has-symbols/commit/1dd42cd86183ed0c50f99b1062345c458babca91)
44 +- [meta] create FUNDING.yml [`aa57a17`](https://github.com/inspect-js/has-symbols/commit/aa57a17b19708906d1927f821ea8e73394d84ca4)
45 +- Only apps should have lockfiles [`a2d8bea`](https://github.com/inspect-js/has-symbols/commit/a2d8bea23a97d15c09eaf60f5b107fcf9a4d57aa)
46 +- [Tests] use `npx aud` instead of `nsp` or `npm audit` with hoops [`9e96cb7`](https://github.com/inspect-js/has-symbols/commit/9e96cb783746cbed0c10ef78e599a8eaa7ebe193)
47 +- [meta] add `funding` field [`a0b32cf`](https://github.com/inspect-js/has-symbols/commit/a0b32cf68e803f963c1639b6d47b0a9d6440bab0)
48 +- [Dev Deps] update `safe-publish-latest` [`cb9f0a5`](https://github.com/inspect-js/has-symbols/commit/cb9f0a521a3a1790f1064d437edd33bb6c3d6af0)
49 +
50 +## v1.0.0 - 2016-09-19
51 +
52 +### Commits
53 +
54 +- Tests. [`ecb6eb9`](https://github.com/inspect-js/has-symbols/commit/ecb6eb934e4883137f3f93b965ba5e0a98df430d)
55 +- package.json [`88a337c`](https://github.com/inspect-js/has-symbols/commit/88a337cee0864a0da35f5d19e69ff0ef0150e46a)
56 +- Initial commit [`42e1e55`](https://github.com/inspect-js/has-symbols/commit/42e1e5502536a2b8ac529c9443984acd14836b1c)
57 +- Initial implementation. [`33f5cc6`](https://github.com/inspect-js/has-symbols/commit/33f5cc6cdff86e2194b081ee842bfdc63caf43fb)
58 +- read me [`01f1170`](https://github.com/inspect-js/has-symbols/commit/01f1170188ff7cb1558aa297f6ba5b516c6d7b0c)
1 +MIT License
2 +
3 +Copyright (c) 2016 Jordan Harband
4 +
5 +Permission is hereby granted, free of charge, to any person obtaining a copy
6 +of this software and associated documentation files (the "Software"), to deal
7 +in the Software without restriction, including without limitation the rights
8 +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 +copies of the Software, and to permit persons to whom the Software is
10 +furnished to do so, subject to the following conditions:
11 +
12 +The above copyright notice and this permission notice shall be included in all
13 +copies or substantial portions of the Software.
14 +
15 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 +SOFTWARE.
1 +# has-symbols <sup>[![Version Badge][2]][1]</sup>
2 +
3 +[![dependency status][5]][6]
4 +[![dev dependency status][7]][8]
5 +[![License][license-image]][license-url]
6 +[![Downloads][downloads-image]][downloads-url]
7 +
8 +[![npm badge][11]][1]
9 +
10 +Determine if the JS environment has Symbol support. Supports spec, or shams.
11 +
12 +## Example
13 +
14 +```js
15 +var hasSymbols = require('has-symbols');
16 +
17 +hasSymbols() === true; // if the environment has native Symbol support. Not polyfillable, not forgeable.
18 +
19 +var hasSymbolsKinda = require('has-symbols/shams');
20 +hasSymbolsKinda() === true; // if the environment has a Symbol sham that mostly follows the spec.
21 +```
22 +
23 +## Supported Symbol shams
24 + - get-own-property-symbols [npm](https://www.npmjs.com/package/get-own-property-symbols) | [github](https://github.com/WebReflection/get-own-property-symbols)
25 + - core-js [npm](https://www.npmjs.com/package/core-js) | [github](https://github.com/zloirock/core-js)
26 +
27 +## Tests
28 +Simply clone the repo, `npm install`, and run `npm test`
29 +
30 +[1]: https://npmjs.org/package/has-symbols
31 +[2]: https://versionbadg.es/inspect-js/has-symbols.svg
32 +[5]: https://david-dm.org/inspect-js/has-symbols.svg
33 +[6]: https://david-dm.org/inspect-js/has-symbols
34 +[7]: https://david-dm.org/inspect-js/has-symbols/dev-status.svg
35 +[8]: https://david-dm.org/inspect-js/has-symbols#info=devDependencies
36 +[11]: https://nodei.co/npm/has-symbols.png?downloads=true&stars=true
37 +[license-image]: https://img.shields.io/npm/l/has-symbols.svg
38 +[license-url]: LICENSE
39 +[downloads-image]: https://img.shields.io/npm/dm/has-symbols.svg
40 +[downloads-url]: https://npm-stat.com/charts.html?package=has-symbols
1 +'use strict';
2 +
3 +var origSymbol = typeof Symbol !== 'undefined' && Symbol;
4 +var hasSymbolSham = require('./shams');
5 +
6 +module.exports = function hasNativeSymbols() {
7 + if (typeof origSymbol !== 'function') { return false; }
8 + if (typeof Symbol !== 'function') { return false; }
9 + if (typeof origSymbol('foo') !== 'symbol') { return false; }
10 + if (typeof Symbol('bar') !== 'symbol') { return false; }
11 +
12 + return hasSymbolSham();
13 +};
1 +{
2 + "name": "has-symbols",
3 + "version": "1.0.2",
4 + "author": {
5 + "name": "Jordan Harband",
6 + "email": "ljharb@gmail.com",
7 + "url": "http://ljharb.codes"
8 + },
9 + "funding": {
10 + "url": "https://github.com/sponsors/ljharb"
11 + },
12 + "contributors": [
13 + {
14 + "name": "Jordan Harband",
15 + "email": "ljharb@gmail.com",
16 + "url": "http://ljharb.codes"
17 + }
18 + ],
19 + "description": "Determine if the JS environment has Symbol support. Supports spec, or shams.",
20 + "license": "MIT",
21 + "main": "index.js",
22 + "scripts": {
23 + "prepublish": "safe-publish-latest",
24 + "pretest": "npm run --silent lint",
25 + "test": "npm run tests-only",
26 + "posttest": "aud --production",
27 + "tests-only": "npm run test:stock && npm run test:staging && npm run test:shams",
28 + "test:stock": "nyc node test",
29 + "test:staging": "nyc node --harmony --es-staging test",
30 + "test:shams": "npm run --silent test:shams:getownpropertysymbols && npm run --silent test:shams:corejs",
31 + "test:shams:corejs": "nyc node test/shams/core-js.js",
32 + "test:shams:getownpropertysymbols": "nyc node test/shams/get-own-property-symbols.js",
33 + "lint": "eslint --ext=js,mjs .",
34 + "version": "auto-changelog && git add CHANGELOG.md",
35 + "postversion": "auto-changelog && git add CHANGELOG.md && git commit --no-edit --amend && git tag -f \"v$(node -e \"console.log(require('./package.json').version)\")\""
36 + },
37 + "repository": {
38 + "type": "git",
39 + "url": "git://github.com/inspect-js/has-symbols.git"
40 + },
41 + "keywords": [
42 + "Symbol",
43 + "symbols",
44 + "typeof",
45 + "sham",
46 + "polyfill",
47 + "native",
48 + "core-js",
49 + "ES6"
50 + ],
51 + "devDependencies": {
52 + "@ljharb/eslint-config": "^17.5.1",
53 + "aud": "^1.1.4",
54 + "auto-changelog": "^2.2.1",
55 + "core-js": "^2.6.12",
56 + "eslint": "^7.20.0",
57 + "get-own-property-symbols": "^0.9.5",
58 + "nyc": "^10.3.2",
59 + "safe-publish-latest": "^1.1.4",
60 + "tape": "^5.2.0"
61 + },
62 + "testling": {
63 + "files": "test/index.js",
64 + "browsers": [
65 + "iexplore/6.0..latest",
66 + "firefox/3.0..6.0",
67 + "firefox/15.0..latest",
68 + "firefox/nightly",
69 + "chrome/4.0..10.0",
70 + "chrome/20.0..latest",
71 + "chrome/canary",
72 + "opera/10.0..latest",
73 + "opera/next",
74 + "safari/4.0..latest",
75 + "ipad/6.0..latest",
76 + "iphone/6.0..latest",
77 + "android-browser/4.2"
78 + ]
79 + },
80 + "engines": {
81 + "node": ">= 0.4"
82 + },
83 + "auto-changelog": {
84 + "output": "CHANGELOG.md",
85 + "template": "keepachangelog",
86 + "unreleased": false,
87 + "commitLimit": false,
88 + "backfillLimit": false,
89 + "hideCredit": true
90 + },
91 + "greenkeeper": {
92 + "ignore": [
93 + "core-js"
94 + ]
95 + }
96 +}
1 +'use strict';
2 +
3 +/* eslint complexity: [2, 18], max-statements: [2, 33] */
4 +module.exports = function hasSymbols() {
5 + if (typeof Symbol !== 'function' || typeof Object.getOwnPropertySymbols !== 'function') { return false; }
6 + if (typeof Symbol.iterator === 'symbol') { return true; }
7 +
8 + var obj = {};
9 + var sym = Symbol('test');
10 + var symObj = Object(sym);
11 + if (typeof sym === 'string') { return false; }
12 +
13 + if (Object.prototype.toString.call(sym) !== '[object Symbol]') { return false; }
14 + if (Object.prototype.toString.call(symObj) !== '[object Symbol]') { return false; }
15 +
16 + // temp disabled per https://github.com/ljharb/object.assign/issues/17
17 + // if (sym instanceof Symbol) { return false; }
18 + // temp disabled per https://github.com/WebReflection/get-own-property-symbols/issues/4
19 + // if (!(symObj instanceof Symbol)) { return false; }
20 +
21 + // if (typeof Symbol.prototype.toString !== 'function') { return false; }
22 + // if (String(sym) !== Symbol.prototype.toString.call(sym)) { return false; }
23 +
24 + var symVal = 42;
25 + obj[sym] = symVal;
26 + for (sym in obj) { return false; } // eslint-disable-line no-restricted-syntax, no-unreachable-loop
27 + if (typeof Object.keys === 'function' && Object.keys(obj).length !== 0) { return false; }
28 +
29 + if (typeof Object.getOwnPropertyNames === 'function' && Object.getOwnPropertyNames(obj).length !== 0) { return false; }
30 +
31 + var syms = Object.getOwnPropertySymbols(obj);
32 + if (syms.length !== 1 || syms[0] !== sym) { return false; }
33 +
34 + if (!Object.prototype.propertyIsEnumerable.call(obj, sym)) { return false; }
35 +
36 + if (typeof Object.getOwnPropertyDescriptor === 'function') {
37 + var descriptor = Object.getOwnPropertyDescriptor(obj, sym);
38 + if (descriptor.value !== symVal || descriptor.enumerable !== true) { return false; }
39 + }
40 +
41 + return true;
42 +};
1 +'use strict';
2 +
3 +var test = require('tape');
4 +var hasSymbols = require('../');
5 +var runSymbolTests = require('./tests');
6 +
7 +test('interface', function (t) {
8 + t.equal(typeof hasSymbols, 'function', 'is a function');
9 + t.equal(typeof hasSymbols(), 'boolean', 'returns a boolean');
10 + t.end();
11 +});
12 +
13 +test('Symbols are supported', { skip: !hasSymbols() }, function (t) {
14 + runSymbolTests(t);
15 + t.end();
16 +});
17 +
18 +test('Symbols are not supported', { skip: hasSymbols() }, function (t) {
19 + t.equal(typeof Symbol, 'undefined', 'global Symbol is undefined');
20 + t.equal(typeof Object.getOwnPropertySymbols, 'undefined', 'Object.getOwnPropertySymbols does not exist');
21 + t.end();
22 +});
1 +'use strict';
2 +
3 +var test = require('tape');
4 +
5 +if (typeof Symbol === 'function' && typeof Symbol() === 'symbol') {
6 + test('has native Symbol support', function (t) {
7 + t.equal(typeof Symbol, 'function');
8 + t.equal(typeof Symbol(), 'symbol');
9 + t.end();
10 + });
11 + return;
12 +}
13 +
14 +var hasSymbols = require('../../shams');
15 +
16 +test('polyfilled Symbols', function (t) {
17 + /* eslint-disable global-require */
18 + t.equal(hasSymbols(), false, 'hasSymbols is false before polyfilling');
19 + require('core-js/fn/symbol');
20 + require('core-js/fn/symbol/to-string-tag');
21 +
22 + require('../tests')(t);
23 +
24 + var hasSymbolsAfter = hasSymbols();
25 + t.equal(hasSymbolsAfter, true, 'hasSymbols is true after polyfilling');
26 + /* eslint-enable global-require */
27 + t.end();
28 +});
1 +'use strict';
2 +
3 +var test = require('tape');
4 +
5 +if (typeof Symbol === 'function' && typeof Symbol() === 'symbol') {
6 + test('has native Symbol support', function (t) {
7 + t.equal(typeof Symbol, 'function');
8 + t.equal(typeof Symbol(), 'symbol');
9 + t.end();
10 + });
11 + return;
12 +}
13 +
14 +var hasSymbols = require('../../shams');
15 +
16 +test('polyfilled Symbols', function (t) {
17 + /* eslint-disable global-require */
18 + t.equal(hasSymbols(), false, 'hasSymbols is false before polyfilling');
19 +
20 + require('get-own-property-symbols');
21 +
22 + require('../tests')(t);
23 +
24 + var hasSymbolsAfter = hasSymbols();
25 + t.equal(hasSymbolsAfter, true, 'hasSymbols is true after polyfilling');
26 + /* eslint-enable global-require */
27 + t.end();
28 +});
1 +'use strict';
2 +
3 +// eslint-disable-next-line consistent-return
4 +module.exports = function runSymbolTests(t) {
5 + t.equal(typeof Symbol, 'function', 'global Symbol is a function');
6 +
7 + if (typeof Symbol !== 'function') { return false; }
8 +
9 + t.notEqual(Symbol(), Symbol(), 'two symbols are not equal');
10 +
11 + /*
12 + t.equal(
13 + Symbol.prototype.toString.call(Symbol('foo')),
14 + Symbol.prototype.toString.call(Symbol('foo')),
15 + 'two symbols with the same description stringify the same'
16 + );
17 + */
18 +
19 + /*
20 + var foo = Symbol('foo');
21 +
22 + t.notEqual(
23 + String(foo),
24 + String(Symbol('bar')),
25 + 'two symbols with different descriptions do not stringify the same'
26 + );
27 + */
28 +
29 + t.equal(typeof Symbol.prototype.toString, 'function', 'Symbol#toString is a function');
30 + // t.equal(String(foo), Symbol.prototype.toString.call(foo), 'Symbol#toString equals String of the same symbol');
31 +
32 + t.equal(typeof Object.getOwnPropertySymbols, 'function', 'Object.getOwnPropertySymbols is a function');
33 +
34 + var obj = {};
35 + var sym = Symbol('test');
36 + var symObj = Object(sym);
37 + t.notEqual(typeof sym, 'string', 'Symbol is not a string');
38 + t.equal(Object.prototype.toString.call(sym), '[object Symbol]', 'symbol primitive Object#toStrings properly');
39 + t.equal(Object.prototype.toString.call(symObj), '[object Symbol]', 'symbol primitive Object#toStrings properly');
40 +
41 + var symVal = 42;
42 + obj[sym] = symVal;
43 + // eslint-disable-next-line no-restricted-syntax
44 + for (sym in obj) { t.fail('symbol property key was found in for..in of object'); }
45 +
46 + t.deepEqual(Object.keys(obj), [], 'no enumerable own keys on symbol-valued object');
47 + t.deepEqual(Object.getOwnPropertyNames(obj), [], 'no own names on symbol-valued object');
48 + t.deepEqual(Object.getOwnPropertySymbols(obj), [sym], 'one own symbol on symbol-valued object');
49 + t.equal(Object.prototype.propertyIsEnumerable.call(obj, sym), true, 'symbol is enumerable');
50 + t.deepEqual(Object.getOwnPropertyDescriptor(obj, sym), {
51 + configurable: true,
52 + enumerable: true,
53 + value: 42,
54 + writable: true
55 + }, 'property descriptor is correct');
56 +};
1 +Copyright (c) 2013 Thiago de Arruda
2 +
3 +Permission is hereby granted, free of charge, to any person
4 +obtaining a copy of this software and associated documentation
5 +files (the "Software"), to deal in the Software without
6 +restriction, including without limitation the rights to use,
7 +copy, modify, merge, publish, distribute, sublicense, and/or sell
8 +copies of the Software, and to permit persons to whom the
9 +Software is furnished to do so, subject to the following
10 +conditions:
11 +
12 +The above copyright notice and this permission notice shall be
13 +included in all copies or substantial portions of the Software.
14 +
15 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17 +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19 +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 +OTHER DEALINGS IN THE SOFTWARE.
1 +# has
2 +
3 +> Object.prototype.hasOwnProperty.call shortcut
4 +
5 +## Installation
6 +
7 +```sh
8 +npm install --save has
9 +```
10 +
11 +## Usage
12 +
13 +```js
14 +var has = require('has');
15 +
16 +has({}, 'hasOwnProperty'); // false
17 +has(Object.prototype, 'hasOwnProperty'); // true
18 +```
1 +{
2 + "name": "has",
3 + "description": "Object.prototype.hasOwnProperty.call shortcut",
4 + "version": "1.0.3",
5 + "homepage": "https://github.com/tarruda/has",
6 + "author": {
7 + "name": "Thiago de Arruda",
8 + "email": "tpadilha84@gmail.com"
9 + },
10 + "contributors": [
11 + {
12 + "name": "Jordan Harband",
13 + "email": "ljharb@gmail.com",
14 + "url": "http://ljharb.codes"
15 + }
16 + ],
17 + "repository": {
18 + "type": "git",
19 + "url": "git://github.com/tarruda/has.git"
20 + },
21 + "bugs": {
22 + "url": "https://github.com/tarruda/has/issues"
23 + },
24 + "license": "MIT",
25 + "licenses": [
26 + {
27 + "type": "MIT",
28 + "url": "https://github.com/tarruda/has/blob/master/LICENSE-MIT"
29 + }
30 + ],
31 + "main": "./src",
32 + "dependencies": {
33 + "function-bind": "^1.1.1"
34 + },
35 + "devDependencies": {
36 + "@ljharb/eslint-config": "^12.2.1",
37 + "eslint": "^4.19.1",
38 + "tape": "^4.9.0"
39 + },
40 + "engines": {
41 + "node": ">= 0.4.0"
42 + },
43 + "scripts": {
44 + "lint": "eslint .",
45 + "pretest": "npm run lint",
46 + "test": "tape test"
47 + }
48 +}
1 +'use strict';
2 +
3 +var bind = require('function-bind');
4 +
5 +module.exports = bind.call(Function.call, Object.prototype.hasOwnProperty);
1 +'use strict';
2 +
3 +var test = require('tape');
4 +var has = require('../');
5 +
6 +test('has', function (t) {
7 + t.equal(has({}, 'hasOwnProperty'), false, 'object literal does not have own property "hasOwnProperty"');
8 + t.equal(has(Object.prototype, 'hasOwnProperty'), true, 'Object.prototype has own property "hasOwnProperty"');
9 + t.end();
10 +});
1 +{
2 + "root": true,
3 +
4 + "extends": "@ljharb",
5 +}
1 +# These are supported funding model platforms
2 +
3 +github: [ljharb]
4 +patreon: # Replace with a single Patreon username
5 +open_collective: # Replace with a single Open Collective username
6 +ko_fi: # Replace with a single Ko-fi username
7 +tidelift: npm/is-nan
8 +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 +liberapay: # Replace with a single Liberapay username
10 +issuehunt: # Replace with a single IssueHunt username
11 +otechie: # Replace with a single Otechie username
12 +custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
1 +{
2 + "all": true,
3 + "check-coverage": false,
4 + "reporter": ["text-summary", "text", "html", "json"],
5 + "lines": 86,
6 + "statements": 85.93,
7 + "functions": 82.43,
8 + "branches": 76.06,
9 + "exclude": [
10 + "coverage",
11 + "test"
12 + ]
13 +}
1 +1.3.2 / 2020-12-23
2 +=================
3 + * [meta] do not publish github action workflow files
4 +
5 +1.3.1 / 2020-12-23
6 +=================
7 + * [Fix] avoid mutating `Number.isNaN` when present
8 + * [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `es5-shim`, `tape`, `functions-have-names`; add `safe-publish-latest`
9 + * [readme] fix repo URLs, remove defunct badges
10 + * [Tests] migrate tests to Github Actions
11 + * [Tests] add `implementation` est; run `es-shim-api` in postlint; use `tape` runner
12 + * [actions] add "Allow Edits" workflow
13 + * [actions] switch Automatic Rebase workflow to `pull_request_target` event
14 +
15 +1.3.0 / 2019-12-15
16 +=================
17 + * [New] add "auto" entry point
18 + * [readme] fix repo URLs
19 + * [docs] clean up README links/formatting
20 + * [Deps] update `define-properties`
21 + * [meta] add `funding` field; FUNDING.yml
22 + * [meta] Only apps should have lockfiles
23 + * [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `covert`, `es5-shim`, `tape`, `@es-shims/api`
24 + * [Tests] use shared travis-ci configs
25 + * [Tests] use `npx aud` instead of `nsp` or `npm audit` with hoops
26 + * [Tests] use `functions-have-names`
27 + * [Tests] remove `jscs`
28 + * [actions] add automatic rebasing / merge commit blocking
29 +
30 +1.2.1 / 2015-08-16
31 +=================
32 + * [Docs] Update readme
33 +
34 +1.2.0 / 2015-08-16
35 +=================
36 + * [New] Implement the [es-shim API](es-shims/api) interface
37 + * [Dev Deps] update `eslint`, `tape`, `es5-shim`, `@ljharb/eslint-config`
38 + * [Tests] up to `io.js` `v3.0`
39 + * [Docs] Switch from vb.teelaun.ch to versionbadg.es for the npm version badge SVG
40 + * [Security] Add `npm run security`
41 +
42 +1.1.0 / 2015-06-24
43 +=================
44 + * Add a "shim" method
45 + * Add `npm run eslint`
46 + * Test latest `node` and `io.js` on `travis-ci`
47 + * Add license and download badges to README
48 + * Update `tape`, `covert`, `jscs`
49 +
50 +1.0.1 / 2014-07-05
51 +=================
52 + * Oops, jscs should be a devDependency
53 +
54 +1.0.0 / 2014-07-05
55 +=================
56 + * Initial release.
1 +The MIT License (MIT)
2 +
3 +Copyright (c) 2014 Jordan Harband
4 +
5 +Permission is hereby granted, free of charge, to any person obtaining a copy of
6 +this software and associated documentation files (the "Software"), to deal in
7 +the Software without restriction, including without limitation the rights to
8 +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 +the Software, and to permit persons to whom the Software is furnished to do so,
10 +subject to the following conditions:
11 +
12 +The above copyright notice and this permission notice shall be included in all
13 +copies or substantial portions of the Software.
14 +
15 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1 +# is-nan <sup>[![Version Badge][2]][1]</sup>
2 +
3 +[![dependency status][5]][6]
4 +[![dev dependency status][7]][8]
5 +[![License][license-image]][license-url]
6 +[![Downloads][downloads-image]][downloads-url]
7 +
8 +[![npm badge][11]][1]
9 +
10 +ES2015-compliant shim for Number.isNaN - the global isNaN returns false positives.
11 +
12 +This package implements the [es-shim API](https://github.com/es-shims/api) interface. It works in an ES3-supported environment and complies with the [spec](http://www.ecma-international.org/ecma-262/6.0/#sec-number.isnan).
13 +
14 +## Example
15 +
16 +```js
17 +Number.isNaN = require('is-nan');
18 +var assert = require('assert');
19 +
20 +assert.notOk(Number.isNaN(undefined));
21 +assert.notOk(Number.isNaN(null));
22 +assert.notOk(Number.isNaN(false));
23 +assert.notOk(Number.isNaN(true));
24 +assert.notOk(Number.isNaN(0));
25 +assert.notOk(Number.isNaN(42));
26 +assert.notOk(Number.isNaN(Infinity));
27 +assert.notOk(Number.isNaN(-Infinity));
28 +assert.notOk(Number.isNaN('foo'));
29 +assert.notOk(Number.isNaN(function () {}));
30 +assert.notOk(Number.isNaN([]));
31 +assert.notOk(Number.isNaN({}));
32 +
33 +assert.ok(Number.isNaN(NaN));
34 +```
35 +
36 +## Tests
37 +Simply clone the repo, `npm install`, and run `npm test`
38 +
39 +[1]: https://npmjs.org/package/is-nan
40 +[2]: https://versionbadg.es/es-shims/is-nan.svg
41 +[5]: https://david-dm.org/es-shims/is-nan.svg
42 +[6]: https://david-dm.org/es-shims/is-nan
43 +[7]: https://david-dm.org/es-shims/is-nan/dev-status.svg
44 +[8]: https://david-dm.org/es-shims/is-nan#info=devDependencies
45 +[11]: https://nodei.co/npm/is-nan.png?downloads=true&stars=true
46 +[license-image]: https://img.shields.io/npm/l/is-nan.svg
47 +[license-url]: LICENSE
48 +[downloads-image]: https://img.shields.io/npm/dm/is-nan.svg
49 +[downloads-url]: https://npm-stat.com/charts.html?package=is-nan
1 +'use strict';
2 +
3 +require('./shim')();
1 +'use strict';
2 +
3 +/* http://www.ecma-international.org/ecma-262/6.0/#sec-number.isnan */
4 +
5 +module.exports = function isNaN(value) {
6 + return value !== value;
7 +};
1 +'use strict';
2 +
3 +var callBind = require('call-bind');
4 +var define = require('define-properties');
5 +
6 +var implementation = require('./implementation');
7 +var getPolyfill = require('./polyfill');
8 +var shim = require('./shim');
9 +
10 +var polyfill = callBind(getPolyfill(), Number);
11 +
12 +/* http://www.ecma-international.org/ecma-262/6.0/#sec-number.isnan */
13 +
14 +define(polyfill, {
15 + getPolyfill: getPolyfill,
16 + implementation: implementation,
17 + shim: shim
18 +});
19 +
20 +module.exports = polyfill;
1 +{
2 + "name": "is-nan",
3 + "version": "1.3.2",
4 + "description": "ES2015-compliant shim for Number.isNaN - the global isNaN returns false positives.",
5 + "author": "Jordan Harband",
6 + "funding": {
7 + "url": "https://github.com/sponsors/ljharb"
8 + },
9 + "license": "MIT",
10 + "main": "index.js",
11 + "scripts": {
12 + "prepublish": "safe-publish-latest",
13 + "pretest": "npm run lint",
14 + "test": "npm run tests-only",
15 + "tests-only": "nyc tape 'test/**/*.js'",
16 + "posttest": "npx aud --production",
17 + "lint": "eslint .",
18 + "postlint": "es-shim-api --bound"
19 + },
20 + "repository": {
21 + "type": "git",
22 + "url": "git://github.com/es-shims/is-nan.git"
23 + },
24 + "bugs": {
25 + "url": "https://github.com/es-shims/is-nan/issues"
26 + },
27 + "homepage": "https://github.com/es-shims/is-nan",
28 + "keywords": [
29 + "is",
30 + "NaN",
31 + "not a number",
32 + "number",
33 + "isNaN",
34 + "ES6",
35 + "ES2015",
36 + "shim",
37 + "polyfill",
38 + "es-shim API"
39 + ],
40 + "dependencies": {
41 + "call-bind": "^1.0.0",
42 + "define-properties": "^1.1.3"
43 + },
44 + "devDependencies": {
45 + "@es-shims/api": "^2.1.2",
46 + "@ljharb/eslint-config": "^17.3.0",
47 + "aud": "^1.1.3",
48 + "es5-shim": "^4.5.14",
49 + "eslint": "^7.16.0",
50 + "functions-have-names": "^1.2.2",
51 + "nyc": "^10.3.2",
52 + "safe-publish-latest": "^1.1.4",
53 + "tape": "^5.0.1"
54 + },
55 + "testling": {
56 + "files": "test.js",
57 + "browsers": [
58 + "iexplore/6.0..latest",
59 + "firefox/3.0..6.0",
60 + "firefox/15.0..latest",
61 + "firefox/nightly",
62 + "chrome/4.0..10.0",
63 + "chrome/20.0..latest",
64 + "chrome/canary",
65 + "opera/10.0..12.0",
66 + "opera/15.0..latest",
67 + "opera/next",
68 + "safari/4.0..latest",
69 + "ipad/6.0..latest",
70 + "iphone/6.0..latest",
71 + "android-browser/4.2"
72 + ]
73 + },
74 + "engines": {
75 + "node": ">= 0.4"
76 + }
77 +}
1 +'use strict';
2 +
3 +var implementation = require('./implementation');
4 +
5 +module.exports = function getPolyfill() {
6 + if (Number.isNaN && Number.isNaN(NaN) && !Number.isNaN('a')) {
7 + return Number.isNaN;
8 + }
9 + return implementation;
10 +};
1 +'use strict';
2 +
3 +var define = require('define-properties');
4 +var getPolyfill = require('./polyfill');
5 +
6 +/* http://www.ecma-international.org/ecma-262/6.0/#sec-number.isnan */
7 +
8 +module.exports = function shimNumberIsNaN() {
9 + var polyfill = getPolyfill();
10 + define(Number, { isNaN: polyfill }, {
11 + isNaN: function testIsNaN() {
12 + return Number.isNaN !== polyfill;
13 + }
14 + });
15 + return polyfill;
16 +};
1 +'use strict';
2 +
3 +var numberIsNaN = require('../');
4 +var test = require('tape');
5 +var runTests = require('./tests');
6 +
7 +test('as a function', function (t) {
8 + runTests(numberIsNaN, t);
9 +
10 + t.end();
11 +});
1 +'use strict';
2 +
3 +require('es5-shim');
4 +
5 +var numberIsNaN = require('../');
6 +numberIsNaN.shim();
7 +
8 +var test = require('tape');
9 +var defineProperties = require('define-properties');
10 +var isEnumerable = Object.prototype.propertyIsEnumerable;
11 +var functionsHaveNames = require('functions-have-names')();
12 +
13 +var runTests = require('./tests');
14 +
15 +test('shimmed', function (t) {
16 + t.equal(Number.isNaN.length, 1, 'Number.isNaN has a length of 1');
17 + t.test('Function name', { skip: !functionsHaveNames }, function (st) {
18 + st.equal(Number.isNaN.name, 'isNaN', 'Number.isNaN has name "isNaN"');
19 + st.end();
20 + });
21 +
22 + t.test('enumerability', { skip: !defineProperties.supportsDescriptors }, function (et) {
23 + et.equal(false, isEnumerable.call(Number, 'isNaN'), 'Number.isNaN is not enumerable');
24 + et.end();
25 + });
26 +
27 + runTests(Number.isNaN, t);
28 +
29 + t.end();
30 +});
1 +'use strict';
2 +
3 +module.exports = function (numberIsNaN, t) {
4 + t.test('not NaN', function (st) {
5 + st.test('primitives', function (sst) {
6 + sst.notOk(numberIsNaN(), 'undefined is not NaN');
7 + sst.notOk(numberIsNaN(null), 'null is not NaN');
8 + sst.notOk(numberIsNaN(false), 'false is not NaN');
9 + sst.notOk(numberIsNaN(true), 'true is not NaN');
10 + sst.notOk(numberIsNaN(0), 'positive zero is not NaN');
11 + sst.notOk(numberIsNaN(Infinity), 'Infinity is not NaN');
12 + sst.notOk(numberIsNaN(-Infinity), '-Infinity is not NaN');
13 + sst.notOk(numberIsNaN('foo'), 'string is not NaN');
14 + sst.notOk(numberIsNaN('NaN'), 'string NaN is not NaN');
15 + sst.end();
16 + });
17 +
18 + st.notOk(numberIsNaN([]), 'array is not NaN');
19 + st.notOk(numberIsNaN({}), 'object is not NaN');
20 + st.notOk(numberIsNaN(function () {}), 'function is not NaN');
21 +
22 + st.test('valueOf', function (vt) {
23 + var obj = {
24 + valueOf: function () {
25 + return NaN;
26 + }
27 + };
28 + vt.ok(numberIsNaN(Number(obj)), 'object with valueOf of NaN, converted to Number, is NaN');
29 + vt.notOk(numberIsNaN(obj), 'object with valueOf of NaN is not NaN');
30 + vt.end();
31 + });
32 +
33 + st.end();
34 + });
35 +
36 + t.test('NaN literal', function (st) {
37 + st.ok(numberIsNaN(NaN), 'NaN is NaN');
38 + st.end();
39 + });
40 +};
1 +# Long timeouts
2 +
3 +Long timeout makes it possible to have a timeout or interval that is longer than 24.8 days (2^31-1 milliseconds).
4 +
5 +
6 +## Usage
7 +
8 +```js
9 +var lt = require('long-timeout')
10 +
11 +var timeout = lt.setTimeout(function() {
12 + console.log('in 30 days')
13 +}, 1000 * 60 * 60 * 24 * 30)
14 +
15 +var interval = lt.setInterval(function() {
16 + console.log('every 30 days')
17 +}, 1000 * 60 * 60 * 24 * 30)
18 +
19 +
20 +// Clear them
21 +lt.clearTimeout(timeout)
22 +lt.clearInterval(interval)
23 +```
24 +
25 +## Install
26 +
27 + npm install long-timeout
28 +
29 +
30 +## Licence
31 +
32 +MIT
1 +var lt = require('./')
2 +
3 +/*
4 + Timeouts
5 + */
6 +lt.setTimeout(function() {
7 + console.log('in a long time')
8 +}, Number.MAX_VALUE)
9 +
10 +lt.setTimeout(function() {
11 + console.log('2 seconds')
12 +}, 2000)
13 +
14 +/*
15 + Intervals
16 + */
17 +lt.setInterval(function() {
18 + console.log('long interval')
19 +}, Number.MAX_VALUE)
20 +
21 +lt.setInterval(function() {
22 + console.log("2 second interval")
23 +}, 2000)
1 +
2 +var TIMEOUT_MAX = 2147483647; // 2^31-1
3 +
4 +exports.setTimeout = function(listener, after) {
5 + return new Timeout(listener, after)
6 +}
7 +exports.setInterval = function(listener, after) {
8 + return new Interval(listener, after)
9 +}
10 +exports.clearTimeout = function(timer) {
11 + if (timer) timer.close()
12 +}
13 +exports.clearInterval = exports.clearTimeout
14 +
15 +exports.Timeout = Timeout
16 +exports.Interval = Interval
17 +
18 +function Timeout(listener, after) {
19 + this.listener = listener
20 + this.after = after
21 + this.unreffed = false
22 + this.start()
23 +}
24 +
25 +Timeout.prototype.unref = function() {
26 + if (!this.unreffed) {
27 + this.unreffed = true
28 + this.timeout.unref()
29 + }
30 +}
31 +
32 +Timeout.prototype.ref = function() {
33 + if (this.unreffed) {
34 + this.unreffed = false
35 + this.timeout.ref()
36 + }
37 +}
38 +
39 +Timeout.prototype.start = function() {
40 + if (this.after <= TIMEOUT_MAX) {
41 + this.timeout = setTimeout(this.listener, this.after)
42 + } else {
43 + var self = this
44 + this.timeout = setTimeout(function() {
45 + self.after -= TIMEOUT_MAX
46 + self.start()
47 + }, TIMEOUT_MAX)
48 + }
49 + if (this.unreffed) {
50 + this.timeout.unref()
51 + }
52 +}
53 +
54 +Timeout.prototype.close = function() {
55 + clearTimeout(this.timeout)
56 +}
57 +
58 +function Interval(listener, after) {
59 + this.listener = listener
60 + this.after = this.timeLeft = after
61 + this.unreffed = false
62 + this.start()
63 +}
64 +
65 +Interval.prototype.unref = function() {
66 + if (!this.unreffed) {
67 + this.unreffed = true
68 + this.timeout.unref()
69 + }
70 +}
71 +
72 +Interval.prototype.ref = function() {
73 + if (this.unreffed) {
74 + this.unreffed = false
75 + this.timeout.ref()
76 + }
77 +}
78 +
79 +Interval.prototype.start = function() {
80 + var self = this
81 +
82 + if (this.timeLeft <= TIMEOUT_MAX) {
83 + this.timeout = setTimeout(function() {
84 + self.listener()
85 + self.timeLeft = self.after
86 + self.start()
87 + }, this.timeLeft)
88 + } else {
89 + this.timeout = setTimeout(function() {
90 + self.timeLeft -= TIMEOUT_MAX
91 + self.start()
92 + }, TIMEOUT_MAX)
93 + }
94 + if (this.unreffed) {
95 + this.timeout.unref()
96 + }
97 +}
98 +
99 +Interval.prototype.close = function() {
100 + Timeout.prototype.close.apply(this, arguments)
101 +}
1 +{
2 + "name": "long-timeout",
3 + "version": "0.1.1",
4 + "description": "Long timeout makes it possible to have a timeout or interval that is longer than 24.8 days (2^31-1 milliseconds).",
5 + "main": "index.js",
6 + "scripts": {
7 + "test": "echo \"Error: no test specified\" && exit 1"
8 + },
9 + "repository": {
10 + "type": "git",
11 + "url": "git://github.com/tellnes/long-timeout.git"
12 + },
13 + "author": "Christian Tellnes <christian@tellnes.no> (http://christian.tellnes.com/)",
14 + "license": "MIT",
15 + "bugs": {
16 + "url": "https://github.com/tellnes/long-timeout/issues"
17 + },
18 + "homepage": "https://github.com/tellnes/long-timeout",
19 + "publishConfig": {
20 + "registry": "https://registry.npmjs.org/"
21 + }
22 +}
1 +# Changelog
2 +
3 +# 1.27.0 (2021-05-08)
4 +
5 + * Fix GMT zone parsing for older versions of Node
6 + * Support multiple units in `toRelative`
7 + * Various documentation updates
8 +
9 +## 1.26.0 (2021-02-13)
10 +
11 + * Add fromISOTime, toISOTime and toMillis to Duration (#803)
12 + * Fix padding of negative years in IsoDate (#871)
13 + * Fix hasSame unit comparison (#798)
14 + * Export VERSION information (#794)
15 + * Durations are considered equal with extra zero units. Fixes #809 (#811)
16 +
17 +## 1.25.0 (2020-08-23)
18 +
19 + * fix fromFormat with Intl formats containing non-breaking spaces
20 + * Support higher precision in ISO milliseconds
21 + * Some fixes for 00:30 timezones
22 + * Fix some throwOnInvalid for invalid Intervals
23 + * Various doc fixes
24 + * Fix Interval#isSame for empty intervals
25 + * Mark package as side effect-free
26 + * Add support for intervals with a large number of seconds
27 +
28 +## 1.24.1 (2020-05-04)
29 +
30 + * Remove erroneous `console.log` call
31 +
32 +## 1.24.0 (2020-05-03)
33 +
34 + * Update polyfills for pollyfilled build
35 +
36 +## 1.23.0 (2020-04-02)
37 +
38 + * Allow minus sign prefix when creating Duration from ISO
39 +
40 +## 1.22.2 (2020-03-25)
41 +
42 + * Added more details to error messages for type errors
43 +
44 +## 1.22.1 (2020-03-19)
45 +
46 + * Added support for ISO basic format to DateTime#toISO
47 +
48 +## 1.22.0 (2020-01-26)
49 +
50 + * Fix setZone's handling of pre-1970 dates with milisecond components
51 + * Fix keepLocalTime for large jumps near the target zone's DST
52 + * Fix cache perf for toRelative()
53 +
54 +## 1.21.3 (2019-11-28)
55 +
56 + * Fix parsing of meridiems in macro tokens in newer versions of v8
57 +
58 +## 1.21.2 (2019-11-18)
59 +
60 + * Fix bug in Chrome Canary that threw off time zone calculations
61 +
62 +## 1.21.1 (2019-11-03)
63 +
64 + * Fix for quarter parsing
65 + * Some documentation updates
66 +
67 +## 1.21.0 (2019-10-30)
68 +
69 + * Added quarter support to the parser
70 + * Fix some rounding issues in ISO formatting
71 +
72 +## 1.20.0 (2019-10-29)
73 +
74 + * Added Duration#mapUnits
75 + * added Interval#toISODate and Interval#toISOTime
76 + * Some documentation fixes
77 +
78 +## 1.19.3
79 +
80 + * Cache offset values
81 + * Fix handling of negative sub 1-hour offsets
82 +
83 +## 1.19.2
84 +
85 + * Speculative fix for Node 6
86 +
87 +## 1.19.1
88 +
89 + * Fix Intl.DateTimeFormat usage for polyfills
90 +
91 +## 1.19.0
92 +
93 + * Interval#splitAt now ignores input dates outside the interval
94 + * Don't allow decimals in DateTime creation
95 +
96 +## 1.18.2
97 +
98 + * Fix handling of decimals in DateTime#plus and #minus
99 +
100 +## 1.18.1
101 +
102 + * Fix validity when adding or subtracting time that exceeds Date max/min boundaries
103 +
104 +## 1.18.0
105 +
106 + * Add support for macro tokens in the parser
107 +
108 +## 1.17.2
109 +
110 + * Fix issue with `toRelative` using `style: short` with plural days
111 +
112 +## 1.17.1
113 +
114 + * Reject out-of-range numbers in DateTime.fromMillis
115 + * Reject 0s in ISO date inputs
116 +
117 +## 1.17.0
118 +
119 + * DateTime.min and DateTime.max throw if they get the wrong kind of arguments
120 + * Fixed throwOnInvalid logic for Interval
121 + * Added `DATETIME_MED_WITH_WEEKDAY` preset
122 +
123 +## 1.16.1
124 +
125 + * Catch errors trying to use Intl in weird versions of IE 11
126 +
127 +## 1.16.0
128 +
129 + * Fixed locale default logic for `DateTime#toFormat("ZZZZ")
130 +
131 +## 1.15.0
132 +
133 + * Added `formatOffset` to Zones
134 +
135 +## 1.14.0
136 +
137 + * Allow the zone argument to Interval.fromISO with duration components
138 + * Ignore the zone argument to Duration factory methods
139 +
140 +## 1.13.3
141 +
142 + * Fix keepLocalTime calculations that span offset changes
143 +
144 +## 1.13.2
145 +
146 + * Fixed ISO formatting for dates > 999
147 +
148 +## 1.13.1
149 +
150 + * Performance improvements for regex parsing
151 +
152 +## 1.13.0
153 +
154 + * Support numberSystem in fromFormat
155 + * Fix validity for bad initial zone specifiers
156 +
157 +## 1.12.1
158 +
159 + * Fix cross-month diffs in some scenarios
160 + * Fix time zone parsing when the time zone isn't at the end
161 + * Memoize IANA zone creation
162 +
163 +## 1.12.0
164 +
165 + * Add some explicit CDN support to the NPM package
166 + * Add week token to duration ISO support
167 + * Lots of cleanup and test coverage changes
168 +
169 +## 1.11.4
170 +
171 + * `setZone("local")` now returns the defaultZone if it is set
172 + * Fixes for the polyfilled build
173 +
174 +## 1.11.3
175 +
176 + * Allow 24:00 in ISO (and other) strings
177 + * Fix some bugs with the typecheck functions like `DateTime.isDateTime()`
178 +
179 +## 1.11.2
180 +
181 + * Fixed handling of some characters in fromFormat literal sections
182 + * Hanlde string values in object arguments to DateTime methods
183 + * Fixed toRelativeCalendar's handling of zones in the base date
184 +
185 +## 1.11.1
186 +
187 + * Fix DateTime#plus() when spanning across AD 100
188 +
189 +## 1.11.0
190 +
191 + * Fix low-year handling for IANA zones
192 + * `DateTime#toLocal()` now uses the default locale
193 + * Fix zero duration formatting
194 + * Many documentation fixes
195 +
196 +## 1.10.0
197 +
198 +- Fix endOf("day") during DSTs (#399)
199 +- Add `Interval#mapEndpoints (#400)
200 +- Add `DateTime#zone` and `Info.normalizeZone` (#404)
201 +
202 +## 1.9.0
203 +
204 +- Add `DateTime#toRelative` and `DateTime#toRelativeCalendar`
205 +
206 +## 1.8.3
207 +
208 +- Allow "UTC" in the zone position of `fromSQL`
209 +- Force `isDateTime` and `isDuration` to return booleans in all cases
210 +
211 +## 1.8.2
212 +
213 +- Trim leading \u200e characters from offset names in Edge 16 and 17
214 +
215 +## 1.8.1
216 +
217 +- Add `DateTime.fromSeconds` and `DateTime#toSeconds`
218 +
219 +## 1.7.1
220 +
221 +- Floor the seconds instead of rounding them when outputting the 'X' format
222 +- Change the options to toLocale to override the configuration (the previous options were essentially ignored)
223 +
224 +## 1.6.2
225 +
226 +- Fixing merge error that resulted in bad error messages
227 +
228 +## 1.6.0
229 +
230 +- **midly breaking** Rework negative durations
231 +- Fix handling weekdays at the end of leap week years
232 +- Add isDuration, isDateTime, and isInterval
233 +- Fix handling of Luxon object arguments passed from other execution contexts
234 +
235 +## 1.5.0
236 +
237 +- Improved error message
238 +- Added DateTime#invalidExplanation, Duration#invalidExplanation, Interval#invalidExplanation to provide more details on invalid objects
239 +
240 +## 1.4.6
241 +
242 +- Cache Intl objects for an 85x speed up on basic operations using non-en locales
243 +
244 +## 1.4.5
245 +
246 +- Fix minified builds
247 +
248 +## 1.4.4
249 +
250 +- Fix hour formatting in RFC822 strings
251 +- Interval.fromISO accepts formats with durations
252 +
253 +## 1.4.3
254 +
255 +Removal accidentally-introduced runtime dependency
256 +
257 +## 1.4.2
258 +
259 +- Handle locale strings with BCP 47 extensions. Especially helpful for environments with funky default locales
260 +- Support for [weekYear]-W[weekNumber] ISO 8601 strings
261 +
262 +## 1.4.1
263 +
264 +- Empty diffs now have all the asked-for units in them, set at 0
265 +- Duration operations perserve the superset of units
266 +
267 +## 1.4.0
268 +
269 +- Add x and X to toFormat for formatting Epoch seconds and Epoch milliseconds
270 +- Parser allows a wider range of IANA zone specifiers
271 +- BREAKING: Etc/GMT+10 is now interpreted as UTC-10, per spec
272 +
273 +## 1.3.3
274 +
275 +Documentation fixes
276 +
277 +## 1.3.2
278 +
279 +- DateTime.fromMillis will throw if passed a non-number
280 +- Fixes for type checking across JS contexts
281 +
282 +## 1.3.1
283 +
284 +- Include milliseconds in Duration#toISO
285 +- Avoid deprecation warning from DateTime#inspect in Node 10
286 +
287 +## 1.3.0
288 +
289 +- **mildly breaking change** Duration.toFormat now floors its outputs instead of rounding them (see #224)
290 +- Added 'floor' option to Duration.toFormat and deprecated the 'round' option
291 +- Added `Dateime.toBSON`
292 +- Fixed infinite loop when passing invalid or zero-length durations to Interval#splitBy
293 +- Added better error handling to Duration.fromObject()
294 +
295 +## 1.2.1
296 +
297 +- 222x speed-up in DateTime creation for non-en locales
298 +- Added `DateTime#toMillis` alias for `DateTime#valueOf`
299 +- Fixed types on zone exports
300 +
301 +## 1.2.0
302 +
303 +- Export Zone classes
304 +- Fix `endOf` and `startOf` for quarters
305 +- Change `toFormat("Z")` to return a number for UTC
306 +- Allow "GTM" as an argument to `setZone`
307 +
308 +## 1.1.0
309 +
310 +- Support for zone names with more than two components
311 +- Fixed long-term-accurate conversions for months
312 +- Added `weeksInWeekYear`
313 +
314 +## 1.0.0
315 +
316 +- The big one-oh. No changes from 0.5.8.
317 +
318 +## 0.5.8
319 +
320 +- Large perf improvements for `DateTime#toFormat()`, when using non-intl numbers
321 +
322 +## 0.5.7
323 +
324 +- Added AMD build to the NPM package
325 +- Large performance improvements to technical formatting (e.g. `DateTime#toISO`)
326 +
327 +## 0.5.6
328 +
329 +- Refactor internals
330 +- Added support for fractional seconds in `Duration.fromISO`
331 +- Added browser global to the NPM package
332 +
333 +## 0.5.5
334 +
335 +- Best-we-can-do fix for `DateTime#toLocaleString()` for fixed-offset zones when showing the zone name in the output
336 +- Fixed `Duration#shiftTo` for unormalized Durations that need a rollup cascade
337 +
338 +## 0.5.4
339 +
340 +- Fix default locales in Node
341 +- Fix prototype to help with React inspection
342 +- Improve REPL output for Durations in Node
343 +
344 +## 0.5.3
345 +
346 +- Remove errant ICU runtime dep (again)
347 +
348 +## 0.5.2
349 +
350 +- Remove comments from minified builds (introduced by 0.5.1)
351 +
352 +## 0.5.1
353 +
354 +- Fixed minified builds (oops)
355 +- Fix computation of fractional parts of diffs
356 +
357 +## 0.5.0
358 +
359 +- `isBefore()` returns true for the end of the interval, consistent with being half-open
360 +- `zoneName` now rturns `null` for invalid DateTimes
361 +- Added quarter support
362 +- Adding a month to Jan 31 gives Feb 28/29
363 +
364 +## 0.4.0
365 +
366 +- Always round down to the nearest millisecond when parsing
367 +
368 +## 0.3.1
369 +
370 +- Fixed `toLocaleString` for fixed-offset zones in the absence of Intl
371 +- Added `Info.isValidIANAZone`
372 +- Made malformed zone specifiers result in invalid DateTime instances
373 +
374 +## 0.3.0
375 +
376 +- Rename DateTime.fromString to DateTime.fromFormat (leaving deprecated DateTime.fromString)
377 +- Rename DateTime.fromStringExplain to DateTime.fromFormatExplain (leaving deprecated DateTime.fromStringExplain)
378 +- Support Etc/GMT IANA zones
379 +- Perf fixes for zones
380 +- Rework build infrastructure
381 +
382 +## 0.2.12
383 +
384 +- Fix DateTime.fromObject's handling of default zones
385 +- Change `keepCalendarTime` to `keepLocalTime`
386 +
387 +## 0.2.11
388 +
389 +- Handle no arguments in `DateTime.min` and `DateTime.max`
390 +- Documentation fixes
391 +
392 +## 0.2.10
393 +
394 +- Fix bug where Durations could sometimes mutate
395 +
396 +## 0.2.9
397 +
398 +- Fix `DateTime.fromMillis(0)` more thoroughly
399 +
400 +## 0.2.8
401 +
402 +- Fix sourcemaps
403 +
404 +## 0.2.7
405 +
406 +- Fix `DateTime.fromMillis(0)`
407 +
408 +## 0.2.6
409 +
410 +- Fix 'h' and 'hh' `toFormat` tokens for midnight
411 +
412 +## 0.2.5
413 +
414 +- Better `shiftTo` behavior for durations with floating point components
415 +
416 +## 0.2.4
417 +
418 +- Fix `toHTTP` to use 24-hour hours
419 +- Tighten up regular expressions
420 +- Various documentation fixes
421 +
422 +## 0.2.3
423 +
424 +- Fixes for `diff` with multiple units
425 +
426 +## 0.2.2
427 +
428 +- Fixes for `fromSQL`, `toSQL`, `toSQLTime`, and `toSQLDate`
429 +- Add `includeOffset` option to `toISO` and `toISOTime`
430 +
431 +## 0.2.1
432 +
433 +- Add `module` field to package.json
434 +
435 +## 0.2.0
436 +
437 +- Remove polyfills from main builds
438 +- Update compilation toolchain to target builds more exactly
439 +- Fix IE in polyfill build
440 +
441 +## 0.1.0
442 +
443 +- Add `.fromSQL`, `#toSQL`, `#toSQLTime`, `#toSQLDate`
444 +- Fix AM/PM parsing
445 +- Major perf improvements
446 +- Default to system locale when using macro formats in `#toFormat`
447 +- `.fromISO` accepts standalone times
448 +- See https://github.com/moment/luxon/issues/93 for important news concerning field accessibility
449 +
450 +## 0.0.22
451 +
452 +- Add 'u' formatting and parsing
453 +- Add 'y', 'yyyyy', and 'yyyyyy' parsing tokens
454 +- Add 'yyyyyy' formatting token
455 +- Better error messages for missing arguments to `DateTime.fromString`
456 +
457 +## 0.0.21
458 +
459 +- Fix zones for Edge
460 +
461 +## 0.0.20
462 +
463 +- Fix `fromISO` to accept various levels of subsecond precision
464 +
465 +## 0.0.19
466 +
467 +- Fixed parsing for ordinals
468 +- Made parsing stricter
469 +
470 +## 0.0.18
471 +
472 +- Fixed formatting for non-hour aligned fixed-offset zones
473 +- Fixed longterm conversion accuracy option in diffs
474 +- Fixed invalid handling in `Interval#set`
475 +
476 +## 0.0.17
477 +
478 +- Fixing formatting for fixed-offset zones
479 +
480 +## 0.0.16
481 +
482 +- Fixes for IE 9 & 10
483 +
484 +## 0.0.15
485 +
486 +- Fixing busted release 0.0.14
487 +
488 +## 0.0.13
489 +
490 +- toLocaleString() and others default to the system's locale
491 +- support for ISO week durations in `Duration.fromISO`
492 +
493 +## 0.0.12
494 +
495 +- Improve non-Intl fallbacks for toLocaleString
496 +- Fix `offsetNameShort` and `offsetNameLong` for non-Intl environments
497 +- Added `weekdayShort`, `weekdayLong`, `monthShort`, `monthLong` DateTime getters
498 +
499 +## 0.0.10
500 +
501 +- Only include build dir in NPM module
502 +
503 +## 0.0.9
504 +
505 +- Move to Moment Github org
506 +
507 +## 0.0.8
508 +
509 +- The local zone can now report its IANA name
510 +- Fixed parsing bug for `yy` and `kk`
511 +- Improved test coverage
512 +
513 +## 0.0.7
514 +
515 +- Added `toLocaleParts`
516 +- Slightly more friendly month/weekday parsing
517 +- Default locale setting
518 +
519 +## 0.0.6
520 +
521 +- Stricter `toJSDate`
522 +- `fromISO` now supports `year` and `year-month` formats
523 +- More graceful degradation in the absence of platform features
524 +
525 +## 0.0.5
526 +
527 +Experimental, but now broadly useful.
1 +# Luxon
2 +
3 +[![MIT License][license-image]][license] [![Build Status][travis-image]][travis-url] [![NPM version][npm-version-image]][npm-url] [![Coverage Status][test-coverage-image]][test-coverage-url] [![Doc coverage][doc-coverage-image]][doc-url] [![PRs welcome][contributing-image]][contributing-url]
4 +
5 +Luxon is a library for working with dates and times in JavaScript.
6 +
7 +```js
8 +DateTime.now().setZone("America/New_York").minus({ weeks: 1 }).endOf("day").toISO();
9 +```
10 +
11 +## Features
12 + * DateTime, Duration, and Interval types.
13 + * Immutable, chainable, unambiguous API.
14 + * Parsing and formatting for common and custom formats.
15 + * Native time zone and Intl support (no locale or tz files).
16 +
17 +## Download/install
18 +
19 +[Download/install instructions](https://moment.github.io/luxon/docs/manual/install.html)
20 +
21 +## Documentation
22 +
23 +* [General documentation][doc-url]
24 +* [API docs](https://moment.github.io/luxon/docs/identifiers.html)
25 +* [Quick tour](https://moment.github.io/luxon/docs/manual/tour.html)
26 +* [For Moment users](https://moment.github.io/luxon/docs/manual/moment.html)
27 +* [Why does Luxon exist?](https://moment.github.io/luxon/docs/manual/why.html)
28 +* [A quick demo](https://moment.github.io/luxon/demo/global.html)
29 +
30 +## Development
31 +
32 +See [contributing](contributing.md).
33 +
34 +![Phasers to stun][phasers-image]
35 +
36 +[license-image]: http://img.shields.io/badge/license-MIT-blue.svg
37 +[license]: license.md
38 +
39 +[travis-url]: http://travis-ci.org/moment/luxon
40 +[travis-image]: https://api.travis-ci.org/moment/luxon.svg?branch=master
41 +
42 +[npm-url]: https://npmjs.org/package/luxon
43 +[npm-version-image]: https://badge.fury.io/js/luxon.svg
44 +
45 +[doc-url]: https://moment.github.io/luxon/docs/
46 +[doc-coverage-image]: https://moment.github.io/luxon/docs/badge.svg
47 +
48 +[test-coverage-url]: https://codecov.io/gh/moment/luxon
49 +[test-coverage-image]: https://codecov.io/gh/moment/luxon/branch/master/graph/badge.svg
50 +
51 +[contributing-url]: https://moment.github.io/luxon/docs/manual/contributing.html
52 +[contributing-image]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg
53 +
54 +[phasers-image]: https://img.shields.io/badge/phasers-stun-brightgreen.svg
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
1 +var luxon=function(e){"use strict";function t(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}function n(e,n,r){return n&&t(e.prototype,n),r&&t(e,r),e}function r(e,t){e.prototype=Object.create(t.prototype),e.prototype.constructor=e,e.__proto__=t}function i(e){return(i=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function o(e,t){return(o=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function a(e,t,n){return(a=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],function(){})),!0}catch(e){return!1}}()?Reflect.construct:function(e,t,n){var r=[null];r.push.apply(r,t);var i=new(Function.bind.apply(e,r));return n&&o(i,n.prototype),i}).apply(null,arguments)}function u(e){var t="function"==typeof Map?new Map:void 0;return(u=function(e){if(null===e||(n=e,-1===Function.toString.call(n).indexOf("[native code]")))return e;var n;if("function"!=typeof e)throw new TypeError("Super expression must either be null or a function");if(void 0!==t){if(t.has(e))return t.get(e);t.set(e,r)}function r(){return a(e,arguments,i(this).constructor)}return r.prototype=Object.create(e.prototype,{constructor:{value:r,enumerable:!1,writable:!0,configurable:!0}}),o(r,e)})(e)}function s(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n<t;n++)r[n]=e[n];return r}function c(e){var t=0;if("undefined"==typeof Symbol||null==e[Symbol.iterator]){if(Array.isArray(e)||(e=function(e,t){if(e){if("string"==typeof e)return s(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(n):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?s(e,t):void 0}}(e)))return function(){return t>=e.length?{done:!0}:{done:!1,value:e[t++]}};throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}return(t=e[Symbol.iterator]()).next.bind(t)}var l=function(e){function t(){return e.apply(this,arguments)||this}return r(t,e),t}(u(Error)),f=function(e){function t(t){return e.call(this,"Invalid DateTime: "+t.toMessage())||this}return r(t,e),t}(l),d=function(e){function t(t){return e.call(this,"Invalid Interval: "+t.toMessage())||this}return r(t,e),t}(l),h=function(e){function t(t){return e.call(this,"Invalid Duration: "+t.toMessage())||this}return r(t,e),t}(l),m=function(e){function t(){return e.apply(this,arguments)||this}return r(t,e),t}(l),y=function(e){function t(t){return e.call(this,"Invalid unit "+t)||this}return r(t,e),t}(l),v=function(e){function t(){return e.apply(this,arguments)||this}return r(t,e),t}(l),g=function(e){function t(){return e.call(this,"Zone is an abstract class")||this}return r(t,e),t}(l),p="numeric",w="short",k="long",b={year:p,month:p,day:p},O={year:p,month:w,day:p},S={year:p,month:w,day:p,weekday:w},T={year:p,month:k,day:p},M={year:p,month:k,day:p,weekday:k},N={hour:p,minute:p},E={hour:p,minute:p,second:p},D={hour:p,minute:p,second:p,timeZoneName:w},I={hour:p,minute:p,second:p,timeZoneName:k},V={hour:p,minute:p,hour12:!1},L={hour:p,minute:p,second:p,hour12:!1},x={hour:p,minute:p,second:p,hour12:!1,timeZoneName:w},C={hour:p,minute:p,second:p,hour12:!1,timeZoneName:k},F={year:p,month:p,day:p,hour:p,minute:p},Z={year:p,month:p,day:p,hour:p,minute:p,second:p},j={year:p,month:w,day:p,hour:p,minute:p},A={year:p,month:w,day:p,hour:p,minute:p,second:p},z={year:p,month:w,day:p,weekday:w,hour:p,minute:p},_={year:p,month:k,day:p,hour:p,minute:p,timeZoneName:w},q={year:p,month:k,day:p,hour:p,minute:p,second:p,timeZoneName:w},H={year:p,month:k,day:p,weekday:k,hour:p,minute:p,timeZoneName:k},U={year:p,month:k,day:p,weekday:k,hour:p,minute:p,second:p,timeZoneName:k};function R(e){return void 0===e}function P(e){return"number"==typeof e}function W(e){return"number"==typeof e&&e%1==0}function J(){try{return"undefined"!=typeof Intl&&Intl.DateTimeFormat}catch(e){return!1}}function Y(){return!R(Intl.DateTimeFormat.prototype.formatToParts)}function G(){try{return"undefined"!=typeof Intl&&!!Intl.RelativeTimeFormat}catch(e){return!1}}function $(e,t,n){if(0!==e.length)return e.reduce(function(e,r){var i=[t(r),r];return e&&n(e[0],i[0])===e[0]?e:i},null)[1]}function B(e,t){return t.reduce(function(t,n){return t[n]=e[n],t},{})}function Q(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function K(e,t,n){return W(e)&&e>=t&&e<=n}function X(e,t){void 0===t&&(t=2);var n=e<0?"-":"",r=n?-1*e:e;return""+n+(r.toString().length<t?("0".repeat(t)+r).slice(-t):r.toString())}function ee(e){return R(e)||null===e||""===e?void 0:parseInt(e,10)}function te(e){if(!R(e)&&null!==e&&""!==e){var t=1e3*parseFloat("0."+e);return Math.floor(t)}}function ne(e,t,n){void 0===n&&(n=!1);var r=Math.pow(10,t);return(n?Math.trunc:Math.round)(e*r)/r}function re(e){return e%4==0&&(e%100!=0||e%400==0)}function ie(e){return re(e)?366:365}function oe(e,t){var n,r,i=(n=t-1)-(r=12)*Math.floor(n/r)+1;return 2===i?re(e+(t-i)/12)?29:28:[31,null,31,30,31,30,31,31,30,31,30,31][i-1]}function ae(e){var t=Date.UTC(e.year,e.month-1,e.day,e.hour,e.minute,e.second,e.millisecond);return e.year<100&&e.year>=0&&(t=new Date(t)).setUTCFullYear(t.getUTCFullYear()-1900),+t}function ue(e){var t=(e+Math.floor(e/4)-Math.floor(e/100)+Math.floor(e/400))%7,n=e-1,r=(n+Math.floor(n/4)-Math.floor(n/100)+Math.floor(n/400))%7;return 4===t||3===r?53:52}function se(e){return e>99?e:e>60?1900+e:2e3+e}function ce(e,t,n,r){void 0===r&&(r=null);var i=new Date(e),o={hour12:!1,year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit"};r&&(o.timeZone=r);var a=Object.assign({timeZoneName:t},o),u=J();if(u&&Y()){var s=new Intl.DateTimeFormat(n,a).formatToParts(i).find(function(e){return"timezonename"===e.type.toLowerCase()});return s?s.value:null}if(u){var c=new Intl.DateTimeFormat(n,o).format(i);return new Intl.DateTimeFormat(n,a).format(i).substring(c.length).replace(/^[, \u200e]+/,"")}return null}function le(e,t){var n=parseInt(e,10);Number.isNaN(n)&&(n=0);var r=parseInt(t,10)||0;return 60*n+(n<0||Object.is(n,-0)?-r:r)}function fe(e){var t=Number(e);if("boolean"==typeof e||""===e||Number.isNaN(t))throw new v("Invalid unit value "+e);return t}function de(e,t,n){var r={};for(var i in e)if(Q(e,i)){if(n.indexOf(i)>=0)continue;var o=e[i];if(void 0===o||null===o)continue;r[t(i)]=fe(o)}return r}function he(e,t){var n=Math.trunc(Math.abs(e/60)),r=Math.trunc(Math.abs(e%60)),i=e>=0?"+":"-";switch(t){case"short":return""+i+X(n,2)+":"+X(r,2);case"narrow":return""+i+n+(r>0?":"+r:"");case"techie":return""+i+X(n,2)+X(r,2);default:throw new RangeError("Value format "+t+" is out of range for property format")}}function me(e){return B(e,["hour","minute","second","millisecond"])}var ye=/[A-Za-z_+-]{1,256}(:?\/[A-Za-z_+-]{1,256}(\/[A-Za-z_+-]{1,256})?)?/;function ve(e){return JSON.stringify(e,Object.keys(e).sort())}var ge=["January","February","March","April","May","June","July","August","September","October","November","December"],pe=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],we=["J","F","M","A","M","J","J","A","S","O","N","D"];function ke(e){switch(e){case"narrow":return[].concat(we);case"short":return[].concat(pe);case"long":return[].concat(ge);case"numeric":return["1","2","3","4","5","6","7","8","9","10","11","12"];case"2-digit":return["01","02","03","04","05","06","07","08","09","10","11","12"];default:return null}}var be=["Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"],Oe=["Mon","Tue","Wed","Thu","Fri","Sat","Sun"],Se=["M","T","W","T","F","S","S"];function Te(e){switch(e){case"narrow":return[].concat(Se);case"short":return[].concat(Oe);case"long":return[].concat(be);case"numeric":return["1","2","3","4","5","6","7"];default:return null}}var Me=["AM","PM"],Ne=["Before Christ","Anno Domini"],Ee=["BC","AD"],De=["B","A"];function Ie(e){switch(e){case"narrow":return[].concat(De);case"short":return[].concat(Ee);case"long":return[].concat(Ne);default:return null}}function Ve(e,t){for(var n,r="",i=c(e);!(n=i()).done;){var o=n.value;o.literal?r+=o.val:r+=t(o.val)}return r}var Le={D:b,DD:O,DDD:T,DDDD:M,t:N,tt:E,ttt:D,tttt:I,T:V,TT:L,TTT:x,TTTT:C,f:F,ff:j,fff:_,ffff:H,F:Z,FF:A,FFF:q,FFFF:U},xe=function(){function e(e,t){this.opts=t,this.loc=e,this.systemLoc=null}e.create=function(t,n){return void 0===n&&(n={}),new e(t,n)},e.parseFormat=function(e){for(var t=null,n="",r=!1,i=[],o=0;o<e.length;o++){var a=e.charAt(o);"'"===a?(n.length>0&&i.push({literal:r,val:n}),t=null,n="",r=!r):r?n+=a:a===t?n+=a:(n.length>0&&i.push({literal:!1,val:n}),n=a,t=a)}return n.length>0&&i.push({literal:r,val:n}),i},e.macroTokenToFormatOpts=function(e){return Le[e]};var t=e.prototype;return t.formatWithSystemDefault=function(e,t){return null===this.systemLoc&&(this.systemLoc=this.loc.redefaultToSystem()),this.systemLoc.dtFormatter(e,Object.assign({},this.opts,t)).format()},t.formatDateTime=function(e,t){return void 0===t&&(t={}),this.loc.dtFormatter(e,Object.assign({},this.opts,t)).format()},t.formatDateTimeParts=function(e,t){return void 0===t&&(t={}),this.loc.dtFormatter(e,Object.assign({},this.opts,t)).formatToParts()},t.resolvedOptions=function(e,t){return void 0===t&&(t={}),this.loc.dtFormatter(e,Object.assign({},this.opts,t)).resolvedOptions()},t.num=function(e,t){if(void 0===t&&(t=0),this.opts.forceSimple)return X(e,t);var n=Object.assign({},this.opts);return t>0&&(n.padTo=t),this.loc.numberFormatter(n).format(e)},t.formatDateTimeFromString=function(t,n){var r=this,i="en"===this.loc.listingMode(),o=this.loc.outputCalendar&&"gregory"!==this.loc.outputCalendar&&Y(),a=function(e,n){return r.loc.extract(t,e,n)},u=function(e){return t.isOffsetFixed&&0===t.offset&&e.allowZ?"Z":t.isValid?t.zone.formatOffset(t.ts,e.format):""},s=function(){return i?function(e){return Me[e.hour<12?0:1]}(t):a({hour:"numeric",hour12:!0},"dayperiod")},c=function(e,n){return i?function(e,t){return ke(t)[e.month-1]}(t,e):a(n?{month:e}:{month:e,day:"numeric"},"month")},l=function(e,n){return i?function(e,t){return Te(t)[e.weekday-1]}(t,e):a(n?{weekday:e}:{weekday:e,month:"long",day:"numeric"},"weekday")},f=function(e){return i?function(e,t){return Ie(t)[e.year<0?0:1]}(t,e):a({era:e},"era")};return Ve(e.parseFormat(n),function(n){switch(n){case"S":return r.num(t.millisecond);case"u":case"SSS":return r.num(t.millisecond,3);case"s":return r.num(t.second);case"ss":return r.num(t.second,2);case"m":return r.num(t.minute);case"mm":return r.num(t.minute,2);case"h":return r.num(t.hour%12==0?12:t.hour%12);case"hh":return r.num(t.hour%12==0?12:t.hour%12,2);case"H":return r.num(t.hour);case"HH":return r.num(t.hour,2);case"Z":return u({format:"narrow",allowZ:r.opts.allowZ});case"ZZ":return u({format:"short",allowZ:r.opts.allowZ});case"ZZZ":return u({format:"techie",allowZ:r.opts.allowZ});case"ZZZZ":return t.zone.offsetName(t.ts,{format:"short",locale:r.loc.locale});case"ZZZZZ":return t.zone.offsetName(t.ts,{format:"long",locale:r.loc.locale});case"z":return t.zoneName;case"a":return s();case"d":return o?a({day:"numeric"},"day"):r.num(t.day);case"dd":return o?a({day:"2-digit"},"day"):r.num(t.day,2);case"c":return r.num(t.weekday);case"ccc":return l("short",!0);case"cccc":return l("long",!0);case"ccccc":return l("narrow",!0);case"E":return r.num(t.weekday);case"EEE":return l("short",!1);case"EEEE":return l("long",!1);case"EEEEE":return l("narrow",!1);case"L":return o?a({month:"numeric",day:"numeric"},"month"):r.num(t.month);case"LL":return o?a({month:"2-digit",day:"numeric"},"month"):r.num(t.month,2);case"LLL":return c("short",!0);case"LLLL":return c("long",!0);case"LLLLL":return c("narrow",!0);case"M":return o?a({month:"numeric"},"month"):r.num(t.month);case"MM":return o?a({month:"2-digit"},"month"):r.num(t.month,2);case"MMM":return c("short",!1);case"MMMM":return c("long",!1);case"MMMMM":return c("narrow",!1);case"y":return o?a({year:"numeric"},"year"):r.num(t.year);case"yy":return o?a({year:"2-digit"},"year"):r.num(t.year.toString().slice(-2),2);case"yyyy":return o?a({year:"numeric"},"year"):r.num(t.year,4);case"yyyyyy":return o?a({year:"numeric"},"year"):r.num(t.year,6);case"G":return f("short");case"GG":return f("long");case"GGGGG":return f("narrow");case"kk":return r.num(t.weekYear.toString().slice(-2),2);case"kkkk":return r.num(t.weekYear,4);case"W":return r.num(t.weekNumber);case"WW":return r.num(t.weekNumber,2);case"o":return r.num(t.ordinal);case"ooo":return r.num(t.ordinal,3);case"q":return r.num(t.quarter);case"qq":return r.num(t.quarter,2);case"X":return r.num(Math.floor(t.ts/1e3));case"x":return r.num(t.ts);default:return function(n){var i=e.macroTokenToFormatOpts(n);return i?r.formatWithSystemDefault(t,i):n}(n)}})},t.formatDurationFromString=function(t,n){var r,i=this,o=function(e){switch(e[0]){case"S":return"millisecond";case"s":return"second";case"m":return"minute";case"h":return"hour";case"d":return"day";case"M":return"month";case"y":return"year";default:return null}},a=e.parseFormat(n),u=a.reduce(function(e,t){var n=t.literal,r=t.val;return n?e:e.concat(r)},[]),s=t.shiftTo.apply(t,u.map(o).filter(function(e){return e}));return Ve(a,(r=s,function(e){var t=o(e);return t?i.num(r.get(t),e.length):e}))},e}(),Ce=function(){function e(e,t){this.reason=e,this.explanation=t}return e.prototype.toMessage=function(){return this.explanation?this.reason+": "+this.explanation:this.reason},e}(),Fe=function(){function e(){}var t=e.prototype;return t.offsetName=function(e,t){throw new g},t.formatOffset=function(e,t){throw new g},t.offset=function(e){throw new g},t.equals=function(e){throw new g},n(e,[{key:"type",get:function(){throw new g}},{key:"name",get:function(){throw new g}},{key:"universal",get:function(){throw new g}},{key:"isValid",get:function(){throw new g}}]),e}(),Ze=null,je=function(e){function t(){return e.apply(this,arguments)||this}r(t,e);var i=t.prototype;return i.offsetName=function(e,t){return ce(e,t.format,t.locale)},i.formatOffset=function(e,t){return he(this.offset(e),t)},i.offset=function(e){return-new Date(e).getTimezoneOffset()},i.equals=function(e){return"local"===e.type},n(t,[{key:"type",get:function(){return"local"}},{key:"name",get:function(){return J()?(new Intl.DateTimeFormat).resolvedOptions().timeZone:"local"}},{key:"universal",get:function(){return!1}},{key:"isValid",get:function(){return!0}}],[{key:"instance",get:function(){return null===Ze&&(Ze=new t),Ze}}]),t}(Fe),Ae=RegExp("^"+ye.source+"$"),ze={};var _e={year:0,month:1,day:2,hour:3,minute:4,second:5};var qe={},He=function(e){function t(n){var r;return(r=e.call(this)||this).zoneName=n,r.valid=t.isValidZone(n),r}r(t,e),t.create=function(e){return qe[e]||(qe[e]=new t(e)),qe[e]},t.resetCache=function(){qe={},ze={}},t.isValidSpecifier=function(e){return!(!e||!e.match(Ae))},t.isValidZone=function(e){try{return new Intl.DateTimeFormat("en-US",{timeZone:e}).format(),!0}catch(e){return!1}},t.parseGMTOffset=function(e){if(e){var t=e.match(/^Etc\/GMT(0|[+-]\d{1,2})$/i);if(t)return-60*parseInt(t[1])}return null};var i=t.prototype;return i.offsetName=function(e,t){return ce(e,t.format,t.locale,this.name)},i.formatOffset=function(e,t){return he(this.offset(e),t)},i.offset=function(e){var t=new Date(e);if(isNaN(t))return NaN;var n,r=(n=this.name,ze[n]||(ze[n]=new Intl.DateTimeFormat("en-US",{hour12:!1,timeZone:n,year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit"})),ze[n]),i=r.formatToParts?function(e,t){for(var n=e.formatToParts(t),r=[],i=0;i<n.length;i++){var o=n[i],a=o.type,u=o.value,s=_e[a];R(s)||(r[s]=parseInt(u,10))}return r}(r,t):function(e,t){var n=e.format(t).replace(/\u200E/g,""),r=/(\d+)\/(\d+)\/(\d+),? (\d+):(\d+):(\d+)/.exec(n),i=r[1],o=r[2];return[r[3],i,o,r[4],r[5],r[6]]}(r,t),o=i[0],a=i[1],u=i[2],s=i[3],c=i[4],l=i[5],f=+t,d=f%1e3;return(ae({year:o,month:a,day:u,hour:24===s?0:s,minute:c,second:l,millisecond:0})-(f-=d>=0?d:1e3+d))/6e4},i.equals=function(e){return"iana"===e.type&&e.name===this.name},n(t,[{key:"type",get:function(){return"iana"}},{key:"name",get:function(){return this.zoneName}},{key:"universal",get:function(){return!1}},{key:"isValid",get:function(){return this.valid}}]),t}(Fe),Ue=null,Re=function(e){function t(t){var n;return(n=e.call(this)||this).fixed=t,n}r(t,e),t.instance=function(e){return 0===e?t.utcInstance:new t(e)},t.parseSpecifier=function(e){if(e){var n=e.match(/^utc(?:([+-]\d{1,2})(?::(\d{2}))?)?$/i);if(n)return new t(le(n[1],n[2]))}return null},n(t,null,[{key:"utcInstance",get:function(){return null===Ue&&(Ue=new t(0)),Ue}}]);var i=t.prototype;return i.offsetName=function(){return this.name},i.formatOffset=function(e,t){return he(this.fixed,t)},i.offset=function(){return this.fixed},i.equals=function(e){return"fixed"===e.type&&e.fixed===this.fixed},n(t,[{key:"type",get:function(){return"fixed"}},{key:"name",get:function(){return 0===this.fixed?"UTC":"UTC"+he(this.fixed,"narrow")}},{key:"universal",get:function(){return!0}},{key:"isValid",get:function(){return!0}}]),t}(Fe),Pe=function(e){function t(t){var n;return(n=e.call(this)||this).zoneName=t,n}r(t,e);var i=t.prototype;return i.offsetName=function(){return null},i.formatOffset=function(){return""},i.offset=function(){return NaN},i.equals=function(){return!1},n(t,[{key:"type",get:function(){return"invalid"}},{key:"name",get:function(){return this.zoneName}},{key:"universal",get:function(){return!1}},{key:"isValid",get:function(){return!1}}]),t}(Fe);function We(e,t){var n;if(R(e)||null===e)return t;if(e instanceof Fe)return e;if("string"==typeof e){var r=e.toLowerCase();return"local"===r?t:"utc"===r||"gmt"===r?Re.utcInstance:null!=(n=He.parseGMTOffset(e))?Re.instance(n):He.isValidSpecifier(r)?He.create(e):Re.parseSpecifier(r)||new Pe(e)}return P(e)?Re.instance(e):"object"==typeof e&&e.offset&&"number"==typeof e.offset?e:new Pe(e)}var Je=function(){return Date.now()},Ye=null,Ge=null,$e=null,Be=null,Qe=!1,Ke=function(){function e(){}return e.resetCaches=function(){ct.resetCache(),He.resetCache()},n(e,null,[{key:"now",get:function(){return Je},set:function(e){Je=e}},{key:"defaultZoneName",get:function(){return e.defaultZone.name},set:function(e){Ye=e?We(e):null}},{key:"defaultZone",get:function(){return Ye||je.instance}},{key:"defaultLocale",get:function(){return Ge},set:function(e){Ge=e}},{key:"defaultNumberingSystem",get:function(){return $e},set:function(e){$e=e}},{key:"defaultOutputCalendar",get:function(){return Be},set:function(e){Be=e}},{key:"throwOnInvalid",get:function(){return Qe},set:function(e){Qe=e}}]),e}(),Xe={};function et(e,t){void 0===t&&(t={});var n=JSON.stringify([e,t]),r=Xe[n];return r||(r=new Intl.DateTimeFormat(e,t),Xe[n]=r),r}var tt={};var nt={};function rt(e,t){void 0===t&&(t={});var n=t,r=(n.base,function(e,t){if(null==e)return{};var n,r,i={},o=Object.keys(e);for(r=0;r<o.length;r++)n=o[r],t.indexOf(n)>=0||(i[n]=e[n]);return i}(n,["base"])),i=JSON.stringify([e,r]),o=nt[i];return o||(o=new Intl.RelativeTimeFormat(e,t),nt[i]=o),o}var it=null;function ot(e,t,n,r,i){var o=e.listingMode(n);return"error"===o?null:"en"===o?r(t):i(t)}var at=function(){function e(e,t,n){if(this.padTo=n.padTo||0,this.floor=n.floor||!1,!t&&J()){var r={useGrouping:!1};n.padTo>0&&(r.minimumIntegerDigits=n.padTo),this.inf=function(e,t){void 0===t&&(t={});var n=JSON.stringify([e,t]),r=tt[n];return r||(r=new Intl.NumberFormat(e,t),tt[n]=r),r}(e,r)}}return e.prototype.format=function(e){if(this.inf){var t=this.floor?Math.floor(e):e;return this.inf.format(t)}return X(this.floor?Math.floor(e):ne(e,3),this.padTo)},e}(),ut=function(){function e(e,t,n){var r;if(this.opts=n,this.hasIntl=J(),e.zone.universal&&this.hasIntl){var i=e.offset/60*-1,o=i>=0?"Etc/GMT+"+i:"Etc/GMT"+i,a=He.isValidZone(o);0!==e.offset&&a?(r=o,this.dt=e):(r="UTC",n.timeZoneName?this.dt=e:this.dt=0===e.offset?e:sr.fromMillis(e.ts+60*e.offset*1e3))}else"local"===e.zone.type?this.dt=e:(this.dt=e,r=e.zone.name);if(this.hasIntl){var u=Object.assign({},this.opts);r&&(u.timeZone=r),this.dtf=et(t,u)}}var t=e.prototype;return t.format=function(){if(this.hasIntl)return this.dtf.format(this.dt.toJSDate());var e=function(e){switch(ve(B(e,["weekday","era","year","month","day","hour","minute","second","timeZoneName","hour12"]))){case ve(b):return"M/d/yyyy";case ve(O):return"LLL d, yyyy";case ve(S):return"EEE, LLL d, yyyy";case ve(T):return"LLLL d, yyyy";case ve(M):return"EEEE, LLLL d, yyyy";case ve(N):return"h:mm a";case ve(E):return"h:mm:ss a";case ve(D):case ve(I):return"h:mm a";case ve(V):return"HH:mm";case ve(L):return"HH:mm:ss";case ve(x):case ve(C):return"HH:mm";case ve(F):return"M/d/yyyy, h:mm a";case ve(j):return"LLL d, yyyy, h:mm a";case ve(_):return"LLLL d, yyyy, h:mm a";case ve(H):return"EEEE, LLLL d, yyyy, h:mm a";case ve(Z):return"M/d/yyyy, h:mm:ss a";case ve(A):return"LLL d, yyyy, h:mm:ss a";case ve(z):return"EEE, d LLL yyyy, h:mm a";case ve(q):return"LLLL d, yyyy, h:mm:ss a";case ve(U):return"EEEE, LLLL d, yyyy, h:mm:ss a";default:return"EEEE, LLLL d, yyyy, h:mm a"}}(this.opts),t=ct.create("en-US");return xe.create(t).formatDateTimeFromString(this.dt,e)},t.formatToParts=function(){return this.hasIntl&&Y()?this.dtf.formatToParts(this.dt.toJSDate()):[]},t.resolvedOptions=function(){return this.hasIntl?this.dtf.resolvedOptions():{locale:"en-US",numberingSystem:"latn",outputCalendar:"gregory"}},e}(),st=function(){function e(e,t,n){this.opts=Object.assign({style:"long"},n),!t&&G()&&(this.rtf=rt(e,n))}var t=e.prototype;return t.format=function(e,t){return this.rtf?this.rtf.format(e,t):function(e,t,n,r){void 0===n&&(n="always"),void 0===r&&(r=!1);var i={years:["year","yr."],quarters:["quarter","qtr."],months:["month","mo."],weeks:["week","wk."],days:["day","day","days"],hours:["hour","hr."],minutes:["minute","min."],seconds:["second","sec."]},o=-1===["hours","minutes","seconds"].indexOf(e);if("auto"===n&&o){var a="days"===e;switch(t){case 1:return a?"tomorrow":"next "+i[e][0];case-1:return a?"yesterday":"last "+i[e][0];case 0:return a?"today":"this "+i[e][0]}}var u=Object.is(t,-0)||t<0,s=Math.abs(t),c=1===s,l=i[e],f=r?c?l[1]:l[2]||l[1]:c?i[e][0]:e;return u?s+" "+f+" ago":"in "+s+" "+f}(t,e,this.opts.numeric,"long"!==this.opts.style)},t.formatToParts=function(e,t){return this.rtf?this.rtf.formatToParts(e,t):[]},e}(),ct=function(){function e(e,t,n,r){var i=function(e){var t=e.indexOf("-u-");if(-1===t)return[e];var n,r=e.substring(0,t);try{n=et(e).resolvedOptions()}catch(e){n=et(r).resolvedOptions()}var i=n;return[r,i.numberingSystem,i.calendar]}(e),o=i[0],a=i[1],u=i[2];this.locale=o,this.numberingSystem=t||a||null,this.outputCalendar=n||u||null,this.intl=function(e,t,n){return J()?n||t?(e+="-u",n&&(e+="-ca-"+n),t&&(e+="-nu-"+t),e):e:[]}(this.locale,this.numberingSystem,this.outputCalendar),this.weekdaysCache={format:{},standalone:{}},this.monthsCache={format:{},standalone:{}},this.meridiemCache=null,this.eraCache={},this.specifiedLocale=r,this.fastNumbersCached=null}e.fromOpts=function(t){return e.create(t.locale,t.numberingSystem,t.outputCalendar,t.defaultToEN)},e.create=function(t,n,r,i){void 0===i&&(i=!1);var o=t||Ke.defaultLocale;return new e(o||(i?"en-US":function(){if(it)return it;if(J()){var e=(new Intl.DateTimeFormat).resolvedOptions().locale;return it=e&&"und"!==e?e:"en-US"}return it="en-US"}()),n||Ke.defaultNumberingSystem,r||Ke.defaultOutputCalendar,o)},e.resetCache=function(){it=null,Xe={},tt={},nt={}},e.fromObject=function(t){var n=void 0===t?{}:t,r=n.locale,i=n.numberingSystem,o=n.outputCalendar;return e.create(r,i,o)};var t=e.prototype;return t.listingMode=function(e){void 0===e&&(e=!0);var t=J()&&Y(),n=this.isEnglish(),r=!(null!==this.numberingSystem&&"latn"!==this.numberingSystem||null!==this.outputCalendar&&"gregory"!==this.outputCalendar);return t||n&&r||e?!t||n&&r?"en":"intl":"error"},t.clone=function(t){return t&&0!==Object.getOwnPropertyNames(t).length?e.create(t.locale||this.specifiedLocale,t.numberingSystem||this.numberingSystem,t.outputCalendar||this.outputCalendar,t.defaultToEN||!1):this},t.redefaultToEN=function(e){return void 0===e&&(e={}),this.clone(Object.assign({},e,{defaultToEN:!0}))},t.redefaultToSystem=function(e){return void 0===e&&(e={}),this.clone(Object.assign({},e,{defaultToEN:!1}))},t.months=function(e,t,n){var r=this;return void 0===t&&(t=!1),void 0===n&&(n=!0),ot(this,e,n,ke,function(){var n=t?{month:e,day:"numeric"}:{month:e},i=t?"format":"standalone";return r.monthsCache[i][e]||(r.monthsCache[i][e]=function(e){for(var t=[],n=1;n<=12;n++){var r=sr.utc(2016,n,1);t.push(e(r))}return t}(function(e){return r.extract(e,n,"month")})),r.monthsCache[i][e]})},t.weekdays=function(e,t,n){var r=this;return void 0===t&&(t=!1),void 0===n&&(n=!0),ot(this,e,n,Te,function(){var n=t?{weekday:e,year:"numeric",month:"long",day:"numeric"}:{weekday:e},i=t?"format":"standalone";return r.weekdaysCache[i][e]||(r.weekdaysCache[i][e]=function(e){for(var t=[],n=1;n<=7;n++){var r=sr.utc(2016,11,13+n);t.push(e(r))}return t}(function(e){return r.extract(e,n,"weekday")})),r.weekdaysCache[i][e]})},t.meridiems=function(e){var t=this;return void 0===e&&(e=!0),ot(this,void 0,e,function(){return Me},function(){if(!t.meridiemCache){var e={hour:"numeric",hour12:!0};t.meridiemCache=[sr.utc(2016,11,13,9),sr.utc(2016,11,13,19)].map(function(n){return t.extract(n,e,"dayperiod")})}return t.meridiemCache})},t.eras=function(e,t){var n=this;return void 0===t&&(t=!0),ot(this,e,t,Ie,function(){var t={era:e};return n.eraCache[e]||(n.eraCache[e]=[sr.utc(-40,1,1),sr.utc(2017,1,1)].map(function(e){return n.extract(e,t,"era")})),n.eraCache[e]})},t.extract=function(e,t,n){var r=this.dtFormatter(e,t).formatToParts().find(function(e){return e.type.toLowerCase()===n});return r?r.value:null},t.numberFormatter=function(e){return void 0===e&&(e={}),new at(this.intl,e.forceSimple||this.fastNumbers,e)},t.dtFormatter=function(e,t){return void 0===t&&(t={}),new ut(e,this.intl,t)},t.relFormatter=function(e){return void 0===e&&(e={}),new st(this.intl,this.isEnglish(),e)},t.isEnglish=function(){return"en"===this.locale||"en-us"===this.locale.toLowerCase()||J()&&new Intl.DateTimeFormat(this.intl).resolvedOptions().locale.startsWith("en-us")},t.equals=function(e){return this.locale===e.locale&&this.numberingSystem===e.numberingSystem&&this.outputCalendar===e.outputCalendar},n(e,[{key:"fastNumbers",get:function(){var e;return null==this.fastNumbersCached&&(this.fastNumbersCached=(!(e=this).numberingSystem||"latn"===e.numberingSystem)&&("latn"===e.numberingSystem||!e.locale||e.locale.startsWith("en")||J()&&"latn"===new Intl.DateTimeFormat(e.intl).resolvedOptions().numberingSystem)),this.fastNumbersCached}}]),e}();function lt(){for(var e=arguments.length,t=new Array(e),n=0;n<e;n++)t[n]=arguments[n];var r=t.reduce(function(e,t){return e+t.source},"");return RegExp("^"+r+"$")}function ft(){for(var e=arguments.length,t=new Array(e),n=0;n<e;n++)t[n]=arguments[n];return function(e){return t.reduce(function(t,n){var r=t[0],i=t[1],o=t[2],a=n(e,o),u=a[0],s=a[1],c=a[2];return[Object.assign(r,u),i||s,c]},[{},null,1]).slice(0,2)}}function dt(e){if(null==e)return[null,null];for(var t=arguments.length,n=new Array(t>1?t-1:0),r=1;r<t;r++)n[r-1]=arguments[r];for(var i=0,o=n;i<o.length;i++){var a=o[i],u=a[0],s=a[1],c=u.exec(e);if(c)return s(c)}return[null,null]}function ht(){for(var e=arguments.length,t=new Array(e),n=0;n<e;n++)t[n]=arguments[n];return function(e,n){var r,i={};for(r=0;r<t.length;r++)i[t[r]]=ee(e[n+r]);return[i,null,n+r]}}var mt=/(?:(Z)|([+-]\d\d)(?::?(\d\d))?)/,yt=/(\d\d)(?::?(\d\d)(?::?(\d\d)(?:[.,](\d{1,30}))?)?)?/,vt=RegExp(""+yt.source+mt.source+"?"),gt=RegExp("(?:T"+vt.source+")?"),pt=ht("weekYear","weekNumber","weekDay"),wt=ht("year","ordinal"),kt=RegExp(yt.source+" ?(?:"+mt.source+"|("+ye.source+"))?"),bt=RegExp("(?: "+kt.source+")?");function Ot(e,t,n){var r=e[t];return R(r)?n:ee(r)}function St(e,t){return[{year:Ot(e,t),month:Ot(e,t+1,1),day:Ot(e,t+2,1)},null,t+3]}function Tt(e,t){return[{hours:Ot(e,t,0),minutes:Ot(e,t+1,0),seconds:Ot(e,t+2,0),milliseconds:te(e[t+3])},null,t+4]}function Mt(e,t){var n=!e[t]&&!e[t+1],r=le(e[t+1],e[t+2]);return[{},n?null:Re.instance(r),t+3]}function Nt(e,t){return[{},e[t]?He.create(e[t]):null,t+1]}var Et=RegExp("^T?"+yt.source+"$"),Dt=/^-?P(?:(?:(-?\d{1,9})Y)?(?:(-?\d{1,9})M)?(?:(-?\d{1,9})W)?(?:(-?\d{1,9})D)?(?:T(?:(-?\d{1,9})H)?(?:(-?\d{1,9})M)?(?:(-?\d{1,20})(?:[.,](-?\d{1,9}))?S)?)?)$/;function It(e){var t=e[0],n=e[1],r=e[2],i=e[3],o=e[4],a=e[5],u=e[6],s=e[7],c=e[8],l="-"===t[0],f=s&&"-"===s[0],d=function(e,t){return void 0===t&&(t=!1),void 0!==e&&(t||e&&l)?-e:e};return[{years:d(ee(n)),months:d(ee(r)),weeks:d(ee(i)),days:d(ee(o)),hours:d(ee(a)),minutes:d(ee(u)),seconds:d(ee(s),"-0"===s),milliseconds:d(te(c),f)}]}var Vt={GMT:0,EDT:-240,EST:-300,CDT:-300,CST:-360,MDT:-360,MST:-420,PDT:-420,PST:-480};function Lt(e,t,n,r,i,o,a){var u={year:2===t.length?se(ee(t)):ee(t),month:pe.indexOf(n)+1,day:ee(r),hour:ee(i),minute:ee(o)};return a&&(u.second=ee(a)),e&&(u.weekday=e.length>3?be.indexOf(e)+1:Oe.indexOf(e)+1),u}var xt=/^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),\s)?(\d{1,2})\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(\d{2,4})\s(\d\d):(\d\d)(?::(\d\d))?\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|(?:([+-]\d\d)(\d\d)))$/;function Ct(e){var t,n=e[1],r=e[2],i=e[3],o=e[4],a=e[5],u=e[6],s=e[7],c=e[8],l=e[9],f=e[10],d=e[11],h=Lt(n,o,i,r,a,u,s);return t=c?Vt[c]:l?0:le(f,d),[h,new Re(t)]}var Ft=/^(Mon|Tue|Wed|Thu|Fri|Sat|Sun), (\d\d) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) (\d{4}) (\d\d):(\d\d):(\d\d) GMT$/,Zt=/^(Monday|Tuesday|Wedsday|Thursday|Friday|Saturday|Sunday), (\d\d)-(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)-(\d\d) (\d\d):(\d\d):(\d\d) GMT$/,jt=/^(Mon|Tue|Wed|Thu|Fri|Sat|Sun) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) ( \d|\d\d) (\d\d):(\d\d):(\d\d) (\d{4})$/;function At(e){var t=e[1],n=e[2],r=e[3];return[Lt(t,e[4],r,n,e[5],e[6],e[7]),Re.utcInstance]}function zt(e){var t=e[1],n=e[2],r=e[3],i=e[4],o=e[5],a=e[6];return[Lt(t,e[7],n,r,i,o,a),Re.utcInstance]}var _t=lt(/([+-]\d{6}|\d{4})(?:-?(\d\d)(?:-?(\d\d))?)?/,gt),qt=lt(/(\d{4})-?W(\d\d)(?:-?(\d))?/,gt),Ht=lt(/(\d{4})-?(\d{3})/,gt),Ut=lt(vt),Rt=ft(St,Tt,Mt),Pt=ft(pt,Tt,Mt),Wt=ft(wt,Tt),Jt=ft(Tt,Mt);var Yt=ft(Tt);var Gt=lt(/(\d{4})-(\d\d)-(\d\d)/,bt),$t=lt(kt),Bt=ft(St,Tt,Mt,Nt),Qt=ft(Tt,Mt,Nt);var Kt={weeks:{days:7,hours:168,minutes:10080,seconds:604800,milliseconds:6048e5},days:{hours:24,minutes:1440,seconds:86400,milliseconds:864e5},hours:{minutes:60,seconds:3600,milliseconds:36e5},minutes:{seconds:60,milliseconds:6e4},seconds:{milliseconds:1e3}},Xt=Object.assign({years:{quarters:4,months:12,weeks:52,days:365,hours:8760,minutes:525600,seconds:31536e3,milliseconds:31536e6},quarters:{months:3,weeks:13,days:91,hours:2184,minutes:131040,seconds:7862400,milliseconds:78624e5},months:{weeks:4,days:30,hours:720,minutes:43200,seconds:2592e3,milliseconds:2592e6}},Kt),en=Object.assign({years:{quarters:4,months:12,weeks:52.1775,days:365.2425,hours:8765.82,minutes:525949.2,seconds:525949.2*60,milliseconds:525949.2*60*1e3},quarters:{months:3,weeks:13.044375,days:91.310625,hours:2191.455,minutes:131487.3,seconds:525949.2*60/4,milliseconds:7889237999.999999},months:{weeks:30.436875/7,days:30.436875,hours:730.485,minutes:43829.1,seconds:2629746,milliseconds:2629746e3}},Kt),tn=["years","quarters","months","weeks","days","hours","minutes","seconds","milliseconds"],nn=tn.slice(0).reverse();function rn(e,t,n){void 0===n&&(n=!1);var r={values:n?t.values:Object.assign({},e.values,t.values||{}),loc:e.loc.clone(t.loc),conversionAccuracy:t.conversionAccuracy||e.conversionAccuracy};return new an(r)}function on(e,t,n,r,i){var o,a=e[i][n],u=t[n]/a,s=!(Math.sign(u)===Math.sign(r[i]))&&0!==r[i]&&Math.abs(u)<=1?(o=u)<0?Math.floor(o):Math.ceil(o):Math.trunc(u);r[i]+=s,t[n]-=s*a}var an=function(){function e(e){var t="longterm"===e.conversionAccuracy||!1;this.values=e.values,this.loc=e.loc||ct.create(),this.conversionAccuracy=t?"longterm":"casual",this.invalid=e.invalid||null,this.matrix=t?en:Xt,this.isLuxonDuration=!0}e.fromMillis=function(t,n){return e.fromObject(Object.assign({milliseconds:t},n))},e.fromObject=function(t){if(null==t||"object"!=typeof t)throw new v("Duration.fromObject: argument expected to be an object, got "+(null===t?"null":typeof t));return new e({values:de(t,e.normalizeUnit,["locale","numberingSystem","conversionAccuracy","zone"]),loc:ct.fromObject(t),conversionAccuracy:t.conversionAccuracy})},e.fromISO=function(t,n){var r=dt(t,[Dt,It])[0];if(r){var i=Object.assign(r,n);return e.fromObject(i)}return e.invalid("unparsable",'the input "'+t+"\" can't be parsed as ISO 8601")},e.fromISOTime=function(t,n){var r=dt(t,[Et,Yt])[0];if(r){var i=Object.assign(r,n);return e.fromObject(i)}return e.invalid("unparsable",'the input "'+t+"\" can't be parsed as ISO 8601")},e.invalid=function(t,n){if(void 0===n&&(n=null),!t)throw new v("need to specify a reason the Duration is invalid");var r=t instanceof Ce?t:new Ce(t,n);if(Ke.throwOnInvalid)throw new h(r);return new e({invalid:r})},e.normalizeUnit=function(e){var t={year:"years",years:"years",quarter:"quarters",quarters:"quarters",month:"months",months:"months",week:"weeks",weeks:"weeks",day:"days",days:"days",hour:"hours",hours:"hours",minute:"minutes",minutes:"minutes",second:"seconds",seconds:"seconds",millisecond:"milliseconds",milliseconds:"milliseconds"}[e?e.toLowerCase():e];if(!t)throw new y(e);return t},e.isDuration=function(e){return e&&e.isLuxonDuration||!1};var t=e.prototype;return t.toFormat=function(e,t){void 0===t&&(t={});var n=Object.assign({},t,{floor:!1!==t.round&&!1!==t.floor});return this.isValid?xe.create(this.loc,n).formatDurationFromString(this,e):"Invalid Duration"},t.toObject=function(e){if(void 0===e&&(e={}),!this.isValid)return{};var t=Object.assign({},this.values);return e.includeConfig&&(t.conversionAccuracy=this.conversionAccuracy,t.numberingSystem=this.loc.numberingSystem,t.locale=this.loc.locale),t},t.toISO=function(){if(!this.isValid)return null;var e="P";return 0!==this.years&&(e+=this.years+"Y"),0===this.months&&0===this.quarters||(e+=this.months+3*this.quarters+"M"),0!==this.weeks&&(e+=this.weeks+"W"),0!==this.days&&(e+=this.days+"D"),0===this.hours&&0===this.minutes&&0===this.seconds&&0===this.milliseconds||(e+="T"),0!==this.hours&&(e+=this.hours+"H"),0!==this.minutes&&(e+=this.minutes+"M"),0===this.seconds&&0===this.milliseconds||(e+=ne(this.seconds+this.milliseconds/1e3,3)+"S"),"P"===e&&(e+="T0S"),e},t.toISOTime=function(e){if(void 0===e&&(e={}),!this.isValid)return null;var t=this.toMillis();if(t<0||t>=864e5)return null;e=Object.assign({suppressMilliseconds:!1,suppressSeconds:!1,includePrefix:!1,format:"extended"},e);var n=this.shiftTo("hours","minutes","seconds","milliseconds"),r="basic"===e.format?"hhmm":"hh:mm";e.suppressSeconds&&0===n.seconds&&0===n.milliseconds||(r+="basic"===e.format?"ss":":ss",e.suppressMilliseconds&&0===n.milliseconds||(r+=".SSS"));var i=n.toFormat(r);return e.includePrefix&&(i="T"+i),i},t.toJSON=function(){return this.toISO()},t.toString=function(){return this.toISO()},t.toMillis=function(){return this.as("milliseconds")},t.valueOf=function(){return this.toMillis()},t.plus=function(e){if(!this.isValid)return this;for(var t,n=un(e),r={},i=c(tn);!(t=i()).done;){var o=t.value;(Q(n.values,o)||Q(this.values,o))&&(r[o]=n.get(o)+this.get(o))}return rn(this,{values:r},!0)},t.minus=function(e){if(!this.isValid)return this;var t=un(e);return this.plus(t.negate())},t.mapUnits=function(e){if(!this.isValid)return this;for(var t={},n=0,r=Object.keys(this.values);n<r.length;n++){var i=r[n];t[i]=fe(e(this.values[i],i))}return rn(this,{values:t},!0)},t.get=function(t){return this[e.normalizeUnit(t)]},t.set=function(t){return this.isValid?rn(this,{values:Object.assign(this.values,de(t,e.normalizeUnit,[]))}):this},t.reconfigure=function(e){var t=void 0===e?{}:e,n=t.locale,r=t.numberingSystem,i=t.conversionAccuracy,o={loc:this.loc.clone({locale:n,numberingSystem:r})};return i&&(o.conversionAccuracy=i),rn(this,o)},t.as=function(e){return this.isValid?this.shiftTo(e).get(e):NaN},t.normalize=function(){if(!this.isValid)return this;var e=this.toObject();return function(e,t){nn.reduce(function(n,r){return R(t[r])?n:(n&&on(e,t,n,t,r),r)},null)}(this.matrix,e),rn(this,{values:e},!0)},t.shiftTo=function(){for(var t=arguments.length,n=new Array(t),r=0;r<t;r++)n[r]=arguments[r];if(!this.isValid)return this;if(0===n.length)return this;n=n.map(function(t){return e.normalizeUnit(t)});for(var i,o,a={},u={},s=this.toObject(),l=c(tn);!(o=l()).done;){var f=o.value;if(n.indexOf(f)>=0){i=f;var d=0;for(var h in u)d+=this.matrix[h][f]*u[h],u[h]=0;P(s[f])&&(d+=s[f]);var m=Math.trunc(d);for(var y in a[f]=m,u[f]=d-m,s)tn.indexOf(y)>tn.indexOf(f)&&on(this.matrix,s,y,a,f)}else P(s[f])&&(u[f]=s[f])}for(var v in u)0!==u[v]&&(a[i]+=v===i?u[v]:u[v]/this.matrix[i][v]);return rn(this,{values:a},!0).normalize()},t.negate=function(){if(!this.isValid)return this;for(var e={},t=0,n=Object.keys(this.values);t<n.length;t++){var r=n[t];e[r]=-this.values[r]}return rn(this,{values:e},!0)},t.equals=function(e){if(!this.isValid||!e.isValid)return!1;if(!this.loc.equals(e.loc))return!1;for(var t,n=c(tn);!(t=n()).done;){var r=t.value;if(i=this.values[r],o=e.values[r],!(void 0===i||0===i?void 0===o||0===o:i===o))return!1}var i,o;return!0},n(e,[{key:"locale",get:function(){return this.isValid?this.loc.locale:null}},{key:"numberingSystem",get:function(){return this.isValid?this.loc.numberingSystem:null}},{key:"years",get:function(){return this.isValid?this.values.years||0:NaN}},{key:"quarters",get:function(){return this.isValid?this.values.quarters||0:NaN}},{key:"months",get:function(){return this.isValid?this.values.months||0:NaN}},{key:"weeks",get:function(){return this.isValid?this.values.weeks||0:NaN}},{key:"days",get:function(){return this.isValid?this.values.days||0:NaN}},{key:"hours",get:function(){return this.isValid?this.values.hours||0:NaN}},{key:"minutes",get:function(){return this.isValid?this.values.minutes||0:NaN}},{key:"seconds",get:function(){return this.isValid?this.values.seconds||0:NaN}},{key:"milliseconds",get:function(){return this.isValid?this.values.milliseconds||0:NaN}},{key:"isValid",get:function(){return null===this.invalid}},{key:"invalidReason",get:function(){return this.invalid?this.invalid.reason:null}},{key:"invalidExplanation",get:function(){return this.invalid?this.invalid.explanation:null}}]),e}();function un(e){if(P(e))return an.fromMillis(e);if(an.isDuration(e))return e;if("object"==typeof e)return an.fromObject(e);throw new v("Unknown duration argument "+e+" of type "+typeof e)}var sn="Invalid Interval";function cn(e,t){return e&&e.isValid?t&&t.isValid?t<e?ln.invalid("end before start","The end of an interval must be after its start, but you had start="+e.toISO()+" and end="+t.toISO()):null:ln.invalid("missing or invalid end"):ln.invalid("missing or invalid start")}var ln=function(){function e(e){this.s=e.start,this.e=e.end,this.invalid=e.invalid||null,this.isLuxonInterval=!0}e.invalid=function(t,n){if(void 0===n&&(n=null),!t)throw new v("need to specify a reason the Interval is invalid");var r=t instanceof Ce?t:new Ce(t,n);if(Ke.throwOnInvalid)throw new d(r);return new e({invalid:r})},e.fromDateTimes=function(t,n){var r=cr(t),i=cr(n),o=cn(r,i);return null==o?new e({start:r,end:i}):o},e.after=function(t,n){var r=un(n),i=cr(t);return e.fromDateTimes(i,i.plus(r))},e.before=function(t,n){var r=un(n),i=cr(t);return e.fromDateTimes(i.minus(r),i)},e.fromISO=function(t,n){var r=(t||"").split("/",2),i=r[0],o=r[1];if(i&&o){var a,u,s,c;try{u=(a=sr.fromISO(i,n)).isValid}catch(o){u=!1}try{c=(s=sr.fromISO(o,n)).isValid}catch(o){c=!1}if(u&&c)return e.fromDateTimes(a,s);if(u){var l=an.fromISO(o,n);if(l.isValid)return e.after(a,l)}else if(c){var f=an.fromISO(i,n);if(f.isValid)return e.before(s,f)}}return e.invalid("unparsable",'the input "'+t+"\" can't be parsed as ISO 8601")},e.isInterval=function(e){return e&&e.isLuxonInterval||!1};var t=e.prototype;return t.length=function(e){return void 0===e&&(e="milliseconds"),this.isValid?this.toDuration.apply(this,[e]).get(e):NaN},t.count=function(e){if(void 0===e&&(e="milliseconds"),!this.isValid)return NaN;var t=this.start.startOf(e),n=this.end.startOf(e);return Math.floor(n.diff(t,e).get(e))+1},t.hasSame=function(e){return!!this.isValid&&(this.isEmpty()||this.e.minus(1).hasSame(this.s,e))},t.isEmpty=function(){return this.s.valueOf()===this.e.valueOf()},t.isAfter=function(e){return!!this.isValid&&this.s>e},t.isBefore=function(e){return!!this.isValid&&this.e<=e},t.contains=function(e){return!!this.isValid&&(this.s<=e&&this.e>e)},t.set=function(t){var n=void 0===t?{}:t,r=n.start,i=n.end;return this.isValid?e.fromDateTimes(r||this.s,i||this.e):this},t.splitAt=function(){var t=this;if(!this.isValid)return[];for(var n=arguments.length,r=new Array(n),i=0;i<n;i++)r[i]=arguments[i];for(var o=r.map(cr).filter(function(e){return t.contains(e)}).sort(),a=[],u=this.s,s=0;u<this.e;){var c=o[s]||this.e,l=+c>+this.e?this.e:c;a.push(e.fromDateTimes(u,l)),u=l,s+=1}return a},t.splitBy=function(t){var n=un(t);if(!this.isValid||!n.isValid||0===n.as("milliseconds"))return[];for(var r,i=this.s,o=1,a=[];i<this.e;){var u=this.start.plus(n.mapUnits(function(e){return e*o}));r=+u>+this.e?this.e:u,a.push(e.fromDateTimes(i,r)),i=r,o+=1}return a},t.divideEqually=function(e){return this.isValid?this.splitBy(this.length()/e).slice(0,e):[]},t.overlaps=function(e){return this.e>e.s&&this.s<e.e},t.abutsStart=function(e){return!!this.isValid&&+this.e==+e.s},t.abutsEnd=function(e){return!!this.isValid&&+e.e==+this.s},t.engulfs=function(e){return!!this.isValid&&(this.s<=e.s&&this.e>=e.e)},t.equals=function(e){return!(!this.isValid||!e.isValid)&&(this.s.equals(e.s)&&this.e.equals(e.e))},t.intersection=function(t){if(!this.isValid)return this;var n=this.s>t.s?this.s:t.s,r=this.e<t.e?this.e:t.e;return n>=r?null:e.fromDateTimes(n,r)},t.union=function(t){if(!this.isValid)return this;var n=this.s<t.s?this.s:t.s,r=this.e>t.e?this.e:t.e;return e.fromDateTimes(n,r)},e.merge=function(e){var t=e.sort(function(e,t){return e.s-t.s}).reduce(function(e,t){var n=e[0],r=e[1];return r?r.overlaps(t)||r.abutsStart(t)?[n,r.union(t)]:[n.concat([r]),t]:[n,t]},[[],null]),n=t[0],r=t[1];return r&&n.push(r),n},e.xor=function(t){for(var n,r,i=null,o=0,a=[],u=t.map(function(e){return[{time:e.s,type:"s"},{time:e.e,type:"e"}]}),s=c((n=Array.prototype).concat.apply(n,u).sort(function(e,t){return e.time-t.time}));!(r=s()).done;){var l=r.value;1===(o+="s"===l.type?1:-1)?i=l.time:(i&&+i!=+l.time&&a.push(e.fromDateTimes(i,l.time)),i=null)}return e.merge(a)},t.difference=function(){for(var t=this,n=arguments.length,r=new Array(n),i=0;i<n;i++)r[i]=arguments[i];return e.xor([this].concat(r)).map(function(e){return t.intersection(e)}).filter(function(e){return e&&!e.isEmpty()})},t.toString=function(){return this.isValid?"["+this.s.toISO()+" – "+this.e.toISO()+")":sn},t.toISO=function(e){return this.isValid?this.s.toISO(e)+"/"+this.e.toISO(e):sn},t.toISODate=function(){return this.isValid?this.s.toISODate()+"/"+this.e.toISODate():sn},t.toISOTime=function(e){return this.isValid?this.s.toISOTime(e)+"/"+this.e.toISOTime(e):sn},t.toFormat=function(e,t){var n=(void 0===t?{}:t).separator,r=void 0===n?" – ":n;return this.isValid?""+this.s.toFormat(e)+r+this.e.toFormat(e):sn},t.toDuration=function(e,t){return this.isValid?this.e.diff(this.s,e,t):an.invalid(this.invalidReason)},t.mapEndpoints=function(t){return e.fromDateTimes(t(this.s),t(this.e))},n(e,[{key:"start",get:function(){return this.isValid?this.s:null}},{key:"end",get:function(){return this.isValid?this.e:null}},{key:"isValid",get:function(){return null===this.invalidReason}},{key:"invalidReason",get:function(){return this.invalid?this.invalid.reason:null}},{key:"invalidExplanation",get:function(){return this.invalid?this.invalid.explanation:null}}]),e}(),fn=function(){function e(){}return e.hasDST=function(e){void 0===e&&(e=Ke.defaultZone);var t=sr.now().setZone(e).set({month:12});return!e.universal&&t.offset!==t.set({month:6}).offset},e.isValidIANAZone=function(e){return He.isValidSpecifier(e)&&He.isValidZone(e)},e.normalizeZone=function(e){return We(e,Ke.defaultZone)},e.months=function(e,t){void 0===e&&(e="long");var n=void 0===t?{}:t,r=n.locale,i=void 0===r?null:r,o=n.numberingSystem,a=void 0===o?null:o,u=n.locObj,s=void 0===u?null:u,c=n.outputCalendar,l=void 0===c?"gregory":c;return(s||ct.create(i,a,l)).months(e)},e.monthsFormat=function(e,t){void 0===e&&(e="long");var n=void 0===t?{}:t,r=n.locale,i=void 0===r?null:r,o=n.numberingSystem,a=void 0===o?null:o,u=n.locObj,s=void 0===u?null:u,c=n.outputCalendar,l=void 0===c?"gregory":c;return(s||ct.create(i,a,l)).months(e,!0)},e.weekdays=function(e,t){void 0===e&&(e="long");var n=void 0===t?{}:t,r=n.locale,i=void 0===r?null:r,o=n.numberingSystem,a=void 0===o?null:o,u=n.locObj;return((void 0===u?null:u)||ct.create(i,a,null)).weekdays(e)},e.weekdaysFormat=function(e,t){void 0===e&&(e="long");var n=void 0===t?{}:t,r=n.locale,i=void 0===r?null:r,o=n.numberingSystem,a=void 0===o?null:o,u=n.locObj;return((void 0===u?null:u)||ct.create(i,a,null)).weekdays(e,!0)},e.meridiems=function(e){var t=(void 0===e?{}:e).locale,n=void 0===t?null:t;return ct.create(n).meridiems()},e.eras=function(e,t){void 0===e&&(e="short");var n=(void 0===t?{}:t).locale,r=void 0===n?null:n;return ct.create(r,null,"gregory").eras(e)},e.features=function(){var e=!1,t=!1,n=!1,r=!1;if(J()){e=!0,t=Y(),r=G();try{n="America/New_York"===new Intl.DateTimeFormat("en",{timeZone:"America/New_York"}).resolvedOptions().timeZone}catch(e){n=!1}}return{intl:e,intlTokens:t,zones:n,relative:r}},e}();function dn(e,t){var n=function(e){return e.toUTC(0,{keepLocalTime:!0}).startOf("day").valueOf()},r=n(t)-n(e);return Math.floor(an.fromMillis(r).as("days"))}function hn(e,t,n,r){var i=function(e,t,n){for(var r,i,o={},a=0,u=[["years",function(e,t){return t.year-e.year}],["quarters",function(e,t){return t.quarter-e.quarter}],["months",function(e,t){return t.month-e.month+12*(t.year-e.year)}],["weeks",function(e,t){var n=dn(e,t);return(n-n%7)/7}],["days",dn]];a<u.length;a++){var s=u[a],c=s[0],l=s[1];if(n.indexOf(c)>=0){var f;r=c;var d,h=l(e,t);(i=e.plus(((f={})[c]=h,f)))>t?(e=e.plus(((d={})[c]=h-1,d)),h-=1):e=i,o[c]=h}}return[e,o,i,r]}(e,t,n),o=i[0],a=i[1],u=i[2],s=i[3],c=t-o,l=n.filter(function(e){return["hours","minutes","seconds","milliseconds"].indexOf(e)>=0});if(0===l.length){var f;if(u<t)u=o.plus(((f={})[s]=1,f));u!==o&&(a[s]=(a[s]||0)+c/(u-o))}var d,h=an.fromObject(Object.assign(a,r));return l.length>0?(d=an.fromMillis(c,r)).shiftTo.apply(d,l).plus(h):h}var mn={arab:"[٠-٩]",arabext:"[۰-۹]",bali:"[᭐-᭙]",beng:"[০-৯]",deva:"[०-९]",fullwide:"[0-9]",gujr:"[૦-૯]",hanidec:"[〇|一|二|三|四|五|六|七|八|九]",khmr:"[០-៩]",knda:"[೦-೯]",laoo:"[໐-໙]",limb:"[᥆-᥏]",mlym:"[൦-൯]",mong:"[᠐-᠙]",mymr:"[၀-၉]",orya:"[୦-୯]",tamldec:"[௦-௯]",telu:"[౦-౯]",thai:"[๐-๙]",tibt:"[༠-༩]",latn:"\\d"},yn={arab:[1632,1641],arabext:[1776,1785],bali:[6992,7001],beng:[2534,2543],deva:[2406,2415],fullwide:[65296,65303],gujr:[2790,2799],khmr:[6112,6121],knda:[3302,3311],laoo:[3792,3801],limb:[6470,6479],mlym:[3430,3439],mong:[6160,6169],mymr:[4160,4169],orya:[2918,2927],tamldec:[3046,3055],telu:[3174,3183],thai:[3664,3673],tibt:[3872,3881]},vn=mn.hanidec.replace(/[\[|\]]/g,"").split("");function gn(e,t){var n=e.numberingSystem;return void 0===t&&(t=""),new RegExp(""+mn[n||"latn"]+t)}var pn="missing Intl.DateTimeFormat.formatToParts support";function wn(e,t){return void 0===t&&(t=function(e){return e}),{regex:e,deser:function(e){var n=e[0];return t(function(e){var t=parseInt(e,10);if(isNaN(t)){t="";for(var n=0;n<e.length;n++){var r=e.charCodeAt(n);if(-1!==e[n].search(mn.hanidec))t+=vn.indexOf(e[n]);else for(var i in yn){var o=yn[i],a=o[0],u=o[1];r>=a&&r<=u&&(t+=r-a)}}return parseInt(t,10)}return t}(n))}}}var kn="( |"+String.fromCharCode(160)+")",bn=new RegExp(kn,"g");function On(e){return e.replace(/\./g,"\\.?").replace(bn,kn)}function Sn(e){return e.replace(/\./g,"").replace(bn," ").toLowerCase()}function Tn(e,t){return null===e?null:{regex:RegExp(e.map(On).join("|")),deser:function(n){var r=n[0];return e.findIndex(function(e){return Sn(r)===Sn(e)})+t}}}function Mn(e,t){return{regex:e,deser:function(e){return le(e[1],e[2])},groups:t}}function Nn(e){return{regex:e,deser:function(e){return e[0]}}}var En={year:{"2-digit":"yy",numeric:"yyyyy"},month:{numeric:"M","2-digit":"MM",short:"MMM",long:"MMMM"},day:{numeric:"d","2-digit":"dd"},weekday:{short:"EEE",long:"EEEE"},dayperiod:"a",dayPeriod:"a",hour:{numeric:"h","2-digit":"hh"},minute:{numeric:"m","2-digit":"mm"},second:{numeric:"s","2-digit":"ss"}};var Dn=null;function In(e,t){if(e.literal)return e;var n=xe.macroTokenToFormatOpts(e.val);if(!n)return e;var r=xe.create(t,n).formatDateTimeParts((Dn||(Dn=sr.fromMillis(1555555555555)),Dn)).map(function(e){return function(e,t,n){var r=e.type,i=e.value;if("literal"===r)return{literal:!0,val:i};var o=n[r],a=En[r];return"object"==typeof a&&(a=a[o]),a?{literal:!1,val:a}:void 0}(e,0,n)});return r.includes(void 0)?e:r}function Vn(e,t,n){var r=function(e,t){var n;return(n=Array.prototype).concat.apply(n,e.map(function(e){return In(e,t)}))}(xe.parseFormat(n),e),i=r.map(function(t){return n=t,i=gn(r=e),o=gn(r,"{2}"),a=gn(r,"{3}"),u=gn(r,"{4}"),s=gn(r,"{6}"),c=gn(r,"{1,2}"),l=gn(r,"{1,3}"),f=gn(r,"{1,6}"),d=gn(r,"{1,9}"),h=gn(r,"{2,4}"),m=gn(r,"{4,6}"),y=function(e){return{regex:RegExp((t=e.val,t.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&"))),deser:function(e){return e[0]},literal:!0};var t},(v=function(e){if(n.literal)return y(e);switch(e.val){case"G":return Tn(r.eras("short",!1),0);case"GG":return Tn(r.eras("long",!1),0);case"y":return wn(f);case"yy":return wn(h,se);case"yyyy":return wn(u);case"yyyyy":return wn(m);case"yyyyyy":return wn(s);case"M":return wn(c);case"MM":return wn(o);case"MMM":return Tn(r.months("short",!0,!1),1);case"MMMM":return Tn(r.months("long",!0,!1),1);case"L":return wn(c);case"LL":return wn(o);case"LLL":return Tn(r.months("short",!1,!1),1);case"LLLL":return Tn(r.months("long",!1,!1),1);case"d":return wn(c);case"dd":return wn(o);case"o":return wn(l);case"ooo":return wn(a);case"HH":return wn(o);case"H":return wn(c);case"hh":return wn(o);case"h":return wn(c);case"mm":return wn(o);case"m":case"q":return wn(c);case"qq":return wn(o);case"s":return wn(c);case"ss":return wn(o);case"S":return wn(l);case"SSS":return wn(a);case"u":return Nn(d);case"a":return Tn(r.meridiems(),0);case"kkkk":return wn(u);case"kk":return wn(h,se);case"W":return wn(c);case"WW":return wn(o);case"E":case"c":return wn(i);case"EEE":return Tn(r.weekdays("short",!1,!1),1);case"EEEE":return Tn(r.weekdays("long",!1,!1),1);case"ccc":return Tn(r.weekdays("short",!0,!1),1);case"cccc":return Tn(r.weekdays("long",!0,!1),1);case"Z":case"ZZ":return Mn(new RegExp("([+-]"+c.source+")(?::("+o.source+"))?"),2);case"ZZZ":return Mn(new RegExp("([+-]"+c.source+")("+o.source+")?"),2);case"z":return Nn(/[a-z_+-/]{1,256}?/i);default:return y(e)}}(n)||{invalidReason:pn}).token=n,v;var n,r,i,o,a,u,s,c,l,f,d,h,m,y,v}),o=i.find(function(e){return e.invalidReason});if(o)return{input:t,tokens:r,invalidReason:o.invalidReason};var a=function(e){return["^"+e.map(function(e){return e.regex}).reduce(function(e,t){return e+"("+t.source+")"},"")+"$",e]}(i),u=a[0],s=a[1],c=RegExp(u,"i"),l=function(e,t,n){var r=e.match(t);if(r){var i={},o=1;for(var a in n)if(Q(n,a)){var u=n[a],s=u.groups?u.groups+1:1;!u.literal&&u.token&&(i[u.token.val[0]]=u.deser(r.slice(o,o+s))),o+=s}return[r,i]}return[r,{}]}(t,c,s),f=l[0],d=l[1],h=d?function(e){var t;return t=R(e.Z)?R(e.z)?null:He.create(e.z):new Re(e.Z),R(e.q)||(e.M=3*(e.q-1)+1),R(e.h)||(e.h<12&&1===e.a?e.h+=12:12===e.h&&0===e.a&&(e.h=0)),0===e.G&&e.y&&(e.y=-e.y),R(e.u)||(e.S=te(e.u)),[Object.keys(e).reduce(function(t,n){var r=function(e){switch(e){case"S":return"millisecond";case"s":return"second";case"m":return"minute";case"h":case"H":return"hour";case"d":return"day";case"o":return"ordinal";case"L":case"M":return"month";case"y":return"year";case"E":case"c":return"weekday";case"W":return"weekNumber";case"k":return"weekYear";case"q":return"quarter";default:return null}}(n);return r&&(t[r]=e[n]),t},{}),t]}(d):[null,null],y=h[0],v=h[1];if(Q(d,"a")&&Q(d,"H"))throw new m("Can't include meridiem when specifying 24-hour format");return{input:t,tokens:r,regex:c,rawMatches:f,matches:d,result:y,zone:v}}var Ln=[0,31,59,90,120,151,181,212,243,273,304,334],xn=[0,31,60,91,121,152,182,213,244,274,305,335];function Cn(e,t){return new Ce("unit out of range","you specified "+t+" (of type "+typeof t+") as a "+e+", which is invalid")}function Fn(e,t,n){var r=new Date(Date.UTC(e,t-1,n)).getUTCDay();return 0===r?7:r}function Zn(e,t,n){return n+(re(e)?xn:Ln)[t-1]}function jn(e,t){var n=re(e)?xn:Ln,r=n.findIndex(function(e){return e<t});return{month:r+1,day:t-n[r]}}function An(e){var t,n=e.year,r=e.month,i=e.day,o=Zn(n,r,i),a=Fn(n,r,i),u=Math.floor((o-a+10)/7);return u<1?u=ue(t=n-1):u>ue(n)?(t=n+1,u=1):t=n,Object.assign({weekYear:t,weekNumber:u,weekday:a},me(e))}function zn(e){var t,n=e.weekYear,r=e.weekNumber,i=e.weekday,o=Fn(n,1,4),a=ie(n),u=7*r+i-o-3;u<1?u+=ie(t=n-1):u>a?(t=n+1,u-=ie(n)):t=n;var s=jn(t,u),c=s.month,l=s.day;return Object.assign({year:t,month:c,day:l},me(e))}function _n(e){var t=e.year,n=Zn(t,e.month,e.day);return Object.assign({year:t,ordinal:n},me(e))}function qn(e){var t=e.year,n=jn(t,e.ordinal),r=n.month,i=n.day;return Object.assign({year:t,month:r,day:i},me(e))}function Hn(e){var t=W(e.year),n=K(e.month,1,12),r=K(e.day,1,oe(e.year,e.month));return t?n?!r&&Cn("day",e.day):Cn("month",e.month):Cn("year",e.year)}function Un(e){var t=e.hour,n=e.minute,r=e.second,i=e.millisecond,o=K(t,0,23)||24===t&&0===n&&0===r&&0===i,a=K(n,0,59),u=K(r,0,59),s=K(i,0,999);return o?a?u?!s&&Cn("millisecond",i):Cn("second",r):Cn("minute",n):Cn("hour",t)}function Rn(e){return new Ce("unsupported zone",'the zone "'+e.name+'" is not supported')}function Pn(e){return null===e.weekData&&(e.weekData=An(e.c)),e.weekData}function Wn(e,t){var n={ts:e.ts,zone:e.zone,c:e.c,o:e.o,loc:e.loc,invalid:e.invalid};return new sr(Object.assign({},n,t,{old:n}))}function Jn(e,t,n){var r=e-60*t*1e3,i=n.offset(r);if(t===i)return[r,t];r-=60*(i-t)*1e3;var o=n.offset(r);return i===o?[r,i]:[e-60*Math.min(i,o)*1e3,Math.max(i,o)]}function Yn(e,t){var n=new Date(e+=60*t*1e3);return{year:n.getUTCFullYear(),month:n.getUTCMonth()+1,day:n.getUTCDate(),hour:n.getUTCHours(),minute:n.getUTCMinutes(),second:n.getUTCSeconds(),millisecond:n.getUTCMilliseconds()}}function Gn(e,t,n){return Jn(ae(e),t,n)}function $n(e,t){var n=e.o,r=e.c.year+Math.trunc(t.years),i=e.c.month+Math.trunc(t.months)+3*Math.trunc(t.quarters),o=Object.assign({},e.c,{year:r,month:i,day:Math.min(e.c.day,oe(r,i))+Math.trunc(t.days)+7*Math.trunc(t.weeks)}),a=an.fromObject({years:t.years-Math.trunc(t.years),quarters:t.quarters-Math.trunc(t.quarters),months:t.months-Math.trunc(t.months),weeks:t.weeks-Math.trunc(t.weeks),days:t.days-Math.trunc(t.days),hours:t.hours,minutes:t.minutes,seconds:t.seconds,milliseconds:t.milliseconds}).as("milliseconds"),u=Jn(ae(o),n,e.zone),s=u[0],c=u[1];return 0!==a&&(s+=a,c=e.zone.offset(s)),{ts:s,o:c}}function Bn(e,t,n,r,i){var o=n.setZone,a=n.zone;if(e&&0!==Object.keys(e).length){var u=t||a,s=sr.fromObject(Object.assign(e,n,{zone:u,setZone:void 0}));return o?s:s.setZone(a)}return sr.invalid(new Ce("unparsable",'the input "'+i+"\" can't be parsed as "+r))}function Qn(e,t,n){return void 0===n&&(n=!0),e.isValid?xe.create(ct.create("en-US"),{allowZ:n,forceSimple:!0}).formatDateTimeFromString(e,t):null}function Kn(e,t){var n=t.suppressSeconds,r=void 0!==n&&n,i=t.suppressMilliseconds,o=void 0!==i&&i,a=t.includeOffset,u=t.includePrefix,s=void 0!==u&&u,c=t.includeZone,l=void 0!==c&&c,f=t.spaceZone,d=void 0!==f&&f,h=t.format,m=void 0===h?"extended":h,y="basic"===m?"HHmm":"HH:mm";r&&0===e.second&&0===e.millisecond||(y+="basic"===m?"ss":":ss",o&&0===e.millisecond||(y+=".SSS")),(l||a)&&d&&(y+=" "),l?y+="z":a&&(y+="basic"===m?"ZZZ":"ZZ");var v=Qn(e,y);return s&&(v="T"+v),v}var Xn={month:1,day:1,hour:0,minute:0,second:0,millisecond:0},er={weekNumber:1,weekday:1,hour:0,minute:0,second:0,millisecond:0},tr={ordinal:1,hour:0,minute:0,second:0,millisecond:0},nr=["year","month","day","hour","minute","second","millisecond"],rr=["weekYear","weekNumber","weekday","hour","minute","second","millisecond"],ir=["year","ordinal","hour","minute","second","millisecond"];function or(e){var t={year:"year",years:"year",month:"month",months:"month",day:"day",days:"day",hour:"hour",hours:"hour",minute:"minute",minutes:"minute",quarter:"quarter",quarters:"quarter",second:"second",seconds:"second",millisecond:"millisecond",milliseconds:"millisecond",weekday:"weekday",weekdays:"weekday",weeknumber:"weekNumber",weeksnumber:"weekNumber",weeknumbers:"weekNumber",weekyear:"weekYear",weekyears:"weekYear",ordinal:"ordinal"}[e.toLowerCase()];if(!t)throw new y(e);return t}function ar(e,t){for(var n,r=c(nr);!(n=r()).done;){var i=n.value;R(e[i])&&(e[i]=Xn[i])}var o=Hn(e)||Un(e);if(o)return sr.invalid(o);var a=Ke.now(),u=Gn(e,t.offset(a),t),s=u[0],l=u[1];return new sr({ts:s,zone:t,o:l})}function ur(e,t,n){var r=!!R(n.round)||n.round,i=function(e,i){return e=ne(e,r||n.calendary?0:2,!0),t.loc.clone(n).relFormatter(n).format(e,i)},o=function(r){return n.calendary?t.hasSame(e,r)?0:t.startOf(r).diff(e.startOf(r),r).get(r):t.diff(e,r).get(r)};if(n.unit)return i(o(n.unit),n.unit);for(var a,u=c(n.units);!(a=u()).done;){var s=a.value,l=o(s);if(Math.abs(l)>=1)return i(l,s)}return i(e>t?-0:0,n.units[n.units.length-1])}var sr=function(){function e(e){var t=e.zone||Ke.defaultZone,n=e.invalid||(Number.isNaN(e.ts)?new Ce("invalid input"):null)||(t.isValid?null:Rn(t));this.ts=R(e.ts)?Ke.now():e.ts;var r=null,i=null;if(!n)if(e.old&&e.old.ts===this.ts&&e.old.zone.equals(t)){var o=[e.old.c,e.old.o];r=o[0],i=o[1]}else{var a=t.offset(this.ts);r=Yn(this.ts,a),r=(n=Number.isNaN(r.year)?new Ce("invalid input"):null)?null:r,i=n?null:a}this._zone=t,this.loc=e.loc||ct.create(),this.invalid=n,this.weekData=null,this.c=r,this.o=i,this.isLuxonDateTime=!0}e.now=function(){return new e({})},e.local=function(t,n,r,i,o,a,u){return R(t)?e.now():ar({year:t,month:n,day:r,hour:i,minute:o,second:a,millisecond:u},Ke.defaultZone)},e.utc=function(t,n,r,i,o,a,u){return R(t)?new e({ts:Ke.now(),zone:Re.utcInstance}):ar({year:t,month:n,day:r,hour:i,minute:o,second:a,millisecond:u},Re.utcInstance)},e.fromJSDate=function(t,n){void 0===n&&(n={});var r,i=(r=t,"[object Date]"===Object.prototype.toString.call(r)?t.valueOf():NaN);if(Number.isNaN(i))return e.invalid("invalid input");var o=We(n.zone,Ke.defaultZone);return o.isValid?new e({ts:i,zone:o,loc:ct.fromObject(n)}):e.invalid(Rn(o))},e.fromMillis=function(t,n){if(void 0===n&&(n={}),P(t))return t<-864e13||t>864e13?e.invalid("Timestamp out of range"):new e({ts:t,zone:We(n.zone,Ke.defaultZone),loc:ct.fromObject(n)});throw new v("fromMillis requires a numerical input, but received a "+typeof t+" with value "+t)},e.fromSeconds=function(t,n){if(void 0===n&&(n={}),P(t))return new e({ts:1e3*t,zone:We(n.zone,Ke.defaultZone),loc:ct.fromObject(n)});throw new v("fromSeconds requires a numerical input")},e.fromObject=function(t){var n=We(t.zone,Ke.defaultZone);if(!n.isValid)return e.invalid(Rn(n));var r=Ke.now(),i=n.offset(r),o=de(t,or,["zone","locale","outputCalendar","numberingSystem"]),a=!R(o.ordinal),u=!R(o.year),s=!R(o.month)||!R(o.day),l=u||s,f=o.weekYear||o.weekNumber,d=ct.fromObject(t);if((l||a)&&f)throw new m("Can't mix weekYear/weekNumber units with year/month/day or ordinals");if(s&&a)throw new m("Can't mix ordinal dates with month/day");var h,y,v=f||o.weekday&&!l,g=Yn(r,i);v?(h=rr,y=er,g=An(g)):a?(h=ir,y=tr,g=_n(g)):(h=nr,y=Xn);for(var p,w=!1,k=c(h);!(p=k()).done;){var b=p.value;R(o[b])?o[b]=w?y[b]:g[b]:w=!0}var O=(v?function(e){var t=W(e.weekYear),n=K(e.weekNumber,1,ue(e.weekYear)),r=K(e.weekday,1,7);return t?n?!r&&Cn("weekday",e.weekday):Cn("week",e.week):Cn("weekYear",e.weekYear)}(o):a?function(e){var t=W(e.year),n=K(e.ordinal,1,ie(e.year));return t?!n&&Cn("ordinal",e.ordinal):Cn("year",e.year)}(o):Hn(o))||Un(o);if(O)return e.invalid(O);var S=Gn(v?zn(o):a?qn(o):o,i,n),T=new e({ts:S[0],zone:n,o:S[1],loc:d});return o.weekday&&l&&t.weekday!==T.weekday?e.invalid("mismatched weekday","you can't specify both a weekday of "+o.weekday+" and a date of "+T.toISO()):T},e.fromISO=function(e,t){void 0===t&&(t={});var n=dt(e,[_t,Rt],[qt,Pt],[Ht,Wt],[Ut,Jt]);return Bn(n[0],n[1],t,"ISO 8601",e)},e.fromRFC2822=function(e,t){void 0===t&&(t={});var n=dt(function(e){return e.replace(/\([^)]*\)|[\n\t]/g," ").replace(/(\s\s+)/g," ").trim()}(e),[xt,Ct]);return Bn(n[0],n[1],t,"RFC 2822",e)},e.fromHTTP=function(e,t){void 0===t&&(t={});var n=dt(e,[Ft,At],[Zt,At],[jt,zt]);return Bn(n[0],n[1],t,"HTTP",t)},e.fromFormat=function(t,n,r){if(void 0===r&&(r={}),R(t)||R(n))throw new v("fromFormat requires an input string and a format");var i=r,o=i.locale,a=void 0===o?null:o,u=i.numberingSystem,s=void 0===u?null:u,c=function(e,t,n){var r=Vn(e,t,n);return[r.result,r.zone,r.invalidReason]}(ct.fromOpts({locale:a,numberingSystem:s,defaultToEN:!0}),t,n),l=c[0],f=c[1],d=c[2];return d?e.invalid(d):Bn(l,f,r,"format "+n,t)},e.fromString=function(t,n,r){return void 0===r&&(r={}),e.fromFormat(t,n,r)},e.fromSQL=function(e,t){void 0===t&&(t={});var n=dt(e,[Gt,Bt],[$t,Qt]);return Bn(n[0],n[1],t,"SQL",e)},e.invalid=function(t,n){if(void 0===n&&(n=null),!t)throw new v("need to specify a reason the DateTime is invalid");var r=t instanceof Ce?t:new Ce(t,n);if(Ke.throwOnInvalid)throw new f(r);return new e({invalid:r})},e.isDateTime=function(e){return e&&e.isLuxonDateTime||!1};var t=e.prototype;return t.get=function(e){return this[e]},t.resolvedLocaleOpts=function(e){void 0===e&&(e={});var t=xe.create(this.loc.clone(e),e).resolvedOptions(this);return{locale:t.locale,numberingSystem:t.numberingSystem,outputCalendar:t.calendar}},t.toUTC=function(e,t){return void 0===e&&(e=0),void 0===t&&(t={}),this.setZone(Re.instance(e),t)},t.toLocal=function(){return this.setZone(Ke.defaultZone)},t.setZone=function(t,n){var r=void 0===n?{}:n,i=r.keepLocalTime,o=void 0!==i&&i,a=r.keepCalendarTime,u=void 0!==a&&a;if((t=We(t,Ke.defaultZone)).equals(this.zone))return this;if(t.isValid){var s=this.ts;if(o||u){var c=t.offset(this.ts);s=Gn(this.toObject(),c,t)[0]}return Wn(this,{ts:s,zone:t})}return e.invalid(Rn(t))},t.reconfigure=function(e){var t=void 0===e?{}:e,n=t.locale,r=t.numberingSystem,i=t.outputCalendar;return Wn(this,{loc:this.loc.clone({locale:n,numberingSystem:r,outputCalendar:i})})},t.setLocale=function(e){return this.reconfigure({locale:e})},t.set=function(e){if(!this.isValid)return this;var t,n=de(e,or,[]),r=!R(n.weekYear)||!R(n.weekNumber)||!R(n.weekday),i=!R(n.ordinal),o=!R(n.year),a=!R(n.month)||!R(n.day),u=o||a,s=n.weekYear||n.weekNumber;if((u||i)&&s)throw new m("Can't mix weekYear/weekNumber units with year/month/day or ordinals");if(a&&i)throw new m("Can't mix ordinal dates with month/day");r?t=zn(Object.assign(An(this.c),n)):R(n.ordinal)?(t=Object.assign(this.toObject(),n),R(n.day)&&(t.day=Math.min(oe(t.year,t.month),t.day))):t=qn(Object.assign(_n(this.c),n));var c=Gn(t,this.o,this.zone);return Wn(this,{ts:c[0],o:c[1]})},t.plus=function(e){return this.isValid?Wn(this,$n(this,un(e))):this},t.minus=function(e){return this.isValid?Wn(this,$n(this,un(e).negate())):this},t.startOf=function(e){if(!this.isValid)return this;var t={},n=an.normalizeUnit(e);switch(n){case"years":t.month=1;case"quarters":case"months":t.day=1;case"weeks":case"days":t.hour=0;case"hours":t.minute=0;case"minutes":t.second=0;case"seconds":t.millisecond=0}if("weeks"===n&&(t.weekday=1),"quarters"===n){var r=Math.ceil(this.month/3);t.month=3*(r-1)+1}return this.set(t)},t.endOf=function(e){var t;return this.isValid?this.plus((t={},t[e]=1,t)).startOf(e).minus(1):this},t.toFormat=function(e,t){return void 0===t&&(t={}),this.isValid?xe.create(this.loc.redefaultToEN(t)).formatDateTimeFromString(this,e):"Invalid DateTime"},t.toLocaleString=function(e){return void 0===e&&(e=b),this.isValid?xe.create(this.loc.clone(e),e).formatDateTime(this):"Invalid DateTime"},t.toLocaleParts=function(e){return void 0===e&&(e={}),this.isValid?xe.create(this.loc.clone(e),e).formatDateTimeParts(this):[]},t.toISO=function(e){return void 0===e&&(e={}),this.isValid?this.toISODate(e)+"T"+this.toISOTime(e):null},t.toISODate=function(e){var t=(void 0===e?{}:e).format,n="basic"===(void 0===t?"extended":t)?"yyyyMMdd":"yyyy-MM-dd";return this.year>9999&&(n="+"+n),Qn(this,n)},t.toISOWeekDate=function(){return Qn(this,"kkkk-'W'WW-c")},t.toISOTime=function(e){var t=void 0===e?{}:e,n=t.suppressMilliseconds,r=void 0!==n&&n,i=t.suppressSeconds,o=void 0!==i&&i,a=t.includeOffset,u=void 0===a||a,s=t.includePrefix,c=void 0!==s&&s,l=t.format;return Kn(this,{suppressSeconds:o,suppressMilliseconds:r,includeOffset:u,includePrefix:c,format:void 0===l?"extended":l})},t.toRFC2822=function(){return Qn(this,"EEE, dd LLL yyyy HH:mm:ss ZZZ",!1)},t.toHTTP=function(){return Qn(this.toUTC(),"EEE, dd LLL yyyy HH:mm:ss 'GMT'")},t.toSQLDate=function(){return Qn(this,"yyyy-MM-dd")},t.toSQLTime=function(e){var t=void 0===e?{}:e,n=t.includeOffset,r=void 0===n||n,i=t.includeZone;return Kn(this,{includeOffset:r,includeZone:void 0!==i&&i,spaceZone:!0})},t.toSQL=function(e){return void 0===e&&(e={}),this.isValid?this.toSQLDate()+" "+this.toSQLTime(e):null},t.toString=function(){return this.isValid?this.toISO():"Invalid DateTime"},t.valueOf=function(){return this.toMillis()},t.toMillis=function(){return this.isValid?this.ts:NaN},t.toSeconds=function(){return this.isValid?this.ts/1e3:NaN},t.toJSON=function(){return this.toISO()},t.toBSON=function(){return this.toJSDate()},t.toObject=function(e){if(void 0===e&&(e={}),!this.isValid)return{};var t=Object.assign({},this.c);return e.includeConfig&&(t.outputCalendar=this.outputCalendar,t.numberingSystem=this.loc.numberingSystem,t.locale=this.loc.locale),t},t.toJSDate=function(){return new Date(this.isValid?this.ts:NaN)},t.diff=function(e,t,n){if(void 0===t&&(t="milliseconds"),void 0===n&&(n={}),!this.isValid||!e.isValid)return an.invalid(this.invalid||e.invalid,"created by diffing an invalid DateTime");var r,i=Object.assign({locale:this.locale,numberingSystem:this.numberingSystem},n),o=(r=t,Array.isArray(r)?r:[r]).map(an.normalizeUnit),a=e.valueOf()>this.valueOf(),u=hn(a?this:e,a?e:this,o,i);return a?u.negate():u},t.diffNow=function(t,n){return void 0===t&&(t="milliseconds"),void 0===n&&(n={}),this.diff(e.now(),t,n)},t.until=function(e){return this.isValid?ln.fromDateTimes(this,e):this},t.hasSame=function(e,t){if(!this.isValid)return!1;var n=e.valueOf(),r=this.setZone(e.zone,{keepLocalTime:!0});return r.startOf(t)<=n&&n<=r.endOf(t)},t.equals=function(e){return this.isValid&&e.isValid&&this.valueOf()===e.valueOf()&&this.zone.equals(e.zone)&&this.loc.equals(e.loc)},t.toRelative=function(t){if(void 0===t&&(t={}),!this.isValid)return null;var n=t.base||e.fromObject({zone:this.zone}),r=t.padding?this<n?-t.padding:t.padding:0,i=["years","months","days","hours","minutes","seconds"],o=t.unit;return Array.isArray(t.unit)&&(i=t.unit,o=void 0),ur(n,this.plus(r),Object.assign(t,{numeric:"always",units:i,unit:o}))},t.toRelativeCalendar=function(t){return void 0===t&&(t={}),this.isValid?ur(t.base||e.fromObject({zone:this.zone}),this,Object.assign(t,{numeric:"auto",units:["years","months","days"],calendary:!0})):null},e.min=function(){for(var t=arguments.length,n=new Array(t),r=0;r<t;r++)n[r]=arguments[r];if(!n.every(e.isDateTime))throw new v("min requires all arguments be DateTimes");return $(n,function(e){return e.valueOf()},Math.min)},e.max=function(){for(var t=arguments.length,n=new Array(t),r=0;r<t;r++)n[r]=arguments[r];if(!n.every(e.isDateTime))throw new v("max requires all arguments be DateTimes");return $(n,function(e){return e.valueOf()},Math.max)},e.fromFormatExplain=function(e,t,n){void 0===n&&(n={});var r=n,i=r.locale,o=void 0===i?null:i,a=r.numberingSystem,u=void 0===a?null:a;return Vn(ct.fromOpts({locale:o,numberingSystem:u,defaultToEN:!0}),e,t)},e.fromStringExplain=function(t,n,r){return void 0===r&&(r={}),e.fromFormatExplain(t,n,r)},n(e,[{key:"isValid",get:function(){return null===this.invalid}},{key:"invalidReason",get:function(){return this.invalid?this.invalid.reason:null}},{key:"invalidExplanation",get:function(){return this.invalid?this.invalid.explanation:null}},{key:"locale",get:function(){return this.isValid?this.loc.locale:null}},{key:"numberingSystem",get:function(){return this.isValid?this.loc.numberingSystem:null}},{key:"outputCalendar",get:function(){return this.isValid?this.loc.outputCalendar:null}},{key:"zone",get:function(){return this._zone}},{key:"zoneName",get:function(){return this.isValid?this.zone.name:null}},{key:"year",get:function(){return this.isValid?this.c.year:NaN}},{key:"quarter",get:function(){return this.isValid?Math.ceil(this.c.month/3):NaN}},{key:"month",get:function(){return this.isValid?this.c.month:NaN}},{key:"day",get:function(){return this.isValid?this.c.day:NaN}},{key:"hour",get:function(){return this.isValid?this.c.hour:NaN}},{key:"minute",get:function(){return this.isValid?this.c.minute:NaN}},{key:"second",get:function(){return this.isValid?this.c.second:NaN}},{key:"millisecond",get:function(){return this.isValid?this.c.millisecond:NaN}},{key:"weekYear",get:function(){return this.isValid?Pn(this).weekYear:NaN}},{key:"weekNumber",get:function(){return this.isValid?Pn(this).weekNumber:NaN}},{key:"weekday",get:function(){return this.isValid?Pn(this).weekday:NaN}},{key:"ordinal",get:function(){return this.isValid?_n(this.c).ordinal:NaN}},{key:"monthShort",get:function(){return this.isValid?fn.months("short",{locObj:this.loc})[this.month-1]:null}},{key:"monthLong",get:function(){return this.isValid?fn.months("long",{locObj:this.loc})[this.month-1]:null}},{key:"weekdayShort",get:function(){return this.isValid?fn.weekdays("short",{locObj:this.loc})[this.weekday-1]:null}},{key:"weekdayLong",get:function(){return this.isValid?fn.weekdays("long",{locObj:this.loc})[this.weekday-1]:null}},{key:"offset",get:function(){return this.isValid?+this.o:NaN}},{key:"offsetNameShort",get:function(){return this.isValid?this.zone.offsetName(this.ts,{format:"short",locale:this.locale}):null}},{key:"offsetNameLong",get:function(){return this.isValid?this.zone.offsetName(this.ts,{format:"long",locale:this.locale}):null}},{key:"isOffsetFixed",get:function(){return this.isValid?this.zone.universal:null}},{key:"isInDST",get:function(){return!this.isOffsetFixed&&(this.offset>this.set({month:1}).offset||this.offset>this.set({month:5}).offset)}},{key:"isInLeapYear",get:function(){return re(this.year)}},{key:"daysInMonth",get:function(){return oe(this.year,this.month)}},{key:"daysInYear",get:function(){return this.isValid?ie(this.year):NaN}},{key:"weeksInWeekYear",get:function(){return this.isValid?ue(this.weekYear):NaN}}],[{key:"DATE_SHORT",get:function(){return b}},{key:"DATE_MED",get:function(){return O}},{key:"DATE_MED_WITH_WEEKDAY",get:function(){return S}},{key:"DATE_FULL",get:function(){return T}},{key:"DATE_HUGE",get:function(){return M}},{key:"TIME_SIMPLE",get:function(){return N}},{key:"TIME_WITH_SECONDS",get:function(){return E}},{key:"TIME_WITH_SHORT_OFFSET",get:function(){return D}},{key:"TIME_WITH_LONG_OFFSET",get:function(){return I}},{key:"TIME_24_SIMPLE",get:function(){return V}},{key:"TIME_24_WITH_SECONDS",get:function(){return L}},{key:"TIME_24_WITH_SHORT_OFFSET",get:function(){return x}},{key:"TIME_24_WITH_LONG_OFFSET",get:function(){return C}},{key:"DATETIME_SHORT",get:function(){return F}},{key:"DATETIME_SHORT_WITH_SECONDS",get:function(){return Z}},{key:"DATETIME_MED",get:function(){return j}},{key:"DATETIME_MED_WITH_SECONDS",get:function(){return A}},{key:"DATETIME_MED_WITH_WEEKDAY",get:function(){return z}},{key:"DATETIME_FULL",get:function(){return _}},{key:"DATETIME_FULL_WITH_SECONDS",get:function(){return q}},{key:"DATETIME_HUGE",get:function(){return H}},{key:"DATETIME_HUGE_WITH_SECONDS",get:function(){return U}}]),e}();function cr(e){if(sr.isDateTime(e))return e;if(e&&e.valueOf&&P(e.valueOf()))return sr.fromJSDate(e);if(e&&"object"==typeof e)return sr.fromObject(e);throw new v("Unknown datetime argument: "+e+", of type "+typeof e)}return e.DateTime=sr,e.Duration=an,e.FixedOffsetZone=Re,e.IANAZone=He,e.Info=fn,e.Interval=ln,e.InvalidZone=Pe,e.LocalZone=je,e.Settings=Ke,e.VERSION="1.27.0",e.Zone=Fe,e}({});
...\ No newline at end of file ...\ No newline at end of file
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
1 +Copyright 2019 JS Foundation and other contributors
2 +
3 +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 +
5 +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 +
7 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1 +{
2 + "name": "luxon",
3 + "version": "1.27.0",
4 + "description": "Immutable date wrapper",
5 + "author": "Isaac Cambron",
6 + "keywords": [
7 + "date",
8 + "immutable"
9 + ],
10 + "repository": "https://github.com/moment/luxon",
11 + "scripts": {
12 + "build": "babel-node tasks/buildAll.js",
13 + "build-node": "babel-node tasks/buildNode.js",
14 + "build-global": "babel-node tasks/buildGlobal.js",
15 + "jest": "jest",
16 + "test": "jest --coverage",
17 + "docs": "esdoc -c docs/index.js",
18 + "site": "cp -r site/** build/",
19 + "lint": "eslint --quiet src test benchmarks",
20 + "lint!": "npm run format && npm run lint",
21 + "format": "prettier --write 'src/**/*.js' 'test/**/*.js' 'benchmarks/*.js'",
22 + "benchmark": "babel-node benchmarks/index.js",
23 + "codecov": "codecov",
24 + "check-doc-coverage": "babel-node tasks/docCoverage"
25 + },
26 + "husky": {
27 + "hooks": {
28 + "pre-commit": "lint-staged"
29 + }
30 + },
31 + "lint-staged": {
32 + "*.{js,json}": [
33 + "prettier --write"
34 + ]
35 + },
36 + "devDependencies": {
37 + "@babel/core": "^7.9.0",
38 + "@babel/node": "^7.8.7",
39 + "@babel/plugin-external-helpers": "^7.8.3",
40 + "@babel/preset-env": "^7.9.5",
41 + "@rollup/plugin-commonjs": "^11.1.0",
42 + "@rollup/plugin-node-resolve": "^7.1.3",
43 + "babel-core": "^7.0.0-bridge.0",
44 + "babel-eslint": "latest",
45 + "babel-jest": "^25.3.0",
46 + "benchmark": "latest",
47 + "codecov": ">= 3.6.5",
48 + "core-js": "^3.6.5",
49 + "esdoc": "^1.1.0",
50 + "esdoc-standard-plugin": "latest",
51 + "eslint": "6.4.0",
52 + "eslint-config-defaults": "latest",
53 + "eslint-config-prettier": "6.3.0",
54 + "eslint-config-standard": "^14.1.1",
55 + "eslint-plugin-babel": "latest",
56 + "eslint-plugin-import": "^2.20.2",
57 + "eslint-plugin-node": "10.0.0",
58 + "eslint-plugin-prettier": "3.1.1",
59 + "eslint-plugin-promise": "latest",
60 + "eslint-plugin-react": "^7.19.0",
61 + "eslint-plugin-standard": "^4.0.1",
62 + "fs-extra": "^6.0.1",
63 + "full-icu": "^1.3.1",
64 + "husky": "^4.2.5",
65 + "jest": "^25.3.0",
66 + "lint-staged": "^10.1.3",
67 + "prettier": "1.14.3",
68 + "rollup": "^1.32.1",
69 + "rollup-plugin-babel": "^4.4.0",
70 + "rollup-plugin-babel-minify": "^6.2.0",
71 + "uglify-es": "^3.3.9"
72 + },
73 + "main": "build/node/luxon.js",
74 + "module": "src/luxon.js",
75 + "browser": "build/cjs-browser/luxon.js",
76 + "jsdelivr": "build/global/luxon.min.js",
77 + "unpkg": "build/global/luxon.min.js",
78 + "engines": {
79 + "node": "*"
80 + },
81 + "files": [
82 + "build/node/luxon.js",
83 + "build/node/luxon.js.map",
84 + "build/cjs-browser/luxon.js",
85 + "build/cjs-browser/luxon.js.map",
86 + "build/amd/luxon.js",
87 + "build/amd/luxon.js.map",
88 + "build/global/luxon.js",
89 + "build/global/luxon.js.map",
90 + "build/global/luxon.min.js",
91 + "build/global/luxon.min.js.map",
92 + "src"
93 + ],
94 + "license": "MIT",
95 + "sideEffects": false
96 +}
1 +import Duration, { friendlyDuration } from "./duration.js";
2 +import Interval from "./interval.js";
3 +import Settings from "./settings.js";
4 +import Info from "./info.js";
5 +import Formatter from "./impl/formatter.js";
6 +import FixedOffsetZone from "./zones/fixedOffsetZone.js";
7 +import Locale from "./impl/locale.js";
8 +import {
9 + isUndefined,
10 + maybeArray,
11 + isDate,
12 + isNumber,
13 + bestBy,
14 + daysInMonth,
15 + daysInYear,
16 + isLeapYear,
17 + weeksInWeekYear,
18 + normalizeObject,
19 + roundTo,
20 + objToLocalTS
21 +} from "./impl/util.js";
22 +import { normalizeZone } from "./impl/zoneUtil.js";
23 +import diff from "./impl/diff.js";
24 +import { parseRFC2822Date, parseISODate, parseHTTPDate, parseSQL } from "./impl/regexParser.js";
25 +import { parseFromTokens, explainFromTokens } from "./impl/tokenParser.js";
26 +import {
27 + gregorianToWeek,
28 + weekToGregorian,
29 + gregorianToOrdinal,
30 + ordinalToGregorian,
31 + hasInvalidGregorianData,
32 + hasInvalidWeekData,
33 + hasInvalidOrdinalData,
34 + hasInvalidTimeData
35 +} from "./impl/conversions.js";
36 +import * as Formats from "./impl/formats.js";
37 +import {
38 + InvalidArgumentError,
39 + ConflictingSpecificationError,
40 + InvalidUnitError,
41 + InvalidDateTimeError
42 +} from "./errors.js";
43 +import Invalid from "./impl/invalid.js";
44 +
45 +const INVALID = "Invalid DateTime";
46 +const MAX_DATE = 8.64e15;
47 +
48 +function unsupportedZone(zone) {
49 + return new Invalid("unsupported zone", `the zone "${zone.name}" is not supported`);
50 +}
51 +
52 +// we cache week data on the DT object and this intermediates the cache
53 +function possiblyCachedWeekData(dt) {
54 + if (dt.weekData === null) {
55 + dt.weekData = gregorianToWeek(dt.c);
56 + }
57 + return dt.weekData;
58 +}
59 +
60 +// clone really means, "make a new object with these modifications". all "setters" really use this
61 +// to create a new object while only changing some of the properties
62 +function clone(inst, alts) {
63 + const current = {
64 + ts: inst.ts,
65 + zone: inst.zone,
66 + c: inst.c,
67 + o: inst.o,
68 + loc: inst.loc,
69 + invalid: inst.invalid
70 + };
71 + return new DateTime(Object.assign({}, current, alts, { old: current }));
72 +}
73 +
74 +// find the right offset a given local time. The o input is our guess, which determines which
75 +// offset we'll pick in ambiguous cases (e.g. there are two 3 AMs b/c Fallback DST)
76 +function fixOffset(localTS, o, tz) {
77 + // Our UTC time is just a guess because our offset is just a guess
78 + let utcGuess = localTS - o * 60 * 1000;
79 +
80 + // Test whether the zone matches the offset for this ts
81 + const o2 = tz.offset(utcGuess);
82 +
83 + // If so, offset didn't change and we're done
84 + if (o === o2) {
85 + return [utcGuess, o];
86 + }
87 +
88 + // If not, change the ts by the difference in the offset
89 + utcGuess -= (o2 - o) * 60 * 1000;
90 +
91 + // If that gives us the local time we want, we're done
92 + const o3 = tz.offset(utcGuess);
93 + if (o2 === o3) {
94 + return [utcGuess, o2];
95 + }
96 +
97 + // If it's different, we're in a hole time. The offset has changed, but the we don't adjust the time
98 + return [localTS - Math.min(o2, o3) * 60 * 1000, Math.max(o2, o3)];
99 +}
100 +
101 +// convert an epoch timestamp into a calendar object with the given offset
102 +function tsToObj(ts, offset) {
103 + ts += offset * 60 * 1000;
104 +
105 + const d = new Date(ts);
106 +
107 + return {
108 + year: d.getUTCFullYear(),
109 + month: d.getUTCMonth() + 1,
110 + day: d.getUTCDate(),
111 + hour: d.getUTCHours(),
112 + minute: d.getUTCMinutes(),
113 + second: d.getUTCSeconds(),
114 + millisecond: d.getUTCMilliseconds()
115 + };
116 +}
117 +
118 +// convert a calendar object to a epoch timestamp
119 +function objToTS(obj, offset, zone) {
120 + return fixOffset(objToLocalTS(obj), offset, zone);
121 +}
122 +
123 +// create a new DT instance by adding a duration, adjusting for DSTs
124 +function adjustTime(inst, dur) {
125 + const oPre = inst.o,
126 + year = inst.c.year + Math.trunc(dur.years),
127 + month = inst.c.month + Math.trunc(dur.months) + Math.trunc(dur.quarters) * 3,
128 + c = Object.assign({}, inst.c, {
129 + year,
130 + month,
131 + day:
132 + Math.min(inst.c.day, daysInMonth(year, month)) +
133 + Math.trunc(dur.days) +
134 + Math.trunc(dur.weeks) * 7
135 + }),
136 + millisToAdd = Duration.fromObject({
137 + years: dur.years - Math.trunc(dur.years),
138 + quarters: dur.quarters - Math.trunc(dur.quarters),
139 + months: dur.months - Math.trunc(dur.months),
140 + weeks: dur.weeks - Math.trunc(dur.weeks),
141 + days: dur.days - Math.trunc(dur.days),
142 + hours: dur.hours,
143 + minutes: dur.minutes,
144 + seconds: dur.seconds,
145 + milliseconds: dur.milliseconds
146 + }).as("milliseconds"),
147 + localTS = objToLocalTS(c);
148 +
149 + let [ts, o] = fixOffset(localTS, oPre, inst.zone);
150 +
151 + if (millisToAdd !== 0) {
152 + ts += millisToAdd;
153 + // that could have changed the offset by going over a DST, but we want to keep the ts the same
154 + o = inst.zone.offset(ts);
155 + }
156 +
157 + return { ts, o };
158 +}
159 +
160 +// helper useful in turning the results of parsing into real dates
161 +// by handling the zone options
162 +function parseDataToDateTime(parsed, parsedZone, opts, format, text) {
163 + const { setZone, zone } = opts;
164 + if (parsed && Object.keys(parsed).length !== 0) {
165 + const interpretationZone = parsedZone || zone,
166 + inst = DateTime.fromObject(
167 + Object.assign(parsed, opts, {
168 + zone: interpretationZone,
169 + // setZone is a valid option in the calling methods, but not in fromObject
170 + setZone: undefined
171 + })
172 + );
173 + return setZone ? inst : inst.setZone(zone);
174 + } else {
175 + return DateTime.invalid(
176 + new Invalid("unparsable", `the input "${text}" can't be parsed as ${format}`)
177 + );
178 + }
179 +}
180 +
181 +// if you want to output a technical format (e.g. RFC 2822), this helper
182 +// helps handle the details
183 +function toTechFormat(dt, format, allowZ = true) {
184 + return dt.isValid
185 + ? Formatter.create(Locale.create("en-US"), {
186 + allowZ,
187 + forceSimple: true
188 + }).formatDateTimeFromString(dt, format)
189 + : null;
190 +}
191 +
192 +// technical time formats (e.g. the time part of ISO 8601), take some options
193 +// and this commonizes their handling
194 +function toTechTimeFormat(
195 + dt,
196 + {
197 + suppressSeconds = false,
198 + suppressMilliseconds = false,
199 + includeOffset,
200 + includePrefix = false,
201 + includeZone = false,
202 + spaceZone = false,
203 + format = "extended"
204 + }
205 +) {
206 + let fmt = format === "basic" ? "HHmm" : "HH:mm";
207 +
208 + if (!suppressSeconds || dt.second !== 0 || dt.millisecond !== 0) {
209 + fmt += format === "basic" ? "ss" : ":ss";
210 + if (!suppressMilliseconds || dt.millisecond !== 0) {
211 + fmt += ".SSS";
212 + }
213 + }
214 +
215 + if ((includeZone || includeOffset) && spaceZone) {
216 + fmt += " ";
217 + }
218 +
219 + if (includeZone) {
220 + fmt += "z";
221 + } else if (includeOffset) {
222 + fmt += format === "basic" ? "ZZZ" : "ZZ";
223 + }
224 +
225 + let str = toTechFormat(dt, fmt);
226 +
227 + if (includePrefix) {
228 + str = "T" + str;
229 + }
230 +
231 + return str;
232 +}
233 +
234 +// defaults for unspecified units in the supported calendars
235 +const defaultUnitValues = {
236 + month: 1,
237 + day: 1,
238 + hour: 0,
239 + minute: 0,
240 + second: 0,
241 + millisecond: 0
242 + },
243 + defaultWeekUnitValues = {
244 + weekNumber: 1,
245 + weekday: 1,
246 + hour: 0,
247 + minute: 0,
248 + second: 0,
249 + millisecond: 0
250 + },
251 + defaultOrdinalUnitValues = {
252 + ordinal: 1,
253 + hour: 0,
254 + minute: 0,
255 + second: 0,
256 + millisecond: 0
257 + };
258 +
259 +// Units in the supported calendars, sorted by bigness
260 +const orderedUnits = ["year", "month", "day", "hour", "minute", "second", "millisecond"],
261 + orderedWeekUnits = [
262 + "weekYear",
263 + "weekNumber",
264 + "weekday",
265 + "hour",
266 + "minute",
267 + "second",
268 + "millisecond"
269 + ],
270 + orderedOrdinalUnits = ["year", "ordinal", "hour", "minute", "second", "millisecond"];
271 +
272 +// standardize case and plurality in units
273 +function normalizeUnit(unit) {
274 + const normalized = {
275 + year: "year",
276 + years: "year",
277 + month: "month",
278 + months: "month",
279 + day: "day",
280 + days: "day",
281 + hour: "hour",
282 + hours: "hour",
283 + minute: "minute",
284 + minutes: "minute",
285 + quarter: "quarter",
286 + quarters: "quarter",
287 + second: "second",
288 + seconds: "second",
289 + millisecond: "millisecond",
290 + milliseconds: "millisecond",
291 + weekday: "weekday",
292 + weekdays: "weekday",
293 + weeknumber: "weekNumber",
294 + weeksnumber: "weekNumber",
295 + weeknumbers: "weekNumber",
296 + weekyear: "weekYear",
297 + weekyears: "weekYear",
298 + ordinal: "ordinal"
299 + }[unit.toLowerCase()];
300 +
301 + if (!normalized) throw new InvalidUnitError(unit);
302 +
303 + return normalized;
304 +}
305 +
306 +// this is a dumbed down version of fromObject() that runs about 60% faster
307 +// but doesn't do any validation, makes a bunch of assumptions about what units
308 +// are present, and so on.
309 +function quickDT(obj, zone) {
310 + // assume we have the higher-order units
311 + for (const u of orderedUnits) {
312 + if (isUndefined(obj[u])) {
313 + obj[u] = defaultUnitValues[u];
314 + }
315 + }
316 +
317 + const invalid = hasInvalidGregorianData(obj) || hasInvalidTimeData(obj);
318 + if (invalid) {
319 + return DateTime.invalid(invalid);
320 + }
321 +
322 + const tsNow = Settings.now(),
323 + offsetProvis = zone.offset(tsNow),
324 + [ts, o] = objToTS(obj, offsetProvis, zone);
325 +
326 + return new DateTime({
327 + ts,
328 + zone,
329 + o
330 + });
331 +}
332 +
333 +function diffRelative(start, end, opts) {
334 + const round = isUndefined(opts.round) ? true : opts.round,
335 + format = (c, unit) => {
336 + c = roundTo(c, round || opts.calendary ? 0 : 2, true);
337 + const formatter = end.loc.clone(opts).relFormatter(opts);
338 + return formatter.format(c, unit);
339 + },
340 + differ = unit => {
341 + if (opts.calendary) {
342 + if (!end.hasSame(start, unit)) {
343 + return end
344 + .startOf(unit)
345 + .diff(start.startOf(unit), unit)
346 + .get(unit);
347 + } else return 0;
348 + } else {
349 + return end.diff(start, unit).get(unit);
350 + }
351 + };
352 +
353 + if (opts.unit) {
354 + return format(differ(opts.unit), opts.unit);
355 + }
356 +
357 + for (const unit of opts.units) {
358 + const count = differ(unit);
359 + if (Math.abs(count) >= 1) {
360 + return format(count, unit);
361 + }
362 + }
363 + return format(start > end ? -0 : 0, opts.units[opts.units.length - 1]);
364 +}
365 +
366 +/**
367 + * A DateTime is an immutable data structure representing a specific date and time and accompanying methods. It contains class and instance methods for creating, parsing, interrogating, transforming, and formatting them.
368 + *
369 + * A DateTime comprises of:
370 + * * A timestamp. Each DateTime instance refers to a specific millisecond of the Unix epoch.
371 + * * A time zone. Each instance is considered in the context of a specific zone (by default the local system's zone).
372 + * * Configuration properties that effect how output strings are formatted, such as `locale`, `numberingSystem`, and `outputCalendar`.
373 + *
374 + * Here is a brief overview of the most commonly used functionality it provides:
375 + *
376 + * * **Creation**: To create a DateTime from its components, use one of its factory class methods: {@link local}, {@link utc}, and (most flexibly) {@link fromObject}. To create one from a standard string format, use {@link fromISO}, {@link fromHTTP}, and {@link fromRFC2822}. To create one from a custom string format, use {@link fromFormat}. To create one from a native JS date, use {@link fromJSDate}.
377 + * * **Gregorian calendar and time**: To examine the Gregorian properties of a DateTime individually (i.e as opposed to collectively through {@link toObject}), use the {@link year}, {@link month},
378 + * {@link day}, {@link hour}, {@link minute}, {@link second}, {@link millisecond} accessors.
379 + * * **Week calendar**: For ISO week calendar attributes, see the {@link weekYear}, {@link weekNumber}, and {@link weekday} accessors.
380 + * * **Configuration** See the {@link locale} and {@link numberingSystem} accessors.
381 + * * **Transformation**: To transform the DateTime into other DateTimes, use {@link set}, {@link reconfigure}, {@link setZone}, {@link setLocale}, {@link plus}, {@link minus}, {@link endOf}, {@link startOf}, {@link toUTC}, and {@link toLocal}.
382 + * * **Output**: To convert the DateTime to other representations, use the {@link toRelative}, {@link toRelativeCalendar}, {@link toJSON}, {@link toISO}, {@link toHTTP}, {@link toObject}, {@link toRFC2822}, {@link toString}, {@link toLocaleString}, {@link toFormat}, {@link toMillis} and {@link toJSDate}.
383 + *
384 + * There's plenty others documented below. In addition, for more information on subtler topics like internationalization, time zones, alternative calendars, validity, and so on, see the external documentation.
385 + */
386 +export default class DateTime {
387 + /**
388 + * @access private
389 + */
390 + constructor(config) {
391 + const zone = config.zone || Settings.defaultZone;
392 +
393 + let invalid =
394 + config.invalid ||
395 + (Number.isNaN(config.ts) ? new Invalid("invalid input") : null) ||
396 + (!zone.isValid ? unsupportedZone(zone) : null);
397 + /**
398 + * @access private
399 + */
400 + this.ts = isUndefined(config.ts) ? Settings.now() : config.ts;
401 +
402 + let c = null,
403 + o = null;
404 + if (!invalid) {
405 + const unchanged = config.old && config.old.ts === this.ts && config.old.zone.equals(zone);
406 +
407 + if (unchanged) {
408 + [c, o] = [config.old.c, config.old.o];
409 + } else {
410 + const ot = zone.offset(this.ts);
411 + c = tsToObj(this.ts, ot);
412 + invalid = Number.isNaN(c.year) ? new Invalid("invalid input") : null;
413 + c = invalid ? null : c;
414 + o = invalid ? null : ot;
415 + }
416 + }
417 +
418 + /**
419 + * @access private
420 + */
421 + this._zone = zone;
422 + /**
423 + * @access private
424 + */
425 + this.loc = config.loc || Locale.create();
426 + /**
427 + * @access private
428 + */
429 + this.invalid = invalid;
430 + /**
431 + * @access private
432 + */
433 + this.weekData = null;
434 + /**
435 + * @access private
436 + */
437 + this.c = c;
438 + /**
439 + * @access private
440 + */
441 + this.o = o;
442 + /**
443 + * @access private
444 + */
445 + this.isLuxonDateTime = true;
446 + }
447 +
448 + // CONSTRUCT
449 +
450 + /**
451 + * Create a DateTime for the current instant, in the system's time zone.
452 + *
453 + * Use Settings to override these default values if needed.
454 + * @example DateTime.now().toISO() //~> now in the ISO format
455 + * @return {DateTime}
456 + */
457 + static now() {
458 + return new DateTime({});
459 + }
460 +
461 + /**
462 + * Create a local DateTime
463 + * @param {number} [year] - The calendar year. If omitted (as in, call `local()` with no arguments), the current time will be used
464 + * @param {number} [month=1] - The month, 1-indexed
465 + * @param {number} [day=1] - The day of the month, 1-indexed
466 + * @param {number} [hour=0] - The hour of the day, in 24-hour time
467 + * @param {number} [minute=0] - The minute of the hour, meaning a number between 0 and 59
468 + * @param {number} [second=0] - The second of the minute, meaning a number between 0 and 59
469 + * @param {number} [millisecond=0] - The millisecond of the second, meaning a number between 0 and 999
470 + * @example DateTime.local() //~> now
471 + * @example DateTime.local(2017) //~> 2017-01-01T00:00:00
472 + * @example DateTime.local(2017, 3) //~> 2017-03-01T00:00:00
473 + * @example DateTime.local(2017, 3, 12) //~> 2017-03-12T00:00:00
474 + * @example DateTime.local(2017, 3, 12, 5) //~> 2017-03-12T05:00:00
475 + * @example DateTime.local(2017, 3, 12, 5, 45) //~> 2017-03-12T05:45:00
476 + * @example DateTime.local(2017, 3, 12, 5, 45, 10) //~> 2017-03-12T05:45:10
477 + * @example DateTime.local(2017, 3, 12, 5, 45, 10, 765) //~> 2017-03-12T05:45:10.765
478 + * @return {DateTime}
479 + */
480 + static local(year, month, day, hour, minute, second, millisecond) {
481 + if (isUndefined(year)) {
482 + return DateTime.now();
483 + } else {
484 + return quickDT(
485 + {
486 + year,
487 + month,
488 + day,
489 + hour,
490 + minute,
491 + second,
492 + millisecond
493 + },
494 + Settings.defaultZone
495 + );
496 + }
497 + }
498 +
499 + /**
500 + * Create a DateTime in UTC
501 + * @param {number} [year] - The calendar year. If omitted (as in, call `utc()` with no arguments), the current time will be used
502 + * @param {number} [month=1] - The month, 1-indexed
503 + * @param {number} [day=1] - The day of the month
504 + * @param {number} [hour=0] - The hour of the day, in 24-hour time
505 + * @param {number} [minute=0] - The minute of the hour, meaning a number between 0 and 59
506 + * @param {number} [second=0] - The second of the minute, meaning a number between 0 and 59
507 + * @param {number} [millisecond=0] - The millisecond of the second, meaning a number between 0 and 999
508 + * @example DateTime.utc() //~> now
509 + * @example DateTime.utc(2017) //~> 2017-01-01T00:00:00Z
510 + * @example DateTime.utc(2017, 3) //~> 2017-03-01T00:00:00Z
511 + * @example DateTime.utc(2017, 3, 12) //~> 2017-03-12T00:00:00Z
512 + * @example DateTime.utc(2017, 3, 12, 5) //~> 2017-03-12T05:00:00Z
513 + * @example DateTime.utc(2017, 3, 12, 5, 45) //~> 2017-03-12T05:45:00Z
514 + * @example DateTime.utc(2017, 3, 12, 5, 45, 10) //~> 2017-03-12T05:45:10Z
515 + * @example DateTime.utc(2017, 3, 12, 5, 45, 10, 765) //~> 2017-03-12T05:45:10.765Z
516 + * @return {DateTime}
517 + */
518 + static utc(year, month, day, hour, minute, second, millisecond) {
519 + if (isUndefined(year)) {
520 + return new DateTime({
521 + ts: Settings.now(),
522 + zone: FixedOffsetZone.utcInstance
523 + });
524 + } else {
525 + return quickDT(
526 + {
527 + year,
528 + month,
529 + day,
530 + hour,
531 + minute,
532 + second,
533 + millisecond
534 + },
535 + FixedOffsetZone.utcInstance
536 + );
537 + }
538 + }
539 +
540 + /**
541 + * Create a DateTime from a JavaScript Date object. Uses the default zone.
542 + * @param {Date} date - a JavaScript Date object
543 + * @param {Object} options - configuration options for the DateTime
544 + * @param {string|Zone} [options.zone='local'] - the zone to place the DateTime into
545 + * @return {DateTime}
546 + */
547 + static fromJSDate(date, options = {}) {
548 + const ts = isDate(date) ? date.valueOf() : NaN;
549 + if (Number.isNaN(ts)) {
550 + return DateTime.invalid("invalid input");
551 + }
552 +
553 + const zoneToUse = normalizeZone(options.zone, Settings.defaultZone);
554 + if (!zoneToUse.isValid) {
555 + return DateTime.invalid(unsupportedZone(zoneToUse));
556 + }
557 +
558 + return new DateTime({
559 + ts: ts,
560 + zone: zoneToUse,
561 + loc: Locale.fromObject(options)
562 + });
563 + }
564 +
565 + /**
566 + * Create a DateTime from a number of milliseconds since the epoch (meaning since 1 January 1970 00:00:00 UTC). Uses the default zone.
567 + * @param {number} milliseconds - a number of milliseconds since 1970 UTC
568 + * @param {Object} options - configuration options for the DateTime
569 + * @param {string|Zone} [options.zone='local'] - the zone to place the DateTime into
570 + * @param {string} [options.locale] - a locale to set on the resulting DateTime instance
571 + * @param {string} options.outputCalendar - the output calendar to set on the resulting DateTime instance
572 + * @param {string} options.numberingSystem - the numbering system to set on the resulting DateTime instance
573 + * @return {DateTime}
574 + */
575 + static fromMillis(milliseconds, options = {}) {
576 + if (!isNumber(milliseconds)) {
577 + throw new InvalidArgumentError(
578 + `fromMillis requires a numerical input, but received a ${typeof milliseconds} with value ${milliseconds}`
579 + );
580 + } else if (milliseconds < -MAX_DATE || milliseconds > MAX_DATE) {
581 + // this isn't perfect because because we can still end up out of range because of additional shifting, but it's a start
582 + return DateTime.invalid("Timestamp out of range");
583 + } else {
584 + return new DateTime({
585 + ts: milliseconds,
586 + zone: normalizeZone(options.zone, Settings.defaultZone),
587 + loc: Locale.fromObject(options)
588 + });
589 + }
590 + }
591 +
592 + /**
593 + * Create a DateTime from a number of seconds since the epoch (meaning since 1 January 1970 00:00:00 UTC). Uses the default zone.
594 + * @param {number} seconds - a number of seconds since 1970 UTC
595 + * @param {Object} options - configuration options for the DateTime
596 + * @param {string|Zone} [options.zone='local'] - the zone to place the DateTime into
597 + * @param {string} [options.locale] - a locale to set on the resulting DateTime instance
598 + * @param {string} options.outputCalendar - the output calendar to set on the resulting DateTime instance
599 + * @param {string} options.numberingSystem - the numbering system to set on the resulting DateTime instance
600 + * @return {DateTime}
601 + */
602 + static fromSeconds(seconds, options = {}) {
603 + if (!isNumber(seconds)) {
604 + throw new InvalidArgumentError("fromSeconds requires a numerical input");
605 + } else {
606 + return new DateTime({
607 + ts: seconds * 1000,
608 + zone: normalizeZone(options.zone, Settings.defaultZone),
609 + loc: Locale.fromObject(options)
610 + });
611 + }
612 + }
613 +
614 + /**
615 + * Create a DateTime from a JavaScript object with keys like 'year' and 'hour' with reasonable defaults.
616 + * @param {Object} obj - the object to create the DateTime from
617 + * @param {number} obj.year - a year, such as 1987
618 + * @param {number} obj.month - a month, 1-12
619 + * @param {number} obj.day - a day of the month, 1-31, depending on the month
620 + * @param {number} obj.ordinal - day of the year, 1-365 or 366
621 + * @param {number} obj.weekYear - an ISO week year
622 + * @param {number} obj.weekNumber - an ISO week number, between 1 and 52 or 53, depending on the year
623 + * @param {number} obj.weekday - an ISO weekday, 1-7, where 1 is Monday and 7 is Sunday
624 + * @param {number} obj.hour - hour of the day, 0-23
625 + * @param {number} obj.minute - minute of the hour, 0-59
626 + * @param {number} obj.second - second of the minute, 0-59
627 + * @param {number} obj.millisecond - millisecond of the second, 0-999
628 + * @param {string|Zone} [obj.zone='local'] - interpret the numbers in the context of a particular zone. Can take any value taken as the first argument to setZone()
629 + * @param {string} [obj.locale='system's locale'] - a locale to set on the resulting DateTime instance
630 + * @param {string} obj.outputCalendar - the output calendar to set on the resulting DateTime instance
631 + * @param {string} obj.numberingSystem - the numbering system to set on the resulting DateTime instance
632 + * @example DateTime.fromObject({ year: 1982, month: 5, day: 25}).toISODate() //=> '1982-05-25'
633 + * @example DateTime.fromObject({ year: 1982 }).toISODate() //=> '1982-01-01'
634 + * @example DateTime.fromObject({ hour: 10, minute: 26, second: 6 }) //~> today at 10:26:06
635 + * @example DateTime.fromObject({ hour: 10, minute: 26, second: 6, zone: 'utc' }),
636 + * @example DateTime.fromObject({ hour: 10, minute: 26, second: 6, zone: 'local' })
637 + * @example DateTime.fromObject({ hour: 10, minute: 26, second: 6, zone: 'America/New_York' })
638 + * @example DateTime.fromObject({ weekYear: 2016, weekNumber: 2, weekday: 3 }).toISODate() //=> '2016-01-13'
639 + * @return {DateTime}
640 + */
641 + static fromObject(obj) {
642 + const zoneToUse = normalizeZone(obj.zone, Settings.defaultZone);
643 + if (!zoneToUse.isValid) {
644 + return DateTime.invalid(unsupportedZone(zoneToUse));
645 + }
646 +
647 + const tsNow = Settings.now(),
648 + offsetProvis = zoneToUse.offset(tsNow),
649 + normalized = normalizeObject(obj, normalizeUnit, [
650 + "zone",
651 + "locale",
652 + "outputCalendar",
653 + "numberingSystem"
654 + ]),
655 + containsOrdinal = !isUndefined(normalized.ordinal),
656 + containsGregorYear = !isUndefined(normalized.year),
657 + containsGregorMD = !isUndefined(normalized.month) || !isUndefined(normalized.day),
658 + containsGregor = containsGregorYear || containsGregorMD,
659 + definiteWeekDef = normalized.weekYear || normalized.weekNumber,
660 + loc = Locale.fromObject(obj);
661 +
662 + // cases:
663 + // just a weekday -> this week's instance of that weekday, no worries
664 + // (gregorian data or ordinal) + (weekYear or weekNumber) -> error
665 + // (gregorian month or day) + ordinal -> error
666 + // otherwise just use weeks or ordinals or gregorian, depending on what's specified
667 +
668 + if ((containsGregor || containsOrdinal) && definiteWeekDef) {
669 + throw new ConflictingSpecificationError(
670 + "Can't mix weekYear/weekNumber units with year/month/day or ordinals"
671 + );
672 + }
673 +
674 + if (containsGregorMD && containsOrdinal) {
675 + throw new ConflictingSpecificationError("Can't mix ordinal dates with month/day");
676 + }
677 +
678 + const useWeekData = definiteWeekDef || (normalized.weekday && !containsGregor);
679 +
680 + // configure ourselves to deal with gregorian dates or week stuff
681 + let units,
682 + defaultValues,
683 + objNow = tsToObj(tsNow, offsetProvis);
684 + if (useWeekData) {
685 + units = orderedWeekUnits;
686 + defaultValues = defaultWeekUnitValues;
687 + objNow = gregorianToWeek(objNow);
688 + } else if (containsOrdinal) {
689 + units = orderedOrdinalUnits;
690 + defaultValues = defaultOrdinalUnitValues;
691 + objNow = gregorianToOrdinal(objNow);
692 + } else {
693 + units = orderedUnits;
694 + defaultValues = defaultUnitValues;
695 + }
696 +
697 + // set default values for missing stuff
698 + let foundFirst = false;
699 + for (const u of units) {
700 + const v = normalized[u];
701 + if (!isUndefined(v)) {
702 + foundFirst = true;
703 + } else if (foundFirst) {
704 + normalized[u] = defaultValues[u];
705 + } else {
706 + normalized[u] = objNow[u];
707 + }
708 + }
709 +
710 + // make sure the values we have are in range
711 + const higherOrderInvalid = useWeekData
712 + ? hasInvalidWeekData(normalized)
713 + : containsOrdinal
714 + ? hasInvalidOrdinalData(normalized)
715 + : hasInvalidGregorianData(normalized),
716 + invalid = higherOrderInvalid || hasInvalidTimeData(normalized);
717 +
718 + if (invalid) {
719 + return DateTime.invalid(invalid);
720 + }
721 +
722 + // compute the actual time
723 + const gregorian = useWeekData
724 + ? weekToGregorian(normalized)
725 + : containsOrdinal
726 + ? ordinalToGregorian(normalized)
727 + : normalized,
728 + [tsFinal, offsetFinal] = objToTS(gregorian, offsetProvis, zoneToUse),
729 + inst = new DateTime({
730 + ts: tsFinal,
731 + zone: zoneToUse,
732 + o: offsetFinal,
733 + loc
734 + });
735 +
736 + // gregorian data + weekday serves only to validate
737 + if (normalized.weekday && containsGregor && obj.weekday !== inst.weekday) {
738 + return DateTime.invalid(
739 + "mismatched weekday",
740 + `you can't specify both a weekday of ${normalized.weekday} and a date of ${inst.toISO()}`
741 + );
742 + }
743 +
744 + return inst;
745 + }
746 +
747 + /**
748 + * Create a DateTime from an ISO 8601 string
749 + * @param {string} text - the ISO string
750 + * @param {Object} opts - options to affect the creation
751 + * @param {string|Zone} [opts.zone='local'] - use this zone if no offset is specified in the input string itself. Will also convert the time to this zone
752 + * @param {boolean} [opts.setZone=false] - override the zone with a fixed-offset zone specified in the string itself, if it specifies one
753 + * @param {string} [opts.locale='system's locale'] - a locale to set on the resulting DateTime instance
754 + * @param {string} [opts.outputCalendar] - the output calendar to set on the resulting DateTime instance
755 + * @param {string} [opts.numberingSystem] - the numbering system to set on the resulting DateTime instance
756 + * @example DateTime.fromISO('2016-05-25T09:08:34.123')
757 + * @example DateTime.fromISO('2016-05-25T09:08:34.123+06:00')
758 + * @example DateTime.fromISO('2016-05-25T09:08:34.123+06:00', {setZone: true})
759 + * @example DateTime.fromISO('2016-05-25T09:08:34.123', {zone: 'utc'})
760 + * @example DateTime.fromISO('2016-W05-4')
761 + * @return {DateTime}
762 + */
763 + static fromISO(text, opts = {}) {
764 + const [vals, parsedZone] = parseISODate(text);
765 + return parseDataToDateTime(vals, parsedZone, opts, "ISO 8601", text);
766 + }
767 +
768 + /**
769 + * Create a DateTime from an RFC 2822 string
770 + * @param {string} text - the RFC 2822 string
771 + * @param {Object} opts - options to affect the creation
772 + * @param {string|Zone} [opts.zone='local'] - convert the time to this zone. Since the offset is always specified in the string itself, this has no effect on the interpretation of string, merely the zone the resulting DateTime is expressed in.
773 + * @param {boolean} [opts.setZone=false] - override the zone with a fixed-offset zone specified in the string itself, if it specifies one
774 + * @param {string} [opts.locale='system's locale'] - a locale to set on the resulting DateTime instance
775 + * @param {string} opts.outputCalendar - the output calendar to set on the resulting DateTime instance
776 + * @param {string} opts.numberingSystem - the numbering system to set on the resulting DateTime instance
777 + * @example DateTime.fromRFC2822('25 Nov 2016 13:23:12 GMT')
778 + * @example DateTime.fromRFC2822('Fri, 25 Nov 2016 13:23:12 +0600')
779 + * @example DateTime.fromRFC2822('25 Nov 2016 13:23 Z')
780 + * @return {DateTime}
781 + */
782 + static fromRFC2822(text, opts = {}) {
783 + const [vals, parsedZone] = parseRFC2822Date(text);
784 + return parseDataToDateTime(vals, parsedZone, opts, "RFC 2822", text);
785 + }
786 +
787 + /**
788 + * Create a DateTime from an HTTP header date
789 + * @see https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1
790 + * @param {string} text - the HTTP header date
791 + * @param {Object} opts - options to affect the creation
792 + * @param {string|Zone} [opts.zone='local'] - convert the time to this zone. Since HTTP dates are always in UTC, this has no effect on the interpretation of string, merely the zone the resulting DateTime is expressed in.
793 + * @param {boolean} [opts.setZone=false] - override the zone with the fixed-offset zone specified in the string. For HTTP dates, this is always UTC, so this option is equivalent to setting the `zone` option to 'utc', but this option is included for consistency with similar methods.
794 + * @param {string} [opts.locale='system's locale'] - a locale to set on the resulting DateTime instance
795 + * @param {string} opts.outputCalendar - the output calendar to set on the resulting DateTime instance
796 + * @param {string} opts.numberingSystem - the numbering system to set on the resulting DateTime instance
797 + * @example DateTime.fromHTTP('Sun, 06 Nov 1994 08:49:37 GMT')
798 + * @example DateTime.fromHTTP('Sunday, 06-Nov-94 08:49:37 GMT')
799 + * @example DateTime.fromHTTP('Sun Nov 6 08:49:37 1994')
800 + * @return {DateTime}
801 + */
802 + static fromHTTP(text, opts = {}) {
803 + const [vals, parsedZone] = parseHTTPDate(text);
804 + return parseDataToDateTime(vals, parsedZone, opts, "HTTP", opts);
805 + }
806 +
807 + /**
808 + * Create a DateTime from an input string and format string.
809 + * Defaults to en-US if no locale has been specified, regardless of the system's locale.
810 + * @see https://moment.github.io/luxon/docs/manual/parsing.html#table-of-tokens
811 + * @param {string} text - the string to parse
812 + * @param {string} fmt - the format the string is expected to be in (see the link below for the formats)
813 + * @param {Object} opts - options to affect the creation
814 + * @param {string|Zone} [opts.zone='local'] - use this zone if no offset is specified in the input string itself. Will also convert the DateTime to this zone
815 + * @param {boolean} [opts.setZone=false] - override the zone with a zone specified in the string itself, if it specifies one
816 + * @param {string} [opts.locale='en-US'] - a locale string to use when parsing. Will also set the DateTime to this locale
817 + * @param {string} opts.numberingSystem - the numbering system to use when parsing. Will also set the resulting DateTime to this numbering system
818 + * @param {string} opts.outputCalendar - the output calendar to set on the resulting DateTime instance
819 + * @return {DateTime}
820 + */
821 + static fromFormat(text, fmt, opts = {}) {
822 + if (isUndefined(text) || isUndefined(fmt)) {
823 + throw new InvalidArgumentError("fromFormat requires an input string and a format");
824 + }
825 +
826 + const { locale = null, numberingSystem = null } = opts,
827 + localeToUse = Locale.fromOpts({
828 + locale,
829 + numberingSystem,
830 + defaultToEN: true
831 + }),
832 + [vals, parsedZone, invalid] = parseFromTokens(localeToUse, text, fmt);
833 + if (invalid) {
834 + return DateTime.invalid(invalid);
835 + } else {
836 + return parseDataToDateTime(vals, parsedZone, opts, `format ${fmt}`, text);
837 + }
838 + }
839 +
840 + /**
841 + * @deprecated use fromFormat instead
842 + */
843 + static fromString(text, fmt, opts = {}) {
844 + return DateTime.fromFormat(text, fmt, opts);
845 + }
846 +
847 + /**
848 + * Create a DateTime from a SQL date, time, or datetime
849 + * Defaults to en-US if no locale has been specified, regardless of the system's locale
850 + * @param {string} text - the string to parse
851 + * @param {Object} opts - options to affect the creation
852 + * @param {string|Zone} [opts.zone='local'] - use this zone if no offset is specified in the input string itself. Will also convert the DateTime to this zone
853 + * @param {boolean} [opts.setZone=false] - override the zone with a zone specified in the string itself, if it specifies one
854 + * @param {string} [opts.locale='en-US'] - a locale string to use when parsing. Will also set the DateTime to this locale
855 + * @param {string} opts.numberingSystem - the numbering system to use when parsing. Will also set the resulting DateTime to this numbering system
856 + * @param {string} opts.outputCalendar - the output calendar to set on the resulting DateTime instance
857 + * @example DateTime.fromSQL('2017-05-15')
858 + * @example DateTime.fromSQL('2017-05-15 09:12:34')
859 + * @example DateTime.fromSQL('2017-05-15 09:12:34.342')
860 + * @example DateTime.fromSQL('2017-05-15 09:12:34.342+06:00')
861 + * @example DateTime.fromSQL('2017-05-15 09:12:34.342 America/Los_Angeles')
862 + * @example DateTime.fromSQL('2017-05-15 09:12:34.342 America/Los_Angeles', { setZone: true })
863 + * @example DateTime.fromSQL('2017-05-15 09:12:34.342', { zone: 'America/Los_Angeles' })
864 + * @example DateTime.fromSQL('09:12:34.342')
865 + * @return {DateTime}
866 + */
867 + static fromSQL(text, opts = {}) {
868 + const [vals, parsedZone] = parseSQL(text);
869 + return parseDataToDateTime(vals, parsedZone, opts, "SQL", text);
870 + }
871 +
872 + /**
873 + * Create an invalid DateTime.
874 + * @param {string} reason - simple string of why this DateTime is invalid. Should not contain parameters or anything else data-dependent
875 + * @param {string} [explanation=null] - longer explanation, may include parameters and other useful debugging information
876 + * @return {DateTime}
877 + */
878 + static invalid(reason, explanation = null) {
879 + if (!reason) {
880 + throw new InvalidArgumentError("need to specify a reason the DateTime is invalid");
881 + }
882 +
883 + const invalid = reason instanceof Invalid ? reason : new Invalid(reason, explanation);
884 +
885 + if (Settings.throwOnInvalid) {
886 + throw new InvalidDateTimeError(invalid);
887 + } else {
888 + return new DateTime({ invalid });
889 + }
890 + }
891 +
892 + /**
893 + * Check if an object is a DateTime. Works across context boundaries
894 + * @param {object} o
895 + * @return {boolean}
896 + */
897 + static isDateTime(o) {
898 + return (o && o.isLuxonDateTime) || false;
899 + }
900 +
901 + // INFO
902 +
903 + /**
904 + * Get the value of unit.
905 + * @param {string} unit - a unit such as 'minute' or 'day'
906 + * @example DateTime.local(2017, 7, 4).get('month'); //=> 7
907 + * @example DateTime.local(2017, 7, 4).get('day'); //=> 4
908 + * @return {number}
909 + */
910 + get(unit) {
911 + return this[unit];
912 + }
913 +
914 + /**
915 + * Returns whether the DateTime is valid. Invalid DateTimes occur when:
916 + * * The DateTime was created from invalid calendar information, such as the 13th month or February 30
917 + * * The DateTime was created by an operation on another invalid date
918 + * @type {boolean}
919 + */
920 + get isValid() {
921 + return this.invalid === null;
922 + }
923 +
924 + /**
925 + * Returns an error code if this DateTime is invalid, or null if the DateTime is valid
926 + * @type {string}
927 + */
928 + get invalidReason() {
929 + return this.invalid ? this.invalid.reason : null;
930 + }
931 +
932 + /**
933 + * Returns an explanation of why this DateTime became invalid, or null if the DateTime is valid
934 + * @type {string}
935 + */
936 + get invalidExplanation() {
937 + return this.invalid ? this.invalid.explanation : null;
938 + }
939 +
940 + /**
941 + * Get the locale of a DateTime, such 'en-GB'. The locale is used when formatting the DateTime
942 + *
943 + * @type {string}
944 + */
945 + get locale() {
946 + return this.isValid ? this.loc.locale : null;
947 + }
948 +
949 + /**
950 + * Get the numbering system of a DateTime, such 'beng'. The numbering system is used when formatting the DateTime
951 + *
952 + * @type {string}
953 + */
954 + get numberingSystem() {
955 + return this.isValid ? this.loc.numberingSystem : null;
956 + }
957 +
958 + /**
959 + * Get the output calendar of a DateTime, such 'islamic'. The output calendar is used when formatting the DateTime
960 + *
961 + * @type {string}
962 + */
963 + get outputCalendar() {
964 + return this.isValid ? this.loc.outputCalendar : null;
965 + }
966 +
967 + /**
968 + * Get the time zone associated with this DateTime.
969 + * @type {Zone}
970 + */
971 + get zone() {
972 + return this._zone;
973 + }
974 +
975 + /**
976 + * Get the name of the time zone.
977 + * @type {string}
978 + */
979 + get zoneName() {
980 + return this.isValid ? this.zone.name : null;
981 + }
982 +
983 + /**
984 + * Get the year
985 + * @example DateTime.local(2017, 5, 25).year //=> 2017
986 + * @type {number}
987 + */
988 + get year() {
989 + return this.isValid ? this.c.year : NaN;
990 + }
991 +
992 + /**
993 + * Get the quarter
994 + * @example DateTime.local(2017, 5, 25).quarter //=> 2
995 + * @type {number}
996 + */
997 + get quarter() {
998 + return this.isValid ? Math.ceil(this.c.month / 3) : NaN;
999 + }
1000 +
1001 + /**
1002 + * Get the month (1-12).
1003 + * @example DateTime.local(2017, 5, 25).month //=> 5
1004 + * @type {number}
1005 + */
1006 + get month() {
1007 + return this.isValid ? this.c.month : NaN;
1008 + }
1009 +
1010 + /**
1011 + * Get the day of the month (1-30ish).
1012 + * @example DateTime.local(2017, 5, 25).day //=> 25
1013 + * @type {number}
1014 + */
1015 + get day() {
1016 + return this.isValid ? this.c.day : NaN;
1017 + }
1018 +
1019 + /**
1020 + * Get the hour of the day (0-23).
1021 + * @example DateTime.local(2017, 5, 25, 9).hour //=> 9
1022 + * @type {number}
1023 + */
1024 + get hour() {
1025 + return this.isValid ? this.c.hour : NaN;
1026 + }
1027 +
1028 + /**
1029 + * Get the minute of the hour (0-59).
1030 + * @example DateTime.local(2017, 5, 25, 9, 30).minute //=> 30
1031 + * @type {number}
1032 + */
1033 + get minute() {
1034 + return this.isValid ? this.c.minute : NaN;
1035 + }
1036 +
1037 + /**
1038 + * Get the second of the minute (0-59).
1039 + * @example DateTime.local(2017, 5, 25, 9, 30, 52).second //=> 52
1040 + * @type {number}
1041 + */
1042 + get second() {
1043 + return this.isValid ? this.c.second : NaN;
1044 + }
1045 +
1046 + /**
1047 + * Get the millisecond of the second (0-999).
1048 + * @example DateTime.local(2017, 5, 25, 9, 30, 52, 654).millisecond //=> 654
1049 + * @type {number}
1050 + */
1051 + get millisecond() {
1052 + return this.isValid ? this.c.millisecond : NaN;
1053 + }
1054 +
1055 + /**
1056 + * Get the week year
1057 + * @see https://en.wikipedia.org/wiki/ISO_week_date
1058 + * @example DateTime.local(2014, 12, 31).weekYear //=> 2015
1059 + * @type {number}
1060 + */
1061 + get weekYear() {
1062 + return this.isValid ? possiblyCachedWeekData(this).weekYear : NaN;
1063 + }
1064 +
1065 + /**
1066 + * Get the week number of the week year (1-52ish).
1067 + * @see https://en.wikipedia.org/wiki/ISO_week_date
1068 + * @example DateTime.local(2017, 5, 25).weekNumber //=> 21
1069 + * @type {number}
1070 + */
1071 + get weekNumber() {
1072 + return this.isValid ? possiblyCachedWeekData(this).weekNumber : NaN;
1073 + }
1074 +
1075 + /**
1076 + * Get the day of the week.
1077 + * 1 is Monday and 7 is Sunday
1078 + * @see https://en.wikipedia.org/wiki/ISO_week_date
1079 + * @example DateTime.local(2014, 11, 31).weekday //=> 4
1080 + * @type {number}
1081 + */
1082 + get weekday() {
1083 + return this.isValid ? possiblyCachedWeekData(this).weekday : NaN;
1084 + }
1085 +
1086 + /**
1087 + * Get the ordinal (meaning the day of the year)
1088 + * @example DateTime.local(2017, 5, 25).ordinal //=> 145
1089 + * @type {number|DateTime}
1090 + */
1091 + get ordinal() {
1092 + return this.isValid ? gregorianToOrdinal(this.c).ordinal : NaN;
1093 + }
1094 +
1095 + /**
1096 + * Get the human readable short month name, such as 'Oct'.
1097 + * Defaults to the system's locale if no locale has been specified
1098 + * @example DateTime.local(2017, 10, 30).monthShort //=> Oct
1099 + * @type {string}
1100 + */
1101 + get monthShort() {
1102 + return this.isValid ? Info.months("short", { locObj: this.loc })[this.month - 1] : null;
1103 + }
1104 +
1105 + /**
1106 + * Get the human readable long month name, such as 'October'.
1107 + * Defaults to the system's locale if no locale has been specified
1108 + * @example DateTime.local(2017, 10, 30).monthLong //=> October
1109 + * @type {string}
1110 + */
1111 + get monthLong() {
1112 + return this.isValid ? Info.months("long", { locObj: this.loc })[this.month - 1] : null;
1113 + }
1114 +
1115 + /**
1116 + * Get the human readable short weekday, such as 'Mon'.
1117 + * Defaults to the system's locale if no locale has been specified
1118 + * @example DateTime.local(2017, 10, 30).weekdayShort //=> Mon
1119 + * @type {string}
1120 + */
1121 + get weekdayShort() {
1122 + return this.isValid ? Info.weekdays("short", { locObj: this.loc })[this.weekday - 1] : null;
1123 + }
1124 +
1125 + /**
1126 + * Get the human readable long weekday, such as 'Monday'.
1127 + * Defaults to the system's locale if no locale has been specified
1128 + * @example DateTime.local(2017, 10, 30).weekdayLong //=> Monday
1129 + * @type {string}
1130 + */
1131 + get weekdayLong() {
1132 + return this.isValid ? Info.weekdays("long", { locObj: this.loc })[this.weekday - 1] : null;
1133 + }
1134 +
1135 + /**
1136 + * Get the UTC offset of this DateTime in minutes
1137 + * @example DateTime.now().offset //=> -240
1138 + * @example DateTime.utc().offset //=> 0
1139 + * @type {number}
1140 + */
1141 + get offset() {
1142 + return this.isValid ? +this.o : NaN;
1143 + }
1144 +
1145 + /**
1146 + * Get the short human name for the zone's current offset, for example "EST" or "EDT".
1147 + * Defaults to the system's locale if no locale has been specified
1148 + * @type {string}
1149 + */
1150 + get offsetNameShort() {
1151 + if (this.isValid) {
1152 + return this.zone.offsetName(this.ts, {
1153 + format: "short",
1154 + locale: this.locale
1155 + });
1156 + } else {
1157 + return null;
1158 + }
1159 + }
1160 +
1161 + /**
1162 + * Get the long human name for the zone's current offset, for example "Eastern Standard Time" or "Eastern Daylight Time".
1163 + * Defaults to the system's locale if no locale has been specified
1164 + * @type {string}
1165 + */
1166 + get offsetNameLong() {
1167 + if (this.isValid) {
1168 + return this.zone.offsetName(this.ts, {
1169 + format: "long",
1170 + locale: this.locale
1171 + });
1172 + } else {
1173 + return null;
1174 + }
1175 + }
1176 +
1177 + /**
1178 + * Get whether this zone's offset ever changes, as in a DST.
1179 + * @type {boolean}
1180 + */
1181 + get isOffsetFixed() {
1182 + return this.isValid ? this.zone.universal : null;
1183 + }
1184 +
1185 + /**
1186 + * Get whether the DateTime is in a DST.
1187 + * @type {boolean}
1188 + */
1189 + get isInDST() {
1190 + if (this.isOffsetFixed) {
1191 + return false;
1192 + } else {
1193 + return (
1194 + this.offset > this.set({ month: 1 }).offset || this.offset > this.set({ month: 5 }).offset
1195 + );
1196 + }
1197 + }
1198 +
1199 + /**
1200 + * Returns true if this DateTime is in a leap year, false otherwise
1201 + * @example DateTime.local(2016).isInLeapYear //=> true
1202 + * @example DateTime.local(2013).isInLeapYear //=> false
1203 + * @type {boolean}
1204 + */
1205 + get isInLeapYear() {
1206 + return isLeapYear(this.year);
1207 + }
1208 +
1209 + /**
1210 + * Returns the number of days in this DateTime's month
1211 + * @example DateTime.local(2016, 2).daysInMonth //=> 29
1212 + * @example DateTime.local(2016, 3).daysInMonth //=> 31
1213 + * @type {number}
1214 + */
1215 + get daysInMonth() {
1216 + return daysInMonth(this.year, this.month);
1217 + }
1218 +
1219 + /**
1220 + * Returns the number of days in this DateTime's year
1221 + * @example DateTime.local(2016).daysInYear //=> 366
1222 + * @example DateTime.local(2013).daysInYear //=> 365
1223 + * @type {number}
1224 + */
1225 + get daysInYear() {
1226 + return this.isValid ? daysInYear(this.year) : NaN;
1227 + }
1228 +
1229 + /**
1230 + * Returns the number of weeks in this DateTime's year
1231 + * @see https://en.wikipedia.org/wiki/ISO_week_date
1232 + * @example DateTime.local(2004).weeksInWeekYear //=> 53
1233 + * @example DateTime.local(2013).weeksInWeekYear //=> 52
1234 + * @type {number}
1235 + */
1236 + get weeksInWeekYear() {
1237 + return this.isValid ? weeksInWeekYear(this.weekYear) : NaN;
1238 + }
1239 +
1240 + /**
1241 + * Returns the resolved Intl options for this DateTime.
1242 + * This is useful in understanding the behavior of formatting methods
1243 + * @param {Object} opts - the same options as toLocaleString
1244 + * @return {Object}
1245 + */
1246 + resolvedLocaleOpts(opts = {}) {
1247 + const { locale, numberingSystem, calendar } = Formatter.create(
1248 + this.loc.clone(opts),
1249 + opts
1250 + ).resolvedOptions(this);
1251 + return { locale, numberingSystem, outputCalendar: calendar };
1252 + }
1253 +
1254 + // TRANSFORM
1255 +
1256 + /**
1257 + * "Set" the DateTime's zone to UTC. Returns a newly-constructed DateTime.
1258 + *
1259 + * Equivalent to {@link setZone}('utc')
1260 + * @param {number} [offset=0] - optionally, an offset from UTC in minutes
1261 + * @param {Object} [opts={}] - options to pass to `setZone()`
1262 + * @return {DateTime}
1263 + */
1264 + toUTC(offset = 0, opts = {}) {
1265 + return this.setZone(FixedOffsetZone.instance(offset), opts);
1266 + }
1267 +
1268 + /**
1269 + * "Set" the DateTime's zone to the host's local zone. Returns a newly-constructed DateTime.
1270 + *
1271 + * Equivalent to `setZone('local')`
1272 + * @return {DateTime}
1273 + */
1274 + toLocal() {
1275 + return this.setZone(Settings.defaultZone);
1276 + }
1277 +
1278 + /**
1279 + * "Set" the DateTime's zone to specified zone. Returns a newly-constructed DateTime.
1280 + *
1281 + * By default, the setter keeps the underlying time the same (as in, the same timestamp), but the new instance will report different local times and consider DSTs when making computations, as with {@link plus}. You may wish to use {@link toLocal} and {@link toUTC} which provide simple convenience wrappers for commonly used zones.
1282 + * @param {string|Zone} [zone='local'] - a zone identifier. As a string, that can be any IANA zone supported by the host environment, or a fixed-offset name of the form 'UTC+3', or the strings 'local' or 'utc'. You may also supply an instance of a {@link Zone} class.
1283 + * @param {Object} opts - options
1284 + * @param {boolean} [opts.keepLocalTime=false] - If true, adjust the underlying time so that the local time stays the same, but in the target zone. You should rarely need this.
1285 + * @return {DateTime}
1286 + */
1287 + setZone(zone, { keepLocalTime = false, keepCalendarTime = false } = {}) {
1288 + zone = normalizeZone(zone, Settings.defaultZone);
1289 + if (zone.equals(this.zone)) {
1290 + return this;
1291 + } else if (!zone.isValid) {
1292 + return DateTime.invalid(unsupportedZone(zone));
1293 + } else {
1294 + let newTS = this.ts;
1295 + if (keepLocalTime || keepCalendarTime) {
1296 + const offsetGuess = zone.offset(this.ts);
1297 + const asObj = this.toObject();
1298 + [newTS] = objToTS(asObj, offsetGuess, zone);
1299 + }
1300 + return clone(this, { ts: newTS, zone });
1301 + }
1302 + }
1303 +
1304 + /**
1305 + * "Set" the locale, numberingSystem, or outputCalendar. Returns a newly-constructed DateTime.
1306 + * @param {Object} properties - the properties to set
1307 + * @example DateTime.local(2017, 5, 25).reconfigure({ locale: 'en-GB' })
1308 + * @return {DateTime}
1309 + */
1310 + reconfigure({ locale, numberingSystem, outputCalendar } = {}) {
1311 + const loc = this.loc.clone({ locale, numberingSystem, outputCalendar });
1312 + return clone(this, { loc });
1313 + }
1314 +
1315 + /**
1316 + * "Set" the locale. Returns a newly-constructed DateTime.
1317 + * Just a convenient alias for reconfigure({ locale })
1318 + * @example DateTime.local(2017, 5, 25).setLocale('en-GB')
1319 + * @return {DateTime}
1320 + */
1321 + setLocale(locale) {
1322 + return this.reconfigure({ locale });
1323 + }
1324 +
1325 + /**
1326 + * "Set" the values of specified units. Returns a newly-constructed DateTime.
1327 + * You can only set units with this method; for "setting" metadata, see {@link reconfigure} and {@link setZone}.
1328 + * @param {Object} values - a mapping of units to numbers
1329 + * @example dt.set({ year: 2017 })
1330 + * @example dt.set({ hour: 8, minute: 30 })
1331 + * @example dt.set({ weekday: 5 })
1332 + * @example dt.set({ year: 2005, ordinal: 234 })
1333 + * @return {DateTime}
1334 + */
1335 + set(values) {
1336 + if (!this.isValid) return this;
1337 +
1338 + const normalized = normalizeObject(values, normalizeUnit, []),
1339 + settingWeekStuff =
1340 + !isUndefined(normalized.weekYear) ||
1341 + !isUndefined(normalized.weekNumber) ||
1342 + !isUndefined(normalized.weekday),
1343 + containsOrdinal = !isUndefined(normalized.ordinal),
1344 + containsGregorYear = !isUndefined(normalized.year),
1345 + containsGregorMD = !isUndefined(normalized.month) || !isUndefined(normalized.day),
1346 + containsGregor = containsGregorYear || containsGregorMD,
1347 + definiteWeekDef = normalized.weekYear || normalized.weekNumber;
1348 +
1349 + if ((containsGregor || containsOrdinal) && definiteWeekDef) {
1350 + throw new ConflictingSpecificationError(
1351 + "Can't mix weekYear/weekNumber units with year/month/day or ordinals"
1352 + );
1353 + }
1354 +
1355 + if (containsGregorMD && containsOrdinal) {
1356 + throw new ConflictingSpecificationError("Can't mix ordinal dates with month/day");
1357 + }
1358 +
1359 + let mixed;
1360 + if (settingWeekStuff) {
1361 + mixed = weekToGregorian(Object.assign(gregorianToWeek(this.c), normalized));
1362 + } else if (!isUndefined(normalized.ordinal)) {
1363 + mixed = ordinalToGregorian(Object.assign(gregorianToOrdinal(this.c), normalized));
1364 + } else {
1365 + mixed = Object.assign(this.toObject(), normalized);
1366 +
1367 + // if we didn't set the day but we ended up on an overflow date,
1368 + // use the last day of the right month
1369 + if (isUndefined(normalized.day)) {
1370 + mixed.day = Math.min(daysInMonth(mixed.year, mixed.month), mixed.day);
1371 + }
1372 + }
1373 +
1374 + const [ts, o] = objToTS(mixed, this.o, this.zone);
1375 + return clone(this, { ts, o });
1376 + }
1377 +
1378 + /**
1379 + * Add a period of time to this DateTime and return the resulting DateTime
1380 + *
1381 + * Adding hours, minutes, seconds, or milliseconds increases the timestamp by the right number of milliseconds. Adding days, months, or years shifts the calendar, accounting for DSTs and leap years along the way. Thus, `dt.plus({ hours: 24 })` may result in a different time than `dt.plus({ days: 1 })` if there's a DST shift in between.
1382 + * @param {Duration|Object|number} duration - The amount to add. Either a Luxon Duration, a number of milliseconds, the object argument to Duration.fromObject()
1383 + * @example DateTime.now().plus(123) //~> in 123 milliseconds
1384 + * @example DateTime.now().plus({ minutes: 15 }) //~> in 15 minutes
1385 + * @example DateTime.now().plus({ days: 1 }) //~> this time tomorrow
1386 + * @example DateTime.now().plus({ days: -1 }) //~> this time yesterday
1387 + * @example DateTime.now().plus({ hours: 3, minutes: 13 }) //~> in 3 hr, 13 min
1388 + * @example DateTime.now().plus(Duration.fromObject({ hours: 3, minutes: 13 })) //~> in 3 hr, 13 min
1389 + * @return {DateTime}
1390 + */
1391 + plus(duration) {
1392 + if (!this.isValid) return this;
1393 + const dur = friendlyDuration(duration);
1394 + return clone(this, adjustTime(this, dur));
1395 + }
1396 +
1397 + /**
1398 + * Subtract a period of time to this DateTime and return the resulting DateTime
1399 + * See {@link plus}
1400 + * @param {Duration|Object|number} duration - The amount to subtract. Either a Luxon Duration, a number of milliseconds, the object argument to Duration.fromObject()
1401 + @return {DateTime}
1402 + */
1403 + minus(duration) {
1404 + if (!this.isValid) return this;
1405 + const dur = friendlyDuration(duration).negate();
1406 + return clone(this, adjustTime(this, dur));
1407 + }
1408 +
1409 + /**
1410 + * "Set" this DateTime to the beginning of a unit of time.
1411 + * @param {string} unit - The unit to go to the beginning of. Can be 'year', 'quarter', 'month', 'week', 'day', 'hour', 'minute', 'second', or 'millisecond'.
1412 + * @example DateTime.local(2014, 3, 3).startOf('month').toISODate(); //=> '2014-03-01'
1413 + * @example DateTime.local(2014, 3, 3).startOf('year').toISODate(); //=> '2014-01-01'
1414 + * @example DateTime.local(2014, 3, 3).startOf('week').toISODate(); //=> '2014-03-03', weeks always start on Mondays
1415 + * @example DateTime.local(2014, 3, 3, 5, 30).startOf('day').toISOTime(); //=> '00:00.000-05:00'
1416 + * @example DateTime.local(2014, 3, 3, 5, 30).startOf('hour').toISOTime(); //=> '05:00:00.000-05:00'
1417 + * @return {DateTime}
1418 + */
1419 + startOf(unit) {
1420 + if (!this.isValid) return this;
1421 + const o = {},
1422 + normalizedUnit = Duration.normalizeUnit(unit);
1423 + switch (normalizedUnit) {
1424 + case "years":
1425 + o.month = 1;
1426 + // falls through
1427 + case "quarters":
1428 + case "months":
1429 + o.day = 1;
1430 + // falls through
1431 + case "weeks":
1432 + case "days":
1433 + o.hour = 0;
1434 + // falls through
1435 + case "hours":
1436 + o.minute = 0;
1437 + // falls through
1438 + case "minutes":
1439 + o.second = 0;
1440 + // falls through
1441 + case "seconds":
1442 + o.millisecond = 0;
1443 + break;
1444 + case "milliseconds":
1445 + break;
1446 + // no default, invalid units throw in normalizeUnit()
1447 + }
1448 +
1449 + if (normalizedUnit === "weeks") {
1450 + o.weekday = 1;
1451 + }
1452 +
1453 + if (normalizedUnit === "quarters") {
1454 + const q = Math.ceil(this.month / 3);
1455 + o.month = (q - 1) * 3 + 1;
1456 + }
1457 +
1458 + return this.set(o);
1459 + }
1460 +
1461 + /**
1462 + * "Set" this DateTime to the end (meaning the last millisecond) of a unit of time
1463 + * @param {string} unit - The unit to go to the end of. Can be 'year', 'quarter', 'month', 'week', 'day', 'hour', 'minute', 'second', or 'millisecond'.
1464 + * @example DateTime.local(2014, 3, 3).endOf('month').toISO(); //=> '2014-03-31T23:59:59.999-05:00'
1465 + * @example DateTime.local(2014, 3, 3).endOf('year').toISO(); //=> '2014-12-31T23:59:59.999-05:00'
1466 + * @example DateTime.local(2014, 3, 3).endOf('week').toISO(); // => '2014-03-09T23:59:59.999-05:00', weeks start on Mondays
1467 + * @example DateTime.local(2014, 3, 3, 5, 30).endOf('day').toISO(); //=> '2014-03-03T23:59:59.999-05:00'
1468 + * @example DateTime.local(2014, 3, 3, 5, 30).endOf('hour').toISO(); //=> '2014-03-03T05:59:59.999-05:00'
1469 + * @return {DateTime}
1470 + */
1471 + endOf(unit) {
1472 + return this.isValid
1473 + ? this.plus({ [unit]: 1 })
1474 + .startOf(unit)
1475 + .minus(1)
1476 + : this;
1477 + }
1478 +
1479 + // OUTPUT
1480 +
1481 + /**
1482 + * Returns a string representation of this DateTime formatted according to the specified format string.
1483 + * **You may not want this.** See {@link toLocaleString} for a more flexible formatting tool. For a table of tokens and their interpretations, see [here](https://moment.github.io/luxon/docs/manual/formatting.html#table-of-tokens).
1484 + * Defaults to en-US if no locale has been specified, regardless of the system's locale.
1485 + * @see https://moment.github.io/luxon/docs/manual/formatting.html#table-of-tokens
1486 + * @param {string} fmt - the format string
1487 + * @param {Object} opts - opts to override the configuration options
1488 + * @example DateTime.now().toFormat('yyyy LLL dd') //=> '2017 Apr 22'
1489 + * @example DateTime.now().setLocale('fr').toFormat('yyyy LLL dd') //=> '2017 avr. 22'
1490 + * @example DateTime.now().toFormat('yyyy LLL dd', { locale: "fr" }) //=> '2017 avr. 22'
1491 + * @example DateTime.now().toFormat("HH 'hours and' mm 'minutes'") //=> '20 hours and 55 minutes'
1492 + * @return {string}
1493 + */
1494 + toFormat(fmt, opts = {}) {
1495 + return this.isValid
1496 + ? Formatter.create(this.loc.redefaultToEN(opts)).formatDateTimeFromString(this, fmt)
1497 + : INVALID;
1498 + }
1499 +
1500 + /**
1501 + * Returns a localized string representing this date. Accepts the same options as the Intl.DateTimeFormat constructor and any presets defined by Luxon, such as `DateTime.DATE_FULL` or `DateTime.TIME_SIMPLE`.
1502 + * The exact behavior of this method is browser-specific, but in general it will return an appropriate representation
1503 + * of the DateTime in the assigned locale.
1504 + * Defaults to the system's locale if no locale has been specified
1505 + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat
1506 + * @param opts {Object} - Intl.DateTimeFormat constructor options and configuration options
1507 + * @example DateTime.now().toLocaleString(); //=> 4/20/2017
1508 + * @example DateTime.now().setLocale('en-gb').toLocaleString(); //=> '20/04/2017'
1509 + * @example DateTime.now().toLocaleString({ locale: 'en-gb' }); //=> '20/04/2017'
1510 + * @example DateTime.now().toLocaleString(DateTime.DATE_FULL); //=> 'April 20, 2017'
1511 + * @example DateTime.now().toLocaleString(DateTime.TIME_SIMPLE); //=> '11:32 AM'
1512 + * @example DateTime.now().toLocaleString(DateTime.DATETIME_SHORT); //=> '4/20/2017, 11:32 AM'
1513 + * @example DateTime.now().toLocaleString({ weekday: 'long', month: 'long', day: '2-digit' }); //=> 'Thursday, April 20'
1514 + * @example DateTime.now().toLocaleString({ weekday: 'short', month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit' }); //=> 'Thu, Apr 20, 11:27 AM'
1515 + * @example DateTime.now().toLocaleString({ hour: '2-digit', minute: '2-digit', hour12: false }); //=> '11:32'
1516 + * @return {string}
1517 + */
1518 + toLocaleString(opts = Formats.DATE_SHORT) {
1519 + return this.isValid
1520 + ? Formatter.create(this.loc.clone(opts), opts).formatDateTime(this)
1521 + : INVALID;
1522 + }
1523 +
1524 + /**
1525 + * Returns an array of format "parts", meaning individual tokens along with metadata. This is allows callers to post-process individual sections of the formatted output.
1526 + * Defaults to the system's locale if no locale has been specified
1527 + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat/formatToParts
1528 + * @param opts {Object} - Intl.DateTimeFormat constructor options, same as `toLocaleString`.
1529 + * @example DateTime.now().toLocaleParts(); //=> [
1530 + * //=> { type: 'day', value: '25' },
1531 + * //=> { type: 'literal', value: '/' },
1532 + * //=> { type: 'month', value: '05' },
1533 + * //=> { type: 'literal', value: '/' },
1534 + * //=> { type: 'year', value: '1982' }
1535 + * //=> ]
1536 + */
1537 + toLocaleParts(opts = {}) {
1538 + return this.isValid
1539 + ? Formatter.create(this.loc.clone(opts), opts).formatDateTimeParts(this)
1540 + : [];
1541 + }
1542 +
1543 + /**
1544 + * Returns an ISO 8601-compliant string representation of this DateTime
1545 + * @param {Object} opts - options
1546 + * @param {boolean} [opts.suppressMilliseconds=false] - exclude milliseconds from the format if they're 0
1547 + * @param {boolean} [opts.suppressSeconds=false] - exclude seconds from the format if they're 0
1548 + * @param {boolean} [opts.includeOffset=true] - include the offset, such as 'Z' or '-04:00'
1549 + * @param {string} [opts.format='extended'] - choose between the basic and extended format
1550 + * @example DateTime.utc(1982, 5, 25).toISO() //=> '1982-05-25T00:00:00.000Z'
1551 + * @example DateTime.now().toISO() //=> '2017-04-22T20:47:05.335-04:00'
1552 + * @example DateTime.now().toISO({ includeOffset: false }) //=> '2017-04-22T20:47:05.335'
1553 + * @example DateTime.now().toISO({ format: 'basic' }) //=> '20170422T204705.335-0400'
1554 + * @return {string}
1555 + */
1556 + toISO(opts = {}) {
1557 + if (!this.isValid) {
1558 + return null;
1559 + }
1560 +
1561 + return `${this.toISODate(opts)}T${this.toISOTime(opts)}`;
1562 + }
1563 +
1564 + /**
1565 + * Returns an ISO 8601-compliant string representation of this DateTime's date component
1566 + * @param {Object} opts - options
1567 + * @param {string} [opts.format='extended'] - choose between the basic and extended format
1568 + * @example DateTime.utc(1982, 5, 25).toISODate() //=> '1982-05-25'
1569 + * @example DateTime.utc(1982, 5, 25).toISODate({ format: 'basic' }) //=> '19820525'
1570 + * @return {string}
1571 + */
1572 + toISODate({ format = "extended" } = {}) {
1573 + let fmt = format === "basic" ? "yyyyMMdd" : "yyyy-MM-dd";
1574 + if (this.year > 9999) {
1575 + fmt = "+" + fmt;
1576 + }
1577 +
1578 + return toTechFormat(this, fmt);
1579 + }
1580 +
1581 + /**
1582 + * Returns an ISO 8601-compliant string representation of this DateTime's week date
1583 + * @example DateTime.utc(1982, 5, 25).toISOWeekDate() //=> '1982-W21-2'
1584 + * @return {string}
1585 + */
1586 + toISOWeekDate() {
1587 + return toTechFormat(this, "kkkk-'W'WW-c");
1588 + }
1589 +
1590 + /**
1591 + * Returns an ISO 8601-compliant string representation of this DateTime's time component
1592 + * @param {Object} opts - options
1593 + * @param {boolean} [opts.suppressMilliseconds=false] - exclude milliseconds from the format if they're 0
1594 + * @param {boolean} [opts.suppressSeconds=false] - exclude seconds from the format if they're 0
1595 + * @param {boolean} [opts.includeOffset=true] - include the offset, such as 'Z' or '-04:00'
1596 + * @param {boolean} [opts.includePrefix=false] - include the `T` prefix
1597 + * @param {string} [opts.format='extended'] - choose between the basic and extended format
1598 + * @example DateTime.utc().set({ hour: 7, minute: 34 }).toISOTime() //=> '07:34:19.361Z'
1599 + * @example DateTime.utc().set({ hour: 7, minute: 34, seconds: 0, milliseconds: 0 }).toISOTime({ suppressSeconds: true }) //=> '07:34Z'
1600 + * @example DateTime.utc().set({ hour: 7, minute: 34 }).toISOTime({ format: 'basic' }) //=> '073419.361Z'
1601 + * @example DateTime.utc().set({ hour: 7, minute: 34 }).toISOTime({ includePrefix: true }) //=> 'T07:34:19.361Z'
1602 + * @return {string}
1603 + */
1604 + toISOTime({
1605 + suppressMilliseconds = false,
1606 + suppressSeconds = false,
1607 + includeOffset = true,
1608 + includePrefix = false,
1609 + format = "extended"
1610 + } = {}) {
1611 + return toTechTimeFormat(this, {
1612 + suppressSeconds,
1613 + suppressMilliseconds,
1614 + includeOffset,
1615 + includePrefix,
1616 + format
1617 + });
1618 + }
1619 +
1620 + /**
1621 + * Returns an RFC 2822-compatible string representation of this DateTime, always in UTC
1622 + * @example DateTime.utc(2014, 7, 13).toRFC2822() //=> 'Sun, 13 Jul 2014 00:00:00 +0000'
1623 + * @example DateTime.local(2014, 7, 13).toRFC2822() //=> 'Sun, 13 Jul 2014 00:00:00 -0400'
1624 + * @return {string}
1625 + */
1626 + toRFC2822() {
1627 + return toTechFormat(this, "EEE, dd LLL yyyy HH:mm:ss ZZZ", false);
1628 + }
1629 +
1630 + /**
1631 + * Returns a string representation of this DateTime appropriate for use in HTTP headers.
1632 + * Specifically, the string conforms to RFC 1123.
1633 + * @see https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1
1634 + * @example DateTime.utc(2014, 7, 13).toHTTP() //=> 'Sun, 13 Jul 2014 00:00:00 GMT'
1635 + * @example DateTime.utc(2014, 7, 13, 19).toHTTP() //=> 'Sun, 13 Jul 2014 19:00:00 GMT'
1636 + * @return {string}
1637 + */
1638 + toHTTP() {
1639 + return toTechFormat(this.toUTC(), "EEE, dd LLL yyyy HH:mm:ss 'GMT'");
1640 + }
1641 +
1642 + /**
1643 + * Returns a string representation of this DateTime appropriate for use in SQL Date
1644 + * @example DateTime.utc(2014, 7, 13).toSQLDate() //=> '2014-07-13'
1645 + * @return {string}
1646 + */
1647 + toSQLDate() {
1648 + return toTechFormat(this, "yyyy-MM-dd");
1649 + }
1650 +
1651 + /**
1652 + * Returns a string representation of this DateTime appropriate for use in SQL Time
1653 + * @param {Object} opts - options
1654 + * @param {boolean} [opts.includeZone=false] - include the zone, such as 'America/New_York'. Overrides includeOffset.
1655 + * @param {boolean} [opts.includeOffset=true] - include the offset, such as 'Z' or '-04:00'
1656 + * @example DateTime.utc().toSQL() //=> '05:15:16.345'
1657 + * @example DateTime.now().toSQL() //=> '05:15:16.345 -04:00'
1658 + * @example DateTime.now().toSQL({ includeOffset: false }) //=> '05:15:16.345'
1659 + * @example DateTime.now().toSQL({ includeZone: false }) //=> '05:15:16.345 America/New_York'
1660 + * @return {string}
1661 + */
1662 + toSQLTime({ includeOffset = true, includeZone = false } = {}) {
1663 + return toTechTimeFormat(this, {
1664 + includeOffset,
1665 + includeZone,
1666 + spaceZone: true
1667 + });
1668 + }
1669 +
1670 + /**
1671 + * Returns a string representation of this DateTime appropriate for use in SQL DateTime
1672 + * @param {Object} opts - options
1673 + * @param {boolean} [opts.includeZone=false] - include the zone, such as 'America/New_York'. Overrides includeOffset.
1674 + * @param {boolean} [opts.includeOffset=true] - include the offset, such as 'Z' or '-04:00'
1675 + * @example DateTime.utc(2014, 7, 13).toSQL() //=> '2014-07-13 00:00:00.000 Z'
1676 + * @example DateTime.local(2014, 7, 13).toSQL() //=> '2014-07-13 00:00:00.000 -04:00'
1677 + * @example DateTime.local(2014, 7, 13).toSQL({ includeOffset: false }) //=> '2014-07-13 00:00:00.000'
1678 + * @example DateTime.local(2014, 7, 13).toSQL({ includeZone: true }) //=> '2014-07-13 00:00:00.000 America/New_York'
1679 + * @return {string}
1680 + */
1681 + toSQL(opts = {}) {
1682 + if (!this.isValid) {
1683 + return null;
1684 + }
1685 +
1686 + return `${this.toSQLDate()} ${this.toSQLTime(opts)}`;
1687 + }
1688 +
1689 + /**
1690 + * Returns a string representation of this DateTime appropriate for debugging
1691 + * @return {string}
1692 + */
1693 + toString() {
1694 + return this.isValid ? this.toISO() : INVALID;
1695 + }
1696 +
1697 + /**
1698 + * Returns the epoch milliseconds of this DateTime. Alias of {@link toMillis}
1699 + * @return {number}
1700 + */
1701 + valueOf() {
1702 + return this.toMillis();
1703 + }
1704 +
1705 + /**
1706 + * Returns the epoch milliseconds of this DateTime.
1707 + * @return {number}
1708 + */
1709 + toMillis() {
1710 + return this.isValid ? this.ts : NaN;
1711 + }
1712 +
1713 + /**
1714 + * Returns the epoch seconds of this DateTime.
1715 + * @return {number}
1716 + */
1717 + toSeconds() {
1718 + return this.isValid ? this.ts / 1000 : NaN;
1719 + }
1720 +
1721 + /**
1722 + * Returns an ISO 8601 representation of this DateTime appropriate for use in JSON.
1723 + * @return {string}
1724 + */
1725 + toJSON() {
1726 + return this.toISO();
1727 + }
1728 +
1729 + /**
1730 + * Returns a BSON serializable equivalent to this DateTime.
1731 + * @return {Date}
1732 + */
1733 + toBSON() {
1734 + return this.toJSDate();
1735 + }
1736 +
1737 + /**
1738 + * Returns a JavaScript object with this DateTime's year, month, day, and so on.
1739 + * @param opts - options for generating the object
1740 + * @param {boolean} [opts.includeConfig=false] - include configuration attributes in the output
1741 + * @example DateTime.now().toObject() //=> { year: 2017, month: 4, day: 22, hour: 20, minute: 49, second: 42, millisecond: 268 }
1742 + * @return {Object}
1743 + */
1744 + toObject(opts = {}) {
1745 + if (!this.isValid) return {};
1746 +
1747 + const base = Object.assign({}, this.c);
1748 +
1749 + if (opts.includeConfig) {
1750 + base.outputCalendar = this.outputCalendar;
1751 + base.numberingSystem = this.loc.numberingSystem;
1752 + base.locale = this.loc.locale;
1753 + }
1754 + return base;
1755 + }
1756 +
1757 + /**
1758 + * Returns a JavaScript Date equivalent to this DateTime.
1759 + * @return {Date}
1760 + */
1761 + toJSDate() {
1762 + return new Date(this.isValid ? this.ts : NaN);
1763 + }
1764 +
1765 + // COMPARE
1766 +
1767 + /**
1768 + * Return the difference between two DateTimes as a Duration.
1769 + * @param {DateTime} otherDateTime - the DateTime to compare this one to
1770 + * @param {string|string[]} [unit=['milliseconds']] - the unit or array of units (such as 'hours' or 'days') to include in the duration.
1771 + * @param {Object} opts - options that affect the creation of the Duration
1772 + * @param {string} [opts.conversionAccuracy='casual'] - the conversion system to use
1773 + * @example
1774 + * var i1 = DateTime.fromISO('1982-05-25T09:45'),
1775 + * i2 = DateTime.fromISO('1983-10-14T10:30');
1776 + * i2.diff(i1).toObject() //=> { milliseconds: 43807500000 }
1777 + * i2.diff(i1, 'hours').toObject() //=> { hours: 12168.75 }
1778 + * i2.diff(i1, ['months', 'days']).toObject() //=> { months: 16, days: 19.03125 }
1779 + * i2.diff(i1, ['months', 'days', 'hours']).toObject() //=> { months: 16, days: 19, hours: 0.75 }
1780 + * @return {Duration}
1781 + */
1782 + diff(otherDateTime, unit = "milliseconds", opts = {}) {
1783 + if (!this.isValid || !otherDateTime.isValid) {
1784 + return Duration.invalid(
1785 + this.invalid || otherDateTime.invalid,
1786 + "created by diffing an invalid DateTime"
1787 + );
1788 + }
1789 +
1790 + const durOpts = Object.assign(
1791 + { locale: this.locale, numberingSystem: this.numberingSystem },
1792 + opts
1793 + );
1794 +
1795 + const units = maybeArray(unit).map(Duration.normalizeUnit),
1796 + otherIsLater = otherDateTime.valueOf() > this.valueOf(),
1797 + earlier = otherIsLater ? this : otherDateTime,
1798 + later = otherIsLater ? otherDateTime : this,
1799 + diffed = diff(earlier, later, units, durOpts);
1800 +
1801 + return otherIsLater ? diffed.negate() : diffed;
1802 + }
1803 +
1804 + /**
1805 + * Return the difference between this DateTime and right now.
1806 + * See {@link diff}
1807 + * @param {string|string[]} [unit=['milliseconds']] - the unit or units units (such as 'hours' or 'days') to include in the duration
1808 + * @param {Object} opts - options that affect the creation of the Duration
1809 + * @param {string} [opts.conversionAccuracy='casual'] - the conversion system to use
1810 + * @return {Duration}
1811 + */
1812 + diffNow(unit = "milliseconds", opts = {}) {
1813 + return this.diff(DateTime.now(), unit, opts);
1814 + }
1815 +
1816 + /**
1817 + * Return an Interval spanning between this DateTime and another DateTime
1818 + * @param {DateTime} otherDateTime - the other end point of the Interval
1819 + * @return {Interval}
1820 + */
1821 + until(otherDateTime) {
1822 + return this.isValid ? Interval.fromDateTimes(this, otherDateTime) : this;
1823 + }
1824 +
1825 + /**
1826 + * Return whether this DateTime is in the same unit of time as another DateTime.
1827 + * Higher-order units must also be identical for this function to return `true`.
1828 + * Note that time zones are **ignored** in this comparison, which compares the **local** calendar time. Use {@link setZone} to convert one of the dates if needed.
1829 + * @param {DateTime} otherDateTime - the other DateTime
1830 + * @param {string} unit - the unit of time to check sameness on
1831 + * @example DateTime.now().hasSame(otherDT, 'day'); //~> true if otherDT is in the same current calendar day
1832 + * @return {boolean}
1833 + */
1834 + hasSame(otherDateTime, unit) {
1835 + if (!this.isValid) return false;
1836 +
1837 + const inputMs = otherDateTime.valueOf();
1838 + const otherZoneDateTime = this.setZone(otherDateTime.zone, { keepLocalTime: true });
1839 + return otherZoneDateTime.startOf(unit) <= inputMs && inputMs <= otherZoneDateTime.endOf(unit);
1840 + }
1841 +
1842 + /**
1843 + * Equality check
1844 + * Two DateTimes are equal iff they represent the same millisecond, have the same zone and location, and are both valid.
1845 + * To compare just the millisecond values, use `+dt1 === +dt2`.
1846 + * @param {DateTime} other - the other DateTime
1847 + * @return {boolean}
1848 + */
1849 + equals(other) {
1850 + return (
1851 + this.isValid &&
1852 + other.isValid &&
1853 + this.valueOf() === other.valueOf() &&
1854 + this.zone.equals(other.zone) &&
1855 + this.loc.equals(other.loc)
1856 + );
1857 + }
1858 +
1859 + /**
1860 + * Returns a string representation of a this time relative to now, such as "in two days". Can only internationalize if your
1861 + * platform supports Intl.RelativeTimeFormat. Rounds down by default.
1862 + * @param {Object} options - options that affect the output
1863 + * @param {DateTime} [options.base=DateTime.now()] - the DateTime to use as the basis to which this time is compared. Defaults to now.
1864 + * @param {string} [options.style="long"] - the style of units, must be "long", "short", or "narrow"
1865 + * @param {string|string[]} options.unit - use a specific unit or array of units; if omitted, or an array, the method will pick the best unit. Use an array or one of "years", "quarters", "months", "weeks", "days", "hours", "minutes", or "seconds"
1866 + * @param {boolean} [options.round=true] - whether to round the numbers in the output.
1867 + * @param {number} [options.padding=0] - padding in milliseconds. This allows you to round up the result if it fits inside the threshold. Don't use in combination with {round: false} because the decimal output will include the padding.
1868 + * @param {string} options.locale - override the locale of this DateTime
1869 + * @param {string} options.numberingSystem - override the numberingSystem of this DateTime. The Intl system may choose not to honor this
1870 + * @example DateTime.now().plus({ days: 1 }).toRelative() //=> "in 1 day"
1871 + * @example DateTime.now().setLocale("es").toRelative({ days: 1 }) //=> "dentro de 1 día"
1872 + * @example DateTime.now().plus({ days: 1 }).toRelative({ locale: "fr" }) //=> "dans 23 heures"
1873 + * @example DateTime.now().minus({ days: 2 }).toRelative() //=> "2 days ago"
1874 + * @example DateTime.now().minus({ days: 2 }).toRelative({ unit: "hours" }) //=> "48 hours ago"
1875 + * @example DateTime.now().minus({ hours: 36 }).toRelative({ round: false }) //=> "1.5 days ago"
1876 + */
1877 + toRelative(options = {}) {
1878 + if (!this.isValid) return null;
1879 + const base = options.base || DateTime.fromObject({ zone: this.zone }),
1880 + padding = options.padding ? (this < base ? -options.padding : options.padding) : 0;
1881 + let units = ["years", "months", "days", "hours", "minutes", "seconds"];
1882 + let unit = options.unit;
1883 + if (Array.isArray(options.unit)) {
1884 + units = options.unit;
1885 + unit = undefined;
1886 + }
1887 + return diffRelative(
1888 + base,
1889 + this.plus(padding),
1890 + Object.assign(options, {
1891 + numeric: "always",
1892 + units,
1893 + unit
1894 + })
1895 + );
1896 + }
1897 +
1898 + /**
1899 + * Returns a string representation of this date relative to today, such as "yesterday" or "next month".
1900 + * Only internationalizes on platforms that supports Intl.RelativeTimeFormat.
1901 + * @param {Object} options - options that affect the output
1902 + * @param {DateTime} [options.base=DateTime.now()] - the DateTime to use as the basis to which this time is compared. Defaults to now.
1903 + * @param {string} options.locale - override the locale of this DateTime
1904 + * @param {string} options.unit - use a specific unit; if omitted, the method will pick the unit. Use one of "years", "quarters", "months", "weeks", or "days"
1905 + * @param {string} options.numberingSystem - override the numberingSystem of this DateTime. The Intl system may choose not to honor this
1906 + * @example DateTime.now().plus({ days: 1 }).toRelativeCalendar() //=> "tomorrow"
1907 + * @example DateTime.now().setLocale("es").plus({ days: 1 }).toRelative() //=> ""mañana"
1908 + * @example DateTime.now().plus({ days: 1 }).toRelativeCalendar({ locale: "fr" }) //=> "demain"
1909 + * @example DateTime.now().minus({ days: 2 }).toRelativeCalendar() //=> "2 days ago"
1910 + */
1911 + toRelativeCalendar(options = {}) {
1912 + if (!this.isValid) return null;
1913 +
1914 + return diffRelative(
1915 + options.base || DateTime.fromObject({ zone: this.zone }),
1916 + this,
1917 + Object.assign(options, {
1918 + numeric: "auto",
1919 + units: ["years", "months", "days"],
1920 + calendary: true
1921 + })
1922 + );
1923 + }
1924 +
1925 + /**
1926 + * Return the min of several date times
1927 + * @param {...DateTime} dateTimes - the DateTimes from which to choose the minimum
1928 + * @return {DateTime} the min DateTime, or undefined if called with no argument
1929 + */
1930 + static min(...dateTimes) {
1931 + if (!dateTimes.every(DateTime.isDateTime)) {
1932 + throw new InvalidArgumentError("min requires all arguments be DateTimes");
1933 + }
1934 + return bestBy(dateTimes, i => i.valueOf(), Math.min);
1935 + }
1936 +
1937 + /**
1938 + * Return the max of several date times
1939 + * @param {...DateTime} dateTimes - the DateTimes from which to choose the maximum
1940 + * @return {DateTime} the max DateTime, or undefined if called with no argument
1941 + */
1942 + static max(...dateTimes) {
1943 + if (!dateTimes.every(DateTime.isDateTime)) {
1944 + throw new InvalidArgumentError("max requires all arguments be DateTimes");
1945 + }
1946 + return bestBy(dateTimes, i => i.valueOf(), Math.max);
1947 + }
1948 +
1949 + // MISC
1950 +
1951 + /**
1952 + * Explain how a string would be parsed by fromFormat()
1953 + * @param {string} text - the string to parse
1954 + * @param {string} fmt - the format the string is expected to be in (see description)
1955 + * @param {Object} options - options taken by fromFormat()
1956 + * @return {Object}
1957 + */
1958 + static fromFormatExplain(text, fmt, options = {}) {
1959 + const { locale = null, numberingSystem = null } = options,
1960 + localeToUse = Locale.fromOpts({
1961 + locale,
1962 + numberingSystem,
1963 + defaultToEN: true
1964 + });
1965 + return explainFromTokens(localeToUse, text, fmt);
1966 + }
1967 +
1968 + /**
1969 + * @deprecated use fromFormatExplain instead
1970 + */
1971 + static fromStringExplain(text, fmt, options = {}) {
1972 + return DateTime.fromFormatExplain(text, fmt, options);
1973 + }
1974 +
1975 + // FORMAT PRESETS
1976 +
1977 + /**
1978 + * {@link toLocaleString} format like 10/14/1983
1979 + * @type {Object}
1980 + */
1981 + static get DATE_SHORT() {
1982 + return Formats.DATE_SHORT;
1983 + }
1984 +
1985 + /**
1986 + * {@link toLocaleString} format like 'Oct 14, 1983'
1987 + * @type {Object}
1988 + */
1989 + static get DATE_MED() {
1990 + return Formats.DATE_MED;
1991 + }
1992 +
1993 + /**
1994 + * {@link toLocaleString} format like 'Fri, Oct 14, 1983'
1995 + * @type {Object}
1996 + */
1997 + static get DATE_MED_WITH_WEEKDAY() {
1998 + return Formats.DATE_MED_WITH_WEEKDAY;
1999 + }
2000 +
2001 + /**
2002 + * {@link toLocaleString} format like 'October 14, 1983'
2003 + * @type {Object}
2004 + */
2005 + static get DATE_FULL() {
2006 + return Formats.DATE_FULL;
2007 + }
2008 +
2009 + /**
2010 + * {@link toLocaleString} format like 'Tuesday, October 14, 1983'
2011 + * @type {Object}
2012 + */
2013 + static get DATE_HUGE() {
2014 + return Formats.DATE_HUGE;
2015 + }
2016 +
2017 + /**
2018 + * {@link toLocaleString} format like '09:30 AM'. Only 12-hour if the locale is.
2019 + * @type {Object}
2020 + */
2021 + static get TIME_SIMPLE() {
2022 + return Formats.TIME_SIMPLE;
2023 + }
2024 +
2025 + /**
2026 + * {@link toLocaleString} format like '09:30:23 AM'. Only 12-hour if the locale is.
2027 + * @type {Object}
2028 + */
2029 + static get TIME_WITH_SECONDS() {
2030 + return Formats.TIME_WITH_SECONDS;
2031 + }
2032 +
2033 + /**
2034 + * {@link toLocaleString} format like '09:30:23 AM EDT'. Only 12-hour if the locale is.
2035 + * @type {Object}
2036 + */
2037 + static get TIME_WITH_SHORT_OFFSET() {
2038 + return Formats.TIME_WITH_SHORT_OFFSET;
2039 + }
2040 +
2041 + /**
2042 + * {@link toLocaleString} format like '09:30:23 AM Eastern Daylight Time'. Only 12-hour if the locale is.
2043 + * @type {Object}
2044 + */
2045 + static get TIME_WITH_LONG_OFFSET() {
2046 + return Formats.TIME_WITH_LONG_OFFSET;
2047 + }
2048 +
2049 + /**
2050 + * {@link toLocaleString} format like '09:30', always 24-hour.
2051 + * @type {Object}
2052 + */
2053 + static get TIME_24_SIMPLE() {
2054 + return Formats.TIME_24_SIMPLE;
2055 + }
2056 +
2057 + /**
2058 + * {@link toLocaleString} format like '09:30:23', always 24-hour.
2059 + * @type {Object}
2060 + */
2061 + static get TIME_24_WITH_SECONDS() {
2062 + return Formats.TIME_24_WITH_SECONDS;
2063 + }
2064 +
2065 + /**
2066 + * {@link toLocaleString} format like '09:30:23 EDT', always 24-hour.
2067 + * @type {Object}
2068 + */
2069 + static get TIME_24_WITH_SHORT_OFFSET() {
2070 + return Formats.TIME_24_WITH_SHORT_OFFSET;
2071 + }
2072 +
2073 + /**
2074 + * {@link toLocaleString} format like '09:30:23 Eastern Daylight Time', always 24-hour.
2075 + * @type {Object}
2076 + */
2077 + static get TIME_24_WITH_LONG_OFFSET() {
2078 + return Formats.TIME_24_WITH_LONG_OFFSET;
2079 + }
2080 +
2081 + /**
2082 + * {@link toLocaleString} format like '10/14/1983, 9:30 AM'. Only 12-hour if the locale is.
2083 + * @type {Object}
2084 + */
2085 + static get DATETIME_SHORT() {
2086 + return Formats.DATETIME_SHORT;
2087 + }
2088 +
2089 + /**
2090 + * {@link toLocaleString} format like '10/14/1983, 9:30:33 AM'. Only 12-hour if the locale is.
2091 + * @type {Object}
2092 + */
2093 + static get DATETIME_SHORT_WITH_SECONDS() {
2094 + return Formats.DATETIME_SHORT_WITH_SECONDS;
2095 + }
2096 +
2097 + /**
2098 + * {@link toLocaleString} format like 'Oct 14, 1983, 9:30 AM'. Only 12-hour if the locale is.
2099 + * @type {Object}
2100 + */
2101 + static get DATETIME_MED() {
2102 + return Formats.DATETIME_MED;
2103 + }
2104 +
2105 + /**
2106 + * {@link toLocaleString} format like 'Oct 14, 1983, 9:30:33 AM'. Only 12-hour if the locale is.
2107 + * @type {Object}
2108 + */
2109 + static get DATETIME_MED_WITH_SECONDS() {
2110 + return Formats.DATETIME_MED_WITH_SECONDS;
2111 + }
2112 +
2113 + /**
2114 + * {@link toLocaleString} format like 'Fri, 14 Oct 1983, 9:30 AM'. Only 12-hour if the locale is.
2115 + * @type {Object}
2116 + */
2117 + static get DATETIME_MED_WITH_WEEKDAY() {
2118 + return Formats.DATETIME_MED_WITH_WEEKDAY;
2119 + }
2120 +
2121 + /**
2122 + * {@link toLocaleString} format like 'October 14, 1983, 9:30 AM EDT'. Only 12-hour if the locale is.
2123 + * @type {Object}
2124 + */
2125 + static get DATETIME_FULL() {
2126 + return Formats.DATETIME_FULL;
2127 + }
2128 +
2129 + /**
2130 + * {@link toLocaleString} format like 'October 14, 1983, 9:30:33 AM EDT'. Only 12-hour if the locale is.
2131 + * @type {Object}
2132 + */
2133 + static get DATETIME_FULL_WITH_SECONDS() {
2134 + return Formats.DATETIME_FULL_WITH_SECONDS;
2135 + }
2136 +
2137 + /**
2138 + * {@link toLocaleString} format like 'Friday, October 14, 1983, 9:30 AM Eastern Daylight Time'. Only 12-hour if the locale is.
2139 + * @type {Object}
2140 + */
2141 + static get DATETIME_HUGE() {
2142 + return Formats.DATETIME_HUGE;
2143 + }
2144 +
2145 + /**
2146 + * {@link toLocaleString} format like 'Friday, October 14, 1983, 9:30:33 AM Eastern Daylight Time'. Only 12-hour if the locale is.
2147 + * @type {Object}
2148 + */
2149 + static get DATETIME_HUGE_WITH_SECONDS() {
2150 + return Formats.DATETIME_HUGE_WITH_SECONDS;
2151 + }
2152 +}
2153 +
2154 +/**
2155 + * @private
2156 + */
2157 +export function friendlyDateTime(dateTimeish) {
2158 + if (DateTime.isDateTime(dateTimeish)) {
2159 + return dateTimeish;
2160 + } else if (dateTimeish && dateTimeish.valueOf && isNumber(dateTimeish.valueOf())) {
2161 + return DateTime.fromJSDate(dateTimeish);
2162 + } else if (dateTimeish && typeof dateTimeish === "object") {
2163 + return DateTime.fromObject(dateTimeish);
2164 + } else {
2165 + throw new InvalidArgumentError(
2166 + `Unknown datetime argument: ${dateTimeish}, of type ${typeof dateTimeish}`
2167 + );
2168 + }
2169 +}
1 +import { InvalidArgumentError, InvalidDurationError, InvalidUnitError } from "./errors.js";
2 +import Formatter from "./impl/formatter.js";
3 +import Invalid from "./impl/invalid.js";
4 +import Locale from "./impl/locale.js";
5 +import { parseISODuration, parseISOTimeOnly } from "./impl/regexParser.js";
6 +import {
7 + asNumber,
8 + hasOwnProperty,
9 + isNumber,
10 + isUndefined,
11 + normalizeObject,
12 + roundTo
13 +} from "./impl/util.js";
14 +import Settings from "./settings.js";
15 +
16 +const INVALID = "Invalid Duration";
17 +
18 +// unit conversion constants
19 +const lowOrderMatrix = {
20 + weeks: {
21 + days: 7,
22 + hours: 7 * 24,
23 + minutes: 7 * 24 * 60,
24 + seconds: 7 * 24 * 60 * 60,
25 + milliseconds: 7 * 24 * 60 * 60 * 1000
26 + },
27 + days: {
28 + hours: 24,
29 + minutes: 24 * 60,
30 + seconds: 24 * 60 * 60,
31 + milliseconds: 24 * 60 * 60 * 1000
32 + },
33 + hours: { minutes: 60, seconds: 60 * 60, milliseconds: 60 * 60 * 1000 },
34 + minutes: { seconds: 60, milliseconds: 60 * 1000 },
35 + seconds: { milliseconds: 1000 }
36 + },
37 + casualMatrix = Object.assign(
38 + {
39 + years: {
40 + quarters: 4,
41 + months: 12,
42 + weeks: 52,
43 + days: 365,
44 + hours: 365 * 24,
45 + minutes: 365 * 24 * 60,
46 + seconds: 365 * 24 * 60 * 60,
47 + milliseconds: 365 * 24 * 60 * 60 * 1000
48 + },
49 + quarters: {
50 + months: 3,
51 + weeks: 13,
52 + days: 91,
53 + hours: 91 * 24,
54 + minutes: 91 * 24 * 60,
55 + seconds: 91 * 24 * 60 * 60,
56 + milliseconds: 91 * 24 * 60 * 60 * 1000
57 + },
58 + months: {
59 + weeks: 4,
60 + days: 30,
61 + hours: 30 * 24,
62 + minutes: 30 * 24 * 60,
63 + seconds: 30 * 24 * 60 * 60,
64 + milliseconds: 30 * 24 * 60 * 60 * 1000
65 + }
66 + },
67 + lowOrderMatrix
68 + ),
69 + daysInYearAccurate = 146097.0 / 400,
70 + daysInMonthAccurate = 146097.0 / 4800,
71 + accurateMatrix = Object.assign(
72 + {
73 + years: {
74 + quarters: 4,
75 + months: 12,
76 + weeks: daysInYearAccurate / 7,
77 + days: daysInYearAccurate,
78 + hours: daysInYearAccurate * 24,
79 + minutes: daysInYearAccurate * 24 * 60,
80 + seconds: daysInYearAccurate * 24 * 60 * 60,
81 + milliseconds: daysInYearAccurate * 24 * 60 * 60 * 1000
82 + },
83 + quarters: {
84 + months: 3,
85 + weeks: daysInYearAccurate / 28,
86 + days: daysInYearAccurate / 4,
87 + hours: (daysInYearAccurate * 24) / 4,
88 + minutes: (daysInYearAccurate * 24 * 60) / 4,
89 + seconds: (daysInYearAccurate * 24 * 60 * 60) / 4,
90 + milliseconds: (daysInYearAccurate * 24 * 60 * 60 * 1000) / 4
91 + },
92 + months: {
93 + weeks: daysInMonthAccurate / 7,
94 + days: daysInMonthAccurate,
95 + hours: daysInMonthAccurate * 24,
96 + minutes: daysInMonthAccurate * 24 * 60,
97 + seconds: daysInMonthAccurate * 24 * 60 * 60,
98 + milliseconds: daysInMonthAccurate * 24 * 60 * 60 * 1000
99 + }
100 + },
101 + lowOrderMatrix
102 + );
103 +
104 +// units ordered by size
105 +const orderedUnits = [
106 + "years",
107 + "quarters",
108 + "months",
109 + "weeks",
110 + "days",
111 + "hours",
112 + "minutes",
113 + "seconds",
114 + "milliseconds"
115 +];
116 +
117 +const reverseUnits = orderedUnits.slice(0).reverse();
118 +
119 +// clone really means "create another instance just like this one, but with these changes"
120 +function clone(dur, alts, clear = false) {
121 + // deep merge for vals
122 + const conf = {
123 + values: clear ? alts.values : Object.assign({}, dur.values, alts.values || {}),
124 + loc: dur.loc.clone(alts.loc),
125 + conversionAccuracy: alts.conversionAccuracy || dur.conversionAccuracy
126 + };
127 + return new Duration(conf);
128 +}
129 +
130 +function antiTrunc(n) {
131 + return n < 0 ? Math.floor(n) : Math.ceil(n);
132 +}
133 +
134 +// NB: mutates parameters
135 +function convert(matrix, fromMap, fromUnit, toMap, toUnit) {
136 + const conv = matrix[toUnit][fromUnit],
137 + raw = fromMap[fromUnit] / conv,
138 + sameSign = Math.sign(raw) === Math.sign(toMap[toUnit]),
139 + // ok, so this is wild, but see the matrix in the tests
140 + added =
141 + !sameSign && toMap[toUnit] !== 0 && Math.abs(raw) <= 1 ? antiTrunc(raw) : Math.trunc(raw);
142 + toMap[toUnit] += added;
143 + fromMap[fromUnit] -= added * conv;
144 +}
145 +
146 +// NB: mutates parameters
147 +function normalizeValues(matrix, vals) {
148 + reverseUnits.reduce((previous, current) => {
149 + if (!isUndefined(vals[current])) {
150 + if (previous) {
151 + convert(matrix, vals, previous, vals, current);
152 + }
153 + return current;
154 + } else {
155 + return previous;
156 + }
157 + }, null);
158 +}
159 +
160 +/**
161 + * A Duration object represents a period of time, like "2 months" or "1 day, 1 hour". Conceptually, it's just a map of units to their quantities, accompanied by some additional configuration and methods for creating, parsing, interrogating, transforming, and formatting them. They can be used on their own or in conjunction with other Luxon types; for example, you can use {@link DateTime.plus} to add a Duration object to a DateTime, producing another DateTime.
162 + *
163 + * Here is a brief overview of commonly used methods and getters in Duration:
164 + *
165 + * * **Creation** To create a Duration, use {@link Duration.fromMillis}, {@link Duration.fromObject}, or {@link Duration.fromISO}.
166 + * * **Unit values** See the {@link Duration.years}, {@link Duration.months}, {@link Duration.weeks}, {@link Duration.days}, {@link Duration.hours}, {@link Duration.minutes}, {@link Duration.seconds}, {@link Duration.milliseconds} accessors.
167 + * * **Configuration** See {@link Duration.locale} and {@link Duration.numberingSystem} accessors.
168 + * * **Transformation** To create new Durations out of old ones use {@link Duration.plus}, {@link Duration.minus}, {@link Duration.normalize}, {@link Duration.set}, {@link Duration.reconfigure}, {@link Duration.shiftTo}, and {@link Duration.negate}.
169 + * * **Output** To convert the Duration into other representations, see {@link Duration.as}, {@link Duration.toISO}, {@link Duration.toFormat}, and {@link Duration.toJSON}
170 + *
171 + * There's are more methods documented below. In addition, for more information on subtler topics like internationalization and validity, see the external documentation.
172 + */
173 +export default class Duration {
174 + /**
175 + * @private
176 + */
177 + constructor(config) {
178 + const accurate = config.conversionAccuracy === "longterm" || false;
179 + /**
180 + * @access private
181 + */
182 + this.values = config.values;
183 + /**
184 + * @access private
185 + */
186 + this.loc = config.loc || Locale.create();
187 + /**
188 + * @access private
189 + */
190 + this.conversionAccuracy = accurate ? "longterm" : "casual";
191 + /**
192 + * @access private
193 + */
194 + this.invalid = config.invalid || null;
195 + /**
196 + * @access private
197 + */
198 + this.matrix = accurate ? accurateMatrix : casualMatrix;
199 + /**
200 + * @access private
201 + */
202 + this.isLuxonDuration = true;
203 + }
204 +
205 + /**
206 + * Create Duration from a number of milliseconds.
207 + * @param {number} count of milliseconds
208 + * @param {Object} opts - options for parsing
209 + * @param {string} [opts.locale='en-US'] - the locale to use
210 + * @param {string} opts.numberingSystem - the numbering system to use
211 + * @param {string} [opts.conversionAccuracy='casual'] - the conversion system to use
212 + * @return {Duration}
213 + */
214 + static fromMillis(count, opts) {
215 + return Duration.fromObject(Object.assign({ milliseconds: count }, opts));
216 + }
217 +
218 + /**
219 + * Create a Duration from a JavaScript object with keys like 'years' and 'hours.
220 + * If this object is empty then a zero milliseconds duration is returned.
221 + * @param {Object} obj - the object to create the DateTime from
222 + * @param {number} obj.years
223 + * @param {number} obj.quarters
224 + * @param {number} obj.months
225 + * @param {number} obj.weeks
226 + * @param {number} obj.days
227 + * @param {number} obj.hours
228 + * @param {number} obj.minutes
229 + * @param {number} obj.seconds
230 + * @param {number} obj.milliseconds
231 + * @param {string} [obj.locale='en-US'] - the locale to use
232 + * @param {string} obj.numberingSystem - the numbering system to use
233 + * @param {string} [obj.conversionAccuracy='casual'] - the conversion system to use
234 + * @return {Duration}
235 + */
236 + static fromObject(obj) {
237 + if (obj == null || typeof obj !== "object") {
238 + throw new InvalidArgumentError(
239 + `Duration.fromObject: argument expected to be an object, got ${
240 + obj === null ? "null" : typeof obj
241 + }`
242 + );
243 + }
244 + return new Duration({
245 + values: normalizeObject(obj, Duration.normalizeUnit, [
246 + "locale",
247 + "numberingSystem",
248 + "conversionAccuracy",
249 + "zone" // a bit of debt; it's super inconvenient internally not to be able to blindly pass this
250 + ]),
251 + loc: Locale.fromObject(obj),
252 + conversionAccuracy: obj.conversionAccuracy
253 + });
254 + }
255 +
256 + /**
257 + * Create a Duration from an ISO 8601 duration string.
258 + * @param {string} text - text to parse
259 + * @param {Object} opts - options for parsing
260 + * @param {string} [opts.locale='en-US'] - the locale to use
261 + * @param {string} opts.numberingSystem - the numbering system to use
262 + * @param {string} [opts.conversionAccuracy='casual'] - the conversion system to use
263 + * @see https://en.wikipedia.org/wiki/ISO_8601#Durations
264 + * @example Duration.fromISO('P3Y6M1W4DT12H30M5S').toObject() //=> { years: 3, months: 6, weeks: 1, days: 4, hours: 12, minutes: 30, seconds: 5 }
265 + * @example Duration.fromISO('PT23H').toObject() //=> { hours: 23 }
266 + * @example Duration.fromISO('P5Y3M').toObject() //=> { years: 5, months: 3 }
267 + * @return {Duration}
268 + */
269 + static fromISO(text, opts) {
270 + const [parsed] = parseISODuration(text);
271 + if (parsed) {
272 + const obj = Object.assign(parsed, opts);
273 + return Duration.fromObject(obj);
274 + } else {
275 + return Duration.invalid("unparsable", `the input "${text}" can't be parsed as ISO 8601`);
276 + }
277 + }
278 +
279 + /**
280 + * Create a Duration from an ISO 8601 time string.
281 + * @param {string} text - text to parse
282 + * @param {Object} opts - options for parsing
283 + * @param {string} [opts.locale='en-US'] - the locale to use
284 + * @param {string} opts.numberingSystem - the numbering system to use
285 + * @param {string} [opts.conversionAccuracy='casual'] - the conversion system to use
286 + * @see https://en.wikipedia.org/wiki/ISO_8601#Times
287 + * @example Duration.fromISOTime('11:22:33.444').toObject() //=> { hours: 11, minutes: 22, seconds: 33, milliseconds: 444 }
288 + * @example Duration.fromISOTime('11:00').toObject() //=> { hours: 11, minutes: 0, seconds: 0 }
289 + * @example Duration.fromISOTime('T11:00').toObject() //=> { hours: 11, minutes: 0, seconds: 0 }
290 + * @example Duration.fromISOTime('1100').toObject() //=> { hours: 11, minutes: 0, seconds: 0 }
291 + * @example Duration.fromISOTime('T1100').toObject() //=> { hours: 11, minutes: 0, seconds: 0 }
292 + * @return {Duration}
293 + */
294 + static fromISOTime(text, opts) {
295 + const [parsed] = parseISOTimeOnly(text);
296 + if (parsed) {
297 + const obj = Object.assign(parsed, opts);
298 + return Duration.fromObject(obj);
299 + } else {
300 + return Duration.invalid("unparsable", `the input "${text}" can't be parsed as ISO 8601`);
301 + }
302 + }
303 +
304 + /**
305 + * Create an invalid Duration.
306 + * @param {string} reason - simple string of why this datetime is invalid. Should not contain parameters or anything else data-dependent
307 + * @param {string} [explanation=null] - longer explanation, may include parameters and other useful debugging information
308 + * @return {Duration}
309 + */
310 + static invalid(reason, explanation = null) {
311 + if (!reason) {
312 + throw new InvalidArgumentError("need to specify a reason the Duration is invalid");
313 + }
314 +
315 + const invalid = reason instanceof Invalid ? reason : new Invalid(reason, explanation);
316 +
317 + if (Settings.throwOnInvalid) {
318 + throw new InvalidDurationError(invalid);
319 + } else {
320 + return new Duration({ invalid });
321 + }
322 + }
323 +
324 + /**
325 + * @private
326 + */
327 + static normalizeUnit(unit) {
328 + const normalized = {
329 + year: "years",
330 + years: "years",
331 + quarter: "quarters",
332 + quarters: "quarters",
333 + month: "months",
334 + months: "months",
335 + week: "weeks",
336 + weeks: "weeks",
337 + day: "days",
338 + days: "days",
339 + hour: "hours",
340 + hours: "hours",
341 + minute: "minutes",
342 + minutes: "minutes",
343 + second: "seconds",
344 + seconds: "seconds",
345 + millisecond: "milliseconds",
346 + milliseconds: "milliseconds"
347 + }[unit ? unit.toLowerCase() : unit];
348 +
349 + if (!normalized) throw new InvalidUnitError(unit);
350 +
351 + return normalized;
352 + }
353 +
354 + /**
355 + * Check if an object is a Duration. Works across context boundaries
356 + * @param {object} o
357 + * @return {boolean}
358 + */
359 + static isDuration(o) {
360 + return (o && o.isLuxonDuration) || false;
361 + }
362 +
363 + /**
364 + * Get the locale of a Duration, such 'en-GB'
365 + * @type {string}
366 + */
367 + get locale() {
368 + return this.isValid ? this.loc.locale : null;
369 + }
370 +
371 + /**
372 + * Get the numbering system of a Duration, such 'beng'. The numbering system is used when formatting the Duration
373 + *
374 + * @type {string}
375 + */
376 + get numberingSystem() {
377 + return this.isValid ? this.loc.numberingSystem : null;
378 + }
379 +
380 + /**
381 + * Returns a string representation of this Duration formatted according to the specified format string. You may use these tokens:
382 + * * `S` for milliseconds
383 + * * `s` for seconds
384 + * * `m` for minutes
385 + * * `h` for hours
386 + * * `d` for days
387 + * * `M` for months
388 + * * `y` for years
389 + * Notes:
390 + * * Add padding by repeating the token, e.g. "yy" pads the years to two digits, "hhhh" pads the hours out to four digits
391 + * * The duration will be converted to the set of units in the format string using {@link Duration.shiftTo} and the Durations's conversion accuracy setting.
392 + * @param {string} fmt - the format string
393 + * @param {Object} opts - options
394 + * @param {boolean} [opts.floor=true] - floor numerical values
395 + * @example Duration.fromObject({ years: 1, days: 6, seconds: 2 }).toFormat("y d s") //=> "1 6 2"
396 + * @example Duration.fromObject({ years: 1, days: 6, seconds: 2 }).toFormat("yy dd sss") //=> "01 06 002"
397 + * @example Duration.fromObject({ years: 1, days: 6, seconds: 2 }).toFormat("M S") //=> "12 518402000"
398 + * @return {string}
399 + */
400 + toFormat(fmt, opts = {}) {
401 + // reverse-compat since 1.2; we always round down now, never up, and we do it by default
402 + const fmtOpts = Object.assign({}, opts, {
403 + floor: opts.round !== false && opts.floor !== false
404 + });
405 + return this.isValid
406 + ? Formatter.create(this.loc, fmtOpts).formatDurationFromString(this, fmt)
407 + : INVALID;
408 + }
409 +
410 + /**
411 + * Returns a JavaScript object with this Duration's values.
412 + * @param opts - options for generating the object
413 + * @param {boolean} [opts.includeConfig=false] - include configuration attributes in the output
414 + * @example Duration.fromObject({ years: 1, days: 6, seconds: 2 }).toObject() //=> { years: 1, days: 6, seconds: 2 }
415 + * @return {Object}
416 + */
417 + toObject(opts = {}) {
418 + if (!this.isValid) return {};
419 +
420 + const base = Object.assign({}, this.values);
421 +
422 + if (opts.includeConfig) {
423 + base.conversionAccuracy = this.conversionAccuracy;
424 + base.numberingSystem = this.loc.numberingSystem;
425 + base.locale = this.loc.locale;
426 + }
427 + return base;
428 + }
429 +
430 + /**
431 + * Returns an ISO 8601-compliant string representation of this Duration.
432 + * @see https://en.wikipedia.org/wiki/ISO_8601#Durations
433 + * @example Duration.fromObject({ years: 3, seconds: 45 }).toISO() //=> 'P3YT45S'
434 + * @example Duration.fromObject({ months: 4, seconds: 45 }).toISO() //=> 'P4MT45S'
435 + * @example Duration.fromObject({ months: 5 }).toISO() //=> 'P5M'
436 + * @example Duration.fromObject({ minutes: 5 }).toISO() //=> 'PT5M'
437 + * @example Duration.fromObject({ milliseconds: 6 }).toISO() //=> 'PT0.006S'
438 + * @return {string}
439 + */
440 + toISO() {
441 + // we could use the formatter, but this is an easier way to get the minimum string
442 + if (!this.isValid) return null;
443 +
444 + let s = "P";
445 + if (this.years !== 0) s += this.years + "Y";
446 + if (this.months !== 0 || this.quarters !== 0) s += this.months + this.quarters * 3 + "M";
447 + if (this.weeks !== 0) s += this.weeks + "W";
448 + if (this.days !== 0) s += this.days + "D";
449 + if (this.hours !== 0 || this.minutes !== 0 || this.seconds !== 0 || this.milliseconds !== 0)
450 + s += "T";
451 + if (this.hours !== 0) s += this.hours + "H";
452 + if (this.minutes !== 0) s += this.minutes + "M";
453 + if (this.seconds !== 0 || this.milliseconds !== 0)
454 + // this will handle "floating point madness" by removing extra decimal places
455 + // https://stackoverflow.com/questions/588004/is-floating-point-math-broken
456 + s += roundTo(this.seconds + this.milliseconds / 1000, 3) + "S";
457 + if (s === "P") s += "T0S";
458 + return s;
459 + }
460 +
461 + /**
462 + * Returns an ISO 8601-compliant string representation of this Duration, formatted as a time of day.
463 + * Note that this will return null if the duration is invalid, negative, or equal to or greater than 24 hours.
464 + * @see https://en.wikipedia.org/wiki/ISO_8601#Times
465 + * @param {Object} opts - options
466 + * @param {boolean} [opts.suppressMilliseconds=false] - exclude milliseconds from the format if they're 0
467 + * @param {boolean} [opts.suppressSeconds=false] - exclude seconds from the format if they're 0
468 + * @param {boolean} [opts.includePrefix=false] - include the `T` prefix
469 + * @param {string} [opts.format='extended'] - choose between the basic and extended format
470 + * @example Duration.fromObject({ hours: 11 }).toISOTime() //=> '11:00:00.000'
471 + * @example Duration.fromObject({ hours: 11 }).toISOTime({ suppressMilliseconds: true }) //=> '11:00:00'
472 + * @example Duration.fromObject({ hours: 11 }).toISOTime({ suppressSeconds: true }) //=> '11:00'
473 + * @example Duration.fromObject({ hours: 11 }).toISOTime({ includePrefix: true }) //=> 'T11:00:00.000'
474 + * @example Duration.fromObject({ hours: 11 }).toISOTime({ format: 'basic' }) //=> '110000.000'
475 + * @return {string}
476 + */
477 + toISOTime(opts = {}) {
478 + if (!this.isValid) return null;
479 +
480 + const millis = this.toMillis();
481 + if (millis < 0 || millis >= 86400000) return null;
482 +
483 + opts = Object.assign(
484 + {
485 + suppressMilliseconds: false,
486 + suppressSeconds: false,
487 + includePrefix: false,
488 + format: "extended"
489 + },
490 + opts
491 + );
492 +
493 + const value = this.shiftTo("hours", "minutes", "seconds", "milliseconds");
494 +
495 + let fmt = opts.format === "basic" ? "hhmm" : "hh:mm";
496 +
497 + if (!opts.suppressSeconds || value.seconds !== 0 || value.milliseconds !== 0) {
498 + fmt += opts.format === "basic" ? "ss" : ":ss";
499 + if (!opts.suppressMilliseconds || value.milliseconds !== 0) {
500 + fmt += ".SSS";
501 + }
502 + }
503 +
504 + let str = value.toFormat(fmt);
505 +
506 + if (opts.includePrefix) {
507 + str = "T" + str;
508 + }
509 +
510 + return str;
511 + }
512 +
513 + /**
514 + * Returns an ISO 8601 representation of this Duration appropriate for use in JSON.
515 + * @return {string}
516 + */
517 + toJSON() {
518 + return this.toISO();
519 + }
520 +
521 + /**
522 + * Returns an ISO 8601 representation of this Duration appropriate for use in debugging.
523 + * @return {string}
524 + */
525 + toString() {
526 + return this.toISO();
527 + }
528 +
529 + /**
530 + * Returns an milliseconds value of this Duration.
531 + * @return {number}
532 + */
533 + toMillis() {
534 + return this.as("milliseconds");
535 + }
536 +
537 + /**
538 + * Returns an milliseconds value of this Duration. Alias of {@link toMillis}
539 + * @return {number}
540 + */
541 + valueOf() {
542 + return this.toMillis();
543 + }
544 +
545 + /**
546 + * Make this Duration longer by the specified amount. Return a newly-constructed Duration.
547 + * @param {Duration|Object|number} duration - The amount to add. Either a Luxon Duration, a number of milliseconds, the object argument to Duration.fromObject()
548 + * @return {Duration}
549 + */
550 + plus(duration) {
551 + if (!this.isValid) return this;
552 +
553 + const dur = friendlyDuration(duration),
554 + result = {};
555 +
556 + for (const k of orderedUnits) {
557 + if (hasOwnProperty(dur.values, k) || hasOwnProperty(this.values, k)) {
558 + result[k] = dur.get(k) + this.get(k);
559 + }
560 + }
561 +
562 + return clone(this, { values: result }, true);
563 + }
564 +
565 + /**
566 + * Make this Duration shorter by the specified amount. Return a newly-constructed Duration.
567 + * @param {Duration|Object|number} duration - The amount to subtract. Either a Luxon Duration, a number of milliseconds, the object argument to Duration.fromObject()
568 + * @return {Duration}
569 + */
570 + minus(duration) {
571 + if (!this.isValid) return this;
572 +
573 + const dur = friendlyDuration(duration);
574 + return this.plus(dur.negate());
575 + }
576 +
577 + /**
578 + * Scale this Duration by the specified amount. Return a newly-constructed Duration.
579 + * @param {function} fn - The function to apply to each unit. Arity is 1 or 2: the value of the unit and, optionally, the unit name. Must return a number.
580 + * @example Duration.fromObject({ hours: 1, minutes: 30 }).mapUnit(x => x * 2) //=> { hours: 2, minutes: 60 }
581 + * @example Duration.fromObject({ hours: 1, minutes: 30 }).mapUnit((x, u) => u === "hour" ? x * 2 : x) //=> { hours: 2, minutes: 30 }
582 + * @return {Duration}
583 + */
584 + mapUnits(fn) {
585 + if (!this.isValid) return this;
586 + const result = {};
587 + for (const k of Object.keys(this.values)) {
588 + result[k] = asNumber(fn(this.values[k], k));
589 + }
590 + return clone(this, { values: result }, true);
591 + }
592 +
593 + /**
594 + * Get the value of unit.
595 + * @param {string} unit - a unit such as 'minute' or 'day'
596 + * @example Duration.fromObject({years: 2, days: 3}).get('years') //=> 2
597 + * @example Duration.fromObject({years: 2, days: 3}).get('months') //=> 0
598 + * @example Duration.fromObject({years: 2, days: 3}).get('days') //=> 3
599 + * @return {number}
600 + */
601 + get(unit) {
602 + return this[Duration.normalizeUnit(unit)];
603 + }
604 +
605 + /**
606 + * "Set" the values of specified units. Return a newly-constructed Duration.
607 + * @param {Object} values - a mapping of units to numbers
608 + * @example dur.set({ years: 2017 })
609 + * @example dur.set({ hours: 8, minutes: 30 })
610 + * @return {Duration}
611 + */
612 + set(values) {
613 + if (!this.isValid) return this;
614 +
615 + const mixed = Object.assign(this.values, normalizeObject(values, Duration.normalizeUnit, []));
616 + return clone(this, { values: mixed });
617 + }
618 +
619 + /**
620 + * "Set" the locale and/or numberingSystem. Returns a newly-constructed Duration.
621 + * @example dur.reconfigure({ locale: 'en-GB' })
622 + * @return {Duration}
623 + */
624 + reconfigure({ locale, numberingSystem, conversionAccuracy } = {}) {
625 + const loc = this.loc.clone({ locale, numberingSystem }),
626 + opts = { loc };
627 +
628 + if (conversionAccuracy) {
629 + opts.conversionAccuracy = conversionAccuracy;
630 + }
631 +
632 + return clone(this, opts);
633 + }
634 +
635 + /**
636 + * Return the length of the duration in the specified unit.
637 + * @param {string} unit - a unit such as 'minutes' or 'days'
638 + * @example Duration.fromObject({years: 1}).as('days') //=> 365
639 + * @example Duration.fromObject({years: 1}).as('months') //=> 12
640 + * @example Duration.fromObject({hours: 60}).as('days') //=> 2.5
641 + * @return {number}
642 + */
643 + as(unit) {
644 + return this.isValid ? this.shiftTo(unit).get(unit) : NaN;
645 + }
646 +
647 + /**
648 + * Reduce this Duration to its canonical representation in its current units.
649 + * @example Duration.fromObject({ years: 2, days: 5000 }).normalize().toObject() //=> { years: 15, days: 255 }
650 + * @example Duration.fromObject({ hours: 12, minutes: -45 }).normalize().toObject() //=> { hours: 11, minutes: 15 }
651 + * @return {Duration}
652 + */
653 + normalize() {
654 + if (!this.isValid) return this;
655 + const vals = this.toObject();
656 + normalizeValues(this.matrix, vals);
657 + return clone(this, { values: vals }, true);
658 + }
659 +
660 + /**
661 + * Convert this Duration into its representation in a different set of units.
662 + * @example Duration.fromObject({ hours: 1, seconds: 30 }).shiftTo('minutes', 'milliseconds').toObject() //=> { minutes: 60, milliseconds: 30000 }
663 + * @return {Duration}
664 + */
665 + shiftTo(...units) {
666 + if (!this.isValid) return this;
667 +
668 + if (units.length === 0) {
669 + return this;
670 + }
671 +
672 + units = units.map(u => Duration.normalizeUnit(u));
673 +
674 + const built = {},
675 + accumulated = {},
676 + vals = this.toObject();
677 + let lastUnit;
678 +
679 + for (const k of orderedUnits) {
680 + if (units.indexOf(k) >= 0) {
681 + lastUnit = k;
682 +
683 + let own = 0;
684 +
685 + // anything we haven't boiled down yet should get boiled to this unit
686 + for (const ak in accumulated) {
687 + own += this.matrix[ak][k] * accumulated[ak];
688 + accumulated[ak] = 0;
689 + }
690 +
691 + // plus anything that's already in this unit
692 + if (isNumber(vals[k])) {
693 + own += vals[k];
694 + }
695 +
696 + const i = Math.trunc(own);
697 + built[k] = i;
698 + accumulated[k] = own - i; // we'd like to absorb these fractions in another unit
699 +
700 + // plus anything further down the chain that should be rolled up in to this
701 + for (const down in vals) {
702 + if (orderedUnits.indexOf(down) > orderedUnits.indexOf(k)) {
703 + convert(this.matrix, vals, down, built, k);
704 + }
705 + }
706 + // otherwise, keep it in the wings to boil it later
707 + } else if (isNumber(vals[k])) {
708 + accumulated[k] = vals[k];
709 + }
710 + }
711 +
712 + // anything leftover becomes the decimal for the last unit
713 + // lastUnit must be defined since units is not empty
714 + for (const key in accumulated) {
715 + if (accumulated[key] !== 0) {
716 + built[lastUnit] +=
717 + key === lastUnit ? accumulated[key] : accumulated[key] / this.matrix[lastUnit][key];
718 + }
719 + }
720 +
721 + return clone(this, { values: built }, true).normalize();
722 + }
723 +
724 + /**
725 + * Return the negative of this Duration.
726 + * @example Duration.fromObject({ hours: 1, seconds: 30 }).negate().toObject() //=> { hours: -1, seconds: -30 }
727 + * @return {Duration}
728 + */
729 + negate() {
730 + if (!this.isValid) return this;
731 + const negated = {};
732 + for (const k of Object.keys(this.values)) {
733 + negated[k] = -this.values[k];
734 + }
735 + return clone(this, { values: negated }, true);
736 + }
737 +
738 + /**
739 + * Get the years.
740 + * @type {number}
741 + */
742 + get years() {
743 + return this.isValid ? this.values.years || 0 : NaN;
744 + }
745 +
746 + /**
747 + * Get the quarters.
748 + * @type {number}
749 + */
750 + get quarters() {
751 + return this.isValid ? this.values.quarters || 0 : NaN;
752 + }
753 +
754 + /**
755 + * Get the months.
756 + * @type {number}
757 + */
758 + get months() {
759 + return this.isValid ? this.values.months || 0 : NaN;
760 + }
761 +
762 + /**
763 + * Get the weeks
764 + * @type {number}
765 + */
766 + get weeks() {
767 + return this.isValid ? this.values.weeks || 0 : NaN;
768 + }
769 +
770 + /**
771 + * Get the days.
772 + * @type {number}
773 + */
774 + get days() {
775 + return this.isValid ? this.values.days || 0 : NaN;
776 + }
777 +
778 + /**
779 + * Get the hours.
780 + * @type {number}
781 + */
782 + get hours() {
783 + return this.isValid ? this.values.hours || 0 : NaN;
784 + }
785 +
786 + /**
787 + * Get the minutes.
788 + * @type {number}
789 + */
790 + get minutes() {
791 + return this.isValid ? this.values.minutes || 0 : NaN;
792 + }
793 +
794 + /**
795 + * Get the seconds.
796 + * @return {number}
797 + */
798 + get seconds() {
799 + return this.isValid ? this.values.seconds || 0 : NaN;
800 + }
801 +
802 + /**
803 + * Get the milliseconds.
804 + * @return {number}
805 + */
806 + get milliseconds() {
807 + return this.isValid ? this.values.milliseconds || 0 : NaN;
808 + }
809 +
810 + /**
811 + * Returns whether the Duration is invalid. Invalid durations are returned by diff operations
812 + * on invalid DateTimes or Intervals.
813 + * @return {boolean}
814 + */
815 + get isValid() {
816 + return this.invalid === null;
817 + }
818 +
819 + /**
820 + * Returns an error code if this Duration became invalid, or null if the Duration is valid
821 + * @return {string}
822 + */
823 + get invalidReason() {
824 + return this.invalid ? this.invalid.reason : null;
825 + }
826 +
827 + /**
828 + * Returns an explanation of why this Duration became invalid, or null if the Duration is valid
829 + * @type {string}
830 + */
831 + get invalidExplanation() {
832 + return this.invalid ? this.invalid.explanation : null;
833 + }
834 +
835 + /**
836 + * Equality check
837 + * Two Durations are equal iff they have the same units and the same values for each unit.
838 + * @param {Duration} other
839 + * @return {boolean}
840 + */
841 + equals(other) {
842 + if (!this.isValid || !other.isValid) {
843 + return false;
844 + }
845 +
846 + if (!this.loc.equals(other.loc)) {
847 + return false;
848 + }
849 +
850 + function eq(v1, v2) {
851 + // Consider 0 and undefined as equal
852 + if (v1 === undefined || v1 === 0) return v2 === undefined || v2 === 0;
853 + return v1 === v2;
854 + }
855 +
856 + for (const u of orderedUnits) {
857 + if (!eq(this.values[u], other.values[u])) {
858 + return false;
859 + }
860 + }
861 + return true;
862 + }
863 +}
864 +
865 +/**
866 + * @private
867 + */
868 +export function friendlyDuration(durationish) {
869 + if (isNumber(durationish)) {
870 + return Duration.fromMillis(durationish);
871 + } else if (Duration.isDuration(durationish)) {
872 + return durationish;
873 + } else if (typeof durationish === "object") {
874 + return Duration.fromObject(durationish);
875 + } else {
876 + throw new InvalidArgumentError(
877 + `Unknown duration argument ${durationish} of type ${typeof durationish}`
878 + );
879 + }
880 +}
1 +// these aren't really private, but nor are they really useful to document
2 +
3 +/**
4 + * @private
5 + */
6 +class LuxonError extends Error {}
7 +
8 +/**
9 + * @private
10 + */
11 +export class InvalidDateTimeError extends LuxonError {
12 + constructor(reason) {
13 + super(`Invalid DateTime: ${reason.toMessage()}`);
14 + }
15 +}
16 +
17 +/**
18 + * @private
19 + */
20 +export class InvalidIntervalError extends LuxonError {
21 + constructor(reason) {
22 + super(`Invalid Interval: ${reason.toMessage()}`);
23 + }
24 +}
25 +
26 +/**
27 + * @private
28 + */
29 +export class InvalidDurationError extends LuxonError {
30 + constructor(reason) {
31 + super(`Invalid Duration: ${reason.toMessage()}`);
32 + }
33 +}
34 +
35 +/**
36 + * @private
37 + */
38 +export class ConflictingSpecificationError extends LuxonError {}
39 +
40 +/**
41 + * @private
42 + */
43 +export class InvalidUnitError extends LuxonError {
44 + constructor(unit) {
45 + super(`Invalid unit ${unit}`);
46 + }
47 +}
48 +
49 +/**
50 + * @private
51 + */
52 +export class InvalidArgumentError extends LuxonError {}
53 +
54 +/**
55 + * @private
56 + */
57 +export class ZoneIsAbstractError extends LuxonError {
58 + constructor() {
59 + super("Zone is an abstract class");
60 + }
61 +}
1 +import {
2 + integerBetween,
3 + isLeapYear,
4 + timeObject,
5 + daysInYear,
6 + daysInMonth,
7 + weeksInWeekYear,
8 + isInteger
9 +} from "./util.js";
10 +import Invalid from "./invalid.js";
11 +
12 +const nonLeapLadder = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334],
13 + leapLadder = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335];
14 +
15 +function unitOutOfRange(unit, value) {
16 + return new Invalid(
17 + "unit out of range",
18 + `you specified ${value} (of type ${typeof value}) as a ${unit}, which is invalid`
19 + );
20 +}
21 +
22 +function dayOfWeek(year, month, day) {
23 + const js = new Date(Date.UTC(year, month - 1, day)).getUTCDay();
24 + return js === 0 ? 7 : js;
25 +}
26 +
27 +function computeOrdinal(year, month, day) {
28 + return day + (isLeapYear(year) ? leapLadder : nonLeapLadder)[month - 1];
29 +}
30 +
31 +function uncomputeOrdinal(year, ordinal) {
32 + const table = isLeapYear(year) ? leapLadder : nonLeapLadder,
33 + month0 = table.findIndex(i => i < ordinal),
34 + day = ordinal - table[month0];
35 + return { month: month0 + 1, day };
36 +}
37 +
38 +/**
39 + * @private
40 + */
41 +
42 +export function gregorianToWeek(gregObj) {
43 + const { year, month, day } = gregObj,
44 + ordinal = computeOrdinal(year, month, day),
45 + weekday = dayOfWeek(year, month, day);
46 +
47 + let weekNumber = Math.floor((ordinal - weekday + 10) / 7),
48 + weekYear;
49 +
50 + if (weekNumber < 1) {
51 + weekYear = year - 1;
52 + weekNumber = weeksInWeekYear(weekYear);
53 + } else if (weekNumber > weeksInWeekYear(year)) {
54 + weekYear = year + 1;
55 + weekNumber = 1;
56 + } else {
57 + weekYear = year;
58 + }
59 +
60 + return Object.assign({ weekYear, weekNumber, weekday }, timeObject(gregObj));
61 +}
62 +
63 +export function weekToGregorian(weekData) {
64 + const { weekYear, weekNumber, weekday } = weekData,
65 + weekdayOfJan4 = dayOfWeek(weekYear, 1, 4),
66 + yearInDays = daysInYear(weekYear);
67 +
68 + let ordinal = weekNumber * 7 + weekday - weekdayOfJan4 - 3,
69 + year;
70 +
71 + if (ordinal < 1) {
72 + year = weekYear - 1;
73 + ordinal += daysInYear(year);
74 + } else if (ordinal > yearInDays) {
75 + year = weekYear + 1;
76 + ordinal -= daysInYear(weekYear);
77 + } else {
78 + year = weekYear;
79 + }
80 +
81 + const { month, day } = uncomputeOrdinal(year, ordinal);
82 +
83 + return Object.assign({ year, month, day }, timeObject(weekData));
84 +}
85 +
86 +export function gregorianToOrdinal(gregData) {
87 + const { year, month, day } = gregData,
88 + ordinal = computeOrdinal(year, month, day);
89 +
90 + return Object.assign({ year, ordinal }, timeObject(gregData));
91 +}
92 +
93 +export function ordinalToGregorian(ordinalData) {
94 + const { year, ordinal } = ordinalData,
95 + { month, day } = uncomputeOrdinal(year, ordinal);
96 +
97 + return Object.assign({ year, month, day }, timeObject(ordinalData));
98 +}
99 +
100 +export function hasInvalidWeekData(obj) {
101 + const validYear = isInteger(obj.weekYear),
102 + validWeek = integerBetween(obj.weekNumber, 1, weeksInWeekYear(obj.weekYear)),
103 + validWeekday = integerBetween(obj.weekday, 1, 7);
104 +
105 + if (!validYear) {
106 + return unitOutOfRange("weekYear", obj.weekYear);
107 + } else if (!validWeek) {
108 + return unitOutOfRange("week", obj.week);
109 + } else if (!validWeekday) {
110 + return unitOutOfRange("weekday", obj.weekday);
111 + } else return false;
112 +}
113 +
114 +export function hasInvalidOrdinalData(obj) {
115 + const validYear = isInteger(obj.year),
116 + validOrdinal = integerBetween(obj.ordinal, 1, daysInYear(obj.year));
117 +
118 + if (!validYear) {
119 + return unitOutOfRange("year", obj.year);
120 + } else if (!validOrdinal) {
121 + return unitOutOfRange("ordinal", obj.ordinal);
122 + } else return false;
123 +}
124 +
125 +export function hasInvalidGregorianData(obj) {
126 + const validYear = isInteger(obj.year),
127 + validMonth = integerBetween(obj.month, 1, 12),
128 + validDay = integerBetween(obj.day, 1, daysInMonth(obj.year, obj.month));
129 +
130 + if (!validYear) {
131 + return unitOutOfRange("year", obj.year);
132 + } else if (!validMonth) {
133 + return unitOutOfRange("month", obj.month);
134 + } else if (!validDay) {
135 + return unitOutOfRange("day", obj.day);
136 + } else return false;
137 +}
138 +
139 +export function hasInvalidTimeData(obj) {
140 + const { hour, minute, second, millisecond } = obj;
141 + const validHour =
142 + integerBetween(hour, 0, 23) ||
143 + (hour === 24 && minute === 0 && second === 0 && millisecond === 0),
144 + validMinute = integerBetween(minute, 0, 59),
145 + validSecond = integerBetween(second, 0, 59),
146 + validMillisecond = integerBetween(millisecond, 0, 999);
147 +
148 + if (!validHour) {
149 + return unitOutOfRange("hour", hour);
150 + } else if (!validMinute) {
151 + return unitOutOfRange("minute", minute);
152 + } else if (!validSecond) {
153 + return unitOutOfRange("second", second);
154 + } else if (!validMillisecond) {
155 + return unitOutOfRange("millisecond", millisecond);
156 + } else return false;
157 +}
1 +import Duration from "../duration.js";
2 +
3 +function dayDiff(earlier, later) {
4 + const utcDayStart = dt =>
5 + dt
6 + .toUTC(0, { keepLocalTime: true })
7 + .startOf("day")
8 + .valueOf(),
9 + ms = utcDayStart(later) - utcDayStart(earlier);
10 + return Math.floor(Duration.fromMillis(ms).as("days"));
11 +}
12 +
13 +function highOrderDiffs(cursor, later, units) {
14 + const differs = [
15 + ["years", (a, b) => b.year - a.year],
16 + ["quarters", (a, b) => b.quarter - a.quarter],
17 + ["months", (a, b) => b.month - a.month + (b.year - a.year) * 12],
18 + [
19 + "weeks",
20 + (a, b) => {
21 + const days = dayDiff(a, b);
22 + return (days - (days % 7)) / 7;
23 + }
24 + ],
25 + ["days", dayDiff]
26 + ];
27 +
28 + const results = {};
29 + let lowestOrder, highWater;
30 +
31 + for (const [unit, differ] of differs) {
32 + if (units.indexOf(unit) >= 0) {
33 + lowestOrder = unit;
34 +
35 + let delta = differ(cursor, later);
36 + highWater = cursor.plus({ [unit]: delta });
37 +
38 + if (highWater > later) {
39 + cursor = cursor.plus({ [unit]: delta - 1 });
40 + delta -= 1;
41 + } else {
42 + cursor = highWater;
43 + }
44 +
45 + results[unit] = delta;
46 + }
47 + }
48 +
49 + return [cursor, results, highWater, lowestOrder];
50 +}
51 +
52 +export default function(earlier, later, units, opts) {
53 + let [cursor, results, highWater, lowestOrder] = highOrderDiffs(earlier, later, units);
54 +
55 + const remainingMillis = later - cursor;
56 +
57 + const lowerOrderUnits = units.filter(
58 + u => ["hours", "minutes", "seconds", "milliseconds"].indexOf(u) >= 0
59 + );
60 +
61 + if (lowerOrderUnits.length === 0) {
62 + if (highWater < later) {
63 + highWater = cursor.plus({ [lowestOrder]: 1 });
64 + }
65 +
66 + if (highWater !== cursor) {
67 + results[lowestOrder] = (results[lowestOrder] || 0) + remainingMillis / (highWater - cursor);
68 + }
69 + }
70 +
71 + const duration = Duration.fromObject(Object.assign(results, opts));
72 +
73 + if (lowerOrderUnits.length > 0) {
74 + return Duration.fromMillis(remainingMillis, opts)
75 + .shiftTo(...lowerOrderUnits)
76 + .plus(duration);
77 + } else {
78 + return duration;
79 + }
80 +}
1 +const numberingSystems = {
2 + arab: "[\u0660-\u0669]",
3 + arabext: "[\u06F0-\u06F9]",
4 + bali: "[\u1B50-\u1B59]",
5 + beng: "[\u09E6-\u09EF]",
6 + deva: "[\u0966-\u096F]",
7 + fullwide: "[\uFF10-\uFF19]",
8 + gujr: "[\u0AE6-\u0AEF]",
9 + hanidec: "[〇|一|二|三|四|五|六|七|八|九]",
10 + khmr: "[\u17E0-\u17E9]",
11 + knda: "[\u0CE6-\u0CEF]",
12 + laoo: "[\u0ED0-\u0ED9]",
13 + limb: "[\u1946-\u194F]",
14 + mlym: "[\u0D66-\u0D6F]",
15 + mong: "[\u1810-\u1819]",
16 + mymr: "[\u1040-\u1049]",
17 + orya: "[\u0B66-\u0B6F]",
18 + tamldec: "[\u0BE6-\u0BEF]",
19 + telu: "[\u0C66-\u0C6F]",
20 + thai: "[\u0E50-\u0E59]",
21 + tibt: "[\u0F20-\u0F29]",
22 + latn: "\\d"
23 +};
24 +
25 +const numberingSystemsUTF16 = {
26 + arab: [1632, 1641],
27 + arabext: [1776, 1785],
28 + bali: [6992, 7001],
29 + beng: [2534, 2543],
30 + deva: [2406, 2415],
31 + fullwide: [65296, 65303],
32 + gujr: [2790, 2799],
33 + khmr: [6112, 6121],
34 + knda: [3302, 3311],
35 + laoo: [3792, 3801],
36 + limb: [6470, 6479],
37 + mlym: [3430, 3439],
38 + mong: [6160, 6169],
39 + mymr: [4160, 4169],
40 + orya: [2918, 2927],
41 + tamldec: [3046, 3055],
42 + telu: [3174, 3183],
43 + thai: [3664, 3673],
44 + tibt: [3872, 3881]
45 +};
46 +
47 +// eslint-disable-next-line
48 +const hanidecChars = numberingSystems.hanidec.replace(/[\[|\]]/g, "").split("");
49 +
50 +export function parseDigits(str) {
51 + let value = parseInt(str, 10);
52 + if (isNaN(value)) {
53 + value = "";
54 + for (let i = 0; i < str.length; i++) {
55 + const code = str.charCodeAt(i);
56 +
57 + if (str[i].search(numberingSystems.hanidec) !== -1) {
58 + value += hanidecChars.indexOf(str[i]);
59 + } else {
60 + for (const key in numberingSystemsUTF16) {
61 + const [min, max] = numberingSystemsUTF16[key];
62 + if (code >= min && code <= max) {
63 + value += code - min;
64 + }
65 + }
66 + }
67 + }
68 + return parseInt(value, 10);
69 + } else {
70 + return value;
71 + }
72 +}
73 +
74 +export function digitRegex({ numberingSystem }, append = "") {
75 + return new RegExp(`${numberingSystems[numberingSystem || "latn"]}${append}`);
76 +}
1 +import * as Formats from "./formats.js";
2 +import { pick } from "./util.js";
3 +
4 +function stringify(obj) {
5 + return JSON.stringify(obj, Object.keys(obj).sort());
6 +}
7 +
8 +/**
9 + * @private
10 + */
11 +
12 +export const monthsLong = [
13 + "January",
14 + "February",
15 + "March",
16 + "April",
17 + "May",
18 + "June",
19 + "July",
20 + "August",
21 + "September",
22 + "October",
23 + "November",
24 + "December"
25 +];
26 +
27 +export const monthsShort = [
28 + "Jan",
29 + "Feb",
30 + "Mar",
31 + "Apr",
32 + "May",
33 + "Jun",
34 + "Jul",
35 + "Aug",
36 + "Sep",
37 + "Oct",
38 + "Nov",
39 + "Dec"
40 +];
41 +
42 +export const monthsNarrow = ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"];
43 +
44 +export function months(length) {
45 + switch (length) {
46 + case "narrow":
47 + return [...monthsNarrow];
48 + case "short":
49 + return [...monthsShort];
50 + case "long":
51 + return [...monthsLong];
52 + case "numeric":
53 + return ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"];
54 + case "2-digit":
55 + return ["01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"];
56 + default:
57 + return null;
58 + }
59 +}
60 +
61 +export const weekdaysLong = [
62 + "Monday",
63 + "Tuesday",
64 + "Wednesday",
65 + "Thursday",
66 + "Friday",
67 + "Saturday",
68 + "Sunday"
69 +];
70 +
71 +export const weekdaysShort = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
72 +
73 +export const weekdaysNarrow = ["M", "T", "W", "T", "F", "S", "S"];
74 +
75 +export function weekdays(length) {
76 + switch (length) {
77 + case "narrow":
78 + return [...weekdaysNarrow];
79 + case "short":
80 + return [...weekdaysShort];
81 + case "long":
82 + return [...weekdaysLong];
83 + case "numeric":
84 + return ["1", "2", "3", "4", "5", "6", "7"];
85 + default:
86 + return null;
87 + }
88 +}
89 +
90 +export const meridiems = ["AM", "PM"];
91 +
92 +export const erasLong = ["Before Christ", "Anno Domini"];
93 +
94 +export const erasShort = ["BC", "AD"];
95 +
96 +export const erasNarrow = ["B", "A"];
97 +
98 +export function eras(length) {
99 + switch (length) {
100 + case "narrow":
101 + return [...erasNarrow];
102 + case "short":
103 + return [...erasShort];
104 + case "long":
105 + return [...erasLong];
106 + default:
107 + return null;
108 + }
109 +}
110 +
111 +export function meridiemForDateTime(dt) {
112 + return meridiems[dt.hour < 12 ? 0 : 1];
113 +}
114 +
115 +export function weekdayForDateTime(dt, length) {
116 + return weekdays(length)[dt.weekday - 1];
117 +}
118 +
119 +export function monthForDateTime(dt, length) {
120 + return months(length)[dt.month - 1];
121 +}
122 +
123 +export function eraForDateTime(dt, length) {
124 + return eras(length)[dt.year < 0 ? 0 : 1];
125 +}
126 +
127 +export function formatRelativeTime(unit, count, numeric = "always", narrow = false) {
128 + const units = {
129 + years: ["year", "yr."],
130 + quarters: ["quarter", "qtr."],
131 + months: ["month", "mo."],
132 + weeks: ["week", "wk."],
133 + days: ["day", "day", "days"],
134 + hours: ["hour", "hr."],
135 + minutes: ["minute", "min."],
136 + seconds: ["second", "sec."]
137 + };
138 +
139 + const lastable = ["hours", "minutes", "seconds"].indexOf(unit) === -1;
140 +
141 + if (numeric === "auto" && lastable) {
142 + const isDay = unit === "days";
143 + switch (count) {
144 + case 1:
145 + return isDay ? "tomorrow" : `next ${units[unit][0]}`;
146 + case -1:
147 + return isDay ? "yesterday" : `last ${units[unit][0]}`;
148 + case 0:
149 + return isDay ? "today" : `this ${units[unit][0]}`;
150 + default: // fall through
151 + }
152 + }
153 +
154 + const isInPast = Object.is(count, -0) || count < 0,
155 + fmtValue = Math.abs(count),
156 + singular = fmtValue === 1,
157 + lilUnits = units[unit],
158 + fmtUnit = narrow
159 + ? singular
160 + ? lilUnits[1]
161 + : lilUnits[2] || lilUnits[1]
162 + : singular
163 + ? units[unit][0]
164 + : unit;
165 + return isInPast ? `${fmtValue} ${fmtUnit} ago` : `in ${fmtValue} ${fmtUnit}`;
166 +}
167 +
168 +export function formatString(knownFormat) {
169 + // these all have the offsets removed because we don't have access to them
170 + // without all the intl stuff this is backfilling
171 + const filtered = pick(knownFormat, [
172 + "weekday",
173 + "era",
174 + "year",
175 + "month",
176 + "day",
177 + "hour",
178 + "minute",
179 + "second",
180 + "timeZoneName",
181 + "hour12"
182 + ]),
183 + key = stringify(filtered),
184 + dateTimeHuge = "EEEE, LLLL d, yyyy, h:mm a";
185 + switch (key) {
186 + case stringify(Formats.DATE_SHORT):
187 + return "M/d/yyyy";
188 + case stringify(Formats.DATE_MED):
189 + return "LLL d, yyyy";
190 + case stringify(Formats.DATE_MED_WITH_WEEKDAY):
191 + return "EEE, LLL d, yyyy";
192 + case stringify(Formats.DATE_FULL):
193 + return "LLLL d, yyyy";
194 + case stringify(Formats.DATE_HUGE):
195 + return "EEEE, LLLL d, yyyy";
196 + case stringify(Formats.TIME_SIMPLE):
197 + return "h:mm a";
198 + case stringify(Formats.TIME_WITH_SECONDS):
199 + return "h:mm:ss a";
200 + case stringify(Formats.TIME_WITH_SHORT_OFFSET):
201 + return "h:mm a";
202 + case stringify(Formats.TIME_WITH_LONG_OFFSET):
203 + return "h:mm a";
204 + case stringify(Formats.TIME_24_SIMPLE):
205 + return "HH:mm";
206 + case stringify(Formats.TIME_24_WITH_SECONDS):
207 + return "HH:mm:ss";
208 + case stringify(Formats.TIME_24_WITH_SHORT_OFFSET):
209 + return "HH:mm";
210 + case stringify(Formats.TIME_24_WITH_LONG_OFFSET):
211 + return "HH:mm";
212 + case stringify(Formats.DATETIME_SHORT):
213 + return "M/d/yyyy, h:mm a";
214 + case stringify(Formats.DATETIME_MED):
215 + return "LLL d, yyyy, h:mm a";
216 + case stringify(Formats.DATETIME_FULL):
217 + return "LLLL d, yyyy, h:mm a";
218 + case stringify(Formats.DATETIME_HUGE):
219 + return dateTimeHuge;
220 + case stringify(Formats.DATETIME_SHORT_WITH_SECONDS):
221 + return "M/d/yyyy, h:mm:ss a";
222 + case stringify(Formats.DATETIME_MED_WITH_SECONDS):
223 + return "LLL d, yyyy, h:mm:ss a";
224 + case stringify(Formats.DATETIME_MED_WITH_WEEKDAY):
225 + return "EEE, d LLL yyyy, h:mm a";
226 + case stringify(Formats.DATETIME_FULL_WITH_SECONDS):
227 + return "LLLL d, yyyy, h:mm:ss a";
228 + case stringify(Formats.DATETIME_HUGE_WITH_SECONDS):
229 + return "EEEE, LLLL d, yyyy, h:mm:ss a";
230 + default:
231 + return dateTimeHuge;
232 + }
233 +}
1 +/**
2 + * @private
3 + */
4 +
5 +const n = "numeric",
6 + s = "short",
7 + l = "long";
8 +
9 +export const DATE_SHORT = {
10 + year: n,
11 + month: n,
12 + day: n
13 +};
14 +
15 +export const DATE_MED = {
16 + year: n,
17 + month: s,
18 + day: n
19 +};
20 +
21 +export const DATE_MED_WITH_WEEKDAY = {
22 + year: n,
23 + month: s,
24 + day: n,
25 + weekday: s
26 +};
27 +
28 +export const DATE_FULL = {
29 + year: n,
30 + month: l,
31 + day: n
32 +};
33 +
34 +export const DATE_HUGE = {
35 + year: n,
36 + month: l,
37 + day: n,
38 + weekday: l
39 +};
40 +
41 +export const TIME_SIMPLE = {
42 + hour: n,
43 + minute: n
44 +};
45 +
46 +export const TIME_WITH_SECONDS = {
47 + hour: n,
48 + minute: n,
49 + second: n
50 +};
51 +
52 +export const TIME_WITH_SHORT_OFFSET = {
53 + hour: n,
54 + minute: n,
55 + second: n,
56 + timeZoneName: s
57 +};
58 +
59 +export const TIME_WITH_LONG_OFFSET = {
60 + hour: n,
61 + minute: n,
62 + second: n,
63 + timeZoneName: l
64 +};
65 +
66 +export const TIME_24_SIMPLE = {
67 + hour: n,
68 + minute: n,
69 + hour12: false
70 +};
71 +
72 +/**
73 + * {@link toLocaleString}; format like '09:30:23', always 24-hour.
74 + */
75 +export const TIME_24_WITH_SECONDS = {
76 + hour: n,
77 + minute: n,
78 + second: n,
79 + hour12: false
80 +};
81 +
82 +/**
83 + * {@link toLocaleString}; format like '09:30:23 EDT', always 24-hour.
84 + */
85 +export const TIME_24_WITH_SHORT_OFFSET = {
86 + hour: n,
87 + minute: n,
88 + second: n,
89 + hour12: false,
90 + timeZoneName: s
91 +};
92 +
93 +/**
94 + * {@link toLocaleString}; format like '09:30:23 Eastern Daylight Time', always 24-hour.
95 + */
96 +export const TIME_24_WITH_LONG_OFFSET = {
97 + hour: n,
98 + minute: n,
99 + second: n,
100 + hour12: false,
101 + timeZoneName: l
102 +};
103 +
104 +/**
105 + * {@link toLocaleString}; format like '10/14/1983, 9:30 AM'. Only 12-hour if the locale is.
106 + */
107 +export const DATETIME_SHORT = {
108 + year: n,
109 + month: n,
110 + day: n,
111 + hour: n,
112 + minute: n
113 +};
114 +
115 +/**
116 + * {@link toLocaleString}; format like '10/14/1983, 9:30:33 AM'. Only 12-hour if the locale is.
117 + */
118 +export const DATETIME_SHORT_WITH_SECONDS = {
119 + year: n,
120 + month: n,
121 + day: n,
122 + hour: n,
123 + minute: n,
124 + second: n
125 +};
126 +
127 +export const DATETIME_MED = {
128 + year: n,
129 + month: s,
130 + day: n,
131 + hour: n,
132 + minute: n
133 +};
134 +
135 +export const DATETIME_MED_WITH_SECONDS = {
136 + year: n,
137 + month: s,
138 + day: n,
139 + hour: n,
140 + minute: n,
141 + second: n
142 +};
143 +
144 +export const DATETIME_MED_WITH_WEEKDAY = {
145 + year: n,
146 + month: s,
147 + day: n,
148 + weekday: s,
149 + hour: n,
150 + minute: n
151 +};
152 +
153 +export const DATETIME_FULL = {
154 + year: n,
155 + month: l,
156 + day: n,
157 + hour: n,
158 + minute: n,
159 + timeZoneName: s
160 +};
161 +
162 +export const DATETIME_FULL_WITH_SECONDS = {
163 + year: n,
164 + month: l,
165 + day: n,
166 + hour: n,
167 + minute: n,
168 + second: n,
169 + timeZoneName: s
170 +};
171 +
172 +export const DATETIME_HUGE = {
173 + year: n,
174 + month: l,
175 + day: n,
176 + weekday: l,
177 + hour: n,
178 + minute: n,
179 + timeZoneName: l
180 +};
181 +
182 +export const DATETIME_HUGE_WITH_SECONDS = {
183 + year: n,
184 + month: l,
185 + day: n,
186 + weekday: l,
187 + hour: n,
188 + minute: n,
189 + second: n,
190 + timeZoneName: l
191 +};
1 +import * as English from "./english.js";
2 +import * as Formats from "./formats.js";
3 +import { hasFormatToParts, padStart } from "./util.js";
4 +
5 +function stringifyTokens(splits, tokenToString) {
6 + let s = "";
7 + for (const token of splits) {
8 + if (token.literal) {
9 + s += token.val;
10 + } else {
11 + s += tokenToString(token.val);
12 + }
13 + }
14 + return s;
15 +}
16 +
17 +const macroTokenToFormatOpts = {
18 + D: Formats.DATE_SHORT,
19 + DD: Formats.DATE_MED,
20 + DDD: Formats.DATE_FULL,
21 + DDDD: Formats.DATE_HUGE,
22 + t: Formats.TIME_SIMPLE,
23 + tt: Formats.TIME_WITH_SECONDS,
24 + ttt: Formats.TIME_WITH_SHORT_OFFSET,
25 + tttt: Formats.TIME_WITH_LONG_OFFSET,
26 + T: Formats.TIME_24_SIMPLE,
27 + TT: Formats.TIME_24_WITH_SECONDS,
28 + TTT: Formats.TIME_24_WITH_SHORT_OFFSET,
29 + TTTT: Formats.TIME_24_WITH_LONG_OFFSET,
30 + f: Formats.DATETIME_SHORT,
31 + ff: Formats.DATETIME_MED,
32 + fff: Formats.DATETIME_FULL,
33 + ffff: Formats.DATETIME_HUGE,
34 + F: Formats.DATETIME_SHORT_WITH_SECONDS,
35 + FF: Formats.DATETIME_MED_WITH_SECONDS,
36 + FFF: Formats.DATETIME_FULL_WITH_SECONDS,
37 + FFFF: Formats.DATETIME_HUGE_WITH_SECONDS
38 +};
39 +
40 +/**
41 + * @private
42 + */
43 +
44 +export default class Formatter {
45 + static create(locale, opts = {}) {
46 + return new Formatter(locale, opts);
47 + }
48 +
49 + static parseFormat(fmt) {
50 + let current = null,
51 + currentFull = "",
52 + bracketed = false;
53 + const splits = [];
54 + for (let i = 0; i < fmt.length; i++) {
55 + const c = fmt.charAt(i);
56 + if (c === "'") {
57 + if (currentFull.length > 0) {
58 + splits.push({ literal: bracketed, val: currentFull });
59 + }
60 + current = null;
61 + currentFull = "";
62 + bracketed = !bracketed;
63 + } else if (bracketed) {
64 + currentFull += c;
65 + } else if (c === current) {
66 + currentFull += c;
67 + } else {
68 + if (currentFull.length > 0) {
69 + splits.push({ literal: false, val: currentFull });
70 + }
71 + currentFull = c;
72 + current = c;
73 + }
74 + }
75 +
76 + if (currentFull.length > 0) {
77 + splits.push({ literal: bracketed, val: currentFull });
78 + }
79 +
80 + return splits;
81 + }
82 +
83 + static macroTokenToFormatOpts(token) {
84 + return macroTokenToFormatOpts[token];
85 + }
86 +
87 + constructor(locale, formatOpts) {
88 + this.opts = formatOpts;
89 + this.loc = locale;
90 + this.systemLoc = null;
91 + }
92 +
93 + formatWithSystemDefault(dt, opts) {
94 + if (this.systemLoc === null) {
95 + this.systemLoc = this.loc.redefaultToSystem();
96 + }
97 + const df = this.systemLoc.dtFormatter(dt, Object.assign({}, this.opts, opts));
98 + return df.format();
99 + }
100 +
101 + formatDateTime(dt, opts = {}) {
102 + const df = this.loc.dtFormatter(dt, Object.assign({}, this.opts, opts));
103 + return df.format();
104 + }
105 +
106 + formatDateTimeParts(dt, opts = {}) {
107 + const df = this.loc.dtFormatter(dt, Object.assign({}, this.opts, opts));
108 + return df.formatToParts();
109 + }
110 +
111 + resolvedOptions(dt, opts = {}) {
112 + const df = this.loc.dtFormatter(dt, Object.assign({}, this.opts, opts));
113 + return df.resolvedOptions();
114 + }
115 +
116 + num(n, p = 0) {
117 + // we get some perf out of doing this here, annoyingly
118 + if (this.opts.forceSimple) {
119 + return padStart(n, p);
120 + }
121 +
122 + const opts = Object.assign({}, this.opts);
123 +
124 + if (p > 0) {
125 + opts.padTo = p;
126 + }
127 +
128 + return this.loc.numberFormatter(opts).format(n);
129 + }
130 +
131 + formatDateTimeFromString(dt, fmt) {
132 + const knownEnglish = this.loc.listingMode() === "en",
133 + useDateTimeFormatter =
134 + this.loc.outputCalendar && this.loc.outputCalendar !== "gregory" && hasFormatToParts(),
135 + string = (opts, extract) => this.loc.extract(dt, opts, extract),
136 + formatOffset = opts => {
137 + if (dt.isOffsetFixed && dt.offset === 0 && opts.allowZ) {
138 + return "Z";
139 + }
140 +
141 + return dt.isValid ? dt.zone.formatOffset(dt.ts, opts.format) : "";
142 + },
143 + meridiem = () =>
144 + knownEnglish
145 + ? English.meridiemForDateTime(dt)
146 + : string({ hour: "numeric", hour12: true }, "dayperiod"),
147 + month = (length, standalone) =>
148 + knownEnglish
149 + ? English.monthForDateTime(dt, length)
150 + : string(standalone ? { month: length } : { month: length, day: "numeric" }, "month"),
151 + weekday = (length, standalone) =>
152 + knownEnglish
153 + ? English.weekdayForDateTime(dt, length)
154 + : string(
155 + standalone ? { weekday: length } : { weekday: length, month: "long", day: "numeric" },
156 + "weekday"
157 + ),
158 + maybeMacro = token => {
159 + const formatOpts = Formatter.macroTokenToFormatOpts(token);
160 + if (formatOpts) {
161 + return this.formatWithSystemDefault(dt, formatOpts);
162 + } else {
163 + return token;
164 + }
165 + },
166 + era = length =>
167 + knownEnglish ? English.eraForDateTime(dt, length) : string({ era: length }, "era"),
168 + tokenToString = token => {
169 + // Where possible: http://cldr.unicode.org/translation/date-time-1/date-time#TOC-Standalone-vs.-Format-Styles
170 + switch (token) {
171 + // ms
172 + case "S":
173 + return this.num(dt.millisecond);
174 + case "u":
175 + // falls through
176 + case "SSS":
177 + return this.num(dt.millisecond, 3);
178 + // seconds
179 + case "s":
180 + return this.num(dt.second);
181 + case "ss":
182 + return this.num(dt.second, 2);
183 + // minutes
184 + case "m":
185 + return this.num(dt.minute);
186 + case "mm":
187 + return this.num(dt.minute, 2);
188 + // hours
189 + case "h":
190 + return this.num(dt.hour % 12 === 0 ? 12 : dt.hour % 12);
191 + case "hh":
192 + return this.num(dt.hour % 12 === 0 ? 12 : dt.hour % 12, 2);
193 + case "H":
194 + return this.num(dt.hour);
195 + case "HH":
196 + return this.num(dt.hour, 2);
197 + // offset
198 + case "Z":
199 + // like +6
200 + return formatOffset({ format: "narrow", allowZ: this.opts.allowZ });
201 + case "ZZ":
202 + // like +06:00
203 + return formatOffset({ format: "short", allowZ: this.opts.allowZ });
204 + case "ZZZ":
205 + // like +0600
206 + return formatOffset({ format: "techie", allowZ: this.opts.allowZ });
207 + case "ZZZZ":
208 + // like EST
209 + return dt.zone.offsetName(dt.ts, { format: "short", locale: this.loc.locale });
210 + case "ZZZZZ":
211 + // like Eastern Standard Time
212 + return dt.zone.offsetName(dt.ts, { format: "long", locale: this.loc.locale });
213 + // zone
214 + case "z":
215 + // like America/New_York
216 + return dt.zoneName;
217 + // meridiems
218 + case "a":
219 + return meridiem();
220 + // dates
221 + case "d":
222 + return useDateTimeFormatter ? string({ day: "numeric" }, "day") : this.num(dt.day);
223 + case "dd":
224 + return useDateTimeFormatter ? string({ day: "2-digit" }, "day") : this.num(dt.day, 2);
225 + // weekdays - standalone
226 + case "c":
227 + // like 1
228 + return this.num(dt.weekday);
229 + case "ccc":
230 + // like 'Tues'
231 + return weekday("short", true);
232 + case "cccc":
233 + // like 'Tuesday'
234 + return weekday("long", true);
235 + case "ccccc":
236 + // like 'T'
237 + return weekday("narrow", true);
238 + // weekdays - format
239 + case "E":
240 + // like 1
241 + return this.num(dt.weekday);
242 + case "EEE":
243 + // like 'Tues'
244 + return weekday("short", false);
245 + case "EEEE":
246 + // like 'Tuesday'
247 + return weekday("long", false);
248 + case "EEEEE":
249 + // like 'T'
250 + return weekday("narrow", false);
251 + // months - standalone
252 + case "L":
253 + // like 1
254 + return useDateTimeFormatter
255 + ? string({ month: "numeric", day: "numeric" }, "month")
256 + : this.num(dt.month);
257 + case "LL":
258 + // like 01, doesn't seem to work
259 + return useDateTimeFormatter
260 + ? string({ month: "2-digit", day: "numeric" }, "month")
261 + : this.num(dt.month, 2);
262 + case "LLL":
263 + // like Jan
264 + return month("short", true);
265 + case "LLLL":
266 + // like January
267 + return month("long", true);
268 + case "LLLLL":
269 + // like J
270 + return month("narrow", true);
271 + // months - format
272 + case "M":
273 + // like 1
274 + return useDateTimeFormatter
275 + ? string({ month: "numeric" }, "month")
276 + : this.num(dt.month);
277 + case "MM":
278 + // like 01
279 + return useDateTimeFormatter
280 + ? string({ month: "2-digit" }, "month")
281 + : this.num(dt.month, 2);
282 + case "MMM":
283 + // like Jan
284 + return month("short", false);
285 + case "MMMM":
286 + // like January
287 + return month("long", false);
288 + case "MMMMM":
289 + // like J
290 + return month("narrow", false);
291 + // years
292 + case "y":
293 + // like 2014
294 + return useDateTimeFormatter ? string({ year: "numeric" }, "year") : this.num(dt.year);
295 + case "yy":
296 + // like 14
297 + return useDateTimeFormatter
298 + ? string({ year: "2-digit" }, "year")
299 + : this.num(dt.year.toString().slice(-2), 2);
300 + case "yyyy":
301 + // like 0012
302 + return useDateTimeFormatter
303 + ? string({ year: "numeric" }, "year")
304 + : this.num(dt.year, 4);
305 + case "yyyyyy":
306 + // like 000012
307 + return useDateTimeFormatter
308 + ? string({ year: "numeric" }, "year")
309 + : this.num(dt.year, 6);
310 + // eras
311 + case "G":
312 + // like AD
313 + return era("short");
314 + case "GG":
315 + // like Anno Domini
316 + return era("long");
317 + case "GGGGG":
318 + return era("narrow");
319 + case "kk":
320 + return this.num(dt.weekYear.toString().slice(-2), 2);
321 + case "kkkk":
322 + return this.num(dt.weekYear, 4);
323 + case "W":
324 + return this.num(dt.weekNumber);
325 + case "WW":
326 + return this.num(dt.weekNumber, 2);
327 + case "o":
328 + return this.num(dt.ordinal);
329 + case "ooo":
330 + return this.num(dt.ordinal, 3);
331 + case "q":
332 + // like 1
333 + return this.num(dt.quarter);
334 + case "qq":
335 + // like 01
336 + return this.num(dt.quarter, 2);
337 + case "X":
338 + return this.num(Math.floor(dt.ts / 1000));
339 + case "x":
340 + return this.num(dt.ts);
341 + default:
342 + return maybeMacro(token);
343 + }
344 + };
345 +
346 + return stringifyTokens(Formatter.parseFormat(fmt), tokenToString);
347 + }
348 +
349 + formatDurationFromString(dur, fmt) {
350 + const tokenToField = token => {
351 + switch (token[0]) {
352 + case "S":
353 + return "millisecond";
354 + case "s":
355 + return "second";
356 + case "m":
357 + return "minute";
358 + case "h":
359 + return "hour";
360 + case "d":
361 + return "day";
362 + case "M":
363 + return "month";
364 + case "y":
365 + return "year";
366 + default:
367 + return null;
368 + }
369 + },
370 + tokenToString = lildur => token => {
371 + const mapped = tokenToField(token);
372 + if (mapped) {
373 + return this.num(lildur.get(mapped), token.length);
374 + } else {
375 + return token;
376 + }
377 + },
378 + tokens = Formatter.parseFormat(fmt),
379 + realTokens = tokens.reduce(
380 + (found, { literal, val }) => (literal ? found : found.concat(val)),
381 + []
382 + ),
383 + collapsed = dur.shiftTo(...realTokens.map(tokenToField).filter(t => t));
384 + return stringifyTokens(tokens, tokenToString(collapsed));
385 + }
386 +}
1 +export default class Invalid {
2 + constructor(reason, explanation) {
3 + this.reason = reason;
4 + this.explanation = explanation;
5 + }
6 +
7 + toMessage() {
8 + if (this.explanation) {
9 + return `${this.reason}: ${this.explanation}`;
10 + } else {
11 + return this.reason;
12 + }
13 + }
14 +}
1 +import { hasFormatToParts, hasIntl, padStart, roundTo, hasRelative } from "./util.js";
2 +import * as English from "./english.js";
3 +import Settings from "../settings.js";
4 +import DateTime from "../datetime.js";
5 +import Formatter from "./formatter.js";
6 +import IANAZone from "../zones/IANAZone.js";
7 +
8 +let intlDTCache = {};
9 +function getCachedDTF(locString, opts = {}) {
10 + const key = JSON.stringify([locString, opts]);
11 + let dtf = intlDTCache[key];
12 + if (!dtf) {
13 + dtf = new Intl.DateTimeFormat(locString, opts);
14 + intlDTCache[key] = dtf;
15 + }
16 + return dtf;
17 +}
18 +
19 +let intlNumCache = {};
20 +function getCachedINF(locString, opts = {}) {
21 + const key = JSON.stringify([locString, opts]);
22 + let inf = intlNumCache[key];
23 + if (!inf) {
24 + inf = new Intl.NumberFormat(locString, opts);
25 + intlNumCache[key] = inf;
26 + }
27 + return inf;
28 +}
29 +
30 +let intlRelCache = {};
31 +function getCachedRTF(locString, opts = {}) {
32 + const { base, ...cacheKeyOpts } = opts; // exclude `base` from the options
33 + const key = JSON.stringify([locString, cacheKeyOpts]);
34 + let inf = intlRelCache[key];
35 + if (!inf) {
36 + inf = new Intl.RelativeTimeFormat(locString, opts);
37 + intlRelCache[key] = inf;
38 + }
39 + return inf;
40 +}
41 +
42 +let sysLocaleCache = null;
43 +function systemLocale() {
44 + if (sysLocaleCache) {
45 + return sysLocaleCache;
46 + } else if (hasIntl()) {
47 + const computedSys = new Intl.DateTimeFormat().resolvedOptions().locale;
48 + // node sometimes defaults to "und". Override that because that is dumb
49 + sysLocaleCache = !computedSys || computedSys === "und" ? "en-US" : computedSys;
50 + return sysLocaleCache;
51 + } else {
52 + sysLocaleCache = "en-US";
53 + return sysLocaleCache;
54 + }
55 +}
56 +
57 +function parseLocaleString(localeStr) {
58 + // I really want to avoid writing a BCP 47 parser
59 + // see, e.g. https://github.com/wooorm/bcp-47
60 + // Instead, we'll do this:
61 +
62 + // a) if the string has no -u extensions, just leave it alone
63 + // b) if it does, use Intl to resolve everything
64 + // c) if Intl fails, try again without the -u
65 +
66 + const uIndex = localeStr.indexOf("-u-");
67 + if (uIndex === -1) {
68 + return [localeStr];
69 + } else {
70 + let options;
71 + const smaller = localeStr.substring(0, uIndex);
72 + try {
73 + options = getCachedDTF(localeStr).resolvedOptions();
74 + } catch (e) {
75 + options = getCachedDTF(smaller).resolvedOptions();
76 + }
77 +
78 + const { numberingSystem, calendar } = options;
79 + // return the smaller one so that we can append the calendar and numbering overrides to it
80 + return [smaller, numberingSystem, calendar];
81 + }
82 +}
83 +
84 +function intlConfigString(localeStr, numberingSystem, outputCalendar) {
85 + if (hasIntl()) {
86 + if (outputCalendar || numberingSystem) {
87 + localeStr += "-u";
88 +
89 + if (outputCalendar) {
90 + localeStr += `-ca-${outputCalendar}`;
91 + }
92 +
93 + if (numberingSystem) {
94 + localeStr += `-nu-${numberingSystem}`;
95 + }
96 + return localeStr;
97 + } else {
98 + return localeStr;
99 + }
100 + } else {
101 + return [];
102 + }
103 +}
104 +
105 +function mapMonths(f) {
106 + const ms = [];
107 + for (let i = 1; i <= 12; i++) {
108 + const dt = DateTime.utc(2016, i, 1);
109 + ms.push(f(dt));
110 + }
111 + return ms;
112 +}
113 +
114 +function mapWeekdays(f) {
115 + const ms = [];
116 + for (let i = 1; i <= 7; i++) {
117 + const dt = DateTime.utc(2016, 11, 13 + i);
118 + ms.push(f(dt));
119 + }
120 + return ms;
121 +}
122 +
123 +function listStuff(loc, length, defaultOK, englishFn, intlFn) {
124 + const mode = loc.listingMode(defaultOK);
125 +
126 + if (mode === "error") {
127 + return null;
128 + } else if (mode === "en") {
129 + return englishFn(length);
130 + } else {
131 + return intlFn(length);
132 + }
133 +}
134 +
135 +function supportsFastNumbers(loc) {
136 + if (loc.numberingSystem && loc.numberingSystem !== "latn") {
137 + return false;
138 + } else {
139 + return (
140 + loc.numberingSystem === "latn" ||
141 + !loc.locale ||
142 + loc.locale.startsWith("en") ||
143 + (hasIntl() && new Intl.DateTimeFormat(loc.intl).resolvedOptions().numberingSystem === "latn")
144 + );
145 + }
146 +}
147 +
148 +/**
149 + * @private
150 + */
151 +
152 +class PolyNumberFormatter {
153 + constructor(intl, forceSimple, opts) {
154 + this.padTo = opts.padTo || 0;
155 + this.floor = opts.floor || false;
156 +
157 + if (!forceSimple && hasIntl()) {
158 + const intlOpts = { useGrouping: false };
159 + if (opts.padTo > 0) intlOpts.minimumIntegerDigits = opts.padTo;
160 + this.inf = getCachedINF(intl, intlOpts);
161 + }
162 + }
163 +
164 + format(i) {
165 + if (this.inf) {
166 + const fixed = this.floor ? Math.floor(i) : i;
167 + return this.inf.format(fixed);
168 + } else {
169 + // to match the browser's numberformatter defaults
170 + const fixed = this.floor ? Math.floor(i) : roundTo(i, 3);
171 + return padStart(fixed, this.padTo);
172 + }
173 + }
174 +}
175 +
176 +/**
177 + * @private
178 + */
179 +
180 +class PolyDateFormatter {
181 + constructor(dt, intl, opts) {
182 + this.opts = opts;
183 + this.hasIntl = hasIntl();
184 +
185 + let z;
186 + if (dt.zone.universal && this.hasIntl) {
187 + // UTC-8 or Etc/UTC-8 are not part of tzdata, only Etc/GMT+8 and the like.
188 + // That is why fixed-offset TZ is set to that unless it is:
189 + // 1. Representing offset 0 when UTC is used to maintain previous behavior and does not become GMT.
190 + // 2. Unsupported by the browser:
191 + // - some do not support Etc/
192 + // - < Etc/GMT-14, > Etc/GMT+12, and 30-minute or 45-minute offsets are not part of tzdata
193 + const gmtOffset = -1 * (dt.offset / 60);
194 + const offsetZ = gmtOffset >= 0 ? `Etc/GMT+${gmtOffset}` : `Etc/GMT${gmtOffset}`;
195 + const isOffsetZoneSupported = IANAZone.isValidZone(offsetZ);
196 + if (dt.offset !== 0 && isOffsetZoneSupported) {
197 + z = offsetZ;
198 + this.dt = dt;
199 + } else {
200 + // Not all fixed-offset zones like Etc/+4:30 are present in tzdata.
201 + // So we have to make do. Two cases:
202 + // 1. The format options tell us to show the zone. We can't do that, so the best
203 + // we can do is format the date in UTC.
204 + // 2. The format options don't tell us to show the zone. Then we can adjust them
205 + // the time and tell the formatter to show it to us in UTC, so that the time is right
206 + // and the bad zone doesn't show up.
207 + z = "UTC";
208 + if (opts.timeZoneName) {
209 + this.dt = dt;
210 + } else {
211 + this.dt = dt.offset === 0 ? dt : DateTime.fromMillis(dt.ts + dt.offset * 60 * 1000);
212 + }
213 + }
214 + } else if (dt.zone.type === "local") {
215 + this.dt = dt;
216 + } else {
217 + this.dt = dt;
218 + z = dt.zone.name;
219 + }
220 +
221 + if (this.hasIntl) {
222 + const intlOpts = Object.assign({}, this.opts);
223 + if (z) {
224 + intlOpts.timeZone = z;
225 + }
226 + this.dtf = getCachedDTF(intl, intlOpts);
227 + }
228 + }
229 +
230 + format() {
231 + if (this.hasIntl) {
232 + return this.dtf.format(this.dt.toJSDate());
233 + } else {
234 + const tokenFormat = English.formatString(this.opts),
235 + loc = Locale.create("en-US");
236 + return Formatter.create(loc).formatDateTimeFromString(this.dt, tokenFormat);
237 + }
238 + }
239 +
240 + formatToParts() {
241 + if (this.hasIntl && hasFormatToParts()) {
242 + return this.dtf.formatToParts(this.dt.toJSDate());
243 + } else {
244 + // This is kind of a cop out. We actually could do this for English. However, we couldn't do it for intl strings
245 + // and IMO it's too weird to have an uncanny valley like that
246 + return [];
247 + }
248 + }
249 +
250 + resolvedOptions() {
251 + if (this.hasIntl) {
252 + return this.dtf.resolvedOptions();
253 + } else {
254 + return {
255 + locale: "en-US",
256 + numberingSystem: "latn",
257 + outputCalendar: "gregory"
258 + };
259 + }
260 + }
261 +}
262 +
263 +/**
264 + * @private
265 + */
266 +class PolyRelFormatter {
267 + constructor(intl, isEnglish, opts) {
268 + this.opts = Object.assign({ style: "long" }, opts);
269 + if (!isEnglish && hasRelative()) {
270 + this.rtf = getCachedRTF(intl, opts);
271 + }
272 + }
273 +
274 + format(count, unit) {
275 + if (this.rtf) {
276 + return this.rtf.format(count, unit);
277 + } else {
278 + return English.formatRelativeTime(unit, count, this.opts.numeric, this.opts.style !== "long");
279 + }
280 + }
281 +
282 + formatToParts(count, unit) {
283 + if (this.rtf) {
284 + return this.rtf.formatToParts(count, unit);
285 + } else {
286 + return [];
287 + }
288 + }
289 +}
290 +
291 +/**
292 + * @private
293 + */
294 +
295 +export default class Locale {
296 + static fromOpts(opts) {
297 + return Locale.create(opts.locale, opts.numberingSystem, opts.outputCalendar, opts.defaultToEN);
298 + }
299 +
300 + static create(locale, numberingSystem, outputCalendar, defaultToEN = false) {
301 + const specifiedLocale = locale || Settings.defaultLocale,
302 + // the system locale is useful for human readable strings but annoying for parsing/formatting known formats
303 + localeR = specifiedLocale || (defaultToEN ? "en-US" : systemLocale()),
304 + numberingSystemR = numberingSystem || Settings.defaultNumberingSystem,
305 + outputCalendarR = outputCalendar || Settings.defaultOutputCalendar;
306 + return new Locale(localeR, numberingSystemR, outputCalendarR, specifiedLocale);
307 + }
308 +
309 + static resetCache() {
310 + sysLocaleCache = null;
311 + intlDTCache = {};
312 + intlNumCache = {};
313 + intlRelCache = {};
314 + }
315 +
316 + static fromObject({ locale, numberingSystem, outputCalendar } = {}) {
317 + return Locale.create(locale, numberingSystem, outputCalendar);
318 + }
319 +
320 + constructor(locale, numbering, outputCalendar, specifiedLocale) {
321 + const [parsedLocale, parsedNumberingSystem, parsedOutputCalendar] = parseLocaleString(locale);
322 +
323 + this.locale = parsedLocale;
324 + this.numberingSystem = numbering || parsedNumberingSystem || null;
325 + this.outputCalendar = outputCalendar || parsedOutputCalendar || null;
326 + this.intl = intlConfigString(this.locale, this.numberingSystem, this.outputCalendar);
327 +
328 + this.weekdaysCache = { format: {}, standalone: {} };
329 + this.monthsCache = { format: {}, standalone: {} };
330 + this.meridiemCache = null;
331 + this.eraCache = {};
332 +
333 + this.specifiedLocale = specifiedLocale;
334 + this.fastNumbersCached = null;
335 + }
336 +
337 + get fastNumbers() {
338 + if (this.fastNumbersCached == null) {
339 + this.fastNumbersCached = supportsFastNumbers(this);
340 + }
341 +
342 + return this.fastNumbersCached;
343 + }
344 +
345 + listingMode(defaultOK = true) {
346 + const intl = hasIntl(),
347 + hasFTP = intl && hasFormatToParts(),
348 + isActuallyEn = this.isEnglish(),
349 + hasNoWeirdness =
350 + (this.numberingSystem === null || this.numberingSystem === "latn") &&
351 + (this.outputCalendar === null || this.outputCalendar === "gregory");
352 +
353 + if (!hasFTP && !(isActuallyEn && hasNoWeirdness) && !defaultOK) {
354 + return "error";
355 + } else if (!hasFTP || (isActuallyEn && hasNoWeirdness)) {
356 + return "en";
357 + } else {
358 + return "intl";
359 + }
360 + }
361 +
362 + clone(alts) {
363 + if (!alts || Object.getOwnPropertyNames(alts).length === 0) {
364 + return this;
365 + } else {
366 + return Locale.create(
367 + alts.locale || this.specifiedLocale,
368 + alts.numberingSystem || this.numberingSystem,
369 + alts.outputCalendar || this.outputCalendar,
370 + alts.defaultToEN || false
371 + );
372 + }
373 + }
374 +
375 + redefaultToEN(alts = {}) {
376 + return this.clone(Object.assign({}, alts, { defaultToEN: true }));
377 + }
378 +
379 + redefaultToSystem(alts = {}) {
380 + return this.clone(Object.assign({}, alts, { defaultToEN: false }));
381 + }
382 +
383 + months(length, format = false, defaultOK = true) {
384 + return listStuff(this, length, defaultOK, English.months, () => {
385 + const intl = format ? { month: length, day: "numeric" } : { month: length },
386 + formatStr = format ? "format" : "standalone";
387 + if (!this.monthsCache[formatStr][length]) {
388 + this.monthsCache[formatStr][length] = mapMonths(dt => this.extract(dt, intl, "month"));
389 + }
390 + return this.monthsCache[formatStr][length];
391 + });
392 + }
393 +
394 + weekdays(length, format = false, defaultOK = true) {
395 + return listStuff(this, length, defaultOK, English.weekdays, () => {
396 + const intl = format
397 + ? { weekday: length, year: "numeric", month: "long", day: "numeric" }
398 + : { weekday: length },
399 + formatStr = format ? "format" : "standalone";
400 + if (!this.weekdaysCache[formatStr][length]) {
401 + this.weekdaysCache[formatStr][length] = mapWeekdays(dt =>
402 + this.extract(dt, intl, "weekday")
403 + );
404 + }
405 + return this.weekdaysCache[formatStr][length];
406 + });
407 + }
408 +
409 + meridiems(defaultOK = true) {
410 + return listStuff(
411 + this,
412 + undefined,
413 + defaultOK,
414 + () => English.meridiems,
415 + () => {
416 + // In theory there could be aribitrary day periods. We're gonna assume there are exactly two
417 + // for AM and PM. This is probably wrong, but it's makes parsing way easier.
418 + if (!this.meridiemCache) {
419 + const intl = { hour: "numeric", hour12: true };
420 + this.meridiemCache = [DateTime.utc(2016, 11, 13, 9), DateTime.utc(2016, 11, 13, 19)].map(
421 + dt => this.extract(dt, intl, "dayperiod")
422 + );
423 + }
424 +
425 + return this.meridiemCache;
426 + }
427 + );
428 + }
429 +
430 + eras(length, defaultOK = true) {
431 + return listStuff(this, length, defaultOK, English.eras, () => {
432 + const intl = { era: length };
433 +
434 + // This is problematic. Different calendars are going to define eras totally differently. What I need is the minimum set of dates
435 + // to definitely enumerate them.
436 + if (!this.eraCache[length]) {
437 + this.eraCache[length] = [DateTime.utc(-40, 1, 1), DateTime.utc(2017, 1, 1)].map(dt =>
438 + this.extract(dt, intl, "era")
439 + );
440 + }
441 +
442 + return this.eraCache[length];
443 + });
444 + }
445 +
446 + extract(dt, intlOpts, field) {
447 + const df = this.dtFormatter(dt, intlOpts),
448 + results = df.formatToParts(),
449 + matching = results.find(m => m.type.toLowerCase() === field);
450 + return matching ? matching.value : null;
451 + }
452 +
453 + numberFormatter(opts = {}) {
454 + // this forcesimple option is never used (the only caller short-circuits on it, but it seems safer to leave)
455 + // (in contrast, the rest of the condition is used heavily)
456 + return new PolyNumberFormatter(this.intl, opts.forceSimple || this.fastNumbers, opts);
457 + }
458 +
459 + dtFormatter(dt, intlOpts = {}) {
460 + return new PolyDateFormatter(dt, this.intl, intlOpts);
461 + }
462 +
463 + relFormatter(opts = {}) {
464 + return new PolyRelFormatter(this.intl, this.isEnglish(), opts);
465 + }
466 +
467 + isEnglish() {
468 + return (
469 + this.locale === "en" ||
470 + this.locale.toLowerCase() === "en-us" ||
471 + (hasIntl() && new Intl.DateTimeFormat(this.intl).resolvedOptions().locale.startsWith("en-us"))
472 + );
473 + }
474 +
475 + equals(other) {
476 + return (
477 + this.locale === other.locale &&
478 + this.numberingSystem === other.numberingSystem &&
479 + this.outputCalendar === other.outputCalendar
480 + );
481 + }
482 +}
1 +import {
2 + untruncateYear,
3 + signedOffset,
4 + parseInteger,
5 + parseMillis,
6 + ianaRegex,
7 + isUndefined
8 +} from "./util.js";
9 +import * as English from "./english.js";
10 +import FixedOffsetZone from "../zones/fixedOffsetZone.js";
11 +import IANAZone from "../zones/IANAZone.js";
12 +
13 +/*
14 + * This file handles parsing for well-specified formats. Here's how it works:
15 + * Two things go into parsing: a regex to match with and an extractor to take apart the groups in the match.
16 + * An extractor is just a function that takes a regex match array and returns a { year: ..., month: ... } object
17 + * parse() does the work of executing the regex and applying the extractor. It takes multiple regex/extractor pairs to try in sequence.
18 + * Extractors can take a "cursor" representing the offset in the match to look at. This makes it easy to combine extractors.
19 + * combineExtractors() does the work of combining them, keeping track of the cursor through multiple extractions.
20 + * Some extractions are super dumb and simpleParse and fromStrings help DRY them.
21 + */
22 +
23 +function combineRegexes(...regexes) {
24 + const full = regexes.reduce((f, r) => f + r.source, "");
25 + return RegExp(`^${full}$`);
26 +}
27 +
28 +function combineExtractors(...extractors) {
29 + return m =>
30 + extractors
31 + .reduce(
32 + ([mergedVals, mergedZone, cursor], ex) => {
33 + const [val, zone, next] = ex(m, cursor);
34 + return [Object.assign(mergedVals, val), mergedZone || zone, next];
35 + },
36 + [{}, null, 1]
37 + )
38 + .slice(0, 2);
39 +}
40 +
41 +function parse(s, ...patterns) {
42 + if (s == null) {
43 + return [null, null];
44 + }
45 +
46 + for (const [regex, extractor] of patterns) {
47 + const m = regex.exec(s);
48 + if (m) {
49 + return extractor(m);
50 + }
51 + }
52 + return [null, null];
53 +}
54 +
55 +function simpleParse(...keys) {
56 + return (match, cursor) => {
57 + const ret = {};
58 + let i;
59 +
60 + for (i = 0; i < keys.length; i++) {
61 + ret[keys[i]] = parseInteger(match[cursor + i]);
62 + }
63 + return [ret, null, cursor + i];
64 + };
65 +}
66 +
67 +// ISO and SQL parsing
68 +const offsetRegex = /(?:(Z)|([+-]\d\d)(?::?(\d\d))?)/,
69 + isoTimeBaseRegex = /(\d\d)(?::?(\d\d)(?::?(\d\d)(?:[.,](\d{1,30}))?)?)?/,
70 + isoTimeRegex = RegExp(`${isoTimeBaseRegex.source}${offsetRegex.source}?`),
71 + isoTimeExtensionRegex = RegExp(`(?:T${isoTimeRegex.source})?`),
72 + isoYmdRegex = /([+-]\d{6}|\d{4})(?:-?(\d\d)(?:-?(\d\d))?)?/,
73 + isoWeekRegex = /(\d{4})-?W(\d\d)(?:-?(\d))?/,
74 + isoOrdinalRegex = /(\d{4})-?(\d{3})/,
75 + extractISOWeekData = simpleParse("weekYear", "weekNumber", "weekDay"),
76 + extractISOOrdinalData = simpleParse("year", "ordinal"),
77 + sqlYmdRegex = /(\d{4})-(\d\d)-(\d\d)/, // dumbed-down version of the ISO one
78 + sqlTimeRegex = RegExp(
79 + `${isoTimeBaseRegex.source} ?(?:${offsetRegex.source}|(${ianaRegex.source}))?`
80 + ),
81 + sqlTimeExtensionRegex = RegExp(`(?: ${sqlTimeRegex.source})?`);
82 +
83 +function int(match, pos, fallback) {
84 + const m = match[pos];
85 + return isUndefined(m) ? fallback : parseInteger(m);
86 +}
87 +
88 +function extractISOYmd(match, cursor) {
89 + const item = {
90 + year: int(match, cursor),
91 + month: int(match, cursor + 1, 1),
92 + day: int(match, cursor + 2, 1)
93 + };
94 +
95 + return [item, null, cursor + 3];
96 +}
97 +
98 +function extractISOTime(match, cursor) {
99 + const item = {
100 + hours: int(match, cursor, 0),
101 + minutes: int(match, cursor + 1, 0),
102 + seconds: int(match, cursor + 2, 0),
103 + milliseconds: parseMillis(match[cursor + 3])
104 + };
105 +
106 + return [item, null, cursor + 4];
107 +}
108 +
109 +function extractISOOffset(match, cursor) {
110 + const local = !match[cursor] && !match[cursor + 1],
111 + fullOffset = signedOffset(match[cursor + 1], match[cursor + 2]),
112 + zone = local ? null : FixedOffsetZone.instance(fullOffset);
113 + return [{}, zone, cursor + 3];
114 +}
115 +
116 +function extractIANAZone(match, cursor) {
117 + const zone = match[cursor] ? IANAZone.create(match[cursor]) : null;
118 + return [{}, zone, cursor + 1];
119 +}
120 +
121 +// ISO time parsing
122 +
123 +const isoTimeOnly = RegExp(`^T?${isoTimeBaseRegex.source}$`);
124 +
125 +// ISO duration parsing
126 +
127 +const isoDuration = /^-?P(?:(?:(-?\d{1,9})Y)?(?:(-?\d{1,9})M)?(?:(-?\d{1,9})W)?(?:(-?\d{1,9})D)?(?:T(?:(-?\d{1,9})H)?(?:(-?\d{1,9})M)?(?:(-?\d{1,20})(?:[.,](-?\d{1,9}))?S)?)?)$/;
128 +
129 +function extractISODuration(match) {
130 + const [
131 + s,
132 + yearStr,
133 + monthStr,
134 + weekStr,
135 + dayStr,
136 + hourStr,
137 + minuteStr,
138 + secondStr,
139 + millisecondsStr
140 + ] = match;
141 +
142 + const hasNegativePrefix = s[0] === "-";
143 + const negativeSeconds = secondStr && secondStr[0] === "-";
144 +
145 + const maybeNegate = (num, force = false) =>
146 + num !== undefined && (force || (num && hasNegativePrefix)) ? -num : num;
147 +
148 + return [
149 + {
150 + years: maybeNegate(parseInteger(yearStr)),
151 + months: maybeNegate(parseInteger(monthStr)),
152 + weeks: maybeNegate(parseInteger(weekStr)),
153 + days: maybeNegate(parseInteger(dayStr)),
154 + hours: maybeNegate(parseInteger(hourStr)),
155 + minutes: maybeNegate(parseInteger(minuteStr)),
156 + seconds: maybeNegate(parseInteger(secondStr), secondStr === "-0"),
157 + milliseconds: maybeNegate(parseMillis(millisecondsStr), negativeSeconds)
158 + }
159 + ];
160 +}
161 +
162 +// These are a little braindead. EDT *should* tell us that we're in, say, America/New_York
163 +// and not just that we're in -240 *right now*. But since I don't think these are used that often
164 +// I'm just going to ignore that
165 +const obsOffsets = {
166 + GMT: 0,
167 + EDT: -4 * 60,
168 + EST: -5 * 60,
169 + CDT: -5 * 60,
170 + CST: -6 * 60,
171 + MDT: -6 * 60,
172 + MST: -7 * 60,
173 + PDT: -7 * 60,
174 + PST: -8 * 60
175 +};
176 +
177 +function fromStrings(weekdayStr, yearStr, monthStr, dayStr, hourStr, minuteStr, secondStr) {
178 + const result = {
179 + year: yearStr.length === 2 ? untruncateYear(parseInteger(yearStr)) : parseInteger(yearStr),
180 + month: English.monthsShort.indexOf(monthStr) + 1,
181 + day: parseInteger(dayStr),
182 + hour: parseInteger(hourStr),
183 + minute: parseInteger(minuteStr)
184 + };
185 +
186 + if (secondStr) result.second = parseInteger(secondStr);
187 + if (weekdayStr) {
188 + result.weekday =
189 + weekdayStr.length > 3
190 + ? English.weekdaysLong.indexOf(weekdayStr) + 1
191 + : English.weekdaysShort.indexOf(weekdayStr) + 1;
192 + }
193 +
194 + return result;
195 +}
196 +
197 +// RFC 2822/5322
198 +const rfc2822 = /^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),\s)?(\d{1,2})\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(\d{2,4})\s(\d\d):(\d\d)(?::(\d\d))?\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|(?:([+-]\d\d)(\d\d)))$/;
199 +
200 +function extractRFC2822(match) {
201 + const [
202 + ,
203 + weekdayStr,
204 + dayStr,
205 + monthStr,
206 + yearStr,
207 + hourStr,
208 + minuteStr,
209 + secondStr,
210 + obsOffset,
211 + milOffset,
212 + offHourStr,
213 + offMinuteStr
214 + ] = match,
215 + result = fromStrings(weekdayStr, yearStr, monthStr, dayStr, hourStr, minuteStr, secondStr);
216 +
217 + let offset;
218 + if (obsOffset) {
219 + offset = obsOffsets[obsOffset];
220 + } else if (milOffset) {
221 + offset = 0;
222 + } else {
223 + offset = signedOffset(offHourStr, offMinuteStr);
224 + }
225 +
226 + return [result, new FixedOffsetZone(offset)];
227 +}
228 +
229 +function preprocessRFC2822(s) {
230 + // Remove comments and folding whitespace and replace multiple-spaces with a single space
231 + return s
232 + .replace(/\([^)]*\)|[\n\t]/g, " ")
233 + .replace(/(\s\s+)/g, " ")
234 + .trim();
235 +}
236 +
237 +// http date
238 +
239 +const rfc1123 = /^(Mon|Tue|Wed|Thu|Fri|Sat|Sun), (\d\d) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) (\d{4}) (\d\d):(\d\d):(\d\d) GMT$/,
240 + rfc850 = /^(Monday|Tuesday|Wedsday|Thursday|Friday|Saturday|Sunday), (\d\d)-(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)-(\d\d) (\d\d):(\d\d):(\d\d) GMT$/,
241 + ascii = /^(Mon|Tue|Wed|Thu|Fri|Sat|Sun) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) ( \d|\d\d) (\d\d):(\d\d):(\d\d) (\d{4})$/;
242 +
243 +function extractRFC1123Or850(match) {
244 + const [, weekdayStr, dayStr, monthStr, yearStr, hourStr, minuteStr, secondStr] = match,
245 + result = fromStrings(weekdayStr, yearStr, monthStr, dayStr, hourStr, minuteStr, secondStr);
246 + return [result, FixedOffsetZone.utcInstance];
247 +}
248 +
249 +function extractASCII(match) {
250 + const [, weekdayStr, monthStr, dayStr, hourStr, minuteStr, secondStr, yearStr] = match,
251 + result = fromStrings(weekdayStr, yearStr, monthStr, dayStr, hourStr, minuteStr, secondStr);
252 + return [result, FixedOffsetZone.utcInstance];
253 +}
254 +
255 +const isoYmdWithTimeExtensionRegex = combineRegexes(isoYmdRegex, isoTimeExtensionRegex);
256 +const isoWeekWithTimeExtensionRegex = combineRegexes(isoWeekRegex, isoTimeExtensionRegex);
257 +const isoOrdinalWithTimeExtensionRegex = combineRegexes(isoOrdinalRegex, isoTimeExtensionRegex);
258 +const isoTimeCombinedRegex = combineRegexes(isoTimeRegex);
259 +
260 +const extractISOYmdTimeAndOffset = combineExtractors(
261 + extractISOYmd,
262 + extractISOTime,
263 + extractISOOffset
264 +);
265 +const extractISOWeekTimeAndOffset = combineExtractors(
266 + extractISOWeekData,
267 + extractISOTime,
268 + extractISOOffset
269 +);
270 +const extractISOOrdinalDataAndTime = combineExtractors(extractISOOrdinalData, extractISOTime);
271 +const extractISOTimeAndOffset = combineExtractors(extractISOTime, extractISOOffset);
272 +
273 +/**
274 + * @private
275 + */
276 +
277 +export function parseISODate(s) {
278 + return parse(
279 + s,
280 + [isoYmdWithTimeExtensionRegex, extractISOYmdTimeAndOffset],
281 + [isoWeekWithTimeExtensionRegex, extractISOWeekTimeAndOffset],
282 + [isoOrdinalWithTimeExtensionRegex, extractISOOrdinalDataAndTime],
283 + [isoTimeCombinedRegex, extractISOTimeAndOffset]
284 + );
285 +}
286 +
287 +export function parseRFC2822Date(s) {
288 + return parse(preprocessRFC2822(s), [rfc2822, extractRFC2822]);
289 +}
290 +
291 +export function parseHTTPDate(s) {
292 + return parse(
293 + s,
294 + [rfc1123, extractRFC1123Or850],
295 + [rfc850, extractRFC1123Or850],
296 + [ascii, extractASCII]
297 + );
298 +}
299 +
300 +export function parseISODuration(s) {
301 + return parse(s, [isoDuration, extractISODuration]);
302 +}
303 +
304 +const extractISOTimeOnly = combineExtractors(extractISOTime);
305 +
306 +export function parseISOTimeOnly(s) {
307 + return parse(s, [isoTimeOnly, extractISOTimeOnly]);
308 +}
309 +
310 +const sqlYmdWithTimeExtensionRegex = combineRegexes(sqlYmdRegex, sqlTimeExtensionRegex);
311 +const sqlTimeCombinedRegex = combineRegexes(sqlTimeRegex);
312 +
313 +const extractISOYmdTimeOffsetAndIANAZone = combineExtractors(
314 + extractISOYmd,
315 + extractISOTime,
316 + extractISOOffset,
317 + extractIANAZone
318 +);
319 +const extractISOTimeOffsetAndIANAZone = combineExtractors(
320 + extractISOTime,
321 + extractISOOffset,
322 + extractIANAZone
323 +);
324 +
325 +export function parseSQL(s) {
326 + return parse(
327 + s,
328 + [sqlYmdWithTimeExtensionRegex, extractISOYmdTimeOffsetAndIANAZone],
329 + [sqlTimeCombinedRegex, extractISOTimeOffsetAndIANAZone]
330 + );
331 +}
1 +import { parseMillis, isUndefined, untruncateYear, signedOffset, hasOwnProperty } from "./util.js";
2 +import Formatter from "./formatter.js";
3 +import FixedOffsetZone from "../zones/fixedOffsetZone.js";
4 +import IANAZone from "../zones/IANAZone.js";
5 +import DateTime from "../datetime.js";
6 +import { digitRegex, parseDigits } from "./digits.js";
7 +import { ConflictingSpecificationError } from "../errors.js";
8 +
9 +const MISSING_FTP = "missing Intl.DateTimeFormat.formatToParts support";
10 +
11 +function intUnit(regex, post = i => i) {
12 + return { regex, deser: ([s]) => post(parseDigits(s)) };
13 +}
14 +
15 +const NBSP = String.fromCharCode(160);
16 +const spaceOrNBSP = `( |${NBSP})`;
17 +const spaceOrNBSPRegExp = new RegExp(spaceOrNBSP, "g");
18 +
19 +function fixListRegex(s) {
20 + // make dots optional and also make them literal
21 + // make space and non breakable space characters interchangeable
22 + return s.replace(/\./g, "\\.?").replace(spaceOrNBSPRegExp, spaceOrNBSP);
23 +}
24 +
25 +function stripInsensitivities(s) {
26 + return s
27 + .replace(/\./g, "") // ignore dots that were made optional
28 + .replace(spaceOrNBSPRegExp, " ") // interchange space and nbsp
29 + .toLowerCase();
30 +}
31 +
32 +function oneOf(strings, startIndex) {
33 + if (strings === null) {
34 + return null;
35 + } else {
36 + return {
37 + regex: RegExp(strings.map(fixListRegex).join("|")),
38 + deser: ([s]) =>
39 + strings.findIndex(i => stripInsensitivities(s) === stripInsensitivities(i)) + startIndex
40 + };
41 + }
42 +}
43 +
44 +function offset(regex, groups) {
45 + return { regex, deser: ([, h, m]) => signedOffset(h, m), groups };
46 +}
47 +
48 +function simple(regex) {
49 + return { regex, deser: ([s]) => s };
50 +}
51 +
52 +function escapeToken(value) {
53 + // eslint-disable-next-line no-useless-escape
54 + return value.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
55 +}
56 +
57 +function unitForToken(token, loc) {
58 + const one = digitRegex(loc),
59 + two = digitRegex(loc, "{2}"),
60 + three = digitRegex(loc, "{3}"),
61 + four = digitRegex(loc, "{4}"),
62 + six = digitRegex(loc, "{6}"),
63 + oneOrTwo = digitRegex(loc, "{1,2}"),
64 + oneToThree = digitRegex(loc, "{1,3}"),
65 + oneToSix = digitRegex(loc, "{1,6}"),
66 + oneToNine = digitRegex(loc, "{1,9}"),
67 + twoToFour = digitRegex(loc, "{2,4}"),
68 + fourToSix = digitRegex(loc, "{4,6}"),
69 + literal = t => ({ regex: RegExp(escapeToken(t.val)), deser: ([s]) => s, literal: true }),
70 + unitate = t => {
71 + if (token.literal) {
72 + return literal(t);
73 + }
74 + switch (t.val) {
75 + // era
76 + case "G":
77 + return oneOf(loc.eras("short", false), 0);
78 + case "GG":
79 + return oneOf(loc.eras("long", false), 0);
80 + // years
81 + case "y":
82 + return intUnit(oneToSix);
83 + case "yy":
84 + return intUnit(twoToFour, untruncateYear);
85 + case "yyyy":
86 + return intUnit(four);
87 + case "yyyyy":
88 + return intUnit(fourToSix);
89 + case "yyyyyy":
90 + return intUnit(six);
91 + // months
92 + case "M":
93 + return intUnit(oneOrTwo);
94 + case "MM":
95 + return intUnit(two);
96 + case "MMM":
97 + return oneOf(loc.months("short", true, false), 1);
98 + case "MMMM":
99 + return oneOf(loc.months("long", true, false), 1);
100 + case "L":
101 + return intUnit(oneOrTwo);
102 + case "LL":
103 + return intUnit(two);
104 + case "LLL":
105 + return oneOf(loc.months("short", false, false), 1);
106 + case "LLLL":
107 + return oneOf(loc.months("long", false, false), 1);
108 + // dates
109 + case "d":
110 + return intUnit(oneOrTwo);
111 + case "dd":
112 + return intUnit(two);
113 + // ordinals
114 + case "o":
115 + return intUnit(oneToThree);
116 + case "ooo":
117 + return intUnit(three);
118 + // time
119 + case "HH":
120 + return intUnit(two);
121 + case "H":
122 + return intUnit(oneOrTwo);
123 + case "hh":
124 + return intUnit(two);
125 + case "h":
126 + return intUnit(oneOrTwo);
127 + case "mm":
128 + return intUnit(two);
129 + case "m":
130 + return intUnit(oneOrTwo);
131 + case "q":
132 + return intUnit(oneOrTwo);
133 + case "qq":
134 + return intUnit(two);
135 + case "s":
136 + return intUnit(oneOrTwo);
137 + case "ss":
138 + return intUnit(two);
139 + case "S":
140 + return intUnit(oneToThree);
141 + case "SSS":
142 + return intUnit(three);
143 + case "u":
144 + return simple(oneToNine);
145 + // meridiem
146 + case "a":
147 + return oneOf(loc.meridiems(), 0);
148 + // weekYear (k)
149 + case "kkkk":
150 + return intUnit(four);
151 + case "kk":
152 + return intUnit(twoToFour, untruncateYear);
153 + // weekNumber (W)
154 + case "W":
155 + return intUnit(oneOrTwo);
156 + case "WW":
157 + return intUnit(two);
158 + // weekdays
159 + case "E":
160 + case "c":
161 + return intUnit(one);
162 + case "EEE":
163 + return oneOf(loc.weekdays("short", false, false), 1);
164 + case "EEEE":
165 + return oneOf(loc.weekdays("long", false, false), 1);
166 + case "ccc":
167 + return oneOf(loc.weekdays("short", true, false), 1);
168 + case "cccc":
169 + return oneOf(loc.weekdays("long", true, false), 1);
170 + // offset/zone
171 + case "Z":
172 + case "ZZ":
173 + return offset(new RegExp(`([+-]${oneOrTwo.source})(?::(${two.source}))?`), 2);
174 + case "ZZZ":
175 + return offset(new RegExp(`([+-]${oneOrTwo.source})(${two.source})?`), 2);
176 + // we don't support ZZZZ (PST) or ZZZZZ (Pacific Standard Time) in parsing
177 + // because we don't have any way to figure out what they are
178 + case "z":
179 + return simple(/[a-z_+-/]{1,256}?/i);
180 + default:
181 + return literal(t);
182 + }
183 + };
184 +
185 + const unit = unitate(token) || {
186 + invalidReason: MISSING_FTP
187 + };
188 +
189 + unit.token = token;
190 +
191 + return unit;
192 +}
193 +
194 +const partTypeStyleToTokenVal = {
195 + year: {
196 + "2-digit": "yy",
197 + numeric: "yyyyy"
198 + },
199 + month: {
200 + numeric: "M",
201 + "2-digit": "MM",
202 + short: "MMM",
203 + long: "MMMM"
204 + },
205 + day: {
206 + numeric: "d",
207 + "2-digit": "dd"
208 + },
209 + weekday: {
210 + short: "EEE",
211 + long: "EEEE"
212 + },
213 + dayperiod: "a",
214 + dayPeriod: "a",
215 + hour: {
216 + numeric: "h",
217 + "2-digit": "hh"
218 + },
219 + minute: {
220 + numeric: "m",
221 + "2-digit": "mm"
222 + },
223 + second: {
224 + numeric: "s",
225 + "2-digit": "ss"
226 + }
227 +};
228 +
229 +function tokenForPart(part, locale, formatOpts) {
230 + const { type, value } = part;
231 +
232 + if (type === "literal") {
233 + return {
234 + literal: true,
235 + val: value
236 + };
237 + }
238 +
239 + const style = formatOpts[type];
240 +
241 + let val = partTypeStyleToTokenVal[type];
242 + if (typeof val === "object") {
243 + val = val[style];
244 + }
245 +
246 + if (val) {
247 + return {
248 + literal: false,
249 + val
250 + };
251 + }
252 +
253 + return undefined;
254 +}
255 +
256 +function buildRegex(units) {
257 + const re = units.map(u => u.regex).reduce((f, r) => `${f}(${r.source})`, "");
258 + return [`^${re}$`, units];
259 +}
260 +
261 +function match(input, regex, handlers) {
262 + const matches = input.match(regex);
263 +
264 + if (matches) {
265 + const all = {};
266 + let matchIndex = 1;
267 + for (const i in handlers) {
268 + if (hasOwnProperty(handlers, i)) {
269 + const h = handlers[i],
270 + groups = h.groups ? h.groups + 1 : 1;
271 + if (!h.literal && h.token) {
272 + all[h.token.val[0]] = h.deser(matches.slice(matchIndex, matchIndex + groups));
273 + }
274 + matchIndex += groups;
275 + }
276 + }
277 + return [matches, all];
278 + } else {
279 + return [matches, {}];
280 + }
281 +}
282 +
283 +function dateTimeFromMatches(matches) {
284 + const toField = token => {
285 + switch (token) {
286 + case "S":
287 + return "millisecond";
288 + case "s":
289 + return "second";
290 + case "m":
291 + return "minute";
292 + case "h":
293 + case "H":
294 + return "hour";
295 + case "d":
296 + return "day";
297 + case "o":
298 + return "ordinal";
299 + case "L":
300 + case "M":
301 + return "month";
302 + case "y":
303 + return "year";
304 + case "E":
305 + case "c":
306 + return "weekday";
307 + case "W":
308 + return "weekNumber";
309 + case "k":
310 + return "weekYear";
311 + case "q":
312 + return "quarter";
313 + default:
314 + return null;
315 + }
316 + };
317 +
318 + let zone;
319 + if (!isUndefined(matches.Z)) {
320 + zone = new FixedOffsetZone(matches.Z);
321 + } else if (!isUndefined(matches.z)) {
322 + zone = IANAZone.create(matches.z);
323 + } else {
324 + zone = null;
325 + }
326 +
327 + if (!isUndefined(matches.q)) {
328 + matches.M = (matches.q - 1) * 3 + 1;
329 + }
330 +
331 + if (!isUndefined(matches.h)) {
332 + if (matches.h < 12 && matches.a === 1) {
333 + matches.h += 12;
334 + } else if (matches.h === 12 && matches.a === 0) {
335 + matches.h = 0;
336 + }
337 + }
338 +
339 + if (matches.G === 0 && matches.y) {
340 + matches.y = -matches.y;
341 + }
342 +
343 + if (!isUndefined(matches.u)) {
344 + matches.S = parseMillis(matches.u);
345 + }
346 +
347 + const vals = Object.keys(matches).reduce((r, k) => {
348 + const f = toField(k);
349 + if (f) {
350 + r[f] = matches[k];
351 + }
352 +
353 + return r;
354 + }, {});
355 +
356 + return [vals, zone];
357 +}
358 +
359 +let dummyDateTimeCache = null;
360 +
361 +function getDummyDateTime() {
362 + if (!dummyDateTimeCache) {
363 + dummyDateTimeCache = DateTime.fromMillis(1555555555555);
364 + }
365 +
366 + return dummyDateTimeCache;
367 +}
368 +
369 +function maybeExpandMacroToken(token, locale) {
370 + if (token.literal) {
371 + return token;
372 + }
373 +
374 + const formatOpts = Formatter.macroTokenToFormatOpts(token.val);
375 +
376 + if (!formatOpts) {
377 + return token;
378 + }
379 +
380 + const formatter = Formatter.create(locale, formatOpts);
381 + const parts = formatter.formatDateTimeParts(getDummyDateTime());
382 +
383 + const tokens = parts.map(p => tokenForPart(p, locale, formatOpts));
384 +
385 + if (tokens.includes(undefined)) {
386 + return token;
387 + }
388 +
389 + return tokens;
390 +}
391 +
392 +function expandMacroTokens(tokens, locale) {
393 + return Array.prototype.concat(...tokens.map(t => maybeExpandMacroToken(t, locale)));
394 +}
395 +
396 +/**
397 + * @private
398 + */
399 +
400 +export function explainFromTokens(locale, input, format) {
401 + const tokens = expandMacroTokens(Formatter.parseFormat(format), locale),
402 + units = tokens.map(t => unitForToken(t, locale)),
403 + disqualifyingUnit = units.find(t => t.invalidReason);
404 +
405 + if (disqualifyingUnit) {
406 + return { input, tokens, invalidReason: disqualifyingUnit.invalidReason };
407 + } else {
408 + const [regexString, handlers] = buildRegex(units),
409 + regex = RegExp(regexString, "i"),
410 + [rawMatches, matches] = match(input, regex, handlers),
411 + [result, zone] = matches ? dateTimeFromMatches(matches) : [null, null];
412 + if (hasOwnProperty(matches, "a") && hasOwnProperty(matches, "H")) {
413 + throw new ConflictingSpecificationError(
414 + "Can't include meridiem when specifying 24-hour format"
415 + );
416 + }
417 + return { input, tokens, regex, rawMatches, matches, result, zone };
418 + }
419 +}
420 +
421 +export function parseFromTokens(locale, input, format) {
422 + const { result, zone, invalidReason } = explainFromTokens(locale, input, format);
423 + return [result, zone, invalidReason];
424 +}
1 +/*
2 + This is just a junk drawer, containing anything used across multiple classes.
3 + Because Luxon is small(ish), this should stay small and we won't worry about splitting
4 + it up into, say, parsingUtil.js and basicUtil.js and so on. But they are divided up by feature area.
5 +*/
6 +
7 +import { InvalidArgumentError } from "../errors.js";
8 +
9 +/**
10 + * @private
11 + */
12 +
13 +// TYPES
14 +
15 +export function isUndefined(o) {
16 + return typeof o === "undefined";
17 +}
18 +
19 +export function isNumber(o) {
20 + return typeof o === "number";
21 +}
22 +
23 +export function isInteger(o) {
24 + return typeof o === "number" && o % 1 === 0;
25 +}
26 +
27 +export function isString(o) {
28 + return typeof o === "string";
29 +}
30 +
31 +export function isDate(o) {
32 + return Object.prototype.toString.call(o) === "[object Date]";
33 +}
34 +
35 +// CAPABILITIES
36 +
37 +export function hasIntl() {
38 + try {
39 + return typeof Intl !== "undefined" && Intl.DateTimeFormat;
40 + } catch (e) {
41 + return false;
42 + }
43 +}
44 +
45 +export function hasFormatToParts() {
46 + return !isUndefined(Intl.DateTimeFormat.prototype.formatToParts);
47 +}
48 +
49 +export function hasRelative() {
50 + try {
51 + return typeof Intl !== "undefined" && !!Intl.RelativeTimeFormat;
52 + } catch (e) {
53 + return false;
54 + }
55 +}
56 +
57 +// OBJECTS AND ARRAYS
58 +
59 +export function maybeArray(thing) {
60 + return Array.isArray(thing) ? thing : [thing];
61 +}
62 +
63 +export function bestBy(arr, by, compare) {
64 + if (arr.length === 0) {
65 + return undefined;
66 + }
67 + return arr.reduce((best, next) => {
68 + const pair = [by(next), next];
69 + if (!best) {
70 + return pair;
71 + } else if (compare(best[0], pair[0]) === best[0]) {
72 + return best;
73 + } else {
74 + return pair;
75 + }
76 + }, null)[1];
77 +}
78 +
79 +export function pick(obj, keys) {
80 + return keys.reduce((a, k) => {
81 + a[k] = obj[k];
82 + return a;
83 + }, {});
84 +}
85 +
86 +export function hasOwnProperty(obj, prop) {
87 + return Object.prototype.hasOwnProperty.call(obj, prop);
88 +}
89 +
90 +// NUMBERS AND STRINGS
91 +
92 +export function integerBetween(thing, bottom, top) {
93 + return isInteger(thing) && thing >= bottom && thing <= top;
94 +}
95 +
96 +// x % n but takes the sign of n instead of x
97 +export function floorMod(x, n) {
98 + return x - n * Math.floor(x / n);
99 +}
100 +
101 +export function padStart(input, n = 2) {
102 + const minus = input < 0 ? "-" : "";
103 + const target = minus ? input * -1 : input;
104 + let result;
105 +
106 + if (target.toString().length < n) {
107 + result = ("0".repeat(n) + target).slice(-n);
108 + } else {
109 + result = target.toString();
110 + }
111 +
112 + return `${minus}${result}`;
113 +}
114 +
115 +export function parseInteger(string) {
116 + if (isUndefined(string) || string === null || string === "") {
117 + return undefined;
118 + } else {
119 + return parseInt(string, 10);
120 + }
121 +}
122 +
123 +export function parseMillis(fraction) {
124 + // Return undefined (instead of 0) in these cases, where fraction is not set
125 + if (isUndefined(fraction) || fraction === null || fraction === "") {
126 + return undefined;
127 + } else {
128 + const f = parseFloat("0." + fraction) * 1000;
129 + return Math.floor(f);
130 + }
131 +}
132 +
133 +export function roundTo(number, digits, towardZero = false) {
134 + const factor = 10 ** digits,
135 + rounder = towardZero ? Math.trunc : Math.round;
136 + return rounder(number * factor) / factor;
137 +}
138 +
139 +// DATE BASICS
140 +
141 +export function isLeapYear(year) {
142 + return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
143 +}
144 +
145 +export function daysInYear(year) {
146 + return isLeapYear(year) ? 366 : 365;
147 +}
148 +
149 +export function daysInMonth(year, month) {
150 + const modMonth = floorMod(month - 1, 12) + 1,
151 + modYear = year + (month - modMonth) / 12;
152 +
153 + if (modMonth === 2) {
154 + return isLeapYear(modYear) ? 29 : 28;
155 + } else {
156 + return [31, null, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][modMonth - 1];
157 + }
158 +}
159 +
160 +// covert a calendar object to a local timestamp (epoch, but with the offset baked in)
161 +export function objToLocalTS(obj) {
162 + let d = Date.UTC(
163 + obj.year,
164 + obj.month - 1,
165 + obj.day,
166 + obj.hour,
167 + obj.minute,
168 + obj.second,
169 + obj.millisecond
170 + );
171 +
172 + // for legacy reasons, years between 0 and 99 are interpreted as 19XX; revert that
173 + if (obj.year < 100 && obj.year >= 0) {
174 + d = new Date(d);
175 + d.setUTCFullYear(d.getUTCFullYear() - 1900);
176 + }
177 + return +d;
178 +}
179 +
180 +export function weeksInWeekYear(weekYear) {
181 + const p1 =
182 + (weekYear +
183 + Math.floor(weekYear / 4) -
184 + Math.floor(weekYear / 100) +
185 + Math.floor(weekYear / 400)) %
186 + 7,
187 + last = weekYear - 1,
188 + p2 = (last + Math.floor(last / 4) - Math.floor(last / 100) + Math.floor(last / 400)) % 7;
189 + return p1 === 4 || p2 === 3 ? 53 : 52;
190 +}
191 +
192 +export function untruncateYear(year) {
193 + if (year > 99) {
194 + return year;
195 + } else return year > 60 ? 1900 + year : 2000 + year;
196 +}
197 +
198 +// PARSING
199 +
200 +export function parseZoneInfo(ts, offsetFormat, locale, timeZone = null) {
201 + const date = new Date(ts),
202 + intlOpts = {
203 + hour12: false,
204 + year: "numeric",
205 + month: "2-digit",
206 + day: "2-digit",
207 + hour: "2-digit",
208 + minute: "2-digit"
209 + };
210 +
211 + if (timeZone) {
212 + intlOpts.timeZone = timeZone;
213 + }
214 +
215 + const modified = Object.assign({ timeZoneName: offsetFormat }, intlOpts),
216 + intl = hasIntl();
217 +
218 + if (intl && hasFormatToParts()) {
219 + const parsed = new Intl.DateTimeFormat(locale, modified)
220 + .formatToParts(date)
221 + .find(m => m.type.toLowerCase() === "timezonename");
222 + return parsed ? parsed.value : null;
223 + } else if (intl) {
224 + // this probably doesn't work for all locales
225 + const without = new Intl.DateTimeFormat(locale, intlOpts).format(date),
226 + included = new Intl.DateTimeFormat(locale, modified).format(date),
227 + diffed = included.substring(without.length),
228 + trimmed = diffed.replace(/^[, \u200e]+/, "");
229 + return trimmed;
230 + } else {
231 + return null;
232 + }
233 +}
234 +
235 +// signedOffset('-5', '30') -> -330
236 +export function signedOffset(offHourStr, offMinuteStr) {
237 + let offHour = parseInt(offHourStr, 10);
238 +
239 + // don't || this because we want to preserve -0
240 + if (Number.isNaN(offHour)) {
241 + offHour = 0;
242 + }
243 +
244 + const offMin = parseInt(offMinuteStr, 10) || 0,
245 + offMinSigned = offHour < 0 || Object.is(offHour, -0) ? -offMin : offMin;
246 + return offHour * 60 + offMinSigned;
247 +}
248 +
249 +// COERCION
250 +
251 +export function asNumber(value) {
252 + const numericValue = Number(value);
253 + if (typeof value === "boolean" || value === "" || Number.isNaN(numericValue))
254 + throw new InvalidArgumentError(`Invalid unit value ${value}`);
255 + return numericValue;
256 +}
257 +
258 +export function normalizeObject(obj, normalizer, nonUnitKeys) {
259 + const normalized = {};
260 + for (const u in obj) {
261 + if (hasOwnProperty(obj, u)) {
262 + if (nonUnitKeys.indexOf(u) >= 0) continue;
263 + const v = obj[u];
264 + if (v === undefined || v === null) continue;
265 + normalized[normalizer(u)] = asNumber(v);
266 + }
267 + }
268 + return normalized;
269 +}
270 +
271 +export function formatOffset(offset, format) {
272 + const hours = Math.trunc(Math.abs(offset / 60)),
273 + minutes = Math.trunc(Math.abs(offset % 60)),
274 + sign = offset >= 0 ? "+" : "-";
275 +
276 + switch (format) {
277 + case "short":
278 + return `${sign}${padStart(hours, 2)}:${padStart(minutes, 2)}`;
279 + case "narrow":
280 + return `${sign}${hours}${minutes > 0 ? `:${minutes}` : ""}`;
281 + case "techie":
282 + return `${sign}${padStart(hours, 2)}${padStart(minutes, 2)}`;
283 + default:
284 + throw new RangeError(`Value format ${format} is out of range for property format`);
285 + }
286 +}
287 +
288 +export function timeObject(obj) {
289 + return pick(obj, ["hour", "minute", "second", "millisecond"]);
290 +}
291 +
292 +export const ianaRegex = /[A-Za-z_+-]{1,256}(:?\/[A-Za-z_+-]{1,256}(\/[A-Za-z_+-]{1,256})?)?/;
1 +/**
2 + * @private
3 + */
4 +
5 +import Zone from "../zone.js";
6 +import IANAZone from "../zones/IANAZone.js";
7 +import FixedOffsetZone from "../zones/fixedOffsetZone.js";
8 +import InvalidZone from "../zones/invalidZone.js";
9 +
10 +import { isUndefined, isString, isNumber } from "./util.js";
11 +
12 +export function normalizeZone(input, defaultZone) {
13 + let offset;
14 + if (isUndefined(input) || input === null) {
15 + return defaultZone;
16 + } else if (input instanceof Zone) {
17 + return input;
18 + } else if (isString(input)) {
19 + const lowered = input.toLowerCase();
20 + if (lowered === "local") return defaultZone;
21 + else if (lowered === "utc" || lowered === "gmt") return FixedOffsetZone.utcInstance;
22 + else if ((offset = IANAZone.parseGMTOffset(input)) != null) {
23 + // handle Etc/GMT-4, which V8 chokes on
24 + return FixedOffsetZone.instance(offset);
25 + } else if (IANAZone.isValidSpecifier(lowered)) return IANAZone.create(input);
26 + else return FixedOffsetZone.parseSpecifier(lowered) || new InvalidZone(input);
27 + } else if (isNumber(input)) {
28 + return FixedOffsetZone.instance(input);
29 + } else if (typeof input === "object" && input.offset && typeof input.offset === "number") {
30 + // This is dumb, but the instanceof check above doesn't seem to really work
31 + // so we're duck checking it
32 + return input;
33 + } else {
34 + return new InvalidZone(input);
35 + }
36 +}
1 +import DateTime from "./datetime.js";
2 +import Settings from "./settings.js";
3 +import Locale from "./impl/locale.js";
4 +import IANAZone from "./zones/IANAZone.js";
5 +import { normalizeZone } from "./impl/zoneUtil.js";
6 +
7 +import { hasFormatToParts, hasIntl, hasRelative } from "./impl/util.js";
8 +
9 +/**
10 + * The Info class contains static methods for retrieving general time and date related data. For example, it has methods for finding out if a time zone has a DST, for listing the months in any supported locale, and for discovering which of Luxon features are available in the current environment.
11 + */
12 +export default class Info {
13 + /**
14 + * Return whether the specified zone contains a DST.
15 + * @param {string|Zone} [zone='local'] - Zone to check. Defaults to the environment's local zone.
16 + * @return {boolean}
17 + */
18 + static hasDST(zone = Settings.defaultZone) {
19 + const proto = DateTime.now()
20 + .setZone(zone)
21 + .set({ month: 12 });
22 +
23 + return !zone.universal && proto.offset !== proto.set({ month: 6 }).offset;
24 + }
25 +
26 + /**
27 + * Return whether the specified zone is a valid IANA specifier.
28 + * @param {string} zone - Zone to check
29 + * @return {boolean}
30 + */
31 + static isValidIANAZone(zone) {
32 + return IANAZone.isValidSpecifier(zone) && IANAZone.isValidZone(zone);
33 + }
34 +
35 + /**
36 + * Converts the input into a {@link Zone} instance.
37 + *
38 + * * If `input` is already a Zone instance, it is returned unchanged.
39 + * * If `input` is a string containing a valid time zone name, a Zone instance
40 + * with that name is returned.
41 + * * If `input` is a string that doesn't refer to a known time zone, a Zone
42 + * instance with {@link Zone.isValid} == false is returned.
43 + * * If `input is a number, a Zone instance with the specified fixed offset
44 + * in minutes is returned.
45 + * * If `input` is `null` or `undefined`, the default zone is returned.
46 + * @param {string|Zone|number} [input] - the value to be converted
47 + * @return {Zone}
48 + */
49 + static normalizeZone(input) {
50 + return normalizeZone(input, Settings.defaultZone);
51 + }
52 +
53 + /**
54 + * Return an array of standalone month names.
55 + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat
56 + * @param {string} [length='long'] - the length of the month representation, such as "numeric", "2-digit", "narrow", "short", "long"
57 + * @param {Object} opts - options
58 + * @param {string} [opts.locale] - the locale code
59 + * @param {string} [opts.numberingSystem=null] - the numbering system
60 + * @param {string} [opts.locObj=null] - an existing locale object to use
61 + * @param {string} [opts.outputCalendar='gregory'] - the calendar
62 + * @example Info.months()[0] //=> 'January'
63 + * @example Info.months('short')[0] //=> 'Jan'
64 + * @example Info.months('numeric')[0] //=> '1'
65 + * @example Info.months('short', { locale: 'fr-CA' } )[0] //=> 'janv.'
66 + * @example Info.months('numeric', { locale: 'ar' })[0] //=> '١'
67 + * @example Info.months('long', { outputCalendar: 'islamic' })[0] //=> 'Rabiʻ I'
68 + * @return {[string]}
69 + */
70 + static months(
71 + length = "long",
72 + { locale = null, numberingSystem = null, locObj = null, outputCalendar = "gregory" } = {}
73 + ) {
74 + return (locObj || Locale.create(locale, numberingSystem, outputCalendar)).months(length);
75 + }
76 +
77 + /**
78 + * Return an array of format month names.
79 + * Format months differ from standalone months in that they're meant to appear next to the day of the month. In some languages, that
80 + * changes the string.
81 + * See {@link months}
82 + * @param {string} [length='long'] - the length of the month representation, such as "numeric", "2-digit", "narrow", "short", "long"
83 + * @param {Object} opts - options
84 + * @param {string} [opts.locale] - the locale code
85 + * @param {string} [opts.numberingSystem=null] - the numbering system
86 + * @param {string} [opts.locObj=null] - an existing locale object to use
87 + * @param {string} [opts.outputCalendar='gregory'] - the calendar
88 + * @return {[string]}
89 + */
90 + static monthsFormat(
91 + length = "long",
92 + { locale = null, numberingSystem = null, locObj = null, outputCalendar = "gregory" } = {}
93 + ) {
94 + return (locObj || Locale.create(locale, numberingSystem, outputCalendar)).months(length, true);
95 + }
96 +
97 + /**
98 + * Return an array of standalone week names.
99 + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat
100 + * @param {string} [length='long'] - the length of the weekday representation, such as "narrow", "short", "long".
101 + * @param {Object} opts - options
102 + * @param {string} [opts.locale] - the locale code
103 + * @param {string} [opts.numberingSystem=null] - the numbering system
104 + * @param {string} [opts.locObj=null] - an existing locale object to use
105 + * @example Info.weekdays()[0] //=> 'Monday'
106 + * @example Info.weekdays('short')[0] //=> 'Mon'
107 + * @example Info.weekdays('short', { locale: 'fr-CA' })[0] //=> 'lun.'
108 + * @example Info.weekdays('short', { locale: 'ar' })[0] //=> 'الاثنين'
109 + * @return {[string]}
110 + */
111 + static weekdays(length = "long", { locale = null, numberingSystem = null, locObj = null } = {}) {
112 + return (locObj || Locale.create(locale, numberingSystem, null)).weekdays(length);
113 + }
114 +
115 + /**
116 + * Return an array of format week names.
117 + * Format weekdays differ from standalone weekdays in that they're meant to appear next to more date information. In some languages, that
118 + * changes the string.
119 + * See {@link weekdays}
120 + * @param {string} [length='long'] - the length of the weekday representation, such as "narrow", "short", "long".
121 + * @param {Object} opts - options
122 + * @param {string} [opts.locale=null] - the locale code
123 + * @param {string} [opts.numberingSystem=null] - the numbering system
124 + * @param {string} [opts.locObj=null] - an existing locale object to use
125 + * @return {[string]}
126 + */
127 + static weekdaysFormat(
128 + length = "long",
129 + { locale = null, numberingSystem = null, locObj = null } = {}
130 + ) {
131 + return (locObj || Locale.create(locale, numberingSystem, null)).weekdays(length, true);
132 + }
133 +
134 + /**
135 + * Return an array of meridiems.
136 + * @param {Object} opts - options
137 + * @param {string} [opts.locale] - the locale code
138 + * @example Info.meridiems() //=> [ 'AM', 'PM' ]
139 + * @example Info.meridiems({ locale: 'my' }) //=> [ 'နံနက်', 'ညနေ' ]
140 + * @return {[string]}
141 + */
142 + static meridiems({ locale = null } = {}) {
143 + return Locale.create(locale).meridiems();
144 + }
145 +
146 + /**
147 + * Return an array of eras, such as ['BC', 'AD']. The locale can be specified, but the calendar system is always Gregorian.
148 + * @param {string} [length='short'] - the length of the era representation, such as "short" or "long".
149 + * @param {Object} opts - options
150 + * @param {string} [opts.locale] - the locale code
151 + * @example Info.eras() //=> [ 'BC', 'AD' ]
152 + * @example Info.eras('long') //=> [ 'Before Christ', 'Anno Domini' ]
153 + * @example Info.eras('long', { locale: 'fr' }) //=> [ 'avant Jésus-Christ', 'après Jésus-Christ' ]
154 + * @return {[string]}
155 + */
156 + static eras(length = "short", { locale = null } = {}) {
157 + return Locale.create(locale, null, "gregory").eras(length);
158 + }
159 +
160 + /**
161 + * Return the set of available features in this environment.
162 + * Some features of Luxon are not available in all environments. For example, on older browsers, timezone support is not available. Use this function to figure out if that's the case.
163 + * Keys:
164 + * * `zones`: whether this environment supports IANA timezones
165 + * * `intlTokens`: whether this environment supports internationalized token-based formatting/parsing
166 + * * `intl`: whether this environment supports general internationalization
167 + * * `relative`: whether this environment supports relative time formatting
168 + * @example Info.features() //=> { intl: true, intlTokens: false, zones: true, relative: false }
169 + * @return {Object}
170 + */
171 + static features() {
172 + let intl = false,
173 + intlTokens = false,
174 + zones = false,
175 + relative = false;
176 +
177 + if (hasIntl()) {
178 + intl = true;
179 + intlTokens = hasFormatToParts();
180 + relative = hasRelative();
181 +
182 + try {
183 + zones =
184 + new Intl.DateTimeFormat("en", { timeZone: "America/New_York" }).resolvedOptions()
185 + .timeZone === "America/New_York";
186 + } catch (e) {
187 + zones = false;
188 + }
189 + }
190 +
191 + return { intl, intlTokens, zones, relative };
192 + }
193 +}
1 +import DateTime, { friendlyDateTime } from "./datetime.js";
2 +import Duration, { friendlyDuration } from "./duration.js";
3 +import Settings from "./settings.js";
4 +import { InvalidArgumentError, InvalidIntervalError } from "./errors.js";
5 +import Invalid from "./impl/invalid.js";
6 +
7 +const INVALID = "Invalid Interval";
8 +
9 +// checks if the start is equal to or before the end
10 +function validateStartEnd(start, end) {
11 + if (!start || !start.isValid) {
12 + return Interval.invalid("missing or invalid start");
13 + } else if (!end || !end.isValid) {
14 + return Interval.invalid("missing or invalid end");
15 + } else if (end < start) {
16 + return Interval.invalid(
17 + "end before start",
18 + `The end of an interval must be after its start, but you had start=${start.toISO()} and end=${end.toISO()}`
19 + );
20 + } else {
21 + return null;
22 + }
23 +}
24 +
25 +/**
26 + * An Interval object represents a half-open interval of time, where each endpoint is a {@link DateTime}. Conceptually, it's a container for those two endpoints, accompanied by methods for creating, parsing, interrogating, comparing, transforming, and formatting them.
27 + *
28 + * Here is a brief overview of the most commonly used methods and getters in Interval:
29 + *
30 + * * **Creation** To create an Interval, use {@link fromDateTimes}, {@link after}, {@link before}, or {@link fromISO}.
31 + * * **Accessors** Use {@link start} and {@link end} to get the start and end.
32 + * * **Interrogation** To analyze the Interval, use {@link count}, {@link length}, {@link hasSame}, {@link contains}, {@link isAfter}, or {@link isBefore}.
33 + * * **Transformation** To create other Intervals out of this one, use {@link set}, {@link splitAt}, {@link splitBy}, {@link divideEqually}, {@link merge}, {@link xor}, {@link union}, {@link intersection}, or {@link difference}.
34 + * * **Comparison** To compare this Interval to another one, use {@link equals}, {@link overlaps}, {@link abutsStart}, {@link abutsEnd}, {@link engulfs}.
35 + * * **Output** To convert the Interval into other representations, see {@link toString}, {@link toISO}, {@link toISODate}, {@link toISOTime}, {@link toFormat}, and {@link toDuration}.
36 + */
37 +export default class Interval {
38 + /**
39 + * @private
40 + */
41 + constructor(config) {
42 + /**
43 + * @access private
44 + */
45 + this.s = config.start;
46 + /**
47 + * @access private
48 + */
49 + this.e = config.end;
50 + /**
51 + * @access private
52 + */
53 + this.invalid = config.invalid || null;
54 + /**
55 + * @access private
56 + */
57 + this.isLuxonInterval = true;
58 + }
59 +
60 + /**
61 + * Create an invalid Interval.
62 + * @param {string} reason - simple string of why this Interval is invalid. Should not contain parameters or anything else data-dependent
63 + * @param {string} [explanation=null] - longer explanation, may include parameters and other useful debugging information
64 + * @return {Interval}
65 + */
66 + static invalid(reason, explanation = null) {
67 + if (!reason) {
68 + throw new InvalidArgumentError("need to specify a reason the Interval is invalid");
69 + }
70 +
71 + const invalid = reason instanceof Invalid ? reason : new Invalid(reason, explanation);
72 +
73 + if (Settings.throwOnInvalid) {
74 + throw new InvalidIntervalError(invalid);
75 + } else {
76 + return new Interval({ invalid });
77 + }
78 + }
79 +
80 + /**
81 + * Create an Interval from a start DateTime and an end DateTime. Inclusive of the start but not the end.
82 + * @param {DateTime|Date|Object} start
83 + * @param {DateTime|Date|Object} end
84 + * @return {Interval}
85 + */
86 + static fromDateTimes(start, end) {
87 + const builtStart = friendlyDateTime(start),
88 + builtEnd = friendlyDateTime(end);
89 +
90 + const validateError = validateStartEnd(builtStart, builtEnd);
91 +
92 + if (validateError == null) {
93 + return new Interval({
94 + start: builtStart,
95 + end: builtEnd
96 + });
97 + } else {
98 + return validateError;
99 + }
100 + }
101 +
102 + /**
103 + * Create an Interval from a start DateTime and a Duration to extend to.
104 + * @param {DateTime|Date|Object} start
105 + * @param {Duration|Object|number} duration - the length of the Interval.
106 + * @return {Interval}
107 + */
108 + static after(start, duration) {
109 + const dur = friendlyDuration(duration),
110 + dt = friendlyDateTime(start);
111 + return Interval.fromDateTimes(dt, dt.plus(dur));
112 + }
113 +
114 + /**
115 + * Create an Interval from an end DateTime and a Duration to extend backwards to.
116 + * @param {DateTime|Date|Object} end
117 + * @param {Duration|Object|number} duration - the length of the Interval.
118 + * @return {Interval}
119 + */
120 + static before(end, duration) {
121 + const dur = friendlyDuration(duration),
122 + dt = friendlyDateTime(end);
123 + return Interval.fromDateTimes(dt.minus(dur), dt);
124 + }
125 +
126 + /**
127 + * Create an Interval from an ISO 8601 string.
128 + * Accepts `<start>/<end>`, `<start>/<duration>`, and `<duration>/<end>` formats.
129 + * @param {string} text - the ISO string to parse
130 + * @param {Object} [opts] - options to pass {@link DateTime.fromISO} and optionally {@link Duration.fromISO}
131 + * @see https://en.wikipedia.org/wiki/ISO_8601#Time_intervals
132 + * @return {Interval}
133 + */
134 + static fromISO(text, opts) {
135 + const [s, e] = (text || "").split("/", 2);
136 + if (s && e) {
137 + let start, startIsValid;
138 + try {
139 + start = DateTime.fromISO(s, opts);
140 + startIsValid = start.isValid;
141 + } catch (e) {
142 + startIsValid = false;
143 + }
144 +
145 + let end, endIsValid;
146 + try {
147 + end = DateTime.fromISO(e, opts);
148 + endIsValid = end.isValid;
149 + } catch (e) {
150 + endIsValid = false;
151 + }
152 +
153 + if (startIsValid && endIsValid) {
154 + return Interval.fromDateTimes(start, end);
155 + }
156 +
157 + if (startIsValid) {
158 + const dur = Duration.fromISO(e, opts);
159 + if (dur.isValid) {
160 + return Interval.after(start, dur);
161 + }
162 + } else if (endIsValid) {
163 + const dur = Duration.fromISO(s, opts);
164 + if (dur.isValid) {
165 + return Interval.before(end, dur);
166 + }
167 + }
168 + }
169 + return Interval.invalid("unparsable", `the input "${text}" can't be parsed as ISO 8601`);
170 + }
171 +
172 + /**
173 + * Check if an object is an Interval. Works across context boundaries
174 + * @param {object} o
175 + * @return {boolean}
176 + */
177 + static isInterval(o) {
178 + return (o && o.isLuxonInterval) || false;
179 + }
180 +
181 + /**
182 + * Returns the start of the Interval
183 + * @type {DateTime}
184 + */
185 + get start() {
186 + return this.isValid ? this.s : null;
187 + }
188 +
189 + /**
190 + * Returns the end of the Interval
191 + * @type {DateTime}
192 + */
193 + get end() {
194 + return this.isValid ? this.e : null;
195 + }
196 +
197 + /**
198 + * Returns whether this Interval's end is at least its start, meaning that the Interval isn't 'backwards'.
199 + * @type {boolean}
200 + */
201 + get isValid() {
202 + return this.invalidReason === null;
203 + }
204 +
205 + /**
206 + * Returns an error code if this Interval is invalid, or null if the Interval is valid
207 + * @type {string}
208 + */
209 + get invalidReason() {
210 + return this.invalid ? this.invalid.reason : null;
211 + }
212 +
213 + /**
214 + * Returns an explanation of why this Interval became invalid, or null if the Interval is valid
215 + * @type {string}
216 + */
217 + get invalidExplanation() {
218 + return this.invalid ? this.invalid.explanation : null;
219 + }
220 +
221 + /**
222 + * Returns the length of the Interval in the specified unit.
223 + * @param {string} unit - the unit (such as 'hours' or 'days') to return the length in.
224 + * @return {number}
225 + */
226 + length(unit = "milliseconds") {
227 + return this.isValid ? this.toDuration(...[unit]).get(unit) : NaN;
228 + }
229 +
230 + /**
231 + * Returns the count of minutes, hours, days, months, or years included in the Interval, even in part.
232 + * Unlike {@link length} this counts sections of the calendar, not periods of time, e.g. specifying 'day'
233 + * asks 'what dates are included in this interval?', not 'how many days long is this interval?'
234 + * @param {string} [unit='milliseconds'] - the unit of time to count.
235 + * @return {number}
236 + */
237 + count(unit = "milliseconds") {
238 + if (!this.isValid) return NaN;
239 + const start = this.start.startOf(unit),
240 + end = this.end.startOf(unit);
241 + return Math.floor(end.diff(start, unit).get(unit)) + 1;
242 + }
243 +
244 + /**
245 + * Returns whether this Interval's start and end are both in the same unit of time
246 + * @param {string} unit - the unit of time to check sameness on
247 + * @return {boolean}
248 + */
249 + hasSame(unit) {
250 + return this.isValid ? this.isEmpty() || this.e.minus(1).hasSame(this.s, unit) : false;
251 + }
252 +
253 + /**
254 + * Return whether this Interval has the same start and end DateTimes.
255 + * @return {boolean}
256 + */
257 + isEmpty() {
258 + return this.s.valueOf() === this.e.valueOf();
259 + }
260 +
261 + /**
262 + * Return whether this Interval's start is after the specified DateTime.
263 + * @param {DateTime} dateTime
264 + * @return {boolean}
265 + */
266 + isAfter(dateTime) {
267 + if (!this.isValid) return false;
268 + return this.s > dateTime;
269 + }
270 +
271 + /**
272 + * Return whether this Interval's end is before the specified DateTime.
273 + * @param {DateTime} dateTime
274 + * @return {boolean}
275 + */
276 + isBefore(dateTime) {
277 + if (!this.isValid) return false;
278 + return this.e <= dateTime;
279 + }
280 +
281 + /**
282 + * Return whether this Interval contains the specified DateTime.
283 + * @param {DateTime} dateTime
284 + * @return {boolean}
285 + */
286 + contains(dateTime) {
287 + if (!this.isValid) return false;
288 + return this.s <= dateTime && this.e > dateTime;
289 + }
290 +
291 + /**
292 + * "Sets" the start and/or end dates. Returns a newly-constructed Interval.
293 + * @param {Object} values - the values to set
294 + * @param {DateTime} values.start - the starting DateTime
295 + * @param {DateTime} values.end - the ending DateTime
296 + * @return {Interval}
297 + */
298 + set({ start, end } = {}) {
299 + if (!this.isValid) return this;
300 + return Interval.fromDateTimes(start || this.s, end || this.e);
301 + }
302 +
303 + /**
304 + * Split this Interval at each of the specified DateTimes
305 + * @param {...[DateTime]} dateTimes - the unit of time to count.
306 + * @return {[Interval]}
307 + */
308 + splitAt(...dateTimes) {
309 + if (!this.isValid) return [];
310 + const sorted = dateTimes
311 + .map(friendlyDateTime)
312 + .filter(d => this.contains(d))
313 + .sort(),
314 + results = [];
315 + let { s } = this,
316 + i = 0;
317 +
318 + while (s < this.e) {
319 + const added = sorted[i] || this.e,
320 + next = +added > +this.e ? this.e : added;
321 + results.push(Interval.fromDateTimes(s, next));
322 + s = next;
323 + i += 1;
324 + }
325 +
326 + return results;
327 + }
328 +
329 + /**
330 + * Split this Interval into smaller Intervals, each of the specified length.
331 + * Left over time is grouped into a smaller interval
332 + * @param {Duration|Object|number} duration - The length of each resulting interval.
333 + * @return {[Interval]}
334 + */
335 + splitBy(duration) {
336 + const dur = friendlyDuration(duration);
337 +
338 + if (!this.isValid || !dur.isValid || dur.as("milliseconds") === 0) {
339 + return [];
340 + }
341 +
342 + let { s } = this,
343 + idx = 1,
344 + next;
345 +
346 + const results = [];
347 + while (s < this.e) {
348 + const added = this.start.plus(dur.mapUnits(x => x * idx));
349 + next = +added > +this.e ? this.e : added;
350 + results.push(Interval.fromDateTimes(s, next));
351 + s = next;
352 + idx += 1;
353 + }
354 +
355 + return results;
356 + }
357 +
358 + /**
359 + * Split this Interval into the specified number of smaller intervals.
360 + * @param {number} numberOfParts - The number of Intervals to divide the Interval into.
361 + * @return {[Interval]}
362 + */
363 + divideEqually(numberOfParts) {
364 + if (!this.isValid) return [];
365 + return this.splitBy(this.length() / numberOfParts).slice(0, numberOfParts);
366 + }
367 +
368 + /**
369 + * Return whether this Interval overlaps with the specified Interval
370 + * @param {Interval} other
371 + * @return {boolean}
372 + */
373 + overlaps(other) {
374 + return this.e > other.s && this.s < other.e;
375 + }
376 +
377 + /**
378 + * Return whether this Interval's end is adjacent to the specified Interval's start.
379 + * @param {Interval} other
380 + * @return {boolean}
381 + */
382 + abutsStart(other) {
383 + if (!this.isValid) return false;
384 + return +this.e === +other.s;
385 + }
386 +
387 + /**
388 + * Return whether this Interval's start is adjacent to the specified Interval's end.
389 + * @param {Interval} other
390 + * @return {boolean}
391 + */
392 + abutsEnd(other) {
393 + if (!this.isValid) return false;
394 + return +other.e === +this.s;
395 + }
396 +
397 + /**
398 + * Return whether this Interval engulfs the start and end of the specified Interval.
399 + * @param {Interval} other
400 + * @return {boolean}
401 + */
402 + engulfs(other) {
403 + if (!this.isValid) return false;
404 + return this.s <= other.s && this.e >= other.e;
405 + }
406 +
407 + /**
408 + * Return whether this Interval has the same start and end as the specified Interval.
409 + * @param {Interval} other
410 + * @return {boolean}
411 + */
412 + equals(other) {
413 + if (!this.isValid || !other.isValid) {
414 + return false;
415 + }
416 +
417 + return this.s.equals(other.s) && this.e.equals(other.e);
418 + }
419 +
420 + /**
421 + * Return an Interval representing the intersection of this Interval and the specified Interval.
422 + * Specifically, the resulting Interval has the maximum start time and the minimum end time of the two Intervals.
423 + * Returns null if the intersection is empty, meaning, the intervals don't intersect.
424 + * @param {Interval} other
425 + * @return {Interval}
426 + */
427 + intersection(other) {
428 + if (!this.isValid) return this;
429 + const s = this.s > other.s ? this.s : other.s,
430 + e = this.e < other.e ? this.e : other.e;
431 +
432 + if (s >= e) {
433 + return null;
434 + } else {
435 + return Interval.fromDateTimes(s, e);
436 + }
437 + }
438 +
439 + /**
440 + * Return an Interval representing the union of this Interval and the specified Interval.
441 + * Specifically, the resulting Interval has the minimum start time and the maximum end time of the two Intervals.
442 + * @param {Interval} other
443 + * @return {Interval}
444 + */
445 + union(other) {
446 + if (!this.isValid) return this;
447 + const s = this.s < other.s ? this.s : other.s,
448 + e = this.e > other.e ? this.e : other.e;
449 + return Interval.fromDateTimes(s, e);
450 + }
451 +
452 + /**
453 + * Merge an array of Intervals into a equivalent minimal set of Intervals.
454 + * Combines overlapping and adjacent Intervals.
455 + * @param {[Interval]} intervals
456 + * @return {[Interval]}
457 + */
458 + static merge(intervals) {
459 + const [found, final] = intervals.sort((a, b) => a.s - b.s).reduce(
460 + ([sofar, current], item) => {
461 + if (!current) {
462 + return [sofar, item];
463 + } else if (current.overlaps(item) || current.abutsStart(item)) {
464 + return [sofar, current.union(item)];
465 + } else {
466 + return [sofar.concat([current]), item];
467 + }
468 + },
469 + [[], null]
470 + );
471 + if (final) {
472 + found.push(final);
473 + }
474 + return found;
475 + }
476 +
477 + /**
478 + * Return an array of Intervals representing the spans of time that only appear in one of the specified Intervals.
479 + * @param {[Interval]} intervals
480 + * @return {[Interval]}
481 + */
482 + static xor(intervals) {
483 + let start = null,
484 + currentCount = 0;
485 + const results = [],
486 + ends = intervals.map(i => [{ time: i.s, type: "s" }, { time: i.e, type: "e" }]),
487 + flattened = Array.prototype.concat(...ends),
488 + arr = flattened.sort((a, b) => a.time - b.time);
489 +
490 + for (const i of arr) {
491 + currentCount += i.type === "s" ? 1 : -1;
492 +
493 + if (currentCount === 1) {
494 + start = i.time;
495 + } else {
496 + if (start && +start !== +i.time) {
497 + results.push(Interval.fromDateTimes(start, i.time));
498 + }
499 +
500 + start = null;
501 + }
502 + }
503 +
504 + return Interval.merge(results);
505 + }
506 +
507 + /**
508 + * Return an Interval representing the span of time in this Interval that doesn't overlap with any of the specified Intervals.
509 + * @param {...Interval} intervals
510 + * @return {[Interval]}
511 + */
512 + difference(...intervals) {
513 + return Interval.xor([this].concat(intervals))
514 + .map(i => this.intersection(i))
515 + .filter(i => i && !i.isEmpty());
516 + }
517 +
518 + /**
519 + * Returns a string representation of this Interval appropriate for debugging.
520 + * @return {string}
521 + */
522 + toString() {
523 + if (!this.isValid) return INVALID;
524 + return `[${this.s.toISO()}${this.e.toISO()})`;
525 + }
526 +
527 + /**
528 + * Returns an ISO 8601-compliant string representation of this Interval.
529 + * @see https://en.wikipedia.org/wiki/ISO_8601#Time_intervals
530 + * @param {Object} opts - The same options as {@link DateTime.toISO}
531 + * @return {string}
532 + */
533 + toISO(opts) {
534 + if (!this.isValid) return INVALID;
535 + return `${this.s.toISO(opts)}/${this.e.toISO(opts)}`;
536 + }
537 +
538 + /**
539 + * Returns an ISO 8601-compliant string representation of date of this Interval.
540 + * The time components are ignored.
541 + * @see https://en.wikipedia.org/wiki/ISO_8601#Time_intervals
542 + * @return {string}
543 + */
544 + toISODate() {
545 + if (!this.isValid) return INVALID;
546 + return `${this.s.toISODate()}/${this.e.toISODate()}`;
547 + }
548 +
549 + /**
550 + * Returns an ISO 8601-compliant string representation of time of this Interval.
551 + * The date components are ignored.
552 + * @see https://en.wikipedia.org/wiki/ISO_8601#Time_intervals
553 + * @param {Object} opts - The same options as {@link DateTime.toISO}
554 + * @return {string}
555 + */
556 + toISOTime(opts) {
557 + if (!this.isValid) return INVALID;
558 + return `${this.s.toISOTime(opts)}/${this.e.toISOTime(opts)}`;
559 + }
560 +
561 + /**
562 + * Returns a string representation of this Interval formatted according to the specified format string.
563 + * @param {string} dateFormat - the format string. This string formats the start and end time. See {@link DateTime.toFormat} for details.
564 + * @param {Object} opts - options
565 + * @param {string} [opts.separator = ' – '] - a separator to place between the start and end representations
566 + * @return {string}
567 + */
568 + toFormat(dateFormat, { separator = " – " } = {}) {
569 + if (!this.isValid) return INVALID;
570 + return `${this.s.toFormat(dateFormat)}${separator}${this.e.toFormat(dateFormat)}`;
571 + }
572 +
573 + /**
574 + * Return a Duration representing the time spanned by this interval.
575 + * @param {string|string[]} [unit=['milliseconds']] - the unit or units (such as 'hours' or 'days') to include in the duration.
576 + * @param {Object} opts - options that affect the creation of the Duration
577 + * @param {string} [opts.conversionAccuracy='casual'] - the conversion system to use
578 + * @example Interval.fromDateTimes(dt1, dt2).toDuration().toObject() //=> { milliseconds: 88489257 }
579 + * @example Interval.fromDateTimes(dt1, dt2).toDuration('days').toObject() //=> { days: 1.0241812152777778 }
580 + * @example Interval.fromDateTimes(dt1, dt2).toDuration(['hours', 'minutes']).toObject() //=> { hours: 24, minutes: 34.82095 }
581 + * @example Interval.fromDateTimes(dt1, dt2).toDuration(['hours', 'minutes', 'seconds']).toObject() //=> { hours: 24, minutes: 34, seconds: 49.257 }
582 + * @example Interval.fromDateTimes(dt1, dt2).toDuration('seconds').toObject() //=> { seconds: 88489.257 }
583 + * @return {Duration}
584 + */
585 + toDuration(unit, opts) {
586 + if (!this.isValid) {
587 + return Duration.invalid(this.invalidReason);
588 + }
589 + return this.e.diff(this.s, unit, opts);
590 + }
591 +
592 + /**
593 + * Run mapFn on the interval start and end, returning a new Interval from the resulting DateTimes
594 + * @param {function} mapFn
595 + * @return {Interval}
596 + * @example Interval.fromDateTimes(dt1, dt2).mapEndpoints(endpoint => endpoint.toUTC())
597 + * @example Interval.fromDateTimes(dt1, dt2).mapEndpoints(endpoint => endpoint.plus({ hours: 2 }))
598 + */
599 + mapEndpoints(mapFn) {
600 + return Interval.fromDateTimes(mapFn(this.s), mapFn(this.e));
601 + }
602 +}
1 +import DateTime from "./datetime.js";
2 +import Duration from "./duration.js";
3 +import Interval from "./interval.js";
4 +import Info from "./info.js";
5 +import Zone from "./zone.js";
6 +import FixedOffsetZone from "./zones/fixedOffsetZone.js";
7 +import IANAZone from "./zones/IANAZone.js";
8 +import InvalidZone from "./zones/invalidZone.js";
9 +import LocalZone from "./zones/localZone.js";
10 +import Settings from "./settings.js";
11 +
12 +const VERSION = "1.27.0";
13 +
14 +export {
15 + VERSION,
16 + DateTime,
17 + Duration,
18 + Interval,
19 + Info,
20 + Zone,
21 + FixedOffsetZone,
22 + IANAZone,
23 + InvalidZone,
24 + LocalZone,
25 + Settings
26 +};
1 +/* eslint import/no-extraneous-dependencies: off */
2 +
3 +import "core-js/features/symbol";
4 +import "core-js/features/symbol/iterator";
5 +import "core-js/features/object";
6 +import "core-js/features/number/is-nan";
7 +import "core-js/features/array";
8 +import "core-js/features/string/virtual/starts-with";
9 +import "core-js/features/string/virtual/repeat";
10 +import "core-js/features/math/trunc";
11 +import "core-js/features/math/sign";
12 +
13 +export * from "./luxon.js";
1 +import LocalZone from "./zones/localZone.js";
2 +import IANAZone from "./zones/IANAZone.js";
3 +import Locale from "./impl/locale.js";
4 +
5 +import { normalizeZone } from "./impl/zoneUtil.js";
6 +
7 +let now = () => Date.now(),
8 + defaultZone = null, // not setting this directly to LocalZone.instance bc loading order issues
9 + defaultLocale = null,
10 + defaultNumberingSystem = null,
11 + defaultOutputCalendar = null,
12 + throwOnInvalid = false;
13 +
14 +/**
15 + * Settings contains static getters and setters that control Luxon's overall behavior. Luxon is a simple library with few options, but the ones it does have live here.
16 + */
17 +export default class Settings {
18 + /**
19 + * Get the callback for returning the current timestamp.
20 + * @type {function}
21 + */
22 + static get now() {
23 + return now;
24 + }
25 +
26 + /**
27 + * Set the callback for returning the current timestamp.
28 + * The function should return a number, which will be interpreted as an Epoch millisecond count
29 + * @type {function}
30 + * @example Settings.now = () => Date.now() + 3000 // pretend it is 3 seconds in the future
31 + * @example Settings.now = () => 0 // always pretend it's Jan 1, 1970 at midnight in UTC time
32 + */
33 + static set now(n) {
34 + now = n;
35 + }
36 +
37 + /**
38 + * Get the default time zone to create DateTimes in.
39 + * @type {string}
40 + */
41 + static get defaultZoneName() {
42 + return Settings.defaultZone.name;
43 + }
44 +
45 + /**
46 + * Set the default time zone to create DateTimes in. Does not affect existing instances.
47 + * @type {string}
48 + */
49 + static set defaultZoneName(z) {
50 + if (!z) {
51 + defaultZone = null;
52 + } else {
53 + defaultZone = normalizeZone(z);
54 + }
55 + }
56 +
57 + /**
58 + * Get the default time zone object to create DateTimes in. Does not affect existing instances.
59 + * @type {Zone}
60 + */
61 + static get defaultZone() {
62 + return defaultZone || LocalZone.instance;
63 + }
64 +
65 + /**
66 + * Get the default locale to create DateTimes with. Does not affect existing instances.
67 + * @type {string}
68 + */
69 + static get defaultLocale() {
70 + return defaultLocale;
71 + }
72 +
73 + /**
74 + * Set the default locale to create DateTimes with. Does not affect existing instances.
75 + * @type {string}
76 + */
77 + static set defaultLocale(locale) {
78 + defaultLocale = locale;
79 + }
80 +
81 + /**
82 + * Get the default numbering system to create DateTimes with. Does not affect existing instances.
83 + * @type {string}
84 + */
85 + static get defaultNumberingSystem() {
86 + return defaultNumberingSystem;
87 + }
88 +
89 + /**
90 + * Set the default numbering system to create DateTimes with. Does not affect existing instances.
91 + * @type {string}
92 + */
93 + static set defaultNumberingSystem(numberingSystem) {
94 + defaultNumberingSystem = numberingSystem;
95 + }
96 +
97 + /**
98 + * Get the default output calendar to create DateTimes with. Does not affect existing instances.
99 + * @type {string}
100 + */
101 + static get defaultOutputCalendar() {
102 + return defaultOutputCalendar;
103 + }
104 +
105 + /**
106 + * Set the default output calendar to create DateTimes with. Does not affect existing instances.
107 + * @type {string}
108 + */
109 + static set defaultOutputCalendar(outputCalendar) {
110 + defaultOutputCalendar = outputCalendar;
111 + }
112 +
113 + /**
114 + * Get whether Luxon will throw when it encounters invalid DateTimes, Durations, or Intervals
115 + * @type {boolean}
116 + */
117 + static get throwOnInvalid() {
118 + return throwOnInvalid;
119 + }
120 +
121 + /**
122 + * Set whether Luxon will throw when it encounters invalid DateTimes, Durations, or Intervals
123 + * @type {boolean}
124 + */
125 + static set throwOnInvalid(t) {
126 + throwOnInvalid = t;
127 + }
128 +
129 + /**
130 + * Reset Luxon's global caches. Should only be necessary in testing scenarios.
131 + * @return {void}
132 + */
133 + static resetCaches() {
134 + Locale.resetCache();
135 + IANAZone.resetCache();
136 + }
137 +}
1 +/* eslint no-unused-vars: "off" */
2 +import { ZoneIsAbstractError } from "./errors.js";
3 +
4 +/**
5 + * @interface
6 + */
7 +export default class Zone {
8 + /**
9 + * The type of zone
10 + * @abstract
11 + * @type {string}
12 + */
13 + get type() {
14 + throw new ZoneIsAbstractError();
15 + }
16 +
17 + /**
18 + * The name of this zone.
19 + * @abstract
20 + * @type {string}
21 + */
22 + get name() {
23 + throw new ZoneIsAbstractError();
24 + }
25 +
26 + /**
27 + * Returns whether the offset is known to be fixed for the whole year.
28 + * @abstract
29 + * @type {boolean}
30 + */
31 + get universal() {
32 + throw new ZoneIsAbstractError();
33 + }
34 +
35 + /**
36 + * Returns the offset's common name (such as EST) at the specified timestamp
37 + * @abstract
38 + * @param {number} ts - Epoch milliseconds for which to get the name
39 + * @param {Object} opts - Options to affect the format
40 + * @param {string} opts.format - What style of offset to return. Accepts 'long' or 'short'.
41 + * @param {string} opts.locale - What locale to return the offset name in.
42 + * @return {string}
43 + */
44 + offsetName(ts, opts) {
45 + throw new ZoneIsAbstractError();
46 + }
47 +
48 + /**
49 + * Returns the offset's value as a string
50 + * @abstract
51 + * @param {number} ts - Epoch milliseconds for which to get the offset
52 + * @param {string} format - What style of offset to return.
53 + * Accepts 'narrow', 'short', or 'techie'. Returning '+6', '+06:00', or '+0600' respectively
54 + * @return {string}
55 + */
56 + formatOffset(ts, format) {
57 + throw new ZoneIsAbstractError();
58 + }
59 +
60 + /**
61 + * Return the offset in minutes for this zone at the specified timestamp.
62 + * @abstract
63 + * @param {number} ts - Epoch milliseconds for which to compute the offset
64 + * @return {number}
65 + */
66 + offset(ts) {
67 + throw new ZoneIsAbstractError();
68 + }
69 +
70 + /**
71 + * Return whether this Zone is equal to another zone
72 + * @abstract
73 + * @param {Zone} otherZone - the zone to compare
74 + * @return {boolean}
75 + */
76 + equals(otherZone) {
77 + throw new ZoneIsAbstractError();
78 + }
79 +
80 + /**
81 + * Return whether this Zone is valid.
82 + * @abstract
83 + * @type {boolean}
84 + */
85 + get isValid() {
86 + throw new ZoneIsAbstractError();
87 + }
88 +}
1 +import { formatOffset, parseZoneInfo, isUndefined, ianaRegex, objToLocalTS } from "../impl/util.js";
2 +import Zone from "../zone.js";
3 +
4 +const matchingRegex = RegExp(`^${ianaRegex.source}$`);
5 +
6 +let dtfCache = {};
7 +function makeDTF(zone) {
8 + if (!dtfCache[zone]) {
9 + dtfCache[zone] = new Intl.DateTimeFormat("en-US", {
10 + hour12: false,
11 + timeZone: zone,
12 + year: "numeric",
13 + month: "2-digit",
14 + day: "2-digit",
15 + hour: "2-digit",
16 + minute: "2-digit",
17 + second: "2-digit"
18 + });
19 + }
20 + return dtfCache[zone];
21 +}
22 +
23 +const typeToPos = {
24 + year: 0,
25 + month: 1,
26 + day: 2,
27 + hour: 3,
28 + minute: 4,
29 + second: 5
30 +};
31 +
32 +function hackyOffset(dtf, date) {
33 + const formatted = dtf.format(date).replace(/\u200E/g, ""),
34 + parsed = /(\d+)\/(\d+)\/(\d+),? (\d+):(\d+):(\d+)/.exec(formatted),
35 + [, fMonth, fDay, fYear, fHour, fMinute, fSecond] = parsed;
36 + return [fYear, fMonth, fDay, fHour, fMinute, fSecond];
37 +}
38 +
39 +function partsOffset(dtf, date) {
40 + const formatted = dtf.formatToParts(date),
41 + filled = [];
42 + for (let i = 0; i < formatted.length; i++) {
43 + const { type, value } = formatted[i],
44 + pos = typeToPos[type];
45 +
46 + if (!isUndefined(pos)) {
47 + filled[pos] = parseInt(value, 10);
48 + }
49 + }
50 + return filled;
51 +}
52 +
53 +let ianaZoneCache = {};
54 +/**
55 + * A zone identified by an IANA identifier, like America/New_York
56 + * @implements {Zone}
57 + */
58 +export default class IANAZone extends Zone {
59 + /**
60 + * @param {string} name - Zone name
61 + * @return {IANAZone}
62 + */
63 + static create(name) {
64 + if (!ianaZoneCache[name]) {
65 + ianaZoneCache[name] = new IANAZone(name);
66 + }
67 + return ianaZoneCache[name];
68 + }
69 +
70 + /**
71 + * Reset local caches. Should only be necessary in testing scenarios.
72 + * @return {void}
73 + */
74 + static resetCache() {
75 + ianaZoneCache = {};
76 + dtfCache = {};
77 + }
78 +
79 + /**
80 + * Returns whether the provided string is a valid specifier. This only checks the string's format, not that the specifier identifies a known zone; see isValidZone for that.
81 + * @param {string} s - The string to check validity on
82 + * @example IANAZone.isValidSpecifier("America/New_York") //=> true
83 + * @example IANAZone.isValidSpecifier("Fantasia/Castle") //=> true
84 + * @example IANAZone.isValidSpecifier("Sport~~blorp") //=> false
85 + * @return {boolean}
86 + */
87 + static isValidSpecifier(s) {
88 + return !!(s && s.match(matchingRegex));
89 + }
90 +
91 + /**
92 + * Returns whether the provided string identifies a real zone
93 + * @param {string} zone - The string to check
94 + * @example IANAZone.isValidZone("America/New_York") //=> true
95 + * @example IANAZone.isValidZone("Fantasia/Castle") //=> false
96 + * @example IANAZone.isValidZone("Sport~~blorp") //=> false
97 + * @return {boolean}
98 + */
99 + static isValidZone(zone) {
100 + try {
101 + new Intl.DateTimeFormat("en-US", { timeZone: zone }).format();
102 + return true;
103 + } catch (e) {
104 + return false;
105 + }
106 + }
107 +
108 + // Etc/GMT+8 -> -480
109 + /** @ignore */
110 + static parseGMTOffset(specifier) {
111 + if (specifier) {
112 + const match = specifier.match(/^Etc\/GMT(0|[+-]\d{1,2})$/i);
113 + if (match) {
114 + return -60 * parseInt(match[1]);
115 + }
116 + }
117 + return null;
118 + }
119 +
120 + constructor(name) {
121 + super();
122 + /** @private **/
123 + this.zoneName = name;
124 + /** @private **/
125 + this.valid = IANAZone.isValidZone(name);
126 + }
127 +
128 + /** @override **/
129 + get type() {
130 + return "iana";
131 + }
132 +
133 + /** @override **/
134 + get name() {
135 + return this.zoneName;
136 + }
137 +
138 + /** @override **/
139 + get universal() {
140 + return false;
141 + }
142 +
143 + /** @override **/
144 + offsetName(ts, { format, locale }) {
145 + return parseZoneInfo(ts, format, locale, this.name);
146 + }
147 +
148 + /** @override **/
149 + formatOffset(ts, format) {
150 + return formatOffset(this.offset(ts), format);
151 + }
152 +
153 + /** @override **/
154 + offset(ts) {
155 + const date = new Date(ts);
156 +
157 + if (isNaN(date)) return NaN;
158 +
159 + const dtf = makeDTF(this.name),
160 + [year, month, day, hour, minute, second] = dtf.formatToParts
161 + ? partsOffset(dtf, date)
162 + : hackyOffset(dtf, date),
163 + // work around https://bugs.chromium.org/p/chromium/issues/detail?id=1025564&can=2&q=%2224%3A00%22%20datetimeformat
164 + adjustedHour = hour === 24 ? 0 : hour;
165 +
166 + const asUTC = objToLocalTS({
167 + year,
168 + month,
169 + day,
170 + hour: adjustedHour,
171 + minute,
172 + second,
173 + millisecond: 0
174 + });
175 +
176 + let asTS = +date;
177 + const over = asTS % 1000;
178 + asTS -= over >= 0 ? over : 1000 + over;
179 + return (asUTC - asTS) / (60 * 1000);
180 + }
181 +
182 + /** @override **/
183 + equals(otherZone) {
184 + return otherZone.type === "iana" && otherZone.name === this.name;
185 + }
186 +
187 + /** @override **/
188 + get isValid() {
189 + return this.valid;
190 + }
191 +}
1 +import { formatOffset, signedOffset } from "../impl/util.js";
2 +import Zone from "../zone.js";
3 +
4 +let singleton = null;
5 +
6 +/**
7 + * A zone with a fixed offset (meaning no DST)
8 + * @implements {Zone}
9 + */
10 +export default class FixedOffsetZone extends Zone {
11 + /**
12 + * Get a singleton instance of UTC
13 + * @return {FixedOffsetZone}
14 + */
15 + static get utcInstance() {
16 + if (singleton === null) {
17 + singleton = new FixedOffsetZone(0);
18 + }
19 + return singleton;
20 + }
21 +
22 + /**
23 + * Get an instance with a specified offset
24 + * @param {number} offset - The offset in minutes
25 + * @return {FixedOffsetZone}
26 + */
27 + static instance(offset) {
28 + return offset === 0 ? FixedOffsetZone.utcInstance : new FixedOffsetZone(offset);
29 + }
30 +
31 + /**
32 + * Get an instance of FixedOffsetZone from a UTC offset string, like "UTC+6"
33 + * @param {string} s - The offset string to parse
34 + * @example FixedOffsetZone.parseSpecifier("UTC+6")
35 + * @example FixedOffsetZone.parseSpecifier("UTC+06")
36 + * @example FixedOffsetZone.parseSpecifier("UTC-6:00")
37 + * @return {FixedOffsetZone}
38 + */
39 + static parseSpecifier(s) {
40 + if (s) {
41 + const r = s.match(/^utc(?:([+-]\d{1,2})(?::(\d{2}))?)?$/i);
42 + if (r) {
43 + return new FixedOffsetZone(signedOffset(r[1], r[2]));
44 + }
45 + }
46 + return null;
47 + }
48 +
49 + constructor(offset) {
50 + super();
51 + /** @private **/
52 + this.fixed = offset;
53 + }
54 +
55 + /** @override **/
56 + get type() {
57 + return "fixed";
58 + }
59 +
60 + /** @override **/
61 + get name() {
62 + return this.fixed === 0 ? "UTC" : `UTC${formatOffset(this.fixed, "narrow")}`;
63 + }
64 +
65 + /** @override **/
66 + offsetName() {
67 + return this.name;
68 + }
69 +
70 + /** @override **/
71 + formatOffset(ts, format) {
72 + return formatOffset(this.fixed, format);
73 + }
74 +
75 + /** @override **/
76 + get universal() {
77 + return true;
78 + }
79 +
80 + /** @override **/
81 + offset() {
82 + return this.fixed;
83 + }
84 +
85 + /** @override **/
86 + equals(otherZone) {
87 + return otherZone.type === "fixed" && otherZone.fixed === this.fixed;
88 + }
89 +
90 + /** @override **/
91 + get isValid() {
92 + return true;
93 + }
94 +}
1 +import Zone from "../zone.js";
2 +
3 +/**
4 + * A zone that failed to parse. You should never need to instantiate this.
5 + * @implements {Zone}
6 + */
7 +export default class InvalidZone extends Zone {
8 + constructor(zoneName) {
9 + super();
10 + /** @private */
11 + this.zoneName = zoneName;
12 + }
13 +
14 + /** @override **/
15 + get type() {
16 + return "invalid";
17 + }
18 +
19 + /** @override **/
20 + get name() {
21 + return this.zoneName;
22 + }
23 +
24 + /** @override **/
25 + get universal() {
26 + return false;
27 + }
28 +
29 + /** @override **/
30 + offsetName() {
31 + return null;
32 + }
33 +
34 + /** @override **/
35 + formatOffset() {
36 + return "";
37 + }
38 +
39 + /** @override **/
40 + offset() {
41 + return NaN;
42 + }
43 +
44 + /** @override **/
45 + equals() {
46 + return false;
47 + }
48 +
49 + /** @override **/
50 + get isValid() {
51 + return false;
52 + }
53 +}
1 +import { formatOffset, parseZoneInfo, hasIntl } from "../impl/util.js";
2 +import Zone from "../zone.js";
3 +
4 +let singleton = null;
5 +
6 +/**
7 + * Represents the local zone for this JavaScript environment.
8 + * @implements {Zone}
9 + */
10 +export default class LocalZone extends Zone {
11 + /**
12 + * Get a singleton instance of the local zone
13 + * @return {LocalZone}
14 + */
15 + static get instance() {
16 + if (singleton === null) {
17 + singleton = new LocalZone();
18 + }
19 + return singleton;
20 + }
21 +
22 + /** @override **/
23 + get type() {
24 + return "local";
25 + }
26 +
27 + /** @override **/
28 + get name() {
29 + if (hasIntl()) {
30 + return new Intl.DateTimeFormat().resolvedOptions().timeZone;
31 + } else return "local";
32 + }
33 +
34 + /** @override **/
35 + get universal() {
36 + return false;
37 + }
38 +
39 + /** @override **/
40 + offsetName(ts, { format, locale }) {
41 + return parseZoneInfo(ts, format, locale);
42 + }
43 +
44 + /** @override **/
45 + formatOffset(ts, format) {
46 + return formatOffset(this.offset(ts), format);
47 + }
48 +
49 + /** @override **/
50 + offset(ts) {
51 + return -new Date(ts).getTimezoneOffset();
52 + }
53 +
54 + /** @override **/
55 + equals(otherZone) {
56 + return otherZone.type === "local";
57 + }
58 +
59 + /** @override **/
60 + get isValid() {
61 + return true;
62 + }
63 +}
1 +# Master (Unreleased)
2 +
3 +# 2.0.0 - 01 February, 2021
4 +
5 +### Breaking changes:
6 +
7 +- Drop support for Node < 6
8 +- Remove support for job objects. See `UPGRADING.md` for more details. #557
9 +
10 +### New features:
11 +
12 +- Introduce compatibility with browsers and Electron #553
13 +
14 +### Bug fixes:
15 +
16 +- Avoid leaking memory on one-off jobs #581
17 +- Address anonymous job id collision problem to ensure that node-schedule could run with the unlimited uptime #582
18 +
19 +### Internal changes:
20 +
21 +- Bump cron-parser from 2.18.0 to 3.1.0 #562
22 +- Bump sinon from 1.17.7 to 9.2.4 #566
23 +- Switch to nyc from istanbul #574
24 +- Convert tests from nodeunit to Tape #575
25 +- Replace var with let/const #577
26 +- Execute airtap tests on a headless Chromium #578
27 +- Make structure more modular #579
1 +Copyright (C) 2015 Matt Patenaude.
2 +
3 +Permission is hereby granted, free of charge, to any person obtaining a copy
4 +of this software and associated documentation files (the "Software"), to deal
5 +in the Software without restriction, including without limitation the rights
6 +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 +copies of the Software, and to permit persons to whom the Software is
8 +furnished to do so, subject to the following conditions:
9 +
10 +The above copyright notice and this permission notice shall be included in
11 +all copies or substantial portions of the Software.
12 +
13 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 +THE SOFTWARE.
1 +# Node Schedule
2 +
3 +[![NPM version](http://img.shields.io/npm/v/node-schedule.svg)](https://www.npmjs.com/package/node-schedule)
4 +[![Downloads](https://img.shields.io/npm/dm/node-schedule.svg)](https://www.npmjs.com/package/node-schedule)
5 +[![Build Status](https://github.com/node-schedule/node-schedule/workflows/ci/badge.svg)](https://github.com/node-schedule/node-schedule/actions)
6 +[![Coverage Status](https://coveralls.io/repos/node-schedule/node-schedule/badge.svg?branch=master)](https://coveralls.io/r/node-schedule/node-schedule?branch=master)
7 +[![Join the chat at https://gitter.im/node-schedule/node-schedule](https://img.shields.io/badge/gitter-chat-green.svg)](https://gitter.im/node-schedule/node-schedule?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
8 +
9 +[![NPM](https://nodei.co/npm/node-schedule.png?downloads=true)](https://nodei.co/npm/node-schedule/)
10 +
11 +Node Schedule is a flexible cron-like and not-cron-like job scheduler for Node.js.
12 +It allows you to schedule jobs (arbitrary functions) for execution at
13 +specific dates, with optional recurrence rules. It only uses a single timer
14 +at any given time (rather than reevaluating upcoming jobs every second/minute).
15 +
16 +Node 6+ is supported.
17 +
18 +## Overview
19 +
20 +Node Schedule is for time-based scheduling, not interval-based scheduling.
21 +
22 +While you can easily bend it to your will, if you only want to do something like
23 +"run this function every 5 minutes", [toad-scheduler](https://github.com/kibertoad/toad-scheduler) would be a better choice. But if you want to, say, "run this function at the :20
24 +and :50 of every hour on the third Tuesday of every month," you'll find that
25 +Node Schedule suits your needs better. Additionally, Node Schedule has Windows
26 +support, unlike true `cron`, since the node runtime is now fully supported.
27 +
28 +Note that Node Schedule is designed for in-process scheduling, i.e. scheduled jobs
29 +will only fire as long as your script is running, and the schedule will disappear
30 +when execution completes. If you need to schedule jobs that will persist even when
31 +your script *isn't* running, consider using actual [cron].
32 +
33 +In case you need durable jobs that persist across restarts and lock system compatible with multi-node deployments,
34 +try [agenda](https://github.com/agenda/agenda) or [bree](https://github.com/breejs/bree).
35 +
36 +## Usage
37 +
38 +### Installation
39 +
40 +You can install using [npm](https://www.npmjs.com/package/node-schedule).
41 +
42 +```
43 +npm install node-schedule
44 +```
45 +
46 +
47 +### Jobs and Scheduling
48 +
49 +Every scheduled job in Node Schedule is represented by a `Job` object. You can
50 +create jobs manually, then execute the `schedule()` method to apply a schedule,
51 +or use the convenience function `scheduleJob()` as demonstrated below.
52 +
53 +`Job` objects are `EventEmitter`s, and emit the following events:
54 +* A `run` event after each execution.
55 +* A `scheduled` event each time they're scheduled to run.
56 +* A `canceled` event when an invocation is canceled before it's executed.
57 + Note that `canceled` is the single-L American spelling.
58 +* An `error` event when a job invocation triggered by a schedule throws or returns
59 + a rejected `Promise`.
60 +
61 +(Both the `scheduled` and `canceled` events receive a JavaScript date object as
62 +a parameter).
63 +Note that jobs are scheduled the first time immediately, so if you create a job
64 +using the `scheduleJob()` convenience method, you'll miss the first `scheduled`
65 +event, but you can query the invocation manually (see below).
66 +
67 +### Cron-style Scheduling
68 +
69 +The cron format consists of:
70 +```
71 +* * * * * *
72 +┬ ┬ ┬ ┬ ┬ ┬
73 +│ │ │ │ │ │
74 +│ │ │ │ │ └ day of week (0 - 7) (0 or 7 is Sun)
75 +│ │ │ │ └───── month (1 - 12)
76 +│ │ │ └────────── day of month (1 - 31)
77 +│ │ └─────────────── hour (0 - 23)
78 +│ └──────────────────── minute (0 - 59)
79 +└───────────────────────── second (0 - 59, OPTIONAL)
80 +```
81 +
82 +Examples with the cron format:
83 +
84 +```js
85 +const schedule = require('node-schedule');
86 +
87 +const job = schedule.scheduleJob('42 * * * *', function(){
88 + console.log('The answer to life, the universe, and everything!');
89 +});
90 +```
91 +
92 +Execute a cron job when the minute is 42 (e.g. 19:42, 20:42, etc.).
93 +
94 +And:
95 +
96 +```js
97 +const job = schedule.scheduleJob('0 17 ? * 0,4-6', function(){
98 + console.log('Today is recognized by Rebecca Black!');
99 +});
100 +```
101 +
102 +Execute a cron job every 5 Minutes = */5 * * * *
103 +
104 +You can also get when it is scheduled to run for every invocation of the job:
105 +```js
106 +const job = schedule.scheduleJob('0 1 * * *', function(fireDate){
107 + console.log('This job was supposed to run at ' + fireDate + ', but actually ran at ' + new Date());
108 +});
109 +```
110 +This is useful when you need to check if there is a delay of the job invocation when the system is busy, or save a record of all invocations of a job for audit purpose.
111 +#### Unsupported Cron Features
112 +
113 +Currently, `W` (nearest weekday) and `L` (last day of month/week) are not supported.
114 +Most other features supported by popular cron implementations should work just fine,
115 +including `#` (nth weekday of the month).
116 +
117 +[cron-parser] is used to parse crontab instructions.
118 +
119 +### Date-based Scheduling
120 +
121 +Say you very specifically want a function to execute at 5:30am on December 21, 2012.
122 +Remember - in JavaScript - 0 - January, 11 - December.
123 +
124 +```js
125 +const schedule = require('node-schedule');
126 +const date = new Date(2012, 11, 21, 5, 30, 0);
127 +
128 +const job = schedule.scheduleJob(date, function(){
129 + console.log('The world is going to end today.');
130 +});
131 +```
132 +
133 +To use current data in the future you can use binding:
134 +
135 +```js
136 +const schedule = require('node-schedule');
137 +const date = new Date(2012, 11, 21, 5, 30, 0);
138 +const x = 'Tada!';
139 +const job = schedule.scheduleJob(date, function(y){
140 + console.log(y);
141 +}.bind(null,x));
142 +x = 'Changing Data';
143 +```
144 +This will log 'Tada!' when the scheduled Job runs, rather than 'Changing Data',
145 +which x changes to immediately after scheduling.
146 +
147 +### Recurrence Rule Scheduling
148 +
149 +You can build recurrence rules to specify when a job should recur. For instance,
150 +consider this rule, which executes the function every hour at 42 minutes after the hour:
151 +
152 +```js
153 +const schedule = require('node-schedule');
154 +
155 +const rule = new schedule.RecurrenceRule();
156 +rule.minute = 42;
157 +
158 +const job = schedule.scheduleJob(rule, function(){
159 + console.log('The answer to life, the universe, and everything!');
160 +});
161 +```
162 +
163 +You can also use arrays to specify a list of acceptable values, and the `Range`
164 +object to specify a range of start and end values, with an optional step parameter.
165 +For instance, this will print a message on Thursday, Friday, Saturday, and Sunday at 5pm:
166 +
167 +```js
168 +const rule = new schedule.RecurrenceRule();
169 +rule.dayOfWeek = [0, new schedule.Range(4, 6)];
170 +rule.hour = 17;
171 +rule.minute = 0;
172 +
173 +const job = schedule.scheduleJob(rule, function(){
174 + console.log('Today is recognized by Rebecca Black!');
175 +});
176 +```
177 +
178 +Timezones are also supported. Here is an example of executing at the start of every day in the UTC timezone.
179 +
180 +```js
181 +const rule = new schedule.RecurrenceRule();
182 +rule.hour = 0;
183 +rule.tz = 'Etc/UTC';
184 +
185 +const job = schedule.scheduleJob(rule, function(){
186 + console.log('A new day has begun in the UTC timezone!');
187 +});
188 +```
189 +
190 +A list of acceptable tz (timezone) values can be found at <https://en.wikipedia.org/wiki/List_of_tz_database_time_zones>
191 +
192 +#### RecurrenceRule properties
193 +
194 +- `second (0-59)`
195 +- `minute (0-59)`
196 +- `hour (0-23)`
197 +- `date (1-31)`
198 +- `month (0-11)`
199 +- `year`
200 +- `dayOfWeek (0-6) Starting with Sunday`
201 +- `tz`
202 +
203 +
204 +> **Note**: It's worth noting that the default value of a component of a recurrence rule is
205 +> `null` (except for second, which is 0 for familiarity with cron). *If we did not
206 +> explicitly set `minute` to 0 above, the message would have instead been logged at
207 +> 5:00pm, 5:01pm, 5:02pm, ..., 5:59pm.* Probably not what you want.
208 +
209 +#### Object Literal Syntax
210 +
211 +To make things a little easier, an object literal syntax is also supported, like
212 +in this example which will log a message every Sunday at 2:30pm:
213 +
214 +```js
215 +const job = schedule.scheduleJob({hour: 14, minute: 30, dayOfWeek: 0}, function(){
216 + console.log('Time for tea!');
217 +});
218 +```
219 +
220 +#### Set StartTime and EndTime
221 +
222 +It will run after 5 seconds and stop after 10 seconds in this example.
223 +The ruledat supports the above.
224 +
225 +```js
226 +const startTime = new Date(Date.now() + 5000);
227 +const endTime = new Date(startTime.getTime() + 5000);
228 +const job = schedule.scheduleJob({ start: startTime, end: endTime, rule: '*/1 * * * * *' }, function(){
229 + console.log('Time for tea!');
230 +});
231 +```
232 +
233 +### Handle Jobs and Job Invocations
234 +
235 +There are some function to get information for a Job and to handle the Job and
236 +Invocations.
237 +
238 +
239 +#### job.cancel(reschedule)
240 +You can invalidate any job with the `cancel()` method:
241 +
242 +```js
243 +j.cancel();
244 +```
245 +All planned invocations will be canceled. When you set the parameter ***reschedule***
246 +to true then the Job is newly scheduled afterwards.
247 +
248 +#### job.cancelNext(reschedule)
249 +This method invalidates the next planned invocation or the job.
250 +When you set the parameter ***reschedule*** to true then the Job is newly scheduled
251 +afterwards.
252 +
253 +#### job.reschedule(spec)
254 +This method cancels all pending invocation and registers the Job completely new again using the given specification.
255 +Return true/false on success/failure.
256 +
257 +#### job.nextInvocation()
258 +This method returns a Date object for the planned next Invocation for this Job. If no invocation is planned the method returns null.
259 +
260 +## Contributing
261 +
262 +This module was originally developed by [Matt Patenaude] who eventually passed over maintainer's mantle over to [Tejas Manohar].
263 +
264 +Currently it is being maintained by [Igor Savin] and [our amazing community].
265 +
266 +We'd love to get your contributions. Individuals making significant and valuable
267 +contributions are given commit-access to the project to contribute as they see fit.
268 +
269 +Before jumping in, check out our [Contributing] page guide!
270 +
271 +## Copyright and license
272 +
273 +Copyright 2015 Matt Patenaude.
274 +
275 +Licensed under the **[MIT License] [license]**.
276 +
277 +
278 +[cron]: http://unixhelp.ed.ac.uk/CGI/man-cgi?crontab+5
279 +[Contributing]: https://github.com/node-schedule/node-schedule/blob/master/CONTRIBUTING.md
280 +[Matt Patenaude]: https://github.com/mattpat
281 +[Tejas Manohar]: http://tejas.io
282 +[Igor Savin]: https://twitter.com/kibertoad
283 +[license]: https://github.com/node-schedule/node-schedule/blob/master/LICENSE
284 +[Tejas Manohar]: https://github.com/tejasmanohar
285 +[our amazing community]: https://github.com/node-schedule/node-schedule/graphs/contributors
286 +[cron-parser]: https://github.com/harrisiirak/cron-parser
1 +## Upgrading to new node-schedule versions
2 +
3 +### Upgrading to version 2.0.0+
4 +
5 +* Node.js versions older than 6 are no longer supported, please update your environment before upgrading.
6 +* In order to prevent memory leaks, one-off jobs (targeted to be executed at an exact date) cannot be rescheduled by name, as reference to them is no longer stored indefinitely. If you want to keep rescheduling them, make sure to store reference to the initial job.
7 +* The `scheduleJob()` method no longer supports passing an object with the job method. If you were using an object, pass the job method directly.
8 +
9 + E.g. code that previously looked like this:
10 + ```javascript
11 + const obj = {
12 + execute() {}
13 + };
14 + Scheduler.scheduleJob(obj);
15 + ```
16 + should be changed to something like this:
17 + ```javascript
18 + function execute() {}
19 + Scheduler.scheduleJob(execute);
20 + ```
1 +'use strict';
2 +
3 +const { cancelJob, rescheduleJob, scheduledJobs, scheduleJob} = require('./lib/schedule')
4 +const { Invocation, RecurrenceRule, Range} = require('./lib/Invocation')
5 +const { Job } = require('./lib/Job')
6 +
7 +module.exports = {
8 + Job,
9 + Invocation,
10 + Range,
11 + RecurrenceRule,
12 + cancelJob,
13 + rescheduleJob,
14 + scheduledJobs,
15 + scheduleJob
16 +}
1 +'use strict';
2 +
3 +const lt = require('long-timeout')
4 +const CronDate = require('cron-parser/lib/date')
5 +const sorted = require('sorted-array-functions')
6 +
7 +const invocations = [];
8 +let currentInvocation = null;
9 +
10 +/* DoesntRecur rule */
11 +const DoesntRecur = new RecurrenceRule();
12 +DoesntRecur.recurs = false;
13 +
14 +/* Invocation object */
15 +function Invocation(job, fireDate, recurrenceRule, endDate) {
16 + this.job = job;
17 + this.fireDate = fireDate;
18 + this.endDate = endDate;
19 + this.recurrenceRule = recurrenceRule || DoesntRecur;
20 +
21 + this.timerID = null;
22 +}
23 +
24 +function sorter(a, b) {
25 + return (a.fireDate.getTime() - b.fireDate.getTime());
26 +}
27 +
28 +/* Range object */
29 +function Range(start, end, step) {
30 + this.start = start || 0;
31 + this.end = end || 60;
32 + this.step = step || 1;
33 +}
34 +
35 +Range.prototype.contains = function(val) {
36 + if (this.step === null || this.step === 1) {
37 + return (val >= this.start && val <= this.end);
38 + } else {
39 + for (let i = this.start; i < this.end; i += this.step) {
40 + if (i === val) {
41 + return true;
42 + }
43 + }
44 +
45 + return false;
46 + }
47 +};
48 +
49 +/* RecurrenceRule object */
50 +/*
51 + Interpreting each property:
52 + null - any value is valid
53 + number - fixed value
54 + Range - value must fall in range
55 + array - value must validate against any item in list
56 +
57 + NOTE: Cron months are 1-based, but RecurrenceRule months are 0-based.
58 +*/
59 +function RecurrenceRule(year, month, date, dayOfWeek, hour, minute, second) {
60 + this.recurs = true;
61 +
62 + this.year = (year == null) ? null : year;
63 + this.month = (month == null) ? null : month;
64 + this.date = (date == null) ? null : date;
65 + this.dayOfWeek = (dayOfWeek == null) ? null : dayOfWeek;
66 + this.hour = (hour == null) ? null : hour;
67 + this.minute = (minute == null) ? null : minute;
68 + this.second = (second == null) ? 0 : second;
69 +}
70 +
71 +RecurrenceRule.prototype.isValid = function() {
72 + function isValidType(num) {
73 + if (Array.isArray(num) || (num instanceof Array)) {
74 + return num.every(function(e) {
75 + return isValidType(e);
76 + });
77 + }
78 + return !(Number.isNaN(Number(num)) && !(num instanceof Range));
79 + }
80 + if (this.month !== null && (this.month < 0 || this.month > 11 || !isValidType(this.month))) {
81 + return false;
82 + }
83 + if (this.dayOfWeek !== null && (this.dayOfWeek < 0 || this.dayOfWeek > 6 || !isValidType(this.dayOfWeek))) {
84 + return false;
85 + }
86 + if (this.hour !== null && (this.hour < 0 || this.hour > 23 || !isValidType(this.hour))) {
87 + return false;
88 + }
89 + if (this.minute !== null && (this.minute < 0 || this.minute > 59 || !isValidType(this.minute))) {
90 + return false;
91 + }
92 + if (this.second !== null && (this.second < 0 || this.second > 59 || !isValidType(this.second))) {
93 + return false;
94 + }
95 + if (this.date !== null) {
96 + if(!isValidType(this.date)) {
97 + return false;
98 + }
99 + switch (this.month) {
100 + case 3:
101 + case 5:
102 + case 8:
103 + case 10:
104 + if (this.date < 1 || this. date > 30) {
105 + return false;
106 + }
107 + break;
108 + case 1:
109 + if (this.date < 1 || this. date > 29) {
110 + return false;
111 + }
112 + break;
113 + default:
114 + if (this.date < 1 || this. date > 31) {
115 + return false;
116 + }
117 + }
118 + }
119 + return true;
120 +};
121 +
122 +RecurrenceRule.prototype.nextInvocationDate = function(base) {
123 + const next = this._nextInvocationDate(base);
124 + return next ? next.toDate() : null;
125 +};
126 +
127 +RecurrenceRule.prototype._nextInvocationDate = function(base) {
128 + base = ((base instanceof CronDate) || (base instanceof Date)) ? base : (new Date());
129 + if (!this.recurs) {
130 + return null;
131 + }
132 +
133 + if(!this.isValid()) {
134 + return null;
135 + }
136 +
137 + const now = new CronDate(Date.now(), this.tz);
138 + let fullYear = now.getFullYear();
139 + if ((this.year !== null) &&
140 + (typeof this.year == 'number') &&
141 + (this.year < fullYear)) {
142 + return null;
143 + }
144 +
145 + let next = new CronDate(base.getTime(), this.tz);
146 + next.addSecond();
147 +
148 + while (true) {
149 + if (this.year !== null) {
150 + fullYear = next.getFullYear();
151 + if ((typeof this.year == 'number') && (this.year < fullYear)) {
152 + next = null;
153 + break;
154 + }
155 +
156 + if (!recurMatch(fullYear, this.year)) {
157 + next.addYear();
158 + next.setMonth(0);
159 + next.setDate(1);
160 + next.setHours(0);
161 + next.setMinutes(0);
162 + next.setSeconds(0);
163 + continue;
164 + }
165 + }
166 + if (this.month != null && !recurMatch(next.getMonth(), this.month)) {
167 + next.addMonth();
168 + continue;
169 + }
170 + if (this.date != null && !recurMatch(next.getDate(), this.date)) {
171 + next.addDay();
172 + continue;
173 + }
174 + if (this.dayOfWeek != null && !recurMatch(next.getDay(), this.dayOfWeek)) {
175 + next.addDay();
176 + continue;
177 + }
178 + if (this.hour != null && !recurMatch(next.getHours(), this.hour)) {
179 + next.addHour();
180 + continue;
181 + }
182 + if (this.minute != null && !recurMatch(next.getMinutes(), this.minute)) {
183 + next.addMinute();
184 + continue;
185 + }
186 + if (this.second != null && !recurMatch(next.getSeconds(), this.second)) {
187 + next.addSecond();
188 + continue;
189 + }
190 +
191 + break;
192 + }
193 +
194 + return next;
195 +};
196 +
197 +function recurMatch(val, matcher) {
198 + if (matcher == null) {
199 + return true;
200 + }
201 +
202 + if (typeof matcher === 'number') {
203 + return (val === matcher);
204 + } else if(typeof matcher === 'string') {
205 + return (val === Number(matcher));
206 + } else if (matcher instanceof Range) {
207 + return matcher.contains(val);
208 + } else if (Array.isArray(matcher) || (matcher instanceof Array)) {
209 + for (let i = 0; i < matcher.length; i++) {
210 + if (recurMatch(val, matcher[i])) {
211 + return true;
212 + }
213 + }
214 + }
215 +
216 + return false;
217 +}
218 +
219 +/* Date-based scheduler */
220 +function runOnDate(date, job) {
221 + const now = Date.now();
222 + const then = date.getTime();
223 +
224 + return lt.setTimeout(function() {
225 + if (then > Date.now())
226 + runOnDate(date, job);
227 + else
228 + job();
229 + }, (then < now ? 0 : then - now));
230 +}
231 +
232 +function scheduleInvocation(invocation) {
233 + sorted.add(invocations, invocation, sorter);
234 + prepareNextInvocation();
235 + const date = invocation.fireDate instanceof CronDate ? invocation.fireDate.toDate() : invocation.fireDate;
236 + invocation.job.emit('scheduled', date);
237 +}
238 +
239 +function prepareNextInvocation() {
240 + if (invocations.length > 0 && currentInvocation !== invocations[0]) {
241 + if (currentInvocation !== null) {
242 + lt.clearTimeout(currentInvocation.timerID);
243 + currentInvocation.timerID = null;
244 + currentInvocation = null;
245 + }
246 +
247 + currentInvocation = invocations[0];
248 +
249 + const job = currentInvocation.job;
250 + const cinv = currentInvocation;
251 + currentInvocation.timerID = runOnDate(currentInvocation.fireDate, function() {
252 + currentInvocationFinished();
253 +
254 + if (job.callback) {
255 + job.callback();
256 + }
257 +
258 + if (cinv.recurrenceRule.recurs || cinv.recurrenceRule._endDate === null) {
259 + const inv = scheduleNextRecurrence(cinv.recurrenceRule, cinv.job, cinv.fireDate, cinv.endDate);
260 + if (inv !== null) {
261 + inv.job.trackInvocation(inv);
262 + }
263 + }
264 +
265 + job.stopTrackingInvocation(cinv);
266 +
267 + try {
268 + const result = job.invoke(cinv.fireDate instanceof CronDate ? cinv.fireDate.toDate() : cinv.fireDate);
269 + job.emit('run');
270 +
271 + if (result instanceof Promise) {
272 + result.catch(function (err) {
273 + job.emit('error', err);
274 + });
275 + }
276 + } catch (err) {
277 + job.emit('error', err);
278 + }
279 +
280 + if (job.isOneTimeJob) {
281 + job.deleteFromSchedule();
282 + }
283 + });
284 + }
285 +}
286 +
287 +function currentInvocationFinished() {
288 + invocations.shift();
289 + currentInvocation = null;
290 + prepareNextInvocation();
291 +}
292 +
293 +function cancelInvocation(invocation) {
294 + const idx = invocations.indexOf(invocation);
295 + if (idx > -1) {
296 + invocations.splice(idx, 1);
297 + if (invocation.timerID !== null) {
298 + lt.clearTimeout(invocation.timerID);
299 + }
300 +
301 + if (currentInvocation === invocation) {
302 + currentInvocation = null;
303 + }
304 +
305 + invocation.job.emit('canceled', invocation.fireDate);
306 + prepareNextInvocation();
307 + }
308 +}
309 +
310 +/* Recurrence scheduler */
311 +function scheduleNextRecurrence(rule, job, prevDate, endDate) {
312 +
313 + prevDate = (prevDate instanceof CronDate) ? prevDate : new CronDate();
314 +
315 + const date = (rule instanceof RecurrenceRule) ? rule._nextInvocationDate(prevDate) : rule.next();
316 + if (date === null) {
317 + return null;
318 + }
319 +
320 + if ((endDate instanceof CronDate) && date.getTime() > endDate.getTime()) {
321 + return null;
322 + }
323 +
324 + const inv = new Invocation(job, date, rule, endDate);
325 + scheduleInvocation(inv);
326 +
327 + return inv;
328 +}
329 +
330 +module.exports = {
331 + Range,
332 + RecurrenceRule,
333 + Invocation,
334 + cancelInvocation,
335 + scheduleInvocation,
336 + scheduleNextRecurrence,
337 + sorter,
338 + _invocations: invocations
339 +}
1 +'use strict';
2 +
3 +const events = require('events')
4 +const cronParser = require('cron-parser')
5 +const CronDate = require('cron-parser/lib/date')
6 +const sorted = require('sorted-array-functions')
7 +
8 +const { scheduleNextRecurrence, scheduleInvocation, cancelInvocation, RecurrenceRule, sorter, Invocation } = require('./Invocation')
9 +const { isValidDate } = require('./utils/dateUtils')
10 +
11 +const scheduledJobs = {};
12 +
13 +let anonJobCounter = 0;
14 +function resolveAnonJobName() {
15 + const now = new Date()
16 + if (anonJobCounter === Number.MAX_SAFE_INTEGER) {
17 + anonJobCounter = 0
18 + }
19 + anonJobCounter++
20 +
21 + return `<Anonymous Job ${anonJobCounter} ${now.toISOString()}>`
22 +}
23 +
24 +function Job(name, job, callback) {
25 + // setup a private pendingInvocations variable
26 + this.pendingInvocations = [];
27 +
28 + //setup a private number of invocations variable
29 + let triggeredJobs = 0;
30 +
31 + // Set scope vars
32 + const jobName = name && typeof name === 'string' ? name : resolveAnonJobName();
33 + this.job = name && typeof name === 'function' ? name : job;
34 +
35 + // Make sure callback is actually a callback
36 + if (this.job === name) {
37 + // Name wasn't provided and maybe a callback is there
38 + this.callback = typeof job === 'function' ? job : false;
39 + } else {
40 + // Name was provided, and maybe a callback is there
41 + this.callback = typeof callback === 'function' ? callback : false;
42 + }
43 +
44 + // Check for generator
45 + if (typeof this.job === 'function' &&
46 + this.job.prototype &&
47 + this.job.prototype.next) {
48 + this.job = function() {
49 + return this.next().value;
50 + }.bind(this.job.call(this));
51 + }
52 +
53 + // define properties
54 + Object.defineProperty(this, 'name', {
55 + value: jobName,
56 + writable: false,
57 + enumerable: true
58 + });
59 +
60 + // method that require private access
61 + this.trackInvocation = function(invocation) {
62 + // add to our invocation list
63 + sorted.add(this.pendingInvocations, invocation, sorter);
64 + return true;
65 + };
66 + this.stopTrackingInvocation = function(invocation) {
67 + const invIdx = this.pendingInvocations.indexOf(invocation);
68 + if (invIdx > -1) {
69 + this.pendingInvocations.splice(invIdx, 1);
70 + return true;
71 + }
72 +
73 + return false;
74 + };
75 + this.triggeredJobs = function() {
76 + return triggeredJobs;
77 + };
78 + this.setTriggeredJobs = function(triggeredJob) {
79 + triggeredJobs = triggeredJob;
80 + };
81 + this.deleteFromSchedule = function() {
82 + deleteScheduledJob(this.name)
83 + };
84 + this.cancel = function(reschedule) {
85 + reschedule = (typeof reschedule == 'boolean') ? reschedule : false;
86 +
87 + let inv, newInv;
88 + const newInvs = [];
89 + for (let j = 0; j < this.pendingInvocations.length; j++) {
90 + inv = this.pendingInvocations[j];
91 +
92 + cancelInvocation(inv);
93 +
94 + if (reschedule && (inv.recurrenceRule.recurs || inv.recurrenceRule.next)) {
95 + newInv = scheduleNextRecurrence(inv.recurrenceRule, this, inv.fireDate, inv.endDate);
96 + if (newInv !== null) {
97 + newInvs.push(newInv);
98 + }
99 + }
100 + }
101 +
102 + this.pendingInvocations = [];
103 +
104 + for (let k = 0; k < newInvs.length; k++) {
105 + this.trackInvocation(newInvs[k]);
106 + }
107 +
108 + // remove from scheduledJobs if reschedule === false
109 + if (!reschedule) {
110 + this.deleteFromSchedule()
111 + }
112 +
113 + return true;
114 + };
115 + this.cancelNext = function(reschedule) {
116 + reschedule = (typeof reschedule == 'boolean') ? reschedule : true;
117 +
118 + if (!this.pendingInvocations.length) {
119 + return false;
120 + }
121 +
122 + let newInv;
123 + const nextInv = this.pendingInvocations.shift();
124 +
125 + cancelInvocation(nextInv);
126 +
127 + if (reschedule && (nextInv.recurrenceRule.recurs || nextInv.recurrenceRule.next)) {
128 + newInv = scheduleNextRecurrence(nextInv.recurrenceRule, this, nextInv.fireDate, nextInv.endDate);
129 + if (newInv !== null) {
130 + this.trackInvocation(newInv);
131 + }
132 + }
133 +
134 + return true;
135 + };
136 + this.reschedule = function(spec) {
137 + let inv;
138 + const invocationsToCancel = this.pendingInvocations.slice();
139 +
140 + for (let j = 0; j < invocationsToCancel.length; j++) {
141 + inv = invocationsToCancel[j];
142 +
143 + cancelInvocation(inv);
144 + }
145 +
146 + this.pendingInvocations = [];
147 +
148 + if (this.schedule(spec)) {
149 + this.setTriggeredJobs(0);
150 + return true;
151 + } else {
152 + this.pendingInvocations = invocationsToCancel;
153 + return false;
154 + }
155 + };
156 + this.nextInvocation = function() {
157 + if (!this.pendingInvocations.length) {
158 + return null;
159 + }
160 + return this.pendingInvocations[0].fireDate;
161 + };
162 +}
163 +
164 +Object.setPrototypeOf(Job.prototype, events.EventEmitter.prototype);
165 +
166 +Job.prototype.invoke = function(fireDate) {
167 + this.setTriggeredJobs(this.triggeredJobs() + 1);
168 + return this.job(fireDate);
169 +};
170 +
171 +Job.prototype.runOnDate = function(date) {
172 + return this.schedule(date);
173 +};
174 +
175 +Job.prototype.schedule = function(spec) {
176 + const self = this;
177 + let success = false;
178 + let inv;
179 + let start;
180 + let end;
181 + let tz;
182 +
183 + // save passed-in value before 'spec' is replaced
184 + if (typeof spec === 'object' && 'tz' in spec) {
185 + tz = spec.tz;
186 + }
187 +
188 + if (typeof spec === 'object' && spec.rule) {
189 + start = spec.start || undefined;
190 + end = spec.end || undefined;
191 + spec = spec.rule;
192 +
193 + if (start) {
194 + if (!(start instanceof Date)) {
195 + start = new Date(start);
196 + }
197 +
198 + start = new CronDate(start, tz);
199 + if (!isValidDate(start) || start.getTime() < Date.now()) {
200 + start = undefined;
201 + }
202 + }
203 +
204 + if (end && !(end instanceof Date) && !isValidDate(end = new Date(end))) {
205 + end = undefined;
206 + }
207 +
208 + if (end) {
209 + end = new CronDate(end, tz);
210 + }
211 + }
212 +
213 + try {
214 + const res = cronParser.parseExpression(spec, {currentDate: start, tz: tz});
215 + inv = scheduleNextRecurrence(res, self, start, end);
216 + if (inv !== null) {
217 + success = self.trackInvocation(inv);
218 + }
219 + } catch (err) {
220 + const type = typeof spec;
221 + if ((type === 'string') || (type === 'number')) {
222 + spec = new Date(spec);
223 + }
224 +
225 + if ((spec instanceof Date) && (isValidDate(spec))) {
226 + spec = new CronDate(spec);
227 + self.isOneTimeJob = true;
228 + if (spec.getTime() >= Date.now()) {
229 + inv = new Invocation(self, spec);
230 + scheduleInvocation(inv);
231 + success = self.trackInvocation(inv);
232 + }
233 + } else if (type === 'object') {
234 + self.isOneTimeJob = false;
235 + if (!(spec instanceof RecurrenceRule)) {
236 + const r = new RecurrenceRule();
237 + if ('year' in spec) {
238 + r.year = spec.year;
239 + }
240 + if ('month' in spec) {
241 + r.month = spec.month;
242 + }
243 + if ('date' in spec) {
244 + r.date = spec.date;
245 + }
246 + if ('dayOfWeek' in spec) {
247 + r.dayOfWeek = spec.dayOfWeek;
248 + }
249 + if ('hour' in spec) {
250 + r.hour = spec.hour;
251 + }
252 + if ('minute' in spec) {
253 + r.minute = spec.minute;
254 + }
255 + if ('second' in spec) {
256 + r.second = spec.second;
257 + }
258 +
259 + spec = r;
260 + }
261 +
262 + spec.tz = tz;
263 + inv = scheduleNextRecurrence(spec, self, start, end);
264 + if (inv !== null) {
265 + success = self.trackInvocation(inv);
266 + }
267 + }
268 + }
269 +
270 + scheduledJobs[this.name] = this;
271 + return success;
272 +};
273 +
274 +function deleteScheduledJob(name) {
275 + if (name) {
276 + delete scheduledJobs[name];
277 + }
278 +}
279 +
280 +module.exports = {
281 + Job,
282 + deleteScheduledJob,
283 + scheduledJobs
284 +}
1 +'use strict';
2 +
3 +/*
4 + node-schedule
5 + A cron-like and not-cron-like job scheduler for Node.
6 +*/
7 +
8 +const { Job, scheduledJobs } = require('./Job')
9 +
10 +/* API
11 + invoke()
12 + runOnDate(date)
13 + schedule(date || recurrenceRule || cronstring)
14 + cancel(reschedule = false)
15 + cancelNext(reschedule = true)
16 +
17 + Property constraints
18 + name: readonly
19 + job: readwrite
20 +*/
21 +
22 +/* Convenience methods */
23 +function scheduleJob() {
24 + if (arguments.length < 2) {
25 + throw new RangeError('Invalid number of arguments');
26 + }
27 +
28 + const name = (arguments.length >= 3 && typeof arguments[0] === 'string') ? arguments[0] : null;
29 + const spec = name ? arguments[1] : arguments[0];
30 + const method = name ? arguments[2] : arguments[1];
31 + const callback = name ? arguments[3] : arguments[2];
32 +
33 + if (typeof method !== 'function') {
34 + throw new RangeError('The job method must be a function.');
35 + }
36 +
37 + const job = new Job(name, method, callback);
38 +
39 + if (job.schedule(spec)) {
40 + return job;
41 + }
42 +
43 + return null;
44 +}
45 +
46 +function rescheduleJob(job, spec) {
47 + if (job instanceof Job) {
48 + if (job.reschedule(spec)) {
49 + return job;
50 + }
51 + } else if (typeof job === 'string') {
52 + if (scheduledJobs.hasOwnProperty(job)) {
53 + if (scheduledJobs[job].reschedule(spec)) {
54 + return scheduledJobs[job];
55 + }
56 + } else {
57 + throw new Error('Cannot reschedule one-off job by name, pass job reference instead')
58 + }
59 + }
60 + return null;
61 +}
62 +
63 +function cancelJob(job) {
64 + let success = false;
65 + if (job instanceof Job) {
66 + success = job.cancel();
67 + } else if (typeof job == 'string' || job instanceof String) {
68 + if (job in scheduledJobs && scheduledJobs.hasOwnProperty(job)) {
69 + success = scheduledJobs[job].cancel();
70 + }
71 + }
72 +
73 + return success;
74 +}
75 +
76 +/* Public API */
77 +module.exports = {
78 + scheduleJob,
79 + rescheduleJob,
80 + scheduledJobs,
81 + cancelJob
82 +}
1 +'use strict';
2 +
3 +function isValidDate(date) {
4 + // Taken from http://stackoverflow.com/a/12372720/1562178
5 + // If getTime() returns NaN it'll return false anyway
6 + return date.getTime() === date.getTime();
7 +}
8 +
9 +module.exports = {
10 + isValidDate
11 +}
1 +{
2 + "name": "node-schedule",
3 + "version": "2.0.0",
4 + "description": "A cron-like and not-cron-like job scheduler for Node.",
5 + "keywords": [
6 + "schedule",
7 + "task",
8 + "job",
9 + "cron",
10 + "recurrent",
11 + "in-memory"
12 + ],
13 + "license": "MIT",
14 + "main": "index.js",
15 + "scripts": {
16 + "test": "tape test/*.js",
17 + "test:browser": "airtap test/cancel-long-running-jobs.js test/convenience-method-test.js test/date-convenience-methods-test.js test/range-test.js test/recurrence-rule-test.js test/schedule-cron-jobs.js test/start-end-test.js",
18 + "coveralls": "nyc report --reporter=lcov",
19 + "test:coverage": "nyc tape test/*.js",
20 + "lint": "eslint lib test",
21 + "lint:fix": "eslint --fix lib test"
22 + },
23 + "author": {
24 + "name": "Matt Patenaude",
25 + "email": "matt@mattpatenaude.com",
26 + "url": "http://mattpatenaude.com"
27 + },
28 + "contributors": [
29 + {
30 + "name": "Igor Savin",
31 + "email": "kibertoad@gmail.com"
32 + }
33 + ],
34 + "repository": {
35 + "type": "git",
36 + "url": "https://github.com/node-schedule/node-schedule.git"
37 + },
38 + "dependencies": {
39 + "cron-parser": "^3.1.0",
40 + "long-timeout": "0.1.1",
41 + "sorted-array-functions": "^1.3.0"
42 + },
43 + "devDependencies": {
44 + "airtap": "^4.0.1",
45 + "eslint": "^7.18.0",
46 + "nyc": "^15.1.0",
47 + "sinon": "^9.2.4",
48 + "tape": "^5.1.1"
49 + },
50 + "engines": {
51 + "node": ">=6"
52 + },
53 + "files": [
54 + "CHANGELOG.md",
55 + "README.md",
56 + "UPGRADING.md",
57 + "LICENSE",
58 + "lib/*"
59 + ]
60 +}
1 +root = true
2 +
3 +[*]
4 +indent_style = tab;
5 +insert_final_newline = true;
6 +quote_type = auto;
7 +space_after_anonymous_functions = true;
8 +space_after_control_statements = true;
9 +spaces_around_operators = true;
10 +trim_trailing_whitespace = true;
11 +spaces_in_brackets = false;
12 +end_of_line = lf;
13 +
1 +{
2 + "root": true,
3 +
4 + "extends": "@ljharb",
5 +
6 + "rules": {
7 + "complexity": [2, 23],
8 + "id-length": [2, { "min": 1, "max": 40 }],
9 + "max-params": [2, 3],
10 + "max-statements": [2, 23],
11 + "max-statements-per-line": [2, { "max": 2 }],
12 + "no-extra-parens": [1],
13 + "no-invalid-this": [1],
14 + "no-restricted-syntax": [2, "BreakStatement", "ContinueStatement", "LabeledStatement", "WithStatement"],
15 + "operator-linebreak": [2, "after"]
16 + }
17 +}
1 +language: node_js
2 +os:
3 + - linux
4 +node_js:
5 + - "11.8"
6 + - "10.15"
7 + - "9.11"
8 + - "8.15"
9 + - "7.10"
10 + - "6.16"
11 + - "5.12"
12 + - "4.9"
13 + - "iojs-v3.3"
14 + - "iojs-v2.5"
15 + - "iojs-v1.8"
16 + - "0.12"
17 + - "0.10"
18 + - "0.8"
19 +before_install:
20 + - 'case "${TRAVIS_NODE_VERSION}" in 0.*) export NPM_CONFIG_STRICT_SSL=false ;; esac'
21 + - 'nvm install-latest-npm'
22 +install:
23 + - 'if [ "${TRAVIS_NODE_VERSION}" = "0.6" ] || [ "${TRAVIS_NODE_VERSION}" = "0.9" ]; then nvm install --latest-npm 0.8 && npm install && nvm use "${TRAVIS_NODE_VERSION}"; else npm install; fi;'
24 +script:
25 + - 'if [ -n "${PRETEST-}" ]; then npm run pretest ; fi'
26 + - 'if [ -n "${POSTTEST-}" ]; then npm run posttest ; fi'
27 + - 'if [ -n "${COVERAGE-}" ]; then npm run coverage ; fi'
28 + - 'if [ -n "${TEST-}" ]; then npm run tests-only ; fi'
29 +sudo: false
30 +env:
31 + - TEST=true
32 +matrix:
33 + fast_finish: true
34 + include:
35 + - node_js: "lts/*"
36 + env: PRETEST=true
37 + - node_js: "lts/*"
38 + env: POSTTEST=true
39 + - node_js: "4"
40 + env: COVERAGE=true
41 + - node_js: "11.7"
42 + env: TEST=true ALLOW_FAILURE=true
43 + - node_js: "11.6"
44 + env: TEST=true ALLOW_FAILURE=true
45 + - node_js: "11.5"
46 + env: TEST=true ALLOW_FAILURE=true
47 + - node_js: "11.4"
48 + env: TEST=true ALLOW_FAILURE=true
49 + - node_js: "11.3"
50 + env: TEST=true ALLOW_FAILURE=true
51 + - node_js: "11.2"
52 + env: TEST=true ALLOW_FAILURE=true
53 + - node_js: "11.1"
54 + env: TEST=true ALLOW_FAILURE=true
55 + - node_js: "11.0"
56 + env: TEST=true ALLOW_FAILURE=true
57 + - node_js: "10.14"
58 + env: TEST=true ALLOW_FAILURE=true
59 + - node_js: "10.13"
60 + env: TEST=true ALLOW_FAILURE=true
61 + - node_js: "10.12"
62 + env: TEST=true ALLOW_FAILURE=true
63 + - node_js: "10.11"
64 + env: TEST=true ALLOW_FAILURE=true
65 + - node_js: "10.10"
66 + env: TEST=true ALLOW_FAILURE=true
67 + - node_js: "10.9"
68 + env: TEST=true ALLOW_FAILURE=true
69 + - node_js: "10.8"
70 + env: TEST=true ALLOW_FAILURE=true
71 + - node_js: "10.7"
72 + env: TEST=true ALLOW_FAILURE=true
73 + - node_js: "10.6"
74 + env: TEST=true ALLOW_FAILURE=true
75 + - node_js: "10.5"
76 + env: TEST=true ALLOW_FAILURE=true
77 + - node_js: "10.4"
78 + env: TEST=true ALLOW_FAILURE=true
79 + - node_js: "10.3"
80 + env: TEST=true ALLOW_FAILURE=true
81 + - node_js: "10.2"
82 + env: TEST=true ALLOW_FAILURE=true
83 + - node_js: "10.1"
84 + env: TEST=true ALLOW_FAILURE=true
85 + - node_js: "10.0"
86 + env: TEST=true ALLOW_FAILURE=true
87 + - node_js: "9.10"
88 + env: TEST=true ALLOW_FAILURE=true
89 + - node_js: "9.9"
90 + env: TEST=true ALLOW_FAILURE=true
91 + - node_js: "9.8"
92 + env: TEST=true ALLOW_FAILURE=true
93 + - node_js: "9.7"
94 + env: TEST=true ALLOW_FAILURE=true
95 + - node_js: "9.6"
96 + env: TEST=true ALLOW_FAILURE=true
97 + - node_js: "9.5"
98 + env: TEST=true ALLOW_FAILURE=true
99 + - node_js: "9.4"
100 + env: TEST=true ALLOW_FAILURE=true
101 + - node_js: "9.3"
102 + env: TEST=true ALLOW_FAILURE=true
103 + - node_js: "9.2"
104 + env: TEST=true ALLOW_FAILURE=true
105 + - node_js: "9.1"
106 + env: TEST=true ALLOW_FAILURE=true
107 + - node_js: "9.0"
108 + env: TEST=true ALLOW_FAILURE=true
109 + - node_js: "8.14"
110 + env: TEST=true ALLOW_FAILURE=true
111 + - node_js: "8.13"
112 + env: TEST=true ALLOW_FAILURE=true
113 + - node_js: "8.12"
114 + env: TEST=true ALLOW_FAILURE=true
115 + - node_js: "8.11"
116 + env: TEST=true ALLOW_FAILURE=true
117 + - node_js: "8.10"
118 + env: TEST=true ALLOW_FAILURE=true
119 + - node_js: "8.9"
120 + env: TEST=true ALLOW_FAILURE=true
121 + - node_js: "8.8"
122 + env: TEST=true ALLOW_FAILURE=true
123 + - node_js: "8.7"
124 + env: TEST=true ALLOW_FAILURE=true
125 + - node_js: "8.6"
126 + env: TEST=true ALLOW_FAILURE=true
127 + - node_js: "8.5"
128 + env: TEST=true ALLOW_FAILURE=true
129 + - node_js: "8.4"
130 + env: TEST=true ALLOW_FAILURE=true
131 + - node_js: "8.3"
132 + env: TEST=true ALLOW_FAILURE=true
133 + - node_js: "8.2"
134 + env: TEST=true ALLOW_FAILURE=true
135 + - node_js: "8.1"
136 + env: TEST=true ALLOW_FAILURE=true
137 + - node_js: "8.0"
138 + env: TEST=true ALLOW_FAILURE=true
139 + - node_js: "7.9"
140 + env: TEST=true ALLOW_FAILURE=true
141 + - node_js: "7.8"
142 + env: TEST=true ALLOW_FAILURE=true
143 + - node_js: "7.7"
144 + env: TEST=true ALLOW_FAILURE=true
145 + - node_js: "7.6"
146 + env: TEST=true ALLOW_FAILURE=true
147 + - node_js: "7.5"
148 + env: TEST=true ALLOW_FAILURE=true
149 + - node_js: "7.4"
150 + env: TEST=true ALLOW_FAILURE=true
151 + - node_js: "7.3"
152 + env: TEST=true ALLOW_FAILURE=true
153 + - node_js: "7.2"
154 + env: TEST=true ALLOW_FAILURE=true
155 + - node_js: "7.1"
156 + env: TEST=true ALLOW_FAILURE=true
157 + - node_js: "7.0"
158 + env: TEST=true ALLOW_FAILURE=true
159 + - node_js: "6.15"
160 + env: TEST=true ALLOW_FAILURE=true
161 + - node_js: "6.14"
162 + env: TEST=true ALLOW_FAILURE=true
163 + - node_js: "6.13"
164 + env: TEST=true ALLOW_FAILURE=true
165 + - node_js: "6.12"
166 + env: TEST=true ALLOW_FAILURE=true
167 + - node_js: "6.11"
168 + env: TEST=true ALLOW_FAILURE=true
169 + - node_js: "6.10"
170 + env: TEST=true ALLOW_FAILURE=true
171 + - node_js: "6.9"
172 + env: TEST=true ALLOW_FAILURE=true
173 + - node_js: "6.8"
174 + env: TEST=true ALLOW_FAILURE=true
175 + - node_js: "6.7"
176 + env: TEST=true ALLOW_FAILURE=true
177 + - node_js: "6.6"
178 + env: TEST=true ALLOW_FAILURE=true
179 + - node_js: "6.5"
180 + env: TEST=true ALLOW_FAILURE=true
181 + - node_js: "6.4"
182 + env: TEST=true ALLOW_FAILURE=true
183 + - node_js: "6.3"
184 + env: TEST=true ALLOW_FAILURE=true
185 + - node_js: "6.2"
186 + env: TEST=true ALLOW_FAILURE=true
187 + - node_js: "6.1"
188 + env: TEST=true ALLOW_FAILURE=true
189 + - node_js: "6.0"
190 + env: TEST=true ALLOW_FAILURE=true
191 + - node_js: "5.11"
192 + env: TEST=true ALLOW_FAILURE=true
193 + - node_js: "5.10"
194 + env: TEST=true ALLOW_FAILURE=true
195 + - node_js: "5.9"
196 + env: TEST=true ALLOW_FAILURE=true
197 + - node_js: "5.8"
198 + env: TEST=true ALLOW_FAILURE=true
199 + - node_js: "5.7"
200 + env: TEST=true ALLOW_FAILURE=true
201 + - node_js: "5.6"
202 + env: TEST=true ALLOW_FAILURE=true
203 + - node_js: "5.5"
204 + env: TEST=true ALLOW_FAILURE=true
205 + - node_js: "5.4"
206 + env: TEST=true ALLOW_FAILURE=true
207 + - node_js: "5.3"
208 + env: TEST=true ALLOW_FAILURE=true
209 + - node_js: "5.2"
210 + env: TEST=true ALLOW_FAILURE=true
211 + - node_js: "5.1"
212 + env: TEST=true ALLOW_FAILURE=true
213 + - node_js: "5.0"
214 + env: TEST=true ALLOW_FAILURE=true
215 + - node_js: "4.8"
216 + env: TEST=true ALLOW_FAILURE=true
217 + - node_js: "4.7"
218 + env: TEST=true ALLOW_FAILURE=true
219 + - node_js: "4.6"
220 + env: TEST=true ALLOW_FAILURE=true
221 + - node_js: "4.5"
222 + env: TEST=true ALLOW_FAILURE=true
223 + - node_js: "4.4"
224 + env: TEST=true ALLOW_FAILURE=true
225 + - node_js: "4.3"
226 + env: TEST=true ALLOW_FAILURE=true
227 + - node_js: "4.2"
228 + env: TEST=true ALLOW_FAILURE=true
229 + - node_js: "4.1"
230 + env: TEST=true ALLOW_FAILURE=true
231 + - node_js: "4.0"
232 + env: TEST=true ALLOW_FAILURE=true
233 + - node_js: "iojs-v3.2"
234 + env: TEST=true ALLOW_FAILURE=true
235 + - node_js: "iojs-v3.1"
236 + env: TEST=true ALLOW_FAILURE=true
237 + - node_js: "iojs-v3.0"
238 + env: TEST=true ALLOW_FAILURE=true
239 + - node_js: "iojs-v2.4"
240 + env: TEST=true ALLOW_FAILURE=true
241 + - node_js: "iojs-v2.3"
242 + env: TEST=true ALLOW_FAILURE=true
243 + - node_js: "iojs-v2.2"
244 + env: TEST=true ALLOW_FAILURE=true
245 + - node_js: "iojs-v2.1"
246 + env: TEST=true ALLOW_FAILURE=true
247 + - node_js: "iojs-v2.0"
248 + env: TEST=true ALLOW_FAILURE=true
249 + - node_js: "iojs-v1.7"
250 + env: TEST=true ALLOW_FAILURE=true
251 + - node_js: "iojs-v1.6"
252 + env: TEST=true ALLOW_FAILURE=true
253 + - node_js: "iojs-v1.5"
254 + env: TEST=true ALLOW_FAILURE=true
255 + - node_js: "iojs-v1.4"
256 + env: TEST=true ALLOW_FAILURE=true
257 + - node_js: "iojs-v1.3"
258 + env: TEST=true ALLOW_FAILURE=true
259 + - node_js: "iojs-v1.2"
260 + env: TEST=true ALLOW_FAILURE=true
261 + - node_js: "iojs-v1.1"
262 + env: TEST=true ALLOW_FAILURE=true
263 + - node_js: "iojs-v1.0"
264 + env: TEST=true ALLOW_FAILURE=true
265 + - node_js: "0.11"
266 + env: TEST=true ALLOW_FAILURE=true
267 + - node_js: "0.9"
268 + env: TEST=true ALLOW_FAILURE=true
269 + - node_js: "0.6"
270 + env: TEST=true ALLOW_FAILURE=true
271 + - node_js: "0.4"
272 + env: TEST=true ALLOW_FAILURE=true
273 + allow_failures:
274 + - os: osx
275 + - env: TEST=true ALLOW_FAILURE=true
276 + - env: COVERAGE=true
277 + - env: POSTTEST=true
1 +1.1.1 / 2019-04-06
2 +=================
3 + * [Fix] exclude deprecated Firefox keys (#53)
4 +
5 +1.1.0 / 2019-02-10
6 +=================
7 + * [New] [Refactor] move full implementation to `implementation` entry point
8 + * [Refactor] only evaluate the implementation if `Object.keys` is not present
9 + * [Tests] up to `node` `v11.8`, `v10.15`, `v8.15`, `v6.16`
10 + * [Tests] remove jscs
11 + * [Tests] switch to `npm audit` from `nsp`
12 +
13 +1.0.12 / 2018-06-18
14 +=================
15 + * [Fix] avoid accessing `window.applicationCache`, to avoid issues with latest Chrome on HTTP (#46)
16 +
17 +1.0.11 / 2016-07-05
18 +=================
19 + * [Fix] exclude keys regarding the style (eg. `pageYOffset`) on `window` to avoid reflow (#32)
20 +
21 +1.0.10 / 2016-07-04
22 +=================
23 + * [Fix] exclude `height` and `width` keys on `window` to avoid reflow (#31)
24 + * [Fix] In IE 6, `window.external` makes `Object.keys` throw
25 + * [Tests] up to `node` `v6.2`, `v5.10`, `v4.4`
26 + * [Tests] use pretest/posttest for linting/security
27 + * [Dev Deps] update `tape`, `jscs`, `nsp`, `eslint`, `@ljharb/eslint-config`
28 + * [Dev Deps] remove unused eccheck script + dep
29 +
30 +1.0.9 / 2015-10-19
31 +=================
32 + * [Fix] Blacklist 'frame' property on window (#16, #17)
33 + * [Dev Deps] update `jscs`, `eslint`, `@ljharb/eslint-config`
34 +
35 +1.0.8 / 2015-10-14
36 +=================
37 + * [Fix] wrap automation equality bug checking in try/catch, per [es5-shim#327](https://github.com/es-shims/es5-shim/issues/327)
38 + * [Fix] Blacklist 'window.frameElement' per [es5-shim#322](https://github.com/es-shims/es5-shim/issues/322)
39 + * [Docs] Switch from vb.teelaun.ch to versionbadg.es for the npm version badge SVG
40 + * [Tests] up to `io.js` `v3.3`, `node` `v4.2`
41 + * [Dev Deps] update `eslint`, `tape`, `@ljharb/eslint-config`, `jscs`
42 +
43 +1.0.7 / 2015-07-18
44 +=================
45 + * [Fix] A proper fix for 176f03335e90d5c8d0d8125a99f27819c9b9cdad / https://github.com/es-shims/es5-shim/issues/275 that doesn't break dontEnum/constructor fixes in IE 8.
46 + * [Fix] Remove deprecation message in Chrome by touching deprecated window properties (#15)
47 + * [Tests] Improve test output for automation equality bugfix
48 + * [Tests] Test on `io.js` `v2.4`
49 +
50 +1.0.6 / 2015-07-09
51 +=================
52 + * [Fix] Use an object lookup rather than ES5's `indexOf` (#14)
53 + * [Tests] ES3 browsers don't have `Array.isArray`
54 + * [Tests] Fix `no-shadow` rule, as well as an IE 8 bug caused by engine NFE shadowing bugs.
55 +
56 +1.0.5 / 2015-07-03
57 +=================
58 + * [Fix] Fix a flabbergasting IE 8 bug where `localStorage.constructor.prototype === localStorage` throws
59 + * [Tests] Test up to `io.js` `v2.3`
60 + * [Dev Deps] Update `nsp`, `eslint`
61 +
62 +1.0.4 / 2015-05-23
63 +=================
64 + * Fix a Safari 5.0 bug with `Object.keys` not working with `arguments`
65 + * Test on latest `node` and `io.js`
66 + * Update `jscs`, `tape`, `eslint`, `nsp`, `is`, `editorconfig-tools`, `covert`
67 +
68 +1.0.3 / 2015-01-06
69 +=================
70 + * Revert "Make `object-keys` more robust against later environment tampering" to maintain ES3 compliance
71 +
72 +1.0.2 / 2014-12-28
73 +=================
74 + * Update lots of dev dependencies
75 + * Tweaks to README
76 + * Make `object-keys` more robust against later environment tampering
77 +
78 +1.0.1 / 2014-09-03
79 +=================
80 + * Update URLs and badges in README
81 +
82 +1.0.0 / 2014-08-26
83 +=================
84 + * v1.0.0
85 +
86 +0.6.1 / 2014-08-25
87 +=================
88 + * v0.6.1
89 + * Updating dependencies (tape, covert, is)
90 + * Update badges in readme
91 + * Use separate var statements
92 +
93 +0.6.0 / 2014-04-23
94 +=================
95 + * v0.6.0
96 + * Updating dependencies (tape, covert)
97 + * Make sure boxed primitives, and arguments objects, work properly in ES3 browsers
98 + * Improve test matrix: test all node versions, but only latest two stables are a failure
99 + * Remove internal foreach shim.
100 +
101 +0.5.1 / 2014-03-09
102 +=================
103 + * 0.5.1
104 + * Updating dependencies (tape, covert, is)
105 + * Removing forEach from the module (but keeping it in tests)
106 +
107 +0.5.0 / 2014-01-30
108 +=================
109 + * 0.5.0
110 + * Explicitly returning the shim, instead of returning native Object.keys when present
111 + * Adding a changelog.
112 + * Cleaning up IIFE wrapping
113 + * Testing on node 0.4 through 0.11
114 +
115 +0.4.0 / 2013-08-14
116 +==================
117 +
118 + * v0.4.0
119 + * In Chrome 4-10 and Safari 4, typeof (new RegExp) === 'function'
120 + * If it's a string, make sure to use charAt instead of brackets.
121 + * Only use Function#call if necessary.
122 + * Making sure the context tests actually run.
123 + * Better function detection
124 + * Adding the android browser
125 + * Fixing testling files
126 + * Updating tape
127 + * Removing the "is" dependency.
128 + * Making an isArguments shim.
129 + * Adding a local forEach shim and tests.
130 + * Updating paths.
131 + * Moving the shim test.
132 + * v0.3.0
133 +
134 +0.3.0 / 2013-05-18
135 +==================
136 +
137 + * README tweak.
138 + * Fixing constructor enum issue. Fixes [#5](https://github.com/ljharb/object-keys/issues/5).
139 + * Adding a test for [#5](https://github.com/ljharb/object-keys/issues/5)
140 + * Updating readme.
141 + * Updating dependencies.
142 + * Giving credit to lodash.
143 + * Make sure that a prototype's constructor property is not enumerable. Fixes [#3](https://github.com/ljharb/object-keys/issues/3).
144 + * Adding additional tests to handle arguments objects, and to skip "prototype" in functions. Fixes [#2](https://github.com/ljharb/object-keys/issues/2).
145 + * Fixing a typo on this test for [#3](https://github.com/ljharb/object-keys/issues/3).
146 + * Adding node 0.10 to travis.
147 + * Adding an IE < 9 test per [#3](https://github.com/ljharb/object-keys/issues/3)
148 + * Adding an iOS 5 mobile Safari test per [#2](https://github.com/ljharb/object-keys/issues/2)
149 + * Moving "indexof" and "is" to be dev dependencies.
150 + * Making sure the shim works with functions.
151 + * Flattening the tests.
152 +
153 +0.2.0 / 2013-05-10
154 +==================
155 +
156 + * v0.2.0
157 + * Object.keys should work with arrays.
158 +
159 +0.1.8 / 2013-05-10
160 +==================
161 +
162 + * v0.1.8
163 + * Upgrading dependencies.
164 + * Using a simpler check.
165 + * Fixing a bug in hasDontEnumBug browsers.
166 + * Using the newest tape!
167 + * Fixing this error test.
168 + * "undefined" is probably a reserved word in ES3.
169 + * Better test message.
170 +
171 +0.1.7 / 2013-04-17
172 +==================
173 +
174 + * Upgrading "is" once more.
175 + * The key "null" is breaking some browsers.
176 +
177 +0.1.6 / 2013-04-17
178 +==================
179 +
180 + * v0.1.6
181 + * Upgrading "is"
182 +
183 +0.1.5 / 2013-04-14
184 +==================
185 +
186 + * Bumping version.
187 + * Adding more testling browsers.
188 + * Updating "is"
189 +
190 +0.1.4 / 2013-04-08
191 +==================
192 +
193 + * Using "is" instead of "is-extended".
194 +
195 +0.1.3 / 2013-04-07
196 +==================
197 +
198 + * Using "foreach" instead of my own shim.
199 + * Removing "tap"; I'll just wait for "tape" to fix its node 0.10 bug.
200 +
201 +0.1.2 / 2013-04-03
202 +==================
203 +
204 + * Adding dependency status; moving links to an index at the bottom.
205 + * Upgrading is-extended; version 0.1.2
206 + * Adding an npm version badge.
207 +
208 +0.1.1 / 2013-04-01
209 +==================
210 +
211 + * Adding Travis CI.
212 + * Bumping the version.
213 + * Adding indexOf since IE sucks.
214 + * Adding a forEach shim since older browsers don't have Array#forEach.
215 + * Upgrading tape - 0.3.2 uses Array#map
216 + * Using explicit end instead of plan.
217 + * Can't test with Array.isArray in older browsers.
218 + * Using is-extended.
219 + * Fixing testling files.
220 + * JSHint/JSLint-ing.
221 + * Removing an unused object.
222 + * Using strict mode.
223 +
224 +0.1.0 / 2013-03-30
225 +==================
226 +
227 + * Changing the exports should have meant a higher version bump.
228 + * Oops, fixing the repo URL.
229 + * Adding more tests.
230 + * 0.0.2
231 + * Merge branch 'export_one_thing'; closes [#1](https://github.com/ljharb/object-keys/issues/1)
232 + * Move shim export to a separate file.
1 +The MIT License (MIT)
2 +
3 +Copyright (C) 2013 Jordan Harband
4 +
5 +Permission is hereby granted, free of charge, to any person obtaining a copy
6 +of this software and associated documentation files (the "Software"), to deal
7 +in the Software without restriction, including without limitation the rights
8 +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 +copies of the Software, and to permit persons to whom the Software is
10 +furnished to do so, subject to the following conditions:
11 +
12 +The above copyright notice and this permission notice shall be included in
13 +all copies or substantial portions of the Software.
14 +
15 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 +THE SOFTWARE.
...\ No newline at end of file ...\ No newline at end of file
1 +#object-keys <sup>[![Version Badge][npm-version-svg]][package-url]</sup>
2 +
3 +[![Build Status][travis-svg]][travis-url]
4 +[![dependency status][deps-svg]][deps-url]
5 +[![dev dependency status][dev-deps-svg]][dev-deps-url]
6 +[![License][license-image]][license-url]
7 +[![Downloads][downloads-image]][downloads-url]
8 +
9 +[![npm badge][npm-badge-png]][package-url]
10 +
11 +[![browser support][testling-svg]][testling-url]
12 +
13 +An Object.keys shim. Invoke its "shim" method to shim Object.keys if it is unavailable.
14 +
15 +Most common usage:
16 +```js
17 +var keys = Object.keys || require('object-keys');
18 +```
19 +
20 +## Example
21 +
22 +```js
23 +var keys = require('object-keys');
24 +var assert = require('assert');
25 +var obj = {
26 + a: true,
27 + b: true,
28 + c: true
29 +};
30 +
31 +assert.deepEqual(keys(obj), ['a', 'b', 'c']);
32 +```
33 +
34 +```js
35 +var keys = require('object-keys');
36 +var assert = require('assert');
37 +/* when Object.keys is not present */
38 +delete Object.keys;
39 +var shimmedKeys = keys.shim();
40 +assert.equal(shimmedKeys, keys);
41 +assert.deepEqual(Object.keys(obj), keys(obj));
42 +```
43 +
44 +```js
45 +var keys = require('object-keys');
46 +var assert = require('assert');
47 +/* when Object.keys is present */
48 +var shimmedKeys = keys.shim();
49 +assert.equal(shimmedKeys, Object.keys);
50 +assert.deepEqual(Object.keys(obj), keys(obj));
51 +```
52 +
53 +## Source
54 +Implementation taken directly from [es5-shim][es5-shim-url], with modifications, including from [lodash][lodash-url].
55 +
56 +## Tests
57 +Simply clone the repo, `npm install`, and run `npm test`
58 +
59 +[package-url]: https://npmjs.org/package/object-keys
60 +[npm-version-svg]: http://versionbadg.es/ljharb/object-keys.svg
61 +[travis-svg]: https://travis-ci.org/ljharb/object-keys.svg
62 +[travis-url]: https://travis-ci.org/ljharb/object-keys
63 +[deps-svg]: https://david-dm.org/ljharb/object-keys.svg
64 +[deps-url]: https://david-dm.org/ljharb/object-keys
65 +[dev-deps-svg]: https://david-dm.org/ljharb/object-keys/dev-status.svg
66 +[dev-deps-url]: https://david-dm.org/ljharb/object-keys#info=devDependencies
67 +[testling-svg]: https://ci.testling.com/ljharb/object-keys.png
68 +[testling-url]: https://ci.testling.com/ljharb/object-keys
69 +[es5-shim-url]: https://github.com/es-shims/es5-shim/blob/master/es5-shim.js#L542-589
70 +[lodash-url]: https://github.com/lodash/lodash
71 +[npm-badge-png]: https://nodei.co/npm/object-keys.png?downloads=true&stars=true
72 +[license-image]: http://img.shields.io/npm/l/object-keys.svg
73 +[license-url]: LICENSE
74 +[downloads-image]: http://img.shields.io/npm/dm/object-keys.svg
75 +[downloads-url]: http://npm-stat.com/charts.html?package=object-keys
76 +
1 +'use strict';
2 +
3 +var keysShim;
4 +if (!Object.keys) {
5 + // modified from https://github.com/es-shims/es5-shim
6 + var has = Object.prototype.hasOwnProperty;
7 + var toStr = Object.prototype.toString;
8 + var isArgs = require('./isArguments'); // eslint-disable-line global-require
9 + var isEnumerable = Object.prototype.propertyIsEnumerable;
10 + var hasDontEnumBug = !isEnumerable.call({ toString: null }, 'toString');
11 + var hasProtoEnumBug = isEnumerable.call(function () {}, 'prototype');
12 + var dontEnums = [
13 + 'toString',
14 + 'toLocaleString',
15 + 'valueOf',
16 + 'hasOwnProperty',
17 + 'isPrototypeOf',
18 + 'propertyIsEnumerable',
19 + 'constructor'
20 + ];
21 + var equalsConstructorPrototype = function (o) {
22 + var ctor = o.constructor;
23 + return ctor && ctor.prototype === o;
24 + };
25 + var excludedKeys = {
26 + $applicationCache: true,
27 + $console: true,
28 + $external: true,
29 + $frame: true,
30 + $frameElement: true,
31 + $frames: true,
32 + $innerHeight: true,
33 + $innerWidth: true,
34 + $onmozfullscreenchange: true,
35 + $onmozfullscreenerror: true,
36 + $outerHeight: true,
37 + $outerWidth: true,
38 + $pageXOffset: true,
39 + $pageYOffset: true,
40 + $parent: true,
41 + $scrollLeft: true,
42 + $scrollTop: true,
43 + $scrollX: true,
44 + $scrollY: true,
45 + $self: true,
46 + $webkitIndexedDB: true,
47 + $webkitStorageInfo: true,
48 + $window: true
49 + };
50 + var hasAutomationEqualityBug = (function () {
51 + /* global window */
52 + if (typeof window === 'undefined') { return false; }
53 + for (var k in window) {
54 + try {
55 + if (!excludedKeys['$' + k] && has.call(window, k) && window[k] !== null && typeof window[k] === 'object') {
56 + try {
57 + equalsConstructorPrototype(window[k]);
58 + } catch (e) {
59 + return true;
60 + }
61 + }
62 + } catch (e) {
63 + return true;
64 + }
65 + }
66 + return false;
67 + }());
68 + var equalsConstructorPrototypeIfNotBuggy = function (o) {
69 + /* global window */
70 + if (typeof window === 'undefined' || !hasAutomationEqualityBug) {
71 + return equalsConstructorPrototype(o);
72 + }
73 + try {
74 + return equalsConstructorPrototype(o);
75 + } catch (e) {
76 + return false;
77 + }
78 + };
79 +
80 + keysShim = function keys(object) {
81 + var isObject = object !== null && typeof object === 'object';
82 + var isFunction = toStr.call(object) === '[object Function]';
83 + var isArguments = isArgs(object);
84 + var isString = isObject && toStr.call(object) === '[object String]';
85 + var theKeys = [];
86 +
87 + if (!isObject && !isFunction && !isArguments) {
88 + throw new TypeError('Object.keys called on a non-object');
89 + }
90 +
91 + var skipProto = hasProtoEnumBug && isFunction;
92 + if (isString && object.length > 0 && !has.call(object, 0)) {
93 + for (var i = 0; i < object.length; ++i) {
94 + theKeys.push(String(i));
95 + }
96 + }
97 +
98 + if (isArguments && object.length > 0) {
99 + for (var j = 0; j < object.length; ++j) {
100 + theKeys.push(String(j));
101 + }
102 + } else {
103 + for (var name in object) {
104 + if (!(skipProto && name === 'prototype') && has.call(object, name)) {
105 + theKeys.push(String(name));
106 + }
107 + }
108 + }
109 +
110 + if (hasDontEnumBug) {
111 + var skipConstructor = equalsConstructorPrototypeIfNotBuggy(object);
112 +
113 + for (var k = 0; k < dontEnums.length; ++k) {
114 + if (!(skipConstructor && dontEnums[k] === 'constructor') && has.call(object, dontEnums[k])) {
115 + theKeys.push(dontEnums[k]);
116 + }
117 + }
118 + }
119 + return theKeys;
120 + };
121 +}
122 +module.exports = keysShim;
1 +'use strict';
2 +
3 +var slice = Array.prototype.slice;
4 +var isArgs = require('./isArguments');
5 +
6 +var origKeys = Object.keys;
7 +var keysShim = origKeys ? function keys(o) { return origKeys(o); } : require('./implementation');
8 +
9 +var originalKeys = Object.keys;
10 +
11 +keysShim.shim = function shimObjectKeys() {
12 + if (Object.keys) {
13 + var keysWorksWithArguments = (function () {
14 + // Safari 5.0 bug
15 + var args = Object.keys(arguments);
16 + return args && args.length === arguments.length;
17 + }(1, 2));
18 + if (!keysWorksWithArguments) {
19 + Object.keys = function keys(object) { // eslint-disable-line func-name-matching
20 + if (isArgs(object)) {
21 + return originalKeys(slice.call(object));
22 + }
23 + return originalKeys(object);
24 + };
25 + }
26 + } else {
27 + Object.keys = keysShim;
28 + }
29 + return Object.keys || keysShim;
30 +};
31 +
32 +module.exports = keysShim;
1 +'use strict';
2 +
3 +var toStr = Object.prototype.toString;
4 +
5 +module.exports = function isArguments(value) {
6 + var str = toStr.call(value);
7 + var isArgs = str === '[object Arguments]';
8 + if (!isArgs) {
9 + isArgs = str !== '[object Array]' &&
10 + value !== null &&
11 + typeof value === 'object' &&
12 + typeof value.length === 'number' &&
13 + value.length >= 0 &&
14 + toStr.call(value.callee) === '[object Function]';
15 + }
16 + return isArgs;
17 +};
1 +{
2 + "name": "object-keys",
3 + "version": "1.1.1",
4 + "author": {
5 + "name": "Jordan Harband",
6 + "email": "ljharb@gmail.com",
7 + "url": "http://ljharb.codes"
8 + },
9 + "contributors": [
10 + {
11 + "name": "Jordan Harband",
12 + "email": "ljharb@gmail.com",
13 + "url": "http://ljharb.codes"
14 + },
15 + {
16 + "name": "Raynos",
17 + "email": "raynos2@gmail.com"
18 + },
19 + {
20 + "name": "Nathan Rajlich",
21 + "email": "nathan@tootallnate.net"
22 + },
23 + {
24 + "name": "Ivan Starkov",
25 + "email": "istarkov@gmail.com"
26 + },
27 + {
28 + "name": "Gary Katsevman",
29 + "email": "git@gkatsev.com"
30 + }
31 + ],
32 + "description": "An Object.keys replacement, in case Object.keys is not available. From https://github.com/es-shims/es5-shim",
33 + "license": "MIT",
34 + "main": "index.js",
35 + "scripts": {
36 + "pretest": "npm run --silent lint",
37 + "test": "npm run --silent tests-only",
38 + "posttest": "npm run --silent audit",
39 + "tests-only": "node test/index.js",
40 + "coverage": "covert test/*.js",
41 + "coverage-quiet": "covert test/*.js --quiet",
42 + "lint": "eslint .",
43 + "preaudit": "npm install --package-lock --package-lock-only",
44 + "audit": "npm audit",
45 + "postaudit": "rm package-lock.json"
46 + },
47 + "repository": {
48 + "type": "git",
49 + "url": "git://github.com/ljharb/object-keys.git"
50 + },
51 + "keywords": [
52 + "Object.keys",
53 + "keys",
54 + "ES5",
55 + "shim"
56 + ],
57 + "dependencies": {},
58 + "devDependencies": {
59 + "@ljharb/eslint-config": "^13.1.1",
60 + "covert": "^1.1.1",
61 + "eslint": "^5.13.0",
62 + "foreach": "^2.0.5",
63 + "indexof": "^0.0.1",
64 + "is": "^3.3.0",
65 + "tape": "^4.9.2"
66 + },
67 + "testling": {
68 + "files": "test/index.js",
69 + "browsers": [
70 + "iexplore/6.0..latest",
71 + "firefox/3.0..6.0",
72 + "firefox/15.0..latest",
73 + "firefox/nightly",
74 + "chrome/4.0..10.0",
75 + "chrome/20.0..latest",
76 + "chrome/canary",
77 + "opera/10.0..latest",
78 + "opera/next",
79 + "safari/4.0..latest",
80 + "ipad/6.0..latest",
81 + "iphone/6.0..latest",
82 + "android-browser/4.2"
83 + ]
84 + },
85 + "engines": {
86 + "node": ">= 0.4"
87 + }
88 +}
1 +'use strict';
2 +
3 +require('./isArguments');
4 +
5 +require('./shim');
1 +language: node_js
2 +node_js:
3 + - '0.10'
4 + - '0.12'
5 + - '4'
6 + - '6'
1 +The MIT License (MIT)
2 +
3 +Copyright (c) 2016 Mathias Buus
4 +
5 +Permission is hereby granted, free of charge, to any person obtaining a copy
6 +of this software and associated documentation files (the "Software"), to deal
7 +in the Software without restriction, including without limitation the rights
8 +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 +copies of the Software, and to permit persons to whom the Software is
10 +furnished to do so, subject to the following conditions:
11 +
12 +The above copyright notice and this permission notice shall be included in
13 +all copies or substantial portions of the Software.
14 +
15 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 +THE SOFTWARE.
1 +# sorted-array-functions
2 +
3 +Maintain and search through a sorted array using some low level functions
4 +
5 +```
6 +npm install sorted-array-functions
7 +```
8 +
9 +[![build status](http://img.shields.io/travis/mafintosh/sorted-array-functions.svg?style=flat)](http://travis-ci.org/mafintosh/sorted-array-functions)
10 +
11 +## Usage
12 +
13 +``` js
14 +var sorted = require('sorted-array-functions')
15 +var list = []
16 +
17 +sorted.add(list, 1)
18 +sorted.add(list, 4)
19 +sorted.add(list, 2)
20 +
21 +console.log(list) // prints out [1, 2, 4]
22 +console.log(sorted.has(list, 2)) // returns true
23 +console.log(sorted.has(list, 3)) // returns false
24 +console.log(sorted.eq(list, 2)) // returns 1 (the index)
25 +console.log(sorted.gt(list, 2)) // returns 2
26 +console.log(sorted.gt(list, 4)) // returns -1
27 +```
28 +
29 +## API
30 +
31 +#### `sorted.add(list, value, [compare])`
32 +
33 +Insert a new value into the list sorted.
34 +Optionally you can use a custom compare function that returns, `compare(a, b)` that returns 1 if `a > b`, 0 if `a === b` and -1 if `a < b`.
35 +
36 +#### `sorted.addFromFront(list, value, [compare])`
37 +
38 +Inserts a new value (same result as `sorted.add()`) optimized for prepend.
39 +
40 +#### `var bool = sorted.remove(list, value, [compare])`
41 +
42 +Remove a value. Returns true if the value was in the list.
43 +
44 +#### `var bool = sorted.has(list, value, [compare])`
45 +
46 +Check if a value is in the list.
47 +
48 +#### `var index = sorted.eq(list, value, [compare])`
49 +
50 +Get the index of a value in the list (uses binary search).
51 +If the value could not be found -1 is returned.
52 +
53 +#### `var index = sorted.gte(list, value, [compare])`
54 +
55 +Get the index of the first value that is `>=`.
56 +If the value could not be found -1 is returned.
57 +
58 +#### `var index = sorted.gt(list, value, [compare])`
59 +
60 +Get the index of the first value that is `>`.
61 +If the value could not be found -1 is returned.
62 +
63 +#### `var index = sorted.lte(list, value, [compare])`
64 +
65 +Get the index of the first value that is `<=`.
66 +If the value could not be found -1 is returned.
67 +
68 +#### `var index = sorted.lt(list, value, [compare])`
69 +
70 +Get the index of the first value that is `<`.
71 +If the value could not be found -1 is returned.
72 +
73 +## License
74 +
75 +MIT
1 +var sorted = require('./')
2 +var list = []
3 +
4 +sorted.add(list, 1)
5 +sorted.add(list, 4)
6 +sorted.add(list, 2)
7 +
8 +console.log(list) // prints out [1, 2, 4]
9 +console.log(sorted.has(list, 2)) // returns true
10 +console.log(sorted.has(list, 3)) // returns false
11 +console.log(sorted.eq(list, 2)) // returns 1 (the index)
12 +console.log(sorted.gt(list, 2)) // returns 2
13 +console.log(sorted.gt(list, 4)) // returns -1
1 +exports.add = add
2 +exports.addFromFront = addFromFront
3 +exports.remove = remove
4 +exports.has = has
5 +exports.eq = eq
6 +exports.lte = lte
7 +exports.lt = lt
8 +exports.gte = gte
9 +exports.gt = gt
10 +exports.nearest = nearest
11 +
12 +function defaultCmp (a, b) {
13 + if (a === b) return 0
14 + return a < b ? -1 : 1
15 +}
16 +
17 +function add (list, value, cmp) {
18 + if (!cmp) cmp = defaultCmp
19 +
20 + var top = list.push(value) - 1
21 +
22 + while (top) {
23 + if (cmp(list[top - 1], value) < 0) return
24 + list[top] = list[top - 1]
25 + list[top - 1] = value
26 + top--
27 + }
28 +}
29 +
30 +function addFromFront (list, value, cmp) {
31 + if (!cmp) cmp = defaultCmp
32 +
33 + var top = list.unshift(value) - 1
34 +
35 + for (var i = 0; i < top; i++) {
36 + if (cmp(value, list[i + 1]) < 0) return
37 + list[i] = list[i + 1]
38 + list[i + 1] = value
39 + }
40 +}
41 +
42 +function lte (list, value, cmp) {
43 + if (!cmp) cmp = defaultCmp
44 +
45 + var i = indexOf(list, value, cmp)
46 + if (i === -1) return -1
47 +
48 + for (; i >= 0; i--) {
49 + var c = cmp(list[i], value)
50 + if (c <= 0) return i
51 + }
52 +
53 + return -1
54 +}
55 +
56 +function lt (list, value, cmp) {
57 + if (!cmp) cmp = defaultCmp
58 +
59 + var i = indexOf(list, value, cmp)
60 + if (i === -1) return -1
61 +
62 + for (; i >= 0; i--) {
63 + var c = cmp(list[i], value)
64 + if (c < 0) return i
65 + }
66 +
67 + return -1
68 +}
69 +
70 +function gte (list, value, cmp) {
71 + if (!cmp) cmp = defaultCmp
72 +
73 + var i = indexOf(list, value, cmp)
74 + if (i === -1) return -1
75 +
76 + for (; i < list.length; i++) {
77 + var c = cmp(list[i], value)
78 + if (c >= 0) return i
79 + }
80 +
81 + return -1
82 +}
83 +
84 +function gt (list, value, cmp) {
85 + if (!cmp) cmp = defaultCmp
86 +
87 + var i = indexOf(list, value, cmp)
88 + if (i === -1) return -1
89 +
90 + for (; i < list.length; i++) {
91 + var c = cmp(list[i], value)
92 + if (c > 0) return i
93 + }
94 +
95 + return -1
96 +}
97 +
98 +function eq (list, value, cmp) {
99 + if (!cmp) cmp = defaultCmp
100 +
101 + var i = indexOf(list, value, cmp)
102 + if (i === -1) return -1
103 + return cmp(list[i], value) === 0 ? i : -1
104 +}
105 +
106 +function nearest (list, value, cmp) {
107 + if (!cmp) cmp = defaultCmp
108 +
109 + var len = list.length
110 + var top = len - 1
111 + var btm = 0
112 + var mid = -1
113 + var trending = 1 // 0 = down, 2 = up
114 +
115 + while (top >= btm && btm >= 0 && top < len) {
116 + mid = Math.floor((top + btm) / 2)
117 +
118 + var c = cmp(list[mid], value)
119 + if (c === 0) return mid
120 +
121 + if (c >= 0) {
122 + if (trending === 1) trending = 0
123 + else if (trending === 2) {
124 + if (Math.abs(list[mid] - value) > Math.abs(list[mid - 1] - value)) return mid - 1
125 + return mid
126 + }
127 +
128 + top = mid - 1
129 + } else {
130 + if (trending === 1) trending = 2
131 + else if (trending === 0) return mid
132 +
133 + btm = mid + 1
134 + }
135 + }
136 +
137 + return mid
138 +}
139 +
140 +function indexOf (list, value, cmp) {
141 + if (!cmp) cmp = defaultCmp
142 +
143 + var len = list.length
144 + var top = len - 1
145 + var btm = 0
146 + var mid = -1
147 +
148 + while (top >= btm && btm >= 0 && top < len) {
149 + mid = Math.floor((top + btm) / 2)
150 +
151 + var c = cmp(list[mid], value)
152 + if (c === 0) return mid
153 +
154 + if (c >= 0) {
155 + top = mid - 1
156 + } else {
157 + btm = mid + 1
158 + }
159 + }
160 +
161 + return mid
162 +}
163 +
164 +function has (list, value, cmp) {
165 + return eq(list, value, cmp) > -1
166 +}
167 +
168 +function remove (list, value, cmp) {
169 + var i = eq(list, value, cmp)
170 + if (i === -1) return false
171 + list.splice(i, 1)
172 + return true
173 +}
1 +{
2 + "name": "sorted-array-functions",
3 + "version": "1.3.0",
4 + "description": "Maintain and search through a sorted array using some low level functions",
5 + "main": "index.js",
6 + "dependencies": {},
7 + "devDependencies": {
8 + "standard": "^8.4.0",
9 + "tape": "^4.6.2"
10 + },
11 + "scripts": {
12 + "test": "standard && tape test.js"
13 + },
14 + "repository": {
15 + "type": "git",
16 + "url": "https://github.com/mafintosh/sorted-array-functions.git"
17 + },
18 + "author": "Mathias Buus (@mafintosh)",
19 + "license": "MIT",
20 + "bugs": {
21 + "url": "https://github.com/mafintosh/sorted-array-functions/issues"
22 + },
23 + "homepage": "https://github.com/mafintosh/sorted-array-functions"
24 +}
1 +var tape = require('tape')
2 +var sorted = require('./')
3 +
4 +tape('add', function (t) {
5 + var list = []
6 +
7 + sorted.add(list, 3)
8 + sorted.add(list, 4)
9 + sorted.add(list, 3)
10 + sorted.add(list, 9)
11 + sorted.add(list, 0)
12 + sorted.add(list, 5)
13 + sorted.add(list, 8)
14 +
15 + t.same(list, [0, 3, 3, 4, 5, 8, 9])
16 + t.end()
17 +})
18 +
19 +tape('addFromFront', function (t) {
20 + var list = []
21 +
22 + sorted.addFromFront(list, 3)
23 + sorted.addFromFront(list, 4)
24 + sorted.addFromFront(list, 3)
25 + sorted.addFromFront(list, 9)
26 + sorted.addFromFront(list, 0)
27 + sorted.addFromFront(list, 5)
28 + sorted.addFromFront(list, 8)
29 +
30 + t.same(list, [0, 3, 3, 4, 5, 8, 9])
31 + t.end()
32 +})
33 +
34 +tape('remove', function (t) {
35 + var list = []
36 +
37 + sorted.add(list, 3)
38 + sorted.add(list, 4)
39 + sorted.add(list, 3)
40 + sorted.add(list, 9)
41 + sorted.add(list, 0)
42 + sorted.add(list, 5)
43 + sorted.add(list, 8)
44 +
45 + sorted.remove(list, 3)
46 + sorted.remove(list, 5)
47 + sorted.remove(list, 6)
48 +
49 + t.same(list, [0, 3, 4, 8, 9])
50 + t.end()
51 +})
52 +
53 +tape('has', function (t) {
54 + var list = []
55 +
56 + sorted.add(list, 3)
57 + t.same(sorted.has(list, 3), true)
58 + t.same(sorted.has(list, 2), false)
59 +
60 + sorted.add(list, 5)
61 + t.same(sorted.has(list, 5), true)
62 + t.same(sorted.has(list, 3), true)
63 + t.same(sorted.has(list, 2), false)
64 +
65 + sorted.add(list, 1)
66 + t.same(sorted.has(list, 1), true)
67 + t.same(sorted.has(list, 5), true)
68 + t.same(sorted.has(list, 3), true)
69 + t.same(sorted.has(list, 2), false)
70 + t.same(sorted.has(list, 8), false)
71 +
72 + t.end()
73 +})
74 +
75 +tape('eq', function (t) {
76 + var list = []
77 +
78 + sorted.add(list, 3)
79 + t.same(sorted.eq(list, 3), 0)
80 + t.same(sorted.eq(list, 2), -1)
81 +
82 + sorted.add(list, 5)
83 + t.same(sorted.eq(list, 5), 1)
84 + t.same(sorted.eq(list, 3), 0)
85 + t.same(sorted.eq(list, 2), -1)
86 +
87 + sorted.add(list, 1)
88 + t.same(sorted.eq(list, 1), 0)
89 + t.same(sorted.eq(list, 5), 2)
90 + t.same(sorted.eq(list, 3), 1)
91 + t.same(sorted.eq(list, 2), -1)
92 + t.same(sorted.eq(list, 8), -1)
93 +
94 + t.end()
95 +})
96 +
97 +tape('gte', function (t) {
98 + var list = []
99 +
100 + sorted.add(list, 3)
101 + t.same(sorted.gte(list, 3), 0)
102 + t.same(sorted.gte(list, 2), 0)
103 +
104 + sorted.add(list, 5)
105 + t.same(sorted.gte(list, 5), 1)
106 + t.same(sorted.gte(list, 3), 0)
107 + t.same(sorted.gte(list, 2), 0)
108 +
109 + sorted.add(list, 1)
110 + t.same(sorted.gte(list, 1), 0)
111 + t.same(sorted.gte(list, 5), 2)
112 + t.same(sorted.gte(list, 3), 1)
113 + t.same(sorted.gte(list, 2), 1)
114 + t.same(sorted.gte(list, 8), -1)
115 +
116 + t.end()
117 +})
118 +
119 +tape('gt', function (t) {
120 + var list = []
121 +
122 + sorted.add(list, 3)
123 + t.same(sorted.gt(list, 3), -1)
124 + t.same(sorted.gt(list, 2), 0)
125 +
126 + sorted.add(list, 5)
127 + t.same(sorted.gt(list, 5), -1)
128 + t.same(sorted.gt(list, 3), 1)
129 + t.same(sorted.gt(list, 2), 0)
130 +
131 + sorted.add(list, 1)
132 + t.same(sorted.gt(list, 1), 1)
133 + t.same(sorted.gt(list, 5), -1)
134 + t.same(sorted.gt(list, 3), 2)
135 + t.same(sorted.gt(list, 2), 1)
136 + t.same(sorted.gt(list, 8), -1)
137 +
138 + t.end()
139 +})
140 +
141 +tape('lte', function (t) {
142 + var list = []
143 +
144 + sorted.add(list, 3)
145 + t.same(sorted.lte(list, 3), 0)
146 + t.same(sorted.lte(list, 2), -1)
147 +
148 + sorted.add(list, 5)
149 + t.same(sorted.lte(list, 6), 1)
150 + t.same(sorted.lte(list, 5), 1)
151 + t.same(sorted.lte(list, 3), 0)
152 + t.same(sorted.lte(list, 2), -1)
153 +
154 + sorted.add(list, 1)
155 + t.same(sorted.lte(list, 1), 0)
156 + t.same(sorted.lte(list, 5), 2)
157 + t.same(sorted.lte(list, 3), 1)
158 + t.same(sorted.lte(list, 2), 0)
159 + t.same(sorted.lte(list, 8), 2)
160 +
161 + t.end()
162 +})
163 +
164 +tape('lt', function (t) {
165 + var list = []
166 +
167 + sorted.add(list, 3)
168 + t.same(sorted.lt(list, 3), -1)
169 + t.same(sorted.lt(list, 2), -1)
170 + t.same(sorted.lt(list, 4), 0)
171 +
172 + sorted.add(list, 5)
173 + t.same(sorted.lt(list, 6), 1)
174 + t.same(sorted.lt(list, 5), 0)
175 + t.same(sorted.lt(list, 3), -1)
176 + t.same(sorted.lt(list, 2), -1)
177 +
178 + sorted.add(list, 1)
179 + t.same(sorted.lt(list, 1), -1)
180 + t.same(sorted.lt(list, 5), 1)
181 + t.same(sorted.lt(list, 3), 0)
182 + t.same(sorted.lt(list, 2), 0)
183 + t.same(sorted.lt(list, 8), 2)
184 +
185 + t.end()
186 +})
187 +
188 +tape('custom compare add', function (t) {
189 + var list = []
190 +
191 + sorted.add(list, {foo: 3}, cmp)
192 + sorted.add(list, {foo: 4}, cmp)
193 + sorted.add(list, {foo: 3}, cmp)
194 + sorted.add(list, {foo: 9}, cmp)
195 + sorted.add(list, {foo: 0}, cmp)
196 + sorted.add(list, {foo: 5}, cmp)
197 + sorted.add(list, {foo: 8}, cmp)
198 +
199 + t.same(list, [{foo: 0}, {foo: 3}, {foo: 3}, {foo: 4}, {foo: 5}, {foo: 8}, {foo: 9}])
200 + t.end()
201 +})
202 +
203 +tape('custom compare remove', function (t) {
204 + var list = []
205 +
206 + sorted.add(list, {foo: 3}, cmp)
207 + sorted.add(list, {foo: 4}, cmp)
208 + sorted.add(list, {foo: 3}, cmp)
209 + sorted.add(list, {foo: 9}, cmp)
210 + sorted.add(list, {foo: 0}, cmp)
211 + sorted.add(list, {foo: 5}, cmp)
212 + sorted.add(list, {foo: 8}, cmp)
213 +
214 + sorted.remove(list, {foo: 3}, cmp)
215 + sorted.remove(list, {foo: 5}, cmp)
216 + sorted.remove(list, {foo: 6}, cmp)
217 +
218 + t.same(list, [{foo: 0}, {foo: 3}, {foo: 4}, {foo: 8}, {foo: 9}])
219 + t.end()
220 +})
221 +
222 +tape('custom compare has', function (t) {
223 + var list = []
224 +
225 + sorted.add(list, {foo: 3}, cmp)
226 + t.same(sorted.has(list, {foo: 3}, cmp), true)
227 + t.same(sorted.has(list, {foo: 2}, cmp), false)
228 +
229 + sorted.add(list, {foo: 5}, cmp)
230 + t.same(sorted.has(list, {foo: 5}, cmp), true)
231 + t.same(sorted.has(list, {foo: 3}, cmp), true)
232 + t.same(sorted.has(list, {foo: 2}, cmp), false)
233 +
234 + sorted.add(list, {foo: 1}, cmp)
235 + t.same(sorted.has(list, {foo: 1}, cmp), true)
236 + t.same(sorted.has(list, {foo: 5}, cmp), true)
237 + t.same(sorted.has(list, {foo: 3}, cmp), true)
238 + t.same(sorted.has(list, {foo: 2}, cmp), false)
239 + t.same(sorted.has(list, {foo: 8}, cmp), false)
240 +
241 + t.end()
242 +})
243 +
244 +tape('custom compare eq', function (t) {
245 + var list = []
246 +
247 + sorted.add(list, {foo: 3}, cmp)
248 + t.same(sorted.eq(list, {foo: 3}, cmp), 0)
249 + t.same(sorted.eq(list, {foo: 2}, cmp), -1)
250 +
251 + sorted.add(list, {foo: 5}, cmp)
252 + t.same(sorted.eq(list, {foo: 5}, cmp), 1)
253 + t.same(sorted.eq(list, {foo: 3}, cmp), 0)
254 + t.same(sorted.eq(list, {foo: 2}, cmp), -1)
255 +
256 + sorted.add(list, {foo: 1}, cmp)
257 + t.same(sorted.eq(list, {foo: 1}, cmp), 0)
258 + t.same(sorted.eq(list, {foo: 5}, cmp), 2)
259 + t.same(sorted.eq(list, {foo: 3}, cmp), 1)
260 + t.same(sorted.eq(list, {foo: 2}, cmp), -1)
261 + t.same(sorted.eq(list, {foo: 8}, cmp), -1)
262 +
263 + t.end()
264 +})
265 +
266 +tape('custom compare gte', function (t) {
267 + var list = []
268 +
269 + sorted.add(list, {foo: 3}, cmp)
270 + t.same(sorted.gte(list, {foo: 3}, cmp), 0)
271 + t.same(sorted.gte(list, {foo: 2}, cmp), 0)
272 +
273 + sorted.add(list, {foo: 5}, cmp)
274 + t.same(sorted.gte(list, {foo: 5}, cmp), 1)
275 + t.same(sorted.gte(list, {foo: 3}, cmp), 0)
276 + t.same(sorted.gte(list, {foo: 2}, cmp), 0)
277 +
278 + sorted.add(list, {foo: 1}, cmp)
279 + t.same(sorted.gte(list, {foo: 1}, cmp), 0)
280 + t.same(sorted.gte(list, {foo: 5}, cmp), 2)
281 + t.same(sorted.gte(list, {foo: 3}, cmp), 1)
282 + t.same(sorted.gte(list, {foo: 2}, cmp), 1)
283 + t.same(sorted.gte(list, {foo: 8}, cmp), -1)
284 +
285 + t.end()
286 +})
287 +
288 +tape('custom compare gt', function (t) {
289 + var list = []
290 +
291 + sorted.add(list, {foo: 3}, cmp)
292 + t.same(sorted.gt(list, {foo: 3}, cmp), -1)
293 + t.same(sorted.gt(list, {foo: 2}, cmp), 0)
294 +
295 + sorted.add(list, {foo: 5}, cmp)
296 + t.same(sorted.gt(list, {foo: 5}, cmp), -1)
297 + t.same(sorted.gt(list, {foo: 3}, cmp), 1)
298 + t.same(sorted.gt(list, {foo: 2}, cmp), 0)
299 +
300 + sorted.add(list, {foo: 1}, cmp)
301 + t.same(sorted.gt(list, {foo: 1}, cmp), 1)
302 + t.same(sorted.gt(list, {foo: 5}, cmp), -1)
303 + t.same(sorted.gt(list, {foo: 3}, cmp), 2)
304 + t.same(sorted.gt(list, {foo: 2}, cmp), 1)
305 + t.same(sorted.gt(list, {foo: 8}, cmp), -1)
306 +
307 + t.end()
308 +})
309 +
310 +tape('custom compare lte', function (t) {
311 + var list = []
312 +
313 + sorted.add(list, {foo: 3}, cmp)
314 + t.same(sorted.lte(list, {foo: 3}, cmp), 0)
315 + t.same(sorted.lte(list, {foo: 2}, cmp), -1)
316 +
317 + sorted.add(list, {foo: 5}, cmp)
318 + t.same(sorted.lte(list, {foo: 6}, cmp), 1)
319 + t.same(sorted.lte(list, {foo: 5}, cmp), 1)
320 + t.same(sorted.lte(list, {foo: 3}, cmp), 0)
321 + t.same(sorted.lte(list, {foo: 2}, cmp), -1)
322 +
323 + sorted.add(list, {foo: 1}, cmp)
324 + t.same(sorted.lte(list, {foo: 1}, cmp), 0)
325 + t.same(sorted.lte(list, {foo: 5}, cmp), 2)
326 + t.same(sorted.lte(list, {foo: 3}, cmp), 1)
327 + t.same(sorted.lte(list, {foo: 2}, cmp), 0)
328 + t.same(sorted.lte(list, {foo: 8}, cmp), 2)
329 +
330 + t.end()
331 +})
332 +
333 +tape('custom compare lt', function (t) {
334 + var list = []
335 +
336 + sorted.add(list, {foo: 3}, cmp)
337 + t.same(sorted.lt(list, {foo: 3}, cmp), -1)
338 + t.same(sorted.lt(list, {foo: 2}, cmp), -1)
339 + t.same(sorted.lt(list, {foo: 4}, cmp), 0)
340 +
341 + sorted.add(list, {foo: 5}, cmp)
342 + t.same(sorted.lt(list, {foo: 6}, cmp), 1)
343 + t.same(sorted.lt(list, {foo: 5}, cmp), 0)
344 + t.same(sorted.lt(list, {foo: 3}, cmp), -1)
345 + t.same(sorted.lt(list, {foo: 2}, cmp), -1)
346 +
347 + sorted.add(list, {foo: 1}, cmp)
348 + t.same(sorted.lt(list, {foo: 1}, cmp), -1)
349 + t.same(sorted.lt(list, {foo: 5}, cmp), 1)
350 + t.same(sorted.lt(list, {foo: 3}, cmp), 0)
351 + t.same(sorted.lt(list, {foo: 2}, cmp), 0)
352 + t.same(sorted.lt(list, {foo: 8}, cmp), 2)
353 +
354 + t.end()
355 +})
356 +
357 +tape('find nearest value', function (t) {
358 + var list = []
359 +
360 + sorted.add(list, 0.001)
361 + sorted.add(list, 10)
362 + sorted.add(list, 20)
363 + sorted.add(list, 30)
364 + sorted.add(list, 40)
365 + sorted.add(list, 50)
366 + sorted.add(list, 70)
367 +
368 + t.equal(sorted.nearest(list, 66), 6)
369 + t.equal(sorted.nearest(list, 51), 5)
370 + t.equal(sorted.nearest(list, 1), 0)
371 + t.equal(sorted.nearest(list, 0), 0)
372 + t.equal(sorted.nearest(list, 69.999), 6)
373 + t.equal(sorted.nearest(list, 72), 6)
374 +
375 + t.end()
376 +})
377 +
378 +function cmp (a, b) {
379 + return a.foo - b.foo
380 +}
381 +
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
11 "dependencies": { 11 "dependencies": {
12 "cheerio": "^1.0.0-rc.9", 12 "cheerio": "^1.0.0-rc.9",
13 "express": "^4.17.1", 13 "express": "^4.17.1",
14 + "node-schedule": "^2.0.0",
14 "puppeteer": "^9.1.1", 15 "puppeteer": "^9.1.1",
15 "sanitize-html": "^2.3.3" 16 "sanitize-html": "^2.3.3"
16 } 17 }
...@@ -191,6 +192,18 @@ ...@@ -191,6 +192,18 @@
191 "node": ">= 0.8" 192 "node": ">= 0.8"
192 } 193 }
193 }, 194 },
195 + "node_modules/call-bind": {
196 + "version": "1.0.2",
197 + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
198 + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
199 + "dependencies": {
200 + "function-bind": "^1.1.1",
201 + "get-intrinsic": "^1.0.2"
202 + },
203 + "funding": {
204 + "url": "https://github.com/sponsors/ljharb"
205 + }
206 + },
194 "node_modules/cheerio": { 207 "node_modules/cheerio": {
195 "version": "1.0.0-rc.9", 208 "version": "1.0.0-rc.9",
196 "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.9.tgz", 209 "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.9.tgz",
...@@ -273,6 +286,18 @@ ...@@ -273,6 +286,18 @@
273 "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 286 "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
274 "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 287 "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
275 }, 288 },
289 + "node_modules/cron-parser": {
290 + "version": "3.5.0",
291 + "resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-3.5.0.tgz",
292 + "integrity": "sha512-wyVZtbRs6qDfFd8ap457w3XVntdvqcwBGxBoTvJQH9KGVKL/fB+h2k3C8AqiVxvUQKN1Ps/Ns46CNViOpVDhfQ==",
293 + "dependencies": {
294 + "is-nan": "^1.3.2",
295 + "luxon": "^1.26.0"
296 + },
297 + "engines": {
298 + "node": ">=0.8"
299 + }
300 + },
276 "node_modules/css-select": { 301 "node_modules/css-select": {
277 "version": "4.1.2", 302 "version": "4.1.2",
278 "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.1.2.tgz", 303 "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.1.2.tgz",
...@@ -315,6 +340,17 @@ ...@@ -315,6 +340,17 @@
315 "node": ">=0.10.0" 340 "node": ">=0.10.0"
316 } 341 }
317 }, 342 },
343 + "node_modules/define-properties": {
344 + "version": "1.1.3",
345 + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
346 + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
347 + "dependencies": {
348 + "object-keys": "^1.0.12"
349 + },
350 + "engines": {
351 + "node": ">= 0.4"
352 + }
353 + },
318 "node_modules/depd": { 354 "node_modules/depd": {
319 "version": "1.1.2", 355 "version": "1.1.2",
320 "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 356 "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
...@@ -581,6 +617,24 @@ ...@@ -581,6 +617,24 @@
581 "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 617 "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
582 "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" 618 "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
583 }, 619 },
620 + "node_modules/function-bind": {
621 + "version": "1.1.1",
622 + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
623 + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
624 + },
625 + "node_modules/get-intrinsic": {
626 + "version": "1.1.1",
627 + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
628 + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==",
629 + "dependencies": {
630 + "function-bind": "^1.1.1",
631 + "has": "^1.0.3",
632 + "has-symbols": "^1.0.1"
633 + },
634 + "funding": {
635 + "url": "https://github.com/sponsors/ljharb"
636 + }
637 + },
584 "node_modules/get-stream": { 638 "node_modules/get-stream": {
585 "version": "5.2.0", 639 "version": "5.2.0",
586 "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", 640 "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
...@@ -614,6 +668,28 @@ ...@@ -614,6 +668,28 @@
614 "url": "https://github.com/sponsors/isaacs" 668 "url": "https://github.com/sponsors/isaacs"
615 } 669 }
616 }, 670 },
671 + "node_modules/has": {
672 + "version": "1.0.3",
673 + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
674 + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
675 + "dependencies": {
676 + "function-bind": "^1.1.1"
677 + },
678 + "engines": {
679 + "node": ">= 0.4.0"
680 + }
681 + },
682 + "node_modules/has-symbols": {
683 + "version": "1.0.2",
684 + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz",
685 + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==",
686 + "engines": {
687 + "node": ">= 0.4"
688 + },
689 + "funding": {
690 + "url": "https://github.com/sponsors/ljharb"
691 + }
692 + },
617 "node_modules/htmlparser2": { 693 "node_modules/htmlparser2": {
618 "version": "6.1.0", 694 "version": "6.1.0",
619 "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", 695 "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz",
...@@ -732,6 +808,21 @@ ...@@ -732,6 +808,21 @@
732 "node": ">= 0.10" 808 "node": ">= 0.10"
733 } 809 }
734 }, 810 },
811 + "node_modules/is-nan": {
812 + "version": "1.3.2",
813 + "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz",
814 + "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==",
815 + "dependencies": {
816 + "call-bind": "^1.0.0",
817 + "define-properties": "^1.1.3"
818 + },
819 + "engines": {
820 + "node": ">= 0.4"
821 + },
822 + "funding": {
823 + "url": "https://github.com/sponsors/ljharb"
824 + }
825 + },
735 "node_modules/is-plain-object": { 826 "node_modules/is-plain-object": {
736 "version": "5.0.0", 827 "version": "5.0.0",
737 "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", 828 "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
...@@ -759,6 +850,19 @@ ...@@ -759,6 +850,19 @@
759 "node": ">=8" 850 "node": ">=8"
760 } 851 }
761 }, 852 },
853 + "node_modules/long-timeout": {
854 + "version": "0.1.1",
855 + "resolved": "https://registry.npmjs.org/long-timeout/-/long-timeout-0.1.1.tgz",
856 + "integrity": "sha1-lyHXiLR+C8taJMLivuGg2lXatRQ="
857 + },
858 + "node_modules/luxon": {
859 + "version": "1.27.0",
860 + "resolved": "https://registry.npmjs.org/luxon/-/luxon-1.27.0.tgz",
861 + "integrity": "sha512-VKsFsPggTA0DvnxtJdiExAucKdAnwbCCNlMM5ENvHlxubqWd0xhZcdb4XgZ7QFNhaRhilXCFxHuoObP5BNA4PA==",
862 + "engines": {
863 + "node": "*"
864 + }
865 + },
762 "node_modules/media-typer": { 866 "node_modules/media-typer": {
763 "version": "0.3.0", 867 "version": "0.3.0",
764 "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 868 "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
...@@ -858,6 +962,19 @@ ...@@ -858,6 +962,19 @@
858 "node": "4.x || >=6.0.0" 962 "node": "4.x || >=6.0.0"
859 } 963 }
860 }, 964 },
965 + "node_modules/node-schedule": {
966 + "version": "2.0.0",
967 + "resolved": "https://registry.npmjs.org/node-schedule/-/node-schedule-2.0.0.tgz",
968 + "integrity": "sha512-cHc9KEcfiuXxYDU+HjsBVo2FkWL1jRAUoczFoMIzRBpOA4p/NRHuuLs85AWOLgKsHtSPjN8csvwIxc2SqMv+CQ==",
969 + "dependencies": {
970 + "cron-parser": "^3.1.0",
971 + "long-timeout": "0.1.1",
972 + "sorted-array-functions": "^1.3.0"
973 + },
974 + "engines": {
975 + "node": ">=6"
976 + }
977 + },
861 "node_modules/nth-check": { 978 "node_modules/nth-check": {
862 "version": "2.0.0", 979 "version": "2.0.0",
863 "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.0.tgz", 980 "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.0.tgz",
...@@ -869,6 +986,14 @@ ...@@ -869,6 +986,14 @@
869 "url": "https://github.com/fb55/nth-check?sponsor=1" 986 "url": "https://github.com/fb55/nth-check?sponsor=1"
870 } 987 }
871 }, 988 },
989 + "node_modules/object-keys": {
990 + "version": "1.1.1",
991 + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
992 + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
993 + "engines": {
994 + "node": ">= 0.4"
995 + }
996 + },
872 "node_modules/on-finished": { 997 "node_modules/on-finished": {
873 "version": "2.3.0", 998 "version": "2.3.0",
874 "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 999 "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
...@@ -1207,6 +1332,11 @@ ...@@ -1207,6 +1332,11 @@
1207 "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", 1332 "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
1208 "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" 1333 "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
1209 }, 1334 },
1335 + "node_modules/sorted-array-functions": {
1336 + "version": "1.3.0",
1337 + "resolved": "https://registry.npmjs.org/sorted-array-functions/-/sorted-array-functions-1.3.0.tgz",
1338 + "integrity": "sha512-2sqgzeFlid6N4Z2fUQ1cvFmTOLRi/sEDzSQ0OKYchqgoPmQBVyM3959qYx3fpS6Esef80KjmpgPeEr028dP3OA=="
1339 + },
1210 "node_modules/source-map": { 1340 "node_modules/source-map": {
1211 "version": "0.6.1", 1341 "version": "0.6.1",
1212 "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 1342 "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
...@@ -1509,6 +1639,15 @@ ...@@ -1509,6 +1639,15 @@
1509 "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", 1639 "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
1510 "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" 1640 "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
1511 }, 1641 },
1642 + "call-bind": {
1643 + "version": "1.0.2",
1644 + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
1645 + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
1646 + "requires": {
1647 + "function-bind": "^1.1.1",
1648 + "get-intrinsic": "^1.0.2"
1649 + }
1650 + },
1512 "cheerio": { 1651 "cheerio": {
1513 "version": "1.0.0-rc.9", 1652 "version": "1.0.0-rc.9",
1514 "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.9.tgz", 1653 "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.9.tgz",
...@@ -1573,6 +1712,15 @@ ...@@ -1573,6 +1712,15 @@
1573 "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 1712 "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
1574 "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 1713 "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
1575 }, 1714 },
1715 + "cron-parser": {
1716 + "version": "3.5.0",
1717 + "resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-3.5.0.tgz",
1718 + "integrity": "sha512-wyVZtbRs6qDfFd8ap457w3XVntdvqcwBGxBoTvJQH9KGVKL/fB+h2k3C8AqiVxvUQKN1Ps/Ns46CNViOpVDhfQ==",
1719 + "requires": {
1720 + "is-nan": "^1.3.2",
1721 + "luxon": "^1.26.0"
1722 + }
1723 + },
1576 "css-select": { 1724 "css-select": {
1577 "version": "4.1.2", 1725 "version": "4.1.2",
1578 "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.1.2.tgz", 1726 "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.1.2.tgz",
...@@ -1603,6 +1751,14 @@ ...@@ -1603,6 +1751,14 @@
1603 "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", 1751 "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
1604 "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==" 1752 "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg=="
1605 }, 1753 },
1754 + "define-properties": {
1755 + "version": "1.1.3",
1756 + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
1757 + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
1758 + "requires": {
1759 + "object-keys": "^1.0.12"
1760 + }
1761 + },
1606 "depd": { 1762 "depd": {
1607 "version": "1.1.2", 1763 "version": "1.1.2",
1608 "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 1764 "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
...@@ -1803,6 +1959,21 @@ ...@@ -1803,6 +1959,21 @@
1803 "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 1959 "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
1804 "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" 1960 "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
1805 }, 1961 },
1962 + "function-bind": {
1963 + "version": "1.1.1",
1964 + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
1965 + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
1966 + },
1967 + "get-intrinsic": {
1968 + "version": "1.1.1",
1969 + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
1970 + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==",
1971 + "requires": {
1972 + "function-bind": "^1.1.1",
1973 + "has": "^1.0.3",
1974 + "has-symbols": "^1.0.1"
1975 + }
1976 + },
1806 "get-stream": { 1977 "get-stream": {
1807 "version": "5.2.0", 1978 "version": "5.2.0",
1808 "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", 1979 "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
...@@ -1824,6 +1995,19 @@ ...@@ -1824,6 +1995,19 @@
1824 "path-is-absolute": "^1.0.0" 1995 "path-is-absolute": "^1.0.0"
1825 } 1996 }
1826 }, 1997 },
1998 + "has": {
1999 + "version": "1.0.3",
2000 + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
2001 + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
2002 + "requires": {
2003 + "function-bind": "^1.1.1"
2004 + }
2005 + },
2006 + "has-symbols": {
2007 + "version": "1.0.2",
2008 + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz",
2009 + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw=="
2010 + },
1827 "htmlparser2": { 2011 "htmlparser2": {
1828 "version": "6.1.0", 2012 "version": "6.1.0",
1829 "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", 2013 "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz",
...@@ -1903,6 +2087,15 @@ ...@@ -1903,6 +2087,15 @@
1903 "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", 2087 "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
1904 "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" 2088 "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
1905 }, 2089 },
2090 + "is-nan": {
2091 + "version": "1.3.2",
2092 + "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz",
2093 + "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==",
2094 + "requires": {
2095 + "call-bind": "^1.0.0",
2096 + "define-properties": "^1.1.3"
2097 + }
2098 + },
1906 "is-plain-object": { 2099 "is-plain-object": {
1907 "version": "5.0.0", 2100 "version": "5.0.0",
1908 "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", 2101 "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
...@@ -1921,6 +2114,16 @@ ...@@ -1921,6 +2114,16 @@
1921 "p-locate": "^4.1.0" 2114 "p-locate": "^4.1.0"
1922 } 2115 }
1923 }, 2116 },
2117 + "long-timeout": {
2118 + "version": "0.1.1",
2119 + "resolved": "https://registry.npmjs.org/long-timeout/-/long-timeout-0.1.1.tgz",
2120 + "integrity": "sha1-lyHXiLR+C8taJMLivuGg2lXatRQ="
2121 + },
2122 + "luxon": {
2123 + "version": "1.27.0",
2124 + "resolved": "https://registry.npmjs.org/luxon/-/luxon-1.27.0.tgz",
2125 + "integrity": "sha512-VKsFsPggTA0DvnxtJdiExAucKdAnwbCCNlMM5ENvHlxubqWd0xhZcdb4XgZ7QFNhaRhilXCFxHuoObP5BNA4PA=="
2126 + },
1924 "media-typer": { 2127 "media-typer": {
1925 "version": "0.3.0", 2128 "version": "0.3.0",
1926 "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 2129 "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
...@@ -1987,6 +2190,16 @@ ...@@ -1987,6 +2190,16 @@
1987 "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", 2190 "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
1988 "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" 2191 "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
1989 }, 2192 },
2193 + "node-schedule": {
2194 + "version": "2.0.0",
2195 + "resolved": "https://registry.npmjs.org/node-schedule/-/node-schedule-2.0.0.tgz",
2196 + "integrity": "sha512-cHc9KEcfiuXxYDU+HjsBVo2FkWL1jRAUoczFoMIzRBpOA4p/NRHuuLs85AWOLgKsHtSPjN8csvwIxc2SqMv+CQ==",
2197 + "requires": {
2198 + "cron-parser": "^3.1.0",
2199 + "long-timeout": "0.1.1",
2200 + "sorted-array-functions": "^1.3.0"
2201 + }
2202 + },
1990 "nth-check": { 2203 "nth-check": {
1991 "version": "2.0.0", 2204 "version": "2.0.0",
1992 "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.0.tgz", 2205 "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.0.tgz",
...@@ -1995,6 +2208,11 @@ ...@@ -1995,6 +2208,11 @@
1995 "boolbase": "^1.0.0" 2208 "boolbase": "^1.0.0"
1996 } 2209 }
1997 }, 2210 },
2211 + "object-keys": {
2212 + "version": "1.1.1",
2213 + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
2214 + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="
2215 + },
1998 "on-finished": { 2216 "on-finished": {
1999 "version": "2.3.0", 2217 "version": "2.3.0",
2000 "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 2218 "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
...@@ -2261,6 +2479,11 @@ ...@@ -2261,6 +2479,11 @@
2261 "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", 2479 "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
2262 "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" 2480 "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
2263 }, 2481 },
2482 + "sorted-array-functions": {
2483 + "version": "1.3.0",
2484 + "resolved": "https://registry.npmjs.org/sorted-array-functions/-/sorted-array-functions-1.3.0.tgz",
2485 + "integrity": "sha512-2sqgzeFlid6N4Z2fUQ1cvFmTOLRi/sEDzSQ0OKYchqgoPmQBVyM3959qYx3fpS6Esef80KjmpgPeEr028dP3OA=="
2486 + },
2264 "source-map": { 2487 "source-map": {
2265 "version": "0.6.1", 2488 "version": "0.6.1",
2266 "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 2489 "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
16 "dependencies": { 16 "dependencies": {
17 "cheerio": "^1.0.0-rc.9", 17 "cheerio": "^1.0.0-rc.9",
18 "express": "^4.17.1", 18 "express": "^4.17.1",
19 + "node-schedule": "^2.0.0",
19 "puppeteer": "^9.1.1", 20 "puppeteer": "^9.1.1",
20 "sanitize-html": "^2.3.3" 21 "sanitize-html": "^2.3.3"
21 } 22 }
......