Link to home
Start Free TrialLog in
Avatar of SniperCode Sheva
SniperCode Sheva

asked on

How to use embargo Date/time in php

Hello, I have a website, and in my website, I want to create a game each Monday for example, so before launching the game I want to put a countdown so that when it arrived at a certain time the game appears...
Avatar of Ray Paseur
Ray Paseur
Flag of United States of America image

In order to make sense of this application you need to understand the nature of HTTP requests and responses, and the correct ways of handling date and time in PHP.  The linked articles will give you the background information you need to assemble the information from the other question into an application that will hold an embargo on the "Monday Game" until the correct time arrives.

When the client makes a request for the Monday Game, the server will check the Date/Time value in the embargo record.  If the embargo has passed, the game will be presented.  If the embargo has not passed, the game will not be presented, and instead, a message telling the wait time will be presented.

If you want to "pretty this up" you might take the information from the DateInterval object $diff and send it to a client-side script in the form of a JSON string or similar.  The client-side script could start a simple client-side countdown timer.  There would be no need to recall the server-side script if the client-side timer is running.  When the (human) client refreshes the page, the countdown timer would naturally start over with the new interval and would continue until it expires.  When the countdown timer expires, the browser can be redirected to request this page again, and that request will present the "Monday Game" because the embargo date/time will have passed.  If the (human) client is away when the embargo expires, that's no problem - the next request to this page will present the "Monday Game."
https://iconoun.com/demo/temp_snipercode_embargo.php
<?php // demo/temp_snipercode_embargo.php
/**
 * https://www.experts-exchange.com/questions/28994254/How-to-use-embargo-Date-time-in-php.html
 * https://www.experts-exchange.com/questions/28993940/How-to-set-a-countdown-by-not-using-the-PC-time-in-PHP.html#a41954003
 *
 * https://www.experts-exchange.com/articles/20920/Handling-Time-and-Date-in-PHP-and-MySQL-OOP-Version.html
 */
error_reporting(E_ALL);


// SET THE EMBARGO DATE/TIME VALUE (THIS MIGHT BE STORED IN A DATABASE)
$embargo_text = '2017-01-11T13:00:00';
$embargo_date = new DateTime($embargo_text);

// GET THE CURRENT DATE/TIME
$current_date = new DateTime('NOW');


// HAS THE EMBARGO PASSED?
if ($current_date > $embargo_date)
{
    // YES, THE EMBARGO IS OVER
    echo PHP_EOL . "Here is the 'Monday Game' - enjoy!";
}
else
{
    // NOT, THE GAME IS STILL EMBARGOED - COMPUTE THE COUNTDOWN VALUES
    $diff = $embargo_date->diff($current_date);
    $countdown = NULL;
    if ($diff->d) $countdown .= "$diff->d day(s)";
    if ($diff->h) $countdown .= ", $diff->h hour(s)";
    if ($diff->i) $countdown .= ", $diff->i minute(s)";
    if ($diff->s) $countdown .= ", $diff->s second(s)";
    echo PHP_EOL . "Sorry, you must wait for $countdown <br>";

    $pattern = 'l, F j, Y \a\t g:ia';
    echo PHP_EOL . "The 'Monday Game' is embargoed until " . $embargo_date->format($pattern);
}

Open in new window

Avatar of SniperCode Sheva
SniperCode Sheva

ASKER

