gctl/lib/utility/utc_time.cpp

450 lines
13 KiB
C++
Raw Normal View History

2024-09-10 15:45:07 +08:00
/********************************************************
*
*
*
*
*
*
* 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;
}