DateTime Manipulation in C# vs Java - java

I'm new to Java. I have a time I am getting from a web-page, this is in the "hh:mm" format (not 24 hour). This comes to me as a string. I then want to combine this string with todays date in order to make a Java Date I can use.
In C#:
string s = "5:45 PM";
DateTime d;
DateTime.TryParse(s, out d);
in Java I have attempted:
String s = "5:45 PM";
Date d = new Date(); // Which instantiates with the current date/time.
String[] arr = s.split(" ");
boolean isPm = arr[1].compareToIgnoreCase("PM") == 0;
arr = arr[0].split(":");
int hours = Integer.parseInt(arr[0]);
d.setHours(isPm ? hours + 12 : hours);
d.setMinutes(Integer.parseInt(arr[1]));
d.setSeconds(0);
Is there a better way to achieve what I want?

Is there a better way to achieve what I want?
Absolutely - in both .NET and in Java, in fact. In .NET I'd (in a biased way) recommend using Noda Time so you can represent just a time of day as a LocalTime, parsing precisely the pattern you expect.
In Java 8 you can do the same thing with java.time.LocalTime:
import java.time.*;
import java.time.format.*;
public class Test {
public static void main(String[] args) {
String text = "5:45 PM";
DateTimeFormatter format = DateTimeFormatter.ofPattern("h:mm a");
LocalTime time = LocalTime.parse(text, format);
System.out.println(time);
}
}
Once you've parsed the text you've got into an appropriate type, you can combine it with other types. For example, to get a ZonedDateTime in the system time zone, using today's date and the specified time of day, you might use:
ZonedDateTime zoned = ZonedDateTime.now().with(time);
That uses the system time zone and clock by default, making it hard to test - I'd recommend passing in a Clock for testability.
(The same sort of thing is available in Noda Time, but slightly differently. Let me know if you need details.)
I would strongly recommend against using java.util.Date, which just represents an instant in time and has an awful API.
The key points here are:
Parse the text with a well-specified format
Parse the text into a type that represents the information it conveys: a time of day
Combine that value with another value which should also be carefully specified (in terms of clock and time zone)
All of these will lead to clear, reliable, testable code. (And the existing .NET code doesn't meet any of those bullet points, IMO.)

To parse the time, you can do as explained in #Jon Skeet's answer:
String input = "5:45 PM";
DateTimeFormatter parser = DateTimeFormatter.ofPattern("h:mm a", Locale.ENGLISH);
LocalTime time = LocalTime.parse(input, parser);
Note that I also used a java.util.Locale because if you don't specify it, it'll use the system's default locale - and some locales can use different symbols for AM/PM field. Using an explicit locale avoids this corner-case (and the default locale can also be changed, even at runtime, so it's better to use an explicit one).
To combine with the today's date, you'll need a java.time.LocalDate (to get the date) and combine with the LocalTime, to get a LocalDateTime:
// combine with today's date
LocalDateTime combined = LocalDate.now().atTime(time);
Then you can format the LocalDateTime using another formatter:
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm");
System.out.println(combined.format(fmt));
The output is:
16/08/2017 17:45
If you want to convert the LocalDateTime to a java.util.Date, you must take care of some details.
A java.util.Date represents the number of milliseconds since 1970-01-01T00:00Z (aka Unix Epoch). It's an instant (a specific point in time). Check this article for more info.
So, the same Date object can represent different dates or times, depending on where you are: think that, right now, at this moment, everybody in the world are in the same instant (the same number of milliseconds since 1970-01-01T00:00Z), but the local date and time is different in each part of the world.
A LocalDateTime represents this concept of "local": it's a date (day, month and year) and a time (hour, minute, second and nanosecond), but without any relation to a specific timezone.
The same LocalDateTime object can represent different instants in time in different timezones. So, to convert it to a Date, you must define in what timezone you want it.
One option is to use the system's default timezone:
// convert to system's default timezone
ZonedDateTime atDefaultTimezone = combined.atZone(ZoneId.systemDefault());
// convert to java.util.Date
Date date = Date.from(atDefaultTimezone.toInstant());
But the default can vary from system/environment, and can also be changed, even at runtime. To not depend on that and have more control over it, you can use an explicit zone:
// convert to a specific timezone
ZonedDateTime zdt = combined.atZone(ZoneId.of("Europe/London"));
// convert to java.util.Date
Date date = Date.from(zdt.toInstant());
Note that I used Europe/London. The API uses IANA timezones names (always in the format Region/City, like America/Sao_Paulo or Europe/Berlin).
Avoid using the 3-letter abbreviations (like CST or PST) because they are ambiguous and not standard.
You can get a list of available timezones (and choose the one that fits best your system) by calling ZoneId.getAvailableZoneIds().
And there's also the corner cases of Daylight Saving Time (when a LocalDateTime can exist twice or can't exist due to overlaps and gaps). In this case, Jon's solution using ZonedDateTime avoids this problem).