There is a problem here, it is the countdown. The countdown is not realtime when I launch the page. But the idea is here.
Is it possible to only put the time not the date.
Comparing only the time for example I want the game to be shown when 10:10 comes but before the  countdown is in realtime, I don't care what the date is.
thank you.
The countdown is not realtime when I launch the page
To the contrary (and this is why I posted the links to the articles -- the articles exist because these are common topics, and it's too much information to repeat for every individual question).  When you launch the page, your browser makes an HTTP request to the server, and the response is generated in as close to "real time" as is technologically feasible in modern networks.  Each time your browser makes an HTTP request to the server, the server will respond with its "real time" response, unless you've done something to incorporate caching, which would not really make sense for an application like this one.  You can click the "refresh" button on your browser and the page will be refreshed.  As you do that you will see that the response changes every time, as time marches forward toward the embargo date/time.

You can also use an AJAX request to the browser to reload the information from the server.  But you don't really need to do that more than once, because the timing information in the server and in the client browser are going to be highly consistent.  If you find that there is some divergence between the server and client times, you're either dealing with a huge load on the client (runaway JavaScript in another window) or a hardware malfunction.  IIRC in Julian's example, he showed how to repeat the HTTP request to the server so that you can get a new set of timing information to restart your JavaScript application that animates the countdown timer in the browser viewport.  You might try it both ways, and see if there is any value to recalling the server.  In any case, there will be very little "cost" to recalling the server.

To the issue of only time and not date, the answer is "yes, of course."  Your internal application on the server will use a DateTime object, but when you format the information for browser display, you will choose the formatting that shows exactly what you want to show to the client.  Please see Producing "Pretty" Dates From DateTime Objects, Computing the Difference Between DateTime Objects, and Time Without Date in this article:
https://www.experts-exchange.com/articles/20920/Handling-Time-and-Date-in-PHP-and-MySQL-OOP-Version.html
Avatar of Julian Hansen
Just for interest here is a variation on the code posted in the previous question that shows two timers both seeded from the server.
The first refreshes from the server every 15 seconds
The second simply decreases last time by 1000ms

You can run it to see if they two times deviate sufficiently to warrant a callback.
<div><span id="days"></span> days <span id="hours"></span> hours <span id="minutes"></span> minutes <span id="seconds"></span> seconds </div>
<div><span id="cddays"></span> days <span id="cdhours"></span> hours <span id="cdminutes"></span> minutes <span id="cdseconds"></span> seconds </div>

<script>
// Change to meet requirements
var endtime = getEndTime();
var currentTime = null;
var clockDown = false;
updateCurrentTime();
var seconds = 1;
setInterval(function() {
  if (seconds++ % 15 == 0) {
    updateCurrentTime();
  }
  else {
    currentTime += 1000;
  }
  clockDown += 1000;
  updateCounter();
}, 1000);

// Function for demo purposes - creates an end date in the future
// so each person who visits the page can see the code working
// without having to configure a hardcoded end date
function getEndTime()
{
  var future = (new Date()).getTime() + 86400000 + 3600000;
  var date = new Date(future);
  console.log(date.getYear());
  var utc = Date.UTC(date.getYear() + 1900, date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds());

  return utc;
}

function updateCounter()
{
  var diff = (endtime - currentTime) / 1000;
  var days = (diff >= 86400) ? parseInt(diff / 86400) : 0;
  diff = diff - days * 86400;
  var hours = parseInt(diff / 3600);
  diff = diff - hours * 3600;
  var minutes = parseInt(diff / 60);
  seconds = diff - minutes * 60;
  
  document.getElementById('days').innerHTML = days;
  document.getElementById('hours').innerHTML = hours;
  document.getElementById('minutes').innerHTML = minutes;
  document.getElementById('seconds').innerHTML = seconds;
  
  var diff = (endtime - clockDown) / 1000;
  var days = (diff >= 86400) ? parseInt(diff / 86400) : 0;
  diff = diff - days * 86400;
  var hours = parseInt(diff / 3600);
  diff = diff - hours * 3600;
  var minutes = parseInt(diff / 60);
  seconds = diff - minutes * 60;
  
  document.getElementById('cddays').innerHTML = days;
  document.getElementById('cdhours').innerHTML = hours;
  document.getElementById('cdminutes').innerHTML = minutes;
  document.getElementById('cdseconds').innerHTML = seconds;
}

function updateCurrentTime()
{
  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      currentTime = (new Date(this.responseText)).getTime();
	  if (!clockDown) {
		clockDown = currentTime;
	  }
    }
  };
  xhttp.open("GET", "t1991.getservertime.php", true);
  xhttp.send();  
}
</script>

Open in new window

Working sample here
I am seeing deviation of 1 second after 30 min continuous running .
@Julian: You're more patient than I am! ;-)
Deviation 1 second constant after roughly 17 hours - based on these results synch with server not necessary in this case.
You're more patient than I am! ;-)
I doubt that - I have seen your teaching examples and articles ...
... up to 4 seconds deviation after 15 hours.
Browser is working hard so that obviously having an effect.
Yes, there is a deviation also when the time comes to what it is expected, the countdown is coming back and not "the game"...
Not the sample above is not an embargo solution - it is merely a test I did to see how accurate setInterval is in keeping track with real time.

