4

What would be a good solution to round a timestamp to the hour?

For example, after using date('d.m.y h:m', time()), I get '02.08.11 7:07'... Now, from this, I'd like to have the timestamp for 7:00.

How can I achieve that?

luqita
  • 4,008
  • 13
  • 60
  • 93

4 Answers4

13

Update

After seeing Greg Miller's "Time Programming Fundamentals" talk about Google's cctz C++ library, I am dissatisfied with my previous answer.

Handling time is a very complex topic (even when discounting relativistic effects). We have to honor things like timezones with uneven offsets, daylight saving time, time jumps due to legislation changes or leap seconds and probably many more quirks. While many of these have only a local effect in time and space, users of our programs can still be unlucky enough to get affected.

TL;DR

It is probably best to rely on underlying functions like in holodoc's answer to handle the quirks of timezone management.

Old Answer

When I remember correctly the timestamp started at exactly 0:00 1.1.1970 so it should suffice to divide it by 3600 (floating) then round/floor/ceil and multiply by 3600 (integer). The division must be floating point for round/ceil to work.

Sample of rounding:

round($timestamp/3600)*3600
7:20 -> 7:00
7:40 -> 8:00

Sample of flooring:

floor($timestamp/3600)*3600
7:20 -> 7:00
7:40 -> 7:00

Sample of ceiling:

ceil($timestamp/3600)*3600
7:20 -> 8:00
7:40 -> 8:00
  • +1 Smart trick, but it requires knowledge about how the time in the timestamp is stored. I would blackbox this functionality (i.e. put it in a function :)), so this intelligence is not everywhere where you need to round hours. – GolezTrol Aug 02 '11 at 17:53
  • Of course this conversion should only be written once. Also it could be made more generic with passing the interval (snap to hours, 15 minutes ...) to the function. – Nobody moving away from SE Aug 02 '11 at 17:55
  • it's not working, do you mean something like: (round($timeHours / 3600) * 3600) ? – luqita Aug 02 '11 at 17:55
  • Look for the value within round. It can be that a integer division is performed there – Nobody moving away from SE Aug 02 '11 at 17:56
  • What does this have to do with when the timestamp starts? – Michael Mior Aug 02 '11 at 17:58
  • If it would have started within a half hour the arithmetic would be wrong. It would have to be shifted to a hour border. – Nobody moving away from SE Aug 02 '11 at 18:01
  • Is anyone **actually reading** what the OP requested in his original post and comments bellow it? The timestamps need to always be **ceiled** to minute zero of the hour. –  Aug 02 '11 at 18:16
  • @holodoc: well he seems to be not very clear about this. Rounding would also result in 00 for the minutes. We should let him make this more clear. Unfortunately I misread lolwut for luqita in the comments to your answer. – Nobody moving away from SE Aug 02 '11 at 18:19
  • Look at the second comment of his original post: "Just 00 every time... I just need a function that gets the 00 minute timestamp for any timestamp inputted." He clearly needs hour "ceiling" not rounding or "flooring". –  Aug 02 '11 at 18:24
  • @holodoc:I hope the last edits satisfy any need in this question. Voted you up for being the one to understand the OP right ;) – Nobody moving away from SE Aug 02 '11 at 18:29
  • 1
    @holodoc "I just need a function that gets the 00 minute timestamp for any timestamp inputted" sound more like 'flooring' to me. The example given in the question confirms that. Are you **actually reading** what the OP requested? Besides, the logic stays the same, you just need to replace round with trunc or and int-cast. – GolezTrol Aug 02 '11 at 18:29
  • Yep my bad... I meant flooring not ceiling :( The discussion got a bit distracting so no wonder I began throwing wrong terms :D Going to change that now. Obviously there is no way to edit the comments now :( –  Aug 02 '11 at 18:32
  • Well, this post now contains all three options to choose from. I do believe that calculating a new timestamp is better, safer and better performant than formatting the time and then converting it back to a timestamp. Still happy with my vote. :) – GolezTrol Aug 02 '11 at 18:34
  • Well I have nothing against different solutions just the fact that on stackoverflow its almost a daily routine to see a "bandwagon effect" every time someone posts a rather unclear question ;) I have nothing against this particular solution (it even got my vote :P) however from my experience its sometimes better to let the existing PHP functions to do the job especially when handling dates and time is in question. The difference in performance is negligible but most of the PHP functions account for things that can get in a way like time zones, DST etc. –  Aug 02 '11 at 18:53
  • @holodoc: Agreed. I only posted this solution because I found it might interest others as well. It has it limits, for example you cannot "round" to months or yours or any other distribution that is not equidistant. I also think that the performance hit is negligible, when it is seldom used but there can be situations when this is critical and I would find it interesting to see how much faster arithmetic’s is than string parsing. – Nobody moving away from SE Aug 02 '11 at 18:57
  • I needed to test it ^^ Its ~20x slower on my system. – Nobody moving away from SE Aug 02 '11 at 19:05
  • Why isn't it working for me? I am using it like this: echo date('d.m.y h:m', time()) . '-----'; $x = (round(time()/3600) * 3600); echo date('d.m.y h:m', $x) . "\n"; the second date shows just one hour more... no rounding! :( – luqita Aug 02 '11 at 19:42
  • OK got it, i was using m, what a noob! – luqita Aug 02 '11 at 19:44
  • @Nobody I don't get it. Which version is faster? The arithmetic one or with PHP functions? –  Aug 02 '11 at 19:59
  • @holodoc: The arithmetic one of course they are much more basic ^^ – Nobody moving away from SE Aug 02 '11 at 20:05
  • @holodoc: 10 mio. It was 1:26 against 0:04. Guess I should have chosen a bigger number but I did not want to wait longer. – Nobody moving away from SE Aug 02 '11 at 20:22
  • 10M iterations inside a single PHP script!? Sorry but if you ever come to as close as 100 000 iterations inside a single PHP script then there has to be something you are doing wrong :) I mean in my ten years of experience with PHP I never came to use more than 50K iterations in a single script and that was purely because of some calculations which had to be done on a large database :) BTW its natural to see that much of a difference in results because of the difference in number of functions used in both approaches. Every function call costs... Especially with 10M iterations ;) –  Aug 02 '11 at 20:47
2

Ever thought about removing the minutes? :)

echo date('d.m.y h:00', time());

If you need the timestamp of the beginning minute just alter your data argument a bit and use strtotime.

echo strtotime(date('d.m.Y H:00:00', time()));
  • What if the time was 7:32, it will need to be 8:00 not 7:00. – MacMac Aug 02 '11 at 17:49
  • That's not what he / she asks for in his further comments on the original post. –  Aug 02 '11 at 17:52
  • Very icky to format a timestamp and then parse it back to timestamp again. Better perform arithmics on the timestamp itself. – GolezTrol Aug 02 '11 at 18:31
  • Icky or not it removes the need to play with arithmetics when all the required functions are already part of the language. Not to mention the fact that further changes in requests are more easily implemented with the date argument which can easily be changed. –  Aug 02 '11 at 18:38
1

Capture the hour and the minute into 2 different variables. If the minutes is >= 30 then add one to the hour.

Not the most elegant solution, but it works. :)

JLZenor
  • 1,460
  • 2
  • 14
  • 23
1

Simplest solution:

date('d.m.y h', time());

It's very uncommon to see any sort of timestamps where the hour is rounded up. If the timestamp says 7:00 - it means it happened in the 7th hour, anytime. If you start rounding up timestamps people will interpret it as having occurred anytime in the 8th hour... which can lead to confusion.

Casey Flynn
  • 13,654
  • 23
  • 103
  • 194