floraison / et-orbi Goto Github PK
View Code? Open in Web Editor NEWTime zones for fugit and rufus-scheduler. Urbi et Orbi.
License: MIT License
Time zones for fugit and rufus-scheduler. Urbi et Orbi.
License: MIT License
Initially seen at jmettraux/rufus-scheduler#261
Replicated in https://gist.github.com/jmettraux/558216497e746da6490c11f305477e69
#<ActiveSupport::TimeZone:0x00007fc6c28a6868 @name="America/New_York", @utc_offset=nil,
@tzinfo=#<TZInfo::DataTimezone: America/New_York>>
/Users/jmettraux/.gem/ruby/2.4.2/gems/et-orbi-1.0.7/lib/et-orbi.rb:304:in `initialize':
Cannot determine timezone from #<ActiveSupport::TimeZone:0x00007fc6c28a6868
@name="America/New_York", @utc_offset=nil,
@tzinfo=#<TZInfo::DataTimezone: America/New_York>> (ArgumentError)
(secs:1508824281.618999,
utc~:"0047-10-24 05:51:21.6189990043640137",ltz~:"EST")
(etz:nil,tnz:"JST",tzid:nil,rv:"2.4.2",rp:"x86_64-darwin14",eov:"1.0.7",
rorv:"5.1.4",astz:[ActiveSupport::TimeZone, "America/New_York"],
debian:nil,centos:nil,osx:"Asia/Tokyo")
Try setting `ENV['TZ'] = 'Continent/City'` in your script (see
https://en.wikipedia.org/wiki/List_of_tz_database_time_zones)
and adding gem 'tzinfo-data'
from t.rb:14:in `new'
from t.rb:14:in `<main>'
make: *** [run] Error 1
Here's a test that reproduces the problem:
diff --git a/spec/module_spec.rb b/spec/module_spec.rb
index a0c980e..893212f 100644
--- a/spec/module_spec.rb
+++ b/spec/module_spec.rb
@@ -288,6 +288,15 @@ describe EtOrbi do
expect(t.strftime('%Y-%m-%d')).to eq(t1.strftime('%Y-%m-%d'))
expect(t.strftime('%H:%M:%S')).to eq('12:00:00')
end
+
+ it 'handles days that have ambiguous daylight savings conversions' do
+
+ Time.active_support_zone = 'America/Chicago'
+
+ t = EtOrbi.parse('2020-11-01 00:00:00')
+
+ expect(t.class).to eq(EtOrbi::EoTime)
+ end
end
end
It fails with:
Failures:
1) EtOrbi.parse when Chronic is defined handles ambiguous daylight savings conversions
Failure/Error: secs = zone.local_to_utc(local).to_f
TZInfo::AmbiguousTime:
Time: 2020-11-01 01:00:00 UTC is an ambiguous local time.
# ./lib/et-orbi/make.rb:42:in `parse'
# ./spec/module_spec.rb:296:in `block (4 levels) in <top (required)>'
I described the problem in-depth in https://gitlab.com/gitlab-org/gitlab/issues/37014#note_248974118.
Essentially, when Chronic is used, the time is advanced unnecessarily:
t = Chronic.parse('2020-11-01 00:00:00')
=> 2020-11-01 01:00:00 -0500
str = [ t.strftime('%F %T'), nil ].compact.join(' ')
=> "2020-11-01 01:00:00"
zone = TZInfo::Timezone.new("America/Chicago")
=> #<TZInfo::DataTimezone: America/Chicago>
zone.local_to_utc(t)
Traceback (most recent call last):
9: from /opt/gitlab/embedded/bin/irb:23:in `<main>'
8: from /opt/gitlab/embedded/bin/irb:23:in `load'
7: from /opt/gitlab/embedded/lib/ruby/gems/2.6.0/gems/irb-1.0.0/exe/irb:11:in `<top (required)>'
6: from (irb):35
5: from (irb):35:in `rescue in irb_binding'
4: from /opt/gitlab/embedded/lib/ruby/gems/2.6.0/gems/tzinfo-1.2.5/lib/tzinfo/timezone.rb:464:in `local_to_utc'
3: from /opt/gitlab/embedded/lib/ruby/gems/2.6.0/gems/tzinfo-1.2.5/lib/tzinfo/time_or_datetime.rb:324:in `wrap'
2: from /opt/gitlab/embedded/lib/ruby/gems/2.6.0/gems/tzinfo-1.2.5/lib/tzinfo/timezone.rb:468:in `block in local_to_utc'
1: from /opt/gitlab/embedded/lib/ruby/gems/2.6.0/gems/tzinfo-1.2.5/lib/tzinfo/timezone.rb:409:in `period_for_local'
TZInfo::AmbiguousTime (Time: 2020-11-01 01:00:00 UTC is an ambiguous local time.)
I propose two ways to solve this here:
Chronic is probably causing issues because it doesn't handle Daylight Savings well: mojombo/chronic#147
I am using rufus
, and when i run rspec
with tests, I get the following warnings:
/Users/me/.rvm/gems/ruby-2.4.2/gems/et-orbi-1.0.5/lib/et-orbi.rb:608: warning: assigned but unused variable - tu
/Users/me/.rvm/gems/ruby-2.4.2/gems/et-orbi-1.0.5/lib/et-orbi.rb:151: warning: instance variable @local_tzone_loaded_at not initialized
/Users/me/.rvm/gems/ruby-2.4.2/gems/et-orbi-1.0.5/lib/et-orbi.rb:153: warning: instance variable @local_tzone_tz not initialized
I imagine they are easy fixes, and just leftover from some change or another.
Investigate, could I allow comparison between EoTime and Time directly, I guess EoTime#< (et al) could help, but I wouldn't want to override Time#< (et al).
Triggerred by https://stackoverflow.com/questions/44841957/error-when-comparing-dates-in-sidekiq-cron
carry on...
As seen in sidekiq-cron/sidekiq-cron#276
require 'benchmark'
require 'chronic'
require 'fugit'
# https://github.com/ondrejbartas/sidekiq-cron/issues/276
crons = [ '0 8 1 * * America/Chicago' ] * 1000
time = Time.now
#EtOrbi.chronic_enabled = false
# leave Chronic parsing enabled (the default as of et-orbi 1.2.3)
puts Benchmark.measure {
crons.map { |cron| Fugit.parse_cron(cron).previous_time(time).utc }
}.to_s
#
# => 109.987803 0.493191 110.480994 (110.569114)
#
# ( ruby 2.6.5p114 (2019-10-01 revision 67812) [x86_64-darwin19]
# ( MacBook Pro mid 2015 16GB RAM
Fugit::Cron#previous_time
ends up calling https://github.com/floraison/et-orbi/blob/master/lib/et-orbi/make.rb#L106-L112 so the incoming Time
instances ends up as a String and that's where Chronic is called. Do avoid that!
Related to jmettraux/rufus-scheduler#277
I use rufus-scheduler and I am not sure where to report it but most of the stacktrace mentions this lib:
C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/et-orbi-1.1.4/lib/et-orbi/zone_aliases.rb:6:in `sub': invalid byte sequence in UTF-8 (ArgumentError)
from C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/et-orbi-1.1.4/lib/et-orbi/zone_aliases.rb:6:in `unalias'
from C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/et-orbi-1.1.4/lib/et-orbi.rb:156:in `get_tzone'
from C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/et-orbi-1.1.4/lib/et-orbi.rb:699:in `determine_local_tzone'
from C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/et-orbi-1.1.4/lib/et-orbi.rb:148:in `get_tzone'
from C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/et-orbi-1.1.4/lib/et-orbi.rb:269:in `get_tzone'
from C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/et-orbi-1.1.4/lib/et-orbi.rb:307:in `initialize'
from C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/et-orbi-1.1.4/lib/et-orbi.rb:21:in `new'
from C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/et-orbi-1.1.4/lib/et-orbi.rb:21:in `now'
from C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/et-orbi-1.1.4/lib/et-orbi.rb:259:in `now'
from C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/rufus-scheduler-3.5.2/lib/rufus/scheduler.rb:539:in `start'
from C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/rufus-scheduler-3.5.2/lib/rufus/scheduler.rb:88:in `initialize'
from C:/Users/marce/RubymineProjects/node_alert/main.rb:114:in `new'
from C:/Users/marce/RubymineProjects/node_alert/main.rb:114:in `<main>'
I am using windows 10. tzinfo is loaded inside the Gemfile.
Simplified code I used to cause this is:
scheduler = Rufus::Scheduler.new
scheduler.every '10m' do
puts '10'
end
I'm trying to parse user-given times for scheduled events, and there are certain timezones parsable by Time and DateTime that are ignored by EtOrbi.
EtOrbi.parse('7AM PST').to_s # "2021-02-19 07:00:00 +0000"
DateTime.parse('7AM PST').to_s # "2021-02-19 07:00:00 -0800"
Time.parse('7AM PST').to_s # "2021-02-19 07:00:00 -0800"
EtOrbi.parse('7AM MDT').to_s # "2021-02-19 07:00:00 +0000"
Time.parse('7AM MDT').to_s # "2021-02-19 07:00:00 -0600"
I've done a bit of research and have a suggestion to offer. Perhaps EtOrbi.extract_zone can use Date._parse
to determine if the string given has timezone info provided, and if so, use that:
def extract_zone(str)
s = str.dup
d = Date._parse(str)
if d && d[:offset]
offset = "UTC#{d[:offset] / 3600}"
s.gsub!(d[:zone], offset) if ZONES_OLSON.include?(offset)
end
# Rest of original code from method
end
This allows the ZoneOffsets parsable by Time.parse to be recognized here too.
I recognize that there are a small number of timezones affected here, mainly US and military timezones, so maybe it's not worth it? I'd definitely like to hear a maintainer's thoughts.
Thanks!
Ruby version: 2.4.1
Bundler version: 1.15.4
macOS Sierra 10.12.6
When trying to run the tests locally, the following failures occur:
Failures:
1) EtOrbi.make_time turns a Date instance into an EoTime instance
Failure/Error:
fail ArgumentError.new(
"cannot determine timezone from #{zone.inspect}" +
"\n#{render_nozone_time(s)}" +
"\n#{self.class.platform_info.sub(',debian:', ",\ndebian:")}" +
"\nTry setting `ENV['TZ'] = 'Continent/City'` in your script " +
"(see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones)" +
(defined?(TZInfo::Data) ? '' : "\nand adding gem 'tzinfo-data'")
) unless @zone
ArgumentError:
cannot determine timezone from "CDT"
(secs:1477976400.0,utc~:"0046-11-01 05:00:00.0",ltz~:"LMT")
(etz:nil,tnz:"CDT",tzid:nil,rv:"2.4.1",rp:"x86_64-darwin16",eov:"1.0.6",rorv:nil,astz:nil,
debian:nil,centos:nil,osx:"America/Chicago")
Try setting `ENV['TZ'] = 'Continent/City'` in your script (see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones)
and adding gem 'tzinfo-data'
# ./lib/et-orbi.rb:244:in `initialize'
# ./lib/et-orbi.rb:96:in `new'
# ./lib/et-orbi.rb:96:in `make_from_time'
# ./lib/et-orbi.rb:101:in `make_from_date'
# ./lib/et-orbi.rb:75:in `make_time'
# ./spec/module_spec.rb:491:in `block (5 levels) in <top (required)>'
# ./spec/spec_helper.rb:22:in `in_zone'
# ./spec/module_spec.rb:485:in `block (4 levels) in <top (required)>'
2) EtOrbi.make_time turns a String into an EoTime instance
Failure/Error: else expect(eot).to eq(exp)
expected: #<EtOrbi::EoTime:0x007f97e6387770 @seconds=1478021409.0, @zone=#<TZInfo::TimezoneProxy: America/Bahia_Banderas>, @time=nil>
got: #<EtOrbi::EoTime:0x007f97e71771c8 @seconds=1478025009.0, @zone=#<TZInfo::TimezoneProxy: America/Bahia_Banderas>, @time=nil>
(compared using ==)
Diff:
@@ -1,5 +1,5 @@
-#<EtOrbi::EoTime:0x007f97e6387770
- @seconds=1478021409.0,
+#<EtOrbi::EoTime:0x007f97e71771c8
+ @seconds=1478025009.0,
@time=nil,
@zone=#<TZInfo::TimezoneProxy: America/Bahia_Banderas>>
# ./spec/module_spec.rb:502:in `block (4 levels) in <top (required)>'
3) EtOrbi::EoTime#monthdays returns the appropriate "0#2"-like string
Failure/Error: expect(mds(local(1970, 1, 1))).to eq(%w[ 4#1 4#-5 ])
expected: ["4#1", "4#-5"]
got: ["3#5", "3#-1"]
(compared using ==)
# ./spec/time_spec.rb:266:in `block (3 levels) in <top (required)>'
Finished in 0.09694 seconds (files took 0.86364 seconds to load)
73 examples, 3 failures
Failed examples:
rspec ./spec/module_spec.rb[1:8:6] # EtOrbi.make_time turns a Date instance into an EoTime instance
rspec ./spec/module_spec.rb[1:8:7] # EtOrbi.make_time turns a String into an EoTime instance
rspec ./spec/time_spec.rb:264 # EtOrbi::EoTime#monthdays returns the appropriate "0#2"-like string
This is from running bundle exec rspec
, which I found is the expected way to run tests from the latest CI run, which is indeed passing. I'm on the same SHA as that run, too (ff967e8).
I would assume this is probably a Mac issue.
As reported for rufus-scheduler at jmettraux/rufus-scheduler#269
Reproduced on Appveyor at https://ci.appveyor.com/project/jmettraux/et-orbi/build/job/p80wb2e7flbbd6oo?fullLog=true#L202 :
[00:00:17] 2) EtOrbi.make_time turns a Date instance into an EoTime instance
[00:00:17] Failure/Error:
[00:00:17] tabbs = (-6..5)
[00:00:17] .collect { |i| (Time.now + i * 30 * 24 * 3600).zone }
[00:00:17] .uniq
[00:00:17] .sort
[00:00:17] .join('|')
[00:00:17]
[00:00:17] SystemStackError:
[00:00:17] stack level too deep
[00:00:17] # ./lib/et-orbi.rb:697:in `now'
[00:00:17] # ./lib/et-orbi.rb:697:in `block in determine_local_tzones'
[00:00:17] # ./lib/et-orbi.rb:697:in `each'
[00:00:17] # ./lib/et-orbi.rb:697:in `collect'
[00:00:17] # ./lib/et-orbi.rb:697:in `determine_local_tzones'
[00:00:17] # ./lib/et-orbi.rb:630:in `determine_local_tzone'
[00:00:17] # ./lib/et-orbi.rb:146:in `get_tzone'
[00:00:17] # ./lib/et-orbi.rb:262:in `get_tzone'
[00:00:17] # ./lib/et-orbi.rb:300:in `initialize'
[00:00:17] # ./lib/et-orbi.rb:19:in `new'
[00:00:17] # ./lib/et-orbi.rb:19:in `now'
[00:00:17] # ./lib/et-orbi.rb:252:in `now'
[00:00:17] # ./lib/et-orbi.rb:188:in `platform_info'
[00:00:17] # ./lib/et-orbi.rb:305:in `initialize'
[00:00:17] # ./lib/et-orbi.rb:19:in `new'
[00:00:17] # ./lib/et-orbi.rb:19:in `now'
[00:00:17] # ./lib/et-orbi.rb:252:in `now'
[00:00:17] # ./lib/et-orbi.rb:188:in `platform_info'
[00:00:17] # ...
for
[00:00:17] rspec './spec/module_spec.rb[1:8:6]' # EtOrbi.make_time turns a Date instance into an EoTime instance
Hello, I'm getting this warning printed when running a test suite.
gems/et-orbi-1.1.3/lib/et-orbi.rb:675: warning: instance variable @_os_zone not initialized
The code imports rufus-scheduler and uses it like:
@@scheduler = LazyObject.new { Rufus::Scheduler.new }
def scheduler; @@scheduler; end
What is missing to be able to not have this warning? Thanks!
Timezone seems to be completely irrelevant when calling match?
on cron object:
> cron = Fugit::Cron.parse("0 0 * * * PST8PDT")
=> #<Fugit::Cron:0x00007ff5e1422e60 @original="0 0 * * * PST8PDT", @cron_s=nil, @seconds=[0], @minutes=[0], @hours=[0], @monthdays=nil, @months=nil, @weekdays=nil, @zone="PST8PDT", @timezone=#<TZInfo::DataTimezone: PST8PDT>>
> cron.match?(Time.utc(2019, 1, 1, 0, 0, 0))
=> true
> cron.match?(Time.new(2019, 1, 1, 0, 0, 0, TZInfo::Timezone.get('UTC')))
=> true
> cron.match?(Time.new(2019, 1, 1, 0, 0, 0, TZInfo::Timezone.get('CET')))
=> true
> cron.match?(Time.new(2019, 1, 1, 0, 0, 0, TZInfo::Timezone.get('PST8PDT')))
=> true
I thought setting timezone in cron would make it only match in specific time zone. In example above i'd expect it to match only in PST8PDT midnight.
Weird: OP uses Active Support Time.zone = xxx
but et-orbi doesn't seem to read Time.zone
I'm using Rails 2.3, rufus-scheduler 3.4, resque-scheduler 4.3, and tzinfo 0.3.53, et-orbi 1.0.3
When I start the scheduler, I'm getting
NoMethodError: undefined method `identifier' for nil:NilClass
.rvm/gems/ruby-2.3.3/gems/tzinfo-0.3.53/lib/tzinfo/timezone.rb:502:in `<=>'
.rvm/gems/ruby-2.3.3/gems/et-orbi-1.0.3/lib/et-orbi.rb:92:in `=='
What I'm finding is that I have a timezone set for the get_tzone function and it doesn't know how to respond to the == nil or the == :local from lines 92 and 93.
In order to make it work, I simply moved the return if you have a timezone above the nil and local check so it now looks like
return o if o.is_a?(::TZInfo::Timezone)
return nil if o == nil
return local_tzone if o == :local
return ::TZInfo::Timezone.get('Zulu') if o == 'Z'
The issue:
sidekiq-cron is giving following error.
2019-09-03T13:53:00.672Z 960 TID-owuukgjt0 ERROR: CRON JOB: /.rvm/gems/ruby-2.2.2@views/gems/sidekiq-cron-0.3.1/lib/sidekiq/cron/job.rb:434:in `<'
2019-09-03T13:53:15.692Z 960 TID-owuukgjt0 ERROR: CRON JOB: comparison of Time with EtOrbi::EoTime failed.
2019-09-03T13:53:15.692Z 960 TID-owuukgjt0 ERROR: CRON JOB: /.rvm/gems/ruby-2.2.2@views/gems/sidekiq-cron-0.3.1/lib/sidekiq/cron/job.rb:434:in `<'
2019-09-03T13:53:30.711Z 960 TID-owuukgjt0 ERROR: CRON JOB: comparison of Time with EtOrbi::EoTime failed
https://stackoverflow.com/questions/46577565/rufus-scheduler-at-cannot-parse-time-on-heroku
ArgumentError
(couldn't parse Wed, 04 Oct 2017 23:47:00 EDT -04:00
(ActiveSupport::TimeWithZone)):
Let's see how it turns out.
https://bugs.ruby-lang.org/issues/14850
Ideally, et-orbi should become unnecessary.
Should be pretty self explanatory - I believe I'm using the most recent version. Below is an illustrative snippet:
require 'et_orbi'
# => true
EtOrbi::VERSION
# => "1.0.5"
puts EtOrbi.platform_info
# (etz:nil,tnz:"UTC",tzid:nil,rv:"1.9.3",rp:"x86_64-linux",eov:"1.0.5",rorv:nil,astz:nil,debian:nil,centos:nil,osx:"UTC")
# => nil
my_time = EtOrbi.make_time(2017, 1, 31, 12, 'Europe/Moscow')
# => 2017-01-31 12:00:00 +0300
my_time.to_time
# => 2017-01-31 12:00:00 UTC
my_time.to_i
# => 1485853200
my_time.to_time.to_i
# => 1485864000
It might make sense to use the system timezone for #to_time
, but I would have expected it to at act more like:
Time.at(my_time.to_f)
Which incidentally is what I'm currently using as a workaround. For a bit of background, I'm using this through the Rufus::Scheduler gem.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.