package net.minecraft.client.renderer.item.properties.select; import com.ibm.icu.text.DateFormat; import com.ibm.icu.text.SimpleDateFormat; import com.ibm.icu.util.Calendar; import com.ibm.icu.util.TimeZone; import com.ibm.icu.util.ULocale; import com.mojang.serialization.Codec; import com.mojang.serialization.DataResult; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; import java.util.Date; import java.util.Optional; import java.util.concurrent.TimeUnit; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.Util; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.item.ItemDisplayContext; import net.minecraft.world.item.ItemStack; import org.jetbrains.annotations.Nullable; @Environment(EnvType.CLIENT) public class LocalTime implements SelectItemModelProperty { public static final String ROOT_LOCALE = ""; private static final long UPDATE_INTERVAL_MS = TimeUnit.SECONDS.toMillis(1L); private static final Codec TIME_ZONE_CODEC = Codec.STRING.comapFlatMap(string -> { TimeZone timeZone = TimeZone.getTimeZone(string); return timeZone.equals(TimeZone.UNKNOWN_ZONE) ? DataResult.error(() -> "Unknown timezone: " + string) : DataResult.success(timeZone); }, TimeZone::getID); private static final MapCodec DATA_MAP_CODEC = RecordCodecBuilder.mapCodec( instance -> instance.group( Codec.STRING.fieldOf("pattern").forGetter(data -> data.format), Codec.STRING.optionalFieldOf("locale", "").forGetter(data -> data.localeId), TIME_ZONE_CODEC.optionalFieldOf("time_zone").forGetter(data -> data.timeZone) ) .apply(instance, LocalTime.Data::new) ); public static final SelectItemModelProperty.Type TYPE = SelectItemModelProperty.Type.create( DATA_MAP_CODEC.flatXmap(LocalTime::create, localTime -> DataResult.success(localTime.data)), Codec.STRING ); private final LocalTime.Data data; private final DateFormat parsedFormat; private long nextUpdateTimeMs; private String lastResult = ""; private LocalTime(LocalTime.Data data, DateFormat parsedFormat) { this.data = data; this.parsedFormat = parsedFormat; } public static LocalTime create(String format, String localeId, Optional timeZone) { return create(new LocalTime.Data(format, localeId, timeZone)).getOrThrow(string -> new IllegalStateException("Failed to validate format: " + string)); } private static DataResult create(LocalTime.Data data) { ULocale uLocale = new ULocale(data.localeId); Calendar calendar = (Calendar)data.timeZone.map(timeZone -> Calendar.getInstance(timeZone, uLocale)).orElseGet(() -> Calendar.getInstance(uLocale)); SimpleDateFormat simpleDateFormat = new SimpleDateFormat(data.format, uLocale); simpleDateFormat.setCalendar(calendar); try { simpleDateFormat.format(new Date()); } catch (Exception var5) { return DataResult.error(() -> "Invalid time format '" + simpleDateFormat + "': " + var5.getMessage()); } return DataResult.success(new LocalTime(data, simpleDateFormat)); } @Nullable public String get(ItemStack itemStack, @Nullable ClientLevel clientLevel, @Nullable LivingEntity livingEntity, int i, ItemDisplayContext itemDisplayContext) { long l = Util.getMillis(); if (l > this.nextUpdateTimeMs) { this.lastResult = this.update(); this.nextUpdateTimeMs = l + UPDATE_INTERVAL_MS; } return this.lastResult; } private String update() { return this.parsedFormat.format(new Date()); } @Override public SelectItemModelProperty.Type type() { return TYPE; } @Environment(EnvType.CLIENT) record Data(String format, String localeId, Optional timeZone) { } }