I save the user photo with Carbon in laravel 9
$user->photo_verified_at = Carbon::now()->timestamp;
But I get this output from database:
Error Message : ORA-01843: not a valid month
Position : 42
Statement : update "USERS" set "PHOTO_VERIFIED_AT" = :p0, "USERS"."UPDATED_AT" = :p1 where "ID" = :p2
Bindings : [1672340645,2022-12-29 19:04:05,25]
If you a look at the data for created_at and updated_at in the database, they have the following format:
29/12/22 18:30:43,000000000
How is it possible to set Carbon to return in that format?
Carbon::now()->timestamp->format('dd/mm/yy H:i:s')
it returns
Error: Call to a member function format() on int
You should be fine with just Carbon::now() or now() (it is a helper function calling Carbon::now())
I am not exactly sure what you are storing there, if it is a timestamp that is literally a number, for example, 13102321 (just a made up number from me), it is the seconds that literally passed since a known date (I think it is January 1st, 1970 or something like that).
So, if you exactly want that fomat, it is as easy as reading the documentation, it will state that you can use the normal PHP's Date format:
$formatedString = now()->format('d/m/Y H:i:s,u000');
You have to add 3 more 0, as it has 9 0, that is nano seconds, super weird, but it is exactly the format you want...
You can also try out just using now() or any method that returns a literal Carbon object, it can or cannot work, depends how you have your model's attribute casted/declared.
Related
I'm trying to query based on two databases and columns.
$model->where('response_time', '<=', Carbon::now()->subMinutes(\DB::raw('anotherTable.created_at'))->diffInMinutes(Carbon::now()))
response_time contains an integer of minutes, ie: 15
If the anotherTable.created_at - response_time (ie, sub 15 mins) is less than the current time, return the row.
I've tried the above but I'm having no luck and no idea how to debug my query. Any help appreciated.
Example query that should pass and return its row (1 minute over):
response_time 15
anotherTable.created_at 21-03-2022 13:40:00
Current time: 21-03-2022 13:56:00
Why Carbon is not working
Carbon is unable to retrieve the value from the database during query generation on the PHP side ($model->where()). Carbon will instead execute immediately on the string value of 'anotherTable.created_at' prior to issuing the query to the database. The equivalent of:
$offset = Carbon::now()
->subMinutes('anotherTable.created_at')
->diffInMinutes(Carbon::now()); // 0
$model->where('response_time', '<=', $offset); // WHERE responst_time <= 0
Carbon Process Breakdown
Carbon\Traits\Date::__call() is used to parse the action and units from the method name as sub and minutes.
The process conditionally calls Carbon\Traits\Units::subUnit('minute', 'anotherTable.created_at') [sic] which calls Carbon\Traits\Units::addUnit($unit, -$value, $overflow) [sic].
The -$value is ultimately processed by PHP as -'anotherTable.created_at' resulting in 0 as if you called Carbon::now()->subMinutes(0).
The issue would have been pointed out if Carbon would throw a TypeError exception when provided a string for the $value argument, as opposed to the expected int. However, the $value argument of Carbon\Traits\Units::subUnit($unit, $value) is not type-hinted.
MySQL Resolution
Instead of using Carbon, use the MySQL TIMESTAMPDIFF() function to produce the difference in minutes between anotherTable.created_at and NOW() in the query.
db<>fiddle Example
Provided anotherTable is being added with a JOIN in the query.
$model->where(
'response_time', '<=', DB::raw('TIMESTAMPDIFF(MINUTE, anotherTable.created_at, NOW())')
);
Give this a try :)
$model->where('response_time', '<=', Carbon::now()
->diffInMinutes(\DB::raw('anotherTable.created_at')));
You cannot rely on the table values being used in a carbon function.
Instead, you have to do your caulcations on the database.
The code below should work in theory, though I have not tested it out.
All culcations that require database data, are done on the database.
Carbon is only used to pass the condition value up to it.
$model->where(
DB::raw('DATE_SUB(anotherTable.created_at, INTERVAL response_time MINUTE)'), '<=', Carbon::now()
);
i hope this will help you
Carbon::parse('21-03-2022 13:40:00')->diffInMinutes(new DateTime)
Or
Carbon::parse('21-03-2022 13:40:00')->diffInMinutes(Carbon::now())
in your case use this:
Carbon::parse(DB::raw('anotherTable.created_at'))->diffInMinutes(Carbon::now())
I seem to have a problem with my Laravel application, and I can't for the life of me figure out whats going on.
I have a simple date field in my MySQL database, its data type is just date, and for this example the value is 2020-08-13, but for some reason, when I try to access the date, it adds a timestamp on the end, minuses 1 day and throws a Carbon trailing data error.
It is definitely this date, because when I soft delete it, the error disappears.
As an example, the error for this date is
Carbon\\Carbon::rawCreateFromFormat('Y-m-d', '2020-08-12T23:0...', NULL)
So as you can see, it's removing a day and trying to format at 11pm the night before?
I have had similar issues to this before where Laravel was adding 0000-00-00 to the date, so I had to remove it in my accessor, but now that its throwing 11pm, my string replace doesn't work anymore. I can obviously just change the string replacer to look for 11pm, but I don't want to have to fix this every time the format changes.
My accessor code is as follows
public function getDateAttribute($value){
//String replace and remove the time from the value if it exists
$value = str_replace(' 00:00:00', '', $value);
return Carbon::CreateFromFormat('Y-m-d', $value)->format('d/m/Y');
}
Has anyone had an error like this before, or have any idea whats going on?
Before Laravel 7, dates would be serialized to a format like the following :
2019-12-02 20:01:00
But, Laravel 7 uses a new date serialization format when using the toArray or toJson method on Eloquent models, with ISO-8601 date format. This ISO-8601 dates are always expressed in UTC, like this :
2019-12-02T20:01:00.283041Z
If you would like to keep using the previous behavior you can override the serializeDate() method on your model, add this into your model :
use DateTimeInterface;
protected function serializeDate(DateTimeInterface $date)
{
return $date->format('Y-m-d H:i:s');
}
first define you column to $dates property on your model :
$dates=['date']
then specify your date format by accessor :
public function getDateAttribute($value){
return $val->format('d/m/Y');
}
public function getDateAttribute($value){
// return 2020-08-17
$date = date('Y-m-d',strtotime($value));
return $date;
}
Make sure of the config file. You will find there a function of local time that can change its value from UTC to the local time of your country
config / app.php
'timezone' => 'Asia/Riyadh',
it's removing a day and trying to format at 11pm the night before
That's clearly a timezone shift (you're likely in GMT+1) and this is a shift to GMT+0 (UTC).
You should not trim the time to force "00:00:00" because this will only work in 1 timezone (there is no worldwide-midnight-moment, there is a different midnight moment in each timezone for each day), while your app now or later may handle multiple timezones. You should rather save the timezone ("Europe/London" for instance) in an other DB column and so you'll keep the complete information and when retrieving your date, you can calculate on the fly the midnight from (before) this date-time according to the user timezone.
Let's say I have this date, e.g.:
12-DEC-14 11.55.51.000000000 AM
and then I assigned it to variable, let's say:
$date = '12-DEC-14 11.55.51.000000000 AM';
Now the question is, how do I add 1 month or 2 months or so to that $date using only
php, without using any oracle sql date functions, just pure php,
because I will save the result to oracle db table in that same format.
The result should be like, e.g.:
$nextmonth = '12-JAN-15 11.55.51000000000 AM';
Then that's the time I can save that $nextmonth in the table column. So how ?
You can use DateTime classes in this case, load that date and define its format. Then after creating the datetime object, adjust it to your x number of months, then present it again with the original format.
$input = '12-DEC-14 11.55.51.000000000 AM';
$date = DateTime::createFromFormat('d-M-y h.i.s A', $input);
$next_month = clone $date;
$next_month->modify('+1 month');
echo strtoupper($next_month->format('d-M-y h.i.s A'));
This is probably only a very long comment rather than a real answer, but some of your comments let me think we have an instance of XY problem here:
it's a TIMESTAMP(6) , but the format is exactly like this inside that column 12-DEC-14 11.55.51.000000000 AM
how bout the succeeding 9 zeroes ?, won't that cause any errors in oracle if I just append it like e.g echo strtoupper($next_month->format('d-M-y h.i.s.000000000 A'));
A TIMESTAMP(6) store a date-time information (a "point in time"). It hasn't any "format" par se. TIMESTAMP are great data type, as they allow you to easily perform calculation on data and time with a great precision at DB level such as "adding one month".
But, without explicit request from your part, a default format is used to convert timestamps from and to strings.
Maybe your real issue is that Oracle try to implicitly convert your string to timestamp using its standard format. Usually, it is a far better idea to explicitly convert data type yourself. From the documentation:
Oracle recommends that you specify explicit conversions, rather than rely on implicit or automatic conversions, for these reasons:
[...]
implicit conversion depends on the context in which it occurs and may not work the same way in every case. For example, implicit conversion from a datetime value to a VARCHAR2 value may return an unexpected year depending on the value of the NLS_DATE_FORMAT parameter.
Please note that above statement is true for TIMESTAMP too, as the "standard" format is user-definable using NLS_TIMESTAMP_FORMAT.
The PHP documentation has a similar warning:
DATE columns are returned as strings formatted to the current date format. The default format can be changed with Oracle environment variables such as NLS_LANG or by a previously executed ALTER SESSION SET NLS_DATE_FORMAT command.
As a personal suggestion, I would say that you should never rely on implicit date/timestamp conversion, as any change of the relevant configuration settings at DB-level or for the current session will break your code.
So, depending the way you insert your value, using an explicit format might be as simple as wrapping the bind variable in a proper TO_TIMESTAMP(....) call:
// query the original data
$q = oci_parse ($connection ,
"INSERT INTO ....
VALUES (TO_TIMESTAMP(:date_as_str, 'YYYY-MM-DD HH:MI:SS.FF'), other_columns...)");
$q = oci_parse ($connection ,
"INSERT INTO ....
VALUES (TO_TIMESTAMP(:date_as_str, 'YYYY-MM-DD HH:MI:SS.FF'), other_columns...)");
Coming back to your initial question: "how to add one month in a raw date" " I will save the result to oracle db table in that same format data type" , this should be as simple as adding 1 month when inserting the value:
$q = oci_parse ($connection ,
"INSERT INTO ....
VALUES (TO_TIMESTAMP(:date_as_str, 'YYYY-MM-DD HH:MI:SS.FF')
+ INTERVAL '1' MONTH, other_columns...)");
I'm trying to get an Eloquent query result for DB::raw("DATE_FORMAT(created_at, '%m-%d-%Y %r') AS created_at") but each time I get this exception from Carbon:
InvalidArgumentException
Unexpected data found. Trailing data
If I change it to just created_at instead of employing MySQL's DATE_FORMAT() function, then it gets the data without issue.
I've not only done this sort of date formatting without issue before, but I checked every field in the database table (there are only 10 for this seed) and each is a standard valid date, so I'm wondering why Carbon is pitching a fit.
Running this in Laravel 4.1.
In an Eloquent query result (model) every date field is a carbon object, it means, if you query a model which contains any timestamp field like created_at, updated_at (basically created using timestamps() during migration) and deleted_at, Laravel converts them to a Carbon object and you may use any public methods of Carbon, for example:
$user = User::find(1);
// '2014-04-20 19:02:09' will become 'Apr 20, 2014'
$user->created_at->toFormattedDateString();
So, you may directly use any public method of Carbon on a timestamp field available in a model. If you try this:
dd($user->created_at);
Then the output will be:
object(Carbon\Carbon)[456]
public 'date' => string '2014-04-20 19:02:09' (length=19)
public 'timezone_type' => int 3
public 'timezone' => string 'UTC' (length=3)
So, if you want to format a date, you may use:
// outputs like: 'Sunday 20th of April 2014 07:02:09 PM'
$user->created_at->format('l jS \\of F Y h:i:s A')
Update:
If you want to change this behavior, means that, if you want tell Laravel that, which fields should be converted automatically to Carbon object then you may override that by creating a method in your model like:
public function getDates()
{
// only this field will be converted to Carbon
return array('updated_at');
}
To totally disable date mutations, simply return an empty array from the getDates method. For more details, check Date Mutators on Laravel website.
I realise the original question refers to MySQL but I had the same error with MSSQL. The problem turned out to be that MSSQL's datetime column type has a precision of .001 seconds but I was setting my model's format to no precision:
protected function getDateFormat()
{
return 'Y-m-d G:i:s';
}
By using the newer DateTime2 column type and turning off the precision, I fixed the error. I.e.
datetime2(0)
You could instead change the format in getDateFormat, of course.
If it helps anyone else, I got the same error when attempting to copy a date.
$user->last_login = Carbon::now();
if ($user->first_login < Carbon::createFromDate(2000, 1, 1)) {
// This is the users first login
$user->first_login = $user->last_login; // FAILS!
}
Turns out Laravel casts the value of $user->last_login to a DateTime+Timezone string. It's no longer a Carbon object.
You could fix the error by using copies of a single Carbon object (example below), or by setting up mutators (setters) on the underlying model.
$now = Carbon::now();
$user->last_login = $now;
if ($user->first_login < Carbon::createFromDate(2000, 1, 1)) {
// This is the users first login
$user->first_login = $now;
}
I would like to get date string filled with zeros for specified date format. I want to use it in case when there is no date given by external API.
Example:
define('DATE_FORMAT', 'Y-m-d H:i:s');
[...]
$date = SomeExternalApi::get_date();
// if date is given it's simple
if(!empty($date)){
$date_obj = DateTime::createFromFormat(SomeExternalApi::SOME_DATE_FORMAT, $date_string);
return $date_obj->format(DATE_FORMAT);
}else{
// SO UGLY BELOW! :( How to use DATE_FORMAT in this case?
return '0000-00-00 00:00:00';
}
I don't want to hardcode "zero string" like in example, because when I change DATE_FORMAT I would like to get zeros formatted new way.
Probably there is no simple way to format "zero string" of date, but maybe someone from this great community has better idea? :)
The best answer for my question is: DON'T GO THIS WAY.
You will never need empty date with zeros - it's super uncommon. The only one place where you can find it is database date field - if there is no date, you will get zeros format. But this will be always the same format, so you can define it as constant and condition as string.
Zeros as I asked are BAD because if you send such zeros through some API to any client like mobile app it has no chance to parse it - it will crash the app without catching errors.
Instead of having zeros date presentation use:
[when saving] put empty string in database, you can set date field to empty string and this will become zeros, do it with UPDATE mytable SET date = "" WHERE id = 2; and mytable.date field is MySQL timestamp in this example,
[when getting] you will get zeros with SELECT date FROM mytable WHERE id = 2;, so define it in your code as constant string 0000-00-00 00:00:00 and equal with returning value from db,
[when response of API] if date is empty return false instead of zeros. Mobile app can check if it's false and try to parse it in any other case. It's the best way to keep empty date and is super simple to check in any format by any language. Lot of simpler than trying to parse some strange zeros format.
Generally if I find date by condition like in example I return false and every single client using this API is happy with it:
if(empty($date)){return false;}
Dates and times in PHP are represented with a UNIX timestamp. The range of possible dates this can represent is not capable of representing the timestamp '0000-00-00 00:00:00' on a 32-bit system (it's over 2000 years ago). Also, actually creating a DateTime representing this single timestamp is probably pretty pointless, the values it represents are unchanging.
However, you could do this by defining absolute values for every sensible format character and using strtr() to convert them to their specific values.
Short example using your example input format:
$placeholders = array(
'Y' => '0000',
'm' => '00',
'd' => '00',
'H' => '00',
'i' => '00',
's' => '00'
);
return strtr(DATE_FORMAT, $placeholders);
However, this could start to get messy when you are dealing with the format elements that represent textual elements such as D. Personally I'd probably pick a default format and use that, as your code in the question shows.