Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions core/src/main/java/org/apache/hop/core/Const.java
Original file line number Diff line number Diff line change
Expand Up @@ -973,6 +973,25 @@
public static final String HOP_AGGREGATION_ALL_NULLS_ARE_ZERO =
"HOP_AGGREGATION_ALL_NULLS_ARE_ZERO";

/**
* The FORMAT locale currently in effect (language_COUNTRY, for example {@code en_US} or {@code
* nl_BE}). Set when a lifecycle environment is enabled so pipelines can see which regional
* settings they are running under.
*/
@Variable(
description =
"The FORMAT locale in effect for number, currency and date conversion (for example en_US). Set automatically when a lifecycle environment with a format locale is enabled.")
public static final String HOP_FORMAT_LOCALE = "HOP_FORMAT_LOCALE";

/**
* The default timezone currently in effect (IANA id, for example {@code Europe/Brussels}). Set
* when a lifecycle environment is enabled.
*/
@Variable(
description =
"The default timezone in effect for date and timestamp conversion (IANA id, for example Europe/Brussels). Set automatically when a lifecycle environment with a timezone is enabled.")
public static final String HOP_TIMEZONE = "HOP_TIMEZONE";

/** The name of the variable containing an alternative default timestamp format */
@Variable(
description = "The name of the variable containing an alternative default timestamp format")
Expand Down Expand Up @@ -1342,7 +1361,7 @@
* @return The rounded floating point value
*/
@Deprecated(since = "2.10")
@SuppressWarnings("java:S1874") // Ignore BigDecimal RoundingModes as code will be removed

Check warning on line 1364 in core/src/main/java/org/apache/hop/core/Const.java

View workflow job for this annotation

GitHub Actions / build

Unsupported @SuppressWarnings("java:S1874")

Check warning on line 1364 in core/src/main/java/org/apache/hop/core/Const.java

View workflow job for this annotation

GitHub Actions / ui-tests

