-
Notifications
You must be signed in to change notification settings - Fork 9
/
dates.dyalog
195 lines (171 loc) · 7.83 KB
/
dates.dyalog
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
:Namespace Dates
(⎕ML ⎕IO)←1
∇ r←CookieFmt date;day;date;mon
⍝ Format date for cookie expiry
⍝ "expires Mon, 09-Dec-2002 13:46:00 GMT"
day←(7 3⍴'MonTueWedThuFriSatSun')[WeekDay date;]
mon←(12 3⍴'janfebmaraprmayjunjulaugsepoctnovdec')[2⊃date;]
r←,('<',day,', >,ZI2,<-',mon,'->,ZI4,< >,ZI2,<:>,ZI2,<:>,ZI2,< GMT>')⎕FMT 1 5⍴(6↑date)[3 1 4 5 6]
∇
∇ SetUtcOffset;ver;⎕USING
:If 0=⎕NC'_UtcOffset'
:If 'Win'≡ver←3↑1⊃'.'⎕WG'APLVersion'
:Hold '_UtcOffset'
⎕USING←,⊂''
_UtcOffset←¯60×(System.TimeZone.CurrentTimeZone.GetUtcOffset ⎕NEW System.DateTime(⎕TS)).Hours
:EndHold
:Else
:If (⊂ver)∊'Lin' 'AIX' 'Sol' 'Mac'
_UtcOffset←¯60×.01×⍎⊃⎕SH'date +%z'
:Else
_UtcOffset←0 ⍝ otherwise, assume GMT
:EndIf
:EndIf
:EndIf
∇
∇ r←{minOffset}HTTPDate ts;sign;day;mon;ver;⎕USING;t
⍝ return RCF 1123/822 compliant date
⍝ minOffset is option number of minutes to offset time with (used for HTTP caching expirations)
minOffset←{0::0 ⋄ minOffset}''
ts←6↑ts
SetUtcOffset
ts←IDNToDate((_UtcOffset+minOffset)÷24×60)+t←DateToIDN ts
day←(7 3⍴'MonTueWedThuFriSatSun')[1+7|(⌊t)-1;]
mon←(12 3⍴'JanFebMarAprMayJunJulAugSepOctNovDec')[2⊃ts;]
r←,('<',day,', >,ZI2,< ',mon,' >,ZI4,< >,ZI2,<:>,ZI2,<:>,ZI2,< GMT>')⎕FMT 1 5⍴ts[3 1 4 5 6]
∇
∇ SM←DateToIDN TS ⍝ Convert date format
SM←2 ⎕NQ'.' 'DateToIDN'(3↑TS)
:If 3<⍴TS
SM+←(24 60 60 1000⊥4↑3↓TS)÷86400000
:EndIf
∇
∇ TS←IDNToDate SM ⍝ Convert IDN to date format : 3↑⎕TS ← IDN (akd TS_SM)
TS←3↑2 ⎕NQ'.' 'IDNToDate'(⌊SM)
TS,←⌊0.5+24 60 60 1000⊤86400000×1|SM
∇
∇ new←ts IdnAdd t
⍝ T is D HH MM SS
new←ts+(0 24 60 60 1000⊥¯5↑t,0)÷86400000
∇
∇ r←Now
r←DateToIDN ⎕TS
∇
∇ r←TSFmt ts
r←,'ZI4,<->,ZI2,<->,ZI2,< >,ZI2,<:>,ZI2,<:>,ZI2'⎕FMT 1 6⍴6↑ts
∇
∇ r←TSFmtNice ts;now;yday;today;z;i;m;idn;s
⍝ Format a vector of IDN's nicely
s←⍴ts
yday←¯1+today←⌊now←DateToIDN ⎕TS
r←↑IDNToDate¨,ts←,ts ⍝ Make matrix
r←'ZI4,<->,ZI2,<->,ZI2,< >,ZI2,<:>,ZI2'⎕FMT 5↑[2]r
:If 0≠⍴i←((ts≥yday)∧~m←ts≥today)/⍳⍴ts
r[i;]←r[i;11+⍳5],' ',((⍴i),10)⍴10↑'Yesterday'
:EndIf
:If 0≠⍴i←m/⍳⍴ts
r[i;]←r[i;11+⍳5],' ',((⍴i),10)⍴10↑'Today'
:EndIf
:If 0≠⍴i←(100>z←⌊(now-ts)×24×60)/⍳⍴ts
r[i;]←16↑[2]↑(⍕¨⌊z[i]),¨(' minutes ago' ' minute ago')[1+1=z[i]]
:EndIf
:If 0≠⍴i←(z<1)/⍳⍴ts
r[i;]←((⍴i),16)⍴16↑'Now'
:EndIf
r←(s,¯1↑⍴r)⍴r
∇
∇ r←WeekDay Date
⍝ Return weekday (Monday=1, Sunday=7)
r←1+7|(DateToIDN 3↑Date)-1
∇
∇ r←{opt}DateFormat ymd
⍝ opt - 0 dd MMM yyyy
⍝ 1 dd Month yyyy
:If 0=⎕NC'opt' ⋄ opt←0 ⋄ :EndIf
:Select ⊃opt
:Case 0
r←(⍕ymd[3]),(1⌽' ',3↑(3ׯ1+ymd[2])↓'JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC'),⍕ymd[1]
:Case 1
r←(⍕ymd[3]),(1⌽' ',ymd[2]⊃'January' 'February' 'March' 'April' 'May' 'June' 'July' 'August' 'September' 'October' 'November' 'December'),⍕ymd[1]
:Else
r←⍕ymd
:EndSelect
∇
∇ r←LogFmtNow;ver
⍝ returns now UTC adjusted, and formatted for log files (Common Log Format)
:If 'Win'≡ver←3↑1⊃'.'⎕WG'APLVersion'
r←((⍎⎕NA'kernel32|GetSystemTime >{U2 U2 U2 U2 U2 U2 U2 U2}')⊂8⍴0)[4 2 1 5 6 7]
r[2]←⊂(12 3⍴'JanFebMarAprMayJunJulAugSepOctNovDec')[,2⊃r;]
r←,'< [>,ZI2,</>,3A1,</>,ZI4,<:>,ZI2,<:>,ZI2,<:>,ZI2,< +0000] >'⎕FMT r
:Else
r←1⊃⎕SH'date +" [%d/%b/%Y:%T %z] "'
:EndIf
∇
∇ r←LogFmt ymdhms
r←ymdhms[3 2 1 4 5 6]
r[2]←⊂(12 3⍴'JanFebMarAprMayJunJulAugSepOctNovDec')[,2⊃r;]
r←,'< [>,ZI2,</>,3A1,</>,ZI4,<:>,ZI2,<:>,ZI2,<:>,ZI2,< +0000] >'⎕FMT r
∇
∇ ts←FTT fts;md;shape;tmp;yr;z;⎕IO
⍝ *** convert ⎕FRDCI/⎕FHIST timestamp(s) to ⎕ts-format for Dyalog/W ***
⍝ right argument: ⎕FRDCI-type timestamp(s) of any shape
⍝ result: ⎕ts-type timestamp(s) with shape <(⍴argument),7>
⎕IO←0 ⋄ shape←⍴fts ⋄ ts←,fts+18626112000
md←365.2501|1+1461|yr←⌊ts÷5184000
tmp←31 61 92 122 153 184 214 245 275 306 337 366
z←(,⍉<⍀tmp∘.≥md)/,((⍴md),12)⍴⍳12
md←(1+12|z+2),[0.1]⌈md-(0,tmp)[z]
ts←(1960+⌊(yr+60)÷365.25),md,⍉24 60 60 60⊤ts
ts[;6]←⌊0.5+ts[;6]×100÷6
ts←(shape,7)⍴ts
∇
∇ r←TTF ts;l;h;d;m;y
⍝ convert a ⎕TS style timestamp into 60ths of a second since 1st January 1970 a la ⎕FRDCI
l←⌈60×ts[7]÷1000 ⍝ convert milliseconds to 60ths
h←3600 60 60⊥ts[4 5 6] ⍝ hours minutes seconds to to the nearest seconds total
d←ts[3]-1 ⍝ days since start of month
m←(2⊃ts)⊃++\0 31,(28+0=4|⊃ts),31 30 31 30 31 31 30 31 30 ⍝ days in completed months
y←{{(365×⍵)+⌈4÷⍨⍵-2}⍵-1970}⊃ts ⍝ days in years since 1970, leap years since 1972
r←l+60×h+86400×y+m+d ⍝ sum, convert to seconds, add seconds in the day convert to 60ths, add on 60ths
∇
tonum←{(b v)←⎕vfi ⍵ ⋄ b/v}
∇ dt←ParseDate str;pos;mon;t;ymd
⍝ str is of the genre "Wed Aug 05 2015 07:30:21 GMT-0400 (Eastern Daylight Time)"
⍝ We need to weed out the day of the week and the time
str←(+/∧\' '=str)↓str ⍝ remove the leading spaces
⍝ What kind of string is this?
:If ~∧/1⊃(dt dt)←{b←~⍵∊'/-:' ⋄ ⎕VFI b\b/⍵}str ⍝ yyyy/mm/dd hh:mm:ss ?
:If 0∊⍴t←'Jan' 'Feb' 'Mar' 'Apr' 'May' 'Jun' 'Jul' 'Aug' 'Sep' 'Oct' 'Nov' 'Dec'⎕S 0 3⊢str ⍝ look for the month as a string. If not found
ymd←3↑tonum str ⍝ grab the 1st 3 numbers found
ymd←ymd[⍒(2×31<ymd)+ymd<12] ⍝ put in correct order
:Else ⍝ otherwise (if found)
(pos mon)←0 1+1⊃t
:If ~0∊⍴t←tonum pos↑str ⍝ any number before the month? (e.g. 2 May 2021)
ymd←⌽⍣(31<⍬⍴t)⊢(1↑tonum pos↓str),mon,t
:Else
ymd←¯1⌽mon,2↑tonum pos↓str
:EndIf
:EndIf
⍝ Now grab the time
dt←ymd,tonum⍕'(\d+):(\d+):(\d+)'⎕S'\1 \2 \3'⊢str
:EndIf
∇
∇ ts←ParseISODate str
SetUtcOffset
ts←IDNToDate(-_UtcOffset÷24×60)+DateToIDN 7↑str{⊃(//)⎕VFI ⍵\⍵/⍺}str∊⎕D
∇
∇ r←{unit}ParseTime string;chunks;units;factors
⍝ Parse time string into units (default is ms)
⍝ String is a string like '5d4h47m6s266ms'
:If 0=⎕NC'unit' ⋄ unit←'ms' ⋄ :EndIf
units←(1 1 1 1 1 0⊂'dhmsms'),⊂''
factors←86400000 3600000 60000 1000 1 1
chunks←{⍵⊆⍨1+⍵∊⎕D},string
:Trap 0/0
r←(⍎¨{⍵∩⎕D}¨chunks)+.×factors[units⍳{#.Strings.lc ⍵~⎕D}¨chunks]
:Else
'Invalid time string'⎕SIGNAL 11
:EndTrap
r←r÷factors[units⍳⊂,unit]
∇
:EndNamespace