Showing posts with label date. Show all posts
Showing posts with label date. Show all posts

Tuesday, February 12, 2013

Thread safe SimpleDateFormat

Run this test class and you will see (as is not predictable run it few times):
/**
 * Please feel free to experiment - not only wrong data but sometimes number format exceptions...
 */
public class SimpleDateTest {

 static SimpleDateFormat df = new SimpleDateFormat("dd-MMM-yyyy");
 static String testdata[] = { "01-Jan-1999", "14-Feb-2001", "31-Dec-2007" };

 /**
  * Test method for SDF.
  */
 @Test
 public void testParse() {
  Runnable r[] = new Runnable[testdata.length];
  for (int i = 0; i < r.length; i++) {
   final int i2 = i;
   r[i] = new Runnable() {
    public void run() {
     try {
      for (int j = 0; j < 1000; j++) {
       String str = testdata[i2];
       String str2 = null;
//         synchronized(df) 
       {
        Date d = df.parse(str);
        str2 = df.format(d);
       }

       Assert.assertEquals("date conversion failed after "
         + j + " iterations.", str, str2);
      }
     } catch (ParseException e) {
      throw new RuntimeException("parse failed");
     }
    }
   };
   new Thread(r[i]).start();
  }
 }
}


Possible outputs are:
Exception in thread "Thread-0" junit.framework.ComparisonFailure: date conversion failed after 0 iterations. expected:<[01-Jan-1999]> but was:<[14-Feb-2001]>
 at junit.framework.Assert.assertEquals(Assert.java:85)

Exception in thread "Thread-0" Exception in thread "Thread-1" java.lang.NumberFormatException: For input string: "19992001.E199920014E"

Exception in thread "Thread-0" java.lang.NumberFormatException: multiple points
 at sun.misc.FloatingDecimal.readJavaFormatString(Unknown Source)

Exception in thread "Thread-0" java.lang.NumberFormatException: For input string: ""
 at java.lang.NumberFormatException.forInputString(Unknown Source)

And solution is synchronize usage of SimpleDateFormat, use ThreadLocal like here: ThreadSafeSimpleDateFormat:
tsd

or my last findings use improved version of ThreadLocal  that creates HashMaps of SDF inside: SafeSimpleDateFormat
public class SafeSimpleDateFormat
{
    private final String _format;
    private static final ThreadLocal<Map<String, SimpleDateFormat>> _dateFormats = new ThreadLocal<Map<String, SimpleDateFormat>>()
    {
        public Map<String, SimpleDateFormat> initialValue()
        {
            return new HashMap<String, SimpleDateFormat>();
        }
    };

    private SimpleDateFormat getDateFormat(String format)
    {
        Map<String, SimpleDateFormat> formatters = _dateFormats.get();
        SimpleDateFormat formatter = formatters.get(format);
        if (formatter == null)
        {
            formatter = new SimpleDateFormat(format);
            formatters.put(format, formatter);
        }
        return formatter;
    }

    public SafeSimpleDateFormat(String format)
    {
        _format = format;
    }

    public String format(Date date)
    {
        return getDateFormat(_format).format(date);
    }

    public String format(Object date)
    {
        return getDateFormat(_format).format(date);
    }

    public Date parse(String day) throws ParseException
    {
        return getDateFormat(_format).parse(day);
    }

    public void setTimeZone(TimeZone tz)
    {
        getDateFormat(_format).setTimeZone(tz);
    }

    public void setCalendar(Calendar cal)
    {
        getDateFormat(_format).setCalendar(cal);
    }

    public void setNumberFormat(NumberFormat format)
    {
        getDateFormat(_format).setNumberFormat(format);
    }

    public void setLenient(boolean lenient)
    {
        getDateFormat(_format).setLenient(lenient);
    }

    public void setDateFormatSymbols(DateFormatSymbols symbols)
    {
        getDateFormat(_format).setDateFormatSymbols(symbols);
    }

    public void set2DigitYearStart(Date date)
    {
        getDateFormat(_format).set2DigitYearStart(date);
    }
} 

Thursday, January 10, 2013

SimpleDataFormat - thread safe

If there is issue connected to dates in multi-threaded application and first to check is: do it use SimpleDateFormat.

Why you should never use SimpleDataFormat? It's not safe! 

.. and here is proof:


Run this test class and you will see (as is not predictable run it few times):

/**
 * Please feel free to experiment - not only wrong data but sometimes number format exceptions...
 */
public class SimpleDateTest {

 static SimpleDateFormat df = new SimpleDateFormat("dd-MMM-yyyy");
 static String testdata[] = { "01-Jan-1999", "14-Feb-2001", "31-Dec-2007" };

 /**
  * Test method for SDF.
  */
 @Test
 public void testParse() {
  Runnable r[] = new Runnable[testdata.length];
  for (int i = 0; i < r.length; i++) {
   final int i2 = i;
   r[i] = new Runnable() {
    public void run() {
     try {
      for (int j = 0; j < 1000; j++) {
       String str = testdata[i2];
       String str2 = null;
//         synchronized(df) 
       {
        Date d = df.parse(str);
        str2 = df.format(d);
       }

       Assert.assertEquals("date conversion failed after "
         + j + " iterations.", str, str2);
      }
     } catch (ParseException e) {
      throw new RuntimeException("parse failed");
     }
    }
   };
   new Thread(r[i]).start();
  }
 }
}

 

 

