Storing time ranges as JSON? I have table with jsonb field - timetable which has next structure:

{
1: [{start:0, finish: 12}],
2: [{start:0, finish: 12}, {start:12, finish:17}],
3: [{start:12, finish: 17}],
4: [{start:0, finish: 12}],
5: [{start:12, finish:17}, {start:17, finish: 0}],
6: [{start:0, finish: 12}, {start:12, finish:17}],
7: [{start:17, finish: 0}]
}


1...7 are days of week, which represent when user is available - morning/afternoon/evening.
I'm doing filter where user could choose morning/afternoon/evening and days of week and get available users. If user checked all three time periods and all days I will have 21 variants. I wrote next query:

SELECT * FROM users WHERE 
timetable &lt;@ {1: [{start: 0, finish: 12}]} OR timetable &lt;@ {1: [{start: 12, finish: 17}]} OR timetable &lt;@ {1: [{start: 17, finish: 0}]} OR
timetable &lt;@ {2: [{start: 0, finish: 12}]} OR timetable &lt;@ {2: [{start: 12, finish: 17}]} OR timetable &lt;@ {2: [{start: 17, finish: 0}]} OR
......
timetable &lt;@ {7: [{start: 0, finish: 12}]} OR timetable &lt;@ {7: [{start: 12, finish: 17}]} OR timetable &lt;@ {7: [{start: 17, finish: 0}]}


Please advice me how to optimize my query. I know if user checked all possible periods and days I could just query with timetable is not null. But I want to optimize my task in general.
Moving away from JSONB

JSONB is not the right tool for every job. I'm going to fix your schema because everyone here is about to cry. 

Here we use an ENUM type and timerange

CREATE TYPE dow AS ENUM (
  'Monday', 'Tuesday', 'Wednesday', 'Thursday',
  'Friday', 'Saturday', 'Sunday'
);

CREATE FUNCTION time_subtype_diff(x time, y time)
RETURNS float8 AS $$
  SELECT EXTRACT(EPOCH FROM (x - y))
$$ LANGUAGE sql
STRICT IMMUTABLE;

CREATE TYPE timerange AS RANGE (
    subtype = time,
    subtype_diff = time_subtype_diff
);

CREATE TABLE available (
  user_id  int,  -- REFERENCES users,
  dow      dow,
  trange  timerange,
  PRIMARY KEY (user_id, dow)
);


now you can be sane (and we support minutes!)

CREATE TABLE users(user_id) AS VALUES (1::int);

INSERT INTO available VALUES 
  ( 1, 'Monday', '[13:42,16:12]' );

SELECT *
FROM users AS u
JOIN available AS a USING (user_id)
WHERE dow = 'Monday' AND a.trange @&gt; '[14:00,15:00]';