All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hfg.datetime.DateUtil Maven / Gradle / Ivy

There is a newer version: 20240423
Show newest version
package com.hfg.datetime;


import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.hfg.units.TimeUnit;
import com.hfg.util.StringBuilderPlus;
import com.hfg.util.collection.OrderedMap;


//------------------------------------------------------------------------------
/**
 Date-related utility functions.
 @author J. Alex Taylor, hairyfatguy.com
 */
//------------------------------------------------------------------------------
// com.hfg XML/HTML Coding Library
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com
// [email protected]
//------------------------------------------------------------------------------

public class DateUtil
{
   /**
    ISO 8601 datetime format. Ex: 2001-07-04T12:08:56.235-07:00
    See http://www.w3.org/TR/NOTE-datetime.
    Not thread-safe on its own.
    */
   public static final DateTimeFormatter W3CDTF_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");

      
   private static final ZoneId UTC_ZONE = ZoneId.of("UTC");
   private static final DateTimeFormatter YYYYMMDD_FORMATTER            = DateTimeFormatter.ofPattern("yyyyMMdd");
   private static final DateTimeFormatter YYYY_MM_DD_HH_mm_aa_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm a");
   private static final DateTimeFormatter YYYY_MM_DD_HH_mm_ss_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
   
   private static final Pattern ESCAPE_SEQUENCE_PATTERN = Pattern.compile("[^\\\\]((?:\\\\\\S)+)");

   private static final Map sPhpSubstitutionMap = new OrderedMap<>(35);
   static
   {
      sPhpSubstitutionMap.put("d", "dd");
      sPhpSubstitutionMap.put("D", "EEE");
      sPhpSubstitutionMap.put("j", "d");
      sPhpSubstitutionMap.put("l", "E");
      sPhpSubstitutionMap.put("N", "u");
      sPhpSubstitutionMap.put("S", "");
      sPhpSubstitutionMap.put("w", "");
      sPhpSubstitutionMap.put("z", "D");
      sPhpSubstitutionMap.put("W", "w");
      sPhpSubstitutionMap.put("M", "MMM");
      sPhpSubstitutionMap.put("F", "M");
      sPhpSubstitutionMap.put("m", "MM");
      sPhpSubstitutionMap.put("n", "MM");
      sPhpSubstitutionMap.put("t", "");
      sPhpSubstitutionMap.put("L", "");
      sPhpSubstitutionMap.put("Y", "YYYY");
      sPhpSubstitutionMap.put("o", "YYYY");
      sPhpSubstitutionMap.put("y", "YY");
      sPhpSubstitutionMap.put("a", "a");
      sPhpSubstitutionMap.put("A", "a");
      sPhpSubstitutionMap.put("g", "h");
      sPhpSubstitutionMap.put("H", "HH");
      sPhpSubstitutionMap.put("G", "H");
      sPhpSubstitutionMap.put("h", "hh");
      sPhpSubstitutionMap.put("i", "mm");
      sPhpSubstitutionMap.put("s", "ss");
      sPhpSubstitutionMap.put("u", "SSS");
      sPhpSubstitutionMap.put("Z", "");
      sPhpSubstitutionMap.put("O", "ZZ");
      sPhpSubstitutionMap.put("P", "XXX");
      sPhpSubstitutionMap.put("T", "zz");
      sPhpSubstitutionMap.put("c", "");
      sPhpSubstitutionMap.put("C", "");
      sPhpSubstitutionMap.put("U", "");
   }

   //##########################################################################
   // PUBLIC METHODS
   //##########################################################################

   //--------------------------------------------------------------------------
   public static synchronized Date threadsafeParse(String inDateString, SimpleDateFormat inFormat)
      throws ParseException
   {
      return inFormat.parse(inDateString);
   }

   //--------------------------------------------------------------------------
   public static String generateElapsedTimeString(long inStartTime)
   {
      return generateElapsedTimeString(inStartTime, System.currentTimeMillis());
   }

   //--------------------------------------------------------------------------
   public static int getCurrentYear()
   {
      return new GregorianCalendar().get(Calendar.YEAR);
   }