Possible outputs are:

Exception in thread "Thread-0" junit.framework.ComparisonFailure: date conversion failed after 0 iterations. expected:<[01-Jan-1999]> but was:<[14-Feb-2001]>
 at junit.framework.Assert.assertEquals(Assert.java:85)

 

Exception in thread "Thread-0" Exception in thread "Thread-1" java.lang.NumberFormatException: For input string: "19992001.E199920014E"

 

Exception in thread "Thread-0" java.lang.NumberFormatException: multiple points
 at sun.misc.FloatingDecimal.readJavaFormatString(Unknown Source)

 

Exception in thread "Thread-0" java.lang.NumberFormatException: For input string: ""
 at java.lang.NumberFormatException.forInputString(Unknown Source)

 

And solution is synchronize usage of SimpleDateFormat, use ThreadLocal like here: ThreadSafeSimpleDateFormat:

tsd

 

or my last findings use improved version of ThreadLocal  that creates HashMaps of SDF inside: SafeSimpleDateFormat 

import java.text.DateFormatSymbols;
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.TimeZone;

/**
 * This class implements a Thread-Safe (re-entrant) SimpleDateFormat
 * class.  It does this by using a ThreadLocal that holds a Map, instead
 * of the traditional approach to hold the SimpleDateFormat in a ThreadLocal.
 *
 * Each ThreadLocal holds a single HashMap containing SimpleDateFormats, keyed
 * by a String format (e.g. "yyyy/M/d", etc.), for each new SimpleDateFormat
 * instance that was created within the threads execution context.
 *
 * @author John DeRegnaucourt (jdereg@gmail.com)
 *         <br/>
 *         Copyright (c) John DeRegnaucourt
 *         <br/><br/>
 *         Licensed under the Apache License, Version 2.0 (the "License");
 *         you may not use this file except in compliance with the License.
 *         You may obtain a copy of the License at
 *         <br/><br/>
 *         http://www.apache.org/licenses/LICENSE-2.0
 *         <br/><br/>
 *         Unless required by applicable law or agreed to in writing, software
 *         distributed under the License is distributed on an "AS IS" BASIS,
 *         WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *         See the License for the specific language governing permissions and
 *         limitations under the License. */
public class SafeSimpleDateFormat
{
    private final String _format;
    private static final ThreadLocal<Map<String, SimpleDateFormat>> _dateFormats = new ThreadLocal<Map<String, SimpleDateFormat>>()
    {
        public Map<String, SimpleDateFormat> initialValue()
        {
            return new HashMap<String, SimpleDateFormat>();
        }
    };

    private SimpleDateFormat getDateFormat(String format)
    {
        Map<String, SimpleDateFormat> formatters = _dateFormats.get();
        SimpleDateFormat formatter = formatters.get(format);
        if (formatter == null)
        {
            formatter = new SimpleDateFormat(format);
            formatters.put(format, formatter);
        }
        return formatter;
    }

    public SafeSimpleDateFormat(String format)
    {
        _format = format;
    }

    public String format(Date date)
    {
        return getDateFormat(_format).format(date);
    }

    public String format(Object date)
    {
        return getDateFormat(_format).format(date);
    }

    public Date parse(String day) throws ParseException
    {
        return getDateFormat(_format).parse(day);
    }

    public void setTimeZone(TimeZone tz)
    {
        getDateFormat(_format).setTimeZone(tz);
    }

    public void setCalendar(Calendar cal)
    {
        getDateFormat(_format).setCalendar(cal);
    }

    public void setNumberFormat(NumberFormat format)
    {
        getDateFormat(_format).setNumberFormat(format);
    }

    public void setLenient(boolean lenient)
    {
        getDateFormat(_format).setLenient(lenient);
    }

    public void setDateFormatSymbols(DateFormatSymbols symbols)
    {
        getDateFormat(_format).setDateFormatSymbols(symbols);
    }

    public void set2DigitYearStart(Date date)
    {
        getDateFormat(_format).set2DigitYearStart(date);
    }
} 

 

 

Wednesday, December 12, 2012

Date for TimeZone, or google first.

This one is about: why you should use google before start writing code!

How effectively get system Date for different TimeZone? I found few different approaches: using over-complicated String operations, SimpleDateFormat, and Calendar. Here I want to share my thoughts and find the best one  ;-)

Below you could find 3 different examples:

  1. Using String operation
  2. SimpleDateFormat
  3. Calendar

Yes, as you probably expect - Calendar will win ;-)

 

