From 0bea3e4ab1ab4f03ee8505faa48e5a0182741e20 Mon Sep 17 00:00:00 2001 From: Andrew Hutchings Date: Tue, 30 Aug 2016 11:25:16 +0100 Subject: [PATCH] Make date handling more in-line with MySQL Date limit of year 1400 was used due to Boost's limits. This patch strips out the use of Boost for date handling and sets the lower limit to year 1000. --- dbcon/ddlpackageproc/createindexprocessor.cpp | 3 - dbcon/ddlpackageproc/ddlpackageprocessor.cpp | 3 - dbcon/dmlpackageproc/dmlpackageprocessor.cpp | 2 - oam/replaytxnlog/replaytxnlog.cpp | 1 - utils/dataconvert/dataconvert.cpp | 20 +- utils/dataconvert/dataconvert.h | 8 +- utils/funcexp/funchelpers.h | 210 +++++++++--------- utils/funcexp/timeextract.h | 9 +- writeengine/client/we_ddlcommandclient.cpp | 2 - writeengine/client/we_ddlcommandclient.h | 1 - writeengine/client/we_dmlcommandclient.cpp | 2 - writeengine/client/we_dmlcommandclient.h | 1 - writeengine/server/we_ddlcommandproc.cpp | 31 +-- writeengine/server/we_ddlcommandproc.h | 1 - writeengine/server/we_dmlcommandproc.cpp | 2 - writeengine/server/we_dmlcommandproc.h | 1 - 16 files changed, 129 insertions(+), 168 deletions(-) diff --git a/dbcon/ddlpackageproc/createindexprocessor.cpp b/dbcon/ddlpackageproc/createindexprocessor.cpp index 1b8f9da62..232b103c0 100644 --- a/dbcon/ddlpackageproc/createindexprocessor.cpp +++ b/dbcon/ddlpackageproc/createindexprocessor.cpp @@ -31,9 +31,6 @@ using namespace std; #include using namespace boost::algorithm; -#include -using namespace boost::gregorian; - using namespace execplan; using namespace ddlpackage; using namespace logging; diff --git a/dbcon/ddlpackageproc/ddlpackageprocessor.cpp b/dbcon/ddlpackageproc/ddlpackageprocessor.cpp index 390f1bd51..65f36c145 100644 --- a/dbcon/ddlpackageproc/ddlpackageprocessor.cpp +++ b/dbcon/ddlpackageproc/ddlpackageprocessor.cpp @@ -45,9 +45,6 @@ using namespace WriteEngine; #include using namespace boost::algorithm; -#include -using namespace boost::gregorian; - #include "cacheutils.h" using namespace cacheutils; diff --git a/dbcon/dmlpackageproc/dmlpackageprocessor.cpp b/dbcon/dmlpackageproc/dmlpackageprocessor.cpp index fd296af7a..d35b0a403 100644 --- a/dbcon/dmlpackageproc/dmlpackageprocessor.cpp +++ b/dbcon/dmlpackageproc/dmlpackageprocessor.cpp @@ -28,8 +28,6 @@ using namespace std; #include using namespace boost::algorithm; #include -#include -using namespace boost::gregorian; #include #include "we_messages.h" diff --git a/oam/replaytxnlog/replaytxnlog.cpp b/oam/replaytxnlog/replaytxnlog.cpp index 51abaf14e..e43866e9a 100644 --- a/oam/replaytxnlog/replaytxnlog.cpp +++ b/oam/replaytxnlog/replaytxnlog.cpp @@ -33,7 +33,6 @@ using namespace std; #include #include -#include #include namespace fs = boost::filesystem; diff --git a/utils/dataconvert/dataconvert.cpp b/utils/dataconvert/dataconvert.cpp index cb54bb185..dcc0786b4 100644 --- a/utils/dataconvert/dataconvert.cpp +++ b/utils/dataconvert/dataconvert.cpp @@ -31,8 +31,6 @@ using namespace std; #include using namespace boost::algorithm; #include -#include -using namespace boost::gregorian; #include "calpontsystemcatalog.h" #include "calpontselectexecutionplan.h" #include "columnresult.h" @@ -644,24 +642,14 @@ bool mysql_str_to_datetime( const string& input, DateTime& output, bool& isDate return false; } - try - { - boost::gregorian::date d(year, mon, day); - // one more check - boost allows year 10000 but we want to limit at 9999 - if( year > 9999 ) - { - output.reset(); - return false; - } - output.year = d.year(); - output.month = d.month(); - output.day = d.day(); - } - catch (...) + if (!isDateValid(day, mon, year)) { output.reset(); return false; } + output.year = year; + output.month = mon; + output.day = day; /** * Now we need to deal with the time portion. diff --git a/utils/dataconvert/dataconvert.h b/utils/dataconvert/dataconvert.h index cefa45ee5..019ebcd77 100644 --- a/utils/dataconvert/dataconvert.h +++ b/utils/dataconvert/dataconvert.h @@ -228,11 +228,17 @@ inline bool isDateValid ( int day, int month, int year) { bool valid = true; + + if ( year == 0 && month == 0 && year == 0 ) + { + return true; + } + int daycheck = getDaysInMonth( month ); if( month == 2 && isLeapYear( year ) ) // 29 days in February in a leap year daycheck = 29; - if ( ( year < 1400 ) || ( year > 9999 ) ) + if ( ( year < 1000 ) || ( year > 9999 ) ) valid = false; else if ( month < 1 || month > 12 ) valid = false; diff --git a/utils/funcexp/funchelpers.h b/utils/funcexp/funchelpers.h index 05ee711e2..2285c796d 100644 --- a/utils/funcexp/funchelpers.h +++ b/utils/funcexp/funchelpers.h @@ -34,7 +34,6 @@ #include #include #include -#include #include "dataconvert.h" #include "operator.h" @@ -172,39 +171,81 @@ static std::string dayOfMonth[32] = "31st" }; +static uint8_t days_in_month[]= {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0}; + // Given a date, calculate the number of days since year 0 +// This is a mirror of calc_daynr, at a later date we should use my_time.h inline uint32_t calc_mysql_daynr( uint32_t year, uint32_t month, uint32_t day ) { + int temp; + int y = year; + long delsum; + if( !dataconvert::isDateValid( day, month, year ) ) return 0; - boost::gregorian::date d( year, month, day ); - // this is the number of days between the beginning of the - // Gregorian calendar (November 24, 4714 B.C.) and the starting - // date offset in MySQL which is year 0. - const uint32_t JULIAN_DAY_OFFSET = 1721060; - return d.julian_day() - JULIAN_DAY_OFFSET; + delsum= (long) (365 * y + 31 *((int) month - 1) + (int) day); + if (month <= 2) + y--; + else + delsum-= (long) ((int) month * 4 + 23) / 10; + temp=(int) ((y/100+1)*3)/4; + + return delsum+(int) y/4-temp; +} + +// used by get_date_from_mysql_daynr() and calc_mysql_week() +inline uint32_t calc_mysql_days_in_year(uint32_t year) +{ + return ((year & 3) == 0 && (year%100 || (year%400 == 0 && year)) ? + 366 : 365); } // convert from a MySQL day number (offset from year 0) to a date +// This is a mirror of get_date_from_daynr, at a later date we should use sql_time.h inline void get_date_from_mysql_daynr(long daynr,dataconvert::DateTime & dateTime) { - // the MySQL day numbers for min/max boost supported dates - const int BOOST_START_OFFSET = 511340; // 1400-01-01 - const int BOOST_END_OFFSET = 3652424; // 9999-12-31 + uint32_t year, temp, leap_day, day_of_year, days_in_year; + uint8_t *month_pos; + uint32_t ret_year, ret_month, ret_day; + const int MAX_DAY_NUMBER= 3652424; - if( daynr < BOOST_START_OFFSET || daynr > BOOST_END_OFFSET ) - { - dateTime.year= dateTime.month = dateTime.day =0; - } - else - { - boost::gregorian::date d(1400,1,1); - d += boost::gregorian::date_duration( daynr - BOOST_START_OFFSET ); - dateTime.year = d.year(); - dateTime.month = d.month(); - dateTime.day = d.day(); - } + + if (daynr < 366 || daynr > MAX_DAY_NUMBER) + { + dateTime.year= dateTime.month = dateTime.day =0; + return; + } + + year= (uint32_t) (daynr*100 / 36525L); + temp=(((year-1)/100+1)*3)/4; + day_of_year=(uint) (daynr - (long) year * 365L) - (year-1)/4 +temp; + while (day_of_year > (days_in_year= calc_mysql_days_in_year(year))) + { + day_of_year-=days_in_year; + (year)++; + } + leap_day=0; + if (days_in_year == 366) + { + if (day_of_year > 31+28) + { + day_of_year--; + if (day_of_year == 31+28) + leap_day=1; /* Handle leapyears leapday */ + } + } + ret_month=1; + for (month_pos= days_in_month ; + day_of_year > (uint32_t) *month_pos ; + day_of_year-= *(month_pos++), (ret_month)++) + ; + ret_year=year; + ret_day=day_of_year+leap_day; + + dateTime.year = ret_year; + dateTime.month = ret_month; + dateTime.day = ret_day; } // Returns the weekday index for a given date: @@ -212,14 +253,15 @@ inline void get_date_from_mysql_daynr(long daynr,dataconvert::DateTime & dateTim // 0 = Sunday, 1 = Monday, ..., 6 = Saturday // else: // 0 = Monday, 1 = Tuesday, ..., 6 = Sunday +// This is a mirror of calc_weekday, at a later date we should use sql_time.h inline uint32_t calc_mysql_weekday( uint32_t year, uint32_t month, uint32_t day, bool sundayFirst ) { if( !dataconvert::isDateValid( day, month, year ) ) return 0; - boost::gregorian::date d( year, month, day ); - uint32_t ret = d.day_of_week(); - return sundayFirst ? ret : (ret + 6) % 7; + uint32_t daynr = calc_mysql_daynr(year, month, day); + return ((int) ((daynr + 5L + (sundayFirst ? 1L : 0L)) % 7)); + } // Flags for calc_mysql_week @@ -250,98 +292,56 @@ inline int16_t convert_mysql_mode_to_modeflags( int16_t mode ) // that need to know the year that the week actually corresponds to - // see MySQL documentation for how the year returned can be different // than the year of the input date +// +// This is a mirror of calc_week, at a later date we should use sql_time.h inline uint32_t calc_mysql_week( uint32_t year, uint32_t month, uint32_t day, int16_t modeflags, uint32_t* weekyear = 0 ) { - // need to make sure that the date is valid for boost::gregorian::date + // need to make sure that the date is valid if( !dataconvert::isDateValid( day, month, year ) ) return 0; - boost::gregorian::date d( year, month, day ); - // default this to the year of the input date - will update in the - // scenarios where it is either 1 behind or 1 ahead - if( weekyear ) - *weekyear = d.year(); + uint32_t days; + uint32_t daynr=calc_mysql_daynr(year,month,day); + uint32_t first_daynr=calc_mysql_daynr(year,1,1); + bool monday_first= modeflags & WEEK_MONDAY_FIRST; + bool week_year= modeflags & WEEK_NO_ZERO; + bool first_weekday= modeflags & WEEK_GT_THREE_DAYS; - // get a date object for the first day of they year in question - boost::gregorian::date yearfirst = boost::gregorian::date(d.year(),1,1); + uint32_t weekday=calc_mysql_weekday(year, 1, 1, !monday_first); + *weekyear=year; - // figure out which day of week Jan-01 is - uint32_t firstweekday = calc_mysql_weekday( d.year(), 1, 1, !( modeflags & WEEK_MONDAY_FIRST ) ); + if (month == 1 && day <= 7-weekday) + { + if (!week_year && + ((first_weekday && weekday != 0) || + (!first_weekday && weekday >= 4))) + return 0; + week_year= 1; + (*weekyear)--; + first_daynr-= (days=calc_mysql_days_in_year(*weekyear)); + weekday= (weekday + 53*7- days) % 7; + } - // calculate the offset to the first week starting day - uint32_t firstoffset = firstweekday ? ( 7 - firstweekday ) : 0; + if ((first_weekday && weekday != 0) || + (!first_weekday && weekday >= 4)) + days= daynr - (first_daynr+ (7-weekday)); + else + days= daynr - (first_daynr - weekday); - // julian day number for the first day of the first week - uint32_t baseday = yearfirst.julian_day() + firstoffset; - - // if using a modeflags where the first week must have >3 days in this year - // we need to check where firstweekday fell. There are 3 cases for Jan-01 - // 1) Jan-01 fell on week start - there are obviously at least - // three days this year and baseday already correct - // 2) Jan-01 fell on weekday 1 - 3 - this means that our baseday is - // pointing to next week but there must be at least 3 days this year - // so we need to adjust baseday back by one week - // 3) Jan-01 fell on weekday 4 - 6 - this means there cannot be >3 - // days in the week with Jan 01 so our baseday is already correct - if( modeflags & WEEK_GT_THREE_DAYS ) - { - if( firstweekday > 0 && firstweekday < 4 ) - { - baseday = baseday - 7; - } - } - - uint32_t weeknum = 0; - if( d.julian_day() < baseday && ( modeflags & WEEK_NO_ZERO ) ) - { - // this is somewhat of a pain - since we aren't using a 0 week, the date - // is actually going to be in the last week of last year and we need to - // figure out which number that is. This code is identical to above - // except we are compuing for last year instead of current year. - boost::gregorian::date lastyearfirst = boost::gregorian::date(d.year()-1,1,1); - firstweekday = calc_mysql_weekday( d.year()-1, 1, 1, !( modeflags & WEEK_MONDAY_FIRST ) ); - firstoffset = firstweekday ? ( 7 - firstweekday ) : 0; - baseday = lastyearfirst.julian_day() + firstoffset; - - // same logic as above - if( modeflags & WEEK_GT_THREE_DAYS ) - { - if( firstweekday > 0 && firstweekday < 4 ) - { - baseday = baseday - 7; - } - } - weeknum = (d.julian_day() - baseday)/ 7 + 1; - - if( weekyear ) - *weekyear = d.year() - 1; - } - else - { - weeknum = (d.julian_day() >= baseday) ? (d.julian_day() - baseday)/ 7 + 1 : 0; - - if( ( modeflags & WEEK_GT_THREE_DAYS ) && - ( modeflags & WEEK_NO_ZERO ) && weeknum > 52 ) - { - // if this happens we aren't sure whether the week will be 53 or 1. - // to get 53, we know this has to be a December date and can subtract - // our date from 32 to figure out how many days of this week - uint32_t daysthisyear = 32 - d.day(); - uint32_t firstweekday = calc_mysql_weekday( d.year(), d.month(), d.day(), !( modeflags & 0x1 ) ); - // to be 1st week of next year there must be > 3 days of next year - if( ( firstweekday + daysthisyear ) < 4 ) - { - weeknum = 1; - if( weekyear ) - *weekyear = d.year() + 1; - } - } - } - - return weeknum; + if (week_year && days >= 52*7) + { + weekday= (weekday + calc_mysql_days_in_year(*weekyear)) % 7; + if ((!first_weekday && weekday < 4) || + (first_weekday && weekday == 0)) + { + (*weekyear)++; + return 1; + } + } + return days/7+1; } inline bool calc_time_diff(int64_t time1, int64_t time2, int l_sign, long long *seconds_out, long long *microseconds_out) diff --git a/utils/funcexp/timeextract.h b/utils/funcexp/timeextract.h index 59c91931b..038404ca6 100644 --- a/utils/funcexp/timeextract.h +++ b/utils/funcexp/timeextract.h @@ -67,8 +67,7 @@ public: if( dayOfWeek < 0 || !dataconvert::isDateValid( 1, 1, dateTime.year ) ) return returnError( dateTime ); - boost::gregorian::date yearfirst( dateTime.year, 1, 1 ); - + uint32_t yearfirst = helpers::calc_mysql_daynr(dateTime.year, 1, 1); // figure out which day of week Jan-01 is uint32_t firstweekday = helpers::calc_mysql_weekday( dateTime.year, 1, 1, sundayFirst ); @@ -76,10 +75,8 @@ public: uint32_t firstoffset = firstweekday ? ( 7 - firstweekday ) : 0; firstoffset += ( ( weekOfYear - 1) * 7 ) + dayOfWeek - ( sundayFirst ? 0 : 1 ); - yearfirst += boost::gregorian::date_duration( firstoffset ); - dateTime.year = yearfirst.year(); - dateTime.month = yearfirst.month(); - dateTime.day = yearfirst.day(); + yearfirst += firstoffset; + helpers::get_date_from_mysql_daynr(yearfirst, dateTime); } if( !dataconvert::isDateTimeValid( dateTime.hour, dateTime.minute, dateTime.second, dateTime.msecond ) ) diff --git a/writeengine/client/we_ddlcommandclient.cpp b/writeengine/client/we_ddlcommandclient.cpp index 3116350a8..31cd290c5 100644 --- a/writeengine/client/we_ddlcommandclient.cpp +++ b/writeengine/client/we_ddlcommandclient.cpp @@ -25,8 +25,6 @@ using namespace messageqcpp; #include "resourcemanager.h" #include "ddlpkg.h" #include "ddlpackageprocessor.h" -#include -using namespace boost::gregorian; #include "dataconvert.h" using namespace dataconvert; using namespace ddlpackage; diff --git a/writeengine/client/we_ddlcommandclient.h b/writeengine/client/we_ddlcommandclient.h index d5a08874f..c42f07a0c 100644 --- a/writeengine/client/we_ddlcommandclient.h +++ b/writeengine/client/we_ddlcommandclient.h @@ -34,7 +34,6 @@ #define EXPORT #endif -#include #include "dataconvert.h" namespace WriteEngine diff --git a/writeengine/client/we_dmlcommandclient.cpp b/writeengine/client/we_dmlcommandclient.cpp index a7c8a4dd6..56612a4e2 100644 --- a/writeengine/client/we_dmlcommandclient.cpp +++ b/writeengine/client/we_dmlcommandclient.cpp @@ -25,8 +25,6 @@ using namespace messageqcpp; #include "resourcemanager.h" #include "../../dbcon/dmlpackage/dmlpkg.h" #include "ddlpackageprocessor.h" -#include -using namespace boost::gregorian; #include "dataconvert.h" using namespace dataconvert; using namespace dmlpackage; diff --git a/writeengine/client/we_dmlcommandclient.h b/writeengine/client/we_dmlcommandclient.h index 2e626e06c..0e9aeb217 100644 --- a/writeengine/client/we_dmlcommandclient.h +++ b/writeengine/client/we_dmlcommandclient.h @@ -34,7 +34,6 @@ #define EXPORT #endif -#include #include "dataconvert.h" namespace WriteEngine diff --git a/writeengine/server/we_ddlcommandproc.cpp b/writeengine/server/we_ddlcommandproc.cpp index c8a6e3add..b896b25a3 100644 --- a/writeengine/server/we_ddlcommandproc.cpp +++ b/writeengine/server/we_ddlcommandproc.cpp @@ -34,8 +34,7 @@ using namespace messageqcpp; #include "we_ddlcommandproc.h" #include "ddlpkg.h" using namespace ddlpackage; -#include -using namespace boost::gregorian; +#include #include "dataconvert.h" using namespace dataconvert; //#include "we_brm.h" @@ -188,25 +187,15 @@ uint8_t WE_DDLCommandProc::writeSystable(ByteStream& bs, std::string &err) } else if (CREATEDATE_COL == column.tableColName.column) { - date d(day_clock::universal_day()); - std::string date = to_iso_string(d); - Date aDay; - int intvalue; - std::string s = date.substr(0, 4); - if (from_string(intvalue, s, std::dec)) - { - aDay.year = intvalue; - } - s = date.substr(4, 2); - if (from_string(intvalue, s, std::dec)) - { - aDay.month = intvalue; - } - s = date.substr(6, 2); - if (from_string(intvalue, s, std::dec)) - { - aDay.day = intvalue; - } + time_t t; + struct tm tmp; + Date aDay; + + t = time(NULL); + gmtime_r(&t, &tmp); + aDay.year = tmp.tm_year; + aDay.month = tmp.tm_mon+1; + aDay.day = tmp.tm_mday; colTuple.data = *(reinterpret_cast (&aDay)); diff --git a/writeengine/server/we_ddlcommandproc.h b/writeengine/server/we_ddlcommandproc.h index 3731863b3..16793aa81 100644 --- a/writeengine/server/we_ddlcommandproc.h +++ b/writeengine/server/we_ddlcommandproc.h @@ -28,7 +28,6 @@ #include "dbrm.h" #include "we_message_handlers.h" #include "liboamcpp.h" -#include #include "dataconvert.h" #include "writeengine.h" diff --git a/writeengine/server/we_dmlcommandproc.cpp b/writeengine/server/we_dmlcommandproc.cpp index bb4bd89ec..59d15063c 100644 --- a/writeengine/server/we_dmlcommandproc.cpp +++ b/writeengine/server/we_dmlcommandproc.cpp @@ -30,8 +30,6 @@ using namespace messageqcpp; using namespace dmlpackage; #include "dmlpackageprocessor.h" using namespace dmlpackageprocessor; -#include -using namespace boost::gregorian; #include "dataconvert.h" using namespace dataconvert; #include "calpontsystemcatalog.h" diff --git a/writeengine/server/we_dmlcommandproc.h b/writeengine/server/we_dmlcommandproc.h index 76efb04ca..b92462bd7 100644 --- a/writeengine/server/we_dmlcommandproc.h +++ b/writeengine/server/we_dmlcommandproc.h @@ -33,7 +33,6 @@ #include "calpontsystemcatalog.h" #include "insertdmlpackage.h" #include "liboamcpp.h" -#include #include "dataconvert.h" #include "writeengine.h" #include "we_convertor.h"