Python3 Leap Year Calculator - debugging

Ok so I'm learning python3 and thought maybe trying out my first script with a leapyear calculator but I can't seem to figure out how to print if the current year is a leapyear or not much less calculate the next two.
Here's my code thus far:
#!/usr/bin/python3
#calculate if this year is a leap year and the next 2 leap years
from datetime import datetime
this_year=(datetime.now().strftime('%Y'))
def is_leap_year(year):
if ((year % 4 == 0) and (year % 100 != 0)) or (year % 400 == 0):
return "{0}, {1} is a leap year".format(True, year)
return "{0} is not a leap year".format(year)
print(is_leap_year(this_year)
So could anyone point out my error(s) here? Many thanks!

You have a whole library of date manipulation routines and whatnot there, just use them:
try:
datetime.date(2016, 2, 29)
print("is leap-year")
except ValueError:
print("isn't leap-year")
If the date() method is able to handle the 29th of February, it's a leap-year, otherwise not.

Related

Ruby. How to write a loop of 1% percentage profit for one year on a 1000$ investment

I'd like to create a ruby program to calculate the 1% on my investment every day for one year.
For example, if I invest 1000$ and get a profit of 1% at the end of the day will be 1010.0$ The second day I will invest 1010.0$ and I will get a 1% profit of 1020.1$ and so on.
I'd like to determine after 365 days what will be my initial investment.
I'm trying with a loop to print every single returning value but as you see I'm still a superrookie.
Thanks. Sam
I made it alone! Thanks for all of your answers!
money = 1000
days = 0
perc = 0.01
while days < 366
puts days
puts money
days += 1
money = money * perc + money
end
1000 * 1.01**365
#=> 37783.43433288728
You don't need to write a program for this; it's a one-line calculation.
But if you want to do it one day at a time and show the output of each day, how about:
money = 1000
(1..365).each do |day|
money *= 1.01
puts "After #{day} days: $#{money.round(2)}"
end
You should use BigDecimal instead of Float when dealing with monetary values:
require 'bigdecimal'
money = BigDecimal('1000')
percentage = BigDecimal('0.01')
For the loop I'd use upto which works very intuitively:
1.upto(365) do |day|
money += (money * percentage).round(2)
printf("%3d: %8.2f$\n", day, money)
end
money * percentage calculates the day's profit, rounded to 2 digits via round. You can adjust the rounding mode by passing a second argument.
printf then outputs day and money using the given formatting:
%3d prints an integer with width 3
%8.2f prints a float with 2 fractional digits and a total width of 8
Output:
1: 1010.00$
2: 1020.10$
3: 1030.30$
...
363: 37039.07$
364: 37409.46$
365: 37783.55$
The following is super simple but will get the job done. Basically you initialize a variable to 1000, we then loop 365 times. We have a block where all the math happens. It takes the value 1000 and multiplies it by 1.01 and overwrites the value of the intial investment. You can change the 365 to 2 or however many days you want. The puts print the value of the start. Just run this by putting it in a .rb file and running 'ruby file.rb'
start = 1000
365.times do
start = start*1.01
puts start
end
puts start

Get the first occurrence of a specific day after a given date

This thread provides an answer to find the first occurrence of a day (e.g. Thursday) after today's date.
I would like to get a more general function which gives the first occurrence of a day (e.g. Thursday) after any given date (e.g. November 1st, 2017).
The method would therefore take 2 arguments: the Date (either as a String, or as a Date object) and the day (as a String).
One way I found is to check each day after with the thursday? method (e.g. my_date_object.thursday?) but you would need a switch / if-else statement to check which day to check against, which makes the method rather bulky, as seen below.
def get_next_day_after_date(date, day)
days_in_week = 7
week = []
days_in_week.times { |day_after| week << (date + day_after) }
if day.casecmp('Monday').zero?
week.select(&:monday?)
elsif day.casecmp('Tuesday').zero?
week.select(&:tuesday?)
elsif day.casecmp('Wednesday').zero?
week.select(&:wednesday?)
# same code for Thursday to Sunday
end
end
Is there a cleaner way to achieve this in pure Ruby? (no use of active_support or rails-specific methods).
require "date"
def get_next_day_after_day(date, day)
date + (Date.strptime(day, "%A") - date) % 7
end

Finding the day in which a given year begins

This question arose when I was trying to understand Sakamoto's algorithm for finding the day of a given date.
I found the working of the algorithm to be difficult to comprehend even after reading the following Stackoverflow answer
So, I decided to first solve a specific problem of finding the day in which a given year begins( Jan-1).
From the Sakamoto's algorithm, I just took the part of adding the additional days contributed by the leap and non-leap years.
My code is as follows:
public String getDay(String date)
{
String[] days = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
int day = Integer.parseInt(date.split("/")[0]);
int month = Integer.parseInt(date.split("/")[1]);
int year = Integer.parseInt(date.split("/")[2]);
year--; // to calculate the additional days till the previous year
int dayOfTheWeek = (year + year/4 - year/100 + year/400) % 7;
return days[dayOfTheWeek];
}
Thus, for the date "1/1/0001", it returns Sunday.
To verify its correctness, I implemented Sakamoto's algorithm and compared the results and my program's result always seems to be one day before the day returned by the Sakamoto's algorithm.
For the date "1/1/0001" my program returns Sunday, while Sakamoto's returns Monday.
So,
1) Does it mean that the Gregorian calendar started on Monday instead of Sunday??
2) If yes, does it mean I should add 1 to the result to get the right day or is my program logically incorrect?
Finally, I used TimeAndDate site's day calculator tool and "1/1/0001" starts on Saturday.
My final question is
3) On what day does the Gregorian calendar start?
Any light on the these questions is much appreciated.
Thanks,
What exactly is the point of reinventing the wheel?
Joda-Time is a de facto standard for date-time operations in Java, and it provides dayOfWeek method for its DateTime objects. See e.g. http://joda-time.sourceforge.net/userguide.html#Querying_DateTimes
If you are then still interested in details how to get the computation right, see https://github.com/JodaOrg/joda-time/blob/master/src/main/java/org/joda/time/chrono/BasicChronology.java#L538

