java.time.Instant.plus(long amountToAdd, TemporalUnit unit) Unsupported unit - java-time

I trying to add few years to current time. My code looks like:
// ten yeas ago
int backYears = 10;
Instant instant = ChronoUnit.YEARS.addTo(Instant.now(), -backYears);
But I got an exception:
java.time.temporal.UnsupportedTemporalTypeException: Unsupported unit: Years
at java.time.Instant.plus(Instant.java:862)
When I opened the method Instant.plus I see the following:
#Override
public Instant plus(long amountToAdd, TemporalUnit unit) {
if (unit instanceof ChronoUnit) {
switch ((ChronoUnit) unit) {
case NANOS: return plusNanos(amountToAdd);
case MICROS: return plus(amountToAdd / 1000_000, (amountToAdd % 1000_000) * 1000);
case MILLIS: return plusMillis(amountToAdd);
case SECONDS: return plusSeconds(amountToAdd);
case MINUTES: return plusSeconds(Math.multiplyExact(amountToAdd, SECONDS_PER_MINUTE));
case HOURS: return plusSeconds(Math.multiplyExact(amountToAdd, SECONDS_PER_HOUR));
case HALF_DAYS: return plusSeconds(Math.multiplyExact(amountToAdd, SECONDS_PER_DAY / 2));
case DAYS: return plusSeconds(Math.multiplyExact(amountToAdd, SECONDS_PER_DAY));
}
throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
}
return unit.addTo(this, amountToAdd);
}
As you can see MONTHS and YEARS are unsupported. But why?
With an old java.util.Calendar I can do that easily:
Calendar c = Calendar.getInstance();
c.setTime(date);
c.add(Calendar.YEAR, amount);
return c.getTime();
The only one reason what I guess is that we don't know how many days in a month and year because of leap day 29 Feb.
But to be honest we also have a leap second.
Thus I think that this is a bug and all ChronoUnits should be supported.
The only one question is: do we need to take in account leap second and leap day.
As for my needs it's okay just to assume that month has 30 days and year 365.
I don't need to make something like Calendar.roll() but this can satisfy me too.

Let’s try something out. I am taking an instant as ZonedDateTime and subtracting 10 years in different time zones.
OffsetDateTime origin = OffsetDateTime.of(2018, 3, 1, 0, 0, 0, 0, ZoneOffset.UTC);
Instant originInstant = origin.toInstant();
Instant tenYearsBackKyiv = origin.atZoneSameInstant(ZoneId.of("Europe/Kiev"))
.minusYears(10)
.toInstant();
long hoursSubtractedKyiv = ChronoUnit.HOURS.between(tenYearsBackKyiv, originInstant);
System.out.println("Hours subtracted in Київ: " + hoursSubtractedKyiv);
Instant tenYearsBackSaoPaulo = origin.atZoneSameInstant(ZoneId.of("America/Sao_Paulo"))
.minusYears(10)
.toInstant();
long hoursSubtractedSaoPaulo = ChronoUnit.HOURS.between(tenYearsBackSaoPaulo, originInstant);
System.out.println("Hours subtracted in São Paulo: " + hoursSubtractedSaoPaulo);
The output is:
Hours subtracted in Київ: 87648
Hours subtracted in São Paulo: 87672
As you can see, 24 hours more (1 day more) is subtracted in São Paulo compared to Київ (Kyiv, Kiev). You may already have figured out that it’s because there we pass from 1 March to 29 February three times in leap years, in Київ only twice.
The old and now outdated Calendar class always had a time zone in it, so knew in which time zone to subtract years (another thing is it was happy to give you a result even in situations where it was unclear which result you wanted). The modern classes ZonedDateTime, OffsetDateTime and LocalDateTime can do the same. So use them. An Instant conceptually doesn’t have a time zone, so refuses to do operations that depend on time zone (I know it’s implemented using UTC, but we should regard this as an irrelevant implementation detail, not as a part of the specification of the interface to the class).
Neither the old nor the modern classes take leap seoncds into account, and you are right, only therefore can an Instant add and subtract days, hours and minutes.

Related

How to calculate the next minute and next 5 minute intevals given a ZonedDateTime

I have a instance of a ZonedDatetime.
ZonedDateTime.now(ZoneId.of("America/New_York"))
I basically need a function that will take an instance of a ZonedDateTime and return the next 1 minute and 5 minute values.
So if the current time is:
2021-10-24T19:46:10.649817
The next minute will be 19:47:00 and the next 5 minute will be 19:50:00
The next 5 minute interval is always like:
1:00
1:05
1:10
1:15
1:20
1:25
...
1:50
1:55
2:00
i.e. the next 5 minute interval is not based on exactly 5 minutes from now, but rather the next 5 minutes based on starting from the beginning of the hour. Same goes for the next 1 minute interval in the future.
def nextIntervals(zdt: ZonedDateTime): (ZonedDateTime, ZonedDateTime) = {
???
}
It is fairly simple to do so without hardcoding the values. Unfortunately I'm not familiar with scala so I'll give you some pseudo code, I believe you'll be able to easily translate it.
nextIntervals(zdt) {
timestamp = zdt.toUnixTimestamp();
return [
new ZonedDateTime(timestamp + (60 - timestamp % 60)),
new ZonedDateTime(timestamp + (300 - timestamp % 300))
]
}
The above code assumes that ZonedDateTime can be instantiated by giving it a unix timestamp, measured in seconds. And also that it can be converted to a unix timestamp.
The idea is pretty simple: the remainder of the modulus will be the time that has elapsed since the last required period (in your case 1 minute or 5 minutes). Take that away from the period itself and you have the time that's left until the next period. Add that to the current time and you have the exact datetime.
Edit:
Here's a working javascript example
function nextIntervals(date) {
let t = date.getTime();
return [
60e3,
300e3,
].map(i => new Date(t + i - t % i));
}
console.log(nextIntervals(new Date));
You can use the following functions to meet your requirements:
ZonedDateTime#plusMinutes
ZonedDateTime#minusMinutes
ZonedDateTime#truncatedTo
Demo:
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
public class Main {
public static void main(String[] args) {
ZonedDateTime now = ZonedDateTime.now(ZoneId.of("America/New_York"));
ZonedDateTime nextMinute = now.plusMinutes(1).truncatedTo(ChronoUnit.MINUTES);
ZonedDateTime nextMultipleOfFiveMin = now.truncatedTo(ChronoUnit.MINUTES)
.minusMinutes(now.getMinute() % 5)
.plusMinutes(5);
System.out.println(now);
System.out.println(nextMinute);
System.out.println(nextMultipleOfFiveMin);
}
}
Output from a sample run:
2021-10-25T16:59:22.662943-04:00[America/New_York]
2021-10-25T17:00-04:00[America/New_York]
2021-10-25T17:00-04:00[America/New_York]
Output from another sample run after a while:
2021-10-25T17:05:09.596952-04:00[America/New_York]
2021-10-25T17:06-04:00[America/New_York]
2021-10-25T17:10-04:00[America/New_York]
ONLINE DEMO
Learn more about the modern Date-Time API from Trail: Date Time. Check this answer and this answer to learn how to use java.time API with JDBC.
Note: The java.util Date-Time API and their formatting API, SimpleDateFormat are outdated and error-prone. It is recommended to stop using them completely and switch to the modern Date-Time API*.
* If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring. Note that Android 8.0 Oreo already provides support for java.time.
We do need a little bit of hand-coded math to handle the 5-minute interval case. Excuse my Java syntax.
ZonedDateTime now = ZonedDateTime.now(ZoneId.of("America/New_York"));
System.out.println("Now: " + now);
// Truncate to the previous 5 minutes
ZonedDateTime zdt = now.truncatedTo(ChronoUnit.MINUTES);
zdt = zdt.withMinute(zdt.getMinute() / 5 * 5);
for (int i = 0; i <= 12; i++) {
zdt = zdt.plusMinutes(5);
System.out.println(zdt);
}
Example output:
Now: 2021-10-25T15:23:31.357567-04:00[America/New_York]
2021-10-25T15:25-04:00[America/New_York]
2021-10-25T15:30-04:00[America/New_York]
2021-10-25T15:35-04:00[America/New_York]
2021-10-25T15:40-04:00[America/New_York]
2021-10-25T15:45-04:00[America/New_York]
2021-10-25T15:50-04:00[America/New_York]
2021-10-25T15:55-04:00[America/New_York]
2021-10-25T16:00-04:00[America/New_York]
2021-10-25T16:05-04:00[America/New_York]
2021-10-25T16:10-04:00[America/New_York]
2021-10-25T16:15-04:00[America/New_York]
2021-10-25T16:20-04:00[America/New_York]
2021-10-25T16:25-04:00[America/New_York]
The trick to truncate to a whole multiple of 5 minutes is to divide by 5, obtain a whole number and discard any remainder, and multiply by 5 again.
The 1-minute interval is similar, only a bit simpler: we don’t need to do any math ourselves, java.time takes care of it all.

Codename One days left in current week or month

I am building a calendar application that needs to calculate the remaining days of (1) the current week and (2) the current month.
What I have tried was using the java.util.Calendar API, but it seems not to be fully supported.
int days = cal.getActualMaximum(Calendar.DAY_OF_MONTH);
How would I do this in CN1?
Many thanks in advance.
We would love to add support for JSR310 in the future which would probably solve this in a more elegant way. This is something that can be done as a cn1lib without modifying Codename One but would require some work...
A bit of a hack to do this is:
int month = cal.get(Calendar.MONTH);
cal.set(Calendar.DAY_OF_MONTH, 1);
if(month == cal.DECEMBER) {
cal.set(Calendar.YEAR, cal.get(Calendar.YEAR) + 1);
cal.set(Calendar.MONTH, Calendar.JANUARY);
} else {
cal.set(Calendar.MONTH, month + 1);
}
int day = 24 * 60 * 60000;
cal.setTime(new Date(cal.getTime().getTime() - day));
I don't know CN1, but you can calculate the values using a subtraction between the max and the current value:
(1) cal.getActualMaximum(Calendar.DAY_OF_WEEK) - cal.get(Calendar.DAY_OF_WEEK)
(2) cal.getActualMaximum(Calendar.DAY_OF_MONTH) - cal.get(Calendar.DAY_OF_MONTH)
For your notification: The first day of the week is sunday in the api of calendar. When your week should start with monday you have to reduce the offset by 1.

Setting up a two week timetable in Swift

NOTE: I am a new Swift programmer, a NOOB if you will.
I am creating a school timetable app just for personal use to practise my coding. However, our school operates on a two week time table system, with 10 days, labeled 1 through to ten. I am wondering if anyone had some ideas as to how I could work out whether the current date is day one or day nine or day 4. I know I could use if statements for the dates, but the would take a long time, and require manual input of the dates. How could I have the app keep count of what day it is, skipping weekends?
EDIT - I could maybe have 14 days, with days 6,7,13 and 14 empty.
FOR EXAMPLE:
The current date is OCT 4, this is day one. I would like the app to be able to work out what day of the timetable the current date is. This would then load the appropriate day (e.g. Subject, Teacher, Classroom). Day One is Monday, Two is Tuesday, Five is Friday, Six is Monday, 10 is Friday. Could I have some sort of rostering system?
I am sorry if the question is vague, please tell me if I need to clarify.
I have been working on a fix for weeks now, so I have decided to turn to help. Any guidance whatsoever would be much appreciated, as I am at a dead end!
Many thanks
The numbers that I'm plugging into this example probably don't match your requirements but consider this as a strategy. (In this case, using a 1-to-14 cycle. If you'd rather get 1-to-10 you can put in a subtraction and a different error to throw on the "bad" days.)
class CyclicDay {
enum CyclicDayError: ErrorType {
case InvalidStartDate
}
lazy var baseline: NSDate? = {
// Set up some start date that matches day 1
var components = NSDateComponents()
components.day = 6
components.month = 9
components.year = 2015
return NSCalendar.currentCalendar().dateFromComponents(components)
}()
func dayOfCycle(testDate: NSDate) throws -> Int {
if let start = baseline {
// Convert difference to days
let interval = testDate.timeIntervalSinceDate(start)
let days = interval / (60 * 60 * 24)
// Convert to value 1..14 to position in a 2-week cycle
return Int(days % 14) + 1
}
throw CyclicDayError.InvalidStartDate
}
}
// Test today
let cd = CyclicDay()
let day = try cd.dayOfCycle(NSDate())

Duration to TemporalUnits

How do I convert a duration to a set of temporal units? I have a duration, and I want to divide that into for instance whole years, whole days and fractional seconds.
I have found PeriodFormatterBuilder for JodaTime, but it seems to facilitate printing as well as division into units, i would just like the division.
I am not sure if you speak about the new java.time-package of Java-8 or about Joda-Time so I try to present solutions for both libraries.
Most important aspect however is that you cannot divide a Duration defined in seconds into years, months etc. in a self-consistent manner because month-based units vary in length of seconds and days. At least not possible without any trick.
Best you can do in this case is to use a reference timestamp in order to recalculate the duration you have. That means you add your duration to the reference timestamp and then evaluate the new duration between the old reference timestamp and the result in years, months, days etc. This is also called normalization and has nothing to do with printing/formatting.
Java-8:
Duration dur = Duration.ofSeconds(5000001); // example
LocalDateTime ref = LocalDateTime.now(); // reference timestamp
LocalDateTime end = ref.plus(dur);
System.out.println(ref);
System.out.println(end);
// normalize first the calendrical part
LocalDateTime ldt = ref;
long years = ChronoUnit.YEARS.between(ldt, end);
// find the months part
ldt = ldt.plus(years, ChronoUnit.YEARS);
long months = ChronoUnit.MONTHS.between(ldt, end);
// find the days part
ldt = ldt.plus(months, ChronoUnit.MONTHS);
long days = ChronoUnit.DAYS.between(ldt, end);
// find the hours part
ldt = ldt.plus(days, ChronoUnit.DAYS);
long hours = ChronoUnit.HOURS.between(ldt, end);
// find the minutes part
ldt = ldt.plus(hours, ChronoUnit.HOURS);
long minutes = ChronoUnit.MINUTES.between(ldt, end);
// find the seconds part
ldt = ldt.plus(minutes, ChronoUnit.MINUTES);
long seconds = ChronoUnit.SECONDS.between(ldt, end);
// print the new normalized duration in ISO-8601-format
System.out.println(
String.format("P%1$dY%2$dM%3$dDT%4$dH%5$dM%6$dS", years, months, days, hours, minutes, seconds));
// example output
// 2015-03-17T12:54:07.943
// 2015-05-14T09:47:28.943
// P0Y1M26DT20H53M21S
Compared with old JDK pre 8 this can be considered as much better because at least elementary methods for calculation of a duration in one given unit are offered. But a general duration type for handling all units spanning from years to seconds is completely missing. And the best duration formatter I could find is just java.util.Formatter.
Joda-Time
That is the second-best Java library when duration handling is needed, in most details better than Java-8 on this area. Joda-Time indeed offers a duration type spanning from years to seconds (and millis) called Period. See here the much simpler solution:
Duration dur = new Duration(5000001 * 1000L); // in milliseconds
LocalDateTime ref = new LocalDateTime(); // reference timestamp
LocalDateTime end = ref.plus(dur);
// construct normalized duration
PeriodType type = PeriodType.yearMonthDayTime().withMillisRemoved();
Period p = new Period(ref, end, type);
// print the new normalized duration
System.out.println(p); // P1M26DT20H53M21S
Small note: I have left out fractional seconds (in Joda-Time limited to milliseconds, in Java-8 up to nanoseconds) in given examples. It is easy to enhance the examples if you really have need for this precision.

Unix gettimeofday() - compatible algorithm for determining week within month?

If I've got a time_t value from gettimeofday() or compatible in a Unix environment (e.g., Linux, BSD), is there a compact algorithm available that would be able to tell me the corresponding week number within the month?
Ideally the return value would work in similar to the way %W behaves in strftime() , except giving the week within the month rather than the week within the year.
I think Java has a W formatting token that does something more or less like what I'm asking.
[Everything below written after answers were posted by David Nehme, Branan, and Sparr.]
I realized that to return this result in a similar way to %W, we want to count the number of Mondays that have occurred in the month so far. If that number is zero, then 0 should be returned.
Thanks to David Nehme and Branan in particular for their solutions which started things on the right track. The bit of code returning [using Branan's variable names] ((ts->mday - 1) / 7) tells the number of complete weeks that have occurred before the current day.
However, if we're counting the number of Mondays that have occurred so far, then we want to count the number of integral weeks, including today, then consider if the fractional week left over also contains any Mondays.
To figure out whether the fractional week left after taking out the whole weeks contains a Monday, we need to consider ts->mday % 7 and compare it to the day of the week, ts->wday. This is easy to see if you write out the combinations, but if we insure the day is not Sunday (wday > 0), then anytime ts->wday <= (ts->mday % 7) we need to increment the count of Mondays by 1. This comes from considering the number of days since the start of the month, and whether, based on the current day of the week within the the first fractional week, the fractional week contains a Monday.
So I would rewrite Branan's return statement as follows:
return (ts->tm_mday / 7) + ((ts->tm_wday > 0) && (ts->tm_wday <= (ts->tm_mday % 7)));
If you define the first week to be days 1-7 of the month, the second week days 8-14, ... then the following code will work.
int week_of_month( const time_t *my_time)
{
struct tm *timeinfo;
timeinfo =localtime(my_time);
return 1 + (timeinfo->tm_mday-1) / 7;
}
Assuming your first week is week 1:
int getWeekOfMonth()
{
time_t my_time;
struct tm *ts;
my_time = time(NULL);
ts = localtime(&my_time);
return ((ts->tm_mday -1) / 7) + 1;
}
For 0-index, drop the +1 in the return statement.
Consider this pseudo-code, since I am writing it in mostly C syntax but pretending I can borrow functionality from other languages (string->int assignment, string->time conversion). Adapt or expand for your language of choice.
int week_num_in_month(time_t timestamp) {
int first_weekday_of_month, day_of_month;
day_of_month = strftime(timestamp,"%d");
first_weekday_of_month = strftime(timefstr(strftime(timestamp,"%d/%m/01")),"%w");
return (day_of_month + first_weekday_of_month - 1 ) / 7 + 1;
}
Obviously I am assuming that you want to handle weeks of the month the way the standard time functions handle weeks of the year, as opposed to just days 1-7, 8-13, etc.