Bruno
Bruno

Reputation: 521

In PHP, check if time is between sunset and sunrise

I've gone over multiple questions and answers here on SO, but I can't seem to figure it out. I am essentially trying to check if it is night at a certain location (lat/lng) at a certain time.

So far, I managed to get the sunrise and sunset times at the particular location and then I am trying to compare it with the current time. I have a bit of a Cinderella situation where it works fine until midnight strikes.

I am creating my variables for comparison like this:


    $currenttime = DateTime::createFromFormat('Y-m-d H:i', $templooptime);
    $sunsettime = DateTime::createFromFormat('Y-m-d H:i', $sunsetfinal);
    $sunrisetime = DateTime::createFromFormat('Y-m-d H:i', $sunrisefinal);

I am doing a simple IF statement like this:

    if ($currenttime>$sunsettime) {
       $nightminutes++;
    }

This outputs the following:

Current Time:2020-09-22 23:58:00| Location: 50.1776906598,-51.077228098126 | Sunset Start: 2020-09-22 18:48:00 |Sunrise Start: 2020-09-22 06:45:00 | Night:287
Current Time:2020-09-22 23:59:00| Location: 50.202093865458,-50.984342445684 | Sunset Start: 2020-09-22 18:47:00 |Sunrise Start: 2020-09-22 06:44:00 | Night:288
Current Time:2020-09-23 00:00:00| Location: 50.226422634856,-50.891362177619 | Sunset Start: 2020-09-23 18:45:00 |Sunrise Start: 2020-09-23 06:46:00 | Night:0
Current Time:2020-09-23 00:01:00| Location: 50.25067682914,-50.798287405434 | Sunset Start: 2020-09-23 18:44:00 |Sunrise Start: 2020-09-23 06:45:00 | Night:0

When I change the logic to:

if ($currenttime>$sunsettime && $currenttime<$sunrisetime) {
    $nightminutes++;
}

It outputs:

Current Time:2020-09-22 23:58:00| Location: 50.1776906598,-51.077228098126 | Sunset Start: 2020-09-22 18:48:00 |Sunrise Start: 2020-09-22 06:45:00 | Night:0
Current Time:2020-09-22 23:59:00| Location: 50.202093865458,-50.984342445684 | Sunset Start: 2020-09-22 18:47:00 |Sunrise Start: 2020-09-22 06:44:00 | Night:0
Current Time:2020-09-23 00:00:00| Location: 50.226422634856,-50.891362177619 | Sunset Start: 2020-09-23 18:45:00 |Sunrise Start: 2020-09-23 06:46:00 | Night:0
Current Time:2020-09-23 00:01:00| Location: 50.25067682914,-50.798287405434 | Sunset Start: 2020-09-23 18:44:00 |Sunrise Start: 2020-09-23 06:45:00 | Night:0

How can I solve this problem?

UPDATE Setting sunset time to one day ahead as per RO's answer, still has this issue:

Current Time:2020-09-22 23:59:00 | Location: 50.202093865458,-50.984342445684 | Sunset Start: 2020-09-22 18:47:00 | Sunrise Start: 2020-09-23 06:46:00 | Night:288
Current Time:2020-09-23 00:00:00 | Location: 50.226422634856,-50.891362177619 | Sunset Start: 2020-09-23 18:45:00 | Sunrise Start: 2020-09-24 06:47:00 | Night:0

When it crosses midnight, then the sunset time is LESS than current time so it shows false. Any ideas?

UPDATE 2 This is the basic code I am using (excerpts):

<?php
    $fulltimestampdateforloop = new DateTime('2020-09-22 23:58:00');
    $coords=["50.1776906598,-51.077228098126","50.202093865458,-50.984342445684","50.226422634856,-50.891362177619","50.25067682914,-50.798287405434"];
    $nightmin=0;
    $offset2=2;
    foreach ($coords as $value) {   
        $latlng = explode(",", $value);
        $lat = $latlng[0];
        $lng = $latlng[1];  
        $str_timestamp = $fulltimestampdateforloop->format('Y-m-d');
        $str_flighttime =$fulltimestampdateforloop->getTimestamp();
        
        $sunsetTime_a = (clone $fulltimestampdateforloop);
        $sunriseTime_a = (clone $fulltimestampdateforloop)->add(new DateInterval('P1D'));               
        
        $sunsetStr = date_sunset($sunsetTime_a->getTimestamp(), SUNFUNCS_RET_STRING, $lat, $lng, 90,$offset2);
        $sunriseStr = date_sunrise($sunriseTime_a->getTimestamp(), SUNFUNCS_RET_STRING, $lat, $lng, 90,$offset2);
        
        $ssettime = explode(":", $sunsetStr);
        $sunsetStr_h = $ssettime[0];
        $sunsetStr_m = $ssettime[1];                
        
        $srisetime = explode(":", $sunriseStr);
        $sunriseStr_h = $srisetime[0];
        $sunriseStr_m = $srisetime[1];

        $sunsetTime_e = (clone $fulltimestampdateforloop)->setTime($sunsetStr_h,$sunsetStr_m);
        $sunriseTime_e = (clone $fulltimestampdateforloop)->add(new DateInterval('P1D'))->setTime($sunriseStr_h,$sunriseStr_m);
        
        if ($fulltimestampdateforloop > $sunsetTime_e && $fulltimestampdateforloop < $sunriseTime_e) {
            $nightmin="Night";
        } else{
             $nightmin="Day";
        }
        echo 'Current Time:'.$fulltimestampdateforloop->format('Y-m-d H:i:s').' | Location: '.$lat.','.$lng.' | Sunset Start: '.$sunsetTime_e->format('Y-m-d H:i:s').' | Sunrise Start: '.$sunriseTime_e->format('Y-m-d H:i:s').' | Night or Day? :  '.$nightmin."<br>";
        $fulltimestampdateforloop->modify('+1 minutes');
    }
?>

Upvotes: 0

Views: 546

Answers (1)

Ro Achterberg
Ro Achterberg

Reputation: 2714

You've calculated your sunrise time to be before the sunset.

See this example for an illustration:

<?php

$today = new DateTime;
$randomAfternoonTime = (clone $today)->setTime(13, 25);
$randomNightTime = (clone $today)->add(new DateInterval('P1D'))->setTime(2, 23);

// Times relevant to Amsterdam on 2020-09-23.
// Sunset happens this evening.
$sunsetTime = (clone $today)->setTime(19, 36);
// Sunrise happens tomorrow, so add one day.
$sunriseTime = (clone $today)->add(new DateInterval('P1D'))->setTime(7, 30);

echo "Sunset at:\n" . $sunsetTime->format('Y-m-d H:i') . "\n\n";
echo "Sunrise at:\n" . $sunriseTime->format('Y-m-d H:i') . "\n\n";

echo "Some time in the afternoon (NOT in between sunset/sunrise):\n" . $randomAfternoonTime->format('Y-m-d H:i') . "\n\n";
echo "At night (in between sunset/sunrise):\n" . $randomNightTime->format('Y-m-d H:i') . "\n\n";

// Shows bool(false): 13:25 is not in between sunset/sunrise.
var_dump($randomAfternoonTime->getTimestamp() > $sunsetTime->getTimestamp() && $randomAfternoonTime->getTimestamp() < $sunriseTime->getTimestamp());

// Shows bool(true): 02:23 is in between sunset/sunrise.
var_dump($randomNightTime->getTimestamp() > $sunsetTime->getTimestamp() && $randomNightTime->getTimestamp() < $sunriseTime->getTimestamp());

See this 3v4l for a live demo: https://3v4l.org/lLUfL

UPDATE

I've taken the liberty of refactoring your code as shown below. I've purposely left in what I think is your logic bug. My code outputs the following:

