I have a table (projects) which contains a start_date and duration (in weeks) and a number (progress_pw) as to how much progress units can be achieved per week
projects
+----+----------+--------+-----------+
| ID |start_date|duration|progress_pw|
+----+----------+--------+-----------+
| 1 |2018-06-15| 2 | 500 |
| 2 |2018-06-19| 4 | 120 |
I want to get a summary by week of the total number of progress units expected to be used in that given week.
Eg:
*In the week starting Monday 11 June project.id 1 is expected to consume 500 units
*In the week starting Monday 18 June project.id 1 is expected to consume 500 units & project.id 2 is expected to consume 120 units with a total consumption of 620 units.
*In the week starting 2018-12-01 (sometime in the future) there are no active projects so 0 units consumed.
$i=0;$weeks=12;
while ($i < $weeks) {
$thisweek = Carbon::now()->addWeeks($i)->startOfWeek()->format('Y-m-d');
$requiredcap = DB::table('projects')->select(DB::raw("sum(progress_pw) as progress"))
->where('install_date', ">=", $thisweek) //<<< This is where im getting stuck!
->get();
$capacity['label'][] = Carbon::now()->addWeeks($i)->startOfWeek()->format('d M');
$capacity['required'][] = $requiredcap;
$i++;
}
Any pointers of the logic behind this one?
Try this:
$i = 0; $weeks = 12;
while ($i < $weeks) {
$start = Carbon::now()->addWeeks($i)->startOfWeek();
$end = (clone $start)->addDays(6);
$requiredcap = DB::table('projects')
->select(DB::raw('sum(progress_pw) as progress'))
->whereBetween('start_date', [$start->toDateString(), $end->toDateString()])
->orWhere(function ($query) use ($start, $end) {
$query->where('start_date', '<', $start->toDateString())
->where('end_date', '>=', $start->toDateString());
})->first();
$capacity['label'][] = $start->format('d M');
$capacity['required'][] = $requiredcap->progress;
$i++;
}
Related
Fairly new to PS, I want to batch file on Sunday between the 1st and the 2nd Tuesday of the month.
I know how to find the the 1st and 2nd Tuesdays I am looking for, cannot figure out the rest.
$FindNthDay = 1
$WeekDay = 'Tuesday'
[datetime]$Today = [datetime]::NOW
$todayM = $Today.Month.ToString()
$todayY = $Today.Year.ToString()
[datetime]$StrtMonth1 = $todayM + '/1/' + $todayY
while ($StrtMonth1.DayofWeek -ine $WeekDay ) { $StrtMonth1 = $StrtMonth1.AddDays(1) }
$StrtMonth1.AddDays(7 * ($FindNthDay - 1))
#
$FindNthDay = 2
$WeekDay = 'Tuesday'
[datetime]$Today = [datetime]::NOW
$todayM = $Today.Month.ToString()
$todayY = $Today.Year.ToString()
[datetime]$StrtMonth = $todayM + '/1/' + $todayY
while ($StrtMonth.DayofWeek -ine $WeekDay ) { $StrtMonth = $StrtMonth.AddDays(1) }
$StrtMonth.AddDays(7 * ($FindNthDay - 1))
I know how to find the the 1st and 2nd Tuesdays
Since there's only one Sunday in between, you only need to find the first one:
# Get-Date -Day 1 will give us the 1st of the current month
$firstTuesday = Get-Date -Day 1
while($firstTuesday.DayOfWeek -ne 'Tuesday') {
$firstTuesday = $firstTuesday.AddDays(1)
}
And then add another 5 days:
$sundayAfterFirstTuesday = $firstTuesday.AddDays(5).Date
Which (in January 2021) gives us:
PS ~> $sundayAfterFirstTuesday
Sunday, January 10, 2021 12:00:00 AM
In addition to Mathias's fine answer, I got hung up on this. Given that .DayOfWeek is an [enum] that's easily converted to a [Int] I was looking for a mathematically concise way to derive the first Tuesday. Since it's fairly obvious how to then find the following Sunday by just doing .AddDays(5) .
Honestly, I was stumbling a bit because While .DayOfWeek is 0 - 6 how many days to add depends on if the current day, in this case the first of the month, is less than or greater than Tuesday (2). It was worth playing around; here are 2 alternate approaches I came up with:
Example 1: A switch statement that's not at all concise but is very readable:
$Day1 = Get-Date "1/1/2021"
Switch ( $Day1.DayOfWeek )
{
'Sunday' { $Sunday = $Day1.Adddays(2).AddDays( 5 ); Break }
'Monday' { $Sunday = $Day1.Adddays(1).AddDays( 5 ); Break }
'Tuesday' { $Sunday = $Day1.AddDays( 5 ); Break }
'Wednesday' { $Sunday = $Day1.Adddays(6).AddDays( 5 ); Break }
'Thursday' { $Sunday = $Day1.Adddays(5).AddDays( 5 ); Break }
'Friday' { $Sunday = $Day1.Adddays(4).AddDays( 5 ); Break }
'Saturday' { $Sunday = $Day1.Adddays(3).AddDays( 5 ); Break }
}
$Sunday
This is also easy to adjust. For example, if you wanted to switch to 1st Sunday between 1st & 2nd Monday etc...
For the mathematical / logic approach it came out a little more crude:
$Day1 = Get-Date "1/1/2021"
If( [Int]$Day1.DayOfWeek -gt 2 ) { $Interval = 7 - ([Int]$Day1.DayOfWeek - 2) }
Else { $Interval = 2 - [Int]$Day1.DayOfWeek }
$Sunday = $Day1.AddDays( $Interval ).AddDays( 5 )
$Sunday
The above example can be more concise in PowerShell 7+ using the ternary operator:
$Day1 = Get-Date "1/1/2021"
$Interval = $Day1.DayOfWeek -gt 2 ? 7 - ([Int]$Day1.DayOfWeek - 2) : 2 - [Int]$Day1.DayOfWeek
$Sunday = $Day1.AddDays( $Interval ).AddDays( 5 )
$Sunday
Note: all of the examples user 1/1/2021 as the starting date, but I did test across multiple 1st days of the month, For example, if the first day was a Monday, Sunday, Thursday etc...
Note: Some of the [Int] casting can probably be removed if one is careful about PowerShell type conversion system, but I wanted to get this out there. If I have time I'll cleanup further.
Or a slightly different approach:
$BaseDate = (Get-Date 3/1/2021)
$BaseDate.AddDays( (& {if ([int]$BaseDate.DayOfWeek -gt 2) {14}
else { 7 } }) - [int]($BaseDate).DayOfWeek)
Sunday, March 7, 2021 12:00:00 AM
I would like Powershell to determine the current week & print monday - friday in the format:
mm/dd/yyyy - mm/dd/yyyy
I saw a way to get the day of the week which is nice but I would like to just have it show the dates.
What I have so far, it works but if I run any day but Monday the dates would be off:
$bar = "------------------------------------"
$today = (Get-Date)
$dates = #($today.AddDays(0).ToString('MM-dd-yyyy'),
$today.AddDays(1).ToString('MM-dd-yyyy'),
$today.AddDays(2).ToString('MM-dd-yyyy'),
$today.AddDays(3).ToString('MM-dd-yyyy'),
$today.AddDays(4).ToString('MM-dd-yyyy'))
$result = "`n{0}`n{1}`n`n`n{2}`n{3}`n`n`n{4}`n{5}`n`n`n{6}`n{7}`n`n`n{8}`n{9}`n" -f $dates[0], $bar, $dates[1], $bar, $dates[2], $bar, $dates[3], $bar, $dates[4], $bar
echo $result
this seems to do what you want. [grin] it uses the builtin weekday list, indexes into that with the current weekday name, calculates the 1st day of the current week, generates an array of dates for the current week, and finally prints it out with the lines & vertical spacing that you seem to want.
# for my locale, the 1st day is "Sunday"
$WeekDayList = [System.DayOfWeek].GetEnumNames()
$Line = '-' * 40
$Newline = [environment]::NewLine
$BlankLineCount = 3
# the ".Date" property gives you midnite, not "now"
$Today = (Get-Date).Date
$TodayNumber = $WeekDayList.IndexOf($Today.DayOfWeek.ToString())
$WeekStartDate = $Today.AddDays(-$TodayNumber)
$CurrentWeek = foreach ($Offset in 0..6)
{
$WeekStartDate.AddDays($Offset).ToString('yyyy-MM-dd')
}
-join ($CurrentWeek -join "$Newline$Line$($Newline * $BlankLineCount)"), "$Line$($Newline * $BlankLineCount)"
output ...
2019-01-20
----------------------------------------
2019-01-21
----------------------------------------
2019-01-22
----------------------------------------
2019-01-23
----------------------------------------
2019-01-24
----------------------------------------
2019-01-25
----------------------------------------
2019-01-26
----------------------------------------
[not part of the output - needed to show that there are two blank lines above this one. [*grin*]
I have come across the requirement to get a date from a week day, and wrote a function to return a [DateTime].
Using this function and ToString() to format the date to your requirements, gives an output of:
2019/01/21 - 2019/01/25
Code:
Function Get-DateFromDay {
param(
[Parameter(Mandatory = $true)]
[ValidateSet('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday')]
[string]$WeekDay,
[Int]$AddWeeks = 0
)
$DayNumber = #{
'Saturday' = 1;
'Sunday' = 0;
'Monday' = -1;
'Tuesday' = -2;
'Wednesday' = -3;
'Thursday' = -4
'Friday' = -5
}
[System.Datetime]$Today = Get-Date
$NumDaysSinceDateFromDay = $Today.DayOfWeek.value__ + $DayNumber[$WeekDay]
[System.Datetime]$DateFromDayThisWeek = $Today.AddDays(-$NumDaysSinceDateFromDay)
$DateFromDayThisWeek.AddDays( + ($AddWeeks * 7))
}
$Monday = (Get-DateFromDay -WeekDay Monday).ToString('yyyy/MM/dd')
$Friday = (Get-DateFromDay -WeekDay Friday).ToString('yyyy/MM/dd')
Write-Output "$Monday - $Friday"
And a few examples of use for those who come across this post in the future:
Thursday next week:
Get-DateFromDay -WeekDay Thursday -AddWeeks 1
Tuesday last week:
Get-DateFromDay -WeekDay Tuesday -AddWeeks -1
I have been using the following code for the last few months, which loops through a period of months from a predefined date until it gets to today's date.
use Date::Pcalc qw(:all);
$startDay = 1;
$startMonth = '4';
$startYear = '2009';
$dateToday = `date +%Y-%m-%d`;
($yt,$mt,$dt) = split(/\-/,$dateToday);
while ($endMonth <= $mt || $startYear < $yt ) {
if ($startMonth eq '12') {
$endMonth = 1;
$endYear = $startYear + 1;
} else {
$endMonth = $startMonth + 1;
$endYear = $startYear;
}
if ($startMonth eq '12') {
$endYear = $startYear + 1;
}
($meYear,$meMonth,$meDay) = Add_Delta_Days($endYear,$endMonth,$startDay,-1);
$endOfMonth = "$meYear-$meMonth-$meDay";
$monthText = Month_to_Text($startMonth);
$startDate = "$startYear-$startMonth-1";
$endDate = "$endYear-$endMonth-1";
print "$startDate - $endDate\n";
if ($startMonth eq '12') {
$startMonth = 1;
$startYear++;
} else {
$startMonth++
}
}
This has been working great for the last few months, but I've realised that now in December, as $endmonth will never be greater $mt (12), this causes an infinite loop.
I've not been able to figure out any alternate way of doing this. I feel like I should be able to fix this relatively easily but I seem to be having severe 'developer's block'
Thanks in advance to anyone who can assist.
my $date = DateTime->new(
time_zone => 'local',
year => $startYear,
month => $startMonth,
day => 1,
);
my $today = DateTime->today(time_zone => 'local');
while ($date <= $today) {
say $date->ymd('-');
$date->add( months => 1 );
}
I think you have a couple of problems with your code. But lets get to the first problem which is the enddate in month 12 which causes a loop in this statement:
while ($endMonth <= $mt || $startYear < $yt ) {
OK what you should do is something like this, once you have the current date, year month and day. You will notice that others have suggested different way to get the current date You should take up this suggestion. However once you have the date this code below should be adopted:
($yt,$mt,$dt) = split(/\-/,$dateToday);
# the line below will create a date like 201212 (yyyy mm) but if the month is a 1 digit month it will place a 0 in front of it to ensure your yymm variable always holds 6 characters in the format of yyyy mm - ok
my $yymm = $yt . ${\(length($mt) == 1 ? '0' : '')} . $mt;
# Now lets check the end date against the yymm
# initialise end date as end_yymm - again it inserts a 0 for single digit month
my $end_yymm = $startyear . ${\(length($startMonth) == 1 ? '0' : '')} . $startMonth;
# the above should get the date as '200904' from your code provide
# the while will check end_yymm like 200904 < 201212 - yes it is...
## the end_yymm will keep getting incremented each month and so will the year component at the end of each year until it reaches 201212
## then the question 201212 < 201212 will cause the while to end
## If you want it go into 201301 then say while ($end_yymm <= $yymm) {
## Hope you get the picture
while ($end_yymm < $yymm) {
if ($startMonth eq '12') {
$endMonth = 1;
$endYear = $startYear + 1;
} else {
$endMonth = $startMonth + 1;
$endYear = $startYear;
}
## Now this one seems to be repeating the endYear calculation as above - to me it seems redundant - maybe get rid of it
if ($startMonth eq '12') {
$endYear = $startYear + 1;
}
## Now that you have the end year and month incremented setup the end_yymm variable again to be picked up in the while statement:
$end_yymm = $startyear . ${\(length($startMonth) == 1 ? '0' : '')} . $startMonth;
# ...... carry on with the rest of your code
} # end the while loop
And that should do it.
All the best
what i want to do is that i wana show user this
Showing 1 to 10 of 50 entries
Showing 11 to 20 of 50 entries
Showing 21 to 30 of 50 entries
Showing 31 to 40 of 50 entries
Showing 41 to 50 of 50 entries
i have used Zend Paginator in my app lets say
Showing A to B of C entries
I can easily find C which is equal to
$result = $DB->fetchAll($sql);
$total =count($result);
if we see here
$page=$this->_getParam('page',1);
//here we can get the requested page#.
//lets hard code this
$paginator->setItemCountPerPage(10);
$per_page =10;
in my view count($this->paginator) give me total number of pages that is if
if total = 101 = $total
than page = 9 = $page
and paginator = 11 = count($this->paginator)
how can i achieve this but generic mean working with next,previous and so on..
Showing A to B of C entries
Is roughly this:
$page = $paginator->getCurrentPageNumber();
$perPage = $paginator->getItemCountPerPage();
$total = $paginator->getTotalItemCount();
$A = ($page - 1) * $perPage + 1;
$B = min($A + $perPage - 1, $total);
$C = $total;
I'm trying to write a program that will calculate the dividends of stocks. I did this without a subroutine. Right now, I'm trying to modify it so it can run using a recursive routine. Any help with this? Because I'm not so good at this.
Here's the original script + a pathetic attempt.
print "A stock xyz's price is now $100. It has 3.78% dividend. You have 1000 of it and reinvest the dividend into the stock.\n";
my %hash;
#stocknum = 1000;
#dividend = 6780;
while ($#dividend != 20) {
$a = $dividend[-1];
$stock = $stocknum[-1];
$div_total= $stock*100*0.0678;
$stock_total = $stock + int($a/100);
push (#stocknum, $stock_total);
push (#dividend, $div_total);
if ($#dividend == 20) {
last;
}
}
shift (#dividend);
$stock_num = $stocknum[-1];
$div = $stock_num*100*0.0678;
push (#dividend, $div);
#hash{#stocknum} = #dividend;
foreach $key(sort keys %hash) {
print "Stock number: $key\t"."Dividend: $hash{$key}\n";
}
$dividend=0.0378;
I don't think you want recursion. I think you just want to loop over the number of cycles of payouts that you're after. It looks like you're getting all mixed up with arrays for some reason.
print <<'HERE';
A stock xyz's price is now $100. It has 6.78% dividend.
You have 1000 of it and reinvest the dividend into the stock.
HERE
my $shares = 1000;
my $price = 100;
my $dividend = 6.78 / 100;
my $cycles = $ARGV[0] || 20;
foreach ( 1 .. $cycles ) {
local $cycle = $_;
local $payout = $shares * $dividend * $price;
local $new_shares = $payout / $price;
write();
$shares += $new_shares;
}
format STDOUT =
#### #####.###### ######.####### ###.###### #####.######
$cycle, $shares, $payout, $new_shares, $shares+$new_shares,
.
format STDOUT_TOP =
###.####%
$dividend
Cycle Shares Payout New Shares Total Shares
----------------------------------------------------------------
.
This gives me the output:
A stock xyz's price is now $100. It has 6.78% dividend.
You have 1000 of it and reinvest the dividend into the stock.
0.0678%
Cycle Shares Payout New Shares Total Shares
----------------------------------------------------------------
1 1000.000000 6780.0000000 67.800000 1067.800000
2 1067.800000 7239.6840000 72.396840 1140.196840
3 1140.196840 7730.5345752 77.305346 1217.502186
4 1217.502186 8254.6648194 82.546648 1300.048834
5 1300.048834 8814.3310942 88.143311 1388.192145
6 1388.192145 9411.9427423 94.119427 1482.311572
7 1482.311572 10050.0724603 100.500725 1582.812297
8 1582.812297 10731.4673731 107.314674 1690.126971
9 1690.126971 11459.0608610 114.590609 1804.717579
10 1804.717579 12235.9851873 122.359852 1927.077431
11 1927.077431 13065.5849830 130.655850 2057.733281
12 2057.733281 13951.4316449 139.514316 2197.247597
13 2197.247597 14897.3387104 148.973387 2346.220985
14 2346.220985 15907.3782750 159.073783 2505.294767
15 2505.294767 16985.8985220 169.858985 2675.153752
16 2675.153752 18137.5424418 181.375424 2856.529177
17 2856.529177 19367.2678194 193.672678 3050.201855
18 3050.201855 20680.3685775 206.803686 3257.005541
19 3257.005541 22082.4975671 220.824976 3477.830517
20 3477.830517 23579.6909021 235.796909 3713.627426
Don't worry about my use of format; I've had that on the brain this weekend since I rewrote some perlfaq stuff about it then also turned it into Use formats to create paginated, plaintext reports. You could just as easily created the output with printf:
print <<'HERE';
A stock xyz's price is now $100. It has 6.78% dividend.
You have 1000 of it and reinvest the dividend into the stock.
Cycle Shares Payout New Shares Total Shares
----------------------------------------------------------------
HERE
my $shares = 1000;
my $price = 100;
my $dividend = 6.78 / 100;
my $cycles = $ARGV[0] || 20;
foreach ( 1 .. $cycles ) {
my $payout = $shares * $dividend * $price;
my $new_shares = $payout / $price;
printf "%4d %12.6f %12.6f %10.6f %12.6f\n",
$_, $shares, $payout, $new_shares, $shares + $new_shares;
$shares += $new_shares;
}
As a side note, you really don't ever want recursion, and especially not in Perl if you can help it. Other languages get away with it because they know how to unroll your recursion to turn it into an iterative process. Perl, being a dynamic language, can't really do that because it doesn't know if the subroutine will have the same definition on the next go around. It's nice as a computer science topic because it makes the programming marginally easier and they know it all works out in the end. I think I talk about this in Mastering Perl somewhere, but Mark Jason Dominus covers it extensively in Higher-Order Perl. Basically, instead of recursion you use a queue, which is a better skill to practice anyway.