450 lines
13 KiB
C++
450 lines
13 KiB
C++
|
/********************************************************
|
||
|
* ██████╗ ██████╗████████╗██╗
|
||
|
* ██╔════╝ ██╔════╝╚══██╔══╝██║
|
||
|
* ██║ ███╗██║ ██║ ██║
|
||
|
* ██║ ██║██║ ██║ ██║
|
||
|
* ╚██████╔╝╚██████╗ ██║ ███████╗
|
||
|
* ╚═════╝ ╚═════╝ ╚═╝ ╚══════╝
|
||
|
* Geophysical Computational Tools & Library (GCTL)
|
||
|
*
|
||
|
* Copyright (c) 2022 Yi Zhang (yizhang-geo@zju.edu.cn)
|
||
|
*
|
||
|
* GCTL is distributed under a dual licensing scheme. You can redistribute
|
||
|
* it and/or modify it under the terms of the GNU Lesser General Public
|
||
|
* License as published by the Free Software Foundation, either version 2
|
||
|
* of the License, or (at your option) any later version. You should have
|
||
|
* received a copy of the GNU Lesser General Public License along with this
|
||
|
* program. If not, see <http://www.gnu.org/licenses/>.
|
||
|
*
|
||
|
* If the terms and conditions of the LGPL v.2. would prevent you from using
|
||
|
* the GCTL, please consider the option to obtain a commercial license for a
|
||
|
* fee. These licenses are offered by the GCTL's original author. As a rule,
|
||
|
* licenses are provided "as-is", unlimited in time for a one time fee. Please
|
||
|
* send corresponding requests to: yizhang-geo@zju.edu.cn. Please do not forget
|
||
|
* to include some description of your company and the realm of its activities.
|
||
|
* Also add information on how to contact you by electronic and paper mail.
|
||
|
******************************************************/
|
||
|
|
||
|
#include "utc_time.h"
|
||
|
|
||
|
const int DaysInMonth[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
||
|
const std::string MonthName[13] = {"Null", "Jan.", "Feb.", "Mar.", "Apr."," May", "Jun.", "Jul.", "Aug.", "Sep.", "Oct.", "Nov.", "Dec."};
|
||
|
|
||
|
// Function returns true if the given year is a leap year and returns false otherwise.
|
||
|
bool gctl::is_leap_year(int year)
|
||
|
{
|
||
|
if (year < 1)
|
||
|
{
|
||
|
throw std::invalid_argument("[GCTL_SEIMIC] Invalid date input for gctl::is_leap_year(...)");
|
||
|
}
|
||
|
|
||
|
return year % 4 == 0 && ( year % 100 != 0 || year % 400 == 0 );
|
||
|
}
|
||
|
|
||
|
// Compute the month and day corresponding to the Julian day within the given year.
|
||
|
void gctl::month_and_day(int year, int julian_day, int &month, int &day )
|
||
|
{
|
||
|
if (year < 1 || julian_day > 366)
|
||
|
{
|
||
|
throw std::invalid_argument("[GCTL_SEIMIC] Invalid date input for gctl::month_and_day(...)");
|
||
|
}
|
||
|
|
||
|
if (!is_leap_year(year) && julian_day > 365)
|
||
|
{
|
||
|
throw std::invalid_argument("[GCTL_SEIMIC] Invalid date input for gctl::month_and_day(...)");
|
||
|
}
|
||
|
|
||
|
bool month_found = false;
|
||
|
month = 1;
|
||
|
|
||
|
// Loop through the months, subtracting the days in this month
|
||
|
// from the Julian day, until the month is found where the
|
||
|
// remaining days is less than or equal to the total days in the month.
|
||
|
while (!month_found)
|
||
|
{
|
||
|
// Calculate the days in this month by looking it up in the array. Add one if it is a leap year.
|
||
|
int days_this_month = DaysInMonth[month];
|
||
|
if (month == 2 && is_leap_year(year)) ++days_this_month;
|
||
|
|
||
|
if (julian_day <= days_this_month) month_found = true; // Done!
|
||
|
else
|
||
|
{
|
||
|
julian_day -= days_this_month;
|
||
|
++ month;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
day = julian_day;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Compute the Julian day
|
||
|
void gctl::julian_day(int year, int month, int day, int &julian_day)
|
||
|
{
|
||
|
if (year < 1 || month < 1 || month > 12 || day < 1)
|
||
|
{
|
||
|
throw std::invalid_argument("[GCTL_SEIMIC] Invalid date input for gctl::julian_day(...)");
|
||
|
}
|
||
|
|
||
|
if (day > DaysInMonth[month] && !(month == 2 && is_leap_year(year) && day < 30))
|
||
|
{
|
||
|
throw std::invalid_argument("[GCTL_SEIMIC] Invalid date input for gctl::julian_day(...)");
|
||
|
}
|
||
|
|
||
|
julian_day = day;
|
||
|
for (size_t i = 0; i < month; i++)
|
||
|
{
|
||
|
julian_day += DaysInMonth[i];
|
||
|
}
|
||
|
|
||
|
if (month > 2 && is_leap_year(year))
|
||
|
{
|
||
|
julian_day++;
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
void gctl::decimal_year2date(double deci_year, int &year, int &month, int &day)
|
||
|
{
|
||
|
year = floor(deci_year);
|
||
|
|
||
|
int julian_day;
|
||
|
if (is_leap_year(year))
|
||
|
{
|
||
|
julian_day = round(366.0*(deci_year - year));
|
||
|
julian_day = std::max(std::min(julian_day, 366), 1);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
julian_day = round(365.0*(deci_year - year));
|
||
|
julian_day = std::max(std::min(julian_day, 365), 1);
|
||
|
}
|
||
|
|
||
|
month_and_day(year, julian_day, month, day);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
gctl::UTC_TIME::UTC_TIME()
|
||
|
{
|
||
|
year = 1900; month = 1; day = 1; julday = 1;
|
||
|
hour = 0; mint = 0; sec = 0; msec = 0;
|
||
|
}
|
||
|
|
||
|
gctl::UTC_TIME::UTC_TIME(int y, int m, int d, int h, int mt, int s, int ms)
|
||
|
{
|
||
|
set_time(y, m, d, h, mt, s, ms);
|
||
|
}
|
||
|
|
||
|
gctl::UTC_TIME::UTC_TIME(int y, int jd, int h, int mt, int s, int ms)
|
||
|
{
|
||
|
set_time(y, jd, h, mt, s, ms);
|
||
|
}
|
||
|
|
||
|
gctl::UTC_TIME::UTC_TIME(std::string t_str)
|
||
|
{
|
||
|
set_time(t_str);
|
||
|
}
|
||
|
|
||
|
void gctl::UTC_TIME::set_time(int y, int m, int d, int h, int mt, int s, int ms)
|
||
|
{
|
||
|
if (h < 0 || h > 23 || mt < 0 || mt > 59 || s < 0 || s > 59 || ms < 0 || ms > 999)
|
||
|
{
|
||
|
throw std::invalid_argument("[GCTL_SEISMIC] Invalid time parameters for gctl::UTC_TIME::set_time(...)");
|
||
|
}
|
||
|
|
||
|
if (y < 1900 || m < 1 || m > 12 || d < 1 || d > 31)
|
||
|
{
|
||
|
throw std::invalid_argument("[GCTL_SEISMIC] Invalid date parameters for gctl::UTC_TIME::set_time(...)");
|
||
|
}
|
||
|
|
||
|
if (d > DaysInMonth[m])
|
||
|
{
|
||
|
if (is_leap_year(y) && m == 2 && d == 29){}
|
||
|
else throw std::invalid_argument("[GCTL_SEISMIC] Invalid date parameters for gctl::UTC_TIME::set_time(...)");
|
||
|
}
|
||
|
|
||
|
//h %= 24; mt %= 60; s %= 60; ms %= 1000;
|
||
|
year = y; month = m; day = d; hour = h;
|
||
|
mint = mt; sec = s; msec = ms;
|
||
|
julian_day(year, month, day, julday);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
void gctl::UTC_TIME::set_time(int y, int jd, int h, int mt, int s, int ms)
|
||
|
{
|
||
|
if (h < 0 || h > 23 || mt < 0 || mt > 59 || s < 0 || s > 59 || ms < 0 || ms > 999)
|
||
|
{
|
||
|
throw std::invalid_argument("[GCTL_SEISMIC] Invalid time parameters for gctl::UTC_TIME::set_time(...)");
|
||
|
}
|
||
|
|
||
|
if (y < 1900)
|
||
|
{
|
||
|
throw std::invalid_argument("[GCTL_SEISMIC] Invalid date parameters for gctl::UTC_TIME::set_time(...)");
|
||
|
}
|
||
|
|
||
|
if (jd > 365)
|
||
|
{
|
||
|
if (is_leap_year(y) && jd == 366){}
|
||
|
else throw std::invalid_argument("[GCTL_SEISMIC] Invalid date parameters for gctl::UTC_TIME::set_time(...)");
|
||
|
}
|
||
|
|
||
|
//h %= 24; mt %= 60; s %= 60; ms %= 1000;
|
||
|
year = y; julday = jd; hour = h;
|
||
|
mint = mt; sec = s; msec = ms;
|
||
|
month_and_day(year, julday, month, day);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
void gctl::UTC_TIME::set_time(std::string t_str)
|
||
|
{
|
||
|
int y, m, d, h, mt, s, ms;
|
||
|
if (7 != sscanf(t_str.c_str(), "%d-%d-%dT%d:%d:%d.%d", &y, &m, &d, &h, &mt, &s, &ms))
|
||
|
{
|
||
|
throw std::invalid_argument("[GCTL_SEISMIC] Invalid time string for gctl::UTC_TIME::set_time(...)");
|
||
|
}
|
||
|
|
||
|
if (h < 0 || h > 23 || mt < 0 || mt > 59 || s < 0 || s > 59 || ms < 0 || ms > 999)
|
||
|
{
|
||
|
throw std::invalid_argument("[GCTL_SEISMIC] Invalid time parameters for gctl::UTC_TIME::set_time(...)");
|
||
|
}
|
||
|
|
||
|
if (y < 1900 || m < 1 || m > 12 || d < 1 || d > 31)
|
||
|
{
|
||
|
throw std::invalid_argument("[GCTL_SEISMIC] Invalid date parameters for gctl::UTC_TIME::set_time(...)");
|
||
|
}
|
||
|
|
||
|
if (d > DaysInMonth[m])
|
||
|
{
|
||
|
if (is_leap_year(y) && m == 2 && d == 29){}
|
||
|
else throw std::invalid_argument("[GCTL_SEISMIC] Invalid date parameters for gctl::UTC_TIME::set_time(...)");
|
||
|
}
|
||
|
|
||
|
year = y; month = m; day = d; hour = h;
|
||
|
mint = mt; sec = s; msec = ms;
|
||
|
julian_day(year, month, day, julday);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
void gctl::UTC_TIME::set_rdseed_time(std::string t_str)
|
||
|
{
|
||
|
sscanf(t_str.c_str(), "%d,%d,%d:%d:%d.%d", &year, &julday, &hour, &mint, &sec, &msec);
|
||
|
|
||
|
msec = round((double) msec/10);
|
||
|
month_and_day(year, julday, month, day);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
void gctl::UTC_TIME::set_to_now()
|
||
|
{
|
||
|
struct timeval tv;
|
||
|
//struct timezone tz;
|
||
|
|
||
|
//gettimeofday(&tv, &tz);
|
||
|
gettimeofday(&tv, nullptr);
|
||
|
std::tm cur_t = *gmtime(&tv.tv_sec);
|
||
|
|
||
|
year = cur_t.tm_year + 1900;
|
||
|
month = cur_t.tm_mon + 1;
|
||
|
day = cur_t.tm_mday;
|
||
|
hour = cur_t.tm_hour;
|
||
|
mint = cur_t.tm_min;
|
||
|
sec = cur_t.tm_sec;
|
||
|
msec = tv.tv_usec/1000;
|
||
|
julian_day(year, month, day, julday);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
void gctl::UTC_TIME::add_duration(double s)
|
||
|
{
|
||
|
if (s < 0)
|
||
|
{
|
||
|
throw std::invalid_argument("[GCTL_SEIMIC] Invalid time duration for gctl::UTC_TIME::add_duration(...)");
|
||
|
}
|
||
|
|
||
|
msec += round(1000*(s - floor(s))); sec += msec/1000; msec %= 1000;
|
||
|
|
||
|
int t = floor(s);
|
||
|
sec += t%60; mint += sec/60; sec %= 60; t = t/60;
|
||
|
mint += t%60; hour += mint/60; mint %= 60; t = t/60;
|
||
|
hour += t%24; julday += hour/24; hour %= 24; t = t/24;
|
||
|
|
||
|
julday += t;
|
||
|
while (julday > 366)
|
||
|
{
|
||
|
if (!is_leap_year(year) && julday == 365) break;
|
||
|
|
||
|
if (is_leap_year(year)) {julday -= 366; year++;}
|
||
|
else {julday -= 365; year++;}
|
||
|
}
|
||
|
|
||
|
month_and_day(year, julday, month, day);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
void gctl::UTC_TIME::round_up_to(time_unit tu)
|
||
|
{
|
||
|
if (tu == Second)
|
||
|
{
|
||
|
if (msec != 0)
|
||
|
{
|
||
|
msec = 0;
|
||
|
add_duration(1);
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
else if (tu == Mintue)
|
||
|
{
|
||
|
if (msec != 0 || sec != 0)
|
||
|
{
|
||
|
msec = 0;
|
||
|
add_duration(60 - sec);
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
else if (tu == Hour)
|
||
|
{
|
||
|
if (msec != 0 || sec != 0 || mint != 0)
|
||
|
{
|
||
|
sec = 0; msec = 0;
|
||
|
add_duration((60 - mint)*60);
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
else if (tu == Day)
|
||
|
{
|
||
|
if (msec != 0 || sec != 0 || mint != 0 || hour != 0)
|
||
|
{
|
||
|
mint = 0; sec = 0; msec = 0;
|
||
|
add_duration((24 - hour)*3600);
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
else if (tu == Month || tu == Year)
|
||
|
{
|
||
|
throw std::invalid_argument("[GCTL_SEISMIC] Invalid parameter for gctl::UTC_TIME::round_up_to(...)");
|
||
|
}
|
||
|
else return;
|
||
|
}
|
||
|
|
||
|
double gctl::UTC_TIME::diff_sec(const UTC_TIME &t) const
|
||
|
{
|
||
|
double d = sec - t.sec
|
||
|
+ 0.001*(msec - t.msec)
|
||
|
+ 60*(mint - t.mint)
|
||
|
+ 3600*(hour - t.hour)
|
||
|
+ 24*3600*(julday - t.julday)
|
||
|
+ 365*24*3600*(year - t.year);
|
||
|
|
||
|
if (year > t.year)
|
||
|
{
|
||
|
for (int i = t.year; i < year; i++)
|
||
|
{
|
||
|
if (is_leap_year(i)) d += 24*3600;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (year < t.year)
|
||
|
{
|
||
|
for (int i = year; i < t.year; i++)
|
||
|
{
|
||
|
if (is_leap_year(i)) d -= 24*3600;
|
||
|
}
|
||
|
}
|
||
|
return d;
|
||
|
}
|
||
|
|
||
|
std::string gctl::UTC_TIME::time_str(bool symbolic) const
|
||
|
{
|
||
|
double f_sec = 1.0*sec + 0.001*msec;
|
||
|
|
||
|
std::stringstream ss;
|
||
|
ss << std::fixed << std::setprecision(3) << f_sec;
|
||
|
std::string s_sec = ss.str();
|
||
|
|
||
|
if (symbolic) return MonthName[month] + " " + std::to_string(day) + " " + std::to_string(year) + " " + std::to_string(hour)+":"+std::to_string(mint)+":"+ s_sec;
|
||
|
return std::to_string(year)+"-"+std::to_string(month)+"-"+std::to_string(day)+"T"+std::to_string(hour)+":"+std::to_string(mint)+":"+s_sec;
|
||
|
}
|
||
|
|
||
|
std::string gctl::UTC_TIME::rdseed_time_str() const
|
||
|
{
|
||
|
double f_sec = 1.0*sec + 0.001*msec;
|
||
|
|
||
|
std::stringstream ss;
|
||
|
ss << std::fixed << std::setprecision(4) << f_sec;
|
||
|
std::string s_str = ss.str();
|
||
|
if (sec < 10) s_str = "0" + s_str;
|
||
|
|
||
|
std::string jday_str = std::to_string(julday);
|
||
|
if (julday < 10) jday_str = "00" + jday_str;
|
||
|
else if (julday < 100) jday_str = "0" + jday_str;
|
||
|
|
||
|
std::string h_str = std::to_string(hour);
|
||
|
if (hour < 10) h_str = "0" + h_str;
|
||
|
|
||
|
std::string m_str = std::to_string(mint);
|
||
|
if (mint < 10) m_str = "0" + m_str;
|
||
|
|
||
|
return std::to_string(year)+"."+jday_str+"."+h_str+"."+m_str+"."+s_str;
|
||
|
}
|
||
|
|
||
|
bool gctl::operator==(const gctl::UTC_TIME &ta, const gctl::UTC_TIME &tb)
|
||
|
{
|
||
|
if (ta.year != tb.year || ta.julday != tb.julday || ta.hour != tb.hour ||
|
||
|
ta.mint != tb.mint || ta.sec != tb.sec || ta.msec != tb.msec)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool gctl::operator!=(const gctl::UTC_TIME &ta, const gctl::UTC_TIME &tb)
|
||
|
{
|
||
|
if (ta.year != tb.year || ta.julday != tb.julday || ta.hour != tb.hour ||
|
||
|
ta.mint != tb.mint || ta.sec != tb.sec || ta.msec != tb.msec)
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool gctl::operator<(const gctl::UTC_TIME &ta, const gctl::UTC_TIME &tb)
|
||
|
{
|
||
|
if (tb.year < ta.year) return false;
|
||
|
else if (tb.year > ta.year) return true;
|
||
|
|
||
|
if (tb.julday < ta.julday) return false;
|
||
|
else if (tb.julday > ta.julday) return true;
|
||
|
|
||
|
if (tb.hour < ta.hour) return false;
|
||
|
else if (tb.hour > ta.hour) return true;
|
||
|
|
||
|
if (tb.mint < ta.mint) return false;
|
||
|
else if (tb.mint > ta.mint) return true;
|
||
|
|
||
|
if (tb.sec < ta.sec) return false;
|
||
|
else if (tb.sec > ta.sec) return true;
|
||
|
|
||
|
if (tb.msec <= ta.msec) return false;
|
||
|
else return true;
|
||
|
}
|
||
|
|
||
|
bool gctl::operator<=(const gctl::UTC_TIME &ta, const gctl::UTC_TIME &tb)
|
||
|
{
|
||
|
if (tb.year < ta.year) return false;
|
||
|
else if (tb.year > ta.year) return true;
|
||
|
|
||
|
if (tb.julday < ta.julday) return false;
|
||
|
else if (tb.julday > ta.julday) return true;
|
||
|
|
||
|
if (tb.hour < ta.hour) return false;
|
||
|
else if (tb.hour > ta.hour) return true;
|
||
|
|
||
|
if (tb.mint < ta.mint) return false;
|
||
|
else if (tb.mint > ta.mint) return true;
|
||
|
|
||
|
if (tb.sec < ta.sec) return false;
|
||
|
else if (tb.sec > ta.sec) return true;
|
||
|
|
||
|
if (tb.msec < ta.msec) return false;
|
||
|
else return true;
|
||
|
}
|