Alternate Week start dates - tableau-api

Using tableau, I am trying to analyze data on a weekly basis but with some modifications.
I want the first week of any given month to end on a Saturday with a minimum of one week.
Middle weeks are 7 days long (Sunday to Saturday).
The last week of a month Should be from Sunday to the last day of the month.
This question is very similar to the solution of this question but with minor differences: https://community.tableau.com/thread/230894
Here is an example for April
Week 1 has April 1 start date
Week 2 has April 12 start date
Week 3 has April 19 start date
Week 4 has April 26 start date

This seems to work:
IF DAY([Update Time])<[ISO Time]
THEN IF [ISO Time] <=6
THEN DATEADD('day',7-[ISO Time],[Update Time])
ELSE DATEADD('day', 7-[ISO Time],[Update Time])
END
ELSE IF [ISO Time]<=6
THEN DATEADD('day',-[ISO Time],[Update Time])
ELSE DATEADD('day',7-[ISO Time],[Update Time])
END
END

Related

PostgreSQL WHERE inside a CASE expression

Say I have this table below:
month
num_of_fruits
gain
2022-01-01
5
4
2022-02-01
10
5
2022-03-01
9
8
I'm looking to create a query based on a calculation. Is it possible to put a WHERE condition inside a CASE expression? The calculation for it is:
Set the 'test' column as num_of_fruits / this month's gain + previous month's gain.
Now the calculation is a bit weird for each month:
January will be num_of_fruits / this month's gain + previous month's (Dec) gain
February will be num_of_fruits / this month's gain + previous month's (Jan) gain + December of last year's gain
March will num_of_fruits / this month's gain + previous month's (Feb) gain + 2 months ago's (Jan) gain + December of last year's gain
The rest follows the same format where it sums the gains of the current month until December of last year's
I tried to come up with a query in PostgreSQL for just the month of January:
CASE
WHEN "month" = '2022-01-01' THEN num_of_fruits / ("gain" + "gain" WHERE (date_trunc('month', "month" - interval '1 month')))
END "test"
I know it's possible to have a calculation after the THEN clause, but is it possible to have a condition there (i.e. getting the gain value of last month's or any previous month's)?
Are there other solutions? Any tips, advice, or suggestions are greatly appreciated!
Thank you so much in advance!

Is there a name for date/time interval format like "1h10m"

