百科.dev
全部条目AI 编程趋势榜开源项目技术资讯提交条目
登录
< 返回工具列表
F

fugit

> 编程语言
开源

针对 Ruby 的时间工具 (cron、解析、持续时间等)、rufus-scheduler 和 flor

516 stars0 点赞1 次浏览
访问官网GitHub

工具介绍

针对 Ruby 的时间工具 (cron、解析、持续时间等)、rufus-scheduler 和 flor

fugit

Sed fugit interea, fugit irreparabile tempus

Virgil, Georgics Book III


Time tools for flor and the floraison group.

It uses et-orbi to represent time instances and raabro as a basis for its parsers.

Fugit is a core dependency of rufus-scheduler >= 3.5.

Related projects

Sister projects

The intersection of those two projects is where fugit is born:

  • rufus-scheduler — a cron/at/in/every/interval in-process scheduler, in fact, it's the father project to this fugit project
  • flor — a Ruby workflow engine, fugit provides the foundation for its time scheduling capabilities

Similar, sometimes overlapping projects

  • chronic — a pure Ruby natural language date parser
  • parse-cron — parses cron expressions and calculates the next occurrence after a given date
  • ice_cube — Ruby date recurrence library
  • ISO8601 — Ruby parser to work with ISO8601 dateTimes and durations
  • chrono — a chain of logics about chronology
  • CronCalc — calculates cron job occurrences
  • Recurrence — a simple library to handle recurring events
  • CronConfigParser — parse the cron configuration for readability
  • ...

Projects using fugit

  • arask — "Automatic RAils taSKs" uses fugit to parse cron strings
  • sidekiq-cron — uses fugit to parse cron strings since version 1.0.0, it was using rufus-scheduler previously
  • rufus-scheduler — as seen above
  • flor — used in the cron procedure
  • que-scheduler — a reliable job scheduler for que
  • serial_scheduler — ruby task scheduler without threading
  • delayed_cron_job — an extension to Delayed::Job that allows you to set cron expressions for your jobs
  • GoodJob — a multithreaded, Postgres-based, Active Job backend for Ruby on Rails
  • Solid Queue — a DB-based queuing backend for Active Job, designed with simplicity and performance in mind
  • qron — stupid cron thread that wakes up from time to time to do what's in its crontab
  • ...

Fugit.parse(s)

The simplest way to use fugit is via Fugit.parse(s).

ruby
require 'fugit'

Fugit.parse('0 0 1 jan *').class         # ==> ::Fugit::Cron
Fugit.parse('12y12M').class              # ==> ::Fugit::Duration

Fugit.parse('2017-12-12').class          # ==> ::EtOrbi::EoTime
Fugit.parse('2017-12-12 UTC').class      # ==> ::EtOrbi::EoTime

Fugit.parse('every day at noon').class   # ==> ::Fugit::Cron

If fugit cannot extract a cron, duration or point in time out of the string, it will return nil.

ruby
Fugit.parse('nada')
  # ==> nil

Fugit.do_parse(s)

Fugit.do_parse(s) is equivalent to Fugit.parse(s), but instead of returning nil, it raises an error if the given string contains no time information.

ruby
Fugit.do_parse('nada')
  # ==> /home/jmettraux/w/fugit/lib/fugit/parse.rb:32
  #     :in `do_parse': found no time information in "nada" (ArgumentError)

parse_cron, parse_in, parse_at, parse_duration, and parse_nat

ruby
require 'fugit'

Fugit.parse_cron('0 0 1 jan *').class       # ==> ::Fugit::Cron
Fugit.parse_duration('12y12M').class        # ==> ::Fugit::Duration

Fugit.parse_at('2017-12-12').class          # ==> ::EtOrbi::EoTime
Fugit.parse_at('2017-12-12 UTC').class      # ==> ::EtOrbi::EoTime

Fugit.parse_nat('every day at noon').class  # ==> ::Fugit::Cron

do_parse_cron, do_parse_in, do_parse_at, do_parse_duration, and do_parse_nat

As Fugit.parse(s) returns nil when it doesn't grok its input, and Fugit.do_parse(s) fails when it doesn't grok, each of the parse_ methods has its partner do_parse_ method.

