Just complementing @Ole V.V.'s answer, I've found another way of doing it using ThreeTenABP, but without needing a regex replacement (although I don't think it's much simpler, more on that below).
Using the DateTimeFormatterBuilder
you can use a HashMap
that maps the values of the offsets to a String
. So, for whole offset hours (like +03:00
), you can map the respective value to the string +3
and so on.
The only problem is that the ZoneOffset
has seconds precision. And the minimum and maximum values are, respectively, UTC-18:00:00
and UTC+18:00:00
. So all possible values are UTC-18:00:00
, UTC-17:59:59
, UTC-17:59:58
and so on. And the formatter requires that all possible values are mapped (otherwise it'll display the offset's seconds value), so the map will have more than 120K entries!
To build this map, I've made 2 loops:
- The first loop maps the whole hour offsets (
+01:00
to +1
, -02:00
to -2
and so on)
- The second loop maps all the other values (they remain unchanged):
- whole hours >= 10 (like
+10:00
)
- not-whole hours (like
+05:30
)
The code to create the formatter:
// builds a hashmap to map all offset values
Map<Long, String> map = new HashMap<>();
// First loop: Map whole hours from 1 to 9 and from -9 to -1
// So a "+01:00" offset is displayed as "+1"
for (int i = 1; i <= 9; i++) {
long seconds = ZoneOffset.ofHours(i).getTotalSeconds();
// 1 hour offset maps to "+1" and so on
map.put(seconds, "+" + i);
// -1 hour offset maps to "-1" and so on
map.put(-seconds, "-" + i);
}
// second loop: Need to map all other possible values for not-whole hours
// offsets like "+10:00" and "+01:30" are not changed
int minOffset = ZoneOffset.MIN.getTotalSeconds();
int maxOffset = ZoneOffset.MAX.getTotalSeconds();
for (long i = minOffset; i <= maxOffset; i++) {
// the map already contains whole hours, don't need to overwrite the values
if (!map.containsKey(i)) {
// uses default String representation (like "+05:30")
map.put(i, ZoneOffset.ofTotalSeconds((int) i).getId());
}
}
// create the formatter
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
// year/month/day and the "|"
.appendPattern("uuuu/MM/dd | ")
// use the map of custom values (offset will use the values in the map)
.appendText(ChronoField.OFFSET_SECONDS, map)
// create formatter
.toFormatter();
Some tests:
LocalDateTime dt = LocalDateTime.of(2017, 5, 1, 10, 0);
ZonedDateTime z = ZonedDateTime.of(dt, ZoneId.of("America/Sao_Paulo")); // UTC-03:00
System.out.println(formatter.format(z)); // 2017/05/01 | -3
// just picking some timezone in Arabia Stardard Time
// (there are more than one timezone in AST: https://en.wikipedia.org/wiki/UTC%2B03:00#Arabia_Standard_Time)
// I don't know in which one you are
z = ZonedDateTime.of(dt, ZoneId.of("Asia/Qatar")); // UTC+03:00
System.out.println(formatter.format(z)); // 2017/05/01 | +3
// 2-digit offset
z = ZonedDateTime.of(dt, ZoneId.of("Asia/Vladivostok")); // UTC+10:00
System.out.println(formatter.format(z)); // 2017/05/01 | +10:00
// not whole hour
z = ZonedDateTime.of(dt, ZoneId.of("Asia/Kathmandu")); // UTC+05:45
System.out.println(formatter.format(z)); // 2017/05/01 | +05:45
The output is:
2017/05/01 | -3
2017/05/01 | +3
2017/05/01 | +10:00
2017/05/01 | +05:45
Notes:
- I don't know if having a 120K-entries map is better than using regular expressions (it's up to you to decide). This approach creates a big map, but at least doesn't require output post-processing (not sure if that's a reasonable trade-off)
- If you want whole-hour offsets >= 10 to be displayed as
+10
, +11
and so on, just change the first for
loop to for (int i = 1; i <= maxOffsetYouWant; i++)
- just reminding that the maximum possible value for maxOffsetYouWant
is 18.