-
Notifications
You must be signed in to change notification settings - Fork 17.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
time AddDate(0, -1, 0) does not work for March. #31145
Comments
I'm going to try to tackle this one. I think I might be able to fix it. I'm pretty sure I know what the problem is. |
I think the result is complying with the documentation:
So in your example: Mar-30-2019 - 01-00-0000 = Feb-30-2019, which, normalized, is Mar-02-2019. |
Oh ok, so it's supposed to do that. Is there a way to "unnormalize" it? Yea, I just got all my contrib all set up and was looking at this part of the code when I saw this message. |
It would seem to me that the proper solution would be the number of days in a month vs the day set after the month has been decremented and whatever is smaller is the day you pick. I'm not a date expert though. Anyways, thanks! |
Closing because this is working as documented and expected. |
But how this behavior is expected? Sub one month from 2019 March 30 results in March 02 This is definitely a bug. It must be Feb 28. duckduckgo.com as well as other various resources show Feb 28 |
@vysdel Yea, I agree but the masters that be seem to not think so. I was going to write my own date library to handle it the way I think is proper. Probably not the best solution but I need it to handle dates another way. I'll post here I guess when I finish my package. |
@vysdel Subtracting one month from March 30 gives you February 30. The date February 30 does not exist, so it is normalized, as documented for Subtracting one month from April 1 gives you March 1. That date does exist, so no normalization is required. What are you really trying to do? |
Please note: before you read this message, that Moment.js and Java do not exhibit this behavior. @iamoryanmoshe I think what he and I am saying is that you say it's documented but there is no explanation. The term normalization means means changing something into a standard. I've never heard of the standard you refer to where you go back one month and you get a date in the same month... Also if you go forward from January you end up in March. How is going from January to March in any sense of the word going forward 1 month... I've done quite a bit of google searching and I can't find any examples of this behavior. It looks like to me someone didn't want to implement the extremely complicated cases. I've been working on writing a package for it and I've already spent a lot of time on it. There are a lot of things to consider like leap year, yes this affects it because of Feb 29, and all the other cases. You ask "What are you really trying to do?" Did you look at the two examples that are provided? I hope this helps explain the situation and why we are frustrated with the responses that we've been given. Thanks so much for taking the time to read this. |
@happilymarrieddad - Thank you for voicing your concerns. I can assure you that a lot of thought has went into designing the time package and it's not just a matter of not wanting to "implement the extremely complicated cases." The term "normalization" is used in various contexts. In this context, it just means dates which overflow or underflow are just adjusted to their future or past dates accordingly. If there is a more apt term for this, please feel free to send a PR so that we can update our docs. There are various scenarios where such a behavior is helpful. For eg, I want to figure out the last day of any month. Normally, it won't be very straightforward because some of them are 30, some are 31, and then there are leap years, DST adjustments and so on. But if dates are normalized, then I can just add 1 month to the current date, and set the day to 0. This effectively goes back 1 day from the first day of the next month, giving you the last day of the current month. If d is your current date, then In effect, the Go time package treats dates like a number line, where adding/subtracting an amount flows over to the next month, rather than wrapping around or throwing an error. |
@happilymarrieddad I see the examples but I don't know what you are trying to do. What does "I'm trying to go back 1 month from March" mean exactly? What precisely do you mean by "back 1 month from March 30"? To me that does not have a precise meaning. But since we wanted to provide an |
@agnivade No worries. I understand what you are saying. @ianlancetaylor You keep saying "What is it that you want" despite me giving specific examples. I'll just write my own package and use that. Thanks everyone! |
@happilymarrieddad Ian is asking what you want because you haven't described the behavior you're looking for. Yes, you've given an example, but it's not possible for us to extrapolate from the example to a precise definition. I've read this thread several times now and I also don't know what your preferred meaning of "subtract a month" is. To put it another way, you'd need to describe why subtracting a month from March 30 should give February 28, just like Agniva and Ian have explained why AddDate behaves as it does. (And what should subtracting a month from March 31 give? What about March 28 and March 29?) |
@cespare I would expect no more than 1 month to be subtracted. Libraries like Momentjs and the Java language handle it the same. With your example of using March then in all those cases you would get February 28 if you were to subtract unless it was a leap year and then you would get February 29. It doesn't make sense to me to transition 2 months or not transition a month just because the days don't to line up. The problem comes in if you are displaying the month for a customer that something happened. Then you add a month from say January at the end of the month and all of a sudden you are showing March. That would be very confusing. It's fine though. I feel like we just disagree. I also might have a unique use case. I will just finish up this package. I guess another attempt at describing my meaning of "subtract a month" would be if you pass in (0, -1, 0) and you display the month before and after it's confusing if you get 3 and 3 or 1 and 3 (in the case of adding a month to the end of January). You passed in -1 (1). Why is it not changing the month or transitioning more than 1 month. You guys keep saying the expected behavior but I can't find any documentation where this is expected behavior that you are describing in any other library or language. In fact, it's the opposite. |
I can't believe it. Everywhere it's Feb 28 or Feb 29 but go, apparently has its own way.
This led to a rather unpleasant bug for us. Yes, I understand it's our fault, possibly a bad design and/or not enough tests. And yes, I've read the docs. But "AddDate normalizes its result" - I personally would never assume to receive Mar 02 from Mar 30 |
I'm sorry this seems so frustrating. The docs for It sounds like you both expect that adding or subtracting a month should always give a result in the adjacent month. The only way that could happen would be if we map February 30 and February 31 both to February 28 (or February 29 in a leap year). That would be possible, though I think different people might find that confusing. But let's consider the case of adding or subtracting a week, rather than a month. If we add seven days to February 23, we get February 30. Now we have to normalize that. With the rule I suggested above, that normalizes to February 28 (or 29). But I think many people would legitimately find that to be very confusing. The right answer for many people would be March 2 (or 1). So I don't know how to apply your implied normalization rule consistently for Again I'm sorry this was confusing and frustrating. But I hope that now that you do understand how the functions work, you can write code that uses them to do what you want. |
anyone curious this seems still a relevant issue, and not just specific to March. below I'm subtracting one month from July 31, and expecting June as a response. but apparently it got
this one has been raised since 2015: #13790 (comment) still the case now as of Aug 2019. maybe a SubstractDate is function is in order that falls back to a previous day or maybe throw an error to let me know some normalisation occurred? |
@mel3kings Please read my immediately preceding comment about adding or subtracting a week rather than a month. |
@ianlancetaylor understand that this is working as coded/documented but not as developers expected. I will just have to create a wrapper function that if subtracting one month returns to me the same month then i need to subtract again 1 day. |
Don't forget to subtract more than 1 day if it's March 29, 30, or 31. |
@ianlancetaylor In response to your example about modifying a date by a week versus a month: weeks/days are inherently different time units from months. If you're adding a week (7 days) then (from my point of view as a developer using a time library) I would expect that the time library can roll some of those days over into another month, just like I would do if looking at a physical calendar. On the flip side, if I'm adding a month, I would always expect there to be exactly one month moved forward, and the time library figures out what day should be placed into the new time from that. So February + 1 == March in all circumstances, and March - 1 == February in all circumstances, but the day value is changed to make sense based on all the complexities of time. This behavior is harder to put together because months have so many different "sizes" throughout the year and between years. Years also have different sizes in different years, but I would still expect that adding a year to a time would still give me the same month in the result, but the day can potentially change. (as a fun edge case of why this can be surprising/challenging to developers: I would assume that under the normalization rules of Go, if you add a year to January 31 23:59:59 and a leap second is added at some point between the starting and desired year, you might end up with February 1 in the desired year because Go would add that second and roll over to the next month instead of leaving it at January 31) Of course, you can't change the behavior of AddDate at this point due to the Go 1 compatibility requirements, but it would be extremely nice if another utility method (or methods, or a different type or something) were added that behaved more like the time libraries in other languages. (Java's Joda-inspired time library and Moment.js for Javascript have been brought up as good examples for creating behavior off of) |
How about a compromise and adding a function like |
By all means go ahead and open a proposal for a new function to add to the time package (https://golang.org/s/proposal). Please be sure to specify precisely how it should behave. Thanks. |
I ran into something similar to this today while adding a months to 2/29/2020--I get different results when adding months and it's causing problems calculating future dates in my app. I think this is related to this issue. Here is a playground: |
@thequailman This issue is closed. See all the discussion above, especially #31145 (comment). |
Understand that this is working as designed. However, worth looking how the excel formula "edate" works and also see https://www.timeanddate.com/date/dateadd.html. Both of which work how I would expect adding / subtracting months to work. The designed approach causes major issues with financial transactions as adding / subtracting months behaves differently than is "convention". |
@roneelnaidu this issue is closed. Unlike many projects, the Go project does not use GitHub Issues for general discussion or asking questions. GitHub Issues are used for tracking bugs and proposals only. For asking questions, see:
|
AddDateX returns the time more attention to months then AddDate. For example, AddDateX(0, 1, 0) applied to 2021-08-31 will returns 2021-09-30. If the desired month does not have this day, temporarily set the day as the maximum day of the desired month, and then process the param days. Fixes golang#31145
Change https://golang.org/cl/354889 mentions this issue: |
What version of Go are you using (
go version
)?Does this issue reproduce with the latest release?
Yes, I tested it on golang.org and my local machine
What operating system and processor architecture are you using (
go env
)?go env
OutputWhat did you do?
I'm trying to go back 1 month from March and I get a really weird result. It goes from March 30th to March 2nd. I'm assuming it's taking the number of days in February and subtracting that from March. It should take the number of days in March and subtract it.
https://play.golang.org/p/n2xvzVFnA4G
What did you expect to see?
It should have been a date in February at least. Momentjs, not I don't like javascript, sets it to February 28 which is a little weird but at least it's February.
What did you see instead?
It set it to March 2nd.
The text was updated successfully, but these errors were encountered: