I am generating epoch timestamp in milliseconds with the following code and it works (verified with https://www.epochconverter.com/). However, when we are setting timezone with JVM option -Duser.timezone=America/Toronto then for some historical dates time offset is differ by one hour. i.e Date=1950-11-19 (yyyy-MM-dd) correct epoch milliseconds -603313200000 (Sunday, November 19, 1950 12:00:00 AM GMT-05:00) but when timezone is set with JVM options value is -603316800000 and Epoch converted shows Saturday, November 18, 1950 11:00:00 PM GMT-05:00. I have used joda time lib with JDK 10
def static Long getEpochTimeStampInMilliSeconds(String simpleDate, String dateFormat) {
Long retVal = null
try {
org.joda.time.format.DateTimeFormatter fmt = DateTimeFormat.forPattern(dateFormat)
DateTimeZone dtz2 = DateTimeZone.forID("America/Toronto")
DateTime parsedDateTime = DateTime.parse(simpleDate, fmt).withZone(dtz2)
retVal = parsedDateTime.getMillis()
} catch (Exception e) {
retVal = null
}
return retVal
}
date format is : "yyyy-MM-dd"
You need to parse with the correct time zone, so instead of calling dateTime.withZone(...) after parsing is done, you need to call dateTimeFormatter.withZone(...) before parsing with the formatter.
If the default time zone, as set by the user.timezone system property is America/Toronto, then the parsed DateTime value is already in that time zone, and dateTime.withZone(...) will do nothing.
If the default time zone is something else, then the parsed DateTime value is in that time zone, which would be a different UTC epoch millisecond value. Calling dateTime.withZone(...) will change the time zone, and hence the time value, but will not change the UTC epoch millisecond value.
def dtz2 = org.joda.time.DateTimeZone.forID("America/Toronto")
def fmt = org.joda.time.format.DateTimeFormat.forPattern(dateFormat).withZone(dtz2)
retVal = org.joda.time.DateTime.parse(simpleDate, fmt).getMillis()
UPDATE
From comment:
I am receiving -603316800000 for 1950-11-19 for all scenario but correct value is -603313200000
Lets test which value is correct, using Java-Time API:
ZoneId zone = ZoneId.of("America/Toronto");
System.out.println(Instant.ofEpochMilli(-603316800000L));
System.out.println(Instant.ofEpochMilli(-603316800000L).atZone(zone));
System.out.println(Instant.ofEpochMilli(-603313200000L));
System.out.println(Instant.ofEpochMilli(-603313200000L).atZone(zone));
Output
1950-11-19T04:00:00Z
1950-11-19T00:00-04:00[America/Toronto] ⬅ Correct value
1950-11-19T05:00:00Z
1950-11-19T01:00-04:00[America/Toronto]
As you can see, the value you get (-603316800000) is the correct value for 1950-11-19 at midnight, Toronto time.
You get offset -04:00 for Toronto, because in 1950, DST lasted until Sun, Nov 26 at 2:00 am (see https://www.timeanddate.com/time/zone/canada/toronto), so the offset is correct for Eastern Daylight Time (EDT).
Don't know why you think -603313200000 is the correct value, but it is not.
Related
I need to get the today midnight time as milliseconds. For example, today's date is 2020-03-20 and I convert this today midnight as milliseconds using the below code. If it works fine in my local environment, but the problem is when I deploy this code to the server, the server has UTC time zone, So even I pass the timezone, the milliseconds returned is wrong. After debugging I found out that this is happening due to parsing the Date, the date uses the default timeZone. I tried to set the timeZone in the Date, but the millisecond value returning is wrong even the date is 2020/03/20 00:00:00. For you to understand more below are some cases.
In the local Environment: Time Zone pass to method = "Asia/Colombo"
Date in milliseconds = 1584642600000
Converted date = Fri Mar 20 2020 00:00:00
In the Server Environment: Time Zone pass to method = "Asia/Colombo"
Date in milliseconds = 1584662400000
Converted date = Fri Mar 20 2020 05:30:00
Note that here Time Zone is the value I pass to my method parameter. I'm using java 8.
private static final String DATE_FORMAT = "yyyy/MM/dd 00:00:00";
private long getTMT(String timeZone) {
try {
ZoneId zoneId = ZoneId.of(timeZone);
ZonedDateTime currentZone = ZonedDateTime.now(zoneId);
ZonedDateTime zonedDateTime =
currentZone.withZoneSameInstant(zoneId);
DateTimeFormatter format =
DateTimeFormatter.ofPattern(DATE_FORMAT);
String formattedDateTime = zonedDateTime.format(format);
SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT);
Date date = sdf.parse(formattedDateTime);
return date.getTime();
} catch (ParseException e) {
e.printStackTrace();
}
}
Hope I explained my question clearly, please let me know your answers/comments on this.
Thank you.
Formatting to string and then parsing from it is a horrible solution, especially since you also have to also construct all these parsers every time, and your format effectively has hacks in it.
There are simpler ways to both construct a midnight ZDT:
ZonedDateTime zdt = LocalDate.now(zoneId).atTime(LocalTime.MIDNIGHT).atZone(zoneID);
And then extracting the epoch-millis value from it:
long epoch = zdt.toInstant().toEpochMilli();
Alternatively, since you know that milliseconds and nanoseconds are always 0 at midnight, this is marginally faster:
long epoch = zdt.toEpochSecond() * 1000;
The second approach wouldn't work if your time is arbitrary, as it will always ignore milli-of-second value.
This Java code, given a date as a string, is supposed to print the epoch timestamp for the same date at the midnight for the CET zone (supposing I'm not in the same zone).
public static void main(String[] args) throws ParseException {
String dateStr = "1995-06-06";
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
formatter.setTimeZone(TimeZone.getTimeZone("CET"));
Date date = formatter.parse(dateStr);
Calendar c = new GregorianCalendar();
c.setTimeZone(TimeZone.getTimeZone("CET"));
c.setTime(date);
c.set(Calendar.HOUR_OF_DAY, 0);
c.set(Calendar.MINUTE, 0);
c.set(Calendar.SECOND, 0);
c.set(Calendar.MILLISECOND, 0);
System.out.println("Epoch timestamp = " + c.getTime().getTime());
}
If I run the above program I should get printed:
Epoch timestamp = 802389600000
And I can verify it's correct here:
https://www.epochconverter.com/timezones?q=802389600&tz=Europe%2FMalta
Now, that works for most of the dates. However, there are some bizarre dates like "1975-09-19", where it doesn't work. In fact, It generates 180313200000 as a timestamp, which gives 1am and not midnight:
https://www.epochconverter.com/timezones?q=180313200&tz=Europe%2FMalta
Can you explain why? What am I missing?
Time zone discrepancy
Your Java code uses CET, which is not really a time zone (for example because most of the areas where it’s used use CEST instead for most of the year). Java translates CET to Europe/Paris. France and Paris did not use summer time (DST) in 1975. It was reintroduced in March 1976.
Your link to the epoch converter specifies Malta time zone (Europe/Malta). Malta did use summer time in 1975: it was on CEST from 20 April to 21 September that year.
This explains the difference in your results.
In Java code
If you wanted Malta time:
String dateStr = "1975-09-19";
long epochTimestamp =
LocalDate
.parse(dateStr)
.atStartOfDay(ZoneId.of("Europe/Malta"))
.toInstant()
.toEpochMilli();
System.out.println("Epoch timestamp = " + epochTimestamp);
This prints:
Epoch timestamp = 180309600000
And the epoch converter that you linked to is happy to agree:
Conversion results (180309600)
180309600 converts to Friday September 19, 1975 00:00:00 (am) in
time zone Europe/Malta (CEST) The offset (difference to Greenwich
Time/GMT) is +02:00 or in seconds 7200. This date is in daylight
saving time.
In Java do use java.time, the modern Java date and time API, for your date and time work. It is so much nicer to work with compared to the old date and time classes like SimpleDateFormat, TimeZone, Date and Calendar. Also setting the hours, etc., to 0 is not the correct way to get the first moment of the day. There are cases where summer time begins at the start of the day, so the first moment of the day is 01:00:00. Java knows that, so the atStartOfDay method will give you the correct forst moment of the day in question.
And no matter if using outdated or modern classes always specify time zone in the region/city format, for example Europe/Paris or Europe/Malta. The three, four and five letter time zone abbreviations are often ambiguous and often not true time zones, so not to be relied on.
Links
Time Zone in Paris, Île-de-France, France
Time Zone in Valletta, Malta
Oracle tutorial: Date Time explaining how to use java.time.
There seems to be a difference concerning daylight saving time between your date examples.
If I use java.time (which should always be used since Java 8), I get results with different offsets:
"+02:00" for "1995-06-06" and
"+01:00" for "1975-09-19"
This is how I got the results:
public static void main(String[] args) {
// provide two sample dates
String workingDateStr = "1995-06-06";
String failingDateStr = "1975-09-19";
// and a formatter that parses the format
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd");
// then parse them to date objects that don't know about time or zone
LocalDate workingDate = LocalDate.parse(workingDateStr, dtf);
LocalDate failingDate = LocalDate.parse(failingDateStr, dtf);
/*
* then create an objects that are aware of time and zone
* by using the parsed dates, adding a time of 00:00:00 and a zone
*/
ZonedDateTime workingZdt = ZonedDateTime.of(workingDate, LocalTime.MIN, ZoneId.of("CET"));
ZonedDateTime failingZdt = ZonedDateTime.of(failingDate, LocalTime.MIN, ZoneId.of("CET"));
// finally, print different representations of the results
System.out.println(workingZdt + " ——> " + workingZdt.toInstant().toEpochMilli());
System.out.println(failingZdt + " ——> " + failingZdt.toInstant().toEpochMilli());
}
Output:
1995-06-06T00:00+02:00[CET] ——> 802389600000
1975-09-19T00:00+01:00[CET] ——> 180313200000
That means you might be better off using specific offsets instead of zones.
This issue could be due to the timing of the introduction of Daylight Saving Time in Malta, have a look at the following code and its output:
public static void main(String[] args) {
// provide two sample dates
String failingDateStr = "1975-09-19";
// and a formatter that parses the format
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd");
// then parse them to date objects that don't know about time or zone
LocalDate failingDate = LocalDate.parse(failingDateStr, dtf);
/*
* then create an objects that are aware of time and zone
* by using the parsed dates, adding a time of 00:00:00 and a zone
*/
ZonedDateTime failingZdt = ZonedDateTime.of(failingDate, LocalTime.MIN, ZoneId.of("CET"));
// add some years to 1975 and...
for (int year = 0; year < 4; year++) {
// ... print the different representations of the result
System.out.println(failingZdt.plusYears(year) + " ——> "
+ failingZdt.plusYears(year).toInstant().toEpochMilli());
}
}
Output:
1975-09-19T00:00+01:00[CET] ——> 180313200000
1976-09-19T00:00+01:00[CET] ——> 211935600000
1977-09-19T00:00+02:00[CET] ——> 243468000000
1978-09-19T00:00+02:00[CET] ——> 275004000000
This output indicates an introduction in 1977... Is that correct?
I have a time stamp like this(form a json response) :
"/Date(1479974400000-0800)/"
I'm trying this function to convert time stamp into date:
public String getDate() {
Calendar cal = Calendar.getInstance(Locale.ENGLISH);
cal.setTimeInMillis(time);
String date = DateFormat.format("dd-MM-yyyy", cal).toString();
return date;
}
How to convert this Timestamp into Date format?
Parse directly into an OffsetDateTime
Java can directly parse your string into an OffsetDateTime. Use this formatter:
private static final DateTimeFormatter JSON_TIMESTAMP_FORMATTER
= new DateTimeFormatterBuilder()
.appendLiteral("/Date(")
.appendValue(ChronoField.INSTANT_SECONDS, 1, 19, SignStyle.NEVER)
.appendValue(ChronoField.MILLI_OF_SECOND, 3)
.appendOffset("+HHMM", "Z")
.appendLiteral(")/")
.toFormatter();
Then just do:
String time = "/Date(1479974400000-0800)/";
OffsetDateTime odt = OffsetDateTime.parse(time, JSON_TIMESTAMP_FORMATTER);
System.out.println(odt);
Output is:
2016-11-24T00:00-08:00
In your string 1479974400000 is a count of milliseconds since the epoch of Jan 1, 1970 at 00:00 UTC, and -0800 is an offset of -8 hours 0 minutes from UTC (corresponding for example to Pacific Standard Time). To parse the milliseconds we need to parse the seconds since the epoch (all digits except the last three) and then the millisecond of second (the last three digits). By specifying the width of the milliseconds field as 3 Java does this. For it to work it requires that the number is at least 4 digits and not negative, that is not within the first 999 milliseconds after the epoch or earlier. This is also why I specify in the formatter that the seconds must not be signed.
I specified Z for offset zero, I don’t know if you may ever receive this. An offset of +0000 for zero can still be parsed too.
Original answer: parse the milliseconds and the offset separately and combine
First I want to make sure the timestamp I have really lives up to the format I expect. I want to make sure if one day it doesn’t, I don’t just pretend and the user will get incorrect results without knowing they are incorrect. So for parsing the timestamp string, since I didn’t find a date-time format that would accept milliseconds since the epoch, I used a regular expression:
String time = "/Date(1479974400000-0800)/";
Pattern pat = Pattern.compile("/Date\\((\\d+)([+-]\\d{4})\\)/");
Matcher m = pat.matcher(time);
if (m.matches()) {
Instant i = Instant.ofEpochMilli(Long.parseLong(m.group(1)));
System.out.println(i);
}
This prints:
2016-11-24T08:00:00Z
If you want an old-fashioned java.util.Date:
System.out.println(Date.from(i));
On my computer it prints
Thu Nov 24 09:00:00 CET 2016
This will depend on your time zone.
It is not clear to me whether you need to use the zone offset and for what purpose. You may retrieve it from the matcher like this:
ZoneOffset zo = ZoneOffset.of(m.group(2));
System.out.println(zo);
This prints:
-08:00
The zone offset can be used with other time classes, like for instance OffsetDateTime. For example:
OffsetDateTime odt = OffsetDateTime.ofInstant(i, zo);
System.out.println(odt);
I hesitate to mention this, though, because I cannot know whether it is what you need. In any case, it prints:
2016-11-24T00:00-08:00
If by date you mean Date instance, then you can do this:
new Date(Long.parseLong("\/Date(1479974400000-0800)\/".substring(7, 20)));
I assume this info in holding the String representing an Epoch and a TimeZone
"/Date(1479974400000-0800)/"
you need to get rid off the all the not necessary parts and keeping only the
1479974400000-0800
then the epoch is 1479974400000 and I guess the Timezone is 0800
then do:
String[] allTimeInfo = "1310928623-0800".split("-");
DateFormat timeZoneFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
timeZoneFormat.setTimeZone(TimeZone.getTimeZone("Etc/GMT-8"));
Date time = new java.util.Date(Long.parseLong(allTimeInfo[0]));
System.out.println(time);
System.out.println(timeZoneFormat.format(time));
The solution works
for me is like this:
String str = obj.getString("eventdate").replaceAll("\\D+", "");
String upToNCharacters = str.substring(0, Math.min(str.length(), 13));
DateFormat timeZoneFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
timeZoneFormat.setTimeZone(TimeZone.getTimeZone("GMT-8"));
Date time = new java.util.Date(Long.parseLong(upToNCharacters));
// System.out.println(time);
model.setDate(String.valueOf(timeZoneFormat.format(time)));
Use time variable where you want
I must say that working with timezones is my nemesis!
In my DB(PostGres) I have a field of type "timestamp without time" zone.
The value I save in it is in UTC time.
What I want to do is displaying the value according to the default timezone of my machine.
So, when I retrieve the value from the DB I first have to "say" that this is a UTC time and therefore I set its time zone to UTC:
private Date lastUpdateToUTC( Date myDate)
{
if ( myDate!= null )
{
SimpleDateFormat dateFormat = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss" );
dateFormat.setTimeZone( TimeZone.getTimeZone( "UTC" ) );
System.out.println("orig: " + dateFormat.format(myDate));
try
{
Date convertedDate = dateFormat.parse( dateFormat.format( myDate) );
dateFormat.setTimeZone( TimeZone.getDefault( ) );
System.out.println("converted: " + dateFormat.format(convertedDate));
return dateFormat.parse( dateFormat.format( convertedDate ) );
....
Let say myDate is 2013-08-05 10:44:08.
What I'm expecting is that the first output is 2013-08-05 10:44:08 and the second 2013-08-05 12:44:08.
Instead I get 2013-08-05 08:44:08 and 2013-08-05 10:44:08...
Where is my error in reasoning?
What should I do to have what I'm expecting?
java.util.Date doesn't support timezone.
For the first output, you set dateFormat to UTC. When you provide the Date to dateFormat, it converts it to UTC. It has no way of knowing which timezone your Date is, it assumes it's local timezone. So looks like in your case it's GMT+2, dateFormat subtracts 2 to make it UTC.
Then you do format - parse, which should not have any affect, then you change timezone to your local timezone and print your original Date value. (This time w/o any conversion).
Your current local timezone is GMT+2, that's why the first output would be T+2 hours.
In order to print the date in UTC timezone, the date will minus 2 hours because of +2 hour offset.
intput: 2013-08-05 10:44:08 // GMT+2 timezone
orig: 2013-08-05 08:44:08 // UTC timezone
converted: 2013-08-05 10:44:08 // default timezone: GMT+2 timezone
You can print the current local timezone to know the offset using dateFormat.getTimeZone().
Take myself as an example, my local timezone is GMT+8, the offset would be 8 * 60 * 60 * 1000 = 28800000 milliseconds.
System.out.println("My Current Timezone: " + dateFormat.getTimeZone());
// My Current Timezone: sun.util.calendar.ZoneInfo[id="Asia/Taipei",offset=28800000,dstSavings=0,useDaylight=false,transitions=42,lastRule=null]
In my web application, I am storing all end-user's date information as UTC format in database, and before showing it to them, just converting the UTC dates to timezones of their choice.
I am using this method to convert a localtime to UTC time (while storing):
public static Date getUTCDateFromStringAndTimezone(String inputDate, TimeZone timezone){
Date date
date = new Date(inputDate)
print("input local date ---> " + date);
//Returns the number of milliseconds since January 1, 1970, 00:00:00 GMT
long msFromEpochGmt = date.getTime()
//gives you the current offset in ms from GMT at the current date
int offsetFromUTC = timezone.getOffset(msFromEpochGmt)*(-1) //this (-1) forces addition or subtraction whatever is reqd to make UTC
print("offsetFromUTC ---> " + offsetFromUTC)
//create a new calendar in GMT timezone, set to this date and add the offset
Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"))
gmtCal.setTime(date)
gmtCal.add(Calendar.MILLISECOND, offsetFromUTC)
return gmtCal.getTime()
}
And this method for converting UTC date to local (while showing):
public static String getLocalDateFromUTCDateAndTimezone(Date utcDate, TimeZone timezone, DateFormat formatter) {
printf ("input utc date ---> " + utcDate)
//Returns the number of milliseconds since January 1, 1970, 00:00:00 GMT
long msFromEpochGmt = utcDate.getTime()
//gives you the current offset in ms from GMT at the current date
int offsetFromUTC = timezone.getOffset(msFromEpochGmt)
print("offsetFromUTC ---> " + offsetFromUTC)
//create a new calendar in GMT timezone, set to this date and add the offset
Calendar localCal = Calendar.getInstance(timezone)
localCal.setTime(utcDate)
localCal.add(Calendar.MILLISECOND, offsetFromUTC)
return formatter.format(localCal.getTime())
}
My question is, if the end-user is within a DST zone, then how do I improve the methods to accommodate their local clock times perfectly.
If you use a custom time zone ID, like GMT+10 you will get TimeZone that does not support DST, eg TimeZone.getTimeZone("GMT+10").useDaylightTime() returns false. But if you use a supported ID eg "America/Chicago" you will get a TimeZone that supports DST. The full list of supported IDs is returned by TimeZone.getAvailableIDs(). Internally Java stores time zone info in jre/lib/zi.