// ============================================================================ // // Copyright (C) 2006-2016 Talend Inc. - www.talend.com // // This source code is available under agreement available at // %InstallDIR%\features\org.talend.rcp.branding.%PRODUCTNAME%\%PRODUCTNAME%license.txt // // You should have received a copy of the agreement // along with this program; if not, write to Talend SA // 9 rue Pages 92150 Suresnes, France // // ============================================================================ package org.talend.dataquality.datamasking.functions; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; /** * @author dprot * * A FieldDate is a field containing a date with format YYYYMMDD */ public class FieldDate extends AbstractField { private static final long serialVersionUID = -4061095254204934437L; public static final List<Integer> cumulativeMonthSize = Collections .unmodifiableList(Arrays.asList(0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334)); public static final List<Integer> cumulativeMonthSizeLeapYear = Collections .unmodifiableList(Arrays.asList(0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335)); public static final List<Integer> monthSize = Collections .unmodifiableList(Arrays.asList(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)); public static final List<Integer> monthSizeLeapYear = Collections .unmodifiableList(Arrays.asList(31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)); private int firstYear; private int lastYear; private List<Integer> numberDaysPerYear = new ArrayList<Integer>(); /** * * Create a field corresponding to a date with format YYYYMMDD, from year 1900 included to 2100 excluded */ public FieldDate() { this(1900, 2100); } /** * Create a field corresponding to a date with format YYYYMMDD, firstYear included, lastYear excluded * * @param firstYear * @param lastYear */ public FieldDate(int firstYear, int lastYear) { super(); length = 8; this.firstYear = firstYear; this.lastYear = lastYear; computeDaysPerYear(); } /** * * Initialize the attribute numberDaysPerYear */ private void computeDaysPerYear() { Integer count = 0; for (int year = firstYear; year <= lastYear; year++) { numberDaysPerYear.add(count); if (isLeapYear(year)) count += 366; else count += 365; } } /** * * @param year * @return true if a given year is a leap year */ private boolean isLeapYear(int year) { if (year % 4 == 0 && year % 100 != 0) return true; if (year % 100 == 0 && year % 400 == 0) return true; return false; } @Override public long getWidth() { return numberDaysPerYear.get(numberDaysPerYear.size() - 1); } @Override public Long encode(String str) { Long dayNumber = 0L; try { int year = Integer.valueOf(str.substring(0, 4)); int month = Integer.valueOf(str.substring(4, 6)); int day = Integer.valueOf(str.substring(6, 8)); // Check if the date exists if (year < firstYear || year >= lastYear) return -1L; if (month < 1 || month > 12) return -1L; if (isLeapYear(year)) { if (day < 1 || day > monthSizeLeapYear.get(month - 1)) return -1L; } else { if (day < 1 || day > monthSize.get(month - 1)) return -1L; } dayNumber = (long) numberDaysPerYear.get(year - firstYear); dayNumber += cumulativeMonthSize.get(month - 1); dayNumber += (day - 1); if (isLeapYear(year)) dayNumber++; } catch (NumberFormatException e) { return -1L; } return dayNumber; } @Override public String decode(long number) { if (number >= getWidth() || number < 0) return ""; int year = findNearest(number, numberDaysPerYear); long remainingDays = number - numberDaysPerYear.get(year); int month = -1, days = -1; if (isLeapYear(year + firstYear)) { month = findNearest(remainingDays, cumulativeMonthSizeLeapYear); days = (int) (remainingDays - cumulativeMonthSizeLeapYear.get(month)); } else { month = findNearest(remainingDays, cumulativeMonthSize); days = (int) (remainingDays - cumulativeMonthSize.get(month)); } String res = ""; res += String.valueOf(year + firstYear); String monthString = String.valueOf(month + 1); // Write month on two digits if (monthString.length() < 2) res += "0"; res += monthString; String dayString = String.valueOf(days + 1); // Write days on two digits if (dayString.length() < 2) res += "0"; res += dayString; return res; } /** * * Find the nearest entry in numberList smaller than number (with a dichotomic search) * * @param number * @param numberList * @return */ private int findNearest(long number, List<Integer> numberList) { int minIndex = 0; int maxIndex = numberList.size() - 1; int mean = (maxIndex + minIndex) / 2; do { if (numberList.get(mean) < number) { minIndex = mean; } else { maxIndex = mean; } mean = (maxIndex + minIndex) / 2; } while (minIndex + 1 < maxIndex); return minIndex; } }