Coder Social home page Coder Social logo

tod's Introduction

Tod

Supplies TimeOfDay class that includes parsing, strftime, comparison, and arithmetic.

Supplies Shift to represent a period of time, using a beginning and ending TimeOfDay. Allows to calculate its duration and to determine if a TimeOfDay is included inside the shift. For nightly shifts (when beginning time is greater than ending time), it supposes the shift ends the following day.

Installation

gem install tod

Examples

Loading Tod

require 'tod'

Creating from hour, minute, and second

Tod::TimeOfDay.new 8                                # => 08:00:00
Tod::TimeOfDay.new 8, 15, 30                        # => 08:15:30

Parsing text

Strings only need to contain an hour. Minutes, seconds, AM or PM, and colons are all optional.

Tod::TimeOfDay.parse "8"                            # => 08:00:00
Tod::TimeOfDay.parse "8am"                          # => 08:00:00
Tod::TimeOfDay.parse "8pm"                          # => 20:00:00
Tod::TimeOfDay.parse "8p"                           # => 20:00:00
Tod::TimeOfDay.parse "9:30"                         # => 09:30:00
Tod::TimeOfDay.parse "15:30"                        # => 15:30:00
Tod::TimeOfDay.parse "3:30pm"                       # => 15:30:00
Tod::TimeOfDay.parse "1230"                         # => 12:30:00
Tod::TimeOfDay.parse "3:25:58"                      # => 03:25:58
Tod::TimeOfDay.parse "515p"                         # => 17:15:00
Tod::TimeOfDay.parse "151253"                       # => 15:12:53
Tod::TimeOfDay.parse "noon"                         # => 12:00:00
Tod::TimeOfDay.parse "midnight"                     # => 00:00:00

Tod::TimeOfDay.parse raises an ArgumentError if the argument to parse is not parsable. Tod::TimeOfDay.try_parse will instead return nil if the argument is not parsable.

Tod::TimeOfDay.try_parse "3:30pm"                   # => 15:30:00
Tod::TimeOfDay.try_parse "foo"                      # => nil

You can also give a block to parse to handle special input with your own logic.

Tod::TimeOfDay.parse "25" do |time_string|
  Tod::TimeOfDay.new(time_string.to_i % 24)
end                                                 # => 01:00:00

Values can be tested with Tod::TimeOfDay.parsable? to see if they can be parsed.

Tod::TimeOfDay.parsable? "3:30pm"                   # => true
Tod::TimeOfDay.parsable? "foo"                      # => false

Adding or subtracting time

Seconds can be added to or subtracted from Tod::TimeOfDay objects. Time correctly wraps around midnight.

Tod::TimeOfDay.new(8) + 3600                        # => 09:00:00
Tod::TimeOfDay.new(8) - 3600                        # => 07:00:00
Tod::TimeOfDay.new(0) - 30                          # => 23:59:30
Tod::TimeOfDay.new(23,59,45) + 30                   # => 00:00:15

Comparing

Tod::TimeOfDay includes Comparable.

Tod::TimeOfDay.new(8) < Tod::TimeOfDay.new(9)            # => true
Tod::TimeOfDay.new(8) == Tod::TimeOfDay.new(9)           # => false
Tod::TimeOfDay.new(9) == Tod::TimeOfDay.new(9)           # => true
Tod::TimeOfDay.new(10) > Tod::TimeOfDay.new(9)           # => true

Formatting

Format strings are passed to Time#strftime.

Tod::TimeOfDay.new(8,30).strftime("%H:%M")          # => "08:30"
Tod::TimeOfDay.new(17,15).strftime("%I:%M %p")      # => "05:15 PM"
Tod::TimeOfDay.new(22,5,15).strftime("%I:%M:%S %p") # => "10:05:15 PM"

Or a Rails style to_formatted_s is aliased to to_s.

Tod::TimeOfDay.new(8,30).to_s(:short)    # => "8:30 am"

Or i18n in a Rails ERB view.

<%= l Tod::TimeOfDay.new(8, 30), format: :short %>

Add new formatters to Tod::TimeOfDay::FORMATS.

Tod::TimeOfDay::FORMATS[:seconds_only] = "%S"
Tod::TimeOfDay.new(8,30,57).to_s(:seconds_only)  # => "57"

Rounding

Round to the given nearest number of seconds.

Tod::TimeOfDay.new(8,15,31).round(5)     # => "08:15:30"
Tod::TimeOfDay.new(8,15,34).round(60)    # => "08:16:00"
Tod::TimeOfDay.new(8,02,29).round(300)   # => "08:00:00"

Convenience methods for dates and times

Pass a date to Tod::TimeOfDay#on and it will return a time with that date and time, in the time zone of the ruby runtime (Time.now.zone).

tod = Tod::TimeOfDay.new 8, 30                  # => 08:30:00
tod.on Date.today                               # => 2010-12-29 08:30:00 -0600

Tod offers Date#at and Time#to_time_of_day. Require 'tod/core_extensions' to enable.

require 'tod/core_extensions'
tod = Tod::TimeOfDay.new 8, 30                  # => 08:30:00
Date.today.at tod                               # => 2010-12-29 08:30:00 -0600
Time.now.to_time_of_day                         # => 16:30:43
DateTime.now.to_time_of_day                     # => 16:30:43

Conversion method

Tod provides a conversion method which will handle a variety of input types:

Tod::TimeOfDay(Tod::TimeOfDay.new(8, 30))           # => 08:30:00
Tod::TimeOfDay("09:45")                        # => 09:45:00
Tod::TimeOfDay(Time.new(2014, 1, 1, 12, 30))   # => 12:30:00
Tod::TimeOfDay(Date.new(2014, 1, 1))           # => 00:00:00

Shifts

Tod::Shift is a range-like object that represents a period of time, using a beginning and ending Tod::TimeOfDay. Allows to calculate its duration and to determine if a Tod::TimeOfDay is included inside the shift. For nightly shifts (when beginning time is greater than ending time), it supposes the shift ends the following day. Tod::Shift behaves like a Ruby range in that it defaults to inclusive endings. For exclusive endings, pass true as the third argument (like a Ruby range).

Creating from Tod::TimeOfDay

Tod::Shift.new(Tod::TimeOfDay.new(9), Tod::TimeOfDay.new(17))
Tod::Shift.new(Tod::TimeOfDay.new(22), Tod::TimeOfDay.new(4))

Duration

Tod::Shift.new(Tod::TimeOfDay.new(9), Tod::TimeOfDay.new(17)).duration # => 28800
Tod::Shift.new(Tod::TimeOfDay.new(20), Tod::TimeOfDay.new(2)).duration # => 21600

Include?

Tod::Shift.new(Tod::TimeOfDay.new(9), Tod::TimeOfDay.new(17)).include?(Tod::TimeOfDay.new(12)) # => true
Tod::Shift.new(Tod::TimeOfDay.new(9), Tod::TimeOfDay.new(17)).include?(Tod::TimeOfDay.new(7))  # => false
Tod::Shift.new(Tod::TimeOfDay.new(20), Tod::TimeOfDay.new(4)).include?(Tod::TimeOfDay.new(2))  # => true
Tod::Shift.new(Tod::TimeOfDay.new(20), Tod::TimeOfDay.new(4)).include?(Tod::TimeOfDay.new(18)) # => false

#include? respects exclusive endings.

Tod::Shift.new(Tod::TimeOfDay.new(5), Tod::TimeOfDay.new(9)).include?(Tod::TimeOfDay.new(9)) # => true
Tod::Shift.new(Tod::TimeOfDay.new(5), Tod::TimeOfDay.new(9), true).include?(Tod::TimeOfDay.new(9)) # => false

Overlaps?

breakfast = Tod::Shift.new(Tod::TimeOfDay.new(8), Tod::TimeOfDay.new(11))
lunch = Tod::Shift.new(Tod::TimeOfDay.new(10), Tod::TimeOfDay.new(14))
breakfast.overlaps?(lunch) # => true
lunch.overlaps?(breakfast) # => true

dinner = Tod::Shift.new(Tod::TimeOfDay.new(18), Tod::TimeOfDay.new(20))
dinner.overlaps?(lunch) # => false

# Exclude ending
morning_shift = Tod::Shift.new(Tod::TimeOfDay.new(9), Tod::TimeOfDay.new(17), true)
evening_shift = Tod::Shift.new(Tod::TimeOfDay.new(17), Tod::TimeOfDay.new(1), true)
morning_shift.overlaps?(evening_shift) # => false

Contains?

workday = Tod::Shift.new(Tod::TimeOfDay.new(9), Tod::TimeOfDay.new(17))
lunch = Tod::Shift.new(Tod::TimeOfDay.new(10), Tod::TimeOfDay.new(14))
workday.contains?(lunch) # => true
lunch.contains?(workday) # => false

dinner = Shift.new(TimeOfDay.new(18), TimeOfDay.new(20))
dinner.overlaps?(lunch) # => false

