diff --git a/Marlin/Marlin_main.cpp b/Marlin/Marlin_main.cpp
index 3e2c641acfa97d50f7c317642862366d222ab2b5..f880434756b04a84dffc921059d3ede345799b1d 100644
--- a/Marlin/Marlin_main.cpp
+++ b/Marlin/Marlin_main.cpp
@@ -60,6 +60,7 @@
 #include "pins_arduino.h"
 #include "math.h"
 #include "nozzle.h"
+#include "timestamp_t.h"
 
 #if ENABLED(USE_WATCHDOG)
   #include "watchdog.h"
@@ -4052,22 +4053,15 @@ inline void gcode_M17() {
  * M31: Get the time since the start of SD Print (or last M109)
  */
 inline void gcode_M31() {
-  millis_t t = print_job_timer.duration();
-  int d = int(t / 60 / 60 / 24),
-      h = int(t / 60 / 60) % 60,
-      m = int(t / 60) % 60,
-      s = int(t % 60);
-  char time[18];                                          // 123456789012345678
-  if (d)
-    sprintf_P(time, PSTR("%id %ih %im %is"), d, h, m, s); // 99d 23h 59m 59s
-  else
-    sprintf_P(time, PSTR("%ih %im %is"), h, m, s);        // 23h 59m 59s
+  char buffer[21];
+  timestamp_t time(print_job_timer.duration());
+  time.toString(buffer);
 
-  lcd_setstatus(time);
+  lcd_setstatus(buffer);
 
   SERIAL_ECHO_START;
   SERIAL_ECHOPGM(MSG_PRINT_TIME " ");
-  SERIAL_ECHOLN(time);
+  SERIAL_ECHOLN(buffer);
 
   thermalManager.autotempShutdown();
 }
diff --git a/Marlin/point_t.h b/Marlin/point_t.h
index dbad668581d8abd79dfc229b54277c64339c941f..360abce64944601995a26aa69a28217635c84d4f 100644
--- a/Marlin/point_t.h
+++ b/Marlin/point_t.h
@@ -23,18 +23,49 @@
 #ifndef __POINT_T__
 #define __POINT_T__
 
+/**
+ * @brief Cartesian Point
+ * @details Represents a three dimensional point on Cartesian coordinate system,
+ *          using an additional fourth dimension for the extrusion length.
+ *
+ * @param x The x-coordinate of the point.
+ * @param y The y-coordinate of the point.
+ * @param z The z-coordinate of the point.
+ * @param e The e-coordinate of the point.
+ */
 struct point_t {
   float x;
   float y;
   float z;
   float e;
 
+  /**
+   * @brief Two dimensional point constructor
+   *
+   * @param x The x-coordinate of the point.
+   * @param y The y-coordinate of the point.
+   */
   point_t(float const x, float const y)
     : point_t(x, y, NAN, NAN) {}
 
+  /**
+   * @brief Three dimensional point constructor
+   *
+   * @param x The x-coordinate of the point.
+   * @param y The y-coordinate of the point.
+   * @param z The z-coordinate of the point.
+   */
   point_t(float const x, float const y, float const z)
     : point_t(x, y, z, NAN) {}
 
+  /**
+   * @brief Tree dimensional point constructor with extrusion length
+   *
+   * @param x The x-coordinate of the point.
+   * @param y The y-coordinate of the point.
+   * @param z The z-coordinate of the point.
+   * @param e The e-coordinate of the point.
+   */
   point_t(float const x, float const y, float const z, float const e) {
     this->x = x;
     this->y = y;
@@ -43,4 +74,4 @@ struct point_t {
   }
 };
 
-#endif
+#endif // __POINT_T__
diff --git a/Marlin/printcounter.cpp b/Marlin/printcounter.cpp
index 489503c3a9daac26125c9cc0379089eb99ef1ef4..9bfb0f755e1f8a84f23cf10fa8b885bff6a58bb7 100644
--- a/Marlin/printcounter.cpp
+++ b/Marlin/printcounter.cpp
@@ -22,6 +22,7 @@
 
 #include "Marlin.h"
 #include "printcounter.h"
+#include "timestamp_t.h"
 
 PrintCounter::PrintCounter(): super() {
   this->loadStats();
@@ -92,6 +93,9 @@ void PrintCounter::saveStats() {
 }
 
 void PrintCounter::showStats() {
+  char buffer[21];
+  timestamp_t time;
+
   SERIAL_PROTOCOLPGM(MSG_STATS);
 
   SERIAL_ECHOPGM("Prints: ");
@@ -107,17 +111,11 @@ void PrintCounter::showStats() {
   SERIAL_EOL;
   SERIAL_PROTOCOLPGM(MSG_STATS);
 
-  uint32_t t = this->data.printTime / 60;
-  SERIAL_ECHOPGM("Total time: ");
-
-  SERIAL_ECHO(t / 60 / 24);
-  SERIAL_ECHOPGM("d ");
+  time.timestamp = this->data.printTime;
+  time.toString(buffer);
 
-  SERIAL_ECHO((t / 60) % 24);
-  SERIAL_ECHOPGM("h ");
-
-  SERIAL_ECHO(t % 60);
-  SERIAL_ECHOPGM("min");
+  SERIAL_ECHOPGM("Total time: ");
+  SERIAL_ECHO(buffer);
 
   #if ENABLED(DEBUG_PRINTCOUNTER)
     SERIAL_ECHOPGM(" (");
@@ -125,17 +123,11 @@ void PrintCounter::showStats() {
     SERIAL_ECHOPGM(")");
   #endif
 
-  uint32_t l = this->data.longestPrint / 60;
-  SERIAL_ECHOPGM(", Longest job: ");
-
-  SERIAL_ECHO(l / 60 / 24);
-  SERIAL_ECHOPGM("d ");
+  time.timestamp = this->data.longestPrint;
+  time.toString(buffer);
 
-  SERIAL_ECHO((l / 60) % 24);
-  SERIAL_ECHOPGM("h ");
-
-  SERIAL_ECHO(l % 60);
-  SERIAL_ECHOPGM("min");
+  SERIAL_ECHOPGM(", Longest job: ");
+  SERIAL_ECHO(buffer);
 
   #if ENABLED(DEBUG_PRINTCOUNTER)
     SERIAL_ECHOPGM(" (");
diff --git a/Marlin/timestamp_t.h b/Marlin/timestamp_t.h
new file mode 100644
index 0000000000000000000000000000000000000000..2a18eb00f5ea66fc831b69a53747ee85ffced598
--- /dev/null
+++ b/Marlin/timestamp_t.h
@@ -0,0 +1,118 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ *
+ * Based on Sprinter and grbl.
+ * Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __TIMESTAMP_T__
+#define __TIMESTAMP_T__
+
+struct timestamp_t {
+  /**
+   * @brief Number of seconds
+   */
+  uint32_t timestamp;
+
+  /**
+   * @brief Date time blank constructor
+   */
+  timestamp_t()
+    : timestamp_t(0) {};
+
+  /**
+   * @brief Date time constructor
+   * @details Initializes the timestamp_t structure based on a number of seconds
+   *
+   * @param seconds The number of seconds
+   */
+  timestamp_t(uint32_t const &seconds) {
+    this->timestamp = seconds;
+  }
+
+  /**
+   * @brief Formats the date as number of years
+   * @return The number of years
+   */
+  inline uint8_t year() const {
+    return this->day() / 365;
+  }
+
+  /**
+   * @brief Formats the date as number of days
+   * @return The number of days
+   */
+  inline uint16_t day() const {
+    return this->hour() / 24;
+  }
+
+  /**
+   * @brief Formats the date as number of hours
+   * @return The number of hours
+   */
+  inline uint32_t hour() const {
+    return this->minute() / 60;
+  }
+
+  /**
+   * @brief Formats the date as number of minutes
+   * @return The number of minutes
+   */
+  inline uint32_t minute() const {
+    return this->second() / 60;
+  }
+
+  /**
+   * @brief Formats the date as number of seconds
+   * @return The number of seconds
+   */
+  inline uint32_t second() const {
+    return this->timestamp;
+  }
+
+  /**
+   * @brief Formats the date as a string
+   * @details Returns the timestamp formated as a string
+   *
+   * @param buffer The array pointed to must be able to accommodate 21 bytes
+   *
+   * String output examples:
+   *  123456789012345678901 (strlen)
+   *  135y 364d 23h 59m 59s
+   *  364d 23h 59m 59s
+   *  23h 59m 59s
+   *  59m 59s
+   *  59s
+   *
+   */
+  void toString(char *buffer) const {
+    int y = this->year(),
+        d = this->day() % 365,
+        h = this->hour() % 24,
+        m = this->minute() % 60,
+        s = this->second() % 60;
+
+    if (y) sprintf_P(buffer, PSTR("%iy %id %ih %im %is"), y, d, h, m, s);
+    else if (d) sprintf_P(buffer, PSTR("%id %ih %im %is"), d, h, m, s);
+    else if (h) sprintf_P(buffer, PSTR("%ih %im %is"), h, m, s);
+    else if (m) sprintf_P(buffer, PSTR("%im %is"), m, s);
+    else sprintf_P(buffer, PSTR("%is"), s);
+  }
+};
+
+#endif // __TIMESTAMP_T__