Baike.dev
All toolsOnline toolsAI codingTrendingOpen sourceNewsSubmit
Log in
< Back to tools
B

by_star

> 编程语言
Open source

Lets you find ActiveRecord + Mongoid objects by year, month, fortnight, week and more!

1.0K stars0 likes2 views
WebsiteGitHub

About

Lets you find ActiveRecord + Mongoid objects by year, month, fortnight, week and more!

ByStar

ByStar (by_*) allows you easily and reliably query ActiveRecord and Mongoid objects based on time.

Examples

ruby
Post.by_year(2013)                           # all posts in 2013
Post.before(Date.today)                      # all posts for before today
Post.yesterday                               # all posts for yesterday
Post.between_times(Time.zone.now - 3.hours,  # all posts in last 3 hours
                   Time.zone.now)
@post.next                                   # next post after a given post

Installation

Install this gem by adding this to your Gemfile:

ruby
gem 'by_star', git: 'https://github.com/radar/by_star'

Then run bundle install

If you are using ActiveRecord, you're done!

Mongoid users, please include the Mongoid::ByStar module for each model you wish to use the functionality. This is the convention among Mongoid plugins.

ruby
class MyModel
  include Mongoid::Document
  include Mongoid::ByStar

Finder Methods

Base Scopes

ByStar adds the following finder scopes (class methods) to your model to query time ranges. These accept a Date, Time, or DateTime object as an argument, which defaults to Time.zone.now if not specified:

Scope Meaning
between_times(start_time, end_time) Finds all records occurring between two given times.
between_dates(start_date, end_date) Finds all records occurring between two given dates, from beginning of start_date until end of end_date.
before(end_time) Finds all records occurring before the given time.
after(start_time) Finds all records occurring after the given time.
at_time(time) Finds all records occurring exactly at the given time, or which overlap the time in the case of "timespan"-type object (see below)

between_times and between_dates supports alternate argument forms:

  • between_times(Range)
  • between_times(Array)
  • between_times(start_time, nil) - same as after(start_time)
  • between_times(nil, end_time) - same as before(end_time)

Time Range Scopes

ByStar adds additional shortcut scopes based on commonly used time ranges. See sections below for detailed argument usage of each:

Scope Meaning
by_day Query by a given date.
by_week Allows zero-based week value from 0 to 52.
by_cweek Allows one-based week value from 1 to 53.
by_weekend Saturday and Sunday only of the given week.
by_fortnight A two-week period, with the first fortnight of the year beginning on 1st January.
by_month Query by month. Allows integer arg, e.g. 11 for November.
by_calendar_month Month as it appears on a calendar; days form previous/following months which are part of the first/last weeks of the given month.
by_quarter 3-month intervals of the year.
by_year Query by year. Allows integer arg, e.g. 2017.

Relative Scopes

ByStar also adds scopes which are relative to the current time. Note the past_* and next_* methods represent a time distance from current time (Time.zone.now), and do not strictly end/begin evenly on a calendar week/month/year (unlike by_* methods which do.)

Scope Meaning
today Finds all occurrences on today's date.
yesterday Finds all occurrences on yesterday's date.
tomorrow Finds all occurrences on tomorrow's date.
past_day Prior 24-hour period from current time.
past_week Prior 7-day period from current time.
past_fortnight Prior 14-day period from current time.
past_month Prior 30-day period from current time.
past_year Prior 365-day period from current time.
next_day Subsequent 24-hour period from current time.
next_week Subsequent 7-day period from current time.
next_fortnight Subsequent 14-day period from current time.
next_month Subsequent 30-day period from current time.
next_year Subsequent 365-day period from current time.

Superlative Finders

Find the oldest or newest records. Returns an object instance (not a relation):

  • newest
  • oldest

Instance Methods

In addition, ByStar adds instance methods to return the next / previous record in the timewise sequence. Returns an object instance (not a relation):

  • object.next
  • object.previous

Kernel Extensions

ByStar extends the kernel Date, Time, and DateTime objects with the following instance methods, which mirror the ActiveSupport methods beginning_of_day, end_of_week, etc:

  • beginning_of_weekend
  • end_of_weekend
  • beginning_of_fortnight
  • end_of_fortnight
  • beginning_of_calendar_month
  • end_of_calendar_month

Lastly, ByStar aliases Rails 3 Date#to_time_in_current_zone to the Rails 4 syntax #in_time_zone, if it has not already been defined.

Usage

Setting the Query Field

By default, ByStar assumes you will use the created_at field to query objects by time. You may specify an alternate field on all query methods as follows:

ruby
Post.by_month("January", field: :updated_at)

Alternatively, you may set a default in your model using the by_star_field macro:

ruby
class Post  "ruby")

Want to count records? Simple:

ruby
Post.by_month.count

Timezone Handling

ByStar date-range finders will use value of Time.zone to evaluate the args. This may cause unexpected behavior when use Time values in timezones other than Time.zone.

ruby
Time.zone = 'Australia/Sydney'
Post.by_day('2020-04-05 18:00:00 EST')
#=> Returns Apr 6th, 0:00 until Apr 6th, 23:59 in Sydney timezone.

:offset Option

All ByStar finders support an :offset option which is applied to time period of the query condition. This is useful in cases where the daily cycle occurs at a time other than midnight.

