The Google Calendar API treats end.date as end.date - 1
My POST Payload
{
'summary': 'THREE DAY EVENT',
'status': 'confirmed',
'start': {
'date': '2020-01-27',
'timeZone': 'America/Los_Angeles'
},
'end': {
'date': '2020-01-29',
'timeZone': 'America/Los_Angeles'
}
}
Which yields the following event on my calendar
Notice the end date of this 3 day all day even is minus 1
When I do a GET on the event id, I am returned a correct object.
{
'kind': 'calendar#event',
'etag': '"REDACTED"',
'id': 'REDACTED',
'status': 'confirmed',
'htmlLink': 'REDACTED',
'created': '2020-01-22T20:15:23.000Z',
'updated': '2020-01-22T20:15:23.896Z',
'summary': 'THREE DAY EVENT',
'creator': {'email': 'REDACTED'},
'organizer': {'email': 'REDACTED',
'displayName': 'REDACTED',
'self': True},
'start': {'date': '2020-01-27'},
'end': {'date': '2020-01-29'},
'iCalUID': 'REDACTED',
'sequence': 0,
'reminders': {'useDefault': True}
}
I've seen this post -- Google Calendar API: Event endTime is decremented by 1 day
Per the Google Calendar API documentation: https://developers.google.com/calendar/v3/reference/events/insert#request-body
end.date -- The date, in the format "yyyy-mm-dd", if this is an all-day event.
For an all-day event, a time string is unneeded and the resulting event should be within the bounds I have provided. The resulting API response is correct, but the event in Google Calendar is not...
As you can see in the API official documentation, the parameter end refers to the exclusive end time of the event, which contrasts with start, which refers to the inclusive start time.
That is, the end date is not included in the event, but the day before.
Reference:
Events.insert request body
I hope this is of any help.
Related
(Hello all. This is my first post using the guided posting mode, so please bear with me)
I have been using Fullcalendar for a little while now to display events from JSON feeds for multiple calendars (One calendar per JSON feed). These JSON feeds are conversions of .ics feeds by ical.js to work fullcalendar. However, I now have need to break these calendars down into lists and filter them based on certain text in the event names in a manner similar to this:
Full event list.
January 1
lunch
gathering
outing
dinner
January 2
lunch
gathering
outing
dinner
January 3
lunch
closing
filtered event list for webpage (filtered for lunch).
January 1
lunch
January 2
lunch
January 3
lunch
Can I filter based on event title with Fullcalendar when the calendar is based on a feed and not a manual list of events?
I am running fullcalendar 3.9.0 (I may move to 3.10). It is the most current version I can run due to other software needing to be configured to work with version 4.
I am also employing the mozilla ical.js script to convert ical feeds into JSON feeds.
My expectation is that I may actually have two areas in which I could possibly filter:
within ical.js or ical_events.js
within my configuration for fullcalendar (maybe in the defaultView section)
Here is a portion of code I use to call up a calendar:
ics_sources = [
{
url:'https://www.example.com/calendaring/15/',
event_properties: {
color:'#7a9b49'
}
},
]
function data_req (url, callback) {
req = new XMLHttpRequest()
req.addEventListener('load', callback)
req.open('GET', url)
req.send()
}
function add_recur_events() {
if (sources_to_load_cnt < 1) {
$('#calendar').fullCalendar('addEventSource', expand_recur_events)
} else {
setTimeout(add_recur_events, 30)
}
}
function load_ics(ics){
data_req(ics.url, function(){
$('#calendar').fullCalendar('addEventSource', fc_events(this.response, ics.event_properties))
sources_to_load_cnt -= 1
})
}
$(document).ready(function() {
$('#calendar').fullCalendar({
header: {
left: '',
center: '',
right: ''
},
viewDisplay: function(view) {
parent.setIframeHeight(iframeId) ;
},
eventClick: function(event) {
window.open(event.url,);
return false;
},
defaultView: $(window).width() < 765 ? 'listYear':'listYear',
nowIndicator: false,
eventLimit: 4,
fixedWeekCount: false,
listDayFormat: 'MMMM Do',
listDayAltFormat: false,
noEventsMessage: "No Currently Scheduled Events"
})
sources_to_load_cnt = ics_sources.length
for (ics of ics_sources) {
load_ics(ics)
}
add_recur_events()
})
Expected (desired) results would be a list pre-filtered in the calendar call based on the desired event title for the end user.
Actual results: I currently do not have any pre-filtering going on.
Stripe connect accounts are configurable to coalesce payouts in a regular payout schedule, e.g. for monthly payouts in our case. For these monthly payouts we need to explain the account owners which of the transactions on our platform (bookings and refunds in our case) produced the overall amount they receive. We store the stripe charge id (resp. refund id) in the booking (resp refund) objects in our database. Thus the question boils down to:
Given a stripe account id, how can you get the list of stripe charge and refund ids that contributed to the last payout?
I've had an extensive exchange with Stripe's support team and there are several puzzle pieces necessary to get there:
Payouts are scoped by accounts
If you query stripe for a list of payouts, you will only receive the payout objects that you, the platform owner, get from stripe. To get the payout objects of a specific account you can use the normal authentication for the platform, but send the stripe account id as a header. So the code snippet to get the last payout looks like this (I'll use ruby snippets as examples for the rest of the answer):
Stripe::Payout.list({limit: 1}, {stripe_account: 'acct_0000001234567890aBcDeFgH'})
=> #<Stripe::ListObject:0x0123456789ab> JSON: {
"object": "list",
"data": [
{"id":"po_1000001234567890aBcDeFgH",
"object":"payout",
"amount":53102,
"arrival_date":1504000000,
"balance_transaction":"txn_2000001234567890aBcDeFgH",
"created":1504000000,
"currency":"eur",
"description":"STRIPE TRANSFER",
"destination":"ba_3000001234567890aBcDeFgH",
"failure_balance_transaction":null,
"failure_code":null,
"failure_message":null,
"livemode":true,"metadata":{},
"method":"standard",
"source_type":"card",
"statement_descriptor":"[…]",
"status":"paid",
"type":"bank_account"
}
],
"has_more": true,
"url": "/v1/payouts"
}
Having the payout id, we can query the list of balance transactions, scoped to a payout:
Stripe::BalanceTransaction.all({
payout: 'po_1000001234567890aBcDeFgH',
limit: 2,
}, {
stripe_account: 'acct_0000001234567890aBcDeFgH'
})
Objects viewed as an account are stripped of most information, compared to those viewed as a platform owner
Even though you now have the payout id, the object is still scoped to the account and you cannot retrieve it as platform owner. But viewed as an account, the payout only shows pseudo charge and refund objects like these (notice the second transaction has a py_7000001234567890aBcDeFgH object as a source instead of a regular ch_ charge object):
Stripe::BalanceTransaction.all({
payout: 'po_1000001234567890aBcDeFgH',
limit: 2,
}, {
stripe_account: 'acct_0000001234567890aBcDeFgH'
})
=> {
:object => "list",
:data => [
{
:id => "txn_4000001234567890aBcDeFgH",
:object => "balance_transaction",
:amount => -53102,
:available_on => 1504000000,
:created => 1504000000,
:currency => "eur",
:description => "STRIPE TRANSFER",
:fee => 0,
:fee_details => [],
:net => -53102,
:source => "po_5000001234567890aBcDeFgH",
:status => "available",
:type => "payout"
},
{
:id => "txn_6000001234567890aBcDeFgH",
:object => "balance_transaction",
:amount => 513,
:available_on => 1504000000,
:created => 1504000000,
:currency => "eur",
:description => nil,
:fee => 0,
:fee_details => [],
:net => 513,
:source => "py_7000001234567890aBcDeFgH",
:status => "available",
:type => "payment"
}
],
:has_more => true,
:url => "/v1/balance/history"
}
You can let stripe automatically expand objects in the response
As an additional parameter, you can give stripe paths of objects which you want stripe to expand in their response. Thus we can walk from the pseudo objects back to the original charge objects via the transfers:
Stripe::BalanceTransaction.all({
payout: 'po_1000001234567890aBcDeFgH',
limit: 2,
expand:['data.source.source_transfer',]
}, {
stripe_account: 'acct_0000001234567890aBcDeFgH'
}).data.second.source.source_transfer.source_transaction
=> "ch_8000001234567890aBcDeFgH"
And if you want to process the whole list you need disambiguate between the source.object attribute:
Stripe::BalanceTransaction.all({
payout: 'po_1000001234567890aBcDeFgH',
limit: 2,
expand:['data.source.source_transfer',]
}, {
stripe_account: 'acct_0000001234567890aBcDeFgH'
}).data.map do |bt|
if bt.source.object == 'charge'
['charge', bt.source.source_transfer.source_transaction]
else
[bt.source.object]
end
end
=> [["payout"], ["charge", "ch_8000001234567890aBcDeFgH"]]
Refunds have no connecting object path back to the original ids
Unfortunately, there is currently no way to get the original re_ objects from the pseudo pyr_ that are returned by the BalanceTransaction list call for refund transactions. The best alternative I've found is to go via the data.source.charge.source_transfer.source_transaction path to get the charge id of the charge on which the refund was issued and use that in combination with the created attribute of the pyr_ to match our database refund object. I'm not sure, though, how stable that method really is. The code to extract that data:
Stripe::BalanceTransaction.all({
payout: 'po_1000001234567890aBcDeFgH',
limit: 100, # max page size, the code to iterate over all pages is TBD
expand: [
'data.source.source_transfer', # For charges
'data.source.charge.source_transfer', # For refunds
]
}, {
stripe_account: 'acct_0000001234567890aBcDeFgH'
}).data.map do |bt|
res = case bt.source.object
when 'charge'
{
charge_id: bt.source.source_transfer.source_transaction
}
when 'refund'
{
charge_id: bt.source.charge.source_transfer.source_transaction
}
else
{}
end
res.merge(type: bt.source.object, amount: bt.amount, created: bt.created)
end
It is now possible to get the refund ids via a "transfer reversal" object:
Stripe::BalanceTransaction.list({
payout: 'po_1000001234567890aBcDeFgH',
expand: [
'data.source.source_transfer', # For charges
'data.source.transfer_reversal', # For refunds
]
}, {
stripe_account: 'acct_0000001234567890aBcDeFgH'
}).auto_paging_each do |balance_transaction|
case balance_transaction.type
when 'payment'
charge_id = balance_transaction.source.source_transfer.source_transaction
when 'payment_refund'
refund_id = balance_transaction.source.charge.source_transfer.source_transaction
end
end
end
I'm using Google APIs to get the user's calendar events and contacts.
While fetching the contacts, I get the response in the following manner:-
[
{
'phones': [],
'image_path': '',
'id': 'ID',
'emails': ['email1'],
'name': ABC
},
{
'phones': [],
'image_path': '',
'id': 'ID',
'emails': ['email2'],
'name': DEF
}
]
While fetching the events, I get the follwoing response:-
[
{
'attendees': [{
'organizer': True,
'displayName': 'ABC',
'id': 'Google+ Id',
'responseStatus': 'accepted'
}, {
'self': True,
'displayName': 'DEF',
'id': 'Google+ id',
'responseStatus': 'accepted'
}],
'organizer': {
'displayName': 'ABC',
'id': 'Google+ id'
},
'creator': {
'displayName': 'ABC',
'id': 'Google+ id'
},
},
{
'organizer': {
'self': True,
'displayName': 'DEF',
'email': 'email2'
},
'creator': {
'self': True,
'displayName': 'DEF',
'email': 'email2'
},
}
]
As you can see that while fetching events, (in attendees, organizers, creators) I get Google+ id in some cases and email_ids in other cases. This does not maintain a uniformity in my code.
Since I've fetched the user contacts as well, and I search the contacts via their email_ids. If I don't get an email_id in attendees, organizers, or creators, I won't be able to reference the contact object.
How can I make sure that I get only the email_ids in attendees, not the Google+ id.
According to Google Calendar API docs
Optional query parameters
alwaysIncludeEmail
boolean Whether to always include a value in the email field for the
organizer, creator and attendees, even if no real email is available
(i.e. a generated, non-working value will be provided). The use of
this option is discouraged and should only be used by clients which
cannot handle the absence of an email address value in the mentioned
places. Optional. The default is False.
Anyway it is not encouraged to use it , because sometimes no real email is available.
Work around:
You can use G+ API to fetch user Email through providing his/her email.
email
This scope requests that your app be given access to:
the user's Google account email address. You access the email address by calling people.get, which returns the emails array (or by calling people.getOpenIdConnect, which returns the email property in OIDC-compliant format).
the name of the Google Apps domain, if any, that the user belongs to. The domain name is returned as the domain property from
people.get (or hd property from getOpenIdConnect)
This email scope is equivalent to and replaces the https://www.googleapis.com/auth/userinfo.email scope.
I'm starting to use Request objects to validate incoming post data, and I've seen examples of how others are using date_format, but I can't seem to get dates to pass even though I'm using a format that passes when you use PHP's date_parse_from_format:
print_r(date_parse_from_format('Y-m-d','2015-07-27'));
Output
UPDATE: their is a warning in date_parse_from_format, but I don't understand why as it reflects the format.
{
"year": 2015,
"month": 2,
"day": 30,
"hour": false,
"minute": false,
"second": false,
"fraction": false,
"warning_count": 1,
"warnings": {
"10": "The parsed date was invalid"
},
"error_count": 0,
"errors": [],
"is_localtime": false
}
Validator
public function rules()
{
return [
'rental_id' => 'required|exists:rentals,id',
'start_at' => 'required|date_format:Y-m-d',
'end_at' => 'required|date_format:Y-m-d'
];
}
Using PostMan I'm sending in a payload of:
rental_id = 1 as text
start_at = 2015-02-30 as text
end_at = 2015-02-30 as text
And it throws a NotFoundHttpException, but if I comment out start_at and end_at in the validator request it passes, and enters my controllers action with the proper payload:
{
"rental_id": "1",
"start_at": "2015-02-30",
"end_at": "2015-02-30"
}
Apparently, the date_format validation failed as I randomly chose 2015-02-30 to test my API, but that day doesn't exist as that would be February 30... oh the shame and the wasted time. Thanks to #ceejayoz for all the help!!!
I'm working with FullCalendar fetching events from a json feed. However, I've run into a problem.
The calendar shows my event just fine in the month view, but when going to the day/week in day-view and week-view, the even is not shown?
My initialization of fullcalendar looks like this:
var options = {
header: {
left: 'prev, next',
center: 'title',
right: 'month,agendaWeek,agendaDay'
},
allDaySlot: false,
monthNames: ["Januar","Februar","Marts","April","Maj","Juni","Juli", "August", "September", "Oktober", "November", "December" ],
monthNamesShort: ['Jan','Feb','Mar','Apr','Maj','Jun','Jul','Aug','Sep','Okt','Nov','Dec'],
dayNames: ['Søndag', 'Mandag', 'Tirsdag', 'Onsdag', 'Torsdag', 'Fredag', 'Lørdag'],
dayNamesShort: ['Søn','Man','Tir','Ons','Tor','Fre','Lør'],
buttonText: {
today: 'I dag',
month: 'Måned',
week: 'Uge',
day: 'Dag'
},
weekends: false,
defaultView: 'agendaWeek',
events: '/feed/',
firstHour: 8,
slotMinutes: 20,
defaultEventMinutes: 120,
axisFormat: 'HH:mm',
timeFormat: {
agenda: 'H:mm{ - h:mm}'
},
minTime: 8,
maxTime: 16
};
$('#calendar-holder').fullCalendar(options);
My feed returns the following json:
[{"id":"7","title":"Elias (10:00)","start":1359108000,"end":1359109800,"url":"\/tidsbestillinger\/edit\/"}]
Any ideas why this is happening?
Solved! Had to set
allDayDefault: false,
while initializing FullCalendar.
Alternatively I could have set
allDay = false
in the JSON - but doing it globally is much easier.
Also make sure that event length must be enough that it can be displayed in slotDuration.
e.g.
my slotDuration is 15 minutes, my event only displayed if it has minimum 2 minutes duration.