parse_cronish and do_parse_cronish

Sometimes you know a cron expression or an "every" natural expression will come in and you want to discard the rest.

ruby
require 'fugit'

Fugit.parse_cronish('0 0 1 jan *').class             # ==> ::Fugit::Cron
Fugit.parse_cronish('every saturday at noon').class  # ==> ::Fugit::Cron

Fugit.parse_cronish('12y12M')        # ==> nil

.parse_cronish(s) will return a Fugit::Cron instance or else nil.

.do_parse_cronish(s) will return a Fugit::Cron instance or else fail with an ArgumentError.

Introduced in fugit 1.8.0.

Fugit::Cron

A class Fugit::Cron to parse cron strings and then #next_time and #previous_time to compute the next or the previous occurrence respectively.

There is also a #brute_frequency method which returns an array [ shortest delta, longest delta, occurrence count ] where delta is the time between two occurrences.

…

Example of cron strings understood by fugit:

…

Please note that '15/30 * * * *' is interpreted as '15-59/30 * * * *' since fugit 1.4.6.

time zones

Fugit accepts a IANA timezone identifier right after a cron string:

ruby
'5 0 * * *  Europe/Rome'      # 5 minutes after midnight, every day, Rome tz
'0 22 * * 1-5  Asia/Tbilisi'  # at 2200 on weekdays in Georgia

'@yearly Asia/Kuala_Lumpur'  # turns into '0 0 1 1 * Asia/Kuala_Lumpur'
'@monthly Asia/Jakarta'      # turns into '0 0 1 * * Asia/Jakarta'
  #
  # those two "ats" and friends since fugit 1.11.2...

When no time zone is specified, fugit uses Ruby's provided timezone.

the first Monday of the month

Fugit tries to follow the man 5 crontab documentation.

There is a surprising thing about this canon, all the columns are joined by ANDs, except for monthday and weekday which are joined together by OR if they are both set (they are not *).

Many people (me included) are surprised when they try to specify "at 05:00 on the first Monday of the month" as 0 5 1-7 * 1 or 0 5 1-7 * mon and the results are off.

The man page says:

Note: The day of a command's execution can be specified by two fields -- day of month, and day of week. If both fields are restricted (ie, are not *), the command will be run when either field matches the current time. For example, ``30 4 1,15 * 5'' would cause a command to be run at 4:30 am on the 1st and 15th of each month, plus every Friday.

Fugit follows this specification.

Since fugit 1.7.0, by adding & right after a day specifier, the day-of-month OR day-of-week becomes day-of-month AND day-of-week.

…

the hash extension

Fugit understands 0 5 * * 1#1 or 0 5 * * mon#1 as "each first Monday of the month, at 05:00".

The hash extension can only be used in the day-of-week field.

ruby
'0 5 * * 1#1'    #
'0 5 * * mon#1'  # the first Monday of the month at 05:00

'0 6 * * 5#4,5#5'      #
'0 6 * * fri#4,fri#5'  # the 4th and 5th Fridays of the month at 06:00

'0 7 * * 5#-1'    #
'0 7 * * fri#-1'  # the last Friday of the month at 07:00

'0 7 * * 5#L'       #
'0 7 * * fri#L'     #
'0 7 * * 5#last'    #
'0 7 * * fri#last'  # the last Friday of the month at 07:00

'0 23 * * mon#2,tue'  # the 2nd Monday of the month and every Tuesday, at 23:00

the modulo extension

Since 1.1.10, fugit also understands cron strings like 9 0 * * sun%2 which can be read as "every other Sunday at 9am" or 12 0 * * mon%4 for "every fourth monday at noon".

The modulo extension can only be used in the day-of-week field.

For odd Sundays, one can write 9 0 * * sun%2+1.

It can be combined, as in 9 0 * * sun%2,tue%3+2, which will match every other Sunday and 1 in 3 Tuesdays (with an offset of 2).

What does sun%2 actually mean?

ruby
t.wday == 0 && t.rweek % 2 == 0

What does tue%3+2 mean?

