12)Which
time zone file is currently used?
Beside checking the ORA_TZFILE
parameter as described above, you can easily check whether you are using the
large or small time zone file with the following query:
SQL> SELECT COUNT(*) FROM
v$timezone_names;
For the default 'large' time zone file:
COUNT(*)
----------
1393 (1250 in Oracle9)
For the 'small' time zone file:
COUNT(*)
---------
636 (616 in Oracle9)
For the default 'large' time zone file:
COUNT(*)
----------
1393 (1250 in Oracle9)
For the 'small' time zone file:
COUNT(*)
---------
636 (616 in Oracle9)
The SESSIONTIMEZONE sql
function returns the value of the current session's time zone:
SQL> SELECT SESSIONTIMEZONE FROM
DUAL;
SESSIONTIMEZONE
---------------
+01:00
SESSIONTIMEZONE
---------------
+01:00
The session time zone can be set to:
- O/S local time zone
- Database time zone
- An absolute offset
- A named region
This can be done in 2 ways, the
first method consists to use one of the following ALTER SESSION SET TIME_ZONE
statements:
SQL> ALTER SESSION SET TIME_ZONE = local;
SQL> ALTER SESSION SET TIME_ZONE = dbtimezone;
SQL> ALTER SESSION SET TIME_ZONE = '-05:00';
SQL> ALTER SESSION SET TIME_ZONE = 'Europe/London';
SQL> ALTER SESSION SET TIME_ZONE = dbtimezone;
SQL> ALTER SESSION SET TIME_ZONE = '-05:00';
SQL> ALTER SESSION SET TIME_ZONE = 'Europe/London';
The alternative method is to set the
(client) operating system environment variable ORA_SDTZ:
ORA_SDTZ
= 'OS_TZ' | 'DB_TZ' | '[+ | -] hh:mm' | 'timezone_region'
$ ORA_SDTZ='OS_TZ'
$ export ORA_SDTZ
$ ORA_SDTZ='-05:00'
$ export ORA_SDTZ
$ export ORA_SDTZ
$ ORA_SDTZ='-05:00'
$ export ORA_SDTZ
If you do not want to set ORA_SDTZ
on all client machines, but still want to be in control over the session time
zone settings for all sessions, then consider using a logon trigger to the
database, in which you can set the session time zone specifically through ALTER
SESSION (as per point a above).
Note that the session timezone
defaults to an offset ( like +05:00), even if the unix TZ variable or Windows
timezone region is set to a named TZ. If you need the session timezone to be a
named timezone then you need to set ORA_SDTZ client (!) environment (or
registry on windows) with an Oracle TZ name.
We do not advice to set the session timezone to the database timezone unless you have a compelling reason to do so. It might be use full for example when your application logic (outside oracle) is doing the mapping to/from the locale timezone to UTC and you are always creating databases using UTC as database timezone .
ORA-1804 indicates that the database
cannot load the time zone file it wants to use. This error will come up every
time that a named time zone is used and the time zone file is not available or
corrupt.
16)Can I rely on Oracle time zones
definitions?
Oracle's time zone data is derived
from the public domain information available at ftp://elsie.nci.nih.gov/pub/ and based on
information maintained by the U.S. Navy Observatory.
Oracle's disclaimer reflects a similar statement as on U.S. Navy Observatory's website at http://tycho.usno.navy.mil/tzones.html:
"Unfortunately, no U.S. Web page can provide official information on world time zones because nations are sovereign powers that can and do change their timekeeping systems as they see fit."
Oracle's disclaimer reflects a similar statement as on U.S. Navy Observatory's website at http://tycho.usno.navy.mil/tzones.html:
"Unfortunately, no U.S. Web page can provide official information on world time zones because nations are sovereign powers that can and do change their timekeeping systems as they see fit."
Oracle periodically checks if a new
time zone data is available and updates Oracle files for a new release. There
is no fixed schedule for such updates.
If a new rule for the daylight saving time is introduced for any time zone and you need the Oracle information to be updated you should log a Service Request with Oracle Support Services requesting a patch for the new rule. Oracle Development will prepare a patched version of the time zone files timezlrg.dat and timezone.dat that you will have to install on the database server and clients. However, please be aware that this is a process that can take some time, so take note of the next question.
If a new rule for the daylight saving time is introduced for any time zone and you need the Oracle information to be updated you should log a Service Request with Oracle Support Services requesting a patch for the new rule. Oracle Development will prepare a patched version of the time zone files timezlrg.dat and timezone.dat that you will have to install on the database server and clients. However, please be aware that this is a process that can take some time, so take note of the next question.
For DSTv7 and below the timezone
transition tables cover a timeframe up to the year 2020, so for a date beyond
2020 you will get not the expected result. DST updates from DSTv9 onwards
expand this to the year 2040.
The Daylight Saving Transition data
for regions that change their Daylight Saving Transition rules frequently and
during irregular periods, such as Israel and Brazil, are quite frequently not
up to date. Other regions might also have made changes that means that the time
zone information in the database is "out of synch" with the real
world. Obviously this may affect the accuracy of the time data for these
regions stored in the TIMESTAMP WITH (LOCAL) TIME ZONE data type.
If you plan to use the TIMESTAMP WITH TIME ZONE data type, and you need to manage time data from these regions, you should use the time zone offset (for example, -05:00), instead of the time zone region name when specifying timestamp literals. When using offsets you are in complete charge about when you change this.
If you plan to use the TIMESTAMP WITH TIME ZONE data type, and you need to manage time data from these regions, you should use the time zone offset (for example, -05:00), instead of the time zone region name when specifying timestamp literals. When using offsets you are in complete charge about when you change this.
The TZ_OFFSET() sql function returns
the time zone offset displacement to the input time zone region.
SQL> SELECT TZ_OFFSET('US/Pacific') FROM DUAL;
TZ_OFFS
-------
-07:00
TZ_OFFS
-------
-07:00
The returned offset depends on the
date this statement is executed. For example, in the 'US/Pacific' time zone, it
may return '-07:00' or '-08:00' whether daylight saving is in effect or not.
To know the offset at a certain time
you need to check the deviation from UTC like this:
SQL> select to_timestamp_tz('2007-01-28 00:00:00 Australia/Melbourne',
'yyyy-mm-dd hh24:mi:ss tzr') at time zone '00:00' from dual;
TO_TIMESTAMP_TZ('2007-01-2800:00:00AUSTRALIA/MELBOURNE','YYYY-MM-DDHH24:MI:
---------------------------------------------------------------------------
27-JAN-07 13.00.00.000000000 +00:00
-- here you see 11 hour SUBSTRACTED, wich means the TZ offset is PLUS 11 UTC
SQL> select to_timestamp_tz('2007-06-28 00:00:00 Australia/Melbourne',
'yyyy-mm-dd hh24:mi:ss tzr') at time zone '00:00' from dual;
TO_TIMESTAMP_TZ('2007-06-2800:00:00AUSTRALIA/MELBOURNE','YYYY-MM-DDHH24:MI:
---------------------------------------------------------------------------
27-JUN-07 14.00.00.000000000 +00:00
-- here you see 10 hour SUBSTRACTED, wich means the TZ offset is PLUS 10 UTC
SQL> select to_timestamp_tz('2007-01-28 00:00:00 US/Pacific',
'yyyy-mm-dd hh24:mi:ss tzr') at time zone '00:00' from dual;
TO_TIMESTAMP_TZ('2007-01-2800:00:00US/PACIFIC','YYYY-MM-DDHH24:MI:SSTZR')AT
---------------------------------------------------------------------------
28-JAN-07 08.00.00.000000000 +00:00
-- here you see 8 hour ADDED, wich means the TZ offset is MINUS 8 UTC
SQL> select to_timestamp_tz('2007-06-28 00:00:00 US/Pacific',
'yyyy-mm-dd hh24:mi:ss tzr') at time zone '00:00' from dual;
TO_TIMESTAMP_TZ('2007-06-2800:00:00US/PACIFIC','YYYY-MM-DDHH24:MI:SSTZR')AT
---------------------------------------------------------------------------
28-JUN-07 07.00.00.000000000 +00:00
-- here you see 7 hour ADDED, wich means the TZ offset is MINUS 7 UTC
You can also use the sessiontimezone
and dbtimezone function in combination with tz_offset:
SQL> SELECT
TZ_OFFSET(SESSIONTIMEZONE), TZ_OFFSET(DBTIMEZONE) FROM DUAL;
TZ_OFFS TZ_OFFS
------- -------
+01:00 +00:00
TZ_OFFS TZ_OFFS
------- -------
+01:00 +00:00
There is no generic solution to this
because many different time zones 'map' onto the same offsets, and usually they
do so at different parts of the year. Even if we take the actual timestamp into
account we still cannot do this mapping
For example if you only have a
offset of +01:00 without a corresponding time zone then there is no way of
knowing whether this is the time in the UK during Summertime/DST (BST) or the
time in Central/Western Europe outside of DST.
However, even if we do know the
timestamp to go with the offset we can have problems. For example
"01-OCT-2006 14:00:00 +02:00" would map onto "Central European
Summer Time", and therefore be valid in all time zones like Europe/Paris,
Europe/Berlin, Europe/Madrid, etc. etc. In itself there is no way of
automatically choosing between these, but that's not much of a problem because
their DST end-dates are at the same time at the end of October, so choosing one
over the other is not going to hurt much. The bigger problem is that this same
timestamp is also valid for Africa/Cairo, because that has just come out of DST
and is back to it's standard offset of +02:00.
The recommended solution for this
would be to create your own function for this mapping, since you will usually
know that your users are only using a limited number of time zones anyway.
Therefore for your own use you will know if "01-OCT-2006 14:00:00
+02:00" is more likely to map to Europe/Rome than to Africa/Cairo, but
this is not something that the Oracle database can automatically determine for
you.
Time zone abbreviations are only
relevant when we want to specify that a specific time zone name should be
interpreted as it's "DST" version or it's "standard time"
version. Certain "words" are valid as a time zone name, and as a time
zone abbreviation - which can lead to issues understanding what's going on, but
is important to realize this. An example of this is "CET". This can
be used as a time zone abbreviation to for example the Europe/Berlin time zone
name to specify we mean standard time during the "end of DST overlap
hour". However, CET can also be used as a real time zone name, just like
"Europe/Berlin", "Europe/Paris" etc.
Time zone names are DST aware. Any timestamp using a time zone name will be automatically adjusted for DST if needed (if DST doesn't exist in a region then obviously it's not used, for example in time zone 'Japan'). Perhaps other than expected the "CET" time zone name, just like EST, MST, PST etc. is defined in Oracle as DST aware! Note that we're talking about "time zone names" here, not "time zone abbreviations". Despite the fact that these 3 letters are obviously an abbreviation in the literal sense of the word. This might at first seem counter-intuitive, since you might expect CET (EST/MST/PST...) to be static. However, the reason for this is usability. Most users using "Central European Time" expect that to reflect the "clock time", and not a notion of "standard time", which is different to "clock time" during the summer.
As stated this might initially seem "strange" to time zone experts, but when you realize the way it's been set up there should not be any problems using it. Afterall, if you want to use a non-DST time zone then you can always use an offset like +01:00.
In general good "usability advise would be to:
Time zone names are DST aware. Any timestamp using a time zone name will be automatically adjusted for DST if needed (if DST doesn't exist in a region then obviously it's not used, for example in time zone 'Japan'). Perhaps other than expected the "CET" time zone name, just like EST, MST, PST etc. is defined in Oracle as DST aware! Note that we're talking about "time zone names" here, not "time zone abbreviations". Despite the fact that these 3 letters are obviously an abbreviation in the literal sense of the word. This might at first seem counter-intuitive, since you might expect CET (EST/MST/PST...) to be static. However, the reason for this is usability. Most users using "Central European Time" expect that to reflect the "clock time", and not a notion of "standard time", which is different to "clock time" during the summer.
As stated this might initially seem "strange" to time zone experts, but when you realize the way it's been set up there should not be any problems using it. Afterall, if you want to use a non-DST time zone then you can always use an offset like +01:00.
In general good "usability advise would be to:
- Use full time zone names ("Europe/Berlin",
"Europe/London", "America/Detroit", etc. etc.) when
DST-awareness is wanted
- Use offsets, or GMT and UTC when you want to make sure
there is no DST involved
- Use no "3 letter" time zone names apart from
UTC (or GMT), because of the confusion with time zone abbreviations.
- Use time zone abbreviations only when needed to specify
the correct time during the overlap hour.
You can simply subtract 2 timestamps
from each other to get a interval, for example this calculates how long it is
since/before lunch on Christmas day:
declare
duration interval day(6) to second(6);
v_start timestamp := to_timestamp('25-DEC-2003 14:00:00.000000', 'DD-MON-YYYY HH24:MI:SSxFF');
v_end timestamp(6) := sysTimestamp;
begin
duration := v_end - v_start;
dbms_output.put_line ('Now: '||to_char(v_end,'DD-MON-YYYY HH24:MI:SSxFF'));
dbms_output.put_line ('Difference: '|| to_char(duration));
end;
/
duration interval day(6) to second(6);
v_start timestamp := to_timestamp('25-DEC-2003 14:00:00.000000', 'DD-MON-YYYY HH24:MI:SSxFF');
v_end timestamp(6) := sysTimestamp;
begin
duration := v_end - v_start;
dbms_output.put_line ('Now: '||to_char(v_end,'DD-MON-YYYY HH24:MI:SSxFF'));
dbms_output.put_line ('Difference: '|| to_char(duration));
end;
/
And it works the same in sql:
create table temp(start_TS
Timestamp(6), duration interval day(6) to second(6) );
Insert into temp values
(to_timestamp('25-DEC-2003 14:00:00.000000', 'DD-MON-YYYY HH24:MI:SSxFF'),
(sysTimeStamp - to_timestamp('25-DEC-2003 14:00:00.000000', 'DD-MON-YYYY HH24:MI:SSxFF')) );
Insert into temp values
(to_timestamp('25-DEC-2003 14:00:00.000000', 'DD-MON-YYYY HH24:MI:SSxFF'),
(sysTimeStamp - to_timestamp('25-DEC-2003 14:00:00.000000', 'DD-MON-YYYY HH24:MI:SSxFF')) );
In some cases you can be working with a time zone like
"US/Eastern" but you are interested in the abbreviation
"EST" or "EDT" (depending on DST). In the v$timezone_names
view you can see all the abbreviations associated with the time zone, like EST
and EDT etc., but you cannot see what the "current" correct
abbreviation is.
The way to get this from the database is by using the TO_CHAR function with he TZD format mask. If you use this on a TIMESTAMP WITH TIME ZONE you will receive the abbreviation associated with the time zone.
If you already work with a TSTZ then you can simply use the TO_CHAR, if you do not have a TSTZ yet, then you can construct this using the FROM_TZ and AT TIME ZONE functions.
For example, lets say your database time zone is US/Eastern (which in itself is not advisable btw, see question 1 above), and you want to make sure you get the correct EST/EDT dependent on the current date/time. In this case you can take the current time, cast that into a TSTZ, and do a TO_CHAR on it:
The way to get this from the database is by using the TO_CHAR function with he TZD format mask. If you use this on a TIMESTAMP WITH TIME ZONE you will receive the abbreviation associated with the time zone.
If you already work with a TSTZ then you can simply use the TO_CHAR, if you do not have a TSTZ yet, then you can construct this using the FROM_TZ and AT TIME ZONE functions.
For example, lets say your database time zone is US/Eastern (which in itself is not advisable btw, see question 1 above), and you want to make sure you get the correct EST/EDT dependent on the current date/time. In this case you can take the current time, cast that into a TSTZ, and do a TO_CHAR on it:
select
TO_CHAR(FROM_TZ(sys_extract_utc(systimestamp),'UTC') AT TIME ZONE
dbtimezone,'TZD') FROM DUAL;
If your database time zone is an
offset rather than a named time zone (which is advisable), then the above query
will fail because the "dbtimezone" function will return this offset.
In that case we can simply run the same statement with the named time zone you're
interested in:
select
TO_CHAR(FROM_TZ(sys_extract_utc(systimestamp),'UTC') AT TIME ZONE
'US/Pacific','TZD') FROM DUAL;
ORA-1882 is triggered when you
specify a incorrect time zone. For example:
select * from scott.emp
where to_timestamp(hiredate) = TIMESTAMP'2003-04-07 00:00:00 QWE';
where to_timestamp(hiredate) = TIMESTAMP'2003-04-07 00:00:00 QWE';
Because the time zone QWE that is
used in the TIMESTAMP literal does not exist you receive ORA-1882.
It is also possible to get ORA-1882 if case of a syntax error in the TIMESTAMP. Most syntax errors in TIMESTAMPS receive ORA-01861 but if the syntax error is on or after the 'seconds' part, it will be interpreted as a time zone. That time zone obviously does not exist so ORA-1882 is raised. For example, you accidentally add a letter 'o' instead of a number '0':
It is also possible to get ORA-1882 if case of a syntax error in the TIMESTAMP. Most syntax errors in TIMESTAMPS receive ORA-01861 but if the syntax error is on or after the 'seconds' part, it will be interpreted as a time zone. That time zone obviously does not exist so ORA-1882 is raised. For example, you accidentally add a letter 'o' instead of a number '0':
select * from scott.emp
where to_timestamp(hiredate) = TIMESTAMP'2003-04-07 00:00:00.00o';
where to_timestamp(hiredate) = TIMESTAMP'2003-04-07 00:00:00.00o';
This syntax error also shows up if
you try to use a 'comma' as the decimal separator in a TIMESTAMP literal. In
'normal' numbers you can set NLS_NUMERIC_CHARACTERS to set your preferences for
the decimal separator and the thousands separator. However, in TIMESTAMP
literals you should ALWAYS use a 'dot' as the decimal separator, regardless of
the setting of NLS_NUMERIC_CHARACTERS. For example:
select * from scott.emp
where to_timestamp(hiredate) = TIMESTAMP'2003-04-07 00:00:00,00';
where to_timestamp(hiredate) = TIMESTAMP'2003-04-07 00:00:00,00';
This will always fail, whatever the
NLS settings are.
It is also possible your RDBMS DST
version simply does not know the timezone, for example trying to set session timezone
to :
SQL> alter session
set time_zone='Africa/Dar_es_Salaam';
ERROR:
ORA-01882: timezone region not found
ERROR:
ORA-01882: timezone region not found
Will fail on an DSTv4 system since Dar-es-Salaam is only added in DSTv5.
Lastly you can also receive ORA-1882 when you query a timestamp, or a table that contains a timestamp, which uses a time zone which is not valid in the client's time zone files. Both the client's and the server's time zone files need to "agree" on a time zone before it's passed as valid. Therefore querying a Oracle10 database from a Oracle9 client can cause a ORA-1882 when the time zone UTC is used.
So first check with a client using
the same version and RDBMS DST patch applied to the client as found on the
server.
Using a Timezone Abbriviation
(select TZABBREV from V$TIMEZONE_NAMES;) with TO_TIMESTAMP_TZ or FROM_TZ
will also give a ORA-1882. You can only use Timezone Names (select TZNAME from
V$TIMEZONE_NAMES;)
There are some specific variables
you can set in Oracle Forms 9i to work with the Oracle time zone support. These
are specifically documented in the following note:
Note 207822.1 Time Zone Support in Forms 9i
Note 207822.1 Time Zone Support in Forms 9i
Please see Note 467722.1 DBMS_SCHEDULER And Time Zones ( DST
) Explained.
For when running trough DBMS_SCHEDULER a job and the output gives the wrong time.
Or your jobs run at wrong time after DST has changed.
For when running trough DBMS_SCHEDULER a job and the output gives the wrong time.
Or your jobs run at wrong time after DST has changed.
There are quite a few websites that
give very good and interesting background information about calendars. The information
on these websites often goes a lot further than the oracle scope and these
websites are in no way linked with Oracle.
28) Why does using Datetime Arithmetic on datatypes having
timezone information seams to give incorrect result ?
It's often not known that
using Datetime Arithmetic in most cases returns a DATE dataype. When using
for example:
SQL> select to_timestamp_tz('25/10/2009 02:20:00 UTC','DD/MM/YYYY HH24:MI:SS TZR') +5 +30/48 from dual;
TO_TIMESTAMP_TZ('25
-------------------
30-10-2009 17:20:00
SQL> select systimestamp +5 +30/48 from dual;
SYSTIMESTAMP+5+30/4
-------------------
01-11-2009 07:39:16
TO_TIMESTAMP_TZ('25
-------------------
30-10-2009 17:20:00
SQL> select systimestamp +5 +30/48 from dual;
SYSTIMESTAMP+5+30/4
-------------------
01-11-2009 07:39:16
the returned dataype is a
DATE, wich is a datatype that has no timezone information . This can be easely
seen when using a slightly different format in the session for DATE, TIMESTAMP
and TIMESTAMP WITH TIMEZONE datatypes.
ALTER SESSION SET NLS_TIMESTAMP_TZ_FORMAT ='DD/MM/YYYY HH24:MI:SS TZR TZD'
NLS_TIMESTAMP_FORMAT ='DD*MM*YYYY HH24:MI:SS'
NLS_DATE_FORMAT ='DD-MM-YYYY HH24:MI:SS';
Or to use the dump function to check
the returned datatype.
SQL> select dump(sysdate,1016) from dual;
DUMP(SYSDATE,1016)
---------------------------------------------------------------
Typ=13 Len=8: d9,7,a,17,d,c,35,0
SQL> select dump( systimestamp,1016) from dual;
DUMP(SYSTIMESTAMP,1016)
---------------------------------------------------------------
Typ=188 Len=20: d9,7,a,17,c,c,36,0,0,7c,31,25,1,0,5,0,0,0,0,0
SQL> select dump(sysdate +5 +30/48,1016) from dual;
DUMP(SYSDATE+5+30/48,1016)
---------------------------------------------------------------
Typ=13 Len=8: d9,7,a,1d,4,c,39,0
SQL> select dump( systimestamp +5 +30/48,1016) from dual;
DUMP(SYSTIMESTAMP+5+30/48,1016)
---------------------------------------------------------------
Typ=13 Len=8: d9,7,a,1d,4,c,3a,0
-- here you see the "type" changing from 188 to 13 (= date )
DUMP(SYSDATE,1016)
---------------------------------------------------------------
Typ=13 Len=8: d9,7,a,17,d,c,35,0
SQL> select dump( systimestamp,1016) from dual;
DUMP(SYSTIMESTAMP,1016)
---------------------------------------------------------------
Typ=188 Len=20: d9,7,a,17,c,c,36,0,0,7c,31,25,1,0,5,0,0,0,0,0
SQL> select dump(sysdate +5 +30/48,1016) from dual;
DUMP(SYSDATE+5+30/48,1016)
---------------------------------------------------------------
Typ=13 Len=8: d9,7,a,1d,4,c,39,0
SQL> select dump( systimestamp +5 +30/48,1016) from dual;
DUMP(SYSTIMESTAMP+5+30/48,1016)
---------------------------------------------------------------
Typ=13 Len=8: d9,7,a,1d,4,c,3a,0
-- here you see the "type" changing from 188 to 13 (= date )
Awesome post!
ReplyDeleteMust have a look atUseful Tips for your Wooden Tables