   //--------------------------------------------------------------------------
   public static int getYear(Date inDate)
   {
      Calendar calendar = Calendar.getInstance();
      calendar.setTime(inDate);
      return calendar.get(Calendar.YEAR);
   }

   //--------------------------------------------------------------------------
   public static Date roundToNextMinute(Date inStartTime)
   {
      Calendar calendar = new GregorianCalendar();
      calendar.setTime(inStartTime);
      calendar.set(Calendar.MILLISECOND, 0);
      calendar.set(Calendar.SECOND, 0);
      calendar.add(Calendar.MINUTE, 1);

      return calendar.getTime();
   }

   //--------------------------------------------------------------------------
   public static Date roundToNextHour(Date inStartTime)
   {
      Calendar calendar = new GregorianCalendar();
      calendar.setTime(inStartTime);
      calendar.set(Calendar.MILLISECOND, 0);
      calendar.set(Calendar.SECOND, 0);
      calendar.set(Calendar.MINUTE, 0);
      calendar.add(Calendar.HOUR_OF_DAY, 1);

      return calendar.getTime();
   }

   //--------------------------------------------------------------------------
   public static String generateElapsedTimeString(long inStartTime, long inEndTime)
   {
      long elapsedMillis = inEndTime - inStartTime;

      long elapsedSec  = 0;
      long elapsedMin  = 0;
      long elapsedHour = 0;

      String output;

      if (elapsedMillis >= TimeUnit.second.getMilliseconds())
      {
         elapsedSec = elapsedMillis / TimeUnit.second.getMilliseconds();

         if (elapsedSec >= 60)
         {
            elapsedMin = elapsedSec / 60;
            elapsedSec = elapsedSec % 60;

            if (elapsedMin >= 60)
            {
               elapsedHour = elapsedMin / 60;
               elapsedMin  = elapsedMin % 60;
            }
         }

         output = String.format("%02d:%02d:%02d", elapsedHour, elapsedMin, elapsedSec);
      }
      else
      {
         output = String.format("00:00:00.%03d", elapsedMillis);
      }

      return output;
   }

   //--------------------------------------------------------------------------
   public static LocalDateTime convertToLocalDateTime(Date inDate) 
   {
      return inDate.toInstant()
            .atZone(ZoneId.systemDefault())
            .toLocalDateTime();
   }

   //--------------------------------------------------------------------------

   public static LocalDate convertToLocalDate(Date inDate)
   {
      return inDate.toInstant()
                   .atZone(ZoneId.systemDefault())
                   .toLocalDate();
   }

   //--------------------------------------------------------------------------
   public static LocalDateTime convertToLocalDateTime(Instant inDate)
   {
      return inDate.atZone(ZoneId.systemDefault())
            .toLocalDateTime();
   }

   //--------------------------------------------------------------------------
   public static ZonedDateTime convertToZonedDateTime(Date inDate) 
   {
      return inDate.toInstant()
            .atZone(ZoneId.systemDefault());
   }

   //--------------------------------------------------------------------------
   public static ZonedDateTime convertToZonedDateTime(Instant inDate)
   {
      return inDate.atZone(ZoneId.systemDefault());
   }

   //--------------------------------------------------------------------------
   public static Date convertToDate(LocalDateTime inValue) 
   {
      return Date.from(inValue.atZone(ZoneId.systemDefault()).toInstant());
   }
   public static Date convertToDate(LocalDate inValue)
   {
      return Date.from(inValue.atStartOfDay(ZoneId.systemDefault()).toInstant());
   }


   //--------------------------------------------------------------------------
   public static ZonedDateTime convertToUTCZonedDateTime(Instant inValue)
   {
      return ZonedDateTime.ofInstant(inValue, UTC_ZONE);
   }
   
   //--------------------------------------------------------------------------
   /**
    Note that Date should really no longer be used! Use java.time.LocalDateTime instead.
    @param  inDate the Date object to be adjusted
    @param  inOffset the ZoneOffset to use for adjustment
    @return a Date object that has been forcibly adjusted by the specified timezone offset.
    */
   public static Date applyZoneOffset(Date inDate, ZoneOffset inOffset)
   {
      ZoneOffset zoneOffset = ZoneId.systemDefault().getRules().getOffset(inDate.toInstant());

      return new Date(inDate.getTime() + ((zoneOffset.getTotalSeconds() - inOffset.getTotalSeconds()) * -1000));
   }

   
   //--------------------------------------------------------------------------
   public static String getISO_8601_Date()
   {
      return getISO_8601_Date(LocalDateTime.now());
   }

