How do I get value of datetime object in drupal 8? - datetime

There is a Datetime object as following and I want to get the date value
Array
(
[0] => Array
(
[value] => Drupal\Core\Datetime\DrupalDateTime Object
(
[formatTranslationCache:protected] =>
[inputTimeRaw:protected] =>
[inputTimeAdjusted:protected] =>
[inputTimeZoneRaw:protected] =>
[inputTimeZoneAdjusted:protected] =>
[inputFormatRaw:protected] =>
[inputFormatAdjusted:protected] =>
[langcode:protected] => en
[errors:protected] => Array
(
)
[dateTimeObject:protected] => DateTime Object
(
[date] => 2018-01-05 01:30:00.000000
[timezone_type] => 3
[timezone] => UTC
)
[stringTranslation:protected] =>
)
)
)
I don't want to get this value by $node->get("field_id")->value;
Because I need dynamic value that means the value should change after I changed date field.
Is it possible?

Figured it out
$date = $form_state->getValue('field_id')[0]['value']->format('Y-m-d H:i:s')
it returns string !!
The point is locate the Object.

The values in the array are of type Drupal\Core\Datetime\DrupalDateTime, check the API on Drupal.org DrupalDateTime Doc.
In order to get value from the object you must use the __toString method as mentioned.
Proceed as :
$dateTime = YourArray[0]['value'];
$date = $dateTime->_toString();
Else
$date = $dateTime->format('Y-m-d h::i::s');
For more date formats check the PHP doc PHP DATE
Edit 1:
The following Code works correctly :
$temp = new \Drupal\Core\Datetime\DrupalDateTime();
echo $temp->__toString();
echo $temp->format('Y-m-d h::i'); die;

I found that in browsers that do no support
<input type="time">
(i.e. Safari) the value is not of type "Drupal\Core\Datetime\DrupalDateTime" but array.

Here is what i did inside a drupal 8 module to get formatted date from DrupalDateTime
I. If you have a date and want format it, just pass it to the static method of the class (DrupalDateTime) as follows. You can replace the string with your date variables.
Below shows both using the static version and non static version of DrupalDateTime
$date = DrupalDateTime::createFromFormat('j-M-Y', '20-Jul-2019');
// Using the static method prints out: 20-Jul-2019:11:am
$date = new DrupalDateTime('now'); // grab current dateTime using NON static
$date->format('l, F j, Y - H:i'); // format it
// prints out nicely formatted version: Tue, Jul 16, 2019 - 11:34:am
// you can remove H:i and what's after it if you don't want hours or am pm
$date = new DrupalDateTime('now'); // grab current dateTime
// Or print $date->format('d-m-Y: H:i A');
// prints out: 16-07-2019: 11:43 AM
More examples:
$date = new DrupalDateTime();
$date->setTimezone(new \DateTimeZone('America/Chicago'));
print $date->format('m/d/Y g:i a');
// The above prints current time for given Timezone
// prints : 07/16/2019 10:59 am
// Another variations of the above except it takes specific date and UTC zone
$date = new DrupalDateTime('2019-07-31 11:30:00', 'UTC');
$date->setTimezone(new \DateTimeZone('America/Chicago'));
print $date->format('m/d/Y g:i a');
// prints 07/31/2019 6:30 am
To use these in your module/code you need to include the following at the top of your file;
use Drupal\Core\Datetime\DrupalDateTime;
Also note that the DrupalDateTime extends DateTimePlus() which it self "wraps the PHP DateTime class with more flexible initialization parameters.... as per docs..."
How to test it with Drush.
Save the above code in a php script, then let drush run the srcipt after it bootstraps drupal like:
drush -r /path-to-your-drupal-documentRoot -l example.com scr ~/path-to your-script
For multisites make sure you use ... drush -l http.... like above
Note:
I posted similar answer to: https://drupal.stackexchange.com/questions/252333/how-to-get-formatted-date-string-from-a-datetimeitem-object/283529#283529

in my case I use date range with duration
in your case you need the bold one
date('Y-m-d\TH:i:s', strtotime($date->get('field')->getValue()[0]['value']))
date('Y-m-d\TH:i:s', strtotime($date->get('field')->getValue()[0]['end_value']))

Related

Save geofield programmatically in Drupal 8

