Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
209 changes: 209 additions & 0 deletions docs/groovy/how-to-guides/business-calendar.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
---
title: Work with calendars
---

This guide will show you how to create and use business calendars in Deephaven. In Deephaven, the calendar API is centered around the [`BusinessCalendar`](/core/javadoc/io/deephaven/time/calendar/BusinessCalendar.html) object, which is a calendar with the concept of business and non-business time. These calendars are highly useful in both Groovy code and Deephaven tables.

## Get a calendar

Getting a calendar is simple. The code block below lists the available calendars and grabs the `USNYSE_EXAMPLE` calendar.

```groovy test-set=1 order=:log
import static io.deephaven.time.calendar.Calendars.calendar
import static io.deephaven.time.calendar.Calendars.calendarNames

println calendarNames()
nyseCal = calendar("USNYSE_EXAMPLE")
println nyseCal.getClass()
```

We can see from the output that `nyseCal` is an [`io.deephaven.time.calendar.BusinessCalendar`](/core/javadoc/io/deephaven/time/calendar/BusinessCalendar.html) object. It's Deephaven's business calendar object. A `BusinessCalendar` has many different methods available that can be useful in queries. The sections below explore those uses.

## Business calendar use

### Input data types

`BusinessCalendar` methods accept either strings or Java date-time types ([`Instant`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/time/Instant.html), [`ZonedDateTime`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/time/ZonedDateTime.html), [`LocalDate`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/time/LocalDate.html)). See [Time in Deephaven](../conceptual/time-in-deephaven.md#1-built-in-java-functions) for conversion functions.

### Create data

Before we can demonstrate the use of business calendars in queries, we'll need to create a table with some data. The following code block creates a month's worth of date-time data spaced 3 minutes apart.

```groovy test-set=1 order=source
// Create sample data
source = emptyTable(10000).update(
"Timestamp = '2024-01-01T00:00:00 ET' + i * 3 * MINUTE",
"Value = randomGaussian(0.0, 0.1)"
)
```

### Business days and business time

The following example calculates the number of business days and non-business days (weekends & holidays) between two timestamps.

```groovy test-set=1 order=result
result = source.update(
"NumBizDays = nyseCal.numberBusinessDates('2024-01-01T00:00:00 ET', Timestamp)",
"NumNonBizDays = nyseCal.numberNonBusinessDates('2024-01-01T00:00:00 ET', Timestamp)"
)
```

The following example shows how to filter data to only business days and business hours. The `source` table is [filtered](./filters.md) twice to create two result tables. The first contains only data that takes place during an NYSE business day, while the second contains only data that takes place during NYSE business hours.

```groovy test-set=1 order=resultBizDays,resultBizHours
resultBizDays = source.where("nyseCal.isBusinessDay(Timestamp)")

resultBizHours = source.where("nyseCal.isBusinessTime(Timestamp)")
```

These filtered tables can be used for analysis, reporting, or plotting data that occurs only during business days or business hours.

## Create a calendar

Deephaven offers [three pre-built calendars](#get-a-calendar) for use: `UTC`, `USNYSE_EXAMPLE`, and `USBANK_EXAMPLE`.

> [!WARNING]
> The calendars that come with Deephaven are meant to serve as examples. They may not be updated. Deephaven recommends users create their own calendars.

The calendar configuration files can be found [here](https://github.com/deephaven/deephaven-core/tree/main/props/configs/src/main/resources/calendar). They use [XML](https://en.wikipedia.org/wiki/XML) to define the properties of the calendar, which include:

- Valid date range
- Country and time zone
- Description
- Operating hours
- Holidays
- More

Users can build their own calendars by creating a calendar file using the format described in [this Javadoc](/core/javadoc/io/deephaven/time/calendar/BusinessCalendarXMLParser.html). This section goes over an example of using a custom-built calendar for a hypothetical business for the year 2024.

This example uses a calendar file found in Deephaven's [examples repository](https://github.com/deephaven/examples/tree/main/Calendar). This guide assumes you have the file on your local machine in the [/data mount point](../conceptual/docker-data-volumes.md). This hypothetical business is called "Company Y", and the calendar only covers the year 2024.

### The calendar file

A calendar XML file contains top-level information about the calendar itself, business days, business hours, and holidays. While most business calendars have a single business period (e.g., 9am - 5pm), some use two distinct business periods separated by a lunch break. The test calendar file below has two distinct periods: from 8am - 12pm and from 1pm - 5pm. It also specifies a series of holidays over the course of the 2024 calendar year, which includes two half-holidays in which business is open for the first of the two business periods. Calendars typically contain data for more than one year, but this example limits it to 2024 only.

The `TestCalendar_2024.calendar` file can be found [here](https://github.com/deephaven/examples/blob/main/Calendar/TestCalendar_2024.calendar). To see its contents, expand the file below. For the examples that use this calendar, it's placed in the folder `/data/examples/Calendar/` in the [Deephaven Docker container](../conceptual/docker-data-volumes.md).

<details>
<summary>Test calendar for 2024</summary>

```xml
<calendar>
<name>TestCalendar_2024</name>
<timeZone>America/New_York</timeZone>
<language>en</language>
<country>US</country>
<firstValidDate>2024-01-01</firstValidDate>
<lastValidDate>2024-12-31</lastValidDate>
<description>
Test calendar for the year 2024.
This calendar uses two business periods instead of one.
The periods are separated by a one hour lunch break.
This calendar file defines standard business hours, weekends, and holidays.
</description>
<default>
<businessTime><open>08:00</open><close>12:00</close><open>13:00</open><close>17:00</close></businessTime>
<weekend>Saturday</weekend>
<weekend>Sunday</weekend>
</default>
<holiday>
<date>2024-01-01</date>
</holiday>
<holiday>
<date>2024-01-15</date>
</holiday>
<holiday>
<date>2024-02-19</date>
</holiday>
<holiday>
<date>2024-03-29</date>
</holiday>
<holiday>
<date>2024-04-01</date>
<businessTime><open>08:00</open><close>12:00</close></businessTime>
</holiday>
<holiday>
<date>2024-05-27</date>
</holiday>
<holiday>
<date>2024-07-04</date>
</holiday>
<holiday>
<date>2024-09-02</date>
</holiday>
<holiday>
<date>2024-10-31</date>
<businessTime><open>08:00</open><close>12:00</close></businessTime>
</holiday>
<holiday>
<date>2024-11-28</date>
</holiday>
<holiday>
<date>2024-11-29</date>
</holiday>
<holiday>
<date>2024-12-25</date>
</holiday>
<holiday>
<date>2024-12-26</date>
</holiday>
</calendar>
```

</details>

For more information on formatting custom calendars, see the [XML Parser Javadoc](/core/javadoc/io/deephaven/time/calendar/BusinessCalendarXMLParser.html).

## Use the new calendar

### Add the calendar to the set of available calendars

There are two ways to add a calendar to the set of available calendars.

The first and simplest way to do so is through the calendar API. The following code block shows how it's done using the path to the calendar file just created.

```groovy skip-test
import static io.deephaven.time.calendar.Calendars.addCalendarFromFile

addCalendarFromFile("/data/examples/Calendar/TestCalendar_2024.calendar")
```

The second way is through the configuration property `Calendar.importPath`. This should point to a text file with line-separated locations of any calendar files to load by default. Say your Docker configuration has a `/data/Calendar` folder that contains three calendar files: `MyCalendar.calendar`, `TestCalendar_2024.calendar`, `CrazyCalendar.calendar`. The text file, which we'll name `calendar_imports.txt` and place in the root of your Deephaven deployment, would look as follows:

```txt
/data/Calendar/MyCalendar.calendar
/data/Calendar/TestCalendar_2024.calendar
/data/Calendar/CrazyCalendar.calendar
```

To make Deephaven load this list of calendars automatically upon startup via [`docker compose`](https://docs.docker.com/compose/), you can set the property directly:

```yaml
services:
deephaven:
image: ghcr.io/deephaven/server:${VERSION:-latest}
ports:
- "${DEEPHAVEN_PORT:-10000}:10000"
volumes:
- ./data:/data
environment:
- START_OPTS=-Xmx4g -DCalendar.importPath="/calendar_imports.txt"
```

Alternatively, a [configuration file](./configuration/config-file.md) could be used to set the property.

### Get an instance of the new calendar

```groovy skip-test
import static io.deephaven.time.calendar.Calendars.calendar

test2024Cal = calendar("TestCalendar_2024")
```

Happy calendar-ing!

## Related documentation

- [Time in Deephaven](../conceptual/time-in-deephaven.md)
- [BusinessCalendar Javadoc](/core/javadoc/io/deephaven/time/calendar/BusinessCalendar.html)
- [XML Parser Javadoc](/core/javadoc/io/deephaven/time/calendar/BusinessCalendarXMLParser.html)
4 changes: 4 additions & 0 deletions docs/groovy/sidebar.json
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,10 @@
"label": "Work with time",
"path": "conceptual/time-in-deephaven.md"
},
{
"label": "Work with calendars",
"path": "how-to-guides/business-calendar.md"
},
{
"label": "Downsample data",
"path": "how-to-guides/downsampling.md"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"file":"how-to-guides/business-calendar.md","objects":{":log":{"type":"Log","data":"[USBANK_EXAMPLE, USNYSE_EXAMPLE, UTC]\nclass io.deephaven.time.calendar.BusinessCalendar\n"}}}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"file":"how-to-guides/business-calendar.md","objects":{"source":{"type":"Table","data":{"columns":[{"name":"Timestamp","type":"java.time.Instant"},{"name":"Value","type":"double"}],"rows":[[{"value":"2024-01-01 00:00:00.000"},{"value":"-0.1041"}],[{"value":"2024-01-01 00:03:00.000"},{"value":"-0.0191"}],[{"value":"2024-01-01 00:06:00.000"},{"value":"0.1638"}],[{"value":"2024-01-01 00:09:00.000"},{"value":"0.1044"}],[{"value":"2024-01-01 00:12:00.000"},{"value":"-0.0856"}],[{"value":"2024-01-01 00:15:00.000"},{"value":"-0.0050"}],[{"value":"2024-01-01 00:18:00.000"},{"value":"-0.1361"}],[{"value":"2024-01-01 00:21:00.000"},{"value":"-0.0545"}],[{"value":"2024-01-01 00:24:00.000"},{"value":"-0.1362"}],[{"value":"2024-01-01 00:27:00.000"},{"value":"-0.1538"}],[{"value":"2024-01-01 00:30:00.000"},{"value":"-0.1054"}],[{"value":"2024-01-01 00:33:00.000"},{"value":"-0.0835"}],[{"value":"2024-01-01 00:36:00.000"},{"value":"-0.1449"}],[{"value":"2024-01-01 00:39:00.000"},{"value":"0.0595"}],[{"value":"2024-01-01 00:42:00.000"},{"value":"0.1696"}],[{"value":"2024-01-01 00:45:00.000"},{"value":"0.1292"}],[{"value":"2024-01-01 00:48:00.000"},{"value":"-0.0623"}],[{"value":"2024-01-01 00:51:00.000"},{"value":"0.1685"}],[{"value":"2024-01-01 00:54:00.000"},{"value":"-0.0306"}],[{"value":"2024-01-01 00:57:00.000"},{"value":"0.0395"}],[{"value":"2024-01-01 01:00:00.000"},{"value":"0.0462"}],[{"value":"2024-01-01 01:03:00.000"},{"value":"0.1159"}],[{"value":"2024-01-01 01:06:00.000"},{"value":"-0.1283"}],[{"value":"2024-01-01 01:09:00.000"},{"value":"0.0265"}],[{"value":"2024-01-01 01:12:00.000"},{"value":"0.1535"}],[{"value":"2024-01-01 01:15:00.000"},{"value":"0.0689"}],[{"value":"2024-01-01 01:18:00.000"},{"value":"-0.0904"}],[{"value":"2024-01-01 01:21:00.000"},{"value":"0.2558"}],[{"value":"2024-01-01 01:24:00.000"},{"value":"0.1013"}],[{"value":"2024-01-01 01:27:00.000"},{"value":"0.1646"}],[{"value":"2024-01-01 01:30:00.000"},{"value":"-0.1646"}],[{"value":"2024-01-01 01:33:00.000"},{"value":"0.0768"}],[{"value":"2024-01-01 01:36:00.000"},{"value":"-0.0029"}],[{"value":"2024-01-01 01:39:00.000"},{"value":"-0.1206"}],[{"value":"2024-01-01 01:42:00.000"},{"value":"0.0998"}],[{"value":"2024-01-01 01:45:00.000"},{"value":"0.0141"}],[{"value":"2024-01-01 01:48:00.000"},{"value":"0.0308"}],[{"value":"2024-01-01 01:51:00.000"},{"value":"-0.0087"}],[{"value":"2024-01-01 01:54:00.000"},{"value":"-0.1384"}],[{"value":"2024-01-01 01:57:00.000"},{"value":"0.0029"}],[{"value":"2024-01-01 02:00:00.000"},{"value":"-0.0901"}],[{"value":"2024-01-01 02:03:00.000"},{"value":"0.0751"}],[{"value":"2024-01-01 02:06:00.000"},{"value":"-0.1029"}],[{"value":"2024-01-01 02:09:00.000"},{"value":"-0.0342"}],[{"value":"2024-01-01 02:12:00.000"},{"value":"-0.0568"}],[{"value":"2024-01-01 02:15:00.000"},{"value":"0.0136"}],[{"value":"2024-01-01 02:18:00.000"},{"value":"0.0742"}],[{"value":"2024-01-01 02:21:00.000"},{"value":"0.0447"}],[{"value":"2024-01-01 02:24:00.000"},{"value":"-0.0460"}],[{"value":"2024-01-01 02:27:00.000"},{"value":"-0.0729"}],[{"value":"2024-01-01 02:30:00.000"},{"value":"0.1773"}],[{"value":"2024-01-01 02:33:00.000"},{"value":"0.0924"}],[{"value":"2024-01-01 02:36:00.000"},{"value":"0.1428"}],[{"value":"2024-01-01 02:39:00.000"},{"value":"0.0197"}],[{"value":"2024-01-01 02:42:00.000"},{"value":"0.0102"}],[{"value":"2024-01-01 02:45:00.000"},{"value":"-0.0533"}],[{"value":"2024-01-01 02:48:00.000"},{"value":"-0.0805"}],[{"value":"2024-01-01 02:51:00.000"},{"value":"-0.0967"}],[{"value":"2024-01-01 02:54:00.000"},{"value":"0.0641"}],[{"value":"2024-01-01 02:57:00.000"},{"value":"0.1529"}],[{"value":"2024-01-01 03:00:00.000"},{"value":"0.1533"}],[{"value":"2024-01-01 03:03:00.000"},{"value":"0.1486"}],[{"value":"2024-01-01 03:06:00.000"},{"value":"0.0504"}],[{"value":"2024-01-01 03:09:00.000"},{"value":"-0.0002"}],[{"value":"2024-01-01 03:12:00.000"},{"value":"-0.1389"}],[{"value":"2024-01-01 03:15:00.000"},{"value":"-0.0816"}],[{"value":"2024-01-01 03:18:00.000"},{"value":"0.0421"}],[{"value":"2024-01-01 03:21:00.000"},{"value":"0.0846"}],[{"value":"2024-01-01 03:24:00.000"},{"value":"-0.0638"}],[{"value":"2024-01-01 03:27:00.000"},{"value":"-0.0235"}],[{"value":"2024-01-01 03:30:00.000"},{"value":"-0.0054"}],[{"value":"2024-01-01 03:33:00.000"},{"value":"0.0166"}],[{"value":"2024-01-01 03:36:00.000"},{"value":"0.0524"}],[{"value":"2024-01-01 03:39:00.000"},{"value":"0.0710"}],[{"value":"2024-01-01 03:42:00.000"},{"value":"0.0625"}],[{"value":"2024-01-01 03:45:00.000"},{"value":"0.1696"}],[{"value":"2024-01-01 03:48:00.000"},{"value":"-0.1240"}],[{"value":"2024-01-01 03:51:00.000"},{"value":"-0.0404"}],[{"value":"2024-01-01 03:54:00.000"},{"value":"0.0140"}],[{"value":"2024-01-01 03:57:00.000"},{"value":"0.1695"}],[{"value":"2024-01-01 04:00:00.000"},{"value":"-0.0477"}],[{"value":"2024-01-01 04:03:00.000"},{"value":"0.0518"}],[{"value":"2024-01-01 04:06:00.000"},{"value":"-0.0571"}],[{"value":"2024-01-01 04:09:00.000"},{"value":"0.0521"}],[{"value":"2024-01-01 04:12:00.000"},{"value":"0.0202"}],[{"value":"2024-01-01 04:15:00.000"},{"value":"-0.0216"}],[{"value":"2024-01-01 04:18:00.000"},{"value":"0.1646"}],[{"value":"2024-01-01 04:21:00.000"},{"value":"0.2154"}],[{"value":"2024-01-01 04:24:00.000"},{"value":"0.0671"}],[{"value":"2024-01-01 04:27:00.000"},{"value":"0.0639"}],[{"value":"2024-01-01 04:30:00.000"},{"value":"0.0859"}],[{"value":"2024-01-01 04:33:00.000"},{"value":"-0.1199"}],[{"value":"2024-01-01 04:36:00.000"},{"value":"-0.0766"}],[{"value":"2024-01-01 04:39:00.000"},{"value":"-0.0811"}],[{"value":"2024-01-01 04:42:00.000"},{"value":"0.0824"}],[{"value":"2024-01-01 04:45:00.000"},{"value":"0.0629"}],[{"value":"2024-01-01 04:48:00.000"},{"value":"0.0036"}],[{"value":"2024-01-01 04:51:00.000"},{"value":"0.0597"}],[{"value":"2024-01-01 04:54:00.000"},{"value":"0.0389"}],[{"value":"2024-01-01 04:57:00.000"},{"value":"-0.0684"}]]}}}}
Loading