   //--------------------------------------------------------------------------
   public static String getISO_8601_Date(Date inDate)
   {
      return getISO_8601_Date(convertToLocalDateTime(inDate));
   }

   //--------------------------------------------------------------------------
   public static String getISO_8601_Date(Instant inDate)
   {
      return getISO_8601_Date(convertToLocalDateTime(inDate));
   }

   //--------------------------------------------------------------------------
   public static String getISO_8601_Date(LocalDateTime inValue)
   {
      return DateTimeFormatter.ISO_LOCAL_DATE.format(inValue);
   }

   //--------------------------------------------------------------------------
   public static String getISO_8601_Date(ZonedDateTime inValue)
   {
      return DateTimeFormatter.ISO_LOCAL_DATE.format(inValue);
   }

   //--------------------------------------------------------------------------
   public static String getISO_8601_DateTime()
   {
      return getISO_8601_DateTime(LocalDateTime.now());
   }

   //--------------------------------------------------------------------------
   public static String getISO_8601_DateTime(Instant inDate)
   {
      return getISO_8601_DateTime(convertToLocalDateTime(inDate));
   }

   //--------------------------------------------------------------------------
   public static String getISO_8601_DateTime(Date inValue)
   {
      return getISO_8601_DateTime(convertToLocalDateTime(inValue));
   }

   //--------------------------------------------------------------------------
   public static String getISO_8601_DateTime(LocalDateTime inValue)
   {
      return DateTimeFormatter.ISO_DATE_TIME.format(inValue);
   }


   //--------------------------------------------------------------------------
   public static String getYYYYMMDD()
   {
      return getYYYYMMDD(LocalDateTime.now());
   }

   //--------------------------------------------------------------------------
   public static String getYYYYMMDD(Date inValue)
   {
      return getYYYYMMDD(convertToLocalDateTime(inValue));
   }

   //--------------------------------------------------------------------------
   public static String getYYYYMMDD(LocalDateTime inValue)
   {
      return YYYYMMDD_FORMATTER.format(inValue);
   }

   //--------------------------------------------------------------------------
   public static String getYYYYMMDD(ZonedDateTime inValue)
   {
      return YYYYMMDD_FORMATTER.format(inValue);
   }

   //--------------------------------------------------------------------------
   public static String getYYYY_MM(Date inValue, char inSeparator)
   {
      return getYYYY_MM(convertToLocalDateTime(inValue), inSeparator);
   }

   //--------------------------------------------------------------------------
   public static String getYYYY_MM(LocalDateTime inValue, char inSeparator)
   {
      return DateTimeFormatter.ofPattern("yyyy" + inSeparator + "MM").format(inValue);
   }

   //--------------------------------------------------------------------------
   public static String getYYYY_MM_DD(Date inValue, char inSeparator)
   {
      return DateTimeFormatter.ofPattern("yyyy" + inSeparator + "MM" + inSeparator + "dd").format(convertToLocalDateTime(inValue));
   }

   //--------------------------------------------------------------------------
   public static String getYYYY_MM_DD()
   {
      return new SimpleDateFormat("yyyy-MM-dd").format(new Date());
   }

   
   //--------------------------------------------------------------------------
   public static String getYYYY_MM_DD_HH_mm_aa()
   {
      return getYYYY_MM_DD_HH_mm_aa(LocalDateTime.now());
   }

   //--------------------------------------------------------------------------
   public static String getYYYY_MM_DD_HH_mm_aa(Date inDate)
   {
      return getYYYY_MM_DD_HH_mm_aa(convertToLocalDateTime(inDate));
   }