ruby
t.wday == 2 && t.rweek % 3 == 2

et-orbi = 1.4.0 : reference set on Monday 2018-12-31

Since 1.4.0, et-orbi starts by default on Monday (2018-12-31), as rday 0 with rweek 0.

Thus, the above code yields:

…

modulo and et-orbi >= 1.4.0 sanity check

Given the cron "0 12 * * mon%2,wed%3+1", here is a piece of code that considers a range of 44 days and tells in its last column if YES or no if each of the days matches the cron.

ruby
require 'fugit'

c = Fugit.parse_cron('0 12 * * mon%2,wed%3+1')

t = EtOrbi.parse('2025-09-20 12:00')

44.times do

  wd = t.strftime('%a')
  wd = %w[ Mon Wed ].include?(wd) ? '*' + wd.upcase : ' ' + wd.downcase

  puts "%14s | rweek: %3d | %%2: %d == 0 | %%3: %d == 1 | ? %3s" % [
    t.strftime('%F') + ' ' + wd,
    t.rweek,
    t.rweek % 2, t.rweek % 3,
    c.match?(t)
      ].map { |e| e == true ? 'YES' : e == false ? 'no' : e }

  w = t.rweek
  t = t.add(24 * 3600)
  puts if t.rweek != w
end

Here's the output:

…

the random extension

Fugit accepts the ~ character to specify a range from which a random value is picked. Either or both the start and the end of the range may be omitted.

ruby
'~ * * * *'    # every hour on a random minute
'~29 * * * *'  # every hour on a random minute between 0 and 29
'30~ * * * *'  # every hour on a random minute between 30 and 59
'~/10 * * * *' # every 10 minutes starting on a random minute between 0 and 9
'0 12 * * 1~5' # every week at noon on a random workday

You may customize how random values are generated by adding the :random option with a boolean or an object that implements the #rand method.

ruby
Fugit.parse('~ * * * *', random: false)        # on a non-random minute ==> '0 * * * *'
Fugit.parse('~ * * * *', random: true)         # on a random minute (default)
Fugit.parse('~ * * * *', random: SecureRandom) # on a cryptographically secure random minute

Please note that the random values are determined at parse time. So "~29 * * * *" at parse time could become "17 * * * *" for example and go on triggering every hour at minute 17, until a new parsing occurs and another (or the same) minute is picked. This aligns with the OpenBSD man 5 crontab, OpenBSD crond rolling the dice at load time.

the second extension

Fugit accepts cron strings with five elements, minute hour day-of-month month day-of-week, the standard cron format or six elements second minute hour day-of-month month day-of-week.

ruby
c = Fugit.parse('* * * * *') # every minute
c = Fugit.parse('5 * * * *') # every hour at minute 5
c = Fugit.parse('* * * * * *') # every second
c = Fugit.parse('5 * * * * *') # every minute at second 5

Fugit::Nat

Fugit understand some kind of "natural" language:

For example, those "every" get turned into Fugit::Cron instances:

…

Directly with Fugit.parse(s) is OK too:

ruby
Fugit.parse('every day at five')  # ==> Fugit::Cron instance '0 5 * * *'

Ambiguous nats

Not all strings result in a clean, single, cron expression. The multi: false|true|:fail argument to Fugit::Nat.parse could help.

…

multi: true indicates to Fugit::Nat that an array of Fugit::Cron instances is expected as a result.

multi: :fail tells Fugit::Nat.parse to fail if the result is more than 1 Fugit::Cron instances.

multi: false is the default behaviour, return a single Fugit::Cron instance or nil w

Issues· 0 开放

查看全部 Issues在 GitHub 打开

暂无开放 Issues,或尚未同步最近议题。

> 标签

Rubyatcroneveryin

暂无评论,来聊聊你的看法吧

> 工具信息

发布日期2026年8月1日
最后更新2026年9月17日
分类编程语言
定价开源

> 相关工具

T
TypeScript
JavaScript 的超集,为前端与全栈提供静态类型
P
Python
通用编程语言,广泛用于 Web、数据与 AI
G
Go
Google 推出的简洁高效系统语言