floraison / fugit

time tools (cron, parsing, durations, ...) for Ruby, rufus-scheduler, and flor
MIT License
353 stars 29 forks source link

cron #previous_time loop breaker issue #95

Closed bdarcet closed 3 months ago

bdarcet commented 3 months ago

Issue description

Error when using Fugit.parse_cron with #previous_time on a specific Time zone (seems to work when using other timezone, tested with America/Chicago for ex)

How to reproduce

The simplest piece of code that reproduces the issue, for example:

require 'fugit'
Fugit.parse_cron("21 0 * * 1%2 America/Sao_Paulo").previous_time

Error and error backtrace (if any)

`block in previous_time': too many loops for "21 0 * * 1%2 America/Sao_Paulo" #previous_time, breaking, cron expression most likely invalid (Feb 30th like?), please fill an issue at https://git.io/fjJCQ (RuntimeError)

Expected behaviour

Something equivalent to:

=> #<EtOrbi::EoTime:0x0000783cc229a6e0 @seconds=1710732000.0, @time=nil, @zone=#<TZInfo::DataTimezone: Etc/UTC>>

Context

Please replace the content of this section with the output of the following commands:

5.15.120+ #1 SMP Thu Aug 24 17:32:58 UTC 2023 x86_64 GNU/Linux

ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [x86_64-linux]

[:env_tz, nil]

(secs:1710150502.092623,utc~:"2024-03-11 09:48:22.09262299537658691",ltz~:"UTC")
(etz:nil,tnz:"UTC",tziv:"2.0.6",tzidv:"1.2023.4",rv:"3.3.0",rp:"x86_64-linux",win:false,rorv:nil,astz:nil,eov:"1.2.7",eotnz:#<TZInfo::DataTimezone: Etc/UTC>,eotnfz:"+0000",eotlzn:"Etc/UTC",eotnfZ:"UTC",debian:"Etc/UTC",centos:nil,osx:"Etc/UTC")

[:fugit, "1.9.0"]

[:now, 2024-03-11 09:48:39.782067209 +0000, :zone, "UTC"]
franckduche commented 3 months ago

Note that with a similar timezone, it works:

Fugit.parse_cron("20 0 * * 1%2 America/Santarem").previous_time
=> #<EtOrbi::EoTime:0x00007aeb992c42c8 @seconds=1709522400.0, @time=nil, @zone=#<TZInfo::DataTimezone: Etc/UTC>>
jmettraux commented 3 months ago
require 'fugit'

def test(x)
  puts
  puts "  --- #{x} ---"
  c = Fugit.parse_cron(x)
  begin
    puts c.previous_time.strftime('%F %T %:z %A')
  rescue => err
    p err
  end
  begin
    puts c.next_time.strftime('%F %T %:z %A')
  rescue => err
    p err
  end
end

#Fugit.parse_cron('21 0 * * 1%2')
test('21 0 * * 1%2')
test('21 0 * * 1%1')

# minute hour day-of-month month day-of-week [flags] command

test('21 0 * * 1%2 UTC')
test('21 0 * * 1%2 America/Chicago')
test('21 0 * * 1%2 America/New_York')

test('21 0 * * 1%2 America/Rio_Branco')
test('21 0 * * 1%2 America/Manaus')
test('21 0 * * 1%2 America/Belem')
test('21 0 * * 1%2 America/Fortaleza')
test('21 0 * * 1%2 America/Recife')
test('21 0 * * 1%2 America/Araguaina')
test('21 0 * * 1%2 America/Maceio')
test('21 0 * * 1%2 America/Bahia')
test('21 0 * * 1%2 America/Sao_Paulo')
test('21 0 * * 1%2 America/Campo_Grande')
test('21 0 * * 1%2 America/Cuiaba')
test('21 0 * * 1%2 America/Santarem')
test('21 0 * * 1%2 America/Porto_Velho')
test('21 0 * * 1%2 America/Boa_Vista')
test('21 0 * * 1%2 America/Manaus')
test('21 0 * * 1%2 America/Eirunepe')
test('21 0 * * 1%2 America/Rio_Branco')

puts
#   --- 21 0 * * 1%2 ---
# 2024-03-04 00:21:00 +09:00 Monday
# 2024-03-18 00:21:00 +09:00 Monday
#
#   --- 21 0 * * 1%1 ---
# 2024-03-11 00:21:00 +09:00 Monday
# 2024-03-18 00:21:00 +09:00 Monday
#
#   --- 21 0 * * 1%2 UTC ---
# 2024-03-04 09:21:00 +09:00 Monday
# 2024-03-18 09:21:00 +09:00 Monday
#
#   --- 21 0 * * 1%2 America/Chicago ---
# 2024-03-04 15:21:00 +09:00 Monday
# 2024-03-18 14:21:00 +09:00 Monday
#
#   --- 21 0 * * 1%2 America/New_York ---
# 2024-03-04 14:21:00 +09:00 Monday
# 2024-03-18 13:21:00 +09:00 Monday
#
#   --- 21 0 * * 1%2 America/Rio_Branco ---
# 2024-03-04 14:21:00 +09:00 Monday
# 2024-03-18 14:21:00 +09:00 Monday
#
#   --- 21 0 * * 1%2 America/Manaus ---
# 2024-03-04 13:21:00 +09:00 Monday
# 2024-03-18 13:21:00 +09:00 Monday
#
#   --- 21 0 * * 1%2 America/Belem ---
# 2024-03-04 12:21:00 +09:00 Monday
# 2024-03-18 12:21:00 +09:00 Monday
#
#   --- 21 0 * * 1%2 America/Fortaleza ---
# 2024-03-04 12:21:00 +09:00 Monday
# 2024-03-18 12:21:00 +09:00 Monday
#
#   --- 21 0 * * 1%2 America/Recife ---
# 2024-03-04 12:21:00 +09:00 Monday
# 2024-03-18 12:21:00 +09:00 Monday
#
#   --- 21 0 * * 1%2 America/Araguaina ---
# 2024-03-04 12:21:00 +09:00 Monday
# 2024-03-18 12:21:00 +09:00 Monday
#
#   --- 21 0 * * 1%2 America/Maceio ---
# 2024-03-04 12:21:00 +09:00 Monday
# 2024-03-18 12:21:00 +09:00 Monday
#
#   --- 21 0 * * 1%2 America/Bahia ---
# 2024-03-04 12:21:00 +09:00 Monday
# 2024-03-18 12:21:00 +09:00 Monday
#
#   --- 21 0 * * 1%2 America/Sao_Paulo ---
# #<RuntimeError: too many loops for "21 0 * * 1%2 America/Sao_Paulo" #previous_time, breaking, cron expression most likely invalid (Feb 30th like?), please fill an issue at https://git.io/fjJCQ>
# 2024-03-18 12:21:00 +09:00 Monday
#
#   --- 21 0 * * 1%2 America/Campo_Grande ---
# #<RuntimeError: too many loops for "21 0 * * 1%2 America/Campo_Grande" #previous_time, breaking, cron expression most likely invalid (Feb 30th like?), please fill an issue at https://git.io/fjJCQ>
# 2024-03-18 13:21:00 +09:00 Monday
#
#   --- 21 0 * * 1%2 America/Cuiaba ---
# #<RuntimeError: too many loops for "21 0 * * 1%2 America/Cuiaba" #previous_time, breaking, cron expression most likely invalid (Feb 30th like?), please fill an issue at https://git.io/fjJCQ>
# 2024-03-18 13:21:00 +09:00 Monday
#
#   --- 21 0 * * 1%2 America/Santarem ---
# 2024-03-04 12:21:00 +09:00 Monday
# 2024-03-18 12:21:00 +09:00 Monday
#
#   --- 21 0 * * 1%2 America/Porto_Velho ---
# 2024-03-04 13:21:00 +09:00 Monday
# 2024-03-18 13:21:00 +09:00 Monday
#
#   --- 21 0 * * 1%2 America/Boa_Vista ---
# 2024-03-04 13:21:00 +09:00 Monday
# 2024-03-18 13:21:00 +09:00 Monday
#
#   --- 21 0 * * 1%2 America/Manaus ---
# 2024-03-04 13:21:00 +09:00 Monday
# 2024-03-18 13:21:00 +09:00 Monday
#
#   --- 21 0 * * 1%2 America/Eirunepe ---
# 2024-03-04 14:21:00 +09:00 Monday
# 2024-03-18 14:21:00 +09:00 Monday
#
#   --- 21 0 * * 1%2 America/Rio_Branco ---
# 2024-03-04 14:21:00 +09:00 Monday
# 2024-03-18 14:21:00 +09:00 Monday
jmettraux commented 3 months ago
p EtOrbi.make_time('2019-01-01 00:00:00', 'America/Sao_Paulo')
p EtOrbi.make_time('2019-01-01 00:00:00', 'America/Santarem')
p EtOrbi.make_time('2019-01-01 00:00:00', 'America/Sao_Paulo').to_s
p EtOrbi.make_time('2019-01-01 00:00:00', 'America/Santarem').to_s
#<EtOrbi::EoTime:0x00000cced5192ac0 @seconds=1546308000.0,
  @zone=#<TZInfo::DataTimezone: America/Sao_Paulo>, @time=nil>
#<EtOrbi::EoTime:0x00000cce8ec5f378 @seconds=1546311600.0,
  @zone=#<TZInfo::DataTimezone: America/Santarem>, @time=nil>
"2019-01-01 00:00:00 -0200"
"2019-01-01 00:00:00 -0300"

The modulo system for weekdays uses the EtOrbi::EoTime#rweek, the reference week is

https://www.timeanddate.com/time/zone/brazil/sao-paulo https://www.timeanddate.com/time/zone/brazil/santarem

The Sao Paulo timezone had daylight saving time until 2019, The Santarem seems not to have DST, hence the -0200 for Sao Paulo (until 2019 Feb 17).

I could

a) increase the iteration count from 2048 to 2048 * 2, but that would result in 2019-02-11 11:21:00 +09:00 Monday, or b) change EtOrbi::EoTime#rweek, but that would impact people using fugit's modulo notation...

I prefer b) but I would draw some flak for it from disgruntled users. a) does not seem right.

Pondering.

jmettraux commented 3 months ago

Please upgrade et-orbi to 1.2.9, it should solve the issue (at least that's what the specs tell me).

Closing the issue, please tell me if there is anything else. Merci beaucoup !

bdarcet commented 3 months ago

@jmettraux we've just tested it and it's working fine for us, thank you for your help and fast response time 🙏

jmettraux commented 3 months ago

Great! A star would be welcome ;-)