diff --git a/package-lock.json b/package-lock.json
index 4f806e283d0c8e34f7e5a3a888f5cb88dcc1f993..98d48f4c4c03c687554409041d59f9818032d1d3 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -112,6 +112,19 @@
         "path-exists": "^3.0.0"
       }
     },
+    "moment": {
+      "version": "2.24.0",
+      "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
+      "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg=="
+    },
+    "moment-timezone": {
+      "version": "0.5.27",
+      "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.27.tgz",
+      "integrity": "sha512-EIKQs7h5sAsjhPCqN6ggx6cEbs94GK050254TIJySD1bzoM5JTYDwAU1IoVOeTOL6Gm27kYJ51/uuvq1kIlrbw==",
+      "requires": {
+        "moment": ">= 2.9.0"
+      }
+    },
     "ms": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
diff --git a/package.json b/package.json
index 5993bd71b2e9c9111a67b8bc2620f284651802ae..0de67f033cde395d4c5a6f2d7628654ec8d6f34f 100644
--- a/package.json
+++ b/package.json
@@ -11,6 +11,7 @@
   "license": "EPL-2.0",
   "dependencies": {
     "axios": "^0.19.0",
+    "moment-timezone": "^0.5.27",
     "random-words": "^1.1.0",
     "uuid": "^3.3.3",
     "yargs": "^14.0.0"
diff --git a/src/main/java/org/eclipsefoundation/marketplace/dto/Listing.java b/src/main/java/org/eclipsefoundation/marketplace/dto/Listing.java
index e70d9fa81f10cf3afebdeded9373962e21c2e652..2a76e21563ef2a4932f55073d02067e4eb8b8c85 100644
--- a/src/main/java/org/eclipsefoundation/marketplace/dto/Listing.java
+++ b/src/main/java/org/eclipsefoundation/marketplace/dto/Listing.java
@@ -48,11 +48,11 @@ public class Listing extends NodeBase {
 
 	@SortableField(name = DatabaseFieldNames.CREATION_DATE)
 	@JsonbProperty(DatabaseFieldNames.CREATION_DATE)
-	private long creationDate;
+	private String creationDate;
 
 	@SortableField(name = DatabaseFieldNames.UPDATE_DATE)
 	@JsonbProperty(DatabaseFieldNames.UPDATE_DATE)
-	private long updateDate;
+	private String updateDate;
 	@JsonbProperty(DatabaseFieldNames.LICENSE_TYPE)
 	private String license;
 	private List<String> marketIds;
@@ -224,28 +224,28 @@ public class Listing extends NodeBase {
 	/**
 	 * @return the creationDate
 	 */
-	public long getCreationDate() {
+	public String getCreationDate() {
 		return creationDate;
 	}
 
 	/**
 	 * @param creationDate the creationDate to set
 	 */
-	public void setCreationDate(long creationDate) {
+	public void setCreationDate(String creationDate) {
 		this.creationDate = creationDate;
 	}
 
 	/**
 	 * @return the updateDate
 	 */
-	public long getUpdateDate() {
+	public String getUpdateDate() {
 		return updateDate;
 	}
 
 	/**
 	 * @param updateDate the updateDate to set
 	 */
-	public void setUpdateDate(long updateDate) {
+	public void setUpdateDate(String updateDate) {
 		this.updateDate = updateDate;
 	}
 
diff --git a/src/main/java/org/eclipsefoundation/marketplace/dto/codecs/ListingCodec.java b/src/main/java/org/eclipsefoundation/marketplace/dto/codecs/ListingCodec.java
index 9265d119d3df81cc39c7fa914fd1204d7dff4901..c8f956adbfdb9d5d96a26b6ab17579c4cc756daf 100644
--- a/src/main/java/org/eclipsefoundation/marketplace/dto/codecs/ListingCodec.java
+++ b/src/main/java/org/eclipsefoundation/marketplace/dto/codecs/ListingCodec.java
@@ -6,7 +6,6 @@
  */
 package org.eclipsefoundation.marketplace.dto.codecs;
 
-import java.util.Date;
 import java.util.UUID;
 import java.util.stream.Collectors;
 
@@ -25,6 +24,7 @@ import org.eclipsefoundation.marketplace.dto.converters.CategoryConverter;
 import org.eclipsefoundation.marketplace.dto.converters.OrganizationConverter;
 import org.eclipsefoundation.marketplace.dto.converters.ListingVersionConverter;
 import org.eclipsefoundation.marketplace.dto.converters.TagConverter;
+import org.eclipsefoundation.marketplace.helper.DateTimeHelper;
 import org.eclipsefoundation.marketplace.namespace.DatabaseFieldNames;
 
 import com.mongodb.MongoClient;
@@ -76,13 +76,13 @@ public class ListingCodec implements CollectibleCodec<Listing> {
 		doc.put(DatabaseFieldNames.TOTAL_NSTALLS, value.getInstallsTotal());
 		doc.put(DatabaseFieldNames.LICENSE_TYPE, value.getLicense());
 		doc.put(DatabaseFieldNames.LISTING_STATUS, value.getStatus());
-		doc.put(DatabaseFieldNames.UPDATE_DATE, new Date(value.getUpdateDate()));
-		doc.put(DatabaseFieldNames.CREATION_DATE, new Date(value.getCreationDate()));
+		doc.put(DatabaseFieldNames.UPDATE_DATE, DateTimeHelper.toRFC3339(value.getUpdateDate()));
+		doc.put(DatabaseFieldNames.CREATION_DATE, DateTimeHelper.toRFC3339(value.getCreationDate()));
 		doc.put(DatabaseFieldNames.FOUNDATION_MEMBER_FLAG, value.isFoundationMember());
 		doc.put(DatabaseFieldNames.CATEGORY_IDS, value.getCategoryIds());
 		doc.put(DatabaseFieldNames.SCREENSHOTS, value.getScreenshots());
 		doc.put(DatabaseFieldNames.MARKET_IDS, value.getMarketIds());
-
+		
 		// for nested document types, use the converters to safely transform into BSON
 		// documents
 		doc.put(DatabaseFieldNames.LISTING_ORGANIZATIONS, organizationConverter.convert(value.getOrganization()));
@@ -102,7 +102,7 @@ public class ListingCodec implements CollectibleCodec<Listing> {
 	public Listing decode(BsonReader reader, DecoderContext decoderContext) {
 		Document document = documentCodec.decode(reader, decoderContext);
 		Listing out = new Listing();
-		
+
 		// for each field, get the value from the encoded object and set it in POJO
 		out.setId(document.getString(DatabaseFieldNames.DOCID));
 		out.setTitle(document.getString(DatabaseFieldNames.TITLE));
@@ -133,9 +133,9 @@ public class ListingCodec implements CollectibleCodec<Listing> {
 		out.setCategories(document.getList(DatabaseFieldNames.LISTING_CATEGORIES, Document.class).stream()
 				.map(categoryConverter::convert).collect(Collectors.toList()));
 
-		// convert date to epoch milli
-		out.setCreationDate(document.getDate(DatabaseFieldNames.CREATION_DATE).toInstant().toEpochMilli());
-		out.setUpdateDate(document.getDate(DatabaseFieldNames.UPDATE_DATE).toInstant().toEpochMilli());
+		// convert date to date string
+		out.setCreationDate(DateTimeHelper.toRFC3339(document.getDate(DatabaseFieldNames.CREATION_DATE)));
+		out.setUpdateDate(DateTimeHelper.toRFC3339(document.getDate(DatabaseFieldNames.UPDATE_DATE)));
 
 		return out;
 	}
diff --git a/src/main/java/org/eclipsefoundation/marketplace/helper/DateTimeHelper.java b/src/main/java/org/eclipsefoundation/marketplace/helper/DateTimeHelper.java
new file mode 100644
index 0000000000000000000000000000000000000000..d6b4157d6c445e202a21fb8f5f9e6221417d3ac1
--- /dev/null
+++ b/src/main/java/org/eclipsefoundation/marketplace/helper/DateTimeHelper.java
@@ -0,0 +1,61 @@
+/* Copyright (c) 2019 Eclipse Foundation and others.
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Public License 2.0
+ * which is available at http://www.eclipse.org/legal/epl-v20.html,
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipsefoundation.marketplace.helper;
+
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.util.Date;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Central implementation for handling date time conversion in the service.
+ * Class uses Java8 DateTime formatters, creating an internal format that
+ * represents RFC 3339
+ * 
+ * @author Martin Lowe
+ */
+public class DateTimeHelper {
+	private static final Logger LOGGER = LoggerFactory.getLogger(DateTimeHelper.class);
+	private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ssXXX");
+
+	/**
+	 * Converts RFC 3339 compliant date string to date object. If non compliant
+	 * string is passed, issue is logged and null is returned. If negative UTC
+	 * timezone (-00:00) is passed, UTC time zone is assumed.
+	 * 
+	 * @param dateString an RFC 3339 date string.
+	 * @return a date object representing time in date string, or null if not in RFC
+	 *         3339 format.
+	 */
+	public static Date toRFC3339(String dateString) {
+		try {
+			return Date.from(ZonedDateTime.parse(dateString, formatter).toInstant());
+		} catch (DateTimeParseException e) {
+			LOGGER.warn("Could not parse date from string '{}'", dateString, e);
+			return null;
+		}
+	}
+
+	/**
+	 * Converts passed date to RFC 3339 compliant date string. Time is adjusted to
+	 * be in UTC time.
+	 * 
+	 * @param date the date object to convert to RFC 3339 format.
+	 * @return the RFC 3339 format date string.
+	 */
+	public static String toRFC3339(Date date) {
+		return formatter.format(date.toInstant().atZone(ZoneId.of("UTC")));
+	}
+
+	// hide constructor
+	private DateTimeHelper() {
+	}
+}
diff --git a/src/main/node/index.js b/src/main/node/index.js
index 441bdecf9954b16025abc089995a6cf38ab059c5..bb88462e57c5451747ee3d7d7516ea5f758eceaa 100644
--- a/src/main/node/index.js
+++ b/src/main/node/index.js
@@ -26,6 +26,7 @@ const argv = require('yargs')
   }).argv;
 
 let max = argv.c;
+var moment = require('moment-timezone');
 const lic_types = ["EPL-2.0","EPL-1.0","GPL"];
 const platforms = ["windows","macos","linux"];
 const eclipseVs = ["4.6","4.7","4.8","4.9","4.10","4.11","4.12"];
@@ -143,6 +144,8 @@ function generateJSON(id) {
     "status": "draft",
   	"support_url": "https://jakarta.ee/about/faq",
   	"license_type": lic_types[Math.floor(Math.random()*lic_types.length)],
+    "created": moment.tz((new Date()).toISOString(), "America/Toronto").format(),
+    "changed": moment.tz((new Date()).toISOString(), "America/Toronto").format(),
   	"authors": [
   		{
   			"full_name": "Martin Lowe",
diff --git a/src/test/java/org/eclipsefoundation/marketplace/helper/DateTimeHelperTest.java b/src/test/java/org/eclipsefoundation/marketplace/helper/DateTimeHelperTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..084893bede63eb7bd65eef1ff8656ca4605f8727
--- /dev/null
+++ b/src/test/java/org/eclipsefoundation/marketplace/helper/DateTimeHelperTest.java
@@ -0,0 +1,100 @@
+/* Copyright (c) 2019 Eclipse Foundation and others.
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Public License 2.0
+ * which is available at http://www.eclipse.org/legal/epl-v20.html,
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.eclipsefoundation.marketplace.helper;
+
+import java.time.ZoneId;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.TimeZone;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import io.quarkus.test.junit.QuarkusTest;
+
+/**
+ * Test class for {@linkplain DateTimeHelper}
+ * 
+ * @author Martin Lowe
+ */
+@QuarkusTest
+public class DateTimeHelperTest {
+
+	@Test
+	public void validRFC3339DateStringIn() {
+		// Test standard time
+		String dateString = "1996-12-19T16:39:57-08:00";
+		// Timezone needs to be set as otherwise it defaults to system local offset.
+		TimeZone tz = TimeZone.getTimeZone(ZoneId.of("GMT-08:00"));
+		Calendar c = Calendar.getInstance(tz);
+
+		Date d = DateTimeHelper.toRFC3339(dateString);
+		c.setTime(d);
+
+		Assertions.assertEquals(1996, c.get(Calendar.YEAR));
+		// 0 based month
+		Assertions.assertEquals(11, c.get(Calendar.MONTH));
+		Assertions.assertEquals(19, c.get(Calendar.DATE));
+		Assertions.assertEquals(16, c.get(Calendar.HOUR_OF_DAY));
+		Assertions.assertEquals(39, c.get(Calendar.MINUTE));
+		Assertions.assertEquals(57, c.get(Calendar.SECOND));
+		Assertions.assertEquals(-TimeUnit.MILLISECONDS.convert(8, TimeUnit.HOURS),
+				c.get(Calendar.ZONE_OFFSET) + c.get(Calendar.DST_OFFSET));
+
+		// Test unknown offset time
+		String unknownOffsetDateString = "1996-12-19T16:39:57-00:00";
+		c = Calendar.getInstance(TimeZone.getTimeZone(ZoneId.of("UTC")));
+
+		// set calendar to actual value for easy reading
+		d = DateTimeHelper.toRFC3339(unknownOffsetDateString);
+		c.setTime(d);
+
+		System.out.println(c.toInstant().toString());
+		Assertions.assertEquals(1996, c.get(Calendar.YEAR));
+		// 0 based month
+		Assertions.assertEquals(11, c.get(Calendar.MONTH));
+		Assertions.assertEquals(19, c.get(Calendar.DATE));
+		Assertions.assertEquals(16, c.get(Calendar.HOUR_OF_DAY));
+		Assertions.assertEquals(39, c.get(Calendar.MINUTE));
+		Assertions.assertEquals(57, c.get(Calendar.SECOND));
+		Assertions.assertEquals(0, c.get(Calendar.ZONE_OFFSET) + c.get(Calendar.DST_OFFSET));
+	}
+
+	@Test
+	public void validRFC3339DateStringOut() {
+		// Set a time equal to "1996-12-19T16:39:57-08:00"
+		// Timezone needs to be set as otherwise it defaults to system local offset.
+		TimeZone tz = TimeZone.getTimeZone(ZoneId.of("GMT-08:00"));
+		// create a calendar instance to represent the given date
+		Calendar c = Calendar.getInstance(tz);
+		c.set(1996, 11, 19, 16, 39, 57);
+
+		// expect UTC time in return
+		String expected = "1996-12-20T00:39:57Z";
+		String actual = DateTimeHelper.toRFC3339(c.getTime());
+
+		Assertions.assertEquals(expected, actual);
+	}
+
+	@Test
+	public void invalidRFC3339DateStringIn() {
+		// test various permutations of the date string to ensure format enforcement
+		Assertions.assertNull(DateTimeHelper.toRFC3339("19991220"),
+				"Expected no returned date object for string: 19991220");
+		Assertions.assertNull(DateTimeHelper.toRFC3339("1996/12/20"),
+				"Expected no returned date object for string: 1996/12/20");
+		Assertions.assertNull(DateTimeHelper.toRFC3339("1996-12-20"),
+				"Expected no returned date object for string: 1996-12-20");
+		Assertions.assertNull(DateTimeHelper.toRFC3339("1996-12-20 16:39:57-08:00"),
+				"Expected no returned date object for string: 1996-12-20 16:39:57-08:00");
+		Assertions.assertNull(DateTimeHelper.toRFC3339("1996-12-20T16:39:57"),
+				"Expected no returned date object for string: 1996-12-20T16:39:57");
+		Assertions.assertNull(DateTimeHelper.toRFC3339("1996-12-20T16:39:57-0800"),
+				"Expected no returned date object for string: 1996-12-20T16:39:57-0800");
+	}
+}