From ab1d88247672d9d28ea90861f062c2423b790dea Mon Sep 17 00:00:00 2001 From: Enrique Zamudio Date: Wed, 6 Jan 2016 11:05:44 -0600 Subject: [PATCH] fix #45 add DATE12 --- src/main/java/com/solab/iso8583/IsoType.java | 8 +- src/main/java/com/solab/iso8583/IsoValue.java | 3 +- .../com/solab/iso8583/MessageFactory.java | 2 +- .../solab/iso8583/codecs/CompositeField.java | 3 +- .../solab/iso8583/parse/Date12ParseInfo.java | 108 ++++++++++++++++++ .../solab/iso8583/parse/FieldParseInfo.java | 2 + .../com/solab/iso8583/TestConfigParser.java | 2 +- .../java/com/solab/iso8583/TestFormats.java | 11 +- .../com/solab/iso8583/parse/TestDates.java | 13 +++ 9 files changed, 142 insertions(+), 10 deletions(-) create mode 100644 src/main/java/com/solab/iso8583/parse/Date12ParseInfo.java diff --git a/src/main/java/com/solab/iso8583/IsoType.java b/src/main/java/com/solab/iso8583/IsoType.java index 77a4e79..ae6555d 100644 --- a/src/main/java/com/solab/iso8583/IsoType.java +++ b/src/main/java/com/solab/iso8583/IsoType.java @@ -60,7 +60,9 @@ public enum IsoType { /** variable length with 4-digit header length. */ LLLLVAR(false, 0), /** variable length byte array with 4-digit header length. */ - LLLLBIN(false, 0); + LLLLBIN(false, 0), + /** Date in format yyMMddHHmmss. */ + DATE12(false,12); private boolean needsLen; private int length; @@ -80,7 +82,7 @@ public enum IsoType { return length; } - /** Formats a Date if the receiver is DATE10, DATE4, DATE_EXP or TIME; throws an exception + /** Formats a Date if the receiver is DATE10, DATE4, DATE_EXP, DATE12 or TIME; throws an exception * otherwise. */ public String format(final Date value, final TimeZone tz) { final SimpleDateFormat sdf; @@ -92,6 +94,8 @@ public enum IsoType { sdf = new SimpleDateFormat("yyMM"); } else if (this == TIME) { sdf = new SimpleDateFormat("HHmmss"); + } else if (this == DATE12) { + sdf = new SimpleDateFormat("yyMMddHHmmss"); } else { throw new IllegalArgumentException("Cannot format date as " + this); } diff --git a/src/main/java/com/solab/iso8583/IsoValue.java b/src/main/java/com/solab/iso8583/IsoValue.java index ea2eeff..f74c9cb 100644 --- a/src/main/java/com/solab/iso8583/IsoValue.java +++ b/src/main/java/com/solab/iso8583/IsoValue.java @@ -333,7 +333,8 @@ public class IsoValue implements Cloneable { buf = new byte[(length / 2) + (length % 2)]; } else if (type == IsoType.AMOUNT) { buf = new byte[6]; - } else if (type == IsoType.DATE10 || type == IsoType.DATE4 || type == IsoType.DATE_EXP || type == IsoType.TIME) { + } else if (type == IsoType.DATE10 || type == IsoType.DATE4 || + type == IsoType.DATE_EXP || type == IsoType.TIME || type == IsoType.DATE12) { buf = new byte[length / 2]; } //Encode in BCD if it's one of these types diff --git a/src/main/java/com/solab/iso8583/MessageFactory.java b/src/main/java/com/solab/iso8583/MessageFactory.java index eb35b09..f0c5182 100644 --- a/src/main/java/com/solab/iso8583/MessageFactory.java +++ b/src/main/java/com/solab/iso8583/MessageFactory.java @@ -446,7 +446,7 @@ public class MessageFactory { m.setField(i, val); if (val != null) { if (val.getType() == IsoType.NUMERIC || val.getType() == IsoType.DATE10 - || val.getType() == IsoType.DATE4 + || val.getType() == IsoType.DATE4 || val.getType() == IsoType.DATE12 || val.getType() == IsoType.DATE_EXP || val.getType() == IsoType.AMOUNT || val.getType() == IsoType.TIME) { diff --git a/src/main/java/com/solab/iso8583/codecs/CompositeField.java b/src/main/java/com/solab/iso8583/codecs/CompositeField.java index 3461e76..2babf98 100644 --- a/src/main/java/com/solab/iso8583/codecs/CompositeField.java +++ b/src/main/java/com/solab/iso8583/codecs/CompositeField.java @@ -86,7 +86,8 @@ public class CompositeField implements CustomBinaryField { if (v != null) { if (v.getType() == IsoType.NUMERIC || v.getType() == IsoType.DATE10 || v.getType() == IsoType.DATE4 || v.getType() == IsoType.DATE_EXP - || v.getType() == IsoType.AMOUNT || v.getType() == IsoType.TIME) { + || v.getType() == IsoType.AMOUNT || v.getType() == IsoType.TIME + || v.getType() == IsoType.DATE12) { pos += (v.getLength() / 2) + (v.getLength() % 2); } else { pos += v.getLength(); diff --git a/src/main/java/com/solab/iso8583/parse/Date12ParseInfo.java b/src/main/java/com/solab/iso8583/parse/Date12ParseInfo.java new file mode 100644 index 0000000..eaf187d --- /dev/null +++ b/src/main/java/com/solab/iso8583/parse/Date12ParseInfo.java @@ -0,0 +1,108 @@ +package com.solab.iso8583.parse; + +import com.solab.iso8583.CustomField; +import com.solab.iso8583.IsoType; +import com.solab.iso8583.IsoValue; + +import java.io.UnsupportedEncodingException; +import java.text.ParseException; +import java.util.Calendar; +import java.util.Date; + +/** + * Blabla. + * + * @author Enrique Zamudio + * Date: 06/01/16 10:52 AM + */ +public class Date12ParseInfo extends DateTimeParseInfo { + + public Date12ParseInfo() { + super(IsoType.DATE12, 12); + } + + @Override + public IsoValue parse(final int field, final byte[] buf, + final int pos, final CustomField custom) + throws ParseException, UnsupportedEncodingException { + if (pos < 0) { + throw new ParseException(String.format("Invalid DATE12 field %d position %d", + field, pos), pos); + } + if (pos+12 > buf.length) { + throw new ParseException(String.format("Insufficient data for DATE12 field %d, pos %d", + field, pos), pos); + } + //A SimpleDateFormat in the case of dates won't help because of the missing data + //we have to use the current date for reference and change what comes in the buffer + Calendar cal = Calendar.getInstance(); + //Set the month in the date + int year; + if (forceStringDecoding) { + year = Integer.parseInt(new String(buf, pos, 2, getCharacterEncoding()), 10); + cal.set(Calendar.MONTH, Integer.parseInt(new String(buf, pos, 2, getCharacterEncoding()), 10)-1); + cal.set(Calendar.DATE, Integer.parseInt(new String(buf, pos+2, 2, getCharacterEncoding()), 10)); + cal.set(Calendar.HOUR_OF_DAY, Integer.parseInt(new String(buf, pos+4, 2, getCharacterEncoding()), 10)); + cal.set(Calendar.MINUTE, Integer.parseInt(new String(buf, pos+6, 2, getCharacterEncoding()), 10)); + cal.set(Calendar.SECOND, Integer.parseInt(new String(buf, pos+8, 2, getCharacterEncoding()), 10)); + } else { + year = ((buf[pos] - 48) * 10) + buf[pos + 1] - 48; + cal.set(Calendar.MONTH, ((buf[pos+2] - 48) * 10) + buf[pos + 3] - 49); + cal.set(Calendar.DATE, ((buf[pos + 4] - 48) * 10) + buf[pos + 5] - 48); + cal.set(Calendar.HOUR_OF_DAY, ((buf[pos + 6] - 48) * 10) + buf[pos + 7] - 48); + cal.set(Calendar.MINUTE, ((buf[pos + 8] - 48) * 10) + buf[pos + 9] - 48); + cal.set(Calendar.SECOND, ((buf[pos + 10] - 48) * 10) + buf[pos + 11] - 48); + } + if (year > 50) { + cal.set(Calendar.YEAR, 1900+year); + } else { + cal.set(Calendar.YEAR, 2000+year); + } + cal.set(Calendar.MILLISECOND,0); + if (tz != null) { + cal.setTimeZone(tz); + } + adjustWithFutureTolerance(cal); + return new IsoValue<>(type, cal.getTime(), null); + } + + @Override + public IsoValue parseBinary(final int field, final byte[] buf, + final int pos, final CustomField custom) + throws ParseException { + if (pos < 0) { + throw new ParseException(String.format("Invalid DATE12 field %d position %d", + field, pos), pos); + } + if (pos+6 > buf.length) { + throw new ParseException(String.format("Insufficient data for DATE12 field %d, pos %d", + field, pos), pos); + } + int[] tens = new int[6]; + int start = 0; + for (int i = pos; i < pos + tens.length; i++) { + tens[start++] = (((buf[i] & 0xf0) >> 4) * 10) + (buf[i] & 0x0f); + } + Calendar cal = Calendar.getInstance(); + //A SimpleDateFormat in the case of dates won't help because of the missing data + //we have to use the current date for reference and change what comes in the buffer + //Set the month in the date + if (tens[0] > 50) { + cal.set(Calendar.YEAR, 1900+tens[0]); + } else { + cal.set(Calendar.YEAR, 2000+tens[0]); + } + cal.set(Calendar.MONTH, tens[1] - 1); + cal.set(Calendar.DATE, tens[2]); + cal.set(Calendar.HOUR_OF_DAY, tens[3]); + cal.set(Calendar.MINUTE, tens[4]); + cal.set(Calendar.SECOND, tens[5]); + cal.set(Calendar.MILLISECOND,0); + if (tz != null) { + cal.setTimeZone(tz); + } + adjustWithFutureTolerance(cal); + return new IsoValue<>(type, cal.getTime(), null); + } + +} diff --git a/src/main/java/com/solab/iso8583/parse/FieldParseInfo.java b/src/main/java/com/solab/iso8583/parse/FieldParseInfo.java index 25fdc2f..bbccf53 100644 --- a/src/main/java/com/solab/iso8583/parse/FieldParseInfo.java +++ b/src/main/java/com/solab/iso8583/parse/FieldParseInfo.java @@ -111,6 +111,8 @@ public abstract class FieldParseInfo { fpi = new BinaryParseInfo(len); } else if (t == IsoType.DATE10) { fpi = new Date10ParseInfo(); + } else if (t == IsoType.DATE12) { + fpi = new Date12ParseInfo(); } else if (t == IsoType.DATE4) { fpi = new Date4ParseInfo(); } else if (t == IsoType.DATE_EXP) { diff --git a/src/test/java/com/solab/iso8583/TestConfigParser.java b/src/test/java/com/solab/iso8583/TestConfigParser.java index 71fc89e..b64941c 100644 --- a/src/test/java/com/solab/iso8583/TestConfigParser.java +++ b/src/test/java/com/solab/iso8583/TestConfigParser.java @@ -215,7 +215,7 @@ public class TestConfigParser { Assert.assertEquals(IsoType.AMOUNT.getLength(), field4.getLength()); // check nested field num 4 from composite field 62 - CompositeField compositeField62 = (CompositeField) isoMessage.getField(62).getValue(); + CompositeField compositeField62 = isoMessage.getField(62).getValue(); IsoValue nestedField4 = compositeField62.getField(0); // first in list Assert.assertEquals(IsoType.ALPHA, nestedField4.getType()); Assert.assertEquals(13, nestedField4.getLength()); diff --git a/src/test/java/com/solab/iso8583/TestFormats.java b/src/test/java/com/solab/iso8583/TestFormats.java index 602065c..5248a14 100644 --- a/src/test/java/com/solab/iso8583/TestFormats.java +++ b/src/test/java/com/solab/iso8583/TestFormats.java @@ -17,22 +17,25 @@ public class TestFormats { @Test public void testDateFormats() { - assert IsoType.DATE10.format(date, null).equals("0125213456"); - assert IsoType.DATE4.format(date, null).equals("0125"); - assert IsoType.DATE_EXP.format(date, null).equals("7301"); - assert IsoType.TIME.format(date, null).equals("213456"); + Assert.assertEquals("0125213456", IsoType.DATE10.format(date, null)); + Assert.assertEquals("0125", IsoType.DATE4.format(date, null)); + Assert.assertEquals("7301", IsoType.DATE_EXP.format(date, null)); + Assert.assertEquals("213456", IsoType.TIME.format(date, null)); + Assert.assertEquals("730125213456", IsoType.DATE12.format(date, null)); //Now with GMT TimeZone gmt = TimeZone.getTimeZone("GMT"); Assert.assertEquals("0126033456", IsoType.DATE10.format(date, gmt)); Assert.assertEquals("0126", IsoType.DATE4.format(date, gmt)); Assert.assertEquals("7301", IsoType.DATE_EXP.format(date, gmt)); Assert.assertEquals("033456", IsoType.TIME.format(date, gmt)); + Assert.assertEquals("730126033456", IsoType.DATE12.format(date, gmt)); //And now with GMT+1 gmt = TimeZone.getTimeZone("GMT+0100"); Assert.assertEquals("0126043456", IsoType.DATE10.format(date, gmt)); Assert.assertEquals("0126", IsoType.DATE4.format(date, gmt)); Assert.assertEquals("7301", IsoType.DATE_EXP.format(date, gmt)); Assert.assertEquals("043456", IsoType.TIME.format(date, gmt)); + Assert.assertEquals("730126043456", IsoType.DATE12.format(date, gmt)); } @Test diff --git a/src/test/java/com/solab/iso8583/parse/TestDates.java b/src/test/java/com/solab/iso8583/parse/TestDates.java index c10bbda..7269285 100644 --- a/src/test/java/com/solab/iso8583/parse/TestDates.java +++ b/src/test/java/com/solab/iso8583/parse/TestDates.java @@ -48,4 +48,17 @@ public class TestDates { Assert.assertEquals(comp.getValue().getTime(), bin.getValue().getTime()); } + @Test + public void testDate12FutureTolerance() throws ParseException, IOException { + Date soon = new Date(System.currentTimeMillis() + 50000); + byte[] buf = IsoType.DATE12.format(soon, null).getBytes(); + IsoValue comp = new Date12ParseInfo().parse(0, buf, 0, null); + assert comp.getValue().after(new Date()); + //Now with the binary + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + comp.write(bout, true, false); + IsoValue bin = new Date12ParseInfo().parseBinary(0, bout.toByteArray(), 0, null); + Assert.assertEquals(comp.getValue().getTime(), bin.getValue().getTime()); + } + }