├── .github └── workflows │ └── ci.yml ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── benches └── bench.rs ├── src ├── date.rs ├── datetime.rs ├── error.rs ├── lib.rs ├── sys │ ├── mod.rs │ ├── stub.rs │ ├── unix.rs │ └── windows.rs └── time.rs └── tests ├── date.rs ├── datetime.rs ├── error.rs ├── offset.rs └── time.rs /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | test: 14 | name: Test ${{ matrix.rust }} on ${{ matrix.os }} 15 | runs-on: ${{ matrix.os }} 16 | strategy: 17 | matrix: 18 | rust: 19 | - stable 20 | - nightly 21 | os: 22 | - ubuntu-latest 23 | steps: 24 | - name: Checkout sources 25 | uses: actions/checkout@v2 26 | - name: Install Rust (${{ matrix.rust }}) 27 | uses: actions-rs/toolchain@v1 28 | with: 29 | profile: minimal 30 | toolchain: ${{ matrix.rust }} 31 | override: true 32 | - name: Run cargo test 33 | uses: actions-rs/cargo@v1 34 | with: 35 | command: test 36 | - name: Run cargo tarpaulin 37 | run: | 38 | cargo install cargo-tarpaulin 39 | cargo tarpaulin --out xml 40 | - name: Upload coverage reports to Codecov 41 | uses: codecov/codecov-action@v3 42 | env: 43 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 44 | with: 45 | file: cobertura.xml 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | 12 | *.lock 13 | /.idea 14 | *.iml 15 | /.vscode 16 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fastdate" 3 | version = "0.3.36" 4 | edition = "2024" 5 | description = "Rust fast date carte" 6 | readme = "README.md" 7 | authors = ["ce "] 8 | license = "Apache-2.0" 9 | repository = "https://github.com/rbatis/fastdate" 10 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 11 | [dependencies] 12 | serde = "1" 13 | time1 = { version = "0.3.41", package = "time",features = ["formatting","parsing","serde"] } 14 | [dev-dependencies] 15 | serde_json = "1" 16 | 17 | [target.'cfg(unix)'.dependencies] 18 | libc = "0.2" 19 | 20 | [target.'cfg(windows)'.dependencies.windows-sys] 21 | version = "0.52.0" 22 | features = [ 23 | "Win32_Foundation", 24 | "Win32_System_Time", 25 | ] 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fastdate 2 | 3 | [![codecov](https://codecov.io/gh/rbatis/fastdate/graph/badge.svg?token=C97H2QBHAQ)](https://codecov.io/gh/rbatis/fastdate) 4 | 5 | 6 | 7 | fastdate of Any RFC3339Micro 8 | 9 | 10 | ## way fastdate? 11 | * full test, Code testing coverage >= 99% 12 | * Powerful, easy to use 13 | * based on crate `time` 14 | 15 | this date cartes is very fast(<= 50ns) including 16 | * offset_sec() 17 | * from_str("2022-12-13 11:12:14.123456") 18 | * now() 19 | * utc() 20 | * week_day() 21 | * to_string() 22 | * eq(),== 23 | * add(),sub() 24 | * format("YYYY-MM-DD hh:mm:ss.000000") 25 | * parse("YYYY-MM-DD,hh:mm:ss.000000","2022-12-13,11:12:14.123456") 26 | * set_offset() 27 | * unix_timestamp() 28 | * unix_timestamp_millis() 29 | * unix_timestamp_nano() 30 | * from_timestamp() 31 | * from_timestamp_millis() 32 | * from_timestamp_nano() 33 | * before(&date) -> bool 34 | * after(&date1) -> bool 35 | * from(v: SystemTime) 36 | * from(v: DateTime) 37 | * from(arg: Date) 38 | * from(arg: Time) 39 | * cmp(&self, other: &DateTime)/>/=/<= and more.... 40 | 41 | ```log 42 | test bench_add ... bench: 14 ns/iter (+/- 0) 43 | test bench_date_display ... bench: 49 ns/iter (+/- 2) 44 | test bench_date_from_str_iso_8601 ... bench: 129 ns/iter (+/- 6) 45 | test bench_date_from_str_iso_8601_time ... bench: 69 ns/iter (+/- 2) 46 | test bench_date_now_local ... bench: 65 ns/iter (+/- 2) 47 | test bench_date_parse_format ... bench: 282 ns/iter (+/- 11) 48 | test bench_date_utc ... bench: 49 ns/iter (+/- 2) 49 | test bench_datetime_from_str ... bench: 130 ns/iter (+/- 10) 50 | test bench_eq ... bench: 10 ns/iter (+/- 0) 51 | test bench_from_timestamp_millis ... bench: 23 ns/iter (+/- 0) 52 | test bench_get_micro ... bench: 0 ns/iter (+/- 0) 53 | test bench_set_offset ... bench: 12 ns/iter (+/- 0) 54 | test bench_timestamp ... bench: 2 ns/iter (+/- 0) 55 | ``` 56 | 57 | * how use? 58 | 59 | #### add to Cargo.tom dep 60 | ```toml 61 | fastdate = "0.3" 62 | ``` 63 | 64 | #### use code 65 | ```rust 66 | use fastdate::DateTime; 67 | fn main(){ 68 | //now with local time zone 69 | DateTime::now(); 70 | //utc time now 71 | DateTime::utc(); 72 | // add 73 | DateTime::now() + Duration::from_secs(1); 74 | // sub 75 | DateTime::now() - Duration::from_secs(1); 76 | //parse allow token = ["YYYY","MM","DD","hh","mm","ss",".000000","+00:00","Z"] 77 | fastdate::DateTime::parse("YYYY-MM-DD hh:mm:ss.000000000Z", "2022-12-13 11:12:14.123456789Z").unwrap(); 78 | fastdate::DateTime::parse("YYYY-MM-DD hh:mm:ss.000000000+00:00", "2022-12-13 11:12:14.123456789+06:00").unwrap(); 79 | DateTime::parse("hh:mm:ss.000000,YYYY-MM-DD","11:12:14.123456,2022-12-13").unwrap(); 80 | //format allow token = ["YYYY","MM","DD","hh","mm","ss",".000000","+00:00","Z"] 81 | let dt = fastdate::DateTime::from(( 82 | Date { 83 | day: 1, 84 | mon: 1, 85 | year: 2000, 86 | }, 87 | Time { 88 | nano: 1233, 89 | sec: 11, 90 | minute: 1, 91 | hour: 1, 92 | }, 93 | )); 94 | let str:String = dt.format("YYYY-MM-DD/hh/mm/ss"); 95 | //befor,after 96 | let date1 = DateTime::from_str("2022-12-12 00:00:00").unwrap(); 97 | let date2 = DateTime::from_str("2022-12-12 01:00:00").unwrap(); 98 | assert_eq!(date2.after(&date1), true); 99 | assert_eq!(date1.before(&date2), true); 100 | //from str 101 | let datetime=DateTime::from_str("1234-12-13 11:12:13.123456"); 102 | //from str time zone 103 | let datetime=DateTime::from_str("1234-12-13 11:12:13.123456+08:00"); 104 | let datetime=DateTime::from_str("1234-12-13 11:12:13.123456Z"); 105 | //to_string() 106 | let s = datetime.to_string();//1234-12-13 11:12:13.123456 107 | //unix_timestamp 108 | let timestamp = DateTime::now().unix_timestamp(); 109 | //from unix_timestamp 110 | let datetime = DateTime::from_timestamp(timestamp); 111 | //unix_timestamp_millis 112 | let timestamp = DateTime::now().unix_timestamp_millis(); 113 | //from unix millis 114 | let datetime = DateTime::from_timestamp_millis(timestamp); 115 | //unix_timestamp_nano 116 | let timestamp = DateTime::now().unix_timestamp_nano(); 117 | //from unix_timestamp_nano 118 | let datetime = DateTime::from_timestamp_nano(timestamp); 119 | //sum Greenwich Mean Time (GMT) from datetime 120 | let time_gmt = DateTime::now().sub(Duration::from_secs(offset_sec() as u64)); 121 | } 122 | ``` -------------------------------------------------------------------------------- /benches/bench.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | extern crate test; 3 | 4 | use fastdate::DateTime; 5 | use std::str::FromStr; 6 | use std::time::Duration; 7 | use test::Bencher; 8 | 9 | //test bench_datetime_from_str ... bench: 11 ns/iter (+/- 0) 10 | #[bench] 11 | fn bench_datetime_from_str(b: &mut Bencher) { 12 | b.iter(|| { 13 | std::hint::black_box({ 14 | DateTime::from_str("1997-12-13T11:12:13.123456Z").expect("TODO: panic message"); 15 | }); 16 | }); 17 | } 18 | 19 | //test bench_date_from_str_iso_8601 ... bench: 41 ns/iter (+/- 2) 20 | #[bench] 21 | fn bench_date_from_str_iso_8601(b: &mut Bencher) { 22 | b.iter(|| { 23 | std::hint::black_box({ 24 | DateTime::from_str("1997-12-13T11:12:13.123456Z").expect("TODO: panic message"); 25 | }); 26 | }); 27 | } 28 | 29 | //test bench_date_from_str_iso_8601_time ... bench: 41 ns/iter (+/- 2) 30 | #[bench] 31 | fn bench_date_from_str_iso_8601_time(b: &mut Bencher) { 32 | b.iter(|| { 33 | std::hint::black_box({ 34 | DateTime::from_str("1997-12-13T11:12:13.123456+09:00").expect("TODO: panic message"); 35 | }); 36 | }); 37 | } 38 | 39 | //test bench_date_parse_format ... bench: 58 ns/iter (+/- 1) 40 | #[bench] 41 | fn bench_date_parse_format(b: &mut Bencher) { 42 | b.iter(|| { 43 | std::hint::black_box({ 44 | DateTime::parse("YYYY-MM-DD hh:mm:ss.000000Z", "2022-12-13 11:12:14.123456Z") 45 | .expect("TODO: panic message"); 46 | }); 47 | }); 48 | } 49 | 50 | //test bench_date_now ... bench: 40 ns/iter (+/- 1) 51 | #[bench] 52 | fn bench_date_utc(b: &mut Bencher) { 53 | b.iter(|| { 54 | std::hint::black_box({ 55 | DateTime::utc(); 56 | }); 57 | }); 58 | } 59 | 60 | //test bench_date_now_local ... bench: 40 ns/iter (+/- 1) 61 | #[bench] 62 | fn bench_date_now_local(b: &mut Bencher) { 63 | b.iter(|| { 64 | std::hint::black_box({ 65 | DateTime::now(); 66 | }); 67 | }); 68 | } 69 | 70 | //test bench_date_display ... bench: 40 ns/iter (+/- 1) 71 | #[bench] 72 | fn bench_date_display(b: &mut Bencher) { 73 | let now = DateTime::now(); 74 | b.iter(|| { 75 | std::hint::black_box({ 76 | now.to_string(); 77 | }); 78 | }); 79 | } 80 | 81 | #[bench] 82 | fn bench_add(b: &mut Bencher) { 83 | let now = DateTime::now(); 84 | b.iter(|| { 85 | std::hint::black_box({ 86 | let _ = now.clone() + Duration::from_secs(24 * 3600); 87 | }); 88 | }); 89 | } 90 | 91 | #[bench] 92 | fn bench_eq(b: &mut Bencher) { 93 | let now = DateTime::now(); 94 | let now2 = DateTime::now(); 95 | b.iter(|| { 96 | std::hint::black_box({ 97 | let _ = now.eq(&now2); 98 | }); 99 | }); 100 | } 101 | 102 | #[bench] 103 | fn bench_set_offset(b: &mut Bencher) { 104 | let now = DateTime::utc(); 105 | b.iter(|| { 106 | std::hint::black_box({ 107 | let _ = now.clone().set_offset(8 * 3600); 108 | }); 109 | }); 110 | } 111 | 112 | #[bench] 113 | fn bench_timestamp(b: &mut Bencher) { 114 | let now = DateTime::utc(); 115 | b.iter(|| { 116 | std::hint::black_box({ 117 | let _ = now.unix_timestamp(); 118 | }); 119 | }); 120 | } 121 | 122 | #[bench] 123 | fn bench_get_micro(b: &mut Bencher) { 124 | let now = DateTime::utc(); 125 | b.iter(|| { 126 | std::hint::black_box({ 127 | let _ = now.micro(); 128 | }); 129 | }); 130 | } 131 | 132 | //27 ns/iter (+/- 1) 133 | #[bench] 134 | fn bench_from_timestamp_millis(b: &mut Bencher) { 135 | b.iter(|| { 136 | std::hint::black_box({ 137 | DateTime::from_timestamp_millis(1692948547638); 138 | }); 139 | }); 140 | } 141 | 142 | //181 ns/iter (+/- 2) 143 | #[bench] 144 | fn bench_format(b: &mut Bencher) { 145 | let dt = DateTime::from_str("1997-12-13T11:12:13.123456+09:00").unwrap(); 146 | b.iter(|| { 147 | std::hint::black_box({ 148 | dt.format("YYYY-MM-DD/hh/mm/ss.000000"); 149 | }); 150 | }); 151 | } 152 | -------------------------------------------------------------------------------- /src/date.rs: -------------------------------------------------------------------------------- 1 | use crate::{DateTime, get_digit_unchecked}; 2 | use serde::{Deserialize, Deserializer, Serialize, Serializer}; 3 | use std::fmt::{self, Display, Formatter}; 4 | use std::str::FromStr; 5 | 6 | use crate::error::Error; 7 | 8 | /// Log timestamp type. 9 | /// 10 | /// Parse using `FromStr` impl. 11 | /// Format using the `Display` trait. 12 | /// Convert timestamp into/from `SystemTime` to use. 13 | /// Supports comparsion and sorting. 14 | #[derive(Clone, Debug, Eq, PartialEq, Hash)] 15 | pub struct Date { 16 | /// 1...31 17 | pub day: u8, 18 | /// 1...12 19 | pub mon: u8, 20 | /// 0000...9999 21 | pub year: i32, 22 | } 23 | 24 | impl Date { 25 | /// Parse a date from bytes, no check is performed for extract characters at the end of the string 26 | pub(crate) fn parse_bytes_partial(bytes: &[u8]) -> Result { 27 | if bytes.len() < 10 { 28 | return Err(Error::E("TooShort".to_string())); 29 | } 30 | let year: u16; 31 | let month: u8; 32 | let day: u8; 33 | unsafe { 34 | let y1 = get_digit_unchecked!(bytes, 0, "InvalidCharYear") as u16; 35 | let y2 = get_digit_unchecked!(bytes, 1, "InvalidCharYear") as u16; 36 | let y3 = get_digit_unchecked!(bytes, 2, "InvalidCharYear") as u16; 37 | let y4 = get_digit_unchecked!(bytes, 3, "InvalidCharYear") as u16; 38 | year = y1 * 1000 + y2 * 100 + y3 * 10 + y4; 39 | 40 | let m1 = get_digit_unchecked!(bytes, 5, "InvalidCharMonth"); 41 | let m2 = get_digit_unchecked!(bytes, 6, "InvalidCharMonth"); 42 | month = m1 * 10 + m2; 43 | 44 | let d1 = get_digit_unchecked!(bytes, 8, "InvalidCharDay"); 45 | let d2 = get_digit_unchecked!(bytes, 9, "InvalidCharDay"); 46 | day = d1 * 10 + d2; 47 | } 48 | 49 | // calculate the maximum number of days in the month, accounting for leap years in the 50 | // gregorian calendar 51 | let max_days = match month { 52 | 1 | 3 | 5 | 7 | 8 | 10 | 12 => 31, 53 | 4 | 6 | 9 | 11 => 30, 54 | 2 => { 55 | if year % 4 == 0 && (year % 100 != 0 || year % 400 == 0) { 56 | 29 57 | } else { 58 | 28 59 | } 60 | } 61 | _ => return Err(Error::E("OutOfRangeMonth".to_string())), 62 | }; 63 | 64 | if day < 1 || day > max_days { 65 | return Err(Error::E("OutOfRangeDay".to_string())); 66 | } 67 | 68 | Ok(Self { 69 | day, 70 | mon: month, 71 | year: year as i32, 72 | }) 73 | } 74 | 75 | /// 1...31 76 | pub fn set_day(mut self, arg: u8) -> Self { 77 | if arg == 0 || arg > 31 { 78 | return self; 79 | } 80 | self.day = arg; 81 | self 82 | } 83 | /// 1...12 84 | pub fn set_mon(mut self, arg: u8) -> Self { 85 | if arg == 0 || arg > 12 { 86 | return self; 87 | } 88 | self.mon = arg; 89 | self 90 | } 91 | /// 0000...9999 92 | pub fn set_year(mut self, arg: i32) -> Self { 93 | if !(0..=9999).contains(&arg) { 94 | return self; 95 | } 96 | self.year = arg; 97 | self 98 | } 99 | 100 | /// get day 101 | pub fn get_day(&self) -> u8 { 102 | self.day 103 | } 104 | 105 | /// get mon 106 | pub fn get_mon(&self) -> u8 { 107 | self.mon 108 | } 109 | 110 | /// get year 111 | pub fn get_year(&self) -> i32 { 112 | self.year 113 | } 114 | 115 | /// display date and return len 116 | pub fn display_date(&self, start: usize, buf: &mut [u8]) -> usize { 117 | buf[start] = b'0' + (self.year / 1000) as u8; 118 | buf[start + 1] = b'0' + (self.year / 100 % 10) as u8; 119 | buf[start + 2] = b'0' + (self.year / 10 % 10) as u8; 120 | buf[start + 3] = b'0' + (self.year % 10) as u8; 121 | 122 | buf[start + 5] = b'0' + (self.mon / 10); 123 | buf[start + 6] = b'0' + (self.mon % 10); 124 | 125 | buf[start + 8] = b'0' + (self.day / 10); 126 | buf[start + 9] = b'0' + (self.day % 10); 127 | 128 | start + 10 129 | } 130 | } 131 | 132 | impl FromStr for Date { 133 | type Err = Error; 134 | 135 | fn from_str(s: &str) -> Result { 136 | Date::parse_bytes_partial(s.as_bytes()) 137 | } 138 | } 139 | 140 | impl Display for Date { 141 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 142 | let mut buf: [u8; 10] = *b"0000-00-00"; 143 | self.display_date(0, &mut buf); 144 | f.write_str(std::str::from_utf8(&buf[..]).unwrap()) 145 | } 146 | } 147 | 148 | impl Serialize for Date { 149 | fn serialize(&self, serializer: S) -> Result 150 | where 151 | S: Serializer, 152 | { 153 | serializer.serialize_str(&self.to_string()) 154 | } 155 | } 156 | 157 | impl<'de> Deserialize<'de> for Date { 158 | fn deserialize(deserializer: D) -> Result 159 | where 160 | D: Deserializer<'de>, 161 | { 162 | use serde::de::Error; 163 | Date::from_str(&String::deserialize(deserializer)?) 164 | .map_err(|e| D::Error::custom(e.to_string())) 165 | } 166 | } 167 | 168 | impl From for Date { 169 | fn from(arg: DateTime) -> Self { 170 | Date { 171 | day: arg.day(), 172 | mon: arg.mon(), 173 | year: arg.year(), 174 | } 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /src/datetime.rs: -------------------------------------------------------------------------------- 1 | use crate::error::Error; 2 | use crate::sys::Timespec; 3 | use crate::{Date, Time}; 4 | use serde::{Deserialize, Deserializer, Serialize, Serializer}; 5 | use std::cmp; 6 | use std::fmt::{Display, Formatter}; 7 | use std::ops::{Add, Sub}; 8 | use std::str::FromStr; 9 | use std::sync::atomic::{AtomicI32, Ordering}; 10 | use std::time::{Duration, SystemTime, UNIX_EPOCH}; 11 | use time1::UtcOffset; 12 | use time1::format_description::well_known::Rfc3339; 13 | 14 | static GLOBAL_OFFSET: AtomicI32 = AtomicI32::new(-99999); 15 | 16 | /// offset with utc 0.zone 17 | pub fn offset_sec() -> i32 { 18 | if GLOBAL_OFFSET.load(Ordering::Relaxed) == -99999 { 19 | GLOBAL_OFFSET.store(Timespec::now().local().tm_utcoff, Ordering::SeqCst); 20 | } 21 | GLOBAL_OFFSET.load(Ordering::Relaxed) 22 | } 23 | 24 | /// set GLOBAL_OFFSET 25 | pub fn set_offset_sec(sec: i32) { 26 | GLOBAL_OFFSET.store(sec, Ordering::SeqCst); 27 | } 28 | 29 | /// Log timestamp type. 30 | /// 31 | /// Parse using `FromStr` impl. 32 | /// Format using the `Display` trait. 33 | /// Convert timestamp into/from `SystemTime` to use. 34 | /// Supports compare and sorting. 35 | #[derive(Clone, Debug, Eq, PartialEq, Hash)] 36 | pub struct DateTime { 37 | pub inner: time1::OffsetDateTime, 38 | } 39 | 40 | impl DateTime { 41 | ///utc time 42 | pub fn utc() -> Self { 43 | Self::from_system_time(SystemTime::now(), 0) 44 | } 45 | ///local zone time 46 | pub fn now() -> Self { 47 | Self::from_system_time(SystemTime::now(), 0).set_offset(offset_sec()) 48 | } 49 | 50 | /// set offset 51 | /// ```rust 52 | /// let mut dt = fastdate::DateTime::utc(); 53 | /// dt = dt.set_offset(fastdate::offset_sec()); 54 | /// ``` 55 | pub fn set_offset(mut self, mut offset_sec: i32) -> DateTime { 56 | offset_sec = offset_sec.clamp(-86399, 86399); 57 | self.inner = self 58 | .inner 59 | .to_offset(UtcOffset::from_whole_seconds(offset_sec).unwrap()); 60 | self 61 | } 62 | 63 | /// add Duration 64 | pub fn add(mut self, d: Duration) -> Self { 65 | self.inner = self.inner.add(d); 66 | self 67 | } 68 | 69 | /// sub Duration 70 | pub fn sub(mut self, d: Duration) -> Self { 71 | self.inner = self.inner.sub(d); 72 | self 73 | } 74 | 75 | ///add/sub sec 76 | pub fn add_sub_sec(self, sec: i64) -> Self { 77 | if sec >= 0 { 78 | self.add(Duration::from_secs(sec as u64)) 79 | } else { 80 | self.sub(Duration::from_secs((-sec) as u64)) 81 | } 82 | } 83 | 84 | /// is self before on other? 85 | pub fn before(&self, other: &DateTime) -> bool { 86 | self < other 87 | } 88 | 89 | /// is self after on other? 90 | pub fn after(&self, other: &DateTime) -> bool { 91 | self > other 92 | } 93 | 94 | /// unix_timestamp sec 95 | pub fn unix_timestamp(&self) -> i64 { 96 | self.inner.unix_timestamp() 97 | } 98 | 99 | ///unix_timestamp micros 100 | pub fn unix_timestamp_micros(&self) -> i64 { 101 | (self.inner.unix_timestamp_nanos() / 1000) as i64 102 | } 103 | 104 | ///unix_timestamp millis 105 | pub fn unix_timestamp_millis(&self) -> i64 { 106 | (self.inner.unix_timestamp_nanos() / 1000000) as i64 107 | } 108 | 109 | ///unix_timestamp nano 110 | pub fn unix_timestamp_nano(&self) -> i128 { 111 | self.inner.unix_timestamp_nanos() 112 | } 113 | 114 | ///from timestamp sec 115 | pub fn from_timestamp(sec: i64) -> DateTime { 116 | if sec >= 0 { 117 | Self::from_system_time(UNIX_EPOCH + Duration::from_secs(sec as u64), 0) 118 | } else { 119 | Self::from_system_time(UNIX_EPOCH - Duration::from_secs((-sec) as u64), 0) 120 | } 121 | } 122 | ///from timestamp micros 123 | pub fn from_timestamp_micros(micros: i64) -> DateTime { 124 | if micros >= 0 { 125 | Self::from_system_time(UNIX_EPOCH + Duration::from_micros(micros as u64), 0) 126 | } else { 127 | Self::from_system_time(UNIX_EPOCH - Duration::from_micros((-micros) as u64), 0) 128 | } 129 | } 130 | ///from timestamp millis 131 | pub fn from_timestamp_millis(ms: i64) -> DateTime { 132 | if ms >= 0 { 133 | Self::from_system_time(UNIX_EPOCH + Duration::from_millis(ms as u64), 0) 134 | } else { 135 | Self::from_system_time(UNIX_EPOCH - Duration::from_millis((-ms) as u64), 0) 136 | } 137 | } 138 | ///from timestamp nano 139 | pub fn from_timestamp_nano(nano: i128) -> DateTime { 140 | if nano >= 0 { 141 | Self::from_system_time(UNIX_EPOCH + Duration::from_nanos(nano as u64), 0) 142 | } else { 143 | Self::from_system_time(UNIX_EPOCH - Duration::from_nanos((-nano) as u64), 0) 144 | } 145 | } 146 | 147 | /// format support token = ["YYYY","MM","DD","hh","mm","ss",".000000",".000000000","+00:00"] 148 | /// ``` 149 | /// let dt = fastdate::DateTime::from(( 150 | /// fastdate::Date { 151 | /// day: 1, 152 | /// mon: 1, 153 | /// year: 2000, 154 | /// }, 155 | /// fastdate::Time { 156 | /// nano: 123456000, 157 | /// sec: 11, 158 | /// minute: 1, 159 | /// hour: 1, 160 | /// })).set_offset(8 * 60 * 60); 161 | /// println!("{}",dt.format("YYYY-MM-DD hh:mm:ss")); 162 | /// println!("{}",dt.format("YYYY-MM-DD hh:mm:ss.000000")); 163 | /// println!("{}",dt.format("YYYY-MM-DD hh:mm:ss.000000+00:00")); 164 | /// println!("{}",dt.format("YYYY/MM/DD/hh/mm/ss/.000000/+00:00")); 165 | /// println!("{}",dt.format("YYYY-MM-DD/hh/mm/ss")); 166 | /// 167 | /// ``` 168 | pub fn format(&self, fmt: &str) -> String { 169 | use std::fmt::Write; 170 | let (mut h, mut m, _) = self.offset_hms(); 171 | let offset = self.offset(); 172 | let add_sub = if offset >= 0 { '+' } else { '-' }; 173 | let mut result = String::with_capacity(fmt.len()); 174 | let chars = fmt.as_bytes(); 175 | let mut index = 0; 176 | let iter = chars.iter(); 177 | for c in iter { 178 | result.push(*c as char); 179 | if result.ends_with(".000000000") { 180 | for _ in 0..".000000000".len() { 181 | result.pop(); 182 | } 183 | write!(result, ".{:09}", self.nano()).unwrap() 184 | } else if result.ends_with(".000000") { 185 | if (index + 3) < fmt.len() 186 | && chars[index + 1] == b'0' 187 | && chars[index + 2] == b'0' 188 | && chars[index + 3] == b'0' 189 | { 190 | index += 1; 191 | continue; 192 | } 193 | for _ in 0..".000000".len() { 194 | result.pop(); 195 | } 196 | write!(result, ".{:06}", self.nano() / 1000).unwrap(); 197 | } else if result.ends_with("+00:00") { 198 | for _ in 0.."+00:00".len() { 199 | result.pop(); 200 | } 201 | h = h.abs(); 202 | m = m.abs(); 203 | write!(result, "{}{:02}:{:02}", add_sub, h, m).unwrap(); 204 | } else if result.ends_with("YYYY") { 205 | for _ in 0.."YYYY".len() { 206 | result.pop(); 207 | } 208 | write!(result, "{:04}", self.year()).unwrap() 209 | } else if result.ends_with("MM") { 210 | for _ in 0.."MM".len() { 211 | result.pop(); 212 | } 213 | result.write_fmt(format_args!("{:02}", self.mon())).unwrap() 214 | } else if result.ends_with("DD") { 215 | for _ in 0.."DD".len() { 216 | result.pop(); 217 | } 218 | write!(result, "{:02}", self.day()).unwrap() 219 | } else if result.ends_with("hh") { 220 | for _ in 0.."hh".len() { 221 | result.pop(); 222 | } 223 | write!(result, "{:02}", self.hour()).unwrap() 224 | } else if result.ends_with("mm") { 225 | for _ in 0.."mm".len() { 226 | result.pop(); 227 | } 228 | write!(result, "{:02}", self.minute()).unwrap(); 229 | } else if result.ends_with("ss") { 230 | for _ in 0.."ss".len() { 231 | result.pop(); 232 | } 233 | write!(result, "{:02}", self.sec()).unwrap(); 234 | } 235 | index += 1; 236 | } 237 | result 238 | } 239 | 240 | /// parse an string by format. 241 | /// format support token = ["YYYY","MM","DD","hh","mm","ss",".000000","+00:00","Z"] 242 | /// format str must be example: 243 | /// parse nano 244 | /// ```rust 245 | /// fastdate::DateTime::parse("YYYY-MM-DD hh:mm:ss.000000000Z", "2022-12-13 11:12:14.123456789Z").unwrap(); 246 | /// fastdate::DateTime::parse("YYYY-MM-DD hh:mm:ss.000000000+00:00", "2022-12-13 11:12:14.123456789+06:00").unwrap(); 247 | /// ``` 248 | /// or time zone(UTC+Hour) 249 | /// ```rust 250 | /// fastdate::DateTime::parse("YYYY-MM-DD hh:mm:ss.000000+00:00", "2022-12-13 11:12:14.123456+06:00").unwrap(); 251 | /// fastdate::DateTime::parse("YYYY-MM-DD hh:mm:ss.000000+00:00", "2022-12-13 11:12:14.123456-03:00").unwrap(); 252 | /// ``` 253 | /// or time zone(UTC) 254 | /// ```rust 255 | /// fastdate::DateTime::parse("YYYY-MM-DD hh:mm:ss.000000Z", "2022-12-13 11:12:14.123456Z").unwrap(); 256 | /// ``` 257 | /// parse local time 258 | /// ```rust 259 | /// fastdate::DateTime::parse("YYYY-MM-DD hh:mm:ss.000000","2022-12-13 11:12:14.123456").unwrap(); 260 | /// ``` 261 | /// or any position 262 | /// ```rust 263 | /// fastdate::DateTime::parse("YYYY-MM-DD,hh:mm:ss.000000","2022-12-13,11:12:14.123456").unwrap(); 264 | /// ``` 265 | /// or time zone(UTC) 266 | /// ```rust 267 | /// fastdate::DateTime::parse("YYYY-MM-DD hh:mm:ss.000000Z", "2022-12-13 11:12:14.123456Z").unwrap(); 268 | /// ``` 269 | /// or time zone(UTC+Hour) 270 | /// ```rust 271 | /// fastdate::DateTime::parse("YYYY-MM-DD hh:mm:ss.000000+00:00", "2022-12-13 11:12:14.123456+08:00").unwrap(); 272 | /// ``` 273 | /// ``` 274 | pub fn parse(format: &str, arg: &str) -> Result { 275 | let mut len = 19; 276 | //this is RFC3339 datetime buffer 277 | let bytes = arg.as_bytes(); 278 | let mut buf: [u8; 35] = *b"0000-00-00T00:00:00.000000000+00:00"; 279 | if let Some(year) = format.find("YYYY") { 280 | for (index, _) in (0..4).enumerate() { 281 | buf[index] = *bytes 282 | .get(year + index) 283 | .ok_or_else(|| Error::from("warn 'YYYY'"))?; 284 | } 285 | } 286 | if let Some(mon) = format.find("MM") { 287 | for (index, _) in (0..2).enumerate() { 288 | buf[5 + index] = *bytes 289 | .get(mon + index) 290 | .ok_or_else(|| Error::from("warn 'MM'"))?; 291 | } 292 | } 293 | if let Some(day) = format.find("DD") { 294 | for (index, _) in (0..2).enumerate() { 295 | buf[8 + index] = *bytes 296 | .get(day + index) 297 | .ok_or_else(|| Error::from("warn 'DD'"))?; 298 | } 299 | } 300 | if let Some(hour) = format.find("hh") { 301 | for (index, _) in (0..2).enumerate() { 302 | buf[11 + index] = *bytes 303 | .get(hour + index) 304 | .ok_or_else(|| Error::from("warn 'hh'"))?; 305 | } 306 | } 307 | if let Some(minute) = format.find("mm") { 308 | for (index, _) in (0..2).enumerate() { 309 | buf[14 + index] = *bytes 310 | .get(minute + index) 311 | .ok_or_else(|| Error::from("warn 'mm'"))?; 312 | } 313 | } 314 | if let Some(sec) = format.find("ss") { 315 | for (index, _) in (0..2).enumerate() { 316 | buf[17 + index] = *bytes 317 | .get(sec + index) 318 | .ok_or_else(|| Error::from("warn 'ss'"))?; 319 | } 320 | } 321 | let mut find_nano = false; 322 | //parse '.000000000' 323 | if let Some(nano) = format.find(".000000000") { 324 | for index in 0..10 { 325 | buf[19 + index] = *bytes 326 | .get(nano + index) 327 | .ok_or_else(|| Error::from("warn '.000000000'"))?; 328 | } 329 | len += 10; 330 | find_nano = true; 331 | } 332 | if !find_nano { 333 | if let Some(micro) = format.find(".000000") { 334 | for index in 0..7 { 335 | buf[19 + index] = *bytes 336 | .get(micro + index) 337 | .ok_or_else(|| Error::from("warn '.000000'"))?; 338 | } 339 | len += 7; 340 | } 341 | } 342 | let mut have_offset = false; 343 | if format.contains("Z") { 344 | buf[len] = b'Z'; 345 | len += 1; 346 | have_offset = true; 347 | } 348 | if let Some(zone) = format.find("+00:00") { 349 | for index in 0..6 { 350 | let x = bytes 351 | .get(zone + index) 352 | .ok_or_else(|| Error::from("warn '+00:00'"))?; 353 | buf[len + index] = *x; 354 | } 355 | len += 6; 356 | have_offset = true; 357 | } 358 | if !have_offset { 359 | let offset_sec = offset_sec(); 360 | let of = UtcOffset::from_whole_seconds(offset_sec).unwrap(); 361 | let (h, m, _) = of.as_hms(); 362 | if offset_sec >= 0 { 363 | buf[len] = b'+'; 364 | len += 1; 365 | } else { 366 | buf[len] = b'-'; 367 | len += 1; 368 | } 369 | buf[len] = b'0' + (h.abs() / 10) as u8; 370 | len += 1; 371 | buf[len] = b'0' + (h.abs() % 10) as u8; 372 | len += 1; 373 | buf[len] = b':'; 374 | len += 1; 375 | buf[len] = b'0' + (m.abs() / 10) as u8; 376 | len += 1; 377 | buf[len] = b'0' + (m.abs() % 10) as u8; 378 | len += 1; 379 | } 380 | let str = std::str::from_utf8(&buf[..len]).unwrap_or_default(); 381 | let inner = time1::OffsetDateTime::parse(str, &Rfc3339) 382 | .map_err(|e| Error::from(format!("{} of '{}'", e, arg)))?; 383 | Ok(Self { inner }) 384 | } 385 | 386 | /// get week_day 387 | pub fn week_day(&self) -> u8 { 388 | let secs_since_epoch = self.unix_timestamp(); 389 | /* 2000-03-01 (mod 400 year, immediately after feb29 */ 390 | const LEAPOCH: i64 = 11017; 391 | let days = (secs_since_epoch / 86400) - LEAPOCH; 392 | let mut wday = (3 + days) % 7; 393 | if wday <= 0 { 394 | wday += 7 395 | }; 396 | wday as u8 397 | } 398 | 399 | pub fn nano(&self) -> u32 { 400 | self.inner.nanosecond() 401 | } 402 | 403 | pub fn ms(&self) -> u16 { 404 | self.inner.millisecond() 405 | } 406 | 407 | /// get micro secs 408 | pub fn micro(&self) -> u32 { 409 | self.inner.microsecond() 410 | } 411 | 412 | /// get sec 413 | pub fn sec(&self) -> u8 { 414 | self.inner.second() 415 | } 416 | 417 | /// minute 418 | pub fn minute(&self) -> u8 { 419 | self.inner.minute() 420 | } 421 | 422 | /// get hour 423 | pub fn hour(&self) -> u8 { 424 | self.inner.hour() 425 | } 426 | 427 | /// get day 428 | pub fn day(&self) -> u8 { 429 | self.inner.day() 430 | } 431 | 432 | /// get mon 433 | pub fn mon(&self) -> u8 { 434 | self.inner.month() as u8 435 | } 436 | 437 | /// get year 438 | pub fn year(&self) -> i32 { 439 | self.inner.year() 440 | } 441 | 442 | ///offset sec 443 | pub fn offset(&self) -> i32 { 444 | self.inner.offset().whole_seconds() 445 | } 446 | 447 | ///offset_hms: hour,minute,sec 448 | pub fn offset_hms(&self) -> (i8, i8, i8) { 449 | self.inner.offset().as_hms() 450 | } 451 | 452 | pub fn from_system_time(s: SystemTime, offset: i32) -> Self { 453 | Self { 454 | inner: time1::OffsetDateTime::from(s), 455 | } 456 | .set_offset(offset) 457 | } 458 | 459 | /// stand "0000-00-00 00:00:00.000000000" 460 | pub fn display_stand(&self) -> String { 461 | let mut v = self.display(false); 462 | v.replace_range(10..11, " "); 463 | v 464 | } 465 | 466 | /// RFC3339 "0000-00-00T00:00:00.000000000Z" 467 | /// RFC3339 "0000-00-00T00:00:00.000000000+00:00:00" 468 | pub fn display(&self, zone: bool) -> String { 469 | let mut buf: [u8; 38] = *b"0000-00-00T00:00:00.000000000+00:00:00"; 470 | let len = self.do_display(&mut buf, zone); 471 | std::str::from_utf8(&buf[..len]).unwrap().to_string() 472 | } 473 | 474 | /// let mut buf: [u8; 38] = *b"0000-00-00T00:00:00.000000000+00:00:00"; 475 | /// than print this: 476 | /// RFC3339 "0000-00-00T00:00:00.000000000Z" 477 | /// RFC3339 "0000-00-00T00:00:00.000000000+00:00:00" 478 | pub fn do_display(&self, buf: &mut [u8; 38], add_zone: bool) -> usize { 479 | let year = self.year(); 480 | let mon = self.mon(); 481 | let day = self.day(); 482 | buf[0] = b'0' + (year / 1000) as u8; 483 | buf[1] = b'0' + (year / 100 % 10) as u8; 484 | buf[2] = b'0' + (year / 10 % 10) as u8; 485 | buf[3] = b'0' + (year % 10) as u8; 486 | buf[5] = b'0' + (mon / 10); 487 | buf[6] = b'0' + (mon % 10); 488 | buf[8] = b'0' + (day / 10); 489 | buf[9] = b'0' + (day % 10); 490 | let time = Time::from(self.clone()); 491 | let mut len = time.display_time(11, buf); 492 | if add_zone { 493 | let offset = self.offset(); 494 | if offset == 0 { 495 | buf[len] = b'Z'; 496 | len += 1; 497 | } else { 498 | let (h, m, s) = self.offset_hms(); 499 | if offset >= 0 { 500 | buf[len] = b'+'; 501 | len += 1; 502 | buf[len] = b'0' + (h as u8 / 10); 503 | len += 1; 504 | buf[len] = b'0' + (h as u8 % 10); 505 | len += 1; 506 | buf[len] = b':'; 507 | len += 1; 508 | buf[len] = b'0' + (m as u8 / 10); 509 | len += 1; 510 | buf[len] = b'0' + (m as u8 % 10); 511 | len += 1; 512 | if s != 0 { 513 | buf[len] = b':'; 514 | len += 1; 515 | buf[len] = b'0' + (s as u8 / 10); 516 | len += 1; 517 | buf[len] = b'0' + (s as u8 % 10); 518 | len += 1; 519 | } 520 | } else { 521 | buf[len] = b'-'; 522 | len += 1; 523 | buf[len] = b'0' + (-h as u8 / 10); 524 | len += 1; 525 | buf[len] = b'0' + (-h as u8 % 10); 526 | len += 1; 527 | buf[len] = b':'; 528 | len += 1; 529 | buf[len] = b'0' + (-m as u8 / 10); 530 | len += 1; 531 | buf[len] = b'0' + (-m as u8 % 10); 532 | len += 1; 533 | if s != 0 { 534 | buf[len] = b':'; 535 | len += 1; 536 | buf[len] = b'0' + (-s as u8 / 10); 537 | len += 1; 538 | buf[len] = b'0' + (-s as u8 % 10); 539 | len += 1; 540 | } 541 | } 542 | } 543 | } 544 | len 545 | } 546 | 547 | pub fn set_nano(mut self, nano: u32) -> Self { 548 | let v = self.nano(); 549 | if nano != v { 550 | self = self.sub(Duration::from_nanos(v as u64)); 551 | self = self.add(Duration::from_micros(nano as u64)); 552 | } 553 | self 554 | } 555 | 556 | pub fn from_str_default(arg: &str, default_offset: i32) -> Result { 557 | let mut v = { 558 | let mut v = String::with_capacity(arg.len() + 6); 559 | for x in arg.chars() { 560 | v.push(x); 561 | } 562 | v 563 | }; 564 | if v.len() == 10 { 565 | v.push_str("T00:00:00.00"); 566 | } 567 | if v.len() > 10 && &v[10..11] != "T" { 568 | v.replace_range(10..11, "T"); 569 | } 570 | let mut have_offset = None; 571 | if v.ends_with("Z") { 572 | v.pop(); 573 | v.push_str("+00:00"); 574 | have_offset = Some(v.len() - 6); 575 | } else { 576 | if v.len() >= 6 { 577 | let index = v.len() - 6; 578 | let b = &v[index..(index + 1)]; 579 | if b == "+" || b == "-" { 580 | have_offset = Some(index); 581 | } 582 | } 583 | if v.len() >= 3 { 584 | let index = v.len() - 3; 585 | let b = &v[index..(index + 1)]; 586 | if b == "+" || b == "-" { 587 | have_offset = Some(index); 588 | v.push_str(":00"); 589 | } 590 | } 591 | } 592 | if let Some(mut offset) = have_offset { 593 | if offset >= 1 { 594 | offset -= 1; 595 | if v.len() > offset && &v[offset..(offset + 1)] == " " { 596 | v.remove(offset); 597 | } 598 | } 599 | } 600 | if have_offset.is_none() { 601 | let of = UtcOffset::from_whole_seconds(default_offset).unwrap(); 602 | let (h, m, _) = of.as_hms(); 603 | if h >= 0 && m >= 0 { 604 | v.push_str(&format!("+{:02}:{:02}", h.abs(), m.abs())); 605 | } else { 606 | v.push_str(&format!("-{:02}:{:02}", h.abs(), m.abs())); 607 | } 608 | } 609 | let inner = time1::OffsetDateTime::parse(&v, &Rfc3339) 610 | .map_err(|e| Error::from(format!("{} of '{}'", e, arg)))?; 611 | Ok(Self { inner }) 612 | } 613 | } 614 | 615 | impl Add for DateTime { 616 | type Output = DateTime; 617 | 618 | fn add(self, rhs: Duration) -> Self::Output { 619 | self.add(rhs) 620 | } 621 | } 622 | 623 | impl Sub for DateTime { 624 | type Output = DateTime; 625 | 626 | fn sub(self, rhs: Duration) -> Self::Output { 627 | self.sub(rhs) 628 | } 629 | } 630 | 631 | impl Add<&Duration> for DateTime { 632 | type Output = DateTime; 633 | 634 | fn add(self, rhs: &Duration) -> Self::Output { 635 | self.add(*rhs) 636 | } 637 | } 638 | 639 | impl Sub<&Duration> for DateTime { 640 | type Output = DateTime; 641 | 642 | fn sub(self, rhs: &Duration) -> Self::Output { 643 | self.sub(*rhs) 644 | } 645 | } 646 | 647 | impl Sub for DateTime { 648 | type Output = Duration; 649 | 650 | fn sub(self, rhs: DateTime) -> Self::Output { 651 | let nano = self.unix_timestamp_nano() - rhs.unix_timestamp_nano(); 652 | Duration::from_nanos(nano as u64) 653 | } 654 | } 655 | 656 | impl From for DateTime { 657 | fn from(v: SystemTime) -> DateTime { 658 | DateTime::from_system_time(v, 0) 659 | } 660 | } 661 | 662 | impl From for SystemTime { 663 | fn from(v: DateTime) -> SystemTime { 664 | let nano = v.unix_timestamp_nano(); 665 | if nano >= 0 { 666 | UNIX_EPOCH + Duration::from_nanos(nano as u64) 667 | } else { 668 | UNIX_EPOCH - Duration::from_nanos(nano as u64) 669 | } 670 | } 671 | } 672 | 673 | impl From for DateTime { 674 | fn from(arg: Date) -> Self { 675 | Self::from_str(&format!( 676 | "{:04}-{:02}-{:02} 00:00:00.000000000Z", 677 | arg.year, arg.mon, arg.day 678 | )) 679 | .unwrap() 680 | } 681 | } 682 | 683 | /// from((Date{},offset_sec())) 684 | impl From<(Date, i32)> for DateTime { 685 | fn from(arg: (Date, i32)) -> Self { 686 | Self::from(arg.0) 687 | .set_offset(arg.1) 688 | .add_sub_sec(-arg.1 as i64) 689 | } 690 | } 691 | 692 | impl From