2
\$\begingroup\$

I am working with dates and times which are not necessarily "on point", which means that while I need the times in fixed quarterly sections, I can't make sure that the saved timestamp isn't a little off.

I need xx:00, xx:15, xx:30 and xx:45 obviously, but the times could be yy:03, yy:14 and alike, you get the hang of it...

Normally I would use a switch statement to select what is about to happen, but in PHP switch does not work with intervals.

Currently, I am stuck with this code:

$temp_minute = date('i', $tdd->datum);

if( ( $temp_minute > 10 ) && ( $temp_minute < 20 ) ) { $minute = '15'; }
elseif( ( $temp_minute > 25 ) && ( $temp_minute < 35 ) ) { $minute = '30'; }
elseif( ( $temp_minute > 40 ) && ( $temp_minute < 50 ) ) { $minute = '45'; }
else { $minute = '00'; }

This seems to be far from elegant but gets the job done.

Are there any "nicer" ways around this, given the case that I might need to add more sections or am I bound to elseif statements?

\$\endgroup\$
  • 1
    \$\begingroup\$ I don't think your code works. What happens if $minutes is 37? I think your code will return 00. Please improve the code first. \$\endgroup\$ – KIKO Software Apr 3 '17 at 20:14
  • \$\begingroup\$ Actually it works in my boundaries. I am waiting for sensor data which is acquired every 15 minutes. and then pushed to a database. But reading and evaluating the sensor data can sometimes be faster or slower and therefore sometimes it´s a minute too late (bad internet) or too early (sensor reads faster than expected) but nothing like 10 minutes too late. \$\endgroup\$ – flomei Apr 3 '17 at 20:57
  • 1
    \$\begingroup\$ Most of the available laguages won't get you to turn that out toswitch/case statemets like that. There are certain extensions with gcc that go into that dircection. \$\endgroup\$ – πάντα ῥεῖ Apr 3 '17 at 21:01
4
\$\begingroup\$

You can do it 'mathematically' like this:

function fixMinutesToQuarter($minutes) {
  return sprintf('%02d',15*(round($minutes/15) % 4));
}

What does it do?

I tested it with this code:

for ($minutes = 0;$minutes < 60;$minutes++) {
  echo $minutes.' gives '.fixMinutesToQuarter($minutes).'<br>';
}

It returns:

0 gives 00       15 gives 15        30 gives 30       45 gives 45
1 gives 00       16 gives 15        31 gives 30       46 gives 45
2 gives 00       17 gives 15        32 gives 30       47 gives 45
3 gives 00       18 gives 15        33 gives 30       48 gives 45
4 gives 00       19 gives 15        34 gives 30       49 gives 45
5 gives 00       20 gives 15        35 gives 30       50 gives 45
6 gives 00       21 gives 15        36 gives 30       51 gives 45
7 gives 00       22 gives 15        37 gives 30       52 gives 45
8 gives 15       23 gives 30        38 gives 45       53 gives 00
9 gives 15       24 gives 30        39 gives 45       54 gives 00
10 gives 15      25 gives 30        40 gives 45       55 gives 00
11 gives 15      26 gives 30        41 gives 45       56 gives 00
12 gives 15      27 gives 30        42 gives 45       57 gives 00
13 gives 15      28 gives 30        43 gives 45       58 gives 00
14 gives 15      29 gives 30        44 gives 45       59 gives 00

In case you can have negative minutes, or minutes > 59 you should limit the range.

If you still prefer your 'if .... else' conditional approach you could now simplify it to:

if     ($temp_minute > 52) $minute = '00';
elseif ($temp_minute > 37) $minute = '45';
elseif ($temp_minute > 22) $minute = '30';
elseif ($temp_minute > 7)  $minute = '15';
else                       $minute = '00';

This can be further shortened by using the ternary operator, but it will then become quite unreadable, and not any faster, so I wouldn't do that.

Personally I prefer the 'mathematical' approach.

\$\endgroup\$
  • \$\begingroup\$ This is a great solution. I like the mathematical approach very much. :-) \$\endgroup\$ – flomei Apr 5 '17 at 8:11
2
\$\begingroup\$

Given your requirements, rounding seems the best solution. But if you want a switch version of your original code, consider

switch (true) {
    case ( $temp_minute > 10 && $temp_minute < 20 ):
        $minute = 15;
        break;
    case ( $temp_minute > 25 && $temp_minute < 35 ):
        $minute = 30;
        break;
    case ( $temp_minute > 40 && $temp_minute < 50 ):
        $minute = 45;
        break;
    default:
        $minute = 0;
}

Unlike other languages, PHP switch statements do not require constant cases. It's up to you if this is better or worse than a big if/else structure.

You can read more about this on Stack Overflow.

If you want something like the Java NavigableMap solution, consider

$time_intervals = [
    5  =>  0,
    20 => 15,
    35 => 30,
    50 => 45,
    60 =>  0,
];

foreach ($time_intervals as $upper_limit => $exact_value) {
    if ($temp_minute < $upper_limit) {
        $minute = $exact_value;
        break;
    }
}

If you do this a lot, you could make a function that returns the correct value and is general purpose.

$minute = value_by($time_intervals, $temp_minute);

function value_by($intervals, $value) {
    foreach ($intervals as $upper_limit => $exact_value) {
        if ($value < $upper_limit) {
            return $exact_value;
        }
    }
}

Then you just have to set up the intervals and call the function.

I don't currently have a PHP compiler setup, so I haven't tested it and may have missed the syntax slightly somewhere.

\$\endgroup\$
  • \$\begingroup\$ Modifying the switch in that way surely is a solution. I will have a closer look at the NavigableMap thing, seems to be useful once there are more intervals to check for. \$\endgroup\$ – flomei Apr 5 '17 at 8:27
1
\$\begingroup\$

Build all the arrays! :)

No, but seriously. Build an array of intervals/arrays and then test within the array for the interval you need. The final (or first - your decision) can be the default for 'in lieu of other options' which would be your 00 option.

The nice thing about this method is that it's easily extensible, and it could be parameterized. You can supply a $options array as a parameter where $options[$i][0] is the start $options[$i][1] is the end, and $options[$i][2] is the result.

On my phone so a code sample is currently out of the question, I'll try to remember to add one later.

\$\endgroup\$
  • \$\begingroup\$ Hm, building arrays seems overkill for this but might be a good solution once this little script needs to get more complicated. Thanks! \$\endgroup\$ – flomei Apr 5 '17 at 8:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.