Skip to content

Commit 8187932

Browse files
committed
backport: conversions for java.util.Calendar + java.util.TimeZone
see issue #705
1 parent e3161d5 commit 8187932

File tree

8 files changed

+484
-12
lines changed

8 files changed

+484
-12
lines changed
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
/*
2+
* -----------------------------------------------------------------------
3+
* Copyright © 2013-2017 Meno Hochschild, <http://www.menodata.de/>
4+
* -----------------------------------------------------------------------
5+
* This file (OldApiTimezone.java) is part of project Time4J.
6+
*
7+
* Time4J is free software: You can redistribute it and/or modify it
8+
* under the terms of the GNU Lesser General Public License as published
9+
* by the Free Software Foundation, either version 2.1 of the License, or
10+
* (at your option) any later version.
11+
*
12+
* Time4J is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU Lesser General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU Lesser General Public License
18+
* along with Time4J. If not, see <http://www.gnu.org/licenses/>.
19+
* -----------------------------------------------------------------------
20+
*/
21+
22+
package net.time4j;
23+
24+
import net.time4j.tz.Timezone;
25+
import net.time4j.tz.TransitionHistory;
26+
import net.time4j.tz.ZonalTransition;
27+
28+
import java.util.Date;
29+
import java.util.List;
30+
31+
32+
/**
33+
* <p>Spezialimplementierung, die die Daten und Regeln einer Time4J-Zeitzone im Gewand des alten API bewahrt.. </p>
34+
*
35+
* @author Meno Hochschild
36+
* @since 3.37/4.32
37+
* @serial include
38+
*/
39+
final class OldApiTimezone
40+
extends java.util.TimeZone {
41+
42+
//~ Statische Felder/Initialisierungen --------------------------------
43+
44+
private static final long serialVersionUID = -6919910650419401271L;
45+
46+
//~ Instanzvariablen --------------------------------------------------
47+
48+
/**
49+
* @serial the underlying Time4J-zone
50+
*/
51+
private final Timezone tz;
52+
53+
//~ Konstruktoren -----------------------------------------------------
54+
55+
OldApiTimezone(Timezone tz) {
56+
super();
57+
58+
this.tz = tz;
59+
60+
this.setID(tz.getID().canonical());
61+
assert (tz.getHistory() != null);
62+
63+
}
64+
65+
//~ Methoden ----------------------------------------------------------
66+
67+
@Override
68+
public int getOffset(long date) {
69+
70+
return this.tz.getOffset(
71+
TemporalType.MILLIS_SINCE_UNIX.translate(Long.valueOf(date))
72+
).getIntegralAmount() * 1000;
73+
74+
}
75+
76+
@Override
77+
public int getOffset(int era, int year, int month, int day, int dayOfWeek, int milliseconds) {
78+
79+
if (milliseconds < 0 || milliseconds >= 86400000) {
80+
throw new IllegalArgumentException("Milliseconds out of range: " + milliseconds);
81+
}
82+
83+
if (era == java.util.GregorianCalendar.BC) {
84+
year = 1 - year;
85+
} else if (era != java.util.GregorianCalendar.AD) {
86+
throw new IllegalArgumentException("Unknown era: " + era);
87+
} else if (dayOfWeek < 1 || dayOfWeek >= 7) {
88+
throw new IllegalArgumentException("Day-of-week out of range: " + dayOfWeek);
89+
}
90+
91+
return this.tz.getOffset(
92+
PlainDate.of(year, month + 1, day), // input month is zero-based
93+
PlainTime.midnightAtStartOfDay().plus(milliseconds, ClockUnit.MILLIS)
94+
).getIntegralAmount() * 1000;
95+
96+
}
97+
98+
@Override
99+
public void setRawOffset(int offsetMillis) {
100+
101+
// manipulation of raw offsets not supported => no-op
102+
103+
}
104+
105+
@Override
106+
public int getRawOffset() {
107+
108+
return this.tz.getStandardOffset(SystemClock.currentMoment()).getIntegralAmount() * 1000;
109+
110+
}
111+
112+
@Override
113+
public int getDSTSavings() {
114+
115+
TransitionHistory history = this.tz.getHistory();
116+
117+
if (history != null) {
118+
List<ZonalTransition> transitions = history.getStdTransitions();
119+
int dst = 0;
120+
for (int i = transitions.size() - 1; i >= 0; i--) {
121+
ZonalTransition t = transitions.get(i);
122+
if (t.isDaylightSaving()) {
123+
dst = t.getDaylightSavingOffset() * 1000;
124+
}
125+
}
126+
return dst;
127+
}
128+
129+
return 0;
130+
131+
}
132+
133+
@Override
134+
public boolean useDaylightTime() {
135+
136+
return !this.tz.isFixed() && (this.getDSTSavings() != 0);
137+
138+
}
139+
140+
@Override
141+
public boolean inDaylightTime(Date date) {
142+
143+
return this.tz.isDaylightSaving(TemporalType.JAVA_UTIL_DATE.translate(date));
144+
145+
}
146+
147+
@Override
148+
public boolean hasSameRules(java.util.TimeZone other) {
149+
150+
if (this == other) {
151+
return true;
152+
} else if (other instanceof OldApiTimezone) {
153+
OldApiTimezone that = (OldApiTimezone) other;
154+
return this.tz.getHistory().equals(that.tz.getHistory());
155+
}
156+
157+
return false;
158+
159+
}
160+
161+
@Override
162+
public boolean equals(Object obj) {
163+
164+
if (obj instanceof OldApiTimezone) {
165+
OldApiTimezone that = (OldApiTimezone) obj;
166+
return this.tz.equals(that.tz);
167+
}
168+
169+
return false;
170+
171+
}
172+
173+
@Override
174+
public int hashCode() {
175+
176+
return this.tz.hashCode();
177+
178+
}
179+
180+
@Override
181+
public String toString() {
182+
183+
return this.getClass().getName() + "@" + this.tz.toString();
184+
185+
}
186+
187+
Timezone getDelegate() {
188+
189+
return this.tz;
190+
191+
}
192+
193+
}

