Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does CONVERT DATE fail with sy-subrc 12 when adding DAYLIGHT SAVING TIME?

Tags:

abap

The following code fails with sy-subrc=12.

CONVERT DATE '20191105'
    TIME '123000'
    DAYLIGHT SAVING TIME 'X'
    INTO TIME STAMP DATA(timestamp)
    TIME ZONE 'CET   '.

In the ABAP documentation it says:

12 : The specified time could not be converted, because dat, tim, or dst contain invalid or inconsistent values.

I noticed that when I remove the 'X' in sy-dayst in the debugger it does the conversion.

But of course I want to take daylight savings into consideration so how can I make this work?

like image 321
Cold_Class Avatar asked Jan 25 '23 17:01

Cold_Class


2 Answers

TL;DR

Never use DAYLIGHT SAVING TIME (except in very special cases if you're expert)

And well said by @konstantin :

"CONVERT DATE command automatically takes care of DST"

Explanation:

DAYLIGHT SAVING TIME 'X' can be used only if the local time (DATE and TIME) matches the one-hour (rarely two hours) interval to switch from summer time to winter time, for the given time zone (note that some time zones have no daylight saving time).

For instance, in 2003, Brazil switched to the winter time on March 9th (Sunday) at 2am; at 2am, the clocks had to be moved back one hour to 1am, and consequently 1:30am occured twice.

So, if ABAP has to convert the local time 2003/03/09 01:30:00 to the UTC time, it has to know if the local time is during the daylight saving time (winter time) or not (summer time), which respectively correspond to the UTC times 2003/03/09 03:30:00 or 2003/03/09 04:30:00.

Demonstration:

DATA: time_stamp TYPE timestamp,
      dat TYPE d,
      tim TYPE t,
      tz  TYPE ttzz-tzone.

tz = 'BRAZIL'. "UTC-03:00
dat = '20030309'.
tim = '013000'.

CONVERT DATE dat TIME tim DAYLIGHT SAVING TIME 'X' " winter time
        INTO TIME STAMP time_stamp TIME ZONE tz.
ASSERT time_stamp = '20030309033000'. " UTC time

CONVERT DATE dat TIME tim DAYLIGHT SAVING TIME ' ' " summer time
        INTO TIME STAMP time_stamp TIME ZONE tz.
ASSERT time_stamp = '20030309043000'. " UTC time

Now, the question is, how to know whether DAYLIGHT SAVING TIME 'X' has to be used or not. In fact, if a database table contains the local time I used in the example above, one can't know whether it's summer time or winter time because the daylight saving time is never assigned to a table column along with the date and time columns.


That's why SAP has introduced a solution, ~10 years ago, which made DAYLIGHT SAVING TIME almost obsolete. The actual problem was not the daylight saving time but the non-continuous time which could lead to issues like chronological sorting. The principle of the solution is to slow down the time during the daylight saving time switch, so that one given time never occurs twice and the time remains continuous. Technically, it affects the system variables SY-DATUM (date) and SY-UZEIT (time). In this "double hour" interval, two real seconds are needed to make one SAP second. This behavior is activated by default (value "on") via the profile parameter zdate/DSTswitch_contloctime which you may view via the transaction code RZ11. Below is the SAP time according to the old ("off") or new way ("on"), if the switch occurs at local time 2am (with "on", you don't see the switch):

off: 1:00, 1:30, 1:00, 1:30, 2:00
on : 1:00, 1:15, 1:30, 1:45, 2:00

Just to add complexity, note that CONVERT still have a one-hour gap. For example, with zdate/DSTswitch_contloctime set to "on" and without DAYLIGHT SAVING TIME, CONVERT will give these UTC timestamps respectively, there is a gap of one hour between 03:59:59 and 05:00:00:

3:00, 3:15, 3:30, 3:45, 5:00

But this gap is not a big drawback, compared to the risk of having chronological sort issues.

More information: https://blogs.sap.com/2009/12/09/daylight-saving-time-and-slowing-down-the-time/ and SAP note 950114 - Profile parameter zdate/DSTswitch_contloctime.


