FirebirdSQL logo

Пакет RDB$BLOB_UTIL

Пакет RDB$BLOB_UTIL предназначен для управления BLOB-объектами так, как это не могут сделать стандартные функции Firebirdтакие, как BLOB_APPEND и SUBSTRING, или они работают очень медленно.

Эти подпрограммы работают с двоичными данными напрямую, даже с текстовыми BLOB.

Функция RDB$BLOB_UTIL.NEW_BLOB

Функция RDB$BLOB_UTIL.NEW_BLOB используется для создания нового BLOB. Он возвращает BLOB, подходящий для добавления данных, как это делает BLOB_APPEND.

Преимущество по сравнению с BLOB_APPEND заключается в том, что можно установить собственные параметры SEGMENTED и TEMP_STORAGE.

Функция BLOB_APPEND всегда создаёт BLOB во временном хранилище. Это может быть не лучшим подходом,если созданный BLOB будет храниться в постоянной таблице, поскольку для этого потребуется копирование.

Возвращённый BLOB из этой функции, даже если TEMP_STORAGE = FALSE, может использоваться с BLOB_APPEND для добавления данных.

Table 1. Входные параметры функции RDB$BLOB_UTIL.NEW_BLOB
Параметр Тип Описание

SEGMENTED

BOOLEAN NOT NULL

Тип BLOB. Если TRUE - будет создан сегментированный BLOB, FALSE - потоковый.

TEMP_STORAGE

BOOLEAN NOT NULL

В каком хранилище создаётся BLOB. TRUE - во временном, FALSE - в постоянном (для записи в обычную таблицу).

Тип возвращаемого результата

BLOB SUB_TYPE BINARY

Функция RDB$BLOB_UTIL.OPEN_BLOB

Функция RDB$BLOB_UTIL.OPEN_BLOB используется для открытия существующего BLOB для чтения.Она возвращает дескриптор (целое число, связанное с транзакцией), подходящий для использования с другими функциями этого пакета,такими как SEEK, READ_DATA и CLOSE_HANDLE.

Table 2. Входные параметры функции RDB$BLOB_UTIL.OPEN_BLOB
Параметр Тип Описание

BLOB

BLOB NOT NULL

Входной BLOB.

Тип возвращаемого результата

INTEGER

Функция RDB$BLOB_UTIL.IS_WRITABLE

Функция RDB$BLOB_UTIL.IS_WRITABLE возвращает TRUE, если BLOB подходит для добавления данных без копирования с использованием BLOB_APPEND.

Table 3. Входные параметры функции RDB$BLOB_UTIL.IS_WRITABLE
Параметр Тип Описание

BLOB

BLOB NOT NULL

Проверяемый BLOB.

Тип возвращаемого результата

BOOLEAN

Функция RDB$BLOB_UTIL.READ_DATA

Функция RDB$BLOB_UTIL.READ_DATA используется для чтения фрагментов данных из дескриптора BLOB,открытого с помощью RDB$BLOB_UTIL.OPEN_BLOB. Когда BLOB полностью прочитан и данных больше нет, она возвращает NULL.

Если в LENGTH передаётся положительное число, то возвращается VARBINARY максимальной длины LENGTH.

Если в LENGTH передаётся NULL, то возвращается только сегмент BLOB с максимальной длиной 32765.

Table 4. Входные параметры функции RDB$BLOB_UTIL.READ_DATA
Параметр Тип Описание

HANDLE

INTEGER NOT NULL

Дескриптор открытого BLOB.

LENGTH

INTEGER

Количество байт, которое необходимо прочитать.

Тип возвращаемого результата

VARBINARY(32765)

Функция RDB$BLOB_UTIL.SEEK

Функция RDB$BLOB_UTIL.SEEK используется для установки позиции для следующего READ_DATA. Она возвращает новую позицию.

Параметр MODE может быть 0 (с начала), 1 (с текущей позиции) или 2 (с конца).

Когда параметр MODE равен 2, OFFSET должен быть нулевым или отрицательным.

Table 5. Входные параметры функции RDB$BLOB_UTIL.SEEK
Параметр Тип Описание

HANDLE

INTEGER NOT NULL

Дескриптор открытого BLOB.

MODE

INTEGER NOT NULL

Режим поиска.

OFFSET

INTEGER NOT NULL

Смещение, байт.

Тип возвращаемого результата

INTEGER

Процедура RDB$BLOB_UTIL.CANCEL_BLOB

Процедура RDB$BLOB_UTIL.CANCEL_BLOB используется для немедленного освобождения временного BLOB-объекта, например созданного с помощью BLOB_APPEND.

Обратите внимание, что если тот же BLOB используется после отмены, с использованием той же переменной или другой с той же ссылкой на идентификатор BLOB,то будет вызвана ошибка "invalid blob id error".

Table 6. Входные параметры процедуры RDB$BLOB_UTIL.CANCEL_BLOB
Параметр Тип Описание

BLOB

BLOB NOT NULL

BLOB для отмены.

Процедура RDB$BLOB_UTIL.CLOSE_HANDLE

Процедура RDB$BLOB_UTIL.CLOSE_HANDLE используется для закрытия дескриптора BLOB, открытого с помощью RDB$BLOB_UTIL.OPEN_BLOB.

Незакрытые дескрипторы закрываются автоматически только при завершении транзакции.

Table 7. Входные параметры процедуры RDB$BLOB_UTIL.CLOSE_HANDLE
Параметр Тип Описание

HANDLE

INTEGER NOT NULL

Дескриптор BLOB для закрытия.

Примеры использования RDB$BLOB_UTIL

Example 1. Создание BLOB во временном пространстве и возврат его в EXECUTE BLOCK
execute block returns (b blob)
as
begin
    -- Create a BLOB handle in the temporary space.
    b = rdb$blob_util.new_blob(false, true);

    -- Add chunks of data.
    b = blob_append(b, '12345');
    b = blob_append(b, '67');

    suspend;
end
Example 2. Открытие BLOB и его возврат по частям в EXECUTE BLOCK
execute block returns (s varchar(10))
as
    declare b blob = '1234567';
    declare bhandle integer;
begin
    -- Open the BLOB and get a BLOB handle.
    bhandle = rdb$blob_util.open_blob(b);

    -- Get chunks of data as string and return.

    s = rdb$blob_util.read_data(bhandle, 3);
    suspend;

    s = rdb$blob_util.read_data(bhandle, 3);
    suspend;

    s = rdb$blob_util.read_data(bhandle, 3);
    suspend;

    -- Here EOF is found, so it returns NULL.
    s = rdb$blob_util.read_data(bhandle, 3);
    suspend;

    -- Close the BLOB handle.
    execute procedure rdb$blob_util.close_handle(bhandle);
end
Example 3. Поиск в BLOB
set term !;

execute block returns (s varchar(10))
as
    declare b blob;
    declare bhandle integer;
begin
    -- Create a stream BLOB handle.
    b = rdb$blob_util.new_blob(false, true);

    -- Add data.
    b = blob_append(b, '0123456789');

    -- Open the BLOB.
    bhandle = rdb$blob_util.open_blob(b);

    -- Seek to 5 since the start.
    rdb$blob_util.seek(bhandle, 0, 5);
    s = rdb$blob_util.read_data(bhandle, 3);
    suspend;

    -- Seek to 2 since the start.
    rdb$blob_util.seek(bhandle, 0, 2);
    s = rdb$blob_util.read_data(bhandle, 3);
    suspend;

    -- Advance 2.
    rdb$blob_util.seek(bhandle, 1, 2);
    s = rdb$blob_util.read_data(bhandle, 3);
    suspend;

    -- Seek to -1 since the end.
    rdb$blob_util.seek(bhandle, 2, -1);
    s = rdb$blob_util.read_data(bhandle, 3);
    suspend;
end!

set term ;!
Example 4. Поверка доступен ли BLOB для записи
create table t(b blob);

set term !;

execute block returns (bool boolean)
as
    declare b blob;
begin
    b = blob_append(null, 'writable');
    bool = rdb$blob_util.is_writable(b);
    suspend;

    insert into t (b) values ('not writable') returning b into b;
    bool = rdb$blob_util.is_writable(b);
    suspend;
end!

set term ;!

Пакет RDB$PROFILER

Пакет RDB$PROFILER предназначен для управления сеансами профилирования.