Rails Time Zone Support

If Rails time zone support is loaded, Date#on and Tod::TimeOfDay#at (when given a Date) will automatically use Time.zone.

When Tod::TimeOfDay#on is given a Time or Time-like object like ActiveSupport::TimeWithZone, Tod will ignore the specified timezone and return the time on that date in UTC. In order to produce an object with the correct time and time zone, pass in an ActiveSupport::TimeZone object. Date#at has analogous behavior.

time = Time.now.in_time_zone("US/Eastern")           # => Mon, 24 Sep 2018 05:07:23 EDT -04:00
tod.on time                                          # => Mon, 24 Sep 2018 08:30:00 UTC +00:00
tod.on time, time.time_zone                          # => Mon, 24 Sep 2018 08:30:00 EDT -04:00
tod.on time, Time.find_zone!("US/Mountain")          # => Mon, 24 Sep 2018 08:30:00 MDT -06:00
Date.tomorrow.at tod, Time.find_zone!("US/Mountain") # => Tue, 25 Sep 2018 08:30:00 MDT -06:00

ActiveRecord Attribute Support

Tod::TimeOfDay can be used as an ActiveRecord attribute to store Tod::TimeOfDay directly in a column of the time type.

Example:

ActiveModel::Type.register(:time_only, Tod::TimeOfDayType)
ActiveRecord::Type.register(:time_only, Tod::TimeOfDayType)

class Order < ActiveRecord::Base
  attribute :time, :time_only
end
order = Order.create(time: Tod::TimeOfDay.new(9,30))
order.time                                      # => 09:30:00

In Rails, this can be used with time_select in forms like so:

f.time_select :time, ignore_date: true

Or with simple_form:

f.input :time, as: :time, ignore_date: true

MongoDB Support

Tod includes optional serialization support for Tod::TimeOfDay to be serialized to MongoDB.

require 'tod/mongoization'

Upgrading from Versions Prior to 2.0.0

Tod has a new focus on not polluting the global namespace.

Tod no longer puts Tod::TimeOfDay and Tod::Shift in the global namespace by default. You can either fully qualify access to these classes or include Tod in the global namespace.

require 'tod'
include Tod # TimeOfDay and Shift are now in the global namespace like in versions prior to 2.0.0

Tod no longer automatically extends the Time and Date classes. Require them explicitly.

require 'tod/core_extensions'
tod = Tod::TimeOfDay.new 8, 30                  # => 08:30:00
Date.today.at tod                               # => 2010-12-29 08:30:00 -0600
Time.now.to_time_of_day                         # => 16:30:43
DateTime.now.to_time_of_day                     # => 16:30:43

Tod no longer automatically includes MongoDB serialization methods in Tod::TimeOfDay. Require them explicitly.

require 'tod/mongoization'

Compatibility

Build Status

Tod is tested against Ruby 2.6.x and Rails 6.x.

License

Copyright (c) 2010-2021 Jack Christensen, released under the MIT license

tod's People

Contributors

aiomaster avatar bitofuniverse avatar bjackson2 avatar brentwheeldon avatar cnorthwood avatar codeandclay avatar fig avatar gregbeech avatar hirokishirai avatar jackc avatar jjb avatar joe81 avatar johnnyshields avatar jonathanpa avatar kennyeni avatar maxim-filimonov avatar mfazekas avatar morozzzko avatar nazamoresco avatar obversity avatar pablorusso avatar paultyng avatar peteryates avatar righi avatar rubydev avatar rymodi avatar sman591 avatar steveh avatar tatey avatar tomclose avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

tod's Issues

Time of day 24:00 unexpected behaviour

While 24:00 is a valid time according to ISO8601 (meant to be equivalent to 00:00 of the next day), Tod fails to parse it. Even more strange is what will happen if the AR serializer is being used and the value in the database is 24:00. Tod will read it as 00:00 (which is NOT the same). Saving the object later on won't change the DB value to 00:00. This can lead to all sorts of weirdness when executing SQL queries.

Support time_select without seconds

Would be nice to support:

  f.time_select :start_time, include_date: false

this will fail converting nil to an integer because time_of_day_type.rb:7 assumes hour, min and second.

initializing a shift with strings is buggy

If I create a shift with strings (e.g. Tod::Shift.new('09:00:00', '11:00:00')) it works at first sight, but the shift is very buggy (e.g. duration returns 2 because the strings are just casted to int.)

I had to spent quite a while to figure out the problem. Obviously I understand that Tod::TimeOfDay should be passed instead of strings.

I see two potential solutions to prevent other people from being confused like I was

a) raise errors when the passed arguments are not of type Tod::TimeOfDay

b) if a string is passed, automatically convert it using Tod::TimeOfDay.parse(param)

Time object can't be converted to Tod::TimeOfDay without require 'tod/core_extension'

Is that right behavior?

irb(main):001:0> require 'tod'
=> true
irb(main):002:0> t = Time.new(2014, 2, 27, 12, 01, 02)
=> 2014-02-27 12:01:02 +0900
irb(main):003:0> tod = Tod::TimeOfDay(t)
ArgumentError: Invalid time of day string
    from /Users/maeshima/.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/tod-2.0.0/lib/tod/time_of_day.rb:122:in `parse'
    from /Users/maeshima/.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/tod-2.0.0/lib/tod/conversions.rb:8:in `TimeOfDay'
    from (irb):3
    from /Users/maeshima/.rbenv/versions/2.2.2/bin/irb:11:in `<main>'
irb(main):004:0> require 'date'
=> true
irb(main):005:0> require 'tod/core_extensions'
=> true
irb(main):006:0> tod = Tod::TimeOfDay(t)
=> #<Tod::TimeOfDay:0x007fbb0b82c468 @hour=12, @minute=1, @second=2, @second_of_day=43262>

Proposal: Implement Tod::TimeOfDay#upto / Tod::TimeOfDay#step

Proposal for new instance methods upto and/or step

These can be used as following cases

Tod::TimeOfDay('10:00').upto(Tod::TimeOfDay('11::00')) do |tod|
  puts tod
end
#=> 10:00::00, 10:00:01, 10:00:02, ...10:59:59, 11:00:00
Tod::TimeOfDay('10:00').step(Tod::TimeOfDay('11::00'), 60) do |tod|
  puts tod
end
#=> 10:00, 10:01, 10:02, ...10:59, 11:00

c.f.
https://rubydoc.info/stdlib/date/Date#step-instance_method
https://rubydoc.info/stdlib/date/Date#upto-instance_method

before working on implementation, I would like to hear a comment about this methods. Thank you!

Rails 5 (beta 3)

I know you don't officially support Rails 5 yet, but I wanted to let you know that I ran into some issues when trying to use it.

Date.at

Date.today.at Tod::TimeOfDay.new 8
NoMethodError: undefined method `at' for Thu, 24 Mar 2016:Date

ActiveRecord Serialize + Time Zone

Tod::TimeOfDay.parse("8:00 AM")
#<Tod::TimeOfDay:0x007f98504f3018 @hour=8, @minute=0, @second=0, @second_of_day=28800>

This is great. If you put it into a postgress :time field it gets translated from EST to UTC, 03:00:00, also great. But when you pull it out again it re-applies the -5 time zone adjustment and displays 22:00:00. :(

I'm going to end up implementing my own (simplified string-based) class, so don't rush to fix these on my account, but I figured you'd want to know about them.

strftime crashes when passing a frozen format string and 24 hour Tod

strftime crashes when passing a frozen format string and 24 hour Tod::TimeOfDay

This can happen when using # frozen_string_literal: true and a hardcoded format string.

nathan@muon app]$ irb
irb(main):001:0> require 'tod'
=> true
irb(main):002:0> Tod::TimeOfDay.new(24).strftime('%H:%M'.freeze)
Traceback (most recent call last):
        6: from /home/nathan/.rbenv/versions/2.6.6/bin/irb:23:in `<main>'
        5: from /home/nathan/.rbenv/versions/2.6.6/bin/irb:23:in `load'
        4: from /home/nathan/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/irb-1.2.4/exe/irb:11:in `<top (required)>'
        3: from (irb):2
        2: from /home/nathan/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/tod-2.2.0/lib/tod/time_of_day.rb:92:in `strftime'
        1: from /home/nathan/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/tod-2.2.0/lib/tod/time_of_day.rb:92:in `gsub!'
FrozenError (can't modify frozen String)
irb(main):003:0> 

Note this actualyl has been fixed in latest master (2e09f11) but no release has been cut

Suggestions on formatting JSON serialization?

Hello! Experimenting with the gem with a basic rails api - any suggestions on a good entry point/mechanism for customizing the JSON output of a ActiveRecord attribute storing a Tod::TimeOfDay? I'd like to show the outpost as a '11:00:00' for example rather than the components.

I'm just using a simple as_json for my models. There's lots of ways of doing this of course, adding a method that does the string formatting, using ActiveModelSerializers and overriding the attribute getter, but I'd like to find a higher-level way of customizing the output, if possible. Thank you!

Unexpected behaviour when parsing an integer

Currently, parse accepts an integer, and treats it as a string. In addition to this seeming strange (e.g. 45300 parses as 04:53), not all integers parse.

Those that in string form represent a valid time of day string do (e.g. 12:35) but those that don't, don't (e.g. 12:55).

Here's an interactive session demonstrating the behaviour, and in particular the annoying interaction with YML which parses times as integers:

[1] pry(main)> require 'tod'
=> true
[2] pry(main)> config = YAML.load(':tod: 12:35')
=> {:tod=>45300}
[3] pry(main)> Tod::TimeOfDay.parse(config[:tod])
=> #<Tod::TimeOfDay:0x00000001446708 @hour=4, @minute=53, @second=0, @second_of_day=17580>
[4] pry(main)> config = YAML.load(':tod: 12:55')
=> {:tod=>46500}
[5] pry(main)> Tod::TimeOfDay.parse(config[:tod])
ArgumentError: Invalid time of day string
from /home/duncan/.rvm/gems/ruby-2.2.3/gems/tod-2.0.2/lib/tod/time_of_day.rb:122:in `parse'

