Προς το περιεχόμενο

PHP: boolean function που να επιστρέφει το αν είμαστε μεταξύ δύο ωρών της ημέρας (με timezone)


philos

Προτεινόμενες αναρτήσεις

Δημοσ. (επεξεργασμένο)

Εντόπισα στο stackoverflow αυτή τη function και τη τροποποίησα λίγο ώστε να δουλέψει όπως θέλω.

Όμως για κάποιο λόγο δεν λειτουργεί σωστά.

	protected function isBetweenTime($from, $to) 
	{
		$date = new \DateTime(null, new \DateTimeZone($this->timezone));	
		$current_time = $date->getTimestamp();
		$f = \DateTime::createFromFormat('!H:i', $from);
		$t = \DateTime::createFromFormat('!H:i', $to);
		$i = \DateTime::createFromFormat('!U', $current_time);
		if ($f > $t) $t->modify('+1 day');
		return ($f <= $i && $i <= $t) || ($f <= $i->modify('+1 day') && $i <= $t);
	}

Το $this->timezone έχει την ζώνη ώρας, πχ είναι "Europe/Athens".

// η ώρα τώρα είναι 12:30 το μεσημέρι. Το ακόλουθο δίνει όμως false
var_dump($this->isBetweenTime('10:30', '14:00'));

Καμιά ιδέα οι ειδικοί; :)

edit: έχω δοκιμάσει να κάνω και:

$current_time = $date->getTimestamp() + $date->getOffset();

αλλά χωρίς αποτέλεσμα.

Επεξ/σία από philos
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

  • Moderators
Δημοσ. (επεξεργασμένο)

Δοκίμασε να βγάλεις το ! από το format.

Δοκίμασε αυτό:

<?php
function isBetweenTime($from, $to) 
{
	$f = \DateTimeImmutable::createFromFormat('H:i', $from, new \DateTimeZone('Europe/Athens'));
	$t = \DateTimeImmutable::createFromFormat('H:i', $to, new \DateTimeZone('Europe/Athens'));
	$now = new \DateTimeImmutable('now', new \DateTimeZone('Europe/Athens'));

	return ($f <= $now && $now <= $t);
}
var_dump(isBetweenTime('10:30', '14:00'));

Δεν περνούσες το timezone μέσα στο to και from που έφτιαχνες.

Επεξ/σία από Kercyn
  • Thanks 1
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Λειτουργεί μια χαρά, ευχαριστώ πολύ! :)

45 λεπτά πριν, Kercyn είπε

Δοκίμασε να βγάλεις το ! από το format.

Δοκίμασε αυτό:


<?php
function isBetweenTime($from, $to) 
{
	$f = \DateTimeImmutable::createFromFormat('H:i', $from, new \DateTimeZone('Europe/Athens'));
	$t = \DateTimeImmutable::createFromFormat('H:i', $to, new \DateTimeZone('Europe/Athens'));
	$now = new \DateTimeImmutable('now', new \DateTimeZone('Europe/Athens'));

	return ($f <= $now && $now <= $t);
}
var_dump(isBetweenTime('10:30', '14:00'));

Δεν περνούσες το timezone μέσα στο to και from που έφτιαχνες.

Μια επιπλέον ερώτηση: η function που παρέθεσες, λειτουργεί σωστά σε περίπτωση αλλαγής ημέρας;

Για παράδειγμα εγώ στο τελικό script θέλω να εντοπίζω τη νύχτα: 22:00 έως 6:00 (το πρωί) της επόμενης.

Βλέπω ότι στο πρώτο post, η function από το stackoverflow έχει την εξής γραμμή:

if ($f > $t) $t->modify('+1 day');
return ($f <= $i && $i <= $t) || ($f <= $i->modify('+1 day') && $i <= $t);

Αν μπορείς να με βοηθήσεις σε αυτό θα χαιρόμουν πολύ, είναι και η τελική λειτουργία του script, απλά δε σκέφτηκα να την αναφέρω αρχικά! :)

 

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

  • Moderators

Για να είμαι ειλικρινής εγώ θα ήμουν πάρα πολύ προσεκτικός στο να θεωρήσω πράγματα που δεν ξέρω αν ισχύουν. Είσαι 100% σίγουρος ότι αν η εφαρμογή σου πάρει $from '22:00' και $to '6:00', θες το $to να αναφέρεται στην επόμενη μέρα; Αποκλείεται δηλαδή κάποιος να μπερδεύτηκε, ή για κάποιο άλλο λόγο να πέρασαν ανάποδα τα arguments; Αυτό που θέλω να πω, είναι ότι θες να πάρεις μια απόφαση με βάση μια πληροφορία που δεν έχεις explicitly, γι' αυτό και αυτός που έγραψε το script στο SO κάνει τις ταρζανιές με τους ελέγχους και την προσθήκη μιας μέρας.

