diff --git a/lib/middleware/template.js b/lib/middleware/template.js
index 8598fe5a1253a1..88dfc57961e2bd 100644
--- a/lib/middleware/template.js
+++ b/lib/middleware/template.js
@@ -2,7 +2,7 @@ const art = require('art-template');
const path = require('path');
const config = require('@/config').value;
const typeRegex = /\.(atom|rss|debug\.json)$/;
-const { collapseWhitespace } = require('@/utils/common-utils');
+const { collapseWhitespace, convertDateToISO8601 } = require('@/utils/common-utils');
module.exports = async (ctx, next) => {
if (ctx.headers['user-agent'] && ctx.headers['user-agent'].includes('Reeder')) {
@@ -28,7 +28,8 @@ module.exports = async (ctx, next) => {
if (!ctx.body) {
let template;
- switch (ctx.state.type[1]) {
+ const outputType = ctx.state.type[1];
+ switch (outputType) {
case 'atom':
template = path.resolve(__dirname, '../views/atom.art');
break;
@@ -66,6 +67,11 @@ module.exports = async (ctx, next) => {
item.itunes_duration =
Math.floor(item.itunes_duration / 3600) + ':' + (Math.floor((item.itunes_duration % 3600) / 60) / 100).toFixed(2).slice(-2) + ':' + (((item.itunes_duration % 3600) % 60) / 100).toFixed(2).slice(-2);
}
+
+ if (outputType === 'atom') {
+ item.pubDate = convertDateToISO8601(item.pubDate);
+ item.updated = convertDateToISO8601(item.updated);
+ }
});
}
diff --git a/lib/utils/common-utils.js b/lib/utils/common-utils.js
index 882cccd7c08d2e..4e29e5086046ab 100644
--- a/lib/utils/common-utils.js
+++ b/lib/utils/common-utils.js
@@ -1,3 +1,5 @@
+const { parseDate } = require('@/utils/parse-date');
+
// convert a string into title case
const toTitleCase = (str) =>
str
@@ -17,7 +19,19 @@ const collapseWhitespace = (str) => {
return str;
};
+const convertDateToISO8601 = (date) => {
+ if (!date) {
+ return date;
+ }
+ if (typeof date !== 'object') {
+ // some routes may call `.toUTCString()` before passing the date to ctx...
+ date = parseDate(date);
+ }
+ return date.toISOString();
+};
+
module.exports = {
toTitleCase,
collapseWhitespace,
+ convertDateToISO8601,
};
diff --git a/lib/views/atom.art b/lib/views/atom.art
index ee0addb78e2c44..b0a754795d0081 100644
--- a/lib/views/atom.art
+++ b/lib/views/atom.art
@@ -37,8 +37,10 @@
{{ $e.guid || $e.id || $e.link }}
- {{ $e.pubDate || $e.updated }}
- {{ $e.updated }}
+ {{ if ($e.pubDate && $e.updated) }}
+ {{ $e.pubDate }}
+ {{ /if }}
+ {{ $e.updated || $e.pubDate }}
{{ if $e.author }}
diff --git a/test/middleware/template.js b/test/middleware/template.js
index 4b31e96fff06ba..89b8c90c061a6d 100644
--- a/test/middleware/template.js
+++ b/test/middleware/template.js
@@ -10,6 +10,8 @@ afterAll(() => {
});
describe('template', () => {
+ const expectPubDate = new Date(1546272000000 - 10 * 1000);
+
it(`.rss`, async () => {
const response1 = await request.get('/test/1.rss');
const parsed1 = await parser.parseString(response1.text);
@@ -25,7 +27,7 @@ describe('template', () => {
expect(parsed1.items[0]).toEqual(expect.any(Object));
expect(parsed1.items[0].title).toEqual(expect.any(String));
expect(parsed1.items[0].link).toEqual(expect.any(String));
- expect(parsed1.items[0].pubDate).toEqual(expect.any(String));
+ expect(parsed1.items[0].pubDate).toBe(expectPubDate.toUTCString());
expect(parsed1.items[0].author).toEqual(expect.any(String));
expect(parsed1.items[0].content).toEqual(expect.any(String));
expect(parsed1.items[0].guid).toEqual(expect.any(String));
@@ -54,7 +56,7 @@ describe('template', () => {
expect(parsed.items[0]).toEqual(expect.any(Object));
expect(parsed.items[0].title).toEqual(expect.any(String));
expect(parsed.items[0].link).toEqual(expect.any(String));
- expect(parsed.items[0].pubDate).toEqual(expect.any(String));
+ expect(parsed.items[0].pubDate).toBe(expectPubDate.toISOString());
expect(parsed.items[0].author).toEqual(expect.any(String));
expect(parsed.items[0].content).toEqual(expect.any(String));
expect(parsed.items[0].id).toEqual(expect.any(String));
diff --git a/test/utils/common-utils.js b/test/utils/common-utils.js
index d0306193c55fb1..e895a485653325 100644
--- a/test/utils/common-utils.js
+++ b/test/utils/common-utils.js
@@ -4,4 +4,33 @@ describe('common-utils', () => {
it('toTitleCase', () => {
expect(utils.toTitleCase('RSSHub IS AS aweSOme aS henry')).toBe('Rsshub Is As Awesome As Henry');
});
+
+ it('convertDateToISO8601', () => {
+ expect(utils.convertDateToISO8601('')).toBe('');
+ expect(utils.convertDateToISO8601(null)).toBe(null);
+ expect(utils.convertDateToISO8601(undefined)).toBe(undefined);
+
+ const date = new Date('2019-01-01');
+ const expected = date.toISOString();
+ expect(utils.convertDateToISO8601(date)).toBe(expected);
+ expect(utils.convertDateToISO8601(date.toISOString())).toBe(expected);
+ expect(utils.convertDateToISO8601(date.toUTCString())).toBe(expected);
+ expect(utils.convertDateToISO8601(date.toLocaleString())).toBe(expected);
+ expect(utils.convertDateToISO8601('Tue, 01 Jan 2019 08:00:00 UTC+8')).toBe(expected);
+
+ expect(utils.convertDateToISO8601('Tue, 01 Jan 2019 00:00:00')).toBe(new Date(date.getTime() + new Date().getTimezoneOffset() * 60 * 1000).toISOString());
+ // need to pass a function in order to use `toThrow`
+ expect(() => {
+ utils.convertDateToISO8601('something invalid');
+ }).toThrow(RangeError);
+ });
+
+ it('collapseWhitespace', () => {
+ expect(utils.collapseWhitespace('')).toBe('');
+ expect(utils.collapseWhitespace(null)).toBe(null);
+ expect(utils.collapseWhitespace(undefined)).toBe(undefined);
+ expect(utils.collapseWhitespace(' \n\n\n ')).toBe('');
+ expect(utils.collapseWhitespace('a string already collapsed')).toBe('a string already collapsed');
+ expect(utils.collapseWhitespace(' \n a lot of whitespaces and \n\n\n\n linebreaks \n\n ')).toBe('a lot of whitespaces and linebreaks');
+ });
});