Difference between two ZonedDateTime in Period - date

Using LocalDate, I got the difference expressed in a Period instance. For example:
LocalDate born = LocalDate.of(1990, Month.SEPTEMBER, 30);
Period myAge = Period.between(born, LocalDate.now());
System.out.println("My age is: "+myAge.getYears()+" years "+myAge.getMonths()+" months "+myAge.getDays()+" days.");
Output:
My age is: 26 years 6 months 23 days
So, I was using period to get the age in years, months and days.
I would like to do the same with ZoneDateTime using two time zones (US/Pacific and Australia/Melbourne). For example:
ZonedDateTime now = ZonedDateTime.now(); // US/Pacific
LocalDate date = LocalDate.of(1990, Month.SEPTEMBER, 30);
LocalTime time = LocalTime.of(23, 55);
ZoneId zone = ZoneId.of("Australia/Melbourne");
ZonedDateTime born = ZonedDateTime.of(date, time, zone);
I would like to get the same output as I got with LocalDate. What should I do? Does it make sense?
Expected output:
My age is: 26 years 6 months 24 days

The first line of Period documentation state that it is a date based amount of time in ISO-8601 format. It is not meant to be used with XXXTime.
However, ZonedDateTime offer you an until method which will return the number of a given ChronoUnit until another Temporal (now in your case).
For example :
born.until(now, ChronoUnit.YEARS);
would return 26.
One trick is to add the difference retrieved to the starting date and then process the next ChronoUnit
For example :
long years = born.until(now, ChronoUnit.YEARS);
born = born.plusYears(years);
long months = born.until(now, ChronoUnit.MONTHS);
born = born.plusMonths(months);
long days = born.until(now, ChronoUnit.DAYS);
And then you can print your variables.

Rather than using a ZonedDateTime for a birth date, I'd recommend storing a LocalDate and a ZoneId (ie. in separate fields/columns).
The simple answer to getting the Period from two LocalDate instances is to call toLocalDate() on both. As you'll realise, there is the problem that those ZonedDateTime instances might be in different time-zones, which might require work to normalize. But again, I'd stress that the better storage for this data is two fields - LocalDate and a ZoneId.
Period p = Period.between(born.toLocalDate(), now.toLocalDate());
// warning! the above ignores time-zones, assuming both are the same
If you need the difference between the two in Period plus Duration (for the remaining seconds), you can use this:
Period p = Period.between(born.toLocalDate(), now.toLocalDate());
Duration d = Duration.between(born.with(now.toLocalDate()), now);
The final option is to use ThreeTen-Extra. The soon to be released v1.1 will contain a class PeriodDuration that combines a Period and a Duration and allows the amount of time between two LocalDateTime instances to be stored.

The primary reason you should use LocalDate to represent birthday is because everyone observes their birthday the same way, or put another way, everyone observes their birthday using the clock according to where they are currently at. If you were born in France, and right now are living in the US which has a different timezone offset, you wouldn't celebrate your birthday by observing France's current date, you would instead check the date where you are currently which is local to you. Temporal classes with timezone support should be used for events that span regions, for example, flight arrivals and departures tracking software. So using ZonedDateTime to describe birthday is not wrong, it is semantically incorrect.
Also, Period implements TemporalAmount which obviously represents a span of time. So using it to represent birthday is (again) not wrong, but it makes your intention less clear. Birthday is an exact moment on the timeline, not a duration, so all in all, LocalDate is the best choice

Related

Cleaner way of dealing with dates in java / Quartz

In order to programmatically schedule a job one day ahead (using quartz) I had to come up with this mess of code:
Date.from(LocalDateTime.from(Instant.now()).plusDays(1).toInstant(ZoneOffset.ofHours(-3)))
Isn't there a way of making this monstruous piece of code more clean, readable?
My goal is to simple pick this moment and add one day to it, no concerns about timezones or little differences in the duration of some given days.
EDIT
To be more specific, I need a java.util.Date that represents one day more than when it is created.
The title you picked asks for dates in Java in a general sense, but your question and your tags show that you might be interested in some Quartz-specific solutions, like these (assuming you're using TriggerBuilder):
TriggerBuilder tb = ...; // initialize your tb
// Option 1
Trigger trigger = tb
.withSchedule(/* pick your flavor */)
.startAt(DateBuilder.futureDate(1, DateBuilder.IntervalUnit.DAY))
.build();
// Option 2
LocalDateTime now = LocalDateTime.now();
Trigger trigger2 = tb
.withSchedule(/* pick your flavor */)
.startAt(DateBuilder.tomorrowAt(now.getHour(), now.getMinute(), now.getSecond()))
.build();
For more info check the DateBuilder API.
There are two forms with no preference I know of for one or the other. Either this one:
Date sameTimeTomorrow = Date.from(Instant.now().plus(Duration.ofDays(1)));
Or this:
Date sameTimeTomorrow = Date.from(Instant.now().plus(1, ChronoUnit.DAYS));
Beware, however, that this adds 24 hours without any consideration of summer time or other anomalies. For example: In my time zone summer time ends in the night between October 27 and 28. So if I run the above on October 27 at 12 noon I will hit October 28 at 13 in my time zone because the time has changed. If I need to hit 12 noon again, I need:
Date sameTimeTomorrow = Date.from(
ZonedDateTime.now(ZoneId.of("America/Sao_Paulo")).plusDays(1).toInstant());
Please substitute your correct time zone.

How can I get the start and end time of a period?

I have an enum TimeFrame that has values like: Yesterday, LastWeek, NextMonth etc.
I'm trying to write a method that takes in a TimeFrame and returns the start and end dates of that period of time.
I looked into the new Java 8 Period class which can take a start time and end time but it doesn't seem there's any clean way to retrieve those values afterwards.
How could I return the start and end date at once cleanly without using a List (seems like the wrong data structure) or some ugly datetime arithmetic?
No, a Period doesn't record a start/end - it just represents the amount of time between them, in a calendrical sense (rather than the "precise amount of time" represented by a Duration).
It sounds like basically you should create your own class that has a start date and an end date. You can still use Period to implement that, but you'd have getStartDate() and getEndDate() returning LocalDate values. You shouldn't need to do any heavy date/time arithmetic yourself - just work out the start date and add the appropriate period, or the end date and subtract the appropriate period, depending on the time frame.
Time marches on
Be careful about passing around enum values for “yesterday”, “tomorrow”, and so on. That raises a couple of issues, time zone and midnight.
Dates and day-of-week only have meaning in the context of a time zone. For any given moment, the date varies around the globe. For example, a few minutes after midnight in Paris is still “yesterday” in Montréal. So when ever you intend “yesterday” and such, always specify the desired/expected time zone as well.
Each non-atomic command and line of code executes separately from the previous. Each execution takes a moment of time, however brief. At any of those moments midnight in the specified time zone may be rolling over into a new day. At that stroke of midnight, your relative-time flag such as “yesterday” suddenly takes on a whole new meaning. That meaning is likely different that was intended by the earlier code given that conditions (the date) were different when that code began.
So it makes more sense to me to be passing around Instant objects, or perhaps OffsetDateTime or ZonedDateTime objects. These date-time values are frozen, representing a specific moment on the timeline. Your earlier original code can verify the meaning of that value, check that the date is indeed a Friday or some such. After such verification, the value can be passed on to other code. Now you need not worry about strange occasional bugs occurring at midnight.
java.time
You don't really need to build a class or enum to express your intention of relative time such as “yesterday”. The java.time classes built into Java 8 and later have plain-reading methods for adding and subtracting days, weeks, months. These are basically one-liners, so just call these plus… and minus… methods directly.
ZonedDateTime now = ZonedDateTime.now();
ZonedDateTime yesterday = now.minusDays( 1 );
ZonedDateTime weekLater = now.plusWeeks( 1 );
That code is implicitly applying the JVM’s current default time zone. Better to specify.
ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime now = ZonedDateTime.now( zoneId );
You may want the date-only without time-of-day and without time zone. Use LocalDate.
LocalDate weekPrior = now.toLocalDate().minusWeeks( 1 );
You may want to get first moment of the day. Not always the time of 00:00:00.0.
ZonedDateTime yesterdayStart = now.minusDays( 1 ).toLocalDate().atStartOfDay( zoneId );
If you want to represent the span of time defined by a pair of date-time values, look at the Interval class found in the ThreeTen-Extra project that extends the java.time framework. This class tracks a pair of Instant objects which are moments on the timeline in UTC. You can extract an Instant from your ZonedDateTime by calling toInstant.
Interval intervalYesterday = Interval.of( yesterdayStart.toInstant() , yesterdayStart.plusDays( 1 ).toInstant() );
To get from an Instant back to a zoned date-time, apply a ZoneId.
ZonedDateTime zdt = ZonedDateTime( instant , zoneId );
For a date-only interval, you'll need to build your own class that stores a pair of LocalDate objects. I would call it something like DateRange.
Tip: Search "Half-Open" to learn about the practice of tracking spans of time where beginning in inclusive while the ending is exclusive.

Why is org.joda.time.LocalDate a date without a timezone?

I think the name org.joda.time.LocalDate is kind of confusing. The documentation says:
LocalDate is an immutable datetime class representing a date without a time zone.
In comparison org.joda.time.DateTime says:
A DateTime calculates its fields with respect to a time zone.
I am always confusing those two so I hope somebody can tell me why those names are supposed to make sense.
Here is my intuition: A local date or time object would represent a point in time but with regards to a location. Hence it should contain the time zone information since the time zone also gives you some sort of location information. In any case you know a little more about somebodies location than without that time zone.
A date-time, at least as it sounds like, should only represent a date and a time. In this sense it is just the long value since 00:00:00 Coordinated Universal Time (UTC), Thursday, 1 January 1970.
So why is it actually the other way around? This naming bugs me every time I have something to do with time stamps. Can somebody explain?
Update:
Interesting link posted by a user: https://sourceforge.net/p/threeten/mailman/message/29962542/
Terminology
Your intuition is the opposite of the terminology used by both Joda-Time and java.time.
Both frameworks have a class named LocalDate to represent a date-only value without time-of-day and without time zone. The ”local“ means ”could be any locality, not any particular locality“.
The ”Local…“ classes are just a rough idea of a time. They are not on the timeline. They have no real meaning until you apply a time zone or offset-from-UTC to get actual moments on the timeline. When you see ”Local“ think: ”this value does not really make sense until we apply a time zone“.
For example, we say that Christmas is on 2016-12-25 this year. But the date is not the same around the world at any given moment. A new day dawns earlier in Paris than in Montréal for example. So to get the moment when Christmas starts in Paris, you must apply the Europe/Paris time zone and get the first moment of that day. That first moment will be represented by DateTime in Joda-Time and by ZonedDateTime (or OffsetDateTime) in java.time.
For example, in java.time:
LocalDate xmas2016 = LocalDate.of( 2016 , 12 , 25 );
ZonedDateTime xmas2016FirstMomentMontreal = xmas2016.atStartOfDay( ZoneId.of( "America/Montreal" ) );
java.time
In the java.time framework, a moment on the timeline in UTC is represented by the Instant class.
Instant now = Instant.now();
Apply a time zone, ZoneId, to get a ZonedDateTime.
ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );
If all you have is an offset-from-UTC rather than a full time zone, than use OffsetDateTime rather than ZonedDateTime.
You can think of this way:
Instant + ZoneId -> ZonedDateTime
Instant + ZoneOffset ->OffsetDateTime
This information has been covered many hundreds of times already on StackOverflow. Please search to learn more and see many pieces of example code.

How to handle dates in neo4j

I'm an historian of medieval history and I'm trying to code networks between kings, dukes, popes etc. over a period of time of about 50 years (from 1220 to 1270) in medieval Germany. As I'm not a specialist for graph-databases I'm looking for a possibility to handle dates and date-ranges.
Are there any possibilities to handle over a date-range to an edge so that the edges, which represents a relationship, disappears after e.g. 3 years?
Are there any possibility to ask for relationships who have their date-tag in a date-range?
The common way to deal with dates in Neo4j is storing them either as a string representation or as millis since epoch (aka msec passed since Jan 01 1970).
The first approach makes the graph more easily readable the latter allows you to do math e.g. calculate deltas.
In your case I'd store two properties called validFrom and validTo on the relationships. You queries need to make sure you're looking for the correct time interval.
E.g. to find the king(s) in charge of France from Jan 01 1220 to Dec 31st 1221 you do:
MATCH (c:Country{name:'France'})-[r:HAS_KING]->(king)
WHERE r.validFrom >= -23667123600000 and r.validTo <=-23604051600000
RETURN king, r.validFrom, r.validTo
addendum
Since Neo4j 3.0 there's the APOC library which provides couple of functions for converting timestamps to/from human readable date strings.
You can also store the dates in their number representation in the following format: YYYYMMDD
In your case 12200101 would be Jan 1st 1220 and 12701231 would be Dec 31st 1270.
It's a useful and readable format and you can perform range searches like:
MATCH (h:HistoricEvent)
WHERE h.date >= 12200101 AND h.date < 12701231
RETURN h
It would also let you order by dates, if you need to.
As of Neo4J 3.4, the system handles duration and dates, see the official documentation. See more examples here.
An example related to the original question: Retrieve the historical events that happened in the last 30 days from now :
WITH duration({days: 30}) AS duration
MATCH (h:HistoricEvent)
WHERE date() - duration < date(h.date)
RETURN h
Another option for dates that keeps the number of nodes/properties you create fairly low is a linked list years (earliest year of interest - latest year), one of months (1-12), and one of dates in a month (1-31). Then every "event" in your graph can be connected to a year, month, and day. This way you don't have to create a new node for every new combination of a year month and day. You just have a single set of months, one of days, and one year. I scale the numbers to make manipulating them easier like so
Years are yyyy*10000
Months are mm*100
Date are dd
so if you run a query such as
match (event)-[:happened]->(t:time)
with event,sum(t.num) as date
return event.name,date
order by date
You will get a list of all events in chronological order with dates like Janurary 17th, 1904 appearing as 19040117 (yyyymmdd format)
Further, since these are linked lists where, for example,
...-(t0:time {num:19040000})-[:precedes]->(t1:time {num:19050000})-...
ordering is built into the nodes too.
This is, so far, how I have liked to do my event dating

Represent date (without time) as single integer

Is there a standard way to represent a date as a single integer? (I only need to store dates, not full timestamps.)
If it matters, the reason I want to do this is because I'm storing the dates in a SQLite database on Android and would like to store them as numbers so they can be compared/sorted efficiently to return results from queries.
One good option might be YYYYMMDD, for example encoding today (Jan 13, 2014) as the integer 20140113.
Advantages:
It works for comparisons, as long as you only care about <, ==, and >;
It's reasonably human-readable;
It's compatible with the ISO 8601 standard.
Disadvantages:
It's not as easy to compute differences between dates;
SQLite won't recognize it as a date.
On the last point: The SQLite3 documentation says that SQLite3 has no specific storage types for dates and/or times. Instead, it recommends using one of:
TEXT as ISO8601 strings ("YYYY-MM-DD HH:MM:SS.SSS").
REAL as Julian day numbers, the number of days since noon in Greenwich on November 24, 4714 B.C. according to the proleptic Gregorian calendar.
INTEGER as Unix Time, the number of seconds since 1970-01-01 00:00:00 UTC.
all of which apparently can be processed using SQLite's built-in date and time functions.
The latter argues in favor of the solution in bdf's answer. Picking an arbitrary time within the specified day is admittedly problematic, but I suggest picking noon UTC is unlikely to cause too many problems as long as you're careful to use it consistently. (Noon UTC can be on a different day if your time zone offset is 12 hours or more, but that's not an issue for most of the world.)
just set the time for every day to an arbitrary time of your choosing, such as 2 am.
Storing them as timestamps anyway might still be a good idea, since you'd have a lot more date formatting options.
An important detail to keep in mind are time zones. If e.g. your time falls in the gap between your time zone offset and GMT you might get unexpected results. So at first I propose we discuss date as the one visible to the user which is usually the one of the local time zone.
So if we assume local time, and we want to make use of the Date objects, there are 2 possible solutions, which I will present as JavaScript unit test style. First one is the one presented by Keith Thompson previously:
let date = new Date('1987-12-31T01:02:03')
let simpleDateInteger = (
date.getFullYear() * 10000 +
(date.getMonth() + 1) * 100 +
date.getDate()
)
expect(simpleDateInteger).toBe(19871231)
let fromSimpleDateInteger = new Date(
simpleDateInteger / 10000, // year
simpleDateInteger / 100 % 100 - 1, // month
simpleDateInteger % 100 // day
)
expect(fromSimpleDateInteger.toDateString()).toEqual(date.toDateString())
If you need more compact integers and each integer +1 representing the next day, i.e. a continuous representation you can go with this one:
let date = new Date('1987-12-31T00:01:02')
const DAY_IN_MILLISECONDS = 86400 * 1000
let timeZoneInMilliSeconds = date.getTimezoneOffset() * 60 * 1000
let continuousDateInteger = Math.floor(
(date.getTime() - timeZoneInMilliSeconds) / DAY_IN_MILLISECONDS
)
expect(continuousDateInteger).toBe(6573)
let fromContinuousDateInteger = new Date(
continuousDateInteger * DAY_IN_MILLISECONDS + timeZoneInMilliSeconds
)
expect(fromContinuousDateInteger.toDateString()).toEqual(date.toDateString())