   //--------------------------------------------------------------------------
   public static String getYYYY_MM_DD_HH_mm_aa(LocalDateTime inValue)
   {
      return YYYY_MM_DD_HH_mm_aa_FORMATTER.format(inValue);
   }

   
   //--------------------------------------------------------------------------
   public static String getYYYY_MM_DD_HH_mm_ss()
   {
      return getYYYY_MM_DD_HH_mm_ss(LocalDateTime.now());
   }

   //--------------------------------------------------------------------------
   public static String getYYYY_MM_DD_HH_mm_ss(Date inDate)
   {
      return getYYYY_MM_DD_HH_mm_ss(convertToLocalDateTime(inDate));
   }

   //--------------------------------------------------------------------------
   public static String getYYYY_MM_DD_HH_mm_ss(LocalDateTime inValue)
   {
      return YYYY_MM_DD_HH_mm_ss_FORMATTER.format(inValue);
   }

   //--------------------------------------------------------------------------
   public static Date addDaysToDate(Date inStartDate, Integer inDays)
   {
      Date date = null;
      if (inStartDate != null && inDays != null)
      {
         Calendar c = Calendar.getInstance();
         c.setTime(inStartDate);
         c.add(Calendar.DATE, inDays);
         date = c.getTime();
      }
      return date;
   }

   //--------------------------------------------------------------------------

   /**
    * @param inStartDate java.util.Date
    * @param inDays number of days to subtract from inStartDate
    * @return the date 'inDays' prior to 'inStartDate' in java.util.Date
    */
   public static Date subtractDaysFromDate(Date inStartDate, Integer inDays)
   {
      Date date = null;
      if (inStartDate != null && inDays != null)
      {
         // convert Date to LocalDate to subtract
         LocalDate localDate = convertToLocalDate(inStartDate).minusDays(inDays);
         // convert LocalDate back to Date
         date = convertToDate(localDate);
      }
      return date;
   }

