source: filezilla/trunk/fuentes/src/engine/timeex.cpp @ 130

Last change on this file since 130 was 130, checked in by jrpelegrina, 4 years ago

First release to xenial

File size: 12.4 KB
Line 
1#include <filezilla.h>
2#include "timeex.h"
3
4#ifndef __WXMSW__
5#include <sys/time.h>
6#endif
7
8#define TIME_ASSERT(x) //wxASSERT(x)
9
10CDateTime::CDateTime(Zone z, int year, int month, int day, int hour, int minute, int second, int millisecond)
11{
12        Set(z, year, month, day, hour, minute, second, millisecond);
13}
14
15CDateTime::CDateTime(time_t t, Accuracy a)
16        : t_(static_cast<int64_t>(t) * 1000)
17        , a_(a)
18{
19        TIME_ASSERT(IsClamped());
20        TIME_ASSERT(a != milliseconds);
21}
22
23namespace {
24void skip(wxChar const*& it, const wxChar* const end)
25{
26        while (it != end && (*it < '0' || *it > '9')) {
27                ++it;
28        }
29}
30
31template<typename T>
32bool parse(wxChar const*& it, wxChar const* end, int count, T & v, int offset)
33{
34        skip(it, end);
35
36        if (end - it < count) {
37                return false;
38        }
39
40        T w = 0;
41
42        wxChar const* const stop = it + count;
43        while (it != stop) {
44                if (*it < '0' || *it > '9') {
45                        return false;
46                }
47                w *= 10;
48                w += *it - '0';
49                ++it;
50        }
51
52        w += offset;
53
54        v = w;
55        return true;
56}
57}
58
59CDateTime::CDateTime(wxString const& str, Zone z)
60{
61        Set(str, z);
62}
63
64CDateTime CDateTime::Now()
65{
66#ifdef __WXMSW__
67        FILETIME ft{};
68        GetSystemTimeAsFileTime(&ft);
69        return CDateTime(ft, milliseconds);
70#else
71        CDateTime ret;
72        timeval tv = { 0, 0 };
73        if (gettimeofday(&tv, 0) == 0) {
74                ret.t_ = static_cast<int64_t>(tv.tv_sec) * 1000 + tv.tv_usec / 1000;
75                ret.a_ = milliseconds;
76        }
77        return ret;
78#endif
79}
80
81bool CDateTime::operator<(CDateTime const& op) const
82{
83        if (t_ == invalid) {
84                return op.t_ != invalid;
85        }
86        else if (op.t_ == invalid) {
87                return false;
88        }
89
90        if (t_ < op.t_) {
91                return true;
92        }
93        if (t_ > op.t_) {
94                return false;
95        }
96
97        return a_ < op.a_;
98}
99
100bool CDateTime::operator<=(CDateTime const& op) const
101{
102        if (t_ == invalid) {
103                return true;
104        }
105        else if (op.t_ == invalid) {
106                return false;
107        }
108
109        if (t_ < op.t_) {
110                return true;
111        }
112        if (t_ > op.t_) {
113                return false;
114        }
115
116        return a_ <= op.a_;
117}
118
119bool CDateTime::operator==(CDateTime const& op) const
120{
121        return t_ == op.t_ && a_ == op.a_;
122}
123
124bool CDateTime::IsClamped()
125{
126        bool ret = true;
127        tm t = GetTm(utc);
128        if (a_ < milliseconds && GetMilliseconds() != 0) {
129                ret = false;
130        }
131        else if (a_ < seconds && t.tm_sec) {
132                ret = false;
133        }
134        else if (a_ < minutes && t.tm_min) {
135                ret = false;
136        }
137        else if (a_ < hours && t.tm_hour) {
138                ret = false;
139        }
140        return ret;
141}
142
143int CDateTime::Compare(CDateTime const& op) const
144{
145        if (t_ == invalid) {
146                return (op.t_ == invalid) ? 0 : -1;
147        }
148        else if (op.t_ == invalid) {
149                return 1;
150        }
151
152        if (a_ == op.a_) {
153                // First fast path: Same accuracy
154                int ret = 0;
155                if (t_ < op.t_) {
156                        ret = -1;
157                }
158                else if (t_ > op.t_) {
159                        ret = 1;
160                }
161                TIME_ASSERT(CompareSlow(op) == ret);
162                return ret;
163        }
164
165        // Second fast path: Lots of difference, at least 2 days
166        int64_t diff = t_ - op.t_;
167        if (diff > 60 * 60 * 24 * 1000 * 2) {
168                TIME_ASSERT(CompareSlow(op) == 1);
169                return 1;
170        }
171        else if (diff < -60 * 60 * 24 * 1000 * 2) {
172                TIME_ASSERT(CompareSlow(op) == -1);
173                return -1;
174        }
175
176        return CompareSlow(op);
177}
178
179int CDateTime::CompareSlow(CDateTime const& op) const
180{
181        tm t1 = GetTm(utc);
182        tm t2 = op.GetTm(utc);
183        if (t1.tm_year < t2.tm_year) {
184                return -1;
185        }
186        else if (t1.tm_year > t2.tm_year) {
187                return 1;
188        }
189        if (t1.tm_mon < t2.tm_mon) {
190                return -1;
191        }
192        else if (t1.tm_mon > t2.tm_mon) {
193                return 1;
194        }
195        if (t1.tm_mday < t2.tm_mday) {
196                return -1;
197        }
198        else if (t1.tm_mday > t2.tm_mday) {
199                return 1;
200        }
201
202        Accuracy a = (a_ < op.a_ ) ? a_ : op.a_;
203
204        if (a < hours) {
205                return 0;
206        }
207        if (t1.tm_hour < t2.tm_hour) {
208                return -1;
209        }
210        else if (t1.tm_hour > t2.tm_hour) {
211                return 1;
212        }
213
214        if (a < minutes) {
215                return 0;
216        }
217        if (t1.tm_min < t2.tm_min) {
218                return -1;
219        }
220        else if (t1.tm_min > t2.tm_min) {
221                return 1;
222        }
223
224        if (a < seconds) {
225                return 0;
226        }
227        if (t1.tm_sec < t2.tm_sec) {
228                return -1;
229        }
230        else if (t1.tm_sec > t2.tm_sec) {
231                return 1;
232        }
233
234        if (a < milliseconds) {
235                return 0;
236        }
237        auto ms1 = GetMilliseconds();
238        auto ms2 = op.GetMilliseconds();
239        if (ms1 < ms2) {
240                return -1;
241        }
242        else if (ms1 > ms2) {
243                return 1;
244        }
245
246        return 0;
247}
248
249CDateTime& CDateTime::operator+=(duration const& op)
250{
251        if (IsValid()) {
252                if (a_ < hours) {
253                        t_ += op.get_days() * 24 * 3600 * 1000;
254                }
255                else if (a_ < minutes) {
256                        t_ += op.get_hours() * 3600 * 1000;
257                }
258                else if (a_ < seconds) {
259                        t_ += op.get_minutes() * 60 * 1000;
260                }
261                else if (a_ < milliseconds) {
262                        t_ += op.get_seconds() * 1000;
263                }
264                else {
265                        t_ += op.get_milliseconds();
266                }
267        }
268        return *this;
269}
270
271CDateTime& CDateTime::operator-=(duration const& op)
272{
273        *this += -op;
274        return *this;
275}
276
277bool CDateTime::Set(Zone z, int year, int month, int day, int hour, int minute, int second, int millisecond)
278{
279        Accuracy a;
280        if (hour == -1) {
281                a = days;
282                TIME_ASSERT(minute == -1);
283                TIME_ASSERT(second == -1);
284                TIME_ASSERT(millisecond == -1);
285                hour = minute = second = millisecond = 0;
286        }
287        else if (minute == -1) {
288                a = hours;
289                TIME_ASSERT(second == -1);
290                TIME_ASSERT(millisecond == -1);
291                minute = second = millisecond = 0;
292        }
293        else if (second == -1) {
294                a = minutes;
295                TIME_ASSERT(millisecond == -1);
296                second = millisecond = 0;
297        }
298        else if (millisecond == -1) {
299                a = seconds;
300                millisecond = 0;
301        }
302        else {
303                a = milliseconds;
304        }
305
306#ifdef __WXMSW__
307        SYSTEMTIME st{};
308        st.wYear = year;
309        st.wMonth = month;
310        st.wDay = day;
311        st.wHour = hour;
312        st.wMinute = minute;
313        st.wSecond = second;
314        st.wMilliseconds = millisecond;
315
316        return Set(st, a, z);
317#else
318
319        tm t{};
320        t.tm_isdst = -1;
321        t.tm_year = year - 1900;
322        t.tm_mon = month - 1;
323        t.tm_mday = day;
324        t.tm_hour = hour;
325        t.tm_min = minute;
326        t.tm_sec = second;
327
328        bool set = Set(t, a, z);
329
330        if (set) {
331                t_ += millisecond;
332        }
333
334        return set;
335#endif
336}
337
338bool CDateTime::Set(wxString const& str, Zone z)
339{
340        wxChar const* it = str.c_str();
341        wxChar const* end = it + str.size();
342
343#ifdef __WXMSW__
344        SYSTEMTIME st{};
345        if (!parse(it, end, 4, st.wYear, 0) ||
346                !parse(it, end, 2, st.wMonth, 0) ||
347                !parse(it, end, 2, st.wDay, 0))
348        {
349                clear();
350                return false;
351        }
352
353        Accuracy a = days;
354        if (parse(it, end, 2, st.wHour, 0)) {
355                a = hours;
356                if (parse(it, end, 2, st.wMinute, 0)) {
357                        a = minutes;
358                        if (parse(it, end, 2, st.wSecond, 0)) {
359                                a = seconds;
360                                if (parse(it, end, 3, st.wMilliseconds, 0)) {
361                                        a = milliseconds;
362                                }
363                        }
364                }
365        }
366        return Set(st, a, z);
367#else
368        tm t{};
369        if (!parse(it, end, 4, t.tm_year, -1900) ||
370                !parse(it, end, 2, t.tm_mon, -1) ||
371                !parse(it, end, 2, t.tm_mday, 0))
372        {
373                clear();
374                return false;
375        }
376
377        Accuracy a = days;
378        int64_t ms{};
379        if (parse(it, end, 2, t.tm_hour, 0)) {
380                a = hours;
381                if (parse(it, end, 2, t.tm_min, 0)) {
382                        a = minutes;
383                        if (parse(it, end, 2, t.tm_sec, 0)) {
384                                a = seconds;
385                                if (parse(it, end, 3, ms, 0)) {
386                                        a = milliseconds;
387                                }
388                        }
389                }
390        }
391        bool set = Set(t, a, z);
392        if (set) {
393                t_ += ms;
394        }
395        return set;
396#endif
397}
398
399#ifdef __WXMSW__
400
401bool CDateTime::Set(SYSTEMTIME const& st, Accuracy a, Zone z)
402{
403        clear();
404
405        FILETIME ft{};
406        if (a >= hours && z == local) {
407                SYSTEMTIME st2{};
408                if (!TzSpecificLocalTimeToSystemTime(0, &st, &st2)) {
409                        return false;
410                }
411                if (!SystemTimeToFileTime(&st2, &ft)) {
412                        return false;
413                }
414        }
415        else if (!SystemTimeToFileTime(&st, &ft)) {
416                return false;
417        }
418        return Set(ft, a);
419}
420
421namespace {
422template<typename T>
423int64_t make_int64_t(T hi, T lo)
424{
425        return (static_cast<int64_t>(hi) << 32) + static_cast<int64_t>(lo);
426}
427
428// This is the offset between FILETIME epoch in 100ns and the Unix epoch in ms.
429int64_t const EPOCH_OFFSET_IN_MSEC = 11644473600000ll;
430}
431
432bool CDateTime::Set(FILETIME const& ft, Accuracy a)
433{
434        if (ft.dwHighDateTime || ft.dwLowDateTime) {
435                // See http://trac.wxwidgets.org/changeset/74423 and http://trac.wxwidgets.org/ticket/13098
436                // Directly converting to time_t
437
438                int64_t t = make_int64_t(ft.dwHighDateTime, ft.dwLowDateTime);
439                t /= 10000; // Convert hundreds of nanoseconds to milliseconds.
440                t -= EPOCH_OFFSET_IN_MSEC;
441                if (t != invalid) {
442                        t_ = t;
443                        a_ = a;
444                        TIME_ASSERT(IsClamped());
445                        return true;
446                }
447        }
448        clear();
449        return false;
450}
451
452#else
453
454bool CDateTime::Set(tm& t, Accuracy a, Zone z)
455{
456        time_t tt;
457
458        errno = 0;
459
460        if (a >= hours && z == local) {
461                 tt = mktime(&t);
462        }
463        else {
464                tt = timegm(&t);
465        }
466
467        if (tt != time_t(-1) || !errno) {
468                t_ = static_cast<int64_t>(tt) * 1000;
469                a_ = a;
470
471                TIME_ASSERT(IsClamped());
472
473                return true;
474        }
475
476        clear();
477        return false;
478}
479
480#endif
481
482bool CDateTime::ImbueTime(int hour, int minute, int second, int millisecond)
483{
484        if (!IsValid() || a_ > days) {
485                return false;
486        }
487
488        if (second == -1) {
489                a_ = minutes;
490                TIME_ASSERT(millisecond == -1);
491                second = millisecond = 0;
492        }
493        else if (millisecond == -1) {
494                a_ = seconds;
495                millisecond = 0;
496        }
497        else {
498                a_ = milliseconds;
499        }
500
501        if (hour < 0 || hour >= 24) {
502                return false;
503        }
504        if (minute < 0 || minute >= 60) {
505                return false;
506        }
507        if (second < 0 || second >= 60) {
508                return false;
509        }
510        if (millisecond < 0 || millisecond >= 1000) {
511                return false;
512        }
513
514        t_ += (hour * 3600 + minute * 60 + second) * 1000 + millisecond;
515        return true;
516}
517
518bool CDateTime::IsValid() const
519{
520        return t_ != invalid;
521}
522
523void CDateTime::clear()
524{
525        a_ = days;
526        t_ = invalid;
527}
528
529#ifdef __VISUALC__
530
531#include <mutex.h>
532
533namespace {
534
535// Sadly wcsftime has shitty error handling, instead of returning 0 and setting errrno, it invokes some crt debug machinary.
536// Fortunately we don't build the official FZ binaries with Visual Studio.
537extern "C" void NullInvalidParameterHandler(const wchar_t*, const wchar_t*, const wchar_t*, unsigned int, uintptr_t)
538{
539}
540
541struct CrtAssertSuppressor
542{
543        CrtAssertSuppressor()
544        {
545                scoped_lock l(m_);
546
547                if (!refs_++) {
548                        oldError = _CrtSetReportMode(_CRT_ERROR, 0);
549                        oldAssert = _CrtSetReportMode(_CRT_ASSERT, 0);
550                        oldHandler = _set_invalid_parameter_handler(NullInvalidParameterHandler);
551                }
552        }
553
554        ~CrtAssertSuppressor()
555        {
556                scoped_lock l(m_);
557
558                if (!--refs_) {
559                        _set_invalid_parameter_handler(oldHandler);
560                        _CrtSetReportMode(_CRT_ASSERT, oldAssert);
561                        _CrtSetReportMode(_CRT_ERROR, oldError);
562                }
563        }
564
565        static int oldError;
566        static int oldAssert;
567        static _invalid_parameter_handler oldHandler;
568
569        static mutex m_;
570        static int refs_;
571};
572
573int CrtAssertSuppressor::oldError{};
574int CrtAssertSuppressor::oldAssert{};
575_invalid_parameter_handler CrtAssertSuppressor::oldHandler{};
576
577mutex CrtAssertSuppressor::m_{};
578int CrtAssertSuppressor::refs_{};
579
580}
581#endif
582
583bool CDateTime::VerifyFormat(wxString const& fmt)
584{
585        wxChar buf[4096];
586        tm t = CDateTime::Now().GetTm(utc);
587
588#ifdef __VISUALC__
589        CrtAssertSuppressor suppressor;
590#endif
591
592        return wxStrftime(buf, sizeof(buf)/sizeof(wxChar), fmt, &t) != 0;
593}
594
595duration operator-(CDateTime const& a, CDateTime const& b)
596{
597        TIME_ASSERT(a.IsValid());
598        TIME_ASSERT(b.IsValid());
599
600        return duration::from_milliseconds(a.t_ - b.t_);
601}
602
603wxString CDateTime::Format(wxString const& fmt, Zone z) const
604{
605        tm t = GetTm(z);
606
607#ifdef __WXMSW__
608        int const count = 1000;
609        wxChar buf[count];
610
611#ifdef __VISUALC__
612        CrtAssertSuppressor suppressor;
613#endif
614        wcsftime(buf, count - 1, fmt, &t);
615        buf[count - 1] = 0;
616        return buf;
617#else
618
619        auto fbuf = fmt.mb_str();
620        if (!fbuf || !*fbuf) {
621                return wxString();
622        }
623
624        int const count = 1000;
625        char buf[count];
626        strftime(buf, count -1, fbuf, &t);
627        buf[count - 1] = 0;
628
629        return buf;
630#endif
631}
632
633time_t CDateTime::GetTimeT() const
634{
635        return t_ / 1000;
636}
637
638tm CDateTime::GetTm(Zone z) const
639{
640        tm ret{};
641        time_t t = GetTimeT();
642#ifdef __WXMSW__
643        // gmtime_s/localtime_s don't work with negative times
644        if (t < 86400) {
645                FILETIME ft = GetFileTime();
646                SYSTEMTIME st;
647                if (FileTimeToSystemTime(&ft, &st)) {
648
649                        if (a_ >= hours && z == local) {
650                                SYSTEMTIME st2;
651                                if (SystemTimeToTzSpecificLocalTime(0, &st, &st2)) {
652                                        st = st2;
653                                }
654                        }
655
656                        ret.tm_year = st.wYear - 1900;
657                        ret.tm_mon = st.wMonth - 1;
658                        ret.tm_mday = st.wDay;
659                        ret.tm_wday = st.wDayOfWeek;
660                        ret.tm_hour = st.wHour;
661                        ret.tm_min = st.wMinute;
662                        ret.tm_sec = st.wSecond;
663                        ret.tm_yday = -1;
664                }
665        }
666        else {
667                // Special case: If having only days, don't perform conversion
668                if (z == utc || a_ == days) {
669                        gmtime_s(&ret, &t);
670                }
671                else {
672                        localtime_s(&ret, &t);
673                }
674        }
675#else
676        if (z == utc || a_ == days) {
677                gmtime_r(&t, &ret);
678        }
679        else {
680                localtime_r(&t, &ret);
681        }
682#endif
683        return ret;
684}
685
686#ifdef __WXMSW__
687
688CDateTime::CDateTime(FILETIME const& ft, Accuracy a)
689{
690        Set(ft, a);
691}
692
693FILETIME CDateTime::GetFileTime() const
694{
695        FILETIME ret{};
696        if (IsValid()) {
697                int64_t t = t_;
698
699                t += EPOCH_OFFSET_IN_MSEC;
700                t *= 10000;
701
702                ret.dwHighDateTime = t >> 32;
703                ret.dwLowDateTime = t & 0xffffffffll;
704        }
705
706        return ret;
707}
708
709#endif
710
711struct foo final
712{
713        foo() noexcept = default;
714        explicit foo(int, float) {}
715        foo(foo const&) noexcept = default;
716        foo(foo &&) noexcept = default;
717
718        foo& operator=(foo const&) noexcept = default;
719        foo& operator=(foo &&) noexcept = default;
720};
721
722foo n() {
723        return foo();
724}
725
726void m()
727{
728        foo r, r2;
729        r = r2;
730        r = n();
731}
Note: See TracBrowser for help on using the repository browser.