Relative Zeitangaben. Wie kann ich wirklich nach TAGEN rechnen?

suntrop

Erfahrenes Mitglied
Hallo PHPler

Ich möchte den relativen Zeitversatz zum heutigen Tag ausgeben. Zum Beispiel: heute, gestern, morgen, am Sonntag, vor 12 Tagen, etc.

Ich habe jetzt schon viele Möglichkeiten versucht. Rechnen mit dem timestamp und den vergangenen Sekunden geht nicht. Ist es aktuell 00:05 Uhr und mein Vergleichszeitpunkt ist 10 Minuten früher, dann will ich "gestern" ausgeben. Also kann ich nicht mit den verstrichenen Sekunden arbeiten.

Mit einem DateTime-Objekt und diff() kann ich scheinbar auch nicht korrekt rechnen. Denn zum einen erhalte ich immer nur positive Werte (egal ob es vor 3 Tagen ist oder in 3 Tagen). Außerdem scheint diff() nicht in Tagen sondern auch in Zeitabständen zu rechnen. Gestern muss also mindestens 24 Stunden zurück liegen. Andernfalls ist es nicht gestern :-)

PHP:
$today = new DateTime();
$yesterday = new DateTime('-1 day');
$tomorrow = new DateTime('+1 day');
$due = new DateTime($date);
		
$span = $due->diff($today)->days;
	
if ($span == 0)
{
	return 'heute';
}
Ein Zeitpunkt vor 20 Stunden wird leider auch als "heute" ausgegeben.

Was kann ich bei der Misere mache? :-) Für heute, morgen, gestern kann ich noch einen direkten Datumsvergleich (Y-m-d) machen. Aber wie berechnen ich die tatsächlichen vergangenen Tage?

Sorry, ich glaube ich habe mich etwas umständlich ausgedrückt :-(
Hoffe dennoch auf Unterstützung. Danke und Grüße.
 
Also wenn ich dich richtig verstanden habe, willst so etwas wie Zeitzonen simulieren? Du brauchst doch nur von deinem aktuellen Timestamp, den Versatz subtrahieren, schon hast du deine neue Zeit.

PHP:
// Dein Timestamp
$timestamp = time();
// Der Versatz
$timeFactor = 60 * 60 * 24;
// Deine Zeit mit Versatz
$localTime = $timstamp - $timeFactor;
// Datumsformat
$timeFormat = DATE_COOKIE; // siehe http://www.php.net/date
// Zeit formatieren und ausgeben
echo date($imeFormat, $localTime);
 
Danke für deine Antwort. Die Zeitzone ist glaube ich bei der relative Zeitausgabe egal.
"Vor 3 Tagen" bleibt "vor 3 Tagen", egal ob in Berlin oder New York.

Mir geht es darum, dass ich nicht in ganzen Tagen rechnen kann.

Angenommen jetzt ist: 2011-12-09 20:00:00

Ich will den relativen Zeitversatz (Differenz in Tagen) zu folgenden Zeitpunkten:

2011-12-08 16:00:00
2011-12-08 22:00:00
2011-12-24 10:00:00

Würde ich jetzt die Differenz in Sekunden berechnen, dann liegt das zweite Datum nur 22 Stunden zurück. Also keinen ganzen Tag in Sekunden ausgedrückt. Dennoch ist es für jeden Mensch "gestern". Es lässt sich jedoch schwer oder nur umständlich berechnen.

Ich denke ich habe eine Lösung gefunden. Ich ziehe die Differenz aus allen Tage zu dem gewünschten Datum um 0 Uhr. Dann gibt es keine Rundungsfehler :-)

Vielleicht hat ja jemand ein besseren Vorschlag.
PHP:
function relative_date($date)
	{
		$due = strtotime($date);
		$now = time();
		setlocale (LC_ALL, 'de_DE');
		
		$today = date('Y-m-d', $now);
		$yesterday = strftime('%Y-%m-%d', strtotime('-1 day', $now));
		$tomorrow = strftime('%Y-%m-%d', strtotime('+1 day', $now));
		//echo $tomorrow;
		
		
		// Today at 00:00:00 o'clock
		$today_day = strftime('%d', time());
		$today_month = strftime('%m', time());
		$today_year = strftime('%Y', time());
		$pastdays = mktime(0, 0, 0, $today_month, $today_day, $today_year) / 86400;
		
		// The due date at 00:00:00 o'clock
		$due_day = strftime('%d', $due);
		$due_month = strftime('%m', $due);
		$due_year = strftime('%Y', $due);
		$newdue = mktime(0, 0, 0, $due_month, $due_day, $due_year);
		
		$daydiff = $pastdays - ($newdue / 86400);
		
		
		if (strftime('%Y-%m-%d', $due) == $today)
		{
			return 'heute';
		}
		if (strftime('%Y-%m-%d', $due) == $yesterday)
		{
			return 'gestern';
		}
		if (strftime('%Y-%m-%d', $due) == $tomorrow)
		{
			return 'morgen';
		}
		
		
		// Due is in the past
		if ($due < $now)
		{
			// Last 7 days
			if ($span < 604800)
			{
				return 'letzter ' . strftime('%A', $due) . ' <span class="light">('. $daydiff .' Tage überfällig)</span>';
			}
			else
			{
				return strftime('%d. %b', $due) . ' <span class="light">('. floor($span/86400) .' Tage überfällig)</span>';
			}
		}
		else // Due is in the future
		{
			$span = $span * -1;
			
			if ($span < 604800)
			{
				return strftime('%A', $due);
			}
			else
			{
				return strftime('%d. %b', $due) . ' <span class="light">(' . strftime('%V', $due) . '. KW, in '. floor($span/86400) .' Tage)</span>';
			}
		}

		return $date;
	}
 
