diff --git a/src/addons/mod/assign/components/index/index.ts b/src/addons/mod/assign/components/index/index.ts index e39660b97..adef90a39 100644 --- a/src/addons/mod/assign/components/index/index.ts +++ b/src/addons/mod/assign/components/index/index.ts @@ -26,6 +26,7 @@ import { CoreTimeUtils } from '@services/utils/time'; import { CoreUtils } from '@services/utils/utils'; import { Translate } from '@singletons'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; +import { CoreTime } from '@singletons/time'; import { AddonModAssignListFilterName } from '../../classes/submissions-source'; import { AddonModAssign, @@ -182,7 +183,7 @@ export class AddonModAssignIndexComponent extends CoreCourseModuleMainActivityCo if (this.assign.duedate - time <= 0) { this.timeRemaining = Translate.instant('addon.mod_assign.assignmentisdue'); } else { - this.timeRemaining = CoreTimeUtils.formatTime(this.assign.duedate - time); + this.timeRemaining = CoreTime.formatTime(this.assign.duedate - time); } if (this.assign.duedate < time) { diff --git a/src/addons/mod/assign/components/submission/submission.ts b/src/addons/mod/assign/components/submission/submission.ts index a5566cbb6..3699d2c59 100644 --- a/src/addons/mod/assign/components/submission/submission.ts +++ b/src/addons/mod/assign/components/submission/submission.ts @@ -58,6 +58,7 @@ import { CoreSync } from '@services/sync'; import { AddonModAssignSubmissionPluginComponent } from '../submission-plugin/submission-plugin'; import { AddonModAssignModuleHandlerService } from '../../services/handlers/module'; import { CanLeave } from '@guards/can-leave'; +import { CoreTime } from '@singletons/time'; /** * Component that displays an assignment submission. @@ -230,7 +231,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can this.timeRemaining = Translate.instant( 'addon.mod_assign.' + (onTime ? earlyString : lateString), - { $a: CoreTimeUtils.formatTime(Math.abs(lateCalculation - lateThreshold)) }, + { $a: CoreTime.formatTime(Math.abs(lateCalculation - lateThreshold)) }, ); this.timeRemainingClass = onTime ? 'earlysubmission' : 'latesubmission'; @@ -242,7 +243,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can const submissionsEnabled = response.lastattempt?.submissionsenabled || response.gradingsummary?.submissionsenabled; this.timeRemaining = Translate.instant( 'addon.mod_assign.' + (submissionsEnabled ? 'overdue' : 'duedatereached'), - { $a: CoreTimeUtils.formatTime(time - this.assign.duedate) }, + { $a: CoreTime.formatTime(time - this.assign.duedate) }, ); this.timeRemainingClass = 'overdue'; this.timeLimitFinished = true; @@ -260,7 +261,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can } // Assignment is not overdue, and no submission has been made. Just display the due date. - this.timeRemaining = CoreTimeUtils.formatTime(this.assign.duedate - time); + this.timeRemaining = CoreTime.formatTime(this.assign.duedate - time); this.timeRemainingClass = 'timeremaining'; } @@ -367,7 +368,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can try { await CoreDomUtils.showConfirm( Translate.instant('addon.mod_assign.confirmstart', { - $a: CoreTimeUtils.formatTime(this.assign.timelimit), + $a: CoreTime.formatTime(this.assign.timelimit), }), undefined, Translate.instant('addon.mod_assign.beginassignment'), diff --git a/src/addons/mod/chat/components/index/index.ts b/src/addons/mod/chat/components/index/index.ts index 813c08cff..7315bdf70 100644 --- a/src/addons/mod/chat/components/index/index.ts +++ b/src/addons/mod/chat/components/index/index.ts @@ -18,6 +18,7 @@ import { CoreCourseContentsPage } from '@features/course/pages/contents/contents import { IonContent } from '@ionic/angular'; import { CoreNavigator } from '@services/navigator'; import { CoreTimeUtils } from '@services/utils/time'; +import { CoreTime } from '@singletons/time'; import { AddonModChat, AddonModChatChat, AddonModChatProvider } from '../../services/chat'; import { AddonModChatModuleHandlerService } from '../../services/handlers/module'; @@ -67,7 +68,7 @@ export class AddonModChatIndexComponent extends CoreCourseModuleMainActivityComp if (this.chat.chattime && this.chat.schedule && span > 0) { this.chatInfo = { date: CoreTimeUtils.userDate(this.chat.chattime * 1000), - fromnow: CoreTimeUtils.formatTime(span), + fromnow: CoreTime.formatTime(span), }; } else { this.chatInfo = undefined; diff --git a/src/addons/mod/h5pactivity/services/h5pactivity.ts b/src/addons/mod/h5pactivity/services/h5pactivity.ts index 7d5dae0c2..15e4a4a9d 100644 --- a/src/addons/mod/h5pactivity/services/h5pactivity.ts +++ b/src/addons/mod/h5pactivity/services/h5pactivity.ts @@ -16,7 +16,6 @@ import { Injectable } from '@angular/core'; import { CoreSites, CoreSitesCommonWSOptions, CoreSitesReadingStrategy } from '@services/sites'; import { CoreWSExternalWarning, CoreWSExternalFile, CoreWSFile } from '@services/ws'; -import { CoreTimeUtils } from '@services/utils/time'; import { CoreUtils } from '@services/utils/utils'; import { CoreSite, CoreSiteWSPreSets } from '@classes/site'; import { CoreCourseLogHelper } from '@features/course/services/log-helper'; @@ -27,6 +26,7 @@ import { makeSingleton, Translate } from '@singletons/index'; import { CoreWSError } from '@classes/errors/wserror'; import { CoreError } from '@classes/errors/error'; import { AddonModH5PActivityAutoSyncData, AddonModH5PActivitySyncProvider } from './h5pactivity-sync'; +import { CoreTime } from '@singletons/time'; const ROOT_CACHE_KEY = 'mmaModH5PActivity:'; @@ -90,8 +90,8 @@ export class AddonModH5PActivityProvider { formattedAttempt.durationReadable = '-'; formattedAttempt.durationCompact = '-'; } else { - formattedAttempt.durationReadable = CoreTimeUtils.formatTime(attempt.duration, 3); - formattedAttempt.durationCompact = CoreTimeUtils.formatTimeShort(attempt.duration); + formattedAttempt.durationReadable = CoreTime.formatTime(attempt.duration, 3); + formattedAttempt.durationCompact = CoreTime.formatTimeShort(attempt.duration); } return formattedAttempt; diff --git a/src/addons/mod/lesson/components/index/index.ts b/src/addons/mod/lesson/components/index/index.ts index 7b41a3d8b..d2e6204b5 100644 --- a/src/addons/mod/lesson/components/index/index.ts +++ b/src/addons/mod/lesson/components/index/index.ts @@ -25,7 +25,6 @@ import { CoreNavigator } from '@services/navigator'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreForms } from '@singletons/form'; import { CoreTextUtils } from '@services/utils/text'; -import { CoreTimeUtils } from '@services/utils/time'; import { CoreUtils } from '@services/utils/utils'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { AddonModLessonRetakeFinishedInSyncDBRecord } from '../../services/database/lesson'; @@ -47,6 +46,7 @@ import { AddonModLessonSyncResult, } from '../../services/lesson-sync'; import { AddonModLessonModuleHandlerService } from '../../services/handlers/module'; +import { CoreTime } from '@singletons/time'; /** * Component that displays a lesson entry page. @@ -505,15 +505,15 @@ export class AddonModLessonIndexComponent extends CoreCourseModuleMainActivityCo // Format times and grades. if (formattedData.avetime != null && formattedData.numofattempts) { formattedData.avetime = Math.floor(formattedData.avetime / formattedData.numofattempts); - this.avetimeReadable = CoreTimeUtils.formatTime(formattedData.avetime); + this.avetimeReadable = CoreTime.formatTime(formattedData.avetime); } if (formattedData.hightime != null) { - this.hightimeReadable = CoreTimeUtils.formatTime(formattedData.hightime); + this.hightimeReadable = CoreTime.formatTime(formattedData.hightime); } if (formattedData.lowtime != null) { - this.lowtimeReadable = CoreTimeUtils.formatTime(formattedData.lowtime); + this.lowtimeReadable = CoreTime.formatTime(formattedData.lowtime); } if (formattedData.lessonscored) { diff --git a/src/addons/mod/lesson/pages/user-retake/user-retake.page.ts b/src/addons/mod/lesson/pages/user-retake/user-retake.page.ts index 996bb5235..4ac48bf57 100644 --- a/src/addons/mod/lesson/pages/user-retake/user-retake.page.ts +++ b/src/addons/mod/lesson/pages/user-retake/user-retake.page.ts @@ -34,8 +34,8 @@ import { AddonModLessonUserAttemptAnswerPageWSData, } from '../../services/lesson'; import { AddonModLessonAnswerData, AddonModLessonHelper } from '../../services/lesson-helper'; -import { CoreTimeUtils } from '@services/utils/time'; import { CoreCourse } from '@features/course/services/course'; +import { CoreTime } from '@singletons/time'; /** * Page that displays a retake made by a certain user. @@ -222,7 +222,7 @@ export class AddonModLessonUserRetakePage implements OnInit { if (formattedData.userstats.gradeinfo) { // Completed. formattedData.userstats.grade = CoreTextUtils.roundToDecimals(formattedData.userstats.grade, 2); - this.timeTakenReadable = CoreTimeUtils.formatTime(formattedData.userstats.timetotake); + this.timeTakenReadable = CoreTime.formatTime(formattedData.userstats.timetotake); } // Format pages data. diff --git a/src/addons/mod/lesson/services/lesson-helper.ts b/src/addons/mod/lesson/services/lesson-helper.ts index 1ec765591..6047b8030 100644 --- a/src/addons/mod/lesson/services/lesson-helper.ts +++ b/src/addons/mod/lesson/services/lesson-helper.ts @@ -26,6 +26,7 @@ import { AddonModLessonGetPageDataWSResponse, AddonModLessonProvider, } from './lesson'; +import { CoreTime } from '@singletons/time'; /** * Helper service that provides some features for quiz. @@ -531,7 +532,7 @@ export class AddonModLessonHelperProvider { } data.timestart = CoreTimeUtils.userDate(retake.timestart * 1000); if (includeDuration) { - data.duration = CoreTimeUtils.formatTime(retake.timeend - retake.timestart); + data.duration = CoreTime.formatTime(retake.timeend - retake.timestart); } } else { // The user has not completed the retake. diff --git a/src/addons/mod/quiz/accessrules/timelimit/component/timelimit.ts b/src/addons/mod/quiz/accessrules/timelimit/component/timelimit.ts index d68cc64fa..b66ae5b3c 100644 --- a/src/addons/mod/quiz/accessrules/timelimit/component/timelimit.ts +++ b/src/addons/mod/quiz/accessrules/timelimit/component/timelimit.ts @@ -16,7 +16,7 @@ import { Component, Input, OnInit } from '@angular/core'; import { FormGroup } from '@angular/forms'; import { AddonModQuizAttemptWSData, AddonModQuizQuizWSData } from '@addons/mod/quiz/services/quiz'; -import { CoreTimeUtils } from '@services/utils/time'; +import { CoreTime } from '@singletons/time'; /** * Component to render the preflight for time limit. @@ -41,7 +41,7 @@ export class AddonModQuizAccessTimeLimitComponent implements OnInit { return; } - this.readableTimeLimit = CoreTimeUtils.formatTime(this.quiz?.timelimit); + this.readableTimeLimit = CoreTime.formatTime(this.quiz?.timelimit); } } diff --git a/src/addons/mod/quiz/pages/player/player.page.ts b/src/addons/mod/quiz/pages/player/player.page.ts index c793a18f1..ceaf636da 100644 --- a/src/addons/mod/quiz/pages/player/player.page.ts +++ b/src/addons/mod/quiz/pages/player/player.page.ts @@ -24,7 +24,6 @@ import { CoreNavigator } from '@services/navigator'; import { CoreSites, CoreSitesReadingStrategy } from '@services/sites'; import { CoreSync } from '@services/sync'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreTimeUtils } from '@services/utils/time'; import { CoreUtils } from '@services/utils/utils'; import { ModalController, Translate } from '@singletons'; import { CoreEvents } from '@singletons/events'; @@ -47,6 +46,7 @@ import { AddonModQuizSync } from '../../services/quiz-sync'; import { CanLeave } from '@guards/can-leave'; import { CoreForms } from '@singletons/form'; import { CoreDom } from '@singletons/dom'; +import { CoreTime } from '@singletons/time'; /** * Page that allows attempting a quiz. @@ -352,7 +352,7 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave { } if (this.quiz!.timelimit && this.quiz!.timelimit > 0) { - this.readableTimeLimit = CoreTimeUtils.formatTime(this.quiz.timelimit); + this.readableTimeLimit = CoreTime.formatTime(this.quiz.timelimit); } // Get access information for the quiz. diff --git a/src/addons/mod/quiz/pages/review/review.page.ts b/src/addons/mod/quiz/pages/review/review.page.ts index 5443ffe30..1a3a4c3f6 100644 --- a/src/addons/mod/quiz/pages/review/review.page.ts +++ b/src/addons/mod/quiz/pages/review/review.page.ts @@ -19,10 +19,10 @@ import { CoreQuestionHelper } from '@features/question/services/question-helper' import { IonContent, IonRefresher } from '@ionic/angular'; import { CoreNavigator } from '@services/navigator'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreTimeUtils } from '@services/utils/time'; import { CoreUtils } from '@services/utils/utils'; import { Translate } from '@singletons'; import { CoreDom } from '@singletons/dom'; +import { CoreTime } from '@singletons/time'; import { AddonModQuizNavigationModalComponent, AddonModQuizNavigationModalReturn, @@ -276,11 +276,11 @@ export class AddonModQuizReviewPage implements OnInit { const timeTaken = (this.attempt.timefinish || 0) - (this.attempt.timestart || 0); if (timeTaken > 0) { // Format time taken. - this.timeTaken = CoreTimeUtils.formatTime(timeTaken); + this.timeTaken = CoreTime.formatTime(timeTaken); // Calculate overdue time. if (this.quiz.timelimit && timeTaken > this.quiz.timelimit + 60) { - this.overTime = CoreTimeUtils.formatTime(timeTaken - this.quiz.timelimit); + this.overTime = CoreTime.formatTime(timeTaken - this.quiz.timelimit); } } else { this.timeTaken = undefined; diff --git a/src/core/pipes/duration.ts b/src/core/pipes/duration.ts index e1c666289..1e127a89b 100644 --- a/src/core/pipes/duration.ts +++ b/src/core/pipes/duration.ts @@ -14,7 +14,7 @@ import { Pipe, PipeTransform } from '@angular/core'; import { CoreLogger } from '@singletons/logger'; -import { CoreTimeUtils } from '@services/utils/time'; +import { CoreTime } from '@singletons/time'; /** * Filter to turn a number of seconds to a duration. E.g. 60 -> 1 minute. @@ -48,7 +48,7 @@ export class CoreDurationPipe implements PipeTransform { seconds = numberSeconds; } - return CoreTimeUtils.formatTime(seconds); + return CoreTime.formatTime(seconds); } } diff --git a/src/core/services/utils/time.ts b/src/core/services/utils/time.ts index bac183265..a771a6a21 100644 --- a/src/core/services/utils/time.ts +++ b/src/core/services/utils/time.ts @@ -16,6 +16,7 @@ import { Injectable } from '@angular/core'; import moment, { LongDateFormatKey } from 'moment'; import { makeSingleton, Translate } from '@singletons'; +import { CoreTime } from '@singletons/time'; /* * "Utils" service with helper functions for date and time. @@ -160,39 +161,10 @@ export class CoreTimeUtilsProvider { * @param seconds A number of seconds * @param precision Number of elements to have in precision. * @return Seconds in a human readable format. + * @deprecated since app 4.0. Use CoreTime.formatTime instead. */ formatTime(seconds: number, precision = 2): string { - precision = precision || 6; // Use max precision if 0 is passed. - - const eventDuration = moment.duration(Math.abs(seconds), 'seconds'); - let durationString = ''; - - if (precision && eventDuration.years() > 0) { - durationString += ' ' + moment.duration(eventDuration.years(), 'years').humanize(); - precision--; - } - if (precision && eventDuration.months() > 0) { - durationString += ' ' + moment.duration(eventDuration.months(), 'months').humanize(); - precision--; - } - if (precision && eventDuration.days() > 0) { - durationString += ' ' + moment.duration(eventDuration.days(), 'days').humanize(); - precision--; - } - if (precision && eventDuration.hours() > 0) { - durationString += ' ' + moment.duration(eventDuration.hours(), 'hours').humanize(); - precision--; - } - if (precision && eventDuration.minutes() > 0) { - durationString += ' ' + moment.duration(eventDuration.minutes(), 'minutes').humanize(); - precision--; - } - if (precision && (eventDuration.seconds() > 0 || !durationString)) { - durationString += ' ' + moment.duration(eventDuration.seconds(), 'seconds').humanize(); - precision--; - } - - return durationString.trim(); + return CoreTime.formatTime(seconds, precision); } /** @@ -200,21 +172,10 @@ export class CoreTimeUtilsProvider { * * @param seconds Seconds * @return Short human readable text. + * @deprecated since app 4.0. Use CoreTime.formatTimeShort instead. */ formatTimeShort(duration: number): string { - const minutes = Math.floor(duration / 60); - const seconds = duration - minutes * 60; - const durations = []; - - if (minutes > 0) { - durations.push(minutes + '\''); - } - - if (seconds > 0 || minutes === 0) { - durations.push(seconds + '\'\''); - } - - return durations.join(' '); + return CoreTime.formatTimeShort(duration); } /** @@ -223,10 +184,10 @@ export class CoreTimeUtilsProvider { * @param duration Duration in seconds * @param precision Number of elements to have in precision. 0 or undefined to full precission. * @return Duration in a human readable format. - * @deprecated since 4.0. Use formatTime instead. + * @deprecated since app 4.0. Use CoreTime.formatTime instead. */ formatDuration(duration: number, precision?: number): string { - return this.formatTime(duration, precision); + return CoreTime.formatTime(duration, precision); } /** @@ -234,10 +195,10 @@ export class CoreTimeUtilsProvider { * * @param duration Duration in seconds * @return Duration in a short human readable format. - * @deprecated since 4.0. Use formatTime instead. + * @deprecated since app 4.0. Use CoreTime.formatTimeShort instead. */ formatDurationShort(duration: number): string { - return this.formatTimeShort(duration); + return CoreTime.formatTimeShort(duration); } /** diff --git a/src/core/singletons/time.ts b/src/core/singletons/time.ts index 8e8f92018..e3a0f0435 100644 --- a/src/core/singletons/time.ts +++ b/src/core/singletons/time.ts @@ -12,11 +12,76 @@ // See the License for the specific language governing permissions and // limitations under the License. +import moment from 'moment'; + /** * Singleton with helper functions for time operations. */ export class CoreTime { + /** + * Returns years, months, days, hours, minutes and seconds in a human readable format. + * + * @param seconds A number of seconds + * @param precision Number of elements to have in precision. + * @return Seconds in a human readable format. + */ + static formatTime(seconds: number, precision = 2): string { + precision = precision || 6; // Use max precision if 0 is passed. + + const eventDuration = moment.duration(Math.abs(seconds), 'seconds'); + let durationString = ''; + + if (precision && eventDuration.years() > 0) { + durationString += ' ' + moment.duration(eventDuration.years(), 'years').humanize(); + precision--; + } + if (precision && eventDuration.months() > 0) { + durationString += ' ' + moment.duration(eventDuration.months(), 'months').humanize(); + precision--; + } + if (precision && eventDuration.days() > 0) { + durationString += ' ' + moment.duration(eventDuration.days(), 'days').humanize(); + precision--; + } + if (precision && eventDuration.hours() > 0) { + durationString += ' ' + moment.duration(eventDuration.hours(), 'hours').humanize(); + precision--; + } + if (precision && eventDuration.minutes() > 0) { + durationString += ' ' + moment.duration(eventDuration.minutes(), 'minutes').humanize(); + precision--; + } + if (precision && (eventDuration.seconds() > 0 || !durationString)) { + durationString += ' ' + moment.duration(eventDuration.seconds(), 'seconds').humanize(); + precision--; + } + + return durationString.trim(); + } + + /** + * Converts a number of seconds into a short human readable format: minutes and seconds, in fromat: 3' 27''. + * + * @param seconds Seconds + * @return Short human readable text. + */ + static formatTimeShort(duration: number): string { + const minutes = Math.floor(duration / 60); + const seconds = duration - minutes * 60; + const durations = []; + + if (minutes > 0) { + durations.push(minutes + '\''); + } + + if (seconds > 0 || minutes === 0) { + durations.push(seconds + '\'\''); + } + + return durations.join(' '); + } + /** * Wrap a function so that it is called only once. *