When you work internationally and support multiple time zones, the problem of associating holidays and business hours can get super hairy.
So you ask, what is the joiner Object? I can just data load that, right? Oh no... that would be too easy.
(Salesforce VERY GOOD Idea for it: https://success.salesforce.com/ideaview?id=087300000006n4CAAQ)
So when you have one country that works the same hours in the same time zone, easy breezy.
You go to Setup, Company Settings, Business Hours (and add Holidays) OR go to Company Settings, Holidays and add Business Hours
Now, you could start a giant opioid addiction and enter the holidays manually, or, google your way to the magical and lengthy book called the Metadata API Guide: Metadata API Guide - BHSettings
A word of warning though, when you edit this code, it does NOT upsert. If you delete pieces to make it more manageable, and do not add them back in before you reload - it could be a dreadful day. My first time, anything I didn't load in was just Poof! All those BH and holidays that I deleted because I wasn't editing them, gone.
So make a backup. And test it in your sandbox or a Dev org, making sure to validate all the places that Business Hours show up correctly after your edits (Accounts, Entitlements, etc.)
You can do this in VS Code or use Packages and Workbench. The Object you want is called BusinessHoursSettings.
The package code below will pull this for you:
<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
<types>
<members>BusinessHours</members>
<name>Settings</name>
</types>
<version>47.0</version>
</Package>
You can either "Retrieve Source from Org"in VS Code or "Migration/Retrieve" in Workbench to get the settings to edit.
Workbench:
Sign in, Click Migration, then Retrieve. Select your file with the above code in it and click next.
Then Download the zip file and you will have the BH settings in the Settings Folder.
VS Code:
Login, Retrieve Source from Org ,and open the file in the Settings folder.
Once you have the code, you can just put the business hours in the holiday, as below.
You can also add BH and holidays here as long as there are no duplicate business hours and you don't add more than 1000 Business hours to a holiday.
<holidays>
<businessHours>Add Up to 1000 Business Hours Here</businessHours>
<businessHours>The Name is Unique so That is Used Here</businessHours>
<activityDate>2020-02-21</activityDate>
<description>IN,MU, NP,LK, IN</description>
<isRecurring>false</isRecurring>
<name>Mahashivaratri 2020</name>
</holidays>
Starting in your sandbox or a Dev org, load the Hours back in. If you are using workbench, zip it up and click Migration, Deploy. For VS Code, Deploy Source to Org.
VSCode Deploys and some abbreviations you will need:
[--json] [--loglevel LOGLEVEL] [-u TARGETUSERNAME]
[-d DEPLOYDIR] [-w WAIT] [--apiversion APIVERSION]
[-l TESTLEVEL] [-r RUNTESTS] [-q VALIDATEDDEPLOYREQUESTID]
[--verbose] [-f ZIPFILE]
1. Convert to metadata
Command: sfdx force:source:convert -r /Downloads/boo/bh/force-app/settings -d tmpBH
Or: sfdx force:source:convert -r /Downloads/boo/bh/force-app/settings
Response: Source was successfully converted to Metadata API format and written to the location: …/boo/bh /metadataPackage_1581106935454
2. Make the deploy directory a zip file. Above, either tmpBH or metadataPackage_1581106935454.
3. Deploy to your QA sandbox and then your full sandbox. If you only have one, you can also deploy to a dev org to test.
Command: sfdx force:mdapi:deploy --checkonly -f BHSetPack.zip -u hawksandbox -l RunSpecifiedTests -r TestBusinessHours
Get the jobID for the next step: sfdx force:mdapi:deploy:report
Job ID | 0Af6g00000Mc8GNCAZ
MDAPI PROGRESS | ████████████████████████████████████████ | 3/3 Files
Deploy command: sfdx force:mdapi:deploy -u hawksandbox –q 0Af6g00000Mc8GNCAZ
4. Deploy in Production Org:
a. sfdx force:mdapi:deploy --checkonly -f BHSetPack.zip -u hawkProduction -l RunSpecifiedTests -r
TestBusinessHours
b. sfdx force:mdapi:deploy:report
Job ID | 0Af6g00000Mc8GNCAZ
MDAPI PROGRESS | ████████████████████████████████████████ | 3/3 Files
c. sfdx force:mdapi:deploy -u hawkproduction –q 0Af6g00000Mc8GNCAZ
Once the code is deployed, note the ID and describe it in your change management tool (preferably with a link!)
If one of your code deployments failed, note that in the case, as well, so there are no orphan deployments. Because Change Sets are viewable and already have a label that is human readable, they don't need to be noted.
Name: Unique name of the business hours.
timeZoneId: for the time that defines business hours. This the IANA time zone (America/New_York,)
Active: are the business hours active?
Default: are the business hours used as the default?
There is a start and end time for each day of the week with the format HH:mm:ss.SSSZ. The value 00:00:00.000Z specifies midnight; so a start of 00:00:00.100Z and end of 00:00:00.000Z would be 24 hours. No data in the start or end means no support those days (ie weekends.)
Fields on Holiday:
Name: name of the holiday. This name does not have to be unique.
Description: description of the holiday.
isRecurring: Indicates whether the holiday is recurring.
activityDate: Required, The date of the holiday. format HH:mm:ss.SSSZ.
recurrenceStartDate: date the holiday starts recurring. Uses the format yyyy-mm-dd. Matches the first activity date.
recurrenceEndDate: date the holiday stops recurring. Uses the format yyyy-mm-dd. Optional.
startTime: start time on the date of the holiday. Uses the format HH:mm:ss.SSSZ. startTime and endTime must be both null or both not null. If they are both null, indicates the whole day. Generally blank.
endTime: end time on the date of the holiday. Uses the format HH:mm:ss.SSSZ. startTime and endTime must be both null or both not null. If they are both null, indicates the whole day. Generally blank
recurrenceType: recurrence type of the holiday. Valid values are: RecursDaily, RecursEveryWeekday, RecursMonthly, RecursMonthlyNth, RecursWeekly, RecursYearly, RecursYealyNth.
recurrenceInterval: interval of weeks, months, or years the holiday recurs.
recurrenceDayOfWeek: day of week the holiday recurs. Valid values: Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday.
I have also seen day of week mask: It is a number and it translates as follows: Sunday = 1; Monday = 2; Tuesday = 4; Wednesday = 8; Thursday = 16; Friday = 32; Saturday = 64; but it may be a remnant of classic.
recurrenceDayOfMonth: day of month the holiday recurs. Valid values: integers 1-31.
recurrenceInstance: Valid values: First, Second, Third, Fourth, Last. Only used for recurrenceType RecursMonthlyNth and RecursYearlyNth. For example, if the recurenceInstance value is First, the holiday recurs on the first Monday of the month every 3 months.
recurrenceMonthOfYear: Valid values: January, February, March, April, May, June, July, August, September, October, November, December.
businessHours: name of the business hours setting that applies to this holiday.
The XML is in a Gist and will look like this:
<?xml version="1.0" encoding="UTF-8"?>
<BusinessHoursSettings xmlns="http://soap.sforce.com/2006/04/metadata">
<!-- The file starts with the business hours and then the holidays. To associate the two, you add the business hours to the holiday xml. -->
<!-- The business hours can start anywhere in between the <holiday> and </holiday> tags
row 0 <holiday> then row 0 <holiday> then
row 1 <businesshours> OR row 1 <description>
row 2 <description> row 2 <businesshours>
Then the rest of the holiday. -->
<!-- DO NOT USE MY NAMES FOR HOLIDAYS. Use teh holiday name and either the area wherer it is celebrated or the month/day so you can tell them apart when adding manually
Manual adds will eventually be done and adding and having to trouble shoot because no one put unique names in is a nightmare... -->
<businessHours>
<active>true</active>
<default>false</default>
<fridayEndTime>17:00:00.000Z</fridayEndTime>
<fridayStartTime>07:00:00.000Z</fridayStartTime>
<mondayEndTime>17:00:00.000Z</mondayEndTime>
<mondayStartTime>07:00:00.000Z</mondayStartTime>
<name>Business Hours Name 1</name>
<thursdayEndTime>17:00:00.000Z</thursdayEndTime>
<thursdayStartTime>07:00:00.000Z</thursdayStartTime>
<timeZoneId>GMT</timeZoneId>
<tuesdayEndTime>17:00:00.000Z</tuesdayEndTime>
<tuesdayStartTime>07:00:00.000Z</tuesdayStartTime>
<wednesdayEndTime>17:00:00.000Z</wednesdayEndTime>
<wednesdayStartTime>07:00:00.000Z</wednesdayStartTime>
</businessHours>
<businessHours>
<active>true</active>
<default>false</default>
<fridayEndTime>18:00:00.000Z</fridayEndTime>
<fridayStartTime>08:00:00.000Z</fridayStartTime>
<mondayEndTime>18:00:00.000Z</mondayEndTime>
<mondayStartTime>08:00:00.000Z</mondayStartTime>
<name>Business Hours Name 2</name>
<thursdayEndTime>18:00:00.000Z</thursdayEndTime>
<thursdayStartTime>08:00:00.000Z</thursdayStartTime>
<timeZoneId>Australia/Adelaide</timeZoneId>
<tuesdayEndTime>18:00:00.000Z</tuesdayEndTime>
<tuesdayStartTime>08:00:00.000Z</tuesdayStartTime>
<wednesdayEndTime>18:00:00.000Z</wednesdayEndTime>
<wednesdayStartTime>08:00:00.000Z</wednesdayStartTime>
</businessHours>
<businessHours>
<active>true</active>
<default>false</default>
<fridayEndTime>18:00:00.000Z</fridayEndTime>
<fridayStartTime>08:00:00.000Z</fridayStartTime>
<mondayEndTime>18:00:00.000Z</mondayEndTime>
<mondayStartTime>08:00:00.000Z</mondayStartTime>
<name>NY_US_EST</name>
<thursdayEndTime>18:00:00.000Z</thursdayEndTime>
<thursdayStartTime>08:00:00.000Z</thursdayStartTime>
<timeZoneId>Australia/Adelaide</timeZoneId>
<tuesdayEndTime>18:00:00.000Z</tuesdayEndTime>
<tuesdayStartTime>08:00:00.000Z</tuesdayStartTime>
<wednesdayEndTime>18:00:00.000Z</wednesdayEndTime>
<wednesdayStartTime>08:00:00.000Z</wednesdayStartTime>
</businessHours>
<holidays>
<activityDate>2020-02-21</activityDate>
<businessHours>Business Hours Name 2</businessHours>
<description>IN,MU, NP,LK, IN</description>
<isRecurring>false</isRecurring>
<name>Mahashivaratri 2020</name>
</holidays>
<holidays>
<businessHours>Business Hours Name 1</businessHours>
<description>AU-NT, IE, </description>
<isRecurring>true</isRecurring>
<name>May Day</name>
<recurrenceDayOfMonth>1</recurrenceDayOfMonth>
<recurrenceMonthOfYear>May</recurrenceMonthOfYear>
<recurrenceStartDate>2020-05-01</recurrenceStartDate>
<recurrenceType>RecursYearly</recurrenceType>
</holidays>
<holidays>
<businessHours>Business Hours Name 2</businessHours>
<businessHours>NY</businessHours>
<description>AS, US</description>
<isRecurring>true</isRecurring>
<name>President's Day</name>
<recurrenceDayOfWeek>Monday</recurrenceDayOfWeek>
<recurrenceInstance>Third</recurrenceInstance>
<recurrenceMonthOfYear>February</recurrenceMonthOfYear>
<recurrenceStartDate>2020-02-17</recurrenceStartDate>
<recurrenceType>RecursYearlyNth</recurrenceType>
</holidays>
</BusinessHoursSettings>