aboutsummaryrefslogblamecommitdiffstats
path: root/util/datetime/base_ut.cpp
blob: 5ada0794b702ab3052c3df38990d511e96853a81 (plain) (tree)
1
2
3
4
5
6
7
8
9
                 
                                                  
 
                                 
                               
                             
                               
                               
                               
 
                  
 
                                     





                                                     






                          
     
  
 
           
                                                                    




                                     
                                                                                                                                                                      



                        
 
                                   










                                                              
                                






                                                                
                            










                                                              
                                    






























































                                                                        








                                                  
                                               















                                                                         
 







                                                    
                                       








                                            
 
                       
 


                                      
 



                                                                                             
 





                                         
 
          










                                                                                             






                                                                      
 


                                  
 

                                                     
 






                                         
 


                                 
 






                                                                          
 






                                                                           
 




                                
 







                                                                  
 


                                                           
             
                                                     
               
                       
 








                                                                                                      
            


                                                                      
      





                                                   
     
 
 
                                        


                                                                                  
                                        
                                        
                                                                                                        
     
                               



                                                                      














                                           
                                 
                               
                                                 
                                 
                     




                                  
 
                                                             













                                                                                              

         
                            




                                          
                                      
                                                                                                                                                   
                                                                                                                                                   
                                                                                                                                                          
     


                                                                                                                                                              
                                  






                                                                                                                     
                                   







                                                        



                                                                                                                      
                                                                                             
     
 









                                                                             
                          


                                                           
                                
                            




                                                                   
                                                          
                                                             
                                                           



                                                          
     
 
                                   

                                  
                                    
                                   
 
                                           
                                                                                                           
     
 
                                            





                                                           
                                           




                                                         











                                                                                     

                                                                                                               
         
















                                                                                                                    

                                                                   


































































































                                                                                                                            
 
#include "base.h"

#include <library/cpp/testing/unittest/registar.h>

#include <util/generic/utility.h>
#include <util/generic/ylimits.h>
#include <util/generic/ymath.h>
#include <util/string/cast.h>
#include <util/stream/output.h>
#include <util/system/compat.h>
#include <util/random/random.h>

#include <climits>

using namespace std::chrono_literals;

struct TTestTime {
    const time_t T_ = 987654321;
    const char* Date_ = "Thu Apr 19 04:25:21 2001\n";
    const char* SprintDate_ = "20010419";
    const long SprintSecs_ = 15921;
    struct tm Tm_;
    TTestTime() {
        Tm_.tm_sec = 21;
        Tm_.tm_min = 25;
        Tm_.tm_hour = 4;
        Tm_.tm_mday = 19;
        Tm_.tm_mon = 3;
        Tm_.tm_year = 101;
        Tm_.tm_wday = 4;
        Tm_.tm_yday = 108;
    }
};

namespace {
    inline void OldDate8601(char* buf, size_t bufLen, time_t when) {
        struct tm theTm;
        struct tm* ret = nullptr;

        ret = GmTimeR(&when, &theTm);

        if (ret) {
            snprintf(buf, bufLen, "%04d-%02d-%02dT%02d:%02d:%02dZ", theTm.tm_year + 1900, theTm.tm_mon + 1, theTm.tm_mday, theTm.tm_hour, theTm.tm_min, theTm.tm_sec);
        } else {
            *buf = '\0';
        }
    }
}

