• Subtract n months from datetime

    From Paulo da Silva@21:1/5 to All on Tue Jun 21 05:29:52 2022
    Hi!

    I implemented a part of a script to subtract n months from datetime.
    Basically I subtracted n%12 from year and n//12 from the month adding 12
    months when it goes<=0. Then used try when converting to datetime again.
    So, if the day is for example 31 for a 30 days month it raises a
    ValuError exception. Then I subtract 1 to day and repeat.

    The code seems too naive and very very complicated!
    What is the best way to achieve this? Any existent module?

    At the very end, what I want is to subtract nx where x can be y, m, w, d
    for respectively years, months, weeks or days.

    I feel I am missing something here ...

    Thanks.
    Paulo

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Paul Bryan@21:1/5 to Paulo da Silva on Mon Jun 20 21:44:48 2022
    Here's how my code does it:


    import calendar

    def add_months(value: date, n: int):
      """Return a date value with n months added (or subtracted if
    negative)."""
      year = value.year + (value.month - 1 + n) // 12
      month = (value.month - 1 + n) % 12 + 1
      day = min(value.day, calendar.monthrange(year, month)[1])
      return date(year, month, day)

    Paul

    On Tue, 2022-06-21 at 05:29 +0100, Paulo da Silva wrote:
    Hi!

    I implemented a part of a script to subtract n months from datetime. Basically I subtracted n%12 from year and n//12 from the month adding
    12
    months when it goes<=0. Then used try when converting to datetime
    again.
    So, if the day is for example 31 for a 30 days month it raises a
    ValuError exception. Then I subtract 1 to day and repeat.

    The code seems too naive and very very complicated!
    What is the best way to achieve this? Any existent module?

    At the very end, what I want is to subtract nx where x can be y, m,
    w, d
    for respectively years, months, weeks or days.

    I feel I am missing something here ...

    Thanks.
    Paulo


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Paul Rubin@21:1/5 to Paulo da Silva on Mon Jun 20 21:46:02 2022
    Paulo da Silva <p_d_a_s_i_l_v_a_ns@nonetnoaddress.pt> writes:
    I implemented a part of a script to subtract n months from datetime.

    Since months don't always have the same lengths, maybe you want to
    subtract some number of days, like 90 days instead of 3 months? Do that
    by making a timedelta and subtracting it from the datetime. Otherwise,
    as you say, how are you supposed to subtract 1 month from July 31,
    since there is no June 31?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Richard Damon@21:1/5 to Paulo da Silva on Tue Jun 21 07:59:59 2022
    On 6/21/22 12:29 AM, Paulo da Silva wrote:
    Hi!

    I implemented a part of a script to subtract n months from datetime. Basically I subtracted n%12 from year and n//12 from the month adding
    12 months when it goes<=0. Then used try when converting to datetime
    again. So, if the day is for example 31 for a 30 days month it raises
    a ValuError exception. Then I subtract 1 to day and repeat.

    The code seems too naive and very very complicated!
    What is the best way to achieve this? Any existent module?

    At the very end, what I want is to subtract nx where x can be y, m, w,
    d for respectively years, months, weeks or days.

    I feel I am missing something here ...

    Thanks.
    Paulo

    The biggest issue with "subtracting months" is getting the right
    definition of what you mean by that, especially in the corner cases,
    once that is established, programming it is fairly easy.

    The problem is that a month isn't a fixed unit of time, but is a period anywhere from 28 to 31 days. (you get the same problem for years, but
    the difference is more special case, the presence or absent of Feb 29th.)

    The normal definition of this operation has the strange property that if
    you subtract a month, then add a month, you sometimes don't get back to
    the same day as you started with. Also subtracting one month, and then subtracting another month might get you a different day than subtracting
    2 months at once (Think of Mar 31st).

    In short, this sort of date operation IS hard, and application specific,
    so while there may be pre-built modules that have this operation, you
    need to see if it uses a compatible definition of what you want.

    One alternative, which breaks other expectations, is to think of a month
    as 30 or 30.5 (so 2 months are 61 days) days, and add that. It says that
    often a month later than a given day isn't the same day of the month,
    but does make some operations less surprising. (This is hard to do to a
    date expressed as year-month-day, but trivial in some other formats like
    a timestamp.)

    --
    Richard Damon

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Dan Stromberg@21:1/5 to pbryan@anode.ca on Tue Jun 21 07:06:15 2022
    On Mon, Jun 20, 2022 at 9:45 PM Paul Bryan <pbryan@anode.ca> wrote:

    Here's how my code does it:


    import calendar

    def add_months(value: date, n: int):
    """Return a date value with n months added (or subtracted if
    negative)."""
    year = value.year + (value.month - 1 + n) // 12
    month = (value.month - 1 + n) % 12 + 1
    day = min(value.day, calendar.monthrange(year, month)[1])
    return date(year, month, day)


    This looks interesting.

    You also could add or subtract the average number of seconds in a month: 2629743.75

    This has the strange property that the time of day, or even calendar day,
    could change. However, it is round-trippable.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Paulo da Silva@21:1/5 to All on Tue Jun 21 17:02:24 2022
    Às 05:44 de 21/06/22, Paul Bryan escreveu:
    Here's how my code does it:


    import calendar

    def add_months(value: date, n: int):
      """Return a date value with n months added (or subtracted if negative)."""
      year = value.year + (value.month - 1 + n) // 12
      month = (value.month - 1 + n) % 12 + 1
      day = min(value.day, calendar.monthrange(year, month)[1])
      return date(year, month, day)

    Paul
    I have a datetime, not a date.
    Anyway, the use of calendar.monthrange simplifies the task a lot.

    Assuming dtnow has the current datetime and dtn the number of months to
    be subtracted, here is my solution (the code was not cleaned yet - just
    a test):
    dtnow_t=list(dtnow.timetuple()[:6]+(dtnow.microsecond,))
    y=dtnow_t[0] # y,m,d,*_=dtnow_t seems slower
    m=dtnow_t[1]
    d=dtnow_t[2]
    dy,dm=divmod(dtn,12)
    y-=dy
    m-=dm
    if m<1:
    m+=12
    y-=1
    daysinmonth=calendar.monthrange(y,m)[1]
    d=min(d,daysinmonth)
    dtnow_t[0]=y
    dtnow_t[1]=m
    dtnow_t[2]=d
    bt=datetime.datetime(*dtnow_t)

    Any comments are welcome.

    Thank you.
    Paulo



    On Tue, 2022-06-21 at 05:29 +0100, Paulo da Silva wrote:
    Hi!

    I implemented a part of a script to subtract n months from datetime.
    Basically I subtracted n%12 from year and n//12 from the month adding
    12
    months when it goes<=0. Then used try when converting to datetime
    again.
    So, if the day is for example 31 for a 30 days month it raises a
    ValuError exception. Then I subtract 1 to day and repeat.

    The code seems too naive and very very complicated!
    What is the best way to achieve this? Any existent module?

    At the very end, what I want is to subtract nx where x can be y, m,
    w, d
    for respectively years, months, weeks or days.

    I feel I am missing something here ...

    Thanks.
    Paulo



    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Paul Rubin@21:1/5 to Paulo da Silva on Tue Jun 21 13:49:51 2022
    Paulo da Silva <p_d_a_s_i_l_v_a_ns@nonetnoaddress.pt> writes:
    Any comments are welcome.

    Richard Damon makes the astute comment that adding a month and then
    subtracting it again could fail to put you back where you started. That doesn't seem good.

    Question: why is it that you want to do this thing, of adding and
    subtracting months? Do you have an application that requires it? Does
    the application precisely specify what adding and subtracting months is supposed to mean? If it is for some business purpose, like figuring out expiration dates of something, you really have to get this clarified by
    the project owner, who might not have thought things through enough.
    You can't just rely on your own judgment to figure out what makes the
    most sense. Someone else's presumptions might be different and they can
    get bitten by the disconnect.

    You're much better off adding and subtracting days rather than months.
    Those at least are usually all the same length, and nobody is likely to
    care about leap seconds either way.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Cameron Simpson@21:1/5 to Paulo da Silva on Wed Jun 22 07:55:29 2022
    On 21Jun2022 17:02, Paulo da Silva <p_d_a_s_i_l_v_a_ns@nonetnoaddress.pt> wrote:
    I have a datetime, not a date.

    Then you need a date. I would break the datetime into a date and a time,
    then do the months stuff to the date, then compose a new datetime from
    the result.

    Anyway, the use of calendar.monthrange simplifies the task a lot.

    Hmm, yes it would.

    The important thing to remember about any solutions mentioned is that
    dates and datetimes have different semantics. Specificly, you can't add
    fixed elapsed times such as seconds to do "calendar like" arithmetic,
    which works in days etc because months have varying numbers of days, and
    days have varying numbers of seconds (not merely the odd leap second but
    also the horrors of timezones and summer/winter time shifts).

    So working with the calendar component (days upwards) is a meaningful
    thing. But working in, say, seconds with the _bjective_ of doing days or
    months is nearly pointless.

    Cheers,
    Cameron Simpson <cs@cskk.id.au>

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Paulo da Silva@21:1/5 to All on Wed Jun 22 17:59:00 2022
    Às 05:29 de 21/06/22, Paulo da Silva escreveu:

    As a general response to some comments ...

    Suppose we need to delete records from a database older than ...
    Today, it's usual to specify days. For example you have to keep some gov
    papers for 90 days. This seems to come from computers era. In our minds, however, we immediately think 90 days=3 months.
    For example, one may want to delete some files older than 9 months. It's
    far more intuitive than 270 days.
    When we talk about years it is still going. For example I need to keep
    my receipts for 5 years because IRS audits.
    Accepting this, it's intuitive, for example, that 3 months before July,
    31 is April, 30.
    The same happens for the years. 5 years before February, 29 is February, 28.

    Again, this is my opinion and that's the way I like it :-)
    Regards
    Paulo

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Stefan Ram@21:1/5 to MRAB on Wed Jun 22 18:00:10 2022
    MRAB <python@mrabarnett.plus.com> writes:
    It's 28 February, you need to keep it for 5 years, therefore you could
    reason that you can dispose of it on 28 February, 5 years hence.
    However, that happens to be a leap year.
    Should you still have it on 29 February?

    Here in Germany, the BGB contains regulations for such cases.

    For example BGB §188, 3:

    |If, in the case of a period determined by months, the day
    |decisive for its expiry is missing in the last month, the
    |period shall end with the expiry of the last day of that
    |month.

    . This does not address your case specifically, I know,
    there also is the more general subsection 2.

    I just want to say: there are rules and regulations, and the
    programmer of an application is supposed to learn and apply them
    according to the requirements specification contract with his
    customer. We here in the newsgroup do not know the details of
    this contract.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From MRAB@21:1/5 to Paulo da Silva on Wed Jun 22 18:48:52 2022
    On 2022-06-22 17:59, Paulo da Silva wrote:
    Às 05:29 de 21/06/22, Paulo da Silva escreveu:

    As a general response to some comments ...

    Suppose we need to delete records from a database older than ...
    Today, it's usual to specify days. For example you have to keep some gov papers for 90 days. This seems to come from computers era. In our minds, however, we immediately think 90 days=3 months.
    For example, one may want to delete some files older than 9 months. It's
    far more intuitive than 270 days.
    When we talk about years it is still going. For example I need to keep
    my receipts for 5 years because IRS audits.
    Accepting this, it's intuitive, for example, that 3 months before July,
    31 is April, 30.
    The same happens for the years. 5 years before February, 29 is February, 28.

    Again, this is my opinion and that's the way I like it :-)

    What makes sense depends on where you're looking from.

    It's 28 February, you need to keep it for 5 years, therefore you could
    reason that you can dispose of it on 28 February, 5 years hence.

    However, that happens to be a leap year.

    Should you still have it on 29 February?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Marco Sulla@21:1/5 to All on Wed Jun 22 20:47:37 2022
    The package arrow has a simple shift method for months, weeks etc

    https://arrow.readthedocs.io/en/latest/#replace-shift

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Barry Scott@21:1/5 to All on Wed Jun 22 20:25:52 2022
    On 22 Jun 2022, at 17:59, Paulo da Silva <p_d_a_s_i_l_v_a_ns@nonetnoaddress.pt> wrote:

    Às 05:29 de 21/06/22, Paulo da Silva escreveu:

    As a general response to some comments ...

    Suppose we need to delete records from a database older than ...
    Today, it's usual to specify days. For example you have to keep some gov papers for 90 days. This seems to come from computers era. In our minds, however, we immediately think 90 days=3 months.
    For example, one may want to delete some files older than 9 months. It's far more intuitive than 270 days.
    When we talk about years it is still going. For example I need to keep my receipts for 5 years because IRS audits.
    Accepting this, it's intuitive, for example, that 3 months before July, 31 is April, 30.
    The same happens for the years. 5 years before February, 29 is February, 28.

    The advantage of 30 days, 90 days etc is that a contract or law does not need to tell you
    how to deal with the problems of calendar months.

    As you say in peoples thoughts that 1 month or 3 months etc. But an accounts department
    will know how to to the number of days till they have to pay up.

    Barry



    Again, this is my opinion and that's the way I like it :-)
    Regards
    Paulo
    --
    https://mail.python.org/mailman/listinfo/python-list

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From MRAB@21:1/5 to Barry Scott on Wed Jun 22 21:52:02 2022
    On 2022-06-22 20:25, Barry Scott wrote:


    On 22 Jun 2022, at 17:59, Paulo da Silva <p_d_a_s_i_l_v_a_ns@nonetnoaddress.pt> wrote:

    Às 05:29 de 21/06/22, Paulo da Silva escreveu:

    As a general response to some comments ...

    Suppose we need to delete records from a database older than ...
    Today, it's usual to specify days. For example you have to keep some gov papers for 90 days. This seems to come from computers era. In our minds, however, we immediately think 90 days=3 months.
    For example, one may want to delete some files older than 9 months. It's far more intuitive than 270 days.
    When we talk about years it is still going. For example I need to keep my receipts for 5 years because IRS audits.
    Accepting this, it's intuitive, for example, that 3 months before July, 31 is April, 30.
    The same happens for the years. 5 years before February, 29 is February, 28.

    The advantage of 30 days, 90 days etc is that a contract or law does not need to tell you
    how to deal with the problems of calendar months.

    As you say in peoples thoughts that 1 month or 3 months etc. But an accounts department
    will know how to to the number of days till they have to pay up.

    OT, but in the UK, when the Gregorian Calendar was adopted, there were complaints.

    It's often believed that they were just being superstitious about
    "losing" 11 days, but the truth is that they were complaining that rent
    was paid by the month, but wages by the number of days worked.

    That month was a lot shorter, with far fewer working days, yet they were
    still expected to pay the same rent!

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Paulo da Silva@21:1/5 to All on Thu Jun 23 01:56:10 2022
    Às 20:25 de 22/06/22, Barry Scott escreveu:


    On 22 Jun 2022, at 17:59, Paulo da Silva <p_d_a_s_i_l_v_a_ns@nonetnoaddress.pt> wrote:

    Às 05:29 de 21/06/22, Paulo da Silva escreveu:

    As a general response to some comments ...

    Suppose we need to delete records from a database older than ...
    Today, it's usual to specify days. For example you have to keep some gov papers for 90 days. This seems to come from computers era. In our minds, however, we immediately think 90 days=3 months.
    For example, one may want to delete some files older than 9 months. It's far more intuitive than 270 days.
    When we talk about years it is still going. For example I need to keep my receipts for 5 years because IRS audits.
    Accepting this, it's intuitive, for example, that 3 months before July, 31 is April, 30.
    The same happens for the years. 5 years before February, 29 is February, 28.

    The advantage of 30 days, 90 days etc is that a contract or law does not need to tell you
    how to deal with the problems of calendar months.

    As you say in peoples thoughts that 1 month or 3 months etc. But an accounts department
    will know how to to the number of days till they have to pay up.

    Yes. But my point is to justify why I want months. And it depends on the application.
    Let's suppose a program for Joe User to clean something - files, for
    example. There are no rules except for the comfort of the user. He would
    prefer to be able to say 9 months back instead of 270 days. And by 9
    months, he expects to count down 9 months. Not 270 days.
    That's what happens with the script I am writing.

    Paulo

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Paulo da Silva@21:1/5 to All on Thu Jun 23 02:00:16 2022
    Às 19:47 de 22/06/22, Marco Sulla escreveu:
    The package arrow has a simple shift method for months, weeks etc

    https://arrow.readthedocs.io/en/latest/#replace-shift

    At first look it seems pretty good! I didn't know it.
    Thank you Marco.

    Paulo

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Peter J. Holzer@21:1/5 to Paulo da Silva on Mon Jun 27 12:45:03 2022
    On 2022-06-21 05:29:52 +0100, Paulo da Silva wrote:
    I implemented a part of a script to subtract n months from datetime. Basically I subtracted n%12 from year and n//12 from the month adding 12 months when it goes<=0. Then used try when converting to datetime again. So, if the day is for example 31 for a 30 days month it raises a ValuError exception. Then I subtract 1 to day and repeat.

    For a recent longish discussion of that matter see the threads starting
    at https://mail.python.org/pipermail/python-list/2022-April/905985.html
    and https://mail.python.org/pipermail/python-list/2022-April/906045.html
    (the latter also contains some prototype code).

    (I apologize for not pursuing that further at the time. I wanted to
    bolster that case with some real world applications, but I was a bit
    swamped with Real Work™ and didn't find anything suitable.)

    hp

    --
    _ | Peter J. Holzer | Story must make more sense than reality.
    |_|_) | |
    | | | hjp@hjp.at | -- Charles Stross, "Creative writing
    __/ | http://www.hjp.at/ | challenge!"

    -----BEGIN PGP SIGNATURE-----

    iQIzBAABCgAdFiEETtJbRjyPwVTYGJ5k8g5IURL+KF0FAmK5ihcACgkQ8g5IURL+ KF0dfg/9HBmaqRIdOV0Lx8l7N4anXfU1jcJtRk9FPDYQiqcoYDiG6FP6lUEurxap Gj7aRr9nlnrndm/zGhHaQ0qZ6ZFwZgUxtE+Hc1SRkkosWWJawzxNbHCsa4qpTo6x auLU1bueCrbJ5/Sc0zYimZ8hVQ3meMOpl+EM0w2OWunAbaqT9VNXrKznDz93ALfM bnC2JkRP8hdlPAH85ETqJRZXFfVOAGz9QOi6NzkXJGN6KCFSuI5CrsF1wIeEvgFd 6unIJ6Q5CdK8GYrXp3TowTvGXkt0mkRPVwG34WGmA9ho5Z5JMq+cFVs+vb/LzhB4 g79/z4AUr04A63XUYpeIrve5LuVFyeIVV5omPjrSFmwh9azzbD8TXNwc4unbG9Nu k4SyMc1STHSm793vgBhO5K0sbRCb9E1l/Aeqq40zDJ2y4ZKGBOXa6eE7EQP5ldt9 eg+NMB5x//VlVCei8VcvlmoK087luqYPivrqpIb54v+ppla5H4LoL7p1UTTer5oX ljmB5gdUl0qpPxMT5/n4GUz8A65RgmdVfOdjEkj68bSFT0X+GY65YAlarYVX3Cwx WRIxmC9OeTwVvBqxcwZSK5JE937Cpo9vNe5gTaA7ac8r5QTrMk0gpigh5ZDOz2qD fQLQTeleN9P2N2PvDFE41YIbAJls73TJe+V+0/v