Note
  • Пакет RDB$PROFILER для управления профилировщиком является стандартным, хотя сам профилировщикявляется подключаемым модулем. Используемый профилировщик зависит от настройки DefaultProfilerPluginв файле firebird.conf или databases.conf или от параметра PLUGIN_NAME в функции START_SESSION.

  • Firebird 5.0 поставляется с подключаемым модулем профилировщика под названием Default_Profiler.

  • Пользователям разрешено профилировать свои собственные подключения. Для профилирования подключений других пользователей требуется системная привилегия PROFILE_ANY_ATTACHMENT.

Подробное описание таблиц и представлений плагина профилирования Default_Profiler см. в приложении Таблицы плагинов. Плагин профилирования Default_Profiler.

Функция START_SESSION

Функция RDB$PROFILER.START_SESSION запускает новый сеанс профилировщика, превращает его в текущий сеанс (для заданного ATTACHMENT_ID) и возвращает его идентификатор.

Table 1. Входные параметры процедуры RDB$PROFILER.START_SESSION
Параметр Тип Описание

DESCRIPTION

VARCHAR(255) CHARACTER SET UTF8

Пользовательское описание сеанса профилирования. По умолчанию NULL.

FLUSH_INTERVAL

INTEGER

Интервал автоматического сброса статистики в таблицы снимков. По умолчанию NULL. Измеряется в секундах.

ATTACHMENT_ID

BIGINT

Идентификатор соединения для которого запускается сеанс профилирования. По умолчанию CURRENT_CONNECTION.

PLUGIN_NAME

VARCHAR(255) CHARACTER SET UTF8

Наименование плагина профилирования. По умолчанию NULL, что обозначает что будет использоваться плагин профилирования указанный в параметре конфигурации DefaultProfilerPlugin.

PLUGIN_OPTIONS

VARCHAR(255) CHARACTER SET UTF8

Параметры специфичные для плагина профилирования. По умолчанию NULL.

Тип выходного результата: BIGINT NOT NULL.

Если параметр FLUSH_INTERVAL отличен от NULL, то включается автоматический сброс статистики так же, как при вызове RDB$PROFILER.SET_FLUSH_INTERVAL вручную.

Если параметр PLUGIN_NAME имеет значение NULL (по умолчанию), он использует конфигурацию базы данных из параметра DefaultProfilerPlugin.

Для плагина Default_Profiler допустимыми значениями параметра PLUGIN_OPTIONS является NULL или строка DETAILED_REQUESTS.

Если указана опция DETAILED_REQUESTS, то таблица PLG$PROF_REQUESTS будет хранить подробные данные запросов, то есть одну запись для каждого вызова оператора. Это может привести к созданию большого количества записей, что приведёт к медленной работе RDB$PROFILER.FLUSH.

Когда DETAILED_REQUESTS не используется (по умолчанию), таблица PLG$PROF_REQUESTS сохраняет агрегированную запись для каждого оператора, используя REQUEST_ID = 0.

Процедура CANCEL_SESSION

Процедура RDB$PROFILER.CANCEL_SESSION отменяет текущий сеанс профилировщика (для заданного ATTACHMENT_ID).

Все данные сеанса, присутствующие в плагине профилировщика, отбрасываются и не сбрасываются.

Уже сброшенные данные не удаляются автоматически.

Table 2. Входные параметры процедуры RDB$PROFILER.CANCEL_SESSION
Параметр Тип Описание

ATTACHMENT_ID

BIGINT

Идентификатор соединения для которого отменяется сеанс профилирования. По умолчанию CURRENT_CONNECTION.

Процедура DISCARD

Процедура RDB$PROFILER.DISCARD удаляет все сеансы (для заданного ATTACHMENT_ID) из памяти, не сбрасывая их.

Если есть активная сессия, она отменяется.

Table 3. Входные параметры процедуры RDB$PROFILER.DISCARD
Параметр Тип Описание

ATTACHMENT_ID

BIGINT

Идентификатор соединения для которого удаляются все сеансы профилирования. По умолчанию CURRENT_CONNECTION.

Процедура FINISH_SESSION

Процедура RDB$PROFILER.FINISH_SESSION завершает текущий сеанс профилировщика (для заданного ATTACHMENT_ID).

Table 4. Входные параметры процедуры RDB$PROFILER.FINISH_SESSION
Параметр Тип Описание

FLUSH

BOOLEAN

Сбрасывать ли текущую статистику профилирования в таблицы моментальных снимков.

ATTACHMENT_ID

