Why does adding 6 days 7½ hours to midnight result in 8:30? - perl

This example takes a base date and adds 7½ hours, 1 day 7½ hours, 2 days 7½ hours, and so on.
use Date::Manip;
use DateTime;
use DateTime::Format::DateManip;
Date::Manip::Date_Init("TZ=America/New_York", "Language=English");
my $otime = DateTime->new(
year => 2013,
month => 3,
day => 4,
hour => 0,
minute => 0,
second => 0,
time_zone => 'America/New_York',
);
my $t1 = UnixDate($otime, "%i:%M %p on %A, %B %e, %Y ");
print "original $t1\n";
for (my $i = 0; $i <= 20; $i++) {
my $dtw = $otime->clone();
$dtw->add(
minutes => (15) * 30,
days => ($i),
);
$t1 = UnixDate($dtw, "%i:%M %p on %A, %B %e, %Y ");
print "$i days $t1\n";
}
When adding 6 days 7½ hours, the result contains an extra hour.
original 12:00 AM on Monday, March 04, 2013
0 days 07:30 AM on Monday, March 04, 2013
1 days 07:30 AM on Tuesday, March 05, 2013
2 days 07:30 AM on Wednesday, March 06, 2013
3 days 07:30 AM on Thursday, March 07, 2013
4 days 07:30 AM on Friday, March 08, 2013
5 days 07:30 AM on Saturday, March 09, 2013
6 days 08:30 AM on Sunday, March 10, 2013 # why 8:30 and not 7:30?
7 days 07:30 AM on Monday, March 11, 2013
8 days 07:30 AM on Tuesday, March 12, 2013
9 days 07:30 AM on Wednesday, March 13, 2013
10 days 07:30 AM on Thursday, March 14, 2013
11 days 07:30 AM on Friday, March 15, 2013
12 days 07:30 AM on Saturday, March 16, 2013
13 days 07:30 AM on Sunday, March 17, 2013
14 days 07:30 AM on Monday, March 18, 2013
15 days 07:30 AM on Tuesday, March 19, 2013
16 days 07:30 AM on Wednesday, March 20, 2013
17 days 07:30 AM on Thursday, March 21, 2013
18 days 07:30 AM on Friday, March 22, 2013
19 days 07:30 AM on Saturday, March 23, 2013
20 days 07:30 AM on Sunday, March 24, 2013

Because Daylight Saving Time begins on March 10, 2013 in the America/New_York timezone. DateTime first adds $i days (to get midnight on March 10) and then adds 450 minutes to get 8:30 AM (because the minute after 1:59 AM on March 10 is 3:00 AM). The order of the parameters to add is not meaningful; see Adding a Duration to a Datetime.
Because it adds days & minutes separately (and processes the days first), the effect only happens on the date when DST actually begins or ends. If you want a particular time, just set it directly instead of using add. Or call add twice, once to add minutes, then again to add days.

Related

Get data per Mon - Sunday week

I have a table with columns(id, name, total, timestamp) column. I wish to obtain data for total per week, per candidate with postgreSQL.
For example
Date, total, name (columns)
Jan 16-23, 23 , Marcus
Jan 16-23, 21 , Dennis
Jan 16-23, 18 , Sydney
Jan 23-39, 14, Marcus
Jan 23-39, 13, Dennis
Jan 23-39, 12, Sydney
Jan 30- Feb 5, 10, Marcus
Jan 30- Feb 5, 14, Dennis
Jan 30- Feb 5, 21, Sydney
How can I achieve this?

How do I count discontinued dates in PowerBI?

