Is there a recommendation or norm on how to add months to a date when there is carry? - date

ISO-8601 defines time intervals, for example P1M is one month.
However it seems that is does not mandate how to determine what day is one month from a given date.
I looked up the documentation of sqlite and in their implementation, given YYYY-MM-DD, adding one month is adding 1 to MM, and then normalizing (if MM is greater than 12, then increment years, then if DD is greater than the number of days of the resulting month, then carry to next month).
However this can produce inconsistencies:
2020-01-29 + P1M = 2020-02-29
2020-01-30 + P1M = 2020-03-01
2020-01-31 + P1M = 2020-03-02
2020-02-01 + P1M = 2020-03-01 ‽, note that this this is sooner than previously
Moreover, with this method, if I specify an interval of one month and one day, should I first add one month, then one day, or should I add first one day, then one month?
2020-01-30 + P3D + P1M = 2020-02-02 + P1M = 2020-03-02
2020-01-30 + P1M + P3D = 2020-03-01 + P3D = 2020-03-04, so later if we add months first
The question is: is there anywhere a canonical way to proceed when adding an interval to a date, when the interval specifies years or months, which are variable durations?

The actual implementation of the term month in interval form is left mostly unspecified in most, if not all, standards on purpose.
The loosely accepted definition is about 30 days. The integer rounding of 365.25 / 12.
The method used varies on several factors:
Simplicity and Convenience:
It is easier to remember common days.
My electric bill is due on the 11th of every month. This is problematic when day is greater than 28.
I get paid the last day of the month.
The meeting is on the second Tuesday of each month.
Ease of calculation:
Fixed Month definition.
30 day grace period for new purchases.
Astronomical:
Lunar Based:
My apologies for the poorly condensed descriptions.
Synodic: Based on phases of the Moon: 29.18 to about 29.93 days(formed the basis of our modern system)
Sidereal: Based on "fixed" star passing. 27.321 days
Tropical: Based on celestial bodies at the Spring(northern hemisphere) Equinox: 27.321 days
Anomalistic: based on angle off of the elliptic orbit: 27.554 days
Draconic: based on angle off of the elliptic plane: 27.212 days
An even solar month would be:
About 365.2422 / 12 ~= 30.43685 days
An even calendar month would be:
Non-leap years: 30.4166666667 days
Leap years: 30.5 days
Or in terms of Weeks:
Just over 4 Weeks.
This is not meant to be an exhaustive list. There are many more historic or esoteric definitions not included here.
If I have missed one in use today, please let me know.
The main take away is that no single definition fits all purposes.
Be consistent and transparent:
Pick one and stick with it.
Let everyone know.

Related

How does rust chrono Duration get number of years or months?

I'm quite new to Rust and chrono library.
I checked the https://docs.rs/chrono/0.4.19/chrono/struct.Duration.html#method.num_weeks, but there's no num_years() or num_months() API in chrono::Duration.
Is there any work around solution for this ?
chrono::Duration provides date and time duration in "ISO 8601 time duration with nanosecond precision", which implies it is representing the duration internally as a number of nanoseconds, and then providing convenience methods to convert into other duration units such as days, weeks, milliseconds, etc.
This is a little at odds with the actual ISO 8601 duration standard, which is a standard of representations and formats. The standard represents durations by the format P[n]Y[n]M[n]DT[n]H[n]M[n]S - which might give you what you want. But this is not what chronos::Duration was designed to provide.
The problem is that in order to represent a duration of months or years, more information than the number of nanoseconds is needed. Durations define the amount of intervening time in a time interval: the time between two points. The start or end time is important, because months or years are not standard durations. There are months of 28, 29 30, 31 days, and years of 365 and 366 days.
If you were to write your own algorithm to format durations in terms of years, months, days, hours, minutes, seconds, etc.. you would have to know the start date. In addition, time zone is important, because daylight savings needs to be taken into account. You would also have to make decisions about how to represent parts of months or years. For example the month of January has 31 days, and February 28, say. What would it mean to represent a duration of 1.75 months from January 1? Would that mean 31 days for January then 0.75 * 28 days in February?
Or you could represent the duration from a start date in a cascading unit format: e.g., 5 years, 4 months and 3 days, 2 hours, 12 minutes and 3 seconds from 1 Jan 1970 12:00Z. Just like the ISO 8601 standard.
So, its not an easy solution, and it all depends what your requirements are. I can understand why the developers of chronos:Duration left off providing num_months() and num_years()!
The crate chronoutil provides RelativeDuration which is "extending Chrono’s Duration to add months and years".
Example from docs.rs:
let one_day = RelativeDuration::days(1);
let one_month = RelativeDuration::months(1);
let delta = one_month + one_day;
let start = NaiveDate::from_ymd(2020, 1, 30);
assert_eq!(start + delta, NaiveDate::from_ymd(2020, 3, 1));