I'd like to submit a pull request to resolve this, but wanted to discuss a suitable resolution first.

I think the right thing to do is to have parse reject anything that isn't a string.

Query by TimeOfDay results in TypeError: Cannot visit Tod::TimeOfDay

Hi,

Not sure if this should work. But query with Tod I get TypeError
e.g.

Event.where(time_from: Tod::TimeOfDay.new(8, 30))

I get TypeError: Cannot visit Tod::TimeOfDay

where model is

class Event < ActiveRecord::Base
...
serialize :time_from, Tod::TimeOfDay
...

I expected this to work.
NOTE:

Event.create(time_from: Tod::TimeOfDay.new(8, 30)) 

works fine.

How to create a time in a specific timezone?

I strongly suspect that my issues arise out of a misunderstanding of how timezones work; if so, please feel free to point me in the direction of the necessary corrections.

My app is building timezones to determine if a given event occurs during specific business hours. For what I hope are obvious reasons, we need to do this calculation in a timezone-aware manner.

I would have expected the ToD gem to build that timezone based on the date I passed in. If I pass in Time.zone.now, I'd expect the result to be build on the current zone. Unfortunately, it appears that you just base it on the default time zone.

How do I create a time in a different time zone than the default zone, in a thread-safe manner so that multiple concurrent jobs won't stomp all over each other?

Rails 6 TypeError

When I try to use the tod gem for my rails app I get a TypeError can't convert nil into Integer. It gets stuck on the first line in the create action in my controller.

def create
    @event = current_user.hosted_events.build(event_params)
    if @event.save
      flash[:notice] = "Event Created"
      redirect_to user_path(current_user)
end

This is my event.rb file

require 'tod/core_extensions'
class Event < ApplicationRecord
  serialize :start_time, Tod::TimeOfDay
end

Tod Fails to Restore when loaded from active record when local time zone isn't DB time zone

If your database is stored in a different time zone (say, utc) than your default time zone (say, pacific), you run into issues when restoring models that have a stored TOD. In my case, I build a time, store it into the database, and then get back a different time value than was stored. If I generate a time with '8', plug it into the database, I get back a time with an hour part of 16 (4:00PM) when I reload the model.

Quick sketch of sample code below, with BusinessHour being a model with start_time as a time field in the DB, and set to serialize to TOD. You can reproduce with a sample application by adding the following to the application.rb file:

config.time_zone='Pacific Time (US & Canada)'
config.active_record.default_timezone = :utc

I'd run this in the console, but you could add it to a spec:

bh = BusinessHour.new
start_time = Tod::TimeOfDay.new(12)
bh.start_time = start_time
bh.save
bh.reload
bh.start_time == start_time # returns false
start_time #<Tod::TimeOfDay:0x007fe88b6fef48 @hour=12, @minute=0, @second=0, @second_of_day=28800>
bh.start_time #<Tod::TimeOfDay:0x007fe88bd51cd8 @hour=20, @minute=0, @second=0, @second_of_day=72000>

My first thought, because the application I'm working on worked fine before we converted to explicitly storing data in UTC, was a time zone issue. Everything worked fine when local time was the same as DB time, it only started breaking down when we had to explicitly lock DB time down to UTC to avoid timezone issues when importing data from the production server for testing.

shifts vs. ruby ranges

I was pleased to find that <=> is defined, so ranges can be used

> morning = Tod::TimeOfDay.new(10)
> noon = Tod::TimeOfDay.new(12)
> afternoon = Tod::TimeOfDay.new(14)
> (morning..afternoon).cover? noon
=> true

What's the value of using Tod::Shift instead of ruby ranges?

undefined method `type' for Tod::TimeOfDay:Class

Hello, thanks for ToD!

I was upgrading to 3.0 from the prior version, just before trying to upgrade my app to Rails 6.1, so here's the stack:

  • Ruby 2.7.2
  • Rails 6.0.4
  • ToD 3.0
class AssignmentSchedule::DaySegment < ApplicationRecord
  attribute :starts_at, Tod::TimeOfDay
  attribute :ends_at, Tod::TimeOfDay
  attribute :accept_time, Tod::TimeOfDay
  attribute :respond_time, Tod::TimeOfDay
  # ...
end

All these fields used to be serialize, but I was getting errors around the expected arguments of the TOD initialize method, so, after reading some issues in this repo, I changed it for attribute.

But now, when I try to run my rspec suite in CI I get this error:

NoMethodError: undefined method `type' for Tod::TimeOfDay:Class
bundle/ruby/2.7.0/gems/activerecord-6.0.4/lib/active_record/attribute_methods/time_zone_conversion.rb:84:in `create_time_zone_conversion_attribute?'
bundle/ruby/2.7.0/gems/activerecord-6.0.4/lib/active_record/attribute_methods/time_zone_conversion.rb:73:in `block (2 levels) in inherited'

Locally, I'm using the dead_end gem which provides some more context info:

NoMethodError:
  undefined method `type' for Tod::TimeOfDay:Class

     3  module ActiveRecord
     4    module AttributeMethods
     5      module TimeZoneConversion
    64        module ClassMethods # :nodoc:
    65          private
    80            def create_time_zone_conversion_attribute?(name, cast_type)
    81              enabled_for_column = time_zone_aware_attributes &&
  โฏ 84              enabled_for_column && time_zone_aware_types.include?(cast_type.type)
    85            end
    86        end
    87      end
    88    end
    89  end

Looking at AR's code, the default set for time_zone_aware_types is %i[datetime time](here)

The cast type here would be Tod::TimeOfDay, as second argument to the attribute declaration

Seems this class needs to define a type method or attribute that returns something that can be reconciled with the time_zone_aware_types config?

In my app, I have it as %i[datetime] as defined in the application as per this config line in application.rb:

config.active_record.time_zone_aware_types = [:datetime]

Other options:

  1. Some more info needs to be passed into the declaration of my attributes? Using options maybe?
  2. Do I need to register a custom type?

Any help or guidance to determine this will be appreciated!

Thanks a lot!

Tod::TimeOfDay#to_formatted_s

Hello,

Would you consider accepting a patch that implemented #to_formatted_s similar to ActiveSupport::Time and ActiveSupport::Date?

I have this monkey patch in my app.

if Tod::TimeOfDay.new(8).respond_to?(:to_formatted_s)
  raise "Tod::TimeOfDay monkey patch may be redundant. Check #{__FILE__}"
else
  module Tod
    class TimeOfDay
      TIME_FORMATS = {
        twelve_hour: -> (tod) { tod.min == 0 ? tod.strftime("%-l %P") : tod.strftime("%-l:%M %P") }
      }

      def to_formatted_s(format = :default)
        if formatter = TIME_FORMATS[format]
          if formatter.respond_to?(:call)
            formatter.call(self).to_s
          else
            strftime(formatter)
          end
        else
          to_s
        end
      end
    end
  end
end

My app is localized it would be great to be able to switch the format based on the current locale. I guess the biggest barrier to this enhancement would be agreeing on the formats?

I'm happy to do the work if the maintainer is supportive.

ActiveRecord 5.2.3 breaks 24:00:00 serialization

  1) Failure:
TimeOfDay with ActiveRecord Serializable Attribute::.load#test_0004_dump 24:00:00 and get it back [/home/jack/dev/tod/test/tod/time_of_day_serializable_attribute_test.rb:61]:
--- expected
+++ actual
@@ -1 +1 @@
-#<Tod::TimeOfDay:0xXXXXXX @hour=24, @minute=0, @second=0, @second_of_day=86400>
+#<Tod::TimeOfDay:0xXXXXXX @hour=0, @minute=0, @second=0, @second_of_day=0>

Works fine with ActiveRecord 5.2.2. Not sure what they changed.

Making `Tod::TimeOfDay.parse` more flexible

Proposal

Currently, the only separator permitted by the parser is a colon (:)

How do you feel about making the parser more flexible by allowing any non-digit character to be treated as a seperator?

Rationale

I work in the rail transport sector and railway timetables can contain a whole variety of separators, each with their own meaning:

  • 08.00 - Regular passenger train
  • 08+00 - Empty coaching stock
  • 08S00 - Stop for staff pick up / set down only
  • 08RM00 - Train will make a reversing move. (Depart in the same direction it arrived)

I'm sure there are other areas, including user input via html forms, where greater flexibilty might be beneficial.

Allowing any separator will make parsing any data more flexible and simpler.

Problems

The change could possibly break clients that rely on Tod parsability to validate data that must only use a colon as a seperator

Can't make it work on rails 5.0.0

Hello,

Thank you so much for developing this gem, it will help me a lot. The problem is that I can't make it work. I'm currently working with ruby 2.3.3 and rails 5.0.0.

I have an Event model with start_time and end_time, that are postgresql's time attributes.

In the migration file:

class CreateEvents < ActiveRecord::Migration[5.0]
  def change
    create_table :events do |t|
      ...
      t.time :start_time
      t.time :end_time
      t.timestamps
    end
  end
end

In event.rb:

class Event < ApplicationRecord
    serialize :start_time, Tod::TimeOfDay
    serialize :end_time, Tod::TimeOfDay

In application.rb:

  config.active_record.time_zone_aware_types = [:datetime]

Finally, when I try to create one I get:

init(main)> event = Event.new(start_date: Date.today, start_time: Tod::TimeOfDay.new(9,30), end_time: Tod::TimeOfDay.new(20,30), full_day_event: false, recurrent: false)
#<Event:0x007f81c03a3b00> {
        :id => nil,
        :start_date => Wed, 05 Sep 2018,
        :start_time => #<Tod::TimeOfDay:0x007f81c03a1378 @hour=9, @minute=30, @second=0, @second_of_day=34200>,
        :end_time => #<Tod::TimeOfDay:0x007f81bf9d4548 @hour=20, @minute=30, @second=0, @second_of_day=73800>,
        :full_day_event => false,
        :recurrent => false,
        :created_at => nil,
        :updated_at => nil
}

init(main)> event.save
   (0.2ms)  BEGIN
  SQL (0.8ms)  INSERT INTO "events" ("start_date", "full_day_event", "recurrent", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5,) RETURNING "id"  [["start_date", "2018-09-05"], ["full_day_event", "f"], ["recurrent", "f"], ["created_at", "2018-09-05 20:11:06.345258"], ["updated_at", "2018-09-05 20:11:06.345258"]]
   (3.2ms)  COMMIT
true

init(main)> event
#<Event:0x007f81c03a3b00> {
        :id => 14,
        :start_date => Wed, 05 Sep 2018,
        :start_time => nil,
        :end_time => nil,
        :full_day_event => false,
        :recurrent => false,
        :created_at => Wed, 05 Sep 2018 17:11:06 -03 -03:00,
        :updated_at => Wed, 05 Sep 2018 17:11:06 -03 -03:00
}

So neither the start_time and end_time are considered on the SQL query and are not saved in the database without showing any errors.

Is there anything I'm doing wrong?

Rails 5 multiparameter attributes

I seems we cannot pass paramters from controller to model in the multiparameter ("(4i)...") way in rails 5, the same thing work in rails 4.
Given the example

class Item < ActiveRecord::Base
  serialize :time, Tod::TimeOfDay
end
...
item.assign_attributes({"time(4i)" => "8", "time(5i)" => "6", "time(6i)" => "5"})
assert_equal Tod::TimeOfDay.new(8,6,5), item.time
--- expected
+++ actual
@@ -1 +1 @@
-#<Tod::TimeOfDay:0xXXXXXX @hour=8, @minute=6, @second=5, @second_of_day=29165>
+nil

Full runnable sample:

begin
  require "bundler/inline"
rescue LoadError => e
  $stderr.puts "Bundler version 1.10 or later is required. Please update your Bundler"
  raise e
end

gemfile(true) do
  source "https://rubygems.org"
  # Activate the gem you are reporting the issue against.
  gem "activerecord", "5.0.1"
  #gem "activerecord", "4.2.1" #
  gem "sqlite3"
  gem "byebug"
  gem "tod"
end

require "active_record"
require "minitest/autorun"
require "logger"

# Ensure backward compatibility with Minitest 4
Minitest::Test = MiniTest::Unit::TestCase unless defined?(Minitest::Test)

# This connection will do for database-independent bug reports.
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
ActiveRecord::Base.logger = Logger.new(STDOUT)

ActiveRecord::Schema.define do
  create_table :items, force: true do |t|
    t.time "time"
  end
end

class Item < ActiveRecord::Base
  serialize :time, Tod::TimeOfDay
end

class BugTest < Minitest::Test
  def test_association_stuff
    item = Item.new
    item.assign_attributes({"time(4i)" => "8", "time(5i)" => "6", "time(6i)" => "5"})

    assert_equal Tod::TimeOfDay.new(8,6,5), item.time
  end
end

Serialization of attributes in Rails 6

In Rails 5.x serializtion of attributes had no issue

serialize :time_open Tod::TimeOfDay

When upgrading my app to Rails 6.1 the same serialization threw the following error:
Cannot serialize Tod::TimeOfDay. Classes passed to serialize must have a 0 argument constructor.

Is there a need to use something like active_model_serializers to serialize the attribute now or am I missing something?

Minutes are truncated when passing in a Float

Minutes are truncated when passing in a Float.

2.3.1 :004 > Tod::TimeOfDay.from_second_of_day(55800.0)
 => #<Tod::TimeOfDay:0x007f957a4237d8 @hour=15, @minute=0, @second=0, @second_of_day=54000>
2.3.1 :005 > Tod::TimeOfDay.from_second_of_day(55800)
 => #<Tod::TimeOfDay:0x007f957a3f2d90 @hour=15, @minute=30, @second=0, @second_of_day=55800>
2.3.1 :006 > Tod::TimeOfDay.from_second_of_day(55800.0.to_i)
 => #<Tod::TimeOfDay:0x007f957a3d0268 @hour=15, @minute=30, @second=0, @second_of_day=55800>

output of subtraction and addition

I was surprised to find that a duration is represented with a Tod::TimeOfDay. This seems semantically incorrect. It seems it should be either some sort of new class representing a period of time or just a float representing seconds, as is done in other libraries.

Would you be open to changing this behavior for the next major version release?

> noon = Tod::TimeOfDay.new(12)
> afternoon = Tod::TimeOfDay.new(14)
> d = afternoon-noon
=> #<Tod::TimeOfDay:0x0000000559f038 @hour=2, @minute=0, @second=0, @second_of_day=7200>
> d.to_i / 60 / 60
=> 2

Slight inconsistency with overlaps? and exclusive end times

I came across one apparent oddity in an edge case, illustrated by this code:

require 'tod'

all_day = Tod::Shift.new(Tod::TimeOfDay.new(9), Tod::TimeOfDay.new(17), true)


puts "In the middle"
instant = Tod::Shift.new(Tod::TimeOfDay.new(10), Tod::TimeOfDay.new(10), true)
puts all_day.overlaps?(instant)   # true
puts instant.overlaps?(all_day)   # true


puts "At the beginning"
instant = Tod::Shift.new(Tod::TimeOfDay.new(9), Tod::TimeOfDay.new(9), true)
puts all_day.overlaps?(instant)   # true
puts instant.overlaps?(all_day)   # false


puts "At the end"
instant = Tod::Shift.new(Tod::TimeOfDay.new(17), Tod::TimeOfDay.new(17), true)
puts all_day.overlaps?(instant)   # false
puts instant.overlaps?(all_day)   # false

All the calls produce the result which I would expect except the third one. Should that not produce false, if only for consistency?

Travis CI shows error "NoMethodError: undefined method `spec' for nil:NilClass"