   //--------------------------------------------------------------------------
   /**
    Makes a best attempt to convert php date format syntax into Java data format syntax.
    Not all specifiers are supported.
    @param inPhpFormatString the date format in php synatx
    @return a SimpleDateFormat object
    */
   public static DateTimeFormatter getFormatterUsingPhpSyntax(String inPhpFormatString)
   {
      /*
      Format      Description                                                               Example returned values
       ------      -----------------------------------------------------------------------   -----------------------
         d         Day of the month, 2 digits with leading zeros                             01 to 31
         D         A short textual representation of the day of the week                     Mon to Sun
         j         Day of the month without leading zeros                                    1 to 31
         l         A full textual representation of the day of the week                      Sunday to Saturday
         N         ISO-8601 numeric representation of the day of the week                    1 (for Monday) through 7 (for Sunday)
         S         English ordinal suffix for the day of the month, 2 characters             st, nd, rd or th. Works well with j
         w         Numeric representation of the day of the week                             0 (for Sunday) to 6 (for Saturday)
         z         The day of the year (starting from 0)                                     0 to 364 (365 in leap years)
         W         ISO-8601 week number of year, weeks starting on Monday                    01 to 53
         F         A full textual representation of a month, such as January or March        January to December
         m         Numeric representation of a month, with leading zeros                     01 to 12
         M         A short textual representation of a month                                 Jan to Dec
         n         Numeric representation of a month, without leading zeros                  1 to 12
         t         Number of days in the given month                                         28 to 31
         L         Whether it's a leap year                                                  1 if it is a leap year, 0 otherwise.
         o         ISO-8601 year number (identical to (Y), but if the ISO week number (W)    Examples: 1998 or 2004
                   belongs to the previous or next year, that year is used instead)
         Y         A full numeric representation of a year, 4 digits                         Examples: 1999 or 2003
         y         A two digit representation of a year                                      Examples: 99 or 03
         a         Lowercase Ante meridiem and Post meridiem                                 am or pm
         A         Uppercase Ante meridiem and Post meridiem                                 AM or PM
         g         12-hour format of an hour without leading zeros                           1 to 12
         G         24-hour format of an hour without leading zeros                           0 to 23
         h         12-hour format of an hour with leading zeros                              01 to 12
         H         24-hour format of an hour with leading zeros                              00 to 23
         i         Minutes, with leading zeros                                               00 to 59
         s         Seconds, with leading zeros                                               00 to 59
         u         Decimal fraction of a second                                              Examples:
                   (minimum 1 digit, arbitrary number of digits allowed)                     001 (i.e. 0.001s) or
                                                                                             100 (i.e. 0.100s) or
                                                                                             999 (i.e. 0.999s) or
                                                                                             999876543210 (i.e. 0.999876543210s)
         O         Difference to Greenwich time (GMT) in hours and minutes                   Example: +1030
         P         Difference to Greenwich time (GMT) with colon between hours and minutes   Example: -08:00
         T         Timezone abbreviation of the machine running the code                     Examples: EST, MDT, PDT ...
         Z         Timezone offset in seconds (negative if west of UTC, positive if east)    -43200 to 50400
         c         ISO 8601 date represented as the local time with an offset to UTC appended.
                   Notes:                                                                    Examples:
                   1) If unspecified, the month / day defaults to the current month / day,   1991 or
                      the time defaults to midnight, while the timezone defaults to the      1992-10 or
                      browser's timezone. If a time is specified, it must include both hours 1993-09-20 or
                      and minutes. The "T" delimiter, seconds, milliseconds and timezone     1994-08-19T16:20+01:00 or
                      are optional.                                                          1995-07-18T17:21:28-02:00 or
                   2) The decimal fraction of a second, if specified, must contain at        1996-06-17T18:22:29.98765+03:00 or
                      least 1 digit (there is no limit to the maximum number                 1997-05-16T19:23:30,12345-0400 or
                      of digits allowed), and may be delimited by either a '.' or a ','      1998-04-15T20:24:31.2468Z or
                   Refer to the examples on the right for the various levels of              1999-03-14T20:24:32Z or
                   date-time granularity which are supported, or see                         2000-02-13T21:25:33
                   http://www.w3.org/TR/NOTE-datetime for more info.                         2001-01-12 22:26:34
         C         An ISO date string as implemented by the native Date object's             1962-06-17T09:21:34.125Z
                   [Date.toISOString](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString)
                   method. This outputs the numeric part with *UTC* hour and minute
                   values, and indicates this by appending the `'Z'` timezone
                   identifier.
         U         Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT)                1193432466 or -2138434463
         MS        Microsoft AJAX serialized dates                                           \/Date(1238606590509)\/ (i.e. UTC milliseconds since epoch) or
                                                                                             \/Date(1238606590509+0800)\/
         time      A javascript millisecond timestamp                                        1350024476440
         timestamp A UNIX timestamp (same as U)                                              1350024866
       */
      StringBuilderPlus buffer = new StringBuilderPlus(inPhpFormatString);

      // Note that the order of replacement is important
      for (String target : sPhpSubstitutionMap.keySet())
      {
         String replacement = sPhpSubstitutionMap.get(target);
         int index;
         int fromIndex = 0;
         while ((index = buffer.indexOf(target, fromIndex)) >= 0)
         {
            if (0 == index
                || getEscapeCount(buffer, index)%2 != 1) // Escaped characters indicated static text in the php format
            {
               buffer.replace(index, index + target.length(), replacement);
               fromIndex = index + replacement.length();
            }
            else
            {
               fromIndex++;
            }
         }
      }

      // Replace escaped characters (php syntax) with a single-quoted version (Java syntax)
      Matcher m = ESCAPE_SEQUENCE_PATTERN.matcher(buffer);
      int fromIndex = 0;
      while (m.find(fromIndex))
      {
         String strippedValue = m.group(1).replaceAll("\\\\", "");
         buffer.replace(m.start() + 1, m.end(), "'" + strippedValue + "'");
      }

      return DateTimeFormatter.ofPattern(buffer.toString());
   }

   //--------------------------------------------------------------------------
   // Returns the number of '\' characters that preceded the character at the specified index
   private static int getEscapeCount(CharSequence inString, int inIndex)
   {
      int count = 0;
      for (int i = inIndex - 1; i >= 0; i--)
      {
         if (inString.charAt(i) == '\\')
         {
            count++;
         }
         else
         {
            break;
         }
      }

      return count;
   }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy