Drizzled Public API Documentation

time_functions.cc

00001 /* -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
00002  *  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
00003  *
00004  *  Copyright (C) 2008-2009 Sun Microsystems, Inc.
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; version 2 of the License.
00009  *
00010  *  This program is distributed in the hope that it will be useful,
00011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  *  GNU General Public License for more details.
00014  *
00015  *  You should have received a copy of the GNU General Public License
00016  *  along with this program; if not, write to the Free Software
00017  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00018  */
00019 
00020 
00021 /* Functions to handle date and time */
00022 
00023 #include <config.h>
00024 #include <drizzled/error.h>
00025 #include <drizzled/util/test.h>
00026 #include <drizzled/tztime.h>
00027 #include <drizzled/session.h>
00028 #include <drizzled/time_functions.h>
00029 
00030 namespace drizzled
00031 {
00032 
00033 /* Some functions to calculate dates */
00034 
00035 
00036 int calc_weekday(long daynr,bool sunday_first_day_of_week)
00037 {
00038   return ((int) ((daynr + 5L + (sunday_first_day_of_week ? 1L : 0L)) % 7));
00039 }
00040 
00041 
00042 uint32_t calc_week(type::Time *l_time, uint32_t week_behaviour, uint32_t *year)
00043 {
00044   uint32_t days;
00045   uint32_t daynr= calc_daynr(l_time->year,l_time->month,l_time->day);
00046   uint32_t first_daynr= calc_daynr(l_time->year,1,1);
00047   bool monday_first= test(week_behaviour & WEEK_MONDAY_FIRST);
00048   bool week_year= test(week_behaviour & WEEK_YEAR);
00049   bool first_weekday= test(week_behaviour & WEEK_FIRST_WEEKDAY);
00050 
00051   uint32_t weekday= calc_weekday(first_daynr, !monday_first);
00052   *year=l_time->year;
00053 
00054   if (l_time->month == 1 && l_time->day <= 7-weekday)
00055   {
00056     if ((!week_year) && ((first_weekday && weekday != 0) || (!first_weekday && weekday >= 4)))
00057       return 0;
00058     week_year= 1;
00059     (*year)--;
00060     first_daynr-= (days=calc_days_in_year(*year));
00061     weekday= (weekday + 53*7- days) % 7;
00062   }
00063 
00064   if ((first_weekday && weekday != 0) ||
00065       (!first_weekday && weekday >= 4))
00066     days= daynr - (first_daynr+ (7-weekday));
00067   else
00068     days= daynr - (first_daynr - weekday);
00069 
00070   if (week_year && days >= 52*7)
00071   {
00072     weekday= (weekday + calc_days_in_year(*year)) % 7;
00073     if ((!first_weekday && weekday < 4) || (first_weekday && weekday == 0))
00074     {
00075       (*year)++;
00076       return 1;
00077     }
00078   }
00079   return days/7+1;
00080 }
00081 
00082 
00083 void get_date_from_daynr(long daynr,
00084                          uint32_t *ret_year,
00085                          uint32_t *ret_month,
00086                          uint32_t *ret_day)
00087 {
00088   uint32_t year,temp,leap_day,day_of_year,days_in_year;
00089   unsigned char *month_pos;
00090 
00091   if (daynr <= 365L || daynr >= 3652500)
00092   {           /* Fix if wrong daynr */
00093     *ret_year= *ret_month = *ret_day =0;
00094   }
00095   else
00096   {
00097     year= (uint32_t) (daynr*100 / 36525L);
00098     temp=(((year-1)/100+1)*3)/4;
00099     day_of_year=(uint32_t) (daynr - (long) year * 365L) - (year-1)/4 +temp;
00100     while (day_of_year > (days_in_year= calc_days_in_year(year)))
00101     {
00102       day_of_year-=days_in_year;
00103       (year)++;
00104     }
00105     leap_day=0;
00106     if (days_in_year == 366)
00107     {
00108       if (day_of_year > 31+28)
00109       {
00110         day_of_year--;
00111         if (day_of_year == 31+28)
00112           leap_day=1;   /* Handle leapyears leapday */
00113       }
00114     }
00115     *ret_month=1;
00116     for (month_pos= days_in_month ;
00117          day_of_year > (uint32_t) *month_pos ;
00118          day_of_year-= *(month_pos++), (*ret_month)++)
00119       ;
00120     *ret_year=year;
00121     *ret_day=day_of_year+leap_day;
00122   }
00123   return;
00124 }
00125 
00126 
00127 type::timestamp_t str_to_datetime_with_warn(Session *session,
00128                                             const char *str, 
00129                                             uint32_t length, 
00130                                             type::Time *l_time,
00131                                             uint32_t flags)
00132 {
00133   type::cut_t was_cut= type::VALID;
00134   type::timestamp_t ts_type;
00135 
00136   ts_type= l_time->store(str, length,
00137                          (flags | (session->variables.sql_mode &
00138                                    (MODE_INVALID_DATES |
00139                                     MODE_NO_ZERO_DATE))),
00140                          was_cut);
00141   if (was_cut || ts_type <= type::DRIZZLE_TIMESTAMP_ERROR)
00142     make_truncated_value_warning(session, DRIZZLE_ERROR::WARN_LEVEL_WARN,
00143                                  str, length, ts_type,  NULL);
00144 
00145   return ts_type;
00146 }
00147 
00148 
00149 bool
00150 str_to_time_with_warn(Session *session, const char *str, uint32_t length, type::Time *l_time)
00151 {
00152   int warning;
00153   bool ret_val= l_time->store(str, length, warning, type::DRIZZLE_TIMESTAMP_TIME);
00154   if (ret_val || warning)
00155     make_truncated_value_warning(session, DRIZZLE_ERROR::WARN_LEVEL_WARN,
00156                                  str, length, type::DRIZZLE_TIMESTAMP_TIME, NULL);
00157   return ret_val;
00158 }
00159 
00160 
00161 void make_truncated_value_warning(Session *session, 
00162                                   DRIZZLE_ERROR::enum_warning_level level,
00163                                   const char *str_val,
00164                                   uint32_t str_length,
00165                                   type::timestamp_t time_type,
00166                                   const char *field_name)
00167 {
00168   char warn_buff[DRIZZLE_ERRMSG_SIZE];
00169   const char *type_str;
00170   CHARSET_INFO *cs= &my_charset_utf8_general_ci;
00171   char buff[128];
00172   String str(buff,(uint32_t) sizeof(buff), system_charset_info);
00173   str.copy(str_val, str_length, system_charset_info);
00174   str[str_length]= 0;               // Ensure we have end 0 for snprintf
00175 
00176   switch (time_type) {
00177   case type::DRIZZLE_TIMESTAMP_DATE:
00178     type_str= "date";
00179     break;
00180 
00181   case type::DRIZZLE_TIMESTAMP_TIME:
00182     type_str= "time";
00183     break;
00184 
00185   case type::DRIZZLE_TIMESTAMP_DATETIME:  // FALLTHROUGH
00186   default:
00187     type_str= "datetime";
00188     break;
00189   }
00190 
00191   if (field_name)
00192   {
00193     cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff),
00194                        ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
00195                        type_str, str.c_ptr(), field_name,
00196                        (uint32_t) session->row_count);
00197   }
00198   else
00199   {
00200     if (time_type > type::DRIZZLE_TIMESTAMP_ERROR)
00201     {
00202       cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff),
00203                          ER(ER_TRUNCATED_WRONG_VALUE),
00204                          type_str, str.c_ptr());
00205     }
00206     else
00207     {
00208       cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff),
00209                          ER(ER_WRONG_VALUE), type_str, str.c_ptr());
00210     }
00211   }
00212   push_warning(session, level,
00213                ER_TRUNCATED_WRONG_VALUE, warn_buff);
00214 }
00215 
00216 
00217 bool
00218 calc_time_diff(type::Time *l_time1, type::Time *l_time2, int l_sign, int64_t *seconds_out,
00219                long *microseconds_out)
00220 {
00221   long days;
00222   bool neg;
00223   int64_t microseconds;
00224 
00225   /*
00226     We suppose that if first argument is DRIZZLE_TIMESTAMP_TIME
00227     the second argument should be TIMESTAMP_TIME also.
00228     We should check it before calc_time_diff call.
00229   */
00230   if (l_time1->time_type == type::DRIZZLE_TIMESTAMP_TIME)  // Time value
00231     days= (long)l_time1->day - l_sign * (long)l_time2->day;
00232   else
00233   {
00234     days= calc_daynr((uint32_t) l_time1->year,
00235                      (uint32_t) l_time1->month,
00236                      (uint32_t) l_time1->day);
00237     if (l_time2->time_type == type::DRIZZLE_TIMESTAMP_TIME)
00238       days-= l_sign * (long)l_time2->day;
00239     else
00240       days-= l_sign*calc_daynr((uint32_t) l_time2->year,
00241                                (uint32_t) l_time2->month,
00242                                (uint32_t) l_time2->day);
00243   }
00244 
00245   microseconds= ((int64_t)days*86400L +
00246                  (int64_t)(l_time1->hour*3600L +
00247                            l_time1->minute*60L +
00248                            l_time1->second) -
00249                  l_sign*(int64_t)(l_time2->hour*3600L +
00250                                   l_time2->minute*60L +
00251                                   l_time2->second)) * 1000000L +
00252     (int64_t)l_time1->second_part -
00253     l_sign*(int64_t)l_time2->second_part;
00254 
00255   neg= 0;
00256   if (microseconds < 0)
00257   {
00258     microseconds= -microseconds;
00259     neg= 1;
00260   }
00261   *seconds_out= microseconds/1000000L;
00262   *microseconds_out= (long) (microseconds%1000000L);
00263   return neg;
00264 }
00265 
00266 } /* namespace drizzled */