Current Time: 2020-09-22 23:58:00 Location: 50.1776906598, -51.077228098126 Sunset Start: 2020-09-22 23:18:17 Sunrise Start: 2020-09-23 11:16:49 Night or day? : night

Current Time: 2020-09-22 23:59:00 Location: 50.202093865458, -50.984342445684 Sunset Start: 2020-09-22 23:17:55 Sunrise Start: 2020-09-23 11:16:27 Night or day? : night

Current Time: 2020-09-23 00:00:00 Location: 50.226422634856, -50.891362177619 Sunset Start: 2020-09-23 23:15:20 Sunrise Start: 2020-09-24 11:17:36 Night or day? : day

Current Time: 2020-09-23 00:01:00 Location: 50.25067682914, -50.798287405434 Sunset Start: 2020-09-23 23:14:57 Sunrise Start: 2020-09-24 11:17:14 Night or day? : day

Both 2020-09-23 00:00:00 and 2020-09-23 00:01:00 demonstrate the problem, where the comparison shouldn't be made on a UNIX timestamp, but on the time alone. Because both 00:01:00 and 00:00:00 are within the sunset/sunrise period ('night'), yet you're comparing full DateTimes. You're close. Let me know if you need any additional help.

Refactored code (with bug purposely left in it):

<?php

$referenceDateTime = new DateTime('2020-09-22 23:58:00');

$coordinates =
    [
        [ 50.1776906598, -51.077228098126 ],
        [ 50.202093865458, -50.984342445684 ],
        [ 50.226422634856, -50.891362177619 ],
        [ 50.25067682914, -50.798287405434 ]
    ];

foreach ($coordinates as $coordinate)
{
    [ $lat, $long ] = $coordinate;

    $sunsetDateTime = (clone $referenceDateTime)->setTimestamp(date_sunset($referenceDateTime->getTimestamp(), SUNFUNCS_RET_TIMESTAMP, $lat, $long, 90, 2));
    $sunriseDateTime = (clone $referenceDateTime)->setTimestamp(date_sunrise((clone $referenceDateTime)->add(new DateInterval('P1D'))->getTimestamp(), SUNFUNCS_RET_TIMESTAMP, $lat, $long, 90, 2));
    
    $daySegment = ($referenceDateTime > $sunsetDateTime && $referenceDateTime < $sunriseDateTime)?'night':'day';
    
    echo "Current Time: {$referenceDateTime->format('Y-m-d H:i:s')}\nLocation: $lat, $long\nSunset Start: {$sunsetDateTime->format('Y-m-d H:i:s')}\nSunrise Start: {$sunriseDateTime->format('Y-m-d H:i:s')}\nNight or day? : $daySegment\n\n";
    
    $referenceDateTime->add(new DateInterval('PT1M'));
}

UPDATE 2

Wanted to figure it out myself, so went along and wrote up the following that will help you think along the right lines:

<?php

$referenceHour = 22;
$referenceMinute = 31;

$sunsetHour = 23;
$sunsetMinute = 50;

$sunriseHour = 7;
$sunriseMinute = 30;

$referenceHour -= ($referenceHour < 12)?-12:12;
$sunsetHour -= ($sunsetHour < 12)?-12:12;
$sunriseHour -= ($sunriseHour < 12)?-12:12;

$referenceTime = ($referenceHour * 60) + $referenceMinute;
$sunsetTime = ($sunsetHour * 60) + $sunsetMinute;
$sunriseTime = ($sunriseHour * 60) + $sunriseMinute;

$daySegment = ($referenceTime >= $sunsetTime && $referenceTime <= $sunriseTime)?'night':'day';

echo "Reference time: " . sprintf('%02d:%02d', $referenceHour, $referenceMinute) . ", sunset time: " . sprintf('%02d:%02d', $sunsetHour, $sunsetMinute) . ", sunrise time: " . sprintf('%02d:%02d', $sunriseHour, $sunriseMinute) . ", night/day: $daySegment\n";

Upvotes: 2

Related Questions