It needs to have a check for when currentTime <= 0 upon which it will refresh the page.

As Ray has outlined above - the responsibility lies with the server for deciding whether or not to show the embargoed page or the coutdown timer. The latter should only do the countdown and then the page refresh when counter = 0.

If you need a sample post back.
it would be good please. But there is something that I am trying to modify with the code that you gave me in the previous question is that i don't need to use the day, year I only need the hour, minute and seconds so I tried by putting instead of this
var endtime = Date.UTC(2017,0,12,11,03,00);

Open in new window

I put this
var endtime =new DateTime('10:10:00');

Open in new window

This is JavaScript
var endtime = Date.UTC(2017,0,12,11,03,00);

Open in new window

This is part JavaScript and part PHP, inappropriately mixed together
var endtime =new DateTime('10:10:00');

Open in new window

... i don't need to use the day, year I only need the hour, minute and seconds ...

That is not how it works.  You need to use the full Date/Time object.  This information is required for correct computation.  You only need to display the fragments that you want your clients to see.  Please refer back to the earlier answer:

To the issue of only time and not date, the answer is "yes, of course."  Your internal application on the server will use a DateTime object, but when you format the information for browser display, you will choose the formatting that shows exactly what you want to show to the client.  Please see Producing "Pretty" Dates From DateTime Objects, Computing the Difference Between DateTime Objects, and Time Without Date in this article:
https://www.experts-exchange.com/articles/20920/Handling-Time-and-Date-in-PHP-and-MySQL-OOP-Version.html

If you take an hour or two of concentrated study, to really get into that article, reading the text and experimenting with the code samples, you will have the underpinning of knowledge you need to work with Date/Time values in PHP.  Of course this assumes you have the time and you want to learn this stuff!  If you're not willing to put in the study time, I understand; time is money and everyone has an agenda.  In that case you should consider hiring a professional developer to do the work for you.  E-E Gigs offers an easy way to hire a professional for a short-term project.
You don't want to break from the UTC bit - it is necessary

Getting rid of the hour minutes and seconds is in the formatting part so you remove the following
  var diff = (endtime - currentTime) / 1000;
  // Don't account for days
  //var days = (diff >= 86400) ? parseInt(diff / 86400) : 0;
  //diff = diff - days * 86400;
  var hours = parseInt(diff / 3600);
  diff = diff - hours * 3600;
  var minutes = parseInt(diff / 60);
  seconds = diff - minutes * 60;
  
  // Take out the update of the days display
  //document.getElementById('days').innerHTML = days;
  document.getElementById('hours').innerHTML = hours;
  document.getElementById('minutes').innerHTML = minutes;
  document.getElementById('seconds').innerHTML = seconds;

Open in new window

We seem to have two threads running simultaneously.
I am reposting code from https://www.experts-exchange.com/questions/28993940/How-to-set-a-countdown-by-not-using-the-PC-time-in-PHP.html?anchorAnswerId=41956736#a41956736
Here is a working sample
Landing page
<?php
// THIS IS A SAMPLE WE MAY WANT TO DEMONSTRATE MANY TIMES
// FOR THIS REASON WE USE A SESSION TO INITIALISE THE TARGET TIME
// 30sec IN THE FUTURE
// REAL WORLD SOLUTIONS WOULD HAVE A FIXED target
session_start();
$target = isset($_SESSION['target']) ? $_SESSION['target'] : strtotime('now + 30 seconds');
$_SESSION['target'] = $target;

// IF THE COUNTDOWN HAS NOT EXPIRED THEN LOAD THE counter PAGE
// ELSE LOAD THE game PAGE
$page = $_SESSION['target'] >= time() ? 't1991.counter.php' : 't1991.game.php';
require_once($page);

Open in new window