I want to count the discontinued dates per ID with filter "FilterByValue" by 1.
What I mean by discontinued dates.
04.01.2021
05.01.2021
06.01.2021
08.01.2021
07.01.2021 date would be missing to be a continued date when a day between dates is missing its discontinued.
Dates have also to be distinct and within the last 90 Days.
RowID is just for explanation purposes.
RowID
ID
FilterByValue
Date
1
1
1
Monday, 4. January 2021
2
1
1
Tuesday, 5. January 2021
3
1
1
Tuesday, 5. January 2021
4
1
1
Wednesday, 6. January 2021
5
1
1
Monday, 11. January 2021
6
1
99
Friday, 8. January 2021
7
2
1
Tuesday, 9. February 2021
8
2
1
Wednesday, 10. February 2021
9
2
1
Thursday, 11. March 2021
10
2
1
Friday, 12. March 2021
11
2
1
Monday, 15. March 2021
12
2
1
Tuesday, 16. March 2021
13
2
99
Sunday, 14. March 2021
14
2
1
Wednesday, 14. April 2021
What I want to achieve:
RowID
ID
CountDiscontinuedDates
1
1
2
2
2
4
What I tried, I think is a bad/ not helping approach:
discontinuesDates = COUNTAX(FILTER(TableName, [ID]=1 && TableName[Date] > (TODAY()-90) && OR (DATEADD( TableName[Date] = (TableName[Datum],1,DAY), DATEADD( TableName[Date] = (TableName[Datum],-1,DAY) ) && TableName[ID] = EARLIER(TableName[ID]) && TableName[Date] = TableName[Date] ), TableName[ID])
discontinuesDates = CALCULATE(COUNT(TableName[ID]), FILTER(TableName, TableName[FilterByValue]=34 && TableName[ID] = EARLIER( TableName[ID]) && DATEADD( TableName[Date],1,DAY) <> EARLIER( TableName[Date])) )
Maybe something like this:
Assuming that FilterByValue is available to use:
_Foo =
CALCULATE (
DISTINCTCOUNT ( DiscontinuedDatesData[Date] ),
FILTER (
DiscontinuedDatesData,
'DiscontinuedDatesData'[Date] >= CALCULATE (MIN ( 'DiscontinuedDatesData'[DATE] ), 'DiscontinuedDatesData', DiscontinuedDatesData[FilterByValue] = 99)
&& 'DiscontinuedDatesData'[Date] >= TODAY() - 90
)
)

Billing cycle, get a date every month (no such Feb 30)

I have a column called anchor which is a timestamp. I have a row with value of jan 30 2020. I want to compare this to feb 29 2020, and it should give me 1 month. Even though its not 30 days, but feb has no more days after 29. I am trying to bill every month.
Here is my sql fiddle - http://sqlfiddle.com/#!17/6906d/2
create table subscription (
id serial,
anchor timestamp
);
insert into subscription (anchor) values
('2020-01-30T00:00:00.0Z'),
('2019-01-30T00:00:00.0Z');
select id,
anchor,
AGE('2020-02-29T00:00:00.0Z', anchor) as "monthsToFeb29-2020",
AGE('2019-02-28T00:00:00.0Z', anchor) as "monthsToFeb28-2019"
from subscription;
Is it possible to get age in the way I am speaking?
My expected results:
For age from jan 30 2020 to feb 29 2020 i expect 1.0 month
For age from jan 30 2020 to feb 28 2019 i expect -11.0 month
For age from jan 30 2019 to feb 29 2020 i expect 13.0 month
For age from jan 30 2019 to feb 28 2019 i expect 1.0 month
(this is how momentjs library does it for those node/js guys out there):
const moment = require('moment');
moment('Jan 30 2019', 'MMM DD YYYY').diff(moment('Feb 29 2020', 'MMM DD YYYY'), 'months', true) === -13.0
moment('Jan 30 2019', 'MMM DD YYYY').diff(moment('Feb 28 2019', 'MMM DD YYYY'), 'months', true) === -1.0
How about:
select round(('2/29/2020'::date - '1/30/2020'::date) / 30.0);
round
-------
1
select round(('02/28/2019'::date - '1/30/2020'::date ) / 30.0);
round
-------
-11
select round(('2/29/2020'::date - '1/30/2019'::date) / 30.0);
round
-------
13
select round(('2/28/2019'::date - '01/30/2019'::date) / 30.0);
round
-------
1
The date subtraction gives you a integer value of days, then you divide by a 30 day month and round to nearest integer. You could put this in a function and use that.

PostgreSQL grouped rolling average (for multiple labels)

I could not find a clear example of this online.
I want a moving average for the last 2 days based on this data:
create table expenses as (
select 'food' as expense, 5.0 as cost, current_date as date
union select 'food', 5.0, current_date - 1
union select 'food', 4.0, current_date - 2
union select 'food', 4.0, current_date - 3
union select 'food', 3.0, current_date - 4
union select 'food', 3.0, current_date - 5
union select 'entertainment', 9.0, current_date
union select 'entertainment', 9.0, current_date - 1
union select 'entertainment', 8.0, current_date - 2
union select 'entertainment', 8.0, current_date - 3
union select 'entertainment', 7.0, current_date - 4
union select 'entertainment', 7.0, current_date - 5
)
Here is the solution I put together
select
expense,
date,
cost,
avg(cost) over
(partition by expense order by date rows 2 preceding) as rolling_avg_cost
from expenses
which gives the result:
expense date cost rolling_avg_cost
entertainment Thursday, March 23, 2017 12:00 AM 7 7
entertainment Friday, March 24, 2017 12:00 AM 7 7
entertainment Saturday, March 25, 2017 12:00 AM 8 7.3
entertainment Sunday, March 26, 2017 12:00 AM 8 7.6
entertainment Monday, March 27, 2017 12:00 AM 9 8.3
entertainment Tuesday, March 28, 2017 12:00 AM 9 8.6
food Thursday, March 23, 2017 12:00 AM 3 3
food Friday, March 24, 2017 12:00 AM 3 3
food Saturday, March 25, 2017 12:00 AM 4 3.3
food Sunday, March 26, 2017 12:00 AM 4 3.6
food Monday, March 27, 2017 12:00 AM 5 4.3
food Tuesday, March 28, 2017 12:00 AM 5 4.6
As can be seen, the window for the rolling average is 3 days inclusive of the current row (i.e. the current row plus the previous two, all divided by 3).

Perl getting beginning of month for every month for last year but GMT hour added on

I'm storing a collection of dates which are the previous 12 months starting from the beginning of the month. So I have:
my #t = localtime time();
my $m = $t[4];
my $y = $t[5];
foreach my $date (keys %$dates_ref) {
$m -= $comparison{$date}; # a hash of numbers to go back the correct number of months
$dates_ref->{$date} = mktime(0,0,0,1,$m,$y);
}
I end up with a bunch of dates like so:
Current month: 1356998400 which is:
Tue Jan 01 2013 00:00:00 GMT+0000 (GMT Standard Time)
Last 12 months:
1325376000 - Sun Jan 01 2012 00:00:00 GMT+0000 (GMT Standard Time)
1328054400 - Wed Feb 01 2012 00:00:00 GMT+0000 (GMT Standard Time)
1330560000 - Thu Mar 01 2012 00:00:00 GMT+0000 (GMT Standard Time)
1333238400 - Sun Apr 01 2012 01:00:00 GMT+0100 (GMT Daylight Time)
1335830400 - Tue May 01 2012 01:00:00 GMT+0100 (GMT Daylight Time)
1338508800 - Fri Jun 01 2012 01:00:00 GMT+0100 (GMT Daylight Time)
1341100800 - Sun Jul 01 2012 01:00:00 GMT+0100 (GMT Daylight Time)
1343779200 - Wed Aug 01 2012 01:00:00 GMT+0100 (GMT Daylight Time)
1346457600 - Sat Sep 01 2012 01:00:00 GMT+0100 (GMT Daylight Time)
1349049600 - Mon Oct 01 2012 01:00:00 GMT+0100 (GMT Daylight Time)
1351728000 - Thu Nov 01 2012 00:00:00 GMT+0000 (GMT Standard Time)
1354320000 - Sat Dec 01 2012 00:00:00 GMT+0000 (GMT Standard Time)
As you can see, April to October have the GMT hour added on. What is the best way of getting it to not do this, so it just starts at 00:00?
E.g. 1333238400 would become 1333234800
you should use the DateTime module, and properly handle the time zones. This is an example:
my $dt = DateTime->new(
year => 2000,
month => 5,
day => 10,
hour => 15,
minute => 15,
time_zone => 'America/Los_Angeles',
);
print $dt->hour; # prints 15
$dt->set_time_zone( 'America/Chicago' );
print $dt->hour; # prints 17
If you're able to install CPAN modules, I'd suggest using Date::Calc - it will let you convert to GMT instead of to your local timezone,
use gmtime instead of localtime in the first line of your script