Comparison using Ruby's .days.ago seems to use reverse logic

Can anyone help me make sense of this, please?
I am getting a very weird behaviour (reverse logic), when I am trying to use the following code.
require 'active_support/all'
c = {
id: 5,
years_of_experience: 4,
github_points: 293,
languages: ['C', 'Ruby', 'Python', 'Clojure'],
date_applied: 5.days.ago.to_date,
age: 26
}
c["date_applied"] > 15.days.ago.to_date - #works
c["date_applied"] < 15.days.ago.to_date - #doesnt work
c["date_applied"] gives a date value stored in a hash.
The latter makes more logical sense, but the first returns the right answer.
The code's behavior is correct, but I think I understand the confusion.
You're reading
c["date_applied"] > 15.days.ago
as:
Is the date applied more than 15 days ago?
and
c["date_applied"] < 15.days.ago
as:
Is the date applied less than 15 days ago?
and it's giving you the reverse of the answer you expect, right?
If that's the case, you should take a moment to understand how time comparisons operate. When you type date1 > date2, you're actually saying,
If I plot date1 and date2 on a number line with time increasing from left to right,
is date1 to the right of date2?
This is the same as when you type 2 > 1. It means,
If I plot 1 and 2 on a number line with the numbers increasing from left to right,
is 2 to the right of 1?
Given that this is how time comparisons operate, let's reexamine your code.
require 'active_support/all'
c = { date_applied: 5.days.ago.to_date }
c[:date_applied] > 15.days.ago.to_date
Correctly interpreted, this says
Is the date 5 days ago further rightward on a left-to-right timeline than the date 15 days ago?
and the answer is yes, or true.
If, on the other hand, you were to incorrectly interpret this as
Is 5 days ago more than 15 days ago?
you would get (or expect to get) the mistaken answer of no, or false.
The correct way to think about the task in English is to reframe the question of
Is date d more than n days ago?
and instead think of it as
Is date d earlier than the date n days ago?
and the correct code becomes apparent:
d.to_date < n.days.ago.to_date
If I understood your question correctly, this should explain it.
irb ## ruby-1.9.3-p448
require 'active_support/time'
c = {
id: 5,
years_of_experience: 4,
github_points: 293,
languages: ['C', 'Ruby', 'Python', 'Clojure'],
date_applied: 5.days.ago.to_date,
age: 26
}
(c[:date_applied] > 15.days.ago.to_date) - #true
(c[:date_applied] < 15.days.ago.to_date) - #false
###or you can try it by adding your own private methods###
class Fixnum
def days
self * 60 * 60 * 24 # we store seconds in a day
end
def ago
Time.now - self
end
end

Calculating future occurrences of Friday the 13th