BIGINT

Идентификатор соединения для которого завершается сеанс профилирования. По умолчанию CURRENT_CONNECTION.

Если значение параметра FLUSH равно TRUE, то таблицы моментальных снимков обновляются данными завершённого сеанса (и старых завершённых сеансов, ещё не присутствующих в моментальном снимке). В противном случае данные остаются только в памяти для последующего обновления.

Вызов RDB$PROFILER.FINISH_SESSION(TRUE) имеет тот же смысл, что и вызов RDB$PROFILER.FINISH_SESSION(FALSE), за которым следует RDB$PROFILER.FLUSH (с использованием того же ATTACHMENT_ID).

Процедура FLUSH

Процедура RDB$PROFILER.FLUSH обновляет таблицы моментальных снимков данными из сеансов профиля (для заданного ATTACHMENT_ID) в памяти.

Table 5. Входные параметры процедуры RDB$PROFILER.FLUSH
Параметр Тип Описание

ATTACHMENT_ID

BIGINT

Идентификатор соединения для которого обновляются таблицы моментальных снимков из сеансов профилирования. По умолчанию CURRENT_CONNECTION.

После обновления данные сохраняются в таблицах PLG$PROF_SESSIONS, PLG$PROF_STATEMENTS, PLG$PROF_RECORD_SOURCES, PLG$PROF_REQUESTS, PLG$PROF_PSQL_STATS и PLG$PROF_RECORD_SOURCE_STATS и могут быть прочитаны и проанализированы пользователем.

Данные обновляются с помощью автономной транзакции, поэтому если процедура вызывается в snapshot транзакции,данные не будут доступны для прямого чтения в той же транзакции.

После сброса завершённые сеансы удаляются из памяти.

Процедура PAUSE_SESSION

Процедура RDB$PROFILER.PAUSE_SESSION приостанавливает текущий сеанс профилировщика (для заданного ATTACHMENT_ID), после чего статистика для последующих выполненных операторов не собирается.

Table 6. Входные параметры процедуры RDB$PROFILER.PAUSE_SESSION
Параметр Тип Описание

FLUSH

BOOLEAN

Сбрасывать ли текущую статистику профилирования в таблицы моментальных снимков.

ATTACHMENT_ID

BIGINT

Идентификатор соединения для которого приостанавливается сеанс профилирования. По умолчанию CURRENT_CONNECTION.

Если параметр FLUSH имеет значение TRUE, таблицы моментальных снимков обновляются данными до текущего момента.В противном случае данные остаются только в памяти для последующего обновления.

Вызов RDB$PROFILER.PAUSE_SESSION(TRUE) имеет тот же смысл, что и вызов RDB$PROFILER.PAUSE_SESSION(FALSE), за которым следует RDB$PROFILER.FLUSH (с использованием того же ATTACHMENT_ID).

Процедура RESUME_SESSION

Процедура RDB$PROFILER.RESUME_SESSION возобновляет текущий сеанс профилировщика (для заданного ATTACHMENT_ID), если он был приостановлен, после чего вновь собирается статистика последующих выполненных операторов.

Table 7. Входные параметры процедуры RDB$PROFILER.RESUME_SESSION
Параметр Тип Описание

ATTACHMENT_ID

BIGINT

Идентификатор соединения для которого возобновляется сеанс профилирования. По умолчанию CURRENT_CONNECTION.

Процедура SET_FLUSH_INTERVAL

Процедура RDB$PROFILER.SET_FLUSH_INTERVAL включает периодическую автоматическую сброс статистики в таблицы моментальных снимков (когда FLUSH_INTERVAL больше 0) или выключает (когда FLUSH_INTERVAL равно 0).

Table 8. Входные параметры процедуры RDB$PROFILER.SET_FLUSH_INTERVAL
Параметр Тип Описание

FLUSH_INTERVAL

INTEGER

Интервал автоматического сброса статистики. Задаётся в секундах.

ATTACHMENT_ID

BIGINT

Идентификатор соединения для которого обновляются таблицы моментальных снимков из сеансов профилирования. По умолчанию CURRENT_CONNECTION.

Как работает профилирования SQL и PSQL кода

Профилировщик позволяет пользователям измерять стоимость производительности кода SQL и PSQL.

Это реализовано с помощью системного пакета в движке, передающего данные плагину профилировщика.

