From 285085d01f1fa5ffe55d88975f6155a486118aed Mon Sep 17 00:00:00 2001
From: Aaron <51387595+AzureAaron@users.noreply.github.com>
Date: Fri, 14 Feb 2025 21:12:32 -0500
Subject: Formatters (#1151)
* Formatters
* java yap
---------
Co-authored-by: Kevinthegreat <92656833+kevinthegreat1@users.noreply.github.com>
---
.../java/de/hysky/skyblocker/utils/Formatters.java | 88 ++++++++++++++++++++++
.../de/hysky/skyblocker/utils/FormattersTest.java | 61 +++++++++++++++
2 files changed, 149 insertions(+)
create mode 100644 src/main/java/de/hysky/skyblocker/utils/Formatters.java
create mode 100644 src/test/java/de/hysky/skyblocker/utils/FormattersTest.java
(limited to 'src')
diff --git a/src/main/java/de/hysky/skyblocker/utils/Formatters.java b/src/main/java/de/hysky/skyblocker/utils/Formatters.java
new file mode 100644
index 00000000..539f9d67
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/utils/Formatters.java
@@ -0,0 +1,88 @@
+package de.hysky.skyblocker.utils;
+
+import java.text.NumberFormat;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.util.Locale;
+
+import com.ibm.icu.text.DateTimePatternGenerator;
+
+import ca.weblite.objc.Client;
+import net.fabricmc.loader.api.FabricLoader;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.util.Util;
+
+/**
+ * Provides useful constants for formatting numbers and dates. If you need to make slight tweaks to a formatter
+ * then {@link NumberFormat#clone()} the object and modify it as needed.
+ */
+public class Formatters {
+ /**
+ * Formats numbers as integers with commas.
+ *
+ * Example: 100,000,000
+ */
+ public static final NumberFormat INTEGER_NUMBERS = NumberFormat.getIntegerInstance(Locale.US);
+ /**
+ * Formats numbers as floats with up to two digits of precision.
+ *
+ * Example: 100,000.15
+ */
+ public static final NumberFormat DOUBLE_NUMBERS = Util.make(NumberFormat.getInstance(Locale.US), nf -> nf.setMaximumFractionDigits(2));
+ /**
+ * Formats numbers as floats with up to one digit of precision.
+ *
+ * Example: 100,000.1
+ */
+ public static final NumberFormat FLOAT_NUMBERS = Util.make(NumberFormat.getInstance(Locale.US), nf -> nf.setMaximumFractionDigits(1));
+ /**
+ * Formats integer numbers in a short format.
+ *
+ * Examples: 10B, 1M, and 5K.
+ */
+ public static final NumberFormat SHORT_INTEGER_NUMBERS = NumberFormat.getCompactNumberInstance(Locale.US, NumberFormat.Style.SHORT);
+ /**
+ * Formats float numbers in a short format.
+ *
+ * Examples: 17.3B, 1.5M, and 10.8K.
+ */
+ public static final NumberFormat SHORT_FLOAT_NUMBERS = Util.make(NumberFormat.getCompactNumberInstance(Locale.US, NumberFormat.Style.SHORT), nf -> nf.setMinimumFractionDigits(1));
+ /**
+ * Formats dates to a standard format.
+ *
+ * Examples: Thu Jan 30 2025 2:00:10 PM, Thu Jan 30 2025 14:00:10
+ */
+ public static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("E MMM d yyyy " + getTimeFormat(), Locale.US).withZone(ZoneId.systemDefault());
+
+ /**
+ * Returns the formatting for the time, always returns 12 hour in development environments for testing purposes.
+ */
+ private static String getTimeFormat() {
+ return is12HourClock() || FabricLoader.getInstance().isDevelopmentEnvironment() ? "h:mm:ss a" : "HH:mm:ss";
+ }
+
+ /**
+ * Determines whether to use the 12 or 24 hour clock for formatting time.
+ *
+ * On macOS this reads the preference for the system clock's time format which accounts for whether a user
+ * chooses 12 or 24 hour time in the System Settings.
+ *
+ * On other platforms, the time format follows the default for the user's current locale.
+ *
+ * @see NSDateFormatter
+ * @see Unicode Locale Data Markup Language (LDML)
+ */
+ private static boolean is12HourClock() {
+ //The j formatting template returns the preferred formatting for the time
+ //If the format contains a (am/pm pattern) then the preference is to use the 12 hour clock, otherwise its the 24 hour clock
+ if (MinecraftClient.IS_SYSTEM_MAC) {
+ Object locale = Client.getInstance().send("NSLocale", "currentLocale");
+ String timeFormat = (String) Client.getInstance().send("NSDateFormatter", "dateFormatFromTemplate:options:locale:", "j", 0, locale);
+
+ return timeFormat.contains("a");
+ } else {
+ return DateTimePatternGenerator.getInstance(Locale.getDefault()).getBestPattern("j").contains("a");
+ }
+ }
+}
+
diff --git a/src/test/java/de/hysky/skyblocker/utils/FormattersTest.java b/src/test/java/de/hysky/skyblocker/utils/FormattersTest.java
new file mode 100644
index 00000000..a564eb06
--- /dev/null
+++ b/src/test/java/de/hysky/skyblocker/utils/FormattersTest.java
@@ -0,0 +1,61 @@
+package de.hysky.skyblocker.utils;
+
+import java.time.Instant;
+import java.util.TimeZone;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+public class FormattersTest {
+
+ @BeforeAll
+ public static void setupEnvironment() {
+ TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
+ }
+
+ @Test
+ void testIntegerNumbers() {
+ Assertions.assertEquals("100,000,000", Formatters.INTEGER_NUMBERS.format(100_000_000));
+ Assertions.assertEquals("99,999,999", Formatters.INTEGER_NUMBERS.format(99_999_999.4));
+ Assertions.assertEquals("88,888,888", Formatters.INTEGER_NUMBERS.format(88_888_888.5)); //Half even rounding
+ Assertions.assertEquals("77,777,777", Formatters.INTEGER_NUMBERS.format(77_777_776.7));
+ }
+
+ @Test
+ void testDoubleNumbers() {
+ Assertions.assertEquals("100,000,000.15", Formatters.DOUBLE_NUMBERS.format(100_000_000.152341));
+ Assertions.assertEquals("99,999,999.98", Formatters.DOUBLE_NUMBERS.format(99_999_999.978));
+ }
+
+ @Test
+ void testFloatNumbers() {
+ Assertions.assertEquals("100,000,000.8", Formatters.FLOAT_NUMBERS.format(100_000_000.7834));
+ Assertions.assertEquals("99,999,999.8", Formatters.FLOAT_NUMBERS.format(99_999_999.84243));
+ }
+
+ @Test
+ void testShortIntegerNumbers() {
+ Assertions.assertEquals("16B", Formatters.SHORT_INTEGER_NUMBERS.format(15_500_000_000L));
+ Assertions.assertEquals("10M", Formatters.SHORT_INTEGER_NUMBERS.format(10_200_000));
+ Assertions.assertEquals("5K", Formatters.SHORT_INTEGER_NUMBERS.format(5_000));
+ }
+
+ @Test
+ void testShortFloatNumbers() {
+ Assertions.assertEquals("14.5B", Formatters.SHORT_FLOAT_NUMBERS.format(14_500_000_000L));
+ Assertions.assertEquals("8.3M", Formatters.SHORT_FLOAT_NUMBERS.format(8_300_000));
+ Assertions.assertEquals("24.7K", Formatters.SHORT_FLOAT_NUMBERS.format(24_740));
+ }
+
+ @Test
+ void testDates() {
+ long Thu_Jan_30th_2025_at_4_10_00_PM = 1738253400000L;
+ long Fri_Jan_31st_2025_at_11_11_00_AM = 1738321860000L;
+ long Sat_Feb_1st_2025_at_12_00_01_AM = 1738368001000L;
+
+ Assertions.assertEquals("Thu Jan 30 2025 4:10:00 PM", Formatters.DATE_FORMATTER.format(Instant.ofEpochMilli(Thu_Jan_30th_2025_at_4_10_00_PM)));
+ Assertions.assertEquals("Fri Jan 31 2025 11:11:00 AM", Formatters.DATE_FORMATTER.format(Instant.ofEpochMilli(Fri_Jan_31st_2025_at_11_11_00_AM)));
+ Assertions.assertEquals("Sat Feb 1 2025 12:00:01 AM", Formatters.DATE_FORMATTER.format(Instant.ofEpochMilli(Sat_Feb_1st_2025_at_12_00_01_AM)));
+ }
+}
--
cgit