001/*
002 * SPDX-License-Identifier: Apache-2.0
003 *
004 * Copyright 2025-2026 The Enola <https://enola.dev> Authors
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 *     https://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package dev.enola.common.time;
019
020import dev.enola.common.context.Singleton;
021import dev.enola.common.context.TLC;
022
023import java.time.ZoneId;
024import java.time.ZoneOffset;
025
026/**
027 * ZoneIdSupplierTLC is a {@link ZoneIdSupplier} implementation that looks up the current TZ from
028 * the {@link TLC}. If it's not found there, then it checks the (static) {@link #SINGLETON}.
029 * Otherwise, it falls back to one passed to the constructor.
030 */
031public final class ZoneIdSupplierTLC implements ZoneIdSupplier {
032
033    public static final Singleton<ZoneId> SINGLETON = new Singleton<>() {};
034
035    /**
036     * Falls back to (variable) {@link ZoneId#systemDefault()} if no TZ has been pushed to the
037     * {@link TLC} nor set on the {@link #SINGLETON}.
038     *
039     * <p>Using this in tests could lead to flaky tests which depend on the local OS default.
040     */
041    public static final ZoneIdSupplierTLC JVM_DEFAULT =
042            new ZoneIdSupplierTLC(ZoneId.systemDefault());
043
044    /**
045     * Falls back to (constant) {@link ZoneOffset#UTC} if no Locale has been pushed to the {@link
046     * TLC} nor set on the {@link #SINGLETON}.
047     */
048    public static final ZoneIdSupplierTLC UTC = new ZoneIdSupplierTLC(ZoneOffset.UTC);
049
050    private final ZoneId falbackZoneId;
051
052    /** Creates a new instance which falls back to the TZ passed to this constructor. */
053    public ZoneIdSupplierTLC(ZoneId fallback) {
054        this.falbackZoneId = fallback;
055    }
056
057    @Override
058    public ZoneId get() {
059        return TLC.optional(ZoneId.class).or(SINGLETON::getOptional).orElse(falbackZoneId);
060    }
061}