{"id":13395142,"url":"https://github.com/radar/by_star","last_synced_at":"2025-05-14T19:08:05.596Z","repository":{"id":456284,"uuid":"80040","full_name":"radar/by_star","owner":"radar","description":"Lets you find ActiveRecord + Mongoid objects by year, month, fortnight, week and more!","archived":false,"fork":false,"pushed_at":"2023-01-18T06:30:34.000Z","size":483,"stargazers_count":1053,"open_issues_count":11,"forks_count":73,"subscribers_count":15,"default_branch":"main","last_synced_at":"2025-05-14T19:07:59.418Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"http://ryanbigg.com","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/radar.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"MIT-LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2008-11-24T01:46:47.000Z","updated_at":"2025-05-13T04:12:27.000Z","dependencies_parsed_at":"2023-02-10T13:32:04.720Z","dependency_job_id":null,"html_url":"https://github.com/radar/by_star","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/radar%2Fby_star","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/radar%2Fby_star/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/radar%2Fby_star/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/radar%2Fby_star/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/radar","download_url":"https://codeload.github.com/radar/by_star/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254209859,"owners_count":22032897,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-30T17:01:43.786Z","updated_at":"2025-05-14T19:08:03.786Z","avatar_url":"https://github.com/radar.png","language":"Ruby","funding_links":[],"categories":["Ruby","Date and Time Processing"],"sub_categories":[],"readme":"# ByStar\n\n[![Build Status](https://travis-ci.org/radar/by_star.svg)](https://travis-ci.org/radar/by_star)\n[![Code Climate](https://codeclimate.com/github/radar/by_star.svg)](https://codeclimate.com/github/radar/by_star)\n\nByStar (by_*) allows you easily and reliably query ActiveRecord and Mongoid objects based on time.\n\n### Examples\n\n```ruby\nPost.by_year(2013)                           # all posts in 2013\nPost.before(Date.today)                      # all posts for before today\nPost.yesterday                               # all posts for yesterday\nPost.between_times(Time.zone.now - 3.hours,  # all posts in last 3 hours\n                   Time.zone.now)\n@post.next                                   # next post after a given post\n```\n\n## Installation\n\nInstall this gem by adding this to your Gemfile:\n\n```ruby\ngem 'by_star', git: 'https://github.com/radar/by_star'\n```\n\nThen run `bundle install`\n\nIf you are using ActiveRecord, you're done!\n\nMongoid users, please include the Mongoid::ByStar module for each model you wish to use the functionality.\nThis is the convention among Mongoid plugins.\n\n```ruby\nclass MyModel\n  include Mongoid::Document\n  include Mongoid::ByStar\n```\n\n## Finder Methods\n\n### Base Scopes\n\nByStar adds the following finder scopes (class methods) to your model to query time ranges.\nThese accept a `Date`, `Time`, or `DateTime` object as an argument, which defaults to `Time.zone.now` if not specified:\n\n| Scope | Meaning |\n| --- | --- |\n| `between_times(start_time, end_time)` | Finds all records occurring between two given times. |\n| `between_dates(start_date, end_date)` | Finds all records occurring between two given dates, from beginning of start_date until end of end_date. |\n| `before(end_time)` | Finds all records occurring before the given time. |\n| `after(start_time)` | Finds all records occurring after the given time. |\n| `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) |\n\n`between_times` and `between_dates` supports alternate argument forms:\n* `between_times(Range)`\n* `between_times(Array)`\n* `between_times(start_time, nil)` - same as `after(start_time)`\n* `between_times(nil, end_time)` - same as `before(end_time)`\n\n### Time Range Scopes\n\nByStar adds additional shortcut scopes based on commonly used time ranges.\nSee sections below for detailed argument usage of each:\n\n| Scope | Meaning |\n| --- | --- |\n| `by_day` | Query by a given date. |\n| `by_week` | Allows zero-based week value from 0 to 52. |\n| `by_cweek` | Allows one-based week value from 1 to 53. |\n| `by_weekend` | Saturday and Sunday only of the given week. |\n| `by_fortnight` | A two-week period, with the first fortnight of the year beginning on 1st January. |\n| `by_month` | Query by month. Allows integer arg, e.g. `11` for November. |\n| `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. |\n| `by_quarter` | 3-month intervals of the year. |\n| `by_year` | Query by year. Allows integer arg, e.g. `2017`. |\n\n### Relative Scopes\n\nByStar also adds scopes which are relative to the current time.\nNote the `past_*` and `next_*` methods represent a time distance from current time (`Time.zone.now`),\nand do not strictly end/begin evenly on a calendar week/month/year (unlike `by_*` methods which do.)\n\n| Scope | Meaning |\n| --- | --- |\n| `today` | Finds all occurrences on today's date. |\n| `yesterday` | Finds all occurrences on yesterday's date. |\n| `tomorrow` | Finds all occurrences on tomorrow's date. |\n| `past_day` | Prior 24-hour period from current time. |\n| `past_week` | Prior 7-day period from current time. |\n| `past_fortnight` | Prior 14-day period from current time. |\n| `past_month` | Prior 30-day period from current time. |\n| `past_year` | Prior 365-day period from current time. |\n| `next_day` | Subsequent 24-hour period from current time. |\n| `next_week` | Subsequent 7-day period from current time. |\n| `next_fortnight` | Subsequent 14-day period from current time. |\n| `next_month` | Subsequent 30-day period from current time. |\n| `next_year` | Subsequent 365-day period from current time. |\n\n### Superlative Finders\n\nFind the oldest or newest records. Returns an object instance (not a relation):\n\n* `newest`\n* `oldest`\n\n### Instance Methods\n\nIn addition, ByStar adds instance methods to return the next / previous record in the timewise sequence.\nReturns an object instance (not a relation):\n\n* `object.next`\n* `object.previous`\n\n### Kernel Extensions\n\nByStar extends the kernel `Date`, `Time`, and `DateTime` objects with the following instance methods,\nwhich mirror the ActiveSupport methods `beginning_of_day`, `end_of_week`, etc:\n\n* `beginning_of_weekend`\n* `end_of_weekend`\n* `beginning_of_fortnight`\n* `end_of_fortnight`\n* `beginning_of_calendar_month`\n* `end_of_calendar_month`\n\nLastly, 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.\n\n## Usage\n\n### Setting the Query Field\n\nBy default, ByStar assumes you will use the `created_at` field to query objects by time.\nYou may specify an alternate field on all query methods as follows:\n\n```ruby\nPost.by_month(\"January\", field: :updated_at)\n```\n\nAlternatively, you may set a default in your model using the `by_star_field` macro:\n\n```ruby\nclass Post \u003c ActiveRecord::Base\n  by_star_field :updated_at\nend\n```\n\n### Scoping the Query\n\nAll ByStar methods (except `oldest`, `newest`, `previous`, `next`) return an `ActiveRecord::Relation`\n(or `Mongoid::Criteria`) which can be daisy-chained with other scopes/finder methods:\n\n```ruby\nPost.by_month.your_scope\nPost.by_month(1).include(:tags).where(\"tags.name\" =\u003e \"ruby\")\n```\n\nWant to count records? Simple:\n\n```ruby\nPost.by_month.count\n```\n\n### Timezone Handling\n\nByStar date-range finders will use value of `Time.zone` to evaluate the args.\nThis may cause unexpected behavior when use Time values in timezones other than `Time.zone`.\n\n```ruby\nTime.zone = 'Australia/Sydney'\nPost.by_day('2020-04-05 18:00:00 EST')\n#=\u003e Returns Apr 6th, 0:00 until Apr 6th, 23:59 in Sydney timezone.\n```\n\n### `:offset` Option\n\nAll ByStar finders support an `:offset` option which is applied to time period of the query condition.\nThis is useful in cases where the daily cycle occurs at a time other than midnight.\n\nFor 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:\n\n```ruby\nPost.by_day('2014-03-05', offset: 9.hours)\n```\n\n**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:\n\n```ruby\nTime.zone = 'Australia/Sydney'\nPost.by_day('2020-04-05', offset: 9.hours)\n#=\u003e Returns Apr 5th, 09:00 until Apr 6th, 08:59\n```\n\n### Timespan Objects\n\nIf your object has both a start and end time, you may pass both params to `by_star_field`:\n\n```ruby\nby_star_field :start_time, :end_time\n```\n\nBy default, ByStar queries will return all objects whose range has any overlap within the desired period (permissive):\n\n```ruby\nMultiDayEvent.by_month(\"January\")\n#=\u003e returns MultiDayEvents that overlap in January,\n#   even if they start in December and/or end in February\n```\n\n### Timespan Objects: `#at_time`\n\nTo find all instances of a timespan object which contain a specific time:\n\n```ruby\nPost.at_time(time)\n```\n\nThis can be useful to find all currently active instances. Note that object instances which start\nexactly at the given `time` will be included in the result, but instances that end exactly at the given\n`time` will not be.\n\n### Timespan Objects: `:strict` Option\n\nIf you'd like to confine results to only those both starting and ending within the given range, use the `:strict` option:\n\n```ruby\nMultiDayEvent.by_month(\"January\", :strict =\u003e true)\n#=\u003e returns MultiDayEvents that both start AND end in January\n```\n\n### Timespan Objects: Database Indexing and `:index_scope` Option\n\nIn 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.\n\nDatabase indexes require querying a range query on a single field, i.e. `start_time \u003e= X and start_time \u003c= Y`.\nIf we use a single-sided query, the database will iterate through all items either from the beginning or until the end of time.\nThis poses a challenge for timespan-type objects which have two fields, i.e. `start_time` and `end_time`.\nThere are two cases to consider:\n\n1) Timespan with `:strict` option, e.g. `start_time \u003e= X and end_time \u003c= Y`.\n\nGiven that this gem requires `start_time \u003e= end_time`, we add the converse constraint `start_time \u003c= Y and end_time \u003e= X`\nto ensure both fields are double-sided, i.e. an index can be used on either field.\n\n2) Timespan without `:strict` option, e.g. \"start_time \u003c Y and end_time \u003e X\".\n\nHere we need to add a condition `start_time \u003e= X` to ensure `start_time` is bounded on both sides.\nTo achieve this, we allow an `:index_scope` option which is the minimum \"strict\" bound on the querying range,\nin other words, it is an assumption about the maximum timespan of objects.\n\n`:index_scope` supports multiple value types:\n\n| `:index_scope` Value | Meaning |\n| --- | --- |\n| `nil` or `false` | No constraint set; query will be one-sided (default, but not recommended) |\n| `Date` or `Time`, etc. | A fixed point in time |\n| `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`. |\n| `Numeric` | Will be converted to seconds, then handled the same as `ActiveSupport::Duration` |\n| `:beginning_of_day` (`Symbol` literal) |\n| `Proc\u003cRange, Hash(options)\u003e` | A proc which evaluates to one of the above types. Args are `(start_time, end_time, options)` |\n\nAn example settings of `:index_scope`:\n\n```\n# The maximum possible object length is 5 hours.\nby_star index_scope: 5.hours\n\n# Objects are guaranteed to start within the same month, with some offset.\nby_star index_scope: -\u003e(start_time, end_time, options){ start_time.beginning_of_month + (options[:offset] || 0) }\n\n# The maximum possible object length half the range being queried.\nby_star index_scope: -\u003e(start_time, end_time, options){ ((start_time - end_time)*0.5).seconds }\n```\n\n### Chronic Support\n\nIf [Chronic](https://github.com/mojombo/chronic) gem is present, it will be used to parse natural-language date/time\nstrings in all ByStar finder methods. Otherwise, the Ruby `Time.parse` kernel method will be used as a fallback.\n\nAs of ByStar 2.2.0, you must explicitly include `gem 'chronic'` into your Gemfile in order to use Chronic.\n\n## Advanced Usage\n\n### between_times\n\nTo find records between two times:\n\n```ruby\nPost.between_times(time1, time2)\n```\n\nYou use a Range like so:\n\n```ruby\nPost.between_times(time1..time2)\n```\n\nAlso works with dates - WARNING: there are currently some caveats see [Issue #49](https://github.com/radar/by_star/issues/49):\n\n```ruby\nPost.between_times(date1, date2)\n```\n\nIt will query records from `date1` (00:00:00 Hrs) until `date2` (23:59:59 Hrs).\n\n### before and after\n\nTo find all posts before / after the current time:\n\n```ruby\nPost.before\nPost.after\n```\n\nTo find all posts before certain time or date:\n\n```ruby\nPost.before(Date.today + 2)\nPost.after(Time.now + 5.days)\n```\n\nYou can also pass a string:\n\n```ruby\nPost.before(\"next tuesday\")\n```\n\nFor Time-Range type objects, only the start time is considered for `before` and `after`.\n\n### previous and next\n\nTo find the prior/subsequent record to a model instance, `previous`/`next` on it:\n\n```ruby\nPost.last.previous\nPost.first.next\n```\n\nYou can specify a field also:\n\n```ruby\nPost.last.previous(field: \"published_at\")\nPost.first.next(field: \"published_at\")\n```\n\nFor Time-Range type objects, only the start time is considered for `previous` and `next`.\n\n### by_year\n\nTo find records from the current year, simply call the method without any arguments:\n\n```ruby\nPost.by_year\n```\n\nTo find records based on a year you can pass it a two or four digit number:\n\n```ruby\nPost.by_year(09)\n```\n\nThis will return all posts in 2009, whereas:\n\n```ruby\nPost.by_year(99)\n```\n\nwill return all the posts in the year 1999.\n\nYou can also specify the full year:\n\n```ruby\nPost.by_year(2009)\nPost.by_year(1999)\n```\n\n### by_month\n\nIf you know the number of the month you want:\n\n```ruby\nPost.by_month(1)\n```\n\nThis will return all posts in the first month (January) of the current year.\n\nIf you like being verbose:\n\n```ruby\nPost.by_month(\"January\")\n```\n\nThis will return all posts created in January of the current year.\n\nIf you want to find all posts in January of last year just do\n\n```ruby\nPost.by_month(1, year: 2007)\n```\n\nor\n\n```ruby\nPost.by_month(\"January\", year: 2007)\n```\n\nThis will perform a find using the column you've specified.\n\nIf you have a Time object you can use it to find the posts:\n\n```ruby\nPost.by_month(Time.local(2012, 11, 24))\n```\n\nThis will find all the posts in November 2012.\n\n### by_calendar_month\n\nFinds records for a given month as shown on a calendar. Includes all the results of `by_month`, plus any results which fall in the same week as the first and last of the month. Useful for working with UI calendars which show rows of weeks.\n\n```ruby\nPost.by_calendar_month\n```\n\nParameter behavior is otherwise the same as `by_month`. Also, `:start_day` option is supported to specify the start day of the week (`:monday`, `:tuesday`, etc.)\n\n### by_fortnight\n\nFortnight numbering starts at 0. The beginning of a fortnight is Monday, 12am.\n\nTo find records from the current fortnight:\n\n```ruby\nPost.by_fortnight\n```\n\nTo find records based on a fortnight, you can pass in a number (representing the fortnight number) or a time object:\n\n```ruby\nPost.by_fortnight(18)\n```\n\nThis will return all posts in the 18th fortnight of the current year.\n\n```ruby\nPost.by_fortnight(18, year: 2012)\n```\n\nThis will return all posts in the 18th fortnight week of 2012.\n\n```ruby\nPost.by_fortnight(Time.local(2012,1,1))\n```\n\nThis will return all posts from the first fortnight of 2012.\n\n### by_week and by_cweek\n\nWeek numbering starts at 0, and cweek numbering starts at 1 (same as `Date#cweek`). The beginning of a week is as defined in `ActiveSupport#beginning_of_week`, which can be configured.\n\nTo find records from the current week:\n\n```ruby\nPost.by_week\nPost.by_cweek  # same result\n```\n\nThis will return all posts in the 37th week of the current year (remember week numbering starts at 0):\n\n```ruby\nPost.by_week(36)\nPost.by_cweek(37)  # same result\n```\n\nThis will return all posts in the 37th week of 2012:\n\n```ruby\nPost.by_week(36, year: 2012)\nPost.by_cweek(37, year: 2012)  # same result\n```\n\nThis will return all posts in the week which contains Jan 1, 2012:\n\n```ruby\nPost.by_week(Time.local(2012,1,1))\nPost.by_cweek(Time.local(2012,1,1))  # same result\n```\n\nYou may pass in a `:start_day` option (`:monday`, `:tuesday`, etc.) to specify the starting day of the week. This may also be configured in Rails.\n\n### by_weekend\n\nIf the time passed in (or the time now is a weekend) it will return posts from 0:00 Saturday to 23:59:59 Sunday. If the time is a week day, it will show all posts for the coming weekend.\n\n```ruby\nPost.by_weekend(Time.now)\n```\n\n### by_day and today\n\nTo find records for today:\n\n```ruby\nPost.by_day\nPost.today\n```\n\nTo find records for a certain day:\n\n```ruby\nPost.by_day(Time.local(2012, 1, 1))\n```\n\nYou can also pass a string:\n\n```ruby\nPost.by_day(\"next tuesday\")\n```\n\nThis will return all posts for the given day.\n\n### by_quarter\n\nFinds records by 3-month quarterly period of year. Quarter numbering starts at 1. The four quarters of the year begin on Jan 1, Apr 1, Jul 1, and Oct 1 respectively.\n\nTo find records from the current quarter:\n\n```ruby\nPost.by_quarter\n```\n\nTo find records based on a quarter, you can pass in a number (representing the quarter number) or a time object:\n\n```ruby\nPost.by_quarter(4)\n```\n\nThis will return all posts in the 4th quarter of the current year.\n\n```ruby\nPost.by_quarter(2, year: 2012)\n```\n\nThis will return all posts in the 2nd quarter of 2012.\n\n```ruby\nPost.by_week(Time.local(2012,1,1))\n```\n\nThis will return all posts from the first quarter of 2012.\n\n## Version Support\n\nByStar is tested against the following versions:\n\n* Ruby 2.0.0+\n* Rails/ActiveRecord 3.2+\n* Mongoid 3.1+\n\nNote that ByStar automatically adds the following version compatibility shims:\n\n* ActiveSupport 3.x: Add `Time/Date/DateTime#in_time_zone` (as an alias to `#to_time_in_current_zone`) for compatibility with Rails 4+.\n* Mongoid 3.x: Adds `Criteria#reorder` method from Mongoid 4.\n\n\n## Testing\n\n### Test Setup\n\nSpecify a database by supplying a `DB` environmental variable:\n\n```bash\nbundle exec rake spec DB=sqlite\n```\n\nYou can also take an ORM-specific test task for a ride:\n\n```bash\nbundle exec rake spec:active_record\n```\n\nHave an Active Record or Mongoid version in mind? Set the environment variables\n`ACTIVE_RECORD_VERSION` and `MONGOID_VERSION` to a version of your choice. A\nversion number provided will translate to `~\u003e VERSION`, and the string `master`\nwill grab the latest from Github.\n\n```bash\n# Update your bundle appropriately...\nACTIVE_RECORD_VERSION=4.0.0 MONGOID_VERSION=master bundle update\n\n# ...then run the specs\nACTIVE_RECORD_VERSION=4.0.0 MONGOID_VERSION=master bundle exec rpsec spec\n```\n\n### Test Implementation\n\nByStar tests use TimeCop to lock the system `Time.now` at Jan 01, 2014, and seed\nobjects with fixed dates according to `spec/fixtures/shared/seeds.rb`.\nNote that the timezone is randomized on each run to shake-out timezone related quirks.\n\n\n## Collaborators\n\nByStar is actively maintained by Ryan Bigg (radar) and Johnny Shields (johnnyshields)\n\nThank you to the following people:\n\n* Thomas Sinclair for the original bump for implementing ByStar\n* [Ruby on Rails](http://rubyonrails.org/) for their support\n* Mislav Marohnic\n* August Lilleas (leethal)\n* gte351s\n* Sam Elliott (lenary)\n* The creators of the [Chronic](https://github.com/mojombo/chronic) gem\n* Erik Fonselius\n* Johnny Shields (johnnyshields)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fradar%2Fby_star","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fradar%2Fby_star","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fradar%2Fby_star/lists"}