diff options
Diffstat (limited to 'mrbgems/mruby-time/src')
| -rw-r--r-- | mrbgems/mruby-time/src/time.c | 301 |
1 files changed, 218 insertions, 83 deletions
diff --git a/mrbgems/mruby-time/src/time.c b/mrbgems/mruby-time/src/time.c index cfd51ac63..9b0549bea 100644 --- a/mrbgems/mruby-time/src/time.c +++ b/mrbgems/mruby-time/src/time.c @@ -4,11 +4,14 @@ ** See Copyright Notice in mruby.h */ +#ifndef MRB_WITHOUT_FLOAT #include <math.h> -#include <time.h> +#endif + #include <mruby.h> #include <mruby/class.h> #include <mruby/data.h> +#include <mruby/time.h> #ifndef MRB_DISABLE_STDIO #include <stdio.h> @@ -16,7 +19,10 @@ #include <string.h> #endif +#include <stdlib.h> + #define NDIV(x,y) (-(-((x)+1)/(y))-1) +#define TO_S_FMT "%Y-%m-%d %H:%M:%S " #if defined(_MSC_VER) && _MSC_VER < 1800 double round(double x) { @@ -24,8 +30,10 @@ double round(double x) { } #endif -#if !defined(__MINGW64__) && defined(_WIN32) -# define llround(x) round(x) +#ifdef MRB_WITHOUT_FLOAT +# if !defined(__MINGW64__) && defined(_WIN32) +# define llround(x) round(x) +# endif #endif #if defined(__MINGW64__) || defined(__MINGW32__) @@ -46,7 +54,7 @@ double round(double x) { /* #define NO_GMTIME_R */ #ifdef _WIN32 -#if _MSC_VER +#ifdef _MSC_VER /* Win32 platform do not provide gmtime_r/localtime_r; emulate them using gmtime_s/localtime_s */ #define gmtime_r(tp, tm) ((gmtime_s((tm), (tp)) == 0) ? (tm) : NULL) #define localtime_r(tp, tm) ((localtime_s((tm), (tp)) == 0) ? (tm) : NULL) @@ -166,13 +174,6 @@ timegm(struct tm *tm) * second level. Also, there are only 2 timezones, namely UTC and LOCAL. */ -enum mrb_timezone { - MRB_TIMEZONE_NONE = 0, - MRB_TIMEZONE_UTC = 1, - MRB_TIMEZONE_LOCAL = 2, - MRB_TIMEZONE_LAST = 3 -}; - typedef struct mrb_timezone_name { const char name[8]; size_t len; @@ -203,21 +204,96 @@ struct mrb_time { static const struct mrb_data_type mrb_time_type = { "Time", mrb_free }; +#ifndef MRB_WITHOUT_FLOAT +void mrb_check_num_exact(mrb_state *mrb, mrb_float num); +typedef mrb_float mrb_sec; +#define mrb_sec_value(mrb, sec) mrb_float_value(mrb, sec) +#else +typedef mrb_int mrb_sec; +#define mrb_sec_value(mrb, sec) mrb_fixnum_value(sec) +#endif + +#ifdef MRB_TIME_T_UINT +typedef uint64_t mrb_time_int; +# define MRB_TIME_MIN 0 +# define MRB_TIME_MAX (sizeof(time_t) <= 4 ? UINT32_MAX : UINT64_MAX) +#else +typedef int64_t mrb_time_int; +# define MRB_TIME_MIN (sizeof(time_t) <= 4 ? INT32_MIN : INT64_MIN) +# define MRB_TIME_MAX (sizeof(time_t) <= 4 ? INT32_MAX : INT64_MAX) +#endif + +static time_t +mrb_to_time_t(mrb_state *mrb, mrb_value obj, time_t *usec) +{ + time_t t; + + switch (mrb_type(obj)) { +#ifndef MRB_WITHOUT_FLOAT + case MRB_TT_FLOAT: + { + mrb_float f = mrb_float(obj); + + mrb_check_num_exact(mrb, f); + if (f > (mrb_float)MRB_TIME_MAX || (mrb_float)MRB_TIME_MIN > f) { + goto out_of_range; + } + + if (usec) { + t = (time_t)f; + *usec = (time_t)llround((f - t) * 1.0e+6); + } + else { + t = (time_t)llround(f); + } + } + break; +#endif /* MRB_WITHOUT_FLOAT */ + default: + case MRB_TT_FIXNUM: + { + mrb_int i = mrb_int(mrb, obj); + + if ((mrb_time_int)i > MRB_TIME_MAX || MRB_TIME_MIN > i) { + goto out_of_range; + } + + t = (time_t)i; + if (usec) { *usec = 0; } + } + break; + } + + return t; + +out_of_range: + mrb_raisef(mrb, E_ARGUMENT_ERROR, "%v out of Time range", obj); + + /* not reached */ + if (usec) { *usec = 0; } + return 0; +} + /** Updates the datetime of a mrb_time based on it's timezone and -seconds setting. Returns self on success, NULL of failure. */ + seconds setting. Returns self on success, NULL of failure. + if `dealloc` is set `true`, it frees `self` on error. */ static struct mrb_time* -time_update_datetime(mrb_state *mrb, struct mrb_time *self) +time_update_datetime(mrb_state *mrb, struct mrb_time *self, int dealloc) { struct tm *aid; + time_t t = self->sec; if (self->timezone == MRB_TIMEZONE_UTC) { - aid = gmtime_r(&self->sec, &self->datetime); + aid = gmtime_r(&t, &self->datetime); } else { - aid = localtime_r(&self->sec, &self->datetime); + aid = localtime_r(&t, &self->datetime); } if (!aid) { - mrb_raisef(mrb, E_ARGUMENT_ERROR, "%S out of Time range", mrb_float_value(mrb, (mrb_float)self->sec)); + mrb_sec sec = (mrb_sec)t; + + if (dealloc) mrb_free(mrb, self); + mrb_raisef(mrb, E_ARGUMENT_ERROR, "%v out of Time range", mrb_sec_value(mrb, sec)); /* not reached */ return NULL; } @@ -234,58 +310,50 @@ mrb_time_wrap(mrb_state *mrb, struct RClass *tc, struct mrb_time *tm) return mrb_obj_value(Data_Wrap_Struct(mrb, tc, &mrb_time_type, tm)); } -void mrb_check_num_exact(mrb_state *mrb, mrb_float num); - /* Allocates a mrb_time object and initializes it. */ static struct mrb_time* -time_alloc(mrb_state *mrb, double sec, double usec, enum mrb_timezone timezone) +time_alloc_time(mrb_state *mrb, time_t sec, time_t usec, enum mrb_timezone timezone) { struct mrb_time *tm; - time_t tsec = 0; - mrb_check_num_exact(mrb, (mrb_float)sec); - mrb_check_num_exact(mrb, (mrb_float)usec); -#ifndef MRB_TIME_T_UINT - if (sizeof(time_t) == 4 && (sec > (double)INT32_MAX || (double)INT32_MIN > sec)) { - goto out_of_range; - } - if (sizeof(time_t) == 8 && (sec > (double)INT64_MAX || (double)INT64_MIN > sec)) { - goto out_of_range; - } -#else - if (sizeof(time_t) == 4 && (sec > (double)UINT32_MAX || (double)0 > sec)) { - goto out_of_range; - } - if (sizeof(time_t) == 8 && (sec > (double)UINT64_MAX || (double)0 > sec)) { - goto out_of_range; - } -#endif - tsec = (time_t)sec; - if ((sec > 0 && tsec < 0) || (sec < 0 && (double)tsec > sec)) { - out_of_range: - mrb_raisef(mrb, E_ARGUMENT_ERROR, "%S out of Time range", mrb_float_value(mrb, sec)); - } tm = (struct mrb_time *)mrb_malloc(mrb, sizeof(struct mrb_time)); - tm->sec = tsec; - tm->usec = (time_t)llround((sec - tm->sec) * 1.0e6 + usec); + tm->sec = sec; + tm->usec = usec; if (tm->usec < 0) { - long sec2 = (long)NDIV(usec,1000000); /* negative div */ + long sec2 = (long)NDIV(tm->usec,1000000); /* negative div */ tm->usec -= sec2 * 1000000; tm->sec += sec2; } else if (tm->usec >= 1000000) { - long sec2 = (long)(usec / 1000000); + long sec2 = (long)(tm->usec / 1000000); tm->usec -= sec2 * 1000000; tm->sec += sec2; } tm->timezone = timezone; - time_update_datetime(mrb, tm); + time_update_datetime(mrb, tm, TRUE); return tm; } +static struct mrb_time* +time_alloc(mrb_state *mrb, mrb_value sec, mrb_value usec, enum mrb_timezone timezone) +{ + time_t tsec, tusec; + + tsec = mrb_to_time_t(mrb, sec, &tusec); + tusec += mrb_to_time_t(mrb, usec, NULL); + + return time_alloc_time(mrb, tsec, tusec, timezone); +} + +static mrb_value +mrb_time_make_time(mrb_state *mrb, struct RClass *c, time_t sec, time_t usec, enum mrb_timezone timezone) +{ + return mrb_time_wrap(mrb, c, time_alloc_time(mrb, sec, usec, timezone)); +} + static mrb_value -mrb_time_make(mrb_state *mrb, struct RClass *c, double sec, double usec, enum mrb_timezone timezone) +mrb_time_make(mrb_state *mrb, struct RClass *c, mrb_value sec, mrb_value usec, enum mrb_timezone timezone) { return mrb_time_wrap(mrb, c, time_alloc(mrb, sec, usec, timezone)); } @@ -293,24 +361,24 @@ mrb_time_make(mrb_state *mrb, struct RClass *c, double sec, double usec, enum mr static struct mrb_time* current_mrb_time(mrb_state *mrb) { + struct mrb_time tmzero = {0}; struct mrb_time *tm; + time_t sec, usec; - tm = (struct mrb_time *)mrb_malloc(mrb, sizeof(*tm)); -#if defined(TIME_UTC) +#if defined(TIME_UTC) && !defined(__ANDROID__) { struct timespec ts; if (timespec_get(&ts, TIME_UTC) == 0) { - mrb_free(mrb, tm); mrb_raise(mrb, E_RUNTIME_ERROR, "timespec_get() failed for unknown reasons"); } - tm->sec = ts.tv_sec; - tm->usec = ts.tv_nsec / 1000; + sec = ts.tv_sec; + usec = ts.tv_nsec / 1000; } #elif defined(NO_GETTIMEOFDAY) { static time_t last_sec = 0, last_usec = 0; - tm->sec = time(NULL); + sec = time(NULL); if (tm->sec != last_sec) { last_sec = tm->sec; last_usec = 0; @@ -319,19 +387,22 @@ current_mrb_time(mrb_state *mrb) /* add 1 usec to differentiate two times */ last_usec += 1; } - tm->usec = last_usec; + usec = last_usec; } #else { struct timeval tv; gettimeofday(&tv, NULL); - tm->sec = tv.tv_sec; - tm->usec = tv.tv_usec; + sec = tv.tv_sec; + usec = tv.tv_usec; } #endif + tm = (struct mrb_time *)mrb_malloc(mrb, sizeof(*tm)); + *tm = tmzero; + tm->sec = sec; tm->usec = usec; tm->timezone = MRB_TIMEZONE_LOCAL; - time_update_datetime(mrb, tm); + time_update_datetime(mrb, tm, TRUE); return tm; } @@ -343,15 +414,23 @@ mrb_time_now(mrb_state *mrb, mrb_value self) return mrb_time_wrap(mrb, mrb_class_ptr(self), current_mrb_time(mrb)); } +MRB_API mrb_value +mrb_time_at(mrb_state *mrb, time_t sec, time_t usec, enum mrb_timezone zone) +{ + return mrb_time_make_time(mrb, mrb_class_get(mrb, "Time"), sec, usec, zone); +} + /* 15.2.19.6.1 */ /* Creates an instance of time at the given time in seconds, etc. */ static mrb_value -mrb_time_at(mrb_state *mrb, mrb_value self) +mrb_time_at_m(mrb_state *mrb, mrb_value self) { - mrb_float f, f2 = 0; + mrb_value sec; + mrb_value usec = mrb_fixnum_value(0); + + mrb_get_args(mrb, "o|o", &sec, &usec); - mrb_get_args(mrb, "f|f", &f, &f2); - return mrb_time_make(mrb, mrb_class_ptr(self), f, f2, MRB_TIMEZONE_LOCAL); + return mrb_time_make(mrb, mrb_class_ptr(self), sec, usec, MRB_TIMEZONE_LOCAL); } static struct mrb_time* @@ -388,7 +467,7 @@ time_mktime(mrb_state *mrb, mrb_int ayear, mrb_int amonth, mrb_int aday, mrb_raise(mrb, E_ARGUMENT_ERROR, "Not a valid time."); } - return time_alloc(mrb, (double)nowsecs, (double)ausec, timezone); + return time_alloc_time(mrb, nowsecs, ausec, timezone); } /* 15.2.19.6.2 */ @@ -474,18 +553,19 @@ mrb_time_cmp(mrb_state *mrb, mrb_value self) static mrb_value mrb_time_plus(mrb_state *mrb, mrb_value self) { - mrb_float f; + mrb_value o; struct mrb_time *tm; + time_t sec, usec; - mrb_get_args(mrb, "f", &f); + mrb_get_args(mrb, "o", &o); tm = time_get_ptr(mrb, self); - return mrb_time_make(mrb, mrb_obj_class(mrb, self), (double)tm->sec+f, (double)tm->usec, tm->timezone); + sec = mrb_to_time_t(mrb, o, &usec); + return mrb_time_make_time(mrb, mrb_obj_class(mrb, self), tm->sec+sec, tm->usec+usec, tm->timezone); } static mrb_value mrb_time_minus(mrb_state *mrb, mrb_value self) { - mrb_float f; mrb_value other; struct mrb_time *tm, *tm2; @@ -493,13 +573,22 @@ mrb_time_minus(mrb_state *mrb, mrb_value self) tm = time_get_ptr(mrb, self); tm2 = DATA_CHECK_GET_PTR(mrb, other, &mrb_time_type, struct mrb_time); if (tm2) { - f = (mrb_float)(tm->sec - tm2->sec) - + (mrb_float)(tm->usec - tm2->usec) / 1.0e6; +#ifndef MRB_WITHOUT_FLOAT + mrb_float f; + f = (mrb_sec)(tm->sec - tm2->sec) + + (mrb_sec)(tm->usec - tm2->usec) / 1.0e6; return mrb_float_value(mrb, f); +#else + mrb_int f; + f = tm->sec - tm2->sec; + if (tm->usec < tm2->usec) f--; + return mrb_fixnum_value(f); +#endif } else { - mrb_get_args(mrb, "f", &f); - return mrb_time_make(mrb, mrb_obj_class(mrb, self), (double)tm->sec-f, (double)tm->usec, tm->timezone); + time_t sec, usec; + sec = mrb_to_time_t(mrb, other, &usec); + return mrb_time_make_time(mrb, mrb_obj_class(mrb, self), tm->sec-sec, tm->usec-usec, tm->timezone); } } @@ -572,10 +661,9 @@ mrb_time_asctime(mrb_state *mrb, mrb_value self) #else char buf[256]; - len = snprintf(buf, sizeof(buf), "%s %s %02d %02d:%02d:%02d %s%d", + len = snprintf(buf, sizeof(buf), "%s %s %2d %02d:%02d:%02d %.4d", wday_names[d->tm_wday], mon_names[d->tm_mon], d->tm_mday, d->tm_hour, d->tm_min, d->tm_sec, - tm->timezone == MRB_TIMEZONE_UTC ? "UTC " : "", d->tm_year + 1900); #endif return mrb_str_new(mrb, buf, len); @@ -616,7 +704,7 @@ mrb_time_getutc(mrb_state *mrb, mrb_value self) tm2 = (struct mrb_time *)mrb_malloc(mrb, sizeof(*tm)); *tm2 = *tm; tm2->timezone = MRB_TIMEZONE_UTC; - time_update_datetime(mrb, tm2); + time_update_datetime(mrb, tm2, TRUE); return mrb_time_wrap(mrb, mrb_obj_class(mrb, self), tm2); } @@ -631,7 +719,7 @@ mrb_time_getlocal(mrb_state *mrb, mrb_value self) tm2 = (struct mrb_time *)mrb_malloc(mrb, sizeof(*tm)); *tm2 = *tm; tm2->timezone = MRB_TIMEZONE_LOCAL; - time_update_datetime(mrb, tm2); + time_update_datetime(mrb, tm2, TRUE); return mrb_time_wrap(mrb, mrb_obj_class(mrb, self), tm2); } @@ -709,7 +797,7 @@ mrb_time_localtime(mrb_state *mrb, mrb_value self) tm = time_get_ptr(mrb, self); tm->timezone = MRB_TIMEZONE_LOCAL; - time_update_datetime(mrb, tm); + time_update_datetime(mrb, tm, FALSE); return self; } @@ -757,7 +845,7 @@ mrb_time_sec(mrb_state *mrb, mrb_value self) return mrb_fixnum_value(tm->datetime.tm_sec); } - +#ifndef MRB_WITHOUT_FLOAT /* 15.2.19.7.24 */ /* Returns a Float with the time since the epoch in seconds. */ static mrb_value @@ -768,32 +856,37 @@ mrb_time_to_f(mrb_state *mrb, mrb_value self) tm = time_get_ptr(mrb, self); return mrb_float_value(mrb, (mrb_float)tm->sec + (mrb_float)tm->usec/1.0e6); } +#endif /* 15.2.19.7.25 */ -/* Returns a Fixnum with the time since the epoch in seconds. */ +/* Returns an Integer with the time since the epoch in seconds. */ static mrb_value mrb_time_to_i(mrb_state *mrb, mrb_value self) { struct mrb_time *tm; tm = time_get_ptr(mrb, self); +#ifndef MRB_WITHOUT_FLOAT if (tm->sec > MRB_INT_MAX || tm->sec < MRB_INT_MIN) { return mrb_float_value(mrb, (mrb_float)tm->sec); } +#endif return mrb_fixnum_value((mrb_int)tm->sec); } /* 15.2.19.7.26 */ -/* Returns a Float with the time since the epoch in microseconds. */ +/* Returns an Integer with the time since the epoch in microseconds. */ static mrb_value mrb_time_usec(mrb_state *mrb, mrb_value self) { struct mrb_time *tm; tm = time_get_ptr(mrb, self); +#ifndef MRB_WITHOUT_FLOAT if (tm->usec > MRB_INT_MAX || tm->usec < MRB_INT_MIN) { return mrb_float_value(mrb, (mrb_float)tm->usec); } +#endif return mrb_fixnum_value((mrb_int)tm->usec); } @@ -806,7 +899,7 @@ mrb_time_utc(mrb_state *mrb, mrb_value self) tm = time_get_ptr(mrb, self); tm->timezone = MRB_TIMEZONE_UTC; - time_update_datetime(mrb, tm); + time_update_datetime(mrb, tm, FALSE); return self; } @@ -821,6 +914,46 @@ mrb_time_utc_p(mrb_state *mrb, mrb_value self) return mrb_bool_value(tm->timezone == MRB_TIMEZONE_UTC); } +static size_t +time_to_s_utc(mrb_state *mrb, struct mrb_time *tm, char *buf, size_t buf_len) +{ + return strftime(buf, buf_len, TO_S_FMT "UTC", &tm->datetime); +} + +static size_t +time_to_s_local(mrb_state *mrb, struct mrb_time *tm, char *buf, size_t buf_len) +{ +#if defined(_MSC_VER) && _MSC_VER < 1900 || defined(__MINGW64__) || defined(__MINGW32__) + struct tm datetime = {0}; + time_t utc_sec = timegm(&tm->datetime); + size_t len; + int offset; + + if (utc_sec == (time_t)-1) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "Not a valid time."); + } + offset = abs((int)(utc_sec - tm->sec) / 60); + datetime.tm_year = 100; + datetime.tm_hour = offset / 60; + datetime.tm_min = offset % 60; + len = strftime(buf, buf_len, TO_S_FMT, &tm->datetime); + buf[len++] = utc_sec < tm->sec ? '-' : '+'; + + return len + strftime(buf + len, buf_len - len, "%H%M", &datetime); +#else + return strftime(buf, buf_len, TO_S_FMT "%z", &tm->datetime); +#endif +} + +static mrb_value +mrb_time_to_s(mrb_state *mrb, mrb_value self) +{ + char buf[64]; + struct mrb_time *tm = time_get_ptr(mrb, self); + mrb_bool utc = tm->timezone == MRB_TIMEZONE_UTC; + size_t len = (utc ? time_to_s_utc : time_to_s_local)(mrb, tm, buf, sizeof(buf)); + return mrb_str_new(mrb, buf, len); +} void mrb_mruby_time_gem_init(mrb_state* mrb) @@ -830,7 +963,7 @@ mrb_mruby_time_gem_init(mrb_state* mrb) tc = mrb_define_class(mrb, "Time", mrb->object_class); MRB_SET_INSTANCE_TT(tc, MRB_TT_DATA); mrb_include_module(mrb, tc, mrb_module_get(mrb, "Comparable")); - mrb_define_class_method(mrb, tc, "at", mrb_time_at, MRB_ARGS_ARG(1, 1)); /* 15.2.19.6.1 */ + mrb_define_class_method(mrb, tc, "at", mrb_time_at_m, MRB_ARGS_ARG(1, 1)); /* 15.2.19.6.1 */ mrb_define_class_method(mrb, tc, "gm", mrb_time_gm, MRB_ARGS_ARG(1,6)); /* 15.2.19.6.2 */ mrb_define_class_method(mrb, tc, "local", mrb_time_local, MRB_ARGS_ARG(1,6)); /* 15.2.19.6.3 */ mrb_define_class_method(mrb, tc, "mktime", mrb_time_local, MRB_ARGS_ARG(1,6));/* 15.2.19.6.4 */ @@ -841,8 +974,8 @@ mrb_mruby_time_gem_init(mrb_state* mrb) mrb_define_method(mrb, tc, "<=>" , mrb_time_cmp , MRB_ARGS_REQ(1)); /* 15.2.19.7.1 */ mrb_define_method(mrb, tc, "+" , mrb_time_plus , MRB_ARGS_REQ(1)); /* 15.2.19.7.2 */ mrb_define_method(mrb, tc, "-" , mrb_time_minus , MRB_ARGS_REQ(1)); /* 15.2.19.7.3 */ - mrb_define_method(mrb, tc, "to_s" , mrb_time_asctime, MRB_ARGS_NONE()); - mrb_define_method(mrb, tc, "inspect", mrb_time_asctime, MRB_ARGS_NONE()); + mrb_define_method(mrb, tc, "to_s" , mrb_time_to_s , MRB_ARGS_NONE()); + mrb_define_method(mrb, tc, "inspect", mrb_time_to_s , MRB_ARGS_NONE()); mrb_define_method(mrb, tc, "asctime", mrb_time_asctime, MRB_ARGS_NONE()); /* 15.2.19.7.4 */ mrb_define_method(mrb, tc, "ctime" , mrb_time_asctime, MRB_ARGS_NONE()); /* 15.2.19.7.5 */ mrb_define_method(mrb, tc, "day" , mrb_time_day , MRB_ARGS_NONE()); /* 15.2.19.7.6 */ @@ -862,7 +995,9 @@ mrb_mruby_time_gem_init(mrb_state* mrb) mrb_define_method(mrb, tc, "sec" , mrb_time_sec, MRB_ARGS_NONE()); /* 15.2.19.7.23 */ mrb_define_method(mrb, tc, "to_i", mrb_time_to_i, MRB_ARGS_NONE()); /* 15.2.19.7.25 */ +#ifndef MRB_WITHOUT_FLOAT mrb_define_method(mrb, tc, "to_f", mrb_time_to_f, MRB_ARGS_NONE()); /* 15.2.19.7.24 */ +#endif mrb_define_method(mrb, tc, "usec", mrb_time_usec, MRB_ARGS_NONE()); /* 15.2.19.7.26 */ mrb_define_method(mrb, tc, "utc" , mrb_time_utc, MRB_ARGS_NONE()); /* 15.2.19.7.27 */ mrb_define_method(mrb, tc, "utc?", mrb_time_utc_p,MRB_ARGS_NONE()); /* 15.2.19.7.28 */ |
