Skip to content

Commit b219ad7

Browse files
committed
📅 improved date handling
1 parent f3a9913 commit b219ad7

File tree

6 files changed

+336
-74
lines changed

6 files changed

+336
-74
lines changed

.DS_Store

0 Bytes
Binary file not shown.

functions/helper.js

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
const { parseISO, isValid } = require('date-fns');
2+
3+
function parseDateString(dateString) {
4+
if (!dateString || typeof dateString !== 'string') {
5+
console.error("parseDateString received invalid input:", dateString);
6+
return null;
7+
}
8+
9+
let parsedDate = null;
10+
11+
try {
12+
parsedDate = parseISO(dateString);
13+
if (isValid(parsedDate)) {
14+
return parsedDate;
15+
}
16+
} catch (e) { /* ignore */ }
17+
try {
18+
parsedDate = new Date(dateString);
19+
if (isValid(parsedDate)) {
20+
return parsedDate;
21+
}
22+
} catch(e) { /* ignore */ }
23+
24+
return null;
25+
}
26+
27+
28+
function sortFeedItems(itemsData) {
29+
if (!Array.isArray(itemsData)) {
30+
console.error("sortFeedItems received non-array input:", itemsData);
31+
return;
32+
}
33+
34+
itemsData.sort((a, b) => {
35+
const dateA = (a && a.dateObject instanceof Date && isValid(a.dateObject)) ? a.dateObject : null;
36+
const dateB = (b && b.dateObject instanceof Date && isValid(b.dateObject)) ? b.dateObject : null;
37+
38+
// Logic to sort items with valid dates first, then items without dates.
39+
if (dateA && dateB) {
40+
// Both items have valid dates, sort descending (newest first)
41+
return dateB.getTime() - dateA.getTime();
42+
} else if (dateA) {
43+
// Only item A has a date, so A should come before B (which has no date)
44+
return -1;
45+
} else if (dateB) {
46+
// Only item B has a date, so B should come before A (which has no date)
47+
return 1;
48+
} else {
49+
// Neither item has a valid date, maintain their relative order
50+
return 0;
51+
}
52+
});
53+
}
54+
55+
56+
module.exports = {
57+
parseDateString,
58+
sortFeedItems
59+
};

functions/index.js

Lines changed: 71 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ const { initializeApp } = require("firebase-admin/app");
44
const { google } = require('googleapis');
55
const sheets = google.sheets('v4');
66

7+
const { format, isValid } = require('date-fns');
8+
9+
const feedUtils = require('./helper.js');
10+
711
setGlobalOptions({ region: "us-central1" });
812
const api_key_2nd_gen = process.env.SHEETS_API_KEY;
913
initializeApp();
@@ -95,68 +99,95 @@ function generateFeedContent(values, mode) {
9599
return { xmlItems, feedDescription };
96100
}
97101

