cache_get() returns $cache objects even if the cached item is stale (expired). The cached data will not be rebuilt every hour in the following example:

* Builds complicated data for the monkey grip.
function custom_monkey_grip_data()

Instead, it’s cached data will persist in the cache system until Drupal cron runs and the cache is expired. Therefore, if cron runs hourly, it will take from one to two hours for the data to be rebuilt (assuming the data is requested very frequently, e.g. every minute). Or if cron never runs, it will only be rebuilt if the cache is cleared manually.

The correct way to make sure that the data will not be cached for more than one hour (without running cron more frequently, or implementing a minimum cache lifetime) is to check for time() > $cache->expire (or REQUEST_TIME > $cache->expire in Drupal 7). E.g.

$cache = cache_get('custom:monkey_grip');
// Also check if the cache is stale, since cache_get() does not.
if (!$cache or time() > $cache->expire) {
// Some expensive processing to build the data...
cache_set('custom:monkey_grip', $data, 'cache', $expire);
// ...

cache_get()‘s documentation fails to document this, so I’m not sure whether this is a bug in Drupal core, or just missing documentation. That remains to be debated over here in the Drupal issue queue. I did find that the issue also exists in Drupal 5, and Drupal 7 (if I correctly interpreted Drupal core’s new object-oriented cache layer).

I also found two uses of cache_get() in Drupal core that fail to check for time() > $cache->expire; One in check_markup(), and another in form_set/get_cache() (though these cases are not bugs). I checked all other uses of cache_get() in Drupal 6 core; None of them specify the $expire parameter in cache_set() relative to time(), so are not subject to this issue.