I have:
a joda LocalDate, so it has no time information and just date
a string which contains time and zone information, like "14:20 CEST"
Either of them can be absent (Scala's Option).
How can I combine these two to get joda LocalDateTime, i.e. entity representing only date and time with no timezone?
To combine these 2 options, the natural way is to use flatMap method like this:
val onlyDateOption: Option[LocalDate] = ???
val timeAndZoneOption: Option[String] = ???
val result: Option[LocalDateTime] = onlyDateOption.flatMap { onlyDate =>
timeAndZoneOption.map { timeAndZone =>
// Some logic here to build the LocalDateTime from onlyDate and timeAndZone
}
}
Which can also be written with for-comprehension in more readible way:
val result: Option[LocalDateTime] = for {
onlyDate <- onlyDateOption
timeAndZone <- timeAndZoneOption
} yield {
// Some logic here to build the LocalDateTime from onlyDate and timeAndZone
}
Now, how to use Joda to build what you're expecting can probably be done in various different ways, one could be:
onlyDate
.toLocalDateTime(LocalTime.MIDNIGHT)
.withHourOfDay(...) // hour extracted from the string somehow
.withMinuteOfHour(...) // minute extracted from the string somehow
I'm not familiar with Joda API, there is probably another easier way
How to combine LocalDate and String using Joda-Time
You have already got an answer treating the use of Option in detail. Here I want to go into more detail with the combination of your LocalDate and your String into a LocalDateTime using Joda-Time. I understand that you are getting a Joda-Time LocalDate from legacy code and need to return a Joda-Time LocalDateTime to legacy code. I am assuming that you know the time zone the abbreviation of which is in the string. I think that you should validate that abbreviation since Central European Time uses the abbreviation CET during the standard time part of the year and CEST during summer time (DST). Excuse my Java code.
DateTimeUtils.setDefaultTimeZoneNames(createTimeZoneNamesMap());
DateTimeFormatter timeFormatter = DateTimeFormat.forPattern("H:mm z");
LocalDate date = new LocalDate(2021, 5, 22);
String timeAndZoneString = "14:20 CEST";
LocalTime time = LocalTime.parse(timeAndZoneString, timeFormatter);
DateTime dateTime = date.toDateTime(time, ZONE);
// Validate time zone abbreviation; take overlap at fall-back into account
String earlierCorrectTimeString = dateTime.withEarlierOffsetAtOverlap()
.toString(timeFormatter);
if (! timeAndZoneString.equals(earlierCorrectTimeString)) {
String laterCorrectTimeString = dateTime.withLaterOffsetAtOverlap()
.toString(timeFormatter);
if (! timeAndZoneString.equals(laterCorrectTimeString)) {
throw new IllegalStateException("Incorrect time zone abbreviation for date");
}
}
LocalDateTime ldt = dateTime.toLocalDateTime();
System.out.println(ldt);
Output:
2021-05-22T14:20:00.000
I have used these two auxiliary declaration:
private static final DateTimeZone ZONE = DateTimeZone.forID("Europe/Paris");
private static Map<String, DateTimeZone> createTimeZoneNamesMap() {
Map<String, DateTimeZone> names = new HashMap<>(4);
names.put("CET", ZONE);
names.put("CEST", ZONE);
return names;
}
The validity of the time on the date is also validated: date.toDateTime() validates that the resulting DateTime would not fall in the gap at the spring-forward and throws an IllegalInstantException: if it would.
If the hours you receive in the string are always two digits, the format pattern string needs to specify this, so HH:mm z.
Please be aware that you are losing information in a corner case: If the time falls in the overlap at fall-back, the time zone abbreviation disambiguates, but the LocalDateTime that you produce is ambiguous. For example the date is 2021-10-31 and the time string is 2:20 CEST. Then we know that the time is in the summer time part of the year, that is, before the clocks are turned back. You return 2021-10-31T02:20:00.000, and the receiver won’t be able to tell whether to understand it as 2021-10-31T02:20:00.000+02:00 (summer time) or 2021-10-31T02:20:00.000+01:00 (standard time).
Related
my method accepts - hours, minutes, seconds and milliseconds separated by sign / as a string parameter
how can I add to the current date the parameters that come to the method.
Example 1: today, 02/10/2021, the method receives metnode data (10/10/10/10) - output - 02/10/2021 10:10:10
Example 2: today, 02/10/2021, the method receives metnode data (55/10/10/10) - output - 02/12/2021 07:10:10
That is, you need to add 55 hours 10 seconds 10 seconds and 10 milliseconds to the current date.
you cannot use the Calendar and StringTokenizer classes.
public void method(String s) {
s = s.replaceAll("/", "-");
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm:ss");
final LocalDateTime now = LocalDateTime.parse(s, formatter.withResolverStyle(ResolverStyle.LENIENT));
System.out.println(now);
}
i found the withResolverStyle (ResolverStyle.LENIENT) method
but did not understand how to use it.
A lenient DateTimeFormatter is enough
I don’t know if it’s the best solution. That probably depends on taste. It does use the ResolverStyle.LENIENT that you mentioned and generally works along the lines of the code in your question, only fixed and slightly simplified.
My formatter includes both date and time. This is necessary for surplus hours to be converted to days.
private static final DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendPattern("uuuu-MM-dd H/m/s/")
.appendValue(ChronoField.MILLI_OF_SECOND)
.toFormatter()
.withResolverStyle(ResolverStyle.LENIENT);
Next thing we need a string that matches the formatter. So let’s prepend the date to the time string that we already have got:
String timeString = "55/10/10/10";
LocalDate today = LocalDate.now(ZoneId.of("America/Regina"));
String dateTimeString = "" + today + ' ' + timeString;
LocalDateTime dateTime = LocalDateTime.parse(dateTimeString, formatter);
System.out.println(dateTime);
The output from my code when I ran it today (February 10) was:
2021-02-12T07:10:10.010
A different idea: use Duration
Edit: An alternative is to use the Duration class. A reason for doing that would be that it really appears that you are adding a duration rather than setting the time of day. A liability is that parsing your string into a Duration is a bit tricky. The Duration.parse method that we want to use only accepts ISO 8601 format. It goes like PT55H10M10.010S for a period of time of 55 hours 10 minutes 10.010 seconds. And yes, milliseconds need to be given as a fraction of the seconds.
String isoTimeString = timeString.replaceFirst("(/)(\\d+)$", "$100$2")
.replaceFirst("(\\d+)/(\\d+)/(\\d+)/0*(\\d{3})", "PT$1H$2M$3.$4S");
Duration dur = Duration.parse(isoTimeString);
LocalDateTime dateTime = LocalDate.now(ZoneId.of("Asia/Kathmandu"))
.atStartOfDay()
.plus(dur);
When I ran it just now — already February 11 in Kathmandu, Nepal — the output was:
2021-02-13T07:10:10.010
I am using two calls to replaceFirst(), each time using a regular expression. The first call simply adds some leading zeroes to the milliseconds. $1 and $2 in the replacement string give us what was matched by the first and the second group denoted with round brackets in the regular expression.
The second replaceFirst() call established the ISO 8601 format, which includes making sure that the milliseconds are exactly three digits so they work as a decimal fraction of the seconds.
Link: ISO 8601
Try this:
public void method(String s) {
String[] arr = s.split("/");
LocalDateTime now = LocalDateTime.of(
LocalDate.now(), LocalTime.of(0, 0))
.plusHours(Integer.parseInt(arr[0]))
.plusMinutes(Integer.parseInt(arr[1]))
.plusSeconds(Integer.parseInt(arr[2]))
.plusNanos(Integer.parseInt(arr[3]) * 1_000_000L);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM/dd/yyyy HH:mm:ss");
System.out.println(now.format(formatter));
}
Look into the LocalDateTime documentation. It offers various means for combining dates. Such as:
plus(amount, unit)
plusDays(days)
plusHours(hours)
plusMinutes(minutes)
just for simplicity , you can your LocalDateTime class. it is easy to understand. please refer to below code is used to add the hours, minuts, second and nanos to current Date Time.
this Date Time then can easy formatted by any format pattern as required.
public void addDateTime(int hours, int minuts, int seconds, int nanos) {
LocalDateTime adt = LocalDateTime.now();
System.out.println(adt);
adt = adt.plusHours(hours);
adt = adt.plusMinutes(minuts);
adt = adt.plusSeconds(seconds);
adt = adt.plusNanos(nanos);
System.out.println(adt);
}
I want to subtract 7 days from Now, but keeping the time, so If now is
12/09/2018 at 18:30, get 05/09/2018 at 18:30...
I've tried:
public static Date subtractDays (int numDaysToSubstract) {
LocalDate now = LocalDate.now().minusDays(numDaysToSubstract);
return Date.from(now.atStartOfDay(ZoneId.systemDefault()).toInstant());
}
But I got 05/09/2018 at 00:00
As others have suggested, LocalDate and atStartOfDay should have been red flags based on just their name. They are the wrong type to describe a time and the wrong method to maintain the time.
It's also kind of pointless to go through LocalDateTime to then convert it to an Instant. Just use an Instant straight up
public static Date subtractDays(int numDaysToSubstract) {
return Date.from(Instant.now().minus(numDaysToSubstract, ChronoUnit.DAYS));
// or
// return Date.from(Instant.now().minus(Duration.ofDays(numDaysToSubstract)));
}
(I assume you're using java.util.Date because of compatibility with some old API.)
It’s unclear from the code in the other answers posted until now how they handle summer time (DST) and other time anomalies. And they do that differently. To make it clearer that you want 18.30 last week if time now is 18.30, no matter if a transition to or from summer time has happened in the meantime I suggest using ZonedDateTime:
System.out.println("Now: " + Instant.now());
Instant aWeekAgo = ZonedDateTime.now(ZoneId.of("Europe/Madrid"))
.minusWeeks(1)
.toInstant();
System.out.println("A week ago in Spain: " + aWeekAgo);
Since summer time in Spain hasn’t ended or begun within the last week, running the code snippet just now gave the same time of day also in UTC (which is what Instant prints):
Now: 2018-09-13T09:46:58.066957Z
A week ago in Spain: 2018-09-06T09:46:58.102680Z
I trust you to adapt the idea to your code.
Use class LocalDateTime instead of LocalDate (which doesn't contain a time component..)
You should use LocalDateTime instead of LocalDate
LocalDate is just a description of the date without time or time-zone
public static Date subtractDays (int numDaysToSubstract) {
LocalDateTime now = LocalDateTime.now().minusDays(numDaysToSubstract);
return Date.from(now.atZone(ZoneId.systemDefault()).toInstant());
}
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class Test {
public static String subtractDays (int numDaysToSubstract) {
LocalDateTime now = LocalDateTime.now().minusDays(numDaysToSubstract);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String formatDateTime = now.format(formatter);
return formatDateTime;
}
public static void main(String[] args){
System.out.println(subtractDays(7));
}
}
I have two instances of the Instant class from java.time such as this:
Instant instant1 = Instant.now();
Instant instant2 = Instant.now().plus(5, ChronoUnit.HOURS);
Now I would like to check if the two instances of Instant are actually on the same date (day, month and year match). Easy I thought, let's just use the shiny new LocalDate and the universal from static method:
LocalDate localdate1 = LocalDate.from(instant1);
LocalDate localdate2 = LocalDate.from(instant2);
if (localdate1.equals(localdate2)) {
// All the awesome
}
Except that universal from method is not so universal and Java complains at runtime with an exception:
java.time.DateTimeException: Unable to obtain LocalDate from TemporalAccessor: 2014-11-04T18:18:12Z of type java.time.Instant
Which leaves me back at square 1.
What is the recommended/fastest way check if two instances of Instant actually have the same date (have the same day, month, and year)?
The Instant class does not work with human units of time, such as
years, months, or days. If you want to perform calculations in those
units, you can convert an Instant to another class, such as
LocalDateTime or ZonedDateTime, by binding the Instant with a time
zone. You can then access the value in the desired units.
http://docs.oracle.com/javase/tutorial/datetime/iso/instant.html
Therefore I suggest the following code:
LocalDate ld1 = LocalDateTime.ofInstant(instant1, ZoneId.systemDefault()).toLocalDate();
LocalDate ld2 = LocalDateTime.ofInstant(instant2, ZoneId.systemDefault()).toLocalDate();
if (ld1.isEqual(ld2)) {
System.out.println("blubb");
}
Alternatively you could use
instant.atOffset(ZoneOffset.UTC).toLocalDate();
Using the following block of code I am trying to convert a UTC JODA time to a specified timezone using a string vale, e.g "Asia/Tokyo"
public void handleTimezoneConversion(TimesheetEntry timesheetEntry, String timezone) {
System.out.println("TO :"+timezone);
System.out.println(timesheetEntry.getStartDateTime());
LocalDateTime startDateTime = timesheetEntry.getStartDateTime();
startDateTime.toDateTime(DateTimeZone.forID(timezone));
timesheetEntry.setStartDateTime(startDateTime);
System.out.println(timesheetEntry.getStartDateTime());
LocalDateTime endDateTime = timesheetEntry.getEndDateTime();
endDateTime.toDateTime(DateTimeZone.forID(timezone));
timesheetEntry.setEndDateTime(endDateTime);
}
When i run it the time stays the same evn though there should be a noticeable difference.
Where am I going wrong, are my methods off course completely?
LocalDateTime's toDateTime() method returns a DateTime. In your code, you're calling toDateTime() but discarding the return value. Instead, you'll want to do something like this:
DateTime newDateTime = startDateTime.toDateTime(DateTimeZone.forID(timezone));
I am wondering if there is a way to get current milliseconds since 1-1-1970 (epoch) using the new LocalDate, LocalTime or LocalDateTime classes of Java 8.
The known way is below:
long currentMilliseconds = new Date().getTime();
or
long currentMilliseconds = System.currentTimeMillis();
I'm not entirely sure what you mean by "current milliseconds" but I'll assume it's the number of milliseconds since the "epoch," namely midnight, January 1, 1970 UTC.
If you want to find the number of milliseconds since the epoch right now, then use System.currentTimeMillis() as Anubian Noob has pointed out. If so, there's no reason to use any of the new java.time APIs to do this.
However, maybe you already have a LocalDateTime or similar object from somewhere and you want to convert it to milliseconds since the epoch. It's not possible to do that directly, since the LocalDateTime family of objects has no notion of what time zone they're in. Thus time zone information needs to be supplied to find the time relative to the epoch, which is in UTC.
Suppose you have a LocalDateTime like this:
LocalDateTime ldt = LocalDateTime.of(2014, 5, 29, 18, 41, 16);
You need to apply the time zone information, giving a ZonedDateTime. I'm in the same time zone as Los Angeles, so I'd do something like this:
ZonedDateTime zdt = ldt.atZone(ZoneId.of("America/Los_Angeles"));
Of course, this makes assumptions about the time zone. And there are edge cases that can occur, for example, if the local time happens to name a time near the Daylight Saving Time (Summer Time) transition. Let's set these aside, but you should be aware that these cases exist.
Anyway, if you can get a valid ZonedDateTime, you can convert this to the number of milliseconds since the epoch, like so:
long millis = zdt.toInstant().toEpochMilli();
What I do so I don't specify a time zone is,
System.out.println("ldt " + LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
System.out.println("ctm " + System.currentTimeMillis());
gives
ldt 1424812121078
ctm 1424812121281
As you can see the numbers are the same except for a small execution time.
Just in case you don't like System.currentTimeMillis, use Instant.now().toEpochMilli()
Since Java 8 you can call java.time.Instant.toEpochMilli().
For example the call
final long currentTimeJava8 = Instant.now().toEpochMilli();
gives you the same results as
final long currentTimeJava1 = System.currentTimeMillis();
To avoid ZoneId you can do:
LocalDateTime date = LocalDateTime.of(1970, 1, 1, 0, 0);
System.out.println("Initial Epoch (TimeInMillis): " + date.toInstant(ZoneOffset.ofTotalSeconds(0)).toEpochMilli());
Getting 0 as value, that's right!
You can use java.sql.Timestamp also to get milliseconds.
LocalDateTime now = LocalDateTime.now();
long milliSeconds = Timestamp.valueOf(now).getTime();
System.out.println("MilliSeconds: "+milliSeconds);
To get the current time in milliseconds (since the epoch), use System.currentTimeMillis().
You can try this:
long diff = LocalDateTime.now().atZone(ZoneOffset.UTC).toInstant().toEpochMilli();
Why didn't anyone mentioned the method LocalDateTime.toEpochSecond():
LocalDateTime localDateTime = ... // whatever e.g. LocalDateTime.now()
long time2epoch = localDateTime.toEpochSecond(ZoneOffset.UTC);
This seems way shorter that many suggested answers above...
For LocalDateTime I do it this way:
LocalDateTime.of(2021,3,18,7,17,24,341000000)
.toInstant(OffsetDateTime.now().getOffset())
.toEpochMilli()
I think this is more simpler:
ZonedDateTime zdt = ZonedDateTime.of(LocalDateTime.now(), ZoneId.systemDefault());
Assert.assertEquals(System.currentTimeMillis(), zdt.toInstant().toEpochMilli());
get the millis like System.currentTimeMillis() (from UTC).
There are some methods available that no one has mentioned here. But I don't see a reason why they should not work.
In case of LocalDate, you can use the toEpochDay() method. It returns the number of days since 01/01/1970. That number then can be easily converted to milliseconds:
long dateInMillis = TimeUnit.DAYS.toMillis(myLocalDate.toEpochDays());
Documentation can be found here.
In case of LocalDateTime, you can use the toEpochSecond() method. It returns the number of seconds since 01/01/1970. That number then can be converted to milliseconds, too:
long dateTimeInMillis = TimeUnit.SECONDS.toMillis(myLocalDateTime.toEpochSeconds());
Documentation for that is here.
If you have a Java 8 Clock, then you can use clock.millis() (although it recommends you use clock.instant() to get a Java 8 Instant, as it's more accurate).
Why would you use a Java 8 clock? So in your DI framework you can create a Clock bean:
#Bean
public Clock getClock() {
return Clock.systemUTC();
}
and then in your tests you can easily Mock it:
#MockBean private Clock clock;
or you can have a different bean:
#Bean
public Clock getClock() {
return Clock.fixed(instant, zone);
}
which helps with tests that assert dates and times immeasurably.
Date and time as String to Long (millis):
String dateTimeString = "2020-12-12T14:34:18.000Z";
DateTimeFormatter formatter = DateTimeFormatter
.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.ENGLISH);
LocalDateTime localDateTime = LocalDateTime
.parse(dateTimeString, formatter);
Long dateTimeMillis = localDateTime
.atZone(ZoneId.systemDefault())
.toInstant()
.toEpochMilli();
default LocalDateTime getDateFromLong(long timestamp) {
try {
return LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneOffset.UTC);
} catch (DateTimeException tdException) {
// throw new
}
}
default Long getLongFromDateTime(LocalDateTime dateTime) {
return dateTime.atOffset(ZoneOffset.UTC).toInstant().toEpochMilli();
}