Note that SY-DAYST corresponds to the DST indicator of the current time of the application server (SY-DATUM, SY-UZEIT, and time zone being defined in table TTZCU). I don't see any possible usage of it. If you want to convert a time which was initially obtained with SY-DATUM and SY-UZEIT, into a UTC time stamp, you should use the method SYSTEMTSTMP_SYST2UTC of class CL_ABAP_TSTMP. Example to get the current time:

cl_abap_tstmp=>systemtstmp_syst2utc(
  EXPORTING
    syst_date = sy-datum
    syst_time = sy-uzeit
  IMPORTING
    utc_tstmp = DATA(now_utc) ).

As @konstantin demonstrated ("DATE '20190331' TIME '023000' [...] local time does not exist in germany"), CONVERT DATE without DAYLIGHT SAVING TIME may return sy-subrc=12 in rare occasions, if the input date and time correspond to something inside the "vanishing hour" due to the switch from winter time to summer time the if the time zone has DST. For instance: "at 2am, it is 3am", the local time "2:30am" doesn't exist at all.

like image 168
Sandra Rossi Avatar answered Jan 28 '23 07:01

Sandra Rossi


So you mean I don't need to pass DAYLIGHT SAVING TIME because CET will cause the Date to convert the correct way already?

tl;dr: Yes, the CONVERT DATE command automatically takes care of DST (at least for timezone CET). The sy-subrc==12 ist set for illegal arguments.

Here's a minimal program for your snippet that covers all the edge cases. I added the resulting timestamps in a readable format (manually edited) in the comments.

CONVERT DATE '20190101' TIME '000000' "" // german 'winter time' (no DST, GMT+1)
        INTO TIME STAMP DATA(timestamp0) "" // 2018-12-31T23:00:00Z
        TIME ZONE 'CET   '.

CONVERT DATE '20190701' TIME '000000' "" // german 'summer time' (DST, GMT+2)
        INTO TIME STAMP DATA(timestamp1) "" // 2019-06-30T22:00:00Z
        TIME ZONE 'CET   '.

CONVERT DATE '20190331' TIME '015959' "" // last second before non DST (GMT+1) ==> DST (GMT+2) transition
        INTO TIME STAMP DATA(timestamp2) "" // 2019-03-31T00:59:59
        TIME ZONE 'CET   '.

CONVERT DATE '20190331' TIME '023000' "" // local time does not exist in germany, it's skipped
        INTO TIME STAMP DATA(timestamp3) "" // '0' <-- is this initial?
        TIME ZONE 'CET   '.
""// sy-subrc == 12 here!

CONVERT DATE '20190331' TIME '030000' "" // first second of german DST (GMT+2)
        INTO TIME STAMP DATA(timestamp4) "" // 2019-03-31T01:00:00Z
        TIME ZONE 'CET   '.

CONVERT DATE '20191027' TIME '015959' "" // last second before SAP undefined time range (turn your system off now)
        INTO TIME STAMP DATA(timestamp5) ""// 2019-10-26T23:59:59Z
        TIME ZONE 'CET   '.

CONVERT DATE '20191027' TIME '023000' "" // in the undefined zone, could theoretically be both DST and non-DST
        INTO TIME STAMP DATA(timestamp6) "" 2019-10-27T00:30:00Z <-- but is treated as DST
        TIME ZONE 'CET   '.

CONVERT DATE '20191027' TIME '030000' "" // first second in non DST time (german winter, GMT+1 again)
        INTO TIME STAMP DATA(timestamp7) "" 2019-10-27T02:00:01Z
        TIME ZONE 'CET   '.

There a reason why modern programming languages handle datetime objects as something more complex than just 8 or 14 numeric character long strings. Dealing with different timezones and local vs. UTC data is annoying in ABAP because most of the SAP tables and dependent function modules and classes randomly assume that stored dates and times are either in UTC or local time (which is not even unique in case of DST!)

like image 33
koks der drache Avatar answered Jan 28 '23 06:01

koks der drache