Skip to content

Commit 919ee65

Browse files
authored
Fix date time format in ICU4J, ICU4C, ICU4X (#297)
* Basic lint applied * Remove unneeded include * Updating to use Z as timezone in generator and in Java * ICU4J date time format - add calendar, fixing many issues * Fix date/time generator to use instant. Remove 'und' locale * Fix ICU4C DateTime parsing of 'Z', resolving many test failures * Fix Rust date/time to accept new UTC instant data. Still not handling timezones correctly * formatted .rs file * Removing debug printing from datatime_fmt.cpp * Add tz_offset_secs to date/time test data so ICU4X can use timezone * Trying to fix timezone setting in ICU4X datetime * Fixing about 1/2 of the ICU4X test failures * Update based on feedback * Moving code lines as suggested
1 parent f89ea86 commit 919ee65

File tree

12 files changed

+305
-192
lines changed

12 files changed

+305
-192
lines changed

executors/cpp/datetime_fmt.cpp

Lines changed: 38 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,9 @@ const string TestDatetimeFmt(json_object *json_in) {
4848
string label_string = json_object_get_string(label_obj);
4949

5050
Calendar *cal = nullptr;
51-
TimeZone *tz = nullptr;
51+
52+
UnicodeString u_tz_utc("UTC");
53+
TimeZone *tz = nullptr; // TimeZone::createTimeZone(u_tz_utc);
5254

5355
// The locale for formatted output
5456
json_object *locale_label_obj = json_object_object_get(json_in, "locale");
@@ -65,46 +67,55 @@ const string TestDatetimeFmt(json_object *json_in) {
6567
json_object *return_json = json_object_new_object();
6668
json_object_object_add(return_json, "label", label_obj);
6769

68-
string calendar_str;
70+
string calendar_str = "gregory";
6971

7072
// Get fields out of the options if present
7173
json_object* options_obj = json_object_object_get(json_in, "options");
7274

7375
if (options_obj) {
74-
json_object* cal_item = json_object_object_get(options_obj, "calendar");
76+
// Check for timezone and calendar
77+
json_object* option_item =
78+
json_object_object_get(options_obj, "timeZone");
79+
if (option_item) {
80+
string timezone_str = json_object_get_string(option_item);
81+
UnicodeString u_tz(timezone_str.c_str());
82+
tz = TimeZone::createTimeZone(u_tz);
83+
}
84+
85+
json_object* cal_item =
86+
json_object_object_get(options_obj, "calendar");
7587
if (cal_item) {
7688
calendar_str = json_object_get_string(cal_item);
77-
78-
// Add '@calendar=' + calendar_string to locale
79-
locale_string = locale_string + "@calendar=" + calendar_str;
80-
display_locale = locale_string.c_str();
81-
82-
if (tz) {
83-
cal = Calendar::createInstance(*tz, display_locale, status);
84-
} else {
85-
cal = Calendar::createInstance(display_locale, status);
86-
}
87-
if (U_FAILURE(status)) {
88-
json_object_object_add(
89-
return_json,
90-
"error",
91-
json_object_new_string("Error in createInstance for calendar"));
92-
return json_object_to_json_string(return_json);
93-
}
9489
}
9590
}
9691

92+
// Add '@calendar=' + calendar_string to locale
93+
locale_string = locale_string + "@calendar=" + calendar_str;
94+
display_locale = locale_string.c_str();
95+
96+
if (tz) {
97+
cal = Calendar::createInstance(tz, display_locale, status);
98+
} else {
99+
cal = Calendar::createInstance(display_locale, status);
100+
}
101+
if (U_FAILURE(status)) {
102+
json_object_object_add(
103+
return_json,
104+
"error",
105+
json_object_new_string("Error in createInstance for calendar"));
106+
return json_object_to_json_string(return_json);
107+
}
108+
97109
DateFormat* df;
98110

99111

100112
// Get the input data as a date object.
101113
// Types of input:
102-
// "input_string" parsable ISO formatted string such as
103-
// "2020-03-02 10:15:17 -08:00"
114+
// "input_string" parsable ISO formatted string of an instant
115+
// "2020-03-02 10:15:17Z
104116

105117
string dateStyle_str;
106118
string timeStyle_str;
107-
string timezone_str;
108119

109120
// Expected values if neither dateStyle nor timeStyle is given explicitly.
110121
icu::DateFormat::EStyle date_style = icu::DateFormat::EStyle::kNone;
@@ -126,17 +137,6 @@ const string TestDatetimeFmt(json_object *json_in) {
126137
timeStyle_str = json_object_get_string(option_item);
127138
time_style = StringToEStyle(timeStyle_str);
128139
}
129-
130-
option_item = json_object_object_get(options_obj, "timeZone");
131-
if (option_item) {
132-
timezone_str = json_object_get_string(option_item);
133-
UnicodeString u_tz(timezone_str.c_str());
134-
tz = TimeZone::createTimeZone(u_tz);
135-
} else {
136-
// Default is UTC
137-
UnicodeString u_tz("UTC");
138-
tz = TimeZone::createTimeZone(u_tz);
139-
}
140140
}
141141

142142
json_object *date_skeleton_obj =
@@ -183,10 +183,9 @@ const string TestDatetimeFmt(json_object *json_in) {
183183
}
184184

185185
// !!! IS OFFSET ALREADY CONSIDERED?
186-
// if (tz) {
187-
// df->setTimeZone(*tz);
188-
// }
189-
186+
if (tz) {
187+
df->setTimeZone(*tz);
188+
}
190189

191190
// Use ISO string form of the date/time.
192191
json_object *input_string_obj =
@@ -219,7 +218,7 @@ const string TestDatetimeFmt(json_object *json_in) {
219218
UnicodeString date_ustring(input_date_string.c_str());
220219

221220
// TODO: handles the offset +/-
222-
SimpleDateFormat iso_date_fmt(u"y-M-d'T'h:m:s", und_locale, status);
221+
SimpleDateFormat iso_date_fmt(u"y-M-d'T'h:m:sZ", und_locale, status);
223222
if (U_FAILURE(status)) {
224223
string error_name = u_errorName(status);
225224
string error_message =

executors/icu4j/74/executor-icu4j/src/main/java/org/unicode/conformance/testtype/datetimeformatter/DateTimeFormatterDateStyle.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ public enum DateTimeFormatterDateStyle {
44
FULL,
55
LONG,
66
MEDIUM,
7-
SHORT;
7+
SHORT,
8+
UNDEFINED;
89

9-
public static org.unicode.conformance.testtype.datetimeformatter.DateTimeFormatterDateStyle DEFAULT = MEDIUM;
10+
public static org.unicode.conformance.testtype.datetimeformatter.DateTimeFormatterDateStyle DEFAULT = UNDEFINED;
1011

1112
public static org.unicode.conformance.testtype.datetimeformatter.DateTimeFormatterDateStyle getFromString(
1213
String s) {
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
package org.unicode.conformance.testtype.datetimeformatter;
22

3+
import java.time.Instant;
4+
35
import java.util.Date;
46
import com.ibm.icu.util.Calendar;
7+
import com.ibm.icu.util.TimeZone;
8+
9+
import java.util.Locale;
510

611
import org.unicode.conformance.testtype.ITestTypeInputJson;
712

@@ -11,11 +16,13 @@ public class DateTimeFormatterInputJson implements ITestTypeInputJson {
1116

1217
public String label;
1318

14-
public String locale;
19+
public String locale_string;
20+
public Locale locale_with_calendar;
1521

16-
// UTC formatted time
22+
// UTC formatted instant in time
1723
public String inputString;
1824

25+
public Instant time_instant;
1926
public Date myDate;
2027

2128
public String skeleton;
@@ -25,13 +32,13 @@ public class DateTimeFormatterInputJson implements ITestTypeInputJson {
2532
public DateTimeFormatterTimeStyle timeStyle;
2633

2734
// TODO!!!
28-
public String calendarString;
29-
// Set calendar from calendarString!
35+
public String calendar_string;
36+
// Set calendar from calendar_string!
3037
public Calendar calendar;
3138

3239
public String numberingSystem;
3340

34-
public String timeZone;
41+
public TimeZone timeZone;
3542

3643
public String timeZoneName;
3744
}

executors/icu4j/74/executor-icu4j/src/main/java/org/unicode/conformance/testtype/datetimeformatter/DateTimeFormatterTester.java

Lines changed: 43 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,26 @@
11
package org.unicode.conformance.testtype.datetimeformatter;
22

3-
import java.text.ParseException;
4-
import java.time.LocalDateTime;
5-
import java.time.ZoneId;
6-
import java.time.format.DateTimeFormatter;
7-
83
import java.util.Date;
4+
import java.time.Instant;
5+
6+
import java.util.Locale;
7+
import java.util.Locale.Builder;
98

109
import com.ibm.icu.util.Calendar;
1110
import com.ibm.icu.text.DateFormat;
11+
import com.ibm.icu.util.TimeZone;
1212
import com.ibm.icu.util.ULocale;
1313

1414
import io.lacuna.bifurcan.IMap;
1515
import io.lacuna.bifurcan.Map;
1616

17-
1817
import org.unicode.conformance.ExecutorUtils;
1918
import org.unicode.conformance.testtype.ITestType;
2019
import org.unicode.conformance.testtype.ITestTypeInputJson;
2120
import org.unicode.conformance.testtype.ITestTypeOutputJson;
2221

2322
public class DateTimeFormatterTester implements ITestType {
23+
private static final int UNDEFINED_DATETIME_STYLE = -1;
2424

2525
public static DateTimeFormatterTester INSTANCE = new DateTimeFormatterTester();
2626

@@ -29,32 +29,27 @@ public ITestTypeInputJson inputMapToJson(Map<String, Object> inputMapData) {
2929
DateTimeFormatterInputJson result = new DateTimeFormatterInputJson();
3030

3131
result.label = (String) inputMapData.get("label", null);
32-
result.locale = (String) inputMapData.get("locale", null);
32+
result.locale_string = (String) inputMapData.get("locale", null);
3333
result.skeleton = (String) inputMapData.get("skeleton", null);
3434

35+
// The instant in UTC time.
3536
result.inputString = (String) inputMapData.get("input_string", null);
3637

3738
java.util.Map<String, Object> inputOptions =
3839
(java.util.Map<String, Object>) inputMapData.get("options", null);
3940

40-
result.timeZone = (String) inputOptions.get("timeZone");
41-
ZoneId thisZoneId;
42-
if (result.timeZone == null) {
43-
thisZoneId = ZoneId.systemDefault();
41+
result.timeZoneName = (String) inputOptions.get("timeZone");
42+
if (result.timeZoneName == null) {
43+
result.timeZone = TimeZone.GMT_ZONE;
4444
} else {
45-
thisZoneId = ZoneId.of(result.timeZone);
45+
result.timeZone = TimeZone.getTimeZone(result.timeZoneName);
4646
}
4747

4848
// Extract ISO part of the input string to parse.
49-
String inputStringDateTime = result.inputString.substring(0, 25);
49+
result.time_instant = Instant.parse(result.inputString);
50+
result.myDate = Date.from(result.time_instant);
5051

5152
// For parsing the input string and converting to java.util.date
52-
LocalDateTime parsedLocalDateTime =
53-
LocalDateTime.parse(inputStringDateTime, DateTimeFormatter.ISO_OFFSET_DATE_TIME);
54-
result.myDate =
55-
java.util.Date.from(parsedLocalDateTime.atZone(thisZoneId)
56-
.toInstant());
57-
5853
result.dateStyle = DateTimeFormatterDateStyle.getFromString(
5954
"" + inputOptions.get("dateStyle")
6055
);
@@ -63,15 +58,15 @@ public ITestTypeInputJson inputMapToJson(Map<String, Object> inputMapData) {
6358
"" + inputOptions.get("timeStyle")
6459
);
6560

66-
result.calendarString = (String) inputOptions.get("calendar");
61+
result.calendar_string = (String) inputOptions.get("calendar");
6762

68-
// TODO!!! Get calendar object. Depends on timezone and locale.
69-
// Just a placeholder for now.
70-
result.calendar = Calendar.getInstance();
63+
result.locale_with_calendar = new Builder().setLanguageTag(result.locale_string)
64+
.setUnicodeLocaleKeyword("ca", result.calendar_string)
65+
.build();
7166

72-
result.numberingSystem = (String) inputOptions.get("numberingSystem");
67+
result.calendar = Calendar.getInstance(result.locale_with_calendar);
7368

74-
result.timeZoneName = (String) inputOptions.get("timeZoneName");
69+
result.numberingSystem = (String) inputOptions.get("numberingSystem");
7570

7671
return result;
7772
}
@@ -114,40 +109,44 @@ public String formatOutputJson(ITestTypeOutputJson outputJson) {
114109

115110
public String getDateTimeFormatterResultString(DateTimeFormatterInputJson input) {
116111

117-
ULocale locale = ULocale.forLanguageTag(input.locale);
112+
ULocale locale = ULocale.forLanguageTag(input.locale_string);
118113

119-
int dateStyle;
114+
int dateStyle = UNDEFINED_DATETIME_STYLE;
120115
switch (input.dateStyle) {
121116
case FULL:
122117
dateStyle = DateFormat.FULL;
123118
break;
124119
case LONG:
125120
dateStyle = DateFormat.LONG;
126121
break;
127-
default:
128122
case MEDIUM:
129123
dateStyle = DateFormat.MEDIUM;
130124
break;
131125
case SHORT:
132126
dateStyle = DateFormat.SHORT;
133127
break;
128+
default:
129+
dateStyle = UNDEFINED_DATETIME_STYLE; // Undefined
130+
break;
134131
}
135132

136-
int timeStyle;
133+
int timeStyle = UNDEFINED_DATETIME_STYLE;
137134
switch (input.timeStyle) {
138135
case FULL:
139136
timeStyle = DateFormat.FULL;
140137
break;
141138
case LONG:
142139
timeStyle = DateFormat.LONG;
143140
break;
144-
default:
145141
case MEDIUM:
146142
timeStyle = DateFormat.MEDIUM;
147143
break;
148144
case SHORT:
149145
timeStyle = DateFormat.SHORT;
150146
break;
147+
default:
148+
timeStyle = UNDEFINED_DATETIME_STYLE; // Undefined
149+
break;
151150

152151
}
153152

@@ -156,10 +155,22 @@ public String getDateTimeFormatterResultString(DateTimeFormatterInputJson input)
156155

157156
DateFormat dtf;
158157
if (input.skeleton != null) {
159-
dtf = DateFormat.getInstanceForSkeleton(cal, input.skeleton, locale);
158+
dtf = DateFormat.getInstanceForSkeleton(cal, input.skeleton, input.locale_with_calendar);
160159
} else {
161-
dtf = DateFormat.getDateTimeInstance(cal, dateStyle, timeStyle, locale);
160+
if (dateStyle != UNDEFINED_DATETIME_STYLE && timeStyle != UNDEFINED_DATETIME_STYLE) {
161+
dtf = DateFormat.getDateTimeInstance(cal, dateStyle, timeStyle, input.locale_with_calendar);
162+
} else
163+
if (dateStyle != UNDEFINED_DATETIME_STYLE) {
164+
dtf = DateFormat.getDateInstance(cal, dateStyle,input.locale_with_calendar);
165+
} else
166+
if (timeStyle != UNDEFINED_DATETIME_STYLE) {
167+
dtf = DateFormat.getTimeInstance(cal, timeStyle, input.locale_with_calendar);
168+
} else {
169+
dtf = DateFormat.getInstance(cal, input.locale_with_calendar);
170+
}
162171
}
172+
dtf.setCalendar(input.calendar);
173+
dtf.setTimeZone(input.timeZone);
163174

164175
return dtf.format(input.myDate);
165176
}

executors/icu4j/74/executor-icu4j/src/main/java/org/unicode/conformance/testtype/datetimeformatter/DateTimeFormatterTimeStyle.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ public enum DateTimeFormatterTimeStyle {
44
FULL,
55
LONG,
66
MEDIUM,
7-
SHORT;
7+
SHORT,
8+
UNDEFINED;
89

9-
public static org.unicode.conformance.testtype.datetimeformatter.DateTimeFormatterTimeStyle DEFAULT = MEDIUM;
10+
public static org.unicode.conformance.testtype.datetimeformatter.DateTimeFormatterTimeStyle DEFAULT = UNDEFINED;
1011

1112
public static org.unicode.conformance.testtype.datetimeformatter.DateTimeFormatterTimeStyle getFromString(
1213
String s) {

0 commit comments

Comments
 (0)