I need to compare time zones such that Asia/Singapore < UTC < Pacific/Honolulu.
I'm working with java.util.TimeZone (which doesn't implement Comparable).
My search for an existing implementation was unsuccessful because of the overwhelming number of questions about comparing dates with different time zones.
Question: What is a correct implementation of Comparator<TimeZone> that will solve this problem (and what makes it better than other solutions, if applicable)?
Note that I'm not able to use Joda Time for this problem, so "use Joda Time" is not a valid answer.
Edit for clarity
The < notation above was not well defined. My particular use case only requires a naive "geographical" ordering from east to west. As the comments have pointed out, a more advanced and generalizable solution would take into account temporal factors like daylight savings time and historical GMT offset changes. So I think there are two orderings we can consider, each requiring a different Comparator<TimeZone> implementation:
Strictly geographical (current UTC) - addressed by my answer.
Sensitive to local or civil time changes - addressed by rgettman's answer.
I rolled my own Comparator<TimeZone> implementation using getRawOffset for the comparison:
#Override
public int compare(TimeZone tz1, TimeZone tz2) {
return tz2.getRawOffset() - tz1.getRawOffset();
}
It seems to have passed a quick test:
final List<TimeZone> timeZones = Arrays.asList(
TimeZone.getTimeZone("UTC"),
TimeZone.getTimeZone("America/Los_Angeles"),
TimeZone.getTimeZone("America/New_York"),
TimeZone.getTimeZone("Pacific/Honolulu"),
TimeZone.getTimeZone("Asia/Singapore")
);
final List<TimeZone> expectedOrder = Arrays.asList(
TimeZone.getTimeZone("Asia/Singapore"),
TimeZone.getTimeZone("UTC"),
TimeZone.getTimeZone("America/New_York"),
TimeZone.getTimeZone("America/Los_Angeles"),
TimeZone.getTimeZone("Pacific/Honolulu")
);
Collections.sort(timeZones, new Comparator<TimeZone>() {
#Override
public int compare(TimeZone tz1, TimeZone tz2) {
return tz2.getRawOffset() - tz1.getRawOffset();
}
});
//Impl note: see AbstractList.equals
System.out.println(timeZones.equals(expectedOrder)); //true
But I'm still wondering whether there are pitfalls to this solution and/or if there's something preferable.
One might be able to create a Comparator<TimeZone> that takes into account time zone differences. The TimeZone may or may not obvserve daylight savings time, which would adjust the raw offset, thus messing up raw-offset-only comparisons. The TimeZone class seems to support the adjustment based on the 2 getOffset methods, but they need a reference date. How about:
public class TimeZoneComparator implements Comparator<TimeZone>
{
private long date;
public TimeZoneComparator(long date)
{
this.date = date;
}
public int compare(TimeZone tz1, TimeZone tz2)
{
return tz2.getOffset(this.date) - tz2.getOffset(this.date);
}
}
Timezones are purely political, so any use of them that is non-conforming will cause LOTS of problems for users, depending on what the app does and who needs it or uses it. You question would be better asked by explaining why you need them ordered like that. There are adjacent timezones where one uses DST the other does not. So 60% of the year, the TZ1 == TZ2, the other 40% TZ1 < TZ2. Or whatever the case may be.
There are geographic (lat long) timezone data sets, and web sites to query for timezone. Even current DST settings. So you may have to settle for a data set that you need to update frequently at least yearly. Or web access.
You probably should not assign magnitude to them. Only geographic ordering - by longitude.
First of if you could tell us what you are trying to do it'd be great. And the answer is not: strict a>b>c order based on local time. I coded calendrics for a while, so I actually used to know this stuff.
What explictly do believe requires this kind of ordering?
Related
Here's my code, I'm trying to my 'calculateButton' variable to understand the current local time so that it can use it in the math equation. Thanks for taking a look. By the current time, I mean the time currently being shown on my computer or the time zone I'm in.
private void bednowButtonActionPerformed(java.awt.event.ActionEvent evt) {
//variable declaration
String calculateButton, outputTime;
double outputtimeConstant;
calculateButton= "";
//math
outputtimeConstant = +104;
outputTime = calculateButton + outputtimeConstant;
//output
bednowOutput.setText(outputTime) ;
}
To get the current date and time just use static method now() of LocalDateTime or ZonedDateTime classes (you mentioned time zone here, so I'm specifying this implementation as well). For some math with these objects afterwards just use their instance methods, like plusDays, plusHours... and so on. I'd recommend reading documentation of these classes, they are quite useful.
If you want to have a String representation simply call toString() method.
How can I obtain the current TAI time in milliseconds in Linux using either Java or C++?
The reason I need this is to be able to accurately take timestamps over a long period of time (on the order of years) and still be able to compare them, without worrying about leap seconds. It is possible for multiple measurements to take place during a leap second and all measurements need to be unambiguous, monotonically increasing, and linearly increasing. This will be a dedicated Linux server. This is for a scientific project which needs precision of about .5 seconds.
I do not currently wish to invest in a GPS timekeeper and hope to use NTP to pool.ntp.org in order to keep the system clock on track.
I have looked into the following solutions:
Java 8 or the ThreeTen Project
The only way to obtain a TAIInstant is to use an Instant and then convert it which, according to the specs, "Conversion from an Instant will not be completely accurate near a leap second in accordance with UTC-SLS." That in and of itself is not a big deal (in fact, using UTC-SLS would also be acceptable). However, using now() in the Instant class also seems to just be a wrapper for System.currentTimeMillis(), which makes me think that during the leap second, the time will still be ambiguous and the project will not actually give me TAI time. The Java 8 specifications also state:
Implementations of the Java time-scale using the JSR-310 API are not
required to provide any clock that is sub-second accurate, or that
progresses monotonically or smoothly. Implementations are therefore
not required to actually perform the UTC-SLS slew or to otherwise be
aware of leap seconds.
Using a right/? timezone
This seems like it would work, however I am not sure if the implementation is smart enough to continue working during a leap second or if System.currentTimeMillis() would even give TAI time. In other words, would the underlying implementation still use UTC, thus giving an ambiguous time during the leap second which is then converted to TAI, or does using a right/ timezone actually work with TAI using System.currentTimeMillis() always (ie even during leap second)?
Using CLOCK_TAI
I tried using CLOCK_TAI in the Linux kernel but found it to be completely identical to CLOCK_REALTIME in my test:
Code:
#include <iostream>
#include <time.h>
long sec(int clock)
{
struct timespec gettime_now;
clock_gettime(clock, &gettime_now);
return gettime_now.tv_sec;
}
int main()
{
std::cout << sec(0) << std::endl; // CLOCK_REALTIME
std::cout << sec(1) << std::endl; // CLOCK_MONOTONIC
std::cout << sec(11) << std::endl; // CLOCK_TAI
return 0;
}
The output was simply:
1427744797
6896
1427744797
Using CLOCK_MONOTONIC
The problem with this is that the timestamps need to remain valid and comparable even if the computer restarts.
CLOCK_REALTIME and CLOCK_TAI return the same because the kernel parameter tai_offset is zero.
Check by using adjtimex(timex tmx) and read the value. I think that ntpd will set it if it is new enough (>4.2.6) and has a leap second file. It may also be able to get it from upstream servers but I haven't been able to verify. The call adjtimex() can set tai_offset manually when run as root. You will need a new-ish man page for adjtimex to see the parameters to set. My debian man page was too old but the command worked.
In addition to the correct accepted answer I would also mention the free Java library Time4J (min version v4.1) as possible solution because
I have written it to fill a gap in Java world (java.time cannot do all),
other answers given so far only talk about C++ (but you also asked for Java),
it works according to the same principles described by #user3427419.
It uses a monotonic clock based on System.nanoTime() but even allows custom implementations via the interface TickProvider. For the purpose of calibration, you can either use net.time4j.SystemClock.MONOTONIC, or you use an SNTP-clock named SntpConnector which just needs some simple configuration to connect to any NTP-time-server you want. And thanks to the built-in leap-second-table Time4J can even show you the announced leap second at the end of this month - in ISO-8601-notation or even as formatted local timestamp string in any timezone (using i18n-module).
A recalibration (in case of NTP - reconnect) of the clocks is possible meaning the clocks can be adapted to intermediate time adjustments (although I strongly recommend not to do it during your measurements or during a leap second). Although such a reconnect of an SNTP clock would normally cause the time stepping back in some cases Time4J tries to apply a smoothing algorithm (if activated in clock configuration) to ensure monotone behaviour. Detailed documentation is available online.
Example:
// Step 0: configure your clock
String ntpServer = "ptbtime1.ptb.de";
SntpConnector clock = new SntpConnector(ntpServer);
// Step 1: Timestamp start of the program and associate it with a counter
clock.connect();
// Step 2: Use the counter for sequential measurements at fixed intervals
Moment m = clock.currentTime();
System.out.println(m); // possible output = 2015-06-30T23:59:60,123456789Z
// Step 3: Timestamp new counter value(s) as necessary to keep your data adequately synced
clock.connect();
I doubt if any C++-based solution is more simple. More code demonstrations can also be studied on DZone.
Update (answer to question in comment):
A slightly simplified solution how to automatically download the given IETF-resource for new leap seconds and to translate it into a Time4J-specific format might look like this:
URL url = new URL("https://www.ietf.org/timezones/data/leap-seconds.list");
BufferedReader br =
new BufferedReader(
new InputStreamReader(url.openStream(), "US-ASCII"));
String line;
PlainDate expires = null;
Moment ntpEpoch = PlainTimestamp.of(1900, 1, 1, 0, 0).atUTC();
List<PlainDate> events = new ArrayList<PlainDate>();
try {
while ((line = br.readLine()) != null) {
if (line.startsWith("##")) {
long expraw = Long.parseLong(line.substring(2).trim());
expires = ntpEpoch.plus(
expraw, TimeUnit.SECONDS)
.toZonalTimestamp(ZonalOffset.UTC).toDate();
continue;
} else if (line.startsWith("#")) {
continue; // comment line
}
// this works for some foreseeable future
long epoch = Long.parseLong(line.substring(0, 10));
// this is no leap second
// but just the official introduction of modern UTC scale
if (epoch == 2272060800L) {
continue;
}
// -1 because we don't want to associate
// the leap second with the following day
PlainDate event =
ntpEpoch.plus(epoch - 1, TimeUnit.SECONDS)
.toZonalTimestamp(ZonalOffset.UTC).toDate();
events.add(event); // we don't assume any negative leap seconds here for simplicity
}
} finally {
br.close();
}
// now let's write the result into time4j-format
// use a location relative to class path of main program (see below)
String path = "C:/work/leapseconds.txt";
Writer writer = new FileWriter(new File(path));
String sep = System.getProperty("line.separator");
try {
for (PlainDate event : events) {
writer.write(event + ", +" + sep);
}
writer.write("#expires=" + expires + sep);
} finally {
writer.close();
}
System.out.println(
"Leap second file was successfully written from IETF-resource.");
// And finally, we can start the main program in a separate process
// with the system property "net.time4j.scale.leapseconds.path"
// set to our leapsecond file path (must be relative to class path)
Some notes:
I recommend to write this code as subprogram called by a simple batch program in order to avoid the main program being dependent on internet connectivity. This batch file would finally call the main program with the mentioned system property. If you set this property then the leap seconds will be read from the file specified there, and any eventually available tzdata-module would then stop to yield any concurrent leap second informations.
The reason I need this is to be able to accurately take timestamps
over a long period of time (on the order of years) and still be able
to compare them, without worrying about leap seconds. It is possible
for multiple measurements to take place during a leap second and all
measurements need to be unambiguous, monotonically increasing, and
linearly increasing.
Then your design is suboptimal. You cannot use time and then somehow meddle through leap seconds. This actually comes up often enough and people fall into the same trap of timestamping measurements using wall clock.
Timestamp start of the program and associate it with a counter
Use the counter for sequential measurements at fixed intervals
Timestamp new counter value(s) as necessary to keep your data adequately synced
If you avoid timestamping for the 1 second that leapsecond can occur (midnight!), you are home free because those can be adjusted later.
Now if you insist on using TAI without counter, all you need is a table with leap seconds that need to be accounted for. Then just use monotonic time. There is also libraries that can do this for you, but they may be out of date so you'll have to maintain them yourself,
http://skarnet.org/software/skalibs/libstddjb/tai.html
You have to implement a TAI clock based on C++ std::steady_clock or similar. To synchronize your TAI clock you could rely on GPS or NTP.
Option TAI from NTP: Your TAI implementation would need knowledge about leap seconds. Probably NTP protocol or referenced resources are the most reliable sources of current and future leap seconds.
Option TAI from GPS: GPS clock has a fixed offset to TAI, you do not have to mess with leap seconds
Our database keeps track of a number of "Production Facilities". Each facility has an address, name, and most importantly timezone. How do I figure out the name of a timezone?
It was obvious for our initial facilities because the code is named after our city (America/Edmonton). How can I more generally determine the correct timezone code to use in Java via the call DateTimeZone.forID("timeZone") ex DateTimeZone.forID("America/Edmonton")?
Specifically we are opening a facility in Atlanta Georgia and I am unsure which code to use? Is it one of these?
"Atlantic/South_Georgia"
US/Eastern"
"EST5EDT"
"EST"
This kind of information can be really tricky (not to mention tricky to keep current). It can actually vary county by county (there is an excellent West Wing scene about this :). Google Maps API recently included support to go from lat+lon to TimeZone
The IANA standard timezone identifier for Eastern Time is America/New_York. Presumably this is the standard Joda time is using, since (1) everyone uses it and (2) the city-name-as-identifier gives it away.
You can get a list with:
for (String string : TimeZone.getAvailableIDs(TimeZone.getTimeZone(
"GMT-05:00").getRawOffset())) {
System.out.println(string);
}
And it will include (among many others) "US/Eastern".
You can list the available timezone id's using this method:
http://docs.oracle.com/javase/7/docs/api/java/util/TimeZone.html#getAvailableIDs()
The time zone data seems to come from this database:
http://en.wikipedia.org/wiki/Tz_database
You can do lookups in it here:
http://twiki.org/cgi-bin/xtra/tzdatepick.html
ok not as simple as title may make it sound. I tried this in a very primal way with c# and it worked, but I have a feeling a better job could be achieved with Java and Oracle as database. So the thing is:
I have a reservation system. multiple bookings could be made on the same day for period between date X and date Y as long as each day in the range can accommodate the requested number. Maximum number of clusters to reserve is 46. Hence logically you would look at each day as a holder of 46 cluster reservation and deduce from that.
Now what I have difficulty working out is:
when there are n number of bookings stored and valid in database, then I want to make new booking. So how do I check if this new date range falls within any of the previously booked days or not. Not talking simply here about x falling in y (as ranges). More like:
X_______________________________________Y
X________________________________y
X________________________________Y
X________________________________Y
as u can see the overlap is happening.
Please let me know how could I do this as it will affect early design of objects
Regards
Assume your date has two methods: isBefore(Date other) and isAfter(Date other). Obviously if they don't you can cure this with an external method or wrapping or something. Edit: java.util.Date has compareTo method you could use.
You do this:
public boolean overlapsWithExisting(Booking booking) {
final Date early = booking.getStart();
final Date late = booking.getEnd();
for(Booking existing : existingBookings) {
if(!(early.isAfter(existing.getEnd()) || late.isBefore(existing.getStart()))
return true;
}
return false;
}
We compare this booking to all existing bookings. If this booking ends before the existing booking even starts, or if this booking starts after the existing booking ends, then it doesn't conflict. Any other condition and they will overlap.
Do this to each booking.
Joda-Time – Interval
Rather than roll your own, use the Interval class in the Joda-Time library. An Interval is a pair of specific points along the timeline, each defined as a DateTime instance.
The Interval class offers overlap, gap, and abuts methods.
Half-Open
Those methods wisely use the Half-Open approach to spans of time where the beginning is inclusive while the ending is exclusive. Search StackOverflow for more info.
table structure:
sysdurationtimeday , sysdurationtimehour, sysdurationtimeminute
1, 12,10
3, 23,10
0, 0,10
i have these 3 fields from database, after getting these 3 values, what is the technique that i can use do cast to which Java Object? (maybe Calendar.class, TimeStamp.class) ?
and use it to compared with record is spent less than 1 day, more than 1 day + less than 3 days. etc?
As long as you're talking durations and not absolute times, this is pretty easy. Just express the time in a convenient unit, say seconds:
time_in_seconds = 86400*sysdurationtimeday +
3600*sysdurationtimehour +
60*sysdurationtimeminute
In Java the standard way to represent this is actually as a long value in milliseconds, ala System.currentTimeMillis().
All the standard Java classes are intended to handle absolute times and need to deal with daylight savings, leap years, and all that crap. At least with the data you gave us, you don't have the required info anyway: there's no way to tell if the day was a daylight savings day and therefore took 23 or 25 hours instead of 24.
I would prefer my own class, overriding the "essential" methods.
public class SysDuration implements Comparable {
int day;
int hour;
int min;
public SysDuration(int day,int hour,int min) {
}
public boolean equals(Object obj) {
}
public int hashCode() {
}
public int compareTo(Object obj) {
}
public boolean spendLess(SysDuration dur) {
}
}
Lots of good answers already.
A sugegstion, perhaps out of scope, if you use durations in java I would prefer to
just calculate and store this in one variable, typically a long in milliseconds
if this resolution is good enough. The splitting in 3 variables usually
make most of the code more complicated.
Calculations are easier and intergartion with libs such as jodatime and similar will be
even more simple.
If you literally want "more than 1 day", that is there's no rounding so d=1, h=23, m=59 gives you 1 day, not 2 days then you can just use sysdurationtimeday and completely ignore hours and minutes. (That assumes you don't have more than 24 in sysdurationtimehour).
Classes such as Calendar don't help, they are for manipulating actual dates, you already are working in durations.
Jodatime supports durations and then you get the operations needed for free.
If you are hesitant to add another dependency and learning curve, I would create a little custom class with a field storing the duration as a number in the desired precision. Then add some methods to do your comparisons or return a Calendar object or a Date aded and subtracted with your duration.
My guess is this will end up being cleaner than using the standard Java API's which always end up in complicated, clunky code when you start manipulating time.