View Javadoc
1   /*
2   Copyright (c) 2018 James Ahlborn
3   
4   Licensed under the Apache License, Version 2.0 (the "License");
5   you may not use this file except in compliance with the License.
6   You may obtain a copy of the License at
7   
8       http://www.apache.org/licenses/LICENSE-2.0
9   
10  Unless required by applicable law or agreed to in writing, software
11  distributed under the License is distributed on an "AS IS" BASIS,
12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  See the License for the specific language governing permissions and
14  limitations under the License.
15  */
16  
17  package com.healthmarketscience.jackcess;
18  
19  import java.text.DateFormat;
20  import java.text.SimpleDateFormat;
21  import java.time.Instant;
22  import java.time.LocalDate;
23  import java.time.LocalDateTime;
24  import java.time.ZoneId;
25  import java.time.ZonedDateTime;
26  import java.time.format.DateTimeFormatter;
27  import java.util.ArrayList;
28  import java.util.Arrays;
29  import java.util.Calendar;
30  import java.util.Date;
31  import java.util.List;
32  import java.util.TimeZone;
33  
34  import com.healthmarketscience.jackcess.impl.ColumnImpl;
35  import com.healthmarketscience.jackcess.impl.DatabaseImpl;
36  import junit.framework.TestCase;
37  import static com.healthmarketscience.jackcess.TestUtil.*;
38  import static com.healthmarketscience.jackcess.impl.JetFormatTest.*;
39  import static com.healthmarketscience.jackcess.Database.*;
40  import static com.healthmarketscience.jackcess.DatabaseBuilder.*;
41  
42  /**
43   *
44   * @author James Ahlborn
45   */
46  public class LocalDateTimeTest extends TestCase
47  {
48    public LocalDateTimeTest(String name) throws Exception {
49      super(name);
50    }
51  
52    public void testWriteAndReadLocalDate() throws Exception {
53      for (final FileFormat fileFormat : SUPPORTED_FILEFORMATS) {
54        Database db = createMem(fileFormat);
55  
56        db.setDateTimeType(DateTimeType.LOCAL_DATE_TIME);
57  
58        Table table = newTable("test")
59          .addColumn(newColumn("name", DataType.TEXT))
60          .addColumn(newColumn("date", DataType.SHORT_DATE_TIME))
61          .toTable(db);
62  
63        // since jackcess does not really store millis, shave them off before
64        // storing the current date/time
65        long curTimeNoMillis = (System.currentTimeMillis() / 1000L);
66        curTimeNoMillis *= 1000L;
67  
68        DateFormat df = new SimpleDateFormat("yyyyMMdd HH:mm:ss");
69        List<Date> dates =
70          new ArrayList<Date>(
71              Arrays.asList(
72                  df.parse("19801231 00:00:00"),
73                  df.parse("19930513 14:43:27"),
74                  null,
75                  df.parse("20210102 02:37:00"),
76                  new Date(curTimeNoMillis)));
77  
78        Calendar c = Calendar.getInstance();
79        for(int year = 1801; year < 2050; year +=3) {
80          for(int month = 0; month <= 12; ++month) {
81            for(int day = 1; day < 29; day += 3) {
82              c.clear();
83              c.set(Calendar.YEAR, year);
84              c.set(Calendar.MONTH, month);
85              c.set(Calendar.DAY_OF_MONTH, day);
86              dates.add(c.getTime());
87            }
88          }
89        }
90  
91        ((DatabaseImpl)db).getPageChannel().startWrite();
92        try {
93          for(Date d : dates) {
94            table.addRow("row " + d, d);
95          }
96        } finally {
97          ((DatabaseImpl)db).getPageChannel().finishWrite();
98        }
99  
100       List<LocalDateTime> foundDates = new ArrayList<LocalDateTime>();
101       for(Row row : table) {
102         foundDates.add(row.getLocalDateTime("date"));
103       }
104 
105       assertEquals(dates.size(), foundDates.size());
106       for(int i = 0; i < dates.size(); ++i) {
107         Date expected = dates.get(i);
108         LocalDateTime found = foundDates.get(i);
109         assertSameDate(expected, found);
110       }
111 
112       db.close();
113     }
114   }
115 
116   public void testAncientLocalDates() throws Exception
117   {
118     ZoneId zoneId = ZoneId.of("America/New_York");
119     DateTimeFormatter sdf = DateTimeFormatter.ofPattern("uuuu-MM-dd");
120 
121     List<String> dates = Arrays.asList("1582-10-15", "1582-10-14",
122                                        "1492-01-10", "1392-01-10");
123 
124     for (final FileFormat fileFormat : SUPPORTED_FILEFORMATS) {
125       Database db = createMem(fileFormat);
126       db.setZoneId(zoneId);
127       db.setDateTimeType(DateTimeType.LOCAL_DATE_TIME);
128 
129       Table table = newTable("test")
130         .addColumn(newColumn("name", DataType.TEXT))
131         .addColumn(newColumn("date", DataType.SHORT_DATE_TIME))
132         .toTable(db);
133 
134       for(String dateStr : dates) {
135         LocalDate ld = LocalDate.parse(dateStr, sdf);
136         table.addRow("row " + dateStr, ld);
137       }
138 
139       List<String> foundDates = new ArrayList<String>();
140       for(Row row : table) {
141         foundDates.add(sdf.format(row.getLocalDateTime("date")));
142       }
143 
144       assertEquals(dates, foundDates);
145 
146       db.close();
147     }
148 
149     for (final TestDB testDB : TestDB.getSupportedForBasename(Basename.OLD_DATES)) {
150       Database db = openCopy(testDB);
151       db.setDateTimeType(DateTimeType.LOCAL_DATE_TIME);
152 
153       Table t = db.getTable("Table1");
154 
155       List<String> foundDates = new ArrayList<String>();
156       for(Row row : t) {
157         foundDates.add(sdf.format(row.getLocalDateTime("DateField")));
158       }
159 
160       assertEquals(dates, foundDates);
161 
162       db.close();
163     }
164 
165   }
166 
167   public void testZoneId() throws Exception
168   {
169     ZoneId zoneId = ZoneId.of("America/New_York");
170     doTestZoneId(zoneId);
171 
172     zoneId = ZoneId.of("Australia/Sydney");
173     doTestZoneId(zoneId);
174   }
175 
176   private static void doTestZoneId(final ZoneId zoneId) throws Exception
177   {
178     final TimeZone tz = TimeZone.getTimeZone(zoneId);
179     ColumnImpl col = new ColumnImpl(null, null, DataType.SHORT_DATE_TIME, 0, 0, 0) {
180       @Override
181       public TimeZone getTimeZone() { return tz; }
182       @Override
183       public ZoneId getZoneId() { return zoneId; }
184       @Override
185       public ColumnImpl.DateTimeFactory getDateTimeFactory() {
186         return getDateTimeFactory(DateTimeType.LOCAL_DATE_TIME);
187       }
188     };
189 
190     SimpleDateFormat df = new SimpleDateFormat("yyyy.MM.dd");
191     df.setTimeZone(tz);
192 
193     long startDate = df.parse("2012.01.01").getTime();
194     long endDate = df.parse("2013.01.01").getTime();
195 
196     Calendar curCal = Calendar.getInstance(tz);
197     curCal.setTimeInMillis(startDate);
198 
199     DateTimeFormatter sdf = DateTimeFormatter.ofPattern("uuuu.MM.dd HH:mm:ss");
200 
201     while(curCal.getTimeInMillis() < endDate) {
202       Date curDate = curCal.getTime();
203       LocalDateTime curLdt = LocalDateTime.ofInstant(
204           Instant.ofEpochMilli(curDate.getTime()), zoneId);
205       LocalDateTime newLdt = ColumnImpl.ldtFromLocalDateDouble(
206           col.toDateDouble(curDate));
207       if(!curLdt.equals(newLdt)) {
208         System.out.println("FOO " + curLdt + " " + newLdt);
209         assertEquals(sdf.format(curLdt), sdf.format(newLdt));
210       }
211       curCal.add(Calendar.MINUTE, 30);
212     }
213   }
214 
215   public void testWriteAndReadTemporals() throws Exception {
216     ZoneId zoneId = ZoneId.of("America/New_York");
217     for (final FileFormat fileFormat : SUPPORTED_FILEFORMATS) {
218       Database db = createMem(fileFormat);
219       db.setZoneId(zoneId);
220       db.setDateTimeType(DateTimeType.LOCAL_DATE_TIME);
221 
222       Table table = newTable("test")
223         .addColumn(newColumn("name", DataType.TEXT))
224         .addColumn(newColumn("date", DataType.SHORT_DATE_TIME))
225         .toTable(db);
226 
227       // since jackcess does not really store millis, shave them off before
228       // storing the current date/time
229       long curTimeNoMillis = (System.currentTimeMillis() / 1000L);
230       curTimeNoMillis *= 1000L;
231 
232       DateFormat df = new SimpleDateFormat("yyyyMMdd HH:mm:ss");
233       List<Date> tmpDates =
234         new ArrayList<Date>(
235             Arrays.asList(
236                 df.parse("19801231 00:00:00"),
237                 df.parse("19930513 14:43:27"),
238                 df.parse("20210102 02:37:00"),
239                 new Date(curTimeNoMillis)));
240 
241       List<Object> objs = new ArrayList<Object>();
242       List<LocalDateTime> expected = new ArrayList<LocalDateTime>();
243       for(Date d : tmpDates) {
244         Instant inst = Instant.ofEpochMilli(d.getTime());
245         objs.add(inst);
246         ZonedDateTime zdt = inst.atZone(zoneId);
247         objs.add(zdt);
248         LocalDateTime ldt = zdt.toLocalDateTime();
249         objs.add(ldt);
250 
251         for(int i = 0; i < 3; ++i) {
252           expected.add(ldt);
253         }
254       }
255 
256       ((DatabaseImpl)db).getPageChannel().startWrite();
257       try {
258         for(Object o : objs) {
259           table.addRow("row " + o, o);
260         }
261       } finally {
262         ((DatabaseImpl)db).getPageChannel().finishWrite();
263       }
264 
265       List<LocalDateTime> foundDates = new ArrayList<LocalDateTime>();
266       for(Row row : table) {
267         foundDates.add(row.getLocalDateTime("date"));
268       }
269 
270       assertEquals(expected, foundDates);
271 
272       db.close();
273     }
274   }
275 
276 }