Skip to content

Commit 5dc10a7

Browse files
committed
WIP: add email date pattern
1 parent e7565b9 commit 5dc10a7

File tree

2 files changed

+85
-0
lines changed

2 files changed

+85
-0
lines changed

lpeg_patterns/email.lua

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,86 @@ local encoding = mime_charset / string.lower
137137
local encoded_text = (printable_character - S"?")^1
138138
local encoded_word = P"=?" * charset * P"?" * encoding * P"?" * C(encoded_text) * P"?="
139139

140+
local date_time do -- RFC 5322 Section 3.3
141+
local _2DIGIT = DIGIT * DIGIT
142+
local _4DIGIT = _2DIGIT * _2DIGIT
143+
144+
local function tonumber_10(s)
145+
return tonumber(s, 10)
146+
end
147+
148+
-- captured value follow's Lua's os.date yday convention
149+
local day_name = (P"Mon" + P"Tue" + P"Wed" + P"Thu" + P"Fri" + P"Sat" + P"Sun") / {
150+
Mon = 2;
151+
Tue = 3;
152+
Wed = 4;
153+
Thu = 5;
154+
Fri = 6;
155+
Sat = 7;
156+
Sun = 1;
157+
}
158+
local obs_day_of_week = CFWS^-1 * day_name * CFWS^-1
159+
local day_of_week = obs_day_of_week
160+
local obs_day = CFWS^-1 * (_2DIGIT^1 / tonumber_10) * CFWS^-1
161+
local day = obs_day / 1
162+
local month = (P"Jan"+ P"Feb"+ P"Mar"+ P"Apr"+ P"May"+ P"Jun"+ P"Jul"+ P"Aug"+ P"Sep"+ P"Oct"+ P"Nov"+ P"Dec") / {
163+
Jan = 1;
164+
Feb = 2;
165+
Mar = 3;
166+
Apr = 4;
167+
May = 5;
168+
Jun = 6;
169+
Jul = 7;
170+
Aug = 8;
171+
Sep = 9;
172+
Oct = 10;
173+
Nov = 11;
174+
Dec = 12;
175+
}
176+
local obs_year = CFWS^-1 * DIGIT^2 * CFWS^-1 / function(y)
177+
local r = tonumber_10(y)
178+
179+
-- If a two digit year is encountered whose value is between 00 and 49,
180+
-- the year is interpreted by adding 2000, ending up with a value between 2000 and 2049.
181+
if #y == 2 and r < 50 then
182+
return r + 2000
183+
end
184+
185+
-- If a two digit year is encountered with a value between 50 and 99,
186+
-- or any three digit year is encountered, the year is interpreted by adding 1900.
187+
if #y <= 3 then
188+
return r + 1900
189+
end
190+
191+
return r
192+
end
193+
local year = obs_year / 1
194+
local date = Cg(day, "day") * Cg(month, "month") * Cg(year, "year")
195+
196+
local obs_hour = CFWS^-1 * (_2DIGIT / tonumber_10) * CFWS^-1
197+
local hour = obs_hour / 1
198+
local obs_minute = CFWS^-1 * (_2DIGIT / tonumber_10) * CFWS^-1
199+
local minute = obs_minute / 1
200+
local obs_second = CFWS^-1 * (_2DIGIT / tonumber_10) * CFWS^-1
201+
local second = obs_second / 1
202+
local time_of_day = Cg(hour, "hour") * P":" * Cg(minute, "min") * (P":" * Cg(second, "sec"))^-1
203+
local obs_zone = (P"UT" + P"GMT") / "+0000"
204+
+ P"EST" / "-0500"
205+
+ P"EDT" / "-0400"
206+
+ P"CST" / "-0600"
207+
+ P"CDT" / "-0500"
208+
+ P"MST" / "-0700"
209+
+ P"MDT" / "-0600"
210+
+ P"PST" / "-0800"
211+
+ P"PDT" / "-0700"
212+
+ R("\65\73", "\75\90", "\97\105", "\107\122") / "-0000"
213+
-- the FWS isn't optional in RFC5322
214+
local zone = FWS^-1 / 0 * C(S"+-" * _4DIGIT) + obs_zone
215+
local time = time_of_day * Cg(zone, "zone")
216+
217+
date_time = (Cg(day_of_week, "wday") * P",")^-1 * date * time * CFWS^-1
218+
end
219+
140220
return {
141221
obs_NO_WS_CTL = obs_NO_WS_CTL;
142222
obs_qp = obs_qp;
@@ -196,6 +276,7 @@ return {
196276
encoding = encoding;
197277
encoded_text = encoded_text;
198278
encoded_word = encoded_word;
279+
date_time = date_time;
199280

200281
-- Handy alias
201282
email = addr_spec;

spec/email_spec.lua

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,3 +157,7 @@ describe("mailbox", function()
157157
mailbox:match "<@wow,@such,,@domains:foo@example.com>")
158158
end)
159159
end)
160+
describe("date", function()
161+
local date_time = lpeg.Ct(require "lpeg_patterns.email".date_time) * EOF
162+
assert.same({year=2022, month=5, day=22, hour=2, min=15, sec=14, zone="+0100", wday=1}, date_time:match("Sun, 22 May 2022 02:15:14 +0100"))
163+
end)

0 commit comments

Comments
 (0)