1 /*
2  datetime_utils.cpp     MindForger thinking notebook
3 
4  Copyright (C) 2016-2020 Martin Dvorak <martin.dvorak@mindforger.com>
5 
6  This program is free software; you can redistribute it and/or
7  modify it under the terms of the GNU General Public License
8  as published by the Free Software Foundation; either version 2
9  of the License, or (at your option) any later version.
10 
11  This program is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with this program. If not, see <http://www.gnu.org/licenses/>.
18  */
19 #include "datetime_utils.h"
20 
21 using namespace std;
22 
23 namespace m8r {
24 
datetimeNow()25 time_t datetimeNow()
26 {
27     return time(nullptr);
28 }
29 
30 /**
31  * @brief Convert character string to datetime.
32  * @param datetime    (struct tm*)calloc(1, sizeof(struct tm)) is expected for result creation.
33  */
datetimeFrom(const char * s,struct tm * datetime)34 struct tm* datetimeFrom(const char* s, struct tm* datetime)
35 {
36     strptime(s, "%Y-%m-%d %H:%M:%S", datetime);
37     return datetime;
38 }
39 
40 /**
41  * @brief Convert datetime to character string.
42  * @param result    char[50] or bigger is expected for result serialization.
43  */
datetimeTo(const struct tm * datetime,char * result)44 char* datetimeTo(const struct tm* datetime, char* result)
45 {
46     if(datetime->tm_isdst) {
47 #ifndef _WIN32
48         tm c {0,0,0,0,0,0,0,0,0,0,0}; // missing initializer required by older GCC versions 4.8.5 and older
49 #else
50         tm c {0,0,0,0,0,0,0,0,0};
51 #endif
52         memcpy(&c, datetime, sizeof(c));
53         if(c.tm_hour) {
54             c.tm_hour=datetime->tm_hour-1;
55         } else {
56             // time travel back in time may have impact on y/m/... > roundtrip required
57             time_t backOneHour = mktime(&c) - 60*60;
58 #ifndef _WIN32
59             localtime_r(&backOneHour, &c);
60 #else
61             localtime_s(&c, &backOneHour);
62 #endif
63 
64         }
65 
66         strftime(result, sizeof(result)*100, "%Y-%m-%d %H:%M:%S", &c);
67     } else {
68         strftime(result, sizeof(result)*100, "%Y-%m-%d %H:%M:%S", datetime);
69     }
70     return result;
71 }
72 
datetimeToString(const time_t ts)73 std::string datetimeToString(const time_t ts)
74 {
75     char to[50];
76     datetimeTo(localtime(&ts), to);
77     return string{to};
78 }
79 
datetimeSeconds(struct tm * datetime)80 time_t datetimeSeconds(struct tm* datetime)
81 {
82     return mktime(datetime);
83 }
84 
85 enum class Pretty
86 {
87     TODAY,
88     THIS_WEEK,
89     THIS_YEAR,
90     LONG_TIME_AGO
91 };
92 
datetimeToPrettyHtml(const time_t ts)93 std::string datetimeToPrettyHtml(const time_t ts)
94 {
95     return datetimeToPrettyHtml(&ts);
96 }
97 
98 constexpr time_t SIX_DAYS = 60 * 60 * 24 * 6;
datetimeToPrettyHtml(const time_t * seconds)99 std::string datetimeToPrettyHtml(const time_t* seconds)
100 {
101     time_t now;
102     time(&now);
103 
104     tm tsS = *localtime(seconds);
105     tm* nowS = localtime(&now);
106 
107     Pretty pretty = Pretty::LONG_TIME_AGO;
108 
109     if(tsS.tm_year==nowS->tm_year) {
110         if(tsS.tm_mon==nowS->tm_mon && tsS.tm_mday==nowS->tm_mday) {
111             pretty = Pretty::TODAY;
112         } else {
113             if(now-*seconds < SIX_DAYS && tsS.tm_wday<=nowS->tm_wday) {
114                 pretty = Pretty::THIS_WEEK;
115             } else {
116                 pretty = Pretty::THIS_YEAR;
117             }
118         }
119     } else {
120         if(now-*seconds < SIX_DAYS) {
121             pretty = Pretty::THIS_WEEK;
122         }
123     }
124 
125     const char *background;
126     char text[50];
127     switch(pretty) {
128     case Pretty::LONG_TIME_AGO:
129         background = "BBBBBB";
130         strftime(text, sizeof(text), "%Y", &tsS);
131         break;
132     case Pretty::THIS_YEAR:
133         background = "888888";
134         strftime(text, sizeof(text), "%b %e", &tsS);
135         break;
136     case Pretty::THIS_WEEK:
137         background= "555555";
138         strftime(text, sizeof(text), "%A", &tsS);
139         break;
140     // IMPROVE this month
141     case Pretty::TODAY:
142         background = "000000";
143         strftime(text, sizeof(text), "%R", &tsS);
144         break;
145     }
146 
147     // note that long timestamp is added to the HTML - it is convenient for parsing/sorting/processing
148     std::string result;
149     result =
150             "<div title='" +
151             to_stringl(*seconds) +
152             "' style='background-color: #" +
153             string(background) +
154             "; color: #ffffff; text-align: center; white-space: pre;'>" + "&nbsp;&nbsp;" +
155             string(text) +
156             "&nbsp;&nbsp;" + "</div>";
157     return result;
158 }
159 
160 } // m8r namespace
161 
162