How to generate random date between two dates using php?
PHP has the rand() function:
$int= rand(1262055681,1262055681);
It also has mt_rand(), which is generally purported to have better randomness in the results:
$int= mt_rand(1262055681,1262055681);
To turn a timestamp into a string, you can use date(), ie:
$string = date("Y-m-d H:i:s",$int);
Getting random date between two dates
Use rand()
:
$random = Carbon::now()->subMinutes(rand(1, 55));
Generate random dates with random times between two dates for selected period and frequency
It's kind of crappy code but I think it will work as you wish.
function getDiffInSeconds(\DateTime $start, \DateTime $end) : int
{
$startTimestamp = $start->getTimestamp();
$endTimestamp = $end->getTimestamp();
return $endTimestamp - $startTimestamp;
}
function getShiftData(\DateTime $start, \DateTime $end) : array
{
$shiftStartHour = \DateTime::createFromFormat('H:i:s', $start->format('H:i:s'));
$shiftEndHour = \DateTime::createFromFormat('H:i:s', $end->format('H:i:s'));
$shiftInSeconds = intval($shiftEndHour->getTimestamp() - $shiftStartHour->getTimestamp());
return [
$shiftStartHour,
$shiftEndHour,
$shiftInSeconds,
];
}
function dayIsWeekendOrHoliday(\DateTime $date, array $holidays = []) : bool
{
$weekendDayIndexes = [
0 => 'Sunday',
6 => 'Saturday',
];
$dayOfWeek = $date->format('w');
if (empty($holidays)) {
$dayIsWeekendOrHoliday = isset($weekendDayIndexes[$dayOfWeek]);
} else {
$dayMonthDate = $date->format('d/m');
$dayMonthYearDate = $date->format('d/m/Y');
$dayIsWeekendOrHoliday = (isset($weekendDayIndexes[$dayOfWeek]) || isset($holidays[$dayMonthDate]) || isset($holidays[$dayMonthYearDate]));
}
return $dayIsWeekendOrHoliday;
}
function getScheduleDates(\DateTime $start, \DateTime $end, int $frequencyInSeconds) : array
{
if ($frequencyInSeconds < (24 * 60 * 60)) {
throw new \InvalidArgumentException('Frequency must be bigger than one day');
}
$diffInSeconds = getDiffInSeconds($start, $end);
// If difference between $start and $end is bigger than two days
if ($diffInSeconds > (2 * 24 * 60 * 60)) {
// If difference is bigger than 2 days we add 1 day to start and subtract 1 day from end
$start->modify('+1 day');
$end->modify('-1 day');
// Getting new $diffInSeconds after $start and $end changes
$diffInSeconds = getDiffInSeconds($start, $end);
}
if ($frequencyInSeconds > $diffInSeconds) {
throw new \InvalidArgumentException('Frequency is bigger than difference between dates');
}
$holidays = [
'01/01' => 'New Year',
'18/04/2020' => 'Easter 1st official holiday because 19/04/2020',
'20/04/2020' => 'Easter',
'21/04/2020' => 'Easter 2nd day',
'27/04' => 'Konings',
'04/05' => '4mei',
'05/05' => '4mei',
'24/12' => 'Christmas 1st day',
'25/12' => 'Christmas 2nd day',
'26/12' => 'Christmas 3nd day',
'27/12' => 'Christmas 3rd day',
'31/12' => 'Old Year'
];
[$shiftStartHour, $shiftEndHour, $shiftInSeconds] = getShiftData($start, $end);
$amountOfNotifications = floor($diffInSeconds / $frequencyInSeconds);
$periodInSeconds = intval($diffInSeconds / $amountOfNotifications);
$maxDaysBetweenNotifications = intval($periodInSeconds / (24 * 60 * 60));
// If $maxDaysBetweenNotifications is equals to 1 then we have to change $periodInSeconds to amount of seconds for one day
if ($maxDaysBetweenNotifications === 1) {
$periodInSeconds = (24 * 60 * 60);
}
$dates = [];
for ($i = 0; $i < $amountOfNotifications; $i++) {
$periodStart = clone $start;
$periodStart->setTimestamp($start->getTimestamp() + ($i * $periodInSeconds));
$seconds = mt_rand(0, $shiftInSeconds);
// If $maxDaysBetweenNotifications is equals to 1 then we have to check only one day without loop through the dates
if ($maxDaysBetweenNotifications === 1) {
$interval = new \DateInterval('P' . $maxDaysBetweenNotifications . 'DT' . $seconds . 'S');
$date = clone $periodStart;
$date->add($interval);
$dayIsWeekendOrHoliday = dayIsWeekendOrHoliday($date, $holidays);
} else {
// When $maxDaysBetweenNotifications we have to loop through the dates to pick them
$loopsCount = 0;
$maxLoops = 3; // Max loops before breaking and skipping the period
do {
$day = mt_rand(0, $maxDaysBetweenNotifications);
$periodStart->modify($shiftStartHour);
$interval = new \DateInterval('P' . $day . 'DT' . $seconds . 'S');
$date = clone $periodStart;
$date->add($interval);
$dayIsWeekendOrHoliday = dayIsWeekendOrHoliday($date, $holidays);
// If the day is weekend or holiday then we have to increment $loopsCount by 1 for each loop
if ($dayIsWeekendOrHoliday === true) {
$loopsCount++;
// If $loopsCount is equals to $maxLoops then we have to break the loop
if ($loopsCount === $maxLoops) {
break;
}
}
} while ($dayIsWeekendOrHoliday);
}
// Adds the date to $dates only if the day is not a weekend day and holiday
if ($dayIsWeekendOrHoliday === false) {
$dates[] = $date;
}
}
return $dates;
}
$start = new \DateTime('2020-12-30 08:00:00', new \DateTimeZone('Europe/Sofia'));
$end = new \DateTime('2021-01-18 17:00:00', new \DateTimeZone('Europe/Sofia'));
$frequencyInSeconds = 86400; // 1 day
$dates = getScheduleDates($start, $end, $frequencyInSeconds);
var_dump($dates);
You have to pass $start
, $end
and $frequencyInSeconds
as I showed in example and then you will get your random dates. Notice that I $start
and $end
must have hours in them because they are used as start and end hours for shifts. Because the rule is to return a date within a shift time only in working days. Also you have to provide frequency in seconds - you can calculate them outside the function or you can change it to calculate them inside. I did it this way because I don't know what are your predefined periods.
This function returns an array of \DateTime() instances so you can do whatever you want with them.
UPDATE 08/01/2020:
Holidays now are part of calculation and they will be excluded from returned dates if they are passed when you are calling the function. You can pass them in d/m
and d/m/Y
formats because of holidays like Easter and in case when the holiday is on weekend but people will get additional dayoff during the working week.
UPDATE 13/01/2020:
I've made updated code version to fix the issue with infinite loops when $frequencyInSeconds
is shorter like 1 day. The new code used few functions getDiffInSeconds
, getShiftData
and dayIsWeekendOrHoliday
as helper methods to reduce code duplication and cleaner and more readable code
PHP to generate random date
You could do it with mt_rand() like this:
echo date('Y-m-d')." ".mt_rand(0, 23).":".mt_rand(0, 59).":".mt_rand(0, 59);
Random time and date between 2 date values
Easy :) Just choose 2 random dates, convert to EPOCH, and random between these 2 values :)
EPOCH - The time since 1/1/1970, in seconds.
You can use the strtotime()
function to make date-strings turn into epoch time, and the date()
function to make it the other way back.
function rand_date($min_date, $max_date) {
/* Gets 2 dates as string, earlier and later date.
Returns date in between them.
*/
$min_epoch = strtotime($min_date);
$max_epoch = strtotime($max_date);
$rand_epoch = rand($min_epoch, $max_epoch);
return date('Y-m-d H:i:s', $rand_epoch);
}
PHP random date and multiple dates
Simply add random number of hours to your string for strtotime
, can do same for minutes
With Hours
<?php
echo date('Y-m-d H:i', strtotime( '+'.mt_rand(0,31).' days '.mt_rand(9,17). 'Hours' ));
?>
With Hours and Minutes
<?php
echo date('Y-m-d H:i', strtotime( '+'.mt_rand(0,31).' days '.mt_rand(9,17). 'Hours '.mt_rand(0,30). 'Minutes' ));
?>
Get random day, month, year, day of the week
Lets hold this short, and here's a link with kinda same question
with mt_rand you can generate random timestamps between those two
use Unix Timestamps for that
then convert that timestamp with the date function for either Day, Month, Year etc.
here's an example:
$int= mt_rand(1232055681,1262055681);
$string = date("Y-m-d H:i:s",$int);
echo "<p>$string</p><br>";
hope this helps and welcome! :)
Generate 50 random-duration time intervals in MySQL or PHP, between two dates
Yes, you can do this in MySQL. The general algorithm for N intervals is:
- Generate N-1 distinct, random timestamps between your begin time + 1 second and your end time - 2 seconds.
- Sort them, oldest to youngest. These are your interval begin points.
- Subtract one second to from each end point to get the prior interval's start point.
Example
Install these generator views, then:
CREATE TABLE times (id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,begin_time DOUBLE NOT NULL,end_time DOUBLE NOT NULL DEFAULT 0);
INSERT INTO times (begin_time) SELECT @low bp UNION SELECT s.bp FROM (SELECT t.bp FROM (SELECT @low + FLOOR(RAND() * (@high-@low)) bp FROM generator_256 JOIN (SELECT @low := UNIX_TIMESTAMP('2014-09-01 00:00:00'), @high := UNIX_TIMESTAMP('2014-10-12 23:59:59')) init LIMIT 49) t ORDER by bp) s;
UPDATE times JOIN (SELECT curr.id, curr.begin_time, (SELECT next.begin_time-1 FROM times next WHERE next.id=curr.id+1) end_time FROM times curr) g ON g.id = times.id SET times.end_time = COALESCE(g.end_time, UNIX_TIMESTAMP('2014-10-12 23:59:59'));
SELECT FROM_UNIXTIME(begin_time), FROM_UNIXTIME(end_time) FROM times;
+---------------------------+-------------------------+
| FROM_UNIXTIME(begin_time) | FROM_UNIXTIME(end_time) |
+---------------------------+-------------------------+
| 2014-09-01 00:00:00 | 2014-09-02 13:32:45 |
| 2014-09-02 13:32:46 | 2014-09-03 07:57:24 |
| 2014-09-03 07:57:25 | 2014-09-04 17:34:01 |
| 2014-09-04 17:34:02 | 2014-09-04 19:46:25 |
| 2014-09-04 19:46:26 | 2014-09-05 17:44:48 |
...
| 2014-10-10 18:39:47 | 2014-10-11 05:11:13 |
| 2014-10-11 05:11:14 | 2014-10-11 11:27:29 |
| 2014-10-11 11:27:30 | 2014-10-12 13:03:02 |
| 2014-10-12 13:03:03 | 2014-10-12 17:55:54 |
| 2014-10-12 17:55:55 | 2014-10-12 19:11:11 |
| 2014-10-12 19:11:12 | 2014-10-12 23:59:59 |
+---------------------------+-------------------------+
50 rows in set (0.00 sec)
Explanation
Let's break these down, step by step. To generate rows in MySQL, you have to use a generator view. A generator view just gives you N rows each time you ask for it. As an example, to get 49 rows (N-1):
SELECT * FROM generator_256 LIMIT 49;
To generate a single random number between two other numbers in MySQL, use the random number formula low + (RAND() * (high-low))
. This formula combined with the generator view gets us the 49 begin points we want for step 1:
SELECT (@low + FLOOR(RAND() * (@high-@low))) AS bp FROM generator_256 LIMIT 49;
(I'm using session variables here to keep the SQL simple. They'll become part of the query in a just a bit. If you want to debug, remember bp is a timestamp, so FROM_UNIXTIME(bp) will show you a human-friendly format.)
Now, to sort the list, use a sub-query: if you sort the generated query, you'll get random values clustered near the beginning time. So, to mostly fulfill step 2:
SELECT t.bp FROM (SELECT @low + FLOOR(RAND() * (@high-@low)) bp FROM generator_256 LIMIT 49) t ORDER by t.bp;
Now, it starts to get tricky. For any given row, we want to fill the end time with one second less than the next row's beginning time. While there are a couple of ways of going about it, I think the cleanest to understand is one that uses the destination table (or a copy of it) to store our generated begin points. (Note I've initialized values for @low and @high here as well as included the beginning point in the list):
CREATE TABLE times (id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,begin_time DOUBLE NOT NULL,end_time DOUBLE NOT NULL DEFAULT 0);
INSERT INTO times (begin_time) SELECT @low bp UNION SELECT s.bp FROM (SELECT t.bp FROM (SELECT @low + FLOOR(RAND() * (@high-@low)) bp FROM generator_256 JOIN (SELECT @low := UNIX_TIMESTAMP('2014-09-01 00:00:00'), @high := UNIX_TIMESTAMP('2014-10-12 23:59:59')) init LIMIT 49) t ORDER by bp) s;
Finally, we can add the end times using a joined update.
UPDATE times JOIN (SELECT curr.id, curr.begin_time, (SELECT next.begin_time-1 FROM times next WHERE next.id=curr.id+1) end_time FROM times curr) g ON g.id = times.id SET times.end_time = COALESCE(g.end_time, UNIX_TIMESTAMP('2014-10-12 23:59:59'));
In my examples here, I've left off two things:
- I'm ignoring the need to start one second after the begin and two seconds prior to the end. Thus, it's possible that one of your random values could equal the begin or end point, which would violate your stated need. You can add these constraints by using the MySQL
INTERVAL
operator. - In a truely random system, a long sequence of identical values is just as likely as a jumbled one. Eg, in binary, 000000000000000 is just as likely as 111111111111111 is just as likely as 0101010111101011. That means we could get 50 identical dates out of our query. You can get around this by generating a whole bunch of random times (1000s, maybe) and culling from that.
How can I generate a random date in the past?
unix timestamp is an integer from 0 to n so you can just use the normal random method in php :)
$timestamp = rand(0, time() - 60*60*24*365*10);
// Prints something like: Monday 8th of August 2005 03:12:46 PM
echo date('l jS \of F Y h:i:s A', $timestamp);
Related Topics
Calling C/C++ Library Function from PHP
How to Handle Error for Duplicate Entries
Populate Another Select Dropdown from Database Based on Dropdown Selection
Selecting Multiple Array Elements
PHP Curl Curlopt_Ssl_Verifypeer Ignored
Php. How to Use Array_Column with an Array of Objects
Php: Get Number of Decimal Digits
How Do Detect That Transaction Has Already Been Started
Apache Rewrite - Get Original Url in PHP
How to Modify Xml File Using PHP
Remove Exif Data from Jpg Using PHP
Regex Backreference to Match Different Values
PHP Combine Two Associative Arrays into One Array
Is There Garbage Collection in PHP
Token Must Be a Short-Lived Token and in a Reasonable Timeframe