As the ABAP documentation of Formatting Settings explains:
The formatting settings are set as follows:
At the start of an internal session they are determined by the related default settings in the fixed values in the user master record of the current user.
Using the statement SET COUNTRY, this default setting for the current internal session can be overwritten using country-specific formats.
But as the ABAP documentation of SET COUNTRY makes clear, there is no way to query what's actually been set with this statement:
Do not confuse the statement SET COUNTRY with the obsolete addition COUNTRY of the statement SET LOCALE LANGUAGE, used for setting the text environment. In particular, it does not have a corresponding GET COUNTRY statement.
Indeed, the ABAP documentation of GET LOCALE - obsolete parameters mentions:
The addition COUNTRY was intended for reading the country key of the current text environment explicitly. cntry expects a character-like data object. The function of this addition was not implemented in full and the result is undefined.
The addition COUNTRY of the statement GET LOCALE does not extract the formatting setting that can be set using SET COUNTRY.
Which leaves me with a bit of a conundrum. I could determine my user defaults with FM SUSR_GET_USER_DEFAULTS. I could figure out the setting for the country from table T005X. But I have no way of figuring out which specific country format was set, or even if one was set in the active session!
How do I determine which formatting settings are currently active?
Bonus question: is there a way to figure this out in the Debugger?
Maybe you can use the function module CLSE_SELECT_USR01.
The following example:
REPORT test.
START-OF-SELECTION.
DATA: decimal_sign , separator.
PERFORM output.
SET COUNTRY 'US'.
PERFORM output.
FORM output.
CALL FUNCTION 'CLSE_SELECT_USR01'
* EXPORTING
* USERNAME = sy-uname
* IV_DELETE_BUFFER = ' '
IMPORTING
* X_USR01 =
* DATE_FORMAT =
decimal_sign = decimal_sign
separator = separator.
WRITE: / 'DECIMAL_SIGN', decimal_sign, 'separator', separator.
ENDFORM.
shows:
My default locale is DE, so I get the actual setting for decimals.
From your comment:
Unfortunately I have to parse and analyse output data that's prepared for screen display from potentially dozens of different form sources.
Do you get the output at runtime or a previous run? Because there is no time machine to get the locale from a call in the past :)
The ABAP statement SET COUNTRY may change the date format, the time format (since ABAP 7.02) and the number format, but there's officially no reverse way to get the current active country code (as you quoted in your question, based on the ABAP documentation). It's quite logical because, for instance, the current number format may be different from the current country code, so it's better to test the directly the kind of format you need to use, as follows.
To detect the current date format, use the official way which returns a character, whose possible values are described in the ABAP documentation of Date Formats):
DATA(current_date_format) = CL_ABAP_DATFM=>GET_DATFM( ).
To detect the current time format, use the official way which returns a character:
DATA(current_time_format) = CL_ABAP_TIMEFM=>GET_ENVIRONMENT_TIMEFM( ).
It returns one of the following values, with an example value corresponding to noon + 5 minutes and 10 seconds (the example value is given if it's output on at least 11 characters):
0 : 12:05:10 (0 to 23)
1 : 12:05:10 PM (0 to 12)
2 : 12:05:10 pm (0 to 12)
3 : 00:05:10 PM (0 to 11)
4 : 00:05:10 pm (0 to 11)
To detect the current number format, based on the idea from #Gert Beukema, you may do as follows:
DATA(current_number_format) = SWITCH usr01-dcpfm(
|{ 1000 NUMBER = ENVIRONMENT DECIMALS = 1 }|
WHEN '1.000,00' THEN ' '
WHEN '1,000.00' THEN 'X'
WHEN '1 000,00' THEN 'Y' ).
NB: the values , X and Y which are returned by this expression are the same values as those used in tables-columns USR01-DCPFM and T005X-XDEZP.
Related
As the ABAP documentation of Formatting Settings explains:
The formatting settings are set as follows:
At the start of an internal session they are determined by the related default settings in the fixed values in the user master record of the current user.
Using the statement SET COUNTRY, this default setting for the current internal session can be overwritten using country-specific formats.
But as the ABAP documentation of SET COUNTRY makes clear, there is no way to query what's actually been set with this statement:
Do not confuse the statement SET COUNTRY with the obsolete addition COUNTRY of the statement SET LOCALE LANGUAGE, used for setting the text environment. In particular, it does not have a corresponding GET COUNTRY statement.
Indeed, the ABAP documentation of GET LOCALE - obsolete parameters mentions:
The addition COUNTRY was intended for reading the country key of the current text environment explicitly. cntry expects a character-like data object. The function of this addition was not implemented in full and the result is undefined.
The addition COUNTRY of the statement GET LOCALE does not extract the formatting setting that can be set using SET COUNTRY.
Which leaves me with a bit of a conundrum. I could determine my user defaults with FM SUSR_GET_USER_DEFAULTS. I could figure out the setting for the country from table T005X. But I have no way of figuring out which specific country format was set, or even if one was set in the active session!
How do I determine which formatting settings are currently active?
Bonus question: is there a way to figure this out in the Debugger?
Maybe you can use the function module CLSE_SELECT_USR01.
The following example:
REPORT test.
START-OF-SELECTION.
DATA: decimal_sign , separator.
PERFORM output.
SET COUNTRY 'US'.
PERFORM output.
FORM output.
CALL FUNCTION 'CLSE_SELECT_USR01'
* EXPORTING
* USERNAME = sy-uname
* IV_DELETE_BUFFER = ' '
IMPORTING
* X_USR01 =
* DATE_FORMAT =
decimal_sign = decimal_sign
separator = separator.
WRITE: / 'DECIMAL_SIGN', decimal_sign, 'separator', separator.
ENDFORM.
shows:
My default locale is DE, so I get the actual setting for decimals.
From your comment:
Unfortunately I have to parse and analyse output data that's prepared for screen display from potentially dozens of different form sources.
Do you get the output at runtime or a previous run? Because there is no time machine to get the locale from a call in the past :)
The ABAP statement SET COUNTRY may change the date format, the time format (since ABAP 7.02) and the number format, but there's officially no reverse way to get the current active country code (as you quoted in your question, based on the ABAP documentation). It's quite logical because, for instance, the current number format may be different from the current country code, so it's better to test the directly the kind of format you need to use, as follows.
To detect the current date format, use the official way which returns a character, whose possible values are described in the ABAP documentation of Date Formats):
DATA(current_date_format) = CL_ABAP_DATFM=>GET_DATFM( ).
To detect the current time format, use the official way which returns a character:
DATA(current_time_format) = CL_ABAP_TIMEFM=>GET_ENVIRONMENT_TIMEFM( ).
It returns one of the following values, with an example value corresponding to noon + 5 minutes and 10 seconds (the example value is given if it's output on at least 11 characters):
0 : 12:05:10 (0 to 23)
1 : 12:05:10 PM (0 to 12)
2 : 12:05:10 pm (0 to 12)
3 : 00:05:10 PM (0 to 11)
4 : 00:05:10 pm (0 to 11)
To detect the current number format, based on the idea from #Gert Beukema, you may do as follows:
DATA(current_number_format) = SWITCH usr01-dcpfm(
|{ 1000 NUMBER = ENVIRONMENT DECIMALS = 1 }|
WHEN '1.000,00' THEN ' '
WHEN '1,000.00' THEN 'X'
WHEN '1 000,00' THEN 'Y' ).
NB: the values , X and Y which are returned by this expression are the same values as those used in tables-columns USR01-DCPFM and T005X-XDEZP.
I'm currently trying to perform a dynamic lossless assignment in an ABAP 7.0v SP26 environment.
Background:
I want to read in a csv file and move it into an internal structure without any data losses. Therefore, I declared the field-symbols:
<lfs_field> TYPE any which represents a structure component
<lfs_element> TYPE string which holds a csv value
Approach:
My current "solution" is this (lo_field is an element description of <lfs_field>):
IF STRLEN( <lfs_element> ) > lo_field->output_length.
RAISE EXCEPTION TYPE cx_sy_conversion_data_loss.
ENDIF.
I don't know how precisely it works, but seems to catch the most obvious cases.
Attempts:
MOVE EXACT <lfs_field> TO <lfs_element>.
...gives me...
Unable to interpret "EXACT". Possible causes: Incorrect spelling or comma error
...while...
COMPUTE EXACT <lfs_field> = <lfs_element>.
...results in...
Incorrect statement: "=" missing .
As the ABAP version is too old I also cannot use EXACT #( ... )
Example:
In this case I'm using normal variables. Lets just pretend they are field-symbols:
DATA: lw_element TYPE string VALUE '10121212212.1256',
lw_field TYPE p DECIMALS 2.
lw_field = lw_element.
* lw_field now contains 10121212212.13 without any notice about the precision loss
So, how would I do a perfect valid lossless assignment with field-symbols?
Don't see an easy way around that. Guess that's why they introduced MOVE EXACT in the first place.
Note that output_length is not a clean solution. For example, string always has output_length 0, but will of course be able to hold a CHAR3 with output_length 3.
Three ideas how you could go about your question:
Parse and compare types. Parse the source field to detect format and length, e.g. "character-like", "60 places". Then get an element descriptor for the target field and check whether the source fits into the target. Don't think it makes sense to start collecting the possibly large CASEs for this here. If you have access to a newer ABAP, you could try generating a large test data set there and use it to reverse-engineer the compatibility rules from MOVE EXACT.
Back-and-forth conversion. Move the value from source to target and back and see whether it changes. If it changes, the fields aren't compatible. This is unprecise, as some formats will change although the values remain the same; for example, -42 could change to 42-, although this is the same in ABAP.
To-longer conversion. Move the field from source to target. Then construct a slightly longer version of target, and move source also there. If the two targets are identical, the fields are compatible. This fails at the boundaries, i.e. if it's not possible to construct a slightly-longer version, e.g. because the maximum number of decimal places of a P field is reached.
DATA target TYPE char3.
DATA source TYPE string VALUE `123.5`.
DATA(lo_target) = CAST cl_abap_elemdescr( cl_abap_elemdescr=>describe_by_data( target ) ).
DATA(lo_longer) = cl_abap_elemdescr=>get_by_kind(
p_type_kind = lo_target->type_kind
p_length = lo_target->length + 1
p_decimals = lo_target->decimals + 1 ).
DATA lv_longer TYPE REF TO data.
CREATE DATA lv_longer TYPE HANDLE lo_longer.
ASSIGN lv_longer->* TO FIELD-SYMBOL(<longer>).
<longer> = source.
target = source.
IF <longer> = target.
WRITE `Fits`.
ELSE.
WRITE `Doesn't fit, ` && target && ` is different from ` && <longer>.
ENDIF.
I'm pretty new to progress and I want to ask a question.
How do I change variable (string) length in runtime?
ex.
define variable cname as char.
define variable clen as int.
cname= "".
DO cnts = 1 TO 5.
IF prc[cnts] <> "" THEN DO:
clen = clen + LENGTH(prc[cnts]).
cname = cname + prc[cnts].
END.
END.
Put cname format '???' at 1. /here change variable length/
Thanks for the reply
If the PUT statement is what you want to change, then
PUT UNFORMATTED cname.
will write the entire string out without having to worry about the length of the FORMAT phrase.
If you need something formatted, then
PUT UNFORMATTED STRING(cname, fill("X", clen)).
will do what you want. Look up the "STRING()" function in the ABL Ref docs.
In Progress 4GL all data is variable length.
This is one of the big differences between Progress and lots of other development environments. (And in my opinion a big advantage.)
Each datatype has a default format, which you can override, but that is only for display purposes.
Display format has no bearing on storage.
You can declare a field with a display format of 3 characters:
define variable x as character no-undo format "x(3)".
And then stuff 60 characters into the field. Progress will not complain.
x = "123456789012345678901234567890123456789012345678901234567890".
It is extremely common for 4gl application code to over-stuff variables and fields.
(If you then use SQL-92 to access the data you will hear much whining and gnashing of teeth from your SQL client. This is easily fixable with the "dbtool" utility.)
You change the display format when you define something:
define variable x as character no-undo format "x(30)".
or when you use it:
put x format "x(15)".
or
display x format "x(43)".
(And in many other ways -- these are just a couple of easy examples.)
Regardless of the display format the length() function will report the actual length of the data.
My dataset has WeekEndingDate and Sales. I am displaying a straight table with all the selected data but I need to have another table showing the following:
Sales (other columns...)
First week : 1,000
Last week : 1,350
Difference : 350
Difference %: 35%
My questions:
a) Can I have the above in one chart/table, or I need 4 different charts showing columns filtered by set expressions?
b) My strategy is having 2 variables (vMinWeek and vMaxWeek), and using them in set expressions. Is that the best route?
c) My set expressions (below) are not working - they sum the whole data set. Would you please help me understanding why?
=max ({$<WeekEndingDate={'$(vMinWeek)'}>} Sales)
Thank you for your help!
Mara
I think the reason your set isn't working is that your WeekEnd date is formatted as a date and your variable is formatted as a number.
The trick with Set Analysis is always to think what you would have to type in a list box to get to your answer. So even though QlikView stores WeekEnd 2014/08/18 as 41869 you can't type 41869 in the WeekEnd list box and get back that date. So I would make your variables of the form =date(min(WeekEnd)).
The second part of your question; getting the table you want. I would do like this. I make a loose table with the dimension values, dual is so that it sorts correctly in the chart we are going to build.
load dual(D,N) as DIM inline [
D,N
First Week,1
Last Week,2
Difference,3
Dif %,4
];
I like defining my variables in the script as well, so I would do this.
set vFirstWeek='=date(min(WeekEnd))';
set vLastWeek='=date(max(WeekEnd))';`
Then when building the straight table we use the dimension as DIM but because DIM isn't connected to anything we have to do some work to get it to display values that fit those dimension values. The num(,'# ##0') is just to format the % differently from the sums. For this to work the number format in the Number tab of the chart must be set to Expression Default.
if(DIM='First Week',num(sum({<WeekEnd={'$(vFirstWeek)'}>} Sales),'# ##0'),
if(DIM='Last Week',num(sum({<WeekEnd={'$(vLastWeek)'}>} Sales),'# ##0'),
if(DIM='Difference',num(sum({<WeekEnd={'$(vFirstWeek)'}>} Sales)-sum({<WeekEnd={'$(vLastWeek)'}>} Sales),'# ##0'),
if(DIM='Dif %',num((sum({<WeekEnd={'$(vFirstWeek)'}>} Sales)-sum({<WeekEnd={'$(vLastWeek)'}>} Sales))/sum({<WeekEnd={'$(vLastWeek)'}>} Sales),'0.00%')))))
I got a date of the type SYDATUM and wondering how to format it in a format like m/d/y.
I've found some snippets on the web, but they were not really helpful.
-thanks yor your help.
You should be more specific - what exactly do you want to do with the date (use type D internally, it's shorter and does the same thing).
Do you want to WRITE it to a list screen? Use the formatting options described in the documentation and online help:
WRITE l_my_date MM/DD/YYYY.
Do you want to convert it to a text variable? Very similar:
WRITE l_my_date TO l_my_text MM/DD/YYYY.
To set the date format in a SAPscript form, see the SET DATE MASK command.
To print the formatted date in a SmartForm, use the WRITE command and a temporary variable (yes, ugly, I know...)
Most controls (ALV Grid for example) should take care of the format automatically.
However - be careful when hard-coding the format into your application. Usually you don't have to do this because the system automatically uses the format specified in the user master data. This ensures that every user will see the date formatted according to their locale settings.
Normally it's better to export the date into the plant level country specific date format:
if w_country is initial.
select single LAND1
from T001W
into w_country
where WERKS eq w_the_plant.
endif.
SET COUNTRY w_country.
write w_the_date to w_export.
for example 03/04/2002 could be different date in different country.
You can try the keyword TRANSLATE. Alternatively suggest you could have a look at this link