I have read in a lot of sources that I should use the geofield_compute_values() function when trying to programmatically save coordinates in Drupal.
However it does not work for me, that function is undefined in the Drupal 8.5.2 that I am using.
I've installed geofield using composer and I can use it as usual in the admin area and there are no problems with saving there.
Here are some examples I've tried with, the first example gives me undefined function geofield_compute_values() :
$geofield_data = geofield_compute_values([
'lat' => $lat,
'lon' => $lon,
], GEOFIELD_INPUT_LAT_LON);
$cbisProduct->set('field_koordinater', $geofield_data);
I have also tried this out with no successful result and no errors :
$geofield = [
'geom' => "POINT (" . $lon . " " . $lat . ")",
'geo_type' => 'point',
'lat' => $lat,
'lon' => $lon,
'left' => $lon,
'top' => $lat,
'right' => $lon,
'bottom' => $lat,
];
$cbisProduct->set('field_koordinater', $geofield);
Seems like you're trying to use the geofield_compute_values() function which was available in 7.x version, but not in 8.x
You should look into the wkt_generator service. i.e.
<?php $wktGenerator = \Drupal::service('geofield.wkt_generator'); ?>
I haven't tried this, but something like this should work:
<?php
$point = [
'lat' => $request->get('lat'),
'lon' => $request->get('lon'),
];
$value = \Drupal::service('geofield.wkt_generator')->WktBuildPoint($point);
$node->field_koordinater->setValue($value);
Also, WktGeneratorTest.php and GeofieldItemTest.php files could be a good start to see how to use the service in your implementation.
This function is not available in Drupal 8. You have to rely on the basic GeofieldItem class that extends FieldItemBase. Also, as mentioned by oman, you can use WktGenerator to easily build points, polygons, etc.
Here a working example. Let's say your have an entity $cbisProduct with a multivalued geofield field_koordinater, and you want to set the first item with arbitrary lat/lon coordinates :
// Get geofield item
$geofield = $cbisProduct->get('field_koordinater')->get(0);
// Generate a point [lat, lon]
$coord = ['45.909621', '6.127147'];
$point = \Drupal::service('geofield.wkt_generator')->WktBuildPoint($coord);
// Calling this function will compute values AND assign geodata to the field instance
$geofield->setValue($point);
// You can read the computed geodata from the field
$geodata = $geofield->getValue();
//dpm($geodata);
// Explicitly set field data (needed if $geofield is not a reference)
$cbisProduct->set('field_koordinater', [$geodata]);
// Save entity
$cbisProduct->save();
Under the hood, GeofieldItem::setValue calls another method responsible to directly assign the computed values to the field instance :
# \Drupal\geofield\Plugin\Field\FieldType\GeofieldItem
protected function populateComputedValues() {
/* #var \Geometry $geom */
$geom = \Drupal::service('geofield.geophp')->load($this->value);
if (!empty($geom)) {
/* #var \Point $centroid */
$centroid = $geom->getCentroid();
$bounding = $geom->getBBox();
$this->geo_type = $geom->geometryType();
$this->lon = $centroid->getX();
$this->lat = $centroid->getY();
$this->left = $bounding['minx'];
$this->top = $bounding['maxy'];
$this->right = $bounding['maxx'];
$this->bottom = $bounding['miny'];
$this->geohash = substr($geom->out('geohash'), 0, GEOFIELD_GEOHASH_LENGTH);
$this->latlon = $centroid->getY() . ',' . $centroid->getX();
}
}
Note : You don't necessarily need WktGenerator for building points, as long as you know the geofield type and how geophp should handle it. For example, the following 2 statements are equivalent :
$point = \Drupal::service('geofield.wkt_generator')->WktBuildPoint($coord);
// is equivalent to
$point = GEOFIELD_TYPE_POINT . '(' . implode(' ', $coord) . ')');
But it is safer to rely on the WktGenerator especially with more complex data types.

Symfony Solarium bundle datetime range

$client = $this->solr->getClient();
$query = $client->createSelect();
$facetSet = $query->getFacetSet();
$facetSet->createFacetField('id')->setField('publication_year');
$facet = $facetSet->createFacetRange('yearsranges');
$facet->setField('publication_year');
$facet->setStart( --MIN DATE VALUE-- );
$facet->setGap( --I NEED TO MAKE RANGE OF 10 YEARS-- );
$facet->setEnd( --NOW DATE TIME-- );
$solrQuery = '*:*';
$query->setQuery($solrQuery);
$query->setRows(0);
$data = $client->select($query)->getFacetSet()->getFacet('yearsranges');
dump($data);
die;
Let first ask you, if it is valid to make ranges of dates in solr facets
"publication_year" field in solr collection. This date is actually a string in this format.
"2009-10-29T23:00:00Z"
Yes it's valid or just use a query filter:
$createQuery->createFilterQuery('range')->setQuery('createdate:[1995-12-31T23:59:59.999Z TO 2007-03-06T00:00:00Z]');
For a facet:
$facet->createQuery('createdate:[1995-12-31T23:59:59.999Z TO 2007-03-06T00:00:00Z]');

Unexpected notice: undefined index when null

I've written a little script that is supposed to create an array that looks something like this:
array(1 => array( 'month' => 'Jan',
'salary' => '01/31/2013',
'bonus' => '02/15/2013'),
2 => array('month' => '',...));
You get the basic idea: the index in the main array are the months (numerically), each holding an array that is to be filled dynamically. The month key depends on the language the user requested, the salary and bonus are assigned the dates when the salary and or bonus was payed. So far no surprizes.
To get the basic structure of that array, I thought this would be the easiest:
$this->months = array_fill_keys(range(1,12), array( 'month' => null,
'salary' => null,
'bonus' => null));
I then fill the array, and things run smoothly all the same, right up until the point where I want to write the data to a file, which I did like so:
private function writeFile()
{
foreach($this->months as $key => $vals)
{
if ($vals['month'] === null)
{//Only filled from date x to date y, some months can be empty
continue;
}
//this seems to raise notices?
if ($vals['salary'] === null)
{
$vals['salary'] = 'Does not apply';
}
fwrite($this->file, implode(',', $vals).PHP_EOL);
}
fclose($this->file);
return $this;
}
The line where I check if the salary is null raises notices: "Warning: Undefined index salary". At the moment I've had to add this to the code:
if (!array_key_exists('salary', $vals) || $vals['salary'] === null)
{
if (!array_key_exists('bonus', $vals) || $vals['bonus'] === null)
{
break;
}
$vals['salary'] = 'Does not apply';
}
To get to the result I need. I have googled this, and stumbled across this bug report, which was last modified 4 years ago (2009-05-08), but the status is still set to "no feedback".
Has anyone else encountered a similar glitch/bug? Or am I missing something here? How can I avoid this issue without the need for all too many if's and function calls without changing my settings (E_STRICT | E_ALL, as it should be).
BTW: I'm running PHP 5.4.7, on Slackware 14. For this little application, I'm using 2 Symfony components (ClassLoader and Console), but since this is part of an object that has nothing to do with Symfony, apart from being loaded through UniversalClassLoader I don't think that's relevant.
Since the bug is said to be PDO related: yes, I am using PDO, but in another class.
I am not sure , but try to use
$this->months = array_fill(1,12, array( 'month' => null,
'salary' => null,
'bonus' => null));
After a couple of var_dumps, I found out what the cause was: The array keys were range(1,12), to determine the which month was I was processing. For that I used a DateTime object in the following fashion:
$date->modify('+ '.$diff.' days');
$key = $date->format('m');
The problem was that the format call returns a string. The goal was to list when the salary and bonus are to be paid. The bonus has to be paid each 15th, or the following Wednesday, if the 15th was a Saturday or Sunday. The salary is to be paid the last day of the month, or the last Friday.
In other words, the bonus pay date is assigned like so:
$key = $date->format('m');
$this->months[$key]['month'] = $date->format('M');
if ($date->format('d') == 15)
{
//find find week-day (15th or following Wednesday)
$this->months[--$key]['bonus'] = $date->format('m/d/Y');
$key++;
//set date to end of month
}
//check week-day, and modify date if required
$this->months[$key]['salary'] = $date->format('m/d/Y');
Because the keys of the $this->months array are numeric, but the format used for $key is a 2-digit string, with leading zeroes, I was having problems.
Each 15th of the month, the $key value is coerced to an integer (decrement/increment operators), but the month is assigned using a string.
The information I provided in my original question was insufficient, sorry for that, but I had just put in an all-nighter. The fix, in the end was pretty easy:
$key = (int) $date->format('m');//cast
I sincerely appreciate all responses, and everyone contributing to the SO community. I would've deleted this question, but if nobody objects, I think I might leave it be as a testimony to my stupidity.

format_date() not working with timezone

Drupal 7.8 install
Site Timezone set to America/New_York in the region settings
I have this code in a page callback
Problem happens on multiple servers
format_date() is not adjusting for the timezone offset by either the default site timezone, or even when I add the timezone string as an argument.
Below is the code, and at the bottom of the code is the output commented out. There are 2 examples using format_date, and the last example is what I had to do to get the correct time to display.
Any ideas on how to get format_date() working with the timezone?
header('content-type: text/plain');
// utc_str as it would come from the db of a date field
$utc_str = '2011-09-01 14:00:00';
// converting to a unix timestamp
$timestamp = strtotime($utc_str);
// first print with format_date, note default site timezone is America/New_York
print 'format_date($timestamp, "custom", "Y-m-d h:s:i"): '. format_date($timestamp, 'custom', 'Y-m-d h:s:i') ."\n";
// next print date by actually setting the timezone string in the argument
// Result:
$tz_str = 'America/New_York';
print 'format_date($timestamp, "custom", "Y-m-d h:s:i", "America/NewYork"): '. format_date($timestamp, 'custom', 'Y-m-d h:s:i', $tz_str) ."\n";
// this is the only way i could get it working
$date = new DateTime($product->field_class_date['und'][0]['value'], new DateTimeZone(date_default_timezone_get()));
$offset = $date->getOffset();
$formatted = date('Y-m-d h:s:i', ($timestamp + $offset));
print $formatted;
/** This is the output
format_date($timestamp, "custom", "Y-m-d h:s:i"): 2011-09-01 02:00:00
format_date($timestamp, "custom", "Y-m-d h:s:i", "America/NewYork"): 2011-09-01 02:00:00
2011-09-01 10:00:00
*/
The way you have solved it is correct. If you use PHP 5.3 or higher you can use the DateTime::add method and simply add the offset, without making a timestamp from it like i did below.
$utcTimezone = new DateTimeZone('UTC');
$timezone = new DateTimeZone('America/New_York');
$dateTime = new DateTime('2011-09-01 14:00:00', $timezone);
$offset = $timezone->getOffset($dateTime);
print date('Y-m-d H:i:s', $dateTime->format('U') + $offset);

