Related
I'm writing a program to represent the "qlocktwo" clock in python.
This is the code:
The most important part to the question is the last four lines (I've left the rest to negate any confusion).
#!/usr/bin/python3
from datetime import datetime
import num2words
import string
grid = "itlisasampmacquarterdctwentyfivexhalfstenftopasterunineonesixthreefourfivetwoeightelevenseventwelvetenseoclock"
now = datetime.now()
minute = int(now.strftime("%M"))
hour = int(now.strftime("%H"))
def checkTime(hour, minute):
if hour >= 12:
hour -= 12
t = "past"
if hour == 0:
a = "oclock"
if minute > 30:
a = 60 - minute
t = "to"
hour += 1
return a, t, hour
elif minute <= 30:
a = minute
t = "past"
return a, t, hour
def round(a):
for i in range(1, 7):
i = i * 5
for j in range(i-2, i + 3):
if a == j:
return i
time = checkTime(hour, 0)
if time[0] != 0:
outMin = num2words.num2words(round(time[0])).replace("-", " ")
else:
outMin = "oclock"
outHour = num2words.num2words(time[2])
output = (f"it is {outMin} {time[1]} {outHour}").split(" ")
newGrid = grid
for i in output:
newGrid = newGrid.replace(i, f"\033[92m{i}\033[0m")
for i in [newGrid[i:i+10] for i in range(0, len(newGrid), 10)]:
print(i)
At the end, I've used this answer to split based on the number of characters (in my case, 10), but this also ends up counting hidden characters, such as terminal colors, so it splits on them as well.
So this is the output I get:
https://imgur.com/fiVFPNT
Whereas, this is the output I want (but with the colors remaining):
https://imgur.com/m1sKUqb
Is there a way to split solely on visible characters?
Problem: I need to convert an amount to Indian currency format
My code: I have the following Python implementation:
import decimal
def currencyInIndiaFormat(n):
d = decimal.Decimal(str(n))
if d.as_tuple().exponent < -2:
s = str(n)
else:
s = '{0:.2f}'.format(n)
l = len(s)
i = l-1;
res = ''
flag = 0
k = 0
while i>=0:
if flag==0:
res = res + s[i]
if s[i]=='.':
flag = 1
elif flag==1:
k = k + 1
res = res + s[i]
if k==3 and i-1>=0:
res = res + ','
flag = 2
k = 0
else:
k = k + 1
res = res + s[i]
if k==2 and i-1>=0:
res = res + ','
flag = 2
k = 0
i = i - 1
return res[::-1]
def main():
n = 100.52
print "INR " + currencyInIndiaFormat(n) # INR 100.52
n = 1000.108
print "INR " + currencyInIndiaFormat(n) # INR 1,000.108
n = 1200000
print "INR " + currencyInIndiaFormat(n) # INR 12,00,000.00
main()
My Question: Is there a way to make my currencyInIndiaFormat function shorter, more concise and clean ? / Is there a better way to write my currencyInIndiaFormat function ?
Note: My question is mainly based on Python implementation of the above stated problem. It is not a duplicate of previously asked questions regarding conversion of currency to Indian format.
Indian Currency Format:
For example, numbers here are represented as:
1
10
100
1,000
10,000
1,00,000
10,00,000
1,00,00,000
10,00,00,000
Refer Indian Numbering System
Too much work.
>>> import locale
>>> locale.setlocale(locale.LC_MONETARY, 'en_IN')
'en_IN'
>>> print(locale.currency(100.52, grouping=True))
₹ 100.52
>>> print(locale.currency(1000.108, grouping=True))
₹ 1,000.11
>>> print(locale.currency(1200000, grouping=True))
₹ 12,00,000.00
You can follow these steps.
Install Babel python package from pip
pip install Babel
In your python script
from babel.numbers import format_currency
format_currency(5433422.8012, 'INR', locale='en_IN')
Output:
₹ 54,33,422.80
def formatINR(number):
s, *d = str(number).partition(".")
r = ",".join([s[x-2:x] for x in range(-3, -len(s), -2)][::-1] + [s[-3:]])
return "".join([r] + d)
It's simple to use:
print(formatINR(123456))
Output
1,23,456
If you want to handle negative numbers
def negativeFormatINR(number):
negativeNumber = False
if number < 0:
number = abs(number)
negativeNumber = True
s, *d = str(number).partition(".")
r = ",".join([s[x-2:x] for x in range(-3, -len(s), -2)][::-1] + [s[-3:]])
value = "".join([r] + d)
if negativeNumber:
return '-' + value
return value
It's simple to use:
print(negativeFormatINR(100-10000))
output
-9,900
Note - THIS IS AN ALTERNATIVE SOLUTION FOR ACTUAL QUESTION
If anyone trying to convert in simple Indian terms like K, L, or Cr with 2 floating-point values, the following solution would work.
def format_cash(amount):
def truncate_float(number, places):
return int(number * (10 ** places)) / 10 ** places
if amount < 1e3:
return amount
if 1e3 <= amount < 1e5:
return str(truncate_float((amount / 1e5) * 100, 2)) + " K"
if 1e5 <= amount < 1e7:
return str(truncate_float((amount / 1e7) * 100, 2)) + " L"
if amount > 1e7:
return str(truncate_float(amount / 1e7, 2)) + " Cr"
Examples
format_cash(7843) --> '7.84 K'
format_cash(78436) --> '78.43 K'
format_cash(784367) --> '7.84 L'
format_cash(7843678) --> '78.43 L'
format_cash(78436789) --> '7.84 Cr'
Here is the other way around:
import re
def in_for(value):
value,b=str(value),''
value=''.join(map(lambda va:va if re.match(r'[0-9,.]',va) else '',value))
val=value
if val.count(',')==0:
v,c,a,cc,ii=val,0,[3,2,2],0,0
val=val[:val.rfind('.')] if val.rfind('.')>=0 else val
for i in val[::-1]:
if c==ii and c!=0:
ii+=a[cc%3]
b=','+i+b
cc+=1
else:
b=i+b
c+=1
b=b[1:] if b[0]==',' else b
val=b+v[value.rfind('.'):] if value.rfind('.')>=0 else b
else:
val=str(val).strip('()').replace(' ','')
v=val.rfind('.')
if v>0:
val=val[:v+3]
return val.rstrip('0').rstrip('.') if '.' in val else val
print(in_for('1000000000000.5445'))
Output will be:
10,000,00,00,000.54
(As mentioned in wikipedia indian number system Ex:67,89,000,00,00,000)
def format_indian(t):
dic = {
4:'Thousand',
5:'Lakh',
6:'Lakh',
7:'Crore',
8:'Crore',
9:'Arab'
}
y = 10
len_of_number = len(str(t))
save = t
z=y
while(t!=0):
t=int(t/y)
z*=10
zeros = len(str(z)) - 3
if zeros>3:
if zeros%2!=0:
string = str(save)+": "+str(save/(z/100))[0:4]+" "+dic[zeros]
else:
string = str(save)+": "+str(save/(z/1000))[0:4]+" "+dic[zeros]
return string
return str(save)+": "+str(save)
This code will Convert Yout Numbers to Lakhs, Crores and arabs in most simplest way. Hope it helps.
for i in [1.234567899 * 10**x for x in range(9)]:
print(format_indian(int(i)))
Output:
1: 1
12: 12
123: 123
1234: 1234
12345: 12.3 Thousand
123456: 1.23 Lakh
1234567: 12.3 Lakh
12345678: 1.23 Crore
123456789: 12.3 Crore
Another way:
def formatted_int(value):
# if the value is 100, 10, 1
if len(str(value)) <= 3:
return value
# if the value is 10,000, 1,000
elif 3 < len(str(value)) <= 5:
return f'{str(value)[:-3]},{str(value)[-3:]} ₹'
# if the value is greater the 10,000
else:
cut = str(value)[:-3]
o = []
while cut:
o.append(cut[-2:]) # appending from 1000th value(right to left)
cut = cut[:-2]
o = o[::-1] # reversing list
res = ",".join(o)
return f'{res},{str(value)[-3:]} ₹'
value1 = 1_00_00_00_000
value2 = 10_00_00_00_000
value3 = 100
print(formatted_int(value1))
print(formatted_int(value2))
print(formatted_int(value3))
Ouput:
1,00,00,00,000 ₹
10,00,00,00,000 ₹
100 ₹
As pgksunilkumar's answer, a little improvement is done in case the the number is in between 0 to -1000
def formatINR(number):
if number < 0 and number > -1000:
return number
else:
s, *d = str(number).partition(".")
r = ",".join([s[x-2:x] for x in range(-3, -len(s), -2)][::-1] + [s[-3:]])
return "".join([r] + d)
now if the number is between 0 to -1000, the format will not disturb the user.
i.e
a = -600
b = -10000000
c = 700
d = 8000000
print(formatINR(a))
print(formatINR(b))
print(formatINR(c))
print(formatINR(d))
output will be:
-600
-1,00,00,000
700
80,00,000
Couldn't make the other two solutions work for me, so I made something a little more low-tech:
def format_as_indian(input):
input_list = list(str(input))
if len(input_list) <= 1:
formatted_input = input
else:
first_number = input_list.pop(0)
last_number = input_list.pop()
formatted_input = first_number + (
(''.join(l + ',' * (n % 2 == 1) for n, l in enumerate(reversed(input_list)))[::-1] + last_number)
)
if len(input_list) % 2 == 0:
formatted_input.lstrip(',')
return formatted_input
This doesn't work with decimals. If you need that, I would suggest saving the decimal portion into another variable and adding it back in at the end.
num=123456789
snum=str(num)
slen=len(snum)
result=''
if (slen-3)%2 !=0 :
snum='x'+snum
for i in range(0,slen-3,2):
result=result+snum[i:i+2]+','
result+=snum[slen-3:]
print(result.replace('x',''))
got a weird one i can not figure out how to solve
basically i have to run a section of code, this code extracts data from a file, which name is the format year-month-day-hour-00-00-consensus
so what i am trying to do is complete a loop that after the code runs adds an hour then once it gets to midnight adds a day etc, however while i have this working, i can not figure out how i can do this for the months as if it was easy as all months being 30 days for example this would be simple, i am having issues defining the length of days in the month, does any one have any ideas ?
this is the code so far :
def calculate_and_write_hsdir(h,d,m,y):
if h < 24:
if d < 10:
d = "0"+str(d)
if h < 10:
h = "0"+str(h)
if m < 10:
m = "0"+str(m)
consensus_file_name = str(y) + "-" + str(m) + "-" + str(d) + "-" + str(h) + "-00-00-consensus"
print consensus_file_name
..... do stuff ....
h = int(h) + 1
else:
h = 00
d = int(d) + 1
# d = d + 1
i pre set this by :
h = 00 #Hour
d = 01 #Day
m = 10 #Month
y = 2013 #Year
calculate_and_write_hsdir(h,d,m,y)
# def run_calculate(h,d,m,y):
# if m == 02:
# if d == 28:
# calculate_and_write_hsdir(h,d,m,y)
i want to start at 2013-10-01 and end at the present day, how can i achieve this ? sorry if it is a bit confusing but struggle on explaining what i want it to achieve
Python has a datetime module that will handle this for you. You make a datetime object representing the date and time, and a timedelta object to represent the addition of 1 hour. You can then, if you need to, check whether the new date is within the same day as the original date by comparing the .day property of the datetime object.
datetime also has a convenient method strftime for printing formatted output based on the date and time.
from datetime import datetime, timedelta
def calculate_and_write_hsdir(h,d,m,y):
before = datetime(hour = h, day = d, month = m, year = y)
now = before + timedelta(hours = 1)
if before.day == now.day:
print 'still the same day'
# ... do stuff ...
else:
print "now it's a different day"
# ... do other stuff ...
print now.strftime('%Y-%m-%d-%H-00-00-consensus')
h = 00 #Hour
d = 01 #Day
m = 10 #Month
y = 2013 #Year
calculate_and_write_hsdir(h, d, m, y)
22:11 + 22:22 = 44:33
varible_A = ('22:11')
varible_B = ('22:11')
the numbers on the left(22) are minutes
the numbers on the right(11) are seconds
I'm trying to add the two numbers to get
total = 44:22
This is a Bonus but would really help me out*
is it possible to treat the digits like time for instance...
varible_A = ('22:50')
varible_B = ('22:30')
I would like to get 45:20
instead of
44:80
Use datetime.timedelta() to model time durations:
from datetime import timedelta
def to_delta(value):
minutes, seconds = map(int, value.split(':'))
return timedelta(minutes=minutes, seconds=seconds)
var_a = to_delta('22:50')
var_b = to_delta('22:30')
var_a + var_b
You can then turn a timedelta() object back to a minutes + seconds representation:
def to_minutes_seconds(delta):
return '{:02.0f}:{:02.0f}'.format(*divmod(delta.total_seconds(), 60))
Demo:
>>> var_a = to_delta('22:50')
>>> var_b = to_delta('22:30')
>>> var_a + var_b
datetime.timedelta(0, 2720)
>>> to_minutes_seconds(var_a + var_b)
'45:20'
Alternatively, the str() result of a timedelta is formatted as HH:MM:SS:
>>> str(var_a + var_b)
'00:45:20'
and may suit your needs too. Note that for deltas that present more than one hour, there is a difference between str() and to_minutes_seconds(); the former shows you hours, minutes and seconds, the latter just shows minutes, where the minutes value can be over 60. Deltas representing more than 24 hours gain an extra prefix for the number of days:
>>> str(timedelta(minutes=65, seconds=10))
'1:05:10'
>>> to_minutes_seconds(timedelta(minutes=65, seconds=10))
'65:10'
>>> str(timedelta(minutes=(60*24)+1, seconds=10))
'1 day, 0:01:10'
I think these should be represented by objects, rather than one line comprehensions etc... As such my suggestion is to use a class like follows:
class Time(object):
def __init__(self,minutes,seconds):
self.minutes = minutes
self.seconds = seconds
def __add__(self,other):
return Time(self.minutes+other.minutes,self.seconds+other.seconds)
def __str__(self):
return "{0}:{1}".format(self.minutes,self.seconds)
A = Time(22,11)
B = Time(22,22)
print(A+B)
Produces
>>>
44:33
You can Use similar function
def add_with_column(*args):
res = (0,0)
for data in args:
nums = map(int, data.split(':'))
res = [i + j for i,j in zip(res, nums)]
return ':'.join([str(res[0] + res[1]/60), str(res[1] % 60)])
>>> add_with_column('22:50', '22:30')
'45:20'
a = '22:50'
b = '22:30'
def add_time(a, b):
a = map(int, a.split(':'))
b = map(int, b.split(':'))
a[1] += b[1]
a[0] += b[0] + a[1]//60
a[1] %= 60
return '{}:{}'.format(*a)
print a, b, add_time(a,b)
Something like this:
>>> def add_time(t1,t2):
h1,s1 = map(int,t1.split(":"))
h2,s2 = map(int,t2.split(":"))
q,r = divmod(s1+s2, 60)
return "{0}:{1:02d}".format( h1+h2+q, r)
...
>>> add_time('22:30','22:50')
'45:20'
>>> add_time('22:30','22:30')
'45:00'
A simple way to do this without importing any external machinery would be:
def add(time1,time2):
t1 = map(int,time1.split(':')) # t1[0] is minutes, t1[1] is seconds
t2 = map(int,time2.split(':')) # map will apply int() to both elements of the array
m = t1[0]+t2[0]+(t1[1]+t2[1])/60 # New minutes; recall that 70/60=1 for integer division
s = (t1[1]+t2[1]) % 60 #new seconds
return str(m)+":"+str(s)
add('22:30','22:50') # prints '45:20'
Of course, you can always import from datetime (which is probably better for practical applications), but if you want to implement time operations by yourself, this is how you can do so.
You want to split the times into their components
def split_time(time):
return time.split(':')
and add them
def add_times(time1, time2):
minutes = time1[0] + time2[0]
seconds = time1[1] + time2[1]
return minutes, seconds
and do modular arithmetic to "carry the minute" when appropriate
def carry_the_minute(minutes, seconds):
minutes += seconds / 60
seconds %= 60
return minutes, seconds
and create a representation of the result
def represent_time(minutes, seconds):
"""Specifies two-character-wide seconds with leading 0."""
return "{0}:{1:02d}".format(minutes, seconds)
Putting it all together will look something like this:
def add_times(time1, time2):
time1 = split_time(time1)
time2 = split_time(time2)
minutes, seconds = add_times(time1, time2)
minutes, seconds = carry_the_minute(minutes, seconds)
print represent_time(minutes, seconds)
I need to compile a program that can convert a Gregorian date to a Mayan one. I also need to use 01/01/1970 as a reference date.
The following auxiliary functions work accordingly and have no mistakes.
# turns dates into tuples
def dmj(date):
"""
>>> dmj('01/01/1970')
(1, 1, 1970)
>>> dmj('00012+00012+02012')
(12, 12, 2012)
"""
tup = ()
for i in date:
if i.isdigit() == False and i != ' ':
date = date.replace(i, ' ')
number_str = ''
for i in date:
number_str += i
if i == ' ':
number = int(number_str)
tup += (number,)
number_str = ''
tup += (int(number_str),)
return tup
# counts days that have passed since 01/01/1970
def daysPassed(date):
"""
>>> daysPassed('01/01/1970')
0
>>> daysPassed('20-7-1988')
6775
>>> daysPassed('00012+00012+02012')
15686
"""
from datetime import date
tup = dmj(date)
begin = date(1970, 1, 1)
end = date(tup[2], tup[1], tup[0])
passed = abs(end - begin)
return passed.days
My idea was to first calculate how many days have passed since the beginning of this pictun (20 baktuns long) in 01/01/1970 and then added the days passed since then according to the given date.
In the Mayan calendar a day is refered to as a kin. Their periods (within one pictun) are as follows:
20 kin = 1 uinal; 18 uinal = 1 tun; 20 tun = 1 katun; 20 katun = 1 baktun
In the long count notation the Mayan date for 01/01/1970 is '12.17.16.7.5'. Baktun are written first, then katuns, etc... Mayan dates start from 0. Basically the first kin of a uinal is number zero and the last one number 19, twenty in total.
I've first compiled the following:
def mayanDate(date, separation='')
"""
>>> mayanDate('01/01/1970')
'12/17/16/7/5'
>>> mayaDate('20-7-1988', separator='/')
'12/18/15/4/0'
>>> mayaDate('00012+00012+02012', separator='-')
'12-19-19-17-11'
>>> mayaDate('21 12 2012', separator='+')
'13+0+0+0+0'
>>> mayaDate('26.03.2407')
'14.0.0.0.0'
"""
days = daysPassed(date) + 13 * 144000 + 18 * 7200\
+ 17 * 400 + 8 * 20 + 6
baktun = str((days //((20 **3) * 18)) - 1)
days = days % ((20 **3) * 18)
katun = str((days //((20 **2) * 18)) - 1)
days = days % ((20 **2) * 18)
tun = str((days // (20 **2)) - 1)
days = days % (20 **2)
uinal = str((days // 20) - 1)
days = days % 20 - 1
kin = str(days)
mayanlist = [baktun, katun, tun, uinal, kin]
for i in date:
if i.isdigit() == False and separator == '':
separator = i
break
mayandate = separator.join(mayanlist)
return mayandate
For some strange reason only 01/01/1970 returns the correct Mayan long notation, despite counting from the beginning of an entire pictun (7,900 years in length!). For all other dates it seems to advance too fast through the calendar, despite my second auxiliary function returning the correct values (even for millennia into the future).
I wonder where's the mistake. For instance mayanDate('20-7-1988') returns '12-18-15-6-0' instead of '12-18-15-4-0' and mayanDate('21 12 2012') returns '13 0 1 12 0' instead of '13 0 0 0 0'.
The issue you're seeing with the negative 1 kin for date '15/01/1970' is due to the removal of one from every date ordinal during the calculation. Taking x%20 will always return a value between 0 and 19 inclusive. Taking one from the result necessarily shifts this range to -1 to 18 inclusive.
The number added to the result of daysPassed(date) seems to be a conversion of the long form of 1/1/1970, with one added to each digit. I'm assuming this has been done to counter the fact that the Mayan calendar starts counting at zero, but is unnecessary. The Mayan date 0.0.0.0.1.5 counts 25, not 151646. This doesn't appear to be the source of the error though, since removing this issue from my own code, I still get the same results as described for the 20-7-1988 and 21-12-2012.
I finally rooted out the error when I went back and switched out all the magic numbers in my code for named constants (it makes the code far easier to debug, read and maintain). You state there are 18 uinal in a tun, and 20 tun in a katun, but these numbers are reversed in the code.
Here's my code:
def mayanDate(date_str, seperation=','):
days_in_kin = 1
kin_in_uinal = 20
uinal_in_tun = 18
tun_in_katun = 20
katun_in_baktun = 20
days_in_uinal = days_in_kin * kin_in_uinal
days_in_tun = days_in_uinal * uinal_in_tun
days_in_katun = days_in_tun * tun_in_katun
days_in_baktun = days_in_katun * katun_in_baktun
days_1970 = 12 * days_in_baktun \
+ 17 * days_in_katun\
+ 16 * days_in_tun\
+ 7 * days_in_uinal\
+ 5 * days_in_kin
total_days = daysPassed(date_str) + days_1970
baktun = total_days // days_in_baktun
total_days -= baktun * days_in_baktun
katun = total_days // days_in_katun
total_days -= katun * days_in_katun
tun = total_days // days_in_tun
total_days -= tun * days_in_tun
uinal = total_days // days_in_uinal
total_days -= uinal * days_in_uinal
kin = total_days // days_in_kin
print seperation.join(map(str, (baktun, katun, tun, uinal, kin)))
(I subtracted the previous calculations from total days, rather than using a modulo operator, since I feel it's cleaner. I guess it's a matter of personal preference.)
I may have found something.
>>>mayanDate('15/01/1970')
'12/17/16/8/-1'
Obviously not possible. -1 has to be 19 here and 8 has to be 7. It seems to turn month too early. Still not out why 01/01/1970 remains correct here. No idea what's so special about that date.