I can calculate the time in milliseconds with the following Ruby snippet:
$ irb
> require 'date'
=> true
> Date.new(2014,9,5).to_time
=> 2014-09-05 00:00:00 +0200
> Date.new(2014,9,5).to_time.to_i * 1000
=> 1409868000000
1409868000000 is the desired result.
How can I get the same result with Java? I set the time zone to CEST since it seems to be what Ruby works with. So I tried this:
GregorianCalendar calendar = new GregorianCalendar(TimeZone.getTimeZone("CEST"));
calendar.set(2014, 9, 5);
System.out.println("" + calendar.getTimeInMillis());
// Returns: 1412498241422
There are three problems with your current code:
"CEST" isn't a time zone Java recognized. Try Europe/Paris instead, as a European time zone
java.util.Calendar uses 0-based months (yes, it's awful)
You haven't cleared out the time part of the Calendar, so it'll give you the current time of day, on that date
This works:
import java.util.*;
public class Test {
public static void main(String[] args) throws Exception {
TimeZone zone = TimeZone.getTimeZone("Europe/Paris");
Calendar calendar = new GregorianCalendar(zone);
// Month 8 = September in 0-based month numbering
calendar.set(2014, 8, 5, 0, 0, 0);
calendar.set(Calendar.MILLISECOND, 0);
System.out.println(calendar.getTimeInMillis());
}
}
If you know the month in advance, you can use the constants instead:
calendar.set(2014, Calendar.SEPTEMBER, 5, 0, 0, 0);
If you can possibly move to using Joda Time or java.time from Java 8, however, those are much cleaner APIs.
For example, in Joda Time:
import org.joda.time.*;
class Test {
public static void main(String[] args) {
DateTimeZone zone = DateTimeZone.forID("Europe/Paris");
// Look ma, a sensible month numbering system!
LocalDate date = new LocalDate(2014, 9, 5);
DateTime zoned = date.toDateTimeAtStartOfDay(zone);
System.out.println(zoned.getMillis());
}
}
java.time
The java.util Date-Time API and their formatting API, SimpleDateFormat are outdated and error-prone. It is recommended to stop using them completely and switch to the modern Date-Time API*, released in March 2014.
Also, quoted below is a notice at the Home Page of Joda-Time:
Note that from Java SE 8 onwards, users are asked to migrate to java.time (JSR-310) - a core part of the JDK which replaces this project.
Solution using java.time, the modern Date-Time API:
import java.time.LocalDate;
import java.time.ZoneId;
public class Main {
public static void main(String[] args) {
long millis = LocalDate.of(2014, 9, 5)
.atStartOfDay(ZoneId.of("Europe/Paris"))
.toInstant()
.toEpochMilli();
System.out.println(millis);
}
}
Output:
1409868000000
ONLINE DEMO
Avoid using two/three/four letter abbreviation for timezone name. Use the convention Region/City e.g. Europe/Paris. You can get the list of timezone names using ZoneId.getAvailableZoneIds().
Learn more about the modern Date-Time API from Trail: Date Time.
* For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.
You can use System.currentTimeMillis() to get the time in milliseconds since start of epoch.
If you want to translate to the same baseline as Ruby is using, that will be as simple as adding some fixed offset (i.e., use System.currentTimeMillis() + DELTA). You should be able to calculate DELTA by trying it in Ruby and in Java on some fixed date to see what the difference is.
Are you sure that Ruby's calculation isn't locale-dependent, though? It would be odd (not to say barely plausible) if it routinely used CEST.
try
calendar.set(2014, 8, 5, 0, 0, 0);
System.out.println("" + (calendar.getTimeInMillis() / 1000) * 1000);
see http://docs.oracle.com/javase/7/docs/api/java/util/Calendar.html#set(int,%20int,%20int,%20int,%20int)
Related
There is requirement to see if some date (ex: expiry date) is greater than or equal to today. Presently JODA time library has been used to achieve this simple comparison. Even some post are recommending that like this.
But recently found some problem with timezones. Date exists in PST and when converted to LocalDate following conversion comes false at 5:00 pm PST, when it should be true -
LocalDate now = LocalDate.fromDateFields(new Date()); // Current date in PST
LocalDate expiryDate = LocalDate.fromDateFields(expiresOn); // expiresOn is java.util.Date
boolean notExpired = expiryDate.isEqual(now) || expiryDate.isAfter(now);
When looked closely, LocalDate expiryDate was using UTC chronology. So at 5:00pm PST, when variable expiryDate contains contains "2021-01-16", variable now becomes "2021-01-17"
Please recommend, what is the better-way to deal with this problem.
I am trying to understand, what special advantages I might achieve by using joda time, because the same compassion can be done using SimpleDateFormatter.
The date-time API of java.util and their formatting API, SimpleDateFormat are outdated and error-prone. It is recommended to stop using them completely and switch to the modern date-time API.
For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7.
If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.
Learn about the modern date-time API from Trail: Date Time.
LocalDate uses JVM's timezone by default
Whenever timezone is involved, make sure to specify the same while creating an instance of LocalDate. A LocalDate uses JVM's timezone by default and you should never compare a LocalDate from one timezone to that of another without converting both of them in the same timezone (the recommended one is UTC). Same is the case with LocalDateTime. Instead of using LocalDate, you should do all processing with objects which have both date and time (e.g. LocalDateTime) and if required you can derive the LocalDate from them.
Also, the java.util.Date object simply represents the number of milliseconds since the standard base time known as "the epoch", namely January 1, 1970, 00:00:00 GMT (or UTC). When you print an object of java.util.Date, its toString method returns the date-time in the JVM's timezone, calculated from this milliseconds value.
Therefore, if you are deriving expiryDate from a java.util.Date object, it is essentially date-time in UTC.
You can convert now-in-PST and expiryDate into java.time.Instant and compare them. A java.time.Instant is an instantaneous point on the UTC time-line.
Demo using the modern date-time API:
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Calendar;
import java.util.Date;
public class Main {
public static void main(String[] args) {
LocalDateTime nowInPST = LocalDateTime.now(ZoneId.of("America/Los_Angeles"));
System.out.println(nowInPST);
// Convert it to date in UTC
Instant nowInPSTConvertedToInstant = nowInPST.atZone(ZoneId.of("America/Los_Angeles"))
.withZoneSameInstant(ZoneId.of("Etc/UTC"))
.toInstant();
// Some java.util.Date
Calendar calendar = Calendar.getInstance();
calendar.set(2020, 0, 10, 10, 10, 10);
Date date = calendar.getTime();
Instant expiry = date.toInstant();
System.out.println(nowInPSTConvertedToInstant.isBefore(expiry));
}
}
Output:
2021-01-17T10:58:38.490041
false
Note: Check the following notice at the Home Page of Joda-Time
Joda-Time is the de facto standard date and time library for Java
prior to Java SE 8. Users are now asked to migrate to java.time
(JSR-310).
Simplify your expression
The following statement
boolean notExpired = expiryDate.isEqual(now) || expiryDate.isAfter(now);
can be simplified as
boolean notExpired = !expiryDate.isBefore(now);
You should consider two APIs:
Joda-Time that you have been using until now is a good library, but in maintenance mode.
The chief developer of Joda-Time, Stephen Colebourne, went on to develop java.time, the modern Java date and time API, drawing on lessons from good and not so good experiences from Joda-Time.
It’s not perfectly clear from your question. I am assuming that expiration has been recorded in UTC and appears to be one day early because it is looked at in Pacific Time. So I am showing you how to keep everything in UTC so comparisons make sense and are accurate.
Joda-Time
System.setProperty("user.timezone", "America/Vancouver");
Date expiresOn = new Date(1_610_841_600_000L); // Jan 17 UTC
System.out.println(expiresOn);
LocalDate now = LocalDate.now(DateTimeZone.UTC);
System.out.println(now);
LocalDate expiryDate = new DateTime(expiresOn, DateTimeZone.UTC).toLocalDate();
System.out.println(expiryDate);
boolean notExpired = expiryDate.isEqual(now) || expiryDate.isAfter(now);
System.out.println("Expired? " + (notExpired ? "No" : "Yes"));
Output when running now:
Sat Jan 16 16:00:00 PST 2021
2021-01-17
2021-01-17
Expired? No
The Joda-Time home page says:
Note that Joda-Time is considered to be a largely “finished” project.
No major enhancements are planned. If using Java SE 8, please migrate
to java.time (JSR-310).
java.time
LocalDate now = LocalDate.now(ZoneOffset.UTC);
System.out.println(now);
LocalDate expiryDate = expiresOn.toInstant()
.atOffset(ZoneOffset.UTC)
.toLocalDate();
System.out.println(expiryDate);
boolean notExpired = expiryDate.isEqual(now) || expiryDate.isAfter(now);
System.out.println("Expired? " + (notExpired ? "No" : "Yes"));
2021-01-17
2021-01-17
Expired? No
A note on taste
My taste is for avoiding unnecessary negations in variable names (and elsewhere). I’d find it simpler to do:
boolean expired = expiryDate.isBefore(now);
System.out.println("Expired? " + expired);
Expired? false
Links
Joda-Time Home
Oracle tutorial: Date Time explaining how to use java.time.
I'm trying to initialise a Joda-Time DateTime object with the hour of 12:00 here is how I do this:
public static final long MINUTE = 60 * 1000;
public static final long HOUR = 60 * MINUTE;
DateTime defaultDate = new DateTime(HOUR * 12);
System.out.print("the hour is: " + defaultDate.getHourOfDay()) // getting 14
Why I am getting 14 and not 12? Maybe Mama didn't teach me how to read clock right?!
You're specifying a number of milliseconds since the Unix epoch, which was midnight UTC.
However, you're implicitly using the system default time zone in your DateTime, and I suspect that at the Unix epoch, your system time zone was UTC+2.
If you want to use a specific time zone, you can pass that in the constructor:
DateTime defaultDate = new DateTime(HOUR * 12, DateTimeZone.UTC);
Also, rather than using your own constants, you could either use DateTimeConstants.MILLIS_PER_HOUR or use java.util.concurrent.TimeUnit for conversions.
java.time
Quoted below is a notice from the home page of Joda-Time:
Note that from Java SE 8 onwards, users are asked to migrate to java.time (JSR-310) - a core part of the JDK which replaces this project.
Solution using java.time, the modern Date-Time API:
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
public class Main {
public static void main(String[] args) {
ZonedDateTime zdt = ZonedDateTime.of(LocalDateTime.of(LocalDate.EPOCH, LocalTime.of(12, 0)), ZoneOffset.UTC);
System.out.println(zdt);
}
}
Output:
1970-01-01T12:00Z
ONLINE DEMO
A couple of important notes:
ZonedDateTime#toString removes seconds and fraction-of-second if they are zero. If you want to display them, you can use DateTimeFormatter e.g.
String formatted = zdt.format(DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSXXX", Locale.ENGLISH));
The Z in the output is the timezone designator for zero-timezone offset. It stands for Zulu and specifies the Etc/UTC timezone (which has the timezone offset of +00:00 hours).
Learn more about the modern Date-Time API from Trail: Date Time.
What went wrong with your code?
Quoted below the is description of DateTime(long) with my emphasis:
Constructs an instance set to the milliseconds from 1970-01-01T00:00:00Z using ISOChronology in the default time zone.
Your place, Israel was at an offset of +02:00 hours in 1970 and therefore the DateTime instance was instantiated with an offset of +02:00 hours.
Demo:
import java.util.concurrent.TimeUnit;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
public class Main {
public static void main(String[] args) {
DateTime defaultDate = new DateTime(TimeUnit.HOURS.toMillis(12), DateTimeZone.UTC);
System.out.println(defaultDate);
}
}
Output:
1970-01-01T12:00:00.000Z
Another thing, which you might have already noticed from the code, is that DO NOT perform calculations yourself if there is already a standard API (e.g. TimeUnit#toMillis) available for the same.
* For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.
I want to convert the current time to the time in a specific timezone with Joda time.
Is there a way to convert DateTime time = new DateTime() to a specific timezone, or perhaps to get the number of hours difference between time.getZone() and another DateTimeZone to then do time.minusHours or time.plusHours?
I want to convert the current time to the time in a specific timezone with Joda time.
It's not really clear whether you've already got the current time or not. If you've already got it, you can use withZone:
DateTime zoned = original.withZone(zone);
If you're just fetching the current time, use the appropriate constructor:
DateTime zoned = new DateTime(zone);
or use DateTime.now:
DateTime zoned = DateTime.now(zone);
Check out DateTimeZone & Interval:
DateTime dt = new DateTime();
// translate to London local time
DateTime dtLondon = dt.withZone(DateTimeZone.forID("Europe/London"));
Interval:
Interval interval = new Interval(start, end); //start and end are two DateTimes
java.time
The java.util Date-Time API and their formatting API, SimpleDateFormat are outdated and error-prone. It is recommended to stop using them completely and switch to the modern Date-Time API*.
Also, quoted below is a notice from the home page of Joda-Time:
Note that from Java SE 8 onwards, users are asked to migrate to java.time (JSR-310) - a core part of the JDK which replaces this project.
Solution using java.time, the modern Date-Time API:
import java.time.ZoneId;
import java.time.ZonedDateTime;
public class Main {
public static void main(String[] args) {
// ZonedDateTime.now() is same as ZonedDateTime.now(ZoneId.systemDefault()). In
// order to specify a specific timezone, use ZoneId.of(...) e.g.
// ZonedDateTime.now(ZoneId.of("Europe/London"));
ZonedDateTime zdtDefaultTz = ZonedDateTime.now();
System.out.println(zdtDefaultTz);
// Convert zdtDefaultTz to a ZonedDateTime in another timezone e.g.
// to ZoneId.of("America/New_York")
ZonedDateTime zdtNewYork = zdtDefaultTz.withZoneSameInstant(ZoneId.of("America/New_York"));
System.out.println(zdtNewYork);
}
}
Output from a sample run:
2021-07-25T15:48:10.584414+01:00[Europe/London]
2021-07-25T10:48:10.584414-04:00[America/New_York]
ONLINE DEMO
Learn more about the modern Date-Time API from Trail: Date Time.
* For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.
I am able to convert date to days using the below code.
SimpleDateFormat sfd = new SimpleDateFormat("yyyy-MM-dd");
String s1 = sfd.format(dateObj);
String a1 [] = s1.split("-");
int year = Integer.parseInt(a1[0].toString());
int month = Integer.parseInt(a1[1])-1;
int day = Integer.parseInt((a1[2]));
Calendar c1 = Calendar.getInstance();
c1.set(year,month,day);
days = c1.getTime().getTime()/(24*60*60*1000);
The above code works accurately in my system which is windows with timezone GMT +5.30.
However the same code in EST or Pacific timezone adds a day by 1 to final result when the time is 20.00 in the system.
What could be the issue ?
Do we need to set Timezone explicitly in the code ?
input dates does not hold any time stamp ..
is it correct to store in java.util.Date instead of java.sql.Date?
EDIT: As per Alex's comment, it's possible that the problems with the start of your code have blinded me to your real aim.
A Date represents an instant in time. That can fall on different dates depending on the time zone, but how do you want that to affect things? Do you want the number of days since the Unix epoch (which is always UTC) or the number of days since the 1st January 1970 in a particular time zone? Why do you want this "number of days" instead of a representation of a date such as LocalDate? What's the use case here?
EDIT: If you just want to know the number of days since the Unix epoch, you can skip most of this:
days = dateObj.getTime() / (24 * 60 * 60 * 1000);
You shouldn't be going through formatting at all just to get the year / month / day. Just create a Calendar, set the relevant time zone, call setTime with the dateObj you've already got, and then clear the hour/minute/second part of the calendar.
However, you should explicitly specify which time zone you want to consider - a Date represents an instant in time, which will mean different dates in different time zones.
You should also consider using Joda Time which makes all of this simpler and has a specific type for dates (LocalDate). That would also make it easy to find the number of days between the Unix epoch and a particular date without performing the division yourself.
java.time
The java.util Date-Time API and their formatting API, SimpleDateFormat are outdated and error-prone. It is recommended to stop using them completely and switch to the modern Date-Time API*.
Also, quoted below is a notice from the home page of Joda-Time:
Note that from Java SE 8 onwards, users are asked to migrate to java.time (JSR-310) - a core part of the JDK which replaces this project.
Solution using java.time, the modern Date-Time API:
You can convert the object of java.util.Date to Instant using Date#toInstant and then you can find the number of days from now until this date using ChronoUnit#between.
Demo:
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.GregorianCalendar;
public class Main {
public static void main(String[] args) {
// A sample java.util.Date
Date dateObj = GregorianCalendar.from(ZonedDateTime.of(2021, 10, 2, 22, 25, 0, 0, ZoneOffset.UTC)).getTime();
Instant instant = dateObj.toInstant();
// Difference between now and the given java.util.Date
System.out.println(ChronoUnit.DAYS.between(Instant.now(), instant));
}
}
Output:
99
ONLINE DEMO
Note that the above code calculates the number of days between two moments/instants represented in UTC. If you have date-time values local to a particular timezone, you need to specify the corresponding ZoneId.
Demo:
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.GregorianCalendar;
public class Main {
public static void main(String[] args) {
ZoneId tz = ZoneId.of("Australia/Brisbane");
// A sample java.util.Date representing the local date and time values in Australia/Brisbane
Date dateObj = GregorianCalendar.from(ZonedDateTime.of(2021, 10, 2, 22, 25, 0, 0, tz)).getTime();
// Difference between now in Australia/Brisbane and the given java.util.Date
System.out.println(ChronoUnit.DAYS.between(Instant.now().atZone(tz), dateObj.toInstant().atZone(tz)));
}
}
Output:
98
ONLINE DEMO
Learn more about the modern Date-Time API from Trail: Date Time.
* For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.
I am trying to convert a date into milliseconds with the following code:
GregorianCalendar gc = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
gc.clear();
gc.set(1900, 1, 1);
long left = gc.getTimeInMillis();
I get left=-2206310400000, but when I check here, I should get -2208988800000.
What am I doing wrong?
You're using 1 for the month number, which means February.
You mean
gc.set(1900, 0, 1);
From the docs:
month - the value used to set the MONTH calendar field. Month value is 0-based. e.g., 0 for January.
Yes, the Java date/time API is broken. If you're doing any significant amount of work in dates/times, I'd suggest you use Joda Time instead.
long left = new DateTime(1900, 1, 1, 0, 0, DateTimeZone.UTC).getMillis();
java.time
The java.util Date-Time API and their formatting API, SimpleDateFormat are outdated and error-prone. It is recommended to stop using them completely and switch to the modern Date-Time API*.
Also, quoted below is a notice from the home page of Joda-Time:
Note that from Java SE 8 onwards, users are asked to migrate to java.time (JSR-310) - a core part of the JDK which replaces this project.
Solution using java.time, the modern Date-Time API:
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
public class Main {
public static void main(String[] args) {
long epochMillis = OffsetDateTime.of(1900, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC)
.toInstant()
.toEpochMilli();
System.out.println(epochMillis);
}
}
Output:
-2208988800000
ONLINE DEMO
Learn more about the modern Date-Time API from Trail: Date Time.
* For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.