String example 1:

 /**
  * This method gets the system date for the passed time zone
  * 
  * @param strTimeZone
  * @return
  */
 public static Date getSystemDateForTimeZoneString(String strTimeZone) {

  Date systemDate = null; 

  try {
   TimeZone timeZone = TimeZone.getTimeZone(strTimeZone);
   Calendar cal = GregorianCalendar.getInstance(timeZone);

   String monthStr = null;
   String dayStr = null;
   String hoursStr = null;
   String minStr = null;
   String secStr = null;

   int month = cal.get(Calendar.MONTH) + 1;

   if (month < 10) {
    monthStr = "0" + month;
   } else {
    monthStr = "" + month;
   }
   int day = cal.get(Calendar.DAY_OF_MONTH);
   if (day < 10) {
    dayStr = "0" + day;
   } else {
    dayStr = "" + day;
   }
   int year = cal.get(Calendar.YEAR);
   int hours = cal.get(Calendar.HOUR_OF_DAY);
   if (hours < 10) {
    hoursStr = "0" + hours;
   } else {
    hoursStr = "" + hours;
   }
   int mins = cal.get(Calendar.MINUTE);
   if (mins < 10) {
    minStr = "0" + mins;
   } else {
    minStr = "" + mins;
   }

   int sec = cal.get(Calendar.SECOND);
   if (sec < 10) {
    secStr = "0" + sec;
   } else {
    secStr = "" + sec;
   }

   String currentDate = monthStr + "-" + dayStr
     + "-" + year + " " + hoursStr + ":" + minStr
     + ":" + secStr;

   SimpleDateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy HH:mm:ss");
   try {
    systemDate = dateFormat.parse(currentDate);
   } catch (ParseException e) {
    e.printStackTrace();
   }

  } catch (Exception e) {
   e.printStackTrace();
  }
  return systemDate;
 }

 

Second implementation using SimpleDataFormat:

 public static Date getSystemDateForTimeZoneSDF(final String strTimeZone) {
  Date systemDate = new Date();
  SimpleDateFormat dateFormat = new SimpleDateFormat(
    "MM-dd-yyyy HH:mm:ss");
  dateFormat.setTimeZone(TimeZone.getTimeZone(strTimeZone));
  String timeZonedSystemDate = dateFormat.format(new Date());
  dateFormat.setTimeZone(TimeZone.getDefault());
  try {
   systemDate = dateFormat.parse(timeZonedSystemDate);
  } catch (ParseException e) {
   e.printStackTrace();
  }
  return systemDate;
 }

 

And last one, Calendar:

 

 public static Date getSystemDateForTimeZoneCalendar(final String strTimeZone) {
  Date date = new Date();
  TimeZone timeZone = TimeZone.getTimeZone(strTimeZone);

  // Returns the number of milliseconds since January 1, 1970, 00:00:00 GMT
  long msFromEpochGmt = date.getTime();

  // gives you the current offset in ms from GMT at the current date
  int offsetFromUTC = timeZone.getOffset(msFromEpochGmt);

  // create a new calendar in GMT timezone, set to this date and add the offset
  Calendar localCalendar = Calendar.getInstance(TimeZone.getDefault());
  localCalendar.setTime(date);
  localCalendar.add(Calendar.MILLISECOND, offsetFromUTC);

  return localCalendar.getTime();
 }

 

Lets run some test code:

  long start1 = System.currentTimeMillis();

  for (int i = 1; i < 100; i++) {
   getSystemDateForTimeZoneString("GMT-2:00");
  }
  long end1 = System.currentTimeMillis();
  System.out.println("String OUTPUT::" + getSystemDateForTimeZoneString("GMT-2:00")
    + " takes:" + (end1 - start1) + " ms");

  long start2 = System.currentTimeMillis();
  for (int i = 1; i < 100; i++) {
   getSystemDateForTimeZoneSDF("GMT-2:00");
  }
  long end2 = System.currentTimeMillis();
  System.out.println("SDF OUTPUT::" + getSystemDateForTimeZoneSDF("GMT-2:00")
    + " takes:" + (end2 - start2) + " ms");

  long start3 = System.currentTimeMillis();
  for (int i = 1; i < 100; i++) {
   getSystemDateForTimeZoneCalendar("GMT-2:00");
  }
  long end3 = System.currentTimeMillis();
  System.out.println("Calendar OUTPUT::" + getSystemDateForTimeZoneCalendar("GMT-2:00")
    + " takes:" + (end3 - start3) + " ms");
 }

 

 

And output:

String      OUTPUT::Tue Jan 08 10:12:08 GMT 2013 takes: 127 ms
SDF         OUTPUT::Tue Jan 08 10:12:08 GMT 2013 takes:  19 ms
Calendar   OUTPUT::Tue Jan 08 10:12:08 GMT 2013 takes:   3 ms

 

Summary:

First thought that comes when I see some code using String calculations is: do not reinvent the wheel - google!!

Of course - as expected - not String solutions are much better - if you have application that is quite a lot using function like that  - you can save a lots of processing time.

 

Kowalski: The Rust-native Agentic AI Framework Evolves to v0.5.0 🦀

  TL;DR: Kowalski v0.5.0 brings deep refactoring, modular architecture, multi-agent orchestration, and robust docs across submodules. If yo...