Y_UNIT_TEST_SUITE(TestSprintDate) {
    Y_UNIT_TEST(Year9999) {
        struct tm t;
        t.tm_year = 9999 - 1900;
        t.tm_mday = 1;
        t.tm_mon = 10;

        char buf[DATE_BUF_LEN];
        DateToString(buf, t);

        TString expectedDate = "99991101";

        UNIT_ASSERT_VALUES_EQUAL(expectedDate, ToString(buf));
    }
    Y_UNIT_TEST(YearAfter9999) {
        struct tm t;
        t.tm_year = 123456 - 1900;
        t.tm_mday = 1;
        t.tm_mon = 10;

        char buf[DATE_BUF_LEN];
        UNIT_ASSERT_EXCEPTION(DateToString(buf, t), yexception);
    }
    Y_UNIT_TEST(SmallYear) {
        struct tm t;
        t.tm_year = 0 - 1900;
        t.tm_mday = 1;
        t.tm_mon = 10;

        char buf[DATE_BUF_LEN];
        DateToString(buf, t);

        const TString expectedDate = TString("00001101");

        UNIT_ASSERT_VALUES_EQUAL(expectedDate, ToString(buf));
    }
    Y_UNIT_TEST(SmallYearAndMonth) {
        struct tm t;
        t.tm_year = 99 - 1900;
        t.tm_mday = 1;
        t.tm_mon = 0;

        char buf[DATE_BUF_LEN];
        DateToString(buf, t);

        const TString expectedDate = TString("00990101");

        UNIT_ASSERT_VALUES_EQUAL(expectedDate, ToString(buf));
    }
    Y_UNIT_TEST(FromZeroTimestamp) {
        const time_t timestamp = 0;

        char buf[DATE_BUF_LEN];
        DateToString(buf, timestamp);

        const TString expectedDate = TString("19700101");

        UNIT_ASSERT_VALUES_EQUAL(expectedDate, ToString(buf));
    }
    Y_UNIT_TEST(FromTimestamp) {
        const time_t timestamp = 1524817858;

        char buf[DATE_BUF_LEN];
        DateToString(buf, timestamp);

        const TString expectedDate = TString("20180427");

        UNIT_ASSERT_VALUES_EQUAL(expectedDate, ToString(buf));
    }
    Y_UNIT_TEST(FromTimestampAsTString) {
        const time_t timestamp = 1524817858;

        const TString expectedDate = TString("20180427");

        UNIT_ASSERT_VALUES_EQUAL(expectedDate, DateToString(timestamp));
    }
    Y_UNIT_TEST(YearToString) {
        struct tm t;
        t.tm_year = 99 - 1900;
        t.tm_mday = 1;
        t.tm_mon = 0;

        TString expectedYear = TString("0099");

        UNIT_ASSERT_VALUES_EQUAL(expectedYear, YearToString(t));
    }
    Y_UNIT_TEST(YearToStringBigYear) {
        struct tm t;
        t.tm_year = 123456 - 1900;
        t.tm_mday = 1;
        t.tm_mon = 0;

        UNIT_ASSERT_EXCEPTION(YearToString(t), yexception);
    }
    Y_UNIT_TEST(YearToStringAsTimestamp) {
        const time_t timestamp = 1524817858;

        const TString expectedYear = TString("2018");

        UNIT_ASSERT_VALUES_EQUAL(expectedYear, YearToString(timestamp));
    }
}

