Git Product home page Git Product logo

Comments (5)

craiglittle avatar craiglittle commented on June 7, 2024

Hey, @take! Glad to hear you're finding the gem useful.

You're correct that Biz#time calculations using day-based durations aren't currently supported. Unlike duration calculations using seconds, minutes, and hours, day-based calculations move into a realm of behavior where people start to have differing opinions about what the optimal behavior should be. Black and white begins to give way to a muddy grey.

After spending good deal of time reading through the issues of similar gems, I know this functionality is important to a subset of users, but it's often plagued by inconsistencies and weird corner cases in practice.

For example, given a standard configuration:

Biz.configure do |config|
  config.hours = {
    mon: {'09:00' => '17:00'},
    tue: {'09:00' => '17:00'},
    wed: {'09:00' => '17:00'},
    thu: {'09:00' => '17:00'},
    fri: {'09:00' => '17:00'},
  }

  config.time_zone = 'Etc/UTC'
end

Consider these examples and possible return values:

# Business day, before hours
Biz.time(1, :day).after(Time.utc(2015, 3, 9, 6))
# => 2015-03-09 09:00:00 UTC?
# *or*
# => 2015-03-10 09:00:00 UTC?

# Business day, during hours
Biz.time(1, :day).after(Time.utc(2015, 3, 9, 12))
# => 2015-03-10 09:00:00 UTC?
# *or*
# => 2015-03-10 12:00:00 UTC?

# Business day, after hours
Biz.time(1, :day).after(Time.utc(2015, 3, 9, 20))
# => 2015-03-10 09:00:00 UTC?
# *or*
# => 2015-03-11 09:00:00 UTC?

# Non-business day
Biz.time(1, :day).after(Time.utc(2015, 3, 8, 12))
# => 2015-03-09 09:00:00 UTC?
# *or*
# => 2015-03-10 09:00:00 UTC?

I currently have an implementation in the works that would work like the following for the above examples:

# Business day, before hours
Biz.time(1, :day).after(Time.utc(2015, 3, 9, 6)) # => 2015-03-10 09:00:00 UTC

# Business day, during hours
Biz.time(1, :day).after(Time.utc(2015, 3, 9, 12)) # => 2015-03-10 09:00:00 UTC

# Business day, after hours
Biz.time(1, :day).after(Time.utc(2015, 3, 9, 20)) # => 2015-03-10 09:00:00 UTC

# Non-business day
Biz.time(1, :day).after(Time.utc(2015, 3, 8, 12)) # => 2015-03-09 09:00:00 UTC

The main idea is to have the calculation operate exclusively on the day level before dropping down to the hours configuration to return the first time after the number of specified business days has elapsed. I don't know if it'll satisfy everyone, but it will at least be consistent and easy to reason about.

In the meantime, I've already merged a PR to remove 'day' duration support for the time being until the real implementation is available. I'd love to hear any comments you have about the proposed implementation.

I'll keep this issue open and marked as a feature request for now.

from biz.

take avatar take commented on June 7, 2024

@craiglittle

Thanks for the detailed informations!

Yea I realised that there are weird corner cases and I also found out some alternative gems has an API for it, and some doesn't.

For example the working_hours gem solves this by adding an API which advances to the next business time.

[9] pry(main)> time
=> 2015-03-09 17:01:00 UTC
[10] pry(main)> time + 1.working.days
=> Tue, 10 Mar 2015 17:01:00 UTC +00:00
[11] pry(main)> (time + 1.working.days).in_working_hours?
=> false
[12] pry(main)> WorkingHours.advance_to_working_time(time + 1.working.days)
=> Wed, 11 Mar 2015 09:00:00 UTC +00:00

but I think we can just include that operation inside the day calculation process, like you mentioned above.

I have an opinion about the current implementation you're working on, would be awesome if you read through it. Basically I agree with the The main idea is to have the calculation operate exclusively on the day level before dropping down to the hours configuration to return the first time after the number of specified business days has elapsed. part, but I think I have a different opinion about how the day exclusive calculation works.

So for the 4 examples you posted above,

# Business day, before hours
Biz.time(1, :day).after(Time.utc(2015, 3, 9, 6)) # => 2015-03-10 09:00:00 UTC

👍

# Business day, during hours
Biz.time(1, :day).after(Time.utc(2015, 3, 9, 12)) # => 2015-03-10 09:00:00 UTC

👎

# Business day, after hours
Biz.time(1, :day).after(Time.utc(2015, 3, 9, 20)) # => 2015-03-10 09:00:00 UTC

👎

# Non-business day
Biz.time(1, :day).after(Time.utc(2015, 3, 8, 12)) # => 2015-03-09 09:00:00 UTC

👎

I'm basically against the last 3 because I think adding 1 business day means the next business time after the 24hours including the next business time.

So I think the last 3 should be

# Business day, during hours
Biz.time(1, :day).after(Time.utc(2015, 3, 9, 12)) # => 2015-03-10 12:00:00 UTC

# Business day, after hours
Biz.time(1, :day).after(Time.utc(2015, 3, 9, 20)) # => 2015-03-11 09:00:00 UTC

# Non-business day
Biz.time(1, :day).after(Time.utc(2015, 3, 8, 12)) # => 2015-03-09 12:00:00 UTC

Of course I don't think this the next business time after the 24hours including the next business time definition will be the definition for everyone, it might have some deficit, and I think there's a high possibility I'll change my mind.

So please tell me what you think about this definition.

Thanks! :D

from biz.

craiglittle avatar craiglittle commented on June 7, 2024

@take Thanks for taking the time to write that up. I think it makes a lot of sense, and the underlying rule is simple and consistent. Let me see what I can do!

from biz.

take avatar take commented on June 7, 2024

@craiglittle Thanks! Please feel free to ping me anytime, I'll be happy to help you :)

from biz.

craiglittle avatar craiglittle commented on June 7, 2024

@take I just opened a PR (#17) for this feature. Let's move the discussion over there!

from biz.

Related Issues (20)

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.