Counter Page
<!doctype html>
<html>
<body>
<span id="hours"></span> hours <span id="minutes"></span> minutes <span id="seconds"></span> seconds 
<script>
// INITIALISE COUNTER WITH TARGET TIME
var endtime = <?php echo $target;?> * 1000;
var currentTime = 0;
updateCurrentTime();
var seconds = 1;
setInterval(function() {
  updateCounter();
  if (currentTime >= endtime) {
	  window.location.reload();
  }
  if (seconds++ % 15 == 0) {
    updateCurrentTime();
  }
  else {
    currentTime += 1000;
  }
}, 1000);

function updateCounter()
{
  var diff = (endtime - currentTime) / 1000;
  var hours = parseInt(diff / 3600);
  diff = diff - hours * 3600;
  var minutes = parseInt(diff / 60);
  seconds = diff - minutes * 60;
  
  document.getElementById('hours').innerHTML = hours;
  document.getElementById('minutes').innerHTML = minutes;
  document.getElementById('seconds').innerHTML = seconds;
}

function updateCurrentTime()
{
  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      currentTime = (new Date(this.responseText)).getTime();
    }
  };
  xhttp.open("GET", "t1991.getservertime.php", true);
  xhttp.send();  
}
</script>
</body>
</html>

Open in new window

Game Page
<!doctype html>
<html>
<body>
<h1>Welcome to the game page</h1>
<a href="t1991.reset.php">Reset</a>
</body>
</html>

Open in new window

Reset Page
<?php
session_start();
unset($_SESSION['target']);
session_destroy();
header('location: t1991c.php');

Open in new window

Server Time Page
<?php
echo gmdate("Y-m-d\TH:i:s\Z");

Open in new window

Working sample here
Here is the page in my website here
index.php
<?php
// THIS IS A SAMPLE WE MAY WANT TO DEMONSTRATE MANY TIMES
// FOR THIS REASON WE USE A SESSION TO INITIALISE THE TARGET TIME
// 30sec IN THE FUTURE
// REAL WORLD SOLUTIONS WOULD HAVE A FIXED target
session_start();
$target = isset($_SESSION['target']) ? $_SESSION['target'] : strtotime('now + 30 seconds');
$_SESSION['target'] = $target;

// IF THE COUNTDOWN HAS NOT EXPIRED THEN LOAD THE counter PAGE
// ELSE LOAD THE game PAGE
$page = $_SESSION['target'] >= time() ? 'counter.php' : 'game.php';
require_once($page);

Open in new window


counter.php
<!doctype html>
<html>
<body>
<span id="hours"></span> hours <span id="minutes"></span> minutes <span id="seconds"></span> seconds 
<script>
// INITIALISE COUNTER WITH TARGET TIME
var endtime = <?php echo $target;?> * 1000;
var currentTime = 0;
updateCurrentTime();
var seconds = 1;
setInterval(function() {
  updateCounter();
  if (currentTime >= endtime) {
	  window.location.reload();
  }
  if (seconds++ % 15 == 0) {
    updateCurrentTime();
  }
  else {
    currentTime += 1000;
  }
}, 1000);

function updateCounter()
{
  var diff = (endtime - currentTime) / 1000;
  var hours = parseInt(diff / 3600);
  diff = diff - hours * 3600;
  var minutes = parseInt(diff / 60);
  seconds = diff - minutes * 60;
  
  document.getElementById('hours').innerHTML = hours;
  document.getElementById('minutes').innerHTML = minutes;
  document.getElementById('seconds').innerHTML = seconds;
}

function updateCurrentTime()
{
  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      currentTime = (new Date(this.responseText)).getTime();
    }
  };
  xhttp.open("GET", "time.php", true);
  xhttp.send();  
}
</script>
</body>
</html>

Open in new window


game.php
<!doctype html>
<html>
<body>
<h1>Welcome to the game page</h1>
<a href="reset.php">Reset</a>
</body>
</html>

Open in new window

reset.php
<?php
session_start();
unset($_SESSION['target']);
session_destroy();
header('location: index.php');

Open in new window


time.php
<?php
echo gmdate("Y-m-d\TH:i:s\Z");

Open in new window

In reset.php your probably want to use session_write_close() instead of session_destroy().  It may not make the application work, but it's strategically better to be sure the session data is written before the redirection occurs.
https://www.experts-exchange.com/articles/11909/PHP-Sessions-Simpler-Than-You-May-Think.html
It looks like a caching issue on the time.php call

