Showing posts with label code. Show all posts
Showing posts with label code. Show all posts

Tuesday, January 08, 2008

Python: Gregorian date to Hijri date conversion and vice versa

Ported from Javascript. Credit goes to the original author of the Javascript (the script is quite widespread but I've never seen those using the script write the proper credit). Below is the code:

#!/usr/bin/env python

import math

def intPart(floatNum):
if floatNum < -0.0000001: return math.ceil(floatNum - 0.0000001)
return math.floor(floatNum + 0.0000001)

def Gregorian2Hijri(yr, mth, day):
if ((yr > 1582) or ((yr == 1582) and (mth > 10)) or \
((yr == 1582) and (mth == 10) and (day > 14))):
jd1 = intPart((1461 * (yr + 4800 + \
intPart
((mth - 14) / 12.0))) / 4)
jd2 = intPart((367 * (mth - 2 - 12 * \
(intPart((mth - 14) / 12.0)))) / 12)
jd3 = intPart((3 * (intPart((yr + 4900 + \
intPart((mth - 14) / 12.0)) / 100))) / 4)
jd = jd1 + jd2 - jd3 + day - 32075
else:
jd1 = intPart((7 * (yr + 5001 + \
intPart
((mth - 9) / 7.0))) / 4)
jd2 = intPart((275 * mth) / 9.0)
jd = 367 * yr - jd1 + jd2 + day + 1729777

l = jd - 1948440 + 10632
n = intPart((l - 1) /10631.0)
l = l - 10631 * n + 354
j1 = (intPart((10985 - l) / 5316.0)) * (intPart((50 * l) / 17719.0))
j2 = (intPart(l / 5670.0)) * (intPart((43 * l) / 15238.0))
j = j1 + j2
l1 = (intPart((30 - j) / 15.0)) * (intPart((17719 * j) / 50.0))
l2 = (intPart(j / 16.0)) * (intPart((15238 * j) / 43.0))
l = l - l1 - l2 + 29
m = intPart((24 * l) / 709.0)
d = l - intPart((709 * m) / 24.0)
y = 30 * n + j - 30

return y, m, d

def Hijri2Gregorian(yr, mth, day):
jd1 = intPart((11 * yr + 3) / 30.0)
jd2 = intPart((mth - 1) / 2.0)
jd = jd1 + 354 * yr + 30 * mth - jd2 + day + 1948440 - 385

if jd > 2299160:
l = jd + 68569
n = intPart((4 * l) / 146097.0)
l = l - intPart((146097 * n + 3) / 4.0)
i = intPart((4000 * (l + 1)) / 1461001.0)
l = l - intPart((1461 * i) / 4.0) + 31
j = intPart((80 * l) / 2447.0)
d = l - intPart((2447 * j) / 80.0)
l = intPart(j / 11.0)
m = j + 2 - 12 * l
y = 100 * (n - 49) + i + l
else:
j = jd + 1402
k = intPart((j - 1) / 1461.0)
l = j - 1461 * k
n = intPart((l - 1) / 365.0) - intPart(l / 1461.0)
i = l - 365 * n + 30
j = intPart((80 * i) / 2447.0)
d = i - intPart((2447 * j) / 80.0)
i = intPart(j / 11.0)
m = j + 2 - 12 * i
y = 4 * k + n + i - 4716

return y, m, d
Usage:
# Convert from Gregorian to Hijri
print Gregorian2Hijri(1972, 12, 9)
print Gregorian2Hijri(2008, 1, 8)

# Convert from Hijri to Gregorian
print Hijri2Gregorian(1392, 11, 3)
print Hijri2Gregorian(1428, 12, 29)

Wednesday, December 19, 2007

PHP: built-in function round() on 32-bit and 64-bit machines

My customer was complaining about a weird rounding problem recently. I do my development work primarily on a 32-bit machine, but the production server is a 64-bit machine. When I tested to run round(), a PHP built-in function, 32-bit machine and 64-bit machine produced different results. So here is my quick fix to properly round a floating number.

<?php

define
('ROUND_HALF_DOWN', 1);
define('ROUND_HALF_EVEN', 2);
define('ROUND_HALF_UP', 3);

/**
* Round floating point number because the built-in round() function produces
* different results on 32-bit and 64-bit machines
*
* @param float $value Floating poing value
* @param integer $prec Precision
* @param integer $rounding Rounding option
*/
function myround($value, $prec=2, $rounding=2) {
list(
$b, $f) = explode('.', (string) $value);
$b = (int) $b;
if ((
$prec - strlen($f)) > 0) {
$f *= pow(10, ($prec - strlen($f)));
}
if (
strlen($f) > $prec) {
$f1 = (int) substr($f, 0, $prec);
$f2 = (int) substr($f, $prec, 1);
$f3 = (int) substr($f, $prec-1, 1);
if (
$rounding === ROUND_HALF_DOWN ||
(
$rounding === ROUND_HALF_EVEN && (($f3 & 1) === 0))) {
$f = ($f2 >= 6) ? $f1 + 1 : $f1;
} elseif (
$rounding === ROUND_HALF_UP ||
(
$rounding === ROUND_HALF_EVEN && (($f3 & 1) === 1))) {
$f = ($f2 >= 5) ? $f1 + 1 : $f1;
}
if (
$f === pow(10, $prec)) {
++
$b;
$f = 0;
}
}
$f = sprintf("%0{$prec}d", $f);
return (float) ((string)
$b . '.' . (string) $f);
}

?>

Then some quick tests:
<?php
echo "1.35: " . myround(1.35, 2, ROUND_HALF_UP) . "\n";
echo "1.425: " . myround(1.90*0.75, 2, ROUND_HALF_UP) . "\n";
echo "1.425: " . myround(1.90*0.75, 3, ROUND_HALF_UP) . "\n";
echo "1.425: " . myround(1.90*0.75, 2, ROUND_HALF_EVEN) . "\n";
echo "1.995: " . myround(1.995, 2, ROUND_HALF_UP) . "\n";
echo "1.995: " . myround(1.995, 2, ROUND_HALF_EVEN) . "\n";
echo "1.995: " . myround(1.995, 2, ROUND_HALF_DOWN) . "\n";
echo "1.015: " . myround(1.015, 2, ROUND_HALF_UP) . "\n";
echo "1.015: " . myround(1.015, 2, ROUND_HALF_EVEN) . "\n";
echo "1.000: " . myround(1.00, 2, ROUND_HALF_UP) . "\n";
echo "4.20: " . myround(4.20, 2, ROUND_HALF_UP) . "\n";
?>