From c37ec7463f82a7f6c157dfd8ddeb5bb69a69bfde Mon Sep 17 00:00:00 2001
From: tamasmeszaros <meszaros.q@gmail.com>
Date: Wed, 11 Sep 2019 12:13:59 +0200
Subject: [PATCH] Add new config values to SL1 zip file config.ini

All requested config values are written into SL1 ini file inside the zip

* TIme.hpp and Time.cpp is now part of libslic3r instead of libslic3r_gui
* Updated time manipulation function: separate timestamp_local_str and timestamp_utc_str
* timestamp_utc_str is used in header_slic3r_generated(). Gcode now contains UTC timestamps
---
 src/libslic3r/CMakeLists.txt             |   2 +
 src/libslic3r/SLA/SLARaster.hpp          |  13 ++-
 src/libslic3r/SLA/SLARasterWriter.cpp    | 107 +++++++++++------------
 src/libslic3r/SLA/SLARasterWriter.hpp    | 107 +++++++++--------------
 src/libslic3r/SLAPrint.cpp               |  76 +++++++++++-----
 src/libslic3r/SLAPrint.hpp               |  14 ++-
 src/{slic3r/Utils => libslic3r}/Time.cpp |  68 ++++++++++----
 src/libslic3r/Time.hpp                   |  47 ++++++++++
 src/libslic3r/Utils.hpp                  |   4 +-
 src/libslic3r/utils.cpp                  |  14 +--
 src/slic3r/CMakeLists.txt                |   2 -
 src/slic3r/Config/Snapshot.cpp           |   2 +-
 src/slic3r/GUI/ConfigSnapshotDialog.cpp  |   2 +-
 src/slic3r/GUI/Plater.cpp                |   3 +-
 src/slic3r/Utils/Time.hpp                |  25 ------
 15 files changed, 273 insertions(+), 213 deletions(-)
 rename src/{slic3r/Utils => libslic3r}/Time.cpp (54%)
 create mode 100644 src/libslic3r/Time.hpp
 delete mode 100644 src/slic3r/Utils/Time.hpp

diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt
index 85e11eded..97e0fc09b 100644
--- a/src/libslic3r/CMakeLists.txt
+++ b/src/libslic3r/CMakeLists.txt
@@ -165,6 +165,8 @@ add_library(libslic3r STATIC
     TriangleMesh.hpp
     utils.cpp
     Utils.hpp
+    Time.cpp
+    Time.hpp
     MTUtils.hpp
     Zipper.hpp
     Zipper.cpp
diff --git a/src/libslic3r/SLA/SLARaster.hpp b/src/libslic3r/SLA/SLARaster.hpp
index d3bd52d92..8b27fd153 100644
--- a/src/libslic3r/SLA/SLARaster.hpp
+++ b/src/libslic3r/SLA/SLARaster.hpp
@@ -68,15 +68,14 @@ public:
 
     /// Type that represents a resolution in pixels.
     struct Resolution {
-        unsigned width_px;
-        unsigned height_px;
+        size_t width_px;
+        size_t height_px;
 
-        inline Resolution(unsigned w = 0, unsigned h = 0):
-            width_px(w), height_px(h) {}
+        inline Resolution(size_t w = 0, size_t h = 0)
+            : width_px(w), height_px(h)
+        {}
 
-        inline unsigned pixels() const /*noexcept*/ {
-            return width_px * height_px;
-        }
+        inline size_t pixels() const { return width_px * height_px; }
     };
 
     /// Types that represents the dimension of a pixel in millimeters.
diff --git a/src/libslic3r/SLA/SLARasterWriter.cpp b/src/libslic3r/SLA/SLARasterWriter.cpp
index f7c3925ac..3e6f015d4 100644
--- a/src/libslic3r/SLA/SLARasterWriter.cpp
+++ b/src/libslic3r/SLA/SLARasterWriter.cpp
@@ -1,5 +1,7 @@
 #include "SLARasterWriter.hpp"
 #include "libslic3r/Zipper.hpp"
+#include "libslic3r/Time.hpp"
+
 #include "ExPolygon.hpp"
 #include <libnest2d/backends/clipper/clipper_polygon.hpp>
 
@@ -10,25 +12,13 @@ namespace Slic3r { namespace sla {
 
 std::string SLARasterWriter::createIniContent(const std::string& projectname) const 
 {
-    auto expt_str = std::to_string(m_exp_time_s);
-    auto expt_first_str = std::to_string(m_exp_time_first_s);
-    auto layerh_str = std::to_string(m_layer_height);
-
-    const std::string cnt_fade_layers = std::to_string(m_cnt_fade_layers);
-    const std::string cnt_slow_layers = std::to_string(m_cnt_slow_layers);
-    const std::string cnt_fast_layers = std::to_string(m_cnt_fast_layers);
-    const std::string used_material   = std::to_string(m_used_material);
-
-    return std::string(
-    "action = print\n"
-    "jobDir = ") + projectname + "\n" +
-    "expTime = " + expt_str + "\n"
-    "expTimeFirst = " + expt_first_str + "\n"
-    "numFade = " + cnt_fade_layers + "\n"
-    "layerHeight = " + layerh_str + "\n"
-    "usedMaterial = " + used_material + "\n"
-    "numSlow = " + cnt_slow_layers + "\n"
-                                     "numFast = " + cnt_fast_layers + "\n";
+    std::string out("action = print\njobDir = ");
+    out += projectname + "\n";
+    
+    for (auto &param : m_config)
+        out += param.first + " = " + param.second + "\n";    
+    
+    return out;
 }
 
 void SLARasterWriter::flpXY(ClipperLib::Polygon &poly)
@@ -53,38 +43,14 @@ void SLARasterWriter::flpXY(ExPolygon &poly)
     }
 }
 
-SLARasterWriter::SLARasterWriter(const SLAPrinterConfig &cfg, 
-                                 const SLAMaterialConfig &mcfg, 
-                                 double layer_height)
+SLARasterWriter::SLARasterWriter(const Raster::Resolution  &res,
+                                 const Raster::PixelDim    &pixdim,
+                                 const std::array<bool, 2> &mirror,
+                                 double gamma)
+    : m_res(res), m_pxdim(pixdim), m_mirror(mirror), m_gamma(gamma)
 {
-    double w = cfg.display_width.getFloat();
-    double h = cfg.display_height.getFloat();
-    auto pw = unsigned(cfg.display_pixels_x.getInt());
-    auto ph = unsigned(cfg.display_pixels_y.getInt());
-    
-    m_mirror[X] = cfg.display_mirror_x.getBool();
-    
     // PNG raster will implicitly do an Y mirror
-    m_mirror[Y] = ! cfg.display_mirror_y.getBool();
-        
-    auto ro = cfg.display_orientation.getInt();
-    
-    if(ro == roPortrait) {
-        std::swap(w, h);
-        std::swap(pw, ph);
-        m_o = roPortrait;
-        
-        // XY flipping implicitly does an X mirror
-        m_mirror[X] = ! m_mirror[X];
-    } else m_o = roLandscape;
-    
-    m_res = Raster::Resolution(pw, ph);
-    m_pxdim = Raster::PixelDim(w/pw, h/ph);
-    m_exp_time_s = mcfg.exposure_time.getFloat();
-    m_exp_time_first_s = mcfg.initial_exposure_time.getFloat();
-    m_layer_height = layer_height;
-    
-    m_gamma = cfg.gamma_correction.getFloat();
+    m_mirror[1] = !m_mirror[1];
 }
 
 void SLARasterWriter::save(const std::string &fpath, const std::string &prjname)
@@ -121,15 +87,44 @@ void SLARasterWriter::save(const std::string &fpath, const std::string &prjname)
     }
 }
 
-void SLARasterWriter::set_statistics(const std::vector<double> statistics)
+namespace {
+
+std::string get_cfg_value(const DynamicPrintConfig &cfg, const std::string &key)
 {
-    if (statistics.size() != psCnt)
-        return;
+    std::string ret;
     
-    m_used_material   = statistics[psUsedMaterial];
-    m_cnt_fade_layers = int(statistics[psNumFade]);
-    m_cnt_slow_layers = int(statistics[psNumSlow]);
-    m_cnt_fast_layers = int(statistics[psNumFast]);
+    if (cfg.has(key)) {
+        auto opt = cfg.option(key);
+        if (opt) ret = opt->serialize();
+    }
+    
+    return ret;    
+}
+
+} // namespace
+
+void SLARasterWriter::set_config(const DynamicPrintConfig &cfg)
+{
+    m_config["layerHeight"]    = get_cfg_value(cfg, "layer_height");
+    m_config["expTime"]        = get_cfg_value(cfg, "exposure_time");
+    m_config["expTimeFirst"]   = get_cfg_value(cfg, "initial_exposure_time");
+    m_config["materialName"]   = get_cfg_value(cfg, "sla_material_settings_id");
+    m_config["printerModel"]   = get_cfg_value(cfg, "printer_model");
+    m_config["printerVariant"] = get_cfg_value(cfg, "printer_variant");
+    m_config["printerProfile"] = get_cfg_value(cfg, "printer_settings_id");
+    m_config["printProfile"]   = get_cfg_value(cfg, "sla_print_settings_id");
+
+    m_config["fileCreationTimestamp"] = Utils::current_utc_time2str();
+    m_config["prusaSlicerVersion"]    = SLIC3R_BUILD_ID;
+}
+
+void SLARasterWriter::set_statistics(const PrintStatistics &stats)
+{
+    m_config["usedMaterial"] = std::to_string(stats.used_material);
+    m_config["numFade"]      = std::to_string(stats.num_fade);
+    m_config["numSlow"]      = std::to_string(stats.num_slow);
+    m_config["numFast"]      = std::to_string(stats.num_fast);
+    m_config["printTime"]    = std::to_string(stats.estimated_print_time_s);
 }
 
 } // namespace sla
