Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
65621a55
Commit
65621a55
authored
Jun 15, 2021
by
Tim Zallmann
Committed by
Natalia Tepluhina
Jun 15, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Split out the datetime utilities
parent
1e916178
Changes
9
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
1125 additions
and
1095 deletions
+1125
-1095
app/assets/javascripts/lib/utils/datetime/date_calculation_utility.js
...avascripts/lib/utils/datetime/date_calculation_utility.js
+703
-0
app/assets/javascripts/lib/utils/datetime/date_format_utility.js
...ets/javascripts/lib/utils/datetime/date_format_utility.js
+260
-0
app/assets/javascripts/lib/utils/datetime/pikaday_utility.js
app/assets/javascripts/lib/utils/datetime/pikaday_utility.js
+28
-0
app/assets/javascripts/lib/utils/datetime/timeago_utility.js
app/assets/javascripts/lib/utils/datetime/timeago_utility.js
+124
-0
app/assets/javascripts/lib/utils/datetime_utility.js
app/assets/javascripts/lib/utils/datetime_utility.js
+4
-1092
app/assets/javascripts/main.js
app/assets/javascripts/main.js
+1
-1
ee/spec/frontend/oncall_schedule/oncall_schedule_spec.js
ee/spec/frontend/oncall_schedule/oncall_schedule_spec.js
+1
-1
ee/spec/frontend/oncall_schedule/schedule/mixins/common_mixin_spec.js
...tend/oncall_schedule/schedule/mixins/common_mixin_spec.js
+1
-1
locale/gitlab.pot
locale/gitlab.pot
+3
-0
No files found.
app/assets/javascripts/lib/utils/datetime/date_calculation_utility.js
0 → 100644
View file @
65621a55
This diff is collapsed.
Click to expand it.
app/assets/javascripts/lib/utils/datetime/date_format_utility.js
0 → 100644
View file @
65621a55
import
dateFormat
from
'
dateformat
'
;
import
{
isString
,
mapValues
,
reduce
}
from
'
lodash
'
;
import
{
s__
,
n__
,
__
}
from
'
../../../locale
'
;
/**
* Returns i18n month names array.
* If `abbreviated` is provided, returns abbreviated
* name.
*
* @param {Boolean} abbreviated
*/
export
const
getMonthNames
=
(
abbreviated
)
=>
{
if
(
abbreviated
)
{
return
[
s__
(
'
Jan
'
),
s__
(
'
Feb
'
),
s__
(
'
Mar
'
),
s__
(
'
Apr
'
),
s__
(
'
May
'
),
s__
(
'
Jun
'
),
s__
(
'
Jul
'
),
s__
(
'
Aug
'
),
s__
(
'
Sep
'
),
s__
(
'
Oct
'
),
s__
(
'
Nov
'
),
s__
(
'
Dec
'
),
];
}
return
[
s__
(
'
January
'
),
s__
(
'
February
'
),
s__
(
'
March
'
),
s__
(
'
April
'
),
s__
(
'
May
'
),
s__
(
'
June
'
),
s__
(
'
July
'
),
s__
(
'
August
'
),
s__
(
'
September
'
),
s__
(
'
October
'
),
s__
(
'
November
'
),
s__
(
'
December
'
),
];
};
/**
* Returns month name based on provided date.
*
* @param {Date} date
* @param {Boolean} abbreviated
*/
export
const
monthInWords
=
(
date
,
abbreviated
=
false
)
=>
{
if
(
!
date
)
{
return
''
;
}
return
getMonthNames
(
abbreviated
)[
date
.
getMonth
()];
};
export
const
dateInWords
=
(
date
,
abbreviated
=
false
,
hideYear
=
false
)
=>
{
if
(
!
date
)
return
date
;
const
month
=
date
.
getMonth
();
const
year
=
date
.
getFullYear
();
const
monthName
=
getMonthNames
(
abbreviated
)[
month
];
if
(
hideYear
)
{
return
`
${
monthName
}
${
date
.
getDate
()}
`
;
}
return
`
${
monthName
}
${
date
.
getDate
()}
,
${
year
}
`
;
};
/**
* Similar to `timeIntervalInWords`, but rounds the return value
* to 1/10th of the largest time unit. For example:
*
* 30 => 30 seconds
* 90 => 1.5 minutes
* 7200 => 2 hours
* 86400 => 1 day
* ... etc.
*
* The largest supported unit is "days".
*
* @param {Number} intervalInSeconds The time interval in seconds
* @returns {String} A humanized description of the time interval
*/
export
const
humanizeTimeInterval
=
(
intervalInSeconds
)
=>
{
if
(
intervalInSeconds
<
60
/* = 1 minute */
)
{
const
seconds
=
Math
.
round
(
intervalInSeconds
*
10
)
/
10
;
return
n__
(
'
%d second
'
,
'
%d seconds
'
,
seconds
);
}
else
if
(
intervalInSeconds
<
3600
/* = 1 hour */
)
{
const
minutes
=
Math
.
round
(
intervalInSeconds
/
6
)
/
10
;
return
n__
(
'
%d minute
'
,
'
%d minutes
'
,
minutes
);
}
else
if
(
intervalInSeconds
<
86400
/* = 1 day */
)
{
const
hours
=
Math
.
round
(
intervalInSeconds
/
360
)
/
10
;
return
n__
(
'
%d hour
'
,
'
%d hours
'
,
hours
);
}
const
days
=
Math
.
round
(
intervalInSeconds
/
8640
)
/
10
;
return
n__
(
'
%d day
'
,
'
%d days
'
,
days
);
};
/**
* Returns i18n weekday names array.
*/
export
const
getWeekdayNames
=
()
=>
[
__
(
'
Sunday
'
),
__
(
'
Monday
'
),
__
(
'
Tuesday
'
),
__
(
'
Wednesday
'
),
__
(
'
Thursday
'
),
__
(
'
Friday
'
),
__
(
'
Saturday
'
),
];
/**
* Given a date object returns the day of the week in English
* @param {date} date
* @returns {String}
*/
export
const
getDayName
=
(
date
)
=>
getWeekdayNames
()[
date
.
getDay
()];
/**
* Returns the i18n month name from a given date
* @example
* formatDateAsMonth(new Date('2020-06-28')) -> 'Jun'
* @param {String} datetime where month is extracted from
* @param {Object} options
* @param {Boolean} options.abbreviated whether to use the abbreviated month string, or not
* @return {String} the i18n month name
*/
export
function
formatDateAsMonth
(
datetime
,
options
=
{})
{
const
{
abbreviated
=
true
}
=
options
;
const
month
=
new
Date
(
datetime
).
getMonth
();
return
getMonthNames
(
abbreviated
)[
month
];
}
/**
* @example
* dateFormat('2017-12-05','mmm d, yyyy h:MMtt Z' ) -> "Dec 5, 2017 12:00am UTC"
* @param {date} datetime
* @param {String} format
* @param {Boolean} UTC convert local time to UTC
* @returns {String}
*/
export
const
formatDate
=
(
datetime
,
format
=
'
mmm d, yyyy h:MMtt Z
'
,
utc
=
false
)
=>
{
if
(
isString
(
datetime
)
&&
datetime
.
match
(
/
\d
+-
\d
+
\d
+ /
))
{
throw
new
Error
(
__
(
'
Invalid date
'
));
}
return
dateFormat
(
datetime
,
format
,
utc
);
};
/**
* Formats milliseconds as timestamp (e.g. 01:02:03).
* This takes durations longer than a day into account (e.g. two days would be 48:00:00).
*
* @param milliseconds
* @returns {string}
*/
export
const
formatTime
=
(
milliseconds
)
=>
{
const
remainingSeconds
=
Math
.
floor
(
milliseconds
/
1000
)
%
60
;
const
remainingMinutes
=
Math
.
floor
(
milliseconds
/
1000
/
60
)
%
60
;
const
remainingHours
=
Math
.
floor
(
milliseconds
/
1000
/
60
/
60
);
let
formattedTime
=
''
;
if
(
remainingHours
<
10
)
formattedTime
+=
'
0
'
;
formattedTime
+=
`
${
remainingHours
}
:`
;
if
(
remainingMinutes
<
10
)
formattedTime
+=
'
0
'
;
formattedTime
+=
`
${
remainingMinutes
}
:`
;
if
(
remainingSeconds
<
10
)
formattedTime
+=
'
0
'
;
formattedTime
+=
remainingSeconds
;
return
formattedTime
;
};
/**
* Port of ruby helper time_interval_in_words.
*
* @param {Number} seconds
* @return {String}
*/
export
const
timeIntervalInWords
=
(
intervalInSeconds
)
=>
{
const
secondsInteger
=
parseInt
(
intervalInSeconds
,
10
);
const
minutes
=
Math
.
floor
(
secondsInteger
/
60
);
const
seconds
=
secondsInteger
-
minutes
*
60
;
const
secondsText
=
n__
(
'
%d second
'
,
'
%d seconds
'
,
seconds
);
return
minutes
>=
1
?
[
n__
(
'
%d minute
'
,
'
%d minutes
'
,
minutes
),
secondsText
].
join
(
'
'
)
:
secondsText
;
};
/**
* Accepts a timeObject (see parseSeconds) and returns a condensed string representation of it
* (e.g. '1w 2d 3h 1m' or '1h 30m'). Zero value units are not included.
* If the 'fullNameFormat' param is passed it returns a non condensed string eg '1 week 3 days'
*/
export
const
stringifyTime
=
(
timeObject
,
fullNameFormat
=
false
)
=>
{
const
reducedTime
=
reduce
(
timeObject
,
(
memo
,
unitValue
,
unitName
)
=>
{
const
isNonZero
=
Boolean
(
unitValue
);
if
(
fullNameFormat
&&
isNonZero
)
{
// Remove traling 's' if unit value is singular
const
formattedUnitName
=
unitValue
>
1
?
unitName
:
unitName
.
replace
(
/s$/
,
''
);
return
`
${
memo
}
${
unitValue
}
${
formattedUnitName
}
`
;
}
return
isNonZero
?
`
${
memo
}
${
unitValue
}${
unitName
.
charAt
(
0
)}
`
:
memo
;
},
''
,
).
trim
();
return
reducedTime
.
length
?
reducedTime
:
'
0m
'
;
};
/**
* Accepts seconds and returns a timeObject { weeks: #, days: #, hours: #, minutes: # }
* Seconds can be negative or positive, zero or non-zero. Can be configured for any day
* or week length.
*/
export
const
parseSeconds
=
(
seconds
,
{
daysPerWeek
=
5
,
hoursPerDay
=
8
,
limitToHours
=
false
,
limitToDays
=
false
}
=
{},
)
=>
{
const
DAYS_PER_WEEK
=
daysPerWeek
;
const
HOURS_PER_DAY
=
hoursPerDay
;
const
SECONDS_PER_MINUTE
=
60
;
const
MINUTES_PER_HOUR
=
60
;
const
MINUTES_PER_WEEK
=
DAYS_PER_WEEK
*
HOURS_PER_DAY
*
MINUTES_PER_HOUR
;
const
MINUTES_PER_DAY
=
HOURS_PER_DAY
*
MINUTES_PER_HOUR
;
const
timePeriodConstraints
=
{
weeks
:
MINUTES_PER_WEEK
,
days
:
MINUTES_PER_DAY
,
hours
:
MINUTES_PER_HOUR
,
minutes
:
1
,
};
if
(
limitToDays
||
limitToHours
)
{
timePeriodConstraints
.
weeks
=
0
;
}
if
(
limitToHours
)
{
timePeriodConstraints
.
days
=
0
;
}
let
unorderedMinutes
=
Math
.
abs
(
seconds
/
SECONDS_PER_MINUTE
);
return
mapValues
(
timePeriodConstraints
,
(
minutesPerPeriod
)
=>
{
if
(
minutesPerPeriod
===
0
)
{
return
0
;
}
const
periodCount
=
Math
.
floor
(
unorderedMinutes
/
minutesPerPeriod
);
unorderedMinutes
-=
periodCount
*
minutesPerPeriod
;
return
periodCount
;
});
};
app/assets/javascripts/lib/utils/datetime/pikaday_utility.js
0 → 100644
View file @
65621a55
export
const
pad
=
(
val
,
len
=
2
)
=>
`0
${
val
}
`
.
slice
(
-
len
);
/**
* Formats dates in Pickaday
* @param {String} dateString Date in yyyy-mm-dd format
* @return {Date} UTC format
*/
export
const
parsePikadayDate
=
(
dateString
)
=>
{
const
parts
=
dateString
.
split
(
'
-
'
);
const
year
=
parseInt
(
parts
[
0
],
10
);
const
month
=
parseInt
(
parts
[
1
]
-
1
,
10
);
const
day
=
parseInt
(
parts
[
2
],
10
);
return
new
Date
(
year
,
month
,
day
);
};
/**
* Used `onSelect` method in pickaday
* @param {Date} date UTC format
* @return {String} Date formatted in yyyy-mm-dd
*/
export
const
pikadayToString
=
(
date
)
=>
{
const
day
=
pad
(
date
.
getDate
());
const
month
=
pad
(
date
.
getMonth
()
+
1
);
const
year
=
date
.
getFullYear
();
return
`
${
year
}
-
${
month
}
-
${
day
}
`
;
};
app/assets/javascripts/lib/utils/datetime/timeago_utility.js
0 → 100644
View file @
65621a55
import
$
from
'
jquery
'
;
import
*
as
timeago
from
'
timeago.js
'
;
import
{
languageCode
,
s__
}
from
'
../../../locale
'
;
import
{
formatDate
}
from
'
./date_format_utility
'
;
window
.
timeago
=
timeago
;
/**
* Timeago uses underscores instead of dashes to separate language from country code.
*
* see https://github.com/hustcc/timeago.js/tree/v3.0.0/locales
*/
const
timeagoLanguageCode
=
languageCode
().
replace
(
/-/g
,
'
_
'
);
/**
* Registers timeago locales
*/
const
memoizedLocaleRemaining
=
()
=>
{
const
cache
=
[];
const
timeAgoLocaleRemaining
=
[
()
=>
[
s__
(
'
Timeago|just now
'
),
s__
(
'
Timeago|right now
'
)],
()
=>
[
s__
(
'
Timeago|just now
'
),
s__
(
'
Timeago|%s seconds remaining
'
)],
()
=>
[
s__
(
'
Timeago|1 minute ago
'
),
s__
(
'
Timeago|1 minute remaining
'
)],
()
=>
[
s__
(
'
Timeago|%s minutes ago
'
),
s__
(
'
Timeago|%s minutes remaining
'
)],
()
=>
[
s__
(
'
Timeago|1 hour ago
'
),
s__
(
'
Timeago|1 hour remaining
'
)],
()
=>
[
s__
(
'
Timeago|%s hours ago
'
),
s__
(
'
Timeago|%s hours remaining
'
)],
()
=>
[
s__
(
'
Timeago|1 day ago
'
),
s__
(
'
Timeago|1 day remaining
'
)],
()
=>
[
s__
(
'
Timeago|%s days ago
'
),
s__
(
'
Timeago|%s days remaining
'
)],
()
=>
[
s__
(
'
Timeago|1 week ago
'
),
s__
(
'
Timeago|1 week remaining
'
)],
()
=>
[
s__
(
'
Timeago|%s weeks ago
'
),
s__
(
'
Timeago|%s weeks remaining
'
)],
()
=>
[
s__
(
'
Timeago|1 month ago
'
),
s__
(
'
Timeago|1 month remaining
'
)],
()
=>
[
s__
(
'
Timeago|%s months ago
'
),
s__
(
'
Timeago|%s months remaining
'
)],
()
=>
[
s__
(
'
Timeago|1 year ago
'
),
s__
(
'
Timeago|1 year remaining
'
)],
()
=>
[
s__
(
'
Timeago|%s years ago
'
),
s__
(
'
Timeago|%s years remaining
'
)],
];
return
(
number
,
index
)
=>
{
if
(
cache
[
index
])
{
return
cache
[
index
];
}
cache
[
index
]
=
timeAgoLocaleRemaining
[
index
]
&&
timeAgoLocaleRemaining
[
index
]();
return
cache
[
index
];
};
};
const
memoizedLocale
=
()
=>
{
const
cache
=
[];
const
timeAgoLocale
=
[
()
=>
[
s__
(
'
Timeago|just now
'
),
s__
(
'
Timeago|right now
'
)],
()
=>
[
s__
(
'
Timeago|just now
'
),
s__
(
'
Timeago|in %s seconds
'
)],
()
=>
[
s__
(
'
Timeago|1 minute ago
'
),
s__
(
'
Timeago|in 1 minute
'
)],
()
=>
[
s__
(
'
Timeago|%s minutes ago
'
),
s__
(
'
Timeago|in %s minutes
'
)],
()
=>
[
s__
(
'
Timeago|1 hour ago
'
),
s__
(
'
Timeago|in 1 hour
'
)],
()
=>
[
s__
(
'
Timeago|%s hours ago
'
),
s__
(
'
Timeago|in %s hours
'
)],
()
=>
[
s__
(
'
Timeago|1 day ago
'
),
s__
(
'
Timeago|in 1 day
'
)],
()
=>
[
s__
(
'
Timeago|%s days ago
'
),
s__
(
'
Timeago|in %s days
'
)],
()
=>
[
s__
(
'
Timeago|1 week ago
'
),
s__
(
'
Timeago|in 1 week
'
)],
()
=>
[
s__
(
'
Timeago|%s weeks ago
'
),
s__
(
'
Timeago|in %s weeks
'
)],
()
=>
[
s__
(
'
Timeago|1 month ago
'
),
s__
(
'
Timeago|in 1 month
'
)],
()
=>
[
s__
(
'
Timeago|%s months ago
'
),
s__
(
'
Timeago|in %s months
'
)],
()
=>
[
s__
(
'
Timeago|1 year ago
'
),
s__
(
'
Timeago|in 1 year
'
)],
()
=>
[
s__
(
'
Timeago|%s years ago
'
),
s__
(
'
Timeago|in %s years
'
)],
];
return
(
number
,
index
)
=>
{
if
(
cache
[
index
])
{
return
cache
[
index
];
}
cache
[
index
]
=
timeAgoLocale
[
index
]
&&
timeAgoLocale
[
index
]();
return
cache
[
index
];
};
};
timeago
.
register
(
timeagoLanguageCode
,
memoizedLocale
());
timeago
.
register
(
`
${
timeagoLanguageCode
}
-remaining`
,
memoizedLocaleRemaining
());
export
const
getTimeago
=
()
=>
timeago
;
/**
* For the given elements, sets a tooltip with a formatted date.
* @param {JQuery} $timeagoEls
* @param {Boolean} setTimeago
*/
export
const
localTimeAgo
=
(
$timeagoEls
,
setTimeago
=
true
)
=>
{
$timeagoEls
.
each
((
i
,
el
)
=>
{
$
(
el
).
text
(
timeago
.
format
(
$
(
el
).
attr
(
'
datetime
'
),
timeagoLanguageCode
));
});
if
(
!
setTimeago
)
{
return
;
}
function
addTimeAgoTooltip
()
{
$timeagoEls
.
each
((
i
,
el
)
=>
{
// Recreate with custom template
el
.
setAttribute
(
'
title
'
,
formatDate
(
el
.
dateTime
));
});
}
requestIdleCallback
(
addTimeAgoTooltip
);
};
/**
* Returns remaining or passed time over the given time.
* @param {*} time
* @param {*} expiredLabel
*/
export
const
timeFor
=
(
time
,
expiredLabel
)
=>
{
if
(
!
time
)
{
return
''
;
}
if
(
new
Date
(
time
)
<
new
Date
())
{
return
expiredLabel
||
s__
(
'
Timeago|Past due
'
);
}
return
timeago
.
format
(
time
,
`
${
timeagoLanguageCode
}
-remaining`
).
trim
();
};
window
.
gl
=
window
.
gl
||
{};
window
.
gl
.
utils
=
{
...(
window
.
gl
.
utils
||
{}),
localTimeAgo
,
};
app/assets/javascripts/lib/utils/datetime_utility.js
View file @
65621a55
This diff is collapsed.
Click to expand it.
app/assets/javascripts/main.js
View file @
65621a55
...
...
@@ -20,7 +20,7 @@ import { removeFlashClickListener } from './flash';
import
initTodoToggle
from
'
./header
'
;
import
initLayoutNav
from
'
./layout_nav
'
;
import
{
handleLocationHash
,
addSelectOnFocusBehaviour
}
from
'
./lib/utils/common_utils
'
;
import
{
localTimeAgo
}
from
'
./lib/utils/datetime_utility
'
;
import
{
localTimeAgo
}
from
'
./lib/utils/datetime
/timeago
_utility
'
;
import
{
getLocationHash
,
visitUrl
}
from
'
./lib/utils/url_utility
'
;
// everything else
...
...
ee/spec/frontend/oncall_schedule/oncall_schedule_spec.js
View file @
65621a55
...
...
@@ -12,7 +12,7 @@ import * as commonUtils from 'ee/oncall_schedules/utils/common_utils';
import
createMockApollo
from
'
helpers/mock_apollo_helper
'
;
import
{
extendedWrapper
}
from
'
helpers/vue_test_utils_helper
'
;
import
waitForPromises
from
'
helpers/wait_for_promises
'
;
import
*
as
dateTimeUtility
from
'
~/lib/utils/datetime_utility
'
;
import
*
as
dateTimeUtility
from
'
~/lib/utils/datetime
/date_calculation
_utility
'
;
import
{
getOncallSchedulesQueryResponse
}
from
'
./mocks/apollo_mock
'
;
import
mockTimezones
from
'
./mocks/mock_timezones.json
'
;
...
...
ee/spec/frontend/oncall_schedule/schedule/mixins/common_mixin_spec.js
View file @
65621a55
...
...
@@ -7,7 +7,7 @@ import {
}
from
'
ee/oncall_schedules/constants
'
;
import
CommonMixin
from
'
ee/oncall_schedules/mixins/common_mixin
'
;
import
{
useFakeDate
}
from
'
helpers/fake_date
'
;
import
*
as
dateTimeUtility
from
'
~/lib/utils/datetime_utility
'
;
import
*
as
dateTimeUtility
from
'
~/lib/utils/datetime
/date_calculation
_utility
'
;
describe
(
'
Schedule Common Mixins
'
,
()
=>
{
// January 3rd, 2018
...
...
locale/gitlab.pot
View file @
65621a55
...
...
@@ -17818,6 +17818,9 @@ msgstr ""
msgid "Invalid login or password"
msgstr ""
msgid "Invalid period"
msgstr ""
msgid "Invalid pin code"
msgstr ""
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment