clock/0000755000176200001440000000000014430501615011344 5ustar liggesusersclock/NAMESPACE0000644000176200001440000007431414427165557012615 0ustar liggesusers# Generated by roxygen2: do not edit by hand S3method("[",clock_rcrd) S3method("[[",clock_rcrd) S3method("names<-",clock_rcrd) S3method(add_days,Date) S3method(add_days,POSIXt) S3method(add_days,clock_calendar) S3method(add_days,clock_duration) S3method(add_days,clock_time_point) S3method(add_days,clock_weekday) S3method(add_days,clock_zoned_time) S3method(add_days,default) S3method(add_hours,POSIXt) S3method(add_hours,clock_calendar) S3method(add_hours,clock_duration) S3method(add_hours,clock_time_point) S3method(add_hours,clock_zoned_time) S3method(add_hours,default) S3method(add_microseconds,clock_calendar) S3method(add_microseconds,clock_duration) S3method(add_microseconds,clock_time_point) S3method(add_microseconds,clock_zoned_time) S3method(add_microseconds,default) S3method(add_milliseconds,clock_calendar) S3method(add_milliseconds,clock_duration) S3method(add_milliseconds,clock_time_point) S3method(add_milliseconds,clock_zoned_time) S3method(add_milliseconds,default) S3method(add_minutes,POSIXt) S3method(add_minutes,clock_calendar) S3method(add_minutes,clock_duration) S3method(add_minutes,clock_time_point) S3method(add_minutes,clock_zoned_time) S3method(add_minutes,default) S3method(add_months,Date) S3method(add_months,POSIXt) S3method(add_months,clock_duration) S3method(add_months,clock_time_point) S3method(add_months,clock_year_month_day) S3method(add_months,clock_year_month_weekday) S3method(add_months,clock_zoned_time) S3method(add_months,default) S3method(add_nanoseconds,clock_calendar) S3method(add_nanoseconds,clock_duration) S3method(add_nanoseconds,clock_time_point) S3method(add_nanoseconds,clock_zoned_time) S3method(add_nanoseconds,default) S3method(add_quarters,Date) S3method(add_quarters,POSIXt) S3method(add_quarters,clock_duration) S3method(add_quarters,clock_time_point) S3method(add_quarters,clock_year_month_day) S3method(add_quarters,clock_year_month_weekday) S3method(add_quarters,clock_year_quarter_day) S3method(add_quarters,clock_zoned_time) S3method(add_quarters,default) S3method(add_seconds,POSIXt) S3method(add_seconds,clock_calendar) S3method(add_seconds,clock_duration) S3method(add_seconds,clock_time_point) S3method(add_seconds,clock_zoned_time) S3method(add_seconds,default) S3method(add_weeks,Date) S3method(add_weeks,POSIXt) S3method(add_weeks,clock_calendar) S3method(add_weeks,clock_duration) S3method(add_weeks,clock_time_point) S3method(add_weeks,clock_zoned_time) S3method(add_weeks,default) S3method(add_years,Date) S3method(add_years,POSIXt) S3method(add_years,clock_duration) S3method(add_years,clock_iso_year_week_day) S3method(add_years,clock_time_point) S3method(add_years,clock_year_day) S3method(add_years,clock_year_month_day) S3method(add_years,clock_year_month_weekday) S3method(add_years,clock_year_quarter_day) S3method(add_years,clock_year_week_day) S3method(add_years,clock_zoned_time) S3method(add_years,default) S3method(as.Date,clock_calendar) S3method(as.Date,clock_time_point) S3method(as.Date,clock_zoned_time) S3method(as.POSIXct,clock_calendar) S3method(as.POSIXct,clock_naive_time) S3method(as.POSIXct,clock_sys_time) S3method(as.POSIXct,clock_zoned_time) S3method(as.POSIXlt,clock_calendar) S3method(as.POSIXlt,clock_naive_time) S3method(as.POSIXlt,clock_sys_time) S3method(as.POSIXlt,clock_zoned_time) S3method(as.character,clock_duration) S3method(as.character,clock_iso_year_week_day) S3method(as.character,clock_naive_time) S3method(as.character,clock_sys_time) S3method(as.character,clock_weekday) S3method(as.character,clock_year_day) S3method(as.character,clock_year_month_day) S3method(as.character,clock_year_month_weekday) S3method(as.character,clock_year_quarter_day) S3method(as.character,clock_year_week_day) S3method(as.character,clock_zoned_time) S3method(as.double,clock_duration) S3method(as.integer,clock_duration) S3method(as_date,Date) S3method(as_date,POSIXt) S3method(as_date,clock_calendar) S3method(as_date,clock_time_point) S3method(as_date,clock_zoned_time) S3method(as_date_time,Date) S3method(as_date_time,POSIXt) S3method(as_date_time,clock_calendar) S3method(as_date_time,clock_naive_time) S3method(as_date_time,clock_sys_time) S3method(as_date_time,clock_zoned_time) S3method(as_duration,clock_duration) S3method(as_duration,clock_time_point) S3method(as_iso_year_week_day,Date) S3method(as_iso_year_week_day,POSIXt) S3method(as_iso_year_week_day,clock_calendar) S3method(as_iso_year_week_day,clock_iso_year_week_day) S3method(as_iso_year_week_day,clock_time_point) S3method(as_iso_year_week_day,default) S3method(as_naive_time,Date) S3method(as_naive_time,POSIXt) S3method(as_naive_time,clock_duration) S3method(as_naive_time,clock_iso_year_week_day) S3method(as_naive_time,clock_naive_time) S3method(as_naive_time,clock_sys_time) S3method(as_naive_time,clock_year_day) S3method(as_naive_time,clock_year_month_day) S3method(as_naive_time,clock_year_month_weekday) S3method(as_naive_time,clock_year_quarter_day) S3method(as_naive_time,clock_year_week_day) S3method(as_naive_time,clock_zoned_time) S3method(as_naive_time,default) S3method(as_sys_time,Date) S3method(as_sys_time,POSIXt) S3method(as_sys_time,clock_duration) S3method(as_sys_time,clock_iso_year_week_day) S3method(as_sys_time,clock_naive_time) S3method(as_sys_time,clock_sys_time) S3method(as_sys_time,clock_year_day) S3method(as_sys_time,clock_year_month_day) S3method(as_sys_time,clock_year_month_weekday) S3method(as_sys_time,clock_year_quarter_day) S3method(as_sys_time,clock_year_week_day) S3method(as_sys_time,clock_zoned_time) S3method(as_sys_time,default) S3method(as_weekday,Date) S3method(as_weekday,POSIXt) S3method(as_weekday,clock_calendar) S3method(as_weekday,clock_time_point) S3method(as_weekday,clock_weekday) S3method(as_year_day,Date) S3method(as_year_day,POSIXt) S3method(as_year_day,clock_calendar) S3method(as_year_day,clock_time_point) S3method(as_year_day,clock_year_day) S3method(as_year_day,default) S3method(as_year_month_day,Date) S3method(as_year_month_day,POSIXt) S3method(as_year_month_day,clock_calendar) S3method(as_year_month_day,clock_time_point) S3method(as_year_month_day,clock_year_month_day) S3method(as_year_month_day,default) S3method(as_year_month_weekday,Date) S3method(as_year_month_weekday,POSIXt) S3method(as_year_month_weekday,clock_calendar) S3method(as_year_month_weekday,clock_time_point) S3method(as_year_month_weekday,clock_year_month_weekday) S3method(as_year_month_weekday,default) S3method(as_year_quarter_day,Date) S3method(as_year_quarter_day,POSIXt) S3method(as_year_quarter_day,clock_calendar) S3method(as_year_quarter_day,clock_time_point) S3method(as_year_quarter_day,clock_year_quarter_day) S3method(as_year_quarter_day,default) S3method(as_year_week_day,Date) S3method(as_year_week_day,POSIXt) S3method(as_year_week_day,clock_calendar) S3method(as_year_week_day,clock_time_point) S3method(as_year_week_day,clock_year_week_day) S3method(as_year_week_day,default) S3method(as_zoned_time,Date) S3method(as_zoned_time,POSIXt) S3method(as_zoned_time,clock_naive_time) S3method(as_zoned_time,clock_sys_time) S3method(as_zoned_time,clock_zoned_time) S3method(as_zoned_time,default) S3method(calendar_count_between,clock_calendar) S3method(calendar_count_between,clock_iso_year_week_day) S3method(calendar_count_between,clock_year_day) S3method(calendar_count_between,clock_year_month_day) S3method(calendar_count_between,clock_year_month_weekday) S3method(calendar_count_between,clock_year_quarter_day) S3method(calendar_count_between,clock_year_week_day) S3method(calendar_end,clock_calendar) S3method(calendar_end,clock_iso_year_week_day) S3method(calendar_end,clock_year_day) S3method(calendar_end,clock_year_month_day) S3method(calendar_end,clock_year_month_weekday) S3method(calendar_end,clock_year_quarter_day) S3method(calendar_end,clock_year_week_day) S3method(calendar_group,clock_calendar) S3method(calendar_group,clock_iso_year_week_day) S3method(calendar_group,clock_year_day) S3method(calendar_group,clock_year_month_day) S3method(calendar_group,clock_year_month_weekday) S3method(calendar_group,clock_year_quarter_day) S3method(calendar_group,clock_year_week_day) S3method(calendar_is_precision,clock_iso_year_week_day) S3method(calendar_is_precision,clock_year_day) S3method(calendar_is_precision,clock_year_month_day) S3method(calendar_is_precision,clock_year_month_weekday) S3method(calendar_is_precision,clock_year_quarter_day) S3method(calendar_is_precision,clock_year_week_day) S3method(calendar_leap_year,clock_calendar) S3method(calendar_leap_year,clock_iso_year_week_day) S3method(calendar_leap_year,clock_year_day) S3method(calendar_leap_year,clock_year_month_day) S3method(calendar_leap_year,clock_year_month_weekday) S3method(calendar_leap_year,clock_year_quarter_day) S3method(calendar_leap_year,clock_year_week_day) S3method(calendar_month_factor,clock_calendar) S3method(calendar_month_factor,clock_year_month_day) S3method(calendar_month_factor,clock_year_month_weekday) S3method(calendar_name,clock_iso_year_week_day) S3method(calendar_name,clock_year_day) S3method(calendar_name,clock_year_month_day) S3method(calendar_name,clock_year_month_weekday) S3method(calendar_name,clock_year_quarter_day) S3method(calendar_name,clock_year_week_day) S3method(calendar_narrow,clock_calendar) S3method(calendar_narrow,clock_iso_year_week_day) S3method(calendar_narrow,clock_year_day) S3method(calendar_narrow,clock_year_month_day) S3method(calendar_narrow,clock_year_month_weekday) S3method(calendar_narrow,clock_year_quarter_day) S3method(calendar_narrow,clock_year_week_day) S3method(calendar_precision,clock_calendar) S3method(calendar_start,clock_calendar) S3method(calendar_start,clock_iso_year_week_day) S3method(calendar_start,clock_year_day) S3method(calendar_start,clock_year_month_day) S3method(calendar_start,clock_year_month_weekday) S3method(calendar_start,clock_year_quarter_day) S3method(calendar_start,clock_year_week_day) S3method(calendar_widen,clock_calendar) S3method(calendar_widen,clock_iso_year_week_day) S3method(calendar_widen,clock_year_day) S3method(calendar_widen,clock_year_month_day) S3method(calendar_widen,clock_year_month_weekday) S3method(calendar_widen,clock_year_quarter_day) S3method(calendar_widen,clock_year_week_day) S3method(clock_maximum,clock_duration) S3method(clock_maximum,clock_iso_year_week_day) S3method(clock_maximum,clock_time_point) S3method(clock_maximum,clock_year_day) S3method(clock_maximum,clock_year_month_day) S3method(clock_maximum,clock_year_month_weekday) S3method(clock_maximum,clock_year_quarter_day) S3method(clock_maximum,clock_year_week_day) S3method(clock_minimum,clock_duration) S3method(clock_minimum,clock_iso_year_week_day) S3method(clock_minimum,clock_time_point) S3method(clock_minimum,clock_year_day) S3method(clock_minimum,clock_year_month_day) S3method(clock_minimum,clock_year_month_weekday) S3method(clock_minimum,clock_year_quarter_day) S3method(clock_minimum,clock_year_week_day) S3method(date_ceiling,Date) S3method(date_ceiling,POSIXt) S3method(date_count_between,Date) S3method(date_count_between,POSIXt) S3method(date_end,Date) S3method(date_end,POSIXt) S3method(date_floor,Date) S3method(date_floor,POSIXt) S3method(date_format,Date) S3method(date_format,POSIXt) S3method(date_group,Date) S3method(date_group,POSIXt) S3method(date_leap_year,Date) S3method(date_leap_year,POSIXt) S3method(date_round,Date) S3method(date_round,POSIXt) S3method(date_seq,Date) S3method(date_seq,POSIXt) S3method(date_set_zone,Date) S3method(date_set_zone,POSIXt) S3method(date_shift,Date) S3method(date_shift,POSIXt) S3method(date_spanning_seq,Date) S3method(date_spanning_seq,POSIXt) S3method(date_start,Date) S3method(date_start,POSIXt) S3method(date_zone,Date) S3method(date_zone,POSIXt) S3method(format,clock_duration) S3method(format,clock_iso_year_week_day) S3method(format,clock_time_point) S3method(format,clock_weekday) S3method(format,clock_year_day) S3method(format,clock_year_month_day) S3method(format,clock_year_month_weekday) S3method(format,clock_year_quarter_day) S3method(format,clock_year_week_day) S3method(format,clock_zoned_time) S3method(get_day,Date) S3method(get_day,POSIXt) S3method(get_day,clock_iso_year_week_day) S3method(get_day,clock_year_day) S3method(get_day,clock_year_month_day) S3method(get_day,clock_year_month_weekday) S3method(get_day,clock_year_quarter_day) S3method(get_day,clock_year_week_day) S3method(get_day,default) S3method(get_hour,POSIXt) S3method(get_hour,clock_iso_year_week_day) S3method(get_hour,clock_year_day) S3method(get_hour,clock_year_month_day) S3method(get_hour,clock_year_month_weekday) S3method(get_hour,clock_year_quarter_day) S3method(get_hour,clock_year_week_day) S3method(get_hour,default) S3method(get_index,clock_year_month_weekday) S3method(get_index,default) S3method(get_microsecond,clock_iso_year_week_day) S3method(get_microsecond,clock_year_day) S3method(get_microsecond,clock_year_month_day) S3method(get_microsecond,clock_year_month_weekday) S3method(get_microsecond,clock_year_quarter_day) S3method(get_microsecond,clock_year_week_day) S3method(get_microsecond,default) S3method(get_millisecond,clock_iso_year_week_day) S3method(get_millisecond,clock_year_day) S3method(get_millisecond,clock_year_month_day) S3method(get_millisecond,clock_year_month_weekday) S3method(get_millisecond,clock_year_quarter_day) S3method(get_millisecond,clock_year_week_day) S3method(get_millisecond,default) S3method(get_minute,POSIXt) S3method(get_minute,clock_iso_year_week_day) S3method(get_minute,clock_year_day) S3method(get_minute,clock_year_month_day) S3method(get_minute,clock_year_month_weekday) S3method(get_minute,clock_year_quarter_day) S3method(get_minute,clock_year_week_day) S3method(get_minute,default) S3method(get_month,Date) S3method(get_month,POSIXt) S3method(get_month,clock_year_month_day) S3method(get_month,clock_year_month_weekday) S3method(get_month,default) S3method(get_nanosecond,clock_iso_year_week_day) S3method(get_nanosecond,clock_year_day) S3method(get_nanosecond,clock_year_month_day) S3method(get_nanosecond,clock_year_month_weekday) S3method(get_nanosecond,clock_year_quarter_day) S3method(get_nanosecond,clock_year_week_day) S3method(get_nanosecond,default) S3method(get_quarter,clock_year_quarter_day) S3method(get_quarter,default) S3method(get_second,POSIXt) S3method(get_second,clock_iso_year_week_day) S3method(get_second,clock_year_day) S3method(get_second,clock_year_month_day) S3method(get_second,clock_year_month_weekday) S3method(get_second,clock_year_quarter_day) S3method(get_second,clock_year_week_day) S3method(get_second,default) S3method(get_week,clock_iso_year_week_day) S3method(get_week,clock_year_week_day) S3method(get_week,default) S3method(get_year,Date) S3method(get_year,POSIXt) S3method(get_year,clock_iso_year_week_day) S3method(get_year,clock_year_day) S3method(get_year,clock_year_month_day) S3method(get_year,clock_year_month_weekday) S3method(get_year,clock_year_quarter_day) S3method(get_year,clock_year_week_day) S3method(get_year,default) S3method(invalid_any,clock_iso_year_week_day) S3method(invalid_any,clock_year_day) S3method(invalid_any,clock_year_month_day) S3method(invalid_any,clock_year_month_weekday) S3method(invalid_any,clock_year_quarter_day) S3method(invalid_any,clock_year_week_day) S3method(invalid_any,default) S3method(invalid_count,clock_iso_year_week_day) S3method(invalid_count,clock_year_day) S3method(invalid_count,clock_year_month_day) S3method(invalid_count,clock_year_month_weekday) S3method(invalid_count,clock_year_quarter_day) S3method(invalid_count,clock_year_week_day) S3method(invalid_count,default) S3method(invalid_detect,clock_iso_year_week_day) S3method(invalid_detect,clock_year_day) S3method(invalid_detect,clock_year_month_day) S3method(invalid_detect,clock_year_month_weekday) S3method(invalid_detect,clock_year_quarter_day) S3method(invalid_detect,clock_year_week_day) S3method(invalid_detect,default) S3method(invalid_remove,clock_calendar) S3method(invalid_remove,default) S3method(invalid_resolve,clock_iso_year_week_day) S3method(invalid_resolve,clock_year_day) S3method(invalid_resolve,clock_year_month_day) S3method(invalid_resolve,clock_year_month_weekday) S3method(invalid_resolve,clock_year_quarter_day) S3method(invalid_resolve,clock_year_week_day) S3method(invalid_resolve,default) S3method(max,clock_rcrd) S3method(min,clock_rcrd) S3method(names,clock_rcrd) S3method(obj_print_data,clock_calendar) S3method(obj_print_data,clock_duration) S3method(obj_print_data,clock_time_point) S3method(obj_print_data,clock_zoned_time) S3method(obj_print_footer,clock_calendar) S3method(obj_print_footer,clock_time_point) S3method(obj_print_footer,clock_zoned_time) S3method(print,clock_calendar) S3method(print,clock_labels) S3method(print,clock_locale) S3method(print,clock_time_point) S3method(print,clock_zoned_time) S3method(range,clock_rcrd) S3method(seq,clock_duration) S3method(seq,clock_iso_year_week_day) S3method(seq,clock_time_point) S3method(seq,clock_year_day) S3method(seq,clock_year_month_day) S3method(seq,clock_year_month_weekday) S3method(seq,clock_year_quarter_day) S3method(seq,clock_year_week_day) S3method(set_day,Date) S3method(set_day,POSIXt) S3method(set_day,clock_iso_year_week_day) S3method(set_day,clock_year_day) S3method(set_day,clock_year_month_day) S3method(set_day,clock_year_month_weekday) S3method(set_day,clock_year_quarter_day) S3method(set_day,clock_year_week_day) S3method(set_day,default) S3method(set_hour,POSIXt) S3method(set_hour,clock_iso_year_week_day) S3method(set_hour,clock_year_day) S3method(set_hour,clock_year_month_day) S3method(set_hour,clock_year_month_weekday) S3method(set_hour,clock_year_quarter_day) S3method(set_hour,clock_year_week_day) S3method(set_hour,default) S3method(set_index,clock_year_month_weekday) S3method(set_index,default) S3method(set_microsecond,clock_iso_year_week_day) S3method(set_microsecond,clock_year_day) S3method(set_microsecond,clock_year_month_day) S3method(set_microsecond,clock_year_month_weekday) S3method(set_microsecond,clock_year_quarter_day) S3method(set_microsecond,clock_year_week_day) S3method(set_microsecond,default) S3method(set_millisecond,clock_iso_year_week_day) S3method(set_millisecond,clock_year_day) S3method(set_millisecond,clock_year_month_day) S3method(set_millisecond,clock_year_month_weekday) S3method(set_millisecond,clock_year_quarter_day) S3method(set_millisecond,clock_year_week_day) S3method(set_millisecond,default) S3method(set_minute,POSIXt) S3method(set_minute,clock_iso_year_week_day) S3method(set_minute,clock_year_day) S3method(set_minute,clock_year_month_day) S3method(set_minute,clock_year_month_weekday) S3method(set_minute,clock_year_quarter_day) S3method(set_minute,clock_year_week_day) S3method(set_minute,default) S3method(set_month,Date) S3method(set_month,POSIXt) S3method(set_month,clock_year_month_day) S3method(set_month,clock_year_month_weekday) S3method(set_month,default) S3method(set_nanosecond,clock_iso_year_week_day) S3method(set_nanosecond,clock_year_day) S3method(set_nanosecond,clock_year_month_day) S3method(set_nanosecond,clock_year_month_weekday) S3method(set_nanosecond,clock_year_quarter_day) S3method(set_nanosecond,clock_year_week_day) S3method(set_nanosecond,default) S3method(set_quarter,clock_year_quarter_day) S3method(set_quarter,default) S3method(set_second,POSIXt) S3method(set_second,clock_iso_year_week_day) S3method(set_second,clock_year_day) S3method(set_second,clock_year_month_day) S3method(set_second,clock_year_month_weekday) S3method(set_second,clock_year_quarter_day) S3method(set_second,clock_year_week_day) S3method(set_second,default) S3method(set_week,clock_iso_year_week_day) S3method(set_week,clock_year_week_day) S3method(set_week,default) S3method(set_year,Date) S3method(set_year,POSIXt) S3method(set_year,clock_iso_year_week_day) S3method(set_year,clock_year_day) S3method(set_year,clock_year_month_day) S3method(set_year,clock_year_month_weekday) S3method(set_year,clock_year_quarter_day) S3method(set_year,clock_year_week_day) S3method(set_year,default) S3method(vec_arith,clock_duration) S3method(vec_arith,clock_iso_year_week_day) S3method(vec_arith,clock_naive_time) S3method(vec_arith,clock_sys_time) S3method(vec_arith,clock_weekday) S3method(vec_arith,clock_year_day) S3method(vec_arith,clock_year_month_day) S3method(vec_arith,clock_year_month_weekday) S3method(vec_arith,clock_year_quarter_day) S3method(vec_arith,clock_year_week_day) S3method(vec_arith.Date,clock_duration) S3method(vec_arith.POSIXct,clock_duration) S3method(vec_arith.POSIXlt,clock_duration) S3method(vec_arith.clock_duration,Date) S3method(vec_arith.clock_duration,MISSING) S3method(vec_arith.clock_duration,POSIXct) S3method(vec_arith.clock_duration,POSIXlt) S3method(vec_arith.clock_duration,clock_duration) S3method(vec_arith.clock_duration,clock_iso_year_week_day) S3method(vec_arith.clock_duration,clock_naive_time) S3method(vec_arith.clock_duration,clock_sys_time) S3method(vec_arith.clock_duration,clock_weekday) S3method(vec_arith.clock_duration,clock_year_day) S3method(vec_arith.clock_duration,clock_year_month_day) S3method(vec_arith.clock_duration,clock_year_month_weekday) S3method(vec_arith.clock_duration,clock_year_quarter_day) S3method(vec_arith.clock_duration,clock_year_week_day) S3method(vec_arith.clock_duration,numeric) S3method(vec_arith.clock_iso_year_week_day,MISSING) S3method(vec_arith.clock_iso_year_week_day,clock_duration) S3method(vec_arith.clock_iso_year_week_day,clock_iso_year_week_day) S3method(vec_arith.clock_iso_year_week_day,numeric) S3method(vec_arith.clock_naive_time,MISSING) S3method(vec_arith.clock_naive_time,clock_duration) S3method(vec_arith.clock_naive_time,clock_naive_time) S3method(vec_arith.clock_naive_time,numeric) S3method(vec_arith.clock_sys_time,MISSING) S3method(vec_arith.clock_sys_time,clock_duration) S3method(vec_arith.clock_sys_time,clock_sys_time) S3method(vec_arith.clock_sys_time,numeric) S3method(vec_arith.clock_weekday,MISSING) S3method(vec_arith.clock_weekday,clock_duration) S3method(vec_arith.clock_weekday,clock_weekday) S3method(vec_arith.clock_weekday,numeric) S3method(vec_arith.clock_year_day,MISSING) S3method(vec_arith.clock_year_day,clock_duration) S3method(vec_arith.clock_year_day,clock_year_day) S3method(vec_arith.clock_year_day,numeric) S3method(vec_arith.clock_year_month_day,MISSING) S3method(vec_arith.clock_year_month_day,clock_duration) S3method(vec_arith.clock_year_month_day,clock_year_month_day) S3method(vec_arith.clock_year_month_day,numeric) S3method(vec_arith.clock_year_month_weekday,MISSING) S3method(vec_arith.clock_year_month_weekday,clock_duration) S3method(vec_arith.clock_year_month_weekday,clock_year_month_weekday) S3method(vec_arith.clock_year_month_weekday,numeric) S3method(vec_arith.clock_year_quarter_day,MISSING) S3method(vec_arith.clock_year_quarter_day,clock_duration) S3method(vec_arith.clock_year_quarter_day,clock_year_quarter_day) S3method(vec_arith.clock_year_quarter_day,numeric) S3method(vec_arith.clock_year_week_day,MISSING) S3method(vec_arith.clock_year_week_day,clock_duration) S3method(vec_arith.clock_year_week_day,clock_year_week_day) S3method(vec_arith.clock_year_week_day,numeric) S3method(vec_arith.numeric,clock_duration) S3method(vec_arith.numeric,clock_iso_year_week_day) S3method(vec_arith.numeric,clock_naive_time) S3method(vec_arith.numeric,clock_sys_time) S3method(vec_arith.numeric,clock_weekday) S3method(vec_arith.numeric,clock_year_day) S3method(vec_arith.numeric,clock_year_month_day) S3method(vec_arith.numeric,clock_year_month_weekday) S3method(vec_arith.numeric,clock_year_quarter_day) S3method(vec_arith.numeric,clock_year_week_day) S3method(vec_cast,clock_duration.clock_duration) S3method(vec_cast,clock_iso_year_week_day.clock_iso_year_week_day) S3method(vec_cast,clock_naive_time.clock_naive_time) S3method(vec_cast,clock_sys_time.clock_sys_time) S3method(vec_cast,clock_weekday.clock_weekday) S3method(vec_cast,clock_year_day.clock_year_day) S3method(vec_cast,clock_year_month_day.clock_year_month_day) S3method(vec_cast,clock_year_month_weekday.clock_year_month_weekday) S3method(vec_cast,clock_year_quarter_day.clock_year_quarter_day) S3method(vec_cast,clock_year_week_day.clock_year_week_day) S3method(vec_cast,clock_zoned_time.clock_zoned_time) S3method(vec_math,clock_duration) S3method(vec_math,clock_rcrd) S3method(vec_math,clock_weekday) S3method(vec_proxy,clock_duration) S3method(vec_proxy,clock_iso_year_week_day) S3method(vec_proxy,clock_time_point) S3method(vec_proxy,clock_year_day) S3method(vec_proxy,clock_year_month_day) S3method(vec_proxy,clock_year_month_weekday) S3method(vec_proxy,clock_year_quarter_day) S3method(vec_proxy,clock_year_week_day) S3method(vec_proxy,clock_zoned_time) S3method(vec_proxy_compare,clock_weekday) S3method(vec_proxy_compare,clock_year_month_weekday) S3method(vec_ptype,clock_iso_year_week_day) S3method(vec_ptype,clock_naive_time) S3method(vec_ptype,clock_sys_time) S3method(vec_ptype,clock_weekday) S3method(vec_ptype,clock_year_day) S3method(vec_ptype,clock_year_month_day) S3method(vec_ptype,clock_year_month_weekday) S3method(vec_ptype,clock_year_quarter_day) S3method(vec_ptype,clock_year_week_day) S3method(vec_ptype,clock_zoned_time) S3method(vec_ptype2,clock_duration.clock_duration) S3method(vec_ptype2,clock_iso_year_week_day.clock_iso_year_week_day) S3method(vec_ptype2,clock_naive_time.clock_naive_time) S3method(vec_ptype2,clock_sys_time.clock_sys_time) S3method(vec_ptype2,clock_weekday.clock_weekday) S3method(vec_ptype2,clock_year_day.clock_year_day) S3method(vec_ptype2,clock_year_month_day.clock_year_month_day) S3method(vec_ptype2,clock_year_month_weekday.clock_year_month_weekday) S3method(vec_ptype2,clock_year_quarter_day.clock_year_quarter_day) S3method(vec_ptype2,clock_year_week_day.clock_year_week_day) S3method(vec_ptype2,clock_zoned_time.clock_zoned_time) S3method(vec_ptype_abbr,clock_duration) S3method(vec_ptype_abbr,clock_iso_year_week_day) S3method(vec_ptype_abbr,clock_naive_time) S3method(vec_ptype_abbr,clock_sys_time) S3method(vec_ptype_abbr,clock_weekday) S3method(vec_ptype_abbr,clock_year_day) S3method(vec_ptype_abbr,clock_year_month_day) S3method(vec_ptype_abbr,clock_year_month_weekday) S3method(vec_ptype_abbr,clock_year_quarter_day) S3method(vec_ptype_abbr,clock_year_week_day) S3method(vec_ptype_abbr,clock_zoned_time) S3method(vec_ptype_full,clock_duration) S3method(vec_ptype_full,clock_iso_year_week_day) S3method(vec_ptype_full,clock_naive_time) S3method(vec_ptype_full,clock_sys_time) S3method(vec_ptype_full,clock_weekday) S3method(vec_ptype_full,clock_year_day) S3method(vec_ptype_full,clock_year_month_day) S3method(vec_ptype_full,clock_year_month_weekday) S3method(vec_ptype_full,clock_year_quarter_day) S3method(vec_ptype_full,clock_year_week_day) S3method(vec_ptype_full,clock_zoned_time) S3method(vec_restore,clock_duration) S3method(vec_restore,clock_iso_year_week_day) S3method(vec_restore,clock_time_point) S3method(vec_restore,clock_year_day) S3method(vec_restore,clock_year_month_day) S3method(vec_restore,clock_year_month_weekday) S3method(vec_restore,clock_year_quarter_day) S3method(vec_restore,clock_year_week_day) S3method(vec_restore,clock_zoned_time) export(add_days) export(add_hours) export(add_microseconds) export(add_milliseconds) export(add_minutes) export(add_months) export(add_nanoseconds) export(add_quarters) export(add_seconds) export(add_weeks) export(add_years) export(as_date) export(as_date_time) export(as_duration) export(as_iso_year_week_day) export(as_naive_time) export(as_sys_time) export(as_weekday) export(as_year_day) export(as_year_month_day) export(as_year_month_weekday) export(as_year_quarter_day) export(as_year_week_day) export(as_zoned_time) export(calendar_count_between) export(calendar_end) export(calendar_group) export(calendar_leap_year) export(calendar_month_factor) export(calendar_narrow) export(calendar_precision) export(calendar_spanning_seq) export(calendar_start) export(calendar_widen) export(clock_labels) export(clock_labels_languages) export(clock_labels_lookup) export(clock_locale) export(date_build) export(date_ceiling) export(date_count_between) export(date_end) export(date_floor) export(date_format) export(date_group) export(date_leap_year) export(date_month_factor) export(date_now) export(date_parse) export(date_round) export(date_seq) export(date_set_zone) export(date_shift) export(date_spanning_seq) export(date_start) export(date_time_build) export(date_time_info) export(date_time_parse) export(date_time_parse_RFC_3339) export(date_time_parse_abbrev) export(date_time_parse_complete) export(date_time_set_zone) export(date_time_zone) export(date_today) export(date_weekday_factor) export(date_zone) export(duration_cast) export(duration_ceiling) export(duration_days) export(duration_floor) export(duration_hours) export(duration_microseconds) export(duration_milliseconds) export(duration_minutes) export(duration_months) export(duration_nanoseconds) export(duration_precision) export(duration_quarters) export(duration_round) export(duration_seconds) export(duration_spanning_seq) export(duration_weeks) export(duration_years) export(get_day) export(get_hour) export(get_index) export(get_microsecond) export(get_millisecond) export(get_minute) export(get_month) export(get_nanosecond) export(get_quarter) export(get_second) export(get_week) export(get_year) export(invalid_any) export(invalid_count) export(invalid_detect) export(invalid_remove) export(invalid_resolve) export(is_duration) export(is_iso_year_week_day) export(is_naive_time) export(is_sys_time) export(is_weekday) export(is_year_day) export(is_year_month_day) export(is_year_month_weekday) export(is_year_quarter_day) export(is_year_week_day) export(is_zoned_time) export(iso_year_week_day) export(naive_time_info) export(naive_time_parse) export(set_day) export(set_hour) export(set_index) export(set_microsecond) export(set_millisecond) export(set_minute) export(set_month) export(set_nanosecond) export(set_quarter) export(set_second) export(set_week) export(set_year) export(sys_time_info) export(sys_time_now) export(sys_time_parse) export(sys_time_parse_RFC_3339) export(time_point_cast) export(time_point_ceiling) export(time_point_count_between) export(time_point_floor) export(time_point_precision) export(time_point_round) export(time_point_shift) export(time_point_spanning_seq) export(tzdb_names) export(tzdb_version) export(weekday) export(weekday_code) export(weekday_factor) export(year_day) export(year_month_day) export(year_month_day_parse) export(year_month_weekday) export(year_quarter_day) export(year_week_day) export(zoned_time_info) export(zoned_time_now) export(zoned_time_parse_abbrev) export(zoned_time_parse_complete) export(zoned_time_precision) export(zoned_time_set_zone) export(zoned_time_zone) import(rlang) import(vctrs) importFrom(tzdb,tzdb_names) importFrom(tzdb,tzdb_version) useDynLib(clock, .registration = TRUE) clock/LICENSE0000644000176200001440000000005314416016713012353 0ustar liggesusersYEAR: 2023 COPYRIGHT HOLDER: clock authors clock/README.md0000644000176200001440000001404514430424423012630 0ustar liggesusers # clock [![Codecov test coverage](https://codecov.io/gh/r-lib/clock/branch/main/graph/badge.svg)](https://app.codecov.io/gh/r-lib/clock?branch=main) [![R-CMD-check](https://github.com/r-lib/clock/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/r-lib/clock/actions/workflows/R-CMD-check.yaml) clock is an R package for working with date-times. It is packed with features, including utilities for: parsing, formatting, arithmetic, rounding, and extraction/updating of individual components. In addition to these tools for manipulating date-times, clock provides entirely new date-time types which are structured to reduce the agony of working with time zones as much as possible. At a high-level, clock: - Provides a new family of date-time classes (durations, time points, zoned-times, and calendars) that partition responsibilities so that you only have to think about time zones when you need them. - Implements a high level API for R’s native date (Date) and date-time (POSIXct) classes that lets you get productive quickly without having to learn the details of clock’s new date-time types. - Requires explicit handling of invalid dates (e.g. what date is one month after January 31st?) and nonexistent or ambiguous times (caused by daylight saving time issues). - Is built on the C++ [date](https://github.com/HowardHinnant/date) library, which provides a correct and high-performance backend. There are four key classes in clock, inspired by the design of the C++ date and chrono libraries. Some types are more efficient than others at particular operations, and it is only when all 4 are taken as a whole do you get a complete date time library. - A *duration* counts units of time, like “5 years” or “6 nanoseconds”. Bigger units are defined in terms of seconds, i.e. 1 day is 86400 seconds and 1 year is 365.2425 days. Durations are important because they form the backbone of clock; it’s relatively rare to use them directly. - A *time point* records an instant in time, like “1:24pm January 1st 2015”. It combines a *duration* with a “clock” that defines when to start counting and what exactly to count. There are two important types of time in clock: sys-time and naive-time. They’re equivalent until you start working with zoned-times. - A *zoned-time* is a time point paired with a time zone. You can create them from either a sys-time or a naive-time, depending on whether you want to convert the time point from UTC (leaving the underlying duration unchanged, but changing the printed time), or declare that the time point is in a specific time zone (leaving the printed time unchanged, but changing the underlying duration). Zoned-times are primarily needed for communication with humans. - A *calendar* represents a date using a combination of fields like year-month-day, year-month-weekday, year-quarter-day, year-day, or iso-year-week-day, along with hour/minute/second fields to represent time within a day (so they’re similar to R’s POSIXlt). Calendar objects are extremely efficient at arithmetic involving irregular periods such as months, quarters, and years and at getting and setting specified components. A calendar can represent invalid dates (like 2020-02-31) which only need to be resolved when converting back to a time point. ## Installation Install the released version from [CRAN](https://CRAN.R-project.org) with: ``` r install.packages("clock") ``` Install the development version from [GitHub](https://github.com/) with: ``` r # install.packages("pak") pak::pak("r-lib/clock") ``` ## Learning With clock, there is a high-level API for manipulating R’s native date and date-time types (Date, POSIXct, and POSIXlt), as well as a low-level API for more advanced date-time manipulations. The high-level API should allow you to quickly get productive with clock without having to learn many of the new types. An easy way to get an overview of this is by looking at the [High Level API](https://clock.r-lib.org/reference/index.html#section-high-level-api) section of the pkgdown reference page. You’ll also want to take a look at clock’s vignettes: - [Getting Started](https://clock.r-lib.org/articles/clock.html) - [Motivations for clock](https://clock.r-lib.org/articles/articles/motivations.html) - [Examples and Recipes](https://clock.r-lib.org/articles/recipes.html) - [Frequently Asked Questions](https://clock.r-lib.org/articles/faq.html) You can also watch this [5 minute lightning talk](https://www.rstudio.com/conference/2022/talks/its-about-time/) about clock:

## Acknowledgments The ideas used to build clock have come from a number of places: - First and foremost, clock depends on and is inspired by the [date](https://github.com/HowardHinnant/date) library by Howard Hinnant, a variant of which has been voted in to C++20. - The R “record” types that clock is built on come from [vctrs](https://github.com/r-lib/vctrs). - The [nanotime](https://github.com/eddelbuettel/nanotime) package was the first to implement a nanosecond resolution time type for R. - The [zoo](https://CRAN.R-project.org/package=zoo) package was the first to implement year-month and year-quarter types, and has functioned as a very successful time series infrastructure package for many years. Additionally, I’d like to thank my wife for being a power user of clock, and having a number of discussions with me around bugs and missing features. You can thank her for `date_count_between()`. clock/data/0000755000176200001440000000000014011271365012256 5ustar liggesusersclock/data/clock_weekdays.rda0000644000176200001440000000022114011271365015730 0ustar liggesusersBZh91AY&SYy}π@o MF4 2mACMij暀d]C'J h,YRXUVWZݳs_\IUEX+d#v)„Hclock/data/clock_iso_weekdays.rda0000644000176200001440000000022714011271365016610 0ustar liggesusersBZh91AY&SYcƎ}@o )=M6<[9i&@}X,Vj@C8k,Q@|~>vZ!Edlū>qY}HKq.1OH  qclock/data/clock_months.rda0000644000176200001440000000031214011271365015425 0ustar liggesusersBZh91AY&SYEπ@ 0)QIѣ'4OLIS4hb0@2 GC۶  BJ T PNd&&Bd 5h'uW" b, NULL)}. If \code{NULL}, defaults to \code{"error"}. If \code{getOption("clock.strict")} is \code{TRUE}, \code{ambiguous} must be supplied and cannot be \code{NULL}. Additionally, \code{ambiguous} cannot be specified as a zoned_time on its own, as this implies \code{NULL} for ambiguous times that the zoned_time cannot resolve. Instead, it must be specified as a list alongside an ambiguous time resolution strategy as described above. This is a convenient way to make production code robust to ambiguous times.} } \value{ \code{x} rounded to the specified \code{precision}. } \description{ These are POSIXct/POSIXlt methods for the \link[=date-and-date-time-rounding]{rounding generics}. \itemize{ \item \code{date_floor()} rounds a date-time down to a multiple of the specified \code{precision}. \item \code{date_ceiling()} rounds a date-time up to a multiple of the specified \code{precision}. \item \code{date_round()} rounds up or down depending on what is closer, rounding up on ties. } You can group by irregular periods such as \code{"month"} or \code{"year"} by using \code{\link[=date_group]{date_group()}}. } \details{ When rounding by \code{"week"}, remember that the \code{origin} determines the "week start". By default, 1970-01-01 is the implicit origin, which is a Thursday. If you would like to round by weeks with a different week start, just supply an origin on the weekday you are interested in. } \examples{ x <- as.POSIXct("2019-03-31", "America/New_York") x <- add_days(x, 0:5) # Flooring by 2 days, note that this is not tied to the current month, # and instead counts from the specified `origin`, so groups can cross # the month boundary date_floor(x, "day", n = 2) # Compare to `date_group()`, which groups by the day of the month date_group(x, "day", n = 2) # Note that daylight saving time gaps can throw off rounding x <- as.POSIXct("1970-04-26 01:59:59", "America/New_York") + c(0, 1) x # Rounding is done in naive-time, which means that rounding by 2 hours # will attempt to generate a time of 1970-04-26 02:00:00, which doesn't # exist in this time zone try(date_floor(x, "hour", n = 2)) # You can handle this by specifying a nonexistent time resolution strategy date_floor(x, "hour", n = 2, nonexistent = "roll-forward") } clock/man/posixt-arithmetic.Rd0000644000176200001440000002634314426717162016106 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/posixt.R \name{posixt-arithmetic} \alias{posixt-arithmetic} \alias{add_years.POSIXt} \alias{add_quarters.POSIXt} \alias{add_months.POSIXt} \alias{add_weeks.POSIXt} \alias{add_days.POSIXt} \alias{add_hours.POSIXt} \alias{add_minutes.POSIXt} \alias{add_seconds.POSIXt} \title{Arithmetic: date-time} \usage{ \method{add_years}{POSIXt}(x, n, ..., invalid = NULL, nonexistent = NULL, ambiguous = x) \method{add_quarters}{POSIXt}(x, n, ..., invalid = NULL, nonexistent = NULL, ambiguous = x) \method{add_months}{POSIXt}(x, n, ..., invalid = NULL, nonexistent = NULL, ambiguous = x) \method{add_weeks}{POSIXt}(x, n, ..., nonexistent = NULL, ambiguous = x) \method{add_days}{POSIXt}(x, n, ..., nonexistent = NULL, ambiguous = x) \method{add_hours}{POSIXt}(x, n, ...) \method{add_minutes}{POSIXt}(x, n, ...) \method{add_seconds}{POSIXt}(x, n, ...) } \arguments{ \item{x}{\verb{[POSIXct / POSIXlt]} A date-time vector.} \item{n}{\verb{[integer / clock_duration]} An integer vector to be converted to a duration, or a duration corresponding to the arithmetic function being used. This corresponds to the number of duration units to add. \code{n} may be negative to subtract units of duration.} \item{...}{These dots are for future extensions and must be empty.} \item{invalid}{\verb{[character(1) / NULL]} One of the following invalid date resolution strategies: \itemize{ \item \code{"previous"}: The previous valid instant in time. \item \code{"previous-day"}: The previous valid day in time, keeping the time of day. \item \code{"next"}: The next valid instant in time. \item \code{"next-day"}: The next valid day in time, keeping the time of day. \item \code{"overflow"}: Overflow by the number of days that the input is invalid by. Time of day is dropped. \item \code{"overflow-day"}: Overflow by the number of days that the input is invalid by. Time of day is kept. \item \code{"NA"}: Replace invalid dates with \code{NA}. \item \code{"error"}: Error on invalid dates. } Using either \code{"previous"} or \code{"next"} is generally recommended, as these two strategies maintain the \emph{relative ordering} between elements of the input. If \code{NULL}, defaults to \code{"error"}. If \code{getOption("clock.strict")} is \code{TRUE}, \code{invalid} must be supplied and cannot be \code{NULL}. This is a convenient way to make production code robust to invalid dates.} \item{nonexistent}{\verb{[character / NULL]} One of the following nonexistent time resolution strategies, allowed to be either length 1, or the same length as the input: \itemize{ \item \code{"roll-forward"}: The next valid instant in time. \item \code{"roll-backward"}: The previous valid instant in time. \item \code{"shift-forward"}: Shift the nonexistent time forward by the size of the daylight saving time gap. \item \verb{"shift-backward}: Shift the nonexistent time backward by the size of the daylight saving time gap. \item \code{"NA"}: Replace nonexistent times with \code{NA}. \item \code{"error"}: Error on nonexistent times. } Using either \code{"roll-forward"} or \code{"roll-backward"} is generally recommended over shifting, as these two strategies maintain the \emph{relative ordering} between elements of the input. If \code{NULL}, defaults to \code{"error"}. If \code{getOption("clock.strict")} is \code{TRUE}, \code{nonexistent} must be supplied and cannot be \code{NULL}. This is a convenient way to make production code robust to nonexistent times.} \item{ambiguous}{\verb{[character / zoned_time / POSIXct / list(2) / NULL]} One of the following ambiguous time resolution strategies, allowed to be either length 1, or the same length as the input: \itemize{ \item \code{"earliest"}: Of the two possible times, choose the earliest one. \item \code{"latest"}: Of the two possible times, choose the latest one. \item \code{"NA"}: Replace ambiguous times with \code{NA}. \item \code{"error"}: Error on ambiguous times. } Alternatively, \code{ambiguous} is allowed to be a zoned_time (or POSIXct) that is either length 1, or the same length as the input. If an ambiguous time is encountered, the zoned_time is consulted. If the zoned_time corresponds to a naive_time that is also ambiguous \emph{and} uses the same daylight saving time transition point as the original ambiguous time, then the offset of the zoned_time is used to resolve the ambiguity. If the ambiguity cannot be resolved by consulting the zoned_time, then this method falls back to \code{NULL}. Finally, \code{ambiguous} is allowed to be a list of size 2, where the first element of the list is a zoned_time (as described above), and the second element of the list is an ambiguous time resolution strategy to use when the ambiguous time cannot be resolved by consulting the zoned_time. Specifying a zoned_time on its own is identical to \verb{list(, NULL)}. If \code{NULL}, defaults to \code{"error"}. If \code{getOption("clock.strict")} is \code{TRUE}, \code{ambiguous} must be supplied and cannot be \code{NULL}. Additionally, \code{ambiguous} cannot be specified as a zoned_time on its own, as this implies \code{NULL} for ambiguous times that the zoned_time cannot resolve. Instead, it must be specified as a list alongside an ambiguous time resolution strategy as described above. This is a convenient way to make production code robust to ambiguous times.} } \value{ \code{x} after performing the arithmetic. } \description{ These are POSIXct/POSIXlt methods for the \link[=clock-arithmetic]{arithmetic generics}. Calendrical based arithmetic: These functions convert to a naive-time, then to a year-month-day, perform the arithmetic, then convert back to a date-time. \itemize{ \item \code{add_years()} \item \code{add_quarters()} \item \code{add_months()} } Naive-time based arithmetic: These functions convert to a naive-time, perform the arithmetic, then convert back to a date-time. \itemize{ \item \code{add_weeks()} \item \code{add_days()} } Sys-time based arithmetic: These functions convert to a sys-time, perform the arithmetic, then convert back to a date-time. \itemize{ \item \code{add_hours()} \item \code{add_minutes()} \item \code{add_seconds()} } } \details{ Adding a single quarter with \code{add_quarters()} is equivalent to adding 3 months. \code{x} and \code{n} are recycled against each other using \link[vctrs:vector_recycling_rules]{tidyverse recycling rules}. Calendrical based arithmetic has the potential to generate invalid dates (like the 31st of February), nonexistent times (due to daylight saving time gaps), and ambiguous times (due to daylight saving time fallbacks). Naive-time based arithmetic will never generate an invalid date, but may generate a nonexistent or ambiguous time (i.e. you added 1 day and landed in a daylight saving time gap). Sys-time based arithmetic operates in the UTC time zone, which means that it will never generate any invalid dates or nonexistent / ambiguous times. The conversion from POSIXct/POSIXlt to the corresponding clock type uses a "best guess" about whether you want to do the arithmetic using a naive-time or a sys-time. For example, when adding months, you probably want to retain the printed time when converting to a year-month-day to perform the arithmetic, so the conversion goes through naive-time. However, when adding smaller units like seconds, you probably want \verb{"2020-03-08 01:59:59" + 1 second} in the America/New_York time zone to return \code{"2020-03-08 03:00:00"}, taking into account the fact that there was a daylight saving time gap. This requires doing the arithmetic in sys-time, so that is what clock converts to. If you disagree with this heuristic for any reason, you can take control and perform the conversions yourself. For example, you could convert the previous example to a naive-time instead of a sys-time manually with \code{\link[=as_naive_time]{as_naive_time()}}, add 1 second giving \code{"2020-03-08 02:00:00"}, then convert back to a POSIXct/POSIXlt, dealing with the nonexistent time that gets created by using the \code{nonexistent} argument of \code{as.POSIXct()}. } \section{Relative ordering}{ For the most part, adding time based units to date-times will retain the relative ordering of the input. For example, if \code{x[1] < x[2]} before the \verb{add_*()} call, then it is generally also true of the result. Using \code{invalid = "previous" / "next"} and \code{nonexistent = "roll-forward" / "roll-backward"} ensures that this holds when invalid and nonexistent issues are encountered. That said, with date-times there is an edge case related to ambiguous times where the relative ordering could change. Consider these three date-times: \if{html}{\out{
}}\preformatted{x <- c( date_time_build(2012, 4, 1, 2, 30, zone = "Australia/Melbourne", ambiguous = "earliest"), date_time_build(2012, 4, 1, 2, 00, zone = "Australia/Melbourne", ambiguous = "latest"), date_time_build(2012, 4, 1, 2, 30, zone = "Australia/Melbourne", ambiguous = "latest") ) x #> [1] "2012-04-01 02:30:00 AEDT" "2012-04-01 02:00:00 AEST" #> [3] "2012-04-01 02:30:00 AEST" }\if{html}{\out{
}} In this case, there was a daylight saving time fallback on \code{2012-04-01} where the clocks went from \verb{02:59:59 AEDT -> 02:00:00 AEST}. So the times above are precisely 30 minutes apart, and they are in increasing order. If we add sys-time based units like hours, minutes, or seconds, then the relative ordering of these date-times will be preserved. However, arithmetic that goes through naive-time, like adding days or months, won't preserve the ordering here: \if{html}{\out{
}}\preformatted{add_days(x, 1) #> [1] "2012-04-02 02:30:00 AEST" "2012-04-02 02:00:00 AEST" #> [3] "2012-04-02 02:30:00 AEST" add_months(x, 1) #> [1] "2012-05-01 02:30:00 AEST" "2012-05-01 02:00:00 AEST" #> [3] "2012-05-01 02:30:00 AEST" }\if{html}{\out{
}} Note that the 1st and 3rd values of the result are the same, and the 1st value is no longer before the 2nd value. Adding larger units of time in naive-time generally does make more sense than adding it in sys-time, but it does come with this one edge case to be aware of when working with date-times (this does not affect dates). If this has the potential to be an issue, consider only adding sys-time based units (hours, minutes, and seconds) which can't have these issues. } \examples{ x <- as.POSIXct("2019-01-01", tz = "America/New_York") add_years(x, 1:5) y <- as.POSIXct("2019-01-31 00:30:00", tz = "America/New_York") # Adding 1 month to `y` generates an invalid date. Unlike year-month-day # types, R's native date-time types cannot handle invalid dates, so you must # resolve them immediately. If you don't you get an error: try(add_months(y, 1:2)) add_months(as_year_month_day(y), 1:2) # Resolve invalid dates by specifying an invalid date resolution strategy # with the `invalid` argument. Using `"previous"` here sets the date-time to # the previous valid moment in time - i.e. the end of the month. The # time is set to the last moment in the day to retain the relative ordering # within your input. If you are okay with potentially losing this, and # want to retain your time of day, you can use `"previous-day"` to set the # date-time to the previous valid day, while keeping the time of day. add_months(y, 1:2, invalid = "previous") add_months(y, 1:2, invalid = "previous-day") } clock/man/date_month_factor.Rd0000644000176200001440000000252414073622034016073 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/date.R \name{date_month_factor} \alias{date_month_factor} \title{Convert a date or date-time to an ordered factor of month names} \usage{ date_month_factor(x, ..., labels = "en", abbreviate = FALSE) } \arguments{ \item{x}{\verb{[Date / POSIXct / POSIXlt]} A date or date-time vector.} \item{...}{These dots are for future extensions and must be empty.} \item{labels}{\verb{[clock_labels / character(1)]} Character representations of localized weekday names, month names, and AM/PM names. Either the language code as string (passed on to \code{\link[=clock_labels_lookup]{clock_labels_lookup()}}), or an object created by \code{\link[=clock_labels]{clock_labels()}}.} \item{abbreviate}{\verb{[logical(1)]} If \code{TRUE}, the abbreviated month names from \code{labels} will be used. If \code{FALSE}, the full month names from \code{labels} will be used.} } \value{ An ordered factor representing the months. } \description{ \code{date_month_factor()} extracts the month values from a date or date-time and converts them to an ordered factor of month names. This can be useful in combination with ggplot2, or for modeling. } \examples{ x <- add_months(as.Date("2019-01-01"), 0:11) date_month_factor(x) date_month_factor(x, abbreviate = TRUE) date_month_factor(x, labels = "fr") } clock/man/clock-codes.Rd0000644000176200001440000000337414422221153014600 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/clock-codes.R \docType{data} \name{clock-codes} \alias{clock-codes} \alias{clock_months} \alias{clock_weekdays} \alias{clock_iso_weekdays} \title{Integer codes} \format{ \itemize{ \item \code{clock_months}: An environment containing month codes. } \itemize{ \item \code{clock_weekdays}: An environment containing weekday codes. } \itemize{ \item \code{clock_iso_weekdays}: An environment containing ISO weekday codes. } } \usage{ clock_months clock_weekdays clock_iso_weekdays } \description{ Objects with useful mappings from month names and weekday names to integer codes. \subsection{Month codes (\code{clock_months})}{ \itemize{ \item \code{january == 1} \item \code{february == 2} \item \code{march == 3} \item \code{april == 4} \item \code{may == 5} \item \code{june == 6} \item \code{july == 7} \item \code{august == 8} \item \code{september == 9} \item \code{october == 10} \item \code{november == 11} \item \code{december == 12} } } \subsection{Weekday codes (\code{clock_weekdays})}{ \itemize{ \item \code{sunday == 1} \item \code{monday == 2} \item \code{tuesday == 3} \item \code{wednesday == 4} \item \code{thursday == 5} \item \code{friday == 6} \item \code{saturday == 7} } } \subsection{ISO weekday codes (\code{clock_iso_weekdays})}{ \itemize{ \item \code{monday == 1} \item \code{tuesday == 2} \item \code{wednesday == 3} \item \code{thursday == 4} \item \code{friday == 5} \item \code{saturday == 6} \item \code{sunday == 7} } } } \examples{ weekday(clock_weekdays$wednesday) year_month_weekday(2019, clock_months$april, clock_weekdays$monday, 1:4) year_week_day(2020, 52, start = clock_weekdays$monday) iso_year_week_day(2020, 52, clock_iso_weekdays$thursday) } \keyword{datasets} clock/man/year-day-boundary.Rd0000644000176200001440000000220014075602251015737 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gregorian-year-day.R \name{year-day-boundary} \alias{year-day-boundary} \alias{calendar_start.clock_year_day} \alias{calendar_end.clock_year_day} \title{Boundaries: year-day} \usage{ \method{calendar_start}{clock_year_day}(x, precision) \method{calendar_end}{clock_year_day}(x, precision) } \arguments{ \item{x}{\verb{[clock_year_day]} A year-day vector.} \item{precision}{\verb{[character(1)]} One of: \itemize{ \item \code{"year"} \item \code{"day"} \item \code{"hour"} \item \code{"minute"} \item \code{"second"} \item \code{"millisecond"} \item \code{"microsecond"} \item \code{"nanosecond"} }} } \value{ \code{x} at the same precision, but with some components altered to be at the boundary value. } \description{ This is a year-day method for the \code{\link[=calendar_start]{calendar_start()}} and \code{\link[=calendar_end]{calendar_end()}} generics. They adjust components of a calendar to the start or end of a specified \code{precision}. } \examples{ # Day precision x <- year_day(2019:2020, 5) x # Compute the last day of the year calendar_end(x, "year") } clock/man/year-week-day-getters.Rd0000644000176200001440000000356214422221153016530 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/week-year-week-day.R \name{year-week-day-getters} \alias{year-week-day-getters} \alias{get_year.clock_year_week_day} \alias{get_week.clock_year_week_day} \alias{get_day.clock_year_week_day} \alias{get_hour.clock_year_week_day} \alias{get_minute.clock_year_week_day} \alias{get_second.clock_year_week_day} \alias{get_millisecond.clock_year_week_day} \alias{get_microsecond.clock_year_week_day} \alias{get_nanosecond.clock_year_week_day} \title{Getters: year-week-day} \usage{ \method{get_year}{clock_year_week_day}(x) \method{get_week}{clock_year_week_day}(x) \method{get_day}{clock_year_week_day}(x) \method{get_hour}{clock_year_week_day}(x) \method{get_minute}{clock_year_week_day}(x) \method{get_second}{clock_year_week_day}(x) \method{get_millisecond}{clock_year_week_day}(x) \method{get_microsecond}{clock_year_week_day}(x) \method{get_nanosecond}{clock_year_week_day}(x) } \arguments{ \item{x}{\verb{[clock_year_week_day]} A year-week-day to get the component from.} } \value{ The component. } \description{ These are year-week-day methods for the \link[=clock-getters]{getter generics}. \itemize{ \item \code{get_year()} returns the year. Note that this can differ from the Gregorian year. \item \code{get_week()} returns the week of the current year. \item \code{get_day()} returns a value between 1-7 indicating the weekday of the current week, where \verb{1 = start of week} and \verb{7 = end of week}, in line with the chosen \code{start}. \item There are sub-daily getters for extracting more precise components. } } \examples{ x <- year_week_day(2019, 50:52, 1:3) x # Get the week get_week(x) # Gets the weekday get_day(x) # Note that the year can differ from the Gregorian year iso <- year_week_day(2019, 1, 1, start = clock_weekdays$monday) ymd <- as_year_month_day(iso) get_year(iso) get_year(ymd) } clock/man/year-week-day-narrow.Rd0000644000176200001440000000175714422221153016367 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/week-year-week-day.R \name{year-week-day-narrow} \alias{year-week-day-narrow} \alias{calendar_narrow.clock_year_week_day} \title{Narrow: year-week-day} \usage{ \method{calendar_narrow}{clock_year_week_day}(x, precision) } \arguments{ \item{x}{\verb{[clock_year_week_day]} A year-week-day vector.} \item{precision}{\verb{[character(1)]} One of: \itemize{ \item \code{"year"} \item \code{"week"} \item \code{"day"} \item \code{"hour"} \item \code{"minute"} \item \code{"second"} \item \code{"millisecond"} \item \code{"microsecond"} \item \code{"nanosecond"} }} } \value{ \code{x} narrowed to the supplied \code{precision}. } \description{ This is a year-week-day method for the \code{\link[=calendar_narrow]{calendar_narrow()}} generic. It narrows a year-week-day vector to the specified \code{precision}. } \examples{ # Day precision x <- year_week_day(2019, 1, 5) x # Narrowed to week precision calendar_narrow(x, "week") } clock/man/clock-getters.Rd0000644000176200001440000000343514422221153015156 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/getters.R \name{clock-getters} \alias{clock-getters} \alias{get_year} \alias{get_quarter} \alias{get_month} \alias{get_week} \alias{get_day} \alias{get_hour} \alias{get_minute} \alias{get_second} \alias{get_millisecond} \alias{get_microsecond} \alias{get_nanosecond} \alias{get_index} \title{Calendar getters} \usage{ get_year(x) get_quarter(x) get_month(x) get_week(x) get_day(x) get_hour(x) get_minute(x) get_second(x) get_millisecond(x) get_microsecond(x) get_nanosecond(x) get_index(x) } \arguments{ \item{x}{\verb{[object]} An object to get the component from.} } \value{ The component. } \description{ This family of functions extract fields from a calendar vector. Each calendar has its own set of supported getters, which are documented on their own help page: \itemize{ \item \link[=year-month-day-getters]{year-month-day} \item \link[=year-month-weekday-getters]{year-month-weekday} \item \link[=year-week-day-getters]{year-week-day} \item \link[=iso-year-week-day-getters]{iso-year-week-day} \item \link[=year-quarter-day-getters]{year-quarter-day} \item \link[=year-day-getters]{year-day} } There are also convenience methods for extracting certain components directly from R's native date and date-time types. \itemize{ \item \link[=Date-getters]{dates (Date)} \item \link[=posixt-getters]{date-times (POSIXct / POSIXlt)} } } \details{ You cannot extract components directly from a time point type, such as sys-time or naive-time. Convert it to a calendar type first. Similarly, a zoned-time must be converted to either a sys-time or naive-time, and then to a calendar type, to be able to extract components from it. } \examples{ x <- year_month_day(2019, 1:3, 5:7, 1, 20, 30) get_month(x) get_day(x) get_second(x) } clock/man/year-week-day-group.Rd0000644000176200001440000000246414422221153016207 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/week-year-week-day.R \name{year-week-day-group} \alias{year-week-day-group} \alias{calendar_group.clock_year_week_day} \title{Grouping: year-week-day} \usage{ \method{calendar_group}{clock_year_week_day}(x, precision, ..., n = 1L) } \arguments{ \item{x}{\verb{[clock_year_week_day]} A year-week-day vector.} \item{precision}{\verb{[character(1)]} One of: \itemize{ \item \code{"year"} \item \code{"week"} \item \code{"day"} \item \code{"hour"} \item \code{"minute"} \item \code{"second"} \item \code{"millisecond"} \item \code{"microsecond"} \item \code{"nanosecond"} }} \item{...}{These dots are for future extensions and must be empty.} \item{n}{\verb{[positive integer(1)]} A single positive integer specifying a multiple of \code{precision} to use.} } \value{ \code{x} grouped at the specified \code{precision}. } \description{ This is a year-week-day method for the \code{\link[=calendar_group]{calendar_group()}} generic. Grouping for a year-week-day object can be done at any precision, as long as \code{x} is at least as precise as \code{precision}. } \examples{ x <- year_week_day(2019, 1:52) # Group by 3 weeks calendar_group(x, "week", n = 3) y <- year_week_day(2000:2020, 1, 1) # Group by 2 years calendar_group(y, "year", n = 2) } clock/man/sys_time_info.Rd0000644000176200001440000000535414265310513015265 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/sys-time.R \name{sys_time_info} \alias{sys_time_info} \title{Info: sys-time} \usage{ sys_time_info(x, zone) } \arguments{ \item{x}{\verb{[clock_sys_time]} A sys-time.} \item{zone}{\verb{[character]} A valid time zone name. Unlike most functions in clock, in \code{sys_time_info()} \code{zone} is vectorized and is recycled against \code{x}.} } \value{ A data frame of low level information. } \description{ \code{sys_time_info()} retrieves a set of low-level information generally not required for most date-time manipulations. It returns a data frame with the following columns: \itemize{ \item \code{begin}, \code{end}: Second precision sys-times specifying the range of the current daylight saving time rule. The range is a half-open interval of \verb{[begin, end)}. \item \code{offset}: A second precision \code{duration} specifying the offset from UTC. \item \code{dst}: A logical vector specifying if daylight saving time is currently active. \item \code{abbreviation}: The time zone abbreviation in use throughout this \code{begin} to \code{end} range. } } \details{ If there have never been any daylight saving time transitions, the minimum supported year value is returned for \code{begin} (typically, a year value of \code{-32767}). If daylight saving time is no longer used in a time zone, the maximum supported year value is returned for \code{end} (typically, a year value of \code{32767}). The \code{offset} is the bridge between sys-time and naive-time for the \code{zone} being used. The relationship of the three values is: \if{html}{\out{
}}\preformatted{offset = naive_time - sys_time }\if{html}{\out{
}} } \examples{ library(vctrs) x <- year_month_day(2021, 03, 14, c(01, 03), c(59, 00), c(59, 00)) x <- as_naive_time(x) x <- as_zoned_time(x, "America/New_York") # x[1] is in EST, x[2] is in EDT x x_sys <- as_sys_time(x) info <- sys_time_info(x_sys, zoned_time_zone(x)) info # Convert `begin` and `end` to zoned-times to see the previous and # next daylight saving time transitions data_frame( x = x, begin = as_zoned_time(info$begin, zoned_time_zone(x)), end = as_zoned_time(info$end, zoned_time_zone(x)) ) # `end` can be used to iterate through daylight saving time transitions # by repeatedly calling `sys_time_info()` sys_time_info(info$end, zoned_time_zone(x)) # Multiple `zone`s can be supplied to look up daylight saving time # information in different time zones zones <- c("America/New_York", "America/Los_Angeles") info2 <- sys_time_info(x_sys[1], zones) info2 # The offset can be used to display the naive-time (i.e. the printed time) # in both of those time zones data_frame( zone = zones, naive_time = x_sys[1] + info2$offset ) } clock/man/year-week-day-boundary.Rd0000644000176200001440000000251514422221153016673 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/week-year-week-day.R \name{year-week-day-boundary} \alias{year-week-day-boundary} \alias{calendar_start.clock_year_week_day} \alias{calendar_end.clock_year_week_day} \title{Boundaries: year-week-day} \usage{ \method{calendar_start}{clock_year_week_day}(x, precision) \method{calendar_end}{clock_year_week_day}(x, precision) } \arguments{ \item{x}{\verb{[clock_year_week_day]} A year-week-day vector.} \item{precision}{\verb{[character(1)]} One of: \itemize{ \item \code{"year"} \item \code{"week"} \item \code{"day"} \item \code{"hour"} \item \code{"minute"} \item \code{"second"} \item \code{"millisecond"} \item \code{"microsecond"} \item \code{"nanosecond"} }} } \value{ \code{x} at the same precision, but with some components altered to be at the boundary value. } \description{ This is an year-week-day method for the \code{\link[=calendar_start]{calendar_start()}} and \code{\link[=calendar_end]{calendar_end()}} generics. They adjust components of a calendar to the start or end of a specified \code{precision}. } \examples{ x <- year_week_day(2019:2020, 5, 6, 10) x # Compute the last moment of the last week of the year calendar_end(x, "year") # Compare that to just setting the week to `"last"`, # which doesn't affect the other components set_week(x, "last") } clock/man/weekday_factor.Rd0000644000176200001440000000315614011253136015377 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/weekday.R \name{weekday_factor} \alias{weekday_factor} \title{Convert a weekday to an ordered factor} \usage{ weekday_factor(x, ..., labels = "en", abbreviate = TRUE, encoding = "western") } \arguments{ \item{x}{\verb{[weekday]} A weekday vector.} \item{...}{These dots are for future extensions and must be empty.} \item{labels}{\verb{[clock_labels / character(1)]} Character representations of localized weekday names, month names, and AM/PM names. Either the language code as string (passed on to \code{\link[=clock_labels_lookup]{clock_labels_lookup()}}), or an object created by \code{\link[=clock_labels]{clock_labels()}}.} \item{abbreviate}{\verb{[logical(1)]} If \code{TRUE}, the abbreviated weekday names from \code{labels} will be used. If \code{FALSE}, the full weekday names from \code{labels} will be used.} \item{encoding}{\verb{[character(1)]} One of: \itemize{ \item \code{"western"}: Encode the weekdays as an ordered factor with levels from Sunday -> Saturday. \item \code{"iso"}: Encode the weekdays as an ordered factor with levels from Monday -> Sunday. }} } \value{ An ordered factor representing the weekdays. } \description{ \code{weekday_factor()} converts a weekday object to an ordered factor. This can be useful in combination with ggplot2, or for modeling. } \examples{ x <- weekday(1:7) # Default to Sunday -> Saturday weekday_factor(x) # ISO encoding is Monday -> Sunday weekday_factor(x, encoding = "iso") # With full names weekday_factor(x, abbreviate = FALSE) # Or a different language weekday_factor(x, labels = "fr") } clock/man/as_weekday.Rd0000644000176200001440000000147414427270231014533 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/weekday.R \name{as_weekday} \alias{as_weekday} \title{Convert to a weekday} \usage{ as_weekday(x, ...) } \arguments{ \item{x}{\verb{[object]} An object to convert to a weekday. Usually a sys-time or naive-time.} \item{...}{These dots are for future extensions and must be empty.} } \value{ A weekday. } \description{ \code{as_weekday()} converts to a weekday type. This is normally useful for converting to a weekday from a sys-time or naive-time. You can use this function along with the \emph{circular arithmetic} that weekday implements to easily get to the "next Monday" or "previous Sunday". } \examples{ x <- as_naive_time(year_month_day(2019, 01, 05)) # This is a Saturday! as_weekday(x) # See the examples in `?weekday` for more usage. } clock/man/date-boundary.Rd0000644000176200001440000000406214075602251015151 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/date.R \name{date-boundary} \alias{date-boundary} \alias{date_start.Date} \alias{date_end.Date} \title{Boundaries: date} \usage{ \method{date_start}{Date}(x, precision, ..., invalid = NULL) \method{date_end}{Date}(x, precision, ..., invalid = NULL) } \arguments{ \item{x}{\verb{[Date]} A date vector.} \item{precision}{\verb{[character(1)]} One of: \itemize{ \item \code{"year"} \item \code{"month"} \item \code{"day"} }} \item{...}{These dots are for future extensions and must be empty.} \item{invalid}{\verb{[character(1) / NULL]} One of the following invalid date resolution strategies: \itemize{ \item \code{"previous"}: The previous valid instant in time. \item \code{"previous-day"}: The previous valid day in time, keeping the time of day. \item \code{"next"}: The next valid instant in time. \item \code{"next-day"}: The next valid day in time, keeping the time of day. \item \code{"overflow"}: Overflow by the number of days that the input is invalid by. Time of day is dropped. \item \code{"overflow-day"}: Overflow by the number of days that the input is invalid by. Time of day is kept. \item \code{"NA"}: Replace invalid dates with \code{NA}. \item \code{"error"}: Error on invalid dates. } Using either \code{"previous"} or \code{"next"} is generally recommended, as these two strategies maintain the \emph{relative ordering} between elements of the input. If \code{NULL}, defaults to \code{"error"}. If \code{getOption("clock.strict")} is \code{TRUE}, \code{invalid} must be supplied and cannot be \code{NULL}. This is a convenient way to make production code robust to invalid dates.} } \value{ \code{x} but with some components altered to be at the boundary value. } \description{ This is a Date method for the \code{\link[=date_start]{date_start()}} and \code{\link[=date_end]{date_end()}} generics. } \examples{ x <- date_build(2019:2021, 2:4, 3:5) x # Last day of the month date_end(x, "month") # Last day of the year date_end(x, "year") # First day of the year date_start(x, "year") } clock/man/year-week-day-count-between.Rd0000644000176200001440000000240214422221153017622 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/week-year-week-day.R \name{year-week-day-count-between} \alias{year-week-day-count-between} \alias{calendar_count_between.clock_year_week_day} \title{Counting: year-week-day} \usage{ \method{calendar_count_between}{clock_year_week_day}(start, end, precision, ..., n = 1L) } \arguments{ \item{start, end}{\verb{[clock_year_week_day]} A pair of year-week-day vectors. These will be recycled to their common size.} \item{precision}{\verb{[character(1)]} One of: \itemize{ \item \code{"year"} }} \item{...}{These dots are for future extensions and must be empty.} \item{n}{\verb{[positive integer(1)]} A single positive integer specifying a multiple of \code{precision} to use.} } \value{ An integer representing the number of \code{precision} units between \code{start} and \code{end}. } \description{ This is an year-week-day method for the \code{\link[=calendar_count_between]{calendar_count_between()}} generic. It counts the number of \code{precision} units between \code{start} and \code{end} (i.e., the number of years). } \examples{ # Compute the number of whole years between two dates x <- year_week_day(2001, 1, 2) y <- year_week_day(2021, 1, c(1, 3)) calendar_count_between(x, y, "year") } clock/man/date-zone.Rd0000644000176200001440000000146514423751216014310 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/clock-deprecated.R \name{date-zone} \alias{date-zone} \alias{date_zone} \alias{date_set_zone} \title{Get or set the time zone} \usage{ date_zone(x) date_set_zone(x, zone) } \arguments{ \item{x}{\verb{[POSIXct / POSIXlt]} A date-time vector.} \item{zone}{\verb{[character(1)]} A valid time zone to switch to.} } \description{ \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} \itemize{ \item \code{date_zone()} is deprecated in favor of \code{\link[=date_time_zone]{date_time_zone()}}. \item \code{date_set_zone()} is deprecated in favor of \code{\link[=date_time_set_zone]{date_time_set_zone()}}. } } \keyword{internal} clock/man/calendar_widen.Rd0000644000176200001440000000407614422221153015351 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/calendar.R \name{calendar_widen} \alias{calendar_widen} \title{Widen a calendar to a more precise precision} \usage{ calendar_widen(x, precision) } \arguments{ \item{x}{\verb{[calendar]} A calendar vector.} \item{precision}{\verb{[character(1)]} A precision. Allowed precisions are dependent on the calendar used.} } \value{ \code{x} widened to the supplied \code{precision}. } \description{ \code{calendar_widen()} widens \code{x} to the specified \code{precision}. It does so by setting new components to their smallest value. Each calendar has its own help page describing the precisions that you can widen to: \itemize{ \item \link[=year-month-day-widen]{year-month-day} \item \link[=year-month-weekday-widen]{year-month-weekday} \item \link[=year-week-day-widen]{year-week-day} \item \link[=iso-year-week-day-widen]{iso-year-week-day} \item \link[=year-quarter-day-widen]{year-quarter-day} \item \link[=year-day-widen]{year-day} } } \details{ A subsecond precision \code{x} cannot be widened. You cannot widen from, say, \code{"millisecond"} to \code{"nanosecond"} precision. clock operates under the philosophy that once you have set the subsecond precision of a calendar, it is "locked in" at that precision. If you expected this to multiply the milliseconds by 1e6 to get to nanosecond precision, you probably want to convert to a time point first, and use \code{\link[=time_point_cast]{time_point_cast()}}. Generally, clock treats calendars at a specific precision as a \emph{range} of values. For example, a month precision year-month-day is treated as a range over \verb{[yyyy-mm-01, yyyy-mm-last]}, with no assumption about the day of the month. However, occasionally it is useful to quickly widen a calendar, assuming that you want the beginning of this range to be used for each component. This is where \code{calendar_widen()} can come in handy. } \examples{ # Month precision x <- year_month_day(2019, 1) x # Widen to day precision calendar_widen(x, "day") # Or second precision calendar_widen(x, "second") } clock/man/Date-arithmetic.Rd0000644000176200001440000000677114416032013015420 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/date.R \name{Date-arithmetic} \alias{Date-arithmetic} \alias{add_years.Date} \alias{add_quarters.Date} \alias{add_months.Date} \alias{add_weeks.Date} \alias{add_days.Date} \title{Arithmetic: date} \usage{ \method{add_years}{Date}(x, n, ..., invalid = NULL) \method{add_quarters}{Date}(x, n, ..., invalid = NULL) \method{add_months}{Date}(x, n, ..., invalid = NULL) \method{add_weeks}{Date}(x, n, ...) \method{add_days}{Date}(x, n, ...) } \arguments{ \item{x}{\verb{[Date]} A Date vector.} \item{n}{\verb{[integer / clock_duration]} An integer vector to be converted to a duration, or a duration corresponding to the arithmetic function being used. This corresponds to the number of duration units to add. \code{n} may be negative to subtract units of duration.} \item{...}{These dots are for future extensions and must be empty.} \item{invalid}{\verb{[character(1) / NULL]} One of the following invalid date resolution strategies: \itemize{ \item \code{"previous"}: The previous valid instant in time. \item \code{"previous-day"}: The previous valid day in time, keeping the time of day. \item \code{"next"}: The next valid instant in time. \item \code{"next-day"}: The next valid day in time, keeping the time of day. \item \code{"overflow"}: Overflow by the number of days that the input is invalid by. Time of day is dropped. \item \code{"overflow-day"}: Overflow by the number of days that the input is invalid by. Time of day is kept. \item \code{"NA"}: Replace invalid dates with \code{NA}. \item \code{"error"}: Error on invalid dates. } Using either \code{"previous"} or \code{"next"} is generally recommended, as these two strategies maintain the \emph{relative ordering} between elements of the input. If \code{NULL}, defaults to \code{"error"}. If \code{getOption("clock.strict")} is \code{TRUE}, \code{invalid} must be supplied and cannot be \code{NULL}. This is a convenient way to make production code robust to invalid dates.} } \value{ \code{x} after performing the arithmetic. } \description{ These are Date methods for the \link[=clock-arithmetic]{arithmetic generics}. Calendrical based arithmetic: These functions convert to a year-month-day calendar, perform the arithmetic, then convert back to a Date. \itemize{ \item \code{add_years()} \item \code{add_quarters()} \item \code{add_months()} } Time point based arithmetic: These functions convert to a time point, perform the arithmetic, then convert back to a Date. \itemize{ \item \code{add_weeks()} \item \code{add_days()} } } \details{ Adding a single quarter with \code{add_quarters()} is equivalent to adding 3 months. \code{x} and \code{n} are recycled against each other using \link[vctrs:vector_recycling_rules]{tidyverse recycling rules}. Only calendrical based arithmetic has the potential to generate invalid dates. Time point based arithmetic, like adding days, will always generate a valid date. } \examples{ x <- as.Date("2019-01-01") add_years(x, 1:5) y <- as.Date("2019-01-31") # Adding 1 month to `y` generates an invalid date. Unlike year-month-day # types, R's native Date type cannot handle invalid dates, so you must # resolve them immediately. If you don't you get an error: try(add_months(y, 1:2)) add_months(as_year_month_day(y), 1:2) # Resolve invalid dates by specifying an invalid date resolution strategy # with the `invalid` argument. Using `"previous"` here sets the date to # the previous valid date - i.e. the end of the month. add_months(y, 1:2, invalid = "previous") } clock/man/as_sys_time.Rd0000644000176200001440000000367614427270231014744 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/sys-time.R \name{as_sys_time} \alias{as_sys_time} \title{Convert to a sys-time} \usage{ as_sys_time(x, ...) } \arguments{ \item{x}{\verb{[object]} An object to convert to a sys-time.} \item{...}{These dots are for future extensions and must be empty.} } \value{ A sys-time vector. } \description{ \code{as_sys_time()} converts \code{x} to a sys-time. You can convert to a sys-time from any calendar type, as long as it has at least day precision. There also must not be any invalid dates. If invalid dates exist, they must first be resolved with \code{\link[=invalid_resolve]{invalid_resolve()}}. Converting to a sys-time from a naive-time retains the printed time, but adds an assumption that the time should be interpreted in the UTC time zone. Converting to a sys-time from a zoned-time retains the underlying duration, but the printed time is the equivalent UTC time to whatever the zoned-time's zone happened to be. Converting to a sys-time from a duration just wraps the duration in a sys-time object, adding the assumption that the time should be interpreted in the UTC time zone. The duration must have at least day precision. There are convenience methods for converting to a sys-time from R's native date and date-time types. Like converting from a zoned-time, these retain the underlying duration, but will change the printed time if the zone was not already UTC. } \examples{ x <- as.Date("2019-01-01") # Dates are assumed to be naive, so the printed time is the same whether # we convert it to sys-time or naive-time as_sys_time(x) as_naive_time(x) y <- as.POSIXct("2019-01-01 01:00:00", tz = "America/New_York") # The sys time displays the equivalent time in UTC (5 hours ahead of # America/New_York at this point in the year) as_sys_time(y) ym <- year_month_day(2019, 02) # A minimum of day precision is required try(as_sys_time(ym)) ymd <- set_day(ym, 10) as_sys_time(ymd) } clock/man/calendar_narrow.Rd0000644000176200001440000000336314422221153015551 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/calendar.R \name{calendar_narrow} \alias{calendar_narrow} \title{Narrow a calendar to a less precise precision} \usage{ calendar_narrow(x, precision) } \arguments{ \item{x}{\verb{[calendar]} A calendar vector.} \item{precision}{\verb{[character(1)]} A precision. Allowed precisions are dependent on the calendar used.} } \value{ \code{x} narrowed to the supplied \code{precision}. } \description{ \code{calendar_narrow()} narrows \code{x} to the specified \code{precision}. It does so by dropping components that represent a precision that is finer than \code{precision}. Each calendar has its own help page describing the precisions that you can narrow to: \itemize{ \item \link[=year-month-day-narrow]{year-month-day} \item \link[=year-month-weekday-narrow]{year-month-weekday} \item \link[=year-week-day-narrow]{year-week-day} \item \link[=iso-year-week-day-narrow]{iso-year-week-day} \item \link[=year-quarter-day-narrow]{year-quarter-day} \item \link[=year-day-narrow]{year-day} } } \details{ A subsecond precision \code{x} cannot be narrowed to another subsecond precision. You cannot narrow from, say, \code{"nanosecond"} to \code{"millisecond"} precision. clock operates under the philosophy that once you have set the subsecond precision of a calendar, it is "locked in" at that precision. If you expected this to use integer division to divide the nanoseconds by 1e6 to get to millisecond precision, you probably want to convert to a time point first, and use \code{\link[=time_point_floor]{time_point_floor()}}. } \examples{ # Hour precision x <- year_month_day(2019, 1, 3, 4) x # Narrowed to day precision calendar_narrow(x, "day") # Or month precision calendar_narrow(x, "month") } clock/man/posixt-count-between.Rd0000644000176200001440000001210614151763504016520 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/posixt.R \name{posixt-count-between} \alias{posixt-count-between} \alias{date_count_between.POSIXt} \title{Counting: date-times} \usage{ \method{date_count_between}{POSIXt}(start, end, precision, ..., n = 1L) } \arguments{ \item{start, end}{\verb{[POSIXct / POSIXlt]} A pair of date-time vectors. These will be recycled to their common size.} \item{precision}{\verb{[character(1)]} One of: \itemize{ \item \code{"year"} \item \code{"quarter"} \item \code{"month"} \item \code{"week"} \item \code{"day"} \item \code{"hour"} \item \code{"minute"} \item \code{"second"} }} \item{...}{These dots are for future extensions and must be empty.} \item{n}{\verb{[positive integer(1)]} A single positive integer specifying a multiple of \code{precision} to use.} } \value{ An integer representing the number of \code{precision} units between \code{start} and \code{end}. } \description{ This is a POSIXct/POSIXlt method for the \code{\link[=date_count_between]{date_count_between()}} generic. \code{date_count_between()} counts the number of \code{precision} units between \code{start} and \code{end} (i.e., the number of years or months). This count corresponds to the \emph{whole number} of units, and will never return a fractional value. This is suitable for, say, computing the whole number of years or months between two dates, accounting for the day of the month and the time of day. Internally, the date-time is converted to one of the following three clock types, and the counting is done directly on that type. The choice of type is based on the most common interpretation of each precision, but is ultimately a heuristic. See the examples for more information. \emph{Calendrical based counting:} These precisions convert to a year-month-day calendar and count while in that type. \itemize{ \item \code{"year"} \item \code{"quarter"} \item \code{"month"} } \emph{Naive-time based counting:} These precisions convert to a naive-time and count while in that type. \itemize{ \item \code{"week"} \item \code{"day"} } \emph{Sys-time based counting:} These precisions convert to a sys-time and count while in that type. \itemize{ \item \code{"hour"} \item \code{"minute"} \item \code{"second"} } } \details{ \code{"quarter"} is equivalent to \code{"month"} precision with \code{n} set to \code{n * 3L}. } \section{Comparison Direction}{ The computed count has the property that if \code{start <= end}, then \verb{start + <= end}. Similarly, if \code{start >= end}, then \verb{start + >= end}. In other words, the comparison direction between \code{start} and \code{end} will never change after adding the count to \code{start}. This makes this function useful for repeated count computations at increasingly fine precisions. } \examples{ start <- date_time_parse("2000-05-05 02:00:00", zone = "America/New_York") end <- date_time_parse( c("2020-05-05 01:00:00", "2020-05-05 03:00:00"), zone = "America/New_York" ) # Age in years date_count_between(start, end, "year") # Number of "whole" months between these dates. i.e. # `2000-05-05 02:00:00 -> 2020-04-05 02:00:00` is 239 months # `2000-05-05 02:00:00 -> 2020-05-05 02:00:00` is 240 months # Since `2020-05-05 01:00:00` occurs before the 2nd hour, # it gets a count of 239 date_count_between(start, end, "month") # Number of seconds between date_count_between(start, end, "second") # --------------------------------------------------------------------------- # Naive-time VS Sys-time interpretation # The difference between whether `start` and `end` are converted to a # naive-time vs a sys-time comes into play when dealing with daylight # savings. # Here are two times around a 1 hour DST gap where clocks jumped from # 01:59:59 -> 03:00:00 x <- date_time_build(1970, 4, 26, 1, 50, 00, zone = "America/New_York") y <- date_time_build(1970, 4, 26, 3, 00, 00, zone = "America/New_York") # When treated like sys-times, these are considered to be 10 minutes apart, # which is the amount of time that would have elapsed if you were watching # a clock as it changed between these two times. date_count_between(x, y, "minute") # Lets add a 3rd date that is ~1 day ahead of these z <- date_time_build(1970, 4, 27, 1, 55, 00, zone = "America/New_York") # When treated like naive-times, `z` is considered to be at least 1 day ahead # of `x`, because `01:55:00` is after `01:50:00`. This is probably what you # expected. date_count_between(x, z, "day") # If these were interpreted like sys-times, then `z` would not be considered # to be 1 day ahead. That would look something like this: date_count_between(x, z, "second") trunc(date_count_between(x, z, "second") / 86400) # This is because there have only been 83,100 elapsed seconds since `x`, # which isn't a full day's worth (86,400 seconds). But we'd generally # consider `z` to be 1 day ahead of `x` (and ignore the DST gap), so that is # how it is implemented. # You can override this by converting directly to sys-time, then using # `time_point_count_between()` x_st <- as_sys_time(x) x_st z_st <- as_sys_time(z) z_st time_point_count_between(x_st, z_st, "day") } clock/man/is_year_month_weekday.Rd0000644000176200001440000000103414002162771016756 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gregorian-year-month-weekday.R \name{is_year_month_weekday} \alias{is_year_month_weekday} \title{Is \code{x} a year-month-weekday?} \usage{ is_year_month_weekday(x) } \arguments{ \item{x}{\verb{[object]} An object.} } \value{ Returns \code{TRUE} if \code{x} inherits from \code{"clock_year_month_weekday"}, otherwise returns \code{FALSE}. } \description{ Check if \code{x} is a year-month-weekday. } \examples{ is_year_month_weekday(year_month_weekday(2019)) } clock/man/year-day-count-between.Rd0000644000176200001440000000242614151750124016703 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gregorian-year-day.R \name{year-day-count-between} \alias{year-day-count-between} \alias{calendar_count_between.clock_year_day} \title{Counting: year-day} \usage{ \method{calendar_count_between}{clock_year_day}(start, end, precision, ..., n = 1L) } \arguments{ \item{start, end}{\verb{[clock_year_day]} A pair of year-day vectors. These will be recycled to their common size.} \item{precision}{\verb{[character(1)]} One of: \itemize{ \item \code{"year"} }} \item{...}{These dots are for future extensions and must be empty.} \item{n}{\verb{[positive integer(1)]} A single positive integer specifying a multiple of \code{precision} to use.} } \value{ An integer representing the number of \code{precision} units between \code{start} and \code{end}. } \description{ This is a year-day method for the \code{\link[=calendar_count_between]{calendar_count_between()}} generic. It counts the number of \code{precision} units between \code{start} and \code{end} (i.e., the number of years). } \examples{ # Compute an individual's age in years x <- year_day(2001, 100) y <- year_day(2021, c(99, 101)) calendar_count_between(x, y, "year") # Or in a whole number multiple of years calendar_count_between(x, y, "year", n = 3) } clock/man/year-month-day-boundary.Rd0000644000176200001440000000310214075602251017064 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gregorian-year-month-day.R \name{year-month-day-boundary} \alias{year-month-day-boundary} \alias{calendar_start.clock_year_month_day} \alias{calendar_end.clock_year_month_day} \title{Boundaries: year-month-day} \usage{ \method{calendar_start}{clock_year_month_day}(x, precision) \method{calendar_end}{clock_year_month_day}(x, precision) } \arguments{ \item{x}{\verb{[clock_year_month_day]} A year-month-day vector.} \item{precision}{\verb{[character(1)]} One of: \itemize{ \item \code{"year"} \item \code{"month"} \item \code{"day"} \item \code{"hour"} \item \code{"minute"} \item \code{"second"} \item \code{"millisecond"} \item \code{"microsecond"} \item \code{"nanosecond"} }} } \value{ \code{x} at the same precision, but with some components altered to be at the boundary value. } \description{ This is a year-month-day method for the \code{\link[=calendar_start]{calendar_start()}} and \code{\link[=calendar_end]{calendar_end()}} generics. They adjust components of a calendar to the start or end of a specified \code{precision}. } \examples{ # Hour precision x <- year_month_day(2019, 2:4, 5, 6) x # Compute the start of the month calendar_start(x, "month") # Or the end of the month, notice that the hour value is adjusted as well calendar_end(x, "month") # Compare that with just setting the day of the month to `"last"`, which # doesn't adjust any other components set_day(x, "last") # You can't compute the start / end at a more precise precision than # the input is at try(calendar_start(x, "second")) } clock/man/Date-getters.Rd0000644000176200001440000000146414073622034014745 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/date.R \name{Date-getters} \alias{Date-getters} \alias{get_year.Date} \alias{get_month.Date} \alias{get_day.Date} \title{Getters: date} \usage{ \method{get_year}{Date}(x) \method{get_month}{Date}(x) \method{get_day}{Date}(x) } \arguments{ \item{x}{\verb{[Date]} A Date to get the component from.} } \value{ The component. } \description{ These are Date methods for the \link[=clock-getters]{getter generics}. \itemize{ \item \code{get_year()} returns the Gregorian year. \item \code{get_month()} returns the month of the year. \item \code{get_day()} returns the day of the month. } For more advanced component extraction, convert to the calendar type that you are interested in. } \examples{ x <- as.Date("2019-01-01") + 0:5 get_day(x) } clock/man/weekday-arithmetic.Rd0000644000176200001440000000225514416031540016171 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/weekday.R \name{weekday-arithmetic} \alias{weekday-arithmetic} \alias{add_days.clock_weekday} \title{Arithmetic: weekday} \usage{ \method{add_days}{clock_weekday}(x, n, ...) } \arguments{ \item{x}{\verb{[clock_weekday]} A weekday vector.} \item{n}{\verb{[integer / clock_duration]} An integer vector to be converted to a duration, or a duration corresponding to the arithmetic function being used. This corresponds to the number of duration units to add. \code{n} may be negative to subtract units of duration.} \item{...}{These dots are for future extensions and must be empty.} } \value{ \code{x} after performing the arithmetic. } \description{ These are weekday methods for the \link[=clock-arithmetic]{arithmetic generics}. \itemize{ \item \code{add_days()} } Also check out the examples on the \code{\link[=weekday]{weekday()}} page for more advanced usage. } \details{ \code{x} and \code{n} are recycled against each other using \link[vctrs:vector_recycling_rules]{tidyverse recycling rules}. } \examples{ saturday <- weekday(clock_weekdays$saturday) saturday add_days(saturday, 1) add_days(saturday, 2) } clock/man/zoned_time_info.Rd0000644000176200001440000000166614422265167015601 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/zoned-time.R \name{zoned_time_info} \alias{zoned_time_info} \title{Info: zoned-time} \usage{ zoned_time_info(x) } \arguments{ \item{x}{\verb{[clock_zoned_time]} A zoned-time.} } \value{ A data frame of low level information. } \description{ \code{zoned_time_info()} retrieves a set of low-level information generally not required for most date-time manipulations. It returns a data frame with the same columns as \code{\link[=sys_time_info]{sys_time_info()}}, but the \code{begin} and \code{end} columns are zoned-times with the same time zone as \code{x}. } \examples{ x <- year_month_day(2021, 03, 14, c(01, 03), c(59, 00), c(59, 00)) x <- as_naive_time(x) x <- as_zoned_time(x, "America/New_York") # x[1] is in EST, x[2] is in EDT x info <- zoned_time_info(x) info # `end` can be used to iterate through daylight saving time transitions zoned_time_info(info$end) } clock/man/as_year_week_day.Rd0000644000176200001440000000231714422221153015701 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/week-year-week-day.R \name{as_year_week_day} \alias{as_year_week_day} \title{Convert to year-week-day} \usage{ as_year_week_day(x, ..., start = NULL) } \arguments{ \item{x}{\verb{[vector]} A vector to convert to year-week-day.} \item{...}{These dots are for future extensions and must be empty.} \item{start}{\verb{[integer(1) / NULL]} The day to consider the start of the week. 1 = Sunday and 7 = Saturday. If \code{NULL}: \itemize{ \item If \code{x} is a year-week-day, it will be returned as is. \item Otherwise, a \code{start} of Sunday will be used. }} } \value{ A year-week-day vector. } \description{ \code{as_year_week_day()} converts a vector to the year-week-day calendar. Time points, Dates, POSIXct, and other calendars can all be converted to year-week-day. } \examples{ # From Date as_year_week_day(as.Date("2019-01-01")) as_year_week_day(as.Date("2019-01-01"), start = clock_weekdays$monday) # From POSIXct, which assumes that the naive time is what should be converted as_year_week_day(as.POSIXct("2019-01-01 02:30:30", "America/New_York")) # From other calendars as_year_week_day(year_quarter_day(2019, quarter = 2, day = 50)) } clock/man/date_group.Rd0000644000176200001440000000205114073622034014537 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/date.R \name{date_group} \alias{date_group} \title{Group date and date-time components} \usage{ date_group(x, precision, ..., n = 1L) } \arguments{ \item{x}{\verb{[Date / POSIXct / POSIXlt]} A date or date-time vector.} \item{precision}{\verb{[character(1)]} A precision. Allowed precisions are dependent on the input used.} \item{...}{These dots are for future extensions and must be empty.} \item{n}{\verb{[positive integer(1)]} A single positive integer specifying a multiple of \code{precision} to use.} } \value{ \code{x}, grouped at \code{precision}. } \description{ \code{date_group()} groups by a single component of a date-time, such as month of the year, or day of the month. There are separate help pages for grouping dates and date-times: \itemize{ \item \link[=date-group]{dates (Date)} \item \link[=posixt-group]{date-times (POSIXct/POSIXlt)} } } \examples{ # See type specific documentation for more examples date_group(as.Date("2019-01-01") + 0:5, "day", n = 2) } clock/man/date-time-zone.Rd0000644000176200001440000000433414423751216015242 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/posixt.R \name{date-time-zone} \alias{date-time-zone} \alias{date_time_zone} \alias{date_time_set_zone} \title{Get or set the time zone} \usage{ date_time_zone(x) date_time_set_zone(x, zone) } \arguments{ \item{x}{\verb{[POSIXct / POSIXlt]} A date-time vector.} \item{zone}{\verb{[character(1)]} A valid time zone to switch to.} } \value{ \itemize{ \item \code{date_time_zone()} returns a string containing the time zone. \item \code{date_time_set_zone()} returns \code{x} with an altered printed time. The underlying duration is not changed. } } \description{ \itemize{ \item \code{date_time_zone()} gets the time zone. \item \code{date_time_set_zone()} sets the time zone. This retains the \emph{underlying duration}, but changes the \emph{printed time} depending on the zone that is chosen. } } \details{ This function is only valid for date-times, as clock treats R's Date class as a \emph{naive} type, which always has a yet-to-be-specified time zone. } \examples{ library(magrittr) # Cannot set or get the zone of Date. # clock assumes that Dates are naive types, like naive-time. x <- date_parse("2019-01-01") try(date_time_zone(x)) try(date_time_set_zone(x, "America/New_York")) x <- date_time_parse("2019-01-02 01:30:00", "America/New_York") x date_time_zone(x) # If it is 1:30am in New York, what time is it in Los Angeles? # Same underlying duration, new printed time date_time_set_zone(x, "America/Los_Angeles") # If you want to retain the printed time, but change the underlying duration, # convert to a naive-time to drop the time zone, then convert back to a # date-time. Be aware that this requires that you handle daylight saving time # irregularities with the `nonexistent` and `ambiguous` arguments to # `as_date_time()`! x \%>\% as_naive_time() \%>\% as_date_time("America/Los_Angeles") y <- date_time_parse("2021-03-28 03:30:00", "America/New_York") y y_nt <- as_naive_time(y) y_nt # Helsinki had a daylight saving time gap where they jumped from # 02:59:59 -> 04:00:00 try(as_date_time(y_nt, "Europe/Helsinki")) as_date_time(y_nt, "Europe/Helsinki", nonexistent = "roll-forward") as_date_time(y_nt, "Europe/Helsinki", nonexistent = "roll-backward") } clock/man/year-month-day-setters.Rd0000644000176200001440000000440614007013620016731 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gregorian-year-month-day.R \name{year-month-day-setters} \alias{year-month-day-setters} \alias{set_year.clock_year_month_day} \alias{set_month.clock_year_month_day} \alias{set_day.clock_year_month_day} \alias{set_hour.clock_year_month_day} \alias{set_minute.clock_year_month_day} \alias{set_second.clock_year_month_day} \alias{set_millisecond.clock_year_month_day} \alias{set_microsecond.clock_year_month_day} \alias{set_nanosecond.clock_year_month_day} \title{Setters: year-month-day} \usage{ \method{set_year}{clock_year_month_day}(x, value, ...) \method{set_month}{clock_year_month_day}(x, value, ...) \method{set_day}{clock_year_month_day}(x, value, ...) \method{set_hour}{clock_year_month_day}(x, value, ...) \method{set_minute}{clock_year_month_day}(x, value, ...) \method{set_second}{clock_year_month_day}(x, value, ...) \method{set_millisecond}{clock_year_month_day}(x, value, ...) \method{set_microsecond}{clock_year_month_day}(x, value, ...) \method{set_nanosecond}{clock_year_month_day}(x, value, ...) } \arguments{ \item{x}{\verb{[clock_year_month_day]} A year-month-day vector.} \item{value}{\verb{[integer / "last"]} The value to set the component to. For \code{set_day()}, this can also be \code{"last"} to set the day to the last day of the month.} \item{...}{These dots are for future extensions and must be empty.} } \value{ \code{x} with the component set. } \description{ These are year-month-day methods for the \link[=clock-setters]{setter generics}. \itemize{ \item \code{set_year()} sets the Gregorian year. \item \code{set_month()} sets the month of the year. Valid values are in the range of \verb{[1, 12]}. \item \code{set_day()} sets the day of the month. Valid values are in the range of \verb{[1, 31]}. \item There are sub-daily setters for setting more precise components. } } \examples{ x <- year_month_day(2019, 1:3) # Set the day set_day(x, 12:14) # Set to the "last" day of the month set_day(x, "last") # Set to an invalid day of the month invalid <- set_day(x, 31) invalid # Then resolve the invalid day by choosing the next valid day invalid_resolve(invalid, invalid = "next") # Cannot set a component two levels more precise than where you currently are try(set_hour(x, 5)) } clock/man/year-quarter-day-getters.Rd0000644000176200001440000000373714007013500017256 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/quarterly-year-quarter-day.R \name{year-quarter-day-getters} \alias{year-quarter-day-getters} \alias{get_year.clock_year_quarter_day} \alias{get_quarter.clock_year_quarter_day} \alias{get_day.clock_year_quarter_day} \alias{get_hour.clock_year_quarter_day} \alias{get_minute.clock_year_quarter_day} \alias{get_second.clock_year_quarter_day} \alias{get_millisecond.clock_year_quarter_day} \alias{get_microsecond.clock_year_quarter_day} \alias{get_nanosecond.clock_year_quarter_day} \title{Getters: year-quarter-day} \usage{ \method{get_year}{clock_year_quarter_day}(x) \method{get_quarter}{clock_year_quarter_day}(x) \method{get_day}{clock_year_quarter_day}(x) \method{get_hour}{clock_year_quarter_day}(x) \method{get_minute}{clock_year_quarter_day}(x) \method{get_second}{clock_year_quarter_day}(x) \method{get_millisecond}{clock_year_quarter_day}(x) \method{get_microsecond}{clock_year_quarter_day}(x) \method{get_nanosecond}{clock_year_quarter_day}(x) } \arguments{ \item{x}{\verb{[clock_year_quarter_day]} A year-quarter-day to get the component from.} } \value{ The component. } \description{ These are year-quarter-day methods for the \link[=clock-getters]{getter generics}. \itemize{ \item \code{get_year()} returns the fiscal year. Note that this can differ from the Gregorian year if \code{start != 1L}. \item \code{get_quarter()} returns the fiscal quarter as a value between 1-4. \item \code{get_day()} returns the day of the fiscal quarter as a value between 1-92. \item There are sub-daily getters for extracting more precise components. } } \examples{ x <- year_quarter_day(2020, 1:4) get_quarter(x) # Set and then get the last day of the quarter x <- set_day(x, "last") get_day(x) # Start the fiscal year in November and choose the 50th day in # each quarter of 2020 november <- 11 y <- year_quarter_day(2020, 1:4, 50, start = 11) y get_day(y) # What does that map to in year-month-day? as_year_month_day(y) } clock/man/duration-rounding.Rd0000644000176200001440000000457614010772357016101 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/duration.R \name{duration-rounding} \alias{duration-rounding} \alias{duration_floor} \alias{duration_ceiling} \alias{duration_round} \title{Duration rounding} \usage{ duration_floor(x, precision, ..., n = 1L) duration_ceiling(x, precision, ..., n = 1L) duration_round(x, precision, ..., n = 1L) } \arguments{ \item{x}{\verb{[clock_duration]} A duration.} \item{precision}{\verb{[character(1)]} A precision. One of: \itemize{ \item \code{"year"} \item \code{"quarter"} \item \code{"month"} \item \code{"week"} \item \code{"day"} \item \code{"hour"} \item \code{"minute"} \item \code{"second"} \item \code{"millisecond"} \item \code{"microsecond"} \item \code{"nanosecond"} }} \item{...}{These dots are for future extensions and must be empty.} \item{n}{\verb{[positive integer(1)]} A positive integer specifying the multiple of \code{precision} to use.} } \value{ \code{x} rounded to the \code{precision}. } \description{ \itemize{ \item \code{duration_floor()} rounds a duration down to a multiple of the specified \code{precision}. \item \code{duration_ceiling()} rounds a duration up to a multiple of the specified \code{precision}. \item \code{duration_round()} rounds up or down depending on what is closer, rounding up on ties. } } \details{ You can floor calendrical durations to other calendrical durations, and chronological durations to other chronological durations, but you can't floor a chronological duration to a calendrical duration (such as flooring from day to month). For more information, see the documentation on the \link[=duration-helper]{duration helper} page. } \examples{ x <- duration_seconds(c(86399, 86401)) duration_floor(x, "day") duration_ceiling(x, "day") # Can't floor from a chronological duration (seconds) # to a calendrical duration (months) try(duration_floor(x, "month")) # Every 2 days, using an origin of day 0 y <- duration_seconds(c(0, 86400, 86400 * 2, 86400 * 3)) duration_floor(y, "day", n = 2) # Shifting the origin to be day 1 origin <- duration_days(1) duration_floor(y - origin, "day", n = 2) + origin # Rounding will round ties up half_day <- 86400 / 2 half_day_durations <- duration_seconds(c(half_day - 1, half_day, half_day + 1)) duration_round(half_day_durations, "day") # With larger units x <- duration_months(c(0, 15, 24)) duration_floor(x, "year") duration_floor(x, "quarter") } clock/man/year-month-weekday-count-between.Rd0000644000176200001440000000353514151763504020712 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gregorian-year-month-weekday.R \name{year-month-weekday-count-between} \alias{year-month-weekday-count-between} \alias{calendar_count_between.clock_year_month_weekday} \title{Counting: year-month-weekday} \usage{ \method{calendar_count_between}{clock_year_month_weekday}(start, end, precision, ..., n = 1L) } \arguments{ \item{start, end}{\verb{[clock_year_month_weekday]} A pair of year-month-weekday vectors. These will be recycled to their common size.} \item{precision}{\verb{[character(1)]} One of: \itemize{ \item \code{"year"} \item \code{"quarter"} \item \code{"month"} }} \item{...}{These dots are for future extensions and must be empty.} \item{n}{\verb{[positive integer(1)]} A single positive integer specifying a multiple of \code{precision} to use.} } \value{ An integer representing the number of \code{precision} units between \code{start} and \code{end}. } \description{ This is a year-month-weekday method for the \code{\link[=calendar_count_between]{calendar_count_between()}} generic. It counts the number of \code{precision} units between \code{start} and \code{end} (i.e., the number of years or months). } \details{ Remember that year-month-weekday is not comparable when it is \code{"day"} precision or finer, so this method is only defined for \code{"year"} and \code{"month"} precision year-month-weekday objects. \code{"quarter"} is equivalent to \code{"month"} precision with \code{n} set to \code{n * 3L}. } \examples{ # Compute the number of months between two dates x <- year_month_weekday(2001, 2) y <- year_month_weekday(2021, c(1, 3)) calendar_count_between(x, y, "month") # Remember that day precision or finer year-month-weekday objects # are not comparable, so this won't work x <- year_month_weekday(2001, 2, 1, 1) try(calendar_count_between(x, x, "month")) } clock/man/year-day-getters.Rd0000644000176200001440000000262314016222032015570 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gregorian-year-day.R \name{year-day-getters} \alias{year-day-getters} \alias{get_year.clock_year_day} \alias{get_day.clock_year_day} \alias{get_hour.clock_year_day} \alias{get_minute.clock_year_day} \alias{get_second.clock_year_day} \alias{get_millisecond.clock_year_day} \alias{get_microsecond.clock_year_day} \alias{get_nanosecond.clock_year_day} \title{Getters: year-day} \usage{ \method{get_year}{clock_year_day}(x) \method{get_day}{clock_year_day}(x) \method{get_hour}{clock_year_day}(x) \method{get_minute}{clock_year_day}(x) \method{get_second}{clock_year_day}(x) \method{get_millisecond}{clock_year_day}(x) \method{get_microsecond}{clock_year_day}(x) \method{get_nanosecond}{clock_year_day}(x) } \arguments{ \item{x}{\verb{[clock_year_day]} A year-day to get the component from.} } \value{ The component. } \description{ These are year-day methods for the \link[=clock-getters]{getter generics}. \itemize{ \item \code{get_year()} returns the Gregorian year. \item \code{get_day()} returns the day of the year. \item There are sub-daily getters for extracting more precise components. } } \examples{ x <- year_day(2019, 101:105, 1, 20, 30) get_day(x) get_second(x) # Cannot extract more precise components y <- year_day(2019, 1) try(get_hour(y)) # Cannot extract components that don't exist for this calendar try(get_quarter(x)) } clock/man/iso-year-week-day-widen.Rd0000644000176200001440000000220014006266045016744 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/iso-year-week-day.R \name{iso-year-week-day-widen} \alias{iso-year-week-day-widen} \alias{calendar_widen.clock_iso_year_week_day} \title{Widen: iso-year-week-day} \usage{ \method{calendar_widen}{clock_iso_year_week_day}(x, precision) } \arguments{ \item{x}{\verb{[clock_iso_year_week_day]} A iso-year-week-day vector.} \item{precision}{\verb{[character(1)]} One of: \itemize{ \item \code{"year"} \item \code{"week"} \item \code{"day"} \item \code{"hour"} \item \code{"minute"} \item \code{"second"} \item \code{"millisecond"} \item \code{"microsecond"} \item \code{"nanosecond"} }} } \value{ \code{x} widened to the supplied \code{precision}. } \description{ This is a iso-year-week-day method for the \code{\link[=calendar_widen]{calendar_widen()}} generic. It widens a iso-year-week-day vector to the specified \code{precision}. } \examples{ # Week precision x <- iso_year_week_day(2019, 1) x # Widen to day precision # In the ISO calendar, the first day of the week is a Monday calendar_widen(x, "day") # Or second precision sec <- calendar_widen(x, "second") sec } clock/man/year-month-weekday-widen.Rd0000644000176200001440000000255514006266143017236 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gregorian-year-month-weekday.R \name{year-month-weekday-widen} \alias{year-month-weekday-widen} \alias{calendar_widen.clock_year_month_weekday} \title{Widen: year-month-weekday} \usage{ \method{calendar_widen}{clock_year_month_weekday}(x, precision) } \arguments{ \item{x}{\verb{[clock_year_month_weekday]} A year-month-weekday vector.} \item{precision}{\verb{[character(1)]} One of: \itemize{ \item \code{"year"} \item \code{"month"} \item \code{"day"} \item \code{"hour"} \item \code{"minute"} \item \code{"second"} \item \code{"millisecond"} \item \code{"microsecond"} \item \code{"nanosecond"} }} } \value{ \code{x} widened to the supplied \code{precision}. } \description{ This is a year-month-weekday method for the \code{\link[=calendar_widen]{calendar_widen()}} generic. It widens a year-month-weekday vector to the specified \code{precision}. } \details{ Widening a month precision year-month-weekday to day precision will set the day and the index to \code{1}. This sets the weekday components to the first Sunday of the month. } \examples{ # Month precision x <- year_month_weekday(2019, 1) x # Widen to day precision # Note that this sets both the day and index to 1, # i.e. the first Sunday of the month. calendar_widen(x, "day") # Or second precision sec <- calendar_widen(x, "second") sec } clock/man/year-month-weekday-narrow.Rd0000644000176200001440000000206114006303002017412 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gregorian-year-month-weekday.R \name{year-month-weekday-narrow} \alias{year-month-weekday-narrow} \alias{calendar_narrow.clock_year_month_weekday} \title{Narrow: year-month-weekday} \usage{ \method{calendar_narrow}{clock_year_month_weekday}(x, precision) } \arguments{ \item{x}{\verb{[clock_year_month_weekday]} A year-month-weekday vector.} \item{precision}{\verb{[character(1)]} One of: \itemize{ \item \code{"year"} \item \code{"month"} \item \code{"day"} \item \code{"hour"} \item \code{"minute"} \item \code{"second"} \item \code{"millisecond"} \item \code{"microsecond"} \item \code{"nanosecond"} }} } \value{ \code{x} narrowed to the supplied \code{precision}. } \description{ This is a year-month-weekday method for the \code{\link[=calendar_narrow]{calendar_narrow()}} generic. It narrows a year-month-weekday vector to the specified \code{precision}. } \examples{ # Day precision x <- year_month_weekday(2019, 1, 1, 2) x # Narrowed to month precision calendar_narrow(x, "month") } clock/man/date-group.Rd0000644000176200001440000000457414073622034014471 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/date.R \name{date-group} \alias{date-group} \alias{date_group.Date} \title{Group date components} \usage{ \method{date_group}{Date}(x, precision, ..., n = 1L, invalid = NULL) } \arguments{ \item{x}{\verb{[Date]} A date vector.} \item{precision}{\verb{[character(1)]} One of: \itemize{ \item \code{"year"} \item \code{"month"} \item \code{"day"} }} \item{...}{These dots are for future extensions and must be empty.} \item{n}{\verb{[positive integer(1)]} A single positive integer specifying a multiple of \code{precision} to use.} \item{invalid}{\verb{[character(1) / NULL]} One of the following invalid date resolution strategies: \itemize{ \item \code{"previous"}: The previous valid instant in time. \item \code{"previous-day"}: The previous valid day in time, keeping the time of day. \item \code{"next"}: The next valid instant in time. \item \code{"next-day"}: The next valid day in time, keeping the time of day. \item \code{"overflow"}: Overflow by the number of days that the input is invalid by. Time of day is dropped. \item \code{"overflow-day"}: Overflow by the number of days that the input is invalid by. Time of day is kept. \item \code{"NA"}: Replace invalid dates with \code{NA}. \item \code{"error"}: Error on invalid dates. } Using either \code{"previous"} or \code{"next"} is generally recommended, as these two strategies maintain the \emph{relative ordering} between elements of the input. If \code{NULL}, defaults to \code{"error"}. If \code{getOption("clock.strict")} is \code{TRUE}, \code{invalid} must be supplied and cannot be \code{NULL}. This is a convenient way to make production code robust to invalid dates.} } \value{ \code{x}, grouped at \code{precision}. } \description{ This is a Date method for the \code{\link[=date_group]{date_group()}} generic. \code{date_group()} groups by a single component of a Date, such as month of the year, or day of the month. If you need to group by more complex components, like ISO weeks, or quarters, convert to a calendar type that contains the component you are interested in grouping by. } \examples{ x <- as.Date("2019-01-01") + -3:5 x # Group by 2 days of the current month. # Note that this resets at the beginning of the month, creating day groups # of [29, 30] [31] [01, 02] [03, 04]. date_group(x, "day", n = 2) # Group by month date_group(x, "month") } clock/man/posixt-getters.Rd0000644000176200001440000000232514007040663015412 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/posixt.R \name{posixt-getters} \alias{posixt-getters} \alias{get_year.POSIXt} \alias{get_month.POSIXt} \alias{get_day.POSIXt} \alias{get_hour.POSIXt} \alias{get_minute.POSIXt} \alias{get_second.POSIXt} \title{Getters: date-time} \usage{ \method{get_year}{POSIXt}(x) \method{get_month}{POSIXt}(x) \method{get_day}{POSIXt}(x) \method{get_hour}{POSIXt}(x) \method{get_minute}{POSIXt}(x) \method{get_second}{POSIXt}(x) } \arguments{ \item{x}{\verb{[POSIXct / POSIXlt]} A date-time type to get the component from.} } \value{ The component. } \description{ These are POSIXct/POSIXlt methods for the \link[=clock-getters]{getter generics}. \itemize{ \item \code{get_year()} returns the Gregorian year. \item \code{get_month()} returns the month of the year. \item \code{get_day()} returns the day of the month. \item There are sub-daily getters for extracting more precise components, up to a precision of seconds. } For more advanced component extraction, convert to the calendar type that you are interested in. } \examples{ x <- as.POSIXct("2019-01-01", tz = "America/New_York") x <- add_days(x, 0:5) x <- set_second(x, 10:15) get_day(x) get_second(x) } clock/man/naive_time_info.Rd0000644000176200001440000001075114017505732015552 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/naive-time.R \name{naive_time_info} \alias{naive_time_info} \title{Info: naive-time} \usage{ naive_time_info(x, zone) } \arguments{ \item{x}{\verb{[clock_naive_time]} A naive-time.} \item{zone}{\verb{[character]} A valid time zone name. Unlike most functions in clock, in \code{naive_time_info()} \code{zone} is vectorized and is recycled against \code{x}.} } \value{ A data frame of low level information. } \description{ \code{naive_time_info()} retrieves a set of low-level information generally not required for most date-time manipulations. It is used implicitly by \code{as_zoned_time()} when converting from a naive-time. It returns a data frame with the following columns: \itemize{ \item \code{type}: A character vector containing one of: \itemize{ \item \code{"unique"}: The naive-time maps uniquely to a zoned-time that can be created with \code{zone}. \item \code{"nonexistent"}: The naive-time does not exist as a zoned-time that can be created with \code{zone}. \item \code{"ambiguous"}: The naive-time exists twice as a zoned-time that can be created with \code{zone}. } \item \code{first}: A \code{\link[=sys_time_info]{sys_time_info()}} data frame. \item \code{second}: A \code{\link[=sys_time_info]{sys_time_info()}} data frame. } \subsection{type == "unique"}{ \itemize{ \item \code{first} will be filled out with sys-info representing daylight saving time information for that time point in \code{zone}. \item \code{second} will contain only \code{NA} values, as there is no ambiguity to represent information for. } } \subsection{type == "nonexistent"}{ \itemize{ \item \code{first} will be filled out with the sys-info that ends just prior to \code{x}. \item \code{second} will be filled out with the sys-info that begins just after \code{x}. } } \subsection{type == "ambiguous"}{ \itemize{ \item \code{first} will be filled out with the sys-info that ends just after \code{x}. \item \code{second} will be filled out with the sys-info that starts just before \code{x}. } } } \details{ If the tibble package is installed, it is recommended to convert the output to a tibble with \code{as_tibble()}, as that will print the df-cols much nicer. } \examples{ library(vctrs) x <- year_month_day(1970, 04, 26, 02, 30, 00) x <- as_naive_time(x) # Maps uniquely to a time in London naive_time_info(x, "Europe/London") # This naive-time never existed in New York! # A DST gap jumped the time from 01:59:59 -> 03:00:00, # skipping the 2 o'clock hour zone <- "America/New_York" info <- naive_time_info(x, zone) info # You can recreate various `nonexistent` strategies with this info as_zoned_time(x, zone, nonexistent = "roll-forward") as_zoned_time(info$first$end, zone) as_zoned_time(x, zone, nonexistent = "roll-backward") as_zoned_time(info$first$end - 1, zone) as_zoned_time(x, zone, nonexistent = "shift-forward") as_zoned_time(as_sys_time(x) - info$first$offset, zone) as_zoned_time(x, zone, nonexistent = "shift-backward") as_zoned_time(as_sys_time(x) - info$second$offset, zone) # --------------------------------------------------------------------------- # Normalizing to UTC # Imagine you had the following printed times, and knowledge that they # are to be interpreted as in the corresponding time zones df <- data_frame( x = c("2020-01-05 02:30:00", "2020-06-03 12:20:05"), zone = c("America/Los_Angeles", "Europe/London") ) # The times are assumed to be naive-times, i.e. if you lived in the `zone` # at the moment the time was recorded, then you would have seen that time # printed on the clock. Currently, these are strings. To convert them to # a time based type, you'll have to acknowledge that R only lets you have # 1 time zone in a vector of date-times at a time. So you'll need to # normalize these naive-times. The easiest thing to normalize them to # is UTC. df$naive <- naive_time_parse(df$x) # Get info about the naive times using a vector of zones info <- naive_time_info(df$naive, df$zone) info # We'll assume that some system generated these naive-times with no # chance of them ever being nonexistent or ambiguous. So now all we have # to do is use the offset to convert the naive-time to a sys-time. The # relationship used is: # offset = naive_time - sys_time df$sys <- as_sys_time(df$naive) - info$first$offset df # At this point, both times are in UTC. From here, you can convert them # both to either America/Los_Angeles or Europe/London as required. as_zoned_time(df$sys, "America/Los_Angeles") as_zoned_time(df$sys, "Europe/London") } clock/man/year-day-widen.Rd0000644000176200001440000000235714016222032015225 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gregorian-year-day.R \name{year-day-widen} \alias{year-day-widen} \alias{calendar_widen.clock_year_day} \title{Widen: year-day} \usage{ \method{calendar_widen}{clock_year_day}(x, precision) } \arguments{ \item{x}{\verb{[clock_year_day]} A year-day vector.} \item{precision}{\verb{[character(1)]} One of: \itemize{ \item \code{"year"} \item \code{"day"} \item \code{"hour"} \item \code{"minute"} \item \code{"second"} \item \code{"millisecond"} \item \code{"microsecond"} \item \code{"nanosecond"} }} } \value{ \code{x} widened to the supplied \code{precision}. } \description{ This is a year-day method for the \code{\link[=calendar_widen]{calendar_widen()}} generic. It widens a year-day vector to the specified \code{precision}. } \examples{ # Year precision x <- year_day(2019) x # Widen to day precision calendar_widen(x, "day") # Or second precision sec <- calendar_widen(x, "second") sec # Second precision can be widened to subsecond precision milli <- calendar_widen(sec, "millisecond") micro <- calendar_widen(sec, "microsecond") milli micro # But once you have "locked in" a subsecond precision, it can't # be widened again try(calendar_widen(milli, "microsecond")) } clock/man/year-quarter-day-arithmetic.Rd0000644000176200001440000000334514416031540017735 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/quarterly-year-quarter-day.R \name{year-quarter-day-arithmetic} \alias{year-quarter-day-arithmetic} \alias{add_years.clock_year_quarter_day} \alias{add_quarters.clock_year_quarter_day} \title{Arithmetic: year-quarter-day} \usage{ \method{add_years}{clock_year_quarter_day}(x, n, ...) \method{add_quarters}{clock_year_quarter_day}(x, n, ...) } \arguments{ \item{x}{\verb{[clock_year_quarter_day]} A year-quarter-day vector.} \item{n}{\verb{[integer / clock_duration]} An integer vector to be converted to a duration, or a duration corresponding to the arithmetic function being used. This corresponds to the number of duration units to add. \code{n} may be negative to subtract units of duration.} \item{...}{These dots are for future extensions and must be empty.} } \value{ \code{x} after performing the arithmetic. } \description{ These are year-quarter-day methods for the \link[=clock-arithmetic]{arithmetic generics}. \itemize{ \item \code{add_years()} \item \code{add_quarters()} } Notably, \emph{you cannot add days to a year-quarter-day}. For day-based arithmetic, first convert to a time point with \code{\link[=as_naive_time]{as_naive_time()}} or \code{\link[=as_sys_time]{as_sys_time()}}. } \details{ \code{x} and \code{n} are recycled against each other using \link[vctrs:vector_recycling_rules]{tidyverse recycling rules}. } \examples{ x <- year_quarter_day(2019, 1:3) x add_quarters(x, 2) # Make the fiscal year start in March y <- year_quarter_day(2019, 1:2, 1, start = 3) y add_quarters(y, 1) # What year-month-day does this correspond to? # Note that the fiscal year doesn't necessarily align with the Gregorian # year! as_year_month_day(add_quarters(y, 1)) } clock/man/zoned-zone.Rd0000644000176200001440000000245614423752321014511 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/zoned-time.R \name{zoned-zone} \alias{zoned-zone} \alias{zoned_time_zone} \alias{zoned_time_set_zone} \title{Get or set the time zone} \usage{ zoned_time_zone(x) zoned_time_set_zone(x, zone) } \arguments{ \item{x}{\verb{[zoned_time]} A zoned time to get or set the time zone of.} \item{zone}{\verb{[character(1)]} A valid time zone to switch to.} } \value{ \itemize{ \item \code{zoned_time_zone()} returns a string containing the time zone. \item \code{zoned_time_set_zone()} returns \code{x} with an altered time zone attribute. The underlying instant is \emph{not} changed. } } \description{ \itemize{ \item \code{zoned_time_zone()} gets the time zone. \item \code{zoned_time_set_zone()} sets the time zone \emph{without changing the underlying instant}. This means that the result will represent the equivalent time in the new time zone. } } \examples{ x <- year_month_day(2019, 1, 1) x <- as_zoned_time(as_naive_time(x), "America/New_York") x zoned_time_zone(x) # Equivalent UTC time zoned_time_set_zone(x, "UTC") # To force a new time zone with the same printed time, # convert to a naive time that has no implied time zone, # then convert back to a zoned time in the new time zone. nt <- as_naive_time(x) nt as_zoned_time(nt, "UTC") } clock/man/is_year_quarter_day.Rd0000644000176200001440000000101214005062256016434 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/quarterly-year-quarter-day.R \name{is_year_quarter_day} \alias{is_year_quarter_day} \title{Is \code{x} a year-quarter-day?} \usage{ is_year_quarter_day(x) } \arguments{ \item{x}{\verb{[object]} An object.} } \value{ Returns \code{TRUE} if \code{x} inherits from \code{"clock_year_quarter_day"}, otherwise returns \code{FALSE}. } \description{ Check if \code{x} is a year-quarter-day. } \examples{ is_year_quarter_day(year_quarter_day(2019)) } clock/man/year-week-day-setters.Rd0000644000176200001440000000450214422221153016537 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/week-year-week-day.R \name{year-week-day-setters} \alias{year-week-day-setters} \alias{set_year.clock_year_week_day} \alias{set_week.clock_year_week_day} \alias{set_day.clock_year_week_day} \alias{set_hour.clock_year_week_day} \alias{set_minute.clock_year_week_day} \alias{set_second.clock_year_week_day} \alias{set_millisecond.clock_year_week_day} \alias{set_microsecond.clock_year_week_day} \alias{set_nanosecond.clock_year_week_day} \title{Setters: year-week-day} \usage{ \method{set_year}{clock_year_week_day}(x, value, ...) \method{set_week}{clock_year_week_day}(x, value, ...) \method{set_day}{clock_year_week_day}(x, value, ...) \method{set_hour}{clock_year_week_day}(x, value, ...) \method{set_minute}{clock_year_week_day}(x, value, ...) \method{set_second}{clock_year_week_day}(x, value, ...) \method{set_millisecond}{clock_year_week_day}(x, value, ...) \method{set_microsecond}{clock_year_week_day}(x, value, ...) \method{set_nanosecond}{clock_year_week_day}(x, value, ...) } \arguments{ \item{x}{\verb{[clock_year_week_day]} A year-week-day vector.} \item{value}{\verb{[integer / "last"]} The value to set the component to. For \code{set_week()}, this can also be \code{"last"} to adjust to the last week of the current year.} \item{...}{These dots are for future extensions and must be empty.} } \value{ \code{x} with the component set. } \description{ These are year-week-day methods for the \link[=clock-setters]{setter generics}. \itemize{ \item \code{set_year()} sets the year. \item \code{set_week()} sets the week of the year. Valid values are in the range of \verb{[1, 53]}. \item \code{set_day()} sets the day of the week. Valid values are in the range of \verb{[1, 7]}. \item There are sub-daily setters for setting more precise components. } } \examples{ # Year precision vector x <- year_week_day(2019:2023) # Promote to week precision by setting the week # (Note that some weeks have 52 weeks, and others have 53) x <- set_week(x, "last") x # Set to an invalid week invalid <- set_week(x, 53) invalid # Here are the invalid ones (they only have 52 weeks) invalid[invalid_detect(invalid)] # Resolve the invalid dates by choosing the previous/next valid moment invalid_resolve(invalid, invalid = "previous") invalid_resolve(invalid, invalid = "next") } clock/man/year-month-day-arithmetic.Rd0000644000176200001440000000426414416031540017400 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gregorian-year-month-day.R \name{year-month-day-arithmetic} \alias{year-month-day-arithmetic} \alias{add_years.clock_year_month_day} \alias{add_quarters.clock_year_month_day} \alias{add_months.clock_year_month_day} \title{Arithmetic: year-month-day} \usage{ \method{add_years}{clock_year_month_day}(x, n, ...) \method{add_quarters}{clock_year_month_day}(x, n, ...) \method{add_months}{clock_year_month_day}(x, n, ...) } \arguments{ \item{x}{\verb{[clock_year_month_day]} A year-month-day vector.} \item{n}{\verb{[integer / clock_duration]} An integer vector to be converted to a duration, or a duration corresponding to the arithmetic function being used. This corresponds to the number of duration units to add. \code{n} may be negative to subtract units of duration.} \item{...}{These dots are for future extensions and must be empty.} } \value{ \code{x} after performing the arithmetic. } \description{ These are year-month-day methods for the \link[=clock-arithmetic]{arithmetic generics}. \itemize{ \item \code{add_years()} \item \code{add_quarters()} \item \code{add_months()} } Notably, \emph{you cannot add days to a year-month-day}. For day-based arithmetic, first convert to a time point with \code{\link[=as_naive_time]{as_naive_time()}} or \code{\link[=as_sys_time]{as_sys_time()}}. } \details{ Adding a single quarter with \code{add_quarters()} is equivalent to adding 3 months. \code{x} and \code{n} are recycled against each other using \link[vctrs:vector_recycling_rules]{tidyverse recycling rules}. } \examples{ x <- year_month_day(2019, 1, 1) add_years(x, 1:5) y <- year_month_day(2019, 1, 31) # Adding 1 month to `y` generates an invalid date y_plus <- add_months(y, 1:2) y_plus # Invalid dates are fine, as long as they are eventually resolved # by either manually resolving, or by calling `invalid_resolve()` # Resolve by returning the previous / next valid moment in time invalid_resolve(y_plus, invalid = "previous") invalid_resolve(y_plus, invalid = "next") # Manually resolve by setting to the last day of the month invalid <- invalid_detect(y_plus) y_plus[invalid] <- set_day(y_plus[invalid], "last") y_plus } clock/man/as_year_day.Rd0000644000176200001440000000146514427270231014677 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gregorian-year-day.R \name{as_year_day} \alias{as_year_day} \title{Convert to year-day} \usage{ as_year_day(x, ...) } \arguments{ \item{x}{\verb{[vector]} A vector to convert to year-day.} \item{...}{These dots are for future extensions and must be empty.} } \value{ A year-day vector. } \description{ \code{as_year_day()} converts a vector to the year-day calendar. Time points, Dates, POSIXct, and other calendars can all be converted to year-day. } \examples{ # From Date as_year_day(as.Date("2019-05-01")) # From POSIXct, which assumes that the naive time is what should be converted as_year_day(as.POSIXct("2019-05-01 02:30:30", "America/New_York")) # From other calendars as_year_day(year_quarter_day(2019, quarter = 2, day = 50)) } clock/man/is_naive_time.Rd0000644000176200001440000000076514017505732015236 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/naive-time.R \name{is_naive_time} \alias{is_naive_time} \title{Is \code{x} a naive-time?} \usage{ is_naive_time(x) } \arguments{ \item{x}{\verb{[object]} An object.} } \value{ \code{TRUE} if \code{x} inherits from \code{"clock_naive_time"}, otherwise \code{FALSE}. } \description{ This function determines if the input is a naive-time object. } \examples{ is_naive_time(1) is_naive_time(as_naive_time(duration_days(1))) } clock/man/duration_cast.Rd0000644000176200001440000000356614017505732015264 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/duration.R \name{duration_cast} \alias{duration_cast} \title{Cast a duration between precisions} \usage{ duration_cast(x, precision) } \arguments{ \item{x}{\verb{[clock_duration]} A duration.} \item{precision}{\verb{[character(1)]} A precision. One of: \itemize{ \item \code{"year"} \item \code{"quarter"} \item \code{"month"} \item \code{"week"} \item \code{"day"} \item \code{"hour"} \item \code{"minute"} \item \code{"second"} \item \code{"millisecond"} \item \code{"microsecond"} \item \code{"nanosecond"} }} } \value{ \code{x} cast to the new \code{precision}. } \description{ Casting is one way to change a duration's precision. Casting to a less precise precision will completely drop information that is more precise than the precision that you are casting to. It does so in a way that makes it round towards zero. Casting to a more precise precision is done through a multiplication by a conversion factor between the current precision and the new precision. } \details{ When you want to change to a less precise precision, you often want \code{\link[=duration_floor]{duration_floor()}} instead of \code{duration_cast()}, as that rounds towards negative infinity, which is generally the desired behavior when working with time points (especially ones pre-1970, which are stored as negative durations). } \examples{ x <- duration_seconds(c(86401, -86401)) # Casting rounds towards 0 cast <- duration_cast(x, "day") cast # Flooring rounds towards negative infinity floor <- duration_floor(x, "day") floor # Flooring is generally more useful when working with time points, # note that the cast ends up rounding the pre-1970 date up to the next # day, while the post-1970 date is rounded down. as_sys_time(x) as_sys_time(cast) as_sys_time(floor) # Casting to a more precise precision duration_cast(x, "millisecond") } clock/man/date_seq.Rd0000644000176200001440000000264414162373152014206 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/date.R \name{date_seq} \alias{date_seq} \title{Sequences: date and date-time} \usage{ date_seq(from, ..., to = NULL, by = NULL, total_size = NULL) } \arguments{ \item{from}{\verb{[Date(1) / POSIXct(1) / POSIXlt(1)]} A date or date-time to start the sequence from.} \item{...}{These dots are for future extensions and must be empty.} \item{to}{\verb{[Date(1) / POSIXct(1) / POSIXlt(1) / NULL]} A date or date-time to stop the sequence at. \code{to} is only included in the result if the resulting sequence divides the distance between \code{from} and \code{to} exactly.} \item{by}{\verb{[integer(1) / clock_duration(1) / NULL]} The unit to increment the sequence by.} \item{total_size}{\verb{[positive integer(1) / NULL]} The size of the resulting sequence. If specified alongside \code{to}, this must generate a non-fractional sequence between \code{from} and \code{to}.} } \value{ A date or date-time vector. } \description{ \code{date_seq()} generates a date (Date) or date-time (POSIXct/POSIXlt) sequence. There are separate help pages for generating sequences for dates and date-times: \itemize{ \item \link[=date-sequence]{dates (Date)} \item \link[=posixt-sequence]{date-times (POSIXct/POSIXlt)} } } \examples{ # See method specific documentation for more examples x <- as.Date("2019-01-01") date_seq(x, by = duration_months(2), total_size = 20) } clock/man/date_count_between.Rd0000644000176200001440000000412114151750124016243 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/date.R \name{date_count_between} \alias{date_count_between} \title{Counting: date and date-time} \usage{ date_count_between(start, end, precision, ..., n = 1L) } \arguments{ \item{start, end}{\verb{[Date / POSIXct / POSIXlt]} A pair of date or date-time vectors. These will be recycled to their common size.} \item{precision}{\verb{[character(1)]} A precision. Allowed precisions are dependent on the calendar used.} \item{...}{These dots are for future extensions and must be empty.} \item{n}{\verb{[positive integer(1)]} A single positive integer specifying a multiple of \code{precision} to use.} } \value{ An integer representing the number of \code{precision} units between \code{start} and \code{end}. } \description{ \code{date_count_between()} counts the number of \code{precision} units between \code{start} and \code{end} (i.e., the number of years or months or hours). This count corresponds to the \emph{whole number} of units, and will never return a fractional value. This is suitable for, say, computing the whole number of years or months between two dates, accounting for the day and time of day. There are separate help pages for counting for dates and date-times: \itemize{ \item \link[=date-count-between]{dates (Date)} \item \link[=posixt-count-between]{date-times (POSIXct/POSIXlt)} } } \section{Comparison Direction}{ The computed count has the property that if \code{start <= end}, then \verb{start + <= end}. Similarly, if \code{start >= end}, then \verb{start + >= end}. In other words, the comparison direction between \code{start} and \code{end} will never change after adding the count to \code{start}. This makes this function useful for repeated count computations at increasingly fine precisions. } \examples{ # See method specific documentation for more examples start <- date_parse("2000-05-05") end <- date_parse(c("2020-05-04", "2020-05-06")) # Age in years date_count_between(start, end, "year") # Number of "whole" months between these dates date_count_between(start, end, "month") } clock/man/seq.clock_year_month_weekday.Rd0000644000176200001440000000424214162373152020235 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gregorian-year-month-weekday.R \name{seq.clock_year_month_weekday} \alias{seq.clock_year_month_weekday} \title{Sequences: year-month-weekday} \usage{ \method{seq}{clock_year_month_weekday}(from, to = NULL, by = NULL, length.out = NULL, along.with = NULL, ...) } \arguments{ \item{from}{\verb{[clock_year_month_weekday(1)]} A \code{"year"} or \code{"month"} precision year-month-weekday to start the sequence from. \code{from} is always included in the result.} \item{to}{\verb{[clock_year_month_weekday(1) / NULL]} A \code{"year"} or \code{"month"} precision year-month-weekday to stop the sequence at. \code{to} is cast to the type of \code{from}. \code{to} is only included in the result if the resulting sequence divides the distance between \code{from} and \code{to} exactly.} \item{by}{\verb{[integer(1) / clock_duration(1) / NULL]} The unit to increment the sequence by. If \code{by} is an integer, it is transformed into a duration with the precision of \code{from}. If \code{by} is a duration, it is cast to the type of \code{from}.} \item{length.out}{\verb{[positive integer(1) / NULL]} The length of the resulting sequence. If specified, \code{along.with} must be \code{NULL}.} \item{along.with}{\verb{[vector / NULL]} A vector who's length determines the length of the resulting sequence. Equivalent to \code{length.out = vec_size(along.with)}. If specified, \code{length.out} must be \code{NULL}.} \item{...}{These dots are for future extensions and must be empty.} } \value{ A sequence with the type of \code{from}. } \description{ This is a year-month-weekday method for the \code{\link[=seq]{seq()}} generic. Sequences can only be generated for \code{"year"} and \code{"month"} precision year-month-weekday vectors. When calling \code{seq()}, exactly two of the following must be specified: \itemize{ \item \code{to} \item \code{by} \item Either \code{length.out} or \code{along.with} } } \examples{ # Monthly sequence x <- seq(year_month_weekday(2019, 1), year_month_weekday(2020, 12), by = 1) x # Which we can then set the indexed weekday of set_day(x, clock_weekdays$sunday, index = "last") } clock/man/year-month-day-narrow.Rd0000644000176200001440000000265314006303002016545 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gregorian-year-month-day.R \name{year-month-day-narrow} \alias{year-month-day-narrow} \alias{calendar_narrow.clock_year_month_day} \title{Narrow: year-month-day} \usage{ \method{calendar_narrow}{clock_year_month_day}(x, precision) } \arguments{ \item{x}{\verb{[clock_year_month_day]} A year-month-day vector.} \item{precision}{\verb{[character(1)]} One of: \itemize{ \item \code{"year"} \item \code{"month"} \item \code{"day"} \item \code{"hour"} \item \code{"minute"} \item \code{"second"} \item \code{"millisecond"} \item \code{"microsecond"} \item \code{"nanosecond"} }} } \value{ \code{x} narrowed to the supplied \code{precision}. } \description{ This is a year-month-day method for the \code{\link[=calendar_narrow]{calendar_narrow()}} generic. It narrows a year-month-day vector to the specified \code{precision}. } \examples{ # Hour precision x <- year_month_day(2019, 1, 3, 4) x # Narrowed to day precision calendar_narrow(x, "day") # Or month precision calendar_narrow(x, "month") # Subsecond precision can be narrowed to second precision milli <- calendar_widen(x, "millisecond") micro <- calendar_widen(x, "microsecond") milli micro calendar_narrow(milli, "second") calendar_narrow(micro, "second") # But once you have "locked in" a subsecond precision, it can't be # narrowed to another subsecond precision try(calendar_narrow(micro, "millisecond")) } clock/man/year-week-day-widen.Rd0000644000176200001440000000216514422221153016157 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/week-year-week-day.R \name{year-week-day-widen} \alias{year-week-day-widen} \alias{calendar_widen.clock_year_week_day} \title{Widen: year-week-day} \usage{ \method{calendar_widen}{clock_year_week_day}(x, precision) } \arguments{ \item{x}{\verb{[clock_year_week_day]} A year-week-day vector.} \item{precision}{\verb{[character(1)]} One of: \itemize{ \item \code{"year"} \item \code{"week"} \item \code{"day"} \item \code{"hour"} \item \code{"minute"} \item \code{"second"} \item \code{"millisecond"} \item \code{"microsecond"} \item \code{"nanosecond"} }} } \value{ \code{x} widened to the supplied \code{precision}. } \description{ This is a year-week-day method for the \code{\link[=calendar_widen]{calendar_widen()}} generic. It widens a year-week-day vector to the specified \code{precision}. } \examples{ # Week precision x <- year_week_day(2019, 1, start = clock_weekdays$monday) x # Widen to day precision # In this calendar, the first day of the week is a Monday calendar_widen(x, "day") # Or second precision sec <- calendar_widen(x, "second") sec } clock/man/posixt-setters.Rd0000644000176200001440000001470614007060667015442 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/posixt.R \name{posixt-setters} \alias{posixt-setters} \alias{set_year.POSIXt} \alias{set_month.POSIXt} \alias{set_day.POSIXt} \alias{set_hour.POSIXt} \alias{set_minute.POSIXt} \alias{set_second.POSIXt} \title{Setters: date-time} \usage{ \method{set_year}{POSIXt}(x, value, ..., invalid = NULL, nonexistent = NULL, ambiguous = x) \method{set_month}{POSIXt}(x, value, ..., invalid = NULL, nonexistent = NULL, ambiguous = x) \method{set_day}{POSIXt}(x, value, ..., invalid = NULL, nonexistent = NULL, ambiguous = x) \method{set_hour}{POSIXt}(x, value, ..., invalid = NULL, nonexistent = NULL, ambiguous = x) \method{set_minute}{POSIXt}(x, value, ..., invalid = NULL, nonexistent = NULL, ambiguous = x) \method{set_second}{POSIXt}(x, value, ..., invalid = NULL, nonexistent = NULL, ambiguous = x) } \arguments{ \item{x}{\verb{[POSIXct / POSIXlt]} A date-time vector.} \item{value}{\verb{[integer / "last"]} The value to set the component to. For \code{set_day()}, this can also be \code{"last"} to set the day to the last day of the month.} \item{...}{These dots are for future extensions and must be empty.} \item{invalid}{\verb{[character(1) / NULL]} One of the following invalid date resolution strategies: \itemize{ \item \code{"previous"}: The previous valid instant in time. \item \code{"previous-day"}: The previous valid day in time, keeping the time of day. \item \code{"next"}: The next valid instant in time. \item \code{"next-day"}: The next valid day in time, keeping the time of day. \item \code{"overflow"}: Overflow by the number of days that the input is invalid by. Time of day is dropped. \item \code{"overflow-day"}: Overflow by the number of days that the input is invalid by. Time of day is kept. \item \code{"NA"}: Replace invalid dates with \code{NA}. \item \code{"error"}: Error on invalid dates. } Using either \code{"previous"} or \code{"next"} is generally recommended, as these two strategies maintain the \emph{relative ordering} between elements of the input. If \code{NULL}, defaults to \code{"error"}. If \code{getOption("clock.strict")} is \code{TRUE}, \code{invalid} must be supplied and cannot be \code{NULL}. This is a convenient way to make production code robust to invalid dates.} \item{nonexistent}{\verb{[character / NULL]} One of the following nonexistent time resolution strategies, allowed to be either length 1, or the same length as the input: \itemize{ \item \code{"roll-forward"}: The next valid instant in time. \item \code{"roll-backward"}: The previous valid instant in time. \item \code{"shift-forward"}: Shift the nonexistent time forward by the size of the daylight saving time gap. \item \verb{"shift-backward}: Shift the nonexistent time backward by the size of the daylight saving time gap. \item \code{"NA"}: Replace nonexistent times with \code{NA}. \item \code{"error"}: Error on nonexistent times. } Using either \code{"roll-forward"} or \code{"roll-backward"} is generally recommended over shifting, as these two strategies maintain the \emph{relative ordering} between elements of the input. If \code{NULL}, defaults to \code{"error"}. If \code{getOption("clock.strict")} is \code{TRUE}, \code{nonexistent} must be supplied and cannot be \code{NULL}. This is a convenient way to make production code robust to nonexistent times.} \item{ambiguous}{\verb{[character / zoned_time / POSIXct / list(2) / NULL]} One of the following ambiguous time resolution strategies, allowed to be either length 1, or the same length as the input: \itemize{ \item \code{"earliest"}: Of the two possible times, choose the earliest one. \item \code{"latest"}: Of the two possible times, choose the latest one. \item \code{"NA"}: Replace ambiguous times with \code{NA}. \item \code{"error"}: Error on ambiguous times. } Alternatively, \code{ambiguous} is allowed to be a zoned_time (or POSIXct) that is either length 1, or the same length as the input. If an ambiguous time is encountered, the zoned_time is consulted. If the zoned_time corresponds to a naive_time that is also ambiguous \emph{and} uses the same daylight saving time transition point as the original ambiguous time, then the offset of the zoned_time is used to resolve the ambiguity. If the ambiguity cannot be resolved by consulting the zoned_time, then this method falls back to \code{NULL}. Finally, \code{ambiguous} is allowed to be a list of size 2, where the first element of the list is a zoned_time (as described above), and the second element of the list is an ambiguous time resolution strategy to use when the ambiguous time cannot be resolved by consulting the zoned_time. Specifying a zoned_time on its own is identical to \verb{list(, NULL)}. If \code{NULL}, defaults to \code{"error"}. If \code{getOption("clock.strict")} is \code{TRUE}, \code{ambiguous} must be supplied and cannot be \code{NULL}. Additionally, \code{ambiguous} cannot be specified as a zoned_time on its own, as this implies \code{NULL} for ambiguous times that the zoned_time cannot resolve. Instead, it must be specified as a list alongside an ambiguous time resolution strategy as described above. This is a convenient way to make production code robust to ambiguous times.} } \value{ \code{x} with the component set. } \description{ These are POSIXct/POSIXlt methods for the \link[=clock-setters]{setter generics}. \itemize{ \item \code{set_year()} sets the year. \item \code{set_month()} sets the month of the year. Valid values are in the range of \verb{[1, 12]}. \item \code{set_day()} sets the day of the month. Valid values are in the range of \verb{[1, 31]}. \item There are sub-daily setters for setting more precise components, up to a precision of seconds. } } \examples{ x <- as.POSIXct("2019-02-01", tz = "America/New_York") # Set the day set_day(x, 12:14) # Set to the "last" day of the month set_day(x, "last") # You cannot set a date-time to an invalid date like you can with # a year-month-day. Instead, the default strategy is to error. try(set_day(x, 31)) set_day(as_year_month_day(x), 31) # You can resolve these issues while setting the day by specifying # an invalid date resolution strategy with `invalid` set_day(x, 31, invalid = "previous") y <- as.POSIXct("2020-03-08 01:30:00", tz = "America/New_York") # Nonexistent and ambiguous times must be resolved immediately when # working with R's native date-time types. An error is thrown by default. try(set_hour(y, 2)) set_hour(y, 2, nonexistent = "roll-forward") set_hour(y, 2, nonexistent = "roll-backward") } clock/man/naive_time_parse.Rd0000644000176200001440000003023214134027754015730 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/naive-time.R \name{naive_time_parse} \alias{naive_time_parse} \title{Parsing: naive-time} \usage{ naive_time_parse( x, ..., format = NULL, precision = "second", locale = clock_locale() ) } \arguments{ \item{x}{\verb{[character]} A character vector to parse.} \item{...}{These dots are for future extensions and must be empty.} \item{format}{\verb{[character / NULL]} A format string. A combination of the following commands, or \code{NULL}, in which case a default format string is used. A vector of multiple format strings can be supplied. They will be tried in the order they are provided. \strong{Year} \itemize{ \item \verb{\%C}: The century as a decimal number. The modified command \verb{\%NC} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. \item \verb{\%y}: The last two decimal digits of the year. If the century is not otherwise specified (e.g. with \verb{\%C}), values in the range \verb{[69 - 99]} are presumed to refer to the years \verb{[1969 - 1999]}, and values in the range \verb{[00 - 68]} are presumed to refer to the years \verb{[2000 - 2068]}. The modified command \verb{\%Ny}, where \code{N} is a positive decimal integer, specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. \item \verb{\%Y}: The year as a decimal number. The modified command \verb{\%NY} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{4}. Leading zeroes are permitted but not required. } \strong{Month} \itemize{ \item \verb{\%b}, \verb{\%B}, \verb{\%h}: The \code{locale}'s full or abbreviated case-insensitive month name. \item \verb{\%m}: The month as a decimal number. January is \code{1}. The modified command \verb{\%Nm} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. } \strong{Day} \itemize{ \item \verb{\%d}, \verb{\%e}: The day of the month as a decimal number. The modified command \verb{\%Nd} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. } \strong{Day of the week} \itemize{ \item \verb{\%a}, \verb{\%A}: The \code{locale}'s full or abbreviated case-insensitive weekday name. \item \verb{\%w}: The weekday as a decimal number (\code{0-6}), where Sunday is \code{0}. The modified command \verb{\%Nw} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{1}. Leading zeroes are permitted but not required. } \strong{ISO 8601 week-based year} \itemize{ \item \verb{\%g}: The last two decimal digits of the ISO week-based year. The modified command \verb{\%Ng} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. \item \verb{\%G}: The ISO week-based year as a decimal number. The modified command \verb{\%NG} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{4}. Leading zeroes are permitted but not required. \item \verb{\%V}: The ISO week-based week number as a decimal number. The modified command \verb{\%NV} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. \item \verb{\%u}: The ISO weekday as a decimal number (\code{1-7}), where Monday is \code{1}. The modified command \verb{\%Nu} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{1}. Leading zeroes are permitted but not required. } \strong{Week of the year} \itemize{ \item \verb{\%U}: The week number of the year as a decimal number. The first Sunday of the year is the first day of week \code{01}. Days of the same year prior to that are in week \code{00}. The modified command \verb{\%NU} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. \item \verb{\%W}: The week number of the year as a decimal number. The first Monday of the year is the first day of week \code{01}. Days of the same year prior to that are in week \code{00}. The modified command \verb{\%NW} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. } \strong{Day of the year} \itemize{ \item \verb{\%j}: The day of the year as a decimal number. January 1 is \code{1}. The modified command \verb{\%Nj} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{3}. Leading zeroes are permitted but not required. } \strong{Date} \itemize{ \item \verb{\%D}, \verb{\%x}: Equivalent to \verb{\%m/\%d/\%y}. \item \verb{\%F}: Equivalent to \verb{\%Y-\%m-\%d}. If modified with a width (like \verb{\%NF}), the width is applied to only \verb{\%Y}. } \strong{Time of day} \itemize{ \item \verb{\%H}: The hour (24-hour clock) as a decimal number. The modified command \verb{\%NH} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. \item \verb{\%I}: The hour (12-hour clock) as a decimal number. The modified command \verb{\%NI} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. \item \verb{\%M}: The minutes as a decimal number. The modified command \verb{\%NM} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. \item \verb{\%S}: The seconds as a decimal number. Leading zeroes are permitted but not required. If encountered, the \code{locale} determines the decimal point character. Generally, the maximum number of characters to read is determined by the precision that you are parsing at. For example, a precision of \code{"second"} would read a maximum of 2 characters, while a precision of \code{"millisecond"} would read a maximum of 6 (2 for the values before the decimal point, 1 for the decimal point, and 3 for the values after it). The modified command \verb{\%NS}, where \code{N} is a positive decimal integer, can be used to exactly specify the maximum number of characters to read. This is only useful if you happen to have seconds with more than 1 leading zero. \item \verb{\%p}: The \code{locale}'s equivalent of the AM/PM designations associated with a 12-hour clock. The command \verb{\%I} must precede \verb{\%p} in the format string. \item \verb{\%R}: Equivalent to \verb{\%H:\%M}. \item \verb{\%T}, \verb{\%X}: Equivalent to \verb{\%H:\%M:\%S}. \item \verb{\%r}: Equivalent to \verb{\%I:\%M:\%S \%p}. } \strong{Time zone} \itemize{ \item \verb{\%z}: The offset from UTC in the format \verb{[+|-]hh[mm]}. For example \code{-0430} refers to 4 hours 30 minutes behind UTC. And \code{04} refers to 4 hours ahead of UTC. The modified command \verb{\%Ez} parses a \code{:} between the hours and minutes and leading zeroes on the hour field are optional: \verb{[+|-]h[h][:mm]}. For example \code{-04:30} refers to 4 hours 30 minutes behind UTC. And \code{4} refers to 4 hours ahead of UTC. \item \verb{\%Z}: The full time zone name or the time zone abbreviation, depending on the function being used. A single word is parsed. This word can only contain characters that are alphanumeric, or one of \code{'_'}, \code{'/'}, \code{'-'} or \code{'+'}. } \strong{Miscellaneous} \itemize{ \item \verb{\%c}: A date and time representation. Equivalent to \verb{\%a \%b \%d \%H:\%M:\%S \%Y}. \item \code{\%\%}: A \verb{\%} character. \item \verb{\%n}: Matches one white space character. \verb{\%n}, \verb{\%t}, and a space can be combined to match a wide range of white-space patterns. For example \code{"\%n "} matches one or more white space characters, and \code{"\%n\%t\%t"} matches one to three white space characters. \item \verb{\%t}: Matches zero or one white space characters. }} \item{precision}{\verb{[character(1)]} A precision for the resulting time point. One of: \itemize{ \item \code{"day"} \item \code{"hour"} \item \code{"minute"} \item \code{"second"} \item \code{"millisecond"} \item \code{"microsecond"} \item \code{"nanosecond"} } Setting the \code{precision} determines how much information \verb{\%S} attempts to parse.} \item{locale}{\verb{[clock_locale]} A locale object created from \code{\link[=clock_locale]{clock_locale()}}.} } \value{ A naive-time. } \description{ \code{naive_time_parse()} is a parser into a naive-time. \code{naive_time_parse()} is useful when you have date-time strings like \code{"2020-01-01T01:04:30"}. If there is no attached UTC offset or time zone name, then parsing this string as a naive-time is your best option. If you know that this string should be interpreted in a specific time zone, parse as a naive-time, then use \code{\link[=as_zoned_time]{as_zoned_time()}}. The default options assume that \code{x} should be parsed at second precision, using a \code{format} string of \code{"\%Y-\%m-\%dT\%H:\%M:\%S"}. This matches the default result from calling \code{format()} on a naive-time. \emph{\code{naive_time_parse()} ignores both the \verb{\%z} and \verb{\%Z} commands.} If your date-time strings contain a full time zone name and a UTC offset, use \code{\link[=zoned_time_parse_complete]{zoned_time_parse_complete()}}. If they contain a time zone abbreviation, use \code{\link[=zoned_time_parse_abbrev]{zoned_time_parse_abbrev()}}. If your date-time strings contain a UTC offset, but not a full time zone name, use \code{\link[=sys_time_parse]{sys_time_parse()}}. } \section{Full Precision Parsing}{ It is highly recommended to parse all of the information in the date-time string into a type at least as precise as the string. For example, if your string has fractional seconds, but you only require seconds, specify a sub-second \code{precision}, then round to seconds manually using whatever convention is appropriate for your use case. Parsing such a string directly into a second precision result is ambiguous and undefined, and is unlikely to work as you might expect. } \examples{ naive_time_parse("2020-01-01T05:06:07") # Day precision naive_time_parse("2020-01-01", precision = "day") # Nanosecond precision, but using a day based format naive_time_parse("2020-01-01", format = "\%Y-\%m-\%d", precision = "nanosecond") # Remember that the `\%z` and `\%Z` commands are ignored entirely! naive_time_parse( "2020-01-01 -4000 America/New_York", format = "\%Y-\%m-\%d \%z \%Z" ) # --------------------------------------------------------------------------- # Fractional seconds and POSIXct # If you have a string with fractional seconds and want to convert it to # a POSIXct, remember that clock treats POSIXct as a second precision type. # Ideally, you'd use a clock type that can support fractional seconds, but # if you really want to parse it into a POSIXct, the correct way to do so # is to parse the full fractional time point with the correct `precision`, # then round to seconds using whatever convention you require, and finally # convert that to POSIXct. x <- c("2020-01-01T00:00:00.123", "2020-01-01T00:00:00.555") # First, parse string with full precision x <- naive_time_parse(x, precision = "millisecond") x # Then round to second with a floor, ceiling, or round to nearest time_point_floor(x, "second") time_point_round(x, "second") # Finally, convert to POSIXct as_date_time(time_point_round(x, "second"), zone = "UTC") } clock/man/year-month-weekday-arithmetic.Rd0000644000176200001440000000354114416031540020251 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gregorian-year-month-weekday.R \name{year-month-weekday-arithmetic} \alias{year-month-weekday-arithmetic} \alias{add_years.clock_year_month_weekday} \alias{add_quarters.clock_year_month_weekday} \alias{add_months.clock_year_month_weekday} \title{Arithmetic: year-month-weekday} \usage{ \method{add_years}{clock_year_month_weekday}(x, n, ...) \method{add_quarters}{clock_year_month_weekday}(x, n, ...) \method{add_months}{clock_year_month_weekday}(x, n, ...) } \arguments{ \item{x}{\verb{[clock_year_month_weekday]} A year-month-weekday vector.} \item{n}{\verb{[integer / clock_duration]} An integer vector to be converted to a duration, or a duration corresponding to the arithmetic function being used. This corresponds to the number of duration units to add. \code{n} may be negative to subtract units of duration.} \item{...}{These dots are for future extensions and must be empty.} } \value{ \code{x} after performing the arithmetic. } \description{ These are year-month-weekday methods for the \link[=clock-arithmetic]{arithmetic generics}. \itemize{ \item \code{add_years()} \item \code{add_quarters()} \item \code{add_months()} } Notably, \emph{you cannot add days to a year-month-weekday}. For day-based arithmetic, first convert to a time point with \code{\link[=as_naive_time]{as_naive_time()}} or \code{\link[=as_sys_time]{as_sys_time()}}. } \details{ Adding a single quarter with \code{add_quarters()} is equivalent to adding 3 months. \code{x} and \code{n} are recycled against each other using \link[vctrs:vector_recycling_rules]{tidyverse recycling rules}. } \examples{ # 2nd Friday in January, 2019 x <- year_month_weekday(2019, 1, clock_weekdays$friday, 2) x add_months(x, 1:5) # These don't necessarily correspond to the same day of the month as_year_month_day(add_months(x, 1:5)) } clock/man/calendar_month_factor.Rd0000644000176200001440000000305714007067565016742 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/calendar.R \name{calendar_month_factor} \alias{calendar_month_factor} \title{Convert a calendar to an ordered factor of month names} \usage{ calendar_month_factor(x, ..., labels = "en", abbreviate = FALSE) } \arguments{ \item{x}{\verb{[calendar]} A calendar vector.} \item{...}{These dots are for future extensions and must be empty.} \item{labels}{\verb{[clock_labels / character(1)]} Character representations of localized weekday names, month names, and AM/PM names. Either the language code as string (passed on to \code{\link[=clock_labels_lookup]{clock_labels_lookup()}}), or an object created by \code{\link[=clock_labels]{clock_labels()}}.} \item{abbreviate}{\verb{[logical(1)]} If \code{TRUE}, the abbreviated month names from \code{labels} will be used. If \code{FALSE}, the full month names from \code{labels} will be used.} } \value{ An ordered factor representing the months. } \description{ \code{calendar_month_factor()} extracts the month values from a calendar and converts them to an ordered factor of month names. This can be useful in combination with ggplot2, or for modeling. This function is only relevant for calendar types that use a month field, i.e. \code{\link[=year_month_day]{year_month_day()}} and \code{\link[=year_month_weekday]{year_month_weekday()}}. The calendar type must have at least month precision. } \examples{ x <- year_month_day(2019, 1:12) calendar_month_factor(x) calendar_month_factor(x, abbreviate = TRUE) calendar_month_factor(x, labels = "fr") } clock/man/as-zoned-time-Date.Rd0000644000176200001440000001143314073622034015741 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/date.R \name{as-zoned-time-Date} \alias{as-zoned-time-Date} \alias{as_zoned_time.Date} \title{Convert to a zoned-time from a date} \usage{ \method{as_zoned_time}{Date}(x, zone, ..., nonexistent = NULL, ambiguous = NULL) } \arguments{ \item{x}{\verb{[Date]} A Date.} \item{zone}{\verb{[character(1)]} The zone to convert to.} \item{...}{These dots are for future extensions and must be empty.} \item{nonexistent}{\verb{[character / NULL]} One of the following nonexistent time resolution strategies, allowed to be either length 1, or the same length as the input: \itemize{ \item \code{"roll-forward"}: The next valid instant in time. \item \code{"roll-backward"}: The previous valid instant in time. \item \code{"shift-forward"}: Shift the nonexistent time forward by the size of the daylight saving time gap. \item \verb{"shift-backward}: Shift the nonexistent time backward by the size of the daylight saving time gap. \item \code{"NA"}: Replace nonexistent times with \code{NA}. \item \code{"error"}: Error on nonexistent times. } Using either \code{"roll-forward"} or \code{"roll-backward"} is generally recommended over shifting, as these two strategies maintain the \emph{relative ordering} between elements of the input. If \code{NULL}, defaults to \code{"error"}. If \code{getOption("clock.strict")} is \code{TRUE}, \code{nonexistent} must be supplied and cannot be \code{NULL}. This is a convenient way to make production code robust to nonexistent times.} \item{ambiguous}{\verb{[character / zoned_time / POSIXct / list(2) / NULL]} One of the following ambiguous time resolution strategies, allowed to be either length 1, or the same length as the input: \itemize{ \item \code{"earliest"}: Of the two possible times, choose the earliest one. \item \code{"latest"}: Of the two possible times, choose the latest one. \item \code{"NA"}: Replace ambiguous times with \code{NA}. \item \code{"error"}: Error on ambiguous times. } Alternatively, \code{ambiguous} is allowed to be a zoned_time (or POSIXct) that is either length 1, or the same length as the input. If an ambiguous time is encountered, the zoned_time is consulted. If the zoned_time corresponds to a naive_time that is also ambiguous \emph{and} uses the same daylight saving time transition point as the original ambiguous time, then the offset of the zoned_time is used to resolve the ambiguity. If the ambiguity cannot be resolved by consulting the zoned_time, then this method falls back to \code{NULL}. Finally, \code{ambiguous} is allowed to be a list of size 2, where the first element of the list is a zoned_time (as described above), and the second element of the list is an ambiguous time resolution strategy to use when the ambiguous time cannot be resolved by consulting the zoned_time. Specifying a zoned_time on its own is identical to \verb{list(, NULL)}. If \code{NULL}, defaults to \code{"error"}. If \code{getOption("clock.strict")} is \code{TRUE}, \code{ambiguous} must be supplied and cannot be \code{NULL}. Additionally, \code{ambiguous} cannot be specified as a zoned_time on its own, as this implies \code{NULL} for ambiguous times that the zoned_time cannot resolve. Instead, it must be specified as a list alongside an ambiguous time resolution strategy as described above. This is a convenient way to make production code robust to ambiguous times.} } \value{ A zoned-time. } \description{ This is a Date method for the \code{\link[=as_zoned_time]{as_zoned_time()}} generic. clock assumes that Dates are \emph{naive} date-time types. Like naive-times, they have a yet-to-be-specified time zone. This method allows you to specify that time zone, keeping the printed time. If possible, the time will be set to midnight (see Details for the rare case in which this is not possible). } \details{ In the rare instance that the specified time zone does not contain a date-time at midnight due to daylight saving time, \code{nonexistent} can be used to resolve the issue. Similarly, if there are two possible midnight times due to a daylight saving time fallback, \code{ambiguous} can be used. } \examples{ x <- as.Date("2019-01-01") # The resulting zoned-times have the same printed time, but are in # different time zones as_zoned_time(x, "UTC") as_zoned_time(x, "America/New_York") # Converting Date -> zoned-time is the same as naive-time -> zoned-time x <- as_naive_time(year_month_day(2019, 1, 1)) as_zoned_time(x, "America/New_York") # In Asia/Beirut, there was a DST gap from # 2021-03-27 23:59:59 -> 2021-03-28 01:00:00, # skipping the 0th hour entirely. This means there is no midnight value. x <- as.Date("2021-03-28") try(as_zoned_time(x, "Asia/Beirut")) # To resolve this, set a `nonexistent` time resolution strategy as_zoned_time(x, "Asia/Beirut", nonexistent = "roll-forward") } clock/man/duration_spanning_seq.Rd0000644000176200001440000000155014425175541017012 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/duration.R \name{duration_spanning_seq} \alias{duration_spanning_seq} \title{Spanning sequence: duration} \usage{ duration_spanning_seq(x) } \arguments{ \item{x}{\verb{[clock_duration]} A duration vector.} } \value{ A sequence along \verb{[min(x), max(x)]}. } \description{ \code{duration_spanning_seq()} generates a regular sequence along the span of \code{x}, i.e. along \verb{[min(x), max(x)]}. } \details{ Missing values are automatically removed before the sequence is generated. If you need more precise sequence generation, call \code{\link[=range]{range()}} and \code{\link[=seq]{seq()}} directly. } \examples{ x <- duration_days(c(1, 5, 2)) duration_spanning_seq(x) # Missing values are removed before the sequence is created x <- vctrs::vec_c(NA, x, NA) duration_spanning_seq(x) } clock/man/seq.clock_year_day.Rd0000644000176200001440000000424414162373152016156 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gregorian-year-day.R \name{seq.clock_year_day} \alias{seq.clock_year_day} \title{Sequences: year-day} \usage{ \method{seq}{clock_year_day}(from, to = NULL, by = NULL, length.out = NULL, along.with = NULL, ...) } \arguments{ \item{from}{\verb{[clock_year_day(1)]} A \code{"year"} precision year-day to start the sequence from. \code{from} is always included in the result.} \item{to}{\verb{[clock_year_day(1) / NULL]} A \code{"year"} precision year-day to stop the sequence at. \code{to} is cast to the type of \code{from}. \code{to} is only included in the result if the resulting sequence divides the distance between \code{from} and \code{to} exactly.} \item{by}{\verb{[integer(1) / clock_duration(1) / NULL]} The unit to increment the sequence by. If \code{by} is an integer, it is transformed into a duration with the precision of \code{from}. If \code{by} is a duration, it is cast to the type of \code{from}.} \item{length.out}{\verb{[positive integer(1) / NULL]} The length of the resulting sequence. If specified, \code{along.with} must be \code{NULL}.} \item{along.with}{\verb{[vector / NULL]} A vector who's length determines the length of the resulting sequence. Equivalent to \code{length.out = vec_size(along.with)}. If specified, \code{length.out} must be \code{NULL}.} \item{...}{These dots are for future extensions and must be empty.} } \value{ A sequence with the type of \code{from}. } \description{ This is a year-day method for the \code{\link[=seq]{seq()}} generic. Sequences can only be generated for \code{"year"} precision year-day vectors. When calling \code{seq()}, exactly two of the following must be specified: \itemize{ \item \code{to} \item \code{by} \item Either \code{length.out} or \code{along.with} } } \examples{ # Yearly sequence x <- seq(year_day(2020), year_day(2040), by = 2) x # Which we can then set the day of to get a sequence of end-of-year values set_day(x, "last") # Daily sequences are not allowed. Use a naive-time for this instead. try(seq(year_day(2019, 1), by = 2, length.out = 2)) as_year_day(seq(as_naive_time(year_day(2019, 1)), by = 2, length.out = 2)) } clock/man/date-and-date-time-boundary.Rd0000644000176200001440000000255514075602251017565 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/date.R \name{date-and-date-time-boundary} \alias{date-and-date-time-boundary} \alias{date_start} \alias{date_end} \title{Boundaries: date and date-time} \usage{ date_start(x, precision, ...) date_end(x, precision, ...) } \arguments{ \item{x}{\verb{[Date / POSIXct / POSIXlt]} A date or date-time vector.} \item{precision}{\verb{[character(1)]} A precision. Allowed precisions are dependent on the input used.} \item{...}{These dots are for future extensions and must be empty.} } \value{ \code{x} but with some components altered to be at the boundary value. } \description{ \itemize{ \item \code{date_start()} computes the date at the start of a particular \code{precision}, such as the "start of the year". \item \code{date_end()} computes the date at the end of a particular \code{precision}, such as the "end of the month". } There are separate help pages for computing boundaries for dates and date-times: \itemize{ \item \link[=date-boundary]{dates (Date)} \item \link[=posixt-boundary]{date-times (POSIXct/POSIXlt)} } } \examples{ # See type specific documentation for more examples x <- date_build(2019, 2:4) date_end(x, "month") x <- date_time_build(2019, 2:4, 3:5, 4, 5, zone = "America/New_York") # Note that the hour, minute, and second components are also adjusted date_end(x, "month") } clock/man/date-formatting.Rd0000644000176200001440000001307514073622034015503 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/date.R \name{date-formatting} \alias{date-formatting} \alias{date_format.Date} \title{Formatting: date} \usage{ \method{date_format}{Date}(x, ..., format = NULL, locale = clock_locale()) } \arguments{ \item{x}{\verb{[Date]} A date vector.} \item{...}{These dots are for future extensions and must be empty.} \item{format}{\verb{[character(1) / NULL]} If \code{NULL}, a default format is used, which depends on the type of the input. Otherwise, a format string which is a combination of: \strong{Year} \itemize{ \item \verb{\%C}: The year divided by 100 using floored division. If the result is a single decimal digit, it is prefixed with \code{0}. \item \verb{\%y}: The last two decimal digits of the year. If the result is a single digit it is prefixed by \code{0}. \item \verb{\%Y}: The year as a decimal number. If the result is less than four digits it is left-padded with \code{0} to four digits. } \strong{Month} \itemize{ \item \verb{\%b}, \verb{\%h}: The \code{locale}'s abbreviated month name. \item \verb{\%B}: The \code{locale}'s full month name. \item \verb{\%m}: The month as a decimal number. January is \code{01}. If the result is a single digit, it is prefixed with \code{0}. } \strong{Day} \itemize{ \item \verb{\%d}: The day of month as a decimal number. If the result is a single decimal digit, it is prefixed with \code{0}. } \strong{Day of the week} \itemize{ \item \verb{\%a}: The \code{locale}'s abbreviated weekday name. \item \verb{\%A}: The \code{locale}'s full weekday name. \item \verb{\%w}: The weekday as a decimal number (\code{0-6}), where Sunday is \code{0}. } \strong{ISO 8601 week-based year} \itemize{ \item \verb{\%g}: The last two decimal digits of the ISO week-based year. If the result is a single digit it is prefixed by \code{0}. \item \verb{\%G}: The ISO week-based year as a decimal number. If the result is less than four digits it is left-padded with \code{0} to four digits. \item \verb{\%V}: The ISO week-based week number as a decimal number. If the result is a single digit, it is prefixed with \code{0}. \item \verb{\%u}: The ISO weekday as a decimal number (\code{1-7}), where Monday is \code{1}. } \strong{Week of the year} \itemize{ \item \verb{\%U}: The week number of the year as a decimal number. The first Sunday of the year is the first day of week \code{01}. Days of the same year prior to that are in week \code{00}. If the result is a single digit, it is prefixed with \code{0}. \item \verb{\%W}: The week number of the year as a decimal number. The first Monday of the year is the first day of week \code{01}. Days of the same year prior to that are in week \code{00}. If the result is a single digit, it is prefixed with \code{0}. } \strong{Day of the year} \itemize{ \item \verb{\%j}: The day of the year as a decimal number. January 1 is \code{001}. If the result is less than three digits, it is left-padded with \code{0} to three digits. } \strong{Date} \itemize{ \item \verb{\%D}, \verb{\%x}: Equivalent to \verb{\%m/\%d/\%y}. \item \verb{\%F}: Equivalent to \verb{\%Y-\%m-\%d}. } \strong{Time of day} \itemize{ \item \verb{\%H}: The hour (24-hour clock) as a decimal number. If the result is a single digit, it is prefixed with \code{0}. \item \verb{\%I}: The hour (12-hour clock) as a decimal number. If the result is a single digit, it is prefixed with \code{0}. \item \verb{\%M}: The minute as a decimal number. If the result is a single digit, it is prefixed with \code{0}. \item \verb{\%S}: Seconds as a decimal number. Fractional seconds are printed at the precision of the input. The character for the decimal point is localized according to \code{locale}. \item \verb{\%p}: The \code{locale}'s equivalent of the AM/PM designations associated with a 12-hour clock. \item \verb{\%R}: Equivalent to \verb{\%H:\%M}. \item \verb{\%T}, \verb{\%X}: Equivalent to \verb{\%H:\%M:\%S}. \item \verb{\%r}: Nearly equivalent to \verb{\%I:\%M:\%S \%p}, but seconds are always printed at second precision. } \strong{Time zone} \itemize{ \item \verb{\%z}: The offset from UTC in the ISO 8601 format. For example \code{-0430} refers to 4 hours 30 minutes behind UTC. If the offset is zero, \code{+0000} is used. The modified command \verb{\%Ez} inserts a \code{:} between the hour and minutes, like \code{-04:30}. \item \verb{\%Z}: The full time zone name. If \code{abbreviate_zone} is \code{TRUE}, the time zone abbreviation. } \strong{Miscellaneous} \itemize{ \item \verb{\%c}: A date and time representation. Similar to, but not exactly the same as, \verb{\%a \%b \%d \%H:\%M:\%S \%Y}. \item \code{\%\%}: A \verb{\%} character. \item \verb{\%n}: A newline character. \item \verb{\%t}: A horizontal-tab character. }} \item{locale}{\verb{[clock_locale]} A locale object created from \code{\link[=clock_locale]{clock_locale()}}.} } \value{ A character vector of the formatted input. } \description{ This is a Date method for the \code{\link[=date_format]{date_format()}} generic. \code{date_format()} formats a date (Date) using a \code{format} string. If \code{format} is \code{NULL}, a default format of \code{"\%Y-\%m-\%d"} is used. } \details{ Because a Date is considered to be a \emph{naive} type in clock, meaning that it currently has no implied time zone, using the \verb{\%z} or \verb{\%Z} format commands is not allowed and will result in \code{NA}. } \examples{ x <- as.Date("2019-01-01") # Default date_format(x) date_format(x, format = "year: \%Y, month: \%m, day: \%d") # With different locales date_format(x, format = "\%A, \%B \%d, \%Y") date_format(x, format = "\%A, \%B \%d, \%Y", locale = clock_locale("fr")) } clock/man/duration-arithmetic.Rd0000644000176200001440000000640314416031540016364 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/duration.R \name{duration-arithmetic} \alias{duration-arithmetic} \alias{add_years.clock_duration} \alias{add_quarters.clock_duration} \alias{add_months.clock_duration} \alias{add_weeks.clock_duration} \alias{add_days.clock_duration} \alias{add_hours.clock_duration} \alias{add_minutes.clock_duration} \alias{add_seconds.clock_duration} \alias{add_milliseconds.clock_duration} \alias{add_microseconds.clock_duration} \alias{add_nanoseconds.clock_duration} \title{Arithmetic: duration} \usage{ \method{add_years}{clock_duration}(x, n, ...) \method{add_quarters}{clock_duration}(x, n, ...) \method{add_months}{clock_duration}(x, n, ...) \method{add_weeks}{clock_duration}(x, n, ...) \method{add_days}{clock_duration}(x, n, ...) \method{add_hours}{clock_duration}(x, n, ...) \method{add_minutes}{clock_duration}(x, n, ...) \method{add_seconds}{clock_duration}(x, n, ...) \method{add_milliseconds}{clock_duration}(x, n, ...) \method{add_microseconds}{clock_duration}(x, n, ...) \method{add_nanoseconds}{clock_duration}(x, n, ...) } \arguments{ \item{x}{\verb{[clock_duration]} A duration vector.} \item{n}{\verb{[integer / clock_duration]} An integer vector to be converted to a duration, or a duration corresponding to the arithmetic function being used. This corresponds to the number of duration units to add. \code{n} may be negative to subtract units of duration.} \item{...}{These dots are for future extensions and must be empty.} } \value{ \code{x} after performing the arithmetic, possibly with a more precise precision. } \description{ These are duration methods for the \link[=clock-arithmetic]{arithmetic generics}. \itemize{ \item \code{add_years()} \item \code{add_quarters()} \item \code{add_months()} \item \code{add_weeks()} \item \code{add_days()} \item \code{add_hours()} \item \code{add_minutes()} \item \code{add_seconds()} \item \code{add_milliseconds()} \item \code{add_microseconds()} \item \code{add_nanoseconds()} } When adding to a duration using one of these functions, a second duration is created based on the function name and \code{n}. The two durations are then added together, and the precision of the result is determined as the \emph{more precise precision} of the two durations. } \details{ You can add calendrical durations to other calendrical durations, and chronological durations to other chronological durations, but you can't add a chronological duration to a calendrical duration (such as adding days and months). For more information, see the documentation on the \link[=duration-helper]{duration helper} page. \code{x} and \code{n} are recycled against each other using \link[vctrs:vector_recycling_rules]{tidyverse recycling rules}. } \examples{ x <- duration_seconds(5) # Addition in the same precision add_seconds(x, 1:10) # Addition with days, defined as 86400 seconds add_days(x, 1) # Similarly, if you start with days and add seconds, you get the common # precision of the two back, which is seconds y <- duration_days(1) add_seconds(y, 5) # But you can't add a chronological duration (days) and # a calendrical duration (months) try(add_months(y, 1)) # You can add years to a duration of months, which adds # an additional 12 months / year z <- duration_months(5) add_years(z, 1) } clock/man/date-count-between.Rd0000644000176200001440000001102114151763504016102 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/date.R \name{date-count-between} \alias{date-count-between} \alias{date_count_between.Date} \title{Counting: date} \usage{ \method{date_count_between}{Date}(start, end, precision, ..., n = 1L) } \arguments{ \item{start, end}{\verb{[Date]} A pair of date vectors. These will be recycled to their common size.} \item{precision}{\verb{[character(1)]} One of: \itemize{ \item \code{"year"} \item \code{"quarter"} \item \code{"month"} \item \code{"week"} \item \code{"day"} }} \item{...}{These dots are for future extensions and must be empty.} \item{n}{\verb{[positive integer(1)]} A single positive integer specifying a multiple of \code{precision} to use.} } \value{ An integer representing the number of \code{precision} units between \code{start} and \code{end}. } \description{ This is a Date method for the \code{\link[=date_count_between]{date_count_between()}} generic. \code{date_count_between()} counts the number of \code{precision} units between \code{start} and \code{end} (i.e., the number of years or months). This count corresponds to the \emph{whole number} of units, and will never return a fractional value. This is suitable for, say, computing the whole number of years or months between two dates, accounting for the day of the month. \emph{Calendrical based counting:} These precisions convert to a year-month-day calendar and count while in that type. \itemize{ \item \code{"year"} \item \code{"quarter"} \item \code{"month"} } \emph{Time point based counting:} These precisions convert to a time point and count while in that type. \itemize{ \item \code{"week"} \item \code{"day"} } For dates, whether a calendar or time point is used is not all that important, but is is fairly important for date-times. } \details{ \code{"quarter"} is equivalent to \code{"month"} precision with \code{n} set to \code{n * 3L}. } \section{Comparison Direction}{ The computed count has the property that if \code{start <= end}, then \verb{start + <= end}. Similarly, if \code{start >= end}, then \verb{start + >= end}. In other words, the comparison direction between \code{start} and \code{end} will never change after adding the count to \code{start}. This makes this function useful for repeated count computations at increasingly fine precisions. } \examples{ start <- date_parse("2000-05-05") end <- date_parse(c("2020-05-04", "2020-05-06")) # Age in years date_count_between(start, end, "year") # Number of "whole" months between these dates. i.e. # `2000-05-05 -> 2020-04-05` is 239 months # `2000-05-05 -> 2020-05-05` is 240 months # Since 2020-05-04 occurs before the 5th of that month, # it gets a count of 239 date_count_between(start, end, "month") # Number of "whole" quarters between (same as `"month"` with `n * 3`) date_count_between(start, end, "quarter") date_count_between(start, end, "month", n = 3) # Number of days between date_count_between(start, end, "day") # Number of full 3 day periods between these two dates date_count_between(start, end, "day", n = 3) # Essentially the truncated value of this date_count_between(start, end, "day") / 3 # --------------------------------------------------------------------------- # Breakdown into full years, months, and days between x <- start years <- date_count_between(x, end, "year") x <- add_years(x, years) months <- date_count_between(x, end, "month") x <- add_months(x, months) days <- date_count_between(x, end, "day") x <- add_days(x, days) data.frame( start = start, end = end, years = years, months = months, days = days ) # Note that when breaking down a date like that, you may need to # set `invalid` during intermediate calculations start <- date_build(2019, c(3, 3, 4), c(30, 31, 1)) end <- date_build(2019, 5, 05) # These are 1 month apart (plus a few days) months <- date_count_between(start, end, "month") # But adding that 1 month to `start` results in an invalid date try(add_months(start, months)) # You can choose various ways to resolve this start_previous <- add_months(start, months, invalid = "previous") start_next <- add_months(start, months, invalid = "next") days_previous <- date_count_between(start_previous, end, "day") days_next <- date_count_between(start_next, end, "day") # Resulting in slightly different day values. # No result is "perfect". Choosing "previous" or "next" both result # in multiple `start` dates having the same month/day breakdown values. data.frame( start = start, end = end, months = months, days_previous = days_previous, days_next = days_next ) } clock/man/iso-year-week-day-count-between.Rd0000644000176200001440000000246114151750124020423 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/iso-year-week-day.R \name{iso-year-week-day-count-between} \alias{iso-year-week-day-count-between} \alias{calendar_count_between.clock_iso_year_week_day} \title{Counting: iso-year-week-day} \usage{ \method{calendar_count_between}{clock_iso_year_week_day}(start, end, precision, ..., n = 1L) } \arguments{ \item{start, end}{\verb{[clock_iso_year_week_day]} A pair of iso-year-week-day vectors. These will be recycled to their common size.} \item{precision}{\verb{[character(1)]} One of: \itemize{ \item \code{"year"} }} \item{...}{These dots are for future extensions and must be empty.} \item{n}{\verb{[positive integer(1)]} A single positive integer specifying a multiple of \code{precision} to use.} } \value{ An integer representing the number of \code{precision} units between \code{start} and \code{end}. } \description{ This is an iso-year-week-day method for the \code{\link[=calendar_count_between]{calendar_count_between()}} generic. It counts the number of \code{precision} units between \code{start} and \code{end} (i.e., the number of ISO years). } \examples{ # Compute the number of whole ISO years between two dates x <- iso_year_week_day(2001, 1, 2) y <- iso_year_week_day(2021, 1, c(1, 3)) calendar_count_between(x, y, "year") } clock/man/clock-setters.Rd0000644000176200001440000000606614422221153015175 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/setters.R \name{clock-setters} \alias{clock-setters} \alias{set_year} \alias{set_quarter} \alias{set_month} \alias{set_week} \alias{set_day} \alias{set_hour} \alias{set_minute} \alias{set_second} \alias{set_millisecond} \alias{set_microsecond} \alias{set_nanosecond} \alias{set_index} \title{Calendar setters} \usage{ set_year(x, value, ...) set_quarter(x, value, ...) set_month(x, value, ...) set_week(x, value, ...) set_day(x, value, ...) set_hour(x, value, ...) set_minute(x, value, ...) set_second(x, value, ...) set_millisecond(x, value, ...) set_microsecond(x, value, ...) set_nanosecond(x, value, ...) set_index(x, value, ...) } \arguments{ \item{x}{\verb{[object]} An object to set the component for.} \item{value}{\verb{[integer]} The value to set the component to.} \item{...}{These dots are for future extensions and must be empty.} } \value{ \code{x} with the component set. } \description{ This family of functions sets fields in a calendar vector. Each calendar has its own set of supported setters, which are documented on their own help page: \itemize{ \item \link[=year-month-day-setters]{year-month-day} \item \link[=year-month-weekday-setters]{year-month-weekday} \item \link[=year-week-day-setters]{year-week-day} \item \link[=iso-year-week-day-setters]{iso-year-week-day} \item \link[=year-quarter-day-setters]{year-quarter-day} \item \link[=year-day-setters]{year-day} } There are also convenience methods for setting certain components directly on R's native date and date-time types. \itemize{ \item \link[=Date-setters]{dates (Date)} \item \link[=posixt-setters]{date-times (POSIXct / POSIXlt)} } Some general rules about setting components on calendar types: \itemize{ \item You can only set components that are relevant to the calendar type that you are working with. For example, you can't set the quarter of a year-month-day type. You'd have to convert to year-quarter-day first. \item You can set a component that is at the current precision, or one level of precision more precise than the current precision. For example, you can set the day field of a month precision year-month-day type, but not the hour field. \item Setting a component can result in an \emph{invalid date}, such as \code{set_day(year_month_day(2019, 02), 31)}, as long as it is eventually resolved either manually or with a strategy from \code{\link[=invalid_resolve]{invalid_resolve()}}. \item With sub-second precisions, you can only set the component corresponding to the precision that you are at. For example, you can set the nanoseconds of the second while at nanosecond precision, but not milliseconds. } } \details{ You cannot set components directly on a time point type, such as sys-time or naive-time. Convert it to a calendar type first. Similarly, a zoned-time must be converted to either a sys-time or naive-time, and then to a calendar type, to be able to set components on it. } \examples{ x <- year_month_day(2019, 1:3) # Set the day set_day(x, 12:14) # Set to the "last" day of the month set_day(x, "last") } clock/man/year-month-weekday-getters.Rd0000644000176200001440000000423514011271365017600 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gregorian-year-month-weekday.R \name{year-month-weekday-getters} \alias{year-month-weekday-getters} \alias{get_year.clock_year_month_weekday} \alias{get_month.clock_year_month_weekday} \alias{get_day.clock_year_month_weekday} \alias{get_index.clock_year_month_weekday} \alias{get_hour.clock_year_month_weekday} \alias{get_minute.clock_year_month_weekday} \alias{get_second.clock_year_month_weekday} \alias{get_millisecond.clock_year_month_weekday} \alias{get_microsecond.clock_year_month_weekday} \alias{get_nanosecond.clock_year_month_weekday} \title{Getters: year-month-weekday} \usage{ \method{get_year}{clock_year_month_weekday}(x) \method{get_month}{clock_year_month_weekday}(x) \method{get_day}{clock_year_month_weekday}(x) \method{get_index}{clock_year_month_weekday}(x) \method{get_hour}{clock_year_month_weekday}(x) \method{get_minute}{clock_year_month_weekday}(x) \method{get_second}{clock_year_month_weekday}(x) \method{get_millisecond}{clock_year_month_weekday}(x) \method{get_microsecond}{clock_year_month_weekday}(x) \method{get_nanosecond}{clock_year_month_weekday}(x) } \arguments{ \item{x}{\verb{[clock_year_month_weekday]} A year-month-weekday to get the component from.} } \value{ The component. } \description{ These are year-month-weekday methods for the \link[=clock-getters]{getter generics}. \itemize{ \item \code{get_year()} returns the Gregorian year. \item \code{get_month()} returns the month of the year. \item \code{get_day()} returns the day of the week encoded from 1-7, where 1 = Sunday and 7 = Saturday. \item \code{get_index()} returns a value from 1-5 indicating that the corresponding weekday is the n-th instance of that weekday in the current month. \item There are sub-daily getters for extracting more precise components. } } \examples{ monday <- clock_weekdays$monday thursday <- clock_weekdays$thursday x <- year_month_weekday(2019, 1, monday:thursday, 1:4) x # Gets the weekday, 1 = Sunday, 7 = Saturday get_day(x) # Gets the index indicating which instance of that particular weekday # it is in the current month (i.e. the "1st Monday of January, 2019") get_index(x) } clock/man/iso-year-week-day-getters.Rd0000644000176200001440000000371114007013500017306 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/iso-year-week-day.R \name{iso-year-week-day-getters} \alias{iso-year-week-day-getters} \alias{get_year.clock_iso_year_week_day} \alias{get_week.clock_iso_year_week_day} \alias{get_day.clock_iso_year_week_day} \alias{get_hour.clock_iso_year_week_day} \alias{get_minute.clock_iso_year_week_day} \alias{get_second.clock_iso_year_week_day} \alias{get_millisecond.clock_iso_year_week_day} \alias{get_microsecond.clock_iso_year_week_day} \alias{get_nanosecond.clock_iso_year_week_day} \title{Getters: iso-year-week-day} \usage{ \method{get_year}{clock_iso_year_week_day}(x) \method{get_week}{clock_iso_year_week_day}(x) \method{get_day}{clock_iso_year_week_day}(x) \method{get_hour}{clock_iso_year_week_day}(x) \method{get_minute}{clock_iso_year_week_day}(x) \method{get_second}{clock_iso_year_week_day}(x) \method{get_millisecond}{clock_iso_year_week_day}(x) \method{get_microsecond}{clock_iso_year_week_day}(x) \method{get_nanosecond}{clock_iso_year_week_day}(x) } \arguments{ \item{x}{\verb{[clock_iso_year_week_day]} A iso-year-week-day to get the component from.} } \value{ The component. } \description{ These are iso-year-week-day methods for the \link[=clock-getters]{getter generics}. \itemize{ \item \code{get_year()} returns the ISO year. Note that this can differ from the Gregorian year. \item \code{get_week()} returns the ISO week of the current ISO year. \item \code{get_day()} returns a value between 1-7 indicating the weekday of the current ISO week, where 1 = Monday and 7 = Sunday, in line with the ISO standard. \item There are sub-daily getters for extracting more precise components. } } \examples{ x <- iso_year_week_day(2019, 50:52, 1:3) x # Get the ISO week get_week(x) # Gets the weekday, 1 = Monday, 7 = Sunday get_day(x) # Note that the ISO year can differ from the Gregorian year iso <- iso_year_week_day(2019, 1, 1) ymd <- as_year_month_day(iso) get_year(iso) get_year(ymd) } clock/man/year_month_day.Rd0000644000176200001440000000452414416030722015415 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gregorian-year-month-day.R \name{year_month_day} \alias{year_month_day} \title{Calendar: year-month-day} \usage{ year_month_day( year, month = NULL, day = NULL, hour = NULL, minute = NULL, second = NULL, subsecond = NULL, ..., subsecond_precision = NULL ) } \arguments{ \item{year}{\verb{[integer]} The year. Values \verb{[-32767, 32767]} are generally allowed.} \item{month}{\verb{[integer / NULL]} The month. Values \verb{[1, 12]} are allowed.} \item{day}{\verb{[integer / "last" / NULL]} The day of the month. Values \verb{[1, 31]} are allowed. If \code{"last"}, then the last day of the month is returned.} \item{hour}{\verb{[integer / NULL]} The hour. Values \verb{[0, 23]} are allowed.} \item{minute}{\verb{[integer / NULL]} The minute. Values \verb{[0, 59]} are allowed.} \item{second}{\verb{[integer / NULL]} The second. Values \verb{[0, 59]} are allowed.} \item{subsecond}{\verb{[integer / NULL]} The subsecond. If specified, \code{subsecond_precision} must also be specified to determine how to interpret the \code{subsecond}. If using milliseconds, values \verb{[0, 999]} are allowed. If using microseconds, values \verb{[0, 999999]} are allowed. If using nanoseconds, values \verb{[0, 999999999]} are allowed.} \item{...}{These dots are for future extensions and must be empty.} \item{subsecond_precision}{\verb{[character(1) / NULL]} The precision to interpret \code{subsecond} as. One of: \code{"millisecond"}, \code{"microsecond"}, or \code{"nanosecond"}.} } \value{ A year-month-day calendar vector. } \description{ \code{year_month_day()} constructs the most common calendar type using the Gregorian year, month, day, and time of day components. } \details{ Fields are recycled against each other using \link[vctrs:vector_recycling_rules]{tidyverse recycling rules}. Fields are collected in order until the first \code{NULL} field is located. No fields after the first \code{NULL} field are used. } \examples{ # Just the year x <- year_month_day(2019:2025) # Year-month type year_month_day(2020, 1:12) # The most common use case involves year, month, and day fields x <- year_month_day(2020, clock_months$january, 1:5) x # Precision can go all the way out to nanosecond year_month_day(2019, 1, 2, 2, 40, 45, 200, subsecond_precision = "nanosecond") } clock/man/iso-year-week-day-boundary.Rd0000644000176200001440000000257414075602251017476 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/iso-year-week-day.R \name{iso-year-week-day-boundary} \alias{iso-year-week-day-boundary} \alias{calendar_start.clock_iso_year_week_day} \alias{calendar_end.clock_iso_year_week_day} \title{Boundaries: iso-year-week-day} \usage{ \method{calendar_start}{clock_iso_year_week_day}(x, precision) \method{calendar_end}{clock_iso_year_week_day}(x, precision) } \arguments{ \item{x}{\verb{[clock_iso_year_week_day]} A iso-year-week-day vector.} \item{precision}{\verb{[character(1)]} One of: \itemize{ \item \code{"year"} \item \code{"week"} \item \code{"day"} \item \code{"hour"} \item \code{"minute"} \item \code{"second"} \item \code{"millisecond"} \item \code{"microsecond"} \item \code{"nanosecond"} }} } \value{ \code{x} at the same precision, but with some components altered to be at the boundary value. } \description{ This is an iso-year-week-day method for the \code{\link[=calendar_start]{calendar_start()}} and \code{\link[=calendar_end]{calendar_end()}} generics. They adjust components of a calendar to the start or end of a specified \code{precision}. } \examples{ x <- iso_year_week_day(2019:2020, 5, 6, 10) x # Compute the last moment of the last iso week of the year calendar_end(x, "year") # Compare that to just setting the week to `"last"`, # which doesn't affect the other components set_week(x, "last") } clock/man/as_naive_time.Rd0000644000176200001440000000262014427270231015214 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/naive-time.R \name{as_naive_time} \alias{as_naive_time} \title{Convert to a naive-time} \usage{ as_naive_time(x, ...) } \arguments{ \item{x}{\verb{[object]} An object to convert to a naive-time.} \item{...}{These dots are for future extensions and must be empty.} } \value{ A naive-time vector. } \description{ \code{as_naive_time()} converts \code{x} to a naive-time. You can convert to a naive-time from any calendar type, as long as it has at least day precision. There also must not be any invalid dates. If invalid dates exist, they must first be resolved with \code{\link[=invalid_resolve]{invalid_resolve()}}. Converting to a naive-time from a sys-time or zoned-time retains the printed time, but drops the assumption that the time should be interpreted with any specific time zone. Converting to a naive-time from a duration just wraps the duration in a naive-time object, there is no assumption about the time zone. The duration must have at least day precision. There are convenience methods for converting to a naive-time from R's native date and date-time types. Like converting from a zoned-time, these retain the printed time. } \examples{ x <- as.Date("2019-01-01") as_naive_time(x) ym <- year_month_day(2019, 02) # A minimum of day precision is required try(as_naive_time(ym)) ymd <- set_day(ym, 10) as_naive_time(ymd) } clock/man/date_build.Rd0000644000176200001440000000440514416032013014500 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/date.R \name{date_build} \alias{date_build} \title{Building: date} \usage{ date_build(year, month = 1L, day = 1L, ..., invalid = NULL) } \arguments{ \item{year}{\verb{[integer]} The year. Values \verb{[-32767, 32767]} are generally allowed.} \item{month}{\verb{[integer]} The month. Values \verb{[1, 12]} are allowed.} \item{day}{\verb{[integer / "last"]} The day of the month. Values \verb{[1, 31]} are allowed. If \code{"last"}, then the last day of the month is returned.} \item{...}{These dots are for future extensions and must be empty.} \item{invalid}{\verb{[character(1) / NULL]} One of the following invalid date resolution strategies: \itemize{ \item \code{"previous"}: The previous valid instant in time. \item \code{"previous-day"}: The previous valid day in time, keeping the time of day. \item \code{"next"}: The next valid instant in time. \item \code{"next-day"}: The next valid day in time, keeping the time of day. \item \code{"overflow"}: Overflow by the number of days that the input is invalid by. Time of day is dropped. \item \code{"overflow-day"}: Overflow by the number of days that the input is invalid by. Time of day is kept. \item \code{"NA"}: Replace invalid dates with \code{NA}. \item \code{"error"}: Error on invalid dates. } Using either \code{"previous"} or \code{"next"} is generally recommended, as these two strategies maintain the \emph{relative ordering} between elements of the input. If \code{NULL}, defaults to \code{"error"}. If \code{getOption("clock.strict")} is \code{TRUE}, \code{invalid} must be supplied and cannot be \code{NULL}. This is a convenient way to make production code robust to invalid dates.} } \value{ A Date. } \description{ \code{date_build()} builds a Date from it's individual components. } \details{ Components are recycled against each other using \link[vctrs:vector_recycling_rules]{tidyverse recycling rules}. } \examples{ date_build(2019) date_build(2019, 1:3) # Generating invalid dates will trigger an error try(date_build(2019, 1:12, 31)) # You can resolve this with `invalid` date_build(2019, 1:12, 31, invalid = "previous") # But this particular case (the last day of the month) is better # specified as: date_build(2019, 1:12, "last") } clock/man/is_weekday.Rd0000644000176200001440000000070514005067725014543 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/weekday.R \name{is_weekday} \alias{is_weekday} \title{Is \code{x} a weekday?} \usage{ is_weekday(x) } \arguments{ \item{x}{\verb{[object]} An object.} } \value{ \code{TRUE} if \code{x} inherits from \code{"clock_weekday"}, otherwise \code{FALSE}. } \description{ This function determines if the input is a weekday object. } \examples{ is_weekday(1) is_weekday(weekday(1)) } clock/man/is_duration.Rd0000644000176200001440000000072414005033743014731 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/duration.R \name{is_duration} \alias{is_duration} \title{Is \code{x} a duration?} \usage{ is_duration(x) } \arguments{ \item{x}{\verb{[object]} An object.} } \value{ \code{TRUE} if \code{x} inherits from \code{"clock_duration"}, otherwise \code{FALSE}. } \description{ This function determines if the input is a duration object. } \examples{ is_duration(1) is_duration(duration_days(1)) } clock/man/year-week-day-arithmetic.Rd0000644000176200001440000000300114422221153017170 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/week-year-week-day.R \name{year-week-day-arithmetic} \alias{year-week-day-arithmetic} \alias{add_years.clock_year_week_day} \title{Arithmetic: year-week-day} \usage{ \method{add_years}{clock_year_week_day}(x, n, ...) } \arguments{ \item{x}{\verb{[clock_year_week_day]} A year-week-day vector.} \item{n}{\verb{[integer / clock_duration]} An integer vector to be converted to a duration, or a duration corresponding to the arithmetic function being used. This corresponds to the number of duration units to add. \code{n} may be negative to subtract units of duration.} \item{...}{These dots are for future extensions and must be empty.} } \value{ \code{x} after performing the arithmetic. } \description{ These are year-week-day methods for the \link[=clock-arithmetic]{arithmetic generics}. \itemize{ \item \code{add_years()} } You cannot add weeks or days to a year-week-day calendar. Adding days is much more efficiently done by converting to a time point first by using \code{\link[=as_naive_time]{as_naive_time()}} or \code{\link[=as_sys_time]{as_sys_time()}}. Adding weeks is equally as efficient as adding 7 days. Additionally, adding weeks to an invalid year-week object (i.e. one set to the 53rd week, when that doesn't exist) would be undefined. } \details{ \code{x} and \code{n} are recycled against each other using \link[vctrs:vector_recycling_rules]{tidyverse recycling rules}. } \examples{ x <- year_week_day(2019, 1, 1) add_years(x, 1:2) } clock/man/date-sequence.Rd0000644000176200001440000001100514162373152015133 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/date.R \name{date-sequence} \alias{date-sequence} \alias{date_seq.Date} \title{Sequences: date} \usage{ \method{date_seq}{Date}(from, ..., to = NULL, by = NULL, total_size = NULL, invalid = NULL) } \arguments{ \item{from}{\verb{[Date(1)]} A date to start the sequence from.} \item{...}{These dots are for future extensions and must be empty.} \item{to}{\verb{[Date(1) / NULL]} A date to stop the sequence at. \code{to} is only included in the result if the resulting sequence divides the distance between \code{from} and \code{to} exactly. If \code{to} is supplied along with \code{by}, all components of \code{to} more precise than the precision of \code{by} must match \code{from} exactly. For example, if \code{by = duration_months(1)}, the day component of \code{to} must match the day component of \code{from}. This ensures that the generated sequence is, at a minimum, a weakly monotonic sequence of dates.} \item{by}{\verb{[integer(1) / clock_duration(1) / NULL]} The unit to increment the sequence by. If \code{by} is an integer, it is equivalent to \code{duration_days(by)}. If \code{by} is a duration, it is allowed to have a precision of: \itemize{ \item year \item quarter \item month \item week \item day }} \item{total_size}{\verb{[positive integer(1) / NULL]} The size of the resulting sequence. If specified alongside \code{to}, this must generate a non-fractional sequence between \code{from} and \code{to}.} \item{invalid}{\verb{[character(1) / NULL]} One of the following invalid date resolution strategies: \itemize{ \item \code{"previous"}: The previous valid instant in time. \item \code{"previous-day"}: The previous valid day in time, keeping the time of day. \item \code{"next"}: The next valid instant in time. \item \code{"next-day"}: The next valid day in time, keeping the time of day. \item \code{"overflow"}: Overflow by the number of days that the input is invalid by. Time of day is dropped. \item \code{"overflow-day"}: Overflow by the number of days that the input is invalid by. Time of day is kept. \item \code{"NA"}: Replace invalid dates with \code{NA}. \item \code{"error"}: Error on invalid dates. } Using either \code{"previous"} or \code{"next"} is generally recommended, as these two strategies maintain the \emph{relative ordering} between elements of the input. If \code{NULL}, defaults to \code{"error"}. If \code{getOption("clock.strict")} is \code{TRUE}, \code{invalid} must be supplied and cannot be \code{NULL}. This is a convenient way to make production code robust to invalid dates.} } \value{ A date vector. } \description{ This is a Date method for the \code{\link[=date_seq]{date_seq()}} generic. \code{date_seq()} generates a date (Date) sequence. When calling \code{date_seq()}, exactly two of the following must be specified: \itemize{ \item \code{to} \item \code{by} \item \code{total_size} } } \examples{ from <- date_build(2019, 1) to <- date_build(2019, 4) # Defaults to daily sequence date_seq(from, to = to, by = 7) # Use durations to change to monthly or yearly sequences date_seq(from, to = to, by = duration_months(1)) date_seq(from, by = duration_years(-2), total_size = 3) # Note that components of `to` more precise than the precision of `by` # must match `from` exactly. For example, this is not well defined: from <- date_build(2019, 5, 2) to <- date_build(2025, 7, 5) try(date_seq(from, to = to, by = duration_years(1))) # The month and day components of `to` must match `from` to <- date_build(2025, 5, 2) date_seq(from, to = to, by = duration_years(1)) # --------------------------------------------------------------------------- # Invalid dates must be resolved with the `invalid` argument from <- date_build(2019, 1, 31) to <- date_build(2019, 12, 31) try(date_seq(from, to = to, by = duration_months(1))) date_seq(from, to = to, by = duration_months(1), invalid = "previous") # Compare this to the base R result, which is often a source of confusion seq(from, to = to, by = "1 month") # This is equivalent to the overflow invalid resolution strategy date_seq(from, to = to, by = duration_months(1), invalid = "overflow") # --------------------------------------------------------------------------- # Usage of `to` and `total_size` must generate a non-fractional sequence # between `from` and `to` from <- date_build(2019, 1, 1) to <- date_build(2019, 1, 4) # These are fine date_seq(from, to = to, total_size = 2) date_seq(from, to = to, total_size = 4) # But this is not! try(date_seq(from, to = to, total_size = 3)) } clock/man/date-and-date-time-shifting.Rd0000644000176200001440000000344714073622034017555 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/date.R \name{date-and-date-time-shifting} \alias{date-and-date-time-shifting} \alias{date_shift} \title{Shifting: date and date-time} \usage{ date_shift(x, target, ..., which = "next", boundary = "keep") } \arguments{ \item{x}{\verb{[Date / POSIXct / POSIXlt]} A date or date-time vector.} \item{target}{\verb{[weekday]} A weekday created from \code{\link[=weekday]{weekday()}} to target. Generally this is length 1, but can also be the same length as \code{x}.} \item{...}{These dots are for future extensions and must be empty.} \item{which}{\verb{[character(1)]} One of: \itemize{ \item \code{"next"}: Shift to the next instance of the \code{target} weekday. \item \verb{"previous}: Shift to the previous instance of the \code{target} weekday. }} \item{boundary}{\verb{[character(1)]} One of: \itemize{ \item \code{"keep"}: If \code{x} is currently on the \code{target} weekday, return it. \item \code{"advance"}: If \code{x} is currently on the \code{target} weekday, advance it anyways. }} } \value{ \code{x} shifted to the \code{target} weekday. } \description{ \code{date_shift()} shifts \code{x} to the \code{target} weekday. You can shift to the next or previous weekday. If \code{x} is currently on the \code{target} weekday, you can choose to leave it alone or advance it to the next instance of the \code{target}. There are separate help pages for shifting dates and date-times: \itemize{ \item \link[=date-shifting]{dates (Date)} \item \link[=posixt-shifting]{date-times (POSIXct/POSIXlt)} } } \examples{ # See the type specific documentation for more examples x <- as.Date("2019-01-01") + 0:1 # A Tuesday and Wednesday as_weekday(x) monday <- weekday(clock_weekdays$monday) # Shift to the next Monday date_shift(x, monday) } clock/man/weekday.Rd0000644000176200001440000000372514017505732014053 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/weekday.R \name{weekday} \alias{weekday} \title{Construct a weekday vector} \usage{ weekday(code = integer(), ..., encoding = "western") } \arguments{ \item{code}{\verb{[integer]} Integer codes between \verb{[1, 7]} representing days of the week. The interpretation of these values depends on \code{encoding}.} \item{...}{These dots are for future extensions and must be empty.} \item{encoding}{\verb{[character(1)]} One of: \itemize{ \item \code{"western"}: Encode weekdays assuming \code{1 == Sunday} and \code{7 == Saturday}. \item \code{"iso"}: Encode weekdays assuming \code{1 == Monday} and \code{7 == Sunday}. This is in line with the ISO standard. }} } \value{ A weekday vector. } \description{ A \code{weekday} is a simple type that represents a day of the week. The most interesting thing about the weekday type is that it implements \emph{circular arithmetic}, which makes determining the "next Monday" or "previous Tuesday" from a sys-time or naive-time easy to compute. See the examples. } \examples{ x <- as_naive_time(year_month_day(2019, 01, 05)) # This is a Saturday! as_weekday(x) # Adjust to the next Wednesday wednesday <- weekday(clock_weekdays$wednesday) # This returns the number of days until the next Wednesday using # circular arithmetic # "Wednesday - Saturday = 4 days until next Wednesday" wednesday - as_weekday(x) # Advance to the next Wednesday x_next_wednesday <- x + (wednesday - as_weekday(x)) as_weekday(x_next_wednesday) # What about the previous Tuesday? tuesday <- weekday(clock_weekdays$tuesday) x - (as_weekday(x) - tuesday) # What about the next Saturday? # With an additional condition that if today is a Saturday, # then advance to the next one. saturday <- weekday(clock_weekdays$saturday) x + 1L + (saturday - as_weekday(x + 1L)) # You can supply an ISO coding for `code` as well, where 1 == Monday. weekday(1:7, encoding = "western") weekday(1:7, encoding = "iso") } clock/man/year-day-arithmetic.Rd0000644000176200001440000000346614416031540016260 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gregorian-year-day.R \name{year-day-arithmetic} \alias{year-day-arithmetic} \alias{add_years.clock_year_day} \title{Arithmetic: year-day} \usage{ \method{add_years}{clock_year_day}(x, n, ...) } \arguments{ \item{x}{\verb{[clock_year_day]} A year-day vector.} \item{n}{\verb{[integer / clock_duration]} An integer vector to be converted to a duration, or a duration corresponding to the arithmetic function being used. This corresponds to the number of duration units to add. \code{n} may be negative to subtract units of duration.} \item{...}{These dots are for future extensions and must be empty.} } \value{ \code{x} after performing the arithmetic. } \description{ These are year-day methods for the \link[=clock-arithmetic]{arithmetic generics}. \itemize{ \item \code{add_years()} } Notably, \emph{you cannot add days to a year-day}. For day-based arithmetic, first convert to a time point with \code{\link[=as_naive_time]{as_naive_time()}} or \code{\link[=as_sys_time]{as_sys_time()}}. } \details{ \code{x} and \code{n} are recycled against each other using \link[vctrs:vector_recycling_rules]{tidyverse recycling rules}. } \examples{ x <- year_day(2019, 10) add_years(x, 1:5) # A valid day in a leap year y <- year_day(2020, 366) y # Adding 1 year to `y` generates an invalid date y_plus <- add_years(y, 1) y_plus # Invalid dates are fine, as long as they are eventually resolved # by either manually resolving, or by calling `invalid_resolve()` # Resolve by returning the previous / next valid moment in time invalid_resolve(y_plus, invalid = "previous") invalid_resolve(y_plus, invalid = "next") # Manually resolve by setting to the last day of the year invalid <- invalid_detect(y_plus) y_plus[invalid] <- set_day(y_plus[invalid], "last") y_plus } clock/man/zoned_time_precision.Rd0000644000176200001440000000104314027170177016623 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/zoned-time.R \name{zoned_time_precision} \alias{zoned_time_precision} \title{Precision: zoned-time} \usage{ zoned_time_precision(x) } \arguments{ \item{x}{\verb{[clock_zoned_time]} A zoned-time.} } \value{ A single string holding the precision of the zoned-time. } \description{ \code{zoned_time_precision()} extracts the precision from a zoned-time. It returns the precision as a single string. } \examples{ zoned_time_precision(zoned_time_now("America/New_York")) } clock/man/as_duration.Rd0000644000176200001440000000155314427270231014725 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/duration.R \name{as_duration} \alias{as_duration} \title{Convert to a duration} \usage{ as_duration(x, ...) } \arguments{ \item{x}{\verb{[object]} An object to convert to a duration.} \item{...}{These dots are for future extensions and must be empty.} } \value{ A duration with the same precision as \code{x}. } \description{ You generally convert to a duration from either a sys-time or a naive-time. The precision of the input is retained in the returned duration. To round an existing duration to another precision, see \code{\link[=duration_floor]{duration_floor()}}. } \examples{ x <- as_sys_time(year_month_day(2019, 01, 01)) # The number of days since 1970-01-01 UTC as_duration(x) x <- x + duration_seconds(1) x # The number of seconds since 1970-01-01 00:00:00 UTC as_duration(x) } clock/man/posixt-group.Rd0000644000176200001440000001317114007060667015100 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/posixt.R \name{posixt-group} \alias{posixt-group} \alias{date_group.POSIXt} \title{Group date-time components} \usage{ \method{date_group}{POSIXt}( x, precision, ..., n = 1L, invalid = NULL, nonexistent = NULL, ambiguous = x ) } \arguments{ \item{x}{\verb{[POSIXct / POSIXlt]} A date-time vector.} \item{precision}{\verb{[character(1)]} One of: \itemize{ \item \code{"year"} \item \code{"month"} \item \code{"day"} \item \code{"hour"} \item \code{"minute"} \item \code{"second"} }} \item{...}{These dots are for future extensions and must be empty.} \item{n}{\verb{[positive integer(1)]} A single positive integer specifying a multiple of \code{precision} to use.} \item{invalid}{\verb{[character(1) / NULL]} One of the following invalid date resolution strategies: \itemize{ \item \code{"previous"}: The previous valid instant in time. \item \code{"previous-day"}: The previous valid day in time, keeping the time of day. \item \code{"next"}: The next valid instant in time. \item \code{"next-day"}: The next valid day in time, keeping the time of day. \item \code{"overflow"}: Overflow by the number of days that the input is invalid by. Time of day is dropped. \item \code{"overflow-day"}: Overflow by the number of days that the input is invalid by. Time of day is kept. \item \code{"NA"}: Replace invalid dates with \code{NA}. \item \code{"error"}: Error on invalid dates. } Using either \code{"previous"} or \code{"next"} is generally recommended, as these two strategies maintain the \emph{relative ordering} between elements of the input. If \code{NULL}, defaults to \code{"error"}. If \code{getOption("clock.strict")} is \code{TRUE}, \code{invalid} must be supplied and cannot be \code{NULL}. This is a convenient way to make production code robust to invalid dates.} \item{nonexistent}{\verb{[character / NULL]} One of the following nonexistent time resolution strategies, allowed to be either length 1, or the same length as the input: \itemize{ \item \code{"roll-forward"}: The next valid instant in time. \item \code{"roll-backward"}: The previous valid instant in time. \item \code{"shift-forward"}: Shift the nonexistent time forward by the size of the daylight saving time gap. \item \verb{"shift-backward}: Shift the nonexistent time backward by the size of the daylight saving time gap. \item \code{"NA"}: Replace nonexistent times with \code{NA}. \item \code{"error"}: Error on nonexistent times. } Using either \code{"roll-forward"} or \code{"roll-backward"} is generally recommended over shifting, as these two strategies maintain the \emph{relative ordering} between elements of the input. If \code{NULL}, defaults to \code{"error"}. If \code{getOption("clock.strict")} is \code{TRUE}, \code{nonexistent} must be supplied and cannot be \code{NULL}. This is a convenient way to make production code robust to nonexistent times.} \item{ambiguous}{\verb{[character / zoned_time / POSIXct / list(2) / NULL]} One of the following ambiguous time resolution strategies, allowed to be either length 1, or the same length as the input: \itemize{ \item \code{"earliest"}: Of the two possible times, choose the earliest one. \item \code{"latest"}: Of the two possible times, choose the latest one. \item \code{"NA"}: Replace ambiguous times with \code{NA}. \item \code{"error"}: Error on ambiguous times. } Alternatively, \code{ambiguous} is allowed to be a zoned_time (or POSIXct) that is either length 1, or the same length as the input. If an ambiguous time is encountered, the zoned_time is consulted. If the zoned_time corresponds to a naive_time that is also ambiguous \emph{and} uses the same daylight saving time transition point as the original ambiguous time, then the offset of the zoned_time is used to resolve the ambiguity. If the ambiguity cannot be resolved by consulting the zoned_time, then this method falls back to \code{NULL}. Finally, \code{ambiguous} is allowed to be a list of size 2, where the first element of the list is a zoned_time (as described above), and the second element of the list is an ambiguous time resolution strategy to use when the ambiguous time cannot be resolved by consulting the zoned_time. Specifying a zoned_time on its own is identical to \verb{list(, NULL)}. If \code{NULL}, defaults to \code{"error"}. If \code{getOption("clock.strict")} is \code{TRUE}, \code{ambiguous} must be supplied and cannot be \code{NULL}. Additionally, \code{ambiguous} cannot be specified as a zoned_time on its own, as this implies \code{NULL} for ambiguous times that the zoned_time cannot resolve. Instead, it must be specified as a list alongside an ambiguous time resolution strategy as described above. This is a convenient way to make production code robust to ambiguous times.} } \value{ \code{x}, grouped at \code{precision}. } \description{ This is a POSIXct/POSIXlt method for the \code{\link[=date_group]{date_group()}} generic. \code{date_group()} groups by a single component of a date-time, such as month of the year, day of the month, or hour of the day. If you need to group by more complex components, like ISO weeks, or quarters, convert to a calendar type that contains the component you are interested in grouping by. } \examples{ x <- as.POSIXct("2019-01-01", "America/New_York") x <- add_days(x, -3:5) # Group by 2 days of the current month. # Note that this resets at the beginning of the month, creating day groups # of [29, 30] [31] [01, 02] [03, 04]. date_group(x, "day", n = 2) # Group by month date_group(x, "month") # Group by hour of the day y <- as.POSIXct("2019-12-30", "America/New_York") y <- add_hours(y, 0:12) y date_group(y, "hour", n = 3) } clock/man/year-month-day-widen.Rd0000644000176200001440000000251214006265167016360 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gregorian-year-month-day.R \name{year-month-day-widen} \alias{year-month-day-widen} \alias{calendar_widen.clock_year_month_day} \title{Widen: year-month-day} \usage{ \method{calendar_widen}{clock_year_month_day}(x, precision) } \arguments{ \item{x}{\verb{[clock_year_month_day]} A year-month-day vector.} \item{precision}{\verb{[character(1)]} One of: \itemize{ \item \code{"year"} \item \code{"month"} \item \code{"day"} \item \code{"hour"} \item \code{"minute"} \item \code{"second"} \item \code{"millisecond"} \item \code{"microsecond"} \item \code{"nanosecond"} }} } \value{ \code{x} widened to the supplied \code{precision}. } \description{ This is a year-month-day method for the \code{\link[=calendar_widen]{calendar_widen()}} generic. It widens a year-month-day vector to the specified \code{precision}. } \examples{ # Month precision x <- year_month_day(2019, 1) x # Widen to day precision calendar_widen(x, "day") # Or second precision sec <- calendar_widen(x, "second") sec # Second precision can be widened to subsecond precision milli <- calendar_widen(sec, "millisecond") micro <- calendar_widen(sec, "microsecond") milli micro # But once you have "locked in" a subsecond precision, it can't # be widened again try(calendar_widen(milli, "microsecond")) } clock/man/as_date_time.Rd0000644000176200001440000001420414035070503015023 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/posixt.R \name{as_date_time} \alias{as_date_time} \alias{as_date_time.POSIXt} \alias{as_date_time.Date} \alias{as_date_time.clock_calendar} \alias{as_date_time.clock_sys_time} \alias{as_date_time.clock_naive_time} \alias{as_date_time.clock_zoned_time} \title{Convert to a date-time} \usage{ as_date_time(x, ...) \method{as_date_time}{POSIXt}(x, ...) \method{as_date_time}{Date}(x, zone, ..., nonexistent = NULL, ambiguous = NULL) \method{as_date_time}{clock_calendar}(x, zone, ..., nonexistent = NULL, ambiguous = NULL) \method{as_date_time}{clock_sys_time}(x, zone, ...) \method{as_date_time}{clock_naive_time}(x, zone, ..., nonexistent = NULL, ambiguous = NULL) \method{as_date_time}{clock_zoned_time}(x, ...) } \arguments{ \item{x}{\verb{[vector]} A vector.} \item{...}{These dots are for future extensions and must be empty.} \item{zone}{\verb{[character(1)]} The zone to convert to.} \item{nonexistent}{\verb{[character / NULL]} One of the following nonexistent time resolution strategies, allowed to be either length 1, or the same length as the input: \itemize{ \item \code{"roll-forward"}: The next valid instant in time. \item \code{"roll-backward"}: The previous valid instant in time. \item \code{"shift-forward"}: Shift the nonexistent time forward by the size of the daylight saving time gap. \item \verb{"shift-backward}: Shift the nonexistent time backward by the size of the daylight saving time gap. \item \code{"NA"}: Replace nonexistent times with \code{NA}. \item \code{"error"}: Error on nonexistent times. } Using either \code{"roll-forward"} or \code{"roll-backward"} is generally recommended over shifting, as these two strategies maintain the \emph{relative ordering} between elements of the input. If \code{NULL}, defaults to \code{"error"}. If \code{getOption("clock.strict")} is \code{TRUE}, \code{nonexistent} must be supplied and cannot be \code{NULL}. This is a convenient way to make production code robust to nonexistent times.} \item{ambiguous}{\verb{[character / zoned_time / POSIXct / list(2) / NULL]} One of the following ambiguous time resolution strategies, allowed to be either length 1, or the same length as the input: \itemize{ \item \code{"earliest"}: Of the two possible times, choose the earliest one. \item \code{"latest"}: Of the two possible times, choose the latest one. \item \code{"NA"}: Replace ambiguous times with \code{NA}. \item \code{"error"}: Error on ambiguous times. } Alternatively, \code{ambiguous} is allowed to be a zoned_time (or POSIXct) that is either length 1, or the same length as the input. If an ambiguous time is encountered, the zoned_time is consulted. If the zoned_time corresponds to a naive_time that is also ambiguous \emph{and} uses the same daylight saving time transition point as the original ambiguous time, then the offset of the zoned_time is used to resolve the ambiguity. If the ambiguity cannot be resolved by consulting the zoned_time, then this method falls back to \code{NULL}. Finally, \code{ambiguous} is allowed to be a list of size 2, where the first element of the list is a zoned_time (as described above), and the second element of the list is an ambiguous time resolution strategy to use when the ambiguous time cannot be resolved by consulting the zoned_time. Specifying a zoned_time on its own is identical to \verb{list(, NULL)}. If \code{NULL}, defaults to \code{"error"}. If \code{getOption("clock.strict")} is \code{TRUE}, \code{ambiguous} must be supplied and cannot be \code{NULL}. Additionally, \code{ambiguous} cannot be specified as a zoned_time on its own, as this implies \code{NULL} for ambiguous times that the zoned_time cannot resolve. Instead, it must be specified as a list alongside an ambiguous time resolution strategy as described above. This is a convenient way to make production code robust to ambiguous times.} } \value{ A date-time with the same length as \code{x}. } \description{ \code{as_date_time()} is a generic function that converts its input to a date-time (POSIXct). There are methods for converting dates (Date), calendars, time points, and zoned-times to date-times. For converting to a date, see \code{\link[=as_date]{as_date()}}. } \details{ Note that clock always assumes that R's Date class is naive, so converting a Date to a POSIXct will always attempt to retain the printed year, month, and day. Where possible, the resulting time will be at midnight (\code{00:00:00}), but in some rare cases this is not possible due to daylight saving time. If that issue ever arises, an error will be thrown, which can be resolved by explicitly supplying \code{nonexistent} or \code{ambiguous}. This is not a drop-in replacement for \code{as.POSIXct()}, as it only converts a limited set of types to POSIXct. For parsing characters as date-times, see \code{\link[=date_time_parse]{date_time_parse()}}. For converting numerics to date-times, see \code{\link[vctrs:new_date]{vctrs::new_datetime()}} or continue to use \code{as.POSIXct()}. } \examples{ x <- as.Date("2019-01-01") # `as.POSIXct()` will always treat Date as UTC, but will show the result # of the conversion in your system time zone, which can be somewhat confusing if (rlang::is_installed("withr")) { withr::with_timezone("UTC", print(as.POSIXct(x))) withr::with_timezone("Europe/Paris", print(as.POSIXct(x))) withr::with_timezone("America/New_York", print(as.POSIXct(x))) } # `as_date_time()` will treat Date as naive, which means that the original # printed date will attempt to be kept wherever possible, no matter the # time zone. The time will be set to midnight. as_date_time(x, "UTC") as_date_time(x, "Europe/Paris") as_date_time(x, "America/New_York") # In some rare cases, this is not possible. # For example, in Asia/Beirut, there was a DST gap from # 2021-03-27 23:59:59 -> 2021-03-28 01:00:00, # skipping the 0th hour entirely. x <- as.Date("2021-03-28") try(as_date_time(x, "Asia/Beirut")) # To resolve this, set a `nonexistent` time resolution strategy as_date_time(x, "Asia/Beirut", nonexistent = "roll-forward") # You can also convert to date-time from other clock types as_date_time(year_month_day(2019, 2, 3, 03), "America/New_York") } clock/man/seq.clock_duration.Rd0000644000176200001440000000463614162373152016213 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/duration.R \name{seq.clock_duration} \alias{seq.clock_duration} \title{Sequences: duration} \usage{ \method{seq}{clock_duration}(from, to = NULL, by = NULL, length.out = NULL, along.with = NULL, ...) } \arguments{ \item{from}{\verb{[clock_duration(1)]} A duration to start the sequence from.} \item{to}{\verb{[clock_duration(1) / NULL]} A duration to stop the sequence at. \code{to} is cast to the type of \code{from}. \code{to} is only included in the result if the resulting sequence divides the distance between \code{from} and \code{to} exactly.} \item{by}{\verb{[integer(1) / clock_duration(1) / NULL]} The unit to increment the sequence by. If \code{by} is an integer, it is transformed into a duration with the precision of \code{from}. If \code{by} is a duration, it is cast to the type of \code{from}.} \item{length.out}{\verb{[positive integer(1) / NULL]} The length of the resulting sequence. If specified, \code{along.with} must be \code{NULL}.} \item{along.with}{\verb{[vector / NULL]} A vector who's length determines the length of the resulting sequence. Equivalent to \code{length.out = vec_size(along.with)}. If specified, \code{length.out} must be \code{NULL}.} \item{...}{These dots are for future extensions and must be empty.} } \value{ A sequence with the type of \code{from}. } \description{ This is a duration method for the \code{\link[=seq]{seq()}} generic. Using \code{seq()} on duration objects always retains the type of \code{from}. When calling \code{seq()}, exactly two of the following must be specified: \itemize{ \item \code{to} \item \code{by} \item Either \code{length.out} or \code{along.with} } } \details{ If \code{from > to} and \code{by > 0}, then the result will be length 0. This matches the behavior of \code{\link[rlang:seq2]{rlang::seq2()}}, and results in nicer theoretical properties when compared with throwing an error. Similarly, if \code{from < to} and \code{by < 0}, then the result will also be length 0. } \examples{ seq(duration_days(0), duration_days(100), by = 5) # Using a duration `by`. Note that `by` is cast to the type of `from`. seq(duration_days(0), duration_days(100), by = duration_weeks(1)) # `to` is cast from 5 years to 60 months # `by` is cast from 1 quarter to 4 months seq(duration_months(0), duration_years(5), by = duration_quarters(1)) seq(duration_days(20), by = 2, length.out = 5) } clock/man/year-month-day-count-between.Rd0000644000176200001440000000322314151763504020030 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gregorian-year-month-day.R \name{year-month-day-count-between} \alias{year-month-day-count-between} \alias{calendar_count_between.clock_year_month_day} \title{Counting: year-month-day} \usage{ \method{calendar_count_between}{clock_year_month_day}(start, end, precision, ..., n = 1L) } \arguments{ \item{start, end}{\verb{[clock_year_month_day]} A pair of year-month-day vectors. These will be recycled to their common size.} \item{precision}{\verb{[character(1)]} One of: \itemize{ \item \code{"year"} \item \code{"quarter"} \item \code{"month"} }} \item{...}{These dots are for future extensions and must be empty.} \item{n}{\verb{[positive integer(1)]} A single positive integer specifying a multiple of \code{precision} to use.} } \value{ An integer representing the number of \code{precision} units between \code{start} and \code{end}. } \description{ This is a year-month-day method for the \code{\link[=calendar_count_between]{calendar_count_between()}} generic. It counts the number of \code{precision} units between \code{start} and \code{end} (i.e., the number of years or months). } \details{ \code{"quarter"} is equivalent to \code{"month"} precision with \code{n} set to \code{n * 3L}. } \examples{ # Compute an individual's age in years x <- year_month_day(2001, 2, 4) today <- year_month_day(2021, 11, 30) calendar_count_between(x, today, "year") # Compute the number of months between two dates, taking # into account the day of the month and time of day x <- year_month_day(2000, 4, 2, 5) y <- year_month_day(2000, 7, c(1, 2, 2), c(3, 4, 6)) calendar_count_between(x, y, "month") } clock/man/as_zoned_time.Rd0000644000176200001440000000206514033370511015227 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/zoned-time.R \name{as_zoned_time} \alias{as_zoned_time} \title{Convert to a zoned-time} \usage{ as_zoned_time(x, ...) } \arguments{ \item{x}{\verb{[object]} An object to convert to a zoned-time.} \item{...}{These dots are for future extensions and must be empty.} } \value{ A zoned-time vector. } \description{ \code{as_zoned_time()} converts \code{x} to a zoned-time. You generally convert to a zoned time from either a sys-time or a naive time. Each are documented on their own page: \itemize{ \item \link[=as-zoned-time-sys-time]{sys-time} \item \link[=as-zoned-time-naive-time]{naive-time} } There are also convenience methods for converting to a zoned time from native R date and date-time types: \itemize{ \item \link[=as-zoned-time-Date]{dates (Date)} \item \link[=as-zoned-time-posixt]{date-times (POSIXct / POSIXlt)} } } \examples{ x <- as.Date("2019-01-01") as_zoned_time(x, "Europe/London") y <- as_naive_time(year_month_day(2019, 2, 1)) as_zoned_time(y, zone = "America/New_York") } clock/man/clock-invalid.Rd0000644000176200001440000000755414117646717015157 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/invalid.R \name{clock-invalid} \alias{clock-invalid} \alias{invalid_detect} \alias{invalid_any} \alias{invalid_count} \alias{invalid_remove} \alias{invalid_resolve} \title{Invalid calendar dates} \usage{ invalid_detect(x) invalid_any(x) invalid_count(x) invalid_remove(x) invalid_resolve(x, ..., invalid = NULL) } \arguments{ \item{x}{\verb{[calendar]} A calendar vector.} \item{...}{These dots are for future extensions and must be empty.} \item{invalid}{\verb{[character(1) / NULL]} One of the following invalid date resolution strategies: \itemize{ \item \code{"previous"}: The previous valid instant in time. \item \code{"previous-day"}: The previous valid day in time, keeping the time of day. \item \code{"next"}: The next valid instant in time. \item \code{"next-day"}: The next valid day in time, keeping the time of day. \item \code{"overflow"}: Overflow by the number of days that the input is invalid by. Time of day is dropped. \item \code{"overflow-day"}: Overflow by the number of days that the input is invalid by. Time of day is kept. \item \code{"NA"}: Replace invalid dates with \code{NA}. \item \code{"error"}: Error on invalid dates. } Using either \code{"previous"} or \code{"next"} is generally recommended, as these two strategies maintain the \emph{relative ordering} between elements of the input. If \code{NULL}, defaults to \code{"error"}. If \code{getOption("clock.strict")} is \code{TRUE}, \code{invalid} must be supplied and cannot be \code{NULL}. This is a convenient way to make production code robust to invalid dates.} } \value{ \itemize{ \item \code{invalid_detect()}: Returns a logical vector detecting invalid dates. \item \code{invalid_any()}: Returns \code{TRUE} if any invalid dates are detected. \item \code{invalid_count()}: Returns a single integer containing the number of invalid dates. \item \code{invalid_remove()}: Returns \code{x} with invalid dates removed. \item \code{invalid_resolve()}: Returns \code{x} with invalid dates resolved using the \code{invalid} strategy. } } \description{ This family of functions is for working with \emph{invalid} calendar dates. Invalid dates represent dates made up of valid individual components, which taken as a whole don't represent valid calendar dates. For example, for \code{\link[=year_month_day]{year_month_day()}} the following component ranges are valid: \verb{year: [-32767, 32767]}, \verb{month: [1, 12]}, \verb{day: [1, 31]}. However, the date \code{2019-02-31} doesn't exist even though it is made up of valid components. This is an example of an invalid date. Invalid dates are allowed in clock, provided that they are eventually resolved by using \code{invalid_resolve()} or by manually resolving them through arithmetic or setter functions. } \details{ Invalid dates must be resolved before converting them to a time point. It is recommended to use \code{"previous"} or \code{"next"} for resolving invalid dates, as these ensure that \emph{relative ordering} among \code{x} is maintained. This is a often a very important property to maintain when doing time series data analysis. See the examples for more information. } \examples{ # Invalid date x <- year_month_day(2019, 04, 30:31, c(3, 2), 30, 00) x invalid_detect(x) # Previous valid moment in time x_previous <- invalid_resolve(x, invalid = "previous") x_previous # Previous valid day, retaining time of day x_previous_day <- invalid_resolve(x, invalid = "previous-day") x_previous_day # Note that `"previous"` retains the relative ordering in `x` x[1] < x[2] x_previous[1] < x_previous[2] # But `"previous-day"` here does not! x_previous_day[1] < x_previous_day[2] # Remove invalid dates entirely invalid_remove(x) y <- year_quarter_day(2019, 1, 90:92) y # Overflow rolls forward by the number of days between `y` and the previous # valid date invalid_resolve(y, invalid = "overflow") } clock/man/clock-arith.Rd0000644000176200001440000000322714422221153014607 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gregorian-year-day.R, % R/gregorian-year-month-day.R, R/gregorian-year-month-weekday.R, % R/iso-year-week-day.R, R/naive-time.R, R/quarterly-year-quarter-day.R, % R/sys-time.R, R/week-year-week-day.R, R/weekday.R \name{vec_arith.clock_year_day} \alias{vec_arith.clock_year_day} \alias{clock-arith} \alias{vec_arith.clock_year_month_day} \alias{vec_arith.clock_year_month_weekday} \alias{vec_arith.clock_iso_year_week_day} \alias{vec_arith.clock_naive_time} \alias{vec_arith.clock_year_quarter_day} \alias{vec_arith.clock_sys_time} \alias{vec_arith.clock_year_week_day} \alias{vec_arith.clock_weekday} \title{Support for vctrs arithmetic} \usage{ \method{vec_arith}{clock_year_day}(op, x, y, ...) \method{vec_arith}{clock_year_month_day}(op, x, y, ...) \method{vec_arith}{clock_year_month_weekday}(op, x, y, ...) \method{vec_arith}{clock_iso_year_week_day}(op, x, y, ...) \method{vec_arith}{clock_naive_time}(op, x, y, ...) \method{vec_arith}{clock_year_quarter_day}(op, x, y, ...) \method{vec_arith}{clock_sys_time}(op, x, y, ...) \method{vec_arith}{clock_year_week_day}(op, x, y, ...) \method{vec_arith}{clock_weekday}(op, x, y, ...) } \arguments{ \item{op}{An arithmetic operator as a string} \item{x, y}{A pair of vectors. For \code{!}, unary \code{+} and unary \code{-}, \code{y} will be a sentinel object of class \code{MISSING}, as created by \code{MISSING()}.} \item{...}{These dots are for future extensions and must be empty.} } \value{ The result of the arithmetic operation. } \description{ Support for vctrs arithmetic } \examples{ vctrs::vec_arith("+", year_month_day(2019), 1) } clock/man/Date-setters.Rd0000644000176200001440000000513714073622034014762 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/date.R \name{Date-setters} \alias{Date-setters} \alias{set_year.Date} \alias{set_month.Date} \alias{set_day.Date} \title{Setters: date} \usage{ \method{set_year}{Date}(x, value, ..., invalid = NULL) \method{set_month}{Date}(x, value, ..., invalid = NULL) \method{set_day}{Date}(x, value, ..., invalid = NULL) } \arguments{ \item{x}{\verb{[Date]} A Date vector.} \item{value}{\verb{[integer / "last"]} The value to set the component to. For \code{set_day()}, this can also be \code{"last"} to set the day to the last day of the month.} \item{...}{These dots are for future extensions and must be empty.} \item{invalid}{\verb{[character(1) / NULL]} One of the following invalid date resolution strategies: \itemize{ \item \code{"previous"}: The previous valid instant in time. \item \code{"previous-day"}: The previous valid day in time, keeping the time of day. \item \code{"next"}: The next valid instant in time. \item \code{"next-day"}: The next valid day in time, keeping the time of day. \item \code{"overflow"}: Overflow by the number of days that the input is invalid by. Time of day is dropped. \item \code{"overflow-day"}: Overflow by the number of days that the input is invalid by. Time of day is kept. \item \code{"NA"}: Replace invalid dates with \code{NA}. \item \code{"error"}: Error on invalid dates. } Using either \code{"previous"} or \code{"next"} is generally recommended, as these two strategies maintain the \emph{relative ordering} between elements of the input. If \code{NULL}, defaults to \code{"error"}. If \code{getOption("clock.strict")} is \code{TRUE}, \code{invalid} must be supplied and cannot be \code{NULL}. This is a convenient way to make production code robust to invalid dates.} } \value{ \code{x} with the component set. } \description{ These are Date methods for the \link[=clock-setters]{setter generics}. \itemize{ \item \code{set_year()} sets the year. \item \code{set_month()} sets the month of the year. Valid values are in the range of \verb{[1, 12]}. \item \code{set_day()} sets the day of the month. Valid values are in the range of \verb{[1, 31]}. } } \examples{ x <- as.Date("2019-02-01") # Set the day set_day(x, 12:14) # Set to the "last" day of the month set_day(x, "last") # You cannot set a Date to an invalid day like you can with # a year-month-day. Instead, the default strategy is to error. try(set_day(x, 31)) set_day(as_year_month_day(x), 31) # You can resolve these issues while setting the day by specifying # an invalid date resolution strategy with `invalid` set_day(x, 31, invalid = "previous") } clock/man/seq.clock_year_quarter_day.Rd0000644000176200001440000000416314162373152017721 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/quarterly-year-quarter-day.R \name{seq.clock_year_quarter_day} \alias{seq.clock_year_quarter_day} \title{Sequences: year-quarter-day} \usage{ \method{seq}{clock_year_quarter_day}(from, to = NULL, by = NULL, length.out = NULL, along.with = NULL, ...) } \arguments{ \item{from}{\verb{[clock_year_quarter_day(1)]} A \code{"year"} or \code{"quarter"} precision year-quarter-day to start the sequence from. \code{from} is always included in the result.} \item{to}{\verb{[clock_year_quarter_day(1) / NULL]} A \code{"year"} or \code{"quarter"} precision year-quarter-day to stop the sequence at. \code{to} is cast to the type of \code{from}. \code{to} is only included in the result if the resulting sequence divides the distance between \code{from} and \code{to} exactly.} \item{by}{\verb{[integer(1) / clock_duration(1) / NULL]} The unit to increment the sequence by. If \code{by} is an integer, it is transformed into a duration with the precision of \code{from}. If \code{by} is a duration, it is cast to the type of \code{from}.} \item{length.out}{\verb{[positive integer(1) / NULL]} The length of the resulting sequence. If specified, \code{along.with} must be \code{NULL}.} \item{along.with}{\verb{[vector / NULL]} A vector who's length determines the length of the resulting sequence. Equivalent to \code{length.out = vec_size(along.with)}. If specified, \code{length.out} must be \code{NULL}.} \item{...}{These dots are for future extensions and must be empty.} } \value{ A sequence with the type of \code{from}. } \description{ This is a year-quarter-day method for the \code{\link[=seq]{seq()}} generic. Sequences can only be generated for \code{"year"} and \code{"quarter"} precision year-quarter-day vectors. When calling \code{seq()}, exactly two of the following must be specified: \itemize{ \item \code{to} \item \code{by} \item Either \code{length.out} or \code{along.with} } } \examples{ # Quarterly sequence x <- seq(year_quarter_day(2020, 1), year_quarter_day(2026, 3), by = 2) x # Which we can then set the day of the quarter of set_day(x, "last") } clock/man/as-zoned-time-sys-time.Rd0000644000176200001440000000270714017505732016645 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/sys-time.R \name{as-zoned-time-sys-time} \alias{as-zoned-time-sys-time} \alias{as_zoned_time.clock_sys_time} \title{Convert to a zoned-time from a sys-time} \usage{ \method{as_zoned_time}{clock_sys_time}(x, zone, ...) } \arguments{ \item{x}{\verb{[clock_sys_time]} A sys-time to convert to a zoned-time.} \item{zone}{\verb{[character(1)]} The zone to convert to.} \item{...}{These dots are for future extensions and must be empty.} } \value{ A zoned-time vector. } \description{ This is a sys-time method for the \code{\link[=as_zoned_time]{as_zoned_time()}} generic. Converting to a zoned-time from a sys-time retains the underlying duration, but changes the printed time, depending on the \code{zone} that you choose. Remember that sys-times are interpreted as UTC. If you want to retain the printed time, try converting to a zoned-time \link[=as-zoned-time-naive-time]{from a naive-time}, which is a time point with a yet-to-be-determined time zone. } \examples{ x <- as_sys_time(year_month_day(2019, 02, 01, 02, 30, 00)) x # Since sys-time is interpreted as UTC, converting to a zoned-time with # a zone of UTC retains the printed time x_utc <- as_zoned_time(x, "UTC") x_utc # Converting to a different zone results in a different printed time, # which corresponds to the exact same point in time, just in a different # part of the work x_ny <- as_zoned_time(x, "America/New_York") x_ny } clock/man/iso-year-week-day-setters.Rd0000644000176200001440000000472614007013620017334 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/iso-year-week-day.R \name{iso-year-week-day-setters} \alias{iso-year-week-day-setters} \alias{set_year.clock_iso_year_week_day} \alias{set_week.clock_iso_year_week_day} \alias{set_day.clock_iso_year_week_day} \alias{set_hour.clock_iso_year_week_day} \alias{set_minute.clock_iso_year_week_day} \alias{set_second.clock_iso_year_week_day} \alias{set_millisecond.clock_iso_year_week_day} \alias{set_microsecond.clock_iso_year_week_day} \alias{set_nanosecond.clock_iso_year_week_day} \title{Setters: iso-year-week-day} \usage{ \method{set_year}{clock_iso_year_week_day}(x, value, ...) \method{set_week}{clock_iso_year_week_day}(x, value, ...) \method{set_day}{clock_iso_year_week_day}(x, value, ...) \method{set_hour}{clock_iso_year_week_day}(x, value, ...) \method{set_minute}{clock_iso_year_week_day}(x, value, ...) \method{set_second}{clock_iso_year_week_day}(x, value, ...) \method{set_millisecond}{clock_iso_year_week_day}(x, value, ...) \method{set_microsecond}{clock_iso_year_week_day}(x, value, ...) \method{set_nanosecond}{clock_iso_year_week_day}(x, value, ...) } \arguments{ \item{x}{\verb{[clock_iso_year_week_day]} A iso-year-week-day vector.} \item{value}{\verb{[integer / "last"]} The value to set the component to. For \code{set_week()}, this can also be \code{"last"} to adjust to the last week of the current ISO year.} \item{...}{These dots are for future extensions and must be empty.} } \value{ \code{x} with the component set. } \description{ These are iso-year-week-day methods for the \link[=clock-setters]{setter generics}. \itemize{ \item \code{set_year()} sets the ISO year. \item \code{set_week()} sets the ISO week of the year. Valid values are in the range of \verb{[1, 53]}. \item \code{set_day()} sets the day of the week. Valid values are in the range of \verb{[1, 7]}, with 1 = Monday, and 7 = Sunday. \item There are sub-daily setters for setting more precise components. } } \examples{ # Year precision vector x <- iso_year_week_day(2019:2023) # Promote to week precision by setting the week # (Note that some ISO weeks have 52 weeks, and others have 53) x <- set_week(x, "last") x # Set to an invalid week invalid <- set_week(x, 53) invalid # Here are the invalid ones (they only have 52 weeks) invalid[invalid_detect(invalid)] # Resolve the invalid dates by choosing the previous/next valid moment invalid_resolve(invalid, invalid = "previous") invalid_resolve(invalid, invalid = "next") } clock/man/seq.clock_iso_year_week_day.Rd0000644000176200001440000000425614162373152020046 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/iso-year-week-day.R \name{seq.clock_iso_year_week_day} \alias{seq.clock_iso_year_week_day} \title{Sequences: iso-year-week-day} \usage{ \method{seq}{clock_iso_year_week_day}(from, to = NULL, by = NULL, length.out = NULL, along.with = NULL, ...) } \arguments{ \item{from}{\verb{[clock_iso_year_week_day(1)]} A \code{"year"} precision iso-year-week-day to start the sequence from. \code{from} is always included in the result.} \item{to}{\verb{[clock_iso_year_week_day(1) / NULL]} A \code{"year"} precision iso-year-week-day to stop the sequence at. \code{to} is cast to the type of \code{from}. \code{to} is only included in the result if the resulting sequence divides the distance between \code{from} and \code{to} exactly.} \item{by}{\verb{[integer(1) / clock_duration(1) / NULL]} The unit to increment the sequence by. If \code{by} is an integer, it is transformed into a duration with the precision of \code{from}. If \code{by} is a duration, it is cast to the type of \code{from}.} \item{length.out}{\verb{[positive integer(1) / NULL]} The length of the resulting sequence. If specified, \code{along.with} must be \code{NULL}.} \item{along.with}{\verb{[vector / NULL]} A vector who's length determines the length of the resulting sequence. Equivalent to \code{length.out = vec_size(along.with)}. If specified, \code{length.out} must be \code{NULL}.} \item{...}{These dots are for future extensions and must be empty.} } \value{ A sequence with the type of \code{from}. } \description{ This is a iso-year-week-day method for the \code{\link[=seq]{seq()}} generic. Sequences can only be generated for \code{"year"} precision iso-year-week-day vectors. If you need to generate week-based sequences, you'll have to convert to a time point first. When calling \code{seq()}, exactly two of the following must be specified: \itemize{ \item \code{to} \item \code{by} \item Either \code{length.out} or \code{along.with} } } \examples{ # Yearly sequence x <- seq(iso_year_week_day(2020), iso_year_week_day(2026), by = 2) x # Which we can then set the week of. # Some years have 53 ISO weeks, some have 52. set_week(x, "last") } clock/man/weekday_code.Rd0000644000176200001440000000171314011253136015030 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/weekday.R \name{weekday_code} \alias{weekday_code} \title{Extract underlying weekday codes} \usage{ weekday_code(x, ..., encoding = "western") } \arguments{ \item{x}{\verb{[weekday]} A weekday vector.} \item{...}{These dots are for future extensions and must be empty.} \item{encoding}{\verb{[character(1)]} One of: \itemize{ \item \code{"western"}: Encode weekdays assuming \code{1 == Sunday} and \code{7 == Saturday}. \item \code{"iso"}: Encode weekdays assuming \code{1 == Monday} and \code{7 == Sunday}. This is in line with the ISO standard. }} } \value{ An integer vector of codes. } \description{ \code{weekday_code()} extracts out the integer code for the weekday. } \examples{ # Here we supply a western encoding to `weekday()` x <- weekday(1:7) x # We can extract out the codes using different encodings weekday_code(x, encoding = "western") weekday_code(x, encoding = "iso") } clock/man/iso_year_week_day.Rd0000644000176200001440000000442214416030722016072 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/iso-year-week-day.R \name{iso_year_week_day} \alias{iso_year_week_day} \title{Calendar: iso-year-week-day} \usage{ iso_year_week_day( year, week = NULL, day = NULL, hour = NULL, minute = NULL, second = NULL, subsecond = NULL, ..., subsecond_precision = NULL ) } \arguments{ \item{year}{\verb{[integer]} The ISO year. Values \verb{[-32767, 32767]} are generally allowed.} \item{week}{\verb{[integer / "last" / NULL]} The ISO week. Values \verb{[1, 53]} are allowed. If \code{"last"}, then the last week of the ISO year is returned.} \item{day}{\verb{[integer / NULL]} The day of the week. Values \verb{[1, 7]} are allowed, with 1 = Monday and 7 = Sunday, in accordance with the ISO specifications.} \item{hour}{\verb{[integer / NULL]} The hour. Values \verb{[0, 23]} are allowed.} \item{minute}{\verb{[integer / NULL]} The minute. Values \verb{[0, 59]} are allowed.} \item{second}{\verb{[integer / NULL]} The second. Values \verb{[0, 59]} are allowed.} \item{subsecond}{\verb{[integer / NULL]} The subsecond. If specified, \code{subsecond_precision} must also be specified to determine how to interpret the \code{subsecond}. If using milliseconds, values \verb{[0, 999]} are allowed. If using microseconds, values \verb{[0, 999999]} are allowed. If using nanoseconds, values \verb{[0, 999999999]} are allowed.} \item{...}{These dots are for future extensions and must be empty.} \item{subsecond_precision}{\verb{[character(1) / NULL]} The precision to interpret \code{subsecond} as. One of: \code{"millisecond"}, \code{"microsecond"}, or \code{"nanosecond"}.} } \value{ A iso-year-week-day calendar vector. } \description{ \code{iso_year_week_day()} constructs a calendar from the ISO year, week number, and week day. } \details{ Fields are recycled against each other using \link[vctrs:vector_recycling_rules]{tidyverse recycling rules}. Fields are collected in order until the first \code{NULL} field is located. No fields after the first \code{NULL} field are used. } \examples{ # Year-week x <- iso_year_week_day(2019:2025, 1) x # 2nd day of the first ISO week in multiple years iso_days <- set_day(x, clock_iso_weekdays$tuesday) iso_days # What year-month-day is this? as_year_month_day(iso_days) } clock/man/as_date.Rd0000644000176200001440000000344514427270231014017 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/date.R \name{as_date} \alias{as_date} \alias{as_date.Date} \alias{as_date.POSIXt} \alias{as_date.clock_calendar} \alias{as_date.clock_time_point} \alias{as_date.clock_zoned_time} \title{Convert to a date} \usage{ as_date(x, ...) \method{as_date}{Date}(x, ...) \method{as_date}{POSIXt}(x, ...) \method{as_date}{clock_calendar}(x, ...) \method{as_date}{clock_time_point}(x, ...) \method{as_date}{clock_zoned_time}(x, ...) } \arguments{ \item{x}{\verb{[vector]} A vector.} \item{...}{These dots are for future extensions and must be empty.} } \value{ A date with the same length as \code{x}. } \description{ \code{as_date()} is a generic function that converts its input to a date (Date). There are methods for converting date-times (POSIXct), calendars, time points, and zoned-times to dates. For converting to a date-time, see \code{\link[=as_date_time]{as_date_time()}}. } \details{ Note that clock always assumes that R's Date class is naive, so converting a POSIXct to a Date will always retain the printed year, month, and day value. This is not a drop-in replacement for \code{as.Date()}, as it only converts a limited set of types to Date. For parsing characters as dates, see \code{\link[=date_parse]{date_parse()}}. For converting numerics to dates, see \code{\link[vctrs:new_date]{vctrs::new_date()}} or continue to use \code{as.Date()}. } \examples{ x <- date_time_parse("2019-01-01 23:02:03", "America/New_York") # R's `as.Date.POSIXct()` method defaults to changing the printed time # to UTC before converting, which can result in odd conversions like this: as.Date(x) # `as_date()` will never change the printed time before converting as_date(x) # Can also convert from other clock types as_date(year_month_day(2019, 2, 5)) } clock/man/figures/0000755000176200001440000000000014423751216013571 5ustar liggesusersclock/man/figures/lifecycle-defunct.svg0000644000176200001440000000242414423751216017701 0ustar liggesusers lifecycle: defunct lifecycle defunct clock/man/figures/lifecycle-maturing.svg0000644000176200001440000000243014423751216020074 0ustar liggesusers lifecycle: maturing lifecycle maturing clock/man/figures/logo.png0000644000176200001440000005172514416016713015247 0ustar liggesusersPNG  IHDRޫhgAMA a cHRMz&u0`:pQ<bKGDtIME : @RIDATxy%G}emH}ߑ ؀X !$lAmn 3sfzLrf%qcc,1,@ Tz7DDfdkWnU|u޻e%!9x4nDrOZ[R۵-i&A7R'EXrs 5 @GiiANV-yA">$^c"X5:*ʒ "HE r+ڧrl*=OpxV.?]FC:HLE\ }z-W96RLpg+.ppT1E*tYr?q͢Vl ,!xVT7oP4S& u/= 4/"B < zv4:R"CDTd'U~um^vIB|< x;Ź'UN^iR6]Vs2RѪ =:{Zz-'Ĩ`#XKDW;f/,J% hW|ww&!փ T9\΂N+Oi5s`Nx<DhȿYi[$Z'uc?SΪ58>bku)avEs g揷  Zy:/ؚ!qH.сU-jpQvFJcVon\i{1/cNC_Bc`M5nO:7{"xn έ"s6 6l0q6_qO;iiÍ@Å7/@Mbo˿ƞUx7wpZŸէHuL"wK[?J?s;>tÅ@C7-@.wsXOmP8b,t5;&nD>>ʺu]\*ܕށ$n;.U9=Mh-Zz%MZ!>>>p inf*,.8zBl1v|FK;?Zeo%޴s'FxZT9q|q=b+pI Nxq*vqZ43N,& n!x+j; Y_!Hb?]hm<.W9U8f᧴Xy557F f-A.4EpAeDaYf#xp! mKcw,ܶ]xK[6o#pblά 1l}+޶d"oW,Y;oFM<8.lUL,ĉe>D[uPZ! |0.\%*.<3 1.v3#0/ƥ~v v,U4;duOO@iK*n2uf1ƾmNc"xN ΫDe_XMNzlq:O+ qWWIn^GCNcŹ5AtЌs7El+HmZ4ےm=c#xa,}~Ֆ=QO:pWw]5 A&+[ז-UZ04 pZvțǗljߑGKcYo` 2G!x\ߝze~s[VwafZxg!QΉ{w˿qb8uŧhGh9;K 2 \5Ko^9 ]\0v޾QwaşgeEMУԫ) 2F/a2;&]k(vD&6~֘e ~l n2i \we~Wn2# ӗ-~QI˭=A =+;i2[:@sFSxeҿ9A 5ǟ?|t$oW܉fĬ䚼l'^~ ţGǽIW可":~_Ϭ].e~GvEfmI:-stx.yx۽~);/-~&|{eG._?o`_rWwDe~t(;0}i1#K`EG#?׀b%-GEߑHKNJ~š}}qp?Ѧ"v#ISWxe~Htel8%< F/|wWwQ]w-^RIe/ܞ|Ǯ;NmSvDp 9+;'llqg˳ؕyv2Je2?wRwa;MbܲŞ*nq@o`rؕ֋r-{c(;'2s&.[Mz7qQe&gv'2o?rc(;/)c-eO֏n?e۟N1sXl_g1=l,˼58;iV(|u{ooS/`$/[Zz-~$m{}Sx 2űBOG-8yDei'?9=_?oF32'.{@ӗ->-/On->z/{+ }j\6 ӗ-BKUlq&q ^Ŝu^ ӗ-HSeoRxl?֯Em/v]@ &'xl]Xez;gwҐ e~S;zg--N[+m C_aǼ)&.[-[lz!\yh?>ܯpZr[e~LRPpp=M⼪ƙvj˴ZSޡZܞr ޳l?>e~~&a?N?We[CKOgA% 7=ePp/e2v||-#슞d9"n>e~o҇/C( vpeUaeW*[<8^%2'*ǖ=|ml|sj( 81mbUW5-G6'dr:4T9%XKjpDeу+[<#ؕ=T9>-{M( iO\5z-O&_;l1Λ:.|\4x8-to6 2Zh)<ԃ'P2ح$*R+X3r'sN PǔKNf^~ݖӗ->@K?Ⱥز6jѬoі{2P0 i&.[|-[\ue2P c qn@8px2۴;Zz+[Yik>zAbw˜liZqZΥsN y65q<"z s2l:([ԶRI,c+ tP*eXQ~,7=ljBڳo݀C>C/ITq)|b{f7 .u4+I o@FK `0+0W na0f3@F p@ #8 `0a0f3@F p@ #8 `0a0f3@F p@ #8 `Q9KLi7E8u炒6rOA .`P^0*1DcݷLeǴ4C6rÁ'0+_ (24*6;j案Ʊ`M6~&'f_{u+8dbPƛGu2TD%Oհq .bq_woGYY9ͿV_JIGcXS׾Kumes(4yoE(/fBPdx𹒧Q@[3Kb8)^q)b=nUxo-@Nlm2 m_KQeV>U5@de^2ja]|{ #]"6MO0YC 7GgHbkFl$Rfx,oƑי)A}+v&My:2(l y%u:}ځ;1DZQ2{19-GޜXZЇ6?vSfdZμ>Ӥ"\"%j<5#$YYY#o6E+LyC2Hv6vӵo1d3h+Q ѵdP)N=}OK?o<qc;FE]{ f7k y $ 4`):‰`gVK#+Dq;5 e.7+N[eFlTdwI2ciSu!ő8SLi|U,r2U0'#foKA!ߦWY$!I"ʪ#k7H}BuՍ 'ҵ %ӿ0,q?0 Mș"6AM9W*k!B%k%vƓ&Črj%r#iIE<且6qޕԥ.#n@^D^d(![YEk,߁uȋxˈCͥ4merϪpIN~<ym췅oŋm#m$Z3;j̎.+RP/xq5ܶ$.d6$Kbh:vL j jh]P`wȃ 1\Pʹ(E@W.<{NEk,:EN6-:g̿F4gߌ$G|M{ݔb-khR苮 ]"uA@z,rn[FB|\?{8\78+&)A4MTiHLڳ*9*œ_nAFvLڹ֋ OpIMlKvAuVH uQpynjx(s: og7yVE[Z.pz7G^PU WיF|jā{8A*iOkc1uk|OEcɳ+2ٌEYi]0O9y6*d(#*K#>.0J(,A|l[$^xB'3 aj_V3nzW۵y邡_1Ѯ(]A>0ƒnK7 c\F2u peI(Kڠz~zqR L⢃9w}k' &lGG&P,N ԛ;MDF &Ql_֣uSgJ~[#7X|((ю7WyVA$,=OŠ|RG}&c4X NB oM@慹 cX)_z} mzsZ.kdP:yƚ=QQrFlbtȒ6u2bʈg8sd&yUcuF荫 {6&pK-gُiȲ&?_%iItmݽǁ{4tS>dˇ),?kJYtN!eVBLվԟc<7ԉ$~s3}=lLt}uB[[`Yf3sFY)I&]uٚs ja pF^P1| 7M[qK;pl?WCvEh]{mXW/4&bZP?6Yhd%|o@>eMz$ƛS/iY>.|~+dNRY_KytxG/hjngJf _nFMd"h=, ]s)Wm+hWYd5on>PͭdUShbL`#Mנ}> zw؞-$;|]Ol\!\)Y@KaIL"lWd ;12&qޗmqW5 ;~c?o6ͼ4OǩAMt!Yq55fMdSx56%?]EcSkjTN쳿n!X[3P$5RNC vb\W7عW"s\KpY qI~q֨r"ЮҾߵ>7m rtῥj^:%S qhHYaOnN_ |;ōi%>6`Ώٰ$a؉otm]l3X|lKTNM&_2D~Yfap<G,)ma/Ԩ%5#sivig[W_6dԁX-(SdDiPSW/\*wAțZ(M- k,+7{0nkx|{if"0r鿫vT)!%cF߆GVFVKW<2&fByYVmO&ҮagdT_d p^dOKȫ_@^%A8ݸz #\kqor2.,S}H2PB.յ%o@Oށc}ֿޡskyB]3^,EWiCYE{q$֝26 BKN61?vCso%jHΒhmcY+ZbC{| }FWìs.m/2ȫ .(%&:pTOR횁%YIh}KN3BIC5+Ɉ ϕ%^'WN47#_H2V97AD׀s՜^<-Ц=тeaV.sB41DwөS)HX>B:HFS4qvq%tx/t*~~؆xb g[]+564mcl>B, yge_5%v捝lS$f 8w-O0FIڏM `'f#^ᷓKtI۬W/1Dz&Y,ێأsG|,)%.o3V:L϶LhSLn~G)f;`&R%!&]|G6{勜J[<'A[`b]%sV>&Vͪ6_i[a%}MțH_~%L/ҷֿ!a&b%ֿfo|JQJ]YtY!'CJW*xKnO>6f kEc$֏DX.9l}ǚW3iUUHJA˹xG {}u"[CݹʧZ$d^Xx}Ѯ@}M綞i,S嵵$m: b^PR)ԣ}xW?Dq MSM"UrBڬ,FJ< +#bkB&oiQ" TNh~c'xV0Ț?{g<7K ,y im`>[=1z~lVJ>oYvOl;T?[OiHLYLB-]?"Y5u' 75$irH4(\R;xIDPzeC 3bzY} QÐ*Bd?_ǻ"gL?f' n(  ROJb6~E]lR®i:uafK@X/87斦H¾]KLwRjkڧgYY1贔){2*DddY?% l.G$$k&cxVxq:]r\@&T /xY5Er =iǘr72!Y+4uVx΍SwBzOwR}ud&lتt*ϖ ɦI\|.=(gsmdp3'V{L[} R!MZ4f,&A=~GnDZ̊0 ".}lƻ"Au''nt\.{yIAHSJBY~8 o:4Mǜ\i"fC>Z"EnTS5}e-i|Q rN.'P5߶c-ק=Qo;a\Kp6Ĥ= ! 1ֲ|wԵ{AP#^_#^d,.D++qC'u-+`>2li)Vj;]$=.dҽ%rJNdeCte*pY)%FHi̢&xI)QZ?)_WY{OHIWae5,li՞sSbc`ot` /R9)f-YֲxSKW|7&'=LTaQ=Gx2VW?-de/dxVyv Tˀ*Q}7`Sq1EboiJTF)iX5c:uN f/ |]{P KokduSv>%u3O a;JE ^LEU@bzv9f/fMoksi/n e䝪c&bĘw̱ݻ{o?2YL{!n#}vG,r#>wu4'v IX p @U_[j^j[?DW5UZJ[F c T];w 4_arc=ϫ",:>M3]-y.y!r*˞(p|8\Co=$Q9"߃Y pqa}A\s 4}+g9Cy%wRr2a-zm7c% ]vLL;xG[t{_bE߶5aj/aT 1~m#y+ͦ &%fGiH W~qWif?z;+_k)_*z^hɘgW]XdL3eOkq:б12_BV=1TIW]+`motw sJxP956?69c/xQNЎYg|]oRCIf%>.񢪹gnF 4*[`U+kx{|ڰ,cbsc`׿l[(Y?9Fc4;!= ppLFddH8;1 yGJo?PY%X)Q1<@rI H˥ɪ\}$l RLAj\:vS19&׎b̤Grp tN )_ĺЃ.`FjADZY=ج\R@:)pA8źsoMM7+Ea&ɻxG ClJz:A:6OgȮm5eb].iiA>3Ξ6uP!foP[1S%ݾG s'd|O/?=Z(4Y[G!B{?s5KRhl ˸ %'8KE)LjHN4=WxԨ: ޾G\ېNOg a }vT'#Q.ro)I1,tNb]۝b lY<52'Ga`*Vn0I Bs$ s<%2:w9%gݹ`:K4%ЖnNqClSDkɛ}1ݕceȭ0Ofݳmd4XR9۟dfeZz? Н ugXAۑ=MpEiӹp{ M&fdL`5%dbikYXaL?Z])٩U8׭f̄N 2bv,`q%.f۽Oܝj՞dDp =I/o{&^;]w ZwB!?+ lc$7eZV@͊I:`Bb̘e[2Lt]Itaf&n鉷qCV+4Lrz<JmAܖ:koӹla= fILEqni9>C8h?4%z2vnŶ]=dWCC:V΢ɨEFDjG ɈfIt^| ~'OOmm*WY [{& `=sZuOK[8]N tx MJlW̾-~ҤxY̢ ]LxKz(p"HZ6ўOzs4Ts ?i2Y+;ܻvn]: ĕ|~l<7.yӴ]~>W:TNueɊnP0 +mҾg ހ*\@^;h{\0%:fݻd{ @.jþ_P>ȌaG,yZ5MVz9kzfүNWƋDhҹo f_(dr{*MGYO%:!I;~mΜ>+Ŀ?_6i'dpMNtVm` ȩS!u.?l׽#C}SY([C5a++mHVhIXj}3cv<oo /܍m<-4mVai?uz(rBWv#?%SwEu8csbvsRCHbiZf|ju[z'(Scv\z{Ð@匘v_rpϖZP6bHS=N^ROE[ȐYorwP$$ˊ6V3KevԾXz WMOuQ~W{or/Έ>f7sw+/mMCo P ~B]NcKHMDRTϊG-k*'_0b&6F%ktHS ̿YiDxI՜˰xZ,%qʦVJhDi^1}4eMuLɂ$kK2=yqih ;o_UV>Ѣ6W?\BjjĮ-z#bG.䦦 wzLVWz<<7g!kovDw<|&H 7銒arɐ'JqV6 "]8rі^_Bk2}}^VN< fcxOD8{ dT5_'|Ʋw%"OdBtB R&3%AŒ?X"IfUTϩM{_ښreqN!NNb%gLD6^ n Pz&4Di?k*6DI2=g)dcMz}*mc,RwFȎdNaHe,7lPɑؾJ֠{{؈ʩqC~$6?#Z'={ Ը>eM%4=S'͜]>T,YvT3 P !K*?)?vIl;p b6Ԏ & }NkH뻒@s?f7CI-I('?X%~E~ۍ_[#b2rv`+mzw)Fuț.h$I,US\^T5$pw, +ҌUnMs㢧kboYBSCnUSn:őD>MKA5]TZc&yנi"6{Lw6!4ڳ*Dl/S?ȍnv`)< ~{)R۽AҺڲBI2rss )wN xI:t2eXvT̆6@tz٠"irk#BEn')щo7t3vx1xbaz|zJ6##LzaqOÏg܍鼡+]ni_θчב^y \dL-' ˘ G\ecrWa2nĶi9-MſduVFC._Hx$QA߾6\]mzַd>kkr?$9k'$D `Vt||^TꁍF%7LAPn)u.vugSh%jdDF;v*cHLbCe,S\hc}N޸۪ΞtĕQI=f;ɱaá,vSk=z[[? ~RWe&9lGt+V99س`e$ I72R)cI휐PF^vʜܮ$sx-)t6Չ# [bWցDr[jMglmt4f;jI#ooH7aĶu-. GSkA.E;C![چQۣ%=lt&c'#YKQڐls\}nhJW6Ȧ$۩@j·[Y2Ԟۘ@35-~껤6aԟG/}v/"7qp;lc\a%{U;JĎR{xXɘ TF񬈐Z2:ŦLb#o3ד\vwg褩)qC*؎nI,M~ oT..\ҹ؄ddN!\6kKA$,qT\o'JĵVtA#Je:q2#Q$(iei|(I_J悔YݱSqy I$~ eJ߆ao؜nP=i[pp.vƓLi͂kZSwktq5 Yɘ`cH^e,kG_Nd'|Ht4J.OkǬ-5ͦO؎azQɨkjam8›ݐr s.0dX  22:a5(>5rz;Sgsv,]9È+ qJl ]|=`OY{@A7ܑQ\q6CF7[˭QzTdȿPU* ٞd18@nl5 =r {ob&~eʀ&Ө5HE"Oێe79 f{eOK͸91,h>3#8BQZAr 3;+;]gXzԀ9@\?if:7pxt z mt[YΨ fl8+3tG]O|FF|ws-PȻ0q8}<Ҏ3CG2̂fAY@h-Ap0a0f3@F p@ #8 `0a0f3@F p@ #8 `0a0f3@F p@ #8 `0a08F=0 || pXqld8Nj gT`<ց;Lr@!ÂpQ vFWNUvEu8 t.tbvq NȒOw>4cD'}d rF>C]%Uǔܠp_$q@@)8!6 *He-kq{pZz<\m*2'07XM̔m]8q@@\T5SC s۴ -GKkU G?7ѐPRXHkO']]G7VU86FJ8O_i{YϲCVXWZ/ K-][9܀p6 "8-bh88hByY[E qP>Ⱥgg-:ahIˍ5.Ի9~*ǔվ`M{"??8.6IsOss뚽[G`?T8%z 꼎 rbX 8Ē5 ;ƈstiuZz-O%7'Rh:LF``^`YXАwQX~1Vs[< `PΩskp|Dj̆Ź~BKU$;d?k cIOB;ٟ|e?p;ǂ,EpzTLk53e dDãXqHkb: fxX0S_ʎa f>.Oi±<Ua^QqnUZYZO~5 5^; O`xoR6>~1ՑnBb6ǂUr׀8YAڧͷh<_fhN˺Eex8Qx" e8Z~씧YQi~ibm>d EC~*'\QxFBh_n/fq:ٺ׷5^HwӐ+0ҭ^KΞq qaȸ1?6izwnY~-"? vm|r%.+[L`_b\wv(՘8jTnjs&Zzv:7N1˺yy; =j\02>qo:l*घ6`],'bK2:;\nj9+BOlޞY6 5SX?YVnK{f~xesev̀i#gOTס͗iu<|3-ۤiIqur9 yu^"q([ BL_>V3>pnEs fک $'0/JC~goО6>~)`b4#wyc msC=¾~f.ˌ^Y%f"lqw([ 8hL^lp'οy5wOs]."Õ֭~4prJl&e~=HKco5Pa{*e.(Wٲ燲ŀ0}5-[ ۙ7#1KNe^I]~ ^hAbc:h_䃜 wۓ۟Wi%ϳeo e9L^fߴu7C`WtRcOlbL_w%-Zffb K 0_+l9l(äe~fǟgeEMdeM;e^oӐ5v4P8{pe~1TS#4ݥ$&oǕf]lte~_qgoyߡ‘A`Eޯ vl|lte~ߣױ7CUߡtRnW5SE'= řt˺߬b=zđe}]-PAF. eӕ=mɍmqȆ+[s^Rl1+3=|oߡ‘O`w]elqo~ \sg2NO┃Yđ]8e1=M)iNj-t=DS=h5$v{iUjP\n >#AvcavDƽ^,:r\lO1?_৿̲.yt9z\2e%N~2~mlq5y*,2&*3m2CW-`1=m1-n+W k %;`p1,O'2/A]~#W#-N&Ɖs-e~q?*mo>NYc=Ĺ xZnryu]?!iʴK&:>Z=΁KG|>X\aj9/F-YROP|,6=4.M8zc >T ~̹ˬ[KG\te~OK䛜u叛@Fl [,vB]"~-Me~ GW5ҡ]8Ki ({lmT~~HCV-}6mV*@ V~[x-[|czK(93S8n2yn}]h[rh\CWxRD[۴lq2oky:*F!=>*.ܶ)[VZG,22P.^)3Q#,3 d=/&p3BqnY?bWYꑱY,!XÅ7[U)-.Ҳ>G?p"pÕ->96o}$e~4ɕv,"xF'Uѐw-kug9iθc6X\o3P7clvYu`²c}ýre~-@@Yƭsc1N[.ޚ|Ri@ xw?_RX_=N{4&'iUxY!oN[ q",Uޅ3РbtxǪ{vE@[;UBϝU2eW*Wwj-9De?M(k]_?DVwo(eH+[|$rJ:?uTDYp~}/'%tEXtdate:create2023-04-13T14:58:10+00:00~)%tEXtdate:modify2023-04-13T14:58:10+00:00#ftEXtSoftwareAdobe ImageReadyqe<IENDB`clock/man/figures/lifecycle-archived.svg0000644000176200001440000000243014423751216020033 0ustar liggesusers lifecycle: archived lifecycle archived clock/man/figures/lifecycle-soft-deprecated.svg0000644000176200001440000000246614423751216021330 0ustar liggesusers lifecycle: soft-deprecated lifecycle soft-deprecated clock/man/figures/lifecycle-questioning.svg0000644000176200001440000000244414423751216020620 0ustar liggesusers lifecycle: questioning lifecycle questioning clock/man/figures/lifecycle-superseded.svg0000644000176200001440000000244014423751216020412 0ustar liggesusers lifecycle: superseded lifecycle superseded clock/man/figures/lifecycle-stable.svg0000644000176200001440000000247214423751216017526 0ustar liggesusers lifecycle: stable lifecycle stable clock/man/figures/lifecycle-experimental.svg0000644000176200001440000000245014423751216020745 0ustar liggesusers lifecycle: experimental lifecycle experimental clock/man/figures/lifecycle-deprecated.svg0000644000176200001440000000244014423751216020347 0ustar liggesusers lifecycle: deprecated lifecycle deprecated clock/man/duration_precision.Rd0000644000176200001440000000113414027170177016314 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/duration.R \name{duration_precision} \alias{duration_precision} \title{Precision: duration} \usage{ duration_precision(x) } \arguments{ \item{x}{\verb{[clock_duration]} A duration.} } \value{ A single string holding the precision of the duration. } \description{ \code{duration_precision()} extracts the precision from a duration object. It returns the precision as a single string. } \examples{ duration_precision(duration_seconds(1)) duration_precision(duration_nanoseconds(2)) duration_precision(duration_quarters(1:5)) } clock/man/reexports.Rd0000644000176200001440000000070214040275170014441 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/tzdb.R \docType{import} \name{reexports} \alias{reexports} \alias{tzdb_names} \alias{tzdb_version} \title{Objects exported from other packages} \keyword{internal} \description{ These objects are imported from other packages. Follow the links below to see their documentation. \describe{ \item{tzdb}{\code{\link[tzdb]{tzdb_names}}, \code{\link[tzdb]{tzdb_version}}} }} clock/man/time_point_count_between.Rd0000644000176200001440000000741414151750124017505 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/time-point.R \name{time_point_count_between} \alias{time_point_count_between} \title{Counting: time point} \usage{ time_point_count_between(start, end, precision, ..., n = 1L) } \arguments{ \item{start, end}{\verb{[clock_time_point]} A pair of time points. These will be recycled to their common size.} \item{precision}{\verb{[character(1)]} One of: \itemize{ \item \code{"week"} \item \code{"day"} \item \code{"hour"} \item \code{"minute"} \item \code{"second"} \item \code{"millisecond"} \item \code{"microsecond"} \item \code{"nanosecond"} }} \item{...}{These dots are for future extensions and must be empty.} \item{n}{\verb{[positive integer(1)]} A single positive integer specifying a multiple of \code{precision} to use.} } \value{ An integer representing the number of \code{precision} units between \code{start} and \code{end}. } \description{ \code{time_point_count_between()} counts the number of \code{precision} units between \code{start} and \code{end} (i.e., the number of days or hours). This count corresponds to the \emph{whole number} of units, and will never return a fractional value. This is suitable for, say, computing the whole number of days between two time points, accounting for the time of day. } \details{ Remember that \code{time_point_count_between()} returns an integer vector. With extremely fine precisions, such as nanoseconds, the count can quickly exceed the maximum value that is allowed in an integer. In this case, an \code{NA} will be returned with a warning. } \section{Comparison Direction}{ The computed count has the property that if \code{start <= end}, then \verb{start + <= end}. Similarly, if \code{start >= end}, then \verb{start + >= end}. In other words, the comparison direction between \code{start} and \code{end} will never change after adding the count to \code{start}. This makes this function useful for repeated count computations at increasingly fine precisions. } \examples{ x <- as_naive_time(year_month_day(2019, 2, 3)) y <- as_naive_time(year_month_day(2019, 2, 10)) # Whole number of days or hours between two time points time_point_count_between(x, y, "day") time_point_count_between(x, y, "hour") # Whole number of 2-day units time_point_count_between(x, y, "day", n = 2) # Leap years are taken into account x <- as_naive_time(year_month_day(c(2020, 2021), 2, 28)) y <- as_naive_time(year_month_day(c(2020, 2021), 3, 01)) time_point_count_between(x, y, "day") # Time of day is taken into account. # `2020-02-02T04 -> 2020-02-03T03` is not a whole day (because of the hour) # `2020-02-02T04 -> 2020-02-03T05` is a whole day x <- as_naive_time(year_month_day(2020, 2, 2, 4)) y <- as_naive_time(year_month_day(2020, 2, 3, c(3, 5))) time_point_count_between(x, y, "day") time_point_count_between(x, y, "hour") # Can compute negative counts (using the same example from above) time_point_count_between(y, x, "day") time_point_count_between(y, x, "hour") # Repeated computation at increasingly fine precisions x <- as_naive_time(year_month_day( 2020, 2, 2, 4, 5, 6, 200, subsecond_precision = "microsecond" )) y <- as_naive_time(year_month_day( 2020, 3, 1, 8, 9, 10, 100, subsecond_precision = "microsecond" )) days <- time_point_count_between(x, y, "day") x <- x + duration_days(days) hours <- time_point_count_between(x, y, "hour") x <- x + duration_hours(hours) minutes <- time_point_count_between(x, y, "minute") x <- x + duration_minutes(minutes) seconds <- time_point_count_between(x, y, "second") x <- x + duration_seconds(seconds) microseconds <- time_point_count_between(x, y, "microsecond") x <- x + duration_microseconds(microseconds) data.frame( days = days, hours = hours, minutes = minutes, seconds = seconds, microseconds = microseconds ) } clock/man/posixt-formatting.Rd0000644000176200001440000001405314133632415016112 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/posixt.R \name{posixt-formatting} \alias{posixt-formatting} \alias{date_format.POSIXt} \title{Formatting: date-time} \usage{ \method{date_format}{POSIXt}( x, ..., format = NULL, locale = clock_locale(), abbreviate_zone = FALSE ) } \arguments{ \item{x}{\verb{[POSIXct / POSIXlt]} A date-time vector.} \item{...}{These dots are for future extensions and must be empty.} \item{format}{\verb{[character(1) / NULL]} If \code{NULL}, a default format is used, which depends on the type of the input. Otherwise, a format string which is a combination of: \strong{Year} \itemize{ \item \verb{\%C}: The year divided by 100 using floored division. If the result is a single decimal digit, it is prefixed with \code{0}. \item \verb{\%y}: The last two decimal digits of the year. If the result is a single digit it is prefixed by \code{0}. \item \verb{\%Y}: The year as a decimal number. If the result is less than four digits it is left-padded with \code{0} to four digits. } \strong{Month} \itemize{ \item \verb{\%b}, \verb{\%h}: The \code{locale}'s abbreviated month name. \item \verb{\%B}: The \code{locale}'s full month name. \item \verb{\%m}: The month as a decimal number. January is \code{01}. If the result is a single digit, it is prefixed with \code{0}. } \strong{Day} \itemize{ \item \verb{\%d}: The day of month as a decimal number. If the result is a single decimal digit, it is prefixed with \code{0}. } \strong{Day of the week} \itemize{ \item \verb{\%a}: The \code{locale}'s abbreviated weekday name. \item \verb{\%A}: The \code{locale}'s full weekday name. \item \verb{\%w}: The weekday as a decimal number (\code{0-6}), where Sunday is \code{0}. } \strong{ISO 8601 week-based year} \itemize{ \item \verb{\%g}: The last two decimal digits of the ISO week-based year. If the result is a single digit it is prefixed by \code{0}. \item \verb{\%G}: The ISO week-based year as a decimal number. If the result is less than four digits it is left-padded with \code{0} to four digits. \item \verb{\%V}: The ISO week-based week number as a decimal number. If the result is a single digit, it is prefixed with \code{0}. \item \verb{\%u}: The ISO weekday as a decimal number (\code{1-7}), where Monday is \code{1}. } \strong{Week of the year} \itemize{ \item \verb{\%U}: The week number of the year as a decimal number. The first Sunday of the year is the first day of week \code{01}. Days of the same year prior to that are in week \code{00}. If the result is a single digit, it is prefixed with \code{0}. \item \verb{\%W}: The week number of the year as a decimal number. The first Monday of the year is the first day of week \code{01}. Days of the same year prior to that are in week \code{00}. If the result is a single digit, it is prefixed with \code{0}. } \strong{Day of the year} \itemize{ \item \verb{\%j}: The day of the year as a decimal number. January 1 is \code{001}. If the result is less than three digits, it is left-padded with \code{0} to three digits. } \strong{Date} \itemize{ \item \verb{\%D}, \verb{\%x}: Equivalent to \verb{\%m/\%d/\%y}. \item \verb{\%F}: Equivalent to \verb{\%Y-\%m-\%d}. } \strong{Time of day} \itemize{ \item \verb{\%H}: The hour (24-hour clock) as a decimal number. If the result is a single digit, it is prefixed with \code{0}. \item \verb{\%I}: The hour (12-hour clock) as a decimal number. If the result is a single digit, it is prefixed with \code{0}. \item \verb{\%M}: The minute as a decimal number. If the result is a single digit, it is prefixed with \code{0}. \item \verb{\%S}: Seconds as a decimal number. Fractional seconds are printed at the precision of the input. The character for the decimal point is localized according to \code{locale}. \item \verb{\%p}: The \code{locale}'s equivalent of the AM/PM designations associated with a 12-hour clock. \item \verb{\%R}: Equivalent to \verb{\%H:\%M}. \item \verb{\%T}, \verb{\%X}: Equivalent to \verb{\%H:\%M:\%S}. \item \verb{\%r}: Nearly equivalent to \verb{\%I:\%M:\%S \%p}, but seconds are always printed at second precision. } \strong{Time zone} \itemize{ \item \verb{\%z}: The offset from UTC in the ISO 8601 format. For example \code{-0430} refers to 4 hours 30 minutes behind UTC. If the offset is zero, \code{+0000} is used. The modified command \verb{\%Ez} inserts a \code{:} between the hour and minutes, like \code{-04:30}. \item \verb{\%Z}: The full time zone name. If \code{abbreviate_zone} is \code{TRUE}, the time zone abbreviation. } \strong{Miscellaneous} \itemize{ \item \verb{\%c}: A date and time representation. Similar to, but not exactly the same as, \verb{\%a \%b \%d \%H:\%M:\%S \%Y}. \item \code{\%\%}: A \verb{\%} character. \item \verb{\%n}: A newline character. \item \verb{\%t}: A horizontal-tab character. }} \item{locale}{\verb{[clock_locale]} A locale object created from \code{\link[=clock_locale]{clock_locale()}}.} \item{abbreviate_zone}{\verb{[logical(1)]} If \code{TRUE}, \verb{\%Z} returns an abbreviated time zone name. If \code{FALSE}, \verb{\%Z} returns the full time zone name.} } \value{ A character vector of the formatted input. } \description{ This is a POSIXct method for the \code{\link[=date_format]{date_format()}} generic. \code{date_format()} formats a date-time (POSIXct) using a \code{format} string. If \code{format} is \code{NULL}, a default format of \code{"\%Y-\%m-\%dT\%H:\%M:\%S\%Ez[\%Z]"} is used. This matches the default format that \code{\link[=date_time_parse_complete]{date_time_parse_complete()}} parses. Additionally, this format matches the de-facto standard extension to RFC 3339 for creating completely unambiguous date-times. } \examples{ x <- date_time_parse( c("1970-04-26 01:30:00", "1970-04-26 03:30:00"), zone = "America/New_York" ) # Default date_format(x) # Which is parseable by `date_time_parse_complete()` date_time_parse_complete(date_format(x)) date_format(x, format = "\%B \%d, \%Y \%H:\%M:\%S") # By default, `\%Z` uses the full zone name, but you can switch to the # abbreviated name date_format(x, format = "\%z \%Z") date_format(x, format = "\%z \%Z", abbreviate_zone = TRUE) } clock/man/as_iso_year_week_day.Rd0000644000176200001440000000164014427270231016557 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/iso-year-week-day.R \name{as_iso_year_week_day} \alias{as_iso_year_week_day} \title{Convert to iso-year-week-day} \usage{ as_iso_year_week_day(x, ...) } \arguments{ \item{x}{\verb{[vector]} A vector to convert to iso-year-week-day.} \item{...}{These dots are for future extensions and must be empty.} } \value{ A iso-year-week-day vector. } \description{ \code{as_iso_year_week_day()} converts a vector to the iso-year-week-day calendar. Time points, Dates, POSIXct, and other calendars can all be converted to iso-year-week-day. } \examples{ # From Date as_iso_year_week_day(as.Date("2019-01-01")) # From POSIXct, which assumes that the naive time is what should be converted as_iso_year_week_day(as.POSIXct("2019-01-01 02:30:30", "America/New_York")) # From other calendars as_iso_year_week_day(year_quarter_day(2019, quarter = 2, day = 50)) } clock/man/is_iso_year_week_day.Rd0000644000176200001440000000101114005062256016555 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/iso-year-week-day.R \name{is_iso_year_week_day} \alias{is_iso_year_week_day} \title{Is \code{x} a iso-year-week-day?} \usage{ is_iso_year_week_day(x) } \arguments{ \item{x}{\verb{[object]} An object.} } \value{ Returns \code{TRUE} if \code{x} inherits from \code{"clock_iso_year_week_day"}, otherwise returns \code{FALSE}. } \description{ Check if \code{x} is a iso-year-week-day. } \examples{ is_iso_year_week_day(iso_year_week_day(2019)) } clock/man/calendar_spanning_seq.Rd0000644000176200001440000000345214425175541016741 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/calendar.R \name{calendar_spanning_seq} \alias{calendar_spanning_seq} \title{Spanning sequence: calendars} \usage{ calendar_spanning_seq(x) } \arguments{ \item{x}{\verb{[clock_calendar]} A calendar vector.} } \value{ A sequence along \verb{[min(x), max(x)]}. } \description{ \code{calendar_spanning_seq()} generates a regular sequence along the span of \code{x}, i.e. along \verb{[min(x), max(x)]}. The sequence is generated at the precision of \code{x}. Importantly, sequences can only be generated if the underlying \code{\link[=seq]{seq()}} method for the calendar in question supports a \code{from} and \code{to} value at the same precision as \code{x}. For example, you can't compute a day precision spanning sequence for a \code{\link[=year_month_day]{year_month_day()}} calendar (you can only compute a year and month one). To create a day precision sequence, you'd have to convert to a time-point first. See the individual \code{\link[=seq]{seq()}} method documentation to learn what precisions are allowed. } \details{ Missing values are automatically removed before the sequence is generated. If you need more precise sequence generation, call \code{\link[=range]{range()}} and \code{\link[=seq]{seq()}} directly. } \examples{ x <- year_month_day(c(2019, 2022, 2020), c(2, 5, 3)) x # Month precision spanning sequence calendar_spanning_seq(x) # Quarter precision: x <- year_quarter_day(c(2005, 2006, 2003), c(4, 2, 3)) calendar_spanning_seq(x) # Can't generate sequences if `seq()` doesn't allow the precision x <- year_month_day(2019, c(1, 2, 1), c(20, 3, 25)) try(calendar_spanning_seq(x)) # Generally this means you need to convert to a time point and use # `time_point_spanning_seq()` instead time_point_spanning_seq(as_sys_time(x)) } clock/man/is_year_day.Rd0000644000176200001440000000070214016222032014666 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gregorian-year-day.R \name{is_year_day} \alias{is_year_day} \title{Is \code{x} a year-day?} \usage{ is_year_day(x) } \arguments{ \item{x}{\verb{[object]} An object.} } \value{ Returns \code{TRUE} if \code{x} inherits from \code{"clock_year_day"}, otherwise returns \code{FALSE}. } \description{ Check if \code{x} is a year-day. } \examples{ is_year_day(year_day(2019)) } clock/man/time_point_precision.Rd0000644000176200001440000000114714027170177016642 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/time-point.R \name{time_point_precision} \alias{time_point_precision} \title{Precision: time point} \usage{ time_point_precision(x) } \arguments{ \item{x}{\verb{[clock_time_point]} A time point.} } \value{ A single string holding the precision of the time point. } \description{ \code{time_point_precision()} extracts the precision from a time point, such as a sys-time or naive-time. It returns the precision as a single string. } \examples{ time_point_precision(sys_time_now()) time_point_precision(as_naive_time(duration_days(1))) } clock/man/year_month_day_parse.Rd0000644000176200001440000002700014133632415016604 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gregorian-year-month-day.R \name{year_month_day_parse} \alias{year_month_day_parse} \title{Parsing: year-month-day} \usage{ year_month_day_parse( x, ..., format = NULL, precision = "day", locale = clock_locale() ) } \arguments{ \item{x}{\verb{[character]} A character vector to parse.} \item{...}{These dots are for future extensions and must be empty.} \item{format}{\verb{[character / NULL]} A format string. A combination of the following commands, or \code{NULL}, in which case a default format string is used. A vector of multiple format strings can be supplied. They will be tried in the order they are provided. \strong{Year} \itemize{ \item \verb{\%C}: The century as a decimal number. The modified command \verb{\%NC} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. \item \verb{\%y}: The last two decimal digits of the year. If the century is not otherwise specified (e.g. with \verb{\%C}), values in the range \verb{[69 - 99]} are presumed to refer to the years \verb{[1969 - 1999]}, and values in the range \verb{[00 - 68]} are presumed to refer to the years \verb{[2000 - 2068]}. The modified command \verb{\%Ny}, where \code{N} is a positive decimal integer, specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. \item \verb{\%Y}: The year as a decimal number. The modified command \verb{\%NY} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{4}. Leading zeroes are permitted but not required. } \strong{Month} \itemize{ \item \verb{\%b}, \verb{\%B}, \verb{\%h}: The \code{locale}'s full or abbreviated case-insensitive month name. \item \verb{\%m}: The month as a decimal number. January is \code{1}. The modified command \verb{\%Nm} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. } \strong{Day} \itemize{ \item \verb{\%d}, \verb{\%e}: The day of the month as a decimal number. The modified command \verb{\%Nd} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. } \strong{Day of the week} \itemize{ \item \verb{\%a}, \verb{\%A}: The \code{locale}'s full or abbreviated case-insensitive weekday name. \item \verb{\%w}: The weekday as a decimal number (\code{0-6}), where Sunday is \code{0}. The modified command \verb{\%Nw} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{1}. Leading zeroes are permitted but not required. } \strong{ISO 8601 week-based year} \itemize{ \item \verb{\%g}: The last two decimal digits of the ISO week-based year. The modified command \verb{\%Ng} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. \item \verb{\%G}: The ISO week-based year as a decimal number. The modified command \verb{\%NG} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{4}. Leading zeroes are permitted but not required. \item \verb{\%V}: The ISO week-based week number as a decimal number. The modified command \verb{\%NV} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. \item \verb{\%u}: The ISO weekday as a decimal number (\code{1-7}), where Monday is \code{1}. The modified command \verb{\%Nu} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{1}. Leading zeroes are permitted but not required. } \strong{Week of the year} \itemize{ \item \verb{\%U}: The week number of the year as a decimal number. The first Sunday of the year is the first day of week \code{01}. Days of the same year prior to that are in week \code{00}. The modified command \verb{\%NU} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. \item \verb{\%W}: The week number of the year as a decimal number. The first Monday of the year is the first day of week \code{01}. Days of the same year prior to that are in week \code{00}. The modified command \verb{\%NW} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. } \strong{Day of the year} \itemize{ \item \verb{\%j}: The day of the year as a decimal number. January 1 is \code{1}. The modified command \verb{\%Nj} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{3}. Leading zeroes are permitted but not required. } \strong{Date} \itemize{ \item \verb{\%D}, \verb{\%x}: Equivalent to \verb{\%m/\%d/\%y}. \item \verb{\%F}: Equivalent to \verb{\%Y-\%m-\%d}. If modified with a width (like \verb{\%NF}), the width is applied to only \verb{\%Y}. } \strong{Time of day} \itemize{ \item \verb{\%H}: The hour (24-hour clock) as a decimal number. The modified command \verb{\%NH} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. \item \verb{\%I}: The hour (12-hour clock) as a decimal number. The modified command \verb{\%NI} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. \item \verb{\%M}: The minutes as a decimal number. The modified command \verb{\%NM} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. \item \verb{\%S}: The seconds as a decimal number. Leading zeroes are permitted but not required. If encountered, the \code{locale} determines the decimal point character. Generally, the maximum number of characters to read is determined by the precision that you are parsing at. For example, a precision of \code{"second"} would read a maximum of 2 characters, while a precision of \code{"millisecond"} would read a maximum of 6 (2 for the values before the decimal point, 1 for the decimal point, and 3 for the values after it). The modified command \verb{\%NS}, where \code{N} is a positive decimal integer, can be used to exactly specify the maximum number of characters to read. This is only useful if you happen to have seconds with more than 1 leading zero. \item \verb{\%p}: The \code{locale}'s equivalent of the AM/PM designations associated with a 12-hour clock. The command \verb{\%I} must precede \verb{\%p} in the format string. \item \verb{\%R}: Equivalent to \verb{\%H:\%M}. \item \verb{\%T}, \verb{\%X}: Equivalent to \verb{\%H:\%M:\%S}. \item \verb{\%r}: Equivalent to \verb{\%I:\%M:\%S \%p}. } \strong{Time zone} \itemize{ \item \verb{\%z}: The offset from UTC in the format \verb{[+|-]hh[mm]}. For example \code{-0430} refers to 4 hours 30 minutes behind UTC. And \code{04} refers to 4 hours ahead of UTC. The modified command \verb{\%Ez} parses a \code{:} between the hours and minutes and leading zeroes on the hour field are optional: \verb{[+|-]h[h][:mm]}. For example \code{-04:30} refers to 4 hours 30 minutes behind UTC. And \code{4} refers to 4 hours ahead of UTC. \item \verb{\%Z}: The full time zone name or the time zone abbreviation, depending on the function being used. A single word is parsed. This word can only contain characters that are alphanumeric, or one of \code{'_'}, \code{'/'}, \code{'-'} or \code{'+'}. } \strong{Miscellaneous} \itemize{ \item \verb{\%c}: A date and time representation. Equivalent to \verb{\%a \%b \%d \%H:\%M:\%S \%Y}. \item \code{\%\%}: A \verb{\%} character. \item \verb{\%n}: Matches one white space character. \verb{\%n}, \verb{\%t}, and a space can be combined to match a wide range of white-space patterns. For example \code{"\%n "} matches one or more white space characters, and \code{"\%n\%t\%t"} matches one to three white space characters. \item \verb{\%t}: Matches zero or one white space characters. }} \item{precision}{\verb{[character(1)]} A precision for the resulting year-month-day. One of: \itemize{ \item \code{"year"} \item \code{"month"} \item \code{"day"} \item \code{"hour"} \item \code{"minute"} \item \code{"second"} \item \code{"millisecond"} \item \code{"microsecond"} \item \code{"nanosecond"} } Setting the \code{precision} determines how much information \verb{\%S} attempts to parse.} \item{locale}{\verb{[clock_locale]} A locale object created by \code{\link[=clock_locale]{clock_locale()}}.} } \value{ A year-month-day calendar vector. If a parsing fails, \code{NA} is returned. } \description{ \code{year_month_day_parse()} parses strings into a year-month-day. The default options assume \code{x} should be parsed at day precision, using a \code{format} string of \code{"\%Y-\%m-\%d"}. If a more precise precision than day is used, then time components will also be parsed. The default format separates date and time components by a \code{"T"} and the time components by a \code{":"}. For example, setting the precision to \code{"second"} will use a default format of \code{"\%Y-\%m-\%dT\%H:\%M:\%S"}. This is aligned with the \code{\link[=format]{format()}} method for year-month-day, and with the RFC 3339 standard. } \details{ \code{year_month_day_parse()} completely ignores the \verb{\%z} and \verb{\%Z} commands. } \section{Full Precision Parsing}{ It is highly recommended to parse all of the information in the date-time string into a type at least as precise as the string. For example, if your string has fractional seconds, but you only require seconds, specify a sub-second \code{precision}, then round to seconds manually using whatever convention is appropriate for your use case. Parsing such a string directly into a second precision result is ambiguous and undefined, and is unlikely to work as you might expect. } \examples{ x <- "2019-01-01" # Default parses at day precision year_month_day_parse(x) # Can parse at less precise precisions too year_month_day_parse(x, precision = "month") year_month_day_parse(x, precision = "year") # Even invalid dates can be round-tripped through format<->parse calls invalid <- year_month_day(2019, 2, 30) year_month_day_parse(format(invalid)) # Can parse with time of day year_month_day_parse( "2019-01-30T02:30:00.123456789", precision = "nanosecond" ) # Can parse using multiple format strings, which will be tried # in the order they are provided x <- c("2019-01-01", "2020-01-01", "2021/2/3") formats <- c("\%Y-\%m-\%d", "\%Y/\%m/\%d") year_month_day_parse(x, format = formats) # Can parse using other format tokens as well year_month_day_parse( "January, 2019", format = "\%B, \%Y", precision = "month" ) # Parsing a French year-month-day year_month_day_parse( "octobre 1, 2000", format = "\%B \%d, \%Y", locale = clock_locale("fr") ) } clock/man/clock-arithmetic.Rd0000644000176200001440000000577714422221153015645 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/arithmetic.R \name{clock-arithmetic} \alias{clock-arithmetic} \alias{add_years} \alias{add_quarters} \alias{add_months} \alias{add_weeks} \alias{add_days} \alias{add_hours} \alias{add_minutes} \alias{add_seconds} \alias{add_milliseconds} \alias{add_microseconds} \alias{add_nanoseconds} \title{Clock arithmetic} \usage{ add_years(x, n, ...) add_quarters(x, n, ...) add_months(x, n, ...) add_weeks(x, n, ...) add_days(x, n, ...) add_hours(x, n, ...) add_minutes(x, n, ...) add_seconds(x, n, ...) add_milliseconds(x, n, ...) add_microseconds(x, n, ...) add_nanoseconds(x, n, ...) } \arguments{ \item{x}{\verb{[object]} An object.} \item{n}{\verb{[integer / clock_duration]} An integer vector to be converted to a duration, or a duration corresponding to the arithmetic function being used. This corresponds to the number of duration units to add. \code{n} may be negative to subtract units of duration.} \item{...}{These dots are for future extensions and must be empty.} } \value{ \code{x} after performing the arithmetic. } \description{ This is the landing page for all clock arithmetic functions. There are specific sub-pages describing how arithmetic works for different calendars and time points, which is where you should look for more information. Calendars are efficient at arithmetic with irregular units of time, such as month, quarters, or years. \itemize{ \item \link[=year-month-day-arithmetic]{year-month-day} \item \link[=year-month-weekday-arithmetic]{year-month-weekday} \item \link[=year-quarter-day-arithmetic]{year-quarter-day} \item \link[=year-week-day-arithmetic]{year-week-day} \item \link[=iso-year-week-day-arithmetic]{iso-year-week-day} \item \link[=year-day-arithmetic]{year-day} } Time points, such as naive-times and sys-times, are efficient at arithmetic with regular, well-defined units of time, such as days, hours, seconds, or nanoseconds. \itemize{ \item \link[=time-point-arithmetic]{time-point} } Durations can use any of these arithmetic functions, and return a new duration with a precision corresponding to the common type of the input and the function used. \itemize{ \item \link[=duration-arithmetic]{duration} } Weekdays can perform day-based circular arithmetic. \itemize{ \item \link[=weekday-arithmetic]{weekday} } There are also convenience methods for doing arithmetic directly on a native R date or date-time type: \itemize{ \item \link[=Date-arithmetic]{dates (Date)} \item \link[=posixt-arithmetic]{date-times (POSIXct / POSIXlt)} } } \details{ \code{x} and \code{n} are recycled against each other using \link[vctrs:vector_recycling_rules]{tidyverse recycling rules}. Months and years are considered "irregular" because some months have more days then others (28, 29, 30, or 31), and some years have more days than others (365 or 366). Days are considered "regular" because they are defined as 86,400 seconds. } \examples{ # See each sub-page for more specific examples x <- year_month_day(2019, 2, 1) add_months(x, 1) } clock/man/calendar_leap_year.Rd0000644000176200001440000000500014422221153016170 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/calendar.R \name{calendar_leap_year} \alias{calendar_leap_year} \title{Is the calendar year a leap year?} \usage{ calendar_leap_year(x) } \arguments{ \item{x}{\verb{[calendar]} A calendar type to detect leap years in.} } \value{ A logical vector the same size as \code{x}. Returns \code{TRUE} if in a leap year, \code{FALSE} if not in a leap year, and \code{NA} if \code{x} is \code{NA}. } \description{ \code{calendar_leap_year()} detects if the calendar year is a leap year - i.e. does it contain one or more extra components than other years? A particular year is a leap year if: \itemize{ \item \code{\link[=year_month_day]{year_month_day()}}: February has 29 days. \item \code{\link[=year_month_weekday]{year_month_weekday()}}: February has a weekday that occurs 5 times. \item \code{\link[=year_week_day]{year_week_day()}}: There are 53 weeks in the year, resulting in 371 days in the year. \item \code{\link[=iso_year_week_day]{iso_year_week_day()}}: There are 53 weeks in the year, resulting in 371 days in the year. \item \code{\link[=year_quarter_day]{year_quarter_day()}}: One of the quarters has 1 more day than normal (the quarter with an extra day depends on the \code{start} used, but will always be the same for a particular \code{start}). This aligns with Gregorian leap years for all \code{start}s except February, in which case the leap year is always 1 year after the Gregorian leap year. \item \code{\link[=year_day]{year_day()}}: There are 366 days in the year. } } \examples{ x <- year_month_day(c(2019:2024, NA)) calendar_leap_year(x) # For year-quarter-day, the leap year typically aligns with the Gregorian # leap year, unless the `start` is February, in which case the leap year is # always 1 year after the Gregorian leap year x <- year_quarter_day(2020:2021, start = clock_months$january) calendar_leap_year(x) x <- year_quarter_day(2020:2021, start = clock_months$february) calendar_leap_year(x) # With a January start, 2020 has the extra day get_day(year_quarter_day(2020, 1:4, "last", start = clock_months$january)) get_day(year_quarter_day(2021, 1:4, "last", start = clock_months$january)) get_day(year_quarter_day(2022, 1:4, "last", start = clock_months$january)) # With a February start, 2021 has the extra day get_day(year_quarter_day(2020, 1:4, "last", start = clock_months$february)) get_day(year_quarter_day(2021, 1:4, "last", start = clock_months$february)) get_day(year_quarter_day(2022, 1:4, "last", start = clock_months$february)) } clock/man/year_quarter_day.Rd0000644000176200001440000000522014416030722015745 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/quarterly-year-quarter-day.R \name{year_quarter_day} \alias{year_quarter_day} \title{Calendar: year-quarter-day} \usage{ year_quarter_day( year, quarter = NULL, day = NULL, hour = NULL, minute = NULL, second = NULL, subsecond = NULL, ..., start = NULL, subsecond_precision = NULL ) } \arguments{ \item{year}{\verb{[integer]} The fiscal year. Values \verb{[-32767, 32767]} are generally allowed.} \item{quarter}{\verb{[integer / NULL]} The fiscal quarter. Values \verb{[1, 4]} are allowed.} \item{day}{\verb{[integer / "last" / NULL]} The day of the quarter. Values \verb{[1, 92]} are allowed. If \code{"last"}, the last day of the quarter is returned.} \item{hour}{\verb{[integer / NULL]} The hour. Values \verb{[0, 23]} are allowed.} \item{minute}{\verb{[integer / NULL]} The minute. Values \verb{[0, 59]} are allowed.} \item{second}{\verb{[integer / NULL]} The second. Values \verb{[0, 59]} are allowed.} \item{subsecond}{\verb{[integer / NULL]} The subsecond. If specified, \code{subsecond_precision} must also be specified to determine how to interpret the \code{subsecond}. If using milliseconds, values \verb{[0, 999]} are allowed. If using microseconds, values \verb{[0, 999999]} are allowed. If using nanoseconds, values \verb{[0, 999999999]} are allowed.} \item{...}{These dots are for future extensions and must be empty.} \item{start}{\verb{[integer(1) / NULL]} The month to start the fiscal year in. 1 = January and 12 = December. If \code{NULL}, a \code{start} of January will be used.} \item{subsecond_precision}{\verb{[character(1) / NULL]} The precision to interpret \code{subsecond} as. One of: \code{"millisecond"}, \code{"microsecond"}, or \code{"nanosecond"}.} } \value{ A year-quarter-day calendar vector. } \description{ \code{year_quarter_day()} constructs a calendar from the fiscal year, fiscal quarter, and day of the quarter, along with a value determining which month the fiscal year \code{start}s in. } \details{ Fields are recycled against each other using \link[vctrs:vector_recycling_rules]{tidyverse recycling rules}. Fields are collected in order until the first \code{NULL} field is located. No fields after the first \code{NULL} field are used. } \examples{ # Year-quarter type x <- year_quarter_day(2019, 1:4) x add_quarters(x, 2) # Set the day to the last day of the quarter x <- set_day(x, "last") x # Start the fiscal year in June june <- 6L y <- year_quarter_day(2019, 1:4, "last", start = june) # Compare the year-month-day values that result from having different # fiscal year start months as_year_month_day(x) as_year_month_day(y) } clock/man/year-quarter-day-setters.Rd0000644000176200001440000000552014017505732017300 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/quarterly-year-quarter-day.R \name{year-quarter-day-setters} \alias{year-quarter-day-setters} \alias{set_year.clock_year_quarter_day} \alias{set_quarter.clock_year_quarter_day} \alias{set_day.clock_year_quarter_day} \alias{set_hour.clock_year_quarter_day} \alias{set_minute.clock_year_quarter_day} \alias{set_second.clock_year_quarter_day} \alias{set_millisecond.clock_year_quarter_day} \alias{set_microsecond.clock_year_quarter_day} \alias{set_nanosecond.clock_year_quarter_day} \title{Setters: year-quarter-day} \usage{ \method{set_year}{clock_year_quarter_day}(x, value, ...) \method{set_quarter}{clock_year_quarter_day}(x, value, ...) \method{set_day}{clock_year_quarter_day}(x, value, ...) \method{set_hour}{clock_year_quarter_day}(x, value, ...) \method{set_minute}{clock_year_quarter_day}(x, value, ...) \method{set_second}{clock_year_quarter_day}(x, value, ...) \method{set_millisecond}{clock_year_quarter_day}(x, value, ...) \method{set_microsecond}{clock_year_quarter_day}(x, value, ...) \method{set_nanosecond}{clock_year_quarter_day}(x, value, ...) } \arguments{ \item{x}{\verb{[clock_year_quarter_day]} A year-quarter-day vector.} \item{value}{\verb{[integer / "last"]} The value to set the component to. For \code{set_day()}, this can also be \code{"last"} to adjust to the last day of the current fiscal quarter.} \item{...}{These dots are for future extensions and must be empty.} } \value{ \code{x} with the component set. } \description{ These are year-quarter-day methods for the \link[=clock-setters]{setter generics}. \itemize{ \item \code{set_year()} sets the fiscal year. \item \code{set_quarter()} sets the fiscal quarter of the year. Valid values are in the range of \verb{[1, 4]}. \item \code{set_day()} sets the day of the fiscal quarter. Valid values are in the range of \verb{[1, 92]}. \item There are sub-daily setters for setting more precise components. } } \examples{ library(magrittr) # Quarter precision vector x <- year_quarter_day(2019, 1:4) x # Promote to day precision by setting the day x <- set_day(x, 1) x # Or set to the last day of the quarter x <- set_day(x, "last") x # What year-month-day is this? as_year_month_day(x) # Set to an invalid day of the quarter # (not all quarters have 92 days) invalid <- set_day(x, 92) invalid # Here are the invalid ones invalid[invalid_detect(invalid)] # Resolve the invalid dates by choosing the previous/next valid moment invalid_resolve(invalid, invalid = "previous") invalid_resolve(invalid, invalid = "next") # Or resolve by "overflowing" by the number of days that you have # gone past the last valid day invalid_resolve(invalid, invalid = "overflow") # This is similar to days <- get_day(invalid) - 1L invalid \%>\% set_day(1) \%>\% as_naive_time() \%>\% add_days(days) \%>\% as_year_quarter_day() } clock/man/as_year_month_day.Rd0000644000176200001440000000160314427270231016076 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gregorian-year-month-day.R \name{as_year_month_day} \alias{as_year_month_day} \title{Convert to year-month-day} \usage{ as_year_month_day(x, ...) } \arguments{ \item{x}{\verb{[vector]} A vector to convert to year-month-day.} \item{...}{These dots are for future extensions and must be empty.} } \value{ A year-month-day vector. } \description{ \code{as_year_month_day()} converts a vector to the year-month-day calendar. Time points, Dates, POSIXct, and other calendars can all be converted to year-month-day. } \examples{ # From Date as_year_month_day(as.Date("2019-01-01")) # From POSIXct, which assumes that the naive time is what should be converted as_year_month_day(as.POSIXct("2019-01-01 02:30:30", "America/New_York")) # From other calendars as_year_month_day(year_quarter_day(2019, quarter = 2, day = 50)) } clock/man/year-month-weekday-boundary.Rd0000644000176200001440000000310414075602251017742 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gregorian-year-month-weekday.R \name{year-month-weekday-boundary} \alias{year-month-weekday-boundary} \alias{calendar_start.clock_year_month_weekday} \alias{calendar_end.clock_year_month_weekday} \title{Boundaries: year-month-weekday} \usage{ \method{calendar_start}{clock_year_month_weekday}(x, precision) \method{calendar_end}{clock_year_month_weekday}(x, precision) } \arguments{ \item{x}{\verb{[clock_year_month_weekday]} A year-month-weekday vector.} \item{precision}{\verb{[character(1)]} One of: \itemize{ \item \code{"year"} \item \code{"month"} }} } \value{ \code{x} at the same precision, but with some components altered to be at the boundary value. } \description{ This is a year-month-weekday method for the \code{\link[=calendar_start]{calendar_start()}} and \code{\link[=calendar_end]{calendar_end()}} generics. They adjust components of a calendar to the start or end of a specified \code{precision}. This method is restricted to only \code{"year"} and \code{"month"} \code{precision}s, and \code{x} can't be more precise than month precision. Computing the "start" of a day precision year-month-weekday object isn't defined because a year-month-weekday with \verb{day = 1, index = 1} doesn't necessarily occur earlier (chronologically) than \verb{day = 2, index = 1}. Because of these restrictions, this method isn't particularly useful, but is included for completeness. } \examples{ # Month precision x <- year_month_weekday(2019, 1:5) x # Compute the last month of the year calendar_end(x, "year") } clock/man/zoned-parsing.Rd0000644000176200001440000003476514133632415015210 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/zoned-time.R \name{zoned-parsing} \alias{zoned-parsing} \alias{zoned_time_parse_complete} \alias{zoned_time_parse_abbrev} \title{Parsing: zoned-time} \usage{ zoned_time_parse_complete( x, ..., format = NULL, precision = "second", locale = clock_locale() ) zoned_time_parse_abbrev( x, zone, ..., format = NULL, precision = "second", locale = clock_locale() ) } \arguments{ \item{x}{\verb{[character]} A character vector to parse.} \item{...}{These dots are for future extensions and must be empty.} \item{format}{\verb{[character / NULL]} A format string. A combination of the following commands, or \code{NULL}, in which case a default format string is used. A vector of multiple format strings can be supplied. They will be tried in the order they are provided. \strong{Year} \itemize{ \item \verb{\%C}: The century as a decimal number. The modified command \verb{\%NC} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. \item \verb{\%y}: The last two decimal digits of the year. If the century is not otherwise specified (e.g. with \verb{\%C}), values in the range \verb{[69 - 99]} are presumed to refer to the years \verb{[1969 - 1999]}, and values in the range \verb{[00 - 68]} are presumed to refer to the years \verb{[2000 - 2068]}. The modified command \verb{\%Ny}, where \code{N} is a positive decimal integer, specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. \item \verb{\%Y}: The year as a decimal number. The modified command \verb{\%NY} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{4}. Leading zeroes are permitted but not required. } \strong{Month} \itemize{ \item \verb{\%b}, \verb{\%B}, \verb{\%h}: The \code{locale}'s full or abbreviated case-insensitive month name. \item \verb{\%m}: The month as a decimal number. January is \code{1}. The modified command \verb{\%Nm} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. } \strong{Day} \itemize{ \item \verb{\%d}, \verb{\%e}: The day of the month as a decimal number. The modified command \verb{\%Nd} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. } \strong{Day of the week} \itemize{ \item \verb{\%a}, \verb{\%A}: The \code{locale}'s full or abbreviated case-insensitive weekday name. \item \verb{\%w}: The weekday as a decimal number (\code{0-6}), where Sunday is \code{0}. The modified command \verb{\%Nw} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{1}. Leading zeroes are permitted but not required. } \strong{ISO 8601 week-based year} \itemize{ \item \verb{\%g}: The last two decimal digits of the ISO week-based year. The modified command \verb{\%Ng} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. \item \verb{\%G}: The ISO week-based year as a decimal number. The modified command \verb{\%NG} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{4}. Leading zeroes are permitted but not required. \item \verb{\%V}: The ISO week-based week number as a decimal number. The modified command \verb{\%NV} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. \item \verb{\%u}: The ISO weekday as a decimal number (\code{1-7}), where Monday is \code{1}. The modified command \verb{\%Nu} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{1}. Leading zeroes are permitted but not required. } \strong{Week of the year} \itemize{ \item \verb{\%U}: The week number of the year as a decimal number. The first Sunday of the year is the first day of week \code{01}. Days of the same year prior to that are in week \code{00}. The modified command \verb{\%NU} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. \item \verb{\%W}: The week number of the year as a decimal number. The first Monday of the year is the first day of week \code{01}. Days of the same year prior to that are in week \code{00}. The modified command \verb{\%NW} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. } \strong{Day of the year} \itemize{ \item \verb{\%j}: The day of the year as a decimal number. January 1 is \code{1}. The modified command \verb{\%Nj} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{3}. Leading zeroes are permitted but not required. } \strong{Date} \itemize{ \item \verb{\%D}, \verb{\%x}: Equivalent to \verb{\%m/\%d/\%y}. \item \verb{\%F}: Equivalent to \verb{\%Y-\%m-\%d}. If modified with a width (like \verb{\%NF}), the width is applied to only \verb{\%Y}. } \strong{Time of day} \itemize{ \item \verb{\%H}: The hour (24-hour clock) as a decimal number. The modified command \verb{\%NH} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. \item \verb{\%I}: The hour (12-hour clock) as a decimal number. The modified command \verb{\%NI} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. \item \verb{\%M}: The minutes as a decimal number. The modified command \verb{\%NM} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. \item \verb{\%S}: The seconds as a decimal number. Leading zeroes are permitted but not required. If encountered, the \code{locale} determines the decimal point character. Generally, the maximum number of characters to read is determined by the precision that you are parsing at. For example, a precision of \code{"second"} would read a maximum of 2 characters, while a precision of \code{"millisecond"} would read a maximum of 6 (2 for the values before the decimal point, 1 for the decimal point, and 3 for the values after it). The modified command \verb{\%NS}, where \code{N} is a positive decimal integer, can be used to exactly specify the maximum number of characters to read. This is only useful if you happen to have seconds with more than 1 leading zero. \item \verb{\%p}: The \code{locale}'s equivalent of the AM/PM designations associated with a 12-hour clock. The command \verb{\%I} must precede \verb{\%p} in the format string. \item \verb{\%R}: Equivalent to \verb{\%H:\%M}. \item \verb{\%T}, \verb{\%X}: Equivalent to \verb{\%H:\%M:\%S}. \item \verb{\%r}: Equivalent to \verb{\%I:\%M:\%S \%p}. } \strong{Time zone} \itemize{ \item \verb{\%z}: The offset from UTC in the format \verb{[+|-]hh[mm]}. For example \code{-0430} refers to 4 hours 30 minutes behind UTC. And \code{04} refers to 4 hours ahead of UTC. The modified command \verb{\%Ez} parses a \code{:} between the hours and minutes and leading zeroes on the hour field are optional: \verb{[+|-]h[h][:mm]}. For example \code{-04:30} refers to 4 hours 30 minutes behind UTC. And \code{4} refers to 4 hours ahead of UTC. \item \verb{\%Z}: The full time zone name or the time zone abbreviation, depending on the function being used. A single word is parsed. This word can only contain characters that are alphanumeric, or one of \code{'_'}, \code{'/'}, \code{'-'} or \code{'+'}. } \strong{Miscellaneous} \itemize{ \item \verb{\%c}: A date and time representation. Equivalent to \verb{\%a \%b \%d \%H:\%M:\%S \%Y}. \item \code{\%\%}: A \verb{\%} character. \item \verb{\%n}: Matches one white space character. \verb{\%n}, \verb{\%t}, and a space can be combined to match a wide range of white-space patterns. For example \code{"\%n "} matches one or more white space characters, and \code{"\%n\%t\%t"} matches one to three white space characters. \item \verb{\%t}: Matches zero or one white space characters. }} \item{precision}{\verb{[character(1)]} A precision for the resulting zoned-time. One of: \itemize{ \item \code{"second"} \item \code{"millisecond"} \item \code{"microsecond"} \item \code{"nanosecond"} } Setting the \code{precision} determines how much information \verb{\%S} attempts to parse.} \item{locale}{\verb{[clock_locale]} A locale object created from \code{\link[=clock_locale]{clock_locale()}}.} \item{zone}{\verb{[character(1)]} A full time zone name.} } \value{ A zoned-time. } \description{ There are two parsers into a zoned-time, \code{zoned_time_parse_complete()} and \code{zoned_time_parse_abbrev()}. \subsection{zoned_time_parse_complete()}{ \code{zoned_time_parse_complete()} is a parser for \emph{complete} date-time strings, like \code{"2019-01-01T00:00:00-05:00[America/New_York]"}. A complete date-time string has both the time zone offset and full time zone name in the string, which is the only way for the string itself to contain all of the information required to construct a zoned-time. Because of this, \code{zoned_time_parse_complete()} requires both the \verb{\%z} and \verb{\%Z} commands to be supplied in the \code{format} string. The default options assume that \code{x} should be parsed at second precision, using a \code{format} string of \code{"\%Y-\%m-\%dT\%H:\%M:\%S\%Ez[\%Z]"}. This matches the default result from calling \code{format()} on a zoned-time. Additionally, this format matches the de-facto standard extension to RFC 3339 for creating completely unambiguous date-times. } \subsection{zoned_time_parse_abbrev()}{ \code{zoned_time_parse_abbrev()} is a parser for date-time strings containing only a time zone abbreviation, like \code{"2019-01-01 00:00:00 EST"}. The time zone abbreviation is not enough to identify the full time zone name that the date-time belongs to, so the full time zone name must be supplied as the \code{zone} argument. However, the time zone abbreviation can help with resolving ambiguity around daylight saving time fallbacks. For \code{zoned_time_parse_abbrev()}, \verb{\%Z} must be supplied and is interpreted as the time zone abbreviation rather than the full time zone name. If used, the \verb{\%z} command must parse correctly, but its value will be completely ignored. The default options assume that \code{x} should be parsed at second precision, using a \code{format} string of \code{"\%Y-\%m-\%d \%H:\%M:\%S \%Z"}. This matches the default result from calling \code{print()} or \code{format(usetz = TRUE)} on a POSIXct date-time. } } \details{ If \code{zoned_time_parse_complete()} is given input that is length zero, all \code{NA}s, or completely fails to parse, then no time zone will be able to be determined. In that case, the result will use \code{"UTC"}. If your date-time strings contain time zone offsets (like \code{-04:00}), but not the full time zone name, you might need \code{\link[=sys_time_parse]{sys_time_parse()}}. If your date-time strings don't contain time zone offsets or the full time zone name, you might need to use \code{\link[=naive_time_parse]{naive_time_parse()}}. From there, if you know the time zone that the date-times are supposed to be in, you can convert to a zoned-time with \code{\link[=as_zoned_time]{as_zoned_time()}}. } \section{Full Precision Parsing}{ It is highly recommended to parse all of the information in the date-time string into a type at least as precise as the string. For example, if your string has fractional seconds, but you only require seconds, specify a sub-second \code{precision}, then round to seconds manually using whatever convention is appropriate for your use case. Parsing such a string directly into a second precision result is ambiguous and undefined, and is unlikely to work as you might expect. } \examples{ library(magrittr) zoned_time_parse_complete("2019-01-01T01:02:03-05:00[America/New_York]") zoned_time_parse_complete( "January 21, 2019 -0500 America/New_York", format = "\%B \%d, \%Y \%z \%Z" ) # Nanosecond precision x <- "2019/12/31 01:05:05.123456700-05:00[America/New_York]" zoned_time_parse_complete( x, format = "\%Y/\%m/\%d \%H:\%M:\%S\%Ez[\%Z]", precision = "nanosecond" ) # The `\%z` offset must correspond to the true offset that would be used # if the input was parsed as a naive-time and then converted to a zoned-time # with `as_zoned_time()`. For example, the time that was parsed above used an # offset of `-05:00`. We can confirm that this is correct with: year_month_day(2019, 1, 1, 1, 2, 3) \%>\% as_naive_time() \%>\% as_zoned_time("America/New_York") # So the following would not parse correctly zoned_time_parse_complete("2019-01-01T01:02:03-04:00[America/New_York]") # `\%z` is useful for breaking ties in otherwise ambiguous times. For example, # these two times are on either side of a daylight saving time fallback. # Without the `\%z` offset, you wouldn't be able to tell them apart! x <- c( "1970-10-25T01:30:00-04:00[America/New_York]", "1970-10-25T01:30:00-05:00[America/New_York]" ) zoned_time_parse_complete(x) # If you have date-time strings with time zone abbreviations, # `zoned_time_parse_abbrev()` should be able to help. The `zone` must be # provided, because multiple countries may use the same time zone # abbreviation. For example: x <- "1970-01-01 02:30:30 IST" # IST = India Standard Time zoned_time_parse_abbrev(x, "Asia/Kolkata") # IST = Israel Standard Time zoned_time_parse_abbrev(x, "Asia/Jerusalem") # The time zone abbreviation is mainly useful for resolving ambiguity # around daylight saving time fallbacks. Without the abbreviation, these # date-times would be ambiguous. x <- c( "1970-10-25 01:30:00 EDT", "1970-10-25 01:30:00 EST" ) zoned_time_parse_abbrev(x, "America/New_York") } clock/man/iso-year-week-day-narrow.Rd0000644000176200001440000000202614006303002017135 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/iso-year-week-day.R \name{iso-year-week-day-narrow} \alias{iso-year-week-day-narrow} \alias{calendar_narrow.clock_iso_year_week_day} \title{Narrow: iso-year-week-day} \usage{ \method{calendar_narrow}{clock_iso_year_week_day}(x, precision) } \arguments{ \item{x}{\verb{[clock_iso_year_week_day]} A iso-year-week-day vector.} \item{precision}{\verb{[character(1)]} One of: \itemize{ \item \code{"year"} \item \code{"week"} \item \code{"day"} \item \code{"hour"} \item \code{"minute"} \item \code{"second"} \item \code{"millisecond"} \item \code{"microsecond"} \item \code{"nanosecond"} }} } \value{ \code{x} narrowed to the supplied \code{precision}. } \description{ This is a iso-year-week-day method for the \code{\link[=calendar_narrow]{calendar_narrow()}} generic. It narrows a iso-year-week-day vector to the specified \code{precision}. } \examples{ # Day precision x <- iso_year_week_day(2019, 1, 5) x # Narrowed to week precision calendar_narrow(x, "week") } clock/man/year-month-weekday-setters.Rd0000644000176200001440000000722414011271365017615 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gregorian-year-month-weekday.R \name{year-month-weekday-setters} \alias{year-month-weekday-setters} \alias{set_year.clock_year_month_weekday} \alias{set_month.clock_year_month_weekday} \alias{set_day.clock_year_month_weekday} \alias{set_index.clock_year_month_weekday} \alias{set_hour.clock_year_month_weekday} \alias{set_minute.clock_year_month_weekday} \alias{set_second.clock_year_month_weekday} \alias{set_millisecond.clock_year_month_weekday} \alias{set_microsecond.clock_year_month_weekday} \alias{set_nanosecond.clock_year_month_weekday} \title{Setters: year-month-weekday} \usage{ \method{set_year}{clock_year_month_weekday}(x, value, ...) \method{set_month}{clock_year_month_weekday}(x, value, ...) \method{set_day}{clock_year_month_weekday}(x, value, ..., index = NULL) \method{set_index}{clock_year_month_weekday}(x, value, ...) \method{set_hour}{clock_year_month_weekday}(x, value, ...) \method{set_minute}{clock_year_month_weekday}(x, value, ...) \method{set_second}{clock_year_month_weekday}(x, value, ...) \method{set_millisecond}{clock_year_month_weekday}(x, value, ...) \method{set_microsecond}{clock_year_month_weekday}(x, value, ...) \method{set_nanosecond}{clock_year_month_weekday}(x, value, ...) } \arguments{ \item{x}{\verb{[clock_year_month_weekday]} A year-month-weekday vector.} \item{value}{\verb{[integer / "last"]} The value to set the component to. For \code{set_index()}, this can also be \code{"last"} to adjust to the last instance of the corresponding weekday in that month.} \item{...}{These dots are for future extensions and must be empty.} \item{index}{\verb{[NULL / integer / "last"]} This argument is only used with \code{set_day()}, and allows you to set the index while also setting the weekday. If \code{x} is a month precision year-month-weekday, \code{index} is required to be set, as you must specify the weekday and the index simultaneously to promote from month to day precision.} } \value{ \code{x} with the component set. } \description{ These are year-month-weekday methods for the \link[=clock-setters]{setter generics}. \itemize{ \item \code{set_year()} sets the Gregorian year. \item \code{set_month()} sets the month of the year. Valid values are in the range of \verb{[1, 12]}. \item \code{set_day()} sets the day of the week. Valid values are in the range of \verb{[1, 7]}, with 1 = Sunday, and 7 = Saturday. \item \code{set_index()} sets the index indicating that the corresponding weekday is the n-th instance of that weekday in the current month. Valid values are in the range of \verb{[1, 5]}. \item There are sub-daily setters for setting more precise components. } } \examples{ x <- year_month_weekday(2019, 1:3) set_year(x, 2020:2022) # Setting the weekday on a month precision year-month-weekday requires # also setting the `index` to fully specify the day information x <- set_day(x, clock_weekdays$sunday, index = 1) x # Once you have at least day precision, you can set the weekday and # the index separately set_day(x, clock_weekdays$monday) set_index(x, 3) # Set to the "last" instance of the corresponding weekday in this month # (Note that some months have 4 Sundays, and others have 5) set_index(x, "last") # Set to an invalid index # January and February of 2019 don't have 5 Sundays! invalid <- set_index(x, 5) invalid # Resolve the invalid dates by choosing the previous/next valid moment invalid_resolve(invalid, invalid = "previous") invalid_resolve(invalid, invalid = "next") # You can also "overflow" the index. This keeps the weekday, but resets # the index to 1 and increments the month value by 1. invalid_resolve(invalid, invalid = "overflow") } clock/man/year_month_weekday.Rd0000644000176200001440000000511414416030722016265 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gregorian-year-month-weekday.R \name{year_month_weekday} \alias{year_month_weekday} \title{Calendar: year-month-weekday} \usage{ year_month_weekday( year, month = NULL, day = NULL, index = NULL, hour = NULL, minute = NULL, second = NULL, subsecond = NULL, ..., subsecond_precision = NULL ) } \arguments{ \item{year}{\verb{[integer]} The year. Values \verb{[-32767, 32767]} are generally allowed.} \item{month}{\verb{[integer / NULL]} The month. Values \verb{[1, 12]} are allowed.} \item{day}{\verb{[integer / NULL]} The weekday of the month. Values \verb{[1, 7]} are allowed, where \code{1} is Sunday and \code{7} is Saturday.} \item{index}{\verb{[integer / "last" / NULL]} The index specifying that \code{day} is the n-th weekday of the month. Values \verb{[1, 5]} are allowed. If \code{"last"}, then the last instance of \code{day} in the current month is returned.} \item{hour}{\verb{[integer / NULL]} The hour. Values \verb{[0, 23]} are allowed.} \item{minute}{\verb{[integer / NULL]} The minute. Values \verb{[0, 59]} are allowed.} \item{second}{\verb{[integer / NULL]} The second. Values \verb{[0, 59]} are allowed.} \item{subsecond}{\verb{[integer / NULL]} The subsecond. If specified, \code{subsecond_precision} must also be specified to determine how to interpret the \code{subsecond}. If using milliseconds, values \verb{[0, 999]} are allowed. If using microseconds, values \verb{[0, 999999]} are allowed. If using nanoseconds, values \verb{[0, 999999999]} are allowed.} \item{...}{These dots are for future extensions and must be empty.} \item{subsecond_precision}{\verb{[character(1) / NULL]} The precision to interpret \code{subsecond} as. One of: \code{"millisecond"}, \code{"microsecond"}, or \code{"nanosecond"}.} } \value{ A year-month-weekday calendar vector. } \description{ \code{year_month_weekday()} constructs a calendar vector from the Gregorian year, month, weekday, and index specifying that this is the n-th weekday of the month. } \details{ Fields are recycled against each other using \link[vctrs:vector_recycling_rules]{tidyverse recycling rules}. Fields are collected in order until the first \code{NULL} field is located. No fields after the first \code{NULL} field are used. } \examples{ # All Fridays in January, 2019 # Note that there was no 5th Friday in January x <- year_month_weekday( 2019, clock_months$january, clock_weekdays$friday, 1:5 ) x invalid_detect(x) # Resolve this invalid date by using the previous valid date invalid_resolve(x, invalid = "previous") } clock/man/posixt-sequence.Rd0000644000176200001440000002431214162373152015551 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/posixt.R \name{posixt-sequence} \alias{posixt-sequence} \alias{date_seq.POSIXt} \title{Sequences: date-time} \usage{ \method{date_seq}{POSIXt}( from, ..., to = NULL, by = NULL, total_size = NULL, invalid = NULL, nonexistent = NULL, ambiguous = NULL ) } \arguments{ \item{from}{\verb{[POSIXct(1) / POSIXlt(1)]} A date-time to start the sequence from.} \item{...}{These dots are for future extensions and must be empty.} \item{to}{\verb{[POSIXct(1) / POSIXlt(1) / NULL]} A date-time to stop the sequence at. \code{to} is only included in the result if the resulting sequence divides the distance between \code{from} and \code{to} exactly. If \code{to} is supplied along with \code{by}, all components of \code{to} more precise than the precision of \code{by} must match \code{from} exactly. For example, if \code{by = duration_months(1)}, the day, hour, minute, and second components of \code{to} must match the corresponding components of \code{from}. This ensures that the generated sequence is, at a minimum, a weakly monotonic sequence of date-times. The time zone of \code{to} must match the time zone of \code{from} exactly.} \item{by}{\verb{[integer(1) / clock_duration(1) / NULL]} The unit to increment the sequence by. If \code{by} is an integer, it is equivalent to \code{duration_seconds(by)}. If \code{by} is a duration, it is allowed to have a precision of: \itemize{ \item year \item quarter \item month \item week \item day \item hour \item minute \item second }} \item{total_size}{\verb{[positive integer(1) / NULL]} The size of the resulting sequence. If specified alongside \code{to}, this must generate a non-fractional sequence between \code{from} and \code{to}.} \item{invalid}{\verb{[character(1) / NULL]} One of the following invalid date resolution strategies: \itemize{ \item \code{"previous"}: The previous valid instant in time. \item \code{"previous-day"}: The previous valid day in time, keeping the time of day. \item \code{"next"}: The next valid instant in time. \item \code{"next-day"}: The next valid day in time, keeping the time of day. \item \code{"overflow"}: Overflow by the number of days that the input is invalid by. Time of day is dropped. \item \code{"overflow-day"}: Overflow by the number of days that the input is invalid by. Time of day is kept. \item \code{"NA"}: Replace invalid dates with \code{NA}. \item \code{"error"}: Error on invalid dates. } Using either \code{"previous"} or \code{"next"} is generally recommended, as these two strategies maintain the \emph{relative ordering} between elements of the input. If \code{NULL}, defaults to \code{"error"}. If \code{getOption("clock.strict")} is \code{TRUE}, \code{invalid} must be supplied and cannot be \code{NULL}. This is a convenient way to make production code robust to invalid dates.} \item{nonexistent}{\verb{[character / NULL]} One of the following nonexistent time resolution strategies, allowed to be either length 1, or the same length as the input: \itemize{ \item \code{"roll-forward"}: The next valid instant in time. \item \code{"roll-backward"}: The previous valid instant in time. \item \code{"shift-forward"}: Shift the nonexistent time forward by the size of the daylight saving time gap. \item \verb{"shift-backward}: Shift the nonexistent time backward by the size of the daylight saving time gap. \item \code{"NA"}: Replace nonexistent times with \code{NA}. \item \code{"error"}: Error on nonexistent times. } Using either \code{"roll-forward"} or \code{"roll-backward"} is generally recommended over shifting, as these two strategies maintain the \emph{relative ordering} between elements of the input. If \code{NULL}, defaults to \code{"error"}. If \code{getOption("clock.strict")} is \code{TRUE}, \code{nonexistent} must be supplied and cannot be \code{NULL}. This is a convenient way to make production code robust to nonexistent times.} \item{ambiguous}{\verb{[character / zoned_time / POSIXct / list(2) / NULL]} One of the following ambiguous time resolution strategies, allowed to be either length 1, or the same length as the input: \itemize{ \item \code{"earliest"}: Of the two possible times, choose the earliest one. \item \code{"latest"}: Of the two possible times, choose the latest one. \item \code{"NA"}: Replace ambiguous times with \code{NA}. \item \code{"error"}: Error on ambiguous times. } Alternatively, \code{ambiguous} is allowed to be a zoned_time (or POSIXct) that is either length 1, or the same length as the input. If an ambiguous time is encountered, the zoned_time is consulted. If the zoned_time corresponds to a naive_time that is also ambiguous \emph{and} uses the same daylight saving time transition point as the original ambiguous time, then the offset of the zoned_time is used to resolve the ambiguity. If the ambiguity cannot be resolved by consulting the zoned_time, then this method falls back to \code{NULL}. Finally, \code{ambiguous} is allowed to be a list of size 2, where the first element of the list is a zoned_time (as described above), and the second element of the list is an ambiguous time resolution strategy to use when the ambiguous time cannot be resolved by consulting the zoned_time. Specifying a zoned_time on its own is identical to \verb{list(, NULL)}. If \code{NULL}, defaults to \code{"error"}. If \code{getOption("clock.strict")} is \code{TRUE}, \code{ambiguous} must be supplied and cannot be \code{NULL}. Additionally, \code{ambiguous} cannot be specified as a zoned_time on its own, as this implies \code{NULL} for ambiguous times that the zoned_time cannot resolve. Instead, it must be specified as a list alongside an ambiguous time resolution strategy as described above. This is a convenient way to make production code robust to ambiguous times.} } \value{ A date-time vector. } \description{ This is a POSIXct method for the \code{\link[=date_seq]{date_seq()}} generic. \code{date_seq()} generates a date-time (POSIXct) sequence. When calling \code{date_seq()}, exactly two of the following must be specified: \itemize{ \item \code{to} \item \code{by} \item \code{total_size} } } \section{Sequence Generation}{ Different methods are used to generate the sequences, depending on the precision implied by \code{by}. They are intended to generate the most intuitive sequences, especially around daylight saving time gaps and fallbacks. See the examples for more details. \subsection{Calendrical based sequences:}{ These convert to a naive-time, then to a year-month-day, generate the sequence, then convert back to a date-time. \itemize{ \item \code{by = duration_years()} \item \code{by = duration_quarters()} \item \code{by = duration_months()} } } \subsection{Naive-time based sequences:}{ These convert to a naive-time, generate the sequence, then convert back to a date-time. \itemize{ \item \code{by = duration_weeks()} \item \code{by = duration_days()} } } \subsection{Sys-time based sequences:}{ These convert to a sys-time, generate the sequence, then convert back to a date-time. \itemize{ \item \code{by = duration_hours()} \item \code{by = duration_minutes()} \item \code{by = duration_seconds()} } } } \examples{ zone <- "America/New_York" from <- date_time_build(2019, 1, zone = zone) to <- date_time_build(2019, 1, second = 50, zone = zone) # Defaults to second precision sequence date_seq(from, to = to, by = 7) to <- date_time_build(2019, 1, 5, zone = zone) # Use durations to change to alternative precisions date_seq(from, to = to, by = duration_days(1)) date_seq(from, to = to, by = duration_hours(10)) date_seq(from, by = duration_minutes(-2), total_size = 3) # Note that components of `to` more precise than the precision of `by` # must match `from` exactly. For example, this is not well defined: from <- date_time_build(2019, 1, 1, 0, 1, 30, zone = zone) to <- date_time_build(2019, 1, 1, 5, 2, 20, zone = zone) try(date_seq(from, to = to, by = duration_hours(1))) # The minute and second components of `to` must match `from` to <- date_time_build(2019, 1, 1, 5, 1, 30, zone = zone) date_seq(from, to = to, by = duration_hours(1)) # --------------------------------------------------------------------------- # Invalid dates must be resolved with the `invalid` argument from <- date_time_build(2019, 1, 31, zone = zone) to <- date_time_build(2019, 12, 31, zone = zone) try(date_seq(from, to = to, by = duration_months(1))) date_seq(from, to = to, by = duration_months(1), invalid = "previous-day") # Compare this to the base R result, which is often a source of confusion seq(from, to = to, by = "1 month") # This is equivalent to the overflow invalid resolution strategy date_seq(from, to = to, by = duration_months(1), invalid = "overflow") # --------------------------------------------------------------------------- # This date-time is 2 days before a daylight saving time gap that occurred # on 2021-03-14 between 01:59:59 -> 03:00:00 from <- as.POSIXct("2021-03-12 02:30:00", "America/New_York") # So creating a daily sequence lands us in that daylight saving time gap, # creating a nonexistent time try(date_seq(from, by = duration_days(1), total_size = 5)) # Resolve the nonexistent time with `nonexistent`. Note that this importantly # allows times after the gap to retain the `02:30:00` time. date_seq(from, by = duration_days(1), total_size = 5, nonexistent = "roll-forward") # Compare this to the base R behavior, where the hour is adjusted from 2->3 # as you cross the daylight saving time gap, and is never restored. This is # equivalent to always using sys-time (rather than naive-time, like clock # uses for daily sequences). seq(from, by = "1 day", length.out = 5) # You can replicate this behavior by generating a second precision sequence # of 86,400 seconds. Seconds always add in sys-time. date_seq(from, by = duration_seconds(86400), total_size = 5) # --------------------------------------------------------------------------- # Usage of `to` and `total_size` must generate a non-fractional sequence # between `from` and `to` from <- date_time_build(2019, 1, 1, 0, 0, 0, zone = "America/New_York") to <- date_time_build(2019, 1, 1, 0, 0, 3, zone = "America/New_York") # These are fine date_seq(from, to = to, total_size = 2) date_seq(from, to = to, total_size = 4) # But this is not! try(date_seq(from, to = to, total_size = 3)) } clock/man/calendar-boundary.Rd0000644000176200001440000000312314422221153015774 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/calendar.R \name{calendar-boundary} \alias{calendar-boundary} \alias{calendar_start} \alias{calendar_end} \title{Boundaries: calendars} \usage{ calendar_start(x, precision) calendar_end(x, precision) } \arguments{ \item{x}{\verb{[calendar]} A calendar vector.} \item{precision}{\verb{[character(1)]} A precision. Allowed precisions are dependent on the calendar used.} } \value{ \code{x} at the same precision, but with some components altered to be at the boundary value. } \description{ \itemize{ \item \code{calendar_start()} computes the start of a calendar at a particular \code{precision}, such as the "start of the quarter". \item \code{calendar_end()} computes the end of a calendar at a particular \code{precision}, such as the "end of the month". } For both \code{calendar_start()} and \code{calendar_end()}, the precision of \code{x} is always retained. Each calendar has its own help page describing the precisions that you can compute a boundary at: \itemize{ \item \link[=year-month-day-boundary]{year-month-day} \item \link[=year-month-weekday-boundary]{year-month-weekday} \item \link[=year-week-day-boundary]{year-week-day} \item \link[=iso-year-week-day-boundary]{iso-year-week-day} \item \link[=year-quarter-day-boundary]{year-quarter-day} \item \link[=year-day-boundary]{year-day} } } \examples{ # Hour precision x <- year_month_day(2019, 2:4, 5, 6) x # Compute the start of the month calendar_start(x, "month") # Or the end of the month, notice that the hour value is adjusted as well calendar_end(x, "month") } clock/man/date_spanning_seq.Rd0000644000176200001440000000255114425175541016104 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/date.R \name{date_spanning_seq} \alias{date_spanning_seq} \title{Spanning sequence: date and date-time} \usage{ date_spanning_seq(x) } \arguments{ \item{x}{\verb{[Date / POSIXct / POSIXlt]} A date or date-time vector.} } \value{ A sequence along \verb{[min(x), max(x)]}. } \description{ \code{date_spanning_seq()} generates a regular sequence along the span of \code{x}, i.e. along \verb{[min(x), max(x)]}. For dates, this generates a day precision sequence, and for date-times it generates a second precision sequence. } \details{ Missing and infinite values are automatically removed before the sequence is generated. For date-times, sys-time based sequences are generated, consistent with \code{\link[=posixt-sequence]{date_seq()}} when using a second precision \code{by} value. If you need more precise sequence generation, call \code{\link[=range]{range()}} and \code{\link[=date_seq]{date_seq()}} directly. } \examples{ x <- date_build(2020, c(1, 2, 1), c(10, 5, 12)) date_spanning_seq(x) # Missing and infinite dates are removed before the sequence is generated x <- c(x, NA, Inf, -Inf) x date_spanning_seq(x) # For date-times, sequences are generated at second precision x <- date_time_build( 2020, 1, 2, 3, c(5, 4, 5), c(10, 48, 12), zone = "America/New_York" ) x date_spanning_seq(x) } clock/man/year-day-setters.Rd0000644000176200001440000000365114016222032015606 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gregorian-year-day.R \name{year-day-setters} \alias{year-day-setters} \alias{set_year.clock_year_day} \alias{set_day.clock_year_day} \alias{set_hour.clock_year_day} \alias{set_minute.clock_year_day} \alias{set_second.clock_year_day} \alias{set_millisecond.clock_year_day} \alias{set_microsecond.clock_year_day} \alias{set_nanosecond.clock_year_day} \title{Setters: year-day} \usage{ \method{set_year}{clock_year_day}(x, value, ...) \method{set_day}{clock_year_day}(x, value, ...) \method{set_hour}{clock_year_day}(x, value, ...) \method{set_minute}{clock_year_day}(x, value, ...) \method{set_second}{clock_year_day}(x, value, ...) \method{set_millisecond}{clock_year_day}(x, value, ...) \method{set_microsecond}{clock_year_day}(x, value, ...) \method{set_nanosecond}{clock_year_day}(x, value, ...) } \arguments{ \item{x}{\verb{[clock_year_day]} A year-day vector.} \item{value}{\verb{[integer / "last"]} The value to set the component to. For \code{set_day()}, this can also be \code{"last"} to set the day to the last day of the year.} \item{...}{These dots are for future extensions and must be empty.} } \value{ \code{x} with the component set. } \description{ These are year-day methods for the \link[=clock-setters]{setter generics}. \itemize{ \item \code{set_year()} sets the Gregorian year. \item \code{set_day()} sets the day of the year. Valid values are in the range of \verb{[1, 366]}. \item There are sub-daily setters for setting more precise components. } } \examples{ x <- year_day(2019) # Set the day set_day(x, 12:14) # Set to the "last" day of the year set_day(x, "last") # Set to an invalid day of the year invalid <- set_day(x, 366) invalid # Then resolve the invalid day by choosing the next valid day invalid_resolve(invalid, invalid = "next") # Cannot set a component two levels more precise than where you currently are try(set_hour(x, 5)) } clock/man/date_leap_year.Rd0000644000176200001440000000131114073622034015342 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/date.R \name{date_leap_year} \alias{date_leap_year} \title{Is the year a leap year?} \usage{ date_leap_year(x) } \arguments{ \item{x}{\verb{[Date / POSIXct / POSIXlt]} A date or date-time to detect leap years in.} } \value{ A logical vector the same size as \code{x}. Returns \code{TRUE} if in a leap year, \code{FALSE} if not in a leap year, and \code{NA} if \code{x} is \code{NA}. } \description{ \code{date_leap_year()} detects if the year is a leap year. } \examples{ x <- as.Date("2019-01-01") x <- add_years(x, 0:5) date_leap_year(x) y <- as.POSIXct("2019-01-01", "America/New_York") y <- add_years(y, 0:5) date_leap_year(y) } clock/man/year_week_day.Rd0000644000176200001440000000545414422221153015223 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/week-year-week-day.R \name{year_week_day} \alias{year_week_day} \title{Calendar: year-week-day} \usage{ year_week_day( year, week = NULL, day = NULL, hour = NULL, minute = NULL, second = NULL, subsecond = NULL, ..., start = NULL, subsecond_precision = NULL ) } \arguments{ \item{year}{\verb{[integer]} The year. Values \verb{[-32767, 32767]} are generally allowed.} \item{week}{\verb{[integer / "last" / NULL]} The week. Values \verb{[1, 53]} are allowed. If \code{"last"}, then the last week of the year is returned.} \item{day}{\verb{[integer / NULL]} The day of the week. Values \verb{[1, 7]} are allowed, with \verb{1 = start of week} and \verb{7 = end of week}, in accordance with \code{start}.} \item{hour}{\verb{[integer / NULL]} The hour. Values \verb{[0, 23]} are allowed.} \item{minute}{\verb{[integer / NULL]} The minute. Values \verb{[0, 59]} are allowed.} \item{second}{\verb{[integer / NULL]} The second. Values \verb{[0, 59]} are allowed.} \item{subsecond}{\verb{[integer / NULL]} The subsecond. If specified, \code{subsecond_precision} must also be specified to determine how to interpret the \code{subsecond}. If using milliseconds, values \verb{[0, 999]} are allowed. If using microseconds, values \verb{[0, 999999]} are allowed. If using nanoseconds, values \verb{[0, 999999999]} are allowed.} \item{...}{These dots are for future extensions and must be empty.} \item{start}{\verb{[integer(1) / NULL]} The day to consider the start of the week. 1 = Sunday and 7 = Saturday. Use \link{clock_weekdays} for a readable way to specify the start. If \code{NULL}, a \code{start} of Sunday will be used.} \item{subsecond_precision}{\verb{[character(1) / NULL]} The precision to interpret \code{subsecond} as. One of: \code{"millisecond"}, \code{"microsecond"}, or \code{"nanosecond"}.} } \value{ A year-week-day calendar vector. } \description{ \code{year_week_day()} constructs a calendar from the year, week number, week day, and the \code{start} of the week. Using \code{start = clock_weekdays$monday} represents the ISO week calendar and is equivalent to using \code{\link[=iso_year_week_day]{iso_year_week_day()}}. Using \code{start = clock_weekdays$sunday} is how Epidemiologists encode their week-based data. } \details{ Fields are recycled against each other using \link[vctrs:vector_recycling_rules]{tidyverse recycling rules}. Fields are collected in order until the first \code{NULL} field is located. No fields after the first \code{NULL} field are used. } \examples{ # Year-week x <- year_week_day(2019:2025, "last") x # Start the week on Monday y <- year_week_day(2019:2025, "last", start = clock_weekdays$monday) y # Last days of the year as_year_month_day(set_day(x, 7)) as_year_month_day(set_day(y, 7)) } clock/man/sys_time_now.Rd0000644000176200001440000000111214017505732015125 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/sys-time.R \name{sys_time_now} \alias{sys_time_now} \title{What is the current sys-time?} \usage{ sys_time_now() } \value{ A sys-time of the current time in UTC. } \description{ \code{sys_time_now()} returns the current time in UTC. } \details{ The time is returned with a nanosecond precision, but the actual amount of data returned is OS dependent. Usually, information at at least the microsecond level is returned, with some platforms returning nanosecond information. } \examples{ x <- sys_time_now() } clock/man/year-month-weekday-group.Rd0000644000176200001440000000305114011271365017252 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gregorian-year-month-weekday.R \name{year-month-weekday-group} \alias{year-month-weekday-group} \alias{calendar_group.clock_year_month_weekday} \title{Grouping: year-month-weekday} \usage{ \method{calendar_group}{clock_year_month_weekday}(x, precision, ..., n = 1L) } \arguments{ \item{x}{\verb{[clock_year_month_weekday]} A year-month-weekday vector.} \item{precision}{\verb{[character(1)]} One of: \itemize{ \item \code{"year"} \item \code{"month"} \item \code{"hour"} \item \code{"minute"} \item \code{"second"} \item \code{"millisecond"} \item \code{"microsecond"} \item \code{"nanosecond"} }} \item{...}{These dots are for future extensions and must be empty.} \item{n}{\verb{[positive integer(1)]} A single positive integer specifying a multiple of \code{precision} to use.} } \value{ \code{x} grouped at the specified \code{precision}. } \description{ This is a year-month-weekday method for the \code{\link[=calendar_group]{calendar_group()}} generic. Grouping for a year-month-weekday object can be done at any precision except for \code{"day"}, as long as \code{x} is at least as precise as \code{precision}. } \details{ Grouping by \code{"day"} is undefined for a year-month-weekday because there are two day fields, the weekday and the index, and there is no clear way to define how to group by that. } \examples{ x <- year_month_weekday(2019, 1:12, clock_weekdays$sunday, 1, 00, 05, 05) x # Group by 3 months - drops more precise components! calendar_group(x, "month", n = 3) } clock/man/iso-year-week-day-group.Rd0000644000176200001440000000254714007014157017004 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/iso-year-week-day.R \name{iso-year-week-day-group} \alias{iso-year-week-day-group} \alias{calendar_group.clock_iso_year_week_day} \title{Grouping: iso-year-week-day} \usage{ \method{calendar_group}{clock_iso_year_week_day}(x, precision, ..., n = 1L) } \arguments{ \item{x}{\verb{[clock_iso_year_week_day]} A iso-year-week-day vector.} \item{precision}{\verb{[character(1)]} One of: \itemize{ \item \code{"year"} \item \code{"week"} \item \code{"day"} \item \code{"hour"} \item \code{"minute"} \item \code{"second"} \item \code{"millisecond"} \item \code{"microsecond"} \item \code{"nanosecond"} }} \item{...}{These dots are for future extensions and must be empty.} \item{n}{\verb{[positive integer(1)]} A single positive integer specifying a multiple of \code{precision} to use.} } \value{ \code{x} grouped at the specified \code{precision}. } \description{ This is a iso-year-week-day method for the \code{\link[=calendar_group]{calendar_group()}} generic. Grouping for a iso-year-week-day object can be done at any precision, as long as \code{x} is at least as precise as \code{precision}. } \examples{ x <- iso_year_week_day(2019, 1:52) # Group by 3 ISO weeks calendar_group(x, "week", n = 3) y <- iso_year_week_day(2000:2020, 1, 1) # Group by 2 ISO years calendar_group(y, "year", n = 2) } clock/man/format.clock_zoned_time.Rd0000644000176200001440000001352714133632415017220 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/zoned-time.R \name{format.clock_zoned_time} \alias{format.clock_zoned_time} \title{Formatting: zoned-time} \usage{ \method{format}{clock_zoned_time}(x, ..., format = NULL, locale = clock_locale(), abbreviate_zone = FALSE) } \arguments{ \item{x}{\verb{[clock_zoned_time]} A zoned-time.} \item{...}{\verb{[dots]} Not used, but no error will be thrown if not empty to remain compatible with usage of the \code{format()} generic.} \item{format}{\verb{[character(1) / NULL]} If \code{NULL}, a default format is used, which depends on the type of the input. Otherwise, a format string which is a combination of: \strong{Year} \itemize{ \item \verb{\%C}: The year divided by 100 using floored division. If the result is a single decimal digit, it is prefixed with \code{0}. \item \verb{\%y}: The last two decimal digits of the year. If the result is a single digit it is prefixed by \code{0}. \item \verb{\%Y}: The year as a decimal number. If the result is less than four digits it is left-padded with \code{0} to four digits. } \strong{Month} \itemize{ \item \verb{\%b}, \verb{\%h}: The \code{locale}'s abbreviated month name. \item \verb{\%B}: The \code{locale}'s full month name. \item \verb{\%m}: The month as a decimal number. January is \code{01}. If the result is a single digit, it is prefixed with \code{0}. } \strong{Day} \itemize{ \item \verb{\%d}: The day of month as a decimal number. If the result is a single decimal digit, it is prefixed with \code{0}. } \strong{Day of the week} \itemize{ \item \verb{\%a}: The \code{locale}'s abbreviated weekday name. \item \verb{\%A}: The \code{locale}'s full weekday name. \item \verb{\%w}: The weekday as a decimal number (\code{0-6}), where Sunday is \code{0}. } \strong{ISO 8601 week-based year} \itemize{ \item \verb{\%g}: The last two decimal digits of the ISO week-based year. If the result is a single digit it is prefixed by \code{0}. \item \verb{\%G}: The ISO week-based year as a decimal number. If the result is less than four digits it is left-padded with \code{0} to four digits. \item \verb{\%V}: The ISO week-based week number as a decimal number. If the result is a single digit, it is prefixed with \code{0}. \item \verb{\%u}: The ISO weekday as a decimal number (\code{1-7}), where Monday is \code{1}. } \strong{Week of the year} \itemize{ \item \verb{\%U}: The week number of the year as a decimal number. The first Sunday of the year is the first day of week \code{01}. Days of the same year prior to that are in week \code{00}. If the result is a single digit, it is prefixed with \code{0}. \item \verb{\%W}: The week number of the year as a decimal number. The first Monday of the year is the first day of week \code{01}. Days of the same year prior to that are in week \code{00}. If the result is a single digit, it is prefixed with \code{0}. } \strong{Day of the year} \itemize{ \item \verb{\%j}: The day of the year as a decimal number. January 1 is \code{001}. If the result is less than three digits, it is left-padded with \code{0} to three digits. } \strong{Date} \itemize{ \item \verb{\%D}, \verb{\%x}: Equivalent to \verb{\%m/\%d/\%y}. \item \verb{\%F}: Equivalent to \verb{\%Y-\%m-\%d}. } \strong{Time of day} \itemize{ \item \verb{\%H}: The hour (24-hour clock) as a decimal number. If the result is a single digit, it is prefixed with \code{0}. \item \verb{\%I}: The hour (12-hour clock) as a decimal number. If the result is a single digit, it is prefixed with \code{0}. \item \verb{\%M}: The minute as a decimal number. If the result is a single digit, it is prefixed with \code{0}. \item \verb{\%S}: Seconds as a decimal number. Fractional seconds are printed at the precision of the input. The character for the decimal point is localized according to \code{locale}. \item \verb{\%p}: The \code{locale}'s equivalent of the AM/PM designations associated with a 12-hour clock. \item \verb{\%R}: Equivalent to \verb{\%H:\%M}. \item \verb{\%T}, \verb{\%X}: Equivalent to \verb{\%H:\%M:\%S}. \item \verb{\%r}: Nearly equivalent to \verb{\%I:\%M:\%S \%p}, but seconds are always printed at second precision. } \strong{Time zone} \itemize{ \item \verb{\%z}: The offset from UTC in the ISO 8601 format. For example \code{-0430} refers to 4 hours 30 minutes behind UTC. If the offset is zero, \code{+0000} is used. The modified command \verb{\%Ez} inserts a \code{:} between the hour and minutes, like \code{-04:30}. \item \verb{\%Z}: The full time zone name. If \code{abbreviate_zone} is \code{TRUE}, the time zone abbreviation. } \strong{Miscellaneous} \itemize{ \item \verb{\%c}: A date and time representation. Similar to, but not exactly the same as, \verb{\%a \%b \%d \%H:\%M:\%S \%Y}. \item \code{\%\%}: A \verb{\%} character. \item \verb{\%n}: A newline character. \item \verb{\%t}: A horizontal-tab character. }} \item{locale}{\verb{[clock_locale]} A locale object created from \code{\link[=clock_locale]{clock_locale()}}.} \item{abbreviate_zone}{\verb{[logical(1)]} If \code{TRUE}, \verb{\%Z} returns an abbreviated time zone name. If \code{FALSE}, \verb{\%Z} returns the full time zone name.} } \value{ A character vector of the formatted input. } \description{ This is a zoned-time method for the \code{\link[=format]{format()}} generic. This function allows you to format a zoned-time using a flexible \code{format} string. If \code{format} is \code{NULL}, a default format of \code{"\%Y-\%m-\%dT\%H:\%M:\%S\%Ez[\%Z]"} is used. This matches the default format that \code{\link[=zoned_time_parse_complete]{zoned_time_parse_complete()}} parses. Additionally, this format matches the de-facto standard extension to RFC 3339 for creating completely unambiguous date-times. } \examples{ x <- year_month_day(2019, 1, 1) x <- as_zoned_time(as_naive_time(x), "America/New_York") format(x) format(x, format = "\%B \%d, \%Y") format(x, format = "\%B \%d, \%Y", locale = clock_locale("fr")) } clock/man/time-point-arithmetic.Rd0000644000176200001440000000761714416031540016634 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/time-point.R \name{time-point-arithmetic} \alias{time-point-arithmetic} \alias{add_weeks.clock_time_point} \alias{add_days.clock_time_point} \alias{add_hours.clock_time_point} \alias{add_minutes.clock_time_point} \alias{add_seconds.clock_time_point} \alias{add_milliseconds.clock_time_point} \alias{add_microseconds.clock_time_point} \alias{add_nanoseconds.clock_time_point} \title{Arithmetic: Time points} \usage{ \method{add_weeks}{clock_time_point}(x, n, ...) \method{add_days}{clock_time_point}(x, n, ...) \method{add_hours}{clock_time_point}(x, n, ...) \method{add_minutes}{clock_time_point}(x, n, ...) \method{add_seconds}{clock_time_point}(x, n, ...) \method{add_milliseconds}{clock_time_point}(x, n, ...) \method{add_microseconds}{clock_time_point}(x, n, ...) \method{add_nanoseconds}{clock_time_point}(x, n, ...) } \arguments{ \item{x}{\verb{[clock_sys_time / clock_naive_time]} A time point vector.} \item{n}{\verb{[integer / clock_duration]} An integer vector to be converted to a duration, or a duration corresponding to the arithmetic function being used. This corresponds to the number of duration units to add. \code{n} may be negative to subtract units of duration.} \item{...}{These dots are for future extensions and must be empty.} } \value{ \code{x} after performing the arithmetic. } \description{ These are naive-time and sys-time methods for the \link[=clock-arithmetic]{arithmetic generics}. \itemize{ \item \code{add_weeks()} \item \code{add_days()} \item \code{add_hours()} \item \code{add_minutes()} \item \code{add_seconds()} \item \code{add_milliseconds()} \item \code{add_microseconds()} \item \code{add_nanoseconds()} } When working with zoned times, generally you convert to either sys-time or naive-time, add the duration, then convert back to zoned time. Typically, \emph{weeks and days} are added in \emph{naive-time}, and \emph{hours, minutes, seconds, and subseconds} are added in \emph{sys-time}. If you aren't using zoned times, arithmetic on sys-times and naive-time is equivalent. If you need to add larger irregular units of time, such as months, quarters, or years, convert to a calendar type with a converter like \code{\link[=as_year_month_day]{as_year_month_day()}}. } \details{ \code{x} and \code{n} are recycled against each other using \link[vctrs:vector_recycling_rules]{tidyverse recycling rules}. } \examples{ library(magrittr) # Say you started with this zoned time, and you want to add 1 day to it x <- as_naive_time(year_month_day(1970, 04, 25, 02, 30, 00)) x <- as_zoned_time(x, "America/New_York") x # Note that there was a daylight saving time gap on 1970-04-26 where # we jumped from 01:59:59 -> 03:00:00. # You can choose to add 1 day in "system time", by first converting to # sys-time (the equivalent UTC time), adding the day, then converting back to # zoned time. If you sat still for exactly 86,400 seconds, this is the # time that you would see after daylight saving time adjusted the clock # (note that the hour field is shifted forward by the size of the gap) as_sys_time(x) x \%>\% as_sys_time() \%>\% add_days(1) \%>\% as_zoned_time(zoned_time_zone(x)) # Alternatively, you can add 1 day in "naive time". Naive time represents # a clock time with a yet-to-be-specified time zone. It tries to maintain # smaller units where possible, so adding 1 day would attempt to return # "1970-04-26T02:30:00" in the America/New_York time zone, but... as_naive_time(x) try({ x \%>\% as_naive_time() \%>\% add_days(1) \%>\% as_zoned_time(zoned_time_zone(x)) }) # ...this time doesn't exist in that time zone! It is "nonexistent". # You can resolve nonexistent times by setting the `nonexistent` argument # when converting to zoned time. Let's roll forward to the next available # moment in time. x \%>\% as_naive_time() \%>\% add_days(1) \%>\% as_zoned_time(zoned_time_zone(x), nonexistent = "roll-forward") } clock/man/as_year_month_weekday.Rd0000644000176200001440000000166714427270231016764 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gregorian-year-month-weekday.R \name{as_year_month_weekday} \alias{as_year_month_weekday} \title{Convert to year-month-weekday} \usage{ as_year_month_weekday(x, ...) } \arguments{ \item{x}{\verb{[vector]} A vector to convert to year-month-weekday.} \item{...}{These dots are for future extensions and must be empty.} } \value{ A year-month-weekday vector. } \description{ \code{as_year_month_weekday()} converts a vector to the year-month-weekday calendar. Time points, Dates, POSIXct, and other calendars can all be converted to year-month-weekday. } \examples{ # From Date as_year_month_weekday(as.Date("2019-01-01")) # From POSIXct, which assumes that the naive time is what should be converted as_year_month_weekday(as.POSIXct("2019-01-01 02:30:30", "America/New_York")) # From other calendars as_year_month_weekday(year_quarter_day(2019, quarter = 2, day = 50)) } clock/man/date-time-parse.Rd0000644000176200001440000005074314265310513015401 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/posixt.R \name{date-time-parse} \alias{date-time-parse} \alias{date_time_parse} \alias{date_time_parse_complete} \alias{date_time_parse_abbrev} \alias{date_time_parse_RFC_3339} \title{Parsing: date-time} \usage{ date_time_parse( x, zone, ..., format = NULL, locale = clock_locale(), nonexistent = NULL, ambiguous = NULL ) date_time_parse_complete(x, ..., format = NULL, locale = clock_locale()) date_time_parse_abbrev(x, zone, ..., format = NULL, locale = clock_locale()) date_time_parse_RFC_3339(x, ..., separator = "T", offset = "Z") } \arguments{ \item{x}{\verb{[character]} A character vector to parse.} \item{zone}{\verb{[character(1)]} A full time zone name.} \item{...}{These dots are for future extensions and must be empty.} \item{format}{\verb{[character / NULL]} A format string. A combination of the following commands, or \code{NULL}, in which case a default format string is used. A vector of multiple format strings can be supplied. They will be tried in the order they are provided. \strong{Year} \itemize{ \item \verb{\%C}: The century as a decimal number. The modified command \verb{\%NC} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. \item \verb{\%y}: The last two decimal digits of the year. If the century is not otherwise specified (e.g. with \verb{\%C}), values in the range \verb{[69 - 99]} are presumed to refer to the years \verb{[1969 - 1999]}, and values in the range \verb{[00 - 68]} are presumed to refer to the years \verb{[2000 - 2068]}. The modified command \verb{\%Ny}, where \code{N} is a positive decimal integer, specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. \item \verb{\%Y}: The year as a decimal number. The modified command \verb{\%NY} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{4}. Leading zeroes are permitted but not required. } \strong{Month} \itemize{ \item \verb{\%b}, \verb{\%B}, \verb{\%h}: The \code{locale}'s full or abbreviated case-insensitive month name. \item \verb{\%m}: The month as a decimal number. January is \code{1}. The modified command \verb{\%Nm} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. } \strong{Day} \itemize{ \item \verb{\%d}, \verb{\%e}: The day of the month as a decimal number. The modified command \verb{\%Nd} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. } \strong{Day of the week} \itemize{ \item \verb{\%a}, \verb{\%A}: The \code{locale}'s full or abbreviated case-insensitive weekday name. \item \verb{\%w}: The weekday as a decimal number (\code{0-6}), where Sunday is \code{0}. The modified command \verb{\%Nw} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{1}. Leading zeroes are permitted but not required. } \strong{ISO 8601 week-based year} \itemize{ \item \verb{\%g}: The last two decimal digits of the ISO week-based year. The modified command \verb{\%Ng} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. \item \verb{\%G}: The ISO week-based year as a decimal number. The modified command \verb{\%NG} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{4}. Leading zeroes are permitted but not required. \item \verb{\%V}: The ISO week-based week number as a decimal number. The modified command \verb{\%NV} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. \item \verb{\%u}: The ISO weekday as a decimal number (\code{1-7}), where Monday is \code{1}. The modified command \verb{\%Nu} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{1}. Leading zeroes are permitted but not required. } \strong{Week of the year} \itemize{ \item \verb{\%U}: The week number of the year as a decimal number. The first Sunday of the year is the first day of week \code{01}. Days of the same year prior to that are in week \code{00}. The modified command \verb{\%NU} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. \item \verb{\%W}: The week number of the year as a decimal number. The first Monday of the year is the first day of week \code{01}. Days of the same year prior to that are in week \code{00}. The modified command \verb{\%NW} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. } \strong{Day of the year} \itemize{ \item \verb{\%j}: The day of the year as a decimal number. January 1 is \code{1}. The modified command \verb{\%Nj} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{3}. Leading zeroes are permitted but not required. } \strong{Date} \itemize{ \item \verb{\%D}, \verb{\%x}: Equivalent to \verb{\%m/\%d/\%y}. \item \verb{\%F}: Equivalent to \verb{\%Y-\%m-\%d}. If modified with a width (like \verb{\%NF}), the width is applied to only \verb{\%Y}. } \strong{Time of day} \itemize{ \item \verb{\%H}: The hour (24-hour clock) as a decimal number. The modified command \verb{\%NH} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. \item \verb{\%I}: The hour (12-hour clock) as a decimal number. The modified command \verb{\%NI} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. \item \verb{\%M}: The minutes as a decimal number. The modified command \verb{\%NM} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. \item \verb{\%S}: The seconds as a decimal number. Leading zeroes are permitted but not required. If encountered, the \code{locale} determines the decimal point character. Generally, the maximum number of characters to read is determined by the precision that you are parsing at. For example, a precision of \code{"second"} would read a maximum of 2 characters, while a precision of \code{"millisecond"} would read a maximum of 6 (2 for the values before the decimal point, 1 for the decimal point, and 3 for the values after it). The modified command \verb{\%NS}, where \code{N} is a positive decimal integer, can be used to exactly specify the maximum number of characters to read. This is only useful if you happen to have seconds with more than 1 leading zero. \item \verb{\%p}: The \code{locale}'s equivalent of the AM/PM designations associated with a 12-hour clock. The command \verb{\%I} must precede \verb{\%p} in the format string. \item \verb{\%R}: Equivalent to \verb{\%H:\%M}. \item \verb{\%T}, \verb{\%X}: Equivalent to \verb{\%H:\%M:\%S}. \item \verb{\%r}: Equivalent to \verb{\%I:\%M:\%S \%p}. } \strong{Time zone} \itemize{ \item \verb{\%z}: The offset from UTC in the format \verb{[+|-]hh[mm]}. For example \code{-0430} refers to 4 hours 30 minutes behind UTC. And \code{04} refers to 4 hours ahead of UTC. The modified command \verb{\%Ez} parses a \code{:} between the hours and minutes and leading zeroes on the hour field are optional: \verb{[+|-]h[h][:mm]}. For example \code{-04:30} refers to 4 hours 30 minutes behind UTC. And \code{4} refers to 4 hours ahead of UTC. \item \verb{\%Z}: The full time zone name or the time zone abbreviation, depending on the function being used. A single word is parsed. This word can only contain characters that are alphanumeric, or one of \code{'_'}, \code{'/'}, \code{'-'} or \code{'+'}. } \strong{Miscellaneous} \itemize{ \item \verb{\%c}: A date and time representation. Equivalent to \verb{\%a \%b \%d \%H:\%M:\%S \%Y}. \item \code{\%\%}: A \verb{\%} character. \item \verb{\%n}: Matches one white space character. \verb{\%n}, \verb{\%t}, and a space can be combined to match a wide range of white-space patterns. For example \code{"\%n "} matches one or more white space characters, and \code{"\%n\%t\%t"} matches one to three white space characters. \item \verb{\%t}: Matches zero or one white space characters. }} \item{locale}{\verb{[clock_locale]} A locale object created from \code{\link[=clock_locale]{clock_locale()}}.} \item{nonexistent}{\verb{[character / NULL]} One of the following nonexistent time resolution strategies, allowed to be either length 1, or the same length as the input: \itemize{ \item \code{"roll-forward"}: The next valid instant in time. \item \code{"roll-backward"}: The previous valid instant in time. \item \code{"shift-forward"}: Shift the nonexistent time forward by the size of the daylight saving time gap. \item \verb{"shift-backward}: Shift the nonexistent time backward by the size of the daylight saving time gap. \item \code{"NA"}: Replace nonexistent times with \code{NA}. \item \code{"error"}: Error on nonexistent times. } Using either \code{"roll-forward"} or \code{"roll-backward"} is generally recommended over shifting, as these two strategies maintain the \emph{relative ordering} between elements of the input. If \code{NULL}, defaults to \code{"error"}. If \code{getOption("clock.strict")} is \code{TRUE}, \code{nonexistent} must be supplied and cannot be \code{NULL}. This is a convenient way to make production code robust to nonexistent times.} \item{ambiguous}{\verb{[character / zoned_time / POSIXct / list(2) / NULL]} One of the following ambiguous time resolution strategies, allowed to be either length 1, or the same length as the input: \itemize{ \item \code{"earliest"}: Of the two possible times, choose the earliest one. \item \code{"latest"}: Of the two possible times, choose the latest one. \item \code{"NA"}: Replace ambiguous times with \code{NA}. \item \code{"error"}: Error on ambiguous times. } Alternatively, \code{ambiguous} is allowed to be a zoned_time (or POSIXct) that is either length 1, or the same length as the input. If an ambiguous time is encountered, the zoned_time is consulted. If the zoned_time corresponds to a naive_time that is also ambiguous \emph{and} uses the same daylight saving time transition point as the original ambiguous time, then the offset of the zoned_time is used to resolve the ambiguity. If the ambiguity cannot be resolved by consulting the zoned_time, then this method falls back to \code{NULL}. Finally, \code{ambiguous} is allowed to be a list of size 2, where the first element of the list is a zoned_time (as described above), and the second element of the list is an ambiguous time resolution strategy to use when the ambiguous time cannot be resolved by consulting the zoned_time. Specifying a zoned_time on its own is identical to \verb{list(, NULL)}. If \code{NULL}, defaults to \code{"error"}. If \code{getOption("clock.strict")} is \code{TRUE}, \code{ambiguous} must be supplied and cannot be \code{NULL}. Additionally, \code{ambiguous} cannot be specified as a zoned_time on its own, as this implies \code{NULL} for ambiguous times that the zoned_time cannot resolve. Instead, it must be specified as a list alongside an ambiguous time resolution strategy as described above. This is a convenient way to make production code robust to ambiguous times.} \item{separator}{\verb{[character(1)]} The separator between the date and time components of the string. One of: \itemize{ \item \code{"T"} \item \code{"t"} \item \code{" "} }} \item{offset}{\verb{[character(1)]} The format of the offset from UTC contained in the string. One of: \itemize{ \item \code{"Z"} \item \code{"z"} \item \code{"\%z"} to parse a numeric offset of the form \code{"+0430"} \item \code{"\%Ez"} to parse a numeric offset of the form \code{"+04:30"} }} } \value{ A POSIXct. } \description{ There are four parsers for parsing strings into POSIXct date-times, \code{date_time_parse()}, \code{date_time_parse_complete()}, \code{date_time_parse_abbrev()}, and \code{date_time_parse_RFC_3339()}. \subsection{date_time_parse()}{ \code{date_time_parse()} is useful for strings like \code{"2019-01-01 00:00:00"}, where the UTC offset and full time zone name are not present in the string. The string is first parsed as a naive-time without any time zone assumptions, and is then converted to a POSIXct with the supplied \code{zone}. Because converting from naive-time to POSIXct may result in nonexistent or ambiguous times due to daylight saving time, these must be resolved explicitly with the \code{nonexistent} and \code{ambiguous} arguments. \code{date_time_parse()} completely ignores the \verb{\%z} and \verb{\%Z} commands. The only time zone specific information that is used is the \code{zone}. The default \code{format} used is \code{"\%Y-\%m-\%d \%H:\%M:\%S"}. This matches the default result from calling \code{format()} on a POSIXct date-time. } \subsection{date_time_parse_complete()}{ \code{date_time_parse_complete()} is a parser for \emph{complete} date-time strings, like \code{"2019-01-01T00:00:00-05:00[America/New_York]"}. A complete date-time string has both the time zone offset and full time zone name in the string, which is the only way for the string itself to contain all of the information required to unambiguously construct a zoned-time. Because of this, \code{date_time_parse_complete()} requires both the \verb{\%z} and \verb{\%Z} commands to be supplied in the \code{format} string. The default \code{format} used is \code{"\%Y-\%m-\%dT\%H:\%M:\%S\%Ez[\%Z]"}. This matches the default result from calling \code{date_format()} on a POSIXct date-time. Additionally, this format matches the de-facto standard extension to RFC 3339 for creating completely unambiguous date-times. } \subsection{date_time_parse_abbrev()}{ \code{date_time_parse_abbrev()} is a parser for date-time strings containing only a time zone abbreviation, like \code{"2019-01-01 00:00:00 EST"}. The time zone abbreviation is not enough to identify the full time zone name that the date-time belongs to, so the full time zone name must be supplied as the \code{zone} argument. However, the time zone abbreviation can help with resolving ambiguity around daylight saving time fallbacks. For \code{date_time_parse_abbrev()}, \verb{\%Z} must be supplied and is interpreted as the time zone abbreviation rather than the full time zone name. If used, the \verb{\%z} command must parse correctly, but its value will be completely ignored. The default \code{format} used is \code{"\%Y-\%m-\%d \%H:\%M:\%S \%Z"}. This matches the default result from calling \code{print()} or \code{format(usetz = TRUE)} on a POSIXct date-time. } \subsection{date_time_parse_RFC_3339()}{ \code{date_time_parse_RFC_3339()} is a parser for date-time strings in the extremely common date-time format outlined by \href{https://datatracker.ietf.org/doc/html/rfc3339}{RFC 3339}. This document outlines a profile of the ISO 8601 format that is even more restrictive, but corresponds to the most common formats that are likely to be used in internet protocols (i.e. through APIs). In particular, this function is intended to parse the following three formats: \if{html}{\out{
}}\preformatted{2019-01-01T00:00:00Z 2019-01-01T00:00:00+0430 2019-01-01T00:00:00+04:30 }\if{html}{\out{
}} This function defaults to parsing the first of these formats by using a format string of \code{"\%Y-\%m-\%dT\%H:\%M:\%SZ"}. If your date-time strings use offsets from UTC rather than \code{"Z"}, then set \code{offset} to one of the following: \itemize{ \item \code{"\%z"} if the offset is of the form \code{"+0430"}. \item \code{"\%Ez"} if the offset is of the form \code{"+04:30"}. } The RFC 3339 standard allows for replacing the \code{"T"} with a \code{"t"} or a space (\code{" "}). Set \code{separator} to adjust this as needed. The date-times returned by this function will always be in the UTC time zone. } } \details{ If \code{date_time_parse_complete()} is given input that is length zero, all \code{NA}s, or completely fails to parse, then no time zone will be able to be determined. In that case, the result will use \code{"UTC"}. If you have strings with sub-second components, then these date-time parsers are not appropriate for you. Remember that clock treats POSIXct as a second precision type, so parsing a string with fractional seconds directly into a POSIXct is ambiguous and undefined. Instead, fully parse the string, including its fractional seconds, into a clock type that can handle it, such as a naive-time with \code{\link[=naive_time_parse]{naive_time_parse()}}, then round to seconds with whatever rounding convention is appropriate for your use case, such as \code{\link[=time_point_floor]{time_point_floor()}}, and finally convert that to POSIXct with \code{\link[=as_date_time]{as_date_time()}}. This gives you complete control over how the fractional seconds are handled when converting to POSIXct. } \examples{ # Parse with a known `zone`, even though that information isn't in the string date_time_parse("2020-01-01 05:06:07", "America/New_York") # Same time as above, except this is a completely unambiguous parse that # doesn't require a `zone` argument, because the zone name and offset are # both present in the string date_time_parse_complete("2020-01-01T05:06:07-05:00[America/New_York]") # Only day components date_time_parse("2020-01-01", "America/New_York", format = "\%Y-\%m-\%d") # `date_time_parse()` may have issues with ambiguous times due to daylight # saving time fallbacks. For example, there were two 1'oclock hours here: x <- date_time_parse("1970-10-25 00:59:59", "America/New_York") # First (earliest) 1'oclock hour add_seconds(x, 1) # Second (latest) 1'oclock hour add_seconds(x, 3601) # If you try to parse this ambiguous time directly, you'll get an error: ambiguous_time <- "1970-10-25 01:00:00" try(date_time_parse(ambiguous_time, "America/New_York")) # Resolve it by specifying whether you'd like to use the # `earliest` or `latest` of the two possible times date_time_parse(ambiguous_time, "America/New_York", ambiguous = "earliest") date_time_parse(ambiguous_time, "America/New_York", ambiguous = "latest") # `date_time_parse_complete()` doesn't have these issues, as it requires # that the offset and zone name are both in the string, which resolves # the ambiguity complete_times <- c( "1970-10-25T01:00:00-04:00[America/New_York]", "1970-10-25T01:00:00-05:00[America/New_York]" ) date_time_parse_complete(complete_times) # `date_time_parse_abbrev()` also doesn't have these issues, since it # uses the time zone abbreviation name to resolve the ambiguity abbrev_times <- c( "1970-10-25 01:00:00 EDT", "1970-10-25 01:00:00 EST" ) date_time_parse_abbrev(abbrev_times, "America/New_York") # --------------------------------------------------------------------------- # RFC 3339 # Typical UTC format x <- "2019-01-01T00:01:02Z" date_time_parse_RFC_3339(x) # With a UTC offset containing a `:` x <- "2019-01-01T00:01:02+02:30" date_time_parse_RFC_3339(x, offset = "\%Ez") # With a space between the date and time and no `:` in the offset x <- "2019-01-01 00:01:02+0230" date_time_parse_RFC_3339(x, separator = " ", offset = "\%z") # --------------------------------------------------------------------------- # Sub-second components # If you have a string with sub-second components, but only require up to # seconds, first parse them into a clock type that can handle sub-seconds to # fully capture that information, then round using whatever convention is # required for your use case before converting to a date-time. x <- c("2019-01-01T00:00:01.1", "2019-01-01T00:00:01.78") x <- naive_time_parse(x, precision = "millisecond") x time_point_floor(x, "second") time_point_round(x, "second") as_date_time(time_point_round(x, "second"), "America/New_York") } clock/man/calendar-count-between.Rd0000644000176200001440000000445214422221153016736 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/calendar.R \name{calendar-count-between} \alias{calendar-count-between} \alias{calendar_count_between} \title{Counting: calendars} \usage{ calendar_count_between(start, end, precision, ..., n = 1L) } \arguments{ \item{start, end}{\verb{[clock_calendar]} A pair of calendar vectors. These will be recycled to their common size.} \item{precision}{\verb{[character(1)]} A precision. Allowed precisions are dependent on the calendar used.} \item{...}{These dots are for future extensions and must be empty.} \item{n}{\verb{[positive integer(1)]} A single positive integer specifying a multiple of \code{precision} to use.} } \value{ An integer representing the number of \code{precision} units between \code{start} and \code{end}. } \description{ \code{calendar_count_between()} counts the number of \code{precision} units between \code{start} and \code{end} (i.e., the number of years or months). This count corresponds to the \emph{whole number} of units, and will never return a fractional value. This is suitable for, say, computing the whole number of years or months between two calendar dates, accounting for the day and time of day. Each calendar has its own help page describing the precisions that you can count at: \itemize{ \item \link[=year-month-day-count-between]{year-month-day} \item \link[=year-month-weekday-count-between]{year-month-weekday} \item \link[=year-week-day-count-between]{year-week-day} \item \link[=iso-year-week-day-count-between]{iso-year-week-day} \item \link[=year-quarter-day-count-between]{year-quarter-day} \item \link[=year-day-count-between]{year-day} } } \section{Comparison Direction}{ The computed count has the property that if \code{start <= end}, then \verb{start + <= end}. Similarly, if \code{start >= end}, then \verb{start + >= end}. In other words, the comparison direction between \code{start} and \code{end} will never change after adding the count to \code{start}. This makes this function useful for repeated count computations at increasingly fine precisions. } \examples{ # Number of whole years between these dates x <- year_month_day(2000, 01, 05) y <- year_month_day(2005, 01, 04:06) # Note that `2000-01-05 -> 2005-01-04` is only 4 full years calendar_count_between(x, y, "year") } clock/man/as_year_quarter_day.Rd0000644000176200001440000000266514005064257016446 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/quarterly-year-quarter-day.R \name{as_year_quarter_day} \alias{as_year_quarter_day} \title{Convert to year-quarter-day} \usage{ as_year_quarter_day(x, ..., start = NULL) } \arguments{ \item{x}{\verb{[vector]} A vector to convert to year-quarter-day.} \item{...}{These dots are for future extensions and must be empty.} \item{start}{\verb{[integer(1) / NULL]} The month to start the fiscal year in. 1 = January and 12 = December. If \code{NULL}: \itemize{ \item If \code{x} is a year-quarter-day, it will be returned as is. \item Otherwise, a \code{start} of January will be used. }} } \value{ A year-quarter-day vector. } \description{ \code{as_year_quarter_day()} converts a vector to the year-quarter-day calendar. Time points, Dates, POSIXct, and other calendars can all be converted to year-quarter-day. } \examples{ # From Date as_year_quarter_day(as.Date("2019-01-01")) as_year_quarter_day(as.Date("2019-01-01"), start = 3) # From POSIXct, which assumes that the naive time is what should be converted as_year_quarter_day(as.POSIXct("2019-01-01 02:30:30", "America/New_York")) # From other calendars tuesday <- 3 as_year_quarter_day(year_month_weekday(2019, 2, tuesday, 2)) # Converting between `start`s x <- year_quarter_day(2019, 01, 01, start = 2) x # Default keeps the same start as_year_quarter_day(x) # But you can change it as_year_quarter_day(x, start = 1) } clock/man/time_point_cast.Rd0000644000176200001440000000353414017505732015601 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/time-point.R \name{time_point_cast} \alias{time_point_cast} \title{Cast a time point between precisions} \usage{ time_point_cast(x, precision) } \arguments{ \item{x}{\verb{[clock_sys_time / clock_naive_time]} A sys-time or naive-time.} \item{precision}{\verb{[character(1)]} A time point precision. One of: \itemize{ \item \code{"day"} \item \code{"hour"} \item \code{"minute"} \item \code{"second"} \item \code{"millisecond"} \item \code{"microsecond"} \item \code{"nanosecond"} }} } \value{ \code{x} cast to the new \code{precision}. } \description{ Casting is one way to change a time point's precision. Casting to a less precise precision will completely drop information that is more precise than the precision that you are casting to. It does so in a way that makes it round towards zero. When converting time points to a less precise precision, you often want \code{\link[=time_point_floor]{time_point_floor()}} instead of \code{time_point_cast()}, as that handles pre-1970 dates (which are stored as negative durations) in a more intuitive manner. Casting to a more precise precision is done through a multiplication by a conversion factor between the current precision and the new precision. } \examples{ # Hour precision time points # One is pre-1970, one is post-1970 x <- duration_hours(c(25, -25)) x <- as_naive_time(x) x # Casting rounds the underlying duration towards 0 cast <- time_point_cast(x, "day") cast # Flooring rounds the underlying duration towards negative infinity, # which is often more intuitive for time points. # Note that the cast ends up rounding the pre-1970 date up to the next # day, while the post-1970 date is rounded down. floor <- time_point_floor(x, "day") floor # Casting to a more precise precision, hour->millisecond time_point_cast(x, "millisecond") } clock/man/year-day-group.Rd0000644000176200001440000000235514017505732015265 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gregorian-year-day.R \name{year-day-group} \alias{year-day-group} \alias{calendar_group.clock_year_day} \title{Grouping: year-day} \usage{ \method{calendar_group}{clock_year_day}(x, precision, ..., n = 1L) } \arguments{ \item{x}{\verb{[clock_year_day]} A year-day vector.} \item{precision}{\verb{[character(1)]} One of: \itemize{ \item \code{"year"} \item \code{"day"} \item \code{"hour"} \item \code{"minute"} \item \code{"second"} \item \code{"millisecond"} \item \code{"microsecond"} \item \code{"nanosecond"} }} \item{...}{These dots are for future extensions and must be empty.} \item{n}{\verb{[positive integer(1)]} A single positive integer specifying a multiple of \code{precision} to use.} } \value{ \code{x} grouped at the specified \code{precision}. } \description{ This is a year-day method for the \code{\link[=calendar_group]{calendar_group()}} generic. Grouping for a year-day object can be done at any precision, as long as \code{x} is at least as precise as \code{precision}. } \examples{ x <- seq(as_naive_time(year_month_day(2019, 1, 1)), by = 5, length.out = 20) x <- as_year_day(x) x # Group by day of the current year calendar_group(x, "day", n = 20) } clock/man/date-today.Rd0000644000176200001440000000220314073622034014440 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/date.R, R/posixt.R \name{date-today} \alias{date-today} \alias{date_today} \alias{date_now} \title{Current date and date-time} \usage{ date_today(zone) date_now(zone) } \arguments{ \item{zone}{\verb{[character(1)]} A time zone to get the current time for.} } \value{ \itemize{ \item \code{date_today()} a single Date. \item \code{date_now()} a single POSIXct. } } \description{ \itemize{ \item \code{date_today()} returns the current date in the specified \code{zone} as a Date. \item \code{date_now()} returns the current date-time in the specified \code{zone} as a POSIXct. } } \details{ clock assumes that Date is a \emph{naive} type, like naive-time. This means that \code{date_today()} first looks up the current date-time in the specified \code{zone}, then converts that to a Date, retaining the printed time while dropping any information about that time zone. } \examples{ # Current date in the local time zone date_today("") # Current date in a specified time zone date_today("Europe/London") # Current date-time in that same time zone date_now("Europe/London") } clock/man/time-point-rounding.Rd0000644000176200001440000001063014426711663016331 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/time-point.R \name{time-point-rounding} \alias{time-point-rounding} \alias{time_point_floor} \alias{time_point_ceiling} \alias{time_point_round} \title{Time point rounding} \usage{ time_point_floor(x, precision, ..., n = 1L, origin = NULL) time_point_ceiling(x, precision, ..., n = 1L, origin = NULL) time_point_round(x, precision, ..., n = 1L, origin = NULL) } \arguments{ \item{x}{\verb{[clock_sys_time / clock_naive_time]} A sys-time or naive-time.} \item{precision}{\verb{[character(1)]} A time point precision. One of: \itemize{ \item \code{"day"} \item \code{"hour"} \item \code{"minute"} \item \code{"second"} \item \code{"millisecond"} \item \code{"microsecond"} \item \code{"nanosecond"} }} \item{...}{These dots are for future extensions and must be empty.} \item{n}{\verb{[positive integer(1)]} A positive integer specifying the multiple of \code{precision} to use.} \item{origin}{\verb{[clock_sys_time(1) / clock_naive_time(1) / NULL]} An origin to begin counting from. Mostly useful when \code{n > 1} and you want to control how the rounding groups are created. If \code{x} is a sys-time, \code{origin} must be a sys-time. If \code{x} is a naive-time, \code{origin} must be a naive-time. The precision of \code{origin} must be equally precise as or less precise than \code{precision}. If \code{NULL}, a default origin of midnight on 1970-01-01 is used.} } \value{ \code{x} rounded to the new \code{precision}. } \description{ \itemize{ \item \code{time_point_floor()} rounds a sys-time or naive-time down to a multiple of the specified \code{precision}. \item \code{time_point_ceiling()} rounds a sys-time or naive-time up to a multiple of the specified \code{precision}. \item \code{time_point_round()} rounds up or down depending on what is closer, rounding up on ties. } Rounding time points is mainly useful for rounding sub-daily time points up to daily time points. It can also be useful for flooring by a set number of days (like 20) with respect to some origin. By default, the origin is 1970-01-01 00:00:00. If you want to group by components, such as "day of the month", rather than by "n days", see \code{\link[=calendar_group]{calendar_group()}}. } \section{Boundary Handling}{ To understand how flooring and ceiling work, you need to know how they create their intervals for rounding. \itemize{ \item \code{time_point_floor()} constructs intervals of \code{[lower, upper)} that bound each element of \code{x}, then always chooses the \emph{left-hand side}. \item \code{time_point_ceiling()} constructs intervals of \code{(lower, upper]} that bound each element of \code{x}, then always chooses the \emph{right-hand side}. } As an easy example, consider 2020-01-02 00:00:05. To floor this to the nearest day, the following interval is constructed, and the left-hand side is returned at day precision: \code{[2020-01-02 00:00:00, 2020-01-03 00:00:00)} To ceiling this to the nearest day, the following interval is constructed, and the right-hand side is returned at day precision: \code{(2020-01-02 00:00:00, 2020-01-03 00:00:00]} Here is another example, this time with a time point on a boundary, 2020-01-02 00:00:00. To floor this to the nearest day, the following interval is constructed, and the left-hand side is returned at day precision: \code{[2020-01-02 00:00:00, 2020-01-03 00:00:00)} To ceiling this to the nearest day, the following interval is constructed, and the right-hand side is returned at day precision: \code{(2020-01-01 00:00:00, 2020-01-02 00:00:00]} Notice that, regardless of whether you are doing a floor or ceiling, if the input falls on a boundary then it will be returned as is. } \examples{ library(magrittr) x <- as_naive_time(year_month_day(2019, 01, 01)) x <- add_days(x, 0:40) head(x) # Floor by sets of 20 days # The implicit origin to start the 20 day counter is 1970-01-01 time_point_floor(x, "day", n = 20) # You can easily customize the origin by supplying a new one # as the `origin` argument origin <- year_month_day(2019, 01, 01) \%>\% as_naive_time() time_point_floor(x, "day", n = 20, origin = origin) # For times on the boundary, floor and ceiling both return the input # at the new precision. Notice how the first element is on the boundary, # and the second is 1 second after the boundary. y <- as_naive_time(year_month_day(2020, 01, 02, 00, 00, c(00, 01))) time_point_floor(y, "day") time_point_ceiling(y, "day") } clock/man/seq.clock_time_point.Rd0000644000176200001440000000461514162373152016532 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/time-point.R \name{seq.clock_time_point} \alias{seq.clock_time_point} \title{Sequences: time points} \usage{ \method{seq}{clock_time_point}(from, to = NULL, by = NULL, length.out = NULL, along.with = NULL, ...) } \arguments{ \item{from}{\verb{[clock_sys_time(1) / clock_naive_time(1)]} A time point to start the sequence from. \code{from} is always included in the result.} \item{to}{\verb{[clock_sys_time(1) / clock_naive_time(1) / NULL]} A time point to stop the sequence at. \code{to} is cast to the type of \code{from}. \code{to} is only included in the result if the resulting sequence divides the distance between \code{from} and \code{to} exactly.} \item{by}{\verb{[integer(1) / clock_duration(1) / NULL]} The unit to increment the sequence by. If \code{by} is an integer, it is transformed into a duration with the precision of \code{from}. If \code{by} is a duration, it is cast to the type of \code{from}.} \item{length.out}{\verb{[positive integer(1) / NULL]} The length of the resulting sequence. If specified, \code{along.with} must be \code{NULL}.} \item{along.with}{\verb{[vector / NULL]} A vector who's length determines the length of the resulting sequence. Equivalent to \code{length.out = vec_size(along.with)}. If specified, \code{length.out} must be \code{NULL}.} \item{...}{These dots are for future extensions and must be empty.} } \value{ A sequence with the type of \code{from}. } \description{ This is a time point method for the \code{\link[=seq]{seq()}} generic. It works for sys-time and naive-time vectors. Sequences can be generated for all valid time point precisions (daily through nanosecond). When calling \code{seq()}, exactly two of the following must be specified: \itemize{ \item \code{to} \item \code{by} \item Either \code{length.out} or \code{along.with} } } \examples{ # Daily sequence seq( as_naive_time(year_month_day(2019, 1, 1)), as_naive_time(year_month_day(2019, 2, 4)), by = 5 ) # Minutely sequence using minute precision naive-time x <- as_naive_time(year_month_day(2019, 1, 2, 3, 3)) x seq(x, by = 4, length.out = 10) # You can use larger step sizes by using a duration-based `by` seq(x, by = duration_days(1), length.out = 5) # Nanosecond sequence from <- as_naive_time(year_month_day(2019, 1, 1)) from <- time_point_cast(from, "nanosecond") to <- from + 100 seq(from, to, by = 10) } clock/man/year-quarter-day-boundary.Rd0000644000176200001440000000277314075602251017437 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/quarterly-year-quarter-day.R \name{year-quarter-day-boundary} \alias{year-quarter-day-boundary} \alias{calendar_start.clock_year_quarter_day} \alias{calendar_end.clock_year_quarter_day} \title{Boundaries: year-quarter-day} \usage{ \method{calendar_start}{clock_year_quarter_day}(x, precision) \method{calendar_end}{clock_year_quarter_day}(x, precision) } \arguments{ \item{x}{\verb{[clock_year_quarter_day]} A year-quarter-day vector.} \item{precision}{\verb{[character(1)]} One of: \itemize{ \item \code{"year"} \item \code{"quarter"} \item \code{"day"} \item \code{"hour"} \item \code{"minute"} \item \code{"second"} \item \code{"millisecond"} \item \code{"microsecond"} \item \code{"nanosecond"} }} } \value{ \code{x} at the same precision, but with some components altered to be at the boundary value. } \description{ This is a year-quarter-day method for the \code{\link[=calendar_start]{calendar_start()}} and \code{\link[=calendar_end]{calendar_end()}} generics. They adjust components of a calendar to the start or end of a specified \code{precision}. } \examples{ x <- year_quarter_day(2019:2020, 2:3, 5, 6, 7, 8, start = clock_months$march) x # Compute the last moment of the fiscal quarter calendar_end(x, "quarter") # Compare that to just setting the day to `"last"`, # which doesn't affect the other components set_day(x, "last") # Compute the start of the fiscal year calendar_start(x, "year") as_date(calendar_start(x, "year")) } clock/man/posixt-boundary.Rd0000644000176200001440000001243714075602251015567 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/posixt.R \name{posixt-boundary} \alias{posixt-boundary} \alias{date_start.POSIXt} \alias{date_end.POSIXt} \title{Boundaries: date-time} \usage{ \method{date_start}{POSIXt}( x, precision, ..., invalid = NULL, nonexistent = NULL, ambiguous = x ) \method{date_end}{POSIXt}(x, precision, ..., invalid = NULL, nonexistent = NULL, ambiguous = x) } \arguments{ \item{x}{\verb{[POSIXct / POSIXlt]} A date-time vector.} \item{precision}{\verb{[character(1)]} One of: \itemize{ \item \code{"year"} \item \code{"month"} \item \code{"day"} \item \code{"hour"} \item \code{"minute"} \item \code{"second"} }} \item{...}{These dots are for future extensions and must be empty.} \item{invalid}{\verb{[character(1) / NULL]} One of the following invalid date resolution strategies: \itemize{ \item \code{"previous"}: The previous valid instant in time. \item \code{"previous-day"}: The previous valid day in time, keeping the time of day. \item \code{"next"}: The next valid instant in time. \item \code{"next-day"}: The next valid day in time, keeping the time of day. \item \code{"overflow"}: Overflow by the number of days that the input is invalid by. Time of day is dropped. \item \code{"overflow-day"}: Overflow by the number of days that the input is invalid by. Time of day is kept. \item \code{"NA"}: Replace invalid dates with \code{NA}. \item \code{"error"}: Error on invalid dates. } Using either \code{"previous"} or \code{"next"} is generally recommended, as these two strategies maintain the \emph{relative ordering} between elements of the input. If \code{NULL}, defaults to \code{"error"}. If \code{getOption("clock.strict")} is \code{TRUE}, \code{invalid} must be supplied and cannot be \code{NULL}. This is a convenient way to make production code robust to invalid dates.} \item{nonexistent}{\verb{[character / NULL]} One of the following nonexistent time resolution strategies, allowed to be either length 1, or the same length as the input: \itemize{ \item \code{"roll-forward"}: The next valid instant in time. \item \code{"roll-backward"}: The previous valid instant in time. \item \code{"shift-forward"}: Shift the nonexistent time forward by the size of the daylight saving time gap. \item \verb{"shift-backward}: Shift the nonexistent time backward by the size of the daylight saving time gap. \item \code{"NA"}: Replace nonexistent times with \code{NA}. \item \code{"error"}: Error on nonexistent times. } Using either \code{"roll-forward"} or \code{"roll-backward"} is generally recommended over shifting, as these two strategies maintain the \emph{relative ordering} between elements of the input. If \code{NULL}, defaults to \code{"error"}. If \code{getOption("clock.strict")} is \code{TRUE}, \code{nonexistent} must be supplied and cannot be \code{NULL}. This is a convenient way to make production code robust to nonexistent times.} \item{ambiguous}{\verb{[character / zoned_time / POSIXct / list(2) / NULL]} One of the following ambiguous time resolution strategies, allowed to be either length 1, or the same length as the input: \itemize{ \item \code{"earliest"}: Of the two possible times, choose the earliest one. \item \code{"latest"}: Of the two possible times, choose the latest one. \item \code{"NA"}: Replace ambiguous times with \code{NA}. \item \code{"error"}: Error on ambiguous times. } Alternatively, \code{ambiguous} is allowed to be a zoned_time (or POSIXct) that is either length 1, or the same length as the input. If an ambiguous time is encountered, the zoned_time is consulted. If the zoned_time corresponds to a naive_time that is also ambiguous \emph{and} uses the same daylight saving time transition point as the original ambiguous time, then the offset of the zoned_time is used to resolve the ambiguity. If the ambiguity cannot be resolved by consulting the zoned_time, then this method falls back to \code{NULL}. Finally, \code{ambiguous} is allowed to be a list of size 2, where the first element of the list is a zoned_time (as described above), and the second element of the list is an ambiguous time resolution strategy to use when the ambiguous time cannot be resolved by consulting the zoned_time. Specifying a zoned_time on its own is identical to \verb{list(, NULL)}. If \code{NULL}, defaults to \code{"error"}. If \code{getOption("clock.strict")} is \code{TRUE}, \code{ambiguous} must be supplied and cannot be \code{NULL}. Additionally, \code{ambiguous} cannot be specified as a zoned_time on its own, as this implies \code{NULL} for ambiguous times that the zoned_time cannot resolve. Instead, it must be specified as a list alongside an ambiguous time resolution strategy as described above. This is a convenient way to make production code robust to ambiguous times.} } \value{ \code{x} but with some components altered to be at the boundary value. } \description{ This is a POSIXct/POSIXlt method for the \code{\link[=date_start]{date_start()}} and \code{\link[=date_end]{date_end()}} generics. } \examples{ x <- date_time_build(2019:2021, 2:4, 3:5, 4, 5, 6, zone = "America/New_York") x # Last moment of the month date_end(x, "month") # Notice that this is different from just setting the day to `"last"` set_day(x, "last") # Last moment of the year date_end(x, "year") # First moment of the hour date_start(x, "hour") } clock/man/year-quarter-day-narrow.Rd0000644000176200001440000000203414006303002017074 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/quarterly-year-quarter-day.R \name{year-quarter-day-narrow} \alias{year-quarter-day-narrow} \alias{calendar_narrow.clock_year_quarter_day} \title{Narrow: year-quarter-day} \usage{ \method{calendar_narrow}{clock_year_quarter_day}(x, precision) } \arguments{ \item{x}{\verb{[clock_year_quarter_day]} A year-quarter-day vector.} \item{precision}{\verb{[character(1)]} One of: \itemize{ \item \code{"year"} \item \code{"quarter"} \item \code{"day"} \item \code{"hour"} \item \code{"minute"} \item \code{"second"} \item \code{"millisecond"} \item \code{"microsecond"} \item \code{"nanosecond"} }} } \value{ \code{x} narrowed to the supplied \code{precision}. } \description{ This is a year-quarter-day method for the \code{\link[=calendar_narrow]{calendar_narrow()}} generic. It narrows a year-quarter-day vector to the specified \code{precision}. } \examples{ # Day precision x <- year_quarter_day(2019, 1, 5) x # Narrow to quarter precision calendar_narrow(x, "quarter") } clock/man/date_parse.Rd0000644000176200001440000002631314133632415014525 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/date.R \name{date_parse} \alias{date_parse} \title{Parsing: date} \usage{ date_parse(x, ..., format = NULL, locale = clock_locale()) } \arguments{ \item{x}{\verb{[character]} A character vector to parse.} \item{...}{These dots are for future extensions and must be empty.} \item{format}{\verb{[character / NULL]} A format string. A combination of the following commands, or \code{NULL}, in which case a default format string is used. A vector of multiple format strings can be supplied. They will be tried in the order they are provided. \strong{Year} \itemize{ \item \verb{\%C}: The century as a decimal number. The modified command \verb{\%NC} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. \item \verb{\%y}: The last two decimal digits of the year. If the century is not otherwise specified (e.g. with \verb{\%C}), values in the range \verb{[69 - 99]} are presumed to refer to the years \verb{[1969 - 1999]}, and values in the range \verb{[00 - 68]} are presumed to refer to the years \verb{[2000 - 2068]}. The modified command \verb{\%Ny}, where \code{N} is a positive decimal integer, specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. \item \verb{\%Y}: The year as a decimal number. The modified command \verb{\%NY} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{4}. Leading zeroes are permitted but not required. } \strong{Month} \itemize{ \item \verb{\%b}, \verb{\%B}, \verb{\%h}: The \code{locale}'s full or abbreviated case-insensitive month name. \item \verb{\%m}: The month as a decimal number. January is \code{1}. The modified command \verb{\%Nm} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. } \strong{Day} \itemize{ \item \verb{\%d}, \verb{\%e}: The day of the month as a decimal number. The modified command \verb{\%Nd} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. } \strong{Day of the week} \itemize{ \item \verb{\%a}, \verb{\%A}: The \code{locale}'s full or abbreviated case-insensitive weekday name. \item \verb{\%w}: The weekday as a decimal number (\code{0-6}), where Sunday is \code{0}. The modified command \verb{\%Nw} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{1}. Leading zeroes are permitted but not required. } \strong{ISO 8601 week-based year} \itemize{ \item \verb{\%g}: The last two decimal digits of the ISO week-based year. The modified command \verb{\%Ng} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. \item \verb{\%G}: The ISO week-based year as a decimal number. The modified command \verb{\%NG} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{4}. Leading zeroes are permitted but not required. \item \verb{\%V}: The ISO week-based week number as a decimal number. The modified command \verb{\%NV} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. \item \verb{\%u}: The ISO weekday as a decimal number (\code{1-7}), where Monday is \code{1}. The modified command \verb{\%Nu} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{1}. Leading zeroes are permitted but not required. } \strong{Week of the year} \itemize{ \item \verb{\%U}: The week number of the year as a decimal number. The first Sunday of the year is the first day of week \code{01}. Days of the same year prior to that are in week \code{00}. The modified command \verb{\%NU} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. \item \verb{\%W}: The week number of the year as a decimal number. The first Monday of the year is the first day of week \code{01}. Days of the same year prior to that are in week \code{00}. The modified command \verb{\%NW} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. } \strong{Day of the year} \itemize{ \item \verb{\%j}: The day of the year as a decimal number. January 1 is \code{1}. The modified command \verb{\%Nj} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{3}. Leading zeroes are permitted but not required. } \strong{Date} \itemize{ \item \verb{\%D}, \verb{\%x}: Equivalent to \verb{\%m/\%d/\%y}. \item \verb{\%F}: Equivalent to \verb{\%Y-\%m-\%d}. If modified with a width (like \verb{\%NF}), the width is applied to only \verb{\%Y}. } \strong{Time of day} \itemize{ \item \verb{\%H}: The hour (24-hour clock) as a decimal number. The modified command \verb{\%NH} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. \item \verb{\%I}: The hour (12-hour clock) as a decimal number. The modified command \verb{\%NI} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. \item \verb{\%M}: The minutes as a decimal number. The modified command \verb{\%NM} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. \item \verb{\%S}: The seconds as a decimal number. Leading zeroes are permitted but not required. If encountered, the \code{locale} determines the decimal point character. Generally, the maximum number of characters to read is determined by the precision that you are parsing at. For example, a precision of \code{"second"} would read a maximum of 2 characters, while a precision of \code{"millisecond"} would read a maximum of 6 (2 for the values before the decimal point, 1 for the decimal point, and 3 for the values after it). The modified command \verb{\%NS}, where \code{N} is a positive decimal integer, can be used to exactly specify the maximum number of characters to read. This is only useful if you happen to have seconds with more than 1 leading zero. \item \verb{\%p}: The \code{locale}'s equivalent of the AM/PM designations associated with a 12-hour clock. The command \verb{\%I} must precede \verb{\%p} in the format string. \item \verb{\%R}: Equivalent to \verb{\%H:\%M}. \item \verb{\%T}, \verb{\%X}: Equivalent to \verb{\%H:\%M:\%S}. \item \verb{\%r}: Equivalent to \verb{\%I:\%M:\%S \%p}. } \strong{Time zone} \itemize{ \item \verb{\%z}: The offset from UTC in the format \verb{[+|-]hh[mm]}. For example \code{-0430} refers to 4 hours 30 minutes behind UTC. And \code{04} refers to 4 hours ahead of UTC. The modified command \verb{\%Ez} parses a \code{:} between the hours and minutes and leading zeroes on the hour field are optional: \verb{[+|-]h[h][:mm]}. For example \code{-04:30} refers to 4 hours 30 minutes behind UTC. And \code{4} refers to 4 hours ahead of UTC. \item \verb{\%Z}: The full time zone name or the time zone abbreviation, depending on the function being used. A single word is parsed. This word can only contain characters that are alphanumeric, or one of \code{'_'}, \code{'/'}, \code{'-'} or \code{'+'}. } \strong{Miscellaneous} \itemize{ \item \verb{\%c}: A date and time representation. Equivalent to \verb{\%a \%b \%d \%H:\%M:\%S \%Y}. \item \code{\%\%}: A \verb{\%} character. \item \verb{\%n}: Matches one white space character. \verb{\%n}, \verb{\%t}, and a space can be combined to match a wide range of white-space patterns. For example \code{"\%n "} matches one or more white space characters, and \code{"\%n\%t\%t"} matches one to three white space characters. \item \verb{\%t}: Matches zero or one white space characters. }} \item{locale}{\verb{[clock_locale]} A locale object created from \code{\link[=clock_locale]{clock_locale()}}.} } \value{ A Date. } \description{ \code{date_parse()} parses strings into a Date. The default \code{format} used is \code{"\%Y-\%m-\%d"}. This matches the default result from calling \code{print()} or \code{format()} on a Date. } \details{ \emph{\code{date_parse()} ignores both the \verb{\%z} and \verb{\%Z} commands,} as clock treats Date as a \emph{naive} type, with a yet-to-be-specified time zone. Parsing strings with sub-daily components, such as hours, minutes, or seconds, should generally be done with \code{\link[=date_time_parse]{date_time_parse()}}. If you only need the date components from a string with sub-daily components, choose one of the following: \itemize{ \item If the date components are at the front of the string, and you don't want the time components to affect the date in any way, you can use \code{\link[=date_parse]{date_parse()}} to parse only the date components. For example, \code{date_parse("2019-01-05 00:01:02", format = "\%Y-\%m-\%d")} will parse through \code{05} and then stop. \item If you want the time components to influence the date, then parse the full string with \code{\link[=date_time_parse]{date_time_parse()}}, round to day precision with a rounding function like \code{\link[=date_round]{date_round()}}, and cast to date with \code{\link[=as_date]{as_date()}}. } Attempting to directly parse all components of a sub-daily string into a Date is ambiguous and undefined, and is unlikely to work as you might expect. For example, \code{date_parse("2019-01-05 00:01:02", format = "\%Y-\%m-\%d \%H:\%M:\%S")} is not officially supported, even if it works in some cases. } \examples{ date_parse("2020-01-01") date_parse( "January 5, 2020", format = "\%B \%d, \%Y" ) # With a different locale date_parse( "janvier 5, 2020", format = "\%B \%d, \%Y", locale = clock_locale("fr") ) # A neat feature of `date_parse()` is the ability to parse # the ISO year-week-day format date_parse("2020-W01-2", format = "\%G-W\%V-\%u") # --------------------------------------------------------------------------- # Sub-daily components # If you have a string with sub-daily components, but only require the date, # first parse them as date-times to fully parse the sub-daily components, # then round using whatever convention is required for your use case before # converting to date. x <- c("2019-01-01 11", "2019-01-01 12") x <- date_time_parse(x, zone = "UTC", format = "\%Y-\%m-\%d \%H") x date_floor(x, "day") date_round(x, "day") as_date(date_round(x, "day")) } clock/man/year-month-day-getters.Rd0000644000176200001440000000330314007013416016713 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gregorian-year-month-day.R \name{year-month-day-getters} \alias{year-month-day-getters} \alias{get_year.clock_year_month_day} \alias{get_month.clock_year_month_day} \alias{get_day.clock_year_month_day} \alias{get_hour.clock_year_month_day} \alias{get_minute.clock_year_month_day} \alias{get_second.clock_year_month_day} \alias{get_millisecond.clock_year_month_day} \alias{get_microsecond.clock_year_month_day} \alias{get_nanosecond.clock_year_month_day} \title{Getters: year-month-day} \usage{ \method{get_year}{clock_year_month_day}(x) \method{get_month}{clock_year_month_day}(x) \method{get_day}{clock_year_month_day}(x) \method{get_hour}{clock_year_month_day}(x) \method{get_minute}{clock_year_month_day}(x) \method{get_second}{clock_year_month_day}(x) \method{get_millisecond}{clock_year_month_day}(x) \method{get_microsecond}{clock_year_month_day}(x) \method{get_nanosecond}{clock_year_month_day}(x) } \arguments{ \item{x}{\verb{[clock_year_month_day]} A year-month-day to get the component from.} } \value{ The component. } \description{ These are year-month-day methods for the \link[=clock-getters]{getter generics}. \itemize{ \item \code{get_year()} returns the Gregorian year. \item \code{get_month()} returns the month of the year. \item \code{get_day()} returns the day of the month. \item There are sub-daily getters for extracting more precise components. } } \examples{ x <- year_month_day(2019, 1:3, 5:7, 1, 20, 30) get_month(x) get_day(x) get_second(x) # Cannot extract more precise components y <- year_month_day(2019, 1) try(get_day(y)) # Cannot extract components that don't exist for this calendar try(get_quarter(x)) } clock/man/is_year_month_day.Rd0000644000176200001440000000077014002162771016110 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gregorian-year-month-day.R \name{is_year_month_day} \alias{is_year_month_day} \title{Is \code{x} a year-month-day?} \usage{ is_year_month_day(x) } \arguments{ \item{x}{\verb{[object]} An object.} } \value{ Returns \code{TRUE} if \code{x} inherits from \code{"clock_year_month_day"}, otherwise returns \code{FALSE}. } \description{ Check if \code{x} is a year-month-day. } \examples{ is_year_month_day(year_month_day(2019)) } clock/man/as-zoned-time-naive-time.Rd0000644000176200001440000002475714017505732017142 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/naive-time.R \name{as-zoned-time-naive-time} \alias{as-zoned-time-naive-time} \alias{as_zoned_time.clock_naive_time} \title{Convert to a zoned-time from a naive-time} \usage{ \method{as_zoned_time}{clock_naive_time}(x, zone, ..., nonexistent = NULL, ambiguous = NULL) } \arguments{ \item{x}{\verb{[clock_naive_time]} A naive-time to convert to a zoned-time.} \item{zone}{\verb{[character(1)]} The zone to convert to.} \item{...}{These dots are for future extensions and must be empty.} \item{nonexistent}{\verb{[character / NULL]} One of the following nonexistent time resolution strategies, allowed to be either length 1, or the same length as the input: \itemize{ \item \code{"roll-forward"}: The next valid instant in time. \item \code{"roll-backward"}: The previous valid instant in time. \item \code{"shift-forward"}: Shift the nonexistent time forward by the size of the daylight saving time gap. \item \verb{"shift-backward}: Shift the nonexistent time backward by the size of the daylight saving time gap. \item \code{"NA"}: Replace nonexistent times with \code{NA}. \item \code{"error"}: Error on nonexistent times. } Using either \code{"roll-forward"} or \code{"roll-backward"} is generally recommended over shifting, as these two strategies maintain the \emph{relative ordering} between elements of the input. If \code{NULL}, defaults to \code{"error"}. If \code{getOption("clock.strict")} is \code{TRUE}, \code{nonexistent} must be supplied and cannot be \code{NULL}. This is a convenient way to make production code robust to nonexistent times.} \item{ambiguous}{\verb{[character / zoned_time / POSIXct / list(2) / NULL]} One of the following ambiguous time resolution strategies, allowed to be either length 1, or the same length as the input: \itemize{ \item \code{"earliest"}: Of the two possible times, choose the earliest one. \item \code{"latest"}: Of the two possible times, choose the latest one. \item \code{"NA"}: Replace ambiguous times with \code{NA}. \item \code{"error"}: Error on ambiguous times. } Alternatively, \code{ambiguous} is allowed to be a zoned_time (or POSIXct) that is either length 1, or the same length as the input. If an ambiguous time is encountered, the zoned_time is consulted. If the zoned_time corresponds to a naive_time that is also ambiguous \emph{and} uses the same daylight saving time transition point as the original ambiguous time, then the offset of the zoned_time is used to resolve the ambiguity. If the ambiguity cannot be resolved by consulting the zoned_time, then this method falls back to \code{NULL}. Finally, \code{ambiguous} is allowed to be a list of size 2, where the first element of the list is a zoned_time (as described above), and the second element of the list is an ambiguous time resolution strategy to use when the ambiguous time cannot be resolved by consulting the zoned_time. Specifying a zoned_time on its own is identical to \verb{list(, NULL)}. If \code{NULL}, defaults to \code{"error"}. If \code{getOption("clock.strict")} is \code{TRUE}, \code{ambiguous} must be supplied and cannot be \code{NULL}. Additionally, \code{ambiguous} cannot be specified as a zoned_time on its own, as this implies \code{NULL} for ambiguous times that the zoned_time cannot resolve. Instead, it must be specified as a list alongside an ambiguous time resolution strategy as described above. This is a convenient way to make production code robust to ambiguous times.} } \value{ A zoned-time vector. } \description{ This is a naive-time method for the \code{\link[=as_zoned_time]{as_zoned_time()}} generic. Converting to a zoned-time from a naive-time retains the printed time, but changes the underlying duration, depending on the \code{zone} that you choose. Naive-times are time points with a yet-to-be-determined time zone. By converting them to a zoned-time, all you are doing is specifying that time zone while attempting to keep all other printed information the same (if possible). If you want to retain the underlying duration, try converting to a zoned-time \link[=as-zoned-time-sys-time]{from a sys-time}, which is a time point interpreted as having a UTC time zone. } \section{Daylight Saving Time}{ Converting from a naive-time to a zoned-time is not always possible due to daylight saving time issues. There are two types of these issues: \emph{Nonexistent} times are the result of daylight saving time "gaps". For example, in the America/New_York time zone, there was a daylight saving time gap 1 second after \code{"2020-03-08 01:59:59"}, where the clocks changed from \code{01:59:59 -> 03:00:00}, completely skipping the 2 o'clock hour. This means that if you had a naive time of \code{"2020-03-08 02:30:00"}, you couldn't convert that straight into a zoned-time with this time zone. To resolve these issues, the \code{nonexistent} argument can be used to specify one of many nonexistent time resolution strategies. \emph{Ambiguous} times are the result of daylight saving time "fallbacks". For example, in the America/New_York time zone, there was a daylight saving time fallback 1 second after \code{"2020-11-01 01:59:59 EDT"}, at which point the clocks "fell backwards" by 1 hour, resulting in a printed time of \code{"2020-11-01 01:00:00 EST"} (note the EDT->EST shift). This resulted in two 1 o'clock hours for this day, so if you had a naive time of \code{"2020-11-01 01:30:00"}, you wouldn't be able to convert that directly into a zoned-time with this time zone, as there is no way for clock to know which of the two ambiguous times you wanted. To resolve these issues, the \code{ambiguous} argument can be used to specify one of many ambiguous time resolution strategies. } \examples{ library(magrittr) x <- as_naive_time(year_month_day(2019, 1, 1)) # Converting a naive-time to a zoned-time generally retains the # printed time, while changing the underlying duration. as_zoned_time(x, "America/New_York") as_zoned_time(x, "America/Los_Angeles") # --------------------------------------------------------------------------- # Nonexistent time: new_york <- "America/New_York" # There was a daylight saving gap in the America/New_York time zone on # 2020-03-08 01:59:59 -> 03:00:00, which means that one of these # naive-times don't exist in that time zone. By default, attempting to # convert it to a zoned time will result in an error. nonexistent_time <- year_month_day(2020, 03, 08, c(02, 03), c(45, 30), 00) nonexistent_time <- as_naive_time(nonexistent_time) try(as_zoned_time(nonexistent_time, new_york)) # Resolve this by specifying a nonexistent time resolution strategy as_zoned_time(nonexistent_time, new_york, nonexistent = "roll-forward") as_zoned_time(nonexistent_time, new_york, nonexistent = "roll-backward") # Note that rolling backwards will choose the last possible moment in # time at the current precision of the input nonexistent_nanotime <- time_point_cast(nonexistent_time, "nanosecond") nonexistent_nanotime as_zoned_time(nonexistent_nanotime, new_york, nonexistent = "roll-backward") # A word of caution - Shifting does not guarantee that the relative ordering # of the input is maintained shifted <- as_zoned_time( nonexistent_time, new_york, nonexistent = "shift-forward" ) shifted # 02:45:00 < 03:30:00 nonexistent_time[1] < nonexistent_time[2] # 03:45:00 > 03:30:00 (relative ordering is lost) shifted[1] < shifted[2] # --------------------------------------------------------------------------- # Ambiguous time: new_york <- "America/New_York" # There was a daylight saving time fallback in the America/New_York time # zone on 2020-11-01 01:59:59 EDT -> 2020-11-01 01:00:00 EST, resulting # in two 1 o'clock hours. This means that the following naive time is # ambiguous since we don't know which of the two 1 o'clocks it belongs to. # By default, attempting to convert it to a zoned time will result in an # error. ambiguous_time <- year_month_day(2020, 11, 01, 01, 30, 00) ambiguous_time <- as_naive_time(ambiguous_time) try(as_zoned_time(ambiguous_time, new_york)) # Resolve this by specifying an ambiguous time resolution strategy earliest <- as_zoned_time(ambiguous_time, new_york, ambiguous = "earliest") latest <- as_zoned_time(ambiguous_time, new_york, ambiguous = "latest") na <- as_zoned_time(ambiguous_time, new_york, ambiguous = "NA") earliest latest na # Now assume that you were given the following zoned-times, i.e., # you didn't build them from scratch so you already know their otherwise # ambiguous offsets x <- c(earliest, latest) x # To set the seconds to 5 in both, you might try: x_naive <- x \%>\% as_naive_time() \%>\% as_year_month_day() \%>\% set_second(5) \%>\% as_naive_time() x_naive # But this fails because you've "lost" the information about which # offsets these ambiguous times started in try(as_zoned_time(x_naive, zoned_time_zone(x))) # To get around this, you can use that information by specifying # `ambiguous = x`, which will use the offset from `x` to resolve the # ambiguity in `x_naive` as long as `x` is also an ambiguous time with the # same daylight saving time transition point as `x_naive` (i.e. here # everything has a transition point of `"2020-11-01 01:00:00 EST"`). as_zoned_time(x_naive, zoned_time_zone(x), ambiguous = x) # Say you added one more time to `x` that would not be considered ambiguous # in naive-time x <- c(x, as_zoned_time(as_sys_time(latest) + 3600, zoned_time_zone(latest))) x # Imagine you want to floor this vector to a multiple of 2 hours, with # an origin of 1am that day. You can do this by subtracting the origin, # flooring, then adding it back origin <- year_month_day(2019, 11, 01, 01, 00, 00) \%>\% as_naive_time() \%>\% as_duration() x_naive <- x \%>\% as_naive_time() \%>\% add_seconds(-origin) \%>\% time_point_floor("hour", n = 2) \%>\% add_seconds(origin) x_naive # You again have ambiguous naive-time points, so you might try using # `ambiguous = x`. It looks like this took care of the first two problems, # but we have an issue at location 3. try(as_zoned_time(x_naive, zoned_time_zone(x), ambiguous = x)) # When we floored from 02:30:00 -> 01:00:00, we went from being # unambiguous -> ambiguous. In clock, this is something you must handle # explicitly, and cannot be handled by using information from `x`. You can # handle this while still retaining the behavior for the other two # time points that were ambiguous before and after the floor by passing a # list containing `x` and an ambiguous time resolution strategy to use # when information from `x` can't resolve ambiguities: as_zoned_time(x_naive, zoned_time_zone(x), ambiguous = list(x, "latest")) } clock/man/date-shifting.Rd0000644000176200001440000000421014073622034015133 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/date.R \name{date-shifting} \alias{date-shifting} \alias{date_shift.Date} \title{Shifting: date} \usage{ \method{date_shift}{Date}(x, target, ..., which = "next", boundary = "keep") } \arguments{ \item{x}{\verb{[Date]} A date vector.} \item{target}{\verb{[weekday]} A weekday created from \code{\link[=weekday]{weekday()}} to target. Generally this is length 1, but can also be the same length as \code{x}.} \item{...}{These dots are for future extensions and must be empty.} \item{which}{\verb{[character(1)]} One of: \itemize{ \item \code{"next"}: Shift to the next instance of the \code{target} weekday. \item \verb{"previous}: Shift to the previous instance of the \code{target} weekday. }} \item{boundary}{\verb{[character(1)]} One of: \itemize{ \item \code{"keep"}: If \code{x} is currently on the \code{target} weekday, return it. \item \code{"advance"}: If \code{x} is currently on the \code{target} weekday, advance it anyways. }} } \value{ \code{x} shifted to the \code{target} weekday. } \description{ \code{date_shift()} shifts \code{x} to the \code{target} weekday. You can shift to the next or previous weekday. If \code{x} is currently on the \code{target} weekday, you can choose to leave it alone or advance it to the next instance of the \code{target}. Weekday shifting is one of the easiest ways to floor by week while controlling what is considered the first day of the week. You can also accomplish this with the \code{origin} argument of \code{\link[=date_floor]{date_floor()}}, but this is slightly easier. } \examples{ x <- as.Date("2019-01-01") + 0:1 # A Tuesday and Wednesday as_weekday(x) monday <- weekday(clock_weekdays$monday) # Shift to the next Monday date_shift(x, monday) # Shift to the previous Monday # This is an easy way to "floor by week" with a target weekday in mind date_shift(x, monday, which = "previous") # What about Tuesday? tuesday <- weekday(clock_weekdays$tuesday) # Notice that the day that was currently on a Tuesday was not shifted date_shift(x, tuesday) # You can force it to `"advance"` date_shift(x, tuesday, boundary = "advance") } clock/man/year-quarter-day-count-between.Rd0000644000176200001440000000303014151750124020354 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/quarterly-year-quarter-day.R \name{year-quarter-day-count-between} \alias{year-quarter-day-count-between} \alias{calendar_count_between.clock_year_quarter_day} \title{Counting: year-quarter-day} \usage{ \method{calendar_count_between}{clock_year_quarter_day}(start, end, precision, ..., n = 1L) } \arguments{ \item{start, end}{\verb{[clock_year_quarter_day]} A pair of year-quarter-day vectors. These will be recycled to their common size.} \item{precision}{\verb{[character(1)]} One of: \itemize{ \item \code{"year"} \item \code{"quarter"} }} \item{...}{These dots are for future extensions and must be empty.} \item{n}{\verb{[positive integer(1)]} A single positive integer specifying a multiple of \code{precision} to use.} } \value{ An integer representing the number of \code{precision} units between \code{start} and \code{end}. } \description{ This is a year-quarter-day method for the \code{\link[=calendar_count_between]{calendar_count_between()}} generic. It counts the number of \code{precision} units between \code{start} and \code{end} (i.e., the number of years or quarters). } \examples{ # Compute the number of whole quarters between two dates x <- year_quarter_day(2020, 3, 91) y <- year_quarter_day(2025, 4, c(90, 92)) calendar_count_between(x, y, "quarter") # Note that this is not always the same as the number of whole 3 month # periods between two dates x <- as_year_month_day(x) y <- as_year_month_day(y) calendar_count_between(x, y, "month", n = 3) } clock/man/clock_locale.Rd0000644000176200001440000000214014013504577015024 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/clock-locale.R \name{clock_locale} \alias{clock_locale} \title{Create a clock locale} \usage{ clock_locale(labels = "en", decimal_mark = ".") } \arguments{ \item{labels}{\verb{[clock_labels / character(1)]} Character representations of localized weekday names, month names, and AM/PM names. Either the language code as string (passed on to \code{\link[=clock_labels_lookup]{clock_labels_lookup()}}), or an object created by \code{\link[=clock_labels]{clock_labels()}}.} \item{decimal_mark}{\verb{[character(1)]} Symbol used for the decimal place when formatting sub-second date-times. Either \code{","} or \code{"."}.} } \value{ A \code{"clock_locale"} object. } \description{ A clock locale contains the information required to format and parse dates. The defaults have been chosen to match US English. A clock locale object can be provided to \code{format()} methods or parse functions (like \code{\link[=year_month_day_parse]{year_month_day_parse()}}) to override the defaults. } \examples{ clock_locale() clock_locale(labels = "fr") } clock/man/year-month-day-group.Rd0000644000176200001440000000324314017505732016405 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gregorian-year-month-day.R \name{year-month-day-group} \alias{year-month-day-group} \alias{calendar_group.clock_year_month_day} \title{Grouping: year-month-day} \usage{ \method{calendar_group}{clock_year_month_day}(x, precision, ..., n = 1L) } \arguments{ \item{x}{\verb{[clock_year_month_day]} A year-month-day vector.} \item{precision}{\verb{[character(1)]} One of: \itemize{ \item \code{"year"} \item \code{"month"} \item \code{"day"} \item \code{"hour"} \item \code{"minute"} \item \code{"second"} \item \code{"millisecond"} \item \code{"microsecond"} \item \code{"nanosecond"} }} \item{...}{These dots are for future extensions and must be empty.} \item{n}{\verb{[positive integer(1)]} A single positive integer specifying a multiple of \code{precision} to use.} } \value{ \code{x} grouped at the specified \code{precision}. } \description{ This is a year-month-day method for the \code{\link[=calendar_group]{calendar_group()}} generic. Grouping for a year-month-day object can be done at any precision, as long as \code{x} is at least as precise as \code{precision}. } \examples{ steps <- duration_days(seq(0, 100, by = 5)) x <- year_month_day(2019, 1, 1) x <- as_naive_time(x) + steps x <- as_year_month_day(x) x # Group by a single month calendar_group(x, "month") # Or multiple months calendar_group(x, "month", n = 2) # Group 3 days of the month together y <- year_month_day(2019, 1, 1:12) calendar_group(y, "day", n = 3) # Group by 5 nanosecond of the current second z <- year_month_day( 2019, 1, 2, 1, 5, 20, 1:20, subsecond_precision = "nanosecond" ) calendar_group(z, "nanosecond", n = 5) } clock/man/date-and-date-time-rounding.Rd0000644000176200001440000000417014073622034017561 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/date.R \name{date-and-date-time-rounding} \alias{date-and-date-time-rounding} \alias{date_floor} \alias{date_ceiling} \alias{date_round} \title{Date and date-time rounding} \usage{ date_floor(x, precision, ..., n = 1L, origin = NULL) date_ceiling(x, precision, ..., n = 1L, origin = NULL) date_round(x, precision, ..., n = 1L, origin = NULL) } \arguments{ \item{x}{\verb{[Date / POSIXct / POSIXlt]} A date or date-time vector.} \item{precision}{\verb{[character(1)]} A precision. Allowed precisions are dependent on the input used.} \item{...}{These dots are for future extensions and must be empty.} \item{n}{\verb{[positive integer(1)]} A single positive integer specifying a multiple of \code{precision} to use.} \item{origin}{\verb{[Date(1) / POSIXct(1) / POSIXlt(1) / NULL]} An origin to start counting from. The default \code{origin} is midnight on 1970-01-01 in the time zone of \code{x}.} } \value{ \code{x} rounded to the specified \code{precision}. } \description{ \itemize{ \item \code{date_floor()} rounds a date or date-time down to a multiple of the specified \code{precision}. \item \code{date_ceiling()} rounds a date or date-time up to a multiple of the specified \code{precision}. \item \code{date_round()} rounds up or down depending on what is closer, rounding up on ties. } There are separate help pages for rounding dates and date-times: \itemize{ \item \link[=date-rounding]{dates (Date)} \item \link[=posixt-rounding]{date-times (POSIXct/POSIXlt)} } These functions round the underlying duration itself, relative to an \code{origin}. For example, rounding to 15 hours will construct groups of 15 hours, starting from \code{origin}, which defaults to a naive time of 1970-01-01 00:00:00. If you want to group by components, such as "day of the month", see \code{\link[=date_group]{date_group()}}. } \examples{ # See the type specific documentation for more examples x <- as.Date("2019-03-31") + 0:5 x # Flooring by 2 days, note that this is not tied to the current month, # and instead counts from the specified `origin`. date_floor(x, "day", n = 2) } clock/man/date-rounding.Rd0000644000176200001440000000525714073622034015161 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/date.R \name{date-rounding} \alias{date-rounding} \alias{date_floor.Date} \alias{date_ceiling.Date} \alias{date_round.Date} \title{Rounding: date} \usage{ \method{date_floor}{Date}(x, precision, ..., n = 1L, origin = NULL) \method{date_ceiling}{Date}(x, precision, ..., n = 1L, origin = NULL) \method{date_round}{Date}(x, precision, ..., n = 1L, origin = NULL) } \arguments{ \item{x}{\verb{[Date]} A date vector.} \item{precision}{\verb{[character(1)]} One of: \itemize{ \item \code{"week"} \item \code{"day"} } \code{"week"} is an alias for \code{"day"} with \code{n * 7}.} \item{...}{These dots are for future extensions and must be empty.} \item{n}{\verb{[positive integer(1)]} A single positive integer specifying a multiple of \code{precision} to use.} \item{origin}{\verb{[Date(1) / NULL]} An origin to start counting from. The default \code{origin} is 1970-01-01.} } \value{ \code{x} rounded to the specified \code{precision}. } \description{ These are Date methods for the \link[=date-and-date-time-rounding]{rounding generics}. \itemize{ \item \code{date_floor()} rounds a date down to a multiple of the specified \code{precision}. \item \code{date_ceiling()} rounds a date up to a multiple of the specified \code{precision}. \item \code{date_round()} rounds up or down depending on what is closer, rounding up on ties. } The only supported rounding \code{precision}s for Dates are \code{"day"} and \code{"week"}. You can group by irregular periods such as \code{"month"} or \code{"year"} by using \code{\link[=date_group]{date_group()}}. } \details{ When rounding by \code{"week"}, remember that the \code{origin} determines the "week start". By default, 1970-01-01 is the implicit origin, which is a Thursday. If you would like to round by weeks with a different week start, just supply an origin on the weekday you are interested in. } \examples{ x <- as.Date("2019-03-31") + 0:5 x # Flooring by 2 days, note that this is not tied to the current month, # and instead counts from the specified `origin`, so groups can cross # the month boundary date_floor(x, "day", n = 2) # Compare to `date_group()`, which groups by the day of the month date_group(x, "day", n = 2) y <- as.Date("2019-01-01") + 0:20 y # Flooring by week uses an implicit `origin` of 1970-01-01, which # is a Thursday date_floor(y, "week") as_weekday(date_floor(y, "week")) # If you want to round by weeks with a different week start, supply an # `origin` that falls on the weekday you care about. This uses a Monday. origin <- as.Date("1970-01-05") as_weekday(origin) date_floor(y, "week", origin = origin) as_weekday(date_floor(y, "week", origin = origin)) } clock/man/date_format.Rd0000644000176200001440000000153614073622034014702 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/date.R \name{date_format} \alias{date_format} \title{Formatting: date and date-time} \usage{ date_format(x, ...) } \arguments{ \item{x}{\verb{[Date / POSIXct / POSIXlt]} A date or date-time vector.} \item{...}{These dots are for future extensions and must be empty.} } \value{ A character vector of the formatted input. } \description{ \code{date_format()} formats a date (Date) or date-time (POSIXct/POSIXlt) using a \code{format} string. There are separate help pages for formatting dates and date-times: \itemize{ \item \link[=date-formatting]{dates (Date)} \item \link[=posixt-formatting]{date-times (POSIXct/POSIXlt)} } } \examples{ # See method specific documentation for more examples x <- as.Date("2019-01-01") date_format(x, format = "year: \%Y, month: \%m, day: \%d") } clock/man/seq.clock_year_month_day.Rd0000644000176200001440000000446714162373152017372 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gregorian-year-month-day.R \name{seq.clock_year_month_day} \alias{seq.clock_year_month_day} \title{Sequences: year-month-day} \usage{ \method{seq}{clock_year_month_day}(from, to = NULL, by = NULL, length.out = NULL, along.with = NULL, ...) } \arguments{ \item{from}{\verb{[clock_year_month_day(1)]} A \code{"year"} or \code{"month"} precision year-month-day to start the sequence from. \code{from} is always included in the result.} \item{to}{\verb{[clock_year_month_day(1) / NULL]} A \code{"year"} or \code{"month"} precision year-month-day to stop the sequence at. \code{to} is cast to the type of \code{from}. \code{to} is only included in the result if the resulting sequence divides the distance between \code{from} and \code{to} exactly.} \item{by}{\verb{[integer(1) / clock_duration(1) / NULL]} The unit to increment the sequence by. If \code{by} is an integer, it is transformed into a duration with the precision of \code{from}. If \code{by} is a duration, it is cast to the type of \code{from}.} \item{length.out}{\verb{[positive integer(1) / NULL]} The length of the resulting sequence. If specified, \code{along.with} must be \code{NULL}.} \item{along.with}{\verb{[vector / NULL]} A vector who's length determines the length of the resulting sequence. Equivalent to \code{length.out = vec_size(along.with)}. If specified, \code{length.out} must be \code{NULL}.} \item{...}{These dots are for future extensions and must be empty.} } \value{ A sequence with the type of \code{from}. } \description{ This is a year-month-day method for the \code{\link[=seq]{seq()}} generic. Sequences can only be generated for \code{"year"} and \code{"month"} precision year-month-day vectors. When calling \code{seq()}, exactly two of the following must be specified: \itemize{ \item \code{to} \item \code{by} \item Either \code{length.out} or \code{along.with} } } \examples{ # Monthly sequence x <- seq(year_month_day(2019, 1), year_month_day(2020, 12), by = 1) x # Which we can then set the day of to get a sequence of end-of-month values set_day(x, "last") # Daily sequences are not allowed. Use a naive-time for this instead. try(seq(year_month_day(2019, 1, 1), by = 2, length.out = 2)) seq(as_naive_time(year_month_day(2019, 1, 1)), by = 2, length.out = 2) } clock/man/clock-package.Rd0000644000176200001440000000206714416016713015103 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/clock-package.R \docType{package} \name{clock-package} \alias{clock} \alias{clock-package} \title{clock: Date-Time Types and Tools} \description{ \if{html}{\figure{logo.png}{options: style='float: right' alt='logo' width='120'}} Provides a comprehensive library for date-time manipulations using a new family of orthogonal date-time classes (durations, time points, zoned-times, and calendars) that partition responsibilities so that the complexities of time zones are only considered when they are really needed. Capabilities include: date-time parsing, formatting, arithmetic, extraction and updating of components, and rounding. } \seealso{ Useful links: \itemize{ \item \url{https://clock.r-lib.org} \item \url{https://github.com/r-lib/clock} \item Report bugs at \url{https://github.com/r-lib/clock/issues} } } \author{ \strong{Maintainer}: Davis Vaughan \email{davis@posit.co} Other contributors: \itemize{ \item Posit Software, PBC [copyright holder, funder] } } \keyword{internal} clock/man/is_sys_time.Rd0000644000176200001440000000074114017505732014744 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/sys-time.R \name{is_sys_time} \alias{is_sys_time} \title{Is \code{x} a sys-time?} \usage{ is_sys_time(x) } \arguments{ \item{x}{\verb{[object]} An object.} } \value{ \code{TRUE} if \code{x} inherits from \code{"clock_sys_time"}, otherwise \code{FALSE}. } \description{ This function determines if the input is a sys-time object. } \examples{ is_sys_time(1) is_sys_time(as_sys_time(duration_days(1))) } clock/man/date_time_info.Rd0000644000176200001440000000204614422265167015370 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/posixt.R \name{date_time_info} \alias{date_time_info} \title{Info: date-time} \usage{ date_time_info(x) } \arguments{ \item{x}{\verb{[POSIXct / POSIXlt]} A date-time.} } \value{ A data frame of low level information. } \description{ \code{date_time_info()} retrieves a set of low-level information generally not required for most date-time manipulations. It returns a data frame with the same columns as \code{\link[=sys_time_info]{sys_time_info()}}, but the \code{begin} and \code{end} columns are date-times with the same time zone as \code{x}, and the \code{offset} column is an integer rather than a second based \link[=duration_seconds]{duration} column since this is part of the high-level API. } \examples{ x <- date_time_build( 2021, 03, 14, c(01, 03), c(59, 00), c(59, 00), zone = "America/New_York" ) # x[1] is in EST, x[2] is in EDT x info <- date_time_info(x) info # `end` can be used to iterate through daylight saving time transitions date_time_info(info$end) } clock/man/is_year_week_day.Rd0000644000176200001440000000075214422221153015712 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/week-year-week-day.R \name{is_year_week_day} \alias{is_year_week_day} \title{Is \code{x} a year-week-day?} \usage{ is_year_week_day(x) } \arguments{ \item{x}{\verb{[object]} An object.} } \value{ Returns \code{TRUE} if \code{x} inherits from \code{"clock_year_week_day"}, otherwise returns \code{FALSE}. } \description{ Check if \code{x} is a year-week-day. } \examples{ is_year_week_day(year_week_day(2019)) } clock/man/time_point_spanning_seq.Rd0000644000176200001440000000211214425175541017327 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/time-point.R \name{time_point_spanning_seq} \alias{time_point_spanning_seq} \title{Spanning sequence: time points} \usage{ time_point_spanning_seq(x) } \arguments{ \item{x}{\verb{[clock_sys_time / clock_naive_time]} A time point vector.} } \value{ A sequence along \verb{[min(x), max(x)]}. } \description{ \code{time_point_spanning_seq()} generates a regular sequence along the span of \code{x}, i.e. along \verb{[min(x), max(x)]}. The sequence is generated at the precision of \code{x}. } \details{ Missing values are automatically removed before the sequence is generated. If you need more precise sequence generation, call \code{\link[=range]{range()}} and \code{\link[=seq]{seq()}} directly. } \examples{ x <- as_naive_time(year_month_day(2019, c(1, 2, 1, 2), c(15, 4, 12, 2))) x time_point_spanning_seq(x) # The sequence is generated at the precision of `x` x <- as_naive_time(c( year_month_day(2019, 1, 1, 5), year_month_day(2019, 1, 2, 10), year_month_day(2019, 1, 1, 3) )) time_point_spanning_seq(x) } clock/man/posixt-shifting.Rd0000644000176200001440000001176414011330212015542 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/posixt.R \name{posixt-shifting} \alias{posixt-shifting} \alias{date_shift.POSIXt} \title{Shifting: date and date-time} \usage{ \method{date_shift}{POSIXt}( x, target, ..., which = "next", boundary = "keep", nonexistent = NULL, ambiguous = x ) } \arguments{ \item{x}{\verb{[POSIXct / POSIXlt]} A date-time vector.} \item{target}{\verb{[weekday]} A weekday created from \code{\link[=weekday]{weekday()}} to target. Generally this is length 1, but can also be the same length as \code{x}.} \item{...}{These dots are for future extensions and must be empty.} \item{which}{\verb{[character(1)]} One of: \itemize{ \item \code{"next"}: Shift to the next instance of the \code{target} weekday. \item \verb{"previous}: Shift to the previous instance of the \code{target} weekday. }} \item{boundary}{\verb{[character(1)]} One of: \itemize{ \item \code{"keep"}: If \code{x} is currently on the \code{target} weekday, return it. \item \code{"advance"}: If \code{x} is currently on the \code{target} weekday, advance it anyways. }} \item{nonexistent}{\verb{[character / NULL]} One of the following nonexistent time resolution strategies, allowed to be either length 1, or the same length as the input: \itemize{ \item \code{"roll-forward"}: The next valid instant in time. \item \code{"roll-backward"}: The previous valid instant in time. \item \code{"shift-forward"}: Shift the nonexistent time forward by the size of the daylight saving time gap. \item \verb{"shift-backward}: Shift the nonexistent time backward by the size of the daylight saving time gap. \item \code{"NA"}: Replace nonexistent times with \code{NA}. \item \code{"error"}: Error on nonexistent times. } Using either \code{"roll-forward"} or \code{"roll-backward"} is generally recommended over shifting, as these two strategies maintain the \emph{relative ordering} between elements of the input. If \code{NULL}, defaults to \code{"error"}. If \code{getOption("clock.strict")} is \code{TRUE}, \code{nonexistent} must be supplied and cannot be \code{NULL}. This is a convenient way to make production code robust to nonexistent times.} \item{ambiguous}{\verb{[character / zoned_time / POSIXct / list(2) / NULL]} One of the following ambiguous time resolution strategies, allowed to be either length 1, or the same length as the input: \itemize{ \item \code{"earliest"}: Of the two possible times, choose the earliest one. \item \code{"latest"}: Of the two possible times, choose the latest one. \item \code{"NA"}: Replace ambiguous times with \code{NA}. \item \code{"error"}: Error on ambiguous times. } Alternatively, \code{ambiguous} is allowed to be a zoned_time (or POSIXct) that is either length 1, or the same length as the input. If an ambiguous time is encountered, the zoned_time is consulted. If the zoned_time corresponds to a naive_time that is also ambiguous \emph{and} uses the same daylight saving time transition point as the original ambiguous time, then the offset of the zoned_time is used to resolve the ambiguity. If the ambiguity cannot be resolved by consulting the zoned_time, then this method falls back to \code{NULL}. Finally, \code{ambiguous} is allowed to be a list of size 2, where the first element of the list is a zoned_time (as described above), and the second element of the list is an ambiguous time resolution strategy to use when the ambiguous time cannot be resolved by consulting the zoned_time. Specifying a zoned_time on its own is identical to \verb{list(, NULL)}. If \code{NULL}, defaults to \code{"error"}. If \code{getOption("clock.strict")} is \code{TRUE}, \code{ambiguous} must be supplied and cannot be \code{NULL}. Additionally, \code{ambiguous} cannot be specified as a zoned_time on its own, as this implies \code{NULL} for ambiguous times that the zoned_time cannot resolve. Instead, it must be specified as a list alongside an ambiguous time resolution strategy as described above. This is a convenient way to make production code robust to ambiguous times.} } \value{ \code{x} shifted to the \code{target} weekday. } \description{ \code{date_shift()} shifts \code{x} to the \code{target} weekday. You can shift to the next or previous weekday. If \code{x} is currently on the \code{target} weekday, you can choose to leave it alone or advance it to the next instance of the \code{target}. Shifting with date-times retains the time of day where possible. Be aware that you can run into daylight saving time issues if you shift into a daylight saving time gap or fallback period. } \examples{ tuesday <- weekday(clock_weekdays$tuesday) x <- as.POSIXct("1970-04-22 02:30:00", "America/New_York") # Shift to the next Tuesday date_shift(x, tuesday) # Be aware that you can run into daylight saving time issues! # Here we shift directly into a daylight saving time gap # from 01:59:59 -> 03:00:00 sunday <- weekday(clock_weekdays$sunday) try(date_shift(x, sunday)) # You can resolve this with the `nonexistent` argument date_shift(x, sunday, nonexistent = "roll-forward") } clock/man/year_day.Rd0000644000176200001440000000401714416030722014205 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gregorian-year-day.R \name{year_day} \alias{year_day} \title{Calendar: year-day} \usage{ year_day( year, day = NULL, hour = NULL, minute = NULL, second = NULL, subsecond = NULL, ..., subsecond_precision = NULL ) } \arguments{ \item{year}{\verb{[integer]} The year. Values \verb{[-32767, 32767]} are generally allowed.} \item{day}{\verb{[integer / NULL]} The day of the year. Values \verb{[1, 366]} are allowed.} \item{hour}{\verb{[integer / NULL]} The hour. Values \verb{[0, 23]} are allowed.} \item{minute}{\verb{[integer / NULL]} The minute. Values \verb{[0, 59]} are allowed.} \item{second}{\verb{[integer / NULL]} The second. Values \verb{[0, 59]} are allowed.} \item{subsecond}{\verb{[integer / NULL]} The subsecond. If specified, \code{subsecond_precision} must also be specified to determine how to interpret the \code{subsecond}. If using milliseconds, values \verb{[0, 999]} are allowed. If using microseconds, values \verb{[0, 999999]} are allowed. If using nanoseconds, values \verb{[0, 999999999]} are allowed.} \item{...}{These dots are for future extensions and must be empty.} \item{subsecond_precision}{\verb{[character(1) / NULL]} The precision to interpret \code{subsecond} as. One of: \code{"millisecond"}, \code{"microsecond"}, or \code{"nanosecond"}.} } \value{ A year-day calendar vector. } \description{ \code{year_day()} constructs a calendar vector from the Gregorian year and day of the year. } \details{ Fields are recycled against each other using \link[vctrs:vector_recycling_rules]{tidyverse recycling rules}. Fields are collected in order until the first \code{NULL} field is located. No fields after the first \code{NULL} field are used. } \examples{ # Just the year x <- year_day(2019:2025) x year_day(2020, 1:10) # Last day of the year, accounting for leap years year_day(2019:2021, "last") # Precision can go all the way out to nanosecond year_day(2019, 100, 2, 40, 45, 200, subsecond_precision = "nanosecond") } clock/man/clock_labels.Rd0000644000176200001440000000326514013504577015040 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/clock-labels.R \name{clock_labels} \alias{clock_labels} \alias{clock_labels_lookup} \alias{clock_labels_languages} \title{Create or retrieve date related labels} \usage{ clock_labels( month, month_abbrev = month, weekday, weekday_abbrev = weekday, am_pm ) clock_labels_lookup(language) clock_labels_languages() } \arguments{ \item{month, month_abbrev}{\verb{[character(12)]} Full and abbreviated month names. Starts with January.} \item{weekday, weekday_abbrev}{\verb{[character(7)]} Full and abbreviated weekday names. Starts with Sunday.} \item{am_pm}{\verb{[character(2)]} Names used for AM and PM.} \item{language}{\verb{[character(1)]} A BCP 47 locale, generally constructed from a two or three digit language code. See \code{clock_labels_languages()} for a complete list of available locales.} } \value{ A \code{"clock_labels"} object. } \description{ When parsing and formatting dates, you often need to know how weekdays of the week and months are represented as text. These functions allow you to either create your own labels, or look them up from a standard set of language specific labels. The standard list is derived from ICU (\url{https://unicode-org.github.io/icu/}) via the stringi package. \itemize{ \item \code{clock_labels_lookup()} looks up a set of labels from a given language code. \item \code{clock_labels_languages()} lists the language codes that are accepted. \item \code{clock_labels()} lets you create your own set of labels. Use this if the currently supported languages don't meet your needs. } } \examples{ clock_labels_lookup("en") clock_labels_lookup("ko") clock_labels_lookup("fr") } clock/man/zoned_time_now.Rd0000644000176200001440000000147614017505732015443 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/zoned-time.R \name{zoned_time_now} \alias{zoned_time_now} \title{What is the current zoned-time?} \usage{ zoned_time_now(zone) } \arguments{ \item{zone}{\verb{[character(1)]} A time zone to get the current time for.} } \value{ A zoned-time of the current time. } \description{ \code{zoned_time_now()} returns the current time in the corresponding \code{zone}. It is a wrapper around \code{\link[=sys_time_now]{sys_time_now()}} that attaches the time zone. } \details{ The time is returned with a nanosecond precision, but the actual amount of data returned is OS dependent. Usually, information at at least the microsecond level is returned, with some platforms returning nanosecond information. } \examples{ x <- zoned_time_now("America/New_York") } clock/man/year-quarter-day-group.Rd0000644000176200001440000000302114007014157016730 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/quarterly-year-quarter-day.R \name{year-quarter-day-group} \alias{year-quarter-day-group} \alias{calendar_group.clock_year_quarter_day} \title{Grouping: year-quarter-day} \usage{ \method{calendar_group}{clock_year_quarter_day}(x, precision, ..., n = 1L) } \arguments{ \item{x}{\verb{[clock_year_quarter_day]} A year-quarter-day vector.} \item{precision}{\verb{[character(1)]} One of: \itemize{ \item \code{"year"} \item \code{"quarter"} \item \code{"day"} \item \code{"hour"} \item \code{"minute"} \item \code{"second"} \item \code{"millisecond"} \item \code{"microsecond"} \item \code{"nanosecond"} }} \item{...}{These dots are for future extensions and must be empty.} \item{n}{\verb{[positive integer(1)]} A single positive integer specifying a multiple of \code{precision} to use.} } \value{ \code{x} grouped at the specified \code{precision}. } \description{ This is a year-quarter-day method for the \code{\link[=calendar_group]{calendar_group()}} generic. Grouping for a year-quarter-day object can be done at any precision, as long as \code{x} is at least as precise as \code{precision}. } \examples{ x <- year_quarter_day(2019, 1:4) x <- c(x, set_year(x, 2020)) # Group by 3 quarters # Note that this is a grouping of 3 quarters of the current year # (i.e. the count resets at the beginning of the next year) calendar_group(x, "quarter", n = 3) # Group by 5 days of the current quarter y <- year_quarter_day(2019, 1, 1:90) calendar_group(y, "day", n = 5) } clock/man/time_point_shift.Rd0000644000176200001440000000427614017505732015770 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/time-point.R \name{time_point_shift} \alias{time_point_shift} \title{Shifting: time point} \usage{ time_point_shift(x, target, ..., which = "next", boundary = "keep") } \arguments{ \item{x}{\verb{[clock_time_point]} A time point.} \item{target}{\verb{[weekday]} A weekday created from \code{\link[=weekday]{weekday()}} to target. Generally this is length 1, but can also be the same length as \code{x}.} \item{...}{These dots are for future extensions and must be empty.} \item{which}{\verb{[character(1)]} One of: \itemize{ \item \code{"next"}: Shift to the next instance of the \code{target} weekday. \item \verb{"previous}: Shift to the previous instance of the \code{target} weekday. }} \item{boundary}{\verb{[character(1)]} One of: \itemize{ \item \code{"keep"}: If \code{x} is currently on the \code{target} weekday, return it. \item \code{"advance"}: If \code{x} is currently on the \code{target} weekday, advance it anyways. }} } \value{ \code{x} shifted to the \code{target} weekday. } \description{ \code{time_point_shift()} shifts \code{x} to the \code{target} weekday. You can shift to the next or previous weekday. If \code{x} is currently on the \code{target} weekday, you can choose to leave it alone or advance it to the next instance of the \code{target}. Weekday shifting is one of the easiest ways to floor by week while controlling what is considered the first day of the week. You can also accomplish this with the \code{origin} argument of \code{\link[=time_point_floor]{time_point_floor()}}, but this is slightly easier. } \examples{ x <- as_naive_time(year_month_day(2019, 1, 1:2)) # A Tuesday and Wednesday as_weekday(x) monday <- weekday(clock_weekdays$monday) # Shift to the next Monday time_point_shift(x, monday) # Shift to the previous Monday # This is an easy way to "floor by week" with a target weekday in mind time_point_shift(x, monday, which = "previous") # What about Tuesday? tuesday <- weekday(clock_weekdays$tuesday) # Notice that the day that was currently on a Tuesday was not shifted time_point_shift(x, tuesday) # You can force it to `"advance"` time_point_shift(x, tuesday, boundary = "advance") } clock/man/year-quarter-day-widen.Rd0000644000176200001440000000211014006266045016704 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/quarterly-year-quarter-day.R \name{year-quarter-day-widen} \alias{year-quarter-day-widen} \alias{calendar_widen.clock_year_quarter_day} \title{Widen: year-quarter-day} \usage{ \method{calendar_widen}{clock_year_quarter_day}(x, precision) } \arguments{ \item{x}{\verb{[clock_year_quarter_day]} A year-quarter-day vector.} \item{precision}{\verb{[character(1)]} One of: \itemize{ \item \code{"year"} \item \code{"quarter"} \item \code{"day"} \item \code{"hour"} \item \code{"minute"} \item \code{"second"} \item \code{"millisecond"} \item \code{"microsecond"} \item \code{"nanosecond"} }} } \value{ \code{x} widened to the supplied \code{precision}. } \description{ This is a year-quarter-day method for the \code{\link[=calendar_widen]{calendar_widen()}} generic. It widens a year-quarter-day vector to the specified \code{precision}. } \examples{ # Quarter precision x <- year_quarter_day(2019, 1) x # Widen to day precision calendar_widen(x, "day") # Or second precision sec <- calendar_widen(x, "second") sec } clock/man/date_time_build.Rd0000644000176200001440000001360114416032013015514 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/posixt.R \name{date_time_build} \alias{date_time_build} \title{Building: date-time} \usage{ date_time_build( year, month = 1L, day = 1L, hour = 0L, minute = 0L, second = 0L, ..., zone, invalid = NULL, nonexistent = NULL, ambiguous = NULL ) } \arguments{ \item{year}{\verb{[integer]} The year. Values \verb{[-32767, 32767]} are generally allowed.} \item{month}{\verb{[integer]} The month. Values \verb{[1, 12]} are allowed.} \item{day}{\verb{[integer / "last"]} The day of the month. Values \verb{[1, 31]} are allowed. If \code{"last"}, then the last day of the month is returned.} \item{hour}{\verb{[integer]} The hour. Values \verb{[0, 23]} are allowed.} \item{minute}{\verb{[integer]} The minute. Values \verb{[0, 59]} are allowed.} \item{second}{\verb{[integer]} The second. Values \verb{[0, 59]} are allowed.} \item{...}{These dots are for future extensions and must be empty.} \item{zone}{\verb{[character(1)]} A valid time zone name. This argument is required, and must be specified by name.} \item{invalid}{\verb{[character(1) / NULL]} One of the following invalid date resolution strategies: \itemize{ \item \code{"previous"}: The previous valid instant in time. \item \code{"previous-day"}: The previous valid day in time, keeping the time of day. \item \code{"next"}: The next valid instant in time. \item \code{"next-day"}: The next valid day in time, keeping the time of day. \item \code{"overflow"}: Overflow by the number of days that the input is invalid by. Time of day is dropped. \item \code{"overflow-day"}: Overflow by the number of days that the input is invalid by. Time of day is kept. \item \code{"NA"}: Replace invalid dates with \code{NA}. \item \code{"error"}: Error on invalid dates. } Using either \code{"previous"} or \code{"next"} is generally recommended, as these two strategies maintain the \emph{relative ordering} between elements of the input. If \code{NULL}, defaults to \code{"error"}. If \code{getOption("clock.strict")} is \code{TRUE}, \code{invalid} must be supplied and cannot be \code{NULL}. This is a convenient way to make production code robust to invalid dates.} \item{nonexistent}{\verb{[character / NULL]} One of the following nonexistent time resolution strategies, allowed to be either length 1, or the same length as the input: \itemize{ \item \code{"roll-forward"}: The next valid instant in time. \item \code{"roll-backward"}: The previous valid instant in time. \item \code{"shift-forward"}: Shift the nonexistent time forward by the size of the daylight saving time gap. \item \verb{"shift-backward}: Shift the nonexistent time backward by the size of the daylight saving time gap. \item \code{"NA"}: Replace nonexistent times with \code{NA}. \item \code{"error"}: Error on nonexistent times. } Using either \code{"roll-forward"} or \code{"roll-backward"} is generally recommended over shifting, as these two strategies maintain the \emph{relative ordering} between elements of the input. If \code{NULL}, defaults to \code{"error"}. If \code{getOption("clock.strict")} is \code{TRUE}, \code{nonexistent} must be supplied and cannot be \code{NULL}. This is a convenient way to make production code robust to nonexistent times.} \item{ambiguous}{\verb{[character / zoned_time / POSIXct / list(2) / NULL]} One of the following ambiguous time resolution strategies, allowed to be either length 1, or the same length as the input: \itemize{ \item \code{"earliest"}: Of the two possible times, choose the earliest one. \item \code{"latest"}: Of the two possible times, choose the latest one. \item \code{"NA"}: Replace ambiguous times with \code{NA}. \item \code{"error"}: Error on ambiguous times. } Alternatively, \code{ambiguous} is allowed to be a zoned_time (or POSIXct) that is either length 1, or the same length as the input. If an ambiguous time is encountered, the zoned_time is consulted. If the zoned_time corresponds to a naive_time that is also ambiguous \emph{and} uses the same daylight saving time transition point as the original ambiguous time, then the offset of the zoned_time is used to resolve the ambiguity. If the ambiguity cannot be resolved by consulting the zoned_time, then this method falls back to \code{NULL}. Finally, \code{ambiguous} is allowed to be a list of size 2, where the first element of the list is a zoned_time (as described above), and the second element of the list is an ambiguous time resolution strategy to use when the ambiguous time cannot be resolved by consulting the zoned_time. Specifying a zoned_time on its own is identical to \verb{list(, NULL)}. If \code{NULL}, defaults to \code{"error"}. If \code{getOption("clock.strict")} is \code{TRUE}, \code{ambiguous} must be supplied and cannot be \code{NULL}. Additionally, \code{ambiguous} cannot be specified as a zoned_time on its own, as this implies \code{NULL} for ambiguous times that the zoned_time cannot resolve. Instead, it must be specified as a list alongside an ambiguous time resolution strategy as described above. This is a convenient way to make production code robust to ambiguous times.} } \value{ A POSIXct. } \description{ \code{date_time_build()} builds a POSIXct from it's individual components. To build a POSIXct, it is required that you specify the \code{zone}. } \details{ Components are recycled against each other using \link[vctrs:vector_recycling_rules]{tidyverse recycling rules}. } \examples{ # The zone argument is required! # clock always requires you to be explicit about your choice of `zone`. try(date_time_build(2020)) date_time_build(2020, zone = "America/New_York") # Nonexistent time due to daylight saving time gap from 01:59:59 -> 03:00:00 try(date_time_build(1970, 4, 26, 1:12, 30, zone = "America/New_York")) # Resolve with a nonexistent time resolution strategy date_time_build( 1970, 4, 26, 1:12, 30, zone = "America/New_York", nonexistent = "roll-forward" ) } clock/man/duration-helper.Rd0000644000176200001440000000567514010772357015534 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/duration.R \name{duration-helper} \alias{duration-helper} \alias{duration_years} \alias{duration_quarters} \alias{duration_months} \alias{duration_weeks} \alias{duration_days} \alias{duration_hours} \alias{duration_minutes} \alias{duration_seconds} \alias{duration_milliseconds} \alias{duration_microseconds} \alias{duration_nanoseconds} \title{Construct a duration} \usage{ duration_years(n = integer()) duration_quarters(n = integer()) duration_months(n = integer()) duration_weeks(n = integer()) duration_days(n = integer()) duration_hours(n = integer()) duration_minutes(n = integer()) duration_seconds(n = integer()) duration_milliseconds(n = integer()) duration_microseconds(n = integer()) duration_nanoseconds(n = integer()) } \arguments{ \item{n}{\verb{[integer]} The number of units of time to use when creating the duration.} } \value{ A duration of the specified precision. } \description{ These helpers construct durations of the specified precision. Durations represent units of time. Durations are separated into two categories: \strong{Calendrical} \itemize{ \item year \item quarter \item month } \strong{Chronological} \itemize{ \item week \item day \item hour \item minute \item second \item millisecond \item microsecond \item nanosecond } Calendrical durations are generally used when manipulating calendar types, like year-month-day. Chronological durations are generally used when working with time points, like sys-time or naive-time. } \section{Internal Representation}{ Durations are internally represented as an integer number of "ticks" along with a ratio describing how it converts to a number of seconds. The following duration ratios are used in clock: \itemize{ \item \verb{1 year == 31556952 seconds} \item \verb{1 quarter == 7889238 seconds} \item \verb{1 month == 2629746 seconds} \item \verb{1 week == 604800 seconds} \item \verb{1 day == 86400 seconds} \item \verb{1 hour == 3600 seconds} \item \verb{1 minute == 60 seconds} \item \verb{1 second == 1 second} \item \verb{1 millisecond == 1 / 1000 seconds} \item \verb{1 microsecond == 1 / 1000000 seconds} \item \verb{1 nanosecond == 1 / 1000000000 seconds} } A duration of 1 year is defined to correspond to the average length of a proleptic Gregorian year, i.e. 365.2425 days. A duration of 1 month is defined as exactly 1/12 of a year. A duration of 1 quarter is defined as exactly 1/4 of a year. A duration of 1 week is defined as exactly 7 days. These conversions come into play when doing operations like adding or flooring durations. Generally, you add two calendrical durations together to get a new calendrical duration, rather than adding a calendrical and a chronological duration together. The one exception is \code{\link[=duration_cast]{duration_cast()}}, which can cast durations to any other precision, with a potential loss of information. } \examples{ duration_years(1:5) duration_nanoseconds(1:5) } clock/man/year-day-narrow.Rd0000644000176200001440000000251714016222032015425 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/gregorian-year-day.R \name{year-day-narrow} \alias{year-day-narrow} \alias{calendar_narrow.clock_year_day} \title{Narrow: year-day} \usage{ \method{calendar_narrow}{clock_year_day}(x, precision) } \arguments{ \item{x}{\verb{[clock_year_day]} A year-day vector.} \item{precision}{\verb{[character(1)]} One of: \itemize{ \item \code{"year"} \item \code{"day"} \item \code{"hour"} \item \code{"minute"} \item \code{"second"} \item \code{"millisecond"} \item \code{"microsecond"} \item \code{"nanosecond"} }} } \value{ \code{x} narrowed to the supplied \code{precision}. } \description{ This is a year-day method for the \code{\link[=calendar_narrow]{calendar_narrow()}} generic. It narrows a year-day vector to the specified \code{precision}. } \examples{ # Hour precision x <- year_day(2019, 3, 4) x # Narrowed to day precision calendar_narrow(x, "day") # Or year precision calendar_narrow(x, "year") # Subsecond precision can be narrowed to second precision milli <- calendar_widen(x, "millisecond") micro <- calendar_widen(x, "microsecond") milli micro calendar_narrow(milli, "second") calendar_narrow(micro, "second") # But once you have "locked in" a subsecond precision, it can't be # narrowed to another subsecond precision try(calendar_narrow(micro, "millisecond")) } clock/man/date_weekday_factor.Rd0000644000176200001440000000337214073622034016401 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/date.R \name{date_weekday_factor} \alias{date_weekday_factor} \title{Convert a date or date-time to a weekday factor} \usage{ date_weekday_factor( x, ..., labels = "en", abbreviate = TRUE, encoding = "western" ) } \arguments{ \item{x}{\verb{[Date / POSIXct / POSIXlt]} A date or date-time vector.} \item{...}{These dots are for future extensions and must be empty.} \item{labels}{\verb{[clock_labels / character(1)]} Character representations of localized weekday names, month names, and AM/PM names. Either the language code as string (passed on to \code{\link[=clock_labels_lookup]{clock_labels_lookup()}}), or an object created by \code{\link[=clock_labels]{clock_labels()}}.} \item{abbreviate}{\verb{[logical(1)]} If \code{TRUE}, the abbreviated weekday names from \code{labels} will be used. If \code{FALSE}, the full weekday names from \code{labels} will be used.} \item{encoding}{\verb{[character(1)]} One of: \itemize{ \item \code{"western"}: Encode the weekdays as an ordered factor with levels from Sunday -> Saturday. \item \code{"iso"}: Encode the weekdays as an ordered factor with levels from Monday -> Sunday. }} } \value{ An ordered factor representing the weekdays. } \description{ \code{date_weekday_factor()} converts a date or date-time to an ordered factor with levels representing the weekday. This can be useful in combination with ggplot2, or for modeling. } \examples{ x <- as.Date("2019-01-01") + 0:6 # Default to Sunday -> Saturday date_weekday_factor(x) # ISO encoding is Monday -> Sunday date_weekday_factor(x, encoding = "iso") # With full names date_weekday_factor(x, abbreviate = FALSE) # Or a different language date_weekday_factor(x, labels = "fr") } clock/man/calendar_precision.Rd0000644000176200001440000000114314027170177016240 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/calendar.R \name{calendar_precision} \alias{calendar_precision} \title{Precision: calendar} \usage{ calendar_precision(x) } \arguments{ \item{x}{\verb{[clock_calendar]} A calendar.} } \value{ A single string holding the precision of the calendar. } \description{ \code{calendar_precision()} extracts the precision from a calendar object. It returns the precision as a single string. } \examples{ calendar_precision(year_month_day(2019)) calendar_precision(year_month_day(2019, 1, 1)) calendar_precision(year_quarter_day(2019, 3)) } clock/man/sys-parsing.Rd0000644000176200001440000003504114265310513014671 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/sys-time.R \name{sys-parsing} \alias{sys-parsing} \alias{sys_time_parse} \alias{sys_time_parse_RFC_3339} \title{Parsing: sys-time} \usage{ sys_time_parse( x, ..., format = NULL, precision = "second", locale = clock_locale() ) sys_time_parse_RFC_3339( x, ..., separator = "T", offset = "Z", precision = "second" ) } \arguments{ \item{x}{\verb{[character]} A character vector to parse.} \item{...}{These dots are for future extensions and must be empty.} \item{format}{\verb{[character / NULL]} A format string. A combination of the following commands, or \code{NULL}, in which case a default format string is used. A vector of multiple format strings can be supplied. They will be tried in the order they are provided. \strong{Year} \itemize{ \item \verb{\%C}: The century as a decimal number. The modified command \verb{\%NC} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. \item \verb{\%y}: The last two decimal digits of the year. If the century is not otherwise specified (e.g. with \verb{\%C}), values in the range \verb{[69 - 99]} are presumed to refer to the years \verb{[1969 - 1999]}, and values in the range \verb{[00 - 68]} are presumed to refer to the years \verb{[2000 - 2068]}. The modified command \verb{\%Ny}, where \code{N} is a positive decimal integer, specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. \item \verb{\%Y}: The year as a decimal number. The modified command \verb{\%NY} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{4}. Leading zeroes are permitted but not required. } \strong{Month} \itemize{ \item \verb{\%b}, \verb{\%B}, \verb{\%h}: The \code{locale}'s full or abbreviated case-insensitive month name. \item \verb{\%m}: The month as a decimal number. January is \code{1}. The modified command \verb{\%Nm} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. } \strong{Day} \itemize{ \item \verb{\%d}, \verb{\%e}: The day of the month as a decimal number. The modified command \verb{\%Nd} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. } \strong{Day of the week} \itemize{ \item \verb{\%a}, \verb{\%A}: The \code{locale}'s full or abbreviated case-insensitive weekday name. \item \verb{\%w}: The weekday as a decimal number (\code{0-6}), where Sunday is \code{0}. The modified command \verb{\%Nw} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{1}. Leading zeroes are permitted but not required. } \strong{ISO 8601 week-based year} \itemize{ \item \verb{\%g}: The last two decimal digits of the ISO week-based year. The modified command \verb{\%Ng} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. \item \verb{\%G}: The ISO week-based year as a decimal number. The modified command \verb{\%NG} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{4}. Leading zeroes are permitted but not required. \item \verb{\%V}: The ISO week-based week number as a decimal number. The modified command \verb{\%NV} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. \item \verb{\%u}: The ISO weekday as a decimal number (\code{1-7}), where Monday is \code{1}. The modified command \verb{\%Nu} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{1}. Leading zeroes are permitted but not required. } \strong{Week of the year} \itemize{ \item \verb{\%U}: The week number of the year as a decimal number. The first Sunday of the year is the first day of week \code{01}. Days of the same year prior to that are in week \code{00}. The modified command \verb{\%NU} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. \item \verb{\%W}: The week number of the year as a decimal number. The first Monday of the year is the first day of week \code{01}. Days of the same year prior to that are in week \code{00}. The modified command \verb{\%NW} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. } \strong{Day of the year} \itemize{ \item \verb{\%j}: The day of the year as a decimal number. January 1 is \code{1}. The modified command \verb{\%Nj} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{3}. Leading zeroes are permitted but not required. } \strong{Date} \itemize{ \item \verb{\%D}, \verb{\%x}: Equivalent to \verb{\%m/\%d/\%y}. \item \verb{\%F}: Equivalent to \verb{\%Y-\%m-\%d}. If modified with a width (like \verb{\%NF}), the width is applied to only \verb{\%Y}. } \strong{Time of day} \itemize{ \item \verb{\%H}: The hour (24-hour clock) as a decimal number. The modified command \verb{\%NH} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. \item \verb{\%I}: The hour (12-hour clock) as a decimal number. The modified command \verb{\%NI} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. \item \verb{\%M}: The minutes as a decimal number. The modified command \verb{\%NM} where \code{N} is a positive decimal integer specifies the maximum number of characters to read. If not specified, the default is \code{2}. Leading zeroes are permitted but not required. \item \verb{\%S}: The seconds as a decimal number. Leading zeroes are permitted but not required. If encountered, the \code{locale} determines the decimal point character. Generally, the maximum number of characters to read is determined by the precision that you are parsing at. For example, a precision of \code{"second"} would read a maximum of 2 characters, while a precision of \code{"millisecond"} would read a maximum of 6 (2 for the values before the decimal point, 1 for the decimal point, and 3 for the values after it). The modified command \verb{\%NS}, where \code{N} is a positive decimal integer, can be used to exactly specify the maximum number of characters to read. This is only useful if you happen to have seconds with more than 1 leading zero. \item \verb{\%p}: The \code{locale}'s equivalent of the AM/PM designations associated with a 12-hour clock. The command \verb{\%I} must precede \verb{\%p} in the format string. \item \verb{\%R}: Equivalent to \verb{\%H:\%M}. \item \verb{\%T}, \verb{\%X}: Equivalent to \verb{\%H:\%M:\%S}. \item \verb{\%r}: Equivalent to \verb{\%I:\%M:\%S \%p}. } \strong{Time zone} \itemize{ \item \verb{\%z}: The offset from UTC in the format \verb{[+|-]hh[mm]}. For example \code{-0430} refers to 4 hours 30 minutes behind UTC. And \code{04} refers to 4 hours ahead of UTC. The modified command \verb{\%Ez} parses a \code{:} between the hours and minutes and leading zeroes on the hour field are optional: \verb{[+|-]h[h][:mm]}. For example \code{-04:30} refers to 4 hours 30 minutes behind UTC. And \code{4} refers to 4 hours ahead of UTC. \item \verb{\%Z}: The full time zone name or the time zone abbreviation, depending on the function being used. A single word is parsed. This word can only contain characters that are alphanumeric, or one of \code{'_'}, \code{'/'}, \code{'-'} or \code{'+'}. } \strong{Miscellaneous} \itemize{ \item \verb{\%c}: A date and time representation. Equivalent to \verb{\%a \%b \%d \%H:\%M:\%S \%Y}. \item \code{\%\%}: A \verb{\%} character. \item \verb{\%n}: Matches one white space character. \verb{\%n}, \verb{\%t}, and a space can be combined to match a wide range of white-space patterns. For example \code{"\%n "} matches one or more white space characters, and \code{"\%n\%t\%t"} matches one to three white space characters. \item \verb{\%t}: Matches zero or one white space characters. }} \item{precision}{\verb{[character(1)]} A precision for the resulting time point. One of: \itemize{ \item \code{"day"} \item \code{"hour"} \item \code{"minute"} \item \code{"second"} \item \code{"millisecond"} \item \code{"microsecond"} \item \code{"nanosecond"} } Setting the \code{precision} determines how much information \verb{\%S} attempts to parse.} \item{locale}{\verb{[clock_locale]} A locale object created from \code{\link[=clock_locale]{clock_locale()}}.} \item{separator}{\verb{[character(1)]} The separator between the date and time components of the string. One of: \itemize{ \item \code{"T"} \item \code{"t"} \item \code{" "} }} \item{offset}{\verb{[character(1)]} The format of the offset from UTC contained in the string. One of: \itemize{ \item \code{"Z"} \item \code{"z"} \item \code{"\%z"} to parse a numeric offset of the form \code{"+0430"} \item \code{"\%Ez"} to parse a numeric offset of the form \code{"+04:30"} }} } \value{ A sys-time. } \description{ There are two parsers into a sys-time, \code{sys_time_parse()} and \code{sys_time_parse_RFC_3339()}. \subsection{sys_time_parse()}{ \code{sys_time_parse()} is useful when you have date-time strings like \code{"2020-01-01T01:04:30"} that you know should be interpreted as UTC, or like \code{"2020-01-01T01:04:30-04:00"} with a UTC offset but no zone name. If you find yourself in the latter situation, then parsing this string as a sys-time using the \verb{\%Ez} command to capture the offset is probably your best option. If you know that this string should be interpreted in a specific time zone, parse as a sys-time to get the UTC equivalent, then use \code{\link[=as_zoned_time]{as_zoned_time()}}. The default options assume that \code{x} should be parsed at second precision, using a \code{format} string of \code{"\%Y-\%m-\%dT\%H:\%M:\%S"}. This matches the default result from calling \code{format()} on a sys-time. \code{sys_time_parse()} is nearly equivalent to \code{\link[=naive_time_parse]{naive_time_parse()}}, except for the fact that the \verb{\%z} command is actually used. Using \verb{\%z} assumes that the rest of the date-time string should be interpreted as a naive-time, which is then shifted by the UTC offset found in \verb{\%z}. The returned time can then be validly interpreted as UTC. \emph{\code{sys_time_parse()} ignores the \verb{\%Z} command.} } \subsection{sys_time_parse_RFC_3339()}{ \code{sys_time_parse_RFC_3339()} is a wrapper around \code{sys_time_parse()} that is intended to parse the extremely common date-time format outlined by \href{https://datatracker.ietf.org/doc/html/rfc3339}{RFC 3339}. This document outlines a profile of the ISO 8601 format that is even more restrictive. In particular, this function is intended to parse the following three formats: \if{html}{\out{
}}\preformatted{2019-01-01T00:00:00Z 2019-01-01T00:00:00+0430 2019-01-01T00:00:00+04:30 }\if{html}{\out{
}} This function defaults to parsing the first of these formats by using a format string of \code{"\%Y-\%m-\%dT\%H:\%M:\%SZ"}. If your date-time strings use offsets from UTC rather than \code{"Z"}, then set \code{offset} to one of the following: \itemize{ \item \code{"\%z"} if the offset is of the form \code{"+0430"}. \item \code{"\%Ez"} if the offset is of the form \code{"+04:30"}. } The RFC 3339 standard allows for replacing the \code{"T"} with a \code{"t"} or a space (\code{" "}). Set \code{separator} to adjust this as needed. For this function, the \code{precision} must be at least \code{"second"}. } } \details{ If your date-time strings contain a full time zone name and a UTC offset, use \code{\link[=zoned_time_parse_complete]{zoned_time_parse_complete()}}. If they contain a time zone abbreviation, use \code{\link[=zoned_time_parse_abbrev]{zoned_time_parse_abbrev()}}. If your date-time strings don't contain an offset from UTC and you aren't sure if they should be treated as UTC or not, you might consider using \code{\link[=naive_time_parse]{naive_time_parse()}}, since the resulting naive-time doesn't come with an assumption of a UTC time zone. } \section{Full Precision Parsing}{ It is highly recommended to parse all of the information in the date-time string into a type at least as precise as the string. For example, if your string has fractional seconds, but you only require seconds, specify a sub-second \code{precision}, then round to seconds manually using whatever convention is appropriate for your use case. Parsing such a string directly into a second precision result is ambiguous and undefined, and is unlikely to work as you might expect. } \examples{ sys_time_parse("2020-01-01T05:06:07") # Day precision sys_time_parse("2020-01-01", precision = "day") # Nanosecond precision, but using a day based format sys_time_parse("2020-01-01", format = "\%Y-\%m-\%d", precision = "nanosecond") # Multiple format strings are allowed for heterogeneous times sys_time_parse( c("2019-01-01", "2019/1/1"), format = c("\%Y/\%m/\%d", "\%Y-\%m-\%d"), precision = "day" ) # The `\%z` command shifts the date-time by subtracting the UTC offset so # that the returned sys-time can be interpreted as UTC sys_time_parse( "2020-01-01 02:00:00 -0400", format = "\%Y-\%m-\%d \%H:\%M:\%S \%z" ) # Remember that the `\%Z` command is ignored entirely! sys_time_parse("2020-01-01 America/New_York", format = "\%Y-\%m-\%d \%Z") # --------------------------------------------------------------------------- # RFC 3339 # Typical UTC format x <- "2019-01-01T00:01:02Z" sys_time_parse_RFC_3339(x) # With a UTC offset containing a `:` x <- "2019-01-01T00:01:02+02:30" sys_time_parse_RFC_3339(x, offset = "\%Ez") # With a space between the date and time and no `:` in the offset x <- "2019-01-01 00:01:02+0230" sys_time_parse_RFC_3339(x, separator = " ", offset = "\%z") } clock/man/is_zoned_time.Rd0000644000176200001440000000077014017505732015247 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/zoned-time.R \name{is_zoned_time} \alias{is_zoned_time} \title{Is \code{x} a zoned-time?} \usage{ is_zoned_time(x) } \arguments{ \item{x}{\verb{[object]} An object.} } \value{ \code{TRUE} if \code{x} inherits from \code{"clock_zoned_time"}, otherwise \code{FALSE}. } \description{ This function determines if the input is a zoned-time object. } \examples{ is_zoned_time(1) is_zoned_time(zoned_time_now("America/New_York")) } clock/man/seq.clock_year_week_day.Rd0000644000176200001440000000417314422221153017162 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/week-year-week-day.R \name{seq.clock_year_week_day} \alias{seq.clock_year_week_day} \title{Sequences: year-week-day} \usage{ \method{seq}{clock_year_week_day}(from, to = NULL, by = NULL, length.out = NULL, along.with = NULL, ...) } \arguments{ \item{from}{\verb{[clock_year_week_day(1)]} A \code{"year"} precision year-week-day to start the sequence from. \code{from} is always included in the result.} \item{to}{\verb{[clock_year_week_day(1) / NULL]} A \code{"year"} precision year-week-day to stop the sequence at. \code{to} is cast to the type of \code{from}. \code{to} is only included in the result if the resulting sequence divides the distance between \code{from} and \code{to} exactly.} \item{by}{\verb{[integer(1) / clock_duration(1) / NULL]} The unit to increment the sequence by. If \code{by} is an integer, it is transformed into a duration with the precision of \code{from}. If \code{by} is a duration, it is cast to the type of \code{from}.} \item{length.out}{\verb{[positive integer(1) / NULL]} The length of the resulting sequence. If specified, \code{along.with} must be \code{NULL}.} \item{along.with}{\verb{[vector / NULL]} A vector who's length determines the length of the resulting sequence. Equivalent to \code{length.out = vec_size(along.with)}. If specified, \code{length.out} must be \code{NULL}.} \item{...}{These dots are for future extensions and must be empty.} } \value{ A sequence with the type of \code{from}. } \description{ This is a year-week-day method for the \code{\link[=seq]{seq()}} generic. Sequences can only be generated for \code{"year"} precision year-week-day vectors. If you need to generate week-based sequences, you'll have to convert to a time point first. When calling \code{seq()}, exactly two of the following must be specified: \itemize{ \item \code{to} \item \code{by} \item Either \code{length.out} or \code{along.with} } } \examples{ # Yearly sequence x <- seq(year_week_day(2020), year_week_day(2026), by = 2) x # Which we can then set the week of. # Some years have 53 weeks, some have 52. set_week(x, "last") } clock/man/iso-year-week-day-arithmetic.Rd0000644000176200001440000000313414416031540017771 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/iso-year-week-day.R \name{iso-year-week-day-arithmetic} \alias{iso-year-week-day-arithmetic} \alias{add_years.clock_iso_year_week_day} \title{Arithmetic: iso-year-week-day} \usage{ \method{add_years}{clock_iso_year_week_day}(x, n, ...) } \arguments{ \item{x}{\verb{[clock_iso_year_week_day]} A iso-year-week-day vector.} \item{n}{\verb{[integer / clock_duration]} An integer vector to be converted to a duration, or a duration corresponding to the arithmetic function being used. This corresponds to the number of duration units to add. \code{n} may be negative to subtract units of duration.} \item{...}{These dots are for future extensions and must be empty.} } \value{ \code{x} after performing the arithmetic. } \description{ These are iso-year-week-day methods for the \link[=clock-arithmetic]{arithmetic generics}. \itemize{ \item \code{add_years()} } You cannot add weeks or days to an iso-year-week-day calendar. Adding days is much more efficiently done by converting to a time point first by using \code{\link[=as_naive_time]{as_naive_time()}} or \code{\link[=as_sys_time]{as_sys_time()}}. Adding weeks is equally as efficient as adding 7 days. Additionally, adding weeks to an invalid iso-year-week object containing \code{iso_year_week_day(2019, 53)} would be undefined, as the 53rd ISO week of 2019 doesn't exist to begin with. } \details{ \code{x} and \code{n} are recycled against each other using \link[vctrs:vector_recycling_rules]{tidyverse recycling rules}. } \examples{ x <- iso_year_week_day(2019, 1, 1) add_years(x, 1:2) } clock/man/calendar_group.Rd0000644000176200001440000000314414422221153015372 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/calendar.R \name{calendar_group} \alias{calendar_group} \title{Group calendar components} \usage{ calendar_group(x, precision, ..., n = 1L) } \arguments{ \item{x}{\verb{[calendar]} A calendar vector.} \item{precision}{\verb{[character(1)]} A precision. Allowed precisions are dependent on the calendar used.} \item{...}{These dots are for future extensions and must be empty.} \item{n}{\verb{[positive integer(1)]} A single positive integer specifying a multiple of \code{precision} to use.} } \value{ \code{x} grouped at the specified \code{precision}. } \description{ \code{calendar_group()} groups at a multiple of the specified precision. Grouping alters the value of a single component (i.e. the month component if grouping by month). Components that are more precise than the precision being grouped at are dropped altogether (i.e. the day component is dropped if grouping by month). Each calendar has its own help page describing the grouping process in more detail: \itemize{ \item \link[=year-month-day-group]{year-month-day} \item \link[=year-month-weekday-group]{year-month-weekday} \item \link[=year-week-day-group]{year-week-day} \item \link[=iso-year-week-day-group]{iso-year-week-day} \item \link[=year-quarter-day-group]{year-quarter-day} \item \link[=year-day-group]{year-day} } } \examples{ # See the calendar specific help pages for more examples x <- year_month_day(2019, c(1, 1, 2, 2, 3, 3, 4, 4), 1:8) x # Group by two months calendar_group(x, "month", n = 2) # Group by two days of the month calendar_group(x, "day", n = 2) } clock/DESCRIPTION0000644000176200001440000000266214430501615013060 0ustar liggesusersPackage: clock Title: Date-Time Types and Tools Version: 0.7.0 Authors@R: c( person("Davis", "Vaughan", , "davis@posit.co", role = c("aut", "cre")), person("Posit Software, PBC", role = c("cph", "fnd")) ) Description: Provides a comprehensive library for date-time manipulations using a new family of orthogonal date-time classes (durations, time points, zoned-times, and calendars) that partition responsibilities so that the complexities of time zones are only considered when they are really needed. Capabilities include: date-time parsing, formatting, arithmetic, extraction and updating of components, and rounding. License: MIT + file LICENSE URL: https://clock.r-lib.org, https://github.com/r-lib/clock BugReports: https://github.com/r-lib/clock/issues Depends: R (>= 3.5.0) Imports: cli (>= 3.6.1), lifecycle (>= 1.0.3), rlang (>= 1.1.0), tzdb (>= 0.4.0), vctrs (>= 0.6.1) Suggests: covr, knitr, magrittr, pillar, rmarkdown, slider (>= 0.3.0), testthat (>= 3.0.0), withr LinkingTo: cpp11 (>= 0.4.3), tzdb (>= 0.4.0) VignetteBuilder: knitr Config/Needs/website: lubridate, tidyverse/tidytemplate Config/testthat/edition: 3 Encoding: UTF-8 LazyData: true RoxygenNote: 7.2.3 NeedsCompilation: yes Packaged: 2023-05-15 17:58:13 UTC; davis Author: Davis Vaughan [aut, cre], Posit Software, PBC [cph, fnd] Maintainer: Davis Vaughan Repository: CRAN Date/Publication: 2023-05-15 19:10:05 UTC clock/build/0000755000176200001440000000000014430471264012451 5ustar liggesusersclock/build/vignette.rds0000644000176200001440000000042414430471264015010 0ustar liggesusersQN0t4R: H% ,PXXuvU^;j*$?>;=!PRJh`e$$1q!bJhv'RC!j05l)3k+ȥr;Q/&wQrLB?&eo-ZB w=PiFov~iȓ gekzD? &clock/tests/0000755000176200001440000000000014002033135012476 5ustar liggesusersclock/tests/testthat/0000755000176200001440000000000014430501615014346 5ustar liggesusersclock/tests/testthat/test-arithmetic.R0000644000176200001440000000144214423730227017605 0ustar liggesusers# ------------------------------------------------------------------------------ # add_*() test_that("arithmetic helpers throw default error", { x <- 1 expect_snapshot(error = TRUE, { add_years(x) }) expect_snapshot(error = TRUE, { add_quarters(x) }) expect_snapshot(error = TRUE, { add_months(x) }) expect_snapshot(error = TRUE, { add_weeks(x) }) expect_snapshot(error = TRUE, { add_days(x) }) expect_snapshot(error = TRUE, { add_hours(x) }) expect_snapshot(error = TRUE, { add_minutes(x) }) expect_snapshot(error = TRUE, { add_seconds(x) }) expect_snapshot(error = TRUE, { add_milliseconds(x) }) expect_snapshot(error = TRUE, { add_microseconds(x) }) expect_snapshot(error = TRUE, { add_nanoseconds(x) }) }) clock/tests/testthat/test-weekday.R0000644000176200001440000001267114423730227017113 0ustar liggesusers# ------------------------------------------------------------------------------ # format() test_that("format - can format() a weekday", { expect_snapshot(format(weekday(1:7))) }) test_that("format - can use full names", { expect_snapshot(format(weekday(1:7), abbreviate = FALSE)) }) test_that("format - can use a different locale", { expect_snapshot(format(weekday(1:7), labels = "fr", abbreviate = FALSE)) }) test_that("format - `labels` is validated", { expect_snapshot(error = TRUE, format(weekday(1), labels = 1)) }) test_that("format - `abbreviate` is validated", { expect_snapshot(error = TRUE, format(weekday(1), abbreviate = "foo")) expect_snapshot(error = TRUE, format(weekday(1), abbreviate = 1)) expect_snapshot(error = TRUE, format(weekday(1), abbreviate = c(TRUE, FALSE))) }) # ------------------------------------------------------------------------------ # as.character() test_that("as.character() works", { x <- weekday(1:7) expect_identical(as.character(x), format(x)) }) test_that("as.character() works with NA", { expect_identical(as.character(weekday(NA)), NA_character_) }) # ------------------------------------------------------------------------------ # weekday_code() test_that("can get the western code", { expect_identical(weekday_code(weekday(1:7)), 1:7) }) test_that("can get the ISO code", { expect_identical(weekday_code(weekday(1:7), encoding = "iso"), c(7L, 1:6)) }) test_that("names are not retained", { expect_named(weekday_code(c(foo = weekday(1))), NULL) }) test_that("NA passes through", { expect_identical(weekday_code(weekday(NA)), NA_integer_) }) test_that("validates `x`", { expect_snapshot(error = TRUE, weekday_code(1)) }) test_that("weekday_code - `encoding` is validated", { expect_snapshot(error = TRUE, weekday_code(weekday(1), encoding = "foo")) expect_snapshot(error = TRUE, weekday_code(weekday(1), encoding = 1)) }) # ------------------------------------------------------------------------------ # weekday_factor() test_that("can make a weekday factor sunday->saturday", { order <- 7:1 x <- weekday(order) x <- weekday_factor(x) levels <- clock_labels_lookup("en")$weekday_abbrev data <- levels[order] expect_s3_class(x, "ordered") expect_identical(as.character(x), data) expect_identical(levels(x), levels) }) test_that("can make a weekday factor monday->sunday", { order <- 7:1 x <- weekday(order) x <- weekday_factor(x, encoding = "iso") levels <- clock_labels_lookup("en")$weekday_abbrev data <- levels[order] levels <- levels[c(2:7, 1L)] expect_s3_class(x, "ordered") expect_identical(as.character(x), data) expect_identical(levels(x), levels) }) test_that("can make a weekday factor with full names", { x <- weekday(7:1) x <- weekday_factor(x, abbreviate = FALSE) levels <- clock_labels_lookup("en")$weekday expect_identical(levels(x), levels) }) test_that("can make a weekday factor with alternate labels", { x <- weekday(7:1) x <- weekday_factor(x, labels = "fr") levels <- clock_labels_lookup("fr")$weekday_abbrev expect_identical(levels(x), levels) }) test_that("`x` is validated", { expect_snapshot(error = TRUE, weekday_factor(1)) }) test_that("`labels` is validated", { expect_snapshot(error = TRUE, weekday_factor(weekday(1), labels = 1)) }) test_that("`encoding` is validated", { expect_snapshot(error = TRUE, weekday_factor(weekday(1), encoding = "foo")) expect_snapshot(error = TRUE, weekday_factor(weekday(1), encoding = 1)) }) test_that("`abbreviate` is validated", { expect_snapshot(error = TRUE, weekday_factor(weekday(1), abbreviate = "foo")) expect_snapshot(error = TRUE, weekday_factor(weekday(1), abbreviate = 1)) expect_snapshot(error = TRUE, weekday_factor(weekday(1), abbreviate = c(TRUE, FALSE))) }) # ------------------------------------------------------------------------------ # add_days() test_that("can add days with circular arithmetic", { expect_identical(add_days(weekday(6:7), 1), weekday(c(7,1))) }) test_that("can add days with NA", { expect_identical( add_days(weekday(c(1, 2, NA)), c(NA, 1, 2)), weekday(c(NA, 3, NA)) ) }) # ------------------------------------------------------------------------------ # vec_arith() test_that("can subtract two weekdays with circular arithmetic", { expect_identical( weekday(3) - weekday(c(1, 7)), duration_days(c(2, 3)) ) }) test_that("subtraction respects NA", { expect_identical( weekday(c(1, NA)) - weekday(c(NA, 2)), duration_days(c(NA, NA)) ) }) # ------------------------------------------------------------------------------ # vec_proxy_compare() test_that("can't compare or order weekdays (#153)", { expect_snapshot(error = TRUE, weekday(1) < weekday(2)) expect_snapshot(error = TRUE, min(weekday(1))) expect_snapshot(error = TRUE, xtfrm(weekday(1:2))) expect_snapshot(error = TRUE, vec_order(weekday(1:2))) }) # ------------------------------------------------------------------------------ # vec_ptype() test_that("ptype is correct", { expect_identical(vec_ptype(weekday(1:7)), weekday(integer())) }) # ------------------------------------------------------------------------------ # vec_math() test_that("is.nan() works", { x <- weekday(c(1, NA)) expect_identical(is.nan(x), c(FALSE, FALSE)) }) test_that("is.finite() works", { x <- weekday(c(1, NA)) expect_identical(is.finite(x), c(TRUE, FALSE)) }) test_that("is.infinite() works", { x <- weekday(c(1, NA)) expect_identical(is.infinite(x), c(FALSE, FALSE)) }) clock/tests/testthat/test-clock-labels.R0000644000176200001440000000441014423730227020005 0ustar liggesusers# ------------------------------------------------------------------------------ # clock_labels() test_that("can make custom labels", { months <- month.name weekdays <- c("Su", "Mo", "Tu", "We", "Th", "Fr", "Sa") am_pm <- c("A", "P") labels <- clock_labels(month = month.name, weekday = weekdays, am_pm = am_pm) expect_s3_class(labels, "clock_labels") expect_snapshot(labels) }) test_that("input is validated", { months <- month.name weekdays <- c("Su", "Mo", "Tu", "We", "Th", "Fr", "Sa") am_pm <- c("A", "P") expect_snapshot(error = TRUE, clock_labels(1)) expect_snapshot(error = TRUE, clock_labels("x")) expect_snapshot(error = TRUE, clock_labels(months, 1)) expect_snapshot(error = TRUE, clock_labels(months, "x")) expect_snapshot(error = TRUE, clock_labels(months, months, 1)) expect_snapshot(error = TRUE, clock_labels(months, months, "x")) expect_snapshot(error = TRUE, clock_labels(months, months, weekdays, 1)) expect_snapshot(error = TRUE, clock_labels(months, months, weekdays, "x")) expect_snapshot(error = TRUE, clock_labels(months, months, weekdays, weekdays, 1)) expect_snapshot(error = TRUE, clock_labels(months, months, weekdays, weekdays, "x")) }) test_that("custom labels are converted to UTF-8 upon entry", { labels <- clock_labels_lookup("fr") month <- iconv(labels$month, from = "UTF-8", to = "latin1") labels <- clock_labels(month, month, labels$weekday, labels$weekday, labels$am_pm) # French February can be marked as latin1 before <- month[2] after <- labels$month[2] expect_identical(Encoding(before), "latin1") expect_identical(Encoding(after), "UTF-8") }) # ------------------------------------------------------------------------------ # clock_labels_lookup() test_that("can lookup a language", { labels <- clock_labels_lookup("fr") expect_s3_class(labels, "clock_labels") expect_snapshot(labels) }) test_that("must be a valid language code", { expect_snapshot(error = TRUE, clock_labels_lookup(1)) expect_snapshot(error = TRUE, clock_labels_lookup("foo")) }) # ------------------------------------------------------------------------------ # clock_labels_languages() test_that("can list all the languages", { langs <- clock_labels_languages() expect_length(langs, 196) expect_snapshot(langs) }) clock/tests/testthat/test-calendar.R0000644000176200001440000003160114425175541017231 0ustar liggesusers# ------------------------------------------------------------------------------ # print() / obj_print_data() / obj_print_footer() test_that("normal print method works", { x <- year_month_day(2019, 1:5) expect_snapshot(x) }) test_that("can limit with `max`", { x <- year_month_day(2019, 1:5) expect_snapshot(print(x, max = 2)) expect_snapshot(print(x, max = 4)) # no footer if length >= max expect_snapshot(print(x, max = 5)) expect_snapshot(print(x, max = 6)) }) test_that("`max` defaults to `getOption('max.print')` but can be overridden", { local_options(max.print = 3) x <- year_month_day(2019, 1:5) expect_snapshot(x) expect_snapshot(print(x, max = 4)) expect_snapshot(print(x, max = 5)) }) test_that("`max` is validated", { x <- year_month_day(2019) expect_snapshot(error = TRUE, print(x, max = -1)) expect_snapshot(error = TRUE, print(x, max = c(1, 2))) expect_snapshot(error = TRUE, print(x, max = NA_integer_)) expect_snapshot(error = TRUE, print(x, max = "foo")) }) # ------------------------------------------------------------------------------ # calendar_group() test_that("group: `precision` is validated", { expect_snapshot(error = TRUE, calendar_group(year_month_day(2019), "foo")) }) test_that("group: `precision` must be calendar specific", { expect_snapshot(error = TRUE, calendar_group(year_month_day(2019), "quarter")) }) test_that("group: `precision` can't be wider than `x`", { expect_snapshot(error = TRUE, calendar_group(year_month_day(2019, 1, 1), "second")) }) test_that("group: can't group a subsecond precision `x` at another subsecond precision", { x <- calendar_widen(year_month_day(2019), "nanosecond") expect_snapshot(error = TRUE, calendar_group(x, "microsecond")) }) test_that("group: can group subsecond precision at the same subsecond precision", { x <- year_month_day(2019, 1, 1, 1, 1, 1, 0:1, subsecond_precision = "millisecond") expect_identical(calendar_group(x, "millisecond", n = 2L), x[c(1, 1)]) }) # ------------------------------------------------------------------------------ # calendar_narrow() test_that("narrow: `precision` is validated", { expect_snapshot(error = TRUE, calendar_narrow(year_month_day(2019), "foo")) }) test_that("narrow: `precision` must be calendar specific", { expect_snapshot(error = TRUE, calendar_narrow(year_month_day(2019), "quarter")) }) test_that("narrow: `precision` can't be wider than `x`", { expect_snapshot(error = TRUE, calendar_narrow(year_month_day(2019, 1, 1), "second")) }) test_that("narrow: can't narrow a subsecond precision `x` to another subsecond precision", { x <- calendar_widen(year_month_day(2019), "nanosecond") expect_snapshot(error = TRUE, calendar_narrow(x, "microsecond")) }) test_that("narrow: can narrow subsecond precision to same subsecond precision", { x <- year_month_day(2019, 1, 1, 1, 1, 1, 1, subsecond_precision = "millisecond") expect_identical(calendar_narrow(x, "millisecond"), x) }) # ------------------------------------------------------------------------------ # calendar_widen() test_that("widen: `precision` is validated", { expect_snapshot(error = TRUE, calendar_widen(year_month_day(2019), "foo")) }) test_that("widen: `precision` must be calendar specific", { expect_snapshot(error = TRUE, calendar_widen(year_month_day(2019), "quarter")) }) test_that("widen: `precision` can't be narrower than `x`", { expect_snapshot(error = TRUE, calendar_widen(year_month_day(2019, 1, 1), "month")) }) test_that("widen: can't widen a subsecond precision `x` to another subsecond precision", { x <- calendar_widen(year_month_day(2019), "millisecond") expect_snapshot(error = TRUE, calendar_widen(x, "microsecond")) }) test_that("widen: can widen subsecond precision to the same subsecond precision", { x <- year_month_day(2019, 1, 1, 1, 1, 1, 1, subsecond_precision = "millisecond") expect_identical(calendar_widen(x, "millisecond"), x) }) # ------------------------------------------------------------------------------ # calendar_start() test_that("start: `x` is validated", { expect_snapshot(error = TRUE, calendar_start(1)) }) test_that("start: `precision` is validated", { expect_snapshot(error = TRUE, calendar_start(year_month_day(2019), "foo")) expect_snapshot(error = TRUE, calendar_start(year_month_day(2019), 1)) }) test_that("start: errors on unsupported precision", { expect_snapshot(error = TRUE, calendar_start(year_month_day(2019, 1), "quarter")) }) test_that("start: `precision` can't be more precise than `x`", { expect_snapshot(error = TRUE, calendar_start(year_month_day(2019), "month")) }) test_that("start: can't mix different subsecond precisions", { x <- year_month_day(2019, 1, 1, 1, 1, 1, 1, subsecond_precision = "microsecond") expect_snapshot(error = TRUE, calendar_start(x, "millisecond")) }) test_that("start: can use same subsecond precision", { x <- year_month_day(2019, 1, 1, 1, 1, 1, 1, subsecond_precision = "microsecond") expect_identical(calendar_start(x, "microsecond"), x) }) test_that("start: can compute day start", { x <- year_month_day(2019, 2, 2) expect_identical(calendar_start(x, "day"), x) x <- year_month_day(2019, 2, 2, 2, 2, 2, 2, subsecond_precision = "nanosecond") expect <- year_month_day(2019, 2, 2, 0, 0, 0, 0, subsecond_precision = "nanosecond") expect_identical(calendar_start(x, "day"), expect) }) test_that("start: can compute hour start", { x <- year_month_day(2019, 2, 2, 2) expect_identical(calendar_start(x, "hour"), x) x <- year_month_day(2019, 2, 2, 2, 2, 2, 2, subsecond_precision = "nanosecond") expect <- year_month_day(2019, 2, 2, 2, 0, 0, 0, subsecond_precision = "nanosecond") expect_identical(calendar_start(x, "hour"), expect) }) test_that("start: can compute minute start", { x <- year_month_day(2019, 2, 2, 2, 2) expect_identical(calendar_start(x, "minute"), x) x <- year_month_day(2019, 2, 2, 2, 2, 2, 2, subsecond_precision = "nanosecond") expect <- year_month_day(2019, 2, 2, 2, 2, 0, 0, subsecond_precision = "nanosecond") expect_identical(calendar_start(x, "minute"), expect) }) test_that("start: can compute second start", { x <- year_month_day(2019, 2, 2, 2, 2, 2) expect_identical(calendar_start(x, "second"), x) x <- year_month_day(2019, 2, 2, 2, 2, 2, 2, subsecond_precision = "nanosecond") expect <- year_month_day(2019, 2, 2, 2, 2, 2, 0, subsecond_precision = "nanosecond") expect_identical(calendar_start(x, "second"), expect) }) test_that("start: invalid dates are adjusted", { x <- year_month_day(2019, 2, 31, 3) expect_identical(calendar_start(x, "year"), year_month_day(2019, 1, 1, 0)) expect_identical(calendar_start(x, "day"), year_month_day(2019, 2, 31, 0)) }) # ------------------------------------------------------------------------------ # calendar_end() test_that("end: `x` is validated", { expect_snapshot(error = TRUE, calendar_end(1)) }) test_that("end: `precision` is validated", { expect_snapshot(error = TRUE, calendar_end(year_month_day(2019), "foo")) expect_snapshot(error = TRUE, calendar_end(year_month_day(2019), 1)) }) test_that("end: errors on unsupported precision", { expect_snapshot(error = TRUE, calendar_end(year_month_day(2019, 1), "quarter")) }) test_that("end: `precision` can't be more precise than `x`", { expect_snapshot(error = TRUE, calendar_end(year_month_day(2019), "month")) }) test_that("end: can't mix different subsecond precisions", { x <- year_month_day(2019, 1, 1, 1, 1, 1, 1, subsecond_precision = "microsecond") expect_snapshot(error = TRUE, calendar_end(x, "millisecond")) }) test_that("end: can use same subsecond precision", { x <- year_month_day(2019, 1, 1, 1, 1, 1, 1, subsecond_precision = "microsecond") expect_identical(calendar_end(x, "microsecond"), x) }) test_that("end: can compute day end", { x <- year_month_day(2019, 2, 2) expect_identical(calendar_end(x, "day"), x) x <- year_month_day(2019, 2, 2, 2, 2, 2, 2, subsecond_precision = "nanosecond") expect <- year_month_day(2019, 2, 2, 23, 59, 59, 999999999L, subsecond_precision = "nanosecond") expect_identical(calendar_end(x, "day"), expect) }) test_that("end: can compute hour end", { x <- year_month_day(2019, 2, 2, 2) expect_identical(calendar_end(x, "hour"), x) x <- year_month_day(2019, 2, 2, 2, 2, 2, 2, subsecond_precision = "nanosecond") expect <- year_month_day(2019, 2, 2, 2, 59, 59, 999999999L, subsecond_precision = "nanosecond") expect_identical(calendar_end(x, "hour"), expect) }) test_that("end: can compute minute end", { x <- year_month_day(2019, 2, 2, 2, 2) expect_identical(calendar_end(x, "minute"), x) x <- year_month_day(2019, 2, 2, 2, 2, 2, 2, subsecond_precision = "nanosecond") expect <- year_month_day(2019, 2, 2, 2, 2, 59, 999999999L, subsecond_precision = "nanosecond") expect_identical(calendar_end(x, "minute"), expect) }) test_that("end: can compute second end", { x <- year_month_day(2019, 2, 2, 2, 2, 2) expect_identical(calendar_end(x, "second"), x) x <- year_month_day(2019, 2, 2, 2, 2, 2, 2, subsecond_precision = "nanosecond") expect <- year_month_day(2019, 2, 2, 2, 2, 2, 999999999L, subsecond_precision = "nanosecond") expect_identical(calendar_end(x, "second"), expect) }) test_that("end: invalid dates are adjusted", { x <- year_month_day(2019, 2, 31, 3) expect_identical(calendar_end(x, "year"), year_month_day(2019, 12, 31, 23)) expect_identical(calendar_end(x, "month"), year_month_day(2019, 2, 28, 23)) expect_identical(calendar_end(x, "day"), year_month_day(2019, 2, 31, 23)) }) # ------------------------------------------------------------------------------ # calendar_count_between() test_that("`n` gets used", { x <- year_month_day(2019, 1) y <- year_month_day(2019, 7) expect_identical(calendar_count_between(x, y, "month", n = 2), 3L) }) test_that("`end` must be a calendar", { x <- year_month_day(2019) expect_snapshot((expect_error(calendar_count_between(x, 1, "year")))) }) test_that("can't count with a precision finer than the calendar precision", { x <- year_month_day(2019) expect_snapshot((expect_error(calendar_count_between(x, x, "month")))) }) test_that("`n` is validated", { x <- year_month_day(2019) expect_snapshot({ (expect_error(calendar_count_between(x, x, "year", n = NA_integer_))) (expect_error(calendar_count_between(x, x, "year", n = -1))) (expect_error(calendar_count_between(x, x, "year", n = 1.5))) (expect_error(calendar_count_between(x, x, "year", n = "x"))) (expect_error(calendar_count_between(x, x, "year", n = c(1L, 2L)))) }) }) # ------------------------------------------------------------------------------ # calendar_spanning_seq() test_that("generates the regular sequence along the full span", { x <- year_month_day(c(2019, 2022, 2020), c(2, 1, 3)) expect_identical( calendar_spanning_seq(x), seq(year_month_day(2019, 2), year_month_day(2022, 1), by = 1) ) }) test_that("missing values are removed", { x <- year_month_day(c(1, NA, 0, 2)) expect_identical(calendar_spanning_seq(x), year_month_day(0:2)) x <- year_month_day(c(NA, NA)) expect_identical(calendar_spanning_seq(x), year_month_day(integer())) }) test_that("works with empty vectors", { x <- year_month_day(integer()) expect_identical(calendar_spanning_seq(x), x) }) test_that("validates the input", { expect_snapshot(error = TRUE, { calendar_spanning_seq(1) }) }) test_that("the input must be at a precision allowed by `seq()`", { expect_snapshot(error = TRUE, { calendar_spanning_seq(year_month_day(2019, 1, 2)) }) }) test_that("errors on types that don't support min/max calls", { # This is fine x <- year_month_weekday(2019, c(1, 4)) expect_identical(calendar_spanning_seq(x), year_month_weekday(2019, 1:4)) # But this is invalid x <- year_month_weekday(2019, 1, 1, 1) expect_snapshot(error = TRUE, { calendar_spanning_seq(x) }) }) # ------------------------------------------------------------------------------ # calendar_precision() test_that("precision: can get the precision", { expect_identical(calendar_precision(year_month_day(2019, 1)), "month") expect_identical(calendar_precision(year_day(2019, 100)), "day") expect_identical(calendar_precision(year_month_day(2019, 1, 1, 1, 1, 1, 1, subsecond_precision = "nanosecond")), "nanosecond") }) test_that("precision: can only be called on calendars", { expect_snapshot(error = TRUE, calendar_precision(sys_days(0))) }) # ------------------------------------------------------------------------------ # add_*() test_that("addition helpers throw error with advice", { x <- year_month_day(2019) expect_snapshot(error = TRUE, { add_weeks(x) }) expect_snapshot(error = TRUE, { add_days(x) }) expect_snapshot(error = TRUE, { add_hours(x) }) expect_snapshot(error = TRUE, { add_minutes(x) }) expect_snapshot(error = TRUE, { add_seconds(x) }) expect_snapshot(error = TRUE, { add_milliseconds(x) }) expect_snapshot(error = TRUE, { add_microseconds(x) }) expect_snapshot(error = TRUE, { add_nanoseconds(x) }) }) clock/tests/testthat/test-week-year-week-day.R0000644000176200001440000006075714427233200021060 0ustar liggesusers# ------------------------------------------------------------------------------ # year_week_day() test_that("helper can create different precisions", { x <- year_week_day(2019, 1:2) expect_identical(get_year(x), c(2019L, 2019L)) expect_identical(get_week(x), 1:2) x <- year_week_day(2019, 1:2, 3) expect_identical(get_day(x), c(3L, 3L)) }) test_that("can create subsecond precision calendars", { x <- year_week_day(2019, 1, 1, 0, 0, 0, 1, subsecond_precision = "millisecond") expect_identical(get_millisecond(x), 1L) x <- year_week_day(2019, 1, 1, 0, 0, 0, 1, subsecond_precision = "microsecond") expect_identical(get_microsecond(x), 1L) x <- year_week_day(2019, 1, 1, 0, 0, 0, 1, subsecond_precision = "nanosecond") expect_identical(get_nanosecond(x), 1L) }) test_that("validates value ranges", { expect_snapshot(error = TRUE, year_week_day(50000)) expect_snapshot(error = TRUE, year_week_day(2020, 54)) expect_snapshot(error = TRUE, year_week_day(2020, 1, 8)) expect_snapshot(error = TRUE, year_week_day(2020, 1, 1, 24)) expect_snapshot(error = TRUE, year_week_day(2020, 1, 1, 1, 60)) expect_snapshot(error = TRUE, year_week_day(2020, 1, 1, 1, 1, 60)) expect_snapshot(error = TRUE, year_week_day(2020, 1, 1, 1, 1, 1, 1000, subsecond_precision = "millisecond")) expect_snapshot(error = TRUE, year_week_day(2020, 1, 1, 1, 1, 1, 1000000, subsecond_precision = "microsecond")) expect_snapshot(error = TRUE, year_week_day(2020, 1, 1, 1, 1, 1, 1000000000, subsecond_precision = "nanosecond")) }) test_that("can get the last week of the year", { x <- year_week_day(2024:2026, "last") expect_identical(get_week(x), c(52L, 53L, 52L)) x <- year_week_day(2024:2026, "last", start = clock_weekdays$monday) expect_identical(get_week(x), c(52L, 52L, 53L)) }) test_that("`NA` propagates through 'last'", { x <- year_week_day(c(2019, NA)) x <- set_week(x, "last") expect_identical(get_week(x), c(52L, NA)) }) test_that("ignores values past first `NULL`", { expect_identical(year_week_day(2019, day = 2), year_week_day(2019)) }) test_that("NA values propagate", { x <- year_week_day(2019, 1:3, c(NA, 2, 3), c(3, 4, NA)) expect_identical(is.na(x), c(TRUE, FALSE, TRUE)) }) # ------------------------------------------------------------------------------ # vec_ptype() test_that("ptype is correct", { base <- year_week_day(1) ptype <- year_week_day(integer()) for (precision in precision_names()) { if (precision == "quarter" || precision == "month") { next } x <- calendar_widen(base, precision) expect <- calendar_widen(ptype, precision) expect_identical(vec_ptype(x), expect) } }) # ------------------------------------------------------------------------------ # vec_proxy() / vec_restore() test_that("proxy is a data frame", { expect_identical(vec_proxy(year_week_day(2019)), data_frame(year = 2019L)) expect_identical(vec_proxy(year_week_day(2019, 1)), data_frame(year = 2019L, week = 1L)) }) test_that("proxy has names on `year`", { x <- set_names(year_week_day(2019, 1), "nm") year <- vec_proxy(x)$year expect_named(year, "nm") }) test_that("restore works", { to <- year_week_day(2019, 1:5) proxy <- vec_slice(vec_proxy(to), 1:2) expect_identical(vec_restore(proxy, to), year_week_day(2019, 1:2)) }) # ------------------------------------------------------------------------------ # vec_ptype_full() test_that("full ptype is correct", { expect_snapshot_output(vec_ptype_full(year_week_day(2019))) expect_snapshot_output(vec_ptype_full(year_week_day(2019, start = 2))) expect_snapshot_output(vec_ptype_full(year_week_day(2019, 1, 1))) expect_snapshot_output(vec_ptype_full(year_week_day(2019, 1, 1, 1, 1, 1, 1, subsecond_precision = "nanosecond"))) expect_snapshot_output(vec_ptype_full(year_week_day(2019, 53))) }) # ------------------------------------------------------------------------------ # vec_ptype_abbr() test_that("abbreviated ptype is correct", { expect_snapshot_output(vec_ptype_abbr(year_week_day(2019))) expect_snapshot_output(vec_ptype_abbr(year_week_day(2019, start = 2))) expect_snapshot_output(vec_ptype_abbr(year_week_day(2019, 1, 1))) expect_snapshot_output(vec_ptype_abbr(year_week_day(2019, 1, 1, 1, 1, 1, 1, subsecond_precision = "nanosecond"))) expect_snapshot_output(vec_ptype_abbr(year_week_day(2019, 53))) }) # ------------------------------------------------------------------------------ # set_*() test_that("setters work", { x <- year_week_day(1L) x <- set_year(x, 2L) expect_identical(get_year(x), 2L) x <- set_week(x, 1L) expect_identical(get_week(x), 1L) x <- set_day(x, 2L) expect_identical(get_day(x), 2L) x <- set_hour(x, 3L) expect_identical(get_hour(x), 3L) x <- set_minute(x, 4L) expect_identical(get_minute(x), 4L) x <- set_second(x, 5L) expect_identical(get_second(x), 5L) ms <- set_millisecond(x, 6L) expect_identical(get_millisecond(ms), 6L) us <- set_microsecond(x, 7L) expect_identical(get_microsecond(us), 7L) ns <- set_nanosecond(x, 8L) expect_identical(get_nanosecond(ns), 8L) }) test_that("setters propagate all missings", { x <- year_week_day(2019, c(1, NA, 3)) x <- set_day(x, c(NA, 2, 4)) expect_identical(vec_detect_missing(x), c(TRUE, TRUE, FALSE)) }) test_that("setters recycling works both ways", { x <- year_week_day(2019) x <- set_week(x, 1:2) expect_identical(x, year_week_day(2019, 1:2)) x <- set_day(x, 1) expect_identical(x, year_week_day(2019, 1:2, 1)) expect_snapshot(error = TRUE, { x <- year_week_day(1:2) y <- 1:3 set_week(x, y) }) }) test_that("setters require integer `value`", { x <- year_week_day(2019, 1, 2, 3, 4, 5) expect_snapshot(error = TRUE, { set_year(x, 1.5) }) expect_snapshot(error = TRUE, { set_week(x, 1.5) }) expect_snapshot(error = TRUE, { set_day(x, 1.5) }) expect_snapshot(error = TRUE, { set_hour(x, 1.5) }) expect_snapshot(error = TRUE, { set_minute(x, 1.5) }) expect_snapshot(error = TRUE, { set_second(x, 1.5) }) expect_snapshot(error = TRUE, { set_millisecond(x, 1.5) }) expect_snapshot(error = TRUE, { set_microsecond(x, 1.5) }) expect_snapshot(error = TRUE, { set_nanosecond(x, 1.5) }) }) test_that("setters check `value` range", { x <- year_week_day(2019, 1, 2, 3, 4, 5) expect_snapshot(error = TRUE, { set_year(x, 100000) }) expect_snapshot(error = TRUE, { set_week(x, 54) }) expect_snapshot(error = TRUE, { set_day(x, 8) }) expect_snapshot(error = TRUE, { set_hour(x, 24) }) expect_snapshot(error = TRUE, { set_minute(x, 60) }) expect_snapshot(error = TRUE, { set_second(x, 60) }) expect_snapshot(error = TRUE, { set_millisecond(x, -1) }) expect_snapshot(error = TRUE, { set_microsecond(x, -1) }) expect_snapshot(error = TRUE, { set_nanosecond(x, -1) }) }) test_that("setters require minimum precision", { expect_snapshot(error = TRUE, { set_day(year_week_day(year = 1), 1) }) expect_snapshot(error = TRUE, { set_hour(year_week_day(year = 1, week = 2), 1) }) expect_snapshot(error = TRUE, { set_minute(year_week_day(year = 1, week = 2, day = 3), 1) }) expect_snapshot(error = TRUE, { set_second(year_week_day(year = 1, week = 2, day = 3, hour = 4), 1) }) expect_snapshot(error = TRUE, { set_millisecond(year_week_day(year = 1, week = 2, day = 3, hour = 4, minute = 5), 1) }) expect_snapshot(error = TRUE, { set_microsecond(year_week_day(year = 1, week = 2, day = 3, hour = 4, minute = 5), 1) }) expect_snapshot(error = TRUE, { set_nanosecond(year_week_day(year = 1, week = 2, day = 3, hour = 4, minute = 5), 1) }) }) test_that("setters require correct subsecond precision", { expect_snapshot(error = TRUE, { set_millisecond(year_week_day(year = 1, week = 2, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "microsecond"), 1) }) expect_snapshot(error = TRUE, { set_millisecond(year_week_day(year = 1, week = 2, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "nanosecond"), 1) }) expect_snapshot(error = TRUE, { set_microsecond(year_week_day(year = 1, week = 2, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "millisecond"), 1) }) expect_snapshot(error = TRUE, { set_microsecond(year_week_day(year = 1, week = 2, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "nanosecond"), 1) }) expect_snapshot(error = TRUE, { set_nanosecond(year_week_day(year = 1, week = 2, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "millisecond"), 1) }) expect_snapshot(error = TRUE, { set_nanosecond(year_week_day(year = 1, week = 2, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "microsecond"), 1) }) }) test_that("setters retain names", { x <- year_week_day(2019) x <- set_names(x, "foo") expect_named(set_week(x, 2), "foo") }) test_that("setting with named `value` strips its names", { x <- year_week_day(2019) x <- set_week(x, set_names(1L, "x")) expect_named(field(x, "week"), NULL) }) # ------------------------------------------------------------------------------ # format() test_that("default formats are correct", { expect_snapshot(format(year_week_day(2019))) expect_snapshot(format(year_week_day(2019, 1))) expect_snapshot(format(year_week_day(2019, 1, 1, 1))) expect_snapshot(format(year_week_day(2019, 1, 1, 1, 2, 3, 50, subsecond_precision = "microsecond"))) }) # ------------------------------------------------------------------------------ # as.character() test_that("as.character() works", { x <- year_week_day(2019, 1) y <- year_week_day(2019, 1, 2) expect_identical(as.character(x), format(x)) expect_identical(as.character(y), format(y)) }) # ------------------------------------------------------------------------------ # calendar_narrow() test_that("can narrow to week", { x_expect <- year_week_day(2019, 2) x <- set_day(x_expect, 1) expect_identical(calendar_narrow(x, "week"), x_expect) expect_identical(calendar_narrow(x_expect, "week"), x_expect) }) test_that("can narrow to day", { x_expect <- year_week_day(2019, 2, 3) x <- set_hour(x_expect, 5) expect_identical(calendar_narrow(x, "day"), x_expect) expect_identical(calendar_narrow(x_expect, "day"), x_expect) }) # ------------------------------------------------------------------------------ # calendar_widen() test_that("can widen to week", { x <- year_week_day(2019) expect_identical(calendar_widen(x, "week"), set_week(x, 1)) }) test_that("can widen to day", { x <- year_week_day(2019) y <- year_week_day(2019, 02) expect_identical(calendar_widen(x, "day"), set_day(set_week(x, 1), 1)) expect_identical(calendar_widen(y, "day"), set_day(y, 1)) }) # ------------------------------------------------------------------------------ # calendar_start() test_that("can compute year start", { x <- year_week_day(2019) expect_identical(calendar_start(x, "year"), x) x <- year_week_day(2019, 2, 2, 2, 2, 2, 2, subsecond_precision = "millisecond") expect <- year_week_day(2019, 1, 1, 0, 0, 0, 0, subsecond_precision = "millisecond") expect_identical(calendar_start(x, "year"), expect) }) test_that("can compute week start", { x <- year_week_day(2019, 2) expect_identical(calendar_start(x, "week"), x) x <- year_week_day(2019, 2, 2, 2, 2, 2, 2, subsecond_precision = "millisecond") expect <- year_week_day(2019, 2, 1, 0, 0, 0, 0, subsecond_precision = "millisecond") expect_identical(calendar_start(x, "week"), expect) }) # ------------------------------------------------------------------------------ # calendar_end() test_that("can compute year end", { x <- year_week_day(2019) expect_identical(calendar_end(x, "year"), x) x <- year_week_day(2019:2020, 2, 2, 2, 2, 2, 2, subsecond_precision = "millisecond") expect <- year_week_day(2019:2020, 52:53, 7, 23, 59, 59, 999L, subsecond_precision = "millisecond") expect_identical(calendar_end(x, "year"), expect) }) test_that("can compute week end", { x <- year_week_day(2019, 2) expect_identical(calendar_end(x, "week"), x) x <- year_week_day(2019, 2, 2, 2, 2, 2, 2, subsecond_precision = "millisecond") expect <- year_week_day(2019, 2, 7, 23, 59, 59, 999L, subsecond_precision = "millisecond") expect_identical(calendar_end(x, "week"), expect) }) # ------------------------------------------------------------------------------ # calendar_count_between() test_that("can compute year counts", { x <- year_week_day(2019, 1, 1) y <- year_week_day(2020, 3, 4) expect_identical(calendar_count_between(x, y, "year"), 1L) }) test_that("can't compute a unsupported count precision", { x <- year_week_day(2019, 1, 1) expect_snapshot((expect_error(calendar_count_between(x, x, "week")))) }) test_that("positive / negative counts are correct", { start <- year_week_day(1972, 03, 04) end <- year_week_day(1973, 03, 03) expect_identical(calendar_count_between(start, end, "year"), 0L) end <- year_week_day(1973, 03, 04) expect_identical(calendar_count_between(start, end, "year"), 1L) end <- year_week_day(1973, 03, 05) expect_identical(calendar_count_between(start, end, "year"), 1L) end <- year_week_day(1971, 03, 03) expect_identical(calendar_count_between(start, end, "year"), -1L) end <- year_week_day(1971, 03, 04) expect_identical(calendar_count_between(start, end, "year"), -1L) end <- year_week_day(1971, 03, 05) expect_identical(calendar_count_between(start, end, "year"), 0L) }) # ------------------------------------------------------------------------------ # seq() test_that("only year precision is allowed", { expect_snapshot(error = TRUE, seq(year_week_day(2019, 1), by = 1, length.out = 2)) }) test_that("seq(to, by) works", { expect_identical(seq(year_week_day(2019), to = year_week_day(2024), by = 2), year_week_day(c(2019, 2021, 2023))) expect_identical(seq(year_week_day(2019), to = year_week_day(2023), by = 2), year_week_day(c(2019, 2021, 2023))) }) test_that("seq(to, length.out) works", { expect_identical(seq(year_week_day(2019), to = year_week_day(2024), length.out = 2), year_week_day(c(2019, 2024))) expect_identical(seq(year_week_day(2019), to = year_week_day(2024), length.out = 6), year_week_day(2019:2024)) expect_identical(seq(year_week_day(2019), to = year_week_day(2024), along.with = 1:2), year_week_day(c(2019, 2024))) }) test_that("seq(by, length.out) works", { expect_identical(seq(year_week_day(2019), by = 2, length.out = 3), year_week_day(c(2019, 2021, 2023))) expect_identical(seq(year_week_day(2019), by = 2, along.with = 1:3), year_week_day(c(2019, 2021, 2023))) }) # ------------------------------------------------------------------------------ # miscellaneous test_that("can roundtrip to naive-time with any `start`", { x <- seq( as_naive_time(year_month_day(-9999, 1, 1)), as_naive_time(year_month_day(9999, 12, 31)), by = 1 ) for (start in seq_len(7)) { expect_identical(x, as_naive_time(as_year_week_day(x, start = start))) } }) test_that("can generate correct last week of the year with any `start`", { start <- 1L expect_identical( as.Date(year_week_day(2019:2023, "last", 7, start = 1)), as.Date(year_month_day( c(2019, 2021, 2022, 2022, 2023), c(12, 1, 1, 12, 12), c(28, 2, 1, 31, 30) )) ) expect_identical( as.Date(year_week_day(2019:2023, "last", 7, start = 2)), as.Date(year_month_day( c(2019, 2021, 2022, 2023, 2023), c(12, 1, 1, 1, 12), c(29, 3, 2, 1, 31) )) ) expect_identical( as.Date(year_week_day(2019:2023, "last", 7, start = 3)), as.Date(year_month_day( c(2019, 2020, 2022, 2023, 2024), c(12, 12, 1, 1, 1), c(30,28, 3, 2, 1) )) ) # ..., 4, 5, ... expect_identical( as.Date(year_week_day(2019:2023, "last", 7, start = 6)), as.Date(year_month_day( c(2020, 2020, 2021, 2022, 2023), c(1, 12, 12, 12, 12), c(2, 31, 30, 29, 28) )) ) expect_identical( as.Date(year_week_day(2019:2023, "last", 7, start = 7)), as.Date(year_month_day( c(2020, 2021, 2021, 2022, 2023), c(1, 1, 12, 12, 12), c(3, 1, 31, 30, 29) )) ) }) # ------------------------------------------------------------------------------ # invalid_detect() test_that("`invalid_detect()` works", { # Not possible to be invalid x <- year_week_day(2019:2020) expect_identical(invalid_detect(x), c(FALSE, FALSE)) # Now possible x <- year_week_day(2019, c(1, 52, 53, NA)) expect_identical(invalid_detect(x), c(FALSE, FALSE, TRUE, FALSE)) # Possible after that too x <- year_week_day(2019, c(1, 52, 53, NA), 1) expect_identical(invalid_detect(x), c(FALSE, FALSE, TRUE, FALSE)) }) test_that("`invalid_detect()` works with different `start`", { x <- year_week_day(2024:2025, 53, start = clock_weekdays$sunday) expect_identical(invalid_detect(x), c(TRUE, FALSE)) x <- year_week_day(2024:2025, 53, start = clock_weekdays$monday) expect_identical(invalid_detect(x), c(TRUE, TRUE)) }) # ------------------------------------------------------------------------------ # invalid_any() test_that("`invalid_any()` works", { # Not possible to be invalid x <- year_week_day(2019:2020) expect_false(invalid_any(x)) # Now possible x <- year_week_day(2019, c(1, 52, 53, NA)) expect_true(invalid_any(x)) # Possible after that too x <- year_week_day(2019, c(1, 52, 53, NA), 1) expect_true(invalid_any(x)) }) test_that("`invalid_any()` works with different `start`", { x <- year_week_day(2024:2025, 53, start = clock_weekdays$sunday) expect_identical(invalid_any(x), TRUE) x <- year_week_day(2024:2025, 53, start = clock_weekdays$monday) expect_identical(invalid_any(x), TRUE) }) # ------------------------------------------------------------------------------ # invalid_count() test_that("`invalid_count()` works", { # Not possible to be invalid x <- year_week_day(2019:2020) expect_identical(invalid_count(x), 0L) # Now possible x <- year_week_day(2019, c(1, 52, 53, NA)) expect_identical(invalid_count(x), 1L) # Possible after that too x <- year_week_day(2019, c(1, 52, 53, NA), 1) expect_identical(invalid_count(x), 1L) }) test_that("`invalid_count()` works with different `start`", { x <- year_week_day(2024:2025, 53, start = clock_weekdays$sunday) expect_identical(invalid_count(x), 1L) x <- year_week_day(2024:2025, 53, start = clock_weekdays$monday) expect_identical(invalid_count(x), 2L) }) # ------------------------------------------------------------------------------ # invalid_resolve() test_that("strict mode can be activated", { local_options(clock.strict = TRUE) expect_snapshot(error = TRUE, invalid_resolve(year_week_day(2019, 1))) }) test_that("can resolve correctly", { x <- year_week_day(2019, 53, 2, 2, 3, 4, 5, subsecond_precision = "millisecond") expect_identical( invalid_resolve(x, invalid = "previous"), year_week_day(2019, 52, 7, 23, 59, 59, 999, subsecond_precision = "millisecond") ) expect_identical( invalid_resolve(x, invalid = "previous-day"), year_week_day(2019, 52, 7, 2, 3, 4, 5, subsecond_precision = "millisecond") ) expect_identical( invalid_resolve(x, invalid = "next"), year_week_day(2020, 01, 1, 0, 0, 0, 0, subsecond_precision = "millisecond") ) expect_identical( invalid_resolve(x, invalid = "next-day"), year_week_day(2020, 01, 1, 2, 3, 4, 5, subsecond_precision = "millisecond") ) expect_identical( invalid_resolve(x, invalid = "overflow"), year_week_day(2020, 01, 02, 0, 0, 0, 0, subsecond_precision = "millisecond") ) expect_identical( invalid_resolve(x, invalid = "overflow-day"), year_week_day(2020, 01, 02, 2, 3, 4, 5, subsecond_precision = "millisecond") ) expect_identical( invalid_resolve(x, invalid = "NA"), year_week_day(NA, NA, NA, NA, NA, NA, NA, subsecond_precision = "millisecond") ) }) test_that("throws known classed error", { expect_snapshot(error = TRUE, invalid_resolve(year_week_day(2019, 53))) expect_error(invalid_resolve(year_week_day(2019, 53)), class = "clock_error_invalid_date") }) test_that("works with always valid precisions", { x <- year_week_day(2019:2020) expect_identical(invalid_resolve(x), x) }) # ------------------------------------------------------------------------------ # vec_math() test_that("is.nan() works", { x <- year_week_day(c(2019, NA)) expect_identical(is.nan(x), c(FALSE, FALSE)) }) test_that("is.finite() works", { x <- year_week_day(c(2019, NA)) expect_identical(is.finite(x), c(TRUE, FALSE)) }) test_that("is.infinite() works", { x <- year_week_day(c(2019, NA)) expect_identical(is.infinite(x), c(FALSE, FALSE)) }) # ------------------------------------------------------------------------------ # clock_minimum() / clock_maximum() test_that("minimums are right", { expect_snapshot({ clock_minimum(year_week_day(1)) clock_minimum(year_week_day(1, 1)) clock_minimum(year_week_day(1, 1, 1)) clock_minimum(year_week_day(1, 1, 1, 1)) clock_minimum(year_week_day(1, 1, 1, 1, 1)) clock_minimum(year_week_day(1, 1, 1, 1, 1, 1)) clock_minimum(year_week_day(1, 1, 1, 1, 1, 1, 1, subsecond_precision = "millisecond")) clock_minimum(year_week_day(1, 1, 1, 1, 1, 1, 1, subsecond_precision = "microsecond")) clock_minimum(year_week_day(1, 1, 1, 1, 1, 1, 1, subsecond_precision = "nanosecond")) }) }) test_that("maximums are right", { expect_snapshot({ clock_maximum(year_week_day(1)) clock_maximum(year_week_day(1, 1)) clock_maximum(year_week_day(1, 1, 1)) clock_maximum(year_week_day(1, 1, 1, 1)) clock_maximum(year_week_day(1, 1, 1, 1, 1)) clock_maximum(year_week_day(1, 1, 1, 1, 1, 1)) clock_maximum(year_week_day(1, 1, 1, 1, 1, 1, 1, subsecond_precision = "millisecond")) clock_maximum(year_week_day(1, 1, 1, 1, 1, 1, 1, subsecond_precision = "microsecond")) clock_maximum(year_week_day(1, 1, 1, 1, 1, 1, 1, subsecond_precision = "nanosecond")) }) }) test_that("minimums and maximums respect `start`", { expect_snapshot({ clock_minimum(year_week_day(1, start = clock_weekdays$friday)) clock_maximum(year_week_day(1, start = clock_weekdays$friday)) }) }) # ------------------------------------------------------------------------------ # min() / max() / range() test_that("min() / max() / range() works", { x <- year_week_day(c(1, 3, 2, 1, -1)) expect_identical(min(x), year_week_day(-1)) expect_identical(max(x), year_week_day(3)) expect_identical(range(x), year_week_day(c(-1, 3))) }) test_that("min() / max() / range() works with `NA`", { x <- year_week_day(c(1, NA, 2, 0)) expect_identical(min(x), year_week_day(NA)) expect_identical(max(x), year_week_day(NA)) expect_identical(range(x), year_week_day(c(NA, NA))) expect_identical(min(x, na.rm = TRUE), year_week_day(0)) expect_identical(max(x, na.rm = TRUE), year_week_day(2)) expect_identical(range(x, na.rm = TRUE), year_week_day(c(0, 2))) }) test_that("min() / max() / range() works when empty", { x <- year_week_day(integer()) expect_identical(min(x), clock_maximum(x)) expect_identical(max(x), clock_minimum(x)) expect_identical(range(x), c(clock_maximum(x), clock_minimum(x))) x <- year_week_day(c(NA, NA)) expect_identical(min(x, na.rm = TRUE), clock_maximum(x)) expect_identical(max(x, na.rm = TRUE), clock_minimum(x)) expect_identical(range(x, na.rm = TRUE), c(clock_maximum(x), clock_minimum(x))) }) # ------------------------------------------------------------------------------ # add_*() test_that("add_years() works", { x <- year_week_day(2019, 1, 2, 3:4) expect_identical( add_years(x, 1:2), year_week_day(c(2020, 2021), 1, 2, 3:4) ) expect_identical( add_years(x, NA), vec_init(x, 2L) ) }) test_that("add_*() respect recycling rules", { expect_length(add_years(year_week_day(1), 1:2), 2L) expect_length(add_years(year_week_day(1:2), 1), 2L) expect_length(add_years(year_week_day(1), integer()), 0L) expect_length(add_years(year_week_day(integer()), 1), 0L) expect_snapshot(error = TRUE, { add_years(year_week_day(1:2), 1:3) }) }) test_that("add_*() retains names", { x <- set_names(year_week_day(1), "x") y <- year_week_day(1) n <- set_names(1, "n") expect_named(add_years(x, n), "x") expect_named(add_years(y, n), "n") }) test_that("`start` value is retained", { expect_identical(year_week_day(2019, 1, 1) + duration_years(1), year_week_day(2020, 1, 1)) expect_identical(year_week_day(2019, 1, 1) + duration_years(5), year_week_day(2024, 1, 1)) # Ensure that the `start` is retained expect_identical( year_week_day(2019, 1, 1, start = 2) + duration_years(5), year_week_day(2024, 1, 1, start = 2) ) }) clock/tests/testthat/test-naive-time.R0000644000176200001440000005134214426734360017523 0ustar liggesusers# ------------------------------------------------------------------------------ # naive_time_info() test_that("naive-time-info returns the right structure", { info <- naive_time_info(naive_days(0), "America/New_York") expect_type(info$type, "character") expect_s3_class(info$first, "data.frame") expect_s3_class(info$second, "data.frame") }) test_that("unique info works", { info <- naive_time_info(naive_days(0), "America/New_York") na_sys_info <- sys_time_info(sys_days(NA), "America/New_York") expect_identical(info$type, "unique") expect_identical(info$second, na_sys_info) begin <- as_sys_time(year_month_day(1969, 10, 26, 06, 00, 00)) end <- as_sys_time(year_month_day(1970, 4, 26, 7, 0, 0)) offset <- duration_seconds(-18000) expect_identical(info$first$begin, begin) expect_identical(info$first$end, end) expect_identical(info$first$offset, offset) expect_identical(info$first$dst, FALSE) expect_identical(info$first$abbreviation, "EST") }) test_that("nonexistent info works", { x <- as_naive_time(year_month_day(1970, 4, 26, 2, 30, 00)) info <- naive_time_info(x, "America/New_York") expect_identical(info$type, "nonexistent") begin <- as_sys_time(year_month_day(1969, 10, 26, 06, 00, 00)) end <- as_sys_time(year_month_day(1970, 4, 26, 7, 0, 0)) offset <- duration_seconds(-18000) expect_identical(info$first$begin, begin) expect_identical(info$first$end, end) expect_identical(info$first$offset, offset) expect_identical(info$first$dst, FALSE) expect_identical(info$first$abbreviation, "EST") begin <- as_sys_time(year_month_day(1970, 4, 26, 7, 0, 0)) end <- as_sys_time(year_month_day(1970, 10, 25, 6, 0, 0)) offset <- duration_seconds(-14400) expect_identical(info$second$begin, begin) expect_identical(info$second$end, end) expect_identical(info$second$offset, offset) expect_identical(info$second$dst, TRUE) expect_identical(info$second$abbreviation, "EDT") }) test_that("ambiguous info works", { x <- as_naive_time(year_month_day(1970, 10, 25, 1, 30, 00)) info <- naive_time_info(x, "America/New_York") expect_identical(info$type, "ambiguous") begin <- as_sys_time(year_month_day(1970, 4, 26, 7, 0, 0)) end <- as_sys_time(year_month_day(1970, 10, 25, 6, 0, 0)) offset <- duration_seconds(-14400) expect_identical(info$first$begin, begin) expect_identical(info$first$end, end) expect_identical(info$first$offset, offset) expect_identical(info$first$dst, TRUE) expect_identical(info$first$abbreviation, "EDT") begin <- as_sys_time(year_month_day(1970, 10, 25, 6, 0, 0)) end <- as_sys_time(year_month_day(1971, 4, 25, 7, 0, 0)) offset <- duration_seconds(-18000) expect_identical(info$second$begin, begin) expect_identical(info$second$end, end) expect_identical(info$second$offset, offset) expect_identical(info$second$dst, FALSE) expect_identical(info$second$abbreviation, "EST") }) test_that("all rows are NA when x is NA", { info <- naive_time_info(naive_days(NA), "UTC") na_sys_info <- sys_time_info(sys_days(NA), "UTC") df <- data_frame(type = NA_character_, first = na_sys_info, second = na_sys_info) expect_identical(info, df) }) test_that("x must be naive", { expect_snapshot(error = TRUE, naive_time_info(sys_days(0), "UTC")) }) test_that("zone is vectorized and recycled against x", { info <- naive_time_info(naive_days(0), c("UTC", "America/New_York")) expect_identical(nrow(info), 2L) expect_snapshot(error = TRUE, naive_time_info(naive_days(0:3), c("UTC", "America/New_York"))) }) # ------------------------------------------------------------------------------ # as.character() test_that("as.character() works", { x <- as_naive_time(year_month_day(2019, 1, 1)) expect_identical(as.character(x), "2019-01-01") x <- as_naive_time(year_month_day(2019, 1, 1, 1, 1)) expect_identical(as.character(x), "2019-01-01T01:01") }) # ------------------------------------------------------------------------------ # naive_time_parse() test_that("can parse day precision", { x <- c("2019-01-01", "2019-01-31") expect_identical( naive_time_parse(x, precision = "day"), as_naive_time(year_month_day(2019, 1, c(1, 31))) ) }) test_that("can parse second precision", { x <- c("2019-01-01T00:00:05", "2019-01-31T00:00:10") expect_identical( naive_time_parse(x, precision = "second"), as_naive_time(year_month_day(2019, 1, c(1, 31), 00, 00, c(05, 10))) ) }) test_that("can parse subsecond precision", { x <- c("2019-01-01T00:00:05.123", "2019-01-31T00:00:10.124") y <- c("2019-01-01T00:00:05.12345", "2019-01-31T00:00:10.124567") z <- c("2019-01-01T00:00:05.12345678", "2019-01-31T00:00:10.124567899") sec <- year_month_day(2019, 1, c(1, 31), 00, 00, c(05, 10)) expect_identical( naive_time_parse(x, precision = "millisecond"), as_naive_time(set_millisecond(sec, c(123, 124))) ) expect_identical( naive_time_parse(y, precision = "microsecond"), as_naive_time(set_microsecond(sec, c(123450, 124567))) ) expect_identical( naive_time_parse(z, precision = "nanosecond"), as_naive_time(set_nanosecond(sec, c(123456780, 124567899))) ) }) test_that("parsing works if `precision` uses a default that doesn't attempt to capture all the info", { # Uses %Y-%m-%d x <- "2019-01-01T01:00:00" expect_identical( naive_time_parse(x, precision = "day"), as_naive_time(year_month_day(2019, 1, 1)) ) # Uses %Y-%m-%dT%H:%M x <- "2019-01-01T01:00:59" expect_identical( naive_time_parse(x, precision = "minute"), as_naive_time(year_month_day(2019, 1, 1, 1, 0)) ) }) test_that("parsing day components with second precision uses midnight as time", { x <- "2019/1/1" expect_identical( naive_time_parse(x, format = "%Y/%m/%d", precision = "second"), as_naive_time(year_month_day(2019, 1, 1, 0, 0, 0)) ) }) test_that("cannot parse invalid dates", { x <- "2019-02-31" expect_warning( expect_identical( naive_time_parse(x, precision = "day"), naive_days(NA) ) ) expect_snapshot(naive_time_parse(x, precision = "day")) }) test_that("can parse with multiple formats", { x <- c("2019-01-01", "2020/1/2", "January 05, 2019") formats <- c("%Y-%m-%d", "%Y/%m/%d", "%B %d, %Y") expect_identical( naive_time_parse(x, format = formats, precision = "day"), as_naive_time(year_month_day(c(2019, 2020, 2019), 1, c(1, 2, 5))) ) }) test_that("failure to parse results in NA", { x <- "2019-01-oh" expect_warning( expect_identical( naive_time_parse(x, format = "%Y-%m-%d", precision = "day"), naive_days(NA) ) ) }) test_that("failure to parse throws a warning", { expect_warning(naive_time_parse("foo"), class = "clock_warning_parse_failures") expect_snapshot(naive_time_parse("foo")) }) test_that("names of input are kept", { x <- c(foo = "2019-01-01") expect_named(naive_time_parse(x, precision = "day"), "foo") }) test_that("can use a different locale", { x <- "janvier 01, 2019" y <- "2019-01-01T00:00:00,123456" expect_identical( naive_time_parse(x, format = "%B %d, %Y", precision = "day", locale = clock_locale("fr")), as_naive_time(year_month_day(2019, 1, 1)) ) expect_identical( naive_time_parse(y, precision = "microsecond", locale = clock_locale(decimal_mark = ",")), as_naive_time(year_month_day(2019, 1, 1, 0, 0, 0, 123456, subsecond_precision = "microsecond")) ) }) test_that("`x` is translated to UTF-8", { x <- "f\u00E9vrier 05, 2019" x <- iconv(x, from = "UTF-8", to = "latin1") locale <- clock_locale("fr") format <- "%B %d, %Y" expect_identical(Encoding(x[1]), "latin1") expect_identical(Encoding(locale$labels$month[2]), "UTF-8") expect_identical( naive_time_parse(x, format = format, precision = "day", locale = locale), as_naive_time(year_month_day(2019, 2, 5)) ) }) test_that("%z is completely ignored, but is required to be parsed correctly if specified", { x <- "2019-01-01 00:00:00+0100" y <- "2019-01-01 00:00:00" expect_identical( naive_time_parse(x, format = "%Y-%m-%d %H:%M:%S%z"), as_naive_time(year_month_day(2019, 1, 1, 0, 0, 0)) ) expect_warning( expect_identical( naive_time_parse(y, format = "%Y-%m-%d %H:%M:%S%z"), naive_seconds(NA) ) ) }) test_that("%Z is completely ignored", { x <- "2019-01-01 00:00:00 America/New_York" expect_identical( naive_time_parse(x, format = "%Y-%m-%d %H:%M:%S %Z"), as_naive_time(year_month_day(2019, 1, 1, 0, 0, 0)) ) }) test_that("parsing rounds parsed components more precise than the resulting container (#207) (#230) (undocumented)", { expect_identical( naive_time_parse("2019-12-31 11", format = "%Y-%m-%d %H", precision = "day"), as_naive_time(year_month_day(2019, 12, 31)) ) expect_identical( naive_time_parse("2019-12-31 12", format = "%Y-%m-%d %H", precision = "day"), as_naive_time(year_month_day(2020, 1, 1)) ) # If you don't try and parse them, it won't round expect_identical( naive_time_parse("2019-12-31 12", format = "%Y-%m-%d", precision = "day"), as_naive_time(year_month_day(2019, 12, 31)) ) }) test_that("parsing rounds parsed subsecond components more precise than the resulting container (#207) (#230) (undocumented)", { # Default N for milliseconds is 6, so `%6S` (2 hour seconds, 1 for decimal, 3 for subseconds) expect_identical( naive_time_parse("2019-01-01 01:01:01.1238", format = "%Y-%m-%d %H:%M:%S", precision = "millisecond"), as_naive_time(year_month_day(2019, 1, 1, 1, 1, 1, 123, subsecond_precision = "millisecond")) ) # Requesting `%7S` parses the full `01.1238`, and the `1238` portion is rounded up expect_identical( naive_time_parse("2019-01-01 01:01:01.1238", format = "%Y-%m-%d %H:%M:%7S", precision = "millisecond"), as_naive_time(year_month_day(2019, 1, 1, 1, 1, 1, 124, subsecond_precision = "millisecond")) ) }) test_that("parsing fails when undocumented rounding behavior would result in invalid 60 second component (#230) (undocumented)", { expect_warning( expect_identical( naive_time_parse("2019-01-01 01:01:59.550", format = "%Y-%m-%d %H:%M:%6S", precision = "second"), as_naive_time(year_month_day(NA, NA, NA, NA, NA, NA)) ), class = "clock_warning_parse_failures" ) }) test_that("`naive_time_parse()` validates `locale`", { expect_snapshot(error = TRUE, { naive_time_parse("2019-01-01T00:00:00", locale = 1) }) }) # ------------------------------------------------------------------------------ # format() test_that("default format is correct", { expect_snapshot(format(naive_seconds(0))) }) test_that("`%Z` generates format warnings (#204)", { x <- naive_seconds(0) expect_warning(format(x, format = "%Z"), class = "clock_warning_format_failures") expect_snapshot(format(x, format = "%Z")) expect_snapshot(format(c(x, x), format = "%Z")) }) test_that("`%z` generates format warnings (#204)", { x <- naive_seconds(0) expect_warning(format(x, format = "%z"), class = "clock_warning_format_failures") expect_snapshot(format(x, format = "%z")) expect_snapshot(format(c(x, x), format = "%z")) }) # ------------------------------------------------------------------------------ # as_zoned_time() test_that("can convert non-ambiguous/nonexistent times to zoned time", { zone <- "America/New_York" x <- as_naive_time(year_month_day(2019, 1, 1)) expect <- as_sys_time(year_month_day(2019, 1, 1, 5)) expect_identical(as_zoned_time(x, zone), as_zoned_time(expect, zone)) }) test_that("sub daily time point precision is retained", { zone <- "America/New_York" x <- as_naive_time(year_month_day(2019, 1, 1, 1, 1, 1, 1, subsecond_precision = "nanosecond")) expect_identical(zoned_time_precision_attribute(as_zoned_time(x, zone)), PRECISION_NANOSECOND) }) test_that("day precision time point is promoted", { zone <- "America/New_York" x <- as_naive_time(year_month_day(2019, 1, 1)) expect_identical(zoned_time_precision_attribute(as_zoned_time(x, zone)), PRECISION_SECOND) }) test_that("can resolve ambiguous issues - character", { zone <- "America/New_York" x <- as_naive_time(year_month_day(1970, 10, 25, 01, 30, 00, 01, subsecond_precision = "millisecond")) earliest <- as_sys_time(year_month_day(1970, 10, 25, 05, 30, 00, 01, subsecond_precision = "millisecond")) latest <- as_sys_time(year_month_day(1970, 10, 25, 06, 30, 00, 01, subsecond_precision = "millisecond")) expect_snapshot(error = TRUE, as_zoned_time(x, zone)) expect_error(as_zoned_time(x, zone), class = "clock_error_ambiguous_time") expect_identical( as_zoned_time(x, zone, ambiguous = "earliest"), as_zoned_time(earliest, zone) ) expect_identical( as_zoned_time(x, zone, ambiguous = "latest"), as_zoned_time(latest, zone) ) expect_identical( as_zoned_time(x, zone, ambiguous = "NA"), as_zoned_time(as_naive_time(duration_milliseconds(NA)), zone) ) }) test_that("can resolve ambiguous issues - zoned-time", { zone <- "America/New_York" nt <- as_naive_time(year_month_day(1970, 10, 25, 01, 30, c(00, 00))) zt <- as_zoned_time(nt, zone, ambiguous = c("earliest", "latest")) expect_identical( as_zoned_time(nt, zone, ambiguous = zt), zt ) nt <- as_naive_time(year_month_day(1970, 10, 25, c(01, 02), 30, 00)) zt <- as_zoned_time(nt, zone, ambiguous = "earliest") expect_identical( as_zoned_time(nt, zone, ambiguous = zt), zt ) # Issue at location 2 because zt[2] isn't ambiguous so we can't use offset # information from it nt_tweaked <- as_naive_time(set_hour(as_year_month_day(nt), 1)) expect_snapshot(error = TRUE, as_zoned_time(nt_tweaked, zone, ambiguous = zt)) # Jump from one ambiguous time to another. Still can't use offset info, # because the ambiguous time transitions are different. ymd <- year_month_day(1969, 10, 26, 01, 30, 00) nt <- as_naive_time(ymd) zt <- as_zoned_time(nt, zone, ambiguous = "earliest") nt_tweaked <- nt + duration_days(364) expect_snapshot(error = TRUE, as_zoned_time(nt_tweaked, zone, ambiguous = zt)) }) test_that("can resolve ambiguous issues - list", { zone <- "America/New_York" nt <- as_naive_time(year_month_day(1970, 10, 25, c(01, 01, 02), 30, 00)) zt <- as_zoned_time(nt, zone, ambiguous = c("earliest", "latest", "error")) nt_tweaked <- as_naive_time(set_hour(as_year_month_day(nt), 1)) # First two are resolved from consulting `zt`, otherwise resolved with "latest" expect_identical( as_zoned_time(nt_tweaked, zone, ambiguous = list(zt, "latest")), as_zoned_time(nt_tweaked, zone, ambiguous = c("earliest", "latest", "latest")) ) }) test_that("zoned-time ambiguous argument is recycled", { zone <- "America/New_York" nt <- as_naive_time(year_month_day(1970, 10, 25, 01, 30, 00)) zt <- as_zoned_time(nt, zone, ambiguous = "earliest") nt_tweaked <- nt + duration_seconds(c(0, 1)) expect_identical( as_zoned_time(nt_tweaked, zone, ambiguous = zt), as_zoned_time(nt_tweaked, zone, ambiguous = "earliest") ) }) test_that("can resolve nonexistent issues", { zone <- "America/New_York" x <- as_naive_time(year_month_day(1970, 04, 26, 02, 30, 00)) expect_snapshot(error = TRUE, as_zoned_time(x, zone)) expect_error(as_zoned_time(x, zone), class = "clock_error_nonexistent_time") expect_identical( as_zoned_time(x, zone, nonexistent = "roll-forward"), as_zoned_time(add_minutes(x, 30), zone) ) expect_identical( as_zoned_time(x, zone, nonexistent = "roll-backward"), as_zoned_time(add_seconds(add_minutes(x, -30), -1), zone) ) expect_identical( as_zoned_time(x, zone, nonexistent = "shift-forward"), as_zoned_time(add_minutes(x, 60), zone) ) expect_identical( as_zoned_time(x, zone, nonexistent = "shift-backward"), as_zoned_time(add_minutes(x, -60), zone) ) expect_identical( as_zoned_time(x, zone, nonexistent = "NA"), as_zoned_time(naive_seconds(NA), zone) ) }) test_that("nonexistent can be vectorized", { zone <- "America/New_York" x <- as_naive_time(year_month_day(1970, 04, 26, 02, 00, c(00, 00))) expect_identical( as_zoned_time(x, zone, nonexistent = c("roll-forward", "roll-backward")), as_zoned_time(x + duration_seconds(c(3600, -1)), zone) ) }) test_that("roll-backward uses precision to determine last moment in time", { zone <- "America/New_York" w <- as_naive_time(year_month_day(1970, 04, 26, 02, 00, 00)) x <- w + duration_milliseconds(0) y <- w + duration_microseconds(0) z <- w + duration_nanoseconds(0) expect_identical( as_zoned_time(w, zone, nonexistent = "roll-backward"), as_zoned_time(add_seconds(w, -1), zone) ) expect_identical( as_zoned_time(x, zone, nonexistent = "roll-backward"), as_zoned_time(add_milliseconds(x, -1), zone) ) expect_identical( as_zoned_time(y, zone, nonexistent = "roll-backward"), as_zoned_time(add_microseconds(y, -1), zone) ) expect_identical( as_zoned_time(z, zone, nonexistent = "roll-backward"), as_zoned_time(add_nanoseconds(z, -1), zone) ) }) test_that("names are retained", { zone <- "America/New_York" x <- as_naive_time(year_month_day(2019, 1, 1)) x <- c(foo = x) expect_named(as_zoned_time(x, zone), "foo") }) test_that("NA pass through", { x <- as_zoned_time(naive_seconds(NA), "America/New_York") expect_true(is.na(x)) }) test_that("`ambiguous` is validated", { zone <- "America/New_York" expect_snapshot(error = TRUE, as_zoned_time(naive_seconds(), zone, ambiguous = 1)) expect_snapshot(error = TRUE, as_zoned_time(naive_seconds(), zone, ambiguous = "foo")) expect_snapshot(error = TRUE, as_zoned_time(naive_seconds(), zone, ambiguous = c("earliest", "latest"))) ambiguous <- as_zoned_time(naive_seconds(), "America/Los_Angeles") expect_snapshot(error = TRUE, as_zoned_time(naive_seconds(), zone, ambiguous = ambiguous)) reference <- as_zoned_time(naive_seconds(), zone) expect_snapshot(error = TRUE, as_zoned_time(naive_seconds(), zone, ambiguous = list())) expect_snapshot(error = TRUE, as_zoned_time(naive_seconds(), zone, ambiguous = list(1, 1))) expect_snapshot(error = TRUE, as_zoned_time(naive_seconds(), zone, ambiguous = list(reference, 1))) expect_snapshot(error = TRUE, as_zoned_time(naive_seconds(), zone, ambiguous = list(reference, "foo"))) }) test_that("`nonexistent` is validated", { zone <- "America/New_York" expect_snapshot(error = TRUE, as_zoned_time(naive_seconds(), zone, nonexistent = 1)) expect_snapshot(error = TRUE, as_zoned_time(naive_seconds(), zone, nonexistent = "foo")) expect_snapshot(error = TRUE, as_zoned_time(naive_seconds(), zone, nonexistent = c("roll-forward", "roll-forward"))) }) test_that("zone is validated", { expect_snapshot(error = TRUE, as_zoned_time(naive_seconds(), "foo")) expect_snapshot(error = TRUE, as_zoned_time(naive_seconds(), 1)) expect_snapshot(error = TRUE, as_zoned_time(naive_seconds(), c("America/New_York", "EST", "EDT"))) }) test_that("strict mode can be activated - nonexistent", { local_options(clock.strict = TRUE) zone <- "America/New_York" expect_snapshot(error = TRUE, as_zoned_time(naive_seconds(), zone, ambiguous = "earliest")) }) test_that("strict mode can be activated - ambiguous", { zone <- "America/New_York" zt <- as_zoned_time(naive_seconds(), zone) local_options(clock.strict = TRUE) expect_snapshot(error = TRUE, as_zoned_time(naive_seconds(), zone, nonexistent = "roll-forward")) expect_snapshot(error = TRUE, as_zoned_time(naive_seconds(), zone, nonexistent = "roll-forward", ambiguous = zt)) expect_snapshot(error = TRUE, as_zoned_time(naive_seconds(), zone, nonexistent = "roll-forward", ambiguous = list(zt, NULL))) }) test_that("empty dots are checked", { expect_snapshot(error = TRUE, as_zoned_time(naive_seconds(), "UTC", "roll-forward")) }) # ------------------------------------------------------------------------------ # vec_ptype_full() / vec_ptype_abbr() test_that("`vec_ptype_full()` prints correctly", { expect_snapshot({ vec_ptype_full(naive_days()) vec_ptype_full(naive_seconds(1:5)) }) }) test_that("`vec_ptype_abbr()` prints correctly", { expect_snapshot({ vec_ptype_abbr(naive_days()) vec_ptype_abbr(naive_seconds(1:5)) }) }) # ------------------------------------------------------------------------------ # vec_ptype() test_that("ptype is correct", { base <- naive_days(0) ptype <- naive_days(integer()) for (precision in precision_names()) { if (precision_to_integer(precision) < PRECISION_DAY) { next } x <- time_point_cast(base, precision) expect <- time_point_cast(ptype, precision) expect_identical(vec_ptype(x), expect) } }) # ------------------------------------------------------------------------------ # vec_math() test_that("is.nan() works", { x <- naive_days(c(2019, NA)) expect_identical(is.nan(x), c(FALSE, FALSE)) }) test_that("is.finite() works", { x <- naive_days(c(2019, NA)) expect_identical(is.finite(x), c(TRUE, FALSE)) }) test_that("is.infinite() works", { x <- naive_days(c(2019, NA)) expect_identical(is.infinite(x), c(FALSE, FALSE)) }) clock/tests/testthat/test-posixt.R0000644000176200001440000013407214426767645017031 0ustar liggesusers# ------------------------------------------------------------------------------ # as_date_time() test_that("can convert POSIXct -> POSIXct", { x <- new_datetime(0, tzone = "America/New_York") expect_identical(as_date_time(x), x) }) test_that("can convert POSIXlt -> POSIXct", { expect <- new_datetime(0, tzone = "America/New_York") x <- as.POSIXlt(expect) expect_identical(as_date_time(x), expect) }) test_that("integer POSIXct are normalized to double", { x <- 0L class(x) <- c("POSIXct", "POSIXt") attr(x, "tzone") <- "America/New_York" expect <- new_datetime(0, tzone = "America/New_York") expect_identical(as_date_time(x), expect) }) test_that("can't accidentally supply `zone` to reinterpret date-time in new zone", { expect_snapshot(error = TRUE, as_date_time(new_datetime(0), zone = "America/New_York")) }) test_that("can convert Date -> POSIXct (Date assumed to be naive)", { x <- date_parse("2019-01-01") expect_identical(as_date_time(x, "America/New_York"), date_time_parse("2019-01-01 00:00:00", "America/New_York")) expect_identical(as_date_time(x, "Europe/London"), date_time_parse("2019-01-01 00:00:00", "Europe/London")) }) test_that("can convert calendar -> POSIXct", { zone <- "America/New_York" expect_identical(as_date_time(year_month_day(1970, 1, 2), zone), new_datetime(104400, zone)) expect_identical(as_date_time(year_quarter_day(1970, 1, 2), zone), new_datetime(104400, zone)) }) test_that("can convert sys-time -> POSIXct", { expect_identical(as_date_time(sys_seconds(0), "UTC"), new_datetime(0, "UTC")) expect_identical(as_date_time(sys_seconds(0), "America/New_York"), new_datetime(0, "America/New_York")) }) test_that("can convert naive-time -> POSIXct", { expect_identical(as_date_time(naive_seconds(0), "UTC"), new_datetime(0, "UTC")) expect_identical(as_date_time(naive_seconds(0), "America/New_York"), new_datetime(18000, "America/New_York")) }) test_that("can convert zoned-time -> POSIXct", { x <- as_zoned_time(naive_time_parse("2019-01-01T23:02:03"), "America/New_York") expect <- date_time_parse("2019-01-01 23:02:03", "America/New_York") expect_identical(as_date_time(x), expect) }) test_that("can resolve nonexistent midnight issues for Date -> POSIXct", { # In Asia/Beirut, DST gap from 2021-03-27 23:59:59 -> 2021-03-28 01:00:00 zone <- "Asia/Beirut" x <- as.Date("2021-03-28") expect_snapshot({ (expect_error(as_date_time(x, zone), class = "clock_error_nonexistent_time")) }) expect_identical( as_date_time(x, zone, nonexistent = "roll-forward"), as_date_time(as_naive_time(year_month_day(2021, 03, 28, 1)), zone) ) }) test_that("can resolve ambiguous midnight issues for Date -> POSIXct", { # In Asia/Amman, DST fallback from 2021-10-29 00:59:59 -> 2021-10-29 00:00:00 zone <- "Asia/Amman" x <- as.Date("2021-10-29") expect_snapshot({ (expect_error(as_date_time(x, zone), class = "clock_error_ambiguous_time")) }) expect_identical( as_date_time(x, zone, ambiguous = "earliest"), date_time_parse_complete("2021-10-29T00:00:00+03:00[Asia/Amman]") ) expect_identical( as_date_time(x, zone, ambiguous = "latest"), date_time_parse_complete("2021-10-29T00:00:00+02:00[Asia/Amman]") ) }) # ------------------------------------------------------------------------------ # as.POSIXct() test_that("casting to POSIXct floors components more precise than seconds (#205)", { x <- naive_time_parse("1969-01-01T01:01:01.678", precision = "millisecond") expect_identical( as.POSIXct(x, "America/New_York"), date_time_parse("1969-01-01 01:01:01", "America/New_York") ) }) test_that("casting to POSIXct with time point less precise than seconds works (#278)", { x <- as_naive_time(year_month_day(2019, 1, 1, 1)) expect_identical( as.POSIXct(x, "America/New_York"), date_time_parse("2019-01-01 01:00:00", "America/New_York") ) x <- as_sys_time(year_month_day(2019, 1, 1, 1)) expect_identical( as.POSIXct(x, "America/New_York"), date_time_parse("2018-12-31 20:00:00", "America/New_York") ) }) # ------------------------------------------------------------------------------ # as_weekday() test_that("can convert to weekday", { x <- as.POSIXct("2019-01-01", tz = "America/New_York") expect_identical(as_weekday(x), weekday(3L)) expect_identical(as_weekday(as.POSIXlt(x)), weekday(3L)) }) test_that("converting to weekday retains names", { x <- c(x = as.POSIXct("2019-01-01", tz = "America/New_York")) expect_named(as_weekday(x), names(x)) expect_named(as_weekday(as.POSIXlt(x)), names(x)) }) # ------------------------------------------------------------------------------ # date_group() test_that("can group by year (#312)", { # Skip on 32-bit machines # https://github.com/r-lib/clock/issues/312#issuecomment-1507528450 skip_if( condition = .Machine$sizeof.pointer < 8L, message = "Likely an integer overflow/underflow bug in base R here" ) # Using year 0 as the starting point x <- as.POSIXct(c("0000-01-05", "0001-02-10", "0002-03-12", "0003-01-15", "0004-12-10"), "America/New_York") expect <- set_month(set_day(x, 1), 1) expect_identical(date_group(x, "year"), expect) expect_identical(date_group(x, "year", n = 3), expect[c(1, 1, 1, 4, 4)]) }) test_that("can group by month/day/hour/minute/second", { x <- as.POSIXct("2019-01-01", "America/New_York") x <- add_days(x, 0:3) expect_identical(date_group(x, "day"), x) expect_identical(date_group(x, "day", n = 2), x[c(1, 1, 3, 3)]) y <- as.POSIXct(c("2019-01-01", "2019-02-05", "2019-03-10"), "America/New_York") expect <- set_day(y, 1) expect_identical(date_group(y, "month"), expect) expect_identical(date_group(y, "month", n = 2), expect[c(1, 1, 3)]) z <- as.POSIXct("2019-01-01", "America/New_York") z <- add_hours(z, c(0, 1, 2, 4, 5)) expect_identical(date_group(z, "hour", n = 2), z[c(1, 1, 3, 4, 4)]) }) test_that("can handle nonexistent times resulting from grouping", { x <- as.POSIXct("1970-04-26 03:30:00", "America/New_York") expect <- set_minute(x, 0) expect_snapshot(error = TRUE, date_group(x, "hour", n = 2)) expect_identical(date_group(x, "hour", n = 2, nonexistent = "roll-forward"), expect) }) test_that("can't group by finer precisions", { x <- as.POSIXct("2019-01-01", "America/New_York") expect_snapshot(error = TRUE, date_group(x, "nanosecond")) }) test_that("can't group by non-year-month-day precisions", { x <- as.POSIXct("2019-01-01", "America/New_York") expect_snapshot(error = TRUE, date_group(x, "quarter")) }) # ------------------------------------------------------------------------------ # date_leap_year() test_that("can detect leap years", { x <- c("2019-01-01", "2020-01-01", NA) x <- as.POSIXct(x, tz = "America/New_York") expect_identical(date_leap_year(x), c(FALSE, TRUE, NA)) }) # ------------------------------------------------------------------------------ # date_floor() test_that("can floor by weeks", { x <- c("1970-01-01", "1970-01-07", "1970-01-08") x <- as.POSIXct(x, "America/New_York") expect_identical(date_floor(x, "week"), x[c(1, 1, 3)]) expect_identical(date_floor(x, "week", n = 2), x[c(1, 1, 1)]) }) test_that("flooring can handle nonexistent times", { x <- as.POSIXct("1970-04-26 01:59:59", "America/New_York") + c(0, 1) expect <- as.POSIXct(c("1970-04-26 00:00:00", "1970-04-26 03:00:00"), "America/New_York") expect_snapshot(error = TRUE, date_floor(x, "hour", n = 2)) expect_identical(date_floor(x, "hour", n = 2, nonexistent = "roll-forward"), expect) }) test_that("flooring can handle ambiguous times", { x <- as.POSIXct("1970-10-25 00:30:00", "America/New_York") + c(3600, 7200) expect <- as.POSIXct("1970-10-25 00:00:00", "America/New_York") + c(3600, 3600) expect_identical(date_floor(x, "hour", ambiguous = "earliest"), expect) }) test_that("`origin` can be used", { origin <- as.POSIXct("1970-01-02", "America/New_York") x <- as.POSIXct(c("1970-01-01", "1970-01-02", "1970-01-03"), "America/New_York") expect <- as.POSIXct(c("1969-12-31", "1970-01-02", "1970-01-02"), "America/New_York") expect_identical(date_floor(x, "day", n = 2, origin = origin), expect) }) test_that("`origin` is floored to the precision of `precision` with a potential warning before rounding", { origin <- as.POSIXct("1970-01-01 01:00:00", "America/New_York") x <- as.POSIXct(c("1970-01-01 00:00:00", "1970-01-02 00:00:00"), "America/New_York") expect_snapshot(date_floor(x, "day", origin = origin)) expect_warning(date_floor(x, "day", origin = origin), class = "clock_warning_invalid_rounding_origin") }) test_that("`origin` is validated", { zone <- "America/New_York" x <- as.POSIXct("2019-01-01", zone) expect_snapshot(error = TRUE, date_floor(x, "day", origin = 1)) expect_snapshot(error = TRUE, date_floor(x, "day", origin = new_datetime(NA_real_, zone))) expect_snapshot(error = TRUE, date_floor(x, "day", origin = new_datetime(Inf, zone))) expect_snapshot(error = TRUE, date_floor(x, "day", origin = new_datetime(c(0, 1), zone))) expect_snapshot(error = TRUE, date_floor(x, "day", origin = new_datetime(0, ""))) expect_snapshot(error = TRUE, date_floor(x, "day", origin = new_datetime(0, "America/Los_Angeles"))) }) # ------------------------------------------------------------------------------ # date_ceiling() test_that("can ceiling", { x <- c("1970-01-01", "1970-01-07", "1970-01-08") x <- as.POSIXct(x, "America/New_York") expect_identical(date_ceiling(x, "week"), x[c(1, 3, 3)]) expect_identical(date_ceiling(x, "week", n = 2), as.POSIXct(c("1970-01-01", "1970-01-15", "1970-01-15"), "America/New_York")) }) # ------------------------------------------------------------------------------ # date_round() test_that("can round", { x <- c("1970-01-01", "1970-01-03", "1970-01-05", "1970-01-08") x <- as.POSIXct(x, "America/New_York") expect_identical(date_round(x, "week"), x[c(1, 1, 4, 4)]) expect_identical(date_round(x, "week", n = 2), as.POSIXct(c("1970-01-01", "1970-01-01", "1970-01-01", "1970-01-15"), "America/New_York")) }) # ------------------------------------------------------------------------------ # date_weekday_factor() test_that("can convert to a weekday factor", { x <- as.POSIXct("2019-01-01", tz = "America/New_York") + 0:6 expect_identical( date_weekday_factor(x), weekday_factor(as_weekday(x)), ) expect_identical( date_weekday_factor(x, labels = "fr", abbreviate = FALSE, encoding = "iso"), weekday_factor(as_weekday(x), labels = "fr", abbreviate = FALSE, encoding = "iso"), ) }) # ------------------------------------------------------------------------------ # date_month_factor() test_that("can convert to a month factor", { x <- add_months(as.POSIXct("2019-01-01", tz = "America/New_York"), 0:11) expect_identical( date_month_factor(x, labels = "fr", abbreviate = TRUE), calendar_month_factor(as_year_month_day(x), labels = "fr", abbreviate = TRUE), ) }) # ------------------------------------------------------------------------------ # date_format() test_that("default format is correct", { x <- date_time_parse("2019-01-01 00:00:00", "America/New_York") expect_snapshot(date_format(x)) }) test_that("can format date-times", { x <- as.POSIXct("2018-12-31 23:59:59", "America/New_York") formats <- test_all_formats() expect_snapshot( vapply( X = formats, FUN = function(format) date_format(x, format = format), FUN.VALUE = character(1) ) ) expect_snapshot( vapply( X = formats, FUN = function(format) date_format(x, format = format, locale = clock_locale("fr")), FUN.VALUE = character(1) ) ) }) # ------------------------------------------------------------------------------ # date_time_zone() test_that("can get the zone of a POSIXt", { ct <- as.POSIXct("2019-01-01", "America/New_York") lt <- as.POSIXlt(ct) expect_identical(date_time_zone(ct), "America/New_York") expect_identical(date_time_zone(lt), "America/New_York") }) test_that("`date_time_zone()` has a special error on Dates", { expect_snapshot(error = TRUE, date_time_zone(new_date(0))) }) test_that("`date_time_zone()` validates `x`", { expect_snapshot(error = TRUE, date_time_zone(1)) }) # ------------------------------------------------------------------------------ # date_time_set_zone() test_that("can set the zone of a POSIXt", { ct <- as.POSIXct("2019-01-01", "America/New_York") lt <- as.POSIXlt(ct) expect <- as.POSIXct("2018-12-31 21:00:00", "America/Los_Angeles") expect_identical(date_time_set_zone(ct, "America/Los_Angeles"), expect) expect_identical(date_time_set_zone(lt, "America/Los_Angeles"), expect) }) test_that("`date_time_set_zone()` has a special error on Dates", { expect_snapshot(error = TRUE, date_time_set_zone(new_date(), "UTC")) }) test_that("`date_time_set_zone()` validates `x`", { expect_snapshot(error = TRUE, date_time_set_zone(1, "UTC")) }) # ------------------------------------------------------------------------------ # date_time_parse() test_that("can parse into a POSIXct", { expect_identical( date_time_parse("2019-12-31 23:59:59", "America/New_York"), as.POSIXct("2019-12-31 23:59:59", tz = "America/New_York") ) }) test_that("can resolve ambiguity and nonexistent times", { expect_snapshot(error = TRUE, date_time_parse("1970-04-26 02:30:00", "America/New_York")) expect_identical( date_time_parse("1970-04-26 02:30:00", "America/New_York", nonexistent = "roll-forward"), date_time_parse("1970-04-26 03:00:00", "America/New_York") ) expect_snapshot(error = TRUE, date_time_parse("1970-10-25 01:30:00", "America/New_York")) expect_identical( date_time_parse("1970-10-25 01:30:00", "America/New_York", ambiguous = "earliest"), add_seconds(date_time_parse("1970-10-25 00:30:00", "America/New_York"), 3600) ) expect_identical( date_time_parse("1970-10-25 01:30:00", "America/New_York", ambiguous = "latest"), add_seconds(date_time_parse("1970-10-25 00:30:00", "America/New_York"), 7200) ) }) test_that("failure to parse throws a warning", { expect_warning(date_time_parse("foo", "America/New_York"), class = "clock_warning_parse_failures") expect_snapshot(date_time_parse("foo", "America/New_York")) }) # ------------------------------------------------------------------------------ # date_time_parse_complete() test_that("can parse into a POSIXct", { expect_identical( date_time_parse_complete("2019-12-31T23:59:59-05:00[America/New_York]"), as.POSIXct("2019-12-31 23:59:59", tz = "America/New_York") ) }) test_that("ambiguity is resolved through the string", { expect_identical( date_time_parse_complete("1970-10-25T01:30:00-04:00[America/New_York]"), add_seconds(date_time_parse("1970-10-25 00:30:00", "America/New_York"), 3600) ) expect_identical( date_time_parse_complete("1970-10-25T01:30:00-05:00[America/New_York]"), add_seconds(date_time_parse("1970-10-25 00:30:00", "America/New_York"), 7200) ) }) test_that("throws warning on failed parses", { expect_warning(date_time_parse_complete("foo"), class = "clock_warning_parse_failures") expect_snapshot(date_time_parse_complete("foo")) }) # ------------------------------------------------------------------------------ # date_time_parse_abbrev() test_that("can parse into a POSIXct using the abbrev", { expect_identical( date_time_parse_abbrev("2019-01-01 01:02:03 EST", "America/New_York"), as.POSIXct(as_naive_time(year_month_day(2019, 1, 1, 1, 2, 3)), "America/New_York") ) }) test_that("ambiguity is resolved through the string", { expect_identical( date_time_parse_abbrev("1970-10-25 01:30:00 EDT", "America/New_York"), add_seconds(date_time_parse("1970-10-25 00:30:00", "America/New_York"), 3600) ) expect_identical( date_time_parse_abbrev("1970-10-25 01:30:00 EST", "America/New_York"), add_seconds(date_time_parse("1970-10-25 00:30:00", "America/New_York"), 7200) ) }) test_that("abbrev - throws warning on failed parses", { expect_warning(date_time_parse_abbrev("foo", "America/New_York"), class = "clock_warning_parse_failures") expect_snapshot(date_time_parse_abbrev("foo", "America/New_York")) }) # ------------------------------------------------------------------------------ # date_time_parse_RFC_3339() test_that("can parse default RFC 3339 format", { x <- "2019-01-01T00:00:01Z" expect_identical( date_time_parse_RFC_3339(x), date_time_build(2019, 1, 1, 0, 0, 1, zone = "UTC") ) }) test_that("`offset` is passed through", { x <- "2019-01-01T00:00:01-03:30" expect_identical( date_time_parse_RFC_3339(x, offset = "%Ez"), as_date_time(sys_time_parse_RFC_3339(x, offset = "%Ez"), zone = "UTC") ) }) test_that("`separator` is passed through", { x <- "2019-01-01 00:00:01Z" expect_identical( date_time_parse_RFC_3339(x, separator = " "), as_date_time(sys_time_parse_RFC_3339(x, separator = " "), zone = "UTC") ) }) # ------------------------------------------------------------------------------ # date_shift() test_that("can shift date times", { x <- date_time_parse("2019-01-01 01:30:35", "America/New_York") x <- x + c(0, 1) monday <- weekday(clock_weekdays$monday) tuesday <- weekday(clock_weekdays$tuesday) expect_identical( date_shift(x, monday), add_days(x, 6) ) expect_identical( date_shift(x, monday, which = "previous"), add_days(x, -1) ) expect_identical( date_shift(x, tuesday, boundary = "advance"), add_days(x, 7) ) }) test_that("`ambiguous = x` retains the offset of `x` if applicable", { x <- date_time_parse("1970-10-25 01:30:00", "America/New_York", ambiguous = "earliest") x <- x + c(0, 3600) expect_snapshot(error = TRUE, date_shift(x, as_weekday(x), ambiguous = "error")) expect_identical(date_shift(x, as_weekday(x)), x) }) # ------------------------------------------------------------------------------ # date_time_build() test_that("can build a POSIXct", { zone <- "America/New_York" expect_identical(date_time_build(2019, zone = zone), as.POSIXct("2019-01-01", tz = zone)) expect_identical(date_time_build(2020, 2, 3, 4, 5, 6, zone = zone), as.POSIXct("2020-02-03 04:05:06", tz = zone)) }) test_that("`zone` is required", { expect_snapshot(error = TRUE, date_time_build(2019)) }) test_that("can handle invalid dates", { zone <- "America/New_York" expect_snapshot(error = TRUE, date_time_build(2019, 1:12, 31, zone = zone)) expect_identical( date_time_build(2019, 1:12, 31, zone = zone, invalid = "previous-day"), date_time_build(2019, 1:12, "last", zone = zone) ) }) test_that("can handle nonexistent times", { zone <- "America/New_York" expect_snapshot(error = TRUE, date_time_build(1970, 4, 26, 2, 30, zone = zone)) expect_identical( date_time_build(1970, 4, 26, 2, 30, zone = zone, nonexistent = "roll-forward"), date_time_build(1970, 4, 26, 3, zone = zone) ) }) test_that("can handle ambiguous times", { zone <- "America/New_York" expect_snapshot(error = TRUE, date_time_build(1970, 10, 25, 1, 30, zone = zone)) expect_identical( date_time_build(1970, 10, 25, 1, 30, zone = zone, ambiguous = "earliest"), date_time_parse_complete("1970-10-25T01:30:00-04:00[America/New_York]") ) expect_identical( date_time_build(1970, 10, 25, 1, 30, zone = zone, ambiguous = "latest"), date_time_parse_complete("1970-10-25T01:30:00-05:00[America/New_York]") ) }) # ------------------------------------------------------------------------------ # date_now() test_that("can get the current date-time", { x <- date_now("America/New_York") expect_length(x, 1) expect_s3_class(x, "POSIXct") }) # ------------------------------------------------------------------------------ # date_time_info() test_that("can get info of a date time (#295)", { zone <- "America/New_York" x <- date_time_build(2019, 1, 1, zone = zone) x <- date_time_info(x) begin <- date_time_build(2018, 11, 4, 1, zone = zone, ambiguous = "latest") end <- date_time_build(2019, 03, 10, 3, zone = zone) expect_identical(x$begin, begin) expect_identical(x$end, end) expect_identical(x$offset, -18000L) expect_identical(x$dst, FALSE) expect_identical(x$abbreviation, "EST") }) test_that("`NA` propagates", { x <- date_time_build(NA, zone = "UTC") info <- date_time_info(x) expect_identical(info$begin, x) expect_identical(info$end, x) expect_identical(info$offset, NA_integer_) expect_identical(info$dst, NA) expect_identical(info$abbreviation, NA_character_) }) test_that("boundaries are handled right", { x <- date_time_build(2019, 1, 1, zone = "UTC") x <- date_time_info(x) # Only snapshotting in case boundaries are different on CRAN expect_snapshot(x$begin) expect_snapshot(x$end) expect_identical(x$offset, 0L) expect_identical(x$dst, FALSE) expect_identical(x$abbreviation, "UTC") }) test_that("works with POSIXlt", { x <- date_time_build(2019, 1, 1, zone = "America/New_York") expect_identical( date_time_info(as.POSIXlt(x)), date_time_info(x) ) }) test_that("input must be a date-time", { expect_snapshot(error = TRUE, { date_time_info(1) }) }) # ------------------------------------------------------------------------------ # date_start() test_that("can get the start", { zone <- "America/New_York" x <- date_time_build(2019, 2, 2, 2, 2, 2, zone = zone) expect_identical(date_start(x, "second"), x) expect_identical(date_start(x, "month"), date_time_build(2019, 2, 1, zone = zone)) expect_identical(date_start(x, "year"), date_time_build(2019, 1, 1, zone = zone)) }) test_that("start: can't use invalid precisions", { expect_snapshot(error = TRUE, date_start(date_time_build(2019, zone = "America/New_York"), "quarter")) }) test_that("can resolve nonexistent start issues", { # In Asia/Beirut, DST gap from 2021-03-27 23:59:59 -> 2021-03-28 01:00:00 zone <- "Asia/Beirut" x <- date_time_build(2021, 3, 28, 2, zone = zone) expect_snapshot({ (expect_error(date_start(x, "day"), class = "clock_error_nonexistent_time")) }) expect_identical( date_start(x, "day", nonexistent = "roll-forward"), date_time_build(2021, 3, 28, 1, zone = zone) ) }) test_that("can resolve ambiguous start issues", { # In Asia/Amman, DST fallback from 2021-10-29 00:59:59 -> 2021-10-29 00:00:00 zone <- "Asia/Amman" x <- date_time_build(2021, 10, 29, 2, zone = zone) expect_snapshot({ (expect_error(date_start(x, "day"), class = "clock_error_ambiguous_time")) }) expect_identical( date_start(x, "day", ambiguous = "earliest"), date_time_parse_complete("2021-10-29T00:00:00+03:00[Asia/Amman]") ) expect_identical( date_start(x, "day", ambiguous = "latest"), date_time_parse_complete("2021-10-29T00:00:00+02:00[Asia/Amman]") ) }) test_that("can automatically resolve ambiguous issues", { # In Asia/Amman, DST fallback from 2021-10-29 00:59:59 -> 2021-10-29 00:00:00 zone <- "Asia/Amman" # Starts and ends the manipulation in the "earliest" hour x <- date_time_build(2021, 10, 29, 0, 20, zone = zone, ambiguous = "earliest") expect_identical( date_start(x, "day"), date_time_parse_complete("2021-10-29T00:00:00+03:00[Asia/Amman]") ) # Starts and ends the manipulation in the "latest" hour x <- date_time_build(2021, 10, 29, 0, 20, zone = zone, ambiguous = "latest") expect_identical( date_start(x, "day"), date_time_parse_complete("2021-10-29T00:00:00+02:00[Asia/Amman]") ) }) # ------------------------------------------------------------------------------ # date_end() test_that("can get the end", { zone <- "America/New_York" x <- date_time_build(2019:2020, 2, 2, 2, 2, 2, zone = zone) expect_identical(date_end(x, "day"), date_time_build(2019:2020, 2, 2, 23, 59, 59, zone = zone)) expect_identical(date_end(x, "month"), date_time_build(2019:2020, 2, 28:29, 23, 59, 59, zone = zone)) }) test_that("end: can't use invalid precisions", { expect_snapshot(error = TRUE, date_end(date_time_build(2019, zone = "America/New_York"), "quarter")) }) # ------------------------------------------------------------------------------ # date_seq() test_that("integer `by` means second precision", { expect_identical( date_seq(new_datetime(0), to = new_datetime(5), by = 1), date_seq(new_datetime(0), to = new_datetime(5), by = duration_seconds(1)) ) }) test_that("calendar backed `by` works", { zone <- "America/New_York" expect_identical( date_seq(date_time_build(2019, 1, 1, zone = zone), to = date_time_build(2019, 6, 1, zone = zone), by = duration_months(2)), date_time_build(2019, c(1, 3, 5), 1, zone = zone) ) expect_identical( date_seq(date_time_build(2019, 1, 1, zone = zone), to = date_time_build(2025, 1, 1, zone = zone), by = duration_years(2)), date_time_build(c(2019, 2021, 2023, 2025), 1, 1, zone = zone) ) }) test_that("naive-time backed `by` works", { zone <- "America/New_York" expect_identical( date_seq(date_time_build(2019, 1, 1, zone = zone), to = date_time_build(2019, 1, 6, zone = zone), by = duration_days(2)), date_time_build(2019, 1, c(1, 3, 5), zone = zone) ) }) test_that("sys-time backed `by` works", { zone <- "America/New_York" expect_identical( date_seq(date_time_build(2019, 1, 1, zone = zone), to = date_time_build(2019, 1, 1, 6, zone = zone), by = duration_hours(3)), date_time_build(2019, 1, 1, c(0, 3, 6), zone = zone) ) expect_identical( date_seq(date_time_build(2019, 1, 1, zone = zone), to = date_time_build(2019, 1, 1, 0, 6, zone = zone), by = duration_minutes(3)), date_time_build(2019, 1, 1, 0, c(0, 3, 6), zone = zone) ) }) test_that("sub daily `by` uses sys-time around DST", { zone <- "America/New_York" from <- date_time_build(1970, 4, 26, 1, 30, zone = zone) expect_identical( date_seq(from, by = duration_hours(1), total_size = 3), date_time_build(1970, 4, 26, c(1, 3, 4), 30, zone = zone) ) }) test_that("daily `by` uses naive-time around DST gaps", { zone <- "America/New_York" from <- date_time_build(1970, 4, 25, 2, 30, zone = zone) expect_snapshot(error = TRUE, date_seq(from, by = duration_days(1), total_size = 3)) expect_identical( date_seq(from, by = duration_days(1), total_size = 3, nonexistent = "roll-forward"), date_time_build(1970, 4, c(25, 26, 27), c(2, 3, 2), c(30, 0, 30), zone = zone) ) }) test_that("daily `by` uses naive-time around DST fallbacks", { zone <- "America/New_York" from <- date_time_build(1970, 10, 24, 1, 30, zone = zone) expect_snapshot(error = TRUE, date_seq(from, by = duration_days(1), total_size = 3)) expect_identical( date_seq(from, by = duration_days(1), total_size = 3, ambiguous = "earliest"), date_time_build(1970, 10, c(24, 25, 26), 1, 30, zone = zone, ambiguous = "earliest") ) }) test_that("monthly / yearly `by` uses calendar -> naive-time around DST gaps", { zone <- "America/New_York" from <- date_time_build(1970, 3, 26, 2, 30, zone = zone) expect_snapshot(error = TRUE, date_seq(from, by = duration_months(1), total_size = 3)) expect_identical( date_seq(from, by = duration_months(1), total_size = 3, nonexistent = "roll-forward"), date_time_build(1970, c(3, 4, 5), 26, c(2, 3, 2), c(30, 0, 30), zone = zone) ) }) test_that("monthly / yearly `by` uses calendar -> naive-time around DST fallbacks", { zone <- "America/New_York" from <- date_time_build(1969, 10, 25, 1, 30, zone = zone) expect_snapshot(error = TRUE, date_seq(from, by = duration_years(1), total_size = 3)) expect_identical( date_seq(from, by = duration_years(1), total_size = 3, ambiguous = "earliest"), date_time_build(c(1969, 1970, 1971), 10, 25, 1, 30, zone = zone, ambiguous = "earliest") ) }) test_that("combining `by` with `total_size` works", { zone <- "America/New_York" expect_identical( date_seq(date_time_build(2019, 1, 1, zone = zone), by = 2, total_size = 3), date_time_build(2019, 1, 1, 0, 0, c(0, 2, 4), zone = zone) ) }) test_that("combining `to` with `total_size` works", { zone <- "America/New_York" expect_identical( date_seq(date_time_build(2019, 1, 1, zone = zone), to = date_time_build(2019, 1, 5, zone = zone), total_size = 3), date_time_build(2019, 1, c(1, 3, 5), zone = zone) ) }) test_that("can resolve invalid dates", { zone <- "America/New_York" from <- date_time_build(2019, 1, 31, zone = zone) to <- date_time_build(2019, 5, 31, zone = zone) expect_snapshot(error = TRUE, date_seq(from, to = to, by = duration_months(1))) expect_identical( date_seq(from, to = to, by = duration_months(1), invalid = "previous-day"), date_time_build(2019, 1:5, "last", zone = zone) ) }) test_that("quarterly `by` works", { zone <- "America/New_York" expect_identical( date_seq(date_time_build(2019, 1, 2, zone = zone), by = duration_quarters(1), total_size = 3), date_seq(date_time_build(2019, 1, 2, zone = zone), by = duration_months(3), total_size = 3) ) }) test_that("weekly `by` works", { zone <- "America/New_York" expect_identical( date_seq(date_time_build(2019, 1, 2, zone = zone), by = duration_weeks(1), total_size = 3), date_seq(date_time_build(2019, 1, 2, zone = zone), by = duration_days(7), total_size = 3) ) }) test_that("components of `from` more precise than `by` are restored", { zone <- "America/New_York" expect_identical( date_seq(date_time_build(2019, 2, 3, 2, 2, 2, zone = zone), by = duration_minutes(1), total_size = 2), date_time_build(2019, 2, 3, 2, 2:3, 2, zone = zone) ) expect_identical( date_seq(date_time_build(2019, 2, 3, 2, 2, 2, zone = zone), by = duration_hours(1), total_size = 2), date_time_build(2019, 2, 3, 2:3, 2, 2, zone = zone) ) expect_identical( date_seq(date_time_build(2019, 2, 3, 2, 2, 2, zone = zone), by = duration_days(1), total_size = 2), date_time_build(2019, 2, 3:4, 2, 2, 2, zone = zone) ) expect_identical( date_seq(date_time_build(2019, 2, 3, 2, 2, 2, zone = zone), by = duration_months(1), total_size = 2), date_time_build(2019, 2:3, 3, 2, 2, 2, zone = zone) ) expect_identical( date_seq(date_time_build(2019, 2, 3, 2, 2, 2, zone = zone), by = duration_years(1), total_size = 2), date_time_build(2019:2020, 2, 3, 2, 2, 2, zone = zone) ) }) test_that("components of `to` more precise than `by` must match `from`", { zone <- "America/New_York" expect_snapshot(error = TRUE, date_seq(date_time_build(2019, 1, 1, 2, 3, 20, zone = zone), to = date_time_build(2019, 2, 2, 1, 3, 5, zone = zone), by = duration_minutes(1))) expect_snapshot(error = TRUE, date_seq(date_time_build(2019, 1, 1, zone = zone), to = date_time_build(2019, 2, 2, 2, zone = zone), by = duration_days(1))) expect_snapshot(error = TRUE, date_seq(date_time_build(2019, 1, 1, zone = zone), to = date_time_build(2019, 2, 2, zone = zone), by = duration_months(1))) expect_snapshot(error = TRUE, date_seq(date_time_build(2019, 1, 1, zone = zone), to = date_time_build(2019, 2, 1, 1, zone = zone), by = duration_months(1))) expect_snapshot(error = TRUE, date_seq(date_time_build(2019, 1, 1, zone = zone), to = date_time_build(2019, 1, 2, zone = zone), by = duration_years(1))) }) test_that("seq() with `from > to && by > 0` or `from < to && by > 0` results in length 0 output (#282)", { zone <- "America/New_York" expect_identical( date_seq(date_time_build(2019, 1, 2, second = 1, zone = zone), to = date_time_build(2019, 1, 2, zone = zone), by = 1), date_time_build(integer(), zone = zone) ) expect_identical( date_seq(date_time_build(2019, zone = zone), to = date_time_build(2020, zone = zone), by = -1), date_time_build(integer(), zone = zone) ) }) test_that("`to` must have same time zone as `by`", { expect_snapshot(error = TRUE, date_seq(date_time_build(1970, zone = "UTC"), to = date_time_build(1970, zone = "America/New_York"), by = 1)) }) test_that("validates integerish `by`", { expect_snapshot(error = TRUE, date_seq(new_datetime(1), by = 1.5, total_size = 1)) }) test_that("validates `total_size` early", { expect_snapshot(error = TRUE, date_seq(new_datetime(1), by = 1, total_size = 1.5)) expect_snapshot(error = TRUE, date_seq(new_datetime(1), by = 1, total_size = NA)) expect_snapshot(error = TRUE, date_seq(new_datetime(1), by = 1, total_size = -1)) }) test_that("`to` and `total_size` must not generate a non-fractional sequence", { expect_snapshot(error = TRUE, date_seq(new_datetime(0), to = new_datetime(3), total_size = 3)) }) test_that("requires exactly two optional arguments", { expect_snapshot(error = TRUE, date_seq(new_datetime(1), by = 1)) expect_snapshot(error = TRUE, date_seq(new_datetime(1), total_size = 1)) expect_snapshot(error = TRUE, date_seq(new_datetime(1), to = new_datetime(1))) }) test_that("requires `to` to be POSIXt", { expect_snapshot(error = TRUE, date_seq(new_datetime(1), to = 1, by = 1)) }) test_that("requires year, month, day, hour, minute, or second precision", { expect_snapshot(error = TRUE, date_seq(new_datetime(1), to = new_datetime(2), by = duration_nanoseconds(1))) }) test_that("checks empty dots", { expect_snapshot(error = TRUE, date_seq(new_datetime(1), new_datetime(2))) }) test_that("golden test: ensure that we never allow components of `to` to differ with `from` (#224)", { # DST gap from 01:59:59 -> 03:00:00 on the 26th from <- date_time_build(1970, 04, 25, 02, 30, 00, zone = "America/New_York") to <- date_time_build(1970, 04, 26, 03, 00, 00, zone = "America/New_York") expect_error(date_seq(from, to = to, by = duration_days(1), nonexistent = "shift-forward")) # Could theoretically generate this, where the second element is past `to` #> "1970-04-25 02:30:00 EST" "1970-04-26 03:30:00 EDT" }) # ------------------------------------------------------------------------------ # date_spanning_seq() test_that("generates the regular sequence along the full span", { zone <- "UTC" x <- date_time_build(2020, minute = c(2, 1, 5), zone = zone) expect_identical( date_spanning_seq(x), seq( date_time_build(2020, minute = 1, zone = zone), date_time_build(2020, minute = 5, zone = zone), by = 1 ) ) }) test_that("missing values are removed", { zone <- "America/New_York" x <- date_time_build(2020, second = c(1, NA, 5, 2), zone = zone) expect_identical(date_spanning_seq(x), date_time_build(2020, second = 1:5, zone = zone)) x <- date_time_build(c(NA, NA), zone = zone) expect_identical(date_spanning_seq(x), new_datetime(tzone = zone)) }) test_that("infinite dates are removed", { zone <- "America/New_York" x <- new_datetime(c(0, Inf, 3, 6, -Inf), tzone = zone) expect_identical(date_spanning_seq(x), new_datetime(as.double(0:6), tzone = zone)) x <- new_datetime(c(Inf, -Inf), tzone = zone) expect_identical(date_spanning_seq(x), new_datetime(tzone = zone)) }) test_that("works with empty vectors", { zone <- "America/New_York" x <- date_time_build(integer(), zone = zone) expect_identical(date_spanning_seq(x), x) }) test_that("uses sys-time when generating the sequence (nonexistent)", { # Because this is happening at second precision and `date_seq()` uses sys-time # when generating second precision sequences zone <- "America/New_York" x <- date_time_build(1970, 4, 26, c(1, 3), c(59, 00), c(58, 02), zone = zone) expect_identical( date_spanning_seq(x), vec_c( date_time_build(1970, 4, 26, 1, 59, 58:59, zone = zone), date_time_build(1970, 4, 26, 3, 00, 00:02, zone = zone) ) ) }) test_that("uses sys-time when generating the sequence (ambiguous)", { # Because this is happening at second precision and `date_seq()` uses sys-time # when generating second precision sequences zone <- "America/New_York" x <- date_time_build( 1970, 10, 25, c(1, 1), c(59, 00), c(58, 02), ambiguous = c("earliest", "latest"), zone = zone ) expect_identical( date_spanning_seq(x), vec_c( date_time_build(1970, 10, 25, 1, 59, 58:59, zone = zone, ambiguous = "earliest"), date_time_build(1970, 10, 25, 1, 00, 00:02, zone = zone, ambiguous = "latest") ) ) }) # ------------------------------------------------------------------------------ # date_count_between() test_that("can compute precisions at year / quarter / month / week / day / hour / minute / second", { x <- date_time_build(2019, 1, 5, 5, zone = "UTC") y <- date_time_build(2025, 1, c(4, 6), zone = "UTC") expect_identical(date_count_between(x, y, "year"), c(5L, 6L)) expect_identical(date_count_between(x, y, "quarter"), c(23L, 24L)) expect_identical(date_count_between(x, y, "month"), c(71L, 72L)) y <- date_time_build(2019, 1, c(25, 27), zone = "UTC") expect_identical(date_count_between(x, y, "week"), c(2L, 3L)) expect_identical(date_count_between(x, y, "day"), c(19L, 21L)) y <- date_time_build(2019, 1, 6, c(5, 6), c(59, 0), zone = "UTC") expect_identical(date_count_between(x, y, "hour"), c(24L, 25L)) expect_identical(date_count_between(x, y, "minute"), c(1499L, 1500L)) expect_identical(date_count_between(x, y, "second"), c(89940L, 90000L)) }) test_that("can use posixlt", { x <- as.POSIXlt(date_time_build(2019, 1, 5, 5, zone = "UTC")) y <- as.POSIXlt(date_time_build(2020, 1, 5, c(4, 5), zone = "UTC")) expect_identical(date_count_between(x, y, "year"), c(0L, 1L)) }) test_that("nonexistent times are handled correctly", { x <- date_time_build(1970, 4, 26, 1, 59, 59, zone = "America/New_York") y <- date_time_build(1970, 4, 26, 3, 00, 00, zone = "America/New_York") # sys-time for hour, minute, second expect_identical(date_count_between(x, y, "second"), 1L) expect_identical(date_count_between(x, y, "hour"), 0L) # naive-time for week and day z <- date_time_build(1970, 5, 3, 2, 00, 00, zone = "America/New_York") expect_identical(date_count_between(x, z, "day"), 7L) expect_identical(date_count_between(y, z, "day"), 6L) # calendar (naive) for year and month z <- date_time_build(1970, 5, 26, 2, 30, 00, zone = "America/New_York") expect_identical(date_count_between(x, z, "month"), 1L) expect_identical(date_count_between(y, z, "month"), 0L) }) test_that("ambiguous times are handled correctly", { x <- date_time_build(1970, 10, 25, 1, 00, 01, zone = "America/New_York", ambiguous = "earliest") y <- date_time_build(1970, 10, 25, 1, 00, 01, zone = "America/New_York", ambiguous = "latest") # sys-time for hour, minute, second expect_identical(date_count_between(x, y, "second"), 3600L) expect_identical(date_count_between(x, y, "hour"), 1L) # naive-time for week and day z <- date_time_build(1970, 11, 01, 1, 00, c(00, 02), zone = "America/New_York") expect_identical(date_count_between(x, z, "week"), c(0L, 1L)) expect_identical(date_count_between(y, z, "week"), c(0L, 1L)) expect_identical(date_count_between(x, z, "day"), c(6L, 7L)) expect_identical(date_count_between(y, z, "day"), c(6L, 7L)) # calendar (naive) for year and month z <- date_time_build(1970, 11, 25, 1, 00, c(00, 02), zone = "America/New_York") expect_identical(date_count_between(x, z, "month"), c(0L, 1L)) expect_identical(date_count_between(y, z, "month"), c(0L, 1L)) }) test_that("must use a valid POSIXt precision", { x <- date_time_build(2019, zone = "UTC") expect_snapshot((expect_error(date_count_between(x, x, "millisecond")))) }) test_that("can't count between a POSIXt and a Date", { x <- date_time_build(2019, zone = "UTC") y <- date_build(2019) expect_snapshot((expect_error(date_count_between(x, y, "year")))) }) # ------------------------------------------------------------------------------ # vec_arith() test_that(" op ", { zone <- "America/New_York" new_posixlt <- function(x, zone) { as.POSIXlt(new_datetime(x, zone)) } expect_identical(vec_arith("+", new_datetime(0, zone), duration_years(1)), new_datetime(31536000, zone)) expect_identical(vec_arith("+", new_posixlt(0, zone), duration_years(1)), new_datetime(31536000, zone)) expect_identical(vec_arith("-", new_datetime(0, zone), duration_years(1)), new_datetime(-31536000, zone)) expect_identical(vec_arith("-", new_posixlt(0, zone), duration_years(1)), new_datetime(-31536000, zone)) expect_identical(vec_arith("+", new_datetime(0, zone), duration_seconds(1)), new_datetime(1, zone)) expect_identical(vec_arith("+", new_posixlt(0, zone), duration_seconds(1)), new_datetime(1, zone)) expect_snapshot(error = TRUE, vec_arith("+", new_datetime(0, zone), duration_milliseconds(1))) expect_snapshot(error = TRUE, vec_arith("+", new_posixlt(0, zone), duration_milliseconds(1))) expect_snapshot(error = TRUE, vec_arith("*", new_datetime(0, zone), duration_years(1))) expect_snapshot(error = TRUE, vec_arith("*", new_posixlt(0, zone), duration_years(1))) }) test_that(" op ", { zone <- "America/New_York" new_posixlt <- function(x, zone) { as.POSIXlt(new_datetime(x, zone)) } expect_identical(vec_arith("+", duration_years(1), new_datetime(0, zone)), new_datetime(31536000, zone)) expect_identical(vec_arith("+", duration_years(1), new_posixlt(0, zone)), new_datetime(31536000, zone)) expect_identical(vec_arith("+", duration_seconds(1), new_datetime(0, zone)), new_datetime(1, zone)) expect_identical(vec_arith("+", duration_seconds(1), new_posixlt(0, zone)), new_datetime(1, zone)) expect_snapshot(error = TRUE, vec_arith("-", duration_years(1), new_datetime(0, zone))) expect_snapshot(error = TRUE, vec_arith("-", duration_years(1), new_posixlt(0, zone))) expect_snapshot(error = TRUE, vec_arith("+", duration_milliseconds(1), new_datetime(0, zone))) expect_snapshot(error = TRUE, vec_arith("+", duration_milliseconds(1), new_posixlt(0, zone))) expect_snapshot(error = TRUE, vec_arith("*", duration_years(1), new_datetime(0, zone))) expect_snapshot(error = TRUE, vec_arith("*", duration_years(1), new_posixlt(0, zone))) }) # ------------------------------------------------------------------------------ # slider_plus() / slider_minus() test_that("`slider_plus()` method is registered", { skip_if_not_installed("slider", minimum_version = "0.3.0") zone <- "America/New_York" x <- date_time_build(2019, 1, 1, 3:4, 30, zone = zone) y <- duration_hours(3) expect_identical( slider::slider_plus(x, y), date_time_build(2019, 1, 1, 6:7, 30, zone = zone) ) expect_identical( slider::slider_plus(as.POSIXlt(x), y), date_time_build(2019, 1, 1, 6:7, 30, zone = zone) ) y <- duration_days(2) expect_identical( slider::slider_plus(x, y), date_time_build(2019, 1, 3, 3:4, 30, zone = zone) ) expect_identical( slider::slider_plus(as.POSIXlt(x), y), date_time_build(2019, 1, 3, 3:4, 30, zone = zone) ) }) test_that("`slider_minus()` method is registered", { skip_if_not_installed("slider", minimum_version = "0.3.0") zone <- "America/New_York" x <- date_time_build(2019, 1, 1, 3:4, 30, zone = zone) y <- duration_hours(3) expect_identical( slider::slider_minus(x, y), date_time_build(2019, 1, 1, 0:1, 30, zone = zone) ) expect_identical( slider::slider_minus(as.POSIXlt(x), y), date_time_build(2019, 1, 1, 0:1, 30, zone = zone) ) y <- duration_days(2) expect_identical( slider::slider_minus(x, y), date_time_build(2018, 12, 30, 3:4, 30, zone = zone) ) expect_identical( slider::slider_minus(as.POSIXlt(x), y), date_time_build(2018, 12, 30, 3:4, 30, zone = zone) ) }) test_that("`slide_index()` works with date-times and durations", { skip_if_not_installed("slider", minimum_version = "0.3.0") zone <- "America/New_York" i <- date_time_build(2019, 1, 1, 1:6, zone = zone) x <- seq_along(i) before <- duration_hours(2) after <- duration_hours(1) expect <- list( 1:2, 1:3, 1:4, 2:5, 3:6, 4:6 ) expect_identical( slider::slide_index(x, i, identity, .before = before, .after = after), expect ) expect_identical( slider::slide_index(x, as.POSIXlt(i), identity, .before = before, .after = after), expect ) }) test_that("`slide_index()` with date-times and sys-time based arithmetic is sensible around ambiguous times", { skip_if_not_installed("slider", minimum_version = "0.3.0") zone <- "America/New_York" hour <- c(0, 1, 1, 2, 3) ambiguous <- c("error", "earliest", "latest", "error", "error") i <- date_time_build(1970, 10, 25, hour, zone = zone, ambiguous = ambiguous) x <- seq_along(i) # Sys-time based arithmetic before <- duration_hours(2) expect_identical( slider::slide_index(x, i, identity, .before = before), list( 1L, 1:2, 1:3, 2:4, 3:5 ) ) }) test_that("`slide_index()` with date-times and sys-time based arithmetic is sensible around nonexistent times", { skip_if_not_installed("slider", minimum_version = "0.3.0") zone <- "America/New_York" i <- date_time_build(1970, 4, 26, 1, 59, 59, zone = zone) i <- add_seconds(i, 0:4) x <- seq_along(i) # Sys-time based arithmetic before <- duration_seconds(2) expect_identical( slider::slide_index(x, i, identity, .before = before), list( 1L, 1:2, 1:3, 2:4, 3:5 ) ) }) test_that("`slide_index()` will error on naive-time based arithmetic and ambiguous times", { skip_if_not_installed("slider", minimum_version = "0.3.0") zone <- "America/New_York" i <- date_time_build(1970, 10, 24, 1, zone = zone) x <- seq_along(i) # Naive-time based arithmetic after <- duration_days(1) expect_snapshot(error = TRUE, { slider::slide_index(x, i, identity, .after = after) }) }) test_that("`slide_index()` will error on naive-time based arithmetic and nonexistent times", { skip_if_not_installed("slider", minimum_version = "0.3.0") zone <- "America/New_York" i <- date_time_build(1970, 4, 25, 2, 30, zone = zone) x <- seq_along(i) # Naive-time based arithmetic after <- duration_days(1) expect_snapshot(error = TRUE, { slider::slide_index(x, i, identity, .after = after) }) }) test_that("`slide_index()` will error on calendrical arithmetic and ambiguous times", { skip_if_not_installed("slider", minimum_version = "0.3.0") zone <- "America/New_York" i <- date_time_build(1970, 9, 25, 1, zone = zone) x <- seq_along(i) # Calendrical based arithmetic after <- duration_months(1) expect_snapshot(error = TRUE, { slider::slide_index(x, i, identity, .after = after) }) }) test_that("`slide_index()` will error on calendrical arithmetic and nonexistent times", { skip_if_not_installed("slider", minimum_version = "0.3.0") zone <- "America/New_York" i <- date_time_build(1970, 3, 26, 2, 30, zone = zone) x <- seq_along(i) # Calendrical based arithmetic after <- duration_months(1) expect_snapshot(error = TRUE, { slider::slide_index(x, i, identity, .after = after) }) }) clock/tests/testthat/test-sys-time.R0000644000176200001440000001702314426734503017234 0ustar liggesusers# ------------------------------------------------------------------------------ # sys_time_now() test_that("returns nanosecond precision", { x <- sys_time_now() expect_identical(time_point_precision_attribute(x), PRECISION_NANOSECOND) }) test_that("returns a single sys-time", { x <- sys_time_now() expect_length(x, 1L) expect_s3_class(x, "clock_sys_time") }) # ------------------------------------------------------------------------------ # sys_time_info() test_that("can lookup sys-info", { # One in EST, one in EDT x <- year_month_day(2021, 03, 14, c(01, 03), c(59, 00), c(59, 00)) x <- as_naive_time(x) x <- as_zoned_time(x, "America/New_York") info <- sys_time_info(as_sys_time(x), zoned_time_zone(x)) beginend1 <- as_sys_time(c( year_month_day(2020, 11, 1, 6, 0, 0), year_month_day(2021, 03, 14, 7, 0, 0) )) beginend2 <- as_sys_time(c( year_month_day(2021, 03, 14, 7, 0, 0), year_month_day(2021, 11, 7, 6, 0, 0) )) expect_identical(info$begin, c(beginend1[1], beginend2[1])) expect_identical(info$end, c(beginend1[2], beginend2[2])) expect_identical(info$offset, duration_seconds(c(-18000, -14400))) expect_identical(info$dst, c(FALSE, TRUE)) expect_identical(info$abbreviation, c("EST", "EDT")) }) test_that("`zone` is vectorized and recycled against `x`", { zones <- c("America/New_York", "Australia/Lord_Howe") x <- as_sys_time(year_month_day(2019, 1, 1)) info <- sys_time_info(x, zones) naive_times <- c( as_naive_time(as_zoned_time(x, zones[1])), as_naive_time(as_zoned_time(x, zones[2])) ) expect_identical(as_naive_time(x + info$offset), naive_times) # DST is dependent on the time zone expect_identical(info$dst, c(FALSE, TRUE)) expect_identical(info$abbreviation, c("EST", "+11")) }) test_that("very old times are looked up correctly", { x <- year_month_day(1800, 01, 01) x <- as_sys_time(x) info <- sys_time_info(x, "America/New_York") end <- as_sys_time(year_month_day(1883, 11, 18, 17, 00, 00)) offset <- duration_seconds(-17762) dst <- FALSE abbreviation <- "LMT" expect_identical(info$end, end) expect_identical(info$offset, offset) expect_identical(info$dst, dst) expect_identical(info$abbreviation, abbreviation) }) # ------------------------------------------------------------------------------ # as.character() test_that("as.character() works", { x <- as_sys_time(year_month_day(2019, 1, 1)) expect_identical(as.character(x), "2019-01-01") x <- as_sys_time(year_month_day(2019, 1, 1, 1, 1)) expect_identical(as.character(x), "2019-01-01T01:01") }) # ------------------------------------------------------------------------------ # sys_time_parse() # Note: Most tests are in `naive_time_parse()`. They share an implementation. test_that("can parse day precision", { x <- c("2019-01-01", "2019-01-31") expect_identical( sys_time_parse(x, precision = "day"), as_sys_time(year_month_day(2019, 1, c(1, 31))) ) }) test_that("%z shifts the result by the offset", { x <- "2019-01-01T00:00:00+0100" y <- "2019-01-01T00:00:00-0100" expect_identical( sys_time_parse(x, format = "%Y-%m-%dT%H:%M:%S%z"), as_sys_time(year_month_day(2018, 12, 31, 23, 0, 0)) ) expect_identical( sys_time_parse(y, format = "%Y-%m-%dT%H:%M:%S%z"), as_sys_time(year_month_day(2019, 1, 1, 1, 0, 0)) ) }) test_that("failure to parse throws a warning", { expect_warning(sys_time_parse("foo"), class = "clock_warning_parse_failures") expect_snapshot(sys_time_parse("foo")) }) # ------------------------------------------------------------------------------ # sys_time_parse_RFC_3339() test_that("can parse default RFC 3339 format", { x <- "2019-01-01T00:00:00Z" expect_identical( sys_time_parse_RFC_3339(x), as_sys_time(year_month_day(2019, 1, 1, 0, 0, 0)) ) }) test_that("can parse with fractional seconds", { x <- "2019-01-01T00:00:00.123Z" expect_identical( sys_time_parse_RFC_3339(x, precision = "millisecond"), as_sys_time(year_month_day(2019, 1, 1, 0, 0, 0, 123, subsecond_precision = "millisecond")) ) }) test_that("can parse with alternative separator", { x <- "2019-01-01t00:00:00Z" expect_identical( sys_time_parse_RFC_3339(x, separator = "t"), as_sys_time(year_month_day(2019, 1, 1, 0, 0, 0)) ) x <- "2019-01-01 00:00:00Z" expect_identical( sys_time_parse_RFC_3339(x, separator = " "), as_sys_time(year_month_day(2019, 1, 1, 0, 0, 0)) ) }) test_that("can parse with alternative offset", { x <- "2019-01-01T00:00:00z" expect_identical( sys_time_parse_RFC_3339(x, offset = "z"), as_sys_time(year_month_day(2019, 1, 1, 0, 0, 0)) ) x <- "2019-01-01T00:00:00-0130" expect_identical( sys_time_parse_RFC_3339(x, offset = "%z"), as_sys_time(year_month_day(2019, 1, 1, 1, 30, 0)) ) x <- "2019-01-01T00:00:00-01:30" expect_identical( sys_time_parse_RFC_3339(x, offset = "%Ez"), as_sys_time(year_month_day(2019, 1, 1, 1, 30, 0)) ) }) test_that("`precision` must be at least second", { x <- "2019-01-01T00:00:00Z" expect_snapshot(error = TRUE, sys_time_parse_RFC_3339(x, precision = "day")) }) test_that("`separator` is validated", { x <- "2019-01-01T00:00:00Z" expect_snapshot(error = TRUE, sys_time_parse_RFC_3339(x, separator = 1)) expect_snapshot(error = TRUE, sys_time_parse_RFC_3339(x, separator = "TT")) }) test_that("`offset` is validated", { x <- "2019-01-01T00:00:00Z" expect_snapshot(error = TRUE, sys_time_parse_RFC_3339(x, offset = 1)) expect_snapshot(error = TRUE, sys_time_parse_RFC_3339(x, offset = "ZZ")) }) test_that("sys-time-parse-RFC-3339: empty dots are checked", { x <- "2019-01-01T00:00:00Z" expect_snapshot(error = TRUE, sys_time_parse_RFC_3339(x, 1)) }) # ------------------------------------------------------------------------------ # format() test_that("default format is correct", { expect_snapshot(format(sys_seconds(0))) }) test_that("allows `%z` and `%Z`", { x <- sys_seconds(0) expect_identical(format(x, format = "%z"), "+0000") expect_identical(format(x, format = "%Ez"), "+00:00") expect_identical(format(x, format = "%Z"), "UTC") }) # ------------------------------------------------------------------------------ # as_zoned_time() test_that("empty dots are checked", { expect_snapshot(error = TRUE, as_zoned_time(sys_seconds(), "UTC", 123)) }) # ------------------------------------------------------------------------------ # vec_ptype_full() / vec_ptype_abbr() test_that("`vec_ptype_full()` prints correctly", { expect_snapshot({ vec_ptype_full(sys_days()) vec_ptype_full(sys_seconds(1:5)) }) }) test_that("`vec_ptype_abbr()` prints correctly", { expect_snapshot({ vec_ptype_abbr(sys_days()) vec_ptype_abbr(sys_seconds(1:5)) }) }) # ------------------------------------------------------------------------------ # vec_ptype() test_that("ptype is correct", { base <- sys_days(0) ptype <- sys_days(integer()) for (precision in precision_names()) { if (precision_to_integer(precision) < PRECISION_DAY) { next } x <- time_point_cast(base, precision) expect <- time_point_cast(ptype, precision) expect_identical(vec_ptype(x), expect) } }) # ------------------------------------------------------------------------------ # vec_math() test_that("is.nan() works", { x <- sys_days(c(2019, NA)) expect_identical(is.nan(x), c(FALSE, FALSE)) }) test_that("is.finite() works", { x <- sys_days(c(2019, NA)) expect_identical(is.finite(x), c(TRUE, FALSE)) }) test_that("is.infinite() works", { x <- sys_days(c(2019, NA)) expect_identical(is.infinite(x), c(FALSE, FALSE)) }) clock/tests/testthat/test-gregorian-year-month-day.R0000644000176200001440000010622514427233200022263 0ustar liggesusers# ------------------------------------------------------------------------------ # year_month_day() test_that("helper can create different precisions", { x <- year_month_day(2019, 1:2) expect_identical(get_year(x), c(2019L, 2019L)) expect_identical(get_month(x), 1:2) x <- year_month_day(2019, 1:2, 3) expect_identical(get_day(x), c(3L, 3L)) }) test_that("can create subsecond precision calendars", { x <- year_month_day(2019, 1, 1, 0, 0, 0, 1, subsecond_precision = "millisecond") expect_identical(get_millisecond(x), 1L) x <- year_month_day(2019, 1, 1, 0, 0, 0, 1, subsecond_precision = "microsecond") expect_identical(get_microsecond(x), 1L) x <- year_month_day(2019, 1, 1, 0, 0, 0, 1, subsecond_precision = "nanosecond") expect_identical(get_nanosecond(x), 1L) }) test_that("requires `subsecond_precision` as needed", { expect_snapshot(error = TRUE, { year_month_day(2019, 1, 1, 0, 0, 0, 1) }) }) test_that("validates `subsecond_precision`", { expect_snapshot(error = TRUE, { year_month_day(2019, 1, 1, 0, 0, 0, 1, subsecond_precision = "second") }) }) test_that("validates value ranges", { expect_snapshot(error = TRUE, year_month_day(50000)) expect_snapshot(error = TRUE, year_month_day(2020, 13)) expect_snapshot(error = TRUE, year_month_day(2020, 1, 32)) expect_snapshot(error = TRUE, year_month_day(2020, 1, 1, 24)) expect_snapshot(error = TRUE, year_month_day(2020, 1, 1, 1, 60)) expect_snapshot(error = TRUE, year_month_day(2020, 1, 1, 1, 1, 60)) expect_snapshot(error = TRUE, year_month_day(2020, 1, 1, 1, 1, 1, 1000, subsecond_precision = "millisecond")) expect_snapshot(error = TRUE, year_month_day(2020, 1, 1, 1, 1, 1, 1000000, subsecond_precision = "microsecond")) expect_snapshot(error = TRUE, year_month_day(2020, 1, 1, 1, 1, 1, 1000000000, subsecond_precision = "nanosecond")) }) test_that("can create a date at the boundary", { x <- year_month_day(32767, 12, 31) expect_identical(get_year(x), 32767L) x <- year_month_day(-32767, 1, 1) expect_identical(get_year(x), -32767L) }) test_that("can get the last day of the month", { x <- year_month_day(2019, 1:2, "last") expect_identical(get_day(x), c(31L, 28L)) }) test_that("`NA` propagates through 'last'", { x <- year_month_day(2019, c(1, NA)) x <- set_day(x, "last") expect_identical(get_day(x), c(31L, NA)) }) test_that("ignores values past first `NULL`", { expect_identical(year_month_day(2019, day = 2), year_month_day(2019)) }) test_that("NA values propagate", { x <- year_month_day(2019, 1:3, c(NA, 2, 3), c(3, 4, NA)) expect_identical(is.na(x), c(TRUE, FALSE, TRUE)) }) test_that("names of `year` are not retained", { expect_named(year_month_day(c(x = 1)), NULL) }) # ------------------------------------------------------------------------------ # vec_ptype() test_that("ptype is correct", { base <- year_month_day(1) ptype <- year_month_day(integer()) for (precision in precision_names()) { if (precision == "quarter" || precision == "week") { next } x <- calendar_widen(base, precision) expect <- calendar_widen(ptype, precision) expect_identical(vec_ptype(x), expect) } }) # ------------------------------------------------------------------------------ # vec_proxy() / vec_restore() test_that("proxy is a data frame", { expect_identical(vec_proxy(year_month_day(2019)), data_frame(year = 2019L)) expect_identical(vec_proxy(year_month_day(2019, 1)), data_frame(year = 2019L, month = 1L)) }) test_that("proxy has names on `year`", { x <- set_names(year_month_day(2019, 1, 1), "nm") year <- vec_proxy(x)$year expect_named(year, "nm") }) test_that("restore works", { to <- year_month_day(2019, 1:5) proxy <- vec_slice(vec_proxy(to), 1:2) expect_identical(vec_restore(proxy, to), year_month_day(2019, 1:2)) }) # ------------------------------------------------------------------------------ # vec_ptype_full() test_that("full ptype is correct", { expect_snapshot_output(vec_ptype_full(year_month_day(2019))) expect_snapshot_output(vec_ptype_full(year_month_day(2019, 1, 1))) expect_snapshot_output(vec_ptype_full(year_month_day(2019, 1, 1, 1, 1, 1, 1, subsecond_precision = "nanosecond"))) expect_snapshot_output(vec_ptype_full(year_month_day(2019, 2, 31))) }) # ------------------------------------------------------------------------------ # vec_ptype_abbr() test_that("abbreviated ptype is correct", { expect_snapshot_output(vec_ptype_abbr(year_month_day(2019))) expect_snapshot_output(vec_ptype_abbr(year_month_day(2019, 1, 1))) expect_snapshot_output(vec_ptype_abbr(year_month_day(2019, 1, 1, 1, 1, 1, 1, subsecond_precision = "nanosecond"))) expect_snapshot_output(vec_ptype_abbr(year_month_day(2019, 2, 31))) }) # ------------------------------------------------------------------------------ # get_*() test_that("subsecond precision getters require exact precisions", { milli <- year_month_day(1, 1, 1, 1, 1, 1, 1, subsecond_precision = "millisecond") micro <- year_month_day(1, 1, 1, 1, 1, 1, 1, subsecond_precision = "microsecond") nano <- year_month_day(1, 1, 1, 1, 1, 1, 1, subsecond_precision = "nanosecond") expect_identical(get_millisecond(milli), 1L) expect_identical(get_microsecond(micro), 1L) expect_identical(get_nanosecond(nano), 1L) expect_snapshot(error = TRUE, { get_millisecond(micro) }) expect_snapshot(error = TRUE, { get_microsecond(milli) }) expect_snapshot(error = TRUE, { get_nanosecond(micro) }) }) # ------------------------------------------------------------------------------ # set_*() test_that("setters work", { x <- year_month_day(1L) x <- set_year(x, 2L) expect_identical(get_year(x), 2L) x <- set_month(x, 1L) expect_identical(get_month(x), 1L) x <- set_day(x, 2L) expect_identical(get_day(x), 2L) x <- set_hour(x, 3L) expect_identical(get_hour(x), 3L) x <- set_minute(x, 4L) expect_identical(get_minute(x), 4L) x <- set_second(x, 5L) expect_identical(get_second(x), 5L) ms <- set_millisecond(x, 6L) expect_identical(get_millisecond(ms), 6L) us <- set_microsecond(x, 7L) expect_identical(get_microsecond(us), 7L) ns <- set_nanosecond(x, 8L) expect_identical(get_nanosecond(ns), 8L) }) test_that("setters propagate all missings", { x <- year_month_day(2019, c(1, NA, 3)) x <- set_day(x, c(NA, 2, 4)) expect_identical(vec_detect_missing(x), c(TRUE, TRUE, FALSE)) }) test_that("setters recycling works both ways", { x <- year_month_day(2019) x <- set_month(x, 1:2) expect_identical(x, year_month_day(2019, 1:2)) x <- set_day(x, 1) expect_identical(x, year_month_day(2019, 1:2, 1)) expect_snapshot(error = TRUE, { x <- year_month_day(1:2) y <- 1:3 set_month(x, y) }) }) test_that("setters require integer `value`", { x <- year_month_day(2019, 1, 2, 3, 4, 5) expect_snapshot(error = TRUE, { set_year(x, 1.5) }) expect_snapshot(error = TRUE, { set_month(x, 1.5) }) expect_snapshot(error = TRUE, { set_day(x, 1.5) }) expect_snapshot(error = TRUE, { set_hour(x, 1.5) }) expect_snapshot(error = TRUE, { set_minute(x, 1.5) }) expect_snapshot(error = TRUE, { set_second(x, 1.5) }) expect_snapshot(error = TRUE, { set_millisecond(x, 1.5) }) expect_snapshot(error = TRUE, { set_microsecond(x, 1.5) }) expect_snapshot(error = TRUE, { set_nanosecond(x, 1.5) }) }) test_that("setters check `value` range", { x <- year_month_day(2019, 1, 2, 3, 4, 5) expect_snapshot(error = TRUE, { set_year(x, 100000) }) expect_snapshot(error = TRUE, { set_month(x, 13) }) expect_snapshot(error = TRUE, { set_day(x, 32) }) expect_snapshot(error = TRUE, { set_hour(x, 24) }) expect_snapshot(error = TRUE, { set_minute(x, 60) }) expect_snapshot(error = TRUE, { set_second(x, 60) }) expect_snapshot(error = TRUE, { set_millisecond(x, -1) }) expect_snapshot(error = TRUE, { set_microsecond(x, -1) }) expect_snapshot(error = TRUE, { set_nanosecond(x, -1) }) }) test_that("setters require minimum precision", { expect_snapshot(error = TRUE, { set_day(year_month_day(year = 1), 1) }) expect_snapshot(error = TRUE, { set_hour(year_month_day(year = 1, month = 2), 1) }) expect_snapshot(error = TRUE, { set_minute(year_month_day(year = 1, month = 2, day = 3), 1) }) expect_snapshot(error = TRUE, { set_second(year_month_day(year = 1, month = 2, day = 3, hour = 4), 1) }) expect_snapshot(error = TRUE, { set_millisecond(year_month_day(year = 1, month = 2, day = 3, hour = 4, minute = 5), 1) }) expect_snapshot(error = TRUE, { set_microsecond(year_month_day(year = 1, month = 2, day = 3, hour = 4, minute = 5), 1) }) expect_snapshot(error = TRUE, { set_nanosecond(year_month_day(year = 1, month = 2, day = 3, hour = 4, minute = 5), 1) }) }) test_that("setters require correct subsecond precision", { expect_snapshot(error = TRUE, { set_millisecond(year_month_day(year = 1, month = 2, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "microsecond"), 1) }) expect_snapshot(error = TRUE, { set_millisecond(year_month_day(year = 1, month = 2, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "nanosecond"), 1) }) expect_snapshot(error = TRUE, { set_microsecond(year_month_day(year = 1, month = 2, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "millisecond"), 1) }) expect_snapshot(error = TRUE, { set_microsecond(year_month_day(year = 1, month = 2, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "nanosecond"), 1) }) expect_snapshot(error = TRUE, { set_nanosecond(year_month_day(year = 1, month = 2, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "millisecond"), 1) }) expect_snapshot(error = TRUE, { set_nanosecond(year_month_day(year = 1, month = 2, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "microsecond"), 1) }) }) test_that("setters retain names", { x <- year_month_day(2019) x <- set_names(x, "foo") expect_named(set_month(x, 2), "foo") }) test_that("setting with named `value` strips its names", { x <- year_month_day(2019) x <- set_month(x, set_names(1L, "x")) expect_named(field(x, "month"), NULL) }) # ------------------------------------------------------------------------------ # as_year_quarter_day() test_that("invalid dates must be resolved when converting to another calendar", { expect_snapshot(error = TRUE, as_year_quarter_day(year_month_day(2019, 2, 31))) }) # ------------------------------------------------------------------------------ # as_sys_time() test_that("invalid dates must be resolved when converting to a sys-time", { expect_snapshot(error = TRUE, as_sys_time(year_month_day(2019, 2, 31))) }) # ------------------------------------------------------------------------------ # as_naive_time() test_that("invalid dates must be resolved when converting to a naive-time", { expect_snapshot(error = TRUE, as_naive_time(year_month_day(2019, 2, 31))) }) # ------------------------------------------------------------------------------ # format() test_that("default formats are correct", { expect_snapshot(format(year_month_day(2019))) expect_snapshot(format(year_month_day(2019, 1))) expect_snapshot(format(year_month_day(2019, 1, 1, 1))) expect_snapshot(format(year_month_day(2019, 1, 1, 1, 2, 3, 50, subsecond_precision = "microsecond"))) }) # ------------------------------------------------------------------------------ # as.character() test_that("as.character() works", { x <- year_month_day(2019, 1) y <- year_month_day(2019, 1, 2) expect_identical(as.character(x), format(x)) expect_identical(as.character(y), format(y)) }) # ------------------------------------------------------------------------------ # year_month_day_parse() test_that("default parses at day precision with ISO format", { x <- "2019-01-01" expect_identical( year_month_day_parse(x), year_month_day(2019, 1, 1) ) }) test_that("can parse years or year-months", { x <- "2019" y <- "2019-01" expect_identical( year_month_day_parse(x, precision = "year"), year_month_day(2019) ) expect_identical( year_month_day_parse(y, precision = "month"), year_month_day(2019, 01) ) }) test_that("can parse second and subsecond precision", { x <- "2019-01-01T05:10:20" y <- "2019-01-01T05:10:20.1234" expect_identical( year_month_day_parse(x, precision = "second"), year_month_day(2019, 1, 1, 5, 10, 20) ) expect_identical( year_month_day_parse(y, precision = "millisecond"), year_month_day(2019, 1, 1, 5, 10, 20, 123, subsecond_precision = "millisecond") ) expect_identical( year_month_day_parse(y, precision = "microsecond"), year_month_day(2019, 1, 1, 5, 10, 20, 123400, subsecond_precision = "microsecond") ) }) test_that("can parse invalid dates", { x <- "2019-2-31" expect_identical( year_month_day_parse(x), year_month_day(2019, 2, 31) ) }) test_that("names are retained from the input", { x <- c(foo = "2019-01-01") expect_named(year_month_day_parse(x), "foo") }) test_that("multiple formats can be provided", { x <- c("2019-01", "2020 Jan") formats <- c("%Y %B", "%Y-%m") expect_identical( year_month_day_parse(x, format = formats, precision = "month"), year_month_day(c(2019, 2020), 1) ) }) test_that("failure to parse results in `NA`", { x <- "2020-01-ohno" expect_warning( expect_identical( year_month_day_parse(x), year_month_day(NA, NA, NA) ) ) }) test_that("failure to parse results in a warning", { expect_warning(year_month_day_parse("foo", precision = "year"), class = "clock_warning_parse_failures") expect_warning(year_month_day_parse("foo", precision = "month"), class = "clock_warning_parse_failures") expect_warning(year_month_day_parse("foo", precision = "day"), class = "clock_warning_parse_failures") expect_warning(year_month_day_parse("foo", precision = "hour"), class = "clock_warning_parse_failures") expect_warning(year_month_day_parse("foo", precision = "minute"), class = "clock_warning_parse_failures") expect_warning(year_month_day_parse("foo", precision = "second"), class = "clock_warning_parse_failures") expect_warning(year_month_day_parse("foo", precision = "millisecond"), class = "clock_warning_parse_failures") expect_warning(year_month_day_parse("foo", precision = "microsecond"), class = "clock_warning_parse_failures") expect_warning(year_month_day_parse("foo", precision = "nanosecond"), class = "clock_warning_parse_failures") expect_snapshot(year_month_day_parse("foo")) }) test_that("can use a different locale", { x <- "janvier 05, 2020" y <- "2019-01-01T00:00:00,123456" expect_identical( year_month_day_parse(x, format = "%B %d, %Y", locale = clock_locale("fr")), year_month_day(2020, 1, 5) ) expect_identical( year_month_day_parse(y, locale = clock_locale(decimal_mark = ","), precision = "microsecond"), year_month_day(2019, 1, 1, 0, 0, 0, 123456, subsecond_precision = "microsecond") ) }) test_that("can use a different locale with UTF-8 strings", { x <- c("1月 01 2019", "3月 05 2020") y <- "ለካቲት 01 2019" expect_identical( year_month_day_parse(x, format = "%B %d %Y", locale = clock_locale("ja")), year_month_day(c(2019, 2020), c(1, 3), c(1, 5)) ) expect_identical( year_month_day_parse(y, format = "%B %d %Y", locale = clock_locale("ti")), year_month_day(2019, 2, 1) ) }) test_that("`format` argument is translated to UTF-8", { x <- "f\u00E9v 2019-05-19" format <- "f\u00E9v %Y-%m-%d" format <- iconv(format, from = "UTF-8", to = "latin1") expect_identical(Encoding(x), "UTF-8") expect_identical(Encoding(format), "latin1") expect_identical( year_month_day_parse(x, format = format), year_month_day(2019, 5, 19) ) }) test_that("`x` is translated to UTF-8", { x <- "2019-f\u00E9vrier-01" x <- iconv(x, from = "UTF-8", to = "latin1") locale <- clock_locale("fr") format <- "%Y-%B-%d" expect_identical(Encoding(x), "latin1") expect_identical(Encoding(locale$labels$month[2]), "UTF-8") expect_identical( year_month_day_parse(x, format = format, locale = locale), year_month_day(2019, 2, 1) ) }) test_that("parsing NA returns NA", { expect_identical( year_month_day_parse(NA_character_), year_month_day(NA, NA, NA) ) expect_identical( year_month_day_parse(NA_character_, precision = "month"), year_month_day(NA, NA) ) }) test_that("parsing doesn't round parsed components more precise than the resulting container (#207)", { # With year-month-day, only the year/month/day components are extracted at the end, # the hour component isn't touched expect_identical( year_month_day_parse("2019-12-31 12", format = "%Y-%m-%d %H", precision = "day"), year_month_day(2019, 12, 31) ) }) test_that("parsing rounds parsed subsecond components more precise than the resulting container (#207)", { # Requesting `%7S` parses the full `01.1238`, and the `1238` portion is rounded up immediately # after parsing the `%S` command, not at the very end expect_identical( year_month_day_parse("2019-01-01 01:01:01.1238", format = "%Y-%m-%d %H:%M:%7S", precision = "millisecond"), year_month_day(2019, 1, 1, 1, 1, 1, 124, subsecond_precision = "millisecond") ) }) # ------------------------------------------------------------------------------ # calendar_group() test_that("works with negative years", { year <- c(-2, -1, 0, 1, 2) x <- year_month_day(year, 1, 1) expect_identical(calendar_group(x, "year"), year_month_day(year)) expect_identical(calendar_group(x, "year", n = 2), year_month_day(c(-2, -2, 0, 0, 2))) }) # ------------------------------------------------------------------------------ # calendar_narrow() test_that("can narrow to month", { x_expect <- year_month_day(2019, 2) x <- set_day(x_expect, 1) expect_identical(calendar_narrow(x, "month"), x_expect) expect_identical(calendar_narrow(x_expect, "month"), x_expect) }) test_that("can narrow to day", { x_expect <- year_month_day(2019, 2, 3) x <- set_hour(x_expect, 5) expect_identical(calendar_narrow(x, "day"), x_expect) expect_identical(calendar_narrow(x_expect, "day"), x_expect) }) test_that("can narrow to hour", { x_expect <- year_month_day(2019, 2, 3, 4) x <- set_minute(x_expect, 5) expect_identical(calendar_narrow(x, "hour"), x_expect) expect_identical(calendar_narrow(x_expect, "hour"), x_expect) }) test_that("can narrow to minute", { x_expect <- year_month_day(2019, 2, 3, 4, 5) x <- set_second(x_expect, 6) expect_identical(calendar_narrow(x, "minute"), x_expect) expect_identical(calendar_narrow(x_expect, "minute"), x_expect) }) test_that("can narrow to second", { expect <- year_month_day(2019, 2, 3, 4, 5, 6) x <- set_millisecond(expect, 7) y <- set_nanosecond(expect, 7) expect_identical(calendar_narrow(x, "second"), expect) expect_identical(calendar_narrow(y, "second"), expect) expect_identical(calendar_narrow(expect, "second"), expect) }) # ------------------------------------------------------------------------------ # calendar_widen() test_that("can widen to month", { x <- year_month_day(2019) expect_identical(calendar_widen(x, "month"), set_month(x, 1)) }) test_that("can widen to day", { x <- year_month_day(2019) y <- year_month_day(2019, 02) expect_identical(calendar_widen(x, "day"), set_day(set_month(x, 1), 1)) expect_identical(calendar_widen(y, "day"), set_day(y, 1)) }) test_that("can widen to hour", { x <- year_month_day(2019) y <- year_month_day(2019, 02, 02) expect_identical(calendar_widen(x, "hour"), set_hour(set_day(set_month(x, 1), 1), 0)) expect_identical(calendar_widen(y, "hour"), set_hour(y, 0)) }) test_that("can widen to minute", { x <- year_month_day(2019) y <- year_month_day(2019, 02, 02, 02) x_expect <- year_month_day(2019, 1, 1, 0, 0) y_expect <- set_minute(y, 0) expect_identical(calendar_widen(x, "minute"), x_expect) expect_identical(calendar_widen(y, "minute"), y_expect) }) test_that("can widen to second", { x <- year_month_day(2019) y <- year_month_day(2019, 02, 02, 02, 02) x_expect <- year_month_day(2019, 1, 1, 0, 0, 0) y_expect <- set_second(y, 0) expect_identical(calendar_widen(x, "second"), x_expect) expect_identical(calendar_widen(y, "second"), y_expect) }) test_that("can widen to subsecond precision", { x <- year_month_day(2019) y <- year_month_day(2019, 02, 02, 02, 02, 02) x_expect <- year_month_day(2019, 1, 1, 0, 0, 0, 0, subsecond_precision = "microsecond") y_expect <- set_nanosecond(y, 0) expect_identical(calendar_widen(x, "microsecond"), x_expect) expect_identical(calendar_widen(y, "nanosecond"), y_expect) }) # ------------------------------------------------------------------------------ # calendar_start() test_that("can compute year start", { x <- year_month_day(2019) expect_identical(calendar_start(x, "year"), x) x <- year_month_day(2019, 2, 2, 2, 2, 2, 2, subsecond_precision = "millisecond") expect <- year_month_day(2019, 1, 1, 0, 0, 0, 0, subsecond_precision = "millisecond") expect_identical(calendar_start(x, "year"), expect) }) test_that("can compute month start", { x <- year_month_day(2019, 2) expect_identical(calendar_start(x, "month"), x) x <- year_month_day(2019, 2, 2, 2, 2, 2, 2, subsecond_precision = "microsecond") expect <- year_month_day(2019, 2, 1, 0, 0, 0, 0, subsecond_precision = "microsecond") expect_identical(calendar_start(x, "month"), expect) }) # ------------------------------------------------------------------------------ # calendar_end() test_that("can compute year end", { x <- year_month_day(2019) expect_identical(calendar_end(x, "year"), x) x <- year_month_day(2019, 2, 2, 2, 2, 2, 2, subsecond_precision = "millisecond") expect <- year_month_day(2019, 12, 31, 23, 59, 59, 999L, subsecond_precision = "millisecond") expect_identical(calendar_end(x, "year"), expect) }) test_that("can compute month end", { x <- year_month_day(2019, 2) expect_identical(calendar_end(x, "month"), x) x <- year_month_day(2019, 2:3, 2, 2, 2, 2, 2, subsecond_precision = "microsecond") expect <- year_month_day(2019, 2:3, c(28, 31), 23, 59, 59, 999999L, subsecond_precision = "microsecond") expect_identical(calendar_end(x, "month"), expect) }) # ------------------------------------------------------------------------------ # calendar_month_factor() test_that("can get a month factor", { expect_identical( calendar_month_factor(year_month_day(2019, 1:12)), factor(month.name, levels = month.name, ordered = TRUE) ) }) test_that("can abbreviate month names", { expect_identical( calendar_month_factor(year_month_day(2019, 1:12), abbreviate = TRUE), factor(month.abb, levels = month.abb, ordered = TRUE) ) }) test_that("can adjust labels language", { labels <- clock_labels_lookup("fr")$month expect_identical( calendar_month_factor(year_month_day(2019, 1:12), labels = "fr"), factor(labels, levels = labels, ordered = TRUE) ) }) test_that("requires month precision", { expect_snapshot(error = TRUE, calendar_month_factor(year_month_day(2019))) }) test_that("`labels` is validated", { expect_snapshot(error = TRUE, calendar_month_factor(year_month_day(2019, 1), labels = 1)) }) test_that("`abbreviate` is validated", { expect_snapshot(error = TRUE, calendar_month_factor(year_month_day(2019, 1), abbreviate = "foo")) expect_snapshot(error = TRUE, calendar_month_factor(year_month_day(2019, 1), abbreviate = 1)) expect_snapshot(error = TRUE, calendar_month_factor(year_month_day(2019, 1), abbreviate = c(TRUE, FALSE))) }) # ------------------------------------------------------------------------------ # calendar_count_between() test_that("can compute year and month counts", { x <- year_month_day(2019, 1, 1) y <- year_month_day(2020, 3, 4) expect_identical(calendar_count_between(x, y, "year"), 1L) expect_identical(calendar_count_between(x, y, "month"), 14L) expect_identical(calendar_count_between(x, y, "month", n = 2), 7L) }) test_that("can compute a quarter count", { x <- year_month_day(2019, 1, 2) y <- year_month_day(2019, 4, c(1, 3)) expect_identical(calendar_count_between(x, y, "quarter"), c(0L, 1L)) expect_identical( calendar_count_between(x, y, "quarter"), calendar_count_between(x, y, "month", n = 3L) ) y <- year_month_day(2020, 4, c(1, 3)) expect_identical(calendar_count_between(x, y, "quarter", n = 2L), c(2L, 2L)) expect_identical( calendar_count_between(x, y, "quarter", n = 2L), calendar_count_between(x, y, "month", n = 6L) ) }) test_that("can't compute a unsupported count precision", { x <- year_month_day(2019, 1, 1) expect_snapshot((expect_error(calendar_count_between(x, x, "day")))) }) test_that("positive / negative counts are correct", { start <- year_month_day(1972, 03, 04) end <- year_month_day(1973, 03, 03) expect_identical(calendar_count_between(start, end, "year"), 0L) expect_identical(calendar_count_between(start, end, "month"), 11L) end <- year_month_day(1973, 03, 04) expect_identical(calendar_count_between(start, end, "year"), 1L) expect_identical(calendar_count_between(start, end, "month"), 12L) end <- year_month_day(1973, 03, 05) expect_identical(calendar_count_between(start, end, "year"), 1L) expect_identical(calendar_count_between(start, end, "month"), 12L) end <- year_month_day(1971, 03, 03) expect_identical(calendar_count_between(start, end, "year"), -1L) expect_identical(calendar_count_between(start, end, "month"), -12L) end <- year_month_day(1971, 03, 04) expect_identical(calendar_count_between(start, end, "year"), -1L) expect_identical(calendar_count_between(start, end, "month"), -12L) end <- year_month_day(1971, 03, 05) expect_identical(calendar_count_between(start, end, "year"), 0L) expect_identical(calendar_count_between(start, end, "month"), -11L) }) # ------------------------------------------------------------------------------ # seq() test_that("only granular precisions are allowed", { expect_snapshot(error = TRUE, seq(year_month_day(2019, 1, 1), by = 1, length.out = 2)) }) test_that("seq(to, by) works", { expect_identical(seq(year_month_day(2019, 1), to = year_month_day(2019, 6), by = 2), year_month_day(2019, c(1, 3, 5))) expect_identical(seq(year_month_day(2019, 1), to = year_month_day(2019, 5), by = 2), year_month_day(2019, c(1, 3, 5))) expect_identical(seq(year_month_day(2019, 1), to = year_month_day(2018, 9), by = -2), year_month_day(c(2019, 2018, 2018), c(1, 11, 9))) expect_identical(seq(year_month_day(2019, 1), to = year_month_day(2018, 8), by = -2), year_month_day(c(2019, 2018, 2018), c(1, 11, 9))) }) test_that("seq(to, length.out) works", { expect_identical(seq(year_month_day(2019, 1), to = year_month_day(2019, 5), length.out = 2), year_month_day(2019, c(1, 5))) expect_identical(seq(year_month_day(2019, 1), to = year_month_day(2019, 5), length.out = 1), year_month_day(2019, 1)) expect_identical(seq(year_month_day(2019, 1), to = year_month_day(2019, 5), length.out = 5), year_month_day(2019, 1:5)) expect_identical(seq(year_month_day(2019, 1), to = year_month_day(2019, 5), along.with = 1:2), year_month_day(2019, c(1, 5))) }) test_that("seq(by, length.out) works", { expect_identical(seq(year_month_day(2019, 1), by = 2, length.out = 3), year_month_day(2019, c(1, 3, 5))) expect_identical(seq(year_month_day(2019, 1), by = -2, length.out = 3), year_month_day(c(2019, 2018, 2018), c(1, 11, 9))) expect_identical(seq(year_month_day(2019, 1), by = 2, along.with = 1:3), year_month_day(2019, c(1, 3, 5))) }) test_that("`by` can be a duration", { expect_identical( seq(year_month_day(2019, 1), to = year_month_day(2025, 5), by = duration_years(1)), seq(year_month_day(2019, 1), to = year_month_day(2025, 5), by = 12) ) expect_identical( seq(year_month_day(2019, 3), by = duration_years(1), length.out = 5), seq(year_month_day(2019, 3), by = 12, length.out = 5) ) }) # ------------------------------------------------------------------------------ # invalid_detect() test_that("`invalid_detect()` works", { # Not possible to be invalid x <- year_month_day(2019:2020, 1) expect_identical(invalid_detect(x), c(FALSE, FALSE)) # Now possible x <- year_month_day(2019, 2, c(1, 28, 31, NA)) expect_identical(invalid_detect(x), c(FALSE, FALSE, TRUE, FALSE)) # Possible after that too x <- year_month_day(2019, 2, c(1, 28, 31, NA)) expect_identical(invalid_detect(x), c(FALSE, FALSE, TRUE, FALSE)) }) # ------------------------------------------------------------------------------ # invalid_any() test_that("`invalid_any()` works", { # Not possible to be invalid x <- year_month_day(2019:2020, 1) expect_false(invalid_any(x)) # Now possible x <- year_month_day(2019, 2, c(1, 28, 31, NA)) expect_true(invalid_any(x)) # Possible after that too x <- year_month_day(2019, 2, c(1, 28, 31, NA)) expect_true(invalid_any(x)) }) # ------------------------------------------------------------------------------ # invalid_count() test_that("`invalid_count()` works", { # Not possible to be invalid x <- year_month_day(2019:2020, 1) expect_identical(invalid_count(x), 0L) # Now possible x <- year_month_day(2019, 2, c(1, 28, 31, NA)) expect_identical(invalid_count(x), 1L) # Possible after that too x <- year_month_day(2019, 2, c(1, 28, 31, NA), 3) expect_identical(invalid_count(x), 1L) }) # ------------------------------------------------------------------------------ # invalid_resolve() test_that("strict mode can be activated", { local_options(clock.strict = TRUE) expect_snapshot(error = TRUE, invalid_resolve(year_month_day(2019, 1, 1))) }) test_that("can resolve correctly", { x <- year_month_day(2019, 02, 31, 2, 3, 4, 5, subsecond_precision = "millisecond") expect_identical( invalid_resolve(x, invalid = "previous"), year_month_day(2019, 02, 28, 23, 59, 59, 999, subsecond_precision = "millisecond") ) expect_identical( invalid_resolve(x, invalid = "previous-day"), year_month_day(2019, 02, 28, 2, 3, 4, 5, subsecond_precision = "millisecond") ) expect_identical( invalid_resolve(x, invalid = "next"), year_month_day(2019, 03, 01, 0, 0, 0, 0, subsecond_precision = "millisecond") ) expect_identical( invalid_resolve(x, invalid = "next-day"), year_month_day(2019, 03, 01, 2, 3, 4, 5, subsecond_precision = "millisecond") ) expect_identical( invalid_resolve(x, invalid = "overflow"), year_month_day(2019, 03, 03, 0, 0, 0, 0, subsecond_precision = "millisecond") ) expect_identical( invalid_resolve(x, invalid = "overflow-day"), year_month_day(2019, 03, 03, 2, 3, 4, 5, subsecond_precision = "millisecond") ) expect_identical( invalid_resolve(x, invalid = "NA"), year_month_day(NA, NA, NA, NA, NA, NA, NA, subsecond_precision = "millisecond") ) }) test_that("throws known classed error", { expect_snapshot(error = TRUE, invalid_resolve(year_month_day(2019, 2, 31))) expect_error(invalid_resolve(year_month_day(2019, 2, 31)), class = "clock_error_invalid_date") }) test_that("works with always valid precisions", { x <- year_month_day(2019:2020) expect_identical(invalid_resolve(x), x) }) # ------------------------------------------------------------------------------ # vec_math() test_that("is.nan() works", { x <- year_month_day(c(2019, NA)) expect_identical(is.nan(x), c(FALSE, FALSE)) }) test_that("is.finite() works", { x <- year_month_day(c(2019, NA)) expect_identical(is.finite(x), c(TRUE, FALSE)) }) test_that("is.infinite() works", { x <- year_month_day(c(2019, NA)) expect_identical(is.infinite(x), c(FALSE, FALSE)) }) # ------------------------------------------------------------------------------ # clock_minimum() / clock_maximum() test_that("minimums are right", { expect_snapshot({ clock_minimum(clock_empty_year_month_day_year) clock_minimum(clock_empty_year_month_day_month) clock_minimum(clock_empty_year_month_day_day) clock_minimum(clock_empty_year_month_day_hour) clock_minimum(clock_empty_year_month_day_minute) clock_minimum(clock_empty_year_month_day_second) clock_minimum(clock_empty_year_month_day_millisecond) clock_minimum(clock_empty_year_month_day_microsecond) clock_minimum(clock_empty_year_month_day_nanosecond) }) }) test_that("maximums are right", { expect_snapshot({ clock_maximum(clock_empty_year_month_day_year) clock_maximum(clock_empty_year_month_day_month) clock_maximum(clock_empty_year_month_day_day) clock_maximum(clock_empty_year_month_day_hour) clock_maximum(clock_empty_year_month_day_minute) clock_maximum(clock_empty_year_month_day_second) clock_maximum(clock_empty_year_month_day_millisecond) clock_maximum(clock_empty_year_month_day_microsecond) clock_maximum(clock_empty_year_month_day_nanosecond) }) }) # ------------------------------------------------------------------------------ # min() / max() / range() test_that("min() / max() / range() works", { x <- year_month_day(c(1, 3, 2, 1, -1)) expect_identical(min(x), year_month_day(-1)) expect_identical(max(x), year_month_day(3)) expect_identical(range(x), year_month_day(c(-1, 3))) }) test_that("min() / max() / range() works with `NA`", { x <- year_month_day(c(1, NA, 2, 0)) expect_identical(min(x), year_month_day(NA)) expect_identical(max(x), year_month_day(NA)) expect_identical(range(x), year_month_day(c(NA, NA))) expect_identical(min(x, na.rm = TRUE), year_month_day(0)) expect_identical(max(x, na.rm = TRUE), year_month_day(2)) expect_identical(range(x, na.rm = TRUE), year_month_day(c(0, 2))) }) test_that("min() / max() / range() works when empty", { x <- year_month_day(integer()) expect_identical(min(x), clock_maximum(x)) expect_identical(max(x), clock_minimum(x)) expect_identical(range(x), c(clock_maximum(x), clock_minimum(x))) x <- year_month_day(c(NA, NA)) expect_identical(min(x, na.rm = TRUE), clock_maximum(x)) expect_identical(max(x, na.rm = TRUE), clock_minimum(x)) expect_identical(range(x, na.rm = TRUE), c(clock_maximum(x), clock_minimum(x))) }) # ------------------------------------------------------------------------------ # add_*() test_that("add_years() works", { x <- year_month_day(2019, 1, 2, 3:4) expect_identical( add_years(x, 1:2), year_month_day(c(2020, 2021), 1, 2, 3:4) ) expect_identical( add_years(x, NA), vec_init(x, 2L) ) }) test_that("add_months() works", { x <- year_month_day(2019, 1, 2, 3:4) expect_identical( add_months(x, 1:2), year_month_day(2019, 2:3, 2, 3:4) ) expect_identical( add_months(x, NA), vec_init(x, 2L) ) }) test_that("add_quarters() works (special)", { x <- year_month_day(2019, 1, 2, 3:4) expect_identical( add_quarters(x, 1:2), add_months(x, (1:2) * 3) ) expect_identical( add_quarters(x, NA), vec_init(x, 2L) ) }) test_that("add_*() respect recycling rules", { expect_length(add_years(year_month_day(1), 1:2), 2L) expect_length(add_years(year_month_day(1:2), 1), 2L) expect_length(add_years(year_month_day(1), integer()), 0L) expect_length(add_years(year_month_day(integer()), 1), 0L) expect_snapshot(error = TRUE, { add_years(year_month_day(1:2), 1:3) }) }) test_that("add_*() retains names", { x <- set_names(year_month_day(1), "x") y <- year_month_day(1) n <- set_names(1, "n") expect_named(add_years(x, n), "x") expect_named(add_years(y, n), "n") }) clock/tests/testthat/test-time-point.R0000644000176200001440000004345214426734572017562 0ustar liggesusers# ------------------------------------------------------------------------------ # print() / obj_print_data() / obj_print_footer() test_that("normal print method works", { x <- as_sys_time(year_month_day(2019, 1:5, 1)) expect_snapshot(x) }) test_that("can limit with `max`", { x <- as_sys_time(year_month_day(2019, 1:5, 1)) expect_snapshot(print(x, max = 2)) expect_snapshot(print(x, max = 4)) # no footer if length >= max expect_snapshot(print(x, max = 5)) expect_snapshot(print(x, max = 6)) }) test_that("`max` defaults to `getOption('max.print')` but can be overridden", { local_options(max.print = 3) x <- as_naive_time(year_month_day(2019, 1:5, 1)) expect_snapshot(x) expect_snapshot(print(x, max = 4)) expect_snapshot(print(x, max = 5)) }) # ------------------------------------------------------------------------------ # time_point_floor() / _ceiling() / _round() test_that("can round to less precise precision", { x <- naive_seconds(c(-86401, -86400, -86399, 0, 86399, 86400, 86401)) floor <- naive_days(c(-2, -1, -1, 0, 0, 1, 1)) ceiling <- naive_days(c(-1, -1, 0, 0, 1, 1, 2)) round <- naive_days(c(-1, -1, -1, 0, 1, 1, 1)) expect_identical(time_point_floor(x, "day"), floor) expect_identical(time_point_ceiling(x, "day"), ceiling) expect_identical(time_point_round(x, "day"), round) floor <- naive_days(c(-2, -2, -2, 0, 0, 0, 0)) ceiling <- naive_days(c(0, 0, 0, 0, 2, 2, 2)) round <- naive_days(c(-2, 0, 0, 0, 0, 2, 2)) expect_identical(time_point_floor(x, "day", n = 2), floor) expect_identical(time_point_ceiling(x, "day", n = 2), ceiling) expect_identical(time_point_round(x, "day", n = 2), round) }) test_that("can round with `origin` altering starting point", { x <- sys_seconds(c(-86401, -86400, -86399, 0, 86399, 86400, 86401)) origin <- sys_days(-1) floor <- sys_days(c(-3, -1, -1, -1, -1, 1, 1)) ceiling <- sys_days(c(-1, -1, 1, 1, 1, 1, 3)) round <- sys_days(c(-1, -1, -1, 1, 1, 1, 1)) expect_identical(time_point_floor(x, "day", origin = origin, n = 2), floor) expect_identical(time_point_ceiling(x, "day", origin = origin, n = 2), ceiling) expect_identical(time_point_round(x, "day", origin = origin, n = 2), round) }) test_that("cannot floor to more precise precision", { expect_snapshot(error = TRUE, time_point_floor(naive_days(), "second")) }) test_that("rounding with `origin` requires same clock", { origin <- sys_days(0) x <- naive_days(0) expect_snapshot(error = TRUE, time_point_floor(x, "day", origin = origin)) }) test_that("`origin` can be cast to a more precise `precision`, but not to a less precise one", { origin1 <- as_naive_time(duration_days(1)) origin2 <- as_naive_time(duration_milliseconds(0)) x <- naive_seconds(0) expect_identical( time_point_floor(x, "hour", origin = origin1, n = 5), time_point_floor(x - as_duration(origin1), "hour", n = 5) + as_duration(origin1) ) expect_snapshot(error = TRUE, time_point_floor(x, "hour", origin = origin2)) }) test_that("`origin` must be size 1", { origin <- naive_days(0:1) x <- naive_days(0) expect_snapshot(error = TRUE, time_point_floor(x, "day", origin = origin)) }) test_that("`origin` must not be `NA`", { origin <- naive_days(NA) x <- naive_days(0) expect_snapshot(error = TRUE, time_point_floor(x, "day", origin = origin)) }) test_that("`origin` can't be Date or POSIXt", { origin1 <- new_date(0) origin2 <- new_datetime(0, "America/New_York") x <- naive_days(0) expect_snapshot(error = TRUE, time_point_floor(x, "day", origin = origin1)) expect_snapshot(error = TRUE, time_point_floor(x, "day", origin = origin2)) }) # ------------------------------------------------------------------------------ # time_point_shift() test_that("can shift to next weekday", { expect_identical( time_point_shift( naive_days(0:1), weekday(clock_weekdays$sunday) ), naive_days(c(3, 3)) ) }) test_that("can shift to next if on the boundary", { naive_sunday <- naive_days(3) sunday <- weekday(clock_weekdays$sunday) expect_identical( time_point_shift(naive_sunday, sunday), naive_sunday ) expect_identical( time_point_shift(naive_sunday, sunday, boundary = "advance"), naive_sunday + 7 ) }) test_that("can shift to previous weekday", { expect_identical( time_point_shift( naive_days(0:1), weekday(clock_weekdays$sunday), which = "previous" ), naive_days(c(-4, -4)) ) }) test_that("can shift to previous weekday if on boundary", { naive_sunday <- naive_days(3) sunday <- weekday(clock_weekdays$sunday) expect_identical( time_point_shift(naive_sunday, sunday, which = "previous"), naive_sunday ) expect_identical( time_point_shift(naive_sunday, sunday, which = "previous", boundary = "advance"), naive_sunday - 7 ) }) test_that("`target` is recycled to size of `x`", { expect_identical( time_point_shift( sys_days(0:1), weekday(1:2) ), sys_days(3:4) ) expect_snapshot(error = TRUE, time_point_shift(sys_days(0), weekday(1:2))) }) test_that("`x` is validated", { expect_snapshot(error = TRUE, time_point_shift(1)) }) test_that("`target` is validated", { expect_snapshot(error = TRUE, time_point_shift(sys_days(0), 1)) }) test_that("`which` is validated", { expect_snapshot(error = TRUE, time_point_shift(sys_days(), weekday(), which = 1)) expect_snapshot(error = TRUE, time_point_shift(sys_days(), weekday(), which = "foo")) expect_snapshot(error = TRUE, time_point_shift(sys_days(), weekday(), which = c("next", "previous"))) }) test_that("`boundary` is validated", { expect_snapshot(error = TRUE, time_point_shift(sys_days(), weekday(), boundary = 1)) expect_snapshot(error = TRUE, time_point_shift(sys_days(), weekday(), boundary = "foo")) expect_snapshot(error = TRUE, time_point_shift(sys_days(), weekday(), boundary = c("keep", "advance"))) }) # ------------------------------------------------------------------------------ # time_point_count_between() test_that("can count units between", { x <- as_naive_time(year_month_day(1990, 02, 03, 04)) y <- as_naive_time(year_month_day(1995, 04, 05, 03)) expect_identical(time_point_count_between(x, y, "day"), 1886L) expect_identical(time_point_count_between(x, y, "hour"), 45287L) }) test_that("'week' is an allowed precision", { x <- sys_days(0) y <- sys_days(13:15) expect_identical(time_point_count_between(x, y, "week"), c(1L, 2L, 2L)) }) test_that("`n` affects the result", { x <- sys_days(0) y <- sys_days(10) expect_identical(time_point_count_between(x, y, "day", n = 2L), 5L) expect_identical(time_point_count_between(x, y, "day", n = 3L), 3L) }) test_that("negative vs positive differences are handled correctly", { one_hour <- duration_hours(1) x <- sys_days(0) y <- sys_days(1) z <- sys_days(-1) expect_identical(time_point_count_between(x, y - one_hour, "day"), 0L) expect_identical(time_point_count_between(x, y, "day"), 1L) expect_identical(time_point_count_between(x, y + one_hour, "day"), 1L) expect_identical(time_point_count_between(x, z - one_hour, "day"), -1L) expect_identical(time_point_count_between(x, z, "day"), -1L) expect_identical(time_point_count_between(x, z + one_hour, "day"), 0L) }) test_that("common precision of inputs and `precision` is taken", { expect_identical( time_point_count_between(sys_days(0), sys_days(2) + duration_hours(1), "second"), 176400L ) expect_identical( time_point_count_between(sys_seconds(0), sys_seconds(86401), "day"), 1L ) }) test_that("OOB results return a warning and NA", { expect_snapshot({ out <- time_point_count_between(sys_days(0), sys_days(1000), "nanosecond") }) expect_identical(out, NA_integer_) }) test_that("both inputs must be time points", { expect_snapshot({ (expect_error(time_point_count_between(sys_days(1), 1))) (expect_error(time_point_count_between(1, sys_days(1)))) }) }) test_that("both inputs must be compatible", { x <- sys_days(1) y <- naive_days(1) expect_snapshot((expect_error( time_point_count_between(x, y) ))) }) test_that("`n` is validated", { x <- sys_days(1) expect_snapshot({ (expect_error(time_point_count_between(x, x, "day", n = NA_integer_))) (expect_error(time_point_count_between(x, x, "day", n = -1))) (expect_error(time_point_count_between(x, x, "day", n = 1.5))) (expect_error(time_point_count_between(x, x, "day", n = "x"))) (expect_error(time_point_count_between(x, x, "day", n = c(1L, 2L)))) }) }) test_that("`precision` must be a time point precision", { x <- sys_days(1) expect_snapshot((expect_error( time_point_count_between(x, x, "year") ))) }) # ------------------------------------------------------------------------------ # seq() test_that("seq(to, by) works", { expect_identical(seq(sys_days(0L), to = sys_days(4L), by = 2), sys_days(c(0L, 2L, 4L))) expect_identical(seq(sys_days(0L), to = sys_days(5L), by = 2), sys_days(c(0L, 2L, 4L))) expect_identical(seq(sys_seconds(0L), to = sys_seconds(-4L), by = -2), sys_seconds(c(0L, -2L, -4L))) expect_identical(seq(sys_seconds(0L), to = sys_seconds(-5L), by = -2), sys_seconds(c(0L, -2L, -4L))) }) test_that("seq(to, length.out) works", { expect_identical(seq(naive_days(0L), to = naive_days(4L), length.out = 2), naive_days(c(0L, 4L))) expect_identical(seq(naive_days(0L), to = naive_days(4L), length.out = 1), naive_days(c(0L))) expect_identical(seq(naive_days(0L), to = naive_days(4L), length.out = 5), naive_days(c(0:4))) expect_identical(seq(naive_seconds(0L), to = naive_seconds(4L), along.with = 1:2), naive_seconds(c(0L, 4L))) }) test_that("seq(by, length.out) works", { expect_identical(seq(naive_seconds(0L), by = 2, length.out = 3), naive_seconds(c(0L, 2L, 4L))) expect_identical(seq(naive_seconds(0L), by = -2, length.out = 3), naive_seconds(c(0L, -2L, -4L))) expect_identical(seq(naive_seconds(0L), by = 2, along.with = 1:3), naive_seconds(c(0L, 2L, 4L))) }) test_that("seq() with `from > to && by > 0` or `from < to && by < 0` results in length 0 output (#282)", { expect_identical(seq(naive_days(2L), to = naive_days(1L), by = 1), naive_days()) expect_identical(seq(naive_days(5L), to = naive_days(1L), by = 1), naive_days()) expect_identical(seq(naive_days(1L), to = naive_days(2L), by = -1), naive_days()) expect_identical(seq(naive_days(1L), to = naive_days(5L), by = -1), naive_days()) # In particular, handles the case where subtraction of distant `from` and `to` would overflow x <- as_naive_time(duration_cast(duration_years(200), "nanosecond")) y <- as_naive_time(duration_cast(duration_years(-200), "nanosecond")) expect_identical(seq(x, y, by = 1), as_naive_time(duration_nanoseconds())) expect_identical(seq(y, x, by = -1), as_naive_time(duration_nanoseconds())) }) test_that("`by` can be a duration", { expect_identical( seq(naive_seconds(0), to = naive_seconds(1000), by = duration_minutes(1)), seq(naive_seconds(0), to = naive_seconds(1000), by = 60) ) expect_identical( seq(as_naive_time(duration_nanoseconds(0)), to = as_naive_time(duration_nanoseconds(2e9)), by = duration_seconds(1)), seq(as_naive_time(duration_nanoseconds(0)), to = as_naive_time(duration_nanoseconds(2e9)), by = 1e9) ) }) test_that("can't mix chronological time points and calendrical durations", { expect_snapshot(error = TRUE, seq(naive_seconds(0), by = duration_years(1), length.out = 2)) }) test_that("can't mix clocks in seq()", { expect_snapshot(error = TRUE, seq(sys_seconds(0), to = naive_seconds(5), by = 1)) }) test_that("`to` is always cast to `from`", { expect_identical( seq(naive_seconds(0), to = naive_days(12), by = duration_days(2)), seq(naive_seconds(0), to = naive_seconds(12 * 86400), by = 86400 * 2) ) expect_snapshot(error = TRUE, seq(naive_days(0), to = naive_seconds(5), by = 2)) }) test_that("can make nanosecond precision seqs", { x <- as_naive_time(duration_nanoseconds(0)) y <- as_naive_time(duration_nanoseconds(10)) expect_identical(seq(x, by = 2, length.out = 5), x + c(0, 2, 4, 6, 8)) expect_identical(seq(x, y, by = 3), x + c(0, 3, 6, 9)) }) # ------------------------------------------------------------------------------ # time_point_spanning_seq() test_that("generates the regular sequence along the full span", { x <- naive_days(c(-5, 5, 6, 0)) expect_identical(time_point_spanning_seq(x), naive_days(-5:6)) }) test_that("missing values are removed", { x <- naive_days(c(1, NA, 0, 2)) expect_identical(time_point_spanning_seq(x), naive_days(0:2)) x <- naive_days(c(NA, NA)) expect_identical(time_point_spanning_seq(x), naive_days()) }) test_that("works with empty vectors", { x <- naive_days() expect_identical(time_point_spanning_seq(x), x) }) test_that("validates the input", { expect_snapshot(error = TRUE, { time_point_spanning_seq(1) }) }) # ------------------------------------------------------------------------------ # vec_arith() test_that("duration to add to a time-point must have at least week precision (#120)", { expect_snapshot(error = TRUE, naive_seconds(0) + duration_years(1)) }) # ------------------------------------------------------------------------------ # time_point_precision() test_that("precision: can get the precision", { expect_identical(time_point_precision(as_naive_time(duration_days(2:5))), "day") expect_identical(time_point_precision(as_naive_time(duration_nanoseconds(2:5))), "nanosecond") }) test_that("precision: can only be called on time points", { expect_snapshot(error = TRUE, time_point_precision(duration_days())) }) # ------------------------------------------------------------------------------ # add_*() test_that("unsupported time point addition throws good error", { x <- naive_seconds() expect_snapshot(error = TRUE, { add_years(x, 1) }) expect_snapshot(error = TRUE, { add_quarters(x, 1) }) expect_snapshot(error = TRUE, { add_months(x, 1) }) }) # ------------------------------------------------------------------------------ # clock_minimum() / clock_maximum() test_that("minimums are right", { skip_if_not_on_os("mac") # Known that the only time point that prints the limits right is nanosecond # due to how the print method goes through year-month-day and the year there # isn't high enough expect_snapshot({ # clock_minimum(as_sys_time(duration_days())) # clock_minimum(as_sys_time(duration_hours())) # clock_minimum(as_sys_time(duration_minutes())) # clock_minimum(as_sys_time(duration_seconds())) # clock_minimum(as_sys_time(duration_milliseconds())) # clock_minimum(as_sys_time(duration_microseconds())) clock_minimum(as_sys_time(duration_nanoseconds())) # clock_minimum(as_naive_time(duration_days())) # clock_minimum(as_naive_time(duration_hours())) # clock_minimum(as_naive_time(duration_minutes())) # clock_minimum(as_naive_time(duration_seconds())) # clock_minimum(as_naive_time(duration_milliseconds())) # clock_minimum(as_naive_time(duration_microseconds())) clock_minimum(as_naive_time(duration_nanoseconds())) }) for (precision in precision_names()) { precision <- precision_to_integer(precision) if (precision < PRECISION_DAY) { next } x <- duration_helper(0L, precision) expect_identical(as_duration(clock_minimum(as_sys_time(x))), clock_minimum(x)) expect_identical(as_duration(clock_minimum(as_naive_time(x))), clock_minimum(x)) } }) test_that("maximums are right", { skip_if_not_on_os("mac") # Known that the only time point that prints the limits right is nanosecond # due to how the print method goes through year-month-day and the year there # isn't high enough expect_snapshot({ # clock_maximum(as_sys_time(duration_days())) # clock_maximum(as_sys_time(duration_hours())) # clock_maximum(as_sys_time(duration_minutes())) # clock_maximum(as_sys_time(duration_seconds())) # clock_maximum(as_sys_time(duration_milliseconds())) # clock_maximum(as_sys_time(duration_microseconds())) clock_maximum(as_sys_time(duration_nanoseconds())) # clock_maximum(as_naive_time(duration_days())) # clock_maximum(as_naive_time(duration_hours())) # clock_maximum(as_naive_time(duration_minutes())) # clock_maximum(as_naive_time(duration_seconds())) # clock_maximum(as_naive_time(duration_milliseconds())) # clock_maximum(as_naive_time(duration_microseconds())) clock_maximum(as_naive_time(duration_nanoseconds())) }) for (precision in precision_names()) { precision <- precision_to_integer(precision) if (precision < PRECISION_DAY) { next } x <- duration_helper(0L, precision) expect_identical(as_duration(clock_maximum(as_sys_time(x))), clock_maximum(x)) expect_identical(as_duration(clock_maximum(as_naive_time(x))), clock_maximum(x)) } }) # ------------------------------------------------------------------------------ # min() / max() / range() test_that("min() / max() / range() works", { x <- naive_days(c(1, 3, 2, 1, -1)) expect_identical(min(x), naive_days(-1)) expect_identical(max(x), naive_days(3)) expect_identical(range(x), naive_days(c(-1, 3))) }) test_that("min() / max() / range() works with `NA`", { x <- naive_days(c(1, NA, 2, 0)) expect_identical(min(x), naive_days(NA)) expect_identical(max(x), naive_days(NA)) expect_identical(range(x), naive_days(c(NA, NA))) expect_identical(min(x, na.rm = TRUE), naive_days(0)) expect_identical(max(x, na.rm = TRUE), naive_days(2)) expect_identical(range(x, na.rm = TRUE), naive_days(c(0, 2))) }) test_that("min() / max() / range() works when empty", { x <- naive_days() expect_identical(min(x), clock_maximum(x)) expect_identical(max(x), clock_minimum(x)) expect_identical(range(x), c(clock_maximum(x), clock_minimum(x))) x <- naive_days(c(NA, NA)) expect_identical(min(x, na.rm = TRUE), clock_maximum(x)) expect_identical(max(x, na.rm = TRUE), clock_minimum(x)) expect_identical(range(x, na.rm = TRUE), c(clock_maximum(x), clock_minimum(x))) }) clock/tests/testthat/test-clock-codes.R0000644000176200001440000000235314011271365017640 0ustar liggesuserstest_that("codes are correct", { expect_identical(clock_months$january, 1L) expect_identical(clock_months$february, 2L) expect_identical(clock_months$march, 3L) expect_identical(clock_months$april, 4L) expect_identical(clock_months$may, 5L) expect_identical(clock_months$june, 6L) expect_identical(clock_months$july, 7L) expect_identical(clock_months$august, 8L) expect_identical(clock_months$september, 9L) expect_identical(clock_months$october, 10L) expect_identical(clock_months$november, 11L) expect_identical(clock_months$december, 12L) expect_identical(clock_weekdays$sunday, 1L) expect_identical(clock_weekdays$monday, 2L) expect_identical(clock_weekdays$tuesday, 3L) expect_identical(clock_weekdays$wednesday, 4L) expect_identical(clock_weekdays$thursday, 5L) expect_identical(clock_weekdays$friday, 6L) expect_identical(clock_weekdays$saturday, 7L) expect_identical(clock_iso_weekdays$monday, 1L) expect_identical(clock_iso_weekdays$tuesday, 2L) expect_identical(clock_iso_weekdays$wednesday, 3L) expect_identical(clock_iso_weekdays$thursday, 4L) expect_identical(clock_iso_weekdays$friday, 5L) expect_identical(clock_iso_weekdays$saturday, 6L) expect_identical(clock_iso_weekdays$sunday, 7L) }) clock/tests/testthat/test-duration.R0000644000176200001440000005140714427204257017312 0ustar liggesusers# ------------------------------------------------------------------------------ # duration_precision_common_cpp() test_that("correctly computes common duration precision", { granular <- c( PRECISION_YEAR, PRECISION_QUARTER, PRECISION_MONTH ) precise <- c( PRECISION_WEEK, PRECISION_DAY, PRECISION_HOUR, PRECISION_MINUTE, PRECISION_SECOND, PRECISION_MILLISECOND, PRECISION_MICROSECOND, PRECISION_NANOSECOND ) for (p1 in granular) { for (p2 in granular) { expect_identical(duration_precision_common_cpp(p1, p2), max(p1, p2)) } } for (p1 in precise) { for (p2 in precise) { expect_identical(duration_precision_common_cpp(p1, p2), max(p1, p2)) } } for (p1 in granular) { for (p2 in precise) { expect_identical(duration_precision_common_cpp(p1, p2), NA_integer_) } } }) # ------------------------------------------------------------------------------ # duration_floor() / _ceiling() / _round() test_that("floor rounds down", { x <- duration_days(2) + duration_seconds(-1:1) x <- c(-x, x) expect2 <- duration_seconds(c(-172800, -172800, -172802, 172798, 172800, 172800)) expect3 <- duration_days(c(-2, -2, -3, 1, 2, 2)) expect4 <- duration_days(c(-2, -2, -4, 0, 2, 2)) expect_identical(duration_floor(x, "second"), x) expect_identical(duration_floor(x, "second", n = 2), expect2) expect_identical(duration_floor(x, "day"), expect3) expect_identical(duration_floor(x, "day", n = 2), expect4) }) test_that("ceiling rounds up", { x <- duration_days(2) + duration_seconds(-1:1) x <- c(-x, x) expect2 <- duration_seconds(c(-172798, -172800, -172800, 172800, 172800, 172802)) expect3 <- duration_days(c(-1, -2, -2, 2, 2, 3)) expect4 <- duration_days(c(0, -2, -2, 2, 2, 4)) expect_identical(duration_ceiling(x, "second"), x) expect_identical(duration_ceiling(x, "second", n = 2), expect2) expect_identical(duration_ceiling(x, "day"), expect3) expect_identical(duration_ceiling(x, "day", n = 2), expect4) }) test_that("round rounds to nearest, ties round up", { x <- duration_days(2) + duration_seconds(-1:3) x <- c(-x, x) expect2 <- duration_seconds(c(-172800, -172800, -172800, -172800, -172804, 172800, 172800, 172800, 172804, 172804)) expect3 <- duration_days(c(-2, -2, -2, -2, -2, 2, 2, 2, 2, 2)) expect4 <- duration_days(c(0, 0, -4, -4, -4, 0, 4, 4, 4, 4)) expect_identical(duration_round(x, "second"), x) expect_identical(duration_round(x, "second", n = 4), expect2) expect_identical(duration_round(x, "day"), expect3) expect_identical(duration_round(x, "day", n = 4), expect4) }) test_that("can't round to more precise precision", { expect_error(duration_floor(duration_seconds(1), "millisecond"), "more precise") }) test_that("can't round across common precision boundary", { expect_snapshot(error = TRUE, duration_ceiling(duration_weeks(), "month")) expect_snapshot(error = TRUE, duration_floor(duration_seconds(), "year")) }) test_that("input is validated", { expect_snapshot(error = TRUE, { duration_floor(1, "year") }) expect_snapshot(error = TRUE, { duration_floor(duration_seconds(1), "foo") }) expect_snapshot(error = TRUE, { duration_floor(duration_seconds(1), "day", n = -1) }) }) # ------------------------------------------------------------------------------ # seq() test_that("seq() validates from", { expect_snapshot(error = TRUE, seq(duration_years(1:2))) expect_snapshot(error = TRUE, seq(duration_years(NA_integer_))) }) test_that("seq() validates length.out / along.with exclusiveness", { expect_snapshot(error = TRUE, seq(duration_years(1L), length.out = 1, along.with = 2)) }) test_that("seq() only takes two optional args", { x <- duration_years(1L) expect_snapshot(error = TRUE, seq(x, to = duration_years(1), by = 1, length.out = 1)) expect_snapshot(error = TRUE, seq(x, to = duration_years(1), by = 1, along.with = 1)) }) test_that("seq() requires two optional args", { x <- duration_years(1L) expect_snapshot(error = TRUE, seq(x, to = duration_years(1))) expect_snapshot(error = TRUE, seq(x, by = 1)) expect_snapshot(error = TRUE, seq(x, length.out = 1)) expect_snapshot(error = TRUE, seq(x, along.with = 1)) }) test_that("seq() validates `to`", { expect_snapshot(error = TRUE, seq(duration_years(1L), to = duration_years(1:2), by = 1)) expect_snapshot(error = TRUE, seq(duration_years(1L), to = 1, by = 1)) expect_snapshot(error = TRUE, seq(duration_years(1L), to = duration_days(1), by = 1)) expect_snapshot(error = TRUE, seq(duration_years(1L), to = duration_years(NA_integer_), by = 1)) }) test_that("seq() validates `by`", { expect_snapshot(error = TRUE, seq(duration_years(1L), to = duration_years(1L), by = 1:2)) expect_snapshot(error = TRUE, seq(duration_years(1L), to = duration_years(1L), by = NA_integer_)) expect_snapshot(error = TRUE, seq(duration_years(1L), to = duration_years(1L), by = 0)) expect_snapshot(error = TRUE, seq(duration_years(1L), to = duration_years(1L), by = duration_years(0))) expect_snapshot(error = TRUE, seq(duration_years(1L), to = duration_years(1L), by = "x")) }) test_that("`by` must be castable to the type of `from`", { expect_snapshot(error = TRUE, seq(duration_years(0), to = duration_years(1), by = duration_months(1))) expect_snapshot(error = TRUE, seq(duration_years(0), to = duration_years(1), by = duration_days(1))) expect_snapshot(error = TRUE, seq(duration_days(0), to = duration_days(1), by = duration_years(1))) }) test_that("seq() validates `length.out`", { expect_snapshot(error = TRUE, seq(duration_years(1L), to = duration_years(1L), length.out = 1:2)) expect_snapshot(error = TRUE, seq(duration_years(1L), to = duration_years(1L), length.out = NA_integer_)) expect_snapshot(error = TRUE, seq(duration_years(1L), to = duration_years(1L), length.out = -1)) expect_snapshot(error = TRUE, seq(duration_years(1L), to = duration_years(1L), length.out = "x")) }) test_that("seq() validates dots", { expect_snapshot(error = TRUE, seq(duration_years(1), duration_years(1), 1, 1, 1, 1)) }) test_that("seq() enforces non-fractional results", { expect_snapshot(error = TRUE, seq(duration_years(1L), to = duration_years(2L), length.out = 3)) expect_snapshot(error = TRUE, seq(duration_years(1L), to = duration_years(2L), along.with = 1:3)) }) test_that("seq() works when from and to are identical", { expect_identical(seq(duration_years(1L), to = duration_years(1L), by = 1), duration_years(1L)) expect_identical(seq(duration_years(1L), to = duration_years(1L), by = -1), duration_years(1L)) }) test_that("seq() with `from > to && by > 0` or `from < to && by < 0` results in length 0 output (#282)", { expect_identical(seq(duration_years(2L), to = duration_years(1L), by = 1), duration_years()) expect_identical(seq(duration_years(5L), to = duration_years(1L), by = 1), duration_years()) expect_identical(seq(duration_years(1L), to = duration_years(2L), by = -1), duration_years()) expect_identical(seq(duration_years(1L), to = duration_years(5L), by = -1), duration_years()) # In particular, handles the case where subtraction of distant `from` and `to` would overflow x <- duration_cast(duration_years(200), "nanosecond") expect_identical(seq(x, -x, by = 1), duration_nanoseconds()) expect_identical(seq(-x, x, by = -1), duration_nanoseconds()) }) test_that("seq(to, by) works", { expect_identical(seq(duration_years(0L), to = duration_years(4L), by = 2), duration_years(c(0L, 2L, 4L))) expect_identical(seq(duration_years(0L), to = duration_years(5L), by = 2), duration_years(c(0L, 2L, 4L))) expect_identical(seq(duration_years(0L), to = duration_years(-4L), by = -2), duration_years(c(0L, -2L, -4L))) expect_identical(seq(duration_years(0L), to = duration_years(-5L), by = -2), duration_years(c(0L, -2L, -4L))) expect_identical(seq(duration_years(4L), to = duration_years(0L), by = -2), duration_years(c(4L, 2L, 0L))) expect_identical(seq(duration_years(4L), to = duration_years(-1L), by = -2), duration_years(c(4L, 2L, 0L))) }) test_that("seq(to, by = ) works", { expect_identical( seq(duration_years(0), to = duration_years(4), by = duration_years(1)), seq(duration_years(0), to = duration_years(4), by = 1) ) expect_identical( seq(duration_months(0), to = duration_months(20), by = duration_years(1)), seq(duration_months(0), to = duration_months(20), by = 12) ) expect_identical( seq(duration_seconds(0), to = duration_seconds(1000), by = duration_minutes(2)), seq(duration_seconds(0), to = duration_seconds(1000), by = 120) ) expect_identical( seq(duration_nanoseconds(0), by = duration_days(2), length.out = 5), duration_nanoseconds(0) + duration_days(c(0, 2, 4, 6, 8)) ) expect_identical( seq(duration_nanoseconds(0), to = duration_days(100000), by = duration_days(10000)), duration_nanoseconds(0) + duration_days(seq(0L, 100000L, by = 10000L)) ) expect_identical( seq(duration_nanoseconds(0), to = -duration_days(100000), by = -duration_days(10000)), duration_nanoseconds(0) - duration_days(seq(0L, 100000L, by = 10000L)) ) }) test_that("seq(to, length.out) works", { expect_identical(seq(duration_years(0L), to = duration_years(4L), length.out = 2), duration_years(c(0L, 4L))) expect_identical(seq(duration_years(0L), to = duration_years(4L), length.out = 1), duration_years(c(0L))) expect_identical(seq(duration_years(0L), to = duration_years(4L), length.out = 5), duration_years(c(0:4))) expect_identical(seq(duration_years(0L), to = duration_years(-4L), length.out = 2), duration_years(c(0L, -4L))) expect_identical(seq(duration_years(0L), to = duration_years(-6L), length.out = 3), duration_years(c(0L, -3L, -6L))) expect_identical(seq(duration_years(0L), to = duration_years(4L), along.with = 1:2), duration_years(c(0L, 4L))) }) test_that("seq(to, length.out = 1) is special cased to return `from`", { expect_identical( seq(duration_years(1), duration_years(5), length.out = 1), duration_years(1) ) }) test_that("seq(by, length.out) works", { expect_identical(seq(duration_years(0L), by = 2, length.out = 3), duration_years(c(0L, 2L, 4L))) expect_identical(seq(duration_years(0L), by = -2, length.out = 3), duration_years(c(0L, -2L, -4L))) expect_identical(seq(duration_years(0L), by = 2, along.with = 1:3), duration_years(c(0L, 2L, 4L))) }) test_that("`to` is always cast to `from`", { expect_identical( seq(duration_months(0), to = duration_years(1), by = 2), seq(duration_months(0), to = duration_months(12), by = 2) ) expect_snapshot(error = TRUE, seq(duration_days(0), to = duration_years(5), by = 2)) expect_snapshot(error = TRUE, seq(duration_years(0), to = duration_months(5), by = 2)) }) test_that("special test to ensure we never lose precision (i.e. by trying to convert to double)", { expect_identical( seq(duration_nanoseconds(0), duration_cast(duration_years(10), "nanosecond"), length.out = 3), duration_nanoseconds(0) + duration_cast(duration_years(c(0, 5, 10)), "nanosecond") ) }) # ------------------------------------------------------------------------------ # duration_spanning_seq() test_that("generates the regular sequence along the full span", { x <- duration_years(c(-5, 5, 6, 0)) expect_identical(duration_spanning_seq(x), duration_years(-5:6)) }) test_that("missing values are removed", { x <- duration_days(c(1, NA, 0, 2)) expect_identical(duration_spanning_seq(x), duration_days(0:2)) x <- duration_days(c(NA, NA)) expect_identical(duration_spanning_seq(x), duration_days()) }) test_that("works with empty vectors", { x <- duration_days() expect_identical(duration_spanning_seq(x), x) }) test_that("validates the input", { expect_snapshot(error = TRUE, { duration_spanning_seq(1) }) }) # ------------------------------------------------------------------------------ # add_*() test_that("can't add chronological and calendrical durations", { expect_snapshot(error = TRUE, add_seconds(duration_years(1), 1)) expect_snapshot(error = TRUE, add_years(duration_seconds(1), 1)) }) # ------------------------------------------------------------------------------ # as_sys_time() / as_naive_time() test_that("can convert week precision duration to time point", { expect_identical(as_sys_time(duration_weeks(c(0, 1))), sys_days(c(0, 7))) expect_identical(as_naive_time(duration_weeks(c(0, 1))), naive_days(c(0, 7))) }) test_that("can't convert calendrical duration to time point", { expect_snapshot(error = TRUE, as_sys_time(duration_years(0))) expect_snapshot(error = TRUE, as_naive_time(duration_years(0))) }) # ------------------------------------------------------------------------------ # as.character() test_that("as.character() works", { x <- duration_days(c(1:2, NA)) expect_identical(as.character(x), c("1", "2", NA_character_)) x <- duration_cast(duration_days(1), "nanosecond") expect_identical(as.character(x), "86400000000000") }) test_that("as.character() retains names", { x <- set_names(duration_days(1), "x") expect_named(as.character(x), "x") }) # ------------------------------------------------------------------------------ # duration_precision() test_that("precision: can get the precision", { expect_identical(duration_precision(duration_months(2:5)), "month") expect_identical(duration_precision(duration_days(1)), "day") expect_identical(duration_precision(duration_nanoseconds(5:6)), "nanosecond") }) test_that("precision: can only be called on durations", { expect_snapshot(error = TRUE, duration_precision(sys_days(0))) }) # ------------------------------------------------------------------------------ # vec_arith() test_that("` / ` is not allowed", { expect_snapshot( (expect_error(duration_years(1) / duration_years(2))) ) }) test_that("` %/% ` works", { expect_identical(duration_years(5) %/% duration_years(2:3), c(2L, 1L)) expect_identical(duration_days(10) %/% duration_hours(7), 34L) }) test_that("` %/% ` handles `0` (#349)", { expect_identical(duration_years(5) %/% duration_years(0), NA_integer_) expect_identical(duration_days(NA) %/% duration_hours(0), NA_integer_) }) test_that("` %/% ` propagates NA", { expect_identical(duration_hours(NA) %/% duration_hours(1), NA_integer_) expect_identical(duration_hours(1) %/% duration_hours(NA), NA_integer_) }) test_that("` %/% ` propagates names", { expect_named(c(x = duration_hours(1)) %/% duration_hours(1:2), c("x", "x")) expect_named(c(x = duration_hours(1)) %/% c(y = duration_hours(1)), "x") expect_named(duration_hours(1) %/% c(y = duration_hours(1)), "y") }) test_that("` %/% ` results in NA for OOB values", { skip_on_cran() one <- duration_hours(1) numerator <- duration_hours(.Machine$integer.max) denominator <- duration_hours(1) expect_identical(numerator %/% denominator, .Machine$integer.max) expect_identical(-numerator %/% denominator, -.Machine$integer.max) expect_snapshot(out <- (numerator + one) %/% denominator) expect_identical(out, NA_integer_) expect_snapshot(out <- (-numerator - one) %/% denominator) expect_identical(out, NA_integer_) }) test_that("` %% ` works", { expect_identical(duration_hours(7) %% duration_hours(3), duration_hours(1)) expect_identical(duration_hours(7) %% duration_hours(4), duration_hours(3)) }) test_that("` %% ` handles `0` (#349)", { expect_identical(duration_hours(7) %% duration_hours(0), duration_hours(NA)) expect_identical(duration_hours(NA) %% duration_hours(0), duration_hours(NA)) }) test_that("` %/% ` works", { expect_identical(duration_years(5) %/% 2, duration_years(2)) expect_identical(duration_years(NA) %/% 2, duration_years(NA)) expect_identical(duration_years(5) %/% NA_integer_, duration_years(NA)) }) test_that("` %/% ` handles `0` (#349)", { expect_identical(duration_years(5) %/% 0, duration_years(NA)) expect_identical(duration_days(NA) %/% 0, duration_days(NA)) }) test_that("` %% ` works (#273)", { expect_identical(duration_hours(7) %% 4, duration_hours(3)) }) test_that("` %% ` handles `0` (#349)", { expect_identical(duration_hours(7) %% 0, duration_hours(NA)) expect_identical(duration_hours(NA) %% 0, duration_hours(NA)) }) test_that("` %% ` propagates `NA`", { expect_identical(duration_hours(7) %% NA_integer_, duration_hours(NA)) expect_identical(duration_hours(NA) %% 4, duration_hours(NA)) }) test_that("` %% ` casts the numeric to integer", { expect_snapshot((expect_error(duration_hours(5) %% 2.5))) }) # ------------------------------------------------------------------------------ # vec_math() test_that("is.nan() works", { x <- duration_years(c(1, NA)) expect_identical(is.nan(x), c(FALSE, FALSE)) }) test_that("is.finite() works", { x <- duration_years(c(1, NA)) expect_identical(is.finite(x), c(TRUE, FALSE)) }) test_that("is.infinite() works", { x <- duration_years(c(1, NA)) expect_identical(is.infinite(x), c(FALSE, FALSE)) }) test_that("abs() works", { x <- duration_hours(c(-2, -1, 0, 1, 2, NA)) expect <- duration_hours(c(2, 1, 0, 1, 2, NA)) expect_identical(abs(x), expect) }) test_that("abs() propagates names", { x <- set_names(duration_years(1:2), c("a", "b")) expect_named(abs(x), c("a", "b")) }) test_that("sign() works", { x <- duration_hours(c(-2, -1, 0, 1, 2, NA)) expect <- c(-1L, -1L, 0L, 1L, 1L, NA) expect_identical(sign(x), expect) }) test_that("sign() propagates names", { x <- set_names(duration_years(1:2), c("a", "b")) expect_named(sign(x), c("a", "b")) }) # ------------------------------------------------------------------------------ # vec_order() test_that("ordering works", { x <- duration_days(c(-1, -3, -2, 1)) expect_identical(vec_order(x), c(2L, 3L, 1L, 4L)) }) # ------------------------------------------------------------------------------ # vec_compare() test_that("comparisons work", { x <- duration_days(c(1, NA, 2)) y <- duration_days(c(0, 2, 3)) expect_identical(vec_compare(x, y), c(1L, NA, -1L)) }) # ------------------------------------------------------------------------------ # clock_minimum() / clock_maximum() test_that("minimums are right", { skip_if_not_on_os("mac") expect_snapshot({ clock_minimum(duration_years()) clock_minimum(duration_quarters()) clock_minimum(duration_months()) clock_minimum(duration_weeks()) clock_minimum(duration_days()) clock_minimum(duration_hours()) clock_minimum(duration_minutes()) clock_minimum(duration_seconds()) clock_minimum(duration_milliseconds()) clock_minimum(duration_microseconds()) clock_minimum(duration_nanoseconds()) }) }) test_that("maximums are right", { skip_if_not_on_os("mac") expect_snapshot({ clock_maximum(duration_years()) clock_maximum(duration_quarters()) clock_maximum(duration_months()) clock_maximum(duration_weeks()) clock_maximum(duration_days()) clock_maximum(duration_hours()) clock_maximum(duration_minutes()) clock_maximum(duration_seconds()) clock_maximum(duration_milliseconds()) clock_maximum(duration_microseconds()) clock_maximum(duration_nanoseconds()) }) }) # ------------------------------------------------------------------------------ # min() / max() / range() test_that("min() / max() / range() works", { x <- duration_years(c(1, 3, 2, 1, -1)) expect_identical(min(x), duration_years(-1)) expect_identical(max(x), duration_years(3)) expect_identical(range(x), duration_years(c(-1, 3))) }) test_that("min() / max() / range() works with `NA`", { x <- duration_days(c(1, NA, 2, 0)) expect_identical(min(x), duration_days(NA)) expect_identical(max(x), duration_days(NA)) expect_identical(range(x), duration_days(c(NA, NA))) expect_identical(min(x, na.rm = TRUE), duration_days(0)) expect_identical(max(x, na.rm = TRUE), duration_days(2)) expect_identical(range(x, na.rm = TRUE), duration_days(c(0, 2))) }) test_that("min() / max() / range() works when empty", { x <- duration_days() expect_identical(min(x), clock_maximum(x)) expect_identical(max(x), clock_minimum(x)) expect_identical(range(x), c(clock_maximum(x), clock_minimum(x))) x <- duration_days(c(NA, NA)) expect_identical(min(x, na.rm = TRUE), clock_maximum(x)) expect_identical(max(x, na.rm = TRUE), clock_minimum(x)) expect_identical(range(x, na.rm = TRUE), c(clock_maximum(x), clock_minimum(x))) }) # ------------------------------------------------------------------------------ # Missing values test_that("`NA` duration prints as expected", { expect_snapshot({ duration_years(NA) duration_quarters(NA) duration_months(NA) duration_weeks(NA) duration_days(NA) duration_hours(NA) duration_minutes(NA) duration_seconds(NA) duration_milliseconds(NA) duration_microseconds(NA) duration_nanoseconds(NA) }) }) clock/tests/testthat/test-clock-deprecated.R0000644000176200001440000000203414423751216020644 0ustar liggesusers# ------------------------------------------------------------------------------ # date_zone() test_that("`date_zone()` is deprecated but works for POSIXt (#326)", { x <- date_time_build(2020, zone = "America/New_York") expect_snapshot({ zone <- date_zone(x) }) expect_identical(zone, "America/New_York") }) test_that("`date_zone()` is deprecated and errors for Date (#326)", { x <- date_build(2020) expect_snapshot(error = TRUE, { date_zone(x) }) }) # ------------------------------------------------------------------------------ # date_set_zone() test_that("`date_set_zone()` is deprecated but works for POSIXt (#326)", { x <- date_time_build(2020, zone = "America/New_York") y <- date_time_set_zone(x, "America/Los_Angeles") expect_snapshot({ x <- date_set_zone(x, "America/Los_Angeles") }) expect_identical(x, y) }) test_that("`date_set_zone()` is deprecated and errors for Date (#326)", { x <- date_build(2020) expect_snapshot(error = TRUE, { date_set_zone(x, "America/Los_Angeles") }) }) clock/tests/testthat/_snaps/0000755000176200001440000000000014427233200015627 5ustar liggesusersclock/tests/testthat/_snaps/gregorian-year-month-day.md0000644000176200001440000004422214427431025022773 0ustar liggesusers# requires `subsecond_precision` as needed Code year_month_day(2019, 1, 1, 0, 0, 0, 1) Condition Error in `year_month_day()`: ! When `subsecond` is provided, `subsecond_precision` must also be specified. # validates `subsecond_precision` Code year_month_day(2019, 1, 1, 0, 0, 0, 1, subsecond_precision = "second") Condition Error in `year_month_day()`: ! `subsecond_precision` must be one of "millisecond", "microsecond", or "nanosecond", not "second". i Did you mean "nanosecond"? # validates value ranges Code year_month_day(50000) Condition Error in `year_month_day()`: ! `year` must be between [-32767, 32767]. i Invalid results at locations: 1. --- Code year_month_day(2020, 13) Condition Error in `year_month_day()`: ! `month` must be between [1, 12]. i Invalid results at locations: 1. --- Code year_month_day(2020, 1, 32) Condition Error in `year_month_day()`: ! `day` must be between [1, 31]. i Invalid results at locations: 1. --- Code year_month_day(2020, 1, 1, 24) Condition Error in `year_month_day()`: ! `hour` must be between [0, 23]. i Invalid results at locations: 1. --- Code year_month_day(2020, 1, 1, 1, 60) Condition Error in `year_month_day()`: ! `minute` must be between [0, 59]. i Invalid results at locations: 1. --- Code year_month_day(2020, 1, 1, 1, 1, 60) Condition Error in `year_month_day()`: ! `second` must be between [0, 59]. i Invalid results at locations: 1. --- Code year_month_day(2020, 1, 1, 1, 1, 1, 1000, subsecond_precision = "millisecond") Condition Error in `year_month_day()`: ! `subsecond` must be between [0, 999]. i Invalid results at locations: 1. --- Code year_month_day(2020, 1, 1, 1, 1, 1, 1e+06, subsecond_precision = "microsecond") Condition Error in `year_month_day()`: ! `subsecond` must be between [0, 999999]. i Invalid results at locations: 1. --- Code year_month_day(2020, 1, 1, 1, 1, 1, 1e+09, subsecond_precision = "nanosecond") Condition Error in `year_month_day()`: ! `subsecond` must be between [0, 999999999]. i Invalid results at locations: 1. # full ptype is correct [1] "year_month_day" --- [1] "year_month_day" --- [1] "year_month_day" --- [1] "year_month_day" # abbreviated ptype is correct [1] "ymd" --- [1] "ymd" --- [1] "ymd" --- [1] "ymd" # subsecond precision getters require exact precisions Code get_millisecond(micro) Condition Error in `get_millisecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "millisecond". i `x` has a precision of "microsecond". --- Code get_microsecond(milli) Condition Error in `get_microsecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "microsecond". i `x` has a precision of "millisecond". --- Code get_nanosecond(micro) Condition Error in `get_nanosecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "nanosecond". i `x` has a precision of "microsecond". # setters recycling works both ways Code x <- year_month_day(1:2) y <- 1:3 set_month(x, y) Condition Error in `set_field_year_month_day()`: ! Can't recycle `x` (size 2) to match `value` (size 3). # setters require integer `value` Code set_year(x, 1.5) Condition Error in `set_field_year_month_day()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 --- Code set_month(x, 1.5) Condition Error in `set_field_year_month_day()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 --- Code set_day(x, 1.5) Condition Error in `set_field_year_month_day()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 --- Code set_hour(x, 1.5) Condition Error in `set_field_year_month_day()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 --- Code set_minute(x, 1.5) Condition Error in `set_field_year_month_day()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 --- Code set_second(x, 1.5) Condition Error in `set_field_year_month_day()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 --- Code set_millisecond(x, 1.5) Condition Error in `set_field_year_month_day()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 --- Code set_microsecond(x, 1.5) Condition Error in `set_field_year_month_day()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 --- Code set_nanosecond(x, 1.5) Condition Error in `set_field_year_month_day()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 # setters check `value` range Code set_year(x, 1e+05) Condition Error in `set_field_year_month_day()`: ! `value` must be between [-32767, 32767]. i Invalid results at locations: 1. --- Code set_month(x, 13) Condition Error in `set_field_year_month_day()`: ! `value` must be between [1, 12]. i Invalid results at locations: 1. --- Code set_day(x, 32) Condition Error in `set_field_year_month_day()`: ! `value` must be between [1, 31]. i Invalid results at locations: 1. --- Code set_hour(x, 24) Condition Error in `set_field_year_month_day()`: ! `value` must be between [0, 23]. i Invalid results at locations: 1. --- Code set_minute(x, 60) Condition Error in `set_field_year_month_day()`: ! `value` must be between [0, 59]. i Invalid results at locations: 1. --- Code set_second(x, 60) Condition Error in `set_field_year_month_day()`: ! `value` must be between [0, 59]. i Invalid results at locations: 1. --- Code set_millisecond(x, -1) Condition Error in `set_field_year_month_day()`: ! `value` must be between [0, 999]. i Invalid results at locations: 1. --- Code set_microsecond(x, -1) Condition Error in `set_field_year_month_day()`: ! `value` must be between [0, 999999]. i Invalid results at locations: 1. --- Code set_nanosecond(x, -1) Condition Error in `set_field_year_month_day()`: ! `value` must be between [0, 999999999]. i Invalid results at locations: 1. # setters require minimum precision Code set_day(year_month_day(year = 1), 1) Condition Error in `set_day()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be at least "month". i `x` has a precision of "year". --- Code set_hour(year_month_day(year = 1, month = 2), 1) Condition Error in `set_hour()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be at least "day". i `x` has a precision of "month". --- Code set_minute(year_month_day(year = 1, month = 2, day = 3), 1) Condition Error in `set_minute()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be at least "hour". i `x` has a precision of "day". --- Code set_second(year_month_day(year = 1, month = 2, day = 3, hour = 4), 1) Condition Error in `set_second()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be at least "minute". i `x` has a precision of "hour". --- Code set_millisecond(year_month_day(year = 1, month = 2, day = 3, hour = 4, minute = 5), 1) Condition Error in `set_millisecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "millisecond". i `x` has a precision of "minute". --- Code set_microsecond(year_month_day(year = 1, month = 2, day = 3, hour = 4, minute = 5), 1) Condition Error in `set_microsecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "microsecond". i `x` has a precision of "minute". --- Code set_nanosecond(year_month_day(year = 1, month = 2, day = 3, hour = 4, minute = 5), 1) Condition Error in `set_nanosecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "nanosecond". i `x` has a precision of "minute". # setters require correct subsecond precision Code set_millisecond(year_month_day(year = 1, month = 2, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "microsecond"), 1) Condition Error in `set_millisecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "millisecond". i `x` has a precision of "microsecond". --- Code set_millisecond(year_month_day(year = 1, month = 2, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "nanosecond"), 1) Condition Error in `set_millisecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "millisecond". i `x` has a precision of "nanosecond". --- Code set_microsecond(year_month_day(year = 1, month = 2, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "millisecond"), 1) Condition Error in `set_microsecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "microsecond". i `x` has a precision of "millisecond". --- Code set_microsecond(year_month_day(year = 1, month = 2, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "nanosecond"), 1) Condition Error in `set_microsecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "microsecond". i `x` has a precision of "nanosecond". --- Code set_nanosecond(year_month_day(year = 1, month = 2, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "millisecond"), 1) Condition Error in `set_nanosecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "nanosecond". i `x` has a precision of "millisecond". --- Code set_nanosecond(year_month_day(year = 1, month = 2, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "microsecond"), 1) Condition Error in `set_nanosecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "nanosecond". i `x` has a precision of "microsecond". # invalid dates must be resolved when converting to another calendar Code as_year_quarter_day(year_month_day(2019, 2, 31)) Condition Error in `as_sys_time()`: ! Can't convert `x` to another type because some dates are invalid. i The following locations are invalid: 1. i Resolve invalid dates with `invalid_resolve()`. # invalid dates must be resolved when converting to a sys-time Code as_sys_time(year_month_day(2019, 2, 31)) Condition Error in `as_sys_time()`: ! Can't convert `x` to another type because some dates are invalid. i The following locations are invalid: 1. i Resolve invalid dates with `invalid_resolve()`. # invalid dates must be resolved when converting to a naive-time Code as_naive_time(year_month_day(2019, 2, 31)) Condition Error in `as_sys_time()`: ! Can't convert `x` to another type because some dates are invalid. i The following locations are invalid: 1. i Resolve invalid dates with `invalid_resolve()`. # default formats are correct Code format(year_month_day(2019)) Output [1] "2019" --- Code format(year_month_day(2019, 1)) Output [1] "2019-01" --- Code format(year_month_day(2019, 1, 1, 1)) Output [1] "2019-01-01T01" --- Code format(year_month_day(2019, 1, 1, 1, 2, 3, 50, subsecond_precision = "microsecond")) Output [1] "2019-01-01T01:02:03.000050" # failure to parse results in a warning Code year_month_day_parse("foo") Condition Warning: Failed to parse 1 string at location 1. Returning `NA` at that location. Output [1]> [1] NA # requires month precision Code calendar_month_factor(year_month_day(2019)) Condition Error in `calendar_month_factor()`: ! `x` must have at least "month" precision. # `labels` is validated Code calendar_month_factor(year_month_day(2019, 1), labels = 1) Condition Error in `calendar_month_factor()`: ! `labels` must be a , not the number 1. # `abbreviate` is validated Code calendar_month_factor(year_month_day(2019, 1), abbreviate = "foo") Condition Error in `calendar_month_factor()`: ! `abbreviate` must be `TRUE` or `FALSE`, not the string "foo". --- Code calendar_month_factor(year_month_day(2019, 1), abbreviate = 1) Condition Error in `calendar_month_factor()`: ! `abbreviate` must be `TRUE` or `FALSE`, not the number 1. --- Code calendar_month_factor(year_month_day(2019, 1), abbreviate = c(TRUE, FALSE)) Condition Error in `calendar_month_factor()`: ! `abbreviate` must be `TRUE` or `FALSE`, not a logical vector. # can't compute a unsupported count precision Code (expect_error(calendar_count_between(x, x, "day"))) Output Error in `calendar_count_between_standardize_precision_n()`: ! `precision` must be one of: 'year', 'quarter', 'month'. # only granular precisions are allowed Code seq(year_month_day(2019, 1, 1), by = 1, length.out = 2) Condition Error in `seq()`: ! `from` must be 'year' or 'month' precision. # strict mode can be activated Code invalid_resolve(year_month_day(2019, 1, 1)) Condition Error in `strict_validate_invalid()`: ! The global option, `clock.strict`, is currently set to `TRUE`. In this mode, `invalid` must be set and cannot be left as `NULL`. # throws known classed error Code invalid_resolve(year_month_day(2019, 2, 31)) Condition Error in `invalid_resolve()`: ! Invalid date found at location 1. i Resolve invalid date issues by specifying the `invalid` argument. # minimums are right Code clock_minimum(clock_empty_year_month_day_year) Output [1]> [1] "-32767" Code clock_minimum(clock_empty_year_month_day_month) Output [1]> [1] "-32767-01" Code clock_minimum(clock_empty_year_month_day_day) Output [1]> [1] "-32767-01-01" Code clock_minimum(clock_empty_year_month_day_hour) Output [1]> [1] "-32767-01-01T00" Code clock_minimum(clock_empty_year_month_day_minute) Output [1]> [1] "-32767-01-01T00:00" Code clock_minimum(clock_empty_year_month_day_second) Output [1]> [1] "-32767-01-01T00:00:00" Code clock_minimum(clock_empty_year_month_day_millisecond) Output [1]> [1] "-32767-01-01T00:00:00.000" Code clock_minimum(clock_empty_year_month_day_microsecond) Output [1]> [1] "-32767-01-01T00:00:00.000000" Code clock_minimum(clock_empty_year_month_day_nanosecond) Output [1]> [1] "-32767-01-01T00:00:00.000000000" # maximums are right Code clock_maximum(clock_empty_year_month_day_year) Output [1]> [1] "32767" Code clock_maximum(clock_empty_year_month_day_month) Output [1]> [1] "32767-12" Code clock_maximum(clock_empty_year_month_day_day) Output [1]> [1] "32767-12-31" Code clock_maximum(clock_empty_year_month_day_hour) Output [1]> [1] "32767-12-31T23" Code clock_maximum(clock_empty_year_month_day_minute) Output [1]> [1] "32767-12-31T23:59" Code clock_maximum(clock_empty_year_month_day_second) Output [1]> [1] "32767-12-31T23:59:59" Code clock_maximum(clock_empty_year_month_day_millisecond) Output [1]> [1] "32767-12-31T23:59:59.999" Code clock_maximum(clock_empty_year_month_day_microsecond) Output [1]> [1] "32767-12-31T23:59:59.999999" Code clock_maximum(clock_empty_year_month_day_nanosecond) Output [1]> [1] "32767-12-31T23:59:59.999999999" # add_*() respect recycling rules Code add_years(year_month_day(1:2), 1:3) Condition Error in `add_years()`: ! Can't recycle `x` (size 2) to match `n` (size 3). clock/tests/testthat/_snaps/clock-labels.md0000644000176200001440000001010214427431004020500 0ustar liggesusers# can make custom labels Code labels Output Weekdays: Su, Mo, Tu, We, Th, Fr, Sa Months: January, February, March, April, May, June, July, August, September, October, November, December AM/PM: A/P # input is validated Code clock_labels(1) Condition Error in `clock_labels()`: ! `month` must be a character vector of length 12. --- Code clock_labels("x") Condition Error in `clock_labels()`: ! `month` must be a character vector of length 12. --- Code clock_labels(months, 1) Condition Error in `clock_labels()`: ! `month_abbrev` must be a character vector of length 12. --- Code clock_labels(months, "x") Condition Error in `clock_labels()`: ! `month_abbrev` must be a character vector of length 12. --- Code clock_labels(months, months, 1) Condition Error in `clock_labels()`: ! `weekday` must be a character vector of length 7. --- Code clock_labels(months, months, "x") Condition Error in `clock_labels()`: ! `weekday` must be a character vector of length 7. --- Code clock_labels(months, months, weekdays, 1) Condition Error in `clock_labels()`: ! `weekday_abbrev` must be a character vector of length 7. --- Code clock_labels(months, months, weekdays, "x") Condition Error in `clock_labels()`: ! `weekday_abbrev` must be a character vector of length 7. --- Code clock_labels(months, months, weekdays, weekdays, 1) Condition Error in `clock_labels()`: ! `am_pm` must be a character vector of length 2. --- Code clock_labels(months, months, weekdays, weekdays, "x") Condition Error in `clock_labels()`: ! `am_pm` must be a character vector of length 2. # can lookup a language Code labels Output Weekdays: dimanche (dim.), lundi (lun.), mardi (mar.), mercredi (mer.), jeudi (jeu.), vendredi (ven.), samedi (sam.) Months: janvier (janv.), février (févr.), mars (mars), avril (avr.), mai (mai), juin (juin), juillet (juil.), août (août), septembre (sept.), octobre (oct.), novembre (nov.), décembre (déc.) AM/PM: AM/PM # must be a valid language code Code clock_labels_lookup(1) Condition Error in `clock_labels_lookup()`: ! `language` must be a single string, not the number 1. --- Code clock_labels_lookup("foo") Condition Error in `clock_labels_lookup()`: ! Can't find a locale for "foo". # can list all the languages Code langs Output [1] "af" "agq" "ak" "am" "ar" "as" "asa" "ast" "az" "bas" "be" "bem" [13] "bez" "bg" "bm" "bn" "bo" "br" "brx" "bs" "ca" "ccp" "ce" "cgg" [25] "chr" "ckb" "cs" "cy" "da" "dav" "de" "dje" "dsb" "dua" "dyo" "dz" [37] "ebu" "ee" "el" "en" "eo" "es" "et" "eu" "ewo" "fa" "ff" "fi" [49] "fil" "fo" "fr" "fur" "fy" "ga" "gd" "gl" "gsw" "gu" "guz" "gv" [61] "ha" "haw" "he" "hi" "hr" "hsb" "hu" "hy" "id" "ig" "ii" "is" [73] "it" "ja" "jgo" "jmc" "ka" "kab" "kam" "kde" "kea" "khq" "ki" "kk" [85] "kkj" "kl" "kln" "km" "kn" "ko" "kok" "ks" "ksb" "ksf" "ksh" "kw" [97] "ky" "lag" "lb" "lg" "lkt" "ln" "lo" "lrc" "lt" "lu" "luo" "luy" [109] "lv" "mas" "mer" "mfe" "mg" "mgh" "mgo" "mk" "ml" "mn" "mr" "ms" [121] "mt" "mua" "my" "mzn" "naq" "nb" "nd" "nds" "ne" "nl" "nmg" "nn" [133] "nnh" "nus" "nyn" "om" "or" "os" "pa" "pl" "ps" "pt" "qu" "rm" [145] "rn" "ro" "rof" "ru" "rw" "rwk" "sah" "saq" "sbp" "se" "seh" "ses" [157] "sg" "shi" "si" "sk" "sl" "smn" "sn" "so" "sq" "sr" "sv" "sw" [169] "ta" "te" "teo" "tg" "th" "ti" "to" "tr" "tt" "twq" "tzm" "ug" [181] "uk" "ur" "uz" "vai" "vi" "vun" "wae" "wo" "xog" "yav" "yi" "yo" [193] "yue" "zgh" "zh" "zu" clock/tests/testthat/_snaps/naive-time.md0000644000176200001440000002045714427431047020230 0ustar liggesusers# x must be naive Code naive_time_info(sys_days(0), "UTC") Condition Error in `naive_time_info()`: ! `x` must be a , not a object. # zone is vectorized and recycled against x Code naive_time_info(naive_days(0:3), c("UTC", "America/New_York")) Condition Error in `naive_time_info()`: ! Can't recycle `x` (size 4) to match `zone` (size 2). # cannot parse invalid dates Code naive_time_parse(x, precision = "day") Condition Warning: Failed to parse 1 string at location 1. Returning `NA` at that location. Output [1]> [1] NA # failure to parse throws a warning Code naive_time_parse("foo") Condition Warning: Failed to parse 1 string at location 1. Returning `NA` at that location. Output [1]> [1] NA # `naive_time_parse()` validates `locale` Code naive_time_parse("2019-01-01T00:00:00", locale = 1) Condition Error in `naive_time_parse()`: ! `locale` must be a , not the number 1. # default format is correct Code format(naive_seconds(0)) Output [1] "1970-01-01T00:00:00" # `%Z` generates format warnings (#204) Code format(x, format = "%Z") Condition Warning: Failed to format 1 string at location 1. Returning `NA` at that location. Output [1] NA --- Code format(c(x, x), format = "%Z") Condition Warning: Failed to format 2 strings, beginning at location 1. Returning `NA` at the locations where there were format failures. Output [1] NA NA # `%z` generates format warnings (#204) Code format(x, format = "%z") Condition Warning: Failed to format 1 string at location 1. Returning `NA` at that location. Output [1] NA --- Code format(c(x, x), format = "%z") Condition Warning: Failed to format 2 strings, beginning at location 1. Returning `NA` at the locations where there were format failures. Output [1] NA NA # can resolve ambiguous issues - character Code as_zoned_time(x, zone) Condition Error in `as_zoned_time()`: ! Ambiguous time due to daylight saving time at location 1. i Resolve ambiguous time issues by specifying the `ambiguous` argument. # can resolve ambiguous issues - zoned-time Code as_zoned_time(nt_tweaked, zone, ambiguous = zt) Condition Error in `as_zoned_time()`: ! Ambiguous time due to daylight saving time at location 2. i Resolve ambiguous time issues by specifying the `ambiguous` argument. --- Code as_zoned_time(nt_tweaked, zone, ambiguous = zt) Condition Error in `as_zoned_time()`: ! Ambiguous time due to daylight saving time at location 1. i Resolve ambiguous time issues by specifying the `ambiguous` argument. # can resolve nonexistent issues Code as_zoned_time(x, zone) Condition Error in `as_zoned_time()`: ! Nonexistent time due to daylight saving time at location 1. i Resolve nonexistent time issues by specifying the `nonexistent` argument. # `ambiguous` is validated Code as_zoned_time(naive_seconds(), zone, ambiguous = 1) Condition Error in `as_zoned_time()`: ! `ambiguous` must be a character vector, a zoned-time, a POSIXct, or a list, not a number. --- Code as_zoned_time(naive_seconds(), zone, ambiguous = "foo") Condition Error: ! 'foo' is not a recognized `ambiguous` option. --- Code as_zoned_time(naive_seconds(), zone, ambiguous = c("earliest", "latest")) Condition Error in `as_zoned_time()`: ! `ambiguous` must have length 1 or 0. --- Code as_zoned_time(naive_seconds(), zone, ambiguous = ambiguous) Condition Error in `as_zoned_time()`: ! A zoned-time or POSIXct `ambiguous` must have the same zone as `zone`. --- Code as_zoned_time(naive_seconds(), zone, ambiguous = list()) Condition Error in `as_zoned_time()`: ! A list `ambiguous` must have length 2. --- Code as_zoned_time(naive_seconds(), zone, ambiguous = list(1, 1)) Condition Error in `as_zoned_time()`: ! The first element of a list `ambiguous` must be a zoned-time or POSIXt. --- Code as_zoned_time(naive_seconds(), zone, ambiguous = list(reference, 1)) Condition Error in `as_zoned_time()`: ! The second element of a list `ambiguous` must be a character vector, or `NULL`. --- Code as_zoned_time(naive_seconds(), zone, ambiguous = list(reference, "foo")) Condition Error: ! 'foo' is not a recognized `ambiguous` option. # `nonexistent` is validated Code as_zoned_time(naive_seconds(), zone, nonexistent = 1) Condition Error in `as_zoned_time()`: ! `nonexistent` must be a character vector or `NULL`, not the number 1. --- Code as_zoned_time(naive_seconds(), zone, nonexistent = "foo") Condition Error: ! 'foo' is not a recognized `nonexistent` option. --- Code as_zoned_time(naive_seconds(), zone, nonexistent = c("roll-forward", "roll-forward")) Condition Error in `as_zoned_time()`: ! `nonexistent` must have length 1 or 0. # zone is validated Code as_zoned_time(naive_seconds(), "foo") Condition Error in `as_zoned_time()`: ! `zone` must be a valid time zone name. i "foo" is invalid. i Allowed time zone names are listed in `clock::tzdb_names()`. --- Code as_zoned_time(naive_seconds(), 1) Condition Error in `as_zoned_time()`: ! `zone` must be a single string, not the number 1. --- Code as_zoned_time(naive_seconds(), c("America/New_York", "EST", "EDT")) Condition Error in `as_zoned_time()`: ! `zone` must be a single string, not a character vector. # strict mode can be activated - nonexistent Code as_zoned_time(naive_seconds(), zone, ambiguous = "earliest") Condition Error in `as_zoned_time()`: ! The global option, `clock.strict`, is currently set to `TRUE`. In this mode, `nonexistent` must be set and cannot be left as `NULL`. # strict mode can be activated - ambiguous Code as_zoned_time(naive_seconds(), zone, nonexistent = "roll-forward") Condition Error in `as_zoned_time()`: ! The global option, `clock.strict`, is currently set to `TRUE`. In this mode, `ambiguous` must be set and cannot be left as `NULL`. Additionally, `ambiguous` cannot be set to a zoned-time or POSIXct unless it is paired with an ambiguous time resolution strategy, like: `list(, 'earliest')`. --- Code as_zoned_time(naive_seconds(), zone, nonexistent = "roll-forward", ambiguous = zt) Condition Error in `as_zoned_time()`: ! The global option, `clock.strict`, is currently set to `TRUE`. In this mode, `ambiguous` must be set and cannot be left as `NULL`. Additionally, `ambiguous` cannot be set to a zoned-time or POSIXct unless it is paired with an ambiguous time resolution strategy, like: `list(, 'earliest')`. --- Code as_zoned_time(naive_seconds(), zone, nonexistent = "roll-forward", ambiguous = list( zt, NULL)) Condition Error in `as_zoned_time()`: ! The global option, `clock.strict`, is currently set to `TRUE`. In this mode, `ambiguous` must be set and cannot be left as `NULL`. Additionally, `ambiguous` cannot be set to a zoned-time or POSIXct unless it is paired with an ambiguous time resolution strategy, like: `list(, 'earliest')`. # empty dots are checked Code as_zoned_time(naive_seconds(), "UTC", "roll-forward") Condition Error in `as_zoned_time()`: ! `...` must be empty. x Problematic argument: * ..1 = "roll-forward" i Did you forget to name an argument? # `vec_ptype_full()` prints correctly Code vec_ptype_full(naive_days()) Output [1] "naive_time" Code vec_ptype_full(naive_seconds(1:5)) Output [1] "naive_time" # `vec_ptype_abbr()` prints correctly Code vec_ptype_abbr(naive_days()) Output [1] "naive" Code vec_ptype_abbr(naive_seconds(1:5)) Output [1] "naive" clock/tests/testthat/_snaps/iso-year-week-day.md0000644000176200001440000003535414427431043021424 0ustar liggesusers# validates value ranges Code iso_year_week_day(50000) Condition Error in `iso_year_week_day()`: ! `year` must be between [-32767, 32767]. i Invalid results at locations: 1. --- Code iso_year_week_day(2020, 54) Condition Error in `iso_year_week_day()`: ! `week` must be between [1, 53]. i Invalid results at locations: 1. --- Code iso_year_week_day(2020, 1, 8) Condition Error in `iso_year_week_day()`: ! `day` must be between [1, 7]. i Invalid results at locations: 1. --- Code iso_year_week_day(2020, 1, 1, 24) Condition Error in `iso_year_week_day()`: ! `hour` must be between [0, 23]. i Invalid results at locations: 1. --- Code iso_year_week_day(2020, 1, 1, 1, 60) Condition Error in `iso_year_week_day()`: ! `minute` must be between [0, 59]. i Invalid results at locations: 1. --- Code iso_year_week_day(2020, 1, 1, 1, 1, 60) Condition Error in `iso_year_week_day()`: ! `second` must be between [0, 59]. i Invalid results at locations: 1. --- Code iso_year_week_day(2020, 1, 1, 1, 1, 1, 1000, subsecond_precision = "millisecond") Condition Error in `iso_year_week_day()`: ! `subsecond` must be between [0, 999]. i Invalid results at locations: 1. --- Code iso_year_week_day(2020, 1, 1, 1, 1, 1, 1e+06, subsecond_precision = "microsecond") Condition Error in `iso_year_week_day()`: ! `subsecond` must be between [0, 999999]. i Invalid results at locations: 1. --- Code iso_year_week_day(2020, 1, 1, 1, 1, 1, 1e+09, subsecond_precision = "nanosecond") Condition Error in `iso_year_week_day()`: ! `subsecond` must be between [0, 999999999]. i Invalid results at locations: 1. # full ptype is correct [1] "iso_year_week_day" --- [1] "iso_year_week_day" --- [1] "iso_year_week_day" --- [1] "iso_year_week_day" # abbreviated ptype is correct [1] "iso_ywd" --- [1] "iso_ywd" --- [1] "iso_ywd" --- [1] "iso_ywd" # setters recycling works both ways Code x <- iso_year_week_day(1:2) y <- 1:3 set_week(x, y) Condition Error in `set_field_iso_year_week_day()`: ! Can't recycle `x` (size 2) to match `value` (size 3). # setters require integer `value` Code set_year(x, 1.5) Condition Error in `set_field_iso_year_week_day()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 --- Code set_week(x, 1.5) Condition Error in `set_field_iso_year_week_day()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 --- Code set_day(x, 1.5) Condition Error in `set_field_iso_year_week_day()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 --- Code set_hour(x, 1.5) Condition Error in `set_field_iso_year_week_day()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 --- Code set_minute(x, 1.5) Condition Error in `set_field_iso_year_week_day()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 --- Code set_second(x, 1.5) Condition Error in `set_field_iso_year_week_day()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 --- Code set_millisecond(x, 1.5) Condition Error in `set_field_iso_year_week_day()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 --- Code set_microsecond(x, 1.5) Condition Error in `set_field_iso_year_week_day()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 --- Code set_nanosecond(x, 1.5) Condition Error in `set_field_iso_year_week_day()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 # setters check `value` range Code set_year(x, 1e+05) Condition Error in `set_field_iso_year_week_day()`: ! `value` must be between [-32767, 32767]. i Invalid results at locations: 1. --- Code set_week(x, 54) Condition Error in `set_field_iso_year_week_day()`: ! `value` must be between [1, 53]. i Invalid results at locations: 1. --- Code set_day(x, 8) Condition Error in `set_field_iso_year_week_day()`: ! `value` must be between [1, 7]. i Invalid results at locations: 1. --- Code set_hour(x, 24) Condition Error in `set_field_iso_year_week_day()`: ! `value` must be between [0, 23]. i Invalid results at locations: 1. --- Code set_minute(x, 60) Condition Error in `set_field_iso_year_week_day()`: ! `value` must be between [0, 59]. i Invalid results at locations: 1. --- Code set_second(x, 60) Condition Error in `set_field_iso_year_week_day()`: ! `value` must be between [0, 59]. i Invalid results at locations: 1. --- Code set_millisecond(x, -1) Condition Error in `set_field_iso_year_week_day()`: ! `value` must be between [0, 999]. i Invalid results at locations: 1. --- Code set_microsecond(x, -1) Condition Error in `set_field_iso_year_week_day()`: ! `value` must be between [0, 999999]. i Invalid results at locations: 1. --- Code set_nanosecond(x, -1) Condition Error in `set_field_iso_year_week_day()`: ! `value` must be between [0, 999999999]. i Invalid results at locations: 1. # setters require minimum precision Code set_day(iso_year_week_day(year = 1), 1) Condition Error in `set_day()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be at least "week". i `x` has a precision of "year". --- Code set_hour(iso_year_week_day(year = 1, week = 2), 1) Condition Error in `set_hour()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be at least "day". i `x` has a precision of "week". --- Code set_minute(iso_year_week_day(year = 1, week = 2, day = 3), 1) Condition Error in `set_minute()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be at least "hour". i `x` has a precision of "day". --- Code set_second(iso_year_week_day(year = 1, week = 2, day = 3, hour = 4), 1) Condition Error in `set_second()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be at least "minute". i `x` has a precision of "hour". --- Code set_millisecond(iso_year_week_day(year = 1, week = 2, day = 3, hour = 4, minute = 5), 1) Condition Error in `set_millisecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "millisecond". i `x` has a precision of "minute". --- Code set_microsecond(iso_year_week_day(year = 1, week = 2, day = 3, hour = 4, minute = 5), 1) Condition Error in `set_microsecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "microsecond". i `x` has a precision of "minute". --- Code set_nanosecond(iso_year_week_day(year = 1, week = 2, day = 3, hour = 4, minute = 5), 1) Condition Error in `set_nanosecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "nanosecond". i `x` has a precision of "minute". # setters require correct subsecond precision Code set_millisecond(iso_year_week_day(year = 1, week = 2, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "microsecond"), 1) Condition Error in `set_millisecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "millisecond". i `x` has a precision of "microsecond". --- Code set_millisecond(iso_year_week_day(year = 1, week = 2, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "nanosecond"), 1) Condition Error in `set_millisecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "millisecond". i `x` has a precision of "nanosecond". --- Code set_microsecond(iso_year_week_day(year = 1, week = 2, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "millisecond"), 1) Condition Error in `set_microsecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "microsecond". i `x` has a precision of "millisecond". --- Code set_microsecond(iso_year_week_day(year = 1, week = 2, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "nanosecond"), 1) Condition Error in `set_microsecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "microsecond". i `x` has a precision of "nanosecond". --- Code set_nanosecond(iso_year_week_day(year = 1, week = 2, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "millisecond"), 1) Condition Error in `set_nanosecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "nanosecond". i `x` has a precision of "millisecond". --- Code set_nanosecond(iso_year_week_day(year = 1, week = 2, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "microsecond"), 1) Condition Error in `set_nanosecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "nanosecond". i `x` has a precision of "microsecond". # default formats are correct Code format(iso_year_week_day(2019)) Output [1] "2019" --- Code format(iso_year_week_day(2019, 1)) Output [1] "2019-W01" --- Code format(iso_year_week_day(2019, 1, 1, 1)) Output [1] "2019-W01-1T01" --- Code format(iso_year_week_day(2019, 1, 1, 1, 2, 3, 50, subsecond_precision = "microsecond")) Output [1] "2019-W01-1T01:02:03.000050" # can't compute a unsupported count precision Code (expect_error(calendar_count_between(x, x, "week"))) Output Error in `calendar_count_between_standardize_precision_n()`: ! `precision` must be one of: 'year'. # only year precision is allowed Code seq(iso_year_week_day(2019, 1), by = 1, length.out = 2) Condition Error in `seq()`: ! `from` must be 'year' precision. # strict mode can be activated Code invalid_resolve(iso_year_week_day(2019, 1)) Condition Error in `strict_validate_invalid()`: ! The global option, `clock.strict`, is currently set to `TRUE`. In this mode, `invalid` must be set and cannot be left as `NULL`. # throws known classed error Code invalid_resolve(iso_year_week_day(2019, 53)) Condition Error in `invalid_resolve()`: ! Invalid date found at location 1. i Resolve invalid date issues by specifying the `invalid` argument. # minimums are right Code clock_minimum(clock_empty_iso_year_week_day_year) Output [1]> [1] "-32767" Code clock_minimum(clock_empty_iso_year_week_day_week) Output [1]> [1] "-32767-W01" Code clock_minimum(clock_empty_iso_year_week_day_day) Output [1]> [1] "-32767-W01-1" Code clock_minimum(clock_empty_iso_year_week_day_hour) Output [1]> [1] "-32767-W01-1T00" Code clock_minimum(clock_empty_iso_year_week_day_minute) Output [1]> [1] "-32767-W01-1T00:00" Code clock_minimum(clock_empty_iso_year_week_day_second) Output [1]> [1] "-32767-W01-1T00:00:00" Code clock_minimum(clock_empty_iso_year_week_day_millisecond) Output [1]> [1] "-32767-W01-1T00:00:00.000" Code clock_minimum(clock_empty_iso_year_week_day_microsecond) Output [1]> [1] "-32767-W01-1T00:00:00.000000" Code clock_minimum(clock_empty_iso_year_week_day_nanosecond) Output [1]> [1] "-32767-W01-1T00:00:00.000000000" # maximums are right Code clock_maximum(clock_empty_iso_year_week_day_year) Output [1]> [1] "32767" Code clock_maximum(clock_empty_iso_year_week_day_week) Output [1]> [1] "32767-W52" Code clock_maximum(clock_empty_iso_year_week_day_day) Output [1]> [1] "32767-W52-7" Code clock_maximum(clock_empty_iso_year_week_day_hour) Output [1]> [1] "32767-W52-7T23" Code clock_maximum(clock_empty_iso_year_week_day_minute) Output [1]> [1] "32767-W52-7T23:59" Code clock_maximum(clock_empty_iso_year_week_day_second) Output [1]> [1] "32767-W52-7T23:59:59" Code clock_maximum(clock_empty_iso_year_week_day_millisecond) Output [1]> [1] "32767-W52-7T23:59:59.999" Code clock_maximum(clock_empty_iso_year_week_day_microsecond) Output [1]> [1] "32767-W52-7T23:59:59.999999" Code clock_maximum(clock_empty_iso_year_week_day_nanosecond) Output [1]> [1] "32767-W52-7T23:59:59.999999999" # add_*() respect recycling rules Code add_years(iso_year_week_day(1:2), 1:3) Condition Error in `add_years()`: ! Can't recycle `x` (size 2) to match `n` (size 3). clock/tests/testthat/_snaps/clock-deprecated.md0000644000176200001440000000210514427431004021342 0ustar liggesusers# `date_zone()` is deprecated but works for POSIXt (#326) Code zone <- date_zone(x) Condition Warning: `date_zone()` was deprecated in clock 0.7.0. i Please use `date_time_zone()` instead. # `date_zone()` is deprecated and errors for Date (#326) Code date_zone(x) Condition Warning: `date_zone()` was deprecated in clock 0.7.0. i Please use `date_time_zone()` instead. Error in `date_zone()`: ! Can't get the zone of a 'Date'. # `date_set_zone()` is deprecated but works for POSIXt (#326) Code x <- date_set_zone(x, "America/Los_Angeles") Condition Warning: `date_set_zone()` was deprecated in clock 0.7.0. i Please use `date_time_set_zone()` instead. # `date_set_zone()` is deprecated and errors for Date (#326) Code date_set_zone(x, "America/Los_Angeles") Condition Warning: `date_set_zone()` was deprecated in clock 0.7.0. i Please use `date_time_set_zone()` instead. Error in `date_set_zone()`: ! Can't set the zone of a 'Date'. clock/tests/testthat/_snaps/zoned-time.md0000644000176200001440000002153714427431122020237 0ustar liggesusers# can't compare zoned-times with different zones Code x > y Condition Error in `vec_compare()`: ! Can't combine `..1` > and `..2` >. Zones can't differ. # normal print method works Code x Output [5]> [1] "2018-12-31T19:00:00-05:00" "2019-01-31T19:00:00-05:00" [3] "2019-02-28T19:00:00-05:00" "2019-03-31T20:00:00-04:00" [5] "2019-04-30T20:00:00-04:00" # can limit with `max` Code print(x, max = 2) Output [5]> [1] "2018-12-31T19:00:00-05:00" "2019-01-31T19:00:00-05:00" Reached `max` or `getOption('max.print')`. Omitted 3 values. --- Code print(x, max = 4) Output [5]> [1] "2018-12-31T19:00:00-05:00" "2019-01-31T19:00:00-05:00" [3] "2019-02-28T19:00:00-05:00" "2019-03-31T20:00:00-04:00" Reached `max` or `getOption('max.print')`. Omitted 1 value. --- Code print(x, max = 5) Output [5]> [1] "2018-12-31T19:00:00-05:00" "2019-01-31T19:00:00-05:00" [3] "2019-02-28T19:00:00-05:00" "2019-03-31T20:00:00-04:00" [5] "2019-04-30T20:00:00-04:00" --- Code print(x, max = 6) Output [5]> [1] "2018-12-31T19:00:00-05:00" "2019-01-31T19:00:00-05:00" [3] "2019-02-28T19:00:00-05:00" "2019-03-31T20:00:00-04:00" [5] "2019-04-30T20:00:00-04:00" # `max` defaults to `getOption('max.print')` but can be overridden Code x Output [5]> [1] "2018-12-31T19:00:00-05:00" "2019-01-31T19:00:00-05:00" [3] "2019-02-28T19:00:00-05:00" Reached `max` or `getOption('max.print')`. Omitted 2 values. --- Code print(x, max = 4) Output [5]> [1] "2018-12-31T19:00:00-05:00" "2019-01-31T19:00:00-05:00" [3] "2019-02-28T19:00:00-05:00" "2019-03-31T20:00:00-04:00" Reached `max` or `getOption('max.print')`. Omitted 1 value. --- Code print(x, max = 5) Output [5]> [1] "2018-12-31T19:00:00-05:00" "2019-01-31T19:00:00-05:00" [3] "2019-02-28T19:00:00-05:00" "2019-03-31T20:00:00-04:00" [5] "2019-04-30T20:00:00-04:00" # `as_zoned_time()` has good default error Code as_zoned_time(1) Condition Error in `as_zoned_time()`: ! Can't convert to . # can't convert zoned-time directly to calendar Code (expect_error(as_year_month_day(x), class = "clock_error_unsupported_conversion") ) Output Error in `as_year_month_day()`: ! Can't convert to . # cannot parse nonexistent time Code zoned_time_parse_complete(x) Condition Warning: Failed to parse 1 string at location 1. Returning `NA` at that location. Output [1]> [1] NA # offset must align with unique offset Code zoned_time_parse_complete(x) Condition Warning: Failed to parse 1 string at location 1. Returning `NA` at that location. Output [1]> [1] NA # offset must align with one of two possible ambiguous offsets Code zoned_time_parse_complete(x) Condition Warning: Failed to parse 2 strings, beginning at location 1. Returning `NA` at the locations where there were parse failures. Output [2]> [1] NA NA # cannot have differing zone names Code zoned_time_parse_complete(x) Condition Error: ! All elements of `x` must have the same time zone name. Found different zone names of: 'America/New_York' and 'America/Los_Angeles'. # zone name must be valid Code zoned_time_parse_complete(x) Condition Error: ! `%Z` must be used, and must result in a valid time zone name, not 'America/New_Yor'. # abbreviation must match the one implied from naive + time zone name lookup Code zoned_time_parse_abbrev(x, "America/New_York") Condition Warning: Failed to parse 1 string at location 1. Returning `NA` at that location. Output [1]> [1] NA # %Z must be used Code zoned_time_parse_abbrev(x, "America/New_York", format = "%Y-%m-%d") Condition Error: ! `%Z` must be used and must result in a time zone abbreviation. # boundaries are handled right Code x$begin Output [1]> [1] "-32767-01-01T00:00:00+00:00" --- Code x$end Output [1]> [1] "32767-12-31T00:00:00+00:00" # input must be a zoned-time Code zoned_time_info(1) Condition Error in `zoned_time_info()`: ! `x` must be a , not the number 1. # zoned-times don't support arithmetic Code add_years(x, 1) Condition Error in `add_years()`: ! Can't perform this operation on a . i Do you need to convert to a calendar first? i Use `as_naive_time()` then `as_year_month_day()` to convert to a calendar that supports `add_years()`. --- Code add_quarters(x, 1) Condition Error in `add_quarters()`: ! Can't perform this operation on a . i Do you need to convert to a calendar first? i Use `as_naive_time()` then `as_year_quarter_day()` to convert to a calendar that supports `add_quarters()`. --- Code add_months(x, 1) Condition Error in `add_months()`: ! Can't perform this operation on a . i Do you need to convert to a calendar first? i Use `as_naive_time()` then `as_year_month_day()` to convert to a calendar that supports `add_months()`. --- Code add_weeks(x, 1) Condition Error in `add_weeks()`: ! Can't perform this operation on a . i Do you need to convert to a time point first? i Use `as_naive_time()` or `as_sys_time()` to convert to a time point. --- Code add_days(x, 1) Condition Error in `add_days()`: ! Can't perform this operation on a . i Do you need to convert to a time point first? i Use `as_naive_time()` or `as_sys_time()` to convert to a time point. --- Code add_hours(x, 1) Condition Error in `add_hours()`: ! Can't perform this operation on a . i Do you need to convert to a time point first? i Use `as_naive_time()` or `as_sys_time()` to convert to a time point. --- Code add_minutes(x, 1) Condition Error in `add_minutes()`: ! Can't perform this operation on a . i Do you need to convert to a time point first? i Use `as_naive_time()` or `as_sys_time()` to convert to a time point. --- Code add_seconds(x, 1) Condition Error in `add_seconds()`: ! Can't perform this operation on a . i Do you need to convert to a time point first? i Use `as_naive_time()` or `as_sys_time()` to convert to a time point. --- Code add_milliseconds(x, 1) Condition Error in `add_milliseconds()`: ! Can't perform this operation on a . i Do you need to convert to a time point first? i Use `as_naive_time()` or `as_sys_time()` to convert to a time point. --- Code add_microseconds(x, 1) Condition Error in `add_microseconds()`: ! Can't perform this operation on a . i Do you need to convert to a time point first? i Use `as_naive_time()` or `as_sys_time()` to convert to a time point. --- Code add_nanoseconds(x, 1) Condition Error in `add_nanoseconds()`: ! Can't perform this operation on a . i Do you need to convert to a time point first? i Use `as_naive_time()` or `as_sys_time()` to convert to a time point. # `zoned_time_zone()` validates `x` Code zoned_time_zone(1) Condition Error in `zoned_time_zone()`: ! `x` must be a , not the number 1. # `zoned_time_set_zone()` validates `x` Code zoned_time_set_zone(1, "UTC") Condition Error in `zoned_time_set_zone()`: ! `x` must be a , not the number 1. # precision: can only be called on zoned-times Code zoned_time_precision(duration_days()) Condition Error in `zoned_time_precision()`: ! `x` must be a , not a object. clock/tests/testthat/_snaps/duration.md0000644000176200001440000003261014427431013020002 0ustar liggesusers# can't round across common precision boundary Code duration_ceiling(duration_weeks(), "month") Condition Error in `duration_ceiling()`: ! Can't ceiling from a chronological precision (week) to a calendrical precision (month). --- Code duration_floor(duration_seconds(), "year") Condition Error in `duration_floor()`: ! Can't floor from a chronological precision (second) to a calendrical precision (year). # input is validated Code duration_floor(1, "year") Condition Error in `duration_floor()`: ! `x` must be a , not the number 1. --- Code duration_floor(duration_seconds(1), "foo") Condition Error in `duration_floor()`: ! `precision` must be one of "year", "quarter", "month", "week", "day", "hour", "minute", "second", "millisecond", "microsecond", or "nanosecond", not "foo". --- Code duration_floor(duration_seconds(1), "day", n = -1) Condition Error in `duration_floor()`: ! `n` must be a whole number larger than or equal to 0, not the number -1. # seq() validates from Code seq(duration_years(1:2)) Condition Error in `seq()`: ! `from` must have size 1, not size 2. --- Code seq(duration_years(NA_integer_)) Condition Error in `seq()`: ! `from` can't contain missing values. i The following locations are missing: 1. # seq() validates length.out / along.with exclusiveness Code seq(duration_years(1L), length.out = 1, along.with = 2) Condition Error in `seq()`: ! Can only specify one of `length.out` and `along.with`. # seq() only takes two optional args Code seq(x, to = duration_years(1), by = 1, length.out = 1) Condition Error in `seq()`: ! Must specify exactly two of: - `to` - `by` - Either `length.out` or `along.with` --- Code seq(x, to = duration_years(1), by = 1, along.with = 1) Condition Error in `seq()`: ! Must specify exactly two of: - `to` - `by` - Either `length.out` or `along.with` # seq() requires two optional args Code seq(x, to = duration_years(1)) Condition Error in `seq()`: ! Must specify exactly two of: - `to` - `by` - Either `length.out` or `along.with` --- Code seq(x, by = 1) Condition Error in `seq()`: ! Must specify exactly two of: - `to` - `by` - Either `length.out` or `along.with` --- Code seq(x, length.out = 1) Condition Error in `seq()`: ! Must specify exactly two of: - `to` - `by` - Either `length.out` or `along.with` --- Code seq(x, along.with = 1) Condition Error in `seq()`: ! Must specify exactly two of: - `to` - `by` - Either `length.out` or `along.with` # seq() validates `to` Code seq(duration_years(1L), to = duration_years(1:2), by = 1) Condition Error in `seq()`: ! `to` must have size 1, not size 2. --- Code seq(duration_years(1L), to = 1, by = 1) Condition Error in `seq()`: ! Can't convert `to` to match type of `from` >. --- Code seq(duration_years(1L), to = duration_days(1), by = 1) Condition Error in `seq()`: ! Can't convert `to` > to match type of `from` >. Can't cast between calendrical durations and chronological durations. --- Code seq(duration_years(1L), to = duration_years(NA_integer_), by = 1) Condition Error in `seq()`: ! `to` can't contain missing values. i The following locations are missing: 1. # seq() validates `by` Code seq(duration_years(1L), to = duration_years(1L), by = 1:2) Condition Error in `seq()`: ! `by` must have size 1, not size 2. --- Code seq(duration_years(1L), to = duration_years(1L), by = NA_integer_) Condition Error in `seq()`: ! `by` can't contain missing values. i The following locations are missing: 1. --- Code seq(duration_years(1L), to = duration_years(1L), by = 0) Condition Error in `seq()`: ! `by` can't be `0`. --- Code seq(duration_years(1L), to = duration_years(1L), by = duration_years(0)) Condition Error in `seq()`: ! `by` can't be `0`. --- Code seq(duration_years(1L), to = duration_years(1L), by = "x") Condition Error in `seq()`: ! Can't convert `by` to . # `by` must be castable to the type of `from` Code seq(duration_years(0), to = duration_years(1), by = duration_months(1)) Condition Error in `seq()`: ! Can't convert `by` > to >. Can't cast to a less precise precision. --- Code seq(duration_years(0), to = duration_years(1), by = duration_days(1)) Condition Error in `seq()`: ! Can't convert `by` > to >. Can't cast between calendrical durations and chronological durations. --- Code seq(duration_days(0), to = duration_days(1), by = duration_years(1)) Condition Error in `seq()`: ! Can't convert `by` > to >. Can't cast between calendrical durations and chronological durations. # seq() validates `length.out` Code seq(duration_years(1L), to = duration_years(1L), length.out = 1:2) Condition Error in `seq()`: ! `length.out` must have size 1, not size 2. --- Code seq(duration_years(1L), to = duration_years(1L), length.out = NA_integer_) Condition Error in `seq()`: ! `length.out` can't contain missing values. i The following locations are missing: 1. --- Code seq(duration_years(1L), to = duration_years(1L), length.out = -1) Condition Error in `seq()`: ! `length.out` can't be negative. --- Code seq(duration_years(1L), to = duration_years(1L), length.out = "x") Condition Error in `seq()`: ! Can't convert `length.out` to . # seq() validates dots Code seq(duration_years(1), duration_years(1), 1, 1, 1, 1) Condition Error in `seq()`: ! `...` must be empty. x Problematic argument: * ..1 = 1 i Did you forget to name an argument? # seq() enforces non-fractional results Code seq(duration_years(1L), to = duration_years(2L), length.out = 3) Condition Error: ! The supplied output size does not result in a non-fractional sequence between `from` and `to`. --- Code seq(duration_years(1L), to = duration_years(2L), along.with = 1:3) Condition Error: ! The supplied output size does not result in a non-fractional sequence between `from` and `to`. # `to` is always cast to `from` Code seq(duration_days(0), to = duration_years(5), by = 2) Condition Error in `seq()`: ! Can't convert `to` > to match type of `from` >. Can't cast between calendrical durations and chronological durations. --- Code seq(duration_years(0), to = duration_months(5), by = 2) Condition Error in `seq()`: ! Can't convert `to` > to match type of `from` >. Can't cast to a less precise precision. # validates the input Code duration_spanning_seq(1) Condition Error in `duration_spanning_seq()`: ! `x` must be a , not the number 1. # can't add chronological and calendrical durations Code add_seconds(duration_years(1), 1) Condition Error in `duration_arith()`: ! Can't combine `x` > and `y` >. Can't combine calendrical durations with chronological durations. --- Code add_years(duration_seconds(1), 1) Condition Error in `duration_arith()`: ! Can't combine `x` > and `y` >. Can't combine calendrical durations with chronological durations. # can't convert calendrical duration to time point Code as_sys_time(duration_years(0)) Condition Error in `as_sys_time()`: ! Can't combine `x` > and >. Can't combine calendrical durations with chronological durations. --- Code as_naive_time(duration_years(0)) Condition Error in `as_sys_time()`: ! Can't combine `x` > and >. Can't combine calendrical durations with chronological durations. # precision: can only be called on durations Code duration_precision(sys_days(0)) Condition Error in `duration_precision()`: ! `x` must be a , not a object. # ` / ` is not allowed Code (expect_error(duration_years(1) / duration_years(2))) Output Error in `vec_arith()`: ! > / > is not permitted Durations only support integer division. Did you want `%/%`? # ` %/% ` results in NA for OOB values Code out <- (numerator + one) %/% denominator Condition Warning: Conversion to integer is outside the range of an integer. `NA` values have been introduced, beginning at location 1. --- Code out <- (-numerator - one) %/% denominator Condition Warning: Conversion to integer is outside the range of an integer. `NA` values have been introduced, beginning at location 1. # ` %% ` casts the numeric to integer Code (expect_error(duration_hours(5) %% 2.5)) Output Error in `duration_scalar_arith()`: ! Can't convert from `y` to due to loss of precision. * Locations: 1 # minimums are right Code clock_minimum(duration_years()) Output [1]> [1] -2147483648 Code clock_minimum(duration_quarters()) Output [1]> [1] -2147483648 Code clock_minimum(duration_months()) Output [1]> [1] -2147483648 Code clock_minimum(duration_weeks()) Output [1]> [1] -2147483648 Code clock_minimum(duration_days()) Output [1]> [1] -2147483648 Code clock_minimum(duration_hours()) Output [1]> [1] -9223372036854775808 Code clock_minimum(duration_minutes()) Output [1]> [1] -9223372036854775808 Code clock_minimum(duration_seconds()) Output [1]> [1] -9223372036854775808 Code clock_minimum(duration_milliseconds()) Output [1]> [1] -9223372036854775808 Code clock_minimum(duration_microseconds()) Output [1]> [1] -9223372036854775808 Code clock_minimum(duration_nanoseconds()) Output [1]> [1] -9223372036854775808 # maximums are right Code clock_maximum(duration_years()) Output [1]> [1] 2147483647 Code clock_maximum(duration_quarters()) Output [1]> [1] 2147483647 Code clock_maximum(duration_months()) Output [1]> [1] 2147483647 Code clock_maximum(duration_weeks()) Output [1]> [1] 2147483647 Code clock_maximum(duration_days()) Output [1]> [1] 2147483647 Code clock_maximum(duration_hours()) Output [1]> [1] 9223372036854775807 Code clock_maximum(duration_minutes()) Output [1]> [1] 9223372036854775807 Code clock_maximum(duration_seconds()) Output [1]> [1] 9223372036854775807 Code clock_maximum(duration_milliseconds()) Output [1]> [1] 9223372036854775807 Code clock_maximum(duration_microseconds()) Output [1]> [1] 9223372036854775807 Code clock_maximum(duration_nanoseconds()) Output [1]> [1] 9223372036854775807 # `NA` duration prints as expected Code duration_years(NA) Output [1]> [1] NA Code duration_quarters(NA) Output [1]> [1] NA Code duration_months(NA) Output [1]> [1] NA Code duration_weeks(NA) Output [1]> [1] NA Code duration_days(NA) Output [1]> [1] NA Code duration_hours(NA) Output [1]> [1] NA Code duration_minutes(NA) Output [1]> [1] NA Code duration_seconds(NA) Output [1]> [1] NA Code duration_milliseconds(NA) Output [1]> [1] NA Code duration_microseconds(NA) Output [1]> [1] NA Code duration_nanoseconds(NA) Output [1]> [1] NA clock/tests/testthat/_snaps/clock-locale.md0000644000176200001440000000254414427431005020511 0ustar liggesusers# can create a default locale object Code locale Output Decimal Mark: . Weekdays: Sunday (Sun), Monday (Mon), Tuesday (Tue), Wednesday (Wed), Thursday (Thu), Friday (Fri), Saturday (Sat) Months: January (Jan), February (Feb), March (Mar), April (Apr), May (May), June (Jun), July (Jul), August (Aug), September (Sep), October (Oct), November (Nov), December (Dec) AM/PM: AM/PM # must use a valid clock-labels object Code clock_locale(1) Condition Error in `clock_locale()`: ! `labels` must be a , not the number 1. # must use a valid decimal-mark Code clock_locale(decimal_mark = "f") Condition Error in `clock_locale()`: ! `decimal_mark` must be one of "." or ",", not "f". # can change the decimal-mark Code x Output Decimal Mark: , Weekdays: Sunday (Sun), Monday (Mon), Tuesday (Tue), Wednesday (Wed), Thursday (Thu), Friday (Fri), Saturday (Sat) Months: January (Jan), February (Feb), March (Mar), April (Apr), May (May), June (Jun), July (Jul), August (Aug), September (Sep), October (Oct), November (Nov), December (Dec) AM/PM: AM/PM clock/tests/testthat/_snaps/posixt.md0000644000176200001440000005756514427431060017525 0ustar liggesusers# can't accidentally supply `zone` to reinterpret date-time in new zone Code as_date_time(new_datetime(0), zone = "America/New_York") Condition Error in `as_date_time()`: ! `...` must be empty. x Problematic argument: * zone = "America/New_York" # can resolve nonexistent midnight issues for Date -> POSIXct Code (expect_error(as_date_time(x, zone), class = "clock_error_nonexistent_time")) Output Error in `as_zoned_time()`: ! Nonexistent time due to daylight saving time at location 1. i Resolve nonexistent time issues by specifying the `nonexistent` argument. # can resolve ambiguous midnight issues for Date -> POSIXct Code (expect_error(as_date_time(x, zone), class = "clock_error_ambiguous_time")) Output Error in `as_zoned_time()`: ! Ambiguous time due to daylight saving time at location 1. i Resolve ambiguous time issues by specifying the `ambiguous` argument. # can handle nonexistent times resulting from grouping Code date_group(x, "hour", n = 2) Condition Error in `as_zoned_time()`: ! Nonexistent time due to daylight saving time at location 1. i Resolve nonexistent time issues by specifying the `nonexistent` argument. # can't group by finer precisions Code date_group(x, "nanosecond") Condition Error in `calendar_group()`: ! Can't group at a precision ("nanosecond") that is more precise than `x` ("second"). # can't group by non-year-month-day precisions Code date_group(x, "quarter") Condition Error in `calendar_group()`: ! `precision` must be a valid precision for a , not "quarter". # flooring can handle nonexistent times Code date_floor(x, "hour", n = 2) Condition Error in `as_zoned_time()`: ! Nonexistent time due to daylight saving time at location 2. i Resolve nonexistent time issues by specifying the `nonexistent` argument. # `origin` is floored to the precision of `precision` with a potential warning before rounding Code date_floor(x, "day", origin = origin) Condition Warning: `origin` has been floored from "second" precision to "day" precision to match `precision`. This floor has resulted in a loss of information. Output [1] "1970-01-01 EST" "1970-01-02 EST" # `origin` is validated Code date_floor(x, "day", origin = 1) Condition Error in `date_floor()`: ! `origin` must be a , not the number 1. --- Code date_floor(x, "day", origin = new_datetime(NA_real_, zone)) Condition Error in `date_floor()`: ! `origin` can't contain missing values. i The following locations are missing: 1. --- Code date_floor(x, "day", origin = new_datetime(Inf, zone)) Condition Error in `date_floor()`: ! `origin` can't be an infinite date. --- Code date_floor(x, "day", origin = new_datetime(c(0, 1), zone)) Condition Error in `date_floor()`: ! `origin` must have size 1, not size 2. --- Code date_floor(x, "day", origin = new_datetime(0, "")) Condition Error in `date_floor()`: ! `origin` must have the same time zone as `x`. --- Code date_floor(x, "day", origin = new_datetime(0, "America/Los_Angeles")) Condition Error in `date_floor()`: ! `origin` must have the same time zone as `x`. # default format is correct Code date_format(x) Output [1] "2019-01-01T00:00:00-05:00[America/New_York]" # can format date-times Code vapply(X = formats, FUN = function(format) date_format(x, format = format), FUN.VALUE = character(1)) Output C: %C y: %y "C: 20" "y: 18" Y: %Y b: %b "Y: 2018" "b: Dec" h: %h B: %B "h: Dec" "B: December" m: %m d: %d "m: 12" "d: 31" a: %a A: %A "a: Mon" "A: Monday" w: %w g: %g "w: 1" "g: 19" G: %G V: %V "G: 2019" "V: 01" u: %u U: %U "u: 1" "U: 52" W: %W j: %j "W: 53" "j: 365" D: %D x: %x "D: 12/31/18" "x: 12/31/18" F: %F H: %H "F: 2018-12-31" "H: 23" I: %I M: %M "I: 11" "M: 59" S: %S p: %p "S: 59" "p: PM" R: %R T: %T "R: 23:59" "T: 23:59:59" X: %X r: %r "X: 23:59:59" "r: 11:59:59 PM" c: %c %: %% "c: Mon Dec 31 23:59:59 2018" "%: %" z: %z Ez: %Ez "z: -0500" "Ez: -05:00" Z: %Z "Z: America/New_York" --- Code vapply(X = formats, FUN = function(format) date_format(x, format = format, locale = clock_locale("fr")), FUN.VALUE = character(1)) Output C: %C y: %y "C: 20" "y: 18" Y: %Y b: %b "Y: 2018" "b: déc." h: %h B: %B "h: déc." "B: décembre" m: %m d: %d "m: 12" "d: 31" a: %a A: %A "a: lun." "A: lundi" w: %w g: %g "w: 1" "g: 19" G: %G V: %V "G: 2019" "V: 01" u: %u U: %U "u: 1" "U: 52" W: %W j: %j "W: 53" "j: 365" D: %D x: %x "D: 12/31/18" "x: 12/31/18" F: %F H: %H "F: 2018-12-31" "H: 23" I: %I M: %M "I: 11" "M: 59" S: %S p: %p "S: 59" "p: PM" R: %R T: %T "R: 23:59" "T: 23:59:59" X: %X r: %r "X: 23:59:59" "r: 11:59:59 PM" c: %c %: %% "c: lun. déc. 31 23:59:59 2018" "%: %" z: %z Ez: %Ez "z: -0500" "Ez: -05:00" Z: %Z "Z: America/New_York" # `date_time_zone()` has a special error on Dates Code date_time_zone(new_date(0)) Condition Error in `date_time_zone()`: ! `x` can't be a . i is considered a naive time with an unspecified time zone. i Time zones can only be get or set for date-times ( or ). # `date_time_zone()` validates `x` Code date_time_zone(1) Condition Error in `date_time_zone()`: ! `x` must be a , not the number 1. # `date_time_set_zone()` has a special error on Dates Code date_time_set_zone(new_date(), "UTC") Condition Error in `date_time_set_zone()`: ! `x` can't be a . i is considered a naive time with an unspecified time zone. i Time zones can only be get or set for date-times ( or ). # `date_time_set_zone()` validates `x` Code date_time_set_zone(1, "UTC") Condition Error in `date_time_set_zone()`: ! `x` must be a , not the number 1. # can resolve ambiguity and nonexistent times Code date_time_parse("1970-04-26 02:30:00", "America/New_York") Condition Error in `as_zoned_time()`: ! Nonexistent time due to daylight saving time at location 1. i Resolve nonexistent time issues by specifying the `nonexistent` argument. --- Code date_time_parse("1970-10-25 01:30:00", "America/New_York") Condition Error in `as_zoned_time()`: ! Ambiguous time due to daylight saving time at location 1. i Resolve ambiguous time issues by specifying the `ambiguous` argument. # failure to parse throws a warning Code date_time_parse("foo", "America/New_York") Condition Warning: Failed to parse 1 string at location 1. Returning `NA` at that location. Output [1] NA # throws warning on failed parses Code date_time_parse_complete("foo") Condition Warning: Failed to parse 1 string at location 1. Returning `NA` at that location. Output [1] NA # abbrev - throws warning on failed parses Code date_time_parse_abbrev("foo", "America/New_York") Condition Warning: Failed to parse 1 string at location 1. Returning `NA` at that location. Output [1] NA # `ambiguous = x` retains the offset of `x` if applicable Code date_shift(x, as_weekday(x), ambiguous = "error") Condition Error in `as_zoned_time()`: ! Ambiguous time due to daylight saving time at location 1. i Resolve ambiguous time issues by specifying the `ambiguous` argument. # `zone` is required Code date_time_build(2019) Condition Error in `date_time_build()`: ! `zone` must be supplied. # can handle invalid dates Code date_time_build(2019, 1:12, 31, zone = zone) Condition Error in `invalid_resolve()`: ! Invalid date found at location 2. i Resolve invalid date issues by specifying the `invalid` argument. # can handle nonexistent times Code date_time_build(1970, 4, 26, 2, 30, zone = zone) Condition Error in `as_zoned_time()`: ! Nonexistent time due to daylight saving time at location 1. i Resolve nonexistent time issues by specifying the `nonexistent` argument. # can handle ambiguous times Code date_time_build(1970, 10, 25, 1, 30, zone = zone) Condition Error in `as_zoned_time()`: ! Ambiguous time due to daylight saving time at location 1. i Resolve ambiguous time issues by specifying the `ambiguous` argument. # boundaries are handled right Code x$begin Output [1] "-32767-01-01 UTC" --- Code x$end Output [1] "32767-12-31 UTC" # input must be a date-time Code date_time_info(1) Condition Error in `date_time_info()`: ! `x` must be a , not the number 1. # start: can't use invalid precisions Code date_start(date_time_build(2019, zone = "America/New_York"), "quarter") Condition Error in `calendar_start_end_checks()`: ! `precision` must be a valid precision for a , not "quarter". # can resolve nonexistent start issues Code (expect_error(date_start(x, "day"), class = "clock_error_nonexistent_time")) Output Error in `as_zoned_time()`: ! Nonexistent time due to daylight saving time at location 1. i Resolve nonexistent time issues by specifying the `nonexistent` argument. # can resolve ambiguous start issues Code (expect_error(date_start(x, "day"), class = "clock_error_ambiguous_time")) Output Error in `as_zoned_time()`: ! Ambiguous time due to daylight saving time at location 1. i Resolve ambiguous time issues by specifying the `ambiguous` argument. # end: can't use invalid precisions Code date_end(date_time_build(2019, zone = "America/New_York"), "quarter") Condition Error in `calendar_start_end_checks()`: ! `precision` must be a valid precision for a , not "quarter". # daily `by` uses naive-time around DST gaps Code date_seq(from, by = duration_days(1), total_size = 3) Condition Error in `as_zoned_time()`: ! Nonexistent time due to daylight saving time at location 2. i Resolve nonexistent time issues by specifying the `nonexistent` argument. # daily `by` uses naive-time around DST fallbacks Code date_seq(from, by = duration_days(1), total_size = 3) Condition Error in `as_zoned_time()`: ! Ambiguous time due to daylight saving time at location 2. i Resolve ambiguous time issues by specifying the `ambiguous` argument. # monthly / yearly `by` uses calendar -> naive-time around DST gaps Code date_seq(from, by = duration_months(1), total_size = 3) Condition Error in `as_zoned_time()`: ! Nonexistent time due to daylight saving time at location 2. i Resolve nonexistent time issues by specifying the `nonexistent` argument. # monthly / yearly `by` uses calendar -> naive-time around DST fallbacks Code date_seq(from, by = duration_years(1), total_size = 3) Condition Error in `as_zoned_time()`: ! Ambiguous time due to daylight saving time at location 2. i Resolve ambiguous time issues by specifying the `ambiguous` argument. # can resolve invalid dates Code date_seq(from, to = to, by = duration_months(1)) Condition Error in `invalid_resolve()`: ! Invalid date found at location 2. i Resolve invalid date issues by specifying the `invalid` argument. # components of `to` more precise than `by` must match `from` Code date_seq(date_time_build(2019, 1, 1, 2, 3, 20, zone = zone), to = date_time_build( 2019, 2, 2, 1, 3, 5, zone = zone), by = duration_minutes(1)) Condition Error in `date_seq()`: ! All components of `from` and `to` more precise than "minute" must match. i `from` is "2019-01-01T07:03:20". i `to` is "2019-02-02T06:03:05". --- Code date_seq(date_time_build(2019, 1, 1, zone = zone), to = date_time_build(2019, 2, 2, 2, zone = zone), by = duration_days(1)) Condition Error in `date_seq()`: ! All components of `from` and `to` more precise than "day" must match. i `from` is "2019-01-01T00:00:00". i `to` is "2019-02-02T02:00:00". --- Code date_seq(date_time_build(2019, 1, 1, zone = zone), to = date_time_build(2019, 2, 2, zone = zone), by = duration_months(1)) Condition Error in `date_seq()`: ! All components of `from` and `to` more precise than "month" must match. i `from` is "2019-01-01T00:00:00". i `to` is "2019-02-02T00:00:00". --- Code date_seq(date_time_build(2019, 1, 1, zone = zone), to = date_time_build(2019, 2, 1, 1, zone = zone), by = duration_months(1)) Condition Error in `date_seq()`: ! All components of `from` and `to` more precise than "month" must match. i `from` is "2019-01-01T00:00:00". i `to` is "2019-02-01T01:00:00". --- Code date_seq(date_time_build(2019, 1, 1, zone = zone), to = date_time_build(2019, 1, 2, zone = zone), by = duration_years(1)) Condition Error in `date_seq()`: ! All components of `from` and `to` more precise than "year" must match. i `from` is "2019-01-01T00:00:00". i `to` is "2019-01-02T00:00:00". # `to` must have same time zone as `by` Code date_seq(date_time_build(1970, zone = "UTC"), to = date_time_build(1970, zone = "America/New_York"), by = 1) Condition Error in `date_seq()`: ! `from` and `to` must have identical time zones. i `from` has zone "UTC". i `to` has zone "America/New_York". # validates integerish `by` Code date_seq(new_datetime(1), by = 1.5, total_size = 1) Condition Error in `date_seq()`: ! Can't convert from `by` to due to loss of precision. * Locations: 1 # validates `total_size` early Code date_seq(new_datetime(1), by = 1, total_size = 1.5) Condition Error in `date_seq()`: ! Can't convert from `total_size` to due to loss of precision. * Locations: 1 --- Code date_seq(new_datetime(1), by = 1, total_size = NA) Condition Error in `date_seq()`: ! `total_size` can't contain missing values. i The following locations are missing: 1. --- Code date_seq(new_datetime(1), by = 1, total_size = -1) Condition Error in `date_seq()`: ! `total_size` can't be negative. # `to` and `total_size` must not generate a non-fractional sequence Code date_seq(new_datetime(0), to = new_datetime(3), total_size = 3) Condition Error: ! The supplied output size does not result in a non-fractional sequence between `from` and `to`. # requires exactly two optional arguments Code date_seq(new_datetime(1), by = 1) Condition Error in `date_seq()`: ! Must specify exactly two of: * `to` * `by` * `total_size` --- Code date_seq(new_datetime(1), total_size = 1) Condition Error in `date_seq()`: ! Must specify exactly two of: * `to` * `by` * `total_size` --- Code date_seq(new_datetime(1), to = new_datetime(1)) Condition Error in `date_seq()`: ! Must specify exactly two of: * `to` * `by` * `total_size` # requires `to` to be POSIXt Code date_seq(new_datetime(1), to = 1, by = 1) Condition Error in `date_seq()`: ! `to` must be a , not the number 1. # requires year, month, day, hour, minute, or second precision Code date_seq(new_datetime(1), to = new_datetime(2), by = duration_nanoseconds(1)) Condition Error in `date_seq()`: ! `by` must have a precision of "year", "quarter", "month", "week", "day", "hour", "minute", or "second", not "nanosecond". # checks empty dots Code date_seq(new_datetime(1), new_datetime(2)) Condition Error in `date_seq()`: ! `...` must be empty. x Problematic argument: * ..1 = new_datetime(2) i Did you forget to name an argument? # must use a valid POSIXt precision Code (expect_error(date_count_between(x, x, "millisecond"))) Output Error in `date_count_between()`: ! `precision` must be "year", "quarter", "month", "week", "day", "hour", "minute", or "second", not "millisecond". # can't count between a POSIXt and a Date Code (expect_error(date_count_between(x, y, "year"))) Output Error in `date_count_between()`: ! `end` must be a , not a object. # op Code vec_arith("+", new_datetime(0, zone), duration_milliseconds(1)) Condition Error in `add_milliseconds()`: ! Can't perform this operation on a . --- Code vec_arith("+", new_posixlt(0, zone), duration_milliseconds(1)) Condition Error in `add_milliseconds()`: ! Can't perform this operation on a . --- Code vec_arith("*", new_datetime(0, zone), duration_years(1)) Condition Error in `arith_posixt_and_duration()`: ! > * > is not permitted --- Code vec_arith("*", new_posixlt(0, zone), duration_years(1)) Condition Error in `arith_posixt_and_duration()`: ! > * > is not permitted # op Code vec_arith("-", duration_years(1), new_datetime(0, zone)) Condition Error in `arith_duration_and_posixt()`: ! > - > is not permitted Can't subtract a POSIXct/POSIXlt from a duration. --- Code vec_arith("-", duration_years(1), new_posixlt(0, zone)) Condition Error in `arith_duration_and_posixt()`: ! > - > is not permitted Can't subtract a POSIXct/POSIXlt from a duration. --- Code vec_arith("+", duration_milliseconds(1), new_datetime(0, zone)) Condition Error in `add_milliseconds()`: ! Can't perform this operation on a . --- Code vec_arith("+", duration_milliseconds(1), new_posixlt(0, zone)) Condition Error in `add_milliseconds()`: ! Can't perform this operation on a . --- Code vec_arith("*", duration_years(1), new_datetime(0, zone)) Condition Error in `arith_duration_and_posixt()`: ! > * > is not permitted --- Code vec_arith("*", duration_years(1), new_posixlt(0, zone)) Condition Error in `arith_duration_and_posixt()`: ! > * > is not permitted # `slide_index()` will error on naive-time based arithmetic and ambiguous times Code slider::slide_index(x, i, identity, .after = after) Condition Error in `as_zoned_time()`: ! Ambiguous time due to daylight saving time at location 1. i Resolve ambiguous time issues by specifying the `ambiguous` argument. # `slide_index()` will error on naive-time based arithmetic and nonexistent times Code slider::slide_index(x, i, identity, .after = after) Condition Error in `as_zoned_time()`: ! Nonexistent time due to daylight saving time at location 1. i Resolve nonexistent time issues by specifying the `nonexistent` argument. # `slide_index()` will error on calendrical arithmetic and ambiguous times Code slider::slide_index(x, i, identity, .after = after) Condition Error in `as_zoned_time()`: ! Ambiguous time due to daylight saving time at location 1. i Resolve ambiguous time issues by specifying the `ambiguous` argument. # `slide_index()` will error on calendrical arithmetic and nonexistent times Code slider::slide_index(x, i, identity, .after = after) Condition Error in `as_zoned_time()`: ! Nonexistent time due to daylight saving time at location 1. i Resolve nonexistent time issues by specifying the `nonexistent` argument. clock/tests/testthat/_snaps/arithmetic.md0000644000176200001440000000277414427431001020313 0ustar liggesusers# arithmetic helpers throw default error Code add_years(x) Condition Error in `add_years()`: ! Can't perform this operation on a . --- Code add_quarters(x) Condition Error in `add_quarters()`: ! Can't perform this operation on a . --- Code add_months(x) Condition Error in `add_months()`: ! Can't perform this operation on a . --- Code add_weeks(x) Condition Error in `add_weeks()`: ! Can't perform this operation on a . --- Code add_days(x) Condition Error in `add_days()`: ! Can't perform this operation on a . --- Code add_hours(x) Condition Error in `add_hours()`: ! Can't perform this operation on a . --- Code add_minutes(x) Condition Error in `add_minutes()`: ! Can't perform this operation on a . --- Code add_seconds(x) Condition Error in `add_seconds()`: ! Can't perform this operation on a . --- Code add_milliseconds(x) Condition Error in `add_milliseconds()`: ! Can't perform this operation on a . --- Code add_microseconds(x) Condition Error in `add_microseconds()`: ! Can't perform this operation on a . --- Code add_nanoseconds(x) Condition Error in `add_nanoseconds()`: ! Can't perform this operation on a . clock/tests/testthat/_snaps/quarterly-year-quarter-day.md0000644000176200001440000003674514427431077023426 0ustar liggesusers# validates value ranges Code year_quarter_day(50000) Condition Error in `year_quarter_day()`: ! `year` must be between [-32767, 32767]. i Invalid results at locations: 1. --- Code year_quarter_day(2020, 5) Condition Error in `year_quarter_day()`: ! `quarter` must be between [1, 4]. i Invalid results at locations: 1. --- Code year_quarter_day(2020, 1, 93) Condition Error in `year_quarter_day()`: ! `day` must be between [1, 92]. i Invalid results at locations: 1. --- Code year_quarter_day(2020, 1, 1, 24) Condition Error in `year_quarter_day()`: ! `hour` must be between [0, 23]. i Invalid results at locations: 1. --- Code year_quarter_day(2020, 1, 1, 1, 60) Condition Error in `year_quarter_day()`: ! `minute` must be between [0, 59]. i Invalid results at locations: 1. --- Code year_quarter_day(2020, 1, 1, 1, 1, 60) Condition Error in `year_quarter_day()`: ! `second` must be between [0, 59]. i Invalid results at locations: 1. --- Code year_quarter_day(2020, 1, 1, 1, 1, 1, 1000, subsecond_precision = "millisecond") Condition Error in `year_quarter_day()`: ! `subsecond` must be between [0, 999]. i Invalid results at locations: 1. --- Code year_quarter_day(2020, 1, 1, 1, 1, 1, 1e+06, subsecond_precision = "microsecond") Condition Error in `year_quarter_day()`: ! `subsecond` must be between [0, 999999]. i Invalid results at locations: 1. --- Code year_quarter_day(2020, 1, 1, 1, 1, 1, 1e+09, subsecond_precision = "nanosecond") Condition Error in `year_quarter_day()`: ! `subsecond` must be between [0, 999999999]. i Invalid results at locations: 1. # full ptype is correct [1] "year_quarter_day" --- [1] "year_quarter_day" --- [1] "year_quarter_day" --- [1] "year_quarter_day" --- [1] "year_quarter_day" # abbreviated ptype is correct [1] "yqd" --- [1] "yqd" --- [1] "yqd" --- [1] "yqd" --- [1] "yqd" # setters recycling works both ways Code x <- year_quarter_day(1:2) y <- 1:3 set_quarter(x, y) Condition Error in `set_field_year_quarter_day()`: ! Can't recycle `x` (size 2) to match `value` (size 3). # setters require integer `value` Code set_year(x, 1.5) Condition Error in `set_field_year_quarter_day()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 --- Code set_quarter(x, 1.5) Condition Error in `set_field_year_quarter_day()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 --- Code set_day(x, 1.5) Condition Error in `set_field_year_quarter_day()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 --- Code set_hour(x, 1.5) Condition Error in `set_field_year_quarter_day()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 --- Code set_minute(x, 1.5) Condition Error in `set_field_year_quarter_day()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 --- Code set_second(x, 1.5) Condition Error in `set_field_year_quarter_day()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 --- Code set_millisecond(x, 1.5) Condition Error in `set_field_year_quarter_day()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 --- Code set_microsecond(x, 1.5) Condition Error in `set_field_year_quarter_day()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 --- Code set_nanosecond(x, 1.5) Condition Error in `set_field_year_quarter_day()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 # setters check `value` range Code set_year(x, 1e+05) Condition Error in `set_field_year_quarter_day()`: ! `value` must be between [-32767, 32767]. i Invalid results at locations: 1. --- Code set_quarter(x, 5) Condition Error in `set_field_year_quarter_day()`: ! `value` must be between [1, 4]. i Invalid results at locations: 1. --- Code set_day(x, 93) Condition Error in `set_field_year_quarter_day()`: ! `value` must be between [1, 92]. i Invalid results at locations: 1. --- Code set_hour(x, 24) Condition Error in `set_field_year_quarter_day()`: ! `value` must be between [0, 23]. i Invalid results at locations: 1. --- Code set_minute(x, 60) Condition Error in `set_field_year_quarter_day()`: ! `value` must be between [0, 59]. i Invalid results at locations: 1. --- Code set_second(x, 60) Condition Error in `set_field_year_quarter_day()`: ! `value` must be between [0, 59]. i Invalid results at locations: 1. --- Code set_millisecond(x, -1) Condition Error in `set_field_year_quarter_day()`: ! `value` must be between [0, 999]. i Invalid results at locations: 1. --- Code set_microsecond(x, -1) Condition Error in `set_field_year_quarter_day()`: ! `value` must be between [0, 999999]. i Invalid results at locations: 1. --- Code set_nanosecond(x, -1) Condition Error in `set_field_year_quarter_day()`: ! `value` must be between [0, 999999999]. i Invalid results at locations: 1. # setters require minimum precision Code set_day(year_quarter_day(year = 1), 1) Condition Error in `set_day()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be at least "quarter". i `x` has a precision of "year". --- Code set_hour(year_quarter_day(year = 1, quarter = 2), 1) Condition Error in `set_hour()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be at least "day". i `x` has a precision of "quarter". --- Code set_minute(year_quarter_day(year = 1, quarter = 2, day = 3), 1) Condition Error in `set_minute()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be at least "hour". i `x` has a precision of "day". --- Code set_second(year_quarter_day(year = 1, quarter = 2, day = 3, hour = 4), 1) Condition Error in `set_second()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be at least "minute". i `x` has a precision of "hour". --- Code set_millisecond(year_quarter_day(year = 1, quarter = 2, day = 3, hour = 4, minute = 5), 1) Condition Error in `set_millisecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "millisecond". i `x` has a precision of "minute". --- Code set_microsecond(year_quarter_day(year = 1, quarter = 2, day = 3, hour = 4, minute = 5), 1) Condition Error in `set_microsecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "microsecond". i `x` has a precision of "minute". --- Code set_nanosecond(year_quarter_day(year = 1, quarter = 2, day = 3, hour = 4, minute = 5), 1) Condition Error in `set_nanosecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "nanosecond". i `x` has a precision of "minute". # setters require correct subsecond precision Code set_millisecond(year_quarter_day(year = 1, quarter = 2, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "microsecond"), 1) Condition Error in `set_millisecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "millisecond". i `x` has a precision of "microsecond". --- Code set_millisecond(year_quarter_day(year = 1, quarter = 2, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "nanosecond"), 1) Condition Error in `set_millisecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "millisecond". i `x` has a precision of "nanosecond". --- Code set_microsecond(year_quarter_day(year = 1, quarter = 2, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "millisecond"), 1) Condition Error in `set_microsecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "microsecond". i `x` has a precision of "millisecond". --- Code set_microsecond(year_quarter_day(year = 1, quarter = 2, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "nanosecond"), 1) Condition Error in `set_microsecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "microsecond". i `x` has a precision of "nanosecond". --- Code set_nanosecond(year_quarter_day(year = 1, quarter = 2, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "millisecond"), 1) Condition Error in `set_nanosecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "nanosecond". i `x` has a precision of "millisecond". --- Code set_nanosecond(year_quarter_day(year = 1, quarter = 2, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "microsecond"), 1) Condition Error in `set_nanosecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "nanosecond". i `x` has a precision of "microsecond". # default formats are correct Code format(year_quarter_day(2019)) Output [1] "2019" --- Code format(year_quarter_day(2019, 1)) Output [1] "2019-Q1" --- Code format(year_quarter_day(2019, 1, 1, 1)) Output [1] "2019-Q1-01T01" --- Code format(year_quarter_day(2019, 1, 1, 1, 2, 3, 50, subsecond_precision = "microsecond")) Output [1] "2019-Q1-01T01:02:03.000050" # can't compute a unsupported count precision Code (expect_error(calendar_count_between(x, x, "day"))) Output Error in `calendar_count_between_standardize_precision_n()`: ! `precision` must be one of: 'year', 'quarter'. # only granular precisions are allowed Code seq(year_quarter_day(2019, 1, 1), by = 1, length.out = 2) Condition Error in `seq()`: ! `from` must be 'year' or 'quarter' precision. # strict mode can be activated Code invalid_resolve(year_quarter_day(2019, 1, 1)) Condition Error in `strict_validate_invalid()`: ! The global option, `clock.strict`, is currently set to `TRUE`. In this mode, `invalid` must be set and cannot be left as `NULL`. # throws known classed error Code invalid_resolve(year_quarter_day(2019, 1, 91)) Condition Error in `invalid_resolve()`: ! Invalid date found at location 1. i Resolve invalid date issues by specifying the `invalid` argument. # minimums are right Code clock_minimum(year_quarter_day(1)) Output [1]> [1] "-32767" Code clock_minimum(year_quarter_day(1, 1)) Output [1]> [1] "-32767-Q1" Code clock_minimum(year_quarter_day(1, 1, 1)) Output [1]> [1] "-32767-Q1-01" Code clock_minimum(year_quarter_day(1, 1, 1, 1)) Output [1]> [1] "-32767-Q1-01T00" Code clock_minimum(year_quarter_day(1, 1, 1, 1, 1)) Output [1]> [1] "-32767-Q1-01T00:00" Code clock_minimum(year_quarter_day(1, 1, 1, 1, 1, 1)) Output [1]> [1] "-32767-Q1-01T00:00:00" Code clock_minimum(year_quarter_day(1, 1, 1, 1, 1, 1, 1, subsecond_precision = "millisecond")) Output [1]> [1] "-32767-Q1-01T00:00:00.000" Code clock_minimum(year_quarter_day(1, 1, 1, 1, 1, 1, 1, subsecond_precision = "microsecond")) Output [1]> [1] "-32767-Q1-01T00:00:00.000000" Code clock_minimum(year_quarter_day(1, 1, 1, 1, 1, 1, 1, subsecond_precision = "nanosecond")) Output [1]> [1] "-32767-Q1-01T00:00:00.000000000" # maximums are right Code clock_maximum(year_quarter_day(1)) Output [1]> [1] "32767" Code clock_maximum(year_quarter_day(1, 1)) Output [1]> [1] "32767-Q4" Code clock_maximum(year_quarter_day(1, 1, 1)) Output [1]> [1] "32767-Q4-92" Code clock_maximum(year_quarter_day(1, 1, 1, 1)) Output [1]> [1] "32767-Q4-92T23" Code clock_maximum(year_quarter_day(1, 1, 1, 1, 1)) Output [1]> [1] "32767-Q4-92T23:59" Code clock_maximum(year_quarter_day(1, 1, 1, 1, 1, 1)) Output [1]> [1] "32767-Q4-92T23:59:59" Code clock_maximum(year_quarter_day(1, 1, 1, 1, 1, 1, 1, subsecond_precision = "millisecond")) Output [1]> [1] "32767-Q4-92T23:59:59.999" Code clock_maximum(year_quarter_day(1, 1, 1, 1, 1, 1, 1, subsecond_precision = "microsecond")) Output [1]> [1] "32767-Q4-92T23:59:59.999999" Code clock_maximum(year_quarter_day(1, 1, 1, 1, 1, 1, 1, subsecond_precision = "nanosecond")) Output [1]> [1] "32767-Q4-92T23:59:59.999999999" # minimums and maximums respect `start` Code clock_minimum(year_quarter_day(1, start = clock_months$february)) Output [1]> [1] "-32767" Code clock_maximum(year_quarter_day(1, start = clock_months$february)) Output [1]> [1] "32767" # add_*() respect recycling rules Code add_years(year_quarter_day(1:2), 1:3) Condition Error in `add_years()`: ! Can't recycle `x` (size 2) to match `n` (size 3). clock/tests/testthat/_snaps/time-point.md0000644000176200001440000002412714427431103020246 0ustar liggesusers# normal print method works Code x Output [5]> [1] "2019-01-01" "2019-02-01" "2019-03-01" "2019-04-01" "2019-05-01" # can limit with `max` Code print(x, max = 2) Output [5]> [1] "2019-01-01" "2019-02-01" Reached `max` or `getOption('max.print')`. Omitted 3 values. --- Code print(x, max = 4) Output [5]> [1] "2019-01-01" "2019-02-01" "2019-03-01" "2019-04-01" Reached `max` or `getOption('max.print')`. Omitted 1 value. --- Code print(x, max = 5) Output [5]> [1] "2019-01-01" "2019-02-01" "2019-03-01" "2019-04-01" "2019-05-01" --- Code print(x, max = 6) Output [5]> [1] "2019-01-01" "2019-02-01" "2019-03-01" "2019-04-01" "2019-05-01" # `max` defaults to `getOption('max.print')` but can be overridden Code x Output [5]> [1] "2019-01-01" "2019-02-01" "2019-03-01" Reached `max` or `getOption('max.print')`. Omitted 2 values. --- Code print(x, max = 4) Output [5]> [1] "2019-01-01" "2019-02-01" "2019-03-01" "2019-04-01" Reached `max` or `getOption('max.print')`. Omitted 1 value. --- Code print(x, max = 5) Output [5]> [1] "2019-01-01" "2019-02-01" "2019-03-01" "2019-04-01" "2019-05-01" # cannot floor to more precise precision Code time_point_floor(naive_days(), "second") Condition Error in `duration_rounder()`: ! Can't floor to a more precise precision. # rounding with `origin` requires same clock Code time_point_floor(x, "day", origin = origin) Condition Error in `time_point_floor()`: ! Can't convert `origin` > to >. # `origin` can be cast to a more precise `precision`, but not to a less precise one Code time_point_floor(x, "hour", origin = origin2) Condition Error in `time_point_floor()`: ! Can't convert `origin` > to >. Can't cast to a less precise precision. # `origin` must be size 1 Code time_point_floor(x, "day", origin = origin) Condition Error in `time_point_floor()`: ! `origin` must have size 1, not size 2. # `origin` must not be `NA` Code time_point_floor(x, "day", origin = origin) Condition Error in `time_point_floor()`: ! `origin` can't contain missing values. i The following locations are missing: 1. # `origin` can't be Date or POSIXt Code time_point_floor(x, "day", origin = origin1) Condition Error in `time_point_floor()`: ! Can't convert `origin` to >. --- Code time_point_floor(x, "day", origin = origin2) Condition Error in `time_point_floor()`: ! Can't convert `origin` > to >. # `target` is recycled to size of `x` Code time_point_shift(sys_days(0), weekday(1:2)) Condition Error in `time_point_shift()`: ! Can't recycle `target` (size 2) to size 1. # `x` is validated Code time_point_shift(1) Condition Error in `time_point_shift()`: ! `x` must be a , not the number 1. # `target` is validated Code time_point_shift(sys_days(0), 1) Condition Error in `time_point_shift()`: ! `target` must be a , not the number 1. # `which` is validated Code time_point_shift(sys_days(), weekday(), which = 1) Condition Error in `time_point_shift()`: ! `which` must be a single string, not the number 1. --- Code time_point_shift(sys_days(), weekday(), which = "foo") Condition Error in `time_point_shift()`: ! `which` must be one of "next" or "previous", not "foo". --- Code time_point_shift(sys_days(), weekday(), which = c("next", "previous")) Condition Error in `time_point_shift()`: ! `which` must be a single string, not a character vector. # `boundary` is validated Code time_point_shift(sys_days(), weekday(), boundary = 1) Condition Error in `time_point_shift()`: ! `boundary` must be a single string, not the number 1. --- Code time_point_shift(sys_days(), weekday(), boundary = "foo") Condition Error in `time_point_shift()`: ! `boundary` must be one of "keep" or "advance", not "foo". --- Code time_point_shift(sys_days(), weekday(), boundary = c("keep", "advance")) Condition Error in `time_point_shift()`: ! `boundary` must be a single string, not a character vector. # OOB results return a warning and NA Code out <- time_point_count_between(sys_days(0), sys_days(1000), "nanosecond") Condition Warning: Conversion from duration to integer is outside the range of an integer. `NA` values have been introduced, beginning at location 1. # both inputs must be time points Code (expect_error(time_point_count_between(sys_days(1), 1))) Output Error in `time_point_count_between()`: ! `end` must be a , not the number 1. Code (expect_error(time_point_count_between(1, sys_days(1)))) Output Error in `time_point_count_between()`: ! `start` must be a , not the number 1. # both inputs must be compatible Code (expect_error(time_point_count_between(x, y))) Output Error in `time_point_count_between()`: ! Can't combine `start` > and `end` >. # `n` is validated Code (expect_error(time_point_count_between(x, x, "day", n = NA_integer_))) Output Error in `time_point_count_between()`: ! `n` must be a whole number, not an integer `NA`. Code (expect_error(time_point_count_between(x, x, "day", n = -1))) Output Error in `time_point_count_between()`: ! `n` must be a whole number larger than or equal to 0, not the number -1. Code (expect_error(time_point_count_between(x, x, "day", n = 1.5))) Output Error in `time_point_count_between()`: ! `n` must be a whole number, not the number 1.5. Code (expect_error(time_point_count_between(x, x, "day", n = "x"))) Output Error in `time_point_count_between()`: ! `n` must be a whole number, not the string "x". Code (expect_error(time_point_count_between(x, x, "day", n = c(1L, 2L)))) Output Error in `time_point_count_between()`: ! `n` must be a whole number, not an integer vector. # `precision` must be a time point precision Code (expect_error(time_point_count_between(x, x, "year"))) Output Error in `time_point_count_between()`: ! `precision` must be at least "week" precision. # can't mix chronological time points and calendrical durations Code seq(naive_seconds(0), by = duration_years(1), length.out = 2) Condition Error in `seq()`: ! Can't convert `by` > to >. Can't cast between calendrical durations and chronological durations. # can't mix clocks in seq() Code seq(sys_seconds(0), to = naive_seconds(5), by = 1) Condition Error in `seq()`: ! Can't convert `to` > to match type of `from` >. # `to` is always cast to `from` Code seq(naive_days(0), to = naive_seconds(5), by = 2) Condition Error in `seq()`: ! Can't convert `to` > to match type of `from` >. Can't cast to a less precise precision. # validates the input Code time_point_spanning_seq(1) Condition Error in `time_point_spanning_seq()`: ! `x` must be a , not the number 1. # duration to add to a time-point must have at least week precision (#120) Code naive_seconds(0) + duration_years(1) Condition Error in `duration_arith()`: ! Can't combine `x` > and `y` >. Can't combine calendrical durations with chronological durations. # precision: can only be called on time points Code time_point_precision(duration_days()) Condition Error in `time_point_precision()`: ! `x` must be a , not a object. # unsupported time point addition throws good error Code add_years(x, 1) Condition Error in `add_years()`: ! Can't perform this operation on a . i Do you need to convert to a calendar first? i Use `as_year_month_day()` for a calendar that supports `add_years()`. --- Code add_quarters(x, 1) Condition Error in `add_quarters()`: ! Can't perform this operation on a . i Do you need to convert to a calendar first? i Use `as_year_quarter_day()` for a calendar that supports `add_quarters()`. --- Code add_months(x, 1) Condition Error in `add_months()`: ! Can't perform this operation on a . i Do you need to convert to a calendar first? i Use `as_year_month_day()` for a calendar that supports `add_months()`. # minimums are right Code clock_minimum(as_sys_time(duration_nanoseconds())) Output [1]> [1] "1677-09-21T00:12:43.145224192" Code clock_minimum(as_naive_time(duration_nanoseconds())) Output [1]> [1] "1677-09-21T00:12:43.145224192" # maximums are right Code clock_maximum(as_sys_time(duration_nanoseconds())) Output [1]> [1] "2262-04-11T23:47:16.854775807" Code clock_maximum(as_naive_time(duration_nanoseconds())) Output [1]> [1] "2262-04-11T23:47:16.854775807" clock/tests/testthat/_snaps/calendar.md0000644000176200001440000003137214427431004017732 0ustar liggesusers# normal print method works Code x Output [5]> [1] "2019-01" "2019-02" "2019-03" "2019-04" "2019-05" # can limit with `max` Code print(x, max = 2) Output [5]> [1] "2019-01" "2019-02" Reached `max` or `getOption('max.print')`. Omitted 3 values. --- Code print(x, max = 4) Output [5]> [1] "2019-01" "2019-02" "2019-03" "2019-04" Reached `max` or `getOption('max.print')`. Omitted 1 value. --- Code print(x, max = 5) Output [5]> [1] "2019-01" "2019-02" "2019-03" "2019-04" "2019-05" --- Code print(x, max = 6) Output [5]> [1] "2019-01" "2019-02" "2019-03" "2019-04" "2019-05" # `max` defaults to `getOption('max.print')` but can be overridden Code x Output [5]> [1] "2019-01" "2019-02" "2019-03" Reached `max` or `getOption('max.print')`. Omitted 2 values. --- Code print(x, max = 4) Output [5]> [1] "2019-01" "2019-02" "2019-03" "2019-04" Reached `max` or `getOption('max.print')`. Omitted 1 value. --- Code print(x, max = 5) Output [5]> [1] "2019-01" "2019-02" "2019-03" "2019-04" "2019-05" # `max` is validated Code print(x, max = -1) Condition Error in `print()`: ! `max` must be a whole number larger than or equal to 0, not the number -1. --- Code print(x, max = c(1, 2)) Condition Error in `print()`: ! `max` must be a whole number, not a double vector. --- Code print(x, max = NA_integer_) Condition Error in `print()`: ! `max` must be a whole number, not an integer `NA`. --- Code print(x, max = "foo") Condition Error in `print()`: ! `max` must be a whole number, not the string "foo". # group: `precision` is validated Code calendar_group(year_month_day(2019), "foo") Condition Error in `calendar_group()`: ! `precision` must be one of "year", "quarter", "month", "week", "day", "hour", "minute", "second", "millisecond", "microsecond", or "nanosecond", not "foo". # group: `precision` must be calendar specific Code calendar_group(year_month_day(2019), "quarter") Condition Error in `calendar_group()`: ! `precision` must be a valid precision for a , not "quarter". # group: `precision` can't be wider than `x` Code calendar_group(year_month_day(2019, 1, 1), "second") Condition Error in `calendar_group()`: ! Can't group at a precision ("second") that is more precise than `x` ("day"). # group: can't group a subsecond precision `x` at another subsecond precision Code calendar_group(x, "microsecond") Condition Error in `calendar_group()`: ! Can't group a subsecond precision `x` ("nanosecond") by another subsecond precision ("microsecond"). # narrow: `precision` is validated Code calendar_narrow(year_month_day(2019), "foo") Condition Error in `calendar_narrow()`: ! `precision` must be one of "year", "quarter", "month", "week", "day", "hour", "minute", "second", "millisecond", "microsecond", or "nanosecond", not "foo". # narrow: `precision` must be calendar specific Code calendar_narrow(year_month_day(2019), "quarter") Condition Error in `calendar_narrow()`: ! `precision` must be a valid precision for a , not "quarter". # narrow: `precision` can't be wider than `x` Code calendar_narrow(year_month_day(2019, 1, 1), "second") Condition Error in `calendar_narrow()`: ! Can't narrow to a precision ("second") that is wider than `x` ("day"). # narrow: can't narrow a subsecond precision `x` to another subsecond precision Code calendar_narrow(x, "microsecond") Condition Error in `calendar_narrow()`: ! Can't narrow a subsecond precision `x` ("nanosecond") to another subsecond precision ("microsecond"). # widen: `precision` is validated Code calendar_widen(year_month_day(2019), "foo") Condition Error in `calendar_widen()`: ! `precision` must be one of "year", "quarter", "month", "week", "day", "hour", "minute", "second", "millisecond", "microsecond", or "nanosecond", not "foo". # widen: `precision` must be calendar specific Code calendar_widen(year_month_day(2019), "quarter") Condition Error in `calendar_widen()`: ! `precision` must be a valid precision for a , not "quarter". # widen: `precision` can't be narrower than `x` Code calendar_widen(year_month_day(2019, 1, 1), "month") Condition Error in `calendar_widen()`: ! Can't widen to a precision ("month") that is narrower than `x` ("day"). # widen: can't widen a subsecond precision `x` to another subsecond precision Code calendar_widen(x, "microsecond") Condition Error in `calendar_widen()`: ! Can't widen a subsecond precision `x` ("millisecond") to another subsecond precision ("microsecond"). # start: `x` is validated Code calendar_start(1) Condition Error in `calendar_start()`: ! `x` must be a , not the number 1. # start: `precision` is validated Code calendar_start(year_month_day(2019), "foo") Condition Error in `calendar_start()`: ! `precision` must be one of "year", "quarter", "month", "week", "day", "hour", "minute", "second", "millisecond", "microsecond", or "nanosecond", not "foo". --- Code calendar_start(year_month_day(2019), 1) Condition Error in `calendar_start()`: ! `precision` must be a single string, not the number 1. # start: errors on unsupported precision Code calendar_start(year_month_day(2019, 1), "quarter") Condition Error in `calendar_start_end_checks()`: ! `precision` must be a valid precision for a , not "quarter". # start: `precision` can't be more precise than `x` Code calendar_start(year_month_day(2019), "month") Condition Error in `calendar_start_end_checks()`: ! Can't compute the start of `x` ("year") at a more precise precision ("month"). # start: can't mix different subsecond precisions Code calendar_start(x, "millisecond") Condition Error in `calendar_start_end_checks()`: ! Can't compute the start of a subsecond precision `x` ("microsecond") at another subsecond precision ("millisecond"). # end: `x` is validated Code calendar_end(1) Condition Error in `calendar_end()`: ! `x` must be a , not the number 1. # end: `precision` is validated Code calendar_end(year_month_day(2019), "foo") Condition Error in `calendar_end()`: ! `precision` must be one of "year", "quarter", "month", "week", "day", "hour", "minute", "second", "millisecond", "microsecond", or "nanosecond", not "foo". --- Code calendar_end(year_month_day(2019), 1) Condition Error in `calendar_end()`: ! `precision` must be a single string, not the number 1. # end: errors on unsupported precision Code calendar_end(year_month_day(2019, 1), "quarter") Condition Error in `calendar_start_end_checks()`: ! `precision` must be a valid precision for a , not "quarter". # end: `precision` can't be more precise than `x` Code calendar_end(year_month_day(2019), "month") Condition Error in `calendar_start_end_checks()`: ! Can't compute the end of `x` ("year") at a more precise precision ("month"). # end: can't mix different subsecond precisions Code calendar_end(x, "millisecond") Condition Error in `calendar_start_end_checks()`: ! Can't compute the end of a subsecond precision `x` ("microsecond") at another subsecond precision ("millisecond"). # `end` must be a calendar Code (expect_error(calendar_count_between(x, 1, "year"))) Output Error in `calendar_count_between()`: ! `end` must be a , not the number 1. # can't count with a precision finer than the calendar precision Code (expect_error(calendar_count_between(x, x, "month"))) Output Error in `calendar_count_between()`: ! Precision of inputs ("year") must be at least as precise as `precision` ("month"). # `n` is validated Code (expect_error(calendar_count_between(x, x, "year", n = NA_integer_))) Output Error in `calendar_count_between()`: ! `n` must be a whole number, not an integer `NA`. Code (expect_error(calendar_count_between(x, x, "year", n = -1))) Output Error in `calendar_count_between()`: ! `n` must be a whole number larger than or equal to 0, not the number -1. Code (expect_error(calendar_count_between(x, x, "year", n = 1.5))) Output Error in `calendar_count_between()`: ! `n` must be a whole number, not the number 1.5. Code (expect_error(calendar_count_between(x, x, "year", n = "x"))) Output Error in `calendar_count_between()`: ! `n` must be a whole number, not the string "x". Code (expect_error(calendar_count_between(x, x, "year", n = c(1L, 2L)))) Output Error in `calendar_count_between()`: ! `n` must be a whole number, not an integer vector. # validates the input Code calendar_spanning_seq(1) Condition Error in `calendar_spanning_seq()`: ! `x` must be a , not the number 1. # the input must be at a precision allowed by `seq()` Code calendar_spanning_seq(year_month_day(2019, 1, 2)) Condition Error in `seq()`: ! `from` must be 'year' or 'month' precision. # errors on types that don't support min/max calls Code calendar_spanning_seq(x) Condition Error in `vec_proxy_compare()`: ! 'year_month_weekday' types with a precision of >= 'day' cannot be trivially compared or ordered. Convert to 'year_month_day' to compare using day-of-month values. # precision: can only be called on calendars Code calendar_precision(sys_days(0)) Condition Error in `calendar_precision()`: ! `x` must be a , not a object. # addition helpers throw error with advice Code add_weeks(x) Condition Error in `add_weeks()`: ! Can't perform this operation on a . i Do you need to convert to a time point first? i Use `as_naive_time()` or `as_sys_time()` to convert to a time point. --- Code add_days(x) Condition Error in `add_days()`: ! Can't perform this operation on a . i Do you need to convert to a time point first? i Use `as_naive_time()` or `as_sys_time()` to convert to a time point. --- Code add_hours(x) Condition Error in `add_hours()`: ! Can't perform this operation on a . i Do you need to convert to a time point first? i Use `as_naive_time()` or `as_sys_time()` to convert to a time point. --- Code add_minutes(x) Condition Error in `add_minutes()`: ! Can't perform this operation on a . i Do you need to convert to a time point first? i Use `as_naive_time()` or `as_sys_time()` to convert to a time point. --- Code add_seconds(x) Condition Error in `add_seconds()`: ! Can't perform this operation on a . i Do you need to convert to a time point first? i Use `as_naive_time()` or `as_sys_time()` to convert to a time point. --- Code add_milliseconds(x) Condition Error in `add_milliseconds()`: ! Can't perform this operation on a . i Do you need to convert to a time point first? i Use `as_naive_time()` or `as_sys_time()` to convert to a time point. --- Code add_microseconds(x) Condition Error in `add_microseconds()`: ! Can't perform this operation on a . i Do you need to convert to a time point first? i Use `as_naive_time()` or `as_sys_time()` to convert to a time point. --- Code add_nanoseconds(x) Condition Error in `add_nanoseconds()`: ! Can't perform this operation on a . i Do you need to convert to a time point first? i Use `as_naive_time()` or `as_sys_time()` to convert to a time point. clock/tests/testthat/_snaps/invalid.md0000644000176200001440000000024714427431033017606 0ustar liggesusers# errors on unsupported types Code invalid_remove(1) Condition Error in `invalid_remove()`: ! Can't perform this operation on a . clock/tests/testthat/_snaps/sys-time.md0000644000176200001440000000435114427431100017725 0ustar liggesusers# failure to parse throws a warning Code sys_time_parse("foo") Condition Warning: Failed to parse 1 string at location 1. Returning `NA` at that location. Output [1]> [1] NA # `precision` must be at least second Code sys_time_parse_RFC_3339(x, precision = "day") Condition Error in `sys_time_parse_RFC_3339()`: ! `precision` must be at least "second" precision. # `separator` is validated Code sys_time_parse_RFC_3339(x, separator = 1) Condition Error in `sys_time_parse_RFC_3339()`: ! `separator` must be a string or character vector. --- Code sys_time_parse_RFC_3339(x, separator = "TT") Condition Error in `sys_time_parse_RFC_3339()`: ! `separator` must be one of "T", "t", or " ", not "TT". # `offset` is validated Code sys_time_parse_RFC_3339(x, offset = 1) Condition Error in `sys_time_parse_RFC_3339()`: ! `offset` must be a string or character vector. --- Code sys_time_parse_RFC_3339(x, offset = "ZZ") Condition Error in `sys_time_parse_RFC_3339()`: ! `offset` must be one of "Z", "z", "%z", or "%Ez", not "ZZ". i Did you mean "%z"? # sys-time-parse-RFC-3339: empty dots are checked Code sys_time_parse_RFC_3339(x, 1) Condition Error in `sys_time_parse()`: ! `...` must be empty. x Problematic argument: * ..1 = 1 i Did you forget to name an argument? # default format is correct Code format(sys_seconds(0)) Output [1] "1970-01-01T00:00:00" # empty dots are checked Code as_zoned_time(sys_seconds(), "UTC", 123) Condition Error in `as_zoned_time()`: ! `...` must be empty. x Problematic argument: * ..1 = 123 i Did you forget to name an argument? # `vec_ptype_full()` prints correctly Code vec_ptype_full(sys_days()) Output [1] "sys_time" Code vec_ptype_full(sys_seconds(1:5)) Output [1] "sys_time" # `vec_ptype_abbr()` prints correctly Code vec_ptype_abbr(sys_days()) Output [1] "sys" Code vec_ptype_abbr(sys_seconds(1:5)) Output [1] "sys" clock/tests/testthat/_snaps/getters.md0000644000176200001440000000313614427431013017633 0ustar liggesusers# getters throw default error Code get_year(x) Condition Error in `get_year()`: ! Can't perform this operation on a . --- Code get_quarter(x) Condition Error in `get_quarter()`: ! Can't perform this operation on a . --- Code get_month(x) Condition Error in `get_month()`: ! Can't perform this operation on a . --- Code get_week(x) Condition Error in `get_week()`: ! Can't perform this operation on a . --- Code get_day(x) Condition Error in `get_day()`: ! Can't perform this operation on a . --- Code get_hour(x) Condition Error in `get_hour()`: ! Can't perform this operation on a . --- Code get_minute(x) Condition Error in `get_minute()`: ! Can't perform this operation on a . --- Code get_second(x) Condition Error in `get_second()`: ! Can't perform this operation on a . --- Code get_millisecond(x) Condition Error in `get_millisecond()`: ! Can't perform this operation on a . --- Code get_microsecond(x) Condition Error in `get_microsecond()`: ! Can't perform this operation on a . --- Code get_nanosecond(x) Condition Error in `get_nanosecond()`: ! Can't perform this operation on a . --- Code get_index(x) Condition Error in `get_index()`: ! Can't perform this operation on a . clock/tests/testthat/_snaps/gregorian-year-day.md0000644000176200001440000003324414427431020021645 0ustar liggesusers# validates value ranges Code year_day(50000) Condition Error in `year_day()`: ! `year` must be between [-32767, 32767]. i Invalid results at locations: 1. --- Code year_day(2020, 367) Condition Error in `year_day()`: ! `day` must be between [1, 366]. i Invalid results at locations: 1. --- Code year_day(2020, 1, 24) Condition Error in `year_day()`: ! `hour` must be between [0, 23]. i Invalid results at locations: 1. --- Code year_day(2020, 1, 1, 60) Condition Error in `year_day()`: ! `minute` must be between [0, 59]. i Invalid results at locations: 1. --- Code year_day(2020, 1, 1, 1, 60) Condition Error in `year_day()`: ! `second` must be between [0, 59]. i Invalid results at locations: 1. --- Code year_day(2020, 1, 1, 1, 1, 1000, subsecond_precision = "millisecond") Condition Error in `year_day()`: ! `subsecond` must be between [0, 999]. i Invalid results at locations: 1. --- Code year_day(2020, 1, 1, 1, 1, 1e+06, subsecond_precision = "microsecond") Condition Error in `year_day()`: ! `subsecond` must be between [0, 999999]. i Invalid results at locations: 1. --- Code year_day(2020, 1, 1, 1, 1, 1e+09, subsecond_precision = "nanosecond") Condition Error in `year_day()`: ! `subsecond` must be between [0, 999999999]. i Invalid results at locations: 1. # full ptype is correct [1] "year_day" --- [1] "year_day" --- [1] "year_day" --- [1] "year_day" # abbreviated ptype is correct [1] "yd" --- [1] "yd" --- [1] "yd" --- [1] "yd" # setters recycling works both ways Code x <- year_day(1:2) y <- 1:3 set_day(x, y) Condition Error in `set_field_year_day()`: ! Can't recycle `x` (size 2) to match `value` (size 3). # setters require integer `value` Code set_year(x, 1.5) Condition Error in `set_field_year_day()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 --- Code set_day(x, 1.5) Condition Error in `set_field_year_day()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 --- Code set_hour(x, 1.5) Condition Error in `set_field_year_day()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 --- Code set_minute(x, 1.5) Condition Error in `set_field_year_day()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 --- Code set_second(x, 1.5) Condition Error in `set_field_year_day()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 --- Code set_millisecond(x, 1.5) Condition Error in `set_field_year_day()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 --- Code set_microsecond(x, 1.5) Condition Error in `set_field_year_day()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 --- Code set_nanosecond(x, 1.5) Condition Error in `set_field_year_day()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 # setters check `value` range Code set_year(x, 1e+05) Condition Error in `set_field_year_day()`: ! `value` must be between [-32767, 32767]. i Invalid results at locations: 1. --- Code set_day(x, 367) Condition Error in `set_field_year_day()`: ! `value` must be between [1, 366]. i Invalid results at locations: 1. --- Code set_hour(x, 24) Condition Error in `set_field_year_day()`: ! `value` must be between [0, 23]. i Invalid results at locations: 1. --- Code set_minute(x, 60) Condition Error in `set_field_year_day()`: ! `value` must be between [0, 59]. i Invalid results at locations: 1. --- Code set_second(x, 60) Condition Error in `set_field_year_day()`: ! `value` must be between [0, 59]. i Invalid results at locations: 1. --- Code set_millisecond(x, -1) Condition Error in `set_field_year_day()`: ! `value` must be between [0, 999]. i Invalid results at locations: 1. --- Code set_microsecond(x, -1) Condition Error in `set_field_year_day()`: ! `value` must be between [0, 999999]. i Invalid results at locations: 1. --- Code set_nanosecond(x, -1) Condition Error in `set_field_year_day()`: ! `value` must be between [0, 999999999]. i Invalid results at locations: 1. # setters require minimum precision Code set_hour(year_day(year = 1), 1) Condition Error in `set_hour()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be at least "day". i `x` has a precision of "year". --- Code set_minute(year_day(year = 1, day = 1), 1) Condition Error in `set_minute()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be at least "hour". i `x` has a precision of "day". --- Code set_second(year_day(year = 1, day = 1, hour = 1), 1) Condition Error in `set_second()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be at least "minute". i `x` has a precision of "hour". --- Code set_millisecond(year_day(year = 1, day = 1, hour = 1, minute = 1), 1) Condition Error in `set_millisecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "millisecond". i `x` has a precision of "minute". --- Code set_microsecond(year_day(year = 1, day = 1, hour = 1, minute = 1), 1) Condition Error in `set_microsecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "microsecond". i `x` has a precision of "minute". --- Code set_nanosecond(year_day(year = 1, day = 1, hour = 1, minute = 1), 1) Condition Error in `set_nanosecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "nanosecond". i `x` has a precision of "minute". # setters require correct subsecond precision Code set_millisecond(year_day(year = 1, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "microsecond"), 1) Condition Error in `set_millisecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "millisecond". i `x` has a precision of "microsecond". --- Code set_millisecond(year_day(year = 1, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "nanosecond"), 1) Condition Error in `set_millisecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "millisecond". i `x` has a precision of "nanosecond". --- Code set_microsecond(year_day(year = 1, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "millisecond"), 1) Condition Error in `set_microsecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "microsecond". i `x` has a precision of "millisecond". --- Code set_microsecond(year_day(year = 1, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "nanosecond"), 1) Condition Error in `set_microsecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "microsecond". i `x` has a precision of "nanosecond". --- Code set_nanosecond(year_day(year = 1, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "millisecond"), 1) Condition Error in `set_nanosecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "nanosecond". i `x` has a precision of "millisecond". --- Code set_nanosecond(year_day(year = 1, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "microsecond"), 1) Condition Error in `set_nanosecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "nanosecond". i `x` has a precision of "microsecond". # invalid dates must be resolved when converting to another calendar Code as_year_quarter_day(year_day(2019, 366)) Condition Error in `as_sys_time()`: ! Can't convert `x` to another type because some dates are invalid. i The following locations are invalid: 1. i Resolve invalid dates with `invalid_resolve()`. # invalid dates must be resolved when converting to a sys-time Code as_sys_time(year_day(2019, 366)) Condition Error in `as_sys_time()`: ! Can't convert `x` to another type because some dates are invalid. i The following locations are invalid: 1. i Resolve invalid dates with `invalid_resolve()`. # invalid dates must be resolved when converting to a naive-time Code as_naive_time(year_day(2019, 366)) Condition Error in `as_sys_time()`: ! Can't convert `x` to another type because some dates are invalid. i The following locations are invalid: 1. i Resolve invalid dates with `invalid_resolve()`. # default formats are correct Code format(year_day(2019)) Output [1] "2019" --- Code format(year_day(2019, 1)) Output [1] "2019-001" --- Code format(year_day(2019, 1, 1)) Output [1] "2019-001T01" --- Code format(year_day(2019, 1, 1, 2, 3, 50, subsecond_precision = "microsecond")) Output [1] "2019-001T01:02:03.000050" # can't compute a unsupported difference precision Code (expect_error(calendar_count_between(x, x, "day"))) Output Error in `calendar_count_between_standardize_precision_n()`: ! `precision` must be one of: 'year'. # only granular precisions are allowed Code seq(year_day(2019, 1), by = 1, length.out = 2) Condition Error in `seq()`: ! `from` must be 'year' precision. # strict mode can be activated Code invalid_resolve(year_day(2019, 1)) Condition Error in `strict_validate_invalid()`: ! The global option, `clock.strict`, is currently set to `TRUE`. In this mode, `invalid` must be set and cannot be left as `NULL`. # throws known classed error Code invalid_resolve(year_day(2019, 366)) Condition Error in `invalid_resolve()`: ! Invalid date found at location 1. i Resolve invalid date issues by specifying the `invalid` argument. # minimums are right Code clock_minimum(clock_empty_year_day_year) Output [1]> [1] "-32767" Code clock_minimum(clock_empty_year_day_day) Output [1]> [1] "-32767-001" Code clock_minimum(clock_empty_year_day_hour) Output [1]> [1] "-32767-001T00" Code clock_minimum(clock_empty_year_day_minute) Output [1]> [1] "-32767-001T00:00" Code clock_minimum(clock_empty_year_day_second) Output [1]> [1] "-32767-001T00:00:00" Code clock_minimum(clock_empty_year_day_millisecond) Output [1]> [1] "-32767-001T00:00:00.000" Code clock_minimum(clock_empty_year_day_microsecond) Output [1]> [1] "-32767-001T00:00:00.000000" Code clock_minimum(clock_empty_year_day_nanosecond) Output [1]> [1] "-32767-001T00:00:00.000000000" # maximums are right Code clock_maximum(clock_empty_year_day_year) Output [1]> [1] "32767" Code clock_maximum(clock_empty_year_day_day) Output [1]> [1] "32767-365" Code clock_maximum(clock_empty_year_day_hour) Output [1]> [1] "32767-365T23" Code clock_maximum(clock_empty_year_day_minute) Output [1]> [1] "32767-365T23:59" Code clock_maximum(clock_empty_year_day_second) Output [1]> [1] "32767-365T23:59:59" Code clock_maximum(clock_empty_year_day_millisecond) Output [1]> [1] "32767-365T23:59:59.999" Code clock_maximum(clock_empty_year_day_microsecond) Output [1]> [1] "32767-365T23:59:59.999999" Code clock_maximum(clock_empty_year_day_nanosecond) Output [1]> [1] "32767-365T23:59:59.999999999" # add_*() respect recycling rules Code add_years(year_day(1:2), 1:3) Condition Error in `add_years()`: ! Can't recycle `x` (size 2) to match `n` (size 3). clock/tests/testthat/_snaps/date.md0000644000176200001440000003327514427431010017077 0ustar liggesusers# invalid dates must be resolved when converting to a Date Code as.Date(year_month_day(2019, 2, 31)) Condition Error in `as_sys_time()`: ! Can't convert `x` to another type because some dates are invalid. i The following locations are invalid: 1. i Resolve invalid dates with `invalid_resolve()`. # can resolve nonexistent midnight issues Code (expect_error(as_zoned_time(x, zone), class = "clock_error_nonexistent_time")) Output Error in `as_zoned_time()`: ! Nonexistent time due to daylight saving time at location 1. i Resolve nonexistent time issues by specifying the `nonexistent` argument. # can resolve ambiguous midnight issues Code (expect_error(as_zoned_time(x, zone), class = "clock_error_ambiguous_time")) Output Error in `as_zoned_time()`: ! Ambiguous time due to daylight saving time at location 1. i Resolve ambiguous time issues by specifying the `ambiguous` argument. # can't group by finer precisions Code date_group(x, "hour") Condition Error in `calendar_group()`: ! Can't group at a precision ("hour") that is more precise than `x` ("day"). --- Code date_group(x, "nanosecond") Condition Error in `calendar_group()`: ! Can't group at a precision ("nanosecond") that is more precise than `x` ("day"). # can't group by non-year-month-day precisions Code date_group(x, "quarter") Condition Error in `calendar_group()`: ! `precision` must be a valid precision for a , not "quarter". # can only floor by week/day Code date_floor(as.Date("2019-01-01"), "hour") Condition Error in `duration_rounder()`: ! Can't floor to a more precise precision. --- Code date_floor(as.Date("2019-01-01"), "month") Condition Error in `time_point_rounder()`: ! `precision` must be one of "day", "hour", "minute", "second", "millisecond", "microsecond", or "nanosecond", not "month". i Did you mean "minute"? # `origin` is validated Code date_floor(x, "day", origin = 1) Condition Error in `date_floor()`: ! `origin` must be a , not the number 1. --- Code date_floor(x, "day", origin = new_date(NA_real_)) Condition Error in `date_floor()`: ! `origin` can't contain missing values. i The following locations are missing: 1. --- Code date_floor(x, "day", origin = new_date(Inf)) Condition Error in `date_floor()`: ! `origin` can't be an infinite date. --- Code date_floor(x, "day", origin = new_date(c(0, 1))) Condition Error in `date_floor()`: ! `origin` must have size 1, not size 2. # can format dates Code vapply(X = formats, FUN = function(format) date_format(x, format = format), FUN.VALUE = character(1)) Output C: %C y: %y "C: 20" "y: 18" Y: %Y b: %b "Y: 2018" "b: Dec" h: %h B: %B "h: Dec" "B: December" m: %m d: %d "m: 12" "d: 31" a: %a A: %A "a: Mon" "A: Monday" w: %w g: %g "w: 1" "g: 19" G: %G V: %V "G: 2019" "V: 01" u: %u U: %U "u: 1" "U: 52" W: %W j: %j "W: 53" "j: 365" D: %D x: %x "D: 12/31/18" "x: 12/31/18" F: %F H: %H "F: 2018-12-31" "H: 00" I: %I M: %M "I: 12" "M: 00" S: %S p: %p "S: 00" "p: AM" R: %R T: %T "R: 00:00" "T: 00:00:00" X: %X r: %r "X: 00:00:00" "r: 12:00:00 AM" c: %c %: %% "c: Mon Dec 31 00:00:00 2018" "%: %" --- Code vapply(X = formats, FUN = function(format) date_format(x, format = format, locale = clock_locale("fr")), FUN.VALUE = character(1)) Output C: %C y: %y "C: 20" "y: 18" Y: %Y b: %b "Y: 2018" "b: déc." h: %h B: %B "h: déc." "B: décembre" m: %m d: %d "m: 12" "d: 31" a: %a A: %A "a: lun." "A: lundi" w: %w g: %g "w: 1" "g: 19" G: %G V: %V "G: 2019" "V: 01" u: %u U: %U "u: 1" "U: 52" W: %W j: %j "W: 53" "j: 365" D: %D x: %x "D: 12/31/18" "x: 12/31/18" F: %F H: %H "F: 2018-12-31" "H: 00" I: %I M: %M "I: 12" "M: 00" S: %S p: %p "S: 00" "p: AM" R: %R T: %T "R: 00:00" "T: 00:00:00" X: %X r: %r "X: 00:00:00" "r: 12:00:00 AM" c: %c %: %% "c: lun. déc. 31 00:00:00 2018" "%: %" # formatting Dates with `%z` or `%Z` returns NA with a warning Code date_format(x, format = "%z") Condition Warning: Failed to format 1 string at location 1. Returning `NA` at that location. Output [1] NA --- Code date_format(x, format = "%Z") Condition Warning: Failed to format 1 string at location 1. Returning `NA` at that location. Output [1] NA # failure to parse throws a warning Code date_parse("foo") Condition Warning: Failed to parse 1 string at location 1. Returning `NA` at that location. Output [1] NA # can handle invalid dates Code date_build(2019, 1:12, 31) Condition Error in `invalid_resolve()`: ! Invalid date found at location 2. i Resolve invalid date issues by specifying the `invalid` argument. # start: can't use invalid precisions Code date_start(date_build(2019), "quarter") Condition Error in `calendar_start_end_checks()`: ! `precision` must be a valid precision for a , not "quarter". # end: can't use invalid precisions Code date_end(date_build(2019), "quarter") Condition Error in `calendar_start_end_checks()`: ! `precision` must be a valid precision for a , not "quarter". # can resolve invalid dates Code date_seq(from, to = to, by = duration_months(1)) Condition Error in `invalid_resolve()`: ! Invalid date found at location 2. i Resolve invalid date issues by specifying the `invalid` argument. # components of `to` more precise than `by` must match `from` Code date_seq(date_build(2019, 1, 1), to = date_build(2019, 2, 2), by = duration_months( 1)) Condition Error in `date_seq()`: ! All components of `from` and `to` more precise than "month" must match. i `from` is "2019-01-01". i `to` is "2019-02-02". --- Code date_seq(date_build(2019, 1, 1), to = date_build(2019, 3, 1), by = duration_years( 1)) Condition Error in `date_seq()`: ! All components of `from` and `to` more precise than "year" must match. i `from` is "2019-01-01". i `to` is "2019-03-01". # validates integerish `by` Code date_seq(new_date(1), by = 1.5, total_size = 1) Condition Error in `date_seq()`: ! Can't convert from `by` to due to loss of precision. * Locations: 1 # validates `total_size` early Code date_seq(new_date(1), by = 1, total_size = 1.5) Condition Error in `date_seq()`: ! Can't convert from `total_size` to due to loss of precision. * Locations: 1 --- Code date_seq(new_date(1), by = 1, total_size = NA) Condition Error in `date_seq()`: ! `total_size` can't contain missing values. i The following locations are missing: 1. --- Code date_seq(new_date(1), by = 1, total_size = -1) Condition Error in `date_seq()`: ! `total_size` can't be negative. # `to` and `total_size` must not generate a non-fractional sequence Code date_seq(new_date(0), to = new_date(3), total_size = 3) Condition Error: ! The supplied output size does not result in a non-fractional sequence between `from` and `to`. # requires exactly two optional arguments Code date_seq(new_date(1), by = 1) Condition Error in `date_seq()`: ! Must specify exactly two of: * `to` * `by` * `total_size` --- Code date_seq(new_date(1), total_size = 1) Condition Error in `date_seq()`: ! Must specify exactly two of: * `to` * `by` * `total_size` --- Code date_seq(new_date(1), to = new_date(1)) Condition Error in `date_seq()`: ! Must specify exactly two of: * `to` * `by` * `total_size` # requires `to` to be Date Code date_seq(new_date(1), to = 1, by = 1) Condition Error in `date_seq()`: ! `to` must be a , not the number 1. # requires year, month, or day precision Code date_seq(new_date(1), to = new_date(2), by = duration_nanoseconds(1)) Condition Error in `date_seq()`: ! `by` must have a precision of "year", "quarter", "month", "week", or "day", not "nanosecond". # checks empty dots Code date_seq(new_date(1), new_date(2)) Condition Error in `date_seq()`: ! `...` must be empty. x Problematic argument: * ..1 = new_date(2) i Did you forget to name an argument? # must use a valid Date precision Code (expect_error(date_count_between(x, x, "hour"))) Output Error in `date_count_between()`: ! `precision` must be "year", "quarter", "month", "week", or "day", not "hour". # can't count between a Date and a POSIXt Code (expect_error(date_count_between(x, y, "year"))) Output Error in `date_count_between()`: ! `end` must be a , not a object. # op Code vec_arith("+", new_date(0), duration_hours(1)) Condition Error in `add_hours()`: ! Can't perform this operation on a . --- Code vec_arith("*", new_date(0), duration_years(1)) Condition Error in `arith_date_and_duration()`: ! * > is not permitted # op Code vec_arith("-", duration_years(1), new_date(0)) Condition Error in `arith_duration_and_date()`: ! > - is not permitted Can't subtract a Date from a duration. --- Code vec_arith("+", duration_hours(1), new_date(0)) Condition Error in `add_hours()`: ! Can't perform this operation on a . --- Code vec_arith("*", duration_years(1), new_date(0)) Condition Error in `arith_duration_and_date()`: ! > * is not permitted # `slide_index()` will error on calendrical arithmetic and invalid dates Code slider::slide_index(x, i, identity, .after = after) Condition Error in `invalid_resolve()`: ! Invalid date found at location 2. i Resolve invalid date issues by specifying the `invalid` argument. clock/tests/testthat/_snaps/gregorian-year-month-weekday.md0000644000176200001440000004313114427431032023643 0ustar liggesusers# both day and index must be specified Code year_month_weekday(2020, 1, 1) Condition Error in `year_month_weekday()`: ! If either `day` or `index` is specified, both must be specified. # validates value ranges Code year_month_weekday(50000) Condition Error in `year_month_weekday()`: ! `year` must be between [-32767, 32767]. i Invalid results at locations: 1. --- Code year_month_weekday(2020, 13) Condition Error in `year_month_weekday()`: ! `month` must be between [1, 12]. i Invalid results at locations: 1. --- Code year_month_weekday(2020, 1, 32, 1) Condition Error in `year_month_weekday()`: ! `day` must be between [1, 7]. i Invalid results at locations: 1. --- Code year_month_weekday(2020, 1, 1, 6) Condition Error in `year_month_weekday()`: ! `index` must be between [1, 5]. i Invalid results at locations: 1. --- Code year_month_weekday(2020, 1, 1, 1, 24) Condition Error in `year_month_weekday()`: ! `hour` must be between [0, 23]. i Invalid results at locations: 1. --- Code year_month_weekday(2020, 1, 1, 1, 1, 60) Condition Error in `year_month_weekday()`: ! `minute` must be between [0, 59]. i Invalid results at locations: 1. --- Code year_month_weekday(2020, 1, 1, 1, 1, 1, 60) Condition Error in `year_month_weekday()`: ! `second` must be between [0, 59]. i Invalid results at locations: 1. --- Code year_month_weekday(2020, 1, 1, 1, 1, 1, 1, 1000, subsecond_precision = "millisecond") Condition Error in `year_month_weekday()`: ! `subsecond` must be between [0, 999]. i Invalid results at locations: 1. --- Code year_month_weekday(2020, 1, 1, 1, 1, 1, 1, 1e+06, subsecond_precision = "microsecond") Condition Error in `year_month_weekday()`: ! `subsecond` must be between [0, 999999]. i Invalid results at locations: 1. --- Code year_month_weekday(2020, 1, 1, 1, 1, 1, 1, 1e+09, subsecond_precision = "nanosecond") Condition Error in `year_month_weekday()`: ! `subsecond` must be between [0, 999999999]. i Invalid results at locations: 1. # cannot compare / sort with day precision or finer Code x > x Condition Error in `vec_proxy_compare()`: ! 'year_month_weekday' types with a precision of >= 'day' cannot be trivially compared or ordered. Convert to 'year_month_day' to compare using day-of-month values. --- Code vec_order(x) Condition Error in `vec_proxy_compare()`: ! 'year_month_weekday' types with a precision of >= 'day' cannot be trivially compared or ordered. Convert to 'year_month_day' to compare using day-of-month values. # full ptype is correct [1] "year_month_weekday" --- [1] "year_month_weekday" --- [1] "year_month_weekday" --- [1] "year_month_weekday" # abbreviated ptype is correct [1] "ymw" --- [1] "ymw" --- [1] "ymw" --- [1] "ymw" # setters recycling works both ways Code x <- year_month_weekday(1:2) y <- 1:3 set_month(x, y) Condition Error in `set_field_year_month_weekday()`: ! Can't recycle `x` (size 2) to match `value` (size 3). # setters require integer `value` Code set_year(x, 1.5) Condition Error in `set_field_year_month_weekday()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 --- Code set_month(x, 1.5) Condition Error in `set_field_year_month_weekday()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 --- Code set_day(x, 1.5) Condition Error in `set_field_year_month_weekday()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 --- Code set_index(x, 1.5) Condition Error in `set_field_year_month_weekday()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 --- Code set_hour(x, 1.5) Condition Error in `set_field_year_month_weekday()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 --- Code set_minute(x, 1.5) Condition Error in `set_field_year_month_weekday()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 --- Code set_second(x, 1.5) Condition Error in `set_field_year_month_weekday()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 --- Code set_millisecond(x, 1.5) Condition Error in `set_field_year_month_weekday()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 --- Code set_microsecond(x, 1.5) Condition Error in `set_field_year_month_weekday()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 --- Code set_nanosecond(x, 1.5) Condition Error in `set_field_year_month_weekday()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 # setters check `value` range Code set_year(x, 1e+05) Condition Error in `set_field_year_month_weekday()`: ! `value` must be between [-32767, 32767]. i Invalid results at locations: 1. --- Code set_month(x, 13) Condition Error in `set_field_year_month_weekday()`: ! `value` must be between [1, 12]. i Invalid results at locations: 1. --- Code set_day(x, 8) Condition Error in `set_field_year_month_weekday()`: ! `value` must be between [1, 7]. i Invalid results at locations: 1. --- Code set_index(x, 6) Condition Error in `set_field_year_month_weekday()`: ! `value` must be between [1, 5]. i Invalid results at locations: 1. --- Code set_hour(x, 24) Condition Error in `set_field_year_month_weekday()`: ! `value` must be between [0, 23]. i Invalid results at locations: 1. --- Code set_minute(x, 60) Condition Error in `set_field_year_month_weekday()`: ! `value` must be between [0, 59]. i Invalid results at locations: 1. --- Code set_second(x, 60) Condition Error in `set_field_year_month_weekday()`: ! `value` must be between [0, 59]. i Invalid results at locations: 1. --- Code set_millisecond(x, -1) Condition Error in `set_field_year_month_weekday()`: ! `value` must be between [0, 999]. i Invalid results at locations: 1. --- Code set_microsecond(x, -1) Condition Error in `set_field_year_month_weekday()`: ! `value` must be between [0, 999999]. i Invalid results at locations: 1. --- Code set_nanosecond(x, -1) Condition Error in `set_field_year_month_weekday()`: ! `value` must be between [0, 999999999]. i Invalid results at locations: 1. # setters require minimum precision Code set_day(year_month_weekday(year = 1), 1, index = 1) Condition Error in `set_day()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be at least "month". i `x` has a precision of "year". --- Code set_hour(year_month_weekday(year = 1, month = 2), 1) Condition Error in `set_hour()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be at least "day". i `x` has a precision of "month". --- Code set_minute(year_month_weekday(year = 1, month = 2, day = 3, index = 1), 1) Condition Error in `set_minute()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be at least "hour". i `x` has a precision of "day". --- Code set_second(year_month_weekday(year = 1, month = 2, day = 3, index = 1, hour = 4), 1) Condition Error in `set_second()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be at least "minute". i `x` has a precision of "hour". --- Code set_millisecond(year_month_weekday(year = 1, month = 2, day = 3, index = 1, hour = 4, minute = 5), 1) Condition Error in `set_millisecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "millisecond". i `x` has a precision of "minute". --- Code set_microsecond(year_month_weekday(year = 1, month = 2, day = 3, index = 1, hour = 4, minute = 5), 1) Condition Error in `set_microsecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "microsecond". i `x` has a precision of "minute". --- Code set_nanosecond(year_month_weekday(year = 1, month = 2, day = 3, index = 1, hour = 4, minute = 5), 1) Condition Error in `set_nanosecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "nanosecond". i `x` has a precision of "minute". # setters require correct subsecond precision Code set_millisecond(year_month_weekday(year = 1, month = 2, day = 3, index = 1, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "microsecond"), 1) Condition Error in `set_millisecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "millisecond". i `x` has a precision of "microsecond". --- Code set_millisecond(year_month_weekday(year = 1, month = 2, day = 3, index = 1, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "nanosecond"), 1) Condition Error in `set_millisecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "millisecond". i `x` has a precision of "nanosecond". --- Code set_microsecond(year_month_weekday(year = 1, month = 2, day = 3, index = 1, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "millisecond"), 1) Condition Error in `set_microsecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "microsecond". i `x` has a precision of "millisecond". --- Code set_microsecond(year_month_weekday(year = 1, month = 2, day = 3, index = 1, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "nanosecond"), 1) Condition Error in `set_microsecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "microsecond". i `x` has a precision of "nanosecond". --- Code set_nanosecond(year_month_weekday(year = 1, month = 2, day = 3, index = 1, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "millisecond"), 1) Condition Error in `set_nanosecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "nanosecond". i `x` has a precision of "millisecond". --- Code set_nanosecond(year_month_weekday(year = 1, month = 2, day = 3, index = 1, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "microsecond"), 1) Condition Error in `set_nanosecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "nanosecond". i `x` has a precision of "microsecond". # default formats are correct Code format(year_month_weekday(2019)) Output [1] "2019" --- Code format(year_month_weekday(2019, 1)) Output [1] "2019-01" --- Code format(year_month_weekday(2019, 1, 1, 2, 1)) Output [1] "2019-01-Sun[2]T01" --- Code format(year_month_weekday(2019, 1, 1, 2, 1, 2, 3, 50, subsecond_precision = "microsecond")) Output [1] "2019-01-Sun[2]T01:02:03.000050" # can't compute start with a year_month_weekday at day precision or greater Code calendar_start(year_month_weekday(2019, 2, 2, 2), "day") Condition Error in `calendar_start()`: ! Computing the start of a 'year_month_weekday' with a precision equal to or more precise than 'day' is undefined. --- Code calendar_start(year_month_weekday(2019, 2, 2, 2), "month") Condition Error in `calendar_start()`: ! Computing the start of a 'year_month_weekday' with a precision equal to or more precise than 'day' is undefined. # can't compute end with a year_month_weekday at day precision or greater Code calendar_end(year_month_weekday(2019, 2, 2, 2), "day") Condition Error in `calendar_end()`: ! Computing the end of a 'year_month_weekday' with a precision equal to or more precise than 'day' is undefined. --- Code calendar_end(year_month_weekday(2019, 2, 2, 2), "month") Condition Error in `calendar_end()`: ! Computing the end of a 'year_month_weekday' with a precision equal to or more precise than 'day' is undefined. # can't compare a 'year_month_weekday' with day precision! Code (expect_error(calendar_count_between(x, x, "month"))) Output Error in `vec_proxy_compare()`: ! 'year_month_weekday' types with a precision of >= 'day' cannot be trivially compared or ordered. Convert to 'year_month_day' to compare using day-of-month values. # only granular precisions are allowed Code seq(year_month_weekday(2019, 1, 1, 1), by = 1, length.out = 2) Condition Error in `seq.clock_year_month_day()`: ! `from` must be 'year' or 'month' precision. # strict mode can be activated Code invalid_resolve(year_month_weekday(2019, 1, 1, 1)) Condition Error in `strict_validate_invalid()`: ! The global option, `clock.strict`, is currently set to `TRUE`. In this mode, `invalid` must be set and cannot be left as `NULL`. # throws known classed error Code invalid_resolve(year_month_weekday(2019, 1, 1, 5)) Condition Error in `invalid_resolve()`: ! Invalid date found at location 1. i Resolve invalid date issues by specifying the `invalid` argument. # minimums are right Code clock_minimum(clock_empty_year_month_weekday_year) Output [1]> [1] "-32767" Code clock_minimum(clock_empty_year_month_weekday_month) Output [1]> [1] "-32767-01" --- Code clock_minimum(year_month_weekday(1, 1, 1, 1)) Condition Error in `clock_minimum()`: ! Can't compute the minimum of this , as it is undefined. i The most precise allowed precision is "month". i `x` has precision "day". # maximums are right Code clock_maximum(clock_empty_year_month_weekday_year) Output [1]> [1] "32767" Code clock_maximum(clock_empty_year_month_weekday_month) Output [1]> [1] "32767-12" --- Code clock_maximum(year_month_weekday(1, 1, 1, 1)) Condition Error in `clock_maximum()`: ! Can't compute the maximum of this , as it is undefined. i The most precise allowed precision is "month". i `x` has precision "day". # min() / max() / range() aren't defined at or past day precision Code min(x) Condition Error in `vec_proxy_compare()`: ! 'year_month_weekday' types with a precision of >= 'day' cannot be trivially compared or ordered. Convert to 'year_month_day' to compare using day-of-month values. --- Code max(x) Condition Error in `vec_proxy_compare()`: ! 'year_month_weekday' types with a precision of >= 'day' cannot be trivially compared or ordered. Convert to 'year_month_day' to compare using day-of-month values. --- Code range(x) Condition Error in `vec_proxy_compare()`: ! 'year_month_weekday' types with a precision of >= 'day' cannot be trivially compared or ordered. Convert to 'year_month_day' to compare using day-of-month values. --- Code min(x) Condition Error in `clock_maximum()`: ! Can't compute the maximum of this , as it is undefined. i The most precise allowed precision is "month". i `x` has precision "day". --- Code max(x) Condition Error in `clock_minimum()`: ! Can't compute the minimum of this , as it is undefined. i The most precise allowed precision is "month". i `x` has precision "day". --- Code range(x) Condition Error in `clock_maximum()`: ! Can't compute the maximum of this , as it is undefined. i The most precise allowed precision is "month". i `x` has precision "day". # add_*() respect recycling rules Code add_years(year_month_weekday(1:2), 1:3) Condition Error in `add_years()`: ! Can't recycle `x` (size 2) to match `n` (size 3). clock/tests/testthat/_snaps/utils.md0000644000176200001440000000077014427431103017317 0ustar liggesusers# POSIXt time zones are standardized as expected Code posixt_tzone_standardize(character()) Condition Warning in `posixt_tzone_standardize()`: POSIXt input had a corrupt time zone attribute of `character(0)`. Defaulting to the current zone by assuming the zone is `""`. Output [1] "" --- Code posixt_tzone_standardize(1) Condition Error in `posixt_tzone_standardize()`: ! A POSIXt time zone should either be a character vector or `NULL`. clock/tests/testthat/_snaps/week-year-week-day.md0000644000176200001440000003602614427431116021563 0ustar liggesusers# validates value ranges Code year_week_day(50000) Condition Error in `year_week_day()`: ! `year` must be between [-32767, 32767]. i Invalid results at locations: 1. --- Code year_week_day(2020, 54) Condition Error in `year_week_day()`: ! `week` must be between [1, 53]. i Invalid results at locations: 1. --- Code year_week_day(2020, 1, 8) Condition Error in `year_week_day()`: ! `day` must be between [1, 7]. i Invalid results at locations: 1. --- Code year_week_day(2020, 1, 1, 24) Condition Error in `year_week_day()`: ! `hour` must be between [0, 23]. i Invalid results at locations: 1. --- Code year_week_day(2020, 1, 1, 1, 60) Condition Error in `year_week_day()`: ! `minute` must be between [0, 59]. i Invalid results at locations: 1. --- Code year_week_day(2020, 1, 1, 1, 1, 60) Condition Error in `year_week_day()`: ! `second` must be between [0, 59]. i Invalid results at locations: 1. --- Code year_week_day(2020, 1, 1, 1, 1, 1, 1000, subsecond_precision = "millisecond") Condition Error in `year_week_day()`: ! `subsecond` must be between [0, 999]. i Invalid results at locations: 1. --- Code year_week_day(2020, 1, 1, 1, 1, 1, 1e+06, subsecond_precision = "microsecond") Condition Error in `year_week_day()`: ! `subsecond` must be between [0, 999999]. i Invalid results at locations: 1. --- Code year_week_day(2020, 1, 1, 1, 1, 1, 1e+09, subsecond_precision = "nanosecond") Condition Error in `year_week_day()`: ! `subsecond` must be between [0, 999999999]. i Invalid results at locations: 1. # full ptype is correct [1] "year_week_day" --- [1] "year_week_day" --- [1] "year_week_day" --- [1] "year_week_day" --- [1] "year_week_day" # abbreviated ptype is correct [1] "ywd" --- [1] "ywd" --- [1] "ywd" --- [1] "ywd" --- [1] "ywd" # setters recycling works both ways Code x <- year_week_day(1:2) y <- 1:3 set_week(x, y) Condition Error in `set_field_year_week_day()`: ! Can't recycle `x` (size 2) to match `value` (size 3). # setters require integer `value` Code set_year(x, 1.5) Condition Error in `set_field_year_week_day()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 --- Code set_week(x, 1.5) Condition Error in `set_field_year_week_day()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 --- Code set_day(x, 1.5) Condition Error in `set_field_year_week_day()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 --- Code set_hour(x, 1.5) Condition Error in `set_field_year_week_day()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 --- Code set_minute(x, 1.5) Condition Error in `set_field_year_week_day()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 --- Code set_second(x, 1.5) Condition Error in `set_field_year_week_day()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 --- Code set_millisecond(x, 1.5) Condition Error in `set_field_year_week_day()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 --- Code set_microsecond(x, 1.5) Condition Error in `set_field_year_week_day()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 --- Code set_nanosecond(x, 1.5) Condition Error in `set_field_year_week_day()`: ! Can't convert from `value` to due to loss of precision. * Locations: 1 # setters check `value` range Code set_year(x, 1e+05) Condition Error in `set_field_year_week_day()`: ! `value` must be between [-32767, 32767]. i Invalid results at locations: 1. --- Code set_week(x, 54) Condition Error in `set_field_year_week_day()`: ! `value` must be between [1, 53]. i Invalid results at locations: 1. --- Code set_day(x, 8) Condition Error in `set_field_year_week_day()`: ! `value` must be between [1, 7]. i Invalid results at locations: 1. --- Code set_hour(x, 24) Condition Error in `set_field_year_week_day()`: ! `value` must be between [0, 23]. i Invalid results at locations: 1. --- Code set_minute(x, 60) Condition Error in `set_field_year_week_day()`: ! `value` must be between [0, 59]. i Invalid results at locations: 1. --- Code set_second(x, 60) Condition Error in `set_field_year_week_day()`: ! `value` must be between [0, 59]. i Invalid results at locations: 1. --- Code set_millisecond(x, -1) Condition Error in `set_field_year_week_day()`: ! `value` must be between [0, 999]. i Invalid results at locations: 1. --- Code set_microsecond(x, -1) Condition Error in `set_field_year_week_day()`: ! `value` must be between [0, 999999]. i Invalid results at locations: 1. --- Code set_nanosecond(x, -1) Condition Error in `set_field_year_week_day()`: ! `value` must be between [0, 999999999]. i Invalid results at locations: 1. # setters require minimum precision Code set_day(year_week_day(year = 1), 1) Condition Error in `set_day()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be at least "week". i `x` has a precision of "year". --- Code set_hour(year_week_day(year = 1, week = 2), 1) Condition Error in `set_hour()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be at least "day". i `x` has a precision of "week". --- Code set_minute(year_week_day(year = 1, week = 2, day = 3), 1) Condition Error in `set_minute()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be at least "hour". i `x` has a precision of "day". --- Code set_second(year_week_day(year = 1, week = 2, day = 3, hour = 4), 1) Condition Error in `set_second()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be at least "minute". i `x` has a precision of "hour". --- Code set_millisecond(year_week_day(year = 1, week = 2, day = 3, hour = 4, minute = 5), 1) Condition Error in `set_millisecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "millisecond". i `x` has a precision of "minute". --- Code set_microsecond(year_week_day(year = 1, week = 2, day = 3, hour = 4, minute = 5), 1) Condition Error in `set_microsecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "microsecond". i `x` has a precision of "minute". --- Code set_nanosecond(year_week_day(year = 1, week = 2, day = 3, hour = 4, minute = 5), 1) Condition Error in `set_nanosecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "nanosecond". i `x` has a precision of "minute". # setters require correct subsecond precision Code set_millisecond(year_week_day(year = 1, week = 2, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "microsecond"), 1) Condition Error in `set_millisecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "millisecond". i `x` has a precision of "microsecond". --- Code set_millisecond(year_week_day(year = 1, week = 2, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "nanosecond"), 1) Condition Error in `set_millisecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "millisecond". i `x` has a precision of "nanosecond". --- Code set_microsecond(year_week_day(year = 1, week = 2, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "millisecond"), 1) Condition Error in `set_microsecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "microsecond". i `x` has a precision of "millisecond". --- Code set_microsecond(year_week_day(year = 1, week = 2, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "nanosecond"), 1) Condition Error in `set_microsecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "microsecond". i `x` has a precision of "nanosecond". --- Code set_nanosecond(year_week_day(year = 1, week = 2, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "millisecond"), 1) Condition Error in `set_nanosecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "nanosecond". i `x` has a precision of "millisecond". --- Code set_nanosecond(year_week_day(year = 1, week = 2, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "microsecond"), 1) Condition Error in `set_nanosecond()`: ! Can't perform this operation because of the precision of `x`. i The precision of `x` must be "second" or "nanosecond". i `x` has a precision of "microsecond". # default formats are correct Code format(year_week_day(2019)) Output [1] "2019" --- Code format(year_week_day(2019, 1)) Output [1] "2019-W01" --- Code format(year_week_day(2019, 1, 1, 1)) Output [1] "2019-W01-1T01" --- Code format(year_week_day(2019, 1, 1, 1, 2, 3, 50, subsecond_precision = "microsecond")) Output [1] "2019-W01-1T01:02:03.000050" # can't compute a unsupported count precision Code (expect_error(calendar_count_between(x, x, "week"))) Output Error in `calendar_count_between_standardize_precision_n()`: ! `precision` must be one of: 'year'. # only year precision is allowed Code seq(year_week_day(2019, 1), by = 1, length.out = 2) Condition Error in `seq()`: ! `from` must be 'year' precision. # strict mode can be activated Code invalid_resolve(year_week_day(2019, 1)) Condition Error in `strict_validate_invalid()`: ! The global option, `clock.strict`, is currently set to `TRUE`. In this mode, `invalid` must be set and cannot be left as `NULL`. # throws known classed error Code invalid_resolve(year_week_day(2019, 53)) Condition Error in `invalid_resolve()`: ! Invalid date found at location 1. i Resolve invalid date issues by specifying the `invalid` argument. # minimums are right Code clock_minimum(year_week_day(1)) Output [1]> [1] "-32767" Code clock_minimum(year_week_day(1, 1)) Output [1]> [1] "-32767-W01" Code clock_minimum(year_week_day(1, 1, 1)) Output [1]> [1] "-32767-W01-1" Code clock_minimum(year_week_day(1, 1, 1, 1)) Output [1]> [1] "-32767-W01-1T00" Code clock_minimum(year_week_day(1, 1, 1, 1, 1)) Output [1]> [1] "-32767-W01-1T00:00" Code clock_minimum(year_week_day(1, 1, 1, 1, 1, 1)) Output [1]> [1] "-32767-W01-1T00:00:00" Code clock_minimum(year_week_day(1, 1, 1, 1, 1, 1, 1, subsecond_precision = "millisecond")) Output [1]> [1] "-32767-W01-1T00:00:00.000" Code clock_minimum(year_week_day(1, 1, 1, 1, 1, 1, 1, subsecond_precision = "microsecond")) Output [1]> [1] "-32767-W01-1T00:00:00.000000" Code clock_minimum(year_week_day(1, 1, 1, 1, 1, 1, 1, subsecond_precision = "nanosecond")) Output [1]> [1] "-32767-W01-1T00:00:00.000000000" # maximums are right Code clock_maximum(year_week_day(1)) Output [1]> [1] "32767" Code clock_maximum(year_week_day(1, 1)) Output [1]> [1] "32767-W52" Code clock_maximum(year_week_day(1, 1, 1)) Output [1]> [1] "32767-W52-7" Code clock_maximum(year_week_day(1, 1, 1, 1)) Output [1]> [1] "32767-W52-7T23" Code clock_maximum(year_week_day(1, 1, 1, 1, 1)) Output [1]> [1] "32767-W52-7T23:59" Code clock_maximum(year_week_day(1, 1, 1, 1, 1, 1)) Output [1]> [1] "32767-W52-7T23:59:59" Code clock_maximum(year_week_day(1, 1, 1, 1, 1, 1, 1, subsecond_precision = "millisecond")) Output [1]> [1] "32767-W52-7T23:59:59.999" Code clock_maximum(year_week_day(1, 1, 1, 1, 1, 1, 1, subsecond_precision = "microsecond")) Output [1]> [1] "32767-W52-7T23:59:59.999999" Code clock_maximum(year_week_day(1, 1, 1, 1, 1, 1, 1, subsecond_precision = "nanosecond")) Output [1]> [1] "32767-W52-7T23:59:59.999999999" # minimums and maximums respect `start` Code clock_minimum(year_week_day(1, start = clock_weekdays$friday)) Output [1]> [1] "-32767" Code clock_maximum(year_week_day(1, start = clock_weekdays$friday)) Output [1]> [1] "32767" # add_*() respect recycling rules Code add_years(year_week_day(1:2), 1:3) Condition Error in `add_years()`: ! Can't recycle `x` (size 2) to match `n` (size 3). clock/tests/testthat/_snaps/weekday.md0000644000176200001440000000766614427431117017630 0ustar liggesusers# format - can format() a weekday Code format(weekday(1:7)) Output [1] "Sun" "Mon" "Tue" "Wed" "Thu" "Fri" "Sat" # format - can use full names Code format(weekday(1:7), abbreviate = FALSE) Output [1] "Sunday" "Monday" "Tuesday" "Wednesday" "Thursday" "Friday" [7] "Saturday" # format - can use a different locale Code format(weekday(1:7), labels = "fr", abbreviate = FALSE) Output [1] "dimanche" "lundi" "mardi" "mercredi" "jeudi" "vendredi" "samedi" # format - `labels` is validated Code format(weekday(1), labels = 1) Condition Error in `format()`: ! `labels` must be a , not the number 1. # format - `abbreviate` is validated Code format(weekday(1), abbreviate = "foo") Condition Error in `format()`: ! `abbreviate` must be `TRUE` or `FALSE`, not the string "foo". --- Code format(weekday(1), abbreviate = 1) Condition Error in `format()`: ! `abbreviate` must be `TRUE` or `FALSE`, not the number 1. --- Code format(weekday(1), abbreviate = c(TRUE, FALSE)) Condition Error in `format()`: ! `abbreviate` must be `TRUE` or `FALSE`, not a logical vector. # validates `x` Code weekday_code(1) Condition Error in `weekday_code()`: ! `x` must be a , not the number 1. # weekday_code - `encoding` is validated Code weekday_code(weekday(1), encoding = "foo") Condition Error in `weekday_code()`: ! `encoding` must be one of "western" or "iso", not "foo". --- Code weekday_code(weekday(1), encoding = 1) Condition Error in `weekday_code()`: ! `encoding` must be a string or character vector. # `x` is validated Code weekday_factor(1) Condition Error in `weekday_factor()`: ! `x` must be a , not the number 1. # `labels` is validated Code weekday_factor(weekday(1), labels = 1) Condition Error in `weekday_factor()`: ! `labels` must be a , not the number 1. # `encoding` is validated Code weekday_factor(weekday(1), encoding = "foo") Condition Error in `weekday_factor()`: ! `encoding` must be one of "western" or "iso", not "foo". --- Code weekday_factor(weekday(1), encoding = 1) Condition Error in `weekday_factor()`: ! `encoding` must be a string or character vector. # `abbreviate` is validated Code weekday_factor(weekday(1), abbreviate = "foo") Condition Error in `weekday_factor()`: ! `abbreviate` must be `TRUE` or `FALSE`, not the string "foo". --- Code weekday_factor(weekday(1), abbreviate = 1) Condition Error in `weekday_factor()`: ! `abbreviate` must be `TRUE` or `FALSE`, not the number 1. --- Code weekday_factor(weekday(1), abbreviate = c(TRUE, FALSE)) Condition Error in `weekday_factor()`: ! `abbreviate` must be `TRUE` or `FALSE`, not a logical vector. # can't compare or order weekdays (#153) Code weekday(1) < weekday(2) Condition Error in `vec_proxy_compare()`: ! Can't compare or order values of the type, as this type does not specify a "first" day of the week. --- Code min(weekday(1)) Condition Error in `vec_proxy_compare()`: ! Can't compare or order values of the type, as this type does not specify a "first" day of the week. --- Code xtfrm(weekday(1:2)) Condition Error in `vec_proxy_compare()`: ! Can't compare or order values of the type, as this type does not specify a "first" day of the week. --- Code vec_order(weekday(1:2)) Condition Error in `vec_proxy_compare()`: ! Can't compare or order values of the type, as this type does not specify a "first" day of the week. clock/tests/testthat/test-iso-year-week-day.R0000644000176200001440000005465314427233200020715 0ustar liggesusers# ------------------------------------------------------------------------------ # iso_year_week_day() test_that("helper can create different precisions", { x <- iso_year_week_day(2019, 1:2) expect_identical(get_year(x), c(2019L, 2019L)) expect_identical(get_week(x), 1:2) x <- iso_year_week_day(2019, 1:2, 3) expect_identical(get_day(x), c(3L, 3L)) }) test_that("can create subsecond precision calendars", { x <- iso_year_week_day(2019, 1, 1, 0, 0, 0, 1, subsecond_precision = "millisecond") expect_identical(get_millisecond(x), 1L) x <- iso_year_week_day(2019, 1, 1, 0, 0, 0, 1, subsecond_precision = "microsecond") expect_identical(get_microsecond(x), 1L) x <- iso_year_week_day(2019, 1, 1, 0, 0, 0, 1, subsecond_precision = "nanosecond") expect_identical(get_nanosecond(x), 1L) }) test_that("validates value ranges", { expect_snapshot(error = TRUE, iso_year_week_day(50000)) expect_snapshot(error = TRUE, iso_year_week_day(2020, 54)) expect_snapshot(error = TRUE, iso_year_week_day(2020, 1, 8)) expect_snapshot(error = TRUE, iso_year_week_day(2020, 1, 1, 24)) expect_snapshot(error = TRUE, iso_year_week_day(2020, 1, 1, 1, 60)) expect_snapshot(error = TRUE, iso_year_week_day(2020, 1, 1, 1, 1, 60)) expect_snapshot(error = TRUE, iso_year_week_day(2020, 1, 1, 1, 1, 1, 1000, subsecond_precision = "millisecond")) expect_snapshot(error = TRUE, iso_year_week_day(2020, 1, 1, 1, 1, 1, 1000000, subsecond_precision = "microsecond")) expect_snapshot(error = TRUE, iso_year_week_day(2020, 1, 1, 1, 1, 1, 1000000000, subsecond_precision = "nanosecond")) }) test_that("can get the last week of the iso year", { x <- iso_year_week_day(2019:2020, "last") expect_identical(get_week(x), c(52L, 53L)) }) test_that("`NA` propagates through 'last'", { x <- iso_year_week_day(c(2019, NA)) x <- set_week(x, "last") expect_identical(get_week(x), c(52L, NA)) }) test_that("ignores values past first `NULL`", { expect_identical(iso_year_week_day(2019, day = 2), iso_year_week_day(2019)) }) test_that("NA values propagate", { x <- iso_year_week_day(2019, 1:3, c(NA, 2, 3), c(3, 4, NA)) expect_identical(is.na(x), c(TRUE, FALSE, TRUE)) }) # ------------------------------------------------------------------------------ # vec_ptype() test_that("ptype is correct", { base <- iso_year_week_day(1) ptype <- iso_year_week_day(integer()) for (precision in precision_names()) { if (precision == "quarter" || precision == "month") { next } x <- calendar_widen(base, precision) expect <- calendar_widen(ptype, precision) expect_identical(vec_ptype(x), expect) } }) # ------------------------------------------------------------------------------ # vec_proxy() / vec_restore() test_that("proxy is a data frame", { expect_identical(vec_proxy(iso_year_week_day(2019)), data_frame(year = 2019L)) expect_identical(vec_proxy(iso_year_week_day(2019, 1)), data_frame(year = 2019L, week = 1L)) }) test_that("proxy has names on `year`", { x <- set_names(iso_year_week_day(2019, 1), "nm") year <- vec_proxy(x)$year expect_named(year, "nm") }) test_that("restore works", { to <- iso_year_week_day(2019, 1:5) proxy <- vec_slice(vec_proxy(to), 1:2) expect_identical(vec_restore(proxy, to), iso_year_week_day(2019, 1:2)) }) # ------------------------------------------------------------------------------ # vec_ptype_full() test_that("full ptype is correct", { expect_snapshot_output(vec_ptype_full(iso_year_week_day(2019))) expect_snapshot_output(vec_ptype_full(iso_year_week_day(2019, 1, 1))) expect_snapshot_output(vec_ptype_full(iso_year_week_day(2019, 1, 1, 1, 1, 1, 1, subsecond_precision = "nanosecond"))) expect_snapshot_output(vec_ptype_full(iso_year_week_day(2019, 53))) }) # ------------------------------------------------------------------------------ # vec_ptype_abbr() test_that("abbreviated ptype is correct", { expect_snapshot_output(vec_ptype_abbr(iso_year_week_day(2019))) expect_snapshot_output(vec_ptype_abbr(iso_year_week_day(2019, 1, 1))) expect_snapshot_output(vec_ptype_abbr(iso_year_week_day(2019, 1, 1, 1, 1, 1, 1, subsecond_precision = "nanosecond"))) expect_snapshot_output(vec_ptype_abbr(iso_year_week_day(2019, 53))) }) # ------------------------------------------------------------------------------ # set_*() test_that("setters work", { x <- iso_year_week_day(1L) x <- set_year(x, 2L) expect_identical(get_year(x), 2L) x <- set_week(x, 1L) expect_identical(get_week(x), 1L) x <- set_day(x, 2L) expect_identical(get_day(x), 2L) x <- set_hour(x, 3L) expect_identical(get_hour(x), 3L) x <- set_minute(x, 4L) expect_identical(get_minute(x), 4L) x <- set_second(x, 5L) expect_identical(get_second(x), 5L) ms <- set_millisecond(x, 6L) expect_identical(get_millisecond(ms), 6L) us <- set_microsecond(x, 7L) expect_identical(get_microsecond(us), 7L) ns <- set_nanosecond(x, 8L) expect_identical(get_nanosecond(ns), 8L) }) test_that("setters propagate all missings", { x <- iso_year_week_day(2019, c(1, NA, 3)) x <- set_day(x, c(NA, 2, 4)) expect_identical(vec_detect_missing(x), c(TRUE, TRUE, FALSE)) }) test_that("setters recycling works both ways", { x <- iso_year_week_day(2019) x <- set_week(x, 1:2) expect_identical(x, iso_year_week_day(2019, 1:2)) x <- set_day(x, 1) expect_identical(x, iso_year_week_day(2019, 1:2, 1)) expect_snapshot(error = TRUE, { x <- iso_year_week_day(1:2) y <- 1:3 set_week(x, y) }) }) test_that("setters require integer `value`", { x <- iso_year_week_day(2019, 1, 2, 3, 4, 5) expect_snapshot(error = TRUE, { set_year(x, 1.5) }) expect_snapshot(error = TRUE, { set_week(x, 1.5) }) expect_snapshot(error = TRUE, { set_day(x, 1.5) }) expect_snapshot(error = TRUE, { set_hour(x, 1.5) }) expect_snapshot(error = TRUE, { set_minute(x, 1.5) }) expect_snapshot(error = TRUE, { set_second(x, 1.5) }) expect_snapshot(error = TRUE, { set_millisecond(x, 1.5) }) expect_snapshot(error = TRUE, { set_microsecond(x, 1.5) }) expect_snapshot(error = TRUE, { set_nanosecond(x, 1.5) }) }) test_that("setters check `value` range", { x <- iso_year_week_day(2019, 1, 2, 3, 4, 5) expect_snapshot(error = TRUE, { set_year(x, 100000) }) expect_snapshot(error = TRUE, { set_week(x, 54) }) expect_snapshot(error = TRUE, { set_day(x, 8) }) expect_snapshot(error = TRUE, { set_hour(x, 24) }) expect_snapshot(error = TRUE, { set_minute(x, 60) }) expect_snapshot(error = TRUE, { set_second(x, 60) }) expect_snapshot(error = TRUE, { set_millisecond(x, -1) }) expect_snapshot(error = TRUE, { set_microsecond(x, -1) }) expect_snapshot(error = TRUE, { set_nanosecond(x, -1) }) }) test_that("setters require minimum precision", { expect_snapshot(error = TRUE, { set_day(iso_year_week_day(year = 1), 1) }) expect_snapshot(error = TRUE, { set_hour(iso_year_week_day(year = 1, week = 2), 1) }) expect_snapshot(error = TRUE, { set_minute(iso_year_week_day(year = 1, week = 2, day = 3), 1) }) expect_snapshot(error = TRUE, { set_second(iso_year_week_day(year = 1, week = 2, day = 3, hour = 4), 1) }) expect_snapshot(error = TRUE, { set_millisecond(iso_year_week_day(year = 1, week = 2, day = 3, hour = 4, minute = 5), 1) }) expect_snapshot(error = TRUE, { set_microsecond(iso_year_week_day(year = 1, week = 2, day = 3, hour = 4, minute = 5), 1) }) expect_snapshot(error = TRUE, { set_nanosecond(iso_year_week_day(year = 1, week = 2, day = 3, hour = 4, minute = 5), 1) }) }) test_that("setters require correct subsecond precision", { expect_snapshot(error = TRUE, { set_millisecond(iso_year_week_day(year = 1, week = 2, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "microsecond"), 1) }) expect_snapshot(error = TRUE, { set_millisecond(iso_year_week_day(year = 1, week = 2, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "nanosecond"), 1) }) expect_snapshot(error = TRUE, { set_microsecond(iso_year_week_day(year = 1, week = 2, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "millisecond"), 1) }) expect_snapshot(error = TRUE, { set_microsecond(iso_year_week_day(year = 1, week = 2, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "nanosecond"), 1) }) expect_snapshot(error = TRUE, { set_nanosecond(iso_year_week_day(year = 1, week = 2, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "millisecond"), 1) }) expect_snapshot(error = TRUE, { set_nanosecond(iso_year_week_day(year = 1, week = 2, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "microsecond"), 1) }) }) test_that("setters retain names", { x <- iso_year_week_day(2019) x <- set_names(x, "foo") expect_named(set_week(x, 2), "foo") }) test_that("setting with named `value` strips its names", { x <- iso_year_week_day(2019) x <- set_week(x, set_names(1L, "x")) expect_named(field(x, "week"), NULL) }) # ------------------------------------------------------------------------------ # format() test_that("default formats are correct", { expect_snapshot(format(iso_year_week_day(2019))) expect_snapshot(format(iso_year_week_day(2019, 1))) expect_snapshot(format(iso_year_week_day(2019, 1, 1, 1))) expect_snapshot(format(iso_year_week_day(2019, 1, 1, 1, 2, 3, 50, subsecond_precision = "microsecond"))) }) # ------------------------------------------------------------------------------ # as.character() test_that("as.character() works", { x <- iso_year_week_day(2019, 1) y <- iso_year_week_day(2019, 1, 2) expect_identical(as.character(x), format(x)) expect_identical(as.character(y), format(y)) }) # ------------------------------------------------------------------------------ # calendar_narrow() test_that("can narrow to week", { x_expect <- iso_year_week_day(2019, 2) x <- set_day(x_expect, 1) expect_identical(calendar_narrow(x, "week"), x_expect) expect_identical(calendar_narrow(x_expect, "week"), x_expect) }) test_that("can narrow to day", { x_expect <- iso_year_week_day(2019, 2, 3) x <- set_hour(x_expect, 5) expect_identical(calendar_narrow(x, "day"), x_expect) expect_identical(calendar_narrow(x_expect, "day"), x_expect) }) # ------------------------------------------------------------------------------ # calendar_widen() test_that("can widen to week", { x <- iso_year_week_day(2019) expect_identical(calendar_widen(x, "week"), set_week(x, 1)) }) test_that("can widen to day", { x <- iso_year_week_day(2019) y <- iso_year_week_day(2019, 02) expect_identical(calendar_widen(x, "day"), set_day(set_week(x, 1), 1)) expect_identical(calendar_widen(y, "day"), set_day(y, 1)) }) # ------------------------------------------------------------------------------ # calendar_start() test_that("can compute year start", { x <- iso_year_week_day(2019) expect_identical(calendar_start(x, "year"), x) x <- iso_year_week_day(2019, 2, 2, 2, 2, 2, 2, subsecond_precision = "millisecond") expect <- iso_year_week_day(2019, 1, 1, 0, 0, 0, 0, subsecond_precision = "millisecond") expect_identical(calendar_start(x, "year"), expect) }) test_that("can compute week start", { x <- iso_year_week_day(2019, 2) expect_identical(calendar_start(x, "week"), x) x <- iso_year_week_day(2019, 2, 2, 2, 2, 2, 2, subsecond_precision = "millisecond") expect <- iso_year_week_day(2019, 2, 1, 0, 0, 0, 0, subsecond_precision = "millisecond") expect_identical(calendar_start(x, "week"), expect) }) # ------------------------------------------------------------------------------ # calendar_end() test_that("can compute year end", { x <- iso_year_week_day(2019) expect_identical(calendar_end(x, "year"), x) x <- iso_year_week_day(2019:2020, 2, 2, 2, 2, 2, 2, subsecond_precision = "millisecond") expect <- iso_year_week_day(2019:2020, 52:53, 7, 23, 59, 59, 999L, subsecond_precision = "millisecond") expect_identical(calendar_end(x, "year"), expect) }) test_that("can compute week end", { x <- iso_year_week_day(2019, 2) expect_identical(calendar_end(x, "week"), x) x <- iso_year_week_day(2019, 2, 2, 2, 2, 2, 2, subsecond_precision = "millisecond") expect <- iso_year_week_day(2019, 2, 7, 23, 59, 59, 999L, subsecond_precision = "millisecond") expect_identical(calendar_end(x, "week"), expect) }) # ------------------------------------------------------------------------------ # calendar_leap_year() test_that("can detect leap years", { # Exactly 71 leap weeks for any 400 year cycle start <- 1900L while (start < 2000L) { # `- 1L` to have exactly 400 years considered in the range since both # `start` and `end` are included by `seq()` end <- start + 400L - 1L x <- iso_year_week_day(seq(start, end)) expect_identical(sum(calendar_leap_year(x)), 71L) start <- start + 1L } }) test_that("`NA` propagates", { expect_identical(calendar_leap_year(iso_year_week_day(NA)), NA) }) # ------------------------------------------------------------------------------ # calendar_count_between() test_that("can compute year and month counts", { x <- iso_year_week_day(2019, 1, 1) y <- iso_year_week_day(2020, 3, 4) expect_identical(calendar_count_between(x, y, "year"), 1L) }) test_that("can't compute a unsupported count precision", { x <- iso_year_week_day(2019, 1, 1) expect_snapshot((expect_error(calendar_count_between(x, x, "week")))) }) test_that("positive / negative counts are correct", { start <- iso_year_week_day(1972, 03, 04) end <- iso_year_week_day(1973, 03, 03) expect_identical(calendar_count_between(start, end, "year"), 0L) end <- iso_year_week_day(1973, 03, 04) expect_identical(calendar_count_between(start, end, "year"), 1L) end <- iso_year_week_day(1973, 03, 05) expect_identical(calendar_count_between(start, end, "year"), 1L) end <- iso_year_week_day(1971, 03, 03) expect_identical(calendar_count_between(start, end, "year"), -1L) end <- iso_year_week_day(1971, 03, 04) expect_identical(calendar_count_between(start, end, "year"), -1L) end <- iso_year_week_day(1971, 03, 05) expect_identical(calendar_count_between(start, end, "year"), 0L) }) # ------------------------------------------------------------------------------ # seq() test_that("only year precision is allowed", { expect_snapshot(error = TRUE, seq(iso_year_week_day(2019, 1), by = 1, length.out = 2)) }) test_that("seq(to, by) works", { expect_identical(seq(iso_year_week_day(2019), to = iso_year_week_day(2024), by = 2), iso_year_week_day(c(2019, 2021, 2023))) expect_identical(seq(iso_year_week_day(2019), to = iso_year_week_day(2023), by = 2), iso_year_week_day(c(2019, 2021, 2023))) }) test_that("seq(to, length.out) works", { expect_identical(seq(iso_year_week_day(2019), to = iso_year_week_day(2024), length.out = 2), iso_year_week_day(c(2019, 2024))) expect_identical(seq(iso_year_week_day(2019), to = iso_year_week_day(2024), length.out = 6), iso_year_week_day(2019:2024)) expect_identical(seq(iso_year_week_day(2019), to = iso_year_week_day(2024), along.with = 1:2), iso_year_week_day(c(2019, 2024))) }) test_that("seq(by, length.out) works", { expect_identical(seq(iso_year_week_day(2019), by = 2, length.out = 3), iso_year_week_day(c(2019, 2021, 2023))) expect_identical(seq(iso_year_week_day(2019), by = 2, along.with = 1:3), iso_year_week_day(c(2019, 2021, 2023))) }) # ------------------------------------------------------------------------------ # invalid_detect() test_that("`invalid_detect()` works", { # Not possible to be invalid x <- iso_year_week_day(2019:2020) expect_identical(invalid_detect(x), c(FALSE, FALSE)) # Now possible x <- iso_year_week_day(2019, c(1, 52, 53, NA)) expect_identical(invalid_detect(x), c(FALSE, FALSE, TRUE, FALSE)) # Possible after that too x <- iso_year_week_day(2019, c(1, 52, 53, NA), 1) expect_identical(invalid_detect(x), c(FALSE, FALSE, TRUE, FALSE)) }) # ------------------------------------------------------------------------------ # invalid_any() test_that("`invalid_any()` works", { # Not possible to be invalid x <- iso_year_week_day(2019:2020) expect_false(invalid_any(x)) # Now possible x <- iso_year_week_day(2019, c(1, 52, 53, NA)) expect_true(invalid_any(x)) # Possible after that too x <- iso_year_week_day(2019, c(1, 52, 53, NA), 1) expect_true(invalid_any(x)) }) # ------------------------------------------------------------------------------ # invalid_count() test_that("`invalid_count()` works", { # Not possible to be invalid x <- iso_year_week_day(2019:2020) expect_identical(invalid_count(x), 0L) # Now possible x <- iso_year_week_day(2019, c(1, 52, 53, NA)) expect_identical(invalid_count(x), 1L) # Possible after that too x <- iso_year_week_day(2019, c(1, 52, 53, NA), 1) expect_identical(invalid_count(x), 1L) }) # ------------------------------------------------------------------------------ # invalid_resolve() test_that("strict mode can be activated", { local_options(clock.strict = TRUE) expect_snapshot(error = TRUE, invalid_resolve(iso_year_week_day(2019, 1))) }) test_that("can resolve correctly", { x <- iso_year_week_day(2019, 53, 2, 2, 3, 4, 5, subsecond_precision = "millisecond") expect_identical( invalid_resolve(x, invalid = "previous"), iso_year_week_day(2019, 52, 7, 23, 59, 59, 999, subsecond_precision = "millisecond") ) expect_identical( invalid_resolve(x, invalid = "previous-day"), iso_year_week_day(2019, 52, 7, 2, 3, 4, 5, subsecond_precision = "millisecond") ) expect_identical( invalid_resolve(x, invalid = "next"), iso_year_week_day(2020, 01, 1, 0, 0, 0, 0, subsecond_precision = "millisecond") ) expect_identical( invalid_resolve(x, invalid = "next-day"), iso_year_week_day(2020, 01, 1, 2, 3, 4, 5, subsecond_precision = "millisecond") ) expect_identical( invalid_resolve(x, invalid = "overflow"), iso_year_week_day(2020, 01, 02, 0, 0, 0, 0, subsecond_precision = "millisecond") ) expect_identical( invalid_resolve(x, invalid = "overflow-day"), iso_year_week_day(2020, 01, 02, 2, 3, 4, 5, subsecond_precision = "millisecond") ) expect_identical( invalid_resolve(x, invalid = "NA"), iso_year_week_day(NA, NA, NA, NA, NA, NA, NA, subsecond_precision = "millisecond") ) }) test_that("throws known classed error", { expect_snapshot(error = TRUE, invalid_resolve(iso_year_week_day(2019, 53))) expect_error(invalid_resolve(iso_year_week_day(2019, 53)), class = "clock_error_invalid_date") }) test_that("works with always valid precisions", { x <- iso_year_week_day(2019:2020) expect_identical(invalid_resolve(x), x) }) # ------------------------------------------------------------------------------ # vec_math() test_that("is.nan() works", { x <- iso_year_week_day(c(2019, NA)) expect_identical(is.nan(x), c(FALSE, FALSE)) }) test_that("is.finite() works", { x <- iso_year_week_day(c(2019, NA)) expect_identical(is.finite(x), c(TRUE, FALSE)) }) test_that("is.infinite() works", { x <- iso_year_week_day(c(2019, NA)) expect_identical(is.infinite(x), c(FALSE, FALSE)) }) # ------------------------------------------------------------------------------ # clock_minimum() / clock_maximum() test_that("minimums are right", { expect_snapshot({ clock_minimum(clock_empty_iso_year_week_day_year) clock_minimum(clock_empty_iso_year_week_day_week) clock_minimum(clock_empty_iso_year_week_day_day) clock_minimum(clock_empty_iso_year_week_day_hour) clock_minimum(clock_empty_iso_year_week_day_minute) clock_minimum(clock_empty_iso_year_week_day_second) clock_minimum(clock_empty_iso_year_week_day_millisecond) clock_minimum(clock_empty_iso_year_week_day_microsecond) clock_minimum(clock_empty_iso_year_week_day_nanosecond) }) }) test_that("maximums are right", { expect_snapshot({ clock_maximum(clock_empty_iso_year_week_day_year) clock_maximum(clock_empty_iso_year_week_day_week) clock_maximum(clock_empty_iso_year_week_day_day) clock_maximum(clock_empty_iso_year_week_day_hour) clock_maximum(clock_empty_iso_year_week_day_minute) clock_maximum(clock_empty_iso_year_week_day_second) clock_maximum(clock_empty_iso_year_week_day_millisecond) clock_maximum(clock_empty_iso_year_week_day_microsecond) clock_maximum(clock_empty_iso_year_week_day_nanosecond) }) }) # ------------------------------------------------------------------------------ # min() / max() / range() test_that("min() / max() / range() works", { x <- iso_year_week_day(c(1, 3, 2, 1, -1)) expect_identical(min(x), iso_year_week_day(-1)) expect_identical(max(x), iso_year_week_day(3)) expect_identical(range(x), iso_year_week_day(c(-1, 3))) }) test_that("min() / max() / range() works with `NA`", { x <- iso_year_week_day(c(1, NA, 2, 0)) expect_identical(min(x), iso_year_week_day(NA)) expect_identical(max(x), iso_year_week_day(NA)) expect_identical(range(x), iso_year_week_day(c(NA, NA))) expect_identical(min(x, na.rm = TRUE), iso_year_week_day(0)) expect_identical(max(x, na.rm = TRUE), iso_year_week_day(2)) expect_identical(range(x, na.rm = TRUE), iso_year_week_day(c(0, 2))) }) test_that("min() / max() / range() works when empty", { x <- iso_year_week_day(integer()) expect_identical(min(x), clock_maximum(x)) expect_identical(max(x), clock_minimum(x)) expect_identical(range(x), c(clock_maximum(x), clock_minimum(x))) x <- iso_year_week_day(c(NA, NA)) expect_identical(min(x, na.rm = TRUE), clock_maximum(x)) expect_identical(max(x, na.rm = TRUE), clock_minimum(x)) expect_identical(range(x, na.rm = TRUE), c(clock_maximum(x), clock_minimum(x))) }) # ------------------------------------------------------------------------------ # add_*() test_that("add_years() works", { x <- iso_year_week_day(2019, 1, 2, 3:4) expect_identical( add_years(x, 1:2), iso_year_week_day(c(2020, 2021), 1, 2, 3:4) ) expect_identical( add_years(x, NA), vec_init(x, 2L) ) }) test_that("add_*() respect recycling rules", { expect_length(add_years(iso_year_week_day(1), 1:2), 2L) expect_length(add_years(iso_year_week_day(1:2), 1), 2L) expect_length(add_years(iso_year_week_day(1), integer()), 0L) expect_length(add_years(iso_year_week_day(integer()), 1), 0L) expect_snapshot(error = TRUE, { add_years(iso_year_week_day(1:2), 1:3) }) }) test_that("add_*() retains names", { x <- set_names(iso_year_week_day(1), "x") y <- iso_year_week_day(1) n <- set_names(1, "n") expect_named(add_years(x, n), "x") expect_named(add_years(y, n), "n") }) clock/tests/testthat/test-gregorian-year-month-weekday.R0000644000176200001440000006073614427233200023145 0ustar liggesusers# ------------------------------------------------------------------------------ # year_month_weekday() test_that("helper can create different precisions", { x <- year_month_weekday(2019, 1:2) expect_identical(get_year(x), c(2019L, 2019L)) expect_identical(get_month(x), 1:2) x <- year_month_weekday(2019, 1:2, clock_weekdays$monday, 2) expect_identical(get_day(x), c(clock_weekdays$monday, clock_weekdays$monday)) expect_identical(get_index(x), c(2L, 2L)) }) test_that("can create subsecond precision calendars", { x <- year_month_weekday(2019, 1, 1, 1, 0, 0, 0, 1, subsecond_precision = "millisecond") expect_identical(get_millisecond(x), 1L) x <- year_month_weekday(2019, 1, 1, 1, 0, 0, 0, 1, subsecond_precision = "microsecond") expect_identical(get_microsecond(x), 1L) x <- year_month_weekday(2019, 1, 1, 1, 0, 0, 0, 1, subsecond_precision = "nanosecond") expect_identical(get_nanosecond(x), 1L) }) test_that("both day and index must be specified", { expect_snapshot(error = TRUE, year_month_weekday(2020, 1, 1)) }) test_that("validates value ranges", { expect_snapshot(error = TRUE, year_month_weekday(50000)) expect_snapshot(error = TRUE, year_month_weekday(2020, 13)) expect_snapshot(error = TRUE, year_month_weekday(2020, 1, 32, 1)) expect_snapshot(error = TRUE, year_month_weekday(2020, 1, 1, 6)) expect_snapshot(error = TRUE, year_month_weekday(2020, 1, 1, 1, 24)) expect_snapshot(error = TRUE, year_month_weekday(2020, 1, 1, 1, 1, 60)) expect_snapshot(error = TRUE, year_month_weekday(2020, 1, 1, 1, 1, 1, 60)) expect_snapshot(error = TRUE, year_month_weekday(2020, 1, 1, 1, 1, 1, 1, 1000, subsecond_precision = "millisecond")) expect_snapshot(error = TRUE, year_month_weekday(2020, 1, 1, 1, 1, 1, 1, 1000000, subsecond_precision = "microsecond")) expect_snapshot(error = TRUE, year_month_weekday(2020, 1, 1, 1, 1, 1, 1, 1000000000, subsecond_precision = "nanosecond")) }) test_that("can get the last indexed weekday of the month", { x <- year_month_weekday(2019, 1:4, clock_weekdays$monday, "last") expect_identical(get_index(x), c(4L, 4L, 4L, 5L)) }) test_that("`NA` propagates through 'last'", { x <- year_month_weekday(2019, c(1, NA), clock_weekdays$monday, 1) x <- set_index(x, "last") expect_identical(get_index(x), c(4L, NA)) }) test_that("ignores values past first `NULL`", { expect_identical(year_month_weekday(2019, minute = 2), year_month_weekday(2019)) }) test_that("NA values propagate", { x <- year_month_weekday(2019, 1:3, c(NA, 2, 3), c(3, 4, NA)) expect_identical(is.na(x), c(TRUE, FALSE, TRUE)) }) # ------------------------------------------------------------------------------ # vec_ptype() test_that("ptype is correct", { base <- year_month_weekday(1) ptype <- year_month_weekday(integer()) for (precision in precision_names()) { if (precision == "quarter" || precision == "week") { next } x <- calendar_widen(base, precision) expect <- calendar_widen(ptype, precision) expect_identical(vec_ptype(x), expect) } }) # ------------------------------------------------------------------------------ # vec_proxy() / vec_restore() test_that("proxy is a data frame", { expect_identical(vec_proxy(year_month_weekday(2019)), data_frame(year = 2019L)) expect_identical(vec_proxy(year_month_weekday(2019, 1)), data_frame(year = 2019L, month = 1L)) }) test_that("proxy has names on `year`", { x <- set_names(year_month_weekday(2019, 1), "nm") year <- vec_proxy(x)$year expect_named(year, "nm") }) test_that("restore works", { to <- year_month_weekday(2019, 1:5) proxy <- vec_slice(vec_proxy(to), 1:2) expect_identical(vec_restore(proxy, to), year_month_weekday(2019, 1:2)) }) # ------------------------------------------------------------------------------ # vec_proxy_compare() test_that("can compare with year / month precision", { expect_identical( year_month_weekday(2019) > year_month_weekday(2018:2020), c(TRUE, FALSE, FALSE) ) expect_identical( year_month_weekday(2019, 2) > year_month_weekday(2019, 1:3), c(TRUE, FALSE, FALSE) ) }) test_that("cannot compare / sort with day precision or finer", { x <- year_month_weekday(2019, 1, 1, 1) expect_snapshot(error = TRUE, x > x) expect_snapshot(error = TRUE, vec_order(x)) }) # ------------------------------------------------------------------------------ # vec_ptype_full() test_that("full ptype is correct", { expect_snapshot_output(vec_ptype_full(year_month_weekday(2019))) expect_snapshot_output(vec_ptype_full(year_month_weekday(2019, 1, 1, 2))) expect_snapshot_output(vec_ptype_full(year_month_weekday(2019, 1, 1, 2, 1, 1, 1, 1, subsecond_precision = "nanosecond"))) expect_snapshot_output(vec_ptype_full(year_month_weekday(2019, 1, 1, 5))) }) # ------------------------------------------------------------------------------ # vec_ptype_abbr() test_that("abbreviated ptype is correct", { expect_snapshot_output(vec_ptype_abbr(year_month_weekday(2019))) expect_snapshot_output(vec_ptype_abbr(year_month_weekday(2019, 1, 1, 2))) expect_snapshot_output(vec_ptype_abbr(year_month_weekday(2019, 1, 1, 2, 1, 1, 1, 1, subsecond_precision = "nanosecond"))) expect_snapshot_output(vec_ptype_abbr(year_month_weekday(2019, 1, 1, 5))) }) # ------------------------------------------------------------------------------ # set_*() test_that("setters work", { x <- year_month_weekday(1L) x <- set_year(x, 2L) expect_identical(get_year(x), 2L) x <- set_month(x, 1L) expect_identical(get_month(x), 1L) x <- set_day(x, 2L, index = 1L) expect_identical(get_day(x), 2L) expect_identical(get_index(x), 1L) x <- set_index(x, 3L) expect_identical(get_index(x), 3L) x <- set_hour(x, 3L) expect_identical(get_hour(x), 3L) x <- set_minute(x, 4L) expect_identical(get_minute(x), 4L) x <- set_second(x, 5L) expect_identical(get_second(x), 5L) ms <- set_millisecond(x, 6L) expect_identical(get_millisecond(ms), 6L) us <- set_microsecond(x, 7L) expect_identical(get_microsecond(us), 7L) ns <- set_nanosecond(x, 8L) expect_identical(get_nanosecond(ns), 8L) }) test_that("setters propagate all missings", { x <- year_month_weekday(2019, c(1, NA, 3, 4)) x <- set_day(x, c(NA, 2, 4, 5), index = c(1, 2, 3, NA)) expect_identical(vec_detect_missing(x), c(TRUE, TRUE, FALSE, TRUE)) }) test_that("setters recycling works both ways", { x <- year_month_weekday(2019) x <- set_month(x, 1:2) expect_identical(x, year_month_weekday(2019, 1:2)) x <- set_day(x, 1, index = 1) expect_identical(x, year_month_weekday(2019, 1:2, 1, 1)) expect_snapshot(error = TRUE, { x <- year_month_weekday(1:2) y <- 1:3 set_month(x, y) }) }) test_that("setters require integer `value`", { x <- year_month_weekday(2019, 1, 2, 3, 4, 5, 6) expect_snapshot(error = TRUE, { set_year(x, 1.5) }) expect_snapshot(error = TRUE, { set_month(x, 1.5) }) expect_snapshot(error = TRUE, { set_day(x, 1.5) }) expect_snapshot(error = TRUE, { set_index(x, 1.5) }) expect_snapshot(error = TRUE, { set_hour(x, 1.5) }) expect_snapshot(error = TRUE, { set_minute(x, 1.5) }) expect_snapshot(error = TRUE, { set_second(x, 1.5) }) expect_snapshot(error = TRUE, { set_millisecond(x, 1.5) }) expect_snapshot(error = TRUE, { set_microsecond(x, 1.5) }) expect_snapshot(error = TRUE, { set_nanosecond(x, 1.5) }) }) test_that("setters check `value` range", { x <- year_month_weekday(2019, 1, 2, 3, 4, 5, 6) expect_snapshot(error = TRUE, { set_year(x, 100000) }) expect_snapshot(error = TRUE, { set_month(x, 13) }) expect_snapshot(error = TRUE, { set_day(x, 8) }) expect_snapshot(error = TRUE, { set_index(x, 6) }) expect_snapshot(error = TRUE, { set_hour(x, 24) }) expect_snapshot(error = TRUE, { set_minute(x, 60) }) expect_snapshot(error = TRUE, { set_second(x, 60) }) expect_snapshot(error = TRUE, { set_millisecond(x, -1) }) expect_snapshot(error = TRUE, { set_microsecond(x, -1) }) expect_snapshot(error = TRUE, { set_nanosecond(x, -1) }) }) test_that("setters require minimum precision", { expect_snapshot(error = TRUE, { set_day(year_month_weekday(year = 1), 1, index = 1) }) expect_snapshot(error = TRUE, { set_hour(year_month_weekday(year = 1, month = 2), 1) }) expect_snapshot(error = TRUE, { set_minute(year_month_weekday(year = 1, month = 2, day = 3, index = 1), 1) }) expect_snapshot(error = TRUE, { set_second(year_month_weekday(year = 1, month = 2, day = 3, index = 1, hour = 4), 1) }) expect_snapshot(error = TRUE, { set_millisecond(year_month_weekday(year = 1, month = 2, day = 3, index = 1, hour = 4, minute = 5), 1) }) expect_snapshot(error = TRUE, { set_microsecond(year_month_weekday(year = 1, month = 2, day = 3, index = 1, hour = 4, minute = 5), 1) }) expect_snapshot(error = TRUE, { set_nanosecond(year_month_weekday(year = 1, month = 2, day = 3, index = 1, hour = 4, minute = 5), 1) }) }) test_that("setters require correct subsecond precision", { expect_snapshot(error = TRUE, { set_millisecond(year_month_weekday(year = 1, month = 2, day = 3, index = 1, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "microsecond"), 1) }) expect_snapshot(error = TRUE, { set_millisecond(year_month_weekday(year = 1, month = 2, day = 3, index = 1, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "nanosecond"), 1) }) expect_snapshot(error = TRUE, { set_microsecond(year_month_weekday(year = 1, month = 2, day = 3, index = 1, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "millisecond"), 1) }) expect_snapshot(error = TRUE, { set_microsecond(year_month_weekday(year = 1, month = 2, day = 3, index = 1, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "nanosecond"), 1) }) expect_snapshot(error = TRUE, { set_nanosecond(year_month_weekday(year = 1, month = 2, day = 3, index = 1, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "millisecond"), 1) }) expect_snapshot(error = TRUE, { set_nanosecond(year_month_weekday(year = 1, month = 2, day = 3, index = 1, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "microsecond"), 1) }) }) test_that("setters retain names", { x <- year_month_weekday(2019) x <- set_names(x, "foo") expect_named(set_month(x, 2), "foo") }) test_that("setting with named `value` strips its names", { x <- year_month_weekday(2019) x <- set_month(x, set_names(1L, "x")) expect_named(field(x, "month"), NULL) }) # ------------------------------------------------------------------------------ # format() test_that("default formats are correct", { expect_snapshot(format(year_month_weekday(2019))) expect_snapshot(format(year_month_weekday(2019, 1))) expect_snapshot(format(year_month_weekday(2019, 1, 1, 2, 1))) expect_snapshot(format(year_month_weekday(2019, 1, 1, 2, 1, 2, 3, 50, subsecond_precision = "microsecond"))) }) # ------------------------------------------------------------------------------ # as.character() test_that("as.character() works", { x <- year_month_weekday(2019, 1) y <- year_month_weekday(2019, 1, 2, 1) expect_identical(as.character(x), format(x)) expect_identical(as.character(y), format(y)) }) # ------------------------------------------------------------------------------ # calendar_narrow() test_that("can narrow to month", { x_expect <- year_month_weekday(2019, 2) x <- set_day(x_expect, 1, index = 2) expect_identical(calendar_narrow(x, "month"), x_expect) expect_identical(calendar_narrow(x_expect, "month"), x_expect) }) test_that("can narrow to day", { x_expect <- year_month_weekday(2019, 2, 3, 2) x <- set_hour(x_expect, 5) expect_identical(calendar_narrow(x, "day"), x_expect) expect_identical(calendar_narrow(x_expect, "day"), x_expect) }) # ------------------------------------------------------------------------------ # calendar_widen() test_that("can widen to month", { x <- year_month_weekday(2019) expect_identical(calendar_widen(x, "month"), set_month(x, 1)) }) test_that("can widen to day", { x <- year_month_weekday(2019) y <- year_month_weekday(2019, 02) expect_identical(calendar_widen(x, "day"), set_day(set_month(x, 1), 1, index = 1)) expect_identical(calendar_widen(y, "day"), set_day(y, 1, index = 1)) }) # ------------------------------------------------------------------------------ # calendar_start() test_that("can compute year start", { x <- year_month_weekday(2019) expect_identical(calendar_start(x, "year"), x) x <- year_month_weekday(2019, 2) expect_identical(calendar_start(x, "year"), year_month_weekday(2019, 1)) }) test_that("can compute month start", { x <- year_month_weekday(2019, 2) expect_identical(calendar_start(x, "month"), x) }) test_that("can't compute start with a year_month_weekday at day precision or greater", { expect_snapshot(error = TRUE, calendar_start(year_month_weekday(2019, 2, 2, 2), "day")) expect_snapshot(error = TRUE, calendar_start(year_month_weekday(2019, 2, 2, 2), "month")) }) # ------------------------------------------------------------------------------ # calendar_end() test_that("can compute year end", { x <- year_month_weekday(2019) expect_identical(calendar_end(x, "year"), x) x <- year_month_weekday(2019, 2) expect_identical(calendar_end(x, "year"), year_month_weekday(2019, 12)) }) test_that("can compute month end", { x <- year_month_weekday(2019, 2) expect_identical(calendar_end(x, "month"), x) }) test_that("can't compute end with a year_month_weekday at day precision or greater", { expect_snapshot(error = TRUE, calendar_end(year_month_weekday(2019, 2, 2, 2), "day")) expect_snapshot(error = TRUE, calendar_end(year_month_weekday(2019, 2, 2, 2), "month")) }) # ------------------------------------------------------------------------------ # calendar_month_factor() test_that("can get a month factor", { expect_identical( calendar_month_factor(year_month_weekday(2019, 1:12)), factor(month.name, levels = month.name, ordered = TRUE) ) }) # ------------------------------------------------------------------------------ # calendar_count_between() test_that("can compute year and month counts", { x <- year_month_weekday(2019, 1) y <- year_month_weekday(2020, 3) expect_identical(calendar_count_between(x, y, "year"), 1L) expect_identical(calendar_count_between(x, y, "month"), 14L) expect_identical(calendar_count_between(x, y, "month", n = 2), 7L) }) test_that("can compute a quarter count", { x <- year_month_weekday(2019, 1) y <- year_month_weekday(2019, c(3, 5)) expect_identical(calendar_count_between(x, y, "quarter"), c(0L, 1L)) expect_identical( calendar_count_between(x, y, "quarter"), calendar_count_between(x, y, "month", n = 3L) ) y <- year_month_weekday(2020, c(3, 5)) expect_identical(calendar_count_between(x, y, "quarter", n = 2L), c(2L, 2L)) expect_identical( calendar_count_between(x, y, "quarter", n = 2L), calendar_count_between(x, y, "month", n = 6L) ) }) test_that("positive / negative counts are correct", { start <- year_month_weekday(1972, 04) end <- year_month_weekday(1973, 03) expect_identical(calendar_count_between(start, end, "year"), 0L) expect_identical(calendar_count_between(start, end, "month"), 11L) end <- year_month_weekday(1973, 04) expect_identical(calendar_count_between(start, end, "year"), 1L) expect_identical(calendar_count_between(start, end, "month"), 12L) end <- year_month_weekday(1973, 05) expect_identical(calendar_count_between(start, end, "year"), 1L) expect_identical(calendar_count_between(start, end, "month"), 13L) end <- year_month_weekday(1971, 03) expect_identical(calendar_count_between(start, end, "year"), -1L) expect_identical(calendar_count_between(start, end, "month"), -13L) end <- year_month_weekday(1971, 04) expect_identical(calendar_count_between(start, end, "year"), -1L) expect_identical(calendar_count_between(start, end, "month"), -12L) end <- year_month_weekday(1971, 05) expect_identical(calendar_count_between(start, end, "year"), 0L) expect_identical(calendar_count_between(start, end, "month"), -11L) }) test_that("can't compare a 'year_month_weekday' with day precision!", { x <- year_month_weekday(2019, 1, 1, 1) expect_snapshot((expect_error(calendar_count_between(x, x, "month")))) }) # ------------------------------------------------------------------------------ # seq() test_that("only granular precisions are allowed", { expect_snapshot(error = TRUE, seq(year_month_weekday(2019, 1, 1, 1), by = 1, length.out = 2)) }) # NOTE: Most tests are done by `year_month_day()` since they share an implementation test_that("can generate a sequence", { expect_identical(seq(year_month_day(2019, 1), to = year_month_day(2019, 6), by = 2), year_month_day(2019, c(1, 3, 5))) }) # ------------------------------------------------------------------------------ # invalid_detect() test_that("`invalid_detect()` works", { # Not possible to be invalid x <- year_month_weekday(2019:2020, 1:2) expect_identical(invalid_detect(x), c(FALSE, FALSE)) # Now possible x <- year_month_weekday(2019, 1, 1, c(1, 2, 5, NA)) expect_identical(invalid_detect(x), c(FALSE, FALSE, TRUE, FALSE)) # Possible after that too x <- year_month_weekday(2019, 1, 1, c(1, 2, 5, NA), 3) expect_identical(invalid_detect(x), c(FALSE, FALSE, TRUE, FALSE)) }) # ------------------------------------------------------------------------------ # invalid_any() test_that("`invalid_any()` works", { # Not possible to be invalid x <- year_month_weekday(2019:2020, 1:2) expect_false(invalid_any(x)) # Now possible x <- year_month_weekday(2019, 1, 1, c(1, 2, 5, NA)) expect_true(invalid_any(x)) # Possible after that too x <- year_month_weekday(2019, 1, 1, c(1, 2, 5, NA), 3) expect_true(invalid_any(x)) }) # ------------------------------------------------------------------------------ # invalid_count() test_that("`invalid_count()` works", { # Not possible to be invalid x <- year_month_weekday(2019:2020, 1:2) expect_identical(invalid_count(x), 0L) # Now possible x <- year_month_weekday(2019, 1, 1, c(1, 2, 5, NA)) expect_identical(invalid_count(x), 1L) # Possible after that too x <- year_month_weekday(2019, 1, 1, c(1, 2, 5, NA), 3) expect_identical(invalid_count(x), 1L) }) # ------------------------------------------------------------------------------ # invalid_resolve() test_that("strict mode can be activated", { local_options(clock.strict = TRUE) expect_snapshot(error = TRUE, invalid_resolve(year_month_weekday(2019, 1, 1, 1))) }) test_that("can resolve correctly", { x <- year_month_weekday(2019, 1, clock_weekdays$friday, 5, 2, 3, 4, 5, subsecond_precision = "millisecond") expect_identical( invalid_resolve(x, invalid = "previous"), year_month_weekday(2019, 1, clock_weekdays$thursday, 5, 23, 59, 59, 999, subsecond_precision = "millisecond") ) expect_identical( invalid_resolve(x, invalid = "previous-day"), year_month_weekday(2019, 1, clock_weekdays$thursday, 5, 2, 3, 4, 5, subsecond_precision = "millisecond") ) expect_identical( invalid_resolve(x, invalid = "next"), year_month_weekday(2019, 2, clock_weekdays$friday, 1, 0, 0, 0, 0, subsecond_precision = "millisecond") ) expect_identical( invalid_resolve(x, invalid = "next-day"), year_month_weekday(2019, 2, clock_weekdays$friday, 1, 2, 3, 4, 5, subsecond_precision = "millisecond") ) expect_identical( invalid_resolve(x, invalid = "overflow"), year_month_weekday(2019, 2, clock_weekdays$friday, 1, 0, 0, 0, 0, subsecond_precision = "millisecond") ) expect_identical( invalid_resolve(x, invalid = "overflow-day"), year_month_weekday(2019, 2, clock_weekdays$friday, 1, 2, 3, 4, 5, subsecond_precision = "millisecond") ) expect_identical( invalid_resolve(x, invalid = "NA"), year_month_weekday(NA, NA, NA, NA, NA, NA, NA, NA, subsecond_precision = "millisecond") ) }) test_that("throws known classed error", { expect_snapshot(error = TRUE, invalid_resolve(year_month_weekday(2019, 1, 1, 5))) expect_error(invalid_resolve(year_month_weekday(2019, 1, 1, 5)), class = "clock_error_invalid_date") }) test_that("works with always valid precisions", { x <- year_month_weekday(2019:2020, 1:2) expect_identical(invalid_resolve(x), x) }) # ------------------------------------------------------------------------------ # vec_math() test_that("is.nan() works", { x <- year_month_weekday(c(2019, NA)) expect_identical(is.nan(x), c(FALSE, FALSE)) }) test_that("is.finite() works", { x <- year_month_weekday(c(2019, NA)) expect_identical(is.finite(x), c(TRUE, FALSE)) }) test_that("is.infinite() works", { x <- year_month_weekday(c(2019, NA)) expect_identical(is.infinite(x), c(FALSE, FALSE)) }) # ------------------------------------------------------------------------------ # clock_minimum() / clock_maximum() test_that("minimums are right", { expect_snapshot({ clock_minimum(clock_empty_year_month_weekday_year) clock_minimum(clock_empty_year_month_weekday_month) }) # Not defined at or past day precision expect_snapshot(error = TRUE, { clock_minimum(year_month_weekday(1, 1, 1, 1)) }) }) test_that("maximums are right", { expect_snapshot({ clock_maximum(clock_empty_year_month_weekday_year) clock_maximum(clock_empty_year_month_weekday_month) }) # Not defined at or past day precision expect_snapshot(error = TRUE, { clock_maximum(year_month_weekday(1, 1, 1, 1)) }) }) # ------------------------------------------------------------------------------ # min() / max() / range() test_that("min() / max() / range() works", { x <- year_month_weekday(c(1, 3, 2, 1, -1)) expect_identical(min(x), year_month_weekday(-1)) expect_identical(max(x), year_month_weekday(3)) expect_identical(range(x), year_month_weekday(c(-1, 3))) }) test_that("min() / max() / range() works with `NA`", { x <- year_month_weekday(c(1, NA, 2, 0)) expect_identical(min(x), year_month_weekday(NA)) expect_identical(max(x), year_month_weekday(NA)) expect_identical(range(x), year_month_weekday(c(NA, NA))) expect_identical(min(x, na.rm = TRUE), year_month_weekday(0)) expect_identical(max(x, na.rm = TRUE), year_month_weekday(2)) expect_identical(range(x, na.rm = TRUE), year_month_weekday(c(0, 2))) }) test_that("min() / max() / range() works when empty", { x <- year_month_weekday(integer()) expect_identical(min(x), clock_maximum(x)) expect_identical(max(x), clock_minimum(x)) expect_identical(range(x), c(clock_maximum(x), clock_minimum(x))) x <- year_month_weekday(c(NA, NA)) expect_identical(min(x, na.rm = TRUE), clock_maximum(x)) expect_identical(max(x, na.rm = TRUE), clock_minimum(x)) expect_identical(range(x, na.rm = TRUE), c(clock_maximum(x), clock_minimum(x))) }) test_that("min() / max() / range() aren't defined at or past day precision", { x <- year_month_weekday(1, 1, 1, 1) expect_snapshot(error = TRUE, min(x)) expect_snapshot(error = TRUE, max(x)) expect_snapshot(error = TRUE, range(x)) x <- year_month_weekday(integer(), integer(), integer(), integer()) expect_snapshot(error = TRUE, min(x)) expect_snapshot(error = TRUE, max(x)) expect_snapshot(error = TRUE, range(x)) }) # ------------------------------------------------------------------------------ # add_*() test_that("add_years() works", { x <- year_month_weekday(2019, 1, 2, 3:4, 5) expect_identical( add_years(x, 1:2), year_month_weekday(c(2020, 2021), 1, 2, 3:4, 5) ) expect_identical( add_years(x, NA), vec_init(x, 2L) ) }) test_that("add_months() works", { x <- year_month_weekday(2019, 1, 2, 3:4, 5) expect_identical( add_months(x, 1:2), year_month_weekday(2019, 2:3, 2, 3:4, 5) ) expect_identical( add_months(x, NA), vec_init(x, 2L) ) }) test_that("add_quarters() works (special)", { x <- year_month_weekday(2019, 1, 2, 3:4, 5) expect_identical( add_quarters(x, 1:2), add_months(x, (1:2) * 3) ) expect_identical( add_quarters(x, NA), vec_init(x, 2L) ) }) test_that("add_*() respect recycling rules", { expect_length(add_years(year_month_weekday(1), 1:2), 2L) expect_length(add_years(year_month_weekday(1:2), 1), 2L) expect_length(add_years(year_month_weekday(1), integer()), 0L) expect_length(add_years(year_month_weekday(integer()), 1), 0L) expect_snapshot(error = TRUE, { add_years(year_month_weekday(1:2), 1:3) }) }) test_that("add_*() retains names", { x <- set_names(year_month_weekday(1), "x") y <- year_month_weekday(1) n <- set_names(1, "n") expect_named(add_years(x, n), "x") expect_named(add_years(y, n), "n") }) clock/tests/testthat/test-clock-locale.R0000644000176200001440000000132214423730227020001 0ustar liggesuserstest_that("can create a default locale object", { locale <- clock_locale() expect_s3_class(locale, "clock_locale") expect_snapshot(locale) }) test_that("can change labels with either the language name or labels object", { labels1 <- clock_locale("fr") labels2 <- clock_locale(clock_labels_lookup("fr")) expect_identical(labels1, labels2) }) test_that("must use a valid clock-labels object", { expect_snapshot(error = TRUE, clock_locale(1)) }) test_that("must use a valid decimal-mark", { expect_snapshot(error = TRUE, clock_locale(decimal_mark = "f")) }) test_that("can change the decimal-mark", { x <- clock_locale(decimal_mark = ",") expect_identical(x$decimal_mark, ",") expect_snapshot(x) }) clock/tests/testthat/test-date.R0000644000176200001440000005241614426767326016415 0ustar liggesusers# ------------------------------------------------------------------------------ # as_date() test_that("can convert Date -> Date", { expect_identical(as_date(new_date(0)), new_date(0)) }) test_that("integer Dates are normalized to double", { # seq.Date() can make these x <- 0L class(x) <- "Date" expect_identical(as_date(x), new_date(0)) }) test_that("can convert POSIXct / POSIXlt -> Date (Date assumed to be naive)", { ct <- date_time_parse("2019-01-01 23:00:00", "America/New_York") lt <- as.POSIXlt(ct) expect <- date_parse("2019-01-01") expect_identical(as_date(ct), expect) expect_identical(as_date(lt), expect) }) test_that("can convert calendar -> Date", { expect_identical(as_date(year_month_day(1970, 1, 2)), new_date(1)) expect_identical(as_date(year_quarter_day(1970, 1, 2)), new_date(1)) }) test_that("can convert sys-time -> Date", { expect_identical(as_date(sys_seconds(0)), new_date(0)) }) test_that("can convert naive-time -> Date", { expect_identical(as_date(naive_seconds(0)), new_date(0)) }) test_that("can convert zoned-time -> Date", { x <- as_zoned_time(naive_time_parse("2019-01-01T23:02:03"), "America/New_York") expect <- date_parse("2019-01-01") expect_identical(as_date(x), expect) }) # ------------------------------------------------------------------------------ # as.Date() test_that("invalid dates must be resolved when converting to a Date", { expect_snapshot(error = TRUE, as.Date(year_month_day(2019, 2, 31))) }) test_that("conversion from zoned-time uses naive-time as an intermediate", { x <- as_naive_time(year_month_day(2019, 12, 31, 23, 30, 00)) x <- as_zoned_time(x, "America/New_York") expect_identical(as.Date(x), as.Date("2019-12-31")) }) test_that("casting to Date floors components more precise than days (#205)", { x <- naive_time_parse("1969-01-01T13", precision = "hour") expect_identical( as.Date(x), date_build(1969, 1, 1) ) }) # ------------------------------------------------------------------------------ # as_sys_time() test_that("converting to sys-time floors fractional dates (#191)", { x <- new_date(c(-0.5, 1.5)) y <- new_date(c(-1, 1)) expect_identical(as_sys_time(x), as_sys_time(y)) expect_identical(as.Date(as_sys_time(x)), y) }) test_that("converting to sys-time works with integer storage dates", { # These can occur from `seq.Date(from, to, length.out)` x <- structure(1L, class = "Date") y <- new_date(1) expect_identical(as_sys_time(x), as_sys_time(y)) expect_identical(as.Date(as_sys_time(x)), y) }) # ------------------------------------------------------------------------------ # as_zoned_time() test_that("Dates are assumed to be naive", { x <- new_date(0) nt <- as_naive_time(year_month_day(1970, 1, 1)) expect_identical(as_zoned_time(x, "UTC"), as_zoned_time(nt, "UTC")) expect_identical(as_zoned_time(x, "America/New_York"), as_zoned_time(nt, "America/New_York")) }) test_that("can resolve nonexistent midnight issues", { # In Asia/Beirut, DST gap from 2021-03-27 23:59:59 -> 2021-03-28 01:00:00 zone <- "Asia/Beirut" x <- as.Date("2021-03-28") expect_snapshot({ (expect_error(as_zoned_time(x, zone), class = "clock_error_nonexistent_time")) }) expect_identical( as_zoned_time(x, zone, nonexistent = "roll-forward"), as_zoned_time(as_naive_time(year_month_day(2021, 03, 28, 1)), zone) ) }) test_that("can resolve ambiguous midnight issues", { # In Asia/Amman, DST fallback from 2021-10-29 00:59:59 -> 2021-10-29 00:00:00 zone <- "Asia/Amman" x <- as.Date("2021-10-29") expect_snapshot({ (expect_error(as_zoned_time(x, zone), class = "clock_error_ambiguous_time")) }) expect_identical( as_zoned_time(x, zone, ambiguous = "earliest"), zoned_time_parse_complete("2021-10-29T00:00:00+03:00[Asia/Amman]") ) expect_identical( as_zoned_time(x, zone, ambiguous = "latest"), zoned_time_parse_complete("2021-10-29T00:00:00+02:00[Asia/Amman]") ) }) # ------------------------------------------------------------------------------ # as_weekday() test_that("can convert to weekday", { x <- as.Date("2019-01-01") expect_identical(as_weekday(x), weekday(3L)) }) test_that("converting to weekday retains names", { x <- c(x = as.Date("2019-01-01")) expect_named(as_weekday(x), names(x)) }) # ------------------------------------------------------------------------------ # date_group() test_that("can group by year/month/day", { x <- as.Date("2019-01-01") + c(0, 1, 2, 3) expect_identical(date_group(x, "day"), x) expect_identical(date_group(x, "day", n = 2), x[c(1, 1, 3, 3)]) y <- as.Date(c("2019-01-01", "2019-02-05", "2019-03-10")) expect <- set_day(y, 1) expect_identical(date_group(y, "month"), expect) expect_identical(date_group(y, "month", n = 2), expect[c(1, 1, 3)]) # Using year 0 as the starting point z <- as.Date(c("0000-01-05", "0001-02-10", "0002-03-12", "0003-01-15", "0004-12-10")) expect <- set_month(set_day(z, 1), 1) expect_identical(date_group(z, "year"), expect) expect_identical(date_group(z, "year", n = 3), expect[c(1, 1, 1, 4, 4)]) }) test_that("can't group by finer precisions", { x <- as.Date("2019-01-01") expect_snapshot(error = TRUE, date_group(x, "hour")) expect_snapshot(error = TRUE, date_group(x, "nanosecond")) }) test_that("can't group by non-year-month-day precisions", { x <- as.Date("2019-01-01") expect_snapshot(error = TRUE, date_group(x, "quarter")) }) # ------------------------------------------------------------------------------ # date_leap_year() test_that("can detect leap years", { x <- c("2019-01-01", "2020-01-01", NA) x <- as.Date(x) expect_identical(date_leap_year(x), c(FALSE, TRUE, NA)) }) # ------------------------------------------------------------------------------ # date_floor() test_that("can floor by weeks", { x <- c("1970-01-01", "1970-01-07", "1970-01-08") x <- as.Date(x) expect_identical(date_floor(x, "week"), x[c(1, 1, 3)]) expect_identical(date_floor(x, "week", n = 2), x[c(1, 1, 1)]) }) test_that("can only floor by week/day", { expect_snapshot(error = TRUE, date_floor(as.Date("2019-01-01"), "hour")) expect_snapshot(error = TRUE, date_floor(as.Date("2019-01-01"), "month")) }) test_that("`origin` can be used", { origin <- as.Date("1970-01-02") x <- as.Date(c("1970-01-01", "1970-01-02", "1970-01-03")) expect <- as.Date(c("1969-12-31", "1970-01-02", "1970-01-02")) expect_identical(date_floor(x, "day", n = 2, origin = origin), expect) }) test_that("`origin` is validated", { x <- as.Date("2019-01-01") expect_snapshot(error = TRUE, date_floor(x, "day", origin = 1)) expect_snapshot(error = TRUE, date_floor(x, "day", origin = new_date(NA_real_))) expect_snapshot(error = TRUE, date_floor(x, "day", origin = new_date(Inf))) expect_snapshot(error = TRUE, date_floor(x, "day", origin = new_date(c(0, 1)))) }) # ------------------------------------------------------------------------------ # date_ceiling() test_that("can ceiling", { x <- c("1970-01-01", "1970-01-07", "1970-01-08") x <- as.Date(x) expect_identical(date_ceiling(x, "week"), x[c(1, 3, 3)]) expect_identical(date_ceiling(x, "week", n = 2), as.Date(c("1970-01-01", "1970-01-15", "1970-01-15"))) }) # ------------------------------------------------------------------------------ # date_round() test_that("can round", { x <- c("1970-01-01", "1970-01-03", "1970-01-05", "1970-01-08") x <- as.Date(x) expect_identical(date_round(x, "week"), x[c(1, 1, 4, 4)]) expect_identical(date_round(x, "week", n = 2), as.Date(c("1970-01-01", "1970-01-01", "1970-01-01", "1970-01-15"))) }) # ------------------------------------------------------------------------------ # date_weekday_factor() test_that("can convert to a weekday factor", { x <- as.Date("2019-01-01") + 0:6 expect_identical( date_weekday_factor(x), weekday_factor(as_weekday(x)), ) expect_identical( date_weekday_factor(x, labels = "fr", abbreviate = FALSE, encoding = "iso"), weekday_factor(as_weekday(x), labels = "fr", abbreviate = FALSE, encoding = "iso"), ) }) # ------------------------------------------------------------------------------ # date_month_factor() test_that("can convert to a month factor", { x <- add_months(as.Date("2019-01-01"), 0:11) expect_identical( date_month_factor(x, labels = "fr", abbreviate = TRUE), calendar_month_factor(as_year_month_day(x), labels = "fr", abbreviate = TRUE), ) }) # ------------------------------------------------------------------------------ # date_format() test_that("can format dates", { x <- as.Date("2018-12-31") formats <- test_all_formats(zone = FALSE) expect_snapshot( vapply( X = formats, FUN = function(format) date_format(x, format = format), FUN.VALUE = character(1) ) ) expect_snapshot( vapply( X = formats, FUN = function(format) date_format(x, format = format, locale = clock_locale("fr")), FUN.VALUE = character(1) ) ) }) test_that("formatting Dates with `%z` or `%Z` returns NA with a warning", { x <- as.Date("2018-01-01") expect_warning(date_format(x, format = "%z"), class = "clock_warning_format_failures") expect_snapshot(date_format(x, format = "%z")) expect_warning(date_format(x, format = "%Z"), class = "clock_warning_format_failures") expect_snapshot(date_format(x, format = "%Z")) }) # ------------------------------------------------------------------------------ # date_parse() test_that("`%z` and `%Z` commands are ignored", { expect_identical( date_parse("2019-12-31 -0500", format = "%Y-%m-%d %z"), as.Date("2019-12-31") ) expect_identical( date_parse("2019-12-31 America/New_York", format = "%Y-%m-%d %Z"), as.Date("2019-12-31") ) }) test_that("parsing into a date if you requested to parse time components rounds the time (#207) (#230) (undocumented)", { expect_identical( date_parse("2019-12-31 11:59:59", format = "%Y-%m-%d %H:%M:%S"), as.Date("2019-12-31") ) expect_identical( date_parse("2019-12-31 12:00:00", format = "%Y-%m-%d %H:%M:%S"), as.Date("2020-01-01") ) }) test_that("parsing fails when undocumented rounding behavior would result in invalid 60 second component (#230) (undocumented)", { expect_warning( expect_identical( date_parse("2019-01-01 01:01:59.550", format = "%Y-%m-%d %H:%M:%6S"), new_date(NA_real_) ), class = "clock_warning_parse_failures" ) }) test_that("failure to parse throws a warning", { expect_warning(date_parse("foo"), class = "clock_warning_parse_failures") expect_snapshot(date_parse("foo")) }) # ------------------------------------------------------------------------------ # date_shift() test_that("can shift dates", { x <- date_parse("2019-01-01") x <- x + c(0, 1) monday <- weekday(clock_weekdays$monday) tuesday <- weekday(clock_weekdays$tuesday) expect_identical( date_shift(x, monday), add_days(x, c(6, 5)) ) expect_identical( date_shift(x, monday, which = "previous"), add_days(x, c(-1, -2)) ) expect_identical( date_shift(x, tuesday, boundary = "advance"), add_days(x, c(7, 6)) ) }) # ------------------------------------------------------------------------------ # date_build() test_that("can build a Date", { expect_identical(date_build(2019), as.Date("2019-01-01")) expect_identical(date_build(2020, 2, 3), as.Date("2020-02-03")) }) test_that("can handle invalid dates", { expect_snapshot(error = TRUE, date_build(2019, 1:12, 31)) expect_identical( date_build(2019, 1:12, 31, invalid = "previous"), date_build(2019, 1:12, "last") ) }) # ------------------------------------------------------------------------------ # date_today() test_that("can get the current date", { x <- date_today("America/New_York") expect_length(x, 1) expect_s3_class(x, "Date") }) # ------------------------------------------------------------------------------ # date_start() test_that("can get the start", { x <- date_build(2019, 2, 2) expect_identical(date_start(x, "day"), x) expect_identical(date_start(x, "month"), date_build(2019, 2, 1)) expect_identical(date_start(x, "year"), date_build(2019, 1, 1)) }) test_that("start: can't use invalid precisions", { expect_snapshot(error = TRUE, date_start(date_build(2019), "quarter")) }) # ------------------------------------------------------------------------------ # date_end() test_that("can get the end", { x <- date_build(2019:2020, 2, 2) expect_identical(date_end(x, "day"), x) expect_identical(date_end(x, "month"), date_build(2019:2020, 2, 28:29)) expect_identical(date_end(x, "year"), date_build(2019:2020, 12, 31)) }) test_that("end: can't use invalid precisions", { expect_snapshot(error = TRUE, date_end(date_build(2019), "quarter")) }) # ------------------------------------------------------------------------------ # date_seq() test_that("integer `by` means day precision", { expect_identical( date_seq(new_date(0), to = new_date(5), by = 1), date_seq(new_date(0), to = new_date(5), by = duration_days(1)) ) }) test_that("day precision seq works", { expect_identical( date_seq(new_date(0), to = new_date(5), by = duration_days(2)), new_date(c(0, 2, 4)) ) }) test_that("granular `by` are allowed", { expect_identical( date_seq(date_build(2019, 1, 1), to = date_build(2019, 6, 1), by = duration_months(2)), date_build(2019, c(1, 3, 5), 1) ) expect_identical( date_seq(date_build(2019, 1, 1), to = date_build(2025, 1, 1), by = duration_years(2)), date_build(c(2019, 2021, 2023, 2025), 1, 1) ) }) test_that("combining `by` with `total_size` works", { expect_identical( date_seq(date_build(2019, 1, 1), by = 2, total_size = 3), date_build(2019, 1, c(1, 3, 5)) ) }) test_that("combining `to` with `total_size` works", { expect_identical( date_seq(date_build(2019, 1, 1), to = date_build(2019, 1, 5), total_size = 3), date_build(2019, 1, c(1, 3, 5)) ) }) test_that("can resolve invalid dates", { from <- date_build(2019, 1, 31) to <- date_build(2019, 5, 31) expect_snapshot(error = TRUE, date_seq(from, to = to, by = duration_months(1))) expect_identical( date_seq(from, to = to, by = duration_months(1), invalid = "previous"), date_build(2019, 1:5, "last") ) }) test_that("quarterly `by` works", { expect_identical( date_seq(date_build(2019, 1, 2), by = duration_quarters(1), total_size = 3), date_seq(date_build(2019, 1, 2), by = duration_months(3), total_size = 3) ) }) test_that("weekly `by` works", { expect_identical( date_seq(date_build(2019, 1, 2), by = duration_weeks(1), total_size = 3), date_seq(date_build(2019, 1, 2), by = duration_days(7), total_size = 3) ) }) test_that("components of `from` more precise than `by` are restored", { expect_identical( date_seq(date_build(2019, 2, 3), by = duration_months(1), total_size = 2), date_build(2019, 2:3, 3) ) expect_identical( date_seq(date_build(2019, 2, 3), by = duration_years(1), total_size = 2), date_build(2019:2020, 2, 3) ) }) test_that("seq() with `from > to && by > 0` or `from < to && by > 0` results in length 0 output (#282)", { expect_identical( date_seq(date_build(2019, 1, 2), to = date_build(2019, 1, 1), by = 1), date_build(integer()) ) expect_identical( date_seq(date_build(2019), to = date_build(2020), by = -1), date_build(integer()) ) }) test_that("components of `to` more precise than `by` must match `from`", { expect_snapshot(error = TRUE, date_seq(date_build(2019, 1, 1), to = date_build(2019, 2, 2), by = duration_months(1))) expect_snapshot(error = TRUE, date_seq(date_build(2019, 1, 1), to = date_build(2019, 3, 1), by = duration_years(1))) }) test_that("validates integerish `by`", { expect_snapshot(error = TRUE, date_seq(new_date(1), by = 1.5, total_size = 1)) }) test_that("validates `total_size` early", { expect_snapshot(error = TRUE, date_seq(new_date(1), by = 1, total_size = 1.5)) expect_snapshot(error = TRUE, date_seq(new_date(1), by = 1, total_size = NA)) expect_snapshot(error = TRUE, date_seq(new_date(1), by = 1, total_size = -1)) }) test_that("`to` and `total_size` must not generate a non-fractional sequence", { expect_snapshot(error = TRUE, date_seq(new_date(0), to = new_date(3), total_size = 3)) }) test_that("requires exactly two optional arguments", { expect_snapshot(error = TRUE, date_seq(new_date(1), by = 1)) expect_snapshot(error = TRUE, date_seq(new_date(1), total_size = 1)) expect_snapshot(error = TRUE, date_seq(new_date(1), to = new_date(1))) }) test_that("requires `to` to be Date", { expect_snapshot(error = TRUE, date_seq(new_date(1), to = 1, by = 1)) }) test_that("requires year, month, or day precision", { expect_snapshot(error = TRUE, date_seq(new_date(1), to = new_date(2), by = duration_nanoseconds(1))) }) test_that("checks empty dots", { expect_snapshot(error = TRUE, date_seq(new_date(1), new_date(2))) }) test_that("golden test: ensure that we never allow components of `to` to differ with `from` (#224)", { from <- date_build(1970, 01, 31) to <- date_build(1970, 03, 01) expect_error(date_seq(from, to = to, by = duration_months(1), invalid = "overflow")) # Could theoretically generate this, where the second element is past `to` #> "1970-01-31" "1970-03-03" }) # ------------------------------------------------------------------------------ # date_spanning_seq() test_that("generates the regular sequence along the full span", { x <- date_build(c(2019, 2022, 2020), c(2, 1, 3), c(3, 5, 2)) expect_identical( date_spanning_seq(x), seq(date_build(2019, 2, 3), date_build(2022, 1, 5), by = 1) ) }) test_that("missing values are removed", { x <- date_build(2020, 1, c(1, NA, 5, 2)) expect_identical(date_spanning_seq(x), date_build(2020, 1, 1:5)) x <- date_build(c(NA, NA)) expect_identical(date_spanning_seq(x), new_date()) }) test_that("infinite dates are removed", { x <- new_date(c(0, Inf, 3, 6, -Inf)) expect_identical(date_spanning_seq(x), new_date(as.double(0:6))) x <- new_date(c(Inf, -Inf)) expect_identical(date_spanning_seq(x), new_date()) }) test_that("works with empty vectors", { x <- date_build(integer()) expect_identical(date_spanning_seq(x), x) }) # ------------------------------------------------------------------------------ # date_count_between() test_that("can compute precisions at year / quarter / month / week / day", { x <- date_build(2019, 1, 5) y <- date_build(2025, 1, c(4, 6)) expect_identical(date_count_between(x, y, "year"), c(5L, 6L)) expect_identical(date_count_between(x, y, "quarter"), c(23L, 24L)) expect_identical(date_count_between(x, y, "month"), c(71L, 72L)) y <- date_build(2019, 1, c(25, 26)) expect_identical(date_count_between(x, y, "week"), c(2L, 3L)) expect_identical(date_count_between(x, y, "day"), c(20L, 21L)) }) test_that("must use a valid Date precision", { x <- date_build(2019) expect_snapshot((expect_error(date_count_between(x, x, "hour")))) }) test_that("can't count between a Date and a POSIXt", { x <- date_build(2019) y <- date_time_build(2019, zone = "UTC") expect_snapshot((expect_error(date_count_between(x, y, "year")))) }) # ------------------------------------------------------------------------------ # vec_arith() test_that(" op ", { expect_identical(vec_arith("+", new_date(0), duration_years(1)), new_date(365)) expect_identical(vec_arith("-", new_date(0), duration_years(1)), new_date(-365)) expect_snapshot(error = TRUE, vec_arith("+", new_date(0), duration_hours(1))) expect_snapshot(error = TRUE, vec_arith("*", new_date(0), duration_years(1))) }) test_that(" op ", { expect_identical(vec_arith("+", duration_years(1), new_date(0)), new_date(365)) expect_snapshot(error = TRUE, vec_arith("-", duration_years(1), new_date(0))) expect_snapshot(error = TRUE, vec_arith("+", duration_hours(1), new_date(0))) expect_snapshot(error = TRUE, vec_arith("*", duration_years(1), new_date(0))) }) # ------------------------------------------------------------------------------ # slider_plus() / slider_minus() test_that("`slider_plus()` method is registered", { skip_if_not_installed("slider", minimum_version = "0.3.0") x <- date_build(2019, 1, 1:2) y <- duration_days(2) expect_identical( slider::slider_plus(x, y), date_build(2019, 1, 3:4) ) y <- duration_years(1) expect_identical( slider::slider_plus(x, y), date_build(2020, 1, 1:2) ) }) test_that("`slider_minus()` method is registered", { skip_if_not_installed("slider", minimum_version = "0.3.0") x <- date_build(2019, 1, 1:2) y <- duration_days(2) expect_identical( slider::slider_minus(x, y), date_build(2018, 12, 30:31) ) y <- duration_years(1) expect_identical( slider::slider_minus(x, y), date_build(2018, 1, 1:2) ) }) test_that("`slide_index()` works with dates and durations", { skip_if_not_installed("slider", minimum_version = "0.3.0") i <- date_build(2019, 1, 1:4) x <- seq_along(i) before <- duration_days(1) after <- duration_days(2) expect_identical( slider::slide_index(x, i, identity, .before = before, .after = after), list( 1:3, 1:4, 2:4, 3:4 ) ) }) test_that("`slide_index()` will error on calendrical arithmetic and invalid dates", { skip_if_not_installed("slider", minimum_version = "0.3.0") i <- date_build(2019, 1, 28:31) x <- seq_along(i) after <- duration_months(1) expect_snapshot(error = TRUE, { slider::slide_index(x, i, identity, .after = after) }) }) clock/tests/testthat/test-zoned-time.R0000644000176200001440000004504214423752771017543 0ustar liggesusers# ------------------------------------------------------------------------------ # vec_proxy_compare() # Really testing `vec_proxy()` and `vec_ptype2()` / `vec_cast()` test_that("can't compare zoned-times with different zones", { x <- as_zoned_time(sys_days(0), "America/New_York") y <- as_zoned_time(sys_days(0), "UTC") expect_snapshot(error = TRUE, x > y) }) test_that("can compare zoned-times with same zone", { x <- as_zoned_time(sys_days(0:1), "America/New_York") expect_false(x[1] > x[2]) expect_true(x[1] < x[2]) }) # ------------------------------------------------------------------------------ # print() / obj_print_data() / obj_print_footer() test_that("normal print method works", { x <- as_zoned_time(as_sys_time(year_month_day(2019, 1:5, 1)), "America/New_York") expect_snapshot(x) }) test_that("can limit with `max`", { x <- as_zoned_time(as_sys_time(year_month_day(2019, 1:5, 1)), "America/New_York") expect_snapshot(print(x, max = 2)) expect_snapshot(print(x, max = 4)) # no footer if length >= max expect_snapshot(print(x, max = 5)) expect_snapshot(print(x, max = 6)) }) test_that("`max` defaults to `getOption('max.print')` but can be overridden", { local_options(max.print = 3) x <- as_zoned_time(as_sys_time(year_month_day(2019, 1:5, 1)), "America/New_York") expect_snapshot(x) expect_snapshot(print(x, max = 4)) expect_snapshot(print(x, max = 5)) }) # ------------------------------------------------------------------------------ # as_zoned_time() test_that("`as_zoned_time()` has good default error", { expect_snapshot(error = TRUE, { as_zoned_time(1) }) }) # ------------------------------------------------------------------------------ # as_year_month_day() test_that("can't convert zoned-time directly to calendar", { x <- as_zoned_time(as_naive_time(year_month_day(2019, 1, 1)), "America/New_York") expect_snapshot({ (expect_error(as_year_month_day(x), class = "clock_error_unsupported_conversion")) }) }) # ------------------------------------------------------------------------------ # as.character() test_that("as.character() works", { expect <- "2019-01-01T01:02:03-05:00[America/New_York]" x <- zoned_time_parse_complete(expect) expect_identical(as.character(x), expect) }) # ------------------------------------------------------------------------------ # zoned_time_parse_complete() test_that("can parse what we format with seconds precision zoned time", { zone <- "America/New_York" x <- as_zoned_time(as_naive_time(year_month_day(2019, 1, 1)), zone) expect_identical( zoned_time_parse_complete(format(x)), x ) }) test_that("can parse subsecond zoned time", { zone <- "America/New_York" x <- "2019-01-01T01:02:03.123-05:00[America/New_York]" y <- "2019-01-01T01:02:03.1234-05:00[America/New_York]" z <- "2019-01-01T01:02:03.123456789-05:00[America/New_York]" expect_identical( zoned_time_parse_complete(x, precision = "millisecond"), as_zoned_time(as_naive_time(year_month_day(2019, 1, 1, 1, 2, 3, 123, subsecond_precision = "millisecond")), zone) ) expect_identical( zoned_time_parse_complete(y, precision = "microsecond"), as_zoned_time(as_naive_time(year_month_day(2019, 1, 1, 1, 2, 3, 123400, subsecond_precision = "microsecond")), zone) ) expect_identical( zoned_time_parse_complete(z, precision = "nanosecond"), as_zoned_time(as_naive_time(year_month_day(2019, 1, 1, 1, 2, 3, 123456789, subsecond_precision = "nanosecond")), zone) ) }) test_that("multiple formats can be used", { zone <- "America/New_York" x <- c( "1970-10-25 05:30:00-05:00[America/New_York]", "1970/10/25 05:30:00-05:00[America/New_York]" ) formats <- c( "%Y-%m-%d %H:%M:%S%Ez[%Z]", "%Y/%m/%d %H:%M:%S%Ez[%Z]" ) expect_identical( zoned_time_parse_complete(x, format = formats), as_zoned_time( as_naive_time(year_month_day(1970, 10, 25, 05, 30, c(00, 00))), zone ) ) }) test_that("cannot parse nonexistent time", { zone <- "America/New_York" x <- "1970-04-26T02:30:00-05:00[America/New_York]" expect_warning( expect_identical( zoned_time_parse_complete(x), as_zoned_time(naive_seconds(NA), zone) ) ) expect_snapshot(zoned_time_parse_complete(x)) }) test_that("ambiguous times are resolved by the offset", { zone <- "America/New_York" x <- c( "1970-10-25T01:30:00-04:00[America/New_York]", "1970-10-25T01:30:00-05:00[America/New_York]" ) expect_identical( zoned_time_parse_complete(x), as_zoned_time( as_naive_time(year_month_day(1970, 10, 25, 01, 30, c(00, 00))), zone, ambiguous = c("earliest", "latest") ) ) }) test_that("offset must align with unique offset", { zone <- "America/New_York" # Should be `-05:00` x <- "2019-01-01T01:02:03-03:00[America/New_York]" expect_warning( expect_identical( zoned_time_parse_complete(x), as_zoned_time(naive_seconds(NA), zone) ) ) expect_snapshot(zoned_time_parse_complete(x)) }) test_that("offset must align with one of two possible ambiguous offsets", { zone <- "America/New_York" # Should be `-04:00` or `-05:00` x <- c( "1970-10-25T01:30:00-03:00[America/New_York]", "1970-10-25T01:30:00-06:00[America/New_York]" ) expect_warning( expect_identical( zoned_time_parse_complete(x), as_zoned_time(naive_seconds(c(NA, NA)), zone) ) ) expect_snapshot(zoned_time_parse_complete(x)) }) test_that("cannot have differing zone names", { x <- c( "2019-01-01T01:02:03-05:00[America/New_York]", "2019-01-01T01:02:03-08:00[America/Los_Angeles]" ) expect_snapshot(error = TRUE, zoned_time_parse_complete(x)) }) test_that("zone name must be valid", { x <- "2019-01-01T01:02:03-05:00[America/New_Yor]" expect_snapshot(error = TRUE, zoned_time_parse_complete(x)) }) test_that("empty input uses UTC time zone (#162)", { expect_identical( zoned_time_parse_complete(character()), as_zoned_time(naive_seconds(), "UTC") ) expect_identical( zoned_time_parse_complete(character(), precision = "nanosecond"), as_zoned_time(as_naive_time(duration_nanoseconds()), "UTC") ) }) test_that("all `NA`s uses UTC time zone (#162)", { expect_identical( zoned_time_parse_complete(c(NA_character_, NA_character_)), as_zoned_time(naive_seconds(c(NA, NA)), "UTC") ) }) test_that("all failures uses UTC time zone (#162)", { expect_warning( expect_identical( zoned_time_parse_complete(c("foo", "bar")), as_zoned_time(naive_seconds(c(NA, NA)), "UTC") ) ) }) test_that("`x` is translated to UTF-8", { x <- "2019-f\u00E9vrier-01 01:02:03-05:00[America/New_York]" x <- iconv(x, from = "UTF-8", to = "latin1") locale <- clock_locale("fr") format <- "%Y-%B-%d %H:%M:%S%Ez[%Z]" expect_identical(Encoding(x), "latin1") expect_identical(Encoding(locale$labels$month[2]), "UTF-8") expect_identical( zoned_time_parse_complete(x, format = format, locale = locale), as_zoned_time(as_naive_time(year_month_day(2019, 2, 1, 1, 2, 3)), "America/New_York") ) }) test_that("leftover subseconds result in a parse failure", { x <- "2019-01-01T01:01:01.1238-05:00[America/New_York]" # This is fine expect_identical( zoned_time_parse_complete(x, precision = "microsecond"), as_zoned_time(as_naive_time(year_month_day(2019, 1, 1, 1, 1, 1, 123800, subsecond_precision = "microsecond")), "America/New_York") ) # This defaults to `%6S`, which parses `01.123` then stops, # leaving a `8` for `%z` to parse, resulting in a failure. Because everything # fails, we get a UTC time zone. expect_warning( expect_identical( zoned_time_parse_complete(x, precision = "millisecond"), as_zoned_time(naive_seconds(NA) + duration_milliseconds(NA), zone = "UTC") ), class = "clock_warning_parse_failures" ) }) test_that("parsing rounds parsed subsecond components more precise than the resulting container (#207) (#230) (undocumented)", { x <- "2019-01-01 01:01:01.1238-05:00[America/New_York]" # Requesting `%7S` parses the full `01.1238`, and the `1238` portion is rounded up expect_identical( zoned_time_parse_complete(x, precision = "millisecond", format = "%Y-%m-%d %H:%M:%7S%Ez[%Z]"), as_zoned_time(as_naive_time(year_month_day(2019, 1, 1, 1, 1, 1, 124, subsecond_precision = "millisecond")), "America/New_York") ) }) test_that("parsing fails when undocumented rounding behavior would result in invalid 60 second component (#230) (undocumented)", { x <- "2019-01-01 01:01:59.550-05:00[America/New_York]" # Requesting `%6S` parses the full `59.550`, which is immediately rounded to `60` which looks invalid. # The correct way to do this is to parse the milliseconds, then round. expect_warning( expect_identical( zoned_time_parse_complete(x, precision = "second", format = "%Y-%m-%d %H:%M:%6S%Ez[%Z]"), as_zoned_time(as_naive_time(year_month_day(NA, NA, NA, NA, NA, NA)), zone = "UTC") ), class = "clock_warning_parse_failures" ) }) # ------------------------------------------------------------------------------ # zoned_time_parse_abbrev() test_that("can parse with abbreviation and zone name", { expect_identical( zoned_time_parse_abbrev("2019-01-01 01:02:03 EST", "America/New_York"), zoned_time_parse_complete("2019-01-01T01:02:03-05:00[America/New_York]") ) }) test_that("can parse when abbreviation is an offset", { expect_identical( zoned_time_parse_abbrev("2019-01-01 01:02:03 +11", "Australia/Lord_Howe"), zoned_time_parse_complete("2019-01-01T01:02:03+11:00[Australia/Lord_Howe]") ) expect_identical( zoned_time_parse_abbrev("2019-10-01 01:02:03 +1030", "Australia/Lord_Howe"), zoned_time_parse_complete("2019-10-01T01:02:03+10:30[Australia/Lord_Howe]") ) }) test_that("can parse at more precise precisions", { expect_identical( zoned_time_parse_abbrev("2019-01-01 01:02:03.123 EST", "America/New_York", precision = "millisecond"), as_zoned_time(as_naive_time(year_month_day(2019, 1, 1, 1, 2, 3, 123, subsecond_precision = "millisecond")), "America/New_York") ) expect_identical( zoned_time_parse_abbrev("2019-01-01 01:02:03.123456 EST", "America/New_York", precision = "nanosecond"), as_zoned_time(as_naive_time(year_month_day(2019, 1, 1, 1, 2, 3, 123456000, subsecond_precision = "nanosecond")), "America/New_York") ) }) test_that("abbreviation is used to resolve ambiguity", { x <- c( "1970-10-25 01:30:00 EDT", "1970-10-25 01:30:00 EST" ) expect <- c( "1970-10-25T01:30:00-04:00[America/New_York]", "1970-10-25T01:30:00-05:00[America/New_York]" ) expect_identical( zoned_time_parse_abbrev(x, "America/New_York"), zoned_time_parse_complete(expect) ) }) test_that("nonexistent times are NAs", { expect_warning( expect_identical( zoned_time_parse_abbrev("1970-04-26 02:30:00 EST", "America/New_York"), as_zoned_time(sys_seconds(NA), "America/New_York") ) ) }) test_that("abbreviation must match the one implied from naive + time zone name lookup", { x <- "1970-01-01 00:00:00 FOOBAR" expect_warning( expect_identical( zoned_time_parse_abbrev(x, "America/New_York"), as_zoned_time(sys_days(NA), "America/New_York") ) ) # Should be EST x <- "1970-01-01 00:00:00 EDT" expect_warning( expect_identical( zoned_time_parse_abbrev(x, "America/New_York"), as_zoned_time(sys_days(NA), "America/New_York") ) ) expect_snapshot(zoned_time_parse_abbrev(x, "America/New_York")) }) test_that("%Z must be used", { x <- "1970-01-01" expect_snapshot(error = TRUE, zoned_time_parse_abbrev(x, "America/New_York", format = "%Y-%m-%d") ) }) test_that("%z can be parsed (but is ignored really)", { expect <- zoned_time_parse_complete("1970-01-01T00:00:00-05:00[America/New_York]") x <- "1970-01-01 00:00:00-05:00 EST" expect_identical( zoned_time_parse_abbrev(x, "America/New_York", format = "%Y-%m-%d %H:%M:%S%Ez %Z"), expect ) }) test_that("%z that is incorrect technically slips through unnoticed", { expect <- zoned_time_parse_complete("1970-01-01T00:00:00-05:00[America/New_York]") x <- "1970-01-01 00:00:00-02:00 EST" expect_identical( zoned_time_parse_abbrev(x, "America/New_York", format = "%Y-%m-%d %H:%M:%S%Ez %Z"), expect ) }) test_that("%z must parse correctly if included", { expect <- as_zoned_time(sys_days(NA), "America/New_York") x <- "1970-01-01 00:00:00-0a:00 EST" expect_warning( result <- zoned_time_parse_abbrev(x, "America/New_York", format = "%Y-%m-%d %H:%M:%S%Ez %Z") ) expect_identical(result, expect) }) test_that("multiple formats can be attempted", { x <- c("1970-01-01 EST", "1970-01-01 05:06:07 EST") formats <- c("%Y-%m-%d %H:%M:%S %Z", "%Y-%m-%d %Z") expect <- as_zoned_time( as_naive_time(year_month_day(1970, 1, 1, c(0, 5), c(0, 6), c(0, 7))), "America/New_York" ) expect_identical( zoned_time_parse_abbrev(x, "America/New_York", format = formats), expect ) }) test_that("NA parses correctly", { expect_identical( zoned_time_parse_abbrev(NA_character_, "America/New_York"), as_zoned_time(sys_seconds(NA), "America/New_York") ) expect_identical( zoned_time_parse_abbrev(NA_character_, "America/New_York", precision = "nanosecond"), as_zoned_time(as_sys_time(duration_nanoseconds(NA)), "America/New_York") ) }) test_that("`x` is translated to UTF-8", { x <- "2019-f\u00E9vrier-01 01:02:03-05:00[EST]" x <- iconv(x, from = "UTF-8", to = "latin1") locale <- clock_locale("fr") format <- "%Y-%B-%d %H:%M:%S%Ez[%Z]" expect_identical(Encoding(x), "latin1") expect_identical(Encoding(locale$labels$month[2]), "UTF-8") expect_identical( zoned_time_parse_abbrev(x, "America/New_York", format = format, locale = locale), as_zoned_time(as_naive_time(year_month_day(2019, 2, 1, 1, 2, 3)), "America/New_York") ) }) # ------------------------------------------------------------------------------ # zoned_time_info() test_that("can get info of a zoned time (#295)", { zone <- "America/New_York" x <- as_zoned_time(as_naive_time(year_month_day(2019, 1, 1)), zone) x <- zoned_time_info(x) begin <- as_zoned_time(as_naive_time(year_month_day(2018, 11, 4, 1)), zone, ambiguous = "latest") end <- as_zoned_time(as_naive_time(year_month_day(2019, 03, 10, 3)), zone) expect_identical(x$begin, begin) expect_identical(x$end, end) expect_identical(x$offset, duration_seconds(-18000)) expect_identical(x$dst, FALSE) expect_identical(x$abbreviation, "EST") }) test_that("`NA` propagates", { x <- as_zoned_time(as_naive_time(year_month_day(NA, NA, NA)), "UTC") info <- zoned_time_info(x) expect_identical(info$begin, x) expect_identical(info$end, x) expect_identical(info$offset, duration_seconds(NA)) expect_identical(info$dst, NA) expect_identical(info$abbreviation, NA_character_) }) test_that("boundaries are handled right", { x <- as_zoned_time(as_naive_time(year_month_day(2019, 1, 1)), "UTC") x <- zoned_time_info(x) # Only snapshotting in case boundaries are different on CRAN expect_snapshot(x$begin) expect_snapshot(x$end) expect_identical(x$offset, duration_seconds(0)) expect_identical(x$dst, FALSE) expect_identical(x$abbreviation, "UTC") }) test_that("input must be a zoned-time", { expect_snapshot(error = TRUE, { zoned_time_info(1) }) }) # ------------------------------------------------------------------------------ # add_*() test_that("zoned-times don't support arithmetic", { x <- as_zoned_time(as_naive_time(year_month_day(2019, 1, 1)), "America/New_York") expect_snapshot(error = TRUE, add_years(x, 1)) expect_snapshot(error = TRUE, add_quarters(x, 1)) expect_snapshot(error = TRUE, add_months(x, 1)) expect_snapshot(error = TRUE, add_weeks(x, 1)) expect_snapshot(error = TRUE, add_days(x, 1)) expect_snapshot(error = TRUE, add_hours(x, 1)) expect_snapshot(error = TRUE, add_minutes(x, 1)) expect_snapshot(error = TRUE, add_seconds(x, 1)) expect_snapshot(error = TRUE, add_milliseconds(x, 1)) expect_snapshot(error = TRUE, add_microseconds(x, 1)) expect_snapshot(error = TRUE, add_nanoseconds(x, 1)) }) # ------------------------------------------------------------------------------ # vec_ptype() test_that("ptype is correct", { zones <- c("UTC", "America/New_York", "") for (zone in zones) { for (precision in precision_names()) { precision <- precision_to_integer(precision) if (precision < PRECISION_SECOND) { next } x <- duration_helper(0L, precision) x <- as_zoned_time(as_naive_time(x), zone) ptype <- duration_helper(integer(), precision) ptype <- as_zoned_time(as_naive_time(ptype), zone) expect_identical(vec_ptype(x), ptype) } } }) # ------------------------------------------------------------------------------ # vec_math() test_that("is.nan() works", { zone <- "America/New_York" x <- as_zoned_time(naive_days(c(2019, NA)), zone) expect_identical(is.nan(x), c(FALSE, FALSE)) }) test_that("is.finite() works", { zone <- "America/New_York" x <- as_zoned_time(naive_days(c(2019, NA)), zone) expect_identical(is.finite(x), c(TRUE, FALSE)) }) test_that("is.infinite() works", { zone <- "America/New_York" x <- as_zoned_time(naive_days(c(2019, NA)), zone) expect_identical(is.infinite(x), c(FALSE, FALSE)) }) # ------------------------------------------------------------------------------ # zoned_time_zone() test_that("can get the time zone of a zoned-time", { zone <- "America/New_York" x <- as_zoned_time(naive_days(0), zone) expect_identical(zoned_time_zone(x), zone) }) test_that("`zoned_time_zone()` validates `x`", { expect_snapshot(error = TRUE, { zoned_time_zone(1) }) }) # ------------------------------------------------------------------------------ # zoned_time_set_zone() test_that("can set the time zone of a zoned-time", { zone1 <- "America/New_York" zone2 <- "America/Los_Angeles" sys <- sys_days(0) x <- as_zoned_time(sys, zone1) y <- as_zoned_time(sys, zone2) expect_identical(zoned_time_set_zone(x, zone2), y) }) test_that("`zoned_time_set_zone()` validates `x`", { expect_snapshot(error = TRUE, { zoned_time_set_zone(1, "UTC") }) }) # ------------------------------------------------------------------------------ # zoned_time_precision() test_that("precision: can get the precision", { zone <- "America/New_York" expect_identical(zoned_time_precision(as_zoned_time(as_naive_time(duration_seconds(2:5)), zone)), "second") expect_identical(zoned_time_precision(as_zoned_time(as_naive_time(duration_nanoseconds(2:5)), zone)), "nanosecond") }) test_that("precision: can only be called on zoned-times", { expect_snapshot(error = TRUE, zoned_time_precision(duration_days())) }) clock/tests/testthat/test-utils.R0000644000176200001440000000117314423730227016615 0ustar liggesuserstest_that("POSIXt time zones are standardized as expected", { expect_identical(posixt_tzone_standardize(NULL), "") expect_identical(posixt_tzone_standardize(""), "") expect_identical(posixt_tzone_standardize("America/New_York"), "America/New_York") # POSIXlt can return something like this expect_identical(posixt_tzone_standardize(c("", "EST", "EDT")), "") # Unknown if this can happen, but we handle it anyways expect_snapshot(posixt_tzone_standardize(character())) expect_warning( expect_identical(posixt_tzone_standardize(character()), "") ) expect_snapshot(error = TRUE, posixt_tzone_standardize(1)) }) clock/tests/testthat/test-quarterly-year-quarter-day.R0000644000176200001440000006512214427233200022674 0ustar liggesusers# ------------------------------------------------------------------------------ # year_quarter_day() test_that("helper can create different precisions", { x <- year_quarter_day(2019, 1:2) expect_identical(get_year(x), c(2019L, 2019L)) expect_identical(get_quarter(x), 1:2) x <- year_quarter_day(2019, 1:2, 3) expect_identical(get_day(x), c(3L, 3L)) }) test_that("can create subsecond precision calendars", { x <- year_quarter_day(2019, 1, 1, 0, 0, 0, 1, subsecond_precision = "millisecond") expect_identical(get_millisecond(x), 1L) x <- year_quarter_day(2019, 1, 1, 0, 0, 0, 1, subsecond_precision = "microsecond") expect_identical(get_microsecond(x), 1L) x <- year_quarter_day(2019, 1, 1, 0, 0, 0, 1, subsecond_precision = "nanosecond") expect_identical(get_nanosecond(x), 1L) }) test_that("validates value ranges", { expect_snapshot(error = TRUE, year_quarter_day(50000)) expect_snapshot(error = TRUE, year_quarter_day(2020, 5)) expect_snapshot(error = TRUE, year_quarter_day(2020, 1, 93)) expect_snapshot(error = TRUE, year_quarter_day(2020, 1, 1, 24)) expect_snapshot(error = TRUE, year_quarter_day(2020, 1, 1, 1, 60)) expect_snapshot(error = TRUE, year_quarter_day(2020, 1, 1, 1, 1, 60)) expect_snapshot(error = TRUE, year_quarter_day(2020, 1, 1, 1, 1, 1, 1000, subsecond_precision = "millisecond")) expect_snapshot(error = TRUE, year_quarter_day(2020, 1, 1, 1, 1, 1, 1000000, subsecond_precision = "microsecond")) expect_snapshot(error = TRUE, year_quarter_day(2020, 1, 1, 1, 1, 1, 1000000000, subsecond_precision = "nanosecond")) }) test_that("can get the last day of the quarter", { x <- year_quarter_day(2019, 1:2, "last") expect_identical(get_day(x), c(90L, 91L)) }) test_that("`NA` propagates through 'last'", { x <- year_quarter_day(2019, c(1, NA)) x <- set_day(x, "last") expect_identical(get_day(x), c(90L, NA)) }) test_that("ignores values past first `NULL`", { expect_identical(year_quarter_day(2019, day = 2), year_quarter_day(2019)) }) test_that("NA values propagate", { x <- year_quarter_day(2019, 1:3, c(NA, 2, 3), c(3, 4, NA)) expect_identical(is.na(x), c(TRUE, FALSE, TRUE)) }) # ------------------------------------------------------------------------------ # vec_ptype() test_that("ptype is correct", { base <- year_quarter_day(1) ptype <- year_quarter_day(integer()) for (precision in precision_names()) { if (precision == "month" || precision == "week") { next } x <- calendar_widen(base, precision) expect <- calendar_widen(ptype, precision) expect_identical(vec_ptype(x), expect) } }) # ------------------------------------------------------------------------------ # vec_proxy() / vec_restore() test_that("proxy is a data frame", { expect_identical(vec_proxy(year_quarter_day(2019)), data_frame(year = 2019L)) expect_identical(vec_proxy(year_quarter_day(2019, 1)), data_frame(year = 2019L, quarter = 1L)) }) test_that("proxy has names on `year`", { x <- set_names(year_quarter_day(2019, 1), "nm") year <- vec_proxy(x)$year expect_named(year, "nm") }) test_that("restore works", { to <- year_quarter_day(2019, 1:4) proxy <- vec_slice(vec_proxy(to), 1:2) expect_identical(vec_restore(proxy, to), year_quarter_day(2019, 1:2)) }) # ------------------------------------------------------------------------------ # vec_ptype_full() test_that("full ptype is correct", { expect_snapshot_output(vec_ptype_full(year_quarter_day(2019))) expect_snapshot_output(vec_ptype_full(year_quarter_day(2019, start = 2))) expect_snapshot_output(vec_ptype_full(year_quarter_day(2019, 1, 1))) expect_snapshot_output(vec_ptype_full(year_quarter_day(2019, 1, 1, 1, 1, 1, 1, subsecond_precision = "nanosecond"))) expect_snapshot_output(vec_ptype_full(year_quarter_day(2019, 1, 92))) }) # ------------------------------------------------------------------------------ # vec_ptype_abbr() test_that("abbreviated ptype is correct", { expect_snapshot_output(vec_ptype_abbr(year_quarter_day(2019))) expect_snapshot_output(vec_ptype_abbr(year_quarter_day(2019, start = 2))) expect_snapshot_output(vec_ptype_abbr(year_quarter_day(2019, 1, 1))) expect_snapshot_output(vec_ptype_abbr(year_quarter_day(2019, 1, 1, 1, 1, 1, 1, subsecond_precision = "nanosecond"))) expect_snapshot_output(vec_ptype_abbr(year_quarter_day(2019, 1, 92))) }) # ------------------------------------------------------------------------------ # set_*() test_that("setters work", { x <- year_quarter_day(1L) x <- set_year(x, 2L) expect_identical(get_year(x), 2L) x <- set_quarter(x, 1L) expect_identical(get_quarter(x), 1L) x <- set_day(x, 2L) expect_identical(get_day(x), 2L) x <- set_hour(x, 3L) expect_identical(get_hour(x), 3L) x <- set_minute(x, 4L) expect_identical(get_minute(x), 4L) x <- set_second(x, 5L) expect_identical(get_second(x), 5L) ms <- set_millisecond(x, 6L) expect_identical(get_millisecond(ms), 6L) us <- set_microsecond(x, 7L) expect_identical(get_microsecond(us), 7L) ns <- set_nanosecond(x, 8L) expect_identical(get_nanosecond(ns), 8L) }) test_that("setters propagate all missings", { x <- year_quarter_day(2019, c(1, NA, 3)) x <- set_day(x, c(NA, 2, 4)) expect_identical(vec_detect_missing(x), c(TRUE, TRUE, FALSE)) }) test_that("setters recycling works both ways", { x <- year_quarter_day(2019) x <- set_quarter(x, 1:2) expect_identical(x, year_quarter_day(2019, 1:2)) x <- set_day(x, 1) expect_identical(x, year_quarter_day(2019, 1:2, 1)) expect_snapshot(error = TRUE, { x <- year_quarter_day(1:2) y <- 1:3 set_quarter(x, y) }) }) test_that("setters require integer `value`", { x <- year_quarter_day(2019, 1, 2, 3, 4, 5) expect_snapshot(error = TRUE, { set_year(x, 1.5) }) expect_snapshot(error = TRUE, { set_quarter(x, 1.5) }) expect_snapshot(error = TRUE, { set_day(x, 1.5) }) expect_snapshot(error = TRUE, { set_hour(x, 1.5) }) expect_snapshot(error = TRUE, { set_minute(x, 1.5) }) expect_snapshot(error = TRUE, { set_second(x, 1.5) }) expect_snapshot(error = TRUE, { set_millisecond(x, 1.5) }) expect_snapshot(error = TRUE, { set_microsecond(x, 1.5) }) expect_snapshot(error = TRUE, { set_nanosecond(x, 1.5) }) }) test_that("setters check `value` range", { x <- year_quarter_day(2019, 1, 2, 3, 4, 5) expect_snapshot(error = TRUE, { set_year(x, 100000) }) expect_snapshot(error = TRUE, { set_quarter(x, 5) }) expect_snapshot(error = TRUE, { set_day(x, 93) }) expect_snapshot(error = TRUE, { set_hour(x, 24) }) expect_snapshot(error = TRUE, { set_minute(x, 60) }) expect_snapshot(error = TRUE, { set_second(x, 60) }) expect_snapshot(error = TRUE, { set_millisecond(x, -1) }) expect_snapshot(error = TRUE, { set_microsecond(x, -1) }) expect_snapshot(error = TRUE, { set_nanosecond(x, -1) }) }) test_that("setters require minimum precision", { expect_snapshot(error = TRUE, { set_day(year_quarter_day(year = 1), 1) }) expect_snapshot(error = TRUE, { set_hour(year_quarter_day(year = 1, quarter = 2), 1) }) expect_snapshot(error = TRUE, { set_minute(year_quarter_day(year = 1, quarter = 2, day = 3), 1) }) expect_snapshot(error = TRUE, { set_second(year_quarter_day(year = 1, quarter = 2, day = 3, hour = 4), 1) }) expect_snapshot(error = TRUE, { set_millisecond(year_quarter_day(year = 1, quarter = 2, day = 3, hour = 4, minute = 5), 1) }) expect_snapshot(error = TRUE, { set_microsecond(year_quarter_day(year = 1, quarter = 2, day = 3, hour = 4, minute = 5), 1) }) expect_snapshot(error = TRUE, { set_nanosecond(year_quarter_day(year = 1, quarter = 2, day = 3, hour = 4, minute = 5), 1) }) }) test_that("setters require correct subsecond precision", { expect_snapshot(error = TRUE, { set_millisecond(year_quarter_day(year = 1, quarter = 2, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "microsecond"), 1) }) expect_snapshot(error = TRUE, { set_millisecond(year_quarter_day(year = 1, quarter = 2, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "nanosecond"), 1) }) expect_snapshot(error = TRUE, { set_microsecond(year_quarter_day(year = 1, quarter = 2, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "millisecond"), 1) }) expect_snapshot(error = TRUE, { set_microsecond(year_quarter_day(year = 1, quarter = 2, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "nanosecond"), 1) }) expect_snapshot(error = TRUE, { set_nanosecond(year_quarter_day(year = 1, quarter = 2, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "millisecond"), 1) }) expect_snapshot(error = TRUE, { set_nanosecond(year_quarter_day(year = 1, quarter = 2, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "microsecond"), 1) }) }) test_that("setters retain names", { x <- year_quarter_day(2019) x <- set_names(x, "foo") expect_named(set_quarter(x, 2), "foo") }) test_that("setting with named `value` strips its names", { x <- year_quarter_day(2019) x <- set_quarter(x, set_names(1L, "x")) expect_named(field(x, "quarter"), NULL) }) # ------------------------------------------------------------------------------ # format() test_that("default formats are correct", { expect_snapshot(format(year_quarter_day(2019))) expect_snapshot(format(year_quarter_day(2019, 1))) expect_snapshot(format(year_quarter_day(2019, 1, 1, 1))) expect_snapshot(format(year_quarter_day(2019, 1, 1, 1, 2, 3, 50, subsecond_precision = "microsecond"))) }) # ------------------------------------------------------------------------------ # as.character() test_that("as.character() works", { x <- year_quarter_day(2019, 1) y <- year_quarter_day(2019, 1, 2) expect_identical(as.character(x), format(x)) expect_identical(as.character(y), format(y)) }) # ------------------------------------------------------------------------------ # calendar_narrow() test_that("can narrow to week", { x_expect <- year_quarter_day(2019, 2) x <- set_day(x_expect, 1) expect_identical(calendar_narrow(x, "quarter"), x_expect) expect_identical(calendar_narrow(x_expect, "quarter"), x_expect) }) test_that("can narrow to day", { x_expect <- year_quarter_day(2019, 2, 3) x <- set_hour(x_expect, 5) expect_identical(calendar_narrow(x, "day"), x_expect) expect_identical(calendar_narrow(x_expect, "day"), x_expect) }) # ------------------------------------------------------------------------------ # calendar_widen() test_that("can widen to quarter", { x <- year_quarter_day(2019) expect_identical(calendar_widen(x, "quarter"), set_quarter(x, 1)) }) test_that("can widen to day", { x <- year_quarter_day(2019) y <- year_quarter_day(2019, 02) expect_identical(calendar_widen(x, "day"), set_day(set_quarter(x, 1), 1)) expect_identical(calendar_widen(y, "day"), set_day(y, 1)) }) # ------------------------------------------------------------------------------ # calendar_start() test_that("can compute year start", { x <- year_quarter_day(2019) expect_identical(calendar_start(x, "year"), x) x <- year_quarter_day(2019, 2, 2, 2, 2, 2, 2, subsecond_precision = "millisecond") expect <- year_quarter_day(2019, 1, 1, 0, 0, 0, 0, subsecond_precision = "millisecond") expect_identical(calendar_start(x, "year"), expect) }) test_that("can compute quarter start", { x <- year_quarter_day(2019, 2) expect_identical(calendar_start(x, "quarter"), x) x <- year_quarter_day(2019, 2, 2, 2, 2, 2, 2, subsecond_precision = "millisecond") expect <- year_quarter_day(2019, 2, 1, 0, 0, 0, 0, subsecond_precision = "millisecond") expect_identical(calendar_start(x, "quarter"), expect) }) # ------------------------------------------------------------------------------ # calendar_end() test_that("can compute year end", { x <- year_quarter_day(2019) expect_identical(calendar_end(x, "year"), x) x <- year_quarter_day(2019, 2, 2, 2, 2, 2, 2, subsecond_precision = "millisecond") expect <- year_quarter_day(2019, 4, 92, 23, 59, 59, 999L, subsecond_precision = "millisecond") expect_identical(calendar_end(x, "year"), expect) }) test_that("can compute quarter end", { x <- year_quarter_day(2019, 2) expect_identical(calendar_end(x, "quarter"), x) x <- year_quarter_day(2019, 2:3, 2, 2, 2, 2, 2, subsecond_precision = "millisecond") expect <- year_quarter_day(2019, 2:3, 91:92, 23, 59, 59, 999L, subsecond_precision = "millisecond") expect_identical(calendar_end(x, "quarter"), expect) }) # ------------------------------------------------------------------------------ # calendar_leap_year() test_that("can detect leap years", { year <- 2020 for (start in as.list(clock_months)) { x <- year_quarter_day(year, start = start) expect <- start != clock_months$february expect_identical(calendar_leap_year(x), expect) } year <- 2021 for (start in as.list(clock_months)) { x <- year_quarter_day(year, start = start) expect <- start == clock_months$february expect_identical(calendar_leap_year(x), expect) } }) test_that("`NA` propagates", { expect_identical(calendar_leap_year(year_quarter_day(NA)), NA) }) # ------------------------------------------------------------------------------ # calendar_count_between() test_that("can compute year and month counts", { x <- year_quarter_day(2019, 1, 1) y <- year_quarter_day(2020, 3, 4) expect_identical(calendar_count_between(x, y, "year"), 1L) expect_identical(calendar_count_between(x, y, "quarter"), 6L) expect_identical(calendar_count_between(x, y, "quarter", n = 2), 3L) }) test_that("works with different quarter start months", { x <- year_quarter_day(2019, 1, 1, start = clock_months$march) y <- year_quarter_day(2020, 3, 4, start = clock_months$march) expect_identical(calendar_count_between(x, y, "year"), 1L) expect_identical(calendar_count_between(x, y, "quarter"), 6L) expect_identical(calendar_count_between(x, y, "quarter", n = 2), 3L) }) test_that("can't compute a unsupported count precision", { x <- year_quarter_day(2019, 1, 1) expect_snapshot((expect_error(calendar_count_between(x, x, "day")))) }) test_that("positive / negative counts are correct", { start <- year_quarter_day(1972, 03, 04) end <- year_quarter_day(1973, 03, 03) expect_identical(calendar_count_between(start, end, "year"), 0L) expect_identical(calendar_count_between(start, end, "quarter"), 3L) end <- year_quarter_day(1973, 03, 04) expect_identical(calendar_count_between(start, end, "year"), 1L) expect_identical(calendar_count_between(start, end, "quarter"), 4L) end <- year_quarter_day(1973, 03, 05) expect_identical(calendar_count_between(start, end, "year"), 1L) expect_identical(calendar_count_between(start, end, "quarter"), 4L) end <- year_quarter_day(1971, 03, 03) expect_identical(calendar_count_between(start, end, "year"), -1L) expect_identical(calendar_count_between(start, end, "quarter"), -4L) end <- year_quarter_day(1971, 03, 04) expect_identical(calendar_count_between(start, end, "year"), -1L) expect_identical(calendar_count_between(start, end, "quarter"), -4L) end <- year_quarter_day(1971, 03, 05) expect_identical(calendar_count_between(start, end, "year"), 0L) expect_identical(calendar_count_between(start, end, "quarter"), -3L) }) # ------------------------------------------------------------------------------ # seq() test_that("only granular precisions are allowed", { expect_snapshot(error = TRUE, seq(year_quarter_day(2019, 1, 1), by = 1, length.out = 2)) }) test_that("seq(to, by) works", { expect_identical(seq(year_quarter_day(2019, 1), to = year_quarter_day(2020, 2), by = 2), year_quarter_day(c(2019, 2019, 2020), c(1, 3, 1))) expect_identical(seq(year_quarter_day(2019, 1), to = year_quarter_day(2020, 1), by = 2), year_quarter_day(c(2019, 2019, 2020), c(1, 3, 1))) }) test_that("seq(to, length.out) works", { expect_identical(seq(year_quarter_day(2019, 1), to = year_quarter_day(2020, 2), length.out = 2), year_quarter_day(c(2019, 2020), c(1, 2))) expect_identical(seq(year_quarter_day(2019, 1), to = year_quarter_day(2020, 2), length.out = 6), year_quarter_day(2019, 1) + 0:5) expect_identical(seq(year_quarter_day(2019, 1), to = year_quarter_day(2020, 2), along.with = 1:2), year_quarter_day(c(2019, 2020), c(1, 2))) }) test_that("seq(by, length.out) works", { expect_identical(seq(year_quarter_day(2019, 1), by = 2, length.out = 3), year_quarter_day(2019, 1) + c(0, 2, 4)) expect_identical(seq(year_quarter_day(2019, 1), by = -2, length.out = 3), year_quarter_day(2019, 1) + c(0, -2, -4)) expect_identical(seq(year_quarter_day(2019, 1), by = 2, along.with = 1:3), year_quarter_day(2019, 1) + c(0, 2, 4)) }) # ------------------------------------------------------------------------------ # miscellaneous test_that("can roundtrip to naive-time with any `start`", { x <- seq( as_naive_time(year_month_day(-9999, 1, 1)), as_naive_time(year_month_day(9999, 12, 31)), by = 1 ) for (start in seq_len(12)) { expect_identical(x, as_naive_time(as_year_quarter_day(x, start = start))) } }) test_that("can generate correct last days of the quarter with any `start`", { start <- 1L expect_identical( as.Date(year_quarter_day(2019, 1:4, "last", start = 1)), as.Date(year_month_day(2019, c(3, 6, 9, 12), "last")) ) expect_identical( as.Date(year_quarter_day(2019, 1:4, "last", start = 2)), as.Date(year_month_day(c(2018, 2018, 2018, 2019), c(4, 7, 10, 1), "last")) ) expect_identical( as.Date(year_quarter_day(2019, 1:4, "last", start = 3)), as.Date(year_month_day(c(2018, 2018, 2018, 2019), c(5, 8, 11, 2), "last")) ) # ..., 4, 5, 6, 7, 8, 9, ... expect_identical( as.Date(year_quarter_day(2019, 1:4, "last", start = 10)), as.Date(year_month_day(c(2018, 2019, 2019, 2019), c(12, 3, 6, 9), "last")) ) expect_identical( as.Date(year_quarter_day(2019, 1:4, "last", start = 11)), as.Date(year_month_day(2019, c(1, 4, 7, 10), "last")) ) expect_identical( as.Date(year_quarter_day(2019, 1:4, "last", start = 12)), as.Date(year_month_day(2019, c(2, 5, 8, 11), "last")) ) # Leap year! expect_identical( as.Date(year_quarter_day(2020, 1:4, "last", start = 1)), as.Date(year_month_day(2020, c(3, 6, 9, 12), "last")) ) expect_identical( as.Date(year_quarter_day(2020, 1:4, "last", start = 2)), as.Date(year_month_day(c(2019, 2019, 2019, 2020), c(4, 7, 10, 1), "last")) ) expect_identical( as.Date(year_quarter_day(2020, 1:4, "last", start = 3)), as.Date(year_month_day(c(2019, 2019, 2019, 2020), c(5, 8, 11, 2), "last")) ) }) # ------------------------------------------------------------------------------ # invalid_detect() test_that("`invalid_detect()` works", { # Not possible to be invalid x <- year_quarter_day(2019:2020, 2:3) expect_identical(invalid_detect(x), c(FALSE, FALSE)) # Now possible x <- year_quarter_day(2019, 1, c(1, 90, 91, NA)) expect_identical(invalid_detect(x), c(FALSE, FALSE, TRUE, FALSE)) # Possible after that too x <- year_quarter_day(2019, 1, c(1, 90, 91, NA), 1) expect_identical(invalid_detect(x), c(FALSE, FALSE, TRUE, FALSE)) }) # ------------------------------------------------------------------------------ # invalid_any() test_that("`invalid_any()` works", { # Not possible to be invalid x <- year_quarter_day(2019:2020, 2:3) expect_false(invalid_any(x)) # Now possible x <- year_quarter_day(2019, 1, c(1, 90, 91, NA)) expect_true(invalid_any(x)) # Possible after that too x <- year_quarter_day(2019, 1, c(1, 90, 91, NA), 1) expect_true(invalid_any(x)) }) # ------------------------------------------------------------------------------ # invalid_count() test_that("`invalid_count()` works", { # Not possible to be invalid x <- year_quarter_day(2019:2020, 2:3) expect_identical(invalid_count(x), 0L) # Now possible x <- year_quarter_day(2019, 1, c(1, 90, 91, NA)) expect_identical(invalid_count(x), 1L) # Possible after that too x <- year_quarter_day(2019, 1, c(1, 90, 91, NA), 1) expect_identical(invalid_count(x), 1L) }) # ------------------------------------------------------------------------------ # invalid_resolve() test_that("strict mode can be activated", { local_options(clock.strict = TRUE) expect_snapshot(error = TRUE, invalid_resolve(year_quarter_day(2019, 1, 1))) }) test_that("can resolve correctly", { x <- year_quarter_day(2019, 1, 92, 2, 3, 4, 5, subsecond_precision = "millisecond") expect_identical( invalid_resolve(x, invalid = "previous"), year_quarter_day(2019, 1, 90, 23, 59, 59, 999, subsecond_precision = "millisecond") ) expect_identical( invalid_resolve(x, invalid = "previous-day"), year_quarter_day(2019, 1, 90, 2, 3, 4, 5, subsecond_precision = "millisecond") ) expect_identical( invalid_resolve(x, invalid = "next"), year_quarter_day(2019, 2, 1, 0, 0, 0, 0, subsecond_precision = "millisecond") ) expect_identical( invalid_resolve(x, invalid = "next-day"), year_quarter_day(2019, 2, 1, 2, 3, 4, 5, subsecond_precision = "millisecond") ) expect_identical( invalid_resolve(x, invalid = "overflow"), year_quarter_day(2019, 2, 2, 0, 0, 0, 0, subsecond_precision = "millisecond") ) expect_identical( invalid_resolve(x, invalid = "overflow-day"), year_quarter_day(2019, 2, 2, 2, 3, 4, 5, subsecond_precision = "millisecond") ) expect_identical( invalid_resolve(x, invalid = "NA"), year_quarter_day(NA, NA, NA, NA, NA, NA, NA, subsecond_precision = "millisecond") ) }) test_that("throws known classed error", { expect_snapshot(error = TRUE, invalid_resolve(year_quarter_day(2019, 1, 91))) expect_error(invalid_resolve(year_quarter_day(2019, 1, 91)), class = "clock_error_invalid_date") }) test_that("works with always valid precisions", { x <- year_quarter_day(2019:2020, 1:2) expect_identical(invalid_resolve(x), x) }) # ------------------------------------------------------------------------------ # vec_math() test_that("is.nan() works", { x <- year_quarter_day(c(2019, NA)) expect_identical(is.nan(x), c(FALSE, FALSE)) }) test_that("is.finite() works", { x <- year_quarter_day(c(2019, NA)) expect_identical(is.finite(x), c(TRUE, FALSE)) }) test_that("is.infinite() works", { x <- year_quarter_day(c(2019, NA)) expect_identical(is.infinite(x), c(FALSE, FALSE)) }) # ------------------------------------------------------------------------------ # clock_minimum() / clock_maximum() test_that("minimums are right", { expect_snapshot({ clock_minimum(year_quarter_day(1)) clock_minimum(year_quarter_day(1, 1)) clock_minimum(year_quarter_day(1, 1, 1)) clock_minimum(year_quarter_day(1, 1, 1, 1)) clock_minimum(year_quarter_day(1, 1, 1, 1, 1)) clock_minimum(year_quarter_day(1, 1, 1, 1, 1, 1)) clock_minimum(year_quarter_day(1, 1, 1, 1, 1, 1, 1, subsecond_precision = "millisecond")) clock_minimum(year_quarter_day(1, 1, 1, 1, 1, 1, 1, subsecond_precision = "microsecond")) clock_minimum(year_quarter_day(1, 1, 1, 1, 1, 1, 1, subsecond_precision = "nanosecond")) }) }) test_that("maximums are right", { expect_snapshot({ clock_maximum(year_quarter_day(1)) clock_maximum(year_quarter_day(1, 1)) clock_maximum(year_quarter_day(1, 1, 1)) clock_maximum(year_quarter_day(1, 1, 1, 1)) clock_maximum(year_quarter_day(1, 1, 1, 1, 1)) clock_maximum(year_quarter_day(1, 1, 1, 1, 1, 1)) clock_maximum(year_quarter_day(1, 1, 1, 1, 1, 1, 1, subsecond_precision = "millisecond")) clock_maximum(year_quarter_day(1, 1, 1, 1, 1, 1, 1, subsecond_precision = "microsecond")) clock_maximum(year_quarter_day(1, 1, 1, 1, 1, 1, 1, subsecond_precision = "nanosecond")) }) }) test_that("minimums and maximums respect `start`", { expect_snapshot({ clock_minimum(year_quarter_day(1, start = clock_months$february)) clock_maximum(year_quarter_day(1, start = clock_months$february)) }) }) # ------------------------------------------------------------------------------ # min() / max() / range() test_that("min() / max() / range() works", { x <- year_quarter_day(c(1, 3, 2, 1, -1)) expect_identical(min(x), year_quarter_day(-1)) expect_identical(max(x), year_quarter_day(3)) expect_identical(range(x), year_quarter_day(c(-1, 3))) }) test_that("min() / max() / range() works with `NA`", { x <- year_quarter_day(c(1, NA, 2, 0)) expect_identical(min(x), year_quarter_day(NA)) expect_identical(max(x), year_quarter_day(NA)) expect_identical(range(x), year_quarter_day(c(NA, NA))) expect_identical(min(x, na.rm = TRUE), year_quarter_day(0)) expect_identical(max(x, na.rm = TRUE), year_quarter_day(2)) expect_identical(range(x, na.rm = TRUE), year_quarter_day(c(0, 2))) }) test_that("min() / max() / range() works when empty", { x <- year_quarter_day(integer()) expect_identical(min(x), clock_maximum(x)) expect_identical(max(x), clock_minimum(x)) expect_identical(range(x), c(clock_maximum(x), clock_minimum(x))) x <- year_quarter_day(c(NA, NA)) expect_identical(min(x, na.rm = TRUE), clock_maximum(x)) expect_identical(max(x, na.rm = TRUE), clock_minimum(x)) expect_identical(range(x, na.rm = TRUE), c(clock_maximum(x), clock_minimum(x))) }) # ------------------------------------------------------------------------------ # add_*() test_that("add_years() works", { x <- year_quarter_day(2019, 1, 2, 3:4) expect_identical( add_years(x, 1:2), year_quarter_day(c(2020, 2021), 1, 2, 3:4) ) expect_identical( add_years(x, NA), vec_init(x, 2L) ) }) test_that("add_quarters() works", { x <- year_quarter_day(2019, 1, 2, 3:4) expect_identical( add_quarters(x, 1:2), year_quarter_day(2019, c(2, 3), 2, 3:4) ) expect_identical( add_quarters(x, NA), vec_init(x, 2L) ) }) test_that("add_*() respect recycling rules", { expect_length(add_years(year_quarter_day(1), 1:2), 2L) expect_length(add_years(year_quarter_day(1:2), 1), 2L) expect_length(add_years(year_quarter_day(1), integer()), 0L) expect_length(add_years(year_quarter_day(integer()), 1), 0L) expect_snapshot(error = TRUE, { add_years(year_quarter_day(1:2), 1:3) }) }) test_that("add_*() retains names", { x <- set_names(year_quarter_day(1), "x") y <- year_quarter_day(1) n <- set_names(1, "n") expect_named(add_years(x, n), "x") expect_named(add_years(y, n), "n") }) test_that("`start` value is retained", { expect_identical(year_quarter_day(2019, 1, 1) + duration_quarters(1), year_quarter_day(2019, 2, 1)) expect_identical(year_quarter_day(2019, 1, 1) + duration_quarters(5), year_quarter_day(2020, 2, 1)) # Ensure that the `start` is retained expect_identical( year_quarter_day(2019, 1, 1, start = 2) + duration_quarters(5), year_quarter_day(2020, 2, 1, start = 2) ) }) clock/tests/testthat/test-gregorian-year-day.R0000644000176200001440000005556414427233200021151 0ustar liggesusers# ------------------------------------------------------------------------------ # year_day() test_that("helper can create different precisions", { x <- year_day(2019, 1:2) expect_identical(get_year(x), c(2019L, 2019L)) expect_identical(get_day(x), 1:2) x <- year_day(2019) expect_identical(get_year(x), 2019L) }) test_that("can create subsecond precision calendars", { x <- year_day(2019, 1, 0, 0, 0, 1, subsecond_precision = "millisecond") expect_identical(get_millisecond(x), 1L) x <- year_day(2019, 1, 0, 0, 0, 1, subsecond_precision = "microsecond") expect_identical(get_microsecond(x), 1L) x <- year_day(2019, 1, 0, 0, 0, 1, subsecond_precision = "nanosecond") expect_identical(get_nanosecond(x), 1L) }) test_that("validates value ranges", { expect_snapshot(error = TRUE, year_day(50000)) expect_snapshot(error = TRUE, year_day(2020, 367)) expect_snapshot(error = TRUE, year_day(2020, 1, 24)) expect_snapshot(error = TRUE, year_day(2020, 1, 1, 60)) expect_snapshot(error = TRUE, year_day(2020, 1, 1, 1, 60)) expect_snapshot(error = TRUE, year_day(2020, 1, 1, 1, 1, 1000, subsecond_precision = "millisecond")) expect_snapshot(error = TRUE, year_day(2020, 1, 1, 1, 1, 1000000, subsecond_precision = "microsecond")) expect_snapshot(error = TRUE, year_day(2020, 1, 1, 1, 1, 1000000000, subsecond_precision = "nanosecond")) }) test_that("can create a date at the boundary", { x <- year_day(32767, 365) expect_identical(get_year(x), 32767L) x <- year_day(-32767, 1) expect_identical(get_year(x), -32767L) }) test_that("can get the last day of the year", { x <- year_day(2019:2020, "last") expect_identical(get_day(x), c(365L, 366L)) }) test_that("`NA` propagates through 'last'", { x <- year_day(2019, c(1, NA)) x <- set_day(x, "last") expect_identical(get_day(x), c(365L, NA)) }) test_that("ignores values past first `NULL`", { expect_identical(year_day(2019, hour = 2), year_day(2019)) }) test_that("NA values propagate", { x <- year_day(2019, 1:3, c(NA, 2, 3), c(3, 4, NA)) expect_identical(is.na(x), c(TRUE, FALSE, TRUE)) }) test_that("names of `year` are not retained", { expect_named(year_day(c(x = 1)), NULL) }) # ------------------------------------------------------------------------------ # vec_ptype() test_that("ptype is correct", { base <- year_day(1) ptype <- year_day(integer()) for (precision in precision_names()) { if (precision == "quarter" || precision == "month" || precision == "week") { next } x <- calendar_widen(base, precision) expect <- calendar_widen(ptype, precision) expect_identical(vec_ptype(x), expect) } }) # ------------------------------------------------------------------------------ # vec_proxy() / vec_restore() test_that("proxy is a data frame", { expect_identical(vec_proxy(year_day(2019)), data_frame(year = 2019L)) expect_identical(vec_proxy(year_day(2019, 1)), data_frame(year = 2019L, day = 1L)) }) test_that("proxy has names on `year`", { x <- set_names(year_day(2019, 1), "nm") year <- vec_proxy(x)$year expect_named(year, "nm") }) test_that("restore works", { to <- year_day(2019, 1:5) proxy <- vec_slice(vec_proxy(to), 1:2) expect_identical(vec_restore(proxy, to), year_day(2019, 1:2)) }) # ------------------------------------------------------------------------------ # vec_ptype_full() test_that("full ptype is correct", { expect_snapshot_output(vec_ptype_full(year_day(2019))) expect_snapshot_output(vec_ptype_full(year_day(2019, 1, 1))) expect_snapshot_output(vec_ptype_full(year_day(2019, 1, 1, 1, 1, 1, subsecond_precision = "nanosecond"))) expect_snapshot_output(vec_ptype_full(year_day(2019, 366))) }) # ------------------------------------------------------------------------------ # vec_ptype_abbr() test_that("abbreviated ptype is correct", { expect_snapshot_output(vec_ptype_abbr(year_day(2019))) expect_snapshot_output(vec_ptype_abbr(year_day(2019, 1, 1))) expect_snapshot_output(vec_ptype_abbr(year_day(2019, 1, 1, 1, 1, 1, subsecond_precision = "nanosecond"))) expect_snapshot_output(vec_ptype_abbr(year_day(2019, 366))) }) # ------------------------------------------------------------------------------ # set_*() test_that("setters work", { x <- year_day(1L) x <- set_year(x, 2L) expect_identical(get_year(x), 2L) x <- set_day(x, 2L) expect_identical(get_day(x), 2L) x <- set_hour(x, 3L) expect_identical(get_hour(x), 3L) x <- set_minute(x, 4L) expect_identical(get_minute(x), 4L) x <- set_second(x, 5L) expect_identical(get_second(x), 5L) ms <- set_millisecond(x, 6L) expect_identical(get_millisecond(ms), 6L) us <- set_microsecond(x, 7L) expect_identical(get_microsecond(us), 7L) ns <- set_nanosecond(x, 8L) expect_identical(get_nanosecond(ns), 8L) }) test_that("setters propagate all missings", { x <- year_day(2019, c(1, NA, 3)) x <- set_hour(x, c(NA, 2, 4)) expect_identical(vec_detect_missing(x), c(TRUE, TRUE, FALSE)) }) test_that("setters recycling works both ways", { x <- year_day(2019) x <- set_day(x, 1:2) expect_identical(x, year_day(2019, 1:2)) x <- set_hour(x, 1) expect_identical(x, year_day(2019, 1:2, 1)) expect_snapshot(error = TRUE, { x <- year_day(1:2) y <- 1:3 set_day(x, y) }) }) test_that("setters require integer `value`", { x <- year_day(2019, 1, 2, 3, 4) expect_snapshot(error = TRUE, { set_year(x, 1.5) }) expect_snapshot(error = TRUE, { set_day(x, 1.5) }) expect_snapshot(error = TRUE, { set_hour(x, 1.5) }) expect_snapshot(error = TRUE, { set_minute(x, 1.5) }) expect_snapshot(error = TRUE, { set_second(x, 1.5) }) expect_snapshot(error = TRUE, { set_millisecond(x, 1.5) }) expect_snapshot(error = TRUE, { set_microsecond(x, 1.5) }) expect_snapshot(error = TRUE, { set_nanosecond(x, 1.5) }) }) test_that("setters check `value` range", { x <- year_day(2019, 1, 2, 3, 4) expect_snapshot(error = TRUE, { set_year(x, 100000) }) expect_snapshot(error = TRUE, { set_day(x, 367) }) expect_snapshot(error = TRUE, { set_hour(x, 24) }) expect_snapshot(error = TRUE, { set_minute(x, 60) }) expect_snapshot(error = TRUE, { set_second(x, 60) }) expect_snapshot(error = TRUE, { set_millisecond(x, -1) }) expect_snapshot(error = TRUE, { set_microsecond(x, -1) }) expect_snapshot(error = TRUE, { set_nanosecond(x, -1) }) }) test_that("setters require minimum precision", { expect_snapshot(error = TRUE, { set_hour(year_day(year = 1), 1) }) expect_snapshot(error = TRUE, { set_minute(year_day(year = 1, day = 1), 1) }) expect_snapshot(error = TRUE, { set_second(year_day(year = 1, day = 1, hour = 1), 1) }) expect_snapshot(error = TRUE, { set_millisecond(year_day(year = 1, day = 1, hour = 1, minute = 1), 1) }) expect_snapshot(error = TRUE, { set_microsecond(year_day(year = 1, day = 1, hour = 1, minute = 1), 1) }) expect_snapshot(error = TRUE, { set_nanosecond(year_day(year = 1, day = 1, hour = 1, minute = 1), 1) }) }) test_that("setters require correct subsecond precision", { expect_snapshot(error = TRUE, { set_millisecond(year_day(year = 1, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "microsecond"), 1) }) expect_snapshot(error = TRUE, { set_millisecond(year_day(year = 1, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "nanosecond"), 1) }) expect_snapshot(error = TRUE, { set_microsecond(year_day(year = 1, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "millisecond"), 1) }) expect_snapshot(error = TRUE, { set_microsecond(year_day(year = 1, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "nanosecond"), 1) }) expect_snapshot(error = TRUE, { set_nanosecond(year_day(year = 1, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "millisecond"), 1) }) expect_snapshot(error = TRUE, { set_nanosecond(year_day(year = 1, day = 3, hour = 4, minute = 5, second = 6, subsecond = 7, subsecond_precision = "microsecond"), 1) }) }) test_that("setters retain names", { x <- year_day(2019) x <- set_names(x, "foo") expect_named(set_day(x, 2), "foo") }) test_that("setting with named `value` strips its names", { x <- year_day(2019) x <- set_day(x, set_names(1L, "x")) expect_named(field(x, "day"), NULL) }) # ------------------------------------------------------------------------------ # as_year_quarter_day() test_that("invalid dates must be resolved when converting to another calendar", { expect_snapshot(error = TRUE, as_year_quarter_day(year_day(2019, 366))) }) # ------------------------------------------------------------------------------ # as_sys_time() test_that("invalid dates must be resolved when converting to a sys-time", { expect_snapshot(error = TRUE, as_sys_time(year_day(2019, 366))) }) # ------------------------------------------------------------------------------ # as_naive_time() test_that("invalid dates must be resolved when converting to a naive-time", { expect_snapshot(error = TRUE, as_naive_time(year_day(2019, 366))) }) # ------------------------------------------------------------------------------ # format() test_that("default formats are correct", { expect_snapshot(format(year_day(2019))) expect_snapshot(format(year_day(2019, 1))) expect_snapshot(format(year_day(2019, 1, 1))) expect_snapshot(format(year_day(2019, 1, 1, 2, 3, 50, subsecond_precision = "microsecond"))) }) # ------------------------------------------------------------------------------ # as.character() test_that("as.character() works", { x <- year_day(2019, 1) y <- year_day(2019, 1, 2) expect_identical(as.character(x), format(x)) expect_identical(as.character(y), format(y)) }) # ------------------------------------------------------------------------------ # calendar_group() test_that("can group by day of the year", { x <- year_day(2019, 1, c(3, 4)) expect_identical(calendar_group(x, "day"), calendar_narrow(x, "day")) }) test_that("can group by multiple days, resetting at the new year", { x <- c(year_day(2019, 364:365), year_day(2020, 1:3)) expect_identical( calendar_group(x, "day", n = 2), c(year_day(2019, c(363, 365)), year_day(2020, c(1, 1, 3))) ) }) # ------------------------------------------------------------------------------ # calendar_narrow() test_that("can narrow to year", { x_expect <- year_day(2019) x <- set_day(x_expect, 1) expect_identical(calendar_narrow(x, "year"), x_expect) expect_identical(calendar_narrow(x_expect, "year"), x_expect) }) test_that("can narrow to day", { x_expect <- year_day(2019, 2) x <- set_hour(x_expect, 5) expect_identical(calendar_narrow(x, "day"), x_expect) expect_identical(calendar_narrow(x_expect, "day"), x_expect) }) test_that("can narrow to hour", { x_expect <- year_day(2019, 3, 4) x <- set_minute(x_expect, 5) expect_identical(calendar_narrow(x, "hour"), x_expect) expect_identical(calendar_narrow(x_expect, "hour"), x_expect) }) test_that("can narrow to minute", { x_expect <- year_day(2019, 3, 4, 5) x <- set_second(x_expect, 6) expect_identical(calendar_narrow(x, "minute"), x_expect) expect_identical(calendar_narrow(x_expect, "minute"), x_expect) }) test_that("can narrow to second", { expect <- year_day(2019, 3, 4, 5, 6) x <- set_millisecond(expect, 7) y <- set_nanosecond(expect, 7) expect_identical(calendar_narrow(x, "second"), expect) expect_identical(calendar_narrow(y, "second"), expect) expect_identical(calendar_narrow(expect, "second"), expect) }) # ------------------------------------------------------------------------------ # calendar_widen() test_that("can widen to year", { x <- year_day(2019) expect_identical(calendar_widen(x, "year"), x) }) test_that("can widen to day", { x <- year_day(2019) expect_identical(calendar_widen(x, "day"), set_day(x, 1)) }) test_that("can widen to hour", { x <- year_day(2019) y <- year_day(2019, 02) expect_identical(calendar_widen(x, "hour"), set_hour(set_day(x, 1), 0)) expect_identical(calendar_widen(y, "hour"), set_hour(y, 0)) }) test_that("can widen to minute", { x <- year_day(2019) y <- year_day(2019, 02, 02) x_expect <- year_day(2019, 1, 0, 0) y_expect <- set_minute(y, 0) expect_identical(calendar_widen(x, "minute"), x_expect) expect_identical(calendar_widen(y, "minute"), y_expect) }) test_that("can widen to second", { x <- year_day(2019) y <- year_day(2019, 02, 02, 02) x_expect <- year_day(2019, 1, 0, 0, 0) y_expect <- set_second(y, 0) expect_identical(calendar_widen(x, "second"), x_expect) expect_identical(calendar_widen(y, "second"), y_expect) }) test_that("can widen to subsecond precision", { x <- year_day(2019) y <- year_day(2019, 02, 02, 02, 02) x_expect <- year_day(2019, 1, 0, 0, 0, 0, subsecond_precision = "microsecond") y_expect <- set_nanosecond(y, 0) expect_identical(calendar_widen(x, "microsecond"), x_expect) expect_identical(calendar_widen(y, "nanosecond"), y_expect) }) # ------------------------------------------------------------------------------ # calendar_start() test_that("can compute year start", { x <- year_day(2019) expect_identical(calendar_start(x, "year"), x) x <- year_day(2019, 2, 2, 2, 2, 2, subsecond_precision = "millisecond") expect <- year_day(2019, 1, 0, 0, 0, 0, subsecond_precision = "millisecond") expect_identical(calendar_start(x, "year"), expect) }) # ------------------------------------------------------------------------------ # calendar_end() test_that("can compute year end", { x <- year_day(2019) expect_identical(calendar_end(x, "year"), x) x <- year_day(2019:2020, 2, 2, 2, 2, 2, subsecond_precision = "millisecond") expect <- year_day(2019:2020, 365:366, 23, 59, 59, 999L, subsecond_precision = "millisecond") expect_identical(calendar_end(x, "year"), expect) }) # ------------------------------------------------------------------------------ # calendar_leap_year() test_that("can detect leap years", { x <- year_day(c(2019, 2020, NA), 1) expect_identical(calendar_leap_year(x), c(FALSE, TRUE, NA)) }) # ------------------------------------------------------------------------------ # calendar_count_between() test_that("can compute year counts", { x <- year_day(2019, 1) y <- year_day(2020, 3) expect_identical(calendar_count_between(x, y, "year"), 1L) }) test_that("can't compute a unsupported difference precision", { x <- year_day(2019, 1) expect_snapshot((expect_error(calendar_count_between(x, x, "day")))) }) test_that("positive / negative differences are correct", { start <- year_day(1972, 04) end <- year_day(1973, 03) expect_identical(calendar_count_between(start, end, "year"), 0L) end <- year_day(1973, 04) expect_identical(calendar_count_between(start, end, "year"), 1L) end <- year_day(1973, 05) expect_identical(calendar_count_between(start, end, "year"), 1L) end <- year_day(1971, 03) expect_identical(calendar_count_between(start, end, "year"), -1L) end <- year_day(1971, 04) expect_identical(calendar_count_between(start, end, "year"), -1L) end <- year_day(1971, 05) expect_identical(calendar_count_between(start, end, "year"), 0L) }) # ------------------------------------------------------------------------------ # seq() test_that("only granular precisions are allowed", { expect_snapshot(error = TRUE, seq(year_day(2019, 1), by = 1, length.out = 2)) }) test_that("seq(to, by) works", { expect_identical(seq(year_day(2019), to = year_day(2024), by = 2), year_day(c(2019, 2021, 2023))) expect_identical(seq(year_day(2019), to = year_day(2023), by = 2), year_day(c(2019, 2021, 2023))) }) test_that("seq(to, length.out) works", { expect_identical(seq(year_day(2019), to = year_day(2024), length.out = 2), year_day(c(2019, 2024))) expect_identical(seq(year_day(2019), to = year_day(2024), length.out = 6), year_day(2019:2024)) expect_identical(seq(year_day(2019), to = year_day(2024), along.with = 1:2), year_day(c(2019, 2024))) }) test_that("seq(by, length.out) works", { expect_identical(seq(year_day(2019), by = 2, length.out = 3), year_day(c(2019, 2021, 2023))) expect_identical(seq(year_day(2019), by = 2, along.with = 1:3), year_day(c(2019, 2021, 2023))) }) # ------------------------------------------------------------------------------ # miscellaneous test_that("can roundtrip to naive-time", { x <- seq( as_naive_time(year_month_day(-9999, 1, 1)), as_naive_time(year_month_day(9999, 12, 31)), by = 1 ) expect_identical(x, as_naive_time(as_year_day(x))) }) # ------------------------------------------------------------------------------ # invalid_detect() test_that("`invalid_detect()` works", { # Not possible to be invalid x <- year_day(2019:2020) expect_identical(invalid_detect(x), c(FALSE, FALSE)) # Now possible x <- year_day(2019, c(1, 365, 366, NA)) expect_identical(invalid_detect(x), c(FALSE, FALSE, TRUE, FALSE)) # Possible after that too x <- year_day(2019, c(1, 365, 366, NA), 3) expect_identical(invalid_detect(x), c(FALSE, FALSE, TRUE, FALSE)) }) # ------------------------------------------------------------------------------ # invalid_any() test_that("`invalid_any()` works", { # Not possible to be invalid x <- year_day(2019:2020) expect_false(invalid_any(x)) # Now possible x <- year_day(2019, c(1, 365, 366, NA)) expect_true(invalid_any(x)) # Possible after that too x <- year_day(2019, c(1, 365, 366, NA), 3) expect_true(invalid_any(x)) }) # ------------------------------------------------------------------------------ # invalid_count() test_that("`invalid_count()` works", { # Not possible to be invalid x <- year_day(2019:2020) expect_identical(invalid_count(x), 0L) # Now possible x <- year_day(2019, c(1, 365, 366, NA)) expect_identical(invalid_count(x), 1L) # Possible after that too x <- year_day(2019, c(1, 365, 366, NA), 3) expect_identical(invalid_count(x), 1L) }) # ------------------------------------------------------------------------------ # invalid_resolve() test_that("strict mode can be activated", { local_options(clock.strict = TRUE) expect_snapshot(error = TRUE, invalid_resolve(year_day(2019, 1))) }) test_that("can resolve correctly", { x <- year_day(2019, 366, 2, 3, 4, 5, subsecond_precision = "millisecond") expect_identical( invalid_resolve(x, invalid = "previous"), year_day(2019, 365, 23, 59, 59, 999, subsecond_precision = "millisecond") ) expect_identical( invalid_resolve(x, invalid = "previous-day"), year_day(2019, 365, 2, 3, 4, 5, subsecond_precision = "millisecond") ) expect_identical( invalid_resolve(x, invalid = "next"), year_day(2020, 1, 0, 0, 0, 0, subsecond_precision = "millisecond") ) expect_identical( invalid_resolve(x, invalid = "next-day"), year_day(2020, 1, 2, 3, 4, 5, subsecond_precision = "millisecond") ) expect_identical( invalid_resolve(x, invalid = "overflow"), year_day(2020, 1, 0, 0, 0, 0, subsecond_precision = "millisecond") ) expect_identical( invalid_resolve(x, invalid = "overflow-day"), year_day(2020, 1, 2, 3, 4, 5, subsecond_precision = "millisecond") ) expect_identical( invalid_resolve(x, invalid = "NA"), year_day(NA, NA, NA, NA, NA, NA, subsecond_precision = "millisecond") ) }) test_that("throws known classed error", { expect_snapshot(error = TRUE, invalid_resolve(year_day(2019, 366))) expect_error(invalid_resolve(year_day(2019, 366)), class = "clock_error_invalid_date") }) test_that("works with always valid precisions", { x <- year_day(2019:2020) expect_identical(invalid_resolve(x), x) }) # ------------------------------------------------------------------------------ # vec_math() test_that("is.nan() works", { x <- year_day(c(2019, NA)) expect_identical(is.nan(x), c(FALSE, FALSE)) }) test_that("is.finite() works", { x <- year_day(c(2019, NA)) expect_identical(is.finite(x), c(TRUE, FALSE)) }) test_that("is.infinite() works", { x <- year_day(c(2019, NA)) expect_identical(is.infinite(x), c(FALSE, FALSE)) }) # ------------------------------------------------------------------------------ # clock_minimum() / clock_maximum() test_that("minimums are right", { expect_snapshot({ clock_minimum(clock_empty_year_day_year) clock_minimum(clock_empty_year_day_day) clock_minimum(clock_empty_year_day_hour) clock_minimum(clock_empty_year_day_minute) clock_minimum(clock_empty_year_day_second) clock_minimum(clock_empty_year_day_millisecond) clock_minimum(clock_empty_year_day_microsecond) clock_minimum(clock_empty_year_day_nanosecond) }) }) test_that("maximums are right", { expect_snapshot({ clock_maximum(clock_empty_year_day_year) clock_maximum(clock_empty_year_day_day) clock_maximum(clock_empty_year_day_hour) clock_maximum(clock_empty_year_day_minute) clock_maximum(clock_empty_year_day_second) clock_maximum(clock_empty_year_day_millisecond) clock_maximum(clock_empty_year_day_microsecond) clock_maximum(clock_empty_year_day_nanosecond) }) }) # ------------------------------------------------------------------------------ # min() / max() / range() test_that("min() / max() / range() works", { x <- year_day(c(1, 3, 2, 1, -1)) expect_identical(min(x), year_day(-1)) expect_identical(max(x), year_day(3)) expect_identical(range(x), year_day(c(-1, 3))) }) test_that("min() / max() / range() works with `NA`", { x <- year_day(c(1, NA, 2, 0)) expect_identical(min(x), year_day(NA)) expect_identical(max(x), year_day(NA)) expect_identical(range(x), year_day(c(NA, NA))) expect_identical(min(x, na.rm = TRUE), year_day(0)) expect_identical(max(x, na.rm = TRUE), year_day(2)) expect_identical(range(x, na.rm = TRUE), year_day(c(0, 2))) }) test_that("min() / max() / range() works when empty", { x <- year_day(integer()) expect_identical(min(x), clock_maximum(x)) expect_identical(max(x), clock_minimum(x)) expect_identical(range(x), c(clock_maximum(x), clock_minimum(x))) x <- year_day(c(NA, NA)) expect_identical(min(x, na.rm = TRUE), clock_maximum(x)) expect_identical(max(x, na.rm = TRUE), clock_minimum(x)) expect_identical(range(x, na.rm = TRUE), c(clock_maximum(x), clock_minimum(x))) }) # ------------------------------------------------------------------------------ # add_*() test_that("add_years() works", { x <- year_day(2019, 1, 2, 3:4) expect_identical( add_years(x, 1:2), year_day(c(2020, 2021), 1, 2, 3:4) ) expect_identical( add_years(x, NA), vec_init(x, 2L) ) }) test_that("add_*() respect recycling rules", { expect_length(add_years(year_day(1), 1:2), 2L) expect_length(add_years(year_day(1:2), 1), 2L) expect_length(add_years(year_day(1), integer()), 0L) expect_length(add_years(year_day(integer()), 1), 0L) expect_snapshot(error = TRUE, { add_years(year_day(1:2), 1:3) }) }) test_that("add_*() retains names", { x <- set_names(year_day(1), "x") y <- year_day(1) n <- set_names(1, "n") expect_named(add_years(x, n), "x") expect_named(add_years(y, n), "n") }) clock/tests/testthat/helper-format.R0000644000176200001440000000107414176022242017241 0ustar liggesuserstest_all_formats <- function(zone = TRUE) { out <- c( "C: %C", "y: %y", "Y: %Y", "b: %b", "h: %h", "B: %B", "m: %m", "d: %d", "a: %a", "A: %A", "w: %w", "g: %g", "G: %G", "V: %V", "u: %u", "U: %U", "W: %W", "j: %j", "D: %D", "x: %x", "F: %F", "H: %H", "I: %I", "M: %M", "S: %S", "p: %p", "R: %R", "T: %T", "X: %X", "r: %r", "c: %c", "%: %%" ) if (!zone) { return(out) } c( out, "z: %z", "Ez: %Ez", "Z: %Z" ) } clock/tests/testthat/test-getters.R0000644000176200001440000000150414423730227017130 0ustar liggesusers# ------------------------------------------------------------------------------ # get_*() test_that("getters throw default error", { x <- 1 expect_snapshot(error = TRUE, { get_year(x) }) expect_snapshot(error = TRUE, { get_quarter(x) }) expect_snapshot(error = TRUE, { get_month(x) }) expect_snapshot(error = TRUE, { get_week(x) }) expect_snapshot(error = TRUE, { get_day(x) }) expect_snapshot(error = TRUE, { get_hour(x) }) expect_snapshot(error = TRUE, { get_minute(x) }) expect_snapshot(error = TRUE, { get_second(x) }) expect_snapshot(error = TRUE, { get_millisecond(x) }) expect_snapshot(error = TRUE, { get_microsecond(x) }) expect_snapshot(error = TRUE, { get_nanosecond(x) }) expect_snapshot(error = TRUE, { get_index(x) }) }) clock/tests/testthat/test-invalid.R0000644000176200001440000000064714423730227017110 0ustar liggesusers# ------------------------------------------------------------------------------ # invalid_remove() test_that("can remove invalid dates from calendar types", { x <- year_month_day(2020, 2, 28:30) expect_identical(invalid_remove(x), x[1:2]) x <- year_day(2019, 365:366) expect_identical(invalid_remove(x), x[1L]) }) test_that("errors on unsupported types", { expect_snapshot(error = TRUE, invalid_remove(1)) }) clock/tests/testthat/helper-os.R0000644000176200001440000000104614424761554016405 0ustar liggesusersskip_if_not_on_os <- function(os) { os <- match.arg(os, choices = c("windows", "mac", "linux", "solaris")) system <- system_os() message <- switch( os, windows = if (system != "windows") "Not on Windows", mac = if (system != "darwin") "Not on Mac", linux = if (system != "linux") "Not on Linux", solaris = if (system != "sunos") "Not on Solaris" ) if (is.null(message)) { invisible(TRUE) } else { skip(message) } } # `testthat:::system_os()` system_os <- function() { tolower(Sys.info()[["sysname"]]) } clock/tests/testthat.R0000644000176200001440000000006613766750142014507 0ustar liggesuserslibrary(testthat) library(clock) test_check("clock") clock/src/0000755000176200001440000000000014430471265012142 5ustar liggesusersclock/src/parse.h0000644000176200001440000015420014136243763013432 0ustar liggesusers#ifndef CLOCK_PARSE_H #define CLOCK_PARSE_H #include "clock.h" // ----------------------------------------------------------------------------- namespace rclock { /* * Custom version of `read_long_double()` that allows for supplying the * `decimal_mark` directly. */ template long double read_seconds(std::basic_istream& is, const CharT& decimal_mark, unsigned m = 1, unsigned M = 10) { unsigned count = 0; unsigned fcount = 0; unsigned long long i = 0; unsigned long long f = 0; bool parsing_fraction = false; auto decimal_point = Traits::to_int_type(decimal_mark); while (true) { auto ic = is.peek(); if (Traits::eq_int_type(ic, Traits::eof())) break; if (Traits::eq_int_type(ic, decimal_point)) { decimal_point = Traits::eof(); parsing_fraction = true; } else { auto c = static_cast(Traits::to_char_type(ic)); if (!('0' <= c && c <= '9')) break; if (!parsing_fraction) { i = 10*i + static_cast(c - '0'); } else { f = 10*f + static_cast(c - '0'); ++fcount; } } (void)is.get(); if (++count == M) break; } if (count < m) { is.setstate(std::ios::failbit); return 0; } return static_cast(i) + static_cast(f)/std::pow(10.L, fcount); } // Takes the `read()` variant for rld, removes the Args..., and // uses `read_seconds()` template void read(std::basic_istream& is, const CharT& decimal_mark, date::detail::rld a0) { auto x = read_seconds(is, decimal_mark, a0.m, a0.M); if (is.fail()) return; a0.i = x; } } // namespace rclock // ----------------------------------------------------------------------------- namespace rclock { template > std::basic_istream& from_stream(std::basic_istream& is, const CharT* fmt, const std::pair& month_names_pair, const std::pair& weekday_names_pair, const std::pair& ampm_names_pair, const CharT& decimal_mark, date::fields& fds, std::basic_string* abbrev, std::chrono::minutes* offset) { using namespace date; using std::numeric_limits; using std::ios; using std::chrono::duration; using std::chrono::duration_cast; using std::chrono::seconds; using std::chrono::minutes; using std::chrono::hours; using date::detail::round_i; typename std::basic_istream::sentry ok{is, true}; if (ok) { date::detail::save_istream ss(is); is.fill(' '); is.flags(std::ios::skipws | std::ios::dec); is.width(0); const CharT* command = nullptr; auto modified = CharT{}; auto width = -1; CONSTDATA int not_a_year = numeric_limits::min(); CONSTDATA int not_a_2digit_year = 100; CONSTDATA int not_a_century = not_a_year / 100; CONSTDATA int not_a_month = 0; CONSTDATA int not_a_day = 0; CONSTDATA int not_a_hour = numeric_limits::min(); CONSTDATA int not_a_hour_12_value = 0; CONSTDATA int not_a_minute = not_a_hour; CONSTDATA Duration not_a_second = Duration::min(); CONSTDATA int not_a_doy = -1; CONSTDATA int not_a_weekday = 8; CONSTDATA int not_a_week_num = 100; CONSTDATA int not_a_ampm = -1; CONSTDATA minutes not_a_offset = minutes::min(); int Y = not_a_year; // c, F, Y * int y = not_a_2digit_year; // D, x, y * int g = not_a_2digit_year; // g * int G = not_a_year; // G * int C = not_a_century; // C * int m = not_a_month; // b, B, h, m, c, D, F, x * int d = not_a_day; // c, d, D, e, F, x * int j = not_a_doy; // j * int wd = not_a_weekday; // a, A, u, w * int H = not_a_hour; // c, H, R, T, X * int I = not_a_hour_12_value; // I, r * int p = not_a_ampm; // p, r * int M = not_a_minute; // c, M, r, R, T, X * Duration s = not_a_second; // c, r, S, T, X * int U = not_a_week_num; // U * int V = not_a_week_num; // V * int W = not_a_week_num; // W * std::basic_string temp_abbrev; // Z * minutes temp_offset = not_a_offset; // z * using date::detail::read; using date::detail::rs; using date::detail::ru; using date::detail::rld; using date::detail::checked_set; for (; *fmt != CharT{} && !is.fail(); ++fmt) { switch (*fmt) { case 'a': case 'A': case 'u': case 'w': // wd: a, A, u, w if (command) { int trial_wd = not_a_weekday; if (*fmt == 'a' || *fmt == 'A') { if (modified == CharT{}) { auto nm = weekday_names_pair; auto i = date::detail::scan_keyword(is, nm.first, nm.second) - nm.first; if (!is.fail()) trial_wd = i % 7; } else read(is, CharT{'%'}, width, modified, *fmt); } else // *fmt == 'u' || *fmt == 'w' { if (modified != CharT{'E'}) { read(is, ru{trial_wd, 1, width == -1 ? 1u : static_cast(width)}); if (!is.fail()) { if (*fmt == 'u') { if (!(1 <= trial_wd && trial_wd <= 7)) { trial_wd = not_a_weekday; is.setstate(ios::failbit); } else if (trial_wd == 7) trial_wd = 0; } else // *fmt == 'w' { if (!(0 <= trial_wd && trial_wd <= 6)) { trial_wd = not_a_weekday; is.setstate(ios::failbit); } } } } else read(is, CharT{'%'}, width, modified, *fmt); } if (trial_wd != not_a_weekday) checked_set(wd, trial_wd, not_a_weekday, is); } else // !command read(is, *fmt); command = nullptr; width = -1; modified = CharT{}; break; case 'b': case 'B': case 'h': if (command) { if (modified == CharT{}) { int ttm = not_a_month; auto nm = month_names_pair; auto i = date::detail::scan_keyword(is, nm.first, nm.second) - nm.first; if (!is.fail()) ttm = i % 12 + 1; checked_set(m, ttm, not_a_month, is); } else read(is, CharT{'%'}, width, modified, *fmt); command = nullptr; width = -1; modified = CharT{}; } else read(is, *fmt); break; case 'c': if (command) { if (modified != CharT{'O'}) { // "%a %b %e %T %Y" auto nm = weekday_names_pair; auto i = date::detail::scan_keyword(is, nm.first, nm.second) - nm.first; checked_set(wd, static_cast(i % 7), not_a_weekday, is); ws(is); nm = month_names_pair; i = date::detail::scan_keyword(is, nm.first, nm.second) - nm.first; checked_set(m, static_cast(i % 12 + 1), not_a_month, is); ws(is); int td = not_a_day; read(is, rs{td, 1, 2}); checked_set(d, td, not_a_day, is); ws(is); using dfs = date::detail::decimal_format_seconds; CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width; int tH; int tM; long double S{}; read(is, ru{tH, 1, 2}, CharT{':'}, ru{tM, 1, 2}, CharT{':'}); rclock::read(is, decimal_mark, rld{S, 1, w}); checked_set(H, tH, not_a_hour, is); checked_set(M, tM, not_a_minute, is); checked_set(s, round_i(duration{S}), not_a_second, is); ws(is); int tY = not_a_year; read(is, rs{tY, 1, 4u}); checked_set(Y, tY, not_a_year, is); } else read(is, CharT{'%'}, width, modified, *fmt); command = nullptr; width = -1; modified = CharT{}; } else read(is, *fmt); break; case 'x': if (command) { if (modified != CharT{'O'}) { // "%m/%d/%y" int ty = not_a_2digit_year; int tm = not_a_month; int td = not_a_day; read(is, ru{tm, 1, 2}, CharT{'/'}, ru{td, 1, 2}, CharT{'/'}, rs{ty, 1, 2}); checked_set(y, ty, not_a_2digit_year, is); checked_set(m, tm, not_a_month, is); checked_set(d, td, not_a_day, is); } else read(is, CharT{'%'}, width, modified, *fmt); command = nullptr; width = -1; modified = CharT{}; } else read(is, *fmt); break; case 'X': if (command) { if (modified != CharT{'O'}) { // "%T" using dfs = date::detail::decimal_format_seconds; CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width; int tH = not_a_hour; int tM = not_a_minute; long double S{}; read(is, ru{tH, 1, 2}, CharT{':'}, ru{tM, 1, 2}, CharT{':'}); rclock::read(is, decimal_mark, rld{S, 1, w}); checked_set(H, tH, not_a_hour, is); checked_set(M, tM, not_a_minute, is); checked_set(s, round_i(duration{S}), not_a_second, is); } else read(is, CharT{'%'}, width, modified, *fmt); command = nullptr; width = -1; modified = CharT{}; } else read(is, *fmt); break; case 'C': if (command) { int tC = not_a_century; read(is, rs{tC, 1, width == -1 ? 2u : static_cast(width)}); checked_set(C, tC, not_a_century, is); command = nullptr; width = -1; modified = CharT{}; } else read(is, *fmt); break; case 'D': if (command) { if (modified == CharT{}) { int tn = not_a_month; int td = not_a_day; int ty = not_a_2digit_year; read(is, ru{tn, 1, 2}, CharT{'\0'}, CharT{'/'}, CharT{'\0'}, ru{td, 1, 2}, CharT{'\0'}, CharT{'/'}, CharT{'\0'}, rs{ty, 1, 2}); checked_set(y, ty, not_a_2digit_year, is); checked_set(m, tn, not_a_month, is); checked_set(d, td, not_a_day, is); } else read(is, CharT{'%'}, width, modified, *fmt); command = nullptr; width = -1; modified = CharT{}; } else read(is, *fmt); break; case 'F': if (command) { if (modified == CharT{}) { int tY = not_a_year; int tn = not_a_month; int td = not_a_day; read(is, rs{tY, 1, width == -1 ? 4u : static_cast(width)}, CharT{'-'}, ru{tn, 1, 2}, CharT{'-'}, ru{td, 1, 2}); checked_set(Y, tY, not_a_year, is); checked_set(m, tn, not_a_month, is); checked_set(d, td, not_a_day, is); } else read(is, CharT{'%'}, width, modified, *fmt); command = nullptr; width = -1; modified = CharT{}; } else read(is, *fmt); break; case 'd': case 'e': if (command) { if (modified != CharT{'E'}) { int td = not_a_day; read(is, rs{td, 1, width == -1 ? 2u : static_cast(width)}); checked_set(d, td, not_a_day, is); } else read(is, CharT{'%'}, width, modified, *fmt); command = nullptr; width = -1; modified = CharT{}; } else read(is, *fmt); break; case 'H': if (command) { if (modified != CharT{'E'}) { int tH = not_a_hour; read(is, ru{tH, 1, width == -1 ? 2u : static_cast(width)}); checked_set(H, tH, not_a_hour, is); } else read(is, CharT{'%'}, width, modified, *fmt); command = nullptr; width = -1; modified = CharT{}; } else read(is, *fmt); break; case 'I': if (command) { if (modified == CharT{}) { int tI = not_a_hour_12_value; // reads in an hour into I, but most be in [1, 12] read(is, rs{tI, 1, width == -1 ? 2u : static_cast(width)}); if (!(1 <= tI && tI <= 12)) is.setstate(ios::failbit); checked_set(I, tI, not_a_hour_12_value, is); } else read(is, CharT{'%'}, width, modified, *fmt); command = nullptr; width = -1; modified = CharT{}; } else read(is, *fmt); break; case 'j': if (command) { if (modified == CharT{}) { int tj = not_a_doy; read(is, ru{tj, 1, width == -1 ? 3u : static_cast(width)}); checked_set(j, tj, not_a_doy, is); } else read(is, CharT{'%'}, width, modified, *fmt); command = nullptr; width = -1; modified = CharT{}; } else read(is, *fmt); break; case 'M': if (command) { if (modified != CharT{'E'}) { int tM = not_a_minute; read(is, ru{tM, 1, width == -1 ? 2u : static_cast(width)}); checked_set(M, tM, not_a_minute, is); } else read(is, CharT{'%'}, width, modified, *fmt); command = nullptr; width = -1; modified = CharT{}; } else read(is, *fmt); break; case 'm': if (command) { if (modified != CharT{'E'}) { int tn = not_a_month; read(is, rs{tn, 1, width == -1 ? 2u : static_cast(width)}); checked_set(m, tn, not_a_month, is); } else read(is, CharT{'%'}, width, modified, *fmt); command = nullptr; width = -1; modified = CharT{}; } else read(is, *fmt); break; case 'n': case 't': if (command) { if (modified == CharT{}) { // %n matches a single white space character // %t matches 0 or 1 white space characters auto ic = is.peek(); if (Traits::eq_int_type(ic, Traits::eof())) { ios::iostate err = ios::eofbit; if (*fmt == 'n') err |= ios::failbit; is.setstate(err); break; } if (isspace(ic)) { (void)is.get(); } else if (*fmt == 'n') is.setstate(ios::failbit); } else read(is, CharT{'%'}, width, modified, *fmt); command = nullptr; width = -1; modified = CharT{}; } else read(is, *fmt); break; case 'p': if (command) { if (modified == CharT{}) { int tp = not_a_ampm; auto nm = ampm_names_pair; auto i = date::detail::scan_keyword(is, nm.first, nm.second) - nm.first; tp = static_cast(i); checked_set(p, tp, not_a_ampm, is); } else read(is, CharT{'%'}, width, modified, *fmt); command = nullptr; width = -1; modified = CharT{}; } else read(is, *fmt); break; case 'r': if (command) { if (modified == CharT{}) { // "%I:%M:%S %p" using dfs = date::detail::decimal_format_seconds; CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width; long double S{}; int tI = not_a_hour_12_value; int tM = not_a_minute; read(is, ru{tI, 1, 2}, CharT{':'}, ru{tM, 1, 2}, CharT{':'}); rclock::read(is, decimal_mark, rld{S, 1, w}); checked_set(I, tI, not_a_hour_12_value, is); checked_set(M, tM, not_a_minute, is); checked_set(s, round_i(duration{S}), not_a_second, is); ws(is); auto nm = ampm_names_pair; auto i = date::detail::scan_keyword(is, nm.first, nm.second) - nm.first; checked_set(p, static_cast(i), not_a_ampm, is); } else read(is, CharT{'%'}, width, modified, *fmt); command = nullptr; width = -1; modified = CharT{}; } else read(is, *fmt); break; case 'R': if (command) { if (modified == CharT{}) { int tH = not_a_hour; int tM = not_a_minute; read(is, ru{tH, 1, 2}, CharT{'\0'}, CharT{':'}, CharT{'\0'}, ru{tM, 1, 2}, CharT{'\0'}); checked_set(H, tH, not_a_hour, is); checked_set(M, tM, not_a_minute, is); } else read(is, CharT{'%'}, width, modified, *fmt); command = nullptr; width = -1; modified = CharT{}; } else read(is, *fmt); break; case 'S': if (command) { if (modified != CharT{'E'}) { using dfs = date::detail::decimal_format_seconds; CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width; long double S{}; rclock::read(is, decimal_mark, rld{S, 1, width == -1 ? w : static_cast(width)}); checked_set(s, round_i(duration{S}), not_a_second, is); } else read(is, CharT{'%'}, width, modified, *fmt); command = nullptr; width = -1; modified = CharT{}; } else read(is, *fmt); break; case 'T': if (command) { if (modified == CharT{}) { using dfs = date::detail::decimal_format_seconds; CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width; int tH = not_a_hour; int tM = not_a_minute; long double S{}; read(is, ru{tH, 1, 2}, CharT{':'}, ru{tM, 1, 2}, CharT{':'}); rclock::read(is, decimal_mark, rld{S, 1, w}); checked_set(H, tH, not_a_hour, is); checked_set(M, tM, not_a_minute, is); checked_set(s, round_i(duration{S}), not_a_second, is); } else read(is, CharT{'%'}, width, modified, *fmt); command = nullptr; width = -1; modified = CharT{}; } else read(is, *fmt); break; case 'Y': if (command) { if (modified != CharT{'O'}) { int tY = not_a_year; read(is, rs{tY, 1, width == -1 ? 4u : static_cast(width)}); checked_set(Y, tY, not_a_year, is); } else read(is, CharT{'%'}, width, modified, *fmt); command = nullptr; width = -1; modified = CharT{}; } else read(is, *fmt); break; case 'y': if (command) { int ty = not_a_2digit_year; read(is, ru{ty, 1, width == -1 ? 2u : static_cast(width)}); checked_set(y, ty, not_a_2digit_year, is); command = nullptr; width = -1; modified = CharT{}; } else read(is, *fmt); break; case 'g': if (command) { if (modified == CharT{}) { int tg = not_a_2digit_year; read(is, ru{tg, 1, width == -1 ? 2u : static_cast(width)}); checked_set(g, tg, not_a_2digit_year, is); } else read(is, CharT{'%'}, width, modified, *fmt); command = nullptr; width = -1; modified = CharT{}; } else read(is, *fmt); break; case 'G': if (command) { if (modified == CharT{}) { int tG = not_a_year; read(is, rs{tG, 1, width == -1 ? 4u : static_cast(width)}); checked_set(G, tG, not_a_year, is); } else read(is, CharT{'%'}, width, modified, *fmt); command = nullptr; width = -1; modified = CharT{}; } else read(is, *fmt); break; case 'U': if (command) { if (modified == CharT{}) { int tU = not_a_week_num; read(is, ru{tU, 1, width == -1 ? 2u : static_cast(width)}); checked_set(U, tU, not_a_week_num, is); } else read(is, CharT{'%'}, width, modified, *fmt); command = nullptr; width = -1; modified = CharT{}; } else read(is, *fmt); break; case 'V': if (command) { if (modified == CharT{}) { int tV = not_a_week_num; read(is, ru{tV, 1, width == -1 ? 2u : static_cast(width)}); checked_set(V, tV, not_a_week_num, is); } else read(is, CharT{'%'}, width, modified, *fmt); command = nullptr; width = -1; modified = CharT{}; } else read(is, *fmt); break; case 'W': if (command) { if (modified == CharT{}) { int tW = not_a_week_num; read(is, ru{tW, 1, width == -1 ? 2u : static_cast(width)}); checked_set(W, tW, not_a_week_num, is); } else read(is, CharT{'%'}, width, modified, *fmt); command = nullptr; width = -1; modified = CharT{}; } else read(is, *fmt); break; case 'E': case 'O': if (command) { if (modified == CharT{}) { modified = *fmt; } else { read(is, CharT{'%'}, width, modified, *fmt); command = nullptr; width = -1; modified = CharT{}; } } else read(is, *fmt); break; case '%': if (command) { if (modified == CharT{}) read(is, *fmt); else read(is, CharT{'%'}, width, modified, *fmt); command = nullptr; width = -1; modified = CharT{}; } else command = fmt; break; case 'z': if (command) { int tH, tM; minutes toff = not_a_offset; bool neg = false; auto ic = is.peek(); if (!Traits::eq_int_type(ic, Traits::eof())) { auto c = static_cast(Traits::to_char_type(ic)); if (c == '-') neg = true; } if (modified == CharT{}) { read(is, rs{tH, 2, 2}); if (!is.fail()) toff = hours{std::abs(tH)}; if (is.good()) { ic = is.peek(); if (!Traits::eq_int_type(ic, Traits::eof())) { auto c = static_cast(Traits::to_char_type(ic)); if ('0' <= c && c <= '9') { read(is, ru{tM, 2, 2}); if (!is.fail()) toff += minutes{tM}; } } } } else { read(is, rs{tH, 1, 2}); if (!is.fail()) toff = hours{std::abs(tH)}; if (is.good()) { ic = is.peek(); if (!Traits::eq_int_type(ic, Traits::eof())) { auto c = static_cast(Traits::to_char_type(ic)); if (c == ':') { (void)is.get(); read(is, ru{tM, 2, 2}); if (!is.fail()) toff += minutes{tM}; } } } } if (neg) toff = -toff; checked_set(temp_offset, toff, not_a_offset, is); command = nullptr; width = -1; modified = CharT{}; } else read(is, *fmt); break; case 'Z': if (command) { if (modified == CharT{}) { std::basic_string buf; while (is.rdstate() == std::ios::goodbit) { auto i = is.rdbuf()->sgetc(); if (Traits::eq_int_type(i, Traits::eof())) { is.setstate(ios::eofbit); break; } auto wc = Traits::to_char_type(i); auto c = static_cast(wc); // is c a valid time zone name or abbreviation character? if (!(CharT{1} < wc && wc < CharT{127}) || !(isalnum(c) || c == '_' || c == '/' || c == '-' || c == '+')) break; buf.push_back(c); is.rdbuf()->sbumpc(); } if (buf.empty()) is.setstate(ios::failbit); checked_set(temp_abbrev, buf, {}, is); } else read(is, CharT{'%'}, width, modified, *fmt); command = nullptr; width = -1; modified = CharT{}; } else read(is, *fmt); break; default: if (command) { if (width == -1 && modified == CharT{} && '0' <= *fmt && *fmt <= '9') { width = static_cast(*fmt) - '0'; while ('0' <= fmt[1] && fmt[1] <= '9') width = 10*width + static_cast(*++fmt) - '0'; } else { if (modified == CharT{}) read(is, CharT{'%'}, width, *fmt); else read(is, CharT{'%'}, width, modified, *fmt); command = nullptr; width = -1; modified = CharT{}; } } else // !command { if (isspace(static_cast(*fmt))) { // space matches 0 or more white space characters if (is.good()) ws(is); } else read(is, *fmt); } break; } } // is.fail() || *fmt == CharT{} if (is.rdstate() == ios::goodbit && command) { if (modified == CharT{}) read(is, CharT{'%'}, width); else read(is, CharT{'%'}, width, modified); } if (!is.fail()) { if (y != not_a_2digit_year) { // Convert y and an optional C to Y if (!(0 <= y && y <= 99)) goto broken; if (C == not_a_century) { if (Y == not_a_year) { if (y >= 69) C = 19; else C = 20; } else { C = (Y >= 0 ? Y : Y-100) / 100; } } int tY; if (C >= 0) tY = 100*C + y; else tY = 100*(C+1) - (y == 0 ? 100 : y); if (Y != not_a_year && Y != tY) goto broken; Y = tY; } if (g != not_a_2digit_year) { // Convert g and an optional C to G if (!(0 <= g && g <= 99)) goto broken; if (C == not_a_century) { if (G == not_a_year) { if (g >= 69) C = 19; else C = 20; } else { C = (G >= 0 ? G : G-100) / 100; } } int tG; if (C >= 0) tG = 100*C + g; else tG = 100*(C+1) - (g == 0 ? 100 : g); if (G != not_a_year && G != tG) goto broken; G = tG; } if (Y < static_cast(year::min()) || Y > static_cast(year::max())) Y = not_a_year; bool computed = false; if (G != not_a_year && V != not_a_week_num && wd != not_a_weekday) { year_month_day ymd_trial = sys_days(year{G-1}/December/Thursday[last]) + (Monday-Thursday) + weeks{V-1} + (date::weekday{static_cast(wd)}-Monday); if (Y == not_a_year) Y = static_cast(ymd_trial.year()); else if (year{Y} != ymd_trial.year()) goto broken; if (m == not_a_month) m = static_cast(static_cast(ymd_trial.month())); else if (month(static_cast(m)) != ymd_trial.month()) goto broken; if (d == not_a_day) d = static_cast(static_cast(ymd_trial.day())); else if (day(static_cast(d)) != ymd_trial.day()) goto broken; computed = true; } if (Y != not_a_year && U != not_a_week_num && wd != not_a_weekday) { year_month_day ymd_trial = sys_days(year{Y}/January/Sunday[1]) + weeks{U-1} + (date::weekday{static_cast(wd)} - Sunday); if (Y == not_a_year) Y = static_cast(ymd_trial.year()); else if (year{Y} != ymd_trial.year()) goto broken; if (m == not_a_month) m = static_cast(static_cast(ymd_trial.month())); else if (month(static_cast(m)) != ymd_trial.month()) goto broken; if (d == not_a_day) d = static_cast(static_cast(ymd_trial.day())); else if (day(static_cast(d)) != ymd_trial.day()) goto broken; computed = true; } if (Y != not_a_year && W != not_a_week_num && wd != not_a_weekday) { year_month_day ymd_trial = sys_days(year{Y}/January/Monday[1]) + weeks{W-1} + (date::weekday{static_cast(wd)} - Monday); if (Y == not_a_year) Y = static_cast(ymd_trial.year()); else if (year{Y} != ymd_trial.year()) goto broken; if (m == not_a_month) m = static_cast(static_cast(ymd_trial.month())); else if (month(static_cast(m)) != ymd_trial.month()) goto broken; if (d == not_a_day) d = static_cast(static_cast(ymd_trial.day())); else if (day(static_cast(d)) != ymd_trial.day()) goto broken; computed = true; } if (j != not_a_doy && Y != not_a_year) { auto ymd_trial = year_month_day{local_days(year{Y}/1/1) + days{j-1}}; if (m == 0) m = static_cast(static_cast(ymd_trial.month())); else if (month(static_cast(m)) != ymd_trial.month()) goto broken; if (d == 0) d = static_cast(static_cast(ymd_trial.day())); else if (day(static_cast(d)) != ymd_trial.day()) goto broken; j = not_a_doy; } auto ymd = year{Y}/m/d; if (ymd.ok()) { if (wd == not_a_weekday) wd = static_cast((date::weekday(sys_days(ymd)) - Sunday).count()); else if (wd != static_cast((date::weekday(sys_days(ymd)) - Sunday).count())) goto broken; if (!computed) { if (G != not_a_year || V != not_a_week_num) { sys_days sd = ymd; auto G_trial = year_month_day{sd + days{3}}.year(); auto start = sys_days((G_trial - years{1})/December/Thursday[last]) + (Monday - Thursday); if (sd < start) { --G_trial; if (V != not_a_week_num) start = sys_days((G_trial - years{1})/December/Thursday[last]) + (Monday - Thursday); } if (G != not_a_year && G != static_cast(G_trial)) goto broken; if (V != not_a_week_num) { auto V_trial = duration_cast(sd - start).count() + 1; if (V != V_trial) goto broken; } } if (U != not_a_week_num) { auto start = sys_days(Sunday[1]/January/ymd.year()); auto U_trial = floor(sys_days(ymd) - start).count() + 1; if (U != U_trial) goto broken; } if (W != not_a_week_num) { auto start = sys_days(Monday[1]/January/ymd.year()); auto W_trial = floor(sys_days(ymd) - start).count() + 1; if (W != W_trial) goto broken; } } } fds.ymd = ymd; if (I != not_a_hour_12_value) { if (!(1 <= I && I <= 12)) goto broken; if (p != not_a_ampm) { // p is in [0, 1] == [AM, PM] // Store trial H in I if (I == 12) --p; I += p*12; // Either set H from I or make sure H and I are consistent if (H == not_a_hour) H = I; else if (I != H) goto broken; } else // p == not_a_ampm { // if H, make sure H and I could be consistent if (H != not_a_hour) { if (I == 12) { if (H != 0 && H != 12) goto broken; } else if (!(I == H || I == H+12)) { goto broken; } } } } if (H != not_a_hour) { fds.has_tod = true; fds.tod = hh_mm_ss{hours{H}}; } if (M != not_a_minute) { fds.has_tod = true; fds.tod.minutes(date::detail::undocumented{}) = minutes{M}; } if (s != not_a_second) { fds.has_tod = true; const date::detail::decimal_format_seconds dfs{s}; fds.tod.seconds(date::detail::undocumented{}) = dfs.seconds(); fds.tod.subseconds(date::detail::undocumented{}) = dfs.subseconds(); } if (j != not_a_doy) { fds.has_tod = true; fds.tod.hours(date::detail::undocumented{}) += hours{days{j}}; } if (wd != not_a_weekday) fds.wd = date::weekday{static_cast(wd)}; if (abbrev != nullptr) *abbrev = std::move(temp_abbrev); if (offset != nullptr && temp_offset != not_a_offset) *offset = temp_offset; } return is; } broken: is.setstate(ios::failbit); return is; } // Parse into sys_time template > std::basic_istream& from_stream(std::basic_istream& is, const CharT* fmt, const std::pair& month_names_pair, const std::pair& weekday_names_pair, const std::pair& ampm_names_pair, const CharT& decimal_mark, date::sys_time& tp, std::basic_string* abbrev = nullptr, std::chrono::minutes* offset = nullptr) { using CT = typename std::common_type::type; using date::detail::round_i; std::chrono::minutes offset_local{}; auto offptr = offset ? offset : &offset_local; date::fields fds{}; fds.has_tod = true; rclock::from_stream(is, fmt, month_names_pair, weekday_names_pair, ampm_names_pair, decimal_mark, fds, abbrev, offptr); if (!fds.ymd.ok() || !fds.tod.in_conventional_range()) is.setstate(std::ios::failbit); if (!is.fail()) tp = round_i(date::sys_days(fds.ymd) - *offptr + fds.tod.to_duration()); return is; } // Parse into local_time template > std::basic_istream& from_stream(std::basic_istream& is, const CharT* fmt, const std::pair& month_names_pair, const std::pair& weekday_names_pair, const std::pair& ampm_names_pair, const CharT& decimal_mark, date::local_time& tp, std::basic_string* abbrev = nullptr, std::chrono::minutes* offset = nullptr) { using CT = typename std::common_type::type; using date::detail::round_i; date::fields fds{}; fds.has_tod = true; rclock::from_stream(is, fmt, month_names_pair, weekday_names_pair, ampm_names_pair, decimal_mark, fds, abbrev, offset); if (!fds.ymd.ok() || !fds.tod.in_conventional_range()) is.setstate(std::ios::failbit); if (!is.fail()) tp = round_i(date::local_days(fds.ymd) + fds.tod.to_duration()); return is; } template > std::basic_istream& from_stream(std::basic_istream& is, const CharT* fmt, const std::pair& month_names_pair, const std::pair& weekday_names_pair, const std::pair& ampm_names_pair, const CharT& decimal_mark, date::year& y, std::basic_string* abbrev = nullptr, std::chrono::minutes* offset = nullptr) { using CT = std::chrono::seconds; date::fields fds{}; rclock::from_stream(is, fmt, month_names_pair, weekday_names_pair, ampm_names_pair, decimal_mark, fds, abbrev, offset); if (!fds.ymd.year().ok()) is.setstate(std::ios::failbit); if (!is.fail()) y = fds.ymd.year(); return is; } template > std::basic_istream& from_stream(std::basic_istream& is, const CharT* fmt, const std::pair& month_names_pair, const std::pair& weekday_names_pair, const std::pair& ampm_names_pair, const CharT& decimal_mark, date::year_month& ym, std::basic_string* abbrev = nullptr, std::chrono::minutes* offset = nullptr) { using CT = std::chrono::seconds; date::fields fds{}; rclock::from_stream(is, fmt, month_names_pair, weekday_names_pair, ampm_names_pair, decimal_mark, fds, abbrev, offset); if (!fds.ymd.month().ok()) is.setstate(std::ios::failbit); if (!is.fail()) ym = fds.ymd.year()/fds.ymd.month(); return is; } /* * Extra template variations of `from_stream()` * * Both allow "invalid" dates to be parsed, like 2020-02-30. This ensures we * can parse() what we produce from format(). * * The first `from_stream()` variant accepts and ymd and tod directly, which * no `from_stream()` variants do. */ template > std::basic_istream& from_stream(std::basic_istream& is, const CharT* fmt, const std::pair& month_names_pair, const std::pair& weekday_names_pair, const std::pair& ampm_names_pair, const CharT& decimal_mark, date::year_month_day& ymd, date::hh_mm_ss& tod, std::basic_string* abbrev = nullptr, std::chrono::minutes* offset = nullptr) { using CT = typename std::common_type::type; std::chrono::minutes offset_local{}; std::chrono::minutes* offptr = offset ? offset : &offset_local; date::fields fds{}; fds.has_tod = true; rclock::from_stream(is, fmt, month_names_pair, weekday_names_pair, ampm_names_pair, decimal_mark, fds, abbrev, offptr); // Fields must be `ok()` independently, not jointly. i.e. invalid dates are allowed. if (!fds.ymd.year().ok() || !fds.ymd.month().ok() || !fds.ymd.day().ok() || !fds.tod.in_conventional_range()) is.setstate(std::ios::failbit); if (!is.fail()) { ymd = fds.ymd; tod = fds.tod; } return is; } template > std::basic_istream& from_stream(std::basic_istream& is, const CharT* fmt, const std::pair& month_names_pair, const std::pair& weekday_names_pair, const std::pair& ampm_names_pair, const CharT& decimal_mark, date::year_month_day& ymd, std::basic_string* abbrev = nullptr, std::chrono::minutes* offset = nullptr) { using CT = std::chrono::seconds; date::fields fds{}; rclock::from_stream(is, fmt, month_names_pair, weekday_names_pair, ampm_names_pair, decimal_mark, fds, abbrev, offset); // Fields must be `ok()` independently, not jointly. i.e. invalid dates are allowed. if (!fds.ymd.year().ok() || !fds.ymd.month().ok() || !fds.ymd.day().ok()) is.setstate(std::ios::failbit); if (!is.fail()) ymd = fds.ymd; return is; } static inline void fill_formats(const cpp11::strings& src, std::vector& dest) { const r_ssize size = src.size(); for (r_ssize i = 0; i < size; ++i) { std::string elt = src[i]; dest[i] = elt; } } } // namespace rclock #endif clock/src/format.cpp0000644000176200001440000013177414422225070014143 0ustar liggesusers#include "clock.h" #include "utils.h" #include "duration.h" #include "enums.h" #include "resolve.h" #include "zone.h" #include "fill.h" #include "failure.h" #include #include /* * Reference for format tokens * https://howardhinnant.github.io/date/date.html#to_stream_formatting */ // ----------------------------------------------------------------------------- /* * Custom seconds formatter to be able to use user-supplied decimal mark. * Essentially copies the `decimal_format_seconds` `<<` operator. */ template static inline std::basic_ostream& stream_seconds(std::basic_ostream& os, const date::hh_mm_ss& tod, const CharT* decimal_mark) { using date::detail::save_ostream; // NOTE: Custom formatting here to use user-supplied `decimal_mark` save_ostream _(os); os.fill('0'); os.width(2); os.flags(std::ios::dec | std::ios::right); auto dfs = tod.decimal_format_seconds(date::detail::undocumented{}); os << dfs.seconds().count(); if (dfs.width > 0) { os << decimal_mark; save_ostream _s(os); os.imbue(std::locale::classic()); os.width(dfs.width); os << dfs.subseconds().count(); } return os; } /* * Copying the `hh_mm_ss` `<<` operator to be able to use a user-supplied * `decimal_mark`. */ template static inline std::basic_ostream& stream_tod(std::basic_ostream& os, const date::hh_mm_ss& tod, const CharT* decimal_mark) { if (tod.is_negative()) os << '-'; if (tod.hours() < std::chrono::hours{10}) os << '0'; os << tod.hours().count() << ':'; if (tod.minutes() < std::chrono::minutes{10}) os << '0'; os << tod.minutes().count() << ':'; return stream_seconds(os, tod, decimal_mark); } // ----------------------------------------------------------------------------- template std::basic_ostream& clock_to_stream(std::basic_ostream& os, const CharT* fmt, const date::fields& fds, const std::pair& month_names_pair, const std::pair& weekday_names_pair, const std::pair& ampm_names_pair, const CharT* decimal_mark, const std::string* abbrev = nullptr, const std::chrono::seconds* offset_sec = nullptr) { using date::detail::save_ostream; using date::detail::get_units; using date::detail::extract_weekday; using date::detail::extract_month; using std::ios; using std::chrono::duration_cast; using std::chrono::seconds; using std::chrono::minutes; using std::chrono::hours; date::detail::save_ostream ss(os); os.fill(' '); os.flags(std::ios::skipws | std::ios::dec); os.width(0); tm tm{}; bool insert_negative = fds.has_tod && fds.tod.to_duration() < Duration::zero(); const CharT* command = nullptr; CharT modified = CharT{}; for (; *fmt; ++fmt) { switch (*fmt) { case 'a': case 'A': if (command) { if (modified == CharT{}) { tm.tm_wday = static_cast(extract_weekday(os, fds)); if (os.fail()) return os; os << weekday_names_pair.first[tm.tm_wday+7*(*fmt == 'a')]; } else { os << CharT{'%'} << modified << *fmt; modified = CharT{}; } command = nullptr; } else os << *fmt; break; case 'b': case 'B': case 'h': if (command) { if (modified == CharT{}) { tm.tm_mon = static_cast(extract_month(os, fds)) - 1; os << month_names_pair.first[tm.tm_mon+12*(*fmt != 'B')]; } else { os << CharT{'%'} << modified << *fmt; modified = CharT{}; } command = nullptr; } else os << *fmt; break; case 'c': case 'x': if (command) { if (modified == CharT{}) { if (!fds.ymd.ok()) os.setstate(std::ios::failbit); if (*fmt == 'c' && !fds.has_tod) os.setstate(std::ios::failbit); if (*fmt == 'c') { auto wd = static_cast(extract_weekday(os, fds)); os << weekday_names_pair.first[static_cast(wd)+7] << ' '; os << month_names_pair.first[extract_month(os, fds)-1+12] << ' '; auto d = static_cast(static_cast(fds.ymd.day())); if (d < 10) os << ' '; os << d << ' ' << date::make_time(duration_cast(fds.tod.to_duration())) << ' ' << fds.ymd.year(); } else // *fmt == 'x' { auto const& ymd = fds.ymd; save_ostream _(os); os.fill('0'); os.flags(std::ios::dec | std::ios::right); os.width(2); os << static_cast(ymd.month()) << CharT{'/'}; os.width(2); os << static_cast(ymd.day()) << CharT{'/'}; os.width(2); os << static_cast(ymd.year()) % 100; } } else { os << CharT{'%'} << modified << *fmt; modified = CharT{}; } command = nullptr; } else os << *fmt; break; case 'C': if (command) { if (modified == CharT{}) { if (!fds.ymd.year().ok()) os.setstate(std::ios::failbit); auto y = static_cast(fds.ymd.year()); save_ostream _(os); os.fill('0'); os.flags(std::ios::dec | std::ios::right); if (y >= 0) { os.width(2); os << y/100; } else { os << CharT{'-'}; os.width(2); os << -(y-99)/100; } } else { os << CharT{'%'} << modified << *fmt; modified = CharT{}; } command = nullptr; } else os << *fmt; break; case 'd': case 'e': if (command) { if (modified == CharT{}) { if (!fds.ymd.day().ok()) os.setstate(std::ios::failbit); auto d = static_cast(static_cast(fds.ymd.day())); save_ostream _(os); if (*fmt == CharT{'d'}) os.fill('0'); else os.fill(' '); os.flags(std::ios::dec | std::ios::right); os.width(2); os << d; } else { os << CharT{'%'} << modified << *fmt; modified = CharT{}; } command = nullptr; } else os << *fmt; break; case 'D': if (command) { if (modified == CharT{}) { if (!fds.ymd.ok()) os.setstate(std::ios::failbit); auto const& ymd = fds.ymd; save_ostream _(os); os.fill('0'); os.flags(std::ios::dec | std::ios::right); os.width(2); os << static_cast(ymd.month()) << CharT{'/'}; os.width(2); os << static_cast(ymd.day()) << CharT{'/'}; os.width(2); os << static_cast(ymd.year()) % 100; } else { os << CharT{'%'} << modified << *fmt; modified = CharT{}; } command = nullptr; } else os << *fmt; break; case 'F': if (command) { if (modified == CharT{}) { if (!fds.ymd.ok()) os.setstate(std::ios::failbit); auto const& ymd = fds.ymd; save_ostream _(os); os.imbue(std::locale::classic()); os.fill('0'); os.flags(std::ios::dec | std::ios::right); os.width(4); os << static_cast(ymd.year()) << CharT{'-'}; os.width(2); os << static_cast(ymd.month()) << CharT{'-'}; os.width(2); os << static_cast(ymd.day()); } else { os << CharT{'%'} << modified << *fmt; modified = CharT{}; } command = nullptr; } else os << *fmt; break; case 'g': case 'G': if (command) { if (modified == CharT{}) { if (!fds.ymd.ok()) os.setstate(std::ios::failbit); auto ld = date::local_days(fds.ymd); auto y = date::year_month_day{ld + date::days{3}}.year(); auto start = date::local_days((y-date::years{1})/date::December/date::Thursday[date::last]) + (date::Monday-date::Thursday); if (ld < start) --y; if (*fmt == CharT{'G'}) os << y; else { save_ostream _(os); os.fill('0'); os.flags(std::ios::dec | std::ios::right); os.width(2); os << std::abs(static_cast(y)) % 100; } } else { os << CharT{'%'} << modified << *fmt; modified = CharT{}; } command = nullptr; } else os << *fmt; break; case 'H': case 'I': if (command) { if (modified == CharT{}) { if (!fds.has_tod) os.setstate(std::ios::failbit); if (insert_negative) { os << '-'; insert_negative = false; } auto hms = fds.tod; auto h = *fmt == CharT{'I'} ? date::make12(hms.hours()) : hms.hours(); if (h < hours{10}) os << CharT{'0'}; os << h.count(); } else { os << CharT{'%'} << modified << *fmt; modified = CharT{}; } command = nullptr; } else os << *fmt; break; case 'j': if (command) { if (modified == CharT{}) { if (fds.ymd.ok() || fds.has_tod) { date::days doy; if (fds.ymd.ok()) { auto ld = date::local_days(fds.ymd); auto y = fds.ymd.year(); doy = ld - date::local_days(y/date::January/1) + date::days{1}; } else { doy = duration_cast(fds.tod.to_duration()); } save_ostream _(os); os.fill('0'); os.flags(std::ios::dec | std::ios::right); os.width(3); os << doy.count(); } else { os.setstate(std::ios::failbit); } } else { os << CharT{'%'} << modified << *fmt; modified = CharT{}; } command = nullptr; } else os << *fmt; break; case 'm': if (command) { if (modified == CharT{}) { if (!fds.ymd.month().ok()) os.setstate(std::ios::failbit); auto m = static_cast(fds.ymd.month()); if (m < 10) os << CharT{'0'}; os << m; } else { os << CharT{'%'} << modified << *fmt; modified = CharT{}; } command = nullptr; } else os << *fmt; break; case 'M': if (command) { if (modified == CharT{}) { if (!fds.has_tod) os.setstate(std::ios::failbit); if (insert_negative) { os << '-'; insert_negative = false; } if (fds.tod.minutes() < minutes{10}) os << CharT{'0'}; os << fds.tod.minutes().count(); } else { os << CharT{'%'} << modified << *fmt; modified = CharT{}; } command = nullptr; } else os << *fmt; break; case 'n': if (command) { if (modified == CharT{}) os << CharT{'\n'}; else { os << CharT{'%'} << modified << *fmt; modified = CharT{}; } command = nullptr; } else os << *fmt; break; case 'p': if (command) { if (modified == CharT{}) { if (!fds.has_tod) os.setstate(std::ios::failbit); if (date::is_am(fds.tod.hours())) os << ampm_names_pair.first[0]; else os << ampm_names_pair.first[1]; } else { os << CharT{'%'} << modified << *fmt; } modified = CharT{}; command = nullptr; } else os << *fmt; break; case 'Q': case 'q': // TODO: These seem to be duration format values. Replace with quarter formatters? if (command) { if (modified == CharT{}) { if (!fds.has_tod) os.setstate(std::ios::failbit); auto d = fds.tod.to_duration(); if (*fmt == 'q') os << get_units(typename decltype(d)::period::type{}); else os << d.count(); } else { os << CharT{'%'} << modified << *fmt; } modified = CharT{}; command = nullptr; } else os << *fmt; break; case 'r': if (command) { if (modified == CharT{}) { if (!fds.has_tod) os.setstate(std::ios::failbit); date::hh_mm_ss tod(duration_cast(fds.tod.to_duration())); save_ostream _(os); os.fill('0'); os.width(2); os << date::make12(tod.hours()).count() << CharT{':'}; os.width(2); os << tod.minutes().count() << CharT{':'}; os.width(2); os << tod.seconds().count() << CharT{' '}; if (date::is_am(tod.hours())) os << ampm_names_pair.first[0]; else os << ampm_names_pair.first[1]; } else { os << CharT{'%'} << modified << *fmt; } modified = CharT{}; command = nullptr; } else os << *fmt; break; case 'R': if (command) { if (modified == CharT{}) { if (!fds.has_tod) os.setstate(std::ios::failbit); if (fds.tod.hours() < hours{10}) os << CharT{'0'}; os << fds.tod.hours().count() << CharT{':'}; if (fds.tod.minutes() < minutes{10}) os << CharT{'0'}; os << fds.tod.minutes().count(); } else { os << CharT{'%'} << modified << *fmt; modified = CharT{}; } command = nullptr; } else os << *fmt; break; case 'S': if (command) { if (modified == CharT{}) { if (!fds.has_tod) os.setstate(std::ios::failbit); if (insert_negative) { os << '-'; insert_negative = false; } stream_seconds(os, fds.tod, decimal_mark); } else { os << CharT{'%'} << modified << *fmt; modified = CharT{}; } command = nullptr; } else os << *fmt; break; case 't': if (command) { if (modified == CharT{}) os << CharT{'\t'}; else { os << CharT{'%'} << modified << *fmt; modified = CharT{}; } command = nullptr; } else os << *fmt; break; case 'T': if (command) { if (modified == CharT{}) { if (!fds.has_tod) os.setstate(std::ios::failbit); stream_tod(os, fds.tod, decimal_mark); } else { os << CharT{'%'} << modified << *fmt; modified = CharT{}; } command = nullptr; } else os << *fmt; break; case 'u': if (command) { if (modified == CharT{}) { auto wd = extract_weekday(os, fds); os << (wd != 0 ? wd : 7u); } else { os << CharT{'%'} << modified << *fmt; modified = CharT{}; } command = nullptr; } else os << *fmt; break; case 'U': if (command) { if (modified == CharT{}) { auto const& ymd = fds.ymd; if (!ymd.ok()) os.setstate(std::ios::failbit); auto ld = date::local_days(ymd); auto st = date::local_days(date::Sunday[1]/date::January/ymd.year()); if (ld < st) os << CharT{'0'} << CharT{'0'}; else { auto wn = duration_cast(ld - st).count() + 1; if (wn < 10) os << CharT{'0'}; os << wn; } } else { os << CharT{'%'} << modified << *fmt; modified = CharT{}; } command = nullptr; } else os << *fmt; break; case 'V': if (command) { if (modified == CharT{}) { if (!fds.ymd.ok()) os.setstate(std::ios::failbit); auto ld = date::local_days(fds.ymd); auto y = date::year_month_day{ld + date::days{3}}.year(); auto st = date::local_days((y-date::years{1})/12/date::Thursday[date::last]) + (date::Monday-date::Thursday); if (ld < st) { --y; st = date::local_days((y - date::years{1})/12/date::Thursday[date::last]) + (date::Monday-date::Thursday); } auto wn = duration_cast(ld - st).count() + 1; if (wn < 10) os << CharT{'0'}; os << wn; } else { os << CharT{'%'} << modified << *fmt; modified = CharT{}; } command = nullptr; } else os << *fmt; break; case 'w': if (command) { auto wd = extract_weekday(os, fds); if (os.fail()) return os; if (modified == CharT{}) { os << wd; } else { os << CharT{'%'} << modified << *fmt; modified = CharT{}; } command = nullptr; } else os << *fmt; break; case 'W': if (command) { if (modified == CharT{}) { auto const& ymd = fds.ymd; if (!ymd.ok()) os.setstate(std::ios::failbit); auto ld = date::local_days(ymd); auto st = date::local_days(date::Monday[1]/date::January/ymd.year()); if (ld < st) os << CharT{'0'} << CharT{'0'}; else { auto wn = duration_cast(ld - st).count() + 1; if (wn < 10) os << CharT{'0'}; os << wn; } } else { os << CharT{'%'} << modified << *fmt; modified = CharT{}; } command = nullptr; } else os << *fmt; break; case 'X': if (command) { if (modified == CharT{}) { if (!fds.has_tod) os.setstate(std::ios::failbit); stream_tod(os, fds.tod, decimal_mark); } else { os << CharT{'%'} << modified << *fmt; modified = CharT{}; } command = nullptr; } else os << *fmt; break; case 'y': if (command) { if (modified == CharT{}) { if (!fds.ymd.year().ok()) os.setstate(std::ios::failbit); auto y = static_cast(fds.ymd.year()); y = std::abs(y) % 100; if (y < 10) os << CharT{'0'}; os << y; } else { os << CharT{'%'} << modified << *fmt; modified = CharT{}; } command = nullptr; } else os << *fmt; break; case 'Y': if (command) { if (modified == CharT{}) { if (!fds.ymd.year().ok()) os.setstate(std::ios::failbit); auto y = fds.ymd.year(); save_ostream _(os); os.imbue(std::locale::classic()); os << y; } else { os << CharT{'%'} << modified << *fmt; modified = CharT{}; } command = nullptr; } else os << *fmt; break; case 'z': if (command) { if (offset_sec == nullptr) { // Can not format %z with unknown offset os.setstate(ios::failbit); return os; } auto m = duration_cast(*offset_sec); auto neg = m < minutes{0}; m = date::abs(m); auto h = duration_cast(m); m -= h; if (neg) os << CharT{'-'}; else os << CharT{'+'}; if (h < hours{10}) os << CharT{'0'}; os << h.count(); if (modified != CharT{}) os << CharT{':'}; if (m < minutes{10}) os << CharT{'0'}; os << m.count(); command = nullptr; modified = CharT{}; } else os << *fmt; break; case 'Z': if (command) { if (modified == CharT{}) { if (abbrev == nullptr) { // Can not format %Z with unknown time_zone os.setstate(ios::failbit); return os; } for (auto c : *abbrev) os << CharT(c); } else { os << CharT{'%'} << modified << *fmt; modified = CharT{}; } command = nullptr; } else os << *fmt; break; case 'E': case 'O': if (command) { if (modified == CharT{}) { modified = *fmt; } else { os << CharT{'%'} << modified << *fmt; command = nullptr; modified = CharT{}; } } else os << *fmt; break; case '%': if (command) { if (modified == CharT{}) { os << CharT{'%'}; command = nullptr; } else { os << CharT{'%'} << modified << CharT{'%'}; command = nullptr; modified = CharT{}; } } else command = fmt; break; default: if (command) { os << CharT{'%'}; command = nullptr; } if (modified != CharT{}) { os << modified; modified = CharT{}; } os << *fmt; break; } } if (command) os << CharT{'%'}; if (modified != CharT{}) os << modified; return os; } // ----------------------------------------------------------------------------- /* * Adding another template variation of `to_stream()` that parses from a ymd * and tod directly. This way we keep the precision when parsing large dates * (i.e. with year past 1970 +/- 292) that have nanoseconds */ template inline std::basic_ostream& clock_to_stream(std::basic_ostream& os, const CharT* fmt, const date::year_month_day& ymd, const date::hh_mm_ss& hms, const std::pair& month_names_pair, const std::pair& weekday_names_pair, const std::pair& ampm_names_pair, const CharT* decimal_mark, const std::string* abbrev = nullptr, const std::chrono::seconds* offset_sec = nullptr) { date::fields fds{ymd, hms}; return clock_to_stream(os, fmt, fds, month_names_pair, weekday_names_pair, ampm_names_pair, decimal_mark, abbrev, offset_sec); } template inline std::basic_ostream& clock_to_stream(std::basic_ostream& os, const CharT* fmt, const date::local_time& tp, const std::pair& month_names_pair, const std::pair& weekday_names_pair, const std::pair& ampm_names_pair, const CharT* decimal_mark, const std::string* abbrev = nullptr, const std::chrono::seconds* offset_sec = nullptr) { using CT = typename std::common_type::type; auto ld = std::chrono::time_point_cast(tp); date::fields fds; if (ld <= tp) fds = date::fields{date::year_month_day{ld}, date::hh_mm_ss{tp-date::local_seconds{ld}}}; else fds = date::fields{date::year_month_day{ld - date::days{1}}, date::hh_mm_ss{date::days{1} - (date::local_seconds{ld} - tp)}}; return clock_to_stream(os, fmt, fds, month_names_pair, weekday_names_pair, ampm_names_pair, decimal_mark, abbrev, offset_sec); } template inline std::basic_ostream& clock_to_stream(std::basic_ostream& os, const CharT* fmt, const date::sys_time& tp, const std::pair& month_names_pair, const std::pair& weekday_names_pair, const std::pair& ampm_names_pair, const CharT* decimal_mark) { using std::chrono::seconds; using CT = typename std::common_type::type; const std::string abbrev("UTC"); CONSTDATA seconds offset{0}; auto sd = std::chrono::time_point_cast(tp); date::fields fds; if (sd <= tp) fds = date::fields{date::year_month_day{sd}, date::hh_mm_ss{tp-date::sys_seconds{sd}}}; else fds = date::fields{date::year_month_day{sd - date::days{1}}, date::hh_mm_ss{date::days{1} - (date::sys_seconds{sd} - tp)}}; return clock_to_stream(os, fmt, fds, month_names_pair, weekday_names_pair, ampm_names_pair, decimal_mark, &abbrev, &offset); } // ----------------------------------------------------------------------------- template cpp11::writable::strings format_time_point_impl(cpp11::list_of& fields, const cpp11::strings& format, const cpp11::strings& month, const cpp11::strings& month_abbrev, const cpp11::strings& weekday, const cpp11::strings& weekday_abbrev, const cpp11::strings& am_pm, const cpp11::strings& decimal_mark) { using Duration = typename ClockDuration::chrono_duration; const ClockDuration x{fields}; const r_ssize size = x.size(); cpp11::writable::strings out(size); if (format.size() != 1) { clock_abort("`format` must have size 1."); } std::string string_format(format[0]); const char* c_format = string_format.c_str(); std::ostringstream stream; stream.imbue(std::locale::classic()); std::string month_names[24]; const std::pair& month_names_pair = fill_month_names( month, month_abbrev, month_names ); std::string weekday_names[14]; const std::pair& weekday_names_pair = fill_weekday_names( weekday, weekday_abbrev, weekday_names ); std::string ampm_names[2]; const std::pair& ampm_names_pair = fill_ampm_names( am_pm, ampm_names ); std::string decimal_mark_string = decimal_mark[0]; const char* decimal_mark_char = decimal_mark_string.c_str(); rclock::failures fail{}; for (r_ssize i = 0; i < size; ++i) { if (x.is_na(i)) { SET_STRING_ELT(out, i, r_chr_na); continue; } Duration duration = x[i]; std::chrono::time_point tp{duration}; stream.str(std::string()); stream.clear(); clock_to_stream( stream, c_format, tp, month_names_pair, weekday_names_pair, ampm_names_pair, decimal_mark_char ); if (stream.fail()) { fail.write(i); SET_STRING_ELT(out, i, r_chr_na); continue; } std::string str = stream.str(); SET_STRING_ELT(out, i, Rf_mkCharLenCE(str.c_str(), str.size(), CE_UTF8)); } if (fail.any_failures()) { fail.warn_format(); } return out; } [[cpp11::register]] cpp11::writable::strings format_time_point_cpp(cpp11::list_of fields, const cpp11::integers& clock, const cpp11::strings& format, const cpp11::integers& precision_int, const cpp11::strings& month, const cpp11::strings& month_abbrev, const cpp11::strings& weekday, const cpp11::strings& weekday_abbrev, const cpp11::strings& am_pm, const cpp11::strings& decimal_mark) { using namespace rclock; switch (parse_clock_name(clock)) { case clock_name::sys: { switch (parse_precision(precision_int)) { case precision::day: return format_time_point_impl(fields, format, month, month_abbrev, weekday, weekday_abbrev, am_pm, decimal_mark); case precision::hour: return format_time_point_impl(fields, format, month, month_abbrev, weekday, weekday_abbrev, am_pm, decimal_mark); case precision::minute: return format_time_point_impl(fields, format, month, month_abbrev, weekday, weekday_abbrev, am_pm, decimal_mark); case precision::second: return format_time_point_impl(fields, format, month, month_abbrev, weekday, weekday_abbrev, am_pm, decimal_mark); case precision::millisecond: return format_time_point_impl(fields, format, month, month_abbrev, weekday, weekday_abbrev, am_pm, decimal_mark); case precision::microsecond: return format_time_point_impl(fields, format, month, month_abbrev, weekday, weekday_abbrev, am_pm, decimal_mark); case precision::nanosecond: return format_time_point_impl(fields, format, month, month_abbrev, weekday, weekday_abbrev, am_pm, decimal_mark); default: clock_abort("Internal error: Unexpected precision."); } } case clock_name::naive: { switch (parse_precision(precision_int)) { case precision::day: return format_time_point_impl(fields, format, month, month_abbrev, weekday, weekday_abbrev, am_pm, decimal_mark); case precision::hour: return format_time_point_impl(fields, format, month, month_abbrev, weekday, weekday_abbrev, am_pm, decimal_mark); case precision::minute: return format_time_point_impl(fields, format, month, month_abbrev, weekday, weekday_abbrev, am_pm, decimal_mark); case precision::second: return format_time_point_impl(fields, format, month, month_abbrev, weekday, weekday_abbrev, am_pm, decimal_mark); case precision::millisecond: return format_time_point_impl(fields, format, month, month_abbrev, weekday, weekday_abbrev, am_pm, decimal_mark); case precision::microsecond: return format_time_point_impl(fields, format, month, month_abbrev, weekday, weekday_abbrev, am_pm, decimal_mark); case precision::nanosecond: return format_time_point_impl(fields, format, month, month_abbrev, weekday, weekday_abbrev, am_pm, decimal_mark); default: clock_abort("Internal error: Unexpected precision."); } } default: clock_abort("Internal error: Unexpected clock."); } } // ----------------------------------------------------------------------------- template cpp11::writable::strings format_zoned_time_impl(cpp11::list_of& fields, const cpp11::strings& zone, const bool& abbreviate_zone, const cpp11::strings& format, const cpp11::strings& month, const cpp11::strings& month_abbrev, const cpp11::strings& weekday, const cpp11::strings& weekday_abbrev, const cpp11::strings& am_pm, const cpp11::strings& decimal_mark) { using Duration = typename ClockDuration::chrono_duration; const ClockDuration x{fields}; const r_ssize size = x.size(); cpp11::writable::strings out(size); if (format.size() != 1) { clock_abort("`format` must have size 1."); } const std::string string_format(format[0]); const char* c_format = string_format.c_str(); zone_size_validate(zone); const std::string zone_name = cpp11::r_string(zone[0]); const date::time_zone* p_time_zone = zone_name_load(zone_name); // Default printable zone name to full provided zone name std::string zone_name_print = (zone_name.size() == 0) ? zone_name_current() : zone_name; std::ostringstream stream; stream.imbue(std::locale::classic()); std::string month_names[24]; const std::pair& month_names_pair = fill_month_names( month, month_abbrev, month_names ); std::string weekday_names[14]; const std::pair& weekday_names_pair = fill_weekday_names( weekday, weekday_abbrev, weekday_names ); std::string ampm_names[2]; const std::pair& ampm_names_pair = fill_ampm_names( am_pm, ampm_names ); const std::string decimal_mark_string = decimal_mark[0]; const char* decimal_mark_char = decimal_mark_string.c_str(); rclock::failures fail{}; for (r_ssize i = 0; i < size; ++i) { if (x.is_na(i)) { SET_STRING_ELT(out, i, r_chr_na); continue; } const Duration duration = x[i]; const date::sys_time stp{duration}; const date::sys_info info = rclock::get_info(stp, p_time_zone); const std::chrono::seconds offset = info.offset; if (abbreviate_zone) { zone_name_print = info.abbrev; } date::local_time ltp{stp.time_since_epoch() + info.offset}; stream.str(std::string()); stream.clear(); clock_to_stream( stream, c_format, ltp, month_names_pair, weekday_names_pair, ampm_names_pair, decimal_mark_char, &zone_name_print, &offset ); if (stream.fail()) { fail.write(i); SET_STRING_ELT(out, i, r_chr_na); continue; } std::string str = stream.str(); SET_STRING_ELT(out, i, Rf_mkCharLenCE(str.c_str(), str.size(), CE_UTF8)); } if (fail.any_failures()) { fail.warn_format(); } return out; } [[cpp11::register]] cpp11::writable::strings format_zoned_time_cpp(cpp11::list_of fields, const cpp11::strings& zone, const bool& abbreviate_zone, const cpp11::strings& format, const cpp11::integers& precision_int, const cpp11::strings& month, const cpp11::strings& month_abbrev, const cpp11::strings& weekday, const cpp11::strings& weekday_abbrev, const cpp11::strings& am_pm, const cpp11::strings& decimal_mark) { using namespace rclock; switch (parse_precision(precision_int)) { case precision::second: return format_zoned_time_impl(fields, zone, abbreviate_zone, format, month, month_abbrev, weekday, weekday_abbrev, am_pm, decimal_mark); case precision::millisecond: return format_zoned_time_impl(fields, zone, abbreviate_zone, format, month, month_abbrev, weekday, weekday_abbrev, am_pm, decimal_mark); case precision::microsecond: return format_zoned_time_impl(fields, zone, abbreviate_zone, format, month, month_abbrev, weekday, weekday_abbrev, am_pm, decimal_mark); case precision::nanosecond: return format_zoned_time_impl(fields, zone, abbreviate_zone, format, month, month_abbrev, weekday, weekday_abbrev, am_pm, decimal_mark); default: clock_abort("Internal error: Unexpected precision."); } } clock/src/quarterly-year-quarter-day.cpp0000644000176200001440000004367214427233200020073 0ustar liggesusers#include "quarterly-year-quarter-day.h" #include "quarterly-shim.h" #include "calendar.h" #include "duration.h" #include "enums.h" #include "get.h" #include "rcrd.h" // ----------------------------------------------------------------------------- [[cpp11::register]] SEXP new_year_quarter_day_from_fields(SEXP fields, const cpp11::integers& precision_int, SEXP start, SEXP names) { const enum precision precision_val = parse_precision(precision_int); const r_ssize n_fields = Rf_xlength(fields); r_ssize n; switch (precision_val) { case precision::year: n = 1; break; case precision::quarter: n = 2; break; case precision::day: n = 3; break; case precision::hour: n = 4; break; case precision::minute: n = 5; break; case precision::second: n = 6; break; case precision::millisecond: n = 7; break; case precision::microsecond: n = 7; break; case precision::nanosecond: n = 7; break; default: never_reached("new_year_quarter_day_from_fields"); } if (n != n_fields) { clock_abort("With the given precision, `fields` must have length %i, not %i.", n, n_fields); } SEXP out = PROTECT(new_clock_rcrd_from_fields(fields, names, classes_year_quarter_day)); Rf_setAttrib(out, syms_precision, precision_int); Rf_setAttrib(out, syms_start, start); UNPROTECT(1); return out; } // ----------------------------------------------------------------------------- [[cpp11::register]] SEXP year_quarter_day_restore(SEXP x, SEXP to) { SEXP precision = Rf_getAttrib(to, syms_precision); SEXP start = Rf_getAttrib(to, syms_start); SEXP out = PROTECT(clock_rcrd_restore(x, to, classes_year_quarter_day)); Rf_setAttrib(out, syms_precision, precision); Rf_setAttrib(out, syms_start, start); UNPROTECT(1); return out; } // ----------------------------------------------------------------------------- [[cpp11::register]] cpp11::writable::strings format_year_quarter_day_cpp(cpp11::list_of fields, const cpp11::integers& precision_int, const cpp11::integers& start_int) { using namespace rclock; const quarterly::start start = parse_quarterly_start(start_int); cpp11::integers year = rquarterly::get_year(fields); cpp11::integers quarter = rquarterly::get_quarter(fields); cpp11::integers day = rquarterly::get_day(fields); cpp11::integers hour = rquarterly::get_hour(fields); cpp11::integers minute = rquarterly::get_minute(fields); cpp11::integers second = rquarterly::get_second(fields); cpp11::integers subsecond = rquarterly::get_subsecond(fields); rquarterly::y y{year, start}; rquarterly::yqn yqn{year, quarter, start}; rquarterly::yqnqd yqnqd{year, quarter, day, start}; rquarterly::yqnqdh yqnqdh{year, quarter, day, hour, start}; rquarterly::yqnqdhm yqnqdhm{year, quarter, day, hour, minute, start}; rquarterly::yqnqdhms yqnqdhms{year, quarter, day, hour, minute, second, start}; rquarterly::yqnqdhmss yqnqdhmss1{year, quarter, day, hour, minute, second, subsecond, start}; rquarterly::yqnqdhmss yqnqdhmss2{year, quarter, day, hour, minute, second, subsecond, start}; rquarterly::yqnqdhmss yqnqdhmss3{year, quarter, day, hour, minute, second, subsecond, start}; switch (parse_precision(precision_int)) { case precision::year: return format_calendar_impl(y); case precision::quarter: return format_calendar_impl(yqn); case precision::day: return format_calendar_impl(yqnqd); case precision::hour: return format_calendar_impl(yqnqdh); case precision::minute: return format_calendar_impl(yqnqdhm); case precision::second: return format_calendar_impl(yqnqdhms); case precision::millisecond: return format_calendar_impl(yqnqdhmss1); case precision::microsecond: return format_calendar_impl(yqnqdhmss2); case precision::nanosecond: return format_calendar_impl(yqnqdhmss3); default: clock_abort("Internal error: Invalid precision."); } never_reached("format_year_quarter_day_cpp"); } // ----------------------------------------------------------------------------- [[cpp11::register]] cpp11::writable::logicals invalid_detect_year_quarter_day_cpp(const cpp11::integers& year, const cpp11::integers& quarter, const cpp11::integers& day, const cpp11::integers& start_int) { const quarterly::start start = parse_quarterly_start(start_int); rclock::rquarterly::yqnqd x{year, quarter, day, start}; const r_ssize size = x.size(); cpp11::writable::logicals out(size); for (r_ssize i = 0; i < size; ++i) { if (x.is_na(i)) { out[i] = false; } else { out[i] = !x.to_year_quarternum_quarterday(i).ok(); } } return out; } // ----------------------------------------------------------------------------- [[cpp11::register]] bool invalid_any_year_quarter_day_cpp(const cpp11::integers& year, const cpp11::integers& quarter, const cpp11::integers& day, const cpp11::integers& start_int) { const quarterly::start start = parse_quarterly_start(start_int); rclock::rquarterly::yqnqd x{year, quarter, day, start}; const r_ssize size = x.size(); for (r_ssize i = 0; i < size; ++i) { if (!x.is_na(i) && !x.to_year_quarternum_quarterday(i).ok()) { return true; } } return false; } // ----------------------------------------------------------------------------- [[cpp11::register]] int invalid_count_year_quarter_day_cpp(const cpp11::integers& year, const cpp11::integers& quarter, const cpp11::integers& day, const cpp11::integers& start_int) { const quarterly::start start = parse_quarterly_start(start_int); rclock::rquarterly::yqnqd x{year, quarter, day, start}; const r_ssize size = x.size(); int count = 0; for (r_ssize i = 0; i < size; ++i) { count += !x.is_na(i) && !x.to_year_quarternum_quarterday(i).ok(); } return count; } // ----------------------------------------------------------------------------- [[cpp11::register]] cpp11::writable::list invalid_resolve_year_quarter_day_cpp(cpp11::list_of fields, const cpp11::integers& precision_int, const cpp11::integers& start_int, const cpp11::strings& invalid_string, const cpp11::sexp& call) { using namespace rclock; const quarterly::start start = parse_quarterly_start(start_int); const enum invalid invalid_val = parse_invalid(invalid_string); cpp11::integers year = rquarterly::get_year(fields); cpp11::integers quarter = rquarterly::get_quarter(fields); cpp11::integers day = rquarterly::get_day(fields); cpp11::integers hour = rquarterly::get_hour(fields); cpp11::integers minute = rquarterly::get_minute(fields); cpp11::integers second = rquarterly::get_second(fields); cpp11::integers subsecond = rquarterly::get_subsecond(fields); rquarterly::yqnqd yqnqd{year, quarter, day, start}; rquarterly::yqnqdh yqnqdh{year, quarter, day, hour, start}; rquarterly::yqnqdhm yqnqdhm{year, quarter, day, hour, minute, start}; rquarterly::yqnqdhms yqnqdhms{year, quarter, day, hour, minute, second, start}; rquarterly::yqnqdhmss yqnqdhmss1{year, quarter, day, hour, minute, second, subsecond, start}; rquarterly::yqnqdhmss yqnqdhmss2{year, quarter, day, hour, minute, second, subsecond, start}; rquarterly::yqnqdhmss yqnqdhmss3{year, quarter, day, hour, minute, second, subsecond, start}; switch (parse_precision(precision_int)) { case precision::day: return invalid_resolve_calendar_impl(yqnqd, invalid_val, call); case precision::hour: return invalid_resolve_calendar_impl(yqnqdh, invalid_val, call); case precision::minute: return invalid_resolve_calendar_impl(yqnqdhm, invalid_val, call); case precision::second: return invalid_resolve_calendar_impl(yqnqdhms, invalid_val, call); case precision::millisecond: return invalid_resolve_calendar_impl(yqnqdhmss1, invalid_val, call); case precision::microsecond: return invalid_resolve_calendar_impl(yqnqdhmss2, invalid_val, call); case precision::nanosecond: return invalid_resolve_calendar_impl(yqnqdhmss3, invalid_val, call); default: never_reached("invalid_resolve_year_quarter_day_cpp"); } } // ----------------------------------------------------------------------------- [[cpp11::register]] cpp11::writable::integers get_year_quarter_day_last_cpp(const cpp11::integers& year, const cpp11::integers& quarter, const cpp11::integers& start_int) { const quarterly::start start = parse_quarterly_start(start_int); rclock::rquarterly::yqn x{year, quarter, start}; const r_ssize size = x.size(); cpp11::writable::integers out(size); for (r_ssize i = 0; i < size; ++i) { if (x.is_na(i)) { out[i] = r_int_na; } else { const auto elt = x.to_year_quarternum(i) / quarterly::last; out[i] = static_cast(static_cast(elt.quarterday())); } } return out; } // ----------------------------------------------------------------------------- [[cpp11::register]] cpp11::writable::list year_quarter_day_plus_years_cpp(const cpp11::integers& year, const cpp11::integers& start_int, cpp11::list_of fields_n) { const quarterly::start start = parse_quarterly_start(start_int); rclock::rquarterly::y x{year, start}; rclock::duration::years n{fields_n}; return calendar_plus_duration_impl(x, n); } [[cpp11::register]] cpp11::writable::list year_quarter_day_plus_quarters_cpp(const cpp11::integers& year, const cpp11::integers& quarter, const cpp11::integers& start_int, cpp11::list_of fields_n) { const quarterly::start start = parse_quarterly_start(start_int); rclock::rquarterly::yqn x{year, quarter, start}; rclock::duration::quarters n{fields_n}; return calendar_plus_duration_impl(x, n); } // ----------------------------------------------------------------------------- [[cpp11::register]] cpp11::writable::list as_sys_time_year_quarter_day_cpp(cpp11::list_of fields, const cpp11::integers& precision_int, const cpp11::integers& start_int) { using namespace rclock; const quarterly::start start = parse_quarterly_start(start_int); cpp11::integers year = rquarterly::get_year(fields); cpp11::integers quarter = rquarterly::get_quarter(fields); cpp11::integers day = rquarterly::get_day(fields); cpp11::integers hour = rquarterly::get_hour(fields); cpp11::integers minute = rquarterly::get_minute(fields); cpp11::integers second = rquarterly::get_second(fields); cpp11::integers subsecond = rquarterly::get_subsecond(fields); rquarterly::yqnqd yqnqd{year, quarter, day, start}; rquarterly::yqnqdh yqnqdh{year, quarter, day, hour, start}; rquarterly::yqnqdhm yqnqdhm{year, quarter, day, hour, minute, start}; rquarterly::yqnqdhms yqnqdhms{year, quarter, day, hour, minute, second, start}; rquarterly::yqnqdhmss yqnqdhmss1{year, quarter, day, hour, minute, second, subsecond, start}; rquarterly::yqnqdhmss yqnqdhmss2{year, quarter, day, hour, minute, second, subsecond, start}; rquarterly::yqnqdhmss yqnqdhmss3{year, quarter, day, hour, minute, second, subsecond, start}; switch (parse_precision(precision_int)) { case precision::day: return as_sys_time_from_calendar_impl(yqnqd); case precision::hour: return as_sys_time_from_calendar_impl(yqnqdh); case precision::minute: return as_sys_time_from_calendar_impl(yqnqdhm); case precision::second: return as_sys_time_from_calendar_impl(yqnqdhms); case precision::millisecond: return as_sys_time_from_calendar_impl(yqnqdhmss1); case precision::microsecond: return as_sys_time_from_calendar_impl(yqnqdhmss2); case precision::nanosecond: return as_sys_time_from_calendar_impl(yqnqdhmss3); default: { const enum precision precision_val = parse_precision(precision_int); const std::string precision_string = precision_to_cpp_string(precision_val); std::string message = "Can't convert to a time point from a calendar with '" + precision_string + "' precision. " + "A minimum of 'day' precision is required."; clock_abort(message.c_str()); } } never_reached("as_sys_time_year_quarter_day_cpp"); } // ----------------------------------------------------------------------------- template cpp11::writable::list as_year_quarter_day_from_sys_time_impl(cpp11::list_of& fields, quarterly::start start) { using Duration = typename ClockDuration::chrono_duration; const ClockDuration x{fields}; const r_ssize size = x.size(); Calendar out(size, start); for (r_ssize i = 0; i < size; ++i) { if (x.is_na(i)) { out.assign_na(i); } else { const Duration elt = x[i]; const date::sys_time elt_st{elt}; out.assign_sys_time(elt_st, i); } } return out.to_list(); } [[cpp11::register]] cpp11::writable::list as_year_quarter_day_from_sys_time_cpp(cpp11::list_of fields, const cpp11::integers& precision_int, const cpp11::integers& start_int) { using namespace rclock; const quarterly::start start = parse_quarterly_start(start_int); switch (parse_precision(precision_int)) { case precision::day: return as_year_quarter_day_from_sys_time_impl(fields, start); case precision::hour: return as_year_quarter_day_from_sys_time_impl(fields, start); case precision::minute: return as_year_quarter_day_from_sys_time_impl(fields, start); case precision::second: return as_year_quarter_day_from_sys_time_impl(fields, start); case precision::millisecond: return as_year_quarter_day_from_sys_time_impl>(fields, start); case precision::microsecond: return as_year_quarter_day_from_sys_time_impl>(fields, start); case precision::nanosecond: return as_year_quarter_day_from_sys_time_impl>(fields, start); default: clock_abort("Internal error: Invalid precision."); } never_reached("as_year_quarter_day_from_sys_time_cpp"); } // ----------------------------------------------------------------------------- static inline cpp11::writable::list year_minus_year_impl(const rclock::rquarterly::y& x, const rclock::rquarterly::y& y) { const r_ssize size = x.size(); rclock::duration::years out(size); for (r_ssize i = 0; i < size; ++i) { if (x.is_na(i) || y.is_na(i)) { out.assign_na(i); continue; } out.assign(x.to_year(i) - y.to_year(i), i); } return out.to_list(); } static inline cpp11::writable::list year_quarter_minus_year_quarter_impl(const rclock::rquarterly::yqn& x, const rclock::rquarterly::yqn& y) { const r_ssize size = x.size(); rclock::duration::quarters out(size); for (r_ssize i = 0; i < size; ++i) { if (x.is_na(i) || y.is_na(i)) { out.assign_na(i); continue; } out.assign(x.to_year_quarternum(i) - y.to_year_quarternum(i), i); } return out.to_list(); } [[cpp11::register]] cpp11::writable::list year_quarter_day_minus_year_quarter_day_cpp(cpp11::list_of x, cpp11::list_of y, const cpp11::integers& precision_int, const cpp11::integers& start_int) { quarterly::start start = parse_quarterly_start(start_int); const cpp11::integers x_year = rclock::rquarterly::get_year(x); const cpp11::integers x_quarter = rclock::rquarterly::get_quarter(x); const cpp11::integers y_year = rclock::rquarterly::get_year(y); const cpp11::integers y_quarter = rclock::rquarterly::get_quarter(y); const rclock::rquarterly::y x_y{x_year, start}; const rclock::rquarterly::yqn x_yqn{x_year, x_quarter, start}; const rclock::rquarterly::y y_y{y_year, start}; const rclock::rquarterly::yqn y_yqn{y_year, y_quarter, start}; switch (parse_precision(precision_int)) { case precision::year: return year_minus_year_impl(x_y, y_y); case precision::quarter: return year_quarter_minus_year_quarter_impl(x_yqn, y_yqn); default: clock_abort("Internal error: Invalid precision."); } never_reached("year_quarter_day_minus_year_quarter_day_cpp"); } // ----------------------------------------------------------------------------- [[cpp11::register]] cpp11::writable::logicals year_quarter_day_leap_year_cpp(const cpp11::integers& year, const cpp11::integers& start_int) { const quarterly::start start = parse_quarterly_start(start_int); const r_ssize size = year.size(); cpp11::writable::logicals out(size); for (r_ssize i = 0; i < size; ++i) { const int elt = year[i]; if (elt == r_int_na) { out[i] = r_lgl_na; } else { out[i] = rclock::rquarterly::quarterly_shim::year{elt, start}.is_leap(); } } return out; } clock/src/week-year-week-day.cpp0000644000176200001440000003725314427233200016244 0ustar liggesusers#include "week-year-week-day.h" #include "week-shim.h" #include "calendar.h" #include "duration.h" #include "enums.h" #include "get.h" #include "rcrd.h" // ----------------------------------------------------------------------------- [[cpp11::register]] SEXP new_year_week_day_from_fields(SEXP fields, const cpp11::integers& precision_int, SEXP start, SEXP names) { const enum precision precision_val = parse_precision(precision_int); const r_ssize n_fields = Rf_xlength(fields); r_ssize n; switch (precision_val) { case precision::year: n = 1; break; case precision::week: n = 2; break; case precision::day: n = 3; break; case precision::hour: n = 4; break; case precision::minute: n = 5; break; case precision::second: n = 6; break; case precision::millisecond: n = 7; break; case precision::microsecond: n = 7; break; case precision::nanosecond: n = 7; break; default: never_reached("new_year_week_day_from_fields"); } if (n != n_fields) { clock_abort("With the given precision, `fields` must have length %i, not %i.", n, n_fields); } SEXP out = PROTECT(new_clock_rcrd_from_fields(fields, names, classes_year_week_day)); Rf_setAttrib(out, syms_precision, precision_int); Rf_setAttrib(out, syms_start, start); UNPROTECT(1); return out; } // ----------------------------------------------------------------------------- [[cpp11::register]] SEXP year_week_day_restore(SEXP x, SEXP to) { SEXP precision = Rf_getAttrib(to, syms_precision); SEXP start = Rf_getAttrib(to, syms_start); SEXP out = PROTECT(clock_rcrd_restore(x, to, classes_year_week_day)); Rf_setAttrib(out, syms_precision, precision); Rf_setAttrib(out, syms_start, start); UNPROTECT(1); return out; } // ----------------------------------------------------------------------------- [[cpp11::register]] cpp11::writable::strings format_year_week_day_cpp(cpp11::list_of fields, const cpp11::integers& precision_int, const cpp11::integers& start_int) { using namespace rclock; const week::start start = parse_week_start(start_int); cpp11::integers year = rweek::get_year(fields); cpp11::integers week = rweek::get_week(fields); cpp11::integers day = rweek::get_day(fields); cpp11::integers hour = rweek::get_hour(fields); cpp11::integers minute = rweek::get_minute(fields); cpp11::integers second = rweek::get_second(fields); cpp11::integers subsecond = rweek::get_subsecond(fields); rweek::y y{year, start}; rweek::ywn ywn{year, week, start}; rweek::ywnwd ywnwd{year, week, day, start}; rweek::ywnwdh ywnwdh{year, week, day, hour, start}; rweek::ywnwdhm ywnwdhm{year, week, day, hour, minute, start}; rweek::ywnwdhms ywnwdhms{year, week, day, hour, minute, second, start}; rweek::ywnwdhmss ywnwdhmss1{year, week, day, hour, minute, second, subsecond, start}; rweek::ywnwdhmss ywnwdhmss2{year, week, day, hour, minute, second, subsecond, start}; rweek::ywnwdhmss ywnwdhmss3{year, week, day, hour, minute, second, subsecond, start}; switch (parse_precision(precision_int)) { case precision::year: return format_calendar_impl(y); case precision::week: return format_calendar_impl(ywn); case precision::day: return format_calendar_impl(ywnwd); case precision::hour: return format_calendar_impl(ywnwdh); case precision::minute: return format_calendar_impl(ywnwdhm); case precision::second: return format_calendar_impl(ywnwdhms); case precision::millisecond: return format_calendar_impl(ywnwdhmss1); case precision::microsecond: return format_calendar_impl(ywnwdhmss2); case precision::nanosecond: return format_calendar_impl(ywnwdhmss3); default: clock_abort("Internal error: Invalid precision."); } never_reached("format_year_week_day_cpp"); } // ----------------------------------------------------------------------------- [[cpp11::register]] cpp11::writable::logicals invalid_detect_year_week_day_cpp(const cpp11::integers& year, const cpp11::integers& week, const cpp11::integers& start_int) { const week::start start = parse_week_start(start_int); rclock::rweek::ywn x{year, week, start}; const r_ssize size = x.size(); cpp11::writable::logicals out(size); for (r_ssize i = 0; i < size; ++i) { if (x.is_na(i)) { out[i] = false; } else { out[i] = !x.to_year_weeknum(i).ok(); } } return out; } // ----------------------------------------------------------------------------- [[cpp11::register]] bool invalid_any_year_week_day_cpp(const cpp11::integers& year, const cpp11::integers& week, const cpp11::integers& start_int) { const week::start start = parse_week_start(start_int); rclock::rweek::ywn x{year, week, start}; const r_ssize size = x.size(); for (r_ssize i = 0; i < size; ++i) { if (!x.is_na(i) && !x.to_year_weeknum(i).ok()) { return true; } } return false; } // ----------------------------------------------------------------------------- [[cpp11::register]] int invalid_count_year_week_day_cpp(const cpp11::integers& year, const cpp11::integers& week, const cpp11::integers& start_int) { const week::start start = parse_week_start(start_int); rclock::rweek::ywn x{year, week, start}; const r_ssize size = x.size(); int count = 0; for (r_ssize i = 0; i < size; ++i) { count += !x.is_na(i) && !x.to_year_weeknum(i).ok(); } return count; } // ----------------------------------------------------------------------------- [[cpp11::register]] cpp11::writable::list invalid_resolve_year_week_day_cpp(cpp11::list_of fields, const cpp11::integers& precision_int, const cpp11::integers& start_int, const cpp11::strings& invalid_string, const cpp11::sexp& call) { using namespace rclock; const week::start start = parse_week_start(start_int); const enum invalid invalid_val = parse_invalid(invalid_string); cpp11::integers year = rweek::get_year(fields); cpp11::integers week = rweek::get_week(fields); cpp11::integers day = rweek::get_day(fields); cpp11::integers hour = rweek::get_hour(fields); cpp11::integers minute = rweek::get_minute(fields); cpp11::integers second = rweek::get_second(fields); cpp11::integers subsecond = rweek::get_subsecond(fields); rweek::ywn ywn{year, week, start}; rweek::ywnwd ywnwd{year, week, day, start}; rweek::ywnwdh ywnwdh{year, week, day, hour, start}; rweek::ywnwdhm ywnwdhm{year, week, day, hour, minute, start}; rweek::ywnwdhms ywnwdhms{year, week, day, hour, minute, second, start}; rweek::ywnwdhmss ywnwdhmss1{year, week, day, hour, minute, second, subsecond, start}; rweek::ywnwdhmss ywnwdhmss2{year, week, day, hour, minute, second, subsecond, start}; rweek::ywnwdhmss ywnwdhmss3{year, week, day, hour, minute, second, subsecond, start}; switch (parse_precision(precision_int)) { case precision::week: return invalid_resolve_calendar_impl(ywn, invalid_val, call); case precision::day: return invalid_resolve_calendar_impl(ywnwd, invalid_val, call); case precision::hour: return invalid_resolve_calendar_impl(ywnwdh, invalid_val, call); case precision::minute: return invalid_resolve_calendar_impl(ywnwdhm, invalid_val, call); case precision::second: return invalid_resolve_calendar_impl(ywnwdhms, invalid_val, call); case precision::millisecond: return invalid_resolve_calendar_impl(ywnwdhmss1, invalid_val, call); case precision::microsecond: return invalid_resolve_calendar_impl(ywnwdhmss2, invalid_val, call); case precision::nanosecond: return invalid_resolve_calendar_impl(ywnwdhmss3, invalid_val, call); default: never_reached("invalid_resolve_year_week_day_cpp"); } } // ----------------------------------------------------------------------------- [[cpp11::register]] cpp11::writable::integers get_year_week_day_last_cpp(const cpp11::integers& year, const cpp11::integers& start_int) { const week::start start = parse_week_start(start_int); rclock::rweek::y x{year, start}; const r_ssize size = x.size(); cpp11::writable::integers out(size); for (r_ssize i = 0; i < size; ++i) { if (x.is_na(i)) { out[i] = r_int_na; } else { const auto elt = x.to_year(i) / week::last; out[i] = static_cast(static_cast(elt.weeknum())); } } return out; } // ----------------------------------------------------------------------------- [[cpp11::register]] cpp11::writable::list year_week_day_plus_years_cpp(const cpp11::integers& year, const cpp11::integers& start_int, cpp11::list_of fields_n) { const week::start start = parse_week_start(start_int); rclock::rweek::y x{year, start}; rclock::duration::years n{fields_n}; return calendar_plus_duration_impl(x, n); } // ----------------------------------------------------------------------------- [[cpp11::register]] cpp11::writable::list as_sys_time_year_week_day_cpp(cpp11::list_of fields, const cpp11::integers& precision_int, const cpp11::integers& start_int) { using namespace rclock; const week::start start = parse_week_start(start_int); cpp11::integers year = rweek::get_year(fields); cpp11::integers week = rweek::get_week(fields); cpp11::integers day = rweek::get_day(fields); cpp11::integers hour = rweek::get_hour(fields); cpp11::integers minute = rweek::get_minute(fields); cpp11::integers second = rweek::get_second(fields); cpp11::integers subsecond = rweek::get_subsecond(fields); rweek::ywnwd ywnwd{year, week, day, start}; rweek::ywnwdh ywnwdh{year, week, day, hour, start}; rweek::ywnwdhm ywnwdhm{year, week, day, hour, minute, start}; rweek::ywnwdhms ywnwdhms{year, week, day, hour, minute, second, start}; rweek::ywnwdhmss ywnwdhmss1{year, week, day, hour, minute, second, subsecond, start}; rweek::ywnwdhmss ywnwdhmss2{year, week, day, hour, minute, second, subsecond, start}; rweek::ywnwdhmss ywnwdhmss3{year, week, day, hour, minute, second, subsecond, start}; switch (parse_precision(precision_int)) { case precision::day: return as_sys_time_from_calendar_impl(ywnwd); case precision::hour: return as_sys_time_from_calendar_impl(ywnwdh); case precision::minute: return as_sys_time_from_calendar_impl(ywnwdhm); case precision::second: return as_sys_time_from_calendar_impl(ywnwdhms); case precision::millisecond: return as_sys_time_from_calendar_impl(ywnwdhmss1); case precision::microsecond: return as_sys_time_from_calendar_impl(ywnwdhmss2); case precision::nanosecond: return as_sys_time_from_calendar_impl(ywnwdhmss3); default: { const enum precision precision_val = parse_precision(precision_int); const std::string precision_string = precision_to_cpp_string(precision_val); std::string message = "Can't convert to a time point from a calendar with '" + precision_string + "' precision. " + "A minimum of 'day' precision is required."; clock_abort(message.c_str()); } } never_reached("as_sys_time_year_week_day_cpp"); } // ----------------------------------------------------------------------------- template cpp11::writable::list as_year_week_day_from_sys_time_impl(cpp11::list_of& fields, week::start start) { using Duration = typename ClockDuration::chrono_duration; const ClockDuration x{fields}; const r_ssize size = x.size(); Calendar out(size, start); for (r_ssize i = 0; i < size; ++i) { if (x.is_na(i)) { out.assign_na(i); } else { const Duration elt = x[i]; const date::sys_time elt_st{elt}; out.assign_sys_time(elt_st, i); } } return out.to_list(); } [[cpp11::register]] cpp11::writable::list as_year_week_day_from_sys_time_cpp(cpp11::list_of fields, const cpp11::integers& precision_int, const cpp11::integers& start_int) { using namespace rclock; const week::start start = parse_week_start(start_int); switch (parse_precision(precision_int)) { case precision::day: return as_year_week_day_from_sys_time_impl(fields, start); case precision::hour: return as_year_week_day_from_sys_time_impl(fields, start); case precision::minute: return as_year_week_day_from_sys_time_impl(fields, start); case precision::second: return as_year_week_day_from_sys_time_impl(fields, start); case precision::millisecond: return as_year_week_day_from_sys_time_impl>(fields, start); case precision::microsecond: return as_year_week_day_from_sys_time_impl>(fields, start); case precision::nanosecond: return as_year_week_day_from_sys_time_impl>(fields, start); default: clock_abort("Internal error: Invalid precision."); } never_reached("as_year_week_day_from_sys_time_cpp"); } // ----------------------------------------------------------------------------- static inline cpp11::writable::list year_minus_year_impl(const rclock::rweek::y& x, const rclock::rweek::y& y) { const r_ssize size = x.size(); rclock::duration::years out(size); for (r_ssize i = 0; i < size; ++i) { if (x.is_na(i) || y.is_na(i)) { out.assign_na(i); continue; } out.assign(x.to_year(i) - y.to_year(i), i); } return out.to_list(); } [[cpp11::register]] cpp11::writable::list year_week_day_minus_year_week_day_cpp(cpp11::list_of x, cpp11::list_of y, const cpp11::integers& precision_int, const cpp11::integers& start_int) { const week::start start = parse_week_start(start_int); const cpp11::integers x_year = rclock::rweek::get_year(x); const cpp11::integers y_year = rclock::rweek::get_year(y); const rclock::rweek::y x_y{x_year, start}; const rclock::rweek::y y_y{y_year, start}; switch (parse_precision(precision_int)) { case precision::year: return year_minus_year_impl(x_y, y_y); default: clock_abort("Internal error: Invalid precision."); } never_reached("year_week_day_minus_year_week_day_cpp"); } // ----------------------------------------------------------------------------- [[cpp11::register]] cpp11::writable::logicals year_week_day_leap_year_cpp(const cpp11::integers& year, const cpp11::integers& start_int) { const week::start start = parse_week_start(start_int); const r_ssize size = year.size(); cpp11::writable::logicals out(size); for (r_ssize i = 0; i < size; ++i) { const int elt = year[i]; if (elt == r_int_na) { out[i] = r_lgl_na; } else { out[i] = rclock::rweek::week_shim::year{elt, start}.is_leap(); } } return out; } clock/src/zone.cpp0000644000176200001440000000643514042042347013623 0ustar liggesusers#include "zone.h" #include "utils.h" #include /* * Brought over from lubridate/timechange * https://github.com/vspinu/timechange/blob/9c1f769a6c85899665af8b86e43115d392c09989/src/tzone.cpp#L4 */ // ----------------------------------------------------------------------------- [[cpp11::register]] cpp11::writable::logicals zone_is_valid(const cpp11::strings& zone) { zone_size_validate(zone); cpp11::r_string zone_name_r(zone[0]); std::string zone_name(zone_name_r); // Local time if (zone_name.size() == 0) { return cpp11::writable::logicals({cpp11::r_bool(true)}); } const date::time_zone* p_time_zone; if (tzdb::locate_zone(zone_name, p_time_zone)) { return cpp11::writable::logicals({cpp11::r_bool(true)}); } else { return cpp11::writable::logicals({cpp11::r_bool(false)}); } } // ----------------------------------------------------------------------------- [[cpp11::register]] cpp11::writable::strings zone_current() { return cpp11::writable::strings({zone_name_current()}); } // ----------------------------------------------------------------------------- const date::time_zone* zone_name_load_try(const std::string& zone_name); // [[ include("zone.h") ]] const date::time_zone* zone_name_load(const std::string& zone_name) { if (zone_name.size() == 0) { // We look up the local time zone using R's `Sys.timezone()` // or the `TZ` envvar when an empty string is the input. // This is consistent with lubridate. std::string current_zone_name = zone_name_current(); return zone_name_load_try(current_zone_name); } else { return zone_name_load_try(zone_name); } } const date::time_zone* zone_name_load_try(const std::string& zone_name) { const date::time_zone* p_time_zone; if (!tzdb::locate_zone(zone_name, p_time_zone)) { clock_abort("'%s' not found in the timezone database.", zone_name.c_str()); } return p_time_zone; } static std::string zone_name_system(); // [[ include("zone.h") ]] std::string zone_name_current() { const char* tz_env = std::getenv("TZ"); if (tz_env == NULL) { // Unset, use system tz return zone_name_system(); } if (strlen(tz_env) == 0) { // If set but empty, R behaves in a system specific way and there is no way // to infer local time zone. cpp11::warning("Environment variable `TZ` is set to \"\". Using system time zone."); return zone_name_system(); } std::string out(tz_env); return out; } static std::string zone_name_system_get(); static std::string zone_name_system() { // Once per session static std::string ZONE_NAME_SYSTEM = zone_name_system_get(); return ZONE_NAME_SYSTEM; } static std::string zone_name_system_get() { auto sys_timezone = cpp11::package("base")["Sys.timezone"]; cpp11::sexp result = sys_timezone(); if (!clock_is_string(result)) { cpp11::warning("Unexpected result from `Sys.timezone()`, returning 'UTC'."); return "UTC"; } cpp11::strings timezone = cpp11::as_cpp(result); cpp11::r_string tz = timezone[0]; SEXP tz_char(tz); if (tz_char == r_chr_na || strlen(CHAR(tz_char)) == 0) { cpp11::warning( "System timezone name is unknown. " "Please set the environment variable `TZ`. " "Defaulting to 'UTC'." ); return "UTC"; } std::string out(tz); return out; } clock/src/zone.h0000644000176200001440000000064514042042347013265 0ustar liggesusers#ifndef CLOCK_ZONE_H #define CLOCK_ZONE_H #include "clock.h" #include "utils.h" static inline void zone_size_validate(const cpp11::strings& zone) { if (zone.size() != 1) { clock_abort("`zone` must be a single string."); } } std::string zone_name_current(); /* * Load a time zone name, or throw an R error if it can't be loaded */ const date::time_zone* zone_name_load(const std::string& zone_name); #endif clock/src/weekday.cpp0000644000176200001440000000547414422221153014277 0ustar liggesusers#include "duration.h" #include "get.h" static inline unsigned reencode_western_to_c(const unsigned& x) { return x - 1; } static inline unsigned reencode_c_to_western(const unsigned& x) { return x + 1; } [[cpp11::register]] cpp11::writable::integers weekday_add_days_cpp(const cpp11::integers& x, cpp11::list_of n_fields) { const r_ssize size = x.size(); const rclock::duration::days n{n_fields}; cpp11::writable::integers out(size); for (r_ssize i = 0; i < size; ++i) { const int elt = x[i]; if (elt == r_int_na || n.is_na(i)) { out[i] = r_int_na; continue; } const unsigned weekday = reencode_western_to_c(static_cast(elt)); const date::weekday out_elt = date::weekday{weekday} + n[i]; out[i] = static_cast(reencode_c_to_western(out_elt.c_encoding())); } return out; } [[cpp11::register]] cpp11::writable::list weekday_minus_weekday_cpp(const cpp11::integers& x, const cpp11::integers& y) { const r_ssize size = x.size(); rclock::duration::days out(size); for (r_ssize i = 0; i < size; ++i) { const int x_elt = x[i]; const int y_elt = y[i]; if (x_elt == r_int_na || y_elt == r_int_na) { out.assign_na(i); continue; } const unsigned x_weekday = reencode_western_to_c(static_cast(x_elt)); const unsigned y_weekday = reencode_western_to_c(static_cast(y_elt)); const date::days out_elt = date::weekday{x_weekday} - date::weekday{y_weekday}; out.assign(out_elt, i); } return out.to_list(); } [[cpp11::register]] cpp11::writable::integers weekday_from_time_point_cpp(cpp11::list_of x_fields) { const rclock::duration::days x{x_fields}; const r_ssize size = x.size(); cpp11::writable::integers out(size); for (r_ssize i = 0; i < size; ++i) { if (x.is_na(i)) { out[i] = r_int_na; continue; } const date::sys_days elt{x[i]}; const date::weekday weekday{elt}; out[i] = static_cast(reencode_c_to_western(weekday.c_encoding())); } return out; } [[cpp11::register]] cpp11::writable::strings format_weekday_cpp(const cpp11::integers& x, const cpp11::strings& labels) { const r_ssize size = x.size(); const SEXP sun = labels[0]; const SEXP mon = labels[1]; const SEXP tue = labels[2]; const SEXP wed = labels[3]; const SEXP thu = labels[4]; const SEXP fri = labels[5]; const SEXP sat = labels[6]; const std::vector abbreviations = { sun, mon, tue, wed, thu, fri, sat }; cpp11::writable::strings out(size); for (r_ssize i = 0; i < size; ++i) { // Stored in Western encoding [1, 7] => [Sun, Sat] const int elt = x[i]; if (elt == r_int_na) { SET_STRING_ELT(out, i, r_chr_na); continue; } SET_STRING_ELT(out, i, abbreviations[elt - 1]); } return out; } clock/src/gregorian-year-day.cpp0000644000176200001440000003012414427233200016323 0ustar liggesusers#include "gregorian-year-day.h" #include "calendar.h" #include "duration.h" #include "enums.h" #include "get.h" #include "rcrd.h" // ----------------------------------------------------------------------------- [[cpp11::register]] SEXP new_year_day_from_fields(SEXP fields, const cpp11::integers& precision_int, SEXP names) { const enum precision precision_val = parse_precision(precision_int); const r_ssize n_fields = Rf_xlength(fields); r_ssize n; switch (precision_val) { case precision::year: n = 1; break; case precision::day: n = 2; break; case precision::hour: n = 3; break; case precision::minute: n = 4; break; case precision::second: n = 5; break; case precision::millisecond: n = 6; break; case precision::microsecond: n = 6; break; case precision::nanosecond: n = 6; break; default: never_reached("new_year_day_from_fields"); } if (n != n_fields) { clock_abort("With the given precision, `fields` must have length %i, not %i.", n, n_fields); } SEXP out = PROTECT(new_clock_rcrd_from_fields(fields, names, classes_year_day)); Rf_setAttrib(out, syms_precision, precision_int); UNPROTECT(1); return out; } // ----------------------------------------------------------------------------- [[cpp11::register]] SEXP year_day_restore(SEXP x, SEXP to) { SEXP precision = Rf_getAttrib(to, syms_precision); SEXP out = PROTECT(clock_rcrd_restore(x, to, classes_year_day)); Rf_setAttrib(out, syms_precision, precision); UNPROTECT(1); return out; } // ----------------------------------------------------------------------------- [[cpp11::register]] cpp11::writable::strings format_year_day_cpp(cpp11::list_of fields, const cpp11::integers& precision_int) { using namespace rclock; cpp11::integers year = yearday::get_year(fields); cpp11::integers day = yearday::get_day(fields); cpp11::integers hour = yearday::get_hour(fields); cpp11::integers minute = yearday::get_minute(fields); cpp11::integers second = yearday::get_second(fields); cpp11::integers subsecond = yearday::get_subsecond(fields); yearday::y y{year}; yearday::yyd yyd{year, day}; yearday::yydh yydh{year, day, hour}; yearday::yydhm yydhm{year, day, hour, minute}; yearday::yydhms yydhms{year, day, hour, minute, second}; yearday::yydhmss yydhmss1{year, day, hour, minute, second, subsecond}; yearday::yydhmss yydhmss2{year, day, hour, minute, second, subsecond}; yearday::yydhmss yydhmss3{year, day, hour, minute, second, subsecond}; switch (parse_precision(precision_int)) { case precision::year: return format_calendar_impl(y); case precision::day: return format_calendar_impl(yyd); case precision::hour: return format_calendar_impl(yydh); case precision::minute: return format_calendar_impl(yydhm); case precision::second: return format_calendar_impl(yydhms); case precision::millisecond: return format_calendar_impl(yydhmss1); case precision::microsecond: return format_calendar_impl(yydhmss2); case precision::nanosecond: return format_calendar_impl(yydhmss3); default: clock_abort("Internal error: Invalid precision."); } never_reached("format_year_day_cpp"); } // ----------------------------------------------------------------------------- [[cpp11::register]] cpp11::writable::logicals invalid_detect_year_day_cpp(const cpp11::integers& year, const cpp11::integers& day) { rclock::yearday::yyd x{year, day}; const r_ssize size = x.size(); cpp11::writable::logicals out(size); for (r_ssize i = 0; i < size; ++i) { if (x.is_na(i)) { out[i] = false; } else { out[i] = !x.to_year_yearday(i).ok(); } } return out; } // ----------------------------------------------------------------------------- [[cpp11::register]] bool invalid_any_year_day_cpp(const cpp11::integers& year, const cpp11::integers& day) { rclock::yearday::yyd x{year, day}; const r_ssize size = x.size(); for (r_ssize i = 0; i < size; ++i) { if (!x.is_na(i) && !x.to_year_yearday(i).ok()) { return true; } } return false; } // ----------------------------------------------------------------------------- [[cpp11::register]] int invalid_count_year_day_cpp(const cpp11::integers& year, const cpp11::integers& day) { rclock::yearday::yyd x{year, day}; const r_ssize size = x.size(); int count = 0; for (r_ssize i = 0; i < size; ++i) { count += !x.is_na(i) && !x.to_year_yearday(i).ok(); } return count; } // ----------------------------------------------------------------------------- [[cpp11::register]] cpp11::writable::list invalid_resolve_year_day_cpp(cpp11::list_of fields, const cpp11::integers& precision_int, const cpp11::strings& invalid_string, const cpp11::sexp& call) { using namespace rclock; const enum invalid invalid_val = parse_invalid(invalid_string); cpp11::integers year = yearday::get_year(fields); cpp11::integers day = yearday::get_day(fields); cpp11::integers hour = yearday::get_hour(fields); cpp11::integers minute = yearday::get_minute(fields); cpp11::integers second = yearday::get_second(fields); cpp11::integers subsecond = yearday::get_subsecond(fields); yearday::yyd yyd{year, day}; yearday::yydh yydh{year, day, hour}; yearday::yydhm yydhm{year, day, hour, minute}; yearday::yydhms yydhms{year, day, hour, minute, second}; yearday::yydhmss yydhmss1{year, day, hour, minute, second, subsecond}; yearday::yydhmss yydhmss2{year, day, hour, minute, second, subsecond}; yearday::yydhmss yydhmss3{year, day, hour, minute, second, subsecond}; switch (parse_precision(precision_int)) { case precision::day: return invalid_resolve_calendar_impl(yyd, invalid_val, call); case precision::hour: return invalid_resolve_calendar_impl(yydh, invalid_val, call); case precision::minute: return invalid_resolve_calendar_impl(yydhm, invalid_val, call); case precision::second: return invalid_resolve_calendar_impl(yydhms, invalid_val, call); case precision::millisecond: return invalid_resolve_calendar_impl(yydhmss1, invalid_val, call); case precision::microsecond: return invalid_resolve_calendar_impl(yydhmss2, invalid_val, call); case precision::nanosecond: return invalid_resolve_calendar_impl(yydhmss3, invalid_val, call); default: never_reached("invalid_resolve_year_day_cpp"); } } // ----------------------------------------------------------------------------- [[cpp11::register]] cpp11::writable::integers get_year_day_last_cpp(const cpp11::integers& year) { rclock::yearday::y x{year}; const r_ssize size = x.size(); cpp11::writable::integers out(size); for (r_ssize i = 0; i < size; ++i) { if (x.is_na(i)) { out[i] = r_int_na; } else { ordinal::year_yearday_last elt = x.to_year(i) / ordinal::last; out[i] = static_cast(static_cast(elt.yearday())); } } return out; } // ----------------------------------------------------------------------------- [[cpp11::register]] cpp11::writable::list year_day_plus_years_cpp(const cpp11::integers& year, cpp11::list_of fields_n) { rclock::yearday::y x{year}; rclock::duration::years n{fields_n}; return calendar_plus_duration_impl(x, n); } // ----------------------------------------------------------------------------- [[cpp11::register]] cpp11::writable::list as_sys_time_year_day_cpp(cpp11::list_of fields, const cpp11::integers& precision_int) { using namespace rclock; cpp11::integers year = yearday::get_year(fields); cpp11::integers day = yearday::get_day(fields); cpp11::integers hour = yearday::get_hour(fields); cpp11::integers minute = yearday::get_minute(fields); cpp11::integers second = yearday::get_second(fields); cpp11::integers subsecond = yearday::get_subsecond(fields); yearday::yyd yyd{year, day}; yearday::yydh yydh{year, day, hour}; yearday::yydhm yydhm{year, day, hour, minute}; yearday::yydhms yydhms{year, day, hour, minute, second}; yearday::yydhmss yydhmss1{year, day, hour, minute, second, subsecond}; yearday::yydhmss yydhmss2{year, day, hour, minute, second, subsecond}; yearday::yydhmss yydhmss3{year, day, hour, minute, second, subsecond}; switch (parse_precision(precision_int)) { case precision::day: return as_sys_time_from_calendar_impl(yyd); case precision::hour: return as_sys_time_from_calendar_impl(yydh); case precision::minute: return as_sys_time_from_calendar_impl(yydhm); case precision::second: return as_sys_time_from_calendar_impl(yydhms); case precision::millisecond: return as_sys_time_from_calendar_impl(yydhmss1); case precision::microsecond: return as_sys_time_from_calendar_impl(yydhmss2); case precision::nanosecond: return as_sys_time_from_calendar_impl(yydhmss3); default: { const enum precision precision_val = parse_precision(precision_int); const std::string precision_string = precision_to_cpp_string(precision_val); std::string message = "Can't convert to a time point from a calendar with '" + precision_string + "' precision. " + "A minimum of 'day' precision is required."; clock_abort(message.c_str()); } } never_reached("as_sys_time_year_day_cpp"); } // ----------------------------------------------------------------------------- [[cpp11::register]] cpp11::writable::list as_year_day_from_sys_time_cpp(cpp11::list_of fields, const cpp11::integers& precision_int) { using namespace rclock; switch (parse_precision(precision_int)) { case precision::day: return as_calendar_from_sys_time_impl(fields); case precision::hour: return as_calendar_from_sys_time_impl(fields); case precision::minute: return as_calendar_from_sys_time_impl(fields); case precision::second: return as_calendar_from_sys_time_impl(fields); case precision::millisecond: return as_calendar_from_sys_time_impl>(fields); case precision::microsecond: return as_calendar_from_sys_time_impl>(fields); case precision::nanosecond: return as_calendar_from_sys_time_impl>(fields); default: clock_abort("Internal error: Invalid precision."); } never_reached("as_year_day_from_sys_time_cpp"); } // ----------------------------------------------------------------------------- static inline cpp11::writable::list year_minus_year_impl(const rclock::yearday::y& x, const rclock::yearday::y& y) { const r_ssize size = x.size(); rclock::duration::years out(size); for (r_ssize i = 0; i < size; ++i) { if (x.is_na(i) || y.is_na(i)) { out.assign_na(i); continue; } out.assign(x.to_year(i) - y.to_year(i), i); } return out.to_list(); } [[cpp11::register]] cpp11::writable::list year_day_minus_year_day_cpp(cpp11::list_of x, cpp11::list_of y, const cpp11::integers& precision_int) { const cpp11::integers x_year = rclock::yearday::get_year(x); const cpp11::integers y_year = rclock::yearday::get_year(y); const rclock::yearday::y x_y{x_year}; const rclock::yearday::y y_y{y_year}; switch (parse_precision(precision_int)) { case precision::year: return year_minus_year_impl(x_y, y_y); default: clock_abort("Internal error: Invalid precision."); } never_reached("year_day_minus_year_day_cpp"); } clock/src/rcrd.cpp0000644000176200001440000001035614422221153013573 0ustar liggesusers#include "rcrd.h" #include "utils.h" #include "enums.h" SEXP new_clock_rcrd_from_fields(SEXP fields, SEXP names, SEXP classes) { if (TYPEOF(fields) != VECSXP) { clock_abort("`fields` must be a list."); } if (TYPEOF(classes) != STRSXP) { clock_abort("`classes` must be a character vector."); } fields = PROTECT(r_clone_referenced(fields)); // Clear all attributes except for `names`, as we often // pass in other duration or time point objects as `fields` SEXP field_names = Rf_getAttrib(fields, R_NamesSymbol); SET_ATTRIB(fields, r_null); Rf_setAttrib(fields, R_NamesSymbol, field_names); const r_ssize n_fields = Rf_xlength(fields); if (n_fields == 0L) { clock_abort("There must be at least 1 field."); } const SEXP* p_fields = r_list_deref_const(fields); SEXP field0 = p_fields[0]; if (TYPEOF(field0) != INTSXP && TYPEOF(field0) != REALSXP) { clock_abort("All clock_rcrd types have integer or double fields."); } const r_ssize size = Rf_xlength(field0); for (r_ssize i = 1; i < n_fields; ++i) { const SEXP field = p_fields[i]; if (TYPEOF(field) != INTSXP && TYPEOF(field) != REALSXP) { clock_abort("All clock_rcrd types have integer or double fields."); } if (Rf_xlength(field) != size) { clock_abort("All fields must have the same size."); } } Rf_setAttrib(fields, R_ClassSymbol, classes); SEXP field0_names = Rf_getAttrib(field0, R_NamesSymbol); if (names != field0_names) { // If names are "new" (based on pointer comparison), set them. // This can remove names if `names` is `NULL`. // We assume `names` coming through here are validated names, for performance. field0 = set_names_dispatch(field0, names); SET_VECTOR_ELT(fields, 0, field0); } UNPROTECT(1); return fields; } // ----------------------------------------------------------------------------- [[cpp11::register]] SEXP clock_rcrd_proxy(SEXP x) { const r_ssize n_fields = Rf_xlength(x); const SEXP* p_x = r_list_deref_const(x); // clock-rcrds always have at least 1 field const r_ssize size = Rf_xlength(p_x[0]); SEXP out = PROTECT(Rf_allocVector(VECSXP, n_fields)); Rf_setAttrib(out, R_NamesSymbol, Rf_getAttrib(x, R_NamesSymbol)); r_init_data_frame(out, size); for (r_ssize i = 0; i < n_fields; ++i) { SET_VECTOR_ELT(out, i, p_x[i]); } UNPROTECT(1); return out; } // ----------------------------------------------------------------------------- SEXP clock_rcrd_restore(SEXP x, SEXP to, SEXP classes) { const r_ssize n_fields = Rf_xlength(x); const SEXP* p_x = r_list_deref_const(x); SEXP out = PROTECT(Rf_allocVector(VECSXP, n_fields)); Rf_setAttrib(out, R_NamesSymbol, Rf_getAttrib(x, R_NamesSymbol)); Rf_setAttrib(out, R_ClassSymbol, classes); for (r_ssize i = 0; i < n_fields; ++i) { SET_VECTOR_ELT(out, i, p_x[i]); } UNPROTECT(1); return out; } // ----------------------------------------------------------------------------- [[cpp11::register]] SEXP clock_rcrd_names(SEXP x) { SEXP field0 = VECTOR_ELT(x, 0); return Rf_getAttrib(field0, R_NamesSymbol); } // ----------------------------------------------------------------------------- static inline void validate_names(SEXP names, r_ssize size); [[cpp11::register]] SEXP clock_rcrd_set_names(SEXP x, SEXP names) { SEXP field0 = VECTOR_ELT(x, 0); SEXP field0_names = Rf_getAttrib(field0, R_NamesSymbol); if (field0_names == names) { // New and old were either `NULL`, or we are using the exact same chr SEXP return x; } x = PROTECT(r_clone_referenced(x)); if (names != r_null) { const r_ssize size = Rf_xlength(field0); validate_names(names, size); } field0 = set_names_dispatch(field0, names); SET_VECTOR_ELT(x, 0, field0); UNPROTECT(1); return x; } static inline void validate_names(SEXP names, r_ssize size) { if (TYPEOF(names) != STRSXP) { clock_abort("Names must be a character vector."); } const r_ssize names_size = Rf_xlength(names); if (names_size != size) { clock_abort("Names must have length %i, not %i.", size, names_size); } const SEXP* p_names = r_chr_deref_const(names); for (r_ssize i = 0; i < size; ++i) { const SEXP name = p_names[i]; if (name == r_chr_na) { clock_abort("Names cannot be `NA`."); } } } clock/src/gregorian-year-month-day.cpp0000644000176200001440000006651114427233200017457 0ustar liggesusers#include "gregorian-year-month-day.h" #include "calendar.h" #include "duration.h" #include "enums.h" #include "get.h" #include "parse.h" #include "failure.h" #include "fill.h" #include "rcrd.h" // ----------------------------------------------------------------------------- [[cpp11::register]] SEXP new_year_month_day_from_fields(SEXP fields, const cpp11::integers& precision_int, SEXP names) { const enum precision precision_val = parse_precision(precision_int); const r_ssize n_fields = Rf_xlength(fields); r_ssize n; switch (precision_val) { case precision::year: n = 1; break; case precision::month: n = 2; break; case precision::day: n = 3; break; case precision::hour: n = 4; break; case precision::minute: n = 5; break; case precision::second: n = 6; break; case precision::millisecond: n = 7; break; case precision::microsecond: n = 7; break; case precision::nanosecond: n = 7; break; default: never_reached("new_year_month_day_from_fields"); } if (n != n_fields) { clock_abort("With the given precision, `fields` must have length %i, not %i.", n, n_fields); } SEXP out = PROTECT(new_clock_rcrd_from_fields(fields, names, classes_year_month_day)); Rf_setAttrib(out, syms_precision, precision_int); UNPROTECT(1); return out; } // ----------------------------------------------------------------------------- [[cpp11::register]] SEXP year_month_day_restore(SEXP x, SEXP to) { SEXP precision = Rf_getAttrib(to, syms_precision); SEXP out = PROTECT(clock_rcrd_restore(x, to, classes_year_month_day)); Rf_setAttrib(out, syms_precision, precision); UNPROTECT(1); return out; } // ----------------------------------------------------------------------------- [[cpp11::register]] cpp11::writable::strings format_year_month_day_cpp(cpp11::list_of fields, const cpp11::integers& precision_int) { using namespace rclock; cpp11::integers year = gregorian::get_year(fields); cpp11::integers month = gregorian::get_month(fields); cpp11::integers day = gregorian::get_day(fields); cpp11::integers hour = gregorian::get_hour(fields); cpp11::integers minute = gregorian::get_minute(fields); cpp11::integers second = gregorian::get_second(fields); cpp11::integers subsecond = gregorian::get_subsecond(fields); gregorian::y y{year}; gregorian::ym ym{year, month}; gregorian::ymd ymd{year, month, day}; gregorian::ymdh ymdh{year, month, day, hour}; gregorian::ymdhm ymdhm{year, month, day, hour, minute}; gregorian::ymdhms ymdhms{year, month, day, hour, minute, second}; gregorian::ymdhmss ymdhmss1{year, month, day, hour, minute, second, subsecond}; gregorian::ymdhmss ymdhmss2{year, month, day, hour, minute, second, subsecond}; gregorian::ymdhmss ymdhmss3{year, month, day, hour, minute, second, subsecond}; switch (parse_precision(precision_int)) { case precision::year: return format_calendar_impl(y); case precision::month: return format_calendar_impl(ym); case precision::day: return format_calendar_impl(ymd); case precision::hour: return format_calendar_impl(ymdh); case precision::minute: return format_calendar_impl(ymdhm); case precision::second: return format_calendar_impl(ymdhms); case precision::millisecond: return format_calendar_impl(ymdhmss1); case precision::microsecond: return format_calendar_impl(ymdhmss2); case precision::nanosecond: return format_calendar_impl(ymdhmss3); default: clock_abort("Internal error: Invalid precision."); } never_reached("format_year_month_day_cpp"); } // ----------------------------------------------------------------------------- [[cpp11::register]] cpp11::writable::logicals invalid_detect_year_month_day_cpp(const cpp11::integers& year, const cpp11::integers& month, const cpp11::integers& day) { rclock::gregorian::ymd x{year, month, day}; const r_ssize size = x.size(); cpp11::writable::logicals out(size); for (r_ssize i = 0; i < size; ++i) { if (x.is_na(i)) { out[i] = false; } else { out[i] = !x.to_year_month_day(i).ok(); } } return out; } // ----------------------------------------------------------------------------- [[cpp11::register]] bool invalid_any_year_month_day_cpp(const cpp11::integers& year, const cpp11::integers& month, const cpp11::integers& day) { rclock::gregorian::ymd x{year, month, day}; const r_ssize size = x.size(); for (r_ssize i = 0; i < size; ++i) { if (!x.is_na(i) && !x.to_year_month_day(i).ok()) { return true; } } return false; } // ----------------------------------------------------------------------------- [[cpp11::register]] int invalid_count_year_month_day_cpp(const cpp11::integers& year, const cpp11::integers& month, const cpp11::integers& day) { rclock::gregorian::ymd x{year, month, day}; const r_ssize size = x.size(); int count = 0; for (r_ssize i = 0; i < size; ++i) { count += !x.is_na(i) && !x.to_year_month_day(i).ok(); } return count; } // ----------------------------------------------------------------------------- [[cpp11::register]] cpp11::writable::list invalid_resolve_year_month_day_cpp(cpp11::list_of fields, const cpp11::integers& precision_int, const cpp11::strings& invalid_string, const cpp11::sexp& call) { using namespace rclock; const enum invalid invalid_val = parse_invalid(invalid_string); cpp11::integers year = gregorian::get_year(fields); cpp11::integers month = gregorian::get_month(fields); cpp11::integers day = gregorian::get_day(fields); cpp11::integers hour = gregorian::get_hour(fields); cpp11::integers minute = gregorian::get_minute(fields); cpp11::integers second = gregorian::get_second(fields); cpp11::integers subsecond = gregorian::get_subsecond(fields); gregorian::ymd ymd{year, month, day}; gregorian::ymdh ymdh{year, month, day, hour}; gregorian::ymdhm ymdhm{year, month, day, hour, minute}; gregorian::ymdhms ymdhms{year, month, day, hour, minute, second}; gregorian::ymdhmss ymdhmss1{year, month, day, hour, minute, second, subsecond}; gregorian::ymdhmss ymdhmss2{year, month, day, hour, minute, second, subsecond}; gregorian::ymdhmss ymdhmss3{year, month, day, hour, minute, second, subsecond}; switch (parse_precision(precision_int)) { case precision::day: return invalid_resolve_calendar_impl(ymd, invalid_val, call); case precision::hour: return invalid_resolve_calendar_impl(ymdh, invalid_val, call); case precision::minute: return invalid_resolve_calendar_impl(ymdhm, invalid_val, call); case precision::second: return invalid_resolve_calendar_impl(ymdhms, invalid_val, call); case precision::millisecond: return invalid_resolve_calendar_impl(ymdhmss1, invalid_val, call); case precision::microsecond: return invalid_resolve_calendar_impl(ymdhmss2, invalid_val, call); case precision::nanosecond: return invalid_resolve_calendar_impl(ymdhmss3, invalid_val, call); default: never_reached("invalid_resolve_year_month_day_cpp"); } } // ----------------------------------------------------------------------------- [[cpp11::register]] cpp11::writable::integers get_year_month_day_last_cpp(const cpp11::integers& year, const cpp11::integers& month) { rclock::gregorian::ym x{year, month}; const r_ssize size = x.size(); cpp11::writable::integers out(size); for (r_ssize i = 0; i < size; ++i) { if (x.is_na(i)) { out[i] = r_int_na; } else { date::year_month_day_last elt = x.to_year_month(i) / date::last; out[i] = static_cast(static_cast(elt.day())); } } return out; } // ----------------------------------------------------------------------------- [[cpp11::register]] cpp11::writable::list year_month_day_plus_years_cpp(const cpp11::integers& year, cpp11::list_of fields_n) { rclock::gregorian::y x{year}; rclock::duration::years n{fields_n}; return calendar_plus_duration_impl(x, n); } [[cpp11::register]] cpp11::writable::list year_month_day_plus_months_cpp(const cpp11::integers& year, const cpp11::integers& month, cpp11::list_of fields_n) { rclock::gregorian::ym x{year, month}; rclock::duration::months n{fields_n}; return calendar_plus_duration_impl(x, n); } // ----------------------------------------------------------------------------- [[cpp11::register]] cpp11::writable::list as_sys_time_year_month_day_cpp(cpp11::list_of fields, const cpp11::integers& precision_int) { using namespace rclock; cpp11::integers year = gregorian::get_year(fields); cpp11::integers month = gregorian::get_month(fields); cpp11::integers day = gregorian::get_day(fields); cpp11::integers hour = gregorian::get_hour(fields); cpp11::integers minute = gregorian::get_minute(fields); cpp11::integers second = gregorian::get_second(fields); cpp11::integers subsecond = gregorian::get_subsecond(fields); gregorian::ymd ymd{year, month, day}; gregorian::ymdh ymdh{year, month, day, hour}; gregorian::ymdhm ymdhm{year, month, day, hour, minute}; gregorian::ymdhms ymdhms{year, month, day, hour, minute, second}; gregorian::ymdhmss ymdhmss1{year, month, day, hour, minute, second, subsecond}; gregorian::ymdhmss ymdhmss2{year, month, day, hour, minute, second, subsecond}; gregorian::ymdhmss ymdhmss3{year, month, day, hour, minute, second, subsecond}; switch (parse_precision(precision_int)) { case precision::day: return as_sys_time_from_calendar_impl(ymd); case precision::hour: return as_sys_time_from_calendar_impl(ymdh); case precision::minute: return as_sys_time_from_calendar_impl(ymdhm); case precision::second: return as_sys_time_from_calendar_impl(ymdhms); case precision::millisecond: return as_sys_time_from_calendar_impl(ymdhmss1); case precision::microsecond: return as_sys_time_from_calendar_impl(ymdhmss2); case precision::nanosecond: return as_sys_time_from_calendar_impl(ymdhmss3); default: { const enum precision precision_val = parse_precision(precision_int); const std::string precision_string = precision_to_cpp_string(precision_val); std::string message = "Can't convert to a time point from a calendar with '" + precision_string + "' precision. " + "A minimum of 'day' precision is required."; clock_abort(message.c_str()); } } never_reached("as_sys_time_year_month_day_cpp"); } // ----------------------------------------------------------------------------- [[cpp11::register]] cpp11::writable::list as_year_month_day_from_sys_time_cpp(cpp11::list_of fields, const cpp11::integers& precision_int) { using namespace rclock; switch (parse_precision(precision_int)) { case precision::day: return as_calendar_from_sys_time_impl(fields); case precision::hour: return as_calendar_from_sys_time_impl(fields); case precision::minute: return as_calendar_from_sys_time_impl(fields); case precision::second: return as_calendar_from_sys_time_impl(fields); case precision::millisecond: return as_calendar_from_sys_time_impl>(fields); case precision::microsecond: return as_calendar_from_sys_time_impl>(fields); case precision::nanosecond: return as_calendar_from_sys_time_impl>(fields); default: clock_abort("Internal error: Invalid precision."); } never_reached("as_year_month_day_from_sys_time_cpp"); } // ----------------------------------------------------------------------------- static inline cpp11::writable::list year_minus_year_impl(const rclock::gregorian::y& x, const rclock::gregorian::y& y) { const r_ssize size = x.size(); rclock::duration::years out(size); for (r_ssize i = 0; i < size; ++i) { if (x.is_na(i) || y.is_na(i)) { out.assign_na(i); continue; } out.assign(x.to_year(i) - y.to_year(i), i); } return out.to_list(); } static inline cpp11::writable::list year_month_minus_year_month_impl(const rclock::gregorian::ym& x, const rclock::gregorian::ym& y) { const r_ssize size = x.size(); rclock::duration::months out(size); for (r_ssize i = 0; i < size; ++i) { if (x.is_na(i) || y.is_na(i)) { out.assign_na(i); continue; } out.assign(x.to_year_month(i) - y.to_year_month(i), i); } return out.to_list(); } [[cpp11::register]] cpp11::writable::list year_month_day_minus_year_month_day_cpp(cpp11::list_of x, cpp11::list_of y, const cpp11::integers& precision_int) { const cpp11::integers x_year = rclock::gregorian::get_year(x); const cpp11::integers x_month = rclock::gregorian::get_month(x); const cpp11::integers y_year = rclock::gregorian::get_year(y); const cpp11::integers y_month = rclock::gregorian::get_month(y); const rclock::gregorian::y x_y{x_year}; const rclock::gregorian::ym x_ym{x_year, x_month}; const rclock::gregorian::y y_y{y_year}; const rclock::gregorian::ym y_ym{y_year, y_month}; switch (parse_precision(precision_int)) { case precision::year: return year_minus_year_impl(x_y, y_y); case precision::month: return year_month_minus_year_month_impl(x_ym, y_ym); default: clock_abort("Internal error: Invalid precision."); } never_reached("year_month_day_minus_year_month_day_cpp"); } // ----------------------------------------------------------------------------- // Default impl applies to millisecond/microsecond/nanosecond parsers template inline void year_month_day_from_stream(std::istringstream& stream, const std::vector& fmts, const std::pair& month_names_pair, const std::pair& weekday_names_pair, const std::pair& ampm_names_pair, const char& decimal_mark, const r_ssize& i, rclock::failures& fail, Calendar& out) { using Duration = typename Calendar::duration; const r_ssize size = fmts.size(); for (r_ssize j = 0; j < size; ++j) { stream.clear(); stream.seekg(0); const char* fmt = fmts[j].c_str(); date::year_month_day ymd{}; date::hh_mm_ss hms{}; rclock::from_stream( stream, fmt, month_names_pair, weekday_names_pair, ampm_names_pair, decimal_mark, ymd, hms ); if (!stream.fail()) { out.assign_year_month_day(ymd, i); out.assign_hour(hms.hours(), i); out.assign_minute(hms.minutes(), i); out.assign_second(hms.seconds(), i); out.assign_subsecond(hms.subseconds(), i); return; } } fail.write(i); out.assign_na(i); } template <> inline void year_month_day_from_stream(std::istringstream& stream, const std::vector& fmts, const std::pair& month_names_pair, const std::pair& weekday_names_pair, const std::pair& ampm_names_pair, const char& decimal_mark, const r_ssize& i, rclock::failures& fail, rclock::gregorian::y& out) { const r_ssize size = fmts.size(); for (r_ssize j = 0; j < size; ++j) { stream.clear(); stream.seekg(0); const char* fmt = fmts[j].c_str(); date::year x{}; rclock::from_stream( stream, fmt, month_names_pair, weekday_names_pair, ampm_names_pair, decimal_mark, x ); if (!stream.fail()) { out.assign_year(x, i); return; } } fail.write(i); out.assign_na(i); } template <> inline void year_month_day_from_stream(std::istringstream& stream, const std::vector& fmts, const std::pair& month_names_pair, const std::pair& weekday_names_pair, const std::pair& ampm_names_pair, const char& decimal_mark, const r_ssize& i, rclock::failures& fail, rclock::gregorian::ym& out) { const r_ssize size = fmts.size(); for (r_ssize j = 0; j < size; ++j) { stream.clear(); stream.seekg(0); const char* fmt = fmts[j].c_str(); date::year_month x{}; rclock::from_stream( stream, fmt, month_names_pair, weekday_names_pair, ampm_names_pair, decimal_mark, x ); if (!stream.fail()) { out.assign_year_month(x, i); return; } } fail.write(i); out.assign_na(i); } template <> inline void year_month_day_from_stream(std::istringstream& stream, const std::vector& fmts, const std::pair& month_names_pair, const std::pair& weekday_names_pair, const std::pair& ampm_names_pair, const char& decimal_mark, const r_ssize& i, rclock::failures& fail, rclock::gregorian::ymd& out) { const r_ssize size = fmts.size(); for (r_ssize j = 0; j < size; ++j) { stream.clear(); stream.seekg(0); const char* fmt = fmts[j].c_str(); date::year_month_day x{}; rclock::from_stream( stream, fmt, month_names_pair, weekday_names_pair, ampm_names_pair, decimal_mark, x ); if (!stream.fail()) { out.assign_year_month_day(x, i); return; } } fail.write(i); out.assign_na(i); } template <> inline void year_month_day_from_stream(std::istringstream& stream, const std::vector& fmts, const std::pair& month_names_pair, const std::pair& weekday_names_pair, const std::pair& ampm_names_pair, const char& decimal_mark, const r_ssize& i, rclock::failures& fail, rclock::gregorian::ymdh& out) { const r_ssize size = fmts.size(); for (r_ssize j = 0; j < size; ++j) { stream.clear(); stream.seekg(0); const char* fmt = fmts[j].c_str(); date::year_month_day ymd{}; date::hh_mm_ss hms{}; rclock::from_stream( stream, fmt, month_names_pair, weekday_names_pair, ampm_names_pair, decimal_mark, ymd, hms ); if (!stream.fail()) { out.assign_year_month_day(ymd, i); out.assign_hour(hms.hours(), i); return; } } fail.write(i); out.assign_na(i); } template <> inline void year_month_day_from_stream(std::istringstream& stream, const std::vector& fmts, const std::pair& month_names_pair, const std::pair& weekday_names_pair, const std::pair& ampm_names_pair, const char& decimal_mark, const r_ssize& i, rclock::failures& fail, rclock::gregorian::ymdhm& out) { const r_ssize size = fmts.size(); for (r_ssize j = 0; j < size; ++j) { stream.clear(); stream.seekg(0); const char* fmt = fmts[j].c_str(); date::year_month_day ymd{}; date::hh_mm_ss hms{}; rclock::from_stream( stream, fmt, month_names_pair, weekday_names_pair, ampm_names_pair, decimal_mark, ymd, hms ); if (!stream.fail()) { out.assign_year_month_day(ymd, i); out.assign_hour(hms.hours(), i); out.assign_minute(hms.minutes(), i); return; } } fail.write(i); out.assign_na(i); } template <> inline void year_month_day_from_stream(std::istringstream& stream, const std::vector& fmts, const std::pair& month_names_pair, const std::pair& weekday_names_pair, const std::pair& ampm_names_pair, const char& decimal_mark, const r_ssize& i, rclock::failures& fail, rclock::gregorian::ymdhms& out) { const r_ssize size = fmts.size(); for (r_ssize j = 0; j < size; ++j) { stream.clear(); stream.seekg(0); const char* fmt = fmts[j].c_str(); date::year_month_day ymd{}; date::hh_mm_ss hms{}; rclock::from_stream( stream, fmt, month_names_pair, weekday_names_pair, ampm_names_pair, decimal_mark, ymd, hms ); if (!stream.fail()) { out.assign_year_month_day(ymd, i); out.assign_hour(hms.hours(), i); out.assign_minute(hms.minutes(), i); out.assign_second(hms.seconds(), i); return; } } fail.write(i); out.assign_na(i); } template static cpp11::writable::list year_month_day_parse_impl(const cpp11::strings& x, const cpp11::strings& format, const cpp11::strings& month, const cpp11::strings& month_abbrev, const cpp11::strings& weekday, const cpp11::strings& weekday_abbrev, const cpp11::strings& am_pm, const cpp11::strings& mark) { const r_ssize size = x.size(); Calendar out(size); std::vector fmts(format.size()); rclock::fill_formats(format, fmts); char dmark; switch (parse_decimal_mark(mark)) { case decimal_mark::comma: dmark = ','; break; case decimal_mark::period: dmark = '.'; break; default: clock_abort("Internal error: Unknown decimal mark."); } std::string month_names[24]; const std::pair& month_names_pair = fill_month_names( month, month_abbrev, month_names ); std::string weekday_names[14]; const std::pair& weekday_names_pair = fill_weekday_names( weekday, weekday_abbrev, weekday_names ); std::string ampm_names[2]; const std::pair& ampm_names_pair = fill_ampm_names( am_pm, ampm_names ); rclock::failures fail{}; std::istringstream stream; void* vmax = vmaxget(); for (r_ssize i = 0; i < size; ++i) { const SEXP elt = x[i]; if (elt == r_chr_na) { out.assign_na(i); continue; } const char* p_elt = Rf_translateCharUTF8(elt); stream.str(p_elt); year_month_day_from_stream( stream, fmts, month_names_pair, weekday_names_pair, ampm_names_pair, dmark, i, fail, out ); } vmaxset(vmax); if (fail.any_failures()) { fail.warn_parse(); } return out.to_list(); } [[cpp11::register]] cpp11::writable::list year_month_day_parse_cpp(const cpp11::strings& x, const cpp11::strings& format, const cpp11::integers& precision_int, const cpp11::strings& month, const cpp11::strings& month_abbrev, const cpp11::strings& weekday, const cpp11::strings& weekday_abbrev, const cpp11::strings& am_pm, const cpp11::strings& mark) { using namespace rclock; switch (parse_precision(precision_int)) { case precision::year: return year_month_day_parse_impl(x, format, month, month_abbrev, weekday, weekday_abbrev, am_pm, mark); case precision::month: return year_month_day_parse_impl(x, format, month, month_abbrev, weekday, weekday_abbrev, am_pm, mark); case precision::day: return year_month_day_parse_impl(x, format, month, month_abbrev, weekday, weekday_abbrev, am_pm, mark); case precision::hour: return year_month_day_parse_impl(x, format, month, month_abbrev, weekday, weekday_abbrev, am_pm, mark); case precision::minute: return year_month_day_parse_impl(x, format, month, month_abbrev, weekday, weekday_abbrev, am_pm, mark); case precision::second: return year_month_day_parse_impl(x, format, month, month_abbrev, weekday, weekday_abbrev, am_pm, mark); case precision::millisecond: return year_month_day_parse_impl>(x, format, month, month_abbrev, weekday, weekday_abbrev, am_pm, mark); case precision::microsecond: return year_month_day_parse_impl>(x, format, month, month_abbrev, weekday, weekday_abbrev, am_pm, mark); case precision::nanosecond: return year_month_day_parse_impl>(x, format, month, month_abbrev, weekday, weekday_abbrev, am_pm, mark); default: never_reached("year_month_day_parse_cpp"); } } // ----------------------------------------------------------------------------- [[cpp11::register]] cpp11::writable::logicals gregorian_leap_year_cpp(const cpp11::integers& year) { const r_ssize size = year.size(); cpp11::writable::logicals out(size); for (r_ssize i = 0; i < size; ++i) { const int elt = year[i]; if (elt == r_int_na) { out[i] = r_lgl_na; } else { out[i] = date::year{elt}.is_leap(); } } return out; } clock/src/week.h0000644000176200001440000014126314427227145013257 0ustar liggesusers#ifndef WEEK_H #define WEEK_H // The MIT License (MIT) // // For the original `date.h` implementation: // Copyright (c) 2015, 2016, 2017 Howard Hinnant // For the `week.h` extension: // Copyright (c) 2023 Davis Vaughan // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. #include #include namespace week { // y/wn/wd // wn/wd/y // wd/wn/y using days = date::days; using weeks = date::weeks; using years = date::years; // time_point using sys_days = date::sys_days; using local_days = date::local_days; // types struct last_week { explicit last_week() = default; }; // C-encoded to be aligned with `date::weekday` enum class start: unsigned char { sunday = 0u, monday = 1u, tuesday = 2u, wednesday = 3u, thursday = 4u, friday = 5u, saturday = 6u }; template class weekday; class weeknum; template class year; template class year_weeknum; template class year_lastweek; template class weeknum_weekday; template class lastweek_weekday; template class year_weeknum_weekday; template class year_lastweek_weekday; // date composition operators template CONSTCD11 year_weeknum operator/(const year& y, const weeknum& wn) NOEXCEPT; template CONSTCD11 year_weeknum operator/(const year& y, int wn) NOEXCEPT; template CONSTCD11 year_lastweek operator/(const year& y, last_week wn) NOEXCEPT; template CONSTCD11 weeknum_weekday operator/(const weeknum& wn, const weekday& wd) NOEXCEPT; template CONSTCD11 weeknum_weekday operator/(const weeknum& wn, int wd) NOEXCEPT; template CONSTCD11 weeknum_weekday operator/(const weekday& wd, const weeknum& wn) NOEXCEPT; template CONSTCD11 weeknum_weekday operator/(const weekday& wd, int wn) NOEXCEPT; template CONSTCD11 lastweek_weekday operator/(const last_week& wn, const weekday& wd) NOEXCEPT; template CONSTCD11 lastweek_weekday operator/(const last_week& wn, int wd) NOEXCEPT; template CONSTCD11 lastweek_weekday operator/(const weekday& wd, const last_week& wn) NOEXCEPT; template CONSTCD11 year_weeknum_weekday operator/(const year_weeknum& ywn, const weekday& wd) NOEXCEPT; template CONSTCD11 year_weeknum_weekday operator/(const year_weeknum& ywn, int wd) NOEXCEPT; template CONSTCD11 year_weeknum_weekday operator/(const weeknum_weekday& wnwd, const year& y) NOEXCEPT; template CONSTCD11 year_weeknum_weekday operator/(const weeknum_weekday& wnwd, int y) NOEXCEPT; template CONSTCD11 year_lastweek_weekday operator/(const year_lastweek& ylw, const weekday& wd) NOEXCEPT; template CONSTCD11 year_lastweek_weekday operator/(const year_lastweek& ylw, int wd) NOEXCEPT; template CONSTCD11 year_lastweek_weekday operator/(const lastweek_weekday& lwwd, const year& y) NOEXCEPT; template CONSTCD11 year_lastweek_weekday operator/(const lastweek_weekday& lwwd, int y) NOEXCEPT; // weekday template class weekday { unsigned char wd_; public: weekday() = default; explicit CONSTCD11 weekday(unsigned wd) NOEXCEPT; CONSTCD11 weekday(date::weekday wd) NOEXCEPT; explicit weekday(int) = delete; CONSTCD11 weekday(const sys_days& dp) NOEXCEPT; CONSTCD11 explicit weekday(const local_days& dp) NOEXCEPT; weekday& operator++() NOEXCEPT; weekday operator++(int) NOEXCEPT; weekday& operator--() NOEXCEPT; weekday operator--(int) NOEXCEPT; weekday& operator+=(const days& d) NOEXCEPT; weekday& operator-=(const days& d) NOEXCEPT; CONSTCD11 explicit operator unsigned() const NOEXCEPT; CONSTCD11 operator date::weekday() const NOEXCEPT; CONSTCD11 bool ok() const NOEXCEPT; private: static CONSTCD11 unsigned char from_weekday(const date::weekday& wd) NOEXCEPT; static CONSTCD11 unsigned char to_weekday(const weekday& x) NOEXCEPT; }; template CONSTCD11 bool operator==(const weekday& x, const weekday& y) NOEXCEPT; template CONSTCD11 bool operator!=(const weekday& x, const weekday& y) NOEXCEPT; template CONSTCD14 weekday operator+(const weekday& x, const days& y) NOEXCEPT; template CONSTCD14 weekday operator+(const days& x, const weekday& y) NOEXCEPT; template CONSTCD14 weekday operator-(const weekday& x, const days& y) NOEXCEPT; template CONSTCD14 days operator-(const weekday& x, const weekday& y) NOEXCEPT; template std::basic_ostream& operator<<(std::basic_ostream& os, const weekday& wd); // year template class year { short y_; public: year() = default; explicit CONSTCD11 year(int y) NOEXCEPT; year& operator++() NOEXCEPT; year operator++(int) NOEXCEPT; year& operator--() NOEXCEPT; year operator--(int) NOEXCEPT; year& operator+=(const years& y) NOEXCEPT; year& operator-=(const years& y) NOEXCEPT; CONSTCD14 bool is_leap() const NOEXCEPT; CONSTCD11 explicit operator int() const NOEXCEPT; CONSTCD11 bool ok() const NOEXCEPT; static CONSTCD11 year min() NOEXCEPT; static CONSTCD11 year max() NOEXCEPT; }; template CONSTCD11 bool operator==(const year& x, const year& y) NOEXCEPT; template CONSTCD11 bool operator!=(const year& x, const year& y) NOEXCEPT; template CONSTCD11 bool operator< (const year& x, const year& y) NOEXCEPT; template CONSTCD11 bool operator> (const year& x, const year& y) NOEXCEPT; template CONSTCD11 bool operator<=(const year& x, const year& y) NOEXCEPT; template CONSTCD11 bool operator>=(const year& x, const year& y) NOEXCEPT; template CONSTCD11 year operator+(const year& x, const years& y) NOEXCEPT; template CONSTCD11 year operator+(const years& x, const year& y) NOEXCEPT; template CONSTCD11 year operator-(const year& x, const years& y) NOEXCEPT; template CONSTCD11 years operator-(const year& x, const year& y) NOEXCEPT; template std::basic_ostream& operator<<(std::basic_ostream& os, const year& y); // weeknum class weeknum { unsigned char wn_; public: weeknum() = default; explicit CONSTCD11 weeknum(unsigned wn) NOEXCEPT; weeknum& operator++() NOEXCEPT; weeknum operator++(int) NOEXCEPT; weeknum& operator--() NOEXCEPT; weeknum operator--(int) NOEXCEPT; weeknum& operator+=(const weeks& y) NOEXCEPT; weeknum& operator-=(const weeks& y) NOEXCEPT; CONSTCD11 explicit operator unsigned() const NOEXCEPT; CONSTCD11 bool ok() const NOEXCEPT; }; CONSTCD11 bool operator==(const weeknum& x, const weeknum& y) NOEXCEPT; CONSTCD11 bool operator!=(const weeknum& x, const weeknum& y) NOEXCEPT; CONSTCD11 bool operator< (const weeknum& x, const weeknum& y) NOEXCEPT; CONSTCD11 bool operator> (const weeknum& x, const weeknum& y) NOEXCEPT; CONSTCD11 bool operator<=(const weeknum& x, const weeknum& y) NOEXCEPT; CONSTCD11 bool operator>=(const weeknum& x, const weeknum& y) NOEXCEPT; CONSTCD11 weeknum operator+(const weeknum& x, const weeks& y) NOEXCEPT; CONSTCD11 weeknum operator+(const weeks& x, const weeknum& y) NOEXCEPT; CONSTCD11 weeknum operator-(const weeknum& x, const weeks& y) NOEXCEPT; CONSTCD11 weeks operator-(const weeknum& x, const weeknum& y) NOEXCEPT; template std::basic_ostream& operator<<(std::basic_ostream& os, const weeknum& wn); // year_weeknum template class year_weeknum { week::year y_; week::weeknum wn_; public: year_weeknum() = default; CONSTCD11 year_weeknum(const week::year& y, const week::weeknum& wn) NOEXCEPT; CONSTCD14 year_weeknum(const week::year_lastweek& ylw) NOEXCEPT; CONSTCD11 week::year year() const NOEXCEPT; CONSTCD11 week::weeknum weeknum() const NOEXCEPT; year_weeknum& operator+=(const years& dy) NOEXCEPT; year_weeknum& operator-=(const years& dy) NOEXCEPT; CONSTCD11 bool ok() const NOEXCEPT; }; template CONSTCD11 bool operator==(const year_weeknum& x, const year_weeknum& y) NOEXCEPT; template CONSTCD11 bool operator!=(const year_weeknum& x, const year_weeknum& y) NOEXCEPT; template CONSTCD11 bool operator< (const year_weeknum& x, const year_weeknum& y) NOEXCEPT; template CONSTCD11 bool operator> (const year_weeknum& x, const year_weeknum& y) NOEXCEPT; template CONSTCD11 bool operator<=(const year_weeknum& x, const year_weeknum& y) NOEXCEPT; template CONSTCD11 bool operator>=(const year_weeknum& x, const year_weeknum& y) NOEXCEPT; template CONSTCD11 year_weeknum operator+(const year_weeknum& ym, const years& dy) NOEXCEPT; template CONSTCD11 year_weeknum operator+(const years& dy, const year_weeknum& ym) NOEXCEPT; template CONSTCD11 year_weeknum operator-(const year_weeknum& ym, const years& dy) NOEXCEPT; template std::basic_ostream& operator<<(std::basic_ostream& os, const year_weeknum& ym); // year_lastweek template class year_lastweek { week::year y_; public: year_lastweek() = default; CONSTCD11 explicit year_lastweek(const week::year& y) NOEXCEPT; CONSTCD11 week::year year() const NOEXCEPT; CONSTCD14 week::weeknum weeknum() const NOEXCEPT; year_lastweek& operator+=(const years& dy) NOEXCEPT; year_lastweek& operator-=(const years& dy) NOEXCEPT; CONSTCD11 bool ok() const NOEXCEPT; }; template CONSTCD11 bool operator==(const year_lastweek& x, const year_lastweek& y) NOEXCEPT; template CONSTCD11 bool operator!=(const year_lastweek& x, const year_lastweek& y) NOEXCEPT; template CONSTCD11 bool operator< (const year_lastweek& x, const year_lastweek& y) NOEXCEPT; template CONSTCD11 bool operator> (const year_lastweek& x, const year_lastweek& y) NOEXCEPT; template CONSTCD11 bool operator<=(const year_lastweek& x, const year_lastweek& y) NOEXCEPT; template CONSTCD11 bool operator>=(const year_lastweek& x, const year_lastweek& y) NOEXCEPT; template CONSTCD11 year_lastweek operator+(const year_lastweek& ym, const years& dy) NOEXCEPT; template CONSTCD11 year_lastweek operator+(const years& dy, const year_lastweek& ym) NOEXCEPT; template CONSTCD11 year_lastweek operator-(const year_lastweek& ym, const years& dy) NOEXCEPT; template std::basic_ostream& operator<<(std::basic_ostream& os, const year_lastweek& ym); // weeknum_weekday template class weeknum_weekday { week::weeknum wn_; week::weekday wd_; public: weeknum_weekday() = default; CONSTCD11 weeknum_weekday(const week::weeknum& wn, const week::weekday& wd) NOEXCEPT; CONSTCD11 week::weeknum weeknum() const NOEXCEPT; CONSTCD11 week::weekday weekday() const NOEXCEPT; CONSTCD14 bool ok() const NOEXCEPT; }; template CONSTCD11 bool operator==(const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT; template CONSTCD11 bool operator!=(const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT; template CONSTCD11 bool operator< (const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT; template CONSTCD11 bool operator> (const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT; template CONSTCD11 bool operator<=(const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT; template CONSTCD11 bool operator>=(const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT; template std::basic_ostream& operator<<(std::basic_ostream& os, const weeknum_weekday& md); // lastweek_weekday template class lastweek_weekday { week::weekday wd_; public: lastweek_weekday() = default; CONSTCD11 explicit lastweek_weekday(const week::weekday& wd) NOEXCEPT; CONSTCD11 week::weekday weekday() const NOEXCEPT; CONSTCD14 bool ok() const NOEXCEPT; }; template CONSTCD11 bool operator==(const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT; template CONSTCD11 bool operator!=(const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT; template CONSTCD11 bool operator< (const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT; template CONSTCD11 bool operator> (const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT; template CONSTCD11 bool operator<=(const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT; template CONSTCD11 bool operator>=(const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT; template std::basic_ostream& operator<<(std::basic_ostream& os, const lastweek_weekday& md); // year_lastweek_weekday template class year_lastweek_weekday { week::year y_; week::weekday wd_; public: year_lastweek_weekday() = default; CONSTCD11 year_lastweek_weekday(const week::year& y, const week::weekday& wd) NOEXCEPT; year_lastweek_weekday& operator+=(const years& y) NOEXCEPT; year_lastweek_weekday& operator-=(const years& y) NOEXCEPT; CONSTCD11 week::year year() const NOEXCEPT; CONSTCD14 week::weeknum weeknum() const NOEXCEPT; CONSTCD11 week::weekday weekday() const NOEXCEPT; CONSTCD14 operator sys_days() const NOEXCEPT; CONSTCD14 explicit operator local_days() const NOEXCEPT; CONSTCD11 bool ok() const NOEXCEPT; }; template CONSTCD11 bool operator==(const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT; template CONSTCD11 bool operator!=(const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT; template CONSTCD11 bool operator< (const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT; template CONSTCD11 bool operator> (const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT; template CONSTCD11 bool operator<=(const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT; template CONSTCD11 bool operator>=(const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT; template CONSTCD11 year_lastweek_weekday operator+(const year_lastweek_weekday& ywnwd, const years& y) NOEXCEPT; template CONSTCD11 year_lastweek_weekday operator+(const years& y, const year_lastweek_weekday& ywnwd) NOEXCEPT; template CONSTCD11 year_lastweek_weekday operator-(const year_lastweek_weekday& ywnwd, const years& y) NOEXCEPT; template std::basic_ostream& operator<<(std::basic_ostream& os, const year_lastweek_weekday& ywnwd); // class year_weeknum_weekday template class year_weeknum_weekday { week::year y_; week::weeknum wn_; week::weekday wd_; public: year_weeknum_weekday() = default; CONSTCD11 year_weeknum_weekday(const week::year& y, const week::weeknum& wn, const week::weekday& wd) NOEXCEPT; CONSTCD14 year_weeknum_weekday(const year_lastweek_weekday& ylwwd) NOEXCEPT; CONSTCD14 year_weeknum_weekday(const sys_days& dp) NOEXCEPT; CONSTCD14 explicit year_weeknum_weekday(const local_days& dp) NOEXCEPT; year_weeknum_weekday& operator+=(const years& y) NOEXCEPT; year_weeknum_weekday& operator-=(const years& y) NOEXCEPT; CONSTCD11 week::year year() const NOEXCEPT; CONSTCD11 week::weeknum weeknum() const NOEXCEPT; CONSTCD11 week::weekday weekday() const NOEXCEPT; CONSTCD14 operator sys_days() const NOEXCEPT; CONSTCD14 explicit operator local_days() const NOEXCEPT; CONSTCD14 bool ok() const NOEXCEPT; private: static CONSTCD14 year_weeknum_weekday from_days(days dp) NOEXCEPT; }; template CONSTCD11 bool operator==(const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT; template CONSTCD11 bool operator!=(const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT; template CONSTCD11 bool operator< (const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT; template CONSTCD11 bool operator> (const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT; template CONSTCD11 bool operator<=(const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT; template CONSTCD11 bool operator>=(const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT; template CONSTCD11 year_weeknum_weekday operator+(const year_weeknum_weekday& ywnwd, const years& y) NOEXCEPT; template CONSTCD11 year_weeknum_weekday operator+(const years& y, const year_weeknum_weekday& ywnwd) NOEXCEPT; template CONSTCD11 year_weeknum_weekday operator-(const year_weeknum_weekday& ywnwd, const years& y) NOEXCEPT; template std::basic_ostream& operator<<(std::basic_ostream& os, const year_weeknum_weekday& ywnwd); //----------------+ // Implementation | //----------------+ // weekday template CONSTCD11 inline unsigned char weekday::from_weekday(const date::weekday& x) NOEXCEPT { return (x - days{static_cast(S)}).c_encoding() + 1u; } template CONSTCD11 inline unsigned char weekday::to_weekday(const weekday& x) NOEXCEPT { return static_cast(x + days{static_cast(S)}) - 1u; } template CONSTCD11 inline weekday::weekday(unsigned wd) NOEXCEPT : wd_(static_cast(wd)) {} template CONSTCD11 inline weekday::weekday(date::weekday wd) NOEXCEPT : wd_(from_weekday(wd)) {} template CONSTCD11 inline weekday::weekday(const sys_days& dp) NOEXCEPT : wd_(from_weekday(date::weekday(dp))) {} template CONSTCD11 inline weekday::weekday(const local_days& dp) NOEXCEPT : wd_(from_weekday(date::weekday(dp))) {} template inline weekday& weekday::operator++() NOEXCEPT {if (++wd_ == 8) wd_ = 1; return *this;} template inline weekday weekday::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} template inline weekday& weekday::operator--() NOEXCEPT {if (wd_-- == 1) wd_ = 7; return *this;} template inline weekday weekday::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} template inline weekday& weekday::operator+=(const days& d) NOEXCEPT { *this = *this + d; return *this; } template inline weekday& weekday::operator-=(const days& d) NOEXCEPT { *this = *this - d; return *this; } template CONSTCD11 inline weekday::operator unsigned() const NOEXCEPT { return wd_; } template CONSTCD11 inline weekday::operator date::weekday() const NOEXCEPT { return date::weekday{to_weekday(*this)}; } template CONSTCD11 inline bool weekday::ok() const NOEXCEPT {return 1 <= wd_ && wd_ <= 7;} template CONSTCD11 inline bool operator==(const weekday& x, const weekday& y) NOEXCEPT { return static_cast(x) == static_cast(y); } template CONSTCD11 inline bool operator!=(const weekday& x, const weekday& y) NOEXCEPT { return !(x == y); } template CONSTCD14 inline days operator-(const weekday& x, const weekday& y) NOEXCEPT { auto const diff = static_cast(x) - static_cast(y); return days{diff <= 6 ? diff : diff + 7}; } template CONSTCD14 inline weekday operator+(const weekday& x, const days& y) NOEXCEPT { auto const wdu = static_cast(static_cast(x) - 1u) + y.count(); auto const wk = (wdu >= 0 ? wdu : wdu - 6) / 7; return weekday{static_cast(wdu - wk * 7) + 1u}; } template CONSTCD14 inline weekday operator+(const days& x, const weekday& y) NOEXCEPT { return y + x; } template CONSTCD14 inline weekday operator-(const weekday& x, const days& y) NOEXCEPT { return x + -y; } template inline std::basic_ostream& operator<<(std::basic_ostream& os, const weekday& wd) { const date::weekday wd2{wd}; return operator<<(os, wd2); } // year template CONSTCD11 inline year::year(int y) NOEXCEPT : y_(static_cast(y)) {} template inline year& year::operator++() NOEXCEPT {++y_; return *this;} template inline year year::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} template inline year& year::operator--() NOEXCEPT {--y_; return *this;} template inline year year::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} template inline year& year::operator+=(const years& y) NOEXCEPT {*this = *this + y; return *this;} template inline year& year::operator-=(const years& y) NOEXCEPT {*this = *this - y; return *this;} template CONSTCD14 inline bool year::is_leap() const NOEXCEPT { const auto y = date::year{static_cast(y_)}; const auto middle = date::weekday{static_cast(S)} + days{3}; const auto s0 = sys_days((y-years{1})/12/middle[date::last]); const auto s1 = sys_days(y/12/middle[date::last]); return s1-s0 != days{7*52}; } template CONSTCD11 inline year::operator int() const NOEXCEPT {return y_;} template CONSTCD11 inline bool year::ok() const NOEXCEPT {return min() <= *this && *this <= max();} template CONSTCD11 inline year year::min() NOEXCEPT { using std::chrono::seconds; using std::chrono::minutes; using std::chrono::hours; using std::chrono::duration_cast; static_assert(sizeof(seconds)*CHAR_BIT >= 41, "seconds may overflow"); static_assert(sizeof(hours)*CHAR_BIT >= 30, "hours may overflow"); return sizeof(minutes)*CHAR_BIT < 34 ? year{1970} + duration_cast(minutes::min()) : year{std::numeric_limits::min()}; } template CONSTCD11 inline year year::max() NOEXCEPT { using std::chrono::seconds; using std::chrono::minutes; using std::chrono::hours; using std::chrono::duration_cast; static_assert(sizeof(seconds)*CHAR_BIT >= 41, "seconds may overflow"); static_assert(sizeof(hours)*CHAR_BIT >= 30, "hours may overflow"); return sizeof(minutes)*CHAR_BIT < 34 ? year{1969} + duration_cast(minutes::max()) : year{std::numeric_limits::max()}; } template CONSTCD11 inline bool operator==(const year& x, const year& y) NOEXCEPT { return static_cast(x) == static_cast(y); } template CONSTCD11 inline bool operator!=(const year& x, const year& y) NOEXCEPT { return !(x == y); } template CONSTCD11 inline bool operator<(const year& x, const year& y) NOEXCEPT { return static_cast(x) < static_cast(y); } template CONSTCD11 inline bool operator>(const year& x, const year& y) NOEXCEPT { return y < x; } template CONSTCD11 inline bool operator<=(const year& x, const year& y) NOEXCEPT { return !(y < x); } template CONSTCD11 inline bool operator>=(const year& x, const year& y) NOEXCEPT { return !(x < y); } template CONSTCD11 inline years operator-(const year& x, const year& y) NOEXCEPT { return years{static_cast(x) - static_cast(y)}; } template CONSTCD11 inline year operator+(const year& x, const years& y) NOEXCEPT { return year{static_cast(x) + y.count()}; } template CONSTCD11 inline year operator+(const years& x, const year& y) NOEXCEPT { return y + x; } template CONSTCD11 inline year operator-(const year& x, const years& y) NOEXCEPT { return year{static_cast(x) - y.count()}; } template inline std::basic_ostream& operator<<(std::basic_ostream& os, const year& y) { date::detail::save_ostream _(os); os.fill('0'); os.flags(std::ios::dec | std::ios::internal); os.width(4 + (y < year{0})); os << static_cast(y); return os; } #if !defined(_MSC_VER) || (_MSC_VER >= 1900) inline namespace literals { CONSTCD11 inline week::weeknum operator "" _w(unsigned long long wn) NOEXCEPT { return week::weeknum(static_cast(wn)); } #endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) CONSTDATA week::last_week last{}; #if !defined(_MSC_VER) || (_MSC_VER >= 1900) } // inline namespace literals #endif // weeknum CONSTCD11 inline weeknum::weeknum(unsigned wn) NOEXCEPT : wn_(static_cast(wn)) {} inline weeknum& weeknum::operator++() NOEXCEPT {++wn_; return *this;} inline weeknum weeknum::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} inline weeknum& weeknum::operator--() NOEXCEPT {--wn_; return *this;} inline weeknum weeknum::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} inline weeknum& weeknum::operator+=(const weeks& y) NOEXCEPT { *this = *this + y; return *this; } inline weeknum& weeknum::operator-=(const weeks& y) NOEXCEPT { *this = *this - y; return *this; } CONSTCD11 inline weeknum::operator unsigned() const NOEXCEPT {return wn_;} CONSTCD11 inline bool weeknum::ok() const NOEXCEPT {return 1 <= wn_ && wn_ <= 53;} CONSTCD11 inline bool operator==(const weeknum& x, const weeknum& y) NOEXCEPT { return static_cast(x) == static_cast(y); } CONSTCD11 inline bool operator!=(const weeknum& x, const weeknum& y) NOEXCEPT { return !(x == y); } CONSTCD11 inline bool operator<(const weeknum& x, const weeknum& y) NOEXCEPT { return static_cast(x) < static_cast(y); } CONSTCD11 inline bool operator>(const weeknum& x, const weeknum& y) NOEXCEPT { return y < x; } CONSTCD11 inline bool operator<=(const weeknum& x, const weeknum& y) NOEXCEPT { return !(y < x); } CONSTCD11 inline bool operator>=(const weeknum& x, const weeknum& y) NOEXCEPT { return !(x < y); } CONSTCD11 inline weeks operator-(const weeknum& x, const weeknum& y) NOEXCEPT { return weeks{static_cast(static_cast(x)) - static_cast(static_cast(y))}; } CONSTCD11 inline weeknum operator+(const weeknum& x, const weeks& y) NOEXCEPT { return weeknum{static_cast(x) + static_cast(y.count())}; } CONSTCD11 inline weeknum operator+(const weeks& x, const weeknum& y) NOEXCEPT { return y + x; } CONSTCD11 inline weeknum operator-(const weeknum& x, const weeks& y) NOEXCEPT { return x + -y; } template inline std::basic_ostream& operator<<(std::basic_ostream& os, const weeknum& wn) { date::detail::save_ostream _(os); os << 'W'; os.fill('0'); os.flags(std::ios::dec | std::ios::right); os.width(2); os << static_cast(wn); return os; } // year_weeknum template CONSTCD11 inline year_weeknum::year_weeknum(const week::year& y, const week::weeknum& wn) NOEXCEPT : y_(y) , wn_(wn) {} template CONSTCD14 inline year_weeknum::year_weeknum(const week::year_lastweek& ylw) NOEXCEPT : y_(ylw.year()) , wn_(ylw.weeknum()) {} template CONSTCD11 inline year year_weeknum::year() const NOEXCEPT {return y_;} template CONSTCD11 inline weeknum year_weeknum::weeknum() const NOEXCEPT {return wn_;} template CONSTCD11 inline bool year_weeknum::ok() const NOEXCEPT { return y_.ok() && 1u <= static_cast(wn_) && wn_ <= (y_/last).weeknum(); } template inline year_weeknum& year_weeknum::operator+=(const years& dy) NOEXCEPT { *this = *this + dy; return *this; } template inline year_weeknum& year_weeknum::operator-=(const years& dy) NOEXCEPT { *this = *this - dy; return *this; } template CONSTCD11 inline bool operator==(const year_weeknum& x, const year_weeknum& y) NOEXCEPT { return x.year() == y.year() && x.weeknum() == y.weeknum(); } template CONSTCD11 inline bool operator!=(const year_weeknum& x, const year_weeknum& y) NOEXCEPT { return !(x == y); } template CONSTCD11 inline bool operator<(const year_weeknum& x, const year_weeknum& y) NOEXCEPT { return x.year() < y.year() ? true : (x.year() > y.year() ? false : (x.weeknum() < y.weeknum())); } template CONSTCD11 inline bool operator>(const year_weeknum& x, const year_weeknum& y) NOEXCEPT { return y < x; } template CONSTCD11 inline bool operator<=(const year_weeknum& x, const year_weeknum& y) NOEXCEPT { return !(y < x); } template CONSTCD11 inline bool operator>=(const year_weeknum& x, const year_weeknum& y) NOEXCEPT { return !(x < y); } template CONSTCD11 inline year_weeknum operator+(const year_weeknum& ym, const years& dy) NOEXCEPT { return (ym.year() + dy) / ym.weeknum(); } template CONSTCD11 inline year_weeknum operator+(const years& dy, const year_weeknum& ym) NOEXCEPT { return ym + dy; } template CONSTCD11 inline year_weeknum operator-(const year_weeknum& ym, const years& dy) NOEXCEPT { return ym + -dy; } template inline std::basic_ostream& operator<<(std::basic_ostream& os, const year_weeknum& ywn) { return os << ywn.year() << '-' << ywn.weeknum(); } // year_lastweek template CONSTCD11 inline year_lastweek::year_lastweek(const week::year& y) NOEXCEPT : y_(y) {} template CONSTCD11 inline year year_lastweek::year() const NOEXCEPT {return y_;} template CONSTCD14 inline weeknum year_lastweek::weeknum() const NOEXCEPT { return week::weeknum(y_.is_leap() ? 53u : 52u); } template CONSTCD11 inline bool year_lastweek::ok() const NOEXCEPT {return y_.ok();} template inline year_lastweek& year_lastweek::operator+=(const years& dy) NOEXCEPT { *this = *this + dy; return *this; } template inline year_lastweek& year_lastweek::operator-=(const years& dy) NOEXCEPT { *this = *this - dy; return *this; } template CONSTCD11 inline bool operator==(const year_lastweek& x, const year_lastweek& y) NOEXCEPT { return x.year() == y.year(); } template CONSTCD11 inline bool operator!=(const year_lastweek& x, const year_lastweek& y) NOEXCEPT { return !(x == y); } template CONSTCD11 inline bool operator<(const year_lastweek& x, const year_lastweek& y) NOEXCEPT { return x.year() < y.year(); } template CONSTCD11 inline bool operator>(const year_lastweek& x, const year_lastweek& y) NOEXCEPT { return y < x; } template CONSTCD11 inline bool operator<=(const year_lastweek& x, const year_lastweek& y) NOEXCEPT { return !(y < x); } template CONSTCD11 inline bool operator>=(const year_lastweek& x, const year_lastweek& y) NOEXCEPT { return !(x < y); } template CONSTCD11 inline year_lastweek operator+(const year_lastweek& ym, const years& dy) NOEXCEPT { return year_lastweek{ym.year() + dy}; } template CONSTCD11 inline year_lastweek operator+(const years& dy, const year_lastweek& ym) NOEXCEPT { return ym + dy; } template CONSTCD11 inline year_lastweek operator-(const year_lastweek& ym, const years& dy) NOEXCEPT { return ym + -dy; } template inline std::basic_ostream& operator<<(std::basic_ostream& os, const year_lastweek& ywn) { return os << ywn.year() << "-W last"; } // weeknum_weekday template CONSTCD11 inline weeknum_weekday::weeknum_weekday(const week::weeknum& wn, const week::weekday& wd) NOEXCEPT : wn_(wn) , wd_(wd) {} template CONSTCD11 inline weeknum weeknum_weekday::weeknum() const NOEXCEPT {return wn_;} template CONSTCD11 inline weekday weeknum_weekday::weekday() const NOEXCEPT {return wd_;} template CONSTCD14 inline bool weeknum_weekday::ok() const NOEXCEPT { return wn_.ok() && wd_.ok(); } template CONSTCD11 inline bool operator==(const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT { return x.weeknum() == y.weeknum() && x.weekday() == y.weekday(); } template CONSTCD11 inline bool operator!=(const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT { return !(x == y); } template CONSTCD11 inline bool operator<(const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT { return x.weeknum() < y.weeknum() ? true : (x.weeknum() > y.weeknum() ? false : (static_cast(x.weekday()) < static_cast(y.weekday()))); } template CONSTCD11 inline bool operator>(const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT { return y < x; } template CONSTCD11 inline bool operator<=(const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT { return !(y < x); } template CONSTCD11 inline bool operator>=(const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT { return !(x < y); } template inline std::basic_ostream& operator<<(std::basic_ostream& os, const weeknum_weekday& md) { return os << md.weeknum() << '-' << md.weekday(); } // lastweek_weekday template CONSTCD11 inline lastweek_weekday::lastweek_weekday(const week::weekday& wd) NOEXCEPT : wd_(wd) {} template CONSTCD11 inline weekday lastweek_weekday::weekday() const NOEXCEPT {return wd_;} template CONSTCD14 inline bool lastweek_weekday::ok() const NOEXCEPT { return wd_.ok(); } template CONSTCD11 inline bool operator==(const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT { return x.weekday() == y.weekday(); } template CONSTCD11 inline bool operator!=(const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT { return !(x == y); } template CONSTCD11 inline bool operator<(const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT { return static_cast(x.weekday()) < static_cast(y.weekday()); } template CONSTCD11 inline bool operator>(const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT { return y < x; } template CONSTCD11 inline bool operator<=(const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT { return !(y < x); } template CONSTCD11 inline bool operator>=(const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT { return !(x < y); } template inline std::basic_ostream& operator<<(std::basic_ostream& os, const lastweek_weekday& md) { return os << "W last-" << md.weekday(); } // year_lastweek_weekday template CONSTCD11 inline year_lastweek_weekday::year_lastweek_weekday(const week::year& y, const week::weekday& wd) NOEXCEPT : y_(y) , wd_(wd) {} template inline year_lastweek_weekday& year_lastweek_weekday::operator+=(const years& y) NOEXCEPT { *this = *this + y; return *this; } template inline year_lastweek_weekday& year_lastweek_weekday::operator-=(const years& y) NOEXCEPT { *this = *this - y; return *this; } template CONSTCD11 inline year year_lastweek_weekday::year() const NOEXCEPT {return y_;} template CONSTCD14 inline weeknum year_lastweek_weekday::weeknum() const NOEXCEPT { return (y_ / last).weeknum(); } template CONSTCD11 inline weekday year_lastweek_weekday::weekday() const NOEXCEPT {return wd_;} template CONSTCD14 inline year_lastweek_weekday::operator sys_days() const NOEXCEPT { const auto start = date::weekday(static_cast(S)); const auto end = start - days{1}; const auto middle = start + days{3}; return sys_days(date::year{static_cast(y_)}/date::dec/middle[date::last]) + (end - middle) - (end - wd_); } template CONSTCD14 inline year_lastweek_weekday::operator local_days() const NOEXCEPT { const auto start = date::weekday(static_cast(S)); const auto end = start - days{1}; const auto middle = start + days{3}; return local_days(date::year{static_cast(y_)}/date::dec/middle[date::last]) + (end - middle) - (end - wd_); } template CONSTCD11 inline bool year_lastweek_weekday::ok() const NOEXCEPT { return y_.ok() && wd_.ok(); } template CONSTCD11 inline bool operator==(const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT { return x.year() == y.year() && x.weekday() == y.weekday(); } template CONSTCD11 inline bool operator!=(const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT { return !(x == y); } template CONSTCD11 inline bool operator<(const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT { return x.year() < y.year() ? true : (x.year() > y.year() ? false : (static_cast(x.weekday()) < static_cast(y.weekday()))); } template CONSTCD11 inline bool operator>(const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT { return y < x; } template CONSTCD11 inline bool operator<=(const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT { return !(y < x); } template CONSTCD11 inline bool operator>=(const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT { return !(x < y); } template CONSTCD11 inline year_lastweek_weekday operator+(const year_lastweek_weekday& ywnwd, const years& y) NOEXCEPT { return (ywnwd.year() + y) / last / ywnwd.weekday(); } template CONSTCD11 inline year_lastweek_weekday operator+(const years& y, const year_lastweek_weekday& ywnwd) NOEXCEPT { return ywnwd + y; } template CONSTCD11 inline year_lastweek_weekday operator-(const year_lastweek_weekday& ywnwd, const years& y) NOEXCEPT { return ywnwd + -y; } template inline std::basic_ostream& operator<<(std::basic_ostream& os, const year_lastweek_weekday& ywnwd) { return os << ywnwd.year() << "-W last-" << ywnwd.weekday(); } // year_weeknum_weekday template CONSTCD11 inline year_weeknum_weekday::year_weeknum_weekday(const week::year& y, const week::weeknum& wn, const week::weekday& wd) NOEXCEPT : y_(y) , wn_(wn) , wd_(wd) {} template CONSTCD14 inline year_weeknum_weekday::year_weeknum_weekday(const year_lastweek_weekday& ylwwd) NOEXCEPT : y_(ylwwd.year()) , wn_(ylwwd.weeknum()) , wd_(ylwwd.weekday()) {} template CONSTCD14 inline year_weeknum_weekday::year_weeknum_weekday(const sys_days& dp) NOEXCEPT : year_weeknum_weekday(from_days(dp.time_since_epoch())) {} template CONSTCD14 inline year_weeknum_weekday::year_weeknum_weekday(const local_days& dp) NOEXCEPT : year_weeknum_weekday(from_days(dp.time_since_epoch())) {} template inline year_weeknum_weekday& year_weeknum_weekday::operator+=(const years& y) NOEXCEPT { *this = *this + y; return *this; } template inline year_weeknum_weekday& year_weeknum_weekday::operator-=(const years& y) NOEXCEPT { *this = *this - y; return *this; } template CONSTCD11 inline year year_weeknum_weekday::year() const NOEXCEPT {return y_;} template CONSTCD11 inline weeknum year_weeknum_weekday::weeknum() const NOEXCEPT {return wn_;} template CONSTCD11 inline weekday year_weeknum_weekday::weekday() const NOEXCEPT {return wd_;} template CONSTCD14 inline year_weeknum_weekday::operator sys_days() const NOEXCEPT { const auto start = date::weekday(static_cast(S)); const auto middle = start + days{3}; return sys_days(date::year{static_cast(y_)-1}/date::dec/middle[date::last]) + (start - middle) + weeks{static_cast(wn_)-1} + (wd_ - start); } template CONSTCD14 inline year_weeknum_weekday::operator local_days() const NOEXCEPT { const auto start = date::weekday(static_cast(S)); const auto middle = start + days{3}; return local_days(date::year{static_cast(y_)-1}/date::dec/middle[date::last]) + (start - middle) + weeks{static_cast(wn_)-1} + (wd_ - start); } template CONSTCD14 inline bool year_weeknum_weekday::ok() const NOEXCEPT { return y_.ok() && wd_.ok() && week::weeknum{1u} <= wn_ && wn_ <= year_lastweek{y_}.weeknum(); } template CONSTCD14 inline year_weeknum_weekday year_weeknum_weekday::from_days(days d) NOEXCEPT { const auto dp = sys_days{d}; const auto wd = week::weekday{dp}; const auto start = date::weekday{static_cast(S)}; const auto middle = start + days{3}; auto y = date::year_month_day{dp + days{3}}.year(); auto dp_start = sys_days((y - date::years{1})/date::dec/middle[date::last]) + (start-middle); if (dp < dp_start) { --y; dp_start = sys_days((y - date::years{1})/date::dec/middle[date::last]) + (start-middle); } const auto wn = week::weeknum( static_cast(date::trunc(dp - dp_start).count() + 1)); return {week::year(static_cast(y)), wn, wd}; } template CONSTCD11 inline bool operator==(const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT { return x.year() == y.year() && x.weeknum() == y.weeknum() && x.weekday() == y.weekday(); } template CONSTCD11 inline bool operator!=(const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT { return !(x == y); } template CONSTCD11 inline bool operator<(const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT { return x.year() < y.year() ? true : (x.year() > y.year() ? false : (x.weeknum() < y.weeknum() ? true : (x.weeknum() > y.weeknum() ? false : (static_cast(x.weekday()) < static_cast(y.weekday()))))); } template CONSTCD11 inline bool operator>(const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT { return y < x; } template CONSTCD11 inline bool operator<=(const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT { return !(y < x); } template CONSTCD11 inline bool operator>=(const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT { return !(x < y); } template CONSTCD11 inline year_weeknum_weekday operator+(const year_weeknum_weekday& ywnwd, const years& y) NOEXCEPT { return (ywnwd.year() + y) / ywnwd.weeknum() / ywnwd.weekday(); } template CONSTCD11 inline year_weeknum_weekday operator+(const years& y, const year_weeknum_weekday& ywnwd) NOEXCEPT { return ywnwd + y; } template CONSTCD11 inline year_weeknum_weekday operator-(const year_weeknum_weekday& ywnwd, const years& y) NOEXCEPT { return ywnwd + -y; } template inline std::basic_ostream& operator<<(std::basic_ostream& os, const year_weeknum_weekday& ywnwd) { return os << ywnwd.year() << '-' << ywnwd.weeknum() << '-' << ywnwd.weekday(); } // date composition operators template CONSTCD11 inline year_weeknum operator/(const year& y, const weeknum& wn) NOEXCEPT { return {y, wn}; } template CONSTCD11 inline year_weeknum operator/(const year& y, int wn) NOEXCEPT { return y/weeknum(static_cast(wn)); } template CONSTCD11 inline year_lastweek operator/(const year& y, last_week) NOEXCEPT { return year_lastweek{y}; } template CONSTCD11 inline weeknum_weekday operator/(const weeknum& wn, const weekday& wd) NOEXCEPT { return {wn, wd}; } template CONSTCD11 inline weeknum_weekday operator/(const weeknum& wn, int wd) NOEXCEPT { return wn/weekday{static_cast(wd)}; } template CONSTCD11 inline weeknum_weekday operator/(const weekday& wd, const weeknum& wn) NOEXCEPT { return wn/wd; } template CONSTCD11 inline weeknum_weekday operator/(const weekday& wd, int wn) NOEXCEPT { return weeknum{static_cast(wn)}/wd; } template CONSTCD11 inline lastweek_weekday operator/(const last_week&, const weekday& wd) NOEXCEPT { return lastweek_weekday{wd}; } template CONSTCD11 inline lastweek_weekday operator/(const last_week& wn, int wd) NOEXCEPT { return wn / weekday{static_cast(wd)}; } template CONSTCD11 inline lastweek_weekday operator/(const weekday& wd, const last_week& wn) NOEXCEPT { return wn / wd; } template CONSTCD11 inline year_weeknum_weekday operator/(const year_weeknum& ywn, const weekday& wd) NOEXCEPT { return {ywn.year(), ywn.weeknum(), wd}; } template CONSTCD11 inline year_weeknum_weekday operator/(const year_weeknum& ywn, int wd) NOEXCEPT { return ywn / weekday(static_cast(wd)); } template CONSTCD11 inline year_weeknum_weekday operator/(const weeknum_weekday& wnwd, const year& y) NOEXCEPT { return {y, wnwd.weeknum(), wnwd.weekday()}; } template CONSTCD11 inline year_weeknum_weekday operator/(const weeknum_weekday& wnwd, int y) NOEXCEPT { return wnwd / year{y}; } template CONSTCD11 inline year_lastweek_weekday operator/(const year_lastweek& ylw, const weekday& wd) NOEXCEPT { return {ylw.year(), wd}; } template CONSTCD11 inline year_lastweek_weekday operator/(const year_lastweek& ylw, int wd) NOEXCEPT { return ylw / weekday(static_cast(wd)); } template CONSTCD11 inline year_lastweek_weekday operator/(const lastweek_weekday& lwwd, const year& y) NOEXCEPT { return {y, lwwd.weekday()}; } template CONSTCD11 inline year_lastweek_weekday operator/(const lastweek_weekday& lwwd, int y) NOEXCEPT { return lwwd / year{y}; } } // namespace week #endif // WEEK_H clock/src/time-point.cpp0000644000176200001440000002124614422225070014730 0ustar liggesusers#include "clock.h" #include "enums.h" #include "utils.h" #include "rcrd.h" #include "duration.h" #include "parse.h" #include "failure.h" #include "fill.h" #include [[cpp11::register]] SEXP new_time_point_from_fields(SEXP fields, const cpp11::integers& precision_int, const cpp11::integers& clock_int, SEXP names) { const enum precision precision_val = parse_precision(precision_int); const enum clock_name clock_val = parse_clock_name(clock_int); const r_ssize n_fields = Rf_xlength(fields); if (n_fields != 2) { clock_abort("`fields` must be length 2."); } switch (precision_val) { case precision::year: case precision::quarter: case precision::month: case precision::week: { clock_abort("`precision` must be at least 'day' precision."); } case precision::day: case precision::hour: case precision::minute: case precision::second: case precision::millisecond: case precision::microsecond: case precision::nanosecond: { break; } default: { never_reached("new_time_point_from_fields"); } } SEXP classes; switch (clock_val) { case clock_name::naive: classes = classes_naive_time; break; case clock_name::sys: classes = classes_sys_time; break; default: clock_abort("Internal error: Unknown clock."); } SEXP out = PROTECT(new_clock_rcrd_from_fields(fields, names, classes)); Rf_setAttrib(out, syms_precision, precision_int); Rf_setAttrib(out, syms_clock, clock_int); UNPROTECT(1); return out; } // ----------------------------------------------------------------------------- [[cpp11::register]] SEXP time_point_restore(SEXP x, SEXP to) { SEXP clock_int = Rf_getAttrib(to, syms_clock); SEXP precision_int = Rf_getAttrib(to, syms_precision); SEXP classes; switch (parse_clock_name(clock_int)) { case clock_name::naive: classes = classes_naive_time; break; case clock_name::sys: classes = classes_sys_time; break; default: clock_abort("Internal error: Unknown clock."); } SEXP out = PROTECT(clock_rcrd_restore(x, to, classes)); Rf_setAttrib(out, syms_clock, clock_int); Rf_setAttrib(out, syms_precision, precision_int); UNPROTECT(1); return out; } // ----------------------------------------------------------------------------- template static inline void time_point_parse_one(std::istringstream& stream, const std::vector& fmts, const std::pair& month_names_pair, const std::pair& weekday_names_pair, const std::pair& ampm_names_pair, const char& dmark, const r_ssize& i, rclock::failures& fail, ClockDuration& out) { using Duration = typename ClockDuration::chrono_duration; const r_ssize size = fmts.size(); for (r_ssize j = 0; j < size; ++j) { stream.clear(); stream.seekg(0); const char* fmt = fmts[j].c_str(); std::chrono::time_point tp; rclock::from_stream( stream, fmt, month_names_pair, weekday_names_pair, ampm_names_pair, dmark, tp ); if (!stream.fail()) { out.assign(tp.time_since_epoch(), i); return; } } fail.write(i); out.assign_na(i); } template static cpp11::writable::list time_point_parse_impl(const cpp11::strings& x, const cpp11::strings& format, const cpp11::strings& month, const cpp11::strings& month_abbrev, const cpp11::strings& weekday, const cpp11::strings& weekday_abbrev, const cpp11::strings& am_pm, const cpp11::strings& mark) { const r_ssize size = x.size(); ClockDuration out(size); std::vector fmts(format.size()); rclock::fill_formats(format, fmts); char dmark; switch (parse_decimal_mark(mark)) { case decimal_mark::comma: dmark = ','; break; case decimal_mark::period: dmark = '.'; break; default: clock_abort("Internal error: Unknown decimal mark."); } std::string month_names[24]; const std::pair& month_names_pair = fill_month_names( month, month_abbrev, month_names ); std::string weekday_names[14]; const std::pair& weekday_names_pair = fill_weekday_names( weekday, weekday_abbrev, weekday_names ); std::string ampm_names[2]; const std::pair& ampm_names_pair = fill_ampm_names( am_pm, ampm_names ); rclock::failures fail{}; std::istringstream stream; void* vmax = vmaxget(); for (r_ssize i = 0; i < size; ++i) { const SEXP elt = x[i]; if (elt == r_chr_na) { out.assign_na(i); continue; } const char* p_elt = Rf_translateCharUTF8(elt); stream.str(p_elt); time_point_parse_one( stream, fmts, month_names_pair, weekday_names_pair, ampm_names_pair, dmark, i, fail, out ); } vmaxset(vmax); if (fail.any_failures()) { fail.warn_parse(); } return out.to_list(); } [[cpp11::register]] cpp11::writable::list time_point_parse_cpp(const cpp11::strings& x, const cpp11::strings& format, const cpp11::integers& precision_int, const cpp11::integers& clock_int, const cpp11::strings& month, const cpp11::strings& month_abbrev, const cpp11::strings& weekday, const cpp11::strings& weekday_abbrev, const cpp11::strings& am_pm, const cpp11::strings& mark) { using namespace rclock; switch (parse_clock_name(clock_int)) { case clock_name::naive: { switch (parse_precision(precision_int)) { case precision::day: return time_point_parse_impl(x, format, month, month_abbrev, weekday, weekday_abbrev, am_pm, mark); case precision::hour: return time_point_parse_impl(x, format, month, month_abbrev, weekday, weekday_abbrev, am_pm, mark); case precision::minute: return time_point_parse_impl(x, format, month, month_abbrev, weekday, weekday_abbrev, am_pm, mark); case precision::second: return time_point_parse_impl(x, format, month, month_abbrev, weekday, weekday_abbrev, am_pm, mark); case precision::millisecond: return time_point_parse_impl(x, format, month, month_abbrev, weekday, weekday_abbrev, am_pm, mark); case precision::microsecond: return time_point_parse_impl(x, format, month, month_abbrev, weekday, weekday_abbrev, am_pm, mark); case precision::nanosecond: return time_point_parse_impl(x, format, month, month_abbrev, weekday, weekday_abbrev, am_pm, mark); default: never_reached("time_point_parse_cpp"); } } case clock_name::sys: { switch (parse_precision(precision_int)) { case precision::day: return time_point_parse_impl(x, format, month, month_abbrev, weekday, weekday_abbrev, am_pm, mark); case precision::hour: return time_point_parse_impl(x, format, month, month_abbrev, weekday, weekday_abbrev, am_pm, mark); case precision::minute: return time_point_parse_impl(x, format, month, month_abbrev, weekday, weekday_abbrev, am_pm, mark); case precision::second: return time_point_parse_impl(x, format, month, month_abbrev, weekday, weekday_abbrev, am_pm, mark); case precision::millisecond: return time_point_parse_impl(x, format, month, month_abbrev, weekday, weekday_abbrev, am_pm, mark); case precision::microsecond: return time_point_parse_impl(x, format, month, month_abbrev, weekday, weekday_abbrev, am_pm, mark); case precision::nanosecond: return time_point_parse_impl(x, format, month, month_abbrev, weekday, weekday_abbrev, am_pm, mark); default: never_reached("time_point_parse_cpp"); } } default: never_reached("time_point_parse_cpp"); } } clock/src/enums.cpp0000644000176200001440000002003514422221153013763 0ustar liggesusers#include "enums.h" #include "utils.h" #include #include // ----------------------------------------------------------------------------- // [[ include("enums.h") ]] enum invalid parse_invalid(const cpp11::strings& x) { if (x.size() != 1) { clock_abort("`invalid` must be a string with length 1."); } std::string string = x[0]; if (string == "previous") return invalid::previous; if (string == "next") return invalid::next; if (string == "overflow") return invalid::overflow; if (string == "previous-day") return invalid::previous_day; if (string == "next-day") return invalid::next_day; if (string == "overflow-day") return invalid::overflow_day; if (string == "NA") return invalid::na; if (string == "error") return invalid::error; clock_abort("'%s' is not a recognized `invalid` option.", string.c_str()); } // ----------------------------------------------------------------------------- // [[ include("enums.h") ]] enum nonexistent parse_nonexistent_one(const cpp11::r_string& x) { std::string string(x); if (string == "roll-forward") return nonexistent::roll_forward; if (string == "roll-backward") return nonexistent::roll_backward; if (string == "shift-forward") return nonexistent::shift_forward; if (string == "shift-backward") return nonexistent::shift_backward; if (string == "NA") return nonexistent::na; if (string == "error") return nonexistent::error; clock_abort("'%s' is not a recognized `nonexistent` option.", string.c_str()); } // ----------------------------------------------------------------------------- // [[ include("enums.h") ]] enum ambiguous parse_ambiguous_one(const cpp11::r_string& x) { std::string string(x); if (string == "earliest") return ambiguous::earliest; if (string == "latest") return ambiguous::latest; if (string == "NA") return ambiguous::na; if (string == "error") return ambiguous::error; clock_abort("'%s' is not a recognized `ambiguous` option.", string.c_str()); } // ----------------------------------------------------------------------------- // [[ include("enums.h") ]] enum component parse_component(const cpp11::strings& x) { if (x.size() != 1) { clock_abort("`component` must be a string with length 1."); } std::string string = x[0]; if (string == "year") return component::year; if (string == "quarter") return component::quarter; if (string == "month") return component::month; if (string == "week") return component::week; if (string == "day") return component::day; if (string == "hour") return component::hour; if (string == "minute") return component::minute; if (string == "second") return component::second; if (string == "millisecond") return component::millisecond; if (string == "microsecond") return component::microsecond; if (string == "nanosecond") return component::nanosecond; if (string == "index") return component::index; clock_abort("'%s' is not a recognized `component` option.", string.c_str()); } // ----------------------------------------------------------------------------- // [[ include("enums.h") ]] enum week::start parse_week_start(const cpp11::integers& x) { if (x.size() != 1) { clock_abort("`start` must be an integer with length 1."); } const int s = x[0]; if (s == 1) return week::start::sunday; else if (s == 2) return week::start::monday; else if (s == 3) return week::start::tuesday; else if (s == 4) return week::start::wednesday; else if (s == 5) return week::start::thursday; else if (s == 6) return week::start::friday; else if (s == 7) return week::start::saturday; else clock_abort("'%i' is not a recognized `start` option.", s); } // ----------------------------------------------------------------------------- // [[ include("enums.h") ]] enum quarterly::start parse_quarterly_start(const cpp11::integers& x) { if (x.size() != 1) { clock_abort("`start` must be an integer with length 1."); } const int s = x[0]; if (s == 1) return quarterly::start::january; else if (s == 2) return quarterly::start::february; else if (s == 3) return quarterly::start::march; else if (s == 4) return quarterly::start::april; else if (s == 5) return quarterly::start::may; else if (s == 6) return quarterly::start::june; else if (s == 7) return quarterly::start::july; else if (s == 8) return quarterly::start::august; else if (s == 9) return quarterly::start::september; else if (s == 10) return quarterly::start::october; else if (s == 11) return quarterly::start::november; else if (s == 12) return quarterly::start::december; else clock_abort("'%i' is not a recognized `start` option.", s); } // ----------------------------------------------------------------------------- // [[ include("enums.h") ]] enum precision parse_precision(const cpp11::integers& x) { if (x.size() != 1) { clock_abort("`precision` must be an integer with length 1."); } const int elt = x[0]; if (elt > 10 || elt < 0) { clock_abort("`%i` is not a recognized `precision` option.", elt); } return static_cast(static_cast(elt)); } static const std::string chr_year{"year"}; static const std::string chr_quarter{"quarter"}; static const std::string chr_month{"month"}; static const std::string chr_week{"week"}; static const std::string chr_day{"day"}; static const std::string chr_hour{"hour"}; static const std::string chr_minute{"minute"}; static const std::string chr_second{"second"}; static const std::string chr_millisecond{"millisecond"}; static const std::string chr_microsecond{"microsecond"}; static const std::string chr_nanosecond{"nanosecond"}; const std::string& precision_to_cpp_string(const enum precision& x) { switch (x) { case precision::year: return chr_year; case precision::quarter: return chr_quarter; case precision::month: return chr_month; case precision::week: return chr_week; case precision::day: return chr_day; case precision::hour: return chr_hour; case precision::minute: return chr_minute; case precision::second: return chr_second; case precision::millisecond: return chr_millisecond; case precision::microsecond: return chr_microsecond; case precision::nanosecond: return chr_nanosecond; default: never_reached("precision_to_cpp_string"); } } [[cpp11::register]] cpp11::writable::strings precision_to_string(const cpp11::integers& precision_int) { const enum precision precision_val = parse_precision(precision_int); const std::string precision_string = precision_to_cpp_string(precision_val); cpp11::writable::strings out{precision_string}; return out; } // ----------------------------------------------------------------------------- // [[ include("enums.h") ]] enum clock_name parse_clock_name(const cpp11::integers& x) { if (x.size() != 1) { clock_abort("`clock_name` must be an integer with length 1."); } const int elt = x[0]; if (elt > 1 || elt < 0) { clock_abort("`%i` is not a recognized `clock_name` option.", elt); } return static_cast(static_cast(elt)); } static const std::string chr_sys{"sys"}; static const std::string chr_naive{"naive"}; const std::string& clock_name_to_cpp_string(const enum clock_name& x) { switch (x) { case clock_name::sys: return chr_sys; case clock_name::naive: return chr_naive; default: never_reached("clock_name_to_cpp_string"); } } [[cpp11::register]] cpp11::writable::strings clock_to_string(const cpp11::integers& clock_int) { const enum clock_name clock_val = parse_clock_name(clock_int); const std::string clock_string = clock_name_to_cpp_string(clock_val); cpp11::writable::strings out{clock_string}; return out; } // ----------------------------------------------------------------------------- // [[ include("enums.h") ]] enum decimal_mark parse_decimal_mark(const cpp11::strings& x) { if (x.size() != 1) { clock_abort("`decimal_mark` must be a string with length 1."); } std::string string = x[0]; if (string == ".") return decimal_mark::period; if (string == ",") return decimal_mark::comma; clock_abort("'%s' is not a recognized `decimal_mark` option.", string.c_str()); } clock/src/calendar.h0000644000176200001440000000565514423730227014075 0ustar liggesusers#ifndef CLOCK_CALENDAR_H #define CLOCK_CALENDAR_H #include "clock.h" #include "enums.h" #include "integers.h" // ----------------------------------------------------------------------------- template static inline cpp11::writable::strings format_calendar_impl(const Calendar& x) { const r_ssize size = x.size(); cpp11::writable::strings out(size); std::ostringstream stream; for (r_ssize i = 0; i < size; ++i) { if (x.is_na(i)) { SET_STRING_ELT(out, i, r_chr_na); continue; } stream.str(std::string()); stream.clear(); x.stream(stream, i); if (stream.fail()) { // Should never happen but... SET_STRING_ELT(out, i, r_chr_na); continue; } const std::string string = stream.str(); SET_STRING_ELT(out, i, Rf_mkCharLenCE(string.c_str(), string.size(), CE_UTF8)); } return out; } // ----------------------------------------------------------------------------- template static inline cpp11::writable::list invalid_resolve_calendar_impl(Calendar& x, const enum invalid& invalid_val, const cpp11::sexp& call) { const r_ssize size = x.size(); for (r_ssize i = 0; i < size; ++i) { if (x.is_na(i)) { continue; } x.resolve(i, invalid_val, call); } return x.to_list(); } // ----------------------------------------------------------------------------- template static inline cpp11::writable::list calendar_plus_duration_impl(Calendar& x, const ClockDuration& n) { const r_ssize size = x.size(); for (r_ssize i = 0; i < size; ++i) { if (x.is_na(i)) { continue; } if (n.is_na(i)) { x.assign_na(i); continue; } x.add(n[i], i); } return x.to_list(); } // ----------------------------------------------------------------------------- template static inline cpp11::writable::list as_sys_time_from_calendar_impl(const Calendar& x) { using Duration = typename ClockDuration::chrono_duration; const r_ssize size = x.size(); ClockDuration out(size); for (r_ssize i = 0; i < size; ++i) { if (x.is_na(i)) { out.assign_na(i); } else { date::sys_time elt_st = x.to_sys_time(i); Duration elt = elt_st.time_since_epoch(); out.assign(elt, i); } } return out.to_list(); } template cpp11::writable::list as_calendar_from_sys_time_impl(cpp11::list_of& fields) { using Duration = typename ClockDuration::chrono_duration; const ClockDuration x{fields}; const r_ssize size = x.size(); Calendar out(size); for (r_ssize i = 0; i < size; ++i) { if (x.is_na(i)) { out.assign_na(i); } else { Duration elt = x[i]; date::sys_time elt_st{elt}; out.assign_sys_time(elt_st, i); } } return out.to_list(); } #endif clock/src/week-year-week-day.h0000644000176200001440000006265614427166676015744 0ustar liggesusers#ifndef CLOCK_WEEK_YEAR_WEEK_DAY_H #define CLOCK_WEEK_YEAR_WEEK_DAY_H #include "clock.h" #include "week.h" #include "week-shim.h" #include "integers.h" #include "enums.h" #include "utils.h" #include "stream.h" #include "resolve.h" namespace rclock { namespace rweek { namespace detail { inline std::ostringstream& stream_week(std::ostringstream& os, int week) NOEXCEPT { os << 'W'; os.fill('0'); os.flags(std::ios::dec | std::ios::right); os.width(2); os << week; return os; } inline std::ostringstream& stream_day(std::ostringstream& os, int day) NOEXCEPT { os << day; return os; } inline week_shim::year_weeknum_weekday resolve_next_day_ywd(const week_shim::year_weeknum_weekday& x) { // Only invalid on nonexistent week 53 day, rolls to first day of next year return (x.year() + week::years{1}) / week::weeknum{1} / week_shim::weekday(1u); } inline week_shim::year_weeknum_weekday resolve_previous_day_ywd(const week_shim::year_weeknum_weekday& x) { // Only invalid on nonexistent week 53 day, rolls to last day of current year return x.year() / week::last / week_shim::weekday(7u); } inline week_shim::year_weeknum_weekday resolve_overflow_day_ywd(const week_shim::year_weeknum_weekday& x) { // Only invalid on nonexistent week 53 day, rolls into next year return week_shim::year_weeknum_weekday{date::sys_days{x}, x.year().start()}; } inline week_shim::year_weeknum resolve_next_day_yw(const week_shim::year_weeknum& x) { // Only invalid on nonexistent week 53 day, rolls to first week of next year return (x.year() + week::years{1}) / week::weeknum{1}; } inline week_shim::year_weeknum resolve_previous_day_yw(const week_shim::year_weeknum& x) { // Only invalid on nonexistent week 53 day, rolls to last week of current year return x.year() / week::last; } } // namespace detail class y { protected: rclock::integers year_; week::start start_; public: y(r_ssize size, week::start start); y(const cpp11::integers& year, week::start start); bool is_na(r_ssize i) const NOEXCEPT; r_ssize size() const NOEXCEPT; std::ostringstream& stream(std::ostringstream&, r_ssize i) const NOEXCEPT; void add(const date::years& x, r_ssize i) NOEXCEPT; void assign_year(const week_shim::year& x, r_ssize i) NOEXCEPT; void assign_na(r_ssize i) NOEXCEPT; week_shim::year to_year(r_ssize i) const NOEXCEPT; cpp11::writable::list to_list() const; }; class ywn : public y { protected: rclock::integers week_; public: ywn(r_ssize size, week::start start); ywn(const cpp11::integers& year, const cpp11::integers& week, week::start start); std::ostringstream& stream(std::ostringstream&, r_ssize i) const NOEXCEPT; void assign_weeknum(const week::weeknum& x, r_ssize i) NOEXCEPT; void assign_year_weeknum(const week_shim::year_weeknum& x, r_ssize i) NOEXCEPT; void assign_na(r_ssize i) NOEXCEPT; void resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call); week_shim::year_weeknum to_year_weeknum(r_ssize i) const NOEXCEPT; cpp11::writable::list to_list() const; }; class ywnwd : public ywn { protected: rclock::integers day_; public: ywnwd(r_ssize size, week::start start); ywnwd(const cpp11::integers& year, const cpp11::integers& week, const cpp11::integers& day, week::start start); std::ostringstream& stream(std::ostringstream&, r_ssize i) const NOEXCEPT; void assign_weekday(const week_shim::weekday& x, r_ssize i) NOEXCEPT; void assign_year_weeknum_weekday(const week_shim::year_weeknum_weekday& x, r_ssize i) NOEXCEPT; void assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT; void assign_na(r_ssize i) NOEXCEPT; void resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call); date::sys_time to_sys_time(r_ssize i) const NOEXCEPT; week_shim::year_weeknum_weekday to_year_weeknum_weekday(r_ssize i) const NOEXCEPT; cpp11::writable::list to_list() const; }; class ywnwdh : public ywnwd { protected: rclock::integers hour_; public: ywnwdh(r_ssize size, week::start start); ywnwdh(const cpp11::integers& year, const cpp11::integers& week, const cpp11::integers& day, const cpp11::integers& hour, week::start start); std::ostringstream& stream(std::ostringstream&, r_ssize i) const NOEXCEPT; void assign_hour(const std::chrono::hours& x, r_ssize i) NOEXCEPT; void assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT; void assign_na(r_ssize i) NOEXCEPT; void resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call); date::sys_time to_sys_time(r_ssize i) const NOEXCEPT; cpp11::writable::list to_list() const; }; class ywnwdhm : public ywnwdh { protected: rclock::integers minute_; public: ywnwdhm(r_ssize size, week::start start); ywnwdhm(const cpp11::integers& year, const cpp11::integers& week, const cpp11::integers& day, const cpp11::integers& hour, const cpp11::integers& minute, week::start start); std::ostringstream& stream(std::ostringstream&, r_ssize i) const NOEXCEPT; void assign_minute(const std::chrono::minutes& x, r_ssize i) NOEXCEPT; void assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT; void assign_na(r_ssize i) NOEXCEPT; void resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call); date::sys_time to_sys_time(r_ssize i) const NOEXCEPT; cpp11::writable::list to_list() const; }; class ywnwdhms : public ywnwdhm { protected: rclock::integers second_; public: ywnwdhms(r_ssize size, week::start start); ywnwdhms(const cpp11::integers& year, const cpp11::integers& week, const cpp11::integers& day, const cpp11::integers& hour, const cpp11::integers& minute, const cpp11::integers& second, week::start start); std::ostringstream& stream(std::ostringstream&, r_ssize i) const NOEXCEPT; void assign_second(const std::chrono::seconds& x, r_ssize i) NOEXCEPT; void assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT; void assign_na(r_ssize i) NOEXCEPT; void resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call); date::sys_time to_sys_time(r_ssize i) const NOEXCEPT; cpp11::writable::list to_list() const; }; template class ywnwdhmss : public ywnwdhms { protected: rclock::integers subsecond_; public: ywnwdhmss(r_ssize size, week::start start); ywnwdhmss(const cpp11::integers& year, const cpp11::integers& week, const cpp11::integers& day, const cpp11::integers& hour, const cpp11::integers& minute, const cpp11::integers& second, const cpp11::integers& subsecond, week::start start); std::ostringstream& stream(std::ostringstream&, r_ssize i) const NOEXCEPT; void assign_subsecond(const Duration& x, r_ssize i) NOEXCEPT; void assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT; void assign_na(r_ssize i) NOEXCEPT; void resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call); date::sys_time to_sys_time(r_ssize i) const NOEXCEPT; cpp11::writable::list to_list() const; }; // Implementation // y inline y::y(r_ssize size, week::start start) : year_(size) , start_(start) {} inline y::y(const cpp11::integers& year, week::start start) : year_(rclock::integers(year)) , start_(start) {} inline bool y::is_na(r_ssize i) const NOEXCEPT { return year_.is_na(i); } inline r_ssize y::size() const NOEXCEPT { return year_.size(); } inline std::ostringstream& y::stream(std::ostringstream& os, r_ssize i) const NOEXCEPT { rclock::detail::stream_year(os, year_[i]); return os; } inline void y::add(const date::years& x, r_ssize i) NOEXCEPT { assign_year(to_year(i) + x, i); } inline void y::assign_year(const week_shim::year& x, r_ssize i) NOEXCEPT { year_.assign(static_cast(x), i); } inline void y::assign_na(r_ssize i) NOEXCEPT { year_.assign_na(i); } inline week_shim::year y::to_year(r_ssize i) const NOEXCEPT { return week_shim::year{year_[i], start_}; } inline cpp11::writable::list y::to_list() const { cpp11::writable::list out({year_.sexp()}); out.names() = {"year"}; return out; } // ywn inline ywn::ywn(r_ssize size, week::start start) : y(size, start) , week_(size) {} inline ywn::ywn(const cpp11::integers& year, const cpp11::integers& week, week::start start) : y(year, start) , week_(week) {} inline std::ostringstream& ywn::stream(std::ostringstream& os, r_ssize i) const NOEXCEPT { y::stream(os, i); os << '-'; detail::stream_week(os, week_[i]); return os; } inline void ywn::assign_weeknum(const week::weeknum& x, r_ssize i) NOEXCEPT { week_.assign(static_cast(static_cast(x)), i); } inline void ywn::assign_year_weeknum(const week_shim::year_weeknum& x, r_ssize i) NOEXCEPT { assign_year(x.year(), i); assign_weeknum(x.weeknum(), i); } inline void ywn::assign_na(r_ssize i) NOEXCEPT { y::assign_na(i); week_.assign_na(i); } inline void ywn::resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call) { const week_shim::year_weeknum elt = to_year_weeknum(i); if (elt.ok()) { return; } switch (type) { case invalid::next_day: case invalid::next: { assign_year_weeknum(detail::resolve_next_day_yw(elt), i); break; } case invalid::previous_day: case invalid::previous: { assign_year_weeknum(detail::resolve_previous_day_yw(elt), i); break; } case invalid::overflow_day: case invalid::overflow: { // Overflowing invalid 2019-53 results in 2020-01 assign_year_weeknum(detail::resolve_next_day_yw(elt), i); break; } case invalid::na: { assign_na(i); break; } case invalid::error: { rclock::detail::resolve_error(i, call); } } } inline week_shim::year_weeknum ywn::to_year_weeknum(r_ssize i) const NOEXCEPT { return week_shim::year{year_[i], start_} / static_cast(week_[i]); } inline cpp11::writable::list ywn::to_list() const { cpp11::writable::list out({year_.sexp(), week_.sexp()}); out.names() = {"year", "week"}; return out; } // ywnwd inline ywnwd::ywnwd(r_ssize size, week::start start) : ywn(size, start) , day_(size) {} inline ywnwd::ywnwd(const cpp11::integers& year, const cpp11::integers& week, const cpp11::integers& day, week::start start) : ywn(year, week, start) , day_(day) {} inline std::ostringstream& ywnwd::stream(std::ostringstream& os, r_ssize i) const NOEXCEPT { ywn::stream(os, i); os << '-'; detail::stream_day(os, day_[i]); return os; } inline void ywnwd::assign_weekday(const week_shim::weekday& x, r_ssize i) NOEXCEPT { day_.assign(static_cast(static_cast(x)), i); } inline void ywnwd::assign_year_weeknum_weekday(const week_shim::year_weeknum_weekday& x, r_ssize i) NOEXCEPT { assign_year(x.year(), i); assign_weeknum(x.weeknum(), i); assign_weekday(x.weekday(), i); } inline void ywnwd::assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT { week_shim::year_weeknum_weekday ywnwd{x, start_}; assign_year_weeknum_weekday(ywnwd, i); } inline void ywnwd::assign_na(r_ssize i) NOEXCEPT { ywn::assign_na(i); day_.assign_na(i); } inline void ywnwd::resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call) { const week_shim::year_weeknum_weekday elt = to_year_weeknum_weekday(i); if (elt.ok()) { return; } switch (type) { case invalid::next_day: case invalid::next: { assign_year_weeknum_weekday(detail::resolve_next_day_ywd(elt), i); break; } case invalid::previous_day: case invalid::previous: { assign_year_weeknum_weekday(detail::resolve_previous_day_ywd(elt), i); break; } case invalid::overflow_day: case invalid::overflow: { assign_year_weeknum_weekday(detail::resolve_overflow_day_ywd(elt), i); break; } case invalid::na: { assign_na(i); break; } case invalid::error: { rclock::detail::resolve_error(i, call); } } } inline date::sys_time ywnwd::to_sys_time(r_ssize i) const NOEXCEPT { return date::sys_time{to_year_weeknum_weekday(i)}; } inline week_shim::year_weeknum_weekday ywnwd::to_year_weeknum_weekday(r_ssize i) const NOEXCEPT { return week_shim::year{year_[i], start_} / static_cast(week_[i]) / static_cast(day_[i]); } inline cpp11::writable::list ywnwd::to_list() const { cpp11::writable::list out({year_.sexp(), week_.sexp(), day_.sexp()}); out.names() = {"year", "week", "day"}; return out; } // ywnwdh inline ywnwdh::ywnwdh(r_ssize size, week::start start) : ywnwd(size, start) , hour_(size) {} inline ywnwdh::ywnwdh(const cpp11::integers& year, const cpp11::integers& week, const cpp11::integers& day, const cpp11::integers& hour, week::start start) : ywnwd(year, week, day, start) , hour_(hour) {} inline std::ostringstream& ywnwdh::stream(std::ostringstream& os, r_ssize i) const NOEXCEPT { ywnwd::stream(os, i); os << 'T'; rclock::detail::stream_hour(os, hour_[i]); return os; } inline void ywnwdh::assign_hour(const std::chrono::hours& x, r_ssize i) NOEXCEPT { hour_.assign(x.count(), i); } inline void ywnwdh::assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT { const date::sys_time day_point = date::floor(x); const std::chrono::hours hours = x - day_point; ywnwd::assign_sys_time(day_point, i); assign_hour(hours, i); } inline void ywnwdh::assign_na(r_ssize i) NOEXCEPT { ywnwd::assign_na(i); hour_.assign_na(i); } inline void ywnwdh::resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call) { const week_shim::year_weeknum_weekday elt = to_year_weeknum_weekday(i); if (elt.ok()) { return; } switch (type) { case invalid::next_day: assign_year_weeknum_weekday(detail::resolve_next_day_ywd(elt), i); break; case invalid::next: { assign_year_weeknum_weekday(detail::resolve_next_day_ywd(elt), i); assign_hour(rclock::detail::resolve_next_hour(), i); break; } case invalid::previous_day: assign_year_weeknum_weekday(detail::resolve_previous_day_ywd(elt), i); break; case invalid::previous: { assign_year_weeknum_weekday(detail::resolve_previous_day_ywd(elt), i); assign_hour(rclock::detail::resolve_previous_hour(), i); break; } case invalid::overflow_day: assign_year_weeknum_weekday(detail::resolve_overflow_day_ywd(elt), i); break; case invalid::overflow: { assign_year_weeknum_weekday(detail::resolve_overflow_day_ywd(elt), i); assign_hour(rclock::detail::resolve_next_hour(), i); break; } case invalid::na: { assign_na(i); break; } case invalid::error: { rclock::detail::resolve_error(i, call); } } } inline date::sys_time ywnwdh::to_sys_time(r_ssize i) const NOEXCEPT { return ywnwd::to_sys_time(i) + std::chrono::hours{hour_[i]}; } inline cpp11::writable::list ywnwdh::to_list() const { cpp11::writable::list out({year_.sexp(), week_.sexp(), day_.sexp(), hour_.sexp()}); out.names() = {"year", "week", "day", "hour"}; return out; } // ywnwdhm inline ywnwdhm::ywnwdhm(r_ssize size, week::start start) : ywnwdh(size, start) , minute_(size) {} inline ywnwdhm::ywnwdhm(const cpp11::integers& year, const cpp11::integers& week, const cpp11::integers& day, const cpp11::integers& hour, const cpp11::integers& minute, week::start start) : ywnwdh(year, week, day, hour, start) , minute_(minute) {} inline std::ostringstream& ywnwdhm::stream(std::ostringstream& os, r_ssize i) const NOEXCEPT { ywnwdh::stream(os, i); os << ':'; rclock::detail::stream_minute(os, minute_[i]); return os; } inline void ywnwdhm::assign_minute(const std::chrono::minutes& x, r_ssize i) NOEXCEPT { minute_.assign(x.count(), i); } inline void ywnwdhm::assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT { const date::sys_time hour_point = date::floor(x); const std::chrono::minutes minutes = x - hour_point; ywnwdh::assign_sys_time(hour_point, i); assign_minute(minutes, i); } inline void ywnwdhm::assign_na(r_ssize i) NOEXCEPT { ywnwdh::assign_na(i); minute_.assign_na(i); } inline void ywnwdhm::resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call) { const week_shim::year_weeknum_weekday elt = to_year_weeknum_weekday(i); if (elt.ok()) { return; } switch (type) { case invalid::next_day: assign_year_weeknum_weekday(detail::resolve_next_day_ywd(elt), i); break; case invalid::next: { assign_year_weeknum_weekday(detail::resolve_next_day_ywd(elt), i); assign_hour(rclock::detail::resolve_next_hour(), i); assign_minute(rclock::detail::resolve_next_minute(), i); break; } case invalid::previous_day: assign_year_weeknum_weekday(detail::resolve_previous_day_ywd(elt), i); break; case invalid::previous: { assign_year_weeknum_weekday(detail::resolve_previous_day_ywd(elt), i); assign_hour(rclock::detail::resolve_previous_hour(), i); assign_minute(rclock::detail::resolve_previous_minute(), i); break; } case invalid::overflow_day: assign_year_weeknum_weekday(detail::resolve_overflow_day_ywd(elt), i); break; case invalid::overflow: { assign_year_weeknum_weekday(detail::resolve_overflow_day_ywd(elt), i); assign_hour(rclock::detail::resolve_next_hour(), i); assign_minute(rclock::detail::resolve_next_minute(), i); break; } case invalid::na: { assign_na(i); break; } case invalid::error: { rclock::detail::resolve_error(i, call); } } } inline date::sys_time ywnwdhm::to_sys_time(r_ssize i) const NOEXCEPT { return ywnwdh::to_sys_time(i) + std::chrono::minutes{minute_[i]}; } inline cpp11::writable::list ywnwdhm::to_list() const { cpp11::writable::list out({year_.sexp(), week_.sexp(), day_.sexp(), hour_.sexp(), minute_.sexp()}); out.names() = {"year", "week", "day", "hour", "minute"}; return out; } // ywnwdhms inline ywnwdhms::ywnwdhms(r_ssize size, week::start start) : ywnwdhm(size, start) , second_(size) {} inline ywnwdhms::ywnwdhms(const cpp11::integers& year, const cpp11::integers& week, const cpp11::integers& day, const cpp11::integers& hour, const cpp11::integers& minute, const cpp11::integers& second, week::start start) : ywnwdhm(year, week, day, hour, minute, start) , second_(second) {} inline std::ostringstream& ywnwdhms::stream(std::ostringstream& os, r_ssize i) const NOEXCEPT { ywnwdhm::stream(os, i); os << ':'; rclock::detail::stream_second(os, second_[i]); return os; } inline void ywnwdhms::assign_second(const std::chrono::seconds& x, r_ssize i) NOEXCEPT { second_.assign(x.count(), i); } inline void ywnwdhms::assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT { const date::sys_time minute_point = date::floor(x); const std::chrono::seconds seconds = x - minute_point; ywnwdhm::assign_sys_time(minute_point, i); assign_second(seconds, i); } inline void ywnwdhms::assign_na(r_ssize i) NOEXCEPT { ywnwdhm::assign_na(i); second_.assign_na(i); } inline void ywnwdhms::resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call) { const week_shim::year_weeknum_weekday elt = to_year_weeknum_weekday(i); if (elt.ok()) { return; } switch (type) { case invalid::next_day: assign_year_weeknum_weekday(detail::resolve_next_day_ywd(elt), i); break; case invalid::next: { assign_year_weeknum_weekday(detail::resolve_next_day_ywd(elt), i); assign_hour(rclock::detail::resolve_next_hour(), i); assign_minute(rclock::detail::resolve_next_minute(), i); assign_second(rclock::detail::resolve_next_second(), i); break; } case invalid::previous_day: assign_year_weeknum_weekday(detail::resolve_previous_day_ywd(elt), i); break; case invalid::previous: { assign_year_weeknum_weekday(detail::resolve_previous_day_ywd(elt), i); assign_hour(rclock::detail::resolve_previous_hour(), i); assign_minute(rclock::detail::resolve_previous_minute(), i); assign_second(rclock::detail::resolve_previous_second(), i); break; } case invalid::overflow_day: assign_year_weeknum_weekday(detail::resolve_overflow_day_ywd(elt), i); break; case invalid::overflow: { assign_year_weeknum_weekday(detail::resolve_overflow_day_ywd(elt), i); assign_hour(rclock::detail::resolve_next_hour(), i); assign_minute(rclock::detail::resolve_next_minute(), i); assign_second(rclock::detail::resolve_next_second(), i); break; } case invalid::na: { assign_na(i); break; } case invalid::error: { rclock::detail::resolve_error(i, call); } } } inline date::sys_time ywnwdhms::to_sys_time(r_ssize i) const NOEXCEPT { return ywnwdhm::to_sys_time(i) + std::chrono::seconds{second_[i]}; } inline cpp11::writable::list ywnwdhms::to_list() const { cpp11::writable::list out({year_.sexp(), week_.sexp(), day_.sexp(), hour_.sexp(), minute_.sexp(), second_.sexp()}); out.names() = {"year", "week", "day", "hour", "minute", "second"}; return out; } // ywnwdhmss template inline ywnwdhmss::ywnwdhmss(r_ssize size, week::start start) : ywnwdhms(size, start) , subsecond_(size) {} template inline ywnwdhmss::ywnwdhmss(const cpp11::integers& year, const cpp11::integers& week, const cpp11::integers& day, const cpp11::integers& hour, const cpp11::integers& minute, const cpp11::integers& second, const cpp11::integers& subsecond, week::start start) : ywnwdhms(year, week, day, hour, minute, second, start) , subsecond_(subsecond) {} template inline std::ostringstream& ywnwdhmss::stream(std::ostringstream& os, r_ssize i) const NOEXCEPT { ywnwdhm::stream(os, i); os << ':'; rclock::detail::stream_second_and_subsecond(os, second_[i], subsecond_[i]); return os; } template inline void ywnwdhmss::assign_subsecond(const Duration& x, r_ssize i) NOEXCEPT { subsecond_.assign(x.count(), i); } template inline void ywnwdhmss::assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT { const date::sys_time second_point = date::floor(x); const Duration subseconds = x - second_point; ywnwdhms::assign_sys_time(second_point, i); assign_subsecond(subseconds, i); } template inline void ywnwdhmss::assign_na(r_ssize i) NOEXCEPT { ywnwdhms::assign_na(i); subsecond_.assign_na(i); } template inline void ywnwdhmss::resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call) { const week_shim::year_weeknum_weekday elt = to_year_weeknum_weekday(i); if (elt.ok()) { return; } switch (type) { case invalid::next_day: assign_year_weeknum_weekday(detail::resolve_next_day_ywd(elt), i); break; case invalid::next: { assign_year_weeknum_weekday(detail::resolve_next_day_ywd(elt), i); assign_hour(rclock::detail::resolve_next_hour(), i); assign_minute(rclock::detail::resolve_next_minute(), i); assign_second(rclock::detail::resolve_next_second(), i); assign_subsecond(rclock::detail::resolve_next_subsecond(), i); break; } case invalid::previous_day: assign_year_weeknum_weekday(detail::resolve_previous_day_ywd(elt), i); break; case invalid::previous: { assign_year_weeknum_weekday(detail::resolve_previous_day_ywd(elt), i); assign_hour(rclock::detail::resolve_previous_hour(), i); assign_minute(rclock::detail::resolve_previous_minute(), i); assign_second(rclock::detail::resolve_previous_second(), i); assign_subsecond(rclock::detail::resolve_previous_subsecond(), i); break; } case invalid::overflow_day: assign_year_weeknum_weekday(detail::resolve_overflow_day_ywd(elt), i); break; case invalid::overflow: { assign_year_weeknum_weekday(detail::resolve_overflow_day_ywd(elt), i); assign_hour(rclock::detail::resolve_next_hour(), i); assign_minute(rclock::detail::resolve_next_minute(), i); assign_second(rclock::detail::resolve_next_second(), i); assign_subsecond(rclock::detail::resolve_next_subsecond(), i); break; } case invalid::na: { assign_na(i); break; } case invalid::error: { rclock::detail::resolve_error(i, call); } } } template inline date::sys_time ywnwdhmss::to_sys_time(r_ssize i) const NOEXCEPT { return ywnwdhms::to_sys_time(i) + Duration{subsecond_[i]}; } template inline cpp11::writable::list ywnwdhmss::to_list() const { cpp11::writable::list out({year_.sexp(), week_.sexp(), day_.sexp(), hour_.sexp(), minute_.sexp(), second_.sexp(), subsecond_.sexp()}); out.names() = {"year", "week", "day", "hour", "minute", "second", "subsecond"}; return out; } } // namespace rweek } // namespace rclock #endif clock/src/rcrd.h0000644000176200001440000000031014417321664013241 0ustar liggesusers#ifndef CLOCK_RCRD_H #define CLOCK_RCRD_H #include "clock.h" SEXP new_clock_rcrd_from_fields(SEXP fields, SEXP names, SEXP classes); SEXP clock_rcrd_restore(SEXP x, SEXP to, SEXP classes); #endif clock/src/sys-time.cpp0000644000176200001440000000673014422221153014414 0ustar liggesusers#include "duration.h" #include "get.h" #include "zone.h" [[cpp11::register]] cpp11::writable::list sys_time_now_cpp() { using namespace std::chrono; typename system_clock::time_point tp = system_clock::now(); // The precision of `system_clock::now()` is platform dependent. // We just cast them all to nanoseconds to at least return a consistent precision. // - MacOS = 1us // - Linux = 1ns // - Windows = 100ns // https://stackoverflow.com/questions/55120594/different-behaviour-of-system-clock-on-windows-and-linux nanoseconds d = duration_cast(tp.time_since_epoch()); rclock::duration::nanoseconds out(1); out.assign(d, 0); return out.to_list(); } // ----------------------------------------------------------------------------- template static inline cpp11::writable::list sys_time_info_impl(cpp11::list_of& fields, const cpp11::strings& zone) { using Duration = typename ClockDuration::chrono_duration; const ClockDuration x{fields}; const r_ssize size = x.size(); rclock::duration::seconds begin(size); rclock::duration::seconds end(size); rclock::duration::seconds offset(size); cpp11::writable::logicals dst(size); cpp11::writable::strings abbreviation(size); const std::chrono::minutes zero{0}; const bool recycle_zone = zone.size() == 1; const date::time_zone* p_time_zone; if (recycle_zone) { const std::string zone_name = cpp11::r_string(zone[0]); p_time_zone = zone_name_load(zone_name); } for (r_ssize i = 0; i < size; ++i) { if (x.is_na(i)) { begin.assign_na(i); end.assign_na(i); offset.assign_na(i); dst[i] = r_lgl_na; SET_STRING_ELT(abbreviation, i, r_chr_na); continue; } const date::time_zone* p_time_zone_elt; if (recycle_zone) { p_time_zone_elt = p_time_zone; } else { const std::string zone_name_elt = cpp11::r_string(zone[i]); p_time_zone_elt = zone_name_load(zone_name_elt); } const date::sys_time elt{x[i]}; const date::sys_info info = rclock::get_info(elt, p_time_zone_elt); begin.assign(info.begin.time_since_epoch(), i); end.assign(info.end.time_since_epoch(), i); offset.assign(info.offset, i); dst[i] = info.save != zero; SET_STRING_ELT(abbreviation, i, Rf_mkCharLenCE(info.abbrev.c_str(), info.abbrev.size(), CE_UTF8)); } cpp11::writable::list out_begin = begin.to_list(); cpp11::writable::list out_end = end.to_list(); cpp11::writable::list out_offset = offset.to_list(); cpp11::writable::list out = { out_begin, out_end, out_offset, dst, abbreviation}; out.names() = {"begin", "end", "offset", "dst", "abbreviation"}; return out; } [[cpp11::register]] cpp11::writable::list sys_time_info_cpp(cpp11::list_of fields, const cpp11::integers& precision_int, const cpp11::strings& zone) { using namespace rclock; switch (parse_precision(precision_int)) { case precision::day: return sys_time_info_impl(fields, zone); case precision::second: return sys_time_info_impl(fields, zone); case precision::millisecond: return sys_time_info_impl(fields, zone); case precision::microsecond: return sys_time_info_impl(fields, zone); case precision::nanosecond: return sys_time_info_impl(fields, zone); default: clock_abort("Internal error: Should never be called."); } } clock/src/integers.h0000644000176200001440000000316514417321664014142 0ustar liggesusers#ifndef CLOCK_INTEGERS_H #define CLOCK_INTEGERS_H #include "clock.h" namespace rclock { class integers { const cpp11::integers read_; cpp11::writable::integers write_; bool writable_; r_ssize size_; public: integers() noexcept; integers(const cpp11::integers& x); integers(r_ssize size); bool is_na(r_ssize i) const noexcept; r_ssize size() const noexcept; void assign(int x, r_ssize i); void assign_na(r_ssize i); int operator[](r_ssize i) const noexcept; SEXP sexp() const noexcept; }; namespace detail { static const cpp11::integers empty_integers = cpp11::integers{}; } // namespace detail inline integers::integers() noexcept : read_(detail::empty_integers), writable_(false), size_(0) {} inline integers::integers(const cpp11::integers& x) : read_(x), writable_(false), size_(x.size()) {} inline integers::integers(r_ssize size) : read_(detail::empty_integers), write_(cpp11::writable::integers(size)), writable_(true), size_(size) {} inline bool integers::is_na(r_ssize i) const noexcept { return this->operator[](i) == r_int_na; } inline r_ssize integers::size() const noexcept { return size_; } inline void integers::assign(int x, r_ssize i) { if (!writable_) { write_ = cpp11::writable::integers(read_); writable_ = true; } write_[i] = x; } inline void integers::assign_na(r_ssize i) { return assign(r_int_na, i); } inline int integers::operator[](r_ssize i) const noexcept { return writable_ ? write_[i] : read_[i]; } inline SEXP integers::sexp() const noexcept { return writable_ ? write_ : read_; } } // namespace rclock #endif clock/src/utils.h0000644000176200001440000001676614422221153013461 0ustar liggesusers#ifndef CLOCK_UTILS_H #define CLOCK_UTILS_H #include "clock.h" #include #include #include // For `va_start()` and `va_end()` #include // For `vsnprintf()` #include // ----------------------------------------------------------------------------- extern SEXP strings_empty; extern SEXP syms_precision; extern SEXP syms_start; extern SEXP syms_clock; extern SEXP syms_zone; extern SEXP syms_set_names; extern SEXP classes_duration; extern SEXP classes_sys_time; extern SEXP classes_naive_time; extern SEXP classes_zoned_time; extern SEXP classes_year_month_day; extern SEXP classes_year_month_weekday; extern SEXP classes_year_day; extern SEXP classes_year_week_day; extern SEXP classes_iso_year_week_day; extern SEXP classes_year_quarter_day; extern SEXP classes_data_frame; extern SEXP ints_empty; // ----------------------------------------------------------------------------- namespace rclock { namespace detail { } // namespace detail // Essentially date's `time_zone::get_info(sys_time st)`, but goes // through `tzdb::` to get the sys_info template static inline date::sys_info get_info(const date::sys_time& tp, const date::time_zone* p_time_zone) { const date::sys_seconds ss = date::floor(tp); date::sys_info info; if (!tzdb::get_sys_info(ss, p_time_zone, info)) { cpp11::stop("Can't lookup sys information for the supplied time zone."); } return info; } // Essentially date's `time_zone::get_info(local_time lt)`, but goes // through `tzdb::` to get the local_info template static inline date::local_info get_info(const date::local_time& tp, const date::time_zone* p_time_zone) { const date::local_seconds ls = date::floor(tp); date::local_info info; if (!tzdb::get_local_info(ls, p_time_zone, info)) { cpp11::stop("Can't lookup local information for the supplied time zone."); } return info; } // Essentially date's `time_zone::to_local(sys_time tp)`, but goes // through `tzdb::` to get the sys_info template static inline date::local_time::type> get_local_time(const date::sys_time& tp, const date::time_zone* p_time_zone) { using LT = date::local_time::type>; const date::sys_info info = rclock::get_info(tp, p_time_zone); return LT{(tp + info.offset).time_since_epoch()}; } } // namespace rclock // ----------------------------------------------------------------------------- static inline SEXP r_clone_referenced(SEXP x) { if (MAYBE_REFERENCED(x)) { return Rf_shallow_duplicate(x); } else { return x; } } static inline const SEXP* r_chr_deref_const(SEXP x) { return (const SEXP*) STRING_PTR(x); } static inline const SEXP* r_list_deref_const(SEXP x) { #if (R_VERSION < R_Version(3, 5, 0)) return ((const SEXP*) STRING_PTR(x)); #else return ((const SEXP*) DATAPTR_RO(x)); #endif } // ----------------------------------------------------------------------------- template static inline T clock_safe_subtract(T x, T y) { static const T max = std::numeric_limits::max(); static const T min = std::numeric_limits::min(); if ((y > 0 && x < (min + y)) || (y < 0 && x > (max + y))) { cpp11::stop( "Internal error in `clock_safe_subtract()`: " "Subtraction resulted in overflow or underflow." ); } return x - y; } // ----------------------------------------------------------------------------- static inline SEXP new_compact_rownames(r_ssize n_rows) { if (n_rows <= 0) { return ints_empty; } SEXP out = Rf_allocVector(INTSXP, 2); int* p_out = INTEGER(out); p_out[0] = r_int_na; p_out[1] = -n_rows; return out; } static inline void init_compact_rownames(SEXP x, r_ssize n_rows) { SEXP rn = PROTECT(new_compact_rownames(n_rows)); Rf_setAttrib(x, R_RowNamesSymbol, rn); UNPROTECT(1); } static inline void r_init_data_frame(SEXP x, r_ssize n_rows) { init_compact_rownames(x, n_rows); Rf_setAttrib(x, R_ClassSymbol, classes_data_frame); } // ----------------------------------------------------------------------------- /* * `names<-()` can take advantage of shallow duplication using an ALTREP wrapper * which is otherwise not exposed in the R API */ static inline SEXP set_names_dispatch(SEXP x, SEXP value) { SEXP call = PROTECT(Rf_lang3(syms_set_names, x, value)); SEXP out = Rf_eval(call, R_GlobalEnv); UNPROTECT(1); return out; } // ----------------------------------------------------------------------------- // "Safe" variants on rlib functions static inline bool r_dbl_is_missing(double x) { return ISNAN(x); } static inline bool r_is_scalar(SEXP x) { return Rf_xlength(x) == 1; } static inline bool clock_is_scalar(const cpp11::sexp& x) { return cpp11::safe[r_is_scalar](x); } static inline bool r_is_string(SEXP x) { return (TYPEOF(x) == STRSXP) && (Rf_length(x) == 1) && (STRING_ELT(x, 0) != r_chr_na); } static inline bool clock_is_string(const cpp11::sexp& x) { return cpp11::safe[r_is_string](x); } // ----------------------------------------------------------------------------- // Max - If int64_t is a long long, this converts `9223372036854775807` -> // `9223372036854775808` as that is the next possible value representable as a // double. // // Min - This should let `-9223372036854775808` remain as is, as it is // directly representable as a double. // // Practically this just forces us to use `x >= INT64_MAX_AS_DOUBLE` rather // than just `>`, as you can't ever have a double value right on `INT64_MAX`. static const double INT64_MAX_AS_DOUBLE = static_cast(INT64_MAX); static const double INT64_MIN_AS_DOUBLE = static_cast(INT64_MIN); static inline bool clock_dbl_is_oob_for_int64(double x) { return x >= INT64_MAX_AS_DOUBLE || x < INT64_MIN_AS_DOUBLE; } /* * Floor to get rid of fractional seconds. This is the most consistent way to * drop them to pretend like they don't exist. Using `floor()` to round towards * negative infinity is the correct thing to do with pre 1970 (i.e. negative) * times. * * For example: * * unclass(as.POSIXct("1969-12-31 23:59:59.9999", "UTC")) * [1] -0.0001000000000033196556615 * * Truncating to 0 gives 1970-01-01. * * Flooring to -1 gives 1969-12-31 23:59:59, i.e. the "correct" result if we are * ignoring fractional seconds. */ static inline int64_t clock_dbl_as_int64(double x) { x = std::floor(x); return static_cast(x); } // ----------------------------------------------------------------------------- /* * clock_abort() calls back to `rlang::abort()` to actually throw the error, * with unwind protection. */ #define BUFSIZE 8192 static inline void fill_buffer(char* buf, const char* fmt, ...) { std::va_list dots; va_start(dots, fmt); std::vsnprintf(buf, BUFSIZE, fmt, dots); va_end(dots); buf[BUFSIZE - 1] = '\0'; } template void clock_abort [[noreturn]] (const char* fmt, Args... args) { char buf[BUFSIZE]; fill_buffer(buf, fmt, args...); cpp11::r_string string{buf}; cpp11::writable::strings arg({string}); auto abort = cpp11::package("rlang")["abort"]; abort(arg); cpp11::stop("Internal error: Got past an rlang::abort()!"); } #undef BUFSIZE static inline void never_reached [[noreturn]] (const char* fn) { clock_abort("Internal error: Reached the unreachable in `%s()`.", fn); } // ----------------------------------------------------------------------------- #endif clock/src/duration.h0000644000176200001440000003210314423730227014135 0ustar liggesusers#ifndef CLOCK_DURATION_H #define CLOCK_DURATION_H #include "clock.h" #include "doubles.h" #include "enums.h" #include "utils.h" #include namespace rclock { namespace duration { template class duration { public: CONSTCD11 duration(cpp11::list_of& fields) NOEXCEPT; duration(r_ssize size); CONSTCD11 bool is_na(r_ssize i) const NOEXCEPT; CONSTCD11 r_ssize size() const NOEXCEPT; void assign_na(r_ssize i); void assign(const Duration& x, r_ssize i); CONSTCD14 Duration operator[](r_ssize i) const NOEXCEPT; cpp11::writable::list to_list() const; // Only used by `zoned-time.cpp` void convert_local_to_sys_and_assign(const date::local_time& x, const date::local_info& info, const enum nonexistent& nonexistent_val, const enum ambiguous& ambiguous_val, const r_ssize& i, const cpp11::sexp& call); void convert_local_with_reference_to_sys_and_assign(const date::local_time& x, const date::local_info& info, const enum nonexistent& nonexistent_val, const enum ambiguous& ambiguous_val, const date::sys_seconds& reference, const date::time_zone* p_time_zone, const r_ssize& i, const cpp11::sexp& call); using chrono_duration = Duration; protected: rclock::doubles lower_; rclock::doubles upper_; }; using years = duration; using quarters = duration; using months = duration; using weeks = duration; using days = duration; using hours = duration; using minutes = duration; using seconds = duration; using milliseconds = duration; using microseconds = duration; using nanoseconds = duration; // Implementation namespace detail { static inline cpp11::doubles get_lower(cpp11::list_of& fields) { return fields[0]; } static inline cpp11::doubles get_upper(cpp11::list_of& fields) { return fields[1]; } } template CONSTCD11 inline duration::duration(cpp11::list_of& fields) NOEXCEPT : lower_(detail::get_lower(fields)) , upper_(detail::get_upper(fields)) {} template inline duration::duration(r_ssize size) : lower_(size) , upper_(size) {} template CONSTCD11 inline bool duration::is_na(r_ssize i) const NOEXCEPT { return lower_.is_na(i); } template CONSTCD11 inline r_ssize duration::size() const NOEXCEPT { return lower_.size(); } template inline void duration::assign_na(r_ssize i) { lower_.assign_na(i); upper_.assign_na(i); } namespace detail { /* * This pair of functions facilitates: * - Splitting an `int64_t` into two `uint32_t` values, maintaining order * - Combining those two `uint32_t` values back into the original `int32_t` * * The two `uint32_t` values are stored in two doubles. This allows us to store * it in a two column data frame that vctrs knows how to work with, and we can * use the standard `NA_real_` as the missing value without fear of conflicting * with any other valid `int64_t` value. * * Unsigned 32-bit integers are used because bit shifting is undefined on signed * types. * * An arithmetic shift of `- std::numeric_limits::min()` is done to * remap the `int64_t` value into `uint64_t` space, while maintaining order. * This relies on unsigned arithmetic overflow behavior, which is well-defined. * * Taken from vctrs: * https://github.com/r-lib/vctrs/blob/c27b6988bd2f02aa970b6d14a640eccb299e03bb/src/type-integer64.c#L117-L156 */ CONSTCD14 static inline std::pair int64_unpack(int64_t x) { const uint64_t x_u64 = static_cast(x) - std::numeric_limits::min(); const uint32_t left_u32 = static_cast(x_u64 >> 32); const uint32_t right_u32 = static_cast(x_u64); const double left_out = static_cast(left_u32); const double right_out = static_cast(right_u32); return std::make_pair(left_out, right_out); } CONSTCD14 static inline int64_t int64_pack(double left, double right) { const uint32_t left_u32 = static_cast(left); const uint32_t right_u32 = static_cast(right); const uint64_t out_u64 = static_cast(left_u32) << 32 | right_u32; const int64_t out = static_cast(out_u64 + std::numeric_limits::min()); return out; } } // namespace details template inline void duration::assign(const Duration& x, r_ssize i) { const int64_t elt = static_cast(x.count()); std::pair unpacked = detail::int64_unpack(elt); lower_.assign(unpacked.first, i); upper_.assign(unpacked.second, i); } template CONSTCD14 inline Duration duration::operator[](r_ssize i) const NOEXCEPT { using rep = typename Duration::rep; const int64_t packed = detail::int64_pack(lower_[i], upper_[i]); const rep elt = static_cast(packed); return Duration{elt}; } template inline cpp11::writable::list duration::to_list() const { cpp11::writable::list out({lower_.sexp(), upper_.sexp()}); out.names() = {"lower", "upper"}; return out; } namespace detail { template inline date::sys_time info_unique(const date::local_info& info, const date::local_time& lt) { std::chrono::seconds offset = info.first.offset; return date::sys_time{lt.time_since_epoch()} - offset; } template inline date::sys_time info_nonexistent_roll_forward(const date::local_info& info) { return info.second.begin; } template inline date::sys_time info_nonexistent_roll_backward(const date::local_info& info) { return info_nonexistent_roll_forward(info) - Duration{1}; } template inline date::sys_time info_nonexistent_shift_forward(const date::local_info& info, const date::local_time& lt) { std::chrono::seconds offset = info.second.offset; std::chrono::seconds gap = info.second.offset - info.first.offset; date::local_time lt_shift = lt + gap; return date::sys_time{lt_shift.time_since_epoch()} - offset; } template inline date::sys_time info_nonexistent_shift_backward(const date::local_info& info, const date::local_time& lt) { std::chrono::seconds offset = info.first.offset; std::chrono::seconds gap = info.second.offset - info.first.offset; date::local_time lt_shift = lt - gap; return date::sys_time{lt_shift.time_since_epoch()} - offset; } inline void info_nonexistent_error(const r_ssize& i, const cpp11::sexp& call) { cpp11::writable::integers arg(1); arg[0] = (int) i + 1; auto stop = cpp11::package("clock")["stop_clock_nonexistent_time"]; stop(arg, call); } template inline date::sys_time info_ambiguous_earliest(const date::local_info& info, const date::local_time& lt) { std::chrono::seconds offset = info.first.offset; return date::sys_time{lt.time_since_epoch()} - offset; } template inline date::sys_time info_ambiguous_latest(const date::local_info& info, const date::local_time& lt) { std::chrono::seconds offset = info.second.offset; return date::sys_time{lt.time_since_epoch()} - offset; } inline void info_ambiguous_error(const r_ssize& i, const cpp11::sexp& call) { cpp11::writable::integers arg(1); arg[0] = (int) i + 1; auto stop = cpp11::package("clock")["stop_clock_ambiguous_time"]; stop(arg, call); } } // namespace detail /* * Zoned times have at least seconds precision, so we expect that every * instantiation of this will have a `Duration` of at least second precision */ template inline void duration::convert_local_to_sys_and_assign(const date::local_time& x, const date::local_info& info, const enum nonexistent& nonexistent_val, const enum ambiguous& ambiguous_val, const r_ssize& i, const cpp11::sexp& call) { switch (info.result) { case date::local_info::unique: { date::sys_time st = detail::info_unique(info, x); assign(st.time_since_epoch(), i); break; } case date::local_info::nonexistent: { switch (nonexistent_val) { case nonexistent::roll_forward: { date::sys_time st = detail::info_nonexistent_roll_forward(info); assign(st.time_since_epoch(), i); break; } case nonexistent::roll_backward: { date::sys_time st = detail::info_nonexistent_roll_backward(info); assign(st.time_since_epoch(), i); break; } case nonexistent::shift_forward: { date::sys_time st = detail::info_nonexistent_shift_forward(info, x); assign(st.time_since_epoch(), i); break; } case nonexistent::shift_backward: { date::sys_time st = detail::info_nonexistent_shift_backward(info, x); assign(st.time_since_epoch(), i); break; } case nonexistent::na: { assign_na(i); break; } case nonexistent::error: { detail::info_nonexistent_error(i, call); } } break; } case date::local_info::ambiguous: { switch (ambiguous_val) { case ambiguous::earliest: { date::sys_time st = detail::info_ambiguous_earliest(info, x); assign(st.time_since_epoch(), i); break; } case ambiguous::latest: { date::sys_time st = detail::info_ambiguous_latest(info, x); assign(st.time_since_epoch(), i); break; } case ambiguous::na: { assign_na(i); break; } case ambiguous::error: { detail::info_ambiguous_error(i, call); } } break; } } } template inline void duration::convert_local_with_reference_to_sys_and_assign(const date::local_time& x, const date::local_info& info, const enum nonexistent& nonexistent_val, const enum ambiguous& ambiguous_val, const date::sys_seconds& reference, const date::time_zone* p_time_zone, const r_ssize& i, const cpp11::sexp& call) { if (info.result == date::local_info::unique || info.result == date::local_info::nonexistent) { // For `unique` and `nonexistent`, nothing changes convert_local_to_sys_and_assign(x, info, nonexistent_val, ambiguous_val, i, call); return; } const date::local_seconds ref_lt = rclock::get_local_time(reference, p_time_zone); const date::local_info ref_info = rclock::get_info(ref_lt, p_time_zone); if (ref_info.result != date::local_info::ambiguous) { // If reference time is not ambiguous, we can't get any offset information // from it so fallback to using `ambiguous_val` convert_local_to_sys_and_assign(x, info, nonexistent_val, ambiguous_val, i, call); return; } if (ref_info.first.end != info.first.end) { // If reference time is ambiguous, but the transitions don't match, // we again can't get offset information from it convert_local_to_sys_and_assign(x, info, nonexistent_val, ambiguous_val, i, call); return; } const std::chrono::seconds offset = reference < ref_info.first.end ? ref_info.first.offset : ref_info.second.offset; const date::sys_time st = date::sys_time{x.time_since_epoch()} - offset; assign(st.time_since_epoch(), i); } } // namespace duration } // namespace rclock // `std::common_type()` specialization for `rclock::duration` types namespace std { template struct common_type, rclock::duration::duration> { using type = rclock::duration::duration::type>; }; } #endif clock/src/naive-time.cpp0000644000176200001440000001251514422221153014676 0ustar liggesusers#include "duration.h" #include "get.h" #include "zone.h" #include "utils.h" // ----------------------------------------------------------------------------- template static inline cpp11::writable::list naive_time_info_impl(cpp11::list_of& fields, const cpp11::strings& zone) { const ClockDuration x{fields}; const r_ssize size = x.size(); using Duration = typename ClockDuration::chrono_duration; cpp11::writable::strings type(size); cpp11::r_string type_unique{"unique"}; cpp11::r_string type_nonexistent{"nonexistent"}; cpp11::r_string type_ambiguous{"ambiguous"}; rclock::duration::seconds first_begin(size); rclock::duration::seconds first_end(size); rclock::duration::seconds first_offset(size); cpp11::writable::logicals first_dst(size); cpp11::writable::strings first_abbreviation(size); rclock::duration::seconds second_begin(size); rclock::duration::seconds second_end(size); rclock::duration::seconds second_offset(size); cpp11::writable::logicals second_dst(size); cpp11::writable::strings second_abbreviation(size); const std::chrono::minutes zero{0}; const bool recycle_zone = zone.size() == 1; const date::time_zone* p_time_zone; if (recycle_zone) { const std::string zone_name = cpp11::r_string(zone[0]); p_time_zone = zone_name_load(zone_name); } for (r_ssize i = 0; i < size; ++i) { if (x.is_na(i)) { SET_STRING_ELT(type, i, r_chr_na); first_begin.assign_na(i); first_end.assign_na(i); first_offset.assign_na(i); first_dst[i] = r_lgl_na; SET_STRING_ELT(first_abbreviation, i, r_chr_na); second_begin.assign_na(i); second_end.assign_na(i); second_offset.assign_na(i); second_dst[i] = r_lgl_na; SET_STRING_ELT(second_abbreviation, i, r_chr_na); continue; } const date::time_zone* p_time_zone_elt; if (recycle_zone) { p_time_zone_elt = p_time_zone; } else { const std::string zone_name_elt = cpp11::r_string(zone[i]); p_time_zone_elt = zone_name_load(zone_name_elt); } const date::local_time elt{x[i]}; const date::local_info info = rclock::get_info(elt, p_time_zone_elt); const date::sys_info first = info.first; const date::sys_info second = info.second; switch (info.result) { case date::local_info::unique: SET_STRING_ELT(type, i, type_unique); break; case date::local_info::nonexistent: SET_STRING_ELT(type, i, type_nonexistent); break; case date::local_info::ambiguous: SET_STRING_ELT(type, i, type_ambiguous); break; default: never_reached("naive_time_info_impl"); } first_begin.assign(first.begin.time_since_epoch(), i); first_end.assign(first.end.time_since_epoch(), i); first_offset.assign(first.offset, i); first_dst[i] = first.save != zero; SET_STRING_ELT(first_abbreviation, i, Rf_mkCharLenCE(first.abbrev.c_str(), first.abbrev.size(), CE_UTF8)); if (info.result == date::local_info::unique) { // date zero initializes `info.second` since there is no applicable information second_begin.assign_na(i); second_end.assign_na(i); second_offset.assign_na(i); second_dst[i] = r_lgl_na; SET_STRING_ELT(second_abbreviation, i, r_chr_na); } else { second_begin.assign(second.begin.time_since_epoch(), i); second_end.assign(second.end.time_since_epoch(), i); second_offset.assign(second.offset, i); second_dst[i] = second.save != zero; SET_STRING_ELT(second_abbreviation, i, Rf_mkCharLenCE(second.abbrev.c_str(), second.abbrev.size(), CE_UTF8)); } } cpp11::writable::list out_first_begin = first_begin.to_list(); cpp11::writable::list out_first_end = first_end.to_list(); cpp11::writable::list out_first_offset = first_offset.to_list(); cpp11::writable::list out_second_begin = second_begin.to_list(); cpp11::writable::list out_second_end = second_end.to_list(); cpp11::writable::list out_second_offset = second_offset.to_list(); cpp11::writable::list out_first = { out_first_begin, out_first_end, out_first_offset, first_dst, first_abbreviation }; out_first.names() = {"begin", "end", "offset", "dst", "abbreviation"}; cpp11::writable::list out_second = { out_second_begin, out_second_end, out_second_offset, second_dst, second_abbreviation }; out_second.names() = {"begin", "end", "offset", "dst", "abbreviation"}; cpp11::writable::list out = { type, out_first, out_second }; out.names() = {"type", "first", "second"}; return out; } [[cpp11::register]] cpp11::writable::list naive_time_info_cpp(cpp11::list_of fields, const cpp11::integers& precision_int, const cpp11::strings& zone) { using namespace rclock; switch (parse_precision(precision_int)) { case precision::day: return naive_time_info_impl(fields, zone); case precision::second: return naive_time_info_impl(fields, zone); case precision::millisecond: return naive_time_info_impl(fields, zone); case precision::microsecond: return naive_time_info_impl(fields, zone); case precision::nanosecond: return naive_time_info_impl(fields, zone); default: clock_abort("Internal error: Should never be called."); } } clock/src/duration.cpp0000644000176200001440000016133614427204257014506 0ustar liggesusers#include "clock.h" #include "utils.h" #include "duration.h" #include "enums.h" #include "get.h" #include "rcrd.h" #include #include #include #include // ----------------------------------------------------------------------------- [[cpp11::register]] SEXP new_duration_from_fields(SEXP fields, const cpp11::integers& precision_int, SEXP names) { const r_ssize n_fields = Rf_xlength(fields); if (n_fields != 2) { clock_abort("`fields` must be length 2."); } SEXP out = PROTECT(new_clock_rcrd_from_fields(fields, names, classes_duration)); Rf_setAttrib(out, syms_precision, precision_int); UNPROTECT(1); return out; } // ----------------------------------------------------------------------------- [[cpp11::register]] SEXP duration_restore(SEXP x, SEXP to) { SEXP out = PROTECT(clock_rcrd_restore(x, to, classes_duration)); SEXP precision = Rf_getAttrib(to, syms_precision); Rf_setAttrib(out, syms_precision, precision); UNPROTECT(1); return out; } // ----------------------------------------------------------------------------- /* * This is operator<< for durations in `date.h`, but without the unit */ template inline std::basic_ostream& duration_stream(std::basic_ostream& os, const std::chrono::duration& d) { return os << date::detail::make_string::from(d.count()); } // ----------------------------------------------------------------------------- template cpp11::writable::strings format_duration_impl(cpp11::list_of& fields) { const ClockDuration x{fields}; const r_ssize size = x.size(); std::ostringstream stream; cpp11::writable::strings out(size); for (r_ssize i = 0; i < size; ++i) { if (x.is_na(i)) { SET_STRING_ELT(out, i, r_chr_na); continue; } typename ClockDuration::chrono_duration duration = x[i]; stream.str(std::string()); stream.clear(); duration_stream(stream, duration); std::string string = stream.str(); SET_STRING_ELT(out, i, Rf_mkCharLenCE(string.c_str(), string.size(), CE_UTF8)); } return out; } [[cpp11::register]] cpp11::writable::strings format_duration_cpp(cpp11::list_of fields, const cpp11::integers& precision_int) { using namespace rclock; switch (parse_precision(precision_int)) { case precision::year: return format_duration_impl(fields); case precision::quarter: return format_duration_impl(fields); case precision::month: return format_duration_impl(fields); case precision::week: return format_duration_impl(fields); case precision::day: return format_duration_impl(fields); case precision::hour: return format_duration_impl(fields); case precision::minute: return format_duration_impl(fields); case precision::second: return format_duration_impl(fields); case precision::millisecond: return format_duration_impl(fields); case precision::microsecond: return format_duration_impl(fields); case precision::nanosecond: return format_duration_impl(fields); default: never_reached("format_duration_cpp"); } } // ----------------------------------------------------------------------------- template inline cpp11::writable::list_of duration_helper_impl(const cpp11::integers& n) { const r_ssize size = n.size(); ClockDuration out(size); for (r_ssize i = 0; i < size; ++i) { const int n_elt = n[i]; if (n_elt == r_int_na) { out.assign_na(i); continue; } const typename ClockDuration::chrono_duration elt{n_elt}; out.assign(elt, i); } return out.to_list(); } [[cpp11::register]] cpp11::writable::list_of duration_helper_cpp(const cpp11::integers& n, const cpp11::integers& precision_int) { using namespace rclock; switch (parse_precision(precision_int)) { case precision::year: return duration_helper_impl(n); case precision::quarter: return duration_helper_impl(n); case precision::month: return duration_helper_impl(n); case precision::week: return duration_helper_impl(n); case precision::day: return duration_helper_impl(n); case precision::hour: return duration_helper_impl(n); case precision::minute: return duration_helper_impl(n); case precision::second: return duration_helper_impl(n); case precision::millisecond: return duration_helper_impl(n); case precision::microsecond: return duration_helper_impl(n); case precision::nanosecond: return duration_helper_impl(n); default: never_reached("duration_helper_cpp"); } } // ----------------------------------------------------------------------------- template inline cpp11::writable::list duration_cast_impl(cpp11::list_of& fields) { using DurationFrom = typename ClockDurationFrom::chrono_duration; using DurationTo = typename ClockDurationTo::chrono_duration; const ClockDurationFrom x{fields}; if (std::is_same::value) { return(x.to_list()); } const r_ssize size = x.size(); ClockDurationTo out(size); for (r_ssize i = 0; i < size; ++i) { if (x.is_na(i)) { out.assign_na(i); continue; } const DurationFrom x_elt = x[i]; const DurationTo out_elt = std::chrono::duration_cast(x_elt); out.assign(out_elt, i); } return out.to_list(); } template inline cpp11::writable::list duration_cast_switch2(cpp11::list_of& fields, const enum precision precision_to_val) { using namespace rclock; switch (precision_to_val) { case precision::year: return duration_cast_impl(fields); case precision::quarter: return duration_cast_impl(fields); case precision::month: return duration_cast_impl(fields); case precision::week: return duration_cast_impl(fields); case precision::day: return duration_cast_impl(fields); case precision::hour: return duration_cast_impl(fields); case precision::minute: return duration_cast_impl(fields); case precision::second: return duration_cast_impl(fields); case precision::millisecond: return duration_cast_impl(fields); case precision::microsecond: return duration_cast_impl(fields); case precision::nanosecond: return duration_cast_impl(fields); default: never_reached("duration_cast_switch2"); } } inline cpp11::writable::list duration_cast_switch(cpp11::list_of& fields, const enum precision precision_from_val, const enum precision precision_to_val) { using namespace rclock; switch (precision_from_val) { case precision::year: return duration_cast_switch2(fields, precision_to_val); case precision::quarter: return duration_cast_switch2(fields, precision_to_val); case precision::month: return duration_cast_switch2(fields, precision_to_val); case precision::week: return duration_cast_switch2(fields, precision_to_val); case precision::day: return duration_cast_switch2(fields, precision_to_val); case precision::hour: return duration_cast_switch2(fields, precision_to_val); case precision::minute: return duration_cast_switch2(fields, precision_to_val); case precision::second: return duration_cast_switch2(fields, precision_to_val); case precision::millisecond: return duration_cast_switch2(fields, precision_to_val); case precision::microsecond: return duration_cast_switch2(fields, precision_to_val); case precision::nanosecond: return duration_cast_switch2(fields, precision_to_val); default: never_reached("duration_cast_switch"); } } [[cpp11::register]] cpp11::writable::list duration_cast_cpp(cpp11::list_of fields, const cpp11::integers& precision_from, const cpp11::integers& precision_to) { const enum precision precision_from_val = parse_precision(precision_from); const enum precision precision_to_val = parse_precision(precision_to); return duration_cast_switch( fields, precision_from_val, precision_to_val ); } // ----------------------------------------------------------------------------- enum class arith_op { plus, minus, modulus }; template static inline cpp11::writable::list duration_arith_impl(cpp11::list_of& x_fields, cpp11::list_of& y_fields, const enum arith_op& op) { const ClockDuration x{x_fields}; const ClockDuration y{y_fields}; const r_ssize size = x.size(); ClockDuration out(size); switch (op) { case arith_op::plus: { for (r_ssize i = 0; i < size; ++i) { if (x.is_na(i) || y.is_na(i)) { out.assign_na(i); continue; } out.assign(x[i] + y[i], i); } break; } case arith_op::minus: { for (r_ssize i = 0; i < size; ++i) { if (x.is_na(i) || y.is_na(i)) { out.assign_na(i); continue; } out.assign(x[i] - y[i], i); } break; } case arith_op::modulus: { using Duration = typename ClockDuration::chrono_duration; for (r_ssize i = 0; i < size; ++i) { if (x.is_na(i) || y.is_na(i)) { out.assign_na(i); continue; } const Duration x_elt = x[i]; const Duration y_elt = y[i]; if (y_elt == Duration::zero()) { out.assign_na(i); continue; } out.assign(x_elt % y_elt, i); } break; } } return out.to_list(); } static inline cpp11::writable::list duration_arith(cpp11::list_of& x, cpp11::list_of& y, const cpp11::integers& precision_int, const enum arith_op& op) { using namespace rclock; switch (parse_precision(precision_int)) { case precision::year: return duration_arith_impl(x, y, op); case precision::quarter: return duration_arith_impl(x, y, op); case precision::month: return duration_arith_impl(x, y, op); case precision::week: return duration_arith_impl(x, y, op); case precision::day: return duration_arith_impl(x, y, op); case precision::hour: return duration_arith_impl(x, y, op); case precision::minute: return duration_arith_impl(x, y, op); case precision::second: return duration_arith_impl(x, y, op); case precision::millisecond: return duration_arith_impl(x, y, op); case precision::microsecond: return duration_arith_impl(x, y, op); case precision::nanosecond: return duration_arith_impl(x, y, op); default: never_reached("duration_arith"); } } [[cpp11::register]] cpp11::writable::list duration_plus_cpp(cpp11::list_of x, cpp11::list_of y, const cpp11::integers& precision_int) { return duration_arith(x, y, precision_int, arith_op::plus); } [[cpp11::register]] cpp11::writable::list duration_minus_cpp(cpp11::list_of x, cpp11::list_of y, const cpp11::integers& precision_int) { return duration_arith(x, y, precision_int, arith_op::minus); } [[cpp11::register]] cpp11::writable::list duration_modulus_cpp(cpp11::list_of x, cpp11::list_of y, const cpp11::integers& precision_int) { return duration_arith(x, y, precision_int, arith_op::modulus); } // ----------------------------------------------------------------------------- template static inline cpp11::writable::integers duration_integer_divide_impl(cpp11::list_of& x_fields, cpp11::list_of& y_fields) { using Duration = typename ClockDuration::chrono_duration; using Rep = typename Duration::rep; const Rep REP_INT_MAX = static_cast(std::numeric_limits::max()); const Rep REP_INT_MIN = static_cast(std::numeric_limits::min()); const ClockDuration x{x_fields}; const ClockDuration y{y_fields}; const r_ssize size = x.size(); cpp11::writable::integers out(size); bool warn = false; r_ssize loc = 0; for (r_ssize i = 0; i < size; ++i) { if (x.is_na(i) || y.is_na(i)) { out[i] = r_int_na; continue; } const Duration x_elt = x[i]; const Duration y_elt = y[i]; if (y_elt == Duration::zero()) { // Consistent with `2L %/% 0L` rather than `2 %/% 0` since infinite // durations aren't supported out[i] = r_int_na; continue; } const Rep elt = x_elt / y_elt; if (elt > REP_INT_MAX || elt <= REP_INT_MIN) { out[i] = r_int_na; if (!warn) { warn = true; loc = i + 1; } continue; } out[i] = static_cast(elt); } if (warn) { cpp11::warning( "Conversion to integer is outside the range of an integer. " "`NA` values have been introduced, beginning at location %td.", (ptrdiff_t) loc ); } return out; } [[cpp11::register]] cpp11::writable::integers duration_integer_divide_cpp(cpp11::list_of x, cpp11::list_of y, const cpp11::integers& precision_int) { using namespace rclock; switch (parse_precision(precision_int)) { case precision::year: return duration_integer_divide_impl(x, y); case precision::quarter: return duration_integer_divide_impl(x, y); case precision::month: return duration_integer_divide_impl(x, y); case precision::week: return duration_integer_divide_impl(x, y); case precision::day: return duration_integer_divide_impl(x, y); case precision::hour: return duration_integer_divide_impl(x, y); case precision::minute: return duration_integer_divide_impl(x, y); case precision::second: return duration_integer_divide_impl(x, y); case precision::millisecond: return duration_integer_divide_impl(x, y); case precision::microsecond: return duration_integer_divide_impl(x, y); case precision::nanosecond: return duration_integer_divide_impl(x, y); default: never_reached("duration_integer_divide_cpp"); } } // ----------------------------------------------------------------------------- enum class arith_scalar_op { multiply, modulus, divide }; template static inline cpp11::writable::list duration_scalar_arith_impl(cpp11::list_of& x_fields, const cpp11::integers& y, const enum arith_scalar_op& op) { const ClockDuration x{x_fields}; r_ssize size = x.size(); ClockDuration out(size); switch (op) { case arith_scalar_op::multiply: { for (r_ssize i = 0; i < size; ++i) { const int elt_y = y[i]; if (x.is_na(i) || elt_y == r_int_na) { out.assign_na(i); continue; } out.assign(x[i] * elt_y, i); } break; } case arith_scalar_op::modulus: { for (r_ssize i = 0; i < size; ++i) { const int elt_y = y[i]; if (x.is_na(i) || elt_y == r_int_na || elt_y == 0) { out.assign_na(i); continue; } out.assign(x[i] % elt_y, i); } break; } case arith_scalar_op::divide: { for (r_ssize i = 0; i < size; ++i) { const int elt_y = y[i]; if (x.is_na(i) || elt_y == r_int_na || elt_y == 0) { out.assign_na(i); continue; } out.assign(x[i] / elt_y, i); } break; } } return out.to_list(); } static inline cpp11::writable::list duration_scalar_arith(cpp11::list_of& x, const cpp11::integers& y, const cpp11::integers& precision_int, const enum arith_scalar_op& op) { using namespace rclock; switch (parse_precision(precision_int)) { case precision::year: return duration_scalar_arith_impl(x, y, op); case precision::quarter: return duration_scalar_arith_impl(x, y, op); case precision::month: return duration_scalar_arith_impl(x, y, op); case precision::week: return duration_scalar_arith_impl(x, y, op); case precision::day: return duration_scalar_arith_impl(x, y, op); case precision::hour: return duration_scalar_arith_impl(x, y, op); case precision::minute: return duration_scalar_arith_impl(x, y, op); case precision::second: return duration_scalar_arith_impl(x, y, op); case precision::millisecond: return duration_scalar_arith_impl(x, y, op); case precision::microsecond: return duration_scalar_arith_impl(x, y, op); case precision::nanosecond: return duration_scalar_arith_impl(x, y, op); default: never_reached("duration_scalar_arith"); } } [[cpp11::register]] cpp11::writable::list duration_scalar_multiply_cpp(cpp11::list_of x, const cpp11::integers& y, const cpp11::integers& precision_int) { return duration_scalar_arith(x, y, precision_int, arith_scalar_op::multiply); } [[cpp11::register]] cpp11::writable::list duration_scalar_modulus_cpp(cpp11::list_of x, const cpp11::integers& y, const cpp11::integers& precision_int) { return duration_scalar_arith(x, y, precision_int, arith_scalar_op::modulus); } [[cpp11::register]] cpp11::writable::list duration_scalar_divide_cpp(cpp11::list_of x, const cpp11::integers& y, const cpp11::integers& precision_int) { return duration_scalar_arith(x, y, precision_int, arith_scalar_op::divide); } // ----------------------------------------------------------------------------- /* * Restricts normal result returned by `std::common_type()` to only allow * combinations of: * * Calendrical durations: * - year, quarter, month * * Chronological durations: * - week, day, hour, minute, second, microsecond, millisecond, nanosecond * * These two groups consist of durations that are intuitively defined relative * to each other. They also separate how durations are interpreted in clock, * the granular ones are calendrical (and are used with calendars), the precise * ones are chronological (and are used with time points). */ template inline std::pair duration_common_precision_impl() { using CT = typename std::common_type::type; const bool duration1_calendrical = std::is_same::value || std::is_same::value || std::is_same::value; const bool duration2_calendrical = std::is_same::value || std::is_same::value || std::is_same::value; // Duration combinations that cross the // calendrical/chronological boundary are invalid if (duration1_calendrical && !duration2_calendrical) { return std::make_pair(precision::year, false); } if (!duration1_calendrical && duration2_calendrical) { return std::make_pair(precision::year, false); } if (std::is_same::value) { return std::make_pair(precision::year, true); } else if (std::is_same::value) { return std::make_pair(precision::quarter, true); } else if (std::is_same::value) { return std::make_pair(precision::month, true); } else if (std::is_same::value) { return std::make_pair(precision::week, true); } else if (std::is_same::value) { return std::make_pair(precision::day, true); } else if (std::is_same::value) { return std::make_pair(precision::hour, true); } else if (std::is_same::value) { return std::make_pair(precision::minute, true); } else if (std::is_same::value) { return std::make_pair(precision::second, true); } else if (std::is_same::value) { return std::make_pair(precision::millisecond, true); } else if (std::is_same::value) { return std::make_pair(precision::microsecond, true); } else if (std::is_same::value) { return std::make_pair(precision::nanosecond, true); } else { clock_abort("Internal error: Invalid combination of duration precisions."); } never_reached("duration_common_precision_impl"); } template static inline std::pair duration_common_precision_switch2(const enum precision& y_precision) { switch (y_precision) { case precision::year: return duration_common_precision_impl(); case precision::quarter: return duration_common_precision_impl(); case precision::month: return duration_common_precision_impl(); case precision::week: return duration_common_precision_impl(); case precision::day: return duration_common_precision_impl(); case precision::hour: return duration_common_precision_impl(); case precision::minute: return duration_common_precision_impl(); case precision::second: return duration_common_precision_impl(); case precision::millisecond: return duration_common_precision_impl(); case precision::microsecond: return duration_common_precision_impl(); case precision::nanosecond: return duration_common_precision_impl(); } never_reached("duration_common_precision_switch2"); } static inline std::pair duration_common_precision_pair(const enum precision& x_precision, const enum precision& y_precision) { switch (x_precision) { case precision::year: return duration_common_precision_switch2(y_precision); case precision::quarter: return duration_common_precision_switch2(y_precision); case precision::month: return duration_common_precision_switch2(y_precision); case precision::week: return duration_common_precision_switch2(y_precision); case precision::day: return duration_common_precision_switch2(y_precision); case precision::hour: return duration_common_precision_switch2(y_precision); case precision::minute: return duration_common_precision_switch2(y_precision); case precision::second: return duration_common_precision_switch2(y_precision); case precision::millisecond: return duration_common_precision_switch2(y_precision); case precision::microsecond: return duration_common_precision_switch2(y_precision); case precision::nanosecond: return duration_common_precision_switch2(y_precision); } never_reached("duration_common_precision_pair"); } [[cpp11::register]] int duration_precision_common_cpp(const cpp11::integers& x_precision, const cpp11::integers& y_precision) { const enum precision x_precision_val = parse_precision(x_precision); const enum precision y_precision_val = parse_precision(y_precision); const std::pair pair = duration_common_precision_pair(x_precision_val, y_precision_val); if (pair.second) { return static_cast(pair.first); } else { return r_int_na; } } [[cpp11::register]] bool duration_has_common_precision_cpp(const cpp11::integers& x_precision, const cpp11::integers& y_precision) { const enum precision x_precision_val = parse_precision(x_precision); const enum precision y_precision_val = parse_precision(y_precision); return duration_common_precision_pair(x_precision_val, y_precision_val).second; } // ----------------------------------------------------------------------------- enum class rounding { round, floor, ceil, }; template static inline Duration clock_multi_floor_impl(const Duration& x, const int& n) { const typename Duration::rep c = x.count(); return Duration{(c >= 0 ? c : (c - n + 1)) / n * n}; } template static inline DurationTo clock_floor(const DurationFrom& d, const int& n) { const DurationTo x = date::floor(d); return n == 1 ? x : clock_multi_floor_impl(x, n); } template static inline DurationTo clock_ceil(const DurationFrom& d, const int& n) { DurationTo x = clock_floor(d, n); if (x < d) { // Return input at new precision if on boundary, otherwise do ceiling x += DurationTo{n}; } return x; } template static inline DurationTo clock_round(const DurationFrom& d, const int& n) { const DurationTo floor = clock_floor(d, n); const DurationTo ceil = floor < d ? floor + DurationTo{n} : floor; if (ceil - d <= d - floor) { return ceil; } else { return floor; } } template cpp11::writable::list duration_rounding_impl(cpp11::list_of& fields, const int& n, const enum rounding& type) { using DurationFrom = typename ClockDurationFrom::chrono_duration; using DurationTo = typename ClockDurationTo::chrono_duration; const ClockDurationFrom x{fields}; const r_ssize size = x.size(); ClockDurationTo out(size); if (type == rounding::floor) { for (r_ssize i = 0; i < size; ++i) { if (x.is_na(i)) { out.assign_na(i); continue; } const DurationFrom from = x[i]; const DurationTo to = clock_floor(from, n); out.assign(to, i); } } else if (type == rounding::ceil) { for (r_ssize i = 0; i < size; ++i) { if (x.is_na(i)) { out.assign_na(i); continue; } const DurationFrom from = x[i]; const DurationTo to = clock_ceil(from, n); out.assign(to, i); } } else { for (r_ssize i = 0; i < size; ++i) { if (x.is_na(i)) { out.assign_na(i); continue; } const DurationFrom from = x[i]; const DurationTo to = clock_round(from, n); out.assign(to, i); } } return out.to_list(); } inline cpp11::writable::list duration_rounding_switch(cpp11::list_of& fields, const enum precision& precision_from_val, const enum precision& precision_to_val, const int& n, const enum rounding& type) { using namespace rclock; switch (precision_from_val) { case precision::year: { switch (precision_to_val) { case precision::year: return duration_rounding_impl(fields, n, type); default: clock_abort("Internal error: Invalid precision combination."); } } case precision::quarter: { switch (precision_to_val) { case precision::year: return duration_rounding_impl(fields, n, type); case precision::quarter: return duration_rounding_impl(fields, n, type); default: clock_abort("Internal error: Invalid precision combination."); } } case precision::month: { switch (precision_to_val) { case precision::year: return duration_rounding_impl(fields, n, type); case precision::quarter: return duration_rounding_impl(fields, n, type); case precision::month: return duration_rounding_impl(fields, n, type); default: clock_abort("Internal error: Invalid precision combination."); } } case precision::week: { switch (precision_to_val) { case precision::week: return duration_rounding_impl(fields, n, type); default: clock_abort("Internal error: Invalid precision combination."); } } case precision::day: { switch (precision_to_val) { case precision::week: return duration_rounding_impl(fields, n, type); case precision::day: return duration_rounding_impl(fields, n, type); default: clock_abort("Internal error: Invalid precision combination."); } } case precision::hour: { switch (precision_to_val) { case precision::week: return duration_rounding_impl(fields, n, type); case precision::day: return duration_rounding_impl(fields, n, type); case precision::hour: return duration_rounding_impl(fields, n, type); default: clock_abort("Internal error: Invalid precision combination."); } } case precision::minute: { switch (precision_to_val) { case precision::week: return duration_rounding_impl(fields, n, type); case precision::day: return duration_rounding_impl(fields, n, type); case precision::hour: return duration_rounding_impl(fields, n, type); case precision::minute: return duration_rounding_impl(fields, n, type); default: clock_abort("Internal error: Invalid precision combination."); } } case precision::second: { switch (precision_to_val) { case precision::week: return duration_rounding_impl(fields, n, type); case precision::day: return duration_rounding_impl(fields, n, type); case precision::hour: return duration_rounding_impl(fields, n, type); case precision::minute: return duration_rounding_impl(fields, n, type); case precision::second: return duration_rounding_impl(fields, n, type); default: clock_abort("Internal error: Invalid precision combination."); } } case precision::millisecond: { switch (precision_to_val) { case precision::week: return duration_rounding_impl(fields, n, type); case precision::day: return duration_rounding_impl(fields, n, type); case precision::hour: return duration_rounding_impl(fields, n, type); case precision::minute: return duration_rounding_impl(fields, n, type); case precision::second: return duration_rounding_impl(fields, n, type); case precision::millisecond: return duration_rounding_impl(fields, n, type); default: clock_abort("Internal error: Invalid precision combination."); } } case precision::microsecond: { switch (precision_to_val) { case precision::week: return duration_rounding_impl(fields, n, type); case precision::day: return duration_rounding_impl(fields, n, type); case precision::hour: return duration_rounding_impl(fields, n, type); case precision::minute: return duration_rounding_impl(fields, n, type); case precision::second: return duration_rounding_impl(fields, n, type); case precision::millisecond: return duration_rounding_impl(fields, n, type); case precision::microsecond: return duration_rounding_impl(fields, n, type); default: clock_abort("Internal error: Invalid precision combination."); } } case precision::nanosecond: { switch (precision_to_val) { case precision::week: return duration_rounding_impl(fields, n, type); case precision::day: return duration_rounding_impl(fields, n, type); case precision::hour: return duration_rounding_impl(fields, n, type); case precision::minute: return duration_rounding_impl(fields, n, type); case precision::second: return duration_rounding_impl(fields, n, type); case precision::millisecond: return duration_rounding_impl(fields, n, type); case precision::microsecond: return duration_rounding_impl(fields, n, type); case precision::nanosecond: return duration_rounding_impl(fields, n, type); default: clock_abort("Internal error: Invalid precision combination."); } } } never_reached("duration_rounding_switch"); } [[cpp11::register]] cpp11::writable::list duration_floor_cpp(cpp11::list_of fields, const cpp11::integers& precision_from, const cpp11::integers& precision_to, const int& n) { const enum precision precision_from_val = parse_precision(precision_from); const enum precision precision_to_val = parse_precision(precision_to); return duration_rounding_switch( fields, precision_from_val, precision_to_val, n, rounding::floor ); } [[cpp11::register]] cpp11::writable::list duration_ceiling_cpp(cpp11::list_of fields, const cpp11::integers& precision_from, const cpp11::integers& precision_to, const int& n) { const enum precision precision_from_val = parse_precision(precision_from); const enum precision precision_to_val = parse_precision(precision_to); return duration_rounding_switch( fields, precision_from_val, precision_to_val, n, rounding::ceil ); } [[cpp11::register]] cpp11::writable::list duration_round_cpp(cpp11::list_of fields, const cpp11::integers& precision_from, const cpp11::integers& precision_to, const int& n) { const enum precision precision_from_val = parse_precision(precision_from); const enum precision precision_to_val = parse_precision(precision_to); return duration_rounding_switch( fields, precision_from_val, precision_to_val, n, rounding::round ); } // ----------------------------------------------------------------------------- template static inline cpp11::writable::list duration_unary_minus_impl(cpp11::list_of& fields) { const ClockDuration x{fields}; const r_ssize size = x.size(); ClockDuration out(size); for (r_ssize i = 0; i < size; ++i) { if (x.is_na(i)) { out.assign_na(i); continue; } out.assign(-x[i], i); } return out.to_list(); } [[cpp11::register]] cpp11::writable::list duration_unary_minus_cpp(cpp11::list_of fields, const cpp11::integers& precision_int) { using namespace rclock; switch (parse_precision(precision_int)) { case precision::year: return duration_unary_minus_impl(fields); case precision::quarter: return duration_unary_minus_impl(fields); case precision::month: return duration_unary_minus_impl(fields); case precision::week: return duration_unary_minus_impl(fields); case precision::day: return duration_unary_minus_impl(fields); case precision::hour: return duration_unary_minus_impl(fields); case precision::minute: return duration_unary_minus_impl(fields); case precision::second: return duration_unary_minus_impl(fields); case precision::millisecond: return duration_unary_minus_impl(fields); case precision::microsecond: return duration_unary_minus_impl(fields); case precision::nanosecond: return duration_unary_minus_impl(fields); default: never_reached("duration_unary_minus_cpp"); } } // ----------------------------------------------------------------------------- template static inline cpp11::writable::integers duration_as_integer_impl(cpp11::list_of& fields) { using Duration = typename ClockDuration::chrono_duration; using Rep = typename Duration::rep; const ClockDuration x{fields}; const r_ssize size = x.size(); cpp11::writable::integers out(size); bool warn = false; r_ssize loc = 0; for (r_ssize i = 0; i < size; ++i) { if (x.is_na(i)) { out[i] = r_int_na; continue; } const Duration elt = x[i]; const Rep elt_rep = elt.count(); if (elt_rep > INT32_MAX || elt_rep <= INT32_MIN) { out[i] = r_int_na; if (!warn) { loc = i + 1; } warn = true; continue; } out[i] = static_cast(elt_rep); } if (warn) { cpp11::warning( "Conversion from duration to integer is outside the range of an integer. " "`NA` values have been introduced, beginning at location %td.", (ptrdiff_t) loc ); } return out; } [[cpp11::register]] cpp11::writable::integers duration_as_integer_cpp(cpp11::list_of fields, const cpp11::integers& precision_int) { using namespace rclock; switch (parse_precision(precision_int)) { case precision::year: return duration_as_integer_impl(fields); case precision::quarter: return duration_as_integer_impl(fields); case precision::month: return duration_as_integer_impl(fields); case precision::week: return duration_as_integer_impl(fields); case precision::day: return duration_as_integer_impl(fields); case precision::hour: return duration_as_integer_impl(fields); case precision::minute: return duration_as_integer_impl(fields); case precision::second: return duration_as_integer_impl(fields); case precision::millisecond: return duration_as_integer_impl(fields); case precision::microsecond: return duration_as_integer_impl(fields); case precision::nanosecond: return duration_as_integer_impl(fields); default: never_reached("duration_as_integer_cpp"); } } // ----------------------------------------------------------------------------- template static inline cpp11::writable::doubles duration_as_double_impl(cpp11::list_of& fields) { using Duration = typename ClockDuration::chrono_duration; using Rep = typename Duration::rep; // Usually 2^53 - 1 // Pass `double`s to `pow()` for Solaris, where `pow(int, int)` is undefined static double DOUBLE_FLT_RADIX = static_cast(FLT_RADIX); static double DOUBLE_DBL_MANT_DIG = static_cast(DBL_MANT_DIG); static int64_t DOUBLE_MAX_NO_LOSS = static_cast(std::pow(DOUBLE_FLT_RADIX, DOUBLE_DBL_MANT_DIG) - 1); static int64_t DOUBLE_MIN_NO_LOSS = -DOUBLE_MAX_NO_LOSS; const ClockDuration x{fields}; const r_ssize size = x.size(); cpp11::writable::doubles out(size); bool warn = false; r_ssize loc = 0; for (r_ssize i = 0; i < size; ++i) { if (x.is_na(i)) { out[i] = r_dbl_na; continue; } const Duration elt = x[i]; const Rep elt_rep = elt.count(); if (elt_rep > DOUBLE_MAX_NO_LOSS || elt_rep < DOUBLE_MIN_NO_LOSS) { if (!warn) { loc = i + 1; } warn = true; } out[i] = static_cast(elt_rep); } if (warn) { cpp11::warning( "Conversion from duration to double is outside the range of lossless conversion. " "Precision may have been lost, beginning at location %td.", (ptrdiff_t) loc ); } return out; } [[cpp11::register]] cpp11::writable::doubles duration_as_double_cpp(cpp11::list_of fields, const cpp11::integers& precision_int) { using namespace rclock; switch (parse_precision(precision_int)) { case precision::year: return duration_as_double_impl(fields); case precision::quarter: return duration_as_double_impl(fields); case precision::month: return duration_as_double_impl(fields); case precision::week: return duration_as_double_impl(fields); case precision::day: return duration_as_double_impl(fields); case precision::hour: return duration_as_double_impl(fields); case precision::minute: return duration_as_double_impl(fields); case precision::second: return duration_as_double_impl(fields); case precision::millisecond: return duration_as_double_impl(fields); case precision::microsecond: return duration_as_double_impl(fields); case precision::nanosecond: return duration_as_double_impl(fields); default: never_reached("duration_as_double_cpp"); } } // ----------------------------------------------------------------------------- template static inline cpp11::writable::list duration_abs_impl(cpp11::list_of& fields) { using Duration = typename ClockDuration::chrono_duration; using Rep = typename Duration::rep; const ClockDuration x{fields}; const r_ssize size = x.size(); ClockDuration out(size); const Rep zero{0}; for (r_ssize i = 0; i < size; ++i) { if (x.is_na(i)) { out.assign_na(i); continue; } const Duration elt = x[i]; const Rep elt_rep = elt.count(); const Rep out_rep = (elt_rep < zero) ? std::abs(elt_rep) : elt_rep; const Duration out_elt{out_rep}; out.assign(out_elt, i); } return out.to_list(); } [[cpp11::register]] cpp11::writable::list duration_abs_cpp(cpp11::list_of fields, const cpp11::integers& precision_int) { using namespace rclock; switch (parse_precision(precision_int)) { case precision::year: return duration_abs_impl(fields); case precision::quarter: return duration_abs_impl(fields); case precision::month: return duration_abs_impl(fields); case precision::week: return duration_abs_impl(fields); case precision::day: return duration_abs_impl(fields); case precision::hour: return duration_abs_impl(fields); case precision::minute: return duration_abs_impl(fields); case precision::second: return duration_abs_impl(fields); case precision::millisecond: return duration_abs_impl(fields); case precision::microsecond: return duration_abs_impl(fields); case precision::nanosecond: return duration_abs_impl(fields); default: never_reached("duration_abs_cpp"); } } // ----------------------------------------------------------------------------- template static inline cpp11::writable::integers duration_sign_impl(cpp11::list_of& fields) { using Duration = typename ClockDuration::chrono_duration; using Rep = typename Duration::rep; const ClockDuration x{fields}; const r_ssize size = x.size(); cpp11::writable::integers out(size); const Rep zero{0}; for (r_ssize i = 0; i < size; ++i) { if (x.is_na(i)) { out[i] = r_int_na; continue; } const Duration elt = x[i]; const Rep elt_rep = elt.count(); if (elt_rep == zero) { out[i] = 0; } else if (elt_rep > zero) { out[i] = 1; } else { out[i] = -1; } } return out; } [[cpp11::register]] cpp11::writable::integers duration_sign_cpp(cpp11::list_of fields, const cpp11::integers& precision_int) { using namespace rclock; switch (parse_precision(precision_int)) { case precision::year: return duration_sign_impl(fields); case precision::quarter: return duration_sign_impl(fields); case precision::month: return duration_sign_impl(fields); case precision::week: return duration_sign_impl(fields); case precision::day: return duration_sign_impl(fields); case precision::hour: return duration_sign_impl(fields); case precision::minute: return duration_sign_impl(fields); case precision::second: return duration_sign_impl(fields); case precision::millisecond: return duration_sign_impl(fields); case precision::microsecond: return duration_sign_impl(fields); case precision::nanosecond: return duration_sign_impl(fields); default: never_reached("duration_sign_cpp"); } } // ----------------------------------------------------------------------------- template static inline cpp11::writable::list duration_seq_by_lo_impl(cpp11::list_of& from_fields, cpp11::list_of& by_fields, const r_ssize size) { using Duration = typename ClockDuration::chrono_duration; const ClockDuration from{from_fields}; const ClockDuration by{by_fields}; ClockDuration out(size); const Duration start = from[0]; const Duration step = by[0]; for (r_ssize i = 0; i < size; ++i) { const Duration elt = start + step * i; out.assign(elt, i); } return out.to_list(); } [[cpp11::register]] cpp11::writable::list duration_seq_by_lo_cpp(cpp11::list_of from, const cpp11::integers& precision_int, cpp11::list_of by, const cpp11::integers& length_out) { using namespace rclock; if (length_out.size() != 1) { clock_abort("Internal error: `length_out` should have size 1."); } const r_ssize size = length_out[0]; switch (parse_precision(precision_int)) { case precision::year: return duration_seq_by_lo_impl(from, by, size); case precision::quarter: return duration_seq_by_lo_impl(from, by, size); case precision::month: return duration_seq_by_lo_impl(from, by, size); case precision::week: return duration_seq_by_lo_impl(from, by, size); case precision::day: return duration_seq_by_lo_impl(from, by, size); case precision::hour: return duration_seq_by_lo_impl(from, by, size); case precision::minute: return duration_seq_by_lo_impl(from, by, size); case precision::second: return duration_seq_by_lo_impl(from, by, size); case precision::millisecond: return duration_seq_by_lo_impl(from, by, size); case precision::microsecond: return duration_seq_by_lo_impl(from, by, size); case precision::nanosecond: return duration_seq_by_lo_impl(from, by, size); default: never_reached("duration_seq_by_lo_cpp"); } } template static inline cpp11::writable::list duration_seq_to_by_impl(cpp11::list_of& from_fields, cpp11::list_of& to_fields, cpp11::list_of& by_fields) { using Duration = typename ClockDuration::chrono_duration; using Rep = typename Duration::rep; const ClockDuration from{from_fields}; const ClockDuration to{to_fields}; const ClockDuration by{by_fields}; const Duration start = from[0]; const Duration end = to[0]; const Duration step = by[0]; // To match `rlang::seq2()`, which has nice mathematical properties of // returning an empty sequence when `start > end` const bool is_empty = (step > Duration::zero() && start > end) || (step < Duration::zero() && start < end); r_ssize size; if (is_empty) { size = 0; } else { const Rep num = clock_safe_subtract(end.count(), start.count()); const Rep den = step.count(); size = static_cast(num / den + 1); } ClockDuration out(size); for (r_ssize i = 0; i < size; ++i) { const Duration elt = start + step * i; out.assign(elt, i); } return out.to_list(); } [[cpp11::register]] cpp11::writable::list duration_seq_to_by_cpp(cpp11::list_of from, const cpp11::integers& precision_int, cpp11::list_of to, cpp11::list_of by) { using namespace rclock; switch (parse_precision(precision_int)) { case precision::year: return duration_seq_to_by_impl(from, to, by); case precision::quarter: return duration_seq_to_by_impl(from, to, by); case precision::month: return duration_seq_to_by_impl(from, to, by); case precision::week: return duration_seq_to_by_impl(from, to, by); case precision::day: return duration_seq_to_by_impl(from, to, by); case precision::hour: return duration_seq_to_by_impl(from, to, by); case precision::minute: return duration_seq_to_by_impl(from, to, by); case precision::second: return duration_seq_to_by_impl(from, to, by); case precision::millisecond: return duration_seq_to_by_impl(from, to, by); case precision::microsecond: return duration_seq_to_by_impl(from, to, by); case precision::nanosecond: return duration_seq_to_by_impl(from, to, by); default: never_reached("duration_seq_to_by_cpp"); } } template static inline cpp11::writable::list duration_seq_to_lo_impl(cpp11::list_of& from_fields, cpp11::list_of& to_fields, const r_ssize& size) { using Duration = typename ClockDuration::chrono_duration; using Rep = typename Duration::rep; const ClockDuration from{from_fields}; const ClockDuration to{to_fields}; ClockDuration out(size); const Duration start = from[0]; const Duration end = to[0]; if (size == 1) { // Avoid division by zero out.assign(start, 0); return out.to_list(); } const Rep num = end.count() - start.count(); const Rep den = static_cast(size - 1); const Rep by = num / den; const Rep rem = num % den; if (rem != Rep{0}) { clock_abort( "The supplied output size does not result in a non-fractional " "sequence between `from` and `to`." ); } const Duration step{by}; for (r_ssize i = 0; i < size; ++i) { const Duration elt = start + step * i; out.assign(elt, i); } return out.to_list(); } [[cpp11::register]] cpp11::writable::list duration_seq_to_lo_cpp(cpp11::list_of from, const cpp11::integers& precision_int, cpp11::list_of to, const cpp11::integers& length_out) { using namespace rclock; if (length_out.size() != 1) { clock_abort("Internal error: `length_out` should have size 1."); } const r_ssize size = length_out[0]; switch (parse_precision(precision_int)) { case precision::year: return duration_seq_to_lo_impl(from, to, size); case precision::quarter: return duration_seq_to_lo_impl(from, to, size); case precision::month: return duration_seq_to_lo_impl(from, to, size); case precision::week: return duration_seq_to_lo_impl(from, to, size); case precision::day: return duration_seq_to_lo_impl(from, to, size); case precision::hour: return duration_seq_to_lo_impl(from, to, size); case precision::minute: return duration_seq_to_lo_impl(from, to, size); case precision::second: return duration_seq_to_lo_impl(from, to, size); case precision::millisecond: return duration_seq_to_lo_impl(from, to, size); case precision::microsecond: return duration_seq_to_lo_impl(from, to, size); case precision::nanosecond: return duration_seq_to_lo_impl(from, to, size); default: never_reached("duration_seq_to_lo_cpp"); } } // ----------------------------------------------------------------------------- template static inline cpp11::writable::list duration_minimum_impl() { using Duration = typename ClockDuration::chrono_duration; ClockDuration out(1); out.assign(Duration::min(), 0); return out.to_list(); } [[cpp11::register]] cpp11::writable::list duration_minimum_cpp(const cpp11::integers& precision_int) { using namespace rclock; switch (parse_precision(precision_int)) { case precision::year: return duration_minimum_impl(); case precision::quarter: return duration_minimum_impl(); case precision::month: return duration_minimum_impl(); case precision::week: return duration_minimum_impl(); case precision::day: return duration_minimum_impl(); case precision::hour: return duration_minimum_impl(); case precision::minute: return duration_minimum_impl(); case precision::second: return duration_minimum_impl(); case precision::millisecond: return duration_minimum_impl(); case precision::microsecond: return duration_minimum_impl(); case precision::nanosecond: return duration_minimum_impl(); default: never_reached("duration_minimum_cpp"); } } template static inline cpp11::writable::list duration_maximum_impl() { using Duration = typename ClockDuration::chrono_duration; ClockDuration out(1); out.assign(Duration::max(), 0); return out.to_list(); } [[cpp11::register]] cpp11::writable::list duration_maximum_cpp(const cpp11::integers& precision_int) { using namespace rclock; switch (parse_precision(precision_int)) { case precision::year: return duration_maximum_impl(); case precision::quarter: return duration_maximum_impl(); case precision::month: return duration_maximum_impl(); case precision::week: return duration_maximum_impl(); case precision::day: return duration_maximum_impl(); case precision::hour: return duration_maximum_impl(); case precision::minute: return duration_maximum_impl(); case precision::second: return duration_maximum_impl(); case precision::millisecond: return duration_maximum_impl(); case precision::microsecond: return duration_maximum_impl(); case precision::nanosecond: return duration_maximum_impl(); default: never_reached("duration_maximum_cpp"); } } clock/src/week-shim.h0000644000176200001440000004353614422221153014205 0ustar liggesusers#ifndef CLOCK_WEEK_SHIM_H #define CLOCK_WEEK_SHIM_H /* * This file contains a minimal shim around the classes in `week.h` that * exposes those classes in a template free way. The templating is necessary * in `week.h` to correctly define the type, since the week start day * is a part of the type itself, but we can gloss over some of those details * for use in `week-year-week-day.h`. */ #include #include "week.h" namespace rclock { namespace rweek { namespace week_shim { class year; class weekday; class year_weeknum; class year_lastweek; class year_weeknum_weekday; class year_lastweek_weekday; // date composition operators CONSTCD11 year_weeknum operator/(const year& y, const week::weeknum& wn) NOEXCEPT; CONSTCD11 year_weeknum operator/(const year& y, int wn) NOEXCEPT; CONSTCD11 year_lastweek operator/(const year& y, week::last_week wn) NOEXCEPT; CONSTCD11 year_weeknum_weekday operator/(const year_weeknum& ywn, const weekday& wd) NOEXCEPT; CONSTCD11 year_weeknum_weekday operator/(const year_weeknum& ywn, int wd) NOEXCEPT; CONSTCD11 year_lastweek_weekday operator/(const year_lastweek& ylw, const weekday& wd) NOEXCEPT; // year class year { short y_; week::start s_; public: year() = default; explicit CONSTCD11 year(int y, week::start s) NOEXCEPT; CONSTCD11 week::start start() const NOEXCEPT; CONSTCD11 explicit operator int() const NOEXCEPT; CONSTCD14 bool is_leap() const NOEXCEPT; }; CONSTCD14 year operator+(const year& x, const week::years& y) NOEXCEPT; CONSTCD14 week::years operator-(const year& x, const year& y) NOEXCEPT; // weekday // `start` is implied by the `rweek::year`, since for clock you can't ever have // a free floating `weekday` (so it doesn't need to know about `start`) class weekday { unsigned char wd_; public: weekday() = default; explicit CONSTCD11 weekday(unsigned wd) NOEXCEPT; CONSTCD11 explicit operator unsigned() const NOEXCEPT; }; // year_weeknum class year_weeknum { week_shim::year y_; week::weeknum wn_; public: year_weeknum() = default; CONSTCD11 year_weeknum(const week_shim::year& y, const week::weeknum& wn) NOEXCEPT; CONSTCD14 year_weeknum(const week_shim::year_lastweek& ylw) NOEXCEPT; CONSTCD11 week_shim::year year() const NOEXCEPT; CONSTCD11 week::weeknum weeknum() const NOEXCEPT; CONSTCD14 bool ok() const NOEXCEPT; }; // year_lastweek class year_lastweek { week_shim::year y_; public: year_lastweek() = default; CONSTCD11 explicit year_lastweek(const week_shim::year& y) NOEXCEPT; CONSTCD11 week_shim::year year() const NOEXCEPT; CONSTCD14 week::weeknum weeknum() const NOEXCEPT; }; // year_weeknum_weekday class year_weeknum_weekday { week_shim::year y_; week::weeknum wn_; week_shim::weekday wd_; public: year_weeknum_weekday() = default; CONSTCD11 year_weeknum_weekday(const week_shim::year& y, const week::weeknum& wn, const week_shim::weekday& wd) NOEXCEPT; CONSTCD11 year_weeknum_weekday(const week_shim::year_weeknum& ywn, const week_shim::weekday& wd) NOEXCEPT; CONSTCD14 year_weeknum_weekday(const year_lastweek_weekday& ylwwd) NOEXCEPT; CONSTCD14 year_weeknum_weekday(const date::sys_days& dp, week::start s) NOEXCEPT; CONSTCD14 year_weeknum_weekday(const date::local_days& dp, week::start s) NOEXCEPT; CONSTCD11 week_shim::year year() const NOEXCEPT; CONSTCD11 week::weeknum weeknum() const NOEXCEPT; CONSTCD11 week_shim::weekday weekday() const NOEXCEPT; CONSTCD14 operator date::sys_days() const NOEXCEPT; CONSTCD14 explicit operator date::local_days() const NOEXCEPT; CONSTCD14 bool ok() const NOEXCEPT; private: CONSTCD14 year_weeknum_weekday from_sys_days(const date::sys_days& dp, week::start s) NOEXCEPT; CONSTCD14 year_weeknum_weekday from_local_days(const date::local_days& dp, week::start s) NOEXCEPT; }; // year_lastweek_weekday class year_lastweek_weekday { week_shim::year y_; week_shim::weekday wd_; public: year_lastweek_weekday() = default; CONSTCD11 year_lastweek_weekday(const week_shim::year& y, const week_shim::weekday& wd) NOEXCEPT; CONSTCD11 week_shim::year year() const NOEXCEPT; CONSTCD14 week::weeknum weeknum() const NOEXCEPT; CONSTCD11 week_shim::weekday weekday() const NOEXCEPT; }; //----------------+ // Implementation | //----------------+ namespace detail { static inline void never_reached [[noreturn]] () { // Compiler hint to allow [[noreturn]] attribute. This is never executed since // `never_reached()` is never actually called. throw std::runtime_error("[[noreturn]]"); } } // namespace detail namespace detail { template CONSTCD14 inline week::year to_week(const week_shim::year& y) NOEXCEPT { return week::year(static_cast(y)); } template CONSTCD14 inline week::weekday to_week(const week_shim::weekday& wd) NOEXCEPT { return week::weekday(static_cast(wd)); } template CONSTCD14 inline week::year_weeknum to_week(const week_shim::year_weeknum& ywn) NOEXCEPT { return week::year_weeknum( to_week(ywn.year()), ywn.weeknum() ); } template CONSTCD14 inline week::year_lastweek to_week(const week_shim::year_lastweek& ylw) NOEXCEPT { return week::year_lastweek( to_week(ylw.year()) ); } template CONSTCD14 inline week::year_weeknum_weekday to_week(const week_shim::year_weeknum_weekday& ywnwd) NOEXCEPT { return week::year_weeknum_weekday( to_week(ywnwd.year()), ywnwd.weeknum(), to_week(ywnwd.weekday()) ); } template CONSTCD14 inline week::year_lastweek_weekday to_week(const week_shim::year_lastweek_weekday& ylwwd) NOEXCEPT { return week::year_lastweek_weekday( to_week(ylwwd.year()), to_week(ylwwd.weekday()) ); } template CONSTCD14 inline week_shim::year from_week(const week::year& y) NOEXCEPT { return week_shim::year(static_cast(y), S); } template CONSTCD14 inline week_shim::weekday from_week(const week::weekday& wd) NOEXCEPT { return week_shim::weekday(static_cast(wd)); } template CONSTCD14 inline week_shim::year_weeknum from_week(const week::year_weeknum& ywn) NOEXCEPT { return week_shim::year_weeknum( from_week(ywn.year()), ywn.weeknum() ); } template CONSTCD14 inline week_shim::year_weeknum_weekday from_week(const week::year_weeknum_weekday& ywnwd) NOEXCEPT { return week_shim::year_weeknum_weekday( from_week(ywnwd.year()), ywnwd.weeknum(), from_week(ywnwd.weekday()) ); } } // namespace detail // year CONSTCD11 inline year::year(int y, week::start s) NOEXCEPT : y_(static_cast(y)), s_(s) {} CONSTCD11 inline week::start year::start() const NOEXCEPT { return s_; } CONSTCD11 inline year::operator int() const NOEXCEPT { return y_; } CONSTCD14 bool year::is_leap() const NOEXCEPT { using start = week::start; using detail::to_week; switch (s_) { case start::sunday: return to_week(*this).is_leap(); case start::monday: return to_week(*this).is_leap(); case start::tuesday: return to_week(*this).is_leap(); case start::wednesday: return to_week(*this).is_leap(); case start::thursday: return to_week(*this).is_leap(); case start::friday: return to_week(*this).is_leap(); case start::saturday: return to_week(*this).is_leap(); default: detail::never_reached(); } } CONSTCD14 inline year operator+(const year& x, const week::years& y) NOEXCEPT { using start = week::start; using detail::from_week; using detail::to_week; switch (x.start()) { case start::sunday: return from_week(to_week(x) + y); case start::monday: return from_week(to_week(x) + y); case start::tuesday: return from_week(to_week(x) + y); case start::wednesday: return from_week(to_week(x) + y); case start::thursday: return from_week(to_week(x) + y); case start::friday: return from_week(to_week(x) + y); case start::saturday: return from_week(to_week(x) + y); default: detail::never_reached(); } } CONSTCD14 inline week::years operator-(const year& x, const year& y) NOEXCEPT { using start = week::start; using detail::to_week; // Assumes `x.start()` and `y.start()` are the same, verified by caller switch (x.start()) { case start::sunday: return to_week(x) - to_week(y); case start::monday: return to_week(x) - to_week(y); case start::tuesday: return to_week(x) - to_week(y); case start::wednesday: return to_week(x) - to_week(y); case start::thursday: return to_week(x) - to_week(y); case start::friday: return to_week(x) - to_week(y); case start::saturday: return to_week(x) - to_week(y); default: detail::never_reached(); } } // weekday CONSTCD11 inline weekday::weekday(unsigned wd) NOEXCEPT : wd_(static_cast(wd)) {} CONSTCD11 inline weekday::operator unsigned() const NOEXCEPT { return wd_; } // year_weeknum CONSTCD11 inline year_weeknum::year_weeknum(const week_shim::year& y, const week::weeknum& wn) NOEXCEPT : y_(y) , wn_(wn) {} CONSTCD14 inline year_weeknum::year_weeknum(const week_shim::year_lastweek& ylw) NOEXCEPT : y_(ylw.year()) , wn_(ylw.weeknum()) {} CONSTCD11 inline week_shim::year year_weeknum::year() const NOEXCEPT { return y_; } CONSTCD11 inline week::weeknum year_weeknum::weeknum() const NOEXCEPT { return wn_; } CONSTCD14 inline bool year_weeknum::ok() const NOEXCEPT { using start = week::start; using detail::to_week; switch (this->year().start()) { case start::sunday: return to_week(*this).ok(); case start::monday: return to_week(*this).ok(); case start::tuesday: return to_week(*this).ok(); case start::wednesday: return to_week(*this).ok(); case start::thursday: return to_week(*this).ok(); case start::friday: return to_week(*this).ok(); case start::saturday: return to_week(*this).ok(); default: detail::never_reached(); } } // year_lastweek CONSTCD11 inline year_lastweek::year_lastweek(const week_shim::year& y) NOEXCEPT : y_(y) {} CONSTCD11 inline year year_lastweek::year() const NOEXCEPT {return y_;} CONSTCD14 inline week::weeknum year_lastweek::weeknum() const NOEXCEPT { using start = week::start; using detail::to_week; switch (this->year().start()) { case start::sunday: return to_week(*this).weeknum(); case start::monday: return to_week(*this).weeknum(); case start::tuesday: return to_week(*this).weeknum(); case start::wednesday: return to_week(*this).weeknum(); case start::thursday: return to_week(*this).weeknum(); case start::friday: return to_week(*this).weeknum(); case start::saturday: return to_week(*this).weeknum(); default: detail::never_reached(); } } // year_weeknum_weekday CONSTCD11 inline year_weeknum_weekday::year_weeknum_weekday(const week_shim::year& y, const week::weeknum& wn, const week_shim::weekday& wd) NOEXCEPT : y_(y) , wn_(wn) , wd_(wd) {} CONSTCD11 inline year_weeknum_weekday::year_weeknum_weekday(const week_shim::year_weeknum& ywn, const week_shim::weekday& wd) NOEXCEPT : y_(ywn.year()) , wn_(ywn.weeknum()) , wd_(wd) {} CONSTCD14 inline year_weeknum_weekday::year_weeknum_weekday(const year_lastweek_weekday& ylwwd) NOEXCEPT : y_(ylwwd.year()) , wn_(ylwwd.weeknum()) , wd_(ylwwd.weekday()) {} CONSTCD14 inline year_weeknum_weekday::year_weeknum_weekday(const date::sys_days& dp, week::start s) NOEXCEPT : year_weeknum_weekday(from_sys_days(dp, s)) {} CONSTCD14 inline year_weeknum_weekday::year_weeknum_weekday(const date::local_days& dp, week::start s) NOEXCEPT : year_weeknum_weekday(from_local_days(dp, s)) {} CONSTCD14 inline year_weeknum_weekday year_weeknum_weekday::from_sys_days(const date::sys_days& dp, week::start s) NOEXCEPT { using start = week::start; using detail::from_week; switch (s) { case start::sunday: return from_week(week::year_weeknum_weekday(dp)); case start::monday: return from_week(week::year_weeknum_weekday(dp)); case start::tuesday: return from_week(week::year_weeknum_weekday(dp)); case start::wednesday: return from_week(week::year_weeknum_weekday(dp)); case start::thursday: return from_week(week::year_weeknum_weekday(dp)); case start::friday: return from_week(week::year_weeknum_weekday(dp)); case start::saturday: return from_week(week::year_weeknum_weekday(dp)); default: detail::never_reached(); } } CONSTCD14 inline year_weeknum_weekday year_weeknum_weekday::from_local_days(const date::local_days& dp, week::start s) NOEXCEPT { return from_sys_days(date::sys_days(dp.time_since_epoch()), s); } CONSTCD11 inline week_shim::year year_weeknum_weekday::year() const NOEXCEPT { return y_; } CONSTCD11 inline week::weeknum year_weeknum_weekday::weeknum() const NOEXCEPT { return wn_; } CONSTCD11 inline week_shim::weekday year_weeknum_weekday::weekday() const NOEXCEPT { return wd_; } CONSTCD14 inline year_weeknum_weekday::operator date::sys_days() const NOEXCEPT { using start = week::start; using detail::to_week; switch (y_.start()) { case start::sunday: return to_week(*this); case start::monday: return to_week(*this); case start::tuesday: return to_week(*this); case start::wednesday: return to_week(*this); case start::thursday: return to_week(*this); case start::friday: return to_week(*this); case start::saturday: return to_week(*this); default: detail::never_reached(); } } CONSTCD14 inline year_weeknum_weekday::operator date::local_days() const NOEXCEPT { using start = week::start; using detail::to_week; switch (y_.start()) { case start::sunday: return date::local_days(to_week(*this)); case start::monday: return date::local_days(to_week(*this)); case start::tuesday: return date::local_days(to_week(*this)); case start::wednesday: return date::local_days(to_week(*this)); case start::thursday: return date::local_days(to_week(*this)); case start::friday: return date::local_days(to_week(*this)); case start::saturday: return date::local_days(to_week(*this)); default: detail::never_reached(); } } CONSTCD14 inline bool year_weeknum_weekday::ok() const NOEXCEPT { using start = week::start; using detail::to_week; switch (y_.start()) { case start::sunday: return to_week(*this).ok(); case start::monday: return to_week(*this).ok(); case start::tuesday: return to_week(*this).ok(); case start::wednesday: return to_week(*this).ok(); case start::thursday: return to_week(*this).ok(); case start::friday: return to_week(*this).ok(); case start::saturday: return to_week(*this).ok(); default: detail::never_reached(); } } // year_lastweek_weekday CONSTCD11 inline year_lastweek_weekday::year_lastweek_weekday(const week_shim::year& y, const week_shim::weekday& wd) NOEXCEPT : y_(y) , wd_(wd) {} CONSTCD11 inline week_shim::year year_lastweek_weekday::year() const NOEXCEPT { return y_; } CONSTCD14 inline week::weeknum year_lastweek_weekday::weeknum() const NOEXCEPT { using start = week::start; using detail::to_week; switch (y_.start()) { case start::sunday: return to_week(*this).weeknum(); case start::monday: return to_week(*this).weeknum(); case start::tuesday: return to_week(*this).weeknum(); case start::wednesday: return to_week(*this).weeknum(); case start::thursday: return to_week(*this).weeknum(); case start::friday: return to_week(*this).weeknum(); case start::saturday: return to_week(*this).weeknum(); default: detail::never_reached(); } } CONSTCD11 inline week_shim::weekday year_lastweek_weekday::weekday() const NOEXCEPT { return wd_; } // year_weeknum from operator/() CONSTCD11 inline year_weeknum operator/(const year& y, const week::weeknum& wn) NOEXCEPT { return {y, wn}; } CONSTCD11 inline year_weeknum operator/(const year& y, int wn) NOEXCEPT { return y / week::weeknum(static_cast(wn)); } // year_lastweek from operator/() CONSTCD11 inline year_lastweek operator/(const year& y, week::last_week) NOEXCEPT { return year_lastweek{y}; } // year_weeknum_weekday from operator/() CONSTCD11 inline year_weeknum_weekday operator/(const year_weeknum& ywn, const weekday& wd) NOEXCEPT { return {ywn, wd}; } CONSTCD11 inline year_weeknum_weekday operator/(const year_weeknum& ywn, int wd) NOEXCEPT { return ywn / weekday(static_cast(wd)); } // year_lastweek_weekday from operator/() CONSTCD11 inline year_lastweek_weekday operator/(const year_lastweek& ylw, const weekday& wd) NOEXCEPT { return {ylw.year(), wd}; } } // namespace week_shim } // namespace rweek } // namespace rclock #endif clock/src/limits.cpp0000644000176200001440000000035514424761554014160 0ustar liggesusers#include "clock.h" [[cpp11::register]] int clock_get_calendar_year_maximum() { return static_cast(date::year::max()); } [[cpp11::register]] int clock_get_calendar_year_minimum() { return static_cast(date::year::min()); } clock/src/stream.h0000644000176200001440000000271714417321664013617 0ustar liggesusers#ifndef CLOCK_STREAM_H #define CLOCK_STREAM_H #include "clock.h" namespace rclock { namespace detail { inline std::ostringstream& stream_year(std::ostringstream& os, int year) NOEXCEPT { os << date::year{year}; return os; } inline std::ostringstream& stream_month(std::ostringstream& os, int month) NOEXCEPT { os.fill('0'); os.flags(std::ios::dec | std::ios::right); os.width(2); os << month; return os; } inline std::ostringstream& stream_day(std::ostringstream& os, int day) NOEXCEPT { os << date::day{static_cast(day)}; return os; } inline std::ostringstream& stream_hour(std::ostringstream& os, int hour) NOEXCEPT { os.fill('0'); os.flags(std::ios::dec | std::ios::right); os.width(2); os << hour; return os; } inline std::ostringstream& stream_minute(std::ostringstream& os, int minute) NOEXCEPT { os.fill('0'); os.flags(std::ios::dec | std::ios::right); os.width(2); os << minute; return os; } inline std::ostringstream& stream_second(std::ostringstream& os, int second) NOEXCEPT { os.fill('0'); os.flags(std::ios::dec | std::ios::right); os.width(2); os << second; return os; } template inline std::ostringstream& stream_second_and_subsecond(std::ostringstream& os, int second, int subsecond) NOEXCEPT { date::detail::decimal_format_seconds dfs{std::chrono::seconds{second} + Duration{subsecond}}; os << dfs; return os; } } // namespace detail } // namespace rclock #endif clock/src/failure.h0000644000176200001440000000250414042042347013735 0ustar liggesusers#ifndef CLOCK_FAILURE_H #define CLOCK_FAILURE_H #include "clock.h" // ----------------------------------------------------------------------------- namespace rclock { class failures { private: r_ssize n_; r_ssize first_; public: CONSTCD11 failures() NOEXCEPT; void write(r_ssize i); CONSTCD11 bool any_failures() const NOEXCEPT; void warn_parse() const; void warn_format() const; }; CONSTCD11 inline failures::failures() NOEXCEPT : n_(0), first_(0) {} inline void failures::write(r_ssize i) { if (n_ == 0) { first_ = i; } ++n_; } CONSTCD11 inline bool failures::any_failures() const NOEXCEPT { return n_ > 0; } inline void failures::warn_parse() const { cpp11::writable::integers n(1); cpp11::writable::integers first(1); n[0] = (int) n_; first[0] = (int) first_ + 1; auto r_warn = cpp11::package("clock")["warn_clock_parse_failures"]; r_warn(n, first); } inline void failures::warn_format() const { cpp11::writable::integers n(1); cpp11::writable::integers first(1); n[0] = (int) n_; first[0] = (int) first_ + 1; auto r_warn = cpp11::package("clock")["warn_clock_format_failures"]; r_warn(n, first); } } // namespace rclock // ----------------------------------------------------------------------------- #endif // CLOCK_FAILURE_H clock/src/get.h0000644000176200001440000001602614422221153013065 0ustar liggesusers#ifndef CLOCK_GET_H #define CLOCK_GET_H #include "clock.h" namespace rclock { namespace gregorian { static inline cpp11::integers get_year(cpp11::list_of& fields) { return fields.size() >= 1 ? fields[0] : cpp11::integers{}; } static inline cpp11::integers get_month(cpp11::list_of& fields) { return fields.size() >= 2 ? fields[1] : cpp11::integers{}; } static inline cpp11::integers get_day(cpp11::list_of& fields) { return fields.size() >= 3 ? fields[2] : cpp11::integers{}; } static inline cpp11::integers get_hour(cpp11::list_of& fields) { return fields.size() >= 4 ? fields[3] : cpp11::integers{}; } static inline cpp11::integers get_minute(cpp11::list_of& fields) { return fields.size() >= 5 ? fields[4] : cpp11::integers{}; } static inline cpp11::integers get_second(cpp11::list_of& fields) { return fields.size() >= 6 ? fields[5] : cpp11::integers{}; } static inline cpp11::integers get_subsecond(cpp11::list_of& fields) { return fields.size() >= 7 ? fields[6] : cpp11::integers{}; } } // namespace gregorian } // namespace rclock // ----------------------------------------------------------------------------- namespace rclock { namespace weekday { static inline cpp11::integers get_year(cpp11::list_of& fields) { return fields.size() >= 1 ? fields[0] : cpp11::integers{}; } static inline cpp11::integers get_month(cpp11::list_of& fields) { return fields.size() >= 2 ? fields[1] : cpp11::integers{}; } static inline cpp11::integers get_day(cpp11::list_of& fields) { return fields.size() >= 3 ? fields[2] : cpp11::integers{}; } static inline cpp11::integers get_index(cpp11::list_of& fields) { return fields.size() >= 4 ? fields[3] : cpp11::integers{}; } static inline cpp11::integers get_hour(cpp11::list_of& fields) { return fields.size() >= 5 ? fields[4] : cpp11::integers{}; } static inline cpp11::integers get_minute(cpp11::list_of& fields) { return fields.size() >= 6 ? fields[5] : cpp11::integers{}; } static inline cpp11::integers get_second(cpp11::list_of& fields) { return fields.size() >= 7 ? fields[6] : cpp11::integers{}; } static inline cpp11::integers get_subsecond(cpp11::list_of& fields) { return fields.size() >= 8 ? fields[7] : cpp11::integers{}; } } // namespace weekday } // namespace rclock // ----------------------------------------------------------------------------- namespace rclock { namespace rquarterly { static inline cpp11::integers get_year(cpp11::list_of& fields) { return fields.size() >= 1 ? fields[0] : cpp11::integers{}; } static inline cpp11::integers get_quarter(cpp11::list_of& fields) { return fields.size() >= 2 ? fields[1] : cpp11::integers{}; } static inline cpp11::integers get_day(cpp11::list_of& fields) { return fields.size() >= 3 ? fields[2] : cpp11::integers{}; } static inline cpp11::integers get_hour(cpp11::list_of& fields) { return fields.size() >= 4 ? fields[3] : cpp11::integers{}; } static inline cpp11::integers get_minute(cpp11::list_of& fields) { return fields.size() >= 5 ? fields[4] : cpp11::integers{}; } static inline cpp11::integers get_second(cpp11::list_of& fields) { return fields.size() >= 6 ? fields[5] : cpp11::integers{}; } static inline cpp11::integers get_subsecond(cpp11::list_of& fields) { return fields.size() >= 7 ? fields[6] : cpp11::integers{}; } } // namespace rquarterly } // namespace rclock // ----------------------------------------------------------------------------- namespace rclock { namespace iso { static inline cpp11::integers get_year(cpp11::list_of& fields) { return fields.size() >= 1 ? fields[0] : cpp11::integers{}; } static inline cpp11::integers get_week(cpp11::list_of& fields) { return fields.size() >= 2 ? fields[1] : cpp11::integers{}; } static inline cpp11::integers get_day(cpp11::list_of& fields) { return fields.size() >= 3 ? fields[2] : cpp11::integers{}; } static inline cpp11::integers get_hour(cpp11::list_of& fields) { return fields.size() >= 4 ? fields[3] : cpp11::integers{}; } static inline cpp11::integers get_minute(cpp11::list_of& fields) { return fields.size() >= 5 ? fields[4] : cpp11::integers{}; } static inline cpp11::integers get_second(cpp11::list_of& fields) { return fields.size() >= 6 ? fields[5] : cpp11::integers{}; } static inline cpp11::integers get_subsecond(cpp11::list_of& fields) { return fields.size() >= 7 ? fields[6] : cpp11::integers{}; } } // namespace iso } // namespace rclock // ----------------------------------------------------------------------------- namespace rclock { namespace rweek { static inline cpp11::integers get_year(cpp11::list_of& fields) { return fields.size() >= 1 ? fields[0] : cpp11::integers{}; } static inline cpp11::integers get_week(cpp11::list_of& fields) { return fields.size() >= 2 ? fields[1] : cpp11::integers{}; } static inline cpp11::integers get_day(cpp11::list_of& fields) { return fields.size() >= 3 ? fields[2] : cpp11::integers{}; } static inline cpp11::integers get_hour(cpp11::list_of& fields) { return fields.size() >= 4 ? fields[3] : cpp11::integers{}; } static inline cpp11::integers get_minute(cpp11::list_of& fields) { return fields.size() >= 5 ? fields[4] : cpp11::integers{}; } static inline cpp11::integers get_second(cpp11::list_of& fields) { return fields.size() >= 6 ? fields[5] : cpp11::integers{}; } static inline cpp11::integers get_subsecond(cpp11::list_of& fields) { return fields.size() >= 7 ? fields[6] : cpp11::integers{}; } } // namespace rweek } // namespace rclock // ----------------------------------------------------------------------------- namespace rclock { namespace yearday { static inline cpp11::integers get_year(cpp11::list_of& fields) { return fields.size() >= 1 ? fields[0] : cpp11::integers{}; } static inline cpp11::integers get_day(cpp11::list_of& fields) { return fields.size() >= 2 ? fields[1] : cpp11::integers{}; } static inline cpp11::integers get_hour(cpp11::list_of& fields) { return fields.size() >= 3 ? fields[2] : cpp11::integers{}; } static inline cpp11::integers get_minute(cpp11::list_of& fields) { return fields.size() >= 4 ? fields[3] : cpp11::integers{}; } static inline cpp11::integers get_second(cpp11::list_of& fields) { return fields.size() >= 5 ? fields[4] : cpp11::integers{}; } static inline cpp11::integers get_subsecond(cpp11::list_of& fields) { return fields.size() >= 6 ? fields[5] : cpp11::integers{}; } } // namespace yearday } // namespace rclock #endif clock/src/quarterly-year-quarter-day.h0000644000176200001440000006277614427166644017567 0ustar liggesusers#ifndef CLOCK_QUARTERLY_YEAR_QUARTER_DAY_H #define CLOCK_QUARTERLY_YEAR_QUARTER_DAY_H #include "clock.h" #include "quarterly-shim.h" #include "integers.h" #include "enums.h" #include "utils.h" #include "stream.h" #include "resolve.h" namespace rclock { namespace rquarterly { namespace detail { inline std::ostringstream& stream_quarter(std::ostringstream& os, int quarter) NOEXCEPT { os << quarterly::quarternum{static_cast(quarter)}; return os; } inline std::ostringstream& stream_day(std::ostringstream& os, int day) NOEXCEPT { os.fill('0'); os.flags(std::ios::dec | std::ios::right); os.width(2); os << day; return os; } inline quarterly_shim::year_quarternum_quarterday resolve_next_day_yqd(const quarterly_shim::year_quarternum_quarterday& x) { return ((x.year() / x.quarternum()) + quarterly::quarters(1)) / quarterly::quarterday{1u}; } inline quarterly_shim::year_quarternum_quarterday resolve_previous_day_yqd(const quarterly_shim::year_quarternum_quarterday& x) { return x.year() / x.quarternum() / quarterly::last; } inline quarterly_shim::year_quarternum_quarterday resolve_overflow_day_yqd(const quarterly_shim::year_quarternum_quarterday& x) { return quarterly_shim::year_quarternum_quarterday{date::sys_days{x}, x.year().start()}; } } // namespace detail class y { protected: rclock::integers year_; quarterly::start start_; public: y(r_ssize size, quarterly::start start); y(const cpp11::integers& year, quarterly::start start); bool is_na(r_ssize i) const NOEXCEPT; r_ssize size() const NOEXCEPT; std::ostringstream& stream(std::ostringstream&, r_ssize i) const NOEXCEPT; void add(const date::years& x, r_ssize i) NOEXCEPT; void assign_year(const quarterly_shim::year& x, r_ssize i) NOEXCEPT; void assign_na(r_ssize i) NOEXCEPT; quarterly_shim::year to_year(r_ssize i) const NOEXCEPT; cpp11::writable::list to_list() const; }; class yqn : public y { protected: rclock::integers quarter_; public: yqn(r_ssize size, quarterly::start start); yqn(const cpp11::integers& year, const cpp11::integers& quarter, quarterly::start start); std::ostringstream& stream(std::ostringstream&, r_ssize i) const NOEXCEPT; void add(const quarterly::quarters& x, r_ssize i) NOEXCEPT; void assign_quarternum(const quarterly::quarternum& x, r_ssize i) NOEXCEPT; void assign_year_quarternum(const quarterly_shim::year_quarternum& x, r_ssize i) NOEXCEPT; void assign_na(r_ssize i) NOEXCEPT; quarterly_shim::year_quarternum to_year_quarternum(r_ssize i) const NOEXCEPT; cpp11::writable::list to_list() const; }; class yqnqd : public yqn { protected: rclock::integers day_; public: yqnqd(r_ssize size, quarterly::start start); yqnqd(const cpp11::integers& year, const cpp11::integers& quarter, const cpp11::integers& day, quarterly::start start); std::ostringstream& stream(std::ostringstream&, r_ssize i) const NOEXCEPT; void assign_quarterday(const quarterly::quarterday& x, r_ssize i) NOEXCEPT; void assign_year_quarternum_quarterday(const quarterly_shim::year_quarternum_quarterday& x, r_ssize i) NOEXCEPT; void assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT; void assign_na(r_ssize i) NOEXCEPT; void resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call); date::sys_time to_sys_time(r_ssize i) const NOEXCEPT; quarterly_shim::year_quarternum_quarterday to_year_quarternum_quarterday(r_ssize i) const NOEXCEPT; cpp11::writable::list to_list() const; }; class yqnqdh : public yqnqd { protected: rclock::integers hour_; public: yqnqdh(r_ssize size, quarterly::start start); yqnqdh(const cpp11::integers& year, const cpp11::integers& quarter, const cpp11::integers& day, const cpp11::integers& hour, quarterly::start start); std::ostringstream& stream(std::ostringstream&, r_ssize i) const NOEXCEPT; void assign_hour(const std::chrono::hours& x, r_ssize i) NOEXCEPT; void assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT; void assign_na(r_ssize i) NOEXCEPT; void resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call); date::sys_time to_sys_time(r_ssize i) const NOEXCEPT; cpp11::writable::list to_list() const; }; class yqnqdhm : public yqnqdh { protected: rclock::integers minute_; public: yqnqdhm(r_ssize size, quarterly::start start); yqnqdhm(const cpp11::integers& year, const cpp11::integers& quarter, const cpp11::integers& day, const cpp11::integers& hour, const cpp11::integers& minute, quarterly::start start); std::ostringstream& stream(std::ostringstream&, r_ssize i) const NOEXCEPT; void assign_minute(const std::chrono::minutes& x, r_ssize i) NOEXCEPT; void assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT; void assign_na(r_ssize i) NOEXCEPT; void resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call); date::sys_time to_sys_time(r_ssize i) const NOEXCEPT; cpp11::writable::list to_list() const; }; class yqnqdhms : public yqnqdhm { protected: rclock::integers second_; public: yqnqdhms(r_ssize size, quarterly::start start); yqnqdhms(const cpp11::integers& year, const cpp11::integers& quarter, const cpp11::integers& day, const cpp11::integers& hour, const cpp11::integers& minute, const cpp11::integers& second, quarterly::start start); std::ostringstream& stream(std::ostringstream&, r_ssize i) const NOEXCEPT; void assign_second(const std::chrono::seconds& x, r_ssize i) NOEXCEPT; void assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT; void assign_na(r_ssize i) NOEXCEPT; void resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call); date::sys_time to_sys_time(r_ssize i) const NOEXCEPT; cpp11::writable::list to_list() const; }; template class yqnqdhmss : public yqnqdhms { protected: rclock::integers subsecond_; public: yqnqdhmss(r_ssize size, quarterly::start start); yqnqdhmss(const cpp11::integers& year, const cpp11::integers& quarter, const cpp11::integers& day, const cpp11::integers& hour, const cpp11::integers& minute, const cpp11::integers& second, const cpp11::integers& subsecond, quarterly::start start); std::ostringstream& stream(std::ostringstream&, r_ssize i) const NOEXCEPT; void assign_subsecond(const Duration& x, r_ssize i) NOEXCEPT; void assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT; void assign_na(r_ssize i) NOEXCEPT; void resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call); date::sys_time to_sys_time(r_ssize i) const NOEXCEPT; cpp11::writable::list to_list() const; }; // Implementation // y inline y::y(r_ssize size, quarterly::start start) : year_(size), start_(start) {} inline y::y(const cpp11::integers& year, quarterly::start start) : year_(rclock::integers(year)), start_(start) {} inline bool y::is_na(r_ssize i) const NOEXCEPT { return year_.is_na(i); } inline r_ssize y::size() const NOEXCEPT { return year_.size(); } inline std::ostringstream& y::stream(std::ostringstream& os, r_ssize i) const NOEXCEPT { rclock::detail::stream_year(os, year_[i]); return os; } inline void y::add(const date::years& x, r_ssize i) NOEXCEPT { assign_year(to_year(i) + x, i); } inline void y::assign_year(const quarterly_shim::year& x, r_ssize i) NOEXCEPT { year_.assign(static_cast(x), i); } inline void y::assign_na(r_ssize i) NOEXCEPT { year_.assign_na(i); } inline quarterly_shim::year y::to_year(r_ssize i) const NOEXCEPT { return quarterly_shim::year{year_[i], start_}; } inline cpp11::writable::list y::to_list() const { cpp11::writable::list out({year_.sexp()}); out.names() = {"year"}; return out; } // yqn inline yqn::yqn(r_ssize size, quarterly::start start) : y(size, start), quarter_(size) {} inline yqn::yqn(const cpp11::integers& year, const cpp11::integers& quarter, quarterly::start start) : y(year, start), quarter_(quarter) {} inline std::ostringstream& yqn::stream(std::ostringstream& os, r_ssize i) const NOEXCEPT { y::stream(os, i); os << '-'; detail::stream_quarter(os, quarter_[i]); return os; } inline void yqn::add(const quarterly::quarters& x, r_ssize i) NOEXCEPT { assign_year_quarternum(to_year_quarternum(i) + x, i); } inline void yqn::assign_quarternum(const quarterly::quarternum& x, r_ssize i) NOEXCEPT { quarter_.assign(static_cast(static_cast(x)), i); } inline void yqn::assign_year_quarternum(const quarterly_shim::year_quarternum& x, r_ssize i) NOEXCEPT { y::assign_year(x.year(), i); assign_quarternum(x.quarternum(), i); } inline void yqn::assign_na(r_ssize i) NOEXCEPT { y::assign_na(i); quarter_.assign_na(i); } inline quarterly_shim::year_quarternum yqn::to_year_quarternum(r_ssize i) const NOEXCEPT { return y::to_year(i) / static_cast(quarter_[i]); } inline cpp11::writable::list yqn::to_list() const { cpp11::writable::list out({y::year_.sexp(), quarter_.sexp()}); out.names() = {"year", "quarter"}; return out; } // yqnqd inline yqnqd::yqnqd(r_ssize size, quarterly::start start) : yqn(size, start), day_(size) {} inline yqnqd::yqnqd(const cpp11::integers& year, const cpp11::integers& quarter, const cpp11::integers& day, quarterly::start start) : yqn(year, quarter, start), day_(day) {} inline std::ostringstream& yqnqd::stream(std::ostringstream& os, r_ssize i) const NOEXCEPT { yqn::stream(os, i); os << '-'; detail::stream_day(os, day_[i]); return os; } inline void yqnqd::assign_quarterday(const quarterly::quarterday& x, r_ssize i) NOEXCEPT { day_.assign(static_cast(static_cast(x)), i); } inline void yqnqd::assign_year_quarternum_quarterday(const quarterly_shim::year_quarternum_quarterday& x, r_ssize i) NOEXCEPT { yqn::assign_year(x.year(), i); yqn::assign_quarternum(x.quarternum(), i); assign_quarterday(x.quarterday(), i); } inline void yqnqd::assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT { quarterly_shim::year_quarternum_quarterday yqnqd{x, y::start_}; assign_year_quarternum_quarterday(yqnqd, i); } inline void yqnqd::assign_na(r_ssize i) NOEXCEPT { yqn::assign_na(i); day_.assign_na(i); } inline void yqnqd::resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call) { const quarterly_shim::year_quarternum_quarterday elt = to_year_quarternum_quarterday(i); if (elt.ok()) { return; } switch (type) { case invalid::next_day: case invalid::next: { assign_year_quarternum_quarterday(detail::resolve_next_day_yqd(elt), i); break; } case invalid::previous_day: case invalid::previous: { assign_quarterday(detail::resolve_previous_day_yqd(elt).quarterday(), i); break; } case invalid::overflow_day: case invalid::overflow: { assign_year_quarternum_quarterday(detail::resolve_overflow_day_yqd(elt), i); break; } case invalid::na: { assign_na(i); break; } case invalid::error: { rclock::detail::resolve_error(i, call); } } } inline date::sys_time yqnqd::to_sys_time(r_ssize i) const NOEXCEPT { return date::sys_time{to_year_quarternum_quarterday(i)}; } inline quarterly_shim::year_quarternum_quarterday yqnqd::to_year_quarternum_quarterday(r_ssize i) const NOEXCEPT { return yqn::to_year_quarternum(i) / static_cast(day_[i]); } inline cpp11::writable::list yqnqd::to_list() const { cpp11::writable::list out({yqn::year_.sexp(), yqn::quarter_.sexp(), day_.sexp()}); out.names() = {"year", "quarter", "day"}; return out; } // yqnqdh inline yqnqdh::yqnqdh(r_ssize size, quarterly::start start) : yqnqd(size, start), hour_(size) {} inline yqnqdh::yqnqdh(const cpp11::integers& year, const cpp11::integers& quarter, const cpp11::integers& day, const cpp11::integers& hour, quarterly::start start) : yqnqd(year, quarter, day, start), hour_(hour) {} inline std::ostringstream& yqnqdh::stream(std::ostringstream& os, r_ssize i) const NOEXCEPT { yqnqd::stream(os, i); os << 'T'; rclock::detail::stream_hour(os, hour_[i]); return os; } inline void yqnqdh::assign_hour(const std::chrono::hours& x, r_ssize i) NOEXCEPT { hour_.assign(x.count(), i); } inline void yqnqdh::assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT { const date::sys_time day_point = date::floor(x); const std::chrono::hours hours = x - day_point; yqnqd::assign_sys_time(day_point, i); assign_hour(hours, i); } inline void yqnqdh::assign_na(r_ssize i) NOEXCEPT { yqnqd::assign_na(i); hour_.assign_na(i); } inline void yqnqdh::resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call) { const quarterly_shim::year_quarternum_quarterday elt = yqnqd::to_year_quarternum_quarterday(i); if (elt.ok()) { return; } switch (type) { case invalid::next_day: yqnqd::assign_year_quarternum_quarterday(detail::resolve_next_day_yqd(elt), i); break; case invalid::next: { yqnqd::assign_year_quarternum_quarterday(detail::resolve_next_day_yqd(elt), i); assign_hour(rclock::detail::resolve_next_hour(), i); break; } case invalid::previous_day: yqnqd::assign_quarterday(detail::resolve_previous_day_yqd(elt).quarterday(), i); break; case invalid::previous: { yqnqd::assign_quarterday(detail::resolve_previous_day_yqd(elt).quarterday(), i); assign_hour(rclock::detail::resolve_previous_hour(), i); break; } case invalid::overflow_day: { yqnqd::assign_year_quarternum_quarterday(detail::resolve_overflow_day_yqd(elt), i); break; } case invalid::overflow: { yqnqd::assign_year_quarternum_quarterday(detail::resolve_overflow_day_yqd(elt), i); assign_hour(rclock::detail::resolve_next_hour(), i); break; } case invalid::na: { assign_na(i); break; } case invalid::error: { rclock::detail::resolve_error(i, call); } } } inline date::sys_time yqnqdh::to_sys_time(r_ssize i) const NOEXCEPT { return yqnqd::to_sys_time(i) + std::chrono::hours{hour_[i]}; } inline cpp11::writable::list yqnqdh::to_list() const { cpp11::writable::list out({yqnqd::year_.sexp(), yqnqd::quarter_.sexp(), yqnqd::day_.sexp(), hour_.sexp()}); out.names() = {"year", "quarter", "day", "hour"}; return out; } // yqnqdhm inline yqnqdhm::yqnqdhm(r_ssize size, quarterly::start start) : yqnqdh(size, start), minute_(size) {} inline yqnqdhm::yqnqdhm(const cpp11::integers& year, const cpp11::integers& quarter, const cpp11::integers& day, const cpp11::integers& hour, const cpp11::integers& minute, quarterly::start start) : yqnqdh(year, quarter, day, hour, start), minute_(minute) {} inline std::ostringstream& yqnqdhm::stream(std::ostringstream& os, r_ssize i) const NOEXCEPT { yqnqdh::stream(os, i); os << ':'; rclock::detail::stream_minute(os, minute_[i]); return os; } inline void yqnqdhm::assign_minute(const std::chrono::minutes& x, r_ssize i) NOEXCEPT { minute_.assign(x.count(), i); } inline void yqnqdhm::assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT { const date::sys_time hour_point = date::floor(x); const std::chrono::minutes minutes = x - hour_point; yqnqdh::assign_sys_time(hour_point, i); assign_minute(minutes, i); } inline void yqnqdhm::assign_na(r_ssize i) NOEXCEPT { yqnqdh::assign_na(i); minute_.assign_na(i); } inline void yqnqdhm::resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call) { const quarterly_shim::year_quarternum_quarterday elt = yqnqdh::to_year_quarternum_quarterday(i); if (elt.ok()) { return; } switch (type) { case invalid::next_day: yqnqdh::assign_year_quarternum_quarterday(detail::resolve_next_day_yqd(elt), i); break; case invalid::next: { yqnqdh::assign_year_quarternum_quarterday(detail::resolve_next_day_yqd(elt), i); yqnqdh::assign_hour(rclock::detail::resolve_next_hour(), i); assign_minute(rclock::detail::resolve_next_minute(), i); break; } case invalid::previous_day: yqnqdh::assign_quarterday(detail::resolve_previous_day_yqd(elt).quarterday(), i); break; case invalid::previous: { yqnqdh::assign_quarterday(detail::resolve_previous_day_yqd(elt).quarterday(), i); yqnqdh::assign_hour(rclock::detail::resolve_previous_hour(), i); assign_minute(rclock::detail::resolve_previous_minute(), i); break; } case invalid::overflow_day: { yqnqdh::assign_year_quarternum_quarterday(detail::resolve_overflow_day_yqd(elt), i); break; } case invalid::overflow: { yqnqdh::assign_year_quarternum_quarterday(detail::resolve_overflow_day_yqd(elt), i); yqnqdh::assign_hour(rclock::detail::resolve_next_hour(), i); assign_minute(rclock::detail::resolve_next_minute(), i); break; } case invalid::na: { assign_na(i); break; } case invalid::error: { rclock::detail::resolve_error(i, call); } } } inline date::sys_time yqnqdhm::to_sys_time(r_ssize i) const NOEXCEPT { return yqnqdh::to_sys_time(i) + std::chrono::minutes{minute_[i]}; } inline cpp11::writable::list yqnqdhm::to_list() const { cpp11::writable::list out({yqnqdh::year_.sexp(), yqnqdh::quarter_.sexp(), yqnqdh::day_.sexp(), yqnqdh::hour_.sexp(), minute_.sexp()}); out.names() = {"year", "quarter", "day", "hour", "minute"}; return out; } // yqnqdhms inline yqnqdhms::yqnqdhms(r_ssize size, quarterly::start start) : yqnqdhm(size, start), second_(size) {} inline yqnqdhms::yqnqdhms(const cpp11::integers& year, const cpp11::integers& quarter, const cpp11::integers& day, const cpp11::integers& hour, const cpp11::integers& minute, const cpp11::integers& second, quarterly::start start) : yqnqdhm(year, quarter, day, hour, minute, start), second_(second) {} inline std::ostringstream& yqnqdhms::stream(std::ostringstream& os, r_ssize i) const NOEXCEPT { yqnqdhm::stream(os, i); os << ':'; rclock::detail::stream_second(os, second_[i]); return os; } inline void yqnqdhms::assign_second(const std::chrono::seconds& x, r_ssize i) NOEXCEPT { second_.assign(x.count(), i); } inline void yqnqdhms::assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT { const date::sys_time minute_point = date::floor(x); const std::chrono::seconds seconds = x - minute_point; yqnqdhm::assign_sys_time(minute_point, i); assign_second(seconds, i); } inline void yqnqdhms::assign_na(r_ssize i) NOEXCEPT { yqnqdhm::assign_na(i); second_.assign_na(i); } inline void yqnqdhms::resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call) { const quarterly_shim::year_quarternum_quarterday elt = yqnqdhms::to_year_quarternum_quarterday(i); if (elt.ok()) { return; } switch (type) { case invalid::next_day: yqnqdhm::assign_year_quarternum_quarterday(detail::resolve_next_day_yqd(elt), i); break; case invalid::next: { yqnqdhm::assign_year_quarternum_quarterday(detail::resolve_next_day_yqd(elt), i); yqnqdhm::assign_hour(rclock::detail::resolve_next_hour(), i); yqnqdhm::assign_minute(rclock::detail::resolve_next_minute(), i); assign_second(rclock::detail::resolve_next_second(), i); break; } case invalid::previous_day: yqnqdhm::assign_quarterday(detail::resolve_previous_day_yqd(elt).quarterday(), i); break; case invalid::previous: { yqnqdhm::assign_quarterday(detail::resolve_previous_day_yqd(elt).quarterday(), i); yqnqdhm::assign_hour(rclock::detail::resolve_previous_hour(), i); yqnqdhm::assign_minute(rclock::detail::resolve_previous_minute(), i); assign_second(rclock::detail::resolve_previous_second(), i); break; } case invalid::overflow_day: { yqnqdhm::assign_year_quarternum_quarterday(detail::resolve_overflow_day_yqd(elt), i); break; } case invalid::overflow: { yqnqdhm::assign_year_quarternum_quarterday(detail::resolve_overflow_day_yqd(elt), i); yqnqdhm::assign_hour(rclock::detail::resolve_next_hour(), i); yqnqdhm::assign_minute(rclock::detail::resolve_next_minute(), i); assign_second(rclock::detail::resolve_next_second(), i); break; } case invalid::na: { assign_na(i); break; } case invalid::error: { rclock::detail::resolve_error(i, call); } } } inline date::sys_time yqnqdhms::to_sys_time(r_ssize i) const NOEXCEPT { return yqnqdhm::to_sys_time(i) + std::chrono::seconds{second_[i]}; } inline cpp11::writable::list yqnqdhms::to_list() const { cpp11::writable::list out({yqnqdhms::year_.sexp(), yqnqdhms::quarter_.sexp(), yqnqdhms::day_.sexp(), yqnqdhms::hour_.sexp(), yqnqdhms::minute_.sexp(), second_.sexp()}); out.names() = {"year", "quarter", "day", "hour", "minute", "second"}; return out; } // yqnqdhmss template inline yqnqdhmss::yqnqdhmss(r_ssize size, quarterly::start start) : yqnqdhms(size, start), subsecond_(size) {} template inline yqnqdhmss::yqnqdhmss(const cpp11::integers& year, const cpp11::integers& quarter, const cpp11::integers& day, const cpp11::integers& hour, const cpp11::integers& minute, const cpp11::integers& second, const cpp11::integers& subsecond, quarterly::start start) : yqnqdhms(year, quarter, day, hour, minute, second, start), subsecond_(subsecond) {} template inline std::ostringstream& yqnqdhmss::stream(std::ostringstream& os, r_ssize i) const NOEXCEPT { yqnqdhm::stream(os, i); os << ':'; rclock::detail::stream_second_and_subsecond(os, yqnqdhms::second_[i], subsecond_[i]); return os; } template inline void yqnqdhmss::assign_subsecond(const Duration& x, r_ssize i) NOEXCEPT { subsecond_.assign(x.count(), i); } template inline void yqnqdhmss::assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT { const date::sys_time second_point = date::floor(x); const Duration subseconds = x - second_point; yqnqdhms::assign_sys_time(second_point, i); assign_subsecond(subseconds, i); } template inline void yqnqdhmss::assign_na(r_ssize i) NOEXCEPT { yqnqdhms::assign_na(i); subsecond_.assign_na(i); } template inline void yqnqdhmss::resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call) { const quarterly_shim::year_quarternum_quarterday elt = yqnqdhms::to_year_quarternum_quarterday(i); if (elt.ok()) { return; } switch (type) { case invalid::next_day: yqnqdhms::assign_year_quarternum_quarterday(detail::resolve_next_day_yqd(elt), i); break; case invalid::next: { yqnqdhms::assign_year_quarternum_quarterday(detail::resolve_next_day_yqd(elt), i); yqnqdhms::assign_hour(rclock::detail::resolve_next_hour(), i); yqnqdhms::assign_minute(rclock::detail::resolve_next_minute(), i); yqnqdhms::assign_second(rclock::detail::resolve_next_second(), i); assign_subsecond(rclock::detail::resolve_next_subsecond(), i); break; } case invalid::previous_day: yqnqdhms::assign_quarterday(detail::resolve_previous_day_yqd(elt).quarterday(), i); break; case invalid::previous: { yqnqdhms::assign_quarterday(detail::resolve_previous_day_yqd(elt).quarterday(), i); yqnqdhms::assign_hour(rclock::detail::resolve_previous_hour(), i); yqnqdhms::assign_minute(rclock::detail::resolve_previous_minute(), i); yqnqdhms::assign_second(rclock::detail::resolve_previous_second(), i); assign_subsecond(rclock::detail::resolve_previous_subsecond(), i); break; } case invalid::overflow_day: { yqnqdhms::assign_year_quarternum_quarterday(detail::resolve_overflow_day_yqd(elt), i); break; } case invalid::overflow: { yqnqdhms::assign_year_quarternum_quarterday(detail::resolve_overflow_day_yqd(elt), i); yqnqdhms::assign_hour(rclock::detail::resolve_next_hour(), i); yqnqdhms::assign_minute(rclock::detail::resolve_next_minute(), i); yqnqdhms::assign_second(rclock::detail::resolve_next_second(), i); assign_subsecond(rclock::detail::resolve_next_subsecond(), i); break; } case invalid::na: { assign_na(i); break; } case invalid::error: { rclock::detail::resolve_error(i, call); } } } template inline date::sys_time yqnqdhmss::to_sys_time(r_ssize i) const NOEXCEPT { return yqnqdhms::to_sys_time(i) + Duration{subsecond_[i]}; } template inline cpp11::writable::list yqnqdhmss::to_list() const { cpp11::writable::list out({yqnqdhms::year_.sexp(), yqnqdhms::quarter_.sexp(), yqnqdhms::day_.sexp(), yqnqdhms::hour_.sexp(), yqnqdhms::minute_.sexp(), yqnqdhms::second_.sexp(), subsecond_.sexp()}); out.names() = {"year", "quarter", "day", "hour", "minute", "second", "subsecond"}; return out; } } // namespace quarterly } // namespace rclock #endif clock/src/utils.cpp0000644000176200001440000002022414422221153013774 0ustar liggesusers#include "utils.h" SEXP strings = NULL; SEXP strings_empty = NULL; SEXP strings_vctrs_vctr = NULL; SEXP strings_vctrs_rcrd = NULL; SEXP strings_clock_rcrd = NULL; SEXP strings_clock_time_point = NULL; SEXP strings_clock_sys_time = NULL; SEXP strings_clock_naive_time = NULL; SEXP strings_clock_zoned_time = NULL; SEXP strings_clock_duration = NULL; SEXP strings_clock_calendar = NULL; SEXP strings_clock_year_month_day = NULL; SEXP strings_clock_year_month_weekday = NULL; SEXP strings_clock_year_day = NULL; SEXP strings_clock_year_week_day = NULL; SEXP strings_clock_iso_year_week_day = NULL; SEXP strings_clock_year_quarter_day = NULL; SEXP strings_data_frame = NULL; SEXP syms_precision = NULL; SEXP syms_start = NULL; SEXP syms_clock = NULL; SEXP syms_zone = NULL; SEXP syms_set_names = NULL; SEXP classes_duration = NULL; SEXP classes_sys_time = NULL; SEXP classes_naive_time = NULL; SEXP classes_zoned_time = NULL; SEXP classes_year_month_day = NULL; SEXP classes_year_month_weekday = NULL; SEXP classes_year_day = NULL; SEXP classes_year_week_day = NULL; SEXP classes_iso_year_week_day = NULL; SEXP classes_year_quarter_day = NULL; SEXP classes_data_frame = NULL; SEXP ints_empty = NULL; [[cpp11::register]] SEXP clock_init_utils() { strings = Rf_allocVector(STRSXP, 17); R_PreserveObject(strings); MARK_NOT_MUTABLE(strings); strings_empty = Rf_mkChar(""); SET_STRING_ELT(strings, 0, strings_empty); strings_vctrs_vctr = Rf_mkChar("vctrs_vctr"); SET_STRING_ELT(strings, 1, strings_vctrs_vctr); strings_vctrs_rcrd = Rf_mkChar("vctrs_rcrd"); SET_STRING_ELT(strings, 2, strings_vctrs_rcrd); strings_clock_rcrd = Rf_mkChar("clock_rcrd"); SET_STRING_ELT(strings, 3, strings_clock_rcrd); strings_clock_time_point = Rf_mkChar("clock_time_point"); SET_STRING_ELT(strings, 4, strings_clock_time_point); strings_clock_sys_time = Rf_mkChar("clock_sys_time"); SET_STRING_ELT(strings, 5, strings_clock_sys_time); strings_clock_naive_time = Rf_mkChar("clock_naive_time"); SET_STRING_ELT(strings, 6, strings_clock_naive_time); strings_clock_zoned_time = Rf_mkChar("clock_zoned_time"); SET_STRING_ELT(strings, 7, strings_clock_zoned_time); strings_clock_duration = Rf_mkChar("clock_duration"); SET_STRING_ELT(strings, 8, strings_clock_duration); strings_clock_calendar = Rf_mkChar("clock_calendar"); SET_STRING_ELT(strings, 9, strings_clock_calendar); strings_clock_year_month_day = Rf_mkChar("clock_year_month_day"); SET_STRING_ELT(strings, 10, strings_clock_year_month_day); strings_clock_year_month_weekday = Rf_mkChar("clock_year_month_weekday"); SET_STRING_ELT(strings, 11, strings_clock_year_month_weekday); strings_clock_year_day = Rf_mkChar("clock_year_day"); SET_STRING_ELT(strings, 12, strings_clock_year_day); strings_clock_year_week_day = Rf_mkChar("clock_year_week_day"); SET_STRING_ELT(strings, 13, strings_clock_year_week_day); strings_clock_iso_year_week_day = Rf_mkChar("clock_iso_year_week_day"); SET_STRING_ELT(strings, 14, strings_clock_iso_year_week_day); strings_clock_year_quarter_day = Rf_mkChar("clock_year_quarter_day"); SET_STRING_ELT(strings, 15, strings_clock_year_quarter_day); strings_data_frame = Rf_mkChar("data.frame"); SET_STRING_ELT(strings, 16, strings_data_frame); syms_precision = Rf_install("precision"); syms_start = Rf_install("start"); syms_clock = Rf_install("clock"); syms_zone = Rf_install("zone"); syms_set_names = Rf_install("names<-"); classes_duration = Rf_allocVector(STRSXP, 4); R_PreserveObject(classes_duration); MARK_NOT_MUTABLE(classes_duration); SET_STRING_ELT(classes_duration, 0, strings_clock_duration); SET_STRING_ELT(classes_duration, 1, strings_clock_rcrd); SET_STRING_ELT(classes_duration, 2, strings_vctrs_rcrd); SET_STRING_ELT(classes_duration, 3, strings_vctrs_vctr); classes_sys_time = Rf_allocVector(STRSXP, 5); R_PreserveObject(classes_sys_time); MARK_NOT_MUTABLE(classes_sys_time); SET_STRING_ELT(classes_sys_time, 0, strings_clock_sys_time); SET_STRING_ELT(classes_sys_time, 1, strings_clock_time_point); SET_STRING_ELT(classes_sys_time, 2, strings_clock_rcrd); SET_STRING_ELT(classes_sys_time, 3, strings_vctrs_rcrd); SET_STRING_ELT(classes_sys_time, 4, strings_vctrs_vctr); classes_naive_time = Rf_allocVector(STRSXP, 5); R_PreserveObject(classes_naive_time); MARK_NOT_MUTABLE(classes_naive_time); SET_STRING_ELT(classes_naive_time, 0, strings_clock_naive_time); SET_STRING_ELT(classes_naive_time, 1, strings_clock_time_point); SET_STRING_ELT(classes_naive_time, 2, strings_clock_rcrd); SET_STRING_ELT(classes_naive_time, 3, strings_vctrs_rcrd); SET_STRING_ELT(classes_naive_time, 4, strings_vctrs_vctr); classes_zoned_time = Rf_allocVector(STRSXP, 4); R_PreserveObject(classes_zoned_time); MARK_NOT_MUTABLE(classes_zoned_time); SET_STRING_ELT(classes_zoned_time, 0, strings_clock_zoned_time); SET_STRING_ELT(classes_zoned_time, 1, strings_clock_rcrd); SET_STRING_ELT(classes_zoned_time, 2, strings_vctrs_rcrd); SET_STRING_ELT(classes_zoned_time, 3, strings_vctrs_vctr); classes_year_month_day = Rf_allocVector(STRSXP, 5); R_PreserveObject(classes_year_month_day); MARK_NOT_MUTABLE(classes_year_month_day); SET_STRING_ELT(classes_year_month_day, 0, strings_clock_year_month_day); SET_STRING_ELT(classes_year_month_day, 1, strings_clock_calendar); SET_STRING_ELT(classes_year_month_day, 2, strings_clock_rcrd); SET_STRING_ELT(classes_year_month_day, 3, strings_vctrs_rcrd); SET_STRING_ELT(classes_year_month_day, 4, strings_vctrs_vctr); classes_year_month_weekday = Rf_allocVector(STRSXP, 5); R_PreserveObject(classes_year_month_weekday); MARK_NOT_MUTABLE(classes_year_month_weekday); SET_STRING_ELT(classes_year_month_weekday, 0, strings_clock_year_month_weekday); SET_STRING_ELT(classes_year_month_weekday, 1, strings_clock_calendar); SET_STRING_ELT(classes_year_month_weekday, 2, strings_clock_rcrd); SET_STRING_ELT(classes_year_month_weekday, 3, strings_vctrs_rcrd); SET_STRING_ELT(classes_year_month_weekday, 4, strings_vctrs_vctr); classes_year_day = Rf_allocVector(STRSXP, 5); R_PreserveObject(classes_year_day); MARK_NOT_MUTABLE(classes_year_day); SET_STRING_ELT(classes_year_day, 0, strings_clock_year_day); SET_STRING_ELT(classes_year_day, 1, strings_clock_calendar); SET_STRING_ELT(classes_year_day, 2, strings_clock_rcrd); SET_STRING_ELT(classes_year_day, 3, strings_vctrs_rcrd); SET_STRING_ELT(classes_year_day, 4, strings_vctrs_vctr); classes_year_week_day = Rf_allocVector(STRSXP, 5); R_PreserveObject(classes_year_week_day); MARK_NOT_MUTABLE(classes_year_week_day); SET_STRING_ELT(classes_year_week_day, 0, strings_clock_year_week_day); SET_STRING_ELT(classes_year_week_day, 1, strings_clock_calendar); SET_STRING_ELT(classes_year_week_day, 2, strings_clock_rcrd); SET_STRING_ELT(classes_year_week_day, 3, strings_vctrs_rcrd); SET_STRING_ELT(classes_year_week_day, 4, strings_vctrs_vctr); classes_iso_year_week_day = Rf_allocVector(STRSXP, 5); R_PreserveObject(classes_iso_year_week_day); MARK_NOT_MUTABLE(classes_iso_year_week_day); SET_STRING_ELT(classes_iso_year_week_day, 0, strings_clock_iso_year_week_day); SET_STRING_ELT(classes_iso_year_week_day, 1, strings_clock_calendar); SET_STRING_ELT(classes_iso_year_week_day, 2, strings_clock_rcrd); SET_STRING_ELT(classes_iso_year_week_day, 3, strings_vctrs_rcrd); SET_STRING_ELT(classes_iso_year_week_day, 4, strings_vctrs_vctr); classes_year_quarter_day = Rf_allocVector(STRSXP, 5); R_PreserveObject(classes_year_quarter_day); MARK_NOT_MUTABLE(classes_year_quarter_day); SET_STRING_ELT(classes_year_quarter_day, 0, strings_clock_year_quarter_day); SET_STRING_ELT(classes_year_quarter_day, 1, strings_clock_calendar); SET_STRING_ELT(classes_year_quarter_day, 2, strings_clock_rcrd); SET_STRING_ELT(classes_year_quarter_day, 3, strings_vctrs_rcrd); SET_STRING_ELT(classes_year_quarter_day, 4, strings_vctrs_vctr); classes_data_frame = Rf_allocVector(STRSXP, 1); R_PreserveObject(classes_data_frame); MARK_NOT_MUTABLE(classes_data_frame); SET_STRING_ELT(classes_data_frame, 0, strings_data_frame); ints_empty = Rf_allocVector(INTSXP, 0); R_PreserveObject(ints_empty); MARK_NOT_MUTABLE(ints_empty); return r_null; } clock/src/quarterly-shim.h0000644000176200001440000005536214427167173015322 0ustar liggesusers#ifndef CLOCK_QUARTERLY_SHIM_H #define CLOCK_QUARTERLY_SHIM_H /* * This file contains a minimal shim around the classes in `quarterly.h` that * exposes those classes in a template free way. The templating is necessary * in `quarterly.h` to correctly define the type, since the quarter start month * is a part of the type itself, but we can gloss over some of those details * for use in `quarterly-year-quarter-day.h`. Encapsulating the templates here * reduces the compilation time of clock significantly (around 3x, on an Intel * Mac the compilation time dropped from 70s to 25s). */ #include #include "quarterly.h" namespace rclock { namespace rquarterly { namespace quarterly_shim { class year; class year_quarternum; class year_quarternum_quarterday; class year_quarternum_quarterday_last; // date composition operators CONSTCD11 year_quarternum operator/(const year& y, const quarterly::quarternum& qn) NOEXCEPT; CONSTCD11 year_quarternum operator/(const year& y, int qn) NOEXCEPT; CONSTCD11 year_quarternum_quarterday operator/(const year_quarternum& yqn, const quarterly::quarterday& qd) NOEXCEPT; CONSTCD11 year_quarternum_quarterday operator/(const year_quarternum& yqn, int qd) NOEXCEPT; CONSTCD11 year_quarternum_quarterday_last operator/(const year_quarternum& yqn, quarterly::last_spec) NOEXCEPT; // year class year { short y_; quarterly::start s_; public: year() = default; explicit CONSTCD11 year(int y, quarterly::start s) NOEXCEPT; CONSTCD11 quarterly::start start() const NOEXCEPT; CONSTCD11 explicit operator int() const NOEXCEPT; CONSTCD14 bool is_leap() const NOEXCEPT; }; CONSTCD14 year operator+(const year& x, const quarterly::years& y) NOEXCEPT; CONSTCD14 quarterly::years operator-(const year& x, const year& y) NOEXCEPT; // year_quarternum class year_quarternum { quarterly_shim::year y_; quarterly::quarternum qn_; public: year_quarternum() = default; CONSTCD11 year_quarternum(const quarterly_shim::year& y, const quarterly::quarternum& qn) NOEXCEPT; CONSTCD11 quarterly_shim::year year() const NOEXCEPT; CONSTCD11 quarterly::quarternum quarternum() const NOEXCEPT; }; CONSTCD14 year_quarternum operator+(const year_quarternum& yqn, const quarterly::quarters& dq) NOEXCEPT; CONSTCD14 quarterly::quarters operator-(const year_quarternum& x, const year_quarternum& y) NOEXCEPT; // year_quarternum_quarterday class year_quarternum_quarterday { quarterly_shim::year y_; quarterly::quarternum qn_; quarterly::quarterday qd_; public: year_quarternum_quarterday() = default; CONSTCD11 year_quarternum_quarterday(const quarterly_shim::year& y, const quarterly::quarternum& qn, const quarterly::quarterday& qd) NOEXCEPT; CONSTCD11 year_quarternum_quarterday(const quarterly_shim::year_quarternum& yqn, const quarterly::quarterday& qd) NOEXCEPT; CONSTCD14 year_quarternum_quarterday(const year_quarternum_quarterday_last& yqnqdl) NOEXCEPT; CONSTCD14 year_quarternum_quarterday(const date::sys_days& dp, quarterly::start s) NOEXCEPT; CONSTCD14 year_quarternum_quarterday(const date::local_days& dp, quarterly::start s) NOEXCEPT; CONSTCD11 quarterly_shim::year year() const NOEXCEPT; CONSTCD11 quarterly::quarternum quarternum() const NOEXCEPT; CONSTCD11 quarterly::quarterday quarterday() const NOEXCEPT; CONSTCD14 operator date::sys_days() const NOEXCEPT; CONSTCD14 explicit operator date::local_days() const NOEXCEPT; CONSTCD14 bool ok() const NOEXCEPT; private: CONSTCD14 year_quarternum_quarterday from_sys_days(const date::sys_days& dp, quarterly::start s) NOEXCEPT; CONSTCD14 year_quarternum_quarterday from_local_days(const date::local_days& dp, quarterly::start s) NOEXCEPT; }; // year_quarternum_quarterday_last class year_quarternum_quarterday_last { quarterly_shim::year y_; quarterly::quarternum qn_; public: year_quarternum_quarterday_last() = default; CONSTCD11 year_quarternum_quarterday_last(const quarterly_shim::year& y, const quarterly::quarternum& qn) NOEXCEPT; CONSTCD11 year_quarternum_quarterday_last(const quarterly_shim::year_quarternum& yqn) NOEXCEPT; CONSTCD11 quarterly_shim::year year() const NOEXCEPT; CONSTCD11 quarterly::quarternum quarternum() const NOEXCEPT; CONSTCD14 quarterly::quarterday quarterday() const NOEXCEPT; }; //----------------+ // Implementation | //----------------+ namespace detail { static inline void never_reached [[noreturn]] () { // Compiler hint to allow [[noreturn]] attribute. This is never executed since // `never_reached()` is never actually called. throw std::runtime_error("[[noreturn]]"); } } // namespace detail namespace detail { template CONSTCD14 inline quarterly::year to_quarterly(const quarterly_shim::year& y) NOEXCEPT { return quarterly::year(static_cast(y)); } template CONSTCD14 inline quarterly::year_quarternum to_quarterly(const quarterly_shim::year_quarternum& yqn) NOEXCEPT { return quarterly::year_quarternum( to_quarterly(yqn.year()), yqn.quarternum() ); } template CONSTCD14 inline quarterly::year_quarternum_quarterday to_quarterly(const quarterly_shim::year_quarternum_quarterday& yqnqd) NOEXCEPT { return quarterly::year_quarternum_quarterday( to_quarterly(yqnqd.year()), yqnqd.quarternum(), yqnqd.quarterday() ); } template CONSTCD14 inline quarterly::year_quarternum_quarterday_last to_quarterly(const quarterly_shim::year_quarternum_quarterday_last& yqnqdl) NOEXCEPT { return quarterly::year_quarternum_quarterday_last( to_quarterly(yqnqdl.year()), yqnqdl.quarternum() ); } template CONSTCD14 inline quarterly_shim::year from_quarterly(const quarterly::year& y) NOEXCEPT { return quarterly_shim::year(static_cast(y), S); } template CONSTCD14 inline quarterly_shim::year_quarternum from_quarterly(const quarterly::year_quarternum& yqn) NOEXCEPT { return quarterly_shim::year_quarternum( from_quarterly(yqn.year()), yqn.quarternum() ); } template CONSTCD14 inline quarterly_shim::year_quarternum_quarterday from_quarterly(const quarterly::year_quarternum_quarterday& yqnqd) NOEXCEPT { return quarterly_shim::year_quarternum_quarterday( from_quarterly(yqnqd.year()), yqnqd.quarternum(), yqnqd.quarterday() ); } } // namespace detail // year CONSTCD11 inline year::year(int y, quarterly::start s) NOEXCEPT : y_(static_cast(y)), s_(s) {} CONSTCD11 inline quarterly::start year::start() const NOEXCEPT { return s_; } CONSTCD11 inline year::operator int() const NOEXCEPT { return y_; } CONSTCD14 bool year::is_leap() const NOEXCEPT { using start = quarterly::start; using detail::to_quarterly; switch (s_) { case start::january: return to_quarterly(*this).is_leap(); case start::february: return to_quarterly(*this).is_leap(); case start::march: return to_quarterly(*this).is_leap(); case start::april: return to_quarterly(*this).is_leap(); case start::may: return to_quarterly(*this).is_leap(); case start::june: return to_quarterly(*this).is_leap(); case start::july: return to_quarterly(*this).is_leap(); case start::august: return to_quarterly(*this).is_leap(); case start::september: return to_quarterly(*this).is_leap(); case start::october: return to_quarterly(*this).is_leap(); case start::november: return to_quarterly(*this).is_leap(); case start::december: return to_quarterly(*this).is_leap(); default: detail::never_reached(); } } CONSTCD14 inline year operator+(const year& x, const quarterly::years& y) NOEXCEPT { using start = quarterly::start; using detail::from_quarterly; using detail::to_quarterly; switch (x.start()) { case start::january: return from_quarterly(to_quarterly(x) + y); case start::february: return from_quarterly(to_quarterly(x) + y); case start::march: return from_quarterly(to_quarterly(x) + y); case start::april: return from_quarterly(to_quarterly(x) + y); case start::may: return from_quarterly(to_quarterly(x) + y); case start::june: return from_quarterly(to_quarterly(x) + y); case start::july: return from_quarterly(to_quarterly(x) + y); case start::august: return from_quarterly(to_quarterly(x) + y); case start::september: return from_quarterly(to_quarterly(x) + y); case start::october: return from_quarterly(to_quarterly(x) + y); case start::november: return from_quarterly(to_quarterly(x) + y); case start::december: return from_quarterly(to_quarterly(x) + y); default: detail::never_reached(); } } CONSTCD14 inline quarterly::years operator-(const year& x, const year& y) NOEXCEPT { using start = quarterly::start; using detail::to_quarterly; // Assumes `x.start()` and `y.start()` are the same, verified by caller switch (x.start()) { case start::january: return to_quarterly(x) - to_quarterly(y); case start::february: return to_quarterly(x) - to_quarterly(y); case start::march: return to_quarterly(x) - to_quarterly(y); case start::april: return to_quarterly(x) - to_quarterly(y); case start::may: return to_quarterly(x) - to_quarterly(y); case start::june: return to_quarterly(x) - to_quarterly(y); case start::july: return to_quarterly(x) - to_quarterly(y); case start::august: return to_quarterly(x) - to_quarterly(y); case start::september: return to_quarterly(x) - to_quarterly(y); case start::october: return to_quarterly(x) - to_quarterly(y); case start::november: return to_quarterly(x) - to_quarterly(y); case start::december: return to_quarterly(x) - to_quarterly(y); default: detail::never_reached(); } } // year_quarternum CONSTCD11 inline year_quarternum::year_quarternum(const quarterly_shim::year& y, const quarterly::quarternum& qn) NOEXCEPT : y_(y) , qn_(qn) {} CONSTCD11 inline quarterly_shim::year year_quarternum::year() const NOEXCEPT { return y_; } CONSTCD11 inline quarterly::quarternum year_quarternum::quarternum() const NOEXCEPT { return qn_; } CONSTCD14 inline year_quarternum operator+(const year_quarternum& yqn, const quarterly::quarters& dq) NOEXCEPT { using start = quarterly::start; using detail::from_quarterly; using detail::to_quarterly; switch (yqn.year().start()) { case start::january: return from_quarterly(to_quarterly(yqn) + dq); case start::february: return from_quarterly(to_quarterly(yqn) + dq); case start::march: return from_quarterly(to_quarterly(yqn) + dq); case start::april: return from_quarterly(to_quarterly(yqn) + dq); case start::may: return from_quarterly(to_quarterly(yqn) + dq); case start::june: return from_quarterly(to_quarterly(yqn) + dq); case start::july: return from_quarterly(to_quarterly(yqn) + dq); case start::august: return from_quarterly(to_quarterly(yqn) + dq); case start::september: return from_quarterly(to_quarterly(yqn) + dq); case start::october: return from_quarterly(to_quarterly(yqn) + dq); case start::november: return from_quarterly(to_quarterly(yqn) + dq); case start::december: return from_quarterly(to_quarterly(yqn) + dq); default: detail::never_reached(); } } CONSTCD14 inline quarterly::quarters operator-(const year_quarternum& x, const year_quarternum& y) NOEXCEPT { using start = quarterly::start; using detail::to_quarterly; // Assumes `x.year().start()` and `y.year().start()` are the same, verified by caller switch (x.year().start()) { case start::january: return to_quarterly(x) - to_quarterly(y); case start::february: return to_quarterly(x) - to_quarterly(y); case start::march: return to_quarterly(x) - to_quarterly(y); case start::april: return to_quarterly(x) - to_quarterly(y); case start::may: return to_quarterly(x) - to_quarterly(y); case start::june: return to_quarterly(x) - to_quarterly(y); case start::july: return to_quarterly(x) - to_quarterly(y); case start::august: return to_quarterly(x) - to_quarterly(y); case start::september: return to_quarterly(x) - to_quarterly(y); case start::october: return to_quarterly(x) - to_quarterly(y); case start::november: return to_quarterly(x) - to_quarterly(y); case start::december: return to_quarterly(x) - to_quarterly(y); default: detail::never_reached(); } } // year_quarternum_quarterday CONSTCD11 inline year_quarternum_quarterday::year_quarternum_quarterday(const quarterly_shim::year& y, const quarterly::quarternum& qn, const quarterly::quarterday& qd) NOEXCEPT : y_(y) , qn_(qn) , qd_(qd) {} CONSTCD11 inline year_quarternum_quarterday::year_quarternum_quarterday(const quarterly_shim::year_quarternum& yqn, const quarterly::quarterday& qd) NOEXCEPT : y_(yqn.year()) , qn_(yqn.quarternum()) , qd_(qd) {} CONSTCD14 inline year_quarternum_quarterday::year_quarternum_quarterday(const year_quarternum_quarterday_last& yqnqdl) NOEXCEPT : y_(yqnqdl.year()) , qn_(yqnqdl.quarternum()) , qd_(yqnqdl.quarterday()) {} CONSTCD14 inline year_quarternum_quarterday::year_quarternum_quarterday(const date::sys_days& dp, quarterly::start s) NOEXCEPT : year_quarternum_quarterday(from_sys_days(dp, s)) {} CONSTCD14 inline year_quarternum_quarterday::year_quarternum_quarterday(const date::local_days& dp, quarterly::start s) NOEXCEPT : year_quarternum_quarterday(from_local_days(dp, s)) {} CONSTCD14 inline year_quarternum_quarterday year_quarternum_quarterday::from_sys_days(const date::sys_days& dp, quarterly::start s) NOEXCEPT { using start = quarterly::start; using detail::from_quarterly; switch (s) { case start::january: return from_quarterly(quarterly::year_quarternum_quarterday(dp)); case start::february: return from_quarterly(quarterly::year_quarternum_quarterday(dp)); case start::march: return from_quarterly(quarterly::year_quarternum_quarterday(dp)); case start::april: return from_quarterly(quarterly::year_quarternum_quarterday(dp)); case start::may: return from_quarterly(quarterly::year_quarternum_quarterday(dp)); case start::june: return from_quarterly(quarterly::year_quarternum_quarterday(dp)); case start::july: return from_quarterly(quarterly::year_quarternum_quarterday(dp)); case start::august: return from_quarterly(quarterly::year_quarternum_quarterday(dp)); case start::september: return from_quarterly(quarterly::year_quarternum_quarterday(dp)); case start::october: return from_quarterly(quarterly::year_quarternum_quarterday(dp)); case start::november: return from_quarterly(quarterly::year_quarternum_quarterday(dp)); case start::december: return from_quarterly(quarterly::year_quarternum_quarterday(dp)); default: detail::never_reached(); } } CONSTCD14 inline year_quarternum_quarterday year_quarternum_quarterday::from_local_days(const date::local_days& dp, quarterly::start s) NOEXCEPT { return from_sys_days(date::sys_days(dp.time_since_epoch()), s); } CONSTCD11 inline quarterly_shim::year year_quarternum_quarterday::year() const NOEXCEPT { return y_; } CONSTCD11 inline quarterly::quarternum year_quarternum_quarterday::quarternum() const NOEXCEPT { return qn_; } CONSTCD11 inline quarterly::quarterday year_quarternum_quarterday::quarterday() const NOEXCEPT { return qd_; } CONSTCD14 inline year_quarternum_quarterday::operator date::sys_days() const NOEXCEPT { using start = quarterly::start; using detail::to_quarterly; switch (y_.start()) { case start::january: return to_quarterly(*this); case start::february: return to_quarterly(*this); case start::march: return to_quarterly(*this); case start::april: return to_quarterly(*this); case start::may: return to_quarterly(*this); case start::june: return to_quarterly(*this); case start::july: return to_quarterly(*this); case start::august: return to_quarterly(*this); case start::september: return to_quarterly(*this); case start::october: return to_quarterly(*this); case start::november: return to_quarterly(*this); case start::december: return to_quarterly(*this); default: detail::never_reached(); } } CONSTCD14 inline year_quarternum_quarterday::operator date::local_days() const NOEXCEPT { using start = quarterly::start; using detail::to_quarterly; switch (y_.start()) { case start::january: return date::local_days(to_quarterly(*this)); case start::february: return date::local_days(to_quarterly(*this)); case start::march: return date::local_days(to_quarterly(*this)); case start::april: return date::local_days(to_quarterly(*this)); case start::may: return date::local_days(to_quarterly(*this)); case start::june: return date::local_days(to_quarterly(*this)); case start::july: return date::local_days(to_quarterly(*this)); case start::august: return date::local_days(to_quarterly(*this)); case start::september: return date::local_days(to_quarterly(*this)); case start::october: return date::local_days(to_quarterly(*this)); case start::november: return date::local_days(to_quarterly(*this)); case start::december: return date::local_days(to_quarterly(*this)); default: detail::never_reached(); } } CONSTCD14 inline bool year_quarternum_quarterday::ok() const NOEXCEPT { using start = quarterly::start; using detail::to_quarterly; switch (y_.start()) { case start::january: return to_quarterly(*this).ok(); case start::february: return to_quarterly(*this).ok(); case start::march: return to_quarterly(*this).ok(); case start::april: return to_quarterly(*this).ok(); case start::may: return to_quarterly(*this).ok(); case start::june: return to_quarterly(*this).ok(); case start::july: return to_quarterly(*this).ok(); case start::august: return to_quarterly(*this).ok(); case start::september: return to_quarterly(*this).ok(); case start::october: return to_quarterly(*this).ok(); case start::november: return to_quarterly(*this).ok(); case start::december: return to_quarterly(*this).ok(); default: detail::never_reached(); } } // year_quarternum_quarterday_last CONSTCD11 inline year_quarternum_quarterday_last::year_quarternum_quarterday_last(const quarterly_shim::year& y, const quarterly::quarternum& qn) NOEXCEPT : y_(y) , qn_(qn) {} CONSTCD11 inline year_quarternum_quarterday_last::year_quarternum_quarterday_last(const quarterly_shim::year_quarternum& yqn) NOEXCEPT : y_(yqn.year()) , qn_(yqn.quarternum()) {} CONSTCD11 inline quarterly_shim::year year_quarternum_quarterday_last::year() const NOEXCEPT { return y_; } CONSTCD11 inline quarterly::quarternum year_quarternum_quarterday_last::quarternum() const NOEXCEPT { return qn_; } CONSTCD14 inline quarterly::quarterday year_quarternum_quarterday_last::quarterday() const NOEXCEPT { using start = quarterly::start; using detail::to_quarterly; switch (y_.start()) { case start::january: return to_quarterly(*this).quarterday(); case start::february: return to_quarterly(*this).quarterday(); case start::march: return to_quarterly(*this).quarterday(); case start::april: return to_quarterly(*this).quarterday(); case start::may: return to_quarterly(*this).quarterday(); case start::june: return to_quarterly(*this).quarterday(); case start::july: return to_quarterly(*this).quarterday(); case start::august: return to_quarterly(*this).quarterday(); case start::september: return to_quarterly(*this).quarterday(); case start::october: return to_quarterly(*this).quarterday(); case start::november: return to_quarterly(*this).quarterday(); case start::december: return to_quarterly(*this).quarterday(); default: detail::never_reached(); } } // year_quarternum from operator/() CONSTCD11 inline year_quarternum operator/(const year& y, const quarterly::quarternum& qn) NOEXCEPT { return {y, qn}; } CONSTCD11 inline year_quarternum operator/(const year& y, int qn) NOEXCEPT { return y / quarterly::quarternum(static_cast(qn)); } // year_quarternum_quarterday from operator/() CONSTCD11 inline year_quarternum_quarterday operator/(const year_quarternum& yqn, const quarterly::quarterday& qd) NOEXCEPT { return {yqn, qd}; } CONSTCD11 inline year_quarternum_quarterday operator/(const year_quarternum& yqn, int qd) NOEXCEPT { return yqn / quarterly::quarterday(static_cast(qd)); } // year_quarternum_quarterday_last from operator/() CONSTCD11 inline year_quarternum_quarterday_last operator/(const year_quarternum& yqn, quarterly::last_spec) NOEXCEPT { return year_quarternum_quarterday_last{yqn}; } } // namespace quarterly_shim } // namespace rquarterly } // namespace rclock #endif clock/src/iso-year-week-day.h0000644000176200001440000006036214427166604015562 0ustar liggesusers#ifndef CLOCK_ISO_YEAR_WEEK_DAY_H #define CLOCK_ISO_YEAR_WEEK_DAY_H #include "clock.h" #include "integers.h" #include "enums.h" #include "utils.h" #include "stream.h" #include "resolve.h" namespace rclock { namespace iso { namespace detail { inline std::ostringstream& stream_week(std::ostringstream& os, int week) NOEXCEPT { os << 'W'; os.fill('0'); os.flags(std::ios::dec | std::ios::right); os.width(2); os << week; return os; } inline std::ostringstream& stream_day(std::ostringstream& os, int day) NOEXCEPT { os << day; return os; } inline iso_week::year_weeknum_weekday resolve_next_day_ywd(const iso_week::year_weeknum_weekday& x) { // Only invalid on nonexistent week 53 day, rolls to first day of next iso year return (x.year() + iso_week::years{1}) / iso_week::weeknum{1} / iso_week::mon; } inline iso_week::year_weeknum_weekday resolve_previous_day_ywd(const iso_week::year_weeknum_weekday& x) { // Only invalid on nonexistent week 53 day, rolls to last day of current iso year return x.year() / iso_week::last / iso_week::sun; } inline iso_week::year_weeknum resolve_next_day_yw(const iso_week::year_weeknum& x) { // Only invalid on nonexistent week 53 day, rolls to first week of next iso year return (x.year() + iso_week::years{1}) / iso_week::weeknum{1}; } inline iso_week::year_weeknum resolve_previous_day_yw(const iso_week::year_weeknum& x) { // Only invalid on nonexistent week 53 day, rolls to last week of current iso year const iso_week::year_lastweek ylw{x.year()}; return iso_week::year_weeknum{ylw.year(), ylw.weeknum()}; } } // namespace detail class y { protected: rclock::integers year_; public: y(r_ssize size); y(const cpp11::integers& year); bool is_na(r_ssize i) const NOEXCEPT; r_ssize size() const NOEXCEPT; std::ostringstream& stream(std::ostringstream&, r_ssize i) const NOEXCEPT; void add(const date::years& x, r_ssize i) NOEXCEPT; void assign_year(const iso_week::year& x, r_ssize i) NOEXCEPT; void assign_na(r_ssize i) NOEXCEPT; iso_week::year to_year(r_ssize i) const NOEXCEPT; cpp11::writable::list to_list() const; }; class ywn : public y { protected: rclock::integers week_; public: ywn(r_ssize size); ywn(const cpp11::integers& year, const cpp11::integers& week); std::ostringstream& stream(std::ostringstream&, r_ssize i) const NOEXCEPT; void assign_weeknum(const iso_week::weeknum& x, r_ssize i) NOEXCEPT; void assign_year_weeknum(const iso_week::year_weeknum& x, r_ssize i) NOEXCEPT; void assign_na(r_ssize i) NOEXCEPT; void resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call); iso_week::year_weeknum to_year_weeknum(r_ssize i) const NOEXCEPT; cpp11::writable::list to_list() const; }; class ywnwd : public ywn { protected: rclock::integers day_; public: ywnwd(r_ssize size); ywnwd(const cpp11::integers& year, const cpp11::integers& week, const cpp11::integers& day); std::ostringstream& stream(std::ostringstream&, r_ssize i) const NOEXCEPT; void assign_weekday(const iso_week::weekday& x, r_ssize i) NOEXCEPT; void assign_year_weeknum_weekday(const iso_week::year_weeknum_weekday& x, r_ssize i) NOEXCEPT; void assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT; void assign_na(r_ssize i) NOEXCEPT; void resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call); date::sys_time to_sys_time(r_ssize i) const NOEXCEPT; iso_week::year_weeknum_weekday to_year_weeknum_weekday(r_ssize i) const NOEXCEPT; cpp11::writable::list to_list() const; }; class ywnwdh : public ywnwd { protected: rclock::integers hour_; public: ywnwdh(r_ssize size); ywnwdh(const cpp11::integers& year, const cpp11::integers& week, const cpp11::integers& day, const cpp11::integers& hour); std::ostringstream& stream(std::ostringstream&, r_ssize i) const NOEXCEPT; void assign_hour(const std::chrono::hours& x, r_ssize i) NOEXCEPT; void assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT; void assign_na(r_ssize i) NOEXCEPT; void resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call); date::sys_time to_sys_time(r_ssize i) const NOEXCEPT; cpp11::writable::list to_list() const; }; class ywnwdhm : public ywnwdh { protected: rclock::integers minute_; public: ywnwdhm(r_ssize size); ywnwdhm(const cpp11::integers& year, const cpp11::integers& week, const cpp11::integers& day, const cpp11::integers& hour, const cpp11::integers& minute); std::ostringstream& stream(std::ostringstream&, r_ssize i) const NOEXCEPT; void assign_minute(const std::chrono::minutes& x, r_ssize i) NOEXCEPT; void assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT; void assign_na(r_ssize i) NOEXCEPT; void resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call); date::sys_time to_sys_time(r_ssize i) const NOEXCEPT; cpp11::writable::list to_list() const; }; class ywnwdhms : public ywnwdhm { protected: rclock::integers second_; public: ywnwdhms(r_ssize size); ywnwdhms(const cpp11::integers& year, const cpp11::integers& week, const cpp11::integers& day, const cpp11::integers& hour, const cpp11::integers& minute, const cpp11::integers& second); std::ostringstream& stream(std::ostringstream&, r_ssize i) const NOEXCEPT; void assign_second(const std::chrono::seconds& x, r_ssize i) NOEXCEPT; void assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT; void assign_na(r_ssize i) NOEXCEPT; void resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call); date::sys_time to_sys_time(r_ssize i) const NOEXCEPT; cpp11::writable::list to_list() const; }; template class ywnwdhmss : public ywnwdhms { protected: rclock::integers subsecond_; public: ywnwdhmss(r_ssize size); ywnwdhmss(const cpp11::integers& year, const cpp11::integers& week, const cpp11::integers& day, const cpp11::integers& hour, const cpp11::integers& minute, const cpp11::integers& second, const cpp11::integers& subsecond); std::ostringstream& stream(std::ostringstream&, r_ssize i) const NOEXCEPT; void assign_subsecond(const Duration& x, r_ssize i) NOEXCEPT; void assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT; void assign_na(r_ssize i) NOEXCEPT; void resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call); date::sys_time to_sys_time(r_ssize i) const NOEXCEPT; cpp11::writable::list to_list() const; }; // Implementation // y inline y::y(r_ssize size) : year_(size) {} inline y::y(const cpp11::integers& year) : year_(rclock::integers(year)) {} inline bool y::is_na(r_ssize i) const NOEXCEPT { return year_.is_na(i); } inline r_ssize y::size() const NOEXCEPT { return year_.size(); } inline std::ostringstream& y::stream(std::ostringstream& os, r_ssize i) const NOEXCEPT { rclock::detail::stream_year(os, year_[i]); return os; } inline void y::add(const date::years& x, r_ssize i) NOEXCEPT { assign_year(to_year(i) + x, i); } inline void y::assign_year(const iso_week::year& x, r_ssize i) NOEXCEPT { year_.assign(static_cast(x), i); } inline void y::assign_na(r_ssize i) NOEXCEPT { year_.assign_na(i); } inline iso_week::year y::to_year(r_ssize i) const NOEXCEPT { return iso_week::year{year_[i]}; } inline cpp11::writable::list y::to_list() const { cpp11::writable::list out({year_.sexp()}); out.names() = {"year"}; return out; } // ywn inline ywn::ywn(r_ssize size) : y(size), week_(size) {} inline ywn::ywn(const cpp11::integers& year, const cpp11::integers& week) : y(year), week_(week) {} inline std::ostringstream& ywn::stream(std::ostringstream& os, r_ssize i) const NOEXCEPT { y::stream(os, i); os << '-'; detail::stream_week(os, week_[i]); return os; } inline void ywn::assign_weeknum(const iso_week::weeknum& x, r_ssize i) NOEXCEPT { week_.assign(static_cast(static_cast(x)), i); } inline void ywn::assign_year_weeknum(const iso_week::year_weeknum& x, r_ssize i) NOEXCEPT { assign_year(x.year(), i); assign_weeknum(x.weeknum(), i); } inline void ywn::assign_na(r_ssize i) NOEXCEPT { y::assign_na(i); week_.assign_na(i); } inline void ywn::resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call) { const iso_week::year_weeknum elt = to_year_weeknum(i); if (elt.ok()) { return; } switch (type) { case invalid::next_day: case invalid::next: { assign_year_weeknum(detail::resolve_next_day_yw(elt), i); break; } case invalid::previous_day: case invalid::previous: { assign_year_weeknum(detail::resolve_previous_day_yw(elt), i); break; } case invalid::overflow_day: case invalid::overflow: { // Overflowing invalid 2019-53 results in 2020-01 assign_year_weeknum(detail::resolve_next_day_yw(elt), i); break; } case invalid::na: { assign_na(i); break; } case invalid::error: { rclock::detail::resolve_error(i, call); } } } inline iso_week::year_weeknum ywn::to_year_weeknum(r_ssize i) const NOEXCEPT { return iso_week::year{year_[i]} / static_cast(week_[i]); } inline cpp11::writable::list ywn::to_list() const { cpp11::writable::list out({year_.sexp(), week_.sexp()}); out.names() = {"year", "week"}; return out; } // ywnwd inline ywnwd::ywnwd(r_ssize size) : ywn(size), day_(size) {} inline ywnwd::ywnwd(const cpp11::integers& year, const cpp11::integers& week, const cpp11::integers& day) : ywn(year, week), day_(day) {} inline std::ostringstream& ywnwd::stream(std::ostringstream& os, r_ssize i) const NOEXCEPT { ywn::stream(os, i); os << '-'; detail::stream_day(os, day_[i]); return os; } inline void ywnwd::assign_weekday(const iso_week::weekday& x, r_ssize i) NOEXCEPT { day_.assign(static_cast(static_cast(x)), i); } inline void ywnwd::assign_year_weeknum_weekday(const iso_week::year_weeknum_weekday& x, r_ssize i) NOEXCEPT { assign_year(x.year(), i); assign_weeknum(x.weeknum(), i); assign_weekday(x.weekday(), i); } inline void ywnwd::assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT { iso_week::year_weeknum_weekday ywnwd{x}; assign_year_weeknum_weekday(ywnwd, i); } inline void ywnwd::assign_na(r_ssize i) NOEXCEPT { ywn::assign_na(i); day_.assign_na(i); } inline void ywnwd::resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call) { const iso_week::year_weeknum_weekday elt = to_year_weeknum_weekday(i); if (elt.ok()) { return; } switch (type) { case invalid::next_day: case invalid::next: { assign_year_weeknum_weekday(detail::resolve_next_day_ywd(elt), i); break; } case invalid::previous_day: case invalid::previous: { assign_year_weeknum_weekday(detail::resolve_previous_day_ywd(elt), i); break; } case invalid::overflow_day: case invalid::overflow: { assign_year_weeknum_weekday(date::sys_days{elt}, i); break; } case invalid::na: { assign_na(i); break; } case invalid::error: { rclock::detail::resolve_error(i, call); } } } inline date::sys_time ywnwd::to_sys_time(r_ssize i) const NOEXCEPT { return date::sys_time{to_year_weeknum_weekday(i)}; } inline iso_week::year_weeknum_weekday ywnwd::to_year_weeknum_weekday(r_ssize i) const NOEXCEPT { return iso_week::year{year_[i]} / iso_week::weeknum{static_cast(week_[i])} / iso_week::weekday{static_cast(day_[i])}; } inline cpp11::writable::list ywnwd::to_list() const { cpp11::writable::list out({year_.sexp(), week_.sexp(), day_.sexp()}); out.names() = {"year", "week", "day"}; return out; } // ywnwdh inline ywnwdh::ywnwdh(r_ssize size) : ywnwd(size), hour_(size) {} inline ywnwdh::ywnwdh(const cpp11::integers& year, const cpp11::integers& week, const cpp11::integers& day, const cpp11::integers& hour) : ywnwd(year, week, day), hour_(hour) {} inline std::ostringstream& ywnwdh::stream(std::ostringstream& os, r_ssize i) const NOEXCEPT { ywnwd::stream(os, i); os << 'T'; rclock::detail::stream_hour(os, hour_[i]); return os; } inline void ywnwdh::assign_hour(const std::chrono::hours& x, r_ssize i) NOEXCEPT { hour_.assign(x.count(), i); } inline void ywnwdh::assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT { const date::sys_time day_point = date::floor(x); const std::chrono::hours hours = x - day_point; ywnwd::assign_sys_time(day_point, i); assign_hour(hours, i); } inline void ywnwdh::assign_na(r_ssize i) NOEXCEPT { ywnwd::assign_na(i); hour_.assign_na(i); } inline void ywnwdh::resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call) { const iso_week::year_weeknum_weekday elt = to_year_weeknum_weekday(i); if (elt.ok()) { return; } switch (type) { case invalid::next_day: assign_year_weeknum_weekday(detail::resolve_next_day_ywd(elt), i); break; case invalid::next: { assign_year_weeknum_weekday(detail::resolve_next_day_ywd(elt), i); assign_hour(rclock::detail::resolve_next_hour(), i); break; } case invalid::previous_day: assign_year_weeknum_weekday(detail::resolve_previous_day_ywd(elt), i); break; case invalid::previous: { assign_year_weeknum_weekday(detail::resolve_previous_day_ywd(elt), i); assign_hour(rclock::detail::resolve_previous_hour(), i); break; } case invalid::overflow_day: assign_year_weeknum_weekday(date::sys_days{elt}, i); break; case invalid::overflow: { assign_year_weeknum_weekday(date::sys_days{elt}, i); assign_hour(rclock::detail::resolve_next_hour(), i); break; } case invalid::na: { assign_na(i); break; } case invalid::error: { rclock::detail::resolve_error(i, call); } } } inline date::sys_time ywnwdh::to_sys_time(r_ssize i) const NOEXCEPT { return ywnwd::to_sys_time(i) + std::chrono::hours{hour_[i]}; } inline cpp11::writable::list ywnwdh::to_list() const { cpp11::writable::list out({year_.sexp(), week_.sexp(), day_.sexp(), hour_.sexp()}); out.names() = {"year", "week", "day", "hour"}; return out; } // ywnwdhm inline ywnwdhm::ywnwdhm(r_ssize size) : ywnwdh(size), minute_(size) {} inline ywnwdhm::ywnwdhm(const cpp11::integers& year, const cpp11::integers& week, const cpp11::integers& day, const cpp11::integers& hour, const cpp11::integers& minute) : ywnwdh(year, week, day, hour), minute_(minute) {} inline std::ostringstream& ywnwdhm::stream(std::ostringstream& os, r_ssize i) const NOEXCEPT { ywnwdh::stream(os, i); os << ':'; rclock::detail::stream_minute(os, minute_[i]); return os; } inline void ywnwdhm::assign_minute(const std::chrono::minutes& x, r_ssize i) NOEXCEPT { minute_.assign(x.count(), i); } inline void ywnwdhm::assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT { const date::sys_time hour_point = date::floor(x); const std::chrono::minutes minutes = x - hour_point; ywnwdh::assign_sys_time(hour_point, i); assign_minute(minutes, i); } inline void ywnwdhm::assign_na(r_ssize i) NOEXCEPT { ywnwdh::assign_na(i); minute_.assign_na(i); } inline void ywnwdhm::resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call) { const iso_week::year_weeknum_weekday elt = to_year_weeknum_weekday(i); if (elt.ok()) { return; } switch (type) { case invalid::next_day: assign_year_weeknum_weekday(detail::resolve_next_day_ywd(elt), i); break; case invalid::next: { assign_year_weeknum_weekday(detail::resolve_next_day_ywd(elt), i); assign_hour(rclock::detail::resolve_next_hour(), i); assign_minute(rclock::detail::resolve_next_minute(), i); break; } case invalid::previous_day: assign_year_weeknum_weekday(detail::resolve_previous_day_ywd(elt), i); break; case invalid::previous: { assign_year_weeknum_weekday(detail::resolve_previous_day_ywd(elt), i); assign_hour(rclock::detail::resolve_previous_hour(), i); assign_minute(rclock::detail::resolve_previous_minute(), i); break; } case invalid::overflow_day: assign_year_weeknum_weekday(date::sys_days{elt}, i); break; case invalid::overflow: { assign_year_weeknum_weekday(date::sys_days{elt}, i); assign_hour(rclock::detail::resolve_next_hour(), i); assign_minute(rclock::detail::resolve_next_minute(), i); break; } case invalid::na: { assign_na(i); break; } case invalid::error: { rclock::detail::resolve_error(i, call); } } } inline date::sys_time ywnwdhm::to_sys_time(r_ssize i) const NOEXCEPT { return ywnwdh::to_sys_time(i) + std::chrono::minutes{minute_[i]}; } inline cpp11::writable::list ywnwdhm::to_list() const { cpp11::writable::list out({year_.sexp(), week_.sexp(), day_.sexp(), hour_.sexp(), minute_.sexp()}); out.names() = {"year", "week", "day", "hour", "minute"}; return out; } // ywnwdhms inline ywnwdhms::ywnwdhms(r_ssize size) : ywnwdhm(size), second_(size) {} inline ywnwdhms::ywnwdhms(const cpp11::integers& year, const cpp11::integers& week, const cpp11::integers& day, const cpp11::integers& hour, const cpp11::integers& minute, const cpp11::integers& second) : ywnwdhm(year, week, day, hour, minute), second_(second) {} inline std::ostringstream& ywnwdhms::stream(std::ostringstream& os, r_ssize i) const NOEXCEPT { ywnwdhm::stream(os, i); os << ':'; rclock::detail::stream_second(os, second_[i]); return os; } inline void ywnwdhms::assign_second(const std::chrono::seconds& x, r_ssize i) NOEXCEPT { second_.assign(x.count(), i); } inline void ywnwdhms::assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT { const date::sys_time minute_point = date::floor(x); const std::chrono::seconds seconds = x - minute_point; ywnwdhm::assign_sys_time(minute_point, i); assign_second(seconds, i); } inline void ywnwdhms::assign_na(r_ssize i) NOEXCEPT { ywnwdhm::assign_na(i); second_.assign_na(i); } inline void ywnwdhms::resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call) { const iso_week::year_weeknum_weekday elt = to_year_weeknum_weekday(i); if (elt.ok()) { return; } switch (type) { case invalid::next_day: assign_year_weeknum_weekday(detail::resolve_next_day_ywd(elt), i); break; case invalid::next: { assign_year_weeknum_weekday(detail::resolve_next_day_ywd(elt), i); assign_hour(rclock::detail::resolve_next_hour(), i); assign_minute(rclock::detail::resolve_next_minute(), i); assign_second(rclock::detail::resolve_next_second(), i); break; } case invalid::previous_day: assign_year_weeknum_weekday(detail::resolve_previous_day_ywd(elt), i); break; case invalid::previous: { assign_year_weeknum_weekday(detail::resolve_previous_day_ywd(elt), i); assign_hour(rclock::detail::resolve_previous_hour(), i); assign_minute(rclock::detail::resolve_previous_minute(), i); assign_second(rclock::detail::resolve_previous_second(), i); break; } case invalid::overflow_day: assign_year_weeknum_weekday(date::sys_days{elt}, i); break; case invalid::overflow: { assign_year_weeknum_weekday(date::sys_days{elt}, i); assign_hour(rclock::detail::resolve_next_hour(), i); assign_minute(rclock::detail::resolve_next_minute(), i); assign_second(rclock::detail::resolve_next_second(), i); break; } case invalid::na: { assign_na(i); break; } case invalid::error: { rclock::detail::resolve_error(i, call); } } } inline date::sys_time ywnwdhms::to_sys_time(r_ssize i) const NOEXCEPT { return ywnwdhm::to_sys_time(i) + std::chrono::seconds{second_[i]}; } inline cpp11::writable::list ywnwdhms::to_list() const { cpp11::writable::list out({year_.sexp(), week_.sexp(), day_.sexp(), hour_.sexp(), minute_.sexp(), second_.sexp()}); out.names() = {"year", "week", "day", "hour", "minute", "second"}; return out; } // ywnwdhmss template inline ywnwdhmss::ywnwdhmss(r_ssize size) : ywnwdhms(size), subsecond_(size) {} template inline ywnwdhmss::ywnwdhmss(const cpp11::integers& year, const cpp11::integers& week, const cpp11::integers& day, const cpp11::integers& hour, const cpp11::integers& minute, const cpp11::integers& second, const cpp11::integers& subsecond) : ywnwdhms(year, week, day, hour, minute, second), subsecond_(subsecond) {} template inline std::ostringstream& ywnwdhmss::stream(std::ostringstream& os, r_ssize i) const NOEXCEPT { ywnwdhm::stream(os, i); os << ':'; rclock::detail::stream_second_and_subsecond(os, second_[i], subsecond_[i]); return os; } template inline void ywnwdhmss::assign_subsecond(const Duration& x, r_ssize i) NOEXCEPT { subsecond_.assign(x.count(), i); } template inline void ywnwdhmss::assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT { const date::sys_time second_point = date::floor(x); const Duration subseconds = x - second_point; ywnwdhms::assign_sys_time(second_point, i); assign_subsecond(subseconds, i); } template inline void ywnwdhmss::assign_na(r_ssize i) NOEXCEPT { ywnwdhms::assign_na(i); subsecond_.assign_na(i); } template inline void ywnwdhmss::resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call) { const iso_week::year_weeknum_weekday elt = to_year_weeknum_weekday(i); if (elt.ok()) { return; } switch (type) { case invalid::next_day: assign_year_weeknum_weekday(detail::resolve_next_day_ywd(elt), i); break; case invalid::next: { assign_year_weeknum_weekday(detail::resolve_next_day_ywd(elt), i); assign_hour(rclock::detail::resolve_next_hour(), i); assign_minute(rclock::detail::resolve_next_minute(), i); assign_second(rclock::detail::resolve_next_second(), i); assign_subsecond(rclock::detail::resolve_next_subsecond(), i); break; } case invalid::previous_day: assign_year_weeknum_weekday(detail::resolve_previous_day_ywd(elt), i); break; case invalid::previous: { assign_year_weeknum_weekday(detail::resolve_previous_day_ywd(elt), i); assign_hour(rclock::detail::resolve_previous_hour(), i); assign_minute(rclock::detail::resolve_previous_minute(), i); assign_second(rclock::detail::resolve_previous_second(), i); assign_subsecond(rclock::detail::resolve_previous_subsecond(), i); break; } case invalid::overflow_day: assign_year_weeknum_weekday(date::sys_days{elt}, i); break; case invalid::overflow: { assign_year_weeknum_weekday(date::sys_days{elt}, i); assign_hour(rclock::detail::resolve_next_hour(), i); assign_minute(rclock::detail::resolve_next_minute(), i); assign_second(rclock::detail::resolve_next_second(), i); assign_subsecond(rclock::detail::resolve_next_subsecond(), i); break; } case invalid::na: { assign_na(i); break; } case invalid::error: { rclock::detail::resolve_error(i, call); } } } template inline date::sys_time ywnwdhmss::to_sys_time(r_ssize i) const NOEXCEPT { return ywnwdhms::to_sys_time(i) + Duration{subsecond_[i]}; } template inline cpp11::writable::list ywnwdhmss::to_list() const { cpp11::writable::list out({year_.sexp(), week_.sexp(), day_.sexp(), hour_.sexp(), minute_.sexp(), second_.sexp(), subsecond_.sexp()}); out.names() = {"year", "week", "day", "hour", "minute", "second", "subsecond"}; return out; } } // namespace iso } // namespace rclock #endif clock/src/quarterly.h0000644000176200001440000015103614427205353014350 0ustar liggesusers#ifndef QUARTERLY_H #define QUARTERLY_H // The MIT License (MIT) // // For the original `date.h` and `iso_week.h` implementations: // Copyright (c) 2015, 2016, 2017 Howard Hinnant // For the `quarterly.h` extension: // Copyright (c) 2020 Davis Vaughan // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. #include #include namespace quarterly { using days = date::days; using years = date::years; using quarters = std::chrono::duration >>; // time_point using sys_days = date::sys_days; using local_days = date::local_days; // types struct last_spec { explicit last_spec() = default; }; enum class start: unsigned char { january = 1u, february = 2u, march = 3u, april = 4u, may = 5u, june = 6u, july = 7u, august = 8u, september = 9u, october = 10u, november = 11u, december = 12u }; class quarterday; class quarternum; template class year; template class year_quarternum; class quarternum_quarterday; class quarternum_quarterday_last; template class year_quarternum_quarterday; template class year_quarternum_quarterday_last; // date composition operators template CONSTCD11 year_quarternum operator/(const year& y, const quarternum& qn) NOEXCEPT; template CONSTCD11 year_quarternum operator/(const year& y, int qn) NOEXCEPT; CONSTCD11 quarternum_quarterday operator/(const quarternum& qn, const quarterday& qd) NOEXCEPT; CONSTCD11 quarternum_quarterday operator/(const quarternum& qn, int qd) NOEXCEPT; CONSTCD11 quarternum_quarterday operator/(int qn, const quarterday& qd) NOEXCEPT; CONSTCD11 quarternum_quarterday operator/(const quarterday& qd, const quarternum& qn) NOEXCEPT; CONSTCD11 quarternum_quarterday operator/(const quarterday& qd, int qn) NOEXCEPT; CONSTCD11 quarternum_quarterday_last operator/(const quarternum& qn, last_spec) NOEXCEPT; CONSTCD11 quarternum_quarterday_last operator/(int qn, last_spec) NOEXCEPT; CONSTCD11 quarternum_quarterday_last operator/(last_spec, const quarternum& qn) NOEXCEPT; CONSTCD11 quarternum_quarterday_last operator/(last_spec, int qn) NOEXCEPT; template CONSTCD11 year_quarternum_quarterday operator/(const year_quarternum& yqn, const quarterday& qd) NOEXCEPT; template CONSTCD11 year_quarternum_quarterday operator/(const year_quarternum& yqn, int qd) NOEXCEPT; template CONSTCD11 year_quarternum_quarterday operator/(const year& y, const quarternum_quarterday& qnqd) NOEXCEPT; template CONSTCD11 year_quarternum_quarterday operator/(const quarternum_quarterday& qnqd, const year& y) NOEXCEPT; template CONSTCD11 year_quarternum_quarterday_last operator/(const year_quarternum& yqn, last_spec) NOEXCEPT; template CONSTCD11 year_quarternum_quarterday_last operator/(const year& y, const quarternum_quarterday_last& qnqdl) NOEXCEPT; template CONSTCD11 year_quarternum_quarterday_last operator/(const quarternum_quarterday_last& qnqdl, const year& y) NOEXCEPT; // quarterday class quarterday { unsigned char qd_; public: quarterday() = default; explicit CONSTCD11 quarterday(unsigned qd) NOEXCEPT; explicit quarterday(int) = delete; CONSTCD14 quarterday& operator++() NOEXCEPT; CONSTCD14 quarterday operator++(int) NOEXCEPT; CONSTCD14 quarterday& operator--() NOEXCEPT; CONSTCD14 quarterday operator--(int) NOEXCEPT; CONSTCD14 quarterday& operator+=(const days& dd) NOEXCEPT; CONSTCD14 quarterday& operator-=(const days& dd) NOEXCEPT; CONSTCD11 explicit operator unsigned() const NOEXCEPT; CONSTCD11 bool ok() const NOEXCEPT; }; CONSTCD11 bool operator==(const quarterday& x, const quarterday& y) NOEXCEPT; CONSTCD11 bool operator!=(const quarterday& x, const quarterday& y) NOEXCEPT; CONSTCD11 bool operator< (const quarterday& x, const quarterday& y) NOEXCEPT; CONSTCD11 bool operator> (const quarterday& x, const quarterday& y) NOEXCEPT; CONSTCD11 bool operator<=(const quarterday& x, const quarterday& y) NOEXCEPT; CONSTCD11 bool operator>=(const quarterday& x, const quarterday& y) NOEXCEPT; CONSTCD11 quarterday operator+(const quarterday& x, const days& y) NOEXCEPT; CONSTCD11 quarterday operator+(const days& x, const quarterday& y) NOEXCEPT; CONSTCD11 quarterday operator-(const quarterday& x, const days& y) NOEXCEPT; CONSTCD11 days operator-(const quarterday& x, const quarterday& y) NOEXCEPT; template std::basic_ostream& operator<<(std::basic_ostream& os, const quarterday& qd); // quarternum class quarternum { unsigned char qn_; public: quarternum() = default; explicit CONSTCD11 quarternum(unsigned qn) NOEXCEPT; explicit quarternum(int) = delete; CONSTCD14 quarternum& operator++() NOEXCEPT; CONSTCD14 quarternum operator++(int) NOEXCEPT; CONSTCD14 quarternum& operator--() NOEXCEPT; CONSTCD14 quarternum operator--(int) NOEXCEPT; CONSTCD14 quarternum& operator+=(const quarters& dq) NOEXCEPT; CONSTCD14 quarternum& operator-=(const quarters& dq) NOEXCEPT; CONSTCD11 explicit operator unsigned() const NOEXCEPT; CONSTCD11 bool ok() const NOEXCEPT; }; CONSTCD11 bool operator==(const quarternum& x, const quarternum& y) NOEXCEPT; CONSTCD11 bool operator!=(const quarternum& x, const quarternum& y) NOEXCEPT; CONSTCD11 bool operator< (const quarternum& x, const quarternum& y) NOEXCEPT; CONSTCD11 bool operator> (const quarternum& x, const quarternum& y) NOEXCEPT; CONSTCD11 bool operator<=(const quarternum& x, const quarternum& y) NOEXCEPT; CONSTCD11 bool operator>=(const quarternum& x, const quarternum& y) NOEXCEPT; CONSTCD11 quarternum operator+(const quarternum& x, const quarters& y) NOEXCEPT; CONSTCD11 quarternum operator+(const quarters& x, const quarternum& y) NOEXCEPT; CONSTCD11 quarternum operator-(const quarternum& x, const quarters& y) NOEXCEPT; CONSTCD11 quarters operator-(const quarternum& x, const quarternum& y) NOEXCEPT; template std::basic_ostream& operator<<(std::basic_ostream& os, const quarternum& qn); // year template class year { short y_; public: year() = default; explicit CONSTCD11 year(int y) NOEXCEPT; CONSTCD14 year& operator++() NOEXCEPT; CONSTCD14 year operator++(int) NOEXCEPT; CONSTCD14 year& operator--() NOEXCEPT; CONSTCD14 year operator--(int) NOEXCEPT; CONSTCD14 year& operator+=(const years& dy) NOEXCEPT; CONSTCD14 year& operator-=(const years& dy) NOEXCEPT; CONSTCD11 explicit operator int() const NOEXCEPT; CONSTCD11 bool ok() const NOEXCEPT; CONSTCD11 bool is_leap() const NOEXCEPT; CONSTCD11 year min() const NOEXCEPT; CONSTCD11 year max() const NOEXCEPT; }; template CONSTCD11 bool operator==(const year& x, const year& y) NOEXCEPT; template CONSTCD11 bool operator!=(const year& x, const year& y) NOEXCEPT; template CONSTCD11 bool operator< (const year& x, const year& y) NOEXCEPT; template CONSTCD11 bool operator> (const year& x, const year& y) NOEXCEPT; template CONSTCD11 bool operator<=(const year& x, const year& y) NOEXCEPT; template CONSTCD11 bool operator>=(const year& x, const year& y) NOEXCEPT; template CONSTCD11 year operator+(const year& x, const years& y) NOEXCEPT; template CONSTCD11 year operator+(const years& x, const year& y) NOEXCEPT; template CONSTCD11 year operator-(const year& x, const years& y) NOEXCEPT; template CONSTCD11 years operator-(const year& x, const year& y) NOEXCEPT; template std::basic_ostream& operator<<(std::basic_ostream& os, const year& y); // year_quarternum template class year_quarternum { quarterly::year y_; quarterly::quarternum qn_; public: year_quarternum() = default; CONSTCD11 year_quarternum(const quarterly::year& y, const quarterly::quarternum& qn) NOEXCEPT; CONSTCD11 quarterly::year year() const NOEXCEPT; CONSTCD11 quarterly::quarternum quarternum() const NOEXCEPT; CONSTCD14 year_quarternum& operator+=(const quarters& dq) NOEXCEPT; CONSTCD14 year_quarternum& operator-=(const quarters& dq) NOEXCEPT; CONSTCD14 year_quarternum& operator+=(const years& dy) NOEXCEPT; CONSTCD14 year_quarternum& operator-=(const years& dy) NOEXCEPT; CONSTCD11 bool ok() const NOEXCEPT; }; template CONSTCD11 bool operator==(const year_quarternum& x, const year_quarternum& y) NOEXCEPT; template CONSTCD11 bool operator!=(const year_quarternum& x, const year_quarternum& y) NOEXCEPT; template CONSTCD11 bool operator< (const year_quarternum& x, const year_quarternum& y) NOEXCEPT; template CONSTCD11 bool operator> (const year_quarternum& x, const year_quarternum& y) NOEXCEPT; template CONSTCD11 bool operator<=(const year_quarternum& x, const year_quarternum& y) NOEXCEPT; template CONSTCD11 bool operator>=(const year_quarternum& x, const year_quarternum& y) NOEXCEPT; template CONSTCD14 year_quarternum operator+(const year_quarternum& yqn, const quarters& dq) NOEXCEPT; template CONSTCD14 year_quarternum operator+(const quarters& dq, const year_quarternum& yqn) NOEXCEPT; template CONSTCD14 year_quarternum operator-(const year_quarternum& yqn, const quarters& dq) NOEXCEPT; template CONSTCD11 year_quarternum operator+(const year_quarternum& yqn, const years& dy) NOEXCEPT; template CONSTCD11 year_quarternum operator+(const years& dy, const year_quarternum& yqn) NOEXCEPT; template CONSTCD11 year_quarternum operator-(const year_quarternum& yqn, const years& dy) NOEXCEPT; template CONSTCD11 quarters operator-(const year_quarternum& x, const year_quarternum& y) NOEXCEPT; template std::basic_ostream& operator<<(std::basic_ostream& os, const year_quarternum& yqn); // quarternum_quarterday class quarternum_quarterday { quarterly::quarternum qn_; quarterly::quarterday qd_; public: quarternum_quarterday() = default; CONSTCD11 quarternum_quarterday(const quarterly::quarternum& qn, const quarterly::quarterday& qd) NOEXCEPT; CONSTCD11 quarterly::quarternum quarternum() const NOEXCEPT; CONSTCD11 quarterly::quarterday quarterday() const NOEXCEPT; CONSTCD14 bool ok() const NOEXCEPT; }; CONSTCD11 bool operator==(const quarternum_quarterday& x, const quarternum_quarterday& y) NOEXCEPT; CONSTCD11 bool operator!=(const quarternum_quarterday& x, const quarternum_quarterday& y) NOEXCEPT; CONSTCD11 bool operator< (const quarternum_quarterday& x, const quarternum_quarterday& y) NOEXCEPT; CONSTCD11 bool operator> (const quarternum_quarterday& x, const quarternum_quarterday& y) NOEXCEPT; CONSTCD11 bool operator<=(const quarternum_quarterday& x, const quarternum_quarterday& y) NOEXCEPT; CONSTCD11 bool operator>=(const quarternum_quarterday& x, const quarternum_quarterday& y) NOEXCEPT; template std::basic_ostream& operator<<(std::basic_ostream& os, const quarternum_quarterday& qnqd); // quarternum_quarterday_last class quarternum_quarterday_last { quarterly::quarternum qn_; public: quarternum_quarterday_last() = default; CONSTCD11 explicit quarternum_quarterday_last(const quarterly::quarternum& qn) NOEXCEPT; CONSTCD11 quarterly::quarternum quarternum() const NOEXCEPT; CONSTCD14 bool ok() const NOEXCEPT; }; CONSTCD11 bool operator==(const quarternum_quarterday_last& x, const quarternum_quarterday_last& y) NOEXCEPT; CONSTCD11 bool operator!=(const quarternum_quarterday_last& x, const quarternum_quarterday_last& y) NOEXCEPT; CONSTCD11 bool operator< (const quarternum_quarterday_last& x, const quarternum_quarterday_last& y) NOEXCEPT; CONSTCD11 bool operator> (const quarternum_quarterday_last& x, const quarternum_quarterday_last& y) NOEXCEPT; CONSTCD11 bool operator<=(const quarternum_quarterday_last& x, const quarternum_quarterday_last& y) NOEXCEPT; CONSTCD11 bool operator>=(const quarternum_quarterday_last& x, const quarternum_quarterday_last& y) NOEXCEPT; template std::basic_ostream& operator<<(std::basic_ostream& os, const quarternum_quarterday_last& qnqdl); // year_quarternum_quarterday_last template class year_quarternum_quarterday_last { quarterly::year y_; quarterly::quarternum qn_; public: year_quarternum_quarterday_last() = default; CONSTCD11 year_quarternum_quarterday_last(const quarterly::year& y, const quarterly::quarternum& qn) NOEXCEPT; CONSTCD11 year_quarternum_quarterday_last(const quarterly::year_quarternum& yqn) NOEXCEPT; CONSTCD14 year_quarternum_quarterday_last& operator+=(const quarters& dq) NOEXCEPT; CONSTCD14 year_quarternum_quarterday_last& operator-=(const quarters& dq) NOEXCEPT; CONSTCD14 year_quarternum_quarterday_last& operator+=(const years& dy) NOEXCEPT; CONSTCD14 year_quarternum_quarterday_last& operator-=(const years& dy) NOEXCEPT; CONSTCD11 quarterly::year year() const NOEXCEPT; CONSTCD11 quarterly::quarternum quarternum() const NOEXCEPT; CONSTCD14 quarterly::quarterday quarterday() const NOEXCEPT; CONSTCD14 operator sys_days() const NOEXCEPT; CONSTCD14 explicit operator local_days() const NOEXCEPT; CONSTCD11 bool ok() const NOEXCEPT; }; template CONSTCD11 bool operator==(const year_quarternum_quarterday_last& x, const year_quarternum_quarterday_last& y) NOEXCEPT; template CONSTCD11 bool operator!=(const year_quarternum_quarterday_last& x, const year_quarternum_quarterday_last& y) NOEXCEPT; template CONSTCD11 bool operator< (const year_quarternum_quarterday_last& x, const year_quarternum_quarterday_last& y) NOEXCEPT; template CONSTCD11 bool operator> (const year_quarternum_quarterday_last& x, const year_quarternum_quarterday_last& y) NOEXCEPT; template CONSTCD11 bool operator<=(const year_quarternum_quarterday_last& x, const year_quarternum_quarterday_last& y) NOEXCEPT; template CONSTCD11 bool operator>=(const year_quarternum_quarterday_last& x, const year_quarternum_quarterday_last& y) NOEXCEPT; template CONSTCD14 year_quarternum_quarterday_last operator+(const year_quarternum_quarterday_last& yqnqdl, const quarters& dq) NOEXCEPT; template CONSTCD14 year_quarternum_quarterday_last operator+(const quarters& dq, const year_quarternum_quarterday_last& yqnqdl) NOEXCEPT; template CONSTCD14 year_quarternum_quarterday_last operator-(const year_quarternum_quarterday_last& yqnqdl, const quarters& dq) NOEXCEPT; template CONSTCD11 year_quarternum_quarterday_last operator+(const year_quarternum_quarterday_last& yqnqdl, const years& dy) NOEXCEPT; template CONSTCD11 year_quarternum_quarterday_last operator+(const years& dy, const year_quarternum_quarterday_last& yqnqdl) NOEXCEPT; template CONSTCD11 year_quarternum_quarterday_last operator-(const year_quarternum_quarterday_last& yqnqdl, const years& dy) NOEXCEPT; template std::basic_ostream& operator<<(std::basic_ostream& os, const year_quarternum_quarterday_last& yqnqdl); // year_quarternum_quarterday template class year_quarternum_quarterday { quarterly::year y_; quarterly::quarternum qn_; quarterly::quarterday qd_; public: year_quarternum_quarterday() = default; CONSTCD11 year_quarternum_quarterday(const quarterly::year& y, const quarterly::quarternum& qn, const quarterly::quarterday& qd) NOEXCEPT; CONSTCD11 year_quarternum_quarterday(const quarterly::year_quarternum& yqn, const quarterly::quarterday& qd) NOEXCEPT; CONSTCD14 year_quarternum_quarterday(const year_quarternum_quarterday_last& yqnqdl) NOEXCEPT; CONSTCD14 year_quarternum_quarterday(const sys_days& dp) NOEXCEPT; CONSTCD14 year_quarternum_quarterday(const local_days& dp) NOEXCEPT; CONSTCD14 year_quarternum_quarterday& operator+=(const quarters& dq) NOEXCEPT; CONSTCD14 year_quarternum_quarterday& operator-=(const quarters& dq) NOEXCEPT; CONSTCD14 year_quarternum_quarterday& operator+=(const years& dy) NOEXCEPT; CONSTCD14 year_quarternum_quarterday& operator-=(const years& dy) NOEXCEPT; CONSTCD11 quarterly::year year() const NOEXCEPT; CONSTCD11 quarterly::quarternum quarternum() const NOEXCEPT; CONSTCD11 quarterly::quarterday quarterday() const NOEXCEPT; CONSTCD14 operator sys_days() const NOEXCEPT; CONSTCD14 explicit operator local_days() const NOEXCEPT; CONSTCD14 bool ok() const NOEXCEPT; private: CONSTCD14 days to_days() const NOEXCEPT; static CONSTCD14 year_quarternum_quarterday from_days(const days& dd) NOEXCEPT; }; template CONSTCD11 bool operator==(const year_quarternum_quarterday& x, const year_quarternum_quarterday& y) NOEXCEPT; template CONSTCD11 bool operator!=(const year_quarternum_quarterday& x, const year_quarternum_quarterday& y) NOEXCEPT; template CONSTCD11 bool operator< (const year_quarternum_quarterday& x, const year_quarternum_quarterday& y) NOEXCEPT; template CONSTCD11 bool operator> (const year_quarternum_quarterday& x, const year_quarternum_quarterday& y) NOEXCEPT; template CONSTCD11 bool operator<=(const year_quarternum_quarterday& x, const year_quarternum_quarterday& y) NOEXCEPT; template CONSTCD11 bool operator>=(const year_quarternum_quarterday& x, const year_quarternum_quarterday& y) NOEXCEPT; template CONSTCD14 year_quarternum_quarterday operator+(const year_quarternum_quarterday& yqnqd, const quarters& dq) NOEXCEPT; template CONSTCD14 year_quarternum_quarterday operator+(const quarters& dq, const year_quarternum_quarterday& yqnqd) NOEXCEPT; template CONSTCD14 year_quarternum_quarterday operator-(const year_quarternum_quarterday& yqnqd, const quarters& dq) NOEXCEPT; template CONSTCD11 year_quarternum_quarterday operator+(const year_quarternum_quarterday& yqnqd, const years& dy) NOEXCEPT; template CONSTCD11 year_quarternum_quarterday operator+(const years& dy, const year_quarternum_quarterday& yqnqd) NOEXCEPT; template CONSTCD11 year_quarternum_quarterday operator-(const year_quarternum_quarterday& yqnqd, const years& dy) NOEXCEPT; template std::basic_ostream& operator<<(std::basic_ostream& os, const year_quarternum_quarterday& yqnqd); //----------------+ // Implementation | //----------------+ // quarterday CONSTCD11 inline quarterday::quarterday(unsigned qd) NOEXCEPT : qd_(static_cast(qd)) {} CONSTCD14 inline quarterday& quarterday::operator++() NOEXCEPT { ++qd_; return *this; } CONSTCD14 inline quarterday quarterday::operator++(int) NOEXCEPT { auto tmp(*this); ++(*this); return tmp; } CONSTCD14 inline quarterday& quarterday::operator--() NOEXCEPT { --qd_; return *this; } CONSTCD14 inline quarterday quarterday::operator--(int) NOEXCEPT { auto tmp(*this); --(*this); return tmp; } CONSTCD14 inline quarterday& quarterday::operator+=(const days& dd) NOEXCEPT { *this = *this + dd; return *this; } CONSTCD14 inline quarterday& quarterday::operator-=(const days& dd) NOEXCEPT { *this = *this - dd; return *this; } CONSTCD11 inline quarterday::operator unsigned() const NOEXCEPT { return qd_; } CONSTCD11 inline bool quarterday::ok() const NOEXCEPT { // 92 from a quarter triplet with [31, 31, 30] days return 1 <= qd_ && qd_ <= 92; } CONSTCD11 inline bool operator==(const quarterday& x, const quarterday& y) NOEXCEPT { return static_cast(x) == static_cast(y); } CONSTCD11 inline bool operator!=(const quarterday& x, const quarterday& y) NOEXCEPT { return !(x == y); } CONSTCD11 inline bool operator<(const quarterday& x, const quarterday& y) NOEXCEPT { return static_cast(x) < static_cast(y); } CONSTCD11 inline bool operator>(const quarterday& x, const quarterday& y) NOEXCEPT { return y < x; } CONSTCD11 inline bool operator<=(const quarterday& x, const quarterday& y) NOEXCEPT { return !(y < x); } CONSTCD11 inline bool operator>=(const quarterday& x, const quarterday& y) NOEXCEPT { return !(x < y); } CONSTCD11 inline days operator-(const quarterday& x, const quarterday& y) NOEXCEPT { return days{static_cast(static_cast(x) - static_cast(y))}; } CONSTCD11 inline quarterday operator+(const quarterday& x, const days& y) NOEXCEPT { return quarterday{static_cast(x) + static_cast(y.count())}; } CONSTCD11 inline quarterday operator+(const days& x, const quarterday& y) NOEXCEPT { return y + x; } CONSTCD11 inline quarterday operator-(const quarterday& x, const days& y) NOEXCEPT { return x + -y; } template inline std::basic_ostream& operator<<(std::basic_ostream& os, const quarterday& qd) { date::detail::save_ostream _(os); os.fill('0'); os.flags(std::ios::dec | std::ios::right); os.width(2); os << static_cast(qd); if (!qd.ok()) os << " is not a valid day of the quarter"; return os; } // quarternum CONSTCD11 inline quarternum::quarternum(unsigned qn) NOEXCEPT : qn_(static_cast(qn)) {} CONSTCD14 inline quarternum& quarternum::operator++() NOEXCEPT { ++qn_; return *this; } CONSTCD14 inline quarternum quarternum::operator++(int) NOEXCEPT { auto tmp(*this); ++(*this); return tmp; } CONSTCD14 inline quarternum& quarternum::operator--() NOEXCEPT { --qn_; return *this; } CONSTCD14 inline quarternum quarternum::operator--(int) NOEXCEPT { auto tmp(*this); --(*this); return tmp; } CONSTCD14 inline quarternum& quarternum::operator+=(const quarters& dq) NOEXCEPT { *this = *this + dq; return *this; } CONSTCD14 inline quarternum& quarternum::operator-=(const quarters& dq) NOEXCEPT { *this = *this - dq; return *this; } CONSTCD11 inline quarternum::operator unsigned() const NOEXCEPT { return qn_; } CONSTCD11 inline bool quarternum::ok() const NOEXCEPT { return 1 <= qn_ && qn_ <= 4; } CONSTCD11 inline bool operator==(const quarternum& x, const quarternum& y) NOEXCEPT { return static_cast(x) == static_cast(y); } CONSTCD11 inline bool operator!=(const quarternum& x, const quarternum& y) NOEXCEPT { return !(x == y); } CONSTCD11 inline bool operator<(const quarternum& x, const quarternum& y) NOEXCEPT { return static_cast(x) < static_cast(y); } CONSTCD11 inline bool operator>(const quarternum& x, const quarternum& y) NOEXCEPT { return y < x; } CONSTCD11 inline bool operator<=(const quarternum& x, const quarternum& y) NOEXCEPT { return !(y < x); } CONSTCD11 inline bool operator>=(const quarternum& x, const quarternum& y) NOEXCEPT { return !(x < y); } CONSTCD11 inline quarters operator-(const quarternum& x, const quarternum& y) NOEXCEPT { return quarters{static_cast(static_cast(x) - static_cast(y))}; } CONSTCD11 inline quarternum operator+(const quarternum& x, const quarters& y) NOEXCEPT { return quarternum{static_cast(x) + static_cast(y.count())}; } CONSTCD11 inline quarternum operator+(const quarters& x, const quarternum& y) NOEXCEPT { return y + x; } CONSTCD11 inline quarternum operator-(const quarternum& x, const quarters& y) NOEXCEPT { return x + -y; } template inline std::basic_ostream& operator<<(std::basic_ostream& os, const quarternum& qn) { date::detail::save_ostream _(os); os.flags(std::ios::dec | std::ios::right); os.width(1); os << 'Q' << static_cast(qn); if (!qn.ok()) os << " is not a valid quarter number"; return os; } // year template CONSTCD11 inline year::year(int y) NOEXCEPT : y_(static_cast(y)) {} template CONSTCD14 inline year& year::operator++() NOEXCEPT { ++y_; return *this; } template CONSTCD14 inline year year::operator++(int) NOEXCEPT { auto tmp(*this); ++(*this); return tmp; } template CONSTCD14 inline year& year::operator--() NOEXCEPT { --y_; return *this; } template CONSTCD14 inline year year::operator--(int) NOEXCEPT { auto tmp(*this); --(*this); return tmp; } template CONSTCD14 inline year& year::operator+=(const years& dy) NOEXCEPT { *this = *this + dy; return *this; } template CONSTCD14 inline year& year::operator-=(const years& dy) NOEXCEPT { *this = *this - dy; return *this; } template CONSTCD11 inline year::operator int() const NOEXCEPT { return y_; } template CONSTCD11 inline bool year::ok() const NOEXCEPT { return y_ != std::numeric_limits::min(); } template CONSTCD11 inline bool year::is_leap() const NOEXCEPT { return date::year{y_}.is_leap(); } template <> CONSTCD11 inline bool year::is_leap() const NOEXCEPT { return date::year{y_ - 1}.is_leap(); } template CONSTCD11 inline bool operator==(const year& x, const year& y) NOEXCEPT { return static_cast(x) == static_cast(y); } template CONSTCD11 inline bool operator!=(const year& x, const year& y) NOEXCEPT { return !(x == y); } template CONSTCD11 inline bool operator<(const year& x, const year& y) NOEXCEPT { return static_cast(x) < static_cast(y); } template CONSTCD11 inline bool operator>(const year& x, const year& y) NOEXCEPT { return y < x; } template CONSTCD11 inline bool operator<=(const year& x, const year& y) NOEXCEPT { return !(y < x); } template CONSTCD11 inline bool operator>=(const year& x, const year& y) NOEXCEPT { return !(x < y); } template CONSTCD11 inline years operator-(const year& x, const year& y) NOEXCEPT { return years{static_cast(x) - static_cast(y)}; } template CONSTCD11 inline year operator+(const year& x, const years& y) NOEXCEPT { return year{static_cast(x) + y.count()}; } template CONSTCD11 inline year operator+(const years& x, const year& y) NOEXCEPT { return y + x; } template CONSTCD11 inline year operator-(const year& x, const years& y) NOEXCEPT { return year{static_cast(x) - y.count()}; } template inline std::basic_ostream& operator<<(std::basic_ostream& os, const year& y) { date::detail::save_ostream _(os); os.fill('0'); os.flags(std::ios::dec | std::ios::internal); os.width(4 + (y < year{0})); os << static_cast(y); return os; } // year_quarternum template CONSTCD11 inline year_quarternum::year_quarternum(const quarterly::year& y, const quarterly::quarternum& qn) NOEXCEPT : y_(y) , qn_(qn) {} template CONSTCD11 inline bool year_quarternum::ok() const NOEXCEPT { return y_.ok() && qn_.ok(); } template CONSTCD11 inline year year_quarternum::year() const NOEXCEPT { return y_; } template CONSTCD11 inline quarternum year_quarternum::quarternum() const NOEXCEPT { return qn_; } template CONSTCD14 inline year_quarternum& year_quarternum::operator+=(const quarters& dq) NOEXCEPT { *this = *this + dq; return *this; } template CONSTCD14 inline year_quarternum& year_quarternum::operator-=(const quarters& dq) NOEXCEPT { *this = *this - dq; return *this; } template CONSTCD14 inline year_quarternum& year_quarternum::operator+=(const years& dy) NOEXCEPT { *this = *this + dy; return *this; } template CONSTCD14 inline year_quarternum& year_quarternum::operator-=(const years& dy) NOEXCEPT { *this = *this - dy; return *this; } template CONSTCD11 inline bool operator==(const year_quarternum& x, const year_quarternum& y) NOEXCEPT { return x.year() == y.year() && x.quarternum() == y.quarternum(); } template CONSTCD11 inline bool operator!=(const year_quarternum& x, const year_quarternum& y) NOEXCEPT { return !(x == y); } template CONSTCD11 inline bool operator<(const year_quarternum& x, const year_quarternum& y) NOEXCEPT { return x.year() < y.year() ? true : (x.year() > y.year() ? false : (x.quarternum() < y.quarternum())); } template CONSTCD11 inline bool operator>(const year_quarternum& x, const year_quarternum& y) NOEXCEPT { return y < x; } template CONSTCD11 inline bool operator<=(const year_quarternum& x, const year_quarternum& y) NOEXCEPT { return !(y < x); } template CONSTCD11 inline bool operator>=(const year_quarternum& x, const year_quarternum& y) NOEXCEPT { return !(x < y); } template CONSTCD14 inline year_quarternum operator+(const year_quarternum& yqn, const quarters& dq) NOEXCEPT { auto dqi = static_cast(static_cast(yqn.quarternum())) - 1 + dq.count(); auto dy = (dqi >= 0 ? dqi : dqi - 3) / 4; dqi = dqi - dy * 4 + 1; return {(yqn.year() + years(dy)), quarternum(static_cast(dqi))}; } template CONSTCD14 inline year_quarternum operator+(const quarters& dq, const year_quarternum& yqn) NOEXCEPT { return yqn + dq; } template CONSTCD14 inline year_quarternum operator-(const year_quarternum& yqn, const quarters& dq) NOEXCEPT { return yqn + -dq; } template CONSTCD11 inline year_quarternum operator+(const year_quarternum& yqn, const years& dy) NOEXCEPT { return {(yqn.year() + dy), yqn.quarternum()}; } template CONSTCD11 inline year_quarternum operator+(const years& dy, const year_quarternum& yqn) NOEXCEPT { return yqn + dy; } template CONSTCD11 inline year_quarternum operator-(const year_quarternum& yqn, const years& dy) NOEXCEPT { return yqn + -dy; } template CONSTCD11 inline quarters operator-(const year_quarternum& x, const year_quarternum& y) NOEXCEPT { return (x.year() - y.year()) + (x.quarternum() - y.quarternum()); } template inline std::basic_ostream& operator<<(std::basic_ostream& os, const year_quarternum& yqn) { return os << yqn.year() << '-' << yqn.quarternum(); } // quarternum_quarterday CONSTCD11 inline quarternum_quarterday::quarternum_quarterday(const quarterly::quarternum& qn, const quarterly::quarterday& qd) NOEXCEPT : qn_(qn) , qd_(qd) {} CONSTCD11 inline quarternum quarternum_quarterday::quarternum() const NOEXCEPT { return qn_; } CONSTCD11 inline quarterday quarternum_quarterday::quarterday() const NOEXCEPT { return qd_; } CONSTCD14 inline bool quarternum_quarterday::ok() const NOEXCEPT { return qn_.ok() && qd_.ok(); } CONSTCD11 inline bool operator==(const quarternum_quarterday& x, const quarternum_quarterday& y) NOEXCEPT { return x.quarternum() == y.quarternum() && x.quarterday() == y.quarterday(); } CONSTCD11 inline bool operator!=(const quarternum_quarterday& x, const quarternum_quarterday& y) NOEXCEPT { return !(x == y); } CONSTCD11 inline bool operator<(const quarternum_quarterday& x, const quarternum_quarterday& y) NOEXCEPT { return x.quarternum() < y.quarternum() ? true : (x.quarternum() > y.quarternum() ? false : (static_cast(x.quarterday()) < static_cast(y.quarterday()))); } CONSTCD11 inline bool operator>(const quarternum_quarterday& x, const quarternum_quarterday& y) NOEXCEPT { return y < x; } CONSTCD11 inline bool operator<=(const quarternum_quarterday& x, const quarternum_quarterday& y) NOEXCEPT { return !(y < x); } CONSTCD11 inline bool operator>=(const quarternum_quarterday& x, const quarternum_quarterday& y) NOEXCEPT { return !(x < y); } template inline std::basic_ostream& operator<<(std::basic_ostream& os, const quarternum_quarterday& qnqd) { return os << qnqd.quarternum() << '-' << qnqd.quarterday(); } // quarternum_quarterday_last CONSTCD11 inline quarternum_quarterday_last::quarternum_quarterday_last(const quarterly::quarternum& qn) NOEXCEPT : qn_(qn) {} CONSTCD11 inline quarternum quarternum_quarterday_last::quarternum() const NOEXCEPT { return qn_; } CONSTCD14 inline bool quarternum_quarterday_last::ok() const NOEXCEPT { return qn_.ok(); } CONSTCD11 inline bool operator==(const quarternum_quarterday_last& x, const quarternum_quarterday_last& y) NOEXCEPT { return x.quarternum() == y.quarternum(); } CONSTCD11 inline bool operator!=(const quarternum_quarterday_last& x, const quarternum_quarterday_last& y) NOEXCEPT { return !(x == y); } CONSTCD11 inline bool operator<(const quarternum_quarterday_last& x, const quarternum_quarterday_last& y) NOEXCEPT { return static_cast(x.quarternum()) < static_cast(y.quarternum()); } CONSTCD11 inline bool operator>(const quarternum_quarterday_last& x, const quarternum_quarterday_last& y) NOEXCEPT { return y < x; } CONSTCD11 inline bool operator<=(const quarternum_quarterday_last& x, const quarternum_quarterday_last& y) NOEXCEPT { return !(y < x); } CONSTCD11 inline bool operator>=(const quarternum_quarterday_last& x, const quarternum_quarterday_last& y) NOEXCEPT { return !(x < y); } template inline std::basic_ostream& operator<<(std::basic_ostream& os, const quarternum_quarterday_last& qnqd) { return os << qnqd.quarternum() << "-last"; } // year_quarternum_quarterday_last template CONSTCD11 inline year_quarternum_quarterday_last::year_quarternum_quarterday_last(const quarterly::year& y, const quarterly::quarternum& qn) NOEXCEPT : y_(y) , qn_(qn) {} template CONSTCD11 inline year_quarternum_quarterday_last::year_quarternum_quarterday_last(const quarterly::year_quarternum& yqn) NOEXCEPT : y_(yqn.year()) , qn_(yqn.quarternum()) {} template CONSTCD14 inline year_quarternum_quarterday_last& year_quarternum_quarterday_last::operator+=(const quarters& dq) NOEXCEPT { *this = *this + dq; return *this; } template CONSTCD14 inline year_quarternum_quarterday_last& year_quarternum_quarterday_last::operator-=(const quarters& dq) NOEXCEPT { *this = *this - dq; return *this; } template CONSTCD14 inline year_quarternum_quarterday_last& year_quarternum_quarterday_last::operator+=(const years& dy) NOEXCEPT { *this = *this + dy; return *this; } template CONSTCD14 inline year_quarternum_quarterday_last& year_quarternum_quarterday_last::operator-=(const years& dy) NOEXCEPT { *this = *this - dy; return *this; } template CONSTCD11 inline year year_quarternum_quarterday_last::year() const NOEXCEPT { return y_; } template CONSTCD11 inline quarternum year_quarternum_quarterday_last::quarternum() const NOEXCEPT { return qn_; } template CONSTCD14 inline quarterday year_quarternum_quarterday_last::quarterday() const NOEXCEPT { CONSTDATA unsigned s = static_cast(S) - 1; // Use an unsigned array rather than a quarterday array to avoid a // gcc warning with Rtools36's old gcc. Issue #43. CONSTDATA unsigned quarterdays[] = { // [12, 1, 2] [1, 2, 3] [2, 3, 4] 90u, 90u, 89u, // [3, 4, 5] [4, 5, 6] [5, 6, 7] 92u, 91u, 92u, // [6, 7, 8] [7, 8, 9] [8, 9, 10] 92u, 92u, 92u, // [9, 10, 11] [10, 11, 12] [11, 12, 1] 91u, 92u, 92u }; const unsigned quarternum = static_cast(qn_) - 1; // Remap [Jan -> Dec] to [Dec -> Jan] to group quarters with February unsigned key = (s == 12) ? 0 : s + 1; key = key + 3 * quarternum; if (key > 11) { key -= 12; } if (!qn_.ok()) { // If `!qn_.ok()`, don't index into `quarterdays[]` to avoid OOB error. // Instead, return the minimum of the possible "last day of quarter" // days, as `year_month_day_last::day()` does. return quarterly::quarterday{quarterdays[2]}; } else if (key <= 2 && y_.is_leap()) { return quarterly::quarterday{quarterdays[key]} + quarterly::days{1u}; } else { return quarterly::quarterday{quarterdays[key]}; } } template CONSTCD14 inline year_quarternum_quarterday_last::operator sys_days() const NOEXCEPT { return sys_days(year_quarternum_quarterday{year(), quarternum(), quarterday()}); } template CONSTCD14 inline year_quarternum_quarterday_last::operator local_days() const NOEXCEPT { return local_days(year_quarternum_quarterday{year(), quarternum(), quarterday()}); } template CONSTCD11 inline bool year_quarternum_quarterday_last::ok() const NOEXCEPT { return y_.ok() && qn_.ok(); } template CONSTCD11 inline bool operator==(const year_quarternum_quarterday_last& x, const year_quarternum_quarterday_last& y) NOEXCEPT { return x.year() == y.year() && x.quarternum() == y.quarternum(); } template CONSTCD11 inline bool operator!=(const year_quarternum_quarterday_last& x, const year_quarternum_quarterday_last& y) NOEXCEPT { return !(x == y); } template CONSTCD11 inline bool operator<(const year_quarternum_quarterday_last& x, const year_quarternum_quarterday_last& y) NOEXCEPT { return x.year() < y.year() ? true : (x.year() > y.year() ? false : x.quarternum() < y.quarternum()); } template CONSTCD11 inline bool operator>(const year_quarternum_quarterday_last& x, const year_quarternum_quarterday_last& y) NOEXCEPT { return y < x; } template CONSTCD11 inline bool operator<=(const year_quarternum_quarterday_last& x, const year_quarternum_quarterday_last& y) NOEXCEPT { return !(y < x); } template CONSTCD11 inline bool operator>=(const year_quarternum_quarterday_last& x, const year_quarternum_quarterday_last& y) NOEXCEPT { return !(x < y); } template CONSTCD14 inline year_quarternum_quarterday_last operator+(const year_quarternum_quarterday_last& yqnqdl, const quarters& dq) NOEXCEPT { return {year_quarternum{yqnqdl.year(), yqnqdl.quarternum()} + dq}; } template CONSTCD14 inline year_quarternum_quarterday_last operator+(const quarters& dq, const year_quarternum_quarterday_last& yqnqdl) NOEXCEPT { return yqnqdl + dq; } template CONSTCD14 inline year_quarternum_quarterday_last operator-(const year_quarternum_quarterday_last& yqnqdl, const quarters& dq) NOEXCEPT { return yqnqdl + -dq; } template CONSTCD11 inline year_quarternum_quarterday_last operator+(const year_quarternum_quarterday_last& yqnqdl, const years& dy) NOEXCEPT { return {yqnqdl.year() + dy, yqnqdl.quarternum()}; } template CONSTCD11 inline year_quarternum_quarterday_last operator+(const years& dy, const year_quarternum_quarterday_last& yqnqdl) NOEXCEPT { return yqnqdl + dy; } template CONSTCD11 inline year_quarternum_quarterday_last operator-(const year_quarternum_quarterday_last& yqnqdl, const years& dy) NOEXCEPT { return yqnqdl + -dy; } template inline std::basic_ostream& operator<<(std::basic_ostream& os, const year_quarternum_quarterday_last& yqnqdl) { return os << yqnqdl.year() << "-" << yqnqdl.quarternum() << "-last"; } // year_quarternum_quarterday template CONSTCD11 inline year_quarternum_quarterday::year_quarternum_quarterday(const quarterly::year& y, const quarterly::quarternum& qn, const quarterly::quarterday& qd) NOEXCEPT : y_(y) , qn_(qn) , qd_(qd) {} template CONSTCD11 inline year_quarternum_quarterday::year_quarternum_quarterday(const quarterly::year_quarternum& yqn, const quarterly::quarterday& qd) NOEXCEPT : y_(yqn.year()) , qn_(yqn.quarternum()) , qd_(qd) {} template CONSTCD14 inline year_quarternum_quarterday::year_quarternum_quarterday(const year_quarternum_quarterday_last& yqnqdl) NOEXCEPT : y_(yqnqdl.year()) , qn_(yqnqdl.quarternum()) , qd_(yqnqdl.quarterday()) {} template CONSTCD14 inline year_quarternum_quarterday::year_quarternum_quarterday(const sys_days& dp) NOEXCEPT : year_quarternum_quarterday(from_days(dp.time_since_epoch())) {} template CONSTCD14 inline year_quarternum_quarterday::year_quarternum_quarterday(const local_days& dp) NOEXCEPT : year_quarternum_quarterday(from_days(dp.time_since_epoch())) {} template CONSTCD14 inline year_quarternum_quarterday& year_quarternum_quarterday::operator+=(const quarters& dq) NOEXCEPT { *this = *this + dq; return *this; } template CONSTCD14 inline year_quarternum_quarterday& year_quarternum_quarterday::operator-=(const quarters& dq) NOEXCEPT { *this = *this - dq; return *this; } template CONSTCD14 inline year_quarternum_quarterday& year_quarternum_quarterday::operator+=(const years& dy) NOEXCEPT { *this = *this + dy; return *this; } template CONSTCD14 inline year_quarternum_quarterday& year_quarternum_quarterday::operator-=(const years& dy) NOEXCEPT { *this = *this - dy; return *this; } template CONSTCD11 inline year year_quarternum_quarterday::year() const NOEXCEPT { return y_; } template CONSTCD11 inline quarternum year_quarternum_quarterday::quarternum() const NOEXCEPT { return qn_; } template CONSTCD11 inline quarterday year_quarternum_quarterday::quarterday() const NOEXCEPT { return qd_; } template CONSTCD14 inline year_quarternum_quarterday::operator sys_days() const NOEXCEPT { return date::sys_days{to_days()}; } template CONSTCD14 inline year_quarternum_quarterday::operator local_days() const NOEXCEPT { return date::local_days{to_days()}; } template CONSTCD14 inline bool year_quarternum_quarterday::ok() const NOEXCEPT { return y_.ok() && qd_.ok() && quarterly::quarterday{1u} <= qd_ && qd_ <= year_quarternum_quarterday_last{y_, qn_}.quarterday(); } template CONSTCD14 inline days year_quarternum_quarterday::to_days() const NOEXCEPT { CONSTDATA unsigned char s = static_cast(S) - 1; const unsigned quarternum = static_cast(qn_) - 1; const unsigned quarterly_month = 3 * quarternum; int year = static_cast(y_); unsigned civil_month = s + quarterly_month; if (civil_month > 11) { civil_month -= 12; } else if (s != 0) { --year; } const unsigned quarterday = static_cast(qd_) - 1; const date::year_month_day quarter_start{ date::year{year} / date::month{civil_month + 1} / date::day{1} }; const date::sys_days quarter_days{ date::sys_days{quarter_start} + date::days{quarterday} }; return quarter_days.time_since_epoch(); } template CONSTCD14 inline year_quarternum_quarterday year_quarternum_quarterday::from_days(const days& dd) NOEXCEPT { CONSTDATA unsigned char s = static_cast(S) - 1; const date::sys_days dp{dd}; const date::year_month_day ymd{dp}; const unsigned civil_month = static_cast(ymd.month()) - 1; int year = static_cast(ymd.year()); int quarterly_month = static_cast(civil_month) - static_cast(s); if (quarterly_month < 0) { quarterly_month += 12; } else if (s != 0) { ++year; } const unsigned quarternum = static_cast(quarterly_month) / 3; const quarterly::year_quarternum_quarterday quarter_start{ quarterly::year{year} / quarterly::quarternum{quarternum + 1} / quarterly::quarterday{1u} }; // Find day of quarter as number of days from start of quarter const days days = dp - sys_days{quarter_start}; const quarterly::quarterday quarterday{static_cast(days.count()) + 1}; return quarter_start.year() / quarter_start.quarternum() / quarterday; } template CONSTCD11 inline bool operator==(const year_quarternum_quarterday& x, const year_quarternum_quarterday& y) NOEXCEPT { return x.year() == y.year() && x.quarternum() == y.quarternum() && x.quarterday() == y.quarterday(); } template CONSTCD11 inline bool operator!=(const year_quarternum_quarterday& x, const year_quarternum_quarterday& y) NOEXCEPT { return !(x == y); } template CONSTCD11 inline bool operator<(const year_quarternum_quarterday& x, const year_quarternum_quarterday& y) NOEXCEPT { return x.year() < y.year() ? true : (x.year() > y.year() ? false : (x.quarternum() < y.quarternum() ? true : (x.quarternum() > y.quarternum() ? false : (x.quarterday() < y.quarterday())))); } template CONSTCD11 inline bool operator>(const year_quarternum_quarterday& x, const year_quarternum_quarterday& y) NOEXCEPT { return y < x; } template CONSTCD11 inline bool operator<=(const year_quarternum_quarterday& x, const year_quarternum_quarterday& y) NOEXCEPT { return !(y < x); } template CONSTCD11 inline bool operator>=(const year_quarternum_quarterday& x, const year_quarternum_quarterday& y) NOEXCEPT { return !(x < y); } template CONSTCD14 inline year_quarternum_quarterday operator+(const year_quarternum_quarterday& yqnqd, const quarters& dq) NOEXCEPT { return {year_quarternum{yqnqd.year(), yqnqd.quarternum()} + dq, yqnqd.quarterday()}; } template CONSTCD14 inline year_quarternum_quarterday operator+(const quarters& dq, const year_quarternum_quarterday& yqnqd) NOEXCEPT { return yqnqd + dq; } template CONSTCD14 inline year_quarternum_quarterday operator-(const year_quarternum_quarterday& yqnqd, const quarters& dq) NOEXCEPT { return yqnqd + -dq; } template CONSTCD11 inline year_quarternum_quarterday operator+(const year_quarternum_quarterday& yqnqd, const years& dy) NOEXCEPT { return {yqnqd.year() + dy, yqnqd.quarternum(), yqnqd.quarterday()}; } template CONSTCD11 inline year_quarternum_quarterday operator+(const years& dy, const year_quarternum_quarterday& yqnqd) NOEXCEPT { return yqnqd + dy; } template CONSTCD11 inline year_quarternum_quarterday operator-(const year_quarternum_quarterday& yqnqd, const years& dy) NOEXCEPT { return yqnqd + -dy; } template inline std::basic_ostream& operator<<(std::basic_ostream& os, const year_quarternum_quarterday& yqnqd) { return os << yqnqd.year() << '-' << yqnqd.quarternum() << '-' << yqnqd.quarterday(); } // literals #if !defined(_MSC_VER) || (_MSC_VER >= 1900) inline namespace literals { #endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) CONSTDATA quarterly::last_spec last{}; #if !defined(_MSC_VER) || (_MSC_VER >= 1900) } // inline namespace literals #endif // year_quarternum from operator/() template CONSTCD11 inline year_quarternum operator/(const year& y, const quarternum& qn) NOEXCEPT { return {y, qn}; } template CONSTCD11 inline year_quarternum operator/(const year& y, int qn) NOEXCEPT { return y / quarternum(static_cast(qn)); } // quarternum_quarterday from operator/() CONSTCD11 inline quarternum_quarterday operator/(const quarternum& qn, const quarterday& qd) NOEXCEPT { return {qn, qd}; } CONSTCD11 inline quarternum_quarterday operator/(const quarternum& qn, int qd) NOEXCEPT { return qn / quarterday(static_cast(qd)); } CONSTCD11 inline quarternum_quarterday operator/(int qn, const quarterday& qd) NOEXCEPT { return quarternum(static_cast(qn)) / qd; } CONSTCD11 inline quarternum_quarterday operator/(const quarterday& qd, const quarternum& qn) NOEXCEPT { return qn / qd; } CONSTCD11 inline quarternum_quarterday operator/(const quarterday& qd, int qn) NOEXCEPT { return qn / qd; } // quarternum_quarterday_last from operator/() CONSTCD11 inline quarternum_quarterday_last operator/(const quarternum& qn, last_spec) NOEXCEPT { return quarternum_quarterday_last{qn}; } CONSTCD11 inline quarternum_quarterday_last operator/(int qn, last_spec) NOEXCEPT { return quarternum(static_cast(qn))/last; } CONSTCD11 inline quarternum_quarterday_last operator/(last_spec, const quarternum& qn) NOEXCEPT { return qn / last; } CONSTCD11 inline quarternum_quarterday_last operator/(last_spec, int qn) NOEXCEPT { return qn / last; } // year_quarternum_quarterday from operator/() template CONSTCD11 inline year_quarternum_quarterday operator/(const year_quarternum& yqn, const quarterday& qd) NOEXCEPT { return {yqn, qd}; } template CONSTCD11 inline year_quarternum_quarterday operator/(const year_quarternum& yqn, int qd) NOEXCEPT { return yqn / quarterday(static_cast(qd)); } template CONSTCD11 inline year_quarternum_quarterday operator/(const year& y, const quarternum_quarterday& qnqd) NOEXCEPT { return y / qnqd.quarternum() / qnqd.quarterday(); } template CONSTCD11 inline year_quarternum_quarterday operator/(const quarternum_quarterday& qnqd, const year& y) NOEXCEPT { return y / qnqd; } // year_quarternum_quarterday_last from operator/() template CONSTCD11 inline year_quarternum_quarterday_last operator/(const year_quarternum& ynq, last_spec) NOEXCEPT { return year_quarternum_quarterday_last{ynq}; } template CONSTCD11 inline year_quarternum_quarterday_last operator/(const year& y, const quarternum_quarterday_last& qnqdl) NOEXCEPT { return {y, qnqdl}; } template CONSTCD11 inline year_quarternum_quarterday_last operator/(const quarternum_quarterday_last& qnqdl, const year& y) NOEXCEPT { return y / qnqdl; } } // namespace quarterly #endif // QUARTERLY_H clock/src/gregorian-year-month-day.h0000644000176200001440000005341314427166513017135 0ustar liggesusers#ifndef CLOCK_GREGORIAN_YEAR_MONTH_DAY_H #define CLOCK_GREGORIAN_YEAR_MONTH_DAY_H #include "clock.h" #include "integers.h" #include "enums.h" #include "utils.h" #include "stream.h" #include "resolve.h" namespace rclock { namespace gregorian { namespace detail { inline date::year_month_day resolve_next_day_ymd(const date::year_month_day& x) { return ((x.year() / x.month()) + date::months(1)) / date::day(1); } inline date::year_month_day resolve_previous_day_ymd(const date::year_month_day& x) { return x.year() / x.month() / date::last; } } // namespace detail class y { protected: rclock::integers year_; public: y(r_ssize size); y(const cpp11::integers& year); bool is_na(r_ssize i) const NOEXCEPT; r_ssize size() const NOEXCEPT; std::ostringstream& stream(std::ostringstream&, r_ssize i) const NOEXCEPT; void add(const date::years& x, r_ssize i) NOEXCEPT; void assign_year(const date::year& x, r_ssize i) NOEXCEPT; void assign_na(r_ssize i) NOEXCEPT; date::year to_year(r_ssize i) const NOEXCEPT; cpp11::writable::list to_list() const; }; class ym : public y { protected: rclock::integers month_; public: ym(r_ssize size); ym(const cpp11::integers& year, const cpp11::integers& month); std::ostringstream& stream(std::ostringstream&, r_ssize i) const NOEXCEPT; void add(const date::months& x, r_ssize i) NOEXCEPT; void assign_month(const date::month& x, r_ssize i) NOEXCEPT; void assign_year_month(const date::year_month& x, r_ssize i) NOEXCEPT; void assign_na(r_ssize i) NOEXCEPT; date::year_month to_year_month(r_ssize i) const NOEXCEPT; cpp11::writable::list to_list() const; }; class ymd : public ym { protected: rclock::integers day_; public: ymd(r_ssize size); ymd(const cpp11::integers& year, const cpp11::integers& month, const cpp11::integers& day); std::ostringstream& stream(std::ostringstream&, r_ssize i) const NOEXCEPT; void assign_day(const date::day& x, r_ssize i) NOEXCEPT; void assign_year_month_day(const date::year_month_day& x, r_ssize i) NOEXCEPT; void assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT; void assign_na(r_ssize i) NOEXCEPT; void resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call); date::sys_time to_sys_time(r_ssize i) const NOEXCEPT; date::year_month_day to_year_month_day(r_ssize i) const NOEXCEPT; cpp11::writable::list to_list() const; }; class ymdh : public ymd { protected: rclock::integers hour_; public: ymdh(r_ssize size); ymdh(const cpp11::integers& year, const cpp11::integers& month, const cpp11::integers& day, const cpp11::integers& hour); std::ostringstream& stream(std::ostringstream&, r_ssize i) const NOEXCEPT; void assign_hour(const std::chrono::hours& x, r_ssize i) NOEXCEPT; void assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT; void assign_na(r_ssize i) NOEXCEPT; void resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call); date::sys_time to_sys_time(r_ssize i) const NOEXCEPT; cpp11::writable::list to_list() const; }; class ymdhm : public ymdh { protected: rclock::integers minute_; public: ymdhm(r_ssize size); ymdhm(const cpp11::integers& year, const cpp11::integers& month, const cpp11::integers& day, const cpp11::integers& hour, const cpp11::integers& minute); std::ostringstream& stream(std::ostringstream&, r_ssize i) const NOEXCEPT; void assign_minute(const std::chrono::minutes& x, r_ssize i) NOEXCEPT; void assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT; void assign_na(r_ssize i) NOEXCEPT; void resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call); date::sys_time to_sys_time(r_ssize i) const NOEXCEPT; cpp11::writable::list to_list() const; }; class ymdhms : public ymdhm { protected: rclock::integers second_; public: ymdhms(r_ssize size); ymdhms(const cpp11::integers& year, const cpp11::integers& month, const cpp11::integers& day, const cpp11::integers& hour, const cpp11::integers& minute, const cpp11::integers& second); std::ostringstream& stream(std::ostringstream&, r_ssize i) const NOEXCEPT; void assign_second(const std::chrono::seconds& x, r_ssize i) NOEXCEPT; void assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT; void assign_na(r_ssize i) NOEXCEPT; void resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call); date::sys_time to_sys_time(r_ssize i) const NOEXCEPT; cpp11::writable::list to_list() const; }; template class ymdhmss : public ymdhms { protected: rclock::integers subsecond_; public: ymdhmss(r_ssize size); ymdhmss(const cpp11::integers& year, const cpp11::integers& month, const cpp11::integers& day, const cpp11::integers& hour, const cpp11::integers& minute, const cpp11::integers& second, const cpp11::integers& subsecond); std::ostringstream& stream(std::ostringstream&, r_ssize i) const NOEXCEPT; void assign_subsecond(const Duration& x, r_ssize i) NOEXCEPT; void assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT; void assign_na(r_ssize i) NOEXCEPT; void resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call); date::sys_time to_sys_time(r_ssize i) const NOEXCEPT; cpp11::writable::list to_list() const; using duration = Duration; }; // Implementation // y inline y::y(r_ssize size) : year_(size) {} inline y::y(const cpp11::integers& year) : year_(rclock::integers(year)) {} inline bool y::is_na(r_ssize i) const NOEXCEPT { return year_.is_na(i); } inline r_ssize y::size() const NOEXCEPT { return year_.size(); } inline std::ostringstream& y::stream(std::ostringstream& os, r_ssize i) const NOEXCEPT { rclock::detail::stream_year(os, year_[i]); return os; } inline void y::add(const date::years& x, r_ssize i) NOEXCEPT { assign_year(to_year(i) + x, i); } inline void y::assign_year(const date::year& x, r_ssize i) NOEXCEPT { year_.assign(static_cast(x), i); } inline void y::assign_na(r_ssize i) NOEXCEPT { year_.assign_na(i); } inline date::year y::to_year(r_ssize i) const NOEXCEPT { return date::year{year_[i]}; } inline cpp11::writable::list y::to_list() const { cpp11::writable::list out({year_.sexp()}); out.names() = {"year"}; return out; } // ym inline ym::ym(r_ssize size) : y(size), month_(size) {} inline ym::ym(const cpp11::integers& year, const cpp11::integers& month) : y(year), month_(month) {} inline std::ostringstream& ym::stream(std::ostringstream& os, r_ssize i) const NOEXCEPT { y::stream(os, i); os << '-'; rclock::detail::stream_month(os, month_[i]); return os; } inline void ym::add(const date::months& x, r_ssize i) NOEXCEPT { assign_year_month(to_year_month(i) + x, i); } inline void ym::assign_month(const date::month& x, r_ssize i) NOEXCEPT { month_.assign(static_cast(static_cast(x)), i); } inline void ym::assign_year_month(const date::year_month& x, r_ssize i) NOEXCEPT { assign_year(x.year(), i); assign_month(x.month(), i); } inline void ym::assign_na(r_ssize i) NOEXCEPT { y::assign_na(i); month_.assign_na(i); } inline date::year_month ym::to_year_month(r_ssize i) const NOEXCEPT { return date::year{year_[i]} / static_cast(month_[i]); } inline cpp11::writable::list ym::to_list() const { cpp11::writable::list out({year_.sexp(), month_.sexp()}); out.names() = {"year", "month"}; return out; } // ymd inline ymd::ymd(r_ssize size) : ym(size), day_(size) {} inline ymd::ymd(const cpp11::integers& year, const cpp11::integers& month, const cpp11::integers& day) : ym(year, month), day_(day) {} inline std::ostringstream& ymd::stream(std::ostringstream& os, r_ssize i) const NOEXCEPT { ym::stream(os, i); os << '-'; rclock::detail::stream_day(os, day_[i]); return os; } inline void ymd::assign_day(const date::day& x, r_ssize i) NOEXCEPT { day_.assign(static_cast(static_cast(x)), i); } inline void ymd::assign_year_month_day(const date::year_month_day& x, r_ssize i) NOEXCEPT { assign_year(x.year(), i); assign_month(x.month(), i); assign_day(x.day(), i); } inline void ymd::assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT { date::year_month_day ymd{x}; assign_year_month_day(ymd, i); } inline void ymd::assign_na(r_ssize i) NOEXCEPT { ym::assign_na(i); day_.assign_na(i); } inline void ymd::resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call) { const date::year_month_day elt = to_year_month_day(i); if (elt.ok()) { return; } switch (type) { case invalid::next_day: case invalid::next: { assign_year_month_day(detail::resolve_next_day_ymd(elt), i); break; } case invalid::previous_day: case invalid::previous: { assign_day(detail::resolve_previous_day_ymd(elt).day(), i); break; } case invalid::overflow_day: case invalid::overflow: { assign_year_month_day(date::sys_days{elt}, i); break; } case invalid::na: { assign_na(i); break; } case invalid::error: { rclock::detail::resolve_error(i, call); } } } inline date::sys_time ymd::to_sys_time(r_ssize i) const NOEXCEPT { return date::sys_time{to_year_month_day(i)}; } inline date::year_month_day ymd::to_year_month_day(r_ssize i) const NOEXCEPT { return date::year{year_[i]} / static_cast(month_[i]) / static_cast(day_[i]); } inline cpp11::writable::list ymd::to_list() const { cpp11::writable::list out({year_.sexp(), month_.sexp(), day_.sexp()}); out.names() = {"year", "month", "day"}; return out; } // ymdh inline ymdh::ymdh(r_ssize size) : ymd(size), hour_(size) {} inline ymdh::ymdh(const cpp11::integers& year, const cpp11::integers& month, const cpp11::integers& day, const cpp11::integers& hour) : ymd(year, month, day), hour_(hour) {} inline std::ostringstream& ymdh::stream(std::ostringstream& os, r_ssize i) const NOEXCEPT { ymd::stream(os, i); os << 'T'; rclock::detail::stream_hour(os, hour_[i]); return os; } inline void ymdh::assign_hour(const std::chrono::hours& x, r_ssize i) NOEXCEPT { hour_.assign(x.count(), i); } inline void ymdh::assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT { const date::sys_time day_point = date::floor(x); const std::chrono::hours hours = x - day_point; ymd::assign_sys_time(day_point, i); assign_hour(hours, i); } inline void ymdh::assign_na(r_ssize i) NOEXCEPT { ymd::assign_na(i); hour_.assign_na(i); } inline void ymdh::resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call) { const date::year_month_day elt = to_year_month_day(i); if (elt.ok()) { return; } switch (type) { case invalid::next_day: assign_year_month_day(detail::resolve_next_day_ymd(elt), i); break; case invalid::next: { assign_year_month_day(detail::resolve_next_day_ymd(elt), i); assign_hour(rclock::detail::resolve_next_hour(), i); break; } case invalid::previous_day: assign_day(detail::resolve_previous_day_ymd(elt).day(), i); break; case invalid::previous: { assign_day(detail::resolve_previous_day_ymd(elt).day(), i); assign_hour(rclock::detail::resolve_previous_hour(), i); break; } case invalid::overflow_day: assign_year_month_day(date::sys_days{elt}, i); break; case invalid::overflow: { assign_year_month_day(date::sys_days{elt}, i); assign_hour(rclock::detail::resolve_next_hour(), i); break; } case invalid::na: { assign_na(i); break; } case invalid::error: { rclock::detail::resolve_error(i, call); } } } inline date::sys_time ymdh::to_sys_time(r_ssize i) const NOEXCEPT { return ymd::to_sys_time(i) + std::chrono::hours{hour_[i]}; } inline cpp11::writable::list ymdh::to_list() const { cpp11::writable::list out({year_.sexp(), month_.sexp(), day_.sexp(), hour_.sexp()}); out.names() = {"year", "month", "day", "hour"}; return out; } // ymdhm inline ymdhm::ymdhm(r_ssize size) : ymdh(size), minute_(size) {} inline ymdhm::ymdhm(const cpp11::integers& year, const cpp11::integers& month, const cpp11::integers& day, const cpp11::integers& hour, const cpp11::integers& minute) : ymdh(year, month, day, hour), minute_(minute) {} inline std::ostringstream& ymdhm::stream(std::ostringstream& os, r_ssize i) const NOEXCEPT { ymdh::stream(os, i); os << ':'; rclock::detail::stream_minute(os, minute_[i]); return os; } inline void ymdhm::assign_minute(const std::chrono::minutes& x, r_ssize i) NOEXCEPT { minute_.assign(x.count(), i); } inline void ymdhm::assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT { const date::sys_time hour_point = date::floor(x); const std::chrono::minutes minutes = x - hour_point; ymdh::assign_sys_time(hour_point, i); assign_minute(minutes, i); } inline void ymdhm::assign_na(r_ssize i) NOEXCEPT { ymdh::assign_na(i); minute_.assign_na(i); } inline void ymdhm::resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call) { const date::year_month_day elt = to_year_month_day(i); if (elt.ok()) { return; } switch (type) { case invalid::next_day: assign_year_month_day(detail::resolve_next_day_ymd(elt), i); break; case invalid::next: { assign_year_month_day(detail::resolve_next_day_ymd(elt), i); assign_hour(rclock::detail::resolve_next_hour(), i); assign_minute(rclock::detail::resolve_next_minute(), i); break; } case invalid::previous_day: assign_day(detail::resolve_previous_day_ymd(elt).day(), i); break; case invalid::previous: { assign_day(detail::resolve_previous_day_ymd(elt).day(), i); assign_hour(rclock::detail::resolve_previous_hour(), i); assign_minute(rclock::detail::resolve_previous_minute(), i); break; } case invalid::overflow_day: assign_year_month_day(date::sys_days{elt}, i); break; case invalid::overflow: { assign_year_month_day(date::sys_days{elt}, i); assign_hour(rclock::detail::resolve_next_hour(), i); assign_minute(rclock::detail::resolve_next_minute(), i); break; } case invalid::na: { assign_na(i); break; } case invalid::error: { rclock::detail::resolve_error(i, call); } } } inline date::sys_time ymdhm::to_sys_time(r_ssize i) const NOEXCEPT { return ymdh::to_sys_time(i) + std::chrono::minutes{minute_[i]}; } inline cpp11::writable::list ymdhm::to_list() const { cpp11::writable::list out({year_.sexp(), month_.sexp(), day_.sexp(), hour_.sexp(), minute_.sexp()}); out.names() = {"year", "month", "day", "hour", "minute"}; return out; } // ymdhms inline ymdhms::ymdhms(r_ssize size) : ymdhm(size), second_(size) {} inline ymdhms::ymdhms(const cpp11::integers& year, const cpp11::integers& month, const cpp11::integers& day, const cpp11::integers& hour, const cpp11::integers& minute, const cpp11::integers& second) : ymdhm(year, month, day, hour, minute), second_(second) {} inline std::ostringstream& ymdhms::stream(std::ostringstream& os, r_ssize i) const NOEXCEPT { ymdhm::stream(os, i); os << ':'; rclock::detail::stream_second(os, second_[i]); return os; } inline void ymdhms::assign_second(const std::chrono::seconds& x, r_ssize i) NOEXCEPT { second_.assign(x.count(), i); } inline void ymdhms::assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT { const date::sys_time minute_point = date::floor(x); const std::chrono::seconds seconds = x - minute_point; ymdhm::assign_sys_time(minute_point, i); assign_second(seconds, i); } inline void ymdhms::assign_na(r_ssize i) NOEXCEPT { ymdhm::assign_na(i); second_.assign_na(i); } inline void ymdhms::resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call) { const date::year_month_day elt = to_year_month_day(i); if (elt.ok()) { return; } switch (type) { case invalid::next_day: assign_year_month_day(detail::resolve_next_day_ymd(elt), i); break; case invalid::next: { assign_year_month_day(detail::resolve_next_day_ymd(elt), i); assign_hour(rclock::detail::resolve_next_hour(), i); assign_minute(rclock::detail::resolve_next_minute(), i); assign_second(rclock::detail::resolve_next_second(), i); break; } case invalid::previous_day: assign_day(detail::resolve_previous_day_ymd(elt).day(), i); break; case invalid::previous: { assign_day(detail::resolve_previous_day_ymd(elt).day(), i); assign_hour(rclock::detail::resolve_previous_hour(), i); assign_minute(rclock::detail::resolve_previous_minute(), i); assign_second(rclock::detail::resolve_previous_second(), i); break; } case invalid::overflow_day: assign_year_month_day(date::sys_days{elt}, i); break; case invalid::overflow: { assign_year_month_day(date::sys_days{elt}, i); assign_hour(rclock::detail::resolve_next_hour(), i); assign_minute(rclock::detail::resolve_next_minute(), i); assign_second(rclock::detail::resolve_next_second(), i); break; } case invalid::na: { assign_na(i); break; } case invalid::error: { rclock::detail::resolve_error(i, call); } } } inline date::sys_time ymdhms::to_sys_time(r_ssize i) const NOEXCEPT { return ymdhm::to_sys_time(i) + std::chrono::seconds{second_[i]}; } inline cpp11::writable::list ymdhms::to_list() const { cpp11::writable::list out({year_.sexp(), month_.sexp(), day_.sexp(), hour_.sexp(), minute_.sexp(), second_.sexp()}); out.names() = {"year", "month", "day", "hour", "minute", "second"}; return out; } // ymdhmss template inline ymdhmss::ymdhmss(r_ssize size) : ymdhms(size), subsecond_(size) {} template inline ymdhmss::ymdhmss(const cpp11::integers& year, const cpp11::integers& month, const cpp11::integers& day, const cpp11::integers& hour, const cpp11::integers& minute, const cpp11::integers& second, const cpp11::integers& subsecond) : ymdhms(year, month, day, hour, minute, second), subsecond_(subsecond) {} template inline std::ostringstream& ymdhmss::stream(std::ostringstream& os, r_ssize i) const NOEXCEPT { ymdhm::stream(os, i); os << ':'; rclock::detail::stream_second_and_subsecond(os, second_[i], subsecond_[i]); return os; } template inline void ymdhmss::assign_subsecond(const Duration& x, r_ssize i) NOEXCEPT { subsecond_.assign(x.count(), i); } template inline void ymdhmss::assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT { const date::sys_time second_point = date::floor(x); const Duration subseconds = x - second_point; ymdhms::assign_sys_time(second_point, i); assign_subsecond(subseconds, i); } template inline void ymdhmss::assign_na(r_ssize i) NOEXCEPT { ymdhms::assign_na(i); subsecond_.assign_na(i); } template inline void ymdhmss::resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call) { const date::year_month_day elt = to_year_month_day(i); if (elt.ok()) { return; } switch (type) { case invalid::next_day: assign_year_month_day(detail::resolve_next_day_ymd(elt), i); break; case invalid::next: { assign_year_month_day(detail::resolve_next_day_ymd(elt), i); assign_hour(rclock::detail::resolve_next_hour(), i); assign_minute(rclock::detail::resolve_next_minute(), i); assign_second(rclock::detail::resolve_next_second(), i); assign_subsecond(rclock::detail::resolve_next_subsecond(), i); break; } case invalid::previous_day: assign_day(detail::resolve_previous_day_ymd(elt).day(), i); break; case invalid::previous: { assign_day(detail::resolve_previous_day_ymd(elt).day(), i); assign_hour(rclock::detail::resolve_previous_hour(), i); assign_minute(rclock::detail::resolve_previous_minute(), i); assign_second(rclock::detail::resolve_previous_second(), i); assign_subsecond(rclock::detail::resolve_previous_subsecond(), i); break; } case invalid::overflow_day: assign_year_month_day(date::sys_days{elt}, i); break; case invalid::overflow: { assign_year_month_day(date::sys_days{elt}, i); assign_hour(rclock::detail::resolve_next_hour(), i); assign_minute(rclock::detail::resolve_next_minute(), i); assign_second(rclock::detail::resolve_next_second(), i); assign_subsecond(rclock::detail::resolve_next_subsecond(), i); break; } case invalid::na: { assign_na(i); break; } case invalid::error: { rclock::detail::resolve_error(i, call); } } } template inline date::sys_time ymdhmss::to_sys_time(r_ssize i) const NOEXCEPT { return ymdhms::to_sys_time(i) + Duration{subsecond_[i]}; } template inline cpp11::writable::list ymdhmss::to_list() const { cpp11::writable::list out({year_.sexp(), month_.sexp(), day_.sexp(), hour_.sexp(), minute_.sexp(), second_.sexp(), subsecond_.sexp()}); out.names() = {"year", "month", "day", "hour", "minute", "second", "subsecond"}; return out; } } // namespace gregorian } // namespace rclock #endif clock/src/ordinal.h0000644000176200001440000005157714040275170013754 0ustar liggesusers#ifndef ORDINAL_H #define ORDINAL_H // The MIT License (MIT) // // For the original `date.h` implementation: // Copyright (c) 2015, 2016, 2017 Howard Hinnant // For the `ordinal.h` extension: // Copyright (c) 2021 Davis Vaughan // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // // Our apologies. When the previous paragraph was written, lowercase had not yet // been invented (that would involve another several millennia of evolution). // We did not mean to shout. #include namespace ordinal { //-----------+ // Interface | //-----------+ // durations using days = date::days; using years = date::years; // time_point using sys_days = date::sys_days; using local_days = date::local_days; // types struct last_day { explicit last_day() = default; }; class yearday; class year; class year_yearday; class year_yearday_last; // date composition operators CONSTCD11 year_yearday operator/(const year& y, const yearday& yd) NOEXCEPT; CONSTCD11 year_yearday operator/(const year& y, int yd) NOEXCEPT; CONSTCD11 year_yearday_last operator/(const year& y, last_day yd) NOEXCEPT; // yearday class yearday { unsigned short yd_; public: yearday() = default; explicit CONSTCD11 yearday(unsigned d) NOEXCEPT; CONSTCD14 yearday& operator++() NOEXCEPT; CONSTCD14 yearday operator++(int) NOEXCEPT; CONSTCD14 yearday& operator--() NOEXCEPT; CONSTCD14 yearday operator--(int) NOEXCEPT; CONSTCD14 yearday& operator+=(const days& d) NOEXCEPT; CONSTCD14 yearday& operator-=(const days& d) NOEXCEPT; CONSTCD11 explicit operator unsigned() const NOEXCEPT; CONSTCD11 bool ok() const NOEXCEPT; }; CONSTCD11 bool operator==(const yearday& x, const yearday& y) NOEXCEPT; CONSTCD11 bool operator!=(const yearday& x, const yearday& y) NOEXCEPT; CONSTCD11 bool operator< (const yearday& x, const yearday& y) NOEXCEPT; CONSTCD11 bool operator> (const yearday& x, const yearday& y) NOEXCEPT; CONSTCD11 bool operator<=(const yearday& x, const yearday& y) NOEXCEPT; CONSTCD11 bool operator>=(const yearday& x, const yearday& y) NOEXCEPT; CONSTCD11 yearday operator+(const yearday& x, const days& y) NOEXCEPT; CONSTCD11 yearday operator+(const days& x, const yearday& y) NOEXCEPT; CONSTCD11 yearday operator-(const yearday& x, const days& y) NOEXCEPT; CONSTCD11 days operator-(const yearday& x, const yearday& y) NOEXCEPT; template std::basic_ostream& operator<<(std::basic_ostream& os, const yearday& yd); // year class year { short y_; public: year() = default; explicit CONSTCD11 year(int y) NOEXCEPT; CONSTCD14 year& operator++() NOEXCEPT; CONSTCD14 year operator++(int) NOEXCEPT; CONSTCD14 year& operator--() NOEXCEPT; CONSTCD14 year operator--(int) NOEXCEPT; CONSTCD14 year& operator+=(const years& y) NOEXCEPT; CONSTCD14 year& operator-=(const years& y) NOEXCEPT; CONSTCD11 year operator-() const NOEXCEPT; CONSTCD11 year operator+() const NOEXCEPT; CONSTCD11 bool is_leap() const NOEXCEPT; CONSTCD11 explicit operator int() const NOEXCEPT; CONSTCD11 bool ok() const NOEXCEPT; static CONSTCD11 year min() NOEXCEPT { return year{-32767}; } static CONSTCD11 year max() NOEXCEPT { return year{32767}; } }; CONSTCD11 bool operator==(const year& x, const year& y) NOEXCEPT; CONSTCD11 bool operator!=(const year& x, const year& y) NOEXCEPT; CONSTCD11 bool operator< (const year& x, const year& y) NOEXCEPT; CONSTCD11 bool operator> (const year& x, const year& y) NOEXCEPT; CONSTCD11 bool operator<=(const year& x, const year& y) NOEXCEPT; CONSTCD11 bool operator>=(const year& x, const year& y) NOEXCEPT; CONSTCD11 year operator+(const year& x, const years& y) NOEXCEPT; CONSTCD11 year operator+(const years& x, const year& y) NOEXCEPT; CONSTCD11 year operator-(const year& x, const years& y) NOEXCEPT; CONSTCD11 years operator-(const year& x, const year& y) NOEXCEPT; template std::basic_ostream& operator<<(std::basic_ostream& os, const year& y); // class year_yearday class year_yearday { ordinal::year y_; ordinal::yearday yd_; public: year_yearday() = default; CONSTCD11 year_yearday(const ordinal::year& y, const ordinal::yearday& yd) NOEXCEPT; CONSTCD14 year_yearday(const year_yearday_last& yydl) NOEXCEPT; CONSTCD14 year_yearday(sys_days dp) NOEXCEPT; CONSTCD14 explicit year_yearday(local_days dp) NOEXCEPT; CONSTCD14 year_yearday& operator+=(const years& y) NOEXCEPT; CONSTCD14 year_yearday& operator-=(const years& y) NOEXCEPT; CONSTCD11 ordinal::year year() const NOEXCEPT; CONSTCD11 ordinal::yearday yearday() const NOEXCEPT; CONSTCD14 operator sys_days() const NOEXCEPT; CONSTCD14 explicit operator local_days() const NOEXCEPT; CONSTCD14 bool ok() const NOEXCEPT; private: static CONSTCD14 year_yearday from_days(const days& dp) NOEXCEPT; CONSTCD14 days to_days() const NOEXCEPT; }; CONSTCD11 bool operator==(const year_yearday& x, const year_yearday& y) NOEXCEPT; CONSTCD11 bool operator!=(const year_yearday& x, const year_yearday& y) NOEXCEPT; CONSTCD11 bool operator< (const year_yearday& x, const year_yearday& y) NOEXCEPT; CONSTCD11 bool operator> (const year_yearday& x, const year_yearday& y) NOEXCEPT; CONSTCD11 bool operator<=(const year_yearday& x, const year_yearday& y) NOEXCEPT; CONSTCD11 bool operator>=(const year_yearday& x, const year_yearday& y) NOEXCEPT; CONSTCD11 year_yearday operator+(const year_yearday& yyd, const years& dy) NOEXCEPT; CONSTCD11 year_yearday operator+(const years& dy, const year_yearday& yyd) NOEXCEPT; CONSTCD11 year_yearday operator-(const year_yearday& yyd, const years& dy) NOEXCEPT; template std::basic_ostream& operator<<(std::basic_ostream& os, const year_yearday& yyd); // year_yearday_last class year_yearday_last { ordinal::year y_; public: CONSTCD11 year_yearday_last(const ordinal::year& y) NOEXCEPT; CONSTCD14 year_yearday_last& operator+=(const years& y) NOEXCEPT; CONSTCD14 year_yearday_last& operator-=(const years& y) NOEXCEPT; CONSTCD11 ordinal::year year() const NOEXCEPT; CONSTCD14 ordinal::yearday yearday() const NOEXCEPT; CONSTCD14 operator sys_days() const NOEXCEPT; CONSTCD14 explicit operator local_days() const NOEXCEPT; CONSTCD11 bool ok() const NOEXCEPT; }; CONSTCD11 bool operator==(const year_yearday_last& x, const year_yearday_last& y) NOEXCEPT; CONSTCD11 bool operator!=(const year_yearday_last& x, const year_yearday_last& y) NOEXCEPT; CONSTCD11 bool operator< (const year_yearday_last& x, const year_yearday_last& y) NOEXCEPT; CONSTCD11 bool operator> (const year_yearday_last& x, const year_yearday_last& y) NOEXCEPT; CONSTCD11 bool operator<=(const year_yearday_last& x, const year_yearday_last& y) NOEXCEPT; CONSTCD11 bool operator>=(const year_yearday_last& x, const year_yearday_last& y) NOEXCEPT; CONSTCD11 year_yearday_last operator+(const year_yearday_last& yydl, const years& dy) NOEXCEPT; CONSTCD11 year_yearday_last operator+(const years& dy, const year_yearday_last& yydl) NOEXCEPT; CONSTCD11 year_yearday_last operator-(const year_yearday_last& yydl, const years& dy) NOEXCEPT; template std::basic_ostream& operator<<(std::basic_ostream& os, const year_yearday_last& yydl); #if !defined(_MSC_VER) || (_MSC_VER >= 1900) inline namespace literals { CONSTCD11 ordinal::yearday operator "" _yd(unsigned long long yd) NOEXCEPT; CONSTCD11 ordinal::year operator "" _y(unsigned long long y) NOEXCEPT; } // inline namespace literals #endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) //----------------+ // Implementation | //----------------+ // yearday CONSTCD11 inline yearday::yearday(unsigned yd) NOEXCEPT : yd_(static_cast(yd)) {} CONSTCD14 inline yearday& yearday::operator++() NOEXCEPT { ++yd_; return *this; } CONSTCD14 inline yearday yearday::operator++(int) NOEXCEPT { auto tmp(*this); ++(*this); return tmp; } CONSTCD14 inline yearday& yearday::operator--() NOEXCEPT { --yd_; return *this; } CONSTCD14 inline yearday yearday::operator--(int) NOEXCEPT { auto tmp(*this); --(*this); return tmp; } CONSTCD14 inline yearday& yearday::operator+=(const days& d) NOEXCEPT { *this = *this + d; return *this; } CONSTCD14 inline yearday& yearday::operator-=(const days& d) NOEXCEPT { *this = *this - d; return *this; } CONSTCD11 inline yearday::operator unsigned() const NOEXCEPT { return yd_; } CONSTCD11 inline bool yearday::ok() const NOEXCEPT { return 1 <= yd_ && yd_ <= 366; } CONSTCD11 inline bool operator==(const yearday& x, const yearday& y) NOEXCEPT { return static_cast(x) == static_cast(y); } CONSTCD11 inline bool operator!=(const yearday& x, const yearday& y) NOEXCEPT { return !(x == y); } CONSTCD11 inline bool operator<(const yearday& x, const yearday& y) NOEXCEPT { return static_cast(x) < static_cast(y); } CONSTCD11 inline bool operator>(const yearday& x, const yearday& y) NOEXCEPT { return y < x; } CONSTCD11 inline bool operator<=(const yearday& x, const yearday& y) NOEXCEPT { return !(y < x); } CONSTCD11 inline bool operator>=(const yearday& x, const yearday& y) NOEXCEPT { return !(x < y); } CONSTCD11 inline days operator-(const yearday& x, const yearday& y) NOEXCEPT { return days{static_cast(static_cast(x) - static_cast(y))}; } CONSTCD11 inline yearday operator+(const yearday& x, const days& y) NOEXCEPT { return yearday{static_cast(x) + static_cast(y.count())}; } CONSTCD11 inline yearday operator+(const days& x, const yearday& y) NOEXCEPT { return y + x; } CONSTCD11 inline yearday operator-(const yearday& x, const days& y) NOEXCEPT { return x + -y; } template inline std::basic_ostream& operator<<(std::basic_ostream& os, const yearday& yd) { date::detail::save_ostream _(os); os.fill('0'); os.flags(std::ios::dec | std::ios::right); os.width(3); os << static_cast(yd); if (!yd.ok()) os << " is not a valid day of the year"; return os; } // year CONSTCD11 inline year::year(int y) NOEXCEPT : y_(static_cast(y)) {} CONSTCD14 inline year& year::operator++() NOEXCEPT { ++y_; return *this; } CONSTCD14 inline year year::operator++(int) NOEXCEPT { auto tmp(*this); ++(*this); return tmp; } CONSTCD14 inline year& year::operator--() NOEXCEPT { --y_; return *this; } CONSTCD14 inline year year::operator--(int) NOEXCEPT { auto tmp(*this); --(*this); return tmp; } CONSTCD14 inline year& year::operator+=(const years& y) NOEXCEPT { *this = *this + y; return *this; } CONSTCD14 inline year& year::operator-=(const years& y) NOEXCEPT { *this = *this - y; return *this; } CONSTCD11 inline year year::operator-() const NOEXCEPT { return year{-y_}; } CONSTCD11 inline year year::operator+() const NOEXCEPT { return *this; } CONSTCD11 inline bool year::is_leap() const NOEXCEPT { return date::year{y_}.is_leap(); } CONSTCD11 inline year::operator int() const NOEXCEPT { return y_; } CONSTCD11 inline bool year::ok() const NOEXCEPT { return y_ != std::numeric_limits::min(); } CONSTCD11 inline bool operator==(const year& x, const year& y) NOEXCEPT { return static_cast(x) == static_cast(y); } CONSTCD11 inline bool operator!=(const year& x, const year& y) NOEXCEPT { return !(x == y); } CONSTCD11 inline bool operator<(const year& x, const year& y) NOEXCEPT { return static_cast(x) < static_cast(y); } CONSTCD11 inline bool operator>(const year& x, const year& y) NOEXCEPT { return y < x; } CONSTCD11 inline bool operator<=(const year& x, const year& y) NOEXCEPT { return !(y < x); } CONSTCD11 inline bool operator>=(const year& x, const year& y) NOEXCEPT { return !(x < y); } CONSTCD11 inline years operator-(const year& x, const year& y) NOEXCEPT { return years{static_cast(x) - static_cast(y)}; } CONSTCD11 inline year operator+(const year& x, const years& y) NOEXCEPT { return year{static_cast(x) + y.count()}; } CONSTCD11 inline year operator+(const years& x, const year& y) NOEXCEPT { return y + x; } CONSTCD11 inline year operator-(const year& x, const years& y) NOEXCEPT { return year{static_cast(x) - y.count()}; } template inline std::basic_ostream& operator<<(std::basic_ostream& os, const year& y) { date::detail::save_ostream _(os); os.fill('0'); os.flags(std::ios::dec | std::ios::internal); os.width(4 + (y < year{0})); os.imbue(std::locale::classic()); os << static_cast(y); if (!y.ok()) os << " is not a valid year"; return os; } #if !defined(_MSC_VER) || (_MSC_VER >= 1900) inline namespace literals { CONSTCD11 inline ordinal::yearday operator "" _yd(unsigned long long yd) NOEXCEPT { return ordinal::yearday{static_cast(yd)}; } CONSTCD11 inline ordinal::year operator "" _y(unsigned long long y) NOEXCEPT { return ordinal::year(static_cast(y)); } #endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) CONSTDATA ordinal::last_day last{}; #if !defined(_MSC_VER) || (_MSC_VER >= 1900) } // inline namespace literals #endif // year_yearday_last CONSTCD11 inline year_yearday_last::year_yearday_last(const ordinal::year& y) NOEXCEPT : y_(y) {} CONSTCD14 inline year_yearday_last& year_yearday_last::operator+=(const years& y) NOEXCEPT { *this = *this + y; return *this; } CONSTCD14 inline year_yearday_last& year_yearday_last::operator-=(const years& y) NOEXCEPT { *this = *this - y; return *this; } CONSTCD11 inline year year_yearday_last::year() const NOEXCEPT { return y_; } CONSTCD14 inline yearday year_yearday_last::yearday() const NOEXCEPT { return y_.is_leap() ? ordinal::yearday{366u} : ordinal::yearday{365u}; } CONSTCD14 inline year_yearday_last::operator sys_days() const NOEXCEPT { return sys_days(year()/yearday()); } CONSTCD14 inline year_yearday_last::operator local_days() const NOEXCEPT { return local_days(year()/yearday()); } CONSTCD11 inline bool year_yearday_last::ok() const NOEXCEPT { return y_.ok(); } CONSTCD11 inline bool operator==(const year_yearday_last& x, const year_yearday_last& y) NOEXCEPT { return x.year() == y.year(); } CONSTCD11 inline bool operator!=(const year_yearday_last& x, const year_yearday_last& y) NOEXCEPT { return !(x == y); } CONSTCD11 inline bool operator<(const year_yearday_last& x, const year_yearday_last& y) NOEXCEPT { return x.year() < y.year(); } CONSTCD11 inline bool operator>(const year_yearday_last& x, const year_yearday_last& y) NOEXCEPT { return y < x; } CONSTCD11 inline bool operator<=(const year_yearday_last& x, const year_yearday_last& y) NOEXCEPT { return !(y < x); } CONSTCD11 inline bool operator>=(const year_yearday_last& x, const year_yearday_last& y) NOEXCEPT { return !(x < y); } template inline std::basic_ostream& operator<<(std::basic_ostream& os, const year_yearday_last& yydl) { return os << yydl.year() << '-' << "last"; } CONSTCD11 inline year_yearday_last operator+(const year_yearday_last& yydl, const years& dy) NOEXCEPT { return year_yearday_last{yydl.year() + dy}; } CONSTCD11 inline year_yearday_last operator+(const years& dy, const year_yearday_last& yydl) NOEXCEPT { return yydl + dy; } CONSTCD11 inline year_yearday_last operator-(const year_yearday_last& yydl, const years& dy) NOEXCEPT { return yydl + (-dy); } // year_yearday CONSTCD11 inline year_yearday::year_yearday(const ordinal::year& y, const ordinal::yearday& yd) NOEXCEPT : y_(y) , yd_(yd) {} CONSTCD14 inline year_yearday::year_yearday(const year_yearday_last& yydl) NOEXCEPT : y_(yydl.year()) , yd_(yydl.yearday()) {} CONSTCD14 inline year_yearday::year_yearday(sys_days dp) NOEXCEPT : year_yearday(from_days(dp.time_since_epoch())) {} CONSTCD14 inline year_yearday::year_yearday(local_days dp) NOEXCEPT : year_yearday(from_days(dp.time_since_epoch())) {} CONSTCD11 inline year year_yearday::year() const NOEXCEPT { return y_; } CONSTCD11 inline yearday year_yearday::yearday() const NOEXCEPT { return yd_; } CONSTCD14 inline year_yearday& year_yearday::operator+=(const years& y) NOEXCEPT { *this = *this + y; return *this; } CONSTCD14 inline year_yearday& year_yearday::operator-=(const years& y) NOEXCEPT { *this = *this - y; return *this; } CONSTCD14 inline days year_yearday::to_days() const NOEXCEPT { const int y = static_cast(y_); const unsigned yd = static_cast(yd_); return sys_days{date::year{y} / 1 / 1}.time_since_epoch() + days{yd - 1}; } CONSTCD14 inline year_yearday::operator sys_days() const NOEXCEPT { return sys_days{to_days()}; } CONSTCD14 inline year_yearday::operator local_days() const NOEXCEPT { return local_days{to_days()}; } CONSTCD14 inline bool year_yearday::ok() const NOEXCEPT { if (!y_.ok()) { return false; } return ordinal::yearday{1} <= yd_ && yd_ <= (y_ / last).yearday(); } CONSTCD11 inline bool operator==(const year_yearday& x, const year_yearday& y) NOEXCEPT { return x.year() == y.year() && x.yearday() == y.yearday(); } CONSTCD11 inline bool operator!=(const year_yearday& x, const year_yearday& y) NOEXCEPT { return !(x == y); } CONSTCD11 inline bool operator<(const year_yearday& x, const year_yearday& y) NOEXCEPT { return x.year() < y.year() ? true : (x.year() > y.year() ? false : (x.yearday() < y.yearday())); } CONSTCD11 inline bool operator>(const year_yearday& x, const year_yearday& y) NOEXCEPT { return y < x; } CONSTCD11 inline bool operator<=(const year_yearday& x, const year_yearday& y) NOEXCEPT { return !(y < x); } CONSTCD11 inline bool operator>=(const year_yearday& x, const year_yearday& y) NOEXCEPT { return !(x < y); } template inline std::basic_ostream& operator<<(std::basic_ostream& os, const year_yearday& yyd) { date::detail::save_ostream _(os); os.fill('0'); os.flags(std::ios::dec | std::ios::right); os.imbue(std::locale::classic()); os << yyd.year() << '-'; os << yyd.yearday(); if (!yyd.ok()) os << " is not a valid date"; return os; } CONSTCD14 inline year_yearday year_yearday::from_days(const days& dp) NOEXCEPT { CONSTDATA unsigned days_before_month[] = { 0u, 31u, 59u, 90u, 120u, 151u, 181u, 212u, 243u, 273u, 304u, 334u }; const date::year_month_day ymd{sys_days{dp}}; const ordinal::year y{static_cast(ymd.year())}; const unsigned m = static_cast(ymd.month()); const unsigned d = static_cast(ymd.day()); const ordinal::yearday yd{days_before_month[m - 1] + (m > 2 && y.is_leap()) + d}; return {y, yd}; } CONSTCD11 inline year_yearday operator+(const year_yearday& yyd, const years& dy) NOEXCEPT { return (yyd.year() + dy) / yyd.yearday(); } CONSTCD11 inline year_yearday operator+(const years& dy, const year_yearday& yyd) NOEXCEPT { return yyd + dy; } CONSTCD11 inline year_yearday operator-(const year_yearday& yyd, const years& dy) NOEXCEPT { return yyd + (-dy); } // year_yearday from operator/() CONSTCD11 year_yearday operator/(const year& y, const yearday& yd) NOEXCEPT { return {y, yd}; } CONSTCD11 year_yearday operator/(const year& y, int yd) NOEXCEPT { return y / yearday(static_cast(yd)); } // year_yearday_last from operator/() CONSTCD11 inline year_yearday_last operator/(const year& y, last_day) NOEXCEPT { return year_yearday_last{y}; } } // namespace ordinal #endif // ORDINAL_H clock/src/gregorian-year-month-weekday.cpp0000644000176200001440000003627514427233200020337 0ustar liggesusers#include "gregorian-year-month-weekday.h" #include "calendar.h" #include "duration.h" #include "enums.h" #include "get.h" #include "rcrd.h" // ----------------------------------------------------------------------------- [[cpp11::register]] SEXP new_year_month_weekday_from_fields(SEXP fields, const cpp11::integers& precision_int, SEXP names) { const enum precision precision_val = parse_precision(precision_int); const r_ssize n_fields = Rf_xlength(fields); r_ssize n; switch (precision_val) { case precision::year: n = 1; break; case precision::month: n = 2; break; case precision::day: n = 4; break; case precision::hour: n = 5; break; case precision::minute: n = 6; break; case precision::second: n = 7; break; case precision::millisecond: n = 8; break; case precision::microsecond: n = 8; break; case precision::nanosecond: n = 8; break; default: never_reached("new_year_month_weekday_from_fields"); } if (n != n_fields) { clock_abort("With the given precision, `fields` must have length %i, not %i.", n, n_fields); } SEXP out = PROTECT(new_clock_rcrd_from_fields(fields, names, classes_year_month_weekday)); Rf_setAttrib(out, syms_precision, precision_int); UNPROTECT(1); return out; } // ----------------------------------------------------------------------------- [[cpp11::register]] SEXP year_month_weekday_restore(SEXP x, SEXP to) { SEXP precision = Rf_getAttrib(to, syms_precision); SEXP out = PROTECT(clock_rcrd_restore(x, to, classes_year_month_weekday)); Rf_setAttrib(out, syms_precision, precision); UNPROTECT(1); return out; } // ----------------------------------------------------------------------------- [[cpp11::register]] cpp11::writable::strings format_year_month_weekday_cpp(cpp11::list_of fields, const cpp11::integers& precision_int) { using namespace rclock; cpp11::integers year = weekday::get_year(fields); cpp11::integers month = weekday::get_month(fields); cpp11::integers day = weekday::get_day(fields); cpp11::integers index = weekday::get_index(fields); cpp11::integers hour = weekday::get_hour(fields); cpp11::integers minute = weekday::get_minute(fields); cpp11::integers second = weekday::get_second(fields); cpp11::integers subsecond = weekday::get_subsecond(fields); weekday::y y{year}; weekday::ym ym{year, month}; weekday::ymwd ymwd{year, month, day, index}; weekday::ymwdh ymwdh{year, month, day, index, hour}; weekday::ymwdhm ymwdhm{year, month, day, index, hour, minute}; weekday::ymwdhms ymwdhms{year, month, day, index, hour, minute, second}; weekday::ymwdhmss ymwdhmss1{year, month, day, index, hour, minute, second, subsecond}; weekday::ymwdhmss ymwdhmss2{year, month, day, index, hour, minute, second, subsecond}; weekday::ymwdhmss ymwdhmss3{year, month, day, index, hour, minute, second, subsecond}; switch (parse_precision(precision_int)) { case precision::year: return format_calendar_impl(y); case precision::month: return format_calendar_impl(ym); case precision::day: return format_calendar_impl(ymwd); case precision::hour: return format_calendar_impl(ymwdh); case precision::minute: return format_calendar_impl(ymwdhm); case precision::second: return format_calendar_impl(ymwdhms); case precision::millisecond: return format_calendar_impl(ymwdhmss1); case precision::microsecond: return format_calendar_impl(ymwdhmss2); case precision::nanosecond: return format_calendar_impl(ymwdhmss3); default: clock_abort("Internal error: Invalid precision."); } never_reached("format_year_month_weekday_cpp"); } // ----------------------------------------------------------------------------- [[cpp11::register]] cpp11::writable::logicals invalid_detect_year_month_weekday_cpp(const cpp11::integers& year, const cpp11::integers& month, const cpp11::integers& day, const cpp11::integers& index) { rclock::weekday::ymwd x{year, month, day, index}; const r_ssize size = x.size(); cpp11::writable::logicals out(size); for (r_ssize i = 0; i < size; ++i) { if (x.is_na(i)) { out[i] = false; } else { out[i] = !x.to_year_month_weekday(i).ok(); } } return out; } // ----------------------------------------------------------------------------- [[cpp11::register]] bool invalid_any_year_month_weekday_cpp(const cpp11::integers& year, const cpp11::integers& month, const cpp11::integers& day, const cpp11::integers& index) { rclock::weekday::ymwd x{year, month, day, index}; const r_ssize size = x.size(); for (r_ssize i = 0; i < size; ++i) { if (!x.is_na(i) && !x.to_year_month_weekday(i).ok()) { return true; } } return false; } // ----------------------------------------------------------------------------- [[cpp11::register]] int invalid_count_year_month_weekday_cpp(const cpp11::integers& year, const cpp11::integers& month, const cpp11::integers& day, const cpp11::integers& index) { rclock::weekday::ymwd x{year, month, day, index}; const r_ssize size = x.size(); int count = 0; for (r_ssize i = 0; i < size; ++i) { count += !x.is_na(i) && !x.to_year_month_weekday(i).ok(); } return count; } // ----------------------------------------------------------------------------- [[cpp11::register]] cpp11::writable::list invalid_resolve_year_month_weekday_cpp(cpp11::list_of fields, const cpp11::integers& precision_int, const cpp11::strings& invalid_string, const cpp11::sexp& call) { using namespace rclock; const enum invalid invalid_val = parse_invalid(invalid_string); cpp11::integers year = weekday::get_year(fields); cpp11::integers month = weekday::get_month(fields); cpp11::integers day = weekday::get_day(fields); cpp11::integers index = weekday::get_index(fields); cpp11::integers hour = weekday::get_hour(fields); cpp11::integers minute = weekday::get_minute(fields); cpp11::integers second = weekday::get_second(fields); cpp11::integers subsecond = weekday::get_subsecond(fields); weekday::ymwd ymwd{year, month, day, index}; weekday::ymwdh ymwdh{year, month, day, index, hour}; weekday::ymwdhm ymwdhm{year, month, day, index, hour, minute}; weekday::ymwdhms ymwdhms{year, month, day, index, hour, minute, second}; weekday::ymwdhmss ymwdhmss1{year, month, day, index, hour, minute, second, subsecond}; weekday::ymwdhmss ymwdhmss2{year, month, day, index, hour, minute, second, subsecond}; weekday::ymwdhmss ymwdhmss3{year, month, day, index, hour, minute, second, subsecond}; switch (parse_precision(precision_int)) { case precision::day: return invalid_resolve_calendar_impl(ymwd, invalid_val, call); case precision::hour: return invalid_resolve_calendar_impl(ymwdh, invalid_val, call); case precision::minute: return invalid_resolve_calendar_impl(ymwdhm, invalid_val, call); case precision::second: return invalid_resolve_calendar_impl(ymwdhms, invalid_val, call); case precision::millisecond: return invalid_resolve_calendar_impl(ymwdhmss1, invalid_val, call); case precision::microsecond: return invalid_resolve_calendar_impl(ymwdhmss2, invalid_val, call); case precision::nanosecond: return invalid_resolve_calendar_impl(ymwdhmss3, invalid_val, call); default: never_reached("invalid_resolve_year_month_weekday_cpp"); } } // ----------------------------------------------------------------------------- [[cpp11::register]] cpp11::writable::integers get_year_month_weekday_last_cpp(const cpp11::integers& year, const cpp11::integers& month, const cpp11::integers& day, const cpp11::integers& index) { rclock::weekday::ymwd x{year, month, day, index}; const r_ssize size = x.size(); cpp11::writable::integers out(size); for (r_ssize i = 0; i < size; ++i) { if (x.is_na(i)) { out[i] = r_int_na; } else { // We require day precision to set the index to last, so this is allowed date::year_month_weekday elt = x.to_year_month_weekday(i); date::year_month_weekday elt_last{elt.year() / elt.month() / elt.weekday()[date::last]}; out[i] = static_cast(static_cast(elt_last.index())); } } return out; } // ----------------------------------------------------------------------------- [[cpp11::register]] cpp11::writable::list year_month_weekday_plus_years_cpp(const cpp11::integers& year, cpp11::list_of fields_n) { rclock::weekday::y x{year}; rclock::duration::years n{fields_n}; return calendar_plus_duration_impl(x, n); } [[cpp11::register]] cpp11::writable::list year_month_weekday_plus_months_cpp(const cpp11::integers& year, const cpp11::integers& month, cpp11::list_of fields_n) { rclock::weekday::ym x{year, month}; rclock::duration::months n{fields_n}; return calendar_plus_duration_impl(x, n); } // ----------------------------------------------------------------------------- [[cpp11::register]] cpp11::writable::list as_sys_time_year_month_weekday_cpp(cpp11::list_of fields, const cpp11::integers& precision_int) { using namespace rclock; cpp11::integers year = weekday::get_year(fields); cpp11::integers month = weekday::get_month(fields); cpp11::integers day = weekday::get_day(fields); cpp11::integers index = weekday::get_index(fields); cpp11::integers hour = weekday::get_hour(fields); cpp11::integers minute = weekday::get_minute(fields); cpp11::integers second = weekday::get_second(fields); cpp11::integers subsecond = weekday::get_subsecond(fields); weekday::ymwd ymwd{year, month, day, index}; weekday::ymwdh ymwdh{year, month, day, index, hour}; weekday::ymwdhm ymwdhm{year, month, day, index, hour, minute}; weekday::ymwdhms ymwdhms{year, month, day, index, hour, minute, second}; weekday::ymwdhmss ymwdhmss1{year, month, day, index, hour, minute, second, subsecond}; weekday::ymwdhmss ymwdhmss2{year, month, day, index, hour, minute, second, subsecond}; weekday::ymwdhmss ymwdhmss3{year, month, day, index, hour, minute, second, subsecond}; switch (parse_precision(precision_int)) { case precision::day: return as_sys_time_from_calendar_impl(ymwd); case precision::hour: return as_sys_time_from_calendar_impl(ymwdh); case precision::minute: return as_sys_time_from_calendar_impl(ymwdhm); case precision::second: return as_sys_time_from_calendar_impl(ymwdhms); case precision::millisecond: return as_sys_time_from_calendar_impl(ymwdhmss1); case precision::microsecond: return as_sys_time_from_calendar_impl(ymwdhmss2); case precision::nanosecond: return as_sys_time_from_calendar_impl(ymwdhmss3); default: { const enum precision precision_val = parse_precision(precision_int); const std::string precision_string = precision_to_cpp_string(precision_val); std::string message = "Can't convert to a time point from a calendar with '" + precision_string + "' precision. " + "A minimum of 'day' precision is required."; clock_abort(message.c_str()); } } never_reached("as_sys_time_year_month_weekday_cpp"); } // ----------------------------------------------------------------------------- [[cpp11::register]] cpp11::writable::list as_year_month_weekday_from_sys_time_cpp(cpp11::list_of fields, const cpp11::integers& precision_int) { using namespace rclock; switch (parse_precision(precision_int)) { case precision::day: return as_calendar_from_sys_time_impl(fields); case precision::hour: return as_calendar_from_sys_time_impl(fields); case precision::minute: return as_calendar_from_sys_time_impl(fields); case precision::second: return as_calendar_from_sys_time_impl(fields); case precision::millisecond: return as_calendar_from_sys_time_impl>(fields); case precision::microsecond: return as_calendar_from_sys_time_impl>(fields); case precision::nanosecond: return as_calendar_from_sys_time_impl>(fields); default: clock_abort("Internal error: Invalid precision."); } never_reached("as_year_month_weekday_from_sys_time_cpp"); } // ----------------------------------------------------------------------------- static inline cpp11::writable::list year_minus_year_impl(const rclock::weekday::y& x, const rclock::weekday::y& y) { const r_ssize size = x.size(); rclock::duration::years out(size); for (r_ssize i = 0; i < size; ++i) { if (x.is_na(i) || y.is_na(i)) { out.assign_na(i); continue; } out.assign(x.to_year(i) - y.to_year(i), i); } return out.to_list(); } static inline cpp11::writable::list year_month_minus_year_month_impl(const rclock::weekday::ym& x, const rclock::weekday::ym& y) { const r_ssize size = x.size(); rclock::duration::months out(size); for (r_ssize i = 0; i < size; ++i) { if (x.is_na(i) || y.is_na(i)) { out.assign_na(i); continue; } out.assign(x.to_year_month(i) - y.to_year_month(i), i); } return out.to_list(); } [[cpp11::register]] cpp11::writable::list year_month_weekday_minus_year_month_weekday_cpp(cpp11::list_of x, cpp11::list_of y, const cpp11::integers& precision_int) { const cpp11::integers x_year = rclock::weekday::get_year(x); const cpp11::integers x_month = rclock::weekday::get_month(x); const cpp11::integers y_year = rclock::weekday::get_year(y); const cpp11::integers y_month = rclock::weekday::get_month(y); const rclock::weekday::y x_y{x_year}; const rclock::weekday::ym x_ym{x_year, x_month}; const rclock::weekday::y y_y{y_year}; const rclock::weekday::ym y_ym{y_year, y_month}; switch (parse_precision(precision_int)) { case precision::year: return year_minus_year_impl(x_y, y_y); case precision::month: return year_month_minus_year_month_impl(x_ym, y_ym); default: clock_abort("Internal error: Invalid precision."); } never_reached("year_month_weekday_minus_year_month_weekday_cpp"); } clock/src/doubles.h0000644000176200001440000000314414422221153013740 0ustar liggesusers#ifndef CLOCK_DOUBLES_H #define CLOCK_DOUBLES_H #include "clock.h" namespace rclock { class doubles { const cpp11::doubles read_; cpp11::writable::doubles write_; bool writable_; r_ssize size_; public: doubles() noexcept; doubles(const cpp11::doubles& x); doubles(r_ssize size); bool is_na(r_ssize i) const noexcept; r_ssize size() const noexcept; void assign(double x, r_ssize i); void assign_na(r_ssize i); double operator[](r_ssize i) const noexcept; SEXP sexp() const noexcept; }; namespace detail { static const cpp11::doubles empty_doubles = cpp11::doubles{}; } // namespace detail inline doubles::doubles() noexcept : read_(detail::empty_doubles), writable_(false), size_(0) {} inline doubles::doubles(const cpp11::doubles& x) : read_(x), writable_(false), size_(x.size()) {} inline doubles::doubles(r_ssize size) : read_(detail::empty_doubles), write_(cpp11::writable::doubles(size)), writable_(true), size_(size) {} inline bool doubles::is_na(r_ssize i) const noexcept { return std::isnan(this->operator[](i)); } inline r_ssize doubles::size() const noexcept { return size_; } inline void doubles::assign(double x, r_ssize i) { if (!writable_) { write_ = cpp11::writable::doubles(read_); writable_ = true; } write_[i] = x; } inline void doubles::assign_na(r_ssize i) { return assign(r_dbl_na, i); } inline double doubles::operator[](r_ssize i) const noexcept { return writable_ ? write_[i] : read_[i]; } inline SEXP doubles::sexp() const noexcept { return writable_ ? write_ : read_; } } // namespace rclock #endif clock/src/clock.h0000644000176200001440000000125714427430747013421 0ustar liggesusers#ifndef CLOCK_CLOCK_H #define CLOCK_CLOCK_H // Include date tooling first to avoid hitting R's `length()` macro #include #include #include #include // Include date extensions next #include "quarterly.h" #include "ordinal.h" #include "week.h" // Include cpp11 next to avoid being included before cpp11 #include // Then include common utility headers #include #include #include #include #define r_null R_NilValue #define r_ssize R_xlen_t #define r_dbl_na NA_REAL #define r_int_na NA_INTEGER #define r_chr_na NA_STRING #define r_lgl_na NA_LOGICAL #endif clock/src/zoned-time.cpp0000644000176200001440000006203614423730227014726 0ustar liggesusers#include "duration.h" #include "enums.h" #include "utils.h" #include "get.h" #include "rcrd.h" #include "zone.h" #include "parse.h" #include "failure.h" #include "fill.h" // ----------------------------------------------------------------------------- [[cpp11::register]] SEXP new_zoned_time_from_fields(SEXP fields, const cpp11::integers& precision_int, const cpp11::strings& zone, SEXP names) { const enum precision precision_val = parse_precision(precision_int); const r_ssize n_fields = Rf_xlength(fields); if (n_fields != 2) { clock_abort("`fields` must be length 2."); } switch (precision_val) { case precision::year: case precision::quarter: case precision::month: case precision::week: case precision::day: case precision::hour: case precision::minute: { clock_abort("`precision` must be at least 'second' precision."); } case precision::second: case precision::millisecond: case precision::microsecond: case precision::nanosecond: { break; } default: { never_reached("new_zoned_time_from_fields"); } } if (!r_is_string(zone)) { clock_abort("`zone` must be a string."); } SEXP out = PROTECT(new_clock_rcrd_from_fields(fields, names, classes_zoned_time)); Rf_setAttrib(out, syms_precision, precision_int); Rf_setAttrib(out, syms_zone, zone); UNPROTECT(1); return out; } // ----------------------------------------------------------------------------- [[cpp11::register]] SEXP zoned_time_restore(SEXP x, SEXP to) { SEXP zone = Rf_getAttrib(to, syms_zone); SEXP precision = Rf_getAttrib(to, syms_precision); SEXP out = PROTECT(clock_rcrd_restore(x, to, classes_zoned_time)); Rf_setAttrib(out, syms_zone, zone); Rf_setAttrib(out, syms_precision, precision); UNPROTECT(1); return out; } // ----------------------------------------------------------------------------- template static inline cpp11::writable::list get_naive_time_impl(cpp11::list_of& fields, const date::time_zone* p_time_zone) { using Duration = typename ClockDuration::chrono_duration; const ClockDuration x{fields}; const r_ssize size = x.size(); ClockDuration out(size); for (r_ssize i = 0; i < size; ++i) { if (x.is_na(i)) { out.assign_na(i); continue; } const Duration elt = x[i]; const date::sys_time elt_st{elt}; const date::local_time elt_lt = rclock::get_local_time(elt_st, p_time_zone); const Duration elt_out = elt_lt.time_since_epoch(); out.assign(elt_out, i); } return out.to_list(); } [[cpp11::register]] cpp11::writable::list get_naive_time_cpp(cpp11::list_of fields, const cpp11::integers& precision_int, const cpp11::strings& zone) { using namespace rclock; zone_size_validate(zone); const std::string zone_name = cpp11::r_string(zone[0]); const date::time_zone* p_time_zone = zone_name_load(zone_name); switch (parse_precision(precision_int)) { case precision::second: return get_naive_time_impl(fields, p_time_zone); case precision::millisecond: return get_naive_time_impl(fields, p_time_zone); case precision::microsecond: return get_naive_time_impl(fields, p_time_zone); case precision::nanosecond: return get_naive_time_impl(fields, p_time_zone); default: clock_abort("Internal error: Should never be called."); } } // ----------------------------------------------------------------------------- template static inline cpp11::writable::list as_zoned_sys_time_from_naive_time_impl(cpp11::list_of& fields, const date::time_zone* p_time_zone, const cpp11::strings& nonexistent_string, const cpp11::strings& ambiguous_string, const cpp11::sexp& call) { using Duration = typename ClockDuration::chrono_duration; const ClockDuration x{fields}; const r_ssize size = x.size(); ClockDuration out(size); const bool recycle_nonexistent = clock_is_scalar(nonexistent_string); const bool recycle_ambiguous = clock_is_scalar(ambiguous_string); enum nonexistent nonexistent_val; enum ambiguous ambiguous_val; if (recycle_nonexistent) { nonexistent_val = parse_nonexistent_one(nonexistent_string[0]); } if (recycle_ambiguous) { ambiguous_val = parse_ambiguous_one(ambiguous_string[0]); } for (r_ssize i = 0; i < size; ++i) { if (x.is_na(i)) { out.assign_na(i); continue; } const enum nonexistent elt_nonexistent_val = recycle_nonexistent ? nonexistent_val : parse_nonexistent_one(nonexistent_string[i]); const enum ambiguous elt_ambiguous_val = recycle_ambiguous ? ambiguous_val : parse_ambiguous_one(ambiguous_string[i]); const Duration elt = x[i]; const date::local_time elt_lt{elt}; const date::local_info elt_info = rclock::get_info(elt_lt, p_time_zone); out.convert_local_to_sys_and_assign( elt_lt, elt_info, elt_nonexistent_val, elt_ambiguous_val, i, call ); } return out.to_list(); } [[cpp11::register]] cpp11::writable::list as_zoned_sys_time_from_naive_time_cpp(cpp11::list_of fields, const cpp11::integers& precision_int, const cpp11::strings& zone, const cpp11::strings& nonexistent_string, const cpp11::strings& ambiguous_string, const cpp11::sexp& call) { using namespace rclock; zone_size_validate(zone); const std::string zone_name = cpp11::r_string(zone[0]); const date::time_zone* p_time_zone = zone_name_load(zone_name); switch (parse_precision(precision_int)) { case precision::second: return as_zoned_sys_time_from_naive_time_impl(fields, p_time_zone, nonexistent_string, ambiguous_string, call); case precision::millisecond: return as_zoned_sys_time_from_naive_time_impl(fields, p_time_zone, nonexistent_string, ambiguous_string, call); case precision::microsecond: return as_zoned_sys_time_from_naive_time_impl(fields, p_time_zone, nonexistent_string, ambiguous_string, call); case precision::nanosecond: return as_zoned_sys_time_from_naive_time_impl(fields, p_time_zone, nonexistent_string, ambiguous_string, call); default: clock_abort("Internal error: Should never be called."); } } // ----------------------------------------------------------------------------- template static inline cpp11::writable::list as_zoned_sys_time_from_naive_time_with_reference_impl(cpp11::list_of& fields, const date::time_zone* p_time_zone, const cpp11::strings& nonexistent_string, const cpp11::strings& ambiguous_string, const rclock::duration::seconds& reference, const cpp11::sexp& call) { using Duration = typename ClockDuration::chrono_duration; const ClockDuration x{fields}; const r_ssize size = x.size(); ClockDuration out(size); const bool recycle_nonexistent = clock_is_scalar(nonexistent_string); const bool recycle_ambiguous = clock_is_scalar(ambiguous_string); const bool recycle_reference = reference.size() == 1; enum nonexistent nonexistent_val; enum ambiguous ambiguous_val; date::sys_seconds reference_val; if (recycle_nonexistent) { nonexistent_val = parse_nonexistent_one(nonexistent_string[0]); } if (recycle_ambiguous) { ambiguous_val = parse_ambiguous_one(ambiguous_string[0]); } if (recycle_reference) { reference_val = date::sys_seconds{reference[0]}; } for (r_ssize i = 0; i < size; ++i) { if (x.is_na(i)) { out.assign_na(i); continue; } const enum nonexistent elt_nonexistent_val = recycle_nonexistent ? nonexistent_val : parse_nonexistent_one(nonexistent_string[i]); const enum ambiguous elt_ambiguous_val = recycle_ambiguous ? ambiguous_val : parse_ambiguous_one(ambiguous_string[i]); const date::sys_seconds elt_reference_val = recycle_reference ? reference_val : date::sys_seconds{reference[i]}; const Duration elt = x[i]; const date::local_time elt_lt{elt}; const date::local_info elt_info = rclock::get_info(elt_lt, p_time_zone); out.convert_local_with_reference_to_sys_and_assign( elt_lt, elt_info, elt_nonexistent_val, elt_ambiguous_val, elt_reference_val, p_time_zone, i, call ); } return out.to_list(); } [[cpp11::register]] cpp11::writable::list as_zoned_sys_time_from_naive_time_with_reference_cpp(cpp11::list_of fields, const cpp11::integers& precision_int, const cpp11::strings& zone, const cpp11::strings& nonexistent_string, const cpp11::strings& ambiguous_string, cpp11::list_of reference_fields, const cpp11::sexp& call) { using namespace rclock; zone_size_validate(zone); const std::string zone_name = cpp11::r_string(zone[0]); const date::time_zone* p_time_zone = zone_name_load(zone_name); const duration::seconds reference{reference_fields}; switch (parse_precision(precision_int)) { case precision::second: return as_zoned_sys_time_from_naive_time_with_reference_impl(fields, p_time_zone, nonexistent_string, ambiguous_string, reference, call); case precision::millisecond: return as_zoned_sys_time_from_naive_time_with_reference_impl(fields, p_time_zone, nonexistent_string, ambiguous_string, reference, call); case precision::microsecond: return as_zoned_sys_time_from_naive_time_with_reference_impl(fields, p_time_zone, nonexistent_string, ambiguous_string, reference, call); case precision::nanosecond: return as_zoned_sys_time_from_naive_time_with_reference_impl(fields, p_time_zone, nonexistent_string, ambiguous_string, reference, call); default: clock_abort("Internal error: Should never be called."); } } // ----------------------------------------------------------------------------- [[cpp11::register]] cpp11::writable::list to_sys_duration_fields_from_sys_seconds_cpp(const cpp11::doubles& seconds) { r_ssize size = seconds.size(); rclock::duration::seconds out(size); for (r_ssize i = 0; i < size; ++i) { // Assume seconds precision! double elt_seconds = seconds[i]; if (r_dbl_is_missing(elt_seconds)) { out.assign_na(i); continue; } if (clock_dbl_is_oob_for_int64(elt_seconds)) { out.assign_na(i); continue; } const int64_t elt = clock_dbl_as_int64(elt_seconds); const std::chrono::seconds elt_sec{elt}; out.assign(elt_sec, i); } return out.to_list(); } // ----------------------------------------------------------------------------- [[cpp11::register]] cpp11::writable::doubles to_sys_seconds_from_sys_duration_fields_cpp(cpp11::list_of fields) { const rclock::duration::seconds x{fields}; const r_ssize size = x.size(); cpp11::writable::doubles out(size); for (r_ssize i = 0; i < size; ++i) { if (x.is_na(i)) { out[i] = r_dbl_na; continue; } const std::chrono::seconds elt = x[i]; out[i] = static_cast(elt.count()); } return out; } // ----------------------------------------------------------------------------- static inline void finalize_parse_zone(const std::string& candidate, std::string& zone, const date::time_zone*& p_time_zone) { if (!tzdb::locate_zone(candidate, p_time_zone)) { std::string message{ "`%%Z` must be used, and must result in a valid time zone name, " "not '" + candidate + "'." }; clock_abort(message.c_str()); } zone = candidate; } static inline void stop_heterogeneous_zones(const std::string& old_zone, const std::string& new_zone) { std::string message{ "All elements of `x` must have the same time zone name. " "Found different zone names of: '" + old_zone + "' and '" + new_zone + "'." }; clock_abort(message.c_str()); } template static inline void zoned_time_parse_complete_one(std::istringstream& stream, const std::vector& fmts, const std::pair& month_names_pair, const std::pair& weekday_names_pair, const std::pair& ampm_names_pair, const char& dmark, const r_ssize& i, rclock::failures& fail, std::string& zone, const date::time_zone*& p_time_zone, ClockDuration& fields) { using Duration = typename ClockDuration::chrono_duration; static const std::chrono::minutes not_an_offset = std::chrono::minutes::min(); const r_ssize size = fmts.size(); for (r_ssize j = 0; j < size; ++j) { stream.clear(); stream.seekg(0); const char* fmt = fmts[j].c_str(); date::local_time lt; std::string new_zone; std::chrono::minutes offset{not_an_offset}; rclock::from_stream( stream, fmt, month_names_pair, weekday_names_pair, ampm_names_pair, dmark, lt, &new_zone, &offset ); if (stream.fail()) { continue; } if (p_time_zone == NULL) { finalize_parse_zone(new_zone, zone, p_time_zone); } else if (new_zone != zone) { stop_heterogeneous_zones(zone, new_zone); } if (offset == not_an_offset) { clock_abort("`%%z` must be used, and must result in a valid offset from UTC."); } const date::local_info info = rclock::get_info(lt, p_time_zone); switch (info.result) { case date::local_info::nonexistent: { continue; } case date::local_info::unique: { if (offset == info.first.offset) { break; } else { continue; } } case date::local_info::ambiguous: { if (offset == info.first.offset || offset == info.second.offset) { break; } else { continue; } } default: { never_reached("zoned_time_parse_complete_one"); } } fields.assign(lt.time_since_epoch() - offset, i); return; } fail.write(i); fields.assign_na(i); } template cpp11::writable::list zoned_time_parse_complete_impl(const cpp11::strings& x, const cpp11::strings& format, const cpp11::strings& month, const cpp11::strings& month_abbrev, const cpp11::strings& weekday, const cpp11::strings& weekday_abbrev, const cpp11::strings& am_pm, const cpp11::strings& mark) { const r_ssize size = x.size(); ClockDuration fields(size); std::vector fmts(format.size()); rclock::fill_formats(format, fmts); char dmark; switch (parse_decimal_mark(mark)) { case decimal_mark::comma: dmark = ','; break; case decimal_mark::period: dmark = '.'; break; default: clock_abort("Internal error: Unknown decimal mark."); } std::string month_names[24]; const std::pair& month_names_pair = fill_month_names( month, month_abbrev, month_names ); std::string weekday_names[14]; const std::pair& weekday_names_pair = fill_weekday_names( weekday, weekday_abbrev, weekday_names ); std::string ampm_names[2]; const std::pair& ampm_names_pair = fill_ampm_names( am_pm, ampm_names ); rclock::failures fail{}; std::string zone; const date::time_zone* p_time_zone = NULL; std::istringstream stream; void* vmax = vmaxget(); for (r_ssize i = 0; i < size; ++i) { const SEXP elt = x[i]; if (elt == r_chr_na) { fields.assign_na(i); continue; } const char* p_elt = Rf_translateCharUTF8(elt); stream.str(p_elt); zoned_time_parse_complete_one( stream, fmts, month_names_pair, weekday_names_pair, ampm_names_pair, dmark, i, fail, zone, p_time_zone, fields ); } vmaxset(vmax); if (fail.any_failures()) { fail.warn_parse(); } if (zone.empty()) { // In the case of all failures, all NAs, or empty input, there will // be no way to determine a time zone. // In those cases, we default to UTC. zone = "UTC"; } cpp11::writable::strings out_zone(1); out_zone[0] = zone; cpp11::writable::list out_fields = fields.to_list(); cpp11::writable::list out = {out_fields, out_zone}; out.names() = {"fields", "zone"}; return out; } [[cpp11::register]] cpp11::writable::list zoned_time_parse_complete_cpp(const cpp11::strings& x, const cpp11::strings& format, const cpp11::integers& precision_int, const cpp11::strings& month, const cpp11::strings& month_abbrev, const cpp11::strings& weekday, const cpp11::strings& weekday_abbrev, const cpp11::strings& am_pm, const cpp11::strings& mark) { using namespace rclock; switch (parse_precision(precision_int)) { case precision::second: return zoned_time_parse_complete_impl(x, format, month, month_abbrev, weekday, weekday_abbrev, am_pm, mark); case precision::millisecond: return zoned_time_parse_complete_impl(x, format, month, month_abbrev, weekday, weekday_abbrev, am_pm, mark); case precision::microsecond: return zoned_time_parse_complete_impl(x, format, month, month_abbrev, weekday, weekday_abbrev, am_pm, mark); case precision::nanosecond: return zoned_time_parse_complete_impl(x, format, month, month_abbrev, weekday, weekday_abbrev, am_pm, mark); default: never_reached("zoned_time_parse_complete_cpp"); } } // ----------------------------------------------------------------------------- template static inline void zoned_time_parse_abbrev_one(std::istringstream& stream, const std::vector& fmts, const std::pair& month_names_pair, const std::pair& weekday_names_pair, const std::pair& ampm_names_pair, const char& dmark, const r_ssize& i, rclock::failures& fail, const date::time_zone*& p_time_zone, ClockDuration& fields) { using Duration = typename ClockDuration::chrono_duration; const r_ssize size = fmts.size(); for (r_ssize j = 0; j < size; ++j) { stream.clear(); stream.seekg(0); const char* fmt = fmts[j].c_str(); date::local_time lt; std::string parsed_abbrev; // Parsed, but ignored std::chrono::minutes parsed_offset{}; rclock::from_stream( stream, fmt, month_names_pair, weekday_names_pair, ampm_names_pair, dmark, lt, &parsed_abbrev, &parsed_offset ); if (stream.fail()) { continue; } if (parsed_abbrev.empty()) { clock_abort("`%%Z` must be used and must result in a time zone abbreviation."); } const date::local_info info = rclock::get_info(lt, p_time_zone); std::chrono::seconds offset{}; switch (info.result) { case date::local_info::nonexistent: { continue; } case date::local_info::unique: { if (parsed_abbrev == info.first.abbrev) { offset = info.first.offset; break; } else { continue; } } case date::local_info::ambiguous: { if (parsed_abbrev == info.first.abbrev) { offset = info.first.offset; break; } else if (parsed_abbrev == info.second.abbrev) { offset = info.second.offset; break; } else { continue; } } default: { never_reached("zoned_time_parse_abbrev_one"); } } fields.assign(lt.time_since_epoch() - offset, i); return; } fail.write(i); fields.assign_na(i); } template cpp11::writable::list zoned_time_parse_abbrev_impl(const cpp11::strings& x, const date::time_zone*& p_time_zone, const cpp11::strings& format, const cpp11::strings& month, const cpp11::strings& month_abbrev, const cpp11::strings& weekday, const cpp11::strings& weekday_abbrev, const cpp11::strings& am_pm, const cpp11::strings& mark) { const r_ssize size = x.size(); ClockDuration fields(size); std::vector fmts(format.size()); rclock::fill_formats(format, fmts); char dmark; switch (parse_decimal_mark(mark)) { case decimal_mark::comma: dmark = ','; break; case decimal_mark::period: dmark = '.'; break; default: clock_abort("Internal error: Unknown decimal mark."); } std::string month_names[24]; const std::pair& month_names_pair = fill_month_names( month, month_abbrev, month_names ); std::string weekday_names[14]; const std::pair& weekday_names_pair = fill_weekday_names( weekday, weekday_abbrev, weekday_names ); std::string ampm_names[2]; const std::pair& ampm_names_pair = fill_ampm_names( am_pm, ampm_names ); rclock::failures fail{}; std::istringstream stream; void* vmax = vmaxget(); for (r_ssize i = 0; i < size; ++i) { const SEXP elt = x[i]; if (elt == r_chr_na) { fields.assign_na(i); continue; } const char* p_elt = Rf_translateCharUTF8(elt); stream.str(p_elt); zoned_time_parse_abbrev_one( stream, fmts, month_names_pair, weekday_names_pair, ampm_names_pair, dmark, i, fail, p_time_zone, fields ); } vmaxset(vmax); if (fail.any_failures()) { fail.warn_parse(); } return fields.to_list(); } [[cpp11::register]] cpp11::writable::list zoned_time_parse_abbrev_cpp(const cpp11::strings& x, const cpp11::strings& zone, const cpp11::strings& format, const cpp11::integers& precision_int, const cpp11::strings& month, const cpp11::strings& month_abbrev, const cpp11::strings& weekday, const cpp11::strings& weekday_abbrev, const cpp11::strings& am_pm, const cpp11::strings& mark) { using namespace rclock; zone_size_validate(zone); const std::string zone_name = cpp11::r_string(zone[0]); const date::time_zone* p_time_zone = zone_name_load(zone_name); switch (parse_precision(precision_int)) { case precision::second: return zoned_time_parse_abbrev_impl(x, p_time_zone, format, month, month_abbrev, weekday, weekday_abbrev, am_pm, mark); case precision::millisecond: return zoned_time_parse_abbrev_impl(x, p_time_zone, format, month, month_abbrev, weekday, weekday_abbrev, am_pm, mark); case precision::microsecond: return zoned_time_parse_abbrev_impl(x, p_time_zone, format, month, month_abbrev, weekday, weekday_abbrev, am_pm, mark); case precision::nanosecond: return zoned_time_parse_abbrev_impl(x, p_time_zone, format, month, month_abbrev, weekday, weekday_abbrev, am_pm, mark); default: never_reached("zoned_time_parse_abbrev_cpp"); } } clock/src/iso-year-week-day.cpp0000644000176200001440000003244614427233200016102 0ustar liggesusers#include "iso-year-week-day.h" #include "calendar.h" #include "duration.h" #include "enums.h" #include "get.h" #include "rcrd.h" // ----------------------------------------------------------------------------- [[cpp11::register]] SEXP new_iso_year_week_day_from_fields(SEXP fields, const cpp11::integers& precision_int, SEXP names) { const enum precision precision_val = parse_precision(precision_int); const r_ssize n_fields = Rf_xlength(fields); r_ssize n; switch (precision_val) { case precision::year: n = 1; break; case precision::week: n = 2; break; case precision::day: n = 3; break; case precision::hour: n = 4; break; case precision::minute: n = 5; break; case precision::second: n = 6; break; case precision::millisecond: n = 7; break; case precision::microsecond: n = 7; break; case precision::nanosecond: n = 7; break; default: never_reached("new_iso_year_week_day_from_fields"); } if (n != n_fields) { clock_abort("With the given precision, `fields` must have length %i, not %i.", n, n_fields); } SEXP out = PROTECT(new_clock_rcrd_from_fields(fields, names, classes_iso_year_week_day)); Rf_setAttrib(out, syms_precision, precision_int); UNPROTECT(1); return out; } // ----------------------------------------------------------------------------- [[cpp11::register]] SEXP iso_year_week_day_restore(SEXP x, SEXP to) { SEXP precision = Rf_getAttrib(to, syms_precision); SEXP out = PROTECT(clock_rcrd_restore(x, to, classes_iso_year_week_day)); Rf_setAttrib(out, syms_precision, precision); UNPROTECT(1); return out; } // ----------------------------------------------------------------------------- [[cpp11::register]] cpp11::writable::strings format_iso_year_week_day_cpp(cpp11::list_of fields, const cpp11::integers& precision_int) { using namespace rclock; cpp11::integers year = iso::get_year(fields); cpp11::integers week = iso::get_week(fields); cpp11::integers day = iso::get_day(fields); cpp11::integers hour = iso::get_hour(fields); cpp11::integers minute = iso::get_minute(fields); cpp11::integers second = iso::get_second(fields); cpp11::integers subsecond = iso::get_subsecond(fields); iso::y y{year}; iso::ywn ywn{year, week}; iso::ywnwd ywnwd{year, week, day}; iso::ywnwdh ywnwdh{year, week, day, hour}; iso::ywnwdhm ywnwdhm{year, week, day, hour, minute}; iso::ywnwdhms ywnwdhms{year, week, day, hour, minute, second}; iso::ywnwdhmss ywnwdhmss1{year, week, day, hour, minute, second, subsecond}; iso::ywnwdhmss ywnwdhmss2{year, week, day, hour, minute, second, subsecond}; iso::ywnwdhmss ywnwdhmss3{year, week, day, hour, minute, second, subsecond}; switch (parse_precision(precision_int)) { case precision::year: return format_calendar_impl(y); case precision::week: return format_calendar_impl(ywn); case precision::day: return format_calendar_impl(ywnwd); case precision::hour: return format_calendar_impl(ywnwdh); case precision::minute: return format_calendar_impl(ywnwdhm); case precision::second: return format_calendar_impl(ywnwdhms); case precision::millisecond: return format_calendar_impl(ywnwdhmss1); case precision::microsecond: return format_calendar_impl(ywnwdhmss2); case precision::nanosecond: return format_calendar_impl(ywnwdhmss3); default: clock_abort("Internal error: Invalid precision."); } never_reached("format_iso_year_week_day_cpp"); } // ----------------------------------------------------------------------------- [[cpp11::register]] cpp11::writable::logicals invalid_detect_iso_year_week_day_cpp(const cpp11::integers& year, const cpp11::integers& week) { rclock::iso::ywn x{year, week}; const r_ssize size = x.size(); cpp11::writable::logicals out(size); for (r_ssize i = 0; i < size; ++i) { if (x.is_na(i)) { out[i] = false; } else { out[i] = !x.to_year_weeknum(i).ok(); } } return out; } // ----------------------------------------------------------------------------- [[cpp11::register]] bool invalid_any_iso_year_week_day_cpp(const cpp11::integers& year, const cpp11::integers& week) { rclock::iso::ywn x{year, week}; const r_ssize size = x.size(); for (r_ssize i = 0; i < size; ++i) { if (!x.is_na(i) && !x.to_year_weeknum(i).ok()) { return true; } } return false; } // ----------------------------------------------------------------------------- [[cpp11::register]] int invalid_count_iso_year_week_day_cpp(const cpp11::integers& year, const cpp11::integers& week) { rclock::iso::ywn x{year, week}; const r_ssize size = x.size(); int count = 0; for (r_ssize i = 0; i < size; ++i) { count += !x.is_na(i) && !x.to_year_weeknum(i).ok(); } return count; } // ----------------------------------------------------------------------------- [[cpp11::register]] cpp11::writable::list invalid_resolve_iso_year_week_day_cpp(cpp11::list_of fields, const cpp11::integers& precision_int, const cpp11::strings& invalid_string, const cpp11::sexp& call) { using namespace rclock; const enum invalid invalid_val = parse_invalid(invalid_string); cpp11::integers year = iso::get_year(fields); cpp11::integers week = iso::get_week(fields); cpp11::integers day = iso::get_day(fields); cpp11::integers hour = iso::get_hour(fields); cpp11::integers minute = iso::get_minute(fields); cpp11::integers second = iso::get_second(fields); cpp11::integers subsecond = iso::get_subsecond(fields); iso::ywn ywn{year, week}; iso::ywnwd ywnwd{year, week, day}; iso::ywnwdh ywnwdh{year, week, day, hour}; iso::ywnwdhm ywnwdhm{year, week, day, hour, minute}; iso::ywnwdhms ywnwdhms{year, week, day, hour, minute, second}; iso::ywnwdhmss ywnwdhmss1{year, week, day, hour, minute, second, subsecond}; iso::ywnwdhmss ywnwdhmss2{year, week, day, hour, minute, second, subsecond}; iso::ywnwdhmss ywnwdhmss3{year, week, day, hour, minute, second, subsecond}; switch (parse_precision(precision_int)) { case precision::week: return invalid_resolve_calendar_impl(ywn, invalid_val, call); case precision::day: return invalid_resolve_calendar_impl(ywnwd, invalid_val, call); case precision::hour: return invalid_resolve_calendar_impl(ywnwdh, invalid_val, call); case precision::minute: return invalid_resolve_calendar_impl(ywnwdhm, invalid_val, call); case precision::second: return invalid_resolve_calendar_impl(ywnwdhms, invalid_val, call); case precision::millisecond: return invalid_resolve_calendar_impl(ywnwdhmss1, invalid_val, call); case precision::microsecond: return invalid_resolve_calendar_impl(ywnwdhmss2, invalid_val, call); case precision::nanosecond: return invalid_resolve_calendar_impl(ywnwdhmss3, invalid_val, call); default: never_reached("invalid_resolve_iso_year_week_day_cpp"); } } // ----------------------------------------------------------------------------- [[cpp11::register]] cpp11::writable::integers get_iso_year_week_day_last_cpp(const cpp11::integers& year) { rclock::iso::y x{year}; const r_ssize size = x.size(); cpp11::writable::integers out(size); for (r_ssize i = 0; i < size; ++i) { if (x.is_na(i)) { out[i] = r_int_na; } else { iso_week::year_lastweek elt = x.to_year(i) / iso_week::last; out[i] = static_cast(static_cast(elt.weeknum())); } } return out; } // ----------------------------------------------------------------------------- [[cpp11::register]] cpp11::writable::list iso_year_week_day_plus_years_cpp(const cpp11::integers& year, cpp11::list_of fields_n) { rclock::iso::y x{year}; rclock::duration::years n{fields_n}; return calendar_plus_duration_impl(x, n); } // ----------------------------------------------------------------------------- [[cpp11::register]] cpp11::writable::list as_sys_time_iso_year_week_day_cpp(cpp11::list_of fields, const cpp11::integers& precision_int) { using namespace rclock; cpp11::integers year = iso::get_year(fields); cpp11::integers week = iso::get_week(fields); cpp11::integers day = iso::get_day(fields); cpp11::integers hour = iso::get_hour(fields); cpp11::integers minute = iso::get_minute(fields); cpp11::integers second = iso::get_second(fields); cpp11::integers subsecond = iso::get_subsecond(fields); iso::ywnwd ywnwd{year, week, day}; iso::ywnwdh ywnwdh{year, week, day, hour}; iso::ywnwdhm ywnwdhm{year, week, day, hour, minute}; iso::ywnwdhms ywnwdhms{year, week, day, hour, minute, second}; iso::ywnwdhmss ywnwdhmss1{year, week, day, hour, minute, second, subsecond}; iso::ywnwdhmss ywnwdhmss2{year, week, day, hour, minute, second, subsecond}; iso::ywnwdhmss ywnwdhmss3{year, week, day, hour, minute, second, subsecond}; switch (parse_precision(precision_int)) { case precision::day: return as_sys_time_from_calendar_impl(ywnwd); case precision::hour: return as_sys_time_from_calendar_impl(ywnwdh); case precision::minute: return as_sys_time_from_calendar_impl(ywnwdhm); case precision::second: return as_sys_time_from_calendar_impl(ywnwdhms); case precision::millisecond: return as_sys_time_from_calendar_impl(ywnwdhmss1); case precision::microsecond: return as_sys_time_from_calendar_impl(ywnwdhmss2); case precision::nanosecond: return as_sys_time_from_calendar_impl(ywnwdhmss3); default: { const enum precision precision_val = parse_precision(precision_int); const std::string precision_string = precision_to_cpp_string(precision_val); std::string message = "Can't convert to a time point from a calendar with '" + precision_string + "' precision. " + "A minimum of 'day' precision is required."; clock_abort(message.c_str()); } } never_reached("as_sys_time_iso_year_week_day_cpp"); } // ----------------------------------------------------------------------------- [[cpp11::register]] cpp11::writable::list as_iso_year_week_day_from_sys_time_cpp(cpp11::list_of fields, const cpp11::integers& precision_int) { using namespace rclock; switch (parse_precision(precision_int)) { case precision::day: return as_calendar_from_sys_time_impl(fields); case precision::hour: return as_calendar_from_sys_time_impl(fields); case precision::minute: return as_calendar_from_sys_time_impl(fields); case precision::second: return as_calendar_from_sys_time_impl(fields); case precision::millisecond: return as_calendar_from_sys_time_impl>(fields); case precision::microsecond: return as_calendar_from_sys_time_impl>(fields); case precision::nanosecond: return as_calendar_from_sys_time_impl>(fields); default: clock_abort("Internal error: Invalid precision."); } never_reached("as_iso_year_week_day_from_sys_time_cpp"); } // ----------------------------------------------------------------------------- static inline cpp11::writable::list year_minus_year_impl(const rclock::iso::y& x, const rclock::iso::y& y) { const r_ssize size = x.size(); rclock::duration::years out(size); for (r_ssize i = 0; i < size; ++i) { if (x.is_na(i) || y.is_na(i)) { out.assign_na(i); continue; } out.assign(x.to_year(i) - y.to_year(i), i); } return out.to_list(); } [[cpp11::register]] cpp11::writable::list iso_year_week_day_minus_iso_year_week_day_cpp(cpp11::list_of x, cpp11::list_of y, const cpp11::integers& precision_int) { const cpp11::integers x_year = rclock::iso::get_year(x); const cpp11::integers y_year = rclock::iso::get_year(y); const rclock::iso::y x_y{x_year}; const rclock::iso::y y_y{y_year}; switch (parse_precision(precision_int)) { case precision::year: return year_minus_year_impl(x_y, y_y); default: clock_abort("Internal error: Invalid precision."); } never_reached("iso_year_week_day_minus_iso_year_week_day_cpp"); } // ----------------------------------------------------------------------------- [[cpp11::register]] cpp11::writable::logicals iso_year_week_day_leap_year_cpp(const cpp11::integers& year) { const r_ssize size = year.size(); cpp11::writable::logicals out(size); for (r_ssize i = 0; i < size; ++i) { const int elt = year[i]; if (elt == r_int_na) { out[i] = r_lgl_na; } else { out[i] = iso_week::year{elt}.is_leap(); } } return out; } clock/src/resolve.h0000644000176200001440000000217414423730227013774 0ustar liggesusers#ifndef CLOCK_RESOLVE_H #define CLOCK_RESOLVE_H #include "clock.h" #include "utils.h" namespace rclock { namespace detail { inline std::chrono::hours resolve_next_hour() { return std::chrono::hours{0}; } inline std::chrono::minutes resolve_next_minute() { return std::chrono::minutes{0}; } inline std::chrono::seconds resolve_next_second() { return std::chrono::seconds{0}; } template inline Duration resolve_next_subsecond() { return Duration{0}; } inline std::chrono::hours resolve_previous_hour() { return std::chrono::hours{23}; } inline std::chrono::minutes resolve_previous_minute() { return std::chrono::minutes{59}; } inline std::chrono::seconds resolve_previous_second() { return std::chrono::seconds{59}; } template inline Duration resolve_previous_subsecond() { return std::chrono::seconds{1} - Duration{1}; } inline void resolve_error(r_ssize i, const cpp11::sexp& call) { cpp11::writable::integers arg(1); arg[0] = (int) i + 1; auto stop = cpp11::package("clock")["stop_clock_invalid_date"]; stop(arg, call); } } // namespace detail } // namespace rclock #endif clock/src/gregorian-year-month-weekday.h0000644000176200001440000005701614427166555020022 0ustar liggesusers#ifndef CLOCK_GREGORIAN_YEAR_MONTH_WEEKDAY_H #define CLOCK_GREGORIAN_YEAR_MONTH_WEEKDAY_H #include "clock.h" #include "integers.h" #include "enums.h" #include "utils.h" #include "stream.h" #include "resolve.h" namespace rclock { namespace weekday { namespace detail { inline std::ostringstream& stream_day(std::ostringstream& os, int day) NOEXCEPT { os << date::weekday{static_cast(day - 1)}; return os; } inline std::ostringstream& stream_index(std::ostringstream& os, int index) NOEXCEPT { os << index; return os; } inline date::year_month_weekday resolve_next_day_ymw(const date::year_month_weekday& x) { // First day in next month return date::year_month_weekday{((x.year() / x.month()) + date::months(1)) / date::day(1)}; } inline date::year_month_weekday resolve_previous_day_ymw(const date::year_month_weekday& x) { // Last day in current month return date::year_month_weekday{x.year() / x.month() / date::last}; } } // namespace detail class y { protected: rclock::integers year_; public: y(r_ssize size); y(const cpp11::integers& year); bool is_na(r_ssize i) const NOEXCEPT; r_ssize size() const NOEXCEPT; std::ostringstream& stream(std::ostringstream&, r_ssize i) const NOEXCEPT; void add(const date::years& x, r_ssize i) NOEXCEPT; void assign_year(const date::year& x, r_ssize i) NOEXCEPT; void assign_na(r_ssize i) NOEXCEPT; date::year to_year(r_ssize i) const NOEXCEPT; cpp11::writable::list to_list() const; }; class ym : public y { protected: rclock::integers month_; public: ym(r_ssize size); ym(const cpp11::integers& year, const cpp11::integers& month); std::ostringstream& stream(std::ostringstream&, r_ssize i) const NOEXCEPT; void add(const date::months& x, r_ssize i) NOEXCEPT; void assign_month(const date::month& x, r_ssize i) NOEXCEPT; void assign_year_month(const date::year_month& x, r_ssize i) NOEXCEPT; void assign_na(r_ssize i) NOEXCEPT; date::year_month to_year_month(r_ssize i) const NOEXCEPT; cpp11::writable::list to_list() const; }; class ymwd : public ym { protected: rclock::integers day_; rclock::integers index_; public: ymwd(r_ssize size); ymwd(const cpp11::integers& year, const cpp11::integers& month, const cpp11::integers& day, const cpp11::integers& index); std::ostringstream& stream(std::ostringstream&, r_ssize i) const NOEXCEPT; void assign_weekday(const date::weekday& x, r_ssize i) NOEXCEPT; void assign_index(const unsigned& x, r_ssize i) NOEXCEPT; void assign_year_month_weekday(const date::year_month_weekday& x, r_ssize i) NOEXCEPT; void assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT; void assign_na(r_ssize i) NOEXCEPT; void resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call); date::sys_time to_sys_time(r_ssize i) const NOEXCEPT; date::year_month_weekday to_year_month_weekday(r_ssize i) const NOEXCEPT; cpp11::writable::list to_list() const; }; class ymwdh : public ymwd { protected: rclock::integers hour_; public: ymwdh(r_ssize size); ymwdh(const cpp11::integers& year, const cpp11::integers& month, const cpp11::integers& day, const cpp11::integers& index, const cpp11::integers& hour); std::ostringstream& stream(std::ostringstream&, r_ssize i) const NOEXCEPT; void assign_hour(const std::chrono::hours& x, r_ssize i) NOEXCEPT; void assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT; void assign_na(r_ssize i) NOEXCEPT; void resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call); date::sys_time to_sys_time(r_ssize i) const NOEXCEPT; cpp11::writable::list to_list() const; }; class ymwdhm : public ymwdh { protected: rclock::integers minute_; public: ymwdhm(r_ssize size); ymwdhm(const cpp11::integers& year, const cpp11::integers& month, const cpp11::integers& day, const cpp11::integers& index, const cpp11::integers& hour, const cpp11::integers& minute); std::ostringstream& stream(std::ostringstream&, r_ssize i) const NOEXCEPT; void assign_minute(const std::chrono::minutes& x, r_ssize i) NOEXCEPT; void assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT; void assign_na(r_ssize i) NOEXCEPT; void resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call); date::sys_time to_sys_time(r_ssize i) const NOEXCEPT; cpp11::writable::list to_list() const; }; class ymwdhms : public ymwdhm { protected: rclock::integers second_; public: ymwdhms(r_ssize size); ymwdhms(const cpp11::integers& year, const cpp11::integers& month, const cpp11::integers& day, const cpp11::integers& index, const cpp11::integers& hour, const cpp11::integers& minute, const cpp11::integers& second); std::ostringstream& stream(std::ostringstream&, r_ssize i) const NOEXCEPT; void assign_second(const std::chrono::seconds& x, r_ssize i) NOEXCEPT; void assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT; void assign_na(r_ssize i) NOEXCEPT; void resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call); date::sys_time to_sys_time(r_ssize i) const NOEXCEPT; cpp11::writable::list to_list() const; }; template class ymwdhmss : public ymwdhms { protected: rclock::integers subsecond_; public: ymwdhmss(r_ssize size); ymwdhmss(const cpp11::integers& year, const cpp11::integers& month, const cpp11::integers& day, const cpp11::integers& index, const cpp11::integers& hour, const cpp11::integers& minute, const cpp11::integers& second, const cpp11::integers& subsecond); std::ostringstream& stream(std::ostringstream&, r_ssize i) const NOEXCEPT; void assign_subsecond(const Duration& x, r_ssize i) NOEXCEPT; void assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT; void assign_na(r_ssize i) NOEXCEPT; void resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call); date::sys_time to_sys_time(r_ssize i) const NOEXCEPT; cpp11::writable::list to_list() const; }; // Implementation // y inline y::y(r_ssize size) : year_(size) {} inline y::y(const cpp11::integers& year) : year_(rclock::integers(year)) {} inline bool y::is_na(r_ssize i) const NOEXCEPT { return year_.is_na(i); } inline r_ssize y::size() const NOEXCEPT { return year_.size(); } inline std::ostringstream& y::stream(std::ostringstream& os, r_ssize i) const NOEXCEPT { rclock::detail::stream_year(os, year_[i]); return os; } inline void y::add(const date::years& x, r_ssize i) NOEXCEPT { assign_year(to_year(i) + x, i); } inline void y::assign_year(const date::year& x, r_ssize i) NOEXCEPT { year_.assign(static_cast(x), i); } inline void y::assign_na(r_ssize i) NOEXCEPT { year_.assign_na(i); } inline date::year y::to_year(r_ssize i) const NOEXCEPT { return date::year{year_[i]}; } inline cpp11::writable::list y::to_list() const { cpp11::writable::list out({year_.sexp()}); out.names() = {"year"}; return out; } // ym inline ym::ym(r_ssize size) : y(size), month_(size) {} inline ym::ym(const cpp11::integers& year, const cpp11::integers& month) : y(year), month_(month) {} inline std::ostringstream& ym::stream(std::ostringstream& os, r_ssize i) const NOEXCEPT { y::stream(os, i); os << '-'; rclock::detail::stream_month(os, month_[i]); return os; } inline void ym::add(const date::months& x, r_ssize i) NOEXCEPT { assign_year_month(to_year_month(i) + x, i); } inline void ym::assign_month(const date::month& x, r_ssize i) NOEXCEPT { month_.assign(static_cast(static_cast(x)), i); } inline void ym::assign_year_month(const date::year_month& x, r_ssize i) NOEXCEPT { assign_year(x.year(), i); assign_month(x.month(), i); } inline void ym::assign_na(r_ssize i) NOEXCEPT { y::assign_na(i); month_.assign_na(i); } inline date::year_month ym::to_year_month(r_ssize i) const NOEXCEPT { return date::year{year_[i]} / static_cast(month_[i]); } inline cpp11::writable::list ym::to_list() const { cpp11::writable::list out({year_.sexp(), month_.sexp()}); out.names() = {"year", "month"}; return out; } // ymwd inline ymwd::ymwd(r_ssize size) : ym(size), day_(size), index_(size) {} inline ymwd::ymwd(const cpp11::integers& year, const cpp11::integers& month, const cpp11::integers& day, const cpp11::integers& index) : ym(year, month), day_(day), index_(index) {} inline std::ostringstream& ymwd::stream(std::ostringstream& os, r_ssize i) const NOEXCEPT { ym::stream(os, i); os << '-'; detail::stream_day(os, day_[i]); os << '['; detail::stream_index(os, index_[i]); os << ']'; return os; } inline void ymwd::assign_weekday(const date::weekday& x, r_ssize i) NOEXCEPT { day_.assign(static_cast(x.c_encoding() + 1), i); } inline void ymwd::assign_index(const unsigned& x, r_ssize i) NOEXCEPT { index_.assign(static_cast(x), i); } inline void ymwd::assign_year_month_weekday(const date::year_month_weekday& x, r_ssize i) NOEXCEPT { assign_year(x.year(), i); assign_month(x.month(), i); assign_weekday(x.weekday(), i); assign_index(x.index(), i); } inline void ymwd::assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT { date::year_month_weekday ymwd{x}; assign_year_month_weekday(ymwd, i); } inline void ymwd::assign_na(r_ssize i) NOEXCEPT { ym::assign_na(i); day_.assign_na(i); index_.assign_na(i); } inline void ymwd::resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call) { const date::year_month_weekday elt = to_year_month_weekday(i); if (elt.ok()) { return; } switch (type) { case invalid::next_day: case invalid::next: { assign_year_month_weekday(detail::resolve_next_day_ymw(elt), i); break; } case invalid::previous_day: case invalid::previous: { assign_year_month_weekday(detail::resolve_previous_day_ymw(elt), i); break; } case invalid::overflow_day: case invalid::overflow: { assign_year_month_weekday(date::sys_days{elt}, i); break; } case invalid::na: { assign_na(i); break; } case invalid::error: { rclock::detail::resolve_error(i, call); } } } inline date::sys_time ymwd::to_sys_time(r_ssize i) const NOEXCEPT { return date::sys_time{to_year_month_weekday(i)}; } inline date::year_month_weekday ymwd::to_year_month_weekday(r_ssize i) const NOEXCEPT { return date::year{year_[i]} / date::month{static_cast(month_[i])} / date::weekday{static_cast(day_[i] - 1)}[static_cast(index_[i])]; } inline cpp11::writable::list ymwd::to_list() const { cpp11::writable::list out({year_.sexp(), month_.sexp(), day_.sexp(), index_.sexp()}); out.names() = {"year", "month", "day", "index"}; return out; } // ymwdh inline ymwdh::ymwdh(r_ssize size) : ymwd(size), hour_(size) {} inline ymwdh::ymwdh(const cpp11::integers& year, const cpp11::integers& month, const cpp11::integers& day, const cpp11::integers& index, const cpp11::integers& hour) : ymwd(year, month, day, index), hour_(hour) {} inline std::ostringstream& ymwdh::stream(std::ostringstream& os, r_ssize i) const NOEXCEPT { ymwd::stream(os, i); os << 'T'; rclock::detail::stream_hour(os, hour_[i]); return os; } inline void ymwdh::assign_hour(const std::chrono::hours& x, r_ssize i) NOEXCEPT { hour_.assign(x.count(), i); } inline void ymwdh::assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT { const date::sys_time day_point = date::floor(x); const std::chrono::hours hours = x - day_point; ymwd::assign_sys_time(day_point, i); assign_hour(hours, i); } inline void ymwdh::assign_na(r_ssize i) NOEXCEPT { ymwd::assign_na(i); hour_.assign_na(i); } inline void ymwdh::resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call) { const date::year_month_weekday elt = to_year_month_weekday(i); if (elt.ok()) { return; } switch (type) { case invalid::next_day: assign_year_month_weekday(detail::resolve_next_day_ymw(elt), i); break; case invalid::next: { assign_year_month_weekday(detail::resolve_next_day_ymw(elt), i); assign_hour(rclock::detail::resolve_next_hour(), i); break; } case invalid::previous_day: assign_year_month_weekday(detail::resolve_previous_day_ymw(elt), i); break; case invalid::previous: { assign_year_month_weekday(detail::resolve_previous_day_ymw(elt), i); assign_hour(rclock::detail::resolve_previous_hour(), i); break; } case invalid::overflow_day: assign_year_month_weekday(date::sys_days{elt}, i); break; case invalid::overflow: { assign_year_month_weekday(date::sys_days{elt}, i); assign_hour(rclock::detail::resolve_next_hour(), i); break; } case invalid::na: { assign_na(i); break; } case invalid::error: { rclock::detail::resolve_error(i, call); } } } inline date::sys_time ymwdh::to_sys_time(r_ssize i) const NOEXCEPT { return ymwd::to_sys_time(i) + std::chrono::hours{hour_[i]}; } inline cpp11::writable::list ymwdh::to_list() const { cpp11::writable::list out({year_.sexp(), month_.sexp(), day_.sexp(), index_.sexp(), hour_.sexp()}); out.names() = {"year", "month", "day", "index", "hour"}; return out; } // ymwdhm inline ymwdhm::ymwdhm(r_ssize size) : ymwdh(size), minute_(size) {} inline ymwdhm::ymwdhm(const cpp11::integers& year, const cpp11::integers& month, const cpp11::integers& day, const cpp11::integers& index, const cpp11::integers& hour, const cpp11::integers& minute) : ymwdh(year, month, day, index, hour), minute_(minute) {} inline std::ostringstream& ymwdhm::stream(std::ostringstream& os, r_ssize i) const NOEXCEPT { ymwdh::stream(os, i); os << ':'; rclock::detail::stream_minute(os, minute_[i]); return os; } inline void ymwdhm::assign_minute(const std::chrono::minutes& x, r_ssize i) NOEXCEPT { minute_.assign(x.count(), i); } inline void ymwdhm::assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT { const date::sys_time hour_point = date::floor(x); const std::chrono::minutes minutes = x - hour_point; ymwdh::assign_sys_time(hour_point, i); assign_minute(minutes, i); } inline void ymwdhm::assign_na(r_ssize i) NOEXCEPT { ymwdh::assign_na(i); minute_.assign_na(i); } inline void ymwdhm::resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call) { const date::year_month_weekday elt = to_year_month_weekday(i); if (elt.ok()) { return; } switch (type) { case invalid::next_day: assign_year_month_weekday(detail::resolve_next_day_ymw(elt), i); break; case invalid::next: { assign_year_month_weekday(detail::resolve_next_day_ymw(elt), i); assign_hour(rclock::detail::resolve_next_hour(), i); assign_minute(rclock::detail::resolve_next_minute(), i); break; } case invalid::previous_day: assign_year_month_weekday(detail::resolve_previous_day_ymw(elt), i); break; case invalid::previous: { assign_year_month_weekday(detail::resolve_previous_day_ymw(elt), i); assign_hour(rclock::detail::resolve_previous_hour(), i); assign_minute(rclock::detail::resolve_previous_minute(), i); break; } case invalid::overflow_day: assign_year_month_weekday(date::sys_days{elt}, i); break; case invalid::overflow: { assign_year_month_weekday(date::sys_days{elt}, i); assign_hour(rclock::detail::resolve_next_hour(), i); assign_minute(rclock::detail::resolve_next_minute(), i); break; } case invalid::na: { assign_na(i); break; } case invalid::error: { rclock::detail::resolve_error(i, call); } } } inline date::sys_time ymwdhm::to_sys_time(r_ssize i) const NOEXCEPT { return ymwdh::to_sys_time(i) + std::chrono::minutes{minute_[i]}; } inline cpp11::writable::list ymwdhm::to_list() const { cpp11::writable::list out({year_.sexp(), month_.sexp(), day_.sexp(), index_.sexp(), hour_.sexp(), minute_.sexp()}); out.names() = {"year", "month", "day", "index", "hour", "minute"}; return out; } // ymwdhms inline ymwdhms::ymwdhms(r_ssize size) : ymwdhm(size), second_(size) {} inline ymwdhms::ymwdhms(const cpp11::integers& year, const cpp11::integers& month, const cpp11::integers& day, const cpp11::integers& index, const cpp11::integers& hour, const cpp11::integers& minute, const cpp11::integers& second) : ymwdhm(year, month, day, index, hour, minute), second_(second) {} inline std::ostringstream& ymwdhms::stream(std::ostringstream& os, r_ssize i) const NOEXCEPT { ymwdhm::stream(os, i); os << ':'; rclock::detail::stream_second(os, second_[i]); return os; } inline void ymwdhms::assign_second(const std::chrono::seconds& x, r_ssize i) NOEXCEPT { second_.assign(x.count(), i); } inline void ymwdhms::assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT { const date::sys_time minute_point = date::floor(x); const std::chrono::seconds seconds = x - minute_point; ymwdhm::assign_sys_time(minute_point, i); assign_second(seconds, i); } inline void ymwdhms::assign_na(r_ssize i) NOEXCEPT { ymwdhm::assign_na(i); second_.assign_na(i); } inline void ymwdhms::resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call) { const date::year_month_weekday elt = to_year_month_weekday(i); if (elt.ok()) { return; } switch (type) { case invalid::next_day: assign_year_month_weekday(detail::resolve_next_day_ymw(elt), i); break; case invalid::next: { assign_year_month_weekday(detail::resolve_next_day_ymw(elt), i); assign_hour(rclock::detail::resolve_next_hour(), i); assign_minute(rclock::detail::resolve_next_minute(), i); assign_second(rclock::detail::resolve_next_second(), i); break; } case invalid::previous_day: assign_year_month_weekday(detail::resolve_previous_day_ymw(elt), i); break; case invalid::previous: { assign_year_month_weekday(detail::resolve_previous_day_ymw(elt), i); assign_hour(rclock::detail::resolve_previous_hour(), i); assign_minute(rclock::detail::resolve_previous_minute(), i); assign_second(rclock::detail::resolve_previous_second(), i); break; } case invalid::overflow_day: assign_year_month_weekday(date::sys_days{elt}, i); break; case invalid::overflow: { assign_year_month_weekday(date::sys_days{elt}, i); assign_hour(rclock::detail::resolve_next_hour(), i); assign_minute(rclock::detail::resolve_next_minute(), i); assign_second(rclock::detail::resolve_next_second(), i); break; } case invalid::na: { assign_na(i); break; } case invalid::error: { rclock::detail::resolve_error(i, call); } } } inline date::sys_time ymwdhms::to_sys_time(r_ssize i) const NOEXCEPT { return ymwdhm::to_sys_time(i) + std::chrono::seconds{second_[i]}; } inline cpp11::writable::list ymwdhms::to_list() const { cpp11::writable::list out({year_.sexp(), month_.sexp(), day_.sexp(), index_.sexp(), hour_.sexp(), minute_.sexp(), second_.sexp()}); out.names() = {"year", "month", "day", "index", "hour", "minute", "second"}; return out; } // ymwdhmss template inline ymwdhmss::ymwdhmss(r_ssize size) : ymwdhms(size), subsecond_(size) {} template inline ymwdhmss::ymwdhmss(const cpp11::integers& year, const cpp11::integers& month, const cpp11::integers& day, const cpp11::integers& index, const cpp11::integers& hour, const cpp11::integers& minute, const cpp11::integers& second, const cpp11::integers& subsecond) : ymwdhms(year, month, day, index, hour, minute, second), subsecond_(subsecond) {} template inline std::ostringstream& ymwdhmss::stream(std::ostringstream& os, r_ssize i) const NOEXCEPT { ymwdhm::stream(os, i); os << ':'; rclock::detail::stream_second_and_subsecond(os, second_[i], subsecond_[i]); return os; } template inline void ymwdhmss::assign_subsecond(const Duration& x, r_ssize i) NOEXCEPT { subsecond_.assign(x.count(), i); } template inline void ymwdhmss::assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT { const date::sys_time second_point = date::floor(x); const Duration subseconds = x - second_point; ymwdhms::assign_sys_time(second_point, i); assign_subsecond(subseconds, i); } template inline void ymwdhmss::assign_na(r_ssize i) NOEXCEPT { ymwdhms::assign_na(i); subsecond_.assign_na(i); } template inline void ymwdhmss::resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call) { const date::year_month_weekday elt = to_year_month_weekday(i); if (elt.ok()) { return; } switch (type) { case invalid::next_day: assign_year_month_weekday(detail::resolve_next_day_ymw(elt), i); break; case invalid::next: { assign_year_month_weekday(detail::resolve_next_day_ymw(elt), i); assign_hour(rclock::detail::resolve_next_hour(), i); assign_minute(rclock::detail::resolve_next_minute(), i); assign_second(rclock::detail::resolve_next_second(), i); assign_subsecond(rclock::detail::resolve_next_subsecond(), i); break; } case invalid::previous_day: assign_year_month_weekday(detail::resolve_previous_day_ymw(elt), i); break; case invalid::previous: { assign_year_month_weekday(detail::resolve_previous_day_ymw(elt), i); assign_hour(rclock::detail::resolve_previous_hour(), i); assign_minute(rclock::detail::resolve_previous_minute(), i); assign_second(rclock::detail::resolve_previous_second(), i); assign_subsecond(rclock::detail::resolve_previous_subsecond(), i); break; } case invalid::overflow_day: assign_year_month_weekday(date::sys_days{elt}, i); break; case invalid::overflow: { assign_year_month_weekday(date::sys_days{elt}, i); assign_hour(rclock::detail::resolve_next_hour(), i); assign_minute(rclock::detail::resolve_next_minute(), i); assign_second(rclock::detail::resolve_next_second(), i); assign_subsecond(rclock::detail::resolve_next_subsecond(), i); break; } case invalid::na: { assign_na(i); break; } case invalid::error: { rclock::detail::resolve_error(i, call); } } } template inline date::sys_time ymwdhmss::to_sys_time(r_ssize i) const NOEXCEPT { return ymwdhms::to_sys_time(i) + Duration{subsecond_[i]}; } template inline cpp11::writable::list ymwdhmss::to_list() const { cpp11::writable::list out({year_.sexp(), month_.sexp(), day_.sexp(), index_.sexp(), hour_.sexp(), minute_.sexp(), second_.sexp(), subsecond_.sexp()}); out.names() = {"year", "month", "day", "index", "hour", "minute", "second", "subsecond"}; return out; } } // namespace weekday } // namespace rclock #endif clock/src/cpp11.cpp0000644000176200001440000026513514427430744013611 0ustar liggesusers// Generated by cpp11: do not edit by hand // clang-format off #include "cpp11/declarations.hpp" #include // duration.cpp SEXP new_duration_from_fields(SEXP fields, const cpp11::integers& precision_int, SEXP names); extern "C" SEXP _clock_new_duration_from_fields(SEXP fields, SEXP precision_int, SEXP names) { BEGIN_CPP11 return cpp11::as_sexp(new_duration_from_fields(cpp11::as_cpp>(fields), cpp11::as_cpp>(precision_int), cpp11::as_cpp>(names))); END_CPP11 } // duration.cpp SEXP duration_restore(SEXP x, SEXP to); extern "C" SEXP _clock_duration_restore(SEXP x, SEXP to) { BEGIN_CPP11 return cpp11::as_sexp(duration_restore(cpp11::as_cpp>(x), cpp11::as_cpp>(to))); END_CPP11 } // duration.cpp cpp11::writable::strings format_duration_cpp(cpp11::list_of fields, const cpp11::integers& precision_int); extern "C" SEXP _clock_format_duration_cpp(SEXP fields, SEXP precision_int) { BEGIN_CPP11 return cpp11::as_sexp(format_duration_cpp(cpp11::as_cpp>>(fields), cpp11::as_cpp>(precision_int))); END_CPP11 } // duration.cpp cpp11::writable::list_of duration_helper_cpp(const cpp11::integers& n, const cpp11::integers& precision_int); extern "C" SEXP _clock_duration_helper_cpp(SEXP n, SEXP precision_int) { BEGIN_CPP11 return cpp11::as_sexp(duration_helper_cpp(cpp11::as_cpp>(n), cpp11::as_cpp>(precision_int))); END_CPP11 } // duration.cpp cpp11::writable::list duration_cast_cpp(cpp11::list_of fields, const cpp11::integers& precision_from, const cpp11::integers& precision_to); extern "C" SEXP _clock_duration_cast_cpp(SEXP fields, SEXP precision_from, SEXP precision_to) { BEGIN_CPP11 return cpp11::as_sexp(duration_cast_cpp(cpp11::as_cpp>>(fields), cpp11::as_cpp>(precision_from), cpp11::as_cpp>(precision_to))); END_CPP11 } // duration.cpp cpp11::writable::list duration_plus_cpp(cpp11::list_of x, cpp11::list_of y, const cpp11::integers& precision_int); extern "C" SEXP _clock_duration_plus_cpp(SEXP x, SEXP y, SEXP precision_int) { BEGIN_CPP11 return cpp11::as_sexp(duration_plus_cpp(cpp11::as_cpp>>(x), cpp11::as_cpp>>(y), cpp11::as_cpp>(precision_int))); END_CPP11 } // duration.cpp cpp11::writable::list duration_minus_cpp(cpp11::list_of x, cpp11::list_of y, const cpp11::integers& precision_int); extern "C" SEXP _clock_duration_minus_cpp(SEXP x, SEXP y, SEXP precision_int) { BEGIN_CPP11 return cpp11::as_sexp(duration_minus_cpp(cpp11::as_cpp>>(x), cpp11::as_cpp>>(y), cpp11::as_cpp>(precision_int))); END_CPP11 } // duration.cpp cpp11::writable::list duration_modulus_cpp(cpp11::list_of x, cpp11::list_of y, const cpp11::integers& precision_int); extern "C" SEXP _clock_duration_modulus_cpp(SEXP x, SEXP y, SEXP precision_int) { BEGIN_CPP11 return cpp11::as_sexp(duration_modulus_cpp(cpp11::as_cpp>>(x), cpp11::as_cpp>>(y), cpp11::as_cpp>(precision_int))); END_CPP11 } // duration.cpp cpp11::writable::integers duration_integer_divide_cpp(cpp11::list_of x, cpp11::list_of y, const cpp11::integers& precision_int); extern "C" SEXP _clock_duration_integer_divide_cpp(SEXP x, SEXP y, SEXP precision_int) { BEGIN_CPP11 return cpp11::as_sexp(duration_integer_divide_cpp(cpp11::as_cpp>>(x), cpp11::as_cpp>>(y), cpp11::as_cpp>(precision_int))); END_CPP11 } // duration.cpp cpp11::writable::list duration_scalar_multiply_cpp(cpp11::list_of x, const cpp11::integers& y, const cpp11::integers& precision_int); extern "C" SEXP _clock_duration_scalar_multiply_cpp(SEXP x, SEXP y, SEXP precision_int) { BEGIN_CPP11 return cpp11::as_sexp(duration_scalar_multiply_cpp(cpp11::as_cpp>>(x), cpp11::as_cpp>(y), cpp11::as_cpp>(precision_int))); END_CPP11 } // duration.cpp cpp11::writable::list duration_scalar_modulus_cpp(cpp11::list_of x, const cpp11::integers& y, const cpp11::integers& precision_int); extern "C" SEXP _clock_duration_scalar_modulus_cpp(SEXP x, SEXP y, SEXP precision_int) { BEGIN_CPP11 return cpp11::as_sexp(duration_scalar_modulus_cpp(cpp11::as_cpp>>(x), cpp11::as_cpp>(y), cpp11::as_cpp>(precision_int))); END_CPP11 } // duration.cpp cpp11::writable::list duration_scalar_divide_cpp(cpp11::list_of x, const cpp11::integers& y, const cpp11::integers& precision_int); extern "C" SEXP _clock_duration_scalar_divide_cpp(SEXP x, SEXP y, SEXP precision_int) { BEGIN_CPP11 return cpp11::as_sexp(duration_scalar_divide_cpp(cpp11::as_cpp>>(x), cpp11::as_cpp>(y), cpp11::as_cpp>(precision_int))); END_CPP11 } // duration.cpp int duration_precision_common_cpp(const cpp11::integers& x_precision, const cpp11::integers& y_precision); extern "C" SEXP _clock_duration_precision_common_cpp(SEXP x_precision, SEXP y_precision) { BEGIN_CPP11 return cpp11::as_sexp(duration_precision_common_cpp(cpp11::as_cpp>(x_precision), cpp11::as_cpp>(y_precision))); END_CPP11 } // duration.cpp bool duration_has_common_precision_cpp(const cpp11::integers& x_precision, const cpp11::integers& y_precision); extern "C" SEXP _clock_duration_has_common_precision_cpp(SEXP x_precision, SEXP y_precision) { BEGIN_CPP11 return cpp11::as_sexp(duration_has_common_precision_cpp(cpp11::as_cpp>(x_precision), cpp11::as_cpp>(y_precision))); END_CPP11 } // duration.cpp cpp11::writable::list duration_floor_cpp(cpp11::list_of fields, const cpp11::integers& precision_from, const cpp11::integers& precision_to, const int& n); extern "C" SEXP _clock_duration_floor_cpp(SEXP fields, SEXP precision_from, SEXP precision_to, SEXP n) { BEGIN_CPP11 return cpp11::as_sexp(duration_floor_cpp(cpp11::as_cpp>>(fields), cpp11::as_cpp>(precision_from), cpp11::as_cpp>(precision_to), cpp11::as_cpp>(n))); END_CPP11 } // duration.cpp cpp11::writable::list duration_ceiling_cpp(cpp11::list_of fields, const cpp11::integers& precision_from, const cpp11::integers& precision_to, const int& n); extern "C" SEXP _clock_duration_ceiling_cpp(SEXP fields, SEXP precision_from, SEXP precision_to, SEXP n) { BEGIN_CPP11 return cpp11::as_sexp(duration_ceiling_cpp(cpp11::as_cpp>>(fields), cpp11::as_cpp>(precision_from), cpp11::as_cpp>(precision_to), cpp11::as_cpp>(n))); END_CPP11 } // duration.cpp cpp11::writable::list duration_round_cpp(cpp11::list_of fields, const cpp11::integers& precision_from, const cpp11::integers& precision_to, const int& n); extern "C" SEXP _clock_duration_round_cpp(SEXP fields, SEXP precision_from, SEXP precision_to, SEXP n) { BEGIN_CPP11 return cpp11::as_sexp(duration_round_cpp(cpp11::as_cpp>>(fields), cpp11::as_cpp>(precision_from), cpp11::as_cpp>(precision_to), cpp11::as_cpp>(n))); END_CPP11 } // duration.cpp cpp11::writable::list duration_unary_minus_cpp(cpp11::list_of fields, const cpp11::integers& precision_int); extern "C" SEXP _clock_duration_unary_minus_cpp(SEXP fields, SEXP precision_int) { BEGIN_CPP11 return cpp11::as_sexp(duration_unary_minus_cpp(cpp11::as_cpp>>(fields), cpp11::as_cpp>(precision_int))); END_CPP11 } // duration.cpp cpp11::writable::integers duration_as_integer_cpp(cpp11::list_of fields, const cpp11::integers& precision_int); extern "C" SEXP _clock_duration_as_integer_cpp(SEXP fields, SEXP precision_int) { BEGIN_CPP11 return cpp11::as_sexp(duration_as_integer_cpp(cpp11::as_cpp>>(fields), cpp11::as_cpp>(precision_int))); END_CPP11 } // duration.cpp cpp11::writable::doubles duration_as_double_cpp(cpp11::list_of fields, const cpp11::integers& precision_int); extern "C" SEXP _clock_duration_as_double_cpp(SEXP fields, SEXP precision_int) { BEGIN_CPP11 return cpp11::as_sexp(duration_as_double_cpp(cpp11::as_cpp>>(fields), cpp11::as_cpp>(precision_int))); END_CPP11 } // duration.cpp cpp11::writable::list duration_abs_cpp(cpp11::list_of fields, const cpp11::integers& precision_int); extern "C" SEXP _clock_duration_abs_cpp(SEXP fields, SEXP precision_int) { BEGIN_CPP11 return cpp11::as_sexp(duration_abs_cpp(cpp11::as_cpp>>(fields), cpp11::as_cpp>(precision_int))); END_CPP11 } // duration.cpp cpp11::writable::integers duration_sign_cpp(cpp11::list_of fields, const cpp11::integers& precision_int); extern "C" SEXP _clock_duration_sign_cpp(SEXP fields, SEXP precision_int) { BEGIN_CPP11 return cpp11::as_sexp(duration_sign_cpp(cpp11::as_cpp>>(fields), cpp11::as_cpp>(precision_int))); END_CPP11 } // duration.cpp cpp11::writable::list duration_seq_by_lo_cpp(cpp11::list_of from, const cpp11::integers& precision_int, cpp11::list_of by, const cpp11::integers& length_out); extern "C" SEXP _clock_duration_seq_by_lo_cpp(SEXP from, SEXP precision_int, SEXP by, SEXP length_out) { BEGIN_CPP11 return cpp11::as_sexp(duration_seq_by_lo_cpp(cpp11::as_cpp>>(from), cpp11::as_cpp>(precision_int), cpp11::as_cpp>>(by), cpp11::as_cpp>(length_out))); END_CPP11 } // duration.cpp cpp11::writable::list duration_seq_to_by_cpp(cpp11::list_of from, const cpp11::integers& precision_int, cpp11::list_of to, cpp11::list_of by); extern "C" SEXP _clock_duration_seq_to_by_cpp(SEXP from, SEXP precision_int, SEXP to, SEXP by) { BEGIN_CPP11 return cpp11::as_sexp(duration_seq_to_by_cpp(cpp11::as_cpp>>(from), cpp11::as_cpp>(precision_int), cpp11::as_cpp>>(to), cpp11::as_cpp>>(by))); END_CPP11 } // duration.cpp cpp11::writable::list duration_seq_to_lo_cpp(cpp11::list_of from, const cpp11::integers& precision_int, cpp11::list_of to, const cpp11::integers& length_out); extern "C" SEXP _clock_duration_seq_to_lo_cpp(SEXP from, SEXP precision_int, SEXP to, SEXP length_out) { BEGIN_CPP11 return cpp11::as_sexp(duration_seq_to_lo_cpp(cpp11::as_cpp>>(from), cpp11::as_cpp>(precision_int), cpp11::as_cpp>>(to), cpp11::as_cpp>(length_out))); END_CPP11 } // duration.cpp cpp11::writable::list duration_minimum_cpp(const cpp11::integers& precision_int); extern "C" SEXP _clock_duration_minimum_cpp(SEXP precision_int) { BEGIN_CPP11 return cpp11::as_sexp(duration_minimum_cpp(cpp11::as_cpp>(precision_int))); END_CPP11 } // duration.cpp cpp11::writable::list duration_maximum_cpp(const cpp11::integers& precision_int); extern "C" SEXP _clock_duration_maximum_cpp(SEXP precision_int) { BEGIN_CPP11 return cpp11::as_sexp(duration_maximum_cpp(cpp11::as_cpp>(precision_int))); END_CPP11 } // enums.cpp cpp11::writable::strings precision_to_string(const cpp11::integers& precision_int); extern "C" SEXP _clock_precision_to_string(SEXP precision_int) { BEGIN_CPP11 return cpp11::as_sexp(precision_to_string(cpp11::as_cpp>(precision_int))); END_CPP11 } // enums.cpp cpp11::writable::strings clock_to_string(const cpp11::integers& clock_int); extern "C" SEXP _clock_clock_to_string(SEXP clock_int) { BEGIN_CPP11 return cpp11::as_sexp(clock_to_string(cpp11::as_cpp>(clock_int))); END_CPP11 } // format.cpp cpp11::writable::strings format_time_point_cpp(cpp11::list_of fields, const cpp11::integers& clock, const cpp11::strings& format, const cpp11::integers& precision_int, const cpp11::strings& month, const cpp11::strings& month_abbrev, const cpp11::strings& weekday, const cpp11::strings& weekday_abbrev, const cpp11::strings& am_pm, const cpp11::strings& decimal_mark); extern "C" SEXP _clock_format_time_point_cpp(SEXP fields, SEXP clock, SEXP format, SEXP precision_int, SEXP month, SEXP month_abbrev, SEXP weekday, SEXP weekday_abbrev, SEXP am_pm, SEXP decimal_mark) { BEGIN_CPP11 return cpp11::as_sexp(format_time_point_cpp(cpp11::as_cpp>>(fields), cpp11::as_cpp>(clock), cpp11::as_cpp>(format), cpp11::as_cpp>(precision_int), cpp11::as_cpp>(month), cpp11::as_cpp>(month_abbrev), cpp11::as_cpp>(weekday), cpp11::as_cpp>(weekday_abbrev), cpp11::as_cpp>(am_pm), cpp11::as_cpp>(decimal_mark))); END_CPP11 } // format.cpp cpp11::writable::strings format_zoned_time_cpp(cpp11::list_of fields, const cpp11::strings& zone, const bool& abbreviate_zone, const cpp11::strings& format, const cpp11::integers& precision_int, const cpp11::strings& month, const cpp11::strings& month_abbrev, const cpp11::strings& weekday, const cpp11::strings& weekday_abbrev, const cpp11::strings& am_pm, const cpp11::strings& decimal_mark); extern "C" SEXP _clock_format_zoned_time_cpp(SEXP fields, SEXP zone, SEXP abbreviate_zone, SEXP format, SEXP precision_int, SEXP month, SEXP month_abbrev, SEXP weekday, SEXP weekday_abbrev, SEXP am_pm, SEXP decimal_mark) { BEGIN_CPP11 return cpp11::as_sexp(format_zoned_time_cpp(cpp11::as_cpp>>(fields), cpp11::as_cpp>(zone), cpp11::as_cpp>(abbreviate_zone), cpp11::as_cpp>(format), cpp11::as_cpp>(precision_int), cpp11::as_cpp>(month), cpp11::as_cpp>(month_abbrev), cpp11::as_cpp>(weekday), cpp11::as_cpp>(weekday_abbrev), cpp11::as_cpp>(am_pm), cpp11::as_cpp>(decimal_mark))); END_CPP11 } // gregorian-year-day.cpp SEXP new_year_day_from_fields(SEXP fields, const cpp11::integers& precision_int, SEXP names); extern "C" SEXP _clock_new_year_day_from_fields(SEXP fields, SEXP precision_int, SEXP names) { BEGIN_CPP11 return cpp11::as_sexp(new_year_day_from_fields(cpp11::as_cpp>(fields), cpp11::as_cpp>(precision_int), cpp11::as_cpp>(names))); END_CPP11 } // gregorian-year-day.cpp SEXP year_day_restore(SEXP x, SEXP to); extern "C" SEXP _clock_year_day_restore(SEXP x, SEXP to) { BEGIN_CPP11 return cpp11::as_sexp(year_day_restore(cpp11::as_cpp>(x), cpp11::as_cpp>(to))); END_CPP11 } // gregorian-year-day.cpp cpp11::writable::strings format_year_day_cpp(cpp11::list_of fields, const cpp11::integers& precision_int); extern "C" SEXP _clock_format_year_day_cpp(SEXP fields, SEXP precision_int) { BEGIN_CPP11 return cpp11::as_sexp(format_year_day_cpp(cpp11::as_cpp>>(fields), cpp11::as_cpp>(precision_int))); END_CPP11 } // gregorian-year-day.cpp cpp11::writable::logicals invalid_detect_year_day_cpp(const cpp11::integers& year, const cpp11::integers& day); extern "C" SEXP _clock_invalid_detect_year_day_cpp(SEXP year, SEXP day) { BEGIN_CPP11 return cpp11::as_sexp(invalid_detect_year_day_cpp(cpp11::as_cpp>(year), cpp11::as_cpp>(day))); END_CPP11 } // gregorian-year-day.cpp bool invalid_any_year_day_cpp(const cpp11::integers& year, const cpp11::integers& day); extern "C" SEXP _clock_invalid_any_year_day_cpp(SEXP year, SEXP day) { BEGIN_CPP11 return cpp11::as_sexp(invalid_any_year_day_cpp(cpp11::as_cpp>(year), cpp11::as_cpp>(day))); END_CPP11 } // gregorian-year-day.cpp int invalid_count_year_day_cpp(const cpp11::integers& year, const cpp11::integers& day); extern "C" SEXP _clock_invalid_count_year_day_cpp(SEXP year, SEXP day) { BEGIN_CPP11 return cpp11::as_sexp(invalid_count_year_day_cpp(cpp11::as_cpp>(year), cpp11::as_cpp>(day))); END_CPP11 } // gregorian-year-day.cpp cpp11::writable::list invalid_resolve_year_day_cpp(cpp11::list_of fields, const cpp11::integers& precision_int, const cpp11::strings& invalid_string, const cpp11::sexp& call); extern "C" SEXP _clock_invalid_resolve_year_day_cpp(SEXP fields, SEXP precision_int, SEXP invalid_string, SEXP call) { BEGIN_CPP11 return cpp11::as_sexp(invalid_resolve_year_day_cpp(cpp11::as_cpp>>(fields), cpp11::as_cpp>(precision_int), cpp11::as_cpp>(invalid_string), cpp11::as_cpp>(call))); END_CPP11 } // gregorian-year-day.cpp cpp11::writable::integers get_year_day_last_cpp(const cpp11::integers& year); extern "C" SEXP _clock_get_year_day_last_cpp(SEXP year) { BEGIN_CPP11 return cpp11::as_sexp(get_year_day_last_cpp(cpp11::as_cpp>(year))); END_CPP11 } // gregorian-year-day.cpp cpp11::writable::list year_day_plus_years_cpp(const cpp11::integers& year, cpp11::list_of fields_n); extern "C" SEXP _clock_year_day_plus_years_cpp(SEXP year, SEXP fields_n) { BEGIN_CPP11 return cpp11::as_sexp(year_day_plus_years_cpp(cpp11::as_cpp>(year), cpp11::as_cpp>>(fields_n))); END_CPP11 } // gregorian-year-day.cpp cpp11::writable::list as_sys_time_year_day_cpp(cpp11::list_of fields, const cpp11::integers& precision_int); extern "C" SEXP _clock_as_sys_time_year_day_cpp(SEXP fields, SEXP precision_int) { BEGIN_CPP11 return cpp11::as_sexp(as_sys_time_year_day_cpp(cpp11::as_cpp>>(fields), cpp11::as_cpp>(precision_int))); END_CPP11 } // gregorian-year-day.cpp cpp11::writable::list as_year_day_from_sys_time_cpp(cpp11::list_of fields, const cpp11::integers& precision_int); extern "C" SEXP _clock_as_year_day_from_sys_time_cpp(SEXP fields, SEXP precision_int) { BEGIN_CPP11 return cpp11::as_sexp(as_year_day_from_sys_time_cpp(cpp11::as_cpp>>(fields), cpp11::as_cpp>(precision_int))); END_CPP11 } // gregorian-year-day.cpp cpp11::writable::list year_day_minus_year_day_cpp(cpp11::list_of x, cpp11::list_of y, const cpp11::integers& precision_int); extern "C" SEXP _clock_year_day_minus_year_day_cpp(SEXP x, SEXP y, SEXP precision_int) { BEGIN_CPP11 return cpp11::as_sexp(year_day_minus_year_day_cpp(cpp11::as_cpp>>(x), cpp11::as_cpp>>(y), cpp11::as_cpp>(precision_int))); END_CPP11 } // gregorian-year-month-day.cpp SEXP new_year_month_day_from_fields(SEXP fields, const cpp11::integers& precision_int, SEXP names); extern "C" SEXP _clock_new_year_month_day_from_fields(SEXP fields, SEXP precision_int, SEXP names) { BEGIN_CPP11 return cpp11::as_sexp(new_year_month_day_from_fields(cpp11::as_cpp>(fields), cpp11::as_cpp>(precision_int), cpp11::as_cpp>(names))); END_CPP11 } // gregorian-year-month-day.cpp SEXP year_month_day_restore(SEXP x, SEXP to); extern "C" SEXP _clock_year_month_day_restore(SEXP x, SEXP to) { BEGIN_CPP11 return cpp11::as_sexp(year_month_day_restore(cpp11::as_cpp>(x), cpp11::as_cpp>(to))); END_CPP11 } // gregorian-year-month-day.cpp cpp11::writable::strings format_year_month_day_cpp(cpp11::list_of fields, const cpp11::integers& precision_int); extern "C" SEXP _clock_format_year_month_day_cpp(SEXP fields, SEXP precision_int) { BEGIN_CPP11 return cpp11::as_sexp(format_year_month_day_cpp(cpp11::as_cpp>>(fields), cpp11::as_cpp>(precision_int))); END_CPP11 } // gregorian-year-month-day.cpp cpp11::writable::logicals invalid_detect_year_month_day_cpp(const cpp11::integers& year, const cpp11::integers& month, const cpp11::integers& day); extern "C" SEXP _clock_invalid_detect_year_month_day_cpp(SEXP year, SEXP month, SEXP day) { BEGIN_CPP11 return cpp11::as_sexp(invalid_detect_year_month_day_cpp(cpp11::as_cpp>(year), cpp11::as_cpp>(month), cpp11::as_cpp>(day))); END_CPP11 } // gregorian-year-month-day.cpp bool invalid_any_year_month_day_cpp(const cpp11::integers& year, const cpp11::integers& month, const cpp11::integers& day); extern "C" SEXP _clock_invalid_any_year_month_day_cpp(SEXP year, SEXP month, SEXP day) { BEGIN_CPP11 return cpp11::as_sexp(invalid_any_year_month_day_cpp(cpp11::as_cpp>(year), cpp11::as_cpp>(month), cpp11::as_cpp>(day))); END_CPP11 } // gregorian-year-month-day.cpp int invalid_count_year_month_day_cpp(const cpp11::integers& year, const cpp11::integers& month, const cpp11::integers& day); extern "C" SEXP _clock_invalid_count_year_month_day_cpp(SEXP year, SEXP month, SEXP day) { BEGIN_CPP11 return cpp11::as_sexp(invalid_count_year_month_day_cpp(cpp11::as_cpp>(year), cpp11::as_cpp>(month), cpp11::as_cpp>(day))); END_CPP11 } // gregorian-year-month-day.cpp cpp11::writable::list invalid_resolve_year_month_day_cpp(cpp11::list_of fields, const cpp11::integers& precision_int, const cpp11::strings& invalid_string, const cpp11::sexp& call); extern "C" SEXP _clock_invalid_resolve_year_month_day_cpp(SEXP fields, SEXP precision_int, SEXP invalid_string, SEXP call) { BEGIN_CPP11 return cpp11::as_sexp(invalid_resolve_year_month_day_cpp(cpp11::as_cpp>>(fields), cpp11::as_cpp>(precision_int), cpp11::as_cpp>(invalid_string), cpp11::as_cpp>(call))); END_CPP11 } // gregorian-year-month-day.cpp cpp11::writable::integers get_year_month_day_last_cpp(const cpp11::integers& year, const cpp11::integers& month); extern "C" SEXP _clock_get_year_month_day_last_cpp(SEXP year, SEXP month) { BEGIN_CPP11 return cpp11::as_sexp(get_year_month_day_last_cpp(cpp11::as_cpp>(year), cpp11::as_cpp>(month))); END_CPP11 } // gregorian-year-month-day.cpp cpp11::writable::list year_month_day_plus_years_cpp(const cpp11::integers& year, cpp11::list_of fields_n); extern "C" SEXP _clock_year_month_day_plus_years_cpp(SEXP year, SEXP fields_n) { BEGIN_CPP11 return cpp11::as_sexp(year_month_day_plus_years_cpp(cpp11::as_cpp>(year), cpp11::as_cpp>>(fields_n))); END_CPP11 } // gregorian-year-month-day.cpp cpp11::writable::list year_month_day_plus_months_cpp(const cpp11::integers& year, const cpp11::integers& month, cpp11::list_of fields_n); extern "C" SEXP _clock_year_month_day_plus_months_cpp(SEXP year, SEXP month, SEXP fields_n) { BEGIN_CPP11 return cpp11::as_sexp(year_month_day_plus_months_cpp(cpp11::as_cpp>(year), cpp11::as_cpp>(month), cpp11::as_cpp>>(fields_n))); END_CPP11 } // gregorian-year-month-day.cpp cpp11::writable::list as_sys_time_year_month_day_cpp(cpp11::list_of fields, const cpp11::integers& precision_int); extern "C" SEXP _clock_as_sys_time_year_month_day_cpp(SEXP fields, SEXP precision_int) { BEGIN_CPP11 return cpp11::as_sexp(as_sys_time_year_month_day_cpp(cpp11::as_cpp>>(fields), cpp11::as_cpp>(precision_int))); END_CPP11 } // gregorian-year-month-day.cpp cpp11::writable::list as_year_month_day_from_sys_time_cpp(cpp11::list_of fields, const cpp11::integers& precision_int); extern "C" SEXP _clock_as_year_month_day_from_sys_time_cpp(SEXP fields, SEXP precision_int) { BEGIN_CPP11 return cpp11::as_sexp(as_year_month_day_from_sys_time_cpp(cpp11::as_cpp>>(fields), cpp11::as_cpp>(precision_int))); END_CPP11 } // gregorian-year-month-day.cpp cpp11::writable::list year_month_day_minus_year_month_day_cpp(cpp11::list_of x, cpp11::list_of y, const cpp11::integers& precision_int); extern "C" SEXP _clock_year_month_day_minus_year_month_day_cpp(SEXP x, SEXP y, SEXP precision_int) { BEGIN_CPP11 return cpp11::as_sexp(year_month_day_minus_year_month_day_cpp(cpp11::as_cpp>>(x), cpp11::as_cpp>>(y), cpp11::as_cpp>(precision_int))); END_CPP11 } // gregorian-year-month-day.cpp cpp11::writable::list year_month_day_parse_cpp(const cpp11::strings& x, const cpp11::strings& format, const cpp11::integers& precision_int, const cpp11::strings& month, const cpp11::strings& month_abbrev, const cpp11::strings& weekday, const cpp11::strings& weekday_abbrev, const cpp11::strings& am_pm, const cpp11::strings& mark); extern "C" SEXP _clock_year_month_day_parse_cpp(SEXP x, SEXP format, SEXP precision_int, SEXP month, SEXP month_abbrev, SEXP weekday, SEXP weekday_abbrev, SEXP am_pm, SEXP mark) { BEGIN_CPP11 return cpp11::as_sexp(year_month_day_parse_cpp(cpp11::as_cpp>(x), cpp11::as_cpp>(format), cpp11::as_cpp>(precision_int), cpp11::as_cpp>(month), cpp11::as_cpp>(month_abbrev), cpp11::as_cpp>(weekday), cpp11::as_cpp>(weekday_abbrev), cpp11::as_cpp>(am_pm), cpp11::as_cpp>(mark))); END_CPP11 } // gregorian-year-month-day.cpp cpp11::writable::logicals gregorian_leap_year_cpp(const cpp11::integers& year); extern "C" SEXP _clock_gregorian_leap_year_cpp(SEXP year) { BEGIN_CPP11 return cpp11::as_sexp(gregorian_leap_year_cpp(cpp11::as_cpp>(year))); END_CPP11 } // gregorian-year-month-weekday.cpp SEXP new_year_month_weekday_from_fields(SEXP fields, const cpp11::integers& precision_int, SEXP names); extern "C" SEXP _clock_new_year_month_weekday_from_fields(SEXP fields, SEXP precision_int, SEXP names) { BEGIN_CPP11 return cpp11::as_sexp(new_year_month_weekday_from_fields(cpp11::as_cpp>(fields), cpp11::as_cpp>(precision_int), cpp11::as_cpp>(names))); END_CPP11 } // gregorian-year-month-weekday.cpp SEXP year_month_weekday_restore(SEXP x, SEXP to); extern "C" SEXP _clock_year_month_weekday_restore(SEXP x, SEXP to) { BEGIN_CPP11 return cpp11::as_sexp(year_month_weekday_restore(cpp11::as_cpp>(x), cpp11::as_cpp>(to))); END_CPP11 } // gregorian-year-month-weekday.cpp cpp11::writable::strings format_year_month_weekday_cpp(cpp11::list_of fields, const cpp11::integers& precision_int); extern "C" SEXP _clock_format_year_month_weekday_cpp(SEXP fields, SEXP precision_int) { BEGIN_CPP11 return cpp11::as_sexp(format_year_month_weekday_cpp(cpp11::as_cpp>>(fields), cpp11::as_cpp>(precision_int))); END_CPP11 } // gregorian-year-month-weekday.cpp cpp11::writable::logicals invalid_detect_year_month_weekday_cpp(const cpp11::integers& year, const cpp11::integers& month, const cpp11::integers& day, const cpp11::integers& index); extern "C" SEXP _clock_invalid_detect_year_month_weekday_cpp(SEXP year, SEXP month, SEXP day, SEXP index) { BEGIN_CPP11 return cpp11::as_sexp(invalid_detect_year_month_weekday_cpp(cpp11::as_cpp>(year), cpp11::as_cpp>(month), cpp11::as_cpp>(day), cpp11::as_cpp>(index))); END_CPP11 } // gregorian-year-month-weekday.cpp bool invalid_any_year_month_weekday_cpp(const cpp11::integers& year, const cpp11::integers& month, const cpp11::integers& day, const cpp11::integers& index); extern "C" SEXP _clock_invalid_any_year_month_weekday_cpp(SEXP year, SEXP month, SEXP day, SEXP index) { BEGIN_CPP11 return cpp11::as_sexp(invalid_any_year_month_weekday_cpp(cpp11::as_cpp>(year), cpp11::as_cpp>(month), cpp11::as_cpp>(day), cpp11::as_cpp>(index))); END_CPP11 } // gregorian-year-month-weekday.cpp int invalid_count_year_month_weekday_cpp(const cpp11::integers& year, const cpp11::integers& month, const cpp11::integers& day, const cpp11::integers& index); extern "C" SEXP _clock_invalid_count_year_month_weekday_cpp(SEXP year, SEXP month, SEXP day, SEXP index) { BEGIN_CPP11 return cpp11::as_sexp(invalid_count_year_month_weekday_cpp(cpp11::as_cpp>(year), cpp11::as_cpp>(month), cpp11::as_cpp>(day), cpp11::as_cpp>(index))); END_CPP11 } // gregorian-year-month-weekday.cpp cpp11::writable::list invalid_resolve_year_month_weekday_cpp(cpp11::list_of fields, const cpp11::integers& precision_int, const cpp11::strings& invalid_string, const cpp11::sexp& call); extern "C" SEXP _clock_invalid_resolve_year_month_weekday_cpp(SEXP fields, SEXP precision_int, SEXP invalid_string, SEXP call) { BEGIN_CPP11 return cpp11::as_sexp(invalid_resolve_year_month_weekday_cpp(cpp11::as_cpp>>(fields), cpp11::as_cpp>(precision_int), cpp11::as_cpp>(invalid_string), cpp11::as_cpp>(call))); END_CPP11 } // gregorian-year-month-weekday.cpp cpp11::writable::integers get_year_month_weekday_last_cpp(const cpp11::integers& year, const cpp11::integers& month, const cpp11::integers& day, const cpp11::integers& index); extern "C" SEXP _clock_get_year_month_weekday_last_cpp(SEXP year, SEXP month, SEXP day, SEXP index) { BEGIN_CPP11 return cpp11::as_sexp(get_year_month_weekday_last_cpp(cpp11::as_cpp>(year), cpp11::as_cpp>(month), cpp11::as_cpp>(day), cpp11::as_cpp>(index))); END_CPP11 } // gregorian-year-month-weekday.cpp cpp11::writable::list year_month_weekday_plus_years_cpp(const cpp11::integers& year, cpp11::list_of fields_n); extern "C" SEXP _clock_year_month_weekday_plus_years_cpp(SEXP year, SEXP fields_n) { BEGIN_CPP11 return cpp11::as_sexp(year_month_weekday_plus_years_cpp(cpp11::as_cpp>(year), cpp11::as_cpp>>(fields_n))); END_CPP11 } // gregorian-year-month-weekday.cpp cpp11::writable::list year_month_weekday_plus_months_cpp(const cpp11::integers& year, const cpp11::integers& month, cpp11::list_of fields_n); extern "C" SEXP _clock_year_month_weekday_plus_months_cpp(SEXP year, SEXP month, SEXP fields_n) { BEGIN_CPP11 return cpp11::as_sexp(year_month_weekday_plus_months_cpp(cpp11::as_cpp>(year), cpp11::as_cpp>(month), cpp11::as_cpp>>(fields_n))); END_CPP11 } // gregorian-year-month-weekday.cpp cpp11::writable::list as_sys_time_year_month_weekday_cpp(cpp11::list_of fields, const cpp11::integers& precision_int); extern "C" SEXP _clock_as_sys_time_year_month_weekday_cpp(SEXP fields, SEXP precision_int) { BEGIN_CPP11 return cpp11::as_sexp(as_sys_time_year_month_weekday_cpp(cpp11::as_cpp>>(fields), cpp11::as_cpp>(precision_int))); END_CPP11 } // gregorian-year-month-weekday.cpp cpp11::writable::list as_year_month_weekday_from_sys_time_cpp(cpp11::list_of fields, const cpp11::integers& precision_int); extern "C" SEXP _clock_as_year_month_weekday_from_sys_time_cpp(SEXP fields, SEXP precision_int) { BEGIN_CPP11 return cpp11::as_sexp(as_year_month_weekday_from_sys_time_cpp(cpp11::as_cpp>>(fields), cpp11::as_cpp>(precision_int))); END_CPP11 } // gregorian-year-month-weekday.cpp cpp11::writable::list year_month_weekday_minus_year_month_weekday_cpp(cpp11::list_of x, cpp11::list_of y, const cpp11::integers& precision_int); extern "C" SEXP _clock_year_month_weekday_minus_year_month_weekday_cpp(SEXP x, SEXP y, SEXP precision_int) { BEGIN_CPP11 return cpp11::as_sexp(year_month_weekday_minus_year_month_weekday_cpp(cpp11::as_cpp>>(x), cpp11::as_cpp>>(y), cpp11::as_cpp>(precision_int))); END_CPP11 } // iso-year-week-day.cpp SEXP new_iso_year_week_day_from_fields(SEXP fields, const cpp11::integers& precision_int, SEXP names); extern "C" SEXP _clock_new_iso_year_week_day_from_fields(SEXP fields, SEXP precision_int, SEXP names) { BEGIN_CPP11 return cpp11::as_sexp(new_iso_year_week_day_from_fields(cpp11::as_cpp>(fields), cpp11::as_cpp>(precision_int), cpp11::as_cpp>(names))); END_CPP11 } // iso-year-week-day.cpp SEXP iso_year_week_day_restore(SEXP x, SEXP to); extern "C" SEXP _clock_iso_year_week_day_restore(SEXP x, SEXP to) { BEGIN_CPP11 return cpp11::as_sexp(iso_year_week_day_restore(cpp11::as_cpp>(x), cpp11::as_cpp>(to))); END_CPP11 } // iso-year-week-day.cpp cpp11::writable::strings format_iso_year_week_day_cpp(cpp11::list_of fields, const cpp11::integers& precision_int); extern "C" SEXP _clock_format_iso_year_week_day_cpp(SEXP fields, SEXP precision_int) { BEGIN_CPP11 return cpp11::as_sexp(format_iso_year_week_day_cpp(cpp11::as_cpp>>(fields), cpp11::as_cpp>(precision_int))); END_CPP11 } // iso-year-week-day.cpp cpp11::writable::logicals invalid_detect_iso_year_week_day_cpp(const cpp11::integers& year, const cpp11::integers& week); extern "C" SEXP _clock_invalid_detect_iso_year_week_day_cpp(SEXP year, SEXP week) { BEGIN_CPP11 return cpp11::as_sexp(invalid_detect_iso_year_week_day_cpp(cpp11::as_cpp>(year), cpp11::as_cpp>(week))); END_CPP11 } // iso-year-week-day.cpp bool invalid_any_iso_year_week_day_cpp(const cpp11::integers& year, const cpp11::integers& week); extern "C" SEXP _clock_invalid_any_iso_year_week_day_cpp(SEXP year, SEXP week) { BEGIN_CPP11 return cpp11::as_sexp(invalid_any_iso_year_week_day_cpp(cpp11::as_cpp>(year), cpp11::as_cpp>(week))); END_CPP11 } // iso-year-week-day.cpp int invalid_count_iso_year_week_day_cpp(const cpp11::integers& year, const cpp11::integers& week); extern "C" SEXP _clock_invalid_count_iso_year_week_day_cpp(SEXP year, SEXP week) { BEGIN_CPP11 return cpp11::as_sexp(invalid_count_iso_year_week_day_cpp(cpp11::as_cpp>(year), cpp11::as_cpp>(week))); END_CPP11 } // iso-year-week-day.cpp cpp11::writable::list invalid_resolve_iso_year_week_day_cpp(cpp11::list_of fields, const cpp11::integers& precision_int, const cpp11::strings& invalid_string, const cpp11::sexp& call); extern "C" SEXP _clock_invalid_resolve_iso_year_week_day_cpp(SEXP fields, SEXP precision_int, SEXP invalid_string, SEXP call) { BEGIN_CPP11 return cpp11::as_sexp(invalid_resolve_iso_year_week_day_cpp(cpp11::as_cpp>>(fields), cpp11::as_cpp>(precision_int), cpp11::as_cpp>(invalid_string), cpp11::as_cpp>(call))); END_CPP11 } // iso-year-week-day.cpp cpp11::writable::integers get_iso_year_week_day_last_cpp(const cpp11::integers& year); extern "C" SEXP _clock_get_iso_year_week_day_last_cpp(SEXP year) { BEGIN_CPP11 return cpp11::as_sexp(get_iso_year_week_day_last_cpp(cpp11::as_cpp>(year))); END_CPP11 } // iso-year-week-day.cpp cpp11::writable::list iso_year_week_day_plus_years_cpp(const cpp11::integers& year, cpp11::list_of fields_n); extern "C" SEXP _clock_iso_year_week_day_plus_years_cpp(SEXP year, SEXP fields_n) { BEGIN_CPP11 return cpp11::as_sexp(iso_year_week_day_plus_years_cpp(cpp11::as_cpp>(year), cpp11::as_cpp>>(fields_n))); END_CPP11 } // iso-year-week-day.cpp cpp11::writable::list as_sys_time_iso_year_week_day_cpp(cpp11::list_of fields, const cpp11::integers& precision_int); extern "C" SEXP _clock_as_sys_time_iso_year_week_day_cpp(SEXP fields, SEXP precision_int) { BEGIN_CPP11 return cpp11::as_sexp(as_sys_time_iso_year_week_day_cpp(cpp11::as_cpp>>(fields), cpp11::as_cpp>(precision_int))); END_CPP11 } // iso-year-week-day.cpp cpp11::writable::list as_iso_year_week_day_from_sys_time_cpp(cpp11::list_of fields, const cpp11::integers& precision_int); extern "C" SEXP _clock_as_iso_year_week_day_from_sys_time_cpp(SEXP fields, SEXP precision_int) { BEGIN_CPP11 return cpp11::as_sexp(as_iso_year_week_day_from_sys_time_cpp(cpp11::as_cpp>>(fields), cpp11::as_cpp>(precision_int))); END_CPP11 } // iso-year-week-day.cpp cpp11::writable::list iso_year_week_day_minus_iso_year_week_day_cpp(cpp11::list_of x, cpp11::list_of y, const cpp11::integers& precision_int); extern "C" SEXP _clock_iso_year_week_day_minus_iso_year_week_day_cpp(SEXP x, SEXP y, SEXP precision_int) { BEGIN_CPP11 return cpp11::as_sexp(iso_year_week_day_minus_iso_year_week_day_cpp(cpp11::as_cpp>>(x), cpp11::as_cpp>>(y), cpp11::as_cpp>(precision_int))); END_CPP11 } // iso-year-week-day.cpp cpp11::writable::logicals iso_year_week_day_leap_year_cpp(const cpp11::integers& year); extern "C" SEXP _clock_iso_year_week_day_leap_year_cpp(SEXP year) { BEGIN_CPP11 return cpp11::as_sexp(iso_year_week_day_leap_year_cpp(cpp11::as_cpp>(year))); END_CPP11 } // limits.cpp int clock_get_calendar_year_maximum(); extern "C" SEXP _clock_clock_get_calendar_year_maximum() { BEGIN_CPP11 return cpp11::as_sexp(clock_get_calendar_year_maximum()); END_CPP11 } // limits.cpp int clock_get_calendar_year_minimum(); extern "C" SEXP _clock_clock_get_calendar_year_minimum() { BEGIN_CPP11 return cpp11::as_sexp(clock_get_calendar_year_minimum()); END_CPP11 } // naive-time.cpp cpp11::writable::list naive_time_info_cpp(cpp11::list_of fields, const cpp11::integers& precision_int, const cpp11::strings& zone); extern "C" SEXP _clock_naive_time_info_cpp(SEXP fields, SEXP precision_int, SEXP zone) { BEGIN_CPP11 return cpp11::as_sexp(naive_time_info_cpp(cpp11::as_cpp>>(fields), cpp11::as_cpp>(precision_int), cpp11::as_cpp>(zone))); END_CPP11 } // quarterly-year-quarter-day.cpp SEXP new_year_quarter_day_from_fields(SEXP fields, const cpp11::integers& precision_int, SEXP start, SEXP names); extern "C" SEXP _clock_new_year_quarter_day_from_fields(SEXP fields, SEXP precision_int, SEXP start, SEXP names) { BEGIN_CPP11 return cpp11::as_sexp(new_year_quarter_day_from_fields(cpp11::as_cpp>(fields), cpp11::as_cpp>(precision_int), cpp11::as_cpp>(start), cpp11::as_cpp>(names))); END_CPP11 } // quarterly-year-quarter-day.cpp SEXP year_quarter_day_restore(SEXP x, SEXP to); extern "C" SEXP _clock_year_quarter_day_restore(SEXP x, SEXP to) { BEGIN_CPP11 return cpp11::as_sexp(year_quarter_day_restore(cpp11::as_cpp>(x), cpp11::as_cpp>(to))); END_CPP11 } // quarterly-year-quarter-day.cpp cpp11::writable::strings format_year_quarter_day_cpp(cpp11::list_of fields, const cpp11::integers& precision_int, const cpp11::integers& start_int); extern "C" SEXP _clock_format_year_quarter_day_cpp(SEXP fields, SEXP precision_int, SEXP start_int) { BEGIN_CPP11 return cpp11::as_sexp(format_year_quarter_day_cpp(cpp11::as_cpp>>(fields), cpp11::as_cpp>(precision_int), cpp11::as_cpp>(start_int))); END_CPP11 } // quarterly-year-quarter-day.cpp cpp11::writable::logicals invalid_detect_year_quarter_day_cpp(const cpp11::integers& year, const cpp11::integers& quarter, const cpp11::integers& day, const cpp11::integers& start_int); extern "C" SEXP _clock_invalid_detect_year_quarter_day_cpp(SEXP year, SEXP quarter, SEXP day, SEXP start_int) { BEGIN_CPP11 return cpp11::as_sexp(invalid_detect_year_quarter_day_cpp(cpp11::as_cpp>(year), cpp11::as_cpp>(quarter), cpp11::as_cpp>(day), cpp11::as_cpp>(start_int))); END_CPP11 } // quarterly-year-quarter-day.cpp bool invalid_any_year_quarter_day_cpp(const cpp11::integers& year, const cpp11::integers& quarter, const cpp11::integers& day, const cpp11::integers& start_int); extern "C" SEXP _clock_invalid_any_year_quarter_day_cpp(SEXP year, SEXP quarter, SEXP day, SEXP start_int) { BEGIN_CPP11 return cpp11::as_sexp(invalid_any_year_quarter_day_cpp(cpp11::as_cpp>(year), cpp11::as_cpp>(quarter), cpp11::as_cpp>(day), cpp11::as_cpp>(start_int))); END_CPP11 } // quarterly-year-quarter-day.cpp int invalid_count_year_quarter_day_cpp(const cpp11::integers& year, const cpp11::integers& quarter, const cpp11::integers& day, const cpp11::integers& start_int); extern "C" SEXP _clock_invalid_count_year_quarter_day_cpp(SEXP year, SEXP quarter, SEXP day, SEXP start_int) { BEGIN_CPP11 return cpp11::as_sexp(invalid_count_year_quarter_day_cpp(cpp11::as_cpp>(year), cpp11::as_cpp>(quarter), cpp11::as_cpp>(day), cpp11::as_cpp>(start_int))); END_CPP11 } // quarterly-year-quarter-day.cpp cpp11::writable::list invalid_resolve_year_quarter_day_cpp(cpp11::list_of fields, const cpp11::integers& precision_int, const cpp11::integers& start_int, const cpp11::strings& invalid_string, const cpp11::sexp& call); extern "C" SEXP _clock_invalid_resolve_year_quarter_day_cpp(SEXP fields, SEXP precision_int, SEXP start_int, SEXP invalid_string, SEXP call) { BEGIN_CPP11 return cpp11::as_sexp(invalid_resolve_year_quarter_day_cpp(cpp11::as_cpp>>(fields), cpp11::as_cpp>(precision_int), cpp11::as_cpp>(start_int), cpp11::as_cpp>(invalid_string), cpp11::as_cpp>(call))); END_CPP11 } // quarterly-year-quarter-day.cpp cpp11::writable::integers get_year_quarter_day_last_cpp(const cpp11::integers& year, const cpp11::integers& quarter, const cpp11::integers& start_int); extern "C" SEXP _clock_get_year_quarter_day_last_cpp(SEXP year, SEXP quarter, SEXP start_int) { BEGIN_CPP11 return cpp11::as_sexp(get_year_quarter_day_last_cpp(cpp11::as_cpp>(year), cpp11::as_cpp>(quarter), cpp11::as_cpp>(start_int))); END_CPP11 } // quarterly-year-quarter-day.cpp cpp11::writable::list year_quarter_day_plus_years_cpp(const cpp11::integers& year, const cpp11::integers& start_int, cpp11::list_of fields_n); extern "C" SEXP _clock_year_quarter_day_plus_years_cpp(SEXP year, SEXP start_int, SEXP fields_n) { BEGIN_CPP11 return cpp11::as_sexp(year_quarter_day_plus_years_cpp(cpp11::as_cpp>(year), cpp11::as_cpp>(start_int), cpp11::as_cpp>>(fields_n))); END_CPP11 } // quarterly-year-quarter-day.cpp cpp11::writable::list year_quarter_day_plus_quarters_cpp(const cpp11::integers& year, const cpp11::integers& quarter, const cpp11::integers& start_int, cpp11::list_of fields_n); extern "C" SEXP _clock_year_quarter_day_plus_quarters_cpp(SEXP year, SEXP quarter, SEXP start_int, SEXP fields_n) { BEGIN_CPP11 return cpp11::as_sexp(year_quarter_day_plus_quarters_cpp(cpp11::as_cpp>(year), cpp11::as_cpp>(quarter), cpp11::as_cpp>(start_int), cpp11::as_cpp>>(fields_n))); END_CPP11 } // quarterly-year-quarter-day.cpp cpp11::writable::list as_sys_time_year_quarter_day_cpp(cpp11::list_of fields, const cpp11::integers& precision_int, const cpp11::integers& start_int); extern "C" SEXP _clock_as_sys_time_year_quarter_day_cpp(SEXP fields, SEXP precision_int, SEXP start_int) { BEGIN_CPP11 return cpp11::as_sexp(as_sys_time_year_quarter_day_cpp(cpp11::as_cpp>>(fields), cpp11::as_cpp>(precision_int), cpp11::as_cpp>(start_int))); END_CPP11 } // quarterly-year-quarter-day.cpp cpp11::writable::list as_year_quarter_day_from_sys_time_cpp(cpp11::list_of fields, const cpp11::integers& precision_int, const cpp11::integers& start_int); extern "C" SEXP _clock_as_year_quarter_day_from_sys_time_cpp(SEXP fields, SEXP precision_int, SEXP start_int) { BEGIN_CPP11 return cpp11::as_sexp(as_year_quarter_day_from_sys_time_cpp(cpp11::as_cpp>>(fields), cpp11::as_cpp>(precision_int), cpp11::as_cpp>(start_int))); END_CPP11 } // quarterly-year-quarter-day.cpp cpp11::writable::list year_quarter_day_minus_year_quarter_day_cpp(cpp11::list_of x, cpp11::list_of y, const cpp11::integers& precision_int, const cpp11::integers& start_int); extern "C" SEXP _clock_year_quarter_day_minus_year_quarter_day_cpp(SEXP x, SEXP y, SEXP precision_int, SEXP start_int) { BEGIN_CPP11 return cpp11::as_sexp(year_quarter_day_minus_year_quarter_day_cpp(cpp11::as_cpp>>(x), cpp11::as_cpp>>(y), cpp11::as_cpp>(precision_int), cpp11::as_cpp>(start_int))); END_CPP11 } // quarterly-year-quarter-day.cpp cpp11::writable::logicals year_quarter_day_leap_year_cpp(const cpp11::integers& year, const cpp11::integers& start_int); extern "C" SEXP _clock_year_quarter_day_leap_year_cpp(SEXP year, SEXP start_int) { BEGIN_CPP11 return cpp11::as_sexp(year_quarter_day_leap_year_cpp(cpp11::as_cpp>(year), cpp11::as_cpp>(start_int))); END_CPP11 } // rcrd.cpp SEXP clock_rcrd_proxy(SEXP x); extern "C" SEXP _clock_clock_rcrd_proxy(SEXP x) { BEGIN_CPP11 return cpp11::as_sexp(clock_rcrd_proxy(cpp11::as_cpp>(x))); END_CPP11 } // rcrd.cpp SEXP clock_rcrd_names(SEXP x); extern "C" SEXP _clock_clock_rcrd_names(SEXP x) { BEGIN_CPP11 return cpp11::as_sexp(clock_rcrd_names(cpp11::as_cpp>(x))); END_CPP11 } // rcrd.cpp SEXP clock_rcrd_set_names(SEXP x, SEXP names); extern "C" SEXP _clock_clock_rcrd_set_names(SEXP x, SEXP names) { BEGIN_CPP11 return cpp11::as_sexp(clock_rcrd_set_names(cpp11::as_cpp>(x), cpp11::as_cpp>(names))); END_CPP11 } // sys-time.cpp cpp11::writable::list sys_time_now_cpp(); extern "C" SEXP _clock_sys_time_now_cpp() { BEGIN_CPP11 return cpp11::as_sexp(sys_time_now_cpp()); END_CPP11 } // sys-time.cpp cpp11::writable::list sys_time_info_cpp(cpp11::list_of fields, const cpp11::integers& precision_int, const cpp11::strings& zone); extern "C" SEXP _clock_sys_time_info_cpp(SEXP fields, SEXP precision_int, SEXP zone) { BEGIN_CPP11 return cpp11::as_sexp(sys_time_info_cpp(cpp11::as_cpp>>(fields), cpp11::as_cpp>(precision_int), cpp11::as_cpp>(zone))); END_CPP11 } // time-point.cpp SEXP new_time_point_from_fields(SEXP fields, const cpp11::integers& precision_int, const cpp11::integers& clock_int, SEXP names); extern "C" SEXP _clock_new_time_point_from_fields(SEXP fields, SEXP precision_int, SEXP clock_int, SEXP names) { BEGIN_CPP11 return cpp11::as_sexp(new_time_point_from_fields(cpp11::as_cpp>(fields), cpp11::as_cpp>(precision_int), cpp11::as_cpp>(clock_int), cpp11::as_cpp>(names))); END_CPP11 } // time-point.cpp SEXP time_point_restore(SEXP x, SEXP to); extern "C" SEXP _clock_time_point_restore(SEXP x, SEXP to) { BEGIN_CPP11 return cpp11::as_sexp(time_point_restore(cpp11::as_cpp>(x), cpp11::as_cpp>(to))); END_CPP11 } // time-point.cpp cpp11::writable::list time_point_parse_cpp(const cpp11::strings& x, const cpp11::strings& format, const cpp11::integers& precision_int, const cpp11::integers& clock_int, const cpp11::strings& month, const cpp11::strings& month_abbrev, const cpp11::strings& weekday, const cpp11::strings& weekday_abbrev, const cpp11::strings& am_pm, const cpp11::strings& mark); extern "C" SEXP _clock_time_point_parse_cpp(SEXP x, SEXP format, SEXP precision_int, SEXP clock_int, SEXP month, SEXP month_abbrev, SEXP weekday, SEXP weekday_abbrev, SEXP am_pm, SEXP mark) { BEGIN_CPP11 return cpp11::as_sexp(time_point_parse_cpp(cpp11::as_cpp>(x), cpp11::as_cpp>(format), cpp11::as_cpp>(precision_int), cpp11::as_cpp>(clock_int), cpp11::as_cpp>(month), cpp11::as_cpp>(month_abbrev), cpp11::as_cpp>(weekday), cpp11::as_cpp>(weekday_abbrev), cpp11::as_cpp>(am_pm), cpp11::as_cpp>(mark))); END_CPP11 } // utils.cpp SEXP clock_init_utils(); extern "C" SEXP _clock_clock_init_utils() { BEGIN_CPP11 return cpp11::as_sexp(clock_init_utils()); END_CPP11 } // week-year-week-day.cpp SEXP new_year_week_day_from_fields(SEXP fields, const cpp11::integers& precision_int, SEXP start, SEXP names); extern "C" SEXP _clock_new_year_week_day_from_fields(SEXP fields, SEXP precision_int, SEXP start, SEXP names) { BEGIN_CPP11 return cpp11::as_sexp(new_year_week_day_from_fields(cpp11::as_cpp>(fields), cpp11::as_cpp>(precision_int), cpp11::as_cpp>(start), cpp11::as_cpp>(names))); END_CPP11 } // week-year-week-day.cpp SEXP year_week_day_restore(SEXP x, SEXP to); extern "C" SEXP _clock_year_week_day_restore(SEXP x, SEXP to) { BEGIN_CPP11 return cpp11::as_sexp(year_week_day_restore(cpp11::as_cpp>(x), cpp11::as_cpp>(to))); END_CPP11 } // week-year-week-day.cpp cpp11::writable::strings format_year_week_day_cpp(cpp11::list_of fields, const cpp11::integers& precision_int, const cpp11::integers& start_int); extern "C" SEXP _clock_format_year_week_day_cpp(SEXP fields, SEXP precision_int, SEXP start_int) { BEGIN_CPP11 return cpp11::as_sexp(format_year_week_day_cpp(cpp11::as_cpp>>(fields), cpp11::as_cpp>(precision_int), cpp11::as_cpp>(start_int))); END_CPP11 } // week-year-week-day.cpp cpp11::writable::logicals invalid_detect_year_week_day_cpp(const cpp11::integers& year, const cpp11::integers& week, const cpp11::integers& start_int); extern "C" SEXP _clock_invalid_detect_year_week_day_cpp(SEXP year, SEXP week, SEXP start_int) { BEGIN_CPP11 return cpp11::as_sexp(invalid_detect_year_week_day_cpp(cpp11::as_cpp>(year), cpp11::as_cpp>(week), cpp11::as_cpp>(start_int))); END_CPP11 } // week-year-week-day.cpp bool invalid_any_year_week_day_cpp(const cpp11::integers& year, const cpp11::integers& week, const cpp11::integers& start_int); extern "C" SEXP _clock_invalid_any_year_week_day_cpp(SEXP year, SEXP week, SEXP start_int) { BEGIN_CPP11 return cpp11::as_sexp(invalid_any_year_week_day_cpp(cpp11::as_cpp>(year), cpp11::as_cpp>(week), cpp11::as_cpp>(start_int))); END_CPP11 } // week-year-week-day.cpp int invalid_count_year_week_day_cpp(const cpp11::integers& year, const cpp11::integers& week, const cpp11::integers& start_int); extern "C" SEXP _clock_invalid_count_year_week_day_cpp(SEXP year, SEXP week, SEXP start_int) { BEGIN_CPP11 return cpp11::as_sexp(invalid_count_year_week_day_cpp(cpp11::as_cpp>(year), cpp11::as_cpp>(week), cpp11::as_cpp>(start_int))); END_CPP11 } // week-year-week-day.cpp cpp11::writable::list invalid_resolve_year_week_day_cpp(cpp11::list_of fields, const cpp11::integers& precision_int, const cpp11::integers& start_int, const cpp11::strings& invalid_string, const cpp11::sexp& call); extern "C" SEXP _clock_invalid_resolve_year_week_day_cpp(SEXP fields, SEXP precision_int, SEXP start_int, SEXP invalid_string, SEXP call) { BEGIN_CPP11 return cpp11::as_sexp(invalid_resolve_year_week_day_cpp(cpp11::as_cpp>>(fields), cpp11::as_cpp>(precision_int), cpp11::as_cpp>(start_int), cpp11::as_cpp>(invalid_string), cpp11::as_cpp>(call))); END_CPP11 } // week-year-week-day.cpp cpp11::writable::integers get_year_week_day_last_cpp(const cpp11::integers& year, const cpp11::integers& start_int); extern "C" SEXP _clock_get_year_week_day_last_cpp(SEXP year, SEXP start_int) { BEGIN_CPP11 return cpp11::as_sexp(get_year_week_day_last_cpp(cpp11::as_cpp>(year), cpp11::as_cpp>(start_int))); END_CPP11 } // week-year-week-day.cpp cpp11::writable::list year_week_day_plus_years_cpp(const cpp11::integers& year, const cpp11::integers& start_int, cpp11::list_of fields_n); extern "C" SEXP _clock_year_week_day_plus_years_cpp(SEXP year, SEXP start_int, SEXP fields_n) { BEGIN_CPP11 return cpp11::as_sexp(year_week_day_plus_years_cpp(cpp11::as_cpp>(year), cpp11::as_cpp>(start_int), cpp11::as_cpp>>(fields_n))); END_CPP11 } // week-year-week-day.cpp cpp11::writable::list as_sys_time_year_week_day_cpp(cpp11::list_of fields, const cpp11::integers& precision_int, const cpp11::integers& start_int); extern "C" SEXP _clock_as_sys_time_year_week_day_cpp(SEXP fields, SEXP precision_int, SEXP start_int) { BEGIN_CPP11 return cpp11::as_sexp(as_sys_time_year_week_day_cpp(cpp11::as_cpp>>(fields), cpp11::as_cpp>(precision_int), cpp11::as_cpp>(start_int))); END_CPP11 } // week-year-week-day.cpp cpp11::writable::list as_year_week_day_from_sys_time_cpp(cpp11::list_of fields, const cpp11::integers& precision_int, const cpp11::integers& start_int); extern "C" SEXP _clock_as_year_week_day_from_sys_time_cpp(SEXP fields, SEXP precision_int, SEXP start_int) { BEGIN_CPP11 return cpp11::as_sexp(as_year_week_day_from_sys_time_cpp(cpp11::as_cpp>>(fields), cpp11::as_cpp>(precision_int), cpp11::as_cpp>(start_int))); END_CPP11 } // week-year-week-day.cpp cpp11::writable::list year_week_day_minus_year_week_day_cpp(cpp11::list_of x, cpp11::list_of y, const cpp11::integers& precision_int, const cpp11::integers& start_int); extern "C" SEXP _clock_year_week_day_minus_year_week_day_cpp(SEXP x, SEXP y, SEXP precision_int, SEXP start_int) { BEGIN_CPP11 return cpp11::as_sexp(year_week_day_minus_year_week_day_cpp(cpp11::as_cpp>>(x), cpp11::as_cpp>>(y), cpp11::as_cpp>(precision_int), cpp11::as_cpp>(start_int))); END_CPP11 } // week-year-week-day.cpp cpp11::writable::logicals year_week_day_leap_year_cpp(const cpp11::integers& year, const cpp11::integers& start_int); extern "C" SEXP _clock_year_week_day_leap_year_cpp(SEXP year, SEXP start_int) { BEGIN_CPP11 return cpp11::as_sexp(year_week_day_leap_year_cpp(cpp11::as_cpp>(year), cpp11::as_cpp>(start_int))); END_CPP11 } // weekday.cpp cpp11::writable::integers weekday_add_days_cpp(const cpp11::integers& x, cpp11::list_of n_fields); extern "C" SEXP _clock_weekday_add_days_cpp(SEXP x, SEXP n_fields) { BEGIN_CPP11 return cpp11::as_sexp(weekday_add_days_cpp(cpp11::as_cpp>(x), cpp11::as_cpp>>(n_fields))); END_CPP11 } // weekday.cpp cpp11::writable::list weekday_minus_weekday_cpp(const cpp11::integers& x, const cpp11::integers& y); extern "C" SEXP _clock_weekday_minus_weekday_cpp(SEXP x, SEXP y) { BEGIN_CPP11 return cpp11::as_sexp(weekday_minus_weekday_cpp(cpp11::as_cpp>(x), cpp11::as_cpp>(y))); END_CPP11 } // weekday.cpp cpp11::writable::integers weekday_from_time_point_cpp(cpp11::list_of x_fields); extern "C" SEXP _clock_weekday_from_time_point_cpp(SEXP x_fields) { BEGIN_CPP11 return cpp11::as_sexp(weekday_from_time_point_cpp(cpp11::as_cpp>>(x_fields))); END_CPP11 } // weekday.cpp cpp11::writable::strings format_weekday_cpp(const cpp11::integers& x, const cpp11::strings& labels); extern "C" SEXP _clock_format_weekday_cpp(SEXP x, SEXP labels) { BEGIN_CPP11 return cpp11::as_sexp(format_weekday_cpp(cpp11::as_cpp>(x), cpp11::as_cpp>(labels))); END_CPP11 } // zone.cpp cpp11::writable::logicals zone_is_valid(const cpp11::strings& zone); extern "C" SEXP _clock_zone_is_valid(SEXP zone) { BEGIN_CPP11 return cpp11::as_sexp(zone_is_valid(cpp11::as_cpp>(zone))); END_CPP11 } // zone.cpp cpp11::writable::strings zone_current(); extern "C" SEXP _clock_zone_current() { BEGIN_CPP11 return cpp11::as_sexp(zone_current()); END_CPP11 } // zoned-time.cpp SEXP new_zoned_time_from_fields(SEXP fields, const cpp11::integers& precision_int, const cpp11::strings& zone, SEXP names); extern "C" SEXP _clock_new_zoned_time_from_fields(SEXP fields, SEXP precision_int, SEXP zone, SEXP names) { BEGIN_CPP11 return cpp11::as_sexp(new_zoned_time_from_fields(cpp11::as_cpp>(fields), cpp11::as_cpp>(precision_int), cpp11::as_cpp>(zone), cpp11::as_cpp>(names))); END_CPP11 } // zoned-time.cpp SEXP zoned_time_restore(SEXP x, SEXP to); extern "C" SEXP _clock_zoned_time_restore(SEXP x, SEXP to) { BEGIN_CPP11 return cpp11::as_sexp(zoned_time_restore(cpp11::as_cpp>(x), cpp11::as_cpp>(to))); END_CPP11 } // zoned-time.cpp cpp11::writable::list get_naive_time_cpp(cpp11::list_of fields, const cpp11::integers& precision_int, const cpp11::strings& zone); extern "C" SEXP _clock_get_naive_time_cpp(SEXP fields, SEXP precision_int, SEXP zone) { BEGIN_CPP11 return cpp11::as_sexp(get_naive_time_cpp(cpp11::as_cpp>>(fields), cpp11::as_cpp>(precision_int), cpp11::as_cpp>(zone))); END_CPP11 } // zoned-time.cpp cpp11::writable::list as_zoned_sys_time_from_naive_time_cpp(cpp11::list_of fields, const cpp11::integers& precision_int, const cpp11::strings& zone, const cpp11::strings& nonexistent_string, const cpp11::strings& ambiguous_string, const cpp11::sexp& call); extern "C" SEXP _clock_as_zoned_sys_time_from_naive_time_cpp(SEXP fields, SEXP precision_int, SEXP zone, SEXP nonexistent_string, SEXP ambiguous_string, SEXP call) { BEGIN_CPP11 return cpp11::as_sexp(as_zoned_sys_time_from_naive_time_cpp(cpp11::as_cpp>>(fields), cpp11::as_cpp>(precision_int), cpp11::as_cpp>(zone), cpp11::as_cpp>(nonexistent_string), cpp11::as_cpp>(ambiguous_string), cpp11::as_cpp>(call))); END_CPP11 } // zoned-time.cpp cpp11::writable::list as_zoned_sys_time_from_naive_time_with_reference_cpp(cpp11::list_of fields, const cpp11::integers& precision_int, const cpp11::strings& zone, const cpp11::strings& nonexistent_string, const cpp11::strings& ambiguous_string, cpp11::list_of reference_fields, const cpp11::sexp& call); extern "C" SEXP _clock_as_zoned_sys_time_from_naive_time_with_reference_cpp(SEXP fields, SEXP precision_int, SEXP zone, SEXP nonexistent_string, SEXP ambiguous_string, SEXP reference_fields, SEXP call) { BEGIN_CPP11 return cpp11::as_sexp(as_zoned_sys_time_from_naive_time_with_reference_cpp(cpp11::as_cpp>>(fields), cpp11::as_cpp>(precision_int), cpp11::as_cpp>(zone), cpp11::as_cpp>(nonexistent_string), cpp11::as_cpp>(ambiguous_string), cpp11::as_cpp>>(reference_fields), cpp11::as_cpp>(call))); END_CPP11 } // zoned-time.cpp cpp11::writable::list to_sys_duration_fields_from_sys_seconds_cpp(const cpp11::doubles& seconds); extern "C" SEXP _clock_to_sys_duration_fields_from_sys_seconds_cpp(SEXP seconds) { BEGIN_CPP11 return cpp11::as_sexp(to_sys_duration_fields_from_sys_seconds_cpp(cpp11::as_cpp>(seconds))); END_CPP11 } // zoned-time.cpp cpp11::writable::doubles to_sys_seconds_from_sys_duration_fields_cpp(cpp11::list_of fields); extern "C" SEXP _clock_to_sys_seconds_from_sys_duration_fields_cpp(SEXP fields) { BEGIN_CPP11 return cpp11::as_sexp(to_sys_seconds_from_sys_duration_fields_cpp(cpp11::as_cpp>>(fields))); END_CPP11 } // zoned-time.cpp cpp11::writable::list zoned_time_parse_complete_cpp(const cpp11::strings& x, const cpp11::strings& format, const cpp11::integers& precision_int, const cpp11::strings& month, const cpp11::strings& month_abbrev, const cpp11::strings& weekday, const cpp11::strings& weekday_abbrev, const cpp11::strings& am_pm, const cpp11::strings& mark); extern "C" SEXP _clock_zoned_time_parse_complete_cpp(SEXP x, SEXP format, SEXP precision_int, SEXP month, SEXP month_abbrev, SEXP weekday, SEXP weekday_abbrev, SEXP am_pm, SEXP mark) { BEGIN_CPP11 return cpp11::as_sexp(zoned_time_parse_complete_cpp(cpp11::as_cpp>(x), cpp11::as_cpp>(format), cpp11::as_cpp>(precision_int), cpp11::as_cpp>(month), cpp11::as_cpp>(month_abbrev), cpp11::as_cpp>(weekday), cpp11::as_cpp>(weekday_abbrev), cpp11::as_cpp>(am_pm), cpp11::as_cpp>(mark))); END_CPP11 } // zoned-time.cpp cpp11::writable::list zoned_time_parse_abbrev_cpp(const cpp11::strings& x, const cpp11::strings& zone, const cpp11::strings& format, const cpp11::integers& precision_int, const cpp11::strings& month, const cpp11::strings& month_abbrev, const cpp11::strings& weekday, const cpp11::strings& weekday_abbrev, const cpp11::strings& am_pm, const cpp11::strings& mark); extern "C" SEXP _clock_zoned_time_parse_abbrev_cpp(SEXP x, SEXP zone, SEXP format, SEXP precision_int, SEXP month, SEXP month_abbrev, SEXP weekday, SEXP weekday_abbrev, SEXP am_pm, SEXP mark) { BEGIN_CPP11 return cpp11::as_sexp(zoned_time_parse_abbrev_cpp(cpp11::as_cpp>(x), cpp11::as_cpp>(zone), cpp11::as_cpp>(format), cpp11::as_cpp>(precision_int), cpp11::as_cpp>(month), cpp11::as_cpp>(month_abbrev), cpp11::as_cpp>(weekday), cpp11::as_cpp>(weekday_abbrev), cpp11::as_cpp>(am_pm), cpp11::as_cpp>(mark))); END_CPP11 } extern "C" { static const R_CallMethodDef CallEntries[] = { {"_clock_as_iso_year_week_day_from_sys_time_cpp", (DL_FUNC) &_clock_as_iso_year_week_day_from_sys_time_cpp, 2}, {"_clock_as_sys_time_iso_year_week_day_cpp", (DL_FUNC) &_clock_as_sys_time_iso_year_week_day_cpp, 2}, {"_clock_as_sys_time_year_day_cpp", (DL_FUNC) &_clock_as_sys_time_year_day_cpp, 2}, {"_clock_as_sys_time_year_month_day_cpp", (DL_FUNC) &_clock_as_sys_time_year_month_day_cpp, 2}, {"_clock_as_sys_time_year_month_weekday_cpp", (DL_FUNC) &_clock_as_sys_time_year_month_weekday_cpp, 2}, {"_clock_as_sys_time_year_quarter_day_cpp", (DL_FUNC) &_clock_as_sys_time_year_quarter_day_cpp, 3}, {"_clock_as_sys_time_year_week_day_cpp", (DL_FUNC) &_clock_as_sys_time_year_week_day_cpp, 3}, {"_clock_as_year_day_from_sys_time_cpp", (DL_FUNC) &_clock_as_year_day_from_sys_time_cpp, 2}, {"_clock_as_year_month_day_from_sys_time_cpp", (DL_FUNC) &_clock_as_year_month_day_from_sys_time_cpp, 2}, {"_clock_as_year_month_weekday_from_sys_time_cpp", (DL_FUNC) &_clock_as_year_month_weekday_from_sys_time_cpp, 2}, {"_clock_as_year_quarter_day_from_sys_time_cpp", (DL_FUNC) &_clock_as_year_quarter_day_from_sys_time_cpp, 3}, {"_clock_as_year_week_day_from_sys_time_cpp", (DL_FUNC) &_clock_as_year_week_day_from_sys_time_cpp, 3}, {"_clock_as_zoned_sys_time_from_naive_time_cpp", (DL_FUNC) &_clock_as_zoned_sys_time_from_naive_time_cpp, 6}, {"_clock_as_zoned_sys_time_from_naive_time_with_reference_cpp", (DL_FUNC) &_clock_as_zoned_sys_time_from_naive_time_with_reference_cpp, 7}, {"_clock_clock_get_calendar_year_maximum", (DL_FUNC) &_clock_clock_get_calendar_year_maximum, 0}, {"_clock_clock_get_calendar_year_minimum", (DL_FUNC) &_clock_clock_get_calendar_year_minimum, 0}, {"_clock_clock_init_utils", (DL_FUNC) &_clock_clock_init_utils, 0}, {"_clock_clock_rcrd_names", (DL_FUNC) &_clock_clock_rcrd_names, 1}, {"_clock_clock_rcrd_proxy", (DL_FUNC) &_clock_clock_rcrd_proxy, 1}, {"_clock_clock_rcrd_set_names", (DL_FUNC) &_clock_clock_rcrd_set_names, 2}, {"_clock_clock_to_string", (DL_FUNC) &_clock_clock_to_string, 1}, {"_clock_duration_abs_cpp", (DL_FUNC) &_clock_duration_abs_cpp, 2}, {"_clock_duration_as_double_cpp", (DL_FUNC) &_clock_duration_as_double_cpp, 2}, {"_clock_duration_as_integer_cpp", (DL_FUNC) &_clock_duration_as_integer_cpp, 2}, {"_clock_duration_cast_cpp", (DL_FUNC) &_clock_duration_cast_cpp, 3}, {"_clock_duration_ceiling_cpp", (DL_FUNC) &_clock_duration_ceiling_cpp, 4}, {"_clock_duration_floor_cpp", (DL_FUNC) &_clock_duration_floor_cpp, 4}, {"_clock_duration_has_common_precision_cpp", (DL_FUNC) &_clock_duration_has_common_precision_cpp, 2}, {"_clock_duration_helper_cpp", (DL_FUNC) &_clock_duration_helper_cpp, 2}, {"_clock_duration_integer_divide_cpp", (DL_FUNC) &_clock_duration_integer_divide_cpp, 3}, {"_clock_duration_maximum_cpp", (DL_FUNC) &_clock_duration_maximum_cpp, 1}, {"_clock_duration_minimum_cpp", (DL_FUNC) &_clock_duration_minimum_cpp, 1}, {"_clock_duration_minus_cpp", (DL_FUNC) &_clock_duration_minus_cpp, 3}, {"_clock_duration_modulus_cpp", (DL_FUNC) &_clock_duration_modulus_cpp, 3}, {"_clock_duration_plus_cpp", (DL_FUNC) &_clock_duration_plus_cpp, 3}, {"_clock_duration_precision_common_cpp", (DL_FUNC) &_clock_duration_precision_common_cpp, 2}, {"_clock_duration_restore", (DL_FUNC) &_clock_duration_restore, 2}, {"_clock_duration_round_cpp", (DL_FUNC) &_clock_duration_round_cpp, 4}, {"_clock_duration_scalar_divide_cpp", (DL_FUNC) &_clock_duration_scalar_divide_cpp, 3}, {"_clock_duration_scalar_modulus_cpp", (DL_FUNC) &_clock_duration_scalar_modulus_cpp, 3}, {"_clock_duration_scalar_multiply_cpp", (DL_FUNC) &_clock_duration_scalar_multiply_cpp, 3}, {"_clock_duration_seq_by_lo_cpp", (DL_FUNC) &_clock_duration_seq_by_lo_cpp, 4}, {"_clock_duration_seq_to_by_cpp", (DL_FUNC) &_clock_duration_seq_to_by_cpp, 4}, {"_clock_duration_seq_to_lo_cpp", (DL_FUNC) &_clock_duration_seq_to_lo_cpp, 4}, {"_clock_duration_sign_cpp", (DL_FUNC) &_clock_duration_sign_cpp, 2}, {"_clock_duration_unary_minus_cpp", (DL_FUNC) &_clock_duration_unary_minus_cpp, 2}, {"_clock_format_duration_cpp", (DL_FUNC) &_clock_format_duration_cpp, 2}, {"_clock_format_iso_year_week_day_cpp", (DL_FUNC) &_clock_format_iso_year_week_day_cpp, 2}, {"_clock_format_time_point_cpp", (DL_FUNC) &_clock_format_time_point_cpp, 10}, {"_clock_format_weekday_cpp", (DL_FUNC) &_clock_format_weekday_cpp, 2}, {"_clock_format_year_day_cpp", (DL_FUNC) &_clock_format_year_day_cpp, 2}, {"_clock_format_year_month_day_cpp", (DL_FUNC) &_clock_format_year_month_day_cpp, 2}, {"_clock_format_year_month_weekday_cpp", (DL_FUNC) &_clock_format_year_month_weekday_cpp, 2}, {"_clock_format_year_quarter_day_cpp", (DL_FUNC) &_clock_format_year_quarter_day_cpp, 3}, {"_clock_format_year_week_day_cpp", (DL_FUNC) &_clock_format_year_week_day_cpp, 3}, {"_clock_format_zoned_time_cpp", (DL_FUNC) &_clock_format_zoned_time_cpp, 11}, {"_clock_get_iso_year_week_day_last_cpp", (DL_FUNC) &_clock_get_iso_year_week_day_last_cpp, 1}, {"_clock_get_naive_time_cpp", (DL_FUNC) &_clock_get_naive_time_cpp, 3}, {"_clock_get_year_day_last_cpp", (DL_FUNC) &_clock_get_year_day_last_cpp, 1}, {"_clock_get_year_month_day_last_cpp", (DL_FUNC) &_clock_get_year_month_day_last_cpp, 2}, {"_clock_get_year_month_weekday_last_cpp", (DL_FUNC) &_clock_get_year_month_weekday_last_cpp, 4}, {"_clock_get_year_quarter_day_last_cpp", (DL_FUNC) &_clock_get_year_quarter_day_last_cpp, 3}, {"_clock_get_year_week_day_last_cpp", (DL_FUNC) &_clock_get_year_week_day_last_cpp, 2}, {"_clock_gregorian_leap_year_cpp", (DL_FUNC) &_clock_gregorian_leap_year_cpp, 1}, {"_clock_invalid_any_iso_year_week_day_cpp", (DL_FUNC) &_clock_invalid_any_iso_year_week_day_cpp, 2}, {"_clock_invalid_any_year_day_cpp", (DL_FUNC) &_clock_invalid_any_year_day_cpp, 2}, {"_clock_invalid_any_year_month_day_cpp", (DL_FUNC) &_clock_invalid_any_year_month_day_cpp, 3}, {"_clock_invalid_any_year_month_weekday_cpp", (DL_FUNC) &_clock_invalid_any_year_month_weekday_cpp, 4}, {"_clock_invalid_any_year_quarter_day_cpp", (DL_FUNC) &_clock_invalid_any_year_quarter_day_cpp, 4}, {"_clock_invalid_any_year_week_day_cpp", (DL_FUNC) &_clock_invalid_any_year_week_day_cpp, 3}, {"_clock_invalid_count_iso_year_week_day_cpp", (DL_FUNC) &_clock_invalid_count_iso_year_week_day_cpp, 2}, {"_clock_invalid_count_year_day_cpp", (DL_FUNC) &_clock_invalid_count_year_day_cpp, 2}, {"_clock_invalid_count_year_month_day_cpp", (DL_FUNC) &_clock_invalid_count_year_month_day_cpp, 3}, {"_clock_invalid_count_year_month_weekday_cpp", (DL_FUNC) &_clock_invalid_count_year_month_weekday_cpp, 4}, {"_clock_invalid_count_year_quarter_day_cpp", (DL_FUNC) &_clock_invalid_count_year_quarter_day_cpp, 4}, {"_clock_invalid_count_year_week_day_cpp", (DL_FUNC) &_clock_invalid_count_year_week_day_cpp, 3}, {"_clock_invalid_detect_iso_year_week_day_cpp", (DL_FUNC) &_clock_invalid_detect_iso_year_week_day_cpp, 2}, {"_clock_invalid_detect_year_day_cpp", (DL_FUNC) &_clock_invalid_detect_year_day_cpp, 2}, {"_clock_invalid_detect_year_month_day_cpp", (DL_FUNC) &_clock_invalid_detect_year_month_day_cpp, 3}, {"_clock_invalid_detect_year_month_weekday_cpp", (DL_FUNC) &_clock_invalid_detect_year_month_weekday_cpp, 4}, {"_clock_invalid_detect_year_quarter_day_cpp", (DL_FUNC) &_clock_invalid_detect_year_quarter_day_cpp, 4}, {"_clock_invalid_detect_year_week_day_cpp", (DL_FUNC) &_clock_invalid_detect_year_week_day_cpp, 3}, {"_clock_invalid_resolve_iso_year_week_day_cpp", (DL_FUNC) &_clock_invalid_resolve_iso_year_week_day_cpp, 4}, {"_clock_invalid_resolve_year_day_cpp", (DL_FUNC) &_clock_invalid_resolve_year_day_cpp, 4}, {"_clock_invalid_resolve_year_month_day_cpp", (DL_FUNC) &_clock_invalid_resolve_year_month_day_cpp, 4}, {"_clock_invalid_resolve_year_month_weekday_cpp", (DL_FUNC) &_clock_invalid_resolve_year_month_weekday_cpp, 4}, {"_clock_invalid_resolve_year_quarter_day_cpp", (DL_FUNC) &_clock_invalid_resolve_year_quarter_day_cpp, 5}, {"_clock_invalid_resolve_year_week_day_cpp", (DL_FUNC) &_clock_invalid_resolve_year_week_day_cpp, 5}, {"_clock_iso_year_week_day_leap_year_cpp", (DL_FUNC) &_clock_iso_year_week_day_leap_year_cpp, 1}, {"_clock_iso_year_week_day_minus_iso_year_week_day_cpp", (DL_FUNC) &_clock_iso_year_week_day_minus_iso_year_week_day_cpp, 3}, {"_clock_iso_year_week_day_plus_years_cpp", (DL_FUNC) &_clock_iso_year_week_day_plus_years_cpp, 2}, {"_clock_iso_year_week_day_restore", (DL_FUNC) &_clock_iso_year_week_day_restore, 2}, {"_clock_naive_time_info_cpp", (DL_FUNC) &_clock_naive_time_info_cpp, 3}, {"_clock_new_duration_from_fields", (DL_FUNC) &_clock_new_duration_from_fields, 3}, {"_clock_new_iso_year_week_day_from_fields", (DL_FUNC) &_clock_new_iso_year_week_day_from_fields, 3}, {"_clock_new_time_point_from_fields", (DL_FUNC) &_clock_new_time_point_from_fields, 4}, {"_clock_new_year_day_from_fields", (DL_FUNC) &_clock_new_year_day_from_fields, 3}, {"_clock_new_year_month_day_from_fields", (DL_FUNC) &_clock_new_year_month_day_from_fields, 3}, {"_clock_new_year_month_weekday_from_fields", (DL_FUNC) &_clock_new_year_month_weekday_from_fields, 3}, {"_clock_new_year_quarter_day_from_fields", (DL_FUNC) &_clock_new_year_quarter_day_from_fields, 4}, {"_clock_new_year_week_day_from_fields", (DL_FUNC) &_clock_new_year_week_day_from_fields, 4}, {"_clock_new_zoned_time_from_fields", (DL_FUNC) &_clock_new_zoned_time_from_fields, 4}, {"_clock_precision_to_string", (DL_FUNC) &_clock_precision_to_string, 1}, {"_clock_sys_time_info_cpp", (DL_FUNC) &_clock_sys_time_info_cpp, 3}, {"_clock_sys_time_now_cpp", (DL_FUNC) &_clock_sys_time_now_cpp, 0}, {"_clock_time_point_parse_cpp", (DL_FUNC) &_clock_time_point_parse_cpp, 10}, {"_clock_time_point_restore", (DL_FUNC) &_clock_time_point_restore, 2}, {"_clock_to_sys_duration_fields_from_sys_seconds_cpp", (DL_FUNC) &_clock_to_sys_duration_fields_from_sys_seconds_cpp, 1}, {"_clock_to_sys_seconds_from_sys_duration_fields_cpp", (DL_FUNC) &_clock_to_sys_seconds_from_sys_duration_fields_cpp, 1}, {"_clock_weekday_add_days_cpp", (DL_FUNC) &_clock_weekday_add_days_cpp, 2}, {"_clock_weekday_from_time_point_cpp", (DL_FUNC) &_clock_weekday_from_time_point_cpp, 1}, {"_clock_weekday_minus_weekday_cpp", (DL_FUNC) &_clock_weekday_minus_weekday_cpp, 2}, {"_clock_year_day_minus_year_day_cpp", (DL_FUNC) &_clock_year_day_minus_year_day_cpp, 3}, {"_clock_year_day_plus_years_cpp", (DL_FUNC) &_clock_year_day_plus_years_cpp, 2}, {"_clock_year_day_restore", (DL_FUNC) &_clock_year_day_restore, 2}, {"_clock_year_month_day_minus_year_month_day_cpp", (DL_FUNC) &_clock_year_month_day_minus_year_month_day_cpp, 3}, {"_clock_year_month_day_parse_cpp", (DL_FUNC) &_clock_year_month_day_parse_cpp, 9}, {"_clock_year_month_day_plus_months_cpp", (DL_FUNC) &_clock_year_month_day_plus_months_cpp, 3}, {"_clock_year_month_day_plus_years_cpp", (DL_FUNC) &_clock_year_month_day_plus_years_cpp, 2}, {"_clock_year_month_day_restore", (DL_FUNC) &_clock_year_month_day_restore, 2}, {"_clock_year_month_weekday_minus_year_month_weekday_cpp", (DL_FUNC) &_clock_year_month_weekday_minus_year_month_weekday_cpp, 3}, {"_clock_year_month_weekday_plus_months_cpp", (DL_FUNC) &_clock_year_month_weekday_plus_months_cpp, 3}, {"_clock_year_month_weekday_plus_years_cpp", (DL_FUNC) &_clock_year_month_weekday_plus_years_cpp, 2}, {"_clock_year_month_weekday_restore", (DL_FUNC) &_clock_year_month_weekday_restore, 2}, {"_clock_year_quarter_day_leap_year_cpp", (DL_FUNC) &_clock_year_quarter_day_leap_year_cpp, 2}, {"_clock_year_quarter_day_minus_year_quarter_day_cpp", (DL_FUNC) &_clock_year_quarter_day_minus_year_quarter_day_cpp, 4}, {"_clock_year_quarter_day_plus_quarters_cpp", (DL_FUNC) &_clock_year_quarter_day_plus_quarters_cpp, 4}, {"_clock_year_quarter_day_plus_years_cpp", (DL_FUNC) &_clock_year_quarter_day_plus_years_cpp, 3}, {"_clock_year_quarter_day_restore", (DL_FUNC) &_clock_year_quarter_day_restore, 2}, {"_clock_year_week_day_leap_year_cpp", (DL_FUNC) &_clock_year_week_day_leap_year_cpp, 2}, {"_clock_year_week_day_minus_year_week_day_cpp", (DL_FUNC) &_clock_year_week_day_minus_year_week_day_cpp, 4}, {"_clock_year_week_day_plus_years_cpp", (DL_FUNC) &_clock_year_week_day_plus_years_cpp, 3}, {"_clock_year_week_day_restore", (DL_FUNC) &_clock_year_week_day_restore, 2}, {"_clock_zone_current", (DL_FUNC) &_clock_zone_current, 0}, {"_clock_zone_is_valid", (DL_FUNC) &_clock_zone_is_valid, 1}, {"_clock_zoned_time_parse_abbrev_cpp", (DL_FUNC) &_clock_zoned_time_parse_abbrev_cpp, 10}, {"_clock_zoned_time_parse_complete_cpp", (DL_FUNC) &_clock_zoned_time_parse_complete_cpp, 9}, {"_clock_zoned_time_restore", (DL_FUNC) &_clock_zoned_time_restore, 2}, {NULL, NULL, 0} }; } extern "C" attribute_visible void R_init_clock(DllInfo* dll){ R_registerRoutines(dll, NULL, CallEntries, NULL, NULL); R_useDynamicSymbols(dll, FALSE); R_forceSymbols(dll, TRUE); } clock/src/fill.h0000644000176200001440000000331314422225070013231 0ustar liggesusers#ifndef CLOCK_FILL_H #define CLOCK_FILL_H #include "clock.h" #include "enums.h" #include // ----------------------------------------------------------------------------- static inline std::pair fill_weekday_names(const cpp11::strings& weekday, const cpp11::strings& weekday_abbrev, std::string (&weekday_names)[14]) { for (int i = 0; i < 7; ++i) { SEXP string = weekday[i]; weekday_names[i] = std::string{CHAR(string)}; } for (int i = 0; i < 7; ++i) { SEXP string = weekday_abbrev[i]; weekday_names[i + 7] = std::string{CHAR(string)}; } return std::make_pair(weekday_names, weekday_names+sizeof(weekday_names)/sizeof(weekday_names[0])); } static inline std::pair fill_month_names(const cpp11::strings& month, const cpp11::strings& month_abbrev, std::string (&month_names)[24]) { for (int i = 0; i < 12; ++i) { SEXP string = month[i]; month_names[i] = std::string{CHAR(string)}; } for (int i = 0; i < 12; ++i) { SEXP string = month_abbrev[i]; month_names[i + 12] = std::string{CHAR(string)}; } return std::make_pair(month_names, month_names+sizeof(month_names)/sizeof(month_names[0])); } static inline std::pair fill_ampm_names(const cpp11::strings& am_pm, std::string (&m_names)[2]) { for (int i = 0; i < 2; ++i) { SEXP string = am_pm[i]; ampm_names[i] = std::string{CHAR(string)}; } return std::make_pair(ampm_names, ampm_names+sizeof(ampm_names)/sizeof(ampm_names[0])); } // ----------------------------------------------------------------------------- #endif clock/src/gregorian-year-day.h0000644000176200001440000004731314427166444016017 0ustar liggesusers#ifndef CLOCK_GREGORIAN_YEAR_DAY_H #define CLOCK_GREGORIAN_YEAR_DAY_H #include "clock.h" #include "integers.h" #include "enums.h" #include "utils.h" #include "stream.h" #include "resolve.h" namespace rclock { namespace yearday { namespace detail { inline ordinal::year_yearday resolve_next_day_yyd(const ordinal::year_yearday& x) { return {x.year() + ordinal::years{1}, ordinal::yearday{1}}; } inline ordinal::year_yearday resolve_previous_day_yyd(const ordinal::year_yearday& x) { return {x.year(), ordinal::yearday{365}}; } inline std::ostringstream& stream_year(std::ostringstream& os, int year) NOEXCEPT { os << ordinal::year{year}; return os; } inline std::ostringstream& stream_day(std::ostringstream& os, int day) NOEXCEPT { os << ordinal::yearday{static_cast(day)}; return os; } } // namespace detail class y { protected: rclock::integers year_; public: y(r_ssize size); y(const cpp11::integers& year); bool is_na(r_ssize i) const NOEXCEPT; r_ssize size() const NOEXCEPT; std::ostringstream& stream(std::ostringstream&, r_ssize i) const NOEXCEPT; void add(const date::years& x, r_ssize i) NOEXCEPT; void assign_year(const ordinal::year& x, r_ssize i) NOEXCEPT; void assign_na(r_ssize i) NOEXCEPT; ordinal::year to_year(r_ssize i) const NOEXCEPT; cpp11::writable::list to_list() const; }; class yyd : public y { protected: rclock::integers day_; public: yyd(r_ssize size); yyd(const cpp11::integers& year, const cpp11::integers& day); std::ostringstream& stream(std::ostringstream&, r_ssize i) const NOEXCEPT; void assign_day(const ordinal::yearday& x, r_ssize i) NOEXCEPT; void assign_year_yearday(const ordinal::year_yearday& x, r_ssize i) NOEXCEPT; void assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT; void assign_na(r_ssize i) NOEXCEPT; void resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call); date::sys_time to_sys_time(r_ssize i) const NOEXCEPT; ordinal::year_yearday to_year_yearday(r_ssize i) const NOEXCEPT; cpp11::writable::list to_list() const; }; class yydh : public yyd { protected: rclock::integers hour_; public: yydh(r_ssize size); yydh(const cpp11::integers& year, const cpp11::integers& day, const cpp11::integers& hour); std::ostringstream& stream(std::ostringstream&, r_ssize i) const NOEXCEPT; void assign_hour(const std::chrono::hours& x, r_ssize i) NOEXCEPT; void assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT; void assign_na(r_ssize i) NOEXCEPT; void resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call); date::sys_time to_sys_time(r_ssize i) const NOEXCEPT; cpp11::writable::list to_list() const; }; class yydhm : public yydh { protected: rclock::integers minute_; public: yydhm(r_ssize size); yydhm(const cpp11::integers& year, const cpp11::integers& day, const cpp11::integers& hour, const cpp11::integers& minute); std::ostringstream& stream(std::ostringstream&, r_ssize i) const NOEXCEPT; void assign_minute(const std::chrono::minutes& x, r_ssize i) NOEXCEPT; void assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT; void assign_na(r_ssize i) NOEXCEPT; void resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call); date::sys_time to_sys_time(r_ssize i) const NOEXCEPT; cpp11::writable::list to_list() const; }; class yydhms : public yydhm { protected: rclock::integers second_; public: yydhms(r_ssize size); yydhms(const cpp11::integers& year, const cpp11::integers& day, const cpp11::integers& hour, const cpp11::integers& minute, const cpp11::integers& second); std::ostringstream& stream(std::ostringstream&, r_ssize i) const NOEXCEPT; void assign_second(const std::chrono::seconds& x, r_ssize i) NOEXCEPT; void assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT; void assign_na(r_ssize i) NOEXCEPT; void resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call); date::sys_time to_sys_time(r_ssize i) const NOEXCEPT; cpp11::writable::list to_list() const; }; template class yydhmss : public yydhms { protected: rclock::integers subsecond_; public: yydhmss(r_ssize size); yydhmss(const cpp11::integers& year, const cpp11::integers& day, const cpp11::integers& hour, const cpp11::integers& minute, const cpp11::integers& second, const cpp11::integers& subsecond); std::ostringstream& stream(std::ostringstream&, r_ssize i) const NOEXCEPT; void assign_subsecond(const Duration& x, r_ssize i) NOEXCEPT; void assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT; void assign_na(r_ssize i) NOEXCEPT; void resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call); date::sys_time to_sys_time(r_ssize i) const NOEXCEPT; cpp11::writable::list to_list() const; using duration = Duration; }; // Implementation // y inline y::y(r_ssize size) : year_(size) {} inline y::y(const cpp11::integers& year) : year_(rclock::integers(year)) {} inline bool y::is_na(r_ssize i) const NOEXCEPT { return year_.is_na(i); } inline r_ssize y::size() const NOEXCEPT { return year_.size(); } inline std::ostringstream& y::stream(std::ostringstream& os, r_ssize i) const NOEXCEPT { rclock::detail::stream_year(os, year_[i]); return os; } inline void y::add(const date::years& x, r_ssize i) NOEXCEPT { assign_year(to_year(i) + x, i); } inline void y::assign_year(const ordinal::year& x, r_ssize i) NOEXCEPT { year_.assign(static_cast(x), i); } inline void y::assign_na(r_ssize i) NOEXCEPT { year_.assign_na(i); } inline ordinal::year y::to_year(r_ssize i) const NOEXCEPT { return ordinal::year{year_[i]}; } inline cpp11::writable::list y::to_list() const { cpp11::writable::list out({year_.sexp()}); out.names() = {"year"}; return out; } // yyd inline yyd::yyd(r_ssize size) : y(size), day_(size) {} inline yyd::yyd(const cpp11::integers& year, const cpp11::integers& day) : y(year), day_(day) {} inline std::ostringstream& yyd::stream(std::ostringstream& os, r_ssize i) const NOEXCEPT { y::stream(os, i); os << '-'; detail::stream_day(os, day_[i]); return os; } inline void yyd::assign_day(const ordinal::yearday& x, r_ssize i) NOEXCEPT { day_.assign(static_cast(static_cast(x)), i); } inline void yyd::assign_year_yearday(const ordinal::year_yearday& x, r_ssize i) NOEXCEPT { assign_year(x.year(), i); assign_day(x.yearday(), i); } inline void yyd::assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT { ordinal::year_yearday yyd{x}; assign_year_yearday(yyd, i); } inline void yyd::assign_na(r_ssize i) NOEXCEPT { y::assign_na(i); day_.assign_na(i); } inline void yyd::resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call) { const ordinal::year_yearday elt = to_year_yearday(i); if (elt.ok()) { return; } switch (type) { case invalid::next_day: case invalid::next: { assign_year_yearday(detail::resolve_next_day_yyd(elt), i); break; } case invalid::previous_day: case invalid::previous: { assign_day(detail::resolve_previous_day_yyd(elt).yearday(), i); break; } case invalid::overflow_day: case invalid::overflow: { assign_year_yearday(date::sys_days{elt}, i); break; } case invalid::na: { assign_na(i); break; } case invalid::error: { rclock::detail::resolve_error(i, call); } } } inline date::sys_time yyd::to_sys_time(r_ssize i) const NOEXCEPT { return date::sys_time{to_year_yearday(i)}; } inline ordinal::year_yearday yyd::to_year_yearday(r_ssize i) const NOEXCEPT { return ordinal::year{year_[i]} / static_cast(day_[i]); } inline cpp11::writable::list yyd::to_list() const { cpp11::writable::list out({year_.sexp(), day_.sexp()}); out.names() = {"year", "day"}; return out; } // yydh inline yydh::yydh(r_ssize size) : yyd(size), hour_(size) {} inline yydh::yydh(const cpp11::integers& year, const cpp11::integers& day, const cpp11::integers& hour) : yyd(year, day), hour_(hour) {} inline std::ostringstream& yydh::stream(std::ostringstream& os, r_ssize i) const NOEXCEPT { yyd::stream(os, i); os << 'T'; rclock::detail::stream_hour(os, hour_[i]); return os; } inline void yydh::assign_hour(const std::chrono::hours& x, r_ssize i) NOEXCEPT { hour_.assign(x.count(), i); } inline void yydh::assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT { const date::sys_time day_point = date::floor(x); const std::chrono::hours hours = x - day_point; yyd::assign_sys_time(day_point, i); assign_hour(hours, i); } inline void yydh::assign_na(r_ssize i) NOEXCEPT { yyd::assign_na(i); hour_.assign_na(i); } inline void yydh::resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call) { const ordinal::year_yearday elt = to_year_yearday(i); if (elt.ok()) { return; } switch (type) { case invalid::next_day: assign_year_yearday(detail::resolve_next_day_yyd(elt), i); break; case invalid::next: { assign_year_yearday(detail::resolve_next_day_yyd(elt), i); assign_hour(rclock::detail::resolve_next_hour(), i); break; } case invalid::previous_day: assign_day(detail::resolve_previous_day_yyd(elt).yearday(), i); break; case invalid::previous: { assign_day(detail::resolve_previous_day_yyd(elt).yearday(), i); assign_hour(rclock::detail::resolve_previous_hour(), i); break; } case invalid::overflow_day: assign_year_yearday(date::sys_days{elt}, i); break; case invalid::overflow: { assign_year_yearday(date::sys_days{elt}, i); assign_hour(rclock::detail::resolve_next_hour(), i); break; } case invalid::na: { assign_na(i); break; } case invalid::error: { rclock::detail::resolve_error(i, call); } } } inline date::sys_time yydh::to_sys_time(r_ssize i) const NOEXCEPT { return yyd::to_sys_time(i) + std::chrono::hours{hour_[i]}; } inline cpp11::writable::list yydh::to_list() const { cpp11::writable::list out({year_.sexp(), day_.sexp(), hour_.sexp()}); out.names() = {"year", "day", "hour"}; return out; } // yydhm inline yydhm::yydhm(r_ssize size) : yydh(size), minute_(size) {} inline yydhm::yydhm(const cpp11::integers& year, const cpp11::integers& day, const cpp11::integers& hour, const cpp11::integers& minute) : yydh(year, day, hour), minute_(minute) {} inline std::ostringstream& yydhm::stream(std::ostringstream& os, r_ssize i) const NOEXCEPT { yydh::stream(os, i); os << ':'; rclock::detail::stream_minute(os, minute_[i]); return os; } inline void yydhm::assign_minute(const std::chrono::minutes& x, r_ssize i) NOEXCEPT { minute_.assign(x.count(), i); } inline void yydhm::assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT { const date::sys_time hour_point = date::floor(x); const std::chrono::minutes minutes = x - hour_point; yydh::assign_sys_time(hour_point, i); assign_minute(minutes, i); } inline void yydhm::assign_na(r_ssize i) NOEXCEPT { yydh::assign_na(i); minute_.assign_na(i); } inline void yydhm::resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call) { const ordinal::year_yearday elt = to_year_yearday(i); if (elt.ok()) { return; } switch (type) { case invalid::next_day: assign_year_yearday(detail::resolve_next_day_yyd(elt), i); break; case invalid::next: { assign_year_yearday(detail::resolve_next_day_yyd(elt), i); assign_hour(rclock::detail::resolve_next_hour(), i); assign_minute(rclock::detail::resolve_next_minute(), i); break; } case invalid::previous_day: assign_day(detail::resolve_previous_day_yyd(elt).yearday(), i); break; case invalid::previous: { assign_day(detail::resolve_previous_day_yyd(elt).yearday(), i); assign_hour(rclock::detail::resolve_previous_hour(), i); assign_minute(rclock::detail::resolve_previous_minute(), i); break; } case invalid::overflow_day: assign_year_yearday(date::sys_days{elt}, i); break; case invalid::overflow: { assign_year_yearday(date::sys_days{elt}, i); assign_hour(rclock::detail::resolve_next_hour(), i); assign_minute(rclock::detail::resolve_next_minute(), i); break; } case invalid::na: { assign_na(i); break; } case invalid::error: { rclock::detail::resolve_error(i, call); } } } inline date::sys_time yydhm::to_sys_time(r_ssize i) const NOEXCEPT { return yydh::to_sys_time(i) + std::chrono::minutes{minute_[i]}; } inline cpp11::writable::list yydhm::to_list() const { cpp11::writable::list out({year_.sexp(), day_.sexp(), hour_.sexp(), minute_.sexp()}); out.names() = {"year", "day", "hour", "minute"}; return out; } // yydhms inline yydhms::yydhms(r_ssize size) : yydhm(size), second_(size) {} inline yydhms::yydhms(const cpp11::integers& year, const cpp11::integers& day, const cpp11::integers& hour, const cpp11::integers& minute, const cpp11::integers& second) : yydhm(year, day, hour, minute), second_(second) {} inline std::ostringstream& yydhms::stream(std::ostringstream& os, r_ssize i) const NOEXCEPT { yydhm::stream(os, i); os << ':'; rclock::detail::stream_second(os, second_[i]); return os; } inline void yydhms::assign_second(const std::chrono::seconds& x, r_ssize i) NOEXCEPT { second_.assign(x.count(), i); } inline void yydhms::assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT { const date::sys_time minute_point = date::floor(x); const std::chrono::seconds seconds = x - minute_point; yydhm::assign_sys_time(minute_point, i); assign_second(seconds, i); } inline void yydhms::assign_na(r_ssize i) NOEXCEPT { yydhm::assign_na(i); second_.assign_na(i); } inline void yydhms::resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call) { const ordinal::year_yearday elt = to_year_yearday(i); if (elt.ok()) { return; } switch (type) { case invalid::next_day: assign_year_yearday(detail::resolve_next_day_yyd(elt), i); break; case invalid::next: { assign_year_yearday(detail::resolve_next_day_yyd(elt), i); assign_hour(rclock::detail::resolve_next_hour(), i); assign_minute(rclock::detail::resolve_next_minute(), i); assign_second(rclock::detail::resolve_next_second(), i); break; } case invalid::previous_day: assign_day(detail::resolve_previous_day_yyd(elt).yearday(), i); break; case invalid::previous: { assign_day(detail::resolve_previous_day_yyd(elt).yearday(), i); assign_hour(rclock::detail::resolve_previous_hour(), i); assign_minute(rclock::detail::resolve_previous_minute(), i); assign_second(rclock::detail::resolve_previous_second(), i); break; } case invalid::overflow_day: assign_year_yearday(date::sys_days{elt}, i); break; case invalid::overflow: { assign_year_yearday(date::sys_days{elt}, i); assign_hour(rclock::detail::resolve_next_hour(), i); assign_minute(rclock::detail::resolve_next_minute(), i); assign_second(rclock::detail::resolve_next_second(), i); break; } case invalid::na: { assign_na(i); break; } case invalid::error: { rclock::detail::resolve_error(i, call); } } } inline date::sys_time yydhms::to_sys_time(r_ssize i) const NOEXCEPT { return yydhm::to_sys_time(i) + std::chrono::seconds{second_[i]}; } inline cpp11::writable::list yydhms::to_list() const { cpp11::writable::list out({year_.sexp(), day_.sexp(), hour_.sexp(), minute_.sexp(), second_.sexp()}); out.names() = {"year", "day", "hour", "minute", "second"}; return out; } // yydhmss template inline yydhmss::yydhmss(r_ssize size) : yydhms(size), subsecond_(size) {} template inline yydhmss::yydhmss(const cpp11::integers& year, const cpp11::integers& day, const cpp11::integers& hour, const cpp11::integers& minute, const cpp11::integers& second, const cpp11::integers& subsecond) : yydhms(year, day, hour, minute, second), subsecond_(subsecond) {} template inline std::ostringstream& yydhmss::stream(std::ostringstream& os, r_ssize i) const NOEXCEPT { yydhm::stream(os, i); os << ':'; rclock::detail::stream_second_and_subsecond(os, second_[i], subsecond_[i]); return os; } template inline void yydhmss::assign_subsecond(const Duration& x, r_ssize i) NOEXCEPT { subsecond_.assign(x.count(), i); } template inline void yydhmss::assign_sys_time(const date::sys_time& x, r_ssize i) NOEXCEPT { const date::sys_time second_point = date::floor(x); const Duration subseconds = x - second_point; yydhms::assign_sys_time(second_point, i); assign_subsecond(subseconds, i); } template inline void yydhmss::assign_na(r_ssize i) NOEXCEPT { yydhms::assign_na(i); subsecond_.assign_na(i); } template inline void yydhmss::resolve(r_ssize i, const enum invalid type, const cpp11::sexp& call) { const ordinal::year_yearday elt = to_year_yearday(i); if (elt.ok()) { return; } switch (type) { case invalid::next_day: assign_year_yearday(detail::resolve_next_day_yyd(elt), i); break; case invalid::next: { assign_year_yearday(detail::resolve_next_day_yyd(elt), i); assign_hour(rclock::detail::resolve_next_hour(), i); assign_minute(rclock::detail::resolve_next_minute(), i); assign_second(rclock::detail::resolve_next_second(), i); assign_subsecond(rclock::detail::resolve_next_subsecond(), i); break; } case invalid::previous_day: assign_day(detail::resolve_previous_day_yyd(elt).yearday(), i); break; case invalid::previous: { assign_day(detail::resolve_previous_day_yyd(elt).yearday(), i); assign_hour(rclock::detail::resolve_previous_hour(), i); assign_minute(rclock::detail::resolve_previous_minute(), i); assign_second(rclock::detail::resolve_previous_second(), i); assign_subsecond(rclock::detail::resolve_previous_subsecond(), i); break; } case invalid::overflow_day: assign_year_yearday(date::sys_days{elt}, i); break; case invalid::overflow: { assign_year_yearday(date::sys_days{elt}, i); assign_hour(rclock::detail::resolve_next_hour(), i); assign_minute(rclock::detail::resolve_next_minute(), i); assign_second(rclock::detail::resolve_next_second(), i); assign_subsecond(rclock::detail::resolve_next_subsecond(), i); break; } case invalid::na: { assign_na(i); break; } case invalid::error: { rclock::detail::resolve_error(i, call); } } } template inline date::sys_time yydhmss::to_sys_time(r_ssize i) const NOEXCEPT { return yydhms::to_sys_time(i) + Duration{subsecond_[i]}; } template inline cpp11::writable::list yydhmss::to_list() const { cpp11::writable::list out({year_.sexp(), day_.sexp(), hour_.sexp(), minute_.sexp(), second_.sexp(), subsecond_.sexp()}); out.names() = {"year", "day", "hour", "minute", "second", "subsecond"}; return out; } } // namespace yearday } // namespace rclock #endif clock/src/enums.h0000644000176200001440000000440214422221153013430 0ustar liggesusers#ifndef CLOCK_ENUMS_H #define CLOCK_ENUMS_H #include "clock.h" #include "utils.h" // ----------------------------------------------------------------------------- enum class invalid { previous, next, overflow, previous_day, next_day, overflow_day, na, error }; enum invalid parse_invalid(const cpp11::strings& x); // ----------------------------------------------------------------------------- enum class nonexistent { roll_forward, roll_backward, shift_forward, shift_backward, na, error }; enum nonexistent parse_nonexistent_one(const cpp11::r_string& x); // ----------------------------------------------------------------------------- enum class ambiguous { earliest, latest, na, error }; enum ambiguous parse_ambiguous_one(const cpp11::r_string& x); // ----------------------------------------------------------------------------- enum class component { year, quarter, month, week, day, hour, minute, second, millisecond, microsecond, nanosecond, index }; enum component parse_component(const cpp11::strings& x); // ----------------------------------------------------------------------------- enum week::start parse_week_start(const cpp11::integers& x); // ----------------------------------------------------------------------------- enum quarterly::start parse_quarterly_start(const cpp11::integers& x); // ----------------------------------------------------------------------------- enum class precision: unsigned char { year = 0u, quarter = 1u, month = 2u, week = 3u, day = 4u, hour = 5u, minute = 6u, second = 7u, millisecond = 8u, microsecond = 9u, nanosecond = 10u }; enum precision parse_precision(const cpp11::integers& x); const std::string& precision_to_cpp_string(const enum precision& x); // ----------------------------------------------------------------------------- enum class clock_name: unsigned char { sys = 0u, naive = 1u }; enum clock_name parse_clock_name(const cpp11::integers& x); // ----------------------------------------------------------------------------- enum class decimal_mark { period, comma }; enum decimal_mark parse_decimal_mark(const cpp11::strings& x); // ----------------------------------------------------------------------------- #endif clock/vignettes/0000755000176200001440000000000014430471265013363 5ustar liggesusersclock/vignettes/clock.Rmd0000644000176200001440000004026214134036355015124 0ustar liggesusers--- title: "Getting Started" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Getting Started} %\VignetteEncoding{UTF-8} %\VignetteEngine{knitr::rmarkdown} editor_options: chunk_output_type: console --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` ```{r setup} library(clock) library(magrittr) ``` The goal of this vignette is to introduce you to clock's high-level API, which works directly on R's built-in date-time types, Date and POSIXct. For an overview of all of the functionality in the high-level API, check out the pkgdown reference section, [High Level API](https://clock.r-lib.org/reference/index.html#section-high-level-api). One thing you should immediately notice is that every function specific to R's date and date-time types are prefixed with `date_*()`. There are also additional functions for arithmetic (`add_*()`) and getting (`get_*()`) or setting (`set_*()`) components that are also used by other types in clock. As you'll quickly see in this vignette, one of the main goals of clock is to guard you, the user, from unexpected issues caused by frustrating date manipulation concepts like invalid dates and daylight saving time. It does this by letting you know as soon as one of these issues happens, giving you the power to handle it explicitly with one of a number of different resolution strategies. ## Building To create a vector of dates, you can use `date_build()`. This allows you to specify the components individually. ```{r} date_build(2019, 2, 1:5) ``` If you happen to specify an _invalid date_, you'll get an error message: ```{r, error=TRUE} date_build(2019, 1:12, 31) ``` One way to resolve this is by specifying an invalid date resolution strategy using the `invalid` argument. There are multiple options, but in this case we'll ask for the invalid dates to be set to the previous valid moment in time. ```{r} date_build(2019, 1:12, 31, invalid = "previous") ``` To learn more about invalid dates, check out the documentation for `invalid_resolve()`. If we were actually after the "last day of the month", an easier way to specify this would have been: ```{r} date_build(2019, 1:12, "last") ``` You can also create date-times using `date_time_build()`, which generates a POSIXct. Note that you must supply a time zone! ```{r} date_time_build(2019, 1:5, 1, 2, 30, zone = "America/New_York") ``` If you "build" a time that doesn't exist, you'll get an error. For example, on March 8th, 2020, there was a daylight saving time gap of 1 hour in the America/New_York time zone that took us from `01:59:59` directly to `03:00:00`, skipping the 2 o'clock hour entirely. Let's "accidentally" create a time in that gap: ```{r, error=TRUE} date_time_build(2019:2021, 3, 8, 2, 30, zone = "America/New_York") ``` To resolve this issue, we can specify a nonexistent time resolution strategy through the `nonexistent` argument. There are a number of options, including rolling forward or backward to the next or previous valid moments in time: ```{r} zone <- "America/New_York" date_time_build(2019:2021, 3, 8, 2, 30, zone = zone, nonexistent = "roll-forward") date_time_build(2019:2021, 3, 8, 2, 30, zone = zone, nonexistent = "roll-backward") ``` ## Parsing ### Parsing dates To parse dates, use `date_parse()`. Parsing dates requires a _format string_, a combination of _commands_ that specify where date components are in your string. By default, it assumes that you're working with dates in the form `"%Y-%m-%d"` (year-month-day). ```{r} date_parse("2019-01-05") ``` You can change the format string using `format`: ```{r} date_parse("January 5, 2020", format = "%B %d, %Y") ``` Various different locales are supported for parsing month and weekday names in different languages. To parse a French month: ```{r} date_parse( "juillet 10, 2021", format = "%B %d, %Y", locale = clock_locale("fr") ) ``` You can learn about more locale options in the documentation for `clock_locale()`. If you have heterogeneous dates, you can supply multiple format strings: ```{r} x <- c("2020/1/5", "10-03-05", "2020/2/2") formats <- c("%Y/%m/%d", "%y-%m-%d") date_parse(x, format = formats) ``` ### Parsing date-times You have four options when parsing date-times: - `date_time_parse()`: For strings like `"2020-01-01 01:02:03"` where there is neither a time zone offset nor a full (not abbreviated!) time zone name. - `date_time_parse_complete()`: For strings like `"2020-01-01T01:02:03-05:00[America/New_York]"` where there is both a time zone offset and time zone name present in the string. - `date_time_parse_abbrev()`: For strings like `"2020-01-01 01:02:03 EST"` where there is a time zone abbreviation in the string. - `date_time_parse_RFC_3339()`: For strings like `"2020-01-01T01:02:03Z"` or `"2020-01-01T01:02:03-05:00"`, which are in RFC 3339 format and are intended to be interpreted as UTC. #### date_time_parse() `date_time_parse()` requires a `zone` argument, and will ignore any other zone information in the string (i.e. if you tried to specify `%z` and `%Z`). The default format string is `"%Y-%m-%d %H:%M:%S"`. ```{r} date_time_parse("2020-01-01 01:02:03", "America/New_York") ``` If you happen to parse an invalid or ambiguous date-time, you'll get an error. For example, on November 1st, 2020, there were _two_ 1 o'clock hours in the America/New_York time zone due to a daylight saving time fallback. You can see that if we parse a time right before the fallback, and then shift it forward by 1 second, and then 1 hour and 1 second, respectively: ```{r} before <- date_time_parse("2020-11-01 00:59:59", "America/New_York") # First 1 o'clock before + 1 # Second 1 o'clock before + 1 + 3600 ``` The following string doesn't include any information about which of these two 1 o'clocks it belongs to, so it is considered _ambiguous_. Ambiguous times will error when parsing: ```{r, error=TRUE} date_time_parse("2020-11-01 01:30:00", "America/New_York") ``` To fix that, you can specify an ambiguous time resolution strategy with the `ambiguous` argument. ```{r} zone <- "America/New_York" date_time_parse("2020-11-01 01:30:00", zone, ambiguous = "earliest") date_time_parse("2020-11-01 01:30:00", zone, ambiguous = "latest") ``` #### date_time_parse_complete() `date_time_parse_complete()` doesn't have a `zone` argument, and doesn't require `ambiguous` or `nonexistent` arguments, since it assumes that the string you are providing is completely unambiguous. The only way this is possible is by having both a time zone offset, specified by `%z`, and a full time zone name, specified by `%Z`, in the string. The following is an example of an "extended" RFC 3339 format used by Java 8's time library to specify complete date-time strings. This is something that `date_time_parse_complete()` can parse. The default format string follows this extended format, and is `"%Y-%m-%dT%H:%M:%S%z[%Z]"`. ```{r} x <- "2020-01-01T01:02:03-05:00[America/New_York]" date_time_parse_complete(x) ``` #### date_time_parse_abbrev() `date_time_parse_abbrev()` is useful when your date-time strings contain a time zone abbreviation rather than a time zone offset or full time zone name. ```{r} x <- "2020-01-01 01:02:03 EST" date_time_parse_abbrev(x, "America/New_York") ``` The string is first parsed as a naive time without considering the abbreviation, and is then converted to a zoned-time using the supplied `zone`. If an ambiguous time is parsed, the abbreviation is used to resolve the ambiguity. ```{r} x <- c( "1970-10-25 01:30:00 EDT", "1970-10-25 01:30:00 EST" ) date_time_parse_abbrev(x, "America/New_York") ``` You might be wondering why you need to supply `zone` at all. Isn't the abbreviation enough? Unfortunately, multiple countries use the same time zone abbreviations, even though they have different time zones. This means that, in many cases, the abbreviation alone is ambiguous. For example, both India and Israel use `IST` for their standard times. ```{r} x <- "1970-01-01 02:30:30 IST" # IST = India Standard Time date_time_parse_abbrev(x, "Asia/Kolkata") # IST = Israel Standard Time date_time_parse_abbrev(x, "Asia/Jerusalem") ``` #### date_time_parse_RFC_3339() `date_time_parse_RFC_3339()` is useful when your date-time strings come from an API, which means they are likely in an ISO 8601 or RFC 3339 format, and should be interpreted as UTC. The default format string parses the typical RFC 3339 format of `"%Y-%m-%dT%H:%M:%SZ"`. ```{r} x <- "2020-01-01T01:02:03Z" date_time_parse_RFC_3339(x) ``` If your date-time strings contain a numeric offset from UTC rather than a `"Z"`, then you'll need to set the `offset` argument to one of the following: - `"%z"` if the offset is of the form `"-0500"`. - `"%Ez"` if the offset is of the form `"-05:00"`. ```{r} x <- "2020-01-01T01:02:03-0500" date_time_parse_RFC_3339(x, offset = "%z") x <- "2020-01-01T01:02:03-05:00" date_time_parse_RFC_3339(x, offset = "%Ez") ``` ## Grouping, rounding and shifting When performing time-series related data analysis, you often need to summarize your series at a less precise precision. There are many different ways to do this, and the differences between them are subtle, but meaningful. clock offers three different sets of functions for summarization: - `date_group()` - `date_floor()`, `date_ceiling()`, and `date_round()` - `date_shift()` ### Grouping Grouping allows you to summarize a component of a date or date-time _within_ other components. An example of this is grouping by day of the month, which summarizes the day component _within_ the current year-month. ```{r} x <- seq(date_build(2019, 1, 20), date_build(2019, 2, 5), by = 1) x # Grouping by 5 days of the current month date_group(x, "day", n = 5) ``` The thing to note about grouping by day of the month is that at the end of each month, the groups restart. So this created groups for January of `[1, 5], [6, 10], [11, 15], [16, 20], [21, 25], [26, 30], [31]`. You can also group by month or year: ```{r} date_group(x, "month") ``` This also works with date-times, adding the ability to group by hour of the day, minute of the hour, and second of the minute. ```{r} x <- seq( date_time_build(2019, 1, 1, 1, 55, zone = "UTC"), date_time_build(2019, 1, 1, 2, 15, zone = "UTC"), by = 120 ) x date_group(x, "minute", n = 5) ``` ### Rounding While grouping is useful for summarizing _within_ a component, rounding is useful for summarizing _across_ components. It is great for summarizing by, say, a rolling set of 60 days. Rounding operates on the underlying count that makes up your date or date-time. To see what I mean by this, try unclassing a date: ```{r} unclass(date_build(2020, 1, 1)) ``` This is a count of days since the _origin_ that R uses, 1970-01-01, which is considered day 0. If you were to floor by 60 days, this would bundle `[1970-01-01, 1970-03-02), [1970-03-02, 1970-05-01)`, and so on. Equivalently, it bundles counts of `[0, 60), [60, 120)`, etc. ```{r} x <- seq(date_build(1970, 01, 01), date_build(1970, 05, 10), by = 20) date_floor(x, "day", n = 60) date_ceiling(x, "day", n = 60) ``` If you prefer a different origin, you can supply a Date `origin` to `date_floor()`, which determines what "day 0" is considered to be. This can be useful for grouping by multiple weeks if you want to control what is considered the start of the week. Since 1970-01-01 is a Thursday, flooring by 2 weeks would normally generate all Thursdays: ```{r} as_weekday(date_floor(x, "week", n = 14)) ``` To change this you can supply an `origin` on the weekday that you'd like to be considered the first day of the week. ```{r} sunday <- date_build(1970, 01, 04) date_floor(x, "week", n = 14, origin = sunday) as_weekday(date_floor(x, "week", n = 14, origin = sunday)) ``` If you only need to floor by 1 week, it is often easier to use `date_shift()`, as seen in the next section. ### Shifting `date_shift()` allows you to target a weekday, and then shift a vector of dates forward or backward to the next instance of that target. It requires using one of the new types in clock, _weekday_, which is supplied as the target. For example, to shift to the next Tuesday: ```{r} x <- date_build(2020, 1, 1:2) # Wednesday / Thursday as_weekday(x) # `clock_weekdays` is a helper that returns the code corresponding to # the requested day of the week clock_weekdays$tuesday tuesday <- weekday(clock_weekdays$tuesday) tuesday date_shift(x, target = tuesday) ``` Shifting to the _previous_ day of the week is a nice way to floor by 1 week. It allows you to control the start of the week in a way that is slightly easier than using `date_floor(origin = )`. ```{r} x <- seq(date_build(1970, 01, 01), date_build(1970, 01, "last"), by = 3) date_shift(x, tuesday, which = "previous") ``` ## Arithmetic You can do arithmetic with dates and date-times using the family of `add_*()` functions. With dates, you can add years, months, and days. With date-times, you can additionally add hours, minutes, and seconds. ```{r} x <- date_build(2020, 1, 1) add_years(x, 1:5) ``` One of the neat parts about clock is that it requires you to be explicit about how you want to handle invalid dates when doing arithmetic. What is 1 month after January 31st? If you try and create this date, you'll get an error. ```{r, error=TRUE} x <- date_build(2020, 1, 31) add_months(x, 1) ``` clock gives you the power to handle this through the `invalid` option: ```{r} # The previous valid moment in time add_months(x, 1, invalid = "previous") # The next valid moment in time add_months(x, 1, invalid = "next") # Overflow the days. There were 29 days in February, 2020, but we # specified 31. So this overflows 2 days past day 29. add_months(x, 1, invalid = "overflow") # If you don't consider it to be a valid date add_months(x, 1, invalid = "NA") ``` As a teaser, the low level library has a _calendar_ type named year-month-day that powers this operation. It actually gives you _more_ flexibility, allowing `"2020-02-31"` to exist in the wild: ```{r} ymd <- as_year_month_day(x) + duration_months(1) ymd ``` You can use `invalid_resolve(invalid =)` to resolve this like you did in `add_months()`, or you can let it hang around if you expect other operations to make it "valid" again. ```{r} # Adding 1 more month makes it valid again ymd + duration_months(1) ``` When working with date-times, you can additionally add hours, minutes, and seconds. ```{r} x <- date_time_build(2020, 1, 1, 2, 30, zone = "America/New_York") x %>% add_days(1) %>% add_hours(2:5) ``` When adding units of time to a POSIXct, you have to be very careful with daylight saving time issues. clock tries to help you out by letting you know when you run into an issue: ```{r, error=TRUE} x <- date_time_build(1970, 04, 25, 02, 30, 00, zone = "America/New_York") x # Daylight saving time gap on the 26th between 01:59:59 -> 03:00:00 x %>% add_days(1) ``` You can solve this using the `nonexistent` argument to control how these times should be handled. ```{r} # Roll forward to the next valid moment in time x %>% add_days(1, nonexistent = "roll-forward") # Roll backward to the previous valid moment in time x %>% add_days(1, nonexistent = "roll-backward") # Shift forward by adding the size of the DST gap # (this often keeps the time of day, # but doesn't guaratee that relative ordering in `x` is maintained # so I don't recommend it) x %>% add_days(1, nonexistent = "shift-forward") # Replace nonexistent times with an NA x %>% add_days(1, nonexistent = "NA") ``` ## Getting and setting clock provides a family of getters and setters for working with dates and date-times. You can get and set the year, month, or day of a date. ```{r} x <- date_build(2019, 5, 6) get_year(x) get_month(x) get_day(x) x %>% set_day(22) %>% set_month(10) ``` As you might expect by now, setting the date to an invalid date requires you to explicitly handle this: ```{r, error=TRUE} x %>% set_day(31) %>% set_month(4) x %>% set_day(31) %>% set_month(4, invalid = "previous") ``` You can additionally set the hour, minute, and second of a POSIXct. ```{r} x <- date_time_build(2020, 1, 2, 3, zone = "America/New_York") x x %>% set_minute(5) %>% set_second(10) ``` As with other manipulations of POSIXct, you'll have to be aware of daylight saving time when setting components. You may need to supply the `nonexistent` or `ambiguous` arguments of the `set_*()` functions to handle these issues. clock/vignettes/faq.Rmd0000644000176200001440000003454214423751216014605 0ustar liggesusers--- title: "Frequently Asked Questions" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Frequently Asked Questions} %\VignetteEncoding{UTF-8} %\VignetteEngine{knitr::rmarkdown} editor_options: chunk_output_type: console --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` ```{r setup} library(clock) library(magrittr) ``` ## Why can't I do day arithmetic on a year-month-day? It might seem intuitive that since you can do: ```{r} x <- year_month_day(2019, 1, 5) add_months(x, 1) ``` That you should also be able to do: ```{r, error=TRUE} add_days(x, 1) ``` Generally, calendars don't support day based arithmetic, nor do they support arithmetic at more precise precisions than day. Instead, you have to convert to a time point, do the arithmetic there, and then convert back (if you still need a year-month-day after that). ```{r} x %>% as_naive_time() %>% add_days(1) %>% as_year_month_day() ``` The first reason for this is performance. A year-month-day is a *field* type, implemented as multiple parallel vectors holding the year, month, day, and all other components separately. There are two ways that day based arithmetic could be implemented for this: - Increment the day field, then check the year and month field to see if they need to be incremented, accounting for months having a differing number of days, and leap years. - Convert to naive-time, add days, convert back. Both approaches are relatively expensive. One of the goals of the low-level API of clock is to make these expensive operations explicit. This helps make it apparent that when you need to chain together multiple operations, you should try and do all of your *calendrical* arithmetic steps first, then convert to a time point (i.e. the second bullet point from above) to do all of your *chronological* arithmetic. The second reason for this has to do with invalid dates, such as the three in this vector: ```{r} odd_dates <- year_month_day(2019, 2, 28:31) odd_dates ``` What does it mean to "add 1 day" to these? There is no obvious answer to this question. Since clock requires that you first convert to a time point to do day based arithmetic, you'll be forced to call `invalid_resolve()` to handle these invalid dates first. After resolving them manually, then day based arithmetic again makes sense. ```{r} odd_dates %>% invalid_resolve(invalid = "next") odd_dates %>% invalid_resolve(invalid = "next") %>% as_naive_time() %>% add_days(2) odd_dates %>% invalid_resolve(invalid = "overflow") odd_dates %>% invalid_resolve(invalid = "overflow") %>% as_naive_time() %>% add_days(2) ``` ## Why can't I add time to a zoned-time? If you have a zoned-time, such as: ```{r} x <- zoned_time_parse_complete("1970-04-26T01:30:00-05:00[America/New_York]") x ``` You might wonder why you can't add any units of time to it: ```{r, error=TRUE} add_days(x, 1) add_seconds(x, 1) ``` In clock, you can't do much with zoned-times directly. The best way to understand this is to think of a zoned-time as containing 3 things: a sys-time, a naive-time, and a time zone name. You can access those things with: ```{r} x # The printed time with no time zone info as_naive_time(x) # The equivalent time in UTC as_sys_time(x) zoned_time_zone(x) ``` Calling `add_days()` on a zoned-time is then an ambiguous operation. Should we add to the sys-time or the naive-time that is contained in the zoned-time? The answer changes depending on the scenario. Because of this, you have to extract out the relevant time point that you care about, operate on that, and then convert back to zoned-time. This often produces the same result: ```{r} x %>% as_naive_time() %>% add_seconds(1) %>% as_zoned_time(zoned_time_zone(x)) x %>% as_sys_time() %>% add_seconds(1) %>% as_zoned_time(zoned_time_zone(x)) ``` But not always! When daylight saving time is involved, the choice of sys-time or naive-time matters. Let's try adding 30 minutes: ```{r, error=TRUE} # There is a DST gap 1 second after 01:59:59, # which jumps us straight to 03:00:00, # skipping the 2 o'clock hour entirely x %>% as_naive_time() %>% add_minutes(30) %>% as_zoned_time(zoned_time_zone(x)) x %>% as_sys_time() %>% add_minutes(30) %>% as_zoned_time(zoned_time_zone(x)) ``` When adding to the naive-time, we got an error. With the sys-time, everything seems okay. What happened? The sys-time scenario is easy to explain. Technically this converts to UTC, adds the time there, then converts back to your time zone. An easier way to think about this is that you sat in front of your computer for exactly 30 minutes (1800 seconds), then looked at the clock. Assuming that that clock automatically changes itself correctly for daylight saving time, it should read 3 o'clock. The naive-time scenario makes more sense if you break down the steps. First, we convert to naive-time, dropping all time zone information but keeping the printed time: ```{r} x x %>% as_naive_time() ``` We add 30 minutes to this. Because we don't have any time zone information, this lands us at 2 o'clock, which isn't an issue when working with naive-time: ```{r} x %>% as_naive_time() %>% add_minutes(30) ``` Finally, we convert back to zoned-time. If possible, this tries to keep the printed time, and just attaches the relevant time zone onto it. However, in this case that isn't possible, since 2 o'clock didn't exist in this time zone! This *nonexistent time* must be handled explicitly by setting the `nonexistent` argument of `as_zoned_time()`. We can choose from a variety of strategies to handle nonexistent times, but here we just roll forward to the next valid moment in time. ```{r} x %>% as_naive_time() %>% add_minutes(30) %>% as_zoned_time(zoned_time_zone(x), nonexistent = "roll-forward") ``` As a general rule, it often makes the most sense to add: - Years, quarters, and months to a *calendar*. - Weeks and days to a *naive time*. - Hours, minutes, seconds, and subseconds to a *sys time*. This is what the high-level API for POSIXct does. However, this isn't always what you want, so the low-level API requires you to be more explicit. ## Where did my POSIXct subseconds go? ```{r} old <- options(digits.secs = 6, digits = 22) ``` Consider the following POSIXct: ```{r} x <- as.POSIXct("2019-01-01 01:00:00.2", "America/New_York") x ``` It looks like there is some fractional second information here, but converting it to naive-time drops it: ```{r} as_naive_time(x) ``` This is purposeful. clock treats POSIXct as a *second precision* data type. The reason for this has to do with the fact that POSIXct is implemented as a vector of doubles, which have a limit to how precisely they can store information. For example, try parsing a slightly smaller or larger fractional second: ```{r} y <- as.POSIXct( c("2019-01-01 01:00:00.1", "2019-01-01 01:00:00.3"), "America/New_York" ) # Oh dear! y ``` It isn't printing correctly, at the very least. Let's look under the hood: ```{r} unclass(y) ``` Double vectors have a limit to how much precision they can represent, and this is bumping up against that limit. So our `.1` seconds is instead represented as `.099999etc`. This precision loss gets worse the farther we get from the epoch, 1970-01-01, represented as `0` under the hood. For example, here we'll use a number of seconds that represents the year 2050, and add 5 microseconds to it: ```{r} new_utc <- function(x) { class(x) <- c("POSIXct", "POSIXt") attr(x, "tzone") <- "UTC" x } year_2050 <- 2524608000 five_microseconds <- 0.000005 new_utc(year_2050) # Oh no! new_utc(year_2050 + five_microseconds) # Represented internally as: year_2050 + five_microseconds ``` Because of these issues, clock treats POSIXct as a second precision data type, dropping all other information. Instead, you should parse directly into a subsecond clock type: ```{r} naive_time_parse( c("2019-01-01T01:00:00.1", "2019-01-01T01:00:00.3"), precision = "millisecond" ) %>% as_zoned_time("America/New_York") ``` ```{r} # Reset old options options(old) ``` ## What is the time zone of Date? In clock, R's native Date type is actually assumed to be *naive*, i.e. clock assumes that there is a yet-to-be-specified time zone, like with a naive-time. The other possibility is to assume that Date is UTC (like sys-time), but it is often more intuitive for Dates to be naive when manipulating them and converting them to zoned-time or POSIXct. R does not consistently treat Dates as naive or UTC. Instead it switches between them, depending on the function. For example, the Date method of `as.POSIXct()` does not expose a `tz` argument. Instead, it assumes that Date is UTC, and that the result should be shown in local time (as defined by `Sys.timezone()`). This often results in confusing behavior, such as: ```{r} x <- as.Date("2019-01-01") x withr::with_timezone("America/New_York", { print(as.POSIXct(x)) }) ``` With clock, converting to zoned-time from Date will always assume that Date is naive, which will keep the printed date (if possible) and show it in the `zone` you specified. ```{r} as_zoned_time(x, "UTC") as_zoned_time(x, "America/New_York") as_zoned_time(x, "Europe/London") ``` On the other hand, the POSIXct method for `as.Date()` treats Date as a naive type. This is probably what you want, and this example just shows the inconsistency. It is a bit hard to see this, because the `tz` argument of the method defaults to `"UTC"`, but if you set the `tz` argument to the zone of your input, it becomes clear: ```{r} x <- as.POSIXct("2019-01-01 23:00:00", "America/New_York") as.Date(x, tz = date_time_zone(x)) ``` If this assumed that Date was UTC, then it would have resulted in something like: ```{r} utc <- date_time_set_zone(x, "UTC") utc as.Date(utc, tz = date_time_zone(utc)) ``` ## What does clock do with leap seconds? clock currently handles leap seconds in the same way that base R's date-time (POSIXct) class does - it ignores them entirely. While `strptime()` has some very simple capabilities for parsing leap seconds, clock doesn't allow them at all: ```{r, warning=TRUE} raw <- c( "2015-12-31T23:59:59", "2015-12-31T23:59:60", # A real leap second! "2016-01-01T00:00:00" ) x <- sys_time_parse(raw) x ``` ```{r} # Reported as exactly 1 second apart. # In real life these are 2 seconds apart because of the leap second. x[[3]] - x[[1]] ``` Because none of the clock types handle leap seconds, clock currently doesn't offer a way to parse them. Your current best option if you *really* need to parse leap seconds is to use `strptime()`: ```{r} # This returns a POSIXlt, which can handle the special 60s field x <- strptime(raw, format = "%Y-%m-%dT%H:%M:%S", tz = "UTC") x # On conversion to POSIXct, it "rolls" forward as.POSIXct(x) ``` `strptime()` isn't a great solution though, because the parsing is fairly simple. If you try to use a "fake" leap second, it will still accept it, even though it isn't a real time: ```{r} # 2016-12-31 wasn't a leap second date, but it still tries to parse this fake time strptime("2016-12-31T23:59:60", format = "%Y-%m-%dT%H:%M:%S", tz = "UTC") ``` A true solution would check this against a database of actual leap seconds, and would only successfully parse it if it matched a real leap second. The C++ library that powers clock does have this capability, through a `utc_clock` class, and we may expose this in a limited form in the future, with conversion to and from sys-time and naive-time. ## Why doesn't this work with data.table? While the entire high-level API for R's native date (Date) and date-time (POSIXct) types will work fine with data.table, if you try to put any of the major clock types into a data.table, you will probably see this error message: ```{r, eval=FALSE} library(data.table) data.table(x = year_month_day(2019, 1, 1)) #> Error in dimnames(x) <- dn : #> length of 'dimnames' [1] not equal to array extent ``` You won't see this issue when working with data.frames or tibbles. As of now, data.table doesn't support the concept of *record types*. These are implemented as a list of vectors of equal length, that together represent a single idea. The `length()` of these types should be taken from the length of the vectors, not the length of the list. If you unclass any of the clock types, you'll see that they are implemented in this way: ```{r} ymdh <- year_month_day(2019, 1, 1:2, 1) unclass(ymdh) unclass(as_naive_time(ymdh)) ``` I find that record types are extremely useful data structures for building upon R's basic atomic types in ways that otherwise couldn't be done. They allow calendar types to hold information about each component, enabling instant access for retrieval, modification, and grouping. They also allow calendars to represent invalid dates, such as `2019-02-31`, without any issues. Time points use them to store up to nanosecond precision date-times, which are really C++ `int64_t` types that don't nicely fit into any R atomic type (I am aware of the bit64 package, and made a conscious decision to implement as a record type instead. This partly had to do with how missing values are handled, and how that integrates with vctrs). The idea of a record type actually isn't new. R's own POSIXlt type is a record type: ```{r} x <- as.POSIXct("2019-01-01", "America/New_York") # POSIXct is implemented as a double unclass(x) # POSIXlt is a record type unclass(as.POSIXlt(x)) ``` data.table doesn't truly support POSIXlt either. Instead, you get a warning about them converting it to a POSIXct. This is pretty reasonable considering their focus on performance. ```{r, eval=FALSE} data.table(x = as.POSIXlt("2019-01-01", "America/New_York")) #> x #> 1: 2019-01-01 #> Warning message: #> In as.data.table.list(x, keep.rownames = keep.rownames, check.names = check.names, : #> POSIXlt column type detected and converted to POSIXct. We do not recommend use of POSIXlt at all because it uses 40 bytes to store one date. ``` It was previously a bit difficult to create record types in R because there were few examples and no resources to build on. In vctrs, we've added a `vctrs_rcrd` type that serves as a base to build new record types on. Many S3 methods have been written for `vctrs_rcrd`s in a way that should work for any type that builds on top of it, giving you a lot of scaffolding for free. I am hopeful that as more record types make their way into the R ecosystem built on this common foundation, it might be possible for data.table to enable this as an approved type in their package. clock/vignettes/recipes.Rmd0000644000176200001440000006251314423751216015467 0ustar liggesusers--- title: "Examples and Recipes" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Examples and Recipes} %\VignetteEncoding{UTF-8} %\VignetteEngine{knitr::rmarkdown} editor_options: chunk_output_type: console --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` ```{r setup} library(clock) library(magrittr) ``` This vignette shows common examples and recipes that might be useful when learning about clock. Where possible, both the high and low level API are shown. Many of these examples are adapted from the date C++ library's [Examples and Recipes](https://github.com/HowardHinnant/date/wiki/Examples-and-Recipes) page. ## The current local time `zoned_time_now()` returns the current time in a particular time zone. It will display up to nanosecond precision, but the exact amount is OS dependent (on a Mac this displays microsecond level information at nanosecond resolution). Using `""` as the time zone string will try and use whatever R thinks your local time zone is (i.e. from `Sys.timezone()`). ```{r, eval=FALSE} zoned_time_now("") #> [1]> #> [1] "2021-02-10T15:54:29.875011000-05:00" ``` ## The current time somewhere else Pass a time zone name to `zoned_time_now()` to get the current time somewhere else. ```{r, eval=FALSE} zoned_time_now("Asia/Shanghai") #> [1]> #> [1] "2021-02-11T04:54:29.875011000+08:00" ``` ## Set a meeting across time zones Say you need to set a meeting with someone in Shanghai, but you live in New York. If you set a meeting for 9am, what time is that for them? ```{r} my_time <- year_month_day(2019, 1, 30, 9) %>% as_naive_time() %>% as_zoned_time("America/New_York") my_time their_time <- zoned_time_set_zone(my_time, "Asia/Shanghai") their_time ``` ### High level API ```{r} my_time <- as.POSIXct("2019-01-30 09:00:00", "America/New_York") date_time_set_zone(my_time, "Asia/Shanghai") ``` ## Force a specific time zone Say your co-worker in Shanghai (from the last example) accidentally logged on at 9am _their time_. What time would this be for you? The first step to solve this is to force `my_time` to have the same printed time, but use the Asia/Shanghai time zone. You can do this by going through naive-time: ```{r} my_time <- year_month_day(2019, 1, 30, 9) %>% as_naive_time() %>% as_zoned_time("America/New_York") my_time # Drop the time zone information, retaining the printed time my_time %>% as_naive_time() # Add the correct time zone name back on, # again retaining the printed time their_9am <- my_time %>% as_naive_time() %>% as_zoned_time("Asia/Shanghai") their_9am ``` Note that a conversion like this isn't always possible due to daylight saving time issues, in which case you might need to set the `nonexistent` and `ambiguous` arguments of `as_zoned_time()`. What time would this have been for you in New York? ```{r} zoned_time_set_zone(their_9am, "America/New_York") ``` ### High level API ```{r} my_time <- as.POSIXct("2019-01-30 09:00:00", "America/New_York") my_time %>% as_naive_time() %>% as.POSIXct("Asia/Shanghai") %>% date_time_set_zone("America/New_York") ``` ## Finding the next Monday (or Thursday) Given a particular day precision naive-time, how can you compute the next Monday? This is very easily accomplished with `time_point_shift()`. It takes a time point vector and a "target" weekday, and shifts the time points to that target weekday. ```{r} days <- as_naive_time(year_month_day(2019, c(1, 2), 1)) # A Tuesday and a Friday as_weekday(days) monday <- weekday(clock_weekdays$monday) time_point_shift(days, monday) as_weekday(time_point_shift(days, monday)) ``` You can also shift to the previous instance of the target weekday: ```{r} time_point_shift(days, monday, which = "previous") ``` If you happen to already be on the target weekday, the default behavior returns the input unchanged. However, you can also chose to advance to the next instance of the target. ```{r} tuesday <- weekday(clock_weekdays$tuesday) time_point_shift(days, tuesday) time_point_shift(days, tuesday, boundary = "advance") ``` While `time_point_shift()` is built in to clock, it can be useful to discuss the arithmetic going on in the underlying weekday type which powers this function. To do so, we will build some parts of `time_point_shift()` from scratch. The weekday type represents a single day of the week and implements _circular arithmetic_. Let's see the code for a simple version of `time_point_shift()` that just shifts to the next target weekday: ```{r} next_weekday <- function(x, target) { x + (target - as_weekday(x)) } next_weekday(days, monday) as_weekday(next_weekday(days, monday)) ``` Let's break down how `next_weekday()` works. The first step takes the difference between two weekday vectors. It does this using circular arithmetic. Once we get passed the 7th day of the week (whatever that may be), it wraps back around to the 1st day of the week. Implementing weekday arithmetic in this way means that the following nicely returns the number of days until the next Monday as a day based duration: ```{r} monday - as_weekday(days) ``` Which can be added to our day precision `days` vector to get the date of the next Monday: ```{r} days + (monday - as_weekday(days)) ``` The current implementation will return the input if it is already on the target weekday. To use the `boundary = "advance"` behavior, you could implement `next_weekday()` as: ```{r} next_weekday2 <- function(x, target) { x <- x + duration_days(1L) x + (target - as_weekday(x)) } a_monday <- as_naive_time(year_month_day(2018, 12, 31)) as_weekday(a_monday) next_weekday2(a_monday, monday) ``` ### High level API In the high level API, you can use `date_shift()`: ```{r} monday <- weekday(clock_weekdays$monday) x <- as.Date(c("2019-01-01", "2019-02-01")) date_shift(x, monday) # With a date-time y <- as.POSIXct( c("2019-01-01 02:30:30", "2019-02-01 05:20:22"), "America/New_York" ) date_shift(y, monday) ``` Note that adding weekdays to a POSIXct could generate nonexistent or ambiguous times due to daylight saving time, which would have to be handled by supplying `nonexistent` and `ambiguous` arguments to `date_shift()`. ## Generate sequences of dates and date-times clock implements S3 methods for the `seq()` generic function for the calendar and time point types it provides. The precision that you can generate sequences for depends on the type. - year-month-day: Yearly or monthly sequences - year-quarter-day: Yearly or quarterly sequences - sys-time / naive-time: Weekly, Daily, Hourly, ..., Subsecond sequences When generating sequences, the type and precision of `from` determine the result. For example: ```{r} ym <- seq(year_month_day(2019, 1), by = 2, length.out = 10) ym ``` ```{r} yq <- seq(year_quarter_day(2019, 1), by = 2, length.out = 10) ``` This allows you to generate sequences of year-months or year-quarters without having to worry about the day of the month/quarter becoming invalid. You can set the day of the results to get to a day precision calendar. For example, to get the last days of the month/quarter for this sequence: ```{r} set_day(ym, "last") set_day(yq, "last") ``` You won't be able to generate day precision sequences with calendars. Instead, you should use a time point. ```{r} from <- as_naive_time(year_month_day(2019, 1, 1)) to <- as_naive_time(year_month_day(2019, 5, 15)) seq(from, to, by = 20) ``` If you use an integer `by` value, it is interpreted as a duration at the same precision as `from`. You can also use a duration object that can be cast to the same precision as `from`. For example, to generate a sequence spaced out by 90 minutes for these second precision end points: ```{r} from <- as_naive_time(year_month_day(2019, 1, 1, 2, 30, 00)) to <- as_naive_time(year_month_day(2019, 1, 1, 12, 30, 00)) seq(from, to, by = duration_minutes(90)) ``` ### High level API In the high level API, you can use `date_seq()` to generate sequences. This doesn't have all of the flexibility of the `seq()` methods above, but is still extremely useful and has the added benefit of switching between calendars, sys-times, and naive-times automatically for you. If an integer `by` is supplied with a date `from`, it defaults to a daily sequence: ```{r} date_seq(date_build(2019, 1), by = 2, total_size = 10) ``` You can generate a monthly sequence by supplying a month precision duration for `by`. ```{r} date_seq(date_build(2019, 1), by = duration_months(2), total_size = 10) ``` If you supply `to`, be aware that all components of `to` that are more precise than the precision of `by` must match `from` exactly. For example, the day component of `from` and `to` doesn't match here, so the sequence isn't defined. ```{r, error=TRUE} date_seq( date_build(2019, 1, 1), to = date_build(2019, 10, 2), by = duration_months(2) ) ``` `date_seq()` also catches invalid dates for you, forcing you to specify the `invalid` argument to specify how to handle them. ```{r, error=TRUE} jan31 <- date_build(2019, 1, 31) dec31 <- date_build(2019, 12, 31) date_seq(jan31, to = dec31, by = duration_months(1)) ``` By specifying `invalid = "previous"` here, we can generate month end values. ```{r} date_seq(jan31, to = dec31, by = duration_months(1), invalid = "previous") ``` Compare this with the automatic "overflow" behavior of `seq()`, which is often a source of confusion. ```{r} seq(jan31, to = dec31, by = "1 month") ``` ## Grouping by months or quarters When working on a data analysis, you might be required to summarize certain metrics at a monthly or quarterly level. With `calendar_group()`, you can easily summarize at the granular precision that you care about. Take this vector of day precision naive-times in 2019: ```{r} from <- as_naive_time(year_month_day(2019, 1, 1)) to <- as_naive_time(year_month_day(2019, 12, 31)) x <- seq(from, to, by = duration_days(20)) x ``` To group by month, first convert to a year-month-day: ```{r} ymd <- as_year_month_day(x) head(ymd) calendar_group(ymd, "month") ``` To group by quarter, convert to a year-quarter-day: ```{r} yqd <- as_year_quarter_day(x) head(yqd) calendar_group(yqd, "quarter") ``` If you need to group by a multiple of months / quarters, you can do that too: ```{r} calendar_group(ymd, "month", n = 2) calendar_group(yqd, "quarter", n = 2) ``` Note that the returned calendar vector is at the precision we grouped by, not at the original precision with, say, the day of the month / quarter set to `1`. Additionally, be aware that `calendar_group()` groups "within" the component that is one unit of precision larger than the `precision` you specify. So, when grouping by `"day"`, this groups by "day of the month", which can't cross the month or year boundary. If you need to bundle dates together by something like 60 days (i.e. crossing the month boundary), then you should use `time_point_floor()`. ### High level API In the high level API, you can use `date_group()` to group Date vectors by one of their 3 components: year, month, or day. Since month precision dates can't be represented with Date vectors, `date_group()` sets the day of the month to 1. ```{r} x <- seq(as.Date("2019-01-01"), as.Date("2019-12-31"), by = 20) date_group(x, "month") ``` You won't be able to group by `"quarter"`, since this isn't one of the 3 components that the high level API lets you work with. Instead, this is a case where you should convert to a year-quarter-day, group on that type, then convert back to Date. ```{r} x %>% as_year_quarter_day() %>% calendar_group("quarter") %>% set_day(1) %>% as.Date() ``` This is actually equivalent to `date_group(x, "month", n = 3)`. If your fiscal year starts in January, you can use that instead. However, if your fiscal year starts in a different month, say, June, you'll need to use the approach from above like so: ```{r} x %>% as_year_quarter_day(start = clock_months$june) %>% calendar_group("quarter") %>% set_day(1) %>% as.Date() ``` ## Flooring by days While `calendar_group()` can group by "component", it isn't useful for bundling together sets of time points that can cross month/year boundaries, like "60 days" of data. For that, you are better off _flooring_ by rolling sets of 60 days. ```{r} from <- as_naive_time(year_month_day(2019, 1, 1)) to <- as_naive_time(year_month_day(2019, 12, 31)) x <- seq(from, to, by = duration_days(20)) ``` ```{r} time_point_floor(x, "day", n = 60) ``` Flooring operates on the underlying duration, which for day precision time points is a count of days since the _origin_, 1970-01-01. ```{r} unclass(x[1]) ``` The 60 day counter starts here, which means that any times between `[1970-01-01, 1970-03-02)` are all floored to 1970-01-01. At `1970-03-02`, the counter starts again. If you would like to change this origin, you can provide a time point to start counting from with the `origin` argument. This is mostly useful if you are flooring by weeks and you want to change the day of the week that the count starts on. Since 1970-01-01 is a Thursday, flooring by 14 days defaults to returning all Thursdays. ```{r} x <- seq(as_naive_time(year_month_day(2019, 1, 1)), by = 3, length.out = 10) x thursdays <- time_point_floor(x, "day", n = 14) thursdays as_weekday(thursdays) ``` You can use `origin` to change this to floor to Mondays. ```{r} origin <- as_naive_time(year_month_day(2018, 12, 31)) as_weekday(origin) mondays <- time_point_floor(x, "day", n = 14, origin = origin) mondays as_weekday(mondays) ``` ### High level API You can use `date_floor()` with Date and POSIXct types. ```{r} x <- seq(as.Date("2019-01-01"), as.Date("2019-12-31"), by = 20) date_floor(x, "day", n = 60) ``` The `origin` you provide should be another Date. For week precision flooring with Dates, you can specify `"week"` as the precision. ```{r} x <- seq(as.Date("2019-01-01"), by = 3, length.out = 10) origin <- as.Date("2018-12-31") date_floor(x, "week", n = 2, origin = origin) ``` ## Day of the year To get the day of the year, convert to the year-day calendar type and extract the day with `get_day()`. ```{r} x <- year_month_day(2019, clock_months$july, 4) yd <- as_year_day(x) yd get_day(yd) ``` ### High level API ```{r} x <- as.Date("2019-07-04") x %>% as_year_day() %>% get_day() ``` ## Computing an age in years To get the age of an individual in years, use `calendar_count_between()`. ```{r} x <- year_month_day(1980, 12, 14:16) today <- year_month_day(2005, 12, 15) # Note that the month and day of the month are taken into account! # (Time of day would also be taken into account if there was any.) calendar_count_between(x, today, "year") ``` ### High level API You can use `date_count_between()` with Date and POSIXct types. ```{r} x <- date_build(1980, 12, 14:16) today <- date_build(2005, 12, 15) date_count_between(x, today, "year") ``` ## Computing number of weeks since the start of the year `lubridate::week()` is a useful function that returns "the number of complete seven day periods that have occurred between the date and January 1st, plus one." There is no direct equivalent to this, but it is possible to replicate with `calendar_start()` and `time_point_count_between()`. ```{r} x <- year_month_day(2019, 11, 28) # lubridate::week(as.Date(x)) # [1] 48 x_start <- calendar_start(x, "year") x_start time_point_count_between( as_naive_time(x_start), as_naive_time(x), "week" ) + 1L ``` You could also peek at the `lubridate::week()` implementation to see that this is just: ```{r} doy <- get_day(as_year_day(x)) doy (doy - 1L) %/% 7L + 1L ``` ### High level API This is actually a little easier in the high level API because you don't have to think about switching between types. ```{r} x <- date_build(2019, 11, 28) date_count_between(date_start(x, "year"), x, "week") + 1L ``` ## Compute the number of months between two dates How can we compute the number of months between these two dates? ```{r} x <- year_month_day(2013, 10, 15) y <- year_month_day(2016, 10, 13) ``` This is a bit of an ambiguous question because "month" isn't very well-defined, and there are various different interpretations we could take. We might want to ignore the day component entirely, and just compute the number of months between `2013-10` and `2016-10`. ```{r} calendar_narrow(y, "month") - calendar_narrow(x, "month") ``` Or we could include the day of the month, and say that `2013-10-15` to `2014-10-15` defines 1 month (i.e. you have to hit the same day of the month in the next month). ```{r} calendar_count_between(x, y, "month") ``` With this you could also compute the number of days remaining between these two dates. ```{r} x_close <- add_months(x, calendar_count_between(x, y, "month")) x_close x_close_st <- as_sys_time(x_close) y_st <- as_sys_time(y) time_point_count_between(x_close_st, y_st, "day") ``` Or we could compute the number of days between these two dates in units of seconds, and divide that by the average number of seconds in 1 proleptic Gregorian month. ```{r} # Days between x and y days <- as_sys_time(y) - as_sys_time(x) days # In units of seconds days <- duration_cast(days, "second") days <- as.numeric(days) days # Average number of seconds in 1 proleptic Gregorian month avg_sec_in_month <- duration_cast(duration_months(1), "second") avg_sec_in_month <- as.numeric(avg_sec_in_month) days / avg_sec_in_month ``` ### High level API ```{r} x <- date_build(2013, 10, 15) y <- date_build(2016, 10, 13) ``` To ignore the day of the month, first shift to the start of the month, then you can use `date_count_between()`. ```{r} date_count_between(date_start(x, "month"), date_start(y, "month"), "month") ``` To utilize the day field, do the same as above but without calling `date_start()`. ```{r} date_count_between(x, y, "month") ``` There is no high level equivalent to the average length of one proleptic Gregorian month example. ## Computing the ISO year or week The ISO 8601 standard outlines an alternative calendar that is specified by the year, the week of the year, and the day of the week. It also specifies that the _start_ of the week is considered to be a Monday. This ends up meaning that the actual ISO year may be different from the Gregorian year, and is somewhat difficult to compute "by hand". Instead, you can use the `year_week_day()` calendar if you need to work with ISO week dates. ```{r} x <- date_build(2019:2026) y <- as_year_week_day(x, start = clock_weekdays$monday) data.frame(x = x, y = y) ``` ```{r} get_year(y) get_week(y) # Last week in the ISO year set_week(y, "last") ``` The year-week-day calendar is a fully supported calendar, meaning that all of the `calendar_*()` functions work on it: ```{r} calendar_narrow(y, "week") ``` There is also an `iso_year_week_day()` calendar available, which is identical to `year_week_day(start = clock_weekdays$monday)`. That ISO calendar actually existed first, before we generalized it to any `start` weekday. ## Computing the Epidemiological year or week Epidemiologists following the US CDC guidelines use a calendar that is similar to the ISO calendar, but defines the start of the week to be Sunday instead of Monday. `year_week_day()` supports this as well: ```{r} x <- date_build(2019:2026) iso <- as_year_week_day(x, start = clock_weekdays$monday) epi <- as_year_week_day(x, start = clock_weekdays$sunday) data.frame(x = x, iso = iso, epi = epi) ``` ```{r} get_year(epi) get_week(epi) ``` ## Converting a time zone abbreviation into a time zone name It is possible that you might run into date-time strings of the form `"2020-10-25 01:30:00 IST"`, which contain a time zone _abbreviation_ rather than a full time zone name. Because time zone maintainers change the abbreviation they use throughout time, and because multiple time zones sometimes use the same abbreviation, it is generally impossible to parse strings of this form without more information. That said, if you know what time zone this abbreviation goes with, you can parse this time with `zoned_time_parse_abbrev()`, supplying the `zone`. ```{r} x <- "2020-10-25 01:30:00 IST" zoned_time_parse_abbrev(x, "Asia/Kolkata") zoned_time_parse_abbrev(x, "Asia/Jerusalem") ``` If you _don't_ know what time zone this abbreviation goes with, then generally you are out of luck. However, there are low-level tools in this library that can help you generate a list of _possible_ zoned-times this could map to. Assuming that `x` is a naive-time with its corresponding time zone abbreviation attached, the first thing to do is to parse this string as a naive-time. ```{r} x <- naive_time_parse(x, format = "%Y-%m-%d %H:%M:%S IST") x ``` Next, we'll develop a function that attempts to turn this naive-time into a zoned-time, iterating through all of the time zone names available in the time zone database. These time zone names are accessible through `tzdb_names()`. By using the low-level `naive_time_info()`, rather than `as_zoned_time()`, to lookup zone specific information, we'll also get back information about the UTC offset and time zone abbreviation that is currently in use. By matching this abbreviation against our input abbreviation, we can generate a list of zoned-times that use the abbreviation we care about at that particular instance in time. ```{r} naive_find_by_abbrev <- function(x, abbrev) { if (!is_naive_time(x)) { abort("`x` must be a naive-time.") } if (length(x) != 1L) { abort("`x` must be length 1.") } if (!rlang::is_string(abbrev)) { abort("`abbrev` must be a single string.") } zones <- tzdb_names() info <- naive_time_info(x, zones) info$zones <- zones c( compute_uniques(x, info, abbrev), compute_ambiguous(x, info, abbrev) ) } compute_uniques <- function(x, info, abbrev) { info <- info[info$type == "unique",] # If the abbreviation of the unique time matches the input `abbrev`, # then that candidate zone should be in the output matches <- info$first$abbreviation == abbrev zones <- info$zones[matches] lapply(zones, as_zoned_time, x = x) } compute_ambiguous <- function(x, info, abbrev) { info <- info[info$type == "ambiguous",] # Of the two possible times, # does the abbreviation of the earliest match the input `abbrev`? matches <- info$first$abbreviation == abbrev zones <- info$zones[matches] earliest <- lapply(zones, as_zoned_time, x = x, ambiguous = "earliest") # Of the two possible times, # does the abbreviation of the latest match the input `abbrev`? matches <- info$second$abbreviation == abbrev zones <- info$zones[matches] latest <- lapply(zones, as_zoned_time, x = x, ambiguous = "latest") c(earliest, latest) } ``` ```{r} candidates <- naive_find_by_abbrev(x, "IST") candidates ``` While it looks like we got 7 candidates, in reality we only have 3. Asia/Kolkata, Europe/Dublin, and Asia/Jerusalem are our 3 candidates. The others are aliases of those 3 that have been retired but are kept for backwards compatibility. Looking at the code, there are two ways to add a candidate time zone name to the list. If there is a unique mapping from `{naive-time, zone}` to `sys-time`, then we check if the abbreviation that goes with that unique mapping matches our input abbreviation. If so, then we convert `x` to a zoned-time with that time zone. If there is an ambiguous mapping from `{naive-time, zone}` to `sys-time`, which is due to a daylight saving fallback, then we check the abbreviation of both the _earliest_ and _latest_ possible times. If either matches, then we convert `x` to a zoned-time using that time zone and the information about which of the two ambiguous times were used. This example is particularly interesting, since each of the 3 candidates came from a different path. The Asia/Kolkata one is unique, the Europe/Dublin one is ambiguous but the earliest was chosen, and the Asia/Jerusalem one is ambiguous but the latest was chosen: ```{r} as_zoned_time(x, "Asia/Kolkata") as_zoned_time(x, "Europe/Dublin", ambiguous = "earliest") as_zoned_time(x, "Asia/Jerusalem", ambiguous = "latest") ``` ## When is the next daylight saving time event? Given a particular zoned-time, when will it next be affected by daylight saving time? For this, we can use a relatively low level helper, `zoned_time_info()`. It returns a data frame of information about the current daylight saving time transition points, along with information about the offset, the current time zone abbreviation, and whether or not daylight saving time is currently active or not. ```{r} x <- zoned_time_parse_complete("2019-01-01T00:00:00-05:00[America/New_York]") info <- zoned_time_info(x) # Beginning of the current DST range info$begin # Beginning of the next DST range info$end ``` So on 2018-11-04 at (the second) 1 o'clock hour, daylight saving time was turned off. On 2019-03-10 at 3 o'clock, daylight saving time will be considered on again. This is the next moment in time right after a daylight saving time gap of 1 hour, which you can see by subtracting 1 second (in sys-time): ```{r} # Last moment in time in the current DST range info$end %>% as_sys_time() %>% add_seconds(-1) %>% as_zoned_time(zoned_time_zone(x)) ``` ### High level API `date_time_info()` exists in the high level API to do a similar thing. It is basically the same as `zoned_time_info()`, except the `begin` and `end` columns are returned as R POSIXct date-times rather than zoned-times, and the `offset` column is returned as an integer rather than as a clock duration (since we try not to expose high level API users to low level types). ```{r} x <- date_time_parse("2019-01-01 00:00:00", zone = "America/New_York") date_time_info(x) ``` clock/R/0000755000176200001440000000000014427430744011557 5ustar liggesusersclock/R/naive-time.R0000644000176200001440000007335514427270231013746 0ustar liggesusersnew_naive_time_from_fields <- function(fields, precision, names) { new_time_point_from_fields(fields, precision, CLOCK_NAIVE, names) } # ------------------------------------------------------------------------------ naive_days <- function(n = integer()) { names <- NULL duration <- duration_days(n) new_naive_time_from_fields(duration, PRECISION_DAY, names) } naive_seconds <- function(n = integer()) { names <- NULL duration <- duration_seconds(n) new_naive_time_from_fields(duration, PRECISION_SECOND, names) } # ------------------------------------------------------------------------------ #' Is `x` a naive-time? #' #' This function determines if the input is a naive-time object. #' #' @param x `[object]` #' #' An object. #' #' @return `TRUE` if `x` inherits from `"clock_naive_time"`, otherwise `FALSE`. #' #' @export #' @examples #' is_naive_time(1) #' is_naive_time(as_naive_time(duration_days(1))) is_naive_time <- function(x) { inherits(x, "clock_naive_time") } check_naive_time <- function(x, ..., arg = caller_arg(x), call = caller_env()) { check_inherits(x, what = "clock_naive_time", arg = arg, call = call) } # ------------------------------------------------------------------------------ #' Parsing: naive-time #' #' @description #' `naive_time_parse()` is a parser into a naive-time. #' #' `naive_time_parse()` is useful when you have date-time strings like #' `"2020-01-01T01:04:30"`. If there is no attached UTC offset or time zone #' name, then parsing this string as a naive-time is your best option. If #' you know that this string should be interpreted in a specific time zone, #' parse as a naive-time, then use [as_zoned_time()]. #' #' The default options assume that `x` should be parsed at second precision, #' using a `format` string of `"%Y-%m-%dT%H:%M:%S"`. This matches the default #' result from calling `format()` on a naive-time. #' #' _`naive_time_parse()` ignores both the `%z` and `%Z` commands._ #' #' If your date-time strings contain a full time zone name and a UTC offset, use #' [zoned_time_parse_complete()]. If they contain a time zone abbreviation, use #' [zoned_time_parse_abbrev()]. #' #' If your date-time strings contain a UTC offset, but not a full time zone #' name, use [sys_time_parse()]. #' #' @inheritSection zoned-parsing Full Precision Parsing #' #' @inheritParams sys_time_parse #' #' @return A naive-time. #' #' @export #' @examples #' naive_time_parse("2020-01-01T05:06:07") #' #' # Day precision #' naive_time_parse("2020-01-01", precision = "day") #' #' # Nanosecond precision, but using a day based format #' naive_time_parse("2020-01-01", format = "%Y-%m-%d", precision = "nanosecond") #' #' # Remember that the `%z` and `%Z` commands are ignored entirely! #' naive_time_parse( #' "2020-01-01 -4000 America/New_York", #' format = "%Y-%m-%d %z %Z" #' ) #' #' # --------------------------------------------------------------------------- #' # Fractional seconds and POSIXct #' #' # If you have a string with fractional seconds and want to convert it to #' # a POSIXct, remember that clock treats POSIXct as a second precision type. #' # Ideally, you'd use a clock type that can support fractional seconds, but #' # if you really want to parse it into a POSIXct, the correct way to do so #' # is to parse the full fractional time point with the correct `precision`, #' # then round to seconds using whatever convention you require, and finally #' # convert that to POSIXct. #' x <- c("2020-01-01T00:00:00.123", "2020-01-01T00:00:00.555") #' #' # First, parse string with full precision #' x <- naive_time_parse(x, precision = "millisecond") #' x #' #' # Then round to second with a floor, ceiling, or round to nearest #' time_point_floor(x, "second") #' time_point_round(x, "second") #' #' # Finally, convert to POSIXct #' as_date_time(time_point_round(x, "second"), zone = "UTC") naive_time_parse <- function(x, ..., format = NULL, precision = "second", locale = clock_locale()) { check_dots_empty0(...) check_time_point_precision(precision) precision <- precision_to_integer(precision) fields <- time_point_parse( x = x, format = format, precision = precision, locale = locale, clock = CLOCK_NAIVE ) new_naive_time_from_fields(fields, precision, names(x)) } # ------------------------------------------------------------------------------ #' Convert to a naive-time #' #' @description #' `as_naive_time()` converts `x` to a naive-time. #' #' You can convert to a naive-time from any calendar type, as long as it has #' at least day precision. There also must not be any invalid dates. If invalid #' dates exist, they must first be resolved with [invalid_resolve()]. #' #' Converting to a naive-time from a sys-time or zoned-time retains the printed #' time, but drops the assumption that the time should be interpreted with any #' specific time zone. #' #' Converting to a naive-time from a duration just wraps the duration in a #' naive-time object, there is no assumption about the time zone. The duration #' must have at least day precision. #' #' There are convenience methods for converting to a naive-time from R's #' native date and date-time types. Like converting from a zoned-time, these #' retain the printed time. #' #' @inheritParams rlang::args_dots_empty #' #' @param x `[object]` #' #' An object to convert to a naive-time. #' #' @return A naive-time vector. #' #' @export #' @examples #' x <- as.Date("2019-01-01") #' as_naive_time(x) #' #' ym <- year_month_day(2019, 02) #' #' # A minimum of day precision is required #' try(as_naive_time(ym)) #' #' ymd <- set_day(ym, 10) #' as_naive_time(ymd) as_naive_time <- function(x, ...) { UseMethod("as_naive_time") } #' @export as_naive_time.default <- function(x, ...) { stop_clock_unsupported(x) } #' @export as_naive_time.clock_naive_time <- function(x, ...) { check_dots_empty0(...) x } # ------------------------------------------------------------------------------ #' @export as_sys_time.clock_naive_time <- function(x, ...) { check_dots_empty0(...) new_sys_time_from_fields(x, time_point_precision_attribute(x), clock_rcrd_names(x)) } #' Convert to a zoned-time from a naive-time #' #' @description #' This is a naive-time method for the [as_zoned_time()] generic. #' #' Converting to a zoned-time from a naive-time retains the printed time, #' but changes the underlying duration, depending on the `zone` that you choose. #' #' Naive-times are time points with a yet-to-be-determined time zone. By #' converting them to a zoned-time, all you are doing is specifying that #' time zone while attempting to keep all other printed information the #' same (if possible). #' #' If you want to retain the underlying duration, try converting to a zoned-time #' [from a sys-time][as-zoned-time-sys-time], which is a time point #' interpreted as having a UTC time zone. #' #' @section Daylight Saving Time: #' #' Converting from a naive-time to a zoned-time is not always possible due to #' daylight saving time issues. There are two types of these issues: #' #' _Nonexistent_ times are the result of daylight saving time "gaps". #' For example, in the America/New_York time zone, there was a daylight #' saving time gap 1 second after `"2020-03-08 01:59:59"`, where the clocks #' changed from `01:59:59 -> 03:00:00`, completely skipping the 2 o'clock hour. #' This means that if you had a naive time of `"2020-03-08 02:30:00"`, you #' couldn't convert that straight into a zoned-time with this time zone. To #' resolve these issues, the `nonexistent` argument can be used to specify #' one of many nonexistent time resolution strategies. #' #' _Ambiguous_ times are the result of daylight saving time "fallbacks". #' For example, in the America/New_York time zone, there was a daylight #' saving time fallback 1 second after `"2020-11-01 01:59:59 EDT"`, at which #' point the clocks "fell backwards" by 1 hour, resulting in a printed time of #' `"2020-11-01 01:00:00 EST"` (note the EDT->EST shift). This resulted in two #' 1 o'clock hours for this day, so if you had a naive time of #' `"2020-11-01 01:30:00"`, you wouldn't be able to convert that directly #' into a zoned-time with this time zone, as there is no way for clock to know #' which of the two ambiguous times you wanted. To resolve these issues, #' the `ambiguous` argument can be used to specify one of many ambiguous #' time resolution strategies. #' #' @inheritParams rlang::args_dots_empty #' #' @param x `[clock_naive_time]` #' #' A naive-time to convert to a zoned-time. #' #' @param zone `[character(1)]` #' #' The zone to convert to. #' #' @param nonexistent `[character / NULL]` #' #' One of the following nonexistent time resolution strategies, allowed to be #' either length 1, or the same length as the input: #' #' - `"roll-forward"`: The next valid instant in time. #' #' - `"roll-backward"`: The previous valid instant in time. #' #' - `"shift-forward"`: Shift the nonexistent time forward by the size of #' the daylight saving time gap. #' #' - `"shift-backward`: Shift the nonexistent time backward by the size of #' the daylight saving time gap. #' #' - `"NA"`: Replace nonexistent times with `NA`. #' #' - `"error"`: Error on nonexistent times. #' #' Using either `"roll-forward"` or `"roll-backward"` is generally #' recommended over shifting, as these two strategies maintain the #' _relative ordering_ between elements of the input. #' #' If `NULL`, defaults to `"error"`. #' #' If `getOption("clock.strict")` is `TRUE`, `nonexistent` must be supplied #' and cannot be `NULL`. This is a convenient way to make production code #' robust to nonexistent times. #' #' @param ambiguous `[character / zoned_time / POSIXct / list(2) / NULL]` #' #' One of the following ambiguous time resolution strategies, allowed to be #' either length 1, or the same length as the input: #' #' - `"earliest"`: Of the two possible times, choose the earliest one. #' #' - `"latest"`: Of the two possible times, choose the latest one. #' #' - `"NA"`: Replace ambiguous times with `NA`. #' #' - `"error"`: Error on ambiguous times. #' #' Alternatively, `ambiguous` is allowed to be a zoned_time (or POSIXct) that #' is either length 1, or the same length as the input. If an ambiguous time #' is encountered, the zoned_time is consulted. If the zoned_time corresponds #' to a naive_time that is also ambiguous _and_ uses the same daylight saving #' time transition point as the original ambiguous time, then the offset of #' the zoned_time is used to resolve the ambiguity. If the ambiguity cannot be #' resolved by consulting the zoned_time, then this method falls back to #' `NULL`. #' #' Finally, `ambiguous` is allowed to be a list of size 2, where the first #' element of the list is a zoned_time (as described above), and the second #' element of the list is an ambiguous time resolution strategy to use when #' the ambiguous time cannot be resolved by consulting the zoned_time. #' Specifying a zoned_time on its own is identical to `list(, #' NULL)`. #' #' If `NULL`, defaults to `"error"`. #' #' If `getOption("clock.strict")` is `TRUE`, `ambiguous` must be supplied and #' cannot be `NULL`. Additionally, `ambiguous` cannot be specified as a #' zoned_time on its own, as this implies `NULL` for ambiguous times that the #' zoned_time cannot resolve. Instead, it must be specified as a list #' alongside an ambiguous time resolution strategy as described above. This is #' a convenient way to make production code robust to ambiguous times. #' #' @return A zoned-time vector. #' #' @name as-zoned-time-naive-time #' @export #' @examples #' library(magrittr) #' #' x <- as_naive_time(year_month_day(2019, 1, 1)) #' #' # Converting a naive-time to a zoned-time generally retains the #' # printed time, while changing the underlying duration. #' as_zoned_time(x, "America/New_York") #' as_zoned_time(x, "America/Los_Angeles") #' #' # --------------------------------------------------------------------------- #' # Nonexistent time: #' #' new_york <- "America/New_York" #' #' # There was a daylight saving gap in the America/New_York time zone on #' # 2020-03-08 01:59:59 -> 03:00:00, which means that one of these #' # naive-times don't exist in that time zone. By default, attempting to #' # convert it to a zoned time will result in an error. #' nonexistent_time <- year_month_day(2020, 03, 08, c(02, 03), c(45, 30), 00) #' nonexistent_time <- as_naive_time(nonexistent_time) #' try(as_zoned_time(nonexistent_time, new_york)) #' #' # Resolve this by specifying a nonexistent time resolution strategy #' as_zoned_time(nonexistent_time, new_york, nonexistent = "roll-forward") #' as_zoned_time(nonexistent_time, new_york, nonexistent = "roll-backward") #' #' # Note that rolling backwards will choose the last possible moment in #' # time at the current precision of the input #' nonexistent_nanotime <- time_point_cast(nonexistent_time, "nanosecond") #' nonexistent_nanotime #' as_zoned_time(nonexistent_nanotime, new_york, nonexistent = "roll-backward") #' #' # A word of caution - Shifting does not guarantee that the relative ordering #' # of the input is maintained #' shifted <- as_zoned_time( #' nonexistent_time, #' new_york, #' nonexistent = "shift-forward" #' ) #' shifted #' #' # 02:45:00 < 03:30:00 #' nonexistent_time[1] < nonexistent_time[2] #' # 03:45:00 > 03:30:00 (relative ordering is lost) #' shifted[1] < shifted[2] #' #' # --------------------------------------------------------------------------- #' # Ambiguous time: #' #' new_york <- "America/New_York" #' #' # There was a daylight saving time fallback in the America/New_York time #' # zone on 2020-11-01 01:59:59 EDT -> 2020-11-01 01:00:00 EST, resulting #' # in two 1 o'clock hours. This means that the following naive time is #' # ambiguous since we don't know which of the two 1 o'clocks it belongs to. #' # By default, attempting to convert it to a zoned time will result in an #' # error. #' ambiguous_time <- year_month_day(2020, 11, 01, 01, 30, 00) #' ambiguous_time <- as_naive_time(ambiguous_time) #' try(as_zoned_time(ambiguous_time, new_york)) #' #' # Resolve this by specifying an ambiguous time resolution strategy #' earliest <- as_zoned_time(ambiguous_time, new_york, ambiguous = "earliest") #' latest <- as_zoned_time(ambiguous_time, new_york, ambiguous = "latest") #' na <- as_zoned_time(ambiguous_time, new_york, ambiguous = "NA") #' earliest #' latest #' na #' #' # Now assume that you were given the following zoned-times, i.e., #' # you didn't build them from scratch so you already know their otherwise #' # ambiguous offsets #' x <- c(earliest, latest) #' x #' #' # To set the seconds to 5 in both, you might try: #' x_naive <- x %>% #' as_naive_time() %>% #' as_year_month_day() %>% #' set_second(5) %>% #' as_naive_time() #' #' x_naive #' #' # But this fails because you've "lost" the information about which #' # offsets these ambiguous times started in #' try(as_zoned_time(x_naive, zoned_time_zone(x))) #' #' # To get around this, you can use that information by specifying #' # `ambiguous = x`, which will use the offset from `x` to resolve the #' # ambiguity in `x_naive` as long as `x` is also an ambiguous time with the #' # same daylight saving time transition point as `x_naive` (i.e. here #' # everything has a transition point of `"2020-11-01 01:00:00 EST"`). #' as_zoned_time(x_naive, zoned_time_zone(x), ambiguous = x) #' #' # Say you added one more time to `x` that would not be considered ambiguous #' # in naive-time #' x <- c(x, as_zoned_time(as_sys_time(latest) + 3600, zoned_time_zone(latest))) #' x #' #' # Imagine you want to floor this vector to a multiple of 2 hours, with #' # an origin of 1am that day. You can do this by subtracting the origin, #' # flooring, then adding it back #' origin <- year_month_day(2019, 11, 01, 01, 00, 00) %>% #' as_naive_time() %>% #' as_duration() #' #' x_naive <- x %>% #' as_naive_time() %>% #' add_seconds(-origin) %>% #' time_point_floor("hour", n = 2) %>% #' add_seconds(origin) #' #' x_naive #' #' # You again have ambiguous naive-time points, so you might try using #' # `ambiguous = x`. It looks like this took care of the first two problems, #' # but we have an issue at location 3. #' try(as_zoned_time(x_naive, zoned_time_zone(x), ambiguous = x)) #' #' # When we floored from 02:30:00 -> 01:00:00, we went from being #' # unambiguous -> ambiguous. In clock, this is something you must handle #' # explicitly, and cannot be handled by using information from `x`. You can #' # handle this while still retaining the behavior for the other two #' # time points that were ambiguous before and after the floor by passing a #' # list containing `x` and an ambiguous time resolution strategy to use #' # when information from `x` can't resolve ambiguities: #' as_zoned_time(x_naive, zoned_time_zone(x), ambiguous = list(x, "latest")) as_zoned_time.clock_naive_time <- function(x, zone, ..., nonexistent = NULL, ambiguous = NULL) { check_dots_empty0(...) check_zone(zone) # Promote to at least seconds precision for `zoned_time` ptype <- vec_ptype2(x, clock_empty_naive_time_second, y_arg = "") x <- vec_cast(x, ptype) size <- vec_size(x) precision <- time_point_precision_attribute(x) names <- clock_rcrd_names(x) nonexistent <- check_nonexistent(nonexistent, size) info <- check_ambiguous(ambiguous, size, zone) method <- info$method if (identical(method, "string")) { ambiguous <- info$ambiguous fields <- as_zoned_sys_time_from_naive_time_cpp(x, precision, zone, nonexistent, ambiguous, current_env()) } else if (identical(method, "reference")) { reference <- info$reference ambiguous <- info$ambiguous fields <- as_zoned_sys_time_from_naive_time_with_reference_cpp(x, precision, zone, nonexistent, ambiguous, reference, current_env()) } else { abort("Internal error: Unknown ambiguous handling method.") } new_zoned_time_from_fields(fields, precision, zone, names) } check_nonexistent <- function(nonexistent, size, ..., call = caller_env()) { check_dots_empty0(...) nonexistent <- check_nonexistent_strict(nonexistent, call = call) nonexistent_size <- vec_size(nonexistent) if (nonexistent_size != 1L && nonexistent_size != size) { cli::cli_abort("{.arg nonexistent} must have length 1 or {size}.", call = call) } check_character(nonexistent, allow_null = TRUE, call = call) nonexistent } check_ambiguous <- function(ambiguous, size, zone, ..., call = caller_env()) { check_dots_empty0(...) if (is_null(ambiguous)) { ambiguous <- check_ambiguous_strict(ambiguous, call = call) return(list(method = "string", ambiguous = ambiguous)) } if (is_character(ambiguous)) { ambiguous <- check_ambiguous_chr(ambiguous, size, call = call) return(list(method = "string", ambiguous = ambiguous)) } if (is_zoned_time(ambiguous) || inherits(ambiguous, "POSIXt")) { # Implied `NULL`, to be validated by `check_ambiguous_strict()` ambiguous <- list(ambiguous, NULL) } if (is_list(ambiguous)) { result <- check_ambiguous_list(ambiguous, size, zone, call = call) reference <- result$reference ambiguous <- result$ambiguous return(list(method = "reference", reference = reference, ambiguous = ambiguous)) } cli::cli_abort( "{.arg ambiguous} must be a character vector, a zoned-time, a POSIXct, or a list, not {.obj_type_friendly {ambiguous}}.", call = call ) } check_ambiguous_chr <- function(ambiguous, size, call) { ambiguous_size <- vec_size(ambiguous) if (ambiguous_size != 1L && ambiguous_size != size) { cli::cli_abort("{.arg ambiguous} must have length 1 or {size}.", call = call) } ambiguous } check_ambiguous_zoned <- function(ambiguous, size, zone, call) { # POSIXt -> zoned_time reference <- as_zoned_time(ambiguous) reference_size <- vec_size(reference) reference_zone <- zoned_time_zone_attribute(reference) if (reference_size != 1L && reference_size != size) { cli::cli_abort("A zoned-time or POSIXct {.arg ambiguous} must have length 1 or {size}.", call = call) } if (reference_zone != zone) { cli::cli_abort("A zoned-time or POSIXct {.arg ambiguous} must have the same zone as {.arg zone}.", call = call) } # Force seconds precision to avoid the need for C++ templating sys_time <- as_sys_time(reference) sys_time <- time_point_floor(sys_time, "second") reference <- as_zoned_time(sys_time, reference_zone) reference } check_ambiguous_list <- function(ambiguous, size, zone, call) { if (length(ambiguous) != 2L) { cli::cli_abort("A list {.arg ambiguous} must have length 2.", call = call) } reference <- ambiguous[[1]] if (!is_zoned_time(reference) && !inherits(reference, "POSIXt")) { cli::cli_abort("The first element of a list {.arg ambiguous} must be a zoned-time or POSIXt.", call = call) } reference <- check_ambiguous_zoned(reference, size, zone, call = call) ambiguous <- ambiguous[[2]] if (is_null(ambiguous)) { ambiguous <- check_ambiguous_strict(ambiguous, call = call) } if (!is_character(ambiguous)) { cli::cli_abort("The second element of a list {.arg ambiguous} must be a character vector, or `NULL`.", call = call) } ambiguous <- check_ambiguous_chr(ambiguous, size, call = call) list(reference = reference, ambiguous = ambiguous) } #' @export as.character.clock_naive_time <- function(x, ...) { format(x) } # ------------------------------------------------------------------------------ #' Info: naive-time #' #' @description #' `naive_time_info()` retrieves a set of low-level information generally not #' required for most date-time manipulations. It is used implicitly #' by `as_zoned_time()` when converting from a naive-time. #' #' It returns a data frame with the following columns: #' #' - `type`: A character vector containing one of: #' #' - `"unique"`: The naive-time maps uniquely to a zoned-time that can be #' created with `zone`. #' #' - `"nonexistent"`: The naive-time does not exist as a zoned-time that can #' be created with `zone`. #' #' - `"ambiguous"`: The naive-time exists twice as a zoned-time that can be #' created with `zone`. #' #' - `first`: A [sys_time_info()] data frame. #' #' - `second`: A [sys_time_info()] data frame. #' #' ## type == "unique" #' #' - `first` will be filled out with sys-info representing daylight saving time #' information for that time point in `zone`. #' #' - `second` will contain only `NA` values, as there is no ambiguity to #' represent information for. #' #' ## type == "nonexistent" #' #' - `first` will be filled out with the sys-info that ends just prior to `x`. #' #' - `second` will be filled out with the sys-info that begins just after `x`. #' #' ## type == "ambiguous" #' #' - `first` will be filled out with the sys-info that ends just after `x`. #' #' - `second` will be filled out with the sys-info that starts just before `x`. #' #' @details #' If the tibble package is installed, it is recommended to convert the output #' to a tibble with `as_tibble()`, as that will print the df-cols much nicer. #' #' @param x `[clock_naive_time]` #' #' A naive-time. #' #' @param zone `[character]` #' #' A valid time zone name. #' #' Unlike most functions in clock, in `naive_time_info()` `zone` is vectorized #' and is recycled against `x`. #' #' @return A data frame of low level information. #' #' @export #' @examples #' library(vctrs) #' #' x <- year_month_day(1970, 04, 26, 02, 30, 00) #' x <- as_naive_time(x) #' #' # Maps uniquely to a time in London #' naive_time_info(x, "Europe/London") #' #' # This naive-time never existed in New York! #' # A DST gap jumped the time from 01:59:59 -> 03:00:00, #' # skipping the 2 o'clock hour #' zone <- "America/New_York" #' info <- naive_time_info(x, zone) #' info #' #' # You can recreate various `nonexistent` strategies with this info #' as_zoned_time(x, zone, nonexistent = "roll-forward") #' as_zoned_time(info$first$end, zone) #' #' as_zoned_time(x, zone, nonexistent = "roll-backward") #' as_zoned_time(info$first$end - 1, zone) #' #' as_zoned_time(x, zone, nonexistent = "shift-forward") #' as_zoned_time(as_sys_time(x) - info$first$offset, zone) #' #' as_zoned_time(x, zone, nonexistent = "shift-backward") #' as_zoned_time(as_sys_time(x) - info$second$offset, zone) #' #' # --------------------------------------------------------------------------- #' # Normalizing to UTC #' #' # Imagine you had the following printed times, and knowledge that they #' # are to be interpreted as in the corresponding time zones #' df <- data_frame( #' x = c("2020-01-05 02:30:00", "2020-06-03 12:20:05"), #' zone = c("America/Los_Angeles", "Europe/London") #' ) #' #' # The times are assumed to be naive-times, i.e. if you lived in the `zone` #' # at the moment the time was recorded, then you would have seen that time #' # printed on the clock. Currently, these are strings. To convert them to #' # a time based type, you'll have to acknowledge that R only lets you have #' # 1 time zone in a vector of date-times at a time. So you'll need to #' # normalize these naive-times. The easiest thing to normalize them to #' # is UTC. #' df$naive <- naive_time_parse(df$x) #' #' # Get info about the naive times using a vector of zones #' info <- naive_time_info(df$naive, df$zone) #' info #' #' # We'll assume that some system generated these naive-times with no #' # chance of them ever being nonexistent or ambiguous. So now all we have #' # to do is use the offset to convert the naive-time to a sys-time. The #' # relationship used is: #' # offset = naive_time - sys_time #' df$sys <- as_sys_time(df$naive) - info$first$offset #' df #' #' # At this point, both times are in UTC. From here, you can convert them #' # both to either America/Los_Angeles or Europe/London as required. #' as_zoned_time(df$sys, "America/Los_Angeles") #' as_zoned_time(df$sys, "Europe/London") naive_time_info <- function(x, zone) { check_naive_time(x) check_character(zone) precision <- time_point_precision_attribute(x) # Recycle `x` to the common size. `zone` is recycled internally as required, # which is more efficient than reloading the time zone repeatedly. size <- vec_size_common(x = x, zone = zone) x <- vec_recycle(x, size) fields <- naive_time_info_cpp(x, precision, zone) new_naive_time_info_from_fields(fields) } new_naive_time_info_from_fields <- function(fields) { fields[["first"]] <- new_sys_time_info_from_fields(fields[["first"]]) fields[["second"]] <- new_sys_time_info_from_fields(fields[["second"]]) new_data_frame(fields) } # ------------------------------------------------------------------------------ #' @export vec_ptype_full.clock_naive_time <- function(x, ...) { time_point_ptype(x, type = "full") } #' @export vec_ptype_abbr.clock_naive_time <- function(x, ...) { time_point_ptype(x, type = "abbr") } # ------------------------------------------------------------------------------ #' @export vec_ptype.clock_naive_time <- function(x, ...) { switch( time_point_precision_attribute(x) + 1L, abort("Internal error: Invalid precision"), abort("Internal error: Invalid precision"), abort("Internal error: Invalid precision"), abort("Internal error: Invalid precision"), clock_empty_naive_time_day, clock_empty_naive_time_hour, clock_empty_naive_time_minute, clock_empty_naive_time_second, clock_empty_naive_time_millisecond, clock_empty_naive_time_microsecond, clock_empty_naive_time_nanosecond, abort("Internal error: Invalid precision.") ) } #' @export vec_ptype2.clock_naive_time.clock_naive_time <- function(x, y, ...) { ptype2_time_point_and_time_point(x, y, ...) } #' @export vec_cast.clock_naive_time.clock_naive_time <- function(x, to, ...) { cast_time_point_to_time_point(x, to, ...) } # ------------------------------------------------------------------------------ #' @rdname clock-arith #' @method vec_arith clock_naive_time #' @export vec_arith.clock_naive_time <- function(op, x, y, ...) { UseMethod("vec_arith.clock_naive_time", y) } #' @method vec_arith.clock_naive_time MISSING #' @export vec_arith.clock_naive_time.MISSING <- function(op, x, y, ...) { arith_time_point_and_missing(op, x, y, ...) } #' @method vec_arith.clock_naive_time clock_naive_time #' @export vec_arith.clock_naive_time.clock_naive_time <- function(op, x, y, ...) { arith_time_point_and_time_point(op, x, y, ...) } #' @method vec_arith.clock_naive_time clock_duration #' @export vec_arith.clock_naive_time.clock_duration <- function(op, x, y, ...) { arith_time_point_and_duration(op, x, y, ...) } #' @method vec_arith.clock_duration clock_naive_time #' @export vec_arith.clock_duration.clock_naive_time <- function(op, x, y, ...) { arith_duration_and_time_point(op, x, y, ...) } #' @method vec_arith.clock_naive_time numeric #' @export vec_arith.clock_naive_time.numeric <- function(op, x, y, ...) { arith_time_point_and_numeric(op, x, y, ...) } #' @method vec_arith.numeric clock_naive_time #' @export vec_arith.numeric.clock_naive_time <- function(op, x, y, ...) { arith_numeric_and_time_point(op, x, y, ...) } # ------------------------------------------------------------------------------ clock_init_naive_time_utils <- function(env) { day <- as_naive_time(year_month_day(integer(), integer(), integer())) assign("clock_empty_naive_time_day", day, envir = env) assign("clock_empty_naive_time_hour", time_point_cast(day, "hour"), envir = env) assign("clock_empty_naive_time_minute", time_point_cast(day, "minute"), envir = env) assign("clock_empty_naive_time_second", time_point_cast(day, "second"), envir = env) assign("clock_empty_naive_time_millisecond", time_point_cast(day, "millisecond"), envir = env) assign("clock_empty_naive_time_microsecond", time_point_cast(day, "microsecond"), envir = env) assign("clock_empty_naive_time_nanosecond", time_point_cast(day, "nanosecond"), envir = env) invisible(NULL) } clock/R/check.R0000644000176200001440000000537214424761554012771 0ustar liggesuserscheck_between_year <- function(x, ..., call = caller_env(), arg = caller_arg(x)) { check_between(x, lower = clock_calendar_year_minimum, upper = clock_calendar_year_maximum, call = call, arg = arg) } check_between_quarter <- function(x, ..., call = caller_env(), arg = caller_arg(x)) { check_between(x, lower = 1L, upper = 4L, call = call, arg = arg) } check_between_month <- function(x, ..., call = caller_env(), arg = caller_arg(x)) { check_between(x, lower = 1L, upper = 12L, call = call, arg = arg) } check_between_week <- function(x, ..., call = caller_env(), arg = caller_arg(x)) { check_between(x, lower = 1L, upper = 53L, call = call, arg = arg) } check_between_day_of_year <- function(x, ..., call = caller_env(), arg = caller_arg(x)) { check_between(x, lower = 1L, upper = 366L, call = call, arg = arg) } check_between_day_of_quarter <- function(x, ..., call = caller_env(), arg = caller_arg(x)) { check_between(x, lower = 1L, upper = 92L, call = call, arg = arg) } check_between_day_of_month <- function(x, ..., call = caller_env(), arg = caller_arg(x)) { check_between(x, lower = 1L, upper = 31L, call = call, arg = arg) } check_between_day_of_week <- function(x, ..., call = caller_env(), arg = caller_arg(x)) { check_between(x, lower = 1L, upper = 7L, call = call, arg = arg) } check_between_index_of_week <- function(x, ..., call = caller_env(), arg = caller_arg(x)) { check_between(x, lower = 1L, upper = 5L, call = call, arg = arg) } check_between_hour <- function(x, ..., call = caller_env(), arg = caller_arg(x)) { check_between(x, lower = 0L, upper = 23L, call = call, arg = arg) } check_between_minute <- function(x, ..., call = caller_env(), arg = caller_arg(x)) { check_between(x, lower = 0L, upper = 59L, call = call, arg = arg) } check_between_second <- function(x, ..., call = caller_env(), arg = caller_arg(x)) { check_between(x, lower = 0L, upper = 59L, call = call, arg = arg) } check_between_subsecond <- function(x, precision, ..., call = caller_env(), arg = caller_arg(x)) { upper <- switch( precision_to_string(precision), millisecond = 999L, microsecond = 999999L, nanosecond = 999999999L, abort("Invalid precision.", .internal = TRUE) ) check_between(x, lower = 0L, upper = upper, call = call, arg = arg) } check_between <- function(x, lower, upper, ..., call = caller_env(), arg = caller_arg(x)) { ok <- (x >= lower & x <= upper) | vec_detect_missing(x) if (all(ok)) { return(invisible(NULL)) } loc <- which(!ok) message <- c( "{.arg {arg}} must be between [{lower}, {upper}].", i = "Invalid results at locations: {loc}." ) cli::cli_abort(message, call = call) } clock/R/sysdata.rda0000644000176200001440000007512414006131516013714 0ustar liggesusersBZh91AY&SYTF&@W?w@:ogso]'[u5뽀zUI7vwm۹Gv^ U(֟]JS>6nnmk,Sk]hF1f\Wmף[:^6:3ݭ޽=|ލ;g|xн殽v흝dwۛ]wrz"Sv.ͻ;v2:'6Ӎۻ"%4Vvcͽ5[b[o7ۻS{zݸxwwghmvss{"cT=ksK:jos3'kz4 @&FFL&4 445D44O&H=O5h  jxDdIʟ?Jf1O =0Mz@@%j4P443CT?TyOiO 6y!⇉44@4hDBdh!&*~2j`(O4x2zmOS4yOS'@R =M)Mi4ښ4h@ѣ@@hꞠB`FM4ML"m=4j=M'FzMdmA 4$(PMҐ.hG@s@`ENj u z4S/~H/lN="+c`|mƒf;&ox$Jp|\T,ovUKCG?1[#Ӆ_<@555xJ-b6WDNw^h>^čWJڦhV3`Q)E?K<s>̟>FTTn?[qJa 7쿰 J tefgaJ.e>nn8FawSqsÊ_ھ?0.:5.*[zbH~Rظ=W~N|X"3}5_j!DID$IG|:t] fUmMn8*7!yNc+,NfUmݲ1 l՜_qfq;JzA۴mO ^dm^u@P=LX>/?or|.s_)x1}pp0X!LsPA5:mYRR[j{y/uo"y6AAc0˹sswD".a,69q:H_|u};!>]7ί";ÍIDŔA|}/mөlP-s`l~8F%D}6q6jѳ53f<~A[f2B [mrc|9 qBOeHGuz񁍱z@r `B'8{9{$p${ʊ2cC[ X7ሷ.|Fy=@Swk_8Fr)eW-&C# *b,su%dHfF<%tь4+o3{/8bQ EecN,FMNyȈ!ajY cwȵ..dbޗcRЧ~*jiރpTaPܣGp~hd_ź">|eA~wh*F }l1(QqWaR{+&Od5J1NDA΂逦18ӓkз/AA $ro"Sb=p aCP:B1+S=6 }1ҭ!p"L4"0AHp\xNYAQ,=k;| HWjKL I%Q09Zfz!ƑPE/f>*< ǒ$lI05&#Y*#9v"e]0%0ըn*UfXro Do- 5\=~`, \MCTEڲ>vG<)]UYt}&[9gt,o^`r"ɗ&B&hY pk+5HD=ή~>esZg^|B@3y|gC>q4soo {n<ⵘњ%"]Axר6Pb&ad)3Σ_YitQɱ'iljlҭn`щlqF} hn_q}/1]+ٟ2{XFn 8qO&s湂䎣[*ƧgpBpj\鮷cL]&B(V&pĂl F6$x["c/a#cb>!V:lm\֋v_/+Ϗ!s<4T;HB[ yD1H =si?V=y-UoJ$["^xRcO^v9a:t{#;{9G@bT(}]ekzgcui[M]Yf{]KMVzsSa l^;|Y|ڐmSO,Fu ֊/k޳ :[6| j +L\1cX6 Lߍ;xKN3 /NZeodq[L =V*PEQ%L>9[y̡68Kx{넃ad6\Ftd<ʨUʂ22NFCu~EN%Ա(>CϼbQJY8888=fwmƸ߮8nFag'ii[%U5 ,m 㳙Ս5gys1K1=E?ؚT[xfsr䗛V|TS|paz9+}8h8ꌹ TBVI}=۟˃ 8eMTAG 6!tݗ+mwh+m:#wE2N A޷qz}}}}ziT& U#X -;'SIy&}2eHscٚ.fnsA@T(vELg4OyrRVxwؖچ^Z>_w% T6kp&p|Q|̸W^me: 4dT Aj4Դ2$ִZ}7mZYsEʓQIK?:2ꅷ]$2]+\:UI zQZg*RP]Vhw5* 2`mdi2=]]]]^+;&ZN Lrl6 a[KlU6:un^GÍ{x㚢T(U`Ҿk:56)6Y:)o=Ύg˖\;$zZ6& ݤt0ͻ״YV+ߝDZޗȸ4e|`s#|AXa^]6liERL;r BRŐhiVd4\DLrX*8RZ+N!qQQdEk5hV^XelݦSQ? sQ)F]ح5z"c&grt;2tWQ-&:$$T Zjٳ 1SuT[etܷ1[]AU%3"̶e¯FvvWQ+ 2T-lp#~jHLF{IuR14]!j],a 5j 4f6K4E'ФR}yuyK8®m ]ê)˝#IrA|\[Z6LlƢPX| Xľkv%PcAC&J3L޶Wh=宺g5jةz H6A buut=4{f:qh3ӛkΦI[ALKMfxpwz:X-o2:U~U+URTC| [Ru0/{mg@TGdllJrk֑P`QKQ9č8 Se%!) r"q?p m-B|!rjhF1 0PTrn@c8Z F8֮ŔF8dV]awnJV" :=愎 Mۆ68iE$$jZQ*.{bJu+@]"}*{YP/WL>e6H ‡Ӱ_[^,aX iձ!H}Ye|s{gf3QzڟR䦼*[KOfAs=y1ŒL|77j5M_My<WɛF7 s0z'xN2cv*1S -dZ-:Mj߄Jk%b+TjQ!tQ NM}͍u}d*@kZRdST9sȧs'79=dcĉ ={ȞSr')79nq|rDa%W;JwCa~X룗A( Yx2k2p{WX!Z)F `M~kߠðD^ *55wuDgWG G`yKZVRQ‚ S0? aR+JUi?ΡpY y Tԫj[~Lu)ӇQ.(UM%䦋E*"4ʓ1J[p@CO#.c   j(I&;w;>'WF:Le1Έv'Z5Y땮HSiVJRU+ 8fjM昻;Zpkw(chf/ؿ5|7p6i*U$QqI X[`YHi.0\1"jesp{\4māƞ'##<-@{fT>k٧y]>t鱦пD `MEz]YqiNYx9&7Dsnk̵V-K^ S99 %FhaR^v{2$GE^?ѡk=3K|\m)Vjg3VNgʂUV]m7nR}@*[q= \ Gkw""p$݅.8F["hhZ\\N*9 B $t!Em2eDf2浼-CAEJ]|R^Jq1i\>m]4S%!vegd'UUlc,Z;mgjo;BiUlL'=cgъ9mǏ.51. L (.` zwQǤnɹCSv<*(HsnVeӑLl V#jAUX e)3TR 3.|1;4TW.w"0EN9 b*m 0()n !FYxA7JMnȫM;N wwzvutG_"I/{o `JuoyL;BÆl! s% Q^?^wG/" D^׋T6O%B()s* wS ?-ByU'H ʏ&_V4UQL%JGh7ʩb |!aFaYWr0r{ߠo~Q._3Fe_/ٯS4Ww]o}`@SnPUp+VeOӊ[SǕ J<0 `5t瀜8D!Ά9S_l' 90< ,m뿲x: =F' PVemK*WK O$ʊg[VPvNXQU(jzkC'-͂mKݪd1t5dA.=muӾE?h,4SadҖNAga2렿 c(#Vn FB/zږ9Cs*;o)٩v;qcDѐ Qq(D 43*yE^^"VH\.\V>r#ouK?WrQ c( {ľ% 㭋B&:34 ֐@+i8hŕ0pDA 11q&25.{?7g2\$r!xPPoGmO3ˤa忑16X~prusGϫi[o63KvI--ٓ,Wl Qx_YVH@2Q%[D$qIZq 7c~T0LWj.^"v/:`Ga6p0m*@]~ Z!Zi…Flç1J!x1o3?.'8k& UOQ&)"cu2\0`2U,JO;*JIydvj%#2b] AKtȪ@ҙdLLL'S͖/ +s(Li6V#| <*6 cBPsthC{E\/lٯqBFd{) /kvWi:2q}ygϬkL]BLi vH Fb^|5F<θ6ap-PT\{=}[+D> y=(I4u4҉-J:vg_\Fk0XS<%RI@Z|lje{Ucd.|*zc:wT|PNR\NÛDw&e@nH+c>kM,ݘ;%c{^gc<@1mI)[Z?ױAifaf+-G*>Q²7L78ܼ){TȨ7ߕ)FΈ˗Z>V 5v} AZfۈ !|xT& H%cLs P]Z@1Ћh_S{23MfB`%nxw{RKLGMd~w:ffeH10FfAV9vy-11}f/7_ ƒkh#ߒ6C9}o) U/Vp[;VzK4I*X\,k=@b"uGUHlCJkt9LgQFmʶ.pj;#BZ4!v/ rR*1ścn9&"P7 z<0m@T0SVede k)MSD 9[Z1Ҧ Ó v zsthp 0>oxRu8iڢVr’6m㩘fs;P`L6U7~RXrQ%L%WDEVYPLI/TnjYn FPk tԆb6 d +i$iV-)4oܚrn0$-* |V$NGL8 Pvk\; yCEN70iI?N׋F|sWʔ@`HG;O ej "É ڊ2Ran"?N3Bc!K!9 m6G6qۙ5L:W&JRA3.G /) 5LDw\)4z'%YLM+u9u툻V\X|]RzPeA2 P^3w\}:|1 ~uy4"ϟhSwx1ca7si[*H`ЇB[a'3)Y2n=[c~yF֣)=6PD5 #&W9)qYՇ:CL?FᒅvCmc 7>@\p\&I*51zI`afJV֟1$K t @*vw."8N`zr%R_3Ov}%.ə^ZP7աX̎L]NID3ث=EdvͫDfl2Ӽ VGﵦ::;*hc5\X lm)'E+8YLJFKS6%Tlc"" mBDh@vqfYd ~\iEBʇkqݼA8 `P\4Eh1D6;N0M ZC[廒i `GRSɰCB3 &1,U(.Jʹ(9L[A7Aff,!DÉ[L" dlkbsa:`Vw8k"T]0a ILO&6h3h6bqfQ!fsBEI5F#dlFO哸-@H:JPFUE50:㧇(Ǘstf56U\&Dvh{2v%N>h~lTP:#P]j*1LSm`9x\lM2  9ǛwOBBo Efp Tb+o`("6c1w;ҭfj`2x |Mn^1/b-ym8 F;:?8kj(r[-wᶔ\1dW34kڀ3w -ލʷ5HTokȜ`aLߍΦ;Q\L|i=D2^xu|~AԳJmV=&x}qEBp}a H#ǩž۹vSKkfi tew~cPg@*B2Ҷfy:QJ ' ޓz 4ѷ=K9Gp mJ di 7O`){&;?kz@z`!ܠH+KKt0hax4GI6IS]1@F1ι+wr=* R3),Ԇm dXî2ݶ'SXRWnXVc[UGݧ.֟Qu3]-YnћFVlQ_~S4Y#83KuWNi7Tѝ7u a2/6 !a!Tp z0ppxfrOI`Ye!&Xzo}ޱB_R|/AZŎ:RuAp^&SUdF﷫Szx9$͕ @2if |^ e#!U9Q?WYJ7yR"yepS]ꅅ {뇲_epޥXaV>.yQ),Qz%}ajy 8Ǽ+*'dmYW=˲ m4iƿR9]$ڑ0N}L5_FE]δ~/Ģ%*OJeNjuWaBkev!E%ݷ"_ ܾ.1ƌVLN[rAF K5tE?sp5PLzgN Nsr`6Wzx2[Z5<{ˍG1g7{.q0!D^Γ"Hë́gos#H<Ӯcz\4D1A05J#qo_?gt{0~\{h汙M?u%/hqU{6&%<.Xr_a`&ճ%L|2M*ʐ}R^`X`phVY<^GY U9Bf7ML>19匟"d[xoOy67%0]Zi"n6$^wf'CJ~=Vi8oed)Dɴ|\˂/ 86|;O4f*|C%i]4])q~g:&} iQߣR}@Agɷop3&a B(niH j)~siT4(*n f*|@Iv1҃*& ,p:=A" Nx3}!D0i<[\ d<`,rl݇¸㿅*-hE?d[[}>A{1 ꞻ<=X?W\e1}Is.蔉$"M_ )|̣CIqkv*oH"̌㝥ņKtJOyBotF8Y Lӟ!2jׯ8Mb# Y梸ԄNřG8 ٭W5 f 7+rېO"ghd=;&6ENm:7' =4"еj}uZmYƧօI֟>o"ZgЅoi2aOGbfz^ۣV^`0f͡Qb1؈%ضXM:F%3Paly0cfo6 r8 M/xPr}M@, Κn;Ovks޻xi5> +,xRkg-E񛐿=^//~>s ̱"uM{Qſ3MȢL/ w` *|nre]xۼx$n%=uLb"9l&m#~~D҂qx;q g)+ū{{>o:>Fw8in C9l%bI<_Fc=cqA 㠍zZY b ᒫxK3'n^@dB h>ͽ f{9tyF6\29@:UrcP`܁w@ |!{'jROub]Lq @•e  go |Z_i'o 2V]Y.pNPx, klaGȋeB5Awi:]!f!k7 a C(x*NRQ6.YĥQu C'w6 Z)H R }H\2 m,d3<~⌈ u ^rfW;tO_R|č,!@  1˞!c}<Edi )B(`ܝKpykq<ϻ(CFՍ_Ou|%{XY>s<1)zAo~EggPKѝoa]!0ږb>}~Z^1*ы:("JiA,ӋEF'2c:&0Zp;A2PlPZ [JR;6A7Pp6 &N ŷ?fPޏ9Q3̢R1Wz⾝o`8A|#^>F yB8pdymS, …:|<Ɂ*= 8-!ϫ7!!` 6ts5Hc!#e|6|d>#J+2ɦtΟ4se0keK @-)WQ|cPc=f!Q1 D8w`k6_17X+ErIe%Hb (phύsUV:b-k%W&BUuo{&ɐg#Eŀefh>֢[+J-E'%4hejh[[ʣ9v`6 r t_7*n5)K ju{n﹊нs;Mf%2_ RM6xG jyx"Zvq?P<9%k?p:ۛndܱbghF觏 n2l InɕjЛ;D; [Q\CmI_=xW9:pzks4QI|;ur__YMˑ2sQM [u3$}p'|=C)8ۢfip{ {j;8k_ՆKL/aMv؎7b(;0m\ܫr^SX;䘽Y{hy S t5x#nh\^| dBuؖKr34(!R,-Qm&Ѱĥ#ҽwb̈=*HQ|=c45ly1w/\F'J ;R2&xhBip-ۻ6Rj& jC0 I%6oaKq)їYI BV /HXVܒĉL7V] 30h?>W(sGU M J+HpBʍ2gT "Sx k Wo$զKT?42ʽ '1|8G;v^evJ&d`(q~u0bيkފcž px*e@pXB:%==GΎ%C L2?} H 'P )*~ǧ~mRsC~)kzQzmi8#݂IpOE6PQP"Ii "]l$&-Wxx00`Yf/e>6]WE]]:}F1a[|ŀuPx>S$^ k*lو㌉v8 `:#Q)^(v f* Lyf*ۤf e ~\1*A*7^=G: qt5|QJYb{u'bNKEڐ჌s.nʩ\41滊=jo(dG>/8ȷ[s궅+i3]LJGy&KȺrsNi6^iHV)11JDbQF@ EFj\7[ґ,8ݤd. (QtO$Եl|֨ENwWsBsAQ0+, i~[` ]taqhaGb,Hӭv Pn,J9!v4ІɪpxϟM/<@ Ouϕ_sWֹ`f\Ŝf=`ѢE!0#$WTU{pw9D\A6G2D@/%ρ|?#ܢybŊ֢_R'AT>Vu@#.;Lsj=f1bl`@NYǕ,wu%XD | aiOlp(' a]bdEQ%2Adn<PbQ,J !N{Y~` ' X+4Q؁_ \1 @800a0b @XBZ`1gOvE!FF_ }² R[_ y *@(3N,DlJbC_.`@)"Qa?Oy皬!k(o iR `ҫtឱe2b'Z2"Vh'8aH`IxN,UvQ 5CX"<dX2(+D.X^Y\w( !3R!fb@4EyCiyVX Js byzߔi˨xD-9id.phUEĉL&2\JMni(%!9t\;8ȇ̇&H LhkbHIAݓ|QHm|(7";p' hmh  Aǯh0L@Z_TPF%<2A#J{vDcB%"PJcv,Y?XBԗ&2/@|*TYxsz5G//-8p=H-Ipk8i@%Lg耆'6D 2gJE TQ@KC`=z{!#hb 름` %a/gÇs,Mwj%^P]T@aqВ{|s $so9 ElŐGg2pQ c9`!4ɘ ` fIZ8QhŀNlͶkl~;k.CU U%b-¼ȳ0^FS nA1Xx"bW qGkz9 K +*5A@D&M _XR !i &rE`` BՙZ.8k=+^Ζ"11) exC 0 3ۘ(6M`Y(Ç09ddq41Hi6  0pBRQp nnnn7oo.tujZYb<Kn!*<[{'9{x/33 8`, l10- Iσ>u0"&%()E0LQƎ4c9fr񨛢ώuS C*HR%(-?IRkw8+#c:N"SHld &$m|[ak5%@FȂ| |wI +ԋP,(604',T!Rs+*h1]"&WJ¼P*9^U 2& 1Q [B'[ WJ/Ib=Fsc11fƐH+ĥ “Eu׮g# "4f3!UaPu]A")\ݺ 9q֛I@t,i c԰ӥv5z .b8NZ][W̠AYQan8Y NSp,JR]zFm,}!rBMŶa@١uD ˆC txj)wq3ctLPGm8e@! C!v8f g/Xm DV+ d29xC.p*暣E2#S0}w^:l>\ K\4mB܎4XH8R=#8d  1qFF2w24*A"Cc"yCFEvL`L4(҃ b %xp =ĊB̒CbPU v؅r#c7cA",C Ea-0(@@lnьi!z$`T`$%hPN4e^1D\APǿXNR( #t7@ a8QG`xP;PN'<&–\ `ŭBGp1$劯bys00BJE(|އ3fzo!e}}_Ȍg^"bxGc>]ЏQ  E3CxF˳.LS4PPG9.ov =O ـAniN)I4 Eo0L7 1$Ac2Bi:y&e\1Jz %iY1-Zsb*,1e?~w-|Avxs[.w&*j`O{[s*_QTTuzXhiԯrvbe3+^Q~g8H;nȴ h*[Voauُ4 <miF6-3ir.7qvynRӫ?~8i6dBV :~E !B1\~M `[񋍞m?j͌4O6?Hݷx3vՏIdGŀ:D&a-QlaLCL~.43c^v!LrnghU|°OЂ38W018?&Hc?x W0|={Kb>ջ |FS' ss$a?4oPO]7#4FtXƉc Õ¸*"r@%񎸏Mj` qك3AxHWp`*Rv8O?1\)}[=Md>&ZkbT\G1^:d7f..x"ق2q0YL.b펔1(c>d?7 <r~-> a222 %B'Jx3 srEW=ca4 ĺ{}ضAaڛUo*SN5g7#qO)S|d HwK05Yѿh34ᣦ ^`B^?|%`Z,a t\9_K8pR81>$~ g V6!Ai,x \봔N J +)ABd-*2 ;(>OiiFj346X}sCa>EAx34N+ "t˞<6aGuTOHS>(`:A*S 8gx.{o٤ad?_OZ!o5e[XLZsU2DFeA A3jށ Y |[J z0}3WV8 &P E!h`RߎqN`>"8HT>Z?\~L^|$]̳#28:[|H4I@9#z, q󹌮nv̏"? [H :0 ȳ7k7yVȄ@ھƎ-"5D?lETCZ{|o&Pcx~"em7TE1 fN:PeX.s?6ѴHdAh mb* Ɓ\"ܶdzh*6תo3I, CKo#טJ z5uwGxCˡ!m 1|ÚD)fƇ9ۃC/Yb4/9ZB l-VˁL"VӿmK#` QG;9%ԈuXpt2 {QBCK)h/h6mV{&.(5/]w}\X@(YPe ІEֱp^6NNVEe:HCHxiR%Eŀgrnݩ0EQWthXJt7'x'f0tDtjB' z}ǐǏ)SqF'2/6`eǞ>+t?/f/"D]Zp,b22 `mVFvYuBtpH)=r/n>,v:,d0r@ 5]x+!Looh\y9]B=NZN岯|LXvx7%2q)vFg+AuL!BYXs|-+9fLۛi.<c,Kt \|KJy{⎾:ڙZ[ƈTCLIDZ˼631k{{6n3xѼz$Wt: E QGImssBמ5tKi R.3Cùox54$@CE=WWg~E)'sg8ҁZ!of{%Ckc`ڇGdnE~%hQ9 3*Wz>d";'n-=\&_\>A]7f<#14TvI7&*GSAXK+R* ` {р, 4RcG8u[='#uh{iѾ%-Z#sk,_Jk38Z bZ/,)]4w;dd>>/a e5;+6_4OnR1w (f@hLoL+3\27{1% RZ,k+a]UXGZM|Q^xq-}EĢK/%SYBsak+=3S#ܯ+t#@&EjI@f'-ʣ ;Dw)=XDDq;O8񘬌.c {C* ]/0!A88H> B 0xNϏI|1t P38J _)^}ӧq$109BKq-r?X8@; H*@ IpqqY%wJeC@3AH@I%ĖuKj7 h9$N-#pj:NBaI3##8Q8Ht|HZl(Z1)/"8F3b, ER D^'a9FR9K`sn 9 EgP33qv dz7 ? ""BN,$QOd81$S7#9 { fX y$41xN]4Lͬ8n4-u+xfVPa) Q(!1p>^*.1xDfRd@l^yʼn9wo۵^/Ȧ&8dM]&& 4JlZcXnryaͰbMgW' R "yhX {T،g5mX<&R=QbBGI(Q\H٤˂Ōs7]ŋ:1=wJ^cy {Cwe@D03x >U0PzsC tTo}Pd gn<="ExǴX,|R:z]?M2wEvO[W@ǎBg巪>؝l9F(֥QIPSYH ,.{ʌ53*w:+]yA[ĮS1; z h4eUn8 `[u06fvO=y(6)P]tR%Xgt-dt#t46q. {avð+_icv'^"%NYC;4z8N{v8q05^7H2g@110B* ]#>H#$^km9.,_N*2)|xǀE`@0*,OF)K^z~ (3? Y9ƿq){1?kJ*%{R >{ yh#(h\pR1O|89AO4S .IcCR9A(fP̨)0`9s18dh2 )ГQ rC "p8׽ʁD)Gď;d ~+]6&u>sZ|A̵t8888u=ָffvz(<!0_8lOP#3 $ h{n;AgO@}Cc=9C)Y1`[EǰL3l˜k37io&` xo 71zyN{?XzR#Yx} ܘR򢨫_xyf6ՓuT21YGD(_!7`ޭU9~Q=gcdHf`ǒ,Xc0G>C2̪fQϞaGՋ(B"(H*?clock/R/utils.R0000644000176200001440000002337514425175541013053 0ustar liggesusersto_posixct <- function(x) { if (is_POSIXct(x)) { posixct_standardize(x) } else if (is_POSIXlt(x)) { to_posixct_from_posixlt(x) } else { abort("Should either be POSIXct/POSIXlt.", .internal = TRUE) } } is_POSIXt <- function(x) { inherits(x, "POSIXt") } is_POSIXct <- function(x) { inherits(x, "POSIXct") } is_POSIXlt <- function(x) { inherits(x, "POSIXlt") } check_posixt <- function(x, ..., arg = caller_arg(x), call = caller_env()) { check_inherits(x, what = "POSIXt", arg = arg, call = call) } posixct_standardize <- function(x) { if (identical(typeof(x), "double")) { return(x) } # Convert rare integer POSIXct to double # Preserves names storage.mode(x) <- "double" x } to_posixct_from_posixlt <- function(x) { as.POSIXct.POSIXlt(x) } date_standardize <- function(x) { if (identical(typeof(x), "double")) { return(x) } # Convert somewhat rare integer Date to double. # Preserves names. storage.mode(x) <- "double" x } # ------------------------------------------------------------------------------ ones_along <- function(x, na_propagate = FALSE) { out <- rep(1L, vec_size(x)) if (!na_propagate) { return(out) } na <- vec_detect_missing(x) if (any(na)) { out[na] <- NA_integer_ } out } zeros_along <- function(x, na_propagate = FALSE) { out <- vector("integer", length = vec_size(x)) if (!na_propagate) { return(out) } na <- vec_detect_missing(x) if (any(na)) { out[na] <- NA_integer_ } out } # ------------------------------------------------------------------------------ unstructure <- function(x) { set_attributes(x, NULL) } vec_unstructure <- function(x) { # Must unclass first because `names()` might not be the same length before # and after unclassing x <- unclass(x) out <- unstructure(x) names(out) <- names(x) out } set_attributes <- function(x, attributes) { attributes(x) <- attributes x } # ------------------------------------------------------------------------------ posixt_tzone <- function(x) { tzone <- attr(x, "tzone", exact = TRUE) posixt_tzone_standardize(tzone) } posixt_set_tzone <- function(x, tzone) { attr(x, "tzone") <- tzone x } # Standardize an R time zone attribute # # This is slightly different from the lubridate version. For POSIXlt objects, # the time zone attribute might be a character vector of length 3. If the first # element is `""` (which happens on a Mac with `as.POSIXlt(Sys.time())`), then # lubridate will look to the second element and will use that as the time zone. # I think this is incorrect, because those are always time zone abbreviations, # and will fail to load because they aren't true time zone names. I think that # is the reason Vitalie opened the issue noted below, and the reason for the # time zone map in lubridate. This function works more like # `lubridate:::tz.POSIXt()` which just takes the first element of the tzone # attribute. https://github.com/google/cctz/issues/46 # https://github.com/tidyverse/lubridate/blob posixt_tzone_standardize <- function(tzone) { if (is_null(tzone)) { # Like `Sys.time()` return("") } if (!is_character(tzone)) { abort("A POSIXt time zone should either be a character vector or `NULL`.") } n <- length(tzone) if (n == 0L) { warning(paste0( "POSIXt input had a corrupt time zone attribute of `character(0)`. ", "Defaulting to the current zone by assuming the zone is `\"\"`." )) return("") } if (n == 1L) { return(tzone) } # Otherwise `n > 1`, likely `n == 3` for a POSIXt with time zone # abbreviations. The first element is either a full time zone name, or `""`, # so we use that tzone <- tzone[[1]] tzone } # ------------------------------------------------------------------------------ stop_clock <- function(message, ..., call = caller_env(), class = character()) { rlang::abort(message, ..., call = call, class = c(class, "clock_error")) } stop_clock_unsupported <- function(x, ..., details = NULL, call = caller_env()) { class <- class(x)[[1L]] message <- cli::format_inline("Can't perform this operation on a {.cls {class}}.") message <- c(message, details) stop_clock(message, ..., call = call, class = "clock_error_unsupported") } stop_clock_unsupported_conversion <- function(x, to_arg, ..., call = caller_env()) { class <- class(x)[[1L]] message <- cli::format_inline("Can't convert {.cls {class}} to {.cls {to_arg}}.") stop_clock(message, ..., call = call, class = "clock_error_unsupported_conversion") } # Thrown from C++ stop_clock_invalid_date <- function(i, call) { message <- c( cli::format_inline("Invalid date found at location {i}."), i = cli::format_inline("Resolve invalid date issues by specifying the {.arg invalid} argument.") ) stop_clock(message, call = call, class = "clock_error_invalid_date") } # Thrown from C++ stop_clock_nonexistent_time <- function(i, call) { message <- c( cli::format_inline("Nonexistent time due to daylight saving time at location {i}."), i = cli::format_inline("Resolve nonexistent time issues by specifying the {.arg nonexistent} argument.") ) stop_clock(message, call = call, class = "clock_error_nonexistent_time") } # Thrown from C++ stop_clock_ambiguous_time <- function(i, call) { message <- c( cli::format_inline("Ambiguous time due to daylight saving time at location {i}."), i = cli::format_inline("Resolve ambiguous time issues by specifying the {.arg ambiguous} argument.") ) stop_clock(message, call = call, class = "clock_error_ambiguous_time") } # ------------------------------------------------------------------------------ warn_clock <- function(message, class = character()) { rlang::warn(message, class = c(class, "clock_warning")) } # Thrown from C++ warn_clock_parse_failures <- function(n, first) { if (n == 0) { abort("Internal error: warning thrown with zero failures.") } else if (n == 1) { message <- paste0( "Failed to parse 1 string at location ", first, ". ", "Returning `NA` at that location." ) } else { message <- paste0( "Failed to parse ", n, " strings, beginning at location ", first, ". ", "Returning `NA` at the locations where there were parse failures." ) } warn_clock(message, "clock_warning_parse_failures") } # Thrown from C++ warn_clock_format_failures <- function(n, first) { if (n == 0) { abort("Internal error: warning thrown with zero failures.") } else if (n == 1) { message <- paste0( "Failed to format 1 string at location ", first, ". ", "Returning `NA` at that location." ) } else { message <- paste0( "Failed to format ", n, " strings, beginning at location ", first, ". ", "Returning `NA` at the locations where there were format failures." ) } warn_clock(message, "clock_warning_format_failures") } # ------------------------------------------------------------------------------ max_collect <- function(max, ..., error_call = caller_env()) { if (is_null(max)) { max <- getOption("max.print", default = 1000L) } check_number_whole(max, min = 0, call = error_call) max <- vec_cast(max, integer(), call = error_call) max } max_slice <- function(x, max) { if (max < vec_size(x)) { vec_slice(x, seq_len(max)) } else { x } } clock_print <- function(x, max, ..., error_call = caller_env()) { max <- max_collect(max, error_call = error_call) obj_print(x, max = max) invisible(x) } clock_print_footer <- function(x, max) { size <- vec_size(x) if (max >= size) { return(invisible(x)) } n_omitted <- size - max if (n_omitted == 1L) { value <- " value." } else { value <- " values." } cat( "Reached `max` or `getOption('max.print')`. ", "Omitted ", n_omitted, value, "\n", sep = "" ) invisible(x) } # ------------------------------------------------------------------------------ df_list_propagate_missing <- function(x, ..., size = NULL) { check_dots_empty0(...) x <- new_data_frame(x, n = size) complete <- vec_detect_complete(x) if (all(complete)) { return(vec_unstructure(x)) } incomplete <- !complete missing <- vec_detect_missing(x) aligned <- missing == incomplete if (all(aligned)) { # Already fully missing where incomplete return(vec_unstructure(x)) } n <- length(x) out <- vector("list", length = n) out <- set_names(out, names(x)) # Propagate missings x <- vec_assign(x, incomplete, NA) vec_unstructure(x) } # ------------------------------------------------------------------------------ check_inherits <- function(x, what, ..., allow_null = FALSE, arg = caller_arg(x), call = caller_env()) { if (!missing(x)) { if (inherits(x, what)) { return(invisible(NULL)) } if (allow_null && is_null(x)) { return(invisible(NULL)) } } stop_input_type( x = x, what = cli::format_inline("a <{what}>"), arg = arg, call = call ) } check_no_missing <- function(x, ..., arg = caller_arg(x), call = caller_env()) { if (!vec_any_missing(x)) { return(invisible(NULL)) } loc <- vec_detect_missing(x) loc <- which(loc) message <- c( "{.arg {arg}} can't contain missing values.", i = "The following locations are missing: {loc}." ) cli::cli_abort(message, call = call) } # ------------------------------------------------------------------------------ vec_drop_infinite <- function(x) { infinite <- is.infinite(x) if (any(infinite)) { x <- vec_slice(x, !infinite) } x } # ------------------------------------------------------------------------------ is_last <- function(x) { identical(x, "last") } clock/R/zzz.R0000644000176200001440000000302014426711643012531 0ustar liggesusers# nocov start .onLoad <- function(libname, pkgname) { tzdb::tzdb_initialize() clock_init_utils() clock_ns <- topenv(environment()) # Initializers must run after initializing C++ utils and setting tzdata clock_init_duration_utils(clock_ns) clock_init_limits_init(clock_ns) clock_init_year_month_day_utils(clock_ns) clock_init_year_month_weekday_utils(clock_ns) clock_init_iso_year_week_day_utils(clock_ns) clock_init_year_day_utils(clock_ns) clock_init_sys_time_utils(clock_ns) clock_init_naive_time_utils(clock_ns) clock_init_zoned_time_utils(clock_ns) clock_init_weekday_utils(clock_ns) vctrs::s3_register("pillar::pillar_shaft", "clock_calendar", pillar_shaft.clock_calendar) vctrs::s3_register("pillar::pillar_shaft", "clock_time_point", pillar_shaft.clock_time_point) vctrs::s3_register("pillar::pillar_shaft", "clock_zoned_time", pillar_shaft.clock_zoned_time) vctrs::s3_register("slider::slider_plus", "Date.clock_duration", slider_plus.Date.clock_duration) vctrs::s3_register("slider::slider_plus", "POSIXct.clock_duration", slider_plus.POSIXct.clock_duration) vctrs::s3_register("slider::slider_plus", "POSIXlt.clock_duration", slider_plus.POSIXlt.clock_duration) vctrs::s3_register("slider::slider_minus", "Date.clock_duration", slider_minus.Date.clock_duration) vctrs::s3_register("slider::slider_minus", "POSIXct.clock_duration", slider_minus.POSIXct.clock_duration) vctrs::s3_register("slider::slider_minus", "POSIXlt.clock_duration", slider_minus.POSIXlt.clock_duration) } # nocov end clock/R/limits.R0000644000176200001440000000060014424761554013202 0ustar liggesusers# Internal generic clock_minimum <- function(x) { UseMethod("clock_minimum") } # Internal generic clock_maximum <- function(x) { UseMethod("clock_maximum") } clock_init_limits_init <- function(env) { assign("clock_calendar_year_maximum", clock_get_calendar_year_maximum(), envir = env) assign("clock_calendar_year_minimum", clock_get_calendar_year_minimum(), envir = env) } clock/R/clock-codes.R0000644000176200001440000000260414422221153014055 0ustar liggesusers#' Integer codes #' #' @description #' #' Objects with useful mappings from month names and weekday names #' to integer codes. #' #' ## Month codes (`clock_months`) #' #' - `january == 1` #' - `february == 2` #' - `march == 3` #' - `april == 4` #' - `may == 5` #' - `june == 6` #' - `july == 7` #' - `august == 8` #' - `september == 9` #' - `october == 10` #' - `november == 11` #' - `december == 12` #' #' ## Weekday codes (`clock_weekdays`) #' #' - `sunday == 1` #' - `monday == 2` #' - `tuesday == 3` #' - `wednesday == 4` #' - `thursday == 5` #' - `friday == 6` #' - `saturday == 7` #' #' ## ISO weekday codes (`clock_iso_weekdays`) #' #' - `monday == 1` #' - `tuesday == 2` #' - `wednesday == 3` #' - `thursday == 4` #' - `friday == 5` #' - `saturday == 6` #' - `sunday == 7` #' #' @name clock-codes #' #' @examples #' weekday(clock_weekdays$wednesday) #' #' year_month_weekday(2019, clock_months$april, clock_weekdays$monday, 1:4) #' #' year_week_day(2020, 52, start = clock_weekdays$monday) #' #' iso_year_week_day(2020, 52, clock_iso_weekdays$thursday) NULL #' @format - `clock_months`: An environment containing month codes. #' @rdname clock-codes "clock_months" #' @format - `clock_weekdays`: An environment containing weekday codes. #' @rdname clock-codes "clock_weekdays" #' @format - `clock_iso_weekdays`: An environment containing ISO weekday codes. #' @rdname clock-codes "clock_iso_weekdays" clock/R/clock-deprecated.R0000644000176200001440000000221614423755257015101 0ustar liggesusers#' Get or set the time zone #' #' @description #' `r lifecycle::badge("deprecated")` #' #' - `date_zone()` is deprecated in favor of [date_time_zone()]. #' #' - `date_set_zone()` is deprecated in favor of [date_time_set_zone()]. #' #' @inheritParams date_time_zone #' #' @keywords internal #' @name date-zone NULL #' @rdname date-zone #' @export date_zone <- function(x) { # - soft-deprecated: 0.7.0 lifecycle::deprecate_soft( when = "0.7.0", what = "date_zone()", with = "date_time_zone()" ) UseMethod("date_zone") } #' @export date_zone.Date <- function(x) { abort("Can't get the zone of a 'Date'.") } #' @export date_zone.POSIXt <- function(x) { posixt_tzone(x) } #' @rdname date-zone #' @export date_set_zone <- function(x, zone) { # - soft-deprecated: 0.7.0 lifecycle::deprecate_soft( when = "0.7.0", what = "date_set_zone()", with = "date_time_set_zone()" ) UseMethod("date_set_zone") } #' @export date_set_zone.Date <- function(x, zone) { abort("Can't set the zone of a 'Date'.") } #' @export date_set_zone.POSIXt <- function(x, zone) { x <- to_posixct(x) check_zone(zone) posixt_set_tzone(x, zone) } clock/R/clock-locale.R0000644000176200001440000000310514423760205014223 0ustar liggesusers#' Create a clock locale #' #' A clock locale contains the information required to format and parse dates. #' The defaults have been chosen to match US English. A clock locale object can #' be provided to `format()` methods or parse functions (like #' [year_month_day_parse()]) to override the defaults. #' #' @param labels `[clock_labels / character(1)]` #' #' Character representations of localized weekday names, month names, and #' AM/PM names. Either the language code as string (passed on to #' [clock_labels_lookup()]), or an object created by [clock_labels()]. #' #' @param decimal_mark `[character(1)]` #' #' Symbol used for the decimal place when formatting sub-second date-times. #' Either `","` or `"."`. #' #' @return A `"clock_locale"` object. #' #' @export #' @examples #' clock_locale() #' clock_locale(labels = "fr") clock_locale <- function(labels = "en", decimal_mark = ".") { if (is_character(labels)) { labels <- clock_labels_lookup(labels) } check_clock_labels(labels) decimal_mark <- arg_match0(decimal_mark, values = c(".", ",")) new_clock_locale(labels, decimal_mark) } new_clock_locale <- function(labels, decimal_mark) { structure( list( labels = labels, decimal_mark = decimal_mark ), class = "clock_locale" ) } #' @export print.clock_locale <- function (x, ...) { cat("\n") cat("Decimal Mark: ", x$decimal_mark, "\n", sep = "") print(x$labels) } check_clock_locale <- function(x, ..., arg = caller_arg(x), call = caller_env()) { check_inherits(x, what = "clock_locale", arg = arg, call = call) } clock/R/clock-labels.R0000644000176200001440000000710714423730227014235 0ustar liggesusers#' Create or retrieve date related labels #' #' @description #' When parsing and formatting dates, you often need to know how weekdays of #' the week and months are represented as text. These functions allow you #' to either create your own labels, or look them up from a standard set of #' language specific labels. The standard list is derived from ICU #' () via the stringi package. #' #' - `clock_labels_lookup()` looks up a set of labels from a #' given language code. #' #' - `clock_labels_languages()` lists the language codes that are accepted. #' #' - `clock_labels()` lets you create your own set of labels. Use this if the #' currently supported languages don't meet your needs. #' #' @param month,month_abbrev `[character(12)]` #' #' Full and abbreviated month names. Starts with January. #' #' @param weekday,weekday_abbrev `[character(7)]` #' #' Full and abbreviated weekday names. Starts with Sunday. #' #' @param am_pm `[character(2)]` #' #' Names used for AM and PM. #' #' @return A `"clock_labels"` object. #' #' @export #' @examples #' clock_labels_lookup("en") #' clock_labels_lookup("ko") #' clock_labels_lookup("fr") clock_labels <- function(month, month_abbrev = month, weekday, weekday_abbrev = weekday, am_pm) { if (!is_character(month, n = 12L)) { abort("`month` must be a character vector of length 12.") } if (!is_character(month_abbrev, n = 12L)) { abort("`month_abbrev` must be a character vector of length 12.") } if (!is_character(weekday, n = 7L)) { abort("`weekday` must be a character vector of length 7.") } if (!is_character(weekday_abbrev, n = 7L)) { abort("`weekday_abbrev` must be a character vector of length 7.") } if (!is_character(am_pm, n = 2L)) { abort("`am_pm` must be a character vector of length 2.") } structure( list( month = enc2utf8(month), month_abbrev = enc2utf8(month_abbrev), weekday = enc2utf8(weekday), weekday_abbrev = enc2utf8(weekday_abbrev), am_pm = enc2utf8(am_pm) ), class = "clock_labels" ) } #' @rdname clock_labels #' @param language `[character(1)]` #' #' A BCP 47 locale, generally constructed from a two or three #' digit language code. See `clock_labels_languages()` for a complete list of #' available locales. #' @export clock_labels_lookup <- function(language) { check_string(language) labels <- clock_labels_list[[language]] if (is.null(labels)) { cli::cli_abort("Can't find a locale for {.str {language}}.") } labels } #' @export #' @rdname clock_labels clock_labels_languages <- function() { names(clock_labels_list) } #' @export print.clock_labels <- function(x, ...) { cat("\n") if (identical(x$weekday, x$weekday_abbrev)) { weekday <- paste0(x$weekday, collapse = ", ") } else { weekday <- paste0(x$weekday, " (", x$weekday_abbrev, ")", collapse = ", ") } if (identical(x$month, x$month_abbrev)) { month <- paste0(x$month, collapse = ", ") } else { month <- paste0(x$month, " (", x$month_abbrev, ")", collapse = ", ") } am_pm <- paste0(x$am_pm, collapse = "/") cat_wrap("Weekdays: ", weekday) cat_wrap("Months: ", month) cat_wrap("AM/PM: ", am_pm) } check_clock_labels <- function(x, ..., arg = caller_arg(x), call = caller_env()) { check_inherits(x, what = "clock_labels", arg = arg, call = call) } cat_wrap <- function(header, body) { body <- strwrap(body, exdent = nchar(header)) cat(header, paste(body, collapse = "\n"), "\n", sep = "") } clock/R/import-standalone-types-check.R0000644000176200001440000002761614423730227017566 0ustar liggesusers# Standalone file: do not edit by hand # Source: # ---------------------------------------------------------------------- # # --- # repo: r-lib/rlang # file: standalone-types-check.R # last-updated: 2023-03-13 # license: https://unlicense.org # dependencies: standalone-obj-type.R # imports: rlang (>= 1.1.0) # --- # # ## Changelog # # 2023-03-13: # - Improved error messages of number checkers (@teunbrand) # - Added `allow_infinite` argument to `check_number_whole()` (@mgirlich). # - Added `check_data_frame()` (@mgirlich). # # 2023-03-07: # - Added dependency on rlang (>= 1.1.0). # # 2023-02-15: # - Added `check_logical()`. # # - `check_bool()`, `check_number_whole()`, and # `check_number_decimal()` are now implemented in C. # # - For efficiency, `check_number_whole()` and # `check_number_decimal()` now take a `NULL` default for `min` and # `max`. This makes it possible to bypass unnecessary type-checking # and comparisons in the default case of no bounds checks. # # 2022-10-07: # - `check_number_whole()` and `_decimal()` no longer treat # non-numeric types such as factors or dates as numbers. Numeric # types are detected with `is.numeric()`. # # 2022-10-04: # - Added `check_name()` that forbids the empty string. # `check_string()` allows the empty string by default. # # 2022-09-28: # - Removed `what` arguments. # - Added `allow_na` and `allow_null` arguments. # - Added `allow_decimal` and `allow_infinite` arguments. # - Improved errors with absent arguments. # # # 2022-09-16: # - Unprefixed usage of rlang functions with `rlang::` to # avoid onLoad issues when called from rlang (#1482). # # 2022-08-11: # - Added changelog. # # nocov start # Scalars ----------------------------------------------------------------- .standalone_types_check_dot_call <- .Call check_bool <- function(x, ..., allow_na = FALSE, allow_null = FALSE, arg = caller_arg(x), call = caller_env()) { if (!missing(x) && .standalone_types_check_dot_call(ffi_standalone_is_bool_1.0.7, x, allow_na, allow_null)) { return(invisible(NULL)) } stop_input_type( x, c("`TRUE`", "`FALSE`"), ..., allow_na = allow_na, allow_null = allow_null, arg = arg, call = call ) } check_string <- function(x, ..., allow_empty = TRUE, allow_na = FALSE, allow_null = FALSE, arg = caller_arg(x), call = caller_env()) { if (!missing(x)) { is_string <- .rlang_check_is_string( x, allow_empty = allow_empty, allow_na = allow_na, allow_null = allow_null ) if (is_string) { return(invisible(NULL)) } } stop_input_type( x, "a single string", ..., allow_na = allow_na, allow_null = allow_null, arg = arg, call = call ) } .rlang_check_is_string <- function(x, allow_empty, allow_na, allow_null) { if (is_string(x)) { if (allow_empty || !is_string(x, "")) { return(TRUE) } } if (allow_null && is_null(x)) { return(TRUE) } if (allow_na && (identical(x, NA) || identical(x, na_chr))) { return(TRUE) } FALSE } check_name <- function(x, ..., allow_null = FALSE, arg = caller_arg(x), call = caller_env()) { if (!missing(x)) { is_string <- .rlang_check_is_string( x, allow_empty = FALSE, allow_na = FALSE, allow_null = allow_null ) if (is_string) { return(invisible(NULL)) } } stop_input_type( x, "a valid name", ..., allow_na = FALSE, allow_null = allow_null, arg = arg, call = call ) } IS_NUMBER_true <- 0 IS_NUMBER_false <- 1 IS_NUMBER_oob <- 2 check_number_decimal <- function(x, ..., min = NULL, max = NULL, allow_infinite = TRUE, allow_na = FALSE, allow_null = FALSE, arg = caller_arg(x), call = caller_env()) { if (missing(x)) { exit_code <- IS_NUMBER_false } else if (0 == (exit_code <- .standalone_types_check_dot_call( ffi_standalone_check_number_1.0.7, x, allow_decimal = TRUE, min, max, allow_infinite, allow_na, allow_null ))) { return(invisible(NULL)) } .stop_not_number( x, ..., exit_code = exit_code, allow_decimal = TRUE, min = min, max = max, allow_na = allow_na, allow_null = allow_null, arg = arg, call = call ) } check_number_whole <- function(x, ..., min = NULL, max = NULL, allow_infinite = FALSE, allow_na = FALSE, allow_null = FALSE, arg = caller_arg(x), call = caller_env()) { if (missing(x)) { exit_code <- IS_NUMBER_false } else if (0 == (exit_code <- .standalone_types_check_dot_call( ffi_standalone_check_number_1.0.7, x, allow_decimal = FALSE, min, max, allow_infinite, allow_na, allow_null ))) { return(invisible(NULL)) } .stop_not_number( x, ..., exit_code = exit_code, allow_decimal = FALSE, min = min, max = max, allow_na = allow_na, allow_null = allow_null, arg = arg, call = call ) } .stop_not_number <- function(x, ..., exit_code, allow_decimal, min, max, allow_na, allow_null, arg, call) { if (allow_decimal) { what <- "a number" } else { what <- "a whole number" } if (exit_code == IS_NUMBER_oob) { min <- min %||% -Inf max <- max %||% Inf if (min > -Inf && max < Inf) { what <- sprintf("%s between %s and %s", what, min, max) } else if (x < min) { what <- sprintf("%s larger than or equal to %s", what, min) } else if (x > max) { what <- sprintf("%s smaller than or equal to %s", what, max) } else { abort("Unexpected state in OOB check", .internal = TRUE) } } stop_input_type( x, what, ..., allow_na = allow_na, allow_null = allow_null, arg = arg, call = call ) } check_symbol <- function(x, ..., allow_null = FALSE, arg = caller_arg(x), call = caller_env()) { if (!missing(x)) { if (is_symbol(x)) { return(invisible(NULL)) } if (allow_null && is_null(x)) { return(invisible(NULL)) } } stop_input_type( x, "a symbol", ..., allow_na = FALSE, allow_null = allow_null, arg = arg, call = call ) } check_arg <- function(x, ..., allow_null = FALSE, arg = caller_arg(x), call = caller_env()) { if (!missing(x)) { if (is_symbol(x)) { return(invisible(NULL)) } if (allow_null && is_null(x)) { return(invisible(NULL)) } } stop_input_type( x, "an argument name", ..., allow_na = FALSE, allow_null = allow_null, arg = arg, call = call ) } check_call <- function(x, ..., allow_null = FALSE, arg = caller_arg(x), call = caller_env()) { if (!missing(x)) { if (is_call(x)) { return(invisible(NULL)) } if (allow_null && is_null(x)) { return(invisible(NULL)) } } stop_input_type( x, "a defused call", ..., allow_na = FALSE, allow_null = allow_null, arg = arg, call = call ) } check_environment <- function(x, ..., allow_null = FALSE, arg = caller_arg(x), call = caller_env()) { if (!missing(x)) { if (is_environment(x)) { return(invisible(NULL)) } if (allow_null && is_null(x)) { return(invisible(NULL)) } } stop_input_type( x, "an environment", ..., allow_na = FALSE, allow_null = allow_null, arg = arg, call = call ) } check_function <- function(x, ..., allow_null = FALSE, arg = caller_arg(x), call = caller_env()) { if (!missing(x)) { if (is_function(x)) { return(invisible(NULL)) } if (allow_null && is_null(x)) { return(invisible(NULL)) } } stop_input_type( x, "a function", ..., allow_na = FALSE, allow_null = allow_null, arg = arg, call = call ) } check_closure <- function(x, ..., allow_null = FALSE, arg = caller_arg(x), call = caller_env()) { if (!missing(x)) { if (is_closure(x)) { return(invisible(NULL)) } if (allow_null && is_null(x)) { return(invisible(NULL)) } } stop_input_type( x, "an R function", ..., allow_na = FALSE, allow_null = allow_null, arg = arg, call = call ) } check_formula <- function(x, ..., allow_null = FALSE, arg = caller_arg(x), call = caller_env()) { if (!missing(x)) { if (is_formula(x)) { return(invisible(NULL)) } if (allow_null && is_null(x)) { return(invisible(NULL)) } } stop_input_type( x, "a formula", ..., allow_na = FALSE, allow_null = allow_null, arg = arg, call = call ) } # Vectors ----------------------------------------------------------------- check_character <- function(x, ..., allow_null = FALSE, arg = caller_arg(x), call = caller_env()) { if (!missing(x)) { if (is_character(x)) { return(invisible(NULL)) } if (allow_null && is_null(x)) { return(invisible(NULL)) } } stop_input_type( x, "a character vector", ..., allow_na = FALSE, allow_null = allow_null, arg = arg, call = call ) } check_logical <- function(x, ..., allow_null = FALSE, arg = caller_arg(x), call = caller_env()) { if (!missing(x)) { if (is_logical(x)) { return(invisible(NULL)) } if (allow_null && is_null(x)) { return(invisible(NULL)) } } stop_input_type( x, "a logical vector", ..., allow_na = FALSE, allow_null = allow_null, arg = arg, call = call ) } check_data_frame <- function(x, ..., allow_null = FALSE, arg = caller_arg(x), call = caller_env()) { if (!missing(x)) { if (is.data.frame(x)) { return(invisible(NULL)) } if (allow_null && is_null(x)) { return(invisible(NULL)) } } stop_input_type( x, "a data frame", ..., allow_null = allow_null, arg = arg, call = call ) } # nocov end clock/R/zoned-time.R0000644000176200001440000011146714427270231013760 0ustar liggesusers#' Is `x` a zoned-time? #' #' This function determines if the input is a zoned-time object. #' #' @param x `[object]` #' #' An object. #' #' @return `TRUE` if `x` inherits from `"clock_zoned_time"`, otherwise `FALSE`. #' #' @export #' @examples #' is_zoned_time(1) #' is_zoned_time(zoned_time_now("America/New_York")) is_zoned_time <- function(x) { inherits(x, "clock_zoned_time") } check_zoned_time <- function(x, ..., arg = caller_arg(x), call = caller_env()) { check_inherits(x, what = "clock_zoned_time", arg = arg, call = call) } # ------------------------------------------------------------------------------ zoned_time_zone_attribute <- function(x) { attr(x, "zone", exact = TRUE) } zoned_time_set_zone_attribute <- function(x, zone) { attr(x, "zone") <- zone x } zoned_time_precision_attribute <- function(x) { attr(x, "precision", exact = TRUE) } # ------------------------------------------------------------------------------ #' Formatting: zoned-time #' #' @description #' This is a zoned-time method for the [format()] generic. #' #' This function allows you to format a zoned-time using a flexible `format` #' string. #' #' If `format` is `NULL`, a default format of `"%Y-%m-%dT%H:%M:%S%Ez[%Z]"` is #' used. This matches the default format that [zoned_time_parse_complete()] #' parses. Additionally, this format matches the de-facto standard extension to #' RFC 3339 for creating completely unambiguous date-times. #' #' @param x `[clock_zoned_time]` #' #' A zoned-time. #' #' @param ... `[dots]` #' #' Not used, but no error will be thrown if not empty to remain compatible #' with usage of the `format()` generic. #' #' @param format `[character(1) / NULL]` #' #' If `NULL`, a default format is used, which depends on the type of the #' input. #' #' Otherwise, a format string which is a combination of: #' #' **Year** #' #' - `%C`: The year divided by 100 using floored division. If the result #' is a single decimal digit, it is prefixed with `0`. #' #' - `%y`: The last two decimal digits of the year. If the result is a single #' digit it is prefixed by `0`. #' #' - `%Y`: The year as a decimal number. If the result is less than four #' digits it is left-padded with `0` to four digits. #' #' **Month** #' #' - `%b`, `%h`: The `locale`'s abbreviated month name. #' #' - `%B`: The `locale`'s full month name. #' #' - `%m`: The month as a decimal number. January is `01`. If the result is a #' single digit, it is prefixed with `0`. #' #' **Day** #' #' - `%d`: The day of month as a decimal number. If the result is a single #' decimal digit, it is prefixed with `0`. #' #' **Day of the week** #' #' - `%a`: The `locale`'s abbreviated weekday name. #' #' - `%A`: The `locale`'s full weekday name. #' #' - `%w`: The weekday as a decimal number (`0-6`), where Sunday is `0`. #' #' **ISO 8601 week-based year** #' #' - `%g`: The last two decimal digits of the ISO week-based year. If the #' result is a single digit it is prefixed by `0`. #' #' - `%G`: The ISO week-based year as a decimal number. If the result is less #' than four digits it is left-padded with `0` to four digits. #' #' - `%V`: The ISO week-based week number as a decimal number. If the result #' is a single digit, it is prefixed with `0`. #' #' - `%u`: The ISO weekday as a decimal number (`1-7`), where Monday is `1`. #' #' **Week of the year** #' #' - `%U`: The week number of the year as a decimal number. The first Sunday #' of the year is the first day of week `01`. Days of the same year prior to #' that are in week `00`. If the result is a single digit, it is prefixed with #' `0`. #' #' - `%W`: The week number of the year as a decimal number. The first Monday #' of the year is the first day of week `01`. Days of the same year prior to #' that are in week `00`. If the result is a single digit, it is prefixed with #' `0`. #' #' **Day of the year** #' #' - `%j`: The day of the year as a decimal number. January 1 is `001`. If the #' result is less than three digits, it is left-padded with `0` to three #' digits. #' #' **Date** #' #' - `%D`, `%x`: Equivalent to `%m/%d/%y`. #' #' - `%F`: Equivalent to `%Y-%m-%d`. #' #' **Time of day** #' #' - `%H`: The hour (24-hour clock) as a decimal number. If the result is a #' single digit, it is prefixed with `0`. #' #' - `%I`: The hour (12-hour clock) as a decimal number. If the result is a #' single digit, it is prefixed with `0`. #' #' - `%M`: The minute as a decimal number. If the result is a single digit, it #' is prefixed with `0`. #' #' - `%S`: Seconds as a decimal number. Fractional seconds are printed at the #' precision of the input. The character for the decimal point is localized #' according to `locale`. #' #' - `%p`: The `locale`'s equivalent of the AM/PM designations associated with #' a 12-hour clock. #' #' - `%R`: Equivalent to `%H:%M`. #' #' - `%T`, `%X`: Equivalent to `%H:%M:%S`. #' #' - `%r`: Nearly equivalent to `%I:%M:%S %p`, but seconds are always printed #' at second precision. #' #' **Time zone** #' #' - `%z`: The offset from UTC in the ISO 8601 format. For example `-0430` #' refers to 4 hours 30 minutes behind UTC. If the offset is zero, `+0000` is #' used. The modified command `%Ez` inserts a `:` between the hour and #' minutes, like `-04:30`. #' #' - `%Z`: The full time zone name. If `abbreviate_zone` is `TRUE`, the time #' zone abbreviation. #' #' **Miscellaneous** #' #' - `%c`: A date and time representation. Similar to, but not exactly the #' same as, `%a %b %d %H:%M:%S %Y`. #' #' - `%%`: A `%` character. #' #' - `%n`: A newline character. #' #' - `%t`: A horizontal-tab character. #' #' @param locale `[clock_locale]` #' #' A locale object created from [clock_locale()]. #' #' @param abbreviate_zone `[logical(1)]` #' #' If `TRUE`, `%Z` returns an abbreviated time zone name. #' #' If `FALSE`, `%Z` returns the full time zone name. #' #' @return A character vector of the formatted input. #' #' @export #' @examples #' x <- year_month_day(2019, 1, 1) #' x <- as_zoned_time(as_naive_time(x), "America/New_York") #' #' format(x) #' format(x, format = "%B %d, %Y") #' format(x, format = "%B %d, %Y", locale = clock_locale("fr")) format.clock_zoned_time <- function(x, ..., format = NULL, locale = clock_locale(), abbreviate_zone = FALSE) { check_clock_locale(locale) zone <- zoned_time_zone_attribute(x) precision <- zoned_time_precision_attribute(x) if (is_null(format)) { # Collect internal option print_zone_name <- zoned_time_print_zone_name(...) format <- zoned_time_format(print_zone_name) } labels <- locale$labels decimal_mark <- locale$decimal_mark out <- format_zoned_time_cpp( fields = x, zone = zone, abbreviate_zone = abbreviate_zone, format = format, precision_int = precision, month = labels$month, month_abbrev = labels$month_abbrev, weekday = labels$weekday, weekday_abbrev = labels$weekday_abbrev, am_pm = labels$am_pm, decimal_mark = decimal_mark ) names(out) <- clock_rcrd_names(x) out } zoned_time_print_zone_name <- function(..., print_zone_name = TRUE) { print_zone_name } zoned_time_format <- function(print_zone_name) { if (print_zone_name) { "%Y-%m-%dT%H:%M:%S%Ez[%Z]" } else { "%Y-%m-%dT%H:%M:%S%Ez" } } # ------------------------------------------------------------------------------ #' Parsing: zoned-time #' #' @description #' There are two parsers into a zoned-time, `zoned_time_parse_complete()` and #' `zoned_time_parse_abbrev()`. #' #' ## zoned_time_parse_complete() #' #' `zoned_time_parse_complete()` is a parser for _complete_ date-time strings, #' like `"2019-01-01T00:00:00-05:00[America/New_York]"`. A complete date-time #' string has both the time zone offset and full time zone name in the string, #' which is the only way for the string itself to contain all of the information #' required to construct a zoned-time. Because of this, #' `zoned_time_parse_complete()` requires both the `%z` and `%Z` commands to be #' supplied in the `format` string. #' #' The default options assume that `x` should be parsed at second precision, #' using a `format` string of `"%Y-%m-%dT%H:%M:%S%Ez[%Z]"`. This matches the #' default result from calling `format()` on a zoned-time. Additionally, this #' format matches the de-facto standard extension to RFC 3339 for creating #' completely unambiguous date-times. #' #' ## zoned_time_parse_abbrev() #' #' `zoned_time_parse_abbrev()` is a parser for date-time strings containing only #' a time zone abbreviation, like `"2019-01-01 00:00:00 EST"`. The time zone #' abbreviation is not enough to identify the full time zone name that the #' date-time belongs to, so the full time zone name must be supplied as the #' `zone` argument. However, the time zone abbreviation can help with resolving #' ambiguity around daylight saving time fallbacks. #' #' For `zoned_time_parse_abbrev()`, `%Z` must be supplied and is interpreted as #' the time zone abbreviation rather than the full time zone name. #' #' If used, the `%z` command must parse correctly, but its value will be #' completely ignored. #' #' The default options assume that `x` should be parsed at second precision, #' using a `format` string of `"%Y-%m-%d %H:%M:%S %Z"`. This matches the default #' result from calling `print()` or `format(usetz = TRUE)` on a POSIXct #' date-time. #' #' @details #' If `zoned_time_parse_complete()` is given input that is length zero, all #' `NA`s, or completely fails to parse, then no time zone will be able to be #' determined. In that case, the result will use `"UTC"`. #' #' If your date-time strings contain time zone offsets (like `-04:00`), but #' not the full time zone name, you might need [sys_time_parse()]. #' #' If your date-time strings don't contain time zone offsets or the full time #' zone name, you might need to use [naive_time_parse()]. From there, if you #' know the time zone that the date-times are supposed to be in, you can convert #' to a zoned-time with [as_zoned_time()]. #' #' @section Full Precision Parsing: #' #' It is highly recommended to parse all of the information in the date-time #' string into a type at least as precise as the string. For example, if your #' string has fractional seconds, but you only require seconds, specify a #' sub-second `precision`, then round to seconds manually using whatever #' convention is appropriate for your use case. Parsing such a string directly #' into a second precision result is ambiguous and undefined, and is unlikely to #' work as you might expect. #' #' @inheritParams rlang::args_dots_empty #' #' @param x `[character]` #' #' A character vector to parse. #' #' @param zone `[character(1)]` #' #' A full time zone name. #' #' @param format `[character / NULL]` #' #' A format string. A combination of the following commands, or `NULL`, #' in which case a default format string is used. #' #' A vector of multiple format strings can be supplied. They will be tried in #' the order they are provided. #' #' **Year** #' #' - `%C`: The century as a decimal number. The modified command `%NC` where #' `N` is a positive decimal integer specifies the maximum number of #' characters to read. If not specified, the default is `2`. Leading zeroes #' are permitted but not required. #' #' - `%y`: The last two decimal digits of the year. If the century is not #' otherwise specified (e.g. with `%C`), values in the range `[69 - 99]` are #' presumed to refer to the years `[1969 - 1999]`, and values in the range #' `[00 - 68]` are presumed to refer to the years `[2000 - 2068]`. The #' modified command `%Ny`, where `N` is a positive decimal integer, specifies #' the maximum number of characters to read. If not specified, the default is #' `2`. Leading zeroes are permitted but not required. #' #' - `%Y`: The year as a decimal number. The modified command `%NY` where `N` #' is a positive decimal integer specifies the maximum number of characters to #' read. If not specified, the default is `4`. Leading zeroes are permitted #' but not required. #' #' **Month** #' #' - `%b`, `%B`, `%h`: The `locale`'s full or abbreviated case-insensitive #' month name. #' #' - `%m`: The month as a decimal number. January is `1`. The modified command #' `%Nm` where `N` is a positive decimal integer specifies the maximum number #' of characters to read. If not specified, the default is `2`. Leading zeroes #' are permitted but not required. #' #' **Day** #' #' - `%d`, `%e`: The day of the month as a decimal number. The modified #' command `%Nd` where `N` is a positive decimal integer specifies the maximum #' number of characters to read. If not specified, the default is `2`. Leading #' zeroes are permitted but not required. #' #' **Day of the week** #' #' - `%a`, `%A`: The `locale`'s full or abbreviated case-insensitive weekday #' name. #' #' - `%w`: The weekday as a decimal number (`0-6`), where Sunday is `0`. The #' modified command `%Nw` where `N` is a positive decimal integer specifies #' the maximum number of characters to read. If not specified, the default is #' `1`. Leading zeroes are permitted but not required. #' #' **ISO 8601 week-based year** #' #' - `%g`: The last two decimal digits of the ISO week-based year. The #' modified command `%Ng` where `N` is a positive decimal integer specifies #' the maximum number of characters to read. If not specified, the default is #' `2`. Leading zeroes are permitted but not required. #' #' - `%G`: The ISO week-based year as a decimal number. The modified command #' `%NG` where `N` is a positive decimal integer specifies the maximum number #' of characters to read. If not specified, the default is `4`. Leading zeroes #' are permitted but not required. #' #' - `%V`: The ISO week-based week number as a decimal number. The modified #' command `%NV` where `N` is a positive decimal integer specifies the maximum #' number of characters to read. If not specified, the default is `2`. Leading #' zeroes are permitted but not required. #' #' - `%u`: The ISO weekday as a decimal number (`1-7`), where Monday is `1`. #' The modified command `%Nu` where `N` is a positive decimal integer #' specifies the maximum number of characters to read. If not specified, the #' default is `1`. Leading zeroes are permitted but not required. #' #' **Week of the year** #' #' - `%U`: The week number of the year as a decimal number. The first Sunday #' of the year is the first day of week `01`. Days of the same year prior to #' that are in week `00`. The modified command `%NU` where `N` is a positive #' decimal integer specifies the maximum number of characters to read. If not #' specified, the default is `2`. Leading zeroes are permitted but not #' required. #' #' - `%W`: The week number of the year as a decimal number. The first Monday #' of the year is the first day of week `01`. Days of the same year prior to #' that are in week `00`. The modified command `%NW` where `N` is a positive #' decimal integer specifies the maximum number of characters to read. If not #' specified, the default is `2`. Leading zeroes are permitted but not #' required. #' #' **Day of the year** #' #' - `%j`: The day of the year as a decimal number. January 1 is `1`. The #' modified command `%Nj` where `N` is a positive decimal integer specifies #' the maximum number of characters to read. If not specified, the default is #' `3`. Leading zeroes are permitted but not required. #' #' **Date** #' #' - `%D`, `%x`: Equivalent to `%m/%d/%y`. #' #' - `%F`: Equivalent to `%Y-%m-%d`. If modified with a width (like `%NF`), #' the width is applied to only `%Y`. #' #' **Time of day** #' #' - `%H`: The hour (24-hour clock) as a decimal number. The modified command #' `%NH` where `N` is a positive decimal integer specifies the maximum number #' of characters to read. If not specified, the default is `2`. Leading zeroes #' are permitted but not required. #' #' - `%I`: The hour (12-hour clock) as a decimal number. The modified command #' `%NI` where `N` is a positive decimal integer specifies the maximum number #' of characters to read. If not specified, the default is `2`. Leading zeroes #' are permitted but not required. #' #' - `%M`: The minutes as a decimal number. The modified command `%NM` where #' `N` is a positive decimal integer specifies the maximum number of #' characters to read. If not specified, the default is `2`. Leading zeroes #' are permitted but not required. #' #' - `%S`: The seconds as a decimal number. Leading zeroes are permitted but #' not required. If encountered, the `locale` determines the decimal point #' character. Generally, the maximum number of characters to read is #' determined by the precision that you are parsing at. For example, a #' precision of `"second"` would read a maximum of 2 characters, while a #' precision of `"millisecond"` would read a maximum of 6 (2 for the values #' before the decimal point, 1 for the decimal point, and 3 for the values #' after it). The modified command `%NS`, where `N` is a positive decimal #' integer, can be used to exactly specify the maximum number of characters to #' read. This is only useful if you happen to have seconds with more than 1 #' leading zero. #' #' - `%p`: The `locale`'s equivalent of the AM/PM designations associated with #' a 12-hour clock. The command `%I` must precede `%p` in the format string. #' #' - `%R`: Equivalent to `%H:%M`. #' #' - `%T`, `%X`: Equivalent to `%H:%M:%S`. #' #' - `%r`: Equivalent to `%I:%M:%S %p`. #' #' **Time zone** #' #' - `%z`: The offset from UTC in the format `[+|-]hh[mm]`. For example #' `-0430` refers to 4 hours 30 minutes behind UTC. And `04` refers to 4 hours #' ahead of UTC. The modified command `%Ez` parses a `:` between the hours and #' minutes and leading zeroes on the hour field are optional: #' `[+|-]h[h][:mm]`. For example `-04:30` refers to 4 hours 30 minutes behind #' UTC. And `4` refers to 4 hours ahead of UTC. #' #' - `%Z`: The full time zone name or the time zone abbreviation, depending on #' the function being used. A single word is parsed. This word can only #' contain characters that are alphanumeric, or one of `'_'`, `'/'`, `'-'` or #' `'+'`. #' #' **Miscellaneous** #' #' - `%c`: A date and time representation. Equivalent to #' `%a %b %d %H:%M:%S %Y`. #' #' - `%%`: A `%` character. #' #' - `%n`: Matches one white space character. `%n`, `%t`, and a space can be #' combined to match a wide range of white-space patterns. For example `"%n "` #' matches one or more white space characters, and `"%n%t%t"` matches one to #' three white space characters. #' #' - `%t`: Matches zero or one white space characters. #' #' @param precision `[character(1)]` #' #' A precision for the resulting zoned-time. One of: #' #' - `"second"` #' #' - `"millisecond"` #' #' - `"microsecond"` #' #' - `"nanosecond"` #' #' Setting the `precision` determines how much information `%S` attempts #' to parse. #' #' @param locale `[clock_locale]` #' #' A locale object created from [clock_locale()]. #' #' @return A zoned-time. #' #' @name zoned-parsing #' #' @examples #' library(magrittr) #' #' zoned_time_parse_complete("2019-01-01T01:02:03-05:00[America/New_York]") #' #' zoned_time_parse_complete( #' "January 21, 2019 -0500 America/New_York", #' format = "%B %d, %Y %z %Z" #' ) #' #' # Nanosecond precision #' x <- "2019/12/31 01:05:05.123456700-05:00[America/New_York]" #' zoned_time_parse_complete( #' x, #' format = "%Y/%m/%d %H:%M:%S%Ez[%Z]", #' precision = "nanosecond" #' ) #' #' # The `%z` offset must correspond to the true offset that would be used #' # if the input was parsed as a naive-time and then converted to a zoned-time #' # with `as_zoned_time()`. For example, the time that was parsed above used an #' # offset of `-05:00`. We can confirm that this is correct with: #' year_month_day(2019, 1, 1, 1, 2, 3) %>% #' as_naive_time() %>% #' as_zoned_time("America/New_York") #' #' # So the following would not parse correctly #' zoned_time_parse_complete("2019-01-01T01:02:03-04:00[America/New_York]") #' #' # `%z` is useful for breaking ties in otherwise ambiguous times. For example, #' # these two times are on either side of a daylight saving time fallback. #' # Without the `%z` offset, you wouldn't be able to tell them apart! #' x <- c( #' "1970-10-25T01:30:00-04:00[America/New_York]", #' "1970-10-25T01:30:00-05:00[America/New_York]" #' ) #' #' zoned_time_parse_complete(x) #' #' # If you have date-time strings with time zone abbreviations, #' # `zoned_time_parse_abbrev()` should be able to help. The `zone` must be #' # provided, because multiple countries may use the same time zone #' # abbreviation. For example: #' x <- "1970-01-01 02:30:30 IST" #' #' # IST = India Standard Time #' zoned_time_parse_abbrev(x, "Asia/Kolkata") #' #' # IST = Israel Standard Time #' zoned_time_parse_abbrev(x, "Asia/Jerusalem") #' #' # The time zone abbreviation is mainly useful for resolving ambiguity #' # around daylight saving time fallbacks. Without the abbreviation, these #' # date-times would be ambiguous. #' x <- c( #' "1970-10-25 01:30:00 EDT", #' "1970-10-25 01:30:00 EST" #' ) #' zoned_time_parse_abbrev(x, "America/New_York") NULL #' @rdname zoned-parsing #' @export zoned_time_parse_complete <- function(x, ..., format = NULL, precision = "second", locale = clock_locale()) { check_dots_empty0(...) check_character(x) check_zoned_time_precision(precision) precision <- precision_to_integer(precision) check_clock_locale(locale) if (is_null(format)) { # Use both %z and %Z format <- zoned_time_format(print_zone_name = TRUE) } labels <- locale$labels mark <- locale$decimal_mark result <- zoned_time_parse_complete_cpp( x, format, precision, labels$month, labels$month_abbrev, labels$weekday, labels$weekday_abbrev, labels$am_pm, mark ) new_zoned_time_from_fields(result$fields, precision, result$zone, names(x)) } #' @rdname zoned-parsing #' @export zoned_time_parse_abbrev <- function(x, zone, ..., format = NULL, precision = "second", locale = clock_locale()) { check_dots_empty0(...) check_character(x) check_zoned_time_precision(precision) precision <- precision_to_integer(precision) check_clock_locale(locale) if (is_null(format)) { # Like what R POSIXct prints format <- "%Y-%m-%d %H:%M:%S %Z" } labels <- locale$labels mark <- locale$decimal_mark fields <- zoned_time_parse_abbrev_cpp( x, zone, format, precision, labels$month, labels$month_abbrev, labels$weekday, labels$weekday_abbrev, labels$am_pm, mark ) new_zoned_time_from_fields(fields, precision, zone, names(x)) } # ------------------------------------------------------------------------------ # ptype2 / cast will prevent zoned times with different zones from being # compared, so the equality proxy doesn't need to worry about the `zone`. #' @export vec_proxy.clock_zoned_time <- function(x, ...) { .Call(`_clock_clock_rcrd_proxy`, x) } #' @export vec_restore.clock_zoned_time <- function(x, to, ...) { .Call(`_clock_zoned_time_restore`, x, to) } # ------------------------------------------------------------------------------ #' @export vec_ptype_full.clock_zoned_time <- function(x, ...) { zone <- zone_pretty(zoned_time_zone_attribute(x)) precision <- zoned_time_precision_attribute(x) precision <- precision_to_string(precision) paste0("zoned_time<", precision, "><", zone, ">") } #' @export vec_ptype_abbr.clock_zoned_time <- function(x, ...) { zone <- zone_pretty(zoned_time_zone_attribute(x)) precision <- zoned_time_precision_attribute(x) precision <- precision_to_string(precision) precision <- precision_abbr(precision) paste0("zt<", precision, "><", zone, ">") } zone_pretty <- function(zone) { if (identical(zone, "")) { zone <- zone_current() zone <- paste0(zone, " (current)") } zone } # ------------------------------------------------------------------------------ #' @export vec_ptype.clock_zoned_time <- function(x, ...) { zone <- zoned_time_zone_attribute(x) ptype_utc <- switch( zoned_time_precision_attribute(x) + 1L, abort("Internal error: Invalid precision"), abort("Internal error: Invalid precision"), abort("Internal error: Invalid precision"), abort("Internal error: Invalid precision"), abort("Internal error: Invalid precision"), abort("Internal error: Invalid precision"), abort("Internal error: Invalid precision"), clock_empty_zoned_time_utc_second, clock_empty_zoned_time_utc_millisecond, clock_empty_zoned_time_utc_microsecond, clock_empty_zoned_time_utc_nanosecond, abort("Internal error: Invalid precision.") ) ptype <- zoned_time_set_zone_attribute(ptype_utc, zone) ptype } #' @export vec_ptype2.clock_zoned_time.clock_zoned_time <- function(x, y, ...) { x_zone <- zoned_time_zone_attribute(x) y_zone <- zoned_time_zone_attribute(y) if (x_zone != y_zone) { stop_incompatible_type(x, y, ..., details = "Zones can't differ.") } x_precision <- zoned_time_precision_attribute(x) y_precision <- zoned_time_precision_attribute(y) if (x_precision >= y_precision) { x } else { y } } #' @export vec_cast.clock_zoned_time.clock_zoned_time <- function(x, to, ...) { x_zone <- zoned_time_zone_attribute(x) to_zone <- zoned_time_zone_attribute(to) if (x_zone != to_zone) { stop_incompatible_cast(x, to, ..., details = "Zones can't differ.") } x_precision <- zoned_time_precision_attribute(x) to_precision <- zoned_time_precision_attribute(to) if (x_precision == to_precision) { return(x) } if (x_precision > to_precision) { stop_incompatible_cast(x, to, ..., details = "Precision would be lost.") } fields <- duration_cast_cpp(x, x_precision, to_precision) names <- clock_rcrd_names(x) new_zoned_time_from_fields(fields, to_precision, x_zone, names) } # ------------------------------------------------------------------------------ #' @export print.clock_zoned_time <- function(x, ..., max = NULL) { clock_print(x, max) } # - Pass through internal option to not print zone name # - Unlike vctrs, don't use `print(quote = FALSE)` since we want to match base R #' @export obj_print_data.clock_zoned_time <- function(x, ..., max) { if (vec_is_empty(x)) { return(invisible(x)) } x <- max_slice(x, max) out <- format(x, print_zone_name = FALSE) # Pass `max` to avoid base R's default footer print(out, max = max) invisible(x) } #' @export obj_print_footer.clock_zoned_time <- function(x, ..., max) { clock_print_footer(x, max) } # Align left to match pillar_shaft.Date # @export - lazy in .onLoad() pillar_shaft.clock_zoned_time <- function(x, ...) { out <- format(x, print_zone_name = FALSE) pillar::new_pillar_shaft_simple(out, align = "left") } # ------------------------------------------------------------------------------ #' Convert to a zoned-time #' #' @description #' `as_zoned_time()` converts `x` to a zoned-time. You generally convert #' to a zoned time from either a sys-time or a naive time. Each are documented #' on their own page: #' #' - [sys-time][as-zoned-time-sys-time] #' #' - [naive-time][as-zoned-time-naive-time] #' #' There are also convenience methods for converting to a zoned time from #' native R date and date-time types: #' #' - [dates (Date)][as-zoned-time-Date] #' #' - [date-times (POSIXct / POSIXlt)][as-zoned-time-posixt] #' #' @inheritParams rlang::args_dots_empty #' #' @param x `[object]` #' #' An object to convert to a zoned-time. #' #' @return A zoned-time vector. #' #' @export #' @examples #' x <- as.Date("2019-01-01") #' as_zoned_time(x, "Europe/London") #' #' y <- as_naive_time(year_month_day(2019, 2, 1)) #' as_zoned_time(y, zone = "America/New_York") as_zoned_time <- function(x, ...) { UseMethod("as_zoned_time") } #' @export as_zoned_time.default <- function(x, ...) { stop_clock_unsupported_conversion(x, "clock_zoned_time") } #' @export as_zoned_time.clock_zoned_time <- function(x, ...) { check_dots_empty0(...) x } # ------------------------------------------------------------------------------ #' @export as_sys_time.clock_zoned_time <- function(x, ...) { check_dots_empty0(...) names <- clock_rcrd_names(x) precision <- zoned_time_precision_attribute(x) new_sys_time_from_fields(x, precision, names) } #' @export as_naive_time.clock_zoned_time <- function(x, ...) { check_dots_empty0(...) names <- clock_rcrd_names(x) zone <- zoned_time_zone_attribute(x) precision <- zoned_time_precision_attribute(x) fields <- get_naive_time_cpp(x, precision, zone) new_naive_time_from_fields(fields, precision, names) } #' @export as.character.clock_zoned_time <- function(x, ...) { format(x) } # ------------------------------------------------------------------------------ #' What is the current zoned-time? #' #' @description #' `zoned_time_now()` returns the current time in the corresponding `zone`. It #' is a wrapper around [sys_time_now()] that attaches the time zone. #' #' @details #' The time is returned with a nanosecond precision, but the actual amount #' of data returned is OS dependent. Usually, information at at least the #' microsecond level is returned, with some platforms returning nanosecond #' information. #' #' @param zone `[character(1)]` #' #' A time zone to get the current time for. #' #' @return A zoned-time of the current time. #' #' @export #' @examples #' x <- zoned_time_now("America/New_York") zoned_time_now <- function(zone) { check_zone(zone) names <- NULL sys_time <- sys_time_now() precision <- time_point_precision_attribute(sys_time) new_zoned_time_from_fields(sys_time, precision, zone, names) } # ------------------------------------------------------------------------------ #' Info: zoned-time #' #' @description #' `zoned_time_info()` retrieves a set of low-level information generally not #' required for most date-time manipulations. It returns a data frame with the #' same columns as [sys_time_info()], but the `begin` and `end` columns are #' zoned-times with the same time zone as `x`. #' #' @param x `[clock_zoned_time]` #' #' A zoned-time. #' #' @return A data frame of low level information. #' #' @export #' @examples #' x <- year_month_day(2021, 03, 14, c(01, 03), c(59, 00), c(59, 00)) #' x <- as_naive_time(x) #' x <- as_zoned_time(x, "America/New_York") #' #' # x[1] is in EST, x[2] is in EDT #' x #' #' info <- zoned_time_info(x) #' info #' #' # `end` can be used to iterate through daylight saving time transitions #' zoned_time_info(info$end) zoned_time_info <- function(x) { check_zoned_time(x) zone <- zoned_time_zone_attribute(x) x <- as_sys_time(x) out <- sys_time_info(x, zone) out$begin <- as_zoned_time(out$begin, zone = zone) out$end <- as_zoned_time(out$end, zone = zone) out } # ------------------------------------------------------------------------------ #' Get or set the time zone #' #' @description #' - `zoned_time_zone()` gets the time zone. #' #' - `zoned_time_set_zone()` sets the time zone _without changing the #' underlying instant_. This means that the result will represent the equivalent #' time in the new time zone. #' #' @param x `[zoned_time]` #' #' A zoned time to get or set the time zone of. #' #' @param zone `[character(1)]` #' #' A valid time zone to switch to. #' #' @return #' - `zoned_time_zone()` returns a string containing the time zone. #' #' - `zoned_time_set_zone()` returns `x` with an altered time zone attribute. #' The underlying instant is _not_ changed. #' #' @name zoned-zone #' @examples #' x <- year_month_day(2019, 1, 1) #' x <- as_zoned_time(as_naive_time(x), "America/New_York") #' x #' #' zoned_time_zone(x) #' #' # Equivalent UTC time #' zoned_time_set_zone(x, "UTC") #' #' # To force a new time zone with the same printed time, #' # convert to a naive time that has no implied time zone, #' # then convert back to a zoned time in the new time zone. #' nt <- as_naive_time(x) #' nt #' as_zoned_time(nt, "UTC") NULL #' @rdname zoned-zone #' @export zoned_time_zone <- function(x) { check_zoned_time(x) zoned_time_zone_attribute(x) } #' @rdname zoned-zone #' @export zoned_time_set_zone <- function(x, zone) { check_zoned_time(x) check_zone(zone) zoned_time_set_zone_attribute(x, zone) } # ------------------------------------------------------------------------------ #' Precision: zoned-time #' #' `zoned_time_precision()` extracts the precision from a zoned-time. It #' returns the precision as a single string. #' #' @param x `[clock_zoned_time]` #' #' A zoned-time. #' #' @return A single string holding the precision of the zoned-time. #' #' @export #' @examples #' zoned_time_precision(zoned_time_now("America/New_York")) zoned_time_precision <- function(x) { check_zoned_time(x) precision <- zoned_time_precision_attribute(x) precision <- precision_to_string(precision) precision } # ------------------------------------------------------------------------------ #' @export add_years.clock_zoned_time <- function(x, n, ...) { details <- c( i = "Do you need to convert to a calendar first?", i = cli::format_inline(paste0( "Use {.fn as_naive_time} then {.fn as_year_month_day} to convert to a ", "calendar that supports {.fn add_years}." )) ) stop_clock_unsupported(x, details = details) } #' @export add_quarters.clock_zoned_time <- function(x, n, ...) { details <- c( i = "Do you need to convert to a calendar first?", i = cli::format_inline(paste0( "Use {.fn as_naive_time} then {.fn as_year_quarter_day} to convert to a ", "calendar that supports {.fn add_quarters}." )) ) stop_clock_unsupported(x, details = details) } #' @export add_months.clock_zoned_time <- function(x, n, ...) { details <- c( i = "Do you need to convert to a calendar first?", i = cli::format_inline(paste0( "Use {.fn as_naive_time} then {.fn as_year_month_day} to convert to a ", "calendar that supports {.fn add_months}." )) ) stop_clock_unsupported(x, details = details) } #' @export add_weeks.clock_zoned_time <- function(x, n, ...) { stop_clock_unsupported(x, details = advice_convert_to_time_point()) } #' @export add_days.clock_zoned_time <- function(x, n, ...) { stop_clock_unsupported(x, details = advice_convert_to_time_point()) } #' @export add_hours.clock_zoned_time <- function(x, n, ...) { stop_clock_unsupported(x, details = advice_convert_to_time_point()) } #' @export add_minutes.clock_zoned_time <- function(x, n, ...) { stop_clock_unsupported(x, details = advice_convert_to_time_point()) } #' @export add_seconds.clock_zoned_time <- function(x, n, ...) { stop_clock_unsupported(x, details = advice_convert_to_time_point()) } #' @export add_milliseconds.clock_zoned_time <- function(x, n, ...) { stop_clock_unsupported(x, details = advice_convert_to_time_point()) } #' @export add_microseconds.clock_zoned_time <- function(x, n, ...) { stop_clock_unsupported(x, details = advice_convert_to_time_point()) } #' @export add_nanoseconds.clock_zoned_time <- function(x, n, ...) { stop_clock_unsupported(x, details = advice_convert_to_time_point()) } # ------------------------------------------------------------------------------ check_zone <- function(x, ..., arg = caller_arg(x), call = caller_env()) { check_string(x, arg = arg, call = call) if (zone_is_valid(x)) { return(invisible(NULL)) } message <- c( "{.arg {arg}} must be a valid time zone name.", i = "{.str {x}} is invalid.", i = "Allowed time zone names are listed in {.run clock::tzdb_names()}." ) cli::cli_abort(message, call = call) } # ------------------------------------------------------------------------------ check_zoned_time_precision <- function(x, ..., arg = caller_arg(x), call = caller_env()) { check_precision( x = x, values = c("second", "millisecond", "microsecond", "nanosecond"), arg = arg, call = call ) } # ------------------------------------------------------------------------------ clock_init_zoned_time_utils <- function(env) { assign("clock_empty_zoned_time_utc_second", as_zoned_time(as_sys_time(duration_seconds()), "UTC"), envir = env) assign("clock_empty_zoned_time_utc_millisecond", as_zoned_time(as_sys_time(duration_milliseconds()), "UTC"), envir = env) assign("clock_empty_zoned_time_utc_microsecond", as_zoned_time(as_sys_time(duration_microseconds()), "UTC"), envir = env) assign("clock_empty_zoned_time_utc_nanosecond", as_zoned_time(as_sys_time(duration_nanoseconds()), "UTC"), envir = env) invisible(NULL) } clock/R/tzdb.R0000644000176200001440000000017314040275170012635 0ustar liggesusers#' @importFrom tzdb tzdb_names #' @export tzdb::tzdb_names #' @importFrom tzdb tzdb_version #' @export tzdb::tzdb_version clock/R/strict.R0000644000176200001440000000326614427165557013230 0ustar liggesusers# ------------------------------------------------------------------------------ strict_validate_invalid <- function(invalid) { if (!is_null(invalid)) { return(invalid) } if (in_strict_mode()) { abort(paste0( "The global option, `clock.strict`, is currently set to `TRUE`. ", "In this mode, `invalid` must be set and cannot be left as `NULL`." )) } "error" } # ------------------------------------------------------------------------------ check_nonexistent_strict <- function(nonexistent, call = caller_env()) { if (!is_null(nonexistent)) { return(nonexistent) } if (in_strict_mode()) { message <- paste0( "The global option, `clock.strict`, is currently set to `TRUE`. ", "In this mode, `nonexistent` must be set and cannot be left as `NULL`." ) abort(message, call = call) } "error" } # ------------------------------------------------------------------------------ check_ambiguous_strict <- function(ambiguous, call = caller_env()) { if (!is_null(ambiguous)) { return(ambiguous) } if (in_strict_mode()) { message <- paste0( "The global option, `clock.strict`, is currently set to `TRUE`. ", "In this mode, `ambiguous` must be set and cannot be left as `NULL`. ", "Additionally, `ambiguous` cannot be set to a zoned-time or POSIXct ", "unless it is paired with an ambiguous time resolution strategy, like: ", "`list(, 'earliest')`." ) abort(message, call = call) } "error" } # ------------------------------------------------------------------------------ in_strict_mode <- function() { strict <- getOption("clock.strict", default = FALSE) is_true(strict) } clock/R/getters.R0000644000176200001440000000654114423730227013360 0ustar liggesusers#' Calendar getters #' #' @description #' This family of functions extract fields from a calendar vector. Each #' calendar has its own set of supported getters, which are documented on #' their own help page: #' #' - [year-month-day][year-month-day-getters] #' #' - [year-month-weekday][year-month-weekday-getters] #' #' - [year-week-day][year-week-day-getters] #' #' - [iso-year-week-day][iso-year-week-day-getters] #' #' - [year-quarter-day][year-quarter-day-getters] #' #' - [year-day][year-day-getters] #' #' There are also convenience methods for extracting certain components #' directly from R's native date and date-time types. #' #' - [dates (Date)][Date-getters] #' #' - [date-times (POSIXct / POSIXlt)][posixt-getters] #' #' @details #' You cannot extract components directly from a time point type, such as #' sys-time or naive-time. Convert it to a calendar type first. Similarly, #' a zoned-time must be converted to either a sys-time or naive-time, and #' then to a calendar type, to be able to extract components from it. #' #' @param x `[object]` #' #' An object to get the component from. #' #' @return The component. #' #' @name clock-getters #' @examples #' x <- year_month_day(2019, 1:3, 5:7, 1, 20, 30) #' get_month(x) #' get_day(x) #' get_second(x) NULL #' @rdname clock-getters #' @export get_year <- function(x) { UseMethod("get_year") } #' @rdname clock-getters #' @export get_quarter <- function(x) { UseMethod("get_quarter") } #' @rdname clock-getters #' @export get_month <- function(x) { UseMethod("get_month") } #' @rdname clock-getters #' @export get_week <- function(x) { UseMethod("get_week") } #' @rdname clock-getters #' @export get_day <- function(x) { UseMethod("get_day") } #' @rdname clock-getters #' @export get_hour <- function(x) { UseMethod("get_hour") } #' @rdname clock-getters #' @export get_minute <- function(x) { UseMethod("get_minute") } #' @rdname clock-getters #' @export get_second <- function(x) { UseMethod("get_second") } #' @rdname clock-getters #' @export get_millisecond <- function(x) { UseMethod("get_millisecond") } #' @rdname clock-getters #' @export get_microsecond <- function(x) { UseMethod("get_microsecond") } #' @rdname clock-getters #' @export get_nanosecond <- function(x) { UseMethod("get_nanosecond") } #' @rdname clock-getters #' @export get_index <- function(x) { UseMethod("get_index") } # ------------------------------------------------------------------------------ #' @export get_year.default <- function(x) { stop_clock_unsupported(x) } #' @export get_quarter.default <- function(x) { stop_clock_unsupported(x) } #' @export get_month.default <- function(x) { stop_clock_unsupported(x) } #' @export get_week.default <- function(x) { stop_clock_unsupported(x) } #' @export get_day.default <- function(x) { stop_clock_unsupported(x) } #' @export get_hour.default <- function(x) { stop_clock_unsupported(x) } #' @export get_minute.default <- function(x) { stop_clock_unsupported(x) } #' @export get_second.default <- function(x) { stop_clock_unsupported(x) } #' @export get_millisecond.default <- function(x) { stop_clock_unsupported(x) } #' @export get_microsecond.default <- function(x) { stop_clock_unsupported(x) } #' @export get_nanosecond.default <- function(x) { stop_clock_unsupported(x) } #' @export get_index.default <- function(x) { stop_clock_unsupported(x) } clock/R/weekday.R0000644000176200001440000003143214427270231013327 0ustar liggesusers#' Construct a weekday vector #' #' @description #' A `weekday` is a simple type that represents a day of the week. #' #' The most interesting thing about the weekday type is that it implements #' _circular arithmetic_, which makes determining the "next Monday" or #' "previous Tuesday" from a sys-time or naive-time easy to compute. #' See the examples. #' #' @inheritParams rlang::args_dots_empty #' #' @param code `[integer]` #' #' Integer codes between `[1, 7]` representing days of the week. The #' interpretation of these values depends on `encoding`. #' #' @param encoding `[character(1)]` #' #' One of: #' #' - `"western"`: Encode weekdays assuming `1 == Sunday` and `7 == Saturday`. #' #' - `"iso"`: Encode weekdays assuming `1 == Monday` and `7 == Sunday`. This #' is in line with the ISO standard. #' #' @return A weekday vector. #' #' @export #' @examples #' x <- as_naive_time(year_month_day(2019, 01, 05)) #' #' # This is a Saturday! #' as_weekday(x) #' #' # Adjust to the next Wednesday #' wednesday <- weekday(clock_weekdays$wednesday) #' #' # This returns the number of days until the next Wednesday using #' # circular arithmetic #' # "Wednesday - Saturday = 4 days until next Wednesday" #' wednesday - as_weekday(x) #' #' # Advance to the next Wednesday #' x_next_wednesday <- x + (wednesday - as_weekday(x)) #' #' as_weekday(x_next_wednesday) #' #' # What about the previous Tuesday? #' tuesday <- weekday(clock_weekdays$tuesday) #' x - (as_weekday(x) - tuesday) #' #' # What about the next Saturday? #' # With an additional condition that if today is a Saturday, #' # then advance to the next one. #' saturday <- weekday(clock_weekdays$saturday) #' x + 1L + (saturday - as_weekday(x + 1L)) #' #' # You can supply an ISO coding for `code` as well, where 1 == Monday. #' weekday(1:7, encoding = "western") #' weekday(1:7, encoding = "iso") weekday <- function(code = integer(), ..., encoding = "western") { check_dots_empty() # No other helpers retain names, so we don't here either code <- unname(code) code <- vec_cast(code, integer()) oob <- (code > 7L | code < 1L) & (!is.na(code)) if (any(oob)) { abort("`code` must be in range of [1, 7].") } encoding <- check_encoding(encoding) # Store as western encoding if (is_iso_encoding(encoding)) { code <- reencode_iso_to_western(code) } new_weekday(code) } new_weekday <- function(code = integer(), ..., class = NULL) { if (!is_integer(code)) { stop_input_type(code, "an integer vector") } new_vctr( code, ..., class = c(class, "clock_weekday"), inherit_base_type = FALSE ) } # ------------------------------------------------------------------------------ #' Is `x` a weekday? #' #' This function determines if the input is a weekday object. #' #' @param x `[object]` #' #' An object. #' #' @return `TRUE` if `x` inherits from `"clock_weekday"`, otherwise `FALSE`. #' #' @export #' @examples #' is_weekday(1) #' is_weekday(weekday(1)) is_weekday <- function(x) { inherits(x, "clock_weekday") } check_weekday <- function(x, ..., arg = caller_arg(x), call = caller_env()) { check_inherits(x, what = "clock_weekday", arg = arg, call = call) } # ------------------------------------------------------------------------------ #' @export format.clock_weekday <- function(x, ..., labels = "en", abbreviate = TRUE) { if (is_character(labels)) { labels <- clock_labels_lookup(labels) } check_clock_labels(labels) check_bool(abbreviate) if (abbreviate) { labels <- labels$weekday_abbrev } else { labels <- labels$weekday } out <- format_weekday_cpp(x, labels) names(out) <- names(x) out } # ------------------------------------------------------------------------------ #' @export vec_ptype_full.clock_weekday <- function(x, ...) { "weekday" } #' @export vec_ptype_abbr.clock_weekday <- function(x, ...) { "weekday" } # ------------------------------------------------------------------------------ #' @export vec_ptype.clock_weekday <- function(x, ...) { clock_empty_weekday } #' @export vec_ptype2.clock_weekday.clock_weekday <- function(x, y, ...) { x } #' @export vec_cast.clock_weekday.clock_weekday <- function(x, to, ...) { x } # ------------------------------------------------------------------------------ #' @export vec_proxy_compare.clock_weekday <- function(x, ...) { cli::cli_abort(paste0( "Can't compare or order values of the {.cls clock_weekday} type, ", "as this type does not specify a {.str first} day of the week." )) } # ------------------------------------------------------------------------------ #' Convert to a weekday #' #' `as_weekday()` converts to a weekday type. This is normally useful for #' converting to a weekday from a sys-time or naive-time. You can use this #' function along with the _circular arithmetic_ that weekday implements to #' easily get to the "next Monday" or "previous Sunday". #' #' @inheritParams rlang::args_dots_empty #' #' @param x `[object]` #' #' An object to convert to a weekday. Usually a sys-time or naive-time. #' #' @return A weekday. #' #' @export #' @examples #' x <- as_naive_time(year_month_day(2019, 01, 05)) #' #' # This is a Saturday! #' as_weekday(x) #' #' # See the examples in `?weekday` for more usage. as_weekday <- function(x, ...) { UseMethod("as_weekday") } #' @export as_weekday.clock_weekday <- function(x, ...) { check_dots_empty0(...) x } #' @export as_weekday.clock_calendar <- function(x, ...) { abort(c( "Can't extract the weekday from a calendar.", i = "Do you need to convert to a time point first?" )) } # ------------------------------------------------------------------------------ #' @export as.character.clock_weekday <- function(x, ...) { format(x) } # ------------------------------------------------------------------------------ #' Extract underlying weekday codes #' #' `weekday_code()` extracts out the integer code for the weekday. #' #' @inheritParams weekday #' #' @param x `[weekday]` #' #' A weekday vector. #' #' @return An integer vector of codes. #' #' @export #' @examples #' # Here we supply a western encoding to `weekday()` #' x <- weekday(1:7) #' x #' #' # We can extract out the codes using different encodings #' weekday_code(x, encoding = "western") #' weekday_code(x, encoding = "iso") weekday_code <- function(x, ..., encoding = "western") { check_dots_empty0(...) check_weekday(x) encoding <- check_encoding(encoding) x <- unstructure(x) if (is_iso_encoding(encoding)) { x <- reencode_western_to_iso(x) } x } # ------------------------------------------------------------------------------ #' Convert a weekday to an ordered factor #' #' `weekday_factor()` converts a weekday object to an ordered factor. This #' can be useful in combination with ggplot2, or for modeling. #' #' @inheritParams weekday_code #' @inheritParams clock_locale #' #' @param abbreviate `[logical(1)]` #' #' If `TRUE`, the abbreviated weekday names from `labels` will be used. #' #' If `FALSE`, the full weekday names from `labels` will be used. #' #' @param encoding `[character(1)]` #' #' One of: #' #' - `"western"`: Encode the weekdays as an ordered factor with levels from #' Sunday -> Saturday. #' #' - `"iso"`: Encode the weekdays as an ordered factor with levels from #' Monday -> Sunday. #' #' @return An ordered factor representing the weekdays. #' #' @export #' @examples #' x <- weekday(1:7) #' #' # Default to Sunday -> Saturday #' weekday_factor(x) #' #' # ISO encoding is Monday -> Sunday #' weekday_factor(x, encoding = "iso") #' #' # With full names #' weekday_factor(x, abbreviate = FALSE) #' #' # Or a different language #' weekday_factor(x, labels = "fr") weekday_factor <- function(x, ..., labels = "en", abbreviate = TRUE, encoding = "western") { check_dots_empty0(...) check_weekday(x) if (is_character(labels)) { labels <- clock_labels_lookup(labels) } check_clock_labels(labels) check_bool(abbreviate) if (abbreviate) { labels <- labels$weekday_abbrev } else { labels <- labels$weekday } encoding <- check_encoding(encoding) x <- weekday_code(x, encoding = encoding) if (is_iso_encoding(encoding)) { labels <- c(labels[2:7], labels[1L]) } x <- labels[x] factor(x, levels = labels, ordered = TRUE) } # ------------------------------------------------------------------------------ #' @rdname clock-arith #' @method vec_arith clock_weekday #' @export vec_arith.clock_weekday <- function(op, x, y, ...) { UseMethod("vec_arith.clock_weekday", y) } #' @method vec_arith.clock_weekday MISSING #' @export vec_arith.clock_weekday.MISSING <- function(op, x, y, ...) { switch( op, "+" = x, stop_incompatible_op(op, x, y, ...) ) } #' @method vec_arith.clock_weekday clock_weekday #' @export vec_arith.clock_weekday.clock_weekday <- function(op, x, y, ...) { switch( op, "-" = weekday_minus_weekday(op, x, y, ...), stop_incompatible_op(op, x, y, ...) ) } #' @method vec_arith.clock_weekday clock_duration #' @export vec_arith.clock_weekday.clock_duration <- function(op, x, y, ...) { switch( op, "+" = add_duration(x, y), "-" = add_duration(x, -y), stop_incompatible_op(op, x, y, ...) ) } #' @method vec_arith.clock_duration clock_weekday #' @export vec_arith.clock_duration.clock_weekday <- function(op, x, y, ...) { switch( op, "+" = add_duration(y, x, swapped = TRUE), "-" = stop_incompatible_op(op, x, y, details = "Can't subtract a weekday from a duration.", ...), stop_incompatible_op(op, x, y, ...) ) } #' @method vec_arith.clock_weekday numeric #' @export vec_arith.clock_weekday.numeric <- function(op, x, y, ...) { switch( op, "+" = add_duration(x, duration_helper(y, PRECISION_DAY, retain_names = TRUE)), "-" = add_duration(x, duration_helper(-y, PRECISION_DAY, retain_names = TRUE)), stop_incompatible_op(op, x, y, ...) ) } #' @method vec_arith.numeric clock_weekday #' @export vec_arith.numeric.clock_weekday <- function(op, x, y, ...) { switch( op, "+" = add_duration(y, duration_helper(x, PRECISION_DAY, retain_names = TRUE), swapped = TRUE), "-" = stop_incompatible_op(op, x, y, details = "Can't subtract a weekday from a duration.", ...), stop_incompatible_op(op, x, y, ...) ) } # Subtraction between weekdays tells days to next weekday weekday_minus_weekday <- function(op, x, y, ...) { args <- vec_recycle_common(x = x, y = y) x <- args$x y <- args$y names <- names_common(x, y) fields <- weekday_minus_weekday_cpp(x, y) new_duration_from_fields(fields, PRECISION_DAY, names) } # ------------------------------------------------------------------------------ #' Arithmetic: weekday #' #' @description #' These are weekday methods for the #' [arithmetic generics][clock-arithmetic]. #' #' - `add_days()` #' #' Also check out the examples on the [weekday()] page for more advanced #' usage. #' #' @details #' `x` and `n` are recycled against each other using #' [tidyverse recycling rules][vctrs::vector_recycling_rules]. #' #' @inheritParams clock-arithmetic #' #' @param x `[clock_weekday]` #' #' A weekday vector. #' #' @return `x` after performing the arithmetic. #' #' @name weekday-arithmetic #' #' @examples #' saturday <- weekday(clock_weekdays$saturday) #' saturday #' #' add_days(saturday, 1) #' add_days(saturday, 2) NULL #' @rdname weekday-arithmetic #' @export add_days.clock_weekday <- function(x, n, ...) { n <- duration_collect_n(n, PRECISION_DAY) args <- vec_recycle_common(x = x, n = n) x <- args$x n <- args$n names <- names_common(x, n) code <- weekday_add_days_cpp(x, n) names(code) <- names new_weekday(code) } # ------------------------------------------------------------------------------ #' @export vec_math.clock_weekday <- function(.fn, .x, ...) { switch( .fn, is.nan = weekday_is_nan(.x), is.finite = weekday_is_finite(.x), is.infinite = weekday_is_infinite(.x), NextMethod() ) } weekday_is_nan <- function(x) { vec_rep(FALSE, vec_size(x)) } weekday_is_finite <- function(x) { !vec_detect_missing(x) } weekday_is_infinite <- function(x) { vec_rep(FALSE, vec_size(x)) } # ------------------------------------------------------------------------------ check_encoding <- function(encoding, ..., error_call = caller_env()) { arg_match0(encoding, c("western", "iso"), error_call = error_call) } is_iso_encoding <- function(encoding) { identical(encoding, "iso") } reencode_iso_to_western <- function(code) { code <- code + 1L code[code == 8L] <- 1L code } reencode_western_to_iso <- function(code) { code <- code - 1L code[code == 0L] <- 7L code } # ------------------------------------------------------------------------------ clock_init_weekday_utils <- function(env) { assign("clock_empty_weekday", weekday(integer()), envir = env) invisible(NULL) } clock/R/gregorian-year-day.R0000644000176200001440000010263014427270231015363 0ustar liggesusers#' Calendar: year-day #' #' `year_day()` constructs a calendar vector from the Gregorian #' year and day of the year. #' #' @details #' Fields are recycled against each other using #' [tidyverse recycling rules][vctrs::vector_recycling_rules]. #' #' Fields are collected in order until the first `NULL` field is located. No #' fields after the first `NULL` field are used. #' #' @inheritParams year_month_day #' #' @param day `[integer / NULL]` #' #' The day of the year. Values `[1, 366]` are allowed. #' #' @return A year-day calendar vector. #' #' @export #' @examples #' # Just the year #' x <- year_day(2019:2025) #' x #' #' year_day(2020, 1:10) #' #' # Last day of the year, accounting for leap years #' year_day(2019:2021, "last") #' #' # Precision can go all the way out to nanosecond #' year_day(2019, 100, 2, 40, 45, 200, subsecond_precision = "nanosecond") year_day <- function(year, day = NULL, hour = NULL, minute = NULL, second = NULL, subsecond = NULL, ..., subsecond_precision = NULL) { check_dots_empty() # Stop on the first `NULL` argument if (is_null(day)) { precision <- PRECISION_YEAR fields <- list(year = year) } else if (is_null(hour)) { precision <- PRECISION_DAY fields <- list(year = year, day = day) } else if (is_null(minute)) { precision <- PRECISION_HOUR fields <- list(year = year, day = day, hour = hour) } else if (is_null(second)) { precision <- PRECISION_MINUTE fields <- list(year = year, day = day, hour = hour, minute = minute) } else if (is_null(subsecond)) { precision <- PRECISION_SECOND fields <- list(year = year, day = day, hour = hour, minute = minute, second = second) } else { calendar_check_subsecond_precision(subsecond_precision) precision <- precision_to_integer(subsecond_precision) fields <- list(year = year, day = day, hour = hour, minute = minute, second = second, subsecond = subsecond) } if (is_last(fields$day)) { fields$day <- 1L last <- TRUE } else { last <- FALSE } fields <- vec_cast_common(!!!fields, .to = integer()) if (precision >= PRECISION_YEAR) { check_between_year(fields$year, arg = "year") } if (precision >= PRECISION_DAY) { check_between_day_of_year(fields$day, arg = "day") } if (precision >= PRECISION_HOUR) { check_between_hour(fields$hour, arg = "hour") } if (precision >= PRECISION_MINUTE) { check_between_minute(fields$minute, arg = "minute") } if (precision >= PRECISION_SECOND) { check_between_second(fields$second, arg = "second") } if (precision > PRECISION_SECOND) { check_between_subsecond(fields$subsecond, precision, arg = "subsecond") } fields <- vec_recycle_common(!!!fields) fields <- df_list_propagate_missing(fields) names <- NULL out <- new_year_day_from_fields(fields, precision, names) if (last) { out <- set_day(out, "last") } out } # ------------------------------------------------------------------------------ #' @export vec_proxy.clock_year_day <- function(x, ...) { .Call(`_clock_clock_rcrd_proxy`, x) } #' @export vec_restore.clock_year_day <- function(x, to, ...) { .Call(`_clock_year_day_restore`, x, to) } # ------------------------------------------------------------------------------ #' @export format.clock_year_day <- function(x, ...) { out <- format_year_day_cpp(x, calendar_precision_attribute(x)) names(out) <- names(x) out } #' @export vec_ptype_full.clock_year_day <- function(x, ...) { calendar_ptype_full(x, "year_day") } #' @export vec_ptype_abbr.clock_year_day <- function(x, ...) { calendar_ptype_abbr(x, "yd") } # ------------------------------------------------------------------------------ #' Is `x` a year-day? #' #' Check if `x` is a year-day. #' #' @param x `[object]` #' #' An object. #' #' @return Returns `TRUE` if `x` inherits from `"clock_year_day"`, #' otherwise returns `FALSE`. #' #' @export #' @examples #' is_year_day(year_day(2019)) is_year_day <- function(x) { inherits(x, "clock_year_day") } # ------------------------------------------------------------------------------ #' @export vec_ptype.clock_year_day <- function(x, ...) { switch( calendar_precision_attribute(x) + 1L, clock_empty_year_day_year, abort("Internal error: Invalid precision"), abort("Internal error: Invalid precision"), abort("Internal error: Invalid precision"), clock_empty_year_day_day, clock_empty_year_day_hour, clock_empty_year_day_minute, clock_empty_year_day_second, clock_empty_year_day_millisecond, clock_empty_year_day_microsecond, clock_empty_year_day_nanosecond, abort("Internal error: Invalid precision.") ) } #' @export vec_ptype2.clock_year_day.clock_year_day <- function(x, y, ...) { ptype2_calendar_and_calendar(x, y, ...) } #' @export vec_cast.clock_year_day.clock_year_day <- function(x, to, ...) { cast_calendar_to_calendar(x, to, ...) } # ------------------------------------------------------------------------------ #' @export calendar_is_precision.clock_year_day <- function(x, precision) { year_day_is_precision(precision) } year_day_is_precision <- function(precision) { if (precision == PRECISION_YEAR) { TRUE } else if (precision >= PRECISION_DAY && precision <= PRECISION_NANOSECOND) { TRUE } else { FALSE } } # ------------------------------------------------------------------------------ #' @export invalid_detect.clock_year_day <- function(x) { precision <- calendar_precision_attribute(x) if (precision < PRECISION_DAY) { rep_along(x, FALSE) } else { year <- field_year(x) day <- field_day(x) invalid_detect_year_day_cpp(year, day) } } #' @export invalid_any.clock_year_day <- function(x) { precision <- calendar_precision_attribute(x) if (precision < PRECISION_DAY) { FALSE } else { year <- field_year(x) day <- field_day(x) invalid_any_year_day_cpp(year, day) } } #' @export invalid_count.clock_year_day <- function(x) { precision <- calendar_precision_attribute(x) if (precision < PRECISION_DAY) { 0L } else { year <- field_year(x) day <- field_day(x) invalid_count_year_day_cpp(year, day) } } #' @export invalid_resolve.clock_year_day <- function(x, ..., invalid = NULL) { check_dots_empty() precision <- calendar_precision_attribute(x) invalid <- validate_invalid(invalid) if (precision < PRECISION_DAY) { x } else { fields <- invalid_resolve_year_day_cpp(x, precision, invalid, current_env()) new_year_day_from_fields(fields, precision, names(x)) } } # ------------------------------------------------------------------------------ #' Getters: year-day #' #' @description #' These are year-day methods for the [getter generics][clock-getters]. #' #' - `get_year()` returns the Gregorian year. #' #' - `get_day()` returns the day of the year. #' #' - There are sub-daily getters for extracting more precise components. #' #' @param x `[clock_year_day]` #' #' A year-day to get the component from. #' #' @return The component. #' #' @name year-day-getters #' @examples #' x <- year_day(2019, 101:105, 1, 20, 30) #' #' get_day(x) #' get_second(x) #' #' # Cannot extract more precise components #' y <- year_day(2019, 1) #' try(get_hour(y)) #' #' # Cannot extract components that don't exist for this calendar #' try(get_quarter(x)) NULL #' @rdname year-day-getters #' @export get_year.clock_year_day <- function(x) { field_year(x) } #' @rdname year-day-getters #' @export get_day.clock_year_day <- function(x) { calendar_check_minimum_precision(x, PRECISION_DAY) field_day(x) } #' @rdname year-day-getters #' @export get_hour.clock_year_day <- function(x) { calendar_check_minimum_precision(x, PRECISION_HOUR) field_hour(x) } #' @rdname year-day-getters #' @export get_minute.clock_year_day <- function(x) { calendar_check_minimum_precision(x, PRECISION_MINUTE) field_minute(x) } #' @rdname year-day-getters #' @export get_second.clock_year_day <- function(x) { calendar_check_minimum_precision(x, PRECISION_SECOND) field_second(x) } #' @rdname year-day-getters #' @export get_millisecond.clock_year_day <- function(x) { calendar_check_exact_precision(x, PRECISION_MILLISECOND) field_subsecond(x) } #' @rdname year-day-getters #' @export get_microsecond.clock_year_day <- function(x) { calendar_check_exact_precision(x, PRECISION_MICROSECOND) field_subsecond(x) } #' @rdname year-day-getters #' @export get_nanosecond.clock_year_day <- function(x) { calendar_check_exact_precision(x, PRECISION_NANOSECOND) field_subsecond(x) } # ------------------------------------------------------------------------------ #' Setters: year-day #' #' @description #' These are year-day methods for the #' [setter generics][clock-setters]. #' #' - `set_year()` sets the Gregorian year. #' #' - `set_day()` sets the day of the year. Valid values are in the range #' of `[1, 366]`. #' #' - There are sub-daily setters for setting more precise components. #' #' @inheritParams rlang::args_dots_empty #' #' @param x `[clock_year_day]` #' #' A year-day vector. #' #' @param value `[integer / "last"]` #' #' The value to set the component to. #' #' For `set_day()`, this can also be `"last"` to set the day to the #' last day of the year. #' #' @return `x` with the component set. #' #' @name year-day-setters #' @examples #' x <- year_day(2019) #' #' # Set the day #' set_day(x, 12:14) #' #' # Set to the "last" day of the year #' set_day(x, "last") #' #' # Set to an invalid day of the year #' invalid <- set_day(x, 366) #' invalid #' #' # Then resolve the invalid day by choosing the next valid day #' invalid_resolve(invalid, invalid = "next") #' #' # Cannot set a component two levels more precise than where you currently are #' try(set_hour(x, 5)) NULL #' @rdname year-day-setters #' @export set_year.clock_year_day <- function(x, value, ...) { check_dots_empty() set_field_year_day(x, value, "year") } #' @rdname year-day-setters #' @export set_day.clock_year_day <- function(x, value, ...) { check_dots_empty() calendar_check_minimum_precision(x, PRECISION_YEAR) set_field_year_day(x, value, "day") } #' @rdname year-day-setters #' @export set_hour.clock_year_day <- function(x, value, ...) { check_dots_empty() calendar_check_minimum_precision(x, PRECISION_DAY) set_field_year_day(x, value, "hour") } #' @rdname year-day-setters #' @export set_minute.clock_year_day <- function(x, value, ...) { check_dots_empty() calendar_check_minimum_precision(x, PRECISION_HOUR) set_field_year_day(x, value, "minute") } #' @rdname year-day-setters #' @export set_second.clock_year_day <- function(x, value, ...) { check_dots_empty() calendar_check_minimum_precision(x, PRECISION_MINUTE) set_field_year_day(x, value, "second") } #' @rdname year-day-setters #' @export set_millisecond.clock_year_day <- function(x, value, ...) { check_dots_empty() calendar_check_exact_precision(x, c(PRECISION_SECOND, PRECISION_MILLISECOND)) set_field_year_day(x, value, "millisecond") } #' @rdname year-day-setters #' @export set_microsecond.clock_year_day <- function(x, value, ...) { check_dots_empty() calendar_check_exact_precision(x, c(PRECISION_SECOND, PRECISION_MICROSECOND)) set_field_year_day(x, value, "microsecond") } #' @rdname year-day-setters #' @export set_nanosecond.clock_year_day <- function(x, value, ...) { check_dots_empty() calendar_check_exact_precision(x, c(PRECISION_SECOND, PRECISION_NANOSECOND)) set_field_year_day(x, value, "nanosecond") } set_field_year_day <- function(x, value, component) { if (is_last(value) && identical(component, "day")) { return(set_field_year_day_last(x)) } precision_fields <- calendar_precision_attribute(x) precision_value <- year_day_component_to_precision(component) precision_out <- precision_common2(precision_fields, precision_value) names_out <- names(x) value <- vec_cast(value, integer()) value <- unname(value) switch( component, year = check_between_year(value), day = check_between_day_of_year(value), hour = check_between_hour(value), minute = check_between_minute(value), second = check_between_second(value), millisecond = check_between_subsecond(value, PRECISION_MILLISECOND), microsecond = check_between_subsecond(value, PRECISION_MICROSECOND), nanosecond = check_between_subsecond(value, PRECISION_NANOSECOND), abort("Unknown `component`", .internal = TRUE) ) args <- vec_recycle_common(x = x, value = value) args <- df_list_propagate_missing(args) x <- args$x value <- args$value field <- year_day_component_to_field(component) out <- vec_unstructure(x) out[[field]] <- value new_year_day_from_fields(out, precision_out, names = names_out) } set_field_year_day_last <- function(x) { precision_fields <- calendar_precision_attribute(x) precision_out <- precision_common2(precision_fields, PRECISION_DAY) names_out <- names(x) year <- field_year(x) value <- get_year_day_last_cpp(year) out <- vec_unstructure(x) out[["day"]] <- value new_year_day_from_fields(out, precision_out, names = names_out) } # ------------------------------------------------------------------------------ #' @export calendar_name.clock_year_day <- function(x) { "year_day" } # ------------------------------------------------------------------------------ year_day_component_to_precision <- function(component) { switch( component, year = PRECISION_YEAR, day = PRECISION_DAY, hour = PRECISION_HOUR, minute = PRECISION_MINUTE, second = PRECISION_SECOND, millisecond = PRECISION_MILLISECOND, microsecond = PRECISION_MICROSECOND, nanosecond = PRECISION_NANOSECOND, abort("Internal error: Unknown component name.") ) } year_day_component_to_field <- function(component) { switch ( component, year = component, day = component, hour = component, minute = component, second = component, millisecond = "subsecond", microsecond = "subsecond", nanosecond = "subsecond", abort("Internal error: Unknown component name.") ) } # ------------------------------------------------------------------------------ #' @rdname clock-arith #' @method vec_arith clock_year_day #' @export vec_arith.clock_year_day <- function(op, x, y, ...) { UseMethod("vec_arith.clock_year_day", y) } #' @method vec_arith.clock_year_day MISSING #' @export vec_arith.clock_year_day.MISSING <- function(op, x, y, ...) { arith_calendar_and_missing(op, x, y, ...) } #' @method vec_arith.clock_year_day clock_year_day #' @export vec_arith.clock_year_day.clock_year_day <- function(op, x, y, ...) { arith_calendar_and_calendar(op, x, y, ..., calendar_minus_calendar_fn = year_day_minus_year_day) } #' @method vec_arith.clock_year_day clock_duration #' @export vec_arith.clock_year_day.clock_duration <- function(op, x, y, ...) { arith_calendar_and_duration(op, x, y, ...) } #' @method vec_arith.clock_duration clock_year_day #' @export vec_arith.clock_duration.clock_year_day <- function(op, x, y, ...) { arith_duration_and_calendar(op, x, y, ...) } #' @method vec_arith.clock_year_day numeric #' @export vec_arith.clock_year_day.numeric <- function(op, x, y, ...) { arith_calendar_and_numeric(op, x, y, ...) } #' @method vec_arith.numeric clock_year_day #' @export vec_arith.numeric.clock_year_day <- function(op, x, y, ...) { arith_numeric_and_calendar(op, x, y, ...) } year_day_minus_year_day <- function(op, x, y, ...) { args <- vec_recycle_common(x = x, y = y) args <- vec_cast_common(!!!args) x <- args$x y <- args$y names <- names_common(x, y) precision <- calendar_precision_attribute(x) if (precision > PRECISION_YEAR) { stop_incompatible_op(op, x, y, ...) } fields <- year_day_minus_year_day_cpp(x, y, precision) new_duration_from_fields(fields, precision, names) } # ------------------------------------------------------------------------------ #' Arithmetic: year-day #' #' @description #' These are year-day methods for the #' [arithmetic generics][clock-arithmetic]. #' #' - `add_years()` #' #' Notably, _you cannot add days to a year-day_. For day-based arithmetic, #' first convert to a time point with [as_naive_time()] or [as_sys_time()]. #' #' @details #' `x` and `n` are recycled against each other using #' [tidyverse recycling rules][vctrs::vector_recycling_rules]. #' #' @inheritParams clock-arithmetic #' #' @param x `[clock_year_day]` #' #' A year-day vector. #' #' @return `x` after performing the arithmetic. #' #' @name year-day-arithmetic #' #' @examples #' x <- year_day(2019, 10) #' #' add_years(x, 1:5) #' #' # A valid day in a leap year #' y <- year_day(2020, 366) #' y #' #' # Adding 1 year to `y` generates an invalid date #' y_plus <- add_years(y, 1) #' y_plus #' #' # Invalid dates are fine, as long as they are eventually resolved #' # by either manually resolving, or by calling `invalid_resolve()` #' #' # Resolve by returning the previous / next valid moment in time #' invalid_resolve(y_plus, invalid = "previous") #' invalid_resolve(y_plus, invalid = "next") #' #' # Manually resolve by setting to the last day of the year #' invalid <- invalid_detect(y_plus) #' y_plus[invalid] <- set_day(y_plus[invalid], "last") #' y_plus NULL #' @rdname year-day-arithmetic #' @export add_years.clock_year_day <- function(x, n, ...) { year_day_plus_duration(x, n, PRECISION_YEAR) } year_day_plus_duration <- function(x, n, n_precision, ..., error_call = caller_env()) { check_dots_empty0(...) x_precision <- calendar_precision_attribute(x) n <- duration_collect_n(n, n_precision, error_call = error_call) size <- vec_size_common(x = x, n = n, .call = error_call) args <- vec_recycle_common(x = x, n = n, .size = size) x <- args$x n <- args$n names <- names_common(x, n) x <- vec_unstructure(x) if (n_precision == PRECISION_YEAR) { fields <- year_day_plus_years_cpp(x$year, n) x$year <- fields$year } else { abort("Unknown precision.", .internal = TRUE) } if (x_precision != n_precision) { x <- df_list_propagate_missing(x, size = size) } new_year_day_from_fields(x, x_precision, names = names) } # ------------------------------------------------------------------------------ #' Convert to year-day #' #' `as_year_day()` converts a vector to the year-day calendar. #' Time points, Dates, POSIXct, and other calendars can all be converted to #' year-day. #' #' @inheritParams rlang::args_dots_empty #' #' @param x `[vector]` #' #' A vector to convert to year-day. #' #' @return A year-day vector. #' @export #' @examples #' # From Date #' as_year_day(as.Date("2019-05-01")) #' #' # From POSIXct, which assumes that the naive time is what should be converted #' as_year_day(as.POSIXct("2019-05-01 02:30:30", "America/New_York")) #' #' # From other calendars #' as_year_day(year_quarter_day(2019, quarter = 2, day = 50)) as_year_day <- function(x, ...) { UseMethod("as_year_day") } #' @export as_year_day.default <- function(x, ...) { stop_clock_unsupported_conversion(x, "clock_year_day") } #' @export as_year_day.clock_year_day <- function(x, ...) { check_dots_empty0(...) x } # ------------------------------------------------------------------------------ #' @export as_sys_time.clock_year_day <- function(x, ...) { check_dots_empty0(...) calendar_check_no_invalid(x) precision <- calendar_precision_attribute(x) fields <- as_sys_time_year_day_cpp(x, precision) new_sys_time_from_fields(fields, precision, clock_rcrd_names(x)) } #' @export as_naive_time.clock_year_day <- function(x, ...) { check_dots_empty0(...) as_naive_time(as_sys_time(x)) } #' @export as.character.clock_year_day <- function(x, ...) { format(x) } # ------------------------------------------------------------------------------ #' @export calendar_leap_year.clock_year_day <- function(x) { x <- get_year(x) gregorian_leap_year_cpp(x) } # ------------------------------------------------------------------------------ #' Grouping: year-day #' #' @description #' This is a year-day method for the [calendar_group()] generic. #' #' Grouping for a year-day object can be done at any precision, as #' long as `x` is at least as precise as `precision`. #' #' @inheritParams calendar_group #' #' @param x `[clock_year_day]` #' #' A year-day vector. #' #' @param precision `[character(1)]` #' #' One of: #' #' - `"year"` #' - `"day"` #' - `"hour"` #' - `"minute"` #' - `"second"` #' - `"millisecond"` #' - `"microsecond"` #' - `"nanosecond"` #' #' @return `x` grouped at the specified `precision`. #' #' @name year-day-group #' #' @export #' @examples #' x <- seq(as_naive_time(year_month_day(2019, 1, 1)), by = 5, length.out = 20) #' x <- as_year_day(x) #' x #' #' # Group by day of the current year #' calendar_group(x, "day", n = 20) calendar_group.clock_year_day <- function(x, precision, ..., n = 1L) { n <- validate_calendar_group_n(n) x <- calendar_narrow(x, precision) check_precision(precision) precision <- precision_to_integer(precision) if (precision == PRECISION_YEAR) { value <- get_year(x) value <- group_component0(value, n) x <- set_year(x, value) return(x) } if (precision == PRECISION_DAY) { value <- get_day(x) value <- group_component1(value, n) x <- set_day(x, value) return(x) } x <- calendar_group_time(x, n, precision) x } # ------------------------------------------------------------------------------ #' Narrow: year-day #' #' This is a year-day method for the [calendar_narrow()] generic. It #' narrows a year-day vector to the specified `precision`. #' #' @inheritParams year-day-group #' #' @return `x` narrowed to the supplied `precision`. #' #' @name year-day-narrow #' #' @export #' @examples #' # Hour precision #' x <- year_day(2019, 3, 4) #' x #' #' # Narrowed to day precision #' calendar_narrow(x, "day") #' #' # Or year precision #' calendar_narrow(x, "year") #' #' # Subsecond precision can be narrowed to second precision #' milli <- calendar_widen(x, "millisecond") #' micro <- calendar_widen(x, "microsecond") #' milli #' micro #' #' calendar_narrow(milli, "second") #' calendar_narrow(micro, "second") #' #' # But once you have "locked in" a subsecond precision, it can't be #' # narrowed to another subsecond precision #' try(calendar_narrow(micro, "millisecond")) calendar_narrow.clock_year_day <- function(x, precision) { check_precision(precision) precision <- precision_to_integer(precision) out_fields <- list() x_fields <- unclass(x) if (precision >= PRECISION_YEAR) { out_fields[["year"]] <- x_fields[["year"]] } if (precision >= PRECISION_DAY) { out_fields[["day"]] <- x_fields[["day"]] } out_fields <- calendar_narrow_time(out_fields, precision, x_fields) new_year_day_from_fields(out_fields, precision, names = names(x)) } # ------------------------------------------------------------------------------ #' Widen: year-day #' #' This is a year-day method for the [calendar_widen()] generic. It #' widens a year-day vector to the specified `precision`. #' #' @inheritParams year-day-group #' #' @return `x` widened to the supplied `precision`. #' #' @name year-day-widen #' #' @export #' @examples #' # Year precision #' x <- year_day(2019) #' x #' #' # Widen to day precision #' calendar_widen(x, "day") #' #' # Or second precision #' sec <- calendar_widen(x, "second") #' sec #' #' # Second precision can be widened to subsecond precision #' milli <- calendar_widen(sec, "millisecond") #' micro <- calendar_widen(sec, "microsecond") #' milli #' micro #' #' # But once you have "locked in" a subsecond precision, it can't #' # be widened again #' try(calendar_widen(milli, "microsecond")) calendar_widen.clock_year_day <- function(x, precision) { x_precision <- calendar_precision_attribute(x) check_precision(precision) precision <- precision_to_integer(precision) if (precision >= PRECISION_DAY && x_precision < PRECISION_DAY) { x <- set_day(x, 1L) } x <- calendar_widen_time(x, x_precision, precision) x } # ------------------------------------------------------------------------------ #' Boundaries: year-day #' #' This is a year-day method for the [calendar_start()] and #' [calendar_end()] generics. They adjust components of a calendar to the #' start or end of a specified `precision`. #' #' @inheritParams year-day-group #' #' @return `x` at the same precision, but with some components altered to be #' at the boundary value. #' #' @name year-day-boundary #' #' @examples #' # Day precision #' x <- year_day(2019:2020, 5) #' x #' #' # Compute the last day of the year #' calendar_end(x, "year") NULL #' @rdname year-day-boundary #' @export calendar_start.clock_year_day <- function(x, precision) { x_precision <- calendar_precision_attribute(x) check_precision(precision) precision <- precision_to_integer(precision) calendar_start_end_checks(x, x_precision, precision, "start") if (precision <= PRECISION_YEAR && x_precision > PRECISION_YEAR) { x <- set_day(x, 1L) } x <- calendar_start_time(x, x_precision, precision) x } #' @rdname year-day-boundary #' @export calendar_end.clock_year_day <- function(x, precision) { x_precision <- calendar_precision_attribute(x) check_precision(precision) precision <- precision_to_integer(precision) calendar_start_end_checks(x, x_precision, precision, "end") if (precision <= PRECISION_YEAR && x_precision > PRECISION_YEAR) { x <- set_day(x, "last") } x <- calendar_end_time(x, x_precision, precision) x } # ------------------------------------------------------------------------------ #' Counting: year-day #' #' This is a year-day method for the [calendar_count_between()] generic. #' It counts the number of `precision` units between `start` and `end` #' (i.e., the number of years). #' #' @inheritParams calendar-count-between #' #' @param start,end `[clock_year_day]` #' #' A pair of year-day vectors. These will be recycled to their #' common size. #' #' @param precision `[character(1)]` #' #' One of: #' #' - `"year"` #' #' @inherit calendar-count-between return #' #' @name year-day-count-between #' #' @export #' @examples #' # Compute an individual's age in years #' x <- year_day(2001, 100) #' y <- year_day(2021, c(99, 101)) #' #' calendar_count_between(x, y, "year") #' #' # Or in a whole number multiple of years #' calendar_count_between(x, y, "year", n = 3) calendar_count_between.clock_year_day <- function(start, end, precision, ..., n = 1L) { NextMethod() } calendar_count_between_standardize_precision_n.clock_year_day <- function(x, precision, n) { check_precision(precision) precision_int <- precision_to_integer(precision) allowed_precisions <- c(PRECISION_YEAR) if (!(precision_int %in% allowed_precisions)) { abort("`precision` must be one of: 'year'.") } list(precision = precision, n = n) } calendar_count_between_compute.clock_year_day <- function(start, end, precision) { check_precision(precision) precision <- precision_to_integer(precision) if (precision == PRECISION_YEAR) { out <- get_year(end) - get_year(start) return(out) } abort("Internal error: `precision` should be 'year' at this point.") } calendar_count_between_proxy_compare.clock_year_day <- function(start, end, precision) { check_precision(precision) precision <- precision_to_integer(precision) start <- vec_proxy_compare(start) end <- vec_proxy_compare(end) if (precision >= PRECISION_YEAR) { start$year <- NULL end$year <- NULL } list(start = start, end = end) } # ------------------------------------------------------------------------------ #' Sequences: year-day #' #' @description #' This is a year-day method for the [seq()] generic. #' #' Sequences can only be generated for `"year"` precision year-day vectors. #' #' When calling `seq()`, exactly two of the following must be specified: #' - `to` #' - `by` #' - Either `length.out` or `along.with` #' #' @inheritParams seq.clock_duration #' #' @param from `[clock_year_day(1)]` #' #' A `"year"` precision year-day to start the sequence from. #' #' `from` is always included in the result. #' #' @param to `[clock_year_day(1) / NULL]` #' #' A `"year"` precision year-day to stop the sequence at. #' #' `to` is cast to the type of `from`. #' #' `to` is only included in the result if the resulting sequence divides #' the distance between `from` and `to` exactly. #' #' @return A sequence with the type of `from`. #' #' @export #' @examples #' # Yearly sequence #' x <- seq(year_day(2020), year_day(2040), by = 2) #' x #' #' # Which we can then set the day of to get a sequence of end-of-year values #' set_day(x, "last") #' #' # Daily sequences are not allowed. Use a naive-time for this instead. #' try(seq(year_day(2019, 1), by = 2, length.out = 2)) #' as_year_day(seq(as_naive_time(year_day(2019, 1)), by = 2, length.out = 2)) seq.clock_year_day <- function(from, to = NULL, by = NULL, length.out = NULL, along.with = NULL, ...) { precision <- calendar_precision_attribute(from) if (precision > PRECISION_YEAR) { abort("`from` must be 'year' precision.") } seq_impl( from = from, to = to, by = by, length.out = length.out, along.with = along.with, precision = precision, ... ) } # ------------------------------------------------------------------------------ #' @export clock_minimum.clock_year_day <- function(x) { switch( calendar_precision_attribute(x) + 1L, clock_minimum_year_day_year, abort("Invalid precision", .internal = TRUE), abort("Invalid precision", .internal = TRUE), abort("Invalid precision", .internal = TRUE), clock_minimum_year_day_day, clock_minimum_year_day_hour, clock_minimum_year_day_minute, clock_minimum_year_day_second, clock_minimum_year_day_millisecond, clock_minimum_year_day_microsecond, clock_minimum_year_day_nanosecond, abort("Invalid precision", .internal = TRUE) ) } #' @export clock_maximum.clock_year_day <- function(x) { switch( calendar_precision_attribute(x) + 1L, clock_maximum_year_day_year, abort("Invalid precision", .internal = TRUE), abort("Invalid precision", .internal = TRUE), abort("Invalid precision", .internal = TRUE), clock_maximum_year_day_day, clock_maximum_year_day_hour, clock_maximum_year_day_minute, clock_maximum_year_day_second, clock_maximum_year_day_millisecond, clock_maximum_year_day_microsecond, clock_maximum_year_day_nanosecond, abort("Invalid precision", .internal = TRUE) ) } year_day_minimum <- function(precision) { calendar_minimum(precision, year_day(clock_calendar_year_minimum)) } year_day_maximum <- function(precision) { calendar_maximum(precision, year_day(clock_calendar_year_maximum)) } # ------------------------------------------------------------------------------ clock_init_year_day_utils <- function(env) { year <- year_day(integer()) assign("clock_empty_year_day_year", year, envir = env) assign("clock_empty_year_day_day", calendar_widen(year, "day"), envir = env) assign("clock_empty_year_day_hour", calendar_widen(year, "hour"), envir = env) assign("clock_empty_year_day_minute", calendar_widen(year, "minute"), envir = env) assign("clock_empty_year_day_second", calendar_widen(year, "second"), envir = env) assign("clock_empty_year_day_millisecond", calendar_widen(year, "millisecond"), envir = env) assign("clock_empty_year_day_microsecond", calendar_widen(year, "microsecond"), envir = env) assign("clock_empty_year_day_nanosecond", calendar_widen(year, "nanosecond"), envir = env) assign("clock_minimum_year_day_year", year_day_minimum("year"), envir = env) assign("clock_minimum_year_day_day", year_day_minimum("day"), envir = env) assign("clock_minimum_year_day_hour", year_day_minimum("hour"), envir = env) assign("clock_minimum_year_day_minute", year_day_minimum("minute"), envir = env) assign("clock_minimum_year_day_second", year_day_minimum("second"), envir = env) assign("clock_minimum_year_day_millisecond", year_day_minimum("millisecond"), envir = env) assign("clock_minimum_year_day_microsecond", year_day_minimum("microsecond"), envir = env) assign("clock_minimum_year_day_nanosecond", year_day_minimum("nanosecond"), envir = env) assign("clock_maximum_year_day_year", year_day_maximum("year"), envir = env) assign("clock_maximum_year_day_day", year_day_maximum("day"), envir = env) assign("clock_maximum_year_day_hour", year_day_maximum("hour"), envir = env) assign("clock_maximum_year_day_minute", year_day_maximum("minute"), envir = env) assign("clock_maximum_year_day_second", year_day_maximum("second"), envir = env) assign("clock_maximum_year_day_millisecond", year_day_maximum("millisecond"), envir = env) assign("clock_maximum_year_day_microsecond", year_day_maximum("microsecond"), envir = env) assign("clock_maximum_year_day_nanosecond", year_day_maximum("nanosecond"), envir = env) invisible(NULL) } clock/R/duration.R0000644000176200001440000010636214427270231013530 0ustar liggesusers#' Construct a duration #' #' @description #' These helpers construct durations of the specified precision. Durations #' represent units of time. #' #' Durations are separated into two categories: #' #' **Calendrical** #' #' - year #' - quarter #' - month #' #' **Chronological** #' #' - week #' - day #' - hour #' - minute #' - second #' - millisecond #' - microsecond #' - nanosecond #' #' Calendrical durations are generally used when manipulating calendar types, #' like year-month-day. Chronological durations are generally used when #' working with time points, like sys-time or naive-time. #' #' @section Internal Representation: #' #' Durations are internally represented as an integer number of "ticks" along #' with a ratio describing how it converts to a number of seconds. The #' following duration ratios are used in clock: #' #' - `1 year == 31556952 seconds` #' #' - `1 quarter == 7889238 seconds` #' #' - `1 month == 2629746 seconds` #' #' - `1 week == 604800 seconds` #' #' - `1 day == 86400 seconds` #' #' - `1 hour == 3600 seconds` #' #' - `1 minute == 60 seconds` #' #' - `1 second == 1 second` #' #' - `1 millisecond == 1 / 1000 seconds` #' #' - `1 microsecond == 1 / 1000000 seconds` #' #' - `1 nanosecond == 1 / 1000000000 seconds` #' #' A duration of 1 year is defined to correspond to the #' average length of a proleptic Gregorian year, i.e. 365.2425 days. #' #' A duration of 1 month is defined as exactly 1/12 of a year. #' #' A duration of 1 quarter is defined as exactly 1/4 of a year. #' #' A duration of 1 week is defined as exactly 7 days. #' #' These conversions come into play when doing operations like adding or #' flooring durations. Generally, you add two calendrical durations together #' to get a new calendrical duration, rather than adding a calendrical and #' a chronological duration together. The one exception is [duration_cast()], #' which can cast durations to any other precision, with a potential loss of #' information. #' #' @param n `[integer]` #' #' The number of units of time to use when creating the duration. #' #' @return A duration of the specified precision. #' #' @name duration-helper #' #' @examples #' duration_years(1:5) #' duration_nanoseconds(1:5) NULL #' @rdname duration-helper #' @export duration_years <- function(n = integer()) { duration_helper(n, PRECISION_YEAR) } #' @rdname duration-helper #' @export duration_quarters <- function(n = integer()) { duration_helper(n, PRECISION_QUARTER) } #' @rdname duration-helper #' @export duration_months <- function(n = integer()) { duration_helper(n, PRECISION_MONTH) } #' @rdname duration-helper #' @export duration_weeks <- function(n = integer()) { duration_helper(n, PRECISION_WEEK) } #' @rdname duration-helper #' @export duration_days <- function(n = integer()) { duration_helper(n, PRECISION_DAY) } #' @rdname duration-helper #' @export duration_hours <- function(n = integer()) { duration_helper(n, PRECISION_HOUR) } #' @rdname duration-helper #' @export duration_minutes <- function(n = integer()) { duration_helper(n, PRECISION_MINUTE) } #' @rdname duration-helper #' @export duration_seconds <- function(n = integer()) { duration_helper(n, PRECISION_SECOND) } #' @rdname duration-helper #' @export duration_milliseconds <- function(n = integer()) { duration_helper(n, PRECISION_MILLISECOND) } #' @rdname duration-helper #' @export duration_microseconds <- function(n = integer()) { duration_helper(n, PRECISION_MICROSECOND) } #' @rdname duration-helper #' @export duration_nanoseconds <- function(n = integer()) { duration_helper(n, PRECISION_NANOSECOND) } duration_helper <- function(n, precision, ..., retain_names = FALSE, error_arg = caller_arg(n), error_call = caller_env()) { check_dots_empty0(...) # Generally don't retain names for helpers like `duration_years()`, # but might need to during arithmetic if (retain_names) { names <- names(n) } else { names <- NULL } n <- vec_cast(n, integer(), x_arg = error_arg, call = error_call) fields <- duration_helper_cpp(n, precision) new_duration_from_fields(fields, precision, names) } # ------------------------------------------------------------------------------ #' @export format.clock_duration <- function(x, ...) { out <- format_duration_cpp(x, duration_precision_attribute(x)) names(out) <- names(x) out } #' @export obj_print_data.clock_duration <- function(x, ...) { if (vec_size(x) == 0L) { return(invisible(x)) } out <- format(x) print(out, quote = FALSE, na.print = "NA") invisible(x) } #' @export vec_ptype_full.clock_duration <- function(x, ...) { precision <- duration_precision_attribute(x) precision <- precision_to_string(precision) paste0("duration<", precision, ">") } #' @export vec_ptype_abbr.clock_duration <- function(x, ...) { precision <- duration_precision_attribute(x) precision <- precision_to_string(precision) precision <- precision_abbr(precision) paste0("dur<", precision, ">") } # ------------------------------------------------------------------------------ #' @export vec_proxy.clock_duration <- function(x, ...) { .Call(`_clock_clock_rcrd_proxy`, x) } #' @export vec_restore.clock_duration <- function(x, to, ...) { .Call(`_clock_duration_restore`, x, to) } # ------------------------------------------------------------------------------ #' @export vec_ptype2.clock_duration.clock_duration <- function(x, y, ...) { x_precision <- duration_precision_attribute(x) y_precision <- duration_precision_attribute(y) if (x_precision == y_precision) { return(x) } precision <- duration_precision_common_cpp(x_precision, y_precision) if (is.na(precision)) { stop_incompatible_type( x = x, y = y, ..., details = "Can't combine calendrical durations with chronological durations." ) } if (precision == x_precision) { x } else { y } } #' @export vec_cast.clock_duration.clock_duration <- function(x, to, ...) { x_precision <- duration_precision_attribute(x) to_precision <- duration_precision_attribute(to) if (x_precision == to_precision) { return(x) } precision <- duration_precision_common_cpp(x_precision, to_precision) if (is.na(precision)) { stop_incompatible_cast( x = x, to = to, ..., details = "Can't cast between calendrical durations and chronological durations." ) } if (x_precision > to_precision) { stop_incompatible_cast( x = x, to = to, ..., details = "Can't cast to a less precise precision." ) } fields <- duration_cast_cpp(x, x_precision, to_precision) new_duration_from_fields(fields, to_precision, clock_rcrd_names(x)) } # ------------------------------------------------------------------------------ #' Convert to a duration #' #' @description #' You generally convert to a duration from either a sys-time or a naive-time. #' The precision of the input is retained in the returned duration. #' #' To round an existing duration to another precision, see [duration_floor()]. #' #' @inheritParams rlang::args_dots_empty #' #' @param x `[object]` #' #' An object to convert to a duration. #' #' @return A duration with the same precision as `x`. #' #' @export #' @examples #' x <- as_sys_time(year_month_day(2019, 01, 01)) #' #' # The number of days since 1970-01-01 UTC #' as_duration(x) #' #' x <- x + duration_seconds(1) #' x #' #' # The number of seconds since 1970-01-01 00:00:00 UTC #' as_duration(x) as_duration <- function(x, ...) { UseMethod("as_duration") } #' @export as_duration.clock_duration <- function(x, ...) { check_dots_empty0(...) x } # ------------------------------------------------------------------------------ #' @export as_sys_time.clock_duration <- function(x, ...) { check_dots_empty0(...) names <- clock_rcrd_names(x) # Promote to at least day precision for sys-time ptype <- vec_ptype2(x, duration_days(), y_arg = "") x <- vec_cast(x, ptype) precision <- duration_precision_attribute(x) new_sys_time_from_fields(x, precision, names) } #' @export as_naive_time.clock_duration <- function(x, ...) { check_dots_empty0(...) as_naive_time(as_sys_time(x)) } #' @export as.character.clock_duration <- function(x, ...) { format(x) } #' @export as.integer.clock_duration <- function(x, ...) { out <- duration_as_integer_cpp(x, duration_precision_attribute(x)) names(out) <- names(x) out } #' @export as.double.clock_duration <- function(x, ...) { out <- duration_as_double_cpp(x, duration_precision_attribute(x)) names(out) <- names(x) out } # ------------------------------------------------------------------------------ #' Cast a duration between precisions #' #' @description #' Casting is one way to change a duration's precision. #' #' Casting to a less precise precision will completely drop information that #' is more precise than the precision that you are casting to. It does so #' in a way that makes it round towards zero. #' #' Casting to a more precise precision is done through a multiplication by #' a conversion factor between the current precision and the new precision. #' #' @details #' When you want to change to a less precise precision, you often want #' [duration_floor()] instead of `duration_cast()`, as that rounds towards #' negative infinity, which is generally the desired behavior when working with #' time points (especially ones pre-1970, which are stored as negative #' durations). #' #' @param x `[clock_duration]` #' #' A duration. #' #' @param precision `[character(1)]` #' #' A precision. One of: #' #' - `"year"` #' #' - `"quarter"` #' #' - `"month"` #' #' - `"week"` #' #' - `"day"` #' #' - `"hour"` #' #' - `"minute"` #' #' - `"second"` #' #' - `"millisecond"` #' #' - `"microsecond"` #' #' - `"nanosecond"` #' #' @return `x` cast to the new `precision`. #' #' @export #' @examples #' x <- duration_seconds(c(86401, -86401)) #' #' # Casting rounds towards 0 #' cast <- duration_cast(x, "day") #' cast #' #' # Flooring rounds towards negative infinity #' floor <- duration_floor(x, "day") #' floor #' #' # Flooring is generally more useful when working with time points, #' # note that the cast ends up rounding the pre-1970 date up to the next #' # day, while the post-1970 date is rounded down. #' as_sys_time(x) #' as_sys_time(cast) #' as_sys_time(floor) #' #' # Casting to a more precise precision #' duration_cast(x, "millisecond") duration_cast <- function(x, precision) { check_duration(x) x_precision <- duration_precision_attribute(x) check_precision(precision) precision <- precision_to_integer(precision) fields <- duration_cast_cpp(x, x_precision, precision) new_duration_from_fields(fields, precision, clock_rcrd_names(x)) } # ------------------------------------------------------------------------------ #' Duration rounding #' #' @description #' - `duration_floor()` rounds a duration down to a multiple of the specified #' `precision`. #' #' - `duration_ceiling()` rounds a duration up to a multiple of the specified #' `precision`. #' #' - `duration_round()` rounds up or down depending on what is closer, #' rounding up on ties. #' #' @details #' You can floor calendrical durations to other calendrical durations, and #' chronological durations to other chronological durations, but you can't #' floor a chronological duration to a calendrical duration (such as flooring #' from day to month). For more information, see the documentation on the #' [duration helper][duration-helper] page. #' #' @inheritParams rlang::args_dots_empty #' @inheritParams duration_cast #' #' @param n `[positive integer(1)]` #' #' A positive integer specifying the multiple of `precision` to use. #' #' @return `x` rounded to the `precision`. #' #' @name duration-rounding #' #' @examples #' x <- duration_seconds(c(86399, 86401)) #' #' duration_floor(x, "day") #' duration_ceiling(x, "day") #' #' # Can't floor from a chronological duration (seconds) #' # to a calendrical duration (months) #' try(duration_floor(x, "month")) #' #' # Every 2 days, using an origin of day 0 #' y <- duration_seconds(c(0, 86400, 86400 * 2, 86400 * 3)) #' duration_floor(y, "day", n = 2) #' #' # Shifting the origin to be day 1 #' origin <- duration_days(1) #' duration_floor(y - origin, "day", n = 2) + origin #' #' # Rounding will round ties up #' half_day <- 86400 / 2 #' half_day_durations <- duration_seconds(c(half_day - 1, half_day, half_day + 1)) #' duration_round(half_day_durations, "day") #' #' # With larger units #' x <- duration_months(c(0, 15, 24)) #' duration_floor(x, "year") #' duration_floor(x, "quarter") NULL #' @rdname duration-rounding #' @export duration_floor <- function(x, precision, ..., n = 1L) { duration_rounder(x, precision, n, duration_floor_cpp, "floor", ...) } #' @rdname duration-rounding #' @export duration_ceiling <- function(x, precision, ..., n = 1L) { duration_rounder(x, precision, n, duration_ceiling_cpp, "ceiling", ...) } #' @rdname duration-rounding #' @export duration_round <- function(x, precision, ..., n = 1L) { duration_rounder(x, precision, n, duration_round_cpp, "round", ...) } duration_rounder <- function(x, precision, n, rounder, verb, ..., error_arg = caller_arg(x), error_call = caller_env()) { check_dots_empty0(...) check_duration(x, arg = error_arg, call = error_call) check_number_whole(n, min = 0, call = error_call) n <- vec_cast(n, integer(), call = error_call) check_precision(precision, call = error_call) precision <- precision_to_integer(precision) x_precision <- duration_precision_attribute(x) if (x_precision < precision) { cli::cli_abort("Can't {verb} to a more precise precision.", call = error_call) } if (!duration_has_common_precision_cpp(x_precision, precision)) { precision <- precision_to_string(precision) x_precision <- precision_to_string(x_precision) message <- paste0( "Can't {verb} from ", "a chronological precision ({x_precision}) to ", "a calendrical precision ({precision})." ) cli::cli_abort(message, call = error_call) } fields <- rounder(x, x_precision, precision, n) new_duration_from_fields(fields, precision, clock_rcrd_names(x)) } # ------------------------------------------------------------------------------ #' Sequences: duration #' #' @description #' This is a duration method for the [seq()] generic. #' #' Using `seq()` on duration objects always retains the type of `from`. #' #' When calling `seq()`, exactly two of the following must be specified: #' - `to` #' - `by` #' - Either `length.out` or `along.with` #' #' @details #' If `from > to` and `by > 0`, then the result will be length 0. This matches #' the behavior of [rlang::seq2()], and results in nicer theoretical #' properties when compared with throwing an error. Similarly, if `from < to` #' and `by < 0`, then the result will also be length 0. #' #' @inheritParams rlang::args_dots_empty #' #' @param from `[clock_duration(1)]` #' #' A duration to start the sequence from. #' #' @param to `[clock_duration(1) / NULL]` #' #' A duration to stop the sequence at. #' #' `to` is cast to the type of `from`. #' #' `to` is only included in the result if the resulting sequence divides #' the distance between `from` and `to` exactly. #' #' @param by `[integer(1) / clock_duration(1) / NULL]` #' #' The unit to increment the sequence by. #' #' If `by` is an integer, it is transformed into a duration with the #' precision of `from`. #' #' If `by` is a duration, it is cast to the type of `from`. #' #' @param length.out `[positive integer(1) / NULL]` #' #' The length of the resulting sequence. #' #' If specified, `along.with` must be `NULL`. #' #' @param along.with `[vector / NULL]` #' #' A vector who's length determines the length of the resulting sequence. #' #' Equivalent to `length.out = vec_size(along.with)`. #' #' If specified, `length.out` must be `NULL`. #' #' @return A sequence with the type of `from`. #' #' @export #' @examples #' seq(duration_days(0), duration_days(100), by = 5) #' #' # Using a duration `by`. Note that `by` is cast to the type of `from`. #' seq(duration_days(0), duration_days(100), by = duration_weeks(1)) #' #' # `to` is cast from 5 years to 60 months #' # `by` is cast from 1 quarter to 4 months #' seq(duration_months(0), duration_years(5), by = duration_quarters(1)) #' #' seq(duration_days(20), by = 2, length.out = 5) seq.clock_duration <- function(from, to = NULL, by = NULL, length.out = NULL, along.with = NULL, ...) { check_dots_empty0(...) vec_check_size(from, 1L) check_no_missing(from) has_to <- !is_null(to) has_by <- !is_null(by) has_lo <- !is_null(length.out) has_aw <- !is_null(along.with) if (has_aw) { if (has_lo) { abort("Can only specify one of `length.out` and `along.with`.") } else { has_lo <- TRUE length.out <- vec_size(along.with) } } n_has <- sum(has_to, has_by, has_lo) if (n_has != 2L) { message <- paste0( "Must specify exactly two of:\n", "- `to`\n", "- `by`\n", "- Either `length.out` or `along.with`" ) abort(message) } if (has_to) { to <- vec_cast(to, from, x_arg = "to", to_arg = "from") vec_check_size(to, 1L) check_no_missing(to) } if (has_by) { precision <- duration_precision_attribute(from) by <- duration_collect_by(by, precision) vec_check_size(by, size = 1L) check_no_missing(by) if (by == duration_helper(0L, precision)) { abort("`by` can't be `0`.") } } if (has_lo) { length.out <- check_length_out(length.out) } if (has_to) { if (has_by) { duration_seq_to_by(from, to, by) } else { duration_seq_to_lo(from, to, length.out) } } else { duration_seq_by_lo(from, by, length.out) } } duration_seq_to_by <- function(from, to, by) { names <- NULL precision <- duration_precision_attribute(from) fields <- duration_seq_to_by_cpp(from, precision, to, by) new_duration_from_fields(fields, precision, names) } duration_seq_to_lo <- function(from, to, length.out) { names <- NULL precision <- duration_precision_attribute(from) fields <- duration_seq_to_lo_cpp(from, precision, to, length.out) new_duration_from_fields(fields, precision, names) } duration_seq_by_lo <- function(from, by, length.out) { names <- NULL precision <- duration_precision_attribute(from) fields <- duration_seq_by_lo_cpp(from, precision, by, length.out) new_duration_from_fields(fields, precision, names) } duration_collect_by <- function(by, precision, ..., error_arg = caller_arg(by), error_call = caller_env()) { check_dots_empty0(...) if (is_duration(by)) { to <- duration_helper(integer(), precision) vec_cast(by, to, x_arg = error_arg, call = error_call) } else { duration_helper(by, precision, error_arg = error_arg, error_call = error_call) } } check_length_out <- function(x, ..., arg = caller_arg(x), call = caller_env()) { check_dots_empty0(...) out <- vec_cast(x, integer(), x_arg = arg, call = call) vec_check_size(out, 1L, arg = arg, call = call) check_no_missing(out, arg = arg, call = call) if (out < 0) { cli::cli_abort("{.arg {arg}} can't be negative.", call = call) } out } # Used by calendar methods that eventually call the duration seq() method seq_impl <- function(from, to, by, length.out, along.with, precision, ...) { if (!is_null(to)) { to <- vec_cast(to, from, x_arg = "to", to_arg = "from") to <- to - from } start <- from from <- duration_helper(0L, precision) steps <- seq( from = from, to = to, by = by, length.out = length.out, along.with = along.with, ... ) start + steps } # ------------------------------------------------------------------------------ #' Spanning sequence: duration #' #' @description #' `duration_spanning_seq()` generates a regular sequence along the span of `x`, #' i.e. along `[min(x), max(x)]`. #' #' @details #' Missing values are automatically removed before the sequence is generated. #' #' If you need more precise sequence generation, call [range()] and [seq()] #' directly. #' #' @param x `[clock_duration]` #' #' A duration vector. #' #' @return A sequence along `[min(x), max(x)]`. #' #' @export #' @examples #' x <- duration_days(c(1, 5, 2)) #' duration_spanning_seq(x) #' #' # Missing values are removed before the sequence is created #' x <- vctrs::vec_c(NA, x, NA) #' duration_spanning_seq(x) duration_spanning_seq <- function(x) { check_duration(x) spanning_seq_impl(x) } spanning_seq_impl <- function(x) { range <- range(x, na.rm = TRUE) from <- range[[1L]] to <- range[[2L]] seq(from = from, to = to, by = 1L) } # ------------------------------------------------------------------------------ #' @export vec_math.clock_duration <- function(.fn, .x, ...) { switch( .fn, abs = duration_abs(.x), sign = duration_sign(.x), # Pass on to `vec_math.clock_rcrd()` NextMethod() ) } duration_abs <- function(x) { precision <- duration_precision_attribute(x) fields <- duration_abs_cpp(x, precision) new_duration_from_fields(fields, precision, clock_rcrd_names(x)) } duration_sign <- function(x) { out <- duration_sign_cpp(x, duration_precision_attribute(x)) names(out) <- names(x) out } # ------------------------------------------------------------------------------ #' @export #' @method vec_arith clock_duration vec_arith.clock_duration <- function(op, x, y, ...) { UseMethod("vec_arith.clock_duration", y) } #' @export #' @method vec_arith.clock_duration MISSING vec_arith.clock_duration.MISSING <- function(op, x, y, ...) { switch( op, "+" = x, "-" = duration_unary_minus(x), ) } #' @export #' @method vec_arith.clock_duration clock_duration vec_arith.clock_duration.clock_duration <- function(op, x, y, ...) { switch( op, "+" = duration_plus(x, y, names_common(x, y)), "-" = duration_minus(x, y, names_common(x, y)), "/" = stop_incompatible_op(op, x, y, details = "Durations only support integer division. Did you want `%/%`?"), "%%" = duration_modulus(x, y, names_common(x, y)), "%/%" = duration_integer_divide(x, y, names_common(x, y)), stop_incompatible_op(op, x, y) ) } #' @export #' @method vec_arith.clock_duration numeric vec_arith.clock_duration.numeric <- function(op, x, y, ...) { switch( op, "*" = duration_scalar_multiply(x, y, names_common(x, y)), "/" = stop_incompatible_op(op, x, y, details = "Durations only support integer division. Did you want `%/%`?"), "%%" = duration_scalar_modulus(x, y, names_common(x, y)), "%/%" = duration_scalar_divide(x, y, names_common(x, y)), stop_incompatible_op(op, x, y) ) } #' @export #' @method vec_arith.numeric clock_duration vec_arith.numeric.clock_duration <- function(op, x, y, ...) { switch( op, "*" = duration_scalar_multiply(y, x, names_common(x, y)), "/" = stop_incompatible_op(op, x, y, details = "Durations only support integer division. Did you want `%/%`?"), "%/%" = stop_incompatible_op(op, x, y, details = "Cannot divide a numeric by a duration."), stop_incompatible_op(op, x, y) ) } # ------------------------------------------------------------------------------ #' Arithmetic: duration #' #' @description #' These are duration methods for the #' [arithmetic generics][clock-arithmetic]. #' #' - `add_years()` #' #' - `add_quarters()` #' #' - `add_months()` #' #' - `add_weeks()` #' #' - `add_days()` #' #' - `add_hours()` #' #' - `add_minutes()` #' #' - `add_seconds()` #' #' - `add_milliseconds()` #' #' - `add_microseconds()` #' #' - `add_nanoseconds()` #' #' When adding to a duration using one of these functions, a second duration #' is created based on the function name and `n`. The two durations are then #' added together, and the precision of the result is determined as the #' _more precise precision_ of the two durations. #' #' @details #' You can add calendrical durations to other calendrical durations, #' and chronological durations to other chronological durations, but you can't #' add a chronological duration to a calendrical duration #' (such as adding days and months). For more information, see the #' documentation on the [duration helper][duration-helper] page. #' #' `x` and `n` are recycled against each other using #' [tidyverse recycling rules][vctrs::vector_recycling_rules]. #' #' @inheritParams clock-arithmetic #' #' @param x `[clock_duration]` #' #' A duration vector. #' #' @return `x` after performing the arithmetic, possibly with a more precise #' precision. #' #' @name duration-arithmetic #' #' @examples #' x <- duration_seconds(5) #' #' # Addition in the same precision #' add_seconds(x, 1:10) #' #' # Addition with days, defined as 86400 seconds #' add_days(x, 1) #' #' # Similarly, if you start with days and add seconds, you get the common #' # precision of the two back, which is seconds #' y <- duration_days(1) #' add_seconds(y, 5) #' #' # But you can't add a chronological duration (days) and #' # a calendrical duration (months) #' try(add_months(y, 1)) #' #' # You can add years to a duration of months, which adds #' # an additional 12 months / year #' z <- duration_months(5) #' add_years(z, 1) NULL #' @rdname duration-arithmetic #' @export add_years.clock_duration <- function(x, n, ...) { n <- duration_collect_n(n, PRECISION_YEAR) duration_plus(x, n, names_common(x, n)) } #' @rdname duration-arithmetic #' @export add_quarters.clock_duration <- function(x, n, ...) { n <- duration_collect_n(n, PRECISION_QUARTER) duration_plus(x, n, names_common(x, n)) } #' @rdname duration-arithmetic #' @export add_months.clock_duration <- function(x, n, ...) { n <- duration_collect_n(n, PRECISION_MONTH) duration_plus(x, n, names_common(x, n)) } #' @rdname duration-arithmetic #' @export add_weeks.clock_duration <- function(x, n, ...) { n <- duration_collect_n(n, PRECISION_WEEK) duration_plus(x, n, names_common(x, n)) } #' @rdname duration-arithmetic #' @export add_days.clock_duration <- function(x, n, ...) { n <- duration_collect_n(n, PRECISION_DAY) duration_plus(x, n, names_common(x, n)) } #' @rdname duration-arithmetic #' @export add_hours.clock_duration <- function(x, n, ...) { n <- duration_collect_n(n, PRECISION_HOUR) duration_plus(x, n, names_common(x, n)) } #' @rdname duration-arithmetic #' @export add_minutes.clock_duration <- function(x, n, ...) { n <- duration_collect_n(n, PRECISION_MINUTE) duration_plus(x, n, names_common(x, n)) } #' @rdname duration-arithmetic #' @export add_seconds.clock_duration <- function(x, n, ...) { n <- duration_collect_n(n, PRECISION_SECOND) duration_plus(x, n, names_common(x, n)) } #' @rdname duration-arithmetic #' @export add_milliseconds.clock_duration <- function(x, n, ...) { n <- duration_collect_n(n, PRECISION_MILLISECOND) duration_plus(x, n, names_common(x, n)) } #' @rdname duration-arithmetic #' @export add_microseconds.clock_duration <- function(x, n, ...) { n <- duration_collect_n(n, PRECISION_MICROSECOND) duration_plus(x, n, names_common(x, n)) } #' @rdname duration-arithmetic #' @export add_nanoseconds.clock_duration <- function(x, n, ...) { n <- duration_collect_n(n, PRECISION_NANOSECOND) duration_plus(x, n, names_common(x, n)) } duration_collect_n <- function(n, precision, ..., error_arg = caller_arg(n), error_call = caller_env()) { check_dots_empty0(...) if (is_duration_with_precision(n, precision)) { return(n) } if (is_duration(n)) { precision <- precision_to_string(precision) cli::cli_abort( "{.arg {error_arg}} must have {.str {precision}} precision.", call = error_call ) } duration_helper( n = n, precision = precision, retain_names = TRUE, error_arg = error_arg, error_call = error_call ) } # ------------------------------------------------------------------------------ duration_plus <- function(x, y, names) { duration_arith(x, y, names, duration_plus_cpp) } duration_minus <- function(x, y, names) { duration_arith(x, y, names, duration_minus_cpp) } duration_modulus <- function(x, y, names) { duration_arith(x, y, names, duration_modulus_cpp) } duration_arith <- function(x, y, names, fn) { check_duration(x) check_duration(y) args <- vec_cast_common(x = x, y = y) args <- vec_recycle_common(!!!args, names = names) x <- args$x y <- args$y names <- args$names precision <- duration_precision_attribute(x) fields <- fn(x, y, precision) new_duration_from_fields(fields, precision, names) } duration_integer_divide <- function(x, y, names) { duration_arith_integer(x, y, names, duration_integer_divide_cpp) } duration_arith_integer <- function(x, y, names, fn) { check_duration(x) check_duration(y) args <- vec_cast_common(x = x, y = y) args <- vec_recycle_common(!!!args, names = names) x <- args$x y <- args$y names <- args$names precision <- duration_precision_attribute(x) out <- fn(x, y, precision) names(out) <- names out } duration_scalar_multiply <- function(x, y, names) { duration_scalar_arith(x, y, names, duration_scalar_multiply_cpp) } duration_scalar_modulus <- function(x, y, names) { duration_scalar_arith(x, y, names, duration_scalar_modulus_cpp) } duration_scalar_divide <- function(x, y, names) { duration_scalar_arith(x, y, names, duration_scalar_divide_cpp) } duration_scalar_arith <- function(x, y, names, fn) { check_duration(x) precision <- duration_precision_attribute(x) y <- vec_cast(y, integer(), x_arg = "y") args <- vec_recycle_common(x = x, y = y, names = names) x <- args$x y <- args$y names <- args$names fields <- fn(x, y, precision) new_duration_from_fields(fields, precision, names) } names_common <- function(x, y) { names <- names(x) if (is_null(names)) { names <- names(y) } names } duration_unary_minus <- function(x) { precision <- duration_precision_attribute(x) fields <- duration_unary_minus_cpp(x, precision) new_duration_from_fields(fields, precision, names(x)) } # ------------------------------------------------------------------------------ #' Is `x` a duration? #' #' This function determines if the input is a duration object. #' #' @param x `[object]` #' #' An object. #' #' @return `TRUE` if `x` inherits from `"clock_duration"`, otherwise `FALSE`. #' #' @export #' @examples #' is_duration(1) #' is_duration(duration_days(1)) is_duration <- function(x) { inherits(x, "clock_duration") } is_duration_with_precision <- function(x, precision) { is_duration(x) && duration_precision_attribute(x) == precision } check_duration <- function(x, ..., arg = caller_arg(x), call = caller_env()) { check_inherits(x, what = "clock_duration", arg = arg, call = call) } # ------------------------------------------------------------------------------ #' Precision: duration #' #' `duration_precision()` extracts the precision from a duration object. It #' returns the precision as a single string. #' #' @param x `[clock_duration]` #' #' A duration. #' #' @return A single string holding the precision of the duration. #' #' @export #' @examples #' duration_precision(duration_seconds(1)) #' duration_precision(duration_nanoseconds(2)) #' duration_precision(duration_quarters(1:5)) duration_precision <- function(x) { check_duration(x) precision <- duration_precision_attribute(x) precision <- precision_to_string(precision) precision } # ------------------------------------------------------------------------------ #' @export clock_minimum.clock_duration <- function(x) { switch( duration_precision_attribute(x) + 1L, clock_minimum_duration_year, clock_minimum_duration_quarter, clock_minimum_duration_month, clock_minimum_duration_week, clock_minimum_duration_day, clock_minimum_duration_hour, clock_minimum_duration_minute, clock_minimum_duration_second, clock_minimum_duration_millisecond, clock_minimum_duration_microsecond, clock_minimum_duration_nanosecond, abort("Invalid precision", .internal = TRUE) ) } #' @export clock_maximum.clock_duration <- function(x) { switch( duration_precision_attribute(x) + 1L, clock_maximum_duration_year, clock_maximum_duration_quarter, clock_maximum_duration_month, clock_maximum_duration_week, clock_maximum_duration_day, clock_maximum_duration_hour, clock_maximum_duration_minute, clock_maximum_duration_second, clock_maximum_duration_millisecond, clock_maximum_duration_microsecond, clock_maximum_duration_nanosecond, abort("Invalid precision", .internal = TRUE) ) } duration_minimum <- function(precision) { names <- NULL precision <- precision_to_integer(precision) fields <- duration_minimum_cpp(precision) new_duration_from_fields(fields, precision, names) } duration_maximum <- function(precision) { names <- NULL precision <- precision_to_integer(precision) fields <- duration_maximum_cpp(precision) new_duration_from_fields(fields, precision, names) } # ------------------------------------------------------------------------------ duration_precision_attribute <- function(x) { attr(x, "precision", exact = TRUE) } # ------------------------------------------------------------------------------ clock_init_duration_utils <- function(env) { assign("clock_minimum_duration_year", duration_minimum("year"), envir = env) assign("clock_minimum_duration_quarter", duration_minimum("quarter"), envir = env) assign("clock_minimum_duration_month", duration_minimum("month"), envir = env) assign("clock_minimum_duration_week", duration_minimum("week"), envir = env) assign("clock_minimum_duration_day", duration_minimum("day"), envir = env) assign("clock_minimum_duration_hour", duration_minimum("hour"), envir = env) assign("clock_minimum_duration_minute", duration_minimum("minute"), envir = env) assign("clock_minimum_duration_second", duration_minimum("second"), envir = env) assign("clock_minimum_duration_millisecond", duration_minimum("millisecond"), envir = env) assign("clock_minimum_duration_microsecond", duration_minimum("microsecond"), envir = env) assign("clock_minimum_duration_nanosecond", duration_minimum("nanosecond"), envir = env) assign("clock_maximum_duration_year", duration_maximum("year"), envir = env) assign("clock_maximum_duration_quarter", duration_maximum("quarter"), envir = env) assign("clock_maximum_duration_month", duration_maximum("month"), envir = env) assign("clock_maximum_duration_week", duration_maximum("week"), envir = env) assign("clock_maximum_duration_day", duration_maximum("day"), envir = env) assign("clock_maximum_duration_hour", duration_maximum("hour"), envir = env) assign("clock_maximum_duration_minute", duration_maximum("minute"), envir = env) assign("clock_maximum_duration_second", duration_maximum("second"), envir = env) assign("clock_maximum_duration_millisecond", duration_maximum("millisecond"), envir = env) assign("clock_maximum_duration_microsecond", duration_maximum("microsecond"), envir = env) assign("clock_maximum_duration_nanosecond", duration_maximum("nanosecond"), envir = env) invisible(NULL) } clock/R/precision.R0000644000176200001440000000401414423730227013667 0ustar liggesusersPRECISION_YEAR = 0L PRECISION_QUARTER = 1L PRECISION_MONTH = 2L PRECISION_WEEK = 3L PRECISION_DAY = 4L PRECISION_HOUR = 5L PRECISION_MINUTE = 6L PRECISION_SECOND = 7L PRECISION_MILLISECOND = 8L PRECISION_MICROSECOND = 9L PRECISION_NANOSECOND = 10L # ------------------------------------------------------------------------------ check_precision <- function(x, ..., values = NULL, arg = caller_arg(x), call = caller_env()) { values <- values %||% precision_names() check_string(x, allow_empty = FALSE, arg = arg, call = call) arg_match0(x, values = values, arg_nm = arg, error_call = call) } check_precision_subsecond <- function(x, ..., arg = caller_arg(x), call = caller_env()) { check_precision( x = x, values = c("millisecond", "microsecond", "nanosecond"), arg = arg, call = call ) } precision_to_integer <- function(x) { check_string(x, .internal = TRUE) switch( x, year = PRECISION_YEAR, quarter = PRECISION_QUARTER, month = PRECISION_MONTH, week = PRECISION_WEEK, day = PRECISION_DAY, hour = PRECISION_HOUR, minute = PRECISION_MINUTE, second = PRECISION_SECOND, millisecond = PRECISION_MILLISECOND, microsecond = PRECISION_MICROSECOND, nanosecond = PRECISION_NANOSECOND, abort("`x` not recognized.", .internal = TRUE) ) } precision_abbr <- function(precision_string) { switch( precision_string, millisecond = "milli", microsecond = "micro", nanosecond = "nano", precision_string # fallthrough ) } precision_common2 <- function(x, y) { if (x >= y) { x } else { y } } precision_names <- function() { c( "year", "quarter", "month", "week", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond" ) } precision_time_names <- function() { c( "hour", "minute", "second", "millisecond", "microsecond", "nanosecond" ) } clock/R/quarterly-year-quarter-day.R0000644000176200001440000011553314427270231017125 0ustar liggesusers#' Calendar: year-quarter-day #' #' `year_quarter_day()` constructs a calendar from the fiscal year, fiscal #' quarter, and day of the quarter, along with a value determining which #' month the fiscal year `start`s in. #' #' @details #' Fields are recycled against each other using #' [tidyverse recycling rules][vctrs::vector_recycling_rules]. #' #' Fields are collected in order until the first `NULL` field is located. No #' fields after the first `NULL` field are used. #' #' @inheritParams rlang::args_dots_empty #' @inheritParams year_month_day #' #' @param year `[integer]` #' #' The fiscal year. Values `[-32767, 32767]` are generally allowed. #' #' @param quarter `[integer / NULL]` #' #' The fiscal quarter. Values `[1, 4]` are allowed. #' #' @param day `[integer / "last" / NULL]` #' #' The day of the quarter. Values `[1, 92]` are allowed. #' #' If `"last"`, the last day of the quarter is returned. #' #' @param start `[integer(1) / NULL]` #' #' The month to start the fiscal year in. 1 = January and 12 = December. #' #' If `NULL`, a `start` of January will be used. #' #' @return A year-quarter-day calendar vector. #' #' @export #' @examples #' # Year-quarter type #' x <- year_quarter_day(2019, 1:4) #' x #' #' add_quarters(x, 2) #' #' # Set the day to the last day of the quarter #' x <- set_day(x, "last") #' x #' #' # Start the fiscal year in June #' june <- 6L #' y <- year_quarter_day(2019, 1:4, "last", start = june) #' #' # Compare the year-month-day values that result from having different #' # fiscal year start months #' as_year_month_day(x) #' as_year_month_day(y) year_quarter_day <- function(year, quarter = NULL, day = NULL, hour = NULL, minute = NULL, second = NULL, subsecond = NULL, ..., start = NULL, subsecond_precision = NULL) { check_dots_empty() start <- quarterly_validate_start(start) # Stop on the first `NULL` argument if (is_null(quarter)) { precision <- PRECISION_YEAR fields <- list(year = year) } else if (is_null(day)) { precision <- PRECISION_QUARTER fields <- list(year = year, quarter = quarter) } else if (is_null(hour)) { precision <- PRECISION_DAY fields <- list(year = year, quarter = quarter, day = day) } else if (is_null(minute)) { precision <- PRECISION_HOUR fields <- list(year = year, quarter = quarter, day = day, hour = hour) } else if (is_null(second)) { precision <- PRECISION_MINUTE fields <- list(year = year, quarter = quarter, day = day, hour = hour, minute = minute) } else if (is_null(subsecond)) { precision <- PRECISION_SECOND fields <- list(year = year, quarter = quarter, day = day, hour = hour, minute = minute, second = second) } else { calendar_check_subsecond_precision(subsecond_precision) precision <- precision_to_integer(subsecond_precision) fields <- list(year = year, quarter = quarter, day = day, hour = hour, minute = minute, second = second, subsecond = subsecond) } if (is_last(fields$day)) { fields$day <- 1L last <- TRUE } else { last <- FALSE } fields <- vec_cast_common(!!!fields, .to = integer()) if (precision >= PRECISION_YEAR) { check_between_year(fields$year, arg = "year") } if (precision >= PRECISION_QUARTER) { check_between_quarter(fields$quarter, arg = "quarter") } if (precision >= PRECISION_DAY) { check_between_day_of_quarter(fields$day, arg = "day") } if (precision >= PRECISION_HOUR) { check_between_hour(fields$hour, arg = "hour") } if (precision >= PRECISION_MINUTE) { check_between_minute(fields$minute, arg = "minute") } if (precision >= PRECISION_SECOND) { check_between_second(fields$second, arg = "second") } if (precision > PRECISION_SECOND) { check_between_subsecond(fields$subsecond, precision, arg = "subsecond") } fields <- vec_recycle_common(!!!fields) fields <- df_list_propagate_missing(fields) names <- NULL out <- new_year_quarter_day_from_fields(fields, precision, start, names) if (last) { out <- set_day(out, "last") } out } # ------------------------------------------------------------------------------ #' @export vec_proxy.clock_year_quarter_day <- function(x, ...) { .Call(`_clock_clock_rcrd_proxy`, x) } #' @export vec_restore.clock_year_quarter_day <- function(x, to, ...) { .Call(`_clock_year_quarter_day_restore`, x, to) } # ------------------------------------------------------------------------------ #' @export format.clock_year_quarter_day <- function(x, ...) { out <- format_year_quarter_day_cpp(x, calendar_precision_attribute(x), quarterly_start(x)) names(out) <- names(x) out } #' @export vec_ptype_full.clock_year_quarter_day <- function(x, ...) { start <- quarterly_start(x) start <- quarterly_start_prettify(start, abbreviate = FALSE) class <- paste0("year_quarter_day<", start, ">") calendar_ptype_full(x, class) } #' @export vec_ptype_abbr.clock_year_quarter_day <- function(x, ...) { start <- quarterly_start(x) start <- quarterly_start_prettify(start, abbreviate = TRUE) class <- paste0("yqd<", start, ">") calendar_ptype_abbr(x, class) } # ------------------------------------------------------------------------------ #' Is `x` a year-quarter-day? #' #' Check if `x` is a year-quarter-day. #' #' @param x `[object]` #' #' An object. #' #' @return Returns `TRUE` if `x` inherits from `"clock_year_quarter_day"`, #' otherwise returns `FALSE`. #' #' @export #' @examples #' is_year_quarter_day(year_quarter_day(2019)) is_year_quarter_day <- function(x) { inherits(x, "clock_year_quarter_day") } # ------------------------------------------------------------------------------ #' @export vec_ptype.clock_year_quarter_day <- function(x, ...) { names <- NULL precision <- calendar_precision_attribute(x) start <- quarterly_start(x) f <- integer() fields <- switch( precision + 1L, list(year = f), list(year = f, quarter = f), abort("Internal error: Invalid precision"), abort("Internal error: Invalid precision"), list(year = f, quarter = f, day = f), list(year = f, quarter = f, day = f, hour = f), list(year = f, quarter = f, day = f, hour = f, minute = f), list(year = f, quarter = f, day = f, hour = f, minute = f, second = f), list(year = f, quarter = f, day = f, hour = f, minute = f, second = f, subsecond = f), list(year = f, quarter = f, day = f, hour = f, minute = f, second = f, subsecond = f), list(year = f, quarter = f, day = f, hour = f, minute = f, second = f, subsecond = f), abort("Internal error: Invalid precision.") ) new_year_quarter_day_from_fields(fields, precision, start, names) } #' @export vec_ptype2.clock_year_quarter_day.clock_year_quarter_day <- function(x, y, ...) { if (quarterly_start(x) != quarterly_start(y)) { stop_incompatible_type(x, y, ..., details = "Can't combine quarterly types with different `start`s.") } ptype2_calendar_and_calendar(x, y, ...) } #' @export vec_cast.clock_year_quarter_day.clock_year_quarter_day <- function(x, to, ...) { if (quarterly_start(x) != quarterly_start(to)) { stop_incompatible_cast(x, to, ..., details = "Can't cast between quarterly types with different `start`s.") } cast_calendar_to_calendar(x, to, ...) } # ------------------------------------------------------------------------------ #' @export calendar_is_precision.clock_year_quarter_day <- function(x, precision) { year_quarter_day_is_precision(precision) } year_quarter_day_is_precision <- function(precision) { if (precision == PRECISION_YEAR || precision == PRECISION_QUARTER) { TRUE } else if (precision >= PRECISION_DAY && precision <= PRECISION_NANOSECOND) { TRUE } else { FALSE } } # ------------------------------------------------------------------------------ #' @export invalid_detect.clock_year_quarter_day <- function(x) { precision <- calendar_precision_attribute(x) start <- quarterly_start(x) if (precision < PRECISION_DAY) { rep_along(x, FALSE) } else { year <- field_year(x) quarter <- field_quarter(x) day <- field_day(x) invalid_detect_year_quarter_day_cpp(year, quarter, day, start) } } #' @export invalid_any.clock_year_quarter_day <- function(x) { precision <- calendar_precision_attribute(x) start <- quarterly_start(x) if (precision < PRECISION_DAY) { FALSE } else { year <- field_year(x) quarter <- field_quarter(x) day <- field_day(x) invalid_any_year_quarter_day_cpp(year, quarter, day, start) } } #' @export invalid_count.clock_year_quarter_day <- function(x) { precision <- calendar_precision_attribute(x) start <- quarterly_start(x) if (precision < PRECISION_DAY) { 0L } else { year <- field_year(x) quarter <- field_quarter(x) day <- field_day(x) invalid_count_year_quarter_day_cpp(year, quarter, day, start) } } #' @export invalid_resolve.clock_year_quarter_day <- function(x, ..., invalid = NULL) { check_dots_empty() precision <- calendar_precision_attribute(x) start <- quarterly_start(x) invalid <- validate_invalid(invalid) if (precision < PRECISION_DAY) { x } else { fields <- invalid_resolve_year_quarter_day_cpp(x, precision, start, invalid, current_env()) new_year_quarter_day_from_fields(fields, precision, start, names = names(x)) } } # ------------------------------------------------------------------------------ #' Getters: year-quarter-day #' #' @description #' These are year-quarter-day methods for the #' [getter generics][clock-getters]. #' #' - `get_year()` returns the fiscal year. Note that this can differ from the #' Gregorian year if `start != 1L`. #' #' - `get_quarter()` returns the fiscal quarter as a value between 1-4. #' #' - `get_day()` returns the day of the fiscal quarter as a value between 1-92. #' #' - There are sub-daily getters for extracting more precise components. #' #' @param x `[clock_year_quarter_day]` #' #' A year-quarter-day to get the component from. #' #' @return The component. #' #' @name year-quarter-day-getters #' @examples #' x <- year_quarter_day(2020, 1:4) #' #' get_quarter(x) #' #' # Set and then get the last day of the quarter #' x <- set_day(x, "last") #' get_day(x) #' #' # Start the fiscal year in November and choose the 50th day in #' # each quarter of 2020 #' november <- 11 #' y <- year_quarter_day(2020, 1:4, 50, start = 11) #' y #' #' get_day(y) #' #' # What does that map to in year-month-day? #' as_year_month_day(y) NULL #' @rdname year-quarter-day-getters #' @export get_year.clock_year_quarter_day <- function(x) { field_year(x) } #' @rdname year-quarter-day-getters #' @export get_quarter.clock_year_quarter_day <- function(x) { calendar_check_minimum_precision(x, PRECISION_QUARTER) field_quarter(x) } #' @rdname year-quarter-day-getters #' @export get_day.clock_year_quarter_day <- function(x) { calendar_check_minimum_precision(x, PRECISION_DAY) field_day(x) } #' @rdname year-quarter-day-getters #' @export get_hour.clock_year_quarter_day <- function(x) { calendar_check_minimum_precision(x, PRECISION_HOUR) field_hour(x) } #' @rdname year-quarter-day-getters #' @export get_minute.clock_year_quarter_day <- function(x) { calendar_check_minimum_precision(x, PRECISION_MINUTE) field_minute(x) } #' @rdname year-quarter-day-getters #' @export get_second.clock_year_quarter_day <- function(x) { calendar_check_minimum_precision(x, PRECISION_SECOND) field_second(x) } #' @rdname year-quarter-day-getters #' @export get_millisecond.clock_year_quarter_day <- function(x) { calendar_check_exact_precision(x, PRECISION_MILLISECOND) field_subsecond(x) } #' @rdname year-quarter-day-getters #' @export get_microsecond.clock_year_quarter_day <- function(x) { calendar_check_exact_precision(x, PRECISION_MICROSECOND) field_subsecond(x) } #' @rdname year-quarter-day-getters #' @export get_nanosecond.clock_year_quarter_day <- function(x) { calendar_check_exact_precision(x, PRECISION_NANOSECOND) field_subsecond(x) } # ------------------------------------------------------------------------------ #' Setters: year-quarter-day #' #' @description #' These are year-quarter-day methods for the #' [setter generics][clock-setters]. #' #' - `set_year()` sets the fiscal year. #' #' - `set_quarter()` sets the fiscal quarter of the year. #' Valid values are in the range of `[1, 4]`. #' #' - `set_day()` sets the day of the fiscal quarter. #' Valid values are in the range of `[1, 92]`. #' #' - There are sub-daily setters for setting more precise components. #' #' @inheritParams rlang::args_dots_empty #' #' @param x `[clock_year_quarter_day]` #' #' A year-quarter-day vector. #' #' @param value `[integer / "last"]` #' #' The value to set the component to. #' #' For `set_day()`, this can also be `"last"` to adjust to the last #' day of the current fiscal quarter. #' #' @return `x` with the component set. #' #' @name year-quarter-day-setters #' @examples #' library(magrittr) #' #' # Quarter precision vector #' x <- year_quarter_day(2019, 1:4) #' x #' #' # Promote to day precision by setting the day #' x <- set_day(x, 1) #' x #' #' # Or set to the last day of the quarter #' x <- set_day(x, "last") #' x #' #' # What year-month-day is this? #' as_year_month_day(x) #' #' # Set to an invalid day of the quarter #' # (not all quarters have 92 days) #' invalid <- set_day(x, 92) #' invalid #' #' # Here are the invalid ones #' invalid[invalid_detect(invalid)] #' #' # Resolve the invalid dates by choosing the previous/next valid moment #' invalid_resolve(invalid, invalid = "previous") #' invalid_resolve(invalid, invalid = "next") #' #' # Or resolve by "overflowing" by the number of days that you have #' # gone past the last valid day #' invalid_resolve(invalid, invalid = "overflow") #' #' # This is similar to #' days <- get_day(invalid) - 1L #' invalid %>% #' set_day(1) %>% #' as_naive_time() %>% #' add_days(days) %>% #' as_year_quarter_day() NULL #' @rdname year-quarter-day-setters #' @export set_year.clock_year_quarter_day <- function(x, value, ...) { check_dots_empty() set_field_year_quarter_day(x, value, "year") } #' @rdname year-quarter-day-setters #' @export set_quarter.clock_year_quarter_day <- function(x, value, ...) { check_dots_empty() calendar_check_minimum_precision(x, PRECISION_YEAR) set_field_year_quarter_day(x, value, "quarter") } #' @rdname year-quarter-day-setters #' @export set_day.clock_year_quarter_day <- function(x, value, ...) { check_dots_empty() calendar_check_minimum_precision(x, PRECISION_QUARTER) set_field_year_quarter_day(x, value, "day") } #' @rdname year-quarter-day-setters #' @export set_hour.clock_year_quarter_day <- function(x, value, ...) { check_dots_empty() calendar_check_minimum_precision(x, PRECISION_DAY) set_field_year_quarter_day(x, value, "hour") } #' @rdname year-quarter-day-setters #' @export set_minute.clock_year_quarter_day <- function(x, value, ...) { check_dots_empty() calendar_check_minimum_precision(x, PRECISION_HOUR) set_field_year_quarter_day(x, value, "minute") } #' @rdname year-quarter-day-setters #' @export set_second.clock_year_quarter_day <- function(x, value, ...) { check_dots_empty() calendar_check_minimum_precision(x, PRECISION_MINUTE) set_field_year_quarter_day(x, value, "second") } #' @rdname year-quarter-day-setters #' @export set_millisecond.clock_year_quarter_day <- function(x, value, ...) { check_dots_empty() calendar_check_exact_precision(x, c(PRECISION_SECOND, PRECISION_MILLISECOND)) set_field_year_quarter_day(x, value, "millisecond") } #' @rdname year-quarter-day-setters #' @export set_microsecond.clock_year_quarter_day <- function(x, value, ...) { check_dots_empty() calendar_check_exact_precision(x, c(PRECISION_SECOND, PRECISION_MICROSECOND)) set_field_year_quarter_day(x, value, "microsecond") } #' @rdname year-quarter-day-setters #' @export set_nanosecond.clock_year_quarter_day <- function(x, value, ...) { check_dots_empty() calendar_check_exact_precision(x, c(PRECISION_SECOND, PRECISION_NANOSECOND)) set_field_year_quarter_day(x, value, "nanosecond") } set_field_year_quarter_day <- function(x, value, component) { if (is_last(value) && identical(component, "day")) { return(set_field_year_quarter_day_last(x)) } precision_fields <- calendar_precision_attribute(x) precision_value <- year_quarter_day_component_to_precision(component) precision_out <- precision_common2(precision_fields, precision_value) start_out <- quarterly_start(x) names_out <- names(x) value <- vec_cast(value, integer()) value <- unname(value) switch( component, year = check_between_year(value), quarter = check_between_quarter(value), day = check_between_day_of_quarter(value), hour = check_between_hour(value), minute = check_between_minute(value), second = check_between_second(value), millisecond = check_between_subsecond(value, PRECISION_MILLISECOND), microsecond = check_between_subsecond(value, PRECISION_MICROSECOND), nanosecond = check_between_subsecond(value, PRECISION_NANOSECOND), abort("Unknown `component`", .internal = TRUE) ) args <- vec_recycle_common(x = x, value = value) args <- df_list_propagate_missing(args) x <- args$x value <- args$value field <- year_quarter_day_component_to_field(component) out <- vec_unstructure(x) out[[field]] <- value new_year_quarter_day_from_fields(out, precision_out, start_out, names = names_out) } set_field_year_quarter_day_last <- function(x) { precision_fields <- calendar_precision_attribute(x) precision_out <- precision_common2(precision_fields, PRECISION_DAY) start_out <- quarterly_start(x) names_out <- names(x) year <- field_year(x) quarter <- field_quarter(x) value <- get_year_quarter_day_last_cpp(year, quarter, start_out) out <- vec_unstructure(x) out[["day"]] <- value new_year_quarter_day_from_fields(out, precision_out, start_out, names = names_out) } # ------------------------------------------------------------------------------ #' @export calendar_name.clock_year_quarter_day <- function(x) { "year_quarter_day" } # ------------------------------------------------------------------------------ year_quarter_day_component_to_precision <- function(component) { switch( component, year = PRECISION_YEAR, quarter = PRECISION_QUARTER, day = PRECISION_DAY, hour = PRECISION_HOUR, minute = PRECISION_MINUTE, second = PRECISION_SECOND, millisecond = PRECISION_MILLISECOND, microsecond = PRECISION_MICROSECOND, nanosecond = PRECISION_NANOSECOND, abort("Internal error: Unknown component name.") ) } year_quarter_day_component_to_field <- function(component) { switch ( component, year = component, quarter = component, day = component, hour = component, minute = component, second = component, millisecond = "subsecond", microsecond = "subsecond", nanosecond = "subsecond", abort("Internal error: Unknown component name.") ) } # ------------------------------------------------------------------------------ #' @rdname clock-arith #' @method vec_arith clock_year_quarter_day #' @export vec_arith.clock_year_quarter_day <- function(op, x, y, ...) { UseMethod("vec_arith.clock_year_quarter_day", y) } #' @method vec_arith.clock_year_quarter_day MISSING #' @export vec_arith.clock_year_quarter_day.MISSING <- function(op, x, y, ...) { arith_calendar_and_missing(op, x, y, ...) } #' @method vec_arith.clock_year_quarter_day clock_year_quarter_day #' @export vec_arith.clock_year_quarter_day.clock_year_quarter_day <- function(op, x, y, ...) { arith_calendar_and_calendar(op, x, y, ..., calendar_minus_calendar_fn = year_quarter_day_minus_year_quarter_day) } #' @method vec_arith.clock_year_quarter_day clock_duration #' @export vec_arith.clock_year_quarter_day.clock_duration <- function(op, x, y, ...) { arith_calendar_and_duration(op, x, y, ...) } #' @method vec_arith.clock_duration clock_year_quarter_day #' @export vec_arith.clock_duration.clock_year_quarter_day <- function(op, x, y, ...) { arith_duration_and_calendar(op, x, y, ...) } #' @method vec_arith.clock_year_quarter_day numeric #' @export vec_arith.clock_year_quarter_day.numeric <- function(op, x, y, ...) { arith_calendar_and_numeric(op, x, y, ...) } #' @method vec_arith.numeric clock_year_quarter_day #' @export vec_arith.numeric.clock_year_quarter_day <- function(op, x, y, ...) { arith_numeric_and_calendar(op, x, y, ...) } year_quarter_day_minus_year_quarter_day <- function(op, x, y, ...) { args <- vec_recycle_common(x = x, y = y) args <- vec_cast_common(!!!args) x <- args$x y <- args$y names <- names_common(x, y) start <- quarterly_start(x) precision <- calendar_precision_attribute(x) if (precision > PRECISION_QUARTER) { stop_incompatible_op(op, x, y, ...) } fields <- year_quarter_day_minus_year_quarter_day_cpp(x, y, precision, start) new_duration_from_fields(fields, precision, names = names) } # ------------------------------------------------------------------------------ #' Arithmetic: year-quarter-day #' #' @description #' These are year-quarter-day methods for the #' [arithmetic generics][clock-arithmetic]. #' #' - `add_years()` #' #' - `add_quarters()` #' #' Notably, _you cannot add days to a year-quarter-day_. For day-based #' arithmetic, first convert to a time point with [as_naive_time()] or #' [as_sys_time()]. #' #' @details #' `x` and `n` are recycled against each other using #' [tidyverse recycling rules][vctrs::vector_recycling_rules]. #' #' @inheritParams clock-arithmetic #' #' @param x `[clock_year_quarter_day]` #' #' A year-quarter-day vector. #' #' @return `x` after performing the arithmetic. #' #' @name year-quarter-day-arithmetic #' #' @examples #' x <- year_quarter_day(2019, 1:3) #' x #' #' add_quarters(x, 2) #' #' # Make the fiscal year start in March #' y <- year_quarter_day(2019, 1:2, 1, start = 3) #' y #' #' add_quarters(y, 1) #' #' # What year-month-day does this correspond to? #' # Note that the fiscal year doesn't necessarily align with the Gregorian #' # year! #' as_year_month_day(add_quarters(y, 1)) NULL #' @rdname year-quarter-day-arithmetic #' @export add_years.clock_year_quarter_day <- function(x, n, ...) { year_quarter_day_plus_duration(x, n, PRECISION_YEAR) } #' @rdname year-quarter-day-arithmetic #' @export add_quarters.clock_year_quarter_day <- function(x, n, ...) { calendar_check_minimum_precision(x, PRECISION_QUARTER) year_quarter_day_plus_duration(x, n, PRECISION_QUARTER) } year_quarter_day_plus_duration <- function(x, n, n_precision, ..., error_call = caller_env()) { check_dots_empty0(...) start <- quarterly_start(x) x_precision <- calendar_precision_attribute(x) n <- duration_collect_n(n, n_precision, error_call = error_call) size <- vec_size_common(x = x, n = n, .call = error_call) args <- vec_recycle_common(x = x, n = n, .size = size) x <- args$x n <- args$n names <- names_common(x, n) x <- vec_unstructure(x) if (n_precision == PRECISION_YEAR) { fields <- year_quarter_day_plus_years_cpp(x$year, start, n) x$year <- fields$year } else if (n_precision == PRECISION_QUARTER) { fields <- year_quarter_day_plus_quarters_cpp(x$year, x$quarter, start, n) x$year <- fields$year x$quarter <- fields$quarter } else { abort("Unknown precision.", .internal = TRUE) } if (x_precision != n_precision) { x <- df_list_propagate_missing(x, size = size) } new_year_quarter_day_from_fields(x, x_precision, start, names = names) } # ------------------------------------------------------------------------------ #' Convert to year-quarter-day #' #' @description #' `as_year_quarter_day()` converts a vector to the year-quarter-day #' calendar. Time points, Dates, POSIXct, and other calendars can all be #' converted to year-quarter-day. #' #' @inheritParams rlang::args_dots_empty #' #' @param x `[vector]` #' #' A vector to convert to year-quarter-day. #' #' @param start `[integer(1) / NULL]` #' #' The month to start the fiscal year in. 1 = January and 12 = December. #' #' If `NULL`: #' #' - If `x` is a year-quarter-day, it will be returned as is. #' #' - Otherwise, a `start` of January will be used. #' #' @return A year-quarter-day vector. #' @export #' @examples #' # From Date #' as_year_quarter_day(as.Date("2019-01-01")) #' as_year_quarter_day(as.Date("2019-01-01"), start = 3) #' #' # From POSIXct, which assumes that the naive time is what should be converted #' as_year_quarter_day(as.POSIXct("2019-01-01 02:30:30", "America/New_York")) #' #' # From other calendars #' tuesday <- 3 #' as_year_quarter_day(year_month_weekday(2019, 2, tuesday, 2)) #' #' # Converting between `start`s #' x <- year_quarter_day(2019, 01, 01, start = 2) #' x #' #' # Default keeps the same start #' as_year_quarter_day(x) #' #' # But you can change it #' as_year_quarter_day(x, start = 1) as_year_quarter_day <- function(x, ..., start = NULL) { UseMethod("as_year_quarter_day") } #' @export as_year_quarter_day.default <- function(x, ..., start = NULL) { stop_clock_unsupported_conversion(x, "clock_year_quarter_day") } #' @export as_year_quarter_day.clock_year_quarter_day <- function(x, ..., start = NULL) { check_dots_empty0(...) if (is_null(start)) { return(x) } start <- quarterly_validate_start(start) x_start <- quarterly_start(x) if (x_start == start) { return(x) } as_year_quarter_day(as_sys_time(x), start = start) } # ------------------------------------------------------------------------------ #' @export as_sys_time.clock_year_quarter_day <- function(x, ...) { check_dots_empty0(...) calendar_check_no_invalid(x) start <- quarterly_start(x) precision <- calendar_precision_attribute(x) fields <- as_sys_time_year_quarter_day_cpp(x, precision, start) new_sys_time_from_fields(fields, precision, clock_rcrd_names(x)) } #' @export as_naive_time.clock_year_quarter_day <- function(x, ...) { check_dots_empty0(...) as_naive_time(as_sys_time(x)) } #' @export as.character.clock_year_quarter_day <- function(x, ...) { format(x) } # ------------------------------------------------------------------------------ #' @export calendar_leap_year.clock_year_quarter_day <- function(x) { year <- get_year(x) start <- quarterly_start(x) year_quarter_day_leap_year_cpp(year, start) } # ------------------------------------------------------------------------------ #' Grouping: year-quarter-day #' #' @description #' This is a year-quarter-day method for the [calendar_group()] generic. #' #' Grouping for a year-quarter-day object can be done at any precision, as #' long as `x` is at least as precise as `precision`. #' #' @inheritParams calendar_group #' #' @param x `[clock_year_quarter_day]` #' #' A year-quarter-day vector. #' #' @param precision `[character(1)]` #' #' One of: #' #' - `"year"` #' - `"quarter"` #' - `"day"` #' - `"hour"` #' - `"minute"` #' - `"second"` #' - `"millisecond"` #' - `"microsecond"` #' - `"nanosecond"` #' #' @return `x` grouped at the specified `precision`. #' #' @name year-quarter-day-group #' #' @export #' @examples #' x <- year_quarter_day(2019, 1:4) #' x <- c(x, set_year(x, 2020)) #' #' # Group by 3 quarters #' # Note that this is a grouping of 3 quarters of the current year #' # (i.e. the count resets at the beginning of the next year) #' calendar_group(x, "quarter", n = 3) #' #' # Group by 5 days of the current quarter #' y <- year_quarter_day(2019, 1, 1:90) #' calendar_group(y, "day", n = 5) calendar_group.clock_year_quarter_day <- function(x, precision, ..., n = 1L) { n <- validate_calendar_group_n(n) x <- calendar_narrow(x, precision) check_precision(precision) precision <- precision_to_integer(precision) if (precision == PRECISION_YEAR) { value <- get_year(x) value <- group_component0(value, n) x <- set_year(x, value) return(x) } if (precision == PRECISION_QUARTER) { value <- get_quarter(x) value <- group_component1(value, n) x <- set_quarter(x, value) return(x) } if (precision == PRECISION_DAY) { value <- get_day(x) value <- group_component1(value, n) x <- set_day(x, value) return(x) } x <- calendar_group_time(x, n, precision) x } # ------------------------------------------------------------------------------ #' Narrow: year-quarter-day #' #' This is a year-quarter-day method for the [calendar_narrow()] generic. It #' narrows a year-quarter-day vector to the specified `precision`. #' #' @inheritParams year-quarter-day-group #' #' @return `x` narrowed to the supplied `precision`. #' #' @name year-quarter-day-narrow #' #' @export #' @examples #' # Day precision #' x <- year_quarter_day(2019, 1, 5) #' x #' #' # Narrow to quarter precision #' calendar_narrow(x, "quarter") calendar_narrow.clock_year_quarter_day <- function(x, precision) { check_precision(precision) precision <- precision_to_integer(precision) start <- quarterly_start(x) out_fields <- list() x_fields <- unclass(x) if (precision >= PRECISION_YEAR) { out_fields[["year"]] <- x_fields[["year"]] } if (precision >= PRECISION_QUARTER) { out_fields[["quarter"]] <- x_fields[["quarter"]] } if (precision >= PRECISION_DAY) { out_fields[["day"]] <- x_fields[["day"]] } out_fields <- calendar_narrow_time(out_fields, precision, x_fields) new_year_quarter_day_from_fields(out_fields, precision, start, names = names(x)) } # ------------------------------------------------------------------------------ #' Widen: year-quarter-day #' #' This is a year-quarter-day method for the [calendar_widen()] generic. It #' widens a year-quarter-day vector to the specified `precision`. #' #' @inheritParams year-quarter-day-group #' #' @return `x` widened to the supplied `precision`. #' #' @name year-quarter-day-widen #' #' @export #' @examples #' # Quarter precision #' x <- year_quarter_day(2019, 1) #' x #' #' # Widen to day precision #' calendar_widen(x, "day") #' #' # Or second precision #' sec <- calendar_widen(x, "second") #' sec calendar_widen.clock_year_quarter_day <- function(x, precision) { x_precision <- calendar_precision_attribute(x) check_precision(precision) precision <- precision_to_integer(precision) if (precision >= PRECISION_QUARTER && x_precision < PRECISION_QUARTER) { x <- set_quarter(x, 1L) } if (precision >= PRECISION_DAY && x_precision < PRECISION_DAY) { x <- set_day(x, 1L) } x <- calendar_widen_time(x, x_precision, precision) x } # ------------------------------------------------------------------------------ #' Boundaries: year-quarter-day #' #' This is a year-quarter-day method for the [calendar_start()] and #' [calendar_end()] generics. They adjust components of a calendar to the #' start or end of a specified `precision`. #' #' @inheritParams year-quarter-day-group #' #' @return `x` at the same precision, but with some components altered to be #' at the boundary value. #' #' @name year-quarter-day-boundary #' #' @examples #' x <- year_quarter_day(2019:2020, 2:3, 5, 6, 7, 8, start = clock_months$march) #' x #' #' # Compute the last moment of the fiscal quarter #' calendar_end(x, "quarter") #' #' # Compare that to just setting the day to `"last"`, #' # which doesn't affect the other components #' set_day(x, "last") #' #' # Compute the start of the fiscal year #' calendar_start(x, "year") #' #' as_date(calendar_start(x, "year")) NULL #' @rdname year-quarter-day-boundary #' @export calendar_start.clock_year_quarter_day <- function(x, precision) { x_precision <- calendar_precision_attribute(x) check_precision(precision) precision <- precision_to_integer(precision) calendar_start_end_checks(x, x_precision, precision, "start") if (precision <= PRECISION_YEAR && x_precision > PRECISION_YEAR) { x <- set_quarter(x, 1L) } if (precision <= PRECISION_QUARTER && x_precision > PRECISION_QUARTER) { x <- set_day(x, 1L) } x <- calendar_start_time(x, x_precision, precision) x } #' @rdname year-quarter-day-boundary #' @export calendar_end.clock_year_quarter_day <- function(x, precision) { x_precision <- calendar_precision_attribute(x) check_precision(precision) precision <- precision_to_integer(precision) calendar_start_end_checks(x, x_precision, precision, "end") if (precision <= PRECISION_YEAR && x_precision > PRECISION_YEAR) { x <- set_quarter(x, 4L) } if (precision <= PRECISION_QUARTER && x_precision > PRECISION_QUARTER) { x <- set_day(x, "last") } x <- calendar_end_time(x, x_precision, precision) x } # ------------------------------------------------------------------------------ #' Counting: year-quarter-day #' #' This is a year-quarter-day method for the [calendar_count_between()] generic. #' It counts the number of `precision` units between `start` and `end` (i.e., #' the number of years or quarters). #' #' @inheritParams calendar-count-between #' #' @param start,end `[clock_year_quarter_day]` #' #' A pair of year-quarter-day vectors. These will be recycled to their #' common size. #' #' @param precision `[character(1)]` #' #' One of: #' #' - `"year"` #' - `"quarter"` #' #' @inherit calendar-count-between return #' #' @name year-quarter-day-count-between #' #' @export #' @examples #' # Compute the number of whole quarters between two dates #' x <- year_quarter_day(2020, 3, 91) #' y <- year_quarter_day(2025, 4, c(90, 92)) #' calendar_count_between(x, y, "quarter") #' #' # Note that this is not always the same as the number of whole 3 month #' # periods between two dates #' x <- as_year_month_day(x) #' y <- as_year_month_day(y) #' calendar_count_between(x, y, "month", n = 3) calendar_count_between.clock_year_quarter_day <- function(start, end, precision, ..., n = 1L) { NextMethod() } calendar_count_between_standardize_precision_n.clock_year_quarter_day <- function(x, precision, n) { check_precision(precision) precision_int <- precision_to_integer(precision) allowed_precisions <- c(PRECISION_YEAR, PRECISION_QUARTER) if (!(precision_int %in% allowed_precisions)) { abort("`precision` must be one of: 'year', 'quarter'.") } list(precision = precision, n = n) } calendar_count_between_compute.clock_year_quarter_day <- function(start, end, precision) { check_precision(precision) precision <- precision_to_integer(precision) if (precision == PRECISION_YEAR) { out <- get_year(end) - get_year(start) return(out) } if (precision == PRECISION_QUARTER) { out <- (get_year(end) - get_year(start)) * 4L out <- out + (get_quarter(end) - get_quarter(start)) return(out) } abort("Internal error: `precision` should be 'year' or 'quarter' at this point.") } calendar_count_between_proxy_compare.clock_year_quarter_day <- function(start, end, precision) { check_precision(precision) precision <- precision_to_integer(precision) start <- vec_proxy_compare(start) end <- vec_proxy_compare(end) if (precision >= PRECISION_YEAR) { start$year <- NULL end$year <- NULL } if (precision >= PRECISION_QUARTER) { start$quarter <- NULL end$quarter <- NULL } list(start = start, end = end) } # ------------------------------------------------------------------------------ #' Sequences: year-quarter-day #' #' @description #' This is a year-quarter-day method for the [seq()] generic. #' #' Sequences can only be generated for `"year"` and `"quarter"` precision #' year-quarter-day vectors. #' #' When calling `seq()`, exactly two of the following must be specified: #' - `to` #' - `by` #' - Either `length.out` or `along.with` #' #' @inheritParams seq.clock_duration #' #' @param from `[clock_year_quarter_day(1)]` #' #' A `"year"` or `"quarter"` precision year-quarter-day to start the sequence #' from. #' #' `from` is always included in the result. #' #' @param to `[clock_year_quarter_day(1) / NULL]` #' #' A `"year"` or `"quarter"` precision year-quarter-day to stop the sequence #' at. #' #' `to` is cast to the type of `from`. #' #' `to` is only included in the result if the resulting sequence divides #' the distance between `from` and `to` exactly. #' #' @return A sequence with the type of `from`. #' #' @export #' @examples #' # Quarterly sequence #' x <- seq(year_quarter_day(2020, 1), year_quarter_day(2026, 3), by = 2) #' x #' #' # Which we can then set the day of the quarter of #' set_day(x, "last") seq.clock_year_quarter_day <- function(from, to = NULL, by = NULL, length.out = NULL, along.with = NULL, ...) { precision <- calendar_precision_attribute(from) if (precision > PRECISION_QUARTER) { abort("`from` must be 'year' or 'quarter' precision.") } seq_impl( from = from, to = to, by = by, length.out = length.out, along.with = along.with, precision = precision, ... ) } # ------------------------------------------------------------------------------ #' @export clock_minimum.clock_year_quarter_day <- function(x) { names <- NULL start <- quarterly_start(x) fields <- list(year = clock_calendar_year_minimum) year <- new_year_quarter_day_from_fields(fields, PRECISION_YEAR, start, names) precision <- calendar_precision_attribute(x) precision <- precision_to_string(precision) calendar_minimum(precision, year) } #' @export clock_maximum.clock_year_quarter_day <- function(x) { names <- NULL start <- quarterly_start(x) fields <- list(year = clock_calendar_year_maximum) year <- new_year_quarter_day_from_fields(fields, PRECISION_YEAR, start, names) precision <- calendar_precision_attribute(x) precision <- precision_to_string(precision) calendar_maximum(precision, year) } # ------------------------------------------------------------------------------ quarterly_start <- function(x) { attr(x, "start", exact = TRUE) } quarterly_start_prettify <- function(start, ..., abbreviate = FALSE) { check_dots_empty() if (abbreviate) { month.abb[start] } else { month.name[start] } } quarterly_validate_start <- function(start, ..., error_call = caller_env()) { if (is_null(start)) { return(1L) } check_number_whole(start, min = 1, max = 12, call = error_call) start <- vec_cast(start, integer(), call = error_call) start } clock/R/iso-year-week-day.R0000644000176200001440000011311514427270231015131 0ustar liggesusers#' Calendar: iso-year-week-day #' #' `iso_year_week_day()` constructs a calendar from the ISO year, week number, #' and week day. #' #' @details #' Fields are recycled against each other using #' [tidyverse recycling rules][vctrs::vector_recycling_rules]. #' #' Fields are collected in order until the first `NULL` field is located. No #' fields after the first `NULL` field are used. #' #' @inheritParams rlang::args_dots_empty #' @inheritParams year_month_day #' #' @param year `[integer]` #' #' The ISO year. Values `[-32767, 32767]` are generally allowed. #' #' @param week `[integer / "last" / NULL]` #' #' The ISO week. Values `[1, 53]` are allowed. #' #' If `"last"`, then the last week of the ISO year is returned. #' #' @param day `[integer / NULL]` #' #' The day of the week. Values `[1, 7]` are allowed, with 1 = Monday #' and 7 = Sunday, in accordance with the ISO specifications. #' #' @return A iso-year-week-day calendar vector. #' #' @export #' @examples #' # Year-week #' x <- iso_year_week_day(2019:2025, 1) #' x #' #' # 2nd day of the first ISO week in multiple years #' iso_days <- set_day(x, clock_iso_weekdays$tuesday) #' iso_days #' #' # What year-month-day is this? #' as_year_month_day(iso_days) iso_year_week_day <- function(year, week = NULL, day = NULL, hour = NULL, minute = NULL, second = NULL, subsecond = NULL, ..., subsecond_precision = NULL) { check_dots_empty() # Stop on the first `NULL` argument if (is_null(week)) { precision <- PRECISION_YEAR fields <- list(year = year) } else if (is_null(day)) { precision <- PRECISION_WEEK fields <- list(year = year, week = week) } else if (is_null(hour)) { precision <- PRECISION_DAY fields <- list(year = year, week = week, day = day) } else if (is_null(minute)) { precision <- PRECISION_HOUR fields <- list(year = year, week = week, day = day, hour = hour) } else if (is_null(second)) { precision <- PRECISION_MINUTE fields <- list(year = year, week = week, day = day, hour = hour, minute = minute) } else if (is_null(subsecond)) { precision <- PRECISION_SECOND fields <- list(year = year, week = week, day = day, hour = hour, minute = minute, second = second) } else { calendar_check_subsecond_precision(subsecond_precision) precision <- precision_to_integer(subsecond_precision) fields <- list(year = year, week = week, day = day, hour = hour, minute = minute, second = second, subsecond = subsecond) } if (is_last(fields$week)) { fields$week <- 1L last <- TRUE } else { last <- FALSE } fields <- vec_cast_common(!!!fields, .to = integer()) if (precision >= PRECISION_YEAR) { check_between_year(fields$year, arg = "year") } if (precision >= PRECISION_WEEK) { check_between_week(fields$week, arg = "week") } if (precision >= PRECISION_DAY) { check_between_day_of_week(fields$day, arg = "day") } if (precision >= PRECISION_HOUR) { check_between_hour(fields$hour, arg = "hour") } if (precision >= PRECISION_MINUTE) { check_between_minute(fields$minute, arg = "minute") } if (precision >= PRECISION_SECOND) { check_between_second(fields$second, arg = "second") } if (precision > PRECISION_SECOND) { check_between_subsecond(fields$subsecond, precision, arg = "subsecond") } fields <- vec_recycle_common(!!!fields) fields <- df_list_propagate_missing(fields) names <- NULL out <- new_iso_year_week_day_from_fields(fields, precision, names) if (last) { out <- set_week(out, "last") } out } # ------------------------------------------------------------------------------ #' @export vec_proxy.clock_iso_year_week_day <- function(x, ...) { .Call(`_clock_clock_rcrd_proxy`, x) } #' @export vec_restore.clock_iso_year_week_day <- function(x, to, ...) { .Call(`_clock_iso_year_week_day_restore`, x, to) } # ------------------------------------------------------------------------------ #' @export format.clock_iso_year_week_day <- function(x, ...) { out <- format_iso_year_week_day_cpp(x, calendar_precision_attribute(x)) names(out) <- names(x) out } #' @export vec_ptype_full.clock_iso_year_week_day <- function(x, ...) { calendar_ptype_full(x, "iso_year_week_day") } #' @export vec_ptype_abbr.clock_iso_year_week_day <- function(x, ...) { calendar_ptype_abbr(x, "iso_ywd") } # ------------------------------------------------------------------------------ #' Is `x` a iso-year-week-day? #' #' Check if `x` is a iso-year-week-day. #' #' @param x `[object]` #' #' An object. #' #' @return Returns `TRUE` if `x` inherits from `"clock_iso_year_week_day"`, #' otherwise returns `FALSE`. #' #' @export #' @examples #' is_iso_year_week_day(iso_year_week_day(2019)) is_iso_year_week_day <- function(x) { inherits(x, "clock_iso_year_week_day") } # ------------------------------------------------------------------------------ #' @export vec_ptype.clock_iso_year_week_day <- function(x, ...) { switch( calendar_precision_attribute(x) + 1L, clock_empty_iso_year_week_day_year, abort("Internal error: Invalid precision"), abort("Internal error: Invalid precision"), clock_empty_iso_year_week_day_week, clock_empty_iso_year_week_day_day, clock_empty_iso_year_week_day_hour, clock_empty_iso_year_week_day_minute, clock_empty_iso_year_week_day_second, clock_empty_iso_year_week_day_millisecond, clock_empty_iso_year_week_day_microsecond, clock_empty_iso_year_week_day_nanosecond, abort("Internal error: Invalid precision.") ) } #' @export vec_ptype2.clock_iso_year_week_day.clock_iso_year_week_day <- function(x, y, ...) { ptype2_calendar_and_calendar(x, y, ...) } #' @export vec_cast.clock_iso_year_week_day.clock_iso_year_week_day <- function(x, to, ...) { cast_calendar_to_calendar(x, to, ...) } # ------------------------------------------------------------------------------ #' @export calendar_is_precision.clock_iso_year_week_day <- function(x, precision) { iso_year_week_day_is_precision(precision) } iso_year_week_day_is_precision <- function(precision) { if (precision == PRECISION_YEAR || precision == PRECISION_WEEK) { TRUE } else if (precision >= PRECISION_DAY && precision <= PRECISION_NANOSECOND) { TRUE } else { FALSE } } # ------------------------------------------------------------------------------ #' @export invalid_detect.clock_iso_year_week_day <- function(x) { precision <- calendar_precision_attribute(x) if (precision < PRECISION_WEEK) { rep_along(x, FALSE) } else { year <- field_year(x) week <- field_week(x) invalid_detect_iso_year_week_day_cpp(year, week) } } #' @export invalid_any.clock_iso_year_week_day <- function(x) { precision <- calendar_precision_attribute(x) if (precision < PRECISION_WEEK) { FALSE } else { year <- field_year(x) week <- field_week(x) invalid_any_iso_year_week_day_cpp(year, week) } } #' @export invalid_count.clock_iso_year_week_day <- function(x) { precision <- calendar_precision_attribute(x) if (precision < PRECISION_WEEK) { 0L } else { year <- field_year(x) week <- field_week(x) invalid_count_iso_year_week_day_cpp(year, week) } } #' @export invalid_resolve.clock_iso_year_week_day <- function(x, ..., invalid = NULL) { check_dots_empty() precision <- calendar_precision_attribute(x) invalid <- validate_invalid(invalid) if (precision < PRECISION_WEEK) { x } else { fields <- invalid_resolve_iso_year_week_day_cpp(x, precision, invalid, current_env()) new_iso_year_week_day_from_fields(fields, precision, names(x)) } } # ------------------------------------------------------------------------------ #' Getters: iso-year-week-day #' #' @description #' These are iso-year-week-day methods for the #' [getter generics][clock-getters]. #' #' - `get_year()` returns the ISO year. Note that this can differ from the #' Gregorian year. #' #' - `get_week()` returns the ISO week of the current ISO year. #' #' - `get_day()` returns a value between 1-7 indicating the weekday of the #' current ISO week, where 1 = Monday and 7 = Sunday, in line with the #' ISO standard. #' #' - There are sub-daily getters for extracting more precise components. #' #' @param x `[clock_iso_year_week_day]` #' #' A iso-year-week-day to get the component from. #' #' @return The component. #' #' @name iso-year-week-day-getters #' @examples #' x <- iso_year_week_day(2019, 50:52, 1:3) #' x #' #' # Get the ISO week #' get_week(x) #' #' # Gets the weekday, 1 = Monday, 7 = Sunday #' get_day(x) #' #' # Note that the ISO year can differ from the Gregorian year #' iso <- iso_year_week_day(2019, 1, 1) #' ymd <- as_year_month_day(iso) #' #' get_year(iso) #' get_year(ymd) NULL #' @rdname iso-year-week-day-getters #' @export get_year.clock_iso_year_week_day <- function(x) { field_year(x) } #' @rdname iso-year-week-day-getters #' @export get_week.clock_iso_year_week_day <- function(x) { calendar_check_minimum_precision(x, PRECISION_WEEK) field_week(x) } #' @rdname iso-year-week-day-getters #' @export get_day.clock_iso_year_week_day <- function(x) { calendar_check_minimum_precision(x, PRECISION_DAY) field_day(x) } #' @rdname iso-year-week-day-getters #' @export get_hour.clock_iso_year_week_day <- function(x) { calendar_check_minimum_precision(x, PRECISION_HOUR) field_hour(x) } #' @rdname iso-year-week-day-getters #' @export get_minute.clock_iso_year_week_day <- function(x) { calendar_check_minimum_precision(x, PRECISION_MINUTE) field_minute(x) } #' @rdname iso-year-week-day-getters #' @export get_second.clock_iso_year_week_day <- function(x) { calendar_check_minimum_precision(x, PRECISION_SECOND) field_second(x) } #' @rdname iso-year-week-day-getters #' @export get_millisecond.clock_iso_year_week_day <- function(x) { calendar_check_exact_precision(x, PRECISION_MILLISECOND) field_subsecond(x) } #' @rdname iso-year-week-day-getters #' @export get_microsecond.clock_iso_year_week_day <- function(x) { calendar_check_exact_precision(x, PRECISION_MICROSECOND) field_subsecond(x) } #' @rdname iso-year-week-day-getters #' @export get_nanosecond.clock_iso_year_week_day <- function(x) { calendar_check_exact_precision(x, PRECISION_NANOSECOND) field_subsecond(x) } # ------------------------------------------------------------------------------ #' Setters: iso-year-week-day #' #' @description #' These are iso-year-week-day methods for the #' [setter generics][clock-setters]. #' #' - `set_year()` sets the ISO year. #' #' - `set_week()` sets the ISO week of the year. Valid values are in the range #' of `[1, 53]`. #' #' - `set_day()` sets the day of the week. Valid values are in the range of #' `[1, 7]`, with 1 = Monday, and 7 = Sunday. #' #' - There are sub-daily setters for setting more precise components. #' #' @inheritParams rlang::args_dots_empty #' #' @param x `[clock_iso_year_week_day]` #' #' A iso-year-week-day vector. #' #' @param value `[integer / "last"]` #' #' The value to set the component to. #' #' For `set_week()`, this can also be `"last"` to adjust to the last #' week of the current ISO year. #' #' @return `x` with the component set. #' #' @name iso-year-week-day-setters #' @examples #' # Year precision vector #' x <- iso_year_week_day(2019:2023) #' #' # Promote to week precision by setting the week #' # (Note that some ISO weeks have 52 weeks, and others have 53) #' x <- set_week(x, "last") #' x #' #' # Set to an invalid week #' invalid <- set_week(x, 53) #' invalid #' #' # Here are the invalid ones (they only have 52 weeks) #' invalid[invalid_detect(invalid)] #' #' # Resolve the invalid dates by choosing the previous/next valid moment #' invalid_resolve(invalid, invalid = "previous") #' invalid_resolve(invalid, invalid = "next") NULL #' @rdname iso-year-week-day-setters #' @export set_year.clock_iso_year_week_day <- function(x, value, ...) { check_dots_empty() set_field_iso_year_week_day(x, value, "year") } #' @rdname iso-year-week-day-setters #' @export set_week.clock_iso_year_week_day <- function(x, value, ...) { check_dots_empty() calendar_check_minimum_precision(x, PRECISION_YEAR) set_field_iso_year_week_day(x, value, "week") } #' @rdname iso-year-week-day-setters #' @export set_day.clock_iso_year_week_day <- function(x, value, ...) { check_dots_empty() calendar_check_minimum_precision(x, PRECISION_WEEK) set_field_iso_year_week_day(x, value, "day") } #' @rdname iso-year-week-day-setters #' @export set_hour.clock_iso_year_week_day <- function(x, value, ...) { check_dots_empty() calendar_check_minimum_precision(x, PRECISION_DAY) set_field_iso_year_week_day(x, value, "hour") } #' @rdname iso-year-week-day-setters #' @export set_minute.clock_iso_year_week_day <- function(x, value, ...) { check_dots_empty() calendar_check_minimum_precision(x, PRECISION_HOUR) set_field_iso_year_week_day(x, value, "minute") } #' @rdname iso-year-week-day-setters #' @export set_second.clock_iso_year_week_day <- function(x, value, ...) { check_dots_empty() calendar_check_minimum_precision(x, PRECISION_MINUTE) set_field_iso_year_week_day(x, value, "second") } #' @rdname iso-year-week-day-setters #' @export set_millisecond.clock_iso_year_week_day <- function(x, value, ...) { check_dots_empty() calendar_check_exact_precision(x, c(PRECISION_SECOND, PRECISION_MILLISECOND)) set_field_iso_year_week_day(x, value, "millisecond") } #' @rdname iso-year-week-day-setters #' @export set_microsecond.clock_iso_year_week_day <- function(x, value, ...) { check_dots_empty() calendar_check_exact_precision(x, c(PRECISION_SECOND, PRECISION_MICROSECOND)) set_field_iso_year_week_day(x, value, "microsecond") } #' @rdname iso-year-week-day-setters #' @export set_nanosecond.clock_iso_year_week_day <- function(x, value, ...) { check_dots_empty() calendar_check_exact_precision(x, c(PRECISION_SECOND, PRECISION_NANOSECOND)) set_field_iso_year_week_day(x, value, "nanosecond") } set_field_iso_year_week_day <- function(x, value, component) { if (is_last(value) && identical(component, "week")) { return(set_field_iso_year_week_day_last(x)) } precision_fields <- calendar_precision_attribute(x) precision_value <- iso_year_week_day_component_to_precision(component) precision_out <- precision_common2(precision_fields, precision_value) names_out <- names(x) value <- vec_cast(value, integer()) value <- unname(value) switch( component, year = check_between_year(value), week = check_between_week(value), day = check_between_day_of_week(value), hour = check_between_hour(value), minute = check_between_minute(value), second = check_between_second(value), millisecond = check_between_subsecond(value, PRECISION_MILLISECOND), microsecond = check_between_subsecond(value, PRECISION_MICROSECOND), nanosecond = check_between_subsecond(value, PRECISION_NANOSECOND), abort("Unknown `component`", .internal = TRUE) ) args <- vec_recycle_common(x = x, value = value) args <- df_list_propagate_missing(args) x <- args$x value <- args$value field <- iso_year_week_day_component_to_field(component) out <- vec_unstructure(x) out[[field]] <- value new_iso_year_week_day_from_fields(out, precision_out, names = names_out) } set_field_iso_year_week_day_last <- function(x) { precision_fields <- calendar_precision_attribute(x) precision_out <- precision_common2(precision_fields, PRECISION_WEEK) names_out <- names(x) year <- field_year(x) value <- get_iso_year_week_day_last_cpp(year) out <- vec_unstructure(x) out[["week"]] <- value new_iso_year_week_day_from_fields(out, precision_out, names = names_out) } # ------------------------------------------------------------------------------ #' @export calendar_name.clock_iso_year_week_day <- function(x) { "iso_year_week_day" } # ------------------------------------------------------------------------------ iso_year_week_day_component_to_precision <- function(component) { switch( component, year = PRECISION_YEAR, week = PRECISION_WEEK, day = PRECISION_DAY, hour = PRECISION_HOUR, minute = PRECISION_MINUTE, second = PRECISION_SECOND, millisecond = PRECISION_MILLISECOND, microsecond = PRECISION_MICROSECOND, nanosecond = PRECISION_NANOSECOND, abort("Internal error: Unknown component name.") ) } iso_year_week_day_component_to_field <- function(component) { switch ( component, year = component, week = component, day = component, hour = component, minute = component, second = component, millisecond = "subsecond", microsecond = "subsecond", nanosecond = "subsecond", abort("Internal error: Unknown component name.") ) } # ------------------------------------------------------------------------------ #' @rdname clock-arith #' @method vec_arith clock_iso_year_week_day #' @export vec_arith.clock_iso_year_week_day <- function(op, x, y, ...) { UseMethod("vec_arith.clock_iso_year_week_day", y) } #' @method vec_arith.clock_iso_year_week_day MISSING #' @export vec_arith.clock_iso_year_week_day.MISSING <- function(op, x, y, ...) { arith_calendar_and_missing(op, x, y, ...) } #' @method vec_arith.clock_iso_year_week_day clock_iso_year_week_day #' @export vec_arith.clock_iso_year_week_day.clock_iso_year_week_day <- function(op, x, y, ...) { arith_calendar_and_calendar(op, x, y, ..., calendar_minus_calendar_fn = iso_year_week_day_minus_iso_year_week_day) } #' @method vec_arith.clock_iso_year_week_day clock_duration #' @export vec_arith.clock_iso_year_week_day.clock_duration <- function(op, x, y, ...) { arith_calendar_and_duration(op, x, y, ...) } #' @method vec_arith.clock_duration clock_iso_year_week_day #' @export vec_arith.clock_duration.clock_iso_year_week_day <- function(op, x, y, ...) { arith_duration_and_calendar(op, x, y, ...) } #' @method vec_arith.clock_iso_year_week_day numeric #' @export vec_arith.clock_iso_year_week_day.numeric <- function(op, x, y, ...) { arith_calendar_and_numeric(op, x, y, ...) } #' @method vec_arith.numeric clock_iso_year_week_day #' @export vec_arith.numeric.clock_iso_year_week_day <- function(op, x, y, ...) { arith_numeric_and_calendar(op, x, y, ...) } iso_year_week_day_minus_iso_year_week_day <- function(op, x, y, ...) { args <- vec_recycle_common(x = x, y = y) args <- vec_cast_common(!!!args) x <- args$x y <- args$y names <- names_common(x, y) precision <- calendar_precision_attribute(x) if (precision > PRECISION_YEAR) { stop_incompatible_op(op, x, y, ...) } fields <- iso_year_week_day_minus_iso_year_week_day_cpp(x, y, precision) new_duration_from_fields(fields, precision, names = names) } # ------------------------------------------------------------------------------ #' Arithmetic: iso-year-week-day #' #' @description #' These are iso-year-week-day methods for the #' [arithmetic generics][clock-arithmetic]. #' #' - `add_years()` #' #' You cannot add weeks or days to an iso-year-week-day calendar. Adding #' days is much more efficiently done by converting to a time point first #' by using [as_naive_time()] or [as_sys_time()]. Adding weeks is equally #' as efficient as adding 7 days. Additionally, adding weeks to an invalid #' iso-year-week object containing `iso_year_week_day(2019, 53)` would be #' undefined, as the 53rd ISO week of 2019 doesn't exist to begin with. #' #' @details #' `x` and `n` are recycled against each other using #' [tidyverse recycling rules][vctrs::vector_recycling_rules]. #' #' @inheritParams clock-arithmetic #' #' @param x `[clock_iso_year_week_day]` #' #' A iso-year-week-day vector. #' #' @return `x` after performing the arithmetic. #' #' @name iso-year-week-day-arithmetic #' #' @examples #' x <- iso_year_week_day(2019, 1, 1) #' add_years(x, 1:2) NULL #' @rdname iso-year-week-day-arithmetic #' @export add_years.clock_iso_year_week_day <- function(x, n, ...) { iso_year_week_day_plus_duration(x, n, PRECISION_YEAR) } iso_year_week_day_plus_duration <- function(x, n, n_precision, ..., error_call = caller_env()) { check_dots_empty0(...) x_precision <- calendar_precision_attribute(x) n <- duration_collect_n(n, n_precision, error_call = error_call) size <- vec_size_common(x = x, n = n, .call = error_call) args <- vec_recycle_common(x = x, n = n, .size = size) x <- args$x n <- args$n names <- names_common(x, n) x <- vec_unstructure(x) if (n_precision == PRECISION_YEAR) { fields <- iso_year_week_day_plus_years_cpp(x$year, n) x$year <- fields$year } else { abort("Unknown precision.", .internal = TRUE) } if (x_precision != n_precision) { x <- df_list_propagate_missing(x, size = size) } new_iso_year_week_day_from_fields(x, x_precision, names = names) } # ------------------------------------------------------------------------------ #' Convert to iso-year-week-day #' #' `as_iso_year_week_day()` converts a vector to the iso-year-week-day #' calendar. Time points, Dates, POSIXct, and other calendars can all be #' converted to iso-year-week-day. #' #' @inheritParams rlang::args_dots_empty #' #' @param x `[vector]` #' #' A vector to convert to iso-year-week-day. #' #' @return A iso-year-week-day vector. #' @export #' @examples #' # From Date #' as_iso_year_week_day(as.Date("2019-01-01")) #' #' # From POSIXct, which assumes that the naive time is what should be converted #' as_iso_year_week_day(as.POSIXct("2019-01-01 02:30:30", "America/New_York")) #' #' # From other calendars #' as_iso_year_week_day(year_quarter_day(2019, quarter = 2, day = 50)) as_iso_year_week_day <- function(x, ...) { UseMethod("as_iso_year_week_day") } #' @export as_iso_year_week_day.default <- function(x, ...) { stop_clock_unsupported_conversion(x, "clock_iso_year_week_day") } #' @export as_iso_year_week_day.clock_iso_year_week_day <- function(x, ...) { check_dots_empty0(...) x } # ------------------------------------------------------------------------------ #' @export as_sys_time.clock_iso_year_week_day <- function(x, ...) { check_dots_empty0(...) calendar_check_no_invalid(x) precision <- calendar_precision_attribute(x) fields <- as_sys_time_iso_year_week_day_cpp(x, precision) new_sys_time_from_fields(fields, precision, clock_rcrd_names(x)) } #' @export as_naive_time.clock_iso_year_week_day <- function(x, ...) { check_dots_empty0(...) as_naive_time(as_sys_time(x)) } #' @export as.character.clock_iso_year_week_day <- function(x, ...) { format(x) } # ------------------------------------------------------------------------------ #' @export calendar_leap_year.clock_iso_year_week_day <- function(x) { year <- get_year(x) iso_year_week_day_leap_year_cpp(year) } # ------------------------------------------------------------------------------ #' Grouping: iso-year-week-day #' #' @description #' This is a iso-year-week-day method for the [calendar_group()] generic. #' #' Grouping for a iso-year-week-day object can be done at any precision, as #' long as `x` is at least as precise as `precision`. #' #' @inheritParams calendar_group #' #' @param x `[clock_iso_year_week_day]` #' #' A iso-year-week-day vector. #' #' @param precision `[character(1)]` #' #' One of: #' #' - `"year"` #' - `"week"` #' - `"day"` #' - `"hour"` #' - `"minute"` #' - `"second"` #' - `"millisecond"` #' - `"microsecond"` #' - `"nanosecond"` #' #' @return `x` grouped at the specified `precision`. #' #' @name iso-year-week-day-group #' #' @export #' @examples #' x <- iso_year_week_day(2019, 1:52) #' #' # Group by 3 ISO weeks #' calendar_group(x, "week", n = 3) #' #' y <- iso_year_week_day(2000:2020, 1, 1) #' #' # Group by 2 ISO years #' calendar_group(y, "year", n = 2) calendar_group.clock_iso_year_week_day <- function(x, precision, ..., n = 1L) { n <- validate_calendar_group_n(n) x <- calendar_narrow(x, precision) check_precision(precision) precision <- precision_to_integer(precision) if (precision == PRECISION_YEAR) { value <- get_year(x) value <- group_component0(value, n) x <- set_year(x, value) return(x) } if (precision == PRECISION_WEEK) { value <- get_week(x) value <- group_component1(value, n) x <- set_week(x, value) return(x) } if (precision == PRECISION_DAY) { value <- get_day(x) value <- group_component1(value, n) x <- set_day(x, value) return(x) } x <- calendar_group_time(x, n, precision) x } # ------------------------------------------------------------------------------ #' Narrow: iso-year-week-day #' #' This is a iso-year-week-day method for the [calendar_narrow()] generic. It #' narrows a iso-year-week-day vector to the specified `precision`. #' #' @inheritParams iso-year-week-day-group #' #' @return `x` narrowed to the supplied `precision`. #' #' @name iso-year-week-day-narrow #' #' @export #' @examples #' # Day precision #' x <- iso_year_week_day(2019, 1, 5) #' x #' #' # Narrowed to week precision #' calendar_narrow(x, "week") calendar_narrow.clock_iso_year_week_day <- function(x, precision) { check_precision(precision) precision <- precision_to_integer(precision) out_fields <- list() x_fields <- unclass(x) if (precision >= PRECISION_YEAR) { out_fields[["year"]] <- x_fields[["year"]] } if (precision >= PRECISION_WEEK) { out_fields[["week"]] <- x_fields[["week"]] } if (precision >= PRECISION_DAY) { out_fields[["day"]] <- x_fields[["day"]] } out_fields <- calendar_narrow_time(out_fields, precision, x_fields) new_iso_year_week_day_from_fields(out_fields, precision, names = names(x)) } # ------------------------------------------------------------------------------ #' Widen: iso-year-week-day #' #' This is a iso-year-week-day method for the [calendar_widen()] generic. It #' widens a iso-year-week-day vector to the specified `precision`. #' #' @inheritParams iso-year-week-day-group #' #' @return `x` widened to the supplied `precision`. #' #' @name iso-year-week-day-widen #' #' @export #' @examples #' # Week precision #' x <- iso_year_week_day(2019, 1) #' x #' #' # Widen to day precision #' # In the ISO calendar, the first day of the week is a Monday #' calendar_widen(x, "day") #' #' # Or second precision #' sec <- calendar_widen(x, "second") #' sec calendar_widen.clock_iso_year_week_day <- function(x, precision) { x_precision <- calendar_precision_attribute(x) check_precision(precision) precision <- precision_to_integer(precision) if (precision >= PRECISION_WEEK && x_precision < PRECISION_WEEK) { x <- set_week(x, 1L) } if (precision >= PRECISION_DAY && x_precision < PRECISION_DAY) { x <- set_day(x, 1L) } x <- calendar_widen_time(x, x_precision, precision) x } # ------------------------------------------------------------------------------ #' Boundaries: iso-year-week-day #' #' This is an iso-year-week-day method for the [calendar_start()] and #' [calendar_end()] generics. They adjust components of a calendar to the #' start or end of a specified `precision`. #' #' @inheritParams iso-year-week-day-group #' #' @return `x` at the same precision, but with some components altered to be #' at the boundary value. #' #' @name iso-year-week-day-boundary #' #' @examples #' x <- iso_year_week_day(2019:2020, 5, 6, 10) #' x #' #' # Compute the last moment of the last iso week of the year #' calendar_end(x, "year") #' #' # Compare that to just setting the week to `"last"`, #' # which doesn't affect the other components #' set_week(x, "last") NULL #' @rdname iso-year-week-day-boundary #' @export calendar_start.clock_iso_year_week_day <- function(x, precision) { x_precision <- calendar_precision_attribute(x) check_precision(precision) precision <- precision_to_integer(precision) calendar_start_end_checks(x, x_precision, precision, "start") if (precision <= PRECISION_YEAR && x_precision > PRECISION_YEAR) { x <- set_week(x, 1L) } if (precision <= PRECISION_WEEK && x_precision > PRECISION_WEEK) { x <- set_day(x, 1L) } x <- calendar_start_time(x, x_precision, precision) x } #' @rdname iso-year-week-day-boundary #' @export calendar_end.clock_iso_year_week_day <- function(x, precision) { x_precision <- calendar_precision_attribute(x) check_precision(precision) precision <- precision_to_integer(precision) calendar_start_end_checks(x, x_precision, precision, "end") if (precision <= PRECISION_YEAR && x_precision > PRECISION_YEAR) { x <- set_week(x, "last") } if (precision <= PRECISION_WEEK && x_precision > PRECISION_WEEK) { x <- set_day(x, 7L) } x <- calendar_end_time(x, x_precision, precision) x } # ------------------------------------------------------------------------------ #' Counting: iso-year-week-day #' #' This is an iso-year-week-day method for the [calendar_count_between()] #' generic. It counts the number of `precision` units between `start` and `end` #' (i.e., the number of ISO years). #' #' @inheritParams calendar-count-between #' #' @param start,end `[clock_iso_year_week_day]` #' #' A pair of iso-year-week-day vectors. These will be recycled to their #' common size. #' #' @param precision `[character(1)]` #' #' One of: #' #' - `"year"` #' #' @inherit calendar-count-between return #' #' @name iso-year-week-day-count-between #' #' @export #' @examples #' # Compute the number of whole ISO years between two dates #' x <- iso_year_week_day(2001, 1, 2) #' y <- iso_year_week_day(2021, 1, c(1, 3)) #' calendar_count_between(x, y, "year") calendar_count_between.clock_iso_year_week_day <- function(start, end, precision, ..., n = 1L) { NextMethod() } calendar_count_between_standardize_precision_n.clock_iso_year_week_day <- function(x, precision, n) { check_precision(precision) precision_int <- precision_to_integer(precision) allowed_precisions <- c(PRECISION_YEAR) if (!(precision_int %in% allowed_precisions)) { abort("`precision` must be one of: 'year'.") } list(precision = precision, n = n) } calendar_count_between_compute.clock_iso_year_week_day <- function(start, end, precision) { check_precision(precision) precision <- precision_to_integer(precision) if (precision == PRECISION_YEAR) { out <- get_year(end) - get_year(start) return(out) } abort("Internal error: `precision` should be 'year' at this point.") } calendar_count_between_proxy_compare.clock_iso_year_week_day <- function(start, end, precision) { check_precision(precision) precision <- precision_to_integer(precision) start <- vec_proxy_compare(start) end <- vec_proxy_compare(end) if (precision >= PRECISION_YEAR) { start$year <- NULL end$year <- NULL } list(start = start, end = end) } # ------------------------------------------------------------------------------ #' Sequences: iso-year-week-day #' #' @description #' This is a iso-year-week-day method for the [seq()] generic. #' #' Sequences can only be generated for `"year"` precision #' iso-year-week-day vectors. If you need to generate week-based sequences, #' you'll have to convert to a time point first. #' #' When calling `seq()`, exactly two of the following must be specified: #' - `to` #' - `by` #' - Either `length.out` or `along.with` #' #' @inheritParams seq.clock_duration #' #' @param from `[clock_iso_year_week_day(1)]` #' #' A `"year"` precision iso-year-week-day to start the sequence #' from. #' #' `from` is always included in the result. #' #' @param to `[clock_iso_year_week_day(1) / NULL]` #' #' A `"year"` precision iso-year-week-day to stop the sequence #' at. #' #' `to` is cast to the type of `from`. #' #' `to` is only included in the result if the resulting sequence divides #' the distance between `from` and `to` exactly. #' #' @return A sequence with the type of `from`. #' #' @export #' @examples #' # Yearly sequence #' x <- seq(iso_year_week_day(2020), iso_year_week_day(2026), by = 2) #' x #' #' # Which we can then set the week of. #' # Some years have 53 ISO weeks, some have 52. #' set_week(x, "last") seq.clock_iso_year_week_day <- function(from, to = NULL, by = NULL, length.out = NULL, along.with = NULL, ...) { precision <- calendar_precision_attribute(from) if (precision > PRECISION_YEAR) { abort("`from` must be 'year' precision.") } seq_impl( from = from, to = to, by = by, length.out = length.out, along.with = along.with, precision = precision, ... ) } # ------------------------------------------------------------------------------ #' @export clock_minimum.clock_iso_year_week_day <- function(x) { switch( calendar_precision_attribute(x) + 1L, clock_minimum_iso_year_week_day_year, abort("Invalid precision", .internal = TRUE), abort("Invalid precision", .internal = TRUE), clock_minimum_iso_year_week_day_week, clock_minimum_iso_year_week_day_day, clock_minimum_iso_year_week_day_hour, clock_minimum_iso_year_week_day_minute, clock_minimum_iso_year_week_day_second, clock_minimum_iso_year_week_day_millisecond, clock_minimum_iso_year_week_day_microsecond, clock_minimum_iso_year_week_day_nanosecond, abort("Invalid precision", .internal = TRUE) ) } #' @export clock_maximum.clock_iso_year_week_day <- function(x) { switch( calendar_precision_attribute(x) + 1L, clock_maximum_iso_year_week_day_year, abort("Invalid precision", .internal = TRUE), abort("Invalid precision", .internal = TRUE), clock_maximum_iso_year_week_day_week, clock_maximum_iso_year_week_day_day, clock_maximum_iso_year_week_day_hour, clock_maximum_iso_year_week_day_minute, clock_maximum_iso_year_week_day_second, clock_maximum_iso_year_week_day_millisecond, clock_maximum_iso_year_week_day_microsecond, clock_maximum_iso_year_week_day_nanosecond, abort("Invalid precision", .internal = TRUE) ) } iso_year_week_day_minimum <- function(precision) { calendar_minimum(precision, iso_year_week_day(clock_calendar_year_minimum)) } iso_year_week_day_maximum <- function(precision) { calendar_maximum(precision, iso_year_week_day(clock_calendar_year_maximum)) } # ------------------------------------------------------------------------------ clock_init_iso_year_week_day_utils <- function(env) { year <- iso_year_week_day(integer()) assign("clock_empty_iso_year_week_day_year", year, envir = env) assign("clock_empty_iso_year_week_day_week", calendar_widen(year, "week"), envir = env) assign("clock_empty_iso_year_week_day_day", calendar_widen(year, "day"), envir = env) assign("clock_empty_iso_year_week_day_hour", calendar_widen(year, "hour"), envir = env) assign("clock_empty_iso_year_week_day_minute", calendar_widen(year, "minute"), envir = env) assign("clock_empty_iso_year_week_day_second", calendar_widen(year, "second"), envir = env) assign("clock_empty_iso_year_week_day_millisecond", calendar_widen(year, "millisecond"), envir = env) assign("clock_empty_iso_year_week_day_microsecond", calendar_widen(year, "microsecond"), envir = env) assign("clock_empty_iso_year_week_day_nanosecond", calendar_widen(year, "nanosecond"), envir = env) assign("clock_minimum_iso_year_week_day_year", iso_year_week_day_minimum("year"), envir = env) assign("clock_minimum_iso_year_week_day_week", iso_year_week_day_minimum("week"), envir = env) assign("clock_minimum_iso_year_week_day_day", iso_year_week_day_minimum("day"), envir = env) assign("clock_minimum_iso_year_week_day_hour", iso_year_week_day_minimum("hour"), envir = env) assign("clock_minimum_iso_year_week_day_minute", iso_year_week_day_minimum("minute"), envir = env) assign("clock_minimum_iso_year_week_day_second", iso_year_week_day_minimum("second"), envir = env) assign("clock_minimum_iso_year_week_day_millisecond", iso_year_week_day_minimum("millisecond"), envir = env) assign("clock_minimum_iso_year_week_day_microsecond", iso_year_week_day_minimum("microsecond"), envir = env) assign("clock_minimum_iso_year_week_day_nanosecond", iso_year_week_day_minimum("nanosecond"), envir = env) assign("clock_maximum_iso_year_week_day_year", iso_year_week_day_maximum("year"), envir = env) assign("clock_maximum_iso_year_week_day_week", iso_year_week_day_maximum("week"), envir = env) assign("clock_maximum_iso_year_week_day_day", iso_year_week_day_maximum("day"), envir = env) assign("clock_maximum_iso_year_week_day_hour", iso_year_week_day_maximum("hour"), envir = env) assign("clock_maximum_iso_year_week_day_minute", iso_year_week_day_maximum("minute"), envir = env) assign("clock_maximum_iso_year_week_day_second", iso_year_week_day_maximum("second"), envir = env) assign("clock_maximum_iso_year_week_day_millisecond", iso_year_week_day_maximum("millisecond"), envir = env) assign("clock_maximum_iso_year_week_day_microsecond", iso_year_week_day_maximum("microsecond"), envir = env) assign("clock_maximum_iso_year_week_day_nanosecond", iso_year_week_day_maximum("nanosecond"), envir = env) invisible(NULL) } clock/R/clock-package.R0000644000176200001440000000042614426711643014367 0ustar liggesusers#' @keywords internal "_PACKAGE" # The following block is used by usethis to automatically manage # roxygen namespace tags. Modify with care! ## usethis namespace: start #' @import rlang #' @import vctrs #' @useDynLib clock, .registration = TRUE ## usethis namespace: end NULL clock/R/rcrd.R0000644000176200001440000000440314424761554012640 0ustar liggesusers# ------------------------------------------------------------------------------ #' @export names.clock_rcrd <- function(x) { .Call(`_clock_clock_rcrd_names`, x) } #' @export `names<-.clock_rcrd` <- function(x, value) { .Call(`_clock_clock_rcrd_set_names`, x, value) } # ------------------------------------------------------------------------------ # - `[.vctrs_rcrd` accidentally allows subsetting fields through `...` # https://github.com/r-lib/vctrs/issues/1295 #' @export `[.clock_rcrd` <- function(x, i) { i <- maybe_missing(i, default = TRUE) vec_slice(x, i) } # - `[[.vctrs_rcrd` doesn't drop names because names aren't supported for rcrds # - `[[.vctrs_rcrd` allows selections of size >1 # https://github.com/r-lib/vctrs/issues/1294 #' @export `[[.clock_rcrd` <- function(x, i) { size <- vec_size(x) names <- names(x) i <- vec_as_location2(i, n = size, names = names, arg = "i") # Unname - `[[` never returns input with names x <- unname(x) vec_slice(x, i) } # ------------------------------------------------------------------------------ #' @export min.clock_rcrd <- function(x, ..., na.rm = FALSE) { check_bool(na.rm) if (vec_is_empty(x) || (na.rm && vec_all_missing(x))) { clock_maximum(x) } else { NextMethod() } } #' @export max.clock_rcrd <- function(x, ..., na.rm = FALSE) { check_bool(na.rm) if (vec_is_empty(x) || (na.rm && vec_all_missing(x))) { clock_minimum(x) } else { NextMethod() } } #' @export range.clock_rcrd <- function(x, ..., na.rm = FALSE) { check_bool(na.rm) if (vec_is_empty(x) || (na.rm && vec_all_missing(x))) { vec_c(clock_maximum(x), clock_minimum(x)) } else { NextMethod() } } vec_all_missing <- function(x) { vec_any_missing(x) && all(vec_detect_missing(x)) } # ------------------------------------------------------------------------------ #' @export vec_math.clock_rcrd <- function(.fn, .x, ...) { switch( .fn, is.nan = clock_rcrd_is_nan(.x), is.finite = clock_rcrd_is_finite(.x), is.infinite = clock_rcrd_is_infinite(.x), NextMethod() ) } clock_rcrd_is_nan <- function(x) { vec_rep(FALSE, vec_size(x)) } clock_rcrd_is_finite <- function(x) { !vec_detect_missing(x) } clock_rcrd_is_infinite <- function(x) { vec_rep(FALSE, vec_size(x)) } clock/R/cpp11.R0000644000176200001440000004657614427430744012650 0ustar liggesusers# Generated by cpp11: do not edit by hand new_duration_from_fields <- function(fields, precision_int, names) { .Call(`_clock_new_duration_from_fields`, fields, precision_int, names) } duration_restore <- function(x, to) { .Call(`_clock_duration_restore`, x, to) } format_duration_cpp <- function(fields, precision_int) { .Call(`_clock_format_duration_cpp`, fields, precision_int) } duration_helper_cpp <- function(n, precision_int) { .Call(`_clock_duration_helper_cpp`, n, precision_int) } duration_cast_cpp <- function(fields, precision_from, precision_to) { .Call(`_clock_duration_cast_cpp`, fields, precision_from, precision_to) } duration_plus_cpp <- function(x, y, precision_int) { .Call(`_clock_duration_plus_cpp`, x, y, precision_int) } duration_minus_cpp <- function(x, y, precision_int) { .Call(`_clock_duration_minus_cpp`, x, y, precision_int) } duration_modulus_cpp <- function(x, y, precision_int) { .Call(`_clock_duration_modulus_cpp`, x, y, precision_int) } duration_integer_divide_cpp <- function(x, y, precision_int) { .Call(`_clock_duration_integer_divide_cpp`, x, y, precision_int) } duration_scalar_multiply_cpp <- function(x, y, precision_int) { .Call(`_clock_duration_scalar_multiply_cpp`, x, y, precision_int) } duration_scalar_modulus_cpp <- function(x, y, precision_int) { .Call(`_clock_duration_scalar_modulus_cpp`, x, y, precision_int) } duration_scalar_divide_cpp <- function(x, y, precision_int) { .Call(`_clock_duration_scalar_divide_cpp`, x, y, precision_int) } duration_precision_common_cpp <- function(x_precision, y_precision) { .Call(`_clock_duration_precision_common_cpp`, x_precision, y_precision) } duration_has_common_precision_cpp <- function(x_precision, y_precision) { .Call(`_clock_duration_has_common_precision_cpp`, x_precision, y_precision) } duration_floor_cpp <- function(fields, precision_from, precision_to, n) { .Call(`_clock_duration_floor_cpp`, fields, precision_from, precision_to, n) } duration_ceiling_cpp <- function(fields, precision_from, precision_to, n) { .Call(`_clock_duration_ceiling_cpp`, fields, precision_from, precision_to, n) } duration_round_cpp <- function(fields, precision_from, precision_to, n) { .Call(`_clock_duration_round_cpp`, fields, precision_from, precision_to, n) } duration_unary_minus_cpp <- function(fields, precision_int) { .Call(`_clock_duration_unary_minus_cpp`, fields, precision_int) } duration_as_integer_cpp <- function(fields, precision_int) { .Call(`_clock_duration_as_integer_cpp`, fields, precision_int) } duration_as_double_cpp <- function(fields, precision_int) { .Call(`_clock_duration_as_double_cpp`, fields, precision_int) } duration_abs_cpp <- function(fields, precision_int) { .Call(`_clock_duration_abs_cpp`, fields, precision_int) } duration_sign_cpp <- function(fields, precision_int) { .Call(`_clock_duration_sign_cpp`, fields, precision_int) } duration_seq_by_lo_cpp <- function(from, precision_int, by, length_out) { .Call(`_clock_duration_seq_by_lo_cpp`, from, precision_int, by, length_out) } duration_seq_to_by_cpp <- function(from, precision_int, to, by) { .Call(`_clock_duration_seq_to_by_cpp`, from, precision_int, to, by) } duration_seq_to_lo_cpp <- function(from, precision_int, to, length_out) { .Call(`_clock_duration_seq_to_lo_cpp`, from, precision_int, to, length_out) } duration_minimum_cpp <- function(precision_int) { .Call(`_clock_duration_minimum_cpp`, precision_int) } duration_maximum_cpp <- function(precision_int) { .Call(`_clock_duration_maximum_cpp`, precision_int) } precision_to_string <- function(precision_int) { .Call(`_clock_precision_to_string`, precision_int) } clock_to_string <- function(clock_int) { .Call(`_clock_clock_to_string`, clock_int) } format_time_point_cpp <- function(fields, clock, format, precision_int, month, month_abbrev, weekday, weekday_abbrev, am_pm, decimal_mark) { .Call(`_clock_format_time_point_cpp`, fields, clock, format, precision_int, month, month_abbrev, weekday, weekday_abbrev, am_pm, decimal_mark) } format_zoned_time_cpp <- function(fields, zone, abbreviate_zone, format, precision_int, month, month_abbrev, weekday, weekday_abbrev, am_pm, decimal_mark) { .Call(`_clock_format_zoned_time_cpp`, fields, zone, abbreviate_zone, format, precision_int, month, month_abbrev, weekday, weekday_abbrev, am_pm, decimal_mark) } new_year_day_from_fields <- function(fields, precision_int, names) { .Call(`_clock_new_year_day_from_fields`, fields, precision_int, names) } year_day_restore <- function(x, to) { .Call(`_clock_year_day_restore`, x, to) } format_year_day_cpp <- function(fields, precision_int) { .Call(`_clock_format_year_day_cpp`, fields, precision_int) } invalid_detect_year_day_cpp <- function(year, day) { .Call(`_clock_invalid_detect_year_day_cpp`, year, day) } invalid_any_year_day_cpp <- function(year, day) { .Call(`_clock_invalid_any_year_day_cpp`, year, day) } invalid_count_year_day_cpp <- function(year, day) { .Call(`_clock_invalid_count_year_day_cpp`, year, day) } invalid_resolve_year_day_cpp <- function(fields, precision_int, invalid_string, call) { .Call(`_clock_invalid_resolve_year_day_cpp`, fields, precision_int, invalid_string, call) } get_year_day_last_cpp <- function(year) { .Call(`_clock_get_year_day_last_cpp`, year) } year_day_plus_years_cpp <- function(year, fields_n) { .Call(`_clock_year_day_plus_years_cpp`, year, fields_n) } as_sys_time_year_day_cpp <- function(fields, precision_int) { .Call(`_clock_as_sys_time_year_day_cpp`, fields, precision_int) } as_year_day_from_sys_time_cpp <- function(fields, precision_int) { .Call(`_clock_as_year_day_from_sys_time_cpp`, fields, precision_int) } year_day_minus_year_day_cpp <- function(x, y, precision_int) { .Call(`_clock_year_day_minus_year_day_cpp`, x, y, precision_int) } new_year_month_day_from_fields <- function(fields, precision_int, names) { .Call(`_clock_new_year_month_day_from_fields`, fields, precision_int, names) } year_month_day_restore <- function(x, to) { .Call(`_clock_year_month_day_restore`, x, to) } format_year_month_day_cpp <- function(fields, precision_int) { .Call(`_clock_format_year_month_day_cpp`, fields, precision_int) } invalid_detect_year_month_day_cpp <- function(year, month, day) { .Call(`_clock_invalid_detect_year_month_day_cpp`, year, month, day) } invalid_any_year_month_day_cpp <- function(year, month, day) { .Call(`_clock_invalid_any_year_month_day_cpp`, year, month, day) } invalid_count_year_month_day_cpp <- function(year, month, day) { .Call(`_clock_invalid_count_year_month_day_cpp`, year, month, day) } invalid_resolve_year_month_day_cpp <- function(fields, precision_int, invalid_string, call) { .Call(`_clock_invalid_resolve_year_month_day_cpp`, fields, precision_int, invalid_string, call) } get_year_month_day_last_cpp <- function(year, month) { .Call(`_clock_get_year_month_day_last_cpp`, year, month) } year_month_day_plus_years_cpp <- function(year, fields_n) { .Call(`_clock_year_month_day_plus_years_cpp`, year, fields_n) } year_month_day_plus_months_cpp <- function(year, month, fields_n) { .Call(`_clock_year_month_day_plus_months_cpp`, year, month, fields_n) } as_sys_time_year_month_day_cpp <- function(fields, precision_int) { .Call(`_clock_as_sys_time_year_month_day_cpp`, fields, precision_int) } as_year_month_day_from_sys_time_cpp <- function(fields, precision_int) { .Call(`_clock_as_year_month_day_from_sys_time_cpp`, fields, precision_int) } year_month_day_minus_year_month_day_cpp <- function(x, y, precision_int) { .Call(`_clock_year_month_day_minus_year_month_day_cpp`, x, y, precision_int) } year_month_day_parse_cpp <- function(x, format, precision_int, month, month_abbrev, weekday, weekday_abbrev, am_pm, mark) { .Call(`_clock_year_month_day_parse_cpp`, x, format, precision_int, month, month_abbrev, weekday, weekday_abbrev, am_pm, mark) } gregorian_leap_year_cpp <- function(year) { .Call(`_clock_gregorian_leap_year_cpp`, year) } new_year_month_weekday_from_fields <- function(fields, precision_int, names) { .Call(`_clock_new_year_month_weekday_from_fields`, fields, precision_int, names) } year_month_weekday_restore <- function(x, to) { .Call(`_clock_year_month_weekday_restore`, x, to) } format_year_month_weekday_cpp <- function(fields, precision_int) { .Call(`_clock_format_year_month_weekday_cpp`, fields, precision_int) } invalid_detect_year_month_weekday_cpp <- function(year, month, day, index) { .Call(`_clock_invalid_detect_year_month_weekday_cpp`, year, month, day, index) } invalid_any_year_month_weekday_cpp <- function(year, month, day, index) { .Call(`_clock_invalid_any_year_month_weekday_cpp`, year, month, day, index) } invalid_count_year_month_weekday_cpp <- function(year, month, day, index) { .Call(`_clock_invalid_count_year_month_weekday_cpp`, year, month, day, index) } invalid_resolve_year_month_weekday_cpp <- function(fields, precision_int, invalid_string, call) { .Call(`_clock_invalid_resolve_year_month_weekday_cpp`, fields, precision_int, invalid_string, call) } get_year_month_weekday_last_cpp <- function(year, month, day, index) { .Call(`_clock_get_year_month_weekday_last_cpp`, year, month, day, index) } year_month_weekday_plus_years_cpp <- function(year, fields_n) { .Call(`_clock_year_month_weekday_plus_years_cpp`, year, fields_n) } year_month_weekday_plus_months_cpp <- function(year, month, fields_n) { .Call(`_clock_year_month_weekday_plus_months_cpp`, year, month, fields_n) } as_sys_time_year_month_weekday_cpp <- function(fields, precision_int) { .Call(`_clock_as_sys_time_year_month_weekday_cpp`, fields, precision_int) } as_year_month_weekday_from_sys_time_cpp <- function(fields, precision_int) { .Call(`_clock_as_year_month_weekday_from_sys_time_cpp`, fields, precision_int) } year_month_weekday_minus_year_month_weekday_cpp <- function(x, y, precision_int) { .Call(`_clock_year_month_weekday_minus_year_month_weekday_cpp`, x, y, precision_int) } new_iso_year_week_day_from_fields <- function(fields, precision_int, names) { .Call(`_clock_new_iso_year_week_day_from_fields`, fields, precision_int, names) } iso_year_week_day_restore <- function(x, to) { .Call(`_clock_iso_year_week_day_restore`, x, to) } format_iso_year_week_day_cpp <- function(fields, precision_int) { .Call(`_clock_format_iso_year_week_day_cpp`, fields, precision_int) } invalid_detect_iso_year_week_day_cpp <- function(year, week) { .Call(`_clock_invalid_detect_iso_year_week_day_cpp`, year, week) } invalid_any_iso_year_week_day_cpp <- function(year, week) { .Call(`_clock_invalid_any_iso_year_week_day_cpp`, year, week) } invalid_count_iso_year_week_day_cpp <- function(year, week) { .Call(`_clock_invalid_count_iso_year_week_day_cpp`, year, week) } invalid_resolve_iso_year_week_day_cpp <- function(fields, precision_int, invalid_string, call) { .Call(`_clock_invalid_resolve_iso_year_week_day_cpp`, fields, precision_int, invalid_string, call) } get_iso_year_week_day_last_cpp <- function(year) { .Call(`_clock_get_iso_year_week_day_last_cpp`, year) } iso_year_week_day_plus_years_cpp <- function(year, fields_n) { .Call(`_clock_iso_year_week_day_plus_years_cpp`, year, fields_n) } as_sys_time_iso_year_week_day_cpp <- function(fields, precision_int) { .Call(`_clock_as_sys_time_iso_year_week_day_cpp`, fields, precision_int) } as_iso_year_week_day_from_sys_time_cpp <- function(fields, precision_int) { .Call(`_clock_as_iso_year_week_day_from_sys_time_cpp`, fields, precision_int) } iso_year_week_day_minus_iso_year_week_day_cpp <- function(x, y, precision_int) { .Call(`_clock_iso_year_week_day_minus_iso_year_week_day_cpp`, x, y, precision_int) } iso_year_week_day_leap_year_cpp <- function(year) { .Call(`_clock_iso_year_week_day_leap_year_cpp`, year) } clock_get_calendar_year_maximum <- function() { .Call(`_clock_clock_get_calendar_year_maximum`) } clock_get_calendar_year_minimum <- function() { .Call(`_clock_clock_get_calendar_year_minimum`) } naive_time_info_cpp <- function(fields, precision_int, zone) { .Call(`_clock_naive_time_info_cpp`, fields, precision_int, zone) } new_year_quarter_day_from_fields <- function(fields, precision_int, start, names) { .Call(`_clock_new_year_quarter_day_from_fields`, fields, precision_int, start, names) } year_quarter_day_restore <- function(x, to) { .Call(`_clock_year_quarter_day_restore`, x, to) } format_year_quarter_day_cpp <- function(fields, precision_int, start_int) { .Call(`_clock_format_year_quarter_day_cpp`, fields, precision_int, start_int) } invalid_detect_year_quarter_day_cpp <- function(year, quarter, day, start_int) { .Call(`_clock_invalid_detect_year_quarter_day_cpp`, year, quarter, day, start_int) } invalid_any_year_quarter_day_cpp <- function(year, quarter, day, start_int) { .Call(`_clock_invalid_any_year_quarter_day_cpp`, year, quarter, day, start_int) } invalid_count_year_quarter_day_cpp <- function(year, quarter, day, start_int) { .Call(`_clock_invalid_count_year_quarter_day_cpp`, year, quarter, day, start_int) } invalid_resolve_year_quarter_day_cpp <- function(fields, precision_int, start_int, invalid_string, call) { .Call(`_clock_invalid_resolve_year_quarter_day_cpp`, fields, precision_int, start_int, invalid_string, call) } get_year_quarter_day_last_cpp <- function(year, quarter, start_int) { .Call(`_clock_get_year_quarter_day_last_cpp`, year, quarter, start_int) } year_quarter_day_plus_years_cpp <- function(year, start_int, fields_n) { .Call(`_clock_year_quarter_day_plus_years_cpp`, year, start_int, fields_n) } year_quarter_day_plus_quarters_cpp <- function(year, quarter, start_int, fields_n) { .Call(`_clock_year_quarter_day_plus_quarters_cpp`, year, quarter, start_int, fields_n) } as_sys_time_year_quarter_day_cpp <- function(fields, precision_int, start_int) { .Call(`_clock_as_sys_time_year_quarter_day_cpp`, fields, precision_int, start_int) } as_year_quarter_day_from_sys_time_cpp <- function(fields, precision_int, start_int) { .Call(`_clock_as_year_quarter_day_from_sys_time_cpp`, fields, precision_int, start_int) } year_quarter_day_minus_year_quarter_day_cpp <- function(x, y, precision_int, start_int) { .Call(`_clock_year_quarter_day_minus_year_quarter_day_cpp`, x, y, precision_int, start_int) } year_quarter_day_leap_year_cpp <- function(year, start_int) { .Call(`_clock_year_quarter_day_leap_year_cpp`, year, start_int) } clock_rcrd_proxy <- function(x) { .Call(`_clock_clock_rcrd_proxy`, x) } clock_rcrd_names <- function(x) { .Call(`_clock_clock_rcrd_names`, x) } clock_rcrd_set_names <- function(x, names) { .Call(`_clock_clock_rcrd_set_names`, x, names) } sys_time_now_cpp <- function() { .Call(`_clock_sys_time_now_cpp`) } sys_time_info_cpp <- function(fields, precision_int, zone) { .Call(`_clock_sys_time_info_cpp`, fields, precision_int, zone) } new_time_point_from_fields <- function(fields, precision_int, clock_int, names) { .Call(`_clock_new_time_point_from_fields`, fields, precision_int, clock_int, names) } time_point_restore <- function(x, to) { .Call(`_clock_time_point_restore`, x, to) } time_point_parse_cpp <- function(x, format, precision_int, clock_int, month, month_abbrev, weekday, weekday_abbrev, am_pm, mark) { .Call(`_clock_time_point_parse_cpp`, x, format, precision_int, clock_int, month, month_abbrev, weekday, weekday_abbrev, am_pm, mark) } clock_init_utils <- function() { .Call(`_clock_clock_init_utils`) } new_year_week_day_from_fields <- function(fields, precision_int, start, names) { .Call(`_clock_new_year_week_day_from_fields`, fields, precision_int, start, names) } year_week_day_restore <- function(x, to) { .Call(`_clock_year_week_day_restore`, x, to) } format_year_week_day_cpp <- function(fields, precision_int, start_int) { .Call(`_clock_format_year_week_day_cpp`, fields, precision_int, start_int) } invalid_detect_year_week_day_cpp <- function(year, week, start_int) { .Call(`_clock_invalid_detect_year_week_day_cpp`, year, week, start_int) } invalid_any_year_week_day_cpp <- function(year, week, start_int) { .Call(`_clock_invalid_any_year_week_day_cpp`, year, week, start_int) } invalid_count_year_week_day_cpp <- function(year, week, start_int) { .Call(`_clock_invalid_count_year_week_day_cpp`, year, week, start_int) } invalid_resolve_year_week_day_cpp <- function(fields, precision_int, start_int, invalid_string, call) { .Call(`_clock_invalid_resolve_year_week_day_cpp`, fields, precision_int, start_int, invalid_string, call) } get_year_week_day_last_cpp <- function(year, start_int) { .Call(`_clock_get_year_week_day_last_cpp`, year, start_int) } year_week_day_plus_years_cpp <- function(year, start_int, fields_n) { .Call(`_clock_year_week_day_plus_years_cpp`, year, start_int, fields_n) } as_sys_time_year_week_day_cpp <- function(fields, precision_int, start_int) { .Call(`_clock_as_sys_time_year_week_day_cpp`, fields, precision_int, start_int) } as_year_week_day_from_sys_time_cpp <- function(fields, precision_int, start_int) { .Call(`_clock_as_year_week_day_from_sys_time_cpp`, fields, precision_int, start_int) } year_week_day_minus_year_week_day_cpp <- function(x, y, precision_int, start_int) { .Call(`_clock_year_week_day_minus_year_week_day_cpp`, x, y, precision_int, start_int) } year_week_day_leap_year_cpp <- function(year, start_int) { .Call(`_clock_year_week_day_leap_year_cpp`, year, start_int) } weekday_add_days_cpp <- function(x, n_fields) { .Call(`_clock_weekday_add_days_cpp`, x, n_fields) } weekday_minus_weekday_cpp <- function(x, y) { .Call(`_clock_weekday_minus_weekday_cpp`, x, y) } weekday_from_time_point_cpp <- function(x_fields) { .Call(`_clock_weekday_from_time_point_cpp`, x_fields) } format_weekday_cpp <- function(x, labels) { .Call(`_clock_format_weekday_cpp`, x, labels) } zone_is_valid <- function(zone) { .Call(`_clock_zone_is_valid`, zone) } zone_current <- function() { .Call(`_clock_zone_current`) } new_zoned_time_from_fields <- function(fields, precision_int, zone, names) { .Call(`_clock_new_zoned_time_from_fields`, fields, precision_int, zone, names) } zoned_time_restore <- function(x, to) { .Call(`_clock_zoned_time_restore`, x, to) } get_naive_time_cpp <- function(fields, precision_int, zone) { .Call(`_clock_get_naive_time_cpp`, fields, precision_int, zone) } as_zoned_sys_time_from_naive_time_cpp <- function(fields, precision_int, zone, nonexistent_string, ambiguous_string, call) { .Call(`_clock_as_zoned_sys_time_from_naive_time_cpp`, fields, precision_int, zone, nonexistent_string, ambiguous_string, call) } as_zoned_sys_time_from_naive_time_with_reference_cpp <- function(fields, precision_int, zone, nonexistent_string, ambiguous_string, reference_fields, call) { .Call(`_clock_as_zoned_sys_time_from_naive_time_with_reference_cpp`, fields, precision_int, zone, nonexistent_string, ambiguous_string, reference_fields, call) } to_sys_duration_fields_from_sys_seconds_cpp <- function(seconds) { .Call(`_clock_to_sys_duration_fields_from_sys_seconds_cpp`, seconds) } to_sys_seconds_from_sys_duration_fields_cpp <- function(fields) { .Call(`_clock_to_sys_seconds_from_sys_duration_fields_cpp`, fields) } zoned_time_parse_complete_cpp <- function(x, format, precision_int, month, month_abbrev, weekday, weekday_abbrev, am_pm, mark) { .Call(`_clock_zoned_time_parse_complete_cpp`, x, format, precision_int, month, month_abbrev, weekday, weekday_abbrev, am_pm, mark) } zoned_time_parse_abbrev_cpp <- function(x, zone, format, precision_int, month, month_abbrev, weekday, weekday_abbrev, am_pm, mark) { .Call(`_clock_zoned_time_parse_abbrev_cpp`, x, zone, format, precision_int, month, month_abbrev, weekday, weekday_abbrev, am_pm, mark) } clock/R/invalid.R0000644000176200001440000001217114423730227013325 0ustar liggesusers#' Invalid calendar dates #' #' @description #' This family of functions is for working with _invalid_ calendar dates. #' #' Invalid dates represent dates made up of valid individual components, which #' taken as a whole don't represent valid calendar dates. For example, for #' [year_month_day()] the following component ranges are valid: #' `year: [-32767, 32767]`, `month: [1, 12]`, `day: [1, 31]`. #' However, the date `2019-02-31` doesn't exist even though it is made up #' of valid components. This is an example of an invalid date. #' #' Invalid dates are allowed in clock, provided that they are eventually #' resolved by using `invalid_resolve()` or by manually resolving them through #' arithmetic or setter functions. #' #' @details #' Invalid dates must be resolved before converting them to a time point. #' #' It is recommended to use `"previous"` or `"next"` for resolving invalid #' dates, as these ensure that _relative ordering_ among `x` is maintained. #' This is a often a very important property to maintain when doing time series #' data analysis. See the examples for more information. #' #' @inheritParams rlang::args_dots_empty #' #' @param x `[calendar]` #' #' A calendar vector. #' #' @param invalid `[character(1) / NULL]` #' #' One of the following invalid date resolution strategies: #' #' - `"previous"`: The previous valid instant in time. #' #' - `"previous-day"`: The previous valid day in time, keeping the time of #' day. #' #' - `"next"`: The next valid instant in time. #' #' - `"next-day"`: The next valid day in time, keeping the time of day. #' #' - `"overflow"`: Overflow by the number of days that the input is invalid #' by. Time of day is dropped. #' #' - `"overflow-day"`: Overflow by the number of days that the input is #' invalid by. Time of day is kept. #' #' - `"NA"`: Replace invalid dates with `NA`. #' #' - `"error"`: Error on invalid dates. #' #' Using either `"previous"` or `"next"` is generally recommended, as these #' two strategies maintain the _relative ordering_ between elements of the #' input. #' #' If `NULL`, defaults to `"error"`. #' #' If `getOption("clock.strict")` is `TRUE`, `invalid` must be supplied and #' cannot be `NULL`. This is a convenient way to make production code robust #' to invalid dates. #' #' @return #' - `invalid_detect()`: Returns a logical vector detecting invalid dates. #' #' - `invalid_any()`: Returns `TRUE` if any invalid dates are detected. #' #' - `invalid_count()`: Returns a single integer containing the number of #' invalid dates. #' #' - `invalid_remove()`: Returns `x` with invalid dates removed. #' #' - `invalid_resolve()`: Returns `x` with invalid dates resolved using the #' `invalid` strategy. #' #' @name clock-invalid #' @examples #' # Invalid date #' x <- year_month_day(2019, 04, 30:31, c(3, 2), 30, 00) #' x #' #' invalid_detect(x) #' #' # Previous valid moment in time #' x_previous <- invalid_resolve(x, invalid = "previous") #' x_previous #' #' # Previous valid day, retaining time of day #' x_previous_day <- invalid_resolve(x, invalid = "previous-day") #' x_previous_day #' #' # Note that `"previous"` retains the relative ordering in `x` #' x[1] < x[2] #' x_previous[1] < x_previous[2] #' #' # But `"previous-day"` here does not! #' x_previous_day[1] < x_previous_day[2] #' #' # Remove invalid dates entirely #' invalid_remove(x) #' #' y <- year_quarter_day(2019, 1, 90:92) #' y #' #' # Overflow rolls forward by the number of days between `y` and the previous #' # valid date #' invalid_resolve(y, invalid = "overflow") NULL # ------------------------------------------------------------------------------ #' @rdname clock-invalid #' @export invalid_detect <- function(x) { UseMethod("invalid_detect") } #' @export invalid_detect.default <- function(x) { stop_clock_unsupported(x) } # ------------------------------------------------------------------------------ #' @rdname clock-invalid #' @export invalid_any <- function(x) { UseMethod("invalid_any") } #' @export invalid_any.default <- function(x) { stop_clock_unsupported(x) } # ------------------------------------------------------------------------------ #' @rdname clock-invalid #' @export invalid_count <- function(x) { UseMethod("invalid_count") } #' @export invalid_count.default <- function(x) { stop_clock_unsupported(x) } # ------------------------------------------------------------------------------ #' @rdname clock-invalid #' @export invalid_remove <- function(x) { UseMethod("invalid_remove") } #' @export invalid_remove.default <- function(x) { stop_clock_unsupported(x) } #' @export invalid_remove.clock_calendar <- function(x) { x[!invalid_detect(x)] } # ------------------------------------------------------------------------------ #' @rdname clock-invalid #' @export invalid_resolve <- function(x, ..., invalid = NULL) { UseMethod("invalid_resolve") } #' @export invalid_resolve.default <- function(x, ..., invalid = NULL) { stop_clock_unsupported(x) } validate_invalid <- function(invalid) { invalid <- strict_validate_invalid(invalid) if (!is_string(invalid)) { abort("`invalid` must be a character vector with length 1.") } invalid } clock/R/date.R0000644000176200001440000016264514427270231012626 0ustar liggesusers#' @export as_sys_time.Date <- function(x, ...) { check_dots_empty0(...) names <- names(x) x <- unstructure(x) if (is.double(x)) { x <- floor(x) } x <- duration_days(x) new_sys_time_from_fields(x, PRECISION_DAY, names) } #' @export as_naive_time.Date <- function(x, ...) { check_dots_empty0(...) as_naive_time(as_sys_time(x)) } #' Convert to a zoned-time from a date #' #' @description #' This is a Date method for the [as_zoned_time()] generic. #' #' clock assumes that Dates are _naive_ date-time types. Like naive-times, they #' have a yet-to-be-specified time zone. This method allows you to specify that #' time zone, keeping the printed time. If possible, the time will be set to #' midnight (see Details for the rare case in which this is not possible). #' #' @details #' In the rare instance that the specified time zone does not contain a #' date-time at midnight due to daylight saving time, `nonexistent` can be used #' to resolve the issue. Similarly, if there are two possible midnight times due #' to a daylight saving time fallback, `ambiguous` can be used. #' #' @inheritParams rlang::args_dots_empty #' @inheritParams as-zoned-time-naive-time #' #' @param x `[Date]` #' #' A Date. #' #' @return A zoned-time. #' #' @name as-zoned-time-Date #' @export #' @examples #' x <- as.Date("2019-01-01") #' #' # The resulting zoned-times have the same printed time, but are in #' # different time zones #' as_zoned_time(x, "UTC") #' as_zoned_time(x, "America/New_York") #' #' # Converting Date -> zoned-time is the same as naive-time -> zoned-time #' x <- as_naive_time(year_month_day(2019, 1, 1)) #' as_zoned_time(x, "America/New_York") #' #' # In Asia/Beirut, there was a DST gap from #' # 2021-03-27 23:59:59 -> 2021-03-28 01:00:00, #' # skipping the 0th hour entirely. This means there is no midnight value. #' x <- as.Date("2021-03-28") #' try(as_zoned_time(x, "Asia/Beirut")) #' #' # To resolve this, set a `nonexistent` time resolution strategy #' as_zoned_time(x, "Asia/Beirut", nonexistent = "roll-forward") as_zoned_time.Date <- function(x, zone, ..., nonexistent = NULL, ambiguous = NULL) { check_dots_empty0(...) x <- as_naive_time(x) as_zoned_time(x, zone = zone, nonexistent = nonexistent, ambiguous = ambiguous) } #' @export as_year_month_day.Date <- function(x, ...) { check_dots_empty0(...) as_year_month_day(as_naive_time(x)) } #' @export as_year_month_weekday.Date <- function(x, ...) { check_dots_empty0(...) as_year_month_weekday(as_naive_time(x)) } #' @export as_year_quarter_day.Date <- function(x, ..., start = NULL) { check_dots_empty0(...) as_year_quarter_day(as_naive_time(x), start = start) } #' @export as_year_week_day.Date <- function(x, ..., start = NULL) { check_dots_empty0(...) as_year_week_day(as_naive_time(x), start = start) } #' @export as_iso_year_week_day.Date <- function(x, ...) { check_dots_empty0(...) as_iso_year_week_day(as_naive_time(x)) } #' @export as_year_day.Date <- function(x, ...) { check_dots_empty0(...) as_year_day(as_naive_time(x)) } #' @export as_weekday.Date <- function(x, ...) { check_dots_empty0(...) as_weekday(as_naive_time(x)) } # ------------------------------------------------------------------------------ # Not using `check_dots_empty()` because that might # be too aggressive with base generics #' @export as.Date.clock_calendar <- function(x, ...) { as.Date(as_naive_time(x)) } #' @export as.Date.clock_time_point <- function(x, ...) { names <- clock_rcrd_names(x) x <- time_point_floor(x, "day") x <- as_duration(x) x <- as.double(x) names(x) <- names new_date(x) } #' @export as.Date.clock_zoned_time <- function(x, ...) { as.Date(as_naive_time(x)) } # ------------------------------------------------------------------------------ #' Convert to a date #' #' @description #' `as_date()` is a generic function that converts its input to a date (Date). #' #' There are methods for converting date-times (POSIXct), calendars, #' time points, and zoned-times to dates. #' #' For converting to a date-time, see [as_date_time()]. #' #' @details #' Note that clock always assumes that R's Date class is naive, so converting #' a POSIXct to a Date will always retain the printed year, month, and day #' value. #' #' This is not a drop-in replacement for `as.Date()`, as it only converts a #' limited set of types to Date. For parsing characters as dates, see #' [date_parse()]. For converting numerics to dates, see [vctrs::new_date()] or #' continue to use `as.Date()`. #' #' @inheritParams rlang::args_dots_empty #' #' @param x `[vector]` #' #' A vector. #' #' @return A date with the same length as `x`. #' #' @export #' @examples #' x <- date_time_parse("2019-01-01 23:02:03", "America/New_York") #' #' # R's `as.Date.POSIXct()` method defaults to changing the printed time #' # to UTC before converting, which can result in odd conversions like this: #' as.Date(x) #' #' # `as_date()` will never change the printed time before converting #' as_date(x) #' #' # Can also convert from other clock types #' as_date(year_month_day(2019, 2, 5)) as_date <- function(x, ...) { UseMethod("as_date") } #' @rdname as_date #' @export as_date.Date <- function(x, ...) { check_dots_empty0(...) date_standardize(x) } #' @rdname as_date #' @export as_date.POSIXt <- function(x, ...) { check_dots_empty0(...) as.Date(as_naive_time(x)) } #' @rdname as_date #' @export as_date.clock_calendar <- function(x, ...) { check_dots_empty0(...) as.Date(x) } #' @rdname as_date #' @export as_date.clock_time_point <- function(x, ...) { check_dots_empty0(...) as.Date(x) } #' @rdname as_date #' @export as_date.clock_zoned_time <- function(x, ...) { check_dots_empty0(...) as.Date(x) } # ------------------------------------------------------------------------------ #' Getters: date #' #' @description #' These are Date methods for the [getter generics][clock-getters]. #' #' - `get_year()` returns the Gregorian year. #' #' - `get_month()` returns the month of the year. #' #' - `get_day()` returns the day of the month. #' #' For more advanced component extraction, convert to the calendar type #' that you are interested in. #' #' @param x `[Date]` #' #' A Date to get the component from. #' #' @return The component. #' #' @name Date-getters #' @examples #' x <- as.Date("2019-01-01") + 0:5 #' get_day(x) NULL #' @rdname Date-getters #' @export get_year.Date <- function(x) { get_date_field_year_month_day(x, get_year) } #' @rdname Date-getters #' @export get_month.Date <- function(x) { get_date_field_year_month_day(x, get_month) } #' @rdname Date-getters #' @export get_day.Date <- function(x) { get_date_field_year_month_day(x, get_day) } get_date_field_year_month_day <- function(x, get_fn) { x <- as_year_month_day(x) get_fn(x) } # ------------------------------------------------------------------------------ #' Setters: date #' #' @description #' These are Date methods for the [setter generics][clock-setters]. #' #' - `set_year()` sets the year. #' #' - `set_month()` sets the month of the year. Valid values are in the range #' of `[1, 12]`. #' #' - `set_day()` sets the day of the month. Valid values are in the range #' of `[1, 31]`. #' #' @inheritParams rlang::args_dots_empty #' @inheritParams invalid_resolve #' #' @param x `[Date]` #' #' A Date vector. #' #' @param value `[integer / "last"]` #' #' The value to set the component to. #' #' For `set_day()`, this can also be `"last"` to set the day to the #' last day of the month. #' #' @return `x` with the component set. #' #' @name Date-setters #' @examples #' x <- as.Date("2019-02-01") #' #' # Set the day #' set_day(x, 12:14) #' #' # Set to the "last" day of the month #' set_day(x, "last") #' #' # You cannot set a Date to an invalid day like you can with #' # a year-month-day. Instead, the default strategy is to error. #' try(set_day(x, 31)) #' set_day(as_year_month_day(x), 31) #' #' # You can resolve these issues while setting the day by specifying #' # an invalid date resolution strategy with `invalid` #' set_day(x, 31, invalid = "previous") NULL #' @rdname Date-setters #' @export set_year.Date <- function(x, value, ..., invalid = NULL) { check_dots_empty0(...) set_date_field_year_month_day(x, value, invalid, set_year) } #' @rdname Date-setters #' @export set_month.Date <- function(x, value, ..., invalid = NULL) { check_dots_empty0(...) set_date_field_year_month_day(x, value, invalid, set_month) } #' @rdname Date-setters #' @export set_day.Date <- function(x, value, ..., invalid = NULL) { check_dots_empty0(...) set_date_field_year_month_day(x, value, invalid, set_day) } set_date_field_year_month_day <- function(x, value, invalid, set_fn) { x <- as_year_month_day(x) x <- set_fn(x, value) x <- invalid_resolve(x, invalid = invalid) as.Date(x) } # ------------------------------------------------------------------------------ #' @method vec_arith.Date clock_duration #' @export vec_arith.Date.clock_duration <- function(op, x, y, ...) { arith_date_and_duration(op, x, y, ...) } #' @method vec_arith.clock_duration Date #' @export vec_arith.clock_duration.Date <- function(op, x, y, ...) { arith_duration_and_date(op, x, y, ...) } arith_date_and_duration <- function(op, x, y, ...) { switch( op, "+" = add_duration(x, y), "-" = add_duration(x, -y), stop_incompatible_op(op, x, y, ...) ) } arith_duration_and_date <- function(op, x, y, ...) { switch( op, "+" = add_duration(y, x, swapped = TRUE), "-" = stop_incompatible_op(op, x, y, details = "Can't subtract a Date from a duration.", ...), stop_incompatible_op(op, x, y, ...) ) } # ------------------------------------------------------------------------------ # @export - .onLoad() slider_plus.Date.clock_duration <- function(x, y) { vec_arith("+", x, y) } # @export - .onLoad() slider_minus.Date.clock_duration <- function(x, y) { vec_arith("-", x, y) } # ------------------------------------------------------------------------------ #' Arithmetic: date #' #' @description #' These are Date methods for the #' [arithmetic generics][clock-arithmetic]. #' #' Calendrical based arithmetic: #' #' These functions convert to a year-month-day calendar, perform #' the arithmetic, then convert back to a Date. #' #' - `add_years()` #' #' - `add_quarters()` #' #' - `add_months()` #' #' Time point based arithmetic: #' #' These functions convert to a time point, perform the arithmetic, then #' convert back to a Date. #' #' - `add_weeks()` #' #' - `add_days()` #' #' @details #' Adding a single quarter with `add_quarters()` is equivalent to adding #' 3 months. #' #' `x` and `n` are recycled against each other using #' [tidyverse recycling rules][vctrs::vector_recycling_rules]. #' #' Only calendrical based arithmetic has the potential to generate invalid #' dates. Time point based arithmetic, like adding days, will always generate #' a valid date. #' #' @inheritParams clock-arithmetic #' @inheritParams invalid_resolve #' #' @param x `[Date]` #' #' A Date vector. #' #' @return `x` after performing the arithmetic. #' #' @name Date-arithmetic #' #' @examples #' x <- as.Date("2019-01-01") #' #' add_years(x, 1:5) #' #' y <- as.Date("2019-01-31") #' #' # Adding 1 month to `y` generates an invalid date. Unlike year-month-day #' # types, R's native Date type cannot handle invalid dates, so you must #' # resolve them immediately. If you don't you get an error: #' try(add_months(y, 1:2)) #' add_months(as_year_month_day(y), 1:2) #' #' # Resolve invalid dates by specifying an invalid date resolution strategy #' # with the `invalid` argument. Using `"previous"` here sets the date to #' # the previous valid date - i.e. the end of the month. #' add_months(y, 1:2, invalid = "previous") NULL #' @rdname Date-arithmetic #' @export add_years.Date <- function(x, n, ..., invalid = NULL) { check_dots_empty0(...) add_date_duration_year_month_day(x, n, invalid, add_years) } #' @rdname Date-arithmetic #' @export add_quarters.Date <- function(x, n, ..., invalid = NULL) { check_dots_empty0(...) add_date_duration_year_month_day(x, n, invalid, add_quarters) } #' @rdname Date-arithmetic #' @export add_months.Date <- function(x, n, ..., invalid = NULL) { check_dots_empty0(...) add_date_duration_year_month_day(x, n, invalid, add_months) } add_date_duration_year_month_day <- function(x, n, invalid, add_fn) { x <- as_year_month_day(x) x <- add_fn(x, n) x <- invalid_resolve(x, invalid = invalid) as.Date(x) } #' @rdname Date-arithmetic #' @export add_weeks.Date <- function(x, n, ...) { check_dots_empty0(...) add_date_duration_time_point(x, n, add_weeks) } #' @rdname Date-arithmetic #' @export add_days.Date <- function(x, n, ...) { check_dots_empty0(...) add_date_duration_time_point(x, n, add_days) } add_date_duration_time_point <- function(x, n, add_fn) { x <- as_naive_time(x) x <- add_fn(x, n) as.Date(x) } # ------------------------------------------------------------------------------ #' Group date and date-time components #' #' @description #' `date_group()` groups by a single component of a date-time, such as month #' of the year, or day of the month. #' #' There are separate help pages for grouping dates and date-times: #' #' - [dates (Date)][date-group] #' #' - [date-times (POSIXct/POSIXlt)][posixt-group] #' #' @inheritParams calendar_group #' #' @param x `[Date / POSIXct / POSIXlt]` #' #' A date or date-time vector. #' #' @param precision `[character(1)]` #' #' A precision. Allowed precisions are dependent on the input used. #' #' @return `x`, grouped at `precision`. #' #' @export #' @examples #' # See type specific documentation for more examples #' date_group(as.Date("2019-01-01") + 0:5, "day", n = 2) date_group <- function(x, precision, ..., n = 1L) { check_date_or_date_time(x) UseMethod("date_group") } #' Group date components #' #' @description #' This is a Date method for the [date_group()] generic. #' #' `date_group()` groups by a single component of a Date, such as month #' of the year, or day of the month. #' #' If you need to group by more complex components, like ISO weeks, or quarters, #' convert to a calendar type that contains the component you are interested #' in grouping by. #' #' @inheritParams date_group #' @inheritParams invalid_resolve #' #' @param x `[Date]` #' #' A date vector. #' #' @param precision `[character(1)]` #' #' One of: #' #' - `"year"` #' #' - `"month"` #' #' - `"day"` #' #' @return `x`, grouped at `precision`. #' #' @name date-group #' #' @export #' @examples #' x <- as.Date("2019-01-01") + -3:5 #' x #' #' # Group by 2 days of the current month. #' # Note that this resets at the beginning of the month, creating day groups #' # of [29, 30] [31] [01, 02] [03, 04]. #' date_group(x, "day", n = 2) #' #' # Group by month #' date_group(x, "month") date_group.Date <- function(x, precision, ..., n = 1L, invalid = NULL) { check_dots_empty0(...) x <- as_year_month_day(x) x <- calendar_group(x, precision, n = n) x <- calendar_widen(x, "day") as.Date(x, invalid = invalid) } # ------------------------------------------------------------------------------ #' Is the year a leap year? #' #' `date_leap_year()` detects if the year is a leap year. #' #' @param x `[Date / POSIXct / POSIXlt]` #' #' A date or date-time to detect leap years in. #' #' @return A logical vector the same size as `x`. Returns `TRUE` if in a leap #' year, `FALSE` if not in a leap year, and `NA` if `x` is `NA`. #' #' @examples #' x <- as.Date("2019-01-01") #' x <- add_years(x, 0:5) #' date_leap_year(x) #' #' y <- as.POSIXct("2019-01-01", "America/New_York") #' y <- add_years(y, 0:5) #' date_leap_year(y) #' @export date_leap_year <- function(x) { check_date_or_date_time(x) UseMethod("date_leap_year") } #' @export date_leap_year.Date <- function(x) { x <- as_year_month_day(x) calendar_leap_year(x) } # ------------------------------------------------------------------------------ #' Date and date-time rounding #' #' @description #' - `date_floor()` rounds a date or date-time down to a multiple of #' the specified `precision`. #' #' - `date_ceiling()` rounds a date or date-time up to a multiple of #' the specified `precision`. #' #' - `date_round()` rounds up or down depending on what is closer, #' rounding up on ties. #' #' There are separate help pages for rounding dates and date-times: #' #' - [dates (Date)][date-rounding] #' #' - [date-times (POSIXct/POSIXlt)][posixt-rounding] #' #' These functions round the underlying duration itself, relative to an #' `origin`. For example, rounding to 15 hours will construct groups of #' 15 hours, starting from `origin`, which defaults to a naive time of #' 1970-01-01 00:00:00. #' #' If you want to group by components, such as "day of the month", see #' [date_group()]. #' #' @inheritParams date_group #' #' @param origin `[Date(1) / POSIXct(1) / POSIXlt(1) / NULL]` #' #' An origin to start counting from. The default `origin` is #' midnight on 1970-01-01 in the time zone of `x`. #' #' @return `x` rounded to the specified `precision`. #' #' @name date-and-date-time-rounding #' #' @examples #' # See the type specific documentation for more examples #' #' x <- as.Date("2019-03-31") + 0:5 #' x #' #' # Flooring by 2 days, note that this is not tied to the current month, #' # and instead counts from the specified `origin`. #' date_floor(x, "day", n = 2) NULL #' @rdname date-and-date-time-rounding #' @export date_floor <- function(x, precision, ..., n = 1L, origin = NULL) { check_date_or_date_time(x) UseMethod("date_floor") } #' @rdname date-and-date-time-rounding #' @export date_ceiling <- function(x, precision, ..., n = 1L, origin = NULL) { check_date_or_date_time(x) UseMethod("date_ceiling") } #' @rdname date-and-date-time-rounding #' @export date_round <- function(x, precision, ..., n = 1L, origin = NULL) { check_date_or_date_time(x) UseMethod("date_round") } #' Rounding: date #' #' @description #' These are Date methods for the #' [rounding generics][date-and-date-time-rounding]. #' #' - `date_floor()` rounds a date down to a multiple of #' the specified `precision`. #' #' - `date_ceiling()` rounds a date up to a multiple of #' the specified `precision`. #' #' - `date_round()` rounds up or down depending on what is closer, #' rounding up on ties. #' #' The only supported rounding `precision`s for Dates are `"day"` and `"week"`. #' You can group by irregular periods such as `"month"` or `"year"` by using #' [date_group()]. #' #' @details #' When rounding by `"week"`, remember that the `origin` determines the "week #' start". By default, 1970-01-01 is the implicit origin, which is a #' Thursday. If you would like to round by weeks with a different week start, #' just supply an origin on the weekday you are interested in. #' #' @inheritParams date_floor #' #' @param x `[Date]` #' #' A date vector. #' #' @param precision `[character(1)]` #' #' One of: #' #' - `"week"` #' #' - `"day"` #' #' `"week"` is an alias for `"day"` with `n * 7`. #' #' @param origin `[Date(1) / NULL]` #' #' An origin to start counting from. The default `origin` is #' 1970-01-01. #' #' @return `x` rounded to the specified `precision`. #' #' @name date-rounding #' #' @examples #' x <- as.Date("2019-03-31") + 0:5 #' x #' #' # Flooring by 2 days, note that this is not tied to the current month, #' # and instead counts from the specified `origin`, so groups can cross #' # the month boundary #' date_floor(x, "day", n = 2) #' #' # Compare to `date_group()`, which groups by the day of the month #' date_group(x, "day", n = 2) #' #' y <- as.Date("2019-01-01") + 0:20 #' y #' #' # Flooring by week uses an implicit `origin` of 1970-01-01, which #' # is a Thursday #' date_floor(y, "week") #' as_weekday(date_floor(y, "week")) #' #' # If you want to round by weeks with a different week start, supply an #' # `origin` that falls on the weekday you care about. This uses a Monday. #' origin <- as.Date("1970-01-05") #' as_weekday(origin) #' #' date_floor(y, "week", origin = origin) #' as_weekday(date_floor(y, "week", origin = origin)) NULL #' @rdname date-rounding #' @export date_floor.Date <- function(x, precision, ..., n = 1L, origin = NULL) { check_dots_empty0(...) date_rounder(x, precision, n, origin, time_point_floor) } #' @rdname date-rounding #' @export date_ceiling.Date <- function(x, precision, ..., n = 1L, origin = NULL) { check_dots_empty0(...) date_rounder(x, precision, n, origin, time_point_ceiling) } #' @rdname date-rounding #' @export date_round.Date <- function(x, precision, ..., n = 1L, origin = NULL) { check_dots_empty0(...) date_rounder(x, precision, n, origin, time_point_round) } date_rounder <- function(x, precision, n, origin, time_point_rounder, ..., error_call = caller_env()) { check_dots_empty0(...) result <- tweak_date_rounder_precision(precision, n) precision <- result$precision n <- result$n x <- as_naive_time(x) if (!is_null(origin)) { origin <- collect_date_rounder_origin(origin, error_call = error_call) } x <- time_point_rounder(x, precision, n = n, origin = origin) as.Date(x) } # Note: # For Date and POSIXct, which are always day and second precision, we can # allow a special "week" precision for the rounding functions. This isn't # normally allowed for time points, as there is no week precision time point, # and instead you'd do `day`,` `n = n * 7`. This makes that a little easier. tweak_date_rounder_precision <- function(precision, n) { if (identical(precision, "week")) { precision <- "day" n <- n * 7L } list(precision = precision, n = n) } collect_date_rounder_origin <- function(origin, error_call) { check_date(origin, call = error_call) vec_check_size(origin, 1L, call = error_call) check_no_missing(origin, call = error_call) if (is.infinite(origin)) { cli::cli_abort("{.arg origin} can't be an infinite date.", call = error_call) } origin <- as_naive_time(origin) origin } # ------------------------------------------------------------------------------ #' Convert a date or date-time to a weekday factor #' #' `date_weekday_factor()` converts a date or date-time to an ordered factor #' with levels representing the weekday. This can be useful in combination with #' ggplot2, or for modeling. #' #' @inheritParams weekday_factor #' #' @param x `[Date / POSIXct / POSIXlt]` #' #' A date or date-time vector. #' #' @return An ordered factor representing the weekdays. #' #' @export #' @examples #' x <- as.Date("2019-01-01") + 0:6 #' #' # Default to Sunday -> Saturday #' date_weekday_factor(x) #' #' # ISO encoding is Monday -> Sunday #' date_weekday_factor(x, encoding = "iso") #' #' # With full names #' date_weekday_factor(x, abbreviate = FALSE) #' #' # Or a different language #' date_weekday_factor(x, labels = "fr") date_weekday_factor <- function(x, ..., labels = "en", abbreviate = TRUE, encoding = "western") { check_dots_empty0(...) check_date_or_date_time(x) x <- as_weekday(x) weekday_factor(x, labels = labels, abbreviate = abbreviate, encoding = encoding) } # ------------------------------------------------------------------------------ #' Convert a date or date-time to an ordered factor of month names #' #' @description #' `date_month_factor()` extracts the month values from a date or date-time and #' converts them to an ordered factor of month names. This can be useful in #' combination with ggplot2, or for modeling. #' #' @inheritParams calendar_month_factor #' #' @param x `[Date / POSIXct / POSIXlt]` #' #' A date or date-time vector. #' #' @return An ordered factor representing the months. #' #' @export #' @examples #' x <- add_months(as.Date("2019-01-01"), 0:11) #' #' date_month_factor(x) #' date_month_factor(x, abbreviate = TRUE) #' date_month_factor(x, labels = "fr") date_month_factor <- function(x, ..., labels = "en", abbreviate = FALSE) { check_dots_empty0(...) check_date_or_date_time(x) x <- as_year_month_day(x) calendar_month_factor(x, labels = labels, abbreviate = abbreviate) } # ------------------------------------------------------------------------------ #' Formatting: date and date-time #' #' @description #' `date_format()` formats a date (Date) or date-time (POSIXct/POSIXlt) using #' a `format` string. #' #' There are separate help pages for formatting dates and date-times: #' #' - [dates (Date)][date-formatting] #' #' - [date-times (POSIXct/POSIXlt)][posixt-formatting] #' #' @inheritParams rlang::args_dots_empty #' #' @param x `[Date / POSIXct / POSIXlt]` #' #' A date or date-time vector. #' #' @return A character vector of the formatted input. #' #' @export #' @examples #' # See method specific documentation for more examples #' #' x <- as.Date("2019-01-01") #' date_format(x, format = "year: %Y, month: %m, day: %d") date_format <- function(x, ...) { check_date_or_date_time(x) UseMethod("date_format") } #' Formatting: date #' #' @description #' This is a Date method for the [date_format()] generic. #' #' `date_format()` formats a date (Date) using a `format` string. #' #' If `format` is `NULL`, a default format of `"%Y-%m-%d"` is used. #' #' @details #' Because a Date is considered to be a _naive_ type in clock, meaning that #' it currently has no implied time zone, using the `%z` or `%Z` format commands #' is not allowed and will result in `NA`. #' #' @inheritParams rlang::args_dots_empty #' @inheritParams format.clock_zoned_time #' #' @param x `[Date]` #' #' A date vector. #' #' @return A character vector of the formatted input. #' #' @name date-formatting #' #' @export #' @examples #' x <- as.Date("2019-01-01") #' #' # Default #' date_format(x) #' #' date_format(x, format = "year: %Y, month: %m, day: %d") #' #' # With different locales #' date_format(x, format = "%A, %B %d, %Y") #' date_format(x, format = "%A, %B %d, %Y", locale = clock_locale("fr")) date_format.Date <- function(x, ..., format = NULL, locale = clock_locale()) { check_dots_empty0(...) x <- as_naive_time(x) format(x, format = format, locale = locale) } # ------------------------------------------------------------------------------ #' Parsing: date #' #' @description #' `date_parse()` parses strings into a Date. #' #' The default `format` used is `"%Y-%m-%d"`. This matches the default #' result from calling `print()` or `format()` on a Date. #' #' @details #' _`date_parse()` ignores both the `%z` and `%Z` commands,_ as clock treats #' Date as a _naive_ type, with a yet-to-be-specified time zone. #' #' Parsing strings with sub-daily components, such as hours, minutes, or #' seconds, should generally be done with [date_time_parse()]. If you only #' need the date components from a string with sub-daily components, choose #' one of the following: #' #' - If the date components are at the front of the string, and you don't want #' the time components to affect the date in any way, you can use #' [date_parse()] to parse only the date components. For example, #' `date_parse("2019-01-05 00:01:02", format = "%Y-%m-%d")` will parse #' through `05` and then stop. #' #' - If you want the time components to influence the date, then parse the full #' string with [date_time_parse()], round to day precision with a #' rounding function like [date_round()], and cast to date with [as_date()]. #' #' Attempting to directly parse all components of a sub-daily string into a #' Date is ambiguous and undefined, and is unlikely to work as you might expect. #' For example, `date_parse("2019-01-05 00:01:02", format = #' "%Y-%m-%d %H:%M:%S")` is not officially supported, even if it works in #' some cases. #' #' @inheritParams zoned-parsing #' #' @return A Date. #' #' @export #' @examples #' date_parse("2020-01-01") #' #' date_parse( #' "January 5, 2020", #' format = "%B %d, %Y" #' ) #' #' # With a different locale #' date_parse( #' "janvier 5, 2020", #' format = "%B %d, %Y", #' locale = clock_locale("fr") #' ) #' #' # A neat feature of `date_parse()` is the ability to parse #' # the ISO year-week-day format #' date_parse("2020-W01-2", format = "%G-W%V-%u") #' #' # --------------------------------------------------------------------------- #' # Sub-daily components #' #' # If you have a string with sub-daily components, but only require the date, #' # first parse them as date-times to fully parse the sub-daily components, #' # then round using whatever convention is required for your use case before #' # converting to date. #' x <- c("2019-01-01 11", "2019-01-01 12") #' #' x <- date_time_parse(x, zone = "UTC", format = "%Y-%m-%d %H") #' x #' #' date_floor(x, "day") #' date_round(x, "day") #' #' as_date(date_round(x, "day")) date_parse <- function(x, ..., format = NULL, locale = clock_locale()) { check_dots_empty0(...) x <- naive_time_parse(x, format = format, precision = "day", locale = locale) as.Date(x) } # ------------------------------------------------------------------------------ #' Shifting: date and date-time #' #' @description #' `date_shift()` shifts `x` to the `target` weekday. You can shift to the next #' or previous weekday. If `x` is currently on the `target` weekday, you can #' choose to leave it alone or advance it to the next instance of the `target`. #' #' There are separate help pages for shifting dates and date-times: #' #' - [dates (Date)][date-shifting] #' #' - [date-times (POSIXct/POSIXlt)][posixt-shifting] #' #' @inheritParams time_point_shift #' #' @param x `[Date / POSIXct / POSIXlt]` #' #' A date or date-time vector. #' #' @return `x` shifted to the `target` weekday. #' #' @name date-and-date-time-shifting #' #' @export #' @examples #' # See the type specific documentation for more examples #' #' x <- as.Date("2019-01-01") + 0:1 #' #' # A Tuesday and Wednesday #' as_weekday(x) #' #' monday <- weekday(clock_weekdays$monday) #' #' # Shift to the next Monday #' date_shift(x, monday) date_shift <- function(x, target, ..., which = "next", boundary = "keep") { check_date_or_date_time(x) UseMethod("date_shift") } #' Shifting: date #' #' @description #' `date_shift()` shifts `x` to the `target` weekday. You can shift to the next #' or previous weekday. If `x` is currently on the `target` weekday, you can #' choose to leave it alone or advance it to the next instance of the `target`. #' #' Weekday shifting is one of the easiest ways to floor by week while #' controlling what is considered the first day of the week. You can also #' accomplish this with the `origin` argument of [date_floor()], but this is #' slightly easier. #' #' @inheritParams time_point_shift #' #' @param x `[Date]` #' #' A date vector. #' #' @return `x` shifted to the `target` weekday. #' #' @name date-shifting #' #' @export #' @examples #' x <- as.Date("2019-01-01") + 0:1 #' #' # A Tuesday and Wednesday #' as_weekday(x) #' #' monday <- weekday(clock_weekdays$monday) #' #' # Shift to the next Monday #' date_shift(x, monday) #' #' # Shift to the previous Monday #' # This is an easy way to "floor by week" with a target weekday in mind #' date_shift(x, monday, which = "previous") #' #' # What about Tuesday? #' tuesday <- weekday(clock_weekdays$tuesday) #' #' # Notice that the day that was currently on a Tuesday was not shifted #' date_shift(x, tuesday) #' #' # You can force it to `"advance"` #' date_shift(x, tuesday, boundary = "advance") date_shift.Date <- function(x, target, ..., which = "next", boundary = "keep") { check_dots_empty0(...) x <- as_naive_time(x) x <- time_point_shift(x, target, which = which, boundary = boundary) as.Date(x) } # ------------------------------------------------------------------------------ #' Building: date #' #' @description #' `date_build()` builds a Date from it's individual components. #' #' @details #' Components are recycled against each other using #' [tidyverse recycling rules][vctrs::vector_recycling_rules]. #' #' @inheritParams invalid_resolve #' #' @param year `[integer]` #' #' The year. Values `[-32767, 32767]` are generally allowed. #' #' @param month `[integer]` #' #' The month. Values `[1, 12]` are allowed. #' #' @param day `[integer / "last"]` #' #' The day of the month. Values `[1, 31]` are allowed. #' #' If `"last"`, then the last day of the month is returned. #' #' @return A Date. #' #' @export #' @examples #' date_build(2019) #' date_build(2019, 1:3) #' #' # Generating invalid dates will trigger an error #' try(date_build(2019, 1:12, 31)) #' #' # You can resolve this with `invalid` #' date_build(2019, 1:12, 31, invalid = "previous") #' #' # But this particular case (the last day of the month) is better #' # specified as: #' date_build(2019, 1:12, "last") date_build <- function(year, month = 1L, day = 1L, ..., invalid = NULL) { check_dots_empty0(...) x <- year_month_day(year, month, day) x <- invalid_resolve(x, invalid = invalid) as.Date(x) } # ------------------------------------------------------------------------------ #' Current date and date-time #' #' @description #' - `date_today()` returns the current date in the specified `zone` as a Date. #' #' - `date_now()` returns the current date-time in the specified `zone` as a #' POSIXct. #' #' @details #' clock assumes that Date is a _naive_ type, like naive-time. This means that #' `date_today()` first looks up the current date-time in the specified `zone`, #' then converts that to a Date, retaining the printed time while dropping any #' information about that time zone. #' #' @inheritParams zoned_time_now #' #' @return #' - `date_today()` a single Date. #' #' - `date_now()` a single POSIXct. #' #' @name date-today #' #' @examples #' # Current date in the local time zone #' date_today("") #' #' # Current date in a specified time zone #' date_today("Europe/London") #' #' # Current date-time in that same time zone #' date_now("Europe/London") NULL #' @rdname date-today #' @export date_today <- function(zone) { as.Date(zoned_time_now(zone)) } # ------------------------------------------------------------------------------ #' Boundaries: date and date-time #' #' @description #' - `date_start()` computes the date at the start of a particular #' `precision`, such as the "start of the year". #' #' - `date_end()` computes the date at the end of a particular #' `precision`, such as the "end of the month". #' #' There are separate help pages for computing boundaries for dates and #' date-times: #' #' - [dates (Date)][date-boundary] #' #' - [date-times (POSIXct/POSIXlt)][posixt-boundary] #' #' @inheritParams date_group #' #' @param x `[Date / POSIXct / POSIXlt]` #' #' A date or date-time vector. #' #' @param precision `[character(1)]` #' #' A precision. Allowed precisions are dependent on the input used. #' #' @return `x` but with some components altered to be at the boundary value. #' #' @name date-and-date-time-boundary #' #' @examples #' # See type specific documentation for more examples #' #' x <- date_build(2019, 2:4) #' #' date_end(x, "month") #' #' x <- date_time_build(2019, 2:4, 3:5, 4, 5, zone = "America/New_York") #' #' # Note that the hour, minute, and second components are also adjusted #' date_end(x, "month") NULL #' @rdname date-and-date-time-boundary #' @export date_start <- function(x, precision, ...) { check_date_or_date_time(x) UseMethod("date_start") } #' @rdname date-and-date-time-boundary #' @export date_end <- function(x, precision, ...) { check_date_or_date_time(x) UseMethod("date_end") } #' Boundaries: date #' #' @description #' This is a Date method for the [date_start()] and [date_end()] generics. #' #' @inheritParams date_group #' @inheritParams invalid_resolve #' #' @param x `[Date]` #' #' A date vector. #' #' @param precision `[character(1)]` #' #' One of: #' #' - `"year"` #' #' - `"month"` #' #' - `"day"` #' #' @return `x` but with some components altered to be at the boundary value. #' #' @name date-boundary #' #' @examples #' x <- date_build(2019:2021, 2:4, 3:5) #' x #' #' # Last day of the month #' date_end(x, "month") #' #' # Last day of the year #' date_end(x, "year") #' #' # First day of the year #' date_start(x, "year") NULL #' @rdname date-boundary #' @export date_start.Date <- function(x, precision, ..., invalid = NULL) { check_dots_empty0(...) x <- as_year_month_day(x) x <- calendar_start(x, precision) as.Date(x, invalid = invalid) } #' @rdname date-boundary #' @export date_end.Date <- function(x, precision, ..., invalid = NULL) { check_dots_empty0(...) x <- as_year_month_day(x) x <- calendar_end(x, precision) as.Date(x, invalid = invalid) } # ------------------------------------------------------------------------------ #' Sequences: date and date-time #' #' @description #' `date_seq()` generates a date (Date) or date-time (POSIXct/POSIXlt) sequence. #' #' There are separate help pages for generating sequences for dates and #' date-times: #' #' - [dates (Date)][date-sequence] #' #' - [date-times (POSIXct/POSIXlt)][posixt-sequence] #' #' @inheritParams rlang::args_dots_empty #' #' @param from `[Date(1) / POSIXct(1) / POSIXlt(1)]` #' #' A date or date-time to start the sequence from. #' #' @param to `[Date(1) / POSIXct(1) / POSIXlt(1) / NULL]` #' #' A date or date-time to stop the sequence at. #' #' `to` is only included in the result if the resulting sequence divides #' the distance between `from` and `to` exactly. #' #' @param by `[integer(1) / clock_duration(1) / NULL]` #' #' The unit to increment the sequence by. #' #' @param total_size `[positive integer(1) / NULL]` #' #' The size of the resulting sequence. #' #' If specified alongside `to`, this must generate a non-fractional sequence #' between `from` and `to`. #' #' @return A date or date-time vector. #' #' @export #' @examples #' # See method specific documentation for more examples #' #' x <- as.Date("2019-01-01") #' date_seq(x, by = duration_months(2), total_size = 20) date_seq <- function(from, ..., to = NULL, by = NULL, total_size = NULL) { check_date_or_date_time(from) UseMethod("date_seq") } #' Sequences: date #' #' @description #' This is a Date method for the [date_seq()] generic. #' #' `date_seq()` generates a date (Date) sequence. #' #' When calling `date_seq()`, exactly two of the following must be specified: #' - `to` #' - `by` #' - `total_size` #' #' @inheritParams date_seq #' @inheritParams invalid_resolve #' #' @param from `[Date(1)]` #' #' A date to start the sequence from. #' #' @param to `[Date(1) / NULL]` #' #' A date to stop the sequence at. #' #' `to` is only included in the result if the resulting sequence divides #' the distance between `from` and `to` exactly. #' #' If `to` is supplied along with `by`, all components of `to` more precise #' than the precision of `by` must match `from` exactly. For example, if `by = #' duration_months(1)`, the day component of `to` must match the day component #' of `from`. This ensures that the generated sequence is, at a minimum, a #' weakly monotonic sequence of dates. #' #' @param by `[integer(1) / clock_duration(1) / NULL]` #' #' The unit to increment the sequence by. #' #' If `by` is an integer, it is equivalent to `duration_days(by)`. #' #' If `by` is a duration, it is allowed to have a precision of: #' - year #' - quarter #' - month #' - week #' - day #' #' @return A date vector. #' #' @name date-sequence #' #' @export #' @examples #' from <- date_build(2019, 1) #' to <- date_build(2019, 4) #' #' # Defaults to daily sequence #' date_seq(from, to = to, by = 7) #' #' # Use durations to change to monthly or yearly sequences #' date_seq(from, to = to, by = duration_months(1)) #' date_seq(from, by = duration_years(-2), total_size = 3) #' #' # Note that components of `to` more precise than the precision of `by` #' # must match `from` exactly. For example, this is not well defined: #' from <- date_build(2019, 5, 2) #' to <- date_build(2025, 7, 5) #' try(date_seq(from, to = to, by = duration_years(1))) #' #' # The month and day components of `to` must match `from` #' to <- date_build(2025, 5, 2) #' date_seq(from, to = to, by = duration_years(1)) #' #' # --------------------------------------------------------------------------- #' #' # Invalid dates must be resolved with the `invalid` argument #' from <- date_build(2019, 1, 31) #' to <- date_build(2019, 12, 31) #' #' try(date_seq(from, to = to, by = duration_months(1))) #' date_seq(from, to = to, by = duration_months(1), invalid = "previous") #' #' # Compare this to the base R result, which is often a source of confusion #' seq(from, to = to, by = "1 month") #' #' # This is equivalent to the overflow invalid resolution strategy #' date_seq(from, to = to, by = duration_months(1), invalid = "overflow") #' #' # --------------------------------------------------------------------------- #' #' # Usage of `to` and `total_size` must generate a non-fractional sequence #' # between `from` and `to` #' from <- date_build(2019, 1, 1) #' to <- date_build(2019, 1, 4) #' #' # These are fine #' date_seq(from, to = to, total_size = 2) #' date_seq(from, to = to, total_size = 4) #' #' # But this is not! #' try(date_seq(from, to = to, total_size = 3)) date_seq.Date <- function(from, ..., to = NULL, by = NULL, total_size = NULL, invalid = NULL) { check_dots_empty0(...) check_number_of_supplied_optional_arguments(to, by, total_size) check_date(to, allow_null = TRUE) if (!is_null(total_size)) { total_size <- check_length_out(total_size) } if (is_null(by)) { precision <- "day" } else if (is_duration(by)) { precision <- duration_precision(by) } else { precision <- "day" by <- duration_helper(by, PRECISION_DAY) } check_precision(precision) precision_int <- precision_to_integer(precision) if (precision_int == PRECISION_QUARTER) { by <- duration_cast(by, "month") precision <- "month" precision_int <- PRECISION_MONTH } if (precision_int == PRECISION_WEEK) { by <- duration_cast(by, "day") precision <- "day" precision_int <- PRECISION_DAY } if (precision_int %in% c(PRECISION_YEAR, PRECISION_MONTH)) { out <- date_seq_year_month(from, to, by, total_size, precision) out <- invalid_resolve(out, invalid = invalid) out <- as.Date(out) return(out) } if (precision_int == PRECISION_DAY) { out <- date_seq_day(from, to, by, total_size, precision) out <- as.Date(out) return(out) } precisions <- c("year", "quarter", "month", "week", "day") by_precision <- duration_precision(by) cli::cli_abort("`by` must have a precision of {.or {.str {precisions}}}, not {.str {by_precision}}.") } date_seq_year_month <- function(from, to, by, total_size, precision, ..., error_call = caller_env()) { check_dots_empty0(...) has_time <- is_POSIXt(from) from <- as_year_month_day(from) original_from <- from from <- calendar_narrow(from, precision) if (!is_null(to)) { to <- as_year_month_day(to) check_from_to_component_equivalence( from = original_from, to = to, precision = precision, has_time = has_time, error_call = error_call ) to <- calendar_narrow(to, precision) } out <- seq(from, to = to, by = by, length.out = total_size) out <- reset_original_components(out, original_from, precision, has_time) out } date_seq_day <- function(from, to, by, total_size, precision, ..., error_call = caller_env()) { check_dots_empty0(...) date_seq_day_hour_minute_second(from, to, by, total_size, precision, error_call, as_naive_time) } date_seq_hour_minute_second <- function(from, to, by, total_size, precision, ..., error_call = caller_env()) { check_dots_empty0(...) date_seq_day_hour_minute_second(from, to, by, total_size, precision, error_call, as_sys_time) } date_seq_day_hour_minute_second <- function(from, to, by, total_size, precision, error_call, as_time_point_fn) { has_time <- is_POSIXt(from) from <- as_time_point_fn(from) original_from <- from from <- time_point_floor(from, precision) if (!is_null(to)) { to <- as_time_point_fn(to) check_from_to_component_equivalence( from = as_year_month_day(original_from), to = as_year_month_day(to), precision = precision, has_time = has_time, error_call = error_call ) to <- time_point_floor(to, precision) } out <- seq(from, to = to, by = by, length.out = total_size) original_time <- original_from - from out <- out + original_time out } check_from_to_component_equivalence <- function(from, to, precision, has_time, error_call) { ok <- TRUE check_precision(precision, call = error_call) precision_int <- precision_to_integer(precision) if (precision_int < PRECISION_MONTH) { ok <- ok && is_true(get_month(from) == get_month(to)) } if (precision_int < PRECISION_DAY) { ok <- ok && is_true(get_day(from) == get_day(to)) } if (has_time) { if (precision_int < PRECISION_HOUR) { ok <- ok && is_true(get_hour(from) == get_hour(to)) } if (precision_int < PRECISION_MINUTE) { ok <- ok && is_true(get_minute(from) == get_minute(to)) } if (precision_int < PRECISION_SECOND) { ok <- ok && is_true(get_second(from) == get_second(to)) } } if (!ok) { message <- c( "All components of {.arg from} and {.arg to} more precise than {.str {precision}} must match.", i = "{.arg from} is {.str {format(from)}}.", i = "{.arg to} is {.str {format(to)}}." ) cli::cli_abort(message, call = error_call) } invisible() } reset_original_components <- function(out, from, precision, has_time) { check_precision(precision) precision_int <- precision_to_integer(precision) if (precision_int < PRECISION_MONTH) { out <- set_month(out, get_month(from)) } if (precision_int < PRECISION_DAY) { out <- set_day(out, get_day(from)) } if (has_time) { if (precision_int < PRECISION_HOUR) { out <- set_hour(out, get_hour(from)) } if (precision_int < PRECISION_MINUTE) { out <- set_minute(out, get_minute(from)) } if (precision_int < PRECISION_SECOND) { out <- set_second(out, get_second(from)) } } out } check_number_of_supplied_optional_arguments <- function(to, by, total_size, ..., error_call = caller_env()) { check_dots_empty0(...) has_to <- !is_null(to) has_by <- !is_null(by) has_ts <- !is_null(total_size) n_has <- sum(has_to, has_by, has_ts) if (n_has != 2L) { header <- "Must specify exactly two of:" bullets <- cli::format_bullets_raw(c( "*" = cli::format_inline("{.arg to}"), "*" = cli::format_inline("{.arg by}"), "*" = cli::format_inline("{.arg total_size}") )) message <- c(header, bullets) cli::cli_abort(message, call = error_call) } invisible() } # ------------------------------------------------------------------------------ #' Spanning sequence: date and date-time #' #' @description #' `date_spanning_seq()` generates a regular sequence along the span of #' `x`, i.e. along `[min(x), max(x)]`. For dates, this generates a day precision #' sequence, and for date-times it generates a second precision sequence. #' #' @details #' Missing and infinite values are automatically removed before the sequence is #' generated. #' #' For date-times, sys-time based sequences are generated, consistent with #' [`date_seq()`][posixt-sequence] when using a second precision `by` value. #' #' If you need more precise sequence generation, call [range()] and [date_seq()] #' directly. #' #' @param x `[Date / POSIXct / POSIXlt]` #' #' A date or date-time vector. #' #' @return A sequence along `[min(x), max(x)]`. #' #' @export #' @examples #' x <- date_build(2020, c(1, 2, 1), c(10, 5, 12)) #' date_spanning_seq(x) #' #' # Missing and infinite dates are removed before the sequence is generated #' x <- c(x, NA, Inf, -Inf) #' x #' #' date_spanning_seq(x) #' #' # For date-times, sequences are generated at second precision #' x <- date_time_build( #' 2020, 1, 2, 3, c(5, 4, 5), c(10, 48, 12), #' zone = "America/New_York" #' ) #' x #' #' date_spanning_seq(x) date_spanning_seq <- function(x) { check_date_or_date_time(x) UseMethod("date_spanning_seq") } #' @export date_spanning_seq.Date <- function(x) { x <- vec_drop_infinite(x) x <- as_sys_time(x) x <- time_point_spanning_seq(x) as.Date(x) } # ------------------------------------------------------------------------------ #' Counting: date and date-time #' #' @description #' `date_count_between()` counts the number of `precision` units between #' `start` and `end` (i.e., the number of years or months or hours). This count #' corresponds to the _whole number_ of units, and will never return a #' fractional value. #' #' This is suitable for, say, computing the whole number of years or months #' between two dates, accounting for the day and time of day. #' #' There are separate help pages for counting for dates and date-times: #' #' - [dates (Date)][date-count-between] #' #' - [date-times (POSIXct/POSIXlt)][posixt-count-between] #' #' @inheritSection calendar_count_between Comparison Direction #' #' @inheritParams calendar_count_between #' #' @param start,end `[Date / POSIXct / POSIXlt]` #' #' A pair of date or date-time vectors. These will be recycled to their common #' size. #' #' @inherit calendar_count_between return #' #' @export #' @examples #' # See method specific documentation for more examples #' #' start <- date_parse("2000-05-05") #' end <- date_parse(c("2020-05-04", "2020-05-06")) #' #' # Age in years #' date_count_between(start, end, "year") #' #' # Number of "whole" months between these dates #' date_count_between(start, end, "month") date_count_between <- function(start, end, precision, ..., n = 1L) { check_date_or_date_time(start) UseMethod("date_count_between") } #' Counting: date #' #' @description #' This is a Date method for the [date_count_between()] generic. #' #' `date_count_between()` counts the number of `precision` units between #' `start` and `end` (i.e., the number of years or months). This count #' corresponds to the _whole number_ of units, and will never return a #' fractional value. #' #' This is suitable for, say, computing the whole number of years or months #' between two dates, accounting for the day of the month. #' #' _Calendrical based counting:_ #' #' These precisions convert to a year-month-day calendar and count while in that #' type. #' #' - `"year"` #' #' - `"quarter"` #' #' - `"month"` #' #' _Time point based counting:_ #' #' These precisions convert to a time point and count while in that type. #' #' - `"week"` #' #' - `"day"` #' #' For dates, whether a calendar or time point is used is not all that #' important, but is is fairly important for date-times. #' #' @details #' `"quarter"` is equivalent to `"month"` precision with `n` set to `n * 3L`. #' #' @inheritSection calendar_count_between Comparison Direction #' #' @inheritParams date_count_between #' #' @param start,end `[Date]` #' #' A pair of date vectors. These will be recycled to their common #' size. #' #' @param precision `[character(1)]` #' #' One of: #' #' - `"year"` #' - `"quarter"` #' - `"month"` #' - `"week"` #' - `"day"` #' #' @inherit date_count_between return #' #' @name date-count-between #' #' @export #' @examples #' start <- date_parse("2000-05-05") #' end <- date_parse(c("2020-05-04", "2020-05-06")) #' #' # Age in years #' date_count_between(start, end, "year") #' #' # Number of "whole" months between these dates. i.e. #' # `2000-05-05 -> 2020-04-05` is 239 months #' # `2000-05-05 -> 2020-05-05` is 240 months #' # Since 2020-05-04 occurs before the 5th of that month, #' # it gets a count of 239 #' date_count_between(start, end, "month") #' #' # Number of "whole" quarters between (same as `"month"` with `n * 3`) #' date_count_between(start, end, "quarter") #' date_count_between(start, end, "month", n = 3) #' #' # Number of days between #' date_count_between(start, end, "day") #' #' # Number of full 3 day periods between these two dates #' date_count_between(start, end, "day", n = 3) #' #' # Essentially the truncated value of this #' date_count_between(start, end, "day") / 3 #' #' # --------------------------------------------------------------------------- #' #' # Breakdown into full years, months, and days between #' x <- start #' #' years <- date_count_between(x, end, "year") #' x <- add_years(x, years) #' #' months <- date_count_between(x, end, "month") #' x <- add_months(x, months) #' #' days <- date_count_between(x, end, "day") #' x <- add_days(x, days) #' #' data.frame( #' start = start, #' end = end, #' years = years, #' months = months, #' days = days #' ) #' #' # Note that when breaking down a date like that, you may need to #' # set `invalid` during intermediate calculations #' start <- date_build(2019, c(3, 3, 4), c(30, 31, 1)) #' end <- date_build(2019, 5, 05) #' #' # These are 1 month apart (plus a few days) #' months <- date_count_between(start, end, "month") #' #' # But adding that 1 month to `start` results in an invalid date #' try(add_months(start, months)) #' #' # You can choose various ways to resolve this #' start_previous <- add_months(start, months, invalid = "previous") #' start_next <- add_months(start, months, invalid = "next") #' #' days_previous <- date_count_between(start_previous, end, "day") #' days_next <- date_count_between(start_next, end, "day") #' #' # Resulting in slightly different day values. #' # No result is "perfect". Choosing "previous" or "next" both result #' # in multiple `start` dates having the same month/day breakdown values. #' data.frame( #' start = start, #' end = end, #' months = months, #' days_previous = days_previous, #' days_next = days_next #' ) date_count_between.Date <- function(start, end, precision, ..., n = 1L) { check_dots_empty0(...) check_date(end) # Designed to match `add_*()` functions to guarantee that # if `start <= end`, then `start + <= end` allowed_precisions_calendar <- c( PRECISION_YEAR, PRECISION_QUARTER, PRECISION_MONTH ) allowed_precisions_naive_time <- c( PRECISION_WEEK, PRECISION_DAY ) allowed_precisions_sys_time <- c( ) date_count_between_impl( start = start, end = end, precision = precision, n = n, allowed_precisions_calendar = allowed_precisions_calendar, allowed_precisions_naive_time = allowed_precisions_naive_time, allowed_precisions_sys_time = allowed_precisions_sys_time ) } date_count_between_impl <- function(start, end, precision, n, allowed_precisions_calendar, allowed_precisions_naive_time, allowed_precisions_sys_time, ..., error_call = caller_env()) { check_precision(precision, call = error_call) precision_int <- precision_to_integer(precision) if (precision_int %in% allowed_precisions_calendar) { start <- as_year_month_day(start) end <- as_year_month_day(end) out <- calendar_count_between(start, end, precision, n = n) return(out) } if (precision_int %in% allowed_precisions_naive_time) { start <- as_naive_time(start) end <- as_naive_time(end) out <- time_point_count_between(start, end, precision, n = n) return(out) } if (precision_int %in% allowed_precisions_sys_time) { start <- as_sys_time(start) end <- as_sys_time(end) out <- time_point_count_between(start, end, precision, n = n) return(out) } precisions <- c( allowed_precisions_calendar, allowed_precisions_naive_time, allowed_precisions_sys_time ) precisions <- vapply(precisions, precision_to_string, character(1)) cli::cli_abort( "{.arg precision} must be {.or {.str {precisions}}}, not {.str {precision}}.", call = error_call ) } # ------------------------------------------------------------------------------ is_date <- function(x) { inherits(x, "Date") } check_date <- function(x, ..., allow_null = FALSE, arg = caller_arg(x), call = caller_env()) { check_inherits( x = x, what = "Date", allow_null = allow_null, arg = arg, call = call ) } check_date_or_date_time <- function(x, ..., allow_null = FALSE, arg = caller_arg(x), call = caller_env()) { if (!missing(x)) { if (is_date(x) || is_POSIXt(x)) { return(invisible(NULL)) } if (allow_null && is_null(x)) { return(invisible(NULL)) } } stop_input_type( x = x, what = cli::format_inline("a {.cls Date} or {.cls POSIXt}"), allow_null = allow_null, arg = arg, call = call ) } clock/R/arithmetic.R0000644000176200001440000001303214423730227014025 0ustar liggesusers#' Clock arithmetic #' #' @description #' This is the landing page for all clock arithmetic functions. There are #' specific sub-pages describing how arithmetic works for different calendars #' and time points, which is where you should look for more information. #' #' Calendars are efficient at arithmetic with irregular units of time, such as #' month, quarters, or years. #' #' - [year-month-day][year-month-day-arithmetic] #' #' - [year-month-weekday][year-month-weekday-arithmetic] #' #' - [year-quarter-day][year-quarter-day-arithmetic] #' #' - [year-week-day][year-week-day-arithmetic] #' #' - [iso-year-week-day][iso-year-week-day-arithmetic] #' #' - [year-day][year-day-arithmetic] #' #' Time points, such as naive-times and sys-times, are efficient at arithmetic #' with regular, well-defined units of time, such as days, hours, seconds, #' or nanoseconds. #' #' - [time-point][time-point-arithmetic] #' #' Durations can use any of these arithmetic functions, and return a new #' duration with a precision corresponding to the common type of the #' input and the function used. #' #' - [duration][duration-arithmetic] #' #' Weekdays can perform day-based circular arithmetic. #' #' - [weekday][weekday-arithmetic] #' #' There are also convenience methods for doing arithmetic directly on a #' native R date or date-time type: #' #' - [dates (Date)][Date-arithmetic] #' #' - [date-times (POSIXct / POSIXlt)][posixt-arithmetic] #' #' @details #' `x` and `n` are recycled against each other using #' [tidyverse recycling rules][vctrs::vector_recycling_rules]. #' #' Months and years are considered "irregular" because some months have more #' days then others (28, 29, 30, or 31), and some years have more days than #' others (365 or 366). #' #' Days are considered "regular" because they are defined as 86,400 seconds. #' #' @inheritParams rlang::args_dots_empty #' #' @param x `[object]` #' #' An object. #' #' @param n `[integer / clock_duration]` #' #' An integer vector to be converted to a duration, or a duration #' corresponding to the arithmetic function being used. This corresponds #' to the number of duration units to add. `n` may be negative to subtract #' units of duration. #' #' @return `x` after performing the arithmetic. #' #' @name clock-arithmetic #' #' @examples #' # See each sub-page for more specific examples #' x <- year_month_day(2019, 2, 1) #' add_months(x, 1) NULL #' @rdname clock-arithmetic #' @export add_years <- function(x, n, ...) { UseMethod("add_years") } #' @rdname clock-arithmetic #' @export add_quarters <- function(x, n, ...) { UseMethod("add_quarters") } #' @rdname clock-arithmetic #' @export add_months <- function(x, n, ...) { UseMethod("add_months") } #' @rdname clock-arithmetic #' @export add_weeks <- function(x, n, ...) { UseMethod("add_weeks") } #' @rdname clock-arithmetic #' @export add_days <- function(x, n, ...) { UseMethod("add_days") } #' @rdname clock-arithmetic #' @export add_hours <- function(x, n, ...) { UseMethod("add_hours") } #' @rdname clock-arithmetic #' @export add_minutes <- function(x, n, ...) { UseMethod("add_minutes") } #' @rdname clock-arithmetic #' @export add_seconds <- function(x, n, ...) { UseMethod("add_seconds") } #' @rdname clock-arithmetic #' @export add_milliseconds <- function(x, n, ...) { UseMethod("add_milliseconds") } #' @rdname clock-arithmetic #' @export add_microseconds <- function(x, n, ...) { UseMethod("add_microseconds") } #' @rdname clock-arithmetic #' @export add_nanoseconds <- function(x, n, ...) { UseMethod("add_nanoseconds") } # ------------------------------------------------------------------------------ #' @export add_years.default <- function(x, n, ...) { stop_clock_unsupported(x) } #' @export add_quarters.default <- function(x, n, ...) { stop_clock_unsupported(x) } #' @export add_months.default <- function(x, n, ...) { stop_clock_unsupported(x) } #' @export add_weeks.default <- function(x, n, ...) { stop_clock_unsupported(x) } #' @export add_days.default <- function(x, n, ...) { stop_clock_unsupported(x) } #' @export add_hours.default <- function(x, n, ...) { stop_clock_unsupported(x) } #' @export add_minutes.default <- function(x, n, ...) { stop_clock_unsupported(x) } #' @export add_seconds.default <- function(x, n, ...) { stop_clock_unsupported(x) } #' @export add_milliseconds.default <- function(x, n, ...) { stop_clock_unsupported(x) } #' @export add_microseconds.default <- function(x, n, ...) { stop_clock_unsupported(x) } #' @export add_nanoseconds.default <- function(x, n, ...) { stop_clock_unsupported(x) } # ------------------------------------------------------------------------------ add_duration <- function(x, duration, ..., swapped = FALSE) { check_dots_empty() if (swapped) { # `duration` was LHS, so use names from it if applicable, making sure # that we only recycle everything once args <- vec_recycle_common(x = x, duration = duration) x <- args$x duration <- args$duration names(x) <- names_common(duration, x) } precision <- duration_precision_attribute(duration) precision <- precision_to_string(precision) switch ( precision, year = add_years(x, duration), quarter = add_quarters(x, duration), month = add_months(x, duration), week = add_weeks(x, duration), day = add_days(x, duration), hour = add_hours(x, duration), minute = add_minutes(x, duration), second = add_seconds(x, duration), millisecond = add_milliseconds(x, duration), microsecond = add_microseconds(x, duration), nanosecond = add_nanoseconds(x, duration) ) } clock/R/gregorian-year-month-weekday.R0000644000176200001440000012630514427270231017367 0ustar liggesusers#' Calendar: year-month-weekday #' #' `year_month_weekday()` constructs a calendar vector from the Gregorian #' year, month, weekday, and index specifying that this is the n-th weekday #' of the month. #' #' @details #' Fields are recycled against each other using #' [tidyverse recycling rules][vctrs::vector_recycling_rules]. #' #' Fields are collected in order until the first `NULL` field is located. No #' fields after the first `NULL` field are used. #' #' @inheritParams year_month_day #' #' @param day `[integer / NULL]` #' #' The weekday of the month. Values `[1, 7]` are allowed, where `1` is #' Sunday and `7` is Saturday. #' #' @param index `[integer / "last" / NULL]` #' #' The index specifying that `day` is the n-th weekday of the month. #' Values `[1, 5]` are allowed. #' #' If `"last"`, then the last instance of `day` in the current month #' is returned. #' #' @return A year-month-weekday calendar vector. #' #' @export #' @examples #' # All Fridays in January, 2019 #' # Note that there was no 5th Friday in January #' x <- year_month_weekday( #' 2019, #' clock_months$january, #' clock_weekdays$friday, #' 1:5 #' ) #' x #' #' invalid_detect(x) #' #' # Resolve this invalid date by using the previous valid date #' invalid_resolve(x, invalid = "previous") year_month_weekday <- function(year, month = NULL, day = NULL, index = NULL, hour = NULL, minute = NULL, second = NULL, subsecond = NULL, ..., subsecond_precision = NULL) { if (xor(is_null(day), is_null(index))) { abort("If either `day` or `index` is specified, both must be specified.") } # Stop on the first `NULL` argument if (is_null(month)) { precision <- PRECISION_YEAR fields <- list(year = year) } else if (is_null(index)) { precision <- PRECISION_MONTH fields <- list(year = year, month = month) } else if (is_null(hour)) { precision <- PRECISION_DAY fields <- list(year = year, month = month, day = day, index = index) } else if (is_null(minute)) { precision <- PRECISION_HOUR fields <- list(year = year, month = month, day = day, index = index, hour = hour) } else if (is_null(second)) { precision <- PRECISION_MINUTE fields <- list(year = year, month = month, day = day, index = index, hour = hour, minute = minute) } else if (is_null(subsecond)) { precision <- PRECISION_SECOND fields <- list(year = year, month = month, day = day, index = index, hour = hour, minute = minute, second = second) } else { calendar_check_subsecond_precision(subsecond_precision) precision <- precision_to_integer(subsecond_precision) fields <- list(year = year, month = month, day = day, index = index, hour = hour, minute = minute, second = second, subsecond = subsecond) } if (is_last(fields$index)) { fields$index <- 1L last <- TRUE } else { last <- FALSE } fields <- vec_cast_common(!!!fields, .to = integer()) if (precision >= PRECISION_YEAR) { check_between_year(fields$year, arg = "year") } if (precision >= PRECISION_MONTH) { check_between_month(fields$month, arg = "month") } if (precision >= PRECISION_DAY) { check_between_day_of_week(fields$day, arg = "day") check_between_index_of_week(fields$index, arg = "index") } if (precision >= PRECISION_HOUR) { check_between_hour(fields$hour, arg = "hour") } if (precision >= PRECISION_MINUTE) { check_between_minute(fields$minute, arg = "minute") } if (precision >= PRECISION_SECOND) { check_between_second(fields$second, arg = "second") } if (precision > PRECISION_SECOND) { check_between_subsecond(fields$subsecond, precision, arg = "subsecond") } fields <- vec_recycle_common(!!!fields) fields <- df_list_propagate_missing(fields) names <- NULL out <- new_year_month_weekday_from_fields(fields, precision, names) if (last) { out <- set_index(out, "last") } out } # ------------------------------------------------------------------------------ #' @export vec_proxy.clock_year_month_weekday <- function(x, ...) { .Call(`_clock_clock_rcrd_proxy`, x) } #' @export vec_restore.clock_year_month_weekday <- function(x, to, ...) { .Call(`_clock_year_month_weekday_restore`, x, to) } #' @export vec_proxy_compare.clock_year_month_weekday <- function(x, ...) { precision <- calendar_precision_attribute(x) if (precision >= PRECISION_DAY) { # See issue #32 message <- paste0( "'year_month_weekday' types with a precision of >= 'day' cannot be ", "trivially compared or ordered. ", "Convert to 'year_month_day' to compare using day-of-month values." ) abort(message) } # Year / month year-month-weekday precision can be compared without ambiguity vec_proxy(x) } # ------------------------------------------------------------------------------ #' @export format.clock_year_month_weekday <- function(x, ...) { out <- format_year_month_weekday_cpp(x, calendar_precision_attribute(x)) names(out) <- names(x) out } #' @export vec_ptype_full.clock_year_month_weekday <- function(x, ...) { calendar_ptype_full(x, "year_month_weekday") } #' @export vec_ptype_abbr.clock_year_month_weekday <- function(x, ...) { calendar_ptype_abbr(x, "ymw") } # ------------------------------------------------------------------------------ #' Is `x` a year-month-weekday? #' #' Check if `x` is a year-month-weekday. #' #' @param x `[object]` #' #' An object. #' #' @return Returns `TRUE` if `x` inherits from `"clock_year_month_weekday"`, #' otherwise returns `FALSE`. #' #' @export #' @examples #' is_year_month_weekday(year_month_weekday(2019)) is_year_month_weekday <- function(x) { inherits(x, "clock_year_month_weekday") } # ------------------------------------------------------------------------------ #' @export vec_ptype.clock_year_month_weekday <- function(x, ...) { switch( calendar_precision_attribute(x) + 1L, clock_empty_year_month_weekday_year, abort("Internal error: Invalid precision"), clock_empty_year_month_weekday_month, abort("Internal error: Invalid precision"), clock_empty_year_month_weekday_day, clock_empty_year_month_weekday_hour, clock_empty_year_month_weekday_minute, clock_empty_year_month_weekday_second, clock_empty_year_month_weekday_millisecond, clock_empty_year_month_weekday_microsecond, clock_empty_year_month_weekday_nanosecond, abort("Internal error: Invalid precision.") ) } #' @export vec_ptype2.clock_year_month_weekday.clock_year_month_weekday <- function(x, y, ...) { ptype2_calendar_and_calendar(x, y, ...) } #' @export vec_cast.clock_year_month_weekday.clock_year_month_weekday <- function(x, to, ...) { cast_calendar_to_calendar(x, to, ...) } # ------------------------------------------------------------------------------ #' @export calendar_is_precision.clock_year_month_weekday <- function(x, precision) { year_month_weekday_is_precision(precision) } year_month_weekday_is_precision <- function(precision) { if (precision == PRECISION_YEAR || precision == PRECISION_MONTH) { TRUE } else if (precision >= PRECISION_DAY && precision <= PRECISION_NANOSECOND) { TRUE } else { FALSE } } # ------------------------------------------------------------------------------ #' @export invalid_detect.clock_year_month_weekday <- function(x) { precision <- calendar_precision_attribute(x) if (precision < PRECISION_DAY) { rep_along(x, FALSE) } else { year <- field_year(x) month <- field_month(x) day <- field_day(x) index <- field_index(x) invalid_detect_year_month_weekday_cpp(year, month, day, index) } } #' @export invalid_any.clock_year_month_weekday <- function(x) { precision <- calendar_precision_attribute(x) if (precision < PRECISION_DAY) { FALSE } else { year <- field_year(x) month <- field_month(x) day <- field_day(x) index <- field_index(x) invalid_any_year_month_weekday_cpp(year, month, day, index) } } #' @export invalid_count.clock_year_month_weekday <- function(x) { precision <- calendar_precision_attribute(x) if (precision < PRECISION_DAY) { 0L } else { year <- field_year(x) month <- field_month(x) day <- field_day(x) index <- field_index(x) invalid_count_year_month_weekday_cpp(year, month, day, index) } } #' @export invalid_resolve.clock_year_month_weekday <- function(x, ..., invalid = NULL) { check_dots_empty() precision <- calendar_precision_attribute(x) invalid <- validate_invalid(invalid) if (precision < PRECISION_DAY) { x } else { fields <- invalid_resolve_year_month_weekday_cpp(x, precision, invalid, current_env()) new_year_month_weekday_from_fields(fields, precision, names(x)) } } # ------------------------------------------------------------------------------ #' Getters: year-month-weekday #' #' @description #' These are year-month-weekday methods for the #' [getter generics][clock-getters]. #' #' - `get_year()` returns the Gregorian year. #' #' - `get_month()` returns the month of the year. #' #' - `get_day()` returns the day of the week encoded from 1-7, where 1 = Sunday #' and 7 = Saturday. #' #' - `get_index()` returns a value from 1-5 indicating that the corresponding #' weekday is the n-th instance of that weekday in the current month. #' #' - There are sub-daily getters for extracting more precise components. #' #' @param x `[clock_year_month_weekday]` #' #' A year-month-weekday to get the component from. #' #' @return The component. #' #' @name year-month-weekday-getters #' @examples #' monday <- clock_weekdays$monday #' thursday <- clock_weekdays$thursday #' #' x <- year_month_weekday(2019, 1, monday:thursday, 1:4) #' x #' #' # Gets the weekday, 1 = Sunday, 7 = Saturday #' get_day(x) #' #' # Gets the index indicating which instance of that particular weekday #' # it is in the current month (i.e. the "1st Monday of January, 2019") #' get_index(x) NULL #' @rdname year-month-weekday-getters #' @export get_year.clock_year_month_weekday <- function(x) { field_year(x) } #' @rdname year-month-weekday-getters #' @export get_month.clock_year_month_weekday <- function(x) { calendar_check_minimum_precision(x, PRECISION_MONTH) field_month(x) } #' @rdname year-month-weekday-getters #' @export get_day.clock_year_month_weekday <- function(x) { # [Sunday, Saturday] -> [1, 7] calendar_check_minimum_precision(x, PRECISION_DAY) field_day(x) } #' @rdname year-month-weekday-getters #' @export get_index.clock_year_month_weekday <- function(x) { calendar_check_minimum_precision(x, PRECISION_DAY) field_index(x) } #' @rdname year-month-weekday-getters #' @export get_hour.clock_year_month_weekday <- function(x) { calendar_check_minimum_precision(x, PRECISION_HOUR) field_hour(x) } #' @rdname year-month-weekday-getters #' @export get_minute.clock_year_month_weekday <- function(x) { calendar_check_minimum_precision(x, PRECISION_MINUTE) field_minute(x) } #' @rdname year-month-weekday-getters #' @export get_second.clock_year_month_weekday <- function(x) { calendar_check_minimum_precision(x, PRECISION_SECOND) field_second(x) } #' @rdname year-month-weekday-getters #' @export get_millisecond.clock_year_month_weekday <- function(x) { calendar_check_exact_precision(x, PRECISION_MILLISECOND) field_subsecond(x) } #' @rdname year-month-weekday-getters #' @export get_microsecond.clock_year_month_weekday <- function(x) { calendar_check_exact_precision(x, PRECISION_MICROSECOND) field_subsecond(x) } #' @rdname year-month-weekday-getters #' @export get_nanosecond.clock_year_month_weekday <- function(x) { calendar_check_exact_precision(x, PRECISION_NANOSECOND) field_subsecond(x) } # ------------------------------------------------------------------------------ #' Setters: year-month-weekday #' #' @description #' These are year-month-weekday methods for the #' [setter generics][clock-setters]. #' #' - `set_year()` sets the Gregorian year. #' #' - `set_month()` sets the month of the year. Valid values are in the range #' of `[1, 12]`. #' #' - `set_day()` sets the day of the week. Valid values are in the range of #' `[1, 7]`, with 1 = Sunday, and 7 = Saturday. #' #' - `set_index()` sets the index indicating that the corresponding #' weekday is the n-th instance of that weekday in the current month. Valid #' values are in the range of `[1, 5]`. #' #' - There are sub-daily setters for setting more precise components. #' #' @inheritParams rlang::args_dots_empty #' #' @param x `[clock_year_month_weekday]` #' #' A year-month-weekday vector. #' #' @param value `[integer / "last"]` #' #' The value to set the component to. #' #' For `set_index()`, this can also be `"last"` to adjust to the last #' instance of the corresponding weekday in that month. #' #' @param index `[NULL / integer / "last"]` #' #' This argument is only used with `set_day()`, and allows you to set the #' index while also setting the weekday. #' #' If `x` is a month precision year-month-weekday, `index` is required to #' be set, as you must specify the weekday and the index simultaneously to #' promote from month to day precision. #' #' @return `x` with the component set. #' #' @name year-month-weekday-setters #' @examples #' x <- year_month_weekday(2019, 1:3) #' #' set_year(x, 2020:2022) #' #' # Setting the weekday on a month precision year-month-weekday requires #' # also setting the `index` to fully specify the day information #' x <- set_day(x, clock_weekdays$sunday, index = 1) #' x #' #' # Once you have at least day precision, you can set the weekday and #' # the index separately #' set_day(x, clock_weekdays$monday) #' set_index(x, 3) #' #' # Set to the "last" instance of the corresponding weekday in this month #' # (Note that some months have 4 Sundays, and others have 5) #' set_index(x, "last") #' #' # Set to an invalid index #' # January and February of 2019 don't have 5 Sundays! #' invalid <- set_index(x, 5) #' invalid #' #' # Resolve the invalid dates by choosing the previous/next valid moment #' invalid_resolve(invalid, invalid = "previous") #' invalid_resolve(invalid, invalid = "next") #' #' # You can also "overflow" the index. This keeps the weekday, but resets #' # the index to 1 and increments the month value by 1. #' invalid_resolve(invalid, invalid = "overflow") NULL #' @rdname year-month-weekday-setters #' @export set_year.clock_year_month_weekday <- function(x, value, ...) { check_dots_empty() set_field_year_month_weekday(x, value, "year") } #' @rdname year-month-weekday-setters #' @export set_month.clock_year_month_weekday <- function(x, value, ...) { check_dots_empty() calendar_check_minimum_precision(x, PRECISION_YEAR) set_field_year_month_weekday(x, value, "month") } #' @rdname year-month-weekday-setters #' @export set_day.clock_year_month_weekday <- function(x, value, ..., index = NULL) { check_dots_empty() calendar_check_minimum_precision(x, PRECISION_MONTH) has_index <- !is_null(index) precision <- calendar_precision_attribute(x) if (precision == PRECISION_MONTH) { if (!has_index) { abort("For 'month' precision 'year_month_weekday', both the day and index must be set simultaneously.") } # Promote up to day precision so we can assign to fields individually ones <- ones_along(x, na_propagate = TRUE) fields <- list(year = get_year(x), month = get_month(x), day = ones, index = ones) x <- new_year_month_weekday_from_fields(fields, PRECISION_DAY, names(x)) } out <- set_field_year_month_weekday(x, value, "day") if (has_index) { out <- set_field_year_month_weekday(out, index, "index") } out } #' @rdname year-month-weekday-setters #' @export set_index.clock_year_month_weekday <- function(x, value, ...) { check_dots_empty() calendar_check_minimum_precision(x, PRECISION_DAY) set_field_year_month_weekday(x, value, "index") } #' @rdname year-month-weekday-setters #' @export set_hour.clock_year_month_weekday <- function(x, value, ...) { check_dots_empty() calendar_check_minimum_precision(x, PRECISION_DAY) set_field_year_month_weekday(x, value, "hour") } #' @rdname year-month-weekday-setters #' @export set_minute.clock_year_month_weekday <- function(x, value, ...) { check_dots_empty() calendar_check_minimum_precision(x, PRECISION_HOUR) set_field_year_month_weekday(x, value, "minute") } #' @rdname year-month-weekday-setters #' @export set_second.clock_year_month_weekday <- function(x, value, ...) { check_dots_empty() calendar_check_minimum_precision(x, PRECISION_MINUTE) set_field_year_month_weekday(x, value, "second") } #' @rdname year-month-weekday-setters #' @export set_millisecond.clock_year_month_weekday <- function(x, value, ...) { check_dots_empty() calendar_check_exact_precision(x, c(PRECISION_SECOND, PRECISION_MILLISECOND)) set_field_year_month_weekday(x, value, "millisecond") } #' @rdname year-month-weekday-setters #' @export set_microsecond.clock_year_month_weekday <- function(x, value, ...) { check_dots_empty() calendar_check_exact_precision(x, c(PRECISION_SECOND, PRECISION_MICROSECOND)) set_field_year_month_weekday(x, value, "microsecond") } #' @rdname year-month-weekday-setters #' @export set_nanosecond.clock_year_month_weekday <- function(x, value, ...) { check_dots_empty() calendar_check_exact_precision(x, c(PRECISION_SECOND, PRECISION_NANOSECOND)) set_field_year_month_weekday(x, value, "nanosecond") } set_field_year_month_weekday <- function(x, value, component) { if (is_last(value) && identical(component, "index")) { return(set_field_year_month_weekday_last(x)) } precision_fields <- calendar_precision_attribute(x) precision_value <- year_month_weekday_component_to_precision(component) precision_out <- precision_common2(precision_fields, precision_value) names_out <- names(x) value <- vec_cast(value, integer()) value <- unname(value) switch( component, year = check_between_year(value), month = check_between_month(value), day = check_between_day_of_week(value), index = check_between_index_of_week(value), hour = check_between_hour(value), minute = check_between_minute(value), second = check_between_second(value), millisecond = check_between_subsecond(value, PRECISION_MILLISECOND), microsecond = check_between_subsecond(value, PRECISION_MICROSECOND), nanosecond = check_between_subsecond(value, PRECISION_NANOSECOND), abort("Unknown `component`", .internal = TRUE) ) args <- vec_recycle_common(x = x, value = value) args <- df_list_propagate_missing(args) x <- args$x value <- args$value field <- year_month_weekday_component_to_field(component) out <- vec_unstructure(x) out[[field]] <- value new_year_month_weekday_from_fields(out, precision_out, names = names_out) } set_field_year_month_weekday_last <- function(x) { # We require 'day' precision to set the `index` at all, so no # need to find a common precision here precision_out <- calendar_precision_attribute(x) names_out <- names(x) year <- field_year(x) month <- field_month(x) day <- field_day(x) index <- field_index(x) value <- get_year_month_weekday_last_cpp(year, month, day, index) out <- vec_unstructure(x) out[["index"]] <- value new_year_month_weekday_from_fields(out, precision_out, names = names_out) } # ------------------------------------------------------------------------------ #' @export calendar_name.clock_year_month_weekday <- function(x) { "year_month_weekday" } # ------------------------------------------------------------------------------ year_month_weekday_component_to_precision <- function(component) { switch( component, year = PRECISION_YEAR, month = PRECISION_MONTH, day = PRECISION_DAY, index = PRECISION_DAY, hour = PRECISION_HOUR, minute = PRECISION_MINUTE, second = PRECISION_SECOND, millisecond = PRECISION_MILLISECOND, microsecond = PRECISION_MICROSECOND, nanosecond = PRECISION_NANOSECOND, abort("Internal error: Unknown component name.") ) } year_month_weekday_component_to_field <- function(component) { switch ( component, year = component, month = component, day = component, index = component, hour = component, minute = component, second = component, millisecond = "subsecond", microsecond = "subsecond", nanosecond = "subsecond", abort("Internal error: Unknown component name.") ) } # ------------------------------------------------------------------------------ #' @rdname clock-arith #' @method vec_arith clock_year_month_weekday #' @export vec_arith.clock_year_month_weekday <- function(op, x, y, ...) { UseMethod("vec_arith.clock_year_month_weekday", y) } #' @method vec_arith.clock_year_month_weekday MISSING #' @export vec_arith.clock_year_month_weekday.MISSING <- function(op, x, y, ...) { arith_calendar_and_missing(op, x, y, ...) } #' @method vec_arith.clock_year_month_weekday clock_year_month_weekday #' @export vec_arith.clock_year_month_weekday.clock_year_month_weekday <- function(op, x, y, ...) { arith_calendar_and_calendar(op, x, y, ..., calendar_minus_calendar_fn = year_month_weekday_minus_year_month_weekday) } #' @method vec_arith.clock_year_month_weekday clock_duration #' @export vec_arith.clock_year_month_weekday.clock_duration <- function(op, x, y, ...) { arith_calendar_and_duration(op, x, y, ...) } #' @method vec_arith.clock_duration clock_year_month_weekday #' @export vec_arith.clock_duration.clock_year_month_weekday <- function(op, x, y, ...) { arith_duration_and_calendar(op, x, y, ...) } #' @method vec_arith.clock_year_month_weekday numeric #' @export vec_arith.clock_year_month_weekday.numeric <- function(op, x, y, ...) { arith_calendar_and_numeric(op, x, y, ...) } #' @method vec_arith.numeric clock_year_month_weekday #' @export vec_arith.numeric.clock_year_month_weekday <- function(op, x, y, ...) { arith_numeric_and_calendar(op, x, y, ...) } year_month_weekday_minus_year_month_weekday <- function(op, x, y, ...) { args <- vec_recycle_common(x = x, y = y) args <- vec_cast_common(!!!args) x <- args$x y <- args$y names <- names_common(x, y) precision <- calendar_precision_attribute(x) if (precision > PRECISION_MONTH) { stop_incompatible_op(op, x, y, ...) } fields <- year_month_weekday_minus_year_month_weekday_cpp(x, y, precision) new_duration_from_fields(fields, precision, names) } # ------------------------------------------------------------------------------ #' Arithmetic: year-month-weekday #' #' @description #' These are year-month-weekday methods for the #' [arithmetic generics][clock-arithmetic]. #' #' - `add_years()` #' #' - `add_quarters()` #' #' - `add_months()` #' #' Notably, _you cannot add days to a year-month-weekday_. For day-based #' arithmetic, first convert to a time point with [as_naive_time()] or #' [as_sys_time()]. #' #' @details #' Adding a single quarter with `add_quarters()` is equivalent to adding #' 3 months. #' #' `x` and `n` are recycled against each other using #' [tidyverse recycling rules][vctrs::vector_recycling_rules]. #' #' @inheritParams clock-arithmetic #' #' @param x `[clock_year_month_weekday]` #' #' A year-month-weekday vector. #' #' @return `x` after performing the arithmetic. #' #' @name year-month-weekday-arithmetic #' #' @examples #' # 2nd Friday in January, 2019 #' x <- year_month_weekday(2019, 1, clock_weekdays$friday, 2) #' x #' #' add_months(x, 1:5) #' #' # These don't necessarily correspond to the same day of the month #' as_year_month_day(add_months(x, 1:5)) NULL #' @rdname year-month-weekday-arithmetic #' @export add_years.clock_year_month_weekday <- function(x, n, ...) { year_month_weekday_plus_duration(x, n, PRECISION_YEAR) } #' @rdname year-month-weekday-arithmetic #' @export add_quarters.clock_year_month_weekday <- function(x, n, ...) { calendar_check_minimum_precision(x, PRECISION_MONTH) year_month_weekday_plus_duration(x, n, PRECISION_QUARTER) } #' @rdname year-month-weekday-arithmetic #' @export add_months.clock_year_month_weekday <- function(x, n, ...) { calendar_check_minimum_precision(x, PRECISION_MONTH) year_month_weekday_plus_duration(x, n, PRECISION_MONTH) } year_month_weekday_plus_duration <- function(x, n, n_precision, ..., error_call = caller_env()) { check_dots_empty0(...) x_precision <- calendar_precision_attribute(x) n <- duration_collect_n(n, n_precision, error_call = error_call) if (n_precision == PRECISION_QUARTER) { n <- duration_cast(n, "month") n_precision <- PRECISION_MONTH } size <- vec_size_common(x = x, n = n, .call = error_call) args <- vec_recycle_common(x = x, n = n, .size = size) x <- args$x n <- args$n names <- names_common(x, n) x <- vec_unstructure(x) if (n_precision == PRECISION_YEAR) { fields <- year_month_weekday_plus_years_cpp(x$year, n) x$year <- fields$year } else if (n_precision == PRECISION_MONTH) { fields <- year_month_weekday_plus_months_cpp(x$year, x$month, n) x$year <- fields$year x$month <- fields$month } else { abort("Unknown precision.", .internal = TRUE) } if (x_precision != n_precision) { x <- df_list_propagate_missing(x, size = size) } new_year_month_weekday_from_fields(x, x_precision, names = names) } # ------------------------------------------------------------------------------ #' Convert to year-month-weekday #' #' `as_year_month_weekday()` converts a vector to the year-month-weekday #' calendar. Time points, Dates, POSIXct, and other calendars can all be #' converted to year-month-weekday. #' #' @inheritParams rlang::args_dots_empty #' #' @param x `[vector]` #' #' A vector to convert to year-month-weekday. #' #' @return A year-month-weekday vector. #' @export #' @examples #' # From Date #' as_year_month_weekday(as.Date("2019-01-01")) #' #' # From POSIXct, which assumes that the naive time is what should be converted #' as_year_month_weekday(as.POSIXct("2019-01-01 02:30:30", "America/New_York")) #' #' # From other calendars #' as_year_month_weekday(year_quarter_day(2019, quarter = 2, day = 50)) as_year_month_weekday <- function(x, ...) { UseMethod("as_year_month_weekday") } #' @export as_year_month_weekday.default <- function(x, ...) { stop_clock_unsupported_conversion(x, "clock_year_month_weekday") } #' @export as_year_month_weekday.clock_year_month_weekday <- function(x, ...) { check_dots_empty0(...) x } # ------------------------------------------------------------------------------ #' @export as_sys_time.clock_year_month_weekday <- function(x, ...) { check_dots_empty0(...) calendar_check_no_invalid(x) precision <- calendar_precision_attribute(x) fields <- as_sys_time_year_month_weekday_cpp(x, precision) new_sys_time_from_fields(fields, precision, clock_rcrd_names(x)) } #' @export as_naive_time.clock_year_month_weekday <- function(x, ...) { check_dots_empty0(...) as_naive_time(as_sys_time(x)) } #' @export as.character.clock_year_month_weekday <- function(x, ...) { format(x) } # ------------------------------------------------------------------------------ #' @export calendar_leap_year.clock_year_month_weekday <- function(x) { x <- get_year(x) gregorian_leap_year_cpp(x) } # ------------------------------------------------------------------------------ #' @export calendar_month_factor.clock_year_month_weekday <- function(x, ..., labels = "en", abbreviate = FALSE) { check_dots_empty0(...) calendar_month_factor_impl(x, labels, abbreviate) } # ------------------------------------------------------------------------------ #' Grouping: year-month-weekday #' #' @description #' This is a year-month-weekday method for the [calendar_group()] generic. #' #' Grouping for a year-month-weekday object can be done at any precision except #' for `"day"`, as long as `x` is at least as precise as `precision`. #' #' @details #' Grouping by `"day"` is undefined for a year-month-weekday because there are #' two day fields, the weekday and the index, and there is no clear way to #' define how to group by that. #' #' @inheritParams calendar_group #' #' @param x `[clock_year_month_weekday]` #' #' A year-month-weekday vector. #' #' @param precision `[character(1)]` #' #' One of: #' #' - `"year"` #' - `"month"` #' - `"hour"` #' - `"minute"` #' - `"second"` #' - `"millisecond"` #' - `"microsecond"` #' - `"nanosecond"` #' #' @return `x` grouped at the specified `precision`. #' #' @name year-month-weekday-group #' #' @export #' @examples #' x <- year_month_weekday(2019, 1:12, clock_weekdays$sunday, 1, 00, 05, 05) #' x #' #' # Group by 3 months - drops more precise components! #' calendar_group(x, "month", n = 3) calendar_group.clock_year_month_weekday <- function(x, precision, ..., n = 1L) { n <- validate_calendar_group_n(n) x <- calendar_narrow(x, precision) check_precision(precision) precision <- precision_to_integer(precision) if (precision == PRECISION_YEAR) { value <- get_year(x) value <- group_component0(value, n) x <- set_year(x, value) return(x) } if (precision == PRECISION_MONTH) { value <- get_month(x) value <- group_component1(value, n) x <- set_month(x, value) return(x) } if (precision == PRECISION_DAY) { message <- paste0( "Grouping 'year_month_weekday' by 'day' precision is undefined. ", "Convert to 'year_month_day' to group by day of month." ) abort(message) } x <- calendar_group_time(x, n, precision) x } # ------------------------------------------------------------------------------ #' Narrow: year-month-weekday #' #' This is a year-month-weekday method for the [calendar_narrow()] generic. It #' narrows a year-month-weekday vector to the specified `precision`. #' #' @inheritParams year-month-weekday-widen #' #' @return `x` narrowed to the supplied `precision`. #' #' @name year-month-weekday-narrow #' #' @export #' @examples #' # Day precision #' x <- year_month_weekday(2019, 1, 1, 2) #' x #' #' # Narrowed to month precision #' calendar_narrow(x, "month") calendar_narrow.clock_year_month_weekday <- function(x, precision) { check_precision(precision) precision <- precision_to_integer(precision) out_fields <- list() x_fields <- unclass(x) if (precision >= PRECISION_YEAR) { out_fields[["year"]] <- x_fields[["year"]] } if (precision >= PRECISION_MONTH) { out_fields[["month"]] <- x_fields[["month"]] } if (precision >= PRECISION_DAY) { out_fields[["day"]] <- x_fields[["day"]] out_fields[["index"]] <- x_fields[["index"]] } out_fields <- calendar_narrow_time(out_fields, precision, x_fields) new_year_month_weekday_from_fields(out_fields, precision, names = names(x)) } # ------------------------------------------------------------------------------ #' Widen: year-month-weekday #' #' This is a year-month-weekday method for the [calendar_widen()] generic. It #' widens a year-month-weekday vector to the specified `precision`. #' #' @details #' Widening a month precision year-month-weekday to day precision will set #' the day and the index to `1`. This sets the weekday components to the #' first Sunday of the month. #' #' @inheritParams year-month-weekday-group #' #' @param precision `[character(1)]` #' #' One of: #' #' - `"year"` #' - `"month"` #' - `"day"` #' - `"hour"` #' - `"minute"` #' - `"second"` #' - `"millisecond"` #' - `"microsecond"` #' - `"nanosecond"` #' #' @return `x` widened to the supplied `precision`. #' #' @name year-month-weekday-widen #' #' @export #' @examples #' # Month precision #' x <- year_month_weekday(2019, 1) #' x #' #' # Widen to day precision #' # Note that this sets both the day and index to 1, #' # i.e. the first Sunday of the month. #' calendar_widen(x, "day") #' #' # Or second precision #' sec <- calendar_widen(x, "second") #' sec calendar_widen.clock_year_month_weekday <- function(x, precision) { x_precision <- calendar_precision_attribute(x) check_precision(precision) precision <- precision_to_integer(precision) if (precision >= PRECISION_MONTH && x_precision < PRECISION_MONTH) { x <- set_month(x, 1L) } if (precision >= PRECISION_DAY && x_precision < PRECISION_DAY) { x <- set_day(x, 1L, index = 1L) } x <- calendar_widen_time(x, x_precision, precision) x } # ------------------------------------------------------------------------------ #' Boundaries: year-month-weekday #' #' @description #' This is a year-month-weekday method for the [calendar_start()] and #' [calendar_end()] generics. They adjust components of a calendar to the #' start or end of a specified `precision`. #' #' This method is restricted to only `"year"` and `"month"` `precision`s, and #' `x` can't be more precise than month precision. Computing the "start" of #' a day precision year-month-weekday object isn't defined because #' a year-month-weekday with `day = 1, index = 1` doesn't necessarily occur #' earlier (chronologically) than `day = 2, index = 1`. Because of these #' restrictions, this method isn't particularly useful, but is included for #' completeness. #' #' @inheritParams year-month-weekday-group #' #' @param precision `[character(1)]` #' #' One of: #' #' - `"year"` #' - `"month"` #' #' @return `x` at the same precision, but with some components altered to be #' at the boundary value. #' #' @name year-month-weekday-boundary #' #' @examples #' # Month precision #' x <- year_month_weekday(2019, 1:5) #' x #' #' # Compute the last month of the year #' calendar_end(x, "year") NULL #' @rdname year-month-weekday-boundary #' @export calendar_start.clock_year_month_weekday <- function(x, precision) { x_precision <- calendar_precision_attribute(x) check_precision(precision) precision <- precision_to_integer(precision) calendar_start_end_checks(x, x_precision, precision, "start") if (x_precision >= PRECISION_DAY) { message <- paste0( "Computing the start of a 'year_month_weekday' with a precision equal to ", "or more precise than 'day' is undefined." ) abort(message) } if (precision <= PRECISION_YEAR && x_precision > PRECISION_YEAR) { x <- set_month(x, 1L) } x } #' @rdname year-month-weekday-boundary #' @export calendar_end.clock_year_month_weekday <- function(x, precision) { x_precision <- calendar_precision_attribute(x) check_precision(precision) precision <- precision_to_integer(precision) calendar_start_end_checks(x, x_precision, precision, "end") if (x_precision >= PRECISION_DAY) { message <- paste0( "Computing the end of a 'year_month_weekday' with a precision equal to ", "or more precise than 'day' is undefined." ) abort(message) } if (precision <= PRECISION_YEAR && x_precision > PRECISION_YEAR) { x <- set_month(x, 12L) } x } # ------------------------------------------------------------------------------ #' Counting: year-month-weekday #' #' This is a year-month-weekday method for the [calendar_count_between()] #' generic. It counts the number of `precision` units between `start` and `end` #' (i.e., the number of years or months). #' #' @details #' Remember that year-month-weekday is not comparable when it is `"day"` #' precision or finer, so this method is only defined for `"year"` and #' `"month"` precision year-month-weekday objects. #' #' `"quarter"` is equivalent to `"month"` precision with `n` set to `n * 3L`. #' #' @inheritParams calendar-count-between #' #' @param start,end `[clock_year_month_weekday]` #' #' A pair of year-month-weekday vectors. These will be recycled to their #' common size. #' #' @param precision `[character(1)]` #' #' One of: #' #' - `"year"` #' - `"quarter"` #' - `"month"` #' #' @inherit calendar-count-between return #' #' @name year-month-weekday-count-between #' #' @export #' @examples #' # Compute the number of months between two dates #' x <- year_month_weekday(2001, 2) #' y <- year_month_weekday(2021, c(1, 3)) #' #' calendar_count_between(x, y, "month") #' #' # Remember that day precision or finer year-month-weekday objects #' # are not comparable, so this won't work #' x <- year_month_weekday(2001, 2, 1, 1) #' try(calendar_count_between(x, x, "month")) calendar_count_between.clock_year_month_weekday <- function(start, end, precision, ..., n = 1L) { NextMethod() } calendar_count_between_standardize_precision_n.clock_year_month_weekday <- function(x, precision, n) { calendar_count_between_standardize_precision_n.clock_year_month_day(x, precision, n) } calendar_count_between_compute.clock_year_month_weekday <- function(start, end, precision) { calendar_count_between_compute.clock_year_month_day(start, end, precision) } calendar_count_between_proxy_compare.clock_year_month_weekday <- function(start, end, precision) { calendar_count_between_proxy_compare.clock_year_month_day(start, end, precision) } # ------------------------------------------------------------------------------ #' Sequences: year-month-weekday #' #' @description #' This is a year-month-weekday method for the [seq()] generic. #' #' Sequences can only be generated for `"year"` and `"month"` precision #' year-month-weekday vectors. #' #' When calling `seq()`, exactly two of the following must be specified: #' - `to` #' - `by` #' - Either `length.out` or `along.with` #' #' @inheritParams seq.clock_duration #' #' @param from `[clock_year_month_weekday(1)]` #' #' A `"year"` or `"month"` precision year-month-weekday to start the sequence #' from. #' #' `from` is always included in the result. #' #' @param to `[clock_year_month_weekday(1) / NULL]` #' #' A `"year"` or `"month"` precision year-month-weekday to stop the sequence #' at. #' #' `to` is cast to the type of `from`. #' #' `to` is only included in the result if the resulting sequence divides #' the distance between `from` and `to` exactly. #' #' @return A sequence with the type of `from`. #' #' @export #' @examples #' # Monthly sequence #' x <- seq(year_month_weekday(2019, 1), year_month_weekday(2020, 12), by = 1) #' x #' #' # Which we can then set the indexed weekday of #' set_day(x, clock_weekdays$sunday, index = "last") seq.clock_year_month_weekday <- function(from, to = NULL, by = NULL, length.out = NULL, along.with = NULL, ...) { seq.clock_year_month_day(from, to, by, length.out, along.with, ...) } # ------------------------------------------------------------------------------ #' @export clock_minimum.clock_year_month_weekday <- function(x) { check_year_month_weekday_precision_limit(x, "minimum") switch( calendar_precision_attribute(x) + 1L, clock_minimum_year_month_weekday_year, abort("Invalid precision", .internal = TRUE), clock_minimum_year_month_weekday_month, abort("Invalid precision", .internal = TRUE), abort("Invalid precision", .internal = TRUE), abort("Invalid precision", .internal = TRUE), abort("Invalid precision", .internal = TRUE), abort("Invalid precision", .internal = TRUE), abort("Invalid precision", .internal = TRUE), abort("Invalid precision", .internal = TRUE), abort("Invalid precision", .internal = TRUE), abort("Invalid precision", .internal = TRUE) ) } #' @export clock_maximum.clock_year_month_weekday <- function(x) { check_year_month_weekday_precision_limit(x, "maximum") switch( calendar_precision_attribute(x) + 1L, clock_maximum_year_month_weekday_year, abort("Invalid precision", .internal = TRUE), clock_maximum_year_month_weekday_month, abort("Invalid precision", .internal = TRUE), abort("Invalid precision", .internal = TRUE), abort("Invalid precision", .internal = TRUE), abort("Invalid precision", .internal = TRUE), abort("Invalid precision", .internal = TRUE), abort("Invalid precision", .internal = TRUE), abort("Invalid precision", .internal = TRUE), abort("Invalid precision", .internal = TRUE), abort("Invalid precision", .internal = TRUE), ) } year_month_weekday_minimum <- function(precision) { calendar_minimum(precision, year_month_weekday(clock_calendar_year_minimum)) } year_month_weekday_maximum <- function(precision) { calendar_maximum(precision, year_month_weekday(clock_calendar_year_maximum)) } check_year_month_weekday_precision_limit <- function(x, which, ..., arg = caller_arg(x), call = caller_env()) { x_precision <- calendar_precision_attribute(x) precision <- PRECISION_MONTH if (x_precision <= precision) { return(invisible(NULL)) } x_precision <- precision_to_string(x_precision) precision <- precision_to_string(precision) message <- c( "Can't compute the {which} of this {.cls year_month_weekday}, as it is undefined.", i = "The most precise allowed precision is {.str {precision}}.", i = "{.arg {arg}} has precision {.str {x_precision}}." ) cli::cli_abort(message, call = call) } # ------------------------------------------------------------------------------ clock_init_year_month_weekday_utils <- function(env) { year <- year_month_weekday(integer()) assign("clock_empty_year_month_weekday_year", year, envir = env) assign("clock_empty_year_month_weekday_month", calendar_widen(year, "month"), envir = env) assign("clock_empty_year_month_weekday_day", calendar_widen(year, "day"), envir = env) assign("clock_empty_year_month_weekday_hour", calendar_widen(year, "hour"), envir = env) assign("clock_empty_year_month_weekday_minute", calendar_widen(year, "minute"), envir = env) assign("clock_empty_year_month_weekday_second", calendar_widen(year, "second"), envir = env) assign("clock_empty_year_month_weekday_millisecond", calendar_widen(year, "millisecond"), envir = env) assign("clock_empty_year_month_weekday_microsecond", calendar_widen(year, "microsecond"), envir = env) assign("clock_empty_year_month_weekday_nanosecond", calendar_widen(year, "nanosecond"), envir = env) assign("clock_minimum_year_month_weekday_year", year_month_weekday_minimum("year"), envir = env) assign("clock_minimum_year_month_weekday_month", year_month_weekday_minimum("month"), envir = env) assign("clock_maximum_year_month_weekday_year", year_month_weekday_maximum("year"), envir = env) assign("clock_maximum_year_month_weekday_month", year_month_weekday_maximum("month"), envir = env) invisible(NULL) } clock/R/sys-time.R0000644000176200001440000004633514427270231013460 0ustar liggesusersnew_sys_time_from_fields <- function(fields, precision, names) { new_time_point_from_fields(fields, precision, CLOCK_SYS, names) } # ------------------------------------------------------------------------------ sys_days <- function(n = integer()) { names <- NULL duration <- duration_days(n) new_sys_time_from_fields(duration, PRECISION_DAY, names) } sys_seconds <- function(n = integer()) { names <- NULL duration <- duration_seconds(n) new_sys_time_from_fields(duration, PRECISION_SECOND, names) } # ------------------------------------------------------------------------------ #' Is `x` a sys-time? #' #' This function determines if the input is a sys-time object. #' #' @param x `[object]` #' #' An object. #' #' @return `TRUE` if `x` inherits from `"clock_sys_time"`, otherwise `FALSE`. #' #' @export #' @examples #' is_sys_time(1) #' is_sys_time(as_sys_time(duration_days(1))) is_sys_time <- function(x) { inherits(x, "clock_sys_time") } check_sys_time <- function(x, ..., arg = caller_arg(x), call = caller_env()) { check_inherits(x, what = "clock_sys_time", arg = arg, call = call) } # ------------------------------------------------------------------------------ #' Parsing: sys-time #' #' @description #' There are two parsers into a sys-time, `sys_time_parse()` and #' `sys_time_parse_RFC_3339()`. #' #' ## sys_time_parse() #' #' `sys_time_parse()` is useful when you have date-time strings like #' `"2020-01-01T01:04:30"` that you know should be interpreted as UTC, or like #' `"2020-01-01T01:04:30-04:00"` with a UTC offset but no zone name. If you find #' yourself in the latter situation, then parsing this string as a sys-time #' using the `%Ez` command to capture the offset is probably your best option. #' If you know that this string should be interpreted in a specific time zone, #' parse as a sys-time to get the UTC equivalent, then use [as_zoned_time()]. #' #' The default options assume that `x` should be parsed at second precision, #' using a `format` string of `"%Y-%m-%dT%H:%M:%S"`. This matches the default #' result from calling `format()` on a sys-time. #' #' `sys_time_parse()` is nearly equivalent to [naive_time_parse()], except for #' the fact that the `%z` command is actually used. Using `%z` assumes that the #' rest of the date-time string should be interpreted as a naive-time, which is #' then shifted by the UTC offset found in `%z`. The returned time can then be #' validly interpreted as UTC. #' #' _`sys_time_parse()` ignores the `%Z` command._ #' #' ## sys_time_parse_RFC_3339() #' #' `sys_time_parse_RFC_3339()` is a wrapper around `sys_time_parse()` that is #' intended to parse the extremely common date-time format outlined by #' [RFC 3339](https://datatracker.ietf.org/doc/html/rfc3339). This document #' outlines a profile of the ISO 8601 format that is even more restrictive. #' #' In particular, this function is intended to parse the following three #' formats: #' #' ``` #' 2019-01-01T00:00:00Z #' 2019-01-01T00:00:00+0430 #' 2019-01-01T00:00:00+04:30 #' ``` #' #' This function defaults to parsing the first of these formats by using #' a format string of `"%Y-%m-%dT%H:%M:%SZ"`. #' #' If your date-time strings use offsets from UTC rather than `"Z"`, then set #' `offset` to one of the following: #' #' - `"%z"` if the offset is of the form `"+0430"`. #' - `"%Ez"` if the offset is of the form `"+04:30"`. #' #' The RFC 3339 standard allows for replacing the `"T"` with a `"t"` or a space #' (`" "`). Set `separator` to adjust this as needed. #' #' For this function, the `precision` must be at least `"second"`. #' #' @details #' If your date-time strings contain a full time zone name and a UTC offset, use #' [zoned_time_parse_complete()]. If they contain a time zone abbreviation, use #' [zoned_time_parse_abbrev()]. #' #' If your date-time strings don't contain an offset from UTC and you aren't #' sure if they should be treated as UTC or not, you might consider using #' [naive_time_parse()], since the resulting naive-time doesn't come with an #' assumption of a UTC time zone. #' #' @inheritSection zoned-parsing Full Precision Parsing #' #' @inheritParams zoned-parsing #' #' @param precision `[character(1)]` #' #' A precision for the resulting time point. One of: #' #' - `"day"` #' #' - `"hour"` #' #' - `"minute"` #' #' - `"second"` #' #' - `"millisecond"` #' #' - `"microsecond"` #' #' - `"nanosecond"` #' #' Setting the `precision` determines how much information `%S` attempts #' to parse. #' #' @param separator `[character(1)]` #' #' The separator between the date and time components of the string. One of: #' #' - `"T"` #' #' - `"t"` #' #' - `" "` #' #' @param offset `[character(1)]` #' #' The format of the offset from UTC contained in the string. One of: #' #' - `"Z"` #' #' - `"z"` #' #' - `"%z"` to parse a numeric offset of the form `"+0430"` #' #' - `"%Ez"` to parse a numeric offset of the form `"+04:30"` #' #' @return A sys-time. #' #' @name sys-parsing #' #' @examples #' sys_time_parse("2020-01-01T05:06:07") #' #' # Day precision #' sys_time_parse("2020-01-01", precision = "day") #' #' # Nanosecond precision, but using a day based format #' sys_time_parse("2020-01-01", format = "%Y-%m-%d", precision = "nanosecond") #' #' # Multiple format strings are allowed for heterogeneous times #' sys_time_parse( #' c("2019-01-01", "2019/1/1"), #' format = c("%Y/%m/%d", "%Y-%m-%d"), #' precision = "day" #' ) #' #' # The `%z` command shifts the date-time by subtracting the UTC offset so #' # that the returned sys-time can be interpreted as UTC #' sys_time_parse( #' "2020-01-01 02:00:00 -0400", #' format = "%Y-%m-%d %H:%M:%S %z" #' ) #' #' # Remember that the `%Z` command is ignored entirely! #' sys_time_parse("2020-01-01 America/New_York", format = "%Y-%m-%d %Z") #' #' # --------------------------------------------------------------------------- #' # RFC 3339 #' #' # Typical UTC format #' x <- "2019-01-01T00:01:02Z" #' sys_time_parse_RFC_3339(x) #' #' # With a UTC offset containing a `:` #' x <- "2019-01-01T00:01:02+02:30" #' sys_time_parse_RFC_3339(x, offset = "%Ez") #' #' # With a space between the date and time and no `:` in the offset #' x <- "2019-01-01 00:01:02+0230" #' sys_time_parse_RFC_3339(x, separator = " ", offset = "%z") NULL #' @rdname sys-parsing #' @export sys_time_parse <- function(x, ..., format = NULL, precision = "second", locale = clock_locale()) { check_dots_empty0(...) check_time_point_precision(precision) precision <- precision_to_integer(precision) fields <- time_point_parse( x = x, format = format, precision = precision, locale = locale, clock = CLOCK_SYS ) new_sys_time_from_fields(fields, precision, names(x)) } #' @rdname sys-parsing #' @export sys_time_parse_RFC_3339 <- function(x, ..., separator = "T", offset = "Z", precision = "second") { separator <- arg_match0(separator, values = c("T", "t", " ")) offset <- arg_match0(offset, values = c("Z", "z", "%z", "%Ez")) format <- paste0("%Y-%m-%d", separator, "%H:%M:%S", offset) # Only used for error checking check_RFC_3339_precision_string(precision) sys_time_parse(x, ..., format = format, precision = precision) } check_RFC_3339_precision_string <- function(precision, call = caller_env()) { check_precision(precision, call = call) precision <- precision_to_integer(precision) if (!is_valid_RFC_3339_precision(precision)) { cli::cli_abort( "{.arg precision} must be at least {.str second} precision.", call = call ) } precision } is_valid_RFC_3339_precision <- function(precision) { precision >= PRECISION_SECOND } # ------------------------------------------------------------------------------ #' Convert to a sys-time #' #' @description #' `as_sys_time()` converts `x` to a sys-time. #' #' You can convert to a sys-time from any calendar type, as long as it has #' at least day precision. There also must not be any invalid dates. If invalid #' dates exist, they must first be resolved with [invalid_resolve()]. #' #' Converting to a sys-time from a naive-time retains the printed time, #' but adds an assumption that the time should be interpreted in the UTC time #' zone. #' #' Converting to a sys-time from a zoned-time retains the underlying duration, #' but the printed time is the equivalent UTC time to whatever the zoned-time's #' zone happened to be. #' #' Converting to a sys-time from a duration just wraps the duration in a #' sys-time object, adding the assumption that the time should be interpreted #' in the UTC time zone. The duration must have at least day precision. #' #' There are convenience methods for converting to a sys-time from R's #' native date and date-time types. Like converting from a zoned-time, these #' retain the underlying duration, but will change the printed time if the #' zone was not already UTC. #' #' @inheritParams rlang::args_dots_empty #' #' @param x `[object]` #' #' An object to convert to a sys-time. #' #' @return A sys-time vector. #' #' @export #' @examples #' x <- as.Date("2019-01-01") #' #' # Dates are assumed to be naive, so the printed time is the same whether #' # we convert it to sys-time or naive-time #' as_sys_time(x) #' as_naive_time(x) #' #' y <- as.POSIXct("2019-01-01 01:00:00", tz = "America/New_York") #' #' # The sys time displays the equivalent time in UTC (5 hours ahead of #' # America/New_York at this point in the year) #' as_sys_time(y) #' #' ym <- year_month_day(2019, 02) #' #' # A minimum of day precision is required #' try(as_sys_time(ym)) #' #' ymd <- set_day(ym, 10) #' as_sys_time(ymd) as_sys_time <- function(x, ...) { UseMethod("as_sys_time") } #' @export as_sys_time.default <- function(x, ...) { stop_clock_unsupported(x) } #' @export as_sys_time.clock_sys_time <- function(x, ...) { check_dots_empty0(...) x } # ------------------------------------------------------------------------------ #' @export as_naive_time.clock_sys_time <- function(x, ...) { check_dots_empty0(...) new_naive_time_from_fields(x, time_point_precision_attribute(x), clock_rcrd_names(x)) } #' Convert to a zoned-time from a sys-time #' #' @description #' This is a sys-time method for the [as_zoned_time()] generic. #' #' Converting to a zoned-time from a sys-time retains the underlying duration, #' but changes the printed time, depending on the `zone` that you choose. #' Remember that sys-times are interpreted as UTC. #' #' If you want to retain the printed time, try converting to a zoned-time #' [from a naive-time][as-zoned-time-naive-time], which is a time point #' with a yet-to-be-determined time zone. #' #' @inheritParams rlang::args_dots_empty #' #' @param x `[clock_sys_time]` #' #' A sys-time to convert to a zoned-time. #' #' @param zone `[character(1)]` #' #' The zone to convert to. #' #' @return A zoned-time vector. #' #' @name as-zoned-time-sys-time #' @export #' @examples #' x <- as_sys_time(year_month_day(2019, 02, 01, 02, 30, 00)) #' x #' #' # Since sys-time is interpreted as UTC, converting to a zoned-time with #' # a zone of UTC retains the printed time #' x_utc <- as_zoned_time(x, "UTC") #' x_utc #' #' # Converting to a different zone results in a different printed time, #' # which corresponds to the exact same point in time, just in a different #' # part of the work #' x_ny <- as_zoned_time(x, "America/New_York") #' x_ny as_zoned_time.clock_sys_time <- function(x, zone, ...) { check_dots_empty0(...) check_zone(zone) # Promote to at least seconds precision for `zoned_time` x <- vec_cast(x, vec_ptype2(x, clock_empty_sys_time_second)) precision <- time_point_precision_attribute(x) names <- clock_rcrd_names(x) new_zoned_time_from_fields(x, precision, zone, names) } #' @export as.character.clock_sys_time <- function(x, ...) { format(x) } # ------------------------------------------------------------------------------ #' What is the current sys-time? #' #' @description #' `sys_time_now()` returns the current time in UTC. #' #' @details #' The time is returned with a nanosecond precision, but the actual amount #' of data returned is OS dependent. Usually, information at at least the #' microsecond level is returned, with some platforms returning nanosecond #' information. #' #' @return A sys-time of the current time in UTC. #' #' @export #' @examples #' x <- sys_time_now() sys_time_now <- function() { names <- NULL fields <- sys_time_now_cpp() new_sys_time_from_fields(fields, PRECISION_NANOSECOND, names) } # ------------------------------------------------------------------------------ #' Info: sys-time #' #' @description #' `sys_time_info()` retrieves a set of low-level information generally not #' required for most date-time manipulations. It returns a data frame with the #' following columns: #' #' - `begin`, `end`: Second precision sys-times specifying the range of the #' current daylight saving time rule. The range is a half-open interval of #' `[begin, end)`. #' #' - `offset`: A second precision `duration` specifying the offset from UTC. #' #' - `dst`: A logical vector specifying if daylight saving time is currently #' active. #' #' - `abbreviation`: The time zone abbreviation in use throughout this `begin` #' to `end` range. #' #' @details #' If there have never been any daylight saving time transitions, the minimum #' supported year value is returned for `begin` (typically, a year value of #' `-32767`). #' #' If daylight saving time is no longer used in a time zone, the maximum #' supported year value is returned for `end` (typically, a year value of #' `32767`). #' #' The `offset` is the bridge between sys-time and naive-time for the `zone` #' being used. The relationship of the three values is: #' #' ``` #' offset = naive_time - sys_time #' ``` #' #' @param x `[clock_sys_time]` #' #' A sys-time. #' #' @param zone `[character]` #' #' A valid time zone name. #' #' Unlike most functions in clock, in `sys_time_info()` `zone` is vectorized #' and is recycled against `x`. #' #' @return A data frame of low level information. #' #' @export #' @examples #' library(vctrs) #' #' x <- year_month_day(2021, 03, 14, c(01, 03), c(59, 00), c(59, 00)) #' x <- as_naive_time(x) #' x <- as_zoned_time(x, "America/New_York") #' #' # x[1] is in EST, x[2] is in EDT #' x #' #' x_sys <- as_sys_time(x) #' #' info <- sys_time_info(x_sys, zoned_time_zone(x)) #' info #' #' # Convert `begin` and `end` to zoned-times to see the previous and #' # next daylight saving time transitions #' data_frame( #' x = x, #' begin = as_zoned_time(info$begin, zoned_time_zone(x)), #' end = as_zoned_time(info$end, zoned_time_zone(x)) #' ) #' #' # `end` can be used to iterate through daylight saving time transitions #' # by repeatedly calling `sys_time_info()` #' sys_time_info(info$end, zoned_time_zone(x)) #' #' # Multiple `zone`s can be supplied to look up daylight saving time #' # information in different time zones #' zones <- c("America/New_York", "America/Los_Angeles") #' #' info2 <- sys_time_info(x_sys[1], zones) #' info2 #' #' # The offset can be used to display the naive-time (i.e. the printed time) #' # in both of those time zones #' data_frame( #' zone = zones, #' naive_time = x_sys[1] + info2$offset #' ) sys_time_info <- function(x, zone) { check_sys_time(x) check_character(zone) precision <- time_point_precision_attribute(x) # Recycle `x` to the common size. `zone` is recycled internally as required, # which is more efficient than reloading the time zone repeatedly. size <- vec_size_common(x = x, zone = zone) x <- vec_recycle(x, size) fields <- sys_time_info_cpp(x, precision, zone) new_sys_time_info_from_fields(fields) } new_sys_time_info_from_fields <- function(fields) { names <- NULL fields[["begin"]] <- new_sys_time_from_fields(fields[["begin"]], PRECISION_SECOND, names) fields[["end"]] <- new_sys_time_from_fields(fields[["end"]], PRECISION_SECOND, names) fields[["offset"]] <- new_duration_from_fields(fields[["offset"]], PRECISION_SECOND, names) new_data_frame(fields) } # ------------------------------------------------------------------------------ #' @export vec_ptype_full.clock_sys_time <- function(x, ...) { time_point_ptype(x, type = "full") } #' @export vec_ptype_abbr.clock_sys_time <- function(x, ...) { time_point_ptype(x, type = "abbr") } # ------------------------------------------------------------------------------ #' @export vec_ptype.clock_sys_time <- function(x, ...) { switch( time_point_precision_attribute(x) + 1L, abort("Internal error: Invalid precision"), abort("Internal error: Invalid precision"), abort("Internal error: Invalid precision"), abort("Internal error: Invalid precision"), clock_empty_sys_time_day, clock_empty_sys_time_hour, clock_empty_sys_time_minute, clock_empty_sys_time_second, clock_empty_sys_time_millisecond, clock_empty_sys_time_microsecond, clock_empty_sys_time_nanosecond, abort("Internal error: Invalid precision.") ) } #' @export vec_ptype2.clock_sys_time.clock_sys_time <- function(x, y, ...) { ptype2_time_point_and_time_point(x, y, ...) } #' @export vec_cast.clock_sys_time.clock_sys_time <- function(x, to, ...) { cast_time_point_to_time_point(x, to, ...) } # ------------------------------------------------------------------------------ #' @rdname clock-arith #' @method vec_arith clock_sys_time #' @export vec_arith.clock_sys_time <- function(op, x, y, ...) { UseMethod("vec_arith.clock_sys_time", y) } #' @method vec_arith.clock_sys_time MISSING #' @export vec_arith.clock_sys_time.MISSING <- function(op, x, y, ...) { arith_time_point_and_missing(op, x, y, ...) } #' @method vec_arith.clock_sys_time clock_sys_time #' @export vec_arith.clock_sys_time.clock_sys_time <- function(op, x, y, ...) { arith_time_point_and_time_point(op, x, y, ...) } #' @method vec_arith.clock_sys_time clock_duration #' @export vec_arith.clock_sys_time.clock_duration <- function(op, x, y, ...) { arith_time_point_and_duration(op, x, y, ...) } #' @method vec_arith.clock_duration clock_sys_time #' @export vec_arith.clock_duration.clock_sys_time <- function(op, x, y, ...) { arith_duration_and_time_point(op, x, y, ...) } #' @method vec_arith.clock_sys_time numeric #' @export vec_arith.clock_sys_time.numeric <- function(op, x, y, ...) { arith_time_point_and_numeric(op, x, y, ...) } #' @method vec_arith.numeric clock_sys_time #' @export vec_arith.numeric.clock_sys_time <- function(op, x, y, ...) { arith_numeric_and_time_point(op, x, y, ...) } # ------------------------------------------------------------------------------ clock_init_sys_time_utils <- function(env) { day <- as_sys_time(year_month_day(integer(), integer(), integer())) assign("clock_empty_sys_time_day", day, envir = env) assign("clock_empty_sys_time_hour", time_point_cast(day, "hour"), envir = env) assign("clock_empty_sys_time_minute", time_point_cast(day, "minute"), envir = env) assign("clock_empty_sys_time_second", time_point_cast(day, "second"), envir = env) assign("clock_empty_sys_time_millisecond", time_point_cast(day, "millisecond"), envir = env) assign("clock_empty_sys_time_microsecond", time_point_cast(day, "microsecond"), envir = env) assign("clock_empty_sys_time_nanosecond", time_point_cast(day, "nanosecond"), envir = env) invisible(NULL) } clock/R/clock.R0000644000176200001440000000004014002371214012750 0ustar liggesusersCLOCK_SYS = 0L CLOCK_NAIVE = 1L clock/R/calendar.R0000644000176200001440000011360314427270231013450 0ustar liggesusers# ------------------------------------------------------------------------------ #' @export print.clock_calendar <- function(x, ..., max = NULL) { clock_print(x, max) } # - Each subclass implements a `format()` method # - Unlike vctrs, don't use `print(quote = FALSE)` since we want to match base R #' @export obj_print_data.clock_calendar <- function(x, ..., max) { if (vec_is_empty(x)) { return(invisible(x)) } x <- max_slice(x, max) out <- format(x) # Pass `max` to avoid base R's default footer print(out, max = max) invisible(x) } #' @export obj_print_footer.clock_calendar <- function(x, ..., max) { clock_print_footer(x, max) } # Align left to match pillar_shaft.Date # @export - lazy in .onLoad() pillar_shaft.clock_calendar <- function(x, ...) { out <- format(x) pillar::new_pillar_shaft_simple(out, align = "left") } # ------------------------------------------------------------------------------ # Note: Cannot cast between calendar precisions. Casting to a more precise # precision is undefined because we consider things like year-month to be # a range of days over the whole month, and it would be impossible to map # that to just one day. ptype2_calendar_and_calendar <- function(x, y, ...) { if (calendar_precision_attribute(x) == calendar_precision_attribute(y)) { x } else { stop_incompatible_type(x, y, ..., details = "Can't combine calendars with different precisions.") } } cast_calendar_to_calendar <- function(x, to, ...) { if (calendar_precision_attribute(x) == calendar_precision_attribute(to)) { x } else { stop_incompatible_cast(x, to, ..., details = "Can't cast between calendars with different precisions.") } } # ------------------------------------------------------------------------------ #' Is the calendar year a leap year? #' #' @description #' `calendar_leap_year()` detects if the calendar year is a leap year - i.e. #' does it contain one or more extra components than other years? #' #' A particular year is a leap year if: #' #' - [year_month_day()]: February has 29 days. #' #' - [year_month_weekday()]: February has a weekday that occurs 5 times. #' #' - [year_week_day()]: There are 53 weeks in the year, resulting in 371 #' days in the year. #' #' - [iso_year_week_day()]: There are 53 weeks in the year, resulting in 371 #' days in the year. #' #' - [year_quarter_day()]: One of the quarters has 1 more day than normal (the #' quarter with an extra day depends on the `start` used, but will always be #' the same for a particular `start`). This aligns with Gregorian leap years #' for all `start`s except February, in which case the leap year is always 1 #' year after the Gregorian leap year. #' #' - [year_day()]: There are 366 days in the year. #' #' @param x `[calendar]` #' #' A calendar type to detect leap years in. #' #' @return A logical vector the same size as `x`. Returns `TRUE` if in a leap #' year, `FALSE` if not in a leap year, and `NA` if `x` is `NA`. #' #' @export #' @examples #' x <- year_month_day(c(2019:2024, NA)) #' calendar_leap_year(x) #' #' # For year-quarter-day, the leap year typically aligns with the Gregorian #' # leap year, unless the `start` is February, in which case the leap year is #' # always 1 year after the Gregorian leap year #' x <- year_quarter_day(2020:2021, start = clock_months$january) #' calendar_leap_year(x) #' #' x <- year_quarter_day(2020:2021, start = clock_months$february) #' calendar_leap_year(x) #' #' # With a January start, 2020 has the extra day #' get_day(year_quarter_day(2020, 1:4, "last", start = clock_months$january)) #' get_day(year_quarter_day(2021, 1:4, "last", start = clock_months$january)) #' get_day(year_quarter_day(2022, 1:4, "last", start = clock_months$january)) #' #' # With a February start, 2021 has the extra day #' get_day(year_quarter_day(2020, 1:4, "last", start = clock_months$february)) #' get_day(year_quarter_day(2021, 1:4, "last", start = clock_months$february)) #' get_day(year_quarter_day(2022, 1:4, "last", start = clock_months$february)) calendar_leap_year <- function(x) { check_calendar(x) UseMethod("calendar_leap_year") } #' @export calendar_leap_year.clock_calendar <- function(x) { stop_clock_unsupported(x) } # ------------------------------------------------------------------------------ #' Convert a calendar to an ordered factor of month names #' #' @description #' `calendar_month_factor()` extracts the month values from a calendar and #' converts them to an ordered factor of month names. This can be useful in #' combination with ggplot2, or for modeling. #' #' This function is only relevant for calendar types that use a month field, #' i.e. [year_month_day()] and [year_month_weekday()]. The calendar type must #' have at least month precision. #' #' @inheritParams rlang::args_dots_empty #' @inheritParams clock_locale #' #' @param x `[calendar]` #' #' A calendar vector. #' #' @param abbreviate `[logical(1)]` #' #' If `TRUE`, the abbreviated month names from `labels` will be used. #' #' If `FALSE`, the full month names from `labels` will be used. #' #' @return An ordered factor representing the months. #' #' @export #' @examples #' x <- year_month_day(2019, 1:12) #' #' calendar_month_factor(x) #' calendar_month_factor(x, abbreviate = TRUE) #' calendar_month_factor(x, labels = "fr") calendar_month_factor <- function(x, ..., labels = "en", abbreviate = FALSE) { check_calendar(x) UseMethod("calendar_month_factor") } #' @export calendar_month_factor.clock_calendar <- function(x, ..., labels = "en", abbreviate = FALSE) { stop_clock_unsupported(x) } calendar_month_factor_impl <- function(x, labels, abbreviate, ..., error_call = caller_env()) { check_dots_empty0(...) if (calendar_precision_attribute(x) < PRECISION_MONTH) { cli::cli_abort("{.arg x} must have at least {.str month} precision.", call = error_call) } if (is_character(labels)) { labels <- clock_labels_lookup(labels) } check_clock_labels(labels, call = error_call) check_bool(abbreviate, call = error_call) if (abbreviate) { labels <- labels$month_abbrev } else { labels <- labels$month } x <- get_month(x) x <- labels[x] factor(x, levels = labels, ordered = TRUE) } # ------------------------------------------------------------------------------ #' Group calendar components #' #' @description #' `calendar_group()` groups at a multiple of the specified precision. Grouping #' alters the value of a single component (i.e. the month component #' if grouping by month). Components that are more precise than the precision #' being grouped at are dropped altogether (i.e. the day component is dropped #' if grouping by month). #' #' Each calendar has its own help page describing the grouping process in more #' detail: #' #' - [year-month-day][year-month-day-group] #' #' - [year-month-weekday][year-month-weekday-group] #' #' - [year-week-day][year-week-day-group] #' #' - [iso-year-week-day][iso-year-week-day-group] #' #' - [year-quarter-day][year-quarter-day-group] #' #' - [year-day][year-day-group] #' #' @inheritParams rlang::args_dots_empty #' #' @param x `[calendar]` #' #' A calendar vector. #' #' @param precision `[character(1)]` #' #' A precision. Allowed precisions are dependent on the calendar used. #' #' @param n `[positive integer(1)]` #' #' A single positive integer specifying a multiple of `precision` to use. #' #' @return `x` grouped at the specified `precision`. #' #' @export #' @examples #' # See the calendar specific help pages for more examples #' x <- year_month_day(2019, c(1, 1, 2, 2, 3, 3, 4, 4), 1:8) #' x #' #' # Group by two months #' calendar_group(x, "month", n = 2) #' #' # Group by two days of the month #' calendar_group(x, "day", n = 2) calendar_group <- function(x, precision, ..., n = 1L) { check_dots_empty0(...) check_calendar(x) check_precision(precision) precision <- precision_to_integer(precision) calendar_check_precision(x, precision) x_precision <- calendar_precision_attribute(x) if (precision > x_precision) { precision <- precision_to_string(precision) x_precision <- precision_to_string(x_precision) message <- paste0( "Can't group at a precision ({.str {precision}}) ", "that is more precise than `x` ({.str {x_precision}})." ) cli::cli_abort(message) } if (precision > PRECISION_SECOND && x_precision != precision) { # Grouping nanosecond precision by millisecond would be inconsistent # with our general philosophy that you "lock in" the subsecond precision. precision <- precision_to_string(precision) x_precision <- precision_to_string(x_precision) message <- paste0( "Can't group a subsecond precision `x` ({.str {x_precision}}) ", "by another subsecond precision ({.str {precision}})." ) cli::cli_abort(message) } UseMethod("calendar_group") } #' @export calendar_group.clock_calendar <- function(x, precision, ..., n = 1L) { stop_clock_unsupported(x) } calendar_group_time <- function(x, n, precision) { if (precision == PRECISION_HOUR) { value <- get_hour(x) value <- group_component0(value, n) x <- set_hour(x, value) return(x) } if (precision == PRECISION_MINUTE) { value <- get_minute(x) value <- group_component0(value, n) x <- set_minute(x, value) return(x) } if (precision == PRECISION_SECOND) { value <- get_second(x) value <- group_component0(value, n) x <- set_second(x, value) return(x) } # Generic ensures that if `x_precision > PRECISION_SECOND`, # then `precision == x_precision`, making this safe. if (precision == PRECISION_MILLISECOND) { value <- get_millisecond(x) value <- group_component0(value, n) x <- set_millisecond(x, value) return(x) } if (precision == PRECISION_MICROSECOND) { value <- get_microsecond(x) value <- group_component0(value, n) x <- set_microsecond(x, value) return(x) } if (precision == PRECISION_NANOSECOND) { value <- get_nanosecond(x) value <- group_component0(value, n) x <- set_nanosecond(x, value) return(x) } abort("Unknown precision.", .internal = TRUE) } group_component0 <- function(x, n) { (x %/% n) * n } group_component1 <- function(x, n) { ((x - 1L) %/% n) * n + 1L } validate_calendar_group_n <- function(n, ..., error_call = caller_env()) { check_number_whole(n, min = 0, call = error_call) n <- vec_cast(n, integer(), call = error_call) n } # ------------------------------------------------------------------------------ #' Narrow a calendar to a less precise precision #' #' @description #' `calendar_narrow()` narrows `x` to the specified `precision`. It does so #' by dropping components that represent a precision that is finer than #' `precision`. #' #' Each calendar has its own help page describing the precisions that you #' can narrow to: #' #' - [year-month-day][year-month-day-narrow] #' #' - [year-month-weekday][year-month-weekday-narrow] #' #' - [year-week-day][year-week-day-narrow] #' #' - [iso-year-week-day][iso-year-week-day-narrow] #' #' - [year-quarter-day][year-quarter-day-narrow] #' #' - [year-day][year-day-narrow] #' #' @details #' A subsecond precision `x` cannot be narrowed to another subsecond precision. #' You cannot narrow from, say, `"nanosecond"` to `"millisecond"` precision. #' clock operates under the philosophy that once you have set the subsecond #' precision of a calendar, it is "locked in" at that precision. If you #' expected this to use integer division to divide the nanoseconds by 1e6 to #' get to millisecond precision, you probably want to convert to a time point #' first, and use [time_point_floor()]. #' #' @inheritParams calendar_group #' #' @return `x` narrowed to the supplied `precision`. #' #' @export #' @examples #' # Hour precision #' x <- year_month_day(2019, 1, 3, 4) #' x #' #' # Narrowed to day precision #' calendar_narrow(x, "day") #' #' # Or month precision #' calendar_narrow(x, "month") calendar_narrow <- function(x, precision) { check_calendar(x) check_precision(precision) precision <- precision_to_integer(precision) calendar_check_precision(x, precision) x_precision <- calendar_precision_attribute(x) if (precision > x_precision) { precision <- precision_to_string(precision) x_precision <- precision_to_string(x_precision) message <- paste0( "Can't narrow to a precision ({.str {precision}}) ", "that is wider than `x` ({.str {x_precision}})." ) cli::cli_abort(message) } if (precision > PRECISION_SECOND && x_precision != precision) { # Allowing Nanosecond -> Millisecond wouldn't be consistent with us # disallowing `set_millisecond(>)`, and is ambiguous. precision <- precision_to_string(precision) x_precision <- precision_to_string(x_precision) message <- paste0( "Can't narrow a subsecond precision `x` ({.str {x_precision}}) ", "to another subsecond precision ({.str {precision}})." ) cli::cli_abort(message) } UseMethod("calendar_narrow") } #' @export calendar_narrow.clock_calendar <- function(x, precision) { stop_clock_unsupported(x) } calendar_narrow_time <- function(out_fields, out_precision, x_fields) { if (out_precision >= PRECISION_HOUR) { out_fields[["hour"]] <- x_fields[["hour"]] } if (out_precision >= PRECISION_MINUTE) { out_fields[["minute"]] <- x_fields[["minute"]] } if (out_precision >= PRECISION_SECOND) { out_fields[["second"]] <- x_fields[["second"]] } if (out_precision > PRECISION_SECOND) { out_fields[["subsecond"]] <- x_fields[["subsecond"]] } out_fields } # ------------------------------------------------------------------------------ #' Widen a calendar to a more precise precision #' #' @description #' `calendar_widen()` widens `x` to the specified `precision`. It does so #' by setting new components to their smallest value. #' #' Each calendar has its own help page describing the precisions that you #' can widen to: #' #' - [year-month-day][year-month-day-widen] #' #' - [year-month-weekday][year-month-weekday-widen] #' #' - [year-week-day][year-week-day-widen] #' #' - [iso-year-week-day][iso-year-week-day-widen] #' #' - [year-quarter-day][year-quarter-day-widen] #' #' - [year-day][year-day-widen] #' #' @details #' A subsecond precision `x` cannot be widened. You cannot widen from, say, #' `"millisecond"` to `"nanosecond"` precision. clock operates under the #' philosophy that once you have set the subsecond precision of a calendar, #' it is "locked in" at that precision. If you expected this to multiply #' the milliseconds by 1e6 to get to nanosecond precision, you probably #' want to convert to a time point first, and use [time_point_cast()]. #' #' Generally, clock treats calendars at a specific precision as a _range_ of #' values. For example, a month precision year-month-day is treated as a range #' over `[yyyy-mm-01, yyyy-mm-last]`, with no assumption about the day of the #' month. However, occasionally it is useful to quickly widen a calendar, #' assuming that you want the beginning of this range to be used for each #' component. This is where `calendar_widen()` can come in handy. #' #' @inheritParams calendar_group #' #' @return `x` widened to the supplied `precision`. #' #' @export #' @examples #' # Month precision #' x <- year_month_day(2019, 1) #' x #' #' # Widen to day precision #' calendar_widen(x, "day") #' #' # Or second precision #' calendar_widen(x, "second") calendar_widen <- function(x, precision) { check_calendar(x) check_precision(precision) precision <- precision_to_integer(precision) calendar_check_precision(x, precision) x_precision <- calendar_precision_attribute(x) if (x_precision > precision) { precision <- precision_to_string(precision) x_precision <- precision_to_string(x_precision) message <- paste0( "Can't widen to a precision ({.str {precision}}) ", "that is narrower than `x` ({.str {x_precision}})." ) cli::cli_abort(message) } if (x_precision > PRECISION_SECOND && x_precision != precision) { # Allowing Millisecond -> Nanosecond wouldn't be consistent with us # disallowing `set_nanosecond(>)`, and is ambiguous. precision <- precision_to_string(precision) x_precision <- precision_to_string(x_precision) message <- paste0( "Can't widen a subsecond precision `x` ({.str {x_precision}}) ", "to another subsecond precision ({.str {precision}})." ) cli::cli_abort(message) } UseMethod("calendar_widen") } #' @export calendar_widen.clock_calendar <- function(x, precision) { stop_clock_unsupported(x) } calendar_widen_time <- function(x, x_precision, precision) { if (precision >= PRECISION_HOUR && x_precision < PRECISION_HOUR) { x <- set_hour(x, 0L) } if (precision >= PRECISION_MINUTE && x_precision < PRECISION_MINUTE) { x <- set_minute(x, 0L) } if (precision >= PRECISION_SECOND && x_precision < PRECISION_SECOND) { x <- set_second(x, 0L) } # `x` is required to fulfill: # `x_precision < PRECISION_SECOND` or `x_precision == precision` if (precision == PRECISION_MILLISECOND && x_precision != precision) { x <- set_millisecond(x, 0L) } if (precision == PRECISION_MICROSECOND && x_precision != precision) { x <- set_microsecond(x, 0L) } if (precision == PRECISION_NANOSECOND && x_precision != precision) { x <- set_nanosecond(x, 0L) } x } # ------------------------------------------------------------------------------ #' Boundaries: calendars #' #' @description #' - `calendar_start()` computes the start of a calendar at a particular #' `precision`, such as the "start of the quarter". #' #' - `calendar_end()` computes the end of a calendar at a particular #' `precision`, such as the "end of the month". #' #' For both `calendar_start()` and `calendar_end()`, the precision of `x` is #' always retained. #' #' Each calendar has its own help page describing the precisions that you #' can compute a boundary at: #' #' - [year-month-day][year-month-day-boundary] #' #' - [year-month-weekday][year-month-weekday-boundary] #' #' - [year-week-day][year-week-day-boundary] #' #' - [iso-year-week-day][iso-year-week-day-boundary] #' #' - [year-quarter-day][year-quarter-day-boundary] #' #' - [year-day][year-day-boundary] #' #' @inheritParams calendar_group #' #' @return `x` at the same precision, but with some components altered to be #' at the boundary value. #' #' @name calendar-boundary #' @examples #' # Hour precision #' x <- year_month_day(2019, 2:4, 5, 6) #' x #' #' # Compute the start of the month #' calendar_start(x, "month") #' #' # Or the end of the month, notice that the hour value is adjusted as well #' calendar_end(x, "month") NULL #' @rdname calendar-boundary #' @export calendar_start <- function(x, precision) { check_calendar(x) UseMethod("calendar_start") } #' @export calendar_start.clock_calendar <- function(x, precision) { stop_clock_unsupported(x) } #' @rdname calendar-boundary #' @export calendar_end <- function(x, precision) { check_calendar(x) UseMethod("calendar_end") } #' @export calendar_end.clock_calendar <- function(x, precision) { stop_clock_unsupported(x) } calendar_start_end_checks <- function(x, x_precision, precision, which) { calendar_check_precision(x, precision) if (x_precision < precision) { precision <- precision_to_string(precision) x_precision <- precision_to_string(x_precision) message <- paste0( "Can't compute the {which} of `x` ({.str {x_precision}}) ", "at a more precise precision ({.str {precision}})." ) cli::cli_abort(message) } if (precision > PRECISION_SECOND && x_precision != precision) { # Computing the start/end of nanosecond precision at millisecond precision # would be inconsistent with our general philosophy that you "lock in" # the subsecond precision. precision <- precision_to_string(precision) x_precision <- precision_to_string(x_precision) message <- paste0( "Can't compute the {which} of a subsecond precision `x` ({.str {x_precision}}) ", "at another subsecond precision ({.str {precision}})." ) cli::cli_abort(message) } invisible(x) } calendar_start_time <- function(x, x_precision, precision) { values <- list( hour = 0L, minute = 0L, second = 0L, millisecond = 0L, microsecond = 0L, nanosecond = 0L ) calendar_start_end_time(x, x_precision, precision, values) } calendar_end_time <- function(x, x_precision, precision) { values <- list( hour = 23L, minute = 59L, second = 59L, millisecond = 999L, microsecond = 999999L, nanosecond = 999999999L ) calendar_start_end_time(x, x_precision, precision, values) } calendar_start_end_time <- function(x, x_precision, precision, values) { if (precision <= PRECISION_DAY && x_precision > PRECISION_DAY) { x <- set_hour(x, values$hour) } if (precision <= PRECISION_HOUR && x_precision > PRECISION_HOUR) { x <- set_minute(x, values$minute) } if (precision <= PRECISION_MINUTE && x_precision > PRECISION_MINUTE) { x <- set_second(x, values$second) } if (precision <= PRECISION_SECOND && x_precision > PRECISION_SECOND) { if (x_precision == PRECISION_MILLISECOND) { x <- set_millisecond(x, values$millisecond) } else if (x_precision == PRECISION_MICROSECOND) { x <- set_microsecond(x, values$microsecond) } else if (x_precision == PRECISION_NANOSECOND) { x <- set_nanosecond(x, values$nanosecond) } } x } # ------------------------------------------------------------------------------ #' Counting: calendars #' #' @description #' `calendar_count_between()` counts the number of `precision` units between #' `start` and `end` (i.e., the number of years or months). This count #' corresponds to the _whole number_ of units, and will never return a #' fractional value. #' #' This is suitable for, say, computing the whole number of years or months #' between two calendar dates, accounting for the day and time of day. #' #' Each calendar has its own help page describing the precisions that you can #' count at: #' #' - [year-month-day][year-month-day-count-between] #' #' - [year-month-weekday][year-month-weekday-count-between] #' #' - [year-week-day][year-week-day-count-between] #' #' - [iso-year-week-day][iso-year-week-day-count-between] #' #' - [year-quarter-day][year-quarter-day-count-between] #' #' - [year-day][year-day-count-between] #' #' @section Comparison Direction: #' The computed count has the property that if `start <= end`, then #' `start + <= end`. Similarly, if `start >= end`, then #' `start + >= end`. In other words, the comparison direction between #' `start` and `end` will never change after adding the count to `start`. This #' makes this function useful for repeated count computations at #' increasingly fine precisions. #' #' @inheritParams calendar_group #' #' @param start,end `[clock_calendar]` #' #' A pair of calendar vectors. These will be recycled to their common size. #' #' @return An integer representing the number of `precision` units between #' `start` and `end`. #' #' @name calendar-count-between #' @examples #' # Number of whole years between these dates #' x <- year_month_day(2000, 01, 05) #' y <- year_month_day(2005, 01, 04:06) #' #' # Note that `2000-01-05 -> 2005-01-04` is only 4 full years #' calendar_count_between(x, y, "year") NULL #' @rdname calendar-count-between #' @export calendar_count_between <- function(start, end, precision, ..., n = 1L) { check_calendar(start) UseMethod("calendar_count_between") } #' @export calendar_count_between.clock_calendar <- function(start, end, precision, ..., n = 1L) { check_dots_empty0(...) check_calendar(end) size <- vec_size_common(start = start, end = end) args <- vec_cast_common(start = start, end = end) args <- vec_recycle_common(!!!args, .size = size) start <- args[[1]] end <- args[[2]] check_number_whole(n, min = 0) n <- vec_cast(n, integer()) check_precision(precision) precision_int <- precision_to_integer(precision) if (calendar_precision_attribute(start) < precision_int) { start_precision <- precision_to_string(calendar_precision_attribute(start)) message <- paste0( "Precision of inputs ({.str {start_precision}}) must be at least as precise ", "as {.arg precision} ({.str {precision}})." ) cli::cli_abort(message) } args <- calendar_count_between_standardize_precision_n(start, precision, n) precision <- args$precision n <- args$n # Core computation to get the difference (pre-adjustment). # Result is an integer because it represents a count of duration units. out <- calendar_count_between_compute(start, end, precision) # Comparison proxy, truncated to avoid fields already when computing `out` args <- calendar_count_between_proxy_compare(start, end, precision) start_proxy <- args$start end_proxy <- args$end if (ncol(start_proxy) == 0L) { # vctrs bug with vec_compare()? # https://github.com/r-lib/vctrs/issues/1500 comparison <- vec_rep(0L, size) } else { comparison <- vec_compare(end_proxy, start_proxy) } # - When `start > end` and the non-year portion of `start < end`, add 1 # - When `start < end` and the non-year portion of `start > end`, subtract 1 adjustment <- vec_rep(-1L, size) adjustment[start > end] <- 1L adjustment[comparison != adjustment] <- 0L out <- out + adjustment if (n != 1L) { out <- out %/% n } out } # Internal generic calendar_count_between_standardize_precision_n <- function(x, precision, n) { UseMethod("calendar_count_between_standardize_precision_n") } # Internal generic calendar_count_between_compute <- function(start, end, precision) { UseMethod("calendar_count_between_compute") } # Internal generic calendar_count_between_proxy_compare <- function(start, end, precision) { UseMethod("calendar_count_between_proxy_compare") } # ------------------------------------------------------------------------------ #' Spanning sequence: calendars #' #' @description #' `calendar_spanning_seq()` generates a regular sequence along the span of #' `x`, i.e. along `[min(x), max(x)]`. The sequence is generated at the #' precision of `x`. #' #' Importantly, sequences can only be generated if the underlying [seq()] method #' for the calendar in question supports a `from` and `to` value at the same #' precision as `x`. For example, you can't compute a day precision spanning #' sequence for a [year_month_day()] calendar (you can only compute a year #' and month one). To create a day precision sequence, you'd have to convert to #' a time-point first. See the individual [seq()] method documentation to learn #' what precisions are allowed. #' #' @details #' Missing values are automatically removed before the sequence is generated. #' #' If you need more precise sequence generation, call [range()] and [seq()] #' directly. #' #' @param x `[clock_calendar]` #' #' A calendar vector. #' #' @return A sequence along `[min(x), max(x)]`. #' #' @export #' @examples #' x <- year_month_day(c(2019, 2022, 2020), c(2, 5, 3)) #' x #' #' # Month precision spanning sequence #' calendar_spanning_seq(x) #' #' # Quarter precision: #' x <- year_quarter_day(c(2005, 2006, 2003), c(4, 2, 3)) #' calendar_spanning_seq(x) #' #' # Can't generate sequences if `seq()` doesn't allow the precision #' x <- year_month_day(2019, c(1, 2, 1), c(20, 3, 25)) #' try(calendar_spanning_seq(x)) #' #' # Generally this means you need to convert to a time point and use #' # `time_point_spanning_seq()` instead #' time_point_spanning_seq(as_sys_time(x)) calendar_spanning_seq <- function(x) { check_calendar(x) spanning_seq_impl(x) } # ------------------------------------------------------------------------------ #' Precision: calendar #' #' `calendar_precision()` extracts the precision from a calendar object. It #' returns the precision as a single string. #' #' @param x `[clock_calendar]` #' #' A calendar. #' #' @return A single string holding the precision of the calendar. #' #' @export #' @examples #' calendar_precision(year_month_day(2019)) #' calendar_precision(year_month_day(2019, 1, 1)) #' calendar_precision(year_quarter_day(2019, 3)) calendar_precision <- function(x) { check_calendar(x) UseMethod("calendar_precision") } #' @export calendar_precision.clock_calendar <- function(x) { precision <- calendar_precision_attribute(x) precision <- precision_to_string(precision) precision } # ------------------------------------------------------------------------------ # Internal generic calendar_name <- function(x) { UseMethod("calendar_name") } # ------------------------------------------------------------------------------ # Internal generic calendar_is_precision <- function(x, precision) { UseMethod("calendar_is_precision") } calendar_check_precision <- function(x, precision, ..., error_call = caller_env()) { if (calendar_is_precision(x, precision)) { return(invisible(NULL)) } message <- paste0( "{.arg precision} must be a valid precision for a {.cls {calendar_name(x)}}, ", "not {.str {precision_to_string(precision)}}." ) cli::cli_abort(message, call = error_call) } # ------------------------------------------------------------------------------ calendar_precision_attribute <- function(x) { attr(x, "precision", exact = TRUE) } # ------------------------------------------------------------------------------ calendar_check_minimum_precision <- function(x, precision, ..., arg = caller_arg(x), call = caller_env()) { x_precision <- calendar_precision_attribute(x) if (x_precision >= precision) { return(invisible(NULL)) } message <- c( "Can't perform this operation because of the precision of {.arg {arg}}.", i = "The precision of {.arg {arg}} must be at least {.str {precision_to_string(precision)}}.", i = "{.arg {arg}} has a precision of {.str {precision_to_string(x_precision)}}." ) cli::cli_abort(message, call = call) } calendar_check_exact_precision <- function(x, precision, ..., arg = caller_arg(x), call = caller_env()) { x_precision <- calendar_precision_attribute(x) if (x_precision %in% precision) { return(invisible(NULL)) } precision <- vapply(precision, precision_to_string, character(1)) message <- c( "Can't perform this operation because of the precision of {.arg {arg}}.", i = "The precision of {.arg {arg}} must be {.or {.str {precision}}}.", i = "{.arg {arg}} has a precision of {.str {precision_to_string(x_precision)}}." ) cli::cli_abort(message, call = call) } # For use in calendar constructor helpers calendar_check_subsecond_precision <- function(subsecond_precision, ..., call = caller_env()) { if (is_null(subsecond_precision)) { cli::cli_abort( "When {.arg subsecond} is provided, {.arg subsecond_precision} must also be specified.", call = call ) } check_precision_subsecond(subsecond_precision, call = call) } # ------------------------------------------------------------------------------ calendar_check_no_invalid <- function(x, ..., arg = caller_arg(x), call = caller_env()) { if (!invalid_any(x)) { return(invisible(NULL)) } loc <- invalid_detect(x) loc <- which(loc) message <- c( "Can't convert {.arg {arg}} to another type because some dates are invalid.", i = "The following locations are invalid: {loc}.", i = "Resolve invalid dates with {.fn invalid_resolve}." ) cli::cli_abort(message, call = call) } # ------------------------------------------------------------------------------ calendar_ptype_full <- function(x, class) { precision <- calendar_precision_attribute(x) precision <- precision_to_string(precision) paste0(class, "<", precision, ">") } calendar_ptype_abbr <- function(x, abbr) { precision <- calendar_precision_attribute(x) precision <- precision_to_string(precision) precision <- precision_abbr(precision) paste0(abbr, "<", precision, ">") } # ------------------------------------------------------------------------------ arith_calendar_and_missing <- function(op, x, y, ...) { switch ( op, "+" = x, stop_incompatible_op(op, x, y, ...) ) } arith_calendar_and_calendar <- function(op, x, y, ..., calendar_minus_calendar_fn) { switch ( op, "-" = calendar_minus_calendar_fn(op, x, y, ...), stop_incompatible_op(op, x, y, ...) ) } arith_calendar_and_duration <- function(op, x, y, ...) { switch ( op, "+" = add_duration(x, y), "-" = add_duration(x, -y), stop_incompatible_op(op, x, y, ...) ) } arith_duration_and_calendar <- function(op, x, y, ...) { switch ( op, "+" = add_duration(y, x, swapped = TRUE), "-" = stop_incompatible_op(op, x, y, details = "Can't subtract a calendar from a duration.", ...), stop_incompatible_op(op, x, y, ...) ) } arith_calendar_and_numeric <- function(op, x, y, ...) { switch ( op, "+" = add_duration(x, duration_helper(y, calendar_precision_attribute(x), retain_names = TRUE)), "-" = add_duration(x, duration_helper(-y, calendar_precision_attribute(x), retain_names = TRUE)), stop_incompatible_op(op, x, y, ...) ) } arith_numeric_and_calendar <- function(op, x, y, ...) { switch ( op, "+" = add_duration(y, duration_helper(x, calendar_precision_attribute(y), retain_names = TRUE), swapped = TRUE), "-" = stop_incompatible_op(op, x, y, details = "Can't subtract a calendar from a duration.", ...), stop_incompatible_op(op, x, y, ...) ) } # ------------------------------------------------------------------------------ #' @export as_year_month_day.clock_calendar <- function(x, ...) { check_dots_empty0(...) as_year_month_day(as_sys_time(x)) } #' @export as_year_month_weekday.clock_calendar <- function(x, ...) { check_dots_empty0(...) as_year_month_weekday(as_sys_time(x)) } #' @export as_year_week_day.clock_calendar <- function(x, ..., start = NULL) { check_dots_empty0(...) as_year_week_day(as_sys_time(x), start = start) } #' @export as_iso_year_week_day.clock_calendar <- function(x, ...) { check_dots_empty0(...) as_iso_year_week_day(as_sys_time(x)) } #' @export as_year_day.clock_calendar <- function(x, ...) { check_dots_empty0(...) as_year_day(as_sys_time(x)) } #' @export as_year_quarter_day.clock_calendar <- function(x, ..., start = NULL) { check_dots_empty0(...) as_year_quarter_day(as_sys_time(x), start = start) } # ------------------------------------------------------------------------------ #' @export add_weeks.clock_calendar <- function(x, n, ...) { stop_clock_unsupported(x, details = advice_convert_to_time_point()) } #' @export add_days.clock_calendar <- function(x, n, ...) { stop_clock_unsupported(x, details = advice_convert_to_time_point()) } #' @export add_hours.clock_calendar <- function(x, n, ...) { stop_clock_unsupported(x, details = advice_convert_to_time_point()) } #' @export add_minutes.clock_calendar <- function(x, n, ...) { stop_clock_unsupported(x, details = advice_convert_to_time_point()) } #' @export add_seconds.clock_calendar <- function(x, n, ...) { stop_clock_unsupported(x, details = advice_convert_to_time_point()) } #' @export add_milliseconds.clock_calendar <- function(x, n, ...) { stop_clock_unsupported(x, details = advice_convert_to_time_point()) } #' @export add_microseconds.clock_calendar <- function(x, n, ...) { stop_clock_unsupported(x, details = advice_convert_to_time_point()) } #' @export add_nanoseconds.clock_calendar <- function(x, n, ...) { stop_clock_unsupported(x, details = advice_convert_to_time_point()) } advice_convert_to_time_point <- function() { c( i = "Do you need to convert to a time point first?", i = cli::format_inline("Use {.fn as_naive_time} or {.fn as_sys_time} to convert to a time point.") ) } # ------------------------------------------------------------------------------ calendar_minimum <- function(precision, year) { out <- calendar_widen(year, precision) out <- calendar_start(out, "year") out } calendar_maximum <- function(precision, year) { out <- calendar_widen(year, precision) out <- calendar_end(out, "year") out } # ------------------------------------------------------------------------------ field_year <- function(x) { # The `year` field is the first field of every calendar type, which makes # it the field that names are on. When extracting the field, we don't ever # want the names to come with it. out <- field(x, "year") names(out) <- NULL out } field_quarter <- function(x) { field(x, "quarter") } field_month <- function(x) { field(x, "month") } field_week <- function(x) { field(x, "week") } field_day <- function(x) { field(x, "day") } field_hour <- function(x) { field(x, "hour") } field_minute <- function(x) { field(x, "minute") } field_second <- function(x) { field(x, "second") } field_subsecond <- function(x) { field(x, "subsecond") } field_index <- function(x) { field(x, "index") } is_calendar <- function(x) { inherits(x, "clock_calendar") } check_calendar <- function(x, ..., arg = caller_arg(x), call = caller_env()) { check_inherits(x, what = "clock_calendar", arg = arg, call = call) } clock/R/setters.R0000644000176200001440000001137714423730227013377 0ustar liggesusers#' Calendar setters #' #' @description #' This family of functions sets fields in a calendar vector. Each #' calendar has its own set of supported setters, which are documented on #' their own help page: #' #' - [year-month-day][year-month-day-setters] #' #' - [year-month-weekday][year-month-weekday-setters] #' #' - [year-week-day][year-week-day-setters] #' #' - [iso-year-week-day][iso-year-week-day-setters] #' #' - [year-quarter-day][year-quarter-day-setters] #' #' - [year-day][year-day-setters] #' #' There are also convenience methods for setting certain components #' directly on R's native date and date-time types. #' #' - [dates (Date)][Date-setters] #' #' - [date-times (POSIXct / POSIXlt)][posixt-setters] #' #' Some general rules about setting components on calendar types: #' #' - You can only set components that are relevant to the calendar type that #' you are working with. For example, you can't set the quarter of a #' year-month-day type. You'd have to convert to year-quarter-day first. #' #' - You can set a component that is at the current precision, or one level #' of precision more precise than the current precision. For example, #' you can set the day field of a month precision year-month-day type, #' but not the hour field. #' #' - Setting a component can result in an _invalid date_, such as #' `set_day(year_month_day(2019, 02), 31)`, as long as it is eventually #' resolved either manually or with a strategy from [invalid_resolve()]. #' #' - With sub-second precisions, you can only set the component corresponding #' to the precision that you are at. For example, you can set the nanoseconds #' of the second while at nanosecond precision, but not milliseconds. #' #' @details #' You cannot set components directly on a time point type, such as #' sys-time or naive-time. Convert it to a calendar type first. Similarly, #' a zoned-time must be converted to either a sys-time or naive-time, and #' then to a calendar type, to be able to set components on it. #' #' @inheritParams rlang::args_dots_empty #' #' @param x `[object]` #' #' An object to set the component for. #' #' @param value `[integer]` #' #' The value to set the component to. #' #' @return `x` with the component set. #' #' @name clock-setters #' @examples #' x <- year_month_day(2019, 1:3) #' #' # Set the day #' set_day(x, 12:14) #' #' # Set to the "last" day of the month #' set_day(x, "last") NULL #' @rdname clock-setters #' @export set_year <- function(x, value, ...) { UseMethod("set_year") } #' @rdname clock-setters #' @export set_quarter <- function(x, value, ...) { UseMethod("set_quarter") } #' @rdname clock-setters #' @export set_month <- function(x, value, ...) { UseMethod("set_month") } #' @rdname clock-setters #' @export set_week <- function(x, value, ...) { UseMethod("set_week") } #' @rdname clock-setters #' @export set_day <- function(x, value, ...) { UseMethod("set_day") } #' @rdname clock-setters #' @export set_hour <- function(x, value, ...) { UseMethod("set_hour") } #' @rdname clock-setters #' @export set_minute <- function(x, value, ...) { UseMethod("set_minute") } #' @rdname clock-setters #' @export set_second <- function(x, value, ...) { UseMethod("set_second") } #' @rdname clock-setters #' @export set_millisecond <- function(x, value, ...) { UseMethod("set_millisecond") } #' @rdname clock-setters #' @export set_microsecond <- function(x, value, ...) { UseMethod("set_microsecond") } #' @rdname clock-setters #' @export set_nanosecond <- function(x, value, ...) { UseMethod("set_nanosecond") } #' @rdname clock-setters #' @export set_index <- function(x, value, ...) { UseMethod("set_index") } # ------------------------------------------------------------------------------ #' @export set_year.default <- function(x, value, ...) { stop_clock_unsupported(x) } #' @export set_quarter.default <- function(x, value, ...) { stop_clock_unsupported(x) } #' @export set_month.default <- function(x, value, ...) { stop_clock_unsupported(x) } #' @export set_week.default <- function(x, value, ...) { stop_clock_unsupported(x) } #' @export set_day.default <- function(x, value, ...) { stop_clock_unsupported(x) } #' @export set_hour.default <- function(x, value, ...) { stop_clock_unsupported(x) } #' @export set_minute.default <- function(x, value, ...) { stop_clock_unsupported(x) } #' @export set_second.default <- function(x, value, ...) { stop_clock_unsupported(x) } #' @export set_millisecond.default <- function(x, value, ...) { stop_clock_unsupported(x) } #' @export set_microsecond.default <- function(x, value, ...) { stop_clock_unsupported(x) } #' @export set_nanosecond.default <- function(x, value, ...) { stop_clock_unsupported(x) } #' @export set_index.default <- function(x, value, ...) { stop_clock_unsupported(x) } clock/R/import-standalone-obj-type.R0000644000176200001440000002072714427165604017101 0ustar liggesusers# Standalone file: do not edit by hand # Source: # ---------------------------------------------------------------------- # # --- # repo: r-lib/rlang # file: standalone-obj-type.R # last-updated: 2023-05-01 # license: https://unlicense.org # imports: rlang (>= 1.1.0) # --- # # ## Changelog # # 2023-05-01: # - `obj_type_friendly()` now only displays the first class of S3 objects. # # 2023-03-30: # - `stop_input_type()` now handles `I()` input literally in `arg`. # # 2022-10-04: # - `obj_type_friendly(value = TRUE)` now shows numeric scalars # literally. # - `stop_friendly_type()` now takes `show_value`, passed to # `obj_type_friendly()` as the `value` argument. # # 2022-10-03: # - Added `allow_na` and `allow_null` arguments. # - `NULL` is now backticked. # - Better friendly type for infinities and `NaN`. # # 2022-09-16: # - Unprefixed usage of rlang functions with `rlang::` to # avoid onLoad issues when called from rlang (#1482). # # 2022-08-11: # - Prefixed usage of rlang functions with `rlang::`. # # 2022-06-22: # - `friendly_type_of()` is now `obj_type_friendly()`. # - Added `obj_type_oo()`. # # 2021-12-20: # - Added support for scalar values and empty vectors. # - Added `stop_input_type()` # # 2021-06-30: # - Added support for missing arguments. # # 2021-04-19: # - Added support for matrices and arrays (#141). # - Added documentation. # - Added changelog. # # nocov start #' Return English-friendly type #' @param x Any R object. #' @param value Whether to describe the value of `x`. Special values #' like `NA` or `""` are always described. #' @param length Whether to mention the length of vectors and lists. #' @return A string describing the type. Starts with an indefinite #' article, e.g. "an integer vector". #' @noRd obj_type_friendly <- function(x, value = TRUE) { if (is_missing(x)) { return("absent") } if (is.object(x)) { if (inherits(x, "quosure")) { type <- "quosure" } else { type <- class(x)[[1L]] } return(sprintf("a <%s> object", type)) } if (!is_vector(x)) { return(.rlang_as_friendly_type(typeof(x))) } n_dim <- length(dim(x)) if (!n_dim) { if (!is_list(x) && length(x) == 1) { if (is_na(x)) { return(switch( typeof(x), logical = "`NA`", integer = "an integer `NA`", double = if (is.nan(x)) { "`NaN`" } else { "a numeric `NA`" }, complex = "a complex `NA`", character = "a character `NA`", .rlang_stop_unexpected_typeof(x) )) } show_infinites <- function(x) { if (x > 0) { "`Inf`" } else { "`-Inf`" } } str_encode <- function(x, width = 30, ...) { if (nchar(x) > width) { x <- substr(x, 1, width - 3) x <- paste0(x, "...") } encodeString(x, ...) } if (value) { if (is.numeric(x) && is.infinite(x)) { return(show_infinites(x)) } if (is.numeric(x) || is.complex(x)) { number <- as.character(round(x, 2)) what <- if (is.complex(x)) "the complex number" else "the number" return(paste(what, number)) } return(switch( typeof(x), logical = if (x) "`TRUE`" else "`FALSE`", character = { what <- if (nzchar(x)) "the string" else "the empty string" paste(what, str_encode(x, quote = "\"")) }, raw = paste("the raw value", as.character(x)), .rlang_stop_unexpected_typeof(x) )) } return(switch( typeof(x), logical = "a logical value", integer = "an integer", double = if (is.infinite(x)) show_infinites(x) else "a number", complex = "a complex number", character = if (nzchar(x)) "a string" else "\"\"", raw = "a raw value", .rlang_stop_unexpected_typeof(x) )) } if (length(x) == 0) { return(switch( typeof(x), logical = "an empty logical vector", integer = "an empty integer vector", double = "an empty numeric vector", complex = "an empty complex vector", character = "an empty character vector", raw = "an empty raw vector", list = "an empty list", .rlang_stop_unexpected_typeof(x) )) } } vec_type_friendly(x) } vec_type_friendly <- function(x, length = FALSE) { if (!is_vector(x)) { abort("`x` must be a vector.") } type <- typeof(x) n_dim <- length(dim(x)) add_length <- function(type) { if (length && !n_dim) { paste0(type, sprintf(" of length %s", length(x))) } else { type } } if (type == "list") { if (n_dim < 2) { return(add_length("a list")) } else if (is.data.frame(x)) { return("a data frame") } else if (n_dim == 2) { return("a list matrix") } else { return("a list array") } } type <- switch( type, logical = "a logical %s", integer = "an integer %s", numeric = , double = "a double %s", complex = "a complex %s", character = "a character %s", raw = "a raw %s", type = paste0("a ", type, " %s") ) if (n_dim < 2) { kind <- "vector" } else if (n_dim == 2) { kind <- "matrix" } else { kind <- "array" } out <- sprintf(type, kind) if (n_dim >= 2) { out } else { add_length(out) } } .rlang_as_friendly_type <- function(type) { switch( type, list = "a list", NULL = "`NULL`", environment = "an environment", externalptr = "a pointer", weakref = "a weak reference", S4 = "an S4 object", name = , symbol = "a symbol", language = "a call", pairlist = "a pairlist node", expression = "an expression vector", char = "an internal string", promise = "an internal promise", ... = "an internal dots object", any = "an internal `any` object", bytecode = "an internal bytecode object", primitive = , builtin = , special = "a primitive function", closure = "a function", type ) } .rlang_stop_unexpected_typeof <- function(x, call = caller_env()) { abort( sprintf("Unexpected type <%s>.", typeof(x)), call = call ) } #' Return OO type #' @param x Any R object. #' @return One of `"bare"` (for non-OO objects), `"S3"`, `"S4"`, #' `"R6"`, or `"R7"`. #' @noRd obj_type_oo <- function(x) { if (!is.object(x)) { return("bare") } class <- inherits(x, c("R6", "R7_object"), which = TRUE) if (class[[1]]) { "R6" } else if (class[[2]]) { "R7" } else if (isS4(x)) { "S4" } else { "S3" } } #' @param x The object type which does not conform to `what`. Its #' `obj_type_friendly()` is taken and mentioned in the error message. #' @param what The friendly expected type as a string. Can be a #' character vector of expected types, in which case the error #' message mentions all of them in an "or" enumeration. #' @param show_value Passed to `value` argument of `obj_type_friendly()`. #' @param ... Arguments passed to [abort()]. #' @inheritParams args_error_context #' @noRd stop_input_type <- function(x, what, ..., allow_na = FALSE, allow_null = FALSE, show_value = TRUE, arg = caller_arg(x), call = caller_env()) { # From standalone-cli.R cli <- env_get_list( nms = c("format_arg", "format_code"), last = topenv(), default = function(x) sprintf("`%s`", x), inherit = TRUE ) if (allow_na) { what <- c(what, cli$format_code("NA")) } if (allow_null) { what <- c(what, cli$format_code("NULL")) } if (length(what)) { what <- oxford_comma(what) } if (inherits(arg, "AsIs")) { format_arg <- identity } else { format_arg <- cli$format_arg } message <- sprintf( "%s must be %s, not %s.", format_arg(arg), what, obj_type_friendly(x, value = show_value) ) abort(message, ..., call = call, arg = arg) } oxford_comma <- function(chr, sep = ", ", final = "or") { n <- length(chr) if (n < 2) { return(chr) } head <- chr[seq_len(n - 1)] last <- chr[n] head <- paste(head, collapse = sep) # Write a or b. But a, b, or c. if (n > 2) { paste0(head, sep, final, " ", last) } else { paste0(head, " ", final, " ", last) } } # nocov end clock/R/week-year-week-day.R0000644000176200001440000010766714427270231015311 0ustar liggesusers#' Calendar: year-week-day #' #' @description #' `year_week_day()` constructs a calendar from the year, week number, #' week day, and the `start` of the week. #' #' Using `start = clock_weekdays$monday` represents the ISO week calendar and #' is equivalent to using [iso_year_week_day()]. #' #' Using `start = clock_weekdays$sunday` is how Epidemiologists encode their #' week-based data. #' #' @details #' Fields are recycled against each other using #' [tidyverse recycling rules][vctrs::vector_recycling_rules]. #' #' Fields are collected in order until the first `NULL` field is located. No #' fields after the first `NULL` field are used. #' #' @inheritParams rlang::args_dots_empty #' @inheritParams year_month_day #' #' @param year `[integer]` #' #' The year. Values `[-32767, 32767]` are generally allowed. #' #' @param week `[integer / "last" / NULL]` #' #' The week. Values `[1, 53]` are allowed. #' #' If `"last"`, then the last week of the year is returned. #' #' @param day `[integer / NULL]` #' #' The day of the week. Values `[1, 7]` are allowed, with `1 = start of week` #' and `7 = end of week`, in accordance with `start`. #' #' @param start `[integer(1) / NULL]` #' #' The day to consider the start of the week. 1 = Sunday and 7 = Saturday. #' #' Use [clock_weekdays] for a readable way to specify the start. #' #' If `NULL`, a `start` of Sunday will be used. #' #' @return A year-week-day calendar vector. #' #' @export #' @examples #' # Year-week #' x <- year_week_day(2019:2025, "last") #' x #' #' # Start the week on Monday #' y <- year_week_day(2019:2025, "last", start = clock_weekdays$monday) #' y #' #' # Last days of the year #' as_year_month_day(set_day(x, 7)) #' as_year_month_day(set_day(y, 7)) year_week_day <- function(year, week = NULL, day = NULL, hour = NULL, minute = NULL, second = NULL, subsecond = NULL, ..., start = NULL, subsecond_precision = NULL) { check_dots_empty() start <- week_validate_start(start) # Stop on the first `NULL` argument if (is_null(week)) { precision <- PRECISION_YEAR fields <- list(year = year) } else if (is_null(day)) { precision <- PRECISION_WEEK fields <- list(year = year, week = week) } else if (is_null(hour)) { precision <- PRECISION_DAY fields <- list(year = year, week = week, day = day) } else if (is_null(minute)) { precision <- PRECISION_HOUR fields <- list(year = year, week = week, day = day, hour = hour) } else if (is_null(second)) { precision <- PRECISION_MINUTE fields <- list(year = year, week = week, day = day, hour = hour, minute = minute) } else if (is_null(subsecond)) { precision <- PRECISION_SECOND fields <- list(year = year, week = week, day = day, hour = hour, minute = minute, second = second) } else { calendar_check_subsecond_precision(subsecond_precision) precision <- precision_to_integer(subsecond_precision) fields <- list(year = year, week = week, day = day, hour = hour, minute = minute, second = second, subsecond = subsecond) } if (is_last(fields$week)) { fields$week <- 1L last <- TRUE } else { last <- FALSE } fields <- vec_cast_common(!!!fields, .to = integer()) if (precision >= PRECISION_YEAR) { check_between_year(fields$year, arg = "year") } if (precision >= PRECISION_WEEK) { check_between_week(fields$week, arg = "week") } if (precision >= PRECISION_DAY) { check_between_day_of_week(fields$day, arg = "day") } if (precision >= PRECISION_HOUR) { check_between_hour(fields$hour, arg = "hour") } if (precision >= PRECISION_MINUTE) { check_between_minute(fields$minute, arg = "minute") } if (precision >= PRECISION_SECOND) { check_between_second(fields$second, arg = "second") } if (precision > PRECISION_SECOND) { check_between_subsecond(fields$subsecond, precision, arg = "subsecond") } fields <- vec_recycle_common(!!!fields) fields <- df_list_propagate_missing(fields) names <- NULL out <- new_year_week_day_from_fields(fields, precision, start, names) if (last) { out <- set_week(out, "last") } out } # ------------------------------------------------------------------------------ #' @export vec_proxy.clock_year_week_day <- function(x, ...) { .Call(`_clock_clock_rcrd_proxy`, x) } #' @export vec_restore.clock_year_week_day <- function(x, to, ...) { .Call(`_clock_year_week_day_restore`, x, to) } # ------------------------------------------------------------------------------ #' @export format.clock_year_week_day <- function(x, ...) { out <- format_year_week_day_cpp( x, calendar_precision_attribute(x), week_start(x) ) names(out) <- names(x) out } #' @export vec_ptype_full.clock_year_week_day <- function(x, ...) { start <- week_start(x) start <- week_start_prettify(start, abbreviate = FALSE) class <- paste0("year_week_day<", start, ">") calendar_ptype_full(x, class) } #' @export vec_ptype_abbr.clock_year_week_day <- function(x, ...) { start <- week_start(x) start <- week_start_prettify(start, abbreviate = TRUE) class <- paste0("ywd<", start, ">") calendar_ptype_abbr(x, class) } # ------------------------------------------------------------------------------ #' Is `x` a year-week-day? #' #' Check if `x` is a year-week-day. #' #' @param x `[object]` #' #' An object. #' #' @return Returns `TRUE` if `x` inherits from `"clock_year_week_day"`, #' otherwise returns `FALSE`. #' #' @export #' @examples #' is_year_week_day(year_week_day(2019)) is_year_week_day <- function(x) { inherits(x, "clock_year_week_day") } # ------------------------------------------------------------------------------ #' @export vec_ptype.clock_year_week_day <- function(x, ...) { names <- NULL precision <- calendar_precision_attribute(x) start <- week_start(x) f <- integer() fields <- switch( precision + 1L, list(year = f), abort("Internal error: Invalid precision"), abort("Internal error: Invalid precision"), list(year = f, week = f), list(year = f, week = f, day = f), list(year = f, week = f, day = f, hour = f), list(year = f, week = f, day = f, hour = f, minute = f), list(year = f, week = f, day = f, hour = f, minute = f, second = f), list(year = f, week = f, day = f, hour = f, minute = f, second = f, subsecond = f), list(year = f, week = f, day = f, hour = f, minute = f, second = f, subsecond = f), list(year = f, week = f, day = f, hour = f, minute = f, second = f, subsecond = f), abort("Internal error: Invalid precision.") ) new_year_week_day_from_fields(fields, precision, start, names) } #' @export vec_ptype2.clock_year_week_day.clock_year_week_day <- function(x, y, ...) { if (week_start(x) != week_start(y)) { stop_incompatible_type(x, y, ..., details = "Can't combine week types with different `start`s.") } ptype2_calendar_and_calendar(x, y, ...) } #' @export vec_cast.clock_year_week_day.clock_year_week_day <- function(x, to, ...) { if (week_start(x) != week_start(to)) { stop_incompatible_cast(x, to, ..., details = "Can't cast between week types with different `start`s.") } cast_calendar_to_calendar(x, to, ...) } # ------------------------------------------------------------------------------ #' @export calendar_is_precision.clock_year_week_day <- function(x, precision) { year_week_day_is_precision(precision) } year_week_day_is_precision <- function(precision) { if (precision == PRECISION_YEAR || precision == PRECISION_WEEK) { TRUE } else if (precision >= PRECISION_DAY && precision <= PRECISION_NANOSECOND) { TRUE } else { FALSE } } # ------------------------------------------------------------------------------ #' @export invalid_detect.clock_year_week_day <- function(x) { precision <- calendar_precision_attribute(x) start <- week_start(x) if (precision < PRECISION_WEEK) { rep_along(x, FALSE) } else { year <- field_year(x) week <- field_week(x) invalid_detect_year_week_day_cpp(year, week, start) } } #' @export invalid_any.clock_year_week_day <- function(x) { precision <- calendar_precision_attribute(x) start <- week_start(x) if (precision < PRECISION_WEEK) { FALSE } else { year <- field_year(x) week <- field_week(x) invalid_any_year_week_day_cpp(year, week, start) } } #' @export invalid_count.clock_year_week_day <- function(x) { precision <- calendar_precision_attribute(x) start <- week_start(x) if (precision < PRECISION_WEEK) { 0L } else { year <- field_year(x) week <- field_week(x) invalid_count_year_week_day_cpp(year, week, start) } } #' @export invalid_resolve.clock_year_week_day <- function(x, ..., invalid = NULL) { check_dots_empty() precision <- calendar_precision_attribute(x) start <- week_start(x) invalid <- validate_invalid(invalid) if (precision < PRECISION_WEEK) { x } else { fields <- invalid_resolve_year_week_day_cpp(x, precision, start, invalid, current_env()) new_year_week_day_from_fields(fields, precision, start, names(x)) } } # ------------------------------------------------------------------------------ #' Getters: year-week-day #' #' @description #' These are year-week-day methods for the #' [getter generics][clock-getters]. #' #' - `get_year()` returns the year. Note that this can differ from the #' Gregorian year. #' #' - `get_week()` returns the week of the current year. #' #' - `get_day()` returns a value between 1-7 indicating the weekday of the #' current week, where `1 = start of week` and `7 = end of week`, in line with #' the chosen `start`. #' #' - There are sub-daily getters for extracting more precise components. #' #' @param x `[clock_year_week_day]` #' #' A year-week-day to get the component from. #' #' @return The component. #' #' @name year-week-day-getters #' @examples #' x <- year_week_day(2019, 50:52, 1:3) #' x #' #' # Get the week #' get_week(x) #' #' # Gets the weekday #' get_day(x) #' #' # Note that the year can differ from the Gregorian year #' iso <- year_week_day(2019, 1, 1, start = clock_weekdays$monday) #' ymd <- as_year_month_day(iso) #' #' get_year(iso) #' get_year(ymd) NULL #' @rdname year-week-day-getters #' @export get_year.clock_year_week_day <- function(x) { field_year(x) } #' @rdname year-week-day-getters #' @export get_week.clock_year_week_day <- function(x) { calendar_check_minimum_precision(x, PRECISION_WEEK) field_week(x) } #' @rdname year-week-day-getters #' @export get_day.clock_year_week_day <- function(x) { calendar_check_minimum_precision(x, PRECISION_DAY) field_day(x) } #' @rdname year-week-day-getters #' @export get_hour.clock_year_week_day <- function(x) { calendar_check_minimum_precision(x, PRECISION_HOUR) field_hour(x) } #' @rdname year-week-day-getters #' @export get_minute.clock_year_week_day <- function(x) { calendar_check_minimum_precision(x, PRECISION_MINUTE) field_minute(x) } #' @rdname year-week-day-getters #' @export get_second.clock_year_week_day <- function(x) { calendar_check_minimum_precision(x, PRECISION_SECOND) field_second(x) } #' @rdname year-week-day-getters #' @export get_millisecond.clock_year_week_day <- function(x) { calendar_check_exact_precision(x, PRECISION_MILLISECOND) field_subsecond(x) } #' @rdname year-week-day-getters #' @export get_microsecond.clock_year_week_day <- function(x) { calendar_check_exact_precision(x, PRECISION_MICROSECOND) field_subsecond(x) } #' @rdname year-week-day-getters #' @export get_nanosecond.clock_year_week_day <- function(x) { calendar_check_exact_precision(x, PRECISION_NANOSECOND) field_subsecond(x) } # ------------------------------------------------------------------------------ #' Setters: year-week-day #' #' @description #' These are year-week-day methods for the [setter generics][clock-setters]. #' #' - `set_year()` sets the year. #' #' - `set_week()` sets the week of the year. Valid values are in the range #' of `[1, 53]`. #' #' - `set_day()` sets the day of the week. Valid values are in the range of #' `[1, 7]`. #' #' - There are sub-daily setters for setting more precise components. #' #' @inheritParams rlang::args_dots_empty #' #' @param x `[clock_year_week_day]` #' #' A year-week-day vector. #' #' @param value `[integer / "last"]` #' #' The value to set the component to. #' #' For `set_week()`, this can also be `"last"` to adjust to the last #' week of the current year. #' #' @return `x` with the component set. #' #' @name year-week-day-setters #' @examples #' # Year precision vector #' x <- year_week_day(2019:2023) #' #' # Promote to week precision by setting the week #' # (Note that some weeks have 52 weeks, and others have 53) #' x <- set_week(x, "last") #' x #' #' # Set to an invalid week #' invalid <- set_week(x, 53) #' invalid #' #' # Here are the invalid ones (they only have 52 weeks) #' invalid[invalid_detect(invalid)] #' #' # Resolve the invalid dates by choosing the previous/next valid moment #' invalid_resolve(invalid, invalid = "previous") #' invalid_resolve(invalid, invalid = "next") NULL #' @rdname year-week-day-setters #' @export set_year.clock_year_week_day <- function(x, value, ...) { check_dots_empty() set_field_year_week_day(x, value, "year") } #' @rdname year-week-day-setters #' @export set_week.clock_year_week_day <- function(x, value, ...) { check_dots_empty() calendar_check_minimum_precision(x, PRECISION_YEAR) set_field_year_week_day(x, value, "week") } #' @rdname year-week-day-setters #' @export set_day.clock_year_week_day <- function(x, value, ...) { check_dots_empty() calendar_check_minimum_precision(x, PRECISION_WEEK) set_field_year_week_day(x, value, "day") } #' @rdname year-week-day-setters #' @export set_hour.clock_year_week_day <- function(x, value, ...) { check_dots_empty() calendar_check_minimum_precision(x, PRECISION_DAY) set_field_year_week_day(x, value, "hour") } #' @rdname year-week-day-setters #' @export set_minute.clock_year_week_day <- function(x, value, ...) { check_dots_empty() calendar_check_minimum_precision(x, PRECISION_HOUR) set_field_year_week_day(x, value, "minute") } #' @rdname year-week-day-setters #' @export set_second.clock_year_week_day <- function(x, value, ...) { check_dots_empty() calendar_check_minimum_precision(x, PRECISION_MINUTE) set_field_year_week_day(x, value, "second") } #' @rdname year-week-day-setters #' @export set_millisecond.clock_year_week_day <- function(x, value, ...) { check_dots_empty() calendar_check_exact_precision(x, c(PRECISION_SECOND, PRECISION_MILLISECOND)) set_field_year_week_day(x, value, "millisecond") } #' @rdname year-week-day-setters #' @export set_microsecond.clock_year_week_day <- function(x, value, ...) { check_dots_empty() calendar_check_exact_precision(x, c(PRECISION_SECOND, PRECISION_MICROSECOND)) set_field_year_week_day(x, value, "microsecond") } #' @rdname year-week-day-setters #' @export set_nanosecond.clock_year_week_day <- function(x, value, ...) { check_dots_empty() calendar_check_exact_precision(x, c(PRECISION_SECOND, PRECISION_NANOSECOND)) set_field_year_week_day(x, value, "nanosecond") } set_field_year_week_day <- function(x, value, component) { if (is_last(value) && identical(component, "week")) { return(set_field_year_week_day_last(x)) } precision_fields <- calendar_precision_attribute(x) precision_value <- year_week_day_component_to_precision(component) precision_out <- precision_common2(precision_fields, precision_value) start_out <- week_start(x) names_out <- names(x) value <- vec_cast(value, integer()) value <- unname(value) switch( component, year = check_between_year(value), week = check_between_week(value), day = check_between_day_of_week(value), hour = check_between_hour(value), minute = check_between_minute(value), second = check_between_second(value), millisecond = check_between_subsecond(value, PRECISION_MILLISECOND), microsecond = check_between_subsecond(value, PRECISION_MICROSECOND), nanosecond = check_between_subsecond(value, PRECISION_NANOSECOND), abort("Unknown `component`", .internal = TRUE) ) args <- vec_recycle_common(x = x, value = value) args <- df_list_propagate_missing(args) x <- args$x value <- args$value field <- year_week_day_component_to_field(component) out <- vec_unstructure(x) out[[field]] <- value new_year_week_day_from_fields(out, precision_out, start_out, names = names_out) } set_field_year_week_day_last <- function(x) { precision_fields <- calendar_precision_attribute(x) precision_out <- precision_common2(precision_fields, PRECISION_WEEK) start_out <- week_start(x) names_out <- names(x) year <- field_year(x) value <- get_year_week_day_last_cpp(year, start_out) out <- vec_unstructure(x) out[["week"]] <- value new_year_week_day_from_fields(out, precision_out, start_out, names = names_out) } # ------------------------------------------------------------------------------ #' @export calendar_name.clock_year_week_day <- function(x) { "year_week_day" } # ------------------------------------------------------------------------------ year_week_day_component_to_precision <- function(component) { switch( component, year = PRECISION_YEAR, week = PRECISION_WEEK, day = PRECISION_DAY, hour = PRECISION_HOUR, minute = PRECISION_MINUTE, second = PRECISION_SECOND, millisecond = PRECISION_MILLISECOND, microsecond = PRECISION_MICROSECOND, nanosecond = PRECISION_NANOSECOND, abort("Internal error: Unknown component name.") ) } year_week_day_component_to_field <- function(component) { switch ( component, year = component, week = component, day = component, hour = component, minute = component, second = component, millisecond = "subsecond", microsecond = "subsecond", nanosecond = "subsecond", abort("Internal error: Unknown component name.") ) } # ------------------------------------------------------------------------------ #' @rdname clock-arith #' @method vec_arith clock_year_week_day #' @export vec_arith.clock_year_week_day <- function(op, x, y, ...) { UseMethod("vec_arith.clock_year_week_day", y) } #' @method vec_arith.clock_year_week_day MISSING #' @export vec_arith.clock_year_week_day.MISSING <- function(op, x, y, ...) { arith_calendar_and_missing(op, x, y, ...) } #' @method vec_arith.clock_year_week_day clock_year_week_day #' @export vec_arith.clock_year_week_day.clock_year_week_day <- function(op, x, y, ...) { arith_calendar_and_calendar(op, x, y, ..., calendar_minus_calendar_fn = year_week_day_minus_year_week_day) } #' @method vec_arith.clock_year_week_day clock_duration #' @export vec_arith.clock_year_week_day.clock_duration <- function(op, x, y, ...) { arith_calendar_and_duration(op, x, y, ...) } #' @method vec_arith.clock_duration clock_year_week_day #' @export vec_arith.clock_duration.clock_year_week_day <- function(op, x, y, ...) { arith_duration_and_calendar(op, x, y, ...) } #' @method vec_arith.clock_year_week_day numeric #' @export vec_arith.clock_year_week_day.numeric <- function(op, x, y, ...) { arith_calendar_and_numeric(op, x, y, ...) } #' @method vec_arith.numeric clock_year_week_day #' @export vec_arith.numeric.clock_year_week_day <- function(op, x, y, ...) { arith_numeric_and_calendar(op, x, y, ...) } year_week_day_minus_year_week_day <- function(op, x, y, ...) { args <- vec_recycle_common(x = x, y = y) args <- vec_cast_common(!!!args) x <- args$x y <- args$y names <- names_common(x, y) start <- week_start(x) precision <- calendar_precision_attribute(x) if (precision > PRECISION_YEAR) { stop_incompatible_op(op, x, y, ...) } fields <- year_week_day_minus_year_week_day_cpp(x, y, precision, start) new_duration_from_fields(fields, precision, names = names) } # ------------------------------------------------------------------------------ #' Arithmetic: year-week-day #' #' @description #' These are year-week-day methods for the #' [arithmetic generics][clock-arithmetic]. #' #' - `add_years()` #' #' You cannot add weeks or days to a year-week-day calendar. Adding #' days is much more efficiently done by converting to a time point first #' by using [as_naive_time()] or [as_sys_time()]. Adding weeks is equally #' as efficient as adding 7 days. Additionally, adding weeks to an invalid #' year-week object (i.e. one set to the 53rd week, when that doesn't exist) #' would be undefined. #' #' @details #' `x` and `n` are recycled against each other using #' [tidyverse recycling rules][vctrs::vector_recycling_rules]. #' #' @inheritParams clock-arithmetic #' #' @param x `[clock_year_week_day]` #' #' A year-week-day vector. #' #' @return `x` after performing the arithmetic. #' #' @name year-week-day-arithmetic #' #' @examples #' x <- year_week_day(2019, 1, 1) #' add_years(x, 1:2) NULL #' @rdname year-week-day-arithmetic #' @export add_years.clock_year_week_day <- function(x, n, ...) { year_week_day_plus_duration(x, n, PRECISION_YEAR) } year_week_day_plus_duration <- function(x, n, n_precision, ..., error_call = caller_env()) { check_dots_empty0(...) start <- week_start(x) x_precision <- calendar_precision_attribute(x) n <- duration_collect_n(n, n_precision, error_call = error_call) size <- vec_size_common(x = x, n = n, .call = error_call) args <- vec_recycle_common(x = x, n = n, .size = size) x <- args$x n <- args$n names <- names_common(x, n) x <- vec_unstructure(x) if (n_precision == PRECISION_YEAR) { fields <- year_week_day_plus_years_cpp(x$year, start, n) x$year <- fields$year } else { abort("Unknown precision.", .internal = TRUE) } if (x_precision != n_precision) { x <- df_list_propagate_missing(x, size = size) } new_year_week_day_from_fields(x, x_precision, start, names = names) } # ------------------------------------------------------------------------------ #' Convert to year-week-day #' #' `as_year_week_day()` converts a vector to the year-week-day #' calendar. Time points, Dates, POSIXct, and other calendars can all be #' converted to year-week-day. #' #' @inheritParams rlang::args_dots_empty #' #' @param x `[vector]` #' #' A vector to convert to year-week-day. #' #' @param start `[integer(1) / NULL]` #' #' The day to consider the start of the week. 1 = Sunday and 7 = Saturday. #' #' If `NULL`: #' #' - If `x` is a year-week-day, it will be returned as is. #' #' - Otherwise, a `start` of Sunday will be used. #' #' @return A year-week-day vector. #' @export #' @examples #' # From Date #' as_year_week_day(as.Date("2019-01-01")) #' as_year_week_day(as.Date("2019-01-01"), start = clock_weekdays$monday) #' #' # From POSIXct, which assumes that the naive time is what should be converted #' as_year_week_day(as.POSIXct("2019-01-01 02:30:30", "America/New_York")) #' #' # From other calendars #' as_year_week_day(year_quarter_day(2019, quarter = 2, day = 50)) as_year_week_day <- function(x, ..., start = NULL) { UseMethod("as_year_week_day") } #' @export as_year_week_day.default <- function(x, ..., start = NULL) { stop_clock_unsupported_conversion(x, "clock_year_week_day") } #' @export as_year_week_day.clock_year_week_day <- function(x, ..., start = NULL) { check_dots_empty0(...) if (is_null(start)) { return(x) } start <- week_validate_start(start) x_start <- week_start(x) if (x_start == start) { return(x) } as_year_week_day(as_sys_time(x), start = start) } # ------------------------------------------------------------------------------ #' @export as_sys_time.clock_year_week_day <- function(x, ...) { check_dots_empty0(...) calendar_check_no_invalid(x) start <- week_start(x) precision <- calendar_precision_attribute(x) fields <- as_sys_time_year_week_day_cpp(x, precision, start) new_sys_time_from_fields(fields, precision, clock_rcrd_names(x)) } #' @export as_naive_time.clock_year_week_day <- function(x, ...) { check_dots_empty0(...) as_naive_time(as_sys_time(x)) } #' @export as.character.clock_year_week_day <- function(x, ...) { format(x) } # ------------------------------------------------------------------------------ #' @export calendar_leap_year.clock_year_week_day <- function(x) { year <- get_year(x) start <- week_start(x) year_week_day_leap_year_cpp(year, start) } # ------------------------------------------------------------------------------ #' Grouping: year-week-day #' #' @description #' This is a year-week-day method for the [calendar_group()] generic. #' #' Grouping for a year-week-day object can be done at any precision, as #' long as `x` is at least as precise as `precision`. #' #' @inheritParams calendar_group #' #' @param x `[clock_year_week_day]` #' #' A year-week-day vector. #' #' @param precision `[character(1)]` #' #' One of: #' #' - `"year"` #' - `"week"` #' - `"day"` #' - `"hour"` #' - `"minute"` #' - `"second"` #' - `"millisecond"` #' - `"microsecond"` #' - `"nanosecond"` #' #' @return `x` grouped at the specified `precision`. #' #' @name year-week-day-group #' #' @export #' @examples #' x <- year_week_day(2019, 1:52) #' #' # Group by 3 weeks #' calendar_group(x, "week", n = 3) #' #' y <- year_week_day(2000:2020, 1, 1) #' #' # Group by 2 years #' calendar_group(y, "year", n = 2) calendar_group.clock_year_week_day <- function(x, precision, ..., n = 1L) { n <- validate_calendar_group_n(n) x <- calendar_narrow(x, precision) check_precision(precision) precision <- precision_to_integer(precision) if (precision == PRECISION_YEAR) { value <- get_year(x) value <- group_component0(value, n) x <- set_year(x, value) return(x) } if (precision == PRECISION_WEEK) { value <- get_week(x) value <- group_component1(value, n) x <- set_week(x, value) return(x) } if (precision == PRECISION_DAY) { value <- get_day(x) value <- group_component1(value, n) x <- set_day(x, value) return(x) } x <- calendar_group_time(x, n, precision) x } # ------------------------------------------------------------------------------ #' Narrow: year-week-day #' #' This is a year-week-day method for the [calendar_narrow()] generic. It #' narrows a year-week-day vector to the specified `precision`. #' #' @inheritParams year-week-day-group #' #' @return `x` narrowed to the supplied `precision`. #' #' @name year-week-day-narrow #' #' @export #' @examples #' # Day precision #' x <- year_week_day(2019, 1, 5) #' x #' #' # Narrowed to week precision #' calendar_narrow(x, "week") calendar_narrow.clock_year_week_day <- function(x, precision) { check_precision(precision) precision <- precision_to_integer(precision) start <- week_start(x) out_fields <- list() x_fields <- unclass(x) if (precision >= PRECISION_YEAR) { out_fields[["year"]] <- x_fields[["year"]] } if (precision >= PRECISION_WEEK) { out_fields[["week"]] <- x_fields[["week"]] } if (precision >= PRECISION_DAY) { out_fields[["day"]] <- x_fields[["day"]] } out_fields <- calendar_narrow_time(out_fields, precision, x_fields) new_year_week_day_from_fields(out_fields, precision, start, names = names(x)) } # ------------------------------------------------------------------------------ #' Widen: year-week-day #' #' This is a year-week-day method for the [calendar_widen()] generic. It #' widens a year-week-day vector to the specified `precision`. #' #' @inheritParams year-week-day-group #' #' @return `x` widened to the supplied `precision`. #' #' @name year-week-day-widen #' #' @export #' @examples #' # Week precision #' x <- year_week_day(2019, 1, start = clock_weekdays$monday) #' x #' #' # Widen to day precision #' # In this calendar, the first day of the week is a Monday #' calendar_widen(x, "day") #' #' # Or second precision #' sec <- calendar_widen(x, "second") #' sec calendar_widen.clock_year_week_day <- function(x, precision) { x_precision <- calendar_precision_attribute(x) check_precision(precision) precision <- precision_to_integer(precision) if (precision >= PRECISION_WEEK && x_precision < PRECISION_WEEK) { x <- set_week(x, 1L) } if (precision >= PRECISION_DAY && x_precision < PRECISION_DAY) { x <- set_day(x, 1L) } x <- calendar_widen_time(x, x_precision, precision) x } # ------------------------------------------------------------------------------ #' Boundaries: year-week-day #' #' This is an year-week-day method for the [calendar_start()] and #' [calendar_end()] generics. They adjust components of a calendar to the #' start or end of a specified `precision`. #' #' @inheritParams year-week-day-group #' #' @return `x` at the same precision, but with some components altered to be #' at the boundary value. #' #' @name year-week-day-boundary #' #' @examples #' x <- year_week_day(2019:2020, 5, 6, 10) #' x #' #' # Compute the last moment of the last week of the year #' calendar_end(x, "year") #' #' # Compare that to just setting the week to `"last"`, #' # which doesn't affect the other components #' set_week(x, "last") NULL #' @rdname year-week-day-boundary #' @export calendar_start.clock_year_week_day <- function(x, precision) { x_precision <- calendar_precision_attribute(x) check_precision(precision) precision <- precision_to_integer(precision) calendar_start_end_checks(x, x_precision, precision, "start") if (precision <= PRECISION_YEAR && x_precision > PRECISION_YEAR) { x <- set_week(x, 1L) } if (precision <= PRECISION_WEEK && x_precision > PRECISION_WEEK) { x <- set_day(x, 1L) } x <- calendar_start_time(x, x_precision, precision) x } #' @rdname year-week-day-boundary #' @export calendar_end.clock_year_week_day <- function(x, precision) { x_precision <- calendar_precision_attribute(x) check_precision(precision) precision <- precision_to_integer(precision) calendar_start_end_checks(x, x_precision, precision, "end") if (precision <= PRECISION_YEAR && x_precision > PRECISION_YEAR) { x <- set_week(x, "last") } if (precision <= PRECISION_WEEK && x_precision > PRECISION_WEEK) { x <- set_day(x, 7L) } x <- calendar_end_time(x, x_precision, precision) x } # ------------------------------------------------------------------------------ #' Counting: year-week-day #' #' This is an year-week-day method for the [calendar_count_between()] #' generic. It counts the number of `precision` units between `start` and `end` #' (i.e., the number of years). #' #' @inheritParams calendar-count-between #' #' @param start,end `[clock_year_week_day]` #' #' A pair of year-week-day vectors. These will be recycled to their #' common size. #' #' @param precision `[character(1)]` #' #' One of: #' #' - `"year"` #' #' @inherit calendar-count-between return #' #' @name year-week-day-count-between #' #' @export #' @examples #' # Compute the number of whole years between two dates #' x <- year_week_day(2001, 1, 2) #' y <- year_week_day(2021, 1, c(1, 3)) #' calendar_count_between(x, y, "year") calendar_count_between.clock_year_week_day <- function(start, end, precision, ..., n = 1L) { NextMethod() } calendar_count_between_standardize_precision_n.clock_year_week_day <- function(x, precision, n) { check_precision(precision) precision_int <- precision_to_integer(precision) allowed_precisions <- c(PRECISION_YEAR) if (!(precision_int %in% allowed_precisions)) { abort("`precision` must be one of: 'year'.") } list(precision = precision, n = n) } calendar_count_between_compute.clock_year_week_day <- function(start, end, precision) { check_precision(precision) precision <- precision_to_integer(precision) if (precision == PRECISION_YEAR) { out <- get_year(end) - get_year(start) return(out) } abort("Internal error: `precision` should be 'year' at this point.") } calendar_count_between_proxy_compare.clock_year_week_day <- function(start, end, precision) { check_precision(precision) precision <- precision_to_integer(precision) start <- vec_proxy_compare(start) end <- vec_proxy_compare(end) if (precision >= PRECISION_YEAR) { start$year <- NULL end$year <- NULL } list(start = start, end = end) } # ------------------------------------------------------------------------------ #' Sequences: year-week-day #' #' @description #' This is a year-week-day method for the [seq()] generic. #' #' Sequences can only be generated for `"year"` precision #' year-week-day vectors. If you need to generate week-based sequences, #' you'll have to convert to a time point first. #' #' When calling `seq()`, exactly two of the following must be specified: #' - `to` #' - `by` #' - Either `length.out` or `along.with` #' #' @inheritParams seq.clock_duration #' #' @param from `[clock_year_week_day(1)]` #' #' A `"year"` precision year-week-day to start the sequence #' from. #' #' `from` is always included in the result. #' #' @param to `[clock_year_week_day(1) / NULL]` #' #' A `"year"` precision year-week-day to stop the sequence #' at. #' #' `to` is cast to the type of `from`. #' #' `to` is only included in the result if the resulting sequence divides #' the distance between `from` and `to` exactly. #' #' @return A sequence with the type of `from`. #' #' @export #' @examples #' # Yearly sequence #' x <- seq(year_week_day(2020), year_week_day(2026), by = 2) #' x #' #' # Which we can then set the week of. #' # Some years have 53 weeks, some have 52. #' set_week(x, "last") seq.clock_year_week_day <- function(from, to = NULL, by = NULL, length.out = NULL, along.with = NULL, ...) { precision <- calendar_precision_attribute(from) if (precision > PRECISION_YEAR) { abort("`from` must be 'year' precision.") } seq_impl( from = from, to = to, by = by, length.out = length.out, along.with = along.with, precision = precision, ... ) } # ------------------------------------------------------------------------------ #' @export clock_minimum.clock_year_week_day <- function(x) { names <- NULL start <- week_start(x) fields <- list(year = clock_calendar_year_minimum) year <- new_year_week_day_from_fields(fields, PRECISION_YEAR, start, names) precision <- calendar_precision_attribute(x) precision <- precision_to_string(precision) calendar_minimum(precision, year) } #' @export clock_maximum.clock_year_week_day <- function(x) { names <- NULL start <- week_start(x) fields <- list(year = clock_calendar_year_maximum) year <- new_year_week_day_from_fields(fields, PRECISION_YEAR, start, names) precision <- calendar_precision_attribute(x) precision <- precision_to_string(precision) calendar_maximum(precision, year) } # ------------------------------------------------------------------------------ week_start <- function(x) { attr(x, "start", exact = TRUE) } week_start_prettify <- function(start, ..., abbreviate = FALSE) { check_dots_empty() if (abbreviate) { c("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat")[start] } else { c("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday")[start] } } week_validate_start <- function(start, ..., error_call = caller_env()) { if (is_null(start)) { return(1L) } check_number_whole(start, min = 1, max = 7, call = error_call) start <- vec_cast(start, integer(), call = error_call) start } clock/R/time-point.R0000644000176200001440000011052714427270231013766 0ustar liggesusers is_time_point <- function(x) { inherits(x, "clock_time_point") } check_time_point <- function(x, ..., arg = caller_arg(x), call = caller_env()) { check_inherits(x, what = "clock_time_point", arg = arg, call = call) } # ------------------------------------------------------------------------------ time_point_clock_attribute <- function(x) { attr(x, "clock", exact = TRUE) } time_point_precision_attribute <- function(x) { attr(x, "precision", exact = TRUE) } time_point_duration <- function(x, retain_names = FALSE) { if (retain_names) { names <- clock_rcrd_names(x) } else { names <- NULL } precision <- time_point_precision_attribute(x) new_duration_from_fields(x, precision, names) } # ------------------------------------------------------------------------------ #' @export format.clock_time_point <- function(x, ..., format = NULL, locale = clock_locale()) { check_clock_locale(locale) clock <- time_point_clock_attribute(x) precision <- time_point_precision_attribute(x) if (is_null(format)) { format <- time_point_precision_format(precision) } labels <- locale$labels decimal_mark <- locale$decimal_mark out <- format_time_point_cpp( fields = x, clock = clock, format = format, precision_int = precision, month = labels$month, month_abbrev = labels$month_abbrev, weekday = labels$weekday, weekday_abbrev = labels$weekday_abbrev, am_pm = labels$am_pm, decimal_mark = decimal_mark ) names(out) <- clock_rcrd_names(x) out } time_point_precision_format <- function(precision) { precision <- precision_to_string(precision) switch( precision, day = "%Y-%m-%d", hour = "%Y-%m-%dT%H", minute = "%Y-%m-%dT%H:%M", second = "%Y-%m-%dT%H:%M:%S", millisecond = "%Y-%m-%dT%H:%M:%S", microsecond = "%Y-%m-%dT%H:%M:%S", nanosecond = "%Y-%m-%dT%H:%M:%S", abort("Unknown precision.") ) } # ------------------------------------------------------------------------------ time_point_parse <- function(x, format, precision, locale, clock, ..., error_call = caller_env()) { check_dots_empty0(...) check_character(x, call = error_call) if (is_null(format)) { format <- time_point_precision_format(precision) } check_clock_locale(locale, call = error_call) labels <- locale$labels mark <- locale$decimal_mark time_point_parse_cpp( x, format, precision, clock, labels$month, labels$month_abbrev, labels$weekday, labels$weekday_abbrev, labels$am_pm, mark ) } # ------------------------------------------------------------------------------ #' @export print.clock_time_point <- function(x, ..., max = NULL) { clock_print(x, max) } # - Each subclass implements a `format()` method # - Unlike vctrs, don't use `print(quote = FALSE)` since we want to match base R #' @export obj_print_data.clock_time_point <- function(x, ..., max) { if (vec_is_empty(x)) { return(invisible(x)) } x <- max_slice(x, max) out <- format(x) # Pass `max` to avoid base R's default footer print(out, max = max) invisible(x) } #' @export obj_print_footer.clock_time_point <- function(x, ..., max) { clock_print_footer(x, max) } # Align left to match pillar_shaft.Date # @export - lazy in .onLoad() pillar_shaft.clock_time_point <- function(x, ...) { out <- format(x) pillar::new_pillar_shaft_simple(out, align = "left") } # ------------------------------------------------------------------------------ #' @export vec_proxy.clock_time_point <- function(x, ...) { .Call(`_clock_clock_rcrd_proxy`, x) } #' @export vec_restore.clock_time_point <- function(x, to, ...) { .Call(`_clock_time_point_restore`, x, to) } # ------------------------------------------------------------------------------ time_point_ptype <- function(x, ..., type = c("full", "abbr")) { check_dots_empty0(...) type <- arg_match0(type, values = c("full", "abbr")) clock <- time_point_clock_attribute(x) clock <- clock_to_string(clock) precision <- time_point_precision_attribute(x) precision <- precision_to_string(precision) name <- switch( type, full = switch( clock, sys = "sys_time", naive = "naive_time" ), abbr = switch( clock, sys = "sys", naive = "naive" ) ) paste0(name, "<", precision, ">") } # ------------------------------------------------------------------------------ # Caller guarantees that clocks are identical ptype2_time_point_and_time_point <- function(x, y, ...) { if (time_point_precision_attribute(x) >= time_point_precision_attribute(y)) { x } else { y } } # Caller guarantees that clocks are identical cast_time_point_to_time_point <- function(x, to, ...) { x_precision <- time_point_precision_attribute(x) to_precision <- time_point_precision_attribute(to) if (x_precision == to_precision) { return(x) } if (x_precision > to_precision) { stop_incompatible_cast(x, to, ..., details = "Can't cast to a less precise precision.") } fields <- duration_cast_cpp(x, x_precision, to_precision) names <- clock_rcrd_names(x) clock <- time_point_clock_attribute(x) new_time_point_from_fields(fields, to_precision, clock, names) } # ------------------------------------------------------------------------------ arith_time_point_and_missing <- function(op, x, y, ...) { switch ( op, "+" = x, stop_incompatible_op(op, x, y, ...) ) } arith_time_point_and_time_point <- function(op, x, y, ...) { switch ( op, "-" = time_point_minus_time_point(x, y, names_common(x, y)), stop_incompatible_op(op, x, y, ...) ) } arith_time_point_and_duration <- function(op, x, y, ...) { switch ( op, "+" = time_point_plus_duration(x, y, duration_precision_attribute(y), names_common(x, y)), "-" = time_point_minus_duration(x, y, duration_precision_attribute(y), names_common(x, y)), stop_incompatible_op(op, x, y, ...) ) } arith_duration_and_time_point <- function(op, x, y, ...) { switch ( op, "+" = time_point_plus_duration(y, x, duration_precision_attribute(x), names_common(x, y)), "-" = stop_incompatible_op(op, x, y, details = "Can't subtract a time point from a duration.", ...), stop_incompatible_op(op, x, y, ...) ) } arith_time_point_and_numeric <- function(op, x, y, ...) { precision <- time_point_precision_attribute(x) switch ( op, "+" = time_point_plus_duration(x, y, precision, names_common(x, y)), "-" = time_point_minus_duration(x, y, precision, names_common(x, y)), stop_incompatible_op(op, x, y, ...) ) } arith_numeric_and_time_point <- function(op, x, y, ...) { precision <- time_point_precision_attribute(y) switch ( op, "+" = time_point_plus_duration(y, x, precision, names_common(x, y)), "-" = stop_incompatible_op(op, x, y, details = "Can't subtract a time point from a duration.", ...), stop_incompatible_op(op, x, y, ...) ) } # ------------------------------------------------------------------------------ #' Arithmetic: Time points #' #' @description #' These are naive-time and sys-time methods for the #' [arithmetic generics][clock-arithmetic]. #' #' - `add_weeks()` #' #' - `add_days()` #' #' - `add_hours()` #' #' - `add_minutes()` #' #' - `add_seconds()` #' #' - `add_milliseconds()` #' #' - `add_microseconds()` #' #' - `add_nanoseconds()` #' #' When working with zoned times, generally you convert to either sys-time #' or naive-time, add the duration, then convert back to zoned time. Typically, #' _weeks and days_ are added in _naive-time_, and _hours, minutes, seconds, #' and subseconds_ are added in _sys-time_. #' #' If you aren't using zoned times, arithmetic on sys-times and naive-time #' is equivalent. #' #' If you need to add larger irregular units of time, such as months, quarters, #' or years, convert to a calendar type with a converter like #' [as_year_month_day()]. #' #' @details #' `x` and `n` are recycled against each other using #' [tidyverse recycling rules][vctrs::vector_recycling_rules]. #' #' @inheritParams clock-arithmetic #' #' @param x `[clock_sys_time / clock_naive_time]` #' #' A time point vector. #' #' @return `x` after performing the arithmetic. #' #' @name time-point-arithmetic #' #' @examples #' library(magrittr) #' #' # Say you started with this zoned time, and you want to add 1 day to it #' x <- as_naive_time(year_month_day(1970, 04, 25, 02, 30, 00)) #' x <- as_zoned_time(x, "America/New_York") #' x #' #' # Note that there was a daylight saving time gap on 1970-04-26 where #' # we jumped from 01:59:59 -> 03:00:00. #' #' # You can choose to add 1 day in "system time", by first converting to #' # sys-time (the equivalent UTC time), adding the day, then converting back to #' # zoned time. If you sat still for exactly 86,400 seconds, this is the #' # time that you would see after daylight saving time adjusted the clock #' # (note that the hour field is shifted forward by the size of the gap) #' as_sys_time(x) #' #' x %>% #' as_sys_time() %>% #' add_days(1) %>% #' as_zoned_time(zoned_time_zone(x)) #' #' # Alternatively, you can add 1 day in "naive time". Naive time represents #' # a clock time with a yet-to-be-specified time zone. It tries to maintain #' # smaller units where possible, so adding 1 day would attempt to return #' # "1970-04-26T02:30:00" in the America/New_York time zone, but... #' as_naive_time(x) #' #' try({ #' x %>% #' as_naive_time() %>% #' add_days(1) %>% #' as_zoned_time(zoned_time_zone(x)) #' }) #' #' # ...this time doesn't exist in that time zone! It is "nonexistent". #' # You can resolve nonexistent times by setting the `nonexistent` argument #' # when converting to zoned time. Let's roll forward to the next available #' # moment in time. #' x %>% #' as_naive_time() %>% #' add_days(1) %>% #' as_zoned_time(zoned_time_zone(x), nonexistent = "roll-forward") NULL #' @rdname time-point-arithmetic #' @export add_weeks.clock_time_point <- function(x, n, ...) { time_point_plus_duration(x, n, PRECISION_WEEK, names_common(x, n)) } #' @rdname time-point-arithmetic #' @export add_days.clock_time_point <- function(x, n, ...) { time_point_plus_duration(x, n, PRECISION_DAY, names_common(x, n)) } #' @rdname time-point-arithmetic #' @export add_hours.clock_time_point <- function(x, n, ...) { time_point_plus_duration(x, n, PRECISION_HOUR, names_common(x, n)) } #' @rdname time-point-arithmetic #' @export add_minutes.clock_time_point <- function(x, n, ...) { time_point_plus_duration(x, n, PRECISION_MINUTE, names_common(x, n)) } #' @rdname time-point-arithmetic #' @export add_seconds.clock_time_point <- function(x, n, ...) { time_point_plus_duration(x, n, PRECISION_SECOND, names_common(x, n)) } #' @rdname time-point-arithmetic #' @export add_milliseconds.clock_time_point <- function(x, n, ...) { time_point_plus_duration(x, n, PRECISION_MILLISECOND, names_common(x, n)) } #' @rdname time-point-arithmetic #' @export add_microseconds.clock_time_point <- function(x, n, ...) { time_point_plus_duration(x, n, PRECISION_MICROSECOND, names_common(x, n)) } #' @rdname time-point-arithmetic #' @export add_nanoseconds.clock_time_point <- function(x, n, ...) { time_point_plus_duration(x, n, PRECISION_NANOSECOND, names_common(x, n)) } time_point_plus_duration <- function(x, n, precision_n, names) { time_point_arith_duration(x, n, precision_n, names, duration_plus) } time_point_minus_duration <- function(x, n, precision_n, names) { time_point_arith_duration(x, n, precision_n, names, duration_minus) } time_point_arith_duration <- function(x, n, precision_n, names, duration_fn) { clock <- time_point_clock_attribute(x) x <- time_point_duration(x) n <- duration_collect_n(n, precision_n) # Handles recycling and casting duration <- duration_fn(x = x, y = n, names = names) names <- clock_rcrd_names(duration) precision <- duration_precision_attribute(duration) new_time_point_from_fields(duration, precision, clock, names) } time_point_minus_time_point <- function(x, y, names) { args <- vec_recycle_common(x = x, y = y, names = names) x <- args$x y <- args$y names <- args$names x_duration <- time_point_duration(x) y_duration <- time_point_duration(y) duration_minus(x = x_duration, y = y_duration, names = names) } # ------------------------------------------------------------------------------ #' @export add_years.clock_time_point <- function(x, n, ...) { details <- c( i = "Do you need to convert to a calendar first?", i = cli::format_inline("Use {.fn as_year_month_day} for a calendar that supports {.fn add_years}.") ) stop_clock_unsupported(x, details = details) } #' @export add_quarters.clock_time_point <- function(x, n, ...) { details <- c( i = "Do you need to convert to a calendar first?", i = cli::format_inline("Use {.fn as_year_quarter_day} for a calendar that supports {.fn add_quarters}.") ) stop_clock_unsupported(x, details = details) } #' @export add_months.clock_time_point <- function(x, n, ...) { details <- c( i = "Do you need to convert to a calendar first?", i = cli::format_inline("Use {.fn as_year_month_day} for a calendar that supports {.fn add_months}.") ) stop_clock_unsupported(x, details = details) } # ------------------------------------------------------------------------------ #' @export as_duration.clock_time_point <- function(x, ...) { check_dots_empty0(...) time_point_duration(x, retain_names = TRUE) } #' @export as_year_month_day.clock_time_point <- function(x, ...) { check_dots_empty0(...) precision <- time_point_precision_attribute(x) fields <- as_year_month_day_from_sys_time_cpp(x, precision) new_year_month_day_from_fields(fields, precision, names = names(x)) } #' @export as_year_month_weekday.clock_time_point <- function(x, ...) { check_dots_empty0(...) precision <- time_point_precision_attribute(x) fields <- as_year_month_weekday_from_sys_time_cpp(x, precision) new_year_month_weekday_from_fields(fields, precision, names = names(x)) } #' @export as_year_quarter_day.clock_time_point <- function(x, ..., start = NULL) { check_dots_empty0(...) precision <- time_point_precision_attribute(x) start <- quarterly_validate_start(start) fields <- as_year_quarter_day_from_sys_time_cpp(x, precision, start) new_year_quarter_day_from_fields(fields, precision, start, names = names(x)) } #' @export as_year_week_day.clock_time_point <- function(x, ..., start = NULL) { check_dots_empty0(...) precision <- time_point_precision_attribute(x) start <- week_validate_start(start) fields <- as_year_week_day_from_sys_time_cpp(x, precision, start) new_year_week_day_from_fields(fields, precision, start, names = names(x)) } #' @export as_iso_year_week_day.clock_time_point <- function(x, ...) { check_dots_empty0(...) precision <- time_point_precision_attribute(x) fields <- as_iso_year_week_day_from_sys_time_cpp(x, precision) new_iso_year_week_day_from_fields(fields, precision, names = names(x)) } #' @export as_year_day.clock_time_point <- function(x, ...) { check_dots_empty0(...) precision <- time_point_precision_attribute(x) fields <- as_year_day_from_sys_time_cpp(x, precision) new_year_day_from_fields(fields, precision, names = names(x)) } #' @export as_weekday.clock_time_point <- function(x, ...) { check_dots_empty0(...) x <- time_point_cast(x, "day") day <- weekday_from_time_point_cpp(x) names(day) <- clock_rcrd_names(x) new_weekday(day) } # ------------------------------------------------------------------------------ #' Cast a time point between precisions #' #' @description #' Casting is one way to change a time point's precision. #' #' Casting to a less precise precision will completely drop information that #' is more precise than the precision that you are casting to. It does so #' in a way that makes it round towards zero. When converting time points #' to a less precise precision, you often want [time_point_floor()] instead #' of `time_point_cast()`, as that handles pre-1970 dates (which are #' stored as negative durations) in a more intuitive manner. #' #' Casting to a more precise precision is done through a multiplication by #' a conversion factor between the current precision and the new precision. #' #' @param x `[clock_sys_time / clock_naive_time]` #' #' A sys-time or naive-time. #' #' @param precision `[character(1)]` #' #' A time point precision. One of: #' #' - `"day"` #' #' - `"hour"` #' #' - `"minute"` #' #' - `"second"` #' #' - `"millisecond"` #' #' - `"microsecond"` #' #' - `"nanosecond"` #' #' @return `x` cast to the new `precision`. #' #' @export #' @examples #' # Hour precision time points #' # One is pre-1970, one is post-1970 #' x <- duration_hours(c(25, -25)) #' x <- as_naive_time(x) #' x #' #' # Casting rounds the underlying duration towards 0 #' cast <- time_point_cast(x, "day") #' cast #' #' # Flooring rounds the underlying duration towards negative infinity, #' # which is often more intuitive for time points. #' # Note that the cast ends up rounding the pre-1970 date up to the next #' # day, while the post-1970 date is rounded down. #' floor <- time_point_floor(x, "day") #' floor #' #' # Casting to a more precise precision, hour->millisecond #' time_point_cast(x, "millisecond") time_point_cast <- function(x, precision) { check_time_point(x) check_time_point_precision(precision) precision <- precision_to_integer(precision) x_precision <- time_point_precision_attribute(x) fields <- duration_cast_cpp(x, x_precision, precision) names <- clock_rcrd_names(x) clock <- time_point_clock_attribute(x) new_time_point_from_fields(fields, precision, clock, names) } #' Time point rounding #' #' @description #' - `time_point_floor()` rounds a sys-time or naive-time down to a multiple of #' the specified `precision`. #' #' - `time_point_ceiling()` rounds a sys-time or naive-time up to a multiple of #' the specified `precision`. #' #' - `time_point_round()` rounds up or down depending on what is closer, #' rounding up on ties. #' #' Rounding time points is mainly useful for rounding sub-daily time points #' up to daily time points. #' #' It can also be useful for flooring by a set number of days (like 20) with #' respect to some origin. By default, the origin is 1970-01-01 00:00:00. #' #' If you want to group by components, such as "day of the month", rather than #' by "n days", see [calendar_group()]. #' #' @section Boundary Handling: #' #' To understand how flooring and ceiling work, you need to know how they #' create their intervals for rounding. #' #' - `time_point_floor()` constructs intervals of \code{[lower, upper)} that #' bound each element of `x`, then always chooses the _left-hand side_. #' #' - `time_point_ceiling()` constructs intervals of \code{(lower, upper]} that #' bound each element of `x`, then always chooses the _right-hand side_. #' #' As an easy example, consider 2020-01-02 00:00:05. #' #' To floor this to the nearest day, the following interval is constructed, #' and the left-hand side is returned at day precision: #' #' \code{[2020-01-02 00:00:00, 2020-01-03 00:00:00)} #' #' To ceiling this to the nearest day, the following interval #' is constructed, and the right-hand side is returned at day precision: #' #' \code{(2020-01-02 00:00:00, 2020-01-03 00:00:00]} #' #' Here is another example, this time with a time point on a boundary, #' 2020-01-02 00:00:00. #' #' To floor this to the nearest day, the following interval is constructed, #' and the left-hand side is returned at day precision: #' #' \code{[2020-01-02 00:00:00, 2020-01-03 00:00:00)} #' #' To ceiling this to the nearest day, the following interval #' is constructed, and the right-hand side is returned at day precision: #' #' \code{(2020-01-01 00:00:00, 2020-01-02 00:00:00]} #' #' Notice that, regardless of whether you are doing a floor or ceiling, if #' the input falls on a boundary then it will be returned as is. #' #' @inheritParams rlang::args_dots_empty #' @inheritParams time_point_cast #' #' @param n `[positive integer(1)]` #' #' A positive integer specifying the multiple of `precision` to use. #' #' @param origin `[clock_sys_time(1) / clock_naive_time(1) / NULL]` #' #' An origin to begin counting from. Mostly useful when `n > 1` and you #' want to control how the rounding groups are created. #' #' If `x` is a sys-time, `origin` must be a sys-time. #' #' If `x` is a naive-time, `origin` must be a naive-time. #' #' The precision of `origin` must be equally precise as or less #' precise than `precision`. #' #' If `NULL`, a default origin of midnight on 1970-01-01 is used. #' #' @return `x` rounded to the new `precision`. #' #' @name time-point-rounding #' #' @examples #' library(magrittr) #' #' x <- as_naive_time(year_month_day(2019, 01, 01)) #' x <- add_days(x, 0:40) #' head(x) #' #' # Floor by sets of 20 days #' # The implicit origin to start the 20 day counter is 1970-01-01 #' time_point_floor(x, "day", n = 20) #' #' # You can easily customize the origin by supplying a new one #' # as the `origin` argument #' origin <- year_month_day(2019, 01, 01) %>% #' as_naive_time() #' #' time_point_floor(x, "day", n = 20, origin = origin) #' #' # For times on the boundary, floor and ceiling both return the input #' # at the new precision. Notice how the first element is on the boundary, #' # and the second is 1 second after the boundary. #' y <- as_naive_time(year_month_day(2020, 01, 02, 00, 00, c(00, 01))) #' time_point_floor(y, "day") #' time_point_ceiling(y, "day") NULL #' @rdname time-point-rounding #' @export time_point_floor <- function(x, precision, ..., n = 1L, origin = NULL) { time_point_rounder(x, precision, n, origin, duration_floor, ...) } #' @rdname time-point-rounding #' @export time_point_ceiling <- function(x, precision, ..., n = 1L, origin = NULL) { time_point_rounder(x, precision, n, origin, duration_ceiling, ...) } #' @rdname time-point-rounding #' @export time_point_round <- function(x, precision, ..., n = 1L, origin = NULL) { time_point_rounder(x, precision, n, origin, duration_round, ...) } time_point_rounder <- function(x, precision, n, origin, duration_rounder, ..., error_arg = caller_arg(x), error_call = caller_env()) { check_dots_empty0(...) check_time_point(x, arg = error_arg, call = error_call) precision_string <- precision check_time_point_precision(precision, call = error_call) precision <- precision_to_integer(precision) duration <- time_point_duration(x) has_origin <- !is_null(origin) if (has_origin) { origin <- collect_time_point_rounder_origin( origin = origin, x = x, precision = precision, error_call = error_call ) duration <- duration - origin } duration <- duration_rounder(duration, precision_string, n = n) if (has_origin) { duration <- duration + origin } names <- clock_rcrd_names(x) clock <- time_point_clock_attribute(x) new_time_point_from_fields(duration, precision, clock, names) } collect_time_point_rounder_origin <- function(origin, x, precision, error_call) { # Cast `origin` to a time point with the same clock as `x`, # but with a precision of `precision` to_names <- NULL to <- duration_helper(integer(), precision) to <- new_time_point_from_fields(to, precision, time_point_clock_attribute(x), to_names) origin <- vec_cast(origin, to, x_arg = "origin", call = error_call) vec_check_size(origin, 1L, call = error_call) check_no_missing(origin, call = error_call) origin <- as_duration(origin) origin } # ------------------------------------------------------------------------------ #' Shifting: time point #' #' @description #' `time_point_shift()` shifts `x` to the `target` weekday. You can #' shift to the next or previous weekday. If `x` is currently on the `target` #' weekday, you can choose to leave it alone or advance it to the next instance #' of the `target`. #' #' Weekday shifting is one of the easiest ways to floor by week while #' controlling what is considered the first day of the week. You can also #' accomplish this with the `origin` argument of [time_point_floor()], but #' this is slightly easier. #' #' @inheritParams rlang::args_dots_empty #' #' @param x `[clock_time_point]` #' #' A time point. #' #' @param target `[weekday]` #' #' A weekday created from [weekday()] to target. #' #' Generally this is length 1, but can also be the same length as `x`. #' #' @param which `[character(1)]` #' #' One of: #' #' - `"next"`: Shift to the next instance of the `target` weekday. #' #' - `"previous`: Shift to the previous instance of the `target` weekday. #' #' @param boundary `[character(1)]` #' #' One of: #' #' - `"keep"`: If `x` is currently on the `target` weekday, return it. #' #' - `"advance"`: If `x` is currently on the `target` weekday, advance it #' anyways. #' #' @return `x` shifted to the `target` weekday. #' #' @export #' @examples #' x <- as_naive_time(year_month_day(2019, 1, 1:2)) #' #' # A Tuesday and Wednesday #' as_weekday(x) #' #' monday <- weekday(clock_weekdays$monday) #' #' # Shift to the next Monday #' time_point_shift(x, monday) #' #' # Shift to the previous Monday #' # This is an easy way to "floor by week" with a target weekday in mind #' time_point_shift(x, monday, which = "previous") #' #' # What about Tuesday? #' tuesday <- weekday(clock_weekdays$tuesday) #' #' # Notice that the day that was currently on a Tuesday was not shifted #' time_point_shift(x, tuesday) #' #' # You can force it to `"advance"` #' time_point_shift(x, tuesday, boundary = "advance") time_point_shift <- function(x, target, ..., which = "next", boundary = "keep") { check_dots_empty0(...) check_time_point(x) check_weekday(target) target <- vec_recycle(target, vec_size(x), x_arg = "target") check_shift_which(which) check_shift_boundary(boundary) if (is_next(which)) { if (is_advance(boundary)) { x <- x + duration_days(1L) } x <- x + (target - as_weekday(x)) } else { if (is_advance(boundary)) { x <- x - duration_days(1L) } x <- x - (as_weekday(x) - target) } x } check_shift_which <- function(which, call = caller_env()) { check_string(which, call = call) arg_match0(which, values = c("next", "previous"), error_call = call) } check_shift_boundary <- function(boundary, call = caller_env()) { check_string(boundary, call = call) arg_match0(boundary, values = c("keep", "advance"), error_call = call) } is_next <- function(x) { identical(x, "next") } is_advance <- function(x) { identical(x, "advance") } # ------------------------------------------------------------------------------ #' Counting: time point #' #' @description #' `time_point_count_between()` counts the number of `precision` units #' between `start` and `end` (i.e., the number of days or hours). This count #' corresponds to the _whole number_ of units, and will never return a #' fractional value. #' #' This is suitable for, say, computing the whole number of days between two #' time points, accounting for the time of day. #' #' @details #' Remember that `time_point_count_between()` returns an integer vector. #' With extremely fine precisions, such as nanoseconds, the count can quickly #' exceed the maximum value that is allowed in an integer. In this case, an #' `NA` will be returned with a warning. #' #' @inheritSection calendar_count_between Comparison Direction #' #' @inheritParams rlang::args_dots_empty #' #' @param start,end `[clock_time_point]` #' #' A pair of time points. These will be recycled to their common size. #' #' @param precision `[character(1)]` #' #' One of: #' #' - `"week"` #' - `"day"` #' - `"hour"` #' - `"minute"` #' - `"second"` #' - `"millisecond"` #' - `"microsecond"` #' - `"nanosecond"` #' #' @param n `[positive integer(1)]` #' #' A single positive integer specifying a multiple of `precision` to use. #' #' @return An integer representing the number of `precision` units between #' `start` and `end`. #' #' @export #' @examples #' x <- as_naive_time(year_month_day(2019, 2, 3)) #' y <- as_naive_time(year_month_day(2019, 2, 10)) #' #' # Whole number of days or hours between two time points #' time_point_count_between(x, y, "day") #' time_point_count_between(x, y, "hour") #' #' # Whole number of 2-day units #' time_point_count_between(x, y, "day", n = 2) #' #' # Leap years are taken into account #' x <- as_naive_time(year_month_day(c(2020, 2021), 2, 28)) #' y <- as_naive_time(year_month_day(c(2020, 2021), 3, 01)) #' time_point_count_between(x, y, "day") #' #' # Time of day is taken into account. #' # `2020-02-02T04 -> 2020-02-03T03` is not a whole day (because of the hour) #' # `2020-02-02T04 -> 2020-02-03T05` is a whole day #' x <- as_naive_time(year_month_day(2020, 2, 2, 4)) #' y <- as_naive_time(year_month_day(2020, 2, 3, c(3, 5))) #' time_point_count_between(x, y, "day") #' time_point_count_between(x, y, "hour") #' #' # Can compute negative counts (using the same example from above) #' time_point_count_between(y, x, "day") #' time_point_count_between(y, x, "hour") #' #' # Repeated computation at increasingly fine precisions #' x <- as_naive_time(year_month_day( #' 2020, 2, 2, 4, 5, 6, 200, #' subsecond_precision = "microsecond" #' )) #' y <- as_naive_time(year_month_day( #' 2020, 3, 1, 8, 9, 10, 100, #' subsecond_precision = "microsecond" #' )) #' #' days <- time_point_count_between(x, y, "day") #' x <- x + duration_days(days) #' #' hours <- time_point_count_between(x, y, "hour") #' x <- x + duration_hours(hours) #' #' minutes <- time_point_count_between(x, y, "minute") #' x <- x + duration_minutes(minutes) #' #' seconds <- time_point_count_between(x, y, "second") #' x <- x + duration_seconds(seconds) #' #' microseconds <- time_point_count_between(x, y, "microsecond") #' x <- x + duration_microseconds(microseconds) #' #' data.frame( #' days = days, #' hours = hours, #' minutes = minutes, #' seconds = seconds, #' microseconds = microseconds #' ) time_point_count_between <- function(start, end, precision, ..., n = 1L) { check_dots_empty0(...) check_time_point(start) check_time_point(end) args <- vec_cast_common(start = start, end = end) args <- vec_recycle_common(!!!args) start <- args[[1]] end <- args[[2]] check_precision(precision) precision_int <- precision_to_integer(precision) if (precision_int < PRECISION_WEEK) { cli::cli_abort("{.arg precision} must be at least {.str week} precision.") } check_number_whole(n, min = 0) n <- vec_cast(n, integer()) out <- end - start out <- duration_cast(out, precision) if (n != 1L) { out <- out %/% n } as.integer(out) } # ------------------------------------------------------------------------------ #' Sequences: time points #' #' @description #' This is a time point method for the [seq()] generic. It works for sys-time #' and naive-time vectors. #' #' Sequences can be generated for all valid time point precisions (daily through #' nanosecond). #' #' When calling `seq()`, exactly two of the following must be specified: #' - `to` #' - `by` #' - Either `length.out` or `along.with` #' #' @inheritParams seq.clock_duration #' #' @param from `[clock_sys_time(1) / clock_naive_time(1)]` #' #' A time point to start the sequence from. #' #' `from` is always included in the result. #' #' @param to `[clock_sys_time(1) / clock_naive_time(1) / NULL]` #' #' A time point to stop the sequence at. #' #' `to` is cast to the type of `from`. #' #' `to` is only included in the result if the resulting sequence divides #' the distance between `from` and `to` exactly. #' #' @return A sequence with the type of `from`. #' #' @export #' @examples #' # Daily sequence #' seq( #' as_naive_time(year_month_day(2019, 1, 1)), #' as_naive_time(year_month_day(2019, 2, 4)), #' by = 5 #' ) #' #' # Minutely sequence using minute precision naive-time #' x <- as_naive_time(year_month_day(2019, 1, 2, 3, 3)) #' x #' #' seq(x, by = 4, length.out = 10) #' #' # You can use larger step sizes by using a duration-based `by` #' seq(x, by = duration_days(1), length.out = 5) #' #' # Nanosecond sequence #' from <- as_naive_time(year_month_day(2019, 1, 1)) #' from <- time_point_cast(from, "nanosecond") #' to <- from + 100 #' seq(from, to, by = 10) seq.clock_time_point <- function(from, to = NULL, by = NULL, length.out = NULL, along.with = NULL, ...) { names <- NULL clock <- time_point_clock_attribute(from) precision <- time_point_precision_attribute(from) has_to <- !is_null(to) if (has_to) { to <- vec_cast(to, from, x_arg = "to", to_arg = "from") } from <- time_point_duration(from) if (has_to) { to <- time_point_duration(to) } fields <- seq( from = from, to = to, by = by, length.out = length.out, along.with = along.with, ... ) new_time_point_from_fields(fields, precision, clock, names) } # ------------------------------------------------------------------------------ #' Spanning sequence: time points #' #' @description #' `time_point_spanning_seq()` generates a regular sequence along the span of #' `x`, i.e. along `[min(x), max(x)]`. The sequence is generated at the #' precision of `x`. #' #' @details #' Missing values are automatically removed before the sequence is generated. #' #' If you need more precise sequence generation, call [range()] and [seq()] #' directly. #' #' @param x `[clock_sys_time / clock_naive_time]` #' #' A time point vector. #' #' @return A sequence along `[min(x), max(x)]`. #' #' @export #' @examples #' x <- as_naive_time(year_month_day(2019, c(1, 2, 1, 2), c(15, 4, 12, 2))) #' x #' #' time_point_spanning_seq(x) #' #' # The sequence is generated at the precision of `x` #' x <- as_naive_time(c( #' year_month_day(2019, 1, 1, 5), #' year_month_day(2019, 1, 2, 10), #' year_month_day(2019, 1, 1, 3) #' )) #' time_point_spanning_seq(x) time_point_spanning_seq <- function(x) { check_time_point(x) spanning_seq_impl(x) } # ------------------------------------------------------------------------------ #' Precision: time point #' #' `time_point_precision()` extracts the precision from a time point, such #' as a sys-time or naive-time. It returns the precision as a single string. #' #' @param x `[clock_time_point]` #' #' A time point. #' #' @return A single string holding the precision of the time point. #' #' @export #' @examples #' time_point_precision(sys_time_now()) #' time_point_precision(as_naive_time(duration_days(1))) time_point_precision <- function(x) { check_time_point(x) precision <- time_point_precision_attribute(x) precision <- precision_to_string(precision) precision } # ------------------------------------------------------------------------------ # `clock_minimum()` and `clock_maximum()` are known to not print correctly # for anything besides nanosecond precision time points. If you convert the # values to durations, they will print correctly. This has to do with the # print method going through year-month-day, which has a limit on how large the # `year` field can be that doesn't align with the limit of time points. See #331 # for a detailed discussion. The important thing is that the limits still work # correctly for comparison purposes! #' @export clock_minimum.clock_time_point <- function(x) { time_point_limit(x, clock_minimum) } #' @export clock_maximum.clock_time_point <- function(x) { time_point_limit(x, clock_maximum) } time_point_limit <- function(x, fn) { names <- NULL clock <- time_point_clock_attribute(x) precision <- time_point_precision_attribute(x) x <- time_point_duration(x) x <- fn(x) new_time_point_from_fields(x, precision, clock, names) } # ------------------------------------------------------------------------------ check_time_point_precision <- function(x, ..., arg = caller_arg(x), call = caller_env()) { check_precision( x = x, values = c("day", precision_time_names()), arg = arg, call = call ) } clock/R/posixt.R0000644000176200001440000021072014427270231013223 0ustar liggesusers#' @export as_sys_time.POSIXt <- function(x, ...) { # The sys-time that would give the equivalent zoned-time when a zone is attached check_dots_empty0(...) as_sys_time(as_zoned_time(x)) } #' @export as_naive_time.POSIXt <- function(x, ...) { check_dots_empty0(...) as_naive_time(as_zoned_time(x)) } #' Convert to a zoned-time from a date-time #' #' @description #' This is a POSIXct/POSIXlt method for the [as_zoned_time()] generic. #' #' Converting from one of R's native date-time classes (POSIXct or POSIXlt) #' will retain the time zone of that object. There is no `zone` argument. #' #' @inheritParams rlang::args_dots_empty #' #' @param x `[POSIXct / POSIXlt]` #' #' A date-time. #' #' @return A zoned-time. #' #' @name as-zoned-time-posixt #' @export #' @examples #' x <- as.POSIXct("2019-01-01", tz = "America/New_York") #' as_zoned_time(x) as_zoned_time.POSIXt <- function(x, ...) { check_dots_empty0(...) x <- to_posixct(x) names <- names(x) zone <- posixt_tzone(x) fields <- to_sys_duration_fields_from_sys_seconds_cpp(x) new_zoned_time_from_fields(fields, PRECISION_SECOND, zone, names) } #' @export as_year_month_day.POSIXt <- function(x, ...) { # Assumes zoned -> naive -> calendar is what the user expects check_dots_empty0(...) x <- as_naive_time(x) as_year_month_day(x) } #' @export as_year_month_weekday.POSIXt <- function(x, ...) { # Assumes zoned -> naive -> calendar is what the user expects check_dots_empty0(...) x <- as_naive_time(x) as_year_month_weekday(x) } #' @export as_year_quarter_day.POSIXt <- function(x, ..., start = NULL) { # Assumes zoned -> naive -> calendar is what the user expects check_dots_empty0(...) x <- as_naive_time(x) as_year_quarter_day(x, start = start) } #' @export as_year_week_day.POSIXt <- function(x, ..., start = NULL) { # Assumes zoned -> naive -> calendar is what the user expects check_dots_empty0(...) x <- as_naive_time(x) as_year_week_day(x, start = start) } #' @export as_iso_year_week_day.POSIXt <- function(x, ...) { # Assumes zoned -> naive -> calendar is what the user expects check_dots_empty0(...) x <- as_naive_time(x) as_iso_year_week_day(x) } #' @export as_year_day.POSIXt <- function(x, ...) { # Assumes zoned -> naive -> calendar is what the user expects check_dots_empty0(...) x <- as_naive_time(x) as_year_day(x) } #' @export as_weekday.POSIXt <- function(x, ...) { # Assumes zoned -> naive is what the user expects check_dots_empty0(...) x <- as_naive_time(x) as_weekday(x) } # ------------------------------------------------------------------------------ # Not using `check_dots_empty()` because that might # be too aggressive with base generics. Also not passing `...` on to methods # that do check empty dots. Essentially just silently swallowing them. # Using `tz = ""` to be compatible with the generic of `as.POSIXct()`. #' @export as.POSIXct.clock_calendar <- function(x, tz = "", ..., nonexistent = NULL, ambiguous = NULL) { x <- as_naive_time(x) as.POSIXct(x, tz = tz, nonexistent = nonexistent, ambiguous = ambiguous) } #' @export as.POSIXct.clock_sys_time <- function(x, tz = "", ...) { x <- as_zoned_time(x, zone = tz) as.POSIXct(x) } #' @export as.POSIXct.clock_naive_time <- function(x, tz = "", ..., nonexistent = NULL, ambiguous = NULL) { x <- as_zoned_time(x, zone = tz, nonexistent = nonexistent, ambiguous = ambiguous) as.POSIXct(x) } #' @export as.POSIXct.clock_zoned_time <- function(x, ...) { zone <- zoned_time_zone_attribute(x) x <- as_sys_time(x) x <- time_point_floor(x, "second") seconds <- to_sys_seconds_from_sys_duration_fields_cpp(x) names(seconds) <- clock_rcrd_names(x) new_datetime(seconds, zone) } # ------------------------------------------------------------------------------ #' @export as.POSIXlt.clock_calendar <- function(x, tz = "", ..., nonexistent = NULL, ambiguous = NULL) { x <- as.POSIXct(x, tz = tz, nonexistent = nonexistent, ambiguous = ambiguous) as.POSIXlt(x) } #' @export as.POSIXlt.clock_sys_time <- function(x, tz = "", ...) { x <- as.POSIXct(x, tz = tz) as.POSIXlt(x) } #' @export as.POSIXlt.clock_naive_time <- as.POSIXlt.clock_calendar #' @export as.POSIXlt.clock_zoned_time <- function(x, ...) { x <- as.POSIXct(x) as.POSIXlt(x) } # ------------------------------------------------------------------------------ #' Convert to a date-time #' #' @description #' `as_date_time()` is a generic function that converts its input to a date-time #' (POSIXct). #' #' There are methods for converting dates (Date), calendars, time points, and #' zoned-times to date-times. #' #' For converting to a date, see [as_date()]. #' #' @details #' Note that clock always assumes that R's Date class is naive, so converting #' a Date to a POSIXct will always attempt to retain the printed year, month, #' and day. Where possible, the resulting time will be at midnight (`00:00:00`), #' but in some rare cases this is not possible due to daylight saving time. If #' that issue ever arises, an error will be thrown, which can be resolved by #' explicitly supplying `nonexistent` or `ambiguous`. #' #' This is not a drop-in replacement for `as.POSIXct()`, as it only converts a #' limited set of types to POSIXct. For parsing characters as date-times, see #' [date_time_parse()]. For converting numerics to date-times, see #' [vctrs::new_datetime()] or continue to use `as.POSIXct()`. #' #' @inheritParams as-zoned-time-naive-time #' #' @param x `[vector]` #' #' A vector. #' #' @return A date-time with the same length as `x`. #' #' @export #' @examples #' x <- as.Date("2019-01-01") #' #' # `as.POSIXct()` will always treat Date as UTC, but will show the result #' # of the conversion in your system time zone, which can be somewhat confusing #' if (rlang::is_installed("withr")) { #' withr::with_timezone("UTC", print(as.POSIXct(x))) #' withr::with_timezone("Europe/Paris", print(as.POSIXct(x))) #' withr::with_timezone("America/New_York", print(as.POSIXct(x))) #' } #' #' # `as_date_time()` will treat Date as naive, which means that the original #' # printed date will attempt to be kept wherever possible, no matter the #' # time zone. The time will be set to midnight. #' as_date_time(x, "UTC") #' as_date_time(x, "Europe/Paris") #' as_date_time(x, "America/New_York") #' #' # In some rare cases, this is not possible. #' # For example, in Asia/Beirut, there was a DST gap from #' # 2021-03-27 23:59:59 -> 2021-03-28 01:00:00, #' # skipping the 0th hour entirely. #' x <- as.Date("2021-03-28") #' try(as_date_time(x, "Asia/Beirut")) #' #' # To resolve this, set a `nonexistent` time resolution strategy #' as_date_time(x, "Asia/Beirut", nonexistent = "roll-forward") #' #' #' # You can also convert to date-time from other clock types #' as_date_time(year_month_day(2019, 2, 3, 03), "America/New_York") as_date_time <- function(x, ...) { UseMethod("as_date_time") } #' @rdname as_date_time #' @export as_date_time.POSIXt <- function(x, ...) { check_dots_empty0(...) to_posixct(x) } #' @rdname as_date_time #' @export as_date_time.Date <- function(x, zone, ..., nonexistent = NULL, ambiguous = NULL) { check_dots_empty0(...) as.POSIXct(as_naive_time(x), tz = zone, nonexistent = nonexistent, ambiguous = ambiguous) } #' @rdname as_date_time #' @export as_date_time.clock_calendar <- function(x, zone, ..., nonexistent = NULL, ambiguous = NULL) { check_dots_empty0(...) as.POSIXct(x, tz = zone, nonexistent = nonexistent, ambiguous = ambiguous) } #' @rdname as_date_time #' @export as_date_time.clock_sys_time <- function(x, zone, ...) { check_dots_empty0(...) as.POSIXct(x, tz = zone) } #' @rdname as_date_time #' @export as_date_time.clock_naive_time <- function(x, zone, ..., nonexistent = NULL, ambiguous = NULL) { check_dots_empty0(...) as.POSIXct(x, tz = zone, nonexistent = nonexistent, ambiguous = ambiguous) } #' @rdname as_date_time #' @export as_date_time.clock_zoned_time <- function(x, ...) { check_dots_empty0(...) as.POSIXct(x) } # ------------------------------------------------------------------------------ #' Getters: date-time #' #' @description #' These are POSIXct/POSIXlt methods for the [getter generics][clock-getters]. #' #' - `get_year()` returns the Gregorian year. #' #' - `get_month()` returns the month of the year. #' #' - `get_day()` returns the day of the month. #' #' - There are sub-daily getters for extracting more precise components, up to #' a precision of seconds. #' #' For more advanced component extraction, convert to the calendar type #' that you are interested in. #' #' @param x `[POSIXct / POSIXlt]` #' #' A date-time type to get the component from. #' #' @return The component. #' #' @name posixt-getters #' @examples #' x <- as.POSIXct("2019-01-01", tz = "America/New_York") #' #' x <- add_days(x, 0:5) #' x <- set_second(x, 10:15) #' #' get_day(x) #' get_second(x) NULL #' @rdname posixt-getters #' @export get_year.POSIXt <- function(x) { get_posixt_field_year_month_day(x, get_year) } #' @rdname posixt-getters #' @export get_month.POSIXt <- function(x) { get_posixt_field_year_month_day(x, get_month) } #' @rdname posixt-getters #' @export get_day.POSIXt <- function(x) { get_posixt_field_year_month_day(x, get_day) } #' @rdname posixt-getters #' @export get_hour.POSIXt <- function(x) { get_posixt_field_year_month_day(x, get_hour) } #' @rdname posixt-getters #' @export get_minute.POSIXt <- function(x) { get_posixt_field_year_month_day(x, get_minute) } #' @rdname posixt-getters #' @export get_second.POSIXt <- function(x) { get_posixt_field_year_month_day(x, get_second) } get_posixt_field_year_month_day <- function(x, get_fn) { x <- as_year_month_day(x) get_fn(x) } # ------------------------------------------------------------------------------ #' Setters: date-time #' #' @description #' These are POSIXct/POSIXlt methods for the [setter generics][clock-setters]. #' #' - `set_year()` sets the year. #' #' - `set_month()` sets the month of the year. Valid values are in the range #' of `[1, 12]`. #' #' - `set_day()` sets the day of the month. Valid values are in the range #' of `[1, 31]`. #' #' - There are sub-daily setters for setting more precise components, up to #' a precision of seconds. #' #' @inheritParams rlang::args_dots_empty #' @inheritParams invalid_resolve #' @inheritParams as-zoned-time-naive-time #' #' @param x `[POSIXct / POSIXlt]` #' #' A date-time vector. #' #' @param value `[integer / "last"]` #' #' The value to set the component to. #' #' For `set_day()`, this can also be `"last"` to set the day to the #' last day of the month. #' #' @return `x` with the component set. #' #' @name posixt-setters #' @examples #' x <- as.POSIXct("2019-02-01", tz = "America/New_York") #' #' # Set the day #' set_day(x, 12:14) #' #' # Set to the "last" day of the month #' set_day(x, "last") #' #' # You cannot set a date-time to an invalid date like you can with #' # a year-month-day. Instead, the default strategy is to error. #' try(set_day(x, 31)) #' set_day(as_year_month_day(x), 31) #' #' # You can resolve these issues while setting the day by specifying #' # an invalid date resolution strategy with `invalid` #' set_day(x, 31, invalid = "previous") #' #' y <- as.POSIXct("2020-03-08 01:30:00", tz = "America/New_York") #' #' # Nonexistent and ambiguous times must be resolved immediately when #' # working with R's native date-time types. An error is thrown by default. #' try(set_hour(y, 2)) #' set_hour(y, 2, nonexistent = "roll-forward") #' set_hour(y, 2, nonexistent = "roll-backward") NULL #' @rdname posixt-setters #' @export set_year.POSIXt <- function(x, value, ..., invalid = NULL, nonexistent = NULL, ambiguous = x) { check_dots_empty0(...) force(ambiguous) set_posixt_field_year_month_day(x, value, invalid, nonexistent, ambiguous, set_year) } #' @rdname posixt-setters #' @export set_month.POSIXt <- function(x, value, ..., invalid = NULL, nonexistent = NULL, ambiguous = x) { check_dots_empty0(...) force(ambiguous) set_posixt_field_year_month_day(x, value, invalid, nonexistent, ambiguous, set_month) } #' @rdname posixt-setters #' @export set_day.POSIXt <- function(x, value, ..., invalid = NULL, nonexistent = NULL, ambiguous = x) { check_dots_empty0(...) force(ambiguous) set_posixt_field_year_month_day(x, value, invalid, nonexistent, ambiguous, set_day) } #' @rdname posixt-setters #' @export set_hour.POSIXt <- function(x, value, ..., invalid = NULL, nonexistent = NULL, ambiguous = x) { check_dots_empty0(...) force(ambiguous) set_posixt_field_year_month_day(x, value, invalid, nonexistent, ambiguous, set_hour) } #' @rdname posixt-setters #' @export set_minute.POSIXt <- function(x, value, ..., invalid = NULL, nonexistent = NULL, ambiguous = x) { check_dots_empty0(...) force(ambiguous) set_posixt_field_year_month_day(x, value, invalid, nonexistent, ambiguous, set_minute) } #' @rdname posixt-setters #' @export set_second.POSIXt <- function(x, value, ..., invalid = NULL, nonexistent = NULL, ambiguous = x) { check_dots_empty0(...) force(ambiguous) set_posixt_field_year_month_day(x, value, invalid, nonexistent, ambiguous, set_second) } set_posixt_field_year_month_day <- function(x, value, invalid, nonexistent, ambiguous, set_fn) { zone <- posixt_tzone(x) x <- as_year_month_day(x) x <- set_fn(x, value) x <- invalid_resolve(x, invalid = invalid) as.POSIXct(x, tz = zone, nonexistent = nonexistent, ambiguous = ambiguous) } # ------------------------------------------------------------------------------ #' @method vec_arith.POSIXct clock_duration #' @export vec_arith.POSIXct.clock_duration <- function(op, x, y, ...) { arith_posixt_and_duration(op, x, y, ...) } #' @method vec_arith.clock_duration POSIXct #' @export vec_arith.clock_duration.POSIXct <- function(op, x, y, ...) { arith_duration_and_posixt(op, x, y, ...) } #' @method vec_arith.POSIXlt clock_duration #' @export vec_arith.POSIXlt.clock_duration <- function(op, x, y, ...) { arith_posixt_and_duration(op, x, y, ...) } #' @method vec_arith.clock_duration POSIXlt #' @export vec_arith.clock_duration.POSIXlt <- function(op, x, y, ...) { arith_duration_and_posixt(op, x, y, ...) } arith_posixt_and_duration <- function(op, x, y, ...) { switch( op, "+" = add_duration(x, y), "-" = add_duration(x, -y), stop_incompatible_op(op, x, y, ...) ) } arith_duration_and_posixt <- function(op, x, y, ...) { switch( op, "+" = add_duration(y, x, swapped = TRUE), "-" = stop_incompatible_op(op, x, y, details = "Can't subtract a POSIXct/POSIXlt from a duration.", ...), stop_incompatible_op(op, x, y, ...) ) } # ------------------------------------------------------------------------------ # @export - .onLoad() slider_plus.POSIXct.clock_duration <- function(x, y) { vec_arith("+", x, y) } # @export - .onLoad() slider_plus.POSIXlt.clock_duration <- function(x, y) { vec_arith("+", x, y) } # @export - .onLoad() slider_minus.POSIXct.clock_duration <- function(x, y) { vec_arith("-", x, y) } # @export - .onLoad() slider_minus.POSIXlt.clock_duration <- function(x, y) { vec_arith("-", x, y) } # ------------------------------------------------------------------------------ #' Arithmetic: date-time #' #' @description #' These are POSIXct/POSIXlt methods for the #' [arithmetic generics][clock-arithmetic]. #' #' Calendrical based arithmetic: #' #' These functions convert to a naive-time, then to a year-month-day, perform #' the arithmetic, then convert back to a date-time. #' #' - `add_years()` #' #' - `add_quarters()` #' #' - `add_months()` #' #' Naive-time based arithmetic: #' #' These functions convert to a naive-time, perform the arithmetic, then #' convert back to a date-time. #' #' - `add_weeks()` #' #' - `add_days()` #' #' Sys-time based arithmetic: #' #' These functions convert to a sys-time, perform the arithmetic, then #' convert back to a date-time. #' #' - `add_hours()` #' #' - `add_minutes()` #' #' - `add_seconds()` #' #' @details #' Adding a single quarter with `add_quarters()` is equivalent to adding #' 3 months. #' #' `x` and `n` are recycled against each other using #' [tidyverse recycling rules][vctrs::vector_recycling_rules]. #' #' Calendrical based arithmetic has the potential to generate invalid dates #' (like the 31st of February), nonexistent times (due to daylight saving #' time gaps), and ambiguous times (due to daylight saving time fallbacks). #' #' Naive-time based arithmetic will never generate an invalid date, but #' may generate a nonexistent or ambiguous time (i.e. you added 1 day and #' landed in a daylight saving time gap). #' #' Sys-time based arithmetic operates in the UTC time zone, which means #' that it will never generate any invalid dates or nonexistent / ambiguous #' times. #' #' The conversion from POSIXct/POSIXlt to the corresponding clock type uses #' a "best guess" about whether you want to do the arithmetic using a naive-time #' or a sys-time. For example, when adding months, you probably want to #' retain the printed time when converting to a year-month-day to perform #' the arithmetic, so the conversion goes through naive-time. However, #' when adding smaller units like seconds, you probably want #' `"2020-03-08 01:59:59" + 1 second` in the America/New_York time zone to #' return `"2020-03-08 03:00:00"`, taking into account the fact that there #' was a daylight saving time gap. This requires doing the arithmetic in #' sys-time, so that is what clock converts to. If you disagree with this #' heuristic for any reason, you can take control and perform the conversions #' yourself. For example, you could convert the previous example to a #' naive-time instead of a sys-time manually with [as_naive_time()], add #' 1 second giving `"2020-03-08 02:00:00"`, then convert back to a #' POSIXct/POSIXlt, dealing with the nonexistent time that gets created by #' using the `nonexistent` argument of `as.POSIXct()`. #' #' @section Relative ordering: #' #' For the most part, adding time based units to date-times will retain the #' relative ordering of the input. For example, if `x[1] < x[2]` before the #' `add_*()` call, then it is generally also true of the result. Using #' `invalid = "previous" / "next"` and #' `nonexistent = "roll-forward" / "roll-backward"` ensures that this holds #' when invalid and nonexistent issues are encountered. #' #' That said, with date-times there is an edge case related to ambiguous times #' where the relative ordering could change. Consider these three date-times: #' #' ```{r} #' x <- c( #' date_time_build(2012, 4, 1, 2, 30, zone = "Australia/Melbourne", ambiguous = "earliest"), #' date_time_build(2012, 4, 1, 2, 00, zone = "Australia/Melbourne", ambiguous = "latest"), #' date_time_build(2012, 4, 1, 2, 30, zone = "Australia/Melbourne", ambiguous = "latest") #' ) #' x #' ``` #' #' In this case, there was a daylight saving time fallback on `2012-04-01` #' where the clocks went from `02:59:59 AEDT -> 02:00:00 AEST`. So the times #' above are precisely 30 minutes apart, and they are in increasing order. #' #' If we add sys-time based units like hours, minutes, or seconds, then the #' relative ordering of these date-times will be preserved. However, arithmetic #' that goes through naive-time, like adding days or months, won't preserve #' the ordering here: #' #' ```{r} #' add_days(x, 1) #' add_months(x, 1) #' ``` #' #' Note that the 1st and 3rd values of the result are the same, and the 1st #' value is no longer before the 2nd value. #' #' Adding larger units of time in naive-time generally does make more sense #' than adding it in sys-time, but it does come with this one edge case to be #' aware of when working with date-times (this does not affect dates). If this #' has the potential to be an issue, consider only adding sys-time based units #' (hours, minutes, and seconds) which can't have these issues. #' #' @inheritParams clock-arithmetic #' @inheritParams invalid_resolve #' @inheritParams as-zoned-time-naive-time #' #' @param x `[POSIXct / POSIXlt]` #' #' A date-time vector. #' #' @return `x` after performing the arithmetic. #' #' @name posixt-arithmetic #' #' @examples #' x <- as.POSIXct("2019-01-01", tz = "America/New_York") #' #' add_years(x, 1:5) #' #' y <- as.POSIXct("2019-01-31 00:30:00", tz = "America/New_York") #' #' # Adding 1 month to `y` generates an invalid date. Unlike year-month-day #' # types, R's native date-time types cannot handle invalid dates, so you must #' # resolve them immediately. If you don't you get an error: #' try(add_months(y, 1:2)) #' add_months(as_year_month_day(y), 1:2) #' #' # Resolve invalid dates by specifying an invalid date resolution strategy #' # with the `invalid` argument. Using `"previous"` here sets the date-time to #' # the previous valid moment in time - i.e. the end of the month. The #' # time is set to the last moment in the day to retain the relative ordering #' # within your input. If you are okay with potentially losing this, and #' # want to retain your time of day, you can use `"previous-day"` to set the #' # date-time to the previous valid day, while keeping the time of day. #' add_months(y, 1:2, invalid = "previous") #' add_months(y, 1:2, invalid = "previous-day") NULL #' @rdname posixt-arithmetic #' @export add_years.POSIXt <- function(x, n, ..., invalid = NULL, nonexistent = NULL, ambiguous = x) { check_dots_empty0(...) force(ambiguous) add_posixt_duration_year_month_day(x, n, invalid, nonexistent, ambiguous, add_years) } #' @rdname posixt-arithmetic #' @export add_quarters.POSIXt <- function(x, n, ..., invalid = NULL, nonexistent = NULL, ambiguous = x) { check_dots_empty0(...) force(ambiguous) add_posixt_duration_year_month_day(x, n, invalid, nonexistent, ambiguous, add_quarters) } #' @rdname posixt-arithmetic #' @export add_months.POSIXt <- function(x, n, ..., invalid = NULL, nonexistent = NULL, ambiguous = x) { check_dots_empty0(...) force(ambiguous) add_posixt_duration_year_month_day(x, n, invalid, nonexistent, ambiguous, add_months) } add_posixt_duration_year_month_day <- function(x, n, invalid, nonexistent, ambiguous, add_fn) { zone <- posixt_tzone(x) x <- as_year_month_day(x) x <- add_fn(x, n) x <- invalid_resolve(x, invalid = invalid) as.POSIXct(x, tz = zone, nonexistent = nonexistent, ambiguous = ambiguous) } #' @rdname posixt-arithmetic #' @export add_weeks.POSIXt <- function(x, n, ..., nonexistent = NULL, ambiguous = x) { check_dots_empty0(...) force(ambiguous) add_posixt_duration_naive_time_point(x, n, nonexistent, ambiguous, add_weeks) } #' @rdname posixt-arithmetic #' @export add_days.POSIXt <- function(x, n, ..., nonexistent = NULL, ambiguous = x) { check_dots_empty0(...) force(ambiguous) add_posixt_duration_naive_time_point(x, n, nonexistent, ambiguous, add_days) } add_posixt_duration_naive_time_point <- function(x, n, nonexistent, ambiguous, add_fn) { zone <- posixt_tzone(x) x <- as_naive_time(x) x <- add_fn(x, n) as.POSIXct(x, tz = zone, nonexistent = nonexistent, ambiguous = ambiguous) } #' @rdname posixt-arithmetic #' @export add_hours.POSIXt <- function(x, n, ...) { check_dots_empty0(...) add_posixt_duration_sys_time_point(x, n, add_hours) } #' @rdname posixt-arithmetic #' @export add_minutes.POSIXt <- function(x, n, ...) { check_dots_empty0(...) add_posixt_duration_sys_time_point(x, n, add_minutes) } #' @rdname posixt-arithmetic #' @export add_seconds.POSIXt <- function(x, n, ...) { check_dots_empty0(...) add_posixt_duration_sys_time_point(x, n, add_seconds) } add_posixt_duration_sys_time_point <- function(x, n, add_fn) { zone <- posixt_tzone(x) x <- as_sys_time(x) x <- add_fn(x, n) as.POSIXct(x, tz = zone) } # ------------------------------------------------------------------------------ #' Group date-time components #' #' @description #' This is a POSIXct/POSIXlt method for the [date_group()] generic. #' #' `date_group()` groups by a single component of a date-time, such as month #' of the year, day of the month, or hour of the day. #' #' If you need to group by more complex components, like ISO weeks, or quarters, #' convert to a calendar type that contains the component you are interested #' in grouping by. #' #' @inheritParams date_group #' @inheritParams invalid_resolve #' @inheritParams as-zoned-time-naive-time #' #' @param x `[POSIXct / POSIXlt]` #' #' A date-time vector. #' #' @param precision `[character(1)]` #' #' One of: #' #' - `"year"` #' #' - `"month"` #' #' - `"day"` #' #' - `"hour"` #' #' - `"minute"` #' #' - `"second"` #' #' @return `x`, grouped at `precision`. #' #' @name posixt-group #' #' @export #' @examples #' x <- as.POSIXct("2019-01-01", "America/New_York") #' x <- add_days(x, -3:5) #' #' # Group by 2 days of the current month. #' # Note that this resets at the beginning of the month, creating day groups #' # of [29, 30] [31] [01, 02] [03, 04]. #' date_group(x, "day", n = 2) #' #' # Group by month #' date_group(x, "month") #' #' # Group by hour of the day #' y <- as.POSIXct("2019-12-30", "America/New_York") #' y <- add_hours(y, 0:12) #' y #' #' date_group(y, "hour", n = 3) date_group.POSIXt <- function(x, precision, ..., n = 1L, invalid = NULL, nonexistent = NULL, ambiguous = x) { check_dots_empty0(...) force(ambiguous) zone <- date_time_zone(x) x <- as_year_month_day(x) x <- calendar_group(x, precision, n = n) x <- calendar_widen(x, "second") as.POSIXct(x, zone, invalid = invalid, nonexistent = nonexistent, ambiguous = ambiguous) } # ------------------------------------------------------------------------------ #' @export date_leap_year.POSIXt <- function(x) { x <- as_year_month_day(x) calendar_leap_year(x) } # ------------------------------------------------------------------------------ #' Rounding: date-time #' #' @description #' These are POSIXct/POSIXlt methods for the #' [rounding generics][date-and-date-time-rounding]. #' #' - `date_floor()` rounds a date-time down to a multiple of #' the specified `precision`. #' #' - `date_ceiling()` rounds a date-time up to a multiple of #' the specified `precision`. #' #' - `date_round()` rounds up or down depending on what is closer, #' rounding up on ties. #' #' You can group by irregular periods such as `"month"` or `"year"` by using #' [date_group()]. #' #' @details #' When rounding by `"week"`, remember that the `origin` determines the "week #' start". By default, 1970-01-01 is the implicit origin, which is a #' Thursday. If you would like to round by weeks with a different week start, #' just supply an origin on the weekday you are interested in. #' #' @inheritParams date_floor #' @inheritParams as-zoned-time-naive-time #' #' @param x `[POSIXct / POSIXlt]` #' #' A date-time vector. #' #' @param precision `[character(1)]` #' #' One of: #' #' - `"week"` #' #' - `"day"` #' #' - `"hour"` #' #' - `"minute"` #' #' - `"second"` #' #' `"week"` is an alias for `"day"` with `n * 7`. #' #' @param origin `[POSIXct(1) / POSIXlt(1) / NULL]` #' #' An origin to start counting from. #' #' `origin` must have exactly the same time zone as `x`. #' #' `origin` will be floored to `precision`. If information is lost when #' flooring, a warning will be thrown. #' #' If `NULL`, defaults to midnight on 1970-01-01 in the time zone of `x`. #' #' @return `x` rounded to the specified `precision`. #' #' @name posixt-rounding #' #' @examples #' x <- as.POSIXct("2019-03-31", "America/New_York") #' x <- add_days(x, 0:5) #' #' # Flooring by 2 days, note that this is not tied to the current month, #' # and instead counts from the specified `origin`, so groups can cross #' # the month boundary #' date_floor(x, "day", n = 2) #' #' # Compare to `date_group()`, which groups by the day of the month #' date_group(x, "day", n = 2) #' #' # Note that daylight saving time gaps can throw off rounding #' x <- as.POSIXct("1970-04-26 01:59:59", "America/New_York") + c(0, 1) #' x #' #' # Rounding is done in naive-time, which means that rounding by 2 hours #' # will attempt to generate a time of 1970-04-26 02:00:00, which doesn't #' # exist in this time zone #' try(date_floor(x, "hour", n = 2)) #' #' # You can handle this by specifying a nonexistent time resolution strategy #' date_floor(x, "hour", n = 2, nonexistent = "roll-forward") NULL #' @rdname posixt-rounding #' @export date_floor.POSIXt <- function(x, precision, ..., n = 1L, origin = NULL, nonexistent = NULL, ambiguous = x) { check_dots_empty0(...) force(ambiguous) date_time_rounder(x, precision, n, origin, nonexistent, ambiguous, time_point_floor) } #' @rdname posixt-rounding #' @export date_ceiling.POSIXt <- function(x, precision, ..., n = 1L, origin = NULL, nonexistent = NULL, ambiguous = x) { check_dots_empty0(...) force(ambiguous) date_time_rounder(x, precision, n, origin, nonexistent, ambiguous, time_point_ceiling) } #' @rdname posixt-rounding #' @export date_round.POSIXt <- function(x, precision, ..., n = 1L, origin = NULL, nonexistent = NULL, ambiguous = x) { check_dots_empty0(...) force(ambiguous) date_time_rounder(x, precision, n, origin, nonexistent, ambiguous, time_point_round) } date_time_rounder <- function(x, precision, n, origin, nonexistent, ambiguous, time_point_rounder, ..., error_call = caller_env()) { check_dots_empty0(...) result <- tweak_date_rounder_precision(precision, n) precision <- result$precision n <- result$n zone <- date_time_zone(x) x <- as_naive_time(x) if (!is_null(origin)) { origin <- collect_date_time_rounder_origin(origin, zone, precision, error_call = error_call) } x <- time_point_rounder(x, precision, n = n, origin = origin) as.POSIXct(x, zone, nonexistent = nonexistent, ambiguous = ambiguous) } collect_date_time_rounder_origin <- function(origin, zone, precision, error_call) { check_posixt(origin, call = error_call) origin <- to_posixct(origin) vec_check_size(origin, 1L, call = error_call) check_no_missing(origin, call = error_call) if (is.infinite(origin)) { cli::cli_abort("{.arg origin} can't be an infinite date.", call = error_call) } if (!identical(date_time_zone(origin), zone)) { cli::cli_abort("{.arg origin} must have the same time zone as {.arg x}.", call = error_call) } origin <- as_naive_time(origin) # Floor to match the precision of `precision` origin_old <- origin origin <- time_point_floor(origin, precision) # If we lost information while flooring, let the user know if (origin_old != time_point_cast(origin, "second")) { warn_clock_invalid_rounding_origin(precision) } origin } warn_clock_invalid_rounding_origin <- function(precision) { message <- paste0( "{.arg origin} has been floored from {.str second} precision to {.str {precision}} ", "precision to match {.arg precision}. This floor has resulted in a loss of information." ) message <- cli::format_inline(message) rlang::warn(message, class = "clock_warning_invalid_rounding_origin") } # ------------------------------------------------------------------------------ #' Formatting: date-time #' #' @description #' This is a POSIXct method for the [date_format()] generic. #' #' `date_format()` formats a date-time (POSIXct) using a `format` string. #' #' If `format` is `NULL`, a default format of `"%Y-%m-%dT%H:%M:%S%Ez[%Z]"` is #' used. This matches the default format that [date_time_parse_complete()] #' parses. Additionally, this format matches the de-facto standard extension to #' RFC 3339 for creating completely unambiguous date-times. #' #' @inheritParams rlang::args_dots_empty #' @inheritParams format.clock_zoned_time #' #' @param x `[POSIXct / POSIXlt]` #' #' A date-time vector. #' #' @return A character vector of the formatted input. #' #' @name posixt-formatting #' #' @export #' @examples #' x <- date_time_parse( #' c("1970-04-26 01:30:00", "1970-04-26 03:30:00"), #' zone = "America/New_York" #' ) #' #' # Default #' date_format(x) #' #' # Which is parseable by `date_time_parse_complete()` #' date_time_parse_complete(date_format(x)) #' #' date_format(x, format = "%B %d, %Y %H:%M:%S") #' #' # By default, `%Z` uses the full zone name, but you can switch to the #' # abbreviated name #' date_format(x, format = "%z %Z") #' date_format(x, format = "%z %Z", abbreviate_zone = TRUE) date_format.POSIXt <- function(x, ..., format = NULL, locale = clock_locale(), abbreviate_zone = FALSE) { check_dots_empty0(...) x <- as_zoned_time(x) format(x, format = format, locale = locale, abbreviate_zone = abbreviate_zone) } # ------------------------------------------------------------------------------ #' Get or set the time zone #' #' @description #' - `date_time_zone()` gets the time zone. #' #' - `date_time_set_zone()` sets the time zone. This retains the _underlying #' duration_, but changes the _printed time_ depending on the zone that is #' chosen. #' #' @details #' This function is only valid for date-times, as clock treats R's Date class as #' a _naive_ type, which always has a yet-to-be-specified time zone. #' #' @param x `[POSIXct / POSIXlt]` #' #' A date-time vector. #' #' @param zone `[character(1)]` #' #' A valid time zone to switch to. #' #' @return #' - `date_time_zone()` returns a string containing the time zone. #' #' - `date_time_set_zone()` returns `x` with an altered printed time. The #' underlying duration is not changed. #' #' @name date-time-zone #' #' @examples #' library(magrittr) #' #' # Cannot set or get the zone of Date. #' # clock assumes that Dates are naive types, like naive-time. #' x <- date_parse("2019-01-01") #' try(date_time_zone(x)) #' try(date_time_set_zone(x, "America/New_York")) #' #' x <- date_time_parse("2019-01-02 01:30:00", "America/New_York") #' x #' #' date_time_zone(x) #' #' # If it is 1:30am in New York, what time is it in Los Angeles? #' # Same underlying duration, new printed time #' date_time_set_zone(x, "America/Los_Angeles") #' #' # If you want to retain the printed time, but change the underlying duration, #' # convert to a naive-time to drop the time zone, then convert back to a #' # date-time. Be aware that this requires that you handle daylight saving time #' # irregularities with the `nonexistent` and `ambiguous` arguments to #' # `as_date_time()`! #' x %>% #' as_naive_time() %>% #' as_date_time("America/Los_Angeles") #' #' y <- date_time_parse("2021-03-28 03:30:00", "America/New_York") #' y #' #' y_nt <- as_naive_time(y) #' y_nt #' #' # Helsinki had a daylight saving time gap where they jumped from #' # 02:59:59 -> 04:00:00 #' try(as_date_time(y_nt, "Europe/Helsinki")) #' #' as_date_time(y_nt, "Europe/Helsinki", nonexistent = "roll-forward") #' as_date_time(y_nt, "Europe/Helsinki", nonexistent = "roll-backward") NULL #' @rdname date-time-zone #' @export date_time_zone <- function(x) { check_date_zone_error(x) check_posixt(x) posixt_tzone(x) } #' @rdname date-time-zone #' @export date_time_set_zone <- function(x, zone) { check_date_zone_error(x) check_posixt(x) check_zone(zone) x <- to_posixct(x) posixt_set_tzone(x, zone) } check_date_zone_error <- function(x, ..., arg = caller_arg(x), call = caller_env()) { if (!is_date(x)) { return(invisible(NULL)) } message <- c( "{.arg {arg}} can't be a {.cls Date}.", i = "{.cls Date} is considered a naive time with an unspecified time zone.", i = "Time zones can only be get or set for date-times ({.cls POSIXct} or {.cls POSIXlt})." ) cli::cli_abort(message, call = call) } # ------------------------------------------------------------------------------ #' Parsing: date-time #' #' @description #' There are four parsers for parsing strings into POSIXct date-times, #' `date_time_parse()`, `date_time_parse_complete()`, #' `date_time_parse_abbrev()`, and `date_time_parse_RFC_3339()`. #' #' ## date_time_parse() #' #' `date_time_parse()` is useful for strings like `"2019-01-01 00:00:00"`, where #' the UTC offset and full time zone name are not present in the string. The #' string is first parsed as a naive-time without any time zone assumptions, and #' is then converted to a POSIXct with the supplied `zone`. #' #' Because converting from naive-time to POSIXct may result in nonexistent or #' ambiguous times due to daylight saving time, these must be resolved #' explicitly with the `nonexistent` and `ambiguous` arguments. #' #' `date_time_parse()` completely ignores the `%z` and `%Z` commands. The only #' time zone specific information that is used is the `zone`. #' #' The default `format` used is `"%Y-%m-%d %H:%M:%S"`. This matches the default #' result from calling `format()` on a POSIXct date-time. #' #' ## date_time_parse_complete() #' #' `date_time_parse_complete()` is a parser for _complete_ date-time strings, #' like `"2019-01-01T00:00:00-05:00[America/New_York]"`. A complete date-time #' string has both the time zone offset and full time zone name in the string, #' which is the only way for the string itself to contain all of the information #' required to unambiguously construct a zoned-time. Because of this, #' `date_time_parse_complete()` requires both the `%z` and `%Z` commands to be #' supplied in the `format` string. #' #' The default `format` used is `"%Y-%m-%dT%H:%M:%S%Ez[%Z]"`. This matches the #' default result from calling `date_format()` on a POSIXct date-time. #' Additionally, this format matches the de-facto standard extension to RFC 3339 #' for creating completely unambiguous date-times. #' #' ## date_time_parse_abbrev() #' #' `date_time_parse_abbrev()` is a parser for date-time strings containing only #' a time zone abbreviation, like `"2019-01-01 00:00:00 EST"`. The time zone #' abbreviation is not enough to identify the full time zone name that the #' date-time belongs to, so the full time zone name must be supplied as the #' `zone` argument. However, the time zone abbreviation can help with resolving #' ambiguity around daylight saving time fallbacks. #' #' For `date_time_parse_abbrev()`, `%Z` must be supplied and is interpreted as #' the time zone abbreviation rather than the full time zone name. #' #' If used, the `%z` command must parse correctly, but its value will be #' completely ignored. #' #' The default `format` used is `"%Y-%m-%d %H:%M:%S %Z"`. This matches the #' default result from calling `print()` or `format(usetz = TRUE)` on a POSIXct #' date-time. #' #' ## date_time_parse_RFC_3339() #' #' `date_time_parse_RFC_3339()` is a parser for date-time strings in the #' extremely common date-time format outlined by [RFC #' 3339](https://datatracker.ietf.org/doc/html/rfc3339). This document outlines #' a profile of the ISO 8601 format that is even more restrictive, but #' corresponds to the most common formats that are likely to be used in #' internet protocols (i.e. through APIs). #' #' In particular, this function is intended to parse the following three #' formats: #' #' ``` #' 2019-01-01T00:00:00Z #' 2019-01-01T00:00:00+0430 #' 2019-01-01T00:00:00+04:30 #' ``` #' #' This function defaults to parsing the first of these formats by using #' a format string of `"%Y-%m-%dT%H:%M:%SZ"`. #' #' If your date-time strings use offsets from UTC rather than `"Z"`, then set #' `offset` to one of the following: #' #' - `"%z"` if the offset is of the form `"+0430"`. #' - `"%Ez"` if the offset is of the form `"+04:30"`. #' #' The RFC 3339 standard allows for replacing the `"T"` with a `"t"` or a space #' (`" "`). Set `separator` to adjust this as needed. #' #' The date-times returned by this function will always be in the UTC time zone. #' #' @details #' If `date_time_parse_complete()` is given input that is length zero, all #' `NA`s, or completely fails to parse, then no time zone will be able to be #' determined. In that case, the result will use `"UTC"`. #' #' If you have strings with sub-second components, then these date-time parsers #' are not appropriate for you. Remember that clock treats POSIXct as a second #' precision type, so parsing a string with fractional seconds directly into a #' POSIXct is ambiguous and undefined. Instead, fully parse the string, #' including its fractional seconds, into a clock type that can handle it, such #' as a naive-time with [naive_time_parse()], then round to seconds with #' whatever rounding convention is appropriate for your use case, such as #' [time_point_floor()], and finally convert that to POSIXct with #' [as_date_time()]. This gives you complete control over how the fractional #' seconds are handled when converting to POSIXct. #' #' @inheritParams zoned-parsing #' @inheritParams as-zoned-time-naive-time #' @inheritParams sys-parsing #' #' @return A POSIXct. #' #' @name date-time-parse #' #' @examples #' # Parse with a known `zone`, even though that information isn't in the string #' date_time_parse("2020-01-01 05:06:07", "America/New_York") #' #' # Same time as above, except this is a completely unambiguous parse that #' # doesn't require a `zone` argument, because the zone name and offset are #' # both present in the string #' date_time_parse_complete("2020-01-01T05:06:07-05:00[America/New_York]") #' #' # Only day components #' date_time_parse("2020-01-01", "America/New_York", format = "%Y-%m-%d") #' #' # `date_time_parse()` may have issues with ambiguous times due to daylight #' # saving time fallbacks. For example, there were two 1'oclock hours here: #' x <- date_time_parse("1970-10-25 00:59:59", "America/New_York") #' #' # First (earliest) 1'oclock hour #' add_seconds(x, 1) #' # Second (latest) 1'oclock hour #' add_seconds(x, 3601) #' #' # If you try to parse this ambiguous time directly, you'll get an error: #' ambiguous_time <- "1970-10-25 01:00:00" #' try(date_time_parse(ambiguous_time, "America/New_York")) #' #' # Resolve it by specifying whether you'd like to use the #' # `earliest` or `latest` of the two possible times #' date_time_parse(ambiguous_time, "America/New_York", ambiguous = "earliest") #' date_time_parse(ambiguous_time, "America/New_York", ambiguous = "latest") #' #' # `date_time_parse_complete()` doesn't have these issues, as it requires #' # that the offset and zone name are both in the string, which resolves #' # the ambiguity #' complete_times <- c( #' "1970-10-25T01:00:00-04:00[America/New_York]", #' "1970-10-25T01:00:00-05:00[America/New_York]" #' ) #' date_time_parse_complete(complete_times) #' #' # `date_time_parse_abbrev()` also doesn't have these issues, since it #' # uses the time zone abbreviation name to resolve the ambiguity #' abbrev_times <- c( #' "1970-10-25 01:00:00 EDT", #' "1970-10-25 01:00:00 EST" #' ) #' date_time_parse_abbrev(abbrev_times, "America/New_York") #' #' # --------------------------------------------------------------------------- #' # RFC 3339 #' #' # Typical UTC format #' x <- "2019-01-01T00:01:02Z" #' date_time_parse_RFC_3339(x) #' #' # With a UTC offset containing a `:` #' x <- "2019-01-01T00:01:02+02:30" #' date_time_parse_RFC_3339(x, offset = "%Ez") #' #' # With a space between the date and time and no `:` in the offset #' x <- "2019-01-01 00:01:02+0230" #' date_time_parse_RFC_3339(x, separator = " ", offset = "%z") #' #' # --------------------------------------------------------------------------- #' # Sub-second components #' #' # If you have a string with sub-second components, but only require up to #' # seconds, first parse them into a clock type that can handle sub-seconds to #' # fully capture that information, then round using whatever convention is #' # required for your use case before converting to a date-time. #' x <- c("2019-01-01T00:00:01.1", "2019-01-01T00:00:01.78") #' #' x <- naive_time_parse(x, precision = "millisecond") #' x #' #' time_point_floor(x, "second") #' time_point_round(x, "second") #' #' as_date_time(time_point_round(x, "second"), "America/New_York") NULL #' @rdname date-time-parse #' @export date_time_parse <- function(x, zone, ..., format = NULL, locale = clock_locale(), nonexistent = NULL, ambiguous = NULL) { check_dots_empty0(...) if (is_null(format)) { # Default format for `date_time_parse()` doesn't have the `T`, unlike # default format for `naive_time_parse()`. This is intended to parse the # format returned by `format()` by default. format <- "%Y-%m-%d %H:%M:%S" } x <- naive_time_parse(x, format = format, precision = "second", locale = locale) as.POSIXct(x, tz = zone, nonexistent = nonexistent, ambiguous = ambiguous) } #' @rdname date-time-parse #' @export date_time_parse_complete <- function(x, ..., format = NULL, locale = clock_locale()) { check_dots_empty0(...) x <- zoned_time_parse_complete(x, format = format, precision = "second", locale = locale) as.POSIXct(x) } #' @rdname date-time-parse #' @export date_time_parse_abbrev <- function(x, zone, ..., format = NULL, locale = clock_locale()) { check_dots_empty0(...) x <- zoned_time_parse_abbrev(x, zone, format = format, precision = "second", locale = locale) as.POSIXct(x) } #' @rdname date-time-parse #' @export date_time_parse_RFC_3339 <- function(x, ..., separator = "T", offset = "Z") { check_dots_empty0(...) x <- sys_time_parse_RFC_3339(x, separator = separator, offset = offset, precision = "second") # Hard-code UTC because we don't allow optional `zone` arguments that have a # default anywhere else in the package. It seems like it would be bad practice # to have one here, since parsed RFC 3339 strings should probably always be # interpreted as UTC. as.POSIXct(x, tz = "UTC") } # ------------------------------------------------------------------------------ #' Shifting: date and date-time #' #' @description #' `date_shift()` shifts `x` to the `target` weekday. You can shift to the next #' or previous weekday. If `x` is currently on the `target` weekday, you can #' choose to leave it alone or advance it to the next instance of the `target`. #' #' Shifting with date-times retains the time of day where possible. Be aware #' that you can run into daylight saving time issues if you shift into a #' daylight saving time gap or fallback period. #' #' @inheritParams time_point_shift #' @inheritParams as-zoned-time-naive-time #' #' @param x `[POSIXct / POSIXlt]` #' #' A date-time vector. #' #' @return `x` shifted to the `target` weekday. #' #' @name posixt-shifting #' #' @export #' @examples #' tuesday <- weekday(clock_weekdays$tuesday) #' #' x <- as.POSIXct("1970-04-22 02:30:00", "America/New_York") #' #' # Shift to the next Tuesday #' date_shift(x, tuesday) #' #' # Be aware that you can run into daylight saving time issues! #' # Here we shift directly into a daylight saving time gap #' # from 01:59:59 -> 03:00:00 #' sunday <- weekday(clock_weekdays$sunday) #' try(date_shift(x, sunday)) #' #' # You can resolve this with the `nonexistent` argument #' date_shift(x, sunday, nonexistent = "roll-forward") date_shift.POSIXt <- function(x, target, ..., which = "next", boundary = "keep", nonexistent = NULL, ambiguous = x) { check_dots_empty0(...) force(ambiguous) zone <- date_time_zone(x) x <- as_naive_time(x) x <- time_point_shift(x, target, which = which, boundary = boundary) as.POSIXct(x, tz = zone, nonexistent = nonexistent, ambiguous = ambiguous) } # ------------------------------------------------------------------------------ #' Building: date-time #' #' @description #' `date_time_build()` builds a POSIXct from it's individual components. #' #' To build a POSIXct, it is required that you specify the `zone`. #' #' @details #' Components are recycled against each other using #' [tidyverse recycling rules][vctrs::vector_recycling_rules]. #' #' @inheritParams invalid_resolve #' @inheritParams as-zoned-time-naive-time #' #' @param year `[integer]` #' #' The year. Values `[-32767, 32767]` are generally allowed. #' #' @param month `[integer]` #' #' The month. Values `[1, 12]` are allowed. #' #' @param day `[integer / "last"]` #' #' The day of the month. Values `[1, 31]` are allowed. #' #' If `"last"`, then the last day of the month is returned. #' #' @param hour `[integer]` #' #' The hour. Values `[0, 23]` are allowed. #' #' @param minute `[integer]` #' #' The minute. Values `[0, 59]` are allowed. #' #' @param second `[integer]` #' #' The second. Values `[0, 59]` are allowed. #' #' @param zone `[character(1)]` #' #' A valid time zone name. #' #' This argument is required, and must be specified by name. #' #' @return A POSIXct. #' #' @export #' @examples #' # The zone argument is required! #' # clock always requires you to be explicit about your choice of `zone`. #' try(date_time_build(2020)) #' #' date_time_build(2020, zone = "America/New_York") #' #' # Nonexistent time due to daylight saving time gap from 01:59:59 -> 03:00:00 #' try(date_time_build(1970, 4, 26, 1:12, 30, zone = "America/New_York")) #' #' # Resolve with a nonexistent time resolution strategy #' date_time_build( #' 1970, 4, 26, 1:12, 30, #' zone = "America/New_York", #' nonexistent = "roll-forward" #' ) date_time_build <- function(year, month = 1L, day = 1L, hour = 0L, minute = 0L, second = 0L, ..., zone, invalid = NULL, nonexistent = NULL, ambiguous = NULL) { check_dots_empty0(...) if (is_missing(zone)) { abort("`zone` must be supplied.") } x <- year_month_day(year, month, day, hour, minute, second) x <- invalid_resolve(x, invalid = invalid) as.POSIXct(x, tz = zone, nonexistent = nonexistent, ambiguous = ambiguous) } # ------------------------------------------------------------------------------ #' @rdname date-today #' @export date_now <- function(zone) { as.POSIXct(zoned_time_now(zone)) } # ------------------------------------------------------------------------------ #' Info: date-time #' #' @description #' `date_time_info()` retrieves a set of low-level information generally not #' required for most date-time manipulations. It returns a data frame with the #' same columns as [sys_time_info()], but the `begin` and `end` columns are #' date-times with the same time zone as `x`, and the `offset` column is an #' integer rather than a second based [duration][duration_seconds()] column #' since this is part of the high-level API. #' #' @param x `[POSIXct / POSIXlt]` #' #' A date-time. #' #' @return A data frame of low level information. #' #' @export #' @examples #' x <- date_time_build( #' 2021, 03, 14, c(01, 03), c(59, 00), c(59, 00), #' zone = "America/New_York" #' ) #' #' # x[1] is in EST, x[2] is in EDT #' x #' #' info <- date_time_info(x) #' info #' #' # `end` can be used to iterate through daylight saving time transitions #' date_time_info(info$end) date_time_info <- function(x) { check_posixt(x) x <- as_zoned_time(x) out <- zoned_time_info(x) out$begin <- as.POSIXct(out$begin) out$end <- as.POSIXct(out$end) out$offset <- as.integer(out$offset) out } # ------------------------------------------------------------------------------ #' Boundaries: date-time #' #' @description #' This is a POSIXct/POSIXlt method for the [date_start()] and [date_end()] #' generics. #' #' @inheritParams date_group #' @inheritParams invalid_resolve #' @inheritParams as-zoned-time-naive-time #' #' @param x `[POSIXct / POSIXlt]` #' #' A date-time vector. #' #' @param precision `[character(1)]` #' #' One of: #' #' - `"year"` #' #' - `"month"` #' #' - `"day"` #' #' - `"hour"` #' #' - `"minute"` #' #' - `"second"` #' #' @return `x` but with some components altered to be at the boundary value. #' #' @name posixt-boundary #' #' @examples #' x <- date_time_build(2019:2021, 2:4, 3:5, 4, 5, 6, zone = "America/New_York") #' x #' #' # Last moment of the month #' date_end(x, "month") #' #' # Notice that this is different from just setting the day to `"last"` #' set_day(x, "last") #' #' # Last moment of the year #' date_end(x, "year") #' #' # First moment of the hour #' date_start(x, "hour") NULL #' @rdname posixt-boundary #' @export date_start.POSIXt <- function(x, precision, ..., invalid = NULL, nonexistent = NULL, ambiguous = x) { check_dots_empty0(...) force(ambiguous) zone <- date_time_zone(x) x <- as_year_month_day(x) x <- calendar_start(x, precision) as.POSIXct(x, zone, invalid = invalid, nonexistent = nonexistent, ambiguous = ambiguous) } #' @rdname posixt-boundary #' @export date_end.POSIXt <- function(x, precision, ..., invalid = NULL, nonexistent = NULL, ambiguous = x) { check_dots_empty0(...) force(ambiguous) zone <- date_time_zone(x) x <- as_year_month_day(x) x <- calendar_end(x, precision) as.POSIXct(x, zone, invalid = invalid, nonexistent = nonexistent, ambiguous = ambiguous) } # ------------------------------------------------------------------------------ #' Sequences: date-time #' #' @description #' This is a POSIXct method for the [date_seq()] generic. #' #' `date_seq()` generates a date-time (POSIXct) sequence. #' #' When calling `date_seq()`, exactly two of the following must be specified: #' - `to` #' - `by` #' - `total_size` #' #' @section Sequence Generation: #' #' Different methods are used to generate the sequences, depending on the #' precision implied by `by`. They are intended to generate the most intuitive #' sequences, especially around daylight saving time gaps and fallbacks. #' #' See the examples for more details. #' #' ## Calendrical based sequences: #' #' These convert to a naive-time, then to a year-month-day, generate #' the sequence, then convert back to a date-time. #' #' - `by = duration_years()` #' #' - `by = duration_quarters()` #' #' - `by = duration_months()` #' #' ## Naive-time based sequences: #' #' These convert to a naive-time, generate the sequence, then #' convert back to a date-time. #' #' - `by = duration_weeks()` #' #' - `by = duration_days()` #' #' ## Sys-time based sequences: #' #' These convert to a sys-time, generate the sequence, then #' convert back to a date-time. #' #' - `by = duration_hours()` #' #' - `by = duration_minutes()` #' #' - `by = duration_seconds()` #' #' @inheritParams date_seq #' @inheritParams invalid_resolve #' @inheritParams as-zoned-time-naive-time #' #' @param from `[POSIXct(1) / POSIXlt(1)]` #' #' A date-time to start the sequence from. #' #' @param to `[POSIXct(1) / POSIXlt(1) / NULL]` #' #' A date-time to stop the sequence at. #' #' `to` is only included in the result if the resulting sequence divides #' the distance between `from` and `to` exactly. #' #' If `to` is supplied along with `by`, all components of `to` more precise #' than the precision of `by` must match `from` exactly. For example, if `by = #' duration_months(1)`, the day, hour, minute, and second components of `to` #' must match the corresponding components of `from`. This ensures that the #' generated sequence is, at a minimum, a weakly monotonic sequence of #' date-times. #' #' The time zone of `to` must match the time zone of `from` exactly. #' #' @param by `[integer(1) / clock_duration(1) / NULL]` #' #' The unit to increment the sequence by. #' #' If `by` is an integer, it is equivalent to `duration_seconds(by)`. #' #' If `by` is a duration, it is allowed to have a precision of: #' - year #' - quarter #' - month #' - week #' - day #' - hour #' - minute #' - second #' #' @return A date-time vector. #' #' @name posixt-sequence #' #' @export #' @examples #' zone <- "America/New_York" #' #' from <- date_time_build(2019, 1, zone = zone) #' to <- date_time_build(2019, 1, second = 50, zone = zone) #' #' # Defaults to second precision sequence #' date_seq(from, to = to, by = 7) #' #' to <- date_time_build(2019, 1, 5, zone = zone) #' #' # Use durations to change to alternative precisions #' date_seq(from, to = to, by = duration_days(1)) #' date_seq(from, to = to, by = duration_hours(10)) #' date_seq(from, by = duration_minutes(-2), total_size = 3) #' #' # Note that components of `to` more precise than the precision of `by` #' # must match `from` exactly. For example, this is not well defined: #' from <- date_time_build(2019, 1, 1, 0, 1, 30, zone = zone) #' to <- date_time_build(2019, 1, 1, 5, 2, 20, zone = zone) #' try(date_seq(from, to = to, by = duration_hours(1))) #' #' # The minute and second components of `to` must match `from` #' to <- date_time_build(2019, 1, 1, 5, 1, 30, zone = zone) #' date_seq(from, to = to, by = duration_hours(1)) #' #' # --------------------------------------------------------------------------- #' #' # Invalid dates must be resolved with the `invalid` argument #' from <- date_time_build(2019, 1, 31, zone = zone) #' to <- date_time_build(2019, 12, 31, zone = zone) #' #' try(date_seq(from, to = to, by = duration_months(1))) #' date_seq(from, to = to, by = duration_months(1), invalid = "previous-day") #' #' # Compare this to the base R result, which is often a source of confusion #' seq(from, to = to, by = "1 month") #' #' # This is equivalent to the overflow invalid resolution strategy #' date_seq(from, to = to, by = duration_months(1), invalid = "overflow") #' #' # --------------------------------------------------------------------------- #' #' # This date-time is 2 days before a daylight saving time gap that occurred #' # on 2021-03-14 between 01:59:59 -> 03:00:00 #' from <- as.POSIXct("2021-03-12 02:30:00", "America/New_York") #' #' # So creating a daily sequence lands us in that daylight saving time gap, #' # creating a nonexistent time #' try(date_seq(from, by = duration_days(1), total_size = 5)) #' #' # Resolve the nonexistent time with `nonexistent`. Note that this importantly #' # allows times after the gap to retain the `02:30:00` time. #' date_seq(from, by = duration_days(1), total_size = 5, nonexistent = "roll-forward") #' #' # Compare this to the base R behavior, where the hour is adjusted from 2->3 #' # as you cross the daylight saving time gap, and is never restored. This is #' # equivalent to always using sys-time (rather than naive-time, like clock #' # uses for daily sequences). #' seq(from, by = "1 day", length.out = 5) #' #' # You can replicate this behavior by generating a second precision sequence #' # of 86,400 seconds. Seconds always add in sys-time. #' date_seq(from, by = duration_seconds(86400), total_size = 5) #' #' # --------------------------------------------------------------------------- #' #' # Usage of `to` and `total_size` must generate a non-fractional sequence #' # between `from` and `to` #' from <- date_time_build(2019, 1, 1, 0, 0, 0, zone = "America/New_York") #' to <- date_time_build(2019, 1, 1, 0, 0, 3, zone = "America/New_York") #' #' # These are fine #' date_seq(from, to = to, total_size = 2) #' date_seq(from, to = to, total_size = 4) #' #' # But this is not! #' try(date_seq(from, to = to, total_size = 3)) date_seq.POSIXt <- function(from, ..., to = NULL, by = NULL, total_size = NULL, invalid = NULL, nonexistent = NULL, ambiguous = NULL) { check_dots_empty0(...) check_number_of_supplied_optional_arguments(to, by, total_size) from <- to_posixct(from) zone <- date_time_zone(from) if (!is_null(to)) { check_posixt(to) to <- to_posixct(to) if (!identical(zone, date_time_zone(to))) { to_zone <- date_time_zone(to) message <- c( "{.arg from} and {.arg to} must have identical time zones.", i = "{.arg from} has zone {.str {zone}}.", i = "{.arg to} has zone {.str {to_zone}}." ) cli::cli_abort(message) } } if (!is_null(total_size)) { total_size <- check_length_out(total_size) } if (is_null(by)) { precision <- "second" } else if (is_duration(by)) { precision <- duration_precision(by) } else { precision <- "second" by <- duration_helper(by, PRECISION_SECOND) } check_precision(precision) precision_int <- precision_to_integer(precision) if (precision_int == PRECISION_QUARTER) { by <- duration_cast(by, "month") precision <- "month" precision_int <- PRECISION_MONTH } if (precision_int == PRECISION_WEEK) { by <- duration_cast(by, "day") precision <- "day" precision_int <- PRECISION_DAY } if (precision_int %in% c(PRECISION_YEAR, PRECISION_MONTH)) { out <- date_seq_year_month(from, to, by, total_size, precision) out <- invalid_resolve(out, invalid = invalid) out <- as.POSIXct(out, tz = zone, nonexistent = nonexistent, ambiguous = ambiguous) return(out) } if (precision_int == PRECISION_DAY) { out <- date_seq_day(from, to, by, total_size, precision) out <- as.POSIXct(out, tz = zone, nonexistent = nonexistent, ambiguous = ambiguous) return(out) } if (precision_int %in% c(PRECISION_HOUR, PRECISION_MINUTE, PRECISION_SECOND)) { out <- date_seq_hour_minute_second(from, to, by, total_size, precision) out <- as.POSIXct(out, tz = zone) return(out) } precisions <- c("year", "quarter", "month", "week", "day", "hour", "minute", "second") by_precision <- duration_precision(by) cli::cli_abort("`by` must have a precision of {.or {.str {precisions}}}, not {.str {by_precision}}.") } # ------------------------------------------------------------------------------ #' @export date_spanning_seq.POSIXt <- function(x) { zone <- date_time_zone(x) x <- to_posixct(x) x <- vec_drop_infinite(x) x <- as_sys_time(x) x <- time_point_spanning_seq(x) as.POSIXct(x, zone) } # ------------------------------------------------------------------------------ #' Counting: date-times #' #' @description #' This is a POSIXct/POSIXlt method for the [date_count_between()] generic. #' #' `date_count_between()` counts the number of `precision` units between #' `start` and `end` (i.e., the number of years or months). This count #' corresponds to the _whole number_ of units, and will never return a #' fractional value. #' #' This is suitable for, say, computing the whole number of years or months #' between two dates, accounting for the day of the month and the time of day. #' #' Internally, the date-time is converted to one of the following three clock #' types, and the counting is done directly on that type. The choice of type is #' based on the most common interpretation of each precision, but is ultimately #' a heuristic. See the examples for more information. #' #' _Calendrical based counting:_ #' #' These precisions convert to a year-month-day calendar and count while in that #' type. #' #' - `"year"` #' #' - `"quarter"` #' #' - `"month"` #' #' _Naive-time based counting:_ #' #' These precisions convert to a naive-time and count while in that type. #' #' - `"week"` #' #' - `"day"` #' #' _Sys-time based counting:_ #' #' These precisions convert to a sys-time and count while in that type. #' #' - `"hour"` #' #' - `"minute"` #' #' - `"second"` #' #' @details #' `"quarter"` is equivalent to `"month"` precision with `n` set to `n * 3L`. #' #' @inheritSection calendar_count_between Comparison Direction #' #' @inheritParams date_count_between #' #' @param start,end `[POSIXct / POSIXlt]` #' #' A pair of date-time vectors. These will be recycled to their common #' size. #' #' @param precision `[character(1)]` #' #' One of: #' #' - `"year"` #' - `"quarter"` #' - `"month"` #' - `"week"` #' - `"day"` #' - `"hour"` #' - `"minute"` #' - `"second"` #' #' @inherit date_count_between return #' #' @name posixt-count-between #' #' @export #' @examples #' start <- date_time_parse("2000-05-05 02:00:00", zone = "America/New_York") #' end <- date_time_parse( #' c("2020-05-05 01:00:00", "2020-05-05 03:00:00"), #' zone = "America/New_York" #' ) #' #' # Age in years #' date_count_between(start, end, "year") #' #' # Number of "whole" months between these dates. i.e. #' # `2000-05-05 02:00:00 -> 2020-04-05 02:00:00` is 239 months #' # `2000-05-05 02:00:00 -> 2020-05-05 02:00:00` is 240 months #' # Since `2020-05-05 01:00:00` occurs before the 2nd hour, #' # it gets a count of 239 #' date_count_between(start, end, "month") #' #' # Number of seconds between #' date_count_between(start, end, "second") #' #' # --------------------------------------------------------------------------- #' # Naive-time VS Sys-time interpretation #' #' # The difference between whether `start` and `end` are converted to a #' # naive-time vs a sys-time comes into play when dealing with daylight #' # savings. #' #' # Here are two times around a 1 hour DST gap where clocks jumped from #' # 01:59:59 -> 03:00:00 #' x <- date_time_build(1970, 4, 26, 1, 50, 00, zone = "America/New_York") #' y <- date_time_build(1970, 4, 26, 3, 00, 00, zone = "America/New_York") #' #' # When treated like sys-times, these are considered to be 10 minutes apart, #' # which is the amount of time that would have elapsed if you were watching #' # a clock as it changed between these two times. #' date_count_between(x, y, "minute") #' #' # Lets add a 3rd date that is ~1 day ahead of these #' z <- date_time_build(1970, 4, 27, 1, 55, 00, zone = "America/New_York") #' #' # When treated like naive-times, `z` is considered to be at least 1 day ahead #' # of `x`, because `01:55:00` is after `01:50:00`. This is probably what you #' # expected. #' date_count_between(x, z, "day") #' #' # If these were interpreted like sys-times, then `z` would not be considered #' # to be 1 day ahead. That would look something like this: #' date_count_between(x, z, "second") #' trunc(date_count_between(x, z, "second") / 86400) #' #' # This is because there have only been 83,100 elapsed seconds since `x`, #' # which isn't a full day's worth (86,400 seconds). But we'd generally #' # consider `z` to be 1 day ahead of `x` (and ignore the DST gap), so that is #' # how it is implemented. #' #' # You can override this by converting directly to sys-time, then using #' # `time_point_count_between()` #' x_st <- as_sys_time(x) #' x_st #' #' z_st <- as_sys_time(z) #' z_st #' #' time_point_count_between(x_st, z_st, "day") date_count_between.POSIXt <- function(start, end, precision, ..., n = 1L) { check_dots_empty0(...) check_posixt(end) start <- to_posixct(start) end <- to_posixct(end) start_zone <- date_time_zone(start) end_zone <- date_time_zone(end) if (!identical(start_zone, end_zone)) { start_zone <- zone_pretty(start_zone) end_zone <- zone_pretty(end_zone) cli::cli_abort(paste0( "{.arg start} ({start_zone}) and {.arg end} ({end_zone}) ", "must have identical time zones." )) } check_precision(precision) precision_int <- precision_to_integer(precision) # Designed to match `add_*()` functions to guarantee that # if `start <= end`, then `start + <= end` allowed_precisions_calendar <- c( PRECISION_YEAR, PRECISION_QUARTER, PRECISION_MONTH ) allowed_precisions_naive_time <- c( PRECISION_WEEK, PRECISION_DAY ) allowed_precisions_sys_time <- c( PRECISION_HOUR, PRECISION_MINUTE, PRECISION_SECOND ) date_count_between_impl( start = start, end = end, precision = precision, n = n, allowed_precisions_calendar = allowed_precisions_calendar, allowed_precisions_naive_time = allowed_precisions_naive_time, allowed_precisions_sys_time = allowed_precisions_sys_time ) } clock/R/gregorian-year-month-day.R0000644000176200001440000013302514427270231016510 0ustar liggesusers#' Calendar: year-month-day #' #' `year_month_day()` constructs the most common calendar type using the #' Gregorian year, month, day, and time of day components. #' #' @details #' Fields are recycled against each other using #' [tidyverse recycling rules][vctrs::vector_recycling_rules]. #' #' Fields are collected in order until the first `NULL` field is located. No #' fields after the first `NULL` field are used. #' #' @inheritParams rlang::args_dots_empty #' #' @param year `[integer]` #' #' The year. Values `[-32767, 32767]` are generally allowed. #' #' @param month `[integer / NULL]` #' #' The month. Values `[1, 12]` are allowed. #' #' @param day `[integer / "last" / NULL]` #' #' The day of the month. Values `[1, 31]` are allowed. #' #' If `"last"`, then the last day of the month is returned. #' #' @param hour `[integer / NULL]` #' #' The hour. Values `[0, 23]` are allowed. #' #' @param minute `[integer / NULL]` #' #' The minute. Values `[0, 59]` are allowed. #' #' @param second `[integer / NULL]` #' #' The second. Values `[0, 59]` are allowed. #' #' @param subsecond `[integer / NULL]` #' #' The subsecond. If specified, `subsecond_precision` must also be specified #' to determine how to interpret the `subsecond`. #' #' If using milliseconds, values `[0, 999]` are allowed. #' #' If using microseconds, values `[0, 999999]` are allowed. #' #' If using nanoseconds, values `[0, 999999999]` are allowed. #' #' @param subsecond_precision `[character(1) / NULL]` #' #' The precision to interpret `subsecond` as. One of: `"millisecond"`, #' `"microsecond"`, or `"nanosecond"`. #' #' @return A year-month-day calendar vector. #' #' @export #' @examples #' # Just the year #' x <- year_month_day(2019:2025) #' #' # Year-month type #' year_month_day(2020, 1:12) #' #' # The most common use case involves year, month, and day fields #' x <- year_month_day(2020, clock_months$january, 1:5) #' x #' #' # Precision can go all the way out to nanosecond #' year_month_day(2019, 1, 2, 2, 40, 45, 200, subsecond_precision = "nanosecond") year_month_day <- function(year, month = NULL, day = NULL, hour = NULL, minute = NULL, second = NULL, subsecond = NULL, ..., subsecond_precision = NULL) { check_dots_empty() # Stop on the first `NULL` argument if (is_null(month)) { precision <- PRECISION_YEAR fields <- list(year = year) } else if (is_null(day)) { precision <- PRECISION_MONTH fields <- list(year = year, month = month) } else if (is_null(hour)) { precision <- PRECISION_DAY fields <- list(year = year, month = month, day = day) } else if (is_null(minute)) { precision <- PRECISION_HOUR fields <- list(year = year, month = month, day = day, hour = hour) } else if (is_null(second)) { precision <- PRECISION_MINUTE fields <- list(year = year, month = month, day = day, hour = hour, minute = minute) } else if (is_null(subsecond)) { precision <- PRECISION_SECOND fields <- list(year = year, month = month, day = day, hour = hour, minute = minute, second = second) } else { calendar_check_subsecond_precision(subsecond_precision) precision <- precision_to_integer(subsecond_precision) fields <- list(year = year, month = month, day = day, hour = hour, minute = minute, second = second, subsecond = subsecond) } if (is_last(fields$day)) { fields$day <- 1L last <- TRUE } else { last <- FALSE } fields <- vec_cast_common(!!!fields, .to = integer()) if (precision >= PRECISION_YEAR) { check_between_year(fields$year, arg = "year") } if (precision >= PRECISION_MONTH) { check_between_month(fields$month, arg = "month") } if (precision >= PRECISION_DAY) { check_between_day_of_month(fields$day, arg = "day") } if (precision >= PRECISION_HOUR) { check_between_hour(fields$hour, arg = "hour") } if (precision >= PRECISION_MINUTE) { check_between_minute(fields$minute, arg = "minute") } if (precision >= PRECISION_SECOND) { check_between_second(fields$second, arg = "second") } if (precision > PRECISION_SECOND) { check_between_subsecond(fields$subsecond, precision, arg = "subsecond") } fields <- vec_recycle_common(!!!fields) fields <- df_list_propagate_missing(fields) names <- NULL out <- new_year_month_day_from_fields(fields, precision, names) if (last) { out <- set_day(out, "last") } out } # ------------------------------------------------------------------------------ #' @export vec_proxy.clock_year_month_day <- function(x, ...) { .Call(`_clock_clock_rcrd_proxy`, x) } #' @export vec_restore.clock_year_month_day <- function(x, to, ...) { .Call(`_clock_year_month_day_restore`, x, to) } # ------------------------------------------------------------------------------ #' @export format.clock_year_month_day <- function(x, ...) { out <- format_year_month_day_cpp(x, calendar_precision_attribute(x)) names(out) <- names(x) out } #' @export vec_ptype_full.clock_year_month_day <- function(x, ...) { calendar_ptype_full(x, "year_month_day") } #' @export vec_ptype_abbr.clock_year_month_day <- function(x, ...) { calendar_ptype_abbr(x, "ymd") } # ------------------------------------------------------------------------------ #' Parsing: year-month-day #' #' @description #' `year_month_day_parse()` parses strings into a year-month-day. #' #' The default options assume `x` should be parsed at day precision, using a #' `format` string of `"%Y-%m-%d"`. #' #' If a more precise precision than day is used, then time components will also #' be parsed. The default format separates date and time components by a `"T"` #' and the time components by a `":"`. For example, setting the precision to #' `"second"` will use a default format of `"%Y-%m-%dT%H:%M:%S"`. This is #' aligned with the [format()] method for year-month-day, and with the RFC 3339 #' standard. #' #' @details #' `year_month_day_parse()` completely ignores the `%z` and `%Z` commands. #' #' @inheritSection zoned-parsing Full Precision Parsing #' #' @inheritParams zoned-parsing #' #' @param x `[character]` #' #' A character vector to parse. #' #' @param precision `[character(1)]` #' #' A precision for the resulting year-month-day. One of: #' #' - `"year"` #' - `"month"` #' - `"day"` #' - `"hour"` #' - `"minute"` #' - `"second"` #' - `"millisecond"` #' - `"microsecond"` #' - `"nanosecond"` #' #' Setting the `precision` determines how much information `%S` attempts #' to parse. #' #' @param locale `[clock_locale]` #' #' A locale object created by [clock_locale()]. #' #' @return A year-month-day calendar vector. If a parsing fails, `NA` is #' returned. #' #' @export #' @examples #' x <- "2019-01-01" #' #' # Default parses at day precision #' year_month_day_parse(x) #' #' # Can parse at less precise precisions too #' year_month_day_parse(x, precision = "month") #' year_month_day_parse(x, precision = "year") #' #' # Even invalid dates can be round-tripped through format<->parse calls #' invalid <- year_month_day(2019, 2, 30) #' year_month_day_parse(format(invalid)) #' #' # Can parse with time of day #' year_month_day_parse( #' "2019-01-30T02:30:00.123456789", #' precision = "nanosecond" #' ) #' #' # Can parse using multiple format strings, which will be tried #' # in the order they are provided #' x <- c("2019-01-01", "2020-01-01", "2021/2/3") #' formats <- c("%Y-%m-%d", "%Y/%m/%d") #' year_month_day_parse(x, format = formats) #' #' # Can parse using other format tokens as well #' year_month_day_parse( #' "January, 2019", #' format = "%B, %Y", #' precision = "month" #' ) #' #' # Parsing a French year-month-day #' year_month_day_parse( #' "octobre 1, 2000", #' format = "%B %d, %Y", #' locale = clock_locale("fr") #' ) year_month_day_parse <- function(x, ..., format = NULL, precision = "day", locale = clock_locale()) { check_dots_empty() if (!is_character(x)) { abort("`x` must be a character vector.") } year_month_day_check_precision(precision) precision <- precision_to_integer(precision) if (is_null(format)) { format <- year_month_day_format(precision) } check_clock_locale(locale) labels <- locale$labels mark <- locale$decimal_mark fields <- year_month_day_parse_cpp( x, format, precision, labels$month, labels$month_abbrev, labels$weekday, labels$weekday_abbrev, labels$am_pm, mark ) new_year_month_day_from_fields(fields, precision, names(x)) } year_month_day_format <- function(precision) { precision <- precision_to_string(precision) switch( precision, year = "%Y", month = "%Y-%m", day = "%Y-%m-%d", hour = "%Y-%m-%dT%H", minute = "%Y-%m-%dT%H:%M", second = , millisecond = , microsecond = , nanosecond = "%Y-%m-%dT%H:%M:%S", abort("Internal error: Unknown precision.") ) } # ------------------------------------------------------------------------------ #' Is `x` a year-month-day? #' #' Check if `x` is a year-month-day. #' #' @param x `[object]` #' #' An object. #' #' @return Returns `TRUE` if `x` inherits from `"clock_year_month_day"`, #' otherwise returns `FALSE`. #' #' @export #' @examples #' is_year_month_day(year_month_day(2019)) is_year_month_day <- function(x) { inherits(x, "clock_year_month_day") } # ------------------------------------------------------------------------------ #' @export vec_ptype.clock_year_month_day <- function(x, ...) { switch( calendar_precision_attribute(x) + 1L, clock_empty_year_month_day_year, abort("Internal error: Invalid precision"), clock_empty_year_month_day_month, abort("Internal error: Invalid precision"), clock_empty_year_month_day_day, clock_empty_year_month_day_hour, clock_empty_year_month_day_minute, clock_empty_year_month_day_second, clock_empty_year_month_day_millisecond, clock_empty_year_month_day_microsecond, clock_empty_year_month_day_nanosecond, abort("Internal error: Invalid precision.") ) } #' @export vec_ptype2.clock_year_month_day.clock_year_month_day <- function(x, y, ...) { ptype2_calendar_and_calendar(x, y, ...) } #' @export vec_cast.clock_year_month_day.clock_year_month_day <- function(x, to, ...) { cast_calendar_to_calendar(x, to, ...) } # ------------------------------------------------------------------------------ #' @export calendar_is_precision.clock_year_month_day <- function(x, precision) { year_month_day_is_precision(precision) } year_month_day_is_precision <- function(precision) { if (precision == PRECISION_YEAR || precision == PRECISION_MONTH) { TRUE } else if (precision >= PRECISION_DAY && precision <= PRECISION_NANOSECOND) { TRUE } else { FALSE } } year_month_day_check_precision <- function(x, ..., arg = caller_arg(x), call = caller_env()) { check_precision( x = x, values = year_month_day_precision_names(), arg = arg, call = call ) } year_month_day_precision_names <- function() { c("year", "month", "day", precision_time_names()) } # ------------------------------------------------------------------------------ #' @export invalid_detect.clock_year_month_day <- function(x) { precision <- calendar_precision_attribute(x) if (precision < PRECISION_DAY) { rep_along(x, FALSE) } else { year <- field_year(x) month <- field_month(x) day <- field_day(x) invalid_detect_year_month_day_cpp(year, month, day) } } #' @export invalid_any.clock_year_month_day <- function(x) { precision <- calendar_precision_attribute(x) if (precision < PRECISION_DAY) { FALSE } else { year <- field_year(x) month <- field_month(x) day <- field_day(x) invalid_any_year_month_day_cpp(year, month, day) } } #' @export invalid_count.clock_year_month_day <- function(x) { precision <- calendar_precision_attribute(x) if (precision < PRECISION_DAY) { 0L } else { year <- field_year(x) month <- field_month(x) day <- field_day(x) invalid_count_year_month_day_cpp(year, month, day) } } #' @export invalid_resolve.clock_year_month_day <- function(x, ..., invalid = NULL) { check_dots_empty() precision <- calendar_precision_attribute(x) invalid <- validate_invalid(invalid) if (precision < PRECISION_DAY) { x } else { fields <- invalid_resolve_year_month_day_cpp(x, precision, invalid, current_env()) new_year_month_day_from_fields(fields, precision, names(x)) } } # ------------------------------------------------------------------------------ #' Getters: year-month-day #' #' @description #' These are year-month-day methods for the [getter generics][clock-getters]. #' #' - `get_year()` returns the Gregorian year. #' #' - `get_month()` returns the month of the year. #' #' - `get_day()` returns the day of the month. #' #' - There are sub-daily getters for extracting more precise components. #' #' @param x `[clock_year_month_day]` #' #' A year-month-day to get the component from. #' #' @return The component. #' #' @name year-month-day-getters #' @examples #' x <- year_month_day(2019, 1:3, 5:7, 1, 20, 30) #' #' get_month(x) #' get_day(x) #' get_second(x) #' #' # Cannot extract more precise components #' y <- year_month_day(2019, 1) #' try(get_day(y)) #' #' # Cannot extract components that don't exist for this calendar #' try(get_quarter(x)) NULL #' @rdname year-month-day-getters #' @export get_year.clock_year_month_day <- function(x) { field_year(x) } #' @rdname year-month-day-getters #' @export get_month.clock_year_month_day <- function(x) { calendar_check_minimum_precision(x, PRECISION_MONTH) field_month(x) } #' @rdname year-month-day-getters #' @export get_day.clock_year_month_day <- function(x) { calendar_check_minimum_precision(x, PRECISION_DAY) field_day(x) } #' @rdname year-month-day-getters #' @export get_hour.clock_year_month_day <- function(x) { calendar_check_minimum_precision(x, PRECISION_HOUR) field_hour(x) } #' @rdname year-month-day-getters #' @export get_minute.clock_year_month_day <- function(x) { calendar_check_minimum_precision(x, PRECISION_MINUTE) field_minute(x) } #' @rdname year-month-day-getters #' @export get_second.clock_year_month_day <- function(x) { calendar_check_minimum_precision(x, PRECISION_SECOND) field_second(x) } #' @rdname year-month-day-getters #' @export get_millisecond.clock_year_month_day <- function(x) { calendar_check_exact_precision(x, PRECISION_MILLISECOND) field_subsecond(x) } #' @rdname year-month-day-getters #' @export get_microsecond.clock_year_month_day <- function(x) { calendar_check_exact_precision(x, PRECISION_MICROSECOND) field_subsecond(x) } #' @rdname year-month-day-getters #' @export get_nanosecond.clock_year_month_day <- function(x) { calendar_check_exact_precision(x, PRECISION_NANOSECOND) field_subsecond(x) } # ------------------------------------------------------------------------------ #' Setters: year-month-day #' #' @description #' These are year-month-day methods for the #' [setter generics][clock-setters]. #' #' - `set_year()` sets the Gregorian year. #' #' - `set_month()` sets the month of the year. Valid values are in the range #' of `[1, 12]`. #' #' - `set_day()` sets the day of the month. Valid values are in the range #' of `[1, 31]`. #' #' - There are sub-daily setters for setting more precise components. #' #' @inheritParams rlang::args_dots_empty #' #' @param x `[clock_year_month_day]` #' #' A year-month-day vector. #' #' @param value `[integer / "last"]` #' #' The value to set the component to. #' #' For `set_day()`, this can also be `"last"` to set the day to the #' last day of the month. #' #' @return `x` with the component set. #' #' @name year-month-day-setters #' @examples #' x <- year_month_day(2019, 1:3) #' #' # Set the day #' set_day(x, 12:14) #' #' # Set to the "last" day of the month #' set_day(x, "last") #' #' # Set to an invalid day of the month #' invalid <- set_day(x, 31) #' invalid #' #' # Then resolve the invalid day by choosing the next valid day #' invalid_resolve(invalid, invalid = "next") #' #' # Cannot set a component two levels more precise than where you currently are #' try(set_hour(x, 5)) NULL #' @rdname year-month-day-setters #' @export set_year.clock_year_month_day <- function(x, value, ...) { check_dots_empty() set_field_year_month_day(x, value, "year") } #' @rdname year-month-day-setters #' @export set_month.clock_year_month_day <- function(x, value, ...) { check_dots_empty() calendar_check_minimum_precision(x, PRECISION_YEAR) set_field_year_month_day(x, value, "month") } #' @rdname year-month-day-setters #' @export set_day.clock_year_month_day <- function(x, value, ...) { check_dots_empty() calendar_check_minimum_precision(x, PRECISION_MONTH) set_field_year_month_day(x, value, "day") } #' @rdname year-month-day-setters #' @export set_hour.clock_year_month_day <- function(x, value, ...) { check_dots_empty() calendar_check_minimum_precision(x, PRECISION_DAY) set_field_year_month_day(x, value, "hour") } #' @rdname year-month-day-setters #' @export set_minute.clock_year_month_day <- function(x, value, ...) { check_dots_empty() calendar_check_minimum_precision(x, PRECISION_HOUR) set_field_year_month_day(x, value, "minute") } #' @rdname year-month-day-setters #' @export set_second.clock_year_month_day <- function(x, value, ...) { check_dots_empty() calendar_check_minimum_precision(x, PRECISION_MINUTE) set_field_year_month_day(x, value, "second") } #' @rdname year-month-day-setters #' @export set_millisecond.clock_year_month_day <- function(x, value, ...) { check_dots_empty() calendar_check_exact_precision(x, c(PRECISION_SECOND, PRECISION_MILLISECOND)) set_field_year_month_day(x, value, "millisecond") } #' @rdname year-month-day-setters #' @export set_microsecond.clock_year_month_day <- function(x, value, ...) { check_dots_empty() calendar_check_exact_precision(x, c(PRECISION_SECOND, PRECISION_MICROSECOND)) set_field_year_month_day(x, value, "microsecond") } #' @rdname year-month-day-setters #' @export set_nanosecond.clock_year_month_day <- function(x, value, ...) { check_dots_empty() calendar_check_exact_precision(x, c(PRECISION_SECOND, PRECISION_NANOSECOND)) set_field_year_month_day(x, value, "nanosecond") } set_field_year_month_day <- function(x, value, component) { if (is_last(value) && identical(component, "day")) { return(set_field_year_month_day_last(x)) } precision_fields <- calendar_precision_attribute(x) precision_value <- year_month_day_component_to_precision(component) precision_out <- precision_common2(precision_fields, precision_value) names_out <- names(x) value <- vec_cast(value, integer()) value <- unname(value) switch( component, year = check_between_year(value), month = check_between_month(value), day = check_between_day_of_month(value), hour = check_between_hour(value), minute = check_between_minute(value), second = check_between_second(value), millisecond = check_between_subsecond(value, PRECISION_MILLISECOND), microsecond = check_between_subsecond(value, PRECISION_MICROSECOND), nanosecond = check_between_subsecond(value, PRECISION_NANOSECOND), abort("Unknown `component`", .internal = TRUE) ) args <- vec_recycle_common(x = x, value = value) args <- df_list_propagate_missing(args) x <- args$x value <- args$value field <- year_month_day_component_to_field(component) out <- vec_unstructure(x) out[[field]] <- value new_year_month_day_from_fields(out, precision_out, names = names_out) } set_field_year_month_day_last <- function(x) { precision_fields <- calendar_precision_attribute(x) precision_out <- precision_common2(precision_fields, PRECISION_DAY) names_out <- names(x) year <- field_year(x) month <- field_month(x) value <- get_year_month_day_last_cpp(year, month) out <- vec_unstructure(x) out[["day"]] <- value new_year_month_day_from_fields(out, precision_out, names = names_out) } # ------------------------------------------------------------------------------ #' @export calendar_name.clock_year_month_day <- function(x) { "year_month_day" } # ------------------------------------------------------------------------------ year_month_day_component_to_precision <- function(component) { switch( component, year = PRECISION_YEAR, month = PRECISION_MONTH, day = PRECISION_DAY, hour = PRECISION_HOUR, minute = PRECISION_MINUTE, second = PRECISION_SECOND, millisecond = PRECISION_MILLISECOND, microsecond = PRECISION_MICROSECOND, nanosecond = PRECISION_NANOSECOND, abort("Internal error: Unknown component name.") ) } year_month_day_component_to_field <- function(component) { switch ( component, year = component, month = component, day = component, hour = component, minute = component, second = component, millisecond = "subsecond", microsecond = "subsecond", nanosecond = "subsecond", abort("Internal error: Unknown component name.") ) } # ------------------------------------------------------------------------------ #' Support for vctrs arithmetic #' #' @inheritParams vctrs::vec_arith #' @name clock-arith #' #' @return The result of the arithmetic operation. #' @examples #' vctrs::vec_arith("+", year_month_day(2019), 1) NULL #' @rdname clock-arith #' @method vec_arith clock_year_month_day #' @export vec_arith.clock_year_month_day <- function(op, x, y, ...) { UseMethod("vec_arith.clock_year_month_day", y) } #' @method vec_arith.clock_year_month_day MISSING #' @export vec_arith.clock_year_month_day.MISSING <- function(op, x, y, ...) { arith_calendar_and_missing(op, x, y, ...) } #' @method vec_arith.clock_year_month_day clock_year_month_day #' @export vec_arith.clock_year_month_day.clock_year_month_day <- function(op, x, y, ...) { arith_calendar_and_calendar(op, x, y, ..., calendar_minus_calendar_fn = year_month_day_minus_year_month_day) } #' @method vec_arith.clock_year_month_day clock_duration #' @export vec_arith.clock_year_month_day.clock_duration <- function(op, x, y, ...) { arith_calendar_and_duration(op, x, y, ...) } #' @method vec_arith.clock_duration clock_year_month_day #' @export vec_arith.clock_duration.clock_year_month_day <- function(op, x, y, ...) { arith_duration_and_calendar(op, x, y, ...) } #' @method vec_arith.clock_year_month_day numeric #' @export vec_arith.clock_year_month_day.numeric <- function(op, x, y, ...) { arith_calendar_and_numeric(op, x, y, ...) } #' @method vec_arith.numeric clock_year_month_day #' @export vec_arith.numeric.clock_year_month_day <- function(op, x, y, ...) { arith_numeric_and_calendar(op, x, y, ...) } year_month_day_minus_year_month_day <- function(op, x, y, ...) { args <- vec_recycle_common(x = x, y = y) args <- vec_cast_common(!!!args) x <- args$x y <- args$y names <- names_common(x, y) precision <- calendar_precision_attribute(x) if (precision > PRECISION_MONTH) { stop_incompatible_op(op, x, y, ...) } fields <- year_month_day_minus_year_month_day_cpp(x, y, precision) new_duration_from_fields(fields, precision, names) } # ------------------------------------------------------------------------------ #' Arithmetic: year-month-day #' #' @description #' These are year-month-day methods for the #' [arithmetic generics][clock-arithmetic]. #' #' - `add_years()` #' #' - `add_quarters()` #' #' - `add_months()` #' #' Notably, _you cannot add days to a year-month-day_. For day-based arithmetic, #' first convert to a time point with [as_naive_time()] or [as_sys_time()]. #' #' @details #' Adding a single quarter with `add_quarters()` is equivalent to adding #' 3 months. #' #' `x` and `n` are recycled against each other using #' [tidyverse recycling rules][vctrs::vector_recycling_rules]. #' #' @inheritParams clock-arithmetic #' #' @param x `[clock_year_month_day]` #' #' A year-month-day vector. #' #' @return `x` after performing the arithmetic. #' #' @name year-month-day-arithmetic #' #' @examples #' x <- year_month_day(2019, 1, 1) #' #' add_years(x, 1:5) #' #' y <- year_month_day(2019, 1, 31) #' #' # Adding 1 month to `y` generates an invalid date #' y_plus <- add_months(y, 1:2) #' y_plus #' #' # Invalid dates are fine, as long as they are eventually resolved #' # by either manually resolving, or by calling `invalid_resolve()` #' #' # Resolve by returning the previous / next valid moment in time #' invalid_resolve(y_plus, invalid = "previous") #' invalid_resolve(y_plus, invalid = "next") #' #' # Manually resolve by setting to the last day of the month #' invalid <- invalid_detect(y_plus) #' y_plus[invalid] <- set_day(y_plus[invalid], "last") #' y_plus NULL #' @rdname year-month-day-arithmetic #' @export add_years.clock_year_month_day <- function(x, n, ...) { year_month_day_plus_duration(x, n, PRECISION_YEAR) } #' @rdname year-month-day-arithmetic #' @export add_quarters.clock_year_month_day <- function(x, n, ...) { calendar_check_minimum_precision(x, PRECISION_MONTH) year_month_day_plus_duration(x, n, PRECISION_QUARTER) } #' @rdname year-month-day-arithmetic #' @export add_months.clock_year_month_day <- function(x, n, ...) { calendar_check_minimum_precision(x, PRECISION_MONTH) year_month_day_plus_duration(x, n, PRECISION_MONTH) } year_month_day_plus_duration <- function(x, n, n_precision, ..., error_call = caller_env()) { check_dots_empty0(...) x_precision <- calendar_precision_attribute(x) n <- duration_collect_n(n, n_precision, error_call = error_call) if (n_precision == PRECISION_QUARTER) { n <- duration_cast(n, "month") n_precision <- PRECISION_MONTH } size <- vec_size_common(x = x, n = n, .call = error_call) args <- vec_recycle_common(x = x, n = n, .size = size) x <- args$x n <- args$n names <- names_common(x, n) x <- vec_unstructure(x) if (n_precision == PRECISION_YEAR) { fields <- year_month_day_plus_years_cpp(x$year, n) x$year <- fields$year } else if (n_precision == PRECISION_MONTH) { fields <- year_month_day_plus_months_cpp(x$year, x$month, n) x$year <- fields$year x$month <- fields$month } else { abort("Unknown precision.", .internal = TRUE) } if (x_precision != n_precision) { x <- df_list_propagate_missing(x, size = size) } new_year_month_day_from_fields(x, x_precision, names = names) } # ------------------------------------------------------------------------------ #' Convert to year-month-day #' #' `as_year_month_day()` converts a vector to the year-month-day calendar. #' Time points, Dates, POSIXct, and other calendars can all be converted to #' year-month-day. #' #' @inheritParams rlang::args_dots_empty #' #' @param x `[vector]` #' #' A vector to convert to year-month-day. #' #' @return A year-month-day vector. #' @export #' @examples #' # From Date #' as_year_month_day(as.Date("2019-01-01")) #' #' # From POSIXct, which assumes that the naive time is what should be converted #' as_year_month_day(as.POSIXct("2019-01-01 02:30:30", "America/New_York")) #' #' # From other calendars #' as_year_month_day(year_quarter_day(2019, quarter = 2, day = 50)) as_year_month_day <- function(x, ...) { UseMethod("as_year_month_day") } #' @export as_year_month_day.default <- function(x, ...) { stop_clock_unsupported_conversion(x, "clock_year_month_day") } #' @export as_year_month_day.clock_year_month_day <- function(x, ...) { check_dots_empty0(...) x } # ------------------------------------------------------------------------------ #' @export as_sys_time.clock_year_month_day <- function(x, ...) { check_dots_empty0(...) calendar_check_no_invalid(x) precision <- calendar_precision_attribute(x) fields <- as_sys_time_year_month_day_cpp(x, precision) new_sys_time_from_fields(fields, precision, clock_rcrd_names(x)) } #' @export as_naive_time.clock_year_month_day <- function(x, ...) { check_dots_empty0(...) as_naive_time(as_sys_time(x)) } #' @export as.character.clock_year_month_day <- function(x, ...) { format(x) } # ------------------------------------------------------------------------------ #' @export calendar_leap_year.clock_year_month_day <- function(x) { x <- get_year(x) gregorian_leap_year_cpp(x) } # ------------------------------------------------------------------------------ #' @export calendar_month_factor.clock_year_month_day <- function(x, ..., labels = "en", abbreviate = FALSE) { check_dots_empty0(...) calendar_month_factor_impl(x, labels, abbreviate) } # ------------------------------------------------------------------------------ #' Grouping: year-month-day #' #' @description #' This is a year-month-day method for the [calendar_group()] generic. #' #' Grouping for a year-month-day object can be done at any precision, as #' long as `x` is at least as precise as `precision`. #' #' @inheritParams calendar_group #' #' @param x `[clock_year_month_day]` #' #' A year-month-day vector. #' #' @param precision `[character(1)]` #' #' One of: #' #' - `"year"` #' - `"month"` #' - `"day"` #' - `"hour"` #' - `"minute"` #' - `"second"` #' - `"millisecond"` #' - `"microsecond"` #' - `"nanosecond"` #' #' @return `x` grouped at the specified `precision`. #' #' @name year-month-day-group #' #' @export #' @examples #' steps <- duration_days(seq(0, 100, by = 5)) #' x <- year_month_day(2019, 1, 1) #' x <- as_naive_time(x) + steps #' x <- as_year_month_day(x) #' x #' #' # Group by a single month #' calendar_group(x, "month") #' #' # Or multiple months #' calendar_group(x, "month", n = 2) #' #' # Group 3 days of the month together #' y <- year_month_day(2019, 1, 1:12) #' calendar_group(y, "day", n = 3) #' #' # Group by 5 nanosecond of the current second #' z <- year_month_day( #' 2019, 1, 2, 1, 5, 20, 1:20, #' subsecond_precision = "nanosecond" #' ) #' calendar_group(z, "nanosecond", n = 5) calendar_group.clock_year_month_day <- function(x, precision, ..., n = 1L) { n <- validate_calendar_group_n(n) x <- calendar_narrow(x, precision) check_precision(precision) precision <- precision_to_integer(precision) if (precision == PRECISION_YEAR) { value <- get_year(x) value <- group_component0(value, n) x <- set_year(x, value) return(x) } if (precision == PRECISION_MONTH) { value <- get_month(x) value <- group_component1(value, n) x <- set_month(x, value) return(x) } if (precision == PRECISION_DAY) { value <- get_day(x) value <- group_component1(value, n) x <- set_day(x, value) return(x) } x <- calendar_group_time(x, n, precision) x } # ------------------------------------------------------------------------------ #' Narrow: year-month-day #' #' This is a year-month-day method for the [calendar_narrow()] generic. It #' narrows a year-month-day vector to the specified `precision`. #' #' @inheritParams year-month-day-group #' #' @return `x` narrowed to the supplied `precision`. #' #' @name year-month-day-narrow #' #' @export #' @examples #' # Hour precision #' x <- year_month_day(2019, 1, 3, 4) #' x #' #' # Narrowed to day precision #' calendar_narrow(x, "day") #' #' # Or month precision #' calendar_narrow(x, "month") #' #' # Subsecond precision can be narrowed to second precision #' milli <- calendar_widen(x, "millisecond") #' micro <- calendar_widen(x, "microsecond") #' milli #' micro #' #' calendar_narrow(milli, "second") #' calendar_narrow(micro, "second") #' #' # But once you have "locked in" a subsecond precision, it can't be #' # narrowed to another subsecond precision #' try(calendar_narrow(micro, "millisecond")) calendar_narrow.clock_year_month_day <- function(x, precision) { check_precision(precision) precision <- precision_to_integer(precision) out_fields <- list() x_fields <- unclass(x) if (precision >= PRECISION_YEAR) { out_fields[["year"]] <- x_fields[["year"]] } if (precision >= PRECISION_MONTH) { out_fields[["month"]] <- x_fields[["month"]] } if (precision >= PRECISION_DAY) { out_fields[["day"]] <- x_fields[["day"]] } out_fields <- calendar_narrow_time(out_fields, precision, x_fields) new_year_month_day_from_fields(out_fields, precision, names = names(x)) } # ------------------------------------------------------------------------------ #' Widen: year-month-day #' #' This is a year-month-day method for the [calendar_widen()] generic. It #' widens a year-month-day vector to the specified `precision`. #' #' @inheritParams year-month-day-group #' #' @return `x` widened to the supplied `precision`. #' #' @name year-month-day-widen #' #' @export #' @examples #' # Month precision #' x <- year_month_day(2019, 1) #' x #' #' # Widen to day precision #' calendar_widen(x, "day") #' #' # Or second precision #' sec <- calendar_widen(x, "second") #' sec #' #' # Second precision can be widened to subsecond precision #' milli <- calendar_widen(sec, "millisecond") #' micro <- calendar_widen(sec, "microsecond") #' milli #' micro #' #' # But once you have "locked in" a subsecond precision, it can't #' # be widened again #' try(calendar_widen(milli, "microsecond")) calendar_widen.clock_year_month_day <- function(x, precision) { x_precision <- calendar_precision_attribute(x) check_precision(precision) precision <- precision_to_integer(precision) if (precision >= PRECISION_MONTH && x_precision < PRECISION_MONTH) { x <- set_month(x, 1L) } if (precision >= PRECISION_DAY && x_precision < PRECISION_DAY) { x <- set_day(x, 1L) } x <- calendar_widen_time(x, x_precision, precision) x } # ------------------------------------------------------------------------------ #' Boundaries: year-month-day #' #' This is a year-month-day method for the [calendar_start()] and #' [calendar_end()] generics. They adjust components of a calendar to the #' start or end of a specified `precision`. #' #' @inheritParams year-month-day-group #' #' @return `x` at the same precision, but with some components altered to be #' at the boundary value. #' #' @name year-month-day-boundary #' #' @examples #' # Hour precision #' x <- year_month_day(2019, 2:4, 5, 6) #' x #' #' # Compute the start of the month #' calendar_start(x, "month") #' #' # Or the end of the month, notice that the hour value is adjusted as well #' calendar_end(x, "month") #' #' # Compare that with just setting the day of the month to `"last"`, which #' # doesn't adjust any other components #' set_day(x, "last") #' #' # You can't compute the start / end at a more precise precision than #' # the input is at #' try(calendar_start(x, "second")) NULL #' @rdname year-month-day-boundary #' @export calendar_start.clock_year_month_day <- function(x, precision) { x_precision <- calendar_precision_attribute(x) check_precision(precision) precision <- precision_to_integer(precision) calendar_start_end_checks(x, x_precision, precision, "start") if (precision <= PRECISION_YEAR && x_precision > PRECISION_YEAR) { x <- set_month(x, 1L) } if (precision <= PRECISION_MONTH && x_precision > PRECISION_MONTH) { x <- set_day(x, 1L) } x <- calendar_start_time(x, x_precision, precision) x } #' @rdname year-month-day-boundary #' @export calendar_end.clock_year_month_day <- function(x, precision) { x_precision <- calendar_precision_attribute(x) check_precision(precision) precision <- precision_to_integer(precision) calendar_start_end_checks(x, x_precision, precision, "end") if (precision <= PRECISION_YEAR && x_precision > PRECISION_YEAR) { x <- set_month(x, 12L) } if (precision <= PRECISION_MONTH && x_precision > PRECISION_MONTH) { x <- set_day(x, "last") } x <- calendar_end_time(x, x_precision, precision) x } # ------------------------------------------------------------------------------ #' Counting: year-month-day #' #' This is a year-month-day method for the [calendar_count_between()] generic. #' It counts the number of `precision` units between `start` and `end` #' (i.e., the number of years or months). #' #' @details #' `"quarter"` is equivalent to `"month"` precision with `n` set to `n * 3L`. #' #' @inheritParams calendar-count-between #' #' @param start,end `[clock_year_month_day]` #' #' A pair of year-month-day vectors. These will be recycled to their #' common size. #' #' @param precision `[character(1)]` #' #' One of: #' #' - `"year"` #' - `"quarter"` #' - `"month"` #' #' @inherit calendar-count-between return #' #' @name year-month-day-count-between #' #' @export #' @examples #' # Compute an individual's age in years #' x <- year_month_day(2001, 2, 4) #' today <- year_month_day(2021, 11, 30) #' calendar_count_between(x, today, "year") #' #' # Compute the number of months between two dates, taking #' # into account the day of the month and time of day #' x <- year_month_day(2000, 4, 2, 5) #' y <- year_month_day(2000, 7, c(1, 2, 2), c(3, 4, 6)) #' calendar_count_between(x, y, "month") calendar_count_between.clock_year_month_day <- function(start, end, precision, ..., n = 1L) { NextMethod() } calendar_count_between_standardize_precision_n.clock_year_month_day <- function(x, precision, n) { check_precision(precision) precision_int <- precision_to_integer(precision) allowed_precisions <- c(PRECISION_YEAR, PRECISION_QUARTER, PRECISION_MONTH) if (!(precision_int %in% allowed_precisions)) { abort("`precision` must be one of: 'year', 'quarter', 'month'.") } if (precision_int == PRECISION_QUARTER) { # See #270's comment for why this is well-defined precision <- "month" n <- n * 3L } list(precision = precision, n = n) } calendar_count_between_compute.clock_year_month_day <- function(start, end, precision) { check_precision(precision) precision <- precision_to_integer(precision) if (precision == PRECISION_YEAR) { out <- get_year(end) - get_year(start) return(out) } if (precision == PRECISION_MONTH) { out <- (get_year(end) - get_year(start)) * 12L out <- out + (get_month(end) - get_month(start)) return(out) } abort("Internal error: `precision` should be 'year' or 'month' at this point.") } calendar_count_between_proxy_compare.clock_year_month_day <- function(start, end, precision) { check_precision(precision) precision <- precision_to_integer(precision) start <- vec_proxy_compare(start) end <- vec_proxy_compare(end) if (precision >= PRECISION_YEAR) { start$year <- NULL end$year <- NULL } if (precision >= PRECISION_MONTH) { start$month <- NULL end$month <- NULL } list(start = start, end = end) } # ------------------------------------------------------------------------------ #' Sequences: year-month-day #' #' @description #' This is a year-month-day method for the [seq()] generic. #' #' Sequences can only be generated for `"year"` and `"month"` precision #' year-month-day vectors. #' #' When calling `seq()`, exactly two of the following must be specified: #' - `to` #' - `by` #' - Either `length.out` or `along.with` #' #' @inheritParams seq.clock_duration #' #' @param from `[clock_year_month_day(1)]` #' #' A `"year"` or `"month"` precision year-month-day to start the sequence #' from. #' #' `from` is always included in the result. #' #' @param to `[clock_year_month_day(1) / NULL]` #' #' A `"year"` or `"month"` precision year-month-day to stop the sequence #' at. #' #' `to` is cast to the type of `from`. #' #' `to` is only included in the result if the resulting sequence divides #' the distance between `from` and `to` exactly. #' #' @return A sequence with the type of `from`. #' #' @export #' @examples #' # Monthly sequence #' x <- seq(year_month_day(2019, 1), year_month_day(2020, 12), by = 1) #' x #' #' # Which we can then set the day of to get a sequence of end-of-month values #' set_day(x, "last") #' #' # Daily sequences are not allowed. Use a naive-time for this instead. #' try(seq(year_month_day(2019, 1, 1), by = 2, length.out = 2)) #' seq(as_naive_time(year_month_day(2019, 1, 1)), by = 2, length.out = 2) seq.clock_year_month_day <- function(from, to = NULL, by = NULL, length.out = NULL, along.with = NULL, ...) { precision <- calendar_precision_attribute(from) if (precision > PRECISION_MONTH) { abort("`from` must be 'year' or 'month' precision.") } seq_impl( from = from, to = to, by = by, length.out = length.out, along.with = along.with, precision = precision, ... ) } # ------------------------------------------------------------------------------ #' @export clock_minimum.clock_year_month_day <- function(x) { switch( calendar_precision_attribute(x) + 1L, clock_minimum_year_month_day_year, abort("Invalid precision", .internal = TRUE), clock_minimum_year_month_day_month, abort("Invalid precision", .internal = TRUE), clock_minimum_year_month_day_day, clock_minimum_year_month_day_hour, clock_minimum_year_month_day_minute, clock_minimum_year_month_day_second, clock_minimum_year_month_day_millisecond, clock_minimum_year_month_day_microsecond, clock_minimum_year_month_day_nanosecond, abort("Invalid precision", .internal = TRUE) ) } #' @export clock_maximum.clock_year_month_day <- function(x) { switch( calendar_precision_attribute(x) + 1L, clock_maximum_year_month_day_year, abort("Invalid precision", .internal = TRUE), clock_maximum_year_month_day_month, abort("Invalid precision", .internal = TRUE), clock_maximum_year_month_day_day, clock_maximum_year_month_day_hour, clock_maximum_year_month_day_minute, clock_maximum_year_month_day_second, clock_maximum_year_month_day_millisecond, clock_maximum_year_month_day_microsecond, clock_maximum_year_month_day_nanosecond, abort("Invalid precision", .internal = TRUE) ) } year_month_day_minimum <- function(precision) { calendar_minimum(precision, year_month_day(clock_calendar_year_minimum)) } year_month_day_maximum <- function(precision) { calendar_maximum(precision, year_month_day(clock_calendar_year_maximum)) } # ------------------------------------------------------------------------------ clock_init_year_month_day_utils <- function(env) { year <- year_month_day(integer()) assign("clock_empty_year_month_day_year", year, envir = env) assign("clock_empty_year_month_day_month", calendar_widen(year, "month"), envir = env) assign("clock_empty_year_month_day_day", calendar_widen(year, "day"), envir = env) assign("clock_empty_year_month_day_hour", calendar_widen(year, "hour"), envir = env) assign("clock_empty_year_month_day_minute", calendar_widen(year, "minute"), envir = env) assign("clock_empty_year_month_day_second", calendar_widen(year, "second"), envir = env) assign("clock_empty_year_month_day_millisecond", calendar_widen(year, "millisecond"), envir = env) assign("clock_empty_year_month_day_microsecond", calendar_widen(year, "microsecond"), envir = env) assign("clock_empty_year_month_day_nanosecond", calendar_widen(year, "nanosecond"), envir = env) assign("clock_minimum_year_month_day_year", year_month_day_minimum("year"), envir = env) assign("clock_minimum_year_month_day_month", year_month_day_minimum("month"), envir = env) assign("clock_minimum_year_month_day_day", year_month_day_minimum("day"), envir = env) assign("clock_minimum_year_month_day_hour", year_month_day_minimum("hour"), envir = env) assign("clock_minimum_year_month_day_minute", year_month_day_minimum("minute"), envir = env) assign("clock_minimum_year_month_day_second", year_month_day_minimum("second"), envir = env) assign("clock_minimum_year_month_day_millisecond", year_month_day_minimum("millisecond"), envir = env) assign("clock_minimum_year_month_day_microsecond", year_month_day_minimum("microsecond"), envir = env) assign("clock_minimum_year_month_day_nanosecond", year_month_day_minimum("nanosecond"), envir = env) assign("clock_maximum_year_month_day_year", year_month_day_maximum("year"), envir = env) assign("clock_maximum_year_month_day_month", year_month_day_maximum("month"), envir = env) assign("clock_maximum_year_month_day_day", year_month_day_maximum("day"), envir = env) assign("clock_maximum_year_month_day_hour", year_month_day_maximum("hour"), envir = env) assign("clock_maximum_year_month_day_minute", year_month_day_maximum("minute"), envir = env) assign("clock_maximum_year_month_day_second", year_month_day_maximum("second"), envir = env) assign("clock_maximum_year_month_day_millisecond", year_month_day_maximum("millisecond"), envir = env) assign("clock_maximum_year_month_day_microsecond", year_month_day_maximum("microsecond"), envir = env) assign("clock_maximum_year_month_day_nanosecond", year_month_day_maximum("nanosecond"), envir = env) invisible(NULL) } clock/NEWS.md0000644000176200001440000003176314430424554012462 0ustar liggesusers# clock 0.7.0 ## New features * New `year_week_day()` calendar for specifying a date using the year, the week number, and the day of the week, alongside a `start` value representing the day of the week that is considered the start of the week. Using `start = clock_weekdays$monday` is identical to the `iso_year_week_day()` calendar, and using `start = clock_weekdays$sunday` is useful for representing the Epidemiological calendar used by the US CDC guidelines (similar to what is supported by `lubridate::epiweek()` and `lubridate::epiyear()`) (#110). * New `date_spanning_seq()` for generating a regular sequence along the full span of a date or date-time vector (i.e. along `[min(x), max(x)]`). It is similar to `tidyr::full_seq()`, but is a bit simpler and currently has better handling of some edge cases. Additionally included in the low-level API are `calendar_spanning_seq()`, `time_point_spanning_seq()`, and `duration_spanning_seq()` (#279). * New `date_time_info()` and `zoned_time_info()` low-level helpers for accessing the previous/next transition times, the offset from UTC, and the current time zone abbreviation (#295). * `calendar_leap_year()` now supports the year-quarter-day and iso-year-week-day calendars (#332, #333). ## Breaking changes * The storage mechanism for the duration, sys-time, naive-time, and zoned-time types has been altered to more correctly represent the full range of values allowed by the underlying C++ types. This means that if you have serialized a value of one of these types with an old version of clock, then it will no longer unserialize correctly going forward. Technically, rather than storing a variable number of integer vectors representing ticks, ticks of a day, and ticks of a second, we now always store values of these types within two double vectors, regardless of the precision. This simplifies the implementation and allows us to represent the full range of possible `int64_t` values (#331). ## Lifecycle changes * `date_zone()` and `date_set_zone()` have been soft-deprecated in favor of `date_time_zone()` and `date_time_set_zone()` (#326). ## Minor changes and bug fixes * clock now compiles significantly faster (on a 2018 Intel Mac, it used to take ~70 seconds for a full compilation, and now takes ~25 seconds) (#322). * `%%` and `%/%` operators now return a missing value when the right-hand side is `0`. For `%/%`, this is consistent with `2L %/% 0L`, which returns a missing value, rather than with `2 %/% 0`, which returns `Inf`, since infinite durations are not supported (#349). * `seq()` methods for durations and time points handle the empty sequence cases of `from > to && by > 0` and `from < to && by < 0` better when `from` and `to` are very far apart (i.e. when they would otherwise result in overflow if they were subtracted). * `zoned_time_zone()` and `zoned_time_set_zone()` are no longer generic, and now only work for zoned-times. * Documented clock's current stance on leap seconds in the FAQ vignette (clock ignores them like POSIXct) (#309). * Duration vectors now work as `.before` and `.after` arguments of `slider::slide_index()` and friends (#306). * All `as_*()` generics exported by clock now include `...` in their signature to help with extensibility of converting to clock types. These are the only clock generics that are currently "blessed" as fully extensible (#348). * `as.character()` has been implemented for durations. * Fixed `vec_ptype_full()` and `vec_ptype_abbr()` methods for sys-time and naive-time objects (#302). * Many errors have been improved (#219, #286, #595). * Renamed `locale.h` to `fill.h` to avoid clock's `locale.h` being chosen over a system header of the same name on some CentOS machines (#310). * Skipped a test on 32-bit architectures to work around a bug in base R (#312). * R >=3.5.0 is now required, which is in line with tidyverse standards. * vctrs >=0.6.1 and rlang >=1.1.0 are now required. # clock 0.6.1 * `date_seq()` and the `seq()` methods for the calendar, time point, and duration types now allow `from > to` when `by > 0`. This now results in a size zero result rather than an error, which is more in line with `rlang::seq2()` and generally has more useful programmatic properties (#282). * The sys-time method for `as.POSIXct()` now correctly promotes to a precision of at least seconds before attempting the conversion. This matches the behavior of the naive-time method (#278). * Removed the dependency on ellipsis in favor of the equivalent functions in rlang (#288). * Updated tests related to writing UTF-8 on Windows and testthat 3.1.2 (#287). * Updated all snapshot tests to use rlang 1.0.0 (#285). * tzdb >=0.3.0 is now required to get access to the latest time zone database information (2022a). * vctrs >=0.4.1 and rlang >=1.0.4 are now required (#297). * cpp11 >=0.4.2 is now required to ensure that a fix related to unwind protection is included. * R >=3.4.0 is now required. This is consistent with the standards of the tidyverse. # clock 0.6.0 * New `date_count_between()`, `calendar_count_between()`, and `time_point_count_between()` for computing the number of units of time between two dates (i.e. the number of years, months, days, or seconds). This has a number of uses, like computing the age of an individual in years, or determining the number of weeks that have passed since the start of the year (#266). * Modulus is now defined between a duration vector and an integer vector through ` %% `. This returns a duration vector containing the remainder of the division (#273). * Integer division is now defined for two duration objects through ` %/% `. This always returns an integer vector, so be aware that using very precise duration objects (like nanoseconds) can easily generate a division result that is outside the range of an integer. In that case, an `NA` is returned with a warning. # clock 0.5.0 * New `date_time_parse_RFC_3339()` and `sys_time_parse_RFC_3339()` for parsing date-time strings in the [RFC 3339](https://datatracker.ietf.org/doc/html/rfc3339) format. This format is a subset of ISO 8601 representing the most common date-time formats seen in internet protocols, and is particularly useful for parsing date-time strings returned by an API. The default format parses strings like `"2019-01-01T01:02:03Z"` but can be adjusted to parse a numeric offset from UTC with the `offset` argument, which can parse strings like `"2019-01-01T01:02:03-04:30"` (#254). * To align more with RFC 3339 and ISO 8601 standards, the default formats used in many of the date formatting and parsing functions have been slightly altered. The following changes have been made: * Date-times (POSIXct): * `date_format()` now prints a `T` between the date and time. * `date_time_parse_complete()` now expects a `T` between the date and time by default. * Sys-times: * `format()` and `as.character()` now print a `T` between the date and time. * `sys_time_parse()` now expects a `T` between the date and time by default. * Naive-times: * `format()` and `as.character()` now print a `T` between the date and time. * `naive_time_parse()` now expects a `T` between the date and time by default. * Zoned-times: * `format()` and `as.character()` now print a `T` between the date and time. * `zoned_time_parse_complete()` now expects a `T` between the date and time by default. * Calendars: * `format()` and `as.character()` now print a `T` between the date and time. * `year_month_day_parse()` now expects a `T` between the date and time by default. * Further improved documentation of undefined behavior resulting from attempting to parse sub-daily components of a string that is intended to be parsed into a Date (#258). * Bumped required minimum version of tzdb to 0.2.0 to get access to the latest time zone database information (2021e) and to fix a Unicode bug on Windows. # clock 0.4.1 * Updated a test related to upcoming changes in testthat. # clock 0.4.0 * New `date_start()` and `date_end()` for computing the date at the start or end of a particular `precision`, such as the "end of the month" or the "start of the year". These are powered by `calendar_start()` and `calendar_end()`, which allow for even more flexible calendar-specific boundary generation, such as the "last moment in the fiscal quarter" (#232). * New `invalid_remove()` for removing invalid dates. This is just a wrapper around `x[!invalid_detect(x)]`, but works nicely with the pipe (#229). * All clock types now support `is.nan()`, `is.finite()`, and `is.infinite()`. Additionally, duration types now support `abs()` and `sign()` (#235). * tzdb 0.1.2 is now required, which fixes compilation issues on RHEL7/Centos (#234). # clock 0.3.1 * Parsing into a date-time type that is coarser than the original string is now considered ambiguous and undefined behavior. For example, parsing a string with fractional seconds using `date_time_parse(x)` or `naive_time_parse(x, precision = "second")` is no longer considered correct. Instead, if you only require second precision from such a string, parse the full string, with fractional seconds, into a clock type that can handle them, then round to seconds using whatever rounding convention is required for your use case, such as `time_point_floor()` (#230). For example: ``` x <- c("2019-01-01 00:00:59.123", "2019-01-01 00:00:59.556") x <- naive_time_parse(x, precision = "millisecond") x #> [2]> #> [1] "2019-01-01 00:00:59.123" "2019-01-01 00:00:59.556" x <- time_point_round(x, "second") x #> [2]> #> [1] "2019-01-01 00:00:59" "2019-01-01 00:01:00" as_date_time(x, "America/New_York") #> [1] "2019-01-01 00:00:59 EST" "2019-01-01 00:01:00 EST" ``` * Preemptively updated tests related to upcoming changes in testthat (#236). # clock 0.3.0 * New `date_seq()` for generating date and date-time sequences (#218). * clock now uses the tzdb package to access the date library's API. This means that the experimental API that was to be used for vroom has been removed in favor of using the one exposed in tzdb. * `zone_database_names()` and `zone_database_version()` have been removed in favor of re-exporting `tzdb_names()` and `tzdb_version()` from the tzdb package. # clock 0.2.0 * clock now interprets R's Date class as _naive-time_ rather than _sys-time_. This means that it no longer assumes that Date has an implied time zone of UTC (#203). This generally aligns better with how users think Date should work. This resulted in the following changes: * `date_zone()` now errors with Date input, as naive-times do not have a specified time zone. * `date_parse()` now parses into a naive-time, rather than a sys-time, before converting to Date. This means that `%z` and `%Z` are now completely ignored. * The Date method for `date_format()` now uses the naive-time `format()` method rather than the zoned-time one. This means that `%z` and `%Z` are no longer valid format commands. * The zoned-time method for `as.Date()` now converts to Date through an intermediate naive-time, rather than a sys-time. This means that the printed date will always be retained, which is generally what is expected. * The Date method for `as_zoned_time()` now converts to zoned-time through an intermediate naive-time, rather than a sys-time. This means that the printed date will always attempt to be retained, if possible, which is generally what is expected. In the rare case that daylight saving time makes a direct conversion impossible, `nonexistent` and `ambiguous` can be used to resolve any issues. * New `as_date()` and `as_date_time()` for converting to Date and POSIXct respectively. Unlike `as.Date()` and `as.POSIXct()`, these functions always treat Date as a naive-time type, which results in more consistent and intuitive conversions. Note that `as_date()` does conflict with `lubridate::as_date()`, and the lubridate version handles Dates differently (#209). * Added two new convenient helpers (#197): * `date_today()` for getting the current date (Date) * `date_now()` for getting the current date-time (POSIXct) * Fixed a bug where converting from a time point to a Date or POSIXct could round incorrectly (#205). * Errors resulting from invalid dates or nonexistent/ambiguous times are now a little nicer to read through the usage of an info bullet (#200). * Formatting a naive-time with `%Z` or `%z` now warns that there were format failures (#204). * Fixed a Solaris ambiguous behavior issue from calling `pow(int, int)`. * Linking against cpp11 0.2.7 is now required to fix a rare memory leak issue. * Exposed an extremely experimental and limited C++ API for vroom (#322). # clock 0.1.0 * Added a `NEWS.md` file to track changes to the package. clock/MD50000644000176200001440000004672314430501615011670 0ustar liggesusers9956948be775ab0c95affb6d03e2223c *DESCRIPTION 51bdd72c5e8e30746ec7e4a725436d0d *LICENSE 24373b3574dc7f3051692c9ce10a8e3c *NAMESPACE f8d732e45942615dc68574f42f525483 *NEWS.md c6d0ee24ac6b12803aa38092967f6a5e *R/arithmetic.R f7cb1a6257663366e11213b886db6781 *R/calendar.R 5f690bbaa75c3eb2f25f2dedbba62a15 *R/check.R 15b5286e1b0ba6a952f8b82c421f5db5 *R/clock-codes.R ee55c3db417eaeb623b37146d94aa6b1 *R/clock-deprecated.R 6cc03cf5d916fb3d7dc4bbbf0197cb55 *R/clock-labels.R 92f01bde9b174dd194e35f5355df4594 *R/clock-locale.R 164491658c83bc0e8be489edbca6e185 *R/clock-package.R dc65ad5343010e09a92ed151166bd6b5 *R/clock.R 52f23f2b98c2ce0a97e5b670c62cb5fe *R/cpp11.R 367bcef5856eabe4c5a6d401bf1488ba *R/date.R f51a5d06c2431453224d29ffcc18a2d5 *R/duration.R f31d9a30320b4d85a9db66573bb89bbc *R/getters.R 1ca884ae821da2c2b27f483f1c97e1cc *R/gregorian-year-day.R 4eb08fe5c5da9b676846024fe241b0ab *R/gregorian-year-month-day.R 9a48e8e151d47a224da37edea71f7fd7 *R/gregorian-year-month-weekday.R c80a9eb1427c585807cecf618b6f3870 *R/import-standalone-obj-type.R c40f882046a958444c6058a9e2cb9a3b *R/import-standalone-types-check.R 6de9903cbddd52326a8bf7da10a04099 *R/invalid.R b4a50e96082875a8915cdf84ad532a3a *R/iso-year-week-day.R e18837f8b217f381cbcec14746b84fc3 *R/limits.R 51c1f36bbcd6619ae6baf11bee3b25da *R/naive-time.R 2c55f5416499413ea5742155eeb3e313 *R/posixt.R 95f11cf9097d10d8a0b508bb93db0d0c *R/precision.R b3ce49b6d30ba5a367f469ff56bb5222 *R/quarterly-year-quarter-day.R 8c81f218efeb7eb0c1c79e49be649db3 *R/rcrd.R f911a8a5b38ee292b70c8f256828b1a1 *R/setters.R af58f638f1527f93a4dd0a71147270de *R/strict.R 3e05ae36cbdc31da9015449cfb7608c1 *R/sys-time.R 4dbbffcbad6338d769ae949a1c867ac7 *R/sysdata.rda d79ed965a7c3fa81660ecfe7f5199a60 *R/time-point.R 41aeb1fd01e095f48e3f83d916435f7f *R/tzdb.R e3e42fbebc9815031eb835369e7c52a5 *R/utils.R e890b25097a5aa3f77f3b7f60611f116 *R/week-year-week-day.R 587749a7dce62d889b21ba7ed4fc5860 *R/weekday.R 887352649449fa42a88ce032156a8f8c *R/zoned-time.R 2198804cb8a432cbba79b6c9b530201f *R/zzz.R e4f756841f37f647cb32fffc4f18083d *README.md 8abb5a8175b1c1be643c159903929f84 *build/vignette.rds 4777e1b38e5097499672a3d2be3f1d97 *data/clock_iso_weekdays.rda dd15c68c4246e9a9d3fb3e9785b31b29 *data/clock_months.rda 5b417bd0edd29d20e2e03f0d50fde9f4 *data/clock_weekdays.rda 4b25abf81d25b189d2d7e73c8d466968 *inst/doc/clock.R 209e43368080c9120be9c17e31a1e246 *inst/doc/clock.Rmd c90651fbef555df7897bd05ffdf5ab69 *inst/doc/clock.html c7660b1865b5661e60b60638ce36375e *inst/doc/faq.R 7b3e0af301721a6702213c41495ac191 *inst/doc/faq.Rmd a7d5bea20e412e616643b759fea64ff3 *inst/doc/faq.html 5aada0398790e3ddd32cb964424edaed *inst/doc/recipes.R 27af15fb21e13c764c491019e727346b *inst/doc/recipes.Rmd 2b23505a699dfd913bf7a46005fc8800 *inst/doc/recipes.html 6bcdf9f397d2a2d81229ceb56575ab8c *man/Date-arithmetic.Rd 21a40106b5c4945c288985cbfbe50017 *man/Date-getters.Rd 1c1f27c6beae3a510b450031d2855bf1 *man/Date-setters.Rd bfa2c52256205ba3e1ff739ff7e03a50 *man/as-zoned-time-Date.Rd b6b9d57b2d106f5d4453d6e6386904e4 *man/as-zoned-time-naive-time.Rd 8f2d17157a9e0c4e291daca666201aea *man/as-zoned-time-posixt.Rd fb6556235eaf82168048282710378b02 *man/as-zoned-time-sys-time.Rd 84bca2d2dbbcc6eeece76110b3406213 *man/as_date.Rd ec39a08026dc3c5a5eb728df74908b3d *man/as_date_time.Rd 8aaaed84d7c810746d600335288b49a7 *man/as_duration.Rd ba1f14296b71dd41cee23e9cee49fe9b *man/as_iso_year_week_day.Rd 657353e878ea33f762d63794d9844011 *man/as_naive_time.Rd 7552c96b26e3402d078c2a9c3c7568ba *man/as_sys_time.Rd a7c527db7015b255d2e6c8d9ee8faa23 *man/as_weekday.Rd 22bba56391f7538e562d66b872178ea3 *man/as_year_day.Rd 612afb6897ddb35b27ce60884665855d *man/as_year_month_day.Rd a44671acbf2d403324381cf46499d54f *man/as_year_month_weekday.Rd 945f595e0628ba7fe5a4eb95fe6a585d *man/as_year_quarter_day.Rd 5675ae5e6b4c45614b834f572d023d3f *man/as_year_week_day.Rd aea74984bb53a473ae1aa32a09ca4515 *man/as_zoned_time.Rd 0fb7b98d15ff753955f8981124c4500f *man/calendar-boundary.Rd 8b791be4bd422fc8df3a688ee8c6fe09 *man/calendar-count-between.Rd f3323ae7760c21686edf00017df129b5 *man/calendar_group.Rd 0d25d640412bc199f1cd33b8836f792c *man/calendar_leap_year.Rd 692a50f945cc784ff625520463b52330 *man/calendar_month_factor.Rd a281e8b1bd334341500314d993570f72 *man/calendar_narrow.Rd 977ca17b614715674cd10f91b66c3ed9 *man/calendar_precision.Rd 8bab1ba6d9431bc9fafb80a7eb52a9cf *man/calendar_spanning_seq.Rd 659c2da25390046b4f4cf2c58cce7f6e *man/calendar_widen.Rd 473642c4948c731607c0bd64135e0ed8 *man/clock-arith.Rd 895db97b44bf25972727d54e167a6a34 *man/clock-arithmetic.Rd 2bd309451b16b600a10152ff447a585d *man/clock-codes.Rd 244d0ae918189dd477a15a5f7c25b54e *man/clock-getters.Rd 7a488ccb1d43d79428a7922479071e81 *man/clock-invalid.Rd 8690fc9d44e0e6dc327bd0ec5d479301 *man/clock-package.Rd 955a24d214e857851fe31ab029dfa395 *man/clock-setters.Rd ec7d2a185dcb1832cf7caf179cb51572 *man/clock_labels.Rd 5cf4f295f853b07e3c90833fa63874b0 *man/clock_locale.Rd 08376b4cf926120bfef78d1b06b94a3d *man/date-and-date-time-boundary.Rd 1db2efbfe01e1f3c04427747f2912f8a *man/date-and-date-time-rounding.Rd 5e5c87370f5a6d8663ac9d9da0c71a75 *man/date-and-date-time-shifting.Rd 03910dfc6a6e726b25c26988c7653818 *man/date-boundary.Rd 8916b3a9bbb774b80440584636ee69c5 *man/date-count-between.Rd c88daefb68c0eb8b9fc60da0a927c2f0 *man/date-formatting.Rd b23e188322006f76ab89a98f6e77e247 *man/date-group.Rd 699f8cb847216c93b918df7558eb9dd5 *man/date-rounding.Rd 95effd4127af8e0320ffa92f89320182 *man/date-sequence.Rd 9e65a8e0a8785b6193d8ab2aba935571 *man/date-shifting.Rd 961f2e01534810381134f00e13763256 *man/date-time-parse.Rd fbfbc8024d4dace1a367fc375925f2b5 *man/date-time-zone.Rd 0a6e018569868f69ea3a6d4dadf7bda9 *man/date-today.Rd 91432f13c6bda05ea7020738568c4d95 *man/date-zone.Rd adb691dc8ec98452b403067e40769357 *man/date_build.Rd e6d11ee5a3dc7b51732ac10d6c0fd938 *man/date_count_between.Rd 9ff0c659e32f47152282771bbba988cd *man/date_format.Rd 66bff2f54532eebed8e7bf7a5c7e78ed *man/date_group.Rd 9a24549beaaed24fdd4364ee124069c5 *man/date_leap_year.Rd 27fd076fc9758c4c96e4f91bd4413175 *man/date_month_factor.Rd 214a4917256397a2bc82ae2107a06a70 *man/date_parse.Rd 9764da912e43769fd9d5c55efc3791ac *man/date_seq.Rd 02d435f0efe2b9e144c9306effb8cd5a *man/date_spanning_seq.Rd 1dd0727b5b16820938e6c43ffa4aba7d *man/date_time_build.Rd daf8f05a6927288c774c3a8efaffe66c *man/date_time_info.Rd 1ba151c8e444b6eee5ba337b9773fd17 *man/date_weekday_factor.Rd 76ae10891274ce7ea6676978fcd2750d *man/duration-arithmetic.Rd e88ed432394a82a9ff6954b65bf14f68 *man/duration-helper.Rd d0dea1777b7359606e672c9cb7648a6c *man/duration-rounding.Rd 59abe95041a6fe15517e1059f5525aa2 *man/duration_cast.Rd 08d0fa1b331d5812f98e417069958930 *man/duration_precision.Rd 68976510e49b39f0c760461cd2cb287e *man/duration_spanning_seq.Rd a1cbaf3f328e8d74e747faacf640c7fc *man/figures/lifecycle-archived.svg 6f521fb1819410630e279d1abf88685a *man/figures/lifecycle-defunct.svg 391f696f961e28914508628a7af31b74 *man/figures/lifecycle-deprecated.svg 691b1eb2aec9e1bec96b79d11ba5e631 *man/figures/lifecycle-experimental.svg 405e252e54a79b33522e9699e4e9051c *man/figures/lifecycle-maturing.svg f41ed996be135fb35afe00641621da61 *man/figures/lifecycle-questioning.svg 306bef67d1c636f209024cf2403846fd *man/figures/lifecycle-soft-deprecated.svg ed42e3fbd7cc30bc6ca8fa9b658e24a8 *man/figures/lifecycle-stable.svg bf2f1ad432ecccee3400afe533404113 *man/figures/lifecycle-superseded.svg 8f31658e3c8bbae8003a613066fd8fee *man/figures/logo.png f27708f4f3659f8ea76bce1375aec5d1 *man/format.clock_zoned_time.Rd 76e3e68358fca444416ba70d3d7fd3ef *man/is_duration.Rd 9f51fec16fbdbb3510d332e8fcf3f719 *man/is_iso_year_week_day.Rd 7555da5d5a64cd48ee4644ec85d2d685 *man/is_naive_time.Rd 661c6b00d1dcaf2c7a7dfe38d40140c7 *man/is_sys_time.Rd 2dfb8f86e42423c2ca355e4ba331ec26 *man/is_weekday.Rd 0b3641cce28c2e6bc15f60ba02e139ac *man/is_year_day.Rd 30859c5e6e4de4f27268c1e8757befdb *man/is_year_month_day.Rd 1b021f798d9a6e640cb12852fe0e5014 *man/is_year_month_weekday.Rd 08d90255c3b2aea18ab6ccf46eb7d707 *man/is_year_quarter_day.Rd 49732a777dc1cda2c5c5630c994931af *man/is_year_week_day.Rd 77aa8f26c05169a6639f57a1d0addc4e *man/is_zoned_time.Rd 9b180f43f9bb60a88f63fcd323bd417b *man/iso-year-week-day-arithmetic.Rd b15f064a79afc4098fc59de8bdd1bad7 *man/iso-year-week-day-boundary.Rd df2029e93efe50d3a2f770ebd638d849 *man/iso-year-week-day-count-between.Rd bf8f62b21f0047531db8cca317b81c59 *man/iso-year-week-day-getters.Rd 6bb2a3d0f457c566ebc60667824774e0 *man/iso-year-week-day-group.Rd b642ab3b620dad3b30343693331f0ff6 *man/iso-year-week-day-narrow.Rd c72068ef209a326fbdd88165b236c1b6 *man/iso-year-week-day-setters.Rd 009d6965384efe8a79889789e5506bdf *man/iso-year-week-day-widen.Rd 8c0de3f8a719aa5d742e0d20797fac75 *man/iso_year_week_day.Rd b867e826282d95bad598ad16d35c4351 *man/naive_time_info.Rd 2383d81b14fea017dde27b742c8c03af *man/naive_time_parse.Rd 02ec0222e8a12d806de2693e27804f58 *man/posixt-arithmetic.Rd dba2e1e616945a6b59da140b55a9348e *man/posixt-boundary.Rd 067644c37074523c70140c080803db6a *man/posixt-count-between.Rd 9de08f4975d9d505e3b5baff3890e16c *man/posixt-formatting.Rd 88ca292251d368bc339cf79766195bb0 *man/posixt-getters.Rd 6673811e29ee865d9b627b5f262c13bd *man/posixt-group.Rd 4772ea1b8060a0615d78bed9329d7c46 *man/posixt-rounding.Rd 42e1dd910251e9f71afbdace11f9b62d *man/posixt-sequence.Rd b17045f75c11f47bdfd23b4b6b4fe6e0 *man/posixt-setters.Rd ea344c298b573f8038f1e1cbdeed2370 *man/posixt-shifting.Rd ab3b87c590fc222b5a93b8e239a6781a *man/reexports.Rd 340a67bd791bd35f013cb113e0596ca8 *man/seq.clock_duration.Rd f94c6fe91c986013ceff519b2f963ad5 *man/seq.clock_iso_year_week_day.Rd 9a4ca83c3f8c06b82712ac6e7cc1f5e0 *man/seq.clock_time_point.Rd 37a4af1cd2e7526570cdf17895e40a2d *man/seq.clock_year_day.Rd e2142fbc48291a1a0169997e34cf6a47 *man/seq.clock_year_month_day.Rd 1961ae7ab06ab0e000502475cd9bd146 *man/seq.clock_year_month_weekday.Rd 4c2d2ffa94c9673568d6050efb436156 *man/seq.clock_year_quarter_day.Rd 0972c3bbe746b5c8021bd3adcb6c78d2 *man/seq.clock_year_week_day.Rd 100d81b54a816a8aa446177b9be3e38b *man/sys-parsing.Rd 3f399f4804f36eb19f9633426dec4b3f *man/sys_time_info.Rd c9bd0e46b956230fdf3141d4136d5e74 *man/sys_time_now.Rd a4d48aa70e658081b5a07c2a9b24c756 *man/time-point-arithmetic.Rd 14aa0ec25bcf84cf3fd9652b239b63fc *man/time-point-rounding.Rd 823cd532874c6a92e4bf3990afa0b818 *man/time_point_cast.Rd f50a39110027ebdca9d03802eb2d4ba1 *man/time_point_count_between.Rd 4bbad76127b8bddb617d9514d0f09112 *man/time_point_precision.Rd e3522835d18127037b5df612f4f7e057 *man/time_point_shift.Rd 9b6f85674b976ec2df628092d73aed5a *man/time_point_spanning_seq.Rd f0ca4204775f29f073efbe21201c1d25 *man/weekday-arithmetic.Rd 703f3cd429a89451adb0b0f9cbf81d0d *man/weekday.Rd 65a94fa8d4683b81f819daf30347fa78 *man/weekday_code.Rd 2dc51465c0b5aa4664d9b3f274d44a91 *man/weekday_factor.Rd aed1cd69effb7dc35280b29023aa0923 *man/year-day-arithmetic.Rd 1b70bec11175092c4c4e798778c4f408 *man/year-day-boundary.Rd 4ca11d67d4645c4d9efced0a55d3d516 *man/year-day-count-between.Rd 2bd2c83c6b58bf99871e70664e6b595f *man/year-day-getters.Rd 1bae13c80c1aa9d92f4cf6fc065b88e1 *man/year-day-group.Rd 50b5bfaee18e4587757f4ccf27aaa89c *man/year-day-narrow.Rd f36c7dfa11205436f1ab0495fa6f70c9 *man/year-day-setters.Rd cf4342f77c1f33d0e775288807d108bd *man/year-day-widen.Rd 4837c4a55fd4a25b93e8a7634d6629fa *man/year-month-day-arithmetic.Rd d949b86e2649e1ca23b17f388b5f171c *man/year-month-day-boundary.Rd 4498b73bf7fed2acabb9ac3c2d6f49fa *man/year-month-day-count-between.Rd 260873a5aa9061579b9996d9a226d01b *man/year-month-day-getters.Rd 724d24524a6d51662ced97f501661784 *man/year-month-day-group.Rd 3ff97410bc0199e93b85ddb2dffa8bde *man/year-month-day-narrow.Rd 551054d88085f998cd950eb6594c5bc2 *man/year-month-day-setters.Rd ccf8923ddae52142a32063b73ff564d5 *man/year-month-day-widen.Rd a9620b4affd2e1e9e40dd1c1ad7b14c8 *man/year-month-weekday-arithmetic.Rd 53ff8508aeafbd146823b2bef122b639 *man/year-month-weekday-boundary.Rd e3a125172b1e7305eb8ee9f79457a0a8 *man/year-month-weekday-count-between.Rd a50669869f82dc5ce286256c195193e0 *man/year-month-weekday-getters.Rd 71f7db9569277cd2c5ed5b87e7fc920d *man/year-month-weekday-group.Rd 962b0988f30ffe0912b5952bde7d7851 *man/year-month-weekday-narrow.Rd 68002e33c7baf448bcb43b24a29caef6 *man/year-month-weekday-setters.Rd ce3ceeb193052cf71986d54ec8a02199 *man/year-month-weekday-widen.Rd 590f889ead007307c606b4457cf8f0f3 *man/year-quarter-day-arithmetic.Rd 5a4503ae9c9a79e70ad9c5a2735d1eec *man/year-quarter-day-boundary.Rd ed874b0e348fbbfc5c54a04b8bda246c *man/year-quarter-day-count-between.Rd dfc0c53a68ac36361d0fb9cb3c51b202 *man/year-quarter-day-getters.Rd 0b1924fc7c9c34d3fbe891785ba07f48 *man/year-quarter-day-group.Rd d4a50c3a93e21544c3d13ec7c4e6a3d2 *man/year-quarter-day-narrow.Rd 8233f9d97914688ac546787824ab3ee0 *man/year-quarter-day-setters.Rd 3f93aeb42f315c4e79b8bee7d65ec31d *man/year-quarter-day-widen.Rd 4bec74c5802ab6d4c4b778babb26cecd *man/year-week-day-arithmetic.Rd b717009f501f8a2bfe9d13ffed9db201 *man/year-week-day-boundary.Rd 8461f0fdd76d17f116b6f1943dc2eb30 *man/year-week-day-count-between.Rd 79c48a330e7b1a4232a09ca5259c9fd0 *man/year-week-day-getters.Rd 980e50f424d9d5bbf2718d7d6e025221 *man/year-week-day-group.Rd 48bd9dc152090324e37999e7fbc46a1d *man/year-week-day-narrow.Rd dfbbe90fa6a6eb2a085ae7ca1087fe08 *man/year-week-day-setters.Rd 6ad97b20edcb710dd6f032525604157e *man/year-week-day-widen.Rd 994c4a68944f22fde6fdce0432d7bd2d *man/year_day.Rd bdb49288e6b7f664044003f5e8910fa3 *man/year_month_day.Rd 54da260b43fc35ac2599eda686ea709d *man/year_month_day_parse.Rd 64e41dceddb97d5800fb0be1f55dceb6 *man/year_month_weekday.Rd 2db5a2dccf794cebf2ad0b01bf71280f *man/year_quarter_day.Rd 0aea0083e2799ae17f82e477df6da946 *man/year_week_day.Rd d71186ca0bed481afe1698ee13e469be *man/zoned-parsing.Rd bbd82c80b15c6666caaa688d34d2cc62 *man/zoned-zone.Rd 4eb8a61dbd5ec2e3aa52152ba45f9e51 *man/zoned_time_info.Rd 779e60003cd16ddf9b2df6e30606eda7 *man/zoned_time_now.Rd 65e771a978831ce8f6a2eed4b0cafbf2 *man/zoned_time_precision.Rd b937f8796ac5edf3860b83176dd824c1 *src/calendar.h b9719d8862b25d687737ddca8aad6d5d *src/clock.h 800fc66a20836e82751d04b15833e012 *src/cpp11.cpp 179b7a4b72514a97c85e9c0d949d5446 *src/doubles.h 2e296198bd48cef940395341d4af3087 *src/duration.cpp 386dc9aa2b9aba387bd7c39e114cd4a9 *src/duration.h 226a55fe73c29cb619f133748fffe1f2 *src/enums.cpp 6930cc5337fb245cf532ceb081607649 *src/enums.h c31666de903ae9f1fa5e12e40e10e103 *src/failure.h cbfb5161a1a520346da50e5ec05c401d *src/fill.h 1a4594793abe86a0d0aba15687d8b22b *src/format.cpp e965c3637bc784f0f34d7d85f9afc709 *src/get.h f6b363ddeaa69c68e60df368c737d207 *src/gregorian-year-day.cpp 2b56201982eb75f432ad915de307542f *src/gregorian-year-day.h 745ce5df7646b9cac129e6df1d4e0444 *src/gregorian-year-month-day.cpp 5e079512af256298508d2dd06798618d *src/gregorian-year-month-day.h d4314d24a454dc442e150da4c3dd4080 *src/gregorian-year-month-weekday.cpp 13987ade3fde9d29eed50939fba22e22 *src/gregorian-year-month-weekday.h d7350b2a271c8e9436bc49f521b395c6 *src/integers.h e1bc5a0bac75ae4aeff7b69cb64d2031 *src/iso-year-week-day.cpp 541f6874addd7cc2171b46408e9fafad *src/iso-year-week-day.h fa13920c7fb8413b869e05e461c117ee *src/limits.cpp 491e0c5d9c8b859d1d5018602fa5c10d *src/naive-time.cpp d736e361ae59be3fd997b9fe7e78b6a2 *src/ordinal.h 81b71e309b2328c78e5b5aee637f5df3 *src/parse.h e04667c17fb98bf479623d83506cfa9e *src/quarterly-shim.h 21c74e7a6e50b896bda9864f6d395652 *src/quarterly-year-quarter-day.cpp f68f7633c1fdd215f684e50e986b9aa4 *src/quarterly-year-quarter-day.h b65f867b6b102e72471800f9aab4c0b4 *src/quarterly.h e7218ecc6c776136d2651927be92e8f3 *src/rcrd.cpp f82cbd6c2894b979c7fbc000d0639070 *src/rcrd.h ad02430baba37eda36356f7ce0e4022d *src/resolve.h 0dc80aea2dafad8b3f065529167131bd *src/stream.h 39661b57994d8740e158bd9040f40b15 *src/sys-time.cpp 443e0cbf1ea651607998061c7d251853 *src/time-point.cpp 8d168edb3a7f5733e4f759067a67b636 *src/utils.cpp 1c190009c75550c14b72fa5ef9fe1d40 *src/utils.h 1764b24f71b524b7693077c09082cbfa *src/week-shim.h f820bbe340068190eb41d613e094a4f3 *src/week-year-week-day.cpp 56a7707863461d2efc0b0f7b22d06b3e *src/week-year-week-day.h f1fdb5a37074c1bb95edc56c232e98e3 *src/week.h 48525ebe86ead5ea816b74162ac63752 *src/weekday.cpp 12e32af6de1348476371824ac7443533 *src/zone.cpp 74e8f7bb8d1bbc2a5c35bcc6cec3a650 *src/zone.h 3048f3c1a959f7c81b3058f636f3749a *src/zoned-time.cpp 583ce72ab19118ba552c5a7455451d5f *tests/testthat.R 53c4601e83b247bba1eb5f5f223e292b *tests/testthat/_snaps/arithmetic.md 0b886193a13a77c2782e0df69bd2877a *tests/testthat/_snaps/calendar.md 9b8563a9696d2bec4cc120ee2907df25 *tests/testthat/_snaps/clock-deprecated.md 7150909312d523e24da058e3d38c3748 *tests/testthat/_snaps/clock-labels.md bb089797daf5940361ad252626aae296 *tests/testthat/_snaps/clock-locale.md 2b09333156d63c38e7955467f3c55b07 *tests/testthat/_snaps/date.md f8f59609843fc2379928fc5b3880d8ef *tests/testthat/_snaps/duration.md 97a470c3ba8ef972e88efd8fb7233b28 *tests/testthat/_snaps/getters.md 2a9de0f3132568bf19b8ce62b8370af3 *tests/testthat/_snaps/gregorian-year-day.md 8fa2103cf64e448b2833f5b767d7830d *tests/testthat/_snaps/gregorian-year-month-day.md bddcf6608451b3a0ef9eec068e55cdb0 *tests/testthat/_snaps/gregorian-year-month-weekday.md 854ad26a591e7e6a1ec98149b4c23253 *tests/testthat/_snaps/invalid.md 5023fb2918ce3d7fd43f57c0711824cd *tests/testthat/_snaps/iso-year-week-day.md b945d4cd06141255db85f46055dadaae *tests/testthat/_snaps/naive-time.md e0b7081b587f93872e54812503651b76 *tests/testthat/_snaps/posixt.md df4fb33073890637f02b7f67dd2164e3 *tests/testthat/_snaps/quarterly-year-quarter-day.md a8eaa902ca135bb4c5f0b5931767b041 *tests/testthat/_snaps/sys-time.md 7b31543ca5de0727d8c1c3e126785788 *tests/testthat/_snaps/time-point.md de30d07aa874febff48bba639f365cb1 *tests/testthat/_snaps/utils.md dae2a253182ef72f4713c7b876273acd *tests/testthat/_snaps/week-year-week-day.md 8d840154f9543c5e41fa95802dbb19e5 *tests/testthat/_snaps/weekday.md dcb86041fbfdd4f5e28185b300ba221c *tests/testthat/_snaps/zoned-time.md 06aba1d4f03bf9a7bab23a23f0c06aa5 *tests/testthat/helper-format.R db134dcf44858b7a28652a63c6197187 *tests/testthat/helper-os.R 5ef6e3fe86c6f90ffb6faa6320094cff *tests/testthat/test-arithmetic.R 6a0b87938f4a0e24e6d4c0daa543b484 *tests/testthat/test-calendar.R 25c189e7095670d6c750dd5daad1e11c *tests/testthat/test-clock-codes.R 5b15c5f0f0397b2c1694bc9a96ab1457 *tests/testthat/test-clock-deprecated.R 3c996b96ae9d2f097c3c78e8c4471ea3 *tests/testthat/test-clock-labels.R 3bb43a2c78ecda34d1e1d4b697edcdab *tests/testthat/test-clock-locale.R 14f946190c57db5995332db476c926b0 *tests/testthat/test-date.R ded0b3bb490b262de6947a428c7562cc *tests/testthat/test-duration.R 8245f45e0880a7b91500fa57ecf3cace *tests/testthat/test-getters.R 4295d1b58000b85858460328ba788d2d *tests/testthat/test-gregorian-year-day.R 24e0153a31a309b7664575d6c27e1d27 *tests/testthat/test-gregorian-year-month-day.R fc2a99df474d6b4c0d00e8bbcf929f87 *tests/testthat/test-gregorian-year-month-weekday.R 430c9ac17d9dc3d55b7cbfd3e9a7969e *tests/testthat/test-invalid.R 50184def856cd018da7410d277d37dac *tests/testthat/test-iso-year-week-day.R 3350d087acb2c853de528aa2c92192df *tests/testthat/test-naive-time.R fb19cc154ca64be886582c5af447a491 *tests/testthat/test-posixt.R 6671dd2aa27b1c76a2f5cd3e21c520f0 *tests/testthat/test-quarterly-year-quarter-day.R a457d92eb3459ae83be297327644f342 *tests/testthat/test-sys-time.R 56aafb467e10e5f2ff748b9b625839cc *tests/testthat/test-time-point.R 4c3d73593506f05529aced0201473697 *tests/testthat/test-utils.R b9b16ffe4e954d83f98e274f8555ef97 *tests/testthat/test-week-year-week-day.R 84b348b5bd670eca6a34a8f1c2cc7cb5 *tests/testthat/test-weekday.R 23f4746caeaf5716301e0e6c167c1c61 *tests/testthat/test-zoned-time.R 209e43368080c9120be9c17e31a1e246 *vignettes/clock.Rmd 7b3e0af301721a6702213c41495ac191 *vignettes/faq.Rmd 27af15fb21e13c764c491019e727346b *vignettes/recipes.Rmd clock/inst/0000755000176200001440000000000014430471264012327 5ustar liggesusersclock/inst/doc/0000755000176200001440000000000014430471264013074 5ustar liggesusersclock/inst/doc/clock.Rmd0000644000176200001440000004026214134036355014636 0ustar liggesusers--- title: "Getting Started" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Getting Started} %\VignetteEncoding{UTF-8} %\VignetteEngine{knitr::rmarkdown} editor_options: chunk_output_type: console --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` ```{r setup} library(clock) library(magrittr) ``` The goal of this vignette is to introduce you to clock's high-level API, which works directly on R's built-in date-time types, Date and POSIXct. For an overview of all of the functionality in the high-level API, check out the pkgdown reference section, [High Level API](https://clock.r-lib.org/reference/index.html#section-high-level-api). One thing you should immediately notice is that every function specific to R's date and date-time types are prefixed with `date_*()`. There are also additional functions for arithmetic (`add_*()`) and getting (`get_*()`) or setting (`set_*()`) components that are also used by other types in clock. As you'll quickly see in this vignette, one of the main goals of clock is to guard you, the user, from unexpected issues caused by frustrating date manipulation concepts like invalid dates and daylight saving time. It does this by letting you know as soon as one of these issues happens, giving you the power to handle it explicitly with one of a number of different resolution strategies. ## Building To create a vector of dates, you can use `date_build()`. This allows you to specify the components individually. ```{r} date_build(2019, 2, 1:5) ``` If you happen to specify an _invalid date_, you'll get an error message: ```{r, error=TRUE} date_build(2019, 1:12, 31) ``` One way to resolve this is by specifying an invalid date resolution strategy using the `invalid` argument. There are multiple options, but in this case we'll ask for the invalid dates to be set to the previous valid moment in time. ```{r} date_build(2019, 1:12, 31, invalid = "previous") ``` To learn more about invalid dates, check out the documentation for `invalid_resolve()`. If we were actually after the "last day of the month", an easier way to specify this would have been: ```{r} date_build(2019, 1:12, "last") ``` You can also create date-times using `date_time_build()`, which generates a POSIXct. Note that you must supply a time zone! ```{r} date_time_build(2019, 1:5, 1, 2, 30, zone = "America/New_York") ``` If you "build" a time that doesn't exist, you'll get an error. For example, on March 8th, 2020, there was a daylight saving time gap of 1 hour in the America/New_York time zone that took us from `01:59:59` directly to `03:00:00`, skipping the 2 o'clock hour entirely. Let's "accidentally" create a time in that gap: ```{r, error=TRUE} date_time_build(2019:2021, 3, 8, 2, 30, zone = "America/New_York") ``` To resolve this issue, we can specify a nonexistent time resolution strategy through the `nonexistent` argument. There are a number of options, including rolling forward or backward to the next or previous valid moments in time: ```{r} zone <- "America/New_York" date_time_build(2019:2021, 3, 8, 2, 30, zone = zone, nonexistent = "roll-forward") date_time_build(2019:2021, 3, 8, 2, 30, zone = zone, nonexistent = "roll-backward") ``` ## Parsing ### Parsing dates To parse dates, use `date_parse()`. Parsing dates requires a _format string_, a combination of _commands_ that specify where date components are in your string. By default, it assumes that you're working with dates in the form `"%Y-%m-%d"` (year-month-day). ```{r} date_parse("2019-01-05") ``` You can change the format string using `format`: ```{r} date_parse("January 5, 2020", format = "%B %d, %Y") ``` Various different locales are supported for parsing month and weekday names in different languages. To parse a French month: ```{r} date_parse( "juillet 10, 2021", format = "%B %d, %Y", locale = clock_locale("fr") ) ``` You can learn about more locale options in the documentation for `clock_locale()`. If you have heterogeneous dates, you can supply multiple format strings: ```{r} x <- c("2020/1/5", "10-03-05", "2020/2/2") formats <- c("%Y/%m/%d", "%y-%m-%d") date_parse(x, format = formats) ``` ### Parsing date-times You have four options when parsing date-times: - `date_time_parse()`: For strings like `"2020-01-01 01:02:03"` where there is neither a time zone offset nor a full (not abbreviated!) time zone name. - `date_time_parse_complete()`: For strings like `"2020-01-01T01:02:03-05:00[America/New_York]"` where there is both a time zone offset and time zone name present in the string. - `date_time_parse_abbrev()`: For strings like `"2020-01-01 01:02:03 EST"` where there is a time zone abbreviation in the string. - `date_time_parse_RFC_3339()`: For strings like `"2020-01-01T01:02:03Z"` or `"2020-01-01T01:02:03-05:00"`, which are in RFC 3339 format and are intended to be interpreted as UTC. #### date_time_parse() `date_time_parse()` requires a `zone` argument, and will ignore any other zone information in the string (i.e. if you tried to specify `%z` and `%Z`). The default format string is `"%Y-%m-%d %H:%M:%S"`. ```{r} date_time_parse("2020-01-01 01:02:03", "America/New_York") ``` If you happen to parse an invalid or ambiguous date-time, you'll get an error. For example, on November 1st, 2020, there were _two_ 1 o'clock hours in the America/New_York time zone due to a daylight saving time fallback. You can see that if we parse a time right before the fallback, and then shift it forward by 1 second, and then 1 hour and 1 second, respectively: ```{r} before <- date_time_parse("2020-11-01 00:59:59", "America/New_York") # First 1 o'clock before + 1 # Second 1 o'clock before + 1 + 3600 ``` The following string doesn't include any information about which of these two 1 o'clocks it belongs to, so it is considered _ambiguous_. Ambiguous times will error when parsing: ```{r, error=TRUE} date_time_parse("2020-11-01 01:30:00", "America/New_York") ``` To fix that, you can specify an ambiguous time resolution strategy with the `ambiguous` argument. ```{r} zone <- "America/New_York" date_time_parse("2020-11-01 01:30:00", zone, ambiguous = "earliest") date_time_parse("2020-11-01 01:30:00", zone, ambiguous = "latest") ``` #### date_time_parse_complete() `date_time_parse_complete()` doesn't have a `zone` argument, and doesn't require `ambiguous` or `nonexistent` arguments, since it assumes that the string you are providing is completely unambiguous. The only way this is possible is by having both a time zone offset, specified by `%z`, and a full time zone name, specified by `%Z`, in the string. The following is an example of an "extended" RFC 3339 format used by Java 8's time library to specify complete date-time strings. This is something that `date_time_parse_complete()` can parse. The default format string follows this extended format, and is `"%Y-%m-%dT%H:%M:%S%z[%Z]"`. ```{r} x <- "2020-01-01T01:02:03-05:00[America/New_York]" date_time_parse_complete(x) ``` #### date_time_parse_abbrev() `date_time_parse_abbrev()` is useful when your date-time strings contain a time zone abbreviation rather than a time zone offset or full time zone name. ```{r} x <- "2020-01-01 01:02:03 EST" date_time_parse_abbrev(x, "America/New_York") ``` The string is first parsed as a naive time without considering the abbreviation, and is then converted to a zoned-time using the supplied `zone`. If an ambiguous time is parsed, the abbreviation is used to resolve the ambiguity. ```{r} x <- c( "1970-10-25 01:30:00 EDT", "1970-10-25 01:30:00 EST" ) date_time_parse_abbrev(x, "America/New_York") ``` You might be wondering why you need to supply `zone` at all. Isn't the abbreviation enough? Unfortunately, multiple countries use the same time zone abbreviations, even though they have different time zones. This means that, in many cases, the abbreviation alone is ambiguous. For example, both India and Israel use `IST` for their standard times. ```{r} x <- "1970-01-01 02:30:30 IST" # IST = India Standard Time date_time_parse_abbrev(x, "Asia/Kolkata") # IST = Israel Standard Time date_time_parse_abbrev(x, "Asia/Jerusalem") ``` #### date_time_parse_RFC_3339() `date_time_parse_RFC_3339()` is useful when your date-time strings come from an API, which means they are likely in an ISO 8601 or RFC 3339 format, and should be interpreted as UTC. The default format string parses the typical RFC 3339 format of `"%Y-%m-%dT%H:%M:%SZ"`. ```{r} x <- "2020-01-01T01:02:03Z" date_time_parse_RFC_3339(x) ``` If your date-time strings contain a numeric offset from UTC rather than a `"Z"`, then you'll need to set the `offset` argument to one of the following: - `"%z"` if the offset is of the form `"-0500"`. - `"%Ez"` if the offset is of the form `"-05:00"`. ```{r} x <- "2020-01-01T01:02:03-0500" date_time_parse_RFC_3339(x, offset = "%z") x <- "2020-01-01T01:02:03-05:00" date_time_parse_RFC_3339(x, offset = "%Ez") ``` ## Grouping, rounding and shifting When performing time-series related data analysis, you often need to summarize your series at a less precise precision. There are many different ways to do this, and the differences between them are subtle, but meaningful. clock offers three different sets of functions for summarization: - `date_group()` - `date_floor()`, `date_ceiling()`, and `date_round()` - `date_shift()` ### Grouping Grouping allows you to summarize a component of a date or date-time _within_ other components. An example of this is grouping by day of the month, which summarizes the day component _within_ the current year-month. ```{r} x <- seq(date_build(2019, 1, 20), date_build(2019, 2, 5), by = 1) x # Grouping by 5 days of the current month date_group(x, "day", n = 5) ``` The thing to note about grouping by day of the month is that at the end of each month, the groups restart. So this created groups for January of `[1, 5], [6, 10], [11, 15], [16, 20], [21, 25], [26, 30], [31]`. You can also group by month or year: ```{r} date_group(x, "month") ``` This also works with date-times, adding the ability to group by hour of the day, minute of the hour, and second of the minute. ```{r} x <- seq( date_time_build(2019, 1, 1, 1, 55, zone = "UTC"), date_time_build(2019, 1, 1, 2, 15, zone = "UTC"), by = 120 ) x date_group(x, "minute", n = 5) ``` ### Rounding While grouping is useful for summarizing _within_ a component, rounding is useful for summarizing _across_ components. It is great for summarizing by, say, a rolling set of 60 days. Rounding operates on the underlying count that makes up your date or date-time. To see what I mean by this, try unclassing a date: ```{r} unclass(date_build(2020, 1, 1)) ``` This is a count of days since the _origin_ that R uses, 1970-01-01, which is considered day 0. If you were to floor by 60 days, this would bundle `[1970-01-01, 1970-03-02), [1970-03-02, 1970-05-01)`, and so on. Equivalently, it bundles counts of `[0, 60), [60, 120)`, etc. ```{r} x <- seq(date_build(1970, 01, 01), date_build(1970, 05, 10), by = 20) date_floor(x, "day", n = 60) date_ceiling(x, "day", n = 60) ``` If you prefer a different origin, you can supply a Date `origin` to `date_floor()`, which determines what "day 0" is considered to be. This can be useful for grouping by multiple weeks if you want to control what is considered the start of the week. Since 1970-01-01 is a Thursday, flooring by 2 weeks would normally generate all Thursdays: ```{r} as_weekday(date_floor(x, "week", n = 14)) ``` To change this you can supply an `origin` on the weekday that you'd like to be considered the first day of the week. ```{r} sunday <- date_build(1970, 01, 04) date_floor(x, "week", n = 14, origin = sunday) as_weekday(date_floor(x, "week", n = 14, origin = sunday)) ``` If you only need to floor by 1 week, it is often easier to use `date_shift()`, as seen in the next section. ### Shifting `date_shift()` allows you to target a weekday, and then shift a vector of dates forward or backward to the next instance of that target. It requires using one of the new types in clock, _weekday_, which is supplied as the target. For example, to shift to the next Tuesday: ```{r} x <- date_build(2020, 1, 1:2) # Wednesday / Thursday as_weekday(x) # `clock_weekdays` is a helper that returns the code corresponding to # the requested day of the week clock_weekdays$tuesday tuesday <- weekday(clock_weekdays$tuesday) tuesday date_shift(x, target = tuesday) ``` Shifting to the _previous_ day of the week is a nice way to floor by 1 week. It allows you to control the start of the week in a way that is slightly easier than using `date_floor(origin = )`. ```{r} x <- seq(date_build(1970, 01, 01), date_build(1970, 01, "last"), by = 3) date_shift(x, tuesday, which = "previous") ``` ## Arithmetic You can do arithmetic with dates and date-times using the family of `add_*()` functions. With dates, you can add years, months, and days. With date-times, you can additionally add hours, minutes, and seconds. ```{r} x <- date_build(2020, 1, 1) add_years(x, 1:5) ``` One of the neat parts about clock is that it requires you to be explicit about how you want to handle invalid dates when doing arithmetic. What is 1 month after January 31st? If you try and create this date, you'll get an error. ```{r, error=TRUE} x <- date_build(2020, 1, 31) add_months(x, 1) ``` clock gives you the power to handle this through the `invalid` option: ```{r} # The previous valid moment in time add_months(x, 1, invalid = "previous") # The next valid moment in time add_months(x, 1, invalid = "next") # Overflow the days. There were 29 days in February, 2020, but we # specified 31. So this overflows 2 days past day 29. add_months(x, 1, invalid = "overflow") # If you don't consider it to be a valid date add_months(x, 1, invalid = "NA") ``` As a teaser, the low level library has a _calendar_ type named year-month-day that powers this operation. It actually gives you _more_ flexibility, allowing `"2020-02-31"` to exist in the wild: ```{r} ymd <- as_year_month_day(x) + duration_months(1) ymd ``` You can use `invalid_resolve(invalid =)` to resolve this like you did in `add_months()`, or you can let it hang around if you expect other operations to make it "valid" again. ```{r} # Adding 1 more month makes it valid again ymd + duration_months(1) ``` When working with date-times, you can additionally add hours, minutes, and seconds. ```{r} x <- date_time_build(2020, 1, 1, 2, 30, zone = "America/New_York") x %>% add_days(1) %>% add_hours(2:5) ``` When adding units of time to a POSIXct, you have to be very careful with daylight saving time issues. clock tries to help you out by letting you know when you run into an issue: ```{r, error=TRUE} x <- date_time_build(1970, 04, 25, 02, 30, 00, zone = "America/New_York") x # Daylight saving time gap on the 26th between 01:59:59 -> 03:00:00 x %>% add_days(1) ``` You can solve this using the `nonexistent` argument to control how these times should be handled. ```{r} # Roll forward to the next valid moment in time x %>% add_days(1, nonexistent = "roll-forward") # Roll backward to the previous valid moment in time x %>% add_days(1, nonexistent = "roll-backward") # Shift forward by adding the size of the DST gap # (this often keeps the time of day, # but doesn't guaratee that relative ordering in `x` is maintained # so I don't recommend it) x %>% add_days(1, nonexistent = "shift-forward") # Replace nonexistent times with an NA x %>% add_days(1, nonexistent = "NA") ``` ## Getting and setting clock provides a family of getters and setters for working with dates and date-times. You can get and set the year, month, or day of a date. ```{r} x <- date_build(2019, 5, 6) get_year(x) get_month(x) get_day(x) x %>% set_day(22) %>% set_month(10) ``` As you might expect by now, setting the date to an invalid date requires you to explicitly handle this: ```{r, error=TRUE} x %>% set_day(31) %>% set_month(4) x %>% set_day(31) %>% set_month(4, invalid = "previous") ``` You can additionally set the hour, minute, and second of a POSIXct. ```{r} x <- date_time_build(2020, 1, 2, 3, zone = "America/New_York") x x %>% set_minute(5) %>% set_second(10) ``` As with other manipulations of POSIXct, you'll have to be aware of daylight saving time when setting components. You may need to supply the `nonexistent` or `ambiguous` arguments of the `set_*()` functions to handle these issues. clock/inst/doc/faq.Rmd0000644000176200001440000003454214423751216014317 0ustar liggesusers--- title: "Frequently Asked Questions" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Frequently Asked Questions} %\VignetteEncoding{UTF-8} %\VignetteEngine{knitr::rmarkdown} editor_options: chunk_output_type: console --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` ```{r setup} library(clock) library(magrittr) ``` ## Why can't I do day arithmetic on a year-month-day? It might seem intuitive that since you can do: ```{r} x <- year_month_day(2019, 1, 5) add_months(x, 1) ``` That you should also be able to do: ```{r, error=TRUE} add_days(x, 1) ``` Generally, calendars don't support day based arithmetic, nor do they support arithmetic at more precise precisions than day. Instead, you have to convert to a time point, do the arithmetic there, and then convert back (if you still need a year-month-day after that). ```{r} x %>% as_naive_time() %>% add_days(1) %>% as_year_month_day() ``` The first reason for this is performance. A year-month-day is a *field* type, implemented as multiple parallel vectors holding the year, month, day, and all other components separately. There are two ways that day based arithmetic could be implemented for this: - Increment the day field, then check the year and month field to see if they need to be incremented, accounting for months having a differing number of days, and leap years. - Convert to naive-time, add days, convert back. Both approaches are relatively expensive. One of the goals of the low-level API of clock is to make these expensive operations explicit. This helps make it apparent that when you need to chain together multiple operations, you should try and do all of your *calendrical* arithmetic steps first, then convert to a time point (i.e. the second bullet point from above) to do all of your *chronological* arithmetic. The second reason for this has to do with invalid dates, such as the three in this vector: ```{r} odd_dates <- year_month_day(2019, 2, 28:31) odd_dates ``` What does it mean to "add 1 day" to these? There is no obvious answer to this question. Since clock requires that you first convert to a time point to do day based arithmetic, you'll be forced to call `invalid_resolve()` to handle these invalid dates first. After resolving them manually, then day based arithmetic again makes sense. ```{r} odd_dates %>% invalid_resolve(invalid = "next") odd_dates %>% invalid_resolve(invalid = "next") %>% as_naive_time() %>% add_days(2) odd_dates %>% invalid_resolve(invalid = "overflow") odd_dates %>% invalid_resolve(invalid = "overflow") %>% as_naive_time() %>% add_days(2) ``` ## Why can't I add time to a zoned-time? If you have a zoned-time, such as: ```{r} x <- zoned_time_parse_complete("1970-04-26T01:30:00-05:00[America/New_York]") x ``` You might wonder why you can't add any units of time to it: ```{r, error=TRUE} add_days(x, 1) add_seconds(x, 1) ``` In clock, you can't do much with zoned-times directly. The best way to understand this is to think of a zoned-time as containing 3 things: a sys-time, a naive-time, and a time zone name. You can access those things with: ```{r} x # The printed time with no time zone info as_naive_time(x) # The equivalent time in UTC as_sys_time(x) zoned_time_zone(x) ``` Calling `add_days()` on a zoned-time is then an ambiguous operation. Should we add to the sys-time or the naive-time that is contained in the zoned-time? The answer changes depending on the scenario. Because of this, you have to extract out the relevant time point that you care about, operate on that, and then convert back to zoned-time. This often produces the same result: ```{r} x %>% as_naive_time() %>% add_seconds(1) %>% as_zoned_time(zoned_time_zone(x)) x %>% as_sys_time() %>% add_seconds(1) %>% as_zoned_time(zoned_time_zone(x)) ``` But not always! When daylight saving time is involved, the choice of sys-time or naive-time matters. Let's try adding 30 minutes: ```{r, error=TRUE} # There is a DST gap 1 second after 01:59:59, # which jumps us straight to 03:00:00, # skipping the 2 o'clock hour entirely x %>% as_naive_time() %>% add_minutes(30) %>% as_zoned_time(zoned_time_zone(x)) x %>% as_sys_time() %>% add_minutes(30) %>% as_zoned_time(zoned_time_zone(x)) ``` When adding to the naive-time, we got an error. With the sys-time, everything seems okay. What happened? The sys-time scenario is easy to explain. Technically this converts to UTC, adds the time there, then converts back to your time zone. An easier way to think about this is that you sat in front of your computer for exactly 30 minutes (1800 seconds), then looked at the clock. Assuming that that clock automatically changes itself correctly for daylight saving time, it should read 3 o'clock. The naive-time scenario makes more sense if you break down the steps. First, we convert to naive-time, dropping all time zone information but keeping the printed time: ```{r} x x %>% as_naive_time() ``` We add 30 minutes to this. Because we don't have any time zone information, this lands us at 2 o'clock, which isn't an issue when working with naive-time: ```{r} x %>% as_naive_time() %>% add_minutes(30) ``` Finally, we convert back to zoned-time. If possible, this tries to keep the printed time, and just attaches the relevant time zone onto it. However, in this case that isn't possible, since 2 o'clock didn't exist in this time zone! This *nonexistent time* must be handled explicitly by setting the `nonexistent` argument of `as_zoned_time()`. We can choose from a variety of strategies to handle nonexistent times, but here we just roll forward to the next valid moment in time. ```{r} x %>% as_naive_time() %>% add_minutes(30) %>% as_zoned_time(zoned_time_zone(x), nonexistent = "roll-forward") ``` As a general rule, it often makes the most sense to add: - Years, quarters, and months to a *calendar*. - Weeks and days to a *naive time*. - Hours, minutes, seconds, and subseconds to a *sys time*. This is what the high-level API for POSIXct does. However, this isn't always what you want, so the low-level API requires you to be more explicit. ## Where did my POSIXct subseconds go? ```{r} old <- options(digits.secs = 6, digits = 22) ``` Consider the following POSIXct: ```{r} x <- as.POSIXct("2019-01-01 01:00:00.2", "America/New_York") x ``` It looks like there is some fractional second information here, but converting it to naive-time drops it: ```{r} as_naive_time(x) ``` This is purposeful. clock treats POSIXct as a *second precision* data type. The reason for this has to do with the fact that POSIXct is implemented as a vector of doubles, which have a limit to how precisely they can store information. For example, try parsing a slightly smaller or larger fractional second: ```{r} y <- as.POSIXct( c("2019-01-01 01:00:00.1", "2019-01-01 01:00:00.3"), "America/New_York" ) # Oh dear! y ``` It isn't printing correctly, at the very least. Let's look under the hood: ```{r} unclass(y) ``` Double vectors have a limit to how much precision they can represent, and this is bumping up against that limit. So our `.1` seconds is instead represented as `.099999etc`. This precision loss gets worse the farther we get from the epoch, 1970-01-01, represented as `0` under the hood. For example, here we'll use a number of seconds that represents the year 2050, and add 5 microseconds to it: ```{r} new_utc <- function(x) { class(x) <- c("POSIXct", "POSIXt") attr(x, "tzone") <- "UTC" x } year_2050 <- 2524608000 five_microseconds <- 0.000005 new_utc(year_2050) # Oh no! new_utc(year_2050 + five_microseconds) # Represented internally as: year_2050 + five_microseconds ``` Because of these issues, clock treats POSIXct as a second precision data type, dropping all other information. Instead, you should parse directly into a subsecond clock type: ```{r} naive_time_parse( c("2019-01-01T01:00:00.1", "2019-01-01T01:00:00.3"), precision = "millisecond" ) %>% as_zoned_time("America/New_York") ``` ```{r} # Reset old options options(old) ``` ## What is the time zone of Date? In clock, R's native Date type is actually assumed to be *naive*, i.e. clock assumes that there is a yet-to-be-specified time zone, like with a naive-time. The other possibility is to assume that Date is UTC (like sys-time), but it is often more intuitive for Dates to be naive when manipulating them and converting them to zoned-time or POSIXct. R does not consistently treat Dates as naive or UTC. Instead it switches between them, depending on the function. For example, the Date method of `as.POSIXct()` does not expose a `tz` argument. Instead, it assumes that Date is UTC, and that the result should be shown in local time (as defined by `Sys.timezone()`). This often results in confusing behavior, such as: ```{r} x <- as.Date("2019-01-01") x withr::with_timezone("America/New_York", { print(as.POSIXct(x)) }) ``` With clock, converting to zoned-time from Date will always assume that Date is naive, which will keep the printed date (if possible) and show it in the `zone` you specified. ```{r} as_zoned_time(x, "UTC") as_zoned_time(x, "America/New_York") as_zoned_time(x, "Europe/London") ``` On the other hand, the POSIXct method for `as.Date()` treats Date as a naive type. This is probably what you want, and this example just shows the inconsistency. It is a bit hard to see this, because the `tz` argument of the method defaults to `"UTC"`, but if you set the `tz` argument to the zone of your input, it becomes clear: ```{r} x <- as.POSIXct("2019-01-01 23:00:00", "America/New_York") as.Date(x, tz = date_time_zone(x)) ``` If this assumed that Date was UTC, then it would have resulted in something like: ```{r} utc <- date_time_set_zone(x, "UTC") utc as.Date(utc, tz = date_time_zone(utc)) ``` ## What does clock do with leap seconds? clock currently handles leap seconds in the same way that base R's date-time (POSIXct) class does - it ignores them entirely. While `strptime()` has some very simple capabilities for parsing leap seconds, clock doesn't allow them at all: ```{r, warning=TRUE} raw <- c( "2015-12-31T23:59:59", "2015-12-31T23:59:60", # A real leap second! "2016-01-01T00:00:00" ) x <- sys_time_parse(raw) x ``` ```{r} # Reported as exactly 1 second apart. # In real life these are 2 seconds apart because of the leap second. x[[3]] - x[[1]] ``` Because none of the clock types handle leap seconds, clock currently doesn't offer a way to parse them. Your current best option if you *really* need to parse leap seconds is to use `strptime()`: ```{r} # This returns a POSIXlt, which can handle the special 60s field x <- strptime(raw, format = "%Y-%m-%dT%H:%M:%S", tz = "UTC") x # On conversion to POSIXct, it "rolls" forward as.POSIXct(x) ``` `strptime()` isn't a great solution though, because the parsing is fairly simple. If you try to use a "fake" leap second, it will still accept it, even though it isn't a real time: ```{r} # 2016-12-31 wasn't a leap second date, but it still tries to parse this fake time strptime("2016-12-31T23:59:60", format = "%Y-%m-%dT%H:%M:%S", tz = "UTC") ``` A true solution would check this against a database of actual leap seconds, and would only successfully parse it if it matched a real leap second. The C++ library that powers clock does have this capability, through a `utc_clock` class, and we may expose this in a limited form in the future, with conversion to and from sys-time and naive-time. ## Why doesn't this work with data.table? While the entire high-level API for R's native date (Date) and date-time (POSIXct) types will work fine with data.table, if you try to put any of the major clock types into a data.table, you will probably see this error message: ```{r, eval=FALSE} library(data.table) data.table(x = year_month_day(2019, 1, 1)) #> Error in dimnames(x) <- dn : #> length of 'dimnames' [1] not equal to array extent ``` You won't see this issue when working with data.frames or tibbles. As of now, data.table doesn't support the concept of *record types*. These are implemented as a list of vectors of equal length, that together represent a single idea. The `length()` of these types should be taken from the length of the vectors, not the length of the list. If you unclass any of the clock types, you'll see that they are implemented in this way: ```{r} ymdh <- year_month_day(2019, 1, 1:2, 1) unclass(ymdh) unclass(as_naive_time(ymdh)) ``` I find that record types are extremely useful data structures for building upon R's basic atomic types in ways that otherwise couldn't be done. They allow calendar types to hold information about each component, enabling instant access for retrieval, modification, and grouping. They also allow calendars to represent invalid dates, such as `2019-02-31`, without any issues. Time points use them to store up to nanosecond precision date-times, which are really C++ `int64_t` types that don't nicely fit into any R atomic type (I am aware of the bit64 package, and made a conscious decision to implement as a record type instead. This partly had to do with how missing values are handled, and how that integrates with vctrs). The idea of a record type actually isn't new. R's own POSIXlt type is a record type: ```{r} x <- as.POSIXct("2019-01-01", "America/New_York") # POSIXct is implemented as a double unclass(x) # POSIXlt is a record type unclass(as.POSIXlt(x)) ``` data.table doesn't truly support POSIXlt either. Instead, you get a warning about them converting it to a POSIXct. This is pretty reasonable considering their focus on performance. ```{r, eval=FALSE} data.table(x = as.POSIXlt("2019-01-01", "America/New_York")) #> x #> 1: 2019-01-01 #> Warning message: #> In as.data.table.list(x, keep.rownames = keep.rownames, check.names = check.names, : #> POSIXlt column type detected and converted to POSIXct. We do not recommend use of POSIXlt at all because it uses 40 bytes to store one date. ``` It was previously a bit difficult to create record types in R because there were few examples and no resources to build on. In vctrs, we've added a `vctrs_rcrd` type that serves as a base to build new record types on. Many S3 methods have been written for `vctrs_rcrd`s in a way that should work for any type that builds on top of it, giving you a lot of scaffolding for free. I am hopeful that as more record types make their way into the R ecosystem built on this common foundation, it might be possible for data.table to enable this as an approved type in their package. clock/inst/doc/recipes.Rmd0000644000176200001440000006251314423751216015201 0ustar liggesusers--- title: "Examples and Recipes" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Examples and Recipes} %\VignetteEncoding{UTF-8} %\VignetteEngine{knitr::rmarkdown} editor_options: chunk_output_type: console --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` ```{r setup} library(clock) library(magrittr) ``` This vignette shows common examples and recipes that might be useful when learning about clock. Where possible, both the high and low level API are shown. Many of these examples are adapted from the date C++ library's [Examples and Recipes](https://github.com/HowardHinnant/date/wiki/Examples-and-Recipes) page. ## The current local time `zoned_time_now()` returns the current time in a particular time zone. It will display up to nanosecond precision, but the exact amount is OS dependent (on a Mac this displays microsecond level information at nanosecond resolution). Using `""` as the time zone string will try and use whatever R thinks your local time zone is (i.e. from `Sys.timezone()`). ```{r, eval=FALSE} zoned_time_now("") #> [1]> #> [1] "2021-02-10T15:54:29.875011000-05:00" ``` ## The current time somewhere else Pass a time zone name to `zoned_time_now()` to get the current time somewhere else. ```{r, eval=FALSE} zoned_time_now("Asia/Shanghai") #> [1]> #> [1] "2021-02-11T04:54:29.875011000+08:00" ``` ## Set a meeting across time zones Say you need to set a meeting with someone in Shanghai, but you live in New York. If you set a meeting for 9am, what time is that for them? ```{r} my_time <- year_month_day(2019, 1, 30, 9) %>% as_naive_time() %>% as_zoned_time("America/New_York") my_time their_time <- zoned_time_set_zone(my_time, "Asia/Shanghai") their_time ``` ### High level API ```{r} my_time <- as.POSIXct("2019-01-30 09:00:00", "America/New_York") date_time_set_zone(my_time, "Asia/Shanghai") ``` ## Force a specific time zone Say your co-worker in Shanghai (from the last example) accidentally logged on at 9am _their time_. What time would this be for you? The first step to solve this is to force `my_time` to have the same printed time, but use the Asia/Shanghai time zone. You can do this by going through naive-time: ```{r} my_time <- year_month_day(2019, 1, 30, 9) %>% as_naive_time() %>% as_zoned_time("America/New_York") my_time # Drop the time zone information, retaining the printed time my_time %>% as_naive_time() # Add the correct time zone name back on, # again retaining the printed time their_9am <- my_time %>% as_naive_time() %>% as_zoned_time("Asia/Shanghai") their_9am ``` Note that a conversion like this isn't always possible due to daylight saving time issues, in which case you might need to set the `nonexistent` and `ambiguous` arguments of `as_zoned_time()`. What time would this have been for you in New York? ```{r} zoned_time_set_zone(their_9am, "America/New_York") ``` ### High level API ```{r} my_time <- as.POSIXct("2019-01-30 09:00:00", "America/New_York") my_time %>% as_naive_time() %>% as.POSIXct("Asia/Shanghai") %>% date_time_set_zone("America/New_York") ``` ## Finding the next Monday (or Thursday) Given a particular day precision naive-time, how can you compute the next Monday? This is very easily accomplished with `time_point_shift()`. It takes a time point vector and a "target" weekday, and shifts the time points to that target weekday. ```{r} days <- as_naive_time(year_month_day(2019, c(1, 2), 1)) # A Tuesday and a Friday as_weekday(days) monday <- weekday(clock_weekdays$monday) time_point_shift(days, monday) as_weekday(time_point_shift(days, monday)) ``` You can also shift to the previous instance of the target weekday: ```{r} time_point_shift(days, monday, which = "previous") ``` If you happen to already be on the target weekday, the default behavior returns the input unchanged. However, you can also chose to advance to the next instance of the target. ```{r} tuesday <- weekday(clock_weekdays$tuesday) time_point_shift(days, tuesday) time_point_shift(days, tuesday, boundary = "advance") ``` While `time_point_shift()` is built in to clock, it can be useful to discuss the arithmetic going on in the underlying weekday type which powers this function. To do so, we will build some parts of `time_point_shift()` from scratch. The weekday type represents a single day of the week and implements _circular arithmetic_. Let's see the code for a simple version of `time_point_shift()` that just shifts to the next target weekday: ```{r} next_weekday <- function(x, target) { x + (target - as_weekday(x)) } next_weekday(days, monday) as_weekday(next_weekday(days, monday)) ``` Let's break down how `next_weekday()` works. The first step takes the difference between two weekday vectors. It does this using circular arithmetic. Once we get passed the 7th day of the week (whatever that may be), it wraps back around to the 1st day of the week. Implementing weekday arithmetic in this way means that the following nicely returns the number of days until the next Monday as a day based duration: ```{r} monday - as_weekday(days) ``` Which can be added to our day precision `days` vector to get the date of the next Monday: ```{r} days + (monday - as_weekday(days)) ``` The current implementation will return the input if it is already on the target weekday. To use the `boundary = "advance"` behavior, you could implement `next_weekday()` as: ```{r} next_weekday2 <- function(x, target) { x <- x + duration_days(1L) x + (target - as_weekday(x)) } a_monday <- as_naive_time(year_month_day(2018, 12, 31)) as_weekday(a_monday) next_weekday2(a_monday, monday) ``` ### High level API In the high level API, you can use `date_shift()`: ```{r} monday <- weekday(clock_weekdays$monday) x <- as.Date(c("2019-01-01", "2019-02-01")) date_shift(x, monday) # With a date-time y <- as.POSIXct( c("2019-01-01 02:30:30", "2019-02-01 05:20:22"), "America/New_York" ) date_shift(y, monday) ``` Note that adding weekdays to a POSIXct could generate nonexistent or ambiguous times due to daylight saving time, which would have to be handled by supplying `nonexistent` and `ambiguous` arguments to `date_shift()`. ## Generate sequences of dates and date-times clock implements S3 methods for the `seq()` generic function for the calendar and time point types it provides. The precision that you can generate sequences for depends on the type. - year-month-day: Yearly or monthly sequences - year-quarter-day: Yearly or quarterly sequences - sys-time / naive-time: Weekly, Daily, Hourly, ..., Subsecond sequences When generating sequences, the type and precision of `from` determine the result. For example: ```{r} ym <- seq(year_month_day(2019, 1), by = 2, length.out = 10) ym ``` ```{r} yq <- seq(year_quarter_day(2019, 1), by = 2, length.out = 10) ``` This allows you to generate sequences of year-months or year-quarters without having to worry about the day of the month/quarter becoming invalid. You can set the day of the results to get to a day precision calendar. For example, to get the last days of the month/quarter for this sequence: ```{r} set_day(ym, "last") set_day(yq, "last") ``` You won't be able to generate day precision sequences with calendars. Instead, you should use a time point. ```{r} from <- as_naive_time(year_month_day(2019, 1, 1)) to <- as_naive_time(year_month_day(2019, 5, 15)) seq(from, to, by = 20) ``` If you use an integer `by` value, it is interpreted as a duration at the same precision as `from`. You can also use a duration object that can be cast to the same precision as `from`. For example, to generate a sequence spaced out by 90 minutes for these second precision end points: ```{r} from <- as_naive_time(year_month_day(2019, 1, 1, 2, 30, 00)) to <- as_naive_time(year_month_day(2019, 1, 1, 12, 30, 00)) seq(from, to, by = duration_minutes(90)) ``` ### High level API In the high level API, you can use `date_seq()` to generate sequences. This doesn't have all of the flexibility of the `seq()` methods above, but is still extremely useful and has the added benefit of switching between calendars, sys-times, and naive-times automatically for you. If an integer `by` is supplied with a date `from`, it defaults to a daily sequence: ```{r} date_seq(date_build(2019, 1), by = 2, total_size = 10) ``` You can generate a monthly sequence by supplying a month precision duration for `by`. ```{r} date_seq(date_build(2019, 1), by = duration_months(2), total_size = 10) ``` If you supply `to`, be aware that all components of `to` that are more precise than the precision of `by` must match `from` exactly. For example, the day component of `from` and `to` doesn't match here, so the sequence isn't defined. ```{r, error=TRUE} date_seq( date_build(2019, 1, 1), to = date_build(2019, 10, 2), by = duration_months(2) ) ``` `date_seq()` also catches invalid dates for you, forcing you to specify the `invalid` argument to specify how to handle them. ```{r, error=TRUE} jan31 <- date_build(2019, 1, 31) dec31 <- date_build(2019, 12, 31) date_seq(jan31, to = dec31, by = duration_months(1)) ``` By specifying `invalid = "previous"` here, we can generate month end values. ```{r} date_seq(jan31, to = dec31, by = duration_months(1), invalid = "previous") ``` Compare this with the automatic "overflow" behavior of `seq()`, which is often a source of confusion. ```{r} seq(jan31, to = dec31, by = "1 month") ``` ## Grouping by months or quarters When working on a data analysis, you might be required to summarize certain metrics at a monthly or quarterly level. With `calendar_group()`, you can easily summarize at the granular precision that you care about. Take this vector of day precision naive-times in 2019: ```{r} from <- as_naive_time(year_month_day(2019, 1, 1)) to <- as_naive_time(year_month_day(2019, 12, 31)) x <- seq(from, to, by = duration_days(20)) x ``` To group by month, first convert to a year-month-day: ```{r} ymd <- as_year_month_day(x) head(ymd) calendar_group(ymd, "month") ``` To group by quarter, convert to a year-quarter-day: ```{r} yqd <- as_year_quarter_day(x) head(yqd) calendar_group(yqd, "quarter") ``` If you need to group by a multiple of months / quarters, you can do that too: ```{r} calendar_group(ymd, "month", n = 2) calendar_group(yqd, "quarter", n = 2) ``` Note that the returned calendar vector is at the precision we grouped by, not at the original precision with, say, the day of the month / quarter set to `1`. Additionally, be aware that `calendar_group()` groups "within" the component that is one unit of precision larger than the `precision` you specify. So, when grouping by `"day"`, this groups by "day of the month", which can't cross the month or year boundary. If you need to bundle dates together by something like 60 days (i.e. crossing the month boundary), then you should use `time_point_floor()`. ### High level API In the high level API, you can use `date_group()` to group Date vectors by one of their 3 components: year, month, or day. Since month precision dates can't be represented with Date vectors, `date_group()` sets the day of the month to 1. ```{r} x <- seq(as.Date("2019-01-01"), as.Date("2019-12-31"), by = 20) date_group(x, "month") ``` You won't be able to group by `"quarter"`, since this isn't one of the 3 components that the high level API lets you work with. Instead, this is a case where you should convert to a year-quarter-day, group on that type, then convert back to Date. ```{r} x %>% as_year_quarter_day() %>% calendar_group("quarter") %>% set_day(1) %>% as.Date() ``` This is actually equivalent to `date_group(x, "month", n = 3)`. If your fiscal year starts in January, you can use that instead. However, if your fiscal year starts in a different month, say, June, you'll need to use the approach from above like so: ```{r} x %>% as_year_quarter_day(start = clock_months$june) %>% calendar_group("quarter") %>% set_day(1) %>% as.Date() ``` ## Flooring by days While `calendar_group()` can group by "component", it isn't useful for bundling together sets of time points that can cross month/year boundaries, like "60 days" of data. For that, you are better off _flooring_ by rolling sets of 60 days. ```{r} from <- as_naive_time(year_month_day(2019, 1, 1)) to <- as_naive_time(year_month_day(2019, 12, 31)) x <- seq(from, to, by = duration_days(20)) ``` ```{r} time_point_floor(x, "day", n = 60) ``` Flooring operates on the underlying duration, which for day precision time points is a count of days since the _origin_, 1970-01-01. ```{r} unclass(x[1]) ``` The 60 day counter starts here, which means that any times between `[1970-01-01, 1970-03-02)` are all floored to 1970-01-01. At `1970-03-02`, the counter starts again. If you would like to change this origin, you can provide a time point to start counting from with the `origin` argument. This is mostly useful if you are flooring by weeks and you want to change the day of the week that the count starts on. Since 1970-01-01 is a Thursday, flooring by 14 days defaults to returning all Thursdays. ```{r} x <- seq(as_naive_time(year_month_day(2019, 1, 1)), by = 3, length.out = 10) x thursdays <- time_point_floor(x, "day", n = 14) thursdays as_weekday(thursdays) ``` You can use `origin` to change this to floor to Mondays. ```{r} origin <- as_naive_time(year_month_day(2018, 12, 31)) as_weekday(origin) mondays <- time_point_floor(x, "day", n = 14, origin = origin) mondays as_weekday(mondays) ``` ### High level API You can use `date_floor()` with Date and POSIXct types. ```{r} x <- seq(as.Date("2019-01-01"), as.Date("2019-12-31"), by = 20) date_floor(x, "day", n = 60) ``` The `origin` you provide should be another Date. For week precision flooring with Dates, you can specify `"week"` as the precision. ```{r} x <- seq(as.Date("2019-01-01"), by = 3, length.out = 10) origin <- as.Date("2018-12-31") date_floor(x, "week", n = 2, origin = origin) ``` ## Day of the year To get the day of the year, convert to the year-day calendar type and extract the day with `get_day()`. ```{r} x <- year_month_day(2019, clock_months$july, 4) yd <- as_year_day(x) yd get_day(yd) ``` ### High level API ```{r} x <- as.Date("2019-07-04") x %>% as_year_day() %>% get_day() ``` ## Computing an age in years To get the age of an individual in years, use `calendar_count_between()`. ```{r} x <- year_month_day(1980, 12, 14:16) today <- year_month_day(2005, 12, 15) # Note that the month and day of the month are taken into account! # (Time of day would also be taken into account if there was any.) calendar_count_between(x, today, "year") ``` ### High level API You can use `date_count_between()` with Date and POSIXct types. ```{r} x <- date_build(1980, 12, 14:16) today <- date_build(2005, 12, 15) date_count_between(x, today, "year") ``` ## Computing number of weeks since the start of the year `lubridate::week()` is a useful function that returns "the number of complete seven day periods that have occurred between the date and January 1st, plus one." There is no direct equivalent to this, but it is possible to replicate with `calendar_start()` and `time_point_count_between()`. ```{r} x <- year_month_day(2019, 11, 28) # lubridate::week(as.Date(x)) # [1] 48 x_start <- calendar_start(x, "year") x_start time_point_count_between( as_naive_time(x_start), as_naive_time(x), "week" ) + 1L ``` You could also peek at the `lubridate::week()` implementation to see that this is just: ```{r} doy <- get_day(as_year_day(x)) doy (doy - 1L) %/% 7L + 1L ``` ### High level API This is actually a little easier in the high level API because you don't have to think about switching between types. ```{r} x <- date_build(2019, 11, 28) date_count_between(date_start(x, "year"), x, "week") + 1L ``` ## Compute the number of months between two dates How can we compute the number of months between these two dates? ```{r} x <- year_month_day(2013, 10, 15) y <- year_month_day(2016, 10, 13) ``` This is a bit of an ambiguous question because "month" isn't very well-defined, and there are various different interpretations we could take. We might want to ignore the day component entirely, and just compute the number of months between `2013-10` and `2016-10`. ```{r} calendar_narrow(y, "month") - calendar_narrow(x, "month") ``` Or we could include the day of the month, and say that `2013-10-15` to `2014-10-15` defines 1 month (i.e. you have to hit the same day of the month in the next month). ```{r} calendar_count_between(x, y, "month") ``` With this you could also compute the number of days remaining between these two dates. ```{r} x_close <- add_months(x, calendar_count_between(x, y, "month")) x_close x_close_st <- as_sys_time(x_close) y_st <- as_sys_time(y) time_point_count_between(x_close_st, y_st, "day") ``` Or we could compute the number of days between these two dates in units of seconds, and divide that by the average number of seconds in 1 proleptic Gregorian month. ```{r} # Days between x and y days <- as_sys_time(y) - as_sys_time(x) days # In units of seconds days <- duration_cast(days, "second") days <- as.numeric(days) days # Average number of seconds in 1 proleptic Gregorian month avg_sec_in_month <- duration_cast(duration_months(1), "second") avg_sec_in_month <- as.numeric(avg_sec_in_month) days / avg_sec_in_month ``` ### High level API ```{r} x <- date_build(2013, 10, 15) y <- date_build(2016, 10, 13) ``` To ignore the day of the month, first shift to the start of the month, then you can use `date_count_between()`. ```{r} date_count_between(date_start(x, "month"), date_start(y, "month"), "month") ``` To utilize the day field, do the same as above but without calling `date_start()`. ```{r} date_count_between(x, y, "month") ``` There is no high level equivalent to the average length of one proleptic Gregorian month example. ## Computing the ISO year or week The ISO 8601 standard outlines an alternative calendar that is specified by the year, the week of the year, and the day of the week. It also specifies that the _start_ of the week is considered to be a Monday. This ends up meaning that the actual ISO year may be different from the Gregorian year, and is somewhat difficult to compute "by hand". Instead, you can use the `year_week_day()` calendar if you need to work with ISO week dates. ```{r} x <- date_build(2019:2026) y <- as_year_week_day(x, start = clock_weekdays$monday) data.frame(x = x, y = y) ``` ```{r} get_year(y) get_week(y) # Last week in the ISO year set_week(y, "last") ``` The year-week-day calendar is a fully supported calendar, meaning that all of the `calendar_*()` functions work on it: ```{r} calendar_narrow(y, "week") ``` There is also an `iso_year_week_day()` calendar available, which is identical to `year_week_day(start = clock_weekdays$monday)`. That ISO calendar actually existed first, before we generalized it to any `start` weekday. ## Computing the Epidemiological year or week Epidemiologists following the US CDC guidelines use a calendar that is similar to the ISO calendar, but defines the start of the week to be Sunday instead of Monday. `year_week_day()` supports this as well: ```{r} x <- date_build(2019:2026) iso <- as_year_week_day(x, start = clock_weekdays$monday) epi <- as_year_week_day(x, start = clock_weekdays$sunday) data.frame(x = x, iso = iso, epi = epi) ``` ```{r} get_year(epi) get_week(epi) ``` ## Converting a time zone abbreviation into a time zone name It is possible that you might run into date-time strings of the form `"2020-10-25 01:30:00 IST"`, which contain a time zone _abbreviation_ rather than a full time zone name. Because time zone maintainers change the abbreviation they use throughout time, and because multiple time zones sometimes use the same abbreviation, it is generally impossible to parse strings of this form without more information. That said, if you know what time zone this abbreviation goes with, you can parse this time with `zoned_time_parse_abbrev()`, supplying the `zone`. ```{r} x <- "2020-10-25 01:30:00 IST" zoned_time_parse_abbrev(x, "Asia/Kolkata") zoned_time_parse_abbrev(x, "Asia/Jerusalem") ``` If you _don't_ know what time zone this abbreviation goes with, then generally you are out of luck. However, there are low-level tools in this library that can help you generate a list of _possible_ zoned-times this could map to. Assuming that `x` is a naive-time with its corresponding time zone abbreviation attached, the first thing to do is to parse this string as a naive-time. ```{r} x <- naive_time_parse(x, format = "%Y-%m-%d %H:%M:%S IST") x ``` Next, we'll develop a function that attempts to turn this naive-time into a zoned-time, iterating through all of the time zone names available in the time zone database. These time zone names are accessible through `tzdb_names()`. By using the low-level `naive_time_info()`, rather than `as_zoned_time()`, to lookup zone specific information, we'll also get back information about the UTC offset and time zone abbreviation that is currently in use. By matching this abbreviation against our input abbreviation, we can generate a list of zoned-times that use the abbreviation we care about at that particular instance in time. ```{r} naive_find_by_abbrev <- function(x, abbrev) { if (!is_naive_time(x)) { abort("`x` must be a naive-time.") } if (length(x) != 1L) { abort("`x` must be length 1.") } if (!rlang::is_string(abbrev)) { abort("`abbrev` must be a single string.") } zones <- tzdb_names() info <- naive_time_info(x, zones) info$zones <- zones c( compute_uniques(x, info, abbrev), compute_ambiguous(x, info, abbrev) ) } compute_uniques <- function(x, info, abbrev) { info <- info[info$type == "unique",] # If the abbreviation of the unique time matches the input `abbrev`, # then that candidate zone should be in the output matches <- info$first$abbreviation == abbrev zones <- info$zones[matches] lapply(zones, as_zoned_time, x = x) } compute_ambiguous <- function(x, info, abbrev) { info <- info[info$type == "ambiguous",] # Of the two possible times, # does the abbreviation of the earliest match the input `abbrev`? matches <- info$first$abbreviation == abbrev zones <- info$zones[matches] earliest <- lapply(zones, as_zoned_time, x = x, ambiguous = "earliest") # Of the two possible times, # does the abbreviation of the latest match the input `abbrev`? matches <- info$second$abbreviation == abbrev zones <- info$zones[matches] latest <- lapply(zones, as_zoned_time, x = x, ambiguous = "latest") c(earliest, latest) } ``` ```{r} candidates <- naive_find_by_abbrev(x, "IST") candidates ``` While it looks like we got 7 candidates, in reality we only have 3. Asia/Kolkata, Europe/Dublin, and Asia/Jerusalem are our 3 candidates. The others are aliases of those 3 that have been retired but are kept for backwards compatibility. Looking at the code, there are two ways to add a candidate time zone name to the list. If there is a unique mapping from `{naive-time, zone}` to `sys-time`, then we check if the abbreviation that goes with that unique mapping matches our input abbreviation. If so, then we convert `x` to a zoned-time with that time zone. If there is an ambiguous mapping from `{naive-time, zone}` to `sys-time`, which is due to a daylight saving fallback, then we check the abbreviation of both the _earliest_ and _latest_ possible times. If either matches, then we convert `x` to a zoned-time using that time zone and the information about which of the two ambiguous times were used. This example is particularly interesting, since each of the 3 candidates came from a different path. The Asia/Kolkata one is unique, the Europe/Dublin one is ambiguous but the earliest was chosen, and the Asia/Jerusalem one is ambiguous but the latest was chosen: ```{r} as_zoned_time(x, "Asia/Kolkata") as_zoned_time(x, "Europe/Dublin", ambiguous = "earliest") as_zoned_time(x, "Asia/Jerusalem", ambiguous = "latest") ``` ## When is the next daylight saving time event? Given a particular zoned-time, when will it next be affected by daylight saving time? For this, we can use a relatively low level helper, `zoned_time_info()`. It returns a data frame of information about the current daylight saving time transition points, along with information about the offset, the current time zone abbreviation, and whether or not daylight saving time is currently active or not. ```{r} x <- zoned_time_parse_complete("2019-01-01T00:00:00-05:00[America/New_York]") info <- zoned_time_info(x) # Beginning of the current DST range info$begin # Beginning of the next DST range info$end ``` So on 2018-11-04 at (the second) 1 o'clock hour, daylight saving time was turned off. On 2019-03-10 at 3 o'clock, daylight saving time will be considered on again. This is the next moment in time right after a daylight saving time gap of 1 hour, which you can see by subtracting 1 second (in sys-time): ```{r} # Last moment in time in the current DST range info$end %>% as_sys_time() %>% add_seconds(-1) %>% as_zoned_time(zoned_time_zone(x)) ``` ### High level API `date_time_info()` exists in the high level API to do a similar thing. It is basically the same as `zoned_time_info()`, except the `begin` and `end` columns are returned as R POSIXct date-times rather than zoned-times, and the `offset` column is returned as an integer rather than as a clock duration (since we try not to expose high level API users to low level types). ```{r} x <- date_time_parse("2019-01-01 00:00:00", zone = "America/New_York") date_time_info(x) ``` clock/inst/doc/recipes.R0000644000176200001440000003463714430471263014665 0ustar liggesusers## ---- include = FALSE--------------------------------------------------------- knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ## ----setup-------------------------------------------------------------------- library(clock) library(magrittr) ## ---- eval=FALSE-------------------------------------------------------------- # zoned_time_now("") # #> [1]> # #> [1] "2021-02-10T15:54:29.875011000-05:00" ## ---- eval=FALSE-------------------------------------------------------------- # zoned_time_now("Asia/Shanghai") # #> [1]> # #> [1] "2021-02-11T04:54:29.875011000+08:00" ## ----------------------------------------------------------------------------- my_time <- year_month_day(2019, 1, 30, 9) %>% as_naive_time() %>% as_zoned_time("America/New_York") my_time their_time <- zoned_time_set_zone(my_time, "Asia/Shanghai") their_time ## ----------------------------------------------------------------------------- my_time <- as.POSIXct("2019-01-30 09:00:00", "America/New_York") date_time_set_zone(my_time, "Asia/Shanghai") ## ----------------------------------------------------------------------------- my_time <- year_month_day(2019, 1, 30, 9) %>% as_naive_time() %>% as_zoned_time("America/New_York") my_time # Drop the time zone information, retaining the printed time my_time %>% as_naive_time() # Add the correct time zone name back on, # again retaining the printed time their_9am <- my_time %>% as_naive_time() %>% as_zoned_time("Asia/Shanghai") their_9am ## ----------------------------------------------------------------------------- zoned_time_set_zone(their_9am, "America/New_York") ## ----------------------------------------------------------------------------- my_time <- as.POSIXct("2019-01-30 09:00:00", "America/New_York") my_time %>% as_naive_time() %>% as.POSIXct("Asia/Shanghai") %>% date_time_set_zone("America/New_York") ## ----------------------------------------------------------------------------- days <- as_naive_time(year_month_day(2019, c(1, 2), 1)) # A Tuesday and a Friday as_weekday(days) monday <- weekday(clock_weekdays$monday) time_point_shift(days, monday) as_weekday(time_point_shift(days, monday)) ## ----------------------------------------------------------------------------- time_point_shift(days, monday, which = "previous") ## ----------------------------------------------------------------------------- tuesday <- weekday(clock_weekdays$tuesday) time_point_shift(days, tuesday) time_point_shift(days, tuesday, boundary = "advance") ## ----------------------------------------------------------------------------- next_weekday <- function(x, target) { x + (target - as_weekday(x)) } next_weekday(days, monday) as_weekday(next_weekday(days, monday)) ## ----------------------------------------------------------------------------- monday - as_weekday(days) ## ----------------------------------------------------------------------------- days + (monday - as_weekday(days)) ## ----------------------------------------------------------------------------- next_weekday2 <- function(x, target) { x <- x + duration_days(1L) x + (target - as_weekday(x)) } a_monday <- as_naive_time(year_month_day(2018, 12, 31)) as_weekday(a_monday) next_weekday2(a_monday, monday) ## ----------------------------------------------------------------------------- monday <- weekday(clock_weekdays$monday) x <- as.Date(c("2019-01-01", "2019-02-01")) date_shift(x, monday) # With a date-time y <- as.POSIXct( c("2019-01-01 02:30:30", "2019-02-01 05:20:22"), "America/New_York" ) date_shift(y, monday) ## ----------------------------------------------------------------------------- ym <- seq(year_month_day(2019, 1), by = 2, length.out = 10) ym ## ----------------------------------------------------------------------------- yq <- seq(year_quarter_day(2019, 1), by = 2, length.out = 10) ## ----------------------------------------------------------------------------- set_day(ym, "last") set_day(yq, "last") ## ----------------------------------------------------------------------------- from <- as_naive_time(year_month_day(2019, 1, 1)) to <- as_naive_time(year_month_day(2019, 5, 15)) seq(from, to, by = 20) ## ----------------------------------------------------------------------------- from <- as_naive_time(year_month_day(2019, 1, 1, 2, 30, 00)) to <- as_naive_time(year_month_day(2019, 1, 1, 12, 30, 00)) seq(from, to, by = duration_minutes(90)) ## ----------------------------------------------------------------------------- date_seq(date_build(2019, 1), by = 2, total_size = 10) ## ----------------------------------------------------------------------------- date_seq(date_build(2019, 1), by = duration_months(2), total_size = 10) ## ---- error=TRUE-------------------------------------------------------------- date_seq( date_build(2019, 1, 1), to = date_build(2019, 10, 2), by = duration_months(2) ) ## ---- error=TRUE-------------------------------------------------------------- jan31 <- date_build(2019, 1, 31) dec31 <- date_build(2019, 12, 31) date_seq(jan31, to = dec31, by = duration_months(1)) ## ----------------------------------------------------------------------------- date_seq(jan31, to = dec31, by = duration_months(1), invalid = "previous") ## ----------------------------------------------------------------------------- seq(jan31, to = dec31, by = "1 month") ## ----------------------------------------------------------------------------- from <- as_naive_time(year_month_day(2019, 1, 1)) to <- as_naive_time(year_month_day(2019, 12, 31)) x <- seq(from, to, by = duration_days(20)) x ## ----------------------------------------------------------------------------- ymd <- as_year_month_day(x) head(ymd) calendar_group(ymd, "month") ## ----------------------------------------------------------------------------- yqd <- as_year_quarter_day(x) head(yqd) calendar_group(yqd, "quarter") ## ----------------------------------------------------------------------------- calendar_group(ymd, "month", n = 2) calendar_group(yqd, "quarter", n = 2) ## ----------------------------------------------------------------------------- x <- seq(as.Date("2019-01-01"), as.Date("2019-12-31"), by = 20) date_group(x, "month") ## ----------------------------------------------------------------------------- x %>% as_year_quarter_day() %>% calendar_group("quarter") %>% set_day(1) %>% as.Date() ## ----------------------------------------------------------------------------- x %>% as_year_quarter_day(start = clock_months$june) %>% calendar_group("quarter") %>% set_day(1) %>% as.Date() ## ----------------------------------------------------------------------------- from <- as_naive_time(year_month_day(2019, 1, 1)) to <- as_naive_time(year_month_day(2019, 12, 31)) x <- seq(from, to, by = duration_days(20)) ## ----------------------------------------------------------------------------- time_point_floor(x, "day", n = 60) ## ----------------------------------------------------------------------------- unclass(x[1]) ## ----------------------------------------------------------------------------- x <- seq(as_naive_time(year_month_day(2019, 1, 1)), by = 3, length.out = 10) x thursdays <- time_point_floor(x, "day", n = 14) thursdays as_weekday(thursdays) ## ----------------------------------------------------------------------------- origin <- as_naive_time(year_month_day(2018, 12, 31)) as_weekday(origin) mondays <- time_point_floor(x, "day", n = 14, origin = origin) mondays as_weekday(mondays) ## ----------------------------------------------------------------------------- x <- seq(as.Date("2019-01-01"), as.Date("2019-12-31"), by = 20) date_floor(x, "day", n = 60) ## ----------------------------------------------------------------------------- x <- seq(as.Date("2019-01-01"), by = 3, length.out = 10) origin <- as.Date("2018-12-31") date_floor(x, "week", n = 2, origin = origin) ## ----------------------------------------------------------------------------- x <- year_month_day(2019, clock_months$july, 4) yd <- as_year_day(x) yd get_day(yd) ## ----------------------------------------------------------------------------- x <- as.Date("2019-07-04") x %>% as_year_day() %>% get_day() ## ----------------------------------------------------------------------------- x <- year_month_day(1980, 12, 14:16) today <- year_month_day(2005, 12, 15) # Note that the month and day of the month are taken into account! # (Time of day would also be taken into account if there was any.) calendar_count_between(x, today, "year") ## ----------------------------------------------------------------------------- x <- date_build(1980, 12, 14:16) today <- date_build(2005, 12, 15) date_count_between(x, today, "year") ## ----------------------------------------------------------------------------- x <- year_month_day(2019, 11, 28) # lubridate::week(as.Date(x)) # [1] 48 x_start <- calendar_start(x, "year") x_start time_point_count_between( as_naive_time(x_start), as_naive_time(x), "week" ) + 1L ## ----------------------------------------------------------------------------- doy <- get_day(as_year_day(x)) doy (doy - 1L) %/% 7L + 1L ## ----------------------------------------------------------------------------- x <- date_build(2019, 11, 28) date_count_between(date_start(x, "year"), x, "week") + 1L ## ----------------------------------------------------------------------------- x <- year_month_day(2013, 10, 15) y <- year_month_day(2016, 10, 13) ## ----------------------------------------------------------------------------- calendar_narrow(y, "month") - calendar_narrow(x, "month") ## ----------------------------------------------------------------------------- calendar_count_between(x, y, "month") ## ----------------------------------------------------------------------------- x_close <- add_months(x, calendar_count_between(x, y, "month")) x_close x_close_st <- as_sys_time(x_close) y_st <- as_sys_time(y) time_point_count_between(x_close_st, y_st, "day") ## ----------------------------------------------------------------------------- # Days between x and y days <- as_sys_time(y) - as_sys_time(x) days # In units of seconds days <- duration_cast(days, "second") days <- as.numeric(days) days # Average number of seconds in 1 proleptic Gregorian month avg_sec_in_month <- duration_cast(duration_months(1), "second") avg_sec_in_month <- as.numeric(avg_sec_in_month) days / avg_sec_in_month ## ----------------------------------------------------------------------------- x <- date_build(2013, 10, 15) y <- date_build(2016, 10, 13) ## ----------------------------------------------------------------------------- date_count_between(date_start(x, "month"), date_start(y, "month"), "month") ## ----------------------------------------------------------------------------- date_count_between(x, y, "month") ## ----------------------------------------------------------------------------- x <- date_build(2019:2026) y <- as_year_week_day(x, start = clock_weekdays$monday) data.frame(x = x, y = y) ## ----------------------------------------------------------------------------- get_year(y) get_week(y) # Last week in the ISO year set_week(y, "last") ## ----------------------------------------------------------------------------- calendar_narrow(y, "week") ## ----------------------------------------------------------------------------- x <- date_build(2019:2026) iso <- as_year_week_day(x, start = clock_weekdays$monday) epi <- as_year_week_day(x, start = clock_weekdays$sunday) data.frame(x = x, iso = iso, epi = epi) ## ----------------------------------------------------------------------------- get_year(epi) get_week(epi) ## ----------------------------------------------------------------------------- x <- "2020-10-25 01:30:00 IST" zoned_time_parse_abbrev(x, "Asia/Kolkata") zoned_time_parse_abbrev(x, "Asia/Jerusalem") ## ----------------------------------------------------------------------------- x <- naive_time_parse(x, format = "%Y-%m-%d %H:%M:%S IST") x ## ----------------------------------------------------------------------------- naive_find_by_abbrev <- function(x, abbrev) { if (!is_naive_time(x)) { abort("`x` must be a naive-time.") } if (length(x) != 1L) { abort("`x` must be length 1.") } if (!rlang::is_string(abbrev)) { abort("`abbrev` must be a single string.") } zones <- tzdb_names() info <- naive_time_info(x, zones) info$zones <- zones c( compute_uniques(x, info, abbrev), compute_ambiguous(x, info, abbrev) ) } compute_uniques <- function(x, info, abbrev) { info <- info[info$type == "unique",] # If the abbreviation of the unique time matches the input `abbrev`, # then that candidate zone should be in the output matches <- info$first$abbreviation == abbrev zones <- info$zones[matches] lapply(zones, as_zoned_time, x = x) } compute_ambiguous <- function(x, info, abbrev) { info <- info[info$type == "ambiguous",] # Of the two possible times, # does the abbreviation of the earliest match the input `abbrev`? matches <- info$first$abbreviation == abbrev zones <- info$zones[matches] earliest <- lapply(zones, as_zoned_time, x = x, ambiguous = "earliest") # Of the two possible times, # does the abbreviation of the latest match the input `abbrev`? matches <- info$second$abbreviation == abbrev zones <- info$zones[matches] latest <- lapply(zones, as_zoned_time, x = x, ambiguous = "latest") c(earliest, latest) } ## ----------------------------------------------------------------------------- candidates <- naive_find_by_abbrev(x, "IST") candidates ## ----------------------------------------------------------------------------- as_zoned_time(x, "Asia/Kolkata") as_zoned_time(x, "Europe/Dublin", ambiguous = "earliest") as_zoned_time(x, "Asia/Jerusalem", ambiguous = "latest") ## ----------------------------------------------------------------------------- x <- zoned_time_parse_complete("2019-01-01T00:00:00-05:00[America/New_York]") info <- zoned_time_info(x) # Beginning of the current DST range info$begin # Beginning of the next DST range info$end ## ----------------------------------------------------------------------------- # Last moment in time in the current DST range info$end %>% as_sys_time() %>% add_seconds(-1) %>% as_zoned_time(zoned_time_zone(x)) ## ----------------------------------------------------------------------------- x <- date_time_parse("2019-01-01 00:00:00", zone = "America/New_York") date_time_info(x) clock/inst/doc/recipes.html0000644000176200001440000035166414430471264015433 0ustar liggesusers Examples and Recipes

Examples and Recipes

library(clock)
library(magrittr)

This vignette shows common examples and recipes that might be useful when learning about clock. Where possible, both the high and low level API are shown.

Many of these examples are adapted from the date C++ library’s Examples and Recipes page.

The current local time

zoned_time_now() returns the current time in a particular time zone. It will display up to nanosecond precision, but the exact amount is OS dependent (on a Mac this displays microsecond level information at nanosecond resolution).

Using "" as the time zone string will try and use whatever R thinks your local time zone is (i.e. from Sys.timezone()).

zoned_time_now("")
#> <zoned_time<nanosecond><America/New_York (current)>[1]>
#> [1] "2021-02-10T15:54:29.875011000-05:00"

The current time somewhere else

Pass a time zone name to zoned_time_now() to get the current time somewhere else.

zoned_time_now("Asia/Shanghai")
#> <zoned_time<nanosecond><Asia/Shanghai>[1]>
#> [1] "2021-02-11T04:54:29.875011000+08:00"

Set a meeting across time zones

Say you need to set a meeting with someone in Shanghai, but you live in New York. If you set a meeting for 9am, what time is that for them?

my_time <- year_month_day(2019, 1, 30, 9) %>%
  as_naive_time() %>%
  as_zoned_time("America/New_York")

my_time
#> <zoned_time<second><America/New_York>[1]>
#> [1] "2019-01-30T09:00:00-05:00"

their_time <- zoned_time_set_zone(my_time, "Asia/Shanghai")

their_time
#> <zoned_time<second><Asia/Shanghai>[1]>
#> [1] "2019-01-30T22:00:00+08:00"

High level API

my_time <- as.POSIXct("2019-01-30 09:00:00", "America/New_York")

date_time_set_zone(my_time, "Asia/Shanghai")
#> [1] "2019-01-30 22:00:00 CST"

Force a specific time zone

Say your co-worker in Shanghai (from the last example) accidentally logged on at 9am their time. What time would this be for you?

The first step to solve this is to force my_time to have the same printed time, but use the Asia/Shanghai time zone. You can do this by going through naive-time:

my_time <- year_month_day(2019, 1, 30, 9) %>%
  as_naive_time() %>%
  as_zoned_time("America/New_York")

my_time
#> <zoned_time<second><America/New_York>[1]>
#> [1] "2019-01-30T09:00:00-05:00"

# Drop the time zone information, retaining the printed time
my_time %>%
  as_naive_time()
#> <naive_time<second>[1]>
#> [1] "2019-01-30T09:00:00"

# Add the correct time zone name back on,
# again retaining the printed time
their_9am <- my_time %>%
  as_naive_time() %>%
  as_zoned_time("Asia/Shanghai")

their_9am
#> <zoned_time<second><Asia/Shanghai>[1]>
#> [1] "2019-01-30T09:00:00+08:00"

Note that a conversion like this isn’t always possible due to daylight saving time issues, in which case you might need to set the nonexistent and ambiguous arguments of as_zoned_time().

What time would this have been for you in New York?

zoned_time_set_zone(their_9am, "America/New_York")
#> <zoned_time<second><America/New_York>[1]>
#> [1] "2019-01-29T20:00:00-05:00"

High level API

my_time <- as.POSIXct("2019-01-30 09:00:00", "America/New_York")

my_time %>%
  as_naive_time() %>%
  as.POSIXct("Asia/Shanghai") %>%
  date_time_set_zone("America/New_York")
#> [1] "2019-01-29 20:00:00 EST"

Finding the next Monday (or Thursday)

Given a particular day precision naive-time, how can you compute the next Monday? This is very easily accomplished with time_point_shift(). It takes a time point vector and a “target” weekday, and shifts the time points to that target weekday.

days <- as_naive_time(year_month_day(2019, c(1, 2), 1))

# A Tuesday and a Friday
as_weekday(days)
#> <weekday[2]>
#> [1] Tue Fri

monday <- weekday(clock_weekdays$monday)

time_point_shift(days, monday)
#> <naive_time<day>[2]>
#> [1] "2019-01-07" "2019-02-04"

as_weekday(time_point_shift(days, monday))
#> <weekday[2]>
#> [1] Mon Mon

You can also shift to the previous instance of the target weekday:

time_point_shift(days, monday, which = "previous")
#> <naive_time<day>[2]>
#> [1] "2018-12-31" "2019-01-28"

If you happen to already be on the target weekday, the default behavior returns the input unchanged. However, you can also chose to advance to the next instance of the target.

tuesday <- weekday(clock_weekdays$tuesday)

time_point_shift(days, tuesday)
#> <naive_time<day>[2]>
#> [1] "2019-01-01" "2019-02-05"
time_point_shift(days, tuesday, boundary = "advance")
#> <naive_time<day>[2]>
#> [1] "2019-01-08" "2019-02-05"

While time_point_shift() is built in to clock, it can be useful to discuss the arithmetic going on in the underlying weekday type which powers this function. To do so, we will build some parts of time_point_shift() from scratch.

The weekday type represents a single day of the week and implements circular arithmetic. Let’s see the code for a simple version of time_point_shift() that just shifts to the next target weekday:

next_weekday <- function(x, target) {
  x + (target - as_weekday(x))
}

next_weekday(days, monday)
#> <naive_time<day>[2]>
#> [1] "2019-01-07" "2019-02-04"

as_weekday(next_weekday(days, monday))
#> <weekday[2]>
#> [1] Mon Mon

Let’s break down how next_weekday() works. The first step takes the difference between two weekday vectors. It does this using circular arithmetic. Once we get passed the 7th day of the week (whatever that may be), it wraps back around to the 1st day of the week. Implementing weekday arithmetic in this way means that the following nicely returns the number of days until the next Monday as a day based duration:

monday - as_weekday(days)
#> <duration<day>[2]>
#> [1] 6 3

Which can be added to our day precision days vector to get the date of the next Monday:

days + (monday - as_weekday(days))
#> <naive_time<day>[2]>
#> [1] "2019-01-07" "2019-02-04"

The current implementation will return the input if it is already on the target weekday. To use the boundary = "advance" behavior, you could implement next_weekday() as:

next_weekday2 <- function(x, target) {
  x <- x + duration_days(1L)
  x + (target - as_weekday(x))
}

a_monday <- as_naive_time(year_month_day(2018, 12, 31))
as_weekday(a_monday)
#> <weekday[1]>
#> [1] Mon

next_weekday2(a_monday, monday)
#> <naive_time<day>[1]>
#> [1] "2019-01-07"

High level API

In the high level API, you can use date_shift():

monday <- weekday(clock_weekdays$monday)

x <- as.Date(c("2019-01-01", "2019-02-01"))

date_shift(x, monday)
#> [1] "2019-01-07" "2019-02-04"

# With a date-time
y <- as.POSIXct(
  c("2019-01-01 02:30:30", "2019-02-01 05:20:22"), 
  "America/New_York"
)

date_shift(y, monday)
#> [1] "2019-01-07 02:30:30 EST" "2019-02-04 05:20:22 EST"

Note that adding weekdays to a POSIXct could generate nonexistent or ambiguous times due to daylight saving time, which would have to be handled by supplying nonexistent and ambiguous arguments to date_shift().

Generate sequences of dates and date-times

clock implements S3 methods for the seq() generic function for the calendar and time point types it provides. The precision that you can generate sequences for depends on the type.

  • year-month-day: Yearly or monthly sequences
  • year-quarter-day: Yearly or quarterly sequences
  • sys-time / naive-time: Weekly, Daily, Hourly, …, Subsecond sequences

When generating sequences, the type and precision of from determine the result. For example:

ym <- seq(year_month_day(2019, 1), by = 2, length.out = 10)
ym
#> <year_month_day<month>[10]>
#>  [1] "2019-01" "2019-03" "2019-05" "2019-07" "2019-09" "2019-11" "2020-01"
#>  [8] "2020-03" "2020-05" "2020-07"
yq <- seq(year_quarter_day(2019, 1), by = 2, length.out = 10)

This allows you to generate sequences of year-months or year-quarters without having to worry about the day of the month/quarter becoming invalid. You can set the day of the results to get to a day precision calendar. For example, to get the last days of the month/quarter for this sequence:

set_day(ym, "last")
#> <year_month_day<day>[10]>
#>  [1] "2019-01-31" "2019-03-31" "2019-05-31" "2019-07-31" "2019-09-30"
#>  [6] "2019-11-30" "2020-01-31" "2020-03-31" "2020-05-31" "2020-07-31"

set_day(yq, "last")
#> <year_quarter_day<January><day>[10]>
#>  [1] "2019-Q1-90" "2019-Q3-92" "2020-Q1-91" "2020-Q3-92" "2021-Q1-90"
#>  [6] "2021-Q3-92" "2022-Q1-90" "2022-Q3-92" "2023-Q1-90" "2023-Q3-92"

You won’t be able to generate day precision sequences with calendars. Instead, you should use a time point.

from <- as_naive_time(year_month_day(2019, 1, 1))
to <- as_naive_time(year_month_day(2019, 5, 15))

seq(from, to, by = 20)
#> <naive_time<day>[7]>
#> [1] "2019-01-01" "2019-01-21" "2019-02-10" "2019-03-02" "2019-03-22"
#> [6] "2019-04-11" "2019-05-01"

If you use an integer by value, it is interpreted as a duration at the same precision as from. You can also use a duration object that can be cast to the same precision as from. For example, to generate a sequence spaced out by 90 minutes for these second precision end points:

from <- as_naive_time(year_month_day(2019, 1, 1, 2, 30, 00))
to <- as_naive_time(year_month_day(2019, 1, 1, 12, 30, 00))

seq(from, to, by = duration_minutes(90))
#> <naive_time<second>[7]>
#> [1] "2019-01-01T02:30:00" "2019-01-01T04:00:00" "2019-01-01T05:30:00"
#> [4] "2019-01-01T07:00:00" "2019-01-01T08:30:00" "2019-01-01T10:00:00"
#> [7] "2019-01-01T11:30:00"

High level API

In the high level API, you can use date_seq() to generate sequences. This doesn’t have all of the flexibility of the seq() methods above, but is still extremely useful and has the added benefit of switching between calendars, sys-times, and naive-times automatically for you.

If an integer by is supplied with a date from, it defaults to a daily sequence:

date_seq(date_build(2019, 1), by = 2, total_size = 10)
#>  [1] "2019-01-01" "2019-01-03" "2019-01-05" "2019-01-07" "2019-01-09"
#>  [6] "2019-01-11" "2019-01-13" "2019-01-15" "2019-01-17" "2019-01-19"

You can generate a monthly sequence by supplying a month precision duration for by.

date_seq(date_build(2019, 1), by = duration_months(2), total_size = 10)
#>  [1] "2019-01-01" "2019-03-01" "2019-05-01" "2019-07-01" "2019-09-01"
#>  [6] "2019-11-01" "2020-01-01" "2020-03-01" "2020-05-01" "2020-07-01"

If you supply to, be aware that all components of to that are more precise than the precision of by must match from exactly. For example, the day component of from and to doesn’t match here, so the sequence isn’t defined.

date_seq(
  date_build(2019, 1, 1),
  to = date_build(2019, 10, 2),
  by = duration_months(2)
)
#> Error in `date_seq()`:
#> ! All components of `from` and `to` more precise than "month" must
#>   match.
#> ℹ `from` is "2019-01-01".
#> ℹ `to` is "2019-10-02".

date_seq() also catches invalid dates for you, forcing you to specify the invalid argument to specify how to handle them.

jan31 <- date_build(2019, 1, 31)
dec31 <- date_build(2019, 12, 31)

date_seq(jan31, to = dec31, by = duration_months(1))
#> Error in `invalid_resolve()`:
#> ! Invalid date found at location 2.
#> ℹ Resolve invalid date issues by specifying the `invalid` argument.

By specifying invalid = "previous" here, we can generate month end values.

date_seq(jan31, to = dec31, by = duration_months(1), invalid = "previous")
#>  [1] "2019-01-31" "2019-02-28" "2019-03-31" "2019-04-30" "2019-05-31"
#>  [6] "2019-06-30" "2019-07-31" "2019-08-31" "2019-09-30" "2019-10-31"
#> [11] "2019-11-30" "2019-12-31"

Compare this with the automatic “overflow” behavior of seq(), which is often a source of confusion.

seq(jan31, to = dec31, by = "1 month")
#>  [1] "2019-01-31" "2019-03-03" "2019-03-31" "2019-05-01" "2019-05-31"
#>  [6] "2019-07-01" "2019-07-31" "2019-08-31" "2019-10-01" "2019-10-31"
#> [11] "2019-12-01" "2019-12-31"

Grouping by months or quarters

When working on a data analysis, you might be required to summarize certain metrics at a monthly or quarterly level. With calendar_group(), you can easily summarize at the granular precision that you care about. Take this vector of day precision naive-times in 2019:

from <- as_naive_time(year_month_day(2019, 1, 1))
to <- as_naive_time(year_month_day(2019, 12, 31))

x <- seq(from, to, by = duration_days(20))

x
#> <naive_time<day>[19]>
#>  [1] "2019-01-01" "2019-01-21" "2019-02-10" "2019-03-02" "2019-03-22"
#>  [6] "2019-04-11" "2019-05-01" "2019-05-21" "2019-06-10" "2019-06-30"
#> [11] "2019-07-20" "2019-08-09" "2019-08-29" "2019-09-18" "2019-10-08"
#> [16] "2019-10-28" "2019-11-17" "2019-12-07" "2019-12-27"

To group by month, first convert to a year-month-day:

ymd <- as_year_month_day(x)

head(ymd)
#> <year_month_day<day>[6]>
#> [1] "2019-01-01" "2019-01-21" "2019-02-10" "2019-03-02" "2019-03-22"
#> [6] "2019-04-11"

calendar_group(ymd, "month")
#> <year_month_day<month>[19]>
#>  [1] "2019-01" "2019-01" "2019-02" "2019-03" "2019-03" "2019-04" "2019-05"
#>  [8] "2019-05" "2019-06" "2019-06" "2019-07" "2019-08" "2019-08" "2019-09"
#> [15] "2019-10" "2019-10" "2019-11" "2019-12" "2019-12"

To group by quarter, convert to a year-quarter-day:

yqd <- as_year_quarter_day(x)

head(yqd)
#> <year_quarter_day<January><day>[6]>
#> [1] "2019-Q1-01" "2019-Q1-21" "2019-Q1-41" "2019-Q1-61" "2019-Q1-81"
#> [6] "2019-Q2-11"

calendar_group(yqd, "quarter")
#> <year_quarter_day<January><quarter>[19]>
#>  [1] "2019-Q1" "2019-Q1" "2019-Q1" "2019-Q1" "2019-Q1" "2019-Q2" "2019-Q2"
#>  [8] "2019-Q2" "2019-Q2" "2019-Q2" "2019-Q3" "2019-Q3" "2019-Q3" "2019-Q3"
#> [15] "2019-Q4" "2019-Q4" "2019-Q4" "2019-Q4" "2019-Q4"

If you need to group by a multiple of months / quarters, you can do that too:

calendar_group(ymd, "month", n = 2)
#> <year_month_day<month>[19]>
#>  [1] "2019-01" "2019-01" "2019-01" "2019-03" "2019-03" "2019-03" "2019-05"
#>  [8] "2019-05" "2019-05" "2019-05" "2019-07" "2019-07" "2019-07" "2019-09"
#> [15] "2019-09" "2019-09" "2019-11" "2019-11" "2019-11"

calendar_group(yqd, "quarter", n = 2)
#> <year_quarter_day<January><quarter>[19]>
#>  [1] "2019-Q1" "2019-Q1" "2019-Q1" "2019-Q1" "2019-Q1" "2019-Q1" "2019-Q1"
#>  [8] "2019-Q1" "2019-Q1" "2019-Q1" "2019-Q3" "2019-Q3" "2019-Q3" "2019-Q3"
#> [15] "2019-Q3" "2019-Q3" "2019-Q3" "2019-Q3" "2019-Q3"

Note that the returned calendar vector is at the precision we grouped by, not at the original precision with, say, the day of the month / quarter set to 1.

Additionally, be aware that calendar_group() groups “within” the component that is one unit of precision larger than the precision you specify. So, when grouping by "day", this groups by “day of the month”, which can’t cross the month or year boundary. If you need to bundle dates together by something like 60 days (i.e. crossing the month boundary), then you should use time_point_floor().

High level API

In the high level API, you can use date_group() to group Date vectors by one of their 3 components: year, month, or day. Since month precision dates can’t be represented with Date vectors, date_group() sets the day of the month to 1.

x <- seq(as.Date("2019-01-01"), as.Date("2019-12-31"), by = 20)

date_group(x, "month")
#>  [1] "2019-01-01" "2019-01-01" "2019-02-01" "2019-03-01" "2019-03-01"
#>  [6] "2019-04-01" "2019-05-01" "2019-05-01" "2019-06-01" "2019-06-01"
#> [11] "2019-07-01" "2019-08-01" "2019-08-01" "2019-09-01" "2019-10-01"
#> [16] "2019-10-01" "2019-11-01" "2019-12-01" "2019-12-01"

You won’t be able to group by "quarter", since this isn’t one of the 3 components that the high level API lets you work with. Instead, this is a case where you should convert to a year-quarter-day, group on that type, then convert back to Date.

x %>%
  as_year_quarter_day() %>%
  calendar_group("quarter") %>%
  set_day(1) %>%
  as.Date()
#>  [1] "2019-01-01" "2019-01-01" "2019-01-01" "2019-01-01" "2019-01-01"
#>  [6] "2019-04-01" "2019-04-01" "2019-04-01" "2019-04-01" "2019-04-01"
#> [11] "2019-07-01" "2019-07-01" "2019-07-01" "2019-07-01" "2019-10-01"
#> [16] "2019-10-01" "2019-10-01" "2019-10-01" "2019-10-01"

This is actually equivalent to date_group(x, "month", n = 3). If your fiscal year starts in January, you can use that instead. However, if your fiscal year starts in a different month, say, June, you’ll need to use the approach from above like so:

x %>%
  as_year_quarter_day(start = clock_months$june) %>%
  calendar_group("quarter") %>%
  set_day(1) %>%
  as.Date()
#>  [1] "2018-12-01" "2018-12-01" "2018-12-01" "2019-03-01" "2019-03-01"
#>  [6] "2019-03-01" "2019-03-01" "2019-03-01" "2019-06-01" "2019-06-01"
#> [11] "2019-06-01" "2019-06-01" "2019-06-01" "2019-09-01" "2019-09-01"
#> [16] "2019-09-01" "2019-09-01" "2019-12-01" "2019-12-01"

Flooring by days

While calendar_group() can group by “component”, it isn’t useful for bundling together sets of time points that can cross month/year boundaries, like “60 days” of data. For that, you are better off flooring by rolling sets of 60 days.

from <- as_naive_time(year_month_day(2019, 1, 1))
to <- as_naive_time(year_month_day(2019, 12, 31))

x <- seq(from, to, by = duration_days(20))
time_point_floor(x, "day", n = 60)
#> <naive_time<day>[19]>
#>  [1] "2018-12-15" "2018-12-15" "2018-12-15" "2019-02-13" "2019-02-13"
#>  [6] "2019-02-13" "2019-04-14" "2019-04-14" "2019-04-14" "2019-06-13"
#> [11] "2019-06-13" "2019-06-13" "2019-08-12" "2019-08-12" "2019-08-12"
#> [16] "2019-10-11" "2019-10-11" "2019-10-11" "2019-12-10"

Flooring operates on the underlying duration, which for day precision time points is a count of days since the origin, 1970-01-01.

unclass(x[1])
#> $lower
#> [1] 2147483648
#> 
#> $upper
#> [1] 17897
#> 
#> attr(,"clock")
#> [1] 1
#> attr(,"precision")
#> [1] 4

The 60 day counter starts here, which means that any times between [1970-01-01, 1970-03-02) are all floored to 1970-01-01. At 1970-03-02, the counter starts again.

If you would like to change this origin, you can provide a time point to start counting from with the origin argument. This is mostly useful if you are flooring by weeks and you want to change the day of the week that the count starts on. Since 1970-01-01 is a Thursday, flooring by 14 days defaults to returning all Thursdays.

x <- seq(as_naive_time(year_month_day(2019, 1, 1)), by = 3, length.out = 10)
x
#> <naive_time<day>[10]>
#>  [1] "2019-01-01" "2019-01-04" "2019-01-07" "2019-01-10" "2019-01-13"
#>  [6] "2019-01-16" "2019-01-19" "2019-01-22" "2019-01-25" "2019-01-28"

thursdays <- time_point_floor(x, "day", n = 14)
thursdays
#> <naive_time<day>[10]>
#>  [1] "2018-12-27" "2018-12-27" "2018-12-27" "2019-01-10" "2019-01-10"
#>  [6] "2019-01-10" "2019-01-10" "2019-01-10" "2019-01-24" "2019-01-24"

as_weekday(thursdays)
#> <weekday[10]>
#>  [1] Thu Thu Thu Thu Thu Thu Thu Thu Thu Thu

You can use origin to change this to floor to Mondays.

origin <- as_naive_time(year_month_day(2018, 12, 31))
as_weekday(origin)
#> <weekday[1]>
#> [1] Mon

mondays <- time_point_floor(x, "day", n = 14, origin = origin)
mondays
#> <naive_time<day>[10]>
#>  [1] "2018-12-31" "2018-12-31" "2018-12-31" "2018-12-31" "2018-12-31"
#>  [6] "2019-01-14" "2019-01-14" "2019-01-14" "2019-01-14" "2019-01-28"

as_weekday(mondays)
#> <weekday[10]>
#>  [1] Mon Mon Mon Mon Mon Mon Mon Mon Mon Mon

High level API

You can use date_floor() with Date and POSIXct types.

x <- seq(as.Date("2019-01-01"), as.Date("2019-12-31"), by = 20)

date_floor(x, "day", n = 60)
#>  [1] "2018-12-15" "2018-12-15" "2018-12-15" "2019-02-13" "2019-02-13"
#>  [6] "2019-02-13" "2019-04-14" "2019-04-14" "2019-04-14" "2019-06-13"
#> [11] "2019-06-13" "2019-06-13" "2019-08-12" "2019-08-12" "2019-08-12"
#> [16] "2019-10-11" "2019-10-11" "2019-10-11" "2019-12-10"

The origin you provide should be another Date. For week precision flooring with Dates, you can specify "week" as the precision.

x <- seq(as.Date("2019-01-01"), by = 3, length.out = 10)

origin <- as.Date("2018-12-31")

date_floor(x, "week", n = 2, origin = origin)
#>  [1] "2018-12-31" "2018-12-31" "2018-12-31" "2018-12-31" "2018-12-31"
#>  [6] "2019-01-14" "2019-01-14" "2019-01-14" "2019-01-14" "2019-01-28"

Day of the year

To get the day of the year, convert to the year-day calendar type and extract the day with get_day().

x <- year_month_day(2019, clock_months$july, 4)

yd <- as_year_day(x)
yd
#> <year_day<day>[1]>
#> [1] "2019-185"

get_day(yd)
#> [1] 185

High level API

x <- as.Date("2019-07-04")

x %>%
  as_year_day() %>%
  get_day()
#> [1] 185

Computing an age in years

To get the age of an individual in years, use calendar_count_between().

x <- year_month_day(1980, 12, 14:16)
today <- year_month_day(2005, 12, 15)

# Note that the month and day of the month are taken into account!
# (Time of day would also be taken into account if there was any.)
calendar_count_between(x, today, "year")
#> [1] 25 25 24

High level API

You can use date_count_between() with Date and POSIXct types.

x <- date_build(1980, 12, 14:16)
today <- date_build(2005, 12, 15)

date_count_between(x, today, "year")
#> [1] 25 25 24

Computing number of weeks since the start of the year

lubridate::week() is a useful function that returns “the number of complete seven day periods that have occurred between the date and January 1st, plus one.”

There is no direct equivalent to this, but it is possible to replicate with calendar_start() and time_point_count_between().

x <- year_month_day(2019, 11, 28)

# lubridate::week(as.Date(x))
# [1] 48

x_start <- calendar_start(x, "year")
x_start
#> <year_month_day<day>[1]>
#> [1] "2019-01-01"

time_point_count_between(
  as_naive_time(x_start),
  as_naive_time(x),
  "week"
) + 1L
#> [1] 48

You could also peek at the lubridate::week() implementation to see that this is just:

doy <- get_day(as_year_day(x))
doy
#> [1] 332

(doy - 1L) %/% 7L + 1L
#> [1] 48

High level API

This is actually a little easier in the high level API because you don’t have to think about switching between types.

x <- date_build(2019, 11, 28)

date_count_between(date_start(x, "year"), x, "week") + 1L
#> [1] 48

Compute the number of months between two dates

How can we compute the number of months between these two dates?

x <- year_month_day(2013, 10, 15)
y <- year_month_day(2016, 10, 13)

This is a bit of an ambiguous question because “month” isn’t very well-defined, and there are various different interpretations we could take.

We might want to ignore the day component entirely, and just compute the number of months between 2013-10 and 2016-10.

calendar_narrow(y, "month") - calendar_narrow(x, "month")
#> <duration<month>[1]>
#> [1] 36

Or we could include the day of the month, and say that 2013-10-15 to 2014-10-15 defines 1 month (i.e. you have to hit the same day of the month in the next month).

calendar_count_between(x, y, "month")
#> [1] 35

With this you could also compute the number of days remaining between these two dates.

x_close <- add_months(x, calendar_count_between(x, y, "month"))
x_close
#> <year_month_day<day>[1]>
#> [1] "2016-09-15"

x_close_st <- as_sys_time(x_close)
y_st <- as_sys_time(y)

time_point_count_between(x_close_st, y_st, "day")
#> [1] 28

Or we could compute the number of days between these two dates in units of seconds, and divide that by the average number of seconds in 1 proleptic Gregorian month.

# Days between x and y
days <- as_sys_time(y) - as_sys_time(x)
days
#> <duration<day>[1]>
#> [1] 1094

# In units of seconds
days <- duration_cast(days, "second")
days <- as.numeric(days)
days
#> [1] 94521600

# Average number of seconds in 1 proleptic Gregorian month
avg_sec_in_month <- duration_cast(duration_months(1), "second")
avg_sec_in_month <- as.numeric(avg_sec_in_month)

days / avg_sec_in_month
#> [1] 35.94324

High level API

x <- date_build(2013, 10, 15)
y <- date_build(2016, 10, 13)

To ignore the day of the month, first shift to the start of the month, then you can use date_count_between().

date_count_between(date_start(x, "month"), date_start(y, "month"), "month")
#> [1] 36

To utilize the day field, do the same as above but without calling date_start().

date_count_between(x, y, "month")
#> [1] 35

There is no high level equivalent to the average length of one proleptic Gregorian month example.

Computing the ISO year or week

The ISO 8601 standard outlines an alternative calendar that is specified by the year, the week of the year, and the day of the week. It also specifies that the start of the week is considered to be a Monday. This ends up meaning that the actual ISO year may be different from the Gregorian year, and is somewhat difficult to compute “by hand”. Instead, you can use the year_week_day() calendar if you need to work with ISO week dates.

x <- date_build(2019:2026)
y <- as_year_week_day(x, start = clock_weekdays$monday)

data.frame(x = x, y = y)
#>            x          y
#> 1 2019-01-01 2019-W01-2
#> 2 2020-01-01 2020-W01-3
#> 3 2021-01-01 2020-W53-5
#> 4 2022-01-01 2021-W52-6
#> 5 2023-01-01 2022-W52-7
#> 6 2024-01-01 2024-W01-1
#> 7 2025-01-01 2025-W01-3
#> 8 2026-01-01 2026-W01-4
get_year(y)
#> [1] 2019 2020 2020 2021 2022 2024 2025 2026
get_week(y)
#> [1]  1  1 53 52 52  1  1  1

# Last week in the ISO year
set_week(y, "last")
#> <year_week_day<Monday><day>[8]>
#> [1] "2019-W52-2" "2020-W53-3" "2020-W53-5" "2021-W52-6" "2022-W52-7"
#> [6] "2024-W52-1" "2025-W52-3" "2026-W53-4"

The year-week-day calendar is a fully supported calendar, meaning that all of the calendar_*() functions work on it:

calendar_narrow(y, "week")
#> <year_week_day<Monday><week>[8]>
#> [1] "2019-W01" "2020-W01" "2020-W53" "2021-W52" "2022-W52" "2024-W01" "2025-W01"
#> [8] "2026-W01"

There is also an iso_year_week_day() calendar available, which is identical to year_week_day(start = clock_weekdays$monday). That ISO calendar actually existed first, before we generalized it to any start weekday.

Computing the Epidemiological year or week

Epidemiologists following the US CDC guidelines use a calendar that is similar to the ISO calendar, but defines the start of the week to be Sunday instead of Monday. year_week_day() supports this as well:

x <- date_build(2019:2026)
iso <- as_year_week_day(x, start = clock_weekdays$monday)
epi <- as_year_week_day(x, start = clock_weekdays$sunday)

data.frame(x = x, iso = iso, epi = epi)
#>            x        iso        epi
#> 1 2019-01-01 2019-W01-2 2019-W01-3
#> 2 2020-01-01 2020-W01-3 2020-W01-4
#> 3 2021-01-01 2020-W53-5 2020-W53-6
#> 4 2022-01-01 2021-W52-6 2021-W52-7
#> 5 2023-01-01 2022-W52-7 2023-W01-1
#> 6 2024-01-01 2024-W01-1 2024-W01-2
#> 7 2025-01-01 2025-W01-3 2025-W01-4
#> 8 2026-01-01 2026-W01-4 2025-W53-5
get_year(epi)
#> [1] 2019 2020 2020 2021 2023 2024 2025 2025
get_week(epi)
#> [1]  1  1 53 52  1  1  1 53

Converting a time zone abbreviation into a time zone name

It is possible that you might run into date-time strings of the form "2020-10-25 01:30:00 IST", which contain a time zone abbreviation rather than a full time zone name. Because time zone maintainers change the abbreviation they use throughout time, and because multiple time zones sometimes use the same abbreviation, it is generally impossible to parse strings of this form without more information. That said, if you know what time zone this abbreviation goes with, you can parse this time with zoned_time_parse_abbrev(), supplying the zone.

x <- "2020-10-25 01:30:00 IST"

zoned_time_parse_abbrev(x, "Asia/Kolkata")
#> <zoned_time<second><Asia/Kolkata>[1]>
#> [1] "2020-10-25T01:30:00+05:30"
zoned_time_parse_abbrev(x, "Asia/Jerusalem")
#> <zoned_time<second><Asia/Jerusalem>[1]>
#> [1] "2020-10-25T01:30:00+02:00"

If you don’t know what time zone this abbreviation goes with, then generally you are out of luck. However, there are low-level tools in this library that can help you generate a list of possible zoned-times this could map to.

Assuming that x is a naive-time with its corresponding time zone abbreviation attached, the first thing to do is to parse this string as a naive-time.

x <- naive_time_parse(x, format = "%Y-%m-%d %H:%M:%S IST")
x
#> <naive_time<second>[1]>
#> [1] "2020-10-25T01:30:00"

Next, we’ll develop a function that attempts to turn this naive-time into a zoned-time, iterating through all of the time zone names available in the time zone database. These time zone names are accessible through tzdb_names(). By using the low-level naive_time_info(), rather than as_zoned_time(), to lookup zone specific information, we’ll also get back information about the UTC offset and time zone abbreviation that is currently in use. By matching this abbreviation against our input abbreviation, we can generate a list of zoned-times that use the abbreviation we care about at that particular instance in time.

naive_find_by_abbrev <- function(x, abbrev) {
  if (!is_naive_time(x)) {
    abort("`x` must be a naive-time.")
  }
  if (length(x) != 1L) {
    abort("`x` must be length 1.")
  }
  if (!rlang::is_string(abbrev)) {
    abort("`abbrev` must be a single string.")
  }
  
  zones <- tzdb_names()
  info <- naive_time_info(x, zones)
  info$zones <- zones
  
  c(
    compute_uniques(x, info, abbrev),
    compute_ambiguous(x, info, abbrev)
  )
}

compute_uniques <- function(x, info, abbrev) {
  info <- info[info$type == "unique",]
  
  # If the abbreviation of the unique time matches the input `abbrev`,
  # then that candidate zone should be in the output
  matches <- info$first$abbreviation == abbrev
  zones <- info$zones[matches]
  
  lapply(zones, as_zoned_time, x = x)
}

compute_ambiguous <- function(x, info, abbrev) {
  info <- info[info$type == "ambiguous",]

  # Of the two possible times,
  # does the abbreviation of the earliest match the input `abbrev`?
  matches <- info$first$abbreviation == abbrev
  zones <- info$zones[matches]
  
  earliest <- lapply(zones, as_zoned_time, x = x, ambiguous = "earliest")
  
  # Of the two possible times,
  # does the abbreviation of the latest match the input `abbrev`?
  matches <- info$second$abbreviation == abbrev
  zones <- info$zones[matches]
  
  latest <- lapply(zones, as_zoned_time, x = x, ambiguous = "latest")
  
  c(earliest, latest)
}
candidates <- naive_find_by_abbrev(x, "IST")
candidates
#> [[1]]
#> <zoned_time<second><Asia/Calcutta>[1]>
#> [1] "2020-10-25T01:30:00+05:30"
#> 
#> [[2]]
#> <zoned_time<second><Asia/Kolkata>[1]>
#> [1] "2020-10-25T01:30:00+05:30"
#> 
#> [[3]]
#> <zoned_time<second><Eire>[1]>
#> [1] "2020-10-25T01:30:00+01:00"
#> 
#> [[4]]
#> <zoned_time<second><Europe/Dublin>[1]>
#> [1] "2020-10-25T01:30:00+01:00"
#> 
#> [[5]]
#> <zoned_time<second><Asia/Jerusalem>[1]>
#> [1] "2020-10-25T01:30:00+02:00"
#> 
#> [[6]]
#> <zoned_time<second><Asia/Tel_Aviv>[1]>
#> [1] "2020-10-25T01:30:00+02:00"
#> 
#> [[7]]
#> <zoned_time<second><Israel>[1]>
#> [1] "2020-10-25T01:30:00+02:00"

While it looks like we got 7 candidates, in reality we only have 3. Asia/Kolkata, Europe/Dublin, and Asia/Jerusalem are our 3 candidates. The others are aliases of those 3 that have been retired but are kept for backwards compatibility.

Looking at the code, there are two ways to add a candidate time zone name to the list.

If there is a unique mapping from {naive-time, zone} to sys-time, then we check if the abbreviation that goes with that unique mapping matches our input abbreviation. If so, then we convert x to a zoned-time with that time zone.

If there is an ambiguous mapping from {naive-time, zone} to sys-time, which is due to a daylight saving fallback, then we check the abbreviation of both the earliest and latest possible times. If either matches, then we convert x to a zoned-time using that time zone and the information about which of the two ambiguous times were used.

This example is particularly interesting, since each of the 3 candidates came from a different path. The Asia/Kolkata one is unique, the Europe/Dublin one is ambiguous but the earliest was chosen, and the Asia/Jerusalem one is ambiguous but the latest was chosen:

as_zoned_time(x, "Asia/Kolkata")
#> <zoned_time<second><Asia/Kolkata>[1]>
#> [1] "2020-10-25T01:30:00+05:30"
as_zoned_time(x, "Europe/Dublin", ambiguous = "earliest")
#> <zoned_time<second><Europe/Dublin>[1]>
#> [1] "2020-10-25T01:30:00+01:00"
as_zoned_time(x, "Asia/Jerusalem", ambiguous = "latest")
#> <zoned_time<second><Asia/Jerusalem>[1]>
#> [1] "2020-10-25T01:30:00+02:00"

When is the next daylight saving time event?

Given a particular zoned-time, when will it next be affected by daylight saving time? For this, we can use a relatively low level helper, zoned_time_info(). It returns a data frame of information about the current daylight saving time transition points, along with information about the offset, the current time zone abbreviation, and whether or not daylight saving time is currently active or not.

x <- zoned_time_parse_complete("2019-01-01T00:00:00-05:00[America/New_York]")

info <- zoned_time_info(x)

# Beginning of the current DST range
info$begin
#> <zoned_time<second><America/New_York>[1]>
#> [1] "2018-11-04T01:00:00-05:00"

# Beginning of the next DST range
info$end
#> <zoned_time<second><America/New_York>[1]>
#> [1] "2019-03-10T03:00:00-04:00"

So on 2018-11-04 at (the second) 1 o’clock hour, daylight saving time was turned off. On 2019-03-10 at 3 o’clock, daylight saving time will be considered on again. This is the next moment in time right after a daylight saving time gap of 1 hour, which you can see by subtracting 1 second (in sys-time):

# Last moment in time in the current DST range
info$end %>%
  as_sys_time() %>%
  add_seconds(-1) %>%
  as_zoned_time(zoned_time_zone(x))
#> <zoned_time<second><America/New_York>[1]>
#> [1] "2019-03-10T01:59:59-05:00"

High level API

date_time_info() exists in the high level API to do a similar thing. It is basically the same as zoned_time_info(), except the begin and end columns are returned as R POSIXct date-times rather than zoned-times, and the offset column is returned as an integer rather than as a clock duration (since we try not to expose high level API users to low level types).

x <- date_time_parse("2019-01-01 00:00:00", zone = "America/New_York")

date_time_info(x)
#>                 begin                 end offset   dst abbreviation
#> 1 2018-11-04 01:00:00 2019-03-10 03:00:00 -18000 FALSE          EST
clock/inst/doc/clock.html0000644000176200001440000021016014430471261015052 0ustar liggesusers Getting Started

Getting Started

library(clock)
library(magrittr)

The goal of this vignette is to introduce you to clock’s high-level API, which works directly on R’s built-in date-time types, Date and POSIXct. For an overview of all of the functionality in the high-level API, check out the pkgdown reference section, High Level API. One thing you should immediately notice is that every function specific to R’s date and date-time types are prefixed with date_*(). There are also additional functions for arithmetic (add_*()) and getting (get_*()) or setting (set_*()) components that are also used by other types in clock.

As you’ll quickly see in this vignette, one of the main goals of clock is to guard you, the user, from unexpected issues caused by frustrating date manipulation concepts like invalid dates and daylight saving time. It does this by letting you know as soon as one of these issues happens, giving you the power to handle it explicitly with one of a number of different resolution strategies.

Building

To create a vector of dates, you can use date_build(). This allows you to specify the components individually.

date_build(2019, 2, 1:5)
#> [1] "2019-02-01" "2019-02-02" "2019-02-03" "2019-02-04" "2019-02-05"

If you happen to specify an invalid date, you’ll get an error message:

date_build(2019, 1:12, 31)
#> Error in `invalid_resolve()`:
#> ! Invalid date found at location 2.
#> ℹ Resolve invalid date issues by specifying the `invalid` argument.

One way to resolve this is by specifying an invalid date resolution strategy using the invalid argument. There are multiple options, but in this case we’ll ask for the invalid dates to be set to the previous valid moment in time.

date_build(2019, 1:12, 31, invalid = "previous")
#>  [1] "2019-01-31" "2019-02-28" "2019-03-31" "2019-04-30" "2019-05-31"
#>  [6] "2019-06-30" "2019-07-31" "2019-08-31" "2019-09-30" "2019-10-31"
#> [11] "2019-11-30" "2019-12-31"

To learn more about invalid dates, check out the documentation for invalid_resolve().

If we were actually after the “last day of the month”, an easier way to specify this would have been:

date_build(2019, 1:12, "last")
#>  [1] "2019-01-31" "2019-02-28" "2019-03-31" "2019-04-30" "2019-05-31"
#>  [6] "2019-06-30" "2019-07-31" "2019-08-31" "2019-09-30" "2019-10-31"
#> [11] "2019-11-30" "2019-12-31"

You can also create date-times using date_time_build(), which generates a POSIXct. Note that you must supply a time zone!

date_time_build(2019, 1:5, 1, 2, 30, zone = "America/New_York")
#> [1] "2019-01-01 02:30:00 EST" "2019-02-01 02:30:00 EST"
#> [3] "2019-03-01 02:30:00 EST" "2019-04-01 02:30:00 EDT"
#> [5] "2019-05-01 02:30:00 EDT"

If you “build” a time that doesn’t exist, you’ll get an error. For example, on March 8th, 2020, there was a daylight saving time gap of 1 hour in the America/New_York time zone that took us from 01:59:59 directly to 03:00:00, skipping the 2 o’clock hour entirely. Let’s “accidentally” create a time in that gap:

date_time_build(2019:2021, 3, 8, 2, 30, zone = "America/New_York")
#> Error in `as_zoned_time()`:
#> ! Nonexistent time due to daylight saving time at location 2.
#> ℹ Resolve nonexistent time issues by specifying the `nonexistent` argument.

To resolve this issue, we can specify a nonexistent time resolution strategy through the nonexistent argument. There are a number of options, including rolling forward or backward to the next or previous valid moments in time:

zone <- "America/New_York"

date_time_build(2019:2021, 3, 8, 2, 30, zone = zone, nonexistent = "roll-forward")
#> [1] "2019-03-08 02:30:00 EST" "2020-03-08 03:00:00 EDT"
#> [3] "2021-03-08 02:30:00 EST"
date_time_build(2019:2021, 3, 8, 2, 30, zone = zone, nonexistent = "roll-backward")
#> [1] "2019-03-08 02:30:00 EST" "2020-03-08 01:59:59 EST"
#> [3] "2021-03-08 02:30:00 EST"

Parsing

Parsing dates

To parse dates, use date_parse(). Parsing dates requires a format string, a combination of commands that specify where date components are in your string. By default, it assumes that you’re working with dates in the form "%Y-%m-%d" (year-month-day).

date_parse("2019-01-05")
#> [1] "2019-01-05"

You can change the format string using format:

date_parse("January 5, 2020", format = "%B %d, %Y")
#> [1] "2020-01-05"

Various different locales are supported for parsing month and weekday names in different languages. To parse a French month:

date_parse(
  "juillet 10, 2021", 
  format = "%B %d, %Y", 
  locale = clock_locale("fr")
)
#> [1] "2021-07-10"

You can learn about more locale options in the documentation for clock_locale().

If you have heterogeneous dates, you can supply multiple format strings:

x <- c("2020/1/5", "10-03-05", "2020/2/2")
formats <- c("%Y/%m/%d", "%y-%m-%d")

date_parse(x, format = formats)
#> [1] "2020-01-05" "2010-03-05" "2020-02-02"

Parsing date-times

You have four options when parsing date-times:

  • date_time_parse(): For strings like "2020-01-01 01:02:03" where there is neither a time zone offset nor a full (not abbreviated!) time zone name.

  • date_time_parse_complete(): For strings like "2020-01-01T01:02:03-05:00[America/New_York]" where there is both a time zone offset and time zone name present in the string.

  • date_time_parse_abbrev(): For strings like "2020-01-01 01:02:03 EST" where there is a time zone abbreviation in the string.

  • date_time_parse_RFC_3339(): For strings like "2020-01-01T01:02:03Z" or "2020-01-01T01:02:03-05:00", which are in RFC 3339 format and are intended to be interpreted as UTC.

date_time_parse()

date_time_parse() requires a zone argument, and will ignore any other zone information in the string (i.e. if you tried to specify %z and %Z). The default format string is "%Y-%m-%d %H:%M:%S".

date_time_parse("2020-01-01 01:02:03", "America/New_York")
#> [1] "2020-01-01 01:02:03 EST"

If you happen to parse an invalid or ambiguous date-time, you’ll get an error. For example, on November 1st, 2020, there were two 1 o’clock hours in the America/New_York time zone due to a daylight saving time fallback. You can see that if we parse a time right before the fallback, and then shift it forward by 1 second, and then 1 hour and 1 second, respectively:

before <- date_time_parse("2020-11-01 00:59:59", "America/New_York")

# First 1 o'clock
before + 1
#> [1] "2020-11-01 01:00:00 EDT"

# Second 1 o'clock
before + 1 + 3600
#> [1] "2020-11-01 01:00:00 EST"

The following string doesn’t include any information about which of these two 1 o’clocks it belongs to, so it is considered ambiguous. Ambiguous times will error when parsing:

date_time_parse("2020-11-01 01:30:00", "America/New_York")
#> Error in `as_zoned_time()`:
#> ! Ambiguous time due to daylight saving time at location 1.
#> ℹ Resolve ambiguous time issues by specifying the `ambiguous` argument.

To fix that, you can specify an ambiguous time resolution strategy with the ambiguous argument.

zone <- "America/New_York"

date_time_parse("2020-11-01 01:30:00", zone, ambiguous = "earliest")
#> [1] "2020-11-01 01:30:00 EDT"
date_time_parse("2020-11-01 01:30:00", zone, ambiguous = "latest")
#> [1] "2020-11-01 01:30:00 EST"

date_time_parse_complete()

date_time_parse_complete() doesn’t have a zone argument, and doesn’t require ambiguous or nonexistent arguments, since it assumes that the string you are providing is completely unambiguous. The only way this is possible is by having both a time zone offset, specified by %z, and a full time zone name, specified by %Z, in the string.

The following is an example of an “extended” RFC 3339 format used by Java 8’s time library to specify complete date-time strings. This is something that date_time_parse_complete() can parse. The default format string follows this extended format, and is "%Y-%m-%dT%H:%M:%S%z[%Z]".

x <- "2020-01-01T01:02:03-05:00[America/New_York]"

date_time_parse_complete(x)
#> [1] "2020-01-01 01:02:03 EST"

date_time_parse_abbrev()

date_time_parse_abbrev() is useful when your date-time strings contain a time zone abbreviation rather than a time zone offset or full time zone name.

x <- "2020-01-01 01:02:03 EST"

date_time_parse_abbrev(x, "America/New_York")
#> [1] "2020-01-01 01:02:03 EST"

The string is first parsed as a naive time without considering the abbreviation, and is then converted to a zoned-time using the supplied zone. If an ambiguous time is parsed, the abbreviation is used to resolve the ambiguity.

x <- c(
  "1970-10-25 01:30:00 EDT",
  "1970-10-25 01:30:00 EST"
)

date_time_parse_abbrev(x, "America/New_York")
#> [1] "1970-10-25 01:30:00 EDT" "1970-10-25 01:30:00 EST"

You might be wondering why you need to supply zone at all. Isn’t the abbreviation enough? Unfortunately, multiple countries use the same time zone abbreviations, even though they have different time zones. This means that, in many cases, the abbreviation alone is ambiguous. For example, both India and Israel use IST for their standard times.

x <- "1970-01-01 02:30:30 IST"

# IST = India Standard Time
date_time_parse_abbrev(x, "Asia/Kolkata")
#> [1] "1970-01-01 02:30:30 IST"

# IST = Israel Standard Time
date_time_parse_abbrev(x, "Asia/Jerusalem")
#> [1] "1970-01-01 02:30:30 IST"

date_time_parse_RFC_3339()

date_time_parse_RFC_3339() is useful when your date-time strings come from an API, which means they are likely in an ISO 8601 or RFC 3339 format, and should be interpreted as UTC.

The default format string parses the typical RFC 3339 format of "%Y-%m-%dT%H:%M:%SZ".

x <- "2020-01-01T01:02:03Z"

date_time_parse_RFC_3339(x)
#> [1] "2020-01-01 01:02:03 UTC"

If your date-time strings contain a numeric offset from UTC rather than a "Z", then you’ll need to set the offset argument to one of the following:

  • "%z" if the offset is of the form "-0500".
  • "%Ez" if the offset is of the form "-05:00".
x <- "2020-01-01T01:02:03-0500"

date_time_parse_RFC_3339(x, offset = "%z")
#> [1] "2020-01-01 06:02:03 UTC"

x <- "2020-01-01T01:02:03-05:00"

date_time_parse_RFC_3339(x, offset = "%Ez")
#> [1] "2020-01-01 06:02:03 UTC"

Grouping, rounding and shifting

When performing time-series related data analysis, you often need to summarize your series at a less precise precision. There are many different ways to do this, and the differences between them are subtle, but meaningful. clock offers three different sets of functions for summarization:

  • date_group()

  • date_floor(), date_ceiling(), and date_round()

  • date_shift()

Grouping

Grouping allows you to summarize a component of a date or date-time within other components. An example of this is grouping by day of the month, which summarizes the day component within the current year-month.

x <- seq(date_build(2019, 1, 20), date_build(2019, 2, 5), by = 1)
x
#>  [1] "2019-01-20" "2019-01-21" "2019-01-22" "2019-01-23" "2019-01-24"
#>  [6] "2019-01-25" "2019-01-26" "2019-01-27" "2019-01-28" "2019-01-29"
#> [11] "2019-01-30" "2019-01-31" "2019-02-01" "2019-02-02" "2019-02-03"
#> [16] "2019-02-04" "2019-02-05"

# Grouping by 5 days of the current month
date_group(x, "day", n = 5)
#>  [1] "2019-01-16" "2019-01-21" "2019-01-21" "2019-01-21" "2019-01-21"
#>  [6] "2019-01-21" "2019-01-26" "2019-01-26" "2019-01-26" "2019-01-26"
#> [11] "2019-01-26" "2019-01-31" "2019-02-01" "2019-02-01" "2019-02-01"
#> [16] "2019-02-01" "2019-02-01"

The thing to note about grouping by day of the month is that at the end of each month, the groups restart. So this created groups for January of [1, 5], [6, 10], [11, 15], [16, 20], [21, 25], [26, 30], [31].

You can also group by month or year:

date_group(x, "month")
#>  [1] "2019-01-01" "2019-01-01" "2019-01-01" "2019-01-01" "2019-01-01"
#>  [6] "2019-01-01" "2019-01-01" "2019-01-01" "2019-01-01" "2019-01-01"
#> [11] "2019-01-01" "2019-01-01" "2019-02-01" "2019-02-01" "2019-02-01"
#> [16] "2019-02-01" "2019-02-01"

This also works with date-times, adding the ability to group by hour of the day, minute of the hour, and second of the minute.

x <- seq(
  date_time_build(2019, 1, 1, 1, 55, zone = "UTC"),
  date_time_build(2019, 1, 1, 2, 15, zone = "UTC"),
  by = 120
)
x
#>  [1] "2019-01-01 01:55:00 UTC" "2019-01-01 01:57:00 UTC"
#>  [3] "2019-01-01 01:59:00 UTC" "2019-01-01 02:01:00 UTC"
#>  [5] "2019-01-01 02:03:00 UTC" "2019-01-01 02:05:00 UTC"
#>  [7] "2019-01-01 02:07:00 UTC" "2019-01-01 02:09:00 UTC"
#>  [9] "2019-01-01 02:11:00 UTC" "2019-01-01 02:13:00 UTC"
#> [11] "2019-01-01 02:15:00 UTC"

date_group(x, "minute", n = 5)
#>  [1] "2019-01-01 01:55:00 UTC" "2019-01-01 01:55:00 UTC"
#>  [3] "2019-01-01 01:55:00 UTC" "2019-01-01 02:00:00 UTC"
#>  [5] "2019-01-01 02:00:00 UTC" "2019-01-01 02:05:00 UTC"
#>  [7] "2019-01-01 02:05:00 UTC" "2019-01-01 02:05:00 UTC"
#>  [9] "2019-01-01 02:10:00 UTC" "2019-01-01 02:10:00 UTC"
#> [11] "2019-01-01 02:15:00 UTC"

Rounding

While grouping is useful for summarizing within a component, rounding is useful for summarizing across components. It is great for summarizing by, say, a rolling set of 60 days.

Rounding operates on the underlying count that makes up your date or date-time. To see what I mean by this, try unclassing a date:

unclass(date_build(2020, 1, 1))
#> [1] 18262

This is a count of days since the origin that R uses, 1970-01-01, which is considered day 0. If you were to floor by 60 days, this would bundle [1970-01-01, 1970-03-02), [1970-03-02, 1970-05-01), and so on. Equivalently, it bundles counts of [0, 60), [60, 120), etc.

x <- seq(date_build(1970, 01, 01), date_build(1970, 05, 10), by = 20)

date_floor(x, "day", n = 60)
#> [1] "1970-01-01" "1970-01-01" "1970-01-01" "1970-03-02" "1970-03-02"
#> [6] "1970-03-02" "1970-05-01"
date_ceiling(x, "day", n = 60)
#> [1] "1970-01-01" "1970-03-02" "1970-03-02" "1970-03-02" "1970-05-01"
#> [6] "1970-05-01" "1970-05-01"

If you prefer a different origin, you can supply a Date origin to date_floor(), which determines what “day 0” is considered to be. This can be useful for grouping by multiple weeks if you want to control what is considered the start of the week. Since 1970-01-01 is a Thursday, flooring by 2 weeks would normally generate all Thursdays:

as_weekday(date_floor(x, "week", n = 14))
#> <weekday[7]>
#> [1] Thu Thu Thu Thu Thu Thu Thu

To change this you can supply an origin on the weekday that you’d like to be considered the first day of the week.

sunday <- date_build(1970, 01, 04)

date_floor(x, "week", n = 14, origin = sunday)
#> [1] "1969-09-28" "1970-01-04" "1970-01-04" "1970-01-04" "1970-01-04"
#> [6] "1970-01-04" "1970-04-12"

as_weekday(date_floor(x, "week", n = 14, origin = sunday))
#> <weekday[7]>
#> [1] Sun Sun Sun Sun Sun Sun Sun

If you only need to floor by 1 week, it is often easier to use date_shift(), as seen in the next section.

Shifting

date_shift() allows you to target a weekday, and then shift a vector of dates forward or backward to the next instance of that target. It requires using one of the new types in clock, weekday, which is supplied as the target.

For example, to shift to the next Tuesday:

x <- date_build(2020, 1, 1:2)

# Wednesday / Thursday
as_weekday(x)
#> <weekday[2]>
#> [1] Wed Thu

# `clock_weekdays` is a helper that returns the code corresponding to
# the requested day of the week
clock_weekdays$tuesday
#> [1] 3

tuesday <- weekday(clock_weekdays$tuesday)
tuesday
#> <weekday[1]>
#> [1] Tue

date_shift(x, target = tuesday)
#> [1] "2020-01-07" "2020-01-07"

Shifting to the previous day of the week is a nice way to floor by 1 week. It allows you to control the start of the week in a way that is slightly easier than using date_floor(origin = ).

x <- seq(date_build(1970, 01, 01), date_build(1970, 01, "last"), by = 3)

date_shift(x, tuesday, which = "previous")
#>  [1] "1969-12-30" "1969-12-30" "1970-01-06" "1970-01-06" "1970-01-13"
#>  [6] "1970-01-13" "1970-01-13" "1970-01-20" "1970-01-20" "1970-01-27"
#> [11] "1970-01-27"

Arithmetic

You can do arithmetic with dates and date-times using the family of add_*() functions. With dates, you can add years, months, and days. With date-times, you can additionally add hours, minutes, and seconds.

x <- date_build(2020, 1, 1)

add_years(x, 1:5)
#> [1] "2021-01-01" "2022-01-01" "2023-01-01" "2024-01-01" "2025-01-01"

One of the neat parts about clock is that it requires you to be explicit about how you want to handle invalid dates when doing arithmetic. What is 1 month after January 31st? If you try and create this date, you’ll get an error.

x <- date_build(2020, 1, 31)

add_months(x, 1)
#> Error in `invalid_resolve()`:
#> ! Invalid date found at location 1.
#> ℹ Resolve invalid date issues by specifying the `invalid` argument.

clock gives you the power to handle this through the invalid option:

# The previous valid moment in time
add_months(x, 1, invalid = "previous")
#> [1] "2020-02-29"

# The next valid moment in time
add_months(x, 1, invalid = "next")
#> [1] "2020-03-01"

# Overflow the days. There were 29 days in February, 2020, but we
# specified 31. So this overflows 2 days past day 29.
add_months(x, 1, invalid = "overflow")
#> [1] "2020-03-02"

# If you don't consider it to be a valid date
add_months(x, 1, invalid = "NA")
#> [1] NA

As a teaser, the low level library has a calendar type named year-month-day that powers this operation. It actually gives you more flexibility, allowing "2020-02-31" to exist in the wild:

ymd <- as_year_month_day(x) + duration_months(1)
ymd
#> <year_month_day<day>[1]>
#> [1] "2020-02-31"

You can use invalid_resolve(invalid =) to resolve this like you did in add_months(), or you can let it hang around if you expect other operations to make it “valid” again.

# Adding 1 more month makes it valid again
ymd + duration_months(1)
#> <year_month_day<day>[1]>
#> [1] "2020-03-31"

When working with date-times, you can additionally add hours, minutes, and seconds.

x <- date_time_build(2020, 1, 1, 2, 30, zone = "America/New_York")

x %>%
  add_days(1) %>%
  add_hours(2:5)
#> [1] "2020-01-02 04:30:00 EST" "2020-01-02 05:30:00 EST"
#> [3] "2020-01-02 06:30:00 EST" "2020-01-02 07:30:00 EST"

When adding units of time to a POSIXct, you have to be very careful with daylight saving time issues. clock tries to help you out by letting you know when you run into an issue:

x <- date_time_build(1970, 04, 25, 02, 30, 00, zone = "America/New_York")
x
#> [1] "1970-04-25 02:30:00 EST"

# Daylight saving time gap on the 26th between 01:59:59 -> 03:00:00
x %>% add_days(1)
#> Error in `as_zoned_time()`:
#> ! Nonexistent time due to daylight saving time at location 1.
#> ℹ Resolve nonexistent time issues by specifying the `nonexistent` argument.

You can solve this using the nonexistent argument to control how these times should be handled.

# Roll forward to the next valid moment in time
x %>% add_days(1, nonexistent = "roll-forward")
#> [1] "1970-04-26 03:00:00 EDT"

# Roll backward to the previous valid moment in time
x %>% add_days(1, nonexistent = "roll-backward")
#> [1] "1970-04-26 01:59:59 EST"

# Shift forward by adding the size of the DST gap
# (this often keeps the time of day,
# but doesn't guaratee that relative ordering in `x` is maintained
# so I don't recommend it)
x %>% add_days(1, nonexistent = "shift-forward")
#> [1] "1970-04-26 03:30:00 EDT"

# Replace nonexistent times with an NA
x %>% add_days(1, nonexistent = "NA")
#> [1] NA

Getting and setting

clock provides a family of getters and setters for working with dates and date-times. You can get and set the year, month, or day of a date.

x <- date_build(2019, 5, 6)

get_year(x)
#> [1] 2019
get_month(x)
#> [1] 5
get_day(x)
#> [1] 6

x %>%
  set_day(22) %>%
  set_month(10)
#> [1] "2019-10-22"

As you might expect by now, setting the date to an invalid date requires you to explicitly handle this:

x %>%
  set_day(31) %>%
  set_month(4)
#> Error in `invalid_resolve()`:
#> ! Invalid date found at location 1.
#> ℹ Resolve invalid date issues by specifying the `invalid` argument.

x %>%
  set_day(31) %>%
  set_month(4, invalid = "previous")
#> [1] "2019-04-30"

You can additionally set the hour, minute, and second of a POSIXct.

x <- date_time_build(2020, 1, 2, 3, zone = "America/New_York")
x
#> [1] "2020-01-02 03:00:00 EST"

x %>%
  set_minute(5) %>%
  set_second(10)
#> [1] "2020-01-02 03:05:10 EST"

As with other manipulations of POSIXct, you’ll have to be aware of daylight saving time when setting components. You may need to supply the nonexistent or ambiguous arguments of the set_*() functions to handle these issues.

clock/inst/doc/clock.R0000644000176200001440000002001014430471260014277 0ustar liggesusers## ---- include = FALSE--------------------------------------------------------- knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ## ----setup-------------------------------------------------------------------- library(clock) library(magrittr) ## ----------------------------------------------------------------------------- date_build(2019, 2, 1:5) ## ---- error=TRUE-------------------------------------------------------------- date_build(2019, 1:12, 31) ## ----------------------------------------------------------------------------- date_build(2019, 1:12, 31, invalid = "previous") ## ----------------------------------------------------------------------------- date_build(2019, 1:12, "last") ## ----------------------------------------------------------------------------- date_time_build(2019, 1:5, 1, 2, 30, zone = "America/New_York") ## ---- error=TRUE-------------------------------------------------------------- date_time_build(2019:2021, 3, 8, 2, 30, zone = "America/New_York") ## ----------------------------------------------------------------------------- zone <- "America/New_York" date_time_build(2019:2021, 3, 8, 2, 30, zone = zone, nonexistent = "roll-forward") date_time_build(2019:2021, 3, 8, 2, 30, zone = zone, nonexistent = "roll-backward") ## ----------------------------------------------------------------------------- date_parse("2019-01-05") ## ----------------------------------------------------------------------------- date_parse("January 5, 2020", format = "%B %d, %Y") ## ----------------------------------------------------------------------------- date_parse( "juillet 10, 2021", format = "%B %d, %Y", locale = clock_locale("fr") ) ## ----------------------------------------------------------------------------- x <- c("2020/1/5", "10-03-05", "2020/2/2") formats <- c("%Y/%m/%d", "%y-%m-%d") date_parse(x, format = formats) ## ----------------------------------------------------------------------------- date_time_parse("2020-01-01 01:02:03", "America/New_York") ## ----------------------------------------------------------------------------- before <- date_time_parse("2020-11-01 00:59:59", "America/New_York") # First 1 o'clock before + 1 # Second 1 o'clock before + 1 + 3600 ## ---- error=TRUE-------------------------------------------------------------- date_time_parse("2020-11-01 01:30:00", "America/New_York") ## ----------------------------------------------------------------------------- zone <- "America/New_York" date_time_parse("2020-11-01 01:30:00", zone, ambiguous = "earliest") date_time_parse("2020-11-01 01:30:00", zone, ambiguous = "latest") ## ----------------------------------------------------------------------------- x <- "2020-01-01T01:02:03-05:00[America/New_York]" date_time_parse_complete(x) ## ----------------------------------------------------------------------------- x <- "2020-01-01 01:02:03 EST" date_time_parse_abbrev(x, "America/New_York") ## ----------------------------------------------------------------------------- x <- c( "1970-10-25 01:30:00 EDT", "1970-10-25 01:30:00 EST" ) date_time_parse_abbrev(x, "America/New_York") ## ----------------------------------------------------------------------------- x <- "1970-01-01 02:30:30 IST" # IST = India Standard Time date_time_parse_abbrev(x, "Asia/Kolkata") # IST = Israel Standard Time date_time_parse_abbrev(x, "Asia/Jerusalem") ## ----------------------------------------------------------------------------- x <- "2020-01-01T01:02:03Z" date_time_parse_RFC_3339(x) ## ----------------------------------------------------------------------------- x <- "2020-01-01T01:02:03-0500" date_time_parse_RFC_3339(x, offset = "%z") x <- "2020-01-01T01:02:03-05:00" date_time_parse_RFC_3339(x, offset = "%Ez") ## ----------------------------------------------------------------------------- x <- seq(date_build(2019, 1, 20), date_build(2019, 2, 5), by = 1) x # Grouping by 5 days of the current month date_group(x, "day", n = 5) ## ----------------------------------------------------------------------------- date_group(x, "month") ## ----------------------------------------------------------------------------- x <- seq( date_time_build(2019, 1, 1, 1, 55, zone = "UTC"), date_time_build(2019, 1, 1, 2, 15, zone = "UTC"), by = 120 ) x date_group(x, "minute", n = 5) ## ----------------------------------------------------------------------------- unclass(date_build(2020, 1, 1)) ## ----------------------------------------------------------------------------- x <- seq(date_build(1970, 01, 01), date_build(1970, 05, 10), by = 20) date_floor(x, "day", n = 60) date_ceiling(x, "day", n = 60) ## ----------------------------------------------------------------------------- as_weekday(date_floor(x, "week", n = 14)) ## ----------------------------------------------------------------------------- sunday <- date_build(1970, 01, 04) date_floor(x, "week", n = 14, origin = sunday) as_weekday(date_floor(x, "week", n = 14, origin = sunday)) ## ----------------------------------------------------------------------------- x <- date_build(2020, 1, 1:2) # Wednesday / Thursday as_weekday(x) # `clock_weekdays` is a helper that returns the code corresponding to # the requested day of the week clock_weekdays$tuesday tuesday <- weekday(clock_weekdays$tuesday) tuesday date_shift(x, target = tuesday) ## ----------------------------------------------------------------------------- x <- seq(date_build(1970, 01, 01), date_build(1970, 01, "last"), by = 3) date_shift(x, tuesday, which = "previous") ## ----------------------------------------------------------------------------- x <- date_build(2020, 1, 1) add_years(x, 1:5) ## ---- error=TRUE-------------------------------------------------------------- x <- date_build(2020, 1, 31) add_months(x, 1) ## ----------------------------------------------------------------------------- # The previous valid moment in time add_months(x, 1, invalid = "previous") # The next valid moment in time add_months(x, 1, invalid = "next") # Overflow the days. There were 29 days in February, 2020, but we # specified 31. So this overflows 2 days past day 29. add_months(x, 1, invalid = "overflow") # If you don't consider it to be a valid date add_months(x, 1, invalid = "NA") ## ----------------------------------------------------------------------------- ymd <- as_year_month_day(x) + duration_months(1) ymd ## ----------------------------------------------------------------------------- # Adding 1 more month makes it valid again ymd + duration_months(1) ## ----------------------------------------------------------------------------- x <- date_time_build(2020, 1, 1, 2, 30, zone = "America/New_York") x %>% add_days(1) %>% add_hours(2:5) ## ---- error=TRUE-------------------------------------------------------------- x <- date_time_build(1970, 04, 25, 02, 30, 00, zone = "America/New_York") x # Daylight saving time gap on the 26th between 01:59:59 -> 03:00:00 x %>% add_days(1) ## ----------------------------------------------------------------------------- # Roll forward to the next valid moment in time x %>% add_days(1, nonexistent = "roll-forward") # Roll backward to the previous valid moment in time x %>% add_days(1, nonexistent = "roll-backward") # Shift forward by adding the size of the DST gap # (this often keeps the time of day, # but doesn't guaratee that relative ordering in `x` is maintained # so I don't recommend it) x %>% add_days(1, nonexistent = "shift-forward") # Replace nonexistent times with an NA x %>% add_days(1, nonexistent = "NA") ## ----------------------------------------------------------------------------- x <- date_build(2019, 5, 6) get_year(x) get_month(x) get_day(x) x %>% set_day(22) %>% set_month(10) ## ---- error=TRUE-------------------------------------------------------------- x %>% set_day(31) %>% set_month(4) x %>% set_day(31) %>% set_month(4, invalid = "previous") ## ----------------------------------------------------------------------------- x <- date_time_build(2020, 1, 2, 3, zone = "America/New_York") x x %>% set_minute(5) %>% set_second(10) clock/inst/doc/faq.html0000644000176200001440000017216514430471262014543 0ustar liggesusers Frequently Asked Questions

Frequently Asked Questions

library(clock)
library(magrittr)

Why can’t I do day arithmetic on a year-month-day?

It might seem intuitive that since you can do:

x <- year_month_day(2019, 1, 5)

add_months(x, 1)
#> <year_month_day<day>[1]>
#> [1] "2019-02-05"

That you should also be able to do:

add_days(x, 1)
#> Error in `add_days()`:
#> ! Can't perform this operation on a <clock_year_month_day>.
#> ℹ Do you need to convert to a time point first?
#> ℹ Use `as_naive_time()` or `as_sys_time()` to convert to a time point.

Generally, calendars don’t support day based arithmetic, nor do they support arithmetic at more precise precisions than day. Instead, you have to convert to a time point, do the arithmetic there, and then convert back (if you still need a year-month-day after that).

x %>%
  as_naive_time() %>%
  add_days(1) %>%
  as_year_month_day()
#> <year_month_day<day>[1]>
#> [1] "2019-01-06"

The first reason for this is performance. A year-month-day is a field type, implemented as multiple parallel vectors holding the year, month, day, and all other components separately. There are two ways that day based arithmetic could be implemented for this:

  • Increment the day field, then check the year and month field to see if they need to be incremented, accounting for months having a differing number of days, and leap years.

  • Convert to naive-time, add days, convert back.

Both approaches are relatively expensive. One of the goals of the low-level API of clock is to make these expensive operations explicit. This helps make it apparent that when you need to chain together multiple operations, you should try and do all of your calendrical arithmetic steps first, then convert to a time point (i.e. the second bullet point from above) to do all of your chronological arithmetic.

The second reason for this has to do with invalid dates, such as the three in this vector:

odd_dates <- year_month_day(2019, 2, 28:31)
odd_dates
#> <year_month_day<day>[4]>
#> [1] "2019-02-28" "2019-02-29" "2019-02-30" "2019-02-31"

What does it mean to “add 1 day” to these? There is no obvious answer to this question. Since clock requires that you first convert to a time point to do day based arithmetic, you’ll be forced to call invalid_resolve() to handle these invalid dates first. After resolving them manually, then day based arithmetic again makes sense.

odd_dates %>%
  invalid_resolve(invalid = "next")
#> <year_month_day<day>[4]>
#> [1] "2019-02-28" "2019-03-01" "2019-03-01" "2019-03-01"

odd_dates %>%
  invalid_resolve(invalid = "next") %>%
  as_naive_time() %>%
  add_days(2)
#> <naive_time<day>[4]>
#> [1] "2019-03-02" "2019-03-03" "2019-03-03" "2019-03-03"

odd_dates %>%
  invalid_resolve(invalid = "overflow")
#> <year_month_day<day>[4]>
#> [1] "2019-02-28" "2019-03-01" "2019-03-02" "2019-03-03"

odd_dates %>%
  invalid_resolve(invalid = "overflow") %>%
  as_naive_time() %>%
  add_days(2)
#> <naive_time<day>[4]>
#> [1] "2019-03-02" "2019-03-03" "2019-03-04" "2019-03-05"

Why can’t I add time to a zoned-time?

If you have a zoned-time, such as:

x <- zoned_time_parse_complete("1970-04-26T01:30:00-05:00[America/New_York]")
x
#> <zoned_time<second><America/New_York>[1]>
#> [1] "1970-04-26T01:30:00-05:00"

You might wonder why you can’t add any units of time to it:

add_days(x, 1)
#> Error in `add_days()`:
#> ! Can't perform this operation on a <clock_zoned_time>.
#> ℹ Do you need to convert to a time point first?
#> ℹ Use `as_naive_time()` or `as_sys_time()` to convert to a time point.

add_seconds(x, 1)
#> Error in `add_seconds()`:
#> ! Can't perform this operation on a <clock_zoned_time>.
#> ℹ Do you need to convert to a time point first?
#> ℹ Use `as_naive_time()` or `as_sys_time()` to convert to a time point.

In clock, you can’t do much with zoned-times directly. The best way to understand this is to think of a zoned-time as containing 3 things: a sys-time, a naive-time, and a time zone name. You can access those things with:

x
#> <zoned_time<second><America/New_York>[1]>
#> [1] "1970-04-26T01:30:00-05:00"

# The printed time with no time zone info
as_naive_time(x)
#> <naive_time<second>[1]>
#> [1] "1970-04-26T01:30:00"

# The equivalent time in UTC
as_sys_time(x)
#> <sys_time<second>[1]>
#> [1] "1970-04-26T06:30:00"

zoned_time_zone(x)
#> [1] "America/New_York"

Calling add_days() on a zoned-time is then an ambiguous operation. Should we add to the sys-time or the naive-time that is contained in the zoned-time? The answer changes depending on the scenario.

Because of this, you have to extract out the relevant time point that you care about, operate on that, and then convert back to zoned-time. This often produces the same result:

x %>%
  as_naive_time() %>%
  add_seconds(1) %>%
  as_zoned_time(zoned_time_zone(x))
#> <zoned_time<second><America/New_York>[1]>
#> [1] "1970-04-26T01:30:01-05:00"

x %>%
  as_sys_time() %>%
  add_seconds(1) %>%
  as_zoned_time(zoned_time_zone(x))
#> <zoned_time<second><America/New_York>[1]>
#> [1] "1970-04-26T01:30:01-05:00"

But not always! When daylight saving time is involved, the choice of sys-time or naive-time matters. Let’s try adding 30 minutes:

# There is a DST gap 1 second after 01:59:59,
# which jumps us straight to 03:00:00,
# skipping the 2 o'clock hour entirely

x %>%
  as_naive_time() %>%
  add_minutes(30) %>%
  as_zoned_time(zoned_time_zone(x))
#> Error in `as_zoned_time()`:
#> ! Nonexistent time due to daylight saving time at location 1.
#> ℹ Resolve nonexistent time issues by specifying the `nonexistent` argument.

x %>%
  as_sys_time() %>%
  add_minutes(30) %>%
  as_zoned_time(zoned_time_zone(x))
#> <zoned_time<second><America/New_York>[1]>
#> [1] "1970-04-26T03:00:00-04:00"

When adding to the naive-time, we got an error. With the sys-time, everything seems okay. What happened?

The sys-time scenario is easy to explain. Technically this converts to UTC, adds the time there, then converts back to your time zone. An easier way to think about this is that you sat in front of your computer for exactly 30 minutes (1800 seconds), then looked at the clock. Assuming that that clock automatically changes itself correctly for daylight saving time, it should read 3 o’clock.

The naive-time scenario makes more sense if you break down the steps. First, we convert to naive-time, dropping all time zone information but keeping the printed time:

x
#> <zoned_time<second><America/New_York>[1]>
#> [1] "1970-04-26T01:30:00-05:00"

x %>%
  as_naive_time()
#> <naive_time<second>[1]>
#> [1] "1970-04-26T01:30:00"

We add 30 minutes to this. Because we don’t have any time zone information, this lands us at 2 o’clock, which isn’t an issue when working with naive-time:

x %>%
  as_naive_time() %>%
  add_minutes(30)
#> <naive_time<second>[1]>
#> [1] "1970-04-26T02:00:00"

Finally, we convert back to zoned-time. If possible, this tries to keep the printed time, and just attaches the relevant time zone onto it. However, in this case that isn’t possible, since 2 o’clock didn’t exist in this time zone! This nonexistent time must be handled explicitly by setting the nonexistent argument of as_zoned_time(). We can choose from a variety of strategies to handle nonexistent times, but here we just roll forward to the next valid moment in time.

x %>%
  as_naive_time() %>%
  add_minutes(30) %>%
  as_zoned_time(zoned_time_zone(x), nonexistent = "roll-forward")
#> <zoned_time<second><America/New_York>[1]>
#> [1] "1970-04-26T03:00:00-04:00"

As a general rule, it often makes the most sense to add:

  • Years, quarters, and months to a calendar.

  • Weeks and days to a naive time.

  • Hours, minutes, seconds, and subseconds to a sys time.

This is what the high-level API for POSIXct does. However, this isn’t always what you want, so the low-level API requires you to be more explicit.

Where did my POSIXct subseconds go?

old <- options(digits.secs = 6, digits = 22)

Consider the following POSIXct:

x <- as.POSIXct("2019-01-01 01:00:00.2", "America/New_York")
x
#> [1] "2019-01-01 01:00:00.2 EST"

It looks like there is some fractional second information here, but converting it to naive-time drops it:

as_naive_time(x)
#> <naive_time<second>[1]>
#> [1] "2019-01-01T01:00:00"

This is purposeful. clock treats POSIXct as a second precision data type. The reason for this has to do with the fact that POSIXct is implemented as a vector of doubles, which have a limit to how precisely they can store information. For example, try parsing a slightly smaller or larger fractional second:

y <- as.POSIXct(
  c("2019-01-01 01:00:00.1", "2019-01-01 01:00:00.3"), 
  "America/New_York"
)

# Oh dear!
y
#> [1] "2019-01-01 01:00:00.0 EST" "2019-01-01 01:00:00.2 EST"

It isn’t printing correctly, at the very least. Let’s look under the hood:

unclass(y)
#> [1] 1546322400.099999904633 1546322400.299999952316
#> attr(,"tzone")
#> [1] "America/New_York"

Double vectors have a limit to how much precision they can represent, and this is bumping up against that limit. So our .1 seconds is instead represented as .099999etc.

This precision loss gets worse the farther we get from the epoch, 1970-01-01, represented as 0 under the hood. For example, here we’ll use a number of seconds that represents the year 2050, and add 5 microseconds to it:

new_utc <- function(x) {
  class(x) <- c("POSIXct", "POSIXt")
  attr(x, "tzone") <- "UTC"
  x
}

year_2050 <- 2524608000
five_microseconds <- 0.000005

new_utc(year_2050)
#> [1] "2050-01-01 UTC"

# Oh no!
new_utc(year_2050 + five_microseconds)
#> [1] "2050-01-01 00:00:00.000004 UTC"

# Represented internally as:
year_2050 + five_microseconds
#> [1] 2524608000.000004768372

Because of these issues, clock treats POSIXct as a second precision data type, dropping all other information. Instead, you should parse directly into a subsecond clock type:

naive_time_parse(
  c("2019-01-01T01:00:00.1", "2019-01-01T01:00:00.3"), 
  precision = "millisecond"
) %>%
  as_zoned_time("America/New_York")
#> <zoned_time<millisecond><America/New_York>[2]>
#> [1] "2019-01-01T01:00:00.100-05:00" "2019-01-01T01:00:00.300-05:00"
# Reset old options
options(old)

What is the time zone of Date?

In clock, R’s native Date type is actually assumed to be naive, i.e. clock assumes that there is a yet-to-be-specified time zone, like with a naive-time. The other possibility is to assume that Date is UTC (like sys-time), but it is often more intuitive for Dates to be naive when manipulating them and converting them to zoned-time or POSIXct.

R does not consistently treat Dates as naive or UTC. Instead it switches between them, depending on the function.

For example, the Date method of as.POSIXct() does not expose a tz argument. Instead, it assumes that Date is UTC, and that the result should be shown in local time (as defined by Sys.timezone()). This often results in confusing behavior, such as:

x <- as.Date("2019-01-01")
x
#> [1] "2019-01-01"

withr::with_timezone("America/New_York", {
  print(as.POSIXct(x))
})
#> [1] "2018-12-31 19:00:00 EST"

With clock, converting to zoned-time from Date will always assume that Date is naive, which will keep the printed date (if possible) and show it in the zone you specified.

as_zoned_time(x, "UTC")
#> <zoned_time<second><UTC>[1]>
#> [1] "2019-01-01T00:00:00+00:00"

as_zoned_time(x, "America/New_York")
#> <zoned_time<second><America/New_York>[1]>
#> [1] "2019-01-01T00:00:00-05:00"

as_zoned_time(x, "Europe/London")
#> <zoned_time<second><Europe/London>[1]>
#> [1] "2019-01-01T00:00:00+00:00"

On the other hand, the POSIXct method for as.Date() treats Date as a naive type. This is probably what you want, and this example just shows the inconsistency. It is a bit hard to see this, because the tz argument of the method defaults to "UTC", but if you set the tz argument to the zone of your input, it becomes clear:

x <- as.POSIXct("2019-01-01 23:00:00", "America/New_York")

as.Date(x, tz = date_time_zone(x))
#> [1] "2019-01-01"

If this assumed that Date was UTC, then it would have resulted in something like:

utc <- date_time_set_zone(x, "UTC")
utc
#> [1] "2019-01-02 04:00:00 UTC"

as.Date(utc, tz = date_time_zone(utc))
#> [1] "2019-01-02"

What does clock do with leap seconds?

clock currently handles leap seconds in the same way that base R’s date-time (POSIXct) class does - it ignores them entirely. While strptime() has some very simple capabilities for parsing leap seconds, clock doesn’t allow them at all:

raw <- c(
  "2015-12-31T23:59:59", 
  "2015-12-31T23:59:60", # A real leap second!
  "2016-01-01T00:00:00"
)

x <- sys_time_parse(raw)
#> Warning: Failed to parse 1 string at location 2. Returning `NA` at that
#> location.

x
#> <sys_time<second>[3]>
#> [1] "2015-12-31T23:59:59" NA                    "2016-01-01T00:00:00"
# Reported as exactly 1 second apart.
# In real life these are 2 seconds apart because of the leap second.
x[[3]] - x[[1]]
#> <duration<second>[1]>
#> [1] 1

Because none of the clock types handle leap seconds, clock currently doesn’t offer a way to parse them. Your current best option if you really need to parse leap seconds is to use strptime():

# This returns a POSIXlt, which can handle the special 60s field
x <- strptime(raw, format = "%Y-%m-%dT%H:%M:%S", tz = "UTC")
x
#> [1] "2015-12-31 23:59:59 UTC" "2015-12-31 23:59:60 UTC"
#> [3] "2016-01-01 00:00:00 UTC"

# On conversion to POSIXct, it "rolls" forward
as.POSIXct(x)
#> [1] "2015-12-31 23:59:59 UTC" "2016-01-01 00:00:00 UTC"
#> [3] "2016-01-01 00:00:00 UTC"

strptime() isn’t a great solution though, because the parsing is fairly simple. If you try to use a “fake” leap second, it will still accept it, even though it isn’t a real time:

# 2016-12-31 wasn't a leap second date, but it still tries to parse this fake time
strptime("2016-12-31T23:59:60", format = "%Y-%m-%dT%H:%M:%S", tz = "UTC")
#> [1] "2016-12-31 23:59:60 UTC"

A true solution would check this against a database of actual leap seconds, and would only successfully parse it if it matched a real leap second. The C++ library that powers clock does have this capability, through a utc_clock class, and we may expose this in a limited form in the future, with conversion to and from sys-time and naive-time.

Why doesn’t this work with data.table?

While the entire high-level API for R’s native date (Date) and date-time (POSIXct) types will work fine with data.table, if you try to put any of the major clock types into a data.table, you will probably see this error message:

library(data.table)

data.table(x = year_month_day(2019, 1, 1))
#> Error in dimnames(x) <- dn : 
#>   length of 'dimnames' [1] not equal to array extent

You won’t see this issue when working with data.frames or tibbles.

As of now, data.table doesn’t support the concept of record types. These are implemented as a list of vectors of equal length, that together represent a single idea. The length() of these types should be taken from the length of the vectors, not the length of the list. If you unclass any of the clock types, you’ll see that they are implemented in this way:

ymdh <- year_month_day(2019, 1, 1:2, 1)

unclass(ymdh)
#> $year
#> [1] 2019 2019
#> 
#> $month
#> [1] 1 1
#> 
#> $day
#> [1] 1 2
#> 
#> $hour
#> [1] 1 1
#> 
#> attr(,"precision")
#> [1] 5

unclass(as_naive_time(ymdh))
#> $lower
#> [1] 2147483648 2147483648
#> 
#> $upper
#> [1] 429529 429553
#> 
#> attr(,"precision")
#> [1] 5
#> attr(,"clock")
#> [1] 1

I find that record types are extremely useful data structures for building upon R’s basic atomic types in ways that otherwise couldn’t be done. They allow calendar types to hold information about each component, enabling instant access for retrieval, modification, and grouping. They also allow calendars to represent invalid dates, such as 2019-02-31, without any issues. Time points use them to store up to nanosecond precision date-times, which are really C++ int64_t types that don’t nicely fit into any R atomic type (I am aware of the bit64 package, and made a conscious decision to implement as a record type instead. This partly had to do with how missing values are handled, and how that integrates with vctrs).

The idea of a record type actually isn’t new. R’s own POSIXlt type is a record type:

x <- as.POSIXct("2019-01-01", "America/New_York")

# POSIXct is implemented as a double
unclass(x)
#> [1] 1546318800
#> attr(,"tzone")
#> [1] "America/New_York"

# POSIXlt is a record type
unclass(as.POSIXlt(x))
#> $sec
#> [1] 0
#> 
#> $min
#> [1] 0
#> 
#> $hour
#> [1] 0
#> 
#> $mday
#> [1] 1
#> 
#> $mon
#> [1] 0
#> 
#> $year
#> [1] 119
#> 
#> $wday
#> [1] 2
#> 
#> $yday
#> [1] 0
#> 
#> $isdst
#> [1] 0
#> 
#> $zone
#> [1] "EST"
#> 
#> $gmtoff
#> [1] -18000
#> 
#> attr(,"tzone")
#> [1] "America/New_York" "EST"              "EDT"

data.table doesn’t truly support POSIXlt either. Instead, you get a warning about them converting it to a POSIXct. This is pretty reasonable considering their focus on performance.

data.table(x = as.POSIXlt("2019-01-01", "America/New_York"))
#>             x
#> 1: 2019-01-01
#> Warning message:
#> In as.data.table.list(x, keep.rownames = keep.rownames, check.names = check.names,  :
#>   POSIXlt column type detected and converted to POSIXct. We do not recommend use of POSIXlt at all because it uses 40 bytes to store one date.

It was previously a bit difficult to create record types in R because there were few examples and no resources to build on. In vctrs, we’ve added a vctrs_rcrd type that serves as a base to build new record types on. Many S3 methods have been written for vctrs_rcrds in a way that should work for any type that builds on top of it, giving you a lot of scaffolding for free.

I am hopeful that as more record types make their way into the R ecosystem built on this common foundation, it might be possible for data.table to enable this as an approved type in their package.

clock/inst/doc/faq.R0000644000176200001440000001520514430471261013766 0ustar liggesusers## ---- include = FALSE--------------------------------------------------------- knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ## ----setup-------------------------------------------------------------------- library(clock) library(magrittr) ## ----------------------------------------------------------------------------- x <- year_month_day(2019, 1, 5) add_months(x, 1) ## ---- error=TRUE-------------------------------------------------------------- add_days(x, 1) ## ----------------------------------------------------------------------------- x %>% as_naive_time() %>% add_days(1) %>% as_year_month_day() ## ----------------------------------------------------------------------------- odd_dates <- year_month_day(2019, 2, 28:31) odd_dates ## ----------------------------------------------------------------------------- odd_dates %>% invalid_resolve(invalid = "next") odd_dates %>% invalid_resolve(invalid = "next") %>% as_naive_time() %>% add_days(2) odd_dates %>% invalid_resolve(invalid = "overflow") odd_dates %>% invalid_resolve(invalid = "overflow") %>% as_naive_time() %>% add_days(2) ## ----------------------------------------------------------------------------- x <- zoned_time_parse_complete("1970-04-26T01:30:00-05:00[America/New_York]") x ## ---- error=TRUE-------------------------------------------------------------- add_days(x, 1) add_seconds(x, 1) ## ----------------------------------------------------------------------------- x # The printed time with no time zone info as_naive_time(x) # The equivalent time in UTC as_sys_time(x) zoned_time_zone(x) ## ----------------------------------------------------------------------------- x %>% as_naive_time() %>% add_seconds(1) %>% as_zoned_time(zoned_time_zone(x)) x %>% as_sys_time() %>% add_seconds(1) %>% as_zoned_time(zoned_time_zone(x)) ## ---- error=TRUE-------------------------------------------------------------- # There is a DST gap 1 second after 01:59:59, # which jumps us straight to 03:00:00, # skipping the 2 o'clock hour entirely x %>% as_naive_time() %>% add_minutes(30) %>% as_zoned_time(zoned_time_zone(x)) x %>% as_sys_time() %>% add_minutes(30) %>% as_zoned_time(zoned_time_zone(x)) ## ----------------------------------------------------------------------------- x x %>% as_naive_time() ## ----------------------------------------------------------------------------- x %>% as_naive_time() %>% add_minutes(30) ## ----------------------------------------------------------------------------- x %>% as_naive_time() %>% add_minutes(30) %>% as_zoned_time(zoned_time_zone(x), nonexistent = "roll-forward") ## ----------------------------------------------------------------------------- old <- options(digits.secs = 6, digits = 22) ## ----------------------------------------------------------------------------- x <- as.POSIXct("2019-01-01 01:00:00.2", "America/New_York") x ## ----------------------------------------------------------------------------- as_naive_time(x) ## ----------------------------------------------------------------------------- y <- as.POSIXct( c("2019-01-01 01:00:00.1", "2019-01-01 01:00:00.3"), "America/New_York" ) # Oh dear! y ## ----------------------------------------------------------------------------- unclass(y) ## ----------------------------------------------------------------------------- new_utc <- function(x) { class(x) <- c("POSIXct", "POSIXt") attr(x, "tzone") <- "UTC" x } year_2050 <- 2524608000 five_microseconds <- 0.000005 new_utc(year_2050) # Oh no! new_utc(year_2050 + five_microseconds) # Represented internally as: year_2050 + five_microseconds ## ----------------------------------------------------------------------------- naive_time_parse( c("2019-01-01T01:00:00.1", "2019-01-01T01:00:00.3"), precision = "millisecond" ) %>% as_zoned_time("America/New_York") ## ----------------------------------------------------------------------------- # Reset old options options(old) ## ----------------------------------------------------------------------------- x <- as.Date("2019-01-01") x withr::with_timezone("America/New_York", { print(as.POSIXct(x)) }) ## ----------------------------------------------------------------------------- as_zoned_time(x, "UTC") as_zoned_time(x, "America/New_York") as_zoned_time(x, "Europe/London") ## ----------------------------------------------------------------------------- x <- as.POSIXct("2019-01-01 23:00:00", "America/New_York") as.Date(x, tz = date_time_zone(x)) ## ----------------------------------------------------------------------------- utc <- date_time_set_zone(x, "UTC") utc as.Date(utc, tz = date_time_zone(utc)) ## ---- warning=TRUE------------------------------------------------------------ raw <- c( "2015-12-31T23:59:59", "2015-12-31T23:59:60", # A real leap second! "2016-01-01T00:00:00" ) x <- sys_time_parse(raw) x ## ----------------------------------------------------------------------------- # Reported as exactly 1 second apart. # In real life these are 2 seconds apart because of the leap second. x[[3]] - x[[1]] ## ----------------------------------------------------------------------------- # This returns a POSIXlt, which can handle the special 60s field x <- strptime(raw, format = "%Y-%m-%dT%H:%M:%S", tz = "UTC") x # On conversion to POSIXct, it "rolls" forward as.POSIXct(x) ## ----------------------------------------------------------------------------- # 2016-12-31 wasn't a leap second date, but it still tries to parse this fake time strptime("2016-12-31T23:59:60", format = "%Y-%m-%dT%H:%M:%S", tz = "UTC") ## ---- eval=FALSE-------------------------------------------------------------- # library(data.table) # # data.table(x = year_month_day(2019, 1, 1)) # #> Error in dimnames(x) <- dn : # #> length of 'dimnames' [1] not equal to array extent ## ----------------------------------------------------------------------------- ymdh <- year_month_day(2019, 1, 1:2, 1) unclass(ymdh) unclass(as_naive_time(ymdh)) ## ----------------------------------------------------------------------------- x <- as.POSIXct("2019-01-01", "America/New_York") # POSIXct is implemented as a double unclass(x) # POSIXlt is a record type unclass(as.POSIXlt(x)) ## ---- eval=FALSE-------------------------------------------------------------- # data.table(x = as.POSIXlt("2019-01-01", "America/New_York")) # #> x # #> 1: 2019-01-01 # #> Warning message: # #> In as.data.table.list(x, keep.rownames = keep.rownames, check.names = check.names, : # #> POSIXlt column type detected and converted to POSIXct. We do not recommend use of POSIXlt at all because it uses 40 bytes to store one date.