В этой документации части движка и плагина рассматриваются как единое целое, так как будет использоваться профилировщик по умолчанию (Default_Profiler).

Пакет RDB$PROFILER позволяет профилировать выполнение кода PSQL, собирая статистику о том, сколько раз выполнялась каждая строка, а также её минимальное, максимальное и накопленное время выполнения (с точностью до наносекунд), а также открывать и извлекать статистику неявных и явных SQL-курсоров.

Note

К сожалению профилировщик не может работать с базами данных 1 SQL-диалекта.

Это происходит из-за того, что таблицы моментальных снимков содержат поля с типом BIGINT, которые нельзя создать в 1-диалекте.

Для сбора данных профиля пользователь должен сначала запустить сеанс профиля с помощью RDB$PROFILER.START_SESSION. Эта функция возвращает идентификатор сеанса профиля, который позже сохраняется в таблицах моментальных снимков профилировщика для запроса и анализа пользователем. Сеанс профилировщика может быть локальным (то же соединение) или удалённым (другое соединение).

Удалённое профилирование просто перенаправляет команды на удалённое соединение. Это позволяет клиенту одновременно профилировать несколько сеансов.Кроме того, локально или удалённо запущенный сеанс профилировщика может получать команды, выданные в другом соединении.

Для удалённых команд требуется, чтобы целевой сеанс находился в состоянии ожидания,то есть не выполнял других запросов. Когда они не простаивают, вызов блокируется в ожидании этого состояния.

Если удалённое соединение исходит от другого пользователя, вызывающий пользователь должен иметь системную привилегию PROFILE_ANY_ATTACHMENT.

После запуска сеанса в памяти начинает собираться статистика PSQL и SQL операторов. Обратите внимание, что сеанс профилировщика собирает данные только об операторах, выполненных только в том соединении, которое связанно с сеансом профилировщика.

Данные агрегируются и сохраняются для каждого запроса. При запросе таблиц моментальных снимков пользователь может выполнятьдополнительную агрегацию по операторам или использовать вспомогательные представления, которые делают это автоматически.

Сеанс профилирования может быть приостановлен для временного отключения сбора статистики. Его можно возобновить позже, чтобы вернуть сбор статистики в том же сеансе.

Новый сеанс профилировщика может быть запущен, когда уже есть активный сеанс. В этом случае текущий сеанс завершается как будто была вызвана процедураRDB$PROFILER.FINISH_SESSION(FALSE), поэтому таблицы моментальных снимков не обновляются в этот момент.

Чтобы проанализировать собранные данные, пользователь должен сбросить данные в таблицы моментальных снимков,что можно сделать, завершив или приостановив сеанс (с параметром FLUSH, установленным в TRUE) или вызвав RDB$PROFILER.FLUSH.Данные сбрасываются с помощью автономной транзакции (транзакция, запущенная и завершенная для конкретной цели обновления данных профилировщика).

Пример

Ниже приведён пример сеанса профилировщика и запросов для анализа данных.

  1. Подготовка — создание таблицы и процедур, которые будут анализироваться.

    create table tab (
        id integer not null,
        val integer not null
    );
    
    set term !;
    
    create or alter function mult(p1 integer, p2 integer) returns integer
    as
    begin
        return p1 * p2;
    end!
    
    create or alter procedure ins
    as
        declare n integer = 1;
    begin
        while (n <= 1000)
        do
        begin
            if (mod(n, 2) = 1) then
                insert into tab values (:n, mult(:n, 2));
            n = n + 1;
        end
    end!
    
    set term ;!
  2. Запуск профилирования.

    select rdb$profiler.start_session('Profile Session 1') from rdb$database;
    
    set term !;
    
    execute block
    as
    begin
        execute procedure ins;
        delete from tab;
    end!
    
    set term ;!
    
    execute procedure rdb$profiler.finish_session(true);
    
    execute procedure ins;
    
    select rdb$profiler.start_session('Profile Session 2') from rdb$database;
    
    select mod(id, 5),
           sum(val)
      from tab
      where id <= 50
      group by mod(id, 5)
      order by sum(val);
    
    execute procedure rdb$profiler.finish_session(true);
  3. Анализ результатов профилирования.

    set transaction read committed;
    
    select * from plg$prof_sessions;
    
    select * from plg$prof_psql_stats_view;
    
    select * from plg$prof_record_source_stats_view;
    
    select preq.*
      from plg$prof_requests preq
      join plg$prof_sessions pses
        on pses.profile_id = preq.profile_id and
           pses.description = 'Profile Session 1';
    
    select pstat.*
      from plg$prof_psql_stats pstat
      join plg$prof_sessions pses
        on pses.profile_id = pstat.profile_id and
           pses.description = 'Profile Session 1'
      order by pstat.profile_id,
               pstat.request_id,
               pstat.line_num,
               pstat.column_num;
    
    select pstat.*
      from plg$prof_record_source_stats pstat
      join plg$prof_sessions pses
        on pses.profile_id = pstat.profile_id and
           pses.description = 'Profile Session 2'
      order by pstat.profile_id,
               pstat.request_id,
               pstat.cursor_id,
               pstat.record_source_id;

