001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.marshall; 018 019import java.time.*; 020import java.time.format.*; 021import java.util.*; 022 023import org.apache.juneau.marshall.swaps.*; 024 025/** 026 * Supported wire formats for {@link Date} values. 027 * 028 * <p> 029 * Used by {@link MarshallingContext.Builder#dateFormat(DateFormat)}, 030 * {@link Marshalled#dateFormat()}, 031 * {@link MarshalledProp#dateFormat()}, and 032 * {@link MarshalledConfig#dateFormat()} to control how {@link Date} 033 * values are written to and read from the wire. 034 * 035 * <p> 036 * The default is {@link #ISO_LOCAL_DATE_TIME} which preserves the historical wire output produced 037 * before this enum existed (note: {@link Date} carries no zone so the default emits a local date-time, 038 * deliberately differing from {@link CalendarFormat#ISO_OFFSET_DATE_TIME}). 039 * 040 * <p> 041 * Numeric formats ({@link #MILLIS}) emit native numeric wire types on binary serializers 042 * (BSON / CBOR / MsgPack) and bare numbers on text formats. Text formats emit a quoted ISO 8601 string for 043 * every other constant. 044 */ 045@SuppressWarnings({ 046 "java:S2143" // Public API formats/parses Date by design (it's the Date marshalling-format enum); internals already use java.time. 047}) 048public enum DateFormat { 049 050 /** Sentinel meaning "no value configured" — falls through to the next-higher precedence level. */ 051 NOT_SET, 052 053 /** {@link DateTimeFormatter#BASIC_ISO_DATE}: <js>"20111203"</js>. */ 054 BASIC_ISO_DATE, 055 056 /** {@link DateTimeFormatter#ISO_DATE}: <js>"2011-12-03+01:00"</js> or <js>"2011-12-03"</js>. */ 057 ISO_DATE, 058 059 /** {@link DateTimeFormatter#ISO_DATE_TIME}: <js>"2011-12-03T10:15:30+01:00[Europe/Paris]"</js>. */ 060 ISO_DATE_TIME, 061 062 /** {@link DateTimeFormatter#ISO_INSTANT}: <js>"2011-12-03T10:15:30Z"</js>. */ 063 ISO_INSTANT, 064 065 /** {@link DateTimeFormatter#ISO_LOCAL_DATE}: <js>"2011-12-03"</js>. */ 066 ISO_LOCAL_DATE, 067 068 /** {@link DateTimeFormatter#ISO_LOCAL_DATE_TIME} (the default): <js>"2011-12-03T10:15:30"</js>. */ 069 ISO_LOCAL_DATE_TIME, 070 071 /** {@link DateTimeFormatter#ISO_LOCAL_TIME}: <js>"10:15:30"</js>. */ 072 ISO_LOCAL_TIME, 073 074 /** {@link DateTimeFormatter#ISO_OFFSET_DATE}: <js>"2011-12-03+01:00"</js>. */ 075 ISO_OFFSET_DATE, 076 077 /** {@link DateTimeFormatter#ISO_OFFSET_DATE_TIME}: <js>"2011-12-03T10:15:30+01:00"</js>. */ 078 ISO_OFFSET_DATE_TIME, 079 080 /** {@link DateTimeFormatter#ISO_OFFSET_TIME}: <js>"10:15:30+01:00"</js>. */ 081 ISO_OFFSET_TIME, 082 083 /** {@link DateTimeFormatter#ISO_ORDINAL_DATE}: <js>"2012-337"</js>. */ 084 ISO_ORDINAL_DATE, 085 086 /** {@link DateTimeFormatter#ISO_TIME}: <js>"10:15:30+01:00"</js> or <js>"10:15:30"</js>. */ 087 ISO_TIME, 088 089 /** {@link DateTimeFormatter#ISO_WEEK_DATE}: <js>"2012-W48-6"</js>. */ 090 ISO_WEEK_DATE, 091 092 /** {@link DateTimeFormatter#ISO_ZONED_DATE_TIME}: <js>"2011-12-03T10:15:30+01:00[Europe/Paris]"</js>. */ 093 ISO_ZONED_DATE_TIME, 094 095 /** {@link DateTimeFormatter#RFC_1123_DATE_TIME}: <js>"Tue, 3 Jun 2008 11:05:30 GMT"</js>. */ 096 RFC_1123_DATE_TIME, 097 098 /** 099 * Epoch milliseconds as a numeric value. 100 * 101 * <p> 102 * Emitted as a native int64 by binary serializers (BSON / CBOR / MsgPack) and as a bare integer 103 * literal by text serializers. 104 */ 105 MILLIS; 106 107 /** 108 * Formats the specified value using this format. 109 * 110 * @param value The value to format. Can be <jk>null</jk> (returns <jk>null</jk>). 111 * @param zoneId The default zone id used to interpret the date. 112 * @return The formatted string, or <jk>null</jk> if {@code value} is <jk>null</jk>. 113 */ 114 public String format(Date value, ZoneId zoneId) { 115 if (value == null) 116 return null; 117 if (this == MILLIS) 118 return Long.toString(value.getTime()); 119 var zone = zoneId == null ? ZoneId.systemDefault() : zoneId; 120 return formatter().format(value.toInstant().atZone(zone)); 121 } 122 123 /** 124 * Parses the specified wire value using this format. 125 * 126 * @param value The wire value. Can be <jk>null</jk> or blank (returns <jk>null</jk>). 127 * @param zoneId The default zone id used for time-only / unzoned values. 128 * @return The parsed {@link Date}, or <jk>null</jk> if {@code value} is <jk>null</jk> or blank. 129 */ 130 public Date parse(String value, ZoneId zoneId) { 131 if (value == null) 132 return null; 133 var s = value.trim(); 134 if (s.isEmpty()) 135 return null; 136 if (this == MILLIS) 137 return new Date(Long.parseLong(s)); 138 var zone = zoneId == null ? ZoneId.systemDefault() : zoneId; 139 var ta = new DefaultingTemporalAccessor(formatter().parse(s), zone); 140 return Date.from(ZonedDateTime.from(ta).toInstant()); 141 } 142 143 /** 144 * Returns <jk>true</jk> if this format is numeric on the wire. 145 * 146 * @return <jk>true</jk> if this format emits a numeric wire value. 147 */ 148 public boolean isNumeric() { 149 return this == MILLIS; 150 } 151 152 private DateTimeFormatter formatter() { 153 return switch (this) { 154 case NOT_SET, ISO_LOCAL_DATE_TIME -> DateTimeFormatter.ISO_LOCAL_DATE_TIME; 155 case BASIC_ISO_DATE -> DateTimeFormatter.BASIC_ISO_DATE; 156 case ISO_DATE -> DateTimeFormatter.ISO_DATE; 157 case ISO_DATE_TIME -> DateTimeFormatter.ISO_DATE_TIME; 158 case ISO_INSTANT -> DateTimeFormatter.ISO_INSTANT; 159 case ISO_LOCAL_DATE -> DateTimeFormatter.ISO_LOCAL_DATE; 160 case ISO_LOCAL_TIME -> DateTimeFormatter.ISO_LOCAL_TIME; 161 case ISO_OFFSET_DATE -> DateTimeFormatter.ISO_OFFSET_DATE; 162 case ISO_OFFSET_DATE_TIME -> DateTimeFormatter.ISO_OFFSET_DATE_TIME; 163 case ISO_OFFSET_TIME -> DateTimeFormatter.ISO_OFFSET_TIME; 164 case ISO_ORDINAL_DATE -> DateTimeFormatter.ISO_ORDINAL_DATE; 165 case ISO_TIME -> DateTimeFormatter.ISO_TIME; 166 case ISO_WEEK_DATE -> DateTimeFormatter.ISO_WEEK_DATE; 167 case ISO_ZONED_DATE_TIME -> DateTimeFormatter.ISO_ZONED_DATE_TIME; 168 case RFC_1123_DATE_TIME -> DateTimeFormatter.RFC_1123_DATE_TIME; 169 case MILLIS -> throw new IllegalStateException("MILLIS does not use a DateTimeFormatter"); 170 }; 171 } 172}