I'd like to be able to start with a year, and calculate occurrences of Friday the 13th. A brute force solution is easy and obvious. I have something slightly better, but I have no doubt that someone else can come up with an elegant algorithm for this.
Perhaps a little trickier, I'd be interested in giving the program a month, and have it find the next year in which that month has a Friday the 13th.
Feel free to use pseudo code, but I expect people will vote more for working code samples in you favorite language.
Any month that starts with a Sunday has a Friday on the thirteenth. There are only 14 combinations possible knowing what day the first of the year is on (with or without leap year, and sun-sat). You should just calculate it once and get it over with. You'd only check 14*12 possible months to start out with, well with in reason.
resultant table element (from 2009, 2010):
[Thursday,false] => Feb, March, Nov
[Friday,false] => Aug
to fill the table you have a generic month Jan(31),Feb(28).. and then iterate with a seed of each day of the week, noting months that start with sunday, and also with a leap year and without. Pretty straight forward, and once done, you can share it with us :)
Since your brute force algorithm is apparently the intuitive day-by-day iteration option, perhaps you haven't considered the Doomsday Algorithm. It would allow you to simply check if that 13th is a Friday. As far as I know, it is the most efficient solution to the problem.
Here's some example PHP code that goes through a pretty straight forward loop of the dates in a range. I would modify this to check the 13th of each month for Fridayness, rather than checking every Friday for 13thness, as they do in the example.
One thing I noticed is that the first of the month falls on a Sunday during months with a Friday the 13th. You can probably leverage this to make it easier to calculate.
initialize startDate to 13th of the month given in the current year
while (true) {
if (startDate.dayOfWeek == Date.FRIDAY)
break;
else
startDate.year ++;
}
return startDate.year;
This is how I would do it:
Assume year is known and is an integer.
Loop from 1 to 12
Create date with loop index, year and 13 for the day
Determine day of week as per established algorithms
If day of week calculated above is Friday, do your work
If you want to start with a month and year (you have to assume some sort of year), your algorithm becomes
Assume year is known and an integer
Assume month is known and is an integer
Loop
Create date with index of loop as year, known month variable, and 13 for the day
Determine day of week as per established algorithms
If day of week calculate above is Friday, return date, else
Else increment year by 1
I use PowerShell 7.1 x64 on Windows 10, I am also interested in this, though my programming skill is not advanced enough to develop an independent and complex algorithm (without built-in) to solve this, currently I can do this:
$start=Read-Host "Input start year"
$end=Read-Host "Input end year"
$years=$start..$end
$blackfriday=#()
foreach ($year in $years) {
$blackfriday+=1..12 | foreach { [datetime]"$year-$_-13" } | where { $_.DayOfWeek -eq 'Friday'} | foreach {$_.tostring("yyyy, MMMM dd, dddd")}
}
$blackfriday
Update:
Now I have this, by brute forcing year-month-13 to convert date to days, and get the results where days mod 7 equal to 5, I have used all the advanced algorithms found on Wikipedia: https://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week
And I found out they don't work well with PowerShell, don't know if it's just me or Wikipedia got it wrong, anyway here is my script, I have fc compared the results of the script with builtin and this script, and it shows"FC: no differences encountered":
$months=#(
[PSCustomObject]#{Month='January';Days=31}
[PSCustomObject]#{Month='February';Days=28}
[PSCustomObject]#{Month='March';Days=31}
[PSCustomObject]#{Month='April';Days=30}
[PSCustomObject]#{Month='May';Days=31}
[PSCustomObject]#{Month='June';Days=30}
[PSCustomObject]#{Month='July';Days=31}
[PSCustomObject]#{Month='August';Days=31}
[PSCustomObject]#{Month='September';Days=30}
[PSCustomObject]#{Month='October';Days=31}
[PSCustomObject]#{Month='November';Days=30}
[PSCustomObject]#{Month='December';Days=31}
)
function BlackFriday {
param(
[Parameter(ValueFromPipeline=$true, Mandatory=$true, Position=0)] [int64] $start,
[Parameter(ValueFromPipeline=$true, Mandatory=$true, Position=1)] [int64] $end
)
$years=$start..$end
$blackfriday=#()
foreach ($year in $years) {
$array=1..12
foreach ($arra in $array) {
$month=0
for ($i=0;$i -lt $arra-1;$i++) {
$month+=$months[$i].Days
}
[int]$ye=$year
if ($arra -le 2) { $ye-=1}
if ($ye % 4 -eq 0) {$leap=$ye/4}
else {while ($ye % 4 -ne 0) {$ye-=1}
$leap=$ye/4}
if ($ye % 100 -eq 0) {$century=$ye/100}
else {while ($ye % 100 -ne 0) {$ye-=4}
$century=$ye/100}
if ($ye % 400 -eq 0) {$cycle=$ye/400}
else {while ($ye % 400 -ne 0) {$ye-=100}
$cycle=$ye/400}
$leap=$leap-$century+$cycle
$date=[int64](($year-1)*365+$month+13+$leap)
if ($date % 7 -eq 5) {
$name=$months[$arra-1].Month
$blackfriday+=[string]"$year, $name 13, Friday"
}
}
}
$blackfriday
}
$start=Read-Host "Input start year"
$end=Read-Host "Input end year"
BlackFriday $start $end

Resources