How can I check the current timestamp in MongoDB?
Currently I use a query that looks like:
"last_visit": {
"$lt": NumberLong(1373779876)
}
I store that query, and then I have a cron job that makes it run every minute.
If one of my users hasn't logged in since some timestamp, I can know it.
But the NumberLong generated isn't right, as I am currently using time() - 14days via PHP, the resulting NumberLong should be the result of something like:
timestampNOW - someTimestamp (that I generated in PHP)
My current solution stays an absolute date in the past, instead of a relative date such as timeRIGHTNOW - 14 days.
I believe I need a native Mongo command within my query...
I'm not entirely sure what you're asking for, but I think there are two options:
a) You want to construct a query using php that asks mongodb for all users that haven't logged in the last two weeks
<?php
// two weeks ago. note that time() returns number of seconds, not milliseconds
$date = (time() - (14 * 24 * 60 * 60)) * 1000;
// construct query: { "last_visit" : { $lt : $date } }
$query = array( 'last_visit' => array( '$lt' => $date ));
$cursor = $collection->find($query);
// you can now iterate $cursor
?>
b) You want to perform a mongodb query in javascript ('native Mongo command')
db.Foo.find({"last_visit" : {$lt : ISODate().getTime()}});
calling ISODate will create a new ISODate object representing the current time, and getTime will return the number of milliseconds since the UNIX epoch. I don't know how you can pass that query to the PHP mongodb driver, but it should be straightforward.
Current timestamp rounded to whole second:
Timestamp(new Date().getTime()/1000,0)
Timestamp 14 days ago:
Timestamp((new Date().getTime()-(3600*24*14*1000))/1000,0)
Related
I have a table containing a datetime column:
$db = new SQLite3('test.db');
$results = $db->query('CREATE TABLE test (id INTEGER PRIMARY KEY AUTOINCREMENT,
date DATE, foo TEXT);');
and I add a row (storing the datetime of the addition of the row in UTC) with
$results = $db->query('INSERT INTO test (date, foo) VALUES(CURRENT_TIMESTAMP, "bar");');
This works. Now when displaying the rows:
$results = $db->query('SELECT * FROM test ORDER BY date desc');
while ($row = $results->fetchArray()) {
echo $row['date'];
}
the date is displayed like this, in UTC: 2019-04-27 16:41:33.
How to display it in the local timezone instead? (including Daylight Saving)
I can imagine there are different options:
store directly in SQLite with local timezone (but I think this is not good practice)
store in SQLite in UTC, and do the UTC->local timezone conversion during the SELECT. How?
store in SQLite in UTC, and do the UTC->local timezone conversion via PHP.
How to do this properly?
As suggested by a comment, this page mentions the localtime modifier. Here is a solution:
$results = $db->query('SELECT datetime(date, "localtime") AS localdate, foo FROM test ORDER BY date desc');
while ($row = $results->fetchArray()) {
echo $row['localdate'];
}
I would say what you need to do is store you time values as something called a "Unix timestamp", which is literally the number of seconds since UTC 1st January 1970 00:00. You can get the current time by using PHP's time() function. Then change you DB "date" column to an integer type. In this way, the time data stored in your database is completely independent of timezone etc, which is a good thing!
To interpret these timezones properly we can use PHP DateTime object, which will do all the heavy lifting in terms of timezones and DST. The following code snippet gives the basic ideas:
// These need to be provided from the client side
// Native JS will get you the offset, read my comment below for where to get DST from
$TimeOffset = $_POST['TZOffset'];
$isDST = $_POST['isDST'];
// DST needs to be an integer
if ($isDST == 'true'){
$isDst = 1;
}else{
$isDst = 0;
}
// Will give a result like Europe/London
// In most use cases, save this into the users session :)
$TimeZoneName = timezone_name_from_abbr('', $TimeZone * -60, $isDst);
// Now to tell PHP we are working in this timezone
date_default_timezone_set($TimeZoneName);
///// Somewhere else in your script
// Fetched a unix timestamp from your DB; $timestamp
$DT = new DateTime();
$DT -> setTimestamp($timestamp);
// Now you have control over how it is displayed
// For instance, this gives a 2010-04-28 22:41:43 type format
// This will be correct to the user's timezone we calculated earlier
echo $DT -> format('Y-m-d H:i:s');
Whether the client is in DST should obtained from JS, have a look at the answer for this question for a simple method, but many libraries etc can also do it.
So the big question, why go the long way around?
The time data in your database is completely independent of DB/server configuration. Over time these can wander and bugs can come into play; you want your fundamental data to be consistent.
It lets PHP do all the hard work regarding adjusting for timezones and DST, across every region / DST regime. It becomes a big headache quickly if you try and solve this yourself, DST is very awkward.
Time maths is much more efficient and easier, especially in DB calls. All you need to do is numerical comparisons of seconds, rather than dealing with SQL functions etc etc.
No reliance on specific DB functions or engines; its just an integer value! Makes for much easier portability.
A word of caution, be careful of the maximum value in your DB integer column. Find the max/min values, convert them to date times (this is a useful tool) and check against your use case.
The final thing to say, is you can use the DateTime object to interpret time strings (as you are using now) in UTC, then set the timezone before printing them. It will work, but I feel it is much more prone to error.
I hope that helps :)
Look at this SQL solution:
select ts,
cast( round( ( julianday( ts, 'localtime' ) - julianday( ts ) ) * 24 ) as int ) as diff,
datetime( ts, '+' || cast( round( ( julianday( ts, 'localtime' ) - julianday( ts ) ) * 24 ) as int ) || ' hours' ) as local
from test
Result:
ts dif local
2020-03-25 14:09:31 1 2020-03-25 15:09:31
2020-03-31 06:54:08 2 2020-03-31 08:54:08
2020-03-31 14:08:10 2 2020-03-31 16:08:10
2020-04-01 07:23:04 2 2020-04-01 09:23:04
2020-04-01 09:53:19 2 2020-04-01 11:53:19
I've researched this a lot but can't find a satisfactory answer; how do I create a PHP script that will generate a new number each day? Obviously I'm using this for a reason other than to generate a random number daily, but I won't go into that reason, it'll just make this question more complicated. So I'm asking this: How do I generate a random number which will change each day in PHP? Using MySQL will be no problem, but it must be automatic so I won't have to manually change it daily. (Here's my 'script' to generate a random number)
<?php
echo rand(1,100)
?>
Any answers are appreciated, Thanks
- Hugh
Use time() function to generate seed, then use regular rand.
This way you should't to store it anywhere and you always can regenerate it when needed.
function randomEveryDay()
{
$now = time();
$today = $now - ($now % 86400); //86400 = 1 day in seconds
srand($today);
return rand();
}
Or more interesting example without random at all.
function randomEveryDay() {
$now = time();
$today = $now - ($now % 86400);
$hash = sha1('salt string'.$today);
return intval('0x' . substr($hash, 6, 8));
}
Every day you will get the same number in $today, then use any cryptographic\non cryptographic hash function to generate "random".
This is fairly a simple task as long as you understand the basics of crontabs.
Step One: Create the script. This is basically going to be what creates the "text" then inputs it into the database via mysqli. For example, if we are generating a random number, what you have so far is good, you will just need to insert it into a database table. I recommend using a time stamp to give what day it was generated on
Step Two: Create a cronjob. Use the servers crontab to run a task every day, this can be done by adding this to the cron file: This will run a cron each new day.
00 01 * * * php path/to/your/generate.php
Step Three Fetch result from database by using the current date. If you are needing to display that text, pull it from the database using whatever the current day is from date() or DateTime
It's impossible to give a good answer without knowing exactly what you want to do, so this will generate a new number every day:
echo date('Ymd');
I have a scheduled task that runs every 5 minutes that collects some stats on a server I run.
There is a small delay whilst it waits for the request to come back and so records are always being saved 2 or 3 seconds later. I.e the task runs at 2017-14-03 08:00:00, but the records are saved at 2017-14-03 08:00:03.
I am trying to pull the records out to display on a graph. The graph scales to the time period you want to look at (through hard coded buttons that refresh the graph with new data).
The first graph I am trying to do is one over the last 24 hours. Rather than bring back every 5 minute point for the last 24 hours, I just want one per hour. I have built a function to round down to the nearest hour and then get the last 24 hours based off that - it looks like this:
public function last24Hours()
{
$times = [];
$time = Carbon::now()->minute(0)->second(0);
$i = 1;
while($i <= 24)
{
array_push($times, $time->toDateTimeString());
$time->subHour();
$i++;
}
return $times;
}
Using the times returned, I am trying to query the model with whereIn() like so:
$stats = ServerTracking::whereIn('created_at', $this->last24Hours())->get();
The query runs, but nothing comes back - as the created_at time is a couple of seconds off from what I am querying.
I've hit a bit of a roadblock and cannot think of a way to get around this? Any ideas?
You can use selectRaw with a formatted date:
$stats = ServerTracking::selectRaw('foo,bar,DATE_FORMAT(created_at, "%Y-%m-%d %H:00:00") as hour_created')->get()->keyBy('hour_created');
All of the values in each hour will have the same hour_created, and keyBy will only keep one of them (from docs):
If multiple items have the same key, only the last one will appear in the new collection.
Just replace foo and bar with the other values you need. You'll either keep the 0:55 minute values, or the 0:00 minute values, depending on how you sort the query.
Come to think of it, you could use whereRaw to do it your way:
->whereRaw("DATE_FORMAT(created_at, '%Y-%m-%d %H:00:00') in (" .implode(",",$last24Hours). ")")
Not a solution per se, but I would take a different approach. Assuming I understood you are trying to query the last 24hours (1day), I would do
$now = Carbon::now();
$stats = ServerTracking::where('created_at', '<=', $now) //now
->where('created_at', '>=', $now->subHours(24)) //24hours ago
->get();
Using whereBetween is similar, but a bit shorter
$now = Carbon::now();
$stats = ServerTracking::whereBetween('created_at', [$now, $now->subHours(24)])
->get();
So i have in a table tm_timekeep(id(pk), start(varchar), end(varchar)) [and a model for it] in a database with some time intervals in HH:MM format. For example:
id|start|end |
---------------
0 |10:00|10:30|
1 |11:23|11:55|
2 |13:15|15:39|
i would like to insert new rows and modify the existent ones if there are no overlappings between intervals.
if i would like to add as interval 11:57-12:40 would be ok, but 09:00-10:20 wouldn't because of 10:00-10:30, same for updateing a row. And if i would like to update a row, I need a function to check the condition before modifying the row. How should i do this?
My current code snippet:
public function checkInterval($start, $end){
$counter = 0;
$timekeepArray = Timekeep::all();
$start = date('H:i',Input::get('start'));
$end = date('H:i',Input::get('end'));
foreach($timekeepArray as $timekeep)
{
if($start <= $timekeep->end && $end>=$timekeep->start){
counter++; //in range
}
return counter;
}
}
After this i check the value of counter, but my problem is, that i always get 0 even if there are overlappings from input.
It's quite trivial - convert your time intervals to minutes, eg. 10:00 becomes 10 * 60 + 0 = 600 minutes, ends at 630 correspondingly.
Then, when you are trying to insert or update, and I would suggest you do it in Timekeep model, bound to 'saving' model event (see Model Events), you check whether:
1) Pick the last end time in minutes that is less than your new start time (that's a trivial SQL query)
2) See if there is any existing time in between that end time and your new end time - if there is, we have overlapping. Throw an exception, cancel model saving
There are several ways you could convert strings to minutes - you could store integers in MySQL (eg. 600) and then convert them to human readable hours either by defining getAttribute() or making your own methods. Or you could keep storing data as VARCHARs and do simple string manipulations (remove ':', multiply first part by 60) either in SQL or in PHP
I need to compare two dates to show an edit link if it is within 5 mins after the post was made, in PHP. If more than 5 minutes have passed, don't show anything.
$answer_post_date = get_the_time("Y-m-d");
$current_date = date("Y-m-d");
$formated_current_date = strtotime($answer_post_date);
$formated_answer_post_date = strtotime($current_date);
At this point I have two values:
1274414400 ($formated_current_date)
1276056000 ($formated_answer_post_date)
I am not sure what to do next to check if the current date/time is > 5 mins from the answer post date.
Any suggestions would be great.
All I really need the answer to be is a Boolean (yes/no) and if yes, display the minuets left to show the link to edit.
You're only handling dates, how are you supposed to know if the difference is 5 minutes?
Anyway, I'd say the majority of the PHP code that uses the default PHP functions is at least somewhat broken. The problem is you, despite a unix timestamp storing the correct point in time something happens, it does not store timezone information. See here.
So, forget using only date and strtotime. Use the datetime extension.
Store in the database the Unix timestamp and the timezone (by timezone I mean e.g. Europe/Lisbon). Then:
$tz = new DateTimeZone($timezone);
$answer_post_date = new DateTime("$timestamp");
$answer_post_date->setTimeZone($tz);
$current_date = new DateTime("now", $tz);
$diff = $current_date->diff($answer_post_date);
if ($diff->format("a") > 0 ||
$diff->format("h") > 0 ||
$diff->format("m") >= 5) {
//more than 5 minutes have passed
}
Of course, for comparing dates, you can always compare the timestamps.
My understanding of what you need to do:
$delta = ($formated_current_date - $formated_answer_post_date) / 60; // in minutes
if ($delta < 5) {
// show $delta
}
EDIT: Like others pointed out, this alone will not fix all of the issues at hand. As I see it, the smallest change to your current code would be to use a date format with higher granularity - such as "Y-m-d H:i:s". This being enough, like others pointed out, is contingent on the post's date being in the same timezone as your system.
I don't see the need to do a round-trip to a string format and back, regardless of how efficient or reliable it is.
date() will default to calling time() which you can call directly and get the current time in seconds as a Unix epoch timestamp (which is what you're trying to end up with in $formated_answer_post_date). You need to look in the WordPress docs to find the equivalent based on the post's value.
Then you can do a simple comparison of seconds. 5 minutes is 300 seconds.
You will still need to check that the code can assume the timezones of both values will be the same.