I want to check if a given DateTime is before or after current
DateTime. I was converting input time and current time to a common
time zone (say UTC) and comparing DateTime. But I stumbled upon Joda
Api, hence I was curious to know if Joda is capable of doing this
without a time zone conversion. Example:
clientDateTime.isBeforeNow()
Yes, Joda DateTime comparisons with current time do not require a time zone conversion.
When comparing with current time, such as DateTime.isBeforeNow and DateTime.isAfterNow, Joda simply compares the underlying absolute milliseconds since Jan 1, 1970. The same instant in time has exactly the same absolute milliseconds value, regardless of the timezone.
For example, the instant 1355625068295 corresponds to:
DateTime dt = new DateTime(1355625068295L);
DateTime utc = dt.withZone(DateTimeZone.UTC);
DateTime ny = dt.withZone(DateTimeZone.forID("America/New_York"));
DateTime tk = dt.withZone(DateTimeZone.forID("Asia/Tokyo"));
System.out.println(utc.getMillis() + " is " + utc);
System.out.println(ny.getMillis() + " is " + ny);
System.out.println(tk.getMillis() + " is " + tk);
Output:
1355625068295 is 2012-12-16T02:31:08.295Z
1355625068295 is 2012-12-15T21:31:08.295-05:00
1355625068295 is 2012-12-16T11:31:08.295+09:00
And when comparing with "now":
System.out.println("now: " + new DateTime().getMillis());
System.out.println(ny.isBeforeNow());
System.out.println(ny.plusHours(1).isAfterNow());
System.out.println(tk.isBeforeNow());
System.out.println(tk.plusHours(1).isAfterNow());
Output:
now: 1355625752323
true
true
true
true
Related
Using the following code:
class Test {
public static void main(String [] args) {
LocalDate date = LocalDate.of(2018, 11, 4);
LocalTime time = LocalTime.of(1, 59);
ZonedDateTime dt = ZonedDateTime.of(date, time, ZoneId.of("America/New_York"));
System.out.println(dt.getHour() + ":" + dt.getMinute() + ":" + dt.getSecond());
dt = dt.plusMinutes(1);
System.out.println(dt.getHour() + ":" + dt.getMinute() + ":" + dt.getSecond());
dt = dt.plusMinutes(59);
System.out.println(dt.getHour() + ":" + dt.getMinute() + ":" + dt.getSecond());
dt = dt.plusMinutes(1);
System.out.println(dt.getHour() + ":" + dt.getMinute() + ":" + dt.getSecond());
}
}
I get
1:59:0
1:0:0
1:59:0
2:0:0
Is there a way to get to the 1:00:00 from after daylight saving time without going through the 1:00:00 from before daylight saving time?
Use ofStrict to specify which offset you want the resulting zoned date time to be on. America/New_York changes from -04:00 to -05:00 at around the time in question, so you want ZoneOffset.ofHours(-5).
ZonedDateTime dt = ZonedDateTime.ofStrict(
LocalDateTime.of(date, time),
ZoneOffset.ofHours(-5),
ZoneId.of("America/New_York")
);
In case you cannot hardcode the offset in, you can get the offset after using:
var dateTime = LocalDateTime.of(date, time)
var zone = ZoneId.of("America/New_York");
var offsetAfter = zone.getRules()
.getTransition(dateTime)
.getOffsetAfter();
ZonedDateTime dt = ZonedDateTime.ofStrict(
dateTime,
offsetAfter,
zone
);
Or, as Ole V.V. pointed out, you can also use the much shorter:
ZonedDateTime dt = ZonedDateTime.of(
date, time, ZoneId.of("America/New_York")
).withLaterOffsetAtOverlap();
The problem with time zones that honor Daylight Saving Time is that such times are ambiguous. The "America/New_York" zone has two times that are both labeled 2008-11-04T01:00:00. Technically, one of them is 01:00 EDT and the other is 01:00 EST, but not a lot of software will let you make the distinction that way, not least since such three-letter time zone designations are not necessarily globally unique.
The solution is to specify the time either in or relative to Universal time, which doesn't have daylight saving: the first 01:00 Eastern time is 05:00Z, and the second is 06:00Z. So you can either give the time as one of those and the zone as "UTC" (and then convert the result to "America/New_York"), or specify the offset from UTC using ofStrict.
I had this function that convert string type of date to unix timestamp, how to convert the result to timeInSeconds and offsetInNanos
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(
"yyyy-MM-dd-HH-mm-ss");
String dateTimeString = "2016-06-21-10-19-22";
LocalDate date = LocalDate.parse(dateTimeString, formatter);
ZonedDateTime resultado = date.atStartOfDay(ZoneId.of("UTC"));
Instant i = resultado.toInstant();
long timeInSeconds = i.getEpochSecond();
int nanoAdjustment = i.getNano();
System.out.println("" + timeInSeconds + " seconds " + nanoAdjustment + " nanoseconds");
result is 1466467200 seconds 0 nanoseconds
but the correct answer seems to be 1466504362 seconds
Edit
result is 1466467200 seconds 0 nanoseconds
but the correct answer seems to be 1466504362 seconds
I think it convert "2016-06-21-10-19-22" to 2016-06-21T00:00:00+00:00,
how to solve this problem, to convert both date with time and date
without time to correct timeInSeconds?
You are absolutely correct, that is what it does, This is because in your new code in the question you are
Only parsing the date part. You are parsing into a LocalDate, which is a date without time of day, so the time of day in the string is being ignored.
Then calling atStartOfDay(). This makes sure that the time of day is set to — as the method name says — the start of the day, in this case in UTC, so 00:00:00 UTC.
To solve: instead parse into a LocalDateTime so you get both date and time.
LocalDateTime date = LocalDateTime.parse(dateTimeString, formatter);
OffsetDateTime resultado = date.atOffset(ZoneOffset.UTC);
The rest of the code is the same. Now the output is:
1466504362 seconds 0 nanoseconds
This is the result you said you expected. While a ZonedDateTime would have worked too, for UTC it’s overkill, I recommend you use OffsetDateTime as I am showing.
For how to parse a string that may or may not have time of day in it, see some of the questions that I link to at the bottom.
Original answer: java.time
I suppose that by offset in nanos you meant nano adjustment, nanosecond part or nano of second (not offset from UTC). With java.time, the modern Java date and time API, it’s straightforward:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(
"MMM dd yyyy HH:mm:ss.SSS zzz", Locale.ENGLISH);
String dateTimeString = "Jun 13 2003 23:11:52.454 UTC";
Instant i = ZonedDateTime.parse(dateTimeString, formatter)
.toInstant();
long timeInSeconds = i.getEpochSecond();
int nanoAdjustment = i.getNano();
System.out.println("" + timeInSeconds + " seconds " + nanoAdjustment + " nanoseconds");
Output is:
1055545912 seconds 454000000 nanoseconds
I just did what deHaar said in the comments.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Questions on parsing dates with and without times:
Convert date time string like Joda DateTime(String) with Java 8
Parsing an ISO 8601 date in Java8 when all fields (including separators, but not including years) are optional
I know that there are tons of different tutorials on time conversion, but this one got me very confused. My task is to read UTC DATE from Oracle DB and convert it into BST time (in a more human readable format).
Facts:
Field in the DB is of DATE type.
When i perform SELECT query it returns 2011-07-12 15:26:07 result.
I'm located in Poland, hence in July the TimeZone here is UTC+2
What's happening:
On the Java side I'm using "classical" JDBC connection to the DB.
When I perform Timestamp timestampDate = resultSet.getTimestamp(COLUMN_NAME) I get the result ... but ...
System.out.println(timestampDate) prints to the console 2011-07-12 15:26:07.0 (which is similar to what I see in the DB tool.
System.out.println(timestampDate.getTime()); prints to the console 1310477167000 (which is wondering, because according to the ms to date converter i found online, it's basically 2011-07-12 13:26:07.0 (2h earlier - which somehow might be related to Polish timezone on that date)
When I perform conversion according to this code:
ukDateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
ukDateFormatter.setTimeZone(TimeZone.getTimeZone("BST"));
return ukDateFormatter.format(timestampDate.getTime());
I get 2011-07-12 19:26:07 which I can't really explain.
I was also trying this
GregorianCalendar calendar = new GregorianCalendar();
calendar.setTime(timestampDate);
calendar.setTimeZone(TimeZone.getTimeZone("BST"));
return ukDateFormatter.format(calendar.getTime());
with the same result.
Question
How to properly read DATE from Oracle DB in "timezone agnostic" format and convert it into BST?
Here's a way of doing it in the database side:
with dates as (select to_date('01/07/2016 10:39:29', 'dd/mm/yyyy hh24:mi:ss') dt from dual union all
select to_date('01/02/2016 09:18:41', 'dd/mm/yyyy hh24:mi:ss') dt from dual)
select dt,
cast(dt AS TIMESTAMP) utc_dt_ts,
from_tz(cast(dt AS TIMESTAMP), 'UTC') AT time zone 'Europe/London' dt_as_ts_bst,
cast(from_tz(cast(dt AS TIMESTAMP), 'UTC') AT time zone 'Europe/London' AS DATE) dt_at_bst
from dates;
DT UTC_DT_TS DT_AS_TS_BST DT_AT_BST
------------------- ------------------------------------------------- ------------------------------------------------- -------------------
01/07/2016 10:39:29 01-JUL-16 10.39.29.000000 01-JUL-16 11.39.29.000000 EUROPE/LONDON 01/07/2016 11:39:29
01/02/2016 09:18:41 01-FEB-16 09.18.41.000000 01-FEB-16 09.18.41.000000 EUROPE/LONDON 01/02/2016 09:18:41
The fourth column (dt_at_bst) is the one that shows how to take the date and turn it into another date at BST. It does this by first casting the date as a timestamp and then telling Oracle to treat it as a timestamp at UTC and to output the timestamp for the 'Europe/London' region. Specifying the region like this (rather than passing a specific +01:00 timezone) means that the resultant timestamp will be daylight savings aware. Specifying the region as a three letter shortcut is not advised since that may represent more than one region - e.g. BST could be British Summer Time or Bering Standard Time; both very different things!
I have assumed that by BST you mean British Summer Time, so I have specified the region for the timestamp to be moved to as Europe/London. You would need to adjust this as applicable, if you need a different timezone.
I have included a winter and a summer date in my sample data to show you the effects of casting it into BST - the summer time is expecting to be changed, and the winter time is not.
Actually it is not about Oracle, but more about Java.
First of all:
When you use
System.out.println(timestampDate)
in output you see already adjusted time to your computer time zone.
It is always adjusted when you use Date (i.e.
Calendar.getTime() or Timestamp.getTime())
Code to play with:
SimpleDateFormat dtFmt = new SimpleDateFormat("HH:mm:ss");
NumberFormat nFmt = NumberFormat.getIntegerInstance();
nFmt.setMinimumIntegerDigits(2);
long currentTimeMs = System.currentTimeMillis();
GregorianCalendar utcCalendar = new GregorianCalendar(
TimeZone.getTimeZone("UTC"));
GregorianCalendar bstCalendar = new GregorianCalendar(
TimeZone.getTimeZone("Europe/London"));
GregorianCalendar localCalendar = new GregorianCalendar();
utcCalendar.setTimeInMillis(currentTimeMs);
bstCalendar.setTimeInMillis(currentTimeMs);
localCalendar.setTimeInMillis(currentTimeMs);
System.out.println("---- milliseconds ----");
System.out.println("Current ms : " + currentTimeMs);
System.out.println("Local Calendar ms: " + localCalendar.getTimeInMillis());
System.out.println("UTC Calendar ms: " + utcCalendar.getTimeInMillis());
System.out.println("BST Calendar ms: " + bstCalendar.getTimeInMillis());
System.out.println("---- SimpleFormat Time ----");
System.out.println("Current Time: "
+ dtFmt.format(new Date(currentTimeMs)));
System.out.println("Local Time: " + dtFmt.format(localCalendar.getTime()));
System.out.println("UTC Time : " + dtFmt.format(utcCalendar.getTime()));
System.out.println("BST Time : " + dtFmt.format(bstCalendar.getTime()));
System.out.println("---- Calendar Zone Time ----");
System.out.println("Local Zone Time: "
+ nFmt.format(localCalendar.get(Calendar.HOUR_OF_DAY)) + ":"
+ nFmt.format(localCalendar.get(Calendar.MINUTE)) + ":"
+ nFmt.format(localCalendar.get(Calendar.SECOND)));
System.out.println("UTC Zone Time : "
+ nFmt.format(utcCalendar.get(Calendar.HOUR_OF_DAY)) + ":"
+ nFmt.format(utcCalendar.get(Calendar.MINUTE)) + ":"
+ nFmt.format(utcCalendar.get(Calendar.SECOND)));
System.out.println("BST Zone Time : "
+ nFmt.format(bstCalendar.get(Calendar.HOUR_OF_DAY)) + ":"
+ nFmt.format(bstCalendar.get(Calendar.MINUTE)) + ":"
+ nFmt.format(bstCalendar.get(Calendar.SECOND)));
}
As you will see each Calendar returns Time fields (HOUR_OF_DAY, MINUTE, SECOND) according to its TimeZone, not what you print or format from Calendar.getTime())
What I did, and it seems to be working for me:
ukDateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
ukDateFormatter.setTimeZone(TimeZone.getTimeZone("Europe/London"));
and performing:
Timestamp timestampDate = rs.getTimestamp(...);
DateTime dateTime = new
DateTime(timestampDate).withZoneRetainFields(DateTimeZone.UTC);
System.out.println(ukDateFormatter.format(dateTime.getMillis()));
prints:
2011-07-12 16:26:07 from the input 2011-07-12 15:26:07
Why happened here?
What was so problematic here, is that rs.getTimestamp(...) was returning the date from the database "as it is" (since DATE column type doesn't preserve the timezone) implicitly but was adding some information about my local timezone - which I didn't wanted.
Easiest solution was to use joda and create new object, retaining values, but changing timezone to UTC. From that point conversion with SimpleDateFormat is quite straightforward.
I can't seem to figure out why joda time is updating the time and offset hours after daylight saving time, but java time doesn't.
DateTime dateTime = new DateTime("2016-04-05T10:06:21.636-05:00").withDayOfWeek(5);
TemporalField dayOfWeek = WeekFields.ISO.dayOfWeek();
OffsetDateTime offsetDateTime = OffsetDateTime.parse("2016-04-05T10:06:21.636-05:00").with(dayOfWeek, 5);
ZonedDateTime zonedDateTime = ZonedDateTime.parse("2016-04-05T10:06:21.636-05:00").with(dayOfWeek, 5);
System.out.println("dateTime: " + dateTime); // 2016-04-08T11:06:21.636-04:00
System.out.println("offsetDateTime: " + offsetDateTime); // 2016-04-08T10:06:21.636-05:00
System.out.println("zonedDateTime: " + zonedDateTime); // 2016-04-08T10:06:21.636-05:00
Time zone versus offset
You did not provide a time zone, only an offset to both, the offset date time and the zoned date time instances. In both cases, they don't have any clue about daylight saving times as this is an information of the time zone.
So you must provide a time zone when constructing the zoned date time object, and it then it works as you expect.
I have a Java app that needs to be cognizant of the time zone. When I take a Unix epoch time and try to convert it into a timestamp to use for an Oracle SQL call, it is getting the correct timezone, but the timezone "useDaylightTime" value is not correct, i.e., it is currently returning "true", when we are NOT in DST (I am in Florida in TZ "America/New_York").
This is running on Red Hat Linux Enterprise 6, and as far as I can tell, it is correctly set up for the timezone, e.g. 'date' returns:
Wed Nov 28 12:30:12 EST 2012
I can also see, using the 'zdump' utility, that the current value for 'isdst' is 0.
My Java version is 1.6.0_31.
I have Googled this and seen the numerous issues this has caused, but many of them simply say to set the TZ manually, but my issue is not the TZ, but the fact that the default TZ has the "isDaylight" set to 'true'. I believe this is causing my query to return data that is one hour off (I can see that it is).
Here is a simple code piece I have run to try and reproduce this in the simplest way possible:
public class TZdefault {
public static void main(String[] args) throws IOException {
long startTime = System.currentTimeMillis()/1000;
Calendar start = Calendar.getInstance();
start.setTimeInMillis(startTime);
start.setTimeZone(TimeZone.getDefault());
System.out.println("Start UTC: " + start + "ms: " + start.getTimeInMillis());
System.out.println("use daylight: " + start.getTimeZone().useDaylightTime());
} // end main
} // end class
One final thing. If in my code I set the TZ to "EST", it of course does return a TZ with 'isDaylight' set to False. But that is not a good solution.
I wanted to add some more detail that I had been hoping to hide.
I have records in an Oracle 11g database that use TIMESTAMP with TIMEZONE fields. I am simply doing JDBC queries where two of the parameters are using BETWEEN a start timestamp and end timestamp.
When I query this table, I am using a prepared statement that is using a Calendar entry, the sole purpose of which was to try and manipulate the timezone. The bottom line is that I am doing a pstmt.setTimestamp() call using the 'getTimeInMillis' method for the start and end time after the "default" timezone was applied. The log output shows that in fact it is putting in the correct milliseconds, but the returned SQL results are clearly off by one hour exactly!
I am still trying to verify that there is not an issue on the data insertion side as well.
But I have a lot of debug information, and it looks like I am asking for the correct time in my JDBC query.
the timezone useDaylightTime value is not correct, i.e., it is currently returning "true", when we are NOT in DST
I think you're confusing useDaylightTime with inDaylightTime. The former tells you whether there is a transition between daylight time and standard time in the future, not which side of that transition you're on. For example, it returns false for Chinese time zones because China does not adjust for daylight savings time, but it returns true for most US time zones because most US states (except Arizona) do observe daylight savings time.
inDaylightTime
public abstract boolean inDaylightTime(Date date)
Queries if the given date is in Daylight Saving Time in this time zone.
vs
useDaylightTime
public abstract boolean useDaylightTime()
Queries if this TimeZone uses Daylight Saving Time.
If an underlying TimeZone implementation subclass supports historical and future Daylight Saving Time schedule changes, this method refers to the last known Daylight Saving Time rule that can be a future prediction and may not be the same as the current rule. Consider calling observesDaylightTime() if the current rule should also be taken into account.
If you want to disable daylight saving calculation, then you must set your timezone to EST. Else otherwise time will be calculated based on default time zone set for AMERICA/NEW_YORK
TimeZone zoneEST = TimeZone.getTimeZone("EST");
System.out.println(zoneEST.getDSTSavings()); //0 hour
System.out.println(zoneEST.getRawOffset()); //5 hour
TimeZone.setDefault(zoneEST);
System.out.println("");
TimeZone zoneNY = TimeZone.getTimeZone("America/New_York");
System.out.println(zoneNY.getDSTSavings()); // 1 hour
System.out.println(zoneNY.getRawOffset()); // 5 hour
I have found a way to ensure the daylight saving is ignored
TimeZone tz = TimeZone.getTimeZone("GMT");
TimeZone.setDefault(tz);
GregorianCalendar calendar;
calendar = new GregorianCalendar();
Set the timezone before you create your GregorianCalendar object
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
public class TimeZoneTest {
public static void main(String[] argv) throws ParseException {
SimpleDateFormat formatter = new SimpleDateFormat("dd-M-yyyy hh:mm:ss a");
String dateInString = "22-01-2015 10:15:55 AM";
Date date = formatter.parse(dateInString);
TimeZone tz = TimeZone.getDefault();
// From TimeZone Asia/Singapore
System.out.println("TimeZone : " + tz.getID() + " - " + tz.getDisplayName());
System.out.println("TimeZone : " + tz);
System.out.println("Date : " + formatter.format(date));
// To TimeZone America/New_York
SimpleDateFormat sdfAmerica = new SimpleDateFormat("dd-M-yyyy hh:mm:ss a");
TimeZone tzInAmerica = TimeZone.getTimeZone("America/New_York");
sdfAmerica.setTimeZone(tzInAmerica);
String sDateInAmerica = sdfAmerica.format(date); // Convert to String first
Date dateInAmerica = formatter.parse(sDateInAmerica);
System.out.println("\nTimeZone : " + tzInAmerica.getID() +
" - " + tzInAmerica.getDisplayName());
System.out.println("TimeZone : " + tzInAmerica);
System.out.println("Date (String) : " + sDateInAmerica);
System.out.println("Date (Object) : " + formatter.format(dateInAmerica));
}
}