001// Licensed under the MIT license. See LICENSE file in the project root for full license information. 002 003package de.bytefish.pgbulkinsert.pgsql.utils; 004 005import java.time.LocalDate; 006import java.time.LocalDateTime; 007import java.time.OffsetDateTime; 008import java.time.ZoneOffset; 009import java.time.temporal.ChronoUnit; 010import java.util.concurrent.TimeUnit; 011 012public class TimeStampUtils { 013 014 private TimeStampUtils() { 015 016 } 017 018 private static final LocalDateTime JavaEpoch = LocalDateTime.of(1970, 1, 1, 0, 0, 0); 019 020 private static final LocalDateTime PostgresEpoch = LocalDateTime.of(2000, 1, 1, 0, 0, 0); 021 022 private static final long DaysBetweenJavaAndPostgresEpochs = ChronoUnit.DAYS.between(JavaEpoch, PostgresEpoch); 023 024 025 public static long convertToPostgresTimeStamp(LocalDateTime localDateTime) { 026 027 if(localDateTime == null) { 028 throw new IllegalArgumentException("localDateTime"); 029 } 030 // Extract the Time of the Day in Nanoseconds: 031 long timeInNanoseconds = localDateTime 032 .toLocalTime() 033 .toNanoOfDay(); 034 035 // Convert the Nanoseconds to Microseconds: 036 long timeInMicroseconds = timeInNanoseconds / 1000; 037 038 // Now Calculate the Postgres Timestamp: 039 if(localDateTime.isBefore(PostgresEpoch)) { 040 long dateInMicroseconds = (localDateTime.toLocalDate().toEpochDay() - DaysBetweenJavaAndPostgresEpochs) * 86400000000L; 041 042 return dateInMicroseconds + timeInMicroseconds; 043 } else { 044 long dateInMicroseconds = (DaysBetweenJavaAndPostgresEpochs - localDateTime.toLocalDate().toEpochDay()) * 86400000000L; 045 046 return -(dateInMicroseconds - timeInMicroseconds); 047 } 048 } 049 050 public static int toPgDays(LocalDate date) 051 { 052 // Adjust TimeZone Offset: 053 LocalDateTime dateTime = date.atStartOfDay(); 054 // pg time 0 is 2000-01-01 00:00:00: 055 long secs = toPgSecs(getSecondsSinceJavaEpoch(dateTime)); 056 // Needs Days: 057 return (int) TimeUnit.SECONDS.toDays(secs); 058 } 059 060 public static Long toPgSecs(LocalDateTime dateTime) { 061 // pg time 0 is 2000-01-01 00:00:00: 062 long secs = toPgSecs(getSecondsSinceJavaEpoch(dateTime)); 063 // Needs Microseconds: 064 return TimeUnit.SECONDS.toMicros(secs); 065 } 066 067 private static long getSecondsSinceJavaEpoch(LocalDateTime localDateTime) { 068 // Adjust TimeZone Offset: 069 OffsetDateTime zdt = localDateTime.atOffset(ZoneOffset.UTC); 070 // Get the Epoch Milliseconds: 071 long milliseconds = zdt.toInstant().toEpochMilli(); 072 // Turn into Seconds: 073 return TimeUnit.MILLISECONDS.toSeconds(milliseconds); 074 } 075 076 /** 077 * Converts the given java seconds to postgresql seconds. The conversion is valid for any year 100 BC onwards. 078 * 079 * from /org/postgresql/jdbc2/TimestampUtils.java 080 * 081 * @param seconds Postgresql seconds. 082 * @return Java seconds. 083 */ 084 @SuppressWarnings("checkstyle:magicnumber") 085 private static long toPgSecs(final long seconds) { 086 long secs = seconds; 087 // java epoc to postgres epoc 088 secs -= 946684800L; 089 090 // Julian/Greagorian calendar cutoff point 091 if (secs < -13165977600L) { // October 15, 1582 -> October 4, 1582 092 secs -= 86400 * 10; 093 if (secs < -15773356800L) { // 1500-03-01 -> 1500-02-28 094 int years = (int) ((secs + 15773356800L) / -3155823050L); 095 years++; 096 years -= years / 4; 097 secs += years * 86400; 098 } 099 } 100 101 return secs; 102 } 103}