If I have a calendar object with a date of 1/8/2012, how would I determine if it has been at least one year since the object's creation?
java.time
The old java.util.Date/.Calendar classes have been supplanted by the new java.time framework built into Java 8 and later (Tutorial). If you really want .Calendar, search any of the hundreds of existing such questions already posted on StackOverflow.
Here is some example code in java.time. The LocalDate class represents a date-only value without time-of-day or time zone. But note that determining today’s date requires a time zone.
// Then.
LocalDate then = LocalDate.parse ( "2012-01-08" );
// Now.
ZoneId zoneId = ZoneId.of ( "America/Montreal" );
LocalDate today = LocalDate.now ( zoneId );
// Elapsed.
Period period = Period.between ( then , today );
int years = period.getYears ();
// Business logic.
Boolean atLeastOneYearHasPassed = ( years >= 1 );
Dump to console.
System.out.println ( "From then: " + then + " to today: " + today + ", atLeastOneYearHasPassed: " + atLeastOneYearHasPassed + ", years: " + years );
When run.
From then: 2012-01-08 to today: 2015-10-17, atLeastOneYearHasPassed: true, years: 3
First of all, you need two datestamps; one for the account creation date, and the second one for "now".
Then you simply turn the two datestamps into scalar values with the same units (e.g. using Calendar.getTime() gives you milliseconds since some "epoch"), subtract one from the other, see if the difference is more, less or equal to 1 year == 365 days (in the relevant units).
If you are talking about "calendar years" (e.g. taking account of leap years, etcetera) then it gets more complicated, and the rules are potentially locale specific.
I recommend that you use the java.time classes rather than the old Calendar API. If you are new to this, start by reading the Oracle Java tutorial "Lesson: Date-Time Overview"
Related
I'm not sure how can I explain just 'time', so I wanna start with an example.
When I write a movie player, I want to represent the start time(00:00:00) in millis as 0, and if current frame position is on 5 minutes(00:05:00), it would be 300,000(5 * 60 * 1000).
I don't care about timezone, calendar or clock information in real world.
However, JodaTime, for example, Instant is related to Unixtime(based on 1970), Interval, Duration and Period are based on Instant, and LocalTime is based on 24-hour(which enforce hour field to 1-24).
Of course, I can parse "00:05:00" into 300,000 manually, but I wonder how can I achieve this goal with JodaTime or other library.
Duration
Your statement about the Duration class is incorrect. You said it is based on Instant, but, no, it is not.
A Duration represents a span of time not attached to the timeline. A Duration in Joda-Time is merely a count of milliseconds. This fact is stated in its Javadoc.
Ditto for Period, which represents a span-of-time unattached to the timeline on a scale of years-months-days. A Duration works on a scale of hours-minutes-seconds.
To represent a span of five minutes:
Duration d = Duration.standardMinutes( 5 ) ;
If you want to get the moment in the future by that amount of time, add that duration to the current moment.
Instant fiveMinutesInFuture = new Instant().plus( d ) ;
ISO 8601
To represent a duration as text, use the standard ISO 8601 format, with a P at the beginning and a T separating the years-months-days portion from the hours-minutes-seconds portion.
So five minutes is PT5M.
String output = d.toString() ;
Parse such ISO 8601 strings.
Duration d = Duration.parse( "PT5M" ) ; // Five minutes.
Clock format
For serializing a duration object as text, always use the ISO 8601 standard format, never clock format.
For user-interface presentation, I suggest you not represent a span of time using clock notation such as 00:05:00. Doing so is confusing and ambiguous to users. Use the ISO 8601 standard formats instead, as they are designed to be practical and unambiguous.
If you insist on representing the duration in clock time format, call the getStandard… methods to get the hours, minutes, and seconds.
String output =
String.format( "%02d", d.getStandardHours() ) +
":" +
String.format( "%02d", d.getStandardMinutes() ) +
":" +
String.format( "%02d", d.getStandardSeconds() )
;
00:05:00
If the duration might be over 24 hours long, you will also need to call getStandardDays. These days are merely chunks of time of 24-hours long. Such days are not calendar days as the Duration is not attached to the timeline.
java.time
Be aware that Joda-Time is now in maintenance mode. It’s successor, java.time, is built into Java 8 and later as well as Android 26+. For Java 6 & 7 use ThreeTen-Backport. For earlier Android, use ThreeTenABP.
The concepts are the same in java.time as discussed here. The java.time.Duration class is quite similar except for the much finer resolution of nanoseconds and a slightly different syntax.
Instant.now().plus(
Duration.ofMinutes( 5 )
)
Or:
Instant.now().plus(
Duration.parse( "PT5M" )
)
To build a clock-reading, call the to…Part methods.
String output =
String.format( "%02d", d.toHoursPart() ) +
":" +
String.format( "%02d", d.toMinutesPart() ) +
":" +
String.format( "%02d", d.toSecondsPart() )
;
00:05:00
In my application, it saves a preference which is an integer that is the number of days since the epoch. (not relevant but it is used to create backups every x days)
Given this value, how can I reliably create an instance of joda DateTime?
I'm tempted to convert it to milliseconds by doing the multiplication value * 24 * 60 * 60 * 1000 but this will be wrong due to astronomy / solar time?
Multiplying number of days into the number of milliseconds might be more readable if you use a library function. I highly recommend using Joda. :)
You have a number of days since epoch (GMT), and you want a DateTime (date + time + timezone). At a bare minimum, before you get further, you'll need to specify how you want to treat the time and timezone calculation.
The simplest way (which might not be what you intend) would be to create an instant in the local timezone representing the beginning of epoch, and then use plusDays to add the right number of days:
// in class - note that this will cache the current default timezone
private static final DateTime EPOCH_START_INSTANT = new DateTime(0);
// West of Greenwich, this will actually represent the "day" before.
// Day 0 will be Dec 31, 1969, local time.
DateTime localTime = EPOCH_START_INSTANT.plusDays(yourDayCount);
For the purpose of creating a backup every X days, you might instead want a LocalDate initialized at epoch (January 1, 1970), plus the number of days you want. That could then be changed to a specified local time relatively easily.
// in class
private static final EPOCH_LOCALDATE = new LocalDate(1970, 1, 1);
private static final THREE_AM = new LocalTime(3, 0);
LocalDate localDate = EPOCH_LOCALDATE.plusDays(yourDayCount);
// Midnight (or closest valid time thereto) in the default time zone
DateTime startOfDay = localDate.toDateTimeAtStartOfDay();
// 3 AM in the default time zone
DateTime threeAM = localDate.toDateTime(THREE_AM);
The answer by Jeff Bowman is correct.
I'll show the same idea in the java.time framework, intended to succeed Joda-Time.
java.time
Java 8 and later has the new java.time framework built-in. These new classes supplant the old java.util.Date/.Calendar classes. They are inspired by Joda-Time, defined by JSR 310, and extended by the ThreeTen-Extra project.
I am assuming your count-of-day-from-epoch is in UTC. So we can use the Instant class, basically a count of nanoseconds from the first moment of 1970 in UTC.
long myCountOfDays = 16_721L;
Instant instant = Instant.EPOCH.plus ( myCountOfDays , ChronoUnit.DAYS );
Let's adjust into a time zone. Choosing Montreal arbitrarily. Use a proper time zone name, never the 3-4 letter codes like "EST" or "IST".
ZoneId zoneId = ZoneId.of ( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant ( instant , zoneId );
Dump to console.
System.out.println ( "myCountOfDays: " + myCountOfDays + " from epoch: " + Instant.EPOCH + " in UTC is: " + instant + " and in Montréal is: " + zdt + "." );
When run.
myCountOfDays: 16721 from epoch: 1970-01-01T00:00:00Z in UTC is: 2015-10-13T00:00:00Z and in Montréal is: 2015-10-12T20:00-04:00[America/Montreal].
According to the FAQ:
Joda-Time does not support leap seconds. Leap seconds can be supported by writing a new, specialized chronology, or by making a few enhancements to the existing ZonedChronology class. In either case, future versions of Joda-Time will not enable leap seconds by default. Most applications have no need for it, and it might have additional performance costs.
That suggests to me that you need not worry about that aspect.
But rather than doing the math, I would use DateTime#plusDays or MutableDateTime#addDays instead, using The Epoch as your starting point.
I assume, though, that your "days since The Epoch" is allowing for leap days (and that you're using the Gregorian chronology so JodaTime is, too).
Date d = new Date(today.getTimeInMillis());
Date d1 = new Date(dueDate.getTimeInMillis());
int daysUntil = (int) ((d1.getTime() - d.getTime())/ (1000 * 60 * 60 * 24));
Using the above code, where today is a calendar set to 00:00 on the current day, and dueDate is set to 00:00 on the date I am comparing today to, my results from this differ.
There is something in this which varies, making my output either x or x+1 where x is the correct answer.
What is the issue here, and what can I do to make it more stable?
Vague Question
You do not provide actual values, so we cannot determine precisely the problem. We do not know what the today and dueDate variables are.
Outmoded
The question is now outmoded, as the troublesome old date-time classes including java.util.Date/.Calendar have been supplanted by the new java.time framework. See Tutorial. Defined by JSR 310, inspired by Joda-Time, and extended by the ThreeTen-Extra project.
In java.time:
An Instant is a moment on the timeline in UTC.
A ZoneId represents a time zone. Use proper time zone names, never the 3-4 letter codes like "EST" or "IST" as they are neither standardized nor unique.
Conceptually, ZonedDateTime = Instant + ZoneId.
ThreeTen-Extra
Unfortunately, java.time does not include a facility for calculating days elapsed between date-time values. We can use the ThreeTen-Extra project and its Days class with between method to provide that calculation. The ThreeTen-Extra project is a collection of features deemed non-essential for java.time during the JSR process.
ZoneId zoneId = ZoneId.of ( "America/Montreal" );
ZonedDateTime now = ZonedDateTime.now ( zoneId );
ZonedDateTime then = now.minusDays ( 4 );
ZonedDateTime due = now.plusDays ( 3 );
Integer days = org.threeten.extra.Days.between ( then , due ).getAmount ();
Dump to console.
System.out.println ( "From then: " + then + " to due: " + due + " = days: " + days );
From then: 2015-10-31T16:01:13.082-04:00[America/Montreal] to due: 2015-11-07T16:01:13.082-05:00[America/Montreal] = days: 7
Joda-Time
For Android or older versions of Java, use the excellent Joda-Time library.
The Days class is smart and handles anomalies such as Daylight Saving Time (DST).
Note that unlike java.util.Date, a Joda-Time DateTime object knows its own time zone.
// Specify a time zone rather than rely on default.
DateTimeZone timeZone = DateTimeZone.forID( "America/Regina" ); // Or "Europe/London".
DateTime now = new DateTime( timeZone );
DateTime startOfToday = now.withTimeAtStartOfDay();
DateTime fewDaysFromNow = now.plusDays( 3 );
DateTime startOfAnotherDay = fewDaysFromNow.withTimeAtStartOfDay();
Days days = Days.daysBetween( startOfToday, startOfAnotherDay );
Dump to console…
System.out.println( days.getDays() + " days between " + startOfToday + " and " + startOfAnotherDay + "." );
When run…
3 days between 2014-01-21T00:00:00.000-06:00 and 2014-01-24T00:00:00.000-06:00.
There are mainly two reasons why your code is broken:
second parts or millisecond fractions (you might have overlooked)
daylight saving effects
I demonstrate and explain the second reason.
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date d1 = sdf.parse("2016-03-20");
Date d2 = sdf.parse("2016-03-28");
int daysUntil = (int) ((d2.getTime() - d1.getTime()) / (1000 * 60 * 60 * 24));
System.out.println(daysUntil); // 7 (should be 8)
The code was run in timezone "Europe/Berlin". Due to the change from winter time to summer time causing a jump of clocks by one hour forward on 2016-03-27 at 2 am, there is one hour missing. One day has only 23 hours so the division by 24 yields zero resulting in counting one day less.
What can you do else?
Your workaround adding 1000 milliseconds to dueDate sounds as if you have overlooked possible millisecond deltas in your input. This might solve a special case but will usually not be sufficient to solve the daylight saving problem, too. Whatever you choose on base of java.util.Date it is a more or less an evil hack.
The best I have in mind (within the scope of Android-built-in stuff) is to construct an instance of java.util.GregorianCalendar and to add successively one day after one until you have passed the due-date, and then count how many days you have added. Not elegant and errorprone because varying millisecond parts can easily be overlooked here, too.
Otherwise you can try various external libraries for this task. There are four available on Android which can calculate elapsed days in an easy way.
Date4J (main advantage: very small but else limited features)
Threeten-ABP (uses backport of Java-8)
Joda-Time-Android (based on Joda-Time)
Time4A (my own library for Android)
In Order to show a reproducible scenario, I am doing the following
Get the current system time (local time)
Convert Local time to UTC // Works Fine Till here
Reverse the UTC time, back to local time. Followed 3 different approaches (listed below) but all the 3 approaches retains the time in UTC only.
{
long ts = System.currentTimeMillis();
Date localTime = new Date(ts);
String format = "yyyy/MM/dd HH:mm:ss";
SimpleDateFormat sdf = new SimpleDateFormat (format);
// Convert Local Time to UTC (Works Fine)
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
Date gmtTime = new Date(sdf.format(localTime));
System.out.println("Local:" + localTime.toString() + "," + localTime.getTime() + " --> UTC time:" + gmtTime.toString() + "-" + gmtTime.getTime());
// Reverse Convert UTC Time to Locale time (Doesn't work) Approach 1
sdf.setTimeZone(TimeZone.getDefault());
localTime = new Date(sdf.format(gmtTime));
System.out.println("Local:" + localTime.toString() + "," + localTime.getTime() + " --> UTC time:" + gmtTime.toString() + "-" + gmtTime.getTime());
// Reverse Convert UTC Time to Locale time (Doesn't work) Approach 2 using DateFormat
DateFormat df = new SimpleDateFormat (format);
df.setTimeZone(TimeZone.getDefault());
localTime = df.parse((df.format(gmtTime)));
System.out.println("Local:" + localTime.toString() + "," + localTime.getTime() + " --> UTC time:" + gmtTime.toString() + "-" + gmtTime.getTime());
// Approach 3
Calendar c = new GregorianCalendar(TimeZone.getDefault());
c.setTimeInMillis(gmtTime.getTime());
System.out.println("Local Time " + c.toString());
}
I also recommend using Joda as mentioned before.
Solving your problem using standard Java Date objects only can be done as follows:
// **** YOUR CODE **** BEGIN ****
long ts = System.currentTimeMillis();
Date localTime = new Date(ts);
String format = "yyyy/MM/dd HH:mm:ss";
SimpleDateFormat sdf = new SimpleDateFormat(format);
// Convert Local Time to UTC (Works Fine)
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
Date gmtTime = new Date(sdf.format(localTime));
System.out.println("Local:" + localTime.toString() + "," + localTime.getTime() + " --> UTC time:"
+ gmtTime.toString() + "," + gmtTime.getTime());
// **** YOUR CODE **** END ****
// Convert UTC to Local Time
Date fromGmt = new Date(gmtTime.getTime() + TimeZone.getDefault().getOffset(localTime.getTime()));
System.out.println("UTC time:" + gmtTime.toString() + "," + gmtTime.getTime() + " --> Local:"
+ fromGmt.toString() + "-" + fromGmt.getTime());
Output:
Local:Tue Oct 15 12:19:40 CEST 2013,1381832380522 --> UTC time:Tue Oct 15 10:19:40 CEST 2013,1381825180000
UTC time:Tue Oct 15 10:19:40 CEST 2013,1381825180000 --> Local:Tue Oct 15 12:19:40 CEST 2013-1381832380000
You have a date with a known timezone (Here Europe/Madrid), and a target timezone (UTC)
You just need two SimpleDateFormats:
long ts = System.currentTimeMillis();
Date localTime = new Date(ts);
SimpleDateFormat sdfLocal = new SimpleDateFormat ("yyyy/MM/dd HH:mm:ss");
sdfLocal.setTimeZone(TimeZone.getTimeZone("Europe/Madrid"));
SimpleDateFormat sdfUTC = new SimpleDateFormat ("yyyy/MM/dd HH:mm:ss");
sdfUTC.setTimeZone(TimeZone.getTimeZone("UTC"));
// Convert Local Time to UTC
Date utcTime = sdfLocal.parse(sdfUTC.format(localTime));
System.out.println("Local:" + localTime.toString() + "," + localTime.getTime() + " --> UTC time:" + utcTime.toString() + "-" + utcTime.getTime());
// Reverse Convert UTC Time to Locale time
localTime = sdfUTC.parse(sdfLocal.format(utcTime));
System.out.println("UTC:" + utcTime.toString() + "," + utcTime.getTime() + " --> Local time:" + localTime.toString() + "-" + localTime.getTime());
So after see it working you can add this method to your utils:
public Date convertDate(Date dateFrom, String fromTimeZone, String toTimeZone) throws ParseException {
String pattern = "yyyy/MM/dd HH:mm:ss";
SimpleDateFormat sdfFrom = new SimpleDateFormat (pattern);
sdfFrom.setTimeZone(TimeZone.getTimeZone(fromTimeZone));
SimpleDateFormat sdfTo = new SimpleDateFormat (pattern);
sdfTo.setTimeZone(TimeZone.getTimeZone(toTimeZone));
Date dateTo = sdfFrom.parse(sdfTo.format(dateFrom));
return dateTo;
}
Joda-Time
UPDATE: The Joda-Time project is now in maintenance mode, with the team advising migration to the java.time classes. See Tutorial by Oracle.
See my other Answer using the industry-leading java.time classes.
Normally we consider it bad form on StackOverflow.com to answer a specific question by suggesting an alternate technology. But in the case of the date, time, and calendar classes bundled with Java 7 and earlier, those classes are so notoriously bad in both design and execution that I am compelled to suggest using a 3rd-party library instead: Joda-Time.
Joda-Time works by creating immutable objects. So rather than alter the time zone of a DateTime object, we simply instantiate a new DateTime with a different time zone assigned.
Your central concern of using both local and UTC time is so very simple in Joda-Time, taking just 3 lines of code.
org.joda.time.DateTime now = new org.joda.time.DateTime();
System.out.println( "Local time in ISO 8601 format: " + now + " in zone: " + now.getZone() );
System.out.println( "UTC (Zulu) time zone: " + now.toDateTime( org.joda.time.DateTimeZone.UTC ) );
Output when run on the west coast of North America might be:
Local time in ISO 8601 format: 2013-10-15T02:45:30.801-07:00
UTC (Zulu) time zone: 2013-10-15T09:45:30.801Z
Here is a class with several examples and further comments. Using Joda-Time 2.5.
/**
* Created by Basil Bourque on 2013-10-15.
* © Basil Bourque 2013
* This source code may be used freely forever by anyone taking full responsibility for doing so.
*/
public class TimeExample {
public static void main(String[] args) {
// Joda-Time - The popular alternative to Sun/Oracle's notoriously bad date, time, and calendar classes bundled with Java 8 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
// You may output to strings in other formats.
// Capture one moment in time, to be used in all the examples to follow.
org.joda.time.DateTime now = new org.joda.time.DateTime();
System.out.println( "Local time in ISO 8601 format: " + now + " in zone: " + now.getZone() );
System.out.println( "UTC (Zulu) time zone: " + now.toDateTime( org.joda.time.DateTimeZone.UTC ) );
// You may specify a time zone in either of two ways:
// • Using identifiers bundled with Joda-Time
// • Using identifiers bundled with Java via its TimeZone class
// ----| Joda-Time Zones |---------------------------------
// Time zone identifiers defined by Joda-Time…
System.out.println( "Time zones defined in Joda-Time : " + java.util.Arrays.toString( org.joda.time.DateTimeZone.getAvailableIDs().toArray() ) );
// Specify a time zone using DateTimeZone objects from Joda-Time.
// http://joda-time.sourceforge.net/apidocs/org/joda/time/DateTimeZone.html
org.joda.time.DateTimeZone parisDateTimeZone = org.joda.time.DateTimeZone.forID( "Europe/Paris" );
System.out.println( "Paris France (Joda-Time zone): " + now.toDateTime( parisDateTimeZone ) );
// ----| Java Zones |---------------------------------
// Time zone identifiers defined by Java…
System.out.println( "Time zones defined within Java : " + java.util.Arrays.toString( java.util.TimeZone.getAvailableIDs() ) );
// Specify a time zone using TimeZone objects built into Java.
// http://docs.oracle.com/javase/8/docs/api/java/util/TimeZone.html
java.util.TimeZone parisTimeZone = java.util.TimeZone.getTimeZone( "Europe/Paris" );
System.out.println( "Paris France (Java zone): " + now.toDateTime(org.joda.time.DateTimeZone.forTimeZone( parisTimeZone ) ) );
}
}
tl;dr
Instant.now() // Capture the current moment in UTC.
.atZone( ZoneId.systemDefault() ) // Adjust into the JVM's current default time zone. Same moment, different wall-clock time. Produces a `ZonedDateTime` object.
.toInstant() // Extract a `Instant` (always in UTC) object from the `ZonedDateTime` object.
.atZone( ZoneId.of( "Europe/Paris" ) ) // Adjust the `Instant` into a specific time zone. Renders a `ZonedDateTime` object. Same moment, different wall-clock time.
.toInstant() // And back to UTC again.
java.time
The modern approach uses the java.time classes that supplanted the troublesome old legacy date-time classes (Date, Calendar, etc.).
Your use of the word "local" contradicts the usage in the java.time class. In java.time, "local" means any locality or all localities, but not any one particular locality. The java.time classes with names starting with "Local…" all lack any concept of time zone or offset-from-UTC. So they do not represent a specific moment, they are not a point on the timeline, whereas your Question is all about moments, points on the timeline viewed through various wall-clock times.
Get the current system time (local time)
If you want to capture the current moment in UTC, use Instant. The Instant class represents a moment on the timeline in UTC with a resolution of nanoseconds (up to nine (9) digits of a decimal fraction).
Instant instant = Instant.now() ; // Capture the current moment in UTC.
Adjust into a time zone by applying a ZoneId to get a ZonedDateTime. Same moment, same point on the timeline, different wall-clock time.
Specify a proper time zone name in the format of continent/region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 3-4 letter abbreviation such as EST or IST as they are not true time zones, not standardized, and not even unique(!).
ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = instant.atZone( z ) ; // Same moment, different wall-clock time.
As a shortcut, you can skip the usage of Instant to get a ZonedDateTime.
ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = ZonedDateTime.now( z ) ;
Convert Local time to UTC // Works Fine Till here
You can adjust from the zoned date-time to UTC by extracting an Instant from a ZonedDateTime.
ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = ZonedDateTime.now( z ) ;
Instant instant = zdt.toInstant() ;
Reverse the UTC time, back to local time.
As shown above, apply a ZoneId to adjust the same moment into another wall-clock time used by the people of a certain region (a time zone).
Instant instant = Instant.now() ; // Capture current moment in UTC.
ZoneId zDefault = ZoneId.systemDefault() ; // The JVM's current default time zone.
ZonedDateTime zdtDefault = instant.atZone( zDefault ) ;
ZoneId zTunis = ZoneId.of( "Africa/Tunis" ) ; // The JVM's current default time zone.
ZonedDateTime zdtTunis = instant.atZone( zTunis ) ;
ZoneId zAuckland = ZoneId.of( "Pacific/Auckland" ) ; // The JVM's current default time zone.
ZonedDateTime zdtAuckland = instant.atZone( zAuckland ) ;
Going back to UTC from a zoned date-time, call ZonedDateTime::toInstant. Think of it conceptually as: ZonedDateTime = Instant + ZoneId.
Instant instant = zdtAuckland.toInstant() ;
All of these objects, the Instant and the three ZonedDateTime objects all represent the very same simultaneous moment, the same point in history.
Followed 3 different approaches (listed below) but all the 3 approaches retains the time in UTC only.
Forget about trying to fix code using those awful Date, Calendar, and GregorianCalendar classes. They are a wretched mess of bad design and flaws. You need never touch them again. If you must interface with old code not yet updated to java.time, you can convert back-and-forth via new conversion methods added to the old classes.
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, Java SE 10, and later
Built-in.
Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.
I am joining the choir recommending that you skip the now long outdated classes Date, Calendar, SimpleDateFormat and friends. In particular I would warn against using the deprecated methods and constructors of the Date class, like the Date(String) constructor you used. They were deprecated because they don’t work reliably across time zones, so don’t use them. And yes, most of the constructors and methods of that class are deprecated.
While at the time you asked the question, Joda-Time was (from all I know) a clearly better alternative, time has moved on again. Today Joda-Time is a largely finished project, and its developers recommend you use java.time, the modern Java date and time API, instead. I will show you how.
ZonedDateTime localTime = ZonedDateTime.now(ZoneId.systemDefault());
// Convert Local Time to UTC
OffsetDateTime gmtTime
= localTime.toOffsetDateTime().withOffsetSameInstant(ZoneOffset.UTC);
System.out.println("Local:" + localTime.toString()
+ " --> UTC time:" + gmtTime.toString());
// Reverse Convert UTC Time to Local time
localTime = gmtTime.atZoneSameInstant(ZoneId.systemDefault());
System.out.println("Local Time " + localTime.toString());
For starters, note that not only is the code only half as long as yours, it is also clearer to read.
On my computer the code prints:
Local:2017-09-02T07:25:46.211+02:00[Europe/Berlin] --> UTC time:2017-09-02T05:25:46.211Z
Local Time 2017-09-02T07:25:46.211+02:00[Europe/Berlin]
I left out the milliseconds from the epoch. You can always get them from System.currentTimeMillis(); as in your question, and they are independent of time zone, so I didn’t find them intersting here.
I hesitatingly kept your variable name localTime. I think it’s a good name. The modern API has a class called LocalTime, so using that name, only not capitalized, for an object that hasn’t got type LocalTime might confuse some (a LocalTime doesn’t hold time zone information, which we need to keep here to be able to make the right conversion; it also only holds the time-of-day, not the date).
Your conversion from local time to UTC was incorrect and impossible
The outdated Date class doesn’t hold any time zone information (you may say that internally it always uses UTC), so there is no such thing as converting a Date from one time zone to another. When I just ran your code on my computer, the first line it printed, was:
Local:Sat Sep 02 07:25:45 CEST 2017,1504329945967 --> UTC time:Sat Sep 02 05:25:45 CEST 2017-1504322745000
07:25:45 CEST is correct, of course. The correct UTC time would have been 05:25:45 UTC, but it says CEST again, which is incorrect.
Now you will never need the Date class again, :-) but if you were ever going to, the must-read would be All about java.util.Date on Jon Skeet’s coding blog.
Question: Can I use the modern API with my Java version?
If using at least Java 6, you can.
In Java 8 and later the new API comes built-in.
In Java 6 and 7 get the ThreeTen Backport, the backport of the new classes (that’s ThreeTen for JSR-310, where the modern API was first defined).
On Android, use the Android edition of ThreeTen Backport. It’s called ThreeTenABP, and I think that there’s a wonderful explanation in this question: How to use ThreeTenABP in Android Project.
I strongly recommend using Joda Time http://joda-time.sourceforge.net/faq.html
I'm no expert programmer, but I have looked online for a solution to this and cannot find anything to help me.
Here is what I am trying to do for an automation project at work. I am being given a time value in milliseconds. I need to take that time value, add 6 minutes to it, then retrieve the hour, minutes, and AM_PM value so I can then do my test.
The problem is that after I retrieve the time, I then set it with a CALENDAR, do the addition, and when I go to retrieve the minutes and hours, they are not set correctly.
For example, here is my code:
_logger.info("Get current system time in milliseconds");
long currentTime = TimeApi.getCurrentSystemTime(_deviceUnderTestPIN);
_logger.info("Current device time is : " + Long.toString(currentTime));
_logger.info("Set Calendar object with device time");
Calendar now = Calendar.getInstance();
now.setTimeInMillis(currentTime);
long timeSet = now.getTimeInMillis();
_logger.info("Calendar object is set to : " + Long.toString(timeSet));
_logger.info("add mintuesToAdd to current time");
now.add(Calendar.MINUTE, minutesToAdd);
long timeAdd = now.getTimeInMillis();
_logger.info("Calendar Time after Add: " + Long.toString(timeAdd));
_logger.info("set hour and minute");
// if the hour is 12am or 12pm the Calendar object will return 0, we need to pass in 12 so we will set it to 12 below if it returns 0
hour = now.get(Calendar.HOUR);
if (hour == 0) {
hour = 12;
}
minutes = now.get(Calendar.MINUTE);
_logger.info("set amPM");
if (now.get(Calendar.AM_PM) == 0) {
amPM = false;
} else {
amPM = true;
}
_logger.info("Setting alarm hour to: " + Integer.toString(hour));
_logger.info("Setting the alarm minutes to: " + Integer.toString(minutes));
_logger.info("Setting alarm AM_PM to: " + Boolean.toString(amPM));
And here is the output from my test run:
2013-06-06 13:15:36.007 INFO Current device time is : 1370524535000
2013-06-06 13:15:36.007 INFO Set Calendar object with device time
2013-06-06 13:15:36.007 INFO Calendar object is set to : 1370524535000
2013-06-06 13:15:36.007 INFO add mintuesToAdd to current time
2013-06-06 13:15:36.007 INFO Calendar Time after Add: 1370524895000
2013-06-06 13:15:36.007 INFO set hour and minute
2013-06-06 13:15:36.007 INFO set amPM
2013-06-06 13:15:36.023 INFO Setting alarm hour to: 1
2013-06-06 13:15:36.023 INFO Setting the alarm minutes to: 21
2013-06-06 13:15:36.023 INFO Setting alarm AM_PM to: true
As you can see the time value I have and are trying to set it Thu Jun 06 2013 09:15:35 GMT-0400 (Eastern Daylight Time). So the part I don't understand is why is it taking the server time when i call now.get(Calendar.HOUR)???
Any help would be greatly appreciated
I think you need to use the timezome parameter when getting the instance of the Calendar as below:
Calendar now = Calendar.getInstance(TimeZone.getTimeZone("America/New_York"));
tl;dr
Instant.ofEpochMilli( 1_370_524_535_000L ) // Parse count-of-milliseconds-since-epoch as a `Instant`.
.plus( Duration.ofMinutes( 6 ) ) // Calculate six minutes later.
.atZone( ZoneId.of( "Asia/Kolkata" ) ) // Adjust from UTC of a `Instant` to the time zone of a `ZonedDateTime` object.
.toLocalTime() // Extract the time-of-day, without the date and without the time zone.
.format( // Generate a string.
DateTimeFormatter.ofLocalizedTime( FormatStyle.MEDIUM ) // Automatically localize using…
.withLocale( Locale.US ) // …the human language and cultural norms of a given locale.
)
6:51:35 PM
java.time
You are using troublesome old date-time classes that are now legacy, supplanted by the java.time classes.
If given a count of milliseconds since the epoch reference of first moment of 1970 in UTC, 1970-01-01T00:00Z, then parse as a Instant. The Instant class represents a moment on the timeline in UTC with a resolution of nanoseconds (up to nine (9) digits of a decimal fraction).
Instant instant = Instant.ofEpochMilli( 1_370_524_535_000L ) ;
instant.toString(): 2013-06-06T13:15:35Z
Represent your six minute span of time as a Duration.
Duration d = Duration.ofMinutes( 6 ) ; // Span of time (hours-minutes-seconds) unattached to the timeline.
Do the math.
Instant sixMinutesLater = instant.plus( d ) ; // Add the duration of six minutes to the UTC moment.
Convert to the more flexible OffsetDateTime, to complete our other work.
OffsetDateTime odt = OffsetDateTime.ofInstant( sixMinutesLater , ZoneOffset.UTC ) ;
To get the time of day, without a date and without a time zone, extract a LocalTime.
LocalTime lt = odt.toLocalTime() ;
To ask if it is morning, compare to another LocalTime representing noon, 12:00.
Boolean isBeforeNoon = lt.isBefore( LocalTime.NOON ) ;
You can interrogate for the parts: getHour (0-23), getMinute, and so on.
You may want to generate a string representing this value. Specify a formatting pattern, or automatically localize.
DateTimeFormatter f = DateTimeFormatter.ofLocalizedTime( FormatStyle.MEDIUM ).withLocale( Locale.US ) ;
String output = lt.format( f ) ;
The above code assumes you want to work in UTC. If instead you want the time-of-day as seen on a wall-clock by the people of a certain region, you must specify a time zone (ZoneId) to get a ZonedDateTime object.
ZoneId z = ZoneId.of( "Africa/Tunis" );
ZonedDateTime zdtAfricaTunis = instant.atZone( z );
LocalTime ltAfricaTunis = zdtAfricaTunis.toLocalTime();
Dump to console.
System.out.println( "instant.toString(): " + instant + " and sixMinutesLaterInstant: " + sixMinutesLaterInstant );
System.out.println( "odt.toString(): " + odt + " and lt.toString(): " + lt + " is morning: " + isBeforeNoon + " with output: " + output );
System.out.println( "zdtAfricaTunis.toString(): " + zdtAfricaTunis + " and ltAfricaTunis.toString(): " + ltAfricaTunis );
instant.toString(): 2013-06-06T13:15:35Z and sixMinutesLaterInstant: 2013-06-06T13:21:35Z
odt.toString(): 2013-06-06T13:21:35Z and lt.toString(): 14:21:35 is morning: false with output: 1:21:35 PM
zdtAfricaTunis.toString(): 2013-06-06T14:21:35+01:00[Africa/Tunis] and ltAfricaTunis.toString(): 14:21:35
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, and later
Built-in.
Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android, the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.