Skip to content

File utctime.cu

File List > src > utctime.cu

Go to the documentation of this file

#include "utctime.h"

namespace chrono = std::chrono;
using chrono::duration_cast;
using chrono::time_point_cast; 
/*
namespace {

std::time_t to_time_t(const UTCClock::time_point &tp) noexcept
{
  return std::time_t(
      duration_cast<chrono::seconds>(tp.time_since_epoch()).count());
}

UTCClock::time_point from_time_t(std::time_t tt) noexcept
{
  return time_point_cast<UTCClock::duration>(
     chrono::time_point<UTCClock,chrono::seconds>(chrono::seconds(tt)));
}

} // namespace
*/
// Algorithm: http://howardhinnant.github.io/date_algorithms.html
int days_from_epoch(int y, int m, int d)
{
    y -= m <= 2;
    int era = y / 400;
    int yoe = y - era * 400;                                   // [0, 399]
    int doy = (153 * (m + (m > 2 ? -3 : 9)) + 2) / 5 + d - 1;  // [0, 365]
    int doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;           // [0, 146096]
    return era * 146097 + doe - 719468;
}

struct tm* gmtime_r(const time_t* timep, struct tm* tm) {
    const time_t ts = *timep;
    time_t t = ts / 86400;
    unsigned hms = ts % 86400;  /* -86399 <= hms <= 86399. This needs sizeof(int) >= 4. */
    time_t c, f;
    unsigned yday;  /* 0 <= yday <= 426. Also fits to an `unsigned short', but `int' is faster. */
    unsigned a;  /* 0 <= a <= 2133. Also fits to an `unsigned short', but `int' is faster. */
    if ((int)hms < 0) { --t; hms += 86400; }  /* Fix quotient and negative remainder if ts was negative (i.e. before year 1970 CE). */
    /* Now: -24856 <= t <= 24855. */
    tm->tm_sec = hms % 60;
    hms /= 60;
    tm->tm_min = hms % 60;
    tm->tm_hour = hms / 60;
    if (sizeof(time_t) > 4) {  /* Optimization. For int32_t, this would keep t intact, so we won't have to do it. This produces unreachable code. */
        f = (t + 4) % 7;
        if (f < 0) f += 7;  /* Fix negative remainder if (t + 4) was negative. */
        /* Now 0 <= f <= 6. */
        tm->tm_wday = f;
        c = (t << 2) + 102032;
        f = c / 146097;
        if (c % 146097 < 0) --f;  /* Fix negative remainder if c was negative. */
        --f;
        t += f;
        f >>= 2;
        t -= f;
        f = (t << 2) + 102035;
        c = f / 1461;
        if (f % 1461 < 0) --c;  /* Fix negative remainder if f was negative. */
    }
    else {
        tm->tm_wday = (t + 24861) % 7;  /* t + 24861 >= 0. */
        /* Now: -24856 <= t <= 24855. */
        c = ((t << 2) + 102035) / 1461;
    }
    yday = t - 365 * c - (c >> 2) + 25568;
    /* Now: 0 <= yday <= 425. */
    a = yday * 5 + 8;
    /* Now: 8 <= a <= 2133. */
    tm->tm_mon = a / 153;
    a %= 153;  /* No need to fix if a < 0, because a cannot be negative here. */
    /* Now: 2 <= tm->tm_mon <= 13. */
    /* Now: 0 <= a <= 152. */
    tm->tm_mday = 1 + a / 5;  /* No need to fix if a < 0, because a cannot be negative here. */
    /* Now: 1 <= tm->tm_mday <= 31. */
    if (tm->tm_mon >= 12) {
        tm->tm_mon -= 12;
        /* Now: 0 <= tm->tm_mon <= 1. */
        ++c;
        yday -= 366;
    }
    else {  /* Check for leap year (in c). */
   /* Now: 2 <= tm->tm_mon <= 11. */
   /* 1903: not leap; 1904: leap, 1900: not leap; 2000: leap */
   /* With sizeof(time_t) == 4, we have 1901 <= year <= 2038; of these
    * years only 2000 is divisble by 100, and that's a leap year, no we
    * optimize the check to `(c & 3) == 0' only.
    */
        if (!((c & 3) == 0 && (sizeof(time_t) <= 4 || c % 100 != 0 || (c + 300) % 400 == 0))) --yday;  /* These `== 0' comparisons work even if c < 0. */
    }
    tm->tm_year = c;  /* This assignment may overflow or underflow, we don't check it. Example: time_t is a huge int64_t, tm->tm_year is int32_t. */
    /* Now: 0 <= tm->tm_mon <= 11. */
    /* Now: 0 <= yday <= 365. */
    tm->tm_yday = yday;
    tm->tm_isdst = 0;
    return tm;
}


