PLSQL中的时区转换

PLSQL中的时区转换,sql,oracle,plsql,plsqldeveloper,Sql,Oracle,Plsql,Plsqldeveloper,我需要将sysdate和time转换为一个特定的时区,比如EST。我不能假设我现在的时区 如何在plsql中转换它?请帮帮我 以下内容将为您提供当前EST时间(-5小时),而不考虑夏令时: SELECT SYS_EXTRACT_UTC(SYSTIMESTAMP) FROM DUAL 为了考虑夏令时,您有两种选择: 编写一个函数来计算夏令时更改发生的日期 填充包含这些日期的表 如果您只需要支持EST时区,那么编写一个函数可能是最好的选择;否则,我建议填充一个包含这些日期的表。试试以下方法: CR

我需要将sysdate和time转换为一个特定的时区,比如EST。我不能假设我现在的时区


如何在plsql中转换它?请帮帮我

以下内容将为您提供当前EST时间(-5小时),而不考虑夏令时:

SELECT SYS_EXTRACT_UTC(SYSTIMESTAMP) FROM DUAL
为了考虑夏令时,您有两种选择:

  • 编写一个函数来计算夏令时更改发生的日期
  • 填充包含这些日期的表
  • 如果您只需要支持EST时区,那么编写一个函数可能是最好的选择;否则,我建议填充一个包含这些日期的表。

    试试以下方法:

    CREATE TABLE TIMEZONES (ZONE          CHAR(1) PRIMARY KEY,
                            NAMES         VARCHAR2(25) NOT NULL,
                            OFFSET_HOURS  NUMBER NOT NULL);
    
    按如下方式填充它:

    INSERT INTO TIMEZONES (ZONE, NAMES, OFFSET_HOURS) VALUES ('Z', 'GMT', 0);
    INSERT INTO TIMEZONES (ZONE, NAMES, OFFSET_HOURS) VALUES ('N', '-1', -1);
    INSERT INTO TIMEZONES (ZONE, NAMES, OFFSET_HOURS) VALUES ('O', '-2', -2);
    INSERT INTO TIMEZONES (ZONE, NAMES, OFFSET_HOURS) VALUES ('P', '-3', -3);
    INSERT INTO TIMEZONES (ZONE, NAMES, OFFSET_HOURS) VALUES ('Q', '-4 EDT', -4);
    INSERT INTO TIMEZONES (ZONE, NAMES, OFFSET_HOURS) VALUES ('R', 'EST CDT', -5);
    INSERT INTO TIMEZONES (ZONE, NAMES, OFFSET_HOURS) VALUES ('S', 'CST MDT', -6);
    INSERT INTO TIMEZONES (ZONE, NAMES, OFFSET_HOURS) VALUES ('T', 'MST PDT', -7);
    INSERT INTO TIMEZONES (ZONE, NAMES, OFFSET_HOURS) VALUES ('U', 'PST', -8);
    INSERT INTO TIMEZONES (ZONE, NAMES, OFFSET_HOURS) VALUES ('V', '-9', -9);
    INSERT INTO TIMEZONES (ZONE, NAMES, OFFSET_HOURS) VALUES ('W', '-10', -10);
    INSERT INTO TIMEZONES (ZONE, NAMES, OFFSET_HOURS) VALUES ('X', '-11', -11);
    INSERT INTO TIMEZONES (ZONE, NAMES, OFFSET_HOURS) VALUES ('Y', '-12', -12);
    INSERT INTO TIMEZONES (ZONE, NAMES, OFFSET_HOURS) VALUES ('A', '1', -1);
    INSERT INTO TIMEZONES (ZONE, NAMES, OFFSET_HOURS) VALUES ('B', '2', -2);
    INSERT INTO TIMEZONES (ZONE, NAMES, OFFSET_HOURS) VALUES ('C', '3', -3);
    INSERT INTO TIMEZONES (ZONE, NAMES, OFFSET_HOURS) VALUES ('D', '4', -4);
    INSERT INTO TIMEZONES (ZONE, NAMES, OFFSET_HOURS) VALUES ('E', '5', -5);
    INSERT INTO TIMEZONES (ZONE, NAMES, OFFSET_HOURS) VALUES ('F', '6', -6);
    INSERT INTO TIMEZONES (ZONE, NAMES, OFFSET_HOURS) VALUES ('G', '7', -7);
    INSERT INTO TIMEZONES (ZONE, NAMES, OFFSET_HOURS) VALUES ('H', '8', -8);
    INSERT INTO TIMEZONES (ZONE, NAMES, OFFSET_HOURS) VALUES ('I', '9', -9);
    INSERT INTO TIMEZONES (ZONE, NAMES, OFFSET_HOURS) VALUES ('K', '10', -10);
    INSERT INTO TIMEZONES (ZONE, NAMES, OFFSET_HOURS) VALUES ('L', '11', -11);
    INSERT INTO TIMEZONES (ZONE, NAMES, OFFSET_HOURS) VALUES ('M', '12', -12);
    
    鉴于上述情况,你可以这样做

    SELECT SYS_EXTRACT_UTC(SYSTIMESTAMP) + (tz.OFFSET_HOURS / 24)
      FROM TIMEZONES tz
      WHERE tz.NAMES LIKE '%EDT%';
    

    获取-4时区中的本地时间


    共享和享受。

    假设您有带时区的
    时间戳(例如
    systimestamp
    ),您可以在时区使用
    语法。例如,我可以使用当前的
    systimestamp
    ,通过指定不同的时区名称将其转换为UTC(GMT)、东部和太平洋时区

    SQL> ed
    Wrote file afiedt.buf
    
      1  select systimestamp at time zone 'UTC' current_time_in_utc,
      2         systimestamp at time zone 'Us/Eastern' current_time_in_est,
      3         systimestamp at time zone 'US/Pacific' current_time_in_pst
      4*   from dual
    SQL> /
    
    CURRENT_TIME_IN_UTC
    ---------------------------------------------------------------------------
    CURRENT_TIME_IN_EST
    ---------------------------------------------------------------------------
    CURRENT_TIME_IN_PST
    ---------------------------------------------------------------------------
    26-APR-12 05.36.11.802000 PM UTC
    26-APR-12 01.36.11.802000 PM US/EASTERN
    26-APR-12 10.36.11.802000 AM US/PACIFIC
    
    这个怎么样

    select to_timestamp_tz(to_char(sysdate,'YYYY-MM-DD HH24:MI:SS') || ' ' || 'FROM_TIME_ZONE', 'YYYY-MM-DD HH24:MI:SS TZR') at time zone 'TO_TIME_ZONE'
    from dual;
    

    Oracle已经有一个时区表:

    SELECT tzname, tzabbrev from V$TIMEZONE_NAMES
    SELECT TZ_OFFSET('US/Eastern') FROM DUAL;
    
    获取EPT中用于SQL语句的当前时间:

    select to_timestamp(to_char(TRUNC(LOCALTIMESTAMP - 5/1440, 'MI'),'mm/dd/yyyy hh24:mi'),'mm/dd/yyyy hh24:mi') FROM DUAL;
    
    Oracle的11g SQL参考非常好,可在以下位置在线获取:

    您可以快速查找以下函数,我相信您会发现这些函数很有用:

    • 当前时间戳
    • 数据库时区
    • 本地时间戳
    • 特鲁恩(日期)
    • 会话时区
    • 系统提取utc
    • 系统戳记
    • 时间戳
    • 到时间戳
    • 子午偏移

      更改会话设置时区='+00:00';--您现在将看到UTC时间,而不是EPT时间

    不同时区中的当前日期和时间

    返回本地现行时间中的当前日期和时间(sysdate)

     Select sysdate from dual;
    
    select LOCALTIMESTAMP FROM DUAL;
    
    将当前日期返回为UTC

    select LOCALTIMESTAMP at time zone '+00:00' FROM DUAL
    
    --请尝试使用localtimestamp、to_timestamp和to_timestamp,而不要使用sysdate和to_date。它们类似于sysdate和to_date,但添加了时区功能

    select LOCALTIMESTAMP at time zone '+00:00' FROM DUAL; –- returns essentially the same as sysdate but in UTC
    
    or 
    
    ALTER SESSION SET TIME_ZONE = 'UTC';
    select LOCALTIMESTAMP FROM DUAL –- after setting session time_zone to ‘UTC’ this will now return a UTC timestamp
    
    时区转换 获取用于SQL语句的当前UTC时间

    -- Get current time in UTC format and subtract 5 minutes.
    
        LOCALTIMESTAMP at time zone '+00:00' - 5/1440
    
    -- Trunc the time to eliminate seconds
    
    TRUNC(LOCALTIMESTAMP at time zone '+00:00' - 5/1440, 'MI')
    
    -- Convert to characters then back to datetime. 
    
    to_timestamp(to_char(TRUNC(LOCALTIMESTAMP at time zone '+00:00' - 5/1440, 'MI'),'mm/dd/yyyy hh24:mi'),'mm/dd/yyyy hh24:mi')
    
    -- Select from dual to show it works. 
    
    select to_timestamp(to_char(TRUNC(LOCALTIMESTAMP at time zone '+00:00' - 5/1440, 'MI'),'mm/dd/yyyy hh24:mi'),'mm/dd/yyyy hh24:mi') FROM DUAL;
    
    Get the current time in EPT for use in a SQL statement.
    
        -- Get current time in UTC format and subtract 5 minutes.
        ALTER SESSION SET TIME_ZONE = 'US/Eastern'; -- set to EPT time
        select LOCALTIMESTAMP from dual - 5/1440
    -- Trunc the time to eliminate seconds
    
        TRUNC(LOCALTIMESTAMP - 5/1440, 'MI')
    
        -- Convert to characters then back to datetime. 
        to_timestamp(to_char(TRUNC(LOCALTIMESTAMP - 5/1440, 'MI'),'mm/dd/yyyy hh24:mi'),'mm/dd/yyyy hh24:mi')
    
        -- Select from dual to show it works. 
        select to_timestamp(to_char(TRUNC(LOCALTIMESTAMP - 5/1440, 'MI'),'mm/dd/yyyy hh24:mi'),'mm/dd/yyyy hh24:mi') FROM DUAL;
    
    返回数据库和会话时区函数

    ALTER SESSION SET TIME_ZONE = '+00:00';  -- you will now see times in UTC instead of EPT
    ALTER SESSION SET NLS_DATE_FORMAT = 'MM/DD/YYYY HH24:MI:SS';
    
    ALTER SESSION SET TIME_ZONE = '+00:00';  -- you will now see times in UTC instead of EPT
    ALTER SESSION SET TIME_ZONE = 'UTC'; -- set to UTC time same as command above
    SELECT DBTIMEZONE , SESSIONTIMEZONE, CURRENT_TIMESTAMP, LOCALTIMESTAMP, systimestamp, sysdate FROM DUAL; -- see the results
    ALTER SESSION SET TIME_ZONE = 'US/Eastern'; -- set to EPT time
    SELECT DBTIMEZONE , SESSIONTIMEZONE, CURRENT_TIMESTAMP, LOCALTIMESTAMP, systimestamp, sysdate FROM DUAL; -- see the results
    
    SELECT TO_TIMESTAMP_TZ('05/16/2014 11:26:48 -04:00',
       'MM/DD/YYYY HH:MI:SS TZH:TZM') FROM DUAL;
    
    
    SELECT tzname, tzabbrev from V$TIMEZONE_NAMES where tzabbrev = 'EPT';
    SELECT TZ_OFFSET('US/Eastern') FROM DUAL;
    
    -- The following example casts a null column in a UNION operation as TIMESTAMP WITH LOCAL TIME ZONE using the sample tables oe.order_items and oe.orders:
    
    SELECT order_id, line_item_id,
       CAST(NULL AS TIMESTAMP WITH LOCAL TIME ZONE) order_date
       FROM order_items
    UNION
    SELECT order_id, to_number(null), order_date
       FROM orders;
    
    日期时间和时区(至时间戳)

    示例: 以下示例将字符串转换为带时区的时间戳值:

    SELECT TO_TIMESTAMP_TZ('1999-12-01 11:00:00 -8:00',
       'YYYY-MM-DD HH:MI:SS TZH:TZM') FROM DUAL;
    TO_TIMESTAMP_TZ('1999-12-0111:00:00-08:00','YYYY-MM-DDHH:MI:SSTZH:TZM')
    
    以下示例使用示例表oe.order\u items和oe.orders将联合操作中的空列强制转换为带有本地时区的时间戳:

    SELECT order_id, line_item_id,
       CAST(NULL AS TIMESTAMP WITH LOCAL TIME ZONE) order_date
       FROM order_items
    UNION
    SELECT order_id, to_number(null), order_date
       FROM orders;
    
    设置日期和时间格式

    ALTER SESSION SET NLS_DATE_FORMAT = 'MM/DD/YYYY HH24:MI:SS';
    SELECT CURRENT_TIMESTAMP, LOCALTIMESTAMP FROM DUAL;
    
    设置当前/本地时区

    ALTER SESSION SET TIME_ZONE = '-5:00';
    ALTER SESSION SET NLS_DATE_FORMAT = 'MM/DD/YYYY HH24:MI:SS';
    SELECT CURRENT_TIMESTAMP, LOCALTIMESTAMP FROM DUAL;
    
    本地时间(本地时间戳)

    LOCALTIMESTAMP以数据类型TIMESTAMP的值返回会话时区中的当前日期和时间。此函数与CURRENT_TIMESTAMP之间的区别在于LOCALTIMESTAMP返回时间戳值,而CURRENT_TIMESTAMP返回带有时区值的时间戳

    ALTER SESSION SET TIME_ZONE = '-5:00';
        ALTER SESSION SET NLS_DATE_FORMAT = 'DD-MON-YYYY HH24:MI:SS';
        SELECT CURRENT_TIMESTAMP, LOCALTIMESTAMP FROM DUAL;
    
    以下语句使用正确的格式掩码来匹配LOCALTIMESTAMP的返回类型:

        INSERT INTO local_test VALUES
       (TO_TIMESTAMP(LOCALTIMESTAMP, 'DD-MON-RR HH.MI.SSXFF PM'));
    
    The code above is required to include the TIME ZONE portion of the return type of the function
    
    当前时间戳(当前时间戳)

    CURRENT_TIMESTAMP返回会话时区中的当前日期和时间,值为带时区的数据类型TIMESTAMP。时区偏移反映SQL会话的当前本地时间

        ALTER SESSION SET TIME_ZONE = '-5:0';
    ALTER SESSION SET NLS_DATE_FORMAT = 'DD-MON-YYYY HH24:MI:SS';
        SELECT SESSIONTIMEZONE, CURRENT_TIMESTAMP FROM DUAL;
    

    到目前为止,所有方法在使用当前时间戳时都能很好地工作。然而,我注意到Oracle的
    tz_offset
    将为您提供当前的时间偏移。比如七月,

    SELECT TZ_OFFSET('US/Eastern') FROM DUAL;
    
    结果为'-04:00'。在1月份,相同的语句会产生'-05:00'。因此,如果要转换存储在数据库中的日期(与当前系统或会话时间相反),必须进行一些开发

    你可能会注意到

    SELECT TZ_OFFSET('EST') FROM DUAL;
    
    总是返回“-05:00”。不幸的是,对其他美国标准时间(中部、山区和太平洋)的快速测试显示了不同的结果。运行以下命令,亲自查看。6月16日,当DST生效时,我运行了以下命令

    SELECT TZ_OFFSET('US/Eastern'), TZ_OFFSET('EST'),  TZ_OFFSET('EST5EDT') FROM DUAL;
    SELECT TZ_OFFSET('US/Central'), TZ_OFFSET('CST'),  TZ_OFFSET('CST6CDT') FROM DUAL;
    SELECT TZ_OFFSET('US/Mountain'), TZ_OFFSET('MST'),  TZ_OFFSET('MST7MDT') FROM DUAL;
    SELECT TZ_OFFSET('US/Pacific'), TZ_OFFSET('PST'),  TZ_OFFSET('PST8PDT') FROM DUAL;
    
    -04:00      -05:00      -04:00
    -05:00      -05:00      -05:00
    -06:00      -07:00      -06:00
    -07:00      -07:00      -07:00
    
    结果有些令人沮丧。通过快速采样,几乎所有的tz_偏移量查询似乎都以当前的主要时间响应。这在处理当前时间的转换时非常有用,但在处理从数据库中提取的时间时,它会变得平淡无奇

    现在,我可以编写代码来确定我是在标准时间还是在流行时间。然而,对于标准时间或昼间时间,我认为没有办法持续拉一个
    tz_偏移量
    ,只是占主导地位

    这就使得开发人员需要创建自己的表,正如Bob Jarvis在上面所做的那样,来解决这个问题,正如我在下面的代码中所做的那样

    ALTER SESSION SET NLS_DATE_FORMAT = 'MM/DD/YYYY HH24:MI:SS';
    
    Declare
    -- ******** User declarations begin here ******** --
    
    
    
    
    -- ******** User declarations end here ******** --
    
    /*******************  All Declarations of Variables and Functions below this point support Time Zone Conversion (Convert_TZ function) *******************/  
      -- TimeZone Conversion Procedure
    
      -- User Input (Parameters)
      input_date date := TO_TIMESTAMP_TZ('7/1/2009 18:00','mm/dd/yyyy hh24:mi');  -- Try: LocalTimestamp; or TO_TIMESTAMP('2/1/2009 13:00','mm/dd/yyyy hh24:mi')
      --input_date date := LocalTimestamp;
      input_TZ varchar(3) := 'GMT';  -- Exmaples: EST, EDT or EPT for Eastern Standard, Daylight or Prevailing, respectively.
      output_TZ varchar(3) := 'EPT';  -- 
    
      -- Variables
      type date_array is table of date;
      return_date date := localtimestamp;
      temp_date date := to_date('10/27/1974 02:00','mm/dd/yyyy hh24:mi');
      type str_array is table of varchar2(10);
      dow_list str_array;
    
       Function dst_start_stop (input_date DATE) 
            RETURN date_array
        AS
            year_part number(4);
            start_week number(1) := 0;  -- week of month: -1=last, 1=1st, 2=nd, 0=fixed date (like 1974 and 1975)
            stop_week number(1) := 0;   -- week of month: -1=last, 1=1st, 2=nd, 0=fixed date (like 1974 and 1975)
            dst_start date := to_date('01/06/1974 23:59','mm/dd/yyyy hh24:mi');
            dst_stop date := to_date('10/27/1974 23:59','mm/dd/yyyy hh24:mi');
            dst_date date := dst_start;
            dst_msg varchar2(500) := ' ';
            inc_dec number := 0;
            Cnt number(1) := 0;
            dst_dow number(1) := 1;  --  1=Sunday, 2=Monday, etc.
            i number;
            dst_range date_array;
       BEGIN
          dst_range := date_array();
          dst_range.extend(2);
          dst_range(1) := temp_date;
          dst_range(2) := temp_date;
          DBMS_OUTPUT.PUT_LINE('  ** Start: dst_start_stop Func **');
          --insert into dst_range values(dst_start,dst_stop);
          --dst_range(1) := dst_start;
          --dst_range(2) :=  dst_stop;
          year_part := to_number(to_char(input_date,'YYYY'));
          DBMS_OUTPUT.PUT_LINE('      Year: '||year_part);
    
          -- Determine DST formula based on year of input_date
          If year_part > 9999 Then  --   Invalid TempYear > 9999
              dst_msg := 'N/A.  I can''t guess if DST will be applied after 9999.  Standard Time returned.  ';
              Goto found_start_stop;
          ElsIf year_part >= 2007 Then --   2007 forward.  Latest DST Rules used after 2007.
              dst_msg := '2007 forward: Third National DST Standard.  ';
              --dst_msg := dst_msg || 'Spring Forward 2:00 AM second Sunday in March (skip 02:00:00 through 02:59:59, I.E. skip from 01:59:59 to 03:00:00).  '
              --dst_msg := dst_msg || 'Fall Back 2:00 AM first Sunday in November (repeat 02:00:00 to 02:59:59, I.E. jump back from 02:59:59 to 02:00:00 once).  '
              DBMS_OUTPUT.PUT_LINE('    '||dst_msg);
              dst_start := to_date('03/01/'||year_part||' 02:00','mm/dd/yyyy hh24:mi');
              start_week := 2; -- 2nd Sunday in March
              dst_stop := to_date('11/01/'||year_part||' 02:00','mm/dd/yyyy hh24:mi');
              stop_week := 1;  -- 1st Sunday in November
          ElsIf year_part >= 1987 Then  --   1987 thru 2006.
              dst_msg := '1987 thru 2006: Second National DST Standard.  ';
              --dst_msg := dst_msg || 'Spring Forward 2:00 AM first Sunday in April (skip 02:00:00 through 02:59:59, I.E. skip from 01:59:59 to 03:00:00).  ';
              --dst_msg := dst_msg || 'Fall Back 2:00 AM last Sunday in October (repeat 02:00:00 to 02:59:59, I.E. jump back from 02:59:59 to 02:00:00 once).  ';
              DBMS_OUTPUT.PUT_LINE('    '||dst_msg);
              start_week := 1; 
              dst_start := to_date('04/01/'||year_part||' 02:00','mm/dd/yyyy hh24:mi');
              stop_week := -1;  
              dst_stop := to_date('10/31/'||year_part||' 02:00','mm/dd/yyyy hh24:mi');
          ElsIf year_part >= 1976 Then  --   1976 thru 1986 OLD DST Rules used 1961 thru 1973.
              dst_msg := '1976 thru 1986: First National DST Standard (resumed after 1974-1975 extended DST trials).  ';
              --dst_msg := dst_msg || 'Spring Forward 2:00 AM last Sunday in April (skip 02:00:00 through 02:59:59, I.E. skip from 01:59:59 to 03:00:00).  ';
              --dst_msg := dst_msg || 'Fall Back 2:00 AM last Sunday in October (repeat 02:00:00 to 02:59:59, I.E. jump back from 02:59:59 to 02:00:00 once).  ';
              DBMS_OUTPUT.PUT_LINE('    '||dst_msg);
              start_week := -1; 
              dst_start := to_date('04/30/'||year_part||' 02:00','mm/dd/yyyy hh24:mi');
              stop_week := -1;  
              dst_stop := to_date('10/31/'||year_part||' 02:00','mm/dd/yyyy hh24:mi');
          ElsIf year_part = 1975 Then  --   1975 Trial.
              dst_msg := '1975 Trial of Extended DST.  ';
              --dst_msg := dst_msg || 'Spring Forward 2:00 AM Feb 23 (skip 02:00:00 through 02:59:59, I.E. skip from 01:59:59 to 03:00:00).  ';
              --dst_msg := dst_msg || 'Fall Back 2:00 AM Oct 26, the last Sun in Oct (repeat 02:00:00 to 02:59:59, I.E. jump back from 02:59:59 to 02:00:00 once).  ';
              DBMS_OUTPUT.PUT_LINE('    '||dst_msg);
              dst_start := to_date('02/23/1975 02:00','mm/dd/yyyy hh24:mi');
              dst_stop := to_date('10/26/1974 02:00','mm/dd/yyyy hh24:mi');
              Goto found_start_stop;
          ElsIf year_part = 1974 Then  --   1974 Trial.
              dst_msg := '1974 Trial of Extended DST.  ';
              --dst_msg := dst_msg || 'Spring Forward 2:00 AM Jan 6 (skip 02:00:00 through 02:59:59, I.E. skip from 01:59:59 to 03:00:00)).  ';
              --dst_msg := dst_msg || 'Fall Back 2:00 AM Oct 27 (repeat 02:00:00 to 02:59:59, I.E. jump back from 02:59:59 to 02:00:00 once).  ';
              DBMS_OUTPUT.PUT_LINE('    '||dst_msg);
              dst_start := to_date('01/06/1974 02:00','mm/dd/yyyy hh24:mi');
              dst_stop := to_date('10/27/1974 02:00','mm/dd/yyyy hh24:mi');
              Goto found_start_stop;
          ElsIf year_part >= 1961 Then  --   1961 thru 1973 First National DST Standard.
              dst_msg := '1961 thru 1973: First National DST Standard.  ';
              --dst_msg := dst_msg || 'Spring Forward 2:00 AM last Sunday in April (skip 02:00:00 through 02:59:59, I.E. skip from 01:59:59 to 03:00:00).  ';
              --dst_msg := dst_msg || 'Fall Back 2:00 AM last Sunday in October (repeat 02:00:00 to 02:59:59, I.E. jump back from 02:59:59 to 02:00:00 once).  ';
              start_week := -1; 
              dst_start := to_date('04/30/'||input_date||' 02:00','mm/dd/yyyy hh24:mi');
              stop_week := -1;  
              dst_stop := to_date('10/31/'||year_part||' 02:00','mm/dd/yyyy hh24:mi');
          ElsIf year_part >=1900 Then  --   DST was applied inconsistently or not at all
              dst_msg := 'N/A.  Before 1961, DST was applied inconsistently across states or not at all.  Standard Time returned.  ';
              Goto found_start_stop;
          ElsIf year_part < 1900 Then  --   Invalid year_part
              dst_msg := 'N/A. DST never active before 1900';
              Goto found_start_stop;
          Else  --   Invalid year_part 
              dst_msg := 'N/A.  Error.  Invalid datetime value.';
              Goto found_start_stop;
          End If;   
          DBMS_OUTPUT.PUT_LINE('  The code specified the following DST rules for the input date ('||input_date||').  '||dst_msg);
          if start_week > 0 then
            DBMS_OUTPUT.PUT_LINE('    Start on '||dow_list(dst_dow)||' #'||start_week||' of '||trunc(dst_start,'W')||'. ');
          else
            DBMS_OUTPUT.PUT_LINE('    Starts '||start_week||' from the last '||dow_list(dst_dow)||' of '||trunc(dst_start,'W')||'. ');
          end if;
          if stop_week > 0 then
            DBMS_OUTPUT.PUT_LINE('    End on '||dow_list(dst_dow)||' #'||stop_week||' of '||trunc(dst_stop,'W')||'. ');
          else
            DBMS_OUTPUT.PUT_LINE('    Ends '||stop_week||' from the last '||dow_list(dst_dow)||' of '||trunc(dst_stop,'W')||'. ');
          end if;
          DBMS_OUTPUT.PUT_LINE('    ');
    
    
          /* Apply formula determined above to find dst start and stop times for the year of the input_date.
                This section is skipped if start/stop already determined or indeterminant. 
          */
            -- DstStartDay 
            inc_dec := start_week/abs(start_week);  -- results in +1 or -1
            Cnt := 0; i:=0;
            while (Cnt < abs(start_week) and i<20) loop
              i:=i+1;
              if (to_char(dst_start,'D') = dst_dow) then 
                Cnt := Cnt + 1; 
                --DBMS_OUTPUT.PUT_LINE('    Found '||dow_list(dst_dow))||' '||Cnt||': '||dst_start)
              end if;
              if (Cnt < abs(start_week)) then 
                dst_start := dst_start + inc_dec; 
              end if;
            end loop;
            case inc_dec
              when 1 then
                DBMS_OUTPUT.PUT_LINE('  Spring forward on  '||dow_list(dst_dow)||' #'||Cnt||' of the month: '||dst_start);
              else
                DBMS_OUTPUT.PUT_LINE('  Spring forward on the last'||dow_list(dst_dow)||'of the month: '||dst_start);
            end case;
          -- DstStopDay 
            inc_dec := stop_week/abs(stop_week);  -- results in +1 or -1
            Cnt := 0; i :=0;
            while (Cnt < abs(stop_week) and i <20) loop -- to_char(dst_stop,'D') > 1 loop
              i:=i+1;
              if (to_char(dst_stop,'D') = dst_dow) then 
                dst_stop := dst_stop + inc_dec; 
                Cnt := Cnt + 1; 
              end if;
              if (Cnt < abs(stop_week)) then 
                dst_stop := dst_stop + inc_dec; 
              end if;
            end loop;
            case inc_dec
              when 1 then
                DBMS_OUTPUT.PUT_LINE('  Fall back on  '||dow_list(dst_dow)||' #'||Cnt||' of the month: '||dst_stop);
              else
                DBMS_OUTPUT.PUT_LINE('  Fall back on the last'||dow_list(dst_dow)||'of the month: '||dst_stop);
            end case;
        <<found_start_stop>>
          dst_range(1) := dst_start; 
          DBMS_OUTPUT.PUT_LINE('      dst_range(1): '||to_char(dst_range(1),'mm/dd/yyyy hh24:mi')||' = '||dst_start);
          dst_range(2) := dst_stop;
          DBMS_OUTPUT.PUT_LINE('      dst_range(2): '||to_char(dst_range(2),'mm/dd/yyyy hh24:mi')||' = '||dst_stop);
          DBMS_OUTPUT.PUT_LINE('  ** Finish: dst_start_stop Func **');
          Return dst_range;
       END dst_start_stop;
    
       Function is_dst_now
            Return boolean
          AS
              --type date_array is table of date;
              dst_range date_array;
              curr_time date := LocalTimestamp;
          Begin
              dst_range := date_array();
              dst_range.extend(2);
              dst_range := dst_start_stop(curr_time);
              If (dst_range(1) <= curr_time and curr_time < dst_range(2))  then 
                DBMS_OUTPUT.PUT_LINE('DST is  active.');
                Return True;
              Else
                DBMS_OUTPUT.PUT_LINE('DST is NOT active.');
                Return False;
              End If;  
       End;
    
       FUNCTION dst_offset (prevailing_date DATE, dst_start DATE, dst_stop DATE) 
            RETURN number
          AS
                offset_days number :=0;
          BEGIN
    
              DBMS_OUTPUT.PUT_LINE('  Starting dst_offset sub-function:');
              DBMS_OUTPUT.PUT_LINE('        where (input date, DST start, DST stop) = '||to_char(prevailing_date,'mm/dd/yyyy hh24:mi')
                                    ||', '||to_char(dst_start,'mm/dd/yyyy hh24:mi')||', '||to_char(dst_stop,'mm/dd/yyyy hh24:mi'));
    
              If (dst_start <= prevailing_date and prevailing_date < dst_stop) then 
                offset_days :=1/24;
                DBMS_OUTPUT.PUT_LINE('        input date is between dst start and stop');
              Else
                offset_days :=0;
                DBMS_OUTPUT.PUT_LINE('        input date is not between dst start and stop');
              End If;
              DBMS_OUTPUT.PUT_LINE('        Result: DST Offset days = '||offset_days);
              DBMS_OUTPUT.PUT_LINE('                          hours = '||(offset_days*24)); 
              Return offset_days;
       END dst_offset;
    
    -- Begin  --move this down under the function -- ******************
    
       FUNCTION Convert_TZ (input_date DATE, input_tz varchar2, output_tz varchar2) 
       RETURN date
       AS
            -- Variables
            input_sz varchar(3) := substr(input_TZ,1,1)||'S'||substr(input_TZ,3,1);
            input_sz varchar(3) := substr(output_TZ,1,1)||'S'||substr(output_TZ,3,1);
            temp_str varchar2(1000);
            dst_range date_array;
            input_dst_offset number := 0;
            input_tz_offset number := 0;
            input_date_st date; --standard time
            gmt_date date;
            output_dst_offset number := 0;
            output_tz_offset number := 0;
            output_date date;
            output_date_pt date; -- prevailing time
            tz_offset_str varchar2(30);
            --orig_nls_date_format varchar2(30) := NLS_DATE_FORMAT;
            --TempYear number(4,0) := to_char(TempDate,'YYYY');  -- or := trunc(PrevailingTime, YYYY);
    
       BEGIN 
    
          DBMS_OUTPUT.PUT_LINE('Starting Pl/sql procedure.  ');
    
          DBMS_OUTPUT.PUT_LINE('Input Date: '||to_char(input_date,'mm/dd/yyyy hh24:mi')||' '||input_tz||'.  ');
          -- Find DST start/stop dates  
          dst_range := date_array();
          dst_range.extend(2);
          dst_range := dst_start_stop(input_date);
          DBMS_OUTPUT.PUT_LINE('DST date range determined.  ');
          DBMS_OUTPUT.PUT_LINE('      dst_range(1): '||to_char(dst_range(1),'mm/dd/yyyy hh24:mi')||' = '||dst_range(1));
          DBMS_OUTPUT.PUT_LINE('      dst_range(2): '||to_char(dst_range(2),'mm/dd/yyyy hh24:mi')||' = '||dst_range(2));
    
          -- Convert Input Date from input time zone to GMT
            If upper(input_TZ) in ('GMT','UCT') then
                -- If input TZ is GMT, we can skip this conversion!
                DBMS_OUTPUT.PUT_LINE('    Input Time is ('||to_char(input_date,'mm/dd/yyyy hh24:mi')||' '
                                      ||input_tz||' GMT).  No conversion required.  ');
                input_tz_offset := 0;
                input_dst_offset := 0; 
                gmt_date := input_date;
            Else
                -- Convert from local prevailing to local standard time
                    -- Get input_dst_offset 
                    Case upper(substr(input_TZ,2,1)) 
                      When 'S' then 
                        -- already in standard time, not conversion needed.
                        input_dst_offset := 0; -- duplicative
                        DBMS_OUTPUT.PUT_LINE('Standard time ('||input_tz||') entered; no dst offset.' );
                              --input_tz_offset := input_tz_offset;
                      Else 
                        -- run dst_offset function to convert from prevailing or daylight time to standard time.
                        input_dst_offset := dst_offset(input_date, dst_range(1), dst_range(2));
                        input_date_st := input_date - input_dst_offset; 
                        DBMS_OUTPUT.PUT_LINE('Daylight Saving Time Effective ('||to_char(input_date,'mm/dd/yyyy hh24:mi')||' '
                                              ||input_tz||'); 1 hour offset; input DST offset = '||input_dst_offset);
                        DBMS_OUTPUT.PUT_LINE('        where (input_date, dst_start, dst_stop) = '
                                              ||to_char(input_date,'mm/dd/yyyy hh24:mi')
                                              ||' '||input_tz||', '||dst_range(1)||', '||dst_range(2)||', ');
                        DBMS_OUTPUT.PUT_LINE('        which adjusts '||to_char(input_date,'mm/dd/yyyy hh24:mi')||' '||input_tz
                                              ||' daylight to '||input_date_st||' standard time.  ');
                    End Case;
    
                -- Convert from local standard time to GMT
                    SELECT TZ_OFFSET((SELECT max(tzname) FROM V$TIMEZONE_NAMES where tzabbrev = input_TZ)) 
                    INTO tz_offset_str 
                    FROM DUAL;
    
                    input_tz_offset := ( substr(tz_offset_str,1,3)/24 + substr(tz_offset_str,5,2)/1440 );
                    If is_dst_now then 
                      input_tz_offset := input_tz_offset - 1/24;
                    End if;
                    DBMS_OUTPUT.PUT_LINE('    input_tz_offset (fractional days): '||input_tz_offset||'. ');
                    DBMS_OUTPUT.PUT_LINE('    input_tz_offset (hours): '||input_tz_offset*24||'. ');
                    gmt_date := input_date_st - input_tz_offset;
    
            End If;
    
            -- Convert input date from GMT to requested output time zone
            DBMS_OUTPUT.PUT_LINE('  ');
            DBMS_OUTPUT.PUT_LINE('Starting output_date analysis. ');
            If upper(output_TZ) in ('GMT','UCT') then
                -- If desired output TZ is GMT, we can skip this conversion!
                output_tz_offset := 0;
                output_dst_offset := 0; 
                output_date := gmt_date;
                DBMS_OUTPUT.PUT_LINE('    Requested output format is GMT: ('||to_char(output_date,'mm/dd/yyyy hh24:mi')||' '||output_tz||').  No conversion required.  ');
            Else
                -- Get output_dst_offset 
                    Case upper(substr(output_TZ,2,1)) 
                      When 'S' then 
                        output_dst_offset := 0; -- duplicative
                        DBMS_OUTPUT.PUT_LINE('Standard time ('||output_TZ||') entered; no dst offset.' );
                      Else
                        output_dst_offset := dst_offset(gmt_date + output_tz_offset, dst_range(1), dst_range(2));
                    End Case;
                -- Convert from GMT to local standard time
                    SELECT TZ_OFFSET((SELECT max(tzname) FROM V$TIMEZONE_NAMES where tzabbrev = output_TZ)) INTO tz_offset_str FROM DUAL;
                    output_tz_offset := ( substr(tz_offset_str,1,3)/24 + substr(tz_offset_str,5,2)/1440 );
                    DBMS_OUTPUT.PUT_LINE('    output_tz_offset (fractional days): '||output_tz_offset||'. ');
                    DBMS_OUTPUT.PUT_LINE('    output_tz_offset (hours): '||output_tz_offset*24||'. ');
                    If is_dst_now then 
                      output_tz_offset := output_tz_offset - 1/24;
                      DBMS_OUTPUT.PUT_LINE('    tz_offset correction... ');
                      DBMS_OUTPUT.PUT_LINE('      output_tz_offset (fractional days): '||output_tz_offset||'. ');
                      DBMS_OUTPUT.PUT_LINE('      output_tz_offset (hours): '||output_tz_offset*24||'. ');
                    End if;
    
                        output_date := gmt_date + output_tz_offset + output_dst_offset;        
                        DBMS_OUTPUT.PUT_LINE('          gmt_date: '||gmt_date);
                        DBMS_OUTPUT.PUT_LINE('  output_tz_offset: '||output_tz_offset);
                        DBMS_OUTPUT.PUT_LINE(' output_dst_offset: '||output_dst_offset);
    
                        DBMS_OUTPUT.PUT_LINE('Daylight Saving Time ('||output_TZ||') offset = '||output_dst_offset);
                        DBMS_OUTPUT.PUT_LINE('        where (output_date, dst_start, DST_stop) = '||output_date||', '||dst_range(1)||', '||dst_range(2));
                        DBMS_OUTPUT.PUT_LINE('        which adjusts '||output_date||' standard to '||output_date_pt||' daylight time.  ');
            End If;
    
            DBMS_OUTPUT.PUT_LINE('Output Date = '||to_char(output_date,'mm/dd/yyyy hh24:mi')||' '||output_tz||'.  ');
    
        Goto AllDone;
    
        <<FoundError>>      
    
        <<AllDone>>
          DBMS_OUTPUT.PUT_LINE('   ');
          DBMS_OUTPUT.PUT_LINE('***    Results     ***');
          DBMS_OUTPUT.PUT_LINE('   ');
    
          if input_dst_offset <> 0 then
            temp_str := 'daylight saving';
          else
            temp_str := 'standard';
          end if;
          DBMS_OUTPUT.PUT_LINE('    Input Date: '||to_char(input_date,'mm/dd/yyyy hh24:mi')||' '||input_tz||', which falls in  '||temp_str||' time.  ');
          DBMS_OUTPUT.PUT_LINE('    GMT Date: '||to_char(gmt_date,'mm/dd/yyyy hh24:mi')||' UTC/GMT.  ');
          DBMS_OUTPUT.PUT_LINE('    Output Date: '||to_char(output_date,'mm/dd/yyyy hh24:mi')||' UTC/GMT.  ');
          if output_dst_offset <> 0 then
            temp_str := 'daylight saving';
          else
            temp_str := 'standard';
          end if;
          DBMS_OUTPUT.PUT_LINE('    All Done.  Return Value: '||to_char(output_date,'mm/dd/yyyy hh24:mi')||' '||output_tz||'.  ');
          DBMS_OUTPUT.PUT_LINE('   ');
          DBMS_OUTPUT.PUT_LINE('*** End of Results ***');
          DBMS_OUTPUT.PUT_LINE('   ');
          DBMS_OUTPUT.PUT_LINE('LocalTimestamp EPT: '||LocalTimestamp);
          DBMS_OUTPUT.PUT_LINE('LocalTimestamp GMT:'||LOCALTIMESTAMP at time zone '+00:00');
    
          Return output_date;
       END;  
    /*********************** End of Declarations of Variables and Functions for Time Zone Conversion (Convert_TZ function) *********************/  
    Begin  
    /*********************** Start of Procedural Code for  Time Zone Conversion (Convert_TZ function) *********************/  
         /* DOW list is required for Time Zone Conversion (Convert_TZ function) and should not be deleted. */
          dow_list := str_array();
          dow_list.extend(7);
          dow_list(1) := 'Sun';
          dow_list(2) := 'Mon';
          dow_list(3) := 'Tue';
          dow_list(4) := 'Wed';
          dow_list(5) := 'Thu';
          dow_list(6) := 'Fri';
          dow_list(7) := 'Sat';
    
         /* Next 2 lines are example of use of Time Zone Conversion (Convert_TZ function). */
          return_date := Convert_TZ (input_date, input_tz, output_tz);
          DBMS_OUTPUT.PUT_LINE('ta-dah! '||return_date);
    /*********************** End of Procedural Code for Time Zone Conversion (Convert_TZ function) *********************/  
    
    -- ******** User coded begins here ******** --
    End;    
    
    ALTER SESSION SET NLS_DATE_FORMAT='MM/DD/yyyyy HH24:MI:SS';
    声明
    --*******用户声明从这里开始*******--
    --*******用户声明到此结束*******--
    /*******************此点以下的所有变量和函数声明都支持时区转换(转换函数)****************/
    --时区转换程序
    --用户输入(参数)
    输入日期:=至时间戳('7/1/2009 18:00','mm/dd/yyyy hh24:mi');--Try:LocalTimestamp;或时间戳('2/1/2009 13:00','mm/dd/yyyy hh24:mi')
    --输入日期:=本地时间戳;
    输入_TZ varchar(3):=“GMT”;--Exmaples:东部标准、白天或盛行分别为EST、EDT或EPT。
    输出_TZ varchar(3):=“EPT”;--
    --变数
    类型日期\数组是日期表;
    返回日期:=本地时间戳;
    临时日期日期:=截止日期('1974年10月27日02:00','2014年12月24日24:mi');
    str_型数组是varchar2(10)的表;
    道氏列表str_数组;
    功能dst_开始_停止(输入日期)
    返回日期数组
    作为
    年份_零件号(4);
    开始周数(1):=0;——月周:-1=最后一周,1=第一周,2=第二周,0=固定日期(如1974年和1975年)
    停止周数(1):=0;——周
    
    SELECT TZ_OFFSET('US/Eastern'), TZ_OFFSET('EST'),  TZ_OFFSET('EST5EDT') FROM DUAL;
    SELECT TZ_OFFSET('US/Central'), TZ_OFFSET('CST'),  TZ_OFFSET('CST6CDT') FROM DUAL;
    SELECT TZ_OFFSET('US/Mountain'), TZ_OFFSET('MST'),  TZ_OFFSET('MST7MDT') FROM DUAL;
    SELECT TZ_OFFSET('US/Pacific'), TZ_OFFSET('PST'),  TZ_OFFSET('PST8PDT') FROM DUAL;
    
    -04:00      -05:00      -04:00
    -05:00      -05:00      -05:00
    -06:00      -07:00      -06:00
    -07:00      -07:00      -07:00
    
    ALTER SESSION SET NLS_DATE_FORMAT = 'MM/DD/YYYY HH24:MI:SS';
    
    Declare
    -- ******** User declarations begin here ******** --
    
    
    
    
    -- ******** User declarations end here ******** --
    
    /*******************  All Declarations of Variables and Functions below this point support Time Zone Conversion (Convert_TZ function) *******************/  
      -- TimeZone Conversion Procedure
    
      -- User Input (Parameters)
      input_date date := TO_TIMESTAMP_TZ('7/1/2009 18:00','mm/dd/yyyy hh24:mi');  -- Try: LocalTimestamp; or TO_TIMESTAMP('2/1/2009 13:00','mm/dd/yyyy hh24:mi')
      --input_date date := LocalTimestamp;
      input_TZ varchar(3) := 'GMT';  -- Exmaples: EST, EDT or EPT for Eastern Standard, Daylight or Prevailing, respectively.
      output_TZ varchar(3) := 'EPT';  -- 
    
      -- Variables
      type date_array is table of date;
      return_date date := localtimestamp;
      temp_date date := to_date('10/27/1974 02:00','mm/dd/yyyy hh24:mi');
      type str_array is table of varchar2(10);
      dow_list str_array;
    
       Function dst_start_stop (input_date DATE) 
            RETURN date_array
        AS
            year_part number(4);
            start_week number(1) := 0;  -- week of month: -1=last, 1=1st, 2=nd, 0=fixed date (like 1974 and 1975)
            stop_week number(1) := 0;   -- week of month: -1=last, 1=1st, 2=nd, 0=fixed date (like 1974 and 1975)
            dst_start date := to_date('01/06/1974 23:59','mm/dd/yyyy hh24:mi');
            dst_stop date := to_date('10/27/1974 23:59','mm/dd/yyyy hh24:mi');
            dst_date date := dst_start;
            dst_msg varchar2(500) := ' ';
            inc_dec number := 0;
            Cnt number(1) := 0;
            dst_dow number(1) := 1;  --  1=Sunday, 2=Monday, etc.
            i number;
            dst_range date_array;
       BEGIN
          dst_range := date_array();
          dst_range.extend(2);
          dst_range(1) := temp_date;
          dst_range(2) := temp_date;
          DBMS_OUTPUT.PUT_LINE('  ** Start: dst_start_stop Func **');
          --insert into dst_range values(dst_start,dst_stop);
          --dst_range(1) := dst_start;
          --dst_range(2) :=  dst_stop;
          year_part := to_number(to_char(input_date,'YYYY'));
          DBMS_OUTPUT.PUT_LINE('      Year: '||year_part);
    
          -- Determine DST formula based on year of input_date
          If year_part > 9999 Then  --   Invalid TempYear > 9999
              dst_msg := 'N/A.  I can''t guess if DST will be applied after 9999.  Standard Time returned.  ';
              Goto found_start_stop;
          ElsIf year_part >= 2007 Then --   2007 forward.  Latest DST Rules used after 2007.
              dst_msg := '2007 forward: Third National DST Standard.  ';
              --dst_msg := dst_msg || 'Spring Forward 2:00 AM second Sunday in March (skip 02:00:00 through 02:59:59, I.E. skip from 01:59:59 to 03:00:00).  '
              --dst_msg := dst_msg || 'Fall Back 2:00 AM first Sunday in November (repeat 02:00:00 to 02:59:59, I.E. jump back from 02:59:59 to 02:00:00 once).  '
              DBMS_OUTPUT.PUT_LINE('    '||dst_msg);
              dst_start := to_date('03/01/'||year_part||' 02:00','mm/dd/yyyy hh24:mi');
              start_week := 2; -- 2nd Sunday in March
              dst_stop := to_date('11/01/'||year_part||' 02:00','mm/dd/yyyy hh24:mi');
              stop_week := 1;  -- 1st Sunday in November
          ElsIf year_part >= 1987 Then  --   1987 thru 2006.
              dst_msg := '1987 thru 2006: Second National DST Standard.  ';
              --dst_msg := dst_msg || 'Spring Forward 2:00 AM first Sunday in April (skip 02:00:00 through 02:59:59, I.E. skip from 01:59:59 to 03:00:00).  ';
              --dst_msg := dst_msg || 'Fall Back 2:00 AM last Sunday in October (repeat 02:00:00 to 02:59:59, I.E. jump back from 02:59:59 to 02:00:00 once).  ';
              DBMS_OUTPUT.PUT_LINE('    '||dst_msg);
              start_week := 1; 
              dst_start := to_date('04/01/'||year_part||' 02:00','mm/dd/yyyy hh24:mi');
              stop_week := -1;  
              dst_stop := to_date('10/31/'||year_part||' 02:00','mm/dd/yyyy hh24:mi');
          ElsIf year_part >= 1976 Then  --   1976 thru 1986 OLD DST Rules used 1961 thru 1973.
              dst_msg := '1976 thru 1986: First National DST Standard (resumed after 1974-1975 extended DST trials).  ';
              --dst_msg := dst_msg || 'Spring Forward 2:00 AM last Sunday in April (skip 02:00:00 through 02:59:59, I.E. skip from 01:59:59 to 03:00:00).  ';
              --dst_msg := dst_msg || 'Fall Back 2:00 AM last Sunday in October (repeat 02:00:00 to 02:59:59, I.E. jump back from 02:59:59 to 02:00:00 once).  ';
              DBMS_OUTPUT.PUT_LINE('    '||dst_msg);
              start_week := -1; 
              dst_start := to_date('04/30/'||year_part||' 02:00','mm/dd/yyyy hh24:mi');
              stop_week := -1;  
              dst_stop := to_date('10/31/'||year_part||' 02:00','mm/dd/yyyy hh24:mi');
          ElsIf year_part = 1975 Then  --   1975 Trial.
              dst_msg := '1975 Trial of Extended DST.  ';
              --dst_msg := dst_msg || 'Spring Forward 2:00 AM Feb 23 (skip 02:00:00 through 02:59:59, I.E. skip from 01:59:59 to 03:00:00).  ';
              --dst_msg := dst_msg || 'Fall Back 2:00 AM Oct 26, the last Sun in Oct (repeat 02:00:00 to 02:59:59, I.E. jump back from 02:59:59 to 02:00:00 once).  ';
              DBMS_OUTPUT.PUT_LINE('    '||dst_msg);
              dst_start := to_date('02/23/1975 02:00','mm/dd/yyyy hh24:mi');
              dst_stop := to_date('10/26/1974 02:00','mm/dd/yyyy hh24:mi');
              Goto found_start_stop;
          ElsIf year_part = 1974 Then  --   1974 Trial.
              dst_msg := '1974 Trial of Extended DST.  ';
              --dst_msg := dst_msg || 'Spring Forward 2:00 AM Jan 6 (skip 02:00:00 through 02:59:59, I.E. skip from 01:59:59 to 03:00:00)).  ';
              --dst_msg := dst_msg || 'Fall Back 2:00 AM Oct 27 (repeat 02:00:00 to 02:59:59, I.E. jump back from 02:59:59 to 02:00:00 once).  ';
              DBMS_OUTPUT.PUT_LINE('    '||dst_msg);
              dst_start := to_date('01/06/1974 02:00','mm/dd/yyyy hh24:mi');
              dst_stop := to_date('10/27/1974 02:00','mm/dd/yyyy hh24:mi');
              Goto found_start_stop;
          ElsIf year_part >= 1961 Then  --   1961 thru 1973 First National DST Standard.
              dst_msg := '1961 thru 1973: First National DST Standard.  ';
              --dst_msg := dst_msg || 'Spring Forward 2:00 AM last Sunday in April (skip 02:00:00 through 02:59:59, I.E. skip from 01:59:59 to 03:00:00).  ';
              --dst_msg := dst_msg || 'Fall Back 2:00 AM last Sunday in October (repeat 02:00:00 to 02:59:59, I.E. jump back from 02:59:59 to 02:00:00 once).  ';
              start_week := -1; 
              dst_start := to_date('04/30/'||input_date||' 02:00','mm/dd/yyyy hh24:mi');
              stop_week := -1;  
              dst_stop := to_date('10/31/'||year_part||' 02:00','mm/dd/yyyy hh24:mi');
          ElsIf year_part >=1900 Then  --   DST was applied inconsistently or not at all
              dst_msg := 'N/A.  Before 1961, DST was applied inconsistently across states or not at all.  Standard Time returned.  ';
              Goto found_start_stop;
          ElsIf year_part < 1900 Then  --   Invalid year_part
              dst_msg := 'N/A. DST never active before 1900';
              Goto found_start_stop;
          Else  --   Invalid year_part 
              dst_msg := 'N/A.  Error.  Invalid datetime value.';
              Goto found_start_stop;
          End If;   
          DBMS_OUTPUT.PUT_LINE('  The code specified the following DST rules for the input date ('||input_date||').  '||dst_msg);
          if start_week > 0 then
            DBMS_OUTPUT.PUT_LINE('    Start on '||dow_list(dst_dow)||' #'||start_week||' of '||trunc(dst_start,'W')||'. ');
          else
            DBMS_OUTPUT.PUT_LINE('    Starts '||start_week||' from the last '||dow_list(dst_dow)||' of '||trunc(dst_start,'W')||'. ');
          end if;
          if stop_week > 0 then
            DBMS_OUTPUT.PUT_LINE('    End on '||dow_list(dst_dow)||' #'||stop_week||' of '||trunc(dst_stop,'W')||'. ');
          else
            DBMS_OUTPUT.PUT_LINE('    Ends '||stop_week||' from the last '||dow_list(dst_dow)||' of '||trunc(dst_stop,'W')||'. ');
          end if;
          DBMS_OUTPUT.PUT_LINE('    ');
    
    
          /* Apply formula determined above to find dst start and stop times for the year of the input_date.
                This section is skipped if start/stop already determined or indeterminant. 
          */
            -- DstStartDay 
            inc_dec := start_week/abs(start_week);  -- results in +1 or -1
            Cnt := 0; i:=0;
            while (Cnt < abs(start_week) and i<20) loop
              i:=i+1;
              if (to_char(dst_start,'D') = dst_dow) then 
                Cnt := Cnt + 1; 
                --DBMS_OUTPUT.PUT_LINE('    Found '||dow_list(dst_dow))||' '||Cnt||': '||dst_start)
              end if;
              if (Cnt < abs(start_week)) then 
                dst_start := dst_start + inc_dec; 
              end if;
            end loop;
            case inc_dec
              when 1 then
                DBMS_OUTPUT.PUT_LINE('  Spring forward on  '||dow_list(dst_dow)||' #'||Cnt||' of the month: '||dst_start);
              else
                DBMS_OUTPUT.PUT_LINE('  Spring forward on the last'||dow_list(dst_dow)||'of the month: '||dst_start);
            end case;
          -- DstStopDay 
            inc_dec := stop_week/abs(stop_week);  -- results in +1 or -1
            Cnt := 0; i :=0;
            while (Cnt < abs(stop_week) and i <20) loop -- to_char(dst_stop,'D') > 1 loop
              i:=i+1;
              if (to_char(dst_stop,'D') = dst_dow) then 
                dst_stop := dst_stop + inc_dec; 
                Cnt := Cnt + 1; 
              end if;
              if (Cnt < abs(stop_week)) then 
                dst_stop := dst_stop + inc_dec; 
              end if;
            end loop;
            case inc_dec
              when 1 then
                DBMS_OUTPUT.PUT_LINE('  Fall back on  '||dow_list(dst_dow)||' #'||Cnt||' of the month: '||dst_stop);
              else
                DBMS_OUTPUT.PUT_LINE('  Fall back on the last'||dow_list(dst_dow)||'of the month: '||dst_stop);
            end case;
        <<found_start_stop>>
          dst_range(1) := dst_start; 
          DBMS_OUTPUT.PUT_LINE('      dst_range(1): '||to_char(dst_range(1),'mm/dd/yyyy hh24:mi')||' = '||dst_start);
          dst_range(2) := dst_stop;
          DBMS_OUTPUT.PUT_LINE('      dst_range(2): '||to_char(dst_range(2),'mm/dd/yyyy hh24:mi')||' = '||dst_stop);
          DBMS_OUTPUT.PUT_LINE('  ** Finish: dst_start_stop Func **');
          Return dst_range;
       END dst_start_stop;
    
       Function is_dst_now
            Return boolean
          AS
              --type date_array is table of date;
              dst_range date_array;
              curr_time date := LocalTimestamp;
          Begin
              dst_range := date_array();
              dst_range.extend(2);
              dst_range := dst_start_stop(curr_time);
              If (dst_range(1) <= curr_time and curr_time < dst_range(2))  then 
                DBMS_OUTPUT.PUT_LINE('DST is  active.');
                Return True;
              Else
                DBMS_OUTPUT.PUT_LINE('DST is NOT active.');
                Return False;
              End If;  
       End;
    
       FUNCTION dst_offset (prevailing_date DATE, dst_start DATE, dst_stop DATE) 
            RETURN number
          AS
                offset_days number :=0;
          BEGIN
    
              DBMS_OUTPUT.PUT_LINE('  Starting dst_offset sub-function:');
              DBMS_OUTPUT.PUT_LINE('        where (input date, DST start, DST stop) = '||to_char(prevailing_date,'mm/dd/yyyy hh24:mi')
                                    ||', '||to_char(dst_start,'mm/dd/yyyy hh24:mi')||', '||to_char(dst_stop,'mm/dd/yyyy hh24:mi'));
    
              If (dst_start <= prevailing_date and prevailing_date < dst_stop) then 
                offset_days :=1/24;
                DBMS_OUTPUT.PUT_LINE('        input date is between dst start and stop');
              Else
                offset_days :=0;
                DBMS_OUTPUT.PUT_LINE('        input date is not between dst start and stop');
              End If;
              DBMS_OUTPUT.PUT_LINE('        Result: DST Offset days = '||offset_days);
              DBMS_OUTPUT.PUT_LINE('                          hours = '||(offset_days*24)); 
              Return offset_days;
       END dst_offset;
    
    -- Begin  --move this down under the function -- ******************
    
       FUNCTION Convert_TZ (input_date DATE, input_tz varchar2, output_tz varchar2) 
       RETURN date
       AS
            -- Variables
            input_sz varchar(3) := substr(input_TZ,1,1)||'S'||substr(input_TZ,3,1);
            input_sz varchar(3) := substr(output_TZ,1,1)||'S'||substr(output_TZ,3,1);
            temp_str varchar2(1000);
            dst_range date_array;
            input_dst_offset number := 0;
            input_tz_offset number := 0;
            input_date_st date; --standard time
            gmt_date date;
            output_dst_offset number := 0;
            output_tz_offset number := 0;
            output_date date;
            output_date_pt date; -- prevailing time
            tz_offset_str varchar2(30);
            --orig_nls_date_format varchar2(30) := NLS_DATE_FORMAT;
            --TempYear number(4,0) := to_char(TempDate,'YYYY');  -- or := trunc(PrevailingTime, YYYY);
    
       BEGIN 
    
          DBMS_OUTPUT.PUT_LINE('Starting Pl/sql procedure.  ');
    
          DBMS_OUTPUT.PUT_LINE('Input Date: '||to_char(input_date,'mm/dd/yyyy hh24:mi')||' '||input_tz||'.  ');
          -- Find DST start/stop dates  
          dst_range := date_array();
          dst_range.extend(2);
          dst_range := dst_start_stop(input_date);
          DBMS_OUTPUT.PUT_LINE('DST date range determined.  ');
          DBMS_OUTPUT.PUT_LINE('      dst_range(1): '||to_char(dst_range(1),'mm/dd/yyyy hh24:mi')||' = '||dst_range(1));
          DBMS_OUTPUT.PUT_LINE('      dst_range(2): '||to_char(dst_range(2),'mm/dd/yyyy hh24:mi')||' = '||dst_range(2));
    
          -- Convert Input Date from input time zone to GMT
            If upper(input_TZ) in ('GMT','UCT') then
                -- If input TZ is GMT, we can skip this conversion!
                DBMS_OUTPUT.PUT_LINE('    Input Time is ('||to_char(input_date,'mm/dd/yyyy hh24:mi')||' '
                                      ||input_tz||' GMT).  No conversion required.  ');
                input_tz_offset := 0;
                input_dst_offset := 0; 
                gmt_date := input_date;
            Else
                -- Convert from local prevailing to local standard time
                    -- Get input_dst_offset 
                    Case upper(substr(input_TZ,2,1)) 
                      When 'S' then 
                        -- already in standard time, not conversion needed.
                        input_dst_offset := 0; -- duplicative
                        DBMS_OUTPUT.PUT_LINE('Standard time ('||input_tz||') entered; no dst offset.' );
                              --input_tz_offset := input_tz_offset;
                      Else 
                        -- run dst_offset function to convert from prevailing or daylight time to standard time.
                        input_dst_offset := dst_offset(input_date, dst_range(1), dst_range(2));
                        input_date_st := input_date - input_dst_offset; 
                        DBMS_OUTPUT.PUT_LINE('Daylight Saving Time Effective ('||to_char(input_date,'mm/dd/yyyy hh24:mi')||' '
                                              ||input_tz||'); 1 hour offset; input DST offset = '||input_dst_offset);
                        DBMS_OUTPUT.PUT_LINE('        where (input_date, dst_start, dst_stop) = '
                                              ||to_char(input_date,'mm/dd/yyyy hh24:mi')
                                              ||' '||input_tz||', '||dst_range(1)||', '||dst_range(2)||', ');
                        DBMS_OUTPUT.PUT_LINE('        which adjusts '||to_char(input_date,'mm/dd/yyyy hh24:mi')||' '||input_tz
                                              ||' daylight to '||input_date_st||' standard time.  ');
                    End Case;
    
                -- Convert from local standard time to GMT
                    SELECT TZ_OFFSET((SELECT max(tzname) FROM V$TIMEZONE_NAMES where tzabbrev = input_TZ)) 
                    INTO tz_offset_str 
                    FROM DUAL;
    
                    input_tz_offset := ( substr(tz_offset_str,1,3)/24 + substr(tz_offset_str,5,2)/1440 );
                    If is_dst_now then 
                      input_tz_offset := input_tz_offset - 1/24;
                    End if;
                    DBMS_OUTPUT.PUT_LINE('    input_tz_offset (fractional days): '||input_tz_offset||'. ');
                    DBMS_OUTPUT.PUT_LINE('    input_tz_offset (hours): '||input_tz_offset*24||'. ');
                    gmt_date := input_date_st - input_tz_offset;
    
            End If;
    
            -- Convert input date from GMT to requested output time zone
            DBMS_OUTPUT.PUT_LINE('  ');
            DBMS_OUTPUT.PUT_LINE('Starting output_date analysis. ');
            If upper(output_TZ) in ('GMT','UCT') then
                -- If desired output TZ is GMT, we can skip this conversion!
                output_tz_offset := 0;
                output_dst_offset := 0; 
                output_date := gmt_date;
                DBMS_OUTPUT.PUT_LINE('    Requested output format is GMT: ('||to_char(output_date,'mm/dd/yyyy hh24:mi')||' '||output_tz||').  No conversion required.  ');
            Else
                -- Get output_dst_offset 
                    Case upper(substr(output_TZ,2,1)) 
                      When 'S' then 
                        output_dst_offset := 0; -- duplicative
                        DBMS_OUTPUT.PUT_LINE('Standard time ('||output_TZ||') entered; no dst offset.' );
                      Else
                        output_dst_offset := dst_offset(gmt_date + output_tz_offset, dst_range(1), dst_range(2));
                    End Case;
                -- Convert from GMT to local standard time
                    SELECT TZ_OFFSET((SELECT max(tzname) FROM V$TIMEZONE_NAMES where tzabbrev = output_TZ)) INTO tz_offset_str FROM DUAL;
                    output_tz_offset := ( substr(tz_offset_str,1,3)/24 + substr(tz_offset_str,5,2)/1440 );
                    DBMS_OUTPUT.PUT_LINE('    output_tz_offset (fractional days): '||output_tz_offset||'. ');
                    DBMS_OUTPUT.PUT_LINE('    output_tz_offset (hours): '||output_tz_offset*24||'. ');
                    If is_dst_now then 
                      output_tz_offset := output_tz_offset - 1/24;
                      DBMS_OUTPUT.PUT_LINE('    tz_offset correction... ');
                      DBMS_OUTPUT.PUT_LINE('      output_tz_offset (fractional days): '||output_tz_offset||'. ');
                      DBMS_OUTPUT.PUT_LINE('      output_tz_offset (hours): '||output_tz_offset*24||'. ');
                    End if;
    
                        output_date := gmt_date + output_tz_offset + output_dst_offset;        
                        DBMS_OUTPUT.PUT_LINE('          gmt_date: '||gmt_date);
                        DBMS_OUTPUT.PUT_LINE('  output_tz_offset: '||output_tz_offset);
                        DBMS_OUTPUT.PUT_LINE(' output_dst_offset: '||output_dst_offset);
    
                        DBMS_OUTPUT.PUT_LINE('Daylight Saving Time ('||output_TZ||') offset = '||output_dst_offset);
                        DBMS_OUTPUT.PUT_LINE('        where (output_date, dst_start, DST_stop) = '||output_date||', '||dst_range(1)||', '||dst_range(2));
                        DBMS_OUTPUT.PUT_LINE('        which adjusts '||output_date||' standard to '||output_date_pt||' daylight time.  ');
            End If;
    
            DBMS_OUTPUT.PUT_LINE('Output Date = '||to_char(output_date,'mm/dd/yyyy hh24:mi')||' '||output_tz||'.  ');
    
        Goto AllDone;
    
        <<FoundError>>      
    
        <<AllDone>>
          DBMS_OUTPUT.PUT_LINE('   ');
          DBMS_OUTPUT.PUT_LINE('***    Results     ***');
          DBMS_OUTPUT.PUT_LINE('   ');
    
          if input_dst_offset <> 0 then
            temp_str := 'daylight saving';
          else
            temp_str := 'standard';
          end if;
          DBMS_OUTPUT.PUT_LINE('    Input Date: '||to_char(input_date,'mm/dd/yyyy hh24:mi')||' '||input_tz||', which falls in  '||temp_str||' time.  ');
          DBMS_OUTPUT.PUT_LINE('    GMT Date: '||to_char(gmt_date,'mm/dd/yyyy hh24:mi')||' UTC/GMT.  ');
          DBMS_OUTPUT.PUT_LINE('    Output Date: '||to_char(output_date,'mm/dd/yyyy hh24:mi')||' UTC/GMT.  ');
          if output_dst_offset <> 0 then
            temp_str := 'daylight saving';
          else
            temp_str := 'standard';
          end if;
          DBMS_OUTPUT.PUT_LINE('    All Done.  Return Value: '||to_char(output_date,'mm/dd/yyyy hh24:mi')||' '||output_tz||'.  ');
          DBMS_OUTPUT.PUT_LINE('   ');
          DBMS_OUTPUT.PUT_LINE('*** End of Results ***');
          DBMS_OUTPUT.PUT_LINE('   ');
          DBMS_OUTPUT.PUT_LINE('LocalTimestamp EPT: '||LocalTimestamp);
          DBMS_OUTPUT.PUT_LINE('LocalTimestamp GMT:'||LOCALTIMESTAMP at time zone '+00:00');
    
          Return output_date;
       END;  
    /*********************** End of Declarations of Variables and Functions for Time Zone Conversion (Convert_TZ function) *********************/  
    Begin  
    /*********************** Start of Procedural Code for  Time Zone Conversion (Convert_TZ function) *********************/  
         /* DOW list is required for Time Zone Conversion (Convert_TZ function) and should not be deleted. */
          dow_list := str_array();
          dow_list.extend(7);
          dow_list(1) := 'Sun';
          dow_list(2) := 'Mon';
          dow_list(3) := 'Tue';
          dow_list(4) := 'Wed';
          dow_list(5) := 'Thu';
          dow_list(6) := 'Fri';
          dow_list(7) := 'Sat';
    
         /* Next 2 lines are example of use of Time Zone Conversion (Convert_TZ function). */
          return_date := Convert_TZ (input_date, input_tz, output_tz);
          DBMS_OUTPUT.PUT_LINE('ta-dah! '||return_date);
    /*********************** End of Procedural Code for Time Zone Conversion (Convert_TZ function) *********************/  
    
    -- ******** User coded begins here ******** --
    End;    
    
    RTRIM( TO_CHAR( systimestamp at time zone 'GMT', 'DD-MON-YYYY HH24:MI:SS TZR' ))
    RTRIM( TO_CHAR( systimestamp at time zone 'EST', 'DD-MON-YYYY HH24:MI:SS TZR' ))
    RTRIM( TO_CHAR( systimestamp at time zone 'PST', 'DD-MON-YYYY HH24:MI:SS TZR' ))
    
    output:
       05-NOV-2014 17:20:10 GMT
       05-NOV-2014 12:20:10 EST
       05-NOV-2014 09:20:10 PST
    
    substr('05-NOV-2014 09:20:10 PST', -3, 3)   will return 'PST'
    
    RTRIM (EXTRACT (HOUR from systimestamp ))
    output:  17    (notice the 17 is in GMT time)