docnext count = 3

Пакет RDB$TIME_ZONE_UTIL

Пакет RDB$TIME_ZONE_UTIL содержит процедуры и функции для работы с часовыми поясами.

Функция RDB$TIME_ZONE_UTIL.DATABASE_VERSION()

Функция RDB$TIME_ZONE_UTIL.DATABASE_VERSION возвращает версию базы данных часовых поясов (из библиотеки icu).

Тип возвращаемого результата

VARCHAR(10) CHARACTER SET ASCII

Example 1. Использование функции RDB$TIME_ZONE_UTIL.DATABASE_VERSION
SELECT rdb$time_zone_util.database_version()
FROM rdb$database;
DATABASE_VERSION
================
2021a

Процедура RDB$TIME_ZONE_UTIL.TRANSITIONS()

Процедура RDB$TIME_ZONE_UTIL.TRANSITIONS возвращает набор правил для часового пояса между начальной и конечной временной меткой.

Table 1. Входные параметры процедуры RDB$TIME_ZONE_UTIL.TRANSITIONS
Параметр Тип Описание

RDB$TIME_ZONE_NAME

CHAR(63)

Наименование часового пояса

RDB$FROM_TIMESTAMP

TIMESTAMP WITH TIME ZONE

Начало интервала дат

RDB$TO_TIMESTAMP

TIMESTAMP WITH TIME ZONE

Окончание интервала дат

Table 2. Выходные параметры процедуры RDB$TIME_ZONE_UTIL.TRANSITIONS
Параметр Тип Описание

RDB$START_TIMESTAMP

TIMESTAMP WITH TIME ZONE

Дата начала действия правила

RDB$END_TIMESTAMP

TIMESTAMP WITH TIME ZONE

Дата окончания действия правила

RDB$ZONE_OFFSET

SMALLINT

Смещение времени в минутах для заданного часового пояса

RDB$DST_OFFSET

SMALLINT

Летнее смещение времени в минутах для заданного часового пояса

RDB$EFFECTIVE_OFFSET

SMALLINT

Эффективное смещение, вычисляется как RDB$ZONE_OFFSET + RDB$DST_OFFSET

Example 1. Использование процедуры RDB$TIME_ZONE_UTIL.TRANSITIONS
SELECT
  RDB$START_TIMESTAMP,
  RDB$END_TIMESTAMP,
  RDB$ZONE_OFFSET AS ZONE_OFF,
  RDB$DST_OFFSET AS DST_OFF,
  RDB$EFFECTIVE_OFFSET AS OFF
FROM rdb$time_zone_util.transitions(
    'America/Sao_Paulo',
    timestamp '2017-01-01',
    timestamp '2019-01-01');
         RDB$START_TIMESTAMP            RDB$END_TIMESTAMP ZONE_OFF DST_OFF  OFF
============================ ============================ ======== ======= ====
2016-10-16 03:00:00.0000 GMT 2017-02-19 01:59:59.9999 GMT     -180      60 -120
2017-02-19 02:00:00.0000 GMT 2017-10-15 02:59:59.9999 GMT     -180       0 -180
2017-10-15 03:00:00.0000 GMT 2018-02-18 01:59:59.9999 GMT     -180      60 -120
2018-02-18 02:00:00.0000 GMT 2018-10-21 02:59:59.9999 GMT     -180       0 -180
2018-10-21 03:00:00.0000 GMT 2019-02-17 01:59:59.9999 GMT     -180      60 -120