102+
98103
function generateFeedAutoMode(values) {
99-
let xmlItemsAll = "";
100104
if (!values || values.length === 0) {
105+
console.error("No values provided to generateFeedAutoMode.");
101106
return "";
102107
}
103108

109+
const parsedItems = [];
104110
for (const row of values) {
105111
if (row && row.length > 0) {
106-
let currentRowData = [...row];
107-
let title = String(currentRowData.shift() || '');
108112

109-
if (!title) continue;
113+
let title = '';
114+
let titleIndex = -1;
115+
for (let i = 0; i < row.length; i++) {
116+
const cellValue = String(row[i] || '').trim();
117+
if (cellValue !== '') {
118+
title = cellValue; // Found the title
119+
titleIndex = i;
120+
break; // Stop searching
121+
}
122+
}
110123

111-
let link = undefined;
112-
let dateString = undefined;
124+
if (titleIndex === -1) {
125+
console.warn('Skipping row because no non-empty cell found:', row);
126+
continue;
127+
}
113128

114-
const linkIndex = currentRowData.findIndex(cell => typeof cell === 'string' && cell.startsWith('http'));
115-
if (linkIndex !== -1) {
116-
link = currentRowData.splice(linkIndex, 1)[0];
117-
}
129+
const remainingRowData = row.filter((_, index) => index !== titleIndex);
118130

119-
const dateIndex = currentRowData.findIndex(cell => {
120-
if (typeof cell !== 'string') return false;
121-
try {
122-
const potentialDate = new Date(cell);
123-
return !isNaN(potentialDate.getTime()) && potentialDate.toUTCString().slice(0, 25) === cell.slice(0, 25);
124-
} catch (e) {
125-
return false;
126-
}
127-
});
128-
if (dateIndex !== -1) {
129-
dateString = currentRowData.splice(dateIndex, 1)[0];
130-
}
131+
let link = undefined;
132+
let dateObject = null;
133+
let descriptionContent = '';
131134

132-
const descriptionContent = currentRowData.map(cell => String(cell || '')).join(' ');
135+
const remainingCells = [];
136+
for (const cell of remainingRowData) {
137+
const cellString = String(cell || '');
133138

139+
if (!link && cellString.startsWith('http')) {
140+
link = cellString;
141+
continue;
142+
}
134143

135-
let itemDate = new Date();
136-
if (dateString) {
137-
try {
138-
const parsedDate = new Date(dateString);
139-
if (!isNaN(parsedDate.getTime())) {
140-
itemDate = parsedDate;
141-
}
142-
} catch (e) { /* Keep default date if parsing fails */ }
144+
if (!dateObject) {
145+
const parsed = feedUtils.parseDateString(cellString);
146+
if (parsed instanceof Date && isValid(parsed)) {
147+
dateObject = parsed;
148+
continue;
149+
}
150+
}
151+
remainingCells.push(cellString);
143152
}
144-
const pubDateString = itemDate.toUTCString();
153+
descriptionContent = remainingCells.join(' ');
145154

146-
const xmlItem = `<item>
147-
<title><![CDATA[${title}]]></title>
148-
<description><![CDATA[${descriptionContent}]]></description>
149-
${link !== undefined ? `<link><![CDATA[${link}]]></link>` : ''}
150-
${link !== undefined ? `<guid><![CDATA[${link}]]></guid>` : ''}
155+
parsedItems.push({
156+
title,
157+
link,
158+
dateObject, // Can be null if no valid date was found
159+
descriptionContent
160+
});
161+
}
162+
}
163+
164+
feedUtils.sortFeedItems(parsedItems);
165+
166+
let xmlItemsAll = "";
167+
for (const item of parsedItems) {
168+
const itemDate = (item.dateObject instanceof Date && isValid(item.dateObject))
169+
? item.dateObject
170+
: new Date(); // Fallback to now if dateObject is null or invalid
171+
const pubDateString = format(itemDate, "EEE, dd MMM yyyy HH:mm:ss 'GMT'", { timeZone: 'GMT' });
172+
173+
const titleCDATA = `<![CDATA[${item.title}]]>`;
174+
const descriptionCDATA = `<![CDATA[${item.descriptionContent}]]>`;
175+
const linkElement = item.link ? `<link><![CDATA[${item.link}]]></link>` : '';
176+
const guidElement = item.link ? `<guid><![CDATA[${item.link}]]></guid>` : '';
177+
178+
const xmlItem = ` <item>
179+
<title>${titleCDATA}</title>
180+
<description>${descriptionCDATA}</description>
181+
${linkElement}
182+
${guidElement}
151183
<pubDate>${pubDateString}</pubDate>
152184
</item>`;
153-
154-
xmlItemsAll += xmlItem;
155-
}
185+
xmlItemsAll += xmlItem;
156186
}
157187
return xmlItemsAll;
158188
}
159189

190+
160191
function generateFeedManualMode(values) {
161192
let xmlItemsAll = []
162193
for (const key in values) {

0 commit comments

Comments
 (0)