I've found an issue about Java DST change.
When the exact moment that DST(Daylight Saving time) happened, the certain time may appears two times.
For example, for the timezone America/Sao_Paulo, it changed backward one hour from Sunday, 21 February 2016, 00:00:00 to Saturday, 20 February 2016, 23:00:00.
The time 23:00 appears twice that day.
In my use case, I'd like to take the second time. For example, when I want the data of this day, I actually mean this day completely (even if maybe it contains 25 hours).
But in my opinion, it seems that Java take the first time of this moment.
Example:
#Test
public void testDSTChange() throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
TimeZone tz = TimeZone.getTimeZone("America/Sao_Paulo");
long dateInput = sdf.parse("2016-02-20T23:00:00").getTime();
long dateUTC = dateInput - tz.getOffset(dateInput);
long dateInput2 = sdf.parse("2016-02-21T02:00:00").getTime();
long dateUTC2 = dateInput2 - tz.getOffset(dateInput2);
System.out.println("Difference: " + (dateUTC2 - dateUTC) / 3600 / 1000);
}
In this example, it shows that the difference between these two dates is 4 hours, not the 3 hours that I expected. So I wonder that if there's any other way to do it ?
Well, the time at the DST change will occur twice when the DST changes backward, there is no way around it. There may be another way around, but let me make a suggestion, internally use UTC time (GMT) it has no DST internally, convert to local time (wall clock time) on output for display purposes only. You will never have to worry about it again.
In my use case, I'd like to take the second time. For example, when I want the data of this day, I actually mean this day completely (even if maybe it contains 25 hours).
From this statement, I'll assume you are actually doing some kind of range query to gather data over the entire day. The best way to approach this is to query from the start of the day inclusive, to the start of the next day exclusive. In other words: startOfDay <= dataPoint(s) < startOfNextDay
This works well in your scenario, because the clock doesn't actually hit 00:00 until the next day has started. It ticks like this:
23:58
23:59
23:00
23:01
...
23:58
23:59
00:00 <--- start of next day
So use half-open intervals, and it should just work out.
Do be careful in the spring transition though, when the start of the day is 01:00, not 00:00. :)
Also, Jon's comment about using Java 8's new time APIs, or Joda-Time are spot-on. They both offer you better control over this situation. Older Java APIs do not.
LocalDateTime l1 = LocalDateTime.of(2016, Month.FEBRUARY, 20, 23, 0, 0);
ZoneId id = ZoneId.of("America/Sao_Paulo");
ZoneOffset offset = ZoneOffset.of("-08:00");
ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(l1, offset, id);
long el1= zonedDateTime.toEpochSecond();
LocalDateTime l2 = LocalDateTime.of(2016, Month.FEBRUARY, 21, 2, 0, 0);
ZonedDateTime zonedDateTime1 = ZonedDateTime.ofInstant(l2, offset, id);;
long el2= zonedDateTime1.toEpochSecond();
System.out.println("Difference: " + (el2-el1 ) / 3600 );
====================================
on console it prints
Difference: 3
Related
I need to know if a date match a periodicity, for example, periodicity is 1 hour, and date that user gives is 13/09/2021 23:00, the inicial that my java code should take is 13/09/2021 00:00 and check how many times have to add 1 hour to get the date 13/09/2021 23:00.
The idea now is made a loop and add 1hour to the date and save in an array, then check if the date is inside the array and the position. Is there any other way?
If I understand your question correctly, you just want to calculate how many hours there are between two dates. For that, it's cleaner to use the built-in java.time classes. You can read the two dates into LocalDateTime objects and calculate the time span between them with ChronoUnit.HOURS:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm");
LocalDateTime start = LocalDateTime.parse("13/09/2021 00:00", formatter);
LocalDateTime end = LocalDateTime.parse("13/09/2021 23:00", formatter);
long hours = ChronoUnit.HOURS.between(start, end);
The result will be 23.
For various other units (minutes for example), there's ChronoUnit.MINUTES. Have a look at the documentation. There are a lot of different units to choose from.
I am adding a couple of minor refinements compared to the correct answer by QBrute.
The periodicity can be any amount of time in hours, minutes and seconds.
I am taking time zone into account so we also get correct results across summer time transitions (spring forward and fall back) and other time anomalies.
If there isn’t a whole number of periodicities, I am rounding up to be sure to have at least enough.
ZoneId userTimeZone = ZoneId.of("Africa/Dar_es_Salaam");
Duration periodicity = Duration.ofMinutes(5);
ZonedDateTime userTime = ZonedDateTime.of(2021, 9, 13, 23, 0, 0, 0, userTimeZone);
ZonedDateTime initialTime = ZonedDateTime.of(2021, 9, 13, 0, 0, 0, 0, userTimeZone);
Duration timeBetween = Duration.between(initialTime, userTime);
long numberOfPeriodicities = timeBetween.dividedBy(periodicity);
// Has truncation occurred?
if (initialTime.plus(periodicity.multipliedBy(numberOfPeriodicities)).isBefore(userTime)) {
// Need one more periodicity
numberOfPeriodicities++;
}
System.out.println(numberOfPeriodicities);
Output is:
276
If you want a periodicity of 7.5 minutes, specify Duration.ofMinutes(7).plusSeconds(30). The Duration.dividedBy(Duration) method that I am using was introduced in Java 9.
Link: Oracle tutorial: Date Time explaining how to use java.time.
I'm trying to retrieve the Unix timestamp for midnight of the current day. Timezone not relevant.
I'm looking for something like: 1625716800
Every tutorial I've found is for retrieving formatted strings, such as: "Thu Jul 08 2021 04:00:00"
It needs to be midnight. Not just the current seconds or milliseconds.
Any advice is appreciated.
Many thanks.
I'm trying to retrieve the Unix timestamp for midnight of the current day. Timezone not relevant.
This is impossible. The concept of 'a day' does not exist in unix timestamp space. This space just ticks away 1 unit every millisecond that passes, since some universally defined epoch (in unix space, that epoch occurred at that instant in time when someone in greenwich, UK, would tell you that right this very moment it is jan 1st, 1970, midnight). There's no such thing as days in this system, no such thing as dates. Just 'millis since the epoch', and that's all you get.
If you want concepts like 'day', 'month', or 'hour', you simply can't do that in this space; these are human concepts and you can't know what the right answer is unless you involve a political unit which decides how to translate such an epoch to an actual 'it is this date in this month in this year, this hour, this minute, etc'. Political units have timezones, daylight savings times, and more - and they all effect when 'midnight' might be.
When it is midnight in Europe/Amsterdam, it's not midnight in Asia/Singapore. Hopefully this helps you understand that the concept 'at midnight' doesn't make sense unless you add to this some notion of 'where'. Could be 'whereever the system thinks it is', (e.g. platform default timezone), could be at the essentially fictional UTC timezone, could be at some specific timezone, could be at a user-specified timezone. But it's gotta be in some timezone.
Furthermore, 'current day' is ambiguous. That, too, just isn't a thing unless timezones are involved. Current day where? Amsterdam? Singapore? Los Angeles? As I said, millis-since-epoch has no idea what 'day' means. It just knows when the epoch was and what a millisecond is, and it knows nothing more.
In fact, 'midnight' doesn't work at all. It may not exist. Most places will advance the clock by an hour, if they do so at all, at 2 AM, but not all zones do. Thus, midnight may simply be a time that never was. One moment in time it is 23:59:59 on March 22nd. The next moment in time, it is 01:00:00 on March 23rd. Presumably what you actually mean is 'start of day', as in, the very first instant in time that can be called 'the date is now X' in the decided time zone. Which usually is 00:00:00 but there are extremely exotic cases where it's not. The problem is, 'extremely exotic' is not equal to 'never happens'.
Let's assume the zone relevant for 'current day' and 'at the start of this day' are all from the same zone, then what you're looking for:
ZoneId zone = ....;
ZonedDateTime zdt = ZonedDateTime.now(zone);
zdt = zdt.toLocalDate().atStartOfDay(zone);
long v = zdt.toInstant().toEpochMilli();
Where zone can be many things. For example, ZoneOffset.UTC, or ZoneId.systemDefault(), or ZoneId.of("Europe/Amsterdam") for example.
Let's give it a whirl with ZoneOffset.UTC:
ZoneId zone = ZoneOffset.UTC;
ZonedDateTime zdt = ZonedDateTime.now(zone);
zdt = zdt.toLocalDate().atStartOfDay(zone);
long v = zdt.toInstant().toEpochMilli();
System.out.println(v);
See this code run live at IdeOne.com:
1625702400000
import java.util.Calendar;
class test{
public static void main(String[] args) {
Calendar cal = Calendar.getInstance();
cal.set(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DATE), 0, 0, 0);
System.out.println(cal.getTimeInMillis());
}
}
// someTime is epoch in millis (UTC)
final long timeNow = new Date().getTime();
final long midnight = timeNow - timeNow % (3600 * 24 * 1000L);
final long yesterdayMidnight = midnight - (3600 * 24 * 1000L);
// check if same day.
if (someTime >= midnight)
// do something
// check if yesterday
if (someTime >= yesterdayMidnight)
Edited: My purpose is to check whether someTime is in the same day or in the previous day without doing too much heavyweight stuff.
Does this account for day light savings and why? If not, what's the simplest logic?
Your current code doesn't do anything with the local time zone - everything is in UTC, effectively (certainly in terms of your code, which is dealing in "milliseconds since the Unix epoch").
If you want to make your code time-zone-sensitive, you should use (in order of preference):
Java 8's java.time (look at ZonedDateTime and Clock for example)
Joda Time
java.util.Calendar with java.util.TimeZone
Use higher-level abstractions where possible - your code should do as little low-level manipulation of time as possible.
EDIT: Now that we know the purpose, here's an example implementation in Joda Time:
public void calculate(Instant now, Instant then, DateTimeZone zone) {
LocalDate today = new LocalDate(now, zone);
LocalDate otherDay = new LocalDate(then, zone);
if (otherDay.equals(today)) {
// Today day
} else if (otherDay.equals(today.minusDays(1)) {
// Yesterday
} else {
// Neither today nor yesterday
}
}
Note how there's nothing low level here - we're just working out which date each value (now and then) falls in within the given time zone, and then comparing those.
Your check will fail in some cases having in mind daylight savings. Let's assume it is now 5 o' clock on the day when daylight saving happens and at 3 o'clock we've switched the clock forward. Therefor only 4 hours have passed since midnight but it is in fact 5. So midnight in your code will be a time 5 hours ago. This means that if someTime is between 5 hours ago and 4 hours ago(e.g. 4 hours and a half ago) when it's in fact been yesterday your algorithm will report it has been today.
That does not seem to be correct from daylight savings point. First, what's the timezone of someTime date? What about the days/nights(in fact) when the daylight savings take place (one hour +/-)?
If you use Joda lib there is a convenient method DateTime.isBefore() that will return whether one date is before another.
I have the following problem using Joda-Time for parsing and producing date and time around Daylight Saving Time (DST) hours. Here is an example (please, note that March 30th 2008 is Daylight Saving change in Italy):
DateTimeFormatter dtf = DateTimeFormat.forPattern("dd/MM/yyyy HH:mm:ss");
DateTime x = dtf.parseDateTime("30/03/2008 03:00:00");
int h = x.getHourOfDay();
System.out.println(h);
System.out.println(x.toString("dd/MM/yyyy HH:mm:ss"));
DateTime y = x.toDateMidnight().toDateTime().plusHours(h);
System.out.println(y.getHourOfDay());
System.out.println(y.toString("dd/MM/yyyy HH:mm:ss"));
I get the following output:
3
30/03/2008 03:00:00
4
30/03/2008 04:00:00
When i parse hour I get hour is 3. In my data structure I save the day storing midnight time, and then I have some value for each hour of the day (0-23). Then, when I write out the date, I re-compute the full date time making midnight plus hour. When I sum 3 hours to my midnight I get 04:00:00! And if I parse it again, I get hour 4!
Where is my mistake? Is there some way to get hour 2 when I parse or get hour three when I print out?
I have also tried to build output by hand:
String.format("%s %02d:00:00", date.toString("dd/MM/yyyy"), h);
but in this case for hour 2, I produce 30/03/2008 02:00:00 which is not a valid date (since hour 2 does not exist) and cannot be parsed any more.
Thank you in advance for your help.
Filippo
When I sum 3 hours to my midnight I get 04:00:00! And if I parse it again, I get hour 4! Where is my mistake?
You mentioned already that this date is exactly when the time changes. So there is no mistake. March 30, 2010 00:00 CEST (the timezone in Italy) is precisely speaking March 29, 2010 23:00 UTC. When you add 3 hours, you will get March 30, 2010 02:00 UTC. But this is post the moment, that we switch times (which happens on 01:00 UTC), so when you convert time to local timezone you get March 30, 04:00. That's correct behavior.
Is there some way to get hour 2 when I parse or get hour three when I print out?
No, because March 30, 2010 02:00 CEST does not exist. Precisely at March 30, 2010 01:00 UTC we switch time from +1 hour to +2 hours versus UTC, so March 30, 2010 00:59 UTC is March 30, 2010: 01:59 CEST, but March 30, 2010 01:00 UTC become March 30, 2010 03:00 CEST. No 02:xx hour exist on that particular date.
BTW. In a week you can expect another "fun". Can you tell what date in UTC this refers to:
October 31, 2010 02:15 CEST ?
Well, the funny part is, we do not know. It could be either 0ctober 31, 2010 00:15 UTC (before actual time switch) or October 31, 2010 01:15 UTC (after the switch).
That's exactly why you should always store date and times in relation to UTC and convert them to local time zone before displaying, otherwise you risk an ambiguity.
HTH.
The data structure you are saving your data is not very optimal for the days with daylight saving time. Your day in this particular day should only have 23 hours.
If you do:
DateTimeFormatter dtf = DateTimeFormat.forPattern("dd/MM/yyyy HH:mm:ss").withLocale(Locale.US);
DateTime x = dtf.parseDateTime("30/03/2008 00:00:00");
DateTimeFormatter parser = DateTimeFormat.fullDateTime();
System.out.println("Start:"+parser.print(x));
DateTime y = x.plusHours(4);
System.out.println("After add of 4:"+parser.print(y));
You get the expected result, that the time is 05:00.
I recommend that you change the way you store your day and use a date. If not, you must handle daylight saving time when storing the hour of day.
You might do something like this:
In the case where we move the time forward one hour, as this case, you must store 4 and not 5 as the time for 5. And when you calculate the time, you should use the plusHours() method to get the actual time. I think you might get away with something like:
public class DateTest {
private static final int HOUR_TO_TEST = 2;
public static void main(String[] args) {
DateTimeFormatter dtf = DateTimeFormat.forPattern("dd/MM/yyyy HH:mm:ss");
DateTime startOfDay = dtf.parseDateTime("30/03/2008 00:00:00");
/* Obtained from new DateTime() in code in practice */
DateTime actualTimeWhenStoring = startOfDay.plusHours(HOUR_TO_TEST);
int hourOfDay = actualTimeWhenStoring.getHourOfDay();
int hourOffset = startOfDay.plusHours(hourOfDay).getHourOfDay();
System.out.println("Hour of day:" + hourOfDay);
System.out.println("Offset hour:" + hourOffset);
int timeToSave = hourOfDay;
if (hourOffset != hourOfDay) {
timeToSave = (hourOfDay + (hourOfDay - hourOffset));
}
System.out.println("Time to save:" + timeToSave);
/* When obtaining from db: */
DateTime recalculatedTime = startOfDay.plusHours(timeToSave);
System.out.println("Hour of time 'read' from db:" + recalculatedTime.getHourOfDay());
}
}
...or basicly something like that. I'd write a test for it if you choose for going down this route. You can change the HOUR_TO_TEST to see that it moves passed the daylight saving time.
Building on the correct answers by Paweł Dyda & Knubo…
ISO 8601 For String Format
You should never store (serialize) a date-time as a string in the format you mentioned: "30/03/2008 03:00:00". Problems:
Omitted time zone.
Day, Month, Year order is ambiguous.
Should have been translated to UTC time.
If you must serialize a date-time value to text, use a reliable format. The obvious choice is the ISO 8601 standard format. Even better is converting the local time to UTC (Zulu) time zone and then out to ISO 8601 format. Like this: 2013-11-01T04:48:53.044Z
No Midnight
The midnight methods in Joda-Time are deprecated in favor of the Joda-Time method withTimeAtStartOfDay() (see doc). Some days do not have a midnight.
Example Code in Joda-Time 2.3
Some comments about this source code:
// © 2013 Basil Bourque. This source code may be used freely forevery by anyone taking full responsibility for doing so.
// Joda-Time - The popular alternative to Sun/Oracle's notoriously bad date, time, and calendar classes bundled with Java 7 and earlier.
// http://www.joda.org/joda-time/
// Joda-Time will become outmoded by the JSR 310 Date and Time API introduced in Java 8.
// JSR 310 was inspired by Joda-Time but is not directly based on it.
// http://jcp.org/en/jsr/detail?id=310
// By default, Joda-Time produces strings in the standard ISO 8601 format.
// https://en.wikipedia.org/wiki/ISO_8601
Example showing 23 hours in the day of DST (Daylight Saving Time) in Rome Italy, while the day after has 24 hours. Note that the time zone (for Rome) is specified.
// Time Zone list: http://joda-time.sourceforge.net/timezones.html
org.joda.time.DateTimeZone romeTimeZone = org.joda.time.DateTimeZone.forID("Europe/Rome");
org.joda.time.DateTime dayOfDstChange = new org.joda.time.DateTime( 2008, 3, 30, 0, 0, romeTimeZone ) ; // Day when DST
org.joda.time.DateTime dayAfter = dayOfDstChange.plusDays(1);
// How many hours in this day? Should be 23 rather than 24 on day of Daylight Saving Time "springing ahead" to lose one hour.
org.joda.time.Hours hoursObjectForDay = org.joda.time.Hours.hoursBetween(dayOfDstChange.withTimeAtStartOfDay(), dayAfter.withTimeAtStartOfDay());
System.out.println( "Expect 23 hours, got: " + hoursObjectForDay.getHours() ); // Extract an int from object.
// What time is 3 hours after midnight on day of DST change?
org.joda.time.DateTime threeHoursAfterMidnightOnDayOfDst = dayOfDstChange.withTimeAtStartOfDay().plusHours(3);
System.out.println( "Expect 4 AM (04:00) for threeHoursAfterMidnightOnDayOfDst: " + threeHoursAfterMidnightOnDayOfDst );
// What time is 3 hours after midnight on day _after_ DST change?
org.joda.time.DateTime threeHoursAfterMidnightOnDayAfterDst = dayAfter.withTimeAtStartOfDay().plusHours(3);
System.out.println( "Expect 3 AM (03:00) for threeHoursAfterMidnightOnDayAfterDst: " + threeHoursAfterMidnightOnDayAfterDst );
Example of storing a date-time by first translating to UTC. Then upon restoring the date-time object, adjust to the desired time zone.
// Serialize DateTime object to text.
org.joda.time.DateTimeZone romeTimeZone = org.joda.time.DateTimeZone.forID("Europe/Rome");
org.joda.time.DateTime dayOfDstChangeAtThreeHoursAfterMidnight = new org.joda.time.DateTime( 2008, 3, 30, 0, 0, romeTimeZone ).withTimeAtStartOfDay().plusHours(3);
System.out.println("dayOfDstChangeAtThreeHoursAfterMidnight: " + dayOfDstChangeAtThreeHoursAfterMidnight);
// Usually best to first change to UTC (Zulu) time when serializing.
String dateTimeSerialized = dayOfDstChangeAtThreeHoursAfterMidnight.toDateTime( org.joda.time.DateTimeZone.UTC ).toString();
System.out.println( "dateTimeBeingSerialized: " + dateTimeSerialized );
// Restore
org.joda.time.DateTime restoredDateTime = org.joda.time.DateTime.parse( dateTimeSerialized );
System.out.println( "restoredDateTime: " + restoredDateTime );
// Adjust to Rome Italy time zone.
org.joda.time.DateTime restoredDateTimeAdjustedToRomeItaly = restoredDateTime.toDateTime(romeTimeZone);
System.out.println( "restoredDateTimeAdjustedToRomeItaly: " + restoredDateTimeAdjustedToRomeItaly );
When run:
dayOfDstChangeAtThreeHoursAfterMidnight: 2008-03-30T04:00:00.000+02:00
dateTimeBeingSerialized: 2008-03-30T02:00:00.000Z
restoredDateTime: 2008-03-30T02:00:00.000Z
restoredDateTimeAdjustedToRomeItaly: 2008-03-30T04:00:00.000+02:00
How can I get the current local wall clock time (in number of millis since 1 Jan 1970) in London? Since my application can run on a server in any location, I think I need to use a TimeZone of "Europe/London". I also need to take Daylight Savings into account i.e. the application should add an hour during the "summer".
I would prefer to use the standard java.util libraries.
Is this correct?
TimeZone tz = TimeZone.getTimeZone("Europe/London") ;
Calendar cal = Calendar.getInstance(tz);
return cal.getTime().getTime() + tz.getDSTSavings();
Thanks
I'm not sure what this quantity represents, since the "number of millis since 1 Jan 1970" doesn't vary based on location or daylight saving. But, perhaps this calculation is useful to you:
TimeZone london = TimeZone.getTimeZone("Europe/London");
long now = System.currentTimeMillis();
return now + london.getOffset(now);
Most applications are better served using either UTC time or local time; this is really neither. You can get the UTC time and time in a particular zone like this:
Instant now = Instant.now(); /* UTC time */
ZonedDateTime local = now.atZone(ZoneId.of("Europe/London"));
Others have said that it may well not be a good idea to do this - I believe it depends on your situation, but using UTC is certainly something to consider.
However, I think you've missed something here: the number of seconds which have occurred since January 1st 1970 UTC (which is how the Unix epoch is always defined - and is actually the same as in London, as the offset on that date was 0) is obtainable with any of these expressions:
System.currentTimeMillis()
new Date().getTime()
Calendar.getInstance().getTime().getTime()
If you think about it, the number of milliseconds since that particular instant doesn't change depending on which time zone you're in.
Oh, and the normal suggestion - for a much better date and time API, see Joda Time.
To get the current time in London:
SimpleDateFormat f = new SimpleDateFormat("dd MMM yyyy HH:mm:ss z");
f.setTimeZone(TimeZone.getTimeZone("Europe/London"));
System.out.println(f.format(GregorianCalendar.getInstance().getTime()));