Export Bookable Resource Working Hours using ExpandCalendar (Dynamics 365 Field Service)


Recently we had a requirement to export the working hours of Bookable Resources from Dynamics 365 for a specified date range and make that information available to a third-party system.

Rather than reading the underlying calendar rules and building our own logic to calculate the working hours, we looked at using the platform capabilities provided by Dynamics 365 and used the Dataverse ExpandCalendar function

How it works

We first retrieve the Bookable Resources from Dynamics 365 and identify the calendar associated with each resource.

For each calendar, we call the Dataverse ExpandCalendar function for the required date range. Dynamics 365 expands the calendar rules and returns the resulting calendar blocks, including the start and end times along with information such as TimeCode and SubCode.

We then process these results and export the required information to a CSV file.

This approach means we do not need to recreate the calendar rules and recurrence logic ourselves. Dynamics 365 does the calendar expansion for us, and we simply consume the resulting calendar blocks.

Why use ExpandCalendar?

A Bookable Resource calendar is not simply a list of start and end times. Calendar rules can repeat over time and can include changes or exceptions. Building a separate calendar engine to interpret those rules would duplicate logic that Dynamics 365 already understands.

With ExpandCalendar, we provide the calendar and the date range, and Dynamics 365 returns the expanded calendar information for that period.

This makes the solution smaller and easier to maintain, particularly when the calendar configuration changes in Dynamics 365.

Getting the Bookable Resources

The first step is to retrieve the Bookable Resources that we want to process. The important fields for this example are the resource ID, resource name, and calendar ID.

private static List<Entity> GetAllBookableResources(
    IOrganizationService service)
{
    QueryExpression query = new QueryExpression
    {
        EntityName = "bookableresource",
        ColumnSet = new ColumnSet(
            "bookableresourceid",
            "name",
            "calendarid")
    };

    return service.RetrieveMultiple(query).Entities.ToList();
}

The calendar ID is important because it is the calendar that we pass to ExpandCalendar.

Calling ExpandCalendar

The ExpandCalendar function is exposed through the Dataverse Web API. In a .NET console application using Microsoft.PowerPlatform.Dataverse.Client, we can execute the Web API request through the ServiceClient.

private static async Task<List<CalendarBlock>> ExpandCalendarAsync(
    ServiceClient serviceClient,
    Guid calendarId,
    DateTime startUtc,
    DateTime endUtc)
{
    string requestUrl =
        serviceClient.ConnectedOrgUriActual.ToString().TrimEnd('/') +
        "/api/data/v9.2/ExpandCalendar";

    var request = new HttpRequestMessage(
        HttpMethod.Post,
        requestUrl);

    request.Headers.Add(
        "OData-Version",
        "4.0");

    request.Headers.Add(
        "OData-MaxVersion",
        "4.0");

    var body = new
    {
        CalendarId = calendarId,
        Start = startUtc.ToString("yyyy-MM-ddTHH:mm:ssZ"),
        End = endUtc.ToString("yyyy-MM-ddTHH:mm:ssZ")
    };

    string json = JsonConvert.SerializeObject(body);

    request.Content = new StringContent(
        json,
        Encoding.UTF8,
        "application/json");

    HttpResponseMessage response =
        await serviceClient.ExecuteWebRequestAsync(request);

    response.EnsureSuccessStatusCode();

    string responseJson =
        await response.Content.ReadAsStringAsync();

    JObject responseObject =
        JObject.Parse(responseJson);

    List<CalendarBlock> result =
        responseObject["value"]
            .ToObject<List<CalendarBlock>>();

    return result;
}

The exact request shape can vary depending on the Dataverse API version and implementation, so the ExpandCalendar Web API documentation should be used as the authoritative reference for the request parameters.

Working with the response

The response contains the expanded calendar blocks for the requested period. A simplified response can look like this:

{
  "Start": "2026-10-07T19:00:00Z",
  "End": "2026-10-08T04:00:00Z",
  "TimeCode": "Available",
  "SubCode": "Schedulable"
}

For each block, we can capture the start and end time together with TimeCode and SubCode. The application can then transform these results into the format required by the consuming system.

Working hours vs actual availability

One important distinction is that expanded calendar information should not automatically be treated as the complete scheduling availability of a resource.

ExpandCalendar gives us the expanded calendar information for the requested period. Actual scheduling availability can also be affected by existing bookings, requirements, constraints, and other scheduling factors.

If the requirement is to determine when a resource can actually be booked, the Field Service resource availability APIs should be considered instead. For a requirement focused on exporting configured calendar or working-hour information, ExpandCalendar is a good fit.

Refereces

Hope it helps..

Advertisements

Discover more from Nishant Rana's Weblog

Subscribe to get the latest posts sent to your email.

Unknown's avatar

Author: Nishant Rana

I love working in and sharing everything about Microsoft.NET technology !

Please share your thoughts

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Discover more from Nishant Rana's Weblog

Subscribe now to keep reading and get access to the full archive.

Continue reading

Power Platform Puzzles

D365 CRM, Power Platform Tips &Tricks

Power Spark

Power Spark By Shrangarika

Van Carl Nguyen

Exploration of Power Platform

My Trial

It is my experience timeline.

Arpit Power Guide

a guide to powering up community

Welcome to the Blog of Paul Andrew

Sponsored by Cloud Formations Ltd

Deriving Dynamics 365

Deriving Solutions and features on Power Platform/Dynamics 365

The CRM Ninja

Thoughts & musings from a Microsoft Business Applications Ninja!

D CRM Explorer

Learn about Microsoft Dynamics CRM Power Platform customization and implementation and other cool stuffs

Stroke // Jonas Rapp

I know pre-stroke. I will improve who I was.

Power Melange

Power Melange By Shalinee

Clavin's Blog - PPUG.ORG

AI - Power Automate - Power Apps - SharePoint Online - Azure - Nintex - K2 - Artificial Intelligence

Sat Sangha Salon

An Inquiry in Being

The Indoencers

The Influencers & Influences of Indian Music

Monika Halan's blog

Hand's-free money management

D365 Demystified

A closer look at Microsoft Dynamics 365.

Microsoft Mate (msftmate) - Andrew Rogers

Experienced consultant primarily focused on Microsoft Dynamics 365 and the Power Platform

Manmit Rahevar's Blog

One Stop Destination for Microsoft Technology Solutions

MG

Naturally Curious

Brian Illand

Power Platform and Dynamics 365

Steve Mordue

The Professional Paraphraser

Subwoofer 101

Bass defines your home theater

SQLTwins by Nakul Vachhrajani

SQL Server tips and experiences dedicated to my twin daughters.

Everything D365

Discovering Azure DevOps and D365 Business Applications

Tech Wizard

Lets do IT Spells

XRM Tricks (Power Platform & Dynamics CRM )

Power Platform & Dynamics CRM

CRM TIPS BY PRM

Mail to crmtipsbyprm@gmail.com for queries and suggestions

nijos.dev

Giving back to the community what I have learned

Power Platform Learning

Your Go-To Resource for Power Apps, Power Automate & More

xrm CRM Dynamics

Dynamics CRM Technical & Functional Info

Dynamics 365 Blogs - Explained in unique way

Sometimes you need to look at things from different perspective.

CRM Keeper

Dynamics 365 Customer Engagement, CRM, Microsoft CRM, Dynamics CRM