Domanda

Come si eseguono effettivamente operazioni di data e ora come l'aggiunta di date, la ricerca di differenze, la scoperta di quanti giorni esclusi i fine settimana in un intervallo?Personalmente ho iniziato a passare alcune di queste operazioni al mio dbms postgresql poiché in genere avrei bisogno solo di emettere un'istruzione sql per ottenere una risposta, tuttavia, per farlo in modo PHP dovrei scrivere molto più codice che significa più possibilità che si verifichino errori...

Esistono librerie in PHP che eseguono operazioni di data e ora in un modo che non richiede molto codice?che batte SQL in una situazione in cui "Date due date, quanti giorni lavorativi ci sono tra le due date?"Implementare in SQL o $pet_lang' che viene risolto eseguendo questa query?

SELECT  COUNT(*) AS total_days
FROM    (SELECT date '2008-8-26' + generate_series(0,
          (date '2008-9-1' - date '2008-8-26')) AS all_days) AS calendar
WHERE   EXTRACT(isodow FROM all_days) < 6;
È stato utile?

Soluzione

L'oggetto DateTime di Php5+è utile perché è a conoscenza del tempo di salto e della luce del giorno, ma ha bisogno di una certa estensione per risolvere davvero il problema.Ho scritto quanto segue per risolvere un problema simile.Il metodo find_WeekdaysFromThisTo() è a forza bruta, ma funziona abbastanza rapidamente se l'intervallo di tempo è inferiore a 2 anni.

$tryme = new Extended_DateTime('2007-8-26');
$newer = new Extended_DateTime('2008-9-1');

print 'Weekdays From '.$tryme->format('Y-m-d').' To '.$newer->format('Y-m-d').': '.$tryme -> find_WeekdaysFromThisTo($newer) ."\n";
/*  Output:  Weekdays From 2007-08-26 To 2008-09-01: 265  */
print 'All Days From '.$tryme->format('Y-m-d').' To '.$newer->format('Y-m-d').': '.$tryme -> find_AllDaysFromThisTo($newer) ."\n";
/*  Output:  All Days From 2007-08-26 To 2008-09-01: 371   */
$timefrom = $tryme->find_TimeFromThisTo($newer);
print 'Between '.$tryme->format('Y-m-d').' and '.$newer->format('Y-m-d').' there are '.
      $timefrom['years'].' years, '.$timefrom['months'].' months, and '.$timefrom['days'].
      ' days.'."\n";
/*  Output: Between 2007-08-26 and 2008-09-01 there are 1 years, 0 months, and 5 days. */

class Extended_DateTime extends DateTime {

    public function find_TimeFromThisTo($newer) {
        $timefrom = array('years'=>0,'months'=>0,'days'=>0);

        // Clone because we're using modify(), which will destroy the object that was passed in by reference
        $testnewer = clone $newer;

        $timefrom['years'] = $this->find_YearsFromThisTo($testnewer);
        $mod = '-'.$timefrom['years'].' years';
        $testnewer -> modify($mod);

        $timefrom['months'] = $this->find_MonthsFromThisTo($testnewer);
        $mod = '-'.$timefrom['months'].' months';
        $testnewer -> modify($mod);

        $timefrom['days'] = $this->find_AllDaysFromThisTo($testnewer);
        return $timefrom;
    } // end function find_TimeFromThisTo


    public function find_YearsFromThisTo($newer) {
        /*
        If the passed is:
        not an object, not of class DateTime or one of its children,
        or not larger (after) $this
        return false
        */
        if (!is_object($newer) || !($newer instanceof DateTime) || $newer->format('U') < $this->format('U'))
            return FALSE;
        $count = 0;

        // Clone because we're using modify(), which will destroy the object that was passed in by reference
        $testnewer = clone $newer;

        $testnewer -> modify ('-1 year');
        while ( $this->format('U') < $testnewer->format('U')) {
            $count ++;
            $testnewer -> modify ('-1 year');
        }
        return $count;
    } // end function find_YearsFromThisTo


