From 5db9aa4936acc4683c48b5153f99a955fe663a35 Mon Sep 17 00:00:00 2001 From: Chuck Remes Date: Fri, 10 Feb 2017 16:49:47 -0600 Subject: [PATCH 1/2] Some minor instrumentation to the Time classes so we can debug why wday returns a different value on darwin versus freebsd for the same inputs. To see the difference, run: bin/rbx time_test2.rb I also recommend checking this out on linux and running it there. Unfortunately linux has yet another set of definitions for various time_t fields so the printf's need updating in certain cases. I didn't feel like adding another #elsif for linux so we could have all code compile cleanly everywhere. --- machine/class/time.cpp | 71 ++++++ machine/class/time.hpp | 1 + machine/class/time.rb | 521 +++++++++++++++++++++++++++++++++++++++++ machine/util/time64.c | 49 +++- time_test.rb | 7 + time_test2.rb | 9 + 6 files changed, 657 insertions(+), 1 deletion(-) create mode 100644 machine/class/time.rb create mode 100644 time_test.rb create mode 100644 time_test2.rb diff --git a/machine/class/time.cpp b/machine/class/time.cpp index a9838f9fd7..ae2d95bde5 100644 --- a/machine/class/time.cpp +++ b/machine/class/time.cpp @@ -120,8 +120,10 @@ namespace rubinius { time64_t seconds = -1; if(CBOOL(from_gmt) || !offset->nil_p()) { + printf("from_array, call ::timegm64\n"); seconds = ::timegm64(&tm); } else { + printf("from_array, call tzset and ::timelocal64\n"); tzset(); seconds = ::timelocal64(&tm); } @@ -160,18 +162,77 @@ namespace rubinius { return tm; } + void Time::print_tm64(struct tm64* tm) { +#if defined(__FreeBSD__) + printf("tm64: sec [%d] min [%d] hour [%d] mday [%d] mon [%d] year [%ld] wday [%d] yday [%d] isdst [%d] zone [%s]\n", + tm->tm_sec, + tm->tm_min, + tm->tm_hour, + tm->tm_mday, + tm->tm_mon + 1, + tm->tm_year, + tm->tm_wday, + tm->tm_yday + 1, + tm->tm_isdst, + tm->tm_zone); +#else + printf("tm64: sec [%d] min [%d] hour [%d] mday [%d] mon [%d] year [%lld] wday [%d] yday [%d] isdst [%d] zone [%s]\n", + tm->tm_sec, + tm->tm_min, + tm->tm_hour, + tm->tm_mday, + tm->tm_mon + 1, + tm->tm_year, + tm->tm_wday, + tm->tm_yday + 1, + tm->tm_isdst, + tm->tm_zone); +#endif + } + struct tm64 Time::get_tm() { time64_t seconds = sec(); struct tm64 tm; if(Fixnum* off = try_as(offset())) { +#if defined(__FreeBSD__) + printf("Time::get_tm, add offset [%lld] to seconds [%ld]\n", off->to_long_long(), seconds); +#else + printf("Time::get_tm, add offset [%lld] to seconds [%lld]\n", off->to_long_long(), seconds); +#endif seconds += off->to_long_long(); gmtime64_r(&seconds, &tm); +#if defined(__FreeBSD__) + printf("Time::get_tm, called gmtime64_r with [%ld]\n", seconds); +#else + printf("Time::get_tm, called gmtime64_r with [%lld]\n", seconds); +#endif + print_tm64(&tm); } else if(gmt_p()) { +#if defined(__FreeBSD__) + printf("Time::get_tm, calling gmtime64_r with [%ld]\n", seconds); +#else + printf("Time::get_tm, calling gmtime64_r with [%lld]\n", seconds); +#endif gmtime64_r(&seconds, &tm); + printf("Time::get_tm, called gmtime64_r\n"); + print_tm64(&tm); } else { + printf("Time::get_tm, calling tzset\n"); tzset(); +#if defined(__FreeBSD__) + printf("Time::get_tm, tzname [%s] timezone [%s] daylight [not-defined-on-freebsd]\n", *tzname, getenv("TZ")); +#else + printf("Time::get_tm, tzname [%s] timezone [%ld] daylight [%d]\n", *tzname, timezone, daylight); +#endif +#if defined(__FreeBSD__) + printf("Time::get_tm, calling tzset and localtime64_r with [%ld]\n", seconds); +#else + printf("Time::get_tm, calling tzset and localtime64_r with [%lld]\n", seconds); +#endif localtime64_r(&seconds, &tm); + printf("Time::get_tm, called localtime64_r, tzname [%s] ENV['TZ']=[%s]\n", *tzname, getenv("TZ")); + print_tm64(&tm); } return tm; @@ -179,8 +240,10 @@ namespace rubinius { Object* Time::utc_offset(STATE) { if(gmt_p()) { + printf("utc_offset, is_gmt is true, returning 0\n"); return Fixnum::from(0); } else if(!offset()->nil_p()) { + printf("utc_offset, is_gmt is false and offset is non-nil, returning offset [%ld]\n", ((Fixnum*)offset())->to_native()); return offset(); } @@ -189,27 +252,34 @@ namespace rubinius { #ifdef HAVE_TM_NAME struct tm tm = get_tm(); off = -tm.tm_tzadj; + printf("utc_offset, HAVE_TM_NAME\n"); #else /* !HAVE_TM_NAME */ #ifdef HAVE_TM_ZONE #ifdef HAVE_TM_GMTOFF struct tm64 tm = get_tm(); off = tm.tm_gmtoff; + printf("utc_offset, HAVE_TM_ZONE && HAVE_TM_GMTOFF\n"); #else off = _timezone; + printf("utc_offset, HAVE_TM_ZONE && !HAVE_TM_NAME\n"); #endif #else /* !HAVE_TM_ZONE */ #if HAVE_VAR_TIMEZONE #if HAVE_VAR_ALTZONE off = -(daylight ? timezone : altzone); + printf("utc_offset, !HAVE_TM_ZONE && HAVE_VAR_TIMEZONE && HAVE_VAR_ALTZONE\n"); #else off = -timezone; + printf("utc_offset, !HAVE_TM_ZONE && HAVE_VAR_TIMEZONE && !HAVE_VAR_ALTZONE\n"); #endif #else /* !HAVE_VAR_TIMEZONE */ #ifdef HAVE_GETTIMEOFDAY gettimeofday(&tv, &zone); off = -zone.tz_minuteswest * 60; + printf("utc_offset, !HAVE_VAR_TIMEZONE\n"); #else /* no timezone info, then calc by myself */ + printf("utc_offset, calc ourselves\n"); { struct tm utc; time_t now; @@ -221,6 +291,7 @@ namespace rubinius { #endif /* !HAVE_VAR_TIMEZONE */ #endif /* !HAVE_TM_ZONE */ #endif /* !HAVE_TM_NAME */ + printf("utc_offset, returning [%ld]\n", off); return Fixnum::from(off); } diff --git a/machine/class/time.hpp b/machine/class/time.hpp index 375b81078c..3ebbf7446f 100644 --- a/machine/class/time.hpp +++ b/machine/class/time.hpp @@ -30,6 +30,7 @@ namespace rubinius { private: String* locale_string(STATE, const char* data); + void print_tm64(struct tm64* tm); public: static void bootstrap(STATE); diff --git a/machine/class/time.rb b/machine/class/time.rb new file mode 100644 index 0000000000..334ba8bf17 --- /dev/null +++ b/machine/class/time.rb @@ -0,0 +1,521 @@ +class Time + include Comparable + + def self.now + Rubinius.primitive :time_s_now + raise PrimitiveFailure, "Time.now primitive failed" + end + + def self.duplicate(other) + Rubinius.primitive :time_s_dup + raise ArgumentError, "descriptors reference invalid time" + end + + def self.specific(sec, nsec, from_gmt, offset) + Rubinius.primitive :time_s_specific + raise ArgumentError, "descriptors reference invalid time" + end + + def dup + self.class.duplicate(self) + end + + def seconds + Rubinius.primitive :time_seconds + raise PrimitiveFailure, "Time#second primitive failed" + end + + def usec + Rubinius.primitive :time_useconds + raise PrimitiveFailure, "Time#usec primitive failed" + end + + def to_a + Rubinius.primitive :time_decompose + raise PrimitiveFailure, "Time#to_a primitive failed" + end + + def strftime(format) + Rubinius.primitive :time_strftime + raise PrimitiveFailure, "Time#strftime primitive failed" + end + + def self.at(sec, usec=undefined) + if undefined.equal?(usec) + if sec.kind_of?(Time) + return duplicate(sec) + elsif sec.kind_of?(Integer) + return specific(sec, 0, false, nil) + end + end + + if sec.kind_of?(Time) && usec.kind_of?(Integer) + raise TypeError, "can't convert Time into an exact number" + end + + usec = 0 if undefined.equal?(usec) + + s = Rubinius::Type.coerce_to_exact_num(sec) + u = Rubinius::Type.coerce_to_exact_num(usec) + + sec = s.to_i + nsec_frac = s % 1.0 + + sec -= 1 if s < 0 && nsec_frac > 0 + nsec = (nsec_frac * 1_000_000_000 + 0.5).to_i + (u * 1000).to_i + + sec += nsec / 1_000_000_000 + nsec %= 1_000_000_000 + + specific(sec, nsec, false, nil) + end + + def self.from_array(sec, min, hour, mday, month, year, nsec, is_dst, from_gmt, utc_offset) + Rubinius.primitive :time_s_from_array + + if sec.kind_of?(String) + sec = sec.to_i + elsif nsec + sec = Rubinius::Type.coerce_to(sec || 0, Integer, :to_int) + else + s = Rubinius::Type.coerce_to_exact_num(sec || 0) + + sec = s.to_i + nsec_frac = s % 1.0 + + if s < 0 && nsec_frac > 0 + sec -= 1 + end + + nsec = (nsec_frac * 1_000_000_000 + 0.5).to_i + end + + nsec ||= 0 + sec += nsec / 1_000_000_000 + nsec %= 1_000_000_000 + + if utc_offset + utc_offset_sec = utc_offset.to_i + utc_offset_nsec = ((utc_offset % 1.0) * 1_000_000_000 + 0.5).to_i + else + utc_offset_sec = 0 + utc_offset_nsec = 0 + end + + STDERR.puts "#{sec}, #{min}, #{hour}, #{mday}, #{month}, #{year}, #{nsec}, #{is_dst}, #{from_gmt}, #{utc_offset}, #{utc_offset_sec}, #{utc_offset_nsec}" + from_array(sec, min, hour, mday, month, year, nsec, is_dst, from_gmt, utc_offset, utc_offset_sec, utc_offset_nsec) + end + + def self.new(year=undefined, month=nil, day=nil, hour=nil, minute=nil, second=nil, utc_offset=nil) + if undefined.equal?(year) + now + elsif utc_offset == nil + compose(:local, year, month, day, hour, minute, second) + else + compose(Rubinius::Type.coerce_to_utc_offset(utc_offset), year, month, day, hour, minute, second) + end + end + + def inspect + if @is_gmt + str = strftime("%Y-%m-%d %H:%M:%S UTC") + else + str = strftime("%Y-%m-%d %H:%M:%S %z") + end + + str.force_encoding Encoding::US_ASCII + end + + alias_method :to_s, :inspect + + def nsec + Rubinius.primitive :time_nseconds + raise PrimitiveFailure, "Time#nsec primitive failed" + end + + def nsec=(nanoseconds) + Rubinius.primitive :time_set_nseconds + raise PrimitiveFailure, "Time#nsec= primitive failed" + end + private :nsec= + + alias_method :tv_nsec, :nsec + + def subsec + if nsec == 0 + 0 + else + Rational(nsec, 1_000_000_000) + end + end + + def sunday? + wday == 0 + end + + def monday? + wday == 1 + end + + def tuesday? + wday == 2 + end + + def wednesday? + wday == 3 + end + + def thursday? + wday == 4 + end + + def friday? + wday == 5 + end + + def saturday? + wday == 6 + end + + def to_r + (seconds + subsec).to_r + end + + def to_f + to_r.to_f + end + + def +(other) + raise TypeError, 'time + time?' if other.kind_of?(Time) + + case other = Rubinius::Type.coerce_to_exact_num(other) + when Integer + other_sec = other + other_nsec = 0 + else + other_sec, nsec_frac = other.divmod(1) + other_nsec = (nsec_frac * 1_000_000_000).to_i + end + + # Don't use self.class, MRI doesn't honor subclasses here + Time.specific(seconds + other_sec, nsec + other_nsec, @is_gmt, @offset) + end + + def -(other) + if other.kind_of?(Time) + return (seconds - other.seconds) + ((nsec - other.nsec) * 0.000000001) + end + + case other = Rubinius::Type.coerce_to_exact_num(other) + when Integer + other_sec = other + other_nsec = 0 + else + other_sec, nsec_frac = other.divmod(1) + other_nsec = (nsec_frac * 1_000_000_000 + 0.5).to_i + end + + # Don't use self.class, MRI doesn't honor subclasses here + Time.specific(seconds - other_sec, nsec - other_nsec, @is_gmt, @offset) + end + + def localtime(offset=nil) + @is_gmt = false + @offset = nil + @decomposed = nil + + if offset + @offset = Rubinius::Type.coerce_to_utc_offset(offset) + @zone = nil + else + @offset = gmt_offset + @zone = Rubinius.invoke_primitive(:time_env_zone, self) + end + + self + end + + def getlocal(offset=nil) + dup.localtime(offset) + end + + def eql?(other) + other.kind_of?(Time) and seconds == other.seconds and nsec == other.nsec + end + + def <=>(other) + if other.kind_of? Time + (seconds <=> other.seconds).nonzero? or (nsec <=> other.nsec) + else + r = (other <=> self) + return nil if r == nil + return -1 if r > 0 + return 1 if r < 0 + 0 + end + end + + # + # Rounds sub seconds to a given precision in decimal digits + # + # Returns a new time object. + # + # places should be nonnegative, it is 0 by default. + # + def round(places = 0) + return dup if nsec == 0 + + roundable_time = (to_i + subsec.to_r).round(places) + + sec = roundable_time.floor + nano = ((roundable_time - sec) * 1_000_000_000).floor + + Time.specific(sec, nano, @is_gmt, @offset) + end + + class << self + def compose_deal_with_year(year) + year + end + private :compose_deal_with_year + end + + #-- + # TODO: doesn't load ivars + #++ + + def self._load(data) + raise TypeError, 'marshaled time format differ' unless data.bytesize == 8 + + major, minor = data.unpack 'VV' + + if (major & (1 << 31)) == 0 then + at major, minor + else + major &= ~(1 << 31) + + is_gmt = (major >> 30) & 0x1 + year = ((major >> 14) & 0xffff) + 1900 + mon = ((major >> 10) & 0xf) + 1 + mday = (major >> 5) & 0x1f + hour = major & 0x1f + + min = (minor >> 26) & 0x3f + sec = (minor >> 20) & 0x3f + isdst = false + + usec = minor & 0xfffff + + time = gm year, mon, mday, hour, min, sec, usec + time.localtime if is_gmt.zero? + time + end + end + + class << self + private :_load + end + + #-- + # TODO: doesn't dump ivars + #++ + + def _dump(limit = nil) + tm = getgm.to_a + + if (year & 0xffff) != year || year < 1900 then + raise ArgumentError, "year too big to marshal: #{year}" + end + + gmt = @is_gmt ? 1 : 0 + + major = 1 << 31 | # 1 bit + gmt << 30 | # 1 bit + (tm[5] - 1900) << 14 | # 16 bits + (tm[4] - 1) << 10 | # 4 bits + tm[3] << 5 | # 5 bits + tm[2] # 5 bits + minor = tm[1] << 26 | # 6 bits + tm[0] << 20 | # 6 bits + usec # 20 bits + + [major, minor].pack 'VV' + end + + private :_dump + + def self.compose(offset, p1, p2=nil, p3=nil, p4=nil, p5=nil, p6=nil, p7=nil, + yday=undefined, is_dst=undefined, tz=undefined) + if undefined.equal?(tz) + unless undefined.equal?(is_dst) + raise ArgumentError, "wrong number of arguments (9 for 1..8)" + end + + y = p1 + m = p2 + d = p3 + hr = p4 + min = p5 + sec = p6 + usec = p7 + is_dst = -1 + else + y = p6 + m = p5 + d = p4 + hr = p3 + min = p2 + sec = p1 + usec = 0 + is_dst = is_dst ? 1 : 0 + end + + if m.kind_of?(String) or m.respond_to?(:to_str) + m = StringValue(m) + m = MonthValue[m.upcase] || m.to_i + + raise ArgumentError, "month argument out of range" unless m + else + m = Rubinius::Type.coerce_to(m || 1, Integer, :to_int) + end + + y = y.kind_of?(String) ? y.to_i : Rubinius::Type.coerce_to(y, Integer, :to_int) + d = d.kind_of?(String) ? d.to_i : Rubinius::Type.coerce_to(d || 1, Integer, :to_int) + hr = hr.kind_of?(String) ? hr.to_i : Rubinius::Type.coerce_to(hr || 0, Integer, :to_int) + min = min.kind_of?(String) ? min.to_i : Rubinius::Type.coerce_to(min || 0, Integer, :to_int) + + nsec = nil + + if usec.kind_of?(String) + nsec = usec.to_i * 1000 + elsif usec + nsec = (usec * 1000).to_i + end + + y = compose_deal_with_year(y) + + case offset + when :utc + is_dst = -1 + is_utc = true + offset = nil + when :local + is_utc = false + offset = nil + else + is_dst = -1 + is_utc = false + end + + from_array(sec, min, hr, d, m, y, nsec, is_dst, is_utc, offset) + end + + def self.local(*args) + compose(:local, *args) + end + + def self.gm(*args) + compose(:utc, *args) + end + + def succ + self + 1 + end + + def asctime + strftime("%a %b %e %H:%M:%S %Y") + end + + def sec + to_a[0] + end + + def min + to_a[1] + end + + def hour + to_a[2] + end + + def day + to_a[3] + end + + def mon + to_a[4] + end + + def year + to_a[5] + end + + def wday + to_a[6] + end + + def yday + to_a[7] + end + + def dst? + to_a[8] + end + + def zone + zone = to_a[9] + + if zone && Encoding.default_internal + zone.encode Encoding.default_internal + else + zone + end + end + + def gmt? + @is_gmt + end + + alias_method :to_i, :seconds + + def gmt_offset + Rubinius.primitive :time_utc_offset + raise PrimitiveFailure, "Time#gmt_offset primitive failed" + end + + def gmtime + unless @is_gmt + @is_gmt = true + @offset = nil + @decomposed = nil + end + + self + end + + def getgm + dup.gmtime + end + + def hash + seconds ^ usec + end + + class << self + alias_method :mktime, :local + alias_method :utc, :gm + end + + alias_method :utc?, :gmt? + alias_method :month, :mon + alias_method :ctime, :asctime + alias_method :mday, :day + alias_method :to_i, :seconds + alias_method :tv_sec, :seconds + alias_method :tv_usec, :usec + alias_method :utc, :gmtime + alias_method :isdst, :dst? + alias_method :utc_offset, :gmt_offset + alias_method :gmtoff, :gmt_offset + alias_method :getutc, :getgm +end diff --git a/machine/util/time64.c b/machine/util/time64.c index 886354d961..255f3f4960 100644 --- a/machine/util/time64.c +++ b/machine/util/time64.c @@ -198,6 +198,22 @@ int tm64_to_tm(struct tm64* tm64, struct tm* tm) { return 0; } +void print_tm(struct tm* tm) { + printf("tm: sec [%d] min [%d] hour [%d] mday [%d] mon [%d] wday [%d] yday [%d] isdst [%d] gmtoff [%lu] zone [%s] year [%d]\n", + tm->tm_sec, + tm->tm_min, + tm->tm_hour, + tm->tm_mday, + tm->tm_mon, + tm->tm_wday, + tm->tm_yday, + tm->tm_isdst, + tm->tm_gmtoff, + tm->tm_zone, + tm->tm_year + ); +} + /* Copy a regular struct tm into a struct tm64. This is * done after passing it into the standard system functions */ @@ -288,6 +304,7 @@ time64_t timestamp64_mktime(time_t (*func)(struct tm*), struct tm* tm) { time_t time; time = func(tm); + printf("timestamp64_mktime, time after func %ld, errno [%d], errstr [%s]\n", time, errno, strerror(errno)); if(time == -1 && tm->tm_isdst != 0 && (errno == EINVAL || errno == EOVERFLOW)) { /* * Retry one time because of perhaps DST change or inappropriate value of tm_isdst. @@ -299,11 +316,14 @@ time64_t timestamp64_mktime(time_t (*func)(struct tm*), struct tm* tm) { int orig_tm_isdst = tm->tm_isdst; tm->tm_isdst = 0; time = func(tm); + printf("timestamp64_mktime, RETRY, time after func %ld\n", time); if(time == -1 && (errno == EINVAL || errno == EOVERFLOW)) { /* Restore the original value, adjusting tm_isdst didn't help. */ tm->tm_isdst = orig_tm_isdst; + printf("timestamp64_mktime, restore original value, adjustment failed\n"); } } + printf("timestamp64_mktime, returning %ld\n", time); return (time64_t) time; } @@ -314,18 +334,26 @@ time64_t timestamp64(time_t (*func)(struct tm*), struct tm64* tm64) { /* If this succeeds it fits in a standard struct tm */ if(tm64_to_tm(tm64, &tm) == 0) { + printf("timestamp64, fits in standard 'struct tm'\n"); + print_tm(&tm); time = timestamp64_mktime(func, &tm); + printf("timestamp64, call timestamp64_mktime\n"); + print_tm(&tm); /* * This still would fail for 1969-12-31 23:59:59 GMT, but * the fallback code will properly handle that case anyway. */ if(time != -1) { + printf("timestamp64, overflow on tm, copy back\n"); /* Copy back updated information */ tm_to_tm64(&tm, tm64); + print_tm(&tm); return time; } } + printf("timestamp64, time will not fit into standard struct tm\n"); + /* * Convert the struct to a year that is day compatible with * the current day. @@ -334,34 +362,51 @@ time64_t timestamp64(time_t (*func)(struct tm*), struct tm64* tm64) { int64_t year = tm64->tm_year; if(year < 1902) { + printf("timestamp64, year < 1902\n"); /* For years below the 32 bit size time_t value we need to use * the lower comparable years */ int day = day_of_week(year, tm64->tm_mon, 1); if(tm64->tm_mon == 2 && leap_year(year)) { + printf("timestamp64, leap month adjustment\n"); tm.tm_year = lower_leap_month_table[day] - 1900; } else { + printf("timestamp64, common month\n"); tm.tm_year = lower_common_month_table[tm.tm_mon][day] - 1900; } } else if(year > 2037) { + printf("timestamp64, year > 2037\n"); /* For years above the 32 bit size time_t value we need to use * the lower comparable years */ int day = day_of_week(year, tm64->tm_mon, 1); if(tm64->tm_mon == 2 && leap_year(year)) { + printf("timestamp64, leap month adjustment\n"); tm.tm_year = higher_leap_month_table[day] - 1900; } else { + printf("timestamp64, common month\n"); tm.tm_year = higher_common_month_table[tm.tm_mon][day] - 1900; } } + print_tm(&tm); time = timestamp64_mktime(func, &tm); tm_to_tm64(&tm, tm64); if(year != tm64->tm_year) { +#if defined(__FreeBSD__) + printf("timestamp64, year != tm64->tm_year, [%ld] != [%ld]\n", year, tm64->tm_year); +#else + printf("timestamp64, year != tm64->tm_year, [%lld] != [%lld]\n", year, tm64->tm_year); +#endif /* Correct for the changed year to do the mktime computation */ time += year_diff_to_seconds(tm64->tm_year, year, day_before_leap(tm64)); } tm64->tm_year = year; +#if defined(__FreeBSD__) + printf("timestamp64, returning time [%ld]\n", time); +#else + printf("timestamp64, returning time [%lld]\n", time); +#endif return time; } @@ -370,9 +415,11 @@ time64_t mktime64(struct tm64* tm64) { } time64_t timelocal64(struct tm64* tm64) { -#if defined( __OpenBSD__) || defined(__FreeBSD__) +#if defined( __OpenBSD__) || defined(__FreeBSD__) + printf("timelocal64, call timestamp64(timelocal)\n"); return timestamp64(timelocal, tm64); #else + printf("timelocal64, call timestamp64(mktime)\n"); return timestamp64(mktime, tm64); #endif } diff --git a/time_test.rb b/time_test.rb new file mode 100644 index 0000000000..80f9321744 --- /dev/null +++ b/time_test.rb @@ -0,0 +1,7 @@ +ENV['TZ']="America/New_York" + +puts "calling Time.local" + +t1 = Time.local(0,30,1,30,10,2005,0,0,false,"") + + diff --git a/time_test2.rb b/time_test2.rb new file mode 100644 index 0000000000..43b173d793 --- /dev/null +++ b/time_test2.rb @@ -0,0 +1,9 @@ +ENV['TZ']="America/New_York" + +puts "\n\n\ncalling Time.local" + +t1 = Time.local(1801, 12, 31, 23, 59, 59) + +puts "calling t1.wday" +puts t1.wday + From 1dc18dde3dfe7d5f21785f59ecf51e7aa123379e Mon Sep 17 00:00:00 2001 From: Chuck Remes Date: Fri, 10 Feb 2017 17:39:17 -0600 Subject: [PATCH 2/2] add more instrumentation --- machine/class/time.cpp | 24 +++++++++++++---------- machine/util/time64.c | 44 ++++++++++++++++++++++++++++++++++++++++++ time_test2.rb | 4 +++- 3 files changed, 61 insertions(+), 11 deletions(-) diff --git a/machine/class/time.cpp b/machine/class/time.cpp index ae2d95bde5..ad7b446b3c 100644 --- a/machine/class/time.cpp +++ b/machine/class/time.cpp @@ -164,29 +164,33 @@ namespace rubinius { void Time::print_tm64(struct tm64* tm) { #if defined(__FreeBSD__) - printf("tm64: sec [%d] min [%d] hour [%d] mday [%d] mon [%d] year [%ld] wday [%d] yday [%d] isdst [%d] zone [%s]\n", + printf("Time tm64: sec [%d] min [%d] hour [%d] mday [%d] mon [%d] wday [%d] yday [%d] isdst [%d] gmtoff [%d] zone [%s] year [%ld]\n", tm->tm_sec, tm->tm_min, tm->tm_hour, tm->tm_mday, - tm->tm_mon + 1, - tm->tm_year, + tm->tm_mon, tm->tm_wday, - tm->tm_yday + 1, + tm->tm_yday, tm->tm_isdst, - tm->tm_zone); + tm->tm_gmtoff, + tm->tm_zone, + tm->tm_year + ); #else - printf("tm64: sec [%d] min [%d] hour [%d] mday [%d] mon [%d] year [%lld] wday [%d] yday [%d] isdst [%d] zone [%s]\n", + printf("Time tm64: sec [%d] min [%d] hour [%d] mday [%d] mon [%d] wday [%d] yday [%d] isdst [%d] gmtoff [%d] zone [%s] year [%lld]\n", tm->tm_sec, tm->tm_min, tm->tm_hour, tm->tm_mday, - tm->tm_mon + 1, - tm->tm_year, + tm->tm_mon, tm->tm_wday, - tm->tm_yday + 1, + tm->tm_yday, tm->tm_isdst, - tm->tm_zone); + tm->tm_gmtoff, + tm->tm_zone, + tm->tm_year + ); #endif } diff --git a/machine/util/time64.c b/machine/util/time64.c index 255f3f4960..19640ffcc7 100644 --- a/machine/util/time64.c +++ b/machine/util/time64.c @@ -214,6 +214,38 @@ void print_tm(struct tm* tm) { ); } +void print_tm64(struct tm64* tm) { +#if defined(__FreeBSD__) + printf("util tm64: sec [%d] min [%d] hour [%d] mday [%d] mon [%d] wday [%d] yday [%d] isdst [%d] gmtoff [%d] zone [%s] year [%ld]\n", + tm->tm_sec, + tm->tm_min, + tm->tm_hour, + tm->tm_mday, + tm->tm_mon, + tm->tm_wday, + tm->tm_yday, + tm->tm_isdst, + tm->tm_gmtoff, + tm->tm_zone, + tm->tm_year + ); +#else + printf("util tm64: sec [%d] min [%d] hour [%d] mday [%d] mon [%d] wday [%d] yday [%d] isdst [%d] gmtoff [%d] zone [%s] year [%lld]\n", + tm->tm_sec, + tm->tm_min, + tm->tm_hour, + tm->tm_mday, + tm->tm_mon, + tm->tm_wday, + tm->tm_yday, + tm->tm_isdst, + tm->tm_gmtoff, + tm->tm_zone, + tm->tm_year + ); +#endif +} + /* Copy a regular struct tm into a struct tm64. This is * done after passing it into the standard system functions */ @@ -353,6 +385,7 @@ time64_t timestamp64(time_t (*func)(struct tm*), struct tm64* tm64) { } printf("timestamp64, time will not fit into standard struct tm\n"); + print_tm64(tm64); /* * Convert the struct to a year that is day compatible with @@ -387,9 +420,13 @@ time64_t timestamp64(time_t (*func)(struct tm*), struct tm64* tm64) { } } + printf("timestamp64, remaking time after adjustments\n"); print_tm(&tm); + print_tm64(tm64); time = timestamp64_mktime(func, &tm); tm_to_tm64(&tm, tm64); + print_tm(&tm); + print_tm64(tm64); if(year != tm64->tm_year) { #if defined(__FreeBSD__) @@ -407,6 +444,7 @@ time64_t timestamp64(time_t (*func)(struct tm*), struct tm64* tm64) { #else printf("timestamp64, returning time [%lld]\n", time); #endif + print_tm64(tm64); return time; } @@ -535,12 +573,18 @@ struct tm64* localtime64_r(const time64_t* time64, struct tm64* tm64) { memset(&tm, 0, sizeof(tm)); time_t time = (time_t) *time64; + printf("localtime64_r, calling localtime_r with seconds [%ld]\n", time); if(localtime_r(&time, &tm)) { + print_tm(&tm); + printf("localtime64_r, convert tm to tm64\n"); tm_to_tm64(&tm, tm64); + print_tm64(tm64); #ifndef HAVE_TM_ZONE #if HAVE_TZNAME && HAVE_DAYLIGHT + printf("localtime64_r, HAVE_TZNAME && HAVE_DAYLIGHT\n"); tm64->tm_zone = tzname[daylight && tm64->tm_isdst]; #else + printf("localtime64_r, set tm_zone to NULL\n"); tm64->tm_zone = NULL; #endif #endif diff --git a/time_test2.rb b/time_test2.rb index 43b173d793..dd4dd9896f 100644 --- a/time_test2.rb +++ b/time_test2.rb @@ -4,6 +4,8 @@ t1 = Time.local(1801, 12, 31, 23, 59, 59) -puts "calling t1.wday" +puts "\ncalling t1.wday" puts t1.wday +puts "\ncalling t1.utc_offset" +puts t1.utc_offset