core/src/main/java/net/time4j/TemporalType.java

Lines changed: 134 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
* -----------------------------------------------------------------------
3-
* Copyright © 2013-2016 Meno Hochschild, <http://www.menodata.de/>
3+
* Copyright © 2013-2017 Meno Hochschild, <http://www.menodata.de/>
44
* -----------------------------------------------------------------------
55
* This file (TemporalType.java) is part of project Time4J.
66
*
@@ -25,6 +25,12 @@
2525
import net.time4j.engine.ChronoException;
2626
import net.time4j.engine.Converter;
2727
import net.time4j.scale.TimeScale;
28+
import net.time4j.tz.Timezone;
29+
30+
import java.util.Calendar;
31+
import java.util.Date;
32+
import java.util.GregorianCalendar;
33+
import java.util.TimeZone;
2834

2935

3036
/**
@@ -92,7 +98,7 @@ public abstract class TemporalType<S, T>
9298
*
9399
* @since 2.0
94100
*/
95-
public static final TemporalType<java.util.Date, Moment> JAVA_UTIL_DATE =
101+
public static final TemporalType<Date, Moment> JAVA_UTIL_DATE =
96102
new JavaUtilDateRule();
97103

98104
/**
@@ -133,6 +139,48 @@ public abstract class TemporalType<S, T>
133139
public static final TemporalType<Long, Moment> MILLIS_SINCE_UNIX =
134140
new MillisSinceUnixRule();
135141

142+
/**
143+
* <p>Bridge between a traditional Java calendar of type
144+
* {@code java.util.Calendar} and the class {@code ZonalDateTime}.</p>
145+
*
146+
* <p>The conversion tries to keep the instant and zone data involved. A change
147+
* of the local timestamp part of {@code Calendar} is possible, however. This
148+
* concerns the conversion of any non-gregorian calendar. </p>
149+
*
150+
* @since 3.37/4.32
151+
*/
152+
/*[deutsch]
153+
* <p>Br&uuml;cke zwischen einem traditionellen Java-Kalender des Typs
154+
* {@code java.util.Calendar} und der Klasse {@code ZonalDateTime}. </p>
155+
*
156+
* <p>Die Konversion versucht, den Moment und die verwendeten Zeitzonendaten zu erhalten.
157+
* Eine &Auml;nderung des lokalen Zeitstempels ist aber m&ouml;glich. Das betrifft die
158+
* Umwandlung eines jeden nicht-gregorianischen Kalenders. </p>
159+
*
160+
* @since 3.37/4.32
161+
*/
162+
public static final TemporalType<Calendar, ZonalDateTime> JAVA_UTIL_CALENDAR = new CalendarRule();
163+
164+
/**
165+
* <p>Bridge between a traditional Java timezone of type
166+
* {@code java.util.TimeZone} and the class {@code net.time4j.tz.Timezone}.</p>
167+
*
168+
* <p>The conversion tries to keep the data and rules of the zone to be converted. </p>
169+
*
170+
* @since 3.37/4.32
171+
*/
172+
/*[deutsch]
173+
* <p>Br&uuml;cke zwischen einer traditionellen Java-Zeitzone des Typs
174+
* {@code java.util.TimeZone} und der Klasse {@code net.time4j.tz.Timezone}. </p>
175+
*
176+
* <p>Die Konversion versucht, die zugrundeliegenden Daten und Regeln des Ausgangsobjekts zu erhalten. </p>
177+
*
178+
* @since 3.37/4.32
179+
*/
180+
public static final TemporalType<TimeZone, Timezone> JAVA_UTIL_TIMEZONE = new ZoneRule();
181+
182+
private static final String JUT_PROVIDER = "java.util.TimeZone~";
183+
136184
//~ Konstruktoren -----------------------------------------------------
137185