// It  does not modify broken-down time
long long timegm(struct tm const* t)
{
    int year = t->tm_year + 1900;
    int month = t->tm_mon;          // 0-11
    if (month > 11)
    {
        year += month / 12;
        month %= 12;
    }
    else if (month < 0)
    {
        int years_diff = (11 - month) / 12;
        year -= years_diff;
        month += 12 * years_diff;
    }
    int days_since_epoch = days_from_epoch(year, month + 1, t->tm_mday);

    return 60LL * (60LL * (24LL * days_since_epoch + (long long)t->tm_hour) + (long long)t->tm_min) + (long long)t->tm_sec;
}
/*
UTCClock::time_point UTCClock::fromDate(
    int year, int month, int day, int hour, int min, int sec, int usec)
{
  std::tm tm     = {0};
  tm.tm_year     = year - 1900;
  tm.tm_mon      = month - 1;
  tm.tm_mday     = day;
  tm.tm_hour     = hour;
  tm.tm_min      = min;
  tm.tm_sec      = sec;
  tm.tm_isdst    = -1;
  std::time_t tt = timegm(&tm);
  return from_time_t(tt) + chrono::microseconds(usec);
}


void UTCClock::toDate(const UTCClock::time_point &tp,
                      int &year,
                      int &month,
                      int &day,
                      int &hour,
                      int &min,
                      int &sec,
                      int &usec)
{
  std::time_t tt = to_time_t(tp);
  std::tm tm;
  gmtime_r(&tt, &tm);
  year  = tm.tm_year + 1900;
  month = tm.tm_mon + 1;
  day   = tm.tm_mday;
  hour  = tm.tm_hour;
  min   = tm.tm_min;
  chrono::microseconds leftover =
      tp - from_time_t(tt) + chrono::seconds(tm.tm_sec);
  sec = duration_cast<chrono::seconds>(leftover).count();
  usec = (leftover-chrono::seconds(sec)).count();
}
*/

long long date_string_to_time(std::string date)
{
    struct tm tm = { 0 }; // Important, initialize all members
    //int n = 0;
    //int year, mon, day, hour, min, sec;
    std::vector<std::string>  datetime, ddd, ttt;
    datetime = split(date, 'T');
    if (datetime.size() < 2)
    {
        datetime.clear();
        datetime = split(date, ' ');
    }

    ddd = split(datetime[0], '-');
    if (ddd.size() < 3)
    {
        ddd = split(datetime[0], '/');
    }
    // index for the year and mday
    //by default we assume date is written as yyyy-mm-ddTHH:MM:SS
    int indexyear = 0;
    int indexmday = 2;

    if (ddd[0].length() < 4 && ddd[2].length() == 4)//i.e. date is written as dd-mm-yyyy
    {
        int indexyear = 2;
        int indexmday = 0;
    }
    // If you write date like an American (mm-dd-yyyy). it wont work and I can't help

    tm.tm_year = std::stoi(ddd[indexyear]);

    tm.tm_mon = std::stoi(ddd[1]);

    tm.tm_mday = std::stoi(ddd[indexmday]);

    if (datetime.size() > 1)
    {

        ttt = split(datetime[1], ':');

        tm.tm_hour = std::stoi(ttt[0]);
        tm.tm_min = std::stoi(ttt[1]);
        if (ttt.size() == 3)
        {
            tm.tm_sec = std::stoi(ttt[2]);
        }
        else
        {
            tm.tm_sec = 0;
        }
    }
    else
    {
        tm.tm_hour = 0;
        tm.tm_min = 0;
        tm.tm_sec = 0;
    }




    //sscanf(date, "%d-%d-%dT%d:%d:%d %n", &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
    //  &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &n);
    // If scan did not completely succeed or extra junk
    //if (n == 0 || date[n]) {
    //  return (time_t)-1;
    //}
    //tm.tm_isdst = 0; // Eforce output to be standard time. 
    tm.tm_mon--;      // Months since January
    // Assume 2 digit year if in the range 2000-2099, else assume year as given
    if (tm.tm_year >= 0 && tm.tm_year < 100) {
        tm.tm_year += 2000;
    }
    tm.tm_year -= 1900; // Years since 1900
    //UTCTime t1 = UTCClock::fromDate(year, mon, day, hour, min, sec, 0);
    long long t1 = timegm(&tm);
    return t1;
}

double date_string_to_s(std::string datetime, std::string refdate)
{
    //testime1(1);
    //testime2(2);

    //UTCTime ttime = date_string_to_time(datetime);
    //UTCTime reftime = date_string_to_time(refdate);

    double diff;

    std::string::size_type n = datetime.find('T');
    if (std::string::npos == n)
    {
        diff = std::stod(datetime);
    }
    else
    {

        long long ttime = date_string_to_time(datetime);
        long long reftime = date_string_to_time(refdate);

        //double diff = difftime(ttime, reftime);

        //std::chrono::microseconds timeDiff = ttime - reftime;

        //double diff = ((double) duration_cast<std::chrono::milliseconds>(ttime - reftime).count())/1000.0;

        diff = (double)(ttime - reftime);
    }

    return diff;
}

// Read time string. If it is a valid datetime string return s from reftime otherwise return a foat of seconds 
double readinputtimetxt(std::string input, std::string & refdate)
{
    std::string date = trim(input, " ");
    double timeinsec;
    //check if string contains a T a marker of 
    std::vector<std::string>  datetime = split(date, 'T');

    if (datetime.size() > 1)
    {
        //likely a datetime
        if (refdate.empty())
        {
            refdate = date;
        }
        timeinsec = date_string_to_s(date, refdate);


    }
    else
    {
        //Likely a float
        timeinsec = std::stod(datetime[0]);
    }

    return timeinsec;
}

bool testime1(int hour)
{
    bool test = false;
    double eps = 1e-7;

    for (int iy = 1400; iy <= 2800; iy++)
    {
        tm tm1, tm2;

        tm1.tm_year = iy;
        tm2.tm_year = iy;

        tm1.tm_mday = 1;
        tm2.tm_mday = 1;

        tm1.tm_mon = 0;
        tm2.tm_mon = 0;

        tm1.tm_hour = 0;
        tm2.tm_hour = hour;

        tm1.tm_min = 0;
        tm2.tm_min = 0;

        tm1.tm_sec = 0;
        tm2.tm_sec = 0;

        //UTCTime t1 = UTCClock::fromDate(iy, 1, 1, 0, 0, 0, 0);
        //UTCTime t2 = UTCClock::fromDate(iy, 1, 1, hour, 0, 0, 0);

        long long t1 = timegm(&tm1);
        long long t2 = timegm(&tm2);




        //double dt12 = ((double)duration_cast<std::chrono::milliseconds>(t2 - t1).count()) / 1000.0;
        double dt12 = (double)(t2 - t1);
        test = abs(dt12 - (hour * 3600.0)) < eps;
        if (!test)
        {
            printf("Failed datetime calculation: year=%d\n", iy);
            break;
        }
    }


    return test;
}

bool testime2(int hour)
{
    bool test = false;


    for (int iy = 1970; iy <= 2400; iy++)
    {
        //UTCTime t1 = UTCClock::fromDate(1970, 1, 1, 0, 0, 0, 0);
        //UTCTime t2 = UTCClock::fromDate(iy, 1, 1, hour, 0, 0, 0);
        tm tm1, tm2;

        tm1.tm_year = 1970;
        tm2.tm_year = iy;

        tm1.tm_mday = 1;
        tm2.tm_mday = 1;

        tm1.tm_mon = 0;
        tm2.tm_mon = 0;

        tm1.tm_hour = 0;
        tm2.tm_hour = hour;

        tm1.tm_min = 0;
        tm2.tm_min = 0;

        tm1.tm_sec = 0;
        tm2.tm_sec = 0;

        //UTCTime t1 = UTCClock::fromDate(iy, 1, 1, 0, 0, 0, 0);
        //UTCTime t2 = UTCClock::fromDate(iy, 1, 1, hour, 0, 0, 0);

        long long t1 = timegm(&tm1);
        long long t2 = timegm(&tm2);



        //double dt12 = ((double)duration_cast<std::chrono::milliseconds>(t2 - t1).count()) / 1000.0;

        test = t2>t1;
        if (!test)
        {

            int dse = days_from_epoch(tm2.tm_year, tm2.tm_mon + 1, tm2.tm_mday);
            printf("Failed datetime calculation greater than: year=%d\n", iy);
            printf("dse=%d\nt1 = %lld;\n t2=%lld\n",dse, t1, t2);
            break;
        }
    }


    return test;
}