7 ms·
My personal favorite: how long is a month? - An even 30 days = Easy to explain and do math with. Adding/subtracting 1 month can leave you in the same month or
by mr-wendel 6y ago
My personal favorite: how long is a month?
- An even 30 days = Easy to explain and do math with. Adding/subtracting 1 month can leave you in the same month or skip February entirely! Anniversary dates bounce around from month-to-month. Maps poorly to yearly calculations.
- However many days >this< month has = Harder to explain and do math with. Months without 31 days can get skipped when adding/subtracting 1 month. Recurring events get pushed away from the end of the month to cluster around the beginning of the month. It does result in a steady anniversary date for edge cases.
- Closest numeric day, but prev/next month (e.g. Jan 31 -> Feb 28): Easy and hard to explain and do math with. It's great for ensuring events only ever happen once per calendar month, but adds extra ambiguity (e.g. Jan 31 > Feb 28 > March 30? ... or March 28th?)
- 30.4... = Just no... except for the few times this is right and you need consistency to avoid unfairly comparing 28 days vs 31 days.
The consequence is some very surprising things the first time you see them. Some billing systems simply avoid doing work past the 28th of each month (either doing it a bit early or a bit late). Some just embrace the weirdness (whichever you flavor you pick) and you get used to the quirks (e.g. end-of-month lulls and start-of-month spikes).
- gergely 6y agoThese use cases are also "fun" if you add timezones as well into the mix!
- deckard1 6y agoshudder I can only imagine needing to toss in standard/daylight timezone switch in there. Billing code really is the pinnacle of developer pain. It mixes the arbitrariness of special case business and customer rules with the absolute horror of time and date math.
- pc86 6y agoNot only that but in T-SQL, `AT TIME ZONE` takes DST into account but not in the names. So something like: SELECT CreatedDate AT TIME ZONE 'UTC' AT TIME ZONE 'Eastern Standard Time' FROM MyTable would in fact return `CreatedDate` at Eastern Daylight Time if executed today.
- Falkon1313 6y agoEven more fun: You use the standard datetime libraries so you don't have to deal with all that complexity. Only, they automagically muck with the data, including irrelevant precision, such as time and timezone and daylight savings time for a calendar date. And for the sake of completeness, your application allows the customer to specify a default timezone (usually that of their headquarters or something) and individual users to specify their own (useful if they work remote or in another office or something). So now you have your database's timezone (and datetime handling), your server's timezone, your application's timezone (and datetime library handling), the customer's configured timezone, the user's configured timezone, their client's own timezone handling (which may be different if they're on a business trip in a different location), all screwing with the date of something that shouldn't even have a time, timezone, or daylight savings in the first place. That's an endless whack-a-mole of bugs. And trying to just use UTC everywhere possible only gets you so far.
- paulclinger 6y agoMaybe if the date is "close" to the end of the month (as in fewer than X days from the end of the month), then count in the days left, so Jan 31st (0 days left) becomes Feb 28th and then to Mar 31st and so on.
- sk5t 6y agoI'll unwaveringly go with whatever the hardass valid-date-enforcing mechanisms in java.time and PostgreSQL enforce. Add a month to the epoch time? Can't do it because that doesn't adequate indicate what day we think it is. "select '2021-01-30'::date + make_interval(months := 1)" says Feb 28? Done!
- throwawayboise 6y agoAbsolutely. Go with the date/time/interval functions that your database or language libraries provide. Do not try to reinvent this, you will get it wrong 10 times before you get it right.
- arnon 6y agoMy go-to for everything is WWPD - "What would Postgres do?"
- greggyb 6y agoThis still isn't a complete solution, or at the very least holds non-obvious implications. Specifically, date math is not commutative[0]. And, yes, Postgres or java.time might force any arbitrarily ordered expression to always evaluate the same, but this would be based on some special logic for canonicalizing arithmetic on date expressions. Do they do this? I don't know. Even if they could do this in a single expression, there are absolutely scenarios where two date-arithmetic operations may be performed in two separate places in code. If those operate on the same logical date value, then we will see the same commutativity issues. [0]: My sibling comment on this: https://news.ycombinator.com/item?id=26707864 https://news.ycombinator.com/item?id=26707864
- tzs 6y agoIf I ever get to redesign the billing system at work, I'm going to go with fixed day of the month billing. We currently do 30 day billing, and most customers are fine with that, but there are some who need their bill to not come the same week as their rent is due, or not come until after they receive their monthly pension payment, or something like that. Right now we have to have special handling for those people. For handling the different lengths between the months, I've considered a few approaches. 1. If re-bill is supposed to be the Nth of each month, that simply means N-1 days after the 1st. If that falls into another calendar month, fine. So if someone asks to be billed on the 30th of each month, they will be billed on Jan 30th for January, Mar 2nd for February, Mar 30 for March, and so on. I'd make sure the receipt explicitly says "Re-bill for January", "Re-bill for February", etc., so that bill the see on Mar 2nd hopefully will not confuse them, nor will seeing another bill later that month. 2. If re-bill is supposed to be on the 29th-31st, for February it takes place on the 28th (29th in leap years). For other months, if re-bill is supposed to be on 31st, it takes place on the 30th in April, June, September, and November. 3. If you initially order on day 1-21 of the month, your re-billing is the same day ever month. If you initially order on day 22+, your re-billing is on the same number of days before the end of the month as was your initial order. For example, someone who orders on May 30th would re-bill on the 30th in 31 day months, the 29th in 30 days months, and the 27th in February (28th in leap years). If you ask for a change in fixed billing date, you can do so either as Nth day of the month or Mth day from the end of the month, but N or M must be <= 28.
- arnon 6y ago> If I ever get to redesign the billing system at work, I'm going to go with fixed day of the month billing. This may have unintended consequences. Some examples we faced around this: 1. Stress on our APIs when 15,000 invoices are created in the span of a couple of minutes. We had to build more queueing mechanisms around this. Some services just can't handle more than 100 calls a second. 2. Getting rate limited by MasterCard 3. Some payment providers thinking we're brute-forcing them
- virtue3 6y agoAren't all of these handled by a queue and some reasonable sense of going through it slowly? (I can just say that on the internet, I know designing it that way is not exactly trivial).
- vidanay 6y agoThis is why I've never understood why payroll is commonly on the 15th and last days of the month. Why not 1st and 15th? Those dates never change.
- pc86 6y agoIn my experience 1/15 is no less common than 15/L or every two weeks.
- gjvnq 6y agoThe real solution is to use the ISO week calendar for everything.
- SeriousM 6y agoCould you elaborate your solution please?
- dehrmann 6y agoOne place I worked did recurring payments on the day of the month you first set up recurring billing. If the date is the 31st, it got moved to the 30th. Feb 28 was...problematic for billing load.
- faitswulff 6y agoI ran into issues coding an interest calculator for personal usage when I realized that the length of a year is variable. My unit tests were failing because all the finance examples never explained how they dealt with time changing out from under the formulas they were using, and all the computer knew was Unix time. I still don't exactly know how to handle this. If you start an account that compounds interest continuously, does the daily interest rate change if one year is longer than another?
- jkulubya 6y agoIn the kind of finance where you’re dealing with interest payments, you choose/agree on/accept a day count convention and run with it. https://www.investopedia.com/terms/d/daycount.asp https://www.investopedia.com/terms/d/daycount.asp
- icedchai 6y agoyes. I wrote a recurring billing system for a hosting provider, back in the early 2000's. I think I settled on min(day_of_original_order, num_of_days_in_month) for when an order would bill.
- mr-wendel 6y agoOh boy, those were the days. I imagine you're well aware of the DreamHost debacle around their own home-grown billing engine! Looking back I'm amazed at how easily companies got off the ground by hiring eager (but exceptionally green) young web devs to write sensitive code for ordering/sign-up, recurring billing, and more.
- icedchai 6y agoWell, it was my own company with 2 other folks. There was no reasonable alternative in 2001, really.
- bigiain 6y ago> My personal favorite: how long is a month? I like "730.5 hours" (usually rounded to just 730). It's a good fit for services billed by the hour or minute. I use it regularly to answer "how much per month" for AWS stuff. But yeah - it fits in there with your "Just no..." category.
- greggyb 6y agoDon't forget that calendar math is not commutative! Jan 27 + 5 days = Feb 1 Feb 1 + 1 month = Mar 1 Jan 27 + 1 month = Feb 27 Feb 27 + 5 days = Mar 4 (or Mar 3 in a leap year!) Therefore... Jan 27 + 5 days + 1 month = Mar 1 Jan 27 + 1 month + 5 days = Mar 1 AND Jan 27 + 5 days + 1 month = Mar 4 (or 3) Jan 27 + 1 month + 5 days = Mar 4 (or 3) Before someone claims that this is just a February problem, please first consider that you can create these scenarios with any set of months that have differing lengths. Then, please consider that your argument that ignoring February is not a good way to approach any calendaring library....
- arnon 6y agoAll too familiar. Luckily, I have built "date time" functions for a relational DBMS before! I had specifically worked on functions like EXTRACT to pull out different parts like ISO Week numbers, as well as DATEADD and DATEDIFF functions which do date math. It is exceptionally unpleasant!
- dirkt 6y ago> My personal favorite: how long is a month? One solution is to distinguish between a "datetime" and a "duration". You cannot convert a duration to days (or really anything). You can add or subtract a duration from a datetime, and you can convert the difference between two concrete datetimes into days. And that's actually how some libraries already implement it. So the answer to "how long is a month" is "it depends, and I can only say if you apply this duration to a concrete time".
- NickNameNick 6y ago30.4167 days in a month, per the interest calculation in some software I implemented for a bank
- kingdomcome50 6y agoWhat about just mapping it? That is, simply choose the smallest increment necessary for your system and _enumerate_ every value within a window as your source of truth. There is less than a million hours in 100 years. Is this system going to be around in a hundred years? Probably not. It might make some of the math/algorithms a bit painful but I think you could guarantee correctness with the above approach.