How to handle day of week adjustment

I am trying to resolve an issue involving comparing Year Over Year data results by day, and cant seem to quite figure it out.
I want to be able to accurately compare Year Over Year results by a specific day - Historically we have been calculating this as [Years Back] * 364 to obtain the number of days between the two dates, but as the yearly difference becomes greater than 7 it becomes incorrectly calculated.
I attached a picture here showing the anomaly where there is not a constant difference YoY and between two dates. 2015 is not a leap year.
Labor Day vs Memorial Day - YoY:
What is the best way to calculate any given dates past equivalent date? If I have two calendar tables, what do I match on? (I can get any of the following fields to match Date, Day of Week Number, Day of Month November, Week Number of Month, Week number of Calendar Year etc.)
For example, need to input the date, and have an output of past year equivalent
Like So:
Given Date | Past Year equivalent
20190902 | 20180903
20190902 | 20170904
20190902 | 20160905
20190902 | 20150907
20190902 | 20140901

Find and Rank Time Series MATLAB

I know there must be a simple way that I can learn to do this but I cannot imagine how to start. I am tasked with finding a top 10 matching daily wind power time series in a 30-day plus/minus window from the first day in the time series (Jan 1st) matching a single daily wind power time series and it is out of my level of experience in MATLAB. I have successfully done this matching a single time series of the current year with the exact calendar days from previous years, but I need a more robust searching method to find the best correlated time series in a +/- window of time. For example, I'm comparing a 120 day time series (without leap years) with 25 previous years during the same 120-day period (Jan-Apr). The end result will show me the top 10 time series with the years and Julian day or cumulative day listed and a correlation or RMSE value associated with it. My data looks like this arranged in a 365 (days) X 25 (years) array and I thank you very much for your help!
1182573 470528 1638232 2105034 1070466 478257 1096999
879997 715531 1111498 1004556 1894202 1372178 1707984
636173 937769 2119436 742710 1625931 1275567 1228515
967360 1103082 2218855 1643898 1822868 554769 1325642

How to convert month to other duration measurement types?

For some duration-related calculations I need to convert values measured in "months" to other formats, such as years, days, or hours.
For example, what is the proper way to measure a month in terms of days? is it 30 days? or 30.4375 days? (365.25 / 12) and which format would be useful in which cases?
If you have any information on the casual/business use cases for such conversions it would be helpful too.
Unfortunately, there's really no single generally valid answer to your question.
If this is for business use, first check whether there are any existing relevant standards or business practices that define what a "month" means in your business context. If yes, you should follow that definition as closely as possible, however silly or awkward it may seem.
For casual use, the simplest solution is probably to pick any widely use date manipulation library and do whatever it does. The default behavior may not be perfect, but it's probably at least close to a fairly sensible compromise of the many contradictory expectations that users of such a library may have.
OK, but what if you insist on rolling your own solution? In that case, the first choice you should make is how you want to represent date / time values. There are at least two common choices:
The first option is to store dates / times using a simple linear count of fixed time units from a given epoch, such as Julian days or Unix timestamps. This provides a simple and compact date/time representation, makes comparing timestamps and simple date/time arithmetic (like adding n seconds to a time value) easy, and ensures that any time value corresponds to a (more or less) unique and well defined point in time.
The downside, as you've noticed, is that arithmetic using "fuzzy" time units like months or years gets difficult: you can define a year as 365.25 days (or as 365.2425 days, to take into account that only 97 out of every 400 years are leap years in the Gregorian calendar) and a month as 1/12 years, but this will cause adding a year to a date-time value to also shift the time of day by (about) 6 hours, which may be unexpected.
This approach also doesn't let you easily represent "floating" time value, like times of day without a specified date and time zone. (You can sort of deal with floating time zones by doing your time math in UTC and just pretending that it's in your local time zone, but this can cause weird stuff to happen around DST changeovers.) Conversely, it can also cause difficulties if you need to represent imprecise date/time values, such as dates without a time component.
In particular, if you choose the "natural" representation, where imprecise datetimes are represented by their starting point, so that e.g. an unspecified time of day defaults to 00:00:00.0, then anything that causes the time part to be reduced by even a fraction of a second — like, say, shifting to a later time zone, or subtracting a fuzzy time unit that is not an integral number of days — will flip the date part to the previous day. For example, with this representation, subtracting one year (= 265.2425 days) from January 1, 2014 will yield a date in 2012 (specifically, December 31, 2012, 17:56:32)!
You can avoid some of these issues by representing imprecise date/time values by their midpoints instead, so that e.g. the date 2014 is treated as shorthand for June 2, 2014, 12:00:00. What you lose, with this representation, is the ability to build datetimes just by adding up components: with this representation, 2014 + 5 months + 3 days isn't anywhere near May 3, 2014.
Also, just when you think you've at least got simple non-fuzzy time arithmetic unambiguously sorted out, someone's going to tell you about leap seconds...
The alternative approach is to store datetime values in decomposed year / month / day / hour / minute / second / etc. format. With this presentation, time intervals are also naturally stored in a decomposed format: "one month + 17 days" is, in itself, a valid time interval in such a representation, and need not (and should not) be simplified further.
This has a few obvious advantages:
Fuzzy unit arithmetic is (conceptually) simple: to add one year to a date, just increment the year component by one.
Imprecise date/time values can be naturally represented: for a pure date value, the time-of-day components can simply be left undefined (= e.g. represented by negative values for the undefined components, or simply by having each datetime value store its precision).
You have precise control over when and if rollover occurs: adding a year to a date in 2014 will always yield a date in 2015.
You can also support floating time values, such as times of day without a specified date, or dates of year without a specified year. Floating time zones also become supportable.
That said, there are some disadvantages, too:
Implementing date arithmetic gets more complex, since you have to deal with non-trivial carry/borrow rules. (Quick! What's the date 10,000,000 seconds after May 3, 2014?)
You'll still have ambiguities with month arithmetic: what's the date one month after January 31? And does it depend on whether it's a leap year or not?
You can allow such a format to store "impossible" dates like "February 31", with an optional method to normalize them to, say, February 28 (or 29, for a leap year) later. This has the advantage of preserving (some) arithmetic consistency: it allows (January 31 + 1 month) + 1 month to equal March 31 as expected.
In some ways, though this merely postpones the problem: presumably, January 31 + 24 hours should fall on February 1, but what day and month should January 31 + 1 month + 24 hours fall on? The "obvious" choice would be March 1, but whatever you choose, there will be some sequence of arithmetic operations that will yield inconsistent results.

How to find out how many (if any) weekends occur in a given period

I have two numbers; dates in Unix Epoch format.
Say I have two Unix Epoch timestamps:
1349422200 = 05/10/2012 08:30
1350489600 = 17/10/2012 17:00
I am able to find out how many days are between the two date/times, no problem.
What I would like to be able to work out is how many of those days are Saturdays or Sundays (Non-Weekdays). I'm sure there has to be an easy way to go about this but I can't for the life of me figure it out without doing a load of for() loops.
Any help greatly appreciated.
Be more specific with your definition of weekend. Do you only want to include whole weekends (the entire 48 hour period of saturday and sunday)? Or does any range of time containing midnight on Friday night/Saturday morning count as a weekend? Do half weekends count as half, or do you round to the nearest whole number?
In any case:
Figure out what comprises a weekend
Find the first and last weekends in your range of time
Find the time difference between them, in days, and divide by 7
Edit: you're seeking the exact amount of weekend time, with no rounding.
First, split the timespan into 3 chunks:
Everything from the beginning of the range up until the first possible Monday morning at midnight
Everything from the end of the previous block up to the last possible Monday morning at midnight
Everything from the end of the previous block to the end of the range
Then calculate the amount of weekend in each block, and sum them up.
The first block will contain no weekend time if it has a length of zero. If it has a length of 48 hours or less, its weekend time will equal its length. Otherwise, its weekend time will be 48 hours.
The middle block's weekend time is its duration multiplied by 2/7 (trivial, since by our restrictions on its start and end, it is a whole number of weeks.)
The final block will contain no weekend time if its duration is less than or equal to five days. If its duration is greater than five days, its weekend time will equal its duration minus five days.
Special cases
If the block contains no monday midnights, consider the second and third blocks to have a length of zero. Find the beginning and the end of the one weekend inside the block (if it exists) and calculate the overlap.
If the block contains exactly one monday midnight, consider the first block the time before it, the second block to be of zero length, and the third block the time after it, and calculate as usual.
I'm sure there are some much faster and less-processor hungry methods to getting around this. But for me this method was good enough and gets the job done.
//$sDateRaw = First day at 00:00 in Unix Epoch
//$eDateRaw = Last day(+1) at 00:00 in Unix Epoch
//86400 = Number of seconds in 1 day
for($i=$sDateRaw;$i<$eDateRaw; $i+=86400){
if(date("N",$i) != 6 && date("N",$i) != 7){
$weekDays += 1;
}
}
//$weekDays = Total number of days excluding weekends