How to calculate the number of days in a period?
From the documentation:
To define an amount of time with date-based values (years, months,
days), use thePeriod
class. ThePeriod
class provides various get
methods, such asgetMonths
,getDays
, andgetYears
.To present the amount >of time measured in a single unit of time, such as days, you can use the
ChronoUnit.between
method.LocalDate today = LocalDate.now();
LocalDate birthday = LocalDate.of(1960, Month.JANUARY, 1);
Period p = Period.between(birthday, today);
long p2 = ChronoUnit.DAYS.between(birthday, today);
System.out.println("You are " + p.getYears() + " years, " + p.getMonths() +
" months, and " + p.getDays() +
" days old. (" + p2 + " days total)");
The code produces output similar to the following:
You are 53 years, 4 months, and 29 days old. (19508 days total)
excel calculate number of days per month in certain period
I am just posting another IF based variation which I had worked out.
=IF(OR(AND(C$1>$A2,$B2>C$1),AND(EOMONTH(C$1,0)>$A2,$B2>EOMONTH(C$1,0))),MIN($B2,EOMONTH(C$1,0))-MAX($A2,C$1)+1,0)
Primary condition is to check whether any date mentioned on row 1 (within the month) is intersecting the either start or end date specified in cell A2
and B2
. Once this is cleared then the primary logic which you had posted works just fine.
You will have to change the argument separators as per your locale!
Edit-
@AnilGoyal spotted the formula error and I am posting the revised formula. I have put those missing AND
conditions in OR
. In addition to that the formula would have missed a boundary condition where date was first or last day of the month.
=IF(OR(AND(C$1>=$A2,C$1<=$B2),AND(EOMONTH(C$1,0)>=$A2,EOMONTH(C$1,0)<=$B2),AND($A2<=EOMONTH(C$1,0),$A2>=C$1),AND($B2<=EOMONTH(C$1,0),$B2>=C$1)),MIN($B2,EOMONTH(C$1,0))-MAX($A2,C$1)+1,0)
Overall, OP's original formula shall work well. As it turns out below formula seems to work just fine as well.=MAX(0,MIN($B2,EOMONTH(E$1,0))-MAX($A2,E$1)+1)
number of days in a period that fall within another period
Think it figured it out.
=MAX( MIN(project_end, month_end) - MAX(project_start,month_start) + 1 , 0 )
Count the number of days from consecutive timeperiods (Python, datetime)
First, convert n_days
into a numeric value and ensure that the df is sorted:
df['n_days'] = (df['out_date'] - df['in_date']).dt.days
df = df.sort_values(['id','period'])
Add a column counting the days between periods:
df['days_since_last'] = (df['in_date'] - df['out_date'].shift(1)).dt.days
...and make sure those values don't cross between different id
values:
id_changed = (df['id'].shift(1) != df['id'])
df.loc[id_changed, 'days_since_last'] = np.nan
Define a condition that notes where the days between are too high:
days_cut = (df['days_since_last'] >= 90)
Grab a subset of the dataframe where it is either a new id
or a valid consecutive run of days. Assign each of these valid runs a unique value of 'run' (to be used for grouping later):
tmp = df[days_cut | id_changed ].copy()
tmp['run'] = range(len(tmp))
Merge that back into the main dataframe and fill run
forward so that it shows where the valid runs of sequential periods are:
df = pd.merge(df, tmp[['id','period','run']], on=['id','period'], how='left')
df['run'] = df['run'].fillna(method='ffill')
This is what it looks like at that point. You can see that for each id
there are contiguous runs of run
values:
print(df)
id period in_date out_date n_days days_since_last run
0 1 1 2011-02-15 2011-05-21 95 NaN 0.0
1 1 2 2011-11-10 2012-10-11 336 173.0 1.0
2 1 3 2012-10-13 2013-10-25 377 2.0 1.0
3 2 1 2010-04-03 2012-02-16 684 NaN 2.0
4 2 3 2012-02-17 2012-02-19 2 1.0 2.0
5 2 5 2012-08-15 2013-11-23 465 178.0 3.0
6 2 6 2014-01-04 2014-12-18 348 42.0 3.0
7 3 2 2010-06-01 2011-08-21 446 NaN 4.0
8 3 3 2012-03-29 2012-09-11 166 221.0 5.0
9 3 4 2012-09-12 2013-01-10 120 1.0 5.0
Extract the consecutive days for each run
by summing up the n_days
column. The .agg
also tracks the max date in the run, so we can keep only the runs that end in 2013:
consecutive_days = df.groupby(['id','run']).agg( {'n_days' : np.sum, 'out_date' : np.max } )
consecutive_days = consecutive_days[(consecutive_days['out_date'].dt.year == 2013)]
consecutive_days = consecutive_days.drop(columns=['out_date']).rename(columns={'n_days' : 'consecutive_days'})
Finally, merge that back in to the original dataframe and drop the excess columns:
df = pd.merge(df, consecutive_days, on='id', how='left')
df = df.drop(columns=['days_since_last','run'])
print(df)
id period in_date out_date n_days consecutive_days
0 1 1 2011-02-15 2011-05-21 95 713.0
1 1 2 2011-11-10 2012-10-11 336 713.0
2 1 3 2012-10-13 2013-10-25 377 713.0
3 2 1 2010-04-03 2012-02-16 684 NaN
4 2 3 2012-02-17 2012-02-19 2 NaN
5 2 5 2012-08-15 2013-11-23 465 NaN
6 2 6 2014-01-04 2014-12-18 348 NaN
7 3 2 2010-06-01 2011-08-21 446 286.0
8 3 3 2012-03-29 2012-09-11 166 286.0
9 3 4 2012-09-12 2013-01-10 120 286.0
Determine number of days represented by a time range in Java
tl;dr
Always define your spans of time by the Half-Open approach where:
- Beginning is inclusive.
- Ending is exclusive.
When you want the four days of March 28, March 29, March 30, March 31
, make the beginning March 28 and the ending April 1. Run through those dates starting at the first (March 28th) while going up to, but not including, the last (April 1st).
2016-03-28/2016-04-01
Half-Open
Am I missing something
You may be missing an appreciation for the usefulness of the Half-Open approach in defining spans of time.
Generally, the best practice for defining spans of time is the Half-Open approach. In Half-Open, the beginning is inclusive while the ending is exclusive.
This approach solves the problem of dealing with fractional seconds. Intuitively, many programmers will try to find the last possible moment as the ending of a span of time. But that last moment involves an infinitely divisible last second. You might think, "Well, just go to three decimal place for milliseconds, 12:59.59.999 for the end of noon lunch break, as that is all the resolution I will ever need, and that is the resolution of the legacy java.util.Date class.”. But then you would fail to find matches in your database like Postgres that store date-time values with a resolution of microseconds, 12:59:59.999999. So you decide to use six decimal places of fraction, x.999999. But the start experiencing mismatches with date-time values in the java.time classes, and you learn the offer a resolution of nanoseconds for nine digits of fractional second, x.999999999. You can disembark this carousel of frustrating bugs by using the Half-Open approach where the ending runs up to, but does not include, the next whole second.
I believe you will find consistent use of the Half-Open approach throughout your date-time handling code (whether fractional seconds may be involved or not) will:
- Make your code easier to read and comprehend.
- Ease the cognitive load overall.
Knowing all your spans of time carry the same definition eliminates ambiguity. - Reduce bugs.
Examples:
- A noon lunch period starts at the moment the clock strikes noon (12:00:00) and runs up to, but does not include, the moment when the clock strikes one o’clock. That means 12:00:00 to 13:00:00.
- A full day starts at the first moment of the day (not always 00:00:00, by the way) and runs up to, but does not include, the first moment of the following day.
- A week starts on a Monday and runs up to, but does not include, the following Monday. That means seven days in Monday-Monday.
- A month starts of the first of the month and runs up to, but not including, the first of the following month. So the month of March is March 1 to April 1.
LocalDate
Rather than one adding 1
to get a total of days, define your span of time as Half-Open: beginning-is-inclusive, ending-is-exclusive. If you are trying to represent the four dates of March 28, 29, 30, and 31, then I suggest you define a span of time from March 28 to April 1.
LocalDate start = LocalDate.of( 2016, Month.MARCH, 28 ) ; // inclusive
LocalDate stop = LocalDate.of( 2016, Month.APRIL, 1 ) ; // exclusive
Period
The java.time classes wisely use the Half-Open approach. So the Period.between
method treats the ending as exclusive, as noted in the Question. I suggest you go-with-the-flow here rather than fight it. Search Stack Overflow for many more examples of how well this approach works.
Period p = Period.between( start , stop );
p.toString(): P4D
ChronoUnit
If you want a total number of days, such as 45 for a month and a half, use the ChronoUnit
enum, an implementation of TemporalUnit
. See this Question for discussion.
Again, the java.time classes use the Half-Open approach. So
long daysBetween = ChronoUnit.DAYS.between( start, stop );
4
Live code
See this example code run live at IdeOne.com.
Get a Period of Days between two Dates
I believe this is the correct result. If you run the following test you will see that getMonths() returns 1. It seems that getDays is not get the total days but get the remainder days.
@Test
public void testOneMonthPeriodDays() {
Period p = Period.between(LocalDate.of(2017, 06, 01), LocalDate.of(2017, 07, 01));
assertEquals(0, p.getDays());
assertEquals(1, p.getMonths());
}
To get the total number of days between two dates see this question Calculate days between two dates in Java 8
To quote the answer given in that question:
LocalDate dateBefore;
LocalDate dateAfter;
long daysBetween = DAYS.between(dateBefore, dateAfter);
Note that DAYS
is member of java.time.temporal.ChronoUnit
enum.
Count number of days in each continuous period pandas
For consecutive days compare difference by Series.diff
in days by Series.dt.days
for not equal 1
by Series.ne
with cumulative sum by Series.cumsum
and then use GroupBy.size
, remove second level by DataFrame.droplevel
and create DataFrame
:
df['date_code'] = pd.to_datetime(df['date_code'])
df1= (df.groupby(['item_code',df['date_code'].diff().dt.days.ne(1).cumsum()], sort=False)
.size()
.droplevel(1)
.reset_index(name='continuous_days'))
print (df1)
item_code continuous_days
0 8028558104973 3
1 8028558104973 2
2 7622300443269 1
3 7622300443269 2
4 513082 3
And then aggregate values by named aggregations by GroupBy.agg
:
df2 = (df1.groupby('item_code', sort=False, as_index=False)
.agg(**{'no. periods': ('continuous_days','size'),
'min':('continuous_days','min'),
'max':('continuous_days','max'),
'mean':('continuous_days','mean')}))
print (df2)
item_code no. periods min max mean
0 8028558104973 2 2 3 2.5
1 7622300443269 2 1 2 1.5
2 513082 1 3 3 3.0
Related Topics
Difference Between Jvm's Lookupswitch and Tableswitch
Can't Parse String to Localdate (Java 8)
Concurrenthashmap VS Synchronized Hashmap
Handling Datetime Values 0000-00-00 00:00:00 in Jdbc
How to Set Jvm Parameters for Junit Unit Tests
How to Get the Current Date and Time
Converting Little Endian to Big Endian
Tracking Down Cause of Spring's "Not Eligible for Auto-Proxying"
How to Implement Infinity in Java
How to Have Case Insensitive Urls in Spring MVC with Annotated Mappings
Java Getting an Error for Implementing Interface Method with Weaker Access
"No Match Found" When Using Matcher's Group Method
Selenium Webdriver Submit() VS Click()
Java File - Open a File and Write to It