Y_UNIT_TEST_SUITE(TDateTimeTest) {
    Y_UNIT_TEST(Test8601) {
        char buf1[100];
        char buf2[100];

        for (size_t i = 0; i < 1000000; ++i) {
            const time_t t = RandomNumber<ui32>();

            OldDate8601(buf1, sizeof(buf1), t);
            sprint_date8601(buf2, t);

            UNIT_ASSERT_VALUES_EQUAL(TStringBuf(buf1), TStringBuf(buf2));
        }
    }

    inline bool CompareTM(const struct tm& a, const struct tm& b) {
        return (
            a.tm_sec == b.tm_sec &&
            a.tm_min == b.tm_min &&
            a.tm_hour == b.tm_hour &&
            a.tm_mday == b.tm_mday &&
            a.tm_mon == b.tm_mon &&
            a.tm_year == b.tm_year &&
            a.tm_wday == b.tm_wday &&
            a.tm_yday == b.tm_yday);
    }

    static inline TString Str(const struct tm& a) {
        return TStringBuilder() << "("
                                << a.tm_sec << ", "
                                << a.tm_min << ", "
                                << a.tm_hour << ", "
                                << a.tm_mday << ", "
                                << a.tm_mon << ", "
                                << a.tm_year << ", "
                                << a.tm_wday << ", "
#if !defined(_musl_) && !defined(_win_)
                                << a.tm_yday
#endif
                                << ")";
    }

    Y_UNIT_TEST(TestBasicFuncs) {
        ui64 mlsecB = millisec();
        ui64 mcrsecB = MicroSeconds();
        struct timeval tvB;
        gettimeofday(&tvB, nullptr);

        usleep(100000);

        ui64 mlsecA = millisec();
        ui64 mcrsecA = MicroSeconds();
        struct timeval tvA;
        gettimeofday(&tvA, nullptr);

        UNIT_ASSERT(mlsecB + 90 < mlsecA);
        UNIT_ASSERT((mcrsecB + 90000 < mcrsecA));
        //UNIT_ASSERT(ToMicroSeconds(&tvB) + 90000 < ToMicroSeconds(&tvA));
        //UNIT_ASSERT(TVdiff(tvB, tvA) == long(ToMicroSeconds(&tvA) - ToMicroSeconds(&tvB)));
    }

    Y_UNIT_TEST(TestCompatFuncs) {
        struct tm t;
        struct tm* tret = nullptr;
        TTestTime e;
        tret = gmtime_r(&e.T_, &t);
        UNIT_ASSERT(tret == &t);
        UNIT_ASSERT(CompareTM(e.Tm_, t));

        /*
         * strptime seems to be broken on Mac OS X:
         *
         *   struct tm t;
         *   char *ret = strptime("Jul", "%b ", &t);
         *   printf("-%s-\n", ret);
         *
         * yields "- -": ret contains a pointer to a substring of the format string,
         * that should never occur: function returns either NULL or pointer to buf substring.
         *
         * So this test fails on Mac OS X.
         */

        struct tm t2;
        Zero(t2);
        char* ret = strptime(e.Date_, "%a %b %d %H:%M:%S %Y\n ", &t2);
        UNIT_ASSERT(ret == e.Date_ + strlen(e.Date_));
        UNIT_ASSERT_VALUES_EQUAL(Str(e.Tm_), Str(t2));
        time_t t3 = timegm(&t);
        UNIT_ASSERT(t3 == e.T_);
    }

    Y_UNIT_TEST(TestSprintSscan) {
        char buf[256];
        long secs;
        TTestTime e;

        sprint_gm_date(buf, e.T_, &secs);
        UNIT_ASSERT(strcmp(buf, e.SprintDate_) == 0);
        UNIT_ASSERT(secs == e.SprintSecs_);

        struct tm t;
        Zero(t);
        bool ret = sscan_date(buf, t);
        UNIT_ASSERT(ret);
        UNIT_ASSERT(
            t.tm_year == e.Tm_.tm_year &&
            t.tm_mon == e.Tm_.tm_mon &&
            t.tm_mday == e.Tm_.tm_mday);

        buf[0] = 'a';
        ret = sscan_date(buf, t);
        UNIT_ASSERT(!ret);
    }

    Y_UNIT_TEST(TestNow) {
        i64 seconds = Seconds();
        i64 milliseconds = millisec();
        i64 microseconds = MicroSeconds();
        UNIT_ASSERT(Abs(seconds - milliseconds / 1000) <= 1);
        UNIT_ASSERT(Abs(milliseconds - microseconds / 1000) < 100);
        UNIT_ASSERT(seconds > 1243008607); // > time when test was written
    }

    Y_UNIT_TEST(TestStrftime) {
        struct tm tm;
        Zero(tm);
        tm.tm_year = 109;
        tm.tm_mon = 4;
        tm.tm_mday = 29;
        UNIT_ASSERT_STRINGS_EQUAL("2009-05-29", Strftime("%Y-%m-%d", &tm));
    }

    Y_UNIT_TEST(TestNanoSleep) {
        NanoSleep(0);
        NanoSleep(1);
        NanoSleep(1000);
        NanoSleep(1000000);
    }

    static bool TimeZoneEq(const char* zone0, const char* zone1) {
        if (strcmp(zone0, "GMT") == 0) {
            zone0 = "UTC";
        }
        if (strcmp(zone1, "GMT") == 0) {
            zone1 = "UTC";
        }
        return strcmp(zone0, zone1) == 0;
    }

    static bool CompareTMFull(const tm* t0, const tm* t1) {
        return t0 && t1 &&
               CompareTM(*t0, *t1) &&
               (t0->tm_isdst == t1->tm_isdst)
#ifndef _win_
               && (t0->tm_gmtoff == t1->tm_gmtoff) &&
               TimeZoneEq(t0->tm_zone, t1->tm_zone)
#endif // _win_
               && true;
    }

    Y_UNIT_TEST(TestGmTimeR) {
        time_t starttime = static_cast<time_t>(Max<i64>(-12244089600LL, Min<time_t>())); // 1-Jan-1582
        time_t finishtime = static_cast<time_t>(Min<i64>(0xFFFFFFFF * 20, Max<time_t>()));
        time_t step = (finishtime - starttime) / 25;
        struct tm tms0, tms1;
        struct tm* ptm0 = nullptr;
        struct tm* ptm1 = nullptr;
        for (time_t t = starttime; t < finishtime; t += step) {
            ptm0 = GmTimeR(&t, &tms0);
            UNIT_ASSERT_EQUAL(ptm0, &tms0);

#ifdef _win_
            if (tms0.tm_year + 1900 > 3000) {
                // Windows: _MAX__TIME64_T == 23:59:59. 12/31/3000 UTC
                continue;
            }
#endif

            ptm1 = gmtime_r(&t, &tms1);
            if (!ptm1) {
                continue;
            }
            UNIT_ASSERT_EQUAL(ptm1, &tms1);
            UNIT_ASSERT(CompareTMFull(ptm0, ptm1));
        }
    }
}

