From 34880b51919c9e823a1ba59ed1d9a15914f88c89 Mon Sep 17 00:00:00 2001 From: Masahiro Kitagawa Date: Fri, 8 Dec 2023 17:26:09 +0900 Subject: [PATCH] WIP --- .../com/lightcrafts/image/libs/LibRaw.java | 15 +- .../image/metadata/CIFFDirectory.java | 4 +- .../image/metadata/CoreDirectory.java | 21 +-- .../image/metadata/EXIFDirectory.java | 3 +- .../image/metadata/IPTCDirectory.java | 14 +- .../image/metadata/ImageMetadata.java | 8 +- .../image/metadata/TIFFDirectory.java | 4 +- .../image/metadata/XMPConstants.java | 7 +- .../metadata/makernotes/PentaxDirectory.java | 6 +- .../providers/CaptureDateTimeProvider.java | 5 +- .../providers/FileDateTimeProvider.java | 5 +- .../image/metadata/values/DateMetaValue.java | 174 +++++++++--------- .../metadata2/CaptureTimeMetadataEntry.java | 14 +- .../ui/metadata2/FileTimeMetadataEntry.java | 14 +- .../java/com/lightcrafts/utils/TextUtil.java | 21 --- macosx/macosx.iml | 16 -- 16 files changed, 152 insertions(+), 179 deletions(-) delete mode 100644 macosx/macosx.iml diff --git a/lightcrafts/src/main/java/com/lightcrafts/image/libs/LibRaw.java b/lightcrafts/src/main/java/com/lightcrafts/image/libs/LibRaw.java index 6d6d4069c..1d603a8eb 100644 --- a/lightcrafts/src/main/java/com/lightcrafts/image/libs/LibRaw.java +++ b/lightcrafts/src/main/java/com/lightcrafts/image/libs/LibRaw.java @@ -10,8 +10,8 @@ import java.awt.*; import java.awt.image.*; import java.io.File; +import java.time.*; import java.util.Arrays; -import java.util.Date; public class LibRaw extends RawDecoder { final String filePath; @@ -215,8 +215,9 @@ public float getAperture() { return aperture; } - public Date getCaptureDateTime() { - return new Date(1000 * timestamp); + public ZonedDateTime getCaptureDateTime() { + // TODO: Is the libraw timestamp in UTC? + return ZonedDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneOffset.UTC); } public float getFocalLength() { @@ -455,7 +456,13 @@ public static void main(String[] args) { LibRaw libRaw = new LibRaw("/Stuff/Pictures/New Raw Support/Canon 450D/IMG_1598.CR2"); - System.out.println("LibRaw (p:" + libRaw.progress_flags + ", w:" + libRaw.process_warnings + ") - make: " + libRaw.make + ", model: " + libRaw.model + ", timestamp: " + new Date(1000 * libRaw.timestamp)); + System.out.println( + "LibRaw (p:" + libRaw.progress_flags + + ", w:" + libRaw.process_warnings + + ") - make: " + libRaw.make + + ", model: " + libRaw.model + + ", timestamp: " + libRaw.timestamp + " (" + libRaw.getCaptureDateTime() + ")" + ); System.out.println("Filter pattern: " + libRaw.filter_pattern); for (int i = 0; i < 4; i++) diff --git a/lightcrafts/src/main/java/com/lightcrafts/image/metadata/CIFFDirectory.java b/lightcrafts/src/main/java/com/lightcrafts/image/metadata/CIFFDirectory.java index 3b98527a3..2b5adf72c 100644 --- a/lightcrafts/src/main/java/com/lightcrafts/image/metadata/CIFFDirectory.java +++ b/lightcrafts/src/main/java/com/lightcrafts/image/metadata/CIFFDirectory.java @@ -2,9 +2,9 @@ package com.lightcrafts.image.metadata; +import java.time.ZonedDateTime; import java.util.HashMap; import java.util.Map; -import java.util.Date; import java.util.ResourceBundle; import com.lightcrafts.image.metadata.values.DateMetaValue; @@ -130,7 +130,7 @@ public String getCameraModel() { * {@inheritDoc} */ @Override - public Date getCaptureDateTime() { + public ZonedDateTime getCaptureDateTime() { final ImageMetaValue value = getValue( CIFF_CAPTURED_TIME ); return value instanceof DateMetaValue ? ((DateMetaValue)value).getDateValue() : null; diff --git a/lightcrafts/src/main/java/com/lightcrafts/image/metadata/CoreDirectory.java b/lightcrafts/src/main/java/com/lightcrafts/image/metadata/CoreDirectory.java index 3b5384b4a..e98e0c3da 100644 --- a/lightcrafts/src/main/java/com/lightcrafts/image/metadata/CoreDirectory.java +++ b/lightcrafts/src/main/java/com/lightcrafts/image/metadata/CoreDirectory.java @@ -19,6 +19,7 @@ import java.awt.*; import java.awt.color.ICC_Profile; import java.io.File; +import java.time.ZonedDateTime; import java.util.*; import static com.lightcrafts.image.metadata.CoreTags.*; @@ -26,6 +27,7 @@ import static com.lightcrafts.image.metadata.ImageOrientation.ORIENTATION_LANDSCAPE; import static com.lightcrafts.image.metadata.ImageOrientation.ORIENTATION_UNKNOWN; import static com.lightcrafts.image.metadata.XMPConstants.*; +import static java.time.format.DateTimeFormatter.*; /** * A CoreDirectory is-an {@link ImageMetadataDirectory} for @@ -162,7 +164,7 @@ public String getCaption() { * {@inheritDoc} */ @Override - public Date getCaptureDateTime() { + public ZonedDateTime getCaptureDateTime() { final ImageMetaValue value = getValue( CORE_CAPTURE_DATE_TIME ); return value != null ? ((DateMetaValue)value).getDateValue() : null; } @@ -189,7 +191,7 @@ public String getCopyright() { * {@inheritDoc} */ @Override - public Date getFileDateTime() { + public ZonedDateTime getFileDateTime() { final ImageMetaValue value = getValue( CORE_FILE_DATE_TIME ); return value != null ? ((DateMetaValue)value).getDateValue() : null; } @@ -489,9 +491,7 @@ protected Collection toXMP( Document xmpDoc, String nsURI, ); XMLUtil.setTextContentOf( createDateElement, - TextUtil.dateFormat( - ISO_8601_DATE_FORMAT, captureDateValue.getDateValue() - ) + captureDateValue.getDateValue().format(ISO_LOCAL_DATE_TIME) ); xapRDFDescElement.appendChild( createDateElement ); } @@ -509,13 +509,13 @@ protected Collection toXMP( Document xmpDoc, String nsURI, ////////// MetadataDate & ModifyDate - final Date now = new Date(); + final var now = ZonedDateTime.now(); final Element metadataDateElement = xmpDoc.createElementNS( XMP_XAP_NS, XMP_XAP_PREFIX + ":MetadataDate" ); XMLUtil.setTextContentOf( metadataDateElement, - TextUtil.dateFormat( ISO_8601_DATE_FORMAT, now ) + now.format(ISO_OFFSET_DATE_TIME) ); xapRDFDescElement.appendChild( metadataDateElement ); final Element modifyDateElement = xmpDoc.createElementNS( @@ -523,7 +523,7 @@ protected Collection toXMP( Document xmpDoc, String nsURI, ); XMLUtil.setTextContentOf( modifyDateElement, - TextUtil.dateFormat( ISO_8601_DATE_FORMAT, now ) + now.format(ISO_OFFSET_DATE_TIME) ); xapRDFDescElement.appendChild( modifyDateElement ); @@ -675,7 +675,7 @@ private void addCaption( ImageMetadata metadata ) { */ private void addCaptureDateTime( ImageInfo imageInfo ) { removeValue( CORE_CAPTURE_DATE_TIME ); - final Date date = imageInfo.getCurrentMetadata().getCaptureDateTime(); + final var date = imageInfo.getCurrentMetadata().getCaptureDateTime(); if ( date != null ) putValue( CORE_CAPTURE_DATE_TIME, new DateMetaValue( date ) ); } @@ -741,8 +741,7 @@ private void addFileInfo( ImageInfo imageInfo ) { putValue( CORE_DIR_NAME , new StringMetaValue( parentDir ) ); putValue( CORE_FILE_NAME, new StringMetaValue( file.getName() ) ); putValue( CORE_FILE_SIZE, new UnsignedLongMetaValue( file.length() ) ); - final Date date = new Date( file.lastModified() ); - putValue( CORE_FILE_DATE_TIME, new DateMetaValue( date ) ); + putValue( CORE_FILE_DATE_TIME, new DateMetaValue(file.lastModified()) ); removeValue( CORE_ORIGINAL_IMAGE_HEIGHT ); removeValue( CORE_ORIGINAL_IMAGE_WIDTH ); diff --git a/lightcrafts/src/main/java/com/lightcrafts/image/metadata/EXIFDirectory.java b/lightcrafts/src/main/java/com/lightcrafts/image/metadata/EXIFDirectory.java index 161b20084..042faef3c 100644 --- a/lightcrafts/src/main/java/com/lightcrafts/image/metadata/EXIFDirectory.java +++ b/lightcrafts/src/main/java/com/lightcrafts/image/metadata/EXIFDirectory.java @@ -9,6 +9,7 @@ import org.w3c.dom.Document; import org.w3c.dom.Element; +import java.time.ZonedDateTime; import java.util.*; import java.util.stream.Stream; @@ -106,7 +107,7 @@ public String getCaption() { * {@inheritDoc} */ @Override - public Date getCaptureDateTime() { + public ZonedDateTime getCaptureDateTime() { ImageMetaValue value = getValue( EXIF_DATE_TIME_ORIGINAL ); if ( value == null ) value = getValue( EXIF_DATE_TIME_DIGITIZED ); diff --git a/lightcrafts/src/main/java/com/lightcrafts/image/metadata/IPTCDirectory.java b/lightcrafts/src/main/java/com/lightcrafts/image/metadata/IPTCDirectory.java index e7c8d4a40..fe5d8d09d 100644 --- a/lightcrafts/src/main/java/com/lightcrafts/image/metadata/IPTCDirectory.java +++ b/lightcrafts/src/main/java/com/lightcrafts/image/metadata/IPTCDirectory.java @@ -1,8 +1,10 @@ /* Copyright (C) 2005-2011 Fabio Riccardi */ +/* Copyright (C) 2023- Masahiro Kitagawa */ package com.lightcrafts.image.metadata; import java.io.UnsupportedEncodingException; +import java.time.ZonedDateTime; import java.util.*; import java.nio.ByteBuffer; @@ -71,7 +73,7 @@ public String getArtist() { * {@inheritDoc} */ @Override - public Date getCaptureDateTime() { + public ZonedDateTime getCaptureDateTime() { final ImageMetaValue value = getValue( IPTC_DATE_CREATED ); return value instanceof DateMetaValue ? ((DateMetaValue)value).getDateValue() : null; @@ -636,6 +638,8 @@ private static void encodeTag( ByteBuffer buf, int tagID ) { * {@link DateMetaValue} plus the time. */ private ImageMetaValue mergeDateTime( ImageMetaValue date, int timeTagID ) { + // TODO: There might be a better way to do this with ZonedDateTime. + final ImageMetaValue timeValue = getValue( timeTagID ); if ( timeValue == null ) return date; @@ -674,12 +678,10 @@ private ImageMetaValue mergeDateTime( ImageMetaValue date, int timeTagID ) { return date; } - final int delta = ((hh+zh) * 60 * 60 + (mm+zm) * 60 + ss) * 1000; + final int deltaSeconds = (hh+zh) * 60 * 60 + (mm+zm) * 60 + ss; - final DateMetaValue newDateValue = (DateMetaValue)date.clone(); - final Date newDate = newDateValue.getDateValue(); - newDate.setTime( newDate.getTime() + delta ); - return newDateValue; + final var newDate = ((DateMetaValue)date).getDateValue().plusSeconds(deltaSeconds); + return new DateMetaValue(newDate); } catch ( NumberFormatException e ) { return date; diff --git a/lightcrafts/src/main/java/com/lightcrafts/image/metadata/ImageMetadata.java b/lightcrafts/src/main/java/com/lightcrafts/image/metadata/ImageMetadata.java index 93c42cb41..5a36ea1e5 100644 --- a/lightcrafts/src/main/java/com/lightcrafts/image/metadata/ImageMetadata.java +++ b/lightcrafts/src/main/java/com/lightcrafts/image/metadata/ImageMetadata.java @@ -16,6 +16,7 @@ import org.w3c.dom.Element; import java.io.*; +import java.time.ZonedDateTime; import java.util.*; import static com.lightcrafts.image.metadata.CoreTags.*; @@ -255,11 +256,11 @@ public String getCaption() { /** * {@inheritDoc} */ - public Date getCaptureDateTime() { + public ZonedDateTime getCaptureDateTime() { final Collection dirs = findProvidersOf( CaptureDateTimeProvider.class ); for ( ImageMetadataDirectory dir : dirs ) { - final Date date = + final var date = ((CaptureDateTimeProvider)dir).getCaptureDateTime(); if ( date != null ) return date; @@ -364,7 +365,8 @@ public File getFile() { /** * {@inheritDoc} */ - public Date getFileDateTime() { + @Override + public ZonedDateTime getFileDateTime() { final CoreDirectory dir = (CoreDirectory)getDirectoryFor( CoreDirectory.class ); return dir != null ? dir.getFileDateTime() : null; diff --git a/lightcrafts/src/main/java/com/lightcrafts/image/metadata/TIFFDirectory.java b/lightcrafts/src/main/java/com/lightcrafts/image/metadata/TIFFDirectory.java index a46b3866d..ac4df85dd 100644 --- a/lightcrafts/src/main/java/com/lightcrafts/image/metadata/TIFFDirectory.java +++ b/lightcrafts/src/main/java/com/lightcrafts/image/metadata/TIFFDirectory.java @@ -1,4 +1,5 @@ /* Copyright (C) 2005-2011 Fabio Riccardi */ +/* Copyright (C) 2023- Masahiro Kitagawa */ package com.lightcrafts.image.metadata; @@ -15,6 +16,7 @@ import java.awt.image.RenderedImage; import java.io.IOException; +import java.time.ZonedDateTime; import java.util.*; import static com.lightcrafts.image.metadata.ImageMetaType.*; @@ -87,7 +89,7 @@ public String getCaption() { /** * {@inheritDoc} */ - public Date getCaptureDateTime() { + public ZonedDateTime getCaptureDateTime() { final ImageMetaValue value = getValue( TIFF_DATE_TIME ); return value instanceof DateMetaValue ? ((DateMetaValue)value).getDateValue() : null; diff --git a/lightcrafts/src/main/java/com/lightcrafts/image/metadata/XMPConstants.java b/lightcrafts/src/main/java/com/lightcrafts/image/metadata/XMPConstants.java index 7557f9593..652971143 100644 --- a/lightcrafts/src/main/java/com/lightcrafts/image/metadata/XMPConstants.java +++ b/lightcrafts/src/main/java/com/lightcrafts/image/metadata/XMPConstants.java @@ -1,11 +1,10 @@ /* Copyright (C) 2005-2011 Fabio Riccardi */ +/* Copyright (C) 2023- Masahiro Kitagawa */ package com.lightcrafts.image.metadata; import com.lightcrafts.app.Application; -import java.text.SimpleDateFormat; - /** * XMPConstants defines constants for XMP. * @@ -13,10 +12,6 @@ */ public interface XMPConstants { - /** This is the date format required by XMP. */ - SimpleDateFormat ISO_8601_DATE_FORMAT = - new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss" ); - /** The Dublin Core namespace URI. */ String XMP_DC_NS = "http://purl.org/dc/elements/1.1/"; diff --git a/lightcrafts/src/main/java/com/lightcrafts/image/metadata/makernotes/PentaxDirectory.java b/lightcrafts/src/main/java/com/lightcrafts/image/metadata/makernotes/PentaxDirectory.java index 44b9788ae..2e137e804 100644 --- a/lightcrafts/src/main/java/com/lightcrafts/image/metadata/makernotes/PentaxDirectory.java +++ b/lightcrafts/src/main/java/com/lightcrafts/image/metadata/makernotes/PentaxDirectory.java @@ -1,4 +1,5 @@ /* Copyright (C) 2005-2011 Fabio Riccardi */ +/* Copyright (C) 2023- Masahiro Kitagawa */ package com.lightcrafts.image.metadata.makernotes; @@ -17,6 +18,7 @@ import java.awt.image.RenderedImage; import java.io.IOException; +import java.time.ZonedDateTime; import java.util.*; import java.util.regex.Pattern; @@ -52,7 +54,7 @@ public float getAperture() { * {@inheritDoc} */ @Override - public Date getCaptureDateTime() { + public ZonedDateTime getCaptureDateTime() { final ImageMetaValue value = getValue( PENTAX_DATE ); return value instanceof DateMetaValue ? ((DateMetaValue)value).getDateValue() : null; @@ -296,7 +298,7 @@ public void putValue( Integer tagID, ImageMetaValue value ) { if (dateValue == null) { return; } - final Date date = ((DateMetaValue)dateValue).getDateValue(); + final var date = ((DateMetaValue)dateValue).getDateValue(); final byte[] buf = ((UndefinedMetaValue)value).getUndefinedValue(); date.setTime( diff --git a/lightcrafts/src/main/java/com/lightcrafts/image/metadata/providers/CaptureDateTimeProvider.java b/lightcrafts/src/main/java/com/lightcrafts/image/metadata/providers/CaptureDateTimeProvider.java index 6b11d8bf4..22e2651a7 100644 --- a/lightcrafts/src/main/java/com/lightcrafts/image/metadata/providers/CaptureDateTimeProvider.java +++ b/lightcrafts/src/main/java/com/lightcrafts/image/metadata/providers/CaptureDateTimeProvider.java @@ -1,8 +1,9 @@ /* Copyright (C) 2005-2011 Fabio Riccardi */ +/* Copyright (C) 2023- Masahiro Kitagawa */ package com.lightcrafts.image.metadata.providers; -import java.util.Date; +import java.time.ZonedDateTime; /** * A CaptureDateTimeProvider provides the capture date/time of an @@ -18,7 +19,7 @@ public interface CaptureDateTimeProvider extends ImageMetadataProvider { * @return Returns the date/time or null if no date/time is * available. */ - Date getCaptureDateTime(); + ZonedDateTime getCaptureDateTime(); } /* vim:set et sw=4 ts=4: */ diff --git a/lightcrafts/src/main/java/com/lightcrafts/image/metadata/providers/FileDateTimeProvider.java b/lightcrafts/src/main/java/com/lightcrafts/image/metadata/providers/FileDateTimeProvider.java index fd9f8d38d..59fba669a 100644 --- a/lightcrafts/src/main/java/com/lightcrafts/image/metadata/providers/FileDateTimeProvider.java +++ b/lightcrafts/src/main/java/com/lightcrafts/image/metadata/providers/FileDateTimeProvider.java @@ -1,8 +1,9 @@ /* Copyright (C) 2005-2011 Fabio Riccardi */ +/* Copyright (C) 2023- Masahiro Kitagawa */ package com.lightcrafts.image.metadata.providers; -import java.util.Date; +import java.time.ZonedDateTime; /** * A FileDateTimeProvider provides the last modified date/time of @@ -18,7 +19,7 @@ public interface FileDateTimeProvider extends ImageMetadataProvider { * @return Returns the date/time or null if no date/time is * available. */ - Date getFileDateTime(); + ZonedDateTime getFileDateTime(); } /* vim:set et sw=4 ts=4: */ diff --git a/lightcrafts/src/main/java/com/lightcrafts/image/metadata/values/DateMetaValue.java b/lightcrafts/src/main/java/com/lightcrafts/image/metadata/values/DateMetaValue.java index dd14a6d65..ac7bb7d35 100644 --- a/lightcrafts/src/main/java/com/lightcrafts/image/metadata/values/DateMetaValue.java +++ b/lightcrafts/src/main/java/com/lightcrafts/image/metadata/values/DateMetaValue.java @@ -1,21 +1,24 @@ /* Copyright (C) 2005-2011 Fabio Riccardi */ +/* Copyright (C) 2023- Masahiro Kitagawa */ package com.lightcrafts.image.metadata.values; +import com.lightcrafts.image.metadata.ImageMetaType; +import com.lightcrafts.utils.xml.XMLUtil; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; -import java.text.ParseException; -import java.text.SimpleDateFormat; +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; import java.util.Arrays; -import java.util.Date; - -import org.w3c.dom.Document; -import org.w3c.dom.Element; - -import com.lightcrafts.image.metadata.ImageMetaType; -import com.lightcrafts.utils.TextUtil; -import com.lightcrafts.utils.xml.XMLUtil; +import java.util.List; +import java.util.stream.Collectors; import static com.lightcrafts.image.metadata.ImageMetaType.META_DATE; import static com.lightcrafts.image.metadata.XMPConstants.*; @@ -43,7 +46,7 @@ public DateMetaValue() { * * @param values The array of values. */ - public DateMetaValue( Date... values ) { + public DateMetaValue(ZonedDateTime... values) { m_value = values; } @@ -52,8 +55,8 @@ public DateMetaValue( Date... values ) { * * @param value The number of milliseconds since epoch. */ - public DateMetaValue( long value ) { - this( new Date( value ) ); + public DateMetaValue(long value) { + this(zonedDateTimeFromEpochMillis(value)); } /** @@ -80,48 +83,48 @@ public DateMetaValue clone() { * {@inheritDoc} */ public int compareTo( Object o ) { - if ( o instanceof DateMetaValue ) { - final DateMetaValue rightVal = (DateMetaValue)o; - final Date leftDate = getDateValue(); - final Date rightDate = rightVal.getDateValue(); + if (o instanceof DateMetaValue rightVal) { + final var leftDate = getDateValue(); + final var rightDate = rightVal.getDateValue(); return leftDate.compareTo( rightDate ); } return super.compareTo( o ); } /** - * Get the first native {@link Date} array value. + * Get the first native {@link ZonedDateTime} array value. * * @return Returns said value. */ - public Date getDateValue() { + public ZonedDateTime getDateValue() { return getDateValueAt(0); } /** - * Gets the {@link Date} value at the given index. + * Gets the {@link ZonedDateTime} value at the given index. * * @param index The index of the value to get. * @return Returns said value. */ - public Date getDateValueAt( int index ) { + public ZonedDateTime getDateValueAt( int index ) { return m_value[ index ]; } /** - * Get the native {@link Date} array value. + * Get the native {@link ZonedDateTime} array value. * * @return Returns said array. */ - public Date[] getDateValues() { + public ZonedDateTime[] getDateValues() { return m_value; } /** * {@inheritDoc} */ + @Override public long getLongValueAt(int index) { - return getDateValueAt(index).getTime(); + return getDateValueAt(index).toInstant().toEpochMilli(); } /** @@ -154,15 +157,15 @@ public boolean isLegalValue( String value ) { } /** - * Sets the {@link Date} value at the given index. + * Sets the {@link ZonedDateTime} value at the given index. * * @param newValue The new value. * @param index The index to set the value of. */ - public synchronized void setDateValueAt( Date newValue, int index ) { + public synchronized void setDateValueAt(ZonedDateTime newValue, int index) { checkIsEditable(); if ( m_value == null ) - m_value = new Date[ index + 1 ]; + m_value = new ZonedDateTime[index + 1]; else if ( index >= m_value.length ) m_value = Arrays.copyOf(m_value, index + 1); m_value[ index ] = newValue; @@ -173,7 +176,7 @@ else if ( index >= m_value.length ) * {@inheritDoc} */ public void setLongValue( long newValue ) { - setDateValueAt( new Date( newValue ), 0 ); + setDateValueAt(zonedDateTimeFromEpochMillis(newValue), 0); } /** @@ -183,27 +186,22 @@ public Element toXMP( Document xmpDoc, String nsURI, String prefix ) { final String tagName = getTagName(); if ( tagName == null ) return null; + final var formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME; final Element tagElement = xmpDoc.createElementNS( nsURI, prefix + ':' + tagName ); - final Date[] values = getDateValues(); - if ( values.length == 1 ) - XMLUtil.setTextContentOf( - tagElement, - TextUtil.dateFormat( ISO_8601_DATE_FORMAT, m_value[0] ) - ); - else { + final ZonedDateTime[] values = getDateValues(); + if (values.length == 1) { + XMLUtil.setTextContentOf(tagElement, m_value[0].format(formatter)); + } else { final Element seqElement = XMLUtil.addElementChildTo( - tagElement, XMP_RDF_NS, XMP_RDF_PREFIX + ":Seq" + tagElement, XMP_RDF_NS, XMP_RDF_PREFIX + ":Seq" ); - for ( Date value : values ) { + Arrays.stream(values).forEachOrdered(value -> { final Element listItem = XMLUtil.addElementChildTo( - seqElement, XMP_RDF_NS, XMP_RDF_PREFIX + ":li" - ); - XMLUtil.setTextContentOf( - listItem, - TextUtil.dateFormat( ISO_8601_DATE_FORMAT, value ) + seqElement, XMP_RDF_NS, XMP_RDF_PREFIX + ":li" ); - } + XMLUtil.setTextContentOf(listItem, value.format(formatter)); + }); } return tagElement; } @@ -213,9 +211,10 @@ public Element toXMP( Document xmpDoc, String nsURI, String prefix ) { */ public void readExternal( ObjectInput in ) throws IOException { final int length = readHeader( in ); - m_value = new Date[ length ]; - for ( int i = 0; i < length; ++i ) - m_value[i] = new Date( in.readLong() ); + m_value = new ZonedDateTime[length]; + for (int i = 0; i < length; i++) { + m_value[i] = zonedDateTimeFromEpochMillis(in.readLong()); + } } /** @@ -225,8 +224,8 @@ public void readExternal( ObjectInput in ) throws IOException { */ public void writeExternal( ObjectOutput out ) throws IOException { writeHeader( out ); - for ( Date value : m_value ) - out.writeLong( value.getTime() ); + for ( ZonedDateTime value : m_value ) + out.writeLong( value.toInstant().toEpochMilli() ); } ////////// protected ////////////////////////////////////////////////////// @@ -239,12 +238,13 @@ public void writeExternal( ObjectOutput out ) throws IOException { * a parsable date. */ protected void appendValueImpl( String newValue ) { - final Date newDate = parseValue( newValue ); + // TODO: This will be simplified if we declare the m_value as a non-null list. + final ZonedDateTime newDateTime = parseValue( newValue ); if ( m_value == null ) - m_value = new Date[] { newDate }; + m_value = new ZonedDateTime[] { newDateTime }; else { m_value = Arrays.copyOf( m_value, m_value.length + 1 ); - m_value[ m_value.length - 1 ] = newDate; + m_value[ m_value.length - 1 ] = newDateTime; } } @@ -256,10 +256,9 @@ protected void appendValueImpl( String newValue ) { protected String[] getValuesImpl() { if ( m_value == null ) return null; - final String[] value = new String[ m_value.length ]; - for ( int i = 0; i < m_value.length; ++i ) - value[i] = TextUtil.dateFormat( m_canonicalDateFormat, m_value[i] ); - return value; + return Arrays.stream(m_value) + .map(v -> v.format(m_canonicalDateFormat)) + .toArray(String[]::new); } /** @@ -271,7 +270,7 @@ protected String[] getValuesImpl() { */ protected void setValuesImpl( String[] newValue ) { if ( m_value == null || m_value.length != newValue.length ) - m_value = new Date[ newValue.length ]; + m_value = new ZonedDateTime[ newValue.length ]; for ( int i = 0; i < newValue.length; ++i ) m_value[i] = parseValue( newValue[i] ); } @@ -285,18 +284,9 @@ protected void setValuesImpl( String[] newValue ) { protected String toStringImpl() { if ( m_value == null ) return null; - final StringBuilder sb = new StringBuilder(); - boolean comma = false; - for ( Date value : m_value ) { - if ( !comma ) - comma = true; - else - sb.append( ',' ); - sb.append( - TextUtil.dateFormat( m_canonicalDateFormat, value ).trim() - ); - } - return sb.toString(); + return Arrays.stream(m_value) + .map(v -> v.format(m_canonicalDateFormat).trim()) + .collect(Collectors.joining(",")); } ////////// private //////////////////////////////////////////////////////// @@ -305,22 +295,22 @@ protected String toStringImpl() { * Parse a date from a {@link String}. * * @param value The {@link String} to parse. - * @return Returns a {link Date}. + * @return Returns a {link ZonedDateTime}. * @throws IllegalArgumentException if the {@link String} does not contain * a parsable date. */ - private static Date parseValue( String value ) { + private static ZonedDateTime parseValue( String value ) { // // Try parsing the value using all the expected date formats until one // parses successfully. // - for ( SimpleDateFormat format : m_dateFormats ) + for (final var formatter : m_dateFormatters) { try { - return format.parse( value ); - } - catch ( ParseException e ) { + return ZonedDateTime.parse(value, formatter); + } catch (DateTimeParseException e) { // ignore } + } throw new IllegalArgumentException(); } @@ -328,25 +318,29 @@ private static Date parseValue( String value ) { * The date format to use by default because this is apparently the * format that cameras use for their date metadata. */ - private static final SimpleDateFormat m_canonicalDateFormat = - new SimpleDateFormat( "yyyy:MM:dd HH:mm:ss" ); + private static final DateTimeFormatter m_canonicalDateFormat = + DateTimeFormatter.ofPattern("yyyy:MM:dd HH:mm:ss"); + + private static ZonedDateTime zonedDateTimeFromEpochMillis(long epochMillis) { + final var instant = Instant.ofEpochSecond(epochMillis); + return ZonedDateTime.ofInstant(instant, ZoneId.systemDefault()); + } /** * Date formats to be able to parse. */ - private static final SimpleDateFormat[] m_dateFormats = { - ISO_8601_DATE_FORMAT, - m_canonicalDateFormat, - new SimpleDateFormat( "EEE MMM dd HH:mm:ss zzz yyyy" ), - new SimpleDateFormat( "yyyyMMdd" ), // IPTC date format - new SimpleDateFormat( "yyyy-MM-dd" ), - new SimpleDateFormat( "yyyy/MM/dd" ), - new SimpleDateFormat( "dd-MMM-yyyy" ), - new SimpleDateFormat( "dd/MMM/yyyy" ), - new SimpleDateFormat( "MMM dd, yyyy" ) - - }; - - private Date[] m_value; + private static final List m_dateFormatters = Arrays.asList( + DateTimeFormatter.ISO_LOCAL_DATE_TIME, + m_canonicalDateFormat, + DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss zzz yyyy"), + DateTimeFormatter.BASIC_ISO_DATE, // IPTC date format + DateTimeFormatter.ISO_LOCAL_DATE, + DateTimeFormatter.ofPattern("yyyy/MM/dd"), + DateTimeFormatter.ofPattern("dd-MMM-yyyy"), + DateTimeFormatter.ofPattern("dd/MMM/yyyy"), + DateTimeFormatter.ofPattern("MMM dd, yyyy") + ); + + private ZonedDateTime[] m_value; } /* vim:set et sw=4 ts=4: */ diff --git a/lightcrafts/src/main/java/com/lightcrafts/ui/metadata2/CaptureTimeMetadataEntry.java b/lightcrafts/src/main/java/com/lightcrafts/ui/metadata2/CaptureTimeMetadataEntry.java index e390a3aac..c8ec4d5a2 100644 --- a/lightcrafts/src/main/java/com/lightcrafts/ui/metadata2/CaptureTimeMetadataEntry.java +++ b/lightcrafts/src/main/java/com/lightcrafts/ui/metadata2/CaptureTimeMetadataEntry.java @@ -1,4 +1,5 @@ /* Copyright (C) 2005-2011 Fabio Riccardi */ +/* Copyright (C) 2023- Masahiro Kitagawa */ package com.lightcrafts.ui.metadata2; @@ -6,14 +7,15 @@ import com.lightcrafts.image.metadata.ImageMetadata; import com.lightcrafts.image.metadata.CoreDirectory; import com.lightcrafts.image.metadata.CoreTags; -import com.lightcrafts.utils.TextUtil; -import java.util.Date; -import java.text.DateFormat; +import java.time.format.DateTimeFormatter; +import java.time.format.FormatStyle; class CaptureTimeMetadataEntry extends SimpleMetadataEntry { - private final static DateFormat Format = DateFormat.getDateTimeInstance(); + private final static DateTimeFormatter formatter = + DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM) + .withLocale(java.util.Locale.getDefault()); CaptureTimeMetadataEntry() { super(CoreDirectory.class, CoreTags.CORE_CAPTURE_DATE_TIME); @@ -26,9 +28,9 @@ public String getLabel(ImageMetadata meta) { public String getValue(ImageMetadata meta) { final CoreDirectory dir = (CoreDirectory) meta.getDirectoryFor(clazz); if (dir != null) { - final Date date = dir.getCaptureDateTime(); + final var date = dir.getCaptureDateTime(); if (date != null) { - return TextUtil.dateFormat( Format, date ); + return date.format(formatter); } } return ""; diff --git a/lightcrafts/src/main/java/com/lightcrafts/ui/metadata2/FileTimeMetadataEntry.java b/lightcrafts/src/main/java/com/lightcrafts/ui/metadata2/FileTimeMetadataEntry.java index 8f6e6d676..a9e47c964 100644 --- a/lightcrafts/src/main/java/com/lightcrafts/ui/metadata2/FileTimeMetadataEntry.java +++ b/lightcrafts/src/main/java/com/lightcrafts/ui/metadata2/FileTimeMetadataEntry.java @@ -1,4 +1,5 @@ /* Copyright (C) 2005-2011 Fabio Riccardi */ +/* Copyright (C) 2023- Masahiro Kitagawa */ package com.lightcrafts.ui.metadata2; @@ -6,14 +7,15 @@ import com.lightcrafts.image.metadata.CoreDirectory; import com.lightcrafts.image.metadata.CoreTags; import com.lightcrafts.image.metadata.ImageMetadata; -import com.lightcrafts.utils.TextUtil; -import java.util.Date; -import java.text.DateFormat; +import java.time.format.DateTimeFormatter; +import java.time.format.FormatStyle; class FileTimeMetadataEntry extends SimpleMetadataEntry { - private final static DateFormat Format = DateFormat.getDateTimeInstance(); + private final static DateTimeFormatter formatter = + DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM) + .withLocale(java.util.Locale.getDefault()); FileTimeMetadataEntry() { super(CoreDirectory.class, CoreTags.CORE_FILE_DATE_TIME); @@ -26,9 +28,9 @@ public String getLabel(ImageMetadata meta) { public String getValue(ImageMetadata meta) { final CoreDirectory dir = (CoreDirectory) meta.getDirectoryFor(clazz); if (dir != null) { - final Date date = dir.getFileDateTime(); + final var date = dir.getFileDateTime(); if (date != null) { - return TextUtil.dateFormat( Format, date ); + return date.format(formatter); } } return ""; diff --git a/lightcrafts/src/main/java/com/lightcrafts/utils/TextUtil.java b/lightcrafts/src/main/java/com/lightcrafts/utils/TextUtil.java index ac09368a5..a67156fcb 100644 --- a/lightcrafts/src/main/java/com/lightcrafts/utils/TextUtil.java +++ b/lightcrafts/src/main/java/com/lightcrafts/utils/TextUtil.java @@ -3,8 +3,6 @@ package com.lightcrafts.utils; import java.text.Normalizer; -import java.util.Date; -import java.text.DateFormat; /** * A TextUtil is a set of utility functions for text. @@ -24,25 +22,6 @@ public final class TextUtil { /** The number of bytes in 1 gigabyte. */ public static final long GB = MB * K; - /** - * Formats a {@link Date} into a date/time string. The reason this method - * is necessary is because date formats are not thread-safe. From the - * {@link DateFormat} Javadoc: - *
- * Date formats are not synchronized. It is recommended to create separate - * format instances for each thread. If multiple threads access a format - * concurrently, it must be synchronized externally. - *
- * @param f The {@link DateFormat} to use. - * @param d The {@link Date} to be formatted. - * @return Returns the formatted string. - */ - public static String dateFormat( DateFormat f, Date d ) { - synchronized ( f ) { - return f.format( d ); - } - } - /** * Convert the byte array to a string of hexadecimal digits. * diff --git a/macosx/macosx.iml b/macosx/macosx.iml deleted file mode 100644 index 7a15af2c7..000000000 --- a/macosx/macosx.iml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file