/*
* Copyright (C) 2009 eXo Platform SAS.
*
* This 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 software 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 software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.exoplatform.services.jcr.impl.util;
import org.exoplatform.commons.utils.ISO8601;
import org.exoplatform.commons.utils.Tools;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import java.text.ParseException;
import java.util.Calendar;
import javax.jcr.ValueFormatException;
/**
* Created by The eXo Platform SAS Author : Peter Nedonosko peter.nedonosko@exoplatform.com.ua
* 23.08.2006
*
* ISO 8601
*
* Year: YYYY (eg 1997) Year and month: YYYY-MM (eg 1997-07) Complete date: YYYY-MM-DD (eg
* 1997-07-16) Complete date plus hours and minutes: YYYY-MM-DDThh:mmTZD (eg 1997-07-16T19:20+01:00)
* Complete date plus hours, minutes and seconds: YYYY-MM-DDThh:mm:ssTZD (eg
* 1997-07-16T19:20:30+01:00) Complete date plus hours, minutes, seconds and a decimal fraction of a
* second YYYY-MM-DDThh:mm:ss.sTZD (eg 1997-07-16T19:20:30.45+01:00)
*
* where:
*
* YYYY = four-digit year MM = two-digit month (01=January, etc.) DD = two-digit day of month (01
* through 31) hh = two digits of hour (00 through 23) (am/pm NOT allowed) mm = two digits of minute
* (00 through 59) ss = two digits of second (00 through 59) s = one or more digits representing a
* decimal fraction of a second TZD = time zone designator (Z or +hh:mm or -hh:mm) a RFC 822 time
* zone is also accepted: For formatting, the RFC 822 4-digit time zone format is used:
* RFC822TimeZone: Sign TwoDigitHours Minutes TwoDigitHours: Digit Digit like -8000
*
* It's a pb found. If we will set property with date contains timezone different to the current.
* And will get property as string after that. We will have a date with the current timezone,
* actualy the date will be same but in different tz.
*
* "2023-07-05T19:28:00.000-0300" {@literal -->} "2023-07-06T01:28:00.000+0300" - it's same date, but... print
* is different.
*
* The pb can be solved in SimpleDateFormat be setting the formatter timezone before the format
* procedure.
*
* TimeZone tz = TimeZone.getTimeZone("GMT-03:00"); Calendar cdate = Calendar.getInstance();
* SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); sdf.setTimeZone(tz);
* Date d = sdf.parse(javaDate); log.info("parse " + sdf.format(d)); // print date in GMT-03:00
* timezone
*
* @author <a href="mailto:peter.nedonosko@exoplatform.com.ua">Peter Nedonosko</a>
* @version $Id: JCRDateFormat.java 11907 2008-03-13 15:36:21Z ksm $
*/
public class JCRDateFormat
{
public static Log log = ExoLogger.getLogger("exo.jcr.component.core.JCRDateFormat");
/**
* ISO 8601, RFC822 formats for JCR datas deserialization in order of priority of parse
*/
protected static final String[] JCR_FORMATS =
{ISO8601.COMPLETE_DATETIMEMSZ_FORMAT, ISO8601.COMPLETE_DATETIMEMSZRFC822_FORMAT,};
protected static final String CALENDAR_FIELDS_DELIMITER = ";"; // hope
// it
// 's
// unique
// for
// any
// time
// zone
// ID
// etc
// .
protected static final String CALENDAR_FIELDS_SEPARATOR = "--";
/**
* Format date using complete date plus hours, minutes, seconds and a decimal fraction of a second
* format. I.e. format to JCR date value string representation.
*
* @param date
* @return
*/
public static String format(Calendar date)
{
return ISO8601.format(date);
}
/**
* Parse string using possible formats list.
*
* @param dateString
* - date string
* @return - calendar
* @throws ValueFormatException
*/
public static Calendar parse(String dateString) throws ValueFormatException
{
try
{
return ISO8601.parseEx(dateString);
}
catch (ParseException e)
{
throw new ValueFormatException("Can not parse date from [" + dateString + "]", e);
}
catch (NumberFormatException e)
{
throw new ValueFormatException("Can not parse date from [" + dateString + "]", e);
}
}
/**
* Deserialize string (of JCR Value) to the date.
*
* @param serString
* @return
* @throws ValueFormatException
*/
public Calendar deserialize(String serString) throws ValueFormatException
{
final String[] parts = serString.split(CALENDAR_FIELDS_SEPARATOR);
if (parts.length == 2)
{
// try parse serialized string with two formats
// 1. Complete ISO 8610 compliant
// 2. Complete ISO 8610 + RFC822 (time zone) compliant (JCR 1.6 and prior)
Calendar isoCalendar = null;
try
{
isoCalendar = ISO8601.parse(parts[0], JCR_FORMATS);
String[] calendarFields = parts[1].split(CALENDAR_FIELDS_DELIMITER);
if (calendarFields.length == 4)
{
try
{
isoCalendar.setLenient(Boolean.parseBoolean(calendarFields[0]));
isoCalendar.setFirstDayOfWeek(Integer.parseInt(calendarFields[1]));
isoCalendar.setMinimalDaysInFirstWeek(Integer.parseInt(calendarFields[2]));
isoCalendar.setTimeZone(Tools.getTimeZone(calendarFields[3]));
}
catch (Exception e)
{
log.warn("Can't parse serialized fields for the calendar [" + parts[1] + "] but calendar has ["
+ isoCalendar.getTime() + "]", e);
}
}
else
{
log.warn("Fields of the calendar is not serialized properly [" + parts[1] + "] but calendar has ["
+ isoCalendar.getTime() + "]");
}
}
catch (ParseException e)
{
throw new ValueFormatException(e);
}
return isoCalendar;
}
throw new ValueFormatException("Can't deserialize calendar string [" + serString + "]");
}
/**
* Serialize date (of JCR Value) to the string.
*
* @param date
* @return
*/
public byte[] serialize(Calendar date)
{
final String calendarString =
CALENDAR_FIELDS_SEPARATOR + date.isLenient() + CALENDAR_FIELDS_DELIMITER + date.getFirstDayOfWeek()
+ CALENDAR_FIELDS_DELIMITER + date.getMinimalDaysInFirstWeek() + CALENDAR_FIELDS_DELIMITER
+ date.getTimeZone().getID();
return (format(date) + calendarString).getBytes();
}
}