    public function find_MonthsFromThisTo($newer) {
        /*
        If the passed is:
        not an object, not of class DateTime or one of its children,
        or not larger (after) $this
        return false
        */
        if (!is_object($newer) || !($newer instanceof DateTime) || $newer->format('U') < $this->format('U'))
            return FALSE;

        $count = 0;
        // Clone because we're using modify(), which will destroy the object that was passed in by reference
        $testnewer = clone $newer;
        $testnewer -> modify ('-1 month');

        while ( $this->format('U') < $testnewer->format('U')) {
            $count ++;
            $testnewer -> modify ('-1 month');
        }
        return $count;
    } // end function find_MonthsFromThisTo


    public function find_AllDaysFromThisTo($newer) {
        /*
        If the passed is:
        not an object, not of class DateTime or one of its children,
        or not larger (after) $this
        return false
        */
        if (!is_object($newer) || !($newer instanceof DateTime) || $newer->format('U') < $this->format('U'))
            return FALSE;

        $count = 0;
        // Clone because we're using modify(), which will destroy the object that was passed in by reference
        $testnewer = clone $newer;
        $testnewer -> modify ('-1 day');

        while ( $this->format('U') < $testnewer->format('U')) {
            $count ++;
            $testnewer -> modify ('-1 day');
        }
        return $count;
    } // end function find_AllDaysFromThisTo


    public function find_WeekdaysFromThisTo($newer) {
        /*
        If the passed is:
        not an object, not of class DateTime or one of its children,
        or not larger (after) $this
        return false
        */
        if (!is_object($newer) || !($newer instanceof DateTime) || $newer->format('U') < $this->format('U'))
            return FALSE;

        $count = 0;

        // Clone because we're using modify(), which will destroy the object that was passed in by reference
        $testnewer = clone $newer;
        $testnewer -> modify ('-1 day');

        while ( $this->format('U') < $testnewer->format('U')) {
            // If the calculated day is not Sunday or Saturday, count this day
            if ($testnewer->format('w') != '0' && $testnewer->format('w') != '6')
                $count ++;
            $testnewer -> modify ('-1 day');
        }
        return $count;
    } // end function find_WeekdaysFromThisTo

    public function set_Day($newday) {
        if (is_int($newday) && $newday > 0 && $newday < 32 && checkdate($this->format('m'),$newday,$this->format('Y')))
            $this->setDate($this->format('Y'),$this->format('m'),$newday);
    } // end function set_Day


    public function set_Month($newmonth) {
        if (is_int($newmonth) && $newmonth > 0 && $newmonth < 13)
            $this->setDate($this->format('Y'),$newmonth,$this->format('d'));
    } // end function set_Month


    public function set_Year($newyear) {
        if (is_int($newyear) && $newyear > 0)
            $this->setDate($newyear,$this->format('m'),$this->format('d'));
    } // end function set_Year
} // end class Extended_DateTime

Altri suggerimenti

Mentre per la maggior parte delle operazioni datetime normalmente mi convertirei in Unixtime ed eseguirei addizioni, sottrazioni, ecc.sull'intero Unixtime, potresti voler guardare la classe Zend_Date del framework Zend.

Questo ha molte delle funzionalità che descrivi.Sebbene Zend sia classificato come un "framework", funziona eccezionalmente bene come libreria di classi da cui scegliere gli elementi.Lo includiamo regolarmente nei progetti e poi inseriamo semplicemente i pezzi come e quando ne abbiamo bisogno.

strtotime() è utile ma presenta alcuni comportamenti strani che possono apparire di tanto in tanto se non lo si utilizza solo per convertire una stringa di data/ora formattata.

cose come "+1 mese" o "-3 giorni" a volte non possono darti quello che ti aspetti che venga prodotto.

Per aggiungere una data, puoi utilizzare il metodo DateTime::aggiungi (Aggiunge un numero di giorni, mesi, anni, ore, minuti e secondi a un oggetto DateTime), disponibile da PHP 5.3.0 in poi.

Per trovare la differenza tra due date, c'è il Data e ora::diff metodo;ma non sembra esserci un metodo per contare i giorni lavorativi tra due date.