Genauso muss man es machen, es gibt keine andere Lösung, vielleicht einen generalisierten Ansatz. Ist die DateTime-Klasse die aus der SPL (also PHP-Standard)? Dann habe ich eine schönere Lösung für dich:

PHP:
<?php
ini_set('display_errors', 1);
error_reporting(E_ALL|E_STRICT);
date_default_timezone_set('Europe/Berlin');

class CustomDateTime extends DateTime
{
	/**
	 * This function calculates the due of days to now from an specific date 
	 * 
	 * @param DateTime $fromDate The date to calculate the days
	 */
	public function dueDays(DateTime $fromDate)
	{
		// Get the current time stamp		
		$nowTimestamp = $this->getTimestamp();
		// Get the time stamp of the date and time to calculate the due days
		$fromDateTimestamp = $fromDate->getTimestamp();
		
		// Get the first hour of the date now
		$todayZero = $nowTimestamp - ($nowTimestamp % 86400);
		// Get the first hour of the date to calculate the due days
		$fromDayZero = $fromDateTimestamp - ($fromDateTimestamp % 86400);
		
		// Calculate the difference in days
		$diffDays = ($todayZero - $fromDayZero) / 86400;
		
		return abs($diffDays);
	}
}

$now = new CustomDateTime();
$yesterday = new CustomDateTime('-1day');
$specificDate = new CustomDateTime('2011-12-06 17:30');
$tomorrow = new CustomDateTime('+1day');
$nextWeek = new CustomDateTime('+1week');

echo $now->dueDays($yesterday) . "<br/><br/>";
echo $now->dueDays($specificDate) . "<br/><br/>";
echo $now->dueDays($tomorrow) . "<br/><br/>";
echo $now->dueDays($nextWeek) . "<br/><br/>";

EDIT: Habe den Code noch mal angepasst, es werden jetzt absolute Werte zurück gegeben, da man sonst bei "tomorrow" eine -1 zurück bekommt.
 
Zuletzt bearbeitet:
Ich hatte mal ein ähnliches Problem, hab das aber so gelöst, dass ich mit mktime() den Timestamp um 0:00 brerechnet habe und dann geguckt, ob ein timestamp dazwischen liegt. Zum Beispiel so:
Code:
<?php
$gestern = (mktime(0,0,0,date("m",time()),date("d",time()),date("Y",time()))  - 86400);
$heute = mktime(0,0,0,date("m",time()),date("d",time()),date("Y",time()));

if ($timestamp > $gestern AND $timestamp < $heute)
    echo "Gestern";

?>

Ist zwar recht umständlich, aber übersichtilich wie ich finde.
 
Danke euch beiden für die Antworten.
Die CustomDateTime-Klasse sieht gut aus. Etwas übersichtlicher als mein Konstrukt :-) Werde das wohl etwas angepasst übernehmen. Danke dafür!

@kourty
Dein Ansatz ist etwas ähnlich zu meinem. Übersichtlich ist es :) Auch dir besten Dank für den Code!

Grüße
suntrop
 
Wenn du eine zusätzliche Methode implementierst, kannst du auch prüfen, ob ein Datum älter ist als das andere:

PHP:
  /**
   * Checks whether the current date is earlier than the given date as parameter
   *
   * @param DateTime $checkDate The date to check against the current
   * @return boolean true if the current date is earlier, false if not
   */
  public function isEarlierThan(DateTime $checkDate)
  {
    return ($this->getTimestamp() < $checkDate->getTimestamp()) ? true : false;
  }

Im Anhang findest du die CustomDateTime-Klasse mit PHPUnit-Test.
 

Anhänge

Hallo,
so würde es auch funktionieren:
PHP:
<?php

$timeStamp = strtotime('2011-12-24 10:00:00');
$span = unixtojd($timeStamp) - unixtojd();

switch ($span) {
	case -1:
		$msg = 'gestern';
		break;
	case 0:
		$msg = 'heute';
		break;
	case 1:
		$msg = 'morgen';
		break;
	case ($span < -1):
		$msg = 'vor '.$span*(-1).' Tagen';
		break;
	default:
		$msg = 'in '.$span.' Tagen';		
}

echo $msg;

?>
 
Viele Wege führen nach Rom :-)
Die Funktion unixtojd() kannte ich nicht, aber damit ist es ja wirklich simpel eine Tagesdifferenz zu berechnen. Danke für den Tipp!
 
Zurück