Από τη στιγμή που η συνάρτησή σου δεν ελέγχει μόνη την ώρα αλλά και τη μέρα, θεωρώ πως θα έπρεπε να περάσεις και αυτήν την πληροφορία και να μη "θεωρείς" αυθαίρετα πράγματα.

Αν σου αρέσει να ζεις επικίνδυνα, θα μπορούσες να προσθέσεις αυτό. Προσθέτει μια ημέρα στο $to σου αν η ώρα στο $to είναι πριν την ώρα στο $from.

if ($f > $t) {
    $t = $t->add(new DateInterval('P1D'));
}

Υπ' όψην ότι δεν έχω φτιάξει tests για τίποτα απ' αυτά και απλώς τα έτρεξα 2-3 φορές να δω αν δουλεύουν, οπότε τελικά ίσως να είναι και λάθος.

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Ναι, είμαι σίγουρος. Ουσιαστικά θέλω να εμφανίζω κάτι στους χρήστες κατά τη βραδινή περίοδο, βασιζόμενος φυσικά και στο timezone τους. ;)

ok το έβαλα. Επειδή βαριέμαι να αλλάζω ώρα στο PC μου (localhost), θα το τεστάρω πραγματικά τη νύχτα.

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Δημοσ. (επεξεργασμένο)

@Kercyn δυστυχώς η αλλαγή της ημέρας δεν δούλεψε σωστά.

Γι' αυτό το λόγο, έστησα έτσι το function σπάζοντας τις ώρες σε δύο τμήματα (εφόσον θέλω να τρέχει 22:00 - 6:00):

	protected function isBetweenTime($from, $to) 
	{
		$f = \DateTimeImmutable::createFromFormat('H:i', $from, new \DateTimeZone($this->timezone));
		$t = \DateTimeImmutable::createFromFormat('H:i', $to, new \DateTimeZone($this->timezone));	
		$now = new \DateTimeImmutable('now', new \DateTimeZone($this->timezone));

		return ($f <= $now && $now <= $t);
	}
	
	public function test()
	{
		if ($this->isBetweenTime('22:00', '23:59')
		{
			return true;
		}
		
		if ($this->isBetweenTime('00:00', '6:00')
		{
			return true;
		}
		
		return false;
	}

Λειτουργεί σωστά με εξαίρεση κατά το λεπτό 00:00 ή κατά το λεπτό 23:59.

Μπορείτε μήπως να εντοπίσετε γιατί αυτό το μικρό σφάλμα;

Στις 10/4/2021 στις 2:12 ΜΜ, Kercyn είπε

Για να είμαι ειλικρινής εγώ θα ήμουν πάρα πολύ προσεκτικός στο να θεωρήσω πράγματα που δεν ξέρω αν ισχύουν. Είσαι 100% σίγουρος ότι αν η εφαρμογή σου πάρει $from '22:00' και $to '6:00', θες το $to να αναφέρεται στην επόμενη μέρα; Αποκλείεται δηλαδή κάποιος να μπερδεύτηκε, ή για κάποιο άλλο λόγο να πέρασαν ανάποδα τα arguments; Αυτό που θέλω να πω, είναι ότι θες να πάρεις μια απόφαση με βάση μια πληροφορία που δεν έχεις explicitly, γι' αυτό και αυτός που έγραψε το script στο SO κάνει τις ταρζανιές με τους ελέγχους και την προσθήκη μιας μέρας.

Από τη στιγμή που η συνάρτησή σου δεν ελέγχει μόνη την ώρα αλλά και τη μέρα, θεωρώ πως θα έπρεπε να περάσεις και αυτήν την πληροφορία και να μη "θεωρείς" αυθαίρετα πράγματα.

Αν σου αρέσει να ζεις επικίνδυνα, θα μπορούσες να προσθέσεις αυτό. Προσθέτει μια ημέρα στο $to σου αν η ώρα στο $to είναι πριν την ώρα στο $from.



if ($f > $t) {
    $t = $t->add(new DateInterval('P1D'));
}

Υπ' όψην ότι δεν έχω φτιάξει tests για τίποτα απ' αυτά και απλώς τα έτρεξα 2-3 φορές να δω αν δουλεύουν, οπότε τελικά ίσως να είναι και λάθος.

 

Επεξ/σία από philos
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Δημοσ. (επεξεργασμένο)

Αν το πρόβλημα σου είναι η αλλαγή της μέρας τότε το if μπορεί να είναι ανάποδο 

if (!$this->isBetweenTime('06:00', '22:00')

Εναλλακτικά μπορείς να δοκιμάσεις την between της Carbon. 

Υ.Γ. Προσωπικά σε κάτι παρόμοιο με έλεγχο για νυχτερινές ώρες και χρεώσεις παίζω με την Carbon και το ανάποδο if και παίζει μια χαρά!

Επεξ/σία από WebDevGr
  • Thanks 1
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Ευχαριστώ πολυ webdev!!

Προς το παρόν νομίζω βρήκα το bug στην υλοποίηση με τις δύο if. Πολυ πιθανόν το μέχρι 23:59 στο πρώτο if να γίνεται αντιληπτό ως 23:59 και 00 seconds οποτε το ένα λεπτό αυτό μένει ξεκρεμαστο μέχρι τις 00:00 που θα ξεκινήσει το δεύτερο if. Θα δοκιμάσω να βάλω μέχρι 23:59 και 59 seconds στο $to και λογικά θα δουλέψει ελπίζω!!

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Στις 12/4/2021 στις 5:41 ΠΜ, philos είπε

Λειτουργεί σωστά με εξαίρεση κατά το λεπτό 00:00 ή κατά το λεπτό 23:59.

Η ώρα 23:59 αντιστοιχεί στο τελευταίο λεπτό μιας ημέρας, ενώ η ώρα 00:00 στο πρώτο λεπτό μιας (ίσως άλλης) ημέρας.

Σε ποια περίπτωση "δεν λειτουργεί σωστά";

Στην περίπτωση της συνάρτησής σου, η κλήση

isBetweenTime('23:59', '00:00')
		

αφορά δυο διαφορετικές ημέρες.

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

@MastroGiannis ναι το καταλαβαίνω.

Απλά η κλήση μου δε θα έπρεπε να είναι η ακόλουθη για να καλύψω όλο το τελευταίο λεπτό της ημέρας;

if ($this->isBetweenTime('22:00:00', '23:59:59') // με 'H:i:s' στην isBetweenTime
//[...]
if ($this->isBetweenTime('00:00:00', '6:00:00')

Το λέω γιατί όπως είπα, η μέθοδος πριν επέστρεφε false κατά το λεπτό 23:59 εως 00:00 / δεν το κάλυπτε.

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

1 ώρα πριν, philos είπε

Το λέω γιατί όπως είπα, η μέθοδος πριν επέστρεφε false κατά το λεπτό 23:59 εως 00:00 / δεν το κάλυπτε.

Το 23:59 είναι η αρχή του τελευταίου λεπτού της ημέρας ενώ το 23:59:59 είναι η αρχή του τελευταίου δευτερολέπτου της. Χρόνος δεν είναι οι πάσσαλοι ενός φράχτη αλλά τα διαστήματά τους. Μάντεψε σε τι αντιστοιχούν τα διαστήματα πριν τον πρώτο πάσσαλο και μετά τον τελευταίο. Το τέλος μας ημέρας ταυτίζεται  με την αρχή της άλλης – νοητά. Όσα φέρνει η στιγμή δεν τα φέρνει ο χρόνος όλος. Είναι λίγο δυσνόητη η έννοια του Χρόνου (ως a priory έννοια) και μπορείς να πέσεις εύκολα στα παράδοξα του Ζήνωνα και τη φιλοσοφία εν γένει. Ευτυχώς οι υπολογιστές (για την ώρα) έχουν κι αυτοί τα υλικά τους όρια, αλλιώς…

Για να καλύψεις λοιπόν ΟΛΗ την τρέχουσα μέρα, μέχρι και το τέλος του χρόνου της, θα πρέπει το $to να ισούται τουλάχιστον με την αρχή της επόμενης, δηλαδή το σημείο 00:00. Στο σημείο αυτό, έχεις μηδέν ώρα αλλά μια ολόκληρη ημέρα: την προηγούμενη.

Ελπίζω να έγινα κατανοητός.

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε

Πρέπει να είστε μέλος για να αφήσετε σχόλιο

Δημιουργία λογαριασμού

Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!

Δημιουργία νέου λογαριασμού

Σύνδεση

Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.

Συνδεθείτε τώρα
  • Δημιουργία νέου...