time: relative update (#14240)
							parent
							
								
									d75469965e
								
							
						
					
					
						commit
						bf954cc9bc
					
				| 
						 | 
				
			
			@ -87,48 +87,53 @@ const tokens_4 = ['MMMM', 'DDDD', 'DDDo', 'dddd', 'YYYY']
 | 
			
		|||
// custom_format returns a date with custom format
 | 
			
		||||
//
 | 
			
		||||
// |  | Token  | Output |
 | 
			
		||||
// | :-----------  | -------: | :--------- |
 | 
			
		||||
// | Month   | M | 1 2 ... 11 12 |
 | 
			
		||||
// | ----------:  | :------ | :--------- |
 | 
			
		||||
// | **Month**   | M | 1 2 ... 11 12 |
 | 
			
		||||
// |  | Mo | 1st 2nd ... 11th 12th |
 | 
			
		||||
// |  | MM | 01 02 ... 11 12 |
 | 
			
		||||
// |  | MMM | 	Jan Feb ... Nov Dec |
 | 
			
		||||
// |  | MMMM | January February ... November December |
 | 
			
		||||
// | Quarter  | Q | 1 2 3 4 |
 | 
			
		||||
// | **Quarter**  | Q | 1 2 3 4 |
 | 
			
		||||
// |  | QQ | 01 02 03 04 |
 | 
			
		||||
// |  | Qo | 1st 2nd 3rd 4th |
 | 
			
		||||
// | Day of Month  | D | 1 2 ... 30 31 |
 | 
			
		||||
// | **Day of Month**  | D | 1 2 ... 30 31 |
 | 
			
		||||
// |  | Do | 1st 2nd ... 30th 31st |
 | 
			
		||||
// |  | DD | 01 02 ... 30 31 |
 | 
			
		||||
// | Day of Year  | DDD | 1 2 ... 364 365 |
 | 
			
		||||
// | **Day of Year**  | DDD | 1 2 ... 364 365 |
 | 
			
		||||
// |  | DDDo | 1st 2nd ... 364th 365th |
 | 
			
		||||
// |  | DDDD | 001 002 ... 364 365 |
 | 
			
		||||
// | Day of Week  | d | 0 1 ... 5 6 (Sun-Sat) |
 | 
			
		||||
// | **Day of Week**  | d | 0 1 ... 5 6 (Sun-Sat) |
 | 
			
		||||
// |  | c | 1 2 ... 6 7 (Mon-Sun) |
 | 
			
		||||
// |  | dd | Su Mo ... Fr Sa |
 | 
			
		||||
// |  | ddd | Sun Mon ... Fri Sat |
 | 
			
		||||
// |  | dddd | Sunday Monday ... Friday Saturday |
 | 
			
		||||
// | Week of Year  | w | 1 2 ... 52 53 |
 | 
			
		||||
// | **Week of Year**  | w | 1 2 ... 52 53 |
 | 
			
		||||
// |  | wo | 1st 2nd ... 52nd 53rd |
 | 
			
		||||
// |  | ww | 01 02 ... 52 53 |
 | 
			
		||||
// | Year  | YY | 70 71 ... 29 30 |
 | 
			
		||||
// | **Year**  | YY | 70 71 ... 29 30 |
 | 
			
		||||
// |  | YYYY | 1970 1971 ... 2029 2030 |
 | 
			
		||||
// | Era  | N | BC AD |
 | 
			
		||||
// | **Era**  | N | BC AD |
 | 
			
		||||
// |  | NN | Before Christ, Anno Domini |
 | 
			
		||||
// | AM/PM  | A | AM PM |
 | 
			
		||||
// | **AM/PM**  | A | AM PM |
 | 
			
		||||
// |  | a | am pm |
 | 
			
		||||
// | Hour  | H | 0 1 ... 22 23 |
 | 
			
		||||
// | **Hour**  | H | 0 1 ... 22 23 |
 | 
			
		||||
// |  | HH | 00 01 ... 22 23 |
 | 
			
		||||
// |  | h | 1 2 ... 11 12 |
 | 
			
		||||
// |  | hh | 01 02 ... 11 12 |
 | 
			
		||||
// |  | k | 1 2 ... 23 24 |
 | 
			
		||||
// |  | kk | 01 02 ... 23 24 |
 | 
			
		||||
// | Minute  | m | 0 1 ... 58 59 |
 | 
			
		||||
// | **Minute**  | m | 0 1 ... 58 59 |
 | 
			
		||||
// |  | mm | 00 01 ... 58 59 |
 | 
			
		||||
// | Second  | s | 0 1 ... 58 59 |
 | 
			
		||||
// | **Second**  | s | 0 1 ... 58 59 |
 | 
			
		||||
// |  | ss | 00 01 ... 58 59 |
 | 
			
		||||
// | Offset  | Z | -7 -6 ... +5 +6 |
 | 
			
		||||
// | **Offset**  | Z | -7 -6 ... +5 +6 |
 | 
			
		||||
// |  | ZZ | -0700 -0600 ... +0500 +0600 |
 | 
			
		||||
// |  | ZZZ | -07:00 -06:00 ... +05:00 +06:00 |
 | 
			
		||||
//
 | 
			
		||||
// Usage:
 | 
			
		||||
// ```v
 | 
			
		||||
// println(time.now().custom_format('MMMM Mo YY N kk:mm:ss A')) // output like: January 1st 22 AD 13:45:33 PM
 | 
			
		||||
// ```
 | 
			
		||||
pub fn (t Time) custom_format(s string) string {
 | 
			
		||||
	mut tokens := []string{}
 | 
			
		||||
	for i := 0; i < s.len; {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,58 @@
 | 
			
		|||
import time
 | 
			
		||||
 | 
			
		||||
fn test_relative() {
 | 
			
		||||
	// past
 | 
			
		||||
	mut date := time.now()
 | 
			
		||||
	assert date.relative() == 'now'
 | 
			
		||||
	date = date.add_seconds(-61)
 | 
			
		||||
	assert date.relative() == '1 minute ago'
 | 
			
		||||
	assert date.relative_short() == '1m ago'
 | 
			
		||||
	date = date.add_seconds(-120)
 | 
			
		||||
	assert date.relative() == '3 minutes ago'
 | 
			
		||||
	assert date.relative_short() == '3m ago'
 | 
			
		||||
	date = date.add_seconds(-1 * time.seconds_per_hour)
 | 
			
		||||
	assert date.relative() == '1 hour ago'
 | 
			
		||||
	assert date.relative_short() == '1h ago'
 | 
			
		||||
	date = date.add_seconds(-5 * time.seconds_per_hour)
 | 
			
		||||
	assert date.relative() == '6 hours ago'
 | 
			
		||||
	assert date.relative_short() == '6h ago'
 | 
			
		||||
	date = date.add_seconds(-1 * time.seconds_per_day)
 | 
			
		||||
	assert date.relative() == '1 day ago'
 | 
			
		||||
	assert date.relative_short() == '1d ago'
 | 
			
		||||
	date = date.add_seconds(-4 * time.seconds_per_day)
 | 
			
		||||
	assert date.relative() == '5 days ago'
 | 
			
		||||
	assert date.relative_short() == '5d ago'
 | 
			
		||||
	date = time.now().add_seconds(-75 * time.seconds_per_day)
 | 
			
		||||
	assert date.relative() == 'last ${date.custom_format('MMM')} ${date.custom_format('D')}'
 | 
			
		||||
	assert date.relative_short() == '75d ago'
 | 
			
		||||
	date = time.now().add_seconds(-400 * time.seconds_per_day)
 | 
			
		||||
	assert date.relative() == '1 year ago'
 | 
			
		||||
	assert date.relative_short() == '1y ago'
 | 
			
		||||
 | 
			
		||||
	// future
 | 
			
		||||
	date = time.now()
 | 
			
		||||
	date = date.add_seconds(61)
 | 
			
		||||
	assert date.relative() == 'in 1 minute'
 | 
			
		||||
	assert date.relative_short() == 'in 1m'
 | 
			
		||||
	date = date.add_seconds(120)
 | 
			
		||||
	assert date.relative() == 'in 3 minutes'
 | 
			
		||||
	assert date.relative_short() == 'in 3m'
 | 
			
		||||
	date = date.add_seconds(1 * time.seconds_per_hour)
 | 
			
		||||
	assert date.relative() == 'in 1 hour'
 | 
			
		||||
	assert date.relative_short() == 'in 1h'
 | 
			
		||||
	date = date.add_seconds(5 * time.seconds_per_hour)
 | 
			
		||||
	assert date.relative() == 'in 6 hours'
 | 
			
		||||
	assert date.relative_short() == 'in 6h'
 | 
			
		||||
	date = date.add_seconds(time.seconds_per_day)
 | 
			
		||||
	assert date.relative() == 'in 1 day'
 | 
			
		||||
	assert date.relative_short() == 'in 1d'
 | 
			
		||||
	date = date.add_seconds(4 * time.seconds_per_day)
 | 
			
		||||
	assert date.relative() == 'in 5 days'
 | 
			
		||||
	assert date.relative_short() == 'in 5d'
 | 
			
		||||
	date = time.now().add_seconds(75 * time.seconds_per_day)
 | 
			
		||||
	assert date.relative() == 'on ${date.custom_format('MMM')} ${date.custom_format('D')}'
 | 
			
		||||
	assert date.relative_short() == 'in 75d'
 | 
			
		||||
	date = time.now().add_seconds(400 * time.seconds_per_day)
 | 
			
		||||
	assert date.relative() == 'in 1 year'
 | 
			
		||||
	assert date.relative_short() == 'in 1y'
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										118
									
								
								vlib/time/time.v
								
								
								
								
							
							
						
						
									
										118
									
								
								vlib/time/time.v
								
								
								
								
							| 
						 | 
				
			
			@ -131,48 +131,70 @@ pub fn since(t Time) Duration {
 | 
			
		|||
 | 
			
		||||
// relative returns a string representation of the difference between t
 | 
			
		||||
// and the current time.
 | 
			
		||||
//
 | 
			
		||||
// Sample outputs:
 | 
			
		||||
// ```
 | 
			
		||||
// // future
 | 
			
		||||
// now
 | 
			
		||||
// in 5 minutes
 | 
			
		||||
// in 1 day
 | 
			
		||||
// on Feb 17
 | 
			
		||||
// // past
 | 
			
		||||
// 2 hours ago
 | 
			
		||||
// last Jan 15
 | 
			
		||||
// 5 years ago
 | 
			
		||||
// ```
 | 
			
		||||
pub fn (t Time) relative() string {
 | 
			
		||||
	znow := now()
 | 
			
		||||
	secs := znow.unix - t.unix
 | 
			
		||||
	if secs <= 30 {
 | 
			
		||||
		// right now or in the future
 | 
			
		||||
		// TODO handle time in the future
 | 
			
		||||
	mut secs := znow.unix - t.unix
 | 
			
		||||
	mut prefix := ''
 | 
			
		||||
	mut suffix := ''
 | 
			
		||||
	if secs < 0 {
 | 
			
		||||
		secs *= -1
 | 
			
		||||
		prefix = 'in '
 | 
			
		||||
	} else {
 | 
			
		||||
		suffix = ' ago'
 | 
			
		||||
	}
 | 
			
		||||
	if secs < time.seconds_per_minute / 2 {
 | 
			
		||||
		return 'now'
 | 
			
		||||
	}
 | 
			
		||||
	if secs < 60 {
 | 
			
		||||
		return '1m'
 | 
			
		||||
	}
 | 
			
		||||
	if secs < 3600 {
 | 
			
		||||
		m := secs / 60
 | 
			
		||||
	if secs < time.seconds_per_hour {
 | 
			
		||||
		m := secs / time.seconds_per_minute
 | 
			
		||||
		if m == 1 {
 | 
			
		||||
			return '1 minute ago'
 | 
			
		||||
			return '${prefix}1 minute$suffix'
 | 
			
		||||
		}
 | 
			
		||||
		return '$m minutes ago'
 | 
			
		||||
		return '$prefix$m minutes$suffix'
 | 
			
		||||
	}
 | 
			
		||||
	if secs < 3600 * 24 {
 | 
			
		||||
		h := secs / 3600
 | 
			
		||||
	if secs < time.seconds_per_hour * 24 {
 | 
			
		||||
		h := secs / time.seconds_per_hour
 | 
			
		||||
		if h == 1 {
 | 
			
		||||
			return '1 hour ago'
 | 
			
		||||
			return '${prefix}1 hour$suffix'
 | 
			
		||||
		}
 | 
			
		||||
		return '$h hours ago'
 | 
			
		||||
		return '$prefix$h hours$suffix'
 | 
			
		||||
	}
 | 
			
		||||
	if secs < 3600 * 24 * 5 {
 | 
			
		||||
		d := secs / 3600 / 24
 | 
			
		||||
	if secs < time.seconds_per_hour * 24 * 7 {
 | 
			
		||||
		d := secs / time.seconds_per_hour / 24
 | 
			
		||||
		if d == 1 {
 | 
			
		||||
			return '1 day ago'
 | 
			
		||||
			return '${prefix}1 day$suffix'
 | 
			
		||||
		}
 | 
			
		||||
		return '$d days ago'
 | 
			
		||||
		return '$prefix$d days$suffix'
 | 
			
		||||
	}
 | 
			
		||||
	if secs > 3600 * 24 * 10000 {
 | 
			
		||||
		return ''
 | 
			
		||||
	if secs < time.seconds_per_hour * 24 * 365 {
 | 
			
		||||
		if prefix == 'in ' {
 | 
			
		||||
			return 'on $t.md()'
 | 
			
		||||
		}
 | 
			
		||||
		return 'last $t.md()'
 | 
			
		||||
	}
 | 
			
		||||
	return t.md()
 | 
			
		||||
	y := secs / time.seconds_per_hour / 24 / 365
 | 
			
		||||
	if y == 1 {
 | 
			
		||||
		return '${prefix}1 year$suffix'
 | 
			
		||||
	}
 | 
			
		||||
	return '$prefix$y years$suffix'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// relative_short returns a string saying how long ago a time occured as follows:
 | 
			
		||||
// 0-30 seconds: `"now"`; 30-60 seconds: `"1m"`; anything else is rounded to the
 | 
			
		||||
// nearest minute, hour or day; anything higher than 10000 days (about 27 years)
 | 
			
		||||
// years returns an empty string.
 | 
			
		||||
// nearest minute, hour, day, or year
 | 
			
		||||
// Some Examples:
 | 
			
		||||
// `0s -> 'now'`;
 | 
			
		||||
// `20s -> 'now'`;
 | 
			
		||||
| 
						 | 
				
			
			@ -184,28 +206,44 @@ pub fn (t Time) relative() string {
 | 
			
		|||
// `15842354871s -> ''`
 | 
			
		||||
pub fn (t Time) relative_short() string {
 | 
			
		||||
	znow := now()
 | 
			
		||||
	secs := znow.unix - t.unix
 | 
			
		||||
	if secs <= 30 {
 | 
			
		||||
		// right now or in the future
 | 
			
		||||
		// TODO handle time in the future
 | 
			
		||||
	mut secs := znow.unix - t.unix
 | 
			
		||||
	mut prefix := ''
 | 
			
		||||
	mut suffix := ''
 | 
			
		||||
	if secs < 0 {
 | 
			
		||||
		secs *= -1
 | 
			
		||||
		prefix = 'in '
 | 
			
		||||
	} else {
 | 
			
		||||
		suffix = ' ago'
 | 
			
		||||
	}
 | 
			
		||||
	if secs < time.seconds_per_minute / 2 {
 | 
			
		||||
		return 'now'
 | 
			
		||||
	}
 | 
			
		||||
	if secs < 60 {
 | 
			
		||||
		return '1m'
 | 
			
		||||
	if secs < time.seconds_per_hour {
 | 
			
		||||
		m := secs / time.seconds_per_minute
 | 
			
		||||
		if m == 1 {
 | 
			
		||||
			return '${prefix}1m$suffix'
 | 
			
		||||
		}
 | 
			
		||||
		return '$prefix${m}m$suffix'
 | 
			
		||||
	}
 | 
			
		||||
	if secs < 3600 {
 | 
			
		||||
		return '${secs / 60}m'
 | 
			
		||||
	if secs < time.seconds_per_hour * 24 {
 | 
			
		||||
		h := secs / time.seconds_per_hour
 | 
			
		||||
		if h == 1 {
 | 
			
		||||
			return '${prefix}1h$suffix'
 | 
			
		||||
		}
 | 
			
		||||
		return '$prefix${h}h$suffix'
 | 
			
		||||
	}
 | 
			
		||||
	if secs < 3600 * 24 {
 | 
			
		||||
		return '${secs / 3600}h'
 | 
			
		||||
	if secs < time.seconds_per_hour * 24 * 365 {
 | 
			
		||||
		d := secs / time.seconds_per_hour / 24
 | 
			
		||||
		if d == 1 {
 | 
			
		||||
			return '${prefix}1d$suffix'
 | 
			
		||||
		}
 | 
			
		||||
		return '$prefix${d}d$suffix'
 | 
			
		||||
	}
 | 
			
		||||
	if secs < 3600 * 24 * 5 {
 | 
			
		||||
		return '${secs / 3600 / 24}d'
 | 
			
		||||
	y := secs / time.seconds_per_hour / 24 / 365
 | 
			
		||||
	if y == 1 {
 | 
			
		||||
		return '${prefix}1y$suffix'
 | 
			
		||||
	}
 | 
			
		||||
	if secs > 3600 * 24 * 10000 {
 | 
			
		||||
		return ''
 | 
			
		||||
	}
 | 
			
		||||
	return t.md()
 | 
			
		||||
	return '$prefix${y}y$suffix'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// day_of_week returns the current day of a given year, month, and day,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue