Skip to content

Commit 3d13618

Browse files
committed
readability: cover more timestamp formats
1 parent 69744fe commit 3d13618

2 files changed

Lines changed: 60 additions & 6 deletions

File tree

src/llar/fetch/readability.clj

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@
1212
[digest]
1313
[llar.item]
1414
[java-time.api :as time]
15-
[clojure.spec.alpha :as s]))
15+
[clojure.spec.alpha :as s])
16+
(:import
17+
[java.time LocalDate LocalDateTime ZoneOffset ZonedDateTime]
18+
[java.time.format DateTimeFormatter DateTimeFormatterBuilder]))
1619

1720
(defrecord ReadabilityItem
1821
[meta
@@ -79,6 +82,36 @@
7982
(assoc :body (when processed (hick-r/hickory-to-html processed)))
8083
(assoc :hickory processed))))))
8184

85+
(def ^:private published-time-formatter
86+
(-> (DateTimeFormatterBuilder.)
87+
(.append DateTimeFormatter/ISO_LOCAL_DATE_TIME)
88+
;; Readability emits both extended (-05:00 / XXX) and compact
89+
;; (-0500 / XX) numeric offsets.
90+
(.appendPattern "[XXX][XX]['['VV']']")
91+
(.toFormatter)))
92+
93+
(def ^:private local-published-time-formatter
94+
(-> (DateTimeFormatterBuilder.)
95+
(.append DateTimeFormatter/ISO_LOCAL_DATE)
96+
;; Upstream fixtures contain both ISO's T and a space separator.
97+
(.appendPattern "['T'][' ']")
98+
(.append DateTimeFormatter/ISO_LOCAL_TIME)
99+
(.toFormatter)))
100+
101+
(defn- parse-published-time [timestamp]
102+
(try
103+
(ZonedDateTime/parse timestamp published-time-formatter)
104+
(catch java.time.format.DateTimeParseException zoned-error
105+
(try
106+
(.atZone (LocalDateTime/parse timestamp local-published-time-formatter)
107+
ZoneOffset/UTC)
108+
(catch java.time.format.DateTimeParseException _
109+
(try
110+
(.atStartOfDay (LocalDate/parse timestamp DateTimeFormatter/ISO_LOCAL_DATE)
111+
ZoneOffset/UTC)
112+
(catch java.time.format.DateTimeParseException _
113+
(throw zoned-error))))))))
114+
82115
(extend-protocol FetchSource
83116
llar.src.Readability
84117
(fetch-source [src _conditional-tokens]
@@ -91,10 +124,8 @@
91124
(when-not (string/blank? (get-in fetch [:summary :title])) (get-in fetch [:summary :title]))
92125
(when-not (string/blank? (:title metadata)) (:title metadata))
93126
"")
94-
pub-ts (or (when-not (string/blank? (:publishedTime data)) (time/zoned-date-time (time/formatter :iso-zoned-date-time)
95-
(:publishedTime data)))
96-
(when-not (string/blank? (:date metadata)) (time/zoned-date-time (time/formatter :iso-zoned-date-time)
97-
(:date metadata)))
127+
pub-ts (or (when-not (string/blank? (:publishedTime data)) (parse-published-time (:publishedTime data)))
128+
(when-not (string/blank? (:date metadata)) (parse-published-time (:date metadata)))
98129
(get-in fetch [:summary :ts]))]
99130
[(make-readability-item
100131
(fetch/make-meta src)

test/llar/fetch_test.clj

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
[llar.fetch.feed]
1111
[llar.fetch.http]
1212
[llar.fetch.imap]
13-
[llar.fetch.readability]
13+
[llar.fetch.readability :as readability]
1414
[llar.converter :as converter]
1515
[hickory.select :as S]
1616
[llar.fetch.reddit :as reddit]
@@ -34,6 +34,29 @@
3434

3535
(s/check-asserts true)
3636

37+
(deftest readability-published-time-test
38+
(let [parse-published-time @#'readability/parse-published-time]
39+
(testing "compact numeric UTC offset"
40+
(is (= "2016-01-07T00:00-05:00"
41+
(str (parse-published-time "2016-01-07T00:00:00-0500")))))
42+
(testing "ISO offset forms remain unchanged"
43+
(is (= "2016-01-07T00:00Z"
44+
(str (parse-published-time "2016-01-07T00:00:00Z"))))
45+
(is (= "2016-01-07T00:00:00.123-05:00"
46+
(str (parse-published-time "2016-01-07T00:00:00.123-05:00"))))
47+
(is (= "2016-01-07T00:00-05:00[America/New_York]"
48+
(str (parse-published-time
49+
"2016-01-07T00:00:00-0500[America/New_York]")))))
50+
(testing "zone-less forms emitted by Readability"
51+
(is (= "2018-04-05T06:00Z"
52+
(str (parse-published-time "2018-04-05T06:00"))))
53+
(is (= "2019-04-28T06:01:07Z"
54+
(str (parse-published-time "2019-04-28 06:01:07"))))
55+
(is (= "2017-11-03T03:01Z"
56+
(str (parse-published-time "2017-11-03 03:01:00.000000"))))
57+
(is (= "2020-09-21T00:00Z"
58+
(str (parse-published-time "2020-09-21")))))))
59+
3760
(defn fake-fetch-rss [url & _args]
3861
(when-not (= (str url) "http://example.com/feed.xml")
3962
(throw+ {:type ::test-error

0 commit comments

Comments
 (0)