For example, if you'd like to find all Posts from 9:00 on 2014-03-05 until 8:59:59.999 on 2014-03-06, you can do:

ruby
Post.by_day('2014-03-05', offset: 9.hours)

Note: When passing offset in date finders, it will set the hour, minute, and second on the queried date in order to properly handle DST transitions. Example:

ruby
Time.zone = 'Australia/Sydney'
Post.by_day('2020-04-05', offset: 9.hours)
#=> Returns Apr 5th, 09:00 until Apr 6th, 08:59

Timespan Objects

If your object has both a start and end time, you may pass both params to by_star_field:

ruby
by_star_field :start_time, :end_time

By default, ByStar queries will return all objects whose range has any overlap within the desired period (permissive):

ruby
MultiDayEvent.by_month("January")
#=> returns MultiDayEvents that overlap in January,
#   even if they start in December and/or end in February

Timespan Objects: #at_time

To find all instances of a timespan object which contain a specific time:

ruby
Post.at_time(time)

This can be useful to find all currently active instances. Note that object instances which start exactly at the given time will be included in the result, but instances that end exactly at the given time will not be.

Timespan Objects: :strict Option

If you'd like to confine results to only those both starting and ending within the given range, use the :strict option:

ruby
MultiDayEvent.by_month("January", :strict => true)
#=> returns MultiDayEvents that both start AND end in January

Timespan Objects: Database Indexing and :index_scope Option

In order to ensure query performance on large dataset, you must add an index to the query field (e.g. "created_at") be indexed. ByStar does not define indexes automatically.

Database indexes require querying a range query on a single field, i.e. start_time >= X and start_time = X and end_time = end_time, we add the converse constraint start_time = X to ensure both fields are double-sided, i.e. an index can be used on either field.

  1. Timespan without :strict option, e.g. "start_time X".

Here we need to add a condition start_time >= X to ensure start_time is bounded on both sides. To achieve this, we allow an :index_scope option which is the minimum "strict" bound on the querying range, in other words, it is an assumption about the maximum timespan of objects.

:index_scope supports multiple value types:

:index_scope Value Meaning
nil or false No constraint set; query will be one-sided (default, but not recommended)
Date or Time, etc. A fixed point in time
ActiveSupport::Duration (e.g. 1.month) The duration value will be subtracted from the start of the range. In other words, a value of 1.month would imply the longest possible object in the database is no longer than 1.month.
Numeric Will be converted to seconds, then handled the same as ActiveSupport::Duration
:beginning_of_day (Symbol literal)
Proc A proc which evaluates to one of the above types. Args are (start_time, end_time, options)

An example settings of :index_scope:

# The maximum possible object length is 5 hours.
by_star index_scope: 5.hours

# Objects are guaranteed to start within the same month, with some offset.
by_star index_scope: ->(start_time, end_time, options){ start_time.beginning_of_month + (options[:offset] || 0) }

# The maximum possible object length half the range being queried.
by_star index_scope: ->(start_time, end_time, options){ ((start_time - end_time)*0.5).seconds }

Chronic Support

If Chronic gem is present, it will be used to parse natural-language date/time strings in all ByStar finder methods. Otherwise, the Ruby Time.parse kernel method will be used as a fallback.

As of ByStar 2.2.0, you must explicitly include gem 'chronic' into your Gemfile in order to use Chronic.

Advanced Usage

between_times

To find records between two times:

ruby
Post.between_times(time1, time2)

You use a Range like so:

ruby
Post.between_times(time1..time2)

Also works with dates - WARNING: there are currently some caveats see Issue #49:

ruby
Post.between_times(date1, date2)

It will query records from date1 (00:00:00 Hrs) until date2 (23:59:59 Hrs).

before and after

To find all posts before / after the current time:

ruby
Post.before
Post.after

To find all posts before certain time or date:

ruby
Post.before(Date.today + 2)
Post.after(Time.now + 5.days)

You can also pass a string:

ruby
Post.before("next tuesday")

For Time-Range type objects, only the start time is considered for before and after.

previous and next

To find the prior/subsequent record to a model instance, previous/next on it:

ruby
Post.last.previous
Post.first.next

You can specify a field also:

ruby
Post.last.previous(field: "published_at")
Post.first.next(field: "published_at")

For Time-Range type objects, only the start time is considered for previous and next.

by_year

To find records from the current year, simply call the method without any arguments:

ruby
Post.by_year

To find records based on a year you can pass it a two or four digit number:

ruby
Post.by_year(09)

This will return all posts in 2009, whereas:

ruby
Post.by_year(99)

will return all the posts in the year 1999.

You can also specify the full year:

ruby
Post.by_year(2009)
Post.by_year(1999)

by_month

If you know the number of the month you want:

ruby
Post.by_month(1)

This will return all posts in the first month (January) of the current year.

If you like being verbose:

ruby
Post.by_month("January")

This will return all posts created in January of the current year.

If you want to find all posts in January of last year just do

ruby
Post.by_month(1, year: 2007)

or

ruby
Post.by_month("January", year: 2007)

This will perform a find using the column you've speci

Issues· 0 open

View all issuesOpen on GitHub

No open issues yet, or sync has not completed.

> Tags

Ruby

No comments yet. Be the first to share.

> Details

PublishedAug 1, 2026
UpdatedSep 17, 2026
Category编程语言
PricingOpen source

> Related tools

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