why is Time.zone.parse so much slower than DateTime.parse? - ruby-on-rails-3

On my system(*), Time.zone#parse is almost 3.5x slower than DateTime#parse.
Since DateTime.parse already knows how to handle time zones, under what circumstances would you want to use Time.zone.parse?
Here's the benchmark file:
module ParseTimeBenchmark
extend self
N = 100000
TS_WO = "2010-01-01 12:03:04"
TS_W = "2010-01-01 12:03:04 -8:00"
def string_parser(s) ; s.to_datetime ; end
def datetime_parser(s) ; DateTime.parse(s) ; end
def time_zone_parser(s) ; Time.zone.parse(s) ; end
# sanity check
if ((s = string_parser(TS_W)) == datetime_parser(TS_W)) && (s == time_zone_parser(TS_W))
puts("datetimes compare equal")
else
puts("warning: datetimes do not compare equal")
end
Benchmark.bmbm do |x|
x.report("string_parser without zone") { N.times { string_parser(TS_WO) }}
x.report("string_parser with zone") { N.times { string_parser(TS_W) }}
x.report("datetime_parser without zone") { N.times { datetime_parser(TS_WO) }}
x.report("datetime_parser with zone") { N.times { datetime_parser(TS_W) }}
x.report("time_zone_parser without zone") { N.times { time_zone_parser(TS_WO) }}
x.report("time_zone_parser with zone") { N.times { time_zone_parser(TS_W) }}
end
end
and results
>> load '/Users/r/Developer/sketches/parse_time_benchmark.rb'
datetimes compare equal
Rehearsal -----------------------------------------------------------------
string_parser without zone 3.390000 0.030000 3.420000 ( 3.412754)
string_parser with zone 4.000000 0.020000 4.020000 ( 4.028964)
datetime_parser without zone 2.980000 0.020000 3.000000 ( 3.001507)
datetime_parser with zone 3.420000 0.020000 3.440000 ( 3.436074)
time_zone_parser without zone 9.050000 0.050000 9.100000 ( 9.101839)
time_zone_parser with zone 11.950000 0.050000 12.000000 ( 12.012977)
------------------------------------------------------- total: 34.980000sec
user system total real
string_parser without zone 3.470000 0.020000 3.490000 ( 3.495296)
string_parser with zone 4.070000 0.020000 4.090000 ( 4.099520)
datetime_parser without zone 3.000000 0.010000 3.010000 ( 3.030422)
datetime_parser with zone 3.460000 0.030000 3.490000 ( 3.491748)
time_zone_parser without zone 9.190000 0.060000 9.250000 ( 9.254589)
time_zone_parser with zone 11.960000 0.050000 12.010000 ( 12.015786)
=> true
(*) My system: Ruby 1.9.3 (x86_64-darwin10.8.0), Rails 3.2.2, Active Resource 3.2.2

Related

Using TCL to remove a variable

I'm tring to remove cerain usernames from a list if there count value is 0 so that only the users with a count of greater than 0 will appear. I've tried several methods without success so I'm hoping for some pointers in the right direction
if {![mysqlsel $db_handle "
SELECT `crew`.`user`,IF(`count`,`count`,0) AS count
FROM (SELECT DISTINCT `user` FROM `crew`) AS crew
LEFT JOIN (
SELECT `user`,COUNT(`user`) AS count
FROM `info`
WHERE `timestamp` >= UNIX_TIMESTAMP((LAST_DAY(NOW()) + INTERVAL 1 DAY) - INTERVAL 1 MONTH)
GROUP BY `user`) AS info
ON crew.user = info.user $refine
ORDER BY `count` DESC
"]} then {
putnow "PRIVMSG $chan_(kadd) :No results found\."
return 0
}
mysqlmap $db_handle {user count} {
if { $count >= $quota_(amount) || $user != "somename" } {
putnow "PRIVMSG $chan_(kadd) : \[\00314[num [incr place]]\003\] \00314$user\003 is \0033PASSiNG\003 \00314>\003 \00303$count mile(s) this month\003"
} else {
putnow "PRIVMSG $chan_(kadd) : \[\00314[num [incr place]]\003\] \00314$user\003 is \0034FAiLiNG\003 \00314>\003 \00304$count mile(s) this month\003"
}
}
While moving filtering into the query is the right approach here, you can also do it on the Tcl side since I'd bet very good money that mysqlmap follows the standard Tcl looping result code conventions:
mysqlmap $db_handle {user count} {
if {$count == 0} {
# Skip this one entirely
continue
}
if { $count >= $quota_(amount) || $user != "somename" } {
putnow "PRIVMSG $chan_(kadd) : \[\00314[num [incr place]]\003\] \00314$user\003 is \0033PASSiNG\003 \00314>\003 \00303$count mile(s) this month\003"
} else {
putnow "PRIVMSG $chan_(kadd) : \[\00314[num [incr place]]\003\] \00314$user\003 is \0034FAiLiNG\003 \00314>\003 \00304$count mile(s) this month\003"
}
}
Without any testing, here's how the query might get the filter added.
if {![mysqlsel $db_handle "
SELECT `crew`.`user`, `info`.`count` AS `count`
FROM (SELECT DISTINCT `user` FROM `crew`) AS `crew`
LEFT JOIN (
SELECT `user`, COUNT(`user`) AS `count`
FROM `info`
WHERE `timestamp` >= UNIX_TIMESTAMP((LAST_DAY(NOW()) + INTERVAL 1 DAY) - INTERVAL 1 MONTH)
GROUP BY `user`) AS `info`
ON `crew`.`user` = `info`.`user`
WHERE `count` > 0 -- <<< This line here <<<
$refine
ORDER BY `count` DESC
"]} then {
putnow "PRIVMSG $chan_(kadd) :No results found\."
return 0
}
You'll need to adjust the $refine clause generation to account for the fact that there's always a WHERE in front of it. (Also, you are aware that it's a potential SQL injection vector? Surfacing any form of complex querying to users is always potentially tricky as with great power comes great opportunity for abuse.)

Sum returns null in Rails

I am trying to subtract two value while querying the data from the database for creating a graph. but it always return null when i try the code
downtime = Ticket.by_month.where(:sla => true).sum("restore_time - start_time")
You have null values in restore_time or start_time. In SQL, null - X and X - null are null for all X as are null + X and X + null. That means that a null value in any of your restore_time or start_time columns will turn the entire sum into null.
You could ignore the rows with nulls:
Ticket.by_month
.where(:sla => true)
.where.not(:restore_time => nil)
.where.not(:start_time => nil)
.sum('restore_time - start_time')
or convert them to something that makes sense in your context:
Ticket.by_month
.where(:sla => true)
.sum('coalesce(restore_time, something_sensible) - coalesce(start_time, something_sensible)')
or perhaps:
Ticket.by_month
.where(:sla => true)
.sum('coalesce(restore_time - start_time, something_sensible)')
Where something_sensible would be what you want to use in place of nulls, this value would of course have to have the same type as the first argument to the coalesce calls.
How about this?
restore_time = Ticket.by_month.where(:sla => true).sum("restore_time")
start_time = Ticket.by_month.where(:sla => true).sum("start_time")
downtime = restore_time - start_time

Adding days to a date excluding weekends in Shopify

I'm trying to write some code to estimate a shipping date in Shopify. I've been learning liquid but am having trouble writing some logic in Ruby. I'm not sure if Shopify templates use Ruby or Ruby on Rails.
I want to get todays date and add either 3 or 5 days based on a input variable, excusing weekends. Heres how I'd do it in PHP:
$orderDate = date('Y-m-d');
$personalized = true;
$orderDays = ($personalized ? 4 : 2);
(date('H') > 12 ? $orderDays++ : $false);
$d = new DateTime( $orderDate );
$t = $d->getTimestamp();
// loop for X days
for($i=0; $i<$orderDays; $i++){
// add 1 day to timestamp
$addDay = 86400;
// get what day it is next day
$nextDay = date('w', ($t+$addDay));
// if it's Saturday or Sunday get $i-1
if($nextDay == 0 || $nextDay == 6) {
$i--;
}
// modify timestamp, add 1 day
$t = $t+$addDay;
}
$d->setTimestamp($t);
echo $d->format( 'Y-m-d' ). "\n";
Whats the best way to write this for Shopify liquid?
Use the date filter in Liquid.
The date format you are using in Ruby is equivalent to:
{{ "now" | date: "%Y, %m, %-d" }}
A day is 86400 seconds. Example to add 3 days from now and format:
{% assign days = 3 | times: 86400 %}
{{ "now" | date: "%s" | plus: days | date: "%Y, %m, %-d" }}
Note: You need the date: "%s" filter applied first to "now" for Liquid to understand that you are using the current timestamp and not the string "now".
A way to check if an order is made on a weekend would be:
{% assign wday = order.created_at | date: "%a" %}
{% if wday == 'Sat' or wday == 'Sun' %}
el Weekendo
{% endif %}

I want to disable cod when 100 cod orders is completed in prestashop

I want to disable the cod delivery feature in the order page of prestashop 1.6.3 when 100 /50 orders ( this will be a parameter) is completed per day.
How to disable this programmatically by finding out whether 100 cod is been completed .
I will modify the hookPayment() in cashondelivery module to do this :
public function hookPayment($params)
{
if (!$this->active)
return ;
global $smarty;
// Check if cart has product download
if ($this->hasProductDownload($params['cart']))
return false;
//Check whether the cod done exceeds the daily limit if yes dont display the cod option
$cod_limit = Configuration::get('PS_KITS_COD_DAILY_LIMIT');// number of cod
$sql = "select count(*) AS cod_count from ps_orders where module='cashondelivery' and date(date_add) = CURDATE() and ( current_state= 3 or current_state=4)";
if ($row = Db::getInstance()->getRow($sql)){
$cod_count = $row['cod_count'];
}
if ($cod_count >= $cod_limit){
return ;
}
$smarty->assign(array(
'this_path' => $this->_path, //keep for retro compat
'this_path_cod' => $this->_path,
'this_path_ssl' => Tools::getShopDomainSsl(true, true).__PS_BASE_URI__.'modules/'.$this->name.'/'
));
return $this->display(__FILE__, 'payment.tpl');
}

SQL to Linq group by count

I have a single column in a table to count specific rows. The sql query is as below:
SELECT
CASE
WHEN trail LIKE 'ClassA%' THEN 'ClassA'
WHEN trail LIKE 'ClassB%' THEN 'ClassB'
WHEN trail LIKE 'SemA%' THEN 'SemesterA'
WHEN trail LIKE 'SemB%' THEN 'SemesterB'
END AS Logs
, COUNT(*) AS Count
FROM Logs where s_date >= 'from date from UI' and e_date <= 'to date from ui'
GROUP BY
CASE
WHEN trail LIKE 'ClassA%' THEN 'ClassA'
WHEN trail LIKE 'ClassB%' THEN 'ClassB'
WHEN trail LIKE 'SemA%' THEN 'SemesterA'
WHEN trail LIKE 'SemB%' THEN 'SemesterB'
END
The above query result in sql fine as
ClassA 20
ClassB 5
SemesterA 2
SemesterB 50
Now, I need to change this sql to Linq with a date filter (from date, to date).
Please suggest change in query to simplyfy it.
Thanks
Tried:-
var data = _db.Logs.Where(p => p.trail.StartsWith("ClassA") && (p.SDate.Date >= CDate.Date && p.SDate.Date <= FDate.Date)).GroupBy(p => p.trail.StartsWith("ClassA")).Select(s =>
new
{
source = "Class - A total",
percentage = s.Count()
}).Union(_db.Logs.Where(p => p.trail.StartsWith("ClassB") && (p.SDate.Date >= CDate.Date && p.SDate.Date <= FDate.Date)).GroupBy(p => p.trail.StartsWith("ClassB")).Select(s =>
new
{
source = "Class - B total",
percentage = s.Count()
}).Union(_db.Logs.Where(p => p.trail.StartsWith("SemesterA") && (p.SDate.Date >= CDate.Date && p.SDate.Date <= FDate.Date)).GroupBy(p => p.trail.StartsWith("SemesterA")).Select(s =>
new
{
source = "Semester - A total",
percentage = s.Count()
}).Union(_db.Logs.Where(p => p.trail.StartsWith("SemesterB") && (p.SDate.Date >= CDate.Date && p.SDate.Date <= FDate.Date)).GroupBy(p => p.trail.StartsWith("SemesterB")).Select(s =>
new
{
source = "Semester - B total",
percentage = s.Count()
})))).ToList();
Try storing all the interesting starting keys in an enumerable of some sort and then using the built in group by method overload which outputs a result mapped from the key,group pairs (c.f. https://msdn.microsoft.com/en-us/library/vstudio/bb549393(v=vs.100).aspx)
string[] startingKeys = new string[] {"ClassA","ClassB","SemsterA","SemesterB"};
var data =_db.Logs.Where(p=>(p.SDate.Date >= CDate.Date && p.SDate.Date <= FDate.Date)&&startingKeys.Any(k=>p.Logs.StartsWith(k))).GroupBy(p=>startingKeys.Where(k=>p.Logs.StartsWith(k)).First(),(key,items)=>new {source=key,count = items.Count()})
One advantage of this method is you can change the starting keys at runtime if you feel like it.