Related

Java how to remove nth minute from given time string without considering time zone

I tried to remove minute from given time, but some how it is converting time to my local time zone
String timeStamp="20180623 05:58:15" ;
dateFormat inputFormatter = new SimpleDateFormat("yyyyMMdd HH:mm:ss");
Date date = inputFormatter.parse(timeStamp);
date.setMinutes(-2);
logger.info("Before converting : "+date);
DateFormat dateFormat = new SimpleDateFormat("yyyyMMdd HH:mm:ss");
Here it is converting to my local time and subtracting 2 minutes from given time, but I don`t want to check the time zone here instead, what ever time I give it should just subtract 2 minutes.
Start with understanding into how Date works. When you do...
logger.info("Before converting : "+date);
The Date class uses it's toString method to format the the date/time information represented by the Date class into a human readable format. It doesn't "convert" the date/time value in anyway
So taking your code from above (and reworking it so it works), it outputs...
Before converting : Sat Jun 23 04:58:15 AEST 2018
20180623 04:58:15
on my machine - why are the values the same? Because the input doesn't have any time zone information, so the time is likely been treated as been in the machines local timezone (and the value is simply been formatted for output).
Date is just a container for the number of milliseconds since the Unix Epoch, it's format agnostic - meaning it carries not formatting information.
Date is also effectively deprecated - not to mention that setDate is also very much deprecated
A better (starting point) overall is to make use the newer date/time API introduced in Java 8 (and which has back port support for earlier versions of the API)
String timeStamp = "20180623 05:58:15";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd HH:mm:ss", Locale.ENGLISH);
LocalDateTime ldt = LocalDateTime.parse(timeStamp, formatter);
ldt = ldt.minusMinutes(2);
System.out.println(ldt);
System.out.println(ldt.format(formatter));
This will output...
2018-06-23T05:56:15
20180623 05:56:15
The input and the output are still consider as been in the machines local time zone.
but I don`t want to check the time zone here instead, what ever time I give it should just subtract 2 minutes
Just remember, the API still needs to have some concept of time zone, weather it's the local time zone or UTC/GMT, but since your input doesn't provide any kind of information, you need to make a choice over "how" best to handle that issue. The example above just "assumes" local time, but you could use ZonedDateTime and convert it to "common" time zone from which your operations are executed or, better yet, make all your strings carry time zone information
Oh, and for the love of my sanity, stop managing date/time values in String format - get them into an appropriate container as soon as possible and manage them from there - I've spent a week wrangling inappropriately formatted date strings and I'm not happy Jan, not happy

Time zone of a country, whether day light savings is in effect