Y_UNIT_TEST_SUITE(DateTimeTest) {
    Y_UNIT_TEST(TestDurationFromFloat) {
        UNIT_ASSERT_EQUAL(TDuration::MilliSeconds(500), TDuration::Seconds(0.5));
        UNIT_ASSERT_EQUAL(TDuration::MilliSeconds(500), TDuration::Seconds(0.5f));
    }

    Y_UNIT_TEST(TestSecondsLargeValue) {
        unsigned int seconds = UINT_MAX;
        UNIT_ASSERT_VALUES_EQUAL(((ui64)seconds) * 1000000, TDuration::Seconds(seconds).MicroSeconds());
    }

    Y_UNIT_TEST(TestToString) {
#define CHECK_CONVERTIBLE(v)                                         \
    do {                                                             \
        UNIT_ASSERT_VALUES_EQUAL(v, ToString(TDuration::Parse(v)));  \
        UNIT_ASSERT_VALUES_EQUAL(v, TDuration::Parse(v).ToString()); \
    } while (0)
#if 0

        CHECK_CONVERTIBLE("10s");
        CHECK_CONVERTIBLE("1234s");
        CHECK_CONVERTIBLE("1234ms");
        CHECK_CONVERTIBLE("12ms");
        CHECK_CONVERTIBLE("12us");
        CHECK_CONVERTIBLE("1234us");
#endif

        CHECK_CONVERTIBLE("1.000000s");
        CHECK_CONVERTIBLE("11234.000000s");
        CHECK_CONVERTIBLE("0.011122s");
        CHECK_CONVERTIBLE("33.011122s");
    }

    Y_UNIT_TEST(TestFromString) {
        static const struct T {
            const char* const Str;
            const TDuration::TValue MicroSeconds;
            const bool Parseable;
        } tests[] = {
            {"0", 0, true},
            {"1", 1000000, true},
            {"2s", 2000000, true},
            {"3ms", 3000, true},
            {"x3ms", 0, false},
        };

        for (const T* t = tests; t != std::end(tests); ++t) {
            // FromString
            bool parsed = false;
            try {
                TDuration time = FromString<TDuration>(t->Str);
                parsed = true;
                UNIT_ASSERT_EQUAL(t->MicroSeconds, time.MicroSeconds());
            } catch (const yexception&) {
                UNIT_ASSERT_VALUES_EQUAL(parsed, t->Parseable);
            }
            // TryFromString
            TDuration tryTime;
            UNIT_ASSERT_VALUES_EQUAL(TryFromString<TDuration>(t->Str, tryTime), t->Parseable);
            if (t->Parseable) {
                UNIT_ASSERT_EQUAL(t->MicroSeconds, tryTime.MicroSeconds());
            }
        }
    }

    Y_UNIT_TEST(TestSleep) {
        // check does not throw
        Sleep(TDuration::Seconds(0));
        Sleep(TDuration::MicroSeconds(1));
        Sleep(TDuration::MilliSeconds(1));
    }

    Y_UNIT_TEST(TestInstantToString) {
        UNIT_ASSERT_VALUES_EQUAL(TString("2009-08-06T15:19:06.023455Z"), ToString(TInstant::Seconds(1249571946) + TDuration::MicroSeconds(23455)));
        UNIT_ASSERT_VALUES_EQUAL(TString("2022-08-23T13:04:43.023455Z"), ToString(TInstant::Seconds(1661259883) + TDuration::MicroSeconds(23455)));
        UNIT_ASSERT_VALUES_EQUAL(TString("2122-11-23T15:12:26.023455Z"), ToString(TInstant::Seconds(4824889946) + TDuration::MicroSeconds(23455)));
        UNIT_ASSERT_VALUES_EQUAL(TString("2009-08-06T15:19:06.023455Z"), (TInstant::Seconds(1249571946) + TDuration::MicroSeconds(23455)).ToString());
        UNIT_ASSERT_VALUES_EQUAL(TString("2009-08-06T15:19:06Z"), (TInstant::Seconds(1249571946) + TDuration::MicroSeconds(23455)).ToStringUpToSeconds());
    }

    Y_UNIT_TEST(TestInstantToRfc822String) {
        UNIT_ASSERT_VALUES_EQUAL(TString("Thu, 06 Aug 2009 15:19:06 GMT"), (TInstant::Seconds(1249571946) + TDuration::MicroSeconds(23455)).ToRfc822String());
    }

    Y_UNIT_TEST(TestInstantMath) {
        UNIT_ASSERT_VALUES_EQUAL(TInstant::Seconds(1719), TInstant::Seconds(1700) + TDuration::Seconds(19));
        // overflow
        UNIT_ASSERT_VALUES_EQUAL(TInstant::Max(), TInstant::Max() - TDuration::Seconds(10) + TDuration::Seconds(19));
        // underflow
        UNIT_ASSERT_VALUES_EQUAL(TInstant::Zero(), TInstant::Seconds(1000) - TDuration::Seconds(2000));
        UNIT_ASSERT_VALUES_EQUAL(TDuration::Zero(), TInstant::Seconds(1000) - TInstant::Seconds(2000));
    }

    Y_UNIT_TEST(TestDurationMath) {
        TDuration empty;
        UNIT_ASSERT(!empty);
        // ensure that this compiles too
        if (empty) {
            UNIT_ASSERT(false);
        }
        TDuration nonEmpty = TDuration::MicroSeconds(1);
        UNIT_ASSERT(nonEmpty);

        UNIT_ASSERT_VALUES_EQUAL(TDuration::Seconds(110), TDuration::Seconds(77) + TDuration::Seconds(33));
        // overflow
        UNIT_ASSERT_VALUES_EQUAL(TDuration::Max(), TDuration::Max() - TDuration::Seconds(1) + TDuration::Seconds(10));
        // underflow
        UNIT_ASSERT_VALUES_EQUAL(TDuration::Zero(), TDuration::Seconds(20) - TDuration::Seconds(200));
        // division
        UNIT_ASSERT_DOUBLES_EQUAL(TDuration::Minutes(1) / TDuration::Seconds(10), 6.0, 1e-9);
    }

    Y_UNIT_TEST(TestDurationGetters) {
        const TDuration value = TDuration::MicroSeconds(1234567);
        UNIT_ASSERT_VALUES_EQUAL(value.Seconds(), 1);
        UNIT_ASSERT_DOUBLES_EQUAL(value.SecondsFloat(), 1.234567, 1e-9);

        UNIT_ASSERT_VALUES_EQUAL(value.MilliSeconds(), 1234);
        UNIT_ASSERT_DOUBLES_EQUAL(value.MillisecondsFloat(), 1234.567, 1e-9);

        UNIT_ASSERT_VALUES_EQUAL(value.MicroSeconds(), 1234567);
    }

    template <class T>
    void TestTimeUnits() {
        T withTime = T::MicroSeconds(1249571946000000L);
        T onlyMinutes = T::MicroSeconds(1249571940000000L);
        T onlyHours = T::MicroSeconds(1249570800000000L);
        T onlyDays = T::MicroSeconds(1249516800000000L);
        ui64 minutes = 20826199;
        ui64 hours = 347103;
        ui64 days = 14462;

        UNIT_ASSERT_VALUES_EQUAL(withTime.Minutes(), minutes);
        UNIT_ASSERT_VALUES_EQUAL(onlyMinutes, T::Minutes(minutes));
        UNIT_ASSERT_VALUES_EQUAL(onlyMinutes.Minutes(), minutes);

        UNIT_ASSERT_VALUES_EQUAL(withTime.Hours(), hours);
        UNIT_ASSERT_VALUES_EQUAL(onlyMinutes.Hours(), hours);
        UNIT_ASSERT_VALUES_EQUAL(onlyHours, T::Hours(hours));
        UNIT_ASSERT_VALUES_EQUAL(onlyHours.Hours(), hours);

        UNIT_ASSERT_VALUES_EQUAL(withTime.Days(), days);
        UNIT_ASSERT_VALUES_EQUAL(onlyHours.Days(), days);
        UNIT_ASSERT_VALUES_EQUAL(onlyDays, T::Days(days));
        UNIT_ASSERT_VALUES_EQUAL(onlyDays.Days(), days);
    }

    Y_UNIT_TEST(TestInstantUnits) {
        TestTimeUnits<TInstant>();
    }

    Y_UNIT_TEST(TestDurationUnits) {
        TestTimeUnits<TDuration>();
    }

    Y_UNIT_TEST(TestNoexceptConstruction) {
        UNIT_ASSERT_EXCEPTION(TDuration::MilliSeconds(FromString(TStringBuf("not a number"))), yexception);
        UNIT_ASSERT_EXCEPTION(TDuration::Seconds(FromString(TStringBuf("not a number"))), yexception);
    }

    Y_UNIT_TEST(TestFromValueForTDuration) {
        // check that FromValue creates the same TDuration
        TDuration d1 = TDuration::MicroSeconds(12345);
        TDuration d2 = TDuration::FromValue(d1.GetValue());

        UNIT_ASSERT_VALUES_EQUAL(d1, d2);
    }

    Y_UNIT_TEST(TestFromValueForTInstant) {
        // check that FromValue creates the same TInstant
        TInstant i1 = TInstant::MicroSeconds(12345);
        TInstant i2 = TInstant::FromValue(i1.GetValue());

        UNIT_ASSERT_VALUES_EQUAL(i1, i2);
    }

    Y_UNIT_TEST(TestTimeGmDateConversion) {
        tm time{};
        time_t timestamp = 0;

        // Check all days till year 2106 (max year representable if time_t is 32 bit)
        while (time.tm_year < 2106 - 1900) {
            timestamp += 86400;

            GmTimeR(&timestamp, &time);
            time_t newTimestamp = TimeGM(&time);

            UNIT_ASSERT_VALUES_EQUAL_C(
                newTimestamp,
                timestamp,
                "incorrect date " << (1900 + time.tm_year) << "-" << (time.tm_mon + 1) << "-" << time.tm_mday);
        }
    }

    Y_UNIT_TEST(TestTDurationConstructorFromStdChronoDuration) {

        UNIT_ASSERT_VALUES_EQUAL(TDuration::Zero(), TDuration(0ms));
        UNIT_ASSERT_VALUES_EQUAL(TDuration::MicroSeconds(42), TDuration(42us));
        UNIT_ASSERT_VALUES_EQUAL(TDuration::MicroSeconds(42000000000000L), TDuration(42000000000000us));
        UNIT_ASSERT_VALUES_EQUAL(TDuration::MilliSeconds(42), TDuration(42ms));
        UNIT_ASSERT_VALUES_EQUAL(TDuration::MilliSeconds(42.75), TDuration(42.75ms));
        UNIT_ASSERT_VALUES_EQUAL(TDuration::Seconds(42), TDuration(42s));
        UNIT_ASSERT_VALUES_EQUAL(TDuration::Seconds(42.25), TDuration(42.25s));
        UNIT_ASSERT_VALUES_EQUAL(TDuration::Minutes(42), TDuration(42min));
        UNIT_ASSERT_VALUES_EQUAL(TDuration::Hours(42), TDuration(42h));

        // TDuration doesn't support negative durations
        UNIT_ASSERT_VALUES_EQUAL(TDuration::Zero(), TDuration(-5min));

        UNIT_ASSERT_VALUES_EQUAL(TDuration::MilliSeconds(5), TDuration(std::chrono::duration<i8, std::milli>{5ms}));

#if defined(_LIBCPP_STD_VER) && _LIBCPP_STD_VER > 17
        // libstdc++ does not provide std::chrono::days at the time
        // Consider removing this code upon OS_SDK update
        UNIT_ASSERT_VALUES_EQUAL(TDuration::Days(1), TDuration(std::chrono::days{1}));
#endif

        // clump
        UNIT_ASSERT_VALUES_EQUAL(TDuration::Zero(), TDuration(std::chrono::duration<i64>{-1}));
        UNIT_ASSERT_VALUES_EQUAL(TDuration::Zero(), TDuration(std::chrono::duration<double>{-1}));
        UNIT_ASSERT_VALUES_EQUAL(TDuration::Max(),
                                 TDuration(std::chrono::duration<ui64, std::ratio<3600>>{static_cast<ui64>(1e18)}));
        UNIT_ASSERT_VALUES_EQUAL(TDuration::Max(),
                                 TDuration(std::chrono::duration<i64, std::milli>{static_cast<i64>(::Max<ui64>() / 1000)}));
        UNIT_ASSERT_VALUES_EQUAL(TDuration::Max(),
                                 TDuration(std::chrono::duration<double, std::ratio<3600>>{1e18}));
        UNIT_ASSERT_VALUES_EQUAL(TDuration::Max(),
                                 TDuration(std::chrono::duration<double, std::milli>{::Max<ui64>() / 1000}));
        UNIT_ASSERT_VALUES_EQUAL(TDuration::Max(), TDuration(std::chrono::duration<double, std::milli>{
                                                       static_cast<double>(::Max<ui64>()) / 1000 + 0.1}));
        UNIT_ASSERT_VALUES_EQUAL(TDuration::Max(), TDuration(std::chrono::duration<float, std::milli>{
                                                       static_cast<float>(::Max<ui64>()) / 1000 + 0.1}));
    }

    Y_UNIT_TEST(TestTDurationCompareWithStdChronoDuration) {
        UNIT_ASSERT(TDuration::Zero() == 0ms);
        UNIT_ASSERT(TDuration::Seconds(42) == 42s);

        UNIT_ASSERT(0ms == TDuration::Zero());

        UNIT_ASSERT(TDuration::Zero() != 1ms);
        UNIT_ASSERT(TDuration::Zero() != -1ms);
        UNIT_ASSERT(TDuration::MilliSeconds(1) != -1ms);
        UNIT_ASSERT(TDuration::MilliSeconds(1) != -1ms);

        UNIT_ASSERT(1ms != TDuration::Zero());

        UNIT_ASSERT(TDuration::Seconds(2) < 3s);
        UNIT_ASSERT(3s > TDuration::Seconds(2));
        UNIT_ASSERT(!(TDuration::Seconds(2) < 1s));
        UNIT_ASSERT(!(TDuration::Seconds(2) < -3s));
        UNIT_ASSERT(!(TDuration::Seconds(2) < 2s));

        UNIT_ASSERT(2s < TDuration::Seconds(3));

        UNIT_ASSERT(TDuration::Seconds(2) <= 3s);
        UNIT_ASSERT(!(TDuration::Seconds(2) <= 1s));
        UNIT_ASSERT(!(TDuration::Seconds(2) <= -3s));
        UNIT_ASSERT(TDuration::Seconds(2) <= 2s);

        UNIT_ASSERT(2s <= TDuration::Seconds(2));

        UNIT_ASSERT(TDuration::Seconds(2) > -2s);
        UNIT_ASSERT(TDuration::Seconds(2) > 1s);
        UNIT_ASSERT(TDuration::Seconds(2) > 0s);
        UNIT_ASSERT(!(TDuration::Seconds(2) > 3s));
        UNIT_ASSERT(!(TDuration::Seconds(2) > 2s));

        UNIT_ASSERT(2s > TDuration::Seconds(1));

        UNIT_ASSERT(TDuration::Seconds(2) >= -2s);
        UNIT_ASSERT(TDuration::Seconds(2) >= 1s);
        UNIT_ASSERT(TDuration::Seconds(2) >= 0s);
        UNIT_ASSERT(!(TDuration::Seconds(2) >= 3s));
        UNIT_ASSERT(TDuration::Seconds(2) >= 2s);

        UNIT_ASSERT(2s >= TDuration::Seconds(2));

        static_assert(TDuration::Zero() == 0ms);
        static_assert(TDuration::Zero() < 1ms);
    }

    Y_UNIT_TEST(TestAdditionOfStdChronoDuration) {
        UNIT_ASSERT_VALUES_EQUAL(TDuration::Seconds(1) + 2s, TDuration::Seconds(3));
        UNIT_ASSERT_VALUES_EQUAL(2s + TDuration::Seconds(1), TDuration::Seconds(3));
        UNIT_ASSERT_VALUES_EQUAL(-2s + TDuration::Seconds(3), TDuration::Seconds(1));
        UNIT_ASSERT_VALUES_EQUAL(TDuration::Seconds(3) + (-2s), TDuration::Seconds(1));
        UNIT_ASSERT_VALUES_EQUAL(TDuration::Seconds(3) - 2s, TDuration::Seconds(1));
        UNIT_ASSERT_VALUES_EQUAL(TDuration::Seconds(1) - (-2s), TDuration::Seconds(3));
        UNIT_ASSERT_VALUES_EQUAL(3s - TDuration::Seconds(2), TDuration::Seconds(1));
        UNIT_ASSERT_VALUES_EQUAL(3s - TDuration::Seconds(4), TDuration::Zero());

        UNIT_ASSERT_VALUES_EQUAL(TInstant::Seconds(1) + 2s, TInstant::Seconds(3));
        UNIT_ASSERT_VALUES_EQUAL(TInstant::Seconds(3) + (-2s), TInstant::Seconds(1));
        UNIT_ASSERT_VALUES_EQUAL(TInstant::Seconds(3) - 2s, TInstant::Seconds(1));
        UNIT_ASSERT_VALUES_EQUAL(TInstant::Seconds(1) - (-2s), TInstant::Seconds(3));

        // Operations between TDuration/TInstant and std::chrono::duration are performed
        // with saturation according to the rules of TDuration/TInstant
        UNIT_ASSERT_VALUES_EQUAL(TDuration::Max() + 1h, TDuration::Max());
        UNIT_ASSERT_VALUES_EQUAL(TInstant::Max() + 1h, TInstant::Max());
        UNIT_ASSERT_VALUES_EQUAL(1h + TDuration::Max(), TDuration::Max());
        UNIT_ASSERT_VALUES_EQUAL(TInstant::Max() + 1h, TInstant::Max());

        UNIT_ASSERT_VALUES_EQUAL(TDuration::Max() - (-1h), TDuration::Max());
        UNIT_ASSERT_VALUES_EQUAL(TInstant::Max() - (-1h), TInstant::Max());
        UNIT_ASSERT_VALUES_EQUAL(TInstant::Max() - (-1h), TInstant::Max());

        UNIT_ASSERT_VALUES_EQUAL(-1h - TDuration::Max(), TDuration::Zero());
        UNIT_ASSERT_VALUES_EQUAL(1h - TDuration::Max(), TDuration::Zero());

        static_assert(TDuration::Zero() + 1s == 1s);
        static_assert(TInstant::Seconds(1) + 1s == TInstant::Seconds(2));
    }
}