FullCalendar with JSON feed loading slowly

I'm using FullCalendar to load events from custom post types on WP, through a JSON feed. It's working, but it's taking some time to load. Please check here: http://cea3.iscte.pt/en/agenda-3/ (june or august). Do any of you have a clue what can be causing it?
This is the full code for the JSON feed:
<?php
// - standalone json feed -
header('Content-Type:application/json');
// - grab wp load, wherever it's hiding -
if(file_exists('../../../../wp-load.php')) :
include '../../../../wp-load.php';
else:
include '../../../../../wp-load.php';
endif;
global $wpdb;
// - grab date barrier -
$oneyear = strtotime('-1 year') + ( get_option( 'gmt_offset' ) * 3600 );
// - query -
global $wpdb;
$querystr = "
SELECT *
FROM $wpdb->posts wposts, $wpdb->postmeta metastart, $wpdb->postmeta metaend
WHERE (wposts.ID = metastart.post_id AND wposts.ID = metaend.post_id)
AND (metaend.meta_key = 'tf_events_enddate' AND metaend.meta_value > $oneyear )
AND metastart.meta_key = 'tf_events_enddate'
AND wposts.post_type = 'tf_events'
AND wposts.post_status = 'publish'
ORDER BY metastart.meta_value ASC LIMIT 500
";
$events = $wpdb->get_results($querystr, OBJECT);
$jsonevents = array();
// - loop -
if ($events):
global $post;
foreach ($events as $post):
setup_postdata($post);
// - custom variables -
$custom = get_post_custom(get_the_ID());
$sd = $custom["tf_events_startdate"][0];
$ed = $custom["tf_events_enddate"][0];
// - grab gmt for start -
$gmts = date('Y-m-d H:i:s', $sd);
$gmts = get_gmt_from_date($gmts); // this function requires Y-m-d H:i:s
$gmts = strtotime($gmts);
// - grab gmt for end -
$gmte = date('Y-m-d H:i:s', $ed);
$gmte = get_gmt_from_date($gmte); // this function requires Y-m-d H:i:s
$gmte = strtotime($gmte);
// - set to ISO 8601 date format -
$stime = date('c', $gmts);
$etime = date('c', $gmte);
$thetitle = $post->post_title;
$short_title = substr($thetitle,0,50);
$eventpostid = $post->ID;
$eventslug = wp_get_post_terms( $eventpostid, 'tf_eventcategory' );
$eventvenueslug = $eventslug[0]->slug;
$tf_events_link = get_post_meta($post->ID, 'tf_events_link', true);
$tf_events_permalink = get_permalink($post->ID);
if ($tf_events_link) { $url_event = $tf_events_link ; }
else { $url_event = $tf_events_permalink; };
// - json items -
$jsonevents[]= array(
'title' => $short_title . '...',
'allDay' => false, // <- true by default with FullCalendar
'start' => $stime,
'end' => $etime,
'url' => $url_event,
'className' => $eventvenueslug
);
endforeach;
else :
endif;
// - fire away -
echo json_encode($jsonevents);
?>
Thank you.
It does seem like it takes a few seconds to load.
I use this calendar and it takes about 2-6 seconds to load- The longest was 6 seconds but i had about 3 sources and ~40 events.
Now I am not sure if that for you is a long time because you did not specify how long it actaully takes.
It looks like the call is on the same server so the only problem can be the SQL that takes long to reply. Is it dedicated or shared server?
Your PHP looks fine and should execute quickly. I have similar logic using .NET and SQL Server.
This is the time it takes to load your calendar from my side of the world.
The only other way to speed it up is to use Caching
http://arshaw.com/fullcalendar/docs/event_data/events_json_feed/#options
and manage it to reload the source somehow with a clever check of the date? or something.
I am sure the times would be reduced to ms once the feed has been loaded before.
You would have to do something clever like load the current month quickly - and in the background load 1 year or 2 years and cache it. Then when you change months it will be instant because its in memory.

Resources