It's commonplace even outside of software to communicate time or date intervals in a truncated manner. For example: 1h10m translates to "One hour and ten minutes."
This could be abstracted to a set of rules. For instance: A date interval is represented as a combination of _h, _m, (and so on), where _ characters represent non-negative integers or floats, which are summed into one date interval object.
Mixing days, hours, minutes are allowed. For example, 0.5d1h60m would be a synonym for 14h.
Is there a standard defined out there anywhere that resembles this?
The standard for this is a Duration, defined by ISO 8601.
Note that an Interval is a different concept (also defined by the same ISO), although both are closely related:
A Duration defines an amount of time (like "1 hour and 10 minutes" or "2 years, 3 months and 4 days"). But it doesn't tell you when it starts or ends ("1 hours and 10 minutes" relative to what?). It's just the amount of time, by itself.
An Interval (quoting wikipedia) is "the intervening time between two time points". It has a defined start and end dates, but you can use a Duration to define it, as it can have 4 different formats:
Start and end, such as 2007-03-01T13:00:00Z/2008-05-11T15:30:00Z
Start and duration, such as 2007-03-01T13:00:00Z/P1Y2M10DT2H30M
Duration and end, such as P1Y2M10DT2H30M/2008-05-11T15:30:00Z
Duration only, such as P1Y2M10DT2H30M, with additional context information
Cases 1, 2 and 3 are equivalent (all have the same start and end dates). The only difference is that in cases 2 and 3, the duration P1Y2M10DT2H30M is used to calculate the other date (in case 2, you add it to the start date, and in case 3 you subtract it from the end date).
As you can notice above, the standard format for a Duration is P[n]Y[n]M[n]DT[n]H[n]M[n]S, where:
P is the duration designator (for period) placed at the start of the duration representation.
Y is the year designator that follows the value for the number of years.
M is the month designator that follows the value for the number of months.
W is the week designator that follows the value for the number of weeks.
D is the day designator that follows the value for the number of days.
T is the time designator that precedes the time components of the representation.
H is the hour designator that follows the value for the number of hours.
M is the minute designator that follows the value for the number of minutes.
S is the second designator that follows the value for the number of seconds.
So, "1 year and 10 months" is represented as P1Y10M and "1 hour and 10 minutes" is PT1H10M (note that the T is required to resolve the potencial ambiguity between 1 month (P1M) and 1 minute (PT1M), as they use the same letter M as designator).
As #MattJohnson commented, the math with dates it's not always obvious, so the equivalence between different durations can't be what we normally expect.
For the examples below, I'm using Java 8 (just to show how durations can be tricky). Note that the java.time API uses 2 different classes (Period and Duration), but the idea for both is the same (they're both amounts of time).
A duration of 1 month is equivalent to how many days? It depends:
// one month period
Period oneMonth = Period.parse("P1M");
// January 1st
LocalDate jan = LocalDate.of(2016, 1, 1);
System.out.println(jan); // 2016-01-01
// January 1st plus 1 month period = February 1st
LocalDate feb = jan.plus(oneMonth);
System.out.println(feb); // 2016-02-01
// February 1st plus 1 month period = March 1st
LocalDate mar = feb.plus(oneMonth);
System.out.println(mar); // 2016-03-01
// difference between Jan 1st and Feb 1st = 31 days
System.out.println(ChronoUnit.DAYS.between(jan, feb)); // 31
// difference between Feb 1st and Mar 1st = 29 days (2016 is leap year)
System.out.println(ChronoUnit.DAYS.between(feb, mar)); // 29
So, adding 1 month to January 1st results in February 1st - in this case, 1 month is equivalent 31 days (A.K.A. adding a 1 month duration (P1M) is equivalent to adding a 31 days duration (P31D)), and adding 1 month to February 1st results in March 1st (in this case, 1 month = 29 days, because 2016 is a leap year).
1 day = 24 hours? Not always. If there's a Daylight Saving Time shift involved, you can get strange results:
// 1 day period
Period oneDay = Period.parse("P1D");
// 24 hours period
Duration twentyFourHours = Duration.parse("PT24H");
// in Sao Paulo, summer time starts at Oct 15, at midnight
// getting a date one day before DST change, at 10:00 AM
ZonedDateTime z = ZonedDateTime.of(2017, 10, 14, 10, 0, 0, 0, ZoneId.of("America/Sao_Paulo"));
System.out.println(z); // 2017-10-14T10:00-03:00[America/Sao_Paulo]
// add 1 day - gets the same hour (10:00 AM)
System.out.println(z.plus(oneDay)); // 2017-10-15T10:00-02:00[America/Sao_Paulo]
// add 24 hours - gets 11:00 AM because of DST shift (at midnight, clocks moved forward 1 hour)
System.out.println(z.plus(twentyFourHours)); // 2017-10-15T11:00-02:00[America/Sao_Paulo]
In São Paulo, at October 15th, 2017, DST starts (clocks are moved forward by 1 hour), so:
If you add 24 hours to October 14th at 10 AM, you'll get October 15th at 11 AM
But if you add 1 day, you'll get October 15th at 10 AM
So, in this case, 1 day = 23 hours - it means that adding a 1 day duration (P1D) is equivalent to adding a 23 hours duration (PT23H)
When DST ends, is the opposite: clocks move back 1 hour, and 1 day will be equivalent to 25 hours.
So, the standard defines the format and meaning of the amounts of time concepts, but the equivalence between different durations will depend on the context (although it might sound non-intuitive that 1 day is not always 24 hours, but date/time math is not as obvious as we'd like).
You can use moment: http://momentjs.com/docs/#/durations/
moment.duration(100); // 100 milliseconds
moment.duration(60000).humanize(); // a minute
Read more in the above linked docs. And to get all unit values you may want to use the ISO8601 Format:
moment.duration(1, 'd').toISOString() // "P1D"
For example, "P3Y6M4DT12H30M5S" represents a duration of "three years, six months, four days, twelve hours, thirty minutes, and five seconds".
Read more directly under http://momentjs.com/docs/#/durations/as-iso-string/

What is the easiest way to calculate how many days of same date between to days in postgresql

To give an example, starting date '2016-01-01' and ending date '2016-06-01', the number of days of '10th' is 5 as it appears in every month from Jan to May. The number of days of '29th' is also 5 as it is a leap year. The number of days of '30th' is 4 and the number of days of '31st' is 3 as they only appear in certain months.
What is the easiest (or say most elegant) way to calculate this number given the starting and ending dates?
You can create a list of dates using generate_series() and then count the occurrences of each day:
select extract(day from dt) as day_in_month, count(*)
from generate_series(date '2016-01-01', date '2016-06-01', interval '1' day) as g(dt)
group by extract(day from dt)
order by 1;

datenum series to the end of February - Leap year or not

I want to create a list of dates that go until the end of February. However, since the end of February changes from 28 to 29 depending on whether there's a leap year, I'm having trouble with how to consider both options.
Here's what I have so far:
date = datenum(years(i),12,01):1:datenum(years(i)+1,02,29);
This case, when run on a year that is not a leap year, ends up counting March 1st instead of ending on Feb. 28th.
Here's a little hack I came up with. You can check whether a year is a leap year quite easily by calculating the number of days between February 28 and March 1, like so:
datenum(years(i), 3, 1) - datenum(years(i), 2, 28)
Checking whether it's larger than 1 would indicate leap year. This 1 or 0 logical MATLAB convention leads to the second part of the hack: this is exactly the number of days you need to add to Feb 28: 0 if not leap year, 1 if leap year. Here, therefore, is the full hack:
date = datenum(years(i),12,01):datenum(years(i)+1,02, ...
28 + ((datenum(years(i)+1,3,1) - datenum(years(i)+1,2,28))>1) );
UPDATE / IMPROVEMENT:
Answer already accepted, but I came up with an even better solution. I didn't realize that datenum simply counts days. In this case, we can simply say that the last day of February is the day before March 1. This yields the following drastic simplification:
date = datenum(years(i),12,01):1:(datenum(years(i)+1,3,1)-1);
Datenum, for good or ill, takes negative and zero numbers. So the last day of February can be written:
datenum(2015, 3, 0)
With a comment explaining this madness, of course.

Automating a holiday date for any year in Matlab

The code below gives a decimal day for one specific year.
HolidayArrayDate(2) = datenum(2012,01,16,00,00,00); %MartinLutherKingJrBirthday
I am trying to make the holiday more general for an input "dataYear" instead of specifying it as "2012". Martin Luther King Jr Birthday is the third Monday of ever year. When i provide it an input of any year 2010/2011/2012/2013/2014 through "dataYear", it should automatically choose the third Monday in January for me. How would i dot this?
Thank you!
Starting from the first day of the year, 21 days always suffice to find the third Monday. So: get serial date number for first day of the year (with datenum); get day-of-the-week for that and the following 20 days (datestr(..., 'd')); find the first three Mondays (find(...=='M', 3); and finally pick the third one and convert it into date string (datestr):
dataYear = 2012; %// input
f = datenum(dataYear,1,1); %// 1st day of year, in serial date number format
r = find(datestr(f+(0:20), 'd')=='M', 3); %// find three Mondays from that day on
result = datestr(f+r(3)-1); %// third Monday, in date string format