001/**
002 * Logback: the reliable, generic, fast and flexible logging framework.
003 * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
004 *
005 * This program and the accompanying materials are dual-licensed under
006 * either the terms of the Eclipse Public License v1.0 as published by
007 * the Eclipse Foundation
008 *
009 *   or (per the licensee's choosing)
010 *
011 * under the terms of the GNU Lesser General Public License version 2.1
012 * as published by the Free Software Foundation.
013 */
014package ch.qos.logback.core.util;
015
016import java.time.Instant;
017import java.time.OffsetDateTime;
018import java.time.ZoneId;
019import java.time.format.DateTimeFormatter;
020import java.util.concurrent.atomic.AtomicReference;
021
022/**
023 * A CAS implementation of DateTimeFormatter (previously SimpleDateFormat) which
024 * caches results for the duration of a millisecond.
025 * 
026 * @author Ceki Gülcü
027 * @since 0.9.29
028 */
029public class CachingDateFormatter {
030
031    final DateTimeFormatter dtf;
032    final ZoneId zoneId;
033    final AtomicReference<CacheTuple> atomicReference;
034
035    static class CacheTuple {
036        final long lastTimestamp;
037        final String cachedStr;
038
039        public CacheTuple(long lastTimestamp, String cachedStr) {
040            super();
041            this.lastTimestamp = lastTimestamp;
042            this.cachedStr = cachedStr;
043        }
044    }
045
046    public CachingDateFormatter(String pattern) {
047        this(pattern, null);
048    }
049
050    public CachingDateFormatter(String pattern, ZoneId aZoneId) {
051        if (aZoneId == null) {
052            this.zoneId = ZoneId.systemDefault();
053        } else {
054            this.zoneId = aZoneId;
055        }
056
057        dtf = DateTimeFormatter.ofPattern(pattern).withZone(this.zoneId);
058        CacheTuple cacheTuple = new CacheTuple(-1, null);
059        this.atomicReference = new AtomicReference<>(cacheTuple);
060    }
061
062    public final String format(long now) {
063        CacheTuple localCacheTuple = atomicReference.get();
064        CacheTuple oldCacheTuple = localCacheTuple;
065
066        if (now != localCacheTuple.lastTimestamp) {
067            Instant instant = Instant.ofEpochMilli(now);
068            String result = dtf.format(instant);
069            localCacheTuple = new CacheTuple(now, result);
070            // allow a single thread to update the cache reference
071            atomicReference.compareAndSet(oldCacheTuple, localCacheTuple);
072        }
073        return localCacheTuple.cachedStr;
074    }
075
076}