Unsupported @SuppressWarnings("java:S1874")
public static BigDecimal round(BigDecimal f, int places, int roundingMode) {
if (roundingMode == ROUND_HALF_CEILING) {
if (f.signum() >= 0) {
Expand Down Expand Up @@ -2148,7 +2167,7 @@
BufferedReader br;
try {
// Safe: resolving "hostname" through the PATH of the operator who started Hop is intended
@SuppressWarnings("java:S4036")

Check warning on line 2170 in core/src/main/java/org/apache/hop/core/Const.java

View workflow job for this annotation

GitHub Actions / build

Unsupported @SuppressWarnings("java:S4036")

Check warning on line 2170 in core/src/main/java/org/apache/hop/core/Const.java

View workflow job for this annotation

GitHub Actions / ui-tests

Unsupported @SuppressWarnings("java:S4036")
Process pr = Runtime.getRuntime().exec("hostname");
br = new BufferedReader(new InputStreamReader(pr.getInputStream()));
String line;
Expand Down Expand Up @@ -2214,7 +2233,7 @@
String mac = "none";
String os = getSystemOs();
String s = "";
boolean errorOccured = false;

Check warning on line 2236 in core/src/main/java/org/apache/hop/core/Const.java

View workflow job for this annotation

GitHub Actions / build

The value of the local variable errorOccured is not used

Check warning on line 2236 in core/src/main/java/org/apache/hop/core/Const.java

View workflow job for this annotation

GitHub Actions / ui-tests

The value of the local variable errorOccured is not used
if (os.equalsIgnoreCase("Windows NT")
|| os.equalsIgnoreCase("Windows 2000")
|| os.equalsIgnoreCase("Windows XP")
Expand Down
11 changes: 11 additions & 0 deletions core/src/main/java/org/apache/hop/i18n/RegionalSettings.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,13 @@ static Locale captureOperatingSystemLocale() {
return Locale.getDefault(Locale.Category.FORMAT);
}

/**
* The default timezone the JVM started with (including {@code -Duser.timezone}), captured for the
* same reason: a lifecycle environment can install another one, and an environment that inherits
* has to be able to put this one back.
*/
private static final TimeZone OPERATING_SYSTEM_TIME_ZONE = TimeZone.getDefault();

private static RegionalSettings instance;

private Source source;
Expand Down Expand Up @@ -222,4 +229,8 @@ public void setCustomLocale(Locale customLocale) {
public Locale getOperatingSystemLocale() {
return OPERATING_SYSTEM_LOCALE;
}

public TimeZone getOperatingSystemTimeZone() {
return (TimeZone) OPERATING_SYSTEM_TIME_ZONE.clone();
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ specific language governing permissions and limitations
under the License.
////
:imagesdir: ../assets/images
:openvar: ${
:closevar: }

:description: The Configuration perspective is the central place for all Apache Hop configuration options: general settings, look and feel, projects and plugin options.
= Configuration Perspective
Expand Down Expand Up @@ -362,12 +364,17 @@ The reason to untick both options is reproducibility against the preferred langu
Resolution order, most specific first:

. Field-level decimal, grouping, currency, date locale and date timezone on a transform field. These always win.
. The active xref:projects/projects-environments.adoc[lifecycle environment] format locale and timezone, when set.
. This installation tab (operating system, an explicit override, or the preferred language).
. The JVM / operating system default.

The default source is the operating system. Hop GUI, `hop-run` and `hop-server` then agree *on the same machine*, but a laptop on `nl_BE` and a hop-server on `en_US` still format blank fields differently. *Override regional settings* (and copying that `hop-config.json`) is the way to pin a locale for an installation. Transform field symbols remain the way to handle a file that does not match that locale.
The default source on this tab is the operating system. Hop GUI, `hop-run` and `hop-server` then agree *on the same machine*, but a laptop on `nl_BE` and a hop-server on `en_US` still format blank fields differently unless the lifecycle environment pins a locale (or you use *Override regional settings* and copy that `hop-config.json`). Transform field symbols remain the way to handle a file that does not match that locale.

Distributed Beam and Spark workers never load `hop-config.json`. They format with their own JVM locale and timezone unless the pipeline sets field-level symbols.
When an environment is enabled, Hop publishes the effective values as `{openvar}HOP_FORMAT_LOCALE{closevar}` and `{openvar}HOP_TIMEZONE{closevar}` and the start log line's `source=` shows `environment:name` instead of `installation:…`.

Distributed Beam and Spark workers never load `hop-config.json` or the lifecycle environment. They format with their own JVM locale and timezone unless the pipeline sets field-level symbols.

A hop-server JVM is expected to run one default environment for these conversion defaults. Mixing environments with different FORMAT locales in one process is not supported: `Locale.setDefault` and `TimeZone.setDefault` are process-wide. Field-level overrides still work per pipeline.

=== Plugins

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ Click it to open the environment menu, then choose *Add environment...*.
image:hop-gui/environment/environment-add.png[Add environment menu]

This opens the environment properties dialog.
The fields are grouped on tabs: *General*, *Configuration files*, and when the matching plugins are installed *Marketplace plugins* and *System resources*.
The fields are grouped on tabs: *General*, *Regional*, *Configuration files*, and when the matching plugins are installed *Marketplace plugins* and *System resources*.

image:hop-gui/environment/environment-dialog-general-tab.png[Environment Properties General tab,width="90%"]

Expand All @@ -139,6 +139,26 @@ image:hop-gui/environment/environment-dialog-general-tab.png[Environment Propert
|Canvas text|Large text drawn in the top-right of pipeline and workflow canvases when this environment is active|Yes|No|
|===

image:hop-gui/environment/environment-dialog-regional-tab.png[Environment Properties Regional tab,width="90%"]

.Environment Properties -- Regional
[id="tab-env-props-regional",cols="20%,45%,5%,5%,25%",options="header"]
|===
|Property|Description|Variables Supported|Mandatory|Default
|Format locale a|Locale used for decimal separator, grouping separator, currency and date formats when this environment is enabled in Hop GUI, `hop-run` and `hop-server`. Independent of the Hop GUI language.

Leave *Inherit installation / OS* to use the xref:hop-gui/perspective-configuration.adoc#_regional_settings[Configuration perspective Regional settings]. Field-level decimal, grouping and date locale options still override this default. The effective value is published as `'{openvar}HOP_FORMAT_LOCALE{closevar}'`.
|No|No|Inherit installation / OS
|Timezone a|IANA timezone used as the default for date and timestamp conversion (for example `Europe/Brussels`).

Leave *Inherit installation / OS* to keep the JVM default. Field-level date timezones still override this default. The effective value is published as `'{openvar}HOP_TIMEZONE{closevar}'`.
|No|No|Inherit installation / OS
|===

A preview under the two combos shows a sample number, a long date and the selected timezone, updated as you change the values.

Pinning locale and timezone on the environment is how a pipeline that omits field-level decimal and grouping symbols produces the same result on a laptop and on a hop-server. Without it, those defaults follow the machine, which is the classic "it works on my machine" failure.

image:hop-gui/environment/environment-dialog-configuration-files-tab.png[Environment Properties Configuration files tab,width="90%"]

.Environment Properties -- Configuration files
Expand Down
2 changes: 2 additions & 0 deletions docs/hop-user-manual/modules/ROOT/pages/variables.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,7 @@ The default is 5000 (5 seconds).
A value of 0 uses that default.
Set a positive number of milliseconds to change the interval.
A negative value, for example -1, disables the interval flush.
|HOP_FORMAT_LOCALE||The FORMAT locale in effect for number, currency and date conversion (for example `en_US`). Set automatically when a lifecycle environment with a format locale is enabled. See xref:projects/projects-environments.adoc[Projects and Environments].
|HOP_GLOBAL_LOG_VARIABLES_CLEAR_ON_EXPORT|N|Set this variable to N to preserve global log variables defined in pipeline / workflow Properties -> Log panel.
Changing it to true will clear it when export pipeline / workflow.
|HOP_GRAPH_LOGGING_CONNECTION||Set this variable to the name of a Neo4j connection or a graph database connection of a Cypher database to enable execution logging to that database.
Expand Down Expand Up @@ -321,6 +322,7 @@ Changing it to true will remove first and last enclosure symbol from the resulti
|HOP_SYSTEM_HOSTNAME||You can use this variable to speed up hostname lookup.
Hostname lookup is performed by Hop so that it is capable of logging the server on which a workflow or pipeline is executed.
|HOP_TEXT_TAB_SIZE|2|Number of spaces Tab inserts and Shift+Tab removes on the selected lines of a multi-line text field. A line that already starts with a tab gains another tab. Set the value to 0 to insert a tab on every selected line. Useful when writing JavaScript, JSON, XML, and similar files that benefit from proper indentation to recognize file or code structures. Resolved from the Hop configuration, then the environment variable, then a Java system property. See xref:hop-gui/shortcuts.adoc[Hop Shortcuts].
|HOP_TIMEZONE||The default timezone in effect for date and timestamp conversion (IANA id, for example `Europe/Brussels`). Set automatically when a lifecycle environment with a timezone is enabled. See xref:projects/projects-environments.adoc[Projects and Environments].
|HOP_TRANSFORM_PERFORMANCE_SNAPSHOT_LIMIT|0|The maximum number of transform performance snapshots to keep in memory.
Set to 0 to keep all snapshots indefinitely (default)
|HOP_USE_NATIVE_FILE_DIALOG|N|Set this value to Y if you want to use the system file open/save dialog when browsing files
Expand Down
144 changes: 144 additions & 0 deletions integration-tests/locale-eu/0001-format-number-defaults.hpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--

Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

-->
<pipeline>
<info>
<name>0001-format-number-defaults</name>
<name_sync_with_filename>Y</name_sync_with_filename>
<description>Number to String with empty decimal/grouping symbols, so FORMAT locale decides.</description>
<extended_description/>
<pipeline_version/>
<pipeline_type>Normal</pipeline_type>
<parameters>
</parameters>
<capture_transform_performance>N</capture_transform_performance>
<transform_performance_capturing_delay>1000</transform_performance_capturing_delay>
<transform_performance_capturing_size_limit>100</transform_performance_capturing_size_limit>
<created_user>-</created_user>
<created_date>2026/08/29 12:00:00.000</created_date>
<modified_user>-</modified_user>
<modified_date>2026/08/29 12:00:00.000</modified_date>
</info>
<notepads>
</notepads>
<order>
<hop>
<from>sample</from>
<to>to string</to>
<enabled>Y</enabled>
</hop>
<hop>
<from>to string</from>
<to>OUTPUT</to>
<enabled>Y</enabled>
</hop>
</order>
<transform>
<name>sample</name>
<type>DataGrid</type>
<description/>
<distribute>Y</distribute>
<custom_distribution/>
<copies>1</copies>
<partitioning>
<method>none</method>
<schema_name/>
</partitioning>
<fields>
<field>
<name>amount</name>
<type>Number</type>
<format/>
<currency/>
<decimal/>
<group/>
<length>-1</length>
<precision>-1</precision>
<set_empty_string>N</set_empty_string>
</field>
</fields>
<data>
<line>
<item>1234,56</item>
</line>
</data>
<attributes/>
<GUI>
<xloc>128</xloc>
<yloc>128</yloc>
</GUI>
</transform>
<transform>
<name>to string</name>
<type>SelectValues</type>
<description/>
<distribute>Y</distribute>
<custom_distribution/>
<copies>1</copies>
<partitioning>
<method>none</method>
<schema_name/>
</partitioning>
<fields>
<select_unspecified>N</select_unspecified>
<meta>
<name>amount</name>
<rename>amount</rename>
<type>String</type>
<length>-1</length>
<precision>-1</precision>
<conversion_mask>#0.00</conversion_mask>
<date_format_lenient>N</date_format_lenient>
<date_format_locale/>
<date_format_timezone/>
<lenient_string_to_number>N</lenient_string_to_number>
<encoding/>
<decimal_symbol/>
<grouping_symbol/>
<currency_symbol/>
<storage_type/>
</meta>
</fields>
<attributes/>
<GUI>
<xloc>320</xloc>
<yloc>128</yloc>
</GUI>
</transform>
<transform>
<name>OUTPUT</name>
<type>Dummy</type>
<description/>
<distribute>Y</distribute>
<custom_distribution/>
<copies>1</copies>
<partitioning>
<method>none</method>
<schema_name/>
</partitioning>
<attributes/>
<GUI>
<xloc>512</xloc>
<yloc>128</yloc>
</GUI>
</transform>
<transform_error_handling>
</transform_error_handling>
<attributes/>
</pipeline>
Loading
Loading