Hi, @jackc.
I pull requested tod. (PR #35)
then, Travis CI shows errors "NoMethodError: undefined method `spec' for nil:NilClass" in ruby 1.9.3, 2.1.5 and 2.2.0.
But ruby 2.0.0 has no errors.

I think this problem caused by rubygems/rubygems - remove extra caching on spec.
Bundler corresponded for This change at v1.10.0.pre(this commit).
So, I think you need update bundler in Travis CI by add - gem update bundler to .travis.yml.

 before_install:
   - .......
 + - gem update bundler

Can you explain?

Hi @jackc

Seems like a really useful gem, thanks.

I'm curious,

class Order < ActiveRecord::Base
  serialize :time, Tod::TimeOfDay
end
order = Order.create(time: Tod::TimeOfDay.new(9,30))
order.time                                      # => 09:30:00

indicates to me that the call to .new(9,30) is retrievable from the database when calling .time() and I would expect as you've documented to see 09:30:00 as the return value.

I'm not quite getting that behavior and I don't see the method to 'reconstruct' the Tod object back to a string, which as one would imagine is quite necessary.

I've stored something using both the pattern

obj.attr == Tod::TimeOfDay.new(1,0)

&

obj.attr == Tod::TimeOfDay "1:00am"

Both of the above which appear to be storing something like,

<Tod::TimeOfDay:0x007f94249c8190 @hour=1, @minute=0, @second=0, @second_of_day=3600>

In the database. Interesting that no matter the write pattering being used, the return object checks true for equality.

obj.attr = Tod::TimeOfDay("1:00am")
=> #<Tod::TimeOfDay:0x007f9424c23598 @hour=1, @minute=0, @second=0, @second_of_day=3600>

What am I clearly missing here?

Cheers,

JD

Compatibility issue with Rails 5, Chrome 86 and Time fields

Not necessarily an issue with Tod, but a small change to Tod would fix the problem.

Using Tod::TimeOfDay in a Rails 5 app, with Chrome and HTML time fields. There seems to be an odd bit of behaviour in Chrome, in that if an HTML time field has been modified by the user, the contents are returned as "08:30", whilst if the field remains unmodified from when the form was created, the same field comes back as "08:30:00.000", which Tod::TimeOfDay.parse doesn't understand, resulting in the field in the database record being set to nil.

It would appear that when you create a time field in Rails for an item held in a Tod::TimeOfDay with:

<%= f.time_field :my_tod_time %>

Then the format used in the HTML sent to the browser is "08:30:00.000", and if the user doesn't modify it then that's what Chrome sends back. Tod::TimeOfDay then doesn't understand it and you end up with a nil field. I've tried changing default formatting for both Time and Tod::TimeOfDay but neither has en effect. I've yet to identify what causes the long string to be generated in the first place.

I'm far from sure where the fundamental inconsistency is here. Feedback appreciated.

undefined method 'mon' with I18n / Rails 6

Loading development environment (Rails 6.0.3.6)
3.0.0 :001 > I18n.l Tod::TimeOfDay.new(8, 30), format: :short
Traceback (most recent call last):
        1: from (irb):1:in `<main>'
NoMethodError (undefined method `mon' for #<Tod::TimeOfDay:0x0000563b1ef2cfd8 @hour=8, @minute=30, @second=0, @second_of_day=30600>)
Did you mean?  min
  • tod (3.0.0)
  • rails (6.0.3.6)
  • rails-i18n (6.0.0)
  • i18n (1.8.9)

Implement to_proc

Currently and array of strings can't be mapped into ToD with:
string_times.map(&Tod::TimeOfDay.new)

Add union and intersect methods to Tod::Shift

Use cases:

  • union for when you have multiple shifts within the same time period (e.g. different people on a team) and want to find the effective shift when somebody is working.
  • intersect for a similar scenario, but you want to find when somebody is not working.

It's possible that this might be problematic to implement in a general sense because it could be nondeterministic with shifts that go into the next day. I'm going to be writing this anyway as we have the requirement to do this within a day, but would be happy to have a go at writing a more general approach if you're interested in having it in the library?

Serialization does not work with Rails 6.1

Hi There,

I have a Rails 6.0.4 app where I have been using TOD without any issues. But, when I am doing a Rails upgrade to 6.1.4, I found that the serialization does not work anymore. Here is my model file:

class Shift < ApplicationRecord
  serialize :start_time, Tod::TimeOfDay
  serialize :end_time, Tod::TimeOfDay
end

In Rails 6.0.4, I am getting expected results:

> Shift.last.start_time
=> #<Tod::TimeOfDay:0x00007f81aaf8e408 @hour=0, @minute=0, @second=0, @second_of_day=0>
> Shift.last.end_time
=> #<Tod::TimeOfDay:0x00007f81aabb4378 @hour=5, @minute=0, @second=0, @second_of_day=18000>

But, in Rails 6.1.4, I am getting nil values for the TOD fields:

> Shift.last.start_time
=> nil
> Shift.last.end_time
=> nil

Is it a known issue? Or, am I missing something?

Best,
Rakib

undefined method `register' for ActiveRecord::Type:Module

After updating to tod 2.2 in a Rails 4.2 environment/ruby 2.5

28: from bin/rails:4:in <main>' 27: from bin/rails:4:in require'
26: from /Users/cbillen/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/railties-4.2.10/lib/rails/commands.rb:17:in <top (required)>' 25: from /Users/cbillen/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/railties-4.2.10/lib/rails/commands/commands_tasks.rb:39:in run_command!'
24: from /Users/cbillen/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/railties-4.2.10/lib/rails/commands/commands_tasks.rb:67:in console' 23: from /Users/cbillen/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/railties-4.2.10/lib/rails/commands/commands_tasks.rb:142:in require_application_and_environment!'
22: from /Users/cbillen/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/railties-4.2.10/lib/rails/application.rb:328:in require_environment!' 21: from /Users/cbillen/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/activesupport-4.2.10/lib/active_support/dependencies.rb:274:in require'
20: from /Users/cbillen/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/activesupport-4.2.10/lib/active_support/dependencies.rb:240:in load_dependency' 19: from /Users/cbillen/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/activesupport-4.2.10/lib/active_support/dependencies.rb:274:in block in require'
18: from /Users/cbillen/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/activesupport-4.2.10/lib/active_support/dependencies.rb:274:in require' 17: from /Users/cbillen/Projects/heatwave/config/environment.rb:7:in <top (required)>'
16: from /Users/cbillen/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/railties-4.2.10/lib/rails/railtie.rb:194:in method_missing' 15: from /Users/cbillen/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/railties-4.2.10/lib/rails/railtie.rb:194:in public_send'
14: from /Users/cbillen/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/railties-4.2.10/lib/rails/application.rb:352:in initialize!' 13: from /Users/cbillen/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/railties-4.2.10/lib/rails/initializable.rb:54:in run_initializers'
12: from /Users/cbillen/.rbenv/versions/2.5.0/lib/ruby/2.5.0/tsort.rb:205:in tsort_each' 11: from /Users/cbillen/.rbenv/versions/2.5.0/lib/ruby/2.5.0/tsort.rb:226:in tsort_each'
10: from /Users/cbillen/.rbenv/versions/2.5.0/lib/ruby/2.5.0/tsort.rb:347:in each_strongly_connected_component' 9: from /Users/cbillen/.rbenv/versions/2.5.0/lib/ruby/2.5.0/tsort.rb:347:in call'
8: from /Users/cbillen/.rbenv/versions/2.5.0/lib/ruby/2.5.0/tsort.rb:347:in each' 7: from /Users/cbillen/.rbenv/versions/2.5.0/lib/ruby/2.5.0/tsort.rb:349:in block in each_strongly_connected_component'
6: from /Users/cbillen/.rbenv/versions/2.5.0/lib/ruby/2.5.0/tsort.rb:431:in each_strongly_connected_component_from' 5: from /Users/cbillen/.rbenv/versions/2.5.0/lib/ruby/2.5.0/tsort.rb:350:in block (2 levels) in each_strongly_connected_component'
4: from /Users/cbillen/.rbenv/versions/2.5.0/lib/ruby/2.5.0/tsort.rb:228:in block in tsort_each' 3: from /Users/cbillen/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/railties-4.2.10/lib/rails/initializable.rb:55:in block in run_initializers'
2: from /Users/cbillen/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/railties-4.2.10/lib/rails/initializable.rb:30:in run' 1: from /Users/cbillen/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/railties-4.2.10/lib/rails/initializable.rb:30:in instance_exec'
/Users/cbillen/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/tod-2.2.0/lib/tod/railtie.rb:6:in `block in class:Railtie'

Does not work for me at least

I have a model with:

time_from :time
time_to :time

When saving gives: TypeError: can't cast Tod::TimeOfDay to time

Which obviously is because of serialization, if I add the lines to my model:

serialize :time_from, Tod::TimeOfDay
serialize :time_to, Tod::TimeOfDay

It gives the following error:

NoMethodError: undefined method `empty?' for 2000-01-01 08:00:00 UTC:Time
    from /Users/Kenny/.tokaido/Gems/2.1.0/gems/tod-2.0.1/lib/tod/time_of_day.rb:172:in `load'
    from /Users/Kenny/.tokaido/Gems/2.1.0/gems/activerecord-4.2.0/lib/active_record/type/serialized.rb:19:in `type_cast_from_database'
    from /Users/Kenny/.tokaido/Gems/2.1.0/gems/activerecord-4.2.0/lib/active_record/type/mutable.rb:5:in `type_cast_from_user'
    from /Users/Kenny/.tokaido/Gems/2.1.0/gems/activerecord-4.2.0/lib/active_record/attribute.rb:100:in `type_cast'
    from /Users/Kenny/.tokaido/Gems/2.1.0/gems/activerecord-4.2.0/lib/active_record/attribute.rb:42:in `original_value'
    from /Users/Kenny/.tokaido/Gems/2.1.0/gems/activerecord-4.2.0/lib/active_record/attribute.rb:37:in `value'
    from /Users/Kenny/.tokaido/Gems/2.1.0/gems/activerecord-4.2.0/lib/active_record/attribute.rb:46:in `value_for_database'
    from /Users/Kenny/.tokaido/Gems/2.1.0/gems/activerecord-4.2.0/lib/active_record/attribute_methods/dirty.rb:164:in `store_original_raw_attribute'
    from /Users/Kenny/.tokaido/Gems/2.1.0/gems/activerecord-4.2.0/lib/active_record/attribute_methods/dirty.rb:93:in `write_attribute'
    from /Users/Kenny/.tokaido/Gems/2.1.0/gems/activerecord-4.2.0/lib/active_record/attribute_methods.rb:50:in `__temp__4796d656f56627f6d6'

when trying to save the following object:

{:day=>1, :time_from=>#<Tod::TimeOfDay:0x007f8813639a00 @hour=8, @minute=0, @second=0, @second_of_day=28800>, :time_to=>#<Tod::TimeOfDay:0x007f8813638fd8 @hour=15, @minute=0, @second=0, @second_of_day=54000>, :default_duration=>3600 seconds

Active Record value casting can cause unexpected behavior with Time values

  • Rails 5.2.2.1
  • tod version 2.2.0

I work on an application which uses the tod gem. A test started failing today (April 2nd) because of some unexpected behavior when assigning a Time value.

Summary

>> today = Date.parse("2019-04-02")
#=> Tue, 02 Apr 2019
>> schedule.open_at = today.beginning_of_day
#=> Tue, 02 Apr 2019 00:00:00 EDT -04:00
>> schedule.close_at = today.end_of_day
=> Tue, 02 Apr 2019 23:59:59 EDT -04:00
>> [schedule.open_at.to_s, schedule.close_at.to_s]
#=> ["24:00:00", "23:59:59"]

It looks like the "24:00:00" value here happens only on the 2nd of the month, and on other days the result would be ["00:00:00", "23:59:59"]:

>> today = Date.parse("2019-04-03")
#=> Wed, 03 Apr 2019
>> schedule.open_at = today.beginning_of_day
#=> Wed, 03 Apr 2019 00:00:00 EDT -04:00
>> schedule.close_at = today.end_of_day
#=> Wed, 03 Apr 2019 23:59:59 EDT -04:00
>> [schedule.open_at.to_s, schedule.close_at.to_s]
#=> ["00:00:00", "23:59:59"]

Further details

We have an AR model representing a schedule. It has 2 attributes backed by time without time zone:

-- structure.sql
open_at time without time zone NOT NULL,
close_at time without time zone NOT NULL,

Because of the way AR casting works with TimeOfDayType, TimeOfDay.load is called when assigning attributes. In the case where the RHS value is a Time, this can lead to unexpected behavior because of a time.day == 2 conditional in TimeOfDay.load:

>> today = Date.parse("2019-04-02")
#=> Tue, 02 Apr 2019
>> schedule.open_at = today.beginning_of_day
#=> Tue, 02 Apr 2019 00:00:00 EDT -04:00
>> schedule.close_at = today.end_of_day
=> Tue, 02 Apr 2019 23:59:59 EDT -04:00
>> [schedule.open_at.to_s, schedule.close_at.to_s]
#=> ["24:00:00", "23:59:59"]

I think this is pretty unexpected, because the behavior is different for other days, like the 3rd of a month:

>> today = Date.parse("2019-04-03")
#=> Wed, 03 Apr 2019
>> schedule.open_at = today.beginning_of_day
#=> Wed, 03 Apr 2019 00:00:00 EDT -04:00
>> schedule.close_at = today.end_of_day
#=> Wed, 03 Apr 2019 23:59:59 EDT -04:00
>> [schedule.open_at.to_s, schedule.close_at.to_s]
#=> ["00:00:00", "23:59:59"]

You can see that on April 2nd the result is ["24:00:00", "23:59:59"], but on April 3rd the result is ["00:00:00", "23:59:59"].

You can see this more directly by calling methods directly:

>> Tod::TimeOfDay.load(Date.parse("2019-03-26").in_time_zone("US/Eastern").tap { |t| p t }).to_s
Tue, 26 Mar 2019 00:00:00 EDT -04:00
#=> "00:00:00"
>> Tod::TimeOfDay.load(Date.parse("2019-04-02").in_time_zone("US/Eastern").tap { |t| p t }).to_s
Tue, 02 Apr 2019 00:00:00 EDT -04:00
#=> "24:00:00"

I'm not entirely sure why there's a time.day == 2 check, but my guess is that there's an assumption somewhere that time values will mostly have day == 1, so that midnight on day == 2 can represent 24:00.

to_s deprecated in rails 7, becomes to_fs

Rails 7 deprecates to_s, so

Time.new.to_s(:db)
DEPRECATION WARNING: Time#to_s(:db) is deprecated. Please use Time#to_fs(:db) instead.

It would be nice to alias to_formatted_s to to_fs to maintain consistency.

ActiveRecord 6 compatibility

It's seems something has changed in the ActiveRecord 6 serializer API.

jack@glados ~/dev/tod ยฑmasterโšก ยป bundle exec rake
/Users/jack/.asdf/installs/ruby/2.6.5/bin/ruby -w -I"lib:lib:test" -I"/Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/rake-12.3.2/lib" "/Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/rake-12.3.2/lib/rake/rake_test_loader.rb" "test/tod/conversion_test.rb" "test/tod/date_test.rb" "test/tod/shift_test.rb" "test/tod/time_of_day_serializable_attribute_test.rb" "test/tod/time_of_day_test.rb" "test/tod/time_of_day_time_zone_with_active_support_test.rb" "test/tod/time_test.rb"
-- create_table(:orders)
   -> 0.0037s
Run options: --seed 31062

# Running:

E.EE......EEEE........................................................................................................E....................

Finished in 0.035856s, 3876.6176 runs/s, 7307.0058 assertions/s.

  1) Error:
TimeOfDay with ActiveRecord Serializable Attribute::.dump#test_0003_works with multiparam time arguments:
ArgumentError: wrong number of arguments (given 2, expected 1)
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/arel-9.0.0/lib/arel/visitors/to_sql.rb:73:in `compile'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:25:in `to_sql_and_binds'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:165:in `insert'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/query_cache.rb:22:in `insert'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/persistence.rb:375:in `_insert_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/persistence.rb:932:in `_create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/counter_cache.rb:166:in `_create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/locking/optimistic.rb:70:in `_create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/attribute_methods/dirty.rb:211:in `_create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/callbacks.rb:331:in `block in _create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:101:in `run_callbacks'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:825:in `_run_create_callbacks'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/callbacks.rb:331:in `_create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/timestamp.rb:110:in `_create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/persistence.rb:905:in `create_or_update'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/callbacks.rb:327:in `block in create_or_update'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:101:in `run_callbacks'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:825:in `_run_save_callbacks'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/callbacks.rb:327:in `create_or_update'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/timestamp.rb:128:in `create_or_update'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/persistence.rb:503:in `save!'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/validations.rb:53:in `save!'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/transactions.rb:318:in `block in save!'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/transactions.rb:375:in `block in with_transaction_returning_status'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:280:in `block in transaction'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/transaction.rb:280:in `block in within_new_transaction'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/concurrency/load_interlock_aware_monitor.rb:26:in `block (2 levels) in synchronize'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/concurrency/load_interlock_aware_monitor.rb:25:in `handle_interrupt'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/concurrency/load_interlock_aware_monitor.rb:25:in `block in synchronize'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/concurrency/load_interlock_aware_monitor.rb:21:in `handle_interrupt'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/concurrency/load_interlock_aware_monitor.rb:21:in `synchronize'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/transaction.rb:278:in `within_new_transaction'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:280:in `transaction'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/transactions.rb:212:in `transaction'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/transactions.rb:366:in `with_transaction_returning_status'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/transactions.rb:318:in `save!'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/suppressor.rb:48:in `save!'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/persistence.rb:55:in `create!'
    /Users/jack/dev/tod/test/tod/time_of_day_serializable_attribute_test.rb:19:in `block (3 levels) in <top (required)>'

  2) Error:
TimeOfDay with ActiveRecord Serializable Attribute::.dump#test_0001_sets time of day:
ArgumentError: wrong number of arguments (given 2, expected 1)
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/arel-9.0.0/lib/arel/visitors/to_sql.rb:73:in `compile'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:25:in `to_sql_and_binds'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:165:in `insert'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/query_cache.rb:22:in `insert'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/persistence.rb:375:in `_insert_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/persistence.rb:932:in `_create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/counter_cache.rb:166:in `_create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/locking/optimistic.rb:70:in `_create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/attribute_methods/dirty.rb:211:in `_create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/callbacks.rb:331:in `block in _create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:101:in `run_callbacks'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:825:in `_run_create_callbacks'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/callbacks.rb:331:in `_create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/timestamp.rb:110:in `_create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/persistence.rb:905:in `create_or_update'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/callbacks.rb:327:in `block in create_or_update'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:101:in `run_callbacks'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:825:in `_run_save_callbacks'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/callbacks.rb:327:in `create_or_update'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/timestamp.rb:128:in `create_or_update'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/persistence.rb:503:in `save!'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/validations.rb:53:in `save!'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/transactions.rb:318:in `block in save!'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/transactions.rb:375:in `block in with_transaction_returning_status'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:280:in `block in transaction'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/transaction.rb:280:in `block in within_new_transaction'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/concurrency/load_interlock_aware_monitor.rb:26:in `block (2 levels) in synchronize'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/concurrency/load_interlock_aware_monitor.rb:25:in `handle_interrupt'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/concurrency/load_interlock_aware_monitor.rb:25:in `block in synchronize'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/concurrency/load_interlock_aware_monitor.rb:21:in `handle_interrupt'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/concurrency/load_interlock_aware_monitor.rb:21:in `synchronize'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/transaction.rb:278:in `within_new_transaction'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:280:in `transaction'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/transactions.rb:212:in `transaction'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/transactions.rb:366:in `with_transaction_returning_status'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/transactions.rb:318:in `save!'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/suppressor.rb:48:in `save!'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/persistence.rb:55:in `create!'
    /Users/jack/dev/tod/test/tod/time_of_day_serializable_attribute_test.rb:11:in `block (3 levels) in <top (required)>'

  3) Error:
TimeOfDay with ActiveRecord Serializable Attribute::.dump#test_0002_sets nil as value:
ArgumentError: wrong number of arguments (given 2, expected 1)
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/arel-9.0.0/lib/arel/visitors/to_sql.rb:73:in `compile'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:25:in `to_sql_and_binds'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:165:in `insert'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/query_cache.rb:22:in `insert'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/persistence.rb:375:in `_insert_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/persistence.rb:932:in `_create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/counter_cache.rb:166:in `_create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/locking/optimistic.rb:70:in `_create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/attribute_methods/dirty.rb:211:in `_create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/callbacks.rb:331:in `block in _create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:101:in `run_callbacks'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:825:in `_run_create_callbacks'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/callbacks.rb:331:in `_create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/timestamp.rb:110:in `_create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/persistence.rb:905:in `create_or_update'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/callbacks.rb:327:in `block in create_or_update'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:101:in `run_callbacks'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:825:in `_run_save_callbacks'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/callbacks.rb:327:in `create_or_update'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/timestamp.rb:128:in `create_or_update'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/persistence.rb:503:in `save!'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/validations.rb:53:in `save!'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/transactions.rb:318:in `block in save!'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/transactions.rb:375:in `block in with_transaction_returning_status'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:280:in `block in transaction'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/transaction.rb:280:in `block in within_new_transaction'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/concurrency/load_interlock_aware_monitor.rb:26:in `block (2 levels) in synchronize'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/concurrency/load_interlock_aware_monitor.rb:25:in `handle_interrupt'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/concurrency/load_interlock_aware_monitor.rb:25:in `block in synchronize'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/concurrency/load_interlock_aware_monitor.rb:21:in `handle_interrupt'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/concurrency/load_interlock_aware_monitor.rb:21:in `synchronize'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/transaction.rb:278:in `within_new_transaction'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:280:in `transaction'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/transactions.rb:212:in `transaction'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/transactions.rb:366:in `with_transaction_returning_status'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/transactions.rb:318:in `save!'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/suppressor.rb:48:in `save!'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/persistence.rb:55:in `create!'
    /Users/jack/dev/tod/test/tod/time_of_day_serializable_attribute_test.rb:15:in `block (3 levels) in <top (required)>'

  4) Error:
TimeOfDay with ActiveRecord Serializable Attribute::.load#test_0001_loads set Tod::TimeOfDay:
ArgumentError: wrong number of arguments (given 2, expected 1)
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/arel-9.0.0/lib/arel/visitors/to_sql.rb:73:in `compile'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:25:in `to_sql_and_binds'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:165:in `insert'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/query_cache.rb:22:in `insert'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/persistence.rb:375:in `_insert_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/persistence.rb:932:in `_create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/counter_cache.rb:166:in `_create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/locking/optimistic.rb:70:in `_create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/attribute_methods/dirty.rb:211:in `_create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/callbacks.rb:331:in `block in _create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:101:in `run_callbacks'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:825:in `_run_create_callbacks'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/callbacks.rb:331:in `_create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/timestamp.rb:110:in `_create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/persistence.rb:905:in `create_or_update'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/callbacks.rb:327:in `block in create_or_update'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:101:in `run_callbacks'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:825:in `_run_save_callbacks'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/callbacks.rb:327:in `create_or_update'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/timestamp.rb:128:in `create_or_update'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/persistence.rb:503:in `save!'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/validations.rb:53:in `save!'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/transactions.rb:318:in `block in save!'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/transactions.rb:375:in `block in with_transaction_returning_status'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:280:in `block in transaction'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/transaction.rb:280:in `block in within_new_transaction'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/concurrency/load_interlock_aware_monitor.rb:26:in `block (2 levels) in synchronize'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/concurrency/load_interlock_aware_monitor.rb:25:in `handle_interrupt'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/concurrency/load_interlock_aware_monitor.rb:25:in `block in synchronize'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/concurrency/load_interlock_aware_monitor.rb:21:in `handle_interrupt'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/concurrency/load_interlock_aware_monitor.rb:21:in `synchronize'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/transaction.rb:278:in `within_new_transaction'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:280:in `transaction'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/transactions.rb:212:in `transaction'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/transactions.rb:366:in `with_transaction_returning_status'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/transactions.rb:318:in `save!'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/suppressor.rb:48:in `save!'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/persistence.rb:55:in `create!'
    /Users/jack/dev/tod/test/tod/time_of_day_serializable_attribute_test.rb:39:in `block (3 levels) in <top (required)>'

  5) Error:
TimeOfDay with ActiveRecord Serializable Attribute::.load#test_0003_returns nil if time is not set:
ArgumentError: wrong number of arguments (given 2, expected 1)
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/arel-9.0.0/lib/arel/visitors/to_sql.rb:73:in `compile'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:25:in `to_sql_and_binds'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:165:in `insert'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/query_cache.rb:22:in `insert'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/persistence.rb:375:in `_insert_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/persistence.rb:932:in `_create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/counter_cache.rb:166:in `_create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/locking/optimistic.rb:70:in `_create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/attribute_methods/dirty.rb:211:in `_create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/callbacks.rb:331:in `block in _create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:101:in `run_callbacks'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:825:in `_run_create_callbacks'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/callbacks.rb:331:in `_create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/timestamp.rb:110:in `_create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/persistence.rb:905:in `create_or_update'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/callbacks.rb:327:in `block in create_or_update'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:101:in `run_callbacks'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:825:in `_run_save_callbacks'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/callbacks.rb:327:in `create_or_update'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/timestamp.rb:128:in `create_or_update'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/persistence.rb:503:in `save!'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/validations.rb:53:in `save!'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/transactions.rb:318:in `block in save!'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/transactions.rb:375:in `block in with_transaction_returning_status'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:280:in `block in transaction'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/transaction.rb:280:in `block in within_new_transaction'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/concurrency/load_interlock_aware_monitor.rb:26:in `block (2 levels) in synchronize'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/concurrency/load_interlock_aware_monitor.rb:25:in `handle_interrupt'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/concurrency/load_interlock_aware_monitor.rb:25:in `block in synchronize'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/concurrency/load_interlock_aware_monitor.rb:21:in `handle_interrupt'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/concurrency/load_interlock_aware_monitor.rb:21:in `synchronize'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/transaction.rb:278:in `within_new_transaction'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:280:in `transaction'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/transactions.rb:212:in `transaction'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/transactions.rb:366:in `with_transaction_returning_status'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/transactions.rb:318:in `save!'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/suppressor.rb:48:in `save!'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/persistence.rb:55:in `create!'
    /Users/jack/dev/tod/test/tod/time_of_day_serializable_attribute_test.rb:52:in `block (3 levels) in <top (required)>'

  6) Error:
TimeOfDay with ActiveRecord Serializable Attribute::.load#test_0004_dump 24:00:00 and get it back:
ArgumentError: wrong number of arguments (given 2, expected 1)
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/arel-9.0.0/lib/arel/visitors/to_sql.rb:73:in `compile'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:25:in `to_sql_and_binds'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:165:in `insert'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/query_cache.rb:22:in `insert'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/persistence.rb:375:in `_insert_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/persistence.rb:932:in `_create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/counter_cache.rb:166:in `_create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/locking/optimistic.rb:70:in `_create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/attribute_methods/dirty.rb:211:in `_create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/callbacks.rb:331:in `block in _create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:101:in `run_callbacks'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:825:in `_run_create_callbacks'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/callbacks.rb:331:in `_create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/timestamp.rb:110:in `_create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/persistence.rb:905:in `create_or_update'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/callbacks.rb:327:in `block in create_or_update'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:101:in `run_callbacks'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:825:in `_run_save_callbacks'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/callbacks.rb:327:in `create_or_update'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/timestamp.rb:128:in `create_or_update'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/persistence.rb:503:in `save!'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/validations.rb:53:in `save!'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/transactions.rb:318:in `block in save!'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/transactions.rb:375:in `block in with_transaction_returning_status'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:280:in `block in transaction'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/transaction.rb:280:in `block in within_new_transaction'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/concurrency/load_interlock_aware_monitor.rb:26:in `block (2 levels) in synchronize'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/concurrency/load_interlock_aware_monitor.rb:25:in `handle_interrupt'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/concurrency/load_interlock_aware_monitor.rb:25:in `block in synchronize'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/concurrency/load_interlock_aware_monitor.rb:21:in `handle_interrupt'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/concurrency/load_interlock_aware_monitor.rb:21:in `synchronize'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/transaction.rb:278:in `within_new_transaction'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:280:in `transaction'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/transactions.rb:212:in `transaction'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/transactions.rb:366:in `with_transaction_returning_status'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/transactions.rb:318:in `save!'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/suppressor.rb:48:in `save!'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/persistence.rb:55:in `create!'
    /Users/jack/dev/tod/test/tod/time_of_day_serializable_attribute_test.rb:59:in `block (3 levels) in <top (required)>'

  7) Error:
TimeOfDay with ActiveRecord Serializable Attribute::.load#test_0002_loads set Time:
ArgumentError: wrong number of arguments (given 2, expected 1)
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/arel-9.0.0/lib/arel/visitors/to_sql.rb:73:in `compile'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:25:in `to_sql_and_binds'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:165:in `insert'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/query_cache.rb:22:in `insert'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/persistence.rb:375:in `_insert_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/persistence.rb:932:in `_create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/counter_cache.rb:166:in `_create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/locking/optimistic.rb:70:in `_create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/attribute_methods/dirty.rb:211:in `_create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/callbacks.rb:331:in `block in _create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:101:in `run_callbacks'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:825:in `_run_create_callbacks'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/callbacks.rb:331:in `_create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/timestamp.rb:110:in `_create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/persistence.rb:905:in `create_or_update'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/callbacks.rb:327:in `block in create_or_update'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:101:in `run_callbacks'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:825:in `_run_save_callbacks'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/callbacks.rb:327:in `create_or_update'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/timestamp.rb:128:in `create_or_update'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/persistence.rb:503:in `save!'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/validations.rb:53:in `save!'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/transactions.rb:318:in `block in save!'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/transactions.rb:375:in `block in with_transaction_returning_status'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:280:in `block in transaction'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/transaction.rb:280:in `block in within_new_transaction'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/concurrency/load_interlock_aware_monitor.rb:26:in `block (2 levels) in synchronize'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/concurrency/load_interlock_aware_monitor.rb:25:in `handle_interrupt'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/concurrency/load_interlock_aware_monitor.rb:25:in `block in synchronize'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/concurrency/load_interlock_aware_monitor.rb:21:in `handle_interrupt'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/concurrency/load_interlock_aware_monitor.rb:21:in `synchronize'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/transaction.rb:278:in `within_new_transaction'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:280:in `transaction'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/transactions.rb:212:in `transaction'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/transactions.rb:366:in `with_transaction_returning_status'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/transactions.rb:318:in `save!'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/suppressor.rb:48:in `save!'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/persistence.rb:55:in `create!'
    /Users/jack/dev/tod/test/tod/time_of_day_serializable_attribute_test.rb:46:in `block (3 levels) in <top (required)>'

  8) Error:
TimeOfDay with ActiveRecord Serializable Attribute::Order.where#test_0001_handles TimeOfDay as a parameter:
ArgumentError: wrong number of arguments (given 2, expected 1)
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/arel-9.0.0/lib/arel/visitors/to_sql.rb:73:in `compile'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:25:in `to_sql_and_binds'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:165:in `insert'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/query_cache.rb:22:in `insert'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/persistence.rb:375:in `_insert_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/persistence.rb:932:in `_create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/counter_cache.rb:166:in `_create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/locking/optimistic.rb:70:in `_create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/attribute_methods/dirty.rb:211:in `_create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/callbacks.rb:331:in `block in _create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:101:in `run_callbacks'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:825:in `_run_create_callbacks'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/callbacks.rb:331:in `_create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/timestamp.rb:110:in `_create_record'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/persistence.rb:905:in `create_or_update'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/callbacks.rb:327:in `block in create_or_update'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:101:in `run_callbacks'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:825:in `_run_save_callbacks'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/callbacks.rb:327:in `create_or_update'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/timestamp.rb:128:in `create_or_update'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/persistence.rb:503:in `save!'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/validations.rb:53:in `save!'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/transactions.rb:318:in `block in save!'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/transactions.rb:375:in `block in with_transaction_returning_status'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:280:in `block in transaction'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/transaction.rb:280:in `block in within_new_transaction'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/concurrency/load_interlock_aware_monitor.rb:26:in `block (2 levels) in synchronize'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/concurrency/load_interlock_aware_monitor.rb:25:in `handle_interrupt'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/concurrency/load_interlock_aware_monitor.rb:25:in `block in synchronize'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/concurrency/load_interlock_aware_monitor.rb:21:in `handle_interrupt'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/concurrency/load_interlock_aware_monitor.rb:21:in `synchronize'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/transaction.rb:278:in `within_new_transaction'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:280:in `transaction'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/transactions.rb:212:in `transaction'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/transactions.rb:366:in `with_transaction_returning_status'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/transactions.rb:318:in `save!'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/suppressor.rb:48:in `save!'
    /Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/persistence.rb:55:in `create!'
    /Users/jack/dev/tod/test/tod/time_of_day_serializable_attribute_test.rb:68:in `block (3 levels) in <top (required)>'

139 runs, 262 assertions, 0 failures, 8 errors, 0 skips
rake aborted!
Command failed with status (1): [ruby -w -I"lib:lib:test" -I"/Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/rake-12.3.2/lib" "/Users/jack/.asdf/installs/ruby/2.6.5/lib/ruby/gems/2.6.0/gems/rake-12.3.2/lib/rake/rake_test_loader.rb" "test/tod/conversion_test.rb" "test/tod/date_test.rb" "test/tod/shift_test.rb" "test/tod/time_of_day_serializable_attribute_test.rb" "test/tod/time_of_day_test.rb" "test/tod/time_of_day_time_zone_with_active_support_test.rb" "test/tod/time_test.rb" ]
/Users/jack/.asdf/installs/ruby/2.6.5/bin/bundle:23:in `load'
/Users/jack/.asdf/installs/ruby/2.6.5/bin/bundle:23:in `<main>'
Tasks: TOP => default => test
(See full trace by running task with --trace)

License missing from gemspec

RubyGems.org doesn't report a license for your gem. This is because it is not specified in the gemspec of your last release.

via e.g.

spec.license = 'MIT'
# or
spec.licenses = ['MIT', 'GPL-2']

Including a license in your gemspec is an easy way for rubygems.org and other tools to check how your gem is licensed. As you can imagine, scanning your repository for a LICENSE file or parsing the README, and then attempting to identify the license or licenses is much more difficult and more error prone. So, even for projects that already specify a license, including a license in your gemspec is a good practice. See, for example, how rubygems.org uses the gemspec to display the rails gem license.

There is even a License Finder gem to help companies/individuals ensure all gems they use meet their licensing needs. This tool depends on license information being available in the gemspec. This is an important enough issue that even Bundler now generates gems with a default 'MIT' license.

I hope you'll consider specifying a license in your gemspec. If not, please just close the issue with a nice message. In either case, I'll follow up. Thanks for your time!

Appendix:

If you need help choosing a license (sorry, I haven't checked your readme or looked for a license file), GitHub has created a license picker tool. Code without a license specified defaults to 'All rights reserved'-- denying others all rights to use of the code.
Here's a list of the license names I've found and their frequencies

p.s. In case you're wondering how I found you and why I made this issue, it's because I'm collecting stats on gems (I was originally looking for download data) and decided to collect license metadata,too, and make issues for gemspecs not specifying a license as a public service :). See the previous link or my blog post about this project for more information.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.