PEAR::Date sembra che potrebbe avere alcune funzionalità utili.

PEAR::Calendar potrebbe anche essere utile.

Il metodo più semplice consiste nell'utilizzare un timestamp, che rappresenta il numero di secondi trascorsi dal 1 gennaio 2008.Con un tipo di timestamp puoi fare cose come...

now = time();
tomorrow = now + 24 * 60 * 60; // 24 hours * 60 minutes * 60 seconds

Consulta la documentazione per tempo(), data() E mktime() sulle pagine web php.Questi sono i tre metodi che tendo a utilizzare più frequentemente.

Puoi usare una combinazione di strtotime, mktime E data fare i conti

Ecco un esempio che utilizza una combo per eseguire alcuni calcoli http://rushi.wordpress.com/2008/04/13/php-print-out-age-of-date-in-words/ Riprodurrò qui il codice per semplicità

if ($timestamp_diff < (60*60*24*7)) {
   echo floor($timestamp_diff/60/60/24)." Days";
} elseif ($timestamp_diff > (60*60*24*7*4)) {
   echo floor($timestamp_diff/60/60/24/7)." Weeks";
} else {
   $total_months = $months = floor($timestamp_diff/60/60/24/30);
   if($months >= 12) {
      $months = ($total_months % 12);
      $years&nbsp; = ($total_months - $months)/12;
      echo $years . " Years ";
   }
   if($months > 0)
      echo $months . " Months";
}
?>

@Rushi Personalmente non mi piace strtotime()..non so perché, ma stamattina ho scoperto che passare una stringa come questa '2008-09-11 9:5 AM' a strtotime restituisce un falso...

Non penso che il codice che hai fornito risolva il problema di esempio "Date due date, quanti giorni lavorativi ci sono tra le due date?"Implementa in SQL o $pet_lang' e non ho considerato se ho un elenco di giorni festivi...

Puoi ottenere il numero di giorni tra due date in questo modo:

$days = (strtotime("2008-09-10") - strtotime("2008-09-12")) / (60 * 60 * 24);

E puoi creare una funzione del genere (non ho php installato sul mio computer di lavoro, quindi non posso garantire che la sintassi sia corretta al 100%)

function isWorkDay($date)
{
 // check if workday and return true if so
}

function numberOfWorkDays($startdate, $enddate)
{
  $workdays = 0;
  $tmp = strtotime($startdate);
  $end = strtotime($enddate);
  while($tmp <= $end)
  {
    if ( isWorkDay( date("Y-m-d",$tmp) ) ) $workdays++;
    $tmp += 60*60*24;
  }
  return $workdays;
}

Se non ti piace strtotime e hai sempre la data nello stesso formato puoi usare la funzione di esplosione come

list($year, $month, day) = explode("-", $date);

Consiglio vivamente di utilizzare PHP 5.2 Oggetti DateTime, anziché utilizzare i timestamp UNIX, quando si eseguono calcoli sulle date.Quando usi le funzioni di data PHP che restituiscono timestamp UNIX, hai un intervallo molto limitato con cui lavorare (ad es.nulla prima del 1970).

Se dai un'occhiata http://php.net/date , troverai alcuni esempi di utilizzo mktime() per eseguire operazioni.

Un semplice esempio potrebbe essere quello di allenarsi su quale sarebbe la data di domani.Puoi farlo semplicemente aggiungendo 1 al valore del giorno mktime() come segue:

$tomorrow = date("Y-m-d", mktime(0, 0, 0, date("m"), date("d") + 1, date("Y")));

Quindi qui riceverai una data nel formato AAAA-MM-GG contenente la data di domani.Puoi anche sottrarre giorni semplicemente sostituendo "+" con "-". mktime() rende la vita molto più semplice e ti evita di dover fare istruzioni if ​​nidificate e altre codifiche fastidiose.

per ottenere giorni lavorativi/festivi, postgresql CTE ftw -- vedere http://osssmb.wordpress.com/2009/12/02/business-days-working-days-sql-for-postgres-2/

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top