138186
/**
@@ -198,12 +246,12 @@ protected TemporalType() {
198246
//~ Innere Klassen ----------------------------------------------------
199247

200248
private static class JavaUtilDateRule
201-
extends TemporalType<java.util.Date, Moment> {
249+
extends TemporalType<Date, Moment> {
202250

203251
//~ Methoden ------------------------------------------------------
204252

205253
@Override
206-
public Moment translate(java.util.Date source) {
254+
public Moment translate(Date source) {
207255

208256
long millis = source.getTime();
209257
long seconds = MathUtils.floorDivide(millis, 1000);
@@ -213,7 +261,7 @@ public Moment translate(java.util.Date source) {
213261
}
214262

215263
@Override
216-
public java.util.Date from(Moment target) {
264+
public Date from(Moment target) {
217265

218266
long posixTime = target.getPosixTime();
219267
int fraction = target.getNanosecond();
@@ -222,14 +270,14 @@ public java.util.Date from(Moment target) {
222270
MathUtils.safeAdd(
223271
MathUtils.safeMultiply(posixTime, 1000),
224272
fraction / MIO);
225-
return new java.util.Date(millis);
273+
return new Date(millis);
226274

227275
}
228276

229277
@Override
230-
public Class<java.util.Date> getSourceType() {
278+
public Class<Date> getSourceType() {
231279

232-
return java.util.Date.class;
280+
return Date.class;
233281

234282
}
235283

@@ -272,4 +320,82 @@ public Class<Long> getSourceType() {
272320

273321
}
274322

323+
private static class CalendarRule
324+
extends TemporalType<Calendar, ZonalDateTime> {
325+
326+
//~ Methoden ------------------------------------------------------
327+
328+
@Override
329+
public ZonalDateTime translate(Calendar source) {
330+
331+
Moment m = TemporalType.JAVA_UTIL_DATE.translate(source.getTime());
332+
Timezone tz = TemporalType.JAVA_UTIL_TIMEZONE.translate(source.getTimeZone());
333+
return ZonalDateTime.of(m, tz);
334+
335+
}
336+
337+
@Override
338+
public Calendar from(ZonalDateTime time4j) {
339+
340+
Date jud = TemporalType.JAVA_UTIL_DATE.from(time4j.toMoment());
341+
TimeZone tz = TemporalType.JAVA_UTIL_TIMEZONE.from(time4j.getTimezone0());
342+
GregorianCalendar gcal = new GregorianCalendar();
343+
gcal.setGregorianChange(new Date(Long.MIN_VALUE)); // proleptic gregorian
344+
gcal.setFirstDayOfWeek(Calendar.MONDAY); // keeping ISO-8601-semantic
345+
gcal.setMinimalDaysInFirstWeek(4); // keeping ISO-8601-semantic
346+
gcal.setTimeZone(tz);
347+
gcal.setTime(jud);
348+
return gcal;
349+
350+
}
351+
352+
@Override
353+
public Class<Calendar> getSourceType() {
354+
355+
return Calendar.class;
356+
357+
}
358+
359+
}
360+
361+
private static class ZoneRule
362+
extends TemporalType<TimeZone, Timezone> {
363+
364+
//~ Methoden ------------------------------------------------------
365+
366+
@Override
367+
public Timezone translate(TimeZone source) {
368+
369+
if (source instanceof OldApiTimezone) {
370+
return ((OldApiTimezone) source).getDelegate();
371+
} else {
372+
return Timezone.of(JUT_PROVIDER + source.getID());
373+
}
374+
375+
}
376+
377+
@Override
378+
public TimeZone from(Timezone time4j) {
379+
380+
if (time4j.getHistory() == null) {
381+
String id = time4j.getID().canonical();
382+
if (id.startsWith(JUT_PROVIDER)) {
383+
id = id.substring(JUT_PROVIDER.length());
384+
}
385+
return TimeZone.getTimeZone(id);
386+
} else {
387+
return new OldApiTimezone(time4j);
388+
}
389+
390+
}
391+
392+
@Override
393+
public Class<TimeZone> getSourceType() {
394+
395+
return TimeZone.class;
396+
397+
}
398+
399+
}
400+
275401
}

0 commit comments

Comments
 (0)