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.Locale; 021import java.util.concurrent.atomic.AtomicReference; 022 023/** 024 * A CAS implementation of DateTimeFormatter (previously SimpleDateFormat) which 025 * caches results for the duration of a millisecond. 026 * 027 * @author Ceki Gülcü 028 * @since 0.9.29 029 */ 030public class CachingDateFormatter { 031 032 final DateTimeFormatter dtf; 033 final ZoneId zoneId; 034 final AtomicReference<CacheTuple> atomicReference; 035 036 static class CacheTuple { 037 final long lastTimestamp; 038 final String cachedStr; 039 040 public CacheTuple(long lastTimestamp, String cachedStr) { 041 super(); 042 this.lastTimestamp = lastTimestamp; 043 this.cachedStr = cachedStr; 044 } 045 } 046 047 public CachingDateFormatter(String pattern) { 048 this(pattern, null); 049 } 050 051 public CachingDateFormatter(String pattern, ZoneId aZoneId) { 052 this(pattern, aZoneId, null); 053 } 054 055 public CachingDateFormatter(String pattern, ZoneId aZoneId, Locale aLocale) { 056 if (aZoneId == null) { 057 this.zoneId = ZoneId.systemDefault(); 058 } else { 059 this.zoneId = aZoneId; 060 } 061 Locale locale = aLocale != null ? aLocale : Locale.getDefault(); 062 063 dtf = DateTimeFormatter.ofPattern(pattern).withZone(this.zoneId).withLocale(locale); 064 CacheTuple cacheTuple = new CacheTuple(-1, null); 065 this.atomicReference = new AtomicReference<>(cacheTuple); 066 } 067 068 public final String format(long now) { 069 CacheTuple localCacheTuple = atomicReference.get(); 070 CacheTuple oldCacheTuple = localCacheTuple; 071 072 if (now != localCacheTuple.lastTimestamp) { 073 Instant instant = Instant.ofEpochMilli(now); 074 String result = dtf.format(instant); 075 localCacheTuple = new CacheTuple(now, result); 076 // allow a single thread to update the cache reference 077 atomicReference.compareAndSet(oldCacheTuple, localCacheTuple); 078 } 079 return localCacheTuple.cachedStr; 080 } 081 082}