diff --git a/src/libslic3r/SLA/SLARasterWriter.hpp b/src/libslic3r/SLA/SLARasterWriter.hpp
index 7133d2dde..b9202c464 100644
--- a/src/libslic3r/SLA/SLARasterWriter.hpp
+++ b/src/libslic3r/SLA/SLARasterWriter.hpp
@@ -3,8 +3,10 @@
 
 // For png export of the sliced model
 #include <fstream>
+#include <string>
 #include <sstream>
 #include <vector>
+#include <map>
 #include <array>
 
 #include "libslic3r/PrintConfig.hpp"
@@ -23,20 +25,19 @@ namespace Slic3r { namespace sla {
 class SLARasterWriter
 {
 public:
-    enum RasterOrientation {
+    enum Orientation {
         roLandscape,
         roPortrait
     };
     
     // Used for addressing parameters of set_statistics()
-    enum ePrintStatistics
-    {
-        psUsedMaterial = 0,
-        psNumFade,
-        psNumSlow,
-        psNumFast,
-    
-        psCnt
+    struct PrintStatistics
+    {    
+        double used_material = 0.;
+        double estimated_print_time_s = 0.;
+        size_t num_fade = 0;
+        size_t num_slow = 0;
+        size_t num_fast = 0;
     };
     
 private:
@@ -47,21 +48,13 @@ private:
         RawBytes rawbytes;
 
         Layer() = default;
-        Layer(const Layer&) = delete; // The image is big, do not copy by accident
-        Layer& operator=(const Layer&) = delete;
         
-        // /////////////////////////////////////////////////////////////////////
-        // FIXME: the following is needed for MSVC2013 compatibility
-        // /////////////////////////////////////////////////////////////////////
+        // The image is big, do not copy by accident
+        Layer(const Layer&) = delete; 
+        Layer& operator=(const Layer&) = delete;
 
-        // Layer(Layer&& m) = default;
-        // Layer& operator=(Layer&&) = default;
-        Layer(Layer &&m):
-            raster(std::move(m.raster)), rawbytes(std::move(m.rawbytes)) {}
-        Layer& operator=(Layer &&m) {
-            raster = std::move(m.raster); rawbytes = std::move(m.rawbytes);
-            return *this;
-        }
+        Layer(Layer &&m) = default;
+        Layer &operator=(Layer &&) = default;
     };
 
     // We will save the compressed PNG data into RawBytes type buffers in 
@@ -69,66 +62,46 @@ private:
     std::vector<Layer> m_layers_rst;
     Raster::Resolution m_res;
     Raster::PixelDim m_pxdim;
-    double m_exp_time_s = .0, m_exp_time_first_s = .0;
-    double m_layer_height = .0;
-    RasterOrientation m_o = roPortrait;
     std::array<bool, 2> m_mirror;
-    
     double m_gamma;
-
-    double m_used_material = 0.0;
-    int    m_cnt_fade_layers = 0;
-    int    m_cnt_slow_layers = 0;
-    int    m_cnt_fast_layers = 0;
-
+    
+    std::map<std::string, std::string> m_config;
+    
     std::string createIniContent(const std::string& projectname) const;
     
     static void flpXY(ClipperLib::Polygon& poly);
     static void flpXY(ExPolygon& poly);
 
 public:
-
-    SLARasterWriter(const SLAPrinterConfig& cfg, 
-                    const SLAMaterialConfig& mcfg, 
-                    double layer_height);
+    SLARasterWriter(const Raster::Resolution  &res,
+                    const Raster::PixelDim    &pixdim,
+                    const std::array<bool, 2> &mirror,
+                    double gamma = 1.);
 
     SLARasterWriter(const SLARasterWriter& ) = delete;
     SLARasterWriter& operator=(const SLARasterWriter&) = delete;
-
-    // /////////////////////////////////////////////////////////////////////////
-    // FIXME: the following is needed for MSVC2013 compatibility
-    // /////////////////////////////////////////////////////////////////////////
-
-    // SLARasterWriter(SLARasterWriter&& m) = default;
-    // SLARasterWriter& operator=(SLARasterWriter&&) = default;
-    SLARasterWriter(SLARasterWriter&& m):
-        m_layers_rst(std::move(m.m_layers_rst)),
-        m_res(m.m_res),
-        m_pxdim(m.m_pxdim),
-        m_exp_time_s(m.m_exp_time_s),
-        m_exp_time_first_s(m.m_exp_time_first_s),
-        m_layer_height(m.m_layer_height),
-        m_o(m.m_o),
-        m_mirror(std::move(m.m_mirror)),
-        m_gamma(m.m_gamma),
-        m_used_material(m.m_used_material),
-        m_cnt_fade_layers(m.m_cnt_fade_layers),
-        m_cnt_slow_layers(m.m_cnt_slow_layers),
-        m_cnt_fast_layers(m.m_cnt_fast_layers)
-    {}
-
-    // /////////////////////////////////////////////////////////////////////////
+    SLARasterWriter(SLARasterWriter&& m) = default;
+    SLARasterWriter& operator=(SLARasterWriter&&) = default;
 
     inline void layers(unsigned cnt) { if(cnt > 0) m_layers_rst.resize(cnt); }
     inline unsigned layers() const { return unsigned(m_layers_rst.size()); }
     
-    template<class Poly> void draw_polygon(const Poly& p, unsigned lyr) {
+    template<class Poly> void draw_polygon(const Poly& p, unsigned lyr,
+                      Orientation o = roPortrait)
+    {
         assert(lyr < m_layers_rst.size());
-        if(m_o == roPortrait) {
-            Poly poly(p); flpXY(poly);
+        
+        switch (o) {
+        case roPortrait: {
+            Poly poly(p);
+            flpXY(poly);
             m_layers_rst[lyr].raster.draw(poly);
+            break;
+        }
+        case roLandscape:
+            m_layers_rst[lyr].raster.draw(p);
+            break;
         }
-        else m_layers_rst[lyr].raster.draw(p);
     }
 
     inline void begin_layer(unsigned lyr) {
@@ -156,9 +129,11 @@ public:
         }
     }
 
-    void save(const std::string& fpath, const std::string& prjname = "");
+    void save(const std::string &fpath, const std::string &prjname = "");
 
-    void set_statistics(const std::vector<double> statistics);
+    void set_statistics(const PrintStatistics &statistics);
+    
+    void set_config(const DynamicPrintConfig &cfg);
 };
 
 } // namespace sla
diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp
index 30d6fc7c3..46d039c1f 100644
--- a/src/libslic3r/SLAPrint.cpp
+++ b/src/libslic3r/SLAPrint.cpp
@@ -1381,9 +1381,9 @@ void SLAPrint::process()
         // Estimated printing time
         // A layers count o the highest object
         if (m_printer_input.size() == 0)
-            m_print_statistics.estimated_print_time = "N/A";
+            m_print_statistics.estimated_print_time = std::nan("");
         else
-            m_print_statistics.estimated_print_time = get_time_dhms(float(estim_time));
+            m_print_statistics.estimated_print_time = estim_time;
 
         m_print_statistics.fast_layers_count = fast_layers;
         m_print_statistics.slow_layers_count = slow_layers;
@@ -1394,16 +1394,9 @@ void SLAPrint::process()
     // Rasterizing the model objects, and their supports
     auto rasterize = [this]() {
         if(canceled()) return;
-
-        { // create a raster printer for the current print parameters
-            double layerh = m_default_object_config.layer_height.getFloat();
-            m_printer.reset(new sla::SLARasterWriter(m_printer_config,
-                                                     m_material_config,
-                                                     layerh));
-        }
-
-        // Allocate space for all the layers
-        sla::SLARasterWriter &printer = *m_printer;
+        
+        // Set up the printer, allocate space for all the layers
+        sla::SLARasterWriter &printer = init_printer();
 
         auto lvlcnt = unsigned(m_printer_input.size());
         printer.layers(lvlcnt);
@@ -1422,10 +1415,12 @@ void SLAPrint::process()
         double dstatus = m_report_status.status();
 
         SpinMutex slck;
+        
+        auto orientation = get_printer_orientation();
 
         // procedure to process one height level. This will run in parallel
         auto lvlfn =
-        [this, &slck, &printer, increment, &dstatus, &pst]
+        [this, &slck, &printer, increment, &dstatus, &pst, orientation]
             (unsigned level_id)
         {
             if(canceled()) return;
@@ -1436,7 +1431,7 @@ void SLAPrint::process()
             printer.begin_layer(level_id);
 
             for(const ClipperLib::Polygon& poly : printlayer.transformed_slices())
-                printer.draw_polygon(poly, level_id);
+                printer.draw_polygon(poly, level_id, orientation);
 
             // Finish the layer for later saving it.
             printer.finish_layer(level_id);
@@ -1464,12 +1459,18 @@ void SLAPrint::process()
         tbb::parallel_for<unsigned, decltype(lvlfn)>(0, lvlcnt, lvlfn);
 
         // Set statistics values to the printer
-        m_printer->set_statistics(
-            {(m_print_statistics.objects_used_material
-              + m_print_statistics.support_used_material) / 1000,
-             double(m_default_object_config.faded_layers.getInt()),
-             double(m_print_statistics.slow_layers_count),
-             double(m_print_statistics.fast_layers_count)});
+        sla::SLARasterWriter::PrintStatistics stats;
+        stats.used_material = (m_print_statistics.objects_used_material +
+                               m_print_statistics.support_used_material) /
+                              1000;
+        
+        int num_fade = m_default_object_config.faded_layers.getInt();
+        stats.num_fade = num_fade >= 0 ? size_t(num_fade) : size_t(0);
+        stats.num_fast = m_print_statistics.fast_layers_count;
+        stats.num_slow = m_print_statistics.slow_layers_count;
+        stats.estimated_print_time_s = m_print_statistics.estimated_print_time;
+        
+        m_printer->set_statistics(stats);
     };
 
     using slaposFn = std::function<void(SLAPrintObject&)>;
@@ -1653,6 +1654,39 @@ bool SLAPrint::invalidate_state_by_config_options(const std::vector<t_config_opt
     return invalidated;
 }
 
+sla::SLARasterWriter & SLAPrint::init_printer()
+{
+    sla::Raster::Resolution res;
+    sla::Raster::PixelDim   pxdim;
+    std::array<bool, 2>     mirror;
+    double                  gamma;
+
+    double w  = m_printer_config.display_width.getFloat();
+    double h  = m_printer_config.display_height.getFloat();
+    auto   pw = size_t(m_printer_config.display_pixels_x.getInt());
+    auto   ph = size_t(m_printer_config.display_pixels_y.getInt());
+
+    mirror[X] = m_printer_config.display_mirror_x.getBool();
+    mirror[Y] = m_printer_config.display_mirror_y.getBool();
+
+    if (get_printer_orientation() == sla::SLARasterWriter::roPortrait) {
+        std::swap(w, h);
+        std::swap(pw, ph);
+
+        // XY flipping implicitly does an X mirror
+        mirror[X] = !mirror[X];
+    }
+
+    res   = sla::Raster::Resolution{pw, ph};
+    pxdim = sla::Raster::PixelDim{w / pw, h / ph};
+
+    gamma = m_printer_config.gamma_correction.getFloat();
+
+    m_printer.reset(new sla::SLARasterWriter(res, pxdim, mirror, gamma));
+    m_printer->set_config(m_full_print_config);
+    return *m_printer;
+}
+
 // Returns true if an object step is done on all objects and there's at least one object.
 bool SLAPrint::is_step_done(SLAPrintObjectStep step) const
 {
@@ -1932,7 +1966,7 @@ std::vector<sla::SupportPoint> SLAPrintObject::transformed_support_points() cons
 DynamicConfig SLAPrintStatistics::config() const
 {
     DynamicConfig config;
-    const std::string print_time = Slic3r::short_time(this->estimated_print_time);
+    const std::string print_time = Slic3r::short_time(get_time_dhms(float(this->estimated_print_time)));
     config.set_key_value("print_time", new ConfigOptionString(print_time));
     config.set_key_value("objects_used_material", new ConfigOptionFloat(this->objects_used_material));
     config.set_key_value("support_used_material", new ConfigOptionFloat(this->support_used_material));
diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp
index ec3b2d02e..a2bc1325a 100644
--- a/src/libslic3r/SLAPrint.hpp
+++ b/src/libslic3r/SLAPrint.hpp
@@ -300,7 +300,7 @@ class TriangleMesh;
 struct SLAPrintStatistics
 {
     SLAPrintStatistics() { clear(); }
-    std::string                     estimated_print_time;
+    double                          estimated_print_time;
     double                          objects_used_material;
     double                          support_used_material;
     size_t                          slow_layers_count;
@@ -316,7 +316,7 @@ struct SLAPrintStatistics
     std::string             finalize_output_path(const std::string &path_in) const;
 
     void clear() {
-        estimated_print_time.clear();
+        estimated_print_time = 0.;
         objects_used_material = 0.;
         support_used_material = 0.;
         slow_layers_count = 0;
@@ -458,6 +458,16 @@ private:
         
         double status() const { return m_st; }
     } m_report_status;
+    
+    sla::SLARasterWriter &init_printer();
+    
+    inline sla::SLARasterWriter::Orientation get_printer_orientation() const
+    {
+        auto ro = m_printer_config.display_orientation.getInt();
+        return ro == sla::SLARasterWriter::roPortrait ?
+                   sla::SLARasterWriter::roPortrait :
+                   sla::SLARasterWriter::roLandscape;
+    }
 
 	friend SLAPrintObject;
 };
diff --git a/src/slic3r/Utils/Time.cpp b/src/libslic3r/Time.cpp
similarity index 54%
rename from src/slic3r/Utils/Time.cpp
rename to src/libslic3r/Time.cpp
index db1aa31f6..1f65189b8 100644
--- a/src/slic3r/Utils/Time.cpp
+++ b/src/libslic3r/Time.cpp
@@ -1,5 +1,13 @@
 #include "Time.hpp"
 
+#include <iomanip>
+#include <sstream>
+#include <chrono>
+
+//#include <boost/date_time/local_time/local_time.hpp>
+//#include <boost/chrono.hpp>
+
+
 #ifdef WIN32
 	#define WIN32_LEAN_AND_MEAN
 	#include <windows.h>
@@ -9,11 +17,31 @@
 namespace Slic3r {
 namespace Utils {
 
+namespace  {
+
+// FIXME: after we switch to gcc > 4.9 on the build server, please remove me
+#if defined(__GNUC__) && __GNUC__ <= 4
+std::string put_time(const std::tm *tm, const char *fmt)
+{
+    static const constexpr int MAX_CHARS = 200;
+    char out[MAX_CHARS];
+    std::strftime(out, MAX_CHARS, fmt, tm);
+    return out;
+}
+#else
+auto put_time(const std::tm *tm, const char *fmt) -> decltype (std::put_time(tm, fmt))
+{
+    return std::put_time(tm, fmt);
+}
+#endif
+
+}
+
 time_t parse_time_ISO8601Z(const std::string &sdate)
 {
 	int y, M, d, h, m, s;
 	if (sscanf(sdate.c_str(), "%04d%02d%02dT%02d%02d%02dZ", &y, &M, &d, &h, &m, &s) != 6)
-        return (time_t)-1;
+        return time_t(-1);
 	struct tm tms;
     tms.tm_year = y - 1900;  // Year since 1900
 	tms.tm_mon  = M - 1;     // 0-11
@@ -62,24 +90,28 @@ std::string format_local_date_time(time_t time)
 }
 
 time_t get_current_time_utc()
+{    
+    using clk = std::chrono::system_clock;
+    return clk::to_time_t(clk::now());
+}
+
+static std::string tm2str(const std::tm *tm, const char *fmt)
 {
-#ifdef WIN32
-	SYSTEMTIME st;
-	// Retrieves the current system date and time. The system time is expressed in Coordinated Universal Time (UTC).
-	::GetSystemTime(&st);
-	std::tm tm;
-	tm.tm_sec   = st.wSecond;
-	tm.tm_min   = st.wMinute;
-	tm.tm_hour  = st.wHour;
-	tm.tm_mday  = st.wDay;
-	tm.tm_mon   = st.wMonth - 1;
-	tm.tm_year  = st.wYear - 1900;
-	tm.tm_isdst = -1;
-	return _mkgmtime(&tm);
-#else
-	// time() returns the time as the number of seconds since the Epoch, 1970-01-01 00:00:00 +0000 (UTC).
-	return time(nullptr);
-#endif
+    std::stringstream ss;
+    ss << put_time(tm, fmt);
+    return ss.str();
+}
+
+std::string time2str(const time_t &t, TimeZone zone, const char *fmt)
+{
+    std::string ret;
+    
+    switch (zone) {
+    case TimeZone::local: ret = tm2str(std::localtime(&t), fmt); break;
+    case TimeZone::utc:   ret = tm2str(std::gmtime(&t), fmt) + " UTC"; break;
+    }
+    
+    return ret;
 }
 
 }; // namespace Utils
diff --git a/src/libslic3r/Time.hpp b/src/libslic3r/Time.hpp
new file mode 100644
index 000000000..b314e47f7
--- /dev/null
+++ b/src/libslic3r/Time.hpp
@@ -0,0 +1,47 @@
+#ifndef slic3r_Utils_Time_hpp_
+#define slic3r_Utils_Time_hpp_
+
+#include <string>
+#include <ctime>
+
+namespace Slic3r {
+namespace Utils {
+
+// Utilities to convert an UTC time_t to/from an ISO8601 time format,
+// useful for putting timestamps into file and directory names.
+// Returns (time_t)-1 on error.
+time_t parse_time_ISO8601Z(const std::string &s);
+std::string format_time_ISO8601Z(time_t time);
+
+// Format the date and time from an UTC time according to the active locales and a local time zone.
+// TODO: make sure time2str is a suitable replacement
+std::string format_local_date_time(time_t time);
+
+// There is no gmtime() on windows.
+time_t get_current_time_utc();
+
+const constexpr char *const SLIC3R_TIME_FMT = "%Y-%m-%d at %T";
+
+enum class TimeZone { local, utc };
+
+std::string time2str(const time_t &t, TimeZone zone, const char *fmt = SLIC3R_TIME_FMT);
+
+inline std::string current_time2str(TimeZone zone, const char *fmt = SLIC3R_TIME_FMT)
+{
+    return time2str(get_current_time_utc(), zone, fmt);
+}
+
+inline std::string current_local_time2str(const char * fmt = SLIC3R_TIME_FMT)
+{
+    return current_time2str(TimeZone::local, fmt);    
+}
+
+inline std::string current_utc_time2str(const char * fmt = SLIC3R_TIME_FMT)
+{
+    return current_time2str(TimeZone::utc, fmt);
+}
+
+}; // namespace Utils
+}; // namespace Slic3r
+
+#endif /* slic3r_Utils_Time_hpp_ */
diff --git a/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp
index 2b1fdb241..5d847573d 100644
--- a/src/libslic3r/Utils.hpp
+++ b/src/libslic3r/Utils.hpp
@@ -87,11 +87,9 @@ namespace PerlUtils {
 
 std::string string_printf(const char *format, ...);
 
-// Timestamp formatted for header_slic3r_generated().
-extern std::string timestamp_str();
 // Standard "generated by Slic3r version xxx timestamp xxx" header string, 
 // to be placed at the top of Slic3r generated files.
-inline std::string header_slic3r_generated() { return std::string("generated by " SLIC3R_APP_NAME " " SLIC3R_VERSION " " ) + timestamp_str(); }
+std::string header_slic3r_generated();
 
 // getpid platform wrapper
 extern unsigned get_current_pid();
diff --git a/src/libslic3r/utils.cpp b/src/libslic3r/utils.cpp
index e26ed3839..895efdb4d 100644
--- a/src/libslic3r/utils.cpp
+++ b/src/libslic3r/utils.cpp
@@ -6,6 +6,8 @@
 #include <cstdarg>
 #include <stdio.h>
 
+#include "Time.hpp"
+
 #ifdef WIN32
 	#include <windows.h>
 	#include <psapi.h>
@@ -29,7 +31,6 @@
 #include <boost/locale.hpp>
 
 #include <boost/algorithm/string/predicate.hpp>
-#include <boost/date_time/local_time/local_time.hpp>
 #include <boost/filesystem.hpp>
 #include <boost/filesystem/path.hpp>
 #include <boost/nowide/fstream.hpp>
@@ -540,16 +541,9 @@ std::string string_printf(const char *format, ...)
     return res;
 }
 
-
-std::string timestamp_str()
+std::string header_slic3r_generated()
 {
-    const auto now = boost::posix_time::second_clock::local_time();
-    char buf[2048];
-    sprintf(buf, "on %04d-%02d-%02d at %02d:%02d:%02d",
-        // Local date in an ANSII format.
-        int(now.date().year()), int(now.date().month()), int(now.date().day()),
-        int(now.time_of_day().hours()), int(now.time_of_day().minutes()), int(now.time_of_day().seconds()));
-    return buf;
+    return std::string("generated by " SLIC3R_APP_NAME " " SLIC3R_VERSION " on " ) + Utils::current_utc_time2str();
 }
 
 unsigned get_current_pid()
diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt
index e51415d53..161e1a1ff 100644
--- a/src/slic3r/CMakeLists.txt
+++ b/src/slic3r/CMakeLists.txt
@@ -148,8 +148,6 @@ set(SLIC3R_GUI_SOURCES
     Utils/Bonjour.hpp
     Utils/PresetUpdater.cpp
     Utils/PresetUpdater.hpp
-    Utils/Time.cpp
-    Utils/Time.hpp
     Utils/UndoRedo.cpp
     Utils/UndoRedo.hpp
     Utils/HexFile.cpp
diff --git a/src/slic3r/Config/Snapshot.cpp b/src/slic3r/Config/Snapshot.cpp
index 3757ec25b..8fd64bb2a 100644
--- a/src/slic3r/Config/Snapshot.cpp
+++ b/src/slic3r/Config/Snapshot.cpp
@@ -1,7 +1,6 @@
 #include "Snapshot.hpp"
 #include "../GUI/AppConfig.hpp"
 #include "../GUI/PresetBundle.hpp"
-#include "../Utils/Time.hpp"
 
 #include <time.h>
 
@@ -13,6 +12,7 @@
 #include <boost/property_tree/ptree.hpp>
 
 #include "libslic3r/libslic3r.h"
+#include "libslic3r/Time.hpp"
 #include "libslic3r/Config.hpp"
 #include "libslic3r/FileParserError.hpp"
 #include "libslic3r/Utils.hpp"
diff --git a/src/slic3r/GUI/ConfigSnapshotDialog.cpp b/src/slic3r/GUI/ConfigSnapshotDialog.cpp
index 59ed38412..836a0a4d3 100644
--- a/src/slic3r/GUI/ConfigSnapshotDialog.cpp
+++ b/src/slic3r/GUI/ConfigSnapshotDialog.cpp
@@ -2,9 +2,9 @@
 #include "I18N.hpp"
 
 #include "../Config/Snapshot.hpp"
-#include "../Utils/Time.hpp"
 
 #include "libslic3r/Utils.hpp"
+#include "libslic3r/Time.hpp"
 #include "GUI_App.hpp"
 #include "wxExtensions.hpp"
 
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index d6e93b584..ef6a7d3c5 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -1134,7 +1134,8 @@ void Sidebar::show_sliced_info_sizer(const bool show)
             p->sliced_info->SetTextAndShow(siMateril_unit, info_text, new_label);
 
             p->sliced_info->SetTextAndShow(siCost, "N/A"/*wxString::Format("%.2f", ps.total_cost)*/);
-            p->sliced_info->SetTextAndShow(siEstimatedTime, ps.estimated_print_time, _(L("Estimated printing time")) + " :");
+            wxString t_est = std::isnan(ps.estimated_print_time) ? "N/A" : get_time_dhms(float(ps.estimated_print_time));
+            p->sliced_info->SetTextAndShow(siEstimatedTime, t_est, _(L("Estimated printing time")) + " :");
 
             // Hide non-SLA sliced info parameters
             p->sliced_info->SetTextAndShow(siFilament_m, "N/A");
diff --git a/src/slic3r/Utils/Time.hpp b/src/slic3r/Utils/Time.hpp
deleted file mode 100644
index 6a1aefa18..000000000
--- a/src/slic3r/Utils/Time.hpp
+++ /dev/null
@@ -1,25 +0,0 @@
-#ifndef slic3r_Utils_Time_hpp_
-#define slic3r_Utils_Time_hpp_
-
-#include <string>
-#include <ctime>
-
-namespace Slic3r {
-namespace Utils {
-
-// Utilities to convert an UTC time_t to/from an ISO8601 time format,
-// useful for putting timestamps into file and directory names.
-// Returns (time_t)-1 on error.
-extern time_t parse_time_ISO8601Z(const std::string &s);
-extern std::string format_time_ISO8601Z(time_t time);
-
-// Format the date and time from an UTC time according to the active locales and a local time zone.
-extern std::string format_local_date_time(time_t time);
-
-// There is no gmtime() on windows.
-extern time_t get_current_time_utc();
-
-}; // namespace Utils
-}; // namespace Slic3r
-
-#endif /* slic3r_Utils_Time_hpp_ */