Consider the below code:
if(strTimeZoneCd.equals("A"))
locTz = TimeZone.getTimeZone("AST");
else if(strTimeZoneCd.equals("M"))
locTz = TimeZone.getTimeZone("MST");
else if(strTimeZoneCd.equals("P"))
locTz = TimeZone.getTimeZone("PST");
else if(strTimeZoneCd.equals("H"))
locTz = TimeZone.getTimeZone("HST");
else if(strTimeZoneCd.equals("C"))
locTz = TimeZone.getTimeZone("CST");
else if(strTimeZoneCd.equals("E"))
locTz = TimeZone.getTimeZone("EST");
else if(strTimeZoneCd.equals("G"))
locTz = TimeZone.getTimeZone("GMT");
else if(strTimeZoneCd.equals("Y"))
locTz = TimeZone.getTimeZone("AKST");
Here if I am passing A, it will give me AST. But instead of that I need to determine if I should return AST or ADT.
I need to determine if AST is under daylight saving now. If it is under daylight saving, I can return ADT and if it is not I can return AST. But I am not getting how to determine whether that timezone is under daylight saving or not.
Can someone please help me?
First of all, avoid using the short abbreviations for timezones names (like CST, PST or CEST) because they are ambiguous and not standard.
CST, for example, is used by more than one timezone: it can be "Central Standard Time", "China Standard Time" or "Cuba Standard Time". And each one has different rules regarding Dayligh Saving Time, so using the abbreviation might not necessarily get the results you expect.
The TimeZone class assumes some defaults for those short names (all arbitrary choices, as any default is) and also has the bizarre behaviour of returning GMT when the name is unknown.
To avoid those ambiguities, it's better to use IANA timezones names (always in the format Region/City, like Asia/Kolkata or America/New_York).
You can get a list of available timezones (and choose the one that fits best your system) by calling TimeZone.getAvailableIDs().
Then you can use the inDaylightTime() method, as already explained in the other answers.
Another alternative is to use a formatter, because it checks automatically if it's in Daylight Saving Time and prints the zone short name. I also use a java.util.Locale to indicate that the names should be in English (I'm not sure if different languages affect the short zone names, it's a "just in case" approach):
// formatter with `z` (zone short name)
SimpleDateFormat sdf = new SimpleDateFormat("z", Locale.ENGLISH);
// set timezone in the formatter
sdf.setTimeZone(TimeZone.getTimeZone("America/New_York"));
// prints the zone name for the current date
System.out.println(sdf.format(new Date()));
The code above prints EDT (because today, September 13th 2017, New York is in Daylight Saving Time and the abbreviation used is EDT).
In this case, you could create the formatter, and use your if logic to set the correct timezone in it. Then, the formatter takes care of checking if the date is in Daylight Saving Time, returning the correct abbreviation.
Java new Date/Time API
The old classes (Date, Calendar and SimpleDateFormat) have lots of problems and design issues, and they're being replaced by the new APIs.
If you're using Java 8, consider using the new java.time API. It's easier, less bugged and less error-prone than the old APIs.
If you're using Java <= 7, you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, you'll also need the ThreeTenABP (more on how to use it here).
The code below works for both.
The only difference is the package names (in Java 8 is java.time and in ThreeTen Backport (or Android's ThreeTenABP) is org.threeten.bp), but the classes and methods names are the same.
First I create a DateTimeFormatter with the z pattern (that corresponds to zone short name) and English locale.
Then I use a ZonedDateTime, which represents a date and time in a specific timezone, and the now() method to get the current date/time. I also use ZoneId to especify the timezone I want:
// create formatter for short zone name and English locale
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("z", Locale.ENGLISH);
// format current date in New York timezone
System.out.println(fmt.format(ZonedDateTime.now(ZoneId.of("America/New_York"))));
This also prints EDT as well. You could apply the same if logic above to define which timezone will be used in ZoneId.of(). Just remind to not use the short names (CST, etc), because the new API is not so lenient as the old one.
TimeZone assumes lots of arbitrary defaults and returns "GMT" when the zone doesn't exist, but ZoneId will throw an exception if you try to get an invalid zone (some abbreviations should work for retro-compatibility reasons, but the defaults are arbitrary as well, and you should avoid them).
Custom map of zone names
You can optionally create a custom map of timezone names, so you don't need to make lots of if clauses to determine the corresponding zone. Something like this:
// create custom map of zone names
Map<String, String> customZones = new HashMap<>();
// map "E" to New York
customZones.put("E", "America/New_York");
// map "G" to GMT
customZones.put("G", "GMT");
...
// create timezone using the custom map ("E" will create "America/New_York" zone)
ZoneId zone = ZoneId.of("E", customZones);
// format current date in specified timezone
System.out.println(fmt.format(ZonedDateTime.now(zone)));
work around this:
For any particular TimeZone
TimeZone tz = TimeZone.getTimeZone("EST");
boolean inDs = tz.inDaylightTime(new Date());
You can use this program to determine whether that timezone is under day light saving or not.
Also see this link.
TimeZone.getTimeZone("CST") returns GMT
import java.util.Date;
import java.util.TimeZone;
public class test {
public static void main(String[] args) {
System.out.println(TimeZone.getTimeZone("EST").inDaylightTime( new Date() ));
System.out.println(TimeZone.getTimeZone( "GMT-9:00").inDaylightTime( new Date() ));
}
}

Trying to parse a datetime in PDT to a ZonedDateTime representation

How should I parse this datetime value that is in the PDT timezone?
06/24/2017 07:00 AM (PDT)
I want to maintain the timezone so that I can then represent the time in other timezones depending on the website visitors preferences.
I tried using ZonedDateTime but I get a parse error:
java.time.ZonedDateTime.parse("06/24/2017 07:00 AM (PDT)")
The error is:
java.time.format.DateTimeParseException: Text '06/24/2017 07:00 AM (PDT)' could not be parsed at index 0
at java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:1949)
at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1851)
at java.time.ZonedDateTime.parse(ZonedDateTime.java:597)
at java.time.ZonedDateTime.parse(ZonedDateTime.java:582) ... 29 elided
Also, do you agree that I should be using a ZonedDateTime?
Since your format is non-standard, you need to specify it to the parser:
ZonedDateTime.parse(
"06/24/2017 07:00 AM (PDT)",
DateTimeFormatter.ofPattern("MM/dd/yyyy HH:mm a (zzz)")
);
The parse method expects a String in a specific format, like 2007-12-03T10:15:30+01:00[Europe/Paris]. As your input is in a different format, you need a DateTimeFormatter.
One detail to notice is that the API uses IANA timezones names (always in the format Continent/City, like America/Sao_Paulo or Europe/Berlin).
Avoid using the 3-letter abbreviations (like CST or PST) because they are ambiguous and not standard.
The API makes some exceptions with specific IDs and provides some defaults for them. For PDT, it defaults to America/Los_Angeles.
Another detail is that in the example below I used lowercase hh in the pattern: the format has AM/PM indication, so I think that hh is the correct pattern, as its value is from 1 to 12 (the common values when there's the AM/PM indicator).
If you use uppercase HH, it allows values from 0 to 23 (and it's not common to use this with AM/PM), and it will throw an exception if the input contains an hour like 07:00 PM.
So the code will be like:
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("MM/dd/yyyy hh:mm a (zzz)");
ZonedDateTime z = ZonedDateTime.parse("06/24/2017 07:00 AM (PDT)", fmt);
System.out.println(z);
The output is:
2017-06-24T07:00-07:00[America/Los_Angeles]
But not all the 3-letter timezone names will be recognized by the API and will throw an exception.
Anyway, there are other timezones that also are in PDT (like America/Vancouver) - you can get a list of all by calling ZoneId.getAvailableZoneIds(). If you want to use a different timezone as the default, you can create a set of preferred zones and build a formatter with this set:
Set<ZoneId> preferredZones = new HashSet<>();
// set America/Vancouver as preferred zone
preferredZones.add(ZoneId.of("America/Vancouver"));
DateTimeFormatter fmt = new DateTimeFormatterBuilder()
// pattern
.appendPattern("MM/dd/yyyy hh:mm a (")
// append timezone with set of prefered zones
.appendZoneText(TextStyle.SHORT, preferredZones)
// finish the pattern
.appendPattern(")")
// create formatter
.toFormatter();
System.out.println(ZonedDateTime.parse("06/24/2017 07:00 AM (PDT)", fmt));
The API will use the preferred zones set (in this case, America/Vancouver) instead of the default (America/Los_Angeles). The output will be:
2017-06-24T07:00-07:00[America/Vancouver]
It's not clear where the input String's come from. If you can't control their format, then you have no choice: they need to be parsed this way. Then you can convert it to another timezone using the withZoneSameInstant method:
// parse the input string
ZonedDateTime z = ZonedDateTime.parse("06/24/2017 07:00 AM (PDT)", fmt);
// convert to another timezone
ZonedDateTime other = z.withZoneSameInstant(ZoneId.of("America/Sao_Paulo")); // 2017-06-24T11:00-03:00[America/Sao_Paulo]
The value of other will be 2017-06-24T11:00-03:00[America/Sao_Paulo].
But if you can control the output, it's always better (IMO) to internally work with UTC (java.time.Instant), and convert to some timezone only when displaying to users:
// convert ZonedDateTime to instant
ZonedDateTime z = // parse input
// convert to UTC (Instant is always in UTC)
Instant instant = z.toInstant();
// internally work with instant (as it's always in UTC)
// convert instant to some timezone only when necessary (like displaying to users)
ZonedDateTime converted = instant.atZone(ZoneId.of("Europe/London"));
The error you get is well covered in the other answers already.
Also, do you agree that I should be using a ZonedDateTime?
Yes and no. Your string should definitely be parsed into a ZonedDateTime. I recommend you convert it to an Instant and store this. Then when you need to present it to a user according to his/her time zone preference, you may either convert the Instant to a ZonedDateTime again or just format it using a DateTimeFormatter with the desired default time zone.
Why do it this way? First, common practice is to store Instants. Some prefer to store just milliseconds since the epoch, I think this some (often misunderstood) performance measure. Certainly such milliseconds I quite unreadable while Instants can be deciphered on eye-sight, at least roughly. The only other alternative I respect is when you know for certain that your application will never need to be concerned with a time zone (does this ever happen?), then sometimes LocalDateTime is used for storage.
If I understand your situation correctly, you need to store the point in time for display into multiple time zones. You don’t need to store the time zone in which the time was originally entered (like PDT, except PDT is not really a full time zone). Instant is time zone neutral, which is one reason I prefer it over storing the time in some time zone, as ZonedDateTime would. Also an Instant is simpler conceptually, and my guess is that it is also simpler implementation-wise.
There are a couple of much better answers here: Best practices with saving datetime & timezone info in database when data is dependant on datetime.

How to keep original timezone with JodaTime

I have date in String format I need to parse. The format is as following with timezone from all over the world :
String stringDate = "2016-04-29 12:16:49.222+04:30";
String pattern = "yyyy-MM-dd HH:mm:ss.SSSZ";
It seems that java.util.Date doesn't accept timezone with : separator. So I'm trying with Jodatime library :
DateTime formattedDate = DateTimeFormat.forPattern(pattern).parseDateTime(stringDate);
LocalDateTime formattedDate2 = DateTimeFormat.forPattern(pattern).parseLocalDateTime(stringDate);
MutableDateTime formattedDate3 = DateTimeFormat.forPattern(pattern).parseMutableDateTime(stringDate);
System.out.println(formattedDate);
System.out.println(formattedDate2);
System.out.println(formattedDate3);
These lines output :
2016-04-29T09:46:49.222+02:00
2016-04-29T12:16:49.222
2016-04-29T09:46:49.222+02:00
As far as I understand the formatter modify output timezone to comply on mine (I'm in Paris, UTC+2), but I want the output keep its original timezone. Is it possible to do it with Jodatime library? Or should I change for another?
Edit :
Actually I need to get a Date object on which the timezone offset would be 270 (the timezone offset of the stringDate : 4 hour and 30 minutes) in place of 120 (my local timezone offset):
System.out.println(formattedDate.toDate().getTimezoneOffset()); // I expect 270 but I get 120
What you missed is DateTimeFormatter#withOffsetParsed:
Returns a new formatter that will create a datetime with a time zone equal to that of the offset of the parsed string.
Otherwise the formatter will parse it into your local time zone (surprising, I know).
#Test
public void preserveTimeZone() {
String stringDate = "2016-04-29 12:16:49.222+04:30";
String pattern = "yyyy-MM-dd HH:mm:ss.SSSZ";
DateTime dt = DateTimeFormat.forPattern(pattern).withOffsetParsed().parseDateTime(stringDate);
System.out.println(dt); // prints "2016-04-29T12:16:49.222+04:30"
}
As for your edit - java.util.Date does not hold time zone information and the deprecated getTimezoneOffset() method only
Returns the offset, measured in minutes, for the local time zone relative to UTC that is appropriate for the time represented by this Date object.
So you'd better use Joda Time or java.time classes to handle time zones properly.
When I run the same code that you have posted, I end up with
2016-04-29T02:46:49.222-05:00
2016-04-29T12:16:49.222
2016-04-29T02:46:49.222-05:00
which if you will notice, has different hour values AND time-zone values. However, if you look at their millis:
System.out.println(formattedDate.getMillis());
System.out.println(formattedDate2.toDateTime().getMillis());
System.out.println(formattedDate3.getMillis());
you'll see the output
1461916009222
1461950209222
1461916009222
So they have the same epoch time, but are printed out differently. This is due to the mechanism of toString() on DateTime objects, and how they are to be interpreted.
DateTime and LocalDateTime(MutableDateTime is just a mutable version of DateTime) deal with the same epoch time in different ways. LocalDateTime will always assume that epoch time is UTC time(per the javadoc for LocalDateTime), while DateTime will assume that epoch is represented in the time zone of the Chronology which it holds(per the javadoc again). If the TimeZone is not specified at construction time, then the Chronology will assume that you want the timezone of your default Locale, which is set by the JVM. In your case, the default Locale is Paris France, while mine is St. Louis USA. Paris currently holds a +2:00 time zone offset, while St. Louis has -5:00, leading to the different time zone representations when we print it.
To get even more annoying, those offsets can change over time. If I come back in 6 months and try to answer this again, my values will show -6:00 (stupid Daylight savings time!)
The important thing to remember is that these two dates have the same epoch time: we are talking about the same instant in time, we are just representing that time differently when we print it out.
If you want to use a different time zone for representing the output of the parse result, then you can set the DateTimeZone during formatting using DateTimeFormat.withZone() or DateTimeFormat.withLocale:
DateTimeFormatter sdf = DateTimeFormat.forPattern(pattern).withZone(DateTimeZone.forOffsetHoursMinutes(4,30));
System.out.println(formattedDate.getMillis());
System.out.println(formattedDate2.toDateTime().getMillis());
System.out.println(formattedDate3.getMillis());
which will print
2016-04-29 12:16:49.222+0430
2016-04-29 12:16:49.222
2016-04-29 12:16:49.222+0430
notice that the LocalDateTime version still prints out without the TimeZone. That's kind of the feature of LocalDateTime: it is represented without having to deal with all this business.
So that is why your printing values look weird. To further your question about getting a java.util.Date object from the parsed DateTime object: toDate will give you a java.util.Date which represents the same epoch time. However, java.util.Date behaves similarly to DateTime, in that unless otherwise stated, it will use the TimeZone of the default Locale. If you know the Locale ahead of time, then you can use the toDate(Locale) method to ensure you use that Locale's TimeZone offset.
It gets a lot harder if you don't know the TimeZone ahead of time; in the past, I've had to hand-parse the TimeZone hour and minute offsets to determine the proper TimeZone to use. In this exact case that's not too difficult, since the last 6 characters are extremely well-formed and regular(unless, of course, they aren't :)).

Convert UTC date into milliseconds

I am not interested in what the current UTC time is in milliseconds, nor do I need to mess with timezones. My original date is already stored as a UTC timestamp.
I have a date stored in a database in UTC time, "2012-06-14 05:01:25".
I am not interested in the datetime, but just the date portion of the it. So, after retrieving the date in Java, and excluding the hours, minutes, and seconds - I am left with "2012-06-14".
How can I convert this into UTC milliseconds?
EDIT: I'd missed the "ignoring the time of day" part. It's now present, but near the end...
The simplest approach is probably to use SimpleDateFormat, having set the time zone appropriately:
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US);
format.setTimeZone(TimeZone.getTimeZone("UTC"));
Date date = format.parse(text);
long millis = date.getTime();
(Setting the time zone is the important bit here, as otherwise it will interpret the value to be in the local time zone.)
Alternatively, if you're doing anything less trivial than this, use Joda Time which is a much better date/time API. In particular, SimpleDateFormat isn't thread-safe whereas DateTimeFormatter is:
// This can be reused freely across threads after construction.
DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")
.withLocale(Locale.US)
.withZoneUTC();
// Option 1
DateTime datetime = formatter.parseDateTime(text);
long millis = dateTime.getMillis();
// Option 2, more direct, but harder to diagnose errors
long millis = formatter.parseMillis(text);
Now so far, we've parsed the whole whole caboodle. The easiest way of ignoring the date part is just to round it off - after all, Java doesn't observe leap seconds, so we can just truncate it:
long millisPerDay = 24L * 60L * 60L * 1000L; // Or use TimeUnit
long dayMillis = (millis / millisPerDay) * millisPerDay;
That will "round towards 1970" so if you have a date before 1970 it will round to the end of the day - but I suspect that's unlikely to be a problem.
With the Joda Time version you could just use this instead:
DateTime dateTime = formatter.parseDateTime(text);
long millis = dateTime.toLocalDate().getLocalMillis();
I would personally not go with the idea of just taking a substring. Even though you're not actually interested in preserving the hour/minute/second, I think it's appropriate to parse what you've been given and then throw away information. Aside from anything else, it makes your code fail appropriately with bad data, e.g.
"2012-06-100"
or
"2012-06-14 25:01:25"
indicate problems in whatever's supplying you data, and it's good to spot that rather than to continue blindly just because the first 10 characters are okay.
UPDATE: See the modern solution using java.time classes in the correct Answer by Ole V.V..
Simpler
The answer by Jon Skeet is correct. And he makes a good point about including, rather than truncating, the time-of-day info while parsing.
However, his code could be simplified. Especially so because Joda-Time gained an important new method in the latest versions: withTimeAtStartOfDay. This method supplants all the "midnight"-related classes and methods which are now deprecated.
Specifying a Locale is a good habit, as shown in his code. But in this particular case a Locale is not necessary.
His answer correctly suggests the Joda-Time library, far superior to using java.util.Date, .Calendar, and java.text.SimpleTextFormat. Those classes are notoriously troublesome, and should be avoided. Instead use either Joda-Time or the new java.time package built into Java 8 (inspired by Joda-Time, defined by JSR 310).
First Moment Of The Day
You cannot ignore time-of-day if what you want is a count of milliseconds-since-epoch. I suspect what you want is to change the time to first moment of the day. In UTC, this always means the time 00:00:00.000. But note that in local time zones, the first moment may be a different time because of Daylight Saving Time and possibly other anomalies.
ISO 8601
Your string is nearly in standard ISO 8601 format, but we need to swap a T for the SPACE in the middle. Then we can feed the resulting string directly to Joda-Time as Joda-Time has built-in formatters used by default for standard strings.
Example Code
The following example code assumes the intent of your question is to parse a string as a date-time value in UTC time zone, adjust the time to the first moment of the day, and then convert to number of milliseconds since Unix epoch (beginning of 1970 in UTC).
String inputRaw = "2012-06-14 05:01:25";
String input = inputRaw.replace( " ", "T" ); // Replace SPACE with a 'T'.
DateTime dateTime = new DateTime( input, DateTimeZone.UTC ); // Parse, assuming UTC.
DateTime dateTimeTopOfTheDay = dateTime.withTimeAtStartOfDay(); // Adjust to first moment of the day.
long millisecondsSinceUnixEpoch = dateTimeTopOfTheDay.getMillis(); // Convert to millis. Use a 'long', not an 'int'.
java.time and JDBC 4.2
I am providing the modern answer. These days (and for the last several years) you should use java.time, the modern Java date and time API, for your date and time work. And since JDBC 4.2 you can directly retrieve java.time objects from your database (and also store them into it). A modern JPA implementation (Hibernate at least since Hibernate 5) will be happy to do the same. So forget about SimpleDateFormat, Date and other old classes used in most of the old answers. The mentioned ones are poorly designed, and java.time is so much nicer to work with.
Retrieve proper date-time objects from your database
I also recommend that you don’t retrieve your UTC time as a string from the database. If the datatype in SQL is timestamp with time zone (recommended for UTC times), retrieve an OffsetDateTime. For example:
PreparedStatement pStmt = yourDatabaseConnection
.prepareStatement("select utc_time from your_table where id = 7;");
ResultSet rs = pStmt.executeQuery();
if (rs.next()) {
OffsetDateTime utcDateTime = rs.getObject("utc_time", OffsetDateTime.class);
long millisecondsSinceEpoch = utcDateTime.truncatedTo(ChronoUnit.DAYS)
.toInstant()
.toEpochMilli();
System.out.println("Milliseconds since the epoch: " + millisecondsSinceEpoch);
}
If the type in SQL is dateTime or timestamp without time zone, we probably need to retrieve a LocalDateTime instead (details depending on your JDBC driver and the time zone of your database session). It goes in the same manner. For converting your LocalDateTime to OffsetDateTime, see the conversion below.
If you need to convert from a string
If you cannot avoid getting your UTC time as a string as in the question, parse it into a LocalDateTime and convert from there. For example:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss");
String utcTimeString = "2012-06-14 05:01:25";
long millisecondsSinceEpoch = LocalDateTime.parse(utcTimeString, formatter)
.atOffset(ZoneOffset.UTC)
.toInstant()
.toEpochMilli();
System.out.println("Milliseconds since the epoch: " + millisecondsSinceEpoch);
Output:
Milliseconds since the epoch: 1339650085000
Link
Oracle tutorial: Date Time explaining how to use java.time.
Use the Date object in combination with SimpleDateFormat.
There is a method named getTime() in Date which will return the milliseconds for you.
Example that solves your problem :
Date truc = new SimpleDateFormat( "y-m-d").parse( "2010-06-14");
System.out.println(truc.getTime());
SimpleDateFormat ft = new SimpleDateFormat ("yyyy-MM-dd"); //or whatever format you have
Date t = ft.parse('2014-03-20');
String result = String.format("%tQ", t);
System.out.printf("%tQ", t);
There are two methods here:
you put the result milliseconds into a variable result
printing it straight off.
I use a simple and straight forward approach:
Date date = new Date(utcDateInString);
long utcDateInMilliSeconds = date.getTime();

Categories