github.com/gitbundle/modules@v0.0.0-20231025071548-85b91c5c3b01/timeutil/since.go (about)

     1  // Copyright 2023 The GitBundle Inc. All rights reserved.
     2  // Copyright 2017 The Gitea Authors. All rights reserved.
     3  // Use of this source code is governed by a MIT-style
     4  // license that can be found in the LICENSE file.
     5  
     6  package timeutil
     7  
     8  import (
     9  	"fmt"
    10  	"html/template"
    11  	"math"
    12  	"strings"
    13  	"time"
    14  
    15  	"github.com/gitbundle/modules/setting"
    16  	"github.com/gitbundle/modules/translation/i18n"
    17  )
    18  
    19  // Seconds-based time units
    20  const (
    21  	Minute = 60
    22  	Hour   = 60 * Minute
    23  	Day    = 24 * Hour
    24  	Week   = 7 * Day
    25  	Month  = 30 * Day
    26  	Year   = 12 * Month
    27  )
    28  
    29  func round(s float64) int64 {
    30  	return int64(math.Round(s))
    31  }
    32  
    33  func computeTimeDiffFloor(diff int64, lang string) (int64, string) {
    34  	diffStr := ""
    35  	switch {
    36  	case diff <= 0:
    37  		diff = 0
    38  		diffStr = i18n.Tr(lang, "tool.now")
    39  	case diff < 2:
    40  		diff = 0
    41  		diffStr = i18n.Tr(lang, "tool.1s")
    42  	case diff < 1*Minute:
    43  		diffStr = i18n.Tr(lang, "tool.seconds", diff)
    44  		diff = 0
    45  
    46  	case diff < 2*Minute:
    47  		diff -= 1 * Minute
    48  		diffStr = i18n.Tr(lang, "tool.1m")
    49  	case diff < 1*Hour:
    50  		diffStr = i18n.Tr(lang, "tool.minutes", diff/Minute)
    51  		diff -= diff / Minute * Minute
    52  
    53  	case diff < 2*Hour:
    54  		diff -= 1 * Hour
    55  		diffStr = i18n.Tr(lang, "tool.1h")
    56  	case diff < 1*Day:
    57  		diffStr = i18n.Tr(lang, "tool.hours", diff/Hour)
    58  		diff -= diff / Hour * Hour
    59  
    60  	case diff < 2*Day:
    61  		diff -= 1 * Day
    62  		diffStr = i18n.Tr(lang, "tool.1d")
    63  	case diff < 1*Week:
    64  		diffStr = i18n.Tr(lang, "tool.days", diff/Day)
    65  		diff -= diff / Day * Day
    66  
    67  	case diff < 2*Week:
    68  		diff -= 1 * Week
    69  		diffStr = i18n.Tr(lang, "tool.1w")
    70  	case diff < 1*Month:
    71  		diffStr = i18n.Tr(lang, "tool.weeks", diff/Week)
    72  		diff -= diff / Week * Week
    73  
    74  	case diff < 2*Month:
    75  		diff -= 1 * Month
    76  		diffStr = i18n.Tr(lang, "tool.1mon")
    77  	case diff < 1*Year:
    78  		diffStr = i18n.Tr(lang, "tool.months", diff/Month)
    79  		diff -= diff / Month * Month
    80  
    81  	case diff < 2*Year:
    82  		diff -= 1 * Year
    83  		diffStr = i18n.Tr(lang, "tool.1y")
    84  	default:
    85  		diffStr = i18n.Tr(lang, "tool.years", diff/Year)
    86  		diff -= (diff / Year) * Year
    87  	}
    88  	return diff, diffStr
    89  }
    90  
    91  func computeTimeDiff(diff int64, lang string) (int64, string) {
    92  	diffStr := ""
    93  	switch {
    94  	case diff <= 0:
    95  		diff = 0
    96  		diffStr = i18n.Tr(lang, "tool.now")
    97  	case diff < 2:
    98  		diff = 0
    99  		diffStr = i18n.Tr(lang, "tool.1s")
   100  	case diff < 1*Minute:
   101  		diffStr = i18n.Tr(lang, "tool.seconds", diff)
   102  		diff = 0
   103  
   104  	case diff < Minute+Minute/2:
   105  		diff -= 1 * Minute
   106  		diffStr = i18n.Tr(lang, "tool.1m")
   107  	case diff < 1*Hour:
   108  		minutes := round(float64(diff) / Minute)
   109  		if minutes > 1 {
   110  			diffStr = i18n.Tr(lang, "tool.minutes", minutes)
   111  		} else {
   112  			diffStr = i18n.Tr(lang, "tool.1m")
   113  		}
   114  		diff -= diff / Minute * Minute
   115  
   116  	case diff < Hour+Hour/2:
   117  		diff -= 1 * Hour
   118  		diffStr = i18n.Tr(lang, "tool.1h")
   119  	case diff < 1*Day:
   120  		hours := round(float64(diff) / Hour)
   121  		if hours > 1 {
   122  			diffStr = i18n.Tr(lang, "tool.hours", hours)
   123  		} else {
   124  			diffStr = i18n.Tr(lang, "tool.1h")
   125  		}
   126  		diff -= diff / Hour * Hour
   127  
   128  	case diff < Day+Day/2:
   129  		diff -= 1 * Day
   130  		diffStr = i18n.Tr(lang, "tool.1d")
   131  	case diff < 1*Week:
   132  		days := round(float64(diff) / Day)
   133  		if days > 1 {
   134  			diffStr = i18n.Tr(lang, "tool.days", days)
   135  		} else {
   136  			diffStr = i18n.Tr(lang, "tool.1d")
   137  		}
   138  		diff -= diff / Day * Day
   139  
   140  	case diff < Week+Week/2:
   141  		diff -= 1 * Week
   142  		diffStr = i18n.Tr(lang, "tool.1w")
   143  	case diff < 1*Month:
   144  		weeks := round(float64(diff) / Week)
   145  		if weeks > 1 {
   146  			diffStr = i18n.Tr(lang, "tool.weeks", weeks)
   147  		} else {
   148  			diffStr = i18n.Tr(lang, "tool.1w")
   149  		}
   150  		diff -= diff / Week * Week
   151  
   152  	case diff < 1*Month+Month/2:
   153  		diff -= 1 * Month
   154  		diffStr = i18n.Tr(lang, "tool.1mon")
   155  	case diff < 1*Year:
   156  		months := round(float64(diff) / Month)
   157  		if months > 1 {
   158  			diffStr = i18n.Tr(lang, "tool.months", months)
   159  		} else {
   160  			diffStr = i18n.Tr(lang, "tool.1mon")
   161  		}
   162  		diff -= diff / Month * Month
   163  
   164  	case diff < Year+Year/2:
   165  		diff -= 1 * Year
   166  		diffStr = i18n.Tr(lang, "tool.1y")
   167  	default:
   168  		years := round(float64(diff) / Year)
   169  		if years > 1 {
   170  			diffStr = i18n.Tr(lang, "tool.years", years)
   171  		} else {
   172  			diffStr = i18n.Tr(lang, "tool.1y")
   173  		}
   174  		diff -= (diff / Year) * Year
   175  	}
   176  	return diff, diffStr
   177  }
   178  
   179  // MinutesToFriendly returns a user friendly string with number of minutes
   180  // converted to hours and minutes.
   181  func MinutesToFriendly(minutes int, lang string) string {
   182  	duration := time.Duration(minutes) * time.Minute
   183  	return TimeSincePro(time.Now().Add(-duration), lang)
   184  }
   185  
   186  // TimeSincePro calculates the time interval and generate full user-friendly string.
   187  func TimeSincePro(then time.Time, lang string) string {
   188  	return timeSincePro(then, time.Now(), lang)
   189  }
   190  
   191  func timeSincePro(then, now time.Time, lang string) string {
   192  	diff := now.Unix() - then.Unix()
   193  
   194  	if then.After(now) {
   195  		return i18n.Tr(lang, "tool.future")
   196  	}
   197  	if diff == 0 {
   198  		return i18n.Tr(lang, "tool.now")
   199  	}
   200  
   201  	var timeStr, diffStr string
   202  	for {
   203  		if diff == 0 {
   204  			break
   205  		}
   206  
   207  		diff, diffStr = computeTimeDiffFloor(diff, lang)
   208  		timeStr += ", " + diffStr
   209  	}
   210  	return strings.TrimPrefix(timeStr, ", ")
   211  }
   212  
   213  func timeSince(then, now time.Time, lang string) string {
   214  	return timeSinceUnix(then.Unix(), now.Unix(), lang)
   215  }
   216  
   217  func timeSinceUnix(then, now int64, lang string) string {
   218  	lbl := "tool.ago"
   219  	diff := now - then
   220  	if then > now {
   221  		lbl = "tool.from_now"
   222  		diff = then - now
   223  	}
   224  	if diff <= 0 {
   225  		return i18n.Tr(lang, "tool.now")
   226  	}
   227  
   228  	_, diffStr := computeTimeDiff(diff, lang)
   229  	return i18n.Tr(lang, lbl, diffStr)
   230  }
   231  
   232  // RawTimeSince retrieves i18n key of time since t
   233  func RawTimeSince(t time.Time, lang string) string {
   234  	return timeSince(t, time.Now(), lang)
   235  }
   236  
   237  // TimeSince calculates the time interval and generate user-friendly string.
   238  func TimeSince(then time.Time, lang string) template.HTML {
   239  	return htmlTimeSince(then, time.Now(), lang)
   240  }
   241  
   242  func htmlTimeSince(then, now time.Time, lang string) template.HTML {
   243  	return template.HTML(fmt.Sprintf(`<span class="time-since" title="%s">%s</span>`,
   244  		then.In(setting.DefaultUILocation).Format(GetTimeFormat(lang)),
   245  		timeSince(then, now, lang)))
   246  }
   247  
   248  // TimeSinceUnix calculates the time interval and generate user-friendly string.
   249  func TimeSinceUnix(then TimeStamp, lang string) template.HTML {
   250  	return htmlTimeSinceUnix(then, TimeStamp(time.Now().Unix()), lang)
   251  }
   252  
   253  func htmlTimeSinceUnix(then, now TimeStamp, lang string) template.HTML {
   254  	return template.HTML(fmt.Sprintf(`<span class="time-since" title="%s">%s</span>`,
   255  		then.FormatInLocation(GetTimeFormat(lang), setting.DefaultUILocation),
   256  		timeSinceUnix(int64(then), int64(now), lang)))
   257  }