Skip to content

recipes: Using timezones #3927

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
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
118 changes: 118 additions & 0 deletions docs/content/Examples-Tutorials-Recipes/Recipes/timezones.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
---
title: Using timezones
permalink: /recipes/timezones
category: Examples & Tutorials
subCategory: Queries
menuOrder: 4
---

## Use case

We want users from multiple timezones to retrieve metrics that account for these timezones, e.g., online store managers from different locations might want to view sales stats in their timezones.

We would also like our queries to use pre-aggregations to get great performance.

## Data schema

Let's explore the `Orders` cube that contains information about
orders in various statuses, including the transaction date
in the `createdAt` time dimension.

```javascript
cube(`Orders`, {
sql: `SELECT * FROM public.orders`,

dimensions: {
id: {
sql: `id`,
type: `number`,
primaryKey: true
},

status: {
sql: `status`,
type: `string`
},

createdAt: {
sql: `created_at`,
type: `time`,
},

createdAtConverted: {
sql: SQL_UTILS.convertTz(`created_at`),
type: `time`,
},
},
});
```

## Query

To query the API with respect to a timezone, we need to provide the timezone via the [`timezone` property](https://cube.dev/docs/query-format#query-properties). The sales stats will be translated to reflect the point of view of a person from that location, e.g., for an online store manager from New York, let's pass `America/New_York`:

```javascript
{
"dimensions": [
"Orders.status",
"Orders.createdAt",
"Orders.createdAtConverted"
],
"timeDimensions": [ {
"dimension": "Orders.createdAt",
"granularity": "day"
} ],
"order": {
"Orders.createdAt": "desc"
},
"limit": 3,
"timezone": "America/New_York"
}
```

## Result

Let's explore the retrieved data:

```javascript
[
{
"Orders.status": "shipped",
"Orders.createdAt": "2023-11-05T00:00:00.000",
"Orders.createdAtConverted": "2023-11-04T20:00:00.000",
"Orders.createdAt.day": "2023-11-04T00:00:00.000"
},
{
"Orders.status": "shipped",
"Orders.createdAt": "2023-11-04T00:00:00.000",
"Orders.createdAtConverted": "2023-11-03T20:00:00.000",
"Orders.createdAt.day": "2023-11-03T00:00:00.000"
},
{
"Orders.status": "completed",
"Orders.createdAt": "2023-11-04T00:00:00.000",
"Orders.createdAtConverted": "2023-11-03T20:00:00.000",
"Orders.createdAt.day": "2023-11-03T00:00:00.000"
}
]
```

The `Orders.createdAt` time dimension was provided in the `dimensions` part of the query. So, its values were returned "as is", in the UTC timezone. (Apparently, all orders were made at midnight.)

Also, check out the `Orders.createdAt.day` values in the result. They were returned because we've provided `Orders.createdAt` in the `timeDimensions` part of the query. So, they were translated to the New York timezone (shifted 4 hours back from UTC) and also truncated to the start of the day since we've specified the daily `granularity` in the query.

We also added the `Orders.createdAtConverted` to `dimensions` in the query. The respective values were also translated to the New York timezone but not truncated with respect to the granularity. Please check that the `createdAtConverted` dimension is defined using the [`SQL_UTILS.convertTz` method](https://cube.dev/docs/schema/reference/cube#convert-tz) that does the timezone translation.

## Configuration

To allow Cube to build pre-aggregations for timezones that can be specified in queries, we need to provide a list of such timezones via the `scheduledRefreshTimeZones` configuration option:

```javascript
module.exports = {
scheduledRefreshTimeZones: ['America/New_York'],
};
```

## Source code

Please feel free to check out the [full source code](https://github.com/cube-js/cube.js/tree/master/examples/recipes/timezones) or run it with the `docker-compose up` command. You'll see the result, including queried data, in the console.
8 changes: 8 additions & 0 deletions examples/recipes/timezones/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
CUBEJS_DB_HOST=demo-db-recipes.cube.dev
CUBEJS_DB_PORT=5432
CUBEJS_DB_NAME=ecom
CUBEJS_DB_USER=cube
CUBEJS_DB_PASS=12345
CUBEJS_DB_TYPE=postgres
CUBEJS_API_SECRET=SECRET
CUBEJS_DEV_MODE=true
3 changes: 3 additions & 0 deletions examples/recipes/timezones/cube.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
scheduledRefreshTimeZones: ['America/New_York'],
};
19 changes: 19 additions & 0 deletions examples/recipes/timezones/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
version: '2.2'

services:
cube:
image: cubejs/cube:latest
ports:
- 4000:4000
- 3000:3000
env_file: .env
volumes:
- .:/cube/conf

query:
image: cfmanteiga/alpine-bash-curl-jq
depends_on:
- cube
volumes:
- .:/query
entrypoint: ["sh", "query/queries/run.sh"]
16 changes: 16 additions & 0 deletions examples/recipes/timezones/queries/query.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"dimensions": [
"Orders.status",
"Orders.createdAt",
"Orders.createdAtConverted"
],
"timeDimensions": [ {
"dimension": "Orders.createdAt",
"granularity": "day"
} ],
"order": {
"Orders.createdAt": "desc"
},
"limit": 3,
"timezone": "America/New_York"
}
24 changes: 24 additions & 0 deletions examples/recipes/timezones/queries/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/bin/bash

host=cube
port=4000
loadUrl=cubejs-api/v1/load
readyzUrl=readyz

token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjEwMDAwMDAwMDAsImV4cCI6NTAwMDAwMDAwMH0.OHZOpOBVKr-sCwn8sbZ5UFsqI3uCs6e4omT7P6WVMFw

query=$(cat query/queries/query.json)

# Wait for the Cube API to become ready
until curl -s "$host":"$port"/"$readyzUrl" > /dev/null; do
sleep 1
done

# Send the queries
curl "$host":"$port"/"$loadUrl" -H "Authorization: ${token}" -G -s --data-urlencode "query=${query}" -o response.json

echo "Orders (excerpt):"
jq ".data" response.json

echo "Used pre-ggregations:"
jq ".usedPreAggregations" response.json
34 changes: 34 additions & 0 deletions examples/recipes/timezones/schema/Orders.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
cube(`Orders`, {
sql: `SELECT * FROM public.orders`,

dimensions: {
id: {
sql: `id`,
type: `number`,
primaryKey: true
},

status: {
sql: `status`,
type: `string`
},

createdAt: {
sql: `created_at`,
type: `time`,
},

createdAtConverted: {
sql: SQL_UTILS.convertTz(`created_at`),
type: `time`,
},
},

preAggregations: {
main: {
dimensions: [ Orders.status, Orders.createdAt, Orders.createdAtConverted ],
timeDimension: Orders.createdAt,
granularity: "day"
}
},
});