【问题标题】:get next/previous month from a Time object从 Time 对象获取下/上个月
【发布时间】:2011-02-11 14:18:28
【问题描述】:

我有一个 Time 对象,想查找下/上个月。由于每个月的天数不同,因此添加减去天数不起作用。

time = Time.parse('21-12-2008 10:51 UTC')
next_month = time + 31 * 24 * 60 * 60

增加月份也会下降,因为必须处理滚动

time = Time.parse('21-12-2008 10:51 UTC')
next_month = Time.utc(time.year, time.month+1)

time = Time.parse('01-12-2008 10:51 UTC')
previous_month = Time.utc(time.year, time.month-1)

我发现唯一有用的是

time = Time.parse('21-12-2008 10:51 UTC')
d = Date.new(time.year, time.month, time.day)
d >>= 1
next_month = Time.utc(d.year, d.month, d.day, time.hour, time.min, time.sec, time.usec)

有没有我没有看到的更优雅的方法? 你会怎么做?

【问题讨论】:

    标签: ruby


    【解决方案1】:

    Ruby on Rails

    注意:这仅适用于 Rails(感谢 Steve!)但我将其保留在这里以防其他人使用 Rails 并希望使用这些更直观的方法。

    超级简单 - 谢谢 Ruby on Rails!

    Time.now + 1.month
    
    Time.now - 1.month  
    

    或者,如果它与当前时间(仅限 Rails 3+)有关,则为另一个选项。

    1.month.from_now
    
    1.month.ago
    

    【讨论】:

    • #month 方法是 Rails 扩展。它还假设一个月有 30 天,因此会在 31 日返回错误答案。
    • 公平竞争,我使用的是脚本/控制台 - 我想我太 ROR'd 为我自己好。
    • @Steve 在进行日期算术时不假设 30 天。它实际上非常神奇。试试吧! (参见 active_support 中的 ActiveSupport::Duration、Date#plus_with_duration 和 integer/time.rb)。例如>> Time.utc(2010, 2, 1) + 1.months。 => Mon Mar 01 00:00:00 UTC 2010 >> Time.utc(2010, 3, 1) + 1.months => Thu Apr 01 00:00:00 UTC 2010 >> Time.utc(2010, 4, 1) + 1.months => Sat May 01 00:00:00 UTC 2010 或者举个极端的例子:>> Time.utc(2010, 1, 31) + 1.months => Sun Feb 28 00:00:00 UTC 2010
    • >> Time.utc(2010, 1, 31) + 1.months => 2010 年 2 月 28 日星期日 00:00:00 UTC >> Time.utc(2008, 1, 31) + 1 .months => 2008 年 2 月 29 日星期五 00:00:00 UTC
    • @John:这是新的,不是吗?你知道哪个版本的 Rails 添加了这个吗?
    【解决方案2】:

    我个人更喜欢使用:

    Time.now.beginning_of_month - 1.day      # previous month
    Time.now.end_of_month + 1.day            # next month
    

    它始终有效,并且与一个月中的天数无关。

    查找更多信息in this API doc

    【讨论】:

    • 迄今为止的最佳答案
    • 这对我来说很奇怪,但如果你真正想要的是上个月的最后一天或下个月的第一天,它就会起作用。如果您只需要上一个或下一个,并且不太关心那个月的哪一天,那么有更容易和更简单的方法来获取它。
    【解决方案3】:

    您可以使用标准类 DateTime

    require 'date'
    
    dt = Time.new().to_datetime
    => #<DateTime: 2010-04-23T22:31:39+03:00 (424277622199937/172800000,1/8,2299161)>
    
    dt2 = dt >> 1
    => #<DateTime: 2010-05-23T22:31:39+03:00 (424282806199937/172800000,1/8,2299161)>
    
    t = dt2.to_time
    => 2010-05-23 22:31:39 +0200
    

    【讨论】:

      【解决方案4】:

      Time 没有内置方法可以在 Ruby 中执行您想要的操作。我建议您在模块中编写方法来完成这项工作,并扩展 Time 类以使其在其余代码中的使用变得简单。

      您可以使用 DateTime,但方法(&lt;&lt;&gt;&gt;)的命名方式不会让从未使用过它们的人清楚地知道它们的用途。

      【讨论】:

        【解决方案5】:

        下面是有效的

        上个月:

        Time.now.months_since(-1)
        

        下个月:

        Time.now.months_since(1)
        

        【讨论】:

        • 未定义方法`months_since'
        • 你的代码环境是什么,ruby 版本?这是我的代码执行结果irb(main):053:0&gt; Time.now.months_since(-1) =&gt; 2013-10-19 08:40:04 +0800
        • $ ruby​​ -v ruby​​ 1.9.3p327 (2012-11-10 修订版 37606) [x86_64-darwin12.2.1] $ irb >> Time.now.months_since(-1) NoMethodError: undefined method ` 2013-11-19 02:09:23 +0100:Time
        • months_since 是activesupports的方法,你可以在你的cli中执行ri months_since
        【解决方案6】:

        如果你不想加载和依赖额外的库,你可以使用类似的东西:

        module MonthRotator
          def current_month
            self.month
          end
        
          def month_away
            new_month, new_year = current_month == 12 ? [1, year+1] : [(current_month + 1), year]
            Time.local(new_year, new_month, day, hour, sec)
          end
        
          def month_ago
            new_month, new_year = current_month == 1 ? [12, year-1] : [(current_month - 1), year]
            Time.local(new_year, new_month, day, hour, sec)
          end
        end
        
        class Time
          include MonthRotator
        end
        
        
        require 'minitest/autorun'
        
        class MonthRotatorTest < MiniTest::Unit::TestCase
          describe "A month rotator Time extension" do
            it 'should return a next month' do
              next_month_date = Time.local(2010, 12).month_away
              assert_equal next_month_date.month, 1
              assert_equal next_month_date.year, 2011
            end
        
            it 'should return previous month' do
              previous_month_date = Time.local(2011, 1).month_ago
              assert_equal previous_month_date.month, 12
              assert_equal previous_month_date.year, 2010
            end
          end
        end
        

        【讨论】:

        • 这不考虑年份滚动。
        • 感谢您的提醒!相应地更新了我的答案。
        • 感谢您提供不依赖于 active_support / rails 的答案。
        【解决方案7】:

        我只想添加我的普通 ruby​​ 解决方案以确保完整性 将 strftime 中的格式替换为所需的输出

        DateTime.now.prev_month.strftime("%Y-%m-%d")
        DateTime.now.next_month.strftime("%Y-%m-%d")
        

        【讨论】:

          【解决方案8】:

          您可以尝试转换为日期时间。

          Time 为您提供当前日期,DateTime 允许您进行操作。

          看看这个:

          irb(main):041:0> Time.new.strftime("%d/%m/%Y")
                  => "21/05/2015"
          
          irb(main):040:0> Time.new.to_datetime.prev_month.strftime("%d/%m/%Y")
              => "21/04/2015"
          

          【讨论】:

            【解决方案9】:

            这是一个没有 RoR 的普通 ruby​​ 的解决方案,适用于旧的 ruby​​ 版本。

            t=Time.local(2000,"jan",1,20,15,1,0);
            curmon=t.mon;
            prevmon=(Time.local(t.year,t.mon,1,0,0,0,0)-1).mon ;
            puts "#{curmon} #{prevmon}"
            

            【讨论】:

              【解决方案10】:

              您可以通过此代码获取上个月的信息

              require 'time'
              time = Time.parse('2021-09-29 12:31 UTC')
              time.to_date.prev_month.strftime("%b %Y")
              

              【讨论】:

                【解决方案11】:

                一些解决方案假定使用导轨。但是,在纯红宝石中,您可以执行以下操作

                require 'date'    
                
                d = Date.now
                last_month = d<<1
                
                last_month.strftime("%Y-%m-%d")
                

                【讨论】:

                  【解决方案12】:

                  我在此示例中使用 ActiveSupport::TimeZone,但如果您使用 Rails 或 ActiveSupport,它可能会派上用场。

                  如果你想要上个月,你可以减去 1 个月

                  time = Time.zone.parse('21-12-2008 10:51 UTC')
                  time.ago(1.month)
                  

                  【讨论】:

                    【解决方案13】:
                    $ irb
                    irb(main):001:0> time = Time.now
                    => 2016-11-21 10:16:31 -0800
                    irb(main):002:0> year = time.year
                    => 2016
                    irb(main):003:0> month = time.month
                    => 11
                    irb(main):004:0> last_month = month - 1
                    => 10
                    irb(main):005:0> puts time
                    2016-11-21 10:16:31 -0800
                    => nil
                    irb(main):006:0> puts year
                    2016
                    => nil
                    irb(main):007:0> puts month
                    11
                    => nil
                    irb(main):008:0> puts last_month
                    10
                    => nil
                    

                    【讨论】:

                    • 不要在一月份尝试这个。
                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 2019-01-30
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2014-01-23
                    相关资源
                    最近更新 更多