try changing your $get to

xhttp.open("GET", "time.php?" + seconds, true);

Open in new window


@Ray
Why not session_destroy() - the idea is to wipe it out completely so the next iteration starts fresh - did I miss a step?
$get from where ? which file ?
Not $get - the xmlhttprequest - was thinking in jQuery when I posted

xhttp.open("GET", "time.php?" + seconds, true);

Open in new window

i updated it , can you try , when I reset it again the same problem comes back...
... did I miss a step?
I understand the intent.  I think session_write_close() exists for the purpose of "checkpointing" the session data, and that is more likely to be what we want here.  The unset() is removing a specific data element from the session.  This is the element we want removed.  Destroying all of the session data is a bit ham-fisted and probably unnecessary.  This is the man page note from session_destroy(): You do not have to call session_destroy() from usual code. Cleanup $_SESSION array rather than destroying session data.

The reason I recommend session_write_close() is detailed in the article.  TL;DR session_write_close() does what we always assumed the script terminator would do for us - write and close the session.  I have found this necessary after the changes at PHP 5.6 in general procedural code.  I have also found this necessary when AJAX requests start one or more background scripts and the scripts interact through the session.  It forces a predictable, orderly sequence of events.
OIC what you mean. Yes in a general case session_destroy() might have side effects. In this case however I don't need the session for anything else so I am killing it but I see the logic in not putting that out there as a general solution.
Look at this code and tell me what do you think !
<body>
	<div style="visibility:hidden" id="count">
		<span id="hour"></span> hours <span id="minute"></span> minutes <span id="seconde"></span> seconds 
	</div>
	<div  style="visibility:hidden" id="game">
		<span>yes</span> 
	</div>
</body>
<script src="http://code.jquery.com/jquery-1.8.2.min.js" type="text/javascript"></script>

<script>
	
var	 jsondate;
	var  jsonhours;
	var jsonminutes;
var	 jsonhm;
var	 test;
	
function myCallback(json){
 jsondate= new Date(json.dateString);
 jsonhours = jsondate.getHours();
 jsonminutes = jsondate.getMinutes();
 jsonhm= jsonhours+''+jsonminutes;
	
	
}
	
	function startTimer(duration, hour,minute,seconde) {
    var timer = duration,hours, minutes, seconds;
    setInterval(function () {
        hours = parseInt(timer / 3600, 10)
        minutes = parseInt(timer / 60 % 60, 10)
        seconds = parseInt(timer % 60, 10);
        
        hours = hours < 10 ? "0" + hours : hours;

        minutes = minutes < 10 ? "0" + minutes : minutes;
        seconds = seconds < 10 ? "0" + seconds : seconds;

        hour.text(hours);
        minute.text(minutes);
        seconde.text(seconds);

        

        if (--timer < 0) {
			document.getElementById('game').style.visibility="visible";
document.getElementById('count').style.visibility="hidden";

        }
    }, 1000);
}

jQuery(function ($) {
    var tenhourstenminutes = 60 *60*10+(60*10);
        hour = $('#hour');
                minute = $('#minute');
        seconde = $('#seconde');
	alert(jsonhm);
	if(jsonhm >= 00  && jsonhm <= 1830)
	{
		document.getElementById('game').style.visibility="hidden";
document.getElementById('count').style.visibility="visible";
	 startTimer(tenhourstenminutes, hour,minute,seconde);
	}
	else
	{
	       document.getElementById('game').style.visibility="visible";
           document.getElementById('count').style.visibility="hidden";

	}
	

	
        
   
});
</script>
<script type="text/javascript" src="http://www.timeapi.org/utc/now.json?callback=myCallback"></script>

Open in new window

ASKER CERTIFIED SOLUTION
Avatar of Julian Hansen
Julian Hansen
Flag of South Africa image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Who closed this question?  Given that the first comment included a tested and working code sample, it seems like the point distribution is a little off-base.
Looks like the author closed it
Maybe so.  The email message I got didn't look the same as the other messages when an author closes a question, but E-E is